@madarco/agentbox 0.8.0 → 0.9.0
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/{_cloud-attach-T727ZPRV.js → _cloud-attach-ZXBCNWJX.js} +4 -4
- package/dist/{chunk-67N47KUS.js → chunk-BXQMIEHC.js} +106 -31
- package/dist/chunk-BXQMIEHC.js.map +1 -0
- package/dist/{chunk-FODMEHD3.js → chunk-GU5LW4B5.js} +341 -25
- package/dist/chunk-GU5LW4B5.js.map +1 -0
- package/dist/{chunk-BGK32PZE.js → chunk-KL36BRN4.js} +2 -2
- package/dist/chunk-KL36BRN4.js.map +1 -0
- package/dist/chunk-MTVI44DW.js +662 -0
- package/dist/chunk-MTVI44DW.js.map +1 -0
- package/dist/{chunk-6OZDFNBF.js → chunk-NCJP5MTN.js} +201 -44
- package/dist/chunk-NCJP5MTN.js.map +1 -0
- package/dist/{dist-LOZBWMBF.js → dist-32EZBYG4.js} +9 -3
- package/dist/{dist-L4LCG5SJ.js → dist-CX5CGVEB.js} +4 -4
- package/dist/{dist-ZODPD2I6.js → dist-GDHP34ZK.js} +8 -10
- package/dist/dist-GDHP34ZK.js.map +1 -0
- package/dist/dist-XML54CNB.js +849 -0
- package/dist/dist-XML54CNB.js.map +1 -0
- package/dist/index.js +636 -340
- package/dist/index.js.map +1 -1
- package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js → prepared-state-CL4CWXQA-H5THETIM.js} +2 -2
- package/package.json +7 -5
- package/runtime/docker/packages/ctl/dist/bin.cjs +98 -29
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
- package/runtime/hetzner/agentbox-vnc-start +15 -1
- package/runtime/hetzner/ctl.cjs +98 -29
- package/runtime/relay/bin.cjs +229 -37
- package/runtime/vercel/agentbox-checkpoint-cleanup +52 -0
- package/runtime/vercel/agentbox-codex-hooks.json +68 -0
- package/runtime/vercel/agentbox-open +28 -0
- package/runtime/vercel/agentbox-setup-skill.md +196 -0
- package/runtime/vercel/agentbox-vnc-start +91 -0
- package/runtime/vercel/claude-managed-settings.json +115 -0
- package/runtime/vercel/ctl.cjs +23466 -0
- package/runtime/vercel/custom-system-CLAUDE.md +50 -0
- package/runtime/vercel/gh-shim +263 -0
- package/runtime/vercel/git-shim +131 -0
- package/runtime/vercel/scripts/provision.sh +274 -0
- package/dist/chunk-67N47KUS.js.map +0 -1
- package/dist/chunk-6OZDFNBF.js.map +0 -1
- package/dist/chunk-BGK32PZE.js.map +0 -1
- package/dist/chunk-FODMEHD3.js.map +0 -1
- package/dist/dist-ZODPD2I6.js.map +0 -1
- /package/dist/{_cloud-attach-T727ZPRV.js.map → _cloud-attach-ZXBCNWJX.js.map} +0 -0
- /package/dist/{dist-LOZBWMBF.js.map → dist-32EZBYG4.js.map} +0 -0
- /package/dist/{dist-L4LCG5SJ.js.map → dist-CX5CGVEB.js.map} +0 -0
- /package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js.map → prepared-state-CL4CWXQA-H5THETIM.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -16,18 +16,27 @@ import {
|
|
|
16
16
|
secretsPath as secretsPath2,
|
|
17
17
|
syncFirewallSource
|
|
18
18
|
} from "./chunk-I24B6AXR.js";
|
|
19
|
+
import {
|
|
20
|
+
detectSbx,
|
|
21
|
+
ensureVercelCredentials,
|
|
22
|
+
installSbxHint,
|
|
23
|
+
maskKey as maskKey3,
|
|
24
|
+
readVercelCredStatus,
|
|
25
|
+
secretsPath as secretsPath3
|
|
26
|
+
} from "./chunk-MTVI44DW.js";
|
|
19
27
|
import {
|
|
20
28
|
agentSpecsForCloud,
|
|
21
29
|
ensureAgentVolumesForCloud,
|
|
22
30
|
listCloudCheckpoints,
|
|
23
31
|
resolveCloudCheckpoint,
|
|
24
32
|
seedAgentVolumesIfFresh
|
|
25
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-BXQMIEHC.js";
|
|
26
34
|
import {
|
|
27
35
|
ADVANCED_HINT_GROUPS,
|
|
28
36
|
NEW_BOX_ID,
|
|
29
37
|
NEW_BOX_LABEL,
|
|
30
38
|
buildCloudAttachInnerCommand,
|
|
39
|
+
clipboardCaptureAvailable,
|
|
31
40
|
cloudAgentAttach,
|
|
32
41
|
createMenuLines,
|
|
33
42
|
detectHostTerminal,
|
|
@@ -36,15 +45,20 @@ import {
|
|
|
36
45
|
lifecycleMenuLines,
|
|
37
46
|
loadPtyBackend,
|
|
38
47
|
menuLines,
|
|
48
|
+
pasteHostClipboardImage,
|
|
49
|
+
popTerminalTitle,
|
|
39
50
|
postAnswer,
|
|
40
51
|
providerForBox,
|
|
41
52
|
providerForCreate,
|
|
53
|
+
pushTerminalTitle,
|
|
42
54
|
renderFooter,
|
|
43
55
|
runWrappedAttach,
|
|
56
|
+
setTerminalTitle,
|
|
44
57
|
sidebarLines,
|
|
45
58
|
statusLine,
|
|
59
|
+
stripTitleGlyph,
|
|
46
60
|
subscribePrompts
|
|
47
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-GU5LW4B5.js";
|
|
48
62
|
import {
|
|
49
63
|
AmbiguousBoxError,
|
|
50
64
|
BOX_STATUS_EVENT,
|
|
@@ -109,6 +123,7 @@ import {
|
|
|
109
123
|
hashRpcParams,
|
|
110
124
|
hostBackupHasCredentials,
|
|
111
125
|
ideProfile,
|
|
126
|
+
injectPrCreateHead,
|
|
112
127
|
inspectBox,
|
|
113
128
|
installPortless,
|
|
114
129
|
killShellSession,
|
|
@@ -184,7 +199,7 @@ import {
|
|
|
184
199
|
waitForTmuxPaneContent,
|
|
185
200
|
warmUpClaudeCredentials,
|
|
186
201
|
writeJob
|
|
187
|
-
} from "./chunk-
|
|
202
|
+
} from "./chunk-NCJP5MTN.js";
|
|
188
203
|
import {
|
|
189
204
|
DEFAULT_BOX_IMAGE,
|
|
190
205
|
STATE_DIR,
|
|
@@ -192,15 +207,15 @@ import {
|
|
|
192
207
|
imageInfo,
|
|
193
208
|
readState,
|
|
194
209
|
resolveBoxRef
|
|
195
|
-
} from "./chunk-
|
|
210
|
+
} from "./chunk-KL36BRN4.js";
|
|
196
211
|
import "./chunk-G3H2L3O2.js";
|
|
197
212
|
|
|
198
213
|
// src/version.ts
|
|
199
|
-
var AGENTBOX_VERSION = true ? "0.
|
|
200
|
-
var AGENTBOX_COMMIT = true ? "
|
|
214
|
+
var AGENTBOX_VERSION = true ? "0.9.0" : "0.0.0-dev";
|
|
215
|
+
var AGENTBOX_COMMIT = true ? "78c06a7" : "dev";
|
|
201
216
|
|
|
202
217
|
// src/index.ts
|
|
203
|
-
import { Command as
|
|
218
|
+
import { Command as Command45 } from "commander";
|
|
204
219
|
|
|
205
220
|
// src/engine-override.ts
|
|
206
221
|
async function applyEngineOverrideAtStartup() {
|
|
@@ -666,12 +681,23 @@ function buildPromptArgs(agentKind, prompt, userArgs) {
|
|
|
666
681
|
return resolveAgentLauncher(agentKind).buildArgs(prompt, userArgs);
|
|
667
682
|
}
|
|
668
683
|
|
|
684
|
+
// src/lib/queue/parse-max-option.ts
|
|
685
|
+
function parseMaxOption(flag, raw) {
|
|
686
|
+
if (raw === void 0) return void 0;
|
|
687
|
+
const n = Number(raw);
|
|
688
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
689
|
+
throw new Error(`${flag}: expected a positive integer, got "${raw}"`);
|
|
690
|
+
}
|
|
691
|
+
return n;
|
|
692
|
+
}
|
|
693
|
+
|
|
669
694
|
// src/lib/queue/submit.ts
|
|
670
695
|
import { randomBytes } from "crypto";
|
|
671
696
|
import { request as httpRequest } from "http";
|
|
672
697
|
async function submitQueueJob(input) {
|
|
673
698
|
const cfg = await loadQueueConfig();
|
|
674
699
|
const ceiling = typeof input.maxRunningOverride === "number" && input.maxRunningOverride > 0 ? input.maxRunningOverride : cfg.maxConcurrent;
|
|
700
|
+
const maxWorking = typeof input.maxWorkingOverride === "number" && input.maxWorkingOverride > 0 ? input.maxWorkingOverride : void 0;
|
|
675
701
|
const id = newJobId();
|
|
676
702
|
const job = {
|
|
677
703
|
id,
|
|
@@ -683,6 +709,7 @@ async function submitQueueJob(input) {
|
|
|
683
709
|
agentArgs: input.agentArgs,
|
|
684
710
|
createOpts: input.createOpts,
|
|
685
711
|
maxConcurrent: ceiling,
|
|
712
|
+
...maxWorking !== void 0 ? { maxWorking } : {},
|
|
686
713
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
687
714
|
logPath: queueLogPath(id)
|
|
688
715
|
};
|
|
@@ -1143,7 +1170,7 @@ async function dirSizeCapped(dir, cap) {
|
|
|
1143
1170
|
|
|
1144
1171
|
// src/lib/carry-gate.ts
|
|
1145
1172
|
async function runCarryGate(args) {
|
|
1146
|
-
const
|
|
1173
|
+
const log45 = args.onLog ?? (() => {
|
|
1147
1174
|
});
|
|
1148
1175
|
const yamlPath = join4(args.projectRoot, "agentbox.yaml");
|
|
1149
1176
|
const items = await loadCarrySection(yamlPath);
|
|
@@ -1165,7 +1192,7 @@ async function runCarryGate(args) {
|
|
|
1165
1192
|
});
|
|
1166
1193
|
if (decision === "cancel") return { decision: "cancel" };
|
|
1167
1194
|
if (decision === "skip-this-run") {
|
|
1168
|
-
|
|
1195
|
+
log45(`carry: skipped for this box (${String(resolved.entries.length)} entry/entries not copied)`);
|
|
1169
1196
|
return { decision: "skip", entries: [] };
|
|
1170
1197
|
}
|
|
1171
1198
|
return { decision: "approve", entries: resolved.entries };
|
|
@@ -1205,6 +1232,63 @@ async function resolveFromBranch(ref, opts) {
|
|
|
1205
1232
|
}
|
|
1206
1233
|
return ref;
|
|
1207
1234
|
}
|
|
1235
|
+
var UseBranchError = class extends Error {
|
|
1236
|
+
constructor(message) {
|
|
1237
|
+
super(message);
|
|
1238
|
+
this.name = "UseBranchError";
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
async function resolveUseBranch(name, opts) {
|
|
1242
|
+
if (!name || name.length === 0) return void 0;
|
|
1243
|
+
const remote = opts.remote ?? "origin";
|
|
1244
|
+
await execa("git", ["-C", opts.repo, "fetch", "--quiet", remote, name], {
|
|
1245
|
+
reject: false
|
|
1246
|
+
});
|
|
1247
|
+
const exists = await execa(
|
|
1248
|
+
"git",
|
|
1249
|
+
["-C", opts.repo, "show-ref", "--verify", "--quiet", `refs/heads/${name}`],
|
|
1250
|
+
{ reject: false }
|
|
1251
|
+
);
|
|
1252
|
+
if (exists.exitCode !== 0) {
|
|
1253
|
+
throw new UseBranchError(
|
|
1254
|
+
`--use-branch: no local branch "${name}" in ${opts.repo}. Create or check it out on the host first (--use-branch reuses an existing branch; use --from-branch to fork a new box branch from a ref).`
|
|
1255
|
+
);
|
|
1256
|
+
}
|
|
1257
|
+
return name;
|
|
1258
|
+
}
|
|
1259
|
+
async function currentHostBranch(repo) {
|
|
1260
|
+
const r = await execa("git", ["-C", repo, "rev-parse", "--abbrev-ref", "HEAD"], {
|
|
1261
|
+
reject: false
|
|
1262
|
+
});
|
|
1263
|
+
if (r.exitCode !== 0) return void 0;
|
|
1264
|
+
const branch = r.stdout.trim();
|
|
1265
|
+
if (!branch || branch === "HEAD") return void 0;
|
|
1266
|
+
return branch;
|
|
1267
|
+
}
|
|
1268
|
+
async function resolveBranchSelection(opts) {
|
|
1269
|
+
if (opts.useBranch && opts.fromBranch) {
|
|
1270
|
+
throw new UseBranchError(
|
|
1271
|
+
"--use-branch and --from-branch are mutually exclusive: --use-branch reuses an existing branch, --from-branch forks a new box branch from a base ref. Pass only one."
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1274
|
+
if (opts.useBranch) {
|
|
1275
|
+
return { useBranch: await resolveUseBranch(opts.useBranch, { repo: opts.repo }) };
|
|
1276
|
+
}
|
|
1277
|
+
if (opts.fromBranch) {
|
|
1278
|
+
return { fromBranch: await resolveFromBranch(opts.fromBranch, { repo: opts.repo }) };
|
|
1279
|
+
}
|
|
1280
|
+
if (opts.providerName !== "docker" && opts.cloudUseCurrentBranch) {
|
|
1281
|
+
const current = await currentHostBranch(opts.repo);
|
|
1282
|
+
if (current) {
|
|
1283
|
+
opts.log?.(`cloud.useCurrentBranch: starting box on host branch "${current}"`);
|
|
1284
|
+
return { useBranch: current };
|
|
1285
|
+
}
|
|
1286
|
+
opts.log?.(
|
|
1287
|
+
"cloud.useCurrentBranch is set but host HEAD is detached; forking a fresh branch instead"
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
return {};
|
|
1291
|
+
}
|
|
1208
1292
|
|
|
1209
1293
|
// src/session-teleport/claude.ts
|
|
1210
1294
|
import { mkdir, mkdtemp, readdir, readFile as readFile2, stat as stat3, writeFile } from "fs/promises";
|
|
@@ -1784,7 +1868,7 @@ async function maybeRunSetupWizard(args) {
|
|
|
1784
1868
|
}
|
|
1785
1869
|
}
|
|
1786
1870
|
const go = await confirm2({
|
|
1787
|
-
message: "New project
|
|
1871
|
+
message: "New project: run setup wizard? Will install dependencies and setup agentbox.yaml",
|
|
1788
1872
|
initialValue: true
|
|
1789
1873
|
});
|
|
1790
1874
|
if (isCancel3(go) || !go) return { action: "proceed", envFilesToImport };
|
|
@@ -1830,14 +1914,6 @@ function passthroughFlags(opts) {
|
|
|
1830
1914
|
function reattachRef(r) {
|
|
1831
1915
|
return typeof r.projectIndex === "number" ? String(r.projectIndex) : r.name;
|
|
1832
1916
|
}
|
|
1833
|
-
function parseMaxRunningOption(raw) {
|
|
1834
|
-
if (raw === void 0) return void 0;
|
|
1835
|
-
const n = Number(raw);
|
|
1836
|
-
if (!Number.isInteger(n) || n <= 0) {
|
|
1837
|
-
throw new Error(`--max-running: expected a positive integer, got "${raw}"`);
|
|
1838
|
-
}
|
|
1839
|
-
return n;
|
|
1840
|
-
}
|
|
1841
1917
|
function pickCreateOpts(opts) {
|
|
1842
1918
|
return {
|
|
1843
1919
|
workspace: opts.workspace,
|
|
@@ -1865,6 +1941,8 @@ function logPrune(rebuild) {
|
|
|
1865
1941
|
}
|
|
1866
1942
|
var RELAY_HOST_URL = `http://127.0.0.1:${String(DEFAULT_RELAY_PORT)}`;
|
|
1867
1943
|
async function attachClaudeWrapped(box, sessionName, reattach, onError, openIn) {
|
|
1944
|
+
const provider = await providerForBox(box);
|
|
1945
|
+
const canPaste = await clipboardCaptureAvailable();
|
|
1868
1946
|
const code = await runWrappedAttach({
|
|
1869
1947
|
container: box.container,
|
|
1870
1948
|
dockerArgv: buildClaudeAttachArgv(box.container, sessionName),
|
|
@@ -1875,7 +1953,8 @@ async function attachClaudeWrapped(box, sessionName, reattach, onError, openIn)
|
|
|
1875
1953
|
mode: "claude",
|
|
1876
1954
|
detachNotice: formatDetachNotice(reattach),
|
|
1877
1955
|
onError,
|
|
1878
|
-
openIn
|
|
1956
|
+
openIn,
|
|
1957
|
+
onPasteImage: canPaste ? () => pasteHostClipboardImage(provider, box) : void 0
|
|
1879
1958
|
});
|
|
1880
1959
|
process.exit(code);
|
|
1881
1960
|
}
|
|
@@ -1967,15 +2046,21 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
1967
2046
|
).option(
|
|
1968
2047
|
"--from-branch <ref>",
|
|
1969
2048
|
"base the box's per-box branch on this ref (branch / tag / SHA) instead of HEAD. Branch/tag names are fetched from origin first."
|
|
2049
|
+
).option(
|
|
2050
|
+
"-b, --use-branch <name>",
|
|
2051
|
+
"reuse an existing branch directly instead of forking agentbox/<box-name>. Commits/pushes flow straight to it. Docker fails if the host already has it checked out. Mutually exclusive with --from-branch."
|
|
1970
2052
|
).option(
|
|
1971
2053
|
"-v, --verbose",
|
|
1972
2054
|
"bypass the spinner and stream raw provider output (docker build / Daytona snapshot create) to stderr. The same content always lands in ~/.agentbox/logs/claude.log."
|
|
1973
|
-
).option("--attach-in <mode>", ATTACH_IN_HELP).option("--inline", INLINE_HELP).option("-
|
|
2055
|
+
).option("--attach-in <mode>", ATTACH_IN_HELP).option("--inline", INLINE_HELP).option("-d, --no-attach", NO_ATTACH_HELP).option(
|
|
1974
2056
|
"-i, --initial-prompt <text>",
|
|
1975
2057
|
"seed the claude session with this initial user turn and run in background (no attach). Jobs go through the host-wide queue (queue.maxConcurrent). NOTE: this is NOT claude's own `-p` headless print mode \u2014 for that, pass `-- -p ...`."
|
|
1976
2058
|
).option(
|
|
1977
2059
|
"--max-running <n>",
|
|
1978
2060
|
"per-invocation override of queue.maxConcurrent; only honored when `-i` is set"
|
|
2061
|
+
).option(
|
|
2062
|
+
"--max-working <n>",
|
|
2063
|
+
"per-invocation override of queue.maxWorking; only honored when `-i` is set"
|
|
1979
2064
|
).option(
|
|
1980
2065
|
"-c, --continue",
|
|
1981
2066
|
"teleport the most recent host Claude Code session for this cwd into the box and resume from it"
|
|
@@ -2046,7 +2131,8 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2046
2131
|
}
|
|
2047
2132
|
throw err;
|
|
2048
2133
|
}
|
|
2049
|
-
const maxRunningOverride =
|
|
2134
|
+
const maxRunningOverride = parseMaxOption("--max-running", opts.maxRunning);
|
|
2135
|
+
const maxWorkingOverride = parseMaxOption("--max-working", opts.maxWorking);
|
|
2050
2136
|
const result = await submitQueueJob({
|
|
2051
2137
|
agent: "claude-code",
|
|
2052
2138
|
boxName: opts.name ?? "",
|
|
@@ -2054,7 +2140,8 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2054
2140
|
prompt: opts.initialPrompt,
|
|
2055
2141
|
agentArgs: claudeArgs,
|
|
2056
2142
|
createOpts: pickCreateOpts(opts),
|
|
2057
|
-
maxRunningOverride
|
|
2143
|
+
maxRunningOverride,
|
|
2144
|
+
maxWorkingOverride
|
|
2058
2145
|
});
|
|
2059
2146
|
outro2(
|
|
2060
2147
|
`job ${result.job.id} queued (${String(result.runningCount)}/${String(result.maxConcurrent)} running); log: ${result.job.logPath}`
|
|
@@ -2112,10 +2199,18 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2112
2199
|
effectiveClaudeArgs = buildPromptArgs("claude-code", wiz.initialPrompt, claudeArgs);
|
|
2113
2200
|
}
|
|
2114
2201
|
let fromBranch;
|
|
2202
|
+
let useBranch;
|
|
2115
2203
|
try {
|
|
2116
|
-
fromBranch = await
|
|
2204
|
+
({ fromBranch, useBranch } = await resolveBranchSelection({
|
|
2205
|
+
useBranch: opts.useBranch,
|
|
2206
|
+
fromBranch: opts.fromBranch,
|
|
2207
|
+
repo: opts.workspace,
|
|
2208
|
+
providerName,
|
|
2209
|
+
cloudUseCurrentBranch: cfg.effective.cloud.useCurrentBranch,
|
|
2210
|
+
log: (m) => cmdLog.write(m)
|
|
2211
|
+
}));
|
|
2117
2212
|
} catch (err) {
|
|
2118
|
-
if (err instanceof FromBranchError) {
|
|
2213
|
+
if (err instanceof FromBranchError || err instanceof UseBranchError) {
|
|
2119
2214
|
log9.error(err.message);
|
|
2120
2215
|
cmdLog.close();
|
|
2121
2216
|
process.exit(2);
|
|
@@ -2139,6 +2234,7 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2139
2234
|
vnc: { enabled: cfg.effective.box.vnc },
|
|
2140
2235
|
limits: resolveLimits(cfg.effective.box, opts),
|
|
2141
2236
|
fromBranch,
|
|
2237
|
+
useBranch,
|
|
2142
2238
|
projectRoot
|
|
2143
2239
|
},
|
|
2144
2240
|
binary: "claude",
|
|
@@ -2182,6 +2278,7 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2182
2278
|
useSnapshot,
|
|
2183
2279
|
checkpointRef,
|
|
2184
2280
|
fromBranch,
|
|
2281
|
+
useBranch,
|
|
2185
2282
|
image: cfg.effective.box.image,
|
|
2186
2283
|
claudeConfig: { isolate: cfg.effective.box.isolateClaudeConfig },
|
|
2187
2284
|
claudeEnv: resolved.env,
|
|
@@ -2438,7 +2535,7 @@ var claudeStartCommand = new Command2("start").description(
|
|
|
2438
2535
|
).option("--session-name <name>", "tmux session name (default from config; built-in: claude)").option(
|
|
2439
2536
|
"--no-sync-config",
|
|
2440
2537
|
"skip rsyncing the host's ~/.claude into the box's volume before starting (faster; use existing in-box state)"
|
|
2441
|
-
).option("--attach-in <mode>", ATTACH_IN_HELP).option("-i, --inline", INLINE_HELP).option("-
|
|
2538
|
+
).option("--attach-in <mode>", ATTACH_IN_HELP).option("-i, --inline", INLINE_HELP).option("-d, --no-attach", NO_ATTACH_HELP).option(
|
|
2442
2539
|
"-c, --continue",
|
|
2443
2540
|
"teleport the most recent host Claude Code session for this cwd into the box and resume"
|
|
2444
2541
|
).option(
|
|
@@ -2554,6 +2651,17 @@ claudeCommand.addCommand(claudeLoginCommand);
|
|
|
2554
2651
|
// src/commands/checkpoint.ts
|
|
2555
2652
|
import { confirm as confirm4, isCancel as isCancel5, log as log10 } from "@clack/prompts";
|
|
2556
2653
|
import { Command as Command3 } from "commander";
|
|
2654
|
+
var CLOUD_BACKENDS = ["daytona", "hetzner", "vercel"];
|
|
2655
|
+
async function cloudProviderFor(backend) {
|
|
2656
|
+
switch (backend) {
|
|
2657
|
+
case "daytona":
|
|
2658
|
+
return (await import("./dist-CX5CGVEB.js")).daytonaProvider;
|
|
2659
|
+
case "hetzner":
|
|
2660
|
+
return (await import("./dist-GDHP34ZK.js")).hetznerProvider;
|
|
2661
|
+
case "vercel":
|
|
2662
|
+
return (await import("./dist-XML54CNB.js")).vercelProvider;
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2557
2665
|
var CHECKPOINT_NOTICE = "Checkpoint in progress \u2014 the box will be unresponsive for a moment";
|
|
2558
2666
|
var CHECKPOINT_NOTICE_TTL_MS = 66e4;
|
|
2559
2667
|
async function projectRootFor(cwd, recordRoot) {
|
|
@@ -2639,10 +2747,16 @@ var lsSub = new Command3("ls").description("List this project's checkpoints (bot
|
|
|
2639
2747
|
const projectRoot = (await findProjectRoot(process.cwd())).root;
|
|
2640
2748
|
const cfg = await loadEffectiveConfig(projectRoot);
|
|
2641
2749
|
const defDocker = resolveDefaultCheckpoint(cfg.effective, "docker");
|
|
2642
|
-
const defDaytona = resolveDefaultCheckpoint(cfg.effective, "daytona");
|
|
2643
2750
|
const dockerList = await listCheckpoints(projectRoot);
|
|
2644
|
-
const
|
|
2645
|
-
|
|
2751
|
+
const cloudLists = await Promise.all(
|
|
2752
|
+
CLOUD_BACKENDS.map(async (backend) => ({
|
|
2753
|
+
backend,
|
|
2754
|
+
def: resolveDefaultCheckpoint(cfg.effective, backend),
|
|
2755
|
+
items: await listCloudCheckpoints(projectRoot, backend)
|
|
2756
|
+
}))
|
|
2757
|
+
);
|
|
2758
|
+
const totalCloud = cloudLists.reduce((n, c) => n + c.items.length, 0);
|
|
2759
|
+
if (dockerList.length === 0 && totalCloud === 0) {
|
|
2646
2760
|
process.stdout.write(`no checkpoints for ${projectRoot}
|
|
2647
2761
|
`);
|
|
2648
2762
|
return;
|
|
@@ -2654,12 +2768,14 @@ var lsSub = new Command3("ls").description("List this project's checkpoints (bot
|
|
|
2654
2768
|
`
|
|
2655
2769
|
);
|
|
2656
2770
|
}
|
|
2657
|
-
for (const
|
|
2658
|
-
const
|
|
2659
|
-
|
|
2660
|
-
|
|
2771
|
+
for (const { backend, def, items } of cloudLists) {
|
|
2772
|
+
for (const c of items) {
|
|
2773
|
+
const flag = c.name === def ? " *default" : "";
|
|
2774
|
+
process.stdout.write(
|
|
2775
|
+
`${c.name} ${backend} (snapshot) from ${c.manifest.sourceBoxName} ${c.manifest.createdAt}${flag}
|
|
2661
2776
|
`
|
|
2662
|
-
|
|
2777
|
+
);
|
|
2778
|
+
}
|
|
2663
2779
|
}
|
|
2664
2780
|
} catch (err) {
|
|
2665
2781
|
handleLifecycleError(err);
|
|
@@ -2667,13 +2783,16 @@ var lsSub = new Command3("ls").description("List this project's checkpoints (bot
|
|
|
2667
2783
|
});
|
|
2668
2784
|
var setDefaultSub = new Command3("set-default").description("Pin a checkpoint as the project default (box.defaultCheckpoint)").argument("[ref]", "checkpoint name (omit with --clear)").option("--clear", "unset the project default instead of setting one").option(
|
|
2669
2785
|
"--provider <name>",
|
|
2670
|
-
"set the default for only this provider (docker|daytona); without it, sets the cross-provider fallback"
|
|
2786
|
+
"set the default for only this provider (docker|daytona|hetzner|vercel); without it, sets the cross-provider fallback"
|
|
2671
2787
|
).action(async (ref, opts) => {
|
|
2672
2788
|
try {
|
|
2673
2789
|
const projectRoot = (await findProjectRoot(process.cwd())).root;
|
|
2674
2790
|
const providerArg = opts.provider;
|
|
2675
|
-
|
|
2676
|
-
|
|
2791
|
+
const knownProviders = ["docker", ...CLOUD_BACKENDS];
|
|
2792
|
+
if (providerArg !== void 0 && !knownProviders.includes(providerArg)) {
|
|
2793
|
+
throw new Error(
|
|
2794
|
+
`unknown provider '${opts.provider ?? ""}' (known: ${knownProviders.join(", ")})`
|
|
2795
|
+
);
|
|
2677
2796
|
}
|
|
2678
2797
|
const configKey = defaultCheckpointConfigKey(providerArg);
|
|
2679
2798
|
const label = providerArg ? `${providerArg} default checkpoint` : "project default checkpoint";
|
|
@@ -2692,11 +2811,16 @@ var setDefaultSub = new Command3("set-default").description("Pin a checkpoint as
|
|
|
2692
2811
|
if (ref === void 0) {
|
|
2693
2812
|
throw new Error("missing <ref> (or pass --clear to unset the default)");
|
|
2694
2813
|
}
|
|
2695
|
-
const
|
|
2696
|
-
|
|
2697
|
-
const
|
|
2698
|
-
|
|
2699
|
-
|
|
2814
|
+
const dockerHit = (providerArg === void 0 || providerArg === "docker") && (await listCheckpoints(projectRoot)).some((c) => c.name === ref);
|
|
2815
|
+
let cloudHit = false;
|
|
2816
|
+
for (const backend of CLOUD_BACKENDS) {
|
|
2817
|
+
if (providerArg !== void 0 && providerArg !== backend) continue;
|
|
2818
|
+
if (await resolveCloudCheckpoint(projectRoot, backend, ref)) {
|
|
2819
|
+
cloudHit = true;
|
|
2820
|
+
break;
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
if (!dockerHit && !cloudHit) {
|
|
2700
2824
|
throw new Error(`checkpoint not found: ${ref} (see \`agentbox checkpoint ls\`)`);
|
|
2701
2825
|
}
|
|
2702
2826
|
const r = await setConfigValue("project", configKey, ref, projectRoot);
|
|
@@ -2709,8 +2833,12 @@ var setDefaultSub = new Command3("set-default").description("Pin a checkpoint as
|
|
|
2709
2833
|
var rmSub = new Command3("rm").description("Delete a checkpoint (any provider that has it)").argument("<ref>", "checkpoint name").option("-y, --yes", "skip the confirmation prompt").option("--provider <name>", "delete only from this provider's store (default: all)").action(async (ref, opts) => {
|
|
2710
2834
|
try {
|
|
2711
2835
|
const projectRoot = (await findProjectRoot(process.cwd())).root;
|
|
2712
|
-
const
|
|
2713
|
-
const
|
|
2836
|
+
const wantDocker = !opts.provider || opts.provider === "docker";
|
|
2837
|
+
const cloudHits = [];
|
|
2838
|
+
for (const backend of CLOUD_BACKENDS) {
|
|
2839
|
+
if (opts.provider && opts.provider !== backend) continue;
|
|
2840
|
+
if (await resolveCloudCheckpoint(projectRoot, backend, ref)) cloudHits.push(backend);
|
|
2841
|
+
}
|
|
2714
2842
|
if (!opts.yes) {
|
|
2715
2843
|
const ok = await confirm4({ message: `Delete checkpoint ${ref}?`, initialValue: false });
|
|
2716
2844
|
if (isCancel5(ok) || !ok) {
|
|
@@ -2719,7 +2847,7 @@ var rmSub = new Command3("rm").description("Delete a checkpoint (any provider th
|
|
|
2719
2847
|
}
|
|
2720
2848
|
}
|
|
2721
2849
|
let any = false;
|
|
2722
|
-
if (
|
|
2850
|
+
if (wantDocker) {
|
|
2723
2851
|
const removed = await removeCheckpoint(projectRoot, ref);
|
|
2724
2852
|
if (removed) {
|
|
2725
2853
|
any = true;
|
|
@@ -2727,16 +2855,16 @@ var rmSub = new Command3("rm").description("Delete a checkpoint (any provider th
|
|
|
2727
2855
|
`);
|
|
2728
2856
|
}
|
|
2729
2857
|
}
|
|
2730
|
-
|
|
2731
|
-
const { daytonaProvider } = await import("./dist-L4LCG5SJ.js");
|
|
2858
|
+
for (const backend of cloudHits) {
|
|
2732
2859
|
try {
|
|
2733
|
-
await
|
|
2860
|
+
const provider = await cloudProviderFor(backend);
|
|
2861
|
+
await provider.checkpoint?.remove(projectRoot, ref);
|
|
2734
2862
|
any = true;
|
|
2735
|
-
process.stdout.write(`removed
|
|
2863
|
+
process.stdout.write(`removed ${backend} checkpoint ${ref}
|
|
2736
2864
|
`);
|
|
2737
2865
|
} catch (err) {
|
|
2738
2866
|
log10.warn(
|
|
2739
|
-
|
|
2867
|
+
`${backend} checkpoint remove failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2740
2868
|
);
|
|
2741
2869
|
}
|
|
2742
2870
|
}
|
|
@@ -2747,7 +2875,8 @@ var rmSub = new Command3("rm").description("Delete a checkpoint (any provider th
|
|
|
2747
2875
|
["box.defaultCheckpoint", projectBox?.defaultCheckpoint, cfg.effective.box.defaultCheckpoint],
|
|
2748
2876
|
["box.defaultCheckpointDocker", projectBox?.defaultCheckpointDocker, cfg.effective.box.defaultCheckpointDocker],
|
|
2749
2877
|
["box.defaultCheckpointDaytona", projectBox?.defaultCheckpointDaytona, cfg.effective.box.defaultCheckpointDaytona],
|
|
2750
|
-
["box.defaultCheckpointHetzner", projectBox?.defaultCheckpointHetzner, cfg.effective.box.defaultCheckpointHetzner]
|
|
2878
|
+
["box.defaultCheckpointHetzner", projectBox?.defaultCheckpointHetzner, cfg.effective.box.defaultCheckpointHetzner],
|
|
2879
|
+
["box.defaultCheckpointVercel", projectBox?.defaultCheckpointVercel, cfg.effective.box.defaultCheckpointVercel]
|
|
2751
2880
|
];
|
|
2752
2881
|
for (const [key, projectValue, effectiveValue] of defKeys) {
|
|
2753
2882
|
if (projectValue === ref) {
|
|
@@ -2790,6 +2919,16 @@ async function runCloudCheckpointCreate(box, opts) {
|
|
|
2790
2919
|
CHECKPOINT_NOTICE_TTL_MS
|
|
2791
2920
|
);
|
|
2792
2921
|
try {
|
|
2922
|
+
if (opts.setDefault && provider.extractAgentCredentials) {
|
|
2923
|
+
try {
|
|
2924
|
+
const saved = await provider.extractAgentCredentials(box);
|
|
2925
|
+
if (saved.length > 0) {
|
|
2926
|
+
log10.info(`saved ${saved.join(", ")} login to ~/.agentbox for future boxes`);
|
|
2927
|
+
}
|
|
2928
|
+
} catch (err) {
|
|
2929
|
+
log10.warn(`agent credential extract skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2793
2932
|
log10.info(`capturing cloud snapshot '${name}' (this may take a few minutes)`);
|
|
2794
2933
|
const result = await provider.checkpoint.create(box, name);
|
|
2795
2934
|
log10.success(`checkpoint ${result.ref} (daytona snapshot) captured`);
|
|
@@ -3124,14 +3263,6 @@ import { Command as Command5 } from "commander";
|
|
|
3124
3263
|
function reattachRef2(r) {
|
|
3125
3264
|
return typeof r.projectIndex === "number" ? String(r.projectIndex) : r.name;
|
|
3126
3265
|
}
|
|
3127
|
-
function parseMaxRunningOption2(raw) {
|
|
3128
|
-
if (raw === void 0) return void 0;
|
|
3129
|
-
const n = Number(raw);
|
|
3130
|
-
if (!Number.isInteger(n) || n <= 0) {
|
|
3131
|
-
throw new Error(`--max-running: expected a positive integer, got "${raw}"`);
|
|
3132
|
-
}
|
|
3133
|
-
return n;
|
|
3134
|
-
}
|
|
3135
3266
|
function pickCodexCreateOpts(opts) {
|
|
3136
3267
|
return {
|
|
3137
3268
|
workspace: opts.workspace,
|
|
@@ -3245,15 +3376,21 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3245
3376
|
).option(
|
|
3246
3377
|
"--from-branch <ref>",
|
|
3247
3378
|
"base the box's per-box branch on this ref (branch / tag / SHA) instead of HEAD. Branch/tag names are fetched from origin first."
|
|
3379
|
+
).option(
|
|
3380
|
+
"-b, --use-branch <name>",
|
|
3381
|
+
"reuse an existing branch directly instead of forking agentbox/<box-name>. Commits/pushes flow straight to it. Docker fails if the host already has it checked out. Mutually exclusive with --from-branch."
|
|
3248
3382
|
).option(
|
|
3249
3383
|
"-v, --verbose",
|
|
3250
3384
|
"bypass the spinner and stream raw provider output to stderr. The same content always lands in ~/.agentbox/logs/codex.log."
|
|
3251
|
-
).option("--attach-in <mode>", ATTACH_IN_HELP).option("--inline", INLINE_HELP).option("-
|
|
3385
|
+
).option("--attach-in <mode>", ATTACH_IN_HELP).option("--inline", INLINE_HELP).option("-d, --no-attach", NO_ATTACH_HELP).option(
|
|
3252
3386
|
"-i, --initial-prompt <text>",
|
|
3253
3387
|
"seed the codex session with this initial user turn and run in background (no attach). Jobs go through the host-wide queue (queue.maxConcurrent)."
|
|
3254
3388
|
).option(
|
|
3255
3389
|
"--max-running <n>",
|
|
3256
3390
|
"per-invocation override of queue.maxConcurrent; only honored when `-i` is set"
|
|
3391
|
+
).option(
|
|
3392
|
+
"--max-working <n>",
|
|
3393
|
+
"per-invocation override of queue.maxWorking; only honored when `-i` is set"
|
|
3257
3394
|
).option(
|
|
3258
3395
|
"-c, --continue",
|
|
3259
3396
|
"teleport the most recent host Codex session for this cwd into the box and resume from it"
|
|
@@ -3326,7 +3463,8 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3326
3463
|
}
|
|
3327
3464
|
throw err;
|
|
3328
3465
|
}
|
|
3329
|
-
const maxRunningOverride =
|
|
3466
|
+
const maxRunningOverride = parseMaxOption("--max-running", opts.maxRunning);
|
|
3467
|
+
const maxWorkingOverride = parseMaxOption("--max-working", opts.maxWorking);
|
|
3330
3468
|
const result = await submitQueueJob({
|
|
3331
3469
|
agent: "codex",
|
|
3332
3470
|
boxName: opts.name ?? "",
|
|
@@ -3334,7 +3472,8 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3334
3472
|
prompt: opts.initialPrompt,
|
|
3335
3473
|
agentArgs: codexArgs,
|
|
3336
3474
|
createOpts: pickCodexCreateOpts(opts),
|
|
3337
|
-
maxRunningOverride
|
|
3475
|
+
maxRunningOverride,
|
|
3476
|
+
maxWorkingOverride
|
|
3338
3477
|
});
|
|
3339
3478
|
outro3(
|
|
3340
3479
|
`job ${result.job.id} queued (${String(result.runningCount)}/${String(result.maxConcurrent)} running); log: ${result.job.logPath}`
|
|
@@ -3363,10 +3502,18 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3363
3502
|
process.exit(1);
|
|
3364
3503
|
}
|
|
3365
3504
|
let fromBranch;
|
|
3505
|
+
let useBranch;
|
|
3366
3506
|
try {
|
|
3367
|
-
fromBranch = await
|
|
3507
|
+
({ fromBranch, useBranch } = await resolveBranchSelection({
|
|
3508
|
+
useBranch: opts.useBranch,
|
|
3509
|
+
fromBranch: opts.fromBranch,
|
|
3510
|
+
repo: opts.workspace,
|
|
3511
|
+
providerName,
|
|
3512
|
+
cloudUseCurrentBranch: cfg.effective.cloud.useCurrentBranch,
|
|
3513
|
+
log: (m) => cmdLog.write(m)
|
|
3514
|
+
}));
|
|
3368
3515
|
} catch (err) {
|
|
3369
|
-
if (err instanceof FromBranchError) {
|
|
3516
|
+
if (err instanceof FromBranchError || err instanceof UseBranchError) {
|
|
3370
3517
|
log12.error(err.message);
|
|
3371
3518
|
cmdLog.close();
|
|
3372
3519
|
process.exit(2);
|
|
@@ -3389,6 +3536,7 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3389
3536
|
vnc: { enabled: cfg.effective.box.vnc },
|
|
3390
3537
|
limits: resolveLimits(cfg.effective.box, opts),
|
|
3391
3538
|
fromBranch,
|
|
3539
|
+
useBranch,
|
|
3392
3540
|
projectRoot
|
|
3393
3541
|
},
|
|
3394
3542
|
binary: "codex",
|
|
@@ -3439,6 +3587,7 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3439
3587
|
useSnapshot,
|
|
3440
3588
|
checkpointRef,
|
|
3441
3589
|
fromBranch,
|
|
3590
|
+
useBranch,
|
|
3442
3591
|
image: cfg.effective.box.image,
|
|
3443
3592
|
codexConfig: { isolate: cfg.effective.box.isolateCodexConfig },
|
|
3444
3593
|
withPlaywright,
|
|
@@ -3661,7 +3810,7 @@ var codexStartCommand = new Command5("start").description(
|
|
|
3661
3810
|
).option("--session-name <name>", "tmux session name (default from config; built-in: codex)").option(
|
|
3662
3811
|
"--no-sync-config",
|
|
3663
3812
|
"skip rsyncing the host's ~/.codex into the box's volume before starting (faster; use existing in-box state)"
|
|
3664
|
-
).option("--attach-in <mode>", ATTACH_IN_HELP).option("-i, --inline", INLINE_HELP).option("-
|
|
3813
|
+
).option("--attach-in <mode>", ATTACH_IN_HELP).option("-i, --inline", INLINE_HELP).option("-d, --no-attach", NO_ATTACH_HELP).option(
|
|
3665
3814
|
"-c, --continue",
|
|
3666
3815
|
"teleport the most recent host Codex session for this cwd into the box and resume"
|
|
3667
3816
|
).option(
|
|
@@ -3782,14 +3931,6 @@ import { Command as Command6 } from "commander";
|
|
|
3782
3931
|
function reattachRef3(r) {
|
|
3783
3932
|
return typeof r.projectIndex === "number" ? String(r.projectIndex) : r.name;
|
|
3784
3933
|
}
|
|
3785
|
-
function parseMaxRunningOption3(raw) {
|
|
3786
|
-
if (raw === void 0) return void 0;
|
|
3787
|
-
const n = Number(raw);
|
|
3788
|
-
if (!Number.isInteger(n) || n <= 0) {
|
|
3789
|
-
throw new Error(`--max-running: expected a positive integer, got "${raw}"`);
|
|
3790
|
-
}
|
|
3791
|
-
return n;
|
|
3792
|
-
}
|
|
3793
3934
|
function pickOpencodeCreateOpts(opts) {
|
|
3794
3935
|
return {
|
|
3795
3936
|
workspace: opts.workspace,
|
|
@@ -3906,15 +4047,21 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
3906
4047
|
).option(
|
|
3907
4048
|
"--from-branch <ref>",
|
|
3908
4049
|
"base the box's per-box branch on this ref (branch / tag / SHA) instead of HEAD. Branch/tag names are fetched from origin first."
|
|
4050
|
+
).option(
|
|
4051
|
+
"-b, --use-branch <name>",
|
|
4052
|
+
"reuse an existing branch directly instead of forking agentbox/<box-name>. Commits/pushes flow straight to it. Docker fails if the host already has it checked out. Mutually exclusive with --from-branch."
|
|
3909
4053
|
).option(
|
|
3910
4054
|
"-v, --verbose",
|
|
3911
4055
|
"bypass the spinner and stream raw provider output to stderr. The same content always lands in ~/.agentbox/logs/opencode.log."
|
|
3912
|
-
).option("--attach-in <mode>", ATTACH_IN_HELP).option("--inline", INLINE_HELP).option("-
|
|
4056
|
+
).option("--attach-in <mode>", ATTACH_IN_HELP).option("--inline", INLINE_HELP).option("-d, --no-attach", NO_ATTACH_HELP).option(
|
|
3913
4057
|
"-i, --initial-prompt <text>",
|
|
3914
4058
|
"seed the opencode session with this initial user turn and run in background (no attach). Jobs go through the host-wide queue (queue.maxConcurrent)."
|
|
3915
4059
|
).option(
|
|
3916
4060
|
"--max-running <n>",
|
|
3917
4061
|
"per-invocation override of queue.maxConcurrent; only honored when `-i` is set"
|
|
4062
|
+
).option(
|
|
4063
|
+
"--max-working <n>",
|
|
4064
|
+
"per-invocation override of queue.maxWorking; only honored when `-i` is set"
|
|
3918
4065
|
).option(
|
|
3919
4066
|
"-c, --continue",
|
|
3920
4067
|
"session teleport (not yet supported for opencode in v1; emits a friendly error)"
|
|
@@ -3972,7 +4119,8 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
3972
4119
|
}
|
|
3973
4120
|
throw err;
|
|
3974
4121
|
}
|
|
3975
|
-
const maxRunningOverride =
|
|
4122
|
+
const maxRunningOverride = parseMaxOption("--max-running", opts.maxRunning);
|
|
4123
|
+
const maxWorkingOverride = parseMaxOption("--max-working", opts.maxWorking);
|
|
3976
4124
|
const result = await submitQueueJob({
|
|
3977
4125
|
agent: "opencode",
|
|
3978
4126
|
boxName: opts.name ?? "",
|
|
@@ -3980,7 +4128,8 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
3980
4128
|
prompt: opts.initialPrompt,
|
|
3981
4129
|
agentArgs: opencodeArgs,
|
|
3982
4130
|
createOpts: pickOpencodeCreateOpts(opts),
|
|
3983
|
-
maxRunningOverride
|
|
4131
|
+
maxRunningOverride,
|
|
4132
|
+
maxWorkingOverride
|
|
3984
4133
|
});
|
|
3985
4134
|
outro4(
|
|
3986
4135
|
`job ${result.job.id} queued (${String(result.runningCount)}/${String(result.maxConcurrent)} running); log: ${result.job.logPath}`
|
|
@@ -4009,10 +4158,18 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
4009
4158
|
process.exit(1);
|
|
4010
4159
|
}
|
|
4011
4160
|
let fromBranch;
|
|
4161
|
+
let useBranch;
|
|
4012
4162
|
try {
|
|
4013
|
-
fromBranch = await
|
|
4163
|
+
({ fromBranch, useBranch } = await resolveBranchSelection({
|
|
4164
|
+
useBranch: opts.useBranch,
|
|
4165
|
+
fromBranch: opts.fromBranch,
|
|
4166
|
+
repo: opts.workspace,
|
|
4167
|
+
providerName,
|
|
4168
|
+
cloudUseCurrentBranch: cfg.effective.cloud.useCurrentBranch,
|
|
4169
|
+
log: (m) => cmdLog.write(m)
|
|
4170
|
+
}));
|
|
4014
4171
|
} catch (err) {
|
|
4015
|
-
if (err instanceof FromBranchError) {
|
|
4172
|
+
if (err instanceof FromBranchError || err instanceof UseBranchError) {
|
|
4016
4173
|
log13.error(err.message);
|
|
4017
4174
|
cmdLog.close();
|
|
4018
4175
|
process.exit(2);
|
|
@@ -4035,6 +4192,7 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
4035
4192
|
vnc: { enabled: cfg.effective.box.vnc },
|
|
4036
4193
|
limits: resolveLimits(cfg.effective.box, opts),
|
|
4037
4194
|
fromBranch,
|
|
4195
|
+
useBranch,
|
|
4038
4196
|
projectRoot
|
|
4039
4197
|
},
|
|
4040
4198
|
binary: "opencode",
|
|
@@ -4067,6 +4225,7 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
4067
4225
|
useSnapshot,
|
|
4068
4226
|
checkpointRef,
|
|
4069
4227
|
fromBranch,
|
|
4228
|
+
useBranch,
|
|
4070
4229
|
image: cfg.effective.box.image,
|
|
4071
4230
|
opencodeConfig: { isolate: cfg.effective.box.isolateOpencodeConfig },
|
|
4072
4231
|
withPlaywright,
|
|
@@ -4231,7 +4390,7 @@ var opencodeStartCommand = new Command6("start").description(
|
|
|
4231
4390
|
).option("--session-name <name>", "tmux session name (default from config; built-in: opencode)").option(
|
|
4232
4391
|
"--no-sync-config",
|
|
4233
4392
|
"skip rsyncing the host's OpenCode config into the box's volume before starting (faster; use existing in-box state)"
|
|
4234
|
-
).option("--attach-in <mode>", ATTACH_IN_HELP).option("-i, --inline", INLINE_HELP).option("-
|
|
4393
|
+
).option("--attach-in <mode>", ATTACH_IN_HELP).option("-i, --inline", INLINE_HELP).option("-d, --no-attach", NO_ATTACH_HELP).option(
|
|
4235
4394
|
"-c, --continue",
|
|
4236
4395
|
"session teleport (not yet supported for opencode in v1; emits a friendly error)"
|
|
4237
4396
|
).option(
|
|
@@ -4769,6 +4928,9 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
4769
4928
|
).option(
|
|
4770
4929
|
"--from-branch <ref>",
|
|
4771
4930
|
"base the box's per-box branch on this ref (branch / tag / SHA) instead of HEAD. Branch/tag names are fetched from origin first."
|
|
4931
|
+
).option(
|
|
4932
|
+
"-b, --use-branch <name>",
|
|
4933
|
+
"reuse an existing branch directly instead of forking agentbox/<box-name>. Commits/pushes flow straight to it. Docker fails if the host already has it checked out. Mutually exclusive with --from-branch."
|
|
4772
4934
|
).option("-y, --yes", "skip prompts, accept defaults").option(
|
|
4773
4935
|
"--carry-yes",
|
|
4774
4936
|
"auto-approve agentbox.yaml's `carry:` block (also AGENTBOX_CARRY_YES=1). Required for non-TTY use of `-y` when carry: is non-empty."
|
|
@@ -4857,11 +5019,22 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
4857
5019
|
const withPlaywright = cfg.effective.box.withPlaywright || cfg.effective.browser.default !== "agent-browser";
|
|
4858
5020
|
const provider = await providerForCreate({ flag: opts.provider, config: cfg.effective });
|
|
4859
5021
|
let fromBranch;
|
|
5022
|
+
let useBranch;
|
|
4860
5023
|
try {
|
|
4861
|
-
fromBranch = await
|
|
5024
|
+
({ fromBranch, useBranch } = await resolveBranchSelection({
|
|
5025
|
+
useBranch: opts.useBranch,
|
|
5026
|
+
fromBranch: opts.fromBranch,
|
|
5027
|
+
repo: opts.workspace,
|
|
5028
|
+
providerName: provider.name,
|
|
5029
|
+
cloudUseCurrentBranch: cfg.effective.cloud.useCurrentBranch,
|
|
5030
|
+
log: (m2) => {
|
|
5031
|
+
s.message(m2);
|
|
5032
|
+
cmdLog.write(m2);
|
|
5033
|
+
}
|
|
5034
|
+
}));
|
|
4862
5035
|
} catch (err) {
|
|
4863
|
-
if (err instanceof FromBranchError) {
|
|
4864
|
-
s.stop("aborting: invalid
|
|
5036
|
+
if (err instanceof FromBranchError || err instanceof UseBranchError) {
|
|
5037
|
+
s.stop("aborting: invalid branch selection");
|
|
4865
5038
|
log15.error(err.message);
|
|
4866
5039
|
cmdLog.close();
|
|
4867
5040
|
process.exit(2);
|
|
@@ -4881,6 +5054,7 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
4881
5054
|
limits: resolveLimits(cfg.effective.box, opts),
|
|
4882
5055
|
bundleDepth: cfg.effective.box.bundleDepth,
|
|
4883
5056
|
fromBranch,
|
|
5057
|
+
useBranch,
|
|
4884
5058
|
projectRoot,
|
|
4885
5059
|
onLog: (line) => {
|
|
4886
5060
|
s.message(line);
|
|
@@ -4890,7 +5064,14 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
4890
5064
|
useSnapshot,
|
|
4891
5065
|
sharedCache: cfg.effective.box.dockerCacheShared,
|
|
4892
5066
|
portless: portlessEnabled,
|
|
4893
|
-
portlessStateDir: cfg.effective.portless.stateDir || void 0
|
|
5067
|
+
portlessStateDir: cfg.effective.portless.stateDir || void 0,
|
|
5068
|
+
// Vercel-only sizing (box.vercelVcpus / vercelTimeoutMs). The cloud
|
|
5069
|
+
// scaffold reads these as overrides; other providers ignore them.
|
|
5070
|
+
...provider.name === "vercel" ? {
|
|
5071
|
+
vcpus: cfg.effective.box.vercelVcpus,
|
|
5072
|
+
timeoutMs: cfg.effective.box.vercelTimeoutMs,
|
|
5073
|
+
networkPolicy: cfg.effective.box.vercelNetworkPolicy
|
|
5074
|
+
} : {}
|
|
4894
5075
|
}
|
|
4895
5076
|
});
|
|
4896
5077
|
s.stop(`box ${result.record.container} ready`);
|
|
@@ -4945,7 +5126,7 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
4945
5126
|
}
|
|
4946
5127
|
outro5("done");
|
|
4947
5128
|
if (attachClaudeAfter) {
|
|
4948
|
-
const { cloudAgentAttach: cloudAgentAttach2 } = await import("./_cloud-attach-
|
|
5129
|
+
const { cloudAgentAttach: cloudAgentAttach2 } = await import("./_cloud-attach-ZXBCNWJX.js");
|
|
4949
5130
|
await cloudAgentAttach2({
|
|
4950
5131
|
box: result.record,
|
|
4951
5132
|
binary: "claude",
|
|
@@ -5501,6 +5682,9 @@ var Compositor = class {
|
|
|
5501
5682
|
* the poll respawn so it can't interrupt the transition). */
|
|
5502
5683
|
busy = false;
|
|
5503
5684
|
layout;
|
|
5685
|
+
/** Last host terminal/tab title we emitted, to dedupe OSC writes across the
|
|
5686
|
+
* frequent (spinner-driven) drawChrome calls. */
|
|
5687
|
+
lastTitle = null;
|
|
5504
5688
|
prevRows = null;
|
|
5505
5689
|
renderTimer = null;
|
|
5506
5690
|
pollTimer = null;
|
|
@@ -5522,6 +5706,7 @@ var Compositor = class {
|
|
|
5522
5706
|
};
|
|
5523
5707
|
async run() {
|
|
5524
5708
|
this.out.write("\x1B[?1049h\x1B[?25l\x1B[2J" + MOUSE_ENABLE_SEQ + EXT_KEYS_ENABLE_SEQ);
|
|
5709
|
+
pushTerminalTitle(this.out);
|
|
5525
5710
|
if (this.inp.isTTY) this.inp.setRawMode(true);
|
|
5526
5711
|
this.inp.resume();
|
|
5527
5712
|
this.inp.on("data", this.onData);
|
|
@@ -6116,7 +6301,20 @@ var Compositor = class {
|
|
|
6116
6301
|
this.out.write(s + SYNC_END);
|
|
6117
6302
|
}
|
|
6118
6303
|
}
|
|
6304
|
+
/** Drive the host terminal/tab title from the selected box:
|
|
6305
|
+
* `AgentBox: <session title | box name>`, or just `AgentBox` for the
|
|
6306
|
+
* synthetic "+ New box" entry / no selection. Deduped via {@link lastTitle}. */
|
|
6307
|
+
updateTitle() {
|
|
6308
|
+
if (this.tornDown) return;
|
|
6309
|
+
const box = this.selectedBox();
|
|
6310
|
+
const inner = box && box.id !== NEW_BOX_ID ? box.state === "running" && box.sessionTitle ? stripTitleGlyph(box.sessionTitle) : box.name : void 0;
|
|
6311
|
+
const title = inner ? `AgentBox: ${inner}` : "AgentBox";
|
|
6312
|
+
if (title === this.lastTitle) return;
|
|
6313
|
+
this.lastTitle = title;
|
|
6314
|
+
setTerminalTitle(title, this.out);
|
|
6315
|
+
}
|
|
6119
6316
|
drawChrome() {
|
|
6317
|
+
this.updateTitle();
|
|
6120
6318
|
if (this.tornDown || this.layout.tooSmall) return;
|
|
6121
6319
|
const { sidebar, sepX, statusY } = this.layout;
|
|
6122
6320
|
const decorate = this.activePrompts.size > 0 || this.activeNotices.size > 0;
|
|
@@ -6215,6 +6413,7 @@ var Compositor = class {
|
|
|
6215
6413
|
if (this.inp.isTTY) this.inp.setRawMode(false);
|
|
6216
6414
|
this.inp.pause();
|
|
6217
6415
|
this.out.write(EXT_KEYS_DISABLE_SEQ + MOUSE_DISABLE_SEQ + "\x1B[?25h\x1B[0m\x1B[?1049l");
|
|
6416
|
+
popTerminalTitle(this.out);
|
|
6218
6417
|
this.resolveDone?.();
|
|
6219
6418
|
}
|
|
6220
6419
|
};
|
|
@@ -6933,30 +7132,106 @@ var hetznerCommand = new Command13("hetzner").description(
|
|
|
6933
7132
|
"Hetzner Cloud VPS provider \u2014 credentials, firewall, plus sugar for `--provider hetzner` (e.g. `agentbox hetzner create|claude|codex|opencode`)"
|
|
6934
7133
|
).addCommand(loginSub2, { isDefault: true }).addCommand(firewallSub);
|
|
6935
7134
|
|
|
6936
|
-
//
|
|
6937
|
-
import {
|
|
7135
|
+
// ../../packages/sandbox-vercel/dist/cli.js
|
|
7136
|
+
import { log as log19 } from "@clack/prompts";
|
|
6938
7137
|
import { Command as Command14 } from "commander";
|
|
6939
|
-
|
|
7138
|
+
function reportError3(err) {
|
|
7139
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7140
|
+
log19.error(message);
|
|
7141
|
+
process.exitCode = 1;
|
|
7142
|
+
}
|
|
7143
|
+
function relativeExpiry(expiresAt) {
|
|
7144
|
+
const deltaMs = expiresAt * 1e3 - Date.now();
|
|
7145
|
+
if (deltaMs <= 0) return "expired";
|
|
7146
|
+
const mins = Math.round(deltaMs / 6e4);
|
|
7147
|
+
if (mins < 60) return `expires in ${mins}m`;
|
|
7148
|
+
return `expires in ${Math.round(mins / 60)}h`;
|
|
7149
|
+
}
|
|
7150
|
+
async function printStatus3() {
|
|
7151
|
+
const s = readVercelCredStatus();
|
|
7152
|
+
if (s.auth === "none") {
|
|
7153
|
+
process.stdout.write(
|
|
7154
|
+
"vercel: not configured\n run `agentbox vercel login` to set up credentials\n"
|
|
7155
|
+
);
|
|
7156
|
+
return;
|
|
7157
|
+
}
|
|
7158
|
+
const lines = ["vercel: configured"];
|
|
7159
|
+
if (s.auth === "cli") {
|
|
7160
|
+
const det = await detectSbx();
|
|
7161
|
+
lines.push(" auth: Vercel CLI (sandbox) login");
|
|
7162
|
+
lines.push(` cli: ${det.installed ? `installed${det.version ? ` ${det.version}` : ""}` : `not installed \u2014 run \`${installSbxHint()}\``}`);
|
|
7163
|
+
if (s.cli) {
|
|
7164
|
+
if (!s.cli.loggedIn) {
|
|
7165
|
+
lines.push(" session: logged out \u2014 run `agentbox vercel login`");
|
|
7166
|
+
} else {
|
|
7167
|
+
let tokenLine = ` token: ${s.token ? maskKey3(s.token) : "(live, from CLI store)"}`;
|
|
7168
|
+
if (s.cli.expiresAt) tokenLine += ` (${relativeExpiry(s.cli.expiresAt)})`;
|
|
7169
|
+
else tokenLine += " (no expiry recorded \u2014 will refresh on use)";
|
|
7170
|
+
lines.push(tokenLine);
|
|
7171
|
+
}
|
|
7172
|
+
lines.push(` store: ${s.cli.authPath}`);
|
|
7173
|
+
}
|
|
7174
|
+
} else if (s.auth === "oidc") {
|
|
7175
|
+
lines.push(" auth: OIDC token (VERCEL_OIDC_TOKEN)");
|
|
7176
|
+
} else {
|
|
7177
|
+
lines.push(" auth: access token");
|
|
7178
|
+
if (s.token) lines.push(` token: ${maskKey3(s.token)}`);
|
|
7179
|
+
}
|
|
7180
|
+
lines.push(` source: ${s.source}`);
|
|
7181
|
+
if (s.teamId) lines.push(` team: ${s.teamId}`);
|
|
7182
|
+
if (s.projectId) lines.push(` project: ${s.projectId}`);
|
|
7183
|
+
if (s.source === "secrets.env" || s.source === "cli-store") {
|
|
7184
|
+
lines.push(` file: ${secretsPath3()}`);
|
|
7185
|
+
}
|
|
7186
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
7187
|
+
}
|
|
7188
|
+
var loginSub3 = new Command14("login").description("Set up (or rotate) Vercel credentials for sandbox boxes").option("--status", "show what is currently configured (masked) and exit").action(async (opts) => {
|
|
7189
|
+
try {
|
|
7190
|
+
if (opts.status) {
|
|
7191
|
+
await printStatus3();
|
|
7192
|
+
return;
|
|
7193
|
+
}
|
|
7194
|
+
if (!process.stdin.isTTY) {
|
|
7195
|
+
process.stderr.write(
|
|
7196
|
+
"vercel login needs an interactive terminal \u2014 set the VERCEL_TOKEN trio (or VERCEL_OIDC_TOKEN) in the environment or in ~/.agentbox/secrets.env for non-interactive use.\n"
|
|
7197
|
+
);
|
|
7198
|
+
process.exitCode = 1;
|
|
7199
|
+
return;
|
|
7200
|
+
}
|
|
7201
|
+
await ensureVercelCredentials({ force: true });
|
|
7202
|
+
} catch (err) {
|
|
7203
|
+
reportError3(err);
|
|
7204
|
+
}
|
|
7205
|
+
});
|
|
7206
|
+
var vercelCommand = new Command14("vercel").description(
|
|
7207
|
+
"Vercel Sandbox provider \u2014 credentials, plus sugar for `--provider vercel` (e.g. `agentbox vercel create|claude|codex|opencode`)"
|
|
7208
|
+
).addCommand(loginSub3, { isDefault: true });
|
|
7209
|
+
|
|
7210
|
+
// src/commands/destroy.ts
|
|
7211
|
+
import { confirm as confirm7, isCancel as isCancel8, log as log20 } from "@clack/prompts";
|
|
7212
|
+
import { Command as Command15 } from "commander";
|
|
7213
|
+
var destroyCommand = new Command15("destroy").alias("rm").description("Destroy a box and discard its container writable layer (where /workspace lived)").argument(
|
|
6940
7214
|
"[box]",
|
|
6941
7215
|
"box ref: project index, id, id prefix, name, or container (default: the only box in this project)"
|
|
6942
7216
|
).option("-y, --yes", "skip the confirmation prompt").option("--keep-snapshot", "don't delete the snapshot dir under ~/.agentbox/snapshots/").action(async (idOrName, opts) => {
|
|
6943
7217
|
try {
|
|
6944
7218
|
const box = await resolveBoxOrExit(idOrName);
|
|
6945
7219
|
if (!opts.yes) {
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
7220
|
+
log20.warn("Will also wipe the box volume and agent work-in-progress");
|
|
7221
|
+
const rootBranch = box.gitWorktrees?.find((w) => w.kind === "root")?.branch;
|
|
7222
|
+
const lines = [box.name];
|
|
7223
|
+
if (rootBranch) lines.push(`branch: ${rootBranch}`);
|
|
7224
|
+
lines.push(`project: ${box.workspacePath}`);
|
|
6951
7225
|
if (box.snapshotDir) {
|
|
6952
|
-
|
|
7226
|
+
lines.push(`snapshot: ${box.snapshotDir}${opts.keepSnapshot ? " (will be kept)" : ""}`);
|
|
6953
7227
|
}
|
|
7228
|
+
log20.info(lines.join("\n"));
|
|
6954
7229
|
const ok = await confirm7({
|
|
6955
7230
|
message: "Destroy this box?",
|
|
6956
7231
|
initialValue: false
|
|
6957
7232
|
});
|
|
6958
7233
|
if (isCancel8(ok) || !ok) {
|
|
6959
|
-
|
|
7234
|
+
log20.info("cancelled");
|
|
6960
7235
|
return;
|
|
6961
7236
|
}
|
|
6962
7237
|
}
|
|
@@ -6989,17 +7264,17 @@ var destroyCommand = new Command14("destroy").alias("rm").description("Destroy a
|
|
|
6989
7264
|
});
|
|
6990
7265
|
|
|
6991
7266
|
// src/commands/download.ts
|
|
6992
|
-
import { confirm as confirm13, isCancel as isCancel14, log as
|
|
6993
|
-
import { Command as
|
|
7267
|
+
import { confirm as confirm13, isCancel as isCancel14, log as log26 } from "@clack/prompts";
|
|
7268
|
+
import { Command as Command21 } from "commander";
|
|
6994
7269
|
|
|
6995
7270
|
// src/commands/download-claude.ts
|
|
6996
|
-
import { confirm as confirm8, isCancel as isCancel9, log as
|
|
6997
|
-
import { Command as
|
|
7271
|
+
import { confirm as confirm8, isCancel as isCancel9, log as log21 } from "@clack/prompts";
|
|
7272
|
+
import { Command as Command16 } from "commander";
|
|
6998
7273
|
function tag(item) {
|
|
6999
7274
|
const noun = item.category === "plugins" ? "plugin" : item.category.replace(/s$/, "");
|
|
7000
7275
|
return ` ${item.category}/${item.name} (new ${noun})`;
|
|
7001
7276
|
}
|
|
7002
|
-
var downloadClaudeCommand = new
|
|
7277
|
+
var downloadClaudeCommand = new Command16("claude").description(
|
|
7003
7278
|
"Download box-installed Claude skills/plugins/agents/commands back to host ~/.claude (additive)"
|
|
7004
7279
|
).argument(
|
|
7005
7280
|
"[box]",
|
|
@@ -7009,7 +7284,7 @@ var downloadClaudeCommand = new Command15("claude").description(
|
|
|
7009
7284
|
const box = await resolveBoxOrExit(idOrName);
|
|
7010
7285
|
const volume = box.claudeConfigVolume ?? resolveClaudeVolume({ isolate: false, boxId: box.id }).volume;
|
|
7011
7286
|
if (volume === SHARED_CLAUDE_VOLUME) {
|
|
7012
|
-
|
|
7287
|
+
log21.warn(
|
|
7013
7288
|
`Reading the shared ${SHARED_CLAUDE_VOLUME} volume \u2014 it aggregates Claude extensions installed in ANY box, not just ${box.name}.`
|
|
7014
7289
|
);
|
|
7015
7290
|
}
|
|
@@ -7039,7 +7314,7 @@ var downloadClaudeCommand = new Command15("claude").description(
|
|
|
7039
7314
|
initialValue: false
|
|
7040
7315
|
});
|
|
7041
7316
|
if (isCancel9(ok) || !ok) {
|
|
7042
|
-
|
|
7317
|
+
log21.info("cancelled");
|
|
7043
7318
|
return;
|
|
7044
7319
|
}
|
|
7045
7320
|
}
|
|
@@ -7054,9 +7329,9 @@ var downloadClaudeCommand = new Command15("claude").description(
|
|
|
7054
7329
|
});
|
|
7055
7330
|
|
|
7056
7331
|
// src/commands/download-codex.ts
|
|
7057
|
-
import { confirm as confirm9, isCancel as isCancel10, log as
|
|
7058
|
-
import { Command as
|
|
7059
|
-
var downloadCodexCommand = new
|
|
7332
|
+
import { confirm as confirm9, isCancel as isCancel10, log as log22 } from "@clack/prompts";
|
|
7333
|
+
import { Command as Command17 } from "commander";
|
|
7334
|
+
var downloadCodexCommand = new Command17("codex").description(
|
|
7060
7335
|
"Download box-side Codex config/auth (config.toml, auth.json, prompts) back to host ~/.codex (additive)"
|
|
7061
7336
|
).argument(
|
|
7062
7337
|
"[box]",
|
|
@@ -7066,7 +7341,7 @@ var downloadCodexCommand = new Command16("codex").description(
|
|
|
7066
7341
|
const box = await resolveBoxOrExit(idOrName);
|
|
7067
7342
|
const volume = box.codexConfigVolume ?? resolveCodexVolume({ isolate: false, boxId: box.id }).volume;
|
|
7068
7343
|
if (volume === SHARED_CODEX_VOLUME) {
|
|
7069
|
-
|
|
7344
|
+
log22.warn(
|
|
7070
7345
|
`Reading the shared ${SHARED_CODEX_VOLUME} volume \u2014 it aggregates Codex config from ANY box, not just ${box.name}.`
|
|
7071
7346
|
);
|
|
7072
7347
|
}
|
|
@@ -7092,7 +7367,7 @@ var downloadCodexCommand = new Command16("codex").description(
|
|
|
7092
7367
|
initialValue: false
|
|
7093
7368
|
});
|
|
7094
7369
|
if (isCancel10(ok) || !ok) {
|
|
7095
|
-
|
|
7370
|
+
log22.info("cancelled");
|
|
7096
7371
|
return;
|
|
7097
7372
|
}
|
|
7098
7373
|
}
|
|
@@ -7105,9 +7380,9 @@ var downloadCodexCommand = new Command16("codex").description(
|
|
|
7105
7380
|
});
|
|
7106
7381
|
|
|
7107
7382
|
// src/commands/download-opencode.ts
|
|
7108
|
-
import { confirm as confirm10, isCancel as isCancel11, log as
|
|
7109
|
-
import { Command as
|
|
7110
|
-
var downloadOpencodeCommand = new
|
|
7383
|
+
import { confirm as confirm10, isCancel as isCancel11, log as log23 } from "@clack/prompts";
|
|
7384
|
+
import { Command as Command18 } from "commander";
|
|
7385
|
+
var downloadOpencodeCommand = new Command18("opencode").description(
|
|
7111
7386
|
"Download box-side OpenCode config/auth (auth.json, opencode.json, agents, commands, themes) back to host ~/.config + ~/.local/share opencode (additive)"
|
|
7112
7387
|
).argument(
|
|
7113
7388
|
"[box]",
|
|
@@ -7117,7 +7392,7 @@ var downloadOpencodeCommand = new Command17("opencode").description(
|
|
|
7117
7392
|
const box = await resolveBoxOrExit(idOrName);
|
|
7118
7393
|
const volume = box.opencodeConfigVolume ?? resolveOpencodeVolume({ isolate: false, boxId: box.id }).volume;
|
|
7119
7394
|
if (volume === SHARED_OPENCODE_VOLUME) {
|
|
7120
|
-
|
|
7395
|
+
log23.warn(
|
|
7121
7396
|
`Reading the shared ${SHARED_OPENCODE_VOLUME} volume \u2014 it aggregates OpenCode config from ANY box, not just ${box.name}.`
|
|
7122
7397
|
);
|
|
7123
7398
|
}
|
|
@@ -7143,7 +7418,7 @@ var downloadOpencodeCommand = new Command17("opencode").description(
|
|
|
7143
7418
|
initialValue: false
|
|
7144
7419
|
});
|
|
7145
7420
|
if (isCancel11(ok) || !ok) {
|
|
7146
|
-
|
|
7421
|
+
log23.info("cancelled");
|
|
7147
7422
|
return;
|
|
7148
7423
|
}
|
|
7149
7424
|
}
|
|
@@ -7156,8 +7431,8 @@ var downloadOpencodeCommand = new Command17("opencode").description(
|
|
|
7156
7431
|
});
|
|
7157
7432
|
|
|
7158
7433
|
// src/commands/download-config.ts
|
|
7159
|
-
import { confirm as confirm11, isCancel as isCancel12, log as
|
|
7160
|
-
import { Command as
|
|
7434
|
+
import { confirm as confirm11, isCancel as isCancel12, log as log24 } from "@clack/prompts";
|
|
7435
|
+
import { Command as Command19 } from "commander";
|
|
7161
7436
|
function tagChange(line) {
|
|
7162
7437
|
const sp = line.indexOf(" ");
|
|
7163
7438
|
const code = sp === -1 ? line : line.slice(0, sp);
|
|
@@ -7166,7 +7441,7 @@ function tagChange(line) {
|
|
|
7166
7441
|
return ` ${path} ${isNew ? "(new)" : "(overwrites host)"}`;
|
|
7167
7442
|
}
|
|
7168
7443
|
var CONFIG_PATTERNS = ["agentbox.yaml"];
|
|
7169
|
-
var downloadConfigCommand = new
|
|
7444
|
+
var downloadConfigCommand = new Command19("config").description("Download agentbox.yaml box -> host").argument(
|
|
7170
7445
|
"[box]",
|
|
7171
7446
|
"box ref: project index, id, id prefix, name, or container (default: the only box in this project)"
|
|
7172
7447
|
).option("-y, --yes", "skip the confirmation prompt").option("--dry-run", "list matched files and exit; don't write").option("--no-refresh", "skip the box->scratch-dir rsync step").action(async (idOrName, opts) => {
|
|
@@ -7174,15 +7449,15 @@ var downloadConfigCommand = new Command18("config").description("Download agentb
|
|
|
7174
7449
|
const box = await resolveBoxOrExit(idOrName);
|
|
7175
7450
|
const insp = await inspectBox(box.id);
|
|
7176
7451
|
if (insp.state === "paused") {
|
|
7177
|
-
|
|
7452
|
+
log24.info("box is paused; unpausing");
|
|
7178
7453
|
await unpauseBox(box.id);
|
|
7179
7454
|
} else if (insp.state === "stopped") {
|
|
7180
|
-
|
|
7455
|
+
log24.info("box is stopped; starting");
|
|
7181
7456
|
await startBox(box.id);
|
|
7182
7457
|
} else if (insp.state === "missing") {
|
|
7183
7458
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
7184
7459
|
}
|
|
7185
|
-
|
|
7460
|
+
log24.info(`agentbox.yaml bypasses gitignore and copies directly into ${box.workspacePath}`);
|
|
7186
7461
|
const preview = await pullToHost(box, {
|
|
7187
7462
|
dryRun: true,
|
|
7188
7463
|
respectGitignore: false,
|
|
@@ -7210,7 +7485,7 @@ var downloadConfigCommand = new Command18("config").description("Download agentb
|
|
|
7210
7485
|
initialValue: false
|
|
7211
7486
|
});
|
|
7212
7487
|
if (isCancel12(ok) || !ok) {
|
|
7213
|
-
|
|
7488
|
+
log24.info("cancelled");
|
|
7214
7489
|
return;
|
|
7215
7490
|
}
|
|
7216
7491
|
}
|
|
@@ -7232,8 +7507,8 @@ var downloadConfigCommand = new Command18("config").description("Download agentb
|
|
|
7232
7507
|
});
|
|
7233
7508
|
|
|
7234
7509
|
// src/commands/download-env.ts
|
|
7235
|
-
import { confirm as confirm12, isCancel as isCancel13, log as
|
|
7236
|
-
import { Command as
|
|
7510
|
+
import { confirm as confirm12, isCancel as isCancel13, log as log25 } from "@clack/prompts";
|
|
7511
|
+
import { Command as Command20 } from "commander";
|
|
7237
7512
|
function tagChange2(line) {
|
|
7238
7513
|
const sp = line.indexOf(" ");
|
|
7239
7514
|
const code = sp === -1 ? line : line.slice(0, sp);
|
|
@@ -7241,7 +7516,7 @@ function tagChange2(line) {
|
|
|
7241
7516
|
const isNew = /^>f\++$/.test(code);
|
|
7242
7517
|
return ` ${path} ${isNew ? "(new)" : "(overwrites host)"}`;
|
|
7243
7518
|
}
|
|
7244
|
-
var downloadEnvCommand = new
|
|
7519
|
+
var downloadEnvCommand = new Command20("env").description(
|
|
7245
7520
|
"Download gitignored env/config files (.env*, .envrc, secrets.toml, agentbox.yaml, ...) box -> host"
|
|
7246
7521
|
).argument(
|
|
7247
7522
|
"[box]",
|
|
@@ -7256,15 +7531,15 @@ var downloadEnvCommand = new Command19("env").description(
|
|
|
7256
7531
|
const box = await resolveBoxOrExit(idOrName);
|
|
7257
7532
|
const insp = await inspectBox(box.id);
|
|
7258
7533
|
if (insp.state === "paused") {
|
|
7259
|
-
|
|
7534
|
+
log25.info("box is paused; unpausing");
|
|
7260
7535
|
await unpauseBox(box.id);
|
|
7261
7536
|
} else if (insp.state === "stopped") {
|
|
7262
|
-
|
|
7537
|
+
log25.info("box is stopped; starting");
|
|
7263
7538
|
await startBox(box.id);
|
|
7264
7539
|
} else if (insp.state === "missing") {
|
|
7265
7540
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
7266
7541
|
}
|
|
7267
|
-
|
|
7542
|
+
log25.info(
|
|
7268
7543
|
`env/config files bypass gitignore and copy directly into ${box.workspacePath}`
|
|
7269
7544
|
);
|
|
7270
7545
|
const patterns = [...DEFAULT_ENV_PATTERNS, ...opts.pattern];
|
|
@@ -7295,7 +7570,7 @@ var downloadEnvCommand = new Command19("env").description(
|
|
|
7295
7570
|
initialValue: false
|
|
7296
7571
|
});
|
|
7297
7572
|
if (isCancel13(ok) || !ok) {
|
|
7298
|
-
|
|
7573
|
+
log25.info("cancelled");
|
|
7299
7574
|
return;
|
|
7300
7575
|
}
|
|
7301
7576
|
}
|
|
@@ -7317,7 +7592,7 @@ var downloadEnvCommand = new Command19("env").description(
|
|
|
7317
7592
|
});
|
|
7318
7593
|
|
|
7319
7594
|
// src/commands/download.ts
|
|
7320
|
-
var downloadCommand = new
|
|
7595
|
+
var downloadCommand = new Command21("download").enablePositionalOptions().description("Download a box's /workspace back into your host workspace dir (gitignore-aware)").argument(
|
|
7321
7596
|
"[box]",
|
|
7322
7597
|
"box ref: project index, id, id prefix, name, or container (default: the only box in this project)"
|
|
7323
7598
|
).option("-y, --yes", "skip the confirmation prompt").option("--dry-run", "print the change list and exit; don't write").option(
|
|
@@ -7343,7 +7618,7 @@ var downloadCommand = new Command20("download").enablePositionalOptions().descri
|
|
|
7343
7618
|
throw new Error("cloud download does not yet support --dry-run; omit to bulk-pull /workspace.");
|
|
7344
7619
|
}
|
|
7345
7620
|
if (!opts.respectGitignore || opts.includeNodeModules || opts.withEnv || opts.pattern.length > 0) {
|
|
7346
|
-
|
|
7621
|
+
log26.warn(
|
|
7347
7622
|
"cloud download ignores gitignore/--with-env/--pattern filters in v1 \u2014 pulling the whole /workspace tree (Phase 6 polish)."
|
|
7348
7623
|
);
|
|
7349
7624
|
}
|
|
@@ -7353,7 +7628,7 @@ var downloadCommand = new Command20("download").enablePositionalOptions().descri
|
|
|
7353
7628
|
initialValue: false
|
|
7354
7629
|
});
|
|
7355
7630
|
if (isCancel14(ok) || !ok) {
|
|
7356
|
-
|
|
7631
|
+
log26.info("cancelled");
|
|
7357
7632
|
return;
|
|
7358
7633
|
}
|
|
7359
7634
|
}
|
|
@@ -7372,17 +7647,17 @@ var downloadCommand = new Command20("download").enablePositionalOptions().descri
|
|
|
7372
7647
|
}
|
|
7373
7648
|
const insp = await inspectBox(box.id);
|
|
7374
7649
|
if (insp.state === "paused") {
|
|
7375
|
-
|
|
7650
|
+
log26.info("box is paused; unpausing");
|
|
7376
7651
|
await unpauseBox(box.id);
|
|
7377
7652
|
} else if (insp.state === "stopped") {
|
|
7378
|
-
|
|
7653
|
+
log26.info("box is stopped; starting");
|
|
7379
7654
|
await startBox(box.id);
|
|
7380
7655
|
} else if (insp.state === "missing") {
|
|
7381
7656
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
7382
7657
|
}
|
|
7383
7658
|
const rootWorktree = box.gitWorktrees?.find((w) => w.kind === "root");
|
|
7384
7659
|
if (rootWorktree) {
|
|
7385
|
-
|
|
7660
|
+
log26.warn(
|
|
7386
7661
|
`This box has been committing to branch \`${rootWorktree.branch}\` in a separate worktree.
|
|
7387
7662
|
For a git-aware merge instead of a file copy, run from your checkout:
|
|
7388
7663
|
git merge ${rootWorktree.branch}
|
|
@@ -7418,7 +7693,7 @@ Continuing with rsync into ${box.workspacePath}`
|
|
|
7418
7693
|
initialValue: false
|
|
7419
7694
|
});
|
|
7420
7695
|
if (isCancel14(ok) || !ok) {
|
|
7421
|
-
|
|
7696
|
+
log26.info("cancelled");
|
|
7422
7697
|
return;
|
|
7423
7698
|
}
|
|
7424
7699
|
}
|
|
@@ -7446,8 +7721,8 @@ downloadCommand.addCommand(downloadOpencodeCommand);
|
|
|
7446
7721
|
downloadCommand.addCommand(downloadConfigCommand);
|
|
7447
7722
|
|
|
7448
7723
|
// src/commands/drive.ts
|
|
7449
|
-
import { log as
|
|
7450
|
-
import { Command as
|
|
7724
|
+
import { log as log27 } from "@clack/prompts";
|
|
7725
|
+
import { Command as Command22 } from "commander";
|
|
7451
7726
|
|
|
7452
7727
|
// src/lib/drive/keys.ts
|
|
7453
7728
|
var NAMED = {
|
|
@@ -7629,11 +7904,11 @@ var SessionNotFoundError = class extends Error {
|
|
|
7629
7904
|
// src/commands/drive.ts
|
|
7630
7905
|
var PROMPT_ENTER_DELAY_MS = 200;
|
|
7631
7906
|
var POLL_INTERVAL_MS2 = 250;
|
|
7632
|
-
var driveCommand = new
|
|
7907
|
+
var driveCommand = new Command22("drive").description(
|
|
7633
7908
|
"Drive a running tmux session inside a box: snapshot the screen, send keystrokes, type text, or wait for output. Targets the agent session by default (claude \u2192 codex \u2192 opencode)."
|
|
7634
7909
|
);
|
|
7635
7910
|
var sessionOption = ["--session <name>", "tmux session to target (default: first running agent session)"];
|
|
7636
|
-
var driveSnapshotCommand = new
|
|
7911
|
+
var driveSnapshotCommand = new Command22("snapshot").description("Print the rendered terminal contents of the box's active tmux session.").argument(
|
|
7637
7912
|
"[box]",
|
|
7638
7913
|
"box ref: project index, id, id prefix, name, or container (default: the only box in this project)"
|
|
7639
7914
|
).option(sessionOption[0], sessionOption[1]).option("--ansi", "preserve ANSI color/style escape sequences (default: plain text)").option("--with-cursor", "include cursor coordinates and pane size (implies --json)").option("--rows <range>", 'inclusive row range "FROM:TO" (negative numbers walk into scrollback)').option("--json", "emit a JSON envelope { session, cols, rows, cursor?, screen }").action(async (boxRef, opts) => {
|
|
@@ -7662,7 +7937,7 @@ var driveSnapshotCommand = new Command21("snapshot").description("Print the rend
|
|
|
7662
7937
|
handleDriveError(err);
|
|
7663
7938
|
}
|
|
7664
7939
|
});
|
|
7665
|
-
var driveKeypressCommand = new
|
|
7940
|
+
var driveKeypressCommand = new Command22("keypress").description('Send keystrokes parsed via the DSL (e.g. "<C-a>q", "ls<Enter>"). Each arg is concatenated with no spaces.').argument(
|
|
7666
7941
|
"<box>",
|
|
7667
7942
|
"box ref: project index, id, id prefix, name, or container"
|
|
7668
7943
|
).argument("<keys...>", "one or more DSL tokens / literal text; `<<` escapes a literal `<`").option(sessionOption[0], sessionOption[1]).action(async (boxRef, keys, opts) => {
|
|
@@ -7676,7 +7951,7 @@ var driveKeypressCommand = new Command21("keypress").description('Send keystroke
|
|
|
7676
7951
|
handleDriveError(err);
|
|
7677
7952
|
}
|
|
7678
7953
|
});
|
|
7679
|
-
var driveSendTextCommand = new
|
|
7954
|
+
var driveSendTextCommand = new Command22("send-text").description("Type literal text into the session (no DSL parsing, no trailing Enter).").argument("<box>", "box ref").argument("<text>", "literal text to type").option(sessionOption[0], sessionOption[1]).action(async (boxRef, text, opts) => {
|
|
7680
7955
|
try {
|
|
7681
7956
|
const box = await resolveBoxOrExit(boxRef);
|
|
7682
7957
|
const provider = await providerForBox(box);
|
|
@@ -7686,7 +7961,7 @@ var driveSendTextCommand = new Command21("send-text").description("Type literal
|
|
|
7686
7961
|
handleDriveError(err);
|
|
7687
7962
|
}
|
|
7688
7963
|
});
|
|
7689
|
-
var drivePromptCommand = new
|
|
7964
|
+
var drivePromptCommand = new Command22("prompt").description('Type text into the agent session and press Enter \u2014 convenience for "send a message to the running agent".').argument("<box>", "box ref").argument("<text>", "prompt text to send (literal; no DSL parsing)").option(sessionOption[0], sessionOption[1]).option("--delay <ms>", `milliseconds to wait between text and Enter (default: ${String(PROMPT_ENTER_DELAY_MS)})`).action(async (boxRef, text, opts) => {
|
|
7690
7965
|
try {
|
|
7691
7966
|
const box = await resolveBoxOrExit(boxRef);
|
|
7692
7967
|
const provider = await providerForBox(box);
|
|
@@ -7699,7 +7974,7 @@ var drivePromptCommand = new Command21("prompt").description('Type text into the
|
|
|
7699
7974
|
handleDriveError(err);
|
|
7700
7975
|
}
|
|
7701
7976
|
});
|
|
7702
|
-
var driveWaitCommand = new
|
|
7977
|
+
var driveWaitCommand = new Command22("wait").description("Block until --text appears in the session's rendered screen, or exit non-zero on timeout.").argument("<box>", "box ref").requiredOption("--text <str>", "substring to wait for").option("--timeout <ms>", "wall-clock cap in milliseconds (default: 5000)").option(sessionOption[0], sessionOption[1]).option("--json", "emit a JSON envelope { matched, elapsedMs, session, screen? }").action(async (boxRef, opts) => {
|
|
7703
7978
|
try {
|
|
7704
7979
|
const box = await resolveBoxOrExit(boxRef);
|
|
7705
7980
|
const provider = await providerForBox(box);
|
|
@@ -7732,14 +8007,14 @@ var driveWaitCommand = new Command21("wait").description("Block until --text app
|
|
|
7732
8007
|
}) + "\n"
|
|
7733
8008
|
);
|
|
7734
8009
|
} else {
|
|
7735
|
-
|
|
8010
|
+
log27.error(`text not found within ${String(timeoutMs)}ms: ${opts.text}`);
|
|
7736
8011
|
}
|
|
7737
8012
|
process.exit(1);
|
|
7738
8013
|
} catch (err) {
|
|
7739
8014
|
handleDriveError(err);
|
|
7740
8015
|
}
|
|
7741
8016
|
});
|
|
7742
|
-
var driveResizeCommand = new
|
|
8017
|
+
var driveResizeCommand = new Command22("resize").description("Resize the tmux window to <cols> x <rows>.").argument("<box>", "box ref").argument("<cols>", "columns (positive int)").argument("<rows>", "rows (positive int)").option(sessionOption[0], sessionOption[1]).action(async (boxRef, colsStr, rowsStr, opts) => {
|
|
7743
8018
|
try {
|
|
7744
8019
|
const box = await resolveBoxOrExit(boxRef);
|
|
7745
8020
|
const provider = await providerForBox(box);
|
|
@@ -7759,8 +8034,8 @@ driveCommand.addCommand(driveWaitCommand);
|
|
|
7759
8034
|
driveCommand.addCommand(driveResizeCommand);
|
|
7760
8035
|
function handleDriveError(err) {
|
|
7761
8036
|
if (err instanceof SessionNotFoundError) {
|
|
7762
|
-
|
|
7763
|
-
|
|
8037
|
+
log27.error(err.message);
|
|
8038
|
+
log27.info("start an agent first (e.g. `agentbox claude <box>`) or pass --session.");
|
|
7764
8039
|
process.exit(2);
|
|
7765
8040
|
}
|
|
7766
8041
|
handleLifecycleError(err);
|
|
@@ -7784,8 +8059,8 @@ function sleep2(ms) {
|
|
|
7784
8059
|
}
|
|
7785
8060
|
|
|
7786
8061
|
// src/commands/fork.ts
|
|
7787
|
-
import { log as
|
|
7788
|
-
import { Command as
|
|
8062
|
+
import { log as log28 } from "@clack/prompts";
|
|
8063
|
+
import { Command as Command23 } from "commander";
|
|
7789
8064
|
import { existsSync as existsSync4, readdirSync, statSync } from "fs";
|
|
7790
8065
|
import { homedir as homedir8 } from "os";
|
|
7791
8066
|
import { join as join10 } from "path";
|
|
@@ -7837,7 +8112,7 @@ function resolveAttachArgs(attachIn) {
|
|
|
7837
8112
|
if (attachIn === "same") return ["--attach-in", "same"];
|
|
7838
8113
|
return detectHostTerminal() === "unknown" ? ["--no-attach"] : ["--attach-in", attachIn];
|
|
7839
8114
|
}
|
|
7840
|
-
var forkCommand = new
|
|
8115
|
+
var forkCommand = new Command23("fork").description(
|
|
7841
8116
|
"Fork the current host agent session into a new box and resume it there. Opens the box in a new terminal tab under iTerm/tmux; otherwise starts it in the background."
|
|
7842
8117
|
).option("-w, --workspace <path>", "host workspace to mount", process.cwd()).option(
|
|
7843
8118
|
"--agent <name>",
|
|
@@ -7853,19 +8128,19 @@ var forkCommand = new Command22("fork").description(
|
|
|
7853
8128
|
"auto-approve agentbox.yaml's carry: block (fork skips carry by default \u2014 it does not silently re-copy host files into the new box)"
|
|
7854
8129
|
).action(async (opts) => {
|
|
7855
8130
|
if ((process.env.AGENTBOX_RELAY_URL ?? "").trim().length > 0) {
|
|
7856
|
-
|
|
8131
|
+
log28.error(
|
|
7857
8132
|
"agentbox fork runs on the host only: it teleports a host agent session into a new box. You appear to be inside a box (AGENTBOX_RELAY_URL is set) \u2014 box\u2192box fork is not supported yet."
|
|
7858
8133
|
);
|
|
7859
8134
|
process.exit(2);
|
|
7860
8135
|
}
|
|
7861
8136
|
const agent = opts.agent?.trim() || "claude";
|
|
7862
8137
|
if (!FORK_AGENTS.includes(agent)) {
|
|
7863
|
-
|
|
8138
|
+
log28.error(`--agent: expected one of ${FORK_AGENTS.join(", ")}, got "${opts.agent ?? ""}"`);
|
|
7864
8139
|
process.exit(2);
|
|
7865
8140
|
}
|
|
7866
8141
|
const attachIn = opts.attachIn ?? "tab";
|
|
7867
8142
|
if (!FORK_ATTACH_VALUES.includes(attachIn)) {
|
|
7868
|
-
|
|
8143
|
+
log28.error(`--attach-in: expected one of ${FORK_ATTACH_VALUES.join(", ")}, got "${attachIn}"`);
|
|
7869
8144
|
process.exit(2);
|
|
7870
8145
|
}
|
|
7871
8146
|
const provider = opts.provider?.trim();
|
|
@@ -7873,7 +8148,7 @@ var forkCommand = new Command22("fork").description(
|
|
|
7873
8148
|
try {
|
|
7874
8149
|
sessionArgs = resolveSessionArgs(agent, opts);
|
|
7875
8150
|
} catch (err) {
|
|
7876
|
-
|
|
8151
|
+
log28.error(err instanceof Error ? err.message : String(err));
|
|
7877
8152
|
process.exit(2);
|
|
7878
8153
|
}
|
|
7879
8154
|
const subArgv = [
|
|
@@ -7891,8 +8166,8 @@ var forkCommand = new Command22("fork").description(
|
|
|
7891
8166
|
});
|
|
7892
8167
|
|
|
7893
8168
|
// src/commands/install.ts
|
|
7894
|
-
import { intro as intro5, log as
|
|
7895
|
-
import { Command as
|
|
8169
|
+
import { intro as intro5, log as log29, outro as outro6 } from "@clack/prompts";
|
|
8170
|
+
import { Command as Command24 } from "commander";
|
|
7896
8171
|
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync, writeFileSync as writeFileSync3 } from "fs";
|
|
7897
8172
|
import { homedir as homedir9 } from "os";
|
|
7898
8173
|
import { dirname, join as join11, resolve as resolve2 } from "path";
|
|
@@ -7941,7 +8216,7 @@ function writableReason(target, force) {
|
|
|
7941
8216
|
}
|
|
7942
8217
|
return force ? "forced" : "skip";
|
|
7943
8218
|
}
|
|
7944
|
-
var installCommand = new
|
|
8219
|
+
var installCommand = new Command24("install").description(
|
|
7945
8220
|
"Install AgentBox's host-side /agentbox fork command into Claude (~/.claude/skills), and \u2014 when detected \u2014 into Codex (~/.codex/prompts) and OpenCode (~/.config/opencode/commands). Idempotent."
|
|
7946
8221
|
).option("--force", "overwrite existing files even if not AgentBox-managed").option("--dry-run", "print what would be written without changing anything").action((opts) => {
|
|
7947
8222
|
intro5("Installing AgentBox host commands...");
|
|
@@ -7951,7 +8226,7 @@ var installCommand = new Command23("install").description(
|
|
|
7951
8226
|
try {
|
|
7952
8227
|
srcDir = resolveHostSkillsDir();
|
|
7953
8228
|
} catch (err) {
|
|
7954
|
-
|
|
8229
|
+
log29.error(err instanceof Error ? err.message : String(err));
|
|
7955
8230
|
process.exit(1);
|
|
7956
8231
|
}
|
|
7957
8232
|
const written = [];
|
|
@@ -7959,19 +8234,19 @@ var installCommand = new Command23("install").description(
|
|
|
7959
8234
|
for (const t of installTargets()) {
|
|
7960
8235
|
const src = join11(srcDir, t.src);
|
|
7961
8236
|
if (!existsSync5(src)) {
|
|
7962
|
-
|
|
8237
|
+
log29.warn(`bundled file missing (skipped): ${src}`);
|
|
7963
8238
|
skipped++;
|
|
7964
8239
|
continue;
|
|
7965
8240
|
}
|
|
7966
8241
|
if (t.gateDir && !existsSync5(t.gateDir)) continue;
|
|
7967
8242
|
const reason = writableReason(t.dest, force);
|
|
7968
8243
|
if (reason === "skip") {
|
|
7969
|
-
|
|
8244
|
+
log29.warn(`user-modified file at ${t.dest}, skipping; pass --force to overwrite`);
|
|
7970
8245
|
skipped++;
|
|
7971
8246
|
continue;
|
|
7972
8247
|
}
|
|
7973
8248
|
if (dryRun) {
|
|
7974
|
-
|
|
8249
|
+
log29.info(`would write ${t.dest} (${reason})`);
|
|
7975
8250
|
written.push(t.dest);
|
|
7976
8251
|
continue;
|
|
7977
8252
|
}
|
|
@@ -7991,7 +8266,7 @@ var installCommand = new Command23("install").description(
|
|
|
7991
8266
|
});
|
|
7992
8267
|
|
|
7993
8268
|
// src/commands/git.ts
|
|
7994
|
-
import { Command as
|
|
8269
|
+
import { Command as Command25 } from "commander";
|
|
7995
8270
|
var WORKSPACE = "/workspace";
|
|
7996
8271
|
var TOKEN_TTL_MS = 12e4;
|
|
7997
8272
|
async function runInBox(box, argv) {
|
|
@@ -8023,7 +8298,7 @@ function buildPredictedGhPrParams(ghArgs) {
|
|
|
8023
8298
|
async function exitWith(code) {
|
|
8024
8299
|
process.exit(code);
|
|
8025
8300
|
}
|
|
8026
|
-
var pushCommand = new
|
|
8301
|
+
var pushCommand = new Command25("push").description("Push the box's branch via the host relay (host creds, no prompt)").argument("<box>", "box ref: project index, id, id prefix, name, or container").argument("[args...]", "extra flags forwarded to `agentbox-ctl git push` (e.g. --force-with-lease, --tags)").option("--remote <name>", "remote name (default: origin)").allowExcessArguments(true).allowUnknownOption(true).action(async (boxRef, args, opts) => {
|
|
8027
8302
|
try {
|
|
8028
8303
|
const box = await resolveBoxOrExit(boxRef);
|
|
8029
8304
|
const predicted = buildPredictedGitParams(opts.remote, args);
|
|
@@ -8036,7 +8311,7 @@ var pushCommand = new Command24("push").description("Push the box's branch via t
|
|
|
8036
8311
|
handleLifecycleError(err);
|
|
8037
8312
|
}
|
|
8038
8313
|
});
|
|
8039
|
-
var fetchCommand = new
|
|
8314
|
+
var fetchCommand = new Command25("fetch").description("Fetch via the host relay (refs land in the shared .git)").argument("<box>", "box ref").argument("[args...]", "extra flags forwarded to `agentbox-ctl git fetch` (e.g. --prune)").option("--remote <name>", "remote name (default: origin)").allowExcessArguments(true).allowUnknownOption(true).action(async (boxRef, args, opts) => {
|
|
8040
8315
|
try {
|
|
8041
8316
|
const box = await resolveBoxOrExit(boxRef);
|
|
8042
8317
|
const predicted = buildPredictedGitParams(opts.remote, args);
|
|
@@ -8049,7 +8324,7 @@ var fetchCommand = new Command24("fetch").description("Fetch via the host relay
|
|
|
8049
8324
|
handleLifecycleError(err);
|
|
8050
8325
|
}
|
|
8051
8326
|
});
|
|
8052
|
-
var pullCommand = new
|
|
8327
|
+
var pullCommand = new Command25("pull").description(
|
|
8053
8328
|
"Fetch via the relay then merge in /workspace. With <branch>: first `git checkout <branch>` so the box switches base branch and pulls latest \u2014 useful for reusing a box on a new task."
|
|
8054
8329
|
).argument("<box>", "box ref").argument("[branch]", "optional branch to switch to before pulling (e.g. main)").argument("[args...]", "extra flags forwarded to `agentbox-ctl git pull`").option("--remote <name>", "remote name (default: origin)").option("--ff-only", "pass --ff-only to the in-box merge").allowExcessArguments(true).allowUnknownOption(true).action(
|
|
8055
8330
|
async (boxRef, branch, args, opts) => {
|
|
@@ -8071,7 +8346,7 @@ var pullCommand = new Command24("pull").description(
|
|
|
8071
8346
|
}
|
|
8072
8347
|
}
|
|
8073
8348
|
);
|
|
8074
|
-
var checkoutCommand = new
|
|
8349
|
+
var checkoutCommand = new Command25("checkout").description("Change the box's working branch (runs `git checkout <branch>` in /workspace)").argument("<box>", "box ref").argument("<branch>", "branch to check out inside the box").argument("[args...]", "extra flags forwarded to `git checkout`").allowExcessArguments(true).allowUnknownOption(true).action(async (boxRef, branch, args) => {
|
|
8075
8350
|
try {
|
|
8076
8351
|
const box = await resolveBoxOrExit(boxRef);
|
|
8077
8352
|
await exitWith(await runAndStream(box, ["git", "checkout", branch, ...args]));
|
|
@@ -8079,7 +8354,7 @@ var checkoutCommand = new Command24("checkout").description("Change the box's wo
|
|
|
8079
8354
|
handleLifecycleError(err);
|
|
8080
8355
|
}
|
|
8081
8356
|
});
|
|
8082
|
-
var statusCommand = new
|
|
8357
|
+
var statusCommand = new Command25("status").description("Run `git status` in the box's /workspace (read-only, no relay)").argument("<box>", "box ref").argument("[args...]", "extra flags forwarded to `git status`").allowExcessArguments(true).allowUnknownOption(true).action(async (boxRef, args) => {
|
|
8083
8358
|
try {
|
|
8084
8359
|
const box = await resolveBoxOrExit(boxRef);
|
|
8085
8360
|
await exitWith(await runAndStream(box, ["git", "status", ...args]));
|
|
@@ -8098,21 +8373,18 @@ var PR_OP_DESCRIPTIONS = {
|
|
|
8098
8373
|
close: "Close a PR.",
|
|
8099
8374
|
reopen: "Reopen a PR."
|
|
8100
8375
|
};
|
|
8101
|
-
function
|
|
8102
|
-
if (op !== "create") return args;
|
|
8103
|
-
if (args.some((a) => a === "--head" || a.startsWith("--head="))) return args;
|
|
8376
|
+
function injectPrCreateHead2(op, box, args) {
|
|
8104
8377
|
const rootWt = (box.gitWorktrees ?? []).find((w) => w.kind === "root");
|
|
8105
|
-
|
|
8106
|
-
return ["--head", rootWt.branch, ...args];
|
|
8378
|
+
return injectPrCreateHead(op, rootWt?.branch, args);
|
|
8107
8379
|
}
|
|
8108
8380
|
function buildPrSubcommand(op) {
|
|
8109
|
-
return new
|
|
8381
|
+
return new Command25(op).description(PR_OP_DESCRIPTIONS[op]).argument("<box>", "box ref").argument(
|
|
8110
8382
|
"[args...]",
|
|
8111
8383
|
"extra flags forwarded to `gh pr <op>` (e.g. --title, --body, --label, --draft, --json)"
|
|
8112
8384
|
).allowExcessArguments(true).allowUnknownOption(true).action(async (boxRef, args) => {
|
|
8113
8385
|
try {
|
|
8114
8386
|
const box = await resolveBoxOrExit(boxRef);
|
|
8115
|
-
const ghArgs =
|
|
8387
|
+
const ghArgs = injectPrCreateHead2(op, box, args);
|
|
8116
8388
|
const predicted = buildPredictedGhPrParams(ghArgs);
|
|
8117
8389
|
const tokenArgs = await hostInitiatedArgs(box.id, `gh.pr.${op}`, predicted);
|
|
8118
8390
|
const argv = ["agentbox-ctl", "gh", "pr", op, ...tokenArgs, ...ghArgs];
|
|
@@ -8122,18 +8394,18 @@ function buildPrSubcommand(op) {
|
|
|
8122
8394
|
}
|
|
8123
8395
|
});
|
|
8124
8396
|
}
|
|
8125
|
-
var prCommand = new
|
|
8397
|
+
var prCommand = new Command25("pr").description(
|
|
8126
8398
|
"PR operations against a box's branch via the host `gh` CLI"
|
|
8127
8399
|
);
|
|
8128
8400
|
for (const op of GH_PR_OPS) {
|
|
8129
8401
|
const sub = buildPrSubcommand(op);
|
|
8130
8402
|
prCommand.addCommand(sub, op === "create" ? { isDefault: true } : void 0);
|
|
8131
8403
|
}
|
|
8132
|
-
var gitCommand = new
|
|
8404
|
+
var gitCommand = new Command25("git").description("Run git / gh pr operations against a box from the host").addCommand(pushCommand).addCommand(fetchCommand).addCommand(pullCommand).addCommand(checkoutCommand).addCommand(statusCommand).addCommand(prCommand);
|
|
8133
8405
|
|
|
8134
8406
|
// src/commands/list.ts
|
|
8135
|
-
import { log as
|
|
8136
|
-
import { Command as
|
|
8407
|
+
import { log as log30 } from "@clack/prompts";
|
|
8408
|
+
import { Command as Command26 } from "commander";
|
|
8137
8409
|
import { pathToFileURL } from "url";
|
|
8138
8410
|
|
|
8139
8411
|
// src/hyperlink.ts
|
|
@@ -8303,10 +8575,10 @@ async function buildListText(all) {
|
|
|
8303
8575
|
${table}`;
|
|
8304
8576
|
}
|
|
8305
8577
|
var listCommand2 = withWatchOptions(
|
|
8306
|
-
new
|
|
8578
|
+
new Command26("list").alias("ls").description("List agent boxes in the current project (-g for all)").option("-j, --json", "machine-readable JSON output").option("-g, --global", "include boxes from all projects")
|
|
8307
8579
|
).action(async (opts) => {
|
|
8308
8580
|
if (opts.json && opts.watch) {
|
|
8309
|
-
|
|
8581
|
+
log30.error("cannot combine --json with --watch");
|
|
8310
8582
|
process.exit(2);
|
|
8311
8583
|
}
|
|
8312
8584
|
const all = opts.global ?? false;
|
|
@@ -8323,11 +8595,11 @@ var listCommand2 = withWatchOptions(
|
|
|
8323
8595
|
});
|
|
8324
8596
|
|
|
8325
8597
|
// src/commands/logs.ts
|
|
8326
|
-
import { log as
|
|
8327
|
-
import { Command as
|
|
8598
|
+
import { log as log31 } from "@clack/prompts";
|
|
8599
|
+
import { Command as Command27 } from "commander";
|
|
8328
8600
|
import { spawn as spawn3 } from "child_process";
|
|
8329
8601
|
var DAEMON_LOG_PATH = "/var/log/agentbox/ctl-daemon.log";
|
|
8330
|
-
var logsCommand = new
|
|
8602
|
+
var logsCommand = new Command27("logs").description("Print recent log lines from a box service; -f to stream").argument(
|
|
8331
8603
|
"[box]",
|
|
8332
8604
|
"box ref (optional when cwd has exactly 1 box): project index, id, id prefix, name, or container"
|
|
8333
8605
|
).argument("[service]", "service name from agentbox.yaml").option("-n, --tail <n>", "how many recent lines to print first", "200").option("-f, --follow", "keep the connection open and stream new lines").option(
|
|
@@ -8345,9 +8617,9 @@ var logsCommand = new Command26("logs").description("Print recent log lines from
|
|
|
8345
8617
|
service = boxArg;
|
|
8346
8618
|
}
|
|
8347
8619
|
if (!service && !opts.daemon) {
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8620
|
+
log31.error("missing <service> argument");
|
|
8621
|
+
log31.info("usage: agentbox logs [box] <service> [-n N] [-f]");
|
|
8622
|
+
log31.info(" agentbox logs [box] --daemon [-n N] [-f]");
|
|
8351
8623
|
process.exit(2);
|
|
8352
8624
|
}
|
|
8353
8625
|
const box = await resolveBoxOrExit(idOrName);
|
|
@@ -8358,7 +8630,7 @@ var logsCommand = new Command26("logs").description("Print recent log lines from
|
|
|
8358
8630
|
if (!opts.follow) {
|
|
8359
8631
|
const proc = await provider.exec(box, args, { user: "vscode" });
|
|
8360
8632
|
if (proc.exitCode !== 0) {
|
|
8361
|
-
|
|
8633
|
+
log31.error(
|
|
8362
8634
|
`${opts.daemon ? "daemon log" : "agentbox-ctl logs"} failed: ${proc.stderr || proc.stdout}`
|
|
8363
8635
|
);
|
|
8364
8636
|
process.exit(1);
|
|
@@ -8392,7 +8664,10 @@ var logsCommand = new Command26("logs").description("Print recent log lines from
|
|
|
8392
8664
|
});
|
|
8393
8665
|
const [argv0, ...rest] = spec.argv;
|
|
8394
8666
|
if (!argv0) throw new Error("provider.buildAttach returned an empty argv");
|
|
8395
|
-
const child = spawn3(argv0, rest, {
|
|
8667
|
+
const child = spawn3(argv0, rest, {
|
|
8668
|
+
stdio: ["ignore", "inherit", "inherit"],
|
|
8669
|
+
env: spec.env ? { ...process.env, ...spec.env } : process.env
|
|
8670
|
+
});
|
|
8396
8671
|
const cleanup = async () => {
|
|
8397
8672
|
if (spec.cleanup) await spec.cleanup();
|
|
8398
8673
|
};
|
|
@@ -8411,12 +8686,12 @@ var logsCommand = new Command26("logs").description("Print recent log lines from
|
|
|
8411
8686
|
});
|
|
8412
8687
|
|
|
8413
8688
|
// src/commands/open.ts
|
|
8414
|
-
import { log as
|
|
8689
|
+
import { log as log32 } from "@clack/prompts";
|
|
8415
8690
|
import { execa as execa2 } from "execa";
|
|
8416
8691
|
import { existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
8417
8692
|
import { homedir as homedir10 } from "os";
|
|
8418
8693
|
import { join as join12 } from "path";
|
|
8419
|
-
import { Command as
|
|
8694
|
+
import { Command as Command28 } from "commander";
|
|
8420
8695
|
|
|
8421
8696
|
// src/commands/path.ts
|
|
8422
8697
|
async function runPath(box, opts) {
|
|
@@ -8438,7 +8713,7 @@ async function runPath(box, opts) {
|
|
|
8438
8713
|
}
|
|
8439
8714
|
|
|
8440
8715
|
// src/commands/open.ts
|
|
8441
|
-
var openCommand = new
|
|
8716
|
+
var openCommand = new Command28("open").description("Open a box's /workspace in Finder (docker: rsync'd snapshot; cloud: sshfs mount)").argument(
|
|
8442
8717
|
"[box]",
|
|
8443
8718
|
"box ref: project index, id, id prefix, name, or container (default: the only box in this project)"
|
|
8444
8719
|
).option("--no-refresh", "skip the rsync; open whatever's already on disk (docker only)").option(
|
|
@@ -8517,10 +8792,10 @@ async function runCloudOpen(box, provider, opts) {
|
|
|
8517
8792
|
if (!existsSync6(mountRoot)) {
|
|
8518
8793
|
mkdirSync4(mountRoot, { recursive: true, mode: 493 });
|
|
8519
8794
|
} else if (await isMounted(mountRoot)) {
|
|
8520
|
-
|
|
8795
|
+
log32.info(`re-mounting (stale mount detected at ${mountRoot})`);
|
|
8521
8796
|
await tryUnmount(mountRoot);
|
|
8522
8797
|
}
|
|
8523
|
-
|
|
8798
|
+
log32.info(`mounting ${alias}:/workspace at ${mountRoot}`);
|
|
8524
8799
|
const mount = await execa2(
|
|
8525
8800
|
sshfsBin,
|
|
8526
8801
|
[
|
|
@@ -8567,8 +8842,8 @@ async function tryUnmount(path) {
|
|
|
8567
8842
|
}
|
|
8568
8843
|
|
|
8569
8844
|
// src/commands/pause.ts
|
|
8570
|
-
import { Command as
|
|
8571
|
-
var pauseCommand = new
|
|
8845
|
+
import { Command as Command29 } from "commander";
|
|
8846
|
+
var pauseCommand = new Command29("pause").description(
|
|
8572
8847
|
"Pause a box. Docker: `docker pause` (cgroup freeze \u2014 sub-second resume). Cloud: backend.pause (Daytona archive \u2014 cold storage; resume is slower but uses no quota while archived)."
|
|
8573
8848
|
).argument(
|
|
8574
8849
|
"[box]",
|
|
@@ -8591,8 +8866,8 @@ var pauseCommand = new Command28("pause").description(
|
|
|
8591
8866
|
});
|
|
8592
8867
|
|
|
8593
8868
|
// src/commands/prepare.ts
|
|
8594
|
-
import { intro as intro6, log as
|
|
8595
|
-
import { Command as
|
|
8869
|
+
import { intro as intro6, log as log33, spinner as spinner7 } from "@clack/prompts";
|
|
8870
|
+
import { Command as Command30 } from "commander";
|
|
8596
8871
|
async function dockerStatus() {
|
|
8597
8872
|
let img;
|
|
8598
8873
|
try {
|
|
@@ -8650,7 +8925,7 @@ async function renderDocker(status) {
|
|
|
8650
8925
|
}
|
|
8651
8926
|
async function daytonaStatus() {
|
|
8652
8927
|
try {
|
|
8653
|
-
const mod = await import("./dist-
|
|
8928
|
+
const mod = await import("./dist-CX5CGVEB.js");
|
|
8654
8929
|
return await mod.getDaytonaStatus();
|
|
8655
8930
|
} catch (err) {
|
|
8656
8931
|
return { configured: false, reason: err instanceof Error ? err.message.split("\n")[0] : String(err) };
|
|
@@ -8709,11 +8984,11 @@ async function showStatus(opts) {
|
|
|
8709
8984
|
}
|
|
8710
8985
|
process.stdout.write(lines.join("\n") + "\n");
|
|
8711
8986
|
}
|
|
8712
|
-
var prepareCommand = new
|
|
8987
|
+
var prepareCommand = new Command30("prepare").description(
|
|
8713
8988
|
"Build base sandbox images / snapshots, or show what is already prepared across providers."
|
|
8714
8989
|
).option(
|
|
8715
8990
|
"-p, --provider <name>",
|
|
8716
|
-
"provider to prepare (docker | daytona | hetzner). Omit for status-only."
|
|
8991
|
+
"provider to prepare (docker | daytona | hetzner | vercel). Omit for status-only."
|
|
8717
8992
|
).option(
|
|
8718
8993
|
"-n, --name <name>",
|
|
8719
8994
|
"snapshot name (Daytona only; default: agentbox-base-<timestamp>)"
|
|
@@ -8738,7 +9013,7 @@ var prepareCommand = new Command29("prepare").description(
|
|
|
8738
9013
|
}
|
|
8739
9014
|
const provider = await getProvider(providerName);
|
|
8740
9015
|
if (typeof provider.prepare !== "function") {
|
|
8741
|
-
|
|
9016
|
+
log33.error(`provider '${providerName}' does not implement prepare`);
|
|
8742
9017
|
process.exit(1);
|
|
8743
9018
|
}
|
|
8744
9019
|
const sp = spinner7();
|
|
@@ -8759,10 +9034,10 @@ var prepareCommand = new Command29("prepare").description(
|
|
|
8759
9034
|
result.snapshotName,
|
|
8760
9035
|
process.cwd()
|
|
8761
9036
|
);
|
|
8762
|
-
|
|
9037
|
+
log33.success(`box.image = ${result.snapshotName} (written to ${written.path})`);
|
|
8763
9038
|
} catch (err) {
|
|
8764
9039
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8765
|
-
|
|
9040
|
+
log33.warn(
|
|
8766
9041
|
`prepared snapshot '${result.snapshotName}', but failed to pin it into the project config: ${msg}
|
|
8767
9042
|
Run \`agentbox config set --project box.image ${result.snapshotName}\` manually.`
|
|
8768
9043
|
);
|
|
@@ -8772,7 +9047,7 @@ Run \`agentbox config set --project box.image ${result.snapshotName}\` manually.
|
|
|
8772
9047
|
}
|
|
8773
9048
|
process.stdout.write("\n");
|
|
8774
9049
|
await showStatus({ onlyProvider: providerName });
|
|
8775
|
-
|
|
9050
|
+
log33.info(
|
|
8776
9051
|
"tip: install the agentbox host skill so Claude Code on this machine can drive AgentBox for you:\n npx skills add https://github.com/madarco/agentbox --skill agentbox"
|
|
8777
9052
|
);
|
|
8778
9053
|
} catch (err) {
|
|
@@ -8802,8 +9077,8 @@ function describeError(err) {
|
|
|
8802
9077
|
}
|
|
8803
9078
|
|
|
8804
9079
|
// src/commands/prune.ts
|
|
8805
|
-
import { confirm as confirm14, isCancel as isCancel15, log as
|
|
8806
|
-
import { Command as
|
|
9080
|
+
import { confirm as confirm14, isCancel as isCancel15, log as log34 } from "@clack/prompts";
|
|
9081
|
+
import { Command as Command31 } from "commander";
|
|
8807
9082
|
function totalRemovals(r, projectConfigs) {
|
|
8808
9083
|
return r.removedRecords.length + r.removedContainers.length + r.removedVolumes.length + r.removedSnapshotDirs.length + r.removedBoxDirs.length + projectConfigs.length;
|
|
8809
9084
|
}
|
|
@@ -8849,20 +9124,20 @@ async function liveProjectRoots() {
|
|
|
8849
9124
|
return [];
|
|
8850
9125
|
}
|
|
8851
9126
|
}
|
|
8852
|
-
var pruneCommand = new
|
|
9127
|
+
var pruneCommand = new Command31("prune").description("Clean up orphan state.json records (and with --all, orphan docker resources)").option("--dry-run", "show what would be removed, don't change anything").option(
|
|
8853
9128
|
"--all",
|
|
8854
9129
|
"also remove orphan agentbox-* containers, volumes, snapshot dirs, and orphan per-project config dirs"
|
|
8855
9130
|
).option("-y, --yes", "skip the confirmation prompt").option(
|
|
8856
9131
|
"--provider <name>",
|
|
8857
|
-
"restrict prune to a specific provider (docker | daytona). For
|
|
9132
|
+
"restrict prune to a specific provider (docker | daytona | hetzner | vercel). For cloud providers, lists sandboxes that are not in this CLI's state.json and offers to delete them."
|
|
8858
9133
|
).action(async (opts) => {
|
|
8859
9134
|
try {
|
|
8860
|
-
if (opts.provider
|
|
8861
|
-
await
|
|
9135
|
+
if (opts.provider !== void 0 && isCloudPruneProvider(opts.provider)) {
|
|
9136
|
+
await pruneCloud(opts.provider, opts);
|
|
8862
9137
|
return;
|
|
8863
9138
|
}
|
|
8864
9139
|
if (opts.provider !== void 0 && opts.provider !== "docker") {
|
|
8865
|
-
|
|
9140
|
+
log34.error(`unknown provider '${opts.provider}'; expected docker, daytona, hetzner, or vercel`);
|
|
8866
9141
|
process.exit(2);
|
|
8867
9142
|
}
|
|
8868
9143
|
const dryRun = opts.dryRun ?? false;
|
|
@@ -8875,13 +9150,13 @@ var pruneCommand = new Command30("prune").description("Clean up orphan state.jso
|
|
|
8875
9150
|
process.stdout.write("nothing to prune\n");
|
|
8876
9151
|
return;
|
|
8877
9152
|
}
|
|
8878
|
-
|
|
9153
|
+
log34.info(`would remove:
|
|
8879
9154
|
${summary(preview, previewProjects)}`);
|
|
8880
9155
|
if (dryRun) return;
|
|
8881
9156
|
if (!opts.yes) {
|
|
8882
9157
|
const ok = await confirm14({ message: "Proceed with prune?", initialValue: true });
|
|
8883
9158
|
if (isCancel15(ok) || !ok) {
|
|
8884
|
-
|
|
9159
|
+
log34.info("cancelled");
|
|
8885
9160
|
return;
|
|
8886
9161
|
}
|
|
8887
9162
|
}
|
|
@@ -8894,17 +9169,31 @@ ${summary(result, removedProjects)}
|
|
|
8894
9169
|
handleLifecycleError(err);
|
|
8895
9170
|
}
|
|
8896
9171
|
});
|
|
8897
|
-
|
|
9172
|
+
var CLOUD_PRUNE_PROVIDERS = ["daytona", "hetzner", "vercel"];
|
|
9173
|
+
function isCloudPruneProvider(name) {
|
|
9174
|
+
return CLOUD_PRUNE_PROVIDERS.includes(name);
|
|
9175
|
+
}
|
|
9176
|
+
async function cloudBackendFor(provider) {
|
|
9177
|
+
switch (provider) {
|
|
9178
|
+
case "daytona":
|
|
9179
|
+
return (await import("./dist-CX5CGVEB.js")).daytonaBackend;
|
|
9180
|
+
case "hetzner":
|
|
9181
|
+
return (await import("./dist-GDHP34ZK.js")).hetznerBackend;
|
|
9182
|
+
case "vercel":
|
|
9183
|
+
return (await import("./dist-XML54CNB.js")).vercelBackend;
|
|
9184
|
+
}
|
|
9185
|
+
}
|
|
9186
|
+
async function pruneCloud(provider, opts) {
|
|
8898
9187
|
const dryRun = opts.dryRun ?? false;
|
|
8899
|
-
const
|
|
8900
|
-
if (!
|
|
8901
|
-
|
|
9188
|
+
const backend = await cloudBackendFor(provider);
|
|
9189
|
+
if (!backend.list) {
|
|
9190
|
+
log34.error(`${provider} backend doesn't expose \`list()\`; cannot enumerate sandboxes for prune`);
|
|
8902
9191
|
process.exit(2);
|
|
8903
9192
|
}
|
|
8904
|
-
const [remote, state] = await Promise.all([
|
|
9193
|
+
const [remote, state] = await Promise.all([backend.list(), readState()]);
|
|
8905
9194
|
const knownIds = /* @__PURE__ */ new Set();
|
|
8906
9195
|
for (const b of state.boxes) {
|
|
8907
|
-
if ((b.provider ?? "docker") ===
|
|
9196
|
+
if ((b.provider ?? "docker") === provider && b.cloud?.sandboxId) {
|
|
8908
9197
|
knownIds.add(b.cloud.sandboxId);
|
|
8909
9198
|
}
|
|
8910
9199
|
}
|
|
@@ -8914,10 +9203,11 @@ async function pruneDaytona(opts) {
|
|
|
8914
9203
|
return friendly.length > 0;
|
|
8915
9204
|
});
|
|
8916
9205
|
if (orphans.length === 0) {
|
|
8917
|
-
process.stdout.write(
|
|
9206
|
+
process.stdout.write(`no ${provider} orphans found
|
|
9207
|
+
`);
|
|
8918
9208
|
return;
|
|
8919
9209
|
}
|
|
8920
|
-
|
|
9210
|
+
log34.info(`found ${String(orphans.length)} ${provider} sandbox(es) not in this CLI's state:`);
|
|
8921
9211
|
for (const sb of orphans) {
|
|
8922
9212
|
const parts = [sb.sandboxId];
|
|
8923
9213
|
if (sb.name) parts.push(sb.name);
|
|
@@ -8933,7 +9223,7 @@ async function pruneDaytona(opts) {
|
|
|
8933
9223
|
initialValue: false
|
|
8934
9224
|
});
|
|
8935
9225
|
if (isCancel15(ok) || !ok) {
|
|
8936
|
-
|
|
9226
|
+
log34.info("cancelled");
|
|
8937
9227
|
return;
|
|
8938
9228
|
}
|
|
8939
9229
|
}
|
|
@@ -8941,34 +9231,34 @@ async function pruneDaytona(opts) {
|
|
|
8941
9231
|
let failed = 0;
|
|
8942
9232
|
for (const sb of orphans) {
|
|
8943
9233
|
try {
|
|
8944
|
-
await
|
|
9234
|
+
await backend.destroy({ sandboxId: sb.sandboxId });
|
|
8945
9235
|
deleted++;
|
|
8946
9236
|
} catch (err) {
|
|
8947
9237
|
failed++;
|
|
8948
|
-
|
|
9238
|
+
log34.warn(
|
|
8949
9239
|
`delete ${sb.sandboxId} failed: ${err instanceof Error ? err.message : String(err)}`
|
|
8950
9240
|
);
|
|
8951
9241
|
}
|
|
8952
9242
|
}
|
|
8953
9243
|
process.stdout.write(
|
|
8954
|
-
|
|
9244
|
+
`${provider} prune: deleted ${String(deleted)}, failed ${String(failed)}
|
|
8955
9245
|
`
|
|
8956
9246
|
);
|
|
8957
9247
|
}
|
|
8958
9248
|
|
|
8959
9249
|
// src/commands/queue.ts
|
|
8960
9250
|
import { readFile as readFile4, stat as stat5 } from "fs/promises";
|
|
8961
|
-
import { intro as intro7, log as
|
|
8962
|
-
import { Command as
|
|
9251
|
+
import { intro as intro7, log as log35, outro as outro7 } from "@clack/prompts";
|
|
9252
|
+
import { Command as Command32 } from "commander";
|
|
8963
9253
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["done", "failed", "cancelled"]);
|
|
8964
|
-
var queueCommand = new
|
|
8965
|
-
var queueListCommand = new
|
|
9254
|
+
var queueCommand = new Command32("queue").description("Inspect and manage background `agentbox claude|codex|opencode -i` jobs");
|
|
9255
|
+
var queueListCommand = new Command32("list").description("List queued, running, and (with --all) terminal background jobs").option("--all", "include done/failed/cancelled jobs (default: hide terminal)").action(async (opts) => {
|
|
8966
9256
|
const jobs = await loadQueue();
|
|
8967
9257
|
const cfg = await loadQueueConfig();
|
|
8968
9258
|
const visible = opts.all === true ? jobs : jobs.filter((j) => !TERMINAL_STATUSES.has(j.status));
|
|
8969
9259
|
if (visible.length === 0) {
|
|
8970
|
-
|
|
8971
|
-
|
|
9260
|
+
log35.info(opts.all ? "no queued jobs." : "no active queued jobs (--all to see terminal).");
|
|
9261
|
+
log35.info(`queue.maxConcurrent = ${String(cfg.maxConcurrent)} (queue.enabled=${String(cfg.enabled)})`);
|
|
8972
9262
|
return;
|
|
8973
9263
|
}
|
|
8974
9264
|
const rows = visible.map((j) => ({
|
|
@@ -8993,12 +9283,12 @@ var queueListCommand = new Command31("list").description("List queued, running,
|
|
|
8993
9283
|
headers.map((h, i) => pad3(String(r[h]), widths[i])).join(" ") + "\n"
|
|
8994
9284
|
);
|
|
8995
9285
|
}
|
|
8996
|
-
|
|
9286
|
+
log35.info(`queue.maxConcurrent = ${String(cfg.maxConcurrent)} (queue.enabled=${String(cfg.enabled)})`);
|
|
8997
9287
|
});
|
|
8998
|
-
var queueShowCommand = new
|
|
9288
|
+
var queueShowCommand = new Command32("show").description("Dump a job manifest and tail its log").argument("<id>", "queue job id (from `agentbox queue list`)").option("--tail <n>", "lines of log to print (default: 50)", "50").action(async (id, opts) => {
|
|
8999
9289
|
const job = await readJob(id);
|
|
9000
9290
|
if (!job) {
|
|
9001
|
-
|
|
9291
|
+
log35.error(`no job with id ${id}`);
|
|
9002
9292
|
process.exit(1);
|
|
9003
9293
|
}
|
|
9004
9294
|
process.stdout.write(JSON.stringify(job, null, 2) + "\n");
|
|
@@ -9014,18 +9304,18 @@ var queueShowCommand = new Command31("show").description("Dump a job manifest an
|
|
|
9014
9304
|
process.stdout.write(slice.join("\n"));
|
|
9015
9305
|
if (!slice.join("\n").endsWith("\n")) process.stdout.write("\n");
|
|
9016
9306
|
} catch {
|
|
9017
|
-
|
|
9307
|
+
log35.info(`(no log at ${job.logPath} yet)`);
|
|
9018
9308
|
}
|
|
9019
9309
|
});
|
|
9020
|
-
var queueCancelCommand = new
|
|
9310
|
+
var queueCancelCommand = new Command32("cancel").description("Cancel a queued job; running jobs are NOT killed \u2014 use `agentbox destroy` instead").argument("<id>", "queue job id (from `agentbox queue list`)").action(async (id) => {
|
|
9021
9311
|
intro7(`Cancelling queue job ${id}...`);
|
|
9022
9312
|
const job = await readJob(id);
|
|
9023
9313
|
if (!job) {
|
|
9024
|
-
|
|
9314
|
+
log35.error(`no job with id ${id}`);
|
|
9025
9315
|
process.exit(1);
|
|
9026
9316
|
}
|
|
9027
9317
|
if (job.status !== "queued") {
|
|
9028
|
-
|
|
9318
|
+
log35.error(
|
|
9029
9319
|
`job ${id} is ${job.status}; cancel only flips 'queued' \u2192 'cancelled'.` + (job.status === "running" ? ` Use 'agentbox destroy ${job.boxName || id}' to stop the box.` : "")
|
|
9030
9320
|
);
|
|
9031
9321
|
process.exit(1);
|
|
@@ -9039,13 +9329,13 @@ var queueCancelCommand = new Command31("cancel").description("Cancel a queued jo
|
|
|
9039
9329
|
await writeJob(cancelled);
|
|
9040
9330
|
outro7(`job ${id} cancelled`);
|
|
9041
9331
|
});
|
|
9042
|
-
var queueClearCommand = new
|
|
9332
|
+
var queueClearCommand = new Command32("clear").description("Sweep terminal-state manifests from ~/.agentbox/queue/").option("--done", "remove done jobs").option("--failed", "remove failed jobs").option("--cancelled", "remove cancelled jobs").option("--all", "remove every terminal-state job (done + failed + cancelled)").action(async (opts) => {
|
|
9043
9333
|
const targets = /* @__PURE__ */ new Set();
|
|
9044
9334
|
if (opts.all === true || opts.done === true) targets.add("done");
|
|
9045
9335
|
if (opts.all === true || opts.failed === true) targets.add("failed");
|
|
9046
9336
|
if (opts.all === true || opts.cancelled === true) targets.add("cancelled");
|
|
9047
9337
|
if (targets.size === 0) {
|
|
9048
|
-
|
|
9338
|
+
log35.error("pick at least one of: --done, --failed, --cancelled, --all");
|
|
9049
9339
|
process.exit(2);
|
|
9050
9340
|
}
|
|
9051
9341
|
const jobs = await loadQueue();
|
|
@@ -9055,7 +9345,7 @@ var queueClearCommand = new Command31("clear").description("Sweep terminal-state
|
|
|
9055
9345
|
await deleteJob(j.id);
|
|
9056
9346
|
removed += 1;
|
|
9057
9347
|
}
|
|
9058
|
-
|
|
9348
|
+
log35.success(`removed ${String(removed)} manifest${removed === 1 ? "" : "s"}`);
|
|
9059
9349
|
});
|
|
9060
9350
|
var QUEUE_WAIT_EVENTS = [
|
|
9061
9351
|
"new-box",
|
|
@@ -9068,11 +9358,11 @@ var QUEUE_WAIT_EVENTS = [
|
|
|
9068
9358
|
var ACTIVE_JOB_STATUSES = /* @__PURE__ */ new Set(["queued", "running"]);
|
|
9069
9359
|
var DEFAULT_QUEUE_WAIT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
9070
9360
|
var QUEUE_POLL_INTERVAL_MS = 500;
|
|
9071
|
-
var queueWaitForCommand = new
|
|
9361
|
+
var queueWaitForCommand = new Command32("wait-for").description(
|
|
9072
9362
|
`Block until a queue / box event fires. <event> one of: ${QUEUE_WAIT_EVENTS.join(" | ")}.`
|
|
9073
9363
|
).argument("<event>", `target event: ${QUEUE_WAIT_EVENTS.join(" | ")}`).option("--box <ref>", "box ref (required for box-paused / box-running / box-stopped)").option("--job <id>", "queue job id (required for job-done)").option("--timeout <ms>", `wall-clock cap (default: ${String(DEFAULT_QUEUE_WAIT_TIMEOUT_MS)})`).option("--json", "emit a JSON envelope { matched, elapsedMs, ... }").action(async (eventRaw, opts) => {
|
|
9074
9364
|
if (!QUEUE_WAIT_EVENTS.includes(eventRaw)) {
|
|
9075
|
-
|
|
9365
|
+
log35.error(`unknown event '${eventRaw}' (one of: ${QUEUE_WAIT_EVENTS.join(", ")})`);
|
|
9076
9366
|
process.exit(2);
|
|
9077
9367
|
}
|
|
9078
9368
|
const event = eventRaw;
|
|
@@ -9092,11 +9382,11 @@ var queueWaitForCommand = new Command31("wait-for").description(
|
|
|
9092
9382
|
if (opts.json === true) {
|
|
9093
9383
|
process.stdout.write(JSON.stringify({ matched: false, event, elapsedMs }) + "\n");
|
|
9094
9384
|
} else {
|
|
9095
|
-
|
|
9385
|
+
log35.error(`'${event}' did not occur within ${String(timeoutMs)}ms`);
|
|
9096
9386
|
}
|
|
9097
9387
|
process.exit(1);
|
|
9098
9388
|
}
|
|
9099
|
-
|
|
9389
|
+
log35.error(err instanceof Error ? err.message : String(err));
|
|
9100
9390
|
process.exit(1);
|
|
9101
9391
|
}
|
|
9102
9392
|
});
|
|
@@ -9193,8 +9483,8 @@ function truncate(s, max) {
|
|
|
9193
9483
|
}
|
|
9194
9484
|
|
|
9195
9485
|
// src/commands/relay.ts
|
|
9196
|
-
import { log as
|
|
9197
|
-
import { Command as
|
|
9486
|
+
import { log as log36, spinner as spinner8 } from "@clack/prompts";
|
|
9487
|
+
import { Command as Command33 } from "commander";
|
|
9198
9488
|
async function rehydrateFromState() {
|
|
9199
9489
|
const state = await readState();
|
|
9200
9490
|
await rehydrateRelayRegistry(
|
|
@@ -9234,7 +9524,7 @@ function renderStatus(s) {
|
|
|
9234
9524
|
}
|
|
9235
9525
|
return ["relay: not running", ` log: ${s.logFile}`].join("\n");
|
|
9236
9526
|
}
|
|
9237
|
-
var statusSub = new
|
|
9527
|
+
var statusSub = new Command33("status").description("Show whether the host relay is running, with pid / port / box count").option("--json", "emit RelayStatus as JSON").action(async (opts) => {
|
|
9238
9528
|
try {
|
|
9239
9529
|
const s = await getRelayStatus();
|
|
9240
9530
|
if (opts.json) {
|
|
@@ -9246,7 +9536,7 @@ var statusSub = new Command32("status").description("Show whether the host relay
|
|
|
9246
9536
|
handleLifecycleError(err);
|
|
9247
9537
|
}
|
|
9248
9538
|
});
|
|
9249
|
-
var stopSub = new
|
|
9539
|
+
var stopSub = new Command33("stop").description("Stop the host relay process (idempotent)").action(async () => {
|
|
9250
9540
|
try {
|
|
9251
9541
|
const s = spinner8();
|
|
9252
9542
|
s.start("stopping relay");
|
|
@@ -9258,7 +9548,7 @@ var stopSub = new Command32("stop").description("Stop the host relay process (id
|
|
|
9258
9548
|
handleLifecycleError(err);
|
|
9259
9549
|
}
|
|
9260
9550
|
});
|
|
9261
|
-
var startSub = new
|
|
9551
|
+
var startSub = new Command33("start").description("Start the host relay if not already running (idempotent)").action(async () => {
|
|
9262
9552
|
try {
|
|
9263
9553
|
const s = spinner8();
|
|
9264
9554
|
s.start("starting relay");
|
|
@@ -9269,7 +9559,7 @@ var startSub = new Command32("start").description("Start the host relay if not a
|
|
|
9269
9559
|
handleLifecycleError(err);
|
|
9270
9560
|
}
|
|
9271
9561
|
});
|
|
9272
|
-
var restartSub = new
|
|
9562
|
+
var restartSub = new Command33("restart").description("Stop then start the host relay").action(async () => {
|
|
9273
9563
|
try {
|
|
9274
9564
|
const s = spinner8();
|
|
9275
9565
|
s.start("stopping relay");
|
|
@@ -9285,29 +9575,31 @@ var restartSub = new Command32("restart").description("Stop then start the host
|
|
|
9285
9575
|
s2.stop(`relay running on ${ep.hostUrl}`);
|
|
9286
9576
|
} catch (err) {
|
|
9287
9577
|
s2.stop("relay start failed");
|
|
9288
|
-
|
|
9578
|
+
log36.warn(err instanceof Error ? err.message : String(err));
|
|
9289
9579
|
throw err;
|
|
9290
9580
|
}
|
|
9291
9581
|
} catch (err) {
|
|
9292
9582
|
handleLifecycleError(err);
|
|
9293
9583
|
}
|
|
9294
9584
|
});
|
|
9295
|
-
var relayCommand = new
|
|
9585
|
+
var relayCommand = new Command33("relay").description("Manage the host relay process (status / stop / start / restart)").addCommand(statusSub, { isDefault: true }).addCommand(stopSub).addCommand(startSub).addCommand(restartSub);
|
|
9296
9586
|
|
|
9297
9587
|
// src/commands/_run-queued-job.ts
|
|
9298
|
-
import { Command as
|
|
9299
|
-
var runQueuedJobCommand = new
|
|
9300
|
-
const
|
|
9301
|
-
|
|
9588
|
+
import { Command as Command34 } from "commander";
|
|
9589
|
+
var runQueuedJobCommand = new Command34("_run-queued-job").description("internal: run a queued background agent job (do not invoke directly)").argument("<id>", "queue job id (from ~/.agentbox/queue/<id>.json)").action(async (id) => {
|
|
9590
|
+
const log45 = openCommandLog(`queue-${id}`);
|
|
9591
|
+
log45.write(`worker pid=${String(process.pid)} starting for job ${id}`);
|
|
9302
9592
|
let job = null;
|
|
9303
9593
|
try {
|
|
9304
9594
|
job = await readJob(id);
|
|
9305
9595
|
if (!job) {
|
|
9306
|
-
|
|
9307
|
-
|
|
9596
|
+
log45.write(`FATAL: no manifest at id=${id}`);
|
|
9597
|
+
log45.close();
|
|
9308
9598
|
process.exit(64);
|
|
9309
9599
|
}
|
|
9310
|
-
await runDockerJob(job,
|
|
9600
|
+
await runDockerJob(job, log45, (boxId) => {
|
|
9601
|
+
if (job) job = { ...job, boxId };
|
|
9602
|
+
});
|
|
9311
9603
|
const done = {
|
|
9312
9604
|
...job,
|
|
9313
9605
|
status: "done",
|
|
@@ -9315,12 +9607,12 @@ var runQueuedJobCommand = new Command33("_run-queued-job").description("internal
|
|
|
9315
9607
|
exitCode: 0
|
|
9316
9608
|
};
|
|
9317
9609
|
await writeJob(done);
|
|
9318
|
-
|
|
9319
|
-
|
|
9610
|
+
log45.write(`done`);
|
|
9611
|
+
log45.close();
|
|
9320
9612
|
process.exit(0);
|
|
9321
9613
|
} catch (err) {
|
|
9322
9614
|
const msg = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
9323
|
-
|
|
9615
|
+
log45.write(`FAIL: ${msg}`);
|
|
9324
9616
|
if (job) {
|
|
9325
9617
|
try {
|
|
9326
9618
|
const failed = {
|
|
@@ -9334,11 +9626,11 @@ var runQueuedJobCommand = new Command33("_run-queued-job").description("internal
|
|
|
9334
9626
|
} catch {
|
|
9335
9627
|
}
|
|
9336
9628
|
}
|
|
9337
|
-
|
|
9629
|
+
log45.close();
|
|
9338
9630
|
process.exit(1);
|
|
9339
9631
|
}
|
|
9340
9632
|
});
|
|
9341
|
-
async function runDockerJob(job,
|
|
9633
|
+
async function runDockerJob(job, log45, onBoxCreated) {
|
|
9342
9634
|
const opts = job.createOpts;
|
|
9343
9635
|
const cfg = await loadEffectiveConfig(opts.workspace, {
|
|
9344
9636
|
cliOverrides: buildOverridesFromJob(job)
|
|
@@ -9353,7 +9645,7 @@ async function runDockerJob(job, log44) {
|
|
|
9353
9645
|
const useSnapshot = opts.hostSnapshot === false ? false : opts.hostSnapshot === true ? true : cfg.effective.box.hostSnapshot ?? false;
|
|
9354
9646
|
const resolved = job.agent === "claude-code" ? await resolveClaudeAuth(process.env) : null;
|
|
9355
9647
|
const withPlaywright = cfg.effective.box.withPlaywright || cfg.effective.browser.default !== "agent-browser";
|
|
9356
|
-
|
|
9648
|
+
log45.write(`creating box for agent=${job.agent}`);
|
|
9357
9649
|
const result = await createBox({
|
|
9358
9650
|
workspacePath: opts.workspace,
|
|
9359
9651
|
name: opts.name && opts.name.length > 0 ? opts.name : void 0,
|
|
@@ -9375,17 +9667,19 @@ async function runDockerJob(job, log44) {
|
|
|
9375
9667
|
portlessStateDir: cfg.effective.portless.stateDir || void 0,
|
|
9376
9668
|
limits: resolveLimits(cfg.effective.box, opts),
|
|
9377
9669
|
projectRoot,
|
|
9378
|
-
onLog: (line) =>
|
|
9670
|
+
onLog: (line) => log45.write(line)
|
|
9379
9671
|
});
|
|
9380
|
-
|
|
9672
|
+
log45.write(`box created: ${result.record.container}`);
|
|
9673
|
+
onBoxCreated(result.record.id);
|
|
9674
|
+
await writeJob({ ...job, boxId: result.record.id });
|
|
9381
9675
|
const promptedArgs = buildPromptArgs(job.agent, job.prompt, job.agentArgs);
|
|
9382
9676
|
if (job.agent === "claude-code") {
|
|
9383
|
-
|
|
9677
|
+
log45.write(`checking plugin native deps`);
|
|
9384
9678
|
await rebuildPluginNativeDeps(result.record.container, {
|
|
9385
9679
|
volume: result.record.claudeConfigVolume ?? SHARED_CLAUDE_VOLUME,
|
|
9386
|
-
onProgress: (line) =>
|
|
9680
|
+
onProgress: (line) => log45.write(line)
|
|
9387
9681
|
});
|
|
9388
|
-
|
|
9682
|
+
log45.write(`starting claude session`);
|
|
9389
9683
|
await startClaudeSession({
|
|
9390
9684
|
container: result.record.container,
|
|
9391
9685
|
claudeArgs: promptedArgs,
|
|
@@ -9393,22 +9687,22 @@ async function runDockerJob(job, log44) {
|
|
|
9393
9687
|
boxName: result.record.name
|
|
9394
9688
|
});
|
|
9395
9689
|
} else if (job.agent === "codex") {
|
|
9396
|
-
|
|
9690
|
+
log45.write(`checking codex`);
|
|
9397
9691
|
await ensureCodexInstalled(result.record.container, {
|
|
9398
|
-
onProgress: (line) =>
|
|
9692
|
+
onProgress: (line) => log45.write(line)
|
|
9399
9693
|
});
|
|
9400
|
-
|
|
9694
|
+
log45.write(`starting codex session`);
|
|
9401
9695
|
await startCodexSession({
|
|
9402
9696
|
container: result.record.container,
|
|
9403
9697
|
codexArgs: promptedArgs,
|
|
9404
9698
|
sessionName: cfg.effective.codex.sessionName
|
|
9405
9699
|
});
|
|
9406
9700
|
} else if (job.agent === "opencode") {
|
|
9407
|
-
|
|
9701
|
+
log45.write(`checking opencode`);
|
|
9408
9702
|
await ensureOpencodeInstalled(result.record.container, {
|
|
9409
|
-
onProgress: (line) =>
|
|
9703
|
+
onProgress: (line) => log45.write(line)
|
|
9410
9704
|
});
|
|
9411
|
-
|
|
9705
|
+
log45.write(`starting opencode session`);
|
|
9412
9706
|
await startOpencodeSession({
|
|
9413
9707
|
container: result.record.container,
|
|
9414
9708
|
opencodeArgs: promptedArgs,
|
|
@@ -9440,8 +9734,8 @@ function buildOverridesFromJob(job) {
|
|
|
9440
9734
|
|
|
9441
9735
|
// src/commands/screen.ts
|
|
9442
9736
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
9443
|
-
import { log as
|
|
9444
|
-
import { Command as
|
|
9737
|
+
import { log as log37 } from "@clack/prompts";
|
|
9738
|
+
import { Command as Command35 } from "commander";
|
|
9445
9739
|
var SIGNED_URL_TTL_MIN = 1;
|
|
9446
9740
|
var SIGNED_URL_TTL_MAX = 86400;
|
|
9447
9741
|
function parseTtlOrExit(raw) {
|
|
@@ -9454,7 +9748,7 @@ function parseTtlOrExit(raw) {
|
|
|
9454
9748
|
}
|
|
9455
9749
|
return n;
|
|
9456
9750
|
}
|
|
9457
|
-
var screenCommand = new
|
|
9751
|
+
var screenCommand = new Command35("screen").description("Open a box's VNC (noVNC) viewer in the browser (auto-unpause/start)").argument(
|
|
9458
9752
|
"[box]",
|
|
9459
9753
|
"box ref: project index, id, id prefix, name, or container (default: the only box in this project)"
|
|
9460
9754
|
).option("--print", "print the URL to stdout instead of launching the browser").option("--loopback", "docker only: use the 127.0.0.1 URL instead of the OrbStack .orb.local URL").option(
|
|
@@ -9471,10 +9765,10 @@ var screenCommand = new Command34("screen").description("Open a box's VNC (noVNC
|
|
|
9471
9765
|
if (provider === "docker") {
|
|
9472
9766
|
const insp = await inspectBox(box.id);
|
|
9473
9767
|
if (insp.state === "paused") {
|
|
9474
|
-
|
|
9768
|
+
log37.info("box is paused; unpausing");
|
|
9475
9769
|
await unpauseBox(box.id);
|
|
9476
9770
|
} else if (insp.state === "stopped") {
|
|
9477
|
-
|
|
9771
|
+
log37.info("box is stopped; starting");
|
|
9478
9772
|
await startBox(box.id);
|
|
9479
9773
|
} else if (insp.state === "missing") {
|
|
9480
9774
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
@@ -9484,13 +9778,13 @@ var screenCommand = new Command34("screen").description("Open a box's VNC (noVNC
|
|
|
9484
9778
|
const inBoxUrl = exposePort !== void 0 ? box.portlessUrl ?? `http://localhost:${String(exposePort)}` : "about:blank";
|
|
9485
9779
|
const br = await ensureBoxBrowser(box.container, void 0, inBoxUrl);
|
|
9486
9780
|
if (br.up && !br.alreadyRunning) {
|
|
9487
|
-
|
|
9781
|
+
log37.info(
|
|
9488
9782
|
exposePort !== void 0 ? `opened ${inBoxUrl} in the in-box browser (visible in the VNC view)` : "started in-box browser"
|
|
9489
9783
|
);
|
|
9490
9784
|
} else if (br.alreadyRunning) {
|
|
9491
|
-
|
|
9785
|
+
log37.info("in-box browser already running; left it untouched");
|
|
9492
9786
|
} else {
|
|
9493
|
-
|
|
9787
|
+
log37.warn(`could not start in-box browser: ${br.reason ?? "unknown"}`);
|
|
9494
9788
|
}
|
|
9495
9789
|
const engine = await detectEngine();
|
|
9496
9790
|
const urls = buildVncUrls(box, engine);
|
|
@@ -9511,10 +9805,10 @@ var screenCommand = new Command34("screen").description("Open a box's VNC (noVNC
|
|
|
9511
9805
|
const p = await providerForBox(box);
|
|
9512
9806
|
const state = await p.probeState(box);
|
|
9513
9807
|
if (state === "paused") {
|
|
9514
|
-
|
|
9808
|
+
log37.info("box is paused; resuming");
|
|
9515
9809
|
await p.resume(box);
|
|
9516
9810
|
} else if (state === "stopped") {
|
|
9517
|
-
|
|
9811
|
+
log37.info("box is stopped; starting");
|
|
9518
9812
|
await p.start(box);
|
|
9519
9813
|
} else if (state === "missing") {
|
|
9520
9814
|
throw new Error(`cloud sandbox for ${box.name} is missing; was it deleted?`);
|
|
@@ -9540,18 +9834,18 @@ var screenCommand = new Command34("screen").description("Open a box's VNC (noVNC
|
|
|
9540
9834
|
|
|
9541
9835
|
// src/commands/shell.ts
|
|
9542
9836
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
9543
|
-
import { log as
|
|
9544
|
-
import { Command as
|
|
9837
|
+
import { log as log39 } from "@clack/prompts";
|
|
9838
|
+
import { Command as Command36 } from "commander";
|
|
9545
9839
|
|
|
9546
9840
|
// src/commands/_provider-guard.ts
|
|
9547
|
-
import { log as
|
|
9841
|
+
import { log as log38 } from "@clack/prompts";
|
|
9548
9842
|
function requireDockerProvider(box, commandName) {
|
|
9549
9843
|
const provider = box.provider ?? "docker";
|
|
9550
9844
|
if (provider === "docker") return;
|
|
9551
|
-
|
|
9845
|
+
log38.error(
|
|
9552
9846
|
`\`agentbox ${commandName}\` doesn't yet support cloud boxes (this box's provider is '${provider}').`
|
|
9553
9847
|
);
|
|
9554
|
-
|
|
9848
|
+
log38.info(
|
|
9555
9849
|
"Cloud-provider routing for this command is on the Phase 3 backlog. For now: use `agentbox url` for web access, `agentbox-ctl git push` from inside the sandbox via SSH/web terminal, or fall back to the cloud provider's own console."
|
|
9556
9850
|
);
|
|
9557
9851
|
process.exit(2);
|
|
@@ -9591,10 +9885,10 @@ function fmtAgo2(iso) {
|
|
|
9591
9885
|
async function ensureBoxRunning(box) {
|
|
9592
9886
|
const insp = await inspectBox(box.id);
|
|
9593
9887
|
if (insp.state === "paused") {
|
|
9594
|
-
|
|
9888
|
+
log39.info("box is paused; unpausing");
|
|
9595
9889
|
await unpauseBox(box.id);
|
|
9596
9890
|
} else if (insp.state === "stopped") {
|
|
9597
|
-
|
|
9891
|
+
log39.info("box is stopped; starting");
|
|
9598
9892
|
await startBox(box.id);
|
|
9599
9893
|
} else if (insp.state === "missing") {
|
|
9600
9894
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
@@ -9633,7 +9927,7 @@ async function startOrAttachShell(box, cfg) {
|
|
|
9633
9927
|
const label = shellLabel(cfg.sessionName);
|
|
9634
9928
|
const info = await shellSessionInfo(box.container, cfg.sessionName, cfg.user);
|
|
9635
9929
|
if (info.running) {
|
|
9636
|
-
|
|
9930
|
+
log39.info(`reattaching to shell "${label}" \u2014 Control+a d to detach`);
|
|
9637
9931
|
} else {
|
|
9638
9932
|
await startShellSession({
|
|
9639
9933
|
container: box.container,
|
|
@@ -9641,7 +9935,7 @@ async function startOrAttachShell(box, cfg) {
|
|
|
9641
9935
|
user: cfg.user,
|
|
9642
9936
|
login: cfg.login
|
|
9643
9937
|
});
|
|
9644
|
-
|
|
9938
|
+
log39.info(`shell "${label}" \u2014 Control+a d to detach, leaves it running`);
|
|
9645
9939
|
}
|
|
9646
9940
|
const code = await runWrappedAttach({
|
|
9647
9941
|
container: box.container,
|
|
@@ -9656,7 +9950,7 @@ async function startOrAttachShell(box, cfg) {
|
|
|
9656
9950
|
});
|
|
9657
9951
|
process.exit(code);
|
|
9658
9952
|
}
|
|
9659
|
-
var shellCommand = new
|
|
9953
|
+
var shellCommand = new Command36("shell").description(
|
|
9660
9954
|
"Open an interactive shell in a box, in a detachable tmux session (auto-unpause/start)"
|
|
9661
9955
|
).argument(
|
|
9662
9956
|
"[box]",
|
|
@@ -9694,6 +9988,7 @@ var shellCommand = new Command35("shell").description(
|
|
|
9694
9988
|
container: box.name,
|
|
9695
9989
|
command: spec.argv[0],
|
|
9696
9990
|
dockerArgv: spec.argv.slice(1),
|
|
9991
|
+
env: spec.env,
|
|
9697
9992
|
relayBaseUrl: RELAY_HOST_URL5,
|
|
9698
9993
|
boxId: box.id,
|
|
9699
9994
|
boxName: box.name,
|
|
@@ -9746,7 +10041,7 @@ var shellCommand = new Command35("shell").description(
|
|
|
9746
10041
|
handleLifecycleError(err);
|
|
9747
10042
|
}
|
|
9748
10043
|
});
|
|
9749
|
-
var shellAttachCommand = new
|
|
10044
|
+
var shellAttachCommand = new Command36("attach").description(
|
|
9750
10045
|
"Attach to a shell tmux session in a box, starting one if none is running (auto-unpause/start)"
|
|
9751
10046
|
).argument(
|
|
9752
10047
|
"[box]",
|
|
@@ -9784,7 +10079,7 @@ function renderShellTable(sessions) {
|
|
|
9784
10079
|
for (const r of rows) process.stdout.write(`${fmt(r)}
|
|
9785
10080
|
`);
|
|
9786
10081
|
}
|
|
9787
|
-
var shellLsCommand = new
|
|
10082
|
+
var shellLsCommand = new Command36("ls").description("List the shell tmux sessions running in a box").argument(
|
|
9788
10083
|
"[box]",
|
|
9789
10084
|
"box ref: project index, id, id prefix, name, or container (default: the only box in this project)"
|
|
9790
10085
|
).action(async (idOrName) => {
|
|
@@ -9793,11 +10088,11 @@ var shellLsCommand = new Command35("ls").description("List the shell tmux sessio
|
|
|
9793
10088
|
requireDockerProvider(box, "shell");
|
|
9794
10089
|
const insp = await inspectBox(box.id);
|
|
9795
10090
|
if (insp.state !== "running") {
|
|
9796
|
-
|
|
10091
|
+
log39.info(`box ${box.name} is ${insp.state} \u2014 no live shell sessions`);
|
|
9797
10092
|
return;
|
|
9798
10093
|
}
|
|
9799
10094
|
if (insp.shellSessions.length === 0) {
|
|
9800
|
-
|
|
10095
|
+
log39.info(
|
|
9801
10096
|
`no shell sessions in ${box.name} \u2014 start one with: agentbox shell ${reattachRef4(box)}`
|
|
9802
10097
|
);
|
|
9803
10098
|
return;
|
|
@@ -9807,7 +10102,7 @@ var shellLsCommand = new Command35("ls").description("List the shell tmux sessio
|
|
|
9807
10102
|
handleLifecycleError(err);
|
|
9808
10103
|
}
|
|
9809
10104
|
});
|
|
9810
|
-
var shellKillCommand = new
|
|
10105
|
+
var shellKillCommand = new Command36("kill").description("Kill a shell tmux session in a box (the shell and anything running in it)").argument(
|
|
9811
10106
|
"[box]",
|
|
9812
10107
|
"box ref: project index, id, id prefix, name, or container (default: the only box in this project)"
|
|
9813
10108
|
).option("-n, --name <label>", "shell label to kill (default: the box default shell)").option("--all", "kill every shell session in the box").action(async function(idOrName) {
|
|
@@ -9817,25 +10112,25 @@ var shellKillCommand = new Command35("kill").description("Kill a shell tmux sess
|
|
|
9817
10112
|
requireDockerProvider(box, "shell");
|
|
9818
10113
|
const insp = await inspectBox(box.id);
|
|
9819
10114
|
if (insp.state !== "running") {
|
|
9820
|
-
|
|
10115
|
+
log39.info(`box ${box.name} is ${insp.state} \u2014 no shell sessions to kill`);
|
|
9821
10116
|
return;
|
|
9822
10117
|
}
|
|
9823
10118
|
if (opts.all) {
|
|
9824
10119
|
if (insp.shellSessions.length === 0) {
|
|
9825
|
-
|
|
10120
|
+
log39.info(`no shell sessions in ${box.name}`);
|
|
9826
10121
|
return;
|
|
9827
10122
|
}
|
|
9828
10123
|
let killed = 0;
|
|
9829
10124
|
for (const s of insp.shellSessions) {
|
|
9830
10125
|
if (await killShellSession(box.container, s.sessionName)) killed++;
|
|
9831
10126
|
}
|
|
9832
|
-
|
|
10127
|
+
log39.success(`killed ${String(killed)} shell session${killed === 1 ? "" : "s"} in ${box.name}`);
|
|
9833
10128
|
return;
|
|
9834
10129
|
}
|
|
9835
10130
|
const target = shellSessionName(opts.name);
|
|
9836
10131
|
const ok = await killShellSession(box.container, target);
|
|
9837
|
-
if (ok)
|
|
9838
|
-
else
|
|
10132
|
+
if (ok) log39.success(`killed shell "${shellLabel(target)}" in ${box.name}`);
|
|
10133
|
+
else log39.warn(`no shell "${shellLabel(target)}" in ${box.name} (already gone?)`);
|
|
9839
10134
|
} catch (err) {
|
|
9840
10135
|
handleLifecycleError(err);
|
|
9841
10136
|
}
|
|
@@ -9845,8 +10140,8 @@ shellCommand.addCommand(shellLsCommand);
|
|
|
9845
10140
|
shellCommand.addCommand(shellKillCommand);
|
|
9846
10141
|
|
|
9847
10142
|
// src/commands/start.ts
|
|
9848
|
-
import { Command as
|
|
9849
|
-
var startCommand = new
|
|
10143
|
+
import { Command as Command37 } from "commander";
|
|
10144
|
+
var startCommand = new Command37("start").description(
|
|
9850
10145
|
"Start a stopped box. Docker: docker start + relaunch ctl/dockerd/vnc daemons. Cloud: backend.start, then re-resolve preview URLs/tokens, re-launch in-sandbox ctl/dockerd daemons, and re-register with the host relay (so the CloudBoxPoller resumes)."
|
|
9851
10146
|
).argument(
|
|
9852
10147
|
"[box]",
|
|
@@ -9869,8 +10164,8 @@ var startCommand = new Command36("start").description(
|
|
|
9869
10164
|
});
|
|
9870
10165
|
|
|
9871
10166
|
// src/commands/status.ts
|
|
9872
|
-
import { log as
|
|
9873
|
-
import { Command as
|
|
10167
|
+
import { log as log41 } from "@clack/prompts";
|
|
10168
|
+
import { Command as Command38 } from "commander";
|
|
9874
10169
|
|
|
9875
10170
|
// src/endpoints-render.ts
|
|
9876
10171
|
function renderEndpointLines(endpoints, stream) {
|
|
@@ -9900,7 +10195,7 @@ function renderEndpointLines(endpoints, stream) {
|
|
|
9900
10195
|
}
|
|
9901
10196
|
|
|
9902
10197
|
// src/commands/inspect.ts
|
|
9903
|
-
import { log as
|
|
10198
|
+
import { log as log40 } from "@clack/prompts";
|
|
9904
10199
|
function fmtLimit(n, unit) {
|
|
9905
10200
|
return n && n > 0 ? `${String(n)}${unit}` : "unlimited";
|
|
9906
10201
|
}
|
|
@@ -10045,7 +10340,7 @@ function renderCodexActivityCloud(persisted) {
|
|
|
10045
10340
|
async function runInspect(box, opts) {
|
|
10046
10341
|
try {
|
|
10047
10342
|
if (opts.json && opts.watch) {
|
|
10048
|
-
|
|
10343
|
+
log40.error("cannot combine --json with --watch");
|
|
10049
10344
|
process.exit(2);
|
|
10050
10345
|
}
|
|
10051
10346
|
const isCloud = (box.provider ?? "docker") !== "docker";
|
|
@@ -10082,14 +10377,14 @@ async function runInspect(box, opts) {
|
|
|
10082
10377
|
|
|
10083
10378
|
// src/commands/status.ts
|
|
10084
10379
|
var statusCommand2 = withWatchOptions(
|
|
10085
|
-
new
|
|
10380
|
+
new Command38("status").description("Show service + task status from a box's agentbox-ctl daemon").argument(
|
|
10086
10381
|
"[box]",
|
|
10087
10382
|
"box ref: project index, id, id prefix, name, or container (default: the only box in this project)"
|
|
10088
10383
|
).option("-j, --json", "machine-readable JSON output").option("--inspect", "show detailed box info (volumes, limits, paths) instead of service/task status")
|
|
10089
10384
|
).action(async (idOrName, opts) => {
|
|
10090
10385
|
try {
|
|
10091
10386
|
if (opts.json && opts.watch) {
|
|
10092
|
-
|
|
10387
|
+
log41.error("cannot combine --json with --watch");
|
|
10093
10388
|
process.exit(2);
|
|
10094
10389
|
}
|
|
10095
10390
|
const box = await resolveBoxOrExit(idOrName);
|
|
@@ -10256,8 +10551,8 @@ function renderPersisted2(s, state) {
|
|
|
10256
10551
|
}
|
|
10257
10552
|
|
|
10258
10553
|
// src/commands/stop.ts
|
|
10259
|
-
import { Command as
|
|
10260
|
-
var stopCommand = new
|
|
10554
|
+
import { Command as Command39 } from "commander";
|
|
10555
|
+
var stopCommand = new Command39("stop").description(
|
|
10261
10556
|
"Stop a box (Docker: docker stop; preserves upper + node_modules volumes. Cloud: backend.stop \u2014 sandbox stays in your account, disk preserved)."
|
|
10262
10557
|
).argument(
|
|
10263
10558
|
"[box]",
|
|
@@ -10286,7 +10581,7 @@ restart with: agentbox start ${box.name}
|
|
|
10286
10581
|
});
|
|
10287
10582
|
|
|
10288
10583
|
// src/commands/top.ts
|
|
10289
|
-
import { Command as
|
|
10584
|
+
import { Command as Command40 } from "commander";
|
|
10290
10585
|
var COLS = ["BOX", "STATE", "CPU%", "MEM USAGE / LIMIT", "MEM%", "PIDS", "DISK", "NET I/O"];
|
|
10291
10586
|
function row(name, state, s) {
|
|
10292
10587
|
const mem = `${fmtBytes(s.memUsedBytes)} / ${fmtBytes(s.memLimitBytes)}`;
|
|
@@ -10359,7 +10654,7 @@ async function renderProjectFooters() {
|
|
|
10359
10654
|
|
|
10360
10655
|
SYSTEM: ${parts.join(" - ")}` : "";
|
|
10361
10656
|
}
|
|
10362
|
-
var topCommand = new
|
|
10657
|
+
var topCommand = new Command40("top").description("Live resource monitor (cpu/mem/pids/disk) for a box, the project, or every box").argument(
|
|
10363
10658
|
"[box]",
|
|
10364
10659
|
"box ref (default: every box on the host; --project narrows to the cwd's project)"
|
|
10365
10660
|
).option("-p, --project", "show only boxes in the cwd's project").option("--once", "print a single snapshot instead of watching").option("-j, --json", "machine-readable JSON (implies --once)").option("--interval <seconds>", "refresh interval", "2").action(async (idOrName, opts) => {
|
|
@@ -10392,8 +10687,8 @@ var topCommand = new Command39("top").description("Live resource monitor (cpu/me
|
|
|
10392
10687
|
});
|
|
10393
10688
|
|
|
10394
10689
|
// src/commands/unpause.ts
|
|
10395
|
-
import { Command as
|
|
10396
|
-
var unpauseCommand = new
|
|
10690
|
+
import { Command as Command41 } from "commander";
|
|
10691
|
+
var unpauseCommand = new Command41("unpause").description(
|
|
10397
10692
|
"Resume a paused box. Docker: `docker unpause` (sub-second). Cloud: backend.resume (re-hydrates from archive \u2014 slower first time)."
|
|
10398
10693
|
).argument(
|
|
10399
10694
|
"[box]",
|
|
@@ -10417,8 +10712,8 @@ var unpauseCommand = new Command40("unpause").description(
|
|
|
10417
10712
|
|
|
10418
10713
|
// src/commands/update.ts
|
|
10419
10714
|
import { spawn as spawn4 } from "child_process";
|
|
10420
|
-
import { confirm as confirm15, intro as intro8, isCancel as isCancel16, log as
|
|
10421
|
-
import { Command as
|
|
10715
|
+
import { confirm as confirm15, intro as intro8, isCancel as isCancel16, log as log42, outro as outro8, spinner as spinner9 } from "@clack/prompts";
|
|
10716
|
+
import { Command as Command42 } from "commander";
|
|
10422
10717
|
|
|
10423
10718
|
// src/exec-method.ts
|
|
10424
10719
|
function detectExecutionMethod(input) {
|
|
@@ -10462,7 +10757,7 @@ function runInherit(cmd, args) {
|
|
|
10462
10757
|
child.on("close", (code) => resolveP(code ?? 0));
|
|
10463
10758
|
});
|
|
10464
10759
|
}
|
|
10465
|
-
var updateCommand = new
|
|
10760
|
+
var updateCommand = new Command42("self-update").description(
|
|
10466
10761
|
"Update agentbox: self-update via npm/pnpm (unless run via npx), wipe the box image so it rebuilds, and reload the relay"
|
|
10467
10762
|
).option("-y, --yes", "skip the confirmation prompt").option("--dry-run", "show what would happen, don't change anything").option("--skip-self", "skip the package self-update; only refresh the image + relay").action(async (opts) => {
|
|
10468
10763
|
try {
|
|
@@ -10472,7 +10767,7 @@ var updateCommand = new Command41("self-update").description(
|
|
|
10472
10767
|
});
|
|
10473
10768
|
intro8("agentbox self-update");
|
|
10474
10769
|
const selfStep = opts.skipSelf ? "self-update: skipped (--skip-self)" : describeSelfUpdate(method);
|
|
10475
|
-
|
|
10770
|
+
log42.info(
|
|
10476
10771
|
[
|
|
10477
10772
|
"plan:",
|
|
10478
10773
|
` ${selfStep}`,
|
|
@@ -10487,25 +10782,25 @@ var updateCommand = new Command41("self-update").description(
|
|
|
10487
10782
|
if (!opts.yes) {
|
|
10488
10783
|
const ok = await confirm15({ message: "Proceed with update?", initialValue: true });
|
|
10489
10784
|
if (isCancel16(ok) || !ok) {
|
|
10490
|
-
|
|
10785
|
+
log42.info("cancelled");
|
|
10491
10786
|
return;
|
|
10492
10787
|
}
|
|
10493
10788
|
}
|
|
10494
10789
|
let selfUpdated = false;
|
|
10495
10790
|
if (opts.skipSelf) {
|
|
10496
|
-
|
|
10791
|
+
log42.info("skipping self-update (--skip-self)");
|
|
10497
10792
|
} else {
|
|
10498
10793
|
const cmd = selfUpdateCommand(method);
|
|
10499
10794
|
if (cmd === null) {
|
|
10500
|
-
|
|
10795
|
+
log42.info(describeSelfUpdate(method));
|
|
10501
10796
|
} else {
|
|
10502
|
-
|
|
10797
|
+
log42.info(`running: ${cmd.cmd} ${cmd.args.join(" ")}`);
|
|
10503
10798
|
const code = await runInherit(cmd.cmd, cmd.args);
|
|
10504
10799
|
if (code !== 0) {
|
|
10505
10800
|
throw new Error(`${cmd.cmd} exited with code ${String(code)}`);
|
|
10506
10801
|
}
|
|
10507
10802
|
selfUpdated = true;
|
|
10508
|
-
|
|
10803
|
+
log42.success(`updated ${PKG} via ${cmd.cmd}`);
|
|
10509
10804
|
}
|
|
10510
10805
|
}
|
|
10511
10806
|
const s = spinner9();
|
|
@@ -10521,7 +10816,7 @@ var updateCommand = new Command41("self-update").description(
|
|
|
10521
10816
|
stop.stopped ? `stopped relay (pid ${String(stop.pid)})` : "relay was not running"
|
|
10522
10817
|
);
|
|
10523
10818
|
if (selfUpdated) {
|
|
10524
|
-
|
|
10819
|
+
log42.info(
|
|
10525
10820
|
"relay will restart automatically (with the updated build) on your next `agentbox create` / `agentbox claude`"
|
|
10526
10821
|
);
|
|
10527
10822
|
} else {
|
|
@@ -10532,7 +10827,7 @@ var updateCommand = new Command41("self-update").description(
|
|
|
10532
10827
|
sr2.stop(`relay back up on ${ep.hostUrl}`);
|
|
10533
10828
|
} catch (err) {
|
|
10534
10829
|
sr2.stop("relay restart failed");
|
|
10535
|
-
|
|
10830
|
+
log42.warn(
|
|
10536
10831
|
`${err instanceof Error ? err.message : String(err)} \u2014 it will retry on the next box command`
|
|
10537
10832
|
);
|
|
10538
10833
|
}
|
|
@@ -10545,8 +10840,8 @@ var updateCommand = new Command41("self-update").description(
|
|
|
10545
10840
|
|
|
10546
10841
|
// src/commands/url.ts
|
|
10547
10842
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
10548
|
-
import { log as
|
|
10549
|
-
import { Command as
|
|
10843
|
+
import { log as log43 } from "@clack/prompts";
|
|
10844
|
+
import { Command as Command43 } from "commander";
|
|
10550
10845
|
var SIGNED_URL_TTL_MIN2 = 1;
|
|
10551
10846
|
var SIGNED_URL_TTL_MAX2 = 86400;
|
|
10552
10847
|
function parseTtlOrExit2(raw) {
|
|
@@ -10559,7 +10854,7 @@ function parseTtlOrExit2(raw) {
|
|
|
10559
10854
|
}
|
|
10560
10855
|
return n;
|
|
10561
10856
|
}
|
|
10562
|
-
var urlCommand = new
|
|
10857
|
+
var urlCommand = new Command43("url").description(
|
|
10563
10858
|
"Open a box's web app URL in the browser, even when no service declares `expose:` (auto-unpause/start)"
|
|
10564
10859
|
).argument(
|
|
10565
10860
|
"[box]",
|
|
@@ -10578,10 +10873,10 @@ var urlCommand = new Command42("url").description(
|
|
|
10578
10873
|
if (provider === "docker") {
|
|
10579
10874
|
const insp = await inspectBox(box.id);
|
|
10580
10875
|
if (insp.state === "paused") {
|
|
10581
|
-
|
|
10876
|
+
log43.info("box is paused; unpausing");
|
|
10582
10877
|
await unpauseBox(box.id);
|
|
10583
10878
|
} else if (insp.state === "stopped") {
|
|
10584
|
-
|
|
10879
|
+
log43.info("box is stopped; starting");
|
|
10585
10880
|
await startBox(box.id);
|
|
10586
10881
|
} else if (insp.state === "missing") {
|
|
10587
10882
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
@@ -10610,10 +10905,10 @@ var urlCommand = new Command42("url").description(
|
|
|
10610
10905
|
const p = await providerForBox(box);
|
|
10611
10906
|
const state = await p.probeState(box);
|
|
10612
10907
|
if (state === "paused") {
|
|
10613
|
-
|
|
10908
|
+
log43.info("box is paused; resuming");
|
|
10614
10909
|
await p.resume(box);
|
|
10615
10910
|
} else if (state === "stopped") {
|
|
10616
|
-
|
|
10911
|
+
log43.info("box is stopped; starting");
|
|
10617
10912
|
await p.start(box);
|
|
10618
10913
|
} else if (state === "missing") {
|
|
10619
10914
|
throw new Error(`cloud sandbox for ${box.name} is missing; was it deleted?`);
|
|
@@ -10637,9 +10932,9 @@ var urlCommand = new Command42("url").description(
|
|
|
10637
10932
|
});
|
|
10638
10933
|
|
|
10639
10934
|
// src/commands/wait.ts
|
|
10640
|
-
import { log as
|
|
10641
|
-
import { Command as
|
|
10642
|
-
var waitCommand = new
|
|
10935
|
+
import { log as log44 } from "@clack/prompts";
|
|
10936
|
+
import { Command as Command44 } from "commander";
|
|
10937
|
+
var waitCommand = new Command44("wait").description("Block until the box reports all autostart units ready").argument(
|
|
10643
10938
|
"[box]",
|
|
10644
10939
|
"box ref: project index, id, id prefix, name, or container (default: the only box in this project)"
|
|
10645
10940
|
).option("--timeout <ms>", "overall timeout in milliseconds", "120000").option("--units <names...>", "restrict to the named units").option("-j, --json", "machine-readable JSON output").action(async (idOrName, opts) => {
|
|
@@ -10655,7 +10950,7 @@ var waitCommand = new Command43("wait").description("Block until the box reports
|
|
|
10655
10950
|
try {
|
|
10656
10951
|
parsed = JSON.parse(proc.stdout);
|
|
10657
10952
|
} catch {
|
|
10658
|
-
|
|
10953
|
+
log44.error(`agentbox-ctl wait-ready failed: ${proc.stderr || proc.stdout}`);
|
|
10659
10954
|
process.exit(1);
|
|
10660
10955
|
}
|
|
10661
10956
|
if (opts.json) {
|
|
@@ -10694,7 +10989,7 @@ function rewriteProviderPrefix(argv) {
|
|
|
10694
10989
|
process.env.DOCKER_CLI_HINTS ??= "false";
|
|
10695
10990
|
process.env.AGENTBOX_CLI_VERSION = AGENTBOX_VERSION;
|
|
10696
10991
|
process.env.AGENTBOX_CLI_COMMIT = AGENTBOX_COMMIT;
|
|
10697
|
-
var program = new
|
|
10992
|
+
var program = new Command45();
|
|
10698
10993
|
program.name("agentbox").description("Launch coding agents in isolated sandboxes").version(AGENTBOX_VERSION);
|
|
10699
10994
|
program.enablePositionalOptions();
|
|
10700
10995
|
program.addCommand(createCommand);
|
|
@@ -10732,6 +11027,7 @@ program.addCommand(relayCommand);
|
|
|
10732
11027
|
program.addCommand(runQueuedJobCommand, { hidden: true });
|
|
10733
11028
|
program.addCommand(daytonaCommand);
|
|
10734
11029
|
program.addCommand(hetznerCommand);
|
|
11030
|
+
program.addCommand(vercelCommand);
|
|
10735
11031
|
program.addCommand(dockerCommand);
|
|
10736
11032
|
program.addCommand(updateCommand);
|
|
10737
11033
|
program.addCommand(installCommand);
|