@isarai/maestro 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -22,108 +22,6 @@ function resolveBinaryPath(binary) {
22
22
  return null;
23
23
  }
24
24
  }
25
- function runNsjailSelfTest(binaryPath) {
26
- const args = [
27
- "--mode",
28
- "o",
29
- "--chroot",
30
- "/",
31
- "--user",
32
- "0",
33
- "--group",
34
- "0",
35
- "--disable_clone_newuser",
36
- "--disable_clone_newnet",
37
- "--cwd",
38
- "/",
39
- "--bindmount_ro",
40
- "/usr:/usr",
41
- "--bindmount_ro",
42
- "/bin:/bin",
43
- "--bindmount_ro",
44
- "/lib:/lib",
45
- "--bindmount_ro",
46
- "/sbin:/sbin",
47
- "--bindmount_ro",
48
- "/etc:/etc",
49
- "--bindmount",
50
- "/dev:/dev",
51
- "--tmpfsmount",
52
- "/tmp"
53
- ];
54
- if (fs.existsSync("/lib64")) {
55
- args.push("--bindmount_ro", "/lib64:/lib64");
56
- }
57
- args.push("--", "/bin/true");
58
- try {
59
- execFileSync(binaryPath, args, {
60
- stdio: ["ignore", "ignore", "pipe"]
61
- });
62
- return { ok: true, reason: null };
63
- } catch (error) {
64
- return {
65
- ok: false,
66
- reason: formatNsjailSelfTestError(error)
67
- };
68
- }
69
- }
70
- function formatNsjailSelfTestError(error) {
71
- const appArmorProfile = getCurrentAppArmorProfile();
72
- if (error && typeof error === "object" && "stderr" in error && (typeof error.stderr === "string" || Buffer.isBuffer(error.stderr))) {
73
- const stderr = String(error.stderr).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
74
- const detail = stderr.find((line) => line.includes("Permission denied")) || stderr.find((line) => line.includes("buildMountTree()")) || stderr.find((line) => line.includes("runChild()")) || stderr.at(-1);
75
- if (detail) {
76
- return buildNsjailUnavailableReason(detail, appArmorProfile);
77
- }
78
- }
79
- return buildNsjailUnavailableReason("nsjail runtime self-test failed", appArmorProfile);
80
- }
81
- function buildNsjailUnavailableReason(detail, appArmorProfile) {
82
- if (detail.includes("Permission denied")) {
83
- if (appArmorProfile && appArmorProfile !== "unconfined") {
84
- return `${detail}. Active AppArmor profile '${appArmorProfile}' is likely blocking mount namespace setup; when running Maestro in Docker, add \`security_opt: ["seccomp=unconfined", "apparmor=unconfined"]\` to the server container or run it with \`--privileged\`.`;
85
- }
86
- return `${detail}. The runtime is blocking mount namespace setup required by nsjail; when running in Docker, allow unconfined AppArmor/seccomp or run with \`--privileged\`.`;
87
- }
88
- return detail;
89
- }
90
- function getCurrentAppArmorProfile() {
91
- try {
92
- const profile = fs.readFileSync("/proc/self/attr/current", "utf-8").trim();
93
- return profile || null;
94
- } catch {
95
- return null;
96
- }
97
- }
98
- function isNsjailAvailable() {
99
- if (process.platform !== "linux") {
100
- nsjailUnavailableReason = `nsjail is only supported on Linux (current platform: ${process.platform})`;
101
- return false;
102
- }
103
- if (nsjailPath === void 0) {
104
- nsjailPath = resolveBinaryPath("nsjail");
105
- }
106
- if (!nsjailPath) {
107
- nsjailUnavailableReason = "nsjail binary was not found in PATH";
108
- return false;
109
- }
110
- if (nsjailRuntimeAvailable === void 0) {
111
- const result = runNsjailSelfTest(nsjailPath);
112
- nsjailRuntimeAvailable = result.ok;
113
- nsjailUnavailableReason = result.reason;
114
- }
115
- return nsjailRuntimeAvailable;
116
- }
117
- function getNsjailPath() {
118
- if (!isNsjailAvailable() || !nsjailPath) {
119
- const reason = nsjailUnavailableReason ? `: ${nsjailUnavailableReason}` : "";
120
- throw new Error(`nsjail is not available${reason}`);
121
- }
122
- return nsjailPath;
123
- }
124
- function getNsjailUnavailableReason() {
125
- return isNsjailAvailable() ? null : nsjailUnavailableReason ?? null;
126
- }
127
25
  function isDockerAvailable() {
128
26
  if (dockerPath === void 0) {
129
27
  dockerPath = resolveBinaryPath("docker");
@@ -151,126 +49,22 @@ function getDockerPath() {
151
49
  return dockerPath;
152
50
  }
153
51
  function normalizeSandboxProvider(value, legacyEnabled = false) {
154
- if (value === "none" || value === "nsjail" || value === "docker") {
52
+ if (value === "none" || value === "docker") {
155
53
  return value;
156
54
  }
157
- return legacyEnabled ? "nsjail" : "none";
55
+ return legacyEnabled ? "docker" : "none";
158
56
  }
159
57
  function resolveSandboxProviderAvailability(requested, availability) {
160
58
  if (requested === "docker") {
161
59
  return availability.dockerAvailable ? "docker" : "none";
162
60
  }
163
- if (requested === "nsjail") {
164
- return availability.nsjailAvailable ? "nsjail" : "none";
165
- }
166
61
  return "none";
167
62
  }
168
63
  function resolveSandboxProvider(requested) {
169
64
  return resolveSandboxProviderAvailability(requested, {
170
- nsjailAvailable: isNsjailAvailable(),
171
65
  dockerAvailable: isDockerAvailable()
172
66
  });
173
67
  }
174
- function ensureSandboxWritable(dirPath) {
175
- try {
176
- execSync(`chown -R ${SANDBOX_UID}:${SANDBOX_GID} ${JSON.stringify(dirPath)}`, {
177
- stdio: "ignore"
178
- });
179
- } catch {
180
- }
181
- }
182
- function buildNsjailArgs(config) {
183
- const home = os.homedir();
184
- const sandboxHome = "/home/sandbox";
185
- const mountedReadonlyRoots = [...BASE_READONLY_MOUNTS];
186
- const nodeBin = getNodeBinaryDir();
187
- const npmGlobalPrefix = getNpmGlobalPrefix();
188
- const codexCompanionBinDir = getCliCompanionBinDir("codex");
189
- const args = [
190
- "--mode",
191
- "o",
192
- "--chroot",
193
- "/",
194
- "--user",
195
- `${SANDBOX_UID}`,
196
- "--group",
197
- `${SANDBOX_GID}`,
198
- "--disable_clone_newuser",
199
- "--disable_clone_newnet",
200
- "--forward_signals",
201
- "--cwd",
202
- config.cwd,
203
- "--detect_cgroupv2",
204
- "--cgroup_mem_max",
205
- String(config.memoryLimit ?? DEFAULT_MEMORY_LIMIT),
206
- "--cgroup_pids_max",
207
- String(config.maxProcesses ?? DEFAULT_MAX_PROCESSES),
208
- "--rlimit_cpu",
209
- "soft",
210
- "--rlimit_fsize",
211
- "1024",
212
- "--rlimit_nproc",
213
- String(config.maxProcesses ?? DEFAULT_MAX_PROCESSES),
214
- "--rlimit_nofile",
215
- "1024",
216
- "--really_quiet"
217
- ];
218
- for (const dir of BASE_READONLY_MOUNTS) {
219
- if (fs.existsSync(dir)) {
220
- args.push("--bindmount_ro", `${dir}:${dir}`);
221
- }
222
- }
223
- if (fs.existsSync("/lib64")) {
224
- args.push("--bindmount_ro", "/lib64:/lib64");
225
- }
226
- args.push("--bindmount", "/dev:/dev");
227
- args.push("--tmpfsmount", "/tmp");
228
- args.push("--bindmount", `${config.cwd}:${config.cwd}`);
229
- if (fs.existsSync("/nix")) {
230
- args.push("--bindmount_ro", "/nix:/nix");
231
- }
232
- if (nodeBin && !isAlreadyMounted(nodeBin, mountedReadonlyRoots)) {
233
- args.push("--bindmount_ro", `${nodeBin}:${nodeBin}`);
234
- mountedReadonlyRoots.push(nodeBin);
235
- }
236
- if (npmGlobalPrefix && !isAlreadyMounted(npmGlobalPrefix, mountedReadonlyRoots)) {
237
- args.push("--bindmount_ro", `${npmGlobalPrefix}:${npmGlobalPrefix}`);
238
- mountedReadonlyRoots.push(npmGlobalPrefix);
239
- }
240
- if (codexCompanionBinDir && !isAlreadyMounted(codexCompanionBinDir, mountedReadonlyRoots)) {
241
- args.push("--bindmount_ro", `${codexCompanionBinDir}:${codexCompanionBinDir}`);
242
- mountedReadonlyRoots.push(codexCompanionBinDir);
243
- }
244
- mountSharedAgentPaths(args, home);
245
- if (config.readonlyMounts) {
246
- for (const mountPath of config.readonlyMounts) {
247
- if (fs.existsSync(mountPath)) {
248
- args.push("--bindmount_ro", `${mountPath}:${mountPath}`);
249
- }
250
- }
251
- }
252
- if (config.writableMounts) {
253
- for (const mountPath of config.writableMounts) {
254
- if (fs.existsSync(mountPath)) {
255
- args.push("--bindmount", `${mountPath}:${mountPath}`);
256
- }
257
- }
258
- }
259
- args.push("--bindmount", `${sandboxHome}:${sandboxHome}`);
260
- for (const [key, value] of Object.entries(config.env)) {
261
- args.push("--env", `${key}=${value}`);
262
- }
263
- const sandboxPath = buildSandboxPath({
264
- nodeBin,
265
- npmGlobalPrefix,
266
- companionBinDirs: [codexCompanionBinDir]
267
- });
268
- args.push("--env", `PATH=${sandboxPath}`);
269
- args.push("--env", `HOME=${sandboxHome}`);
270
- args.push("--env", "USER=sandbox");
271
- args.push("--env", "TERM=xterm-256color");
272
- return args;
273
- }
274
68
  function buildDockerRunArgs(config, command, image = DEFAULT_DOCKER_IMAGE) {
275
69
  const args = [
276
70
  "run",
@@ -475,15 +269,6 @@ function getSharedAgentPaths(home) {
475
269
  readonly: [gitconfig].filter(fs.existsSync)
476
270
  };
477
271
  }
478
- function mountSharedAgentPaths(args, home) {
479
- const sharedPaths = getSharedAgentPaths(home);
480
- for (const dir of sharedPaths.readwrite) {
481
- args.push("--bindmount", `${dir}:${dir}`);
482
- }
483
- for (const dir of sharedPaths.readonly) {
484
- args.push("--bindmount_ro", `${dir}:${dir}`);
485
- }
486
- }
487
272
  function resolveDockerSandboxDockerfile() {
488
273
  const localDir = path.dirname(fileURLToPath(import.meta.url));
489
274
  const installRoot = process.env.MAESTRO_INSTALL_ROOT?.trim();
@@ -504,65 +289,10 @@ function isPathWithin(requestedPath, basePath) {
504
289
  if (!requestedPath.startsWith(basePath)) return false;
505
290
  return requestedPath.charAt(basePath.length) === path.sep;
506
291
  }
507
- function buildSandboxPath(options) {
508
- const pathParts = [...BASE_PATH_DIRS];
509
- if (options.nodeBin && !pathParts.includes(options.nodeBin)) {
510
- pathParts.unshift(options.nodeBin);
511
- }
512
- const globalBin = options.npmGlobalPrefix ? path.join(options.npmGlobalPrefix, "bin") : null;
513
- if (globalBin && !pathParts.includes(globalBin)) {
514
- pathParts.unshift(globalBin);
515
- }
516
- for (const dir of options.companionBinDirs) {
517
- if (dir && !pathParts.includes(dir)) {
518
- pathParts.unshift(dir);
519
- }
520
- }
521
- return pathParts.join(":");
522
- }
523
- function getNodeBinaryDir() {
524
- try {
525
- const nodePath = execSync("which node", { encoding: "utf-8" }).trim();
526
- return nodePath ? path.dirname(nodePath) : null;
527
- } catch {
528
- return null;
529
- }
530
- }
531
- function getNpmGlobalPrefix() {
532
- try {
533
- const prefix = execSync("npm prefix -g", { encoding: "utf-8" }).trim();
534
- return prefix || null;
535
- } catch {
536
- return null;
537
- }
538
- }
539
- function getCliCompanionBinDir(cliName) {
540
- try {
541
- const cliPath = execSync(`which ${cliName}`, { encoding: "utf-8" }).trim();
542
- if (!cliPath) return null;
543
- return path.dirname(fs.realpathSync(cliPath));
544
- } catch {
545
- return null;
546
- }
547
- }
548
- function isAlreadyMounted(targetPath, mountRoots) {
549
- return mountRoots.some((root) => isPathWithin(targetPath, root));
550
- }
551
- var SANDBOX_UID, SANDBOX_GID, BASE_READONLY_MOUNTS, BASE_PATH_DIRS, DEFAULT_DOCKER_IMAGE, DEFAULT_MEMORY_LIMIT, DEFAULT_MAX_PROCESSES, nsjailPath, nsjailRuntimeAvailable, nsjailUnavailableReason, dockerPath, dockerServerReachable, dockerImageReadyFor, cachedSelfMounts, runningInsideContainer;
292
+ var DEFAULT_DOCKER_IMAGE, DEFAULT_MEMORY_LIMIT, DEFAULT_MAX_PROCESSES, dockerPath, dockerServerReachable, dockerImageReadyFor, cachedSelfMounts, runningInsideContainer;
552
293
  var init_sandbox = __esm({
553
294
  "../server/src/agents/sandbox.ts"() {
554
295
  "use strict";
555
- SANDBOX_UID = 1500;
556
- SANDBOX_GID = 1500;
557
- BASE_READONLY_MOUNTS = ["/usr", "/bin", "/lib", "/sbin", "/etc"];
558
- BASE_PATH_DIRS = [
559
- "/usr/local/sbin",
560
- "/usr/local/bin",
561
- "/usr/sbin",
562
- "/usr/bin",
563
- "/sbin",
564
- "/bin"
565
- ];
566
296
  DEFAULT_DOCKER_IMAGE = process.env.MAESTRO_DOCKER_SANDBOX_IMAGE || "maestro-sandbox:latest";
567
297
  DEFAULT_MEMORY_LIMIT = 512 * 1024 * 1024;
568
298
  DEFAULT_MAX_PROCESSES = 64;
@@ -636,38 +366,12 @@ function spawnPty(options) {
636
366
  ensureSpawnHelperExecutable();
637
367
  const shell = getShellPath(options.env);
638
368
  const cwd = options.cwd || os2.homedir();
639
- const requestedSandboxProvider = options.sandboxProvider ?? (options.sandbox ? "nsjail" : "none");
369
+ const requestedSandboxProvider = options.sandboxProvider ?? (options.sandbox ? "docker" : "none");
640
370
  const sandboxProvider = resolveSandboxProvider(requestedSandboxProvider);
641
371
  let actualSandboxProvider = sandboxProvider;
642
372
  const fullEnv = buildChildEnv(options.env);
643
373
  let ptyProcess;
644
- if (sandboxProvider === "nsjail") {
645
- ensureSandboxWritable(cwd);
646
- const sandboxConfig = {
647
- cwd,
648
- homeDir: options.homeDir,
649
- env: fullEnv,
650
- readonlyMounts: options.readonlyMounts,
651
- writableMounts: options.writableMounts,
652
- memoryLimit: options.memoryLimit
653
- };
654
- const nsjailArgs = [
655
- ...buildNsjailArgs(sandboxConfig),
656
- "--",
657
- shell,
658
- "-l"
659
- ];
660
- ptyProcess = pty.spawn(getNsjailPath(), nsjailArgs, {
661
- name: "xterm-256color",
662
- cols: options.cols ?? 120,
663
- rows: options.rows ?? 30,
664
- cwd,
665
- // nsjail manages env vars via --env flags, but node-pty still needs
666
- // a minimal env for the PTY master side
667
- env: { TERM: "xterm-256color" }
668
- });
669
- console.log(`Spawned sandboxed PTY for terminal ${options.terminalId}`);
670
- } else if (sandboxProvider === "docker") {
374
+ if (sandboxProvider === "docker") {
671
375
  const sandboxConfig = {
672
376
  cwd,
673
377
  homeDir: options.homeDir,
@@ -691,12 +395,6 @@ function spawnPty(options) {
691
395
  });
692
396
  console.log(`Spawned docker-sandboxed PTY for terminal ${options.terminalId}`);
693
397
  } else {
694
- if (requestedSandboxProvider === "nsjail" && !isNsjailAvailable()) {
695
- const reason = getNsjailUnavailableReason();
696
- throw new Error(
697
- `Sandbox requested for terminal ${options.terminalId} but nsjail is not available (platform: ${process.platform}${reason ? `, reason: ${reason}` : ""}).`
698
- );
699
- }
700
398
  if (requestedSandboxProvider === "docker" && !isDockerAvailable()) {
701
399
  throw new Error(
702
400
  `Sandbox requested for terminal ${options.terminalId} but docker is not available.`
@@ -2516,11 +2214,7 @@ function getSettings() {
2516
2214
  const agentDefaultDisableSandbox = getSetting("agentDefaultDisableSandbox");
2517
2215
  const agentDefaultSkipPermissions = getSetting("agentDefaultSkipPermissions");
2518
2216
  const agentDefaultWorktreeMode = getSetting("agentDefaultWorktreeMode");
2519
- const storedSandboxProvider = normalizeSandboxProvider(
2520
- sandboxProvider,
2521
- sandbox === "true"
2522
- );
2523
- const resolvedSandboxProvider = storedSandboxProvider === "nsjail" ? "docker" : storedSandboxProvider;
2217
+ const resolvedSandboxProvider = normalizeSandboxProvider(sandboxProvider, sandbox === "true");
2524
2218
  return {
2525
2219
  autoUpdateEnabled: enabled !== null ? enabled === "true" : DEFAULTS.autoUpdateEnabled,
2526
2220
  autoUpdateIntervalHours: interval !== null ? Number(interval) : DEFAULTS.autoUpdateIntervalHours,
@@ -2555,9 +2249,8 @@ function updateSettings(patch) {
2555
2249
  }
2556
2250
  }
2557
2251
  if (patch.sandboxProvider !== void 0) {
2558
- const provider = patch.sandboxProvider === "nsjail" ? "docker" : patch.sandboxProvider;
2559
- setSetting("sandboxProvider", provider);
2560
- setSetting("sandboxEnabled", String(provider !== "none"));
2252
+ setSetting("sandboxProvider", patch.sandboxProvider);
2253
+ setSetting("sandboxEnabled", String(patch.sandboxProvider !== "none"));
2561
2254
  }
2562
2255
  if (patch.deepgramApiKey !== void 0) {
2563
2256
  setSetting("deepgramApiKey", patch.deepgramApiKey);
@@ -4620,7 +4313,7 @@ import { z as z2 } from "zod";
4620
4313
  import { z } from "zod";
4621
4314
  var AutoSpawnAgentProviderSchema = z.enum(["claude", "codex"]);
4622
4315
  var AutoSpawnAgentWorktreeModeSchema = z.enum(["none", "new"]);
4623
- var SandboxProviderSchema = z.enum(["none", "nsjail", "docker"]);
4316
+ var SandboxProviderSchema = z.enum(["none", "docker"]);
4624
4317
  var SettingsSchema = z.object({
4625
4318
  autoUpdateEnabled: z.boolean().default(false),
4626
4319
  autoUpdateIntervalHours: z.number().min(1).max(168).default(24),
@@ -6959,7 +6652,7 @@ function stopAutoUpdater() {
6959
6652
  }
6960
6653
 
6961
6654
  // ../server/src/services/ollama.ts
6962
- import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5, readFileSync as readFileSync6, existsSync as existsSync15 } from "node:fs";
6655
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5, readFileSync as readFileSync5, existsSync as existsSync15 } from "node:fs";
6963
6656
  import { join as join16 } from "node:path";
6964
6657
  import { homedir as homedir9 } from "node:os";
6965
6658
  var OLLAMA_HOST = process.env.OLLAMA_HOST || "http://localhost:11434";
@@ -7058,7 +6751,7 @@ function writePiModelsConfig(modelId) {
7058
6751
  let existing = {};
7059
6752
  if (existsSync15(PI_MODELS_PATH)) {
7060
6753
  try {
7061
- existing = JSON.parse(readFileSync6(PI_MODELS_PATH, "utf-8"));
6754
+ existing = JSON.parse(readFileSync5(PI_MODELS_PATH, "utf-8"));
7062
6755
  } catch {
7063
6756
  }
7064
6757
  }