@madarco/agentbox 0.10.0 → 0.11.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/CHANGELOG.md +47 -0
- package/dist/{_cloud-attach-O6NYTLES.js → _cloud-attach-6C5NMOHD.js} +4 -4
- package/dist/{chunk-2GPORKYF.js → chunk-D4Q2RUQI.js} +5 -5
- package/dist/chunk-D4Q2RUQI.js.map +1 -0
- package/dist/{chunk-MTVI44DW.js → chunk-ECLLV5JH.js} +6 -3
- package/dist/chunk-ECLLV5JH.js.map +1 -0
- package/dist/{chunk-7UIAO7PC.js → chunk-GUNUBIRB.js} +94 -75
- package/dist/chunk-GUNUBIRB.js.map +1 -0
- package/dist/{chunk-R4O5WPHW.js → chunk-NVSRGC5W.js} +23 -11
- package/dist/chunk-NVSRGC5W.js.map +1 -0
- package/dist/{chunk-I24B6AXR.js → chunk-R5XIDQFR.js} +6 -3
- package/dist/chunk-R5XIDQFR.js.map +1 -0
- package/dist/{chunk-KL36BRN4.js → chunk-SNTHHWKY.js} +70 -17
- package/dist/chunk-SNTHHWKY.js.map +1 -0
- package/dist/{chunk-LEV3KICD.js → chunk-ZGVMN54V.js} +6 -3
- package/dist/{chunk-LEV3KICD.js.map → chunk-ZGVMN54V.js.map} +1 -1
- package/dist/{dist-5FQGYRW5.js → dist-4SUIXKSD.js} +5 -5
- package/dist/{dist-PZW3GWWU.js → dist-HT2YV6PB.js} +5 -5
- package/dist/{dist-TMHSUVTP.js → dist-PJFJNXO2.js} +5 -5
- package/dist/{dist-BQNX7RQE.js → dist-ZEGIMYWZ.js} +13 -3
- package/dist/index.js +632 -547
- package/dist/index.js.map +1 -1
- package/dist/{prepared-state-CL4CWXQA-H5THETIM.js → prepared-state-MQHD3M5F-KE4DT3GX.js} +2 -2
- package/package.json +5 -5
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +1 -1
- package/runtime/docker/packages/ctl/dist/bin.cjs +11 -2
- package/runtime/hetzner/agentbox-setup-skill.md +1 -1
- package/runtime/hetzner/ctl.cjs +11 -2
- package/runtime/relay/bin.cjs +14 -2
- package/runtime/vercel/agentbox-setup-skill.md +1 -1
- package/runtime/vercel/ctl.cjs +11 -2
- package/runtime/vercel/scripts/provision.sh +20 -0
- package/share/agentbox-setup/SKILL.md +1 -1
- package/dist/chunk-2GPORKYF.js.map +0 -1
- package/dist/chunk-7UIAO7PC.js.map +0 -1
- package/dist/chunk-I24B6AXR.js.map +0 -1
- package/dist/chunk-KL36BRN4.js.map +0 -1
- package/dist/chunk-MTVI44DW.js.map +0 -1
- package/dist/chunk-R4O5WPHW.js.map +0 -1
- /package/dist/{_cloud-attach-O6NYTLES.js.map → _cloud-attach-6C5NMOHD.js.map} +0 -0
- /package/dist/{dist-5FQGYRW5.js.map → dist-4SUIXKSD.js.map} +0 -0
- /package/dist/{dist-PZW3GWWU.js.map → dist-HT2YV6PB.js.map} +0 -0
- /package/dist/{dist-TMHSUVTP.js.map → dist-PJFJNXO2.js.map} +0 -0
- /package/dist/{dist-BQNX7RQE.js.map → dist-ZEGIMYWZ.js.map} +0 -0
- /package/dist/{prepared-state-CL4CWXQA-H5THETIM.js.map → prepared-state-MQHD3M5F-KE4DT3GX.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
maskKey,
|
|
6
6
|
readDaytonaCredStatus,
|
|
7
7
|
secretsPath
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ZGVMN54V.js";
|
|
9
9
|
import {
|
|
10
10
|
detectEgressIp,
|
|
11
11
|
ensureHetznerCredentials,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
readHetznerCredStatus,
|
|
16
16
|
secretsPath as secretsPath2,
|
|
17
17
|
syncFirewallSource
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-R5XIDQFR.js";
|
|
19
19
|
import {
|
|
20
20
|
detectSbx,
|
|
21
21
|
ensureVercelCredentials,
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
maskKey as maskKey3,
|
|
24
24
|
readVercelCredStatus,
|
|
25
25
|
secretsPath as secretsPath3
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-ECLLV5JH.js";
|
|
27
27
|
import {
|
|
28
28
|
agentSpecsForCloud,
|
|
29
29
|
ensureAgentVolumesForCloud,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
probeCloudCheckpoint,
|
|
32
32
|
resolveCloudCheckpoint,
|
|
33
33
|
seedAgentVolumesIfFresh
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-D4Q2RUQI.js";
|
|
35
35
|
import {
|
|
36
36
|
ADVANCED_HINT_GROUPS,
|
|
37
37
|
ALERT_BAND_ROWS,
|
|
@@ -61,7 +61,7 @@ import {
|
|
|
61
61
|
statusLine,
|
|
62
62
|
stripTitleGlyph,
|
|
63
63
|
subscribePrompts
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-NVSRGC5W.js";
|
|
65
65
|
import {
|
|
66
66
|
AmbiguousBoxError,
|
|
67
67
|
BOX_STATUS_EVENT,
|
|
@@ -207,20 +207,21 @@ import {
|
|
|
207
207
|
waitForTmuxPaneContent,
|
|
208
208
|
warmUpClaudeCredentials,
|
|
209
209
|
writeJob
|
|
210
|
-
} from "./chunk-
|
|
210
|
+
} from "./chunk-GUNUBIRB.js";
|
|
211
211
|
import {
|
|
212
212
|
DEFAULT_BOX_IMAGE,
|
|
213
213
|
STATE_DIR,
|
|
214
214
|
ensureImage,
|
|
215
|
+
hostOpenCommand,
|
|
215
216
|
imageInfo,
|
|
216
217
|
readState,
|
|
217
218
|
resolveBoxRef
|
|
218
|
-
} from "./chunk-
|
|
219
|
+
} from "./chunk-SNTHHWKY.js";
|
|
219
220
|
import "./chunk-G3H2L3O2.js";
|
|
220
221
|
|
|
221
222
|
// src/version.ts
|
|
222
|
-
var AGENTBOX_VERSION = true ? "0.
|
|
223
|
-
var AGENTBOX_COMMIT = true ? "
|
|
223
|
+
var AGENTBOX_VERSION = true ? "0.11.0" : "0.0.0-dev";
|
|
224
|
+
var AGENTBOX_COMMIT = true ? "1f8ef1db" : "dev";
|
|
224
225
|
|
|
225
226
|
// src/index.ts
|
|
226
227
|
import { Command as Command46 } from "commander";
|
|
@@ -591,7 +592,7 @@ function parsePositiveInt(raw, label) {
|
|
|
591
592
|
}
|
|
592
593
|
|
|
593
594
|
// src/commands/claude.ts
|
|
594
|
-
import { confirm as confirm3, intro, isCancel as isCancel4, log as
|
|
595
|
+
import { confirm as confirm3, intro, isCancel as isCancel4, log as log8, outro, spinner as spinner3 } from "@clack/prompts";
|
|
595
596
|
import { Command as Command2 } from "commander";
|
|
596
597
|
|
|
597
598
|
// src/auth.ts
|
|
@@ -822,9 +823,6 @@ function resolveAttachInOption(opts) {
|
|
|
822
823
|
return void 0;
|
|
823
824
|
}
|
|
824
825
|
|
|
825
|
-
// src/commands/_cloud-agent-create.ts
|
|
826
|
-
import { log as log4, outro } from "@clack/prompts";
|
|
827
|
-
|
|
828
826
|
// src/lib/progress.ts
|
|
829
827
|
import { spinner } from "@clack/prompts";
|
|
830
828
|
|
|
@@ -867,6 +865,138 @@ function makeProgressReporter(verbose) {
|
|
|
867
865
|
};
|
|
868
866
|
}
|
|
869
867
|
|
|
868
|
+
// src/lib/launch-recap.ts
|
|
869
|
+
import { homedir as homedir2 } from "os";
|
|
870
|
+
import { note } from "@clack/prompts";
|
|
871
|
+
|
|
872
|
+
// src/lib/from-branch.ts
|
|
873
|
+
import { execa } from "execa";
|
|
874
|
+
var SHA_RE = /^[0-9a-f]{7,40}$/i;
|
|
875
|
+
var FromBranchError = class extends Error {
|
|
876
|
+
constructor(message) {
|
|
877
|
+
super(message);
|
|
878
|
+
this.name = "FromBranchError";
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
async function resolveFromBranch(ref, opts) {
|
|
882
|
+
if (!ref || ref.length === 0) return void 0;
|
|
883
|
+
const remote = opts.remote ?? "origin";
|
|
884
|
+
const isSha = SHA_RE.test(ref);
|
|
885
|
+
if (!isSha) {
|
|
886
|
+
const fetched = await execa(
|
|
887
|
+
"git",
|
|
888
|
+
["-C", opts.repo, "fetch", "--quiet", remote, ref],
|
|
889
|
+
{ reject: false }
|
|
890
|
+
);
|
|
891
|
+
if (fetched.exitCode !== 0) {
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
const verify = await execa(
|
|
895
|
+
"git",
|
|
896
|
+
["-C", opts.repo, "rev-parse", "--verify", `${ref}^{commit}`],
|
|
897
|
+
{ reject: false }
|
|
898
|
+
);
|
|
899
|
+
if (verify.exitCode !== 0) {
|
|
900
|
+
throw new FromBranchError(
|
|
901
|
+
`--from-branch: unknown ref "${ref}" (not found in ${opts.repo} after fetch). Provide a branch, tag, or SHA reachable from the host repo.`
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
return ref;
|
|
905
|
+
}
|
|
906
|
+
var UseBranchError = class extends Error {
|
|
907
|
+
constructor(message) {
|
|
908
|
+
super(message);
|
|
909
|
+
this.name = "UseBranchError";
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
async function resolveUseBranch(name, opts) {
|
|
913
|
+
if (!name || name.length === 0) return void 0;
|
|
914
|
+
const remote = opts.remote ?? "origin";
|
|
915
|
+
await execa("git", ["-C", opts.repo, "fetch", "--quiet", remote, name], {
|
|
916
|
+
reject: false
|
|
917
|
+
});
|
|
918
|
+
const exists = await execa(
|
|
919
|
+
"git",
|
|
920
|
+
["-C", opts.repo, "show-ref", "--verify", "--quiet", `refs/heads/${name}`],
|
|
921
|
+
{ reject: false }
|
|
922
|
+
);
|
|
923
|
+
if (exists.exitCode !== 0) {
|
|
924
|
+
throw new UseBranchError(
|
|
925
|
+
`--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).`
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
return name;
|
|
929
|
+
}
|
|
930
|
+
async function currentHostBranch(repo) {
|
|
931
|
+
const r = await execa("git", ["-C", repo, "rev-parse", "--abbrev-ref", "HEAD"], {
|
|
932
|
+
reject: false
|
|
933
|
+
});
|
|
934
|
+
if (r.exitCode !== 0) return void 0;
|
|
935
|
+
const branch = r.stdout.trim();
|
|
936
|
+
if (!branch || branch === "HEAD") return void 0;
|
|
937
|
+
return branch;
|
|
938
|
+
}
|
|
939
|
+
async function resolveBranchSelection(opts) {
|
|
940
|
+
if (opts.useBranch && opts.fromBranch) {
|
|
941
|
+
throw new UseBranchError(
|
|
942
|
+
"--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."
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
if (opts.useBranch) {
|
|
946
|
+
return { useBranch: await resolveUseBranch(opts.useBranch, { repo: opts.repo }) };
|
|
947
|
+
}
|
|
948
|
+
if (opts.fromBranch) {
|
|
949
|
+
return { fromBranch: await resolveFromBranch(opts.fromBranch, { repo: opts.repo }) };
|
|
950
|
+
}
|
|
951
|
+
if (opts.providerName !== "docker" && opts.cloudUseCurrentBranch) {
|
|
952
|
+
const current = await currentHostBranch(opts.repo);
|
|
953
|
+
if (current) {
|
|
954
|
+
opts.log?.(`cloud.useCurrentBranch: starting box on host branch "${current}"`);
|
|
955
|
+
return { useBranch: current };
|
|
956
|
+
}
|
|
957
|
+
opts.log?.(
|
|
958
|
+
"cloud.useCurrentBranch is set but host HEAD is detached; forking a fresh branch instead"
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
return {};
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// src/lib/launch-recap.ts
|
|
965
|
+
function homeShorten(p) {
|
|
966
|
+
const home = homedir2();
|
|
967
|
+
return p === home || p.startsWith(home + "/") ? "~" + p.slice(home.length) : p;
|
|
968
|
+
}
|
|
969
|
+
function whiten(text) {
|
|
970
|
+
if (process.env.NO_COLOR) return text;
|
|
971
|
+
return text.split("\n").map((line) => `\x1B[0m\x1B[97m${line}\x1B[0m`).join("\n");
|
|
972
|
+
}
|
|
973
|
+
async function printLaunchRecap(args) {
|
|
974
|
+
const { record } = args;
|
|
975
|
+
const rows = [];
|
|
976
|
+
rows.push([
|
|
977
|
+
"box",
|
|
978
|
+
args.checkpointRef ? `${record.name} (${args.checkpointRef})` : record.name
|
|
979
|
+
]);
|
|
980
|
+
if (record.projectRoot) {
|
|
981
|
+
rows.push(["project", homeShorten(record.projectRoot)]);
|
|
982
|
+
}
|
|
983
|
+
const toBranch = record.gitWorktrees?.find((w) => w.kind === "root")?.branch;
|
|
984
|
+
if (toBranch) {
|
|
985
|
+
if (args.useBranch) {
|
|
986
|
+
rows.push(["branch", `${toBranch} (reused)`]);
|
|
987
|
+
} else {
|
|
988
|
+
const base = args.fromBranch ?? await currentHostBranch(args.workspacePath) ?? "HEAD";
|
|
989
|
+
rows.push(["branch", `${base} \u2192 ${toBranch}`]);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
const pad4 = Math.max(...rows.map(([label]) => label.length)) + 2;
|
|
993
|
+
const body = rows.map(([label, value]) => `${label.padEnd(pad4)}${value}`).join("\n");
|
|
994
|
+
const instruction = args.attaching ? `Ctrl+a d to detach. Reattach with: agentbox ${args.mode} attach ${args.reattach}` : `Attach with: agentbox ${args.mode} attach ${args.reattach}`;
|
|
995
|
+
note(whiten(`${body}
|
|
996
|
+
|
|
997
|
+
${instruction}`));
|
|
998
|
+
}
|
|
999
|
+
|
|
870
1000
|
// src/commands/_cloud-agent-create.ts
|
|
871
1001
|
async function cloudAgentCreate(args) {
|
|
872
1002
|
const s = makeProgressReporter(args.verbose === true);
|
|
@@ -877,12 +1007,7 @@ async function cloudAgentCreate(args) {
|
|
|
877
1007
|
onLog: (line) => s.message(line)
|
|
878
1008
|
});
|
|
879
1009
|
const nSuffix = typeof result.record.projectIndex === "number" ? ` \xB7 n ${String(result.record.projectIndex)}` : "";
|
|
880
|
-
s.stop(`box
|
|
881
|
-
log4.info(`id: ${result.record.id}`);
|
|
882
|
-
log4.info(`provider: ${result.record.provider}`);
|
|
883
|
-
if (result.record.cloud?.sandboxId) {
|
|
884
|
-
log4.info(`sandboxId: ${result.record.cloud.sandboxId}`);
|
|
885
|
-
}
|
|
1010
|
+
s.stop(`box ready${nSuffix}`);
|
|
886
1011
|
let extraArgs = args.extraArgs;
|
|
887
1012
|
if (args.beforeStart) {
|
|
888
1013
|
const hook = await args.beforeStart(result.record);
|
|
@@ -890,13 +1015,19 @@ async function cloudAgentCreate(args) {
|
|
|
890
1015
|
extraArgs = [...hook.agentArgsPrefix, ...extraArgs ?? []];
|
|
891
1016
|
}
|
|
892
1017
|
}
|
|
1018
|
+
await printLaunchRecap({
|
|
1019
|
+
record: result.record,
|
|
1020
|
+
mode: args.mode,
|
|
1021
|
+
reattach: typeof result.record.projectIndex === "number" ? String(result.record.projectIndex) : result.record.name,
|
|
1022
|
+
workspacePath: args.request.workspacePath,
|
|
1023
|
+
fromBranch: args.request.fromBranch,
|
|
1024
|
+
useBranch: args.request.useBranch,
|
|
1025
|
+
checkpointRef: args.request.checkpointRef,
|
|
1026
|
+
attaching: args.attach !== false
|
|
1027
|
+
});
|
|
893
1028
|
if (args.attach === false) {
|
|
894
|
-
outro(
|
|
895
|
-
`session not started \u2014 attach with: agentbox ${args.mode} attach ${result.record.name}`
|
|
896
|
-
);
|
|
897
1029
|
return;
|
|
898
1030
|
}
|
|
899
|
-
outro(`attaching ${args.mode} \u2014 Control+a d to detach, leaves the agent running`);
|
|
900
1031
|
await cloudAgentAttach({
|
|
901
1032
|
box: result.record,
|
|
902
1033
|
binary: args.binary,
|
|
@@ -915,7 +1046,7 @@ async function cloudAgentCreate(args) {
|
|
|
915
1046
|
import { join as join4 } from "path";
|
|
916
1047
|
|
|
917
1048
|
// src/carry-prompt.ts
|
|
918
|
-
import { isCancel, log as
|
|
1049
|
+
import { isCancel, log as log4, select } from "@clack/prompts";
|
|
919
1050
|
|
|
920
1051
|
// src/fmt.ts
|
|
921
1052
|
function fmtBytes(n) {
|
|
@@ -999,7 +1130,7 @@ function printSummary(entries) {
|
|
|
999
1130
|
` ${pad(e.rawSrc, srcW)} \u2192 ${pad(e.rawDest, destW)} ${pad(size, 9)} ${flags.join(", ")}`
|
|
1000
1131
|
);
|
|
1001
1132
|
}
|
|
1002
|
-
|
|
1133
|
+
log4.message(rows.join("\n"));
|
|
1003
1134
|
}
|
|
1004
1135
|
function pad(s, w) {
|
|
1005
1136
|
if (s.length >= w) return s;
|
|
@@ -1008,13 +1139,13 @@ function pad(s, w) {
|
|
|
1008
1139
|
|
|
1009
1140
|
// src/lib/carry-resolve.ts
|
|
1010
1141
|
import { realpath, stat as stat2 } from "fs/promises";
|
|
1011
|
-
import { homedir as
|
|
1142
|
+
import { homedir as homedir3 } from "os";
|
|
1012
1143
|
import { isAbsolute, join as join3, normalize, resolve } from "path";
|
|
1013
1144
|
var DEFAULT_MAX_BYTES = 50 * 1024 * 1024;
|
|
1014
1145
|
var DENYLIST_DEST_PREFIXES = ["/proc", "/sys", "/dev"];
|
|
1015
1146
|
var DENYLIST_DEST_EXACT = /* @__PURE__ */ new Set(["/etc/passwd", "/etc/shadow"]);
|
|
1016
1147
|
async function resolveCarry(items, opts) {
|
|
1017
|
-
const home = opts.homeDir ??
|
|
1148
|
+
const home = opts.homeDir ?? homedir3();
|
|
1018
1149
|
const cap = opts.maxBytes ?? readMaxBytesFromEnv() ?? DEFAULT_MAX_BYTES;
|
|
1019
1150
|
const projectRoot = opts.projectRoot;
|
|
1020
1151
|
const entries = [];
|
|
@@ -1208,7 +1339,7 @@ async function dirSizeCapped(dir, cap) {
|
|
|
1208
1339
|
|
|
1209
1340
|
// src/lib/carry-gate.ts
|
|
1210
1341
|
async function runCarryGate(args) {
|
|
1211
|
-
const
|
|
1342
|
+
const log44 = args.onLog ?? (() => {
|
|
1212
1343
|
});
|
|
1213
1344
|
const yamlPath = join4(args.projectRoot, "agentbox.yaml");
|
|
1214
1345
|
const items = await loadCarrySection(yamlPath);
|
|
@@ -1230,108 +1361,16 @@ async function runCarryGate(args) {
|
|
|
1230
1361
|
});
|
|
1231
1362
|
if (decision === "cancel") return { decision: "cancel" };
|
|
1232
1363
|
if (decision === "skip-this-run") {
|
|
1233
|
-
|
|
1364
|
+
log44(`carry: skipped for this box (${String(resolved.entries.length)} entry/entries not copied)`);
|
|
1234
1365
|
return { decision: "skip", entries: [] };
|
|
1235
1366
|
}
|
|
1236
1367
|
return { decision: "approve", entries: resolved.entries };
|
|
1237
1368
|
}
|
|
1238
1369
|
|
|
1239
|
-
// src/lib/from-branch.ts
|
|
1240
|
-
import { execa } from "execa";
|
|
1241
|
-
var SHA_RE = /^[0-9a-f]{7,40}$/i;
|
|
1242
|
-
var FromBranchError = class extends Error {
|
|
1243
|
-
constructor(message) {
|
|
1244
|
-
super(message);
|
|
1245
|
-
this.name = "FromBranchError";
|
|
1246
|
-
}
|
|
1247
|
-
};
|
|
1248
|
-
async function resolveFromBranch(ref, opts) {
|
|
1249
|
-
if (!ref || ref.length === 0) return void 0;
|
|
1250
|
-
const remote = opts.remote ?? "origin";
|
|
1251
|
-
const isSha = SHA_RE.test(ref);
|
|
1252
|
-
if (!isSha) {
|
|
1253
|
-
const fetched = await execa(
|
|
1254
|
-
"git",
|
|
1255
|
-
["-C", opts.repo, "fetch", "--quiet", remote, ref],
|
|
1256
|
-
{ reject: false }
|
|
1257
|
-
);
|
|
1258
|
-
if (fetched.exitCode !== 0) {
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
const verify = await execa(
|
|
1262
|
-
"git",
|
|
1263
|
-
["-C", opts.repo, "rev-parse", "--verify", `${ref}^{commit}`],
|
|
1264
|
-
{ reject: false }
|
|
1265
|
-
);
|
|
1266
|
-
if (verify.exitCode !== 0) {
|
|
1267
|
-
throw new FromBranchError(
|
|
1268
|
-
`--from-branch: unknown ref "${ref}" (not found in ${opts.repo} after fetch). Provide a branch, tag, or SHA reachable from the host repo.`
|
|
1269
|
-
);
|
|
1270
|
-
}
|
|
1271
|
-
return ref;
|
|
1272
|
-
}
|
|
1273
|
-
var UseBranchError = class extends Error {
|
|
1274
|
-
constructor(message) {
|
|
1275
|
-
super(message);
|
|
1276
|
-
this.name = "UseBranchError";
|
|
1277
|
-
}
|
|
1278
|
-
};
|
|
1279
|
-
async function resolveUseBranch(name, opts) {
|
|
1280
|
-
if (!name || name.length === 0) return void 0;
|
|
1281
|
-
const remote = opts.remote ?? "origin";
|
|
1282
|
-
await execa("git", ["-C", opts.repo, "fetch", "--quiet", remote, name], {
|
|
1283
|
-
reject: false
|
|
1284
|
-
});
|
|
1285
|
-
const exists = await execa(
|
|
1286
|
-
"git",
|
|
1287
|
-
["-C", opts.repo, "show-ref", "--verify", "--quiet", `refs/heads/${name}`],
|
|
1288
|
-
{ reject: false }
|
|
1289
|
-
);
|
|
1290
|
-
if (exists.exitCode !== 0) {
|
|
1291
|
-
throw new UseBranchError(
|
|
1292
|
-
`--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).`
|
|
1293
|
-
);
|
|
1294
|
-
}
|
|
1295
|
-
return name;
|
|
1296
|
-
}
|
|
1297
|
-
async function currentHostBranch(repo) {
|
|
1298
|
-
const r = await execa("git", ["-C", repo, "rev-parse", "--abbrev-ref", "HEAD"], {
|
|
1299
|
-
reject: false
|
|
1300
|
-
});
|
|
1301
|
-
if (r.exitCode !== 0) return void 0;
|
|
1302
|
-
const branch = r.stdout.trim();
|
|
1303
|
-
if (!branch || branch === "HEAD") return void 0;
|
|
1304
|
-
return branch;
|
|
1305
|
-
}
|
|
1306
|
-
async function resolveBranchSelection(opts) {
|
|
1307
|
-
if (opts.useBranch && opts.fromBranch) {
|
|
1308
|
-
throw new UseBranchError(
|
|
1309
|
-
"--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."
|
|
1310
|
-
);
|
|
1311
|
-
}
|
|
1312
|
-
if (opts.useBranch) {
|
|
1313
|
-
return { useBranch: await resolveUseBranch(opts.useBranch, { repo: opts.repo }) };
|
|
1314
|
-
}
|
|
1315
|
-
if (opts.fromBranch) {
|
|
1316
|
-
return { fromBranch: await resolveFromBranch(opts.fromBranch, { repo: opts.repo }) };
|
|
1317
|
-
}
|
|
1318
|
-
if (opts.providerName !== "docker" && opts.cloudUseCurrentBranch) {
|
|
1319
|
-
const current = await currentHostBranch(opts.repo);
|
|
1320
|
-
if (current) {
|
|
1321
|
-
opts.log?.(`cloud.useCurrentBranch: starting box on host branch "${current}"`);
|
|
1322
|
-
return { useBranch: current };
|
|
1323
|
-
}
|
|
1324
|
-
opts.log?.(
|
|
1325
|
-
"cloud.useCurrentBranch is set but host HEAD is detached; forking a fresh branch instead"
|
|
1326
|
-
);
|
|
1327
|
-
}
|
|
1328
|
-
return {};
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
1370
|
// src/session-teleport/claude.ts
|
|
1332
1371
|
import { mkdir, mkdtemp, readdir, readFile as readFile2, stat as stat3, writeFile } from "fs/promises";
|
|
1333
1372
|
import { existsSync } from "fs";
|
|
1334
|
-
import { homedir as
|
|
1373
|
+
import { homedir as homedir4, tmpdir } from "os";
|
|
1335
1374
|
import { join as join5 } from "path";
|
|
1336
1375
|
|
|
1337
1376
|
// src/session-teleport/cwd-encoding.ts
|
|
@@ -1352,7 +1391,7 @@ var TeleportError = class extends Error {
|
|
|
1352
1391
|
// src/session-teleport/claude.ts
|
|
1353
1392
|
var BOX_CLAUDE_PROJECTS_DIR = `/home/vscode/.claude/projects/${BOX_WORKSPACE_ENCODED}`;
|
|
1354
1393
|
async function resolveClaudeTeleport(opts) {
|
|
1355
|
-
const hostHome = opts.hostHome ??
|
|
1394
|
+
const hostHome = opts.hostHome ?? homedir4();
|
|
1356
1395
|
const projectDir = join5(
|
|
1357
1396
|
hostHome,
|
|
1358
1397
|
".claude",
|
|
@@ -1439,10 +1478,10 @@ async function rewriteSessionFile(src, dst, hostCwd) {
|
|
|
1439
1478
|
// src/session-teleport/codex.ts
|
|
1440
1479
|
import { mkdtemp as mkdtemp2, readdir as readdir2, readFile as readFile3, stat as stat4, writeFile as writeFile2 } from "fs/promises";
|
|
1441
1480
|
import { existsSync as existsSync2 } from "fs";
|
|
1442
|
-
import { homedir as
|
|
1481
|
+
import { homedir as homedir5, tmpdir as tmpdir2 } from "os";
|
|
1443
1482
|
import { join as join6 } from "path";
|
|
1444
1483
|
async function resolveCodexTeleport(opts) {
|
|
1445
|
-
const hostHome = opts.hostHome ??
|
|
1484
|
+
const hostHome = opts.hostHome ?? homedir5();
|
|
1446
1485
|
const sessionsRoot = join6(hostHome, ".codex", "sessions");
|
|
1447
1486
|
if (!existsSync2(sessionsRoot)) {
|
|
1448
1487
|
throw new TeleportError(
|
|
@@ -1649,29 +1688,29 @@ async function uploadTeleport(input) {
|
|
|
1649
1688
|
}
|
|
1650
1689
|
|
|
1651
1690
|
// src/lib/install-hint.ts
|
|
1652
|
-
import { log as
|
|
1691
|
+
import { log as log5 } from "@clack/prompts";
|
|
1653
1692
|
import { existsSync as existsSync3, mkdirSync, writeFileSync } from "fs";
|
|
1654
|
-
import { homedir as
|
|
1693
|
+
import { homedir as homedir6 } from "os";
|
|
1655
1694
|
import { join as join7 } from "path";
|
|
1656
1695
|
function maybeShowInstallHint() {
|
|
1657
1696
|
try {
|
|
1658
|
-
const skill = join7(
|
|
1697
|
+
const skill = join7(homedir6(), ".claude", "skills", "agentbox", "SKILL.md");
|
|
1659
1698
|
if (existsSync3(skill)) return;
|
|
1660
|
-
const marker = join7(
|
|
1699
|
+
const marker = join7(homedir6(), ".agentbox", "install-hint-shown");
|
|
1661
1700
|
if (existsSync3(marker)) return;
|
|
1662
|
-
mkdirSync(join7(
|
|
1701
|
+
mkdirSync(join7(homedir6(), ".agentbox"), { recursive: true });
|
|
1663
1702
|
writeFileSync(marker, "");
|
|
1664
|
-
|
|
1703
|
+
log5.info("tip: run 'agentbox install' to enable the /agentbox fork command in host Claude");
|
|
1665
1704
|
} catch {
|
|
1666
1705
|
}
|
|
1667
1706
|
}
|
|
1668
1707
|
|
|
1669
1708
|
// src/lib/log-file.ts
|
|
1670
1709
|
import { closeSync, mkdirSync as mkdirSync2, openSync, renameSync, symlinkSync, unlinkSync, writeFileSync as writeFileSync2, writeSync } from "fs";
|
|
1671
|
-
import { homedir as
|
|
1710
|
+
import { homedir as homedir7 } from "os";
|
|
1672
1711
|
import { join as join8 } from "path";
|
|
1673
1712
|
function stateDir() {
|
|
1674
|
-
return process.env.AGENTBOX_HOME ?? join8(
|
|
1713
|
+
return process.env.AGENTBOX_HOME ?? join8(homedir7(), ".agentbox");
|
|
1675
1714
|
}
|
|
1676
1715
|
function logsDir() {
|
|
1677
1716
|
return join8(stateDir(), "logs");
|
|
@@ -1795,7 +1834,7 @@ function resolveLimits(box, flags) {
|
|
|
1795
1834
|
}
|
|
1796
1835
|
|
|
1797
1836
|
// src/portless-prompt.ts
|
|
1798
|
-
import { confirm, isCancel as isCancel2, log as
|
|
1837
|
+
import { confirm, isCancel as isCancel2, log as log6, spinner as spinner2 } from "@clack/prompts";
|
|
1799
1838
|
async function setupPortlessHost() {
|
|
1800
1839
|
let state = await detectPortless();
|
|
1801
1840
|
if (!state.installed) {
|
|
@@ -1805,13 +1844,13 @@ async function setupPortlessHost() {
|
|
|
1805
1844
|
resetPortlessCache();
|
|
1806
1845
|
s2.stop(ok ? "portless installed" : "portless install failed");
|
|
1807
1846
|
if (!ok) {
|
|
1808
|
-
|
|
1847
|
+
log6.warn(`Could not install Portless \u2014 run \`${portlessInstallHint()}\` yourself.`);
|
|
1809
1848
|
return;
|
|
1810
1849
|
}
|
|
1811
1850
|
state = await detectPortless();
|
|
1812
1851
|
}
|
|
1813
1852
|
if (state.proxyRunning) {
|
|
1814
|
-
|
|
1853
|
+
log6.info("Portless proxy already running \u2014 boxes will use it.");
|
|
1815
1854
|
return;
|
|
1816
1855
|
}
|
|
1817
1856
|
const s = spinner2();
|
|
@@ -1823,7 +1862,7 @@ async function setupPortlessHost() {
|
|
|
1823
1862
|
s.stop("portless proxy started");
|
|
1824
1863
|
} else {
|
|
1825
1864
|
s.stop("portless proxy did not start");
|
|
1826
|
-
|
|
1865
|
+
log6.warn(`Could not start the Portless proxy \u2014 run \`${portlessStartHint()}\` yourself.`);
|
|
1827
1866
|
}
|
|
1828
1867
|
}
|
|
1829
1868
|
async function maybePromptPortless(args) {
|
|
@@ -1838,7 +1877,7 @@ async function maybePromptPortless(args) {
|
|
|
1838
1877
|
try {
|
|
1839
1878
|
await setConfigValue("global", "portless.enabled", answer, args.cwd, { raw: false });
|
|
1840
1879
|
} catch (err) {
|
|
1841
|
-
|
|
1880
|
+
log6.warn(
|
|
1842
1881
|
`Could not save the Portless preference: ${err instanceof Error ? err.message : String(err)}`
|
|
1843
1882
|
);
|
|
1844
1883
|
}
|
|
@@ -1847,18 +1886,18 @@ async function maybePromptPortless(args) {
|
|
|
1847
1886
|
}
|
|
1848
1887
|
|
|
1849
1888
|
// src/wizard.ts
|
|
1850
|
-
import { confirm as confirm2, isCancel as isCancel3, log as
|
|
1889
|
+
import { confirm as confirm2, isCancel as isCancel3, log as log7, multiselect } from "@clack/prompts";
|
|
1851
1890
|
import { basename } from "path";
|
|
1852
1891
|
|
|
1853
1892
|
// src/provider/cloud-backend.ts
|
|
1854
1893
|
async function cloudBackendForProvider(provider) {
|
|
1855
1894
|
switch (provider) {
|
|
1856
1895
|
case "daytona":
|
|
1857
|
-
return (await import("./dist-
|
|
1896
|
+
return (await import("./dist-PJFJNXO2.js")).daytonaBackend;
|
|
1858
1897
|
case "hetzner":
|
|
1859
|
-
return (await import("./dist-
|
|
1898
|
+
return (await import("./dist-4SUIXKSD.js")).hetznerBackend;
|
|
1860
1899
|
case "vercel":
|
|
1861
|
-
return (await import("./dist-
|
|
1900
|
+
return (await import("./dist-HT2YV6PB.js")).vercelBackend;
|
|
1862
1901
|
default:
|
|
1863
1902
|
return null;
|
|
1864
1903
|
}
|
|
@@ -1909,7 +1948,7 @@ async function maybeRunSetupWizard(args) {
|
|
|
1909
1948
|
const proj = await findProjectRoot(args.workspace);
|
|
1910
1949
|
if (proj.hasAgentboxYaml) return { action: "proceed" };
|
|
1911
1950
|
if (args.checkpointRef && await checkpointAppliesHere(args)) {
|
|
1912
|
-
|
|
1951
|
+
log7.info(`starting from checkpoint "${args.checkpointRef}"; skipping agentbox.yaml setup`);
|
|
1913
1952
|
return { action: "proceed" };
|
|
1914
1953
|
}
|
|
1915
1954
|
let envFilesToImport;
|
|
@@ -1998,7 +2037,7 @@ function logPrune(rebuild) {
|
|
|
1998
2037
|
if (rebuild.prunedBytes <= 0) return;
|
|
1999
2038
|
const mb = Math.round(rebuild.prunedBytes / 1024 / 1024);
|
|
2000
2039
|
const n = rebuild.pruned.length;
|
|
2001
|
-
|
|
2040
|
+
log8.info(`pruned ${String(n)} stale plugin cache${n === 1 ? "" : "s"} (${String(mb)} MB freed)`);
|
|
2002
2041
|
}
|
|
2003
2042
|
var RELAY_HOST_URL = `http://127.0.0.1:${String(DEFAULT_RELAY_PORT)}`;
|
|
2004
2043
|
async function attachClaudeWrapped(box, sessionName, reattach, onError, openIn) {
|
|
@@ -2062,7 +2101,7 @@ async function maybeRunClaudeLogin(args) {
|
|
|
2062
2101
|
const message = args.authSource === "auth-file" ? "You're on a legacy API token (shows as 'Claude API'). Sign in with your Claude subscription instead?" : "Sign in with your Claude subscription? (saved and reused by every box)";
|
|
2063
2102
|
const answer = await confirm3({ message, initialValue: true });
|
|
2064
2103
|
if (isCancel4(answer) || !answer) {
|
|
2065
|
-
|
|
2104
|
+
log8.info("Skipped sign-in \u2014 claude will prompt you to /login inside the box.");
|
|
2066
2105
|
return;
|
|
2067
2106
|
}
|
|
2068
2107
|
const s = spinner3();
|
|
@@ -2076,10 +2115,10 @@ async function maybeRunClaudeLogin(args) {
|
|
|
2076
2115
|
s.stop("image ready");
|
|
2077
2116
|
const exitCode = await runClaudeLoginContainer(args.image, ["--claudeai"]);
|
|
2078
2117
|
if (exitCode !== 0) {
|
|
2079
|
-
|
|
2118
|
+
log8.warn("Claude login did not complete; continuing \u2014 run `agentbox claude login` to retry.");
|
|
2080
2119
|
return;
|
|
2081
2120
|
}
|
|
2082
|
-
|
|
2121
|
+
log8.success("Signed in with your Claude subscription \u2014 saved for future boxes.");
|
|
2083
2122
|
}
|
|
2084
2123
|
async function maybeRunCloudClaudeLogin(args) {
|
|
2085
2124
|
if (!process.stdin.isTTY || args.yes) return;
|
|
@@ -2090,7 +2129,7 @@ async function maybeRunCloudClaudeLogin(args) {
|
|
|
2090
2129
|
const message = expired ? "Your saved Claude login looks expired. Sign in again? (saved and reused by every box)" : "Sign in with your Claude subscription? (saved and reused by every box)";
|
|
2091
2130
|
const answer = await confirm3({ message, initialValue: true });
|
|
2092
2131
|
if (isCancel4(answer) || !answer) {
|
|
2093
|
-
|
|
2132
|
+
log8.info("Skipped sign-in \u2014 claude will prompt you to /login inside the box.");
|
|
2094
2133
|
return;
|
|
2095
2134
|
}
|
|
2096
2135
|
const s = spinner3();
|
|
@@ -2104,10 +2143,10 @@ async function maybeRunCloudClaudeLogin(args) {
|
|
|
2104
2143
|
s.stop("image ready");
|
|
2105
2144
|
const exitCode = await runClaudeLoginContainer(args.image, ["--claudeai"]);
|
|
2106
2145
|
if (exitCode !== 0) {
|
|
2107
|
-
|
|
2146
|
+
log8.warn("Claude login did not complete; continuing \u2014 run `agentbox claude login` to retry.");
|
|
2108
2147
|
return;
|
|
2109
2148
|
}
|
|
2110
|
-
|
|
2149
|
+
log8.success("Signed in with your Claude subscription \u2014 saved for future boxes.");
|
|
2111
2150
|
}
|
|
2112
2151
|
var claudeCommand = new Command2("claude").description("Create a sandboxed box and launch Claude Code in a detachable tmux session").option("-w, --workspace <path>", "host workspace to mount", process.cwd()).option("-n, --name <name>", "friendly box name (default: <workspace-basename>-<id>)").option("--host-snapshot", "APFS-clone the host workspace into a per-box scratch dir before seeding /workspace (stabilizes the tar-pipe source)").option("--no-host-snapshot", "tar-pipe directly from the live host workspace at create time").option(
|
|
2113
2152
|
"--snapshot <ref>",
|
|
@@ -2174,14 +2213,14 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2174
2213
|
intro("Starting Claude in a box...");
|
|
2175
2214
|
let resumeMode = null;
|
|
2176
2215
|
if (opts.continue === true && opts.resume) {
|
|
2177
|
-
|
|
2216
|
+
log8.error("only one of -c / --continue / --resume can be passed");
|
|
2178
2217
|
cmdLog.close();
|
|
2179
2218
|
process.exit(2);
|
|
2180
2219
|
}
|
|
2181
2220
|
if (opts.continue === true) resumeMode = { kind: "continue" };
|
|
2182
2221
|
else if (opts.resume) resumeMode = { kind: "resume", id: opts.resume };
|
|
2183
2222
|
if (resumeMode && opts.initialPrompt && opts.initialPrompt.length > 0) {
|
|
2184
|
-
|
|
2223
|
+
log8.error("-i / --initial-prompt cannot be combined with -c / --resume (seeding a new turn into a resumed session is not supported).");
|
|
2185
2224
|
cmdLog.close();
|
|
2186
2225
|
process.exit(2);
|
|
2187
2226
|
}
|
|
@@ -2196,7 +2235,7 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2196
2235
|
});
|
|
2197
2236
|
} catch (err) {
|
|
2198
2237
|
if (err instanceof TeleportError) {
|
|
2199
|
-
|
|
2238
|
+
log8.error(err.message);
|
|
2200
2239
|
cmdLog.close();
|
|
2201
2240
|
process.exit(2);
|
|
2202
2241
|
}
|
|
@@ -2211,7 +2250,7 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2211
2250
|
const isCloud = providerName !== "docker";
|
|
2212
2251
|
if (opts.initialPrompt && opts.initialPrompt.length > 0) {
|
|
2213
2252
|
if (isCloud) {
|
|
2214
|
-
|
|
2253
|
+
log8.error("-i / --initial-prompt is currently docker-only (cloud sessions only start on attach).");
|
|
2215
2254
|
cmdLog.close();
|
|
2216
2255
|
process.exit(2);
|
|
2217
2256
|
}
|
|
@@ -2222,7 +2261,7 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2222
2261
|
});
|
|
2223
2262
|
} catch (err) {
|
|
2224
2263
|
if (err instanceof MissingAgentCredsError) {
|
|
2225
|
-
|
|
2264
|
+
log8.error(err.message);
|
|
2226
2265
|
cmdLog.close();
|
|
2227
2266
|
process.exit(2);
|
|
2228
2267
|
}
|
|
@@ -2240,7 +2279,7 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2240
2279
|
maxRunningOverride,
|
|
2241
2280
|
maxWorkingOverride
|
|
2242
2281
|
});
|
|
2243
|
-
|
|
2282
|
+
outro(
|
|
2244
2283
|
`job ${result.job.id} queued (${String(result.runningCount)}/${String(result.maxConcurrent)} running); log: ${result.job.logPath}`
|
|
2245
2284
|
);
|
|
2246
2285
|
cmdLog.close();
|
|
@@ -2280,13 +2319,13 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2280
2319
|
onLog: (line) => cmdLog.write(line)
|
|
2281
2320
|
});
|
|
2282
2321
|
if (gate.decision === "cancel") {
|
|
2283
|
-
|
|
2322
|
+
log8.warn("carry: cancelled \u2014 not creating the box");
|
|
2284
2323
|
cmdLog.close();
|
|
2285
2324
|
process.exit(0);
|
|
2286
2325
|
}
|
|
2287
2326
|
if (gate.decision === "approve") carryEntries = gate.entries;
|
|
2288
2327
|
} catch (err) {
|
|
2289
|
-
|
|
2328
|
+
log8.error(err instanceof Error ? err.message : String(err));
|
|
2290
2329
|
cmdLog.close();
|
|
2291
2330
|
process.exit(1);
|
|
2292
2331
|
}
|
|
@@ -2316,7 +2355,7 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2316
2355
|
}));
|
|
2317
2356
|
} catch (err) {
|
|
2318
2357
|
if (err instanceof FromBranchError || err instanceof UseBranchError) {
|
|
2319
|
-
|
|
2358
|
+
log8.error(err.message);
|
|
2320
2359
|
cmdLog.close();
|
|
2321
2360
|
process.exit(2);
|
|
2322
2361
|
}
|
|
@@ -2360,7 +2399,7 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2360
2399
|
return { agentArgsPrefix: resumePrepared.forwardArgs };
|
|
2361
2400
|
} catch (err) {
|
|
2362
2401
|
if (err instanceof TeleportError) {
|
|
2363
|
-
|
|
2402
|
+
log8.error(err.message);
|
|
2364
2403
|
cmdLog.close();
|
|
2365
2404
|
process.exit(2);
|
|
2366
2405
|
}
|
|
@@ -2430,8 +2469,8 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2430
2469
|
} catch (err) {
|
|
2431
2470
|
if (err instanceof TeleportError) {
|
|
2432
2471
|
s.stop("teleport failed");
|
|
2433
|
-
|
|
2434
|
-
|
|
2472
|
+
log8.error(err.message);
|
|
2473
|
+
log8.info(
|
|
2435
2474
|
`The box ${result.record.container} is up but unused. Destroy it with: agentbox destroy ${result.record.container} -y`
|
|
2436
2475
|
);
|
|
2437
2476
|
cmdLog.close();
|
|
@@ -2448,20 +2487,26 @@ var claudeCommand = new Command2("claude").description("Create a sandboxed box a
|
|
|
2448
2487
|
boxName: result.record.name
|
|
2449
2488
|
});
|
|
2450
2489
|
const nSuffix = typeof result.record.projectIndex === "number" ? ` \xB7 n ${String(result.record.projectIndex)}` : "";
|
|
2451
|
-
s.stop(`box
|
|
2490
|
+
s.stop(`box ready${nSuffix}`);
|
|
2452
2491
|
logPrune(rebuild);
|
|
2453
2492
|
for (const f of rebuild.failed) {
|
|
2454
|
-
|
|
2493
|
+
log8.warn(`plugin install failed for ${f.dir}; claude may still load it. stderr:
|
|
2455
2494
|
${f.stderr.trim()}`);
|
|
2456
2495
|
}
|
|
2457
2496
|
maybeShowInstallHint();
|
|
2497
|
+
await printLaunchRecap({
|
|
2498
|
+
record: result.record,
|
|
2499
|
+
mode: "claude",
|
|
2500
|
+
reattach: reattachRef(result.record),
|
|
2501
|
+
workspacePath: opts.workspace,
|
|
2502
|
+
fromBranch,
|
|
2503
|
+
useBranch,
|
|
2504
|
+
checkpointRef,
|
|
2505
|
+
attaching: opts.attach !== false
|
|
2506
|
+
});
|
|
2458
2507
|
if (opts.attach === false) {
|
|
2459
|
-
outro2(
|
|
2460
|
-
`session started \u2014 attach with: agentbox claude attach ${reattachRef(result.record)}`
|
|
2461
|
-
);
|
|
2462
2508
|
return;
|
|
2463
2509
|
}
|
|
2464
|
-
outro2("attaching \u2014 Control+a d to detach, leaves claude running");
|
|
2465
2510
|
await attachClaudeWrapped(
|
|
2466
2511
|
result.record,
|
|
2467
2512
|
sessionName,
|
|
@@ -2473,10 +2518,10 @@ ${f.stderr.trim()}`);
|
|
|
2473
2518
|
s.stop("failed");
|
|
2474
2519
|
cmdLog.write(`FAIL: ${err instanceof Error ? err.stack ?? err.message : String(err)}`);
|
|
2475
2520
|
if (err instanceof ClaudeSessionError) {
|
|
2476
|
-
|
|
2521
|
+
log8.error(err.message);
|
|
2477
2522
|
if (containerName) {
|
|
2478
|
-
|
|
2479
|
-
|
|
2523
|
+
log8.info(`The box ${containerName} is still running. Destroy it with:`);
|
|
2524
|
+
log8.info(` agentbox destroy ${containerName} -y`);
|
|
2480
2525
|
}
|
|
2481
2526
|
cmdLog.close();
|
|
2482
2527
|
process.exit(1);
|
|
@@ -2514,12 +2559,12 @@ async function startOrAttachClaude(box, claudeArgs, opts, resumePrepared) {
|
|
|
2514
2559
|
);
|
|
2515
2560
|
}
|
|
2516
2561
|
if (!wantAttach) {
|
|
2517
|
-
|
|
2562
|
+
outro(
|
|
2518
2563
|
`session "${sessionName}" already running \u2014 attach with: agentbox claude attach ${reattachRef(box)}`
|
|
2519
2564
|
);
|
|
2520
2565
|
return;
|
|
2521
2566
|
}
|
|
2522
|
-
|
|
2567
|
+
outro(`session "${sessionName}" already running \u2014 attaching (Control+a d to detach)`);
|
|
2523
2568
|
await attachClaudeWrapped(box, sessionName, reattachRef(box), void 0, openIn);
|
|
2524
2569
|
return;
|
|
2525
2570
|
}
|
|
@@ -2577,7 +2622,7 @@ async function startOrAttachClaude(box, claudeArgs, opts, resumePrepared) {
|
|
|
2577
2622
|
} catch (err) {
|
|
2578
2623
|
if (err instanceof TeleportError) {
|
|
2579
2624
|
s.stop("teleport failed");
|
|
2580
|
-
|
|
2625
|
+
log8.error(err.message);
|
|
2581
2626
|
process.exit(2);
|
|
2582
2627
|
}
|
|
2583
2628
|
throw err;
|
|
@@ -2593,16 +2638,16 @@ async function startOrAttachClaude(box, claudeArgs, opts, resumePrepared) {
|
|
|
2593
2638
|
s.stop(`box ${box.container} ready`);
|
|
2594
2639
|
logPrune(rebuild);
|
|
2595
2640
|
for (const f of rebuild.failed) {
|
|
2596
|
-
|
|
2641
|
+
log8.warn(`plugin install failed for ${f.dir}; claude may still load it. stderr:
|
|
2597
2642
|
${f.stderr.trim()}`);
|
|
2598
2643
|
}
|
|
2599
2644
|
if (!wantAttach) {
|
|
2600
|
-
|
|
2645
|
+
outro(
|
|
2601
2646
|
`session "${sessionName}" started \u2014 attach with: agentbox claude attach ${reattachRef(box)}`
|
|
2602
2647
|
);
|
|
2603
2648
|
return;
|
|
2604
2649
|
}
|
|
2605
|
-
|
|
2650
|
+
outro("attaching \u2014 Control+a d to detach, leaves claude running");
|
|
2606
2651
|
await attachClaudeWrapped(box, sessionName, reattachRef(box), void 0, openIn);
|
|
2607
2652
|
}
|
|
2608
2653
|
var claudeAttachCommand = new Command2("attach").description(
|
|
@@ -2632,7 +2677,7 @@ var claudeAttachCommand = new Command2("attach").description(
|
|
|
2632
2677
|
await startOrAttachClaude(box, [], { ...opts, syncConfig: false });
|
|
2633
2678
|
} catch (err) {
|
|
2634
2679
|
if (err instanceof ClaudeSessionError) {
|
|
2635
|
-
|
|
2680
|
+
log8.error(err.message);
|
|
2636
2681
|
process.exit(1);
|
|
2637
2682
|
}
|
|
2638
2683
|
handleLifecycleError(err);
|
|
@@ -2664,7 +2709,7 @@ var claudeStartCommand = new Command2("start").description(
|
|
|
2664
2709
|
let effectiveClaudeArgs = shifted && idOrName ? [idOrName, ...claudeArgs] : claudeArgs;
|
|
2665
2710
|
let resumeMode = null;
|
|
2666
2711
|
if (opts.continue === true && opts.resume) {
|
|
2667
|
-
|
|
2712
|
+
log8.error("only one of -c / --continue / --resume can be passed");
|
|
2668
2713
|
process.exit(2);
|
|
2669
2714
|
}
|
|
2670
2715
|
if (opts.continue === true) resumeMode = { kind: "continue" };
|
|
@@ -2679,7 +2724,7 @@ var claudeStartCommand = new Command2("start").description(
|
|
|
2679
2724
|
});
|
|
2680
2725
|
} catch (err) {
|
|
2681
2726
|
if (err instanceof TeleportError) {
|
|
2682
|
-
|
|
2727
|
+
log8.error(err.message);
|
|
2683
2728
|
process.exit(2);
|
|
2684
2729
|
}
|
|
2685
2730
|
throw err;
|
|
@@ -2687,7 +2732,7 @@ var claudeStartCommand = new Command2("start").description(
|
|
|
2687
2732
|
}
|
|
2688
2733
|
if ((box.provider ?? "docker") !== "docker") {
|
|
2689
2734
|
if (opts.attach === false) {
|
|
2690
|
-
|
|
2735
|
+
outro(
|
|
2691
2736
|
`--no-attach: cloud agent sessions are started lazily on attach. Run: agentbox claude attach ${reattachRef(box)}`
|
|
2692
2737
|
);
|
|
2693
2738
|
return;
|
|
@@ -2706,7 +2751,7 @@ var claudeStartCommand = new Command2("start").description(
|
|
|
2706
2751
|
effectiveClaudeArgs = [...resumePrepared.forwardArgs, ...effectiveClaudeArgs];
|
|
2707
2752
|
} catch (err) {
|
|
2708
2753
|
if (err instanceof TeleportError) {
|
|
2709
|
-
|
|
2754
|
+
log8.error(err.message);
|
|
2710
2755
|
process.exit(2);
|
|
2711
2756
|
}
|
|
2712
2757
|
throw err;
|
|
@@ -2725,7 +2770,7 @@ var claudeStartCommand = new Command2("start").description(
|
|
|
2725
2770
|
await startOrAttachClaude(box, effectiveClaudeArgs, opts, resumePrepared);
|
|
2726
2771
|
} catch (err) {
|
|
2727
2772
|
if (err instanceof ClaudeSessionError) {
|
|
2728
|
-
|
|
2773
|
+
log8.error(err.message);
|
|
2729
2774
|
process.exit(1);
|
|
2730
2775
|
}
|
|
2731
2776
|
handleLifecycleError(err);
|
|
@@ -2739,7 +2784,7 @@ var claudeLoginCommand = new Command2("login").description(
|
|
|
2739
2784
|
).action(async (args) => {
|
|
2740
2785
|
intro("Signing in to Claude...");
|
|
2741
2786
|
if (!process.stdin.isTTY) {
|
|
2742
|
-
|
|
2787
|
+
log8.error("`agentbox claude login` needs an interactive terminal.");
|
|
2743
2788
|
process.exit(1);
|
|
2744
2789
|
}
|
|
2745
2790
|
try {
|
|
@@ -2751,10 +2796,10 @@ var claudeLoginCommand = new Command2("login").description(
|
|
|
2751
2796
|
s.stop("image ready");
|
|
2752
2797
|
const exitCode = await runClaudeLoginContainer(image, args);
|
|
2753
2798
|
if (exitCode !== 0) {
|
|
2754
|
-
|
|
2799
|
+
log8.warn(`\`claude auth login\` exited with code ${String(exitCode)}`);
|
|
2755
2800
|
process.exit(exitCode);
|
|
2756
2801
|
}
|
|
2757
|
-
|
|
2802
|
+
outro("signed in \u2014 credentials saved for future boxes");
|
|
2758
2803
|
} catch (err) {
|
|
2759
2804
|
handleLifecycleError(err);
|
|
2760
2805
|
}
|
|
@@ -2764,17 +2809,17 @@ claudeCommand.addCommand(claudeStartCommand);
|
|
|
2764
2809
|
claudeCommand.addCommand(claudeLoginCommand);
|
|
2765
2810
|
|
|
2766
2811
|
// src/commands/checkpoint.ts
|
|
2767
|
-
import { confirm as confirm4, isCancel as isCancel5, log as
|
|
2812
|
+
import { confirm as confirm4, isCancel as isCancel5, log as log9 } from "@clack/prompts";
|
|
2768
2813
|
import { Command as Command3 } from "commander";
|
|
2769
2814
|
var CLOUD_BACKENDS = ["daytona", "hetzner", "vercel"];
|
|
2770
2815
|
async function cloudProviderFor(backend) {
|
|
2771
2816
|
switch (backend) {
|
|
2772
2817
|
case "daytona":
|
|
2773
|
-
return (await import("./dist-
|
|
2818
|
+
return (await import("./dist-PJFJNXO2.js")).daytonaProvider;
|
|
2774
2819
|
case "hetzner":
|
|
2775
|
-
return (await import("./dist-
|
|
2820
|
+
return (await import("./dist-4SUIXKSD.js")).hetznerProvider;
|
|
2776
2821
|
case "vercel":
|
|
2777
|
-
return (await import("./dist-
|
|
2822
|
+
return (await import("./dist-HT2YV6PB.js")).vercelProvider;
|
|
2778
2823
|
}
|
|
2779
2824
|
}
|
|
2780
2825
|
var CHECKPOINT_NOTICE = "Checkpoint in progress \u2014 the box will be unresponsive for a moment";
|
|
@@ -2798,10 +2843,10 @@ var createSub = new Command3("create").description("Capture a box state as a pro
|
|
|
2798
2843
|
}
|
|
2799
2844
|
const insp = await inspectBox(box.id);
|
|
2800
2845
|
if (insp.state === "paused") {
|
|
2801
|
-
|
|
2846
|
+
log9.info("box is paused; unpausing");
|
|
2802
2847
|
await unpauseBox(box.id);
|
|
2803
2848
|
} else if (insp.state === "stopped") {
|
|
2804
|
-
|
|
2849
|
+
log9.info("box is stopped; starting");
|
|
2805
2850
|
await startBox(box.id);
|
|
2806
2851
|
} else if (insp.state === "missing") {
|
|
2807
2852
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
@@ -2836,13 +2881,13 @@ var createSub = new Command3("create").description("Capture a box state as a pro
|
|
|
2836
2881
|
setDefault: opts.setDefault === true,
|
|
2837
2882
|
replace: opts.replace === true,
|
|
2838
2883
|
maxLayers: cfg.effective.checkpoint.maxLayers,
|
|
2839
|
-
onLog: (line) =>
|
|
2884
|
+
onLog: (line) => log9.info(line)
|
|
2840
2885
|
});
|
|
2841
|
-
|
|
2886
|
+
log9.success(
|
|
2842
2887
|
`checkpoint ${info.name} (${info.manifest.type}) -> ${info.dir}` + (opts.setDefault ? " [project default]" : "")
|
|
2843
2888
|
);
|
|
2844
2889
|
if (!opts.setDefault) {
|
|
2845
|
-
|
|
2890
|
+
log9.info(
|
|
2846
2891
|
`make it the default for new boxes: agentbox checkpoint set-default ${info.name}`
|
|
2847
2892
|
);
|
|
2848
2893
|
}
|
|
@@ -2957,7 +3002,7 @@ var rmSub = new Command3("rm").description("Delete a checkpoint (any provider th
|
|
|
2957
3002
|
if (!opts.yes) {
|
|
2958
3003
|
const ok = await confirm4({ message: `Delete checkpoint ${ref}?`, initialValue: false });
|
|
2959
3004
|
if (isCancel5(ok) || !ok) {
|
|
2960
|
-
|
|
3005
|
+
log9.info("cancelled");
|
|
2961
3006
|
return;
|
|
2962
3007
|
}
|
|
2963
3008
|
}
|
|
@@ -2978,7 +3023,7 @@ var rmSub = new Command3("rm").description("Delete a checkpoint (any provider th
|
|
|
2978
3023
|
process.stdout.write(`removed ${backend} checkpoint ${ref}
|
|
2979
3024
|
`);
|
|
2980
3025
|
} catch (err) {
|
|
2981
|
-
|
|
3026
|
+
log9.warn(
|
|
2982
3027
|
`${backend} checkpoint remove failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2983
3028
|
);
|
|
2984
3029
|
}
|
|
@@ -2996,9 +3041,9 @@ var rmSub = new Command3("rm").description("Delete a checkpoint (any provider th
|
|
|
2996
3041
|
for (const [key, projectValue, effectiveValue] of defKeys) {
|
|
2997
3042
|
if (projectValue === ref) {
|
|
2998
3043
|
await unsetConfigValue("project", key, projectRoot);
|
|
2999
|
-
|
|
3044
|
+
log9.info(`cleared project ${key} (was ${ref})`);
|
|
3000
3045
|
} else if (effectiveValue === ref) {
|
|
3001
|
-
|
|
3046
|
+
log9.warn(
|
|
3002
3047
|
`${key} = ${ref} is set outside the per-project config (global or agentbox.yaml defaults) \u2014 clear it manually`
|
|
3003
3048
|
);
|
|
3004
3049
|
}
|
|
@@ -3009,17 +3054,17 @@ var rmSub = new Command3("rm").description("Delete a checkpoint (any provider th
|
|
|
3009
3054
|
});
|
|
3010
3055
|
async function runCloudCheckpointCreate(box, opts) {
|
|
3011
3056
|
if (opts.merged) {
|
|
3012
|
-
|
|
3057
|
+
log9.warn("--merged is Docker-only (cloud snapshots are always flattened); ignoring");
|
|
3013
3058
|
}
|
|
3014
3059
|
const projectRoot = await projectRootFor(box.workspacePath, box.projectRoot);
|
|
3015
3060
|
const name = opts.name ?? `${box.name}-${String(Date.now()).slice(-6)}`;
|
|
3016
3061
|
const provider = await providerForBox(box);
|
|
3017
3062
|
const state = await provider.probeState(box);
|
|
3018
3063
|
if (state === "paused") {
|
|
3019
|
-
|
|
3064
|
+
log9.info("box is paused; resuming");
|
|
3020
3065
|
await provider.resume(box);
|
|
3021
3066
|
} else if (state === "stopped") {
|
|
3022
|
-
|
|
3067
|
+
log9.info("box is stopped; starting");
|
|
3023
3068
|
await provider.start(box);
|
|
3024
3069
|
} else if (state === "missing") {
|
|
3025
3070
|
throw new Error(`cloud sandbox for ${box.name} is missing; was it deleted?`);
|
|
@@ -3033,7 +3078,7 @@ async function runCloudCheckpointCreate(box, opts) {
|
|
|
3033
3078
|
initialValue: false
|
|
3034
3079
|
});
|
|
3035
3080
|
if (isCancel5(ok) || !ok) {
|
|
3036
|
-
|
|
3081
|
+
log9.info("cancelled");
|
|
3037
3082
|
return;
|
|
3038
3083
|
}
|
|
3039
3084
|
}
|
|
@@ -3048,21 +3093,21 @@ async function runCloudCheckpointCreate(box, opts) {
|
|
|
3048
3093
|
try {
|
|
3049
3094
|
const saved = await provider.extractAgentCredentials(box);
|
|
3050
3095
|
if (saved.length > 0) {
|
|
3051
|
-
|
|
3096
|
+
log9.info(`saved ${saved.join(", ")} login to ~/.agentbox for future boxes`);
|
|
3052
3097
|
}
|
|
3053
3098
|
} catch (err) {
|
|
3054
|
-
|
|
3099
|
+
log9.warn(`agent credential extract skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
3055
3100
|
}
|
|
3056
3101
|
}
|
|
3057
|
-
|
|
3102
|
+
log9.info(`capturing cloud snapshot '${name}' (this may take a few minutes)`);
|
|
3058
3103
|
const result = await provider.checkpoint.create(box, name);
|
|
3059
|
-
|
|
3104
|
+
log9.success(`checkpoint ${result.ref} (daytona snapshot) captured`);
|
|
3060
3105
|
if (opts.setDefault) {
|
|
3061
3106
|
const key = defaultCheckpointConfigKey(box.provider ?? "daytona");
|
|
3062
3107
|
await setConfigValue("project", key, result.ref, projectRoot);
|
|
3063
|
-
|
|
3108
|
+
log9.info(`set project default checkpoint (${key}) -> ${result.ref}`);
|
|
3064
3109
|
} else {
|
|
3065
|
-
|
|
3110
|
+
log9.info(
|
|
3066
3111
|
`make it the default for new boxes: agentbox checkpoint set-default --provider ${box.provider ?? "daytona"} ${result.ref}`
|
|
3067
3112
|
);
|
|
3068
3113
|
}
|
|
@@ -3074,15 +3119,15 @@ var checkpointCommand = new Command3("checkpoint").alias("checkpoints").descript
|
|
|
3074
3119
|
|
|
3075
3120
|
// src/commands/code.ts
|
|
3076
3121
|
import { spawn } from "child_process";
|
|
3077
|
-
import { log as
|
|
3122
|
+
import { log as log10 } from "@clack/prompts";
|
|
3078
3123
|
import { Command as Command4, InvalidArgumentError } from "commander";
|
|
3079
3124
|
|
|
3080
3125
|
// src/ssh-config.ts
|
|
3081
3126
|
import { promises as fs } from "fs";
|
|
3082
|
-
import { homedir as
|
|
3127
|
+
import { homedir as homedir8 } from "os";
|
|
3083
3128
|
import { join as join9 } from "path";
|
|
3084
3129
|
function sshConfigPath() {
|
|
3085
|
-
return join9(
|
|
3130
|
+
return join9(homedir8(), ".ssh", "config");
|
|
3086
3131
|
}
|
|
3087
3132
|
function beginMarker(alias) {
|
|
3088
3133
|
return `# BEGIN agentbox cloud box ${alias}`;
|
|
@@ -3133,7 +3178,7 @@ function buildBlock(opts) {
|
|
|
3133
3178
|
}
|
|
3134
3179
|
async function writeAgentboxSshAlias(opts) {
|
|
3135
3180
|
const path = sshConfigPath();
|
|
3136
|
-
await fs.mkdir(join9(
|
|
3181
|
+
await fs.mkdir(join9(homedir8(), ".ssh"), { recursive: true, mode: 448 });
|
|
3137
3182
|
const existing = await readConfig();
|
|
3138
3183
|
const stripped = stripBlock(existing, opts.alias);
|
|
3139
3184
|
const separator = stripped.length === 0 || stripped.endsWith("\n") ? "" : "\n";
|
|
@@ -3215,11 +3260,11 @@ var codeCommand = new Command4("code").description("Open a box in VS Code or Cur
|
|
|
3215
3260
|
}
|
|
3216
3261
|
const exit = await launchIde(folderUri, forcedIde);
|
|
3217
3262
|
if (exit.code !== 0) {
|
|
3218
|
-
|
|
3263
|
+
log10.error(`failed to launch ${exit.flavor ? ideProfile(exit.flavor).displayName : "IDE"} via ${exit.via} (exit ${String(exit.code)})`);
|
|
3219
3264
|
process.stdout.write(folderUri + "\n");
|
|
3220
3265
|
process.exit(1);
|
|
3221
3266
|
}
|
|
3222
|
-
|
|
3267
|
+
log10.success(
|
|
3223
3268
|
`opening ${box.name} in ${ideProfile(exit.flavor).displayName} (${exit.via})`
|
|
3224
3269
|
);
|
|
3225
3270
|
} catch (err) {
|
|
@@ -3229,10 +3274,10 @@ var codeCommand = new Command4("code").description("Open a box in VS Code or Cur
|
|
|
3229
3274
|
async function prepareDockerAttach(box, opts) {
|
|
3230
3275
|
const insp = await inspectBox(box.id);
|
|
3231
3276
|
if (insp.state === "paused") {
|
|
3232
|
-
|
|
3277
|
+
log10.info(`box is paused; unpausing`);
|
|
3233
3278
|
await unpauseBox(box.id);
|
|
3234
3279
|
} else if (insp.state === "stopped") {
|
|
3235
|
-
|
|
3280
|
+
log10.info(`box is stopped; starting`);
|
|
3236
3281
|
await startBox(box.id);
|
|
3237
3282
|
} else if (insp.state === "missing") {
|
|
3238
3283
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
@@ -3243,9 +3288,9 @@ async function prepareDockerAttach(box, opts) {
|
|
|
3243
3288
|
const lines = [];
|
|
3244
3289
|
if (reply.timedOut.length > 0) lines.push(`timed out: ${reply.timedOut.join(", ")}`);
|
|
3245
3290
|
if (reply.failed.length > 0) lines.push(`failed: ${reply.failed.join(", ")}`);
|
|
3246
|
-
|
|
3291
|
+
log10.warn(`box not fully ready (${lines.join("; ")}). Opening anyway.`);
|
|
3247
3292
|
} else {
|
|
3248
|
-
|
|
3293
|
+
log10.success("all units ready");
|
|
3249
3294
|
}
|
|
3250
3295
|
}
|
|
3251
3296
|
if (opts.autoTerminals) {
|
|
@@ -3255,14 +3300,14 @@ async function prepareDockerAttach(box, opts) {
|
|
|
3255
3300
|
regen: opts.regenTasks
|
|
3256
3301
|
});
|
|
3257
3302
|
if (r.status === "wrote") {
|
|
3258
|
-
|
|
3303
|
+
log10.info(`wrote /workspace/.vscode/tasks.json (${String(services.length)} service(s))`);
|
|
3259
3304
|
} else if (r.status === "skipped-user-owned") {
|
|
3260
|
-
|
|
3305
|
+
log10.warn(
|
|
3261
3306
|
"user-owned .vscode/tasks.json detected; skipping auto-terminals (pass --regen-tasks to overwrite)"
|
|
3262
3307
|
);
|
|
3263
3308
|
}
|
|
3264
3309
|
} catch (err) {
|
|
3265
|
-
|
|
3310
|
+
log10.warn(
|
|
3266
3311
|
`auto-terminals failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3267
3312
|
);
|
|
3268
3313
|
}
|
|
@@ -3274,10 +3319,10 @@ async function prepareCloudAttach(box, opts) {
|
|
|
3274
3319
|
const p = await providerForBox(box);
|
|
3275
3320
|
const state = await p.probeState(box);
|
|
3276
3321
|
if (state === "paused") {
|
|
3277
|
-
|
|
3322
|
+
log10.info("box is paused; resuming");
|
|
3278
3323
|
await p.resume(box);
|
|
3279
3324
|
} else if (state === "stopped") {
|
|
3280
|
-
|
|
3325
|
+
log10.info("box is stopped; starting");
|
|
3281
3326
|
await p.start(box);
|
|
3282
3327
|
} else if (state === "missing") {
|
|
3283
3328
|
throw new Error(`cloud sandbox for ${box.name} is missing; was it deleted?`);
|
|
@@ -3290,12 +3335,12 @@ async function prepareCloudAttach(box, opts) {
|
|
|
3290
3335
|
const lines = [];
|
|
3291
3336
|
if (reply.timedOut.length > 0) lines.push(`timed out: ${reply.timedOut.join(", ")}`);
|
|
3292
3337
|
if (reply.failed.length > 0) lines.push(`failed: ${reply.failed.join(", ")}`);
|
|
3293
|
-
|
|
3338
|
+
log10.warn(`box not fully ready (${lines.join("; ")}). Opening anyway.`);
|
|
3294
3339
|
} else {
|
|
3295
|
-
|
|
3340
|
+
log10.success("all units ready");
|
|
3296
3341
|
}
|
|
3297
3342
|
} catch (err) {
|
|
3298
|
-
|
|
3343
|
+
log10.warn(`wait-ready failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
3299
3344
|
}
|
|
3300
3345
|
}
|
|
3301
3346
|
if (!p.buildAttach) {
|
|
@@ -3317,7 +3362,7 @@ async function prepareCloudAttach(box, opts) {
|
|
|
3317
3362
|
user: target.user,
|
|
3318
3363
|
identityFile: target.identityFile
|
|
3319
3364
|
});
|
|
3320
|
-
|
|
3365
|
+
log10.info(`updated ~/.ssh/config alias ${alias}`);
|
|
3321
3366
|
return `vscode-remote://ssh-remote+${alias}/workspace`;
|
|
3322
3367
|
}
|
|
3323
3368
|
async function runWaitReadyDocker(container, timeoutMs) {
|
|
@@ -3342,7 +3387,7 @@ async function launchIde(folderUri, forced) {
|
|
|
3342
3387
|
if (code !== null) return code;
|
|
3343
3388
|
const cursor = await tryCli("cursor", folderUri);
|
|
3344
3389
|
if (cursor !== null) return cursor;
|
|
3345
|
-
|
|
3390
|
+
log10.warn("neither `code` nor `cursor` found in PATH; falling back to `open vscode://...`");
|
|
3346
3391
|
return launchOne("vscode", folderUri);
|
|
3347
3392
|
}
|
|
3348
3393
|
async function tryCli(flavor, folderUri) {
|
|
@@ -3355,11 +3400,11 @@ async function launchOne(flavor, folderUri) {
|
|
|
3355
3400
|
const profile = ideProfile(flavor);
|
|
3356
3401
|
const cliCode = await spawnCommand(profile.cli, ["--folder-uri", folderUri]);
|
|
3357
3402
|
if (cliCode !== 127) return { code: cliCode, flavor, via: "cli" };
|
|
3358
|
-
|
|
3359
|
-
`\`${profile.cli}\` not found in PATH; falling back to
|
|
3403
|
+
log10.warn(
|
|
3404
|
+
`\`${profile.cli}\` not found in PATH; falling back to \`${hostOpenCommand()} ${profile.protocolScheme}://...\` (the %2B URL-encoding bug may break attach)`
|
|
3360
3405
|
);
|
|
3361
3406
|
const url = `${profile.protocolScheme}://${folderUri.replace(/^vscode-remote:\/\//, "vscode-remote/")}`;
|
|
3362
|
-
const fallback = await spawnCommand(
|
|
3407
|
+
const fallback = await spawnCommand(hostOpenCommand(), [url]);
|
|
3363
3408
|
return { code: fallback, flavor, via: "open" };
|
|
3364
3409
|
}
|
|
3365
3410
|
function spawnCommand(cmd, args) {
|
|
@@ -3384,9 +3429,9 @@ async function fetchServiceNamesDocker(container) {
|
|
|
3384
3429
|
|
|
3385
3430
|
// src/commands/codex.ts
|
|
3386
3431
|
import { access } from "fs/promises";
|
|
3387
|
-
import { homedir as
|
|
3432
|
+
import { homedir as homedir9 } from "os";
|
|
3388
3433
|
import { join as join10 } from "path";
|
|
3389
|
-
import { confirm as confirm5, intro as intro2, isCancel as isCancel6, log as
|
|
3434
|
+
import { confirm as confirm5, intro as intro2, isCancel as isCancel6, log as log11, outro as outro2, spinner as spinner4 } from "@clack/prompts";
|
|
3390
3435
|
import { Command as Command5 } from "commander";
|
|
3391
3436
|
function reattachRef2(r) {
|
|
3392
3437
|
return typeof r.projectIndex === "number" ? String(r.projectIndex) : r.name;
|
|
@@ -3463,7 +3508,7 @@ async function maybeRunCodexLogin(args) {
|
|
|
3463
3508
|
initialValue: true
|
|
3464
3509
|
});
|
|
3465
3510
|
if (isCancel6(answer) || !answer) {
|
|
3466
|
-
|
|
3511
|
+
log11.info("Skipped sign-in \u2014 codex will prompt you to sign in inside the box.");
|
|
3467
3512
|
return;
|
|
3468
3513
|
}
|
|
3469
3514
|
const s = spinner4();
|
|
@@ -3474,14 +3519,14 @@ async function maybeRunCodexLogin(args) {
|
|
|
3474
3519
|
s.stop("image ready");
|
|
3475
3520
|
const exitCode = await runCodexLoginContainer(args.image, []);
|
|
3476
3521
|
if (exitCode !== 0) {
|
|
3477
|
-
|
|
3522
|
+
log11.warn("Codex login did not complete; continuing \u2014 run `agentbox codex login` to retry.");
|
|
3478
3523
|
return;
|
|
3479
3524
|
}
|
|
3480
|
-
|
|
3525
|
+
log11.success("Signed in to Codex \u2014 saved for future boxes.");
|
|
3481
3526
|
}
|
|
3482
3527
|
async function cloudCodexCredAvailable(env = process.env) {
|
|
3483
3528
|
if ((env["OPENAI_API_KEY"] ?? "").length > 0) return true;
|
|
3484
|
-
for (const p of [CODEX_CREDENTIALS_BACKUP_FILE, join10(
|
|
3529
|
+
for (const p of [CODEX_CREDENTIALS_BACKUP_FILE, join10(homedir9(), ".codex", "auth.json")]) {
|
|
3485
3530
|
try {
|
|
3486
3531
|
await access(p);
|
|
3487
3532
|
return true;
|
|
@@ -3498,7 +3543,7 @@ async function maybeRunCloudCodexLogin(args) {
|
|
|
3498
3543
|
initialValue: true
|
|
3499
3544
|
});
|
|
3500
3545
|
if (isCancel6(answer) || !answer) {
|
|
3501
|
-
|
|
3546
|
+
log11.info("Skipped sign-in \u2014 codex will prompt you to sign in inside the box.");
|
|
3502
3547
|
return;
|
|
3503
3548
|
}
|
|
3504
3549
|
const s = spinner4();
|
|
@@ -3509,12 +3554,12 @@ async function maybeRunCloudCodexLogin(args) {
|
|
|
3509
3554
|
s.stop("image ready");
|
|
3510
3555
|
const exitCode = await runCodexLoginContainer(args.image, []);
|
|
3511
3556
|
if (exitCode !== 0) {
|
|
3512
|
-
|
|
3557
|
+
log11.warn("Codex login did not complete; continuing \u2014 run `agentbox codex login` to retry.");
|
|
3513
3558
|
return;
|
|
3514
3559
|
}
|
|
3515
3560
|
const { copied } = await extractCodexCredentials(SHARED_CODEX_VOLUME, args.image);
|
|
3516
|
-
if (copied)
|
|
3517
|
-
else
|
|
3561
|
+
if (copied) log11.success("Signed in to Codex \u2014 saved for future boxes.");
|
|
3562
|
+
else log11.warn("Codex login finished but no auth.json was captured \u2014 sign in inside the box if needed.");
|
|
3518
3563
|
}
|
|
3519
3564
|
var codexCommand = new Command5("codex").description("Create a sandboxed box and launch OpenAI Codex in a detachable tmux session").option("-w, --workspace <path>", "host workspace to mount", process.cwd()).option("-n, --name <name>", "friendly box name (default: <workspace-basename>-<id>)").option("--host-snapshot", "APFS-clone the host workspace into a per-box scratch dir before seeding /workspace (stabilizes the tar-pipe source)").option("--no-host-snapshot", "tar-pipe directly from the live host workspace at create time").option(
|
|
3520
3565
|
"--snapshot <ref>",
|
|
@@ -3581,14 +3626,14 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3581
3626
|
intro2("Starting Codex in a box...");
|
|
3582
3627
|
let resumeMode = null;
|
|
3583
3628
|
if (opts.continue === true && opts.resume) {
|
|
3584
|
-
|
|
3629
|
+
log11.error("only one of -c / --continue / --resume can be passed");
|
|
3585
3630
|
cmdLog.close();
|
|
3586
3631
|
process.exit(2);
|
|
3587
3632
|
}
|
|
3588
3633
|
if (opts.continue === true) resumeMode = { kind: "continue" };
|
|
3589
3634
|
else if (opts.resume) resumeMode = { kind: "resume", id: opts.resume };
|
|
3590
3635
|
if (resumeMode && opts.initialPrompt && opts.initialPrompt.length > 0) {
|
|
3591
|
-
|
|
3636
|
+
log11.error("-i / --initial-prompt cannot be combined with -c / --resume.");
|
|
3592
3637
|
cmdLog.close();
|
|
3593
3638
|
process.exit(2);
|
|
3594
3639
|
}
|
|
@@ -3603,7 +3648,7 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3603
3648
|
});
|
|
3604
3649
|
} catch (err) {
|
|
3605
3650
|
if (err instanceof TeleportError) {
|
|
3606
|
-
|
|
3651
|
+
log11.error(err.message);
|
|
3607
3652
|
cmdLog.close();
|
|
3608
3653
|
process.exit(2);
|
|
3609
3654
|
}
|
|
@@ -3620,7 +3665,7 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3620
3665
|
const checkpointRef = opts.snapshot && opts.snapshot.length > 0 ? opts.snapshot : providerDefault.length > 0 ? providerDefault : void 0;
|
|
3621
3666
|
if (opts.initialPrompt && opts.initialPrompt.length > 0) {
|
|
3622
3667
|
if (isCloud) {
|
|
3623
|
-
|
|
3668
|
+
log11.error("-i / --initial-prompt is currently docker-only (cloud sessions only start on attach).");
|
|
3624
3669
|
cmdLog.close();
|
|
3625
3670
|
process.exit(2);
|
|
3626
3671
|
}
|
|
@@ -3631,7 +3676,7 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3631
3676
|
});
|
|
3632
3677
|
} catch (err) {
|
|
3633
3678
|
if (err instanceof MissingAgentCredsError) {
|
|
3634
|
-
|
|
3679
|
+
log11.error(err.message);
|
|
3635
3680
|
cmdLog.close();
|
|
3636
3681
|
process.exit(2);
|
|
3637
3682
|
}
|
|
@@ -3649,7 +3694,7 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3649
3694
|
maxRunningOverride,
|
|
3650
3695
|
maxWorkingOverride
|
|
3651
3696
|
});
|
|
3652
|
-
|
|
3697
|
+
outro2(
|
|
3653
3698
|
`job ${result.job.id} queued (${String(result.runningCount)}/${String(result.maxConcurrent)} running); log: ${result.job.logPath}`
|
|
3654
3699
|
);
|
|
3655
3700
|
cmdLog.close();
|
|
@@ -3665,13 +3710,13 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3665
3710
|
onLog: (line) => cmdLog.write(line)
|
|
3666
3711
|
});
|
|
3667
3712
|
if (gate.decision === "cancel") {
|
|
3668
|
-
|
|
3713
|
+
log11.warn("carry: cancelled \u2014 not creating the box");
|
|
3669
3714
|
cmdLog.close();
|
|
3670
3715
|
process.exit(0);
|
|
3671
3716
|
}
|
|
3672
3717
|
if (gate.decision === "approve") carryEntries = gate.entries;
|
|
3673
3718
|
} catch (err) {
|
|
3674
|
-
|
|
3719
|
+
log11.error(err instanceof Error ? err.message : String(err));
|
|
3675
3720
|
cmdLog.close();
|
|
3676
3721
|
process.exit(1);
|
|
3677
3722
|
}
|
|
@@ -3688,7 +3733,7 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3688
3733
|
}));
|
|
3689
3734
|
} catch (err) {
|
|
3690
3735
|
if (err instanceof FromBranchError || err instanceof UseBranchError) {
|
|
3691
|
-
|
|
3736
|
+
log11.error(err.message);
|
|
3692
3737
|
cmdLog.close();
|
|
3693
3738
|
process.exit(2);
|
|
3694
3739
|
}
|
|
@@ -3732,7 +3777,7 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3732
3777
|
return { agentArgsPrefix: resumePrepared.forwardArgs };
|
|
3733
3778
|
} catch (err) {
|
|
3734
3779
|
if (err instanceof TeleportError) {
|
|
3735
|
-
|
|
3780
|
+
log11.error(err.message);
|
|
3736
3781
|
cmdLog.close();
|
|
3737
3782
|
process.exit(2);
|
|
3738
3783
|
}
|
|
@@ -3807,8 +3852,8 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3807
3852
|
} catch (err) {
|
|
3808
3853
|
if (err instanceof TeleportError) {
|
|
3809
3854
|
s.stop("teleport failed");
|
|
3810
|
-
|
|
3811
|
-
|
|
3855
|
+
log11.error(err.message);
|
|
3856
|
+
log11.info(
|
|
3812
3857
|
`The box ${result.record.container} is up but unused. Destroy it with: agentbox destroy ${result.record.container} -y`
|
|
3813
3858
|
);
|
|
3814
3859
|
cmdLog.close();
|
|
@@ -3824,14 +3869,20 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3824
3869
|
sessionName
|
|
3825
3870
|
});
|
|
3826
3871
|
const nSuffix = typeof result.record.projectIndex === "number" ? ` \xB7 n ${String(result.record.projectIndex)}` : "";
|
|
3827
|
-
s.stop(`box
|
|
3872
|
+
s.stop(`box ready${nSuffix}`);
|
|
3873
|
+
await printLaunchRecap({
|
|
3874
|
+
record: result.record,
|
|
3875
|
+
mode: "codex",
|
|
3876
|
+
reattach: reattachRef2(result.record),
|
|
3877
|
+
workspacePath: opts.workspace,
|
|
3878
|
+
fromBranch,
|
|
3879
|
+
useBranch,
|
|
3880
|
+
checkpointRef,
|
|
3881
|
+
attaching: opts.attach !== false
|
|
3882
|
+
});
|
|
3828
3883
|
if (opts.attach === false) {
|
|
3829
|
-
outro3(
|
|
3830
|
-
`session started \u2014 attach with: agentbox codex attach ${reattachRef2(result.record)}`
|
|
3831
|
-
);
|
|
3832
3884
|
return;
|
|
3833
3885
|
}
|
|
3834
|
-
outro3("attaching \u2014 Control+a d to detach, leaves codex running");
|
|
3835
3886
|
await attachCodexWrapped(
|
|
3836
3887
|
result.record,
|
|
3837
3888
|
sessionName,
|
|
@@ -3843,10 +3894,10 @@ var codexCommand = new Command5("codex").description("Create a sandboxed box and
|
|
|
3843
3894
|
s.stop("failed");
|
|
3844
3895
|
cmdLog.write(`FAIL: ${err instanceof Error ? err.stack ?? err.message : String(err)}`);
|
|
3845
3896
|
if (err instanceof CodexSessionError) {
|
|
3846
|
-
|
|
3897
|
+
log11.error(err.message);
|
|
3847
3898
|
if (containerName) {
|
|
3848
|
-
|
|
3849
|
-
|
|
3899
|
+
log11.info(`The box ${containerName} is still running. Destroy it with:`);
|
|
3900
|
+
log11.info(` agentbox destroy ${containerName} -y`);
|
|
3850
3901
|
}
|
|
3851
3902
|
cmdLog.close();
|
|
3852
3903
|
process.exit(1);
|
|
@@ -3883,12 +3934,12 @@ async function startOrAttachCodex(box, codexArgs, opts, resumePrepared) {
|
|
|
3883
3934
|
);
|
|
3884
3935
|
}
|
|
3885
3936
|
if (!wantAttach) {
|
|
3886
|
-
|
|
3937
|
+
outro2(
|
|
3887
3938
|
`session "${sessionName}" already running \u2014 attach with: agentbox codex attach ${reattachRef2(box)}`
|
|
3888
3939
|
);
|
|
3889
3940
|
return;
|
|
3890
3941
|
}
|
|
3891
|
-
|
|
3942
|
+
outro2(`session "${sessionName}" already running \u2014 attaching (Control+a d to detach)`);
|
|
3892
3943
|
await attachCodexWrapped(box, sessionName, reattachRef2(box), void 0, openIn);
|
|
3893
3944
|
return;
|
|
3894
3945
|
}
|
|
@@ -3932,7 +3983,7 @@ async function startOrAttachCodex(box, codexArgs, opts, resumePrepared) {
|
|
|
3932
3983
|
} catch (err) {
|
|
3933
3984
|
if (err instanceof TeleportError) {
|
|
3934
3985
|
s.stop("teleport failed");
|
|
3935
|
-
|
|
3986
|
+
log11.error(err.message);
|
|
3936
3987
|
process.exit(2);
|
|
3937
3988
|
}
|
|
3938
3989
|
throw err;
|
|
@@ -3942,12 +3993,12 @@ async function startOrAttachCodex(box, codexArgs, opts, resumePrepared) {
|
|
|
3942
3993
|
await startCodexSession({ container: box.container, codexArgs: effectiveArgs, sessionName });
|
|
3943
3994
|
s.stop(`box ${box.container} ready`);
|
|
3944
3995
|
if (!wantAttach) {
|
|
3945
|
-
|
|
3996
|
+
outro2(
|
|
3946
3997
|
`session "${sessionName}" started \u2014 attach with: agentbox codex attach ${reattachRef2(box)}`
|
|
3947
3998
|
);
|
|
3948
3999
|
return;
|
|
3949
4000
|
}
|
|
3950
|
-
|
|
4001
|
+
outro2("attaching \u2014 Control+a d to detach, leaves codex running");
|
|
3951
4002
|
await attachCodexWrapped(box, sessionName, reattachRef2(box), void 0, openIn);
|
|
3952
4003
|
}
|
|
3953
4004
|
var codexAttachCommand = new Command5("attach").description(
|
|
@@ -3977,7 +4028,7 @@ var codexAttachCommand = new Command5("attach").description(
|
|
|
3977
4028
|
await startOrAttachCodex(box, [], { ...opts, syncConfig: false });
|
|
3978
4029
|
} catch (err) {
|
|
3979
4030
|
if (err instanceof CodexSessionError) {
|
|
3980
|
-
|
|
4031
|
+
log11.error(err.message);
|
|
3981
4032
|
process.exit(1);
|
|
3982
4033
|
}
|
|
3983
4034
|
handleLifecycleError(err);
|
|
@@ -4009,7 +4060,7 @@ var codexStartCommand = new Command5("start").description(
|
|
|
4009
4060
|
let effectiveCodexArgs = shifted && idOrName ? [idOrName, ...codexArgs] : codexArgs;
|
|
4010
4061
|
let resumeMode = null;
|
|
4011
4062
|
if (opts.continue === true && opts.resume) {
|
|
4012
|
-
|
|
4063
|
+
log11.error("only one of -c / --continue / --resume can be passed");
|
|
4013
4064
|
process.exit(2);
|
|
4014
4065
|
}
|
|
4015
4066
|
if (opts.continue === true) resumeMode = { kind: "continue" };
|
|
@@ -4024,7 +4075,7 @@ var codexStartCommand = new Command5("start").description(
|
|
|
4024
4075
|
});
|
|
4025
4076
|
} catch (err) {
|
|
4026
4077
|
if (err instanceof TeleportError) {
|
|
4027
|
-
|
|
4078
|
+
log11.error(err.message);
|
|
4028
4079
|
process.exit(2);
|
|
4029
4080
|
}
|
|
4030
4081
|
throw err;
|
|
@@ -4032,7 +4083,7 @@ var codexStartCommand = new Command5("start").description(
|
|
|
4032
4083
|
}
|
|
4033
4084
|
if ((box.provider ?? "docker") !== "docker") {
|
|
4034
4085
|
if (opts.attach === false) {
|
|
4035
|
-
|
|
4086
|
+
outro2(
|
|
4036
4087
|
`--no-attach: cloud agent sessions are started lazily on attach. Run: agentbox codex attach ${reattachRef2(box)}`
|
|
4037
4088
|
);
|
|
4038
4089
|
return;
|
|
@@ -4051,7 +4102,7 @@ var codexStartCommand = new Command5("start").description(
|
|
|
4051
4102
|
effectiveCodexArgs = [...resumePrepared.forwardArgs, ...effectiveCodexArgs];
|
|
4052
4103
|
} catch (err) {
|
|
4053
4104
|
if (err instanceof TeleportError) {
|
|
4054
|
-
|
|
4105
|
+
log11.error(err.message);
|
|
4055
4106
|
process.exit(2);
|
|
4056
4107
|
}
|
|
4057
4108
|
throw err;
|
|
@@ -4070,7 +4121,7 @@ var codexStartCommand = new Command5("start").description(
|
|
|
4070
4121
|
await startOrAttachCodex(box, effectiveCodexArgs, opts, resumePrepared);
|
|
4071
4122
|
} catch (err) {
|
|
4072
4123
|
if (err instanceof CodexSessionError) {
|
|
4073
|
-
|
|
4124
|
+
log11.error(err.message);
|
|
4074
4125
|
process.exit(1);
|
|
4075
4126
|
}
|
|
4076
4127
|
handleLifecycleError(err);
|
|
@@ -4084,7 +4135,7 @@ var codexLoginCommand = new Command5("login").description(
|
|
|
4084
4135
|
).action(async (args) => {
|
|
4085
4136
|
intro2("Signing in to Codex...");
|
|
4086
4137
|
if (!process.stdin.isTTY) {
|
|
4087
|
-
|
|
4138
|
+
log11.error("`agentbox codex login` needs an interactive terminal.");
|
|
4088
4139
|
process.exit(1);
|
|
4089
4140
|
}
|
|
4090
4141
|
try {
|
|
@@ -4098,10 +4149,10 @@ var codexLoginCommand = new Command5("login").description(
|
|
|
4098
4149
|
s.stop("image ready");
|
|
4099
4150
|
const exitCode = await runCodexLoginContainer(image, args);
|
|
4100
4151
|
if (exitCode !== 0) {
|
|
4101
|
-
|
|
4152
|
+
log11.warn(`\`codex login\` exited with code ${String(exitCode)}`);
|
|
4102
4153
|
process.exit(exitCode);
|
|
4103
4154
|
}
|
|
4104
|
-
|
|
4155
|
+
outro2("signed in \u2014 credentials saved for future boxes");
|
|
4105
4156
|
} catch (err) {
|
|
4106
4157
|
handleLifecycleError(err);
|
|
4107
4158
|
}
|
|
@@ -4112,9 +4163,9 @@ codexCommand.addCommand(codexLoginCommand);
|
|
|
4112
4163
|
|
|
4113
4164
|
// src/commands/opencode.ts
|
|
4114
4165
|
import { access as access2 } from "fs/promises";
|
|
4115
|
-
import { homedir as
|
|
4166
|
+
import { homedir as homedir10 } from "os";
|
|
4116
4167
|
import { join as join11 } from "path";
|
|
4117
|
-
import { confirm as confirm6, intro as intro3, isCancel as isCancel7, log as
|
|
4168
|
+
import { confirm as confirm6, intro as intro3, isCancel as isCancel7, log as log12, outro as outro3, spinner as spinner5 } from "@clack/prompts";
|
|
4118
4169
|
import { Command as Command6 } from "commander";
|
|
4119
4170
|
function reattachRef3(r) {
|
|
4120
4171
|
return typeof r.projectIndex === "number" ? String(r.projectIndex) : r.name;
|
|
@@ -4188,7 +4239,7 @@ async function maybeRunOpencodeLogin(args) {
|
|
|
4188
4239
|
initialValue: true
|
|
4189
4240
|
});
|
|
4190
4241
|
if (isCancel7(answer) || !answer) {
|
|
4191
|
-
|
|
4242
|
+
log12.info("Skipped sign-in \u2014 opencode will prompt you to sign in inside the box.");
|
|
4192
4243
|
return;
|
|
4193
4244
|
}
|
|
4194
4245
|
const s = spinner5();
|
|
@@ -4202,16 +4253,16 @@ async function maybeRunOpencodeLogin(args) {
|
|
|
4202
4253
|
s.stop("image ready");
|
|
4203
4254
|
const exitCode = await runOpencodeLoginContainer(args.image, []);
|
|
4204
4255
|
if (exitCode !== 0) {
|
|
4205
|
-
|
|
4256
|
+
log12.warn("OpenCode login did not complete; continuing \u2014 run `agentbox opencode login` to retry.");
|
|
4206
4257
|
return;
|
|
4207
4258
|
}
|
|
4208
|
-
|
|
4259
|
+
log12.success("Signed in to OpenCode \u2014 saved for future boxes.");
|
|
4209
4260
|
}
|
|
4210
4261
|
async function cloudOpencodeCredAvailable(env = process.env) {
|
|
4211
4262
|
for (const k of OPENCODE_FORWARDED_ENV_KEYS) {
|
|
4212
4263
|
if ((env[k] ?? "").length > 0) return true;
|
|
4213
4264
|
}
|
|
4214
|
-
for (const p of [OPENCODE_CREDENTIALS_BACKUP_FILE, join11(
|
|
4265
|
+
for (const p of [OPENCODE_CREDENTIALS_BACKUP_FILE, join11(homedir10(), ".local", "share", "opencode", "auth.json")]) {
|
|
4215
4266
|
try {
|
|
4216
4267
|
await access2(p);
|
|
4217
4268
|
return true;
|
|
@@ -4228,7 +4279,7 @@ async function maybeRunCloudOpencodeLogin(args) {
|
|
|
4228
4279
|
initialValue: true
|
|
4229
4280
|
});
|
|
4230
4281
|
if (isCancel7(answer) || !answer) {
|
|
4231
|
-
|
|
4282
|
+
log12.info("Skipped sign-in \u2014 opencode will prompt you to sign in inside the box.");
|
|
4232
4283
|
return;
|
|
4233
4284
|
}
|
|
4234
4285
|
const s = spinner5();
|
|
@@ -4242,12 +4293,12 @@ async function maybeRunCloudOpencodeLogin(args) {
|
|
|
4242
4293
|
s.stop("image ready");
|
|
4243
4294
|
const exitCode = await runOpencodeLoginContainer(args.image, []);
|
|
4244
4295
|
if (exitCode !== 0) {
|
|
4245
|
-
|
|
4296
|
+
log12.warn("OpenCode login did not complete; continuing \u2014 run `agentbox opencode login` to retry.");
|
|
4246
4297
|
return;
|
|
4247
4298
|
}
|
|
4248
4299
|
const { copied } = await extractOpencodeCredentials(SHARED_OPENCODE_VOLUME, args.image);
|
|
4249
|
-
if (copied)
|
|
4250
|
-
else
|
|
4300
|
+
if (copied) log12.success("Signed in to OpenCode \u2014 saved for future boxes.");
|
|
4301
|
+
else log12.warn("OpenCode login finished but no auth.json was captured \u2014 sign in inside the box if needed.");
|
|
4251
4302
|
}
|
|
4252
4303
|
var opencodeCommand = new Command6("opencode").description("Create a sandboxed box and launch OpenCode in a detachable tmux session").option("-w, --workspace <path>", "host workspace to mount", process.cwd()).option("-n, --name <name>", "friendly box name (default: <workspace-basename>-<id>)").option("--host-snapshot", "APFS-clone the host workspace into a per-box scratch dir before seeding /workspace (stabilizes the tar-pipe source)").option("--no-host-snapshot", "tar-pipe directly from the live host workspace at create time").option(
|
|
4253
4304
|
"--snapshot <ref>",
|
|
@@ -4315,7 +4366,7 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
4315
4366
|
});
|
|
4316
4367
|
} catch (err) {
|
|
4317
4368
|
if (err instanceof TeleportError) {
|
|
4318
|
-
|
|
4369
|
+
log12.error(err.message);
|
|
4319
4370
|
cmdLog.close();
|
|
4320
4371
|
process.exit(2);
|
|
4321
4372
|
}
|
|
@@ -4332,7 +4383,7 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
4332
4383
|
const checkpointRef = opts.snapshot && opts.snapshot.length > 0 ? opts.snapshot : providerDefault.length > 0 ? providerDefault : void 0;
|
|
4333
4384
|
if (opts.initialPrompt && opts.initialPrompt.length > 0) {
|
|
4334
4385
|
if (isCloud) {
|
|
4335
|
-
|
|
4386
|
+
log12.error("-i / --initial-prompt is currently docker-only (cloud sessions only start on attach).");
|
|
4336
4387
|
cmdLog.close();
|
|
4337
4388
|
process.exit(2);
|
|
4338
4389
|
}
|
|
@@ -4343,7 +4394,7 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
4343
4394
|
});
|
|
4344
4395
|
} catch (err) {
|
|
4345
4396
|
if (err instanceof MissingAgentCredsError) {
|
|
4346
|
-
|
|
4397
|
+
log12.error(err.message);
|
|
4347
4398
|
cmdLog.close();
|
|
4348
4399
|
process.exit(2);
|
|
4349
4400
|
}
|
|
@@ -4361,7 +4412,7 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
4361
4412
|
maxRunningOverride,
|
|
4362
4413
|
maxWorkingOverride
|
|
4363
4414
|
});
|
|
4364
|
-
|
|
4415
|
+
outro3(
|
|
4365
4416
|
`job ${result.job.id} queued (${String(result.runningCount)}/${String(result.maxConcurrent)} running); log: ${result.job.logPath}`
|
|
4366
4417
|
);
|
|
4367
4418
|
cmdLog.close();
|
|
@@ -4377,13 +4428,13 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
4377
4428
|
onLog: (line) => cmdLog.write(line)
|
|
4378
4429
|
});
|
|
4379
4430
|
if (gate.decision === "cancel") {
|
|
4380
|
-
|
|
4431
|
+
log12.warn("carry: cancelled \u2014 not creating the box");
|
|
4381
4432
|
cmdLog.close();
|
|
4382
4433
|
process.exit(0);
|
|
4383
4434
|
}
|
|
4384
4435
|
if (gate.decision === "approve") carryEntries = gate.entries;
|
|
4385
4436
|
} catch (err) {
|
|
4386
|
-
|
|
4437
|
+
log12.error(err instanceof Error ? err.message : String(err));
|
|
4387
4438
|
cmdLog.close();
|
|
4388
4439
|
process.exit(1);
|
|
4389
4440
|
}
|
|
@@ -4400,7 +4451,7 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
4400
4451
|
}));
|
|
4401
4452
|
} catch (err) {
|
|
4402
4453
|
if (err instanceof FromBranchError || err instanceof UseBranchError) {
|
|
4403
|
-
|
|
4454
|
+
log12.error(err.message);
|
|
4404
4455
|
cmdLog.close();
|
|
4405
4456
|
process.exit(2);
|
|
4406
4457
|
}
|
|
@@ -4489,14 +4540,20 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
4489
4540
|
sessionName
|
|
4490
4541
|
});
|
|
4491
4542
|
const nSuffix = typeof result.record.projectIndex === "number" ? ` \xB7 n ${String(result.record.projectIndex)}` : "";
|
|
4492
|
-
s.stop(`box
|
|
4543
|
+
s.stop(`box ready${nSuffix}`);
|
|
4544
|
+
await printLaunchRecap({
|
|
4545
|
+
record: result.record,
|
|
4546
|
+
mode: "opencode",
|
|
4547
|
+
reattach: reattachRef3(result.record),
|
|
4548
|
+
workspacePath: opts.workspace,
|
|
4549
|
+
fromBranch,
|
|
4550
|
+
useBranch,
|
|
4551
|
+
checkpointRef,
|
|
4552
|
+
attaching: opts.attach !== false
|
|
4553
|
+
});
|
|
4493
4554
|
if (opts.attach === false) {
|
|
4494
|
-
outro4(
|
|
4495
|
-
`session started \u2014 attach with: agentbox opencode attach ${reattachRef3(result.record)}`
|
|
4496
|
-
);
|
|
4497
4555
|
return;
|
|
4498
4556
|
}
|
|
4499
|
-
outro4("attaching \u2014 Control+a d to detach, leaves opencode running");
|
|
4500
4557
|
await attachOpencodeWrapped(
|
|
4501
4558
|
result.record,
|
|
4502
4559
|
sessionName,
|
|
@@ -4508,10 +4565,10 @@ var opencodeCommand = new Command6("opencode").description("Create a sandboxed b
|
|
|
4508
4565
|
s.stop("failed");
|
|
4509
4566
|
cmdLog.write(`FAIL: ${err instanceof Error ? err.stack ?? err.message : String(err)}`);
|
|
4510
4567
|
if (err instanceof OpencodeSessionError) {
|
|
4511
|
-
|
|
4568
|
+
log12.error(err.message);
|
|
4512
4569
|
if (containerName) {
|
|
4513
|
-
|
|
4514
|
-
|
|
4570
|
+
log12.info(`The box ${containerName} is still running. Destroy it with:`);
|
|
4571
|
+
log12.info(` agentbox destroy ${containerName} -y`);
|
|
4515
4572
|
}
|
|
4516
4573
|
cmdLog.close();
|
|
4517
4574
|
process.exit(1);
|
|
@@ -4537,12 +4594,12 @@ async function startOrAttachOpencode(box, opencodeArgs, opts) {
|
|
|
4537
4594
|
const existing = await opencodeSessionInfo(box.container, sessionName);
|
|
4538
4595
|
if (existing.running) {
|
|
4539
4596
|
if (!wantAttach) {
|
|
4540
|
-
|
|
4597
|
+
outro3(
|
|
4541
4598
|
`session "${sessionName}" already running \u2014 attach with: agentbox opencode attach ${reattachRef3(box)}`
|
|
4542
4599
|
);
|
|
4543
4600
|
return;
|
|
4544
4601
|
}
|
|
4545
|
-
|
|
4602
|
+
outro3(`session "${sessionName}" already running \u2014 attaching (Control+a d to detach)`);
|
|
4546
4603
|
await attachOpencodeWrapped(box, sessionName, reattachRef3(box), void 0, openIn);
|
|
4547
4604
|
return;
|
|
4548
4605
|
}
|
|
@@ -4572,12 +4629,12 @@ async function startOrAttachOpencode(box, opencodeArgs, opts) {
|
|
|
4572
4629
|
await startOpencodeSession({ container: box.container, opencodeArgs, sessionName });
|
|
4573
4630
|
s.stop(`box ${box.container} ready`);
|
|
4574
4631
|
if (!wantAttach) {
|
|
4575
|
-
|
|
4632
|
+
outro3(
|
|
4576
4633
|
`session "${sessionName}" started \u2014 attach with: agentbox opencode attach ${reattachRef3(box)}`
|
|
4577
4634
|
);
|
|
4578
4635
|
return;
|
|
4579
4636
|
}
|
|
4580
|
-
|
|
4637
|
+
outro3("attaching \u2014 Control+a d to detach, leaves opencode running");
|
|
4581
4638
|
await attachOpencodeWrapped(box, sessionName, reattachRef3(box), void 0, openIn);
|
|
4582
4639
|
}
|
|
4583
4640
|
var opencodeAttachCommand = new Command6("attach").description(
|
|
@@ -4607,7 +4664,7 @@ var opencodeAttachCommand = new Command6("attach").description(
|
|
|
4607
4664
|
await startOrAttachOpencode(box, [], { ...opts, syncConfig: false });
|
|
4608
4665
|
} catch (err) {
|
|
4609
4666
|
if (err instanceof OpencodeSessionError) {
|
|
4610
|
-
|
|
4667
|
+
log12.error(err.message);
|
|
4611
4668
|
process.exit(1);
|
|
4612
4669
|
}
|
|
4613
4670
|
handleLifecycleError(err);
|
|
@@ -4646,7 +4703,7 @@ var opencodeStartCommand = new Command6("start").description(
|
|
|
4646
4703
|
});
|
|
4647
4704
|
} catch (err) {
|
|
4648
4705
|
if (err instanceof TeleportError) {
|
|
4649
|
-
|
|
4706
|
+
log12.error(err.message);
|
|
4650
4707
|
process.exit(2);
|
|
4651
4708
|
}
|
|
4652
4709
|
throw err;
|
|
@@ -4654,7 +4711,7 @@ var opencodeStartCommand = new Command6("start").description(
|
|
|
4654
4711
|
}
|
|
4655
4712
|
if ((box.provider ?? "docker") !== "docker") {
|
|
4656
4713
|
if (opts.attach === false) {
|
|
4657
|
-
|
|
4714
|
+
outro3(
|
|
4658
4715
|
`--no-attach: cloud agent sessions are started lazily on attach. Run: agentbox opencode attach ${reattachRef3(box)}`
|
|
4659
4716
|
);
|
|
4660
4717
|
return;
|
|
@@ -4675,7 +4732,7 @@ var opencodeStartCommand = new Command6("start").description(
|
|
|
4675
4732
|
await startOrAttachOpencode(box, effectiveOpencodeArgs, opts);
|
|
4676
4733
|
} catch (err) {
|
|
4677
4734
|
if (err instanceof OpencodeSessionError) {
|
|
4678
|
-
|
|
4735
|
+
log12.error(err.message);
|
|
4679
4736
|
process.exit(1);
|
|
4680
4737
|
}
|
|
4681
4738
|
handleLifecycleError(err);
|
|
@@ -4689,7 +4746,7 @@ var opencodeLoginCommand = new Command6("login").description(
|
|
|
4689
4746
|
).action(async (args) => {
|
|
4690
4747
|
intro3("Signing in to OpenCode...");
|
|
4691
4748
|
if (!process.stdin.isTTY) {
|
|
4692
|
-
|
|
4749
|
+
log12.error("`agentbox opencode login` needs an interactive terminal.");
|
|
4693
4750
|
process.exit(1);
|
|
4694
4751
|
}
|
|
4695
4752
|
try {
|
|
@@ -4703,10 +4760,10 @@ var opencodeLoginCommand = new Command6("login").description(
|
|
|
4703
4760
|
s.stop("image ready");
|
|
4704
4761
|
const exitCode = await runOpencodeLoginContainer(image, args);
|
|
4705
4762
|
if (exitCode !== 0) {
|
|
4706
|
-
|
|
4763
|
+
log12.warn(`\`opencode auth login\` exited with code ${String(exitCode)}`);
|
|
4707
4764
|
process.exit(exitCode);
|
|
4708
4765
|
}
|
|
4709
|
-
|
|
4766
|
+
outro3("signed in \u2014 credentials saved for future boxes");
|
|
4710
4767
|
} catch (err) {
|
|
4711
4768
|
handleLifecycleError(err);
|
|
4712
4769
|
}
|
|
@@ -4982,7 +5039,7 @@ function handleError(err) {
|
|
|
4982
5039
|
var configCommand = new Command7("config").description("Read / write layered config (global, per-project, workspace `defaults:` block)").addCommand(getCommand).addCommand(setCommand).addCommand(unsetCommand).addCommand(listCommand).addCommand(pathCommand).addCommand(editCommand).addCommand(listProjectsCommand);
|
|
4983
5040
|
|
|
4984
5041
|
// src/commands/cp.ts
|
|
4985
|
-
import { log as
|
|
5042
|
+
import { log as log13 } from "@clack/prompts";
|
|
4986
5043
|
import { Command as Command8 } from "commander";
|
|
4987
5044
|
function parseBoxArg(arg) {
|
|
4988
5045
|
const idx = arg.indexOf(":");
|
|
@@ -5065,10 +5122,10 @@ var cpCommand = new Command8("cp").description("Copy files between host and box
|
|
|
5065
5122
|
}
|
|
5066
5123
|
const insp = await inspectBox(box.id);
|
|
5067
5124
|
if (insp.state === "paused") {
|
|
5068
|
-
|
|
5125
|
+
log13.info("box is paused; unpausing");
|
|
5069
5126
|
await unpauseBox(box.id);
|
|
5070
5127
|
} else if (insp.state === "stopped") {
|
|
5071
|
-
|
|
5128
|
+
log13.info("box is stopped; starting");
|
|
5072
5129
|
await startBox(box.id);
|
|
5073
5130
|
} else if (insp.state === "missing") {
|
|
5074
5131
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
@@ -5076,7 +5133,7 @@ var cpCommand = new Command8("cp").description("Copy files between host and box
|
|
|
5076
5133
|
if (parsed.direction === "upload") {
|
|
5077
5134
|
const result = await uploadToBox(box, parsed.hostPath, parsed.boxPath);
|
|
5078
5135
|
if (result.warn) {
|
|
5079
|
-
|
|
5136
|
+
log13.warn(`copied to ${box.name}:${result.finalPath}, but ${result.warn}`);
|
|
5080
5137
|
} else {
|
|
5081
5138
|
process.stdout.write(`copied to ${box.name}:${result.finalPath}
|
|
5082
5139
|
`);
|
|
@@ -5092,7 +5149,7 @@ var cpCommand = new Command8("cp").description("Copy files between host and box
|
|
|
5092
5149
|
});
|
|
5093
5150
|
|
|
5094
5151
|
// src/commands/create.ts
|
|
5095
|
-
import { intro as intro4, log as
|
|
5152
|
+
import { intro as intro4, log as log14, outro as outro4 } from "@clack/prompts";
|
|
5096
5153
|
import { Command as Command9 } from "commander";
|
|
5097
5154
|
import { execSync, spawnSync as spawnSync2 } from "child_process";
|
|
5098
5155
|
function buildCliOverrides(opts) {
|
|
@@ -5139,7 +5196,10 @@ async function attachShell(record) {
|
|
|
5139
5196
|
var createCommand = new Command9("create").description("Create and start a new agent box (Docker container with /workspace seeded via in-container git worktree)").option("-w, --workspace <path>", "host workspace to mount", process.cwd()).option("-n, --name <name>", "friendly box name (default: <workspace-basename>-<id>)").option("--provider <name>", "sandbox backend: 'docker' (default) or 'daytona' (cloud)").option("--host-snapshot", "APFS-clone the host workspace into a per-box scratch dir before seeding /workspace (stabilizes the tar-pipe source)").option("--no-host-snapshot", "bind the live workspace directly (host edits leak into reads)").option(
|
|
5140
5197
|
"--snapshot <ref>",
|
|
5141
5198
|
"start from a project checkpoint (see `agentbox checkpoint`); overrides box.defaultCheckpoint"
|
|
5142
|
-
).option("--image <ref>", "override the box image", void 0).option(
|
|
5199
|
+
).option("--image <ref>", "override the box image", void 0).option(
|
|
5200
|
+
"--build",
|
|
5201
|
+
"build the docker base image locally instead of pulling the prebuilt one from the registry"
|
|
5202
|
+
).option("--attach", "drop into a shell inside the box after it is ready").option("--with-playwright", "also install @playwright/cli@latest globally inside the box").option(
|
|
5143
5203
|
"--with-env",
|
|
5144
5204
|
"copy host env/config files (.env*, secrets.toml, agentbox.yaml, ...) into /workspace at create time (gitignore-bypassing)"
|
|
5145
5205
|
).option("--no-vnc", "disable the per-box Xvnc + noVNC web client (on by default)").option(
|
|
@@ -5212,13 +5272,13 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5212
5272
|
onLog: (line) => cmdLog.write(line)
|
|
5213
5273
|
});
|
|
5214
5274
|
if (gate.decision === "cancel") {
|
|
5215
|
-
|
|
5275
|
+
log14.warn("carry: cancelled \u2014 not creating the box");
|
|
5216
5276
|
cmdLog.close();
|
|
5217
5277
|
process.exit(0);
|
|
5218
5278
|
}
|
|
5219
5279
|
if (gate.decision === "approve") carryEntries = gate.entries;
|
|
5220
5280
|
} catch (err) {
|
|
5221
|
-
|
|
5281
|
+
log14.error(err instanceof Error ? err.message : String(err));
|
|
5222
5282
|
cmdLog.close();
|
|
5223
5283
|
process.exit(1);
|
|
5224
5284
|
}
|
|
@@ -5266,7 +5326,7 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5266
5326
|
} catch (err) {
|
|
5267
5327
|
if (err instanceof FromBranchError || err instanceof UseBranchError) {
|
|
5268
5328
|
s.stop("aborting: invalid branch selection");
|
|
5269
|
-
|
|
5329
|
+
log14.error(err.message);
|
|
5270
5330
|
cmdLog.close();
|
|
5271
5331
|
process.exit(2);
|
|
5272
5332
|
}
|
|
@@ -5277,6 +5337,8 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5277
5337
|
name: opts.name,
|
|
5278
5338
|
checkpointRef,
|
|
5279
5339
|
image: cfg.effective.box.image,
|
|
5340
|
+
allowPull: opts.build ? false : void 0,
|
|
5341
|
+
imageRegistry: cfg.effective.box.imageRegistry,
|
|
5280
5342
|
withPlaywright,
|
|
5281
5343
|
withEnv: cfg.effective.box.withEnv,
|
|
5282
5344
|
envFilesToImport: wiz.envFilesToImport,
|
|
@@ -5306,17 +5368,17 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5306
5368
|
}
|
|
5307
5369
|
});
|
|
5308
5370
|
s.stop(`box ${result.record.container} ready`);
|
|
5309
|
-
|
|
5371
|
+
log14.info(`id: ${result.record.id}`);
|
|
5310
5372
|
if (typeof result.record.projectIndex === "number") {
|
|
5311
|
-
|
|
5373
|
+
log14.info(`n: ${String(result.record.projectIndex)} (in ${projectRoot})`);
|
|
5312
5374
|
}
|
|
5313
|
-
|
|
5314
|
-
|
|
5375
|
+
log14.info(`container: ${result.record.container}`);
|
|
5376
|
+
log14.info(`image: ${result.record.image}${result.imageBuilt ? " (built just now)" : ""}`);
|
|
5315
5377
|
if (result.record.snapshotDir) {
|
|
5316
|
-
|
|
5378
|
+
log14.info(`snapshot: ${result.record.snapshotDir}`);
|
|
5317
5379
|
}
|
|
5318
5380
|
if (result.record.checkpointSource) {
|
|
5319
|
-
|
|
5381
|
+
log14.info(
|
|
5320
5382
|
`checkpoint: ${result.record.checkpointSource.ref} (${result.record.checkpointSource.type}) \u2192 ${result.record.checkpointImage ?? "(missing)"}`
|
|
5321
5383
|
);
|
|
5322
5384
|
}
|
|
@@ -5328,7 +5390,7 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5328
5390
|
` agentbox claude attach ${result.record.name}`,
|
|
5329
5391
|
` agentbox url ${result.record.name}`
|
|
5330
5392
|
];
|
|
5331
|
-
|
|
5393
|
+
log14.message(
|
|
5332
5394
|
[
|
|
5333
5395
|
"",
|
|
5334
5396
|
"Try it:",
|
|
@@ -5347,7 +5409,7 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5347
5409
|
const protectedPaths = boxes.map((b) => b.projectRoot).filter((p) => typeof p === "string");
|
|
5348
5410
|
const res = await pruneOrphanProjectConfigs({ protectedPaths });
|
|
5349
5411
|
if (res.removed.length > 0) {
|
|
5350
|
-
|
|
5412
|
+
log14.info(
|
|
5351
5413
|
`cleaned ${String(res.removed.length)} orphan project config dir(s): ` + res.removed.map((r) => r.originalPath).join(", ")
|
|
5352
5414
|
);
|
|
5353
5415
|
}
|
|
@@ -5355,9 +5417,9 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5355
5417
|
} catch {
|
|
5356
5418
|
}
|
|
5357
5419
|
}
|
|
5358
|
-
|
|
5420
|
+
outro4("done");
|
|
5359
5421
|
if (attachClaudeAfter) {
|
|
5360
|
-
const { cloudAgentAttach: cloudAgentAttach2 } = await import("./_cloud-attach-
|
|
5422
|
+
const { cloudAgentAttach: cloudAgentAttach2 } = await import("./_cloud-attach-6C5NMOHD.js");
|
|
5361
5423
|
await cloudAgentAttach2({
|
|
5362
5424
|
box: result.record,
|
|
5363
5425
|
binary: "claude",
|
|
@@ -5373,14 +5435,14 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5373
5435
|
s.stop("failed");
|
|
5374
5436
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5375
5437
|
cmdLog.write(`FAIL: ${err instanceof Error ? err.stack ?? err.message : String(err)}`);
|
|
5376
|
-
|
|
5438
|
+
log14.error(msg);
|
|
5377
5439
|
try {
|
|
5378
5440
|
const running = execSync('docker ps --format "{{.Names}}"', {
|
|
5379
5441
|
stdio: ["ignore", "pipe", "ignore"]
|
|
5380
5442
|
}).toString().split("\n").filter((n) => n.startsWith("agentbox-"));
|
|
5381
5443
|
if (running.length > 0) {
|
|
5382
|
-
|
|
5383
|
-
|
|
5444
|
+
log14.warn(`leftover containers: ${running.join(", ")}`);
|
|
5445
|
+
log14.warn(`remove with: docker rm -f ${running.join(" ")}`);
|
|
5384
5446
|
}
|
|
5385
5447
|
} catch {
|
|
5386
5448
|
}
|
|
@@ -5393,7 +5455,7 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5393
5455
|
|
|
5394
5456
|
// src/commands/dashboard.ts
|
|
5395
5457
|
import { spawn as spawn2 } from "child_process";
|
|
5396
|
-
import { log as
|
|
5458
|
+
import { log as log15 } from "@clack/prompts";
|
|
5397
5459
|
import { Command as Command10 } from "commander";
|
|
5398
5460
|
|
|
5399
5461
|
// src/dashboard/layout.ts
|
|
@@ -6917,7 +6979,7 @@ function toSidebar(b) {
|
|
|
6917
6979
|
var dashboardCommand = new Command10("dashboard").description("Box list + the selected box live Agent session").argument("[box]", "initial box (default: first running box; -p restricts to the cwd project)").option("-p, --project", "only this project's boxes (default: all boxes globally)").action(async (idOrName, opts) => {
|
|
6918
6980
|
try {
|
|
6919
6981
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
6920
|
-
|
|
6982
|
+
log15.error("agentbox dashboard needs an interactive terminal");
|
|
6921
6983
|
process.exit(2);
|
|
6922
6984
|
}
|
|
6923
6985
|
const backend = await loadPtyBackend();
|
|
@@ -6927,10 +6989,10 @@ var dashboardCommand = new Command10("dashboard").description("Box list + the se
|
|
|
6927
6989
|
ptySpawn = backend.ptySpawn;
|
|
6928
6990
|
termCtor = backend.termCtor;
|
|
6929
6991
|
} else {
|
|
6930
|
-
|
|
6992
|
+
log15.error(
|
|
6931
6993
|
"agentbox dashboard is unavailable here (native terminal backend failed to load)"
|
|
6932
6994
|
);
|
|
6933
|
-
|
|
6995
|
+
log15.info("use `agentbox claude` / `agentbox claude attach` instead");
|
|
6934
6996
|
process.exit(2);
|
|
6935
6997
|
}
|
|
6936
6998
|
const project = await findProjectRoot(process.cwd());
|
|
@@ -7242,9 +7304,9 @@ var dashboardCommand = new Command10("dashboard").description("Box list + the se
|
|
|
7242
7304
|
if (!br.up) return `VNC: in-box browser unavailable (${br.reason ?? "box not running?"})`;
|
|
7243
7305
|
} catch {
|
|
7244
7306
|
}
|
|
7245
|
-
detach(
|
|
7307
|
+
detach(hostOpenCommand(), [url]);
|
|
7246
7308
|
if (exposedWebUrl) {
|
|
7247
|
-
detach(
|
|
7309
|
+
detach(hostOpenCommand(), [exposedWebUrl]);
|
|
7248
7310
|
return "Opening VNC + web in browser\u2026";
|
|
7249
7311
|
}
|
|
7250
7312
|
return "Opening VNC in browser\u2026";
|
|
@@ -7253,7 +7315,7 @@ var dashboardCommand = new Command10("dashboard").description("Box list + the se
|
|
|
7253
7315
|
const box = (await listBoxes()).find((b) => b.id === boxId);
|
|
7254
7316
|
if (!box) return "box not found";
|
|
7255
7317
|
const { url } = webTarget(box);
|
|
7256
|
-
detach(
|
|
7318
|
+
detach(hostOpenCommand(), [url]);
|
|
7257
7319
|
return `Opening ${url.replace(/^https?:\/\//, "")}\u2026`;
|
|
7258
7320
|
};
|
|
7259
7321
|
const openCode = async (boxId) => {
|
|
@@ -7335,11 +7397,11 @@ var dashboardCommand = new Command10("dashboard").description("Box list + the se
|
|
|
7335
7397
|
});
|
|
7336
7398
|
|
|
7337
7399
|
// ../../packages/sandbox-daytona/dist/cli.js
|
|
7338
|
-
import { log as
|
|
7400
|
+
import { log as log16, spinner as spinner6 } from "@clack/prompts";
|
|
7339
7401
|
import { Command as Command11 } from "commander";
|
|
7340
7402
|
function reportError(err) {
|
|
7341
7403
|
const message = err instanceof Error ? err.message : String(err);
|
|
7342
|
-
|
|
7404
|
+
log16.error(message);
|
|
7343
7405
|
process.exitCode = 1;
|
|
7344
7406
|
}
|
|
7345
7407
|
var loginSub = new Command11("login").description("Set up (or rotate) Daytona credentials for cloud boxes").option("--status", "show what is currently configured (masked) and exit").action(async (opts) => {
|
|
@@ -7398,7 +7460,7 @@ var resyncSub = new Command11("resync").description(
|
|
|
7398
7460
|
const sb = spinner6();
|
|
7399
7461
|
sb.start(`provisioning throwaway sandbox to refresh: ${agents.join(", ")}`);
|
|
7400
7462
|
const ensured = await ensureAgentVolumesForCloud(daytonaBackend, {
|
|
7401
|
-
onLog: (line) =>
|
|
7463
|
+
onLog: (line) => log16.info(line)
|
|
7402
7464
|
});
|
|
7403
7465
|
if (ensured.agents.length === 0) {
|
|
7404
7466
|
sb.stop("no agent volumes available \u2014 the daytona backend has no volume primitive");
|
|
@@ -7438,7 +7500,7 @@ var resyncSub = new Command11("resync").description(
|
|
|
7438
7500
|
}
|
|
7439
7501
|
sb3.stop("throwaway sandbox destroyed");
|
|
7440
7502
|
}
|
|
7441
|
-
|
|
7503
|
+
log16.success(
|
|
7442
7504
|
`Daytona agent volumes refreshed: ${agents.join(", ")}. Next \`agentbox create --provider daytona\` will use the updated credentials.`
|
|
7443
7505
|
);
|
|
7444
7506
|
} catch (err) {
|
|
@@ -7458,11 +7520,11 @@ var dockerCommand = new Command12("docker").description(
|
|
|
7458
7520
|
});
|
|
7459
7521
|
|
|
7460
7522
|
// ../../packages/sandbox-hetzner/dist/cli.js
|
|
7461
|
-
import { log as
|
|
7523
|
+
import { log as log17 } from "@clack/prompts";
|
|
7462
7524
|
import { Command as Command13 } from "commander";
|
|
7463
7525
|
function reportError2(err) {
|
|
7464
7526
|
const message = err instanceof Error ? err.message : String(err);
|
|
7465
|
-
|
|
7527
|
+
log17.error(message);
|
|
7466
7528
|
process.exitCode = 1;
|
|
7467
7529
|
}
|
|
7468
7530
|
var loginSub2 = new Command13("login").description("Set up (or rotate) Hetzner Cloud credentials for VPS boxes").option("--status", "show what is currently configured (masked) and exit").action(async (opts) => {
|
|
@@ -7606,11 +7668,11 @@ var hetznerCommand = new Command13("hetzner").description(
|
|
|
7606
7668
|
).addCommand(loginSub2, { isDefault: true }).addCommand(firewallSub);
|
|
7607
7669
|
|
|
7608
7670
|
// ../../packages/sandbox-vercel/dist/cli.js
|
|
7609
|
-
import { log as
|
|
7671
|
+
import { log as log18 } from "@clack/prompts";
|
|
7610
7672
|
import { Command as Command14 } from "commander";
|
|
7611
7673
|
function reportError3(err) {
|
|
7612
7674
|
const message = err instanceof Error ? err.message : String(err);
|
|
7613
|
-
|
|
7675
|
+
log18.error(message);
|
|
7614
7676
|
process.exitCode = 1;
|
|
7615
7677
|
}
|
|
7616
7678
|
function relativeExpiry(expiresAt) {
|
|
@@ -7681,7 +7743,7 @@ var vercelCommand = new Command14("vercel").description(
|
|
|
7681
7743
|
).addCommand(loginSub3, { isDefault: true });
|
|
7682
7744
|
|
|
7683
7745
|
// src/commands/destroy.ts
|
|
7684
|
-
import { confirm as confirm7, isCancel as isCancel8, log as
|
|
7746
|
+
import { confirm as confirm7, isCancel as isCancel8, log as log19 } from "@clack/prompts";
|
|
7685
7747
|
import { Command as Command15 } from "commander";
|
|
7686
7748
|
var destroyCommand = new Command15("destroy").alias("rm").description("Destroy a box and discard its container writable layer (where /workspace lived)").argument(
|
|
7687
7749
|
"[box]",
|
|
@@ -7690,7 +7752,7 @@ var destroyCommand = new Command15("destroy").alias("rm").description("Destroy a
|
|
|
7690
7752
|
try {
|
|
7691
7753
|
const box = await resolveBoxOrExit(idOrName);
|
|
7692
7754
|
if (!opts.yes) {
|
|
7693
|
-
|
|
7755
|
+
log19.warn("Will also wipe the box volume and agent work-in-progress");
|
|
7694
7756
|
const rootBranch = box.gitWorktrees?.find((w) => w.kind === "root")?.branch;
|
|
7695
7757
|
const lines = [box.name];
|
|
7696
7758
|
if (rootBranch) lines.push(`branch: ${rootBranch}`);
|
|
@@ -7698,13 +7760,13 @@ var destroyCommand = new Command15("destroy").alias("rm").description("Destroy a
|
|
|
7698
7760
|
if (box.snapshotDir) {
|
|
7699
7761
|
lines.push(`snapshot: ${box.snapshotDir}${opts.keepSnapshot ? " (will be kept)" : ""}`);
|
|
7700
7762
|
}
|
|
7701
|
-
|
|
7763
|
+
log19.info(lines.join("\n"));
|
|
7702
7764
|
const ok = await confirm7({
|
|
7703
7765
|
message: "Destroy this box?",
|
|
7704
7766
|
initialValue: false
|
|
7705
7767
|
});
|
|
7706
7768
|
if (isCancel8(ok) || !ok) {
|
|
7707
|
-
|
|
7769
|
+
log19.info("cancelled");
|
|
7708
7770
|
return;
|
|
7709
7771
|
}
|
|
7710
7772
|
}
|
|
@@ -7737,11 +7799,11 @@ var destroyCommand = new Command15("destroy").alias("rm").description("Destroy a
|
|
|
7737
7799
|
});
|
|
7738
7800
|
|
|
7739
7801
|
// src/commands/download.ts
|
|
7740
|
-
import { confirm as confirm13, isCancel as isCancel14, log as
|
|
7802
|
+
import { confirm as confirm13, isCancel as isCancel14, log as log25 } from "@clack/prompts";
|
|
7741
7803
|
import { Command as Command21 } from "commander";
|
|
7742
7804
|
|
|
7743
7805
|
// src/commands/download-claude.ts
|
|
7744
|
-
import { confirm as confirm8, isCancel as isCancel9, log as
|
|
7806
|
+
import { confirm as confirm8, isCancel as isCancel9, log as log20 } from "@clack/prompts";
|
|
7745
7807
|
import { Command as Command16 } from "commander";
|
|
7746
7808
|
function tag(item) {
|
|
7747
7809
|
const noun = item.category === "plugins" ? "plugin" : item.category.replace(/s$/, "");
|
|
@@ -7757,7 +7819,7 @@ var downloadClaudeCommand = new Command16("claude").description(
|
|
|
7757
7819
|
const box = await resolveBoxOrExit(idOrName);
|
|
7758
7820
|
const volume = box.claudeConfigVolume ?? resolveClaudeVolume({ isolate: false, boxId: box.id }).volume;
|
|
7759
7821
|
if (volume === SHARED_CLAUDE_VOLUME) {
|
|
7760
|
-
|
|
7822
|
+
log20.warn(
|
|
7761
7823
|
`Reading the shared ${SHARED_CLAUDE_VOLUME} volume \u2014 it aggregates Claude extensions installed in ANY box, not just ${box.name}.`
|
|
7762
7824
|
);
|
|
7763
7825
|
}
|
|
@@ -7787,7 +7849,7 @@ var downloadClaudeCommand = new Command16("claude").description(
|
|
|
7787
7849
|
initialValue: false
|
|
7788
7850
|
});
|
|
7789
7851
|
if (isCancel9(ok) || !ok) {
|
|
7790
|
-
|
|
7852
|
+
log20.info("cancelled");
|
|
7791
7853
|
return;
|
|
7792
7854
|
}
|
|
7793
7855
|
}
|
|
@@ -7802,7 +7864,7 @@ var downloadClaudeCommand = new Command16("claude").description(
|
|
|
7802
7864
|
});
|
|
7803
7865
|
|
|
7804
7866
|
// src/commands/download-codex.ts
|
|
7805
|
-
import { confirm as confirm9, isCancel as isCancel10, log as
|
|
7867
|
+
import { confirm as confirm9, isCancel as isCancel10, log as log21 } from "@clack/prompts";
|
|
7806
7868
|
import { Command as Command17 } from "commander";
|
|
7807
7869
|
var downloadCodexCommand = new Command17("codex").description(
|
|
7808
7870
|
"Download box-side Codex config/auth (config.toml, auth.json, prompts) back to host ~/.codex (additive)"
|
|
@@ -7814,7 +7876,7 @@ var downloadCodexCommand = new Command17("codex").description(
|
|
|
7814
7876
|
const box = await resolveBoxOrExit(idOrName);
|
|
7815
7877
|
const volume = box.codexConfigVolume ?? resolveCodexVolume({ isolate: false, boxId: box.id }).volume;
|
|
7816
7878
|
if (volume === SHARED_CODEX_VOLUME) {
|
|
7817
|
-
|
|
7879
|
+
log21.warn(
|
|
7818
7880
|
`Reading the shared ${SHARED_CODEX_VOLUME} volume \u2014 it aggregates Codex config from ANY box, not just ${box.name}.`
|
|
7819
7881
|
);
|
|
7820
7882
|
}
|
|
@@ -7840,7 +7902,7 @@ var downloadCodexCommand = new Command17("codex").description(
|
|
|
7840
7902
|
initialValue: false
|
|
7841
7903
|
});
|
|
7842
7904
|
if (isCancel10(ok) || !ok) {
|
|
7843
|
-
|
|
7905
|
+
log21.info("cancelled");
|
|
7844
7906
|
return;
|
|
7845
7907
|
}
|
|
7846
7908
|
}
|
|
@@ -7853,7 +7915,7 @@ var downloadCodexCommand = new Command17("codex").description(
|
|
|
7853
7915
|
});
|
|
7854
7916
|
|
|
7855
7917
|
// src/commands/download-opencode.ts
|
|
7856
|
-
import { confirm as confirm10, isCancel as isCancel11, log as
|
|
7918
|
+
import { confirm as confirm10, isCancel as isCancel11, log as log22 } from "@clack/prompts";
|
|
7857
7919
|
import { Command as Command18 } from "commander";
|
|
7858
7920
|
var downloadOpencodeCommand = new Command18("opencode").description(
|
|
7859
7921
|
"Download box-side OpenCode config/auth (auth.json, opencode.json, agents, commands, themes) back to host ~/.config + ~/.local/share opencode (additive)"
|
|
@@ -7865,7 +7927,7 @@ var downloadOpencodeCommand = new Command18("opencode").description(
|
|
|
7865
7927
|
const box = await resolveBoxOrExit(idOrName);
|
|
7866
7928
|
const volume = box.opencodeConfigVolume ?? resolveOpencodeVolume({ isolate: false, boxId: box.id }).volume;
|
|
7867
7929
|
if (volume === SHARED_OPENCODE_VOLUME) {
|
|
7868
|
-
|
|
7930
|
+
log22.warn(
|
|
7869
7931
|
`Reading the shared ${SHARED_OPENCODE_VOLUME} volume \u2014 it aggregates OpenCode config from ANY box, not just ${box.name}.`
|
|
7870
7932
|
);
|
|
7871
7933
|
}
|
|
@@ -7891,7 +7953,7 @@ var downloadOpencodeCommand = new Command18("opencode").description(
|
|
|
7891
7953
|
initialValue: false
|
|
7892
7954
|
});
|
|
7893
7955
|
if (isCancel11(ok) || !ok) {
|
|
7894
|
-
|
|
7956
|
+
log22.info("cancelled");
|
|
7895
7957
|
return;
|
|
7896
7958
|
}
|
|
7897
7959
|
}
|
|
@@ -7904,7 +7966,7 @@ var downloadOpencodeCommand = new Command18("opencode").description(
|
|
|
7904
7966
|
});
|
|
7905
7967
|
|
|
7906
7968
|
// src/commands/download-config.ts
|
|
7907
|
-
import { confirm as confirm11, isCancel as isCancel12, log as
|
|
7969
|
+
import { confirm as confirm11, isCancel as isCancel12, log as log23 } from "@clack/prompts";
|
|
7908
7970
|
import { Command as Command19 } from "commander";
|
|
7909
7971
|
function tagChange(line) {
|
|
7910
7972
|
const sp = line.indexOf(" ");
|
|
@@ -7922,15 +7984,15 @@ var downloadConfigCommand = new Command19("config").description("Download agentb
|
|
|
7922
7984
|
const box = await resolveBoxOrExit(idOrName);
|
|
7923
7985
|
const insp = await inspectBox(box.id);
|
|
7924
7986
|
if (insp.state === "paused") {
|
|
7925
|
-
|
|
7987
|
+
log23.info("box is paused; unpausing");
|
|
7926
7988
|
await unpauseBox(box.id);
|
|
7927
7989
|
} else if (insp.state === "stopped") {
|
|
7928
|
-
|
|
7990
|
+
log23.info("box is stopped; starting");
|
|
7929
7991
|
await startBox(box.id);
|
|
7930
7992
|
} else if (insp.state === "missing") {
|
|
7931
7993
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
7932
7994
|
}
|
|
7933
|
-
|
|
7995
|
+
log23.info(`agentbox.yaml bypasses gitignore and copies directly into ${box.workspacePath}`);
|
|
7934
7996
|
const preview = await pullToHost(box, {
|
|
7935
7997
|
dryRun: true,
|
|
7936
7998
|
respectGitignore: false,
|
|
@@ -7958,7 +8020,7 @@ var downloadConfigCommand = new Command19("config").description("Download agentb
|
|
|
7958
8020
|
initialValue: false
|
|
7959
8021
|
});
|
|
7960
8022
|
if (isCancel12(ok) || !ok) {
|
|
7961
|
-
|
|
8023
|
+
log23.info("cancelled");
|
|
7962
8024
|
return;
|
|
7963
8025
|
}
|
|
7964
8026
|
}
|
|
@@ -7980,7 +8042,7 @@ var downloadConfigCommand = new Command19("config").description("Download agentb
|
|
|
7980
8042
|
});
|
|
7981
8043
|
|
|
7982
8044
|
// src/commands/download-env.ts
|
|
7983
|
-
import { confirm as confirm12, isCancel as isCancel13, log as
|
|
8045
|
+
import { confirm as confirm12, isCancel as isCancel13, log as log24 } from "@clack/prompts";
|
|
7984
8046
|
import { Command as Command20 } from "commander";
|
|
7985
8047
|
function tagChange2(line) {
|
|
7986
8048
|
const sp = line.indexOf(" ");
|
|
@@ -8004,15 +8066,15 @@ var downloadEnvCommand = new Command20("env").description(
|
|
|
8004
8066
|
const box = await resolveBoxOrExit(idOrName);
|
|
8005
8067
|
const insp = await inspectBox(box.id);
|
|
8006
8068
|
if (insp.state === "paused") {
|
|
8007
|
-
|
|
8069
|
+
log24.info("box is paused; unpausing");
|
|
8008
8070
|
await unpauseBox(box.id);
|
|
8009
8071
|
} else if (insp.state === "stopped") {
|
|
8010
|
-
|
|
8072
|
+
log24.info("box is stopped; starting");
|
|
8011
8073
|
await startBox(box.id);
|
|
8012
8074
|
} else if (insp.state === "missing") {
|
|
8013
8075
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
8014
8076
|
}
|
|
8015
|
-
|
|
8077
|
+
log24.info(
|
|
8016
8078
|
`env/config files bypass gitignore and copy directly into ${box.workspacePath}`
|
|
8017
8079
|
);
|
|
8018
8080
|
const patterns = [...DEFAULT_ENV_PATTERNS, ...opts.pattern];
|
|
@@ -8043,7 +8105,7 @@ var downloadEnvCommand = new Command20("env").description(
|
|
|
8043
8105
|
initialValue: false
|
|
8044
8106
|
});
|
|
8045
8107
|
if (isCancel13(ok) || !ok) {
|
|
8046
|
-
|
|
8108
|
+
log24.info("cancelled");
|
|
8047
8109
|
return;
|
|
8048
8110
|
}
|
|
8049
8111
|
}
|
|
@@ -8091,7 +8153,7 @@ var downloadCommand = new Command21("download").enablePositionalOptions().descri
|
|
|
8091
8153
|
throw new Error("cloud download does not yet support --dry-run; omit to bulk-pull /workspace.");
|
|
8092
8154
|
}
|
|
8093
8155
|
if (!opts.respectGitignore || opts.includeNodeModules || opts.withEnv || opts.pattern.length > 0) {
|
|
8094
|
-
|
|
8156
|
+
log25.warn(
|
|
8095
8157
|
"cloud download ignores gitignore/--with-env/--pattern filters in v1 \u2014 pulling the whole /workspace tree (Phase 6 polish)."
|
|
8096
8158
|
);
|
|
8097
8159
|
}
|
|
@@ -8101,7 +8163,7 @@ var downloadCommand = new Command21("download").enablePositionalOptions().descri
|
|
|
8101
8163
|
initialValue: false
|
|
8102
8164
|
});
|
|
8103
8165
|
if (isCancel14(ok) || !ok) {
|
|
8104
|
-
|
|
8166
|
+
log25.info("cancelled");
|
|
8105
8167
|
return;
|
|
8106
8168
|
}
|
|
8107
8169
|
}
|
|
@@ -8120,17 +8182,17 @@ var downloadCommand = new Command21("download").enablePositionalOptions().descri
|
|
|
8120
8182
|
}
|
|
8121
8183
|
const insp = await inspectBox(box.id);
|
|
8122
8184
|
if (insp.state === "paused") {
|
|
8123
|
-
|
|
8185
|
+
log25.info("box is paused; unpausing");
|
|
8124
8186
|
await unpauseBox(box.id);
|
|
8125
8187
|
} else if (insp.state === "stopped") {
|
|
8126
|
-
|
|
8188
|
+
log25.info("box is stopped; starting");
|
|
8127
8189
|
await startBox(box.id);
|
|
8128
8190
|
} else if (insp.state === "missing") {
|
|
8129
8191
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
8130
8192
|
}
|
|
8131
8193
|
const rootWorktree = box.gitWorktrees?.find((w) => w.kind === "root");
|
|
8132
8194
|
if (rootWorktree) {
|
|
8133
|
-
|
|
8195
|
+
log25.warn(
|
|
8134
8196
|
`This box has been committing to branch \`${rootWorktree.branch}\` in a separate worktree.
|
|
8135
8197
|
For a git-aware merge instead of a file copy, run from your checkout:
|
|
8136
8198
|
git merge ${rootWorktree.branch}
|
|
@@ -8166,7 +8228,7 @@ Continuing with rsync into ${box.workspacePath}`
|
|
|
8166
8228
|
initialValue: false
|
|
8167
8229
|
});
|
|
8168
8230
|
if (isCancel14(ok) || !ok) {
|
|
8169
|
-
|
|
8231
|
+
log25.info("cancelled");
|
|
8170
8232
|
return;
|
|
8171
8233
|
}
|
|
8172
8234
|
}
|
|
@@ -8194,7 +8256,7 @@ downloadCommand.addCommand(downloadOpencodeCommand);
|
|
|
8194
8256
|
downloadCommand.addCommand(downloadConfigCommand);
|
|
8195
8257
|
|
|
8196
8258
|
// src/commands/drive.ts
|
|
8197
|
-
import { log as
|
|
8259
|
+
import { log as log26 } from "@clack/prompts";
|
|
8198
8260
|
import { Command as Command22 } from "commander";
|
|
8199
8261
|
|
|
8200
8262
|
// src/lib/drive/keys.ts
|
|
@@ -8480,7 +8542,7 @@ var driveWaitCommand = new Command22("wait").description("Block until --text app
|
|
|
8480
8542
|
}) + "\n"
|
|
8481
8543
|
);
|
|
8482
8544
|
} else {
|
|
8483
|
-
|
|
8545
|
+
log26.error(`text not found within ${String(timeoutMs)}ms: ${opts.text}`);
|
|
8484
8546
|
}
|
|
8485
8547
|
process.exit(1);
|
|
8486
8548
|
} catch (err) {
|
|
@@ -8507,8 +8569,8 @@ driveCommand.addCommand(driveWaitCommand);
|
|
|
8507
8569
|
driveCommand.addCommand(driveResizeCommand);
|
|
8508
8570
|
function handleDriveError(err) {
|
|
8509
8571
|
if (err instanceof SessionNotFoundError) {
|
|
8510
|
-
|
|
8511
|
-
|
|
8572
|
+
log26.error(err.message);
|
|
8573
|
+
log26.info("start an agent first (e.g. `agentbox claude <box>`) or pass --session.");
|
|
8512
8574
|
process.exit(2);
|
|
8513
8575
|
}
|
|
8514
8576
|
handleLifecycleError(err);
|
|
@@ -8532,10 +8594,10 @@ function sleep2(ms) {
|
|
|
8532
8594
|
}
|
|
8533
8595
|
|
|
8534
8596
|
// src/commands/fork.ts
|
|
8535
|
-
import { log as
|
|
8597
|
+
import { log as log27 } from "@clack/prompts";
|
|
8536
8598
|
import { Command as Command23 } from "commander";
|
|
8537
8599
|
import { existsSync as existsSync4, readdirSync, statSync } from "fs";
|
|
8538
|
-
import { homedir as
|
|
8600
|
+
import { homedir as homedir11 } from "os";
|
|
8539
8601
|
import { join as join12 } from "path";
|
|
8540
8602
|
var FORK_AGENTS = ["claude", "codex", "opencode"];
|
|
8541
8603
|
var AGENT_COMMAND = {
|
|
@@ -8556,7 +8618,7 @@ function resolveSessionArgs(agent, opts) {
|
|
|
8556
8618
|
}
|
|
8557
8619
|
if (opts.session) return ["--resume", opts.session];
|
|
8558
8620
|
if (agent === "codex") return ["--continue"];
|
|
8559
|
-
const dir = join12(
|
|
8621
|
+
const dir = join12(homedir11(), ".claude", "projects", encodeClaudeProjectsDir(opts.workspace));
|
|
8560
8622
|
if (!existsSync4(dir)) return ["--continue"];
|
|
8561
8623
|
const now = Date.now();
|
|
8562
8624
|
const recent = readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
@@ -8601,19 +8663,19 @@ var forkCommand = new Command23("fork").description(
|
|
|
8601
8663
|
"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)"
|
|
8602
8664
|
).action(async (opts) => {
|
|
8603
8665
|
if ((process.env.AGENTBOX_RELAY_URL ?? "").trim().length > 0) {
|
|
8604
|
-
|
|
8666
|
+
log27.error(
|
|
8605
8667
|
"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."
|
|
8606
8668
|
);
|
|
8607
8669
|
process.exit(2);
|
|
8608
8670
|
}
|
|
8609
8671
|
const agent = opts.agent?.trim() || "claude";
|
|
8610
8672
|
if (!FORK_AGENTS.includes(agent)) {
|
|
8611
|
-
|
|
8673
|
+
log27.error(`--agent: expected one of ${FORK_AGENTS.join(", ")}, got "${opts.agent ?? ""}"`);
|
|
8612
8674
|
process.exit(2);
|
|
8613
8675
|
}
|
|
8614
8676
|
const attachIn = opts.attachIn ?? "tab";
|
|
8615
8677
|
if (!FORK_ATTACH_VALUES.includes(attachIn)) {
|
|
8616
|
-
|
|
8678
|
+
log27.error(`--attach-in: expected one of ${FORK_ATTACH_VALUES.join(", ")}, got "${attachIn}"`);
|
|
8617
8679
|
process.exit(2);
|
|
8618
8680
|
}
|
|
8619
8681
|
const provider = opts.provider?.trim();
|
|
@@ -8621,7 +8683,7 @@ var forkCommand = new Command23("fork").description(
|
|
|
8621
8683
|
try {
|
|
8622
8684
|
sessionArgs = resolveSessionArgs(agent, opts);
|
|
8623
8685
|
} catch (err) {
|
|
8624
|
-
|
|
8686
|
+
log27.error(err instanceof Error ? err.message : String(err));
|
|
8625
8687
|
process.exit(2);
|
|
8626
8688
|
}
|
|
8627
8689
|
const subArgv = [
|
|
@@ -8639,16 +8701,16 @@ var forkCommand = new Command23("fork").description(
|
|
|
8639
8701
|
});
|
|
8640
8702
|
|
|
8641
8703
|
// src/commands/install.ts
|
|
8642
|
-
import { confirm as confirm14, intro as intro6, isCancel as isCancel15, log as
|
|
8704
|
+
import { confirm as confirm14, intro as intro6, isCancel as isCancel15, log as log29, note as note2, outro as outro5, select as select2, spinner as spinner8 } from "@clack/prompts";
|
|
8643
8705
|
import { Command as Command25 } from "commander";
|
|
8644
8706
|
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8645
|
-
import { homedir as
|
|
8707
|
+
import { homedir as homedir14 } from "os";
|
|
8646
8708
|
import { dirname as dirname2, join as join15, resolve as resolve2 } from "path";
|
|
8647
8709
|
import { fileURLToPath } from "url";
|
|
8648
8710
|
|
|
8649
8711
|
// src/lib/doctor-checks.ts
|
|
8650
8712
|
import { accessSync, constants as fsConstants, mkdirSync as mkdirSync3 } from "fs";
|
|
8651
|
-
import { homedir as
|
|
8713
|
+
import { homedir as homedir12 } from "os";
|
|
8652
8714
|
import { join as join13 } from "path";
|
|
8653
8715
|
import { execa as execa2 } from "execa";
|
|
8654
8716
|
var ALL_PROVIDERS = ["docker", "daytona", "hetzner", "vercel"];
|
|
@@ -8688,10 +8750,16 @@ function checkNode() {
|
|
|
8688
8750
|
};
|
|
8689
8751
|
}
|
|
8690
8752
|
function checkPlatform() {
|
|
8691
|
-
|
|
8753
|
+
const supported = process.platform === "darwin" || process.platform === "linux";
|
|
8754
|
+
return {
|
|
8755
|
+
label: "platform",
|
|
8756
|
+
status: supported ? "ok" : "warn",
|
|
8757
|
+
detail: `${process.platform}/${process.arch}`,
|
|
8758
|
+
hint: supported ? void 0 : "agentbox supports macOS and Linux hosts; this OS is untested"
|
|
8759
|
+
};
|
|
8692
8760
|
}
|
|
8693
8761
|
function checkAgentboxHome() {
|
|
8694
|
-
const dir = join13(
|
|
8762
|
+
const dir = join13(homedir12(), ".agentbox");
|
|
8695
8763
|
try {
|
|
8696
8764
|
mkdirSync3(dir, { recursive: true });
|
|
8697
8765
|
accessSync(dir, fsConstants.W_OK);
|
|
@@ -8728,6 +8796,7 @@ async function runSystemChecks() {
|
|
|
8728
8796
|
return [checkNode(), checkPlatform(), checkAgentboxHome(), git, ssh];
|
|
8729
8797
|
}
|
|
8730
8798
|
async function dockerChecks() {
|
|
8799
|
+
const linux = process.platform === "linux";
|
|
8731
8800
|
const cli = await probeVersion("docker");
|
|
8732
8801
|
if (!cli) {
|
|
8733
8802
|
return [
|
|
@@ -8735,25 +8804,34 @@ async function dockerChecks() {
|
|
|
8735
8804
|
label: "docker cli",
|
|
8736
8805
|
status: "warn",
|
|
8737
8806
|
detail: "not found",
|
|
8738
|
-
hint: "install Docker Desktop, OrbStack, or docker engine"
|
|
8807
|
+
hint: linux ? "install docker engine: https://docs.docker.com/engine/install/" : "install Docker Desktop, OrbStack, or docker engine"
|
|
8739
8808
|
}
|
|
8740
8809
|
];
|
|
8741
8810
|
}
|
|
8742
8811
|
const cliRes = { label: "docker cli", status: "ok", detail: cli };
|
|
8743
8812
|
const info = await execa2("docker", ["info"], { reject: false });
|
|
8744
8813
|
if (info.exitCode !== 0) {
|
|
8814
|
+
const permDenied = `${info.stderr ?? ""}`.toLowerCase().includes("permission denied");
|
|
8815
|
+
let hint;
|
|
8816
|
+
if (permDenied && linux) {
|
|
8817
|
+
hint = "add your user to the docker group: `sudo usermod -aG docker $USER`, then log out/in (or run `newgrp docker`)";
|
|
8818
|
+
} else if (linux) {
|
|
8819
|
+
hint = "start Docker: `sudo systemctl start docker` (install docker engine if missing)";
|
|
8820
|
+
} else {
|
|
8821
|
+
hint = "start Docker (Desktop / OrbStack)";
|
|
8822
|
+
}
|
|
8745
8823
|
return [
|
|
8746
8824
|
cliRes,
|
|
8747
8825
|
{
|
|
8748
8826
|
label: "docker daemon",
|
|
8749
8827
|
status: "warn",
|
|
8750
|
-
detail: "unreachable",
|
|
8751
|
-
hint
|
|
8828
|
+
detail: permDenied ? "permission denied" : "unreachable",
|
|
8829
|
+
hint
|
|
8752
8830
|
}
|
|
8753
8831
|
];
|
|
8754
8832
|
}
|
|
8755
8833
|
const daemonRes = { label: "docker daemon", status: "ok", detail: "reachable" };
|
|
8756
|
-
const mod = await import("./dist-
|
|
8834
|
+
const mod = await import("./dist-ZEGIMYWZ.js");
|
|
8757
8835
|
let imgRes;
|
|
8758
8836
|
try {
|
|
8759
8837
|
const img = await mod.imageInfo(mod.DEFAULT_BOX_IMAGE);
|
|
@@ -8784,7 +8862,7 @@ async function dockerChecks() {
|
|
|
8784
8862
|
}
|
|
8785
8863
|
async function daytonaChecks() {
|
|
8786
8864
|
try {
|
|
8787
|
-
const mod = await import("./dist-
|
|
8865
|
+
const mod = await import("./dist-PJFJNXO2.js");
|
|
8788
8866
|
const status = await mod.getDaytonaStatus();
|
|
8789
8867
|
if (!status.configured) {
|
|
8790
8868
|
return [
|
|
@@ -8820,7 +8898,7 @@ async function daytonaChecks() {
|
|
|
8820
8898
|
}
|
|
8821
8899
|
async function hetznerChecks() {
|
|
8822
8900
|
try {
|
|
8823
|
-
const mod = await import("./dist-
|
|
8901
|
+
const mod = await import("./dist-4SUIXKSD.js");
|
|
8824
8902
|
const cred = mod.readHetznerCredStatus();
|
|
8825
8903
|
const credRes = cred.source === "none" ? {
|
|
8826
8904
|
label: "credentials",
|
|
@@ -8852,7 +8930,7 @@ async function hetznerChecks() {
|
|
|
8852
8930
|
}
|
|
8853
8931
|
async function vercelChecks() {
|
|
8854
8932
|
try {
|
|
8855
|
-
const mod = await import("./dist-
|
|
8933
|
+
const mod = await import("./dist-HT2YV6PB.js");
|
|
8856
8934
|
const cred = mod.readVercelCredStatus();
|
|
8857
8935
|
const credRes = cred.auth === "none" ? {
|
|
8858
8936
|
label: "credentials",
|
|
@@ -8979,11 +9057,11 @@ function formatDetailed(groups) {
|
|
|
8979
9057
|
|
|
8980
9058
|
// src/lib/first-run.ts
|
|
8981
9059
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
8982
|
-
import { homedir as
|
|
9060
|
+
import { homedir as homedir13 } from "os";
|
|
8983
9061
|
import { dirname, join as join14 } from "path";
|
|
8984
9062
|
var MARKER_VERSION = 1;
|
|
8985
9063
|
function setupMarkerPath() {
|
|
8986
|
-
return join14(
|
|
9064
|
+
return join14(homedir13(), ".agentbox", "setup-complete.json");
|
|
8987
9065
|
}
|
|
8988
9066
|
function isFirstRun() {
|
|
8989
9067
|
return !existsSync5(setupMarkerPath());
|
|
@@ -9000,7 +9078,7 @@ function markSetupComplete(provider) {
|
|
|
9000
9078
|
}
|
|
9001
9079
|
|
|
9002
9080
|
// src/commands/prepare.ts
|
|
9003
|
-
import { intro as intro5, log as
|
|
9081
|
+
import { intro as intro5, log as log28, spinner as spinner7 } from "@clack/prompts";
|
|
9004
9082
|
import { Command as Command24 } from "commander";
|
|
9005
9083
|
async function dockerStatus() {
|
|
9006
9084
|
let img;
|
|
@@ -9063,7 +9141,7 @@ async function renderDocker(status) {
|
|
|
9063
9141
|
}
|
|
9064
9142
|
async function daytonaStatus() {
|
|
9065
9143
|
try {
|
|
9066
|
-
const mod = await import("./dist-
|
|
9144
|
+
const mod = await import("./dist-PJFJNXO2.js");
|
|
9067
9145
|
return await mod.getDaytonaStatus();
|
|
9068
9146
|
} catch (err) {
|
|
9069
9147
|
return {
|
|
@@ -9137,10 +9215,11 @@ async function runPrepare(providerName, opts = {}) {
|
|
|
9137
9215
|
}
|
|
9138
9216
|
const provider = await getProvider(providerName);
|
|
9139
9217
|
if (typeof provider.prepare !== "function") {
|
|
9140
|
-
|
|
9218
|
+
log28.error(`provider '${providerName}' does not implement prepare`);
|
|
9141
9219
|
process.exit(1);
|
|
9142
9220
|
}
|
|
9143
9221
|
const cwd = opts.cwd ?? process.cwd();
|
|
9222
|
+
const registry = providerName === "docker" ? await loadEffectiveConfig(cwd).then((c) => c.effective.box.imageRegistry).catch(() => void 0) : void 0;
|
|
9144
9223
|
const sp = spinner7();
|
|
9145
9224
|
sp.start(`preparing ${providerName}\u2026`);
|
|
9146
9225
|
try {
|
|
@@ -9148,16 +9227,18 @@ async function runPrepare(providerName, opts = {}) {
|
|
|
9148
9227
|
name: opts.name,
|
|
9149
9228
|
hostWorkspace: cwd,
|
|
9150
9229
|
force: opts.force,
|
|
9230
|
+
allowPull: opts.build ? false : void 0,
|
|
9231
|
+
registry,
|
|
9151
9232
|
onLog: (line) => sp.message(line.slice(0, 80))
|
|
9152
9233
|
});
|
|
9153
9234
|
if (result.snapshotName !== void 0) {
|
|
9154
9235
|
sp.stop(`prepared ${providerName}: snapshot '${result.snapshotName}'`);
|
|
9155
9236
|
try {
|
|
9156
9237
|
const written = await setConfigValue("project", "box.image", result.snapshotName, cwd);
|
|
9157
|
-
|
|
9238
|
+
log28.success(`box.image = ${result.snapshotName} (written to ${written.path})`);
|
|
9158
9239
|
} catch (err) {
|
|
9159
9240
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9160
|
-
|
|
9241
|
+
log28.warn(
|
|
9161
9242
|
`prepared snapshot '${result.snapshotName}', but failed to pin it into the project config: ${msg}
|
|
9162
9243
|
Run \`agentbox config set --project box.image ${result.snapshotName}\` manually.`
|
|
9163
9244
|
);
|
|
@@ -9170,7 +9251,7 @@ Run \`agentbox config set --project box.image ${result.snapshotName}\` manually.
|
|
|
9170
9251
|
await showStatus({ onlyProvider: providerName });
|
|
9171
9252
|
}
|
|
9172
9253
|
if (!opts.suppressTip) {
|
|
9173
|
-
|
|
9254
|
+
log28.info(
|
|
9174
9255
|
"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"
|
|
9175
9256
|
);
|
|
9176
9257
|
}
|
|
@@ -9184,7 +9265,10 @@ var prepareCommand = new Command24("prepare").description(
|
|
|
9184
9265
|
).option(
|
|
9185
9266
|
"-p, --provider <name>",
|
|
9186
9267
|
"provider to prepare (docker | daytona | hetzner | vercel). Omit for status-only."
|
|
9187
|
-
).option("-n, --name <name>", "snapshot name (Daytona only; default: agentbox-base-<timestamp>)").option("-f, --force", "rebuild even if the image / snapshot already exists").option(
|
|
9268
|
+
).option("-n, --name <name>", "snapshot name (Daytona only; default: agentbox-base-<timestamp>)").option("-f, --force", "rebuild even if the image / snapshot already exists").option(
|
|
9269
|
+
"--build",
|
|
9270
|
+
"docker: build the base image locally instead of pulling the prebuilt one from the registry"
|
|
9271
|
+
).option("-y, --yes", "skip confirmation prompts (cost / time warnings)").option("--status", "show status without preparing anything").action(async (opts) => {
|
|
9188
9272
|
if (!opts.provider || opts.status) {
|
|
9189
9273
|
await showStatus({});
|
|
9190
9274
|
return;
|
|
@@ -9194,6 +9278,7 @@ var prepareCommand = new Command24("prepare").description(
|
|
|
9194
9278
|
await runPrepare(providerName, {
|
|
9195
9279
|
name: opts.name,
|
|
9196
9280
|
force: opts.force,
|
|
9281
|
+
build: opts.build,
|
|
9197
9282
|
yes: opts.yes
|
|
9198
9283
|
});
|
|
9199
9284
|
});
|
|
@@ -9298,7 +9383,7 @@ ${LOGO_L2}\x1B[0m` + SYNC_END2);
|
|
|
9298
9383
|
}
|
|
9299
9384
|
var LEGACY_INFO_MARKER = "Drive AgentBox from the host:";
|
|
9300
9385
|
function installTargets() {
|
|
9301
|
-
const home =
|
|
9386
|
+
const home = homedir14();
|
|
9302
9387
|
const claudeSkills = join15(home, ".claude", "skills");
|
|
9303
9388
|
return [
|
|
9304
9389
|
{ src: join15("agentbox", "SKILL.md"), dest: join15(claudeSkills, "agentbox", "SKILL.md") },
|
|
@@ -9349,20 +9434,20 @@ function installHostSkills(opts = {}) {
|
|
|
9349
9434
|
for (const t of installTargets()) {
|
|
9350
9435
|
const src = join15(srcDir, t.src);
|
|
9351
9436
|
if (!existsSync6(src)) {
|
|
9352
|
-
if (!quiet)
|
|
9437
|
+
if (!quiet) log29.warn(`bundled file missing (skipped): ${src}`);
|
|
9353
9438
|
skipped++;
|
|
9354
9439
|
continue;
|
|
9355
9440
|
}
|
|
9356
9441
|
if (t.gateDir && !existsSync6(t.gateDir)) continue;
|
|
9357
9442
|
const reason = writableReason(t.dest, force);
|
|
9358
9443
|
if (reason === "skip") {
|
|
9359
|
-
if (!quiet)
|
|
9444
|
+
if (!quiet) log29.warn(`user-modified file at ${t.dest}, skipping; pass --force to overwrite`);
|
|
9360
9445
|
blocked.push(t.dest);
|
|
9361
9446
|
skipped++;
|
|
9362
9447
|
continue;
|
|
9363
9448
|
}
|
|
9364
9449
|
if (dryRun) {
|
|
9365
|
-
if (!quiet)
|
|
9450
|
+
if (!quiet) log29.info(`would write ${t.dest} (${reason})`);
|
|
9366
9451
|
written.push(t.dest);
|
|
9367
9452
|
continue;
|
|
9368
9453
|
}
|
|
@@ -9394,10 +9479,10 @@ function ensureTty() {
|
|
|
9394
9479
|
async function runProviderLogin(name) {
|
|
9395
9480
|
if (name === "docker") return true;
|
|
9396
9481
|
if (name === "daytona") {
|
|
9397
|
-
const mod2 = await import("./dist-
|
|
9482
|
+
const mod2 = await import("./dist-PJFJNXO2.js");
|
|
9398
9483
|
const status2 = await mod2.getDaytonaStatus();
|
|
9399
9484
|
if (status2.configured) {
|
|
9400
|
-
|
|
9485
|
+
log29.info("daytona: already configured");
|
|
9401
9486
|
const rotate = await confirm14({ message: "Re-authenticate Daytona?", initialValue: false });
|
|
9402
9487
|
if (isCancel15(rotate)) return false;
|
|
9403
9488
|
if (rotate) await mod2.ensureDaytonaCredentials({ force: true });
|
|
@@ -9407,10 +9492,10 @@ async function runProviderLogin(name) {
|
|
|
9407
9492
|
return true;
|
|
9408
9493
|
}
|
|
9409
9494
|
if (name === "hetzner") {
|
|
9410
|
-
const mod2 = await import("./dist-
|
|
9495
|
+
const mod2 = await import("./dist-4SUIXKSD.js");
|
|
9411
9496
|
const status2 = mod2.readHetznerCredStatus();
|
|
9412
9497
|
if (status2.source !== "none") {
|
|
9413
|
-
|
|
9498
|
+
log29.info("hetzner: already configured");
|
|
9414
9499
|
const rotate = await confirm14({ message: "Re-authenticate Hetzner?", initialValue: false });
|
|
9415
9500
|
if (isCancel15(rotate)) return false;
|
|
9416
9501
|
if (rotate) await mod2.ensureHetznerCredentials({ force: true });
|
|
@@ -9419,10 +9504,10 @@ async function runProviderLogin(name) {
|
|
|
9419
9504
|
await mod2.ensureHetznerCredentials();
|
|
9420
9505
|
return true;
|
|
9421
9506
|
}
|
|
9422
|
-
const mod = await import("./dist-
|
|
9507
|
+
const mod = await import("./dist-HT2YV6PB.js");
|
|
9423
9508
|
const status = mod.readVercelCredStatus();
|
|
9424
9509
|
if (status.auth !== "none") {
|
|
9425
|
-
|
|
9510
|
+
log29.info(`vercel: already configured (${status.auth})`);
|
|
9426
9511
|
const rotate = await confirm14({ message: "Re-authenticate Vercel?", initialValue: false });
|
|
9427
9512
|
if (isCancel15(rotate)) return false;
|
|
9428
9513
|
if (rotate) await mod.ensureVercelCredentials({ force: true });
|
|
@@ -9453,11 +9538,11 @@ async function runInstallWizard(opts = {}) {
|
|
|
9453
9538
|
process.stdout.write(" " + formatCompact([sysGroup]) + "\n");
|
|
9454
9539
|
const hardFail = sysResults.find((r) => r.status === "fail");
|
|
9455
9540
|
if (hardFail) {
|
|
9456
|
-
|
|
9457
|
-
|
|
9541
|
+
log29.error(`system check failed: ${hardFail.label} \u2014 ${hardFail.detail}`);
|
|
9542
|
+
log29.info("run `agentbox doctor` for full detail");
|
|
9458
9543
|
const cont = await confirm14({ message: "Continue anyway?", initialValue: false });
|
|
9459
9544
|
if (isCancel15(cont) || !cont) {
|
|
9460
|
-
|
|
9545
|
+
outro5("aborted");
|
|
9461
9546
|
return false;
|
|
9462
9547
|
}
|
|
9463
9548
|
}
|
|
@@ -9465,7 +9550,7 @@ async function runInstallWizard(opts = {}) {
|
|
|
9465
9550
|
if (opts.provider) {
|
|
9466
9551
|
const candidate = opts.provider.trim();
|
|
9467
9552
|
if (!isProviderName(candidate)) {
|
|
9468
|
-
|
|
9553
|
+
log29.error(`unknown --provider: ${candidate}`);
|
|
9469
9554
|
return false;
|
|
9470
9555
|
}
|
|
9471
9556
|
providerName = candidate;
|
|
@@ -9480,7 +9565,7 @@ async function runInstallWizard(opts = {}) {
|
|
|
9480
9565
|
}))
|
|
9481
9566
|
});
|
|
9482
9567
|
if (isCancel15(picked)) {
|
|
9483
|
-
|
|
9568
|
+
outro5("cancelled");
|
|
9484
9569
|
return false;
|
|
9485
9570
|
}
|
|
9486
9571
|
providerName = picked;
|
|
@@ -9488,14 +9573,14 @@ async function runInstallWizard(opts = {}) {
|
|
|
9488
9573
|
if (providerName !== "docker") {
|
|
9489
9574
|
const loggedIn = await runProviderLogin(providerName);
|
|
9490
9575
|
if (!loggedIn) {
|
|
9491
|
-
|
|
9576
|
+
outro5("cancelled");
|
|
9492
9577
|
return false;
|
|
9493
9578
|
}
|
|
9494
9579
|
}
|
|
9495
9580
|
const prepareMsg = providerName === "docker" ? "Build the box image now? (~1GB, a few minutes)" : `Bake the ${providerName} base snapshot now? (a few minutes, uses cloud time)`;
|
|
9496
9581
|
const wantPrepare = opts.yes ? true : await confirm14({ message: prepareMsg, initialValue: true });
|
|
9497
9582
|
if (isCancel15(wantPrepare)) {
|
|
9498
|
-
|
|
9583
|
+
outro5("cancelled");
|
|
9499
9584
|
return false;
|
|
9500
9585
|
}
|
|
9501
9586
|
if (wantPrepare) {
|
|
@@ -9507,12 +9592,12 @@ async function runInstallWizard(opts = {}) {
|
|
|
9507
9592
|
suppressTip: true
|
|
9508
9593
|
});
|
|
9509
9594
|
} catch (err) {
|
|
9510
|
-
|
|
9595
|
+
log29.warn(
|
|
9511
9596
|
`prepare failed: ${err instanceof Error ? err.message : String(err)} \u2014 you can rerun \`agentbox prepare --provider ${providerName}\` later`
|
|
9512
9597
|
);
|
|
9513
9598
|
}
|
|
9514
9599
|
} else {
|
|
9515
|
-
|
|
9600
|
+
log29.info(
|
|
9516
9601
|
`skipped \u2014 the ${providerName} base will build lazily on first \`agentbox ${providerName === "docker" ? "" : providerName + " "}create\``
|
|
9517
9602
|
);
|
|
9518
9603
|
}
|
|
@@ -9527,20 +9612,20 @@ async function runInstallWizard(opts = {}) {
|
|
|
9527
9612
|
sp.stop(`Agentbox Skills: nothing to write (${String(skillRes.skipped)} skipped)`);
|
|
9528
9613
|
}
|
|
9529
9614
|
if (skillRes.blocked.length > 0) {
|
|
9530
|
-
|
|
9615
|
+
log29.warn(
|
|
9531
9616
|
`user-modified host skill file(s) left in place: ${skillRes.blocked.join(", ")}
|
|
9532
9617
|
pass \`agentbox install --skills-only --force\` to overwrite`
|
|
9533
9618
|
);
|
|
9534
9619
|
}
|
|
9535
9620
|
} catch (err) {
|
|
9536
9621
|
sp.stop("Agentbox Skills: failed");
|
|
9537
|
-
|
|
9622
|
+
log29.warn(err instanceof Error ? err.message : String(err));
|
|
9538
9623
|
}
|
|
9539
9624
|
markSetupComplete(providerName);
|
|
9540
9625
|
const providerGroup = await runProviderChecks(providerName);
|
|
9541
9626
|
process.stdout.write(" " + formatCompact([sysGroup, providerGroup]) + "\n");
|
|
9542
|
-
|
|
9543
|
-
|
|
9627
|
+
note2(tutorialBody(providerName), "Next steps");
|
|
9628
|
+
outro5(
|
|
9544
9629
|
opts.fromAutoTrigger ? "\u2728 Setup complete \u2014 continuing with your command\u2026" : "\u2728 Setup complete"
|
|
9545
9630
|
);
|
|
9546
9631
|
return true;
|
|
@@ -9560,20 +9645,20 @@ var installCommand = new Command25("install").description(
|
|
|
9560
9645
|
try {
|
|
9561
9646
|
res = installHostSkills({ force: opts.force, dryRun: opts.dryRun });
|
|
9562
9647
|
} catch (err) {
|
|
9563
|
-
|
|
9648
|
+
log29.error(err instanceof Error ? err.message : String(err));
|
|
9564
9649
|
process.exit(1);
|
|
9565
9650
|
}
|
|
9566
9651
|
if (opts.dryRun) {
|
|
9567
|
-
|
|
9652
|
+
outro5(
|
|
9568
9653
|
`dry-run: ${String(res.written.length)} file(s) would be written, ${String(res.skipped)} skipped`
|
|
9569
9654
|
);
|
|
9570
9655
|
return;
|
|
9571
9656
|
}
|
|
9572
9657
|
if (res.written.length === 0) {
|
|
9573
|
-
|
|
9658
|
+
outro5(`nothing installed (${String(res.skipped)} skipped)`);
|
|
9574
9659
|
return;
|
|
9575
9660
|
}
|
|
9576
|
-
|
|
9661
|
+
outro5(`installed: ${res.written.join(", ")}`);
|
|
9577
9662
|
return;
|
|
9578
9663
|
}
|
|
9579
9664
|
const ok = await runInstallWizard({
|
|
@@ -9765,7 +9850,7 @@ for (const op of GH_PR_OPS) {
|
|
|
9765
9850
|
var gitCommand = new Command27("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);
|
|
9766
9851
|
|
|
9767
9852
|
// src/commands/list.ts
|
|
9768
|
-
import { log as
|
|
9853
|
+
import { log as log30 } from "@clack/prompts";
|
|
9769
9854
|
import { Command as Command28 } from "commander";
|
|
9770
9855
|
import { pathToFileURL } from "url";
|
|
9771
9856
|
|
|
@@ -9980,7 +10065,7 @@ var listCommand2 = withWatchOptions(
|
|
|
9980
10065
|
)
|
|
9981
10066
|
).action(async (opts) => {
|
|
9982
10067
|
if (opts.json && opts.watch) {
|
|
9983
|
-
|
|
10068
|
+
log30.error("cannot combine --json with --watch");
|
|
9984
10069
|
process.exit(2);
|
|
9985
10070
|
}
|
|
9986
10071
|
const all = opts.global ?? false;
|
|
@@ -9998,7 +10083,7 @@ var listCommand2 = withWatchOptions(
|
|
|
9998
10083
|
});
|
|
9999
10084
|
|
|
10000
10085
|
// src/commands/logs.ts
|
|
10001
|
-
import { log as
|
|
10086
|
+
import { log as log31 } from "@clack/prompts";
|
|
10002
10087
|
import { Command as Command29 } from "commander";
|
|
10003
10088
|
import { spawn as spawn3 } from "child_process";
|
|
10004
10089
|
var DAEMON_LOG_PATH = "/var/log/agentbox/ctl-daemon.log";
|
|
@@ -10020,9 +10105,9 @@ var logsCommand = new Command29("logs").description("Print recent log lines from
|
|
|
10020
10105
|
service = boxArg;
|
|
10021
10106
|
}
|
|
10022
10107
|
if (!service && !opts.daemon) {
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10108
|
+
log31.error("missing <service> argument");
|
|
10109
|
+
log31.info("usage: agentbox logs [box] <service> [-n N] [-f]");
|
|
10110
|
+
log31.info(" agentbox logs [box] --daemon [-n N] [-f]");
|
|
10026
10111
|
process.exit(2);
|
|
10027
10112
|
}
|
|
10028
10113
|
const box = await resolveBoxOrExit(idOrName);
|
|
@@ -10033,7 +10118,7 @@ var logsCommand = new Command29("logs").description("Print recent log lines from
|
|
|
10033
10118
|
if (!opts.follow) {
|
|
10034
10119
|
const proc = await provider.exec(box, args, { user: "vscode" });
|
|
10035
10120
|
if (proc.exitCode !== 0) {
|
|
10036
|
-
|
|
10121
|
+
log31.error(
|
|
10037
10122
|
`${opts.daemon ? "daemon log" : "agentbox-ctl logs"} failed: ${proc.stderr || proc.stdout}`
|
|
10038
10123
|
);
|
|
10039
10124
|
process.exit(1);
|
|
@@ -10089,10 +10174,10 @@ var logsCommand = new Command29("logs").description("Print recent log lines from
|
|
|
10089
10174
|
});
|
|
10090
10175
|
|
|
10091
10176
|
// src/commands/open.ts
|
|
10092
|
-
import { log as
|
|
10177
|
+
import { log as log32 } from "@clack/prompts";
|
|
10093
10178
|
import { execa as execa3 } from "execa";
|
|
10094
10179
|
import { existsSync as existsSync7, mkdirSync as mkdirSync6 } from "fs";
|
|
10095
|
-
import { homedir as
|
|
10180
|
+
import { homedir as homedir15 } from "os";
|
|
10096
10181
|
import { join as join16 } from "path";
|
|
10097
10182
|
import { Command as Command30 } from "commander";
|
|
10098
10183
|
|
|
@@ -10155,7 +10240,7 @@ var openCommand = new Command30("open").description("Open a box's /workspace in
|
|
|
10155
10240
|
}
|
|
10156
10241
|
});
|
|
10157
10242
|
async function runCloudOpen(box, provider, opts) {
|
|
10158
|
-
const mountRoot = join16(
|
|
10243
|
+
const mountRoot = join16(homedir15(), ".agentbox", "mounts", box.name);
|
|
10159
10244
|
if (opts.unmount) {
|
|
10160
10245
|
const ok = await tryUnmount(mountRoot);
|
|
10161
10246
|
if (ok) process.stdout.write(`unmounted ${mountRoot}
|
|
@@ -10195,10 +10280,10 @@ async function runCloudOpen(box, provider, opts) {
|
|
|
10195
10280
|
if (!existsSync7(mountRoot)) {
|
|
10196
10281
|
mkdirSync6(mountRoot, { recursive: true, mode: 493 });
|
|
10197
10282
|
} else if (await isMounted(mountRoot)) {
|
|
10198
|
-
|
|
10283
|
+
log32.info(`re-mounting (stale mount detected at ${mountRoot})`);
|
|
10199
10284
|
await tryUnmount(mountRoot);
|
|
10200
10285
|
}
|
|
10201
|
-
|
|
10286
|
+
log32.info(`mounting ${alias}:/workspace at ${mountRoot}`);
|
|
10202
10287
|
const mount = await execa3(
|
|
10203
10288
|
sshfsBin,
|
|
10204
10289
|
[
|
|
@@ -10218,7 +10303,7 @@ async function runCloudOpen(box, provider, opts) {
|
|
|
10218
10303
|
if (mount.exitCode !== 0) {
|
|
10219
10304
|
throw new Error(`sshfs mount failed (exit ${String(mount.exitCode)}): ${mount.stderr || mount.stdout}`);
|
|
10220
10305
|
}
|
|
10221
|
-
await execa3(
|
|
10306
|
+
await execa3(hostOpenCommand(), [mountRoot], { reject: false });
|
|
10222
10307
|
process.stdout.write(`opened ${mountRoot}
|
|
10223
10308
|
`);
|
|
10224
10309
|
process.stdout.write(`unmount later with: agentbox open ${box.name} --unmount
|
|
@@ -10269,7 +10354,7 @@ var pauseCommand = new Command31("pause").description(
|
|
|
10269
10354
|
});
|
|
10270
10355
|
|
|
10271
10356
|
// src/commands/prune.ts
|
|
10272
|
-
import { confirm as confirm15, isCancel as isCancel16, log as
|
|
10357
|
+
import { confirm as confirm15, isCancel as isCancel16, log as log33 } from "@clack/prompts";
|
|
10273
10358
|
import { Command as Command32 } from "commander";
|
|
10274
10359
|
function totalRemovals(r, projectConfigs) {
|
|
10275
10360
|
return r.removedRecords.length + r.removedContainers.length + r.removedVolumes.length + r.removedSnapshotDirs.length + r.removedBoxDirs.length + projectConfigs.length;
|
|
@@ -10329,7 +10414,7 @@ var pruneCommand = new Command32("prune").description("Clean up orphan state.jso
|
|
|
10329
10414
|
return;
|
|
10330
10415
|
}
|
|
10331
10416
|
if (opts.provider !== void 0 && opts.provider !== "docker") {
|
|
10332
|
-
|
|
10417
|
+
log33.error(`unknown provider '${opts.provider}'; expected docker, daytona, hetzner, or vercel`);
|
|
10333
10418
|
process.exit(2);
|
|
10334
10419
|
}
|
|
10335
10420
|
const dryRun = opts.dryRun ?? false;
|
|
@@ -10342,13 +10427,13 @@ var pruneCommand = new Command32("prune").description("Clean up orphan state.jso
|
|
|
10342
10427
|
process.stdout.write("nothing to prune\n");
|
|
10343
10428
|
return;
|
|
10344
10429
|
}
|
|
10345
|
-
|
|
10430
|
+
log33.info(`would remove:
|
|
10346
10431
|
${summary(preview, previewProjects)}`);
|
|
10347
10432
|
if (dryRun) return;
|
|
10348
10433
|
if (!opts.yes) {
|
|
10349
10434
|
const ok = await confirm15({ message: "Proceed with prune?", initialValue: true });
|
|
10350
10435
|
if (isCancel16(ok) || !ok) {
|
|
10351
|
-
|
|
10436
|
+
log33.info("cancelled");
|
|
10352
10437
|
return;
|
|
10353
10438
|
}
|
|
10354
10439
|
}
|
|
@@ -10369,7 +10454,7 @@ async function pruneCloud(provider, opts) {
|
|
|
10369
10454
|
const dryRun = opts.dryRun ?? false;
|
|
10370
10455
|
const backend = await cloudBackendForProvider(provider);
|
|
10371
10456
|
if (!backend.list) {
|
|
10372
|
-
|
|
10457
|
+
log33.error(`${provider} backend doesn't expose \`list()\`; cannot enumerate sandboxes for prune`);
|
|
10373
10458
|
process.exit(2);
|
|
10374
10459
|
}
|
|
10375
10460
|
const [remote, state] = await Promise.all([backend.list(), readState()]);
|
|
@@ -10389,7 +10474,7 @@ async function pruneCloud(provider, opts) {
|
|
|
10389
10474
|
`);
|
|
10390
10475
|
return;
|
|
10391
10476
|
}
|
|
10392
|
-
|
|
10477
|
+
log33.info(`found ${String(orphans.length)} ${provider} sandbox(es) not in this CLI's state:`);
|
|
10393
10478
|
for (const sb of orphans) {
|
|
10394
10479
|
const parts = [sb.sandboxId];
|
|
10395
10480
|
if (sb.name) parts.push(sb.name);
|
|
@@ -10405,7 +10490,7 @@ async function pruneCloud(provider, opts) {
|
|
|
10405
10490
|
initialValue: false
|
|
10406
10491
|
});
|
|
10407
10492
|
if (isCancel16(ok) || !ok) {
|
|
10408
|
-
|
|
10493
|
+
log33.info("cancelled");
|
|
10409
10494
|
return;
|
|
10410
10495
|
}
|
|
10411
10496
|
}
|
|
@@ -10417,7 +10502,7 @@ async function pruneCloud(provider, opts) {
|
|
|
10417
10502
|
deleted++;
|
|
10418
10503
|
} catch (err) {
|
|
10419
10504
|
failed++;
|
|
10420
|
-
|
|
10505
|
+
log33.warn(
|
|
10421
10506
|
`delete ${sb.sandboxId} failed: ${err instanceof Error ? err.message : String(err)}`
|
|
10422
10507
|
);
|
|
10423
10508
|
}
|
|
@@ -10430,7 +10515,7 @@ async function pruneCloud(provider, opts) {
|
|
|
10430
10515
|
|
|
10431
10516
|
// src/commands/queue.ts
|
|
10432
10517
|
import { readFile as readFile4, stat as stat5 } from "fs/promises";
|
|
10433
|
-
import { intro as intro7, log as
|
|
10518
|
+
import { intro as intro7, log as log34, outro as outro6 } from "@clack/prompts";
|
|
10434
10519
|
import { Command as Command33 } from "commander";
|
|
10435
10520
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["done", "failed", "cancelled"]);
|
|
10436
10521
|
var queueCommand = new Command33("queue").description("Inspect and manage background `agentbox claude|codex|opencode -i` jobs");
|
|
@@ -10439,8 +10524,8 @@ var queueListCommand = new Command33("list").description("List queued, running,
|
|
|
10439
10524
|
const cfg = await loadQueueConfig();
|
|
10440
10525
|
const visible = opts.all === true ? jobs : jobs.filter((j) => !TERMINAL_STATUSES.has(j.status));
|
|
10441
10526
|
if (visible.length === 0) {
|
|
10442
|
-
|
|
10443
|
-
|
|
10527
|
+
log34.info(opts.all ? "no queued jobs." : "no active queued jobs (--all to see terminal).");
|
|
10528
|
+
log34.info(`queue.maxConcurrent = ${String(cfg.maxConcurrent)} (queue.enabled=${String(cfg.enabled)})`);
|
|
10444
10529
|
return;
|
|
10445
10530
|
}
|
|
10446
10531
|
const rows = visible.map((j) => ({
|
|
@@ -10465,12 +10550,12 @@ var queueListCommand = new Command33("list").description("List queued, running,
|
|
|
10465
10550
|
headers.map((h, i) => pad4(String(r[h]), widths[i])).join(" ") + "\n"
|
|
10466
10551
|
);
|
|
10467
10552
|
}
|
|
10468
|
-
|
|
10553
|
+
log34.info(`queue.maxConcurrent = ${String(cfg.maxConcurrent)} (queue.enabled=${String(cfg.enabled)})`);
|
|
10469
10554
|
});
|
|
10470
10555
|
var queueShowCommand = new Command33("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) => {
|
|
10471
10556
|
const job = await readJob(id);
|
|
10472
10557
|
if (!job) {
|
|
10473
|
-
|
|
10558
|
+
log34.error(`no job with id ${id}`);
|
|
10474
10559
|
process.exit(1);
|
|
10475
10560
|
}
|
|
10476
10561
|
process.stdout.write(JSON.stringify(job, null, 2) + "\n");
|
|
@@ -10486,18 +10571,18 @@ var queueShowCommand = new Command33("show").description("Dump a job manifest an
|
|
|
10486
10571
|
process.stdout.write(slice.join("\n"));
|
|
10487
10572
|
if (!slice.join("\n").endsWith("\n")) process.stdout.write("\n");
|
|
10488
10573
|
} catch {
|
|
10489
|
-
|
|
10574
|
+
log34.info(`(no log at ${job.logPath} yet)`);
|
|
10490
10575
|
}
|
|
10491
10576
|
});
|
|
10492
10577
|
var queueCancelCommand = new Command33("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) => {
|
|
10493
10578
|
intro7(`Cancelling queue job ${id}...`);
|
|
10494
10579
|
const job = await readJob(id);
|
|
10495
10580
|
if (!job) {
|
|
10496
|
-
|
|
10581
|
+
log34.error(`no job with id ${id}`);
|
|
10497
10582
|
process.exit(1);
|
|
10498
10583
|
}
|
|
10499
10584
|
if (job.status !== "queued") {
|
|
10500
|
-
|
|
10585
|
+
log34.error(
|
|
10501
10586
|
`job ${id} is ${job.status}; cancel only flips 'queued' \u2192 'cancelled'.` + (job.status === "running" ? ` Use 'agentbox destroy ${job.boxName || id}' to stop the box.` : "")
|
|
10502
10587
|
);
|
|
10503
10588
|
process.exit(1);
|
|
@@ -10509,7 +10594,7 @@ var queueCancelCommand = new Command33("cancel").description("Cancel a queued jo
|
|
|
10509
10594
|
reason: "cancelled by user"
|
|
10510
10595
|
};
|
|
10511
10596
|
await writeJob(cancelled);
|
|
10512
|
-
|
|
10597
|
+
outro6(`job ${id} cancelled`);
|
|
10513
10598
|
});
|
|
10514
10599
|
var queueClearCommand = new Command33("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) => {
|
|
10515
10600
|
const targets = /* @__PURE__ */ new Set();
|
|
@@ -10517,7 +10602,7 @@ var queueClearCommand = new Command33("clear").description("Sweep terminal-state
|
|
|
10517
10602
|
if (opts.all === true || opts.failed === true) targets.add("failed");
|
|
10518
10603
|
if (opts.all === true || opts.cancelled === true) targets.add("cancelled");
|
|
10519
10604
|
if (targets.size === 0) {
|
|
10520
|
-
|
|
10605
|
+
log34.error("pick at least one of: --done, --failed, --cancelled, --all");
|
|
10521
10606
|
process.exit(2);
|
|
10522
10607
|
}
|
|
10523
10608
|
const jobs = await loadQueue();
|
|
@@ -10527,7 +10612,7 @@ var queueClearCommand = new Command33("clear").description("Sweep terminal-state
|
|
|
10527
10612
|
await deleteJob(j.id);
|
|
10528
10613
|
removed += 1;
|
|
10529
10614
|
}
|
|
10530
|
-
|
|
10615
|
+
log34.success(`removed ${String(removed)} manifest${removed === 1 ? "" : "s"}`);
|
|
10531
10616
|
});
|
|
10532
10617
|
var QUEUE_WAIT_EVENTS = [
|
|
10533
10618
|
"new-box",
|
|
@@ -10544,7 +10629,7 @@ var queueWaitForCommand = new Command33("wait-for").description(
|
|
|
10544
10629
|
`Block until a queue / box event fires. <event> one of: ${QUEUE_WAIT_EVENTS.join(" | ")}.`
|
|
10545
10630
|
).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) => {
|
|
10546
10631
|
if (!QUEUE_WAIT_EVENTS.includes(eventRaw)) {
|
|
10547
|
-
|
|
10632
|
+
log34.error(`unknown event '${eventRaw}' (one of: ${QUEUE_WAIT_EVENTS.join(", ")})`);
|
|
10548
10633
|
process.exit(2);
|
|
10549
10634
|
}
|
|
10550
10635
|
const event = eventRaw;
|
|
@@ -10564,11 +10649,11 @@ var queueWaitForCommand = new Command33("wait-for").description(
|
|
|
10564
10649
|
if (opts.json === true) {
|
|
10565
10650
|
process.stdout.write(JSON.stringify({ matched: false, event, elapsedMs }) + "\n");
|
|
10566
10651
|
} else {
|
|
10567
|
-
|
|
10652
|
+
log34.error(`'${event}' did not occur within ${String(timeoutMs)}ms`);
|
|
10568
10653
|
}
|
|
10569
10654
|
process.exit(1);
|
|
10570
10655
|
}
|
|
10571
|
-
|
|
10656
|
+
log34.error(err instanceof Error ? err.message : String(err));
|
|
10572
10657
|
process.exit(1);
|
|
10573
10658
|
}
|
|
10574
10659
|
});
|
|
@@ -10665,7 +10750,7 @@ function truncate(s, max) {
|
|
|
10665
10750
|
}
|
|
10666
10751
|
|
|
10667
10752
|
// src/commands/relay.ts
|
|
10668
|
-
import { log as
|
|
10753
|
+
import { log as log35, spinner as spinner9 } from "@clack/prompts";
|
|
10669
10754
|
import { Command as Command34 } from "commander";
|
|
10670
10755
|
async function rehydrateFromState() {
|
|
10671
10756
|
const state = await readState();
|
|
@@ -10759,7 +10844,7 @@ var restartSub = new Command34("restart").description("Stop then start the host
|
|
|
10759
10844
|
s2.stop(`relay running on ${ep.hostUrl}`);
|
|
10760
10845
|
} catch (err) {
|
|
10761
10846
|
s2.stop("relay start failed");
|
|
10762
|
-
|
|
10847
|
+
log35.warn(err instanceof Error ? err.message : String(err));
|
|
10763
10848
|
throw err;
|
|
10764
10849
|
}
|
|
10765
10850
|
} catch (err) {
|
|
@@ -10771,17 +10856,17 @@ var relayCommand = new Command34("relay").description("Manage the host relay pro
|
|
|
10771
10856
|
// src/commands/_run-queued-job.ts
|
|
10772
10857
|
import { Command as Command35 } from "commander";
|
|
10773
10858
|
var runQueuedJobCommand = new Command35("_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) => {
|
|
10774
|
-
const
|
|
10775
|
-
|
|
10859
|
+
const log44 = openCommandLog(`queue-${id}`);
|
|
10860
|
+
log44.write(`worker pid=${String(process.pid)} starting for job ${id}`);
|
|
10776
10861
|
let job = null;
|
|
10777
10862
|
try {
|
|
10778
10863
|
job = await readJob(id);
|
|
10779
10864
|
if (!job) {
|
|
10780
|
-
|
|
10781
|
-
|
|
10865
|
+
log44.write(`FATAL: no manifest at id=${id}`);
|
|
10866
|
+
log44.close();
|
|
10782
10867
|
process.exit(64);
|
|
10783
10868
|
}
|
|
10784
|
-
await runDockerJob(job,
|
|
10869
|
+
await runDockerJob(job, log44, (boxId) => {
|
|
10785
10870
|
if (job) job = { ...job, boxId };
|
|
10786
10871
|
});
|
|
10787
10872
|
const done = {
|
|
@@ -10791,12 +10876,12 @@ var runQueuedJobCommand = new Command35("_run-queued-job").description("internal
|
|
|
10791
10876
|
exitCode: 0
|
|
10792
10877
|
};
|
|
10793
10878
|
await writeJob(done);
|
|
10794
|
-
|
|
10795
|
-
|
|
10879
|
+
log44.write(`done`);
|
|
10880
|
+
log44.close();
|
|
10796
10881
|
process.exit(0);
|
|
10797
10882
|
} catch (err) {
|
|
10798
10883
|
const msg = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
10799
|
-
|
|
10884
|
+
log44.write(`FAIL: ${msg}`);
|
|
10800
10885
|
if (job) {
|
|
10801
10886
|
try {
|
|
10802
10887
|
const failed = {
|
|
@@ -10810,11 +10895,11 @@ var runQueuedJobCommand = new Command35("_run-queued-job").description("internal
|
|
|
10810
10895
|
} catch {
|
|
10811
10896
|
}
|
|
10812
10897
|
}
|
|
10813
|
-
|
|
10898
|
+
log44.close();
|
|
10814
10899
|
process.exit(1);
|
|
10815
10900
|
}
|
|
10816
10901
|
});
|
|
10817
|
-
async function runDockerJob(job,
|
|
10902
|
+
async function runDockerJob(job, log44, onBoxCreated) {
|
|
10818
10903
|
const opts = job.createOpts;
|
|
10819
10904
|
const cfg = await loadEffectiveConfig(opts.workspace, {
|
|
10820
10905
|
cliOverrides: buildOverridesFromJob(job)
|
|
@@ -10829,7 +10914,7 @@ async function runDockerJob(job, log45, onBoxCreated) {
|
|
|
10829
10914
|
const useSnapshot = opts.hostSnapshot === false ? false : opts.hostSnapshot === true ? true : cfg.effective.box.hostSnapshot ?? false;
|
|
10830
10915
|
const resolved = job.agent === "claude-code" ? await resolveClaudeAuth(process.env) : null;
|
|
10831
10916
|
const withPlaywright = cfg.effective.box.withPlaywright || cfg.effective.browser.default !== "agent-browser";
|
|
10832
|
-
|
|
10917
|
+
log44.write(`creating box for agent=${job.agent}`);
|
|
10833
10918
|
const result = await createBox({
|
|
10834
10919
|
workspacePath: opts.workspace,
|
|
10835
10920
|
name: opts.name && opts.name.length > 0 ? opts.name : void 0,
|
|
@@ -10851,19 +10936,19 @@ async function runDockerJob(job, log45, onBoxCreated) {
|
|
|
10851
10936
|
portlessStateDir: cfg.effective.portless.stateDir || void 0,
|
|
10852
10937
|
limits: resolveLimits(cfg.effective.box, opts),
|
|
10853
10938
|
projectRoot,
|
|
10854
|
-
onLog: (line) =>
|
|
10939
|
+
onLog: (line) => log44.write(line)
|
|
10855
10940
|
});
|
|
10856
|
-
|
|
10941
|
+
log44.write(`box created: ${result.record.container}`);
|
|
10857
10942
|
onBoxCreated(result.record.id);
|
|
10858
10943
|
await writeJob({ ...job, boxId: result.record.id });
|
|
10859
10944
|
const promptedArgs = buildPromptArgs(job.agent, job.prompt, job.agentArgs);
|
|
10860
10945
|
if (job.agent === "claude-code") {
|
|
10861
|
-
|
|
10946
|
+
log44.write(`checking plugin native deps`);
|
|
10862
10947
|
await rebuildPluginNativeDeps(result.record.container, {
|
|
10863
10948
|
volume: result.record.claudeConfigVolume ?? SHARED_CLAUDE_VOLUME,
|
|
10864
|
-
onProgress: (line) =>
|
|
10949
|
+
onProgress: (line) => log44.write(line)
|
|
10865
10950
|
});
|
|
10866
|
-
|
|
10951
|
+
log44.write(`starting claude session`);
|
|
10867
10952
|
await startClaudeSession({
|
|
10868
10953
|
container: result.record.container,
|
|
10869
10954
|
claudeArgs: applyClaudeSkipPermissions(promptedArgs, cfg.effective),
|
|
@@ -10871,22 +10956,22 @@ async function runDockerJob(job, log45, onBoxCreated) {
|
|
|
10871
10956
|
boxName: result.record.name
|
|
10872
10957
|
});
|
|
10873
10958
|
} else if (job.agent === "codex") {
|
|
10874
|
-
|
|
10959
|
+
log44.write(`checking codex`);
|
|
10875
10960
|
await ensureCodexInstalled(result.record.container, {
|
|
10876
|
-
onProgress: (line) =>
|
|
10961
|
+
onProgress: (line) => log44.write(line)
|
|
10877
10962
|
});
|
|
10878
|
-
|
|
10963
|
+
log44.write(`starting codex session`);
|
|
10879
10964
|
await startCodexSession({
|
|
10880
10965
|
container: result.record.container,
|
|
10881
10966
|
codexArgs: applyCodexSkipPermissions(promptedArgs, cfg.effective),
|
|
10882
10967
|
sessionName: cfg.effective.codex.sessionName
|
|
10883
10968
|
});
|
|
10884
10969
|
} else if (job.agent === "opencode") {
|
|
10885
|
-
|
|
10970
|
+
log44.write(`checking opencode`);
|
|
10886
10971
|
await ensureOpencodeInstalled(result.record.container, {
|
|
10887
|
-
onProgress: (line) =>
|
|
10972
|
+
onProgress: (line) => log44.write(line)
|
|
10888
10973
|
});
|
|
10889
|
-
|
|
10974
|
+
log44.write(`starting opencode session`);
|
|
10890
10975
|
await startOpencodeSession({
|
|
10891
10976
|
container: result.record.container,
|
|
10892
10977
|
opencodeArgs: promptedArgs,
|
|
@@ -10925,7 +11010,7 @@ function buildOverridesFromJob(job) {
|
|
|
10925
11010
|
|
|
10926
11011
|
// src/commands/screen.ts
|
|
10927
11012
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
10928
|
-
import { log as
|
|
11013
|
+
import { log as log36 } from "@clack/prompts";
|
|
10929
11014
|
import { Command as Command36 } from "commander";
|
|
10930
11015
|
var SIGNED_URL_TTL_MIN = 1;
|
|
10931
11016
|
var SIGNED_URL_TTL_MAX = 86400;
|
|
@@ -10956,10 +11041,10 @@ var screenCommand = new Command36("screen").description("Open a box's VNC (noVNC
|
|
|
10956
11041
|
if (provider === "docker") {
|
|
10957
11042
|
const insp = await inspectBox(box.id);
|
|
10958
11043
|
if (insp.state === "paused") {
|
|
10959
|
-
|
|
11044
|
+
log36.info("box is paused; unpausing");
|
|
10960
11045
|
await unpauseBox(box.id);
|
|
10961
11046
|
} else if (insp.state === "stopped") {
|
|
10962
|
-
|
|
11047
|
+
log36.info("box is stopped; starting");
|
|
10963
11048
|
await startBox(box.id);
|
|
10964
11049
|
} else if (insp.state === "missing") {
|
|
10965
11050
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
@@ -10969,13 +11054,13 @@ var screenCommand = new Command36("screen").description("Open a box's VNC (noVNC
|
|
|
10969
11054
|
const inBoxUrl = exposePort !== void 0 ? box.portlessUrl ?? `http://localhost:${String(exposePort)}` : "about:blank";
|
|
10970
11055
|
const br = await ensureBoxBrowser(box.container, void 0, inBoxUrl);
|
|
10971
11056
|
if (br.up && !br.alreadyRunning) {
|
|
10972
|
-
|
|
11057
|
+
log36.info(
|
|
10973
11058
|
exposePort !== void 0 ? `opened ${inBoxUrl} in the in-box browser (visible in the VNC view)` : "started in-box browser"
|
|
10974
11059
|
);
|
|
10975
11060
|
} else if (br.alreadyRunning) {
|
|
10976
|
-
|
|
11061
|
+
log36.info("in-box browser already running; left it untouched");
|
|
10977
11062
|
} else {
|
|
10978
|
-
|
|
11063
|
+
log36.warn(`could not start in-box browser: ${br.reason ?? "unknown"}`);
|
|
10979
11064
|
}
|
|
10980
11065
|
const engine = await detectEngine();
|
|
10981
11066
|
const urls = buildVncUrls(box, engine);
|
|
@@ -10996,10 +11081,10 @@ var screenCommand = new Command36("screen").description("Open a box's VNC (noVNC
|
|
|
10996
11081
|
const p = await providerForBox(box);
|
|
10997
11082
|
const state = await p.probeState(box);
|
|
10998
11083
|
if (state === "paused") {
|
|
10999
|
-
|
|
11084
|
+
log36.info("box is paused; resuming");
|
|
11000
11085
|
await p.resume(box);
|
|
11001
11086
|
} else if (state === "stopped") {
|
|
11002
|
-
|
|
11087
|
+
log36.info("box is stopped; starting");
|
|
11003
11088
|
await p.start(box);
|
|
11004
11089
|
} else if (state === "missing") {
|
|
11005
11090
|
throw new Error(`cloud sandbox for ${box.name} is missing; was it deleted?`);
|
|
@@ -11014,14 +11099,14 @@ var screenCommand = new Command36("screen").description("Open a box's VNC (noVNC
|
|
|
11014
11099
|
user: "vscode"
|
|
11015
11100
|
});
|
|
11016
11101
|
if (br.exitCode === 0) {
|
|
11017
|
-
|
|
11102
|
+
log36.info(`opened ${webUrl} in the in-box browser (visible in the VNC view)`);
|
|
11018
11103
|
} else {
|
|
11019
|
-
|
|
11104
|
+
log36.warn(
|
|
11020
11105
|
`could not open in-box browser (continuing): ${br.stderr.trim() || br.stdout.trim() || `exit ${String(br.exitCode)}`}`
|
|
11021
11106
|
);
|
|
11022
11107
|
}
|
|
11023
11108
|
} catch (err) {
|
|
11024
|
-
|
|
11109
|
+
log36.warn(
|
|
11025
11110
|
`in-box browser skipped: ${err instanceof Error ? err.message : String(err)}`
|
|
11026
11111
|
);
|
|
11027
11112
|
}
|
|
@@ -11034,7 +11119,7 @@ var screenCommand = new Command36("screen").description("Open a box's VNC (noVNC
|
|
|
11034
11119
|
`);
|
|
11035
11120
|
return;
|
|
11036
11121
|
}
|
|
11037
|
-
const opened = spawnSync3(
|
|
11122
|
+
const opened = spawnSync3(hostOpenCommand(), [url], { stdio: "inherit" });
|
|
11038
11123
|
if (opened.status !== 0) {
|
|
11039
11124
|
throw new Error(`open ${url} failed (exit ${String(opened.status ?? "n/a")})`);
|
|
11040
11125
|
}
|
|
@@ -11047,18 +11132,18 @@ var screenCommand = new Command36("screen").description("Open a box's VNC (noVNC
|
|
|
11047
11132
|
|
|
11048
11133
|
// src/commands/shell.ts
|
|
11049
11134
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
11050
|
-
import { log as
|
|
11135
|
+
import { log as log38 } from "@clack/prompts";
|
|
11051
11136
|
import { Command as Command37 } from "commander";
|
|
11052
11137
|
|
|
11053
11138
|
// src/commands/_provider-guard.ts
|
|
11054
|
-
import { log as
|
|
11139
|
+
import { log as log37 } from "@clack/prompts";
|
|
11055
11140
|
function requireDockerProvider(box, commandName) {
|
|
11056
11141
|
const provider = box.provider ?? "docker";
|
|
11057
11142
|
if (provider === "docker") return;
|
|
11058
|
-
|
|
11143
|
+
log37.error(
|
|
11059
11144
|
`\`agentbox ${commandName}\` doesn't yet support cloud boxes (this box's provider is '${provider}').`
|
|
11060
11145
|
);
|
|
11061
|
-
|
|
11146
|
+
log37.info(
|
|
11062
11147
|
"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."
|
|
11063
11148
|
);
|
|
11064
11149
|
process.exit(2);
|
|
@@ -11098,10 +11183,10 @@ function fmtAgo2(iso) {
|
|
|
11098
11183
|
async function ensureBoxRunning(box) {
|
|
11099
11184
|
const insp = await inspectBox(box.id);
|
|
11100
11185
|
if (insp.state === "paused") {
|
|
11101
|
-
|
|
11186
|
+
log38.info("box is paused; unpausing");
|
|
11102
11187
|
await unpauseBox(box.id);
|
|
11103
11188
|
} else if (insp.state === "stopped") {
|
|
11104
|
-
|
|
11189
|
+
log38.info("box is stopped; starting");
|
|
11105
11190
|
await startBox(box.id);
|
|
11106
11191
|
} else if (insp.state === "missing") {
|
|
11107
11192
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
@@ -11140,7 +11225,7 @@ async function startOrAttachShell(box, cfg) {
|
|
|
11140
11225
|
const label = shellLabel(cfg.sessionName);
|
|
11141
11226
|
const info = await shellSessionInfo(box.container, cfg.sessionName, cfg.user);
|
|
11142
11227
|
if (info.running) {
|
|
11143
|
-
|
|
11228
|
+
log38.info(`reattaching to shell "${label}" \u2014 Control+a d to detach`);
|
|
11144
11229
|
} else {
|
|
11145
11230
|
await startShellSession({
|
|
11146
11231
|
container: box.container,
|
|
@@ -11148,7 +11233,7 @@ async function startOrAttachShell(box, cfg) {
|
|
|
11148
11233
|
user: cfg.user,
|
|
11149
11234
|
login: cfg.login
|
|
11150
11235
|
});
|
|
11151
|
-
|
|
11236
|
+
log38.info(`shell "${label}" \u2014 Control+a d to detach, leaves it running`);
|
|
11152
11237
|
}
|
|
11153
11238
|
const code = await runWrappedAttach({
|
|
11154
11239
|
container: box.container,
|
|
@@ -11301,11 +11386,11 @@ var shellLsCommand = new Command37("ls").description("List the shell tmux sessio
|
|
|
11301
11386
|
requireDockerProvider(box, "shell");
|
|
11302
11387
|
const insp = await inspectBox(box.id);
|
|
11303
11388
|
if (insp.state !== "running") {
|
|
11304
|
-
|
|
11389
|
+
log38.info(`box ${box.name} is ${insp.state} \u2014 no live shell sessions`);
|
|
11305
11390
|
return;
|
|
11306
11391
|
}
|
|
11307
11392
|
if (insp.shellSessions.length === 0) {
|
|
11308
|
-
|
|
11393
|
+
log38.info(
|
|
11309
11394
|
`no shell sessions in ${box.name} \u2014 start one with: agentbox shell ${reattachRef4(box)}`
|
|
11310
11395
|
);
|
|
11311
11396
|
return;
|
|
@@ -11325,25 +11410,25 @@ var shellKillCommand = new Command37("kill").description("Kill a shell tmux sess
|
|
|
11325
11410
|
requireDockerProvider(box, "shell");
|
|
11326
11411
|
const insp = await inspectBox(box.id);
|
|
11327
11412
|
if (insp.state !== "running") {
|
|
11328
|
-
|
|
11413
|
+
log38.info(`box ${box.name} is ${insp.state} \u2014 no shell sessions to kill`);
|
|
11329
11414
|
return;
|
|
11330
11415
|
}
|
|
11331
11416
|
if (opts.all) {
|
|
11332
11417
|
if (insp.shellSessions.length === 0) {
|
|
11333
|
-
|
|
11418
|
+
log38.info(`no shell sessions in ${box.name}`);
|
|
11334
11419
|
return;
|
|
11335
11420
|
}
|
|
11336
11421
|
let killed = 0;
|
|
11337
11422
|
for (const s of insp.shellSessions) {
|
|
11338
11423
|
if (await killShellSession(box.container, s.sessionName)) killed++;
|
|
11339
11424
|
}
|
|
11340
|
-
|
|
11425
|
+
log38.success(`killed ${String(killed)} shell session${killed === 1 ? "" : "s"} in ${box.name}`);
|
|
11341
11426
|
return;
|
|
11342
11427
|
}
|
|
11343
11428
|
const target = shellSessionName(opts.name);
|
|
11344
11429
|
const ok = await killShellSession(box.container, target);
|
|
11345
|
-
if (ok)
|
|
11346
|
-
else
|
|
11430
|
+
if (ok) log38.success(`killed shell "${shellLabel(target)}" in ${box.name}`);
|
|
11431
|
+
else log38.warn(`no shell "${shellLabel(target)}" in ${box.name} (already gone?)`);
|
|
11347
11432
|
} catch (err) {
|
|
11348
11433
|
handleLifecycleError(err);
|
|
11349
11434
|
}
|
|
@@ -11377,7 +11462,7 @@ var startCommand = new Command38("start").description(
|
|
|
11377
11462
|
});
|
|
11378
11463
|
|
|
11379
11464
|
// src/commands/status.ts
|
|
11380
|
-
import { log as
|
|
11465
|
+
import { log as log40 } from "@clack/prompts";
|
|
11381
11466
|
import { Command as Command39 } from "commander";
|
|
11382
11467
|
|
|
11383
11468
|
// src/endpoints-render.ts
|
|
@@ -11408,7 +11493,7 @@ function renderEndpointLines(endpoints, stream) {
|
|
|
11408
11493
|
}
|
|
11409
11494
|
|
|
11410
11495
|
// src/commands/inspect.ts
|
|
11411
|
-
import { log as
|
|
11496
|
+
import { log as log39 } from "@clack/prompts";
|
|
11412
11497
|
function fmtLimit(n, unit) {
|
|
11413
11498
|
return n && n > 0 ? `${String(n)}${unit}` : "unlimited";
|
|
11414
11499
|
}
|
|
@@ -11553,7 +11638,7 @@ function renderCodexActivityCloud(persisted) {
|
|
|
11553
11638
|
async function runInspect(box, opts) {
|
|
11554
11639
|
try {
|
|
11555
11640
|
if (opts.json && opts.watch) {
|
|
11556
|
-
|
|
11641
|
+
log39.error("cannot combine --json with --watch");
|
|
11557
11642
|
process.exit(2);
|
|
11558
11643
|
}
|
|
11559
11644
|
const isCloud = (box.provider ?? "docker") !== "docker";
|
|
@@ -11597,7 +11682,7 @@ var statusCommand2 = withWatchOptions(
|
|
|
11597
11682
|
).action(async (idOrName, opts) => {
|
|
11598
11683
|
try {
|
|
11599
11684
|
if (opts.json && opts.watch) {
|
|
11600
|
-
|
|
11685
|
+
log40.error("cannot combine --json with --watch");
|
|
11601
11686
|
process.exit(2);
|
|
11602
11687
|
}
|
|
11603
11688
|
const box = await resolveBoxOrExit(idOrName);
|
|
@@ -11926,7 +12011,7 @@ var unpauseCommand = new Command42("unpause").description(
|
|
|
11926
12011
|
|
|
11927
12012
|
// src/commands/update.ts
|
|
11928
12013
|
import { spawn as spawn4 } from "child_process";
|
|
11929
|
-
import { confirm as confirm16, intro as intro8, isCancel as isCancel17, log as
|
|
12014
|
+
import { confirm as confirm16, intro as intro8, isCancel as isCancel17, log as log41, outro as outro7, spinner as spinner10 } from "@clack/prompts";
|
|
11930
12015
|
import { Command as Command43 } from "commander";
|
|
11931
12016
|
|
|
11932
12017
|
// src/exec-method.ts
|
|
@@ -11981,7 +12066,7 @@ var updateCommand = new Command43("self-update").description(
|
|
|
11981
12066
|
});
|
|
11982
12067
|
intro8("agentbox self-update");
|
|
11983
12068
|
const selfStep = opts.skipSelf ? "self-update: skipped (--skip-self)" : describeSelfUpdate(method);
|
|
11984
|
-
|
|
12069
|
+
log41.info(
|
|
11985
12070
|
[
|
|
11986
12071
|
"plan:",
|
|
11987
12072
|
` ${selfStep}`,
|
|
@@ -11990,31 +12075,31 @@ var updateCommand = new Command43("self-update").description(
|
|
|
11990
12075
|
].join("\n")
|
|
11991
12076
|
);
|
|
11992
12077
|
if (opts.dryRun) {
|
|
11993
|
-
|
|
12078
|
+
outro7("dry run \u2014 nothing changed");
|
|
11994
12079
|
return;
|
|
11995
12080
|
}
|
|
11996
12081
|
if (!opts.yes) {
|
|
11997
12082
|
const ok = await confirm16({ message: "Proceed with update?", initialValue: true });
|
|
11998
12083
|
if (isCancel17(ok) || !ok) {
|
|
11999
|
-
|
|
12084
|
+
log41.info("cancelled");
|
|
12000
12085
|
return;
|
|
12001
12086
|
}
|
|
12002
12087
|
}
|
|
12003
12088
|
let selfUpdated = false;
|
|
12004
12089
|
if (opts.skipSelf) {
|
|
12005
|
-
|
|
12090
|
+
log41.info("skipping self-update (--skip-self)");
|
|
12006
12091
|
} else {
|
|
12007
12092
|
const cmd = selfUpdateCommand(method);
|
|
12008
12093
|
if (cmd === null) {
|
|
12009
|
-
|
|
12094
|
+
log41.info(describeSelfUpdate(method));
|
|
12010
12095
|
} else {
|
|
12011
|
-
|
|
12096
|
+
log41.info(`running: ${cmd.cmd} ${cmd.args.join(" ")}`);
|
|
12012
12097
|
const code = await runInherit(cmd.cmd, cmd.args);
|
|
12013
12098
|
if (code !== 0) {
|
|
12014
12099
|
throw new Error(`${cmd.cmd} exited with code ${String(code)}`);
|
|
12015
12100
|
}
|
|
12016
12101
|
selfUpdated = true;
|
|
12017
|
-
|
|
12102
|
+
log41.success(`updated ${PKG} via ${cmd.cmd}`);
|
|
12018
12103
|
}
|
|
12019
12104
|
}
|
|
12020
12105
|
const s = spinner10();
|
|
@@ -12030,7 +12115,7 @@ var updateCommand = new Command43("self-update").description(
|
|
|
12030
12115
|
stop.stopped ? `stopped relay (pid ${String(stop.pid)})` : "relay was not running"
|
|
12031
12116
|
);
|
|
12032
12117
|
if (selfUpdated) {
|
|
12033
|
-
|
|
12118
|
+
log41.info(
|
|
12034
12119
|
"relay will restart automatically (with the updated build) on your next `agentbox create` / `agentbox claude`"
|
|
12035
12120
|
);
|
|
12036
12121
|
} else {
|
|
@@ -12041,12 +12126,12 @@ var updateCommand = new Command43("self-update").description(
|
|
|
12041
12126
|
sr2.stop(`relay back up on ${ep.hostUrl}`);
|
|
12042
12127
|
} catch (err) {
|
|
12043
12128
|
sr2.stop("relay restart failed");
|
|
12044
|
-
|
|
12129
|
+
log41.warn(
|
|
12045
12130
|
`${err instanceof Error ? err.message : String(err)} \u2014 it will retry on the next box command`
|
|
12046
12131
|
);
|
|
12047
12132
|
}
|
|
12048
12133
|
}
|
|
12049
|
-
|
|
12134
|
+
outro7("update complete");
|
|
12050
12135
|
} catch (err) {
|
|
12051
12136
|
handleLifecycleError(err);
|
|
12052
12137
|
}
|
|
@@ -12054,7 +12139,7 @@ var updateCommand = new Command43("self-update").description(
|
|
|
12054
12139
|
|
|
12055
12140
|
// src/commands/url.ts
|
|
12056
12141
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
12057
|
-
import { log as
|
|
12142
|
+
import { log as log42 } from "@clack/prompts";
|
|
12058
12143
|
import { Command as Command44 } from "commander";
|
|
12059
12144
|
var SIGNED_URL_TTL_MIN2 = 1;
|
|
12060
12145
|
var SIGNED_URL_TTL_MAX2 = 86400;
|
|
@@ -12087,10 +12172,10 @@ var urlCommand = new Command44("url").description(
|
|
|
12087
12172
|
if (provider === "docker") {
|
|
12088
12173
|
const insp = await inspectBox(box.id);
|
|
12089
12174
|
if (insp.state === "paused") {
|
|
12090
|
-
|
|
12175
|
+
log42.info("box is paused; unpausing");
|
|
12091
12176
|
await unpauseBox(box.id);
|
|
12092
12177
|
} else if (insp.state === "stopped") {
|
|
12093
|
-
|
|
12178
|
+
log42.info("box is stopped; starting");
|
|
12094
12179
|
await startBox(box.id);
|
|
12095
12180
|
} else if (insp.state === "missing") {
|
|
12096
12181
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
@@ -12119,10 +12204,10 @@ var urlCommand = new Command44("url").description(
|
|
|
12119
12204
|
const p = await providerForBox(box);
|
|
12120
12205
|
const state = await p.probeState(box);
|
|
12121
12206
|
if (state === "paused") {
|
|
12122
|
-
|
|
12207
|
+
log42.info("box is paused; resuming");
|
|
12123
12208
|
await p.resume(box);
|
|
12124
12209
|
} else if (state === "stopped") {
|
|
12125
|
-
|
|
12210
|
+
log42.info("box is stopped; starting");
|
|
12126
12211
|
await p.start(box);
|
|
12127
12212
|
} else if (state === "missing") {
|
|
12128
12213
|
throw new Error(`cloud sandbox for ${box.name} is missing; was it deleted?`);
|
|
@@ -12134,7 +12219,7 @@ var urlCommand = new Command44("url").description(
|
|
|
12134
12219
|
`);
|
|
12135
12220
|
return;
|
|
12136
12221
|
}
|
|
12137
|
-
const opened = spawnSync5(
|
|
12222
|
+
const opened = spawnSync5(hostOpenCommand(), [url], { stdio: "inherit" });
|
|
12138
12223
|
if (opened.status !== 0) {
|
|
12139
12224
|
throw new Error(`open ${url} failed (exit ${String(opened.status ?? "n/a")})`);
|
|
12140
12225
|
}
|
|
@@ -12146,7 +12231,7 @@ var urlCommand = new Command44("url").description(
|
|
|
12146
12231
|
});
|
|
12147
12232
|
|
|
12148
12233
|
// src/commands/wait.ts
|
|
12149
|
-
import { log as
|
|
12234
|
+
import { log as log43 } from "@clack/prompts";
|
|
12150
12235
|
import { Command as Command45 } from "commander";
|
|
12151
12236
|
var waitCommand = new Command45("wait").description("Block until the box reports all autostart units ready").argument(
|
|
12152
12237
|
"[box]",
|
|
@@ -12164,7 +12249,7 @@ var waitCommand = new Command45("wait").description("Block until the box reports
|
|
|
12164
12249
|
try {
|
|
12165
12250
|
parsed = JSON.parse(proc.stdout);
|
|
12166
12251
|
} catch {
|
|
12167
|
-
|
|
12252
|
+
log43.error(`agentbox-ctl wait-ready failed: ${proc.stderr || proc.stdout}`);
|
|
12168
12253
|
process.exit(1);
|
|
12169
12254
|
}
|
|
12170
12255
|
if (opts.json) {
|