@getollie/cli 0.2.1 → 0.2.3

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/README.md CHANGED
@@ -60,14 +60,6 @@ DAYTONA_API_KEY=... ollie agent --executor daytona 'run the native test suite an
60
60
 
61
61
  Agent `--ndjson` emits one runner event per line. Agent workspace writes persist to the host.
62
62
 
63
- Use repeatable `--mount <host-path:sandbox-path>` options to expose additional host directories read-write to the agent sandbox:
64
-
65
- ```bash
66
- ollie agent --mount ./data:/data --mount /tmp/cache:/cache 'process the data'
67
- ```
68
-
69
- Mount destinations must be normalized absolute sandbox paths outside `/workspace`, and mount destinations may not overlap. Relative host paths are resolved from the directory where Ollie is invoked. Additional mounts apply to the agent's primary sandbox tools, not delegated local or Daytona executors.
70
-
71
63
  The agent remains in the restricted workspace environment by default. `--executor local` adds a `delegateTask` tool that can start fresh subagents with native `bash`, file, package-manager, build, and test access. The local executor shares the current workspace, runs with the current user's permissions, inherits the current process environment, and is **not isolated**. It must be selected explicitly.
72
64
 
73
65
  `--executor daytona` runs delegated agents in an isolated Daytona sandbox. Ollie uploads the complete workspace before each delegated task, synchronizes all files back afterward, stops the sandbox between tasks while preserving its filesystem, and deletes it when the run finishes. Configure the SDK with `DAYTONA_API_KEY` and optionally `DAYTONA_API_URL`, `DAYTONA_TARGET`, or `OLLIE_DAYTONA_SNAPSHOT`.
@@ -76,10 +68,9 @@ The agent remains in the restricted workspace environment by default. `--executo
76
68
 
77
69
  ```bash
78
70
  ollie sandbox --cwd /path/to/workspace
79
- ollie sandbox --cwd /path/to/workspace --mount ./data:/data
80
71
  ```
81
72
 
82
- The worker reserves stdout for newline-delimited request/response records and retains one sandbox until EOF, so in-session changes persist between requests. Its workspace and additional mount writes persist to the host by default.
73
+ The worker reserves stdout for newline-delimited request/response records and retains one sandbox until EOF, so in-session changes persist between requests. Its workspace writes persist to the host by default.
83
74
 
84
75
  ### Diagnostics
85
76
 
@@ -110,13 +101,11 @@ Configuration precedence is CLI flags, then environment variables, then defaults
110
101
  ```ts
111
102
  import { createJustBash, SANDBOX_WORKSPACE_PATH } from "@getollie/cli";
112
103
 
113
- const bash = createJustBash(process.cwd(), {
114
- mounts: [{ hostPath: "./data", mountPoint: "/data" }],
115
- });
104
+ const bash = createJustBash(process.cwd());
116
105
  const result = await bash.exec(`echo generated > ${SANDBOX_WORKSPACE_PATH}/output.txt`);
