@getollie/cli 0.2.2 → 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/dist/cli.js CHANGED
@@ -7,12 +7,8 @@ import { CommanderError } from "commander";
7
7
  import { Command } from "commander";
8
8
 
9
9
  // src/config.ts
10
- import { access, realpath, stat } from "fs/promises";
11
- import { posix as posix2, resolve as resolve2 } from "path";
12
-
13
- // src/sandbox/mounts.ts
14
- import { realpathSync, statSync } from "fs";
15
- import { posix, resolve } from "path";
10
+ import { access, stat } from "fs/promises";
11
+ import { resolve } from "path";
16
12
 
17
13
  // src/utils/errors.ts
18
14
  var CliError = class extends Error {
@@ -30,68 +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 sandboxPathsOverlap(left, right) {
36
- return left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
37
- }
38
- function sandboxPathContains(parent, child) {
39
- return parent === child || child.startsWith(`${parent}/`);
40
- }
41
- function validateSandboxPath(path, label = "Sandbox path") {
42
- if (!posix.isAbsolute(path) || path.includes("\\") || path.includes("\0") || path.split("/").includes("..") || posix.normalize(path) !== path) {
43
- throw new CliError(`${label} must be a normalized absolute sandbox path: ${path}`);
44
- }
45
- return path;
46
- }
47
- function validateMountPoint(mountPoint) {
48
- validateSandboxPath(mountPoint, "Mount point");
49
- if (mountPoint === "/") {
50
- throw new CliError(`Mount point must be a normalized absolute sandbox path other than /: ${mountPoint}`);
51
- }
52
- }
53
- function validateSandboxMounts(mounts, options = {}) {
54
- const reservedMountPoint = options.reservedMountPoint === void 0 ? SANDBOX_WORKSPACE_PATH : options.reservedMountPoint;
55
- const validated = [];
56
- for (const mount of mounts) {
57
- validateMountPoint(mount.mountPoint);
58
- if (reservedMountPoint && sandboxPathsOverlap(mount.mountPoint, reservedMountPoint)) {
59
- throw new CliError(`Mount point must be outside ${reservedMountPoint}: ${mount.mountPoint}`);
60
- }
61
- let hostPath;
62
- try {
63
- hostPath = realpathSync(resolve(mount.hostPath));
64
- if (!statSync(hostPath).isDirectory()) throw new Error("not a directory");
65
- } catch {
66
- throw new CliError(
67
- `Mount source host path does not exist or is not a directory: ${resolve(mount.hostPath)}`
68
- );
69
- }
70
- const conflict = validated.find(
71
- (candidate) => sandboxPathsOverlap(candidate.mountPoint, mount.mountPoint)
72
- );
73
- if (conflict) {
74
- throw new CliError(
75
- `Mount points must not overlap: ${conflict.mountPoint} and ${mount.mountPoint}`
76
- );
77
- }
78
- validated.push({ hostPath, mountPoint: mount.mountPoint });
79
- }
80
- return validated;
81
- }
82
- function parseSandboxMounts(specs = [], options) {
83
- const mounts = specs.map((spec) => {
84
- const separator = spec.lastIndexOf(":");
85
- if (separator <= 0 || separator === spec.length - 1) {
86
- throw new CliError(
87
- `Invalid mount ${JSON.stringify(spec)}. Expected <host-path>:<sandbox-path>`
88
- );
89
- }
90
- return { hostPath: spec.slice(0, separator), mountPoint: spec.slice(separator + 1) };
91
- });
92
- return validateSandboxMounts(mounts, options);
93
- }
94
-
95
29
  // src/config.ts
96
30
  var DEFAULT_PROVIDER = "openai";
97
31
  var DEFAULT_MODEL = "gpt-5.6";
@@ -100,7 +34,9 @@ var DEFAULT_COMMAND_TIMEOUT_MS = 3e4;
100
34
  var DEFAULT_MAX_OUTPUT_BYTES = 65536;
101
35
  var DEFAULT_EXECUTOR = "none";
102
36
  function parseNumber(name, value, fallback) {
103
- if (value === void 0 || value === "") return fallback;
37
+ if (value === void 0 || value === "") {
38
+ return fallback;
39
+ }
104
40
  const parsed = typeof value === "number" ? value : Number.parseInt(value, 10);
105
41
  if (!Number.isFinite(parsed) || parsed < 0) {
106
42
  throw new CliError(`${name} must be a non-negative integer`);
@@ -117,82 +53,39 @@ function parseExecutor(value) {
117
53
  }
118
54
  return executor;
119
55
  }
120
- async function resolveHostDirectory(value, label) {
121
- const absolute = resolve2(value);
122
- const stats = await stat(absolute).catch(() => null);
123
- if (!stats?.isDirectory()) {
124
- throw new CliError(`${label} host path does not exist or is not a directory: ${absolute}`);
125
- }
126
- await access(absolute);
127
- return realpath(absolute);
128
- }
129
56
  async function resolveRunConfig(input = {}, env = process.env) {
130
- const configuredHostCwd = input.cwd ?? env.OLLIE_CWD;
131
- const configuredSandboxCwd = input.sandboxCwd ?? env.OLLIE_SANDBOX_CWD;
132
- const usedImplicitCwd = configuredHostCwd === void 0 && configuredSandboxCwd === void 0;
133
- const hostCwdValue = configuredHostCwd ?? (usedImplicitCwd ? process.cwd() : void 0);
134
- const explicitMounts = parseSandboxMounts(input.mount, {
135
- reservedMountPoint: hostCwdValue ? "/workspace" : null
136
- });
137
- let workspace;
138
- let mounts;
139
- let sandboxCwd;
140
- if (hostCwdValue) {
141
- workspace = {
142
- hostPath: await resolveHostDirectory(hostCwdValue, "Working directory"),
143
- mountPoint: "/workspace"
144
- };
145
- mounts = explicitMounts;
146
- sandboxCwd = configuredSandboxCwd ?? "/workspace";
147
- } else {
148
- if (!configuredSandboxCwd) {
149
- throw new CliError("Either --cwd or --sandbox-cwd must be specified.");
150
- }
151
- sandboxCwd = configuredSandboxCwd;
152
- validateSandboxPath(sandboxCwd, "Sandbox cwd");
153
- const containing2 = explicitMounts.find(
154
- (mount) => sandboxPathContains(mount.mountPoint, sandboxCwd)
155
- );
156
- if (!containing2) {
157
- const destinations = explicitMounts.map((mount) => mount.mountPoint).join(", ") || "(none)";
158
- throw new CliError(
159
- `Sandbox cwd ${sandboxCwd} is not contained by the primary workspace or any configured mount. Configured mount destinations: ${destinations}`
160
- );
161
- }
162
- workspace = containing2;
163
- mounts = explicitMounts.filter((mount) => mount !== containing2);
164
- }
165
- validateSandboxPath(sandboxCwd, "Sandbox cwd");
166
- const allMounts = [workspace, ...mounts];
167
- const containing = allMounts.find((mount) => sandboxPathContains(mount.mountPoint, sandboxCwd));
168
- if (!containing) {
169
- const destinations = allMounts.map((mount) => mount.mountPoint).join(", ");
170
- throw new CliError(
171
- `Sandbox cwd ${sandboxCwd} is not contained by the primary workspace or any configured mount. Configured mount destinations: ${destinations}`
172
- );
57
+ const cwd = resolve(input.cwd ?? env.OLLIE_CWD ?? process.cwd());
58
+ const stats = await stat(cwd).catch(() => null);
59
+ if (!stats?.isDirectory()) {
60
+ throw new CliError(`Working directory does not exist or is not a directory: ${cwd}`);
173
61
  }
174
- const mappedRelative = posix2.relative(containing.mountPoint, sandboxCwd);
175
- await resolveHostDirectory(resolve2(containing.hostPath, mappedRelative), "Sandbox cwd mapped");
176
- const workspaceRelativeCwd = sandboxPathContains(workspace.mountPoint, sandboxCwd) ? posix2.relative(workspace.mountPoint, sandboxCwd) || "." : null;
62
+ await access(cwd);
177
63
  const executor = parseExecutor(input.executor ?? env.OLLIE_EXECUTOR);
178
64
  return {
179
- cwd: workspace.hostPath,
180
- workspaceMountPoint: workspace.mountPoint,
181
- sandboxCwd,
182
- workspaceRelativeCwd,
183
- mounts,
184
- usedImplicitCwd,
65
+ cwd,
185
66
  provider: input.provider ?? env.OLLIE_PROVIDER ?? DEFAULT_PROVIDER,
186
67
  model: input.model ?? env.OLLIE_MODEL ?? DEFAULT_MODEL,
187
68
  executor,
188
- maxSteps: clamp(parseNumber("max steps", input.maxSteps ?? env.OLLIE_MAX_STEPS, DEFAULT_MAX_STEPS), 1, 100),
69
+ maxSteps: clamp(
70
+ parseNumber("max steps", input.maxSteps ?? env.OLLIE_MAX_STEPS, DEFAULT_MAX_STEPS),
71
+ 1,
72
+ 100
73
+ ),
189
74
  commandTimeoutMs: clamp(
190
- parseNumber("command timeout", input.timeout ?? env.OLLIE_COMMAND_TIMEOUT_MS, DEFAULT_COMMAND_TIMEOUT_MS),
75
+ parseNumber(
76
+ "command timeout",
77
+ input.timeout ?? env.OLLIE_COMMAND_TIMEOUT_MS,
78
+ DEFAULT_COMMAND_TIMEOUT_MS
79
+ ),
191
80
  1,
192
81
  3e5
193
82
  ),
194
83
  maxOutputBytes: clamp(
195
- parseNumber("max output", input.maxOutput ?? env.OLLIE_MAX_OUTPUT_BYTES, DEFAULT_MAX_OUTPUT_BYTES),
84
+ parseNumber(
85
+ "max output",
86
+ input.maxOutput ?? env.OLLIE_MAX_OUTPUT_BYTES,
87
+ DEFAULT_MAX_OUTPUT_BYTES
88
+ ),
196
89
  1,
197
90
  2e6
198
91
  ),
@@ -206,23 +99,23 @@ async function resolveRunConfig(input = {}, env = process.env) {
206
99
  import { access as access2, stat as stat2 } from "fs/promises";
207
100
 
208
101
  // src/sandbox/just-bash.ts
209
- import { realpathSync as realpathSync2, statSync as statSync2 } from "fs";
102
+ import { realpathSync, statSync } from "fs";
210
103
  import { Bash, InMemoryFs, MountableFs, ReadWriteFs } from "just-bash";
211
104
 
212
105
  // src/sandbox/git/index.ts
213
- import { posix as posix4 } from "path";
106
+ import { posix as posix2 } from "path";
214
107
  import { createTwoFilesPatch } from "diff";
215
108
  import * as git from "isomorphic-git";
216
109
  import { defineCommand } from "just-bash";
217
110
 
218
111
  // src/sandbox/git/fs-adapter.ts
219
- import { posix as posix3 } from "path";
112
+ import { posix } from "path";
220
113
  var ROOT = "/workspace";
221
114
  function safePath(path) {
222
115
  if (path.includes("\0") || path.includes("\\")) {
223
116
  throw Object.assign(new Error("Invalid virtual Git path"), { code: "EINVAL" });
224
117
  }
225
- const normalized = posix3.resolve(path);
118
+ const normalized = posix.resolve(path);
226
119
  if (normalized !== ROOT && !normalized.startsWith(`${ROOT}/`)) {
227
120
  throw Object.assign(new Error("Git path is outside /workspace"), { code: "EACCES" });
228
121
  }
@@ -242,7 +135,7 @@ function normalizeError(error) {
242
135
  throw Object.assign(new Error("Virtual filesystem operation failed"), { code: "EIO" });
243
136
  }
244
137
  async function ensureParent(fs, path) {
245
- const parent = posix3.dirname(path);
138
+ const parent = posix.dirname(path);
246
139
  if (!await fs.exists(parent)) await fs.mkdir(parent, { recursive: true });
247
140
  }
248
141
  function statShape(stat4) {
@@ -477,9 +370,9 @@ function virtualPath(ctx, input = ".") {
477
370
  async function repositoryDir(ctx) {
478
371
  let current = virtualPath(ctx);
479
372
  while (current === WORKSPACE || current.startsWith(`${WORKSPACE}/`)) {
480
- if (await ctx.fs.exists(posix4.join(current, ".git"))) return current;
373
+ if (await ctx.fs.exists(posix2.join(current, ".git"))) return current;
481
374
  if (current === WORKSPACE) break;
482
- current = posix4.dirname(current);
375
+ current = posix2.dirname(current);
483
376
  }
484
377
  throw new Error("not a git repository (or any parent up to /workspace)");
485
378
  }
@@ -562,7 +455,7 @@ async function writeGlobalConfig(ctx, values) {
562
455
  ([section, entries]) => `[${section}]
563
456
  ${entries.map(([key, value]) => ` ${key} = ${value}`).join("\n")}`
564
457
  ).join("\n");
565
- await ctx.fs.mkdir(posix4.dirname(GLOBAL_CONFIG_PATH), { recursive: true });
458
+ await ctx.fs.mkdir(posix2.dirname(GLOBAL_CONFIG_PATH), { recursive: true });
566
459
  await ctx.fs.writeFile(GLOBAL_CONFIG_PATH, content ? `${content}
567
460
  ` : "");
568
461
  }
@@ -576,7 +469,7 @@ async function handleInit(context, args) {
576
469
  throw new Error("cannot create repository directory");
577
470
  });
578
471
  await git.init({ fs: context.fs, dir, defaultBranch: branchOpt.value ?? "main" });
579
- return ok(`Initialized empty Git repository in ${posix4.join(dir, ".git")}/
472
+ return ok(`Initialized empty Git repository in ${posix2.join(dir, ".git")}/
580
473
  `);
581
474
  }
582
475
  async function handleStatus(context, args) {
@@ -729,7 +622,7 @@ async function handleDiff(context, args) {
729
622
  const changed = cachedFlag.enabled ? headStatus !== stageStatus : workdirStatus !== stageStatus;
730
623
  if (!changed) continue;
731
624
  const oldBytes = cachedFlag.enabled ? head && headStatus !== 0 ? (await git.readBlob({ fs: context.fs, dir, oid: head, filepath })).blob : empty : await readStage(filepath);
732
- const newBytes = cachedFlag.enabled ? await readStage(filepath) : workdirStatus === 0 ? empty : await context.command.fs.readFileBuffer(posix4.join(dir, filepath));
625
+ const newBytes = cachedFlag.enabled ? await readStage(filepath) : workdirStatus === 0 ? empty : await context.command.fs.readFileBuffer(posix2.join(dir, filepath));
733
626
  patches.push(patch(filepath, oldBytes, newBytes));
734
627
  }
735
628
  return ok(patches.join(""));
@@ -796,7 +689,7 @@ async function handleCheckout(context, args) {
796
689
  const oid = await git.resolveRef({ fs: context.fs, dir, ref });
797
690
  for (const filepath of filepaths) {
798
691
  const blob = await readCheckoutBlob(context, dir, oid, filepath);
799
- await context.command.fs.writeFile(posix4.join(dir, filepath), blob);
692
+ await context.command.fs.writeFile(posix2.join(dir, filepath), blob);
800
693
  }
801
694
  return ok();
802
695
  }
@@ -923,7 +816,7 @@ async function handleClone(context, args) {
923
816
  const [rawUrl, destination, ...rest] = noCheckoutFlag.rest;
924
817
  if (!rawUrl || rest.length) usage("clone requires a URL and optional directory");
925
818
  const url = validateRemoteUrl(rawUrl);
926
- const inferred = posix4.basename(new URL(url).pathname).replace(/\.git$/, "") || "repository";
819
+ const inferred = posix2.basename(new URL(url).pathname).replace(/\.git$/, "") || "repository";
927
820
  const dir = virtualPath(context.command, destination ?? inferred);
928
821
  const depth = positiveInteger(depthOpt.value, "--depth");
929
822
  await git.clone({
@@ -1087,49 +980,34 @@ Supported commands: ${COMMANDS.join(", ")}
1087
980
  }
1088
981
 
1089
982
  // src/sandbox/just-bash.ts
1090
- var SANDBOX_WORKSPACE_PATH2 = "/workspace";
983
+ var SANDBOX_WORKSPACE_PATH = "/workspace";
1091
984
  function canonicalizeWorkspaceRoot(workspaceRoot) {
1092
- const canonicalRoot = realpathSync2(workspaceRoot);
1093
- if (!statSync2(canonicalRoot).isDirectory()) {
985
+ const canonicalRoot = realpathSync(workspaceRoot);
986
+ if (!statSync(canonicalRoot).isDirectory()) {
1094
987
  throw new Error("The sandbox workspace root must be a directory");
1095
988
  }
1096
989
  return canonicalRoot;
1097
990
  }
1098
- function createWorkspaceFs(workspaceRoot, options) {
991
+ function createWorkspaceFs(workspaceRoot) {
1099
992
  const root = canonicalizeWorkspaceRoot(workspaceRoot);
1100
- const workspaceMountPoint = options.workspaceMountPoint ?? SANDBOX_WORKSPACE_PATH2;
1101
- validateSandboxPath(workspaceMountPoint, "Workspace mount point");
1102
- const additionalMounts = validateSandboxMounts(options.mounts ?? [], {
1103
- reservedMountPoint: workspaceMountPoint
1104
- }).map((mount) => ({
1105
- mountPoint: mount.mountPoint,
1106
- filesystem: new ReadWriteFs({ root: mount.hostPath, allowSymlinks: false })
1107
- }));
1108
993
  return new MountableFs({
1109
994
  base: new InMemoryFs(),
1110
995
  mounts: [
1111
996
  {
1112
- mountPoint: workspaceMountPoint,
997
+ mountPoint: SANDBOX_WORKSPACE_PATH,
1113
998
  filesystem: new ReadWriteFs({ root, allowSymlinks: false })
1114
- },
1115
- ...additionalMounts
999
+ }
1116
1000
  ]
1117
1001
  });
1118
1002
  }
1119
1003
  function createJustBash(workspaceRoot, options = {}) {
1120
- const workspaceMountPoint = options.workspaceMountPoint ?? SANDBOX_WORKSPACE_PATH2;
1121
- const sandboxCwd = options.sandboxCwd ?? workspaceMountPoint;
1122
- validateSandboxPath(sandboxCwd, "Sandbox cwd");
1123
- const configuredMountPoints = [workspaceMountPoint, ...(options.mounts ?? []).map((m) => m.mountPoint)];
1124
- if (!configuredMountPoints.some((mountPoint) => sandboxPathContains(mountPoint, sandboxCwd))) {
1125
- throw new CliError(
1126
- `Sandbox cwd ${sandboxCwd} is not contained by the primary workspace or any configured mount.`
1127
- );
1128
- }
1129
1004
  return new Bash({
1130
- fs: createWorkspaceFs(workspaceRoot, options),
1131
- cwd: sandboxCwd,
1132
- env: { HOME: "/home/user", TERM: "xterm-256color" },
1005
+ fs: createWorkspaceFs(workspaceRoot),
1006
+ cwd: SANDBOX_WORKSPACE_PATH,
1007
+ env: {
1008
+ HOME: "/home/user",
1009
+ TERM: "xterm-256color"
1010
+ },
1133
1011
  python: true,
1134
1012
  javascript: true,
1135
1013
  customCommands: options.git === false ? [] : [createGitCommand(options.git)],
@@ -1226,11 +1104,7 @@ async function runExecCommand(cwd, command, options, signal) {
1226
1104
  const combinedSignal = combineSignals([signal, timeout.signal]);
1227
1105
  const timedOut = () => timeout.signal.aborted && !signal?.aborted;
1228
1106
  try {
1229
- const bash = createJustBash(cwd, {
1230
- workspaceMountPoint: options.workspaceMountPoint,
1231
- sandboxCwd: options.sandboxCwd,
1232
- mounts: options.mounts
1233
- });
1107
+ const bash = createJustBash(cwd);
1234
1108
  const result = await bash.exec(command, { env: options.env, signal: combinedSignal });
1235
1109
  return {
1236
1110
  ...result,
@@ -1479,7 +1353,7 @@ var ExecutorController = class {
1479
1353
  // src/executor/daytona.ts
1480
1354
  import { randomUUID } from "crypto";
1481
1355
  import { mkdir, readdir, readFile, rm, stat as stat3, writeFile } from "fs/promises";
1482
- import { dirname, isAbsolute, relative, resolve as resolve3, sep } from "path";
1356
+ import { dirname, isAbsolute, relative, resolve as resolve2, sep } from "path";
1483
1357
  import {
1484
1358
  Daytona,
1485
1359
  DaytonaProcessExecutionTimeoutError
@@ -1514,7 +1388,7 @@ async function listLocalFiles(root) {
1514
1388
  const files = /* @__PURE__ */ new Map();
1515
1389
  async function visit(directory) {
1516
1390
  for (const entry of await readdir(directory, { withFileTypes: true })) {
1517
- const absolutePath = resolve3(directory, entry.name);
1391
+ const absolutePath = resolve2(directory, entry.name);
1518
1392
  if (entry.isDirectory()) {
1519
1393
  await visit(absolutePath);
1520
1394
  } else if (entry.isFile()) {
@@ -1529,14 +1403,12 @@ async function listLocalFiles(root) {
1529
1403
  var DaytonaSandbox = class {
1530
1404
  #sandbox;
1531
1405
  #workspacePath;
1532
- #workingDirectory;
1533
1406
  #commandTimeoutMs;
1534
1407
  #signal;
1535
1408
  #disposed = false;
1536
1409
  constructor(sandbox, options) {
1537
1410
  this.#sandbox = sandbox;
1538
1411
  this.#workspacePath = options.workspacePath;
1539
- this.#workingDirectory = options.workingDirectory ?? options.workspacePath;
1540
1412
  this.#commandTimeoutMs = options.commandTimeoutMs;
1541
1413
  this.#signal = options.signal;
1542
1414
  }
@@ -1553,7 +1425,7 @@ ${marker}
1553
1425
  const response = await abortable(
1554
1426
  this.#sandbox.process.executeCommand(
1555
1427
  wrapped,
1556
- this.#workingDirectory,
1428
+ this.#workspacePath,
1557
1429
  void 0,
1558
1430
  timeoutSeconds(this.#commandTimeoutMs)
1559
1431
  ),
@@ -1600,7 +1472,7 @@ ${marker}
1600
1472
  this.#disposed = true;
1601
1473
  }
1602
1474
  #resolvePath(path) {
1603
- const target = isAbsolute(path) ? resolve3(path) : resolve3(this.#workingDirectory, path);
1475
+ const target = isAbsolute(path) ? resolve2(path) : resolve2(this.#workspacePath, path);
1604
1476
  if (target !== this.#workspacePath && !target.startsWith(`${this.#workspacePath}/`)) {
1605
1477
  throw new Error(`Path is outside the Daytona workspace: ${path}`);
1606
1478
  }
@@ -1616,7 +1488,7 @@ var DaytonaWorkspaceSynchronizer = class {
1616
1488
  #sandbox;
1617
1489
  #knownFiles = /* @__PURE__ */ new Set();
1618
1490
  constructor(cwd, remoteWorkspacePath, sandbox) {
1619
- this.#cwd = resolve3(cwd);
1491
+ this.#cwd = resolve2(cwd);
1620
1492
  this.#remoteWorkspacePath = remoteWorkspacePath;
1621
1493
  this.#sandbox = sandbox;
1622
1494
  }
@@ -1647,7 +1519,7 @@ var DaytonaWorkspaceSynchronizer = class {
1647
1519
  for (const [path, size] of localFiles) {
1648
1520
  signal?.throwIfAborted();
1649
1521
  await abortable(
1650
- this.#sandbox.fs.uploadFile(resolve3(this.#cwd, path), this.#remotePath(path)),
1522
+ this.#sandbox.fs.uploadFile(resolve2(this.#cwd, path), this.#remotePath(path)),
1651
1523
  signal
1652
1524
  );
1653
1525
  if (this.#knownFiles.has(path)) filesUpdated++;
@@ -1679,7 +1551,7 @@ var DaytonaWorkspaceSynchronizer = class {
1679
1551
  let bytesTransferred = 0;
1680
1552
  for (const path of this.#knownFiles) {
1681
1553
  if (remoteFiles.has(path)) continue;
1682
- await rm(resolve3(this.#cwd, path), { force: true });
1554
+ await rm(resolve2(this.#cwd, path), { force: true });
1683
1555
  filesDeleted++;
1684
1556
  }
1685
1557
  for (const path of remoteFiles) {
@@ -1709,7 +1581,7 @@ var DaytonaWorkspaceSynchronizer = class {
1709
1581
  return `${this.#remoteWorkspacePath}/${path}`;
1710
1582
  }
1711
1583
  #localPath(path) {
1712
- const target = resolve3(this.#cwd, path);
1584
+ const target = resolve2(this.#cwd, path);
1713
1585
  if (target !== this.#cwd && !target.startsWith(`${this.#cwd}${sep}`)) {
1714
1586
  throw new Error(`Daytona returned a path outside the workspace: ${path}`);
1715
1587
  }
@@ -1734,24 +1606,16 @@ var DaytonaExecutorSession = class {
1734
1606
  #disposed = false;
1735
1607
  constructor(remote, options, signal) {
1736
1608
  const workspacePath = options.remoteWorkspacePath ?? DEFAULT_REMOTE_WORKSPACE;
1737
- const relativeCwd = options.relativeCwd ?? ".";
1738
- const workingDirectory = resolve3(workspacePath, relativeCwd);
1739
1609
  this.#remote = remote;
1740
1610
  this.#synchronizer = new DaytonaWorkspaceSynchronizer(options.cwd, workspacePath, remote);
1741
1611
  this.sandbox = new DaytonaSandbox(remote, {
1742
1612
  workspacePath,
1743
- workingDirectory,
1744
1613
  commandTimeoutMs: options.commandTimeoutMs,
1745
1614
  signal
1746
1615
  });
1747
1616
  this.context = {
1748
1617
  kind: "daytona",
1749
1618
  workspacePath,
1750
- ...options.relativeCwd !== void 0 ? {
1751
- workingDirectory,
1752
- cwdPreserved: options.relativeCwd !== null,
1753
- ...options.relativeCwd === null ? { cwdLimitation: "The selected sandbox cwd is on an additional mount; starting at the effective workspace root." } : {}
1754
- } : {},
1755
1619
  isolated: true,
1756
1620
  networkAccess: "full"
1757
1621
  };
@@ -1851,7 +1715,7 @@ Terminus-2 spirit:
1851
1715
  - Summarize final findings and important command results clearly.
1852
1716
 
1853
1717
  Runtime context:
1854
- - Working directory: ${options.sandboxCwd ?? "/workspace"}
1718
+ - Working directory: ${SANDBOX_WORKSPACE_PATH}
1855
1719
  - Current time: ${options.now.toISOString()}
1856
1720
  - Maximum model/tool steps: ${options.maxSteps}
1857
1721
  - Per-command timeout: ${options.commandTimeoutMs} ms
@@ -1874,20 +1738,17 @@ Tool policy:
1874
1738
  }
1875
1739
  function buildDelegatedSystemPrompt(options) {
1876
1740
  const executionBoundary = options.context.isolated ? "Commands run natively inside an isolated remote environment." : "Commands run natively with the current user's permissions and are not isolated.";
1877
- const cwdNote = options.context.cwdLimitation ? `
1878
- - Working-directory limitation: ${options.context.cwdLimitation}` : "";
1879
1741
  return `You are Ollie's delegated execution agent. Complete one bounded task using native workspace tools.
1880
1742
 
1881
1743
  Runtime context:
1882
1744
  - Workspace root: ${options.context.workspacePath}
1883
- - Working directory: ${options.context.workingDirectory ?? options.context.workspacePath}${cwdNote}
1884
1745
  - Current time: ${options.now.toISOString()}
1885
1746
  - Maximum model/tool steps: ${options.maxSteps}
1886
1747
  - Per-command timeout: ${options.commandTimeoutMs} ms
1887
1748
  - ${executionBoundary}
1888
1749
 
1889
1750
  Operating rules:
1890
- - Start at the configured working directory and use relative paths for workspace files.
1751
+ - Start at the workspace root and use relative paths for workspace files.
1891
1752
  - Inspect the relevant state before making changes.
1892
1753
  - Keep changes minimal and directly related to the delegated task.
1893
1754
  - Run appropriate native checks or tests and report their actual results.
@@ -1909,12 +1770,8 @@ async function createWorkspaceTools(options) {
1909
1770
  }
1910
1771
  async function createTools(options) {
1911
1772
  return createWorkspaceTools({
1912
- sandbox: createJustBash(options.cwd, {
1913
- mounts: options.mounts,
1914
- workspaceMountPoint: options.workspaceMountPoint,
1915
- sandboxCwd: options.sandboxCwd
1916
- }),
1917
- destination: options.sandboxCwd ?? options.workspaceMountPoint ?? SANDBOX_WORKSPACE_PATH2,
1773
+ sandbox: createJustBash(options.cwd),
1774
+ destination: SANDBOX_WORKSPACE_PATH,
1918
1775
  maxOutputBytes: options.maxOutputBytes,
1919
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."
1920
1777
  });
@@ -1939,9 +1796,9 @@ var ModelTaskDelegator = class {
1939
1796
  });
1940
1797
  const tools = await createWorkspaceTools({
1941
1798
  sandbox: session.sandbox,
1942
- destination: session.context.workingDirectory ?? session.context.workspacePath,
1799
+ destination: session.context.workspacePath,
1943
1800
  maxOutputBytes: this.options.maxOutputBytes,
1944
- extraInstructions: "Use the native workspace tools to complete the delegated task. Use relative paths from the configured working directory, keep changes focused, and verify the result before reporting it."
1801
+ extraInstructions: "Use the native workspace tools to complete the delegated task. Start at the workspace root, use relative paths, keep changes focused, and verify the result before reporting it."
1945
1802
  });
1946
1803
  const result = await generateText({
1947
1804
  model: this.options.model,
@@ -1989,7 +1846,7 @@ var ModelTaskDelegator = class {
1989
1846
  // src/executor/local.ts
1990
1847
  import { spawn } from "child_process";
1991
1848
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
1992
- 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";
1993
1850
  var LocalSandbox = class {
1994
1851
  #cwd;
1995
1852
  #commandTimeoutMs;
@@ -1998,7 +1855,7 @@ var LocalSandbox = class {
1998
1855
  #processes = /* @__PURE__ */ new Set();
1999
1856
  #disposed = false;
2000
1857
  constructor(options, signal) {
2001
- this.#cwd = resolve4(options.cwd, options.relativeCwd ?? ".");
1858
+ this.#cwd = resolve3(options.cwd);
2002
1859
  this.#commandTimeoutMs = options.commandTimeoutMs;
2003
1860
  this.#env = options.env ?? process.env;
2004
1861
  this.#signal = signal;
@@ -2111,7 +1968,7 @@ var LocalSandbox = class {
2111
1968
  this.#processes.clear();
2112
1969
  }
2113
1970
  #resolvePath(path) {
2114
- return isAbsolute2(path) ? path : resolve4(this.#cwd, path);
1971
+ return isAbsolute2(path) ? path : resolve3(this.#cwd, path);
2115
1972
  }
2116
1973
  #assertActive() {
2117
1974
  if (this.#disposed) throw new Error("Local executor session has been disposed");
@@ -2122,17 +1979,11 @@ var LocalExecutorSession = class {
2122
1979
  context;
2123
1980
  #disposed = false;
2124
1981
  constructor(options, signal) {
2125
- const workspacePath = resolve4(options.cwd);
2126
- const workingDirectory = resolve4(workspacePath, options.relativeCwd ?? ".");
1982
+ const workspacePath = resolve3(options.cwd);
2127
1983
  this.sandbox = new LocalSandbox(options, signal);
2128
1984
  this.context = {
2129
1985
  kind: "local",
2130
1986
  workspacePath,
2131
- ...options.relativeCwd !== void 0 ? {
2132
- workingDirectory,
2133
- cwdPreserved: options.relativeCwd !== null,
2134
- ...options.relativeCwd === null ? { cwdLimitation: "The selected sandbox cwd is on an additional mount; starting at the effective workspace root." } : {}
2135
- } : {},
2136
1987
  isolated: false,
2137
1988
  networkAccess: "full"
2138
1989
  };
@@ -2215,7 +2066,7 @@ function createLanguageModel({
2215
2066
  throw new CliError("OPENAI_API_KEY is required when using the openai provider.");
2216
2067
  }
2217
2068
  const openai = createOpenAI({ apiKey: env.OPENAI_API_KEY });
2218
- return openai(model);
2069
+ return openai.chat(model);
2219
2070
  }
2220
2071
 
2221
2072
  // src/runner.ts
@@ -2226,13 +2077,11 @@ function createExecutorProvider(options) {
2226
2077
  case "local":
2227
2078
  return new LocalExecutorProvider({
2228
2079
  cwd: options.cwd,
2229
- relativeCwd: options.workspaceRelativeCwd,
2230
2080
  commandTimeoutMs: options.commandTimeoutMs
2231
2081
  });
2232
2082
  case "daytona":
2233
2083
  return new DaytonaExecutorProvider({
2234
2084
  cwd: options.cwd,
2235
- relativeCwd: options.workspaceRelativeCwd,
2236
2085
  commandTimeoutMs: options.commandTimeoutMs,
2237
2086
  snapshot: process.env.OLLIE_DAYTONA_SNAPSHOT
2238
2087
  });
@@ -2247,9 +2096,6 @@ async function runOneShot(options) {
2247
2096
  });
2248
2097
  const workspaceTools = await createTools({
2249
2098
  cwd: options.cwd,
2250
- workspaceMountPoint: options.workspaceMountPoint,
2251
- sandboxCwd: options.sandboxCwd,
2252
- mounts: options.mounts,
2253
2099
  maxOutputBytes: options.maxOutputBytes
2254
2100
  });
2255
2101
  const executorProvider = createExecutorProvider(options);
@@ -2276,7 +2122,6 @@ async function runOneShot(options) {
2276
2122
  now: /* @__PURE__ */ new Date(),
2277
2123
  maxSteps: options.maxSteps,
2278
2124
  commandTimeoutMs: options.commandTimeoutMs,
2279
- sandboxCwd: options.sandboxCwd,
2280
2125
  executorInstructions: executorProvider?.systemPrompt
2281
2126
  }),
2282
2127
  prompt: options.prompt,
@@ -2328,7 +2173,7 @@ async function runOneShot(options) {
2328
2173
 
2329
2174
  // src/sandbox/runtime.ts
2330
2175
  import { once } from "events";
2331
- import { posix as posix5 } from "path";
2176
+ import { posix as posix3 } from "path";
2332
2177
  import { createInterface } from "readline";
2333
2178
  function isRecord(value) {
2334
2179
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -2366,7 +2211,7 @@ function requireString(params, name) {
2366
2211
  return value;
2367
2212
  }
2368
2213
  function validateVirtualPath(path) {
2369
- if (!posix5.isAbsolute(path) || path.includes("\\") || path.includes("\0") || posix5.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 === "..")) {
2370
2215
  throw new Error("Path must be a normalized absolute virtual path");
2371
2216
  }
2372
2217
  return path;
@@ -2427,11 +2272,7 @@ async function runWorker(options = {}) {
2427
2272
  const workspaceRoot = options.cwd ?? process.cwd();
2428
2273
  const input = options.input ?? process.stdin;
2429
2274
  const output = options.output ?? process.stdout;
2430
- const bash = createJustBash(workspaceRoot, {
2431
- mounts: options.mounts,
2432
- workspaceMountPoint: options.workspaceMountPoint,
2433
- sandboxCwd: options.sandboxCwd
2434
- });
2275
+ const bash = createJustBash(workspaceRoot);
2435
2276
  const lines = createInterface({ input, crlfDelay: Number.POSITIVE_INFINITY });
2436
2277
  const close = () => lines.close();
2437
2278
  if (options.signal?.aborted) {
@@ -2480,17 +2321,7 @@ function truncateText(input, maxBytes) {
2480
2321
 
2481
2322
  // src/cli-program.ts
2482
2323
  function addExecOptions(command, ndjsonDescription = "emit one JSON result record") {
2483
- return addMountOption(command.option("--cwd <host-path>", "host workspace mounted at /workspace").option("--sandbox-cwd <sandbox-path>", "initial working directory inside the sandbox")).option("--timeout <ms>", "command timeout in milliseconds").option("--max-output <bytes>", "maximum retained output").option("--ndjson", ndjsonDescription);
2484
- }
2485
- function collectOption(value, previous) {
2486
- return [...previous ?? [], value];
2487
- }
2488
- function addMountOption(command) {
2489
- return command.option(
2490
- "--mount <host-path:sandbox-path>",
2491
- "mount a host directory read-write (repeatable)",
2492
- collectOption
2493
- );
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);
2494
2325
  }
2495
2326
  function addAgentOptions(command) {
2496
2327
  return addExecOptions(command, "emit newline-delimited JSON event records").option("--provider <provider>", "model provider", "openai").option("--model <model>", "model id").option(
@@ -2499,20 +2330,12 @@ function addAgentOptions(command) {
2499
2330
  "none"
2500
2331
  ).option("--max-steps <number>", "maximum model/tool steps").option("--no-color", "disable color output");
2501
2332
  }
2502
- function warnImplicitCwd(usedImplicitCwd) {
2503
- if (usedImplicitCwd) {
2504
- process.stderr.write(
2505
- "Warning: omitting --cwd and --sandbox-cwd is deprecated; currently using --cwd .\n"
2506
- );
2507
- }
2508
- }
2509
2333
  async function runAgent(promptParts, options) {
2510
2334
  const prompt = promptParts.join(" ").trim();
2511
2335
  if (!prompt) {
2512
2336
  throw new CliError('A prompt is required. Example: ollie agent "summarize this repository"');
2513
2337
  }
2514
2338
  const config = await resolveRunConfig(options);
2515
- warnImplicitCwd(config.usedImplicitCwd);
2516
2339
  const renderer = createRenderer({ ndjson: config.ndjson, color: config.color });
2517
2340
  const controller = new AbortController();
2518
2341
  const onSigint = () => controller.abort();
@@ -2535,7 +2358,6 @@ async function runExec(commandParts, options) {
2535
2358
  throw new CliError('A command is required. Example: ollie "pwd && ls -la"');
2536
2359
  }
2537
2360
  const config = await resolveRunConfig(options);
2538
- warnImplicitCwd(config.usedImplicitCwd);
2539
2361
  const controller = new AbortController();
2540
2362
  let signalExitCode;
2541
2363
  const onSigint = () => {
@@ -2554,10 +2376,7 @@ async function runExec(commandParts, options) {
2554
2376
  shellCommand,
2555
2377
  {
2556
2378
  timeoutMs: config.commandTimeoutMs,
2557
- env: process.env,
2558
- workspaceMountPoint: config.workspaceMountPoint,
2559
- sandboxCwd: config.sandboxCwd,
2560
- mounts: config.mounts
2379
+ env: process.env
2561
2380
  },
2562
2381
  controller.signal
2563
2382
  );
@@ -2596,8 +2415,6 @@ async function runExec(commandParts, options) {
2596
2415
  }
2597
2416
  }
2598
2417
  async function runSandbox(options) {
2599
- const config = await resolveRunConfig(options);
2600
- warnImplicitCwd(config.usedImplicitCwd);
2601
2418
  const controller = new AbortController();
2602
2419
  let signalExitCode;
2603
2420
  const onSigint = () => {
@@ -2612,10 +2429,7 @@ async function runSandbox(options) {
2612
2429
  process.once("SIGTERM", onSigterm);
2613
2430
  try {
2614
2431
  await runWorker({
2615
- cwd: config.cwd,
2616
- workspaceMountPoint: config.workspaceMountPoint,
2617
- sandboxCwd: config.sandboxCwd,
2618
- mounts: config.mounts,
2432
+ cwd: options.cwd,
2619
2433
  signal: controller.signal
2620
2434
  });
2621
2435
  if (signalExitCode !== void 0) {
@@ -2644,10 +2458,7 @@ function createProgram() {
2644
2458
  agent.action(async (prompt, options) => {
2645
2459
  await runAgent(prompt, options);
2646
2460
  });
2647
- const sandbox = addMountOption(
2648
- program.command("sandbox").description("run the persistent sandbox NDJSON command interface").option("--cwd <host-path>", "host workspace mounted at /workspace").option("--sandbox-cwd <sandbox-path>", "initial working directory inside the sandbox")
2649
- );
2650
- sandbox.action(async (options) => {
2461
+ program.command("sandbox").description("run the persistent sandbox NDJSON command interface").option("--cwd <path>", "working directory").action(async (options) => {
2651
2462
  await runSandbox(options);
2652
2463
  });
2653
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) => {
@@ -2657,13 +2468,7 @@ function createProgram() {
2657
2468
  return program;
2658
2469
  }
2659
2470
  var RESERVED_SUBCOMMANDS = /* @__PURE__ */ new Set(["agent", "sandbox", "doctor"]);
2660
- var ROOT_OPTIONS_WITH_VALUES = /* @__PURE__ */ new Set([
2661
- "--cwd",
2662
- "--sandbox-cwd",
2663
- "--mount",
2664
- "--timeout",
2665
- "--max-output"
2666
- ]);
2471
+ var ROOT_OPTIONS_WITH_VALUES = /* @__PURE__ */ new Set(["--cwd", "--timeout", "--max-output"]);
2667
2472
  function rootBoundaryPrecedesCommand(args) {
2668
2473
  for (let index = 0; index < args.length; index += 1) {
2669
2474
  const token = args[index] ?? "";