117
106
  ```
118
107
 
119
- The configured host directory is mounted read-write at `/workspace`, and optional additional host directories can be mounted read-write elsewhere, so writes persist directly to them. Symlinks are disabled, and Python and JavaScript support are enabled.
108
+ The configured host directory is mounted read-write at `/workspace`, so writes persist directly to it. Symlinks are disabled, and Python and JavaScript support are enabled.
120
109
 
121
110
  ### Sandbox Git
122
111
 
package/dist/cli.js CHANGED
@@ -8,11 +8,7 @@ import { Command } from "commander";
8
8
 
9
9
  // src/config.ts
10
10
  import { access, stat } from "fs/promises";
11
- import { resolve as resolve2 } from "path";
12
-
13
- // src/sandbox/mounts.ts
14
- import { realpathSync, statSync } from "fs";
15
- import { posix, resolve } from "path";
11
+ import { resolve } from "path";
16
12
 
17
13
  // src/utils/errors.ts
18
14
  var CliError = class extends Error {
@@ -30,64 +26,6 @@ function getErrorMessage(error) {
30
26
  return String(error);
31
27
  }
32
28
 
33
- // src/sandbox/mounts.ts
34
- var SANDBOX_WORKSPACE_PATH = "/workspace";
35
- function pathsOverlap(left, right) {
36
- return left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
37
- }
38
- function validateMountPoint(mountPoint) {
39
- if (!posix.isAbsolute(mountPoint) || mountPoint === "/" || mountPoint.includes("\\") || mountPoint.includes("\0") || posix.normalize(mountPoint) !== mountPoint) {
40
- throw new CliError(
41
- `Mount point must be a normalized absolute sandbox path other than /: ${mountPoint}`
42
- );
43
- }
44
- if (pathsOverlap(mountPoint, SANDBOX_WORKSPACE_PATH)) {
45
- throw new CliError(`Mount point must be outside ${SANDBOX_WORKSPACE_PATH}: ${mountPoint}`);
46
- }
47
- }
48
- function validateSandboxMounts(mounts) {
49
- const validated = [];
50
- for (const mount of mounts) {
51
- validateMountPoint(mount.mountPoint);
52
- let hostPath;
53
- try {
54
- hostPath = realpathSync(resolve(mount.hostPath));
55
- if (!statSync(hostPath).isDirectory()) {
56
- throw new Error("not a directory");
57
- }
58
- } catch {
59
- throw new CliError(
60
- `Mount source does not exist or is not a directory: ${resolve(mount.hostPath)}`
61
- );
62
- }
63
- const conflict = validated.find(
64
- (candidate) => pathsOverlap(candidate.mountPoint, mount.mountPoint)
65
- );
66
- if (conflict) {
67
- throw new CliError(
68
- `Mount points must not overlap: ${conflict.mountPoint} and ${mount.mountPoint}`
69
- );
70
- }
71
- validated.push({ hostPath, mountPoint: mount.mountPoint });
72
- }
73
- return validated;
74
- }
75
- function parseSandboxMounts(specs = []) {
76
- const mounts = specs.map((spec) => {
77
- const separator = spec.lastIndexOf(":");
78
- if (separator <= 0 || separator === spec.length - 1) {
79
- throw new CliError(
80
- `Invalid mount ${JSON.stringify(spec)}. Expected <host-path>:<sandbox-path>`
81
- );
82
- }
83
- return {
84
- hostPath: spec.slice(0, separator),
85
- mountPoint: spec.slice(separator + 1)
86
- };
87
- });
88
- return validateSandboxMounts(mounts);
89
- }
90
-
91
29
  // src/config.ts
92
30
  var DEFAULT_PROVIDER = "openai";
93
31
  var DEFAULT_MODEL = "gpt-5.6";
@@ -116,7 +54,7 @@ function parseExecutor(value) {
116
54
  return executor;
117
55
  }
118
56
  async function resolveRunConfig(input = {}, env = process.env) {
119
- const cwd = resolve2(input.cwd ?? env.OLLIE_CWD ?? process.cwd());
57
+ const cwd = resolve(input.cwd ?? env.OLLIE_CWD ?? process.cwd());
120
58
  const stats = await stat(cwd).catch(() => null);
121
59
  if (!stats?.isDirectory()) {
122
60
  throw new CliError(`Working directory does not exist or is not a directory: ${cwd}`);
@@ -125,7 +63,6 @@ async function resolveRunConfig(input = {}, env = process.env) {
125
63
  const executor = parseExecutor(input.executor ?? env.OLLIE_EXECUTOR);
126
64
  return {
127
65
  cwd,
128
- mounts: parseSandboxMounts(input.mount),
129
66
  provider: input.provider ?? env.OLLIE_PROVIDER ?? DEFAULT_PROVIDER,
130
67
  model: input.model ?? env.OLLIE_MODEL ?? DEFAULT_MODEL,
131
68
  executor,
@@ -162,23 +99,23 @@ async function resolveRunConfig(input = {}, env = process.env) {
162
99
  import { access as access2, stat as stat2 } from "fs/promises";
163
100
 
164
101
  // src/sandbox/just-bash.ts
165
- import { realpathSync as realpathSync2, statSync as statSync2 } from "fs";
102
+ import { realpathSync, statSync } from "fs";
166
103
  import { Bash, InMemoryFs, MountableFs, ReadWriteFs } from "just-bash";
167
104
 
168
105
  // src/sandbox/git/index.ts
169
- import { posix as posix3 } from "path";
106
+ import { posix as posix2 } from "path";
170
107
  import { createTwoFilesPatch } from "diff";
171
108
  import * as git from "isomorphic-git";
172
109
  import { defineCommand } from "just-bash";
173
110
 
174
111
  // src/sandbox/git/fs-adapter.ts
175
- import { posix as posix2 } from "path";
112
+ import { posix } from "path";
176
113
  var ROOT = "/workspace";
177
114
  function safePath(path) {
178
115
  if (path.includes("\0") || path.includes("\\")) {
179
116
  throw Object.assign(new Error("Invalid virtual Git path"), { code: "EINVAL" });
180
117
  }
181
- const normalized = posix2.resolve(path);
118
+ const normalized = posix.resolve(path);
182
119
  if (normalized !== ROOT && !normalized.startsWith(`${ROOT}/`)) {
183
120
  throw Object.assign(new Error("Git path is outside /workspace"), { code: "EACCES" });
184
121
  }
@@ -198,7 +135,7 @@ function normalizeError(error) {
198
135
  throw Object.assign(new Error("Virtual filesystem operation failed"), { code: "EIO" });
199
136
  }
200
137
  async function ensureParent(fs, path) {
201
- const parent = posix2.dirname(path);
138
+ const parent = posix.dirname(path);
202
139
  if (!await fs.exists(parent)) await fs.mkdir(parent, { recursive: true });
203
140
  }
204
141
  function statShape(stat4) {
@@ -433,9 +370,9 @@ function virtualPath(ctx, input = ".") {
433
370
  async function repositoryDir(ctx) {
434
371
  let current = virtualPath(ctx);
435
372
  while (current === WORKSPACE || current.startsWith(`${WORKSPACE}/`)) {
436
- if (await ctx.fs.exists(posix3.join(current, ".git"))) return current;
373
+ if (await ctx.fs.exists(posix2.join(current, ".git"))) return current;
437
374
  if (current === WORKSPACE) break;
438
- current = posix3.dirname(current);
375
+ current = posix2.dirname(current);
439
376
  }
440
377
  throw new Error("not a git repository (or any parent up to /workspace)");
441
378
  }
@@ -518,7 +455,7 @@ async function writeGlobalConfig(ctx, values) {
518
455
  ([section, entries]) => `[${section}]
519
456
  ${entries.map(([key, value]) => ` ${key} = ${value}`).join("\n")}`
520
457
  ).join("\n");
521
- await ctx.fs.mkdir(posix3.dirname(GLOBAL_CONFIG_PATH), { recursive: true });
458
+ await ctx.fs.mkdir(posix2.dirname(GLOBAL_CONFIG_PATH), { recursive: true });
522
459
  await ctx.fs.writeFile(GLOBAL_CONFIG_PATH, content ? `${content}
523
460
  ` : "");
524
461
  }
@@ -532,7 +469,7 @@ async function handleInit(context, args) {
532
469
  throw new Error("cannot create repository directory");
533
470
  });
534
471
  await git.init({ fs: context.fs, dir, defaultBranch: branchOpt.value ?? "main" });
535
- return ok(`Initialized empty Git repository in ${posix3.join(dir, ".git")}/
472
+ return ok(`Initialized empty Git repository in ${posix2.join(dir, ".git")}/
536
473
  `);
537
474
  }
538
475
  async function handleStatus(context, args) {
@@ -685,7 +622,7 @@ async function handleDiff(context, args) {
685
622
  const changed = cachedFlag.enabled ? headStatus !== stageStatus : workdirStatus !== stageStatus;
686
623
  if (!changed) continue;
687
624
  const oldBytes = cachedFlag.enabled ? head && headStatus !== 0 ? (await git.readBlob({ fs: context.fs, dir, oid: head, filepath })).blob : empty : await readStage(filepath);
688
- const newBytes = cachedFlag.enabled ? await readStage(filepath) : workdirStatus === 0 ? empty : await context.command.fs.readFileBuffer(posix3.join(dir, filepath));
625
+ const newBytes = cachedFlag.enabled ? await readStage(filepath) : workdirStatus === 0 ? empty : await context.command.fs.readFileBuffer(posix2.join(dir, filepath));
689
626
  patches.push(patch(filepath, oldBytes, newBytes));
690
627
  }
691
628
  return ok(patches.join(""));
@@ -752,7 +689,7 @@ async function handleCheckout(context, args) {
752
689
  const oid = await git.resolveRef({ fs: context.fs, dir, ref });
753
690
  for (const filepath of filepaths) {
754
691
  const blob = await readCheckoutBlob(context, dir, oid, filepath);
755
- await context.command.fs.writeFile(posix3.join(dir, filepath), blob);
692
+ await context.command.fs.writeFile(posix2.join(dir, filepath), blob);
756
693
  }
757
694
  return ok();
758
695
  }
@@ -879,7 +816,7 @@ async function handleClone(context, args) {
879
816
  const [rawUrl, destination, ...rest] = noCheckoutFlag.rest;
880
817
  if (!rawUrl || rest.length) usage("clone requires a URL and optional directory");
881
818
  const url = validateRemoteUrl(rawUrl);
882
- const inferred = posix3.basename(new URL(url).pathname).replace(/\.git$/, "") || "repository";
819
+ const inferred = posix2.basename(new URL(url).pathname).replace(/\.git$/, "") || "repository";
883
820
  const dir = virtualPath(context.command, destination ?? inferred);
884
821
  const depth = positiveInteger(depthOpt.value, "--depth");
885
822
  await git.clone({
@@ -1043,35 +980,30 @@ Supported commands: ${COMMANDS.join(", ")}
1043
980
  }
1044
981
 
1045
982
  // src/sandbox/just-bash.ts
1046
- var SANDBOX_WORKSPACE_PATH2 = "/workspace";
983
+ var SANDBOX_WORKSPACE_PATH = "/workspace";
1047
984
  function canonicalizeWorkspaceRoot(workspaceRoot) {
1048
- const canonicalRoot = realpathSync2(workspaceRoot);
1049
- if (!statSync2(canonicalRoot).isDirectory()) {
985
+ const canonicalRoot = realpathSync(workspaceRoot);
986
+ if (!statSync(canonicalRoot).isDirectory()) {
1050
987
  throw new Error("The sandbox workspace root must be a directory");
1051
988
  }
1052
989
  return canonicalRoot;
1053
990
  }
1054
- function createWorkspaceFs(workspaceRoot, options) {
991
+ function createWorkspaceFs(workspaceRoot) {
1055
992
  const root = canonicalizeWorkspaceRoot(workspaceRoot);
1056
- const additionalMounts = validateSandboxMounts(options.mounts ?? []).map((mount) => ({
1057
- mountPoint: mount.mountPoint,
1058
- filesystem: new ReadWriteFs({ root: mount.hostPath, allowSymlinks: false })
1059
- }));
1060
993
  return new MountableFs({
1061
994
  base: new InMemoryFs(),
1062
995
  mounts: [
1063
996
  {
1064
- mountPoint: SANDBOX_WORKSPACE_PATH2,
997
+ mountPoint: SANDBOX_WORKSPACE_PATH,
1065
998
  filesystem: new ReadWriteFs({ root, allowSymlinks: false })
1066
- },
1067
- ...additionalMounts
999
+ }
1068
1000
  ]
1069
1001
  });
1070
1002
  }
1071
1003
  function createJustBash(workspaceRoot, options = {}) {
1072
1004
  return new Bash({
1073
- fs: createWorkspaceFs(workspaceRoot, options),
1074
- cwd: SANDBOX_WORKSPACE_PATH2,
1005
+ fs: createWorkspaceFs(workspaceRoot),
1006
+ cwd: SANDBOX_WORKSPACE_PATH,
1075
1007
  env: {
1076
1008
  HOME: "/home/user",
1077
1009
  TERM: "xterm-256color"
@@ -1421,7 +1353,7 @@ var ExecutorController = class {
1421
1353
  // src/executor/daytona.ts
1422
1354
  import { randomUUID } from "crypto";
1423
1355
  import { mkdir, readdir, readFile, rm, stat as stat3, writeFile } from "fs/promises";
1424
- import { dirname, isAbsolute, relative, resolve as resolve3, sep } from "path";
1356
+ import { dirname, isAbsolute, relative, resolve as resolve2, sep } from "path";
1425
1357
  import {
1426
1358
  Daytona,
1427
1359
  DaytonaProcessExecutionTimeoutError
@@ -1456,7 +1388,7 @@ async function listLocalFiles(root) {
1456
1388
  const files = /* @__PURE__ */ new Map();
1457
1389
  async function visit(directory) {
1458
1390
  for (const entry of await readdir(directory, { withFileTypes: true })) {
1459
- const absolutePath = resolve3(directory, entry.name);
1391
+ const absolutePath = resolve2(directory, entry.name);
1460
1392
  if (entry.isDirectory()) {
1461
1393
  await visit(absolutePath);
1462
1394
  } else if (entry.isFile()) {
@@ -1540,7 +1472,7 @@ ${marker}
1540
1472
  this.#disposed = true;
1541
1473
  }
1542
1474
  #resolvePath(path) {
1543
- const target = isAbsolute(path) ? resolve3(path) : resolve3(this.#workspacePath, path);
1475
+ const target = isAbsolute(path) ? resolve2(path) : resolve2(this.#workspacePath, path);
1544
1476
  if (target !== this.#workspacePath && !target.startsWith(`${this.#workspacePath}/`)) {
1545
1477
  throw new Error(`Path is outside the Daytona workspace: ${path}`);
1546
1478
  }
@@ -1556,7 +1488,7 @@ var DaytonaWorkspaceSynchronizer = class {
1556
1488
  #sandbox;
1557
1489
  #knownFiles = /* @__PURE__ */ new Set();
1558
1490
  constructor(cwd, remoteWorkspacePath, sandbox) {
1559
- this.#cwd = resolve3(cwd);
1491
+ this.#cwd = resolve2(cwd);
1560
1492
  this.#remoteWorkspacePath = remoteWorkspacePath;
1561
1493
  this.#sandbox = sandbox;
1562
1494
  }
@@ -1587,7 +1519,7 @@ var DaytonaWorkspaceSynchronizer = class {
1587
1519
  for (const [path, size] of localFiles) {
1588
1520
  signal?.throwIfAborted();
1589
1521
  await abortable(
1590
- this.#sandbox.fs.uploadFile(resolve3(this.#cwd, path), this.#remotePath(path)),
1522
+ this.#sandbox.fs.uploadFile(resolve2(this.#cwd, path), this.#remotePath(path)),
1591
1523
  signal
1592
1524
  );
1593
1525
  if (this.#knownFiles.has(path)) filesUpdated++;
@@ -1619,7 +1551,7 @@ var DaytonaWorkspaceSynchronizer = class {
1619
1551
  let bytesTransferred = 0;
1620
1552
  for (const path of this.#knownFiles) {
1621
1553
  if (remoteFiles.has(path)) continue;
1622
- await rm(resolve3(this.#cwd, path), { force: true });
1554
+ await rm(resolve2(this.#cwd, path), { force: true });
1623
1555
  filesDeleted++;
1624
1556
  }
1625
1557
  for (const path of remoteFiles) {
@@ -1649,7 +1581,7 @@ var DaytonaWorkspaceSynchronizer = class {
1649
1581
  return `${this.#remoteWorkspacePath}/${path}`;
1650
1582
  }
1651
1583
  #localPath(path) {
1652
- const target = resolve3(this.#cwd, path);
1584
+ const target = resolve2(this.#cwd, path);
1653
1585
  if (target !== this.#cwd && !target.startsWith(`${this.#cwd}${sep}`)) {
1654
1586
  throw new Error(`Daytona returned a path outside the workspace: ${path}`);
1655
1587
  }
@@ -1783,7 +1715,7 @@ Terminus-2 spirit:
1783
1715
  - Summarize final findings and important command results clearly.
1784
1716
 
1785
1717
  Runtime context:
1786
- - Working directory: ${SANDBOX_WORKSPACE_PATH2}
1718
+ - Working directory: ${SANDBOX_WORKSPACE_PATH}
1787
1719
  - Current time: ${options.now.toISOString()}
1788
1720
  - Maximum model/tool steps: ${options.maxSteps}
1789
1721
  - Per-command timeout: ${options.commandTimeoutMs} ms
@@ -1838,8 +1770,8 @@ async function createWorkspaceTools(options) {
1838
1770
  }
1839
1771
  async function createTools(options) {
1840
1772
  return createWorkspaceTools({
1841
- sandbox: createJustBash(options.cwd, { mounts: options.mounts }),
1842
- destination: SANDBOX_WORKSPACE_PATH2,
1773
+ sandbox: createJustBash(options.cwd),
1774
+ destination: SANDBOX_WORKSPACE_PATH,
1843
1775
  maxOutputBytes: options.maxOutputBytes,
1844
1776
  extraInstructions: "Use bash commands to inspect files, search, run tests, and gather terminal state. Sandbox-native git is available; local Git changes persist in the workspace, and git push has permanent remote effects, so destructive actions and pushes must only be used when explicitly requested. Keep commands bounded."
1845
1777
  });
@@ -1914,7 +1846,7 @@ var ModelTaskDelegator = class {
1914
1846
  // src/executor/local.ts
1915
1847
  import { spawn } from "child_process";
1916
1848
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
1917
- import { dirname as dirname2, isAbsolute as isAbsolute2, resolve as resolve4 } from "path";
1849
+ import { dirname as dirname2, isAbsolute as isAbsolute2, resolve as resolve3 } from "path";
1918
1850
  var LocalSandbox = class {
1919
1851
  #cwd;
1920
1852
  #commandTimeoutMs;
@@ -1923,7 +1855,7 @@ var LocalSandbox = class {
1923
1855
  #processes = /* @__PURE__ */ new Set();
1924
1856
  #disposed = false;
1925
1857
  constructor(options, signal) {
1926
- this.#cwd = resolve4(options.cwd);
1858
+ this.#cwd = resolve3(options.cwd);
1927
1859
  this.#commandTimeoutMs = options.commandTimeoutMs;
1928
1860
  this.#env = options.env ?? process.env;
1929
1861
  this.#signal = signal;
@@ -2036,7 +1968,7 @@ var LocalSandbox = class {
2036
1968
  this.#processes.clear();
2037
1969
  }
2038
1970
  #resolvePath(path) {
2039
- return isAbsolute2(path) ? path : resolve4(this.#cwd, path);
1971
+ return isAbsolute2(path) ? path : resolve3(this.#cwd, path);
2040
1972
  }
2041
1973
  #assertActive() {
2042
1974
  if (this.#disposed) throw new Error("Local executor session has been disposed");
@@ -2047,7 +1979,7 @@ var LocalExecutorSession = class {
2047
1979
  context;
2048
1980
  #disposed = false;
2049
1981
  constructor(options, signal) {
2050
- const workspacePath = resolve4(options.cwd);
1982
+ const workspacePath = resolve3(options.cwd);
2051
1983
  this.sandbox = new LocalSandbox(options, signal);
2052
1984
  this.context = {
2053
1985
  kind: "local",
@@ -2134,7 +2066,7 @@ function createLanguageModel({
2134
2066
  throw new CliError("OPENAI_API_KEY is required when using the openai provider.");
2135
2067
  }
2136
2068
  const openai = createOpenAI({ apiKey: env.OPENAI_API_KEY });
2137
- return openai(model);
2069
+ return openai.chat(model);
2138
2070
  }
2139
2071
 
2140
2072
  // src/runner.ts
@@ -2164,7 +2096,6 @@ async function runOneShot(options) {
2164
2096
  });
2165
2097
  const workspaceTools = await createTools({
2166
2098
  cwd: options.cwd,
2167
- mounts: options.mounts,
2168
2099
  maxOutputBytes: options.maxOutputBytes
2169
2100
  });
2170
2101
  const executorProvider = createExecutorProvider(options);
@@ -2242,7 +2173,7 @@ async function runOneShot(options) {
2242
2173
 
2243
2174
  // src/sandbox/runtime.ts
2244
2175
  import { once } from "events";
2245
- import { posix as posix4 } from "path";
2176
+ import { posix as posix3 } from "path";
2246
2177
  import { createInterface } from "readline";
2247
2178
  function isRecord(value) {
2248
2179
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -2280,7 +2211,7 @@ function requireString(params, name) {
2280
2211
  return value;
2281
2212
  }
2282
2213
  function validateVirtualPath(path) {
2283
- if (!posix4.isAbsolute(path) || path.includes("\\") || path.includes("\0") || posix4.normalize(path) !== path || path.split("/").some((component) => component === "." || component === "..")) {
2214
+ if (!posix3.isAbsolute(path) || path.includes("\\") || path.includes("\0") || posix3.normalize(path) !== path || path.split("/").some((component) => component === "." || component === "..")) {
2284
2215
  throw new Error("Path must be a normalized absolute virtual path");
2285
2216
  }
2286
2217
  return path;
@@ -2341,7 +2272,7 @@ async function runWorker(options = {}) {
2341
2272
  const workspaceRoot = options.cwd ?? process.cwd();
2342
2273
  const input = options.input ?? process.stdin;
2343
2274
  const output = options.output ?? process.stdout;
2344
- const bash = createJustBash(workspaceRoot, { mounts: options.mounts });
2275
+ const bash = createJustBash(workspaceRoot);
2345
2276
  const lines = createInterface({ input, crlfDelay: Number.POSITIVE_INFINITY });
2346
2277
  const close = () => lines.close();
2347
2278
  if (options.signal?.aborted) {
@@ -2392,18 +2323,8 @@ function truncateText(input, maxBytes) {
2392
2323
  function addExecOptions(command, ndjsonDescription = "emit one JSON result record") {
2393
2324
  return command.option("--cwd <path>", "working directory").option("--timeout <ms>", "command timeout in milliseconds").option("--max-output <bytes>", "maximum retained output").option("--ndjson", ndjsonDescription);
2394
2325
  }
2395
- function collectOption(value, previous) {
2396
- return [...previous ?? [], value];
2397
- }
2398
- function addMountOption(command) {
2399
- return command.option(
2400
- "--mount <host-path:sandbox-path>",
2401
- "mount a host directory read-write (repeatable)",
2402
- collectOption
2403
- );
2404
- }
2405
2326
  function addAgentOptions(command) {
2406
- return addMountOption(addExecOptions(command, "emit newline-delimited JSON event records")).option("--provider <provider>", "model provider", "openai").option("--model <model>", "model id").option(
2327
+ return addExecOptions(command, "emit newline-delimited JSON event records").option("--provider <provider>", "model provider", "openai").option("--model <model>", "model id").option(
2407
2328
  "--executor <executor>",
2408
2329
  "delegated executor: none, local, or daytona (local is unisolated; daytona requires DAYTONA_API_KEY)",
2409
2330
  "none"
@@ -2509,7 +2430,6 @@ async function runSandbox(options) {
2509
2430
  try {
2510
2431
  await runWorker({
2511
2432
  cwd: options.cwd,
2512
- mounts: parseSandboxMounts(options.mount),
2513
2433
  signal: controller.signal
2514
2434
  });
2515
2435
  if (signalExitCode !== void 0) {
@@ -2538,10 +2458,7 @@ function createProgram() {
2538
2458
  agent.action(async (prompt, options) => {
2539
2459
  await runAgent(prompt, options);
2540
2460
  });
2541
- const sandbox = addMountOption(
2542
- program.command("sandbox").description("run the persistent sandbox NDJSON command interface").option("--cwd <path>", "working directory")
2543
- );
2544
- sandbox.action(async (options) => {
2461
+ program.command("sandbox").description("run the persistent sandbox NDJSON command interface").option("--cwd <path>", "working directory").action(async (options) => {
2545
2462
  await runSandbox(options);
2546
2463
  });
2547
2464
  program.command("doctor").description("validate the local Ollie setup without making model calls").option("--cwd <path>", "working directory").option("--provider <provider>", "model provider", "openai").option("--model <model>", "model id").option("--json", "emit JSON report").action(async (options) => {