@madarco/agentbox 0.10.1 → 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 +34 -0
- package/dist/{_cloud-attach-2DGI6FUA.js → _cloud-attach-6C5NMOHD.js} +4 -4
- package/dist/{chunk-M2UWJKFA.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-CDKVD6UO.js → chunk-GUNUBIRB.js} +69 -66
- package/dist/chunk-GUNUBIRB.js.map +1 -0
- package/dist/{chunk-I7NOGCL4.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-PWUVHPN6.js → chunk-SNTHHWKY.js} +7 -3
- 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-BNI5PQYK.js → dist-4SUIXKSD.js} +5 -5
- package/dist/{dist-SJHY3HYN.js → dist-HT2YV6PB.js} +5 -5
- package/dist/{dist-BD5QJRDC.js → dist-PJFJNXO2.js} +5 -5
- package/dist/{dist-SBCQVFCE.js → dist-ZEGIMYWZ.js} +3 -3
- package/dist/index.js +618 -545
- package/dist/index.js.map +1 -1
- package/dist/{prepared-state-MQHD3M5F-O5M4NIN4.js → prepared-state-MQHD3M5F-KE4DT3GX.js} +2 -2
- package/package.json +6 -6
- package/runtime/docker/packages/ctl/dist/bin.cjs +5 -2
- package/runtime/hetzner/ctl.cjs +5 -2
- package/runtime/relay/bin.cjs +5 -2
- package/runtime/vercel/ctl.cjs +5 -2
- package/runtime/vercel/scripts/provision.sh +20 -0
- package/dist/chunk-CDKVD6UO.js.map +0 -1
- package/dist/chunk-I24B6AXR.js.map +0 -1
- package/dist/chunk-I7NOGCL4.js.map +0 -1
- package/dist/chunk-M2UWJKFA.js.map +0 -1
- package/dist/chunk-MTVI44DW.js.map +0 -1
- package/dist/chunk-PWUVHPN6.js.map +0 -1
- /package/dist/{_cloud-attach-2DGI6FUA.js.map → _cloud-attach-6C5NMOHD.js.map} +0 -0
- /package/dist/{dist-BNI5PQYK.js.map → dist-4SUIXKSD.js.map} +0 -0
- /package/dist/{dist-SJHY3HYN.js.map → dist-HT2YV6PB.js.map} +0 -0
- /package/dist/{dist-BD5QJRDC.js.map → dist-PJFJNXO2.js.map} +0 -0
- /package/dist/{dist-SBCQVFCE.js.map → dist-ZEGIMYWZ.js.map} +0 -0
- /package/dist/{prepared-state-MQHD3M5F-O5M4NIN4.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) {
|
|
@@ -5215,13 +5272,13 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5215
5272
|
onLog: (line) => cmdLog.write(line)
|
|
5216
5273
|
});
|
|
5217
5274
|
if (gate.decision === "cancel") {
|
|
5218
|
-
|
|
5275
|
+
log14.warn("carry: cancelled \u2014 not creating the box");
|
|
5219
5276
|
cmdLog.close();
|
|
5220
5277
|
process.exit(0);
|
|
5221
5278
|
}
|
|
5222
5279
|
if (gate.decision === "approve") carryEntries = gate.entries;
|
|
5223
5280
|
} catch (err) {
|
|
5224
|
-
|
|
5281
|
+
log14.error(err instanceof Error ? err.message : String(err));
|
|
5225
5282
|
cmdLog.close();
|
|
5226
5283
|
process.exit(1);
|
|
5227
5284
|
}
|
|
@@ -5269,7 +5326,7 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5269
5326
|
} catch (err) {
|
|
5270
5327
|
if (err instanceof FromBranchError || err instanceof UseBranchError) {
|
|
5271
5328
|
s.stop("aborting: invalid branch selection");
|
|
5272
|
-
|
|
5329
|
+
log14.error(err.message);
|
|
5273
5330
|
cmdLog.close();
|
|
5274
5331
|
process.exit(2);
|
|
5275
5332
|
}
|
|
@@ -5311,17 +5368,17 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5311
5368
|
}
|
|
5312
5369
|
});
|
|
5313
5370
|
s.stop(`box ${result.record.container} ready`);
|
|
5314
|
-
|
|
5371
|
+
log14.info(`id: ${result.record.id}`);
|
|
5315
5372
|
if (typeof result.record.projectIndex === "number") {
|
|
5316
|
-
|
|
5373
|
+
log14.info(`n: ${String(result.record.projectIndex)} (in ${projectRoot})`);
|
|
5317
5374
|
}
|
|
5318
|
-
|
|
5319
|
-
|
|
5375
|
+
log14.info(`container: ${result.record.container}`);
|
|
5376
|
+
log14.info(`image: ${result.record.image}${result.imageBuilt ? " (built just now)" : ""}`);
|
|
5320
5377
|
if (result.record.snapshotDir) {
|
|
5321
|
-
|
|
5378
|
+
log14.info(`snapshot: ${result.record.snapshotDir}`);
|
|
5322
5379
|
}
|
|
5323
5380
|
if (result.record.checkpointSource) {
|
|
5324
|
-
|
|
5381
|
+
log14.info(
|
|
5325
5382
|
`checkpoint: ${result.record.checkpointSource.ref} (${result.record.checkpointSource.type}) \u2192 ${result.record.checkpointImage ?? "(missing)"}`
|
|
5326
5383
|
);
|
|
5327
5384
|
}
|
|
@@ -5333,7 +5390,7 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5333
5390
|
` agentbox claude attach ${result.record.name}`,
|
|
5334
5391
|
` agentbox url ${result.record.name}`
|
|
5335
5392
|
];
|
|
5336
|
-
|
|
5393
|
+
log14.message(
|
|
5337
5394
|
[
|
|
5338
5395
|
"",
|
|
5339
5396
|
"Try it:",
|
|
@@ -5352,7 +5409,7 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5352
5409
|
const protectedPaths = boxes.map((b) => b.projectRoot).filter((p) => typeof p === "string");
|
|
5353
5410
|
const res = await pruneOrphanProjectConfigs({ protectedPaths });
|
|
5354
5411
|
if (res.removed.length > 0) {
|
|
5355
|
-
|
|
5412
|
+
log14.info(
|
|
5356
5413
|
`cleaned ${String(res.removed.length)} orphan project config dir(s): ` + res.removed.map((r) => r.originalPath).join(", ")
|
|
5357
5414
|
);
|
|
5358
5415
|
}
|
|
@@ -5360,9 +5417,9 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5360
5417
|
} catch {
|
|
5361
5418
|
}
|
|
5362
5419
|
}
|
|
5363
|
-
|
|
5420
|
+
outro4("done");
|
|
5364
5421
|
if (attachClaudeAfter) {
|
|
5365
|
-
const { cloudAgentAttach: cloudAgentAttach2 } = await import("./_cloud-attach-
|
|
5422
|
+
const { cloudAgentAttach: cloudAgentAttach2 } = await import("./_cloud-attach-6C5NMOHD.js");
|
|
5366
5423
|
await cloudAgentAttach2({
|
|
5367
5424
|
box: result.record,
|
|
5368
5425
|
binary: "claude",
|
|
@@ -5378,14 +5435,14 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5378
5435
|
s.stop("failed");
|
|
5379
5436
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5380
5437
|
cmdLog.write(`FAIL: ${err instanceof Error ? err.stack ?? err.message : String(err)}`);
|
|
5381
|
-
|
|
5438
|
+
log14.error(msg);
|
|
5382
5439
|
try {
|
|
5383
5440
|
const running = execSync('docker ps --format "{{.Names}}"', {
|
|
5384
5441
|
stdio: ["ignore", "pipe", "ignore"]
|
|
5385
5442
|
}).toString().split("\n").filter((n) => n.startsWith("agentbox-"));
|
|
5386
5443
|
if (running.length > 0) {
|
|
5387
|
-
|
|
5388
|
-
|
|
5444
|
+
log14.warn(`leftover containers: ${running.join(", ")}`);
|
|
5445
|
+
log14.warn(`remove with: docker rm -f ${running.join(" ")}`);
|
|
5389
5446
|
}
|
|
5390
5447
|
} catch {
|
|
5391
5448
|
}
|
|
@@ -5398,7 +5455,7 @@ var createCommand = new Command9("create").description("Create and start a new a
|
|
|
5398
5455
|
|
|
5399
5456
|
// src/commands/dashboard.ts
|
|
5400
5457
|
import { spawn as spawn2 } from "child_process";
|
|
5401
|
-
import { log as
|
|
5458
|
+
import { log as log15 } from "@clack/prompts";
|
|
5402
5459
|
import { Command as Command10 } from "commander";
|
|
5403
5460
|
|
|
5404
5461
|
// src/dashboard/layout.ts
|
|
@@ -6922,7 +6979,7 @@ function toSidebar(b) {
|
|
|
6922
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) => {
|
|
6923
6980
|
try {
|
|
6924
6981
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
6925
|
-
|
|
6982
|
+
log15.error("agentbox dashboard needs an interactive terminal");
|
|
6926
6983
|
process.exit(2);
|
|
6927
6984
|
}
|
|
6928
6985
|
const backend = await loadPtyBackend();
|
|
@@ -6932,10 +6989,10 @@ var dashboardCommand = new Command10("dashboard").description("Box list + the se
|
|
|
6932
6989
|
ptySpawn = backend.ptySpawn;
|
|
6933
6990
|
termCtor = backend.termCtor;
|
|
6934
6991
|
} else {
|
|
6935
|
-
|
|
6992
|
+
log15.error(
|
|
6936
6993
|
"agentbox dashboard is unavailable here (native terminal backend failed to load)"
|
|
6937
6994
|
);
|
|
6938
|
-
|
|
6995
|
+
log15.info("use `agentbox claude` / `agentbox claude attach` instead");
|
|
6939
6996
|
process.exit(2);
|
|
6940
6997
|
}
|
|
6941
6998
|
const project = await findProjectRoot(process.cwd());
|
|
@@ -7247,9 +7304,9 @@ var dashboardCommand = new Command10("dashboard").description("Box list + the se
|
|
|
7247
7304
|
if (!br.up) return `VNC: in-box browser unavailable (${br.reason ?? "box not running?"})`;
|
|
7248
7305
|
} catch {
|
|
7249
7306
|
}
|
|
7250
|
-
detach(
|
|
7307
|
+
detach(hostOpenCommand(), [url]);
|
|
7251
7308
|
if (exposedWebUrl) {
|
|
7252
|
-
detach(
|
|
7309
|
+
detach(hostOpenCommand(), [exposedWebUrl]);
|
|
7253
7310
|
return "Opening VNC + web in browser\u2026";
|
|
7254
7311
|
}
|
|
7255
7312
|
return "Opening VNC in browser\u2026";
|
|
@@ -7258,7 +7315,7 @@ var dashboardCommand = new Command10("dashboard").description("Box list + the se
|
|
|
7258
7315
|
const box = (await listBoxes()).find((b) => b.id === boxId);
|
|
7259
7316
|
if (!box) return "box not found";
|
|
7260
7317
|
const { url } = webTarget(box);
|
|
7261
|
-
detach(
|
|
7318
|
+
detach(hostOpenCommand(), [url]);
|
|
7262
7319
|
return `Opening ${url.replace(/^https?:\/\//, "")}\u2026`;
|
|
7263
7320
|
};
|
|
7264
7321
|
const openCode = async (boxId) => {
|
|
@@ -7340,11 +7397,11 @@ var dashboardCommand = new Command10("dashboard").description("Box list + the se
|
|
|
7340
7397
|
});
|
|
7341
7398
|
|
|
7342
7399
|
// ../../packages/sandbox-daytona/dist/cli.js
|
|
7343
|
-
import { log as
|
|
7400
|
+
import { log as log16, spinner as spinner6 } from "@clack/prompts";
|
|
7344
7401
|
import { Command as Command11 } from "commander";
|
|
7345
7402
|
function reportError(err) {
|
|
7346
7403
|
const message = err instanceof Error ? err.message : String(err);
|
|
7347
|
-
|
|
7404
|
+
log16.error(message);
|
|
7348
7405
|
process.exitCode = 1;
|
|
7349
7406
|
}
|
|
7350
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) => {
|
|
@@ -7403,7 +7460,7 @@ var resyncSub = new Command11("resync").description(
|
|
|
7403
7460
|
const sb = spinner6();
|
|
7404
7461
|
sb.start(`provisioning throwaway sandbox to refresh: ${agents.join(", ")}`);
|
|
7405
7462
|
const ensured = await ensureAgentVolumesForCloud(daytonaBackend, {
|
|
7406
|
-
onLog: (line) =>
|
|
7463
|
+
onLog: (line) => log16.info(line)
|
|
7407
7464
|
});
|
|
7408
7465
|
if (ensured.agents.length === 0) {
|
|
7409
7466
|
sb.stop("no agent volumes available \u2014 the daytona backend has no volume primitive");
|
|
@@ -7443,7 +7500,7 @@ var resyncSub = new Command11("resync").description(
|
|
|
7443
7500
|
}
|
|
7444
7501
|
sb3.stop("throwaway sandbox destroyed");
|
|
7445
7502
|
}
|
|
7446
|
-
|
|
7503
|
+
log16.success(
|
|
7447
7504
|
`Daytona agent volumes refreshed: ${agents.join(", ")}. Next \`agentbox create --provider daytona\` will use the updated credentials.`
|
|
7448
7505
|
);
|
|
7449
7506
|
} catch (err) {
|
|
@@ -7463,11 +7520,11 @@ var dockerCommand = new Command12("docker").description(
|
|
|
7463
7520
|
});
|
|
7464
7521
|
|
|
7465
7522
|
// ../../packages/sandbox-hetzner/dist/cli.js
|
|
7466
|
-
import { log as
|
|
7523
|
+
import { log as log17 } from "@clack/prompts";
|
|
7467
7524
|
import { Command as Command13 } from "commander";
|
|
7468
7525
|
function reportError2(err) {
|
|
7469
7526
|
const message = err instanceof Error ? err.message : String(err);
|
|
7470
|
-
|
|
7527
|
+
log17.error(message);
|
|
7471
7528
|
process.exitCode = 1;
|
|
7472
7529
|
}
|
|
7473
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) => {
|
|
@@ -7611,11 +7668,11 @@ var hetznerCommand = new Command13("hetzner").description(
|
|
|
7611
7668
|
).addCommand(loginSub2, { isDefault: true }).addCommand(firewallSub);
|
|
7612
7669
|
|
|
7613
7670
|
// ../../packages/sandbox-vercel/dist/cli.js
|
|
7614
|
-
import { log as
|
|
7671
|
+
import { log as log18 } from "@clack/prompts";
|
|
7615
7672
|
import { Command as Command14 } from "commander";
|
|
7616
7673
|
function reportError3(err) {
|
|
7617
7674
|
const message = err instanceof Error ? err.message : String(err);
|
|
7618
|
-
|
|
7675
|
+
log18.error(message);
|
|
7619
7676
|
process.exitCode = 1;
|
|
7620
7677
|
}
|
|
7621
7678
|
function relativeExpiry(expiresAt) {
|
|
@@ -7686,7 +7743,7 @@ var vercelCommand = new Command14("vercel").description(
|
|
|
7686
7743
|
).addCommand(loginSub3, { isDefault: true });
|
|
7687
7744
|
|
|
7688
7745
|
// src/commands/destroy.ts
|
|
7689
|
-
import { confirm as confirm7, isCancel as isCancel8, log as
|
|
7746
|
+
import { confirm as confirm7, isCancel as isCancel8, log as log19 } from "@clack/prompts";
|
|
7690
7747
|
import { Command as Command15 } from "commander";
|
|
7691
7748
|
var destroyCommand = new Command15("destroy").alias("rm").description("Destroy a box and discard its container writable layer (where /workspace lived)").argument(
|
|
7692
7749
|
"[box]",
|
|
@@ -7695,7 +7752,7 @@ var destroyCommand = new Command15("destroy").alias("rm").description("Destroy a
|
|
|
7695
7752
|
try {
|
|
7696
7753
|
const box = await resolveBoxOrExit(idOrName);
|
|
7697
7754
|
if (!opts.yes) {
|
|
7698
|
-
|
|
7755
|
+
log19.warn("Will also wipe the box volume and agent work-in-progress");
|
|
7699
7756
|
const rootBranch = box.gitWorktrees?.find((w) => w.kind === "root")?.branch;
|
|
7700
7757
|
const lines = [box.name];
|
|
7701
7758
|
if (rootBranch) lines.push(`branch: ${rootBranch}`);
|
|
@@ -7703,13 +7760,13 @@ var destroyCommand = new Command15("destroy").alias("rm").description("Destroy a
|
|
|
7703
7760
|
if (box.snapshotDir) {
|
|
7704
7761
|
lines.push(`snapshot: ${box.snapshotDir}${opts.keepSnapshot ? " (will be kept)" : ""}`);
|
|
7705
7762
|
}
|
|
7706
|
-
|
|
7763
|
+
log19.info(lines.join("\n"));
|
|
7707
7764
|
const ok = await confirm7({
|
|
7708
7765
|
message: "Destroy this box?",
|
|
7709
7766
|
initialValue: false
|
|
7710
7767
|
});
|
|
7711
7768
|
if (isCancel8(ok) || !ok) {
|
|
7712
|
-
|
|
7769
|
+
log19.info("cancelled");
|
|
7713
7770
|
return;
|
|
7714
7771
|
}
|
|
7715
7772
|
}
|
|
@@ -7742,11 +7799,11 @@ var destroyCommand = new Command15("destroy").alias("rm").description("Destroy a
|
|
|
7742
7799
|
});
|
|
7743
7800
|
|
|
7744
7801
|
// src/commands/download.ts
|
|
7745
|
-
import { confirm as confirm13, isCancel as isCancel14, log as
|
|
7802
|
+
import { confirm as confirm13, isCancel as isCancel14, log as log25 } from "@clack/prompts";
|
|
7746
7803
|
import { Command as Command21 } from "commander";
|
|
7747
7804
|
|
|
7748
7805
|
// src/commands/download-claude.ts
|
|
7749
|
-
import { confirm as confirm8, isCancel as isCancel9, log as
|
|
7806
|
+
import { confirm as confirm8, isCancel as isCancel9, log as log20 } from "@clack/prompts";
|
|
7750
7807
|
import { Command as Command16 } from "commander";
|
|
7751
7808
|
function tag(item) {
|
|
7752
7809
|
const noun = item.category === "plugins" ? "plugin" : item.category.replace(/s$/, "");
|
|
@@ -7762,7 +7819,7 @@ var downloadClaudeCommand = new Command16("claude").description(
|
|
|
7762
7819
|
const box = await resolveBoxOrExit(idOrName);
|
|
7763
7820
|
const volume = box.claudeConfigVolume ?? resolveClaudeVolume({ isolate: false, boxId: box.id }).volume;
|
|
7764
7821
|
if (volume === SHARED_CLAUDE_VOLUME) {
|
|
7765
|
-
|
|
7822
|
+
log20.warn(
|
|
7766
7823
|
`Reading the shared ${SHARED_CLAUDE_VOLUME} volume \u2014 it aggregates Claude extensions installed in ANY box, not just ${box.name}.`
|
|
7767
7824
|
);
|
|
7768
7825
|
}
|
|
@@ -7792,7 +7849,7 @@ var downloadClaudeCommand = new Command16("claude").description(
|
|
|
7792
7849
|
initialValue: false
|
|
7793
7850
|
});
|
|
7794
7851
|
if (isCancel9(ok) || !ok) {
|
|
7795
|
-
|
|
7852
|
+
log20.info("cancelled");
|
|
7796
7853
|
return;
|
|
7797
7854
|
}
|
|
7798
7855
|
}
|
|
@@ -7807,7 +7864,7 @@ var downloadClaudeCommand = new Command16("claude").description(
|
|
|
7807
7864
|
});
|
|
7808
7865
|
|
|
7809
7866
|
// src/commands/download-codex.ts
|
|
7810
|
-
import { confirm as confirm9, isCancel as isCancel10, log as
|
|
7867
|
+
import { confirm as confirm9, isCancel as isCancel10, log as log21 } from "@clack/prompts";
|
|
7811
7868
|
import { Command as Command17 } from "commander";
|
|
7812
7869
|
var downloadCodexCommand = new Command17("codex").description(
|
|
7813
7870
|
"Download box-side Codex config/auth (config.toml, auth.json, prompts) back to host ~/.codex (additive)"
|
|
@@ -7819,7 +7876,7 @@ var downloadCodexCommand = new Command17("codex").description(
|
|
|
7819
7876
|
const box = await resolveBoxOrExit(idOrName);
|
|
7820
7877
|
const volume = box.codexConfigVolume ?? resolveCodexVolume({ isolate: false, boxId: box.id }).volume;
|
|
7821
7878
|
if (volume === SHARED_CODEX_VOLUME) {
|
|
7822
|
-
|
|
7879
|
+
log21.warn(
|
|
7823
7880
|
`Reading the shared ${SHARED_CODEX_VOLUME} volume \u2014 it aggregates Codex config from ANY box, not just ${box.name}.`
|
|
7824
7881
|
);
|
|
7825
7882
|
}
|
|
@@ -7845,7 +7902,7 @@ var downloadCodexCommand = new Command17("codex").description(
|
|
|
7845
7902
|
initialValue: false
|
|
7846
7903
|
});
|
|
7847
7904
|
if (isCancel10(ok) || !ok) {
|
|
7848
|
-
|
|
7905
|
+
log21.info("cancelled");
|
|
7849
7906
|
return;
|
|
7850
7907
|
}
|
|
7851
7908
|
}
|
|
@@ -7858,7 +7915,7 @@ var downloadCodexCommand = new Command17("codex").description(
|
|
|
7858
7915
|
});
|
|
7859
7916
|
|
|
7860
7917
|
// src/commands/download-opencode.ts
|
|
7861
|
-
import { confirm as confirm10, isCancel as isCancel11, log as
|
|
7918
|
+
import { confirm as confirm10, isCancel as isCancel11, log as log22 } from "@clack/prompts";
|
|
7862
7919
|
import { Command as Command18 } from "commander";
|
|
7863
7920
|
var downloadOpencodeCommand = new Command18("opencode").description(
|
|
7864
7921
|
"Download box-side OpenCode config/auth (auth.json, opencode.json, agents, commands, themes) back to host ~/.config + ~/.local/share opencode (additive)"
|
|
@@ -7870,7 +7927,7 @@ var downloadOpencodeCommand = new Command18("opencode").description(
|
|
|
7870
7927
|
const box = await resolveBoxOrExit(idOrName);
|
|
7871
7928
|
const volume = box.opencodeConfigVolume ?? resolveOpencodeVolume({ isolate: false, boxId: box.id }).volume;
|
|
7872
7929
|
if (volume === SHARED_OPENCODE_VOLUME) {
|
|
7873
|
-
|
|
7930
|
+
log22.warn(
|
|
7874
7931
|
`Reading the shared ${SHARED_OPENCODE_VOLUME} volume \u2014 it aggregates OpenCode config from ANY box, not just ${box.name}.`
|
|
7875
7932
|
);
|
|
7876
7933
|
}
|
|
@@ -7896,7 +7953,7 @@ var downloadOpencodeCommand = new Command18("opencode").description(
|
|
|
7896
7953
|
initialValue: false
|
|
7897
7954
|
});
|
|
7898
7955
|
if (isCancel11(ok) || !ok) {
|
|
7899
|
-
|
|
7956
|
+
log22.info("cancelled");
|
|
7900
7957
|
return;
|
|
7901
7958
|
}
|
|
7902
7959
|
}
|
|
@@ -7909,7 +7966,7 @@ var downloadOpencodeCommand = new Command18("opencode").description(
|
|
|
7909
7966
|
});
|
|
7910
7967
|
|
|
7911
7968
|
// src/commands/download-config.ts
|
|
7912
|
-
import { confirm as confirm11, isCancel as isCancel12, log as
|
|
7969
|
+
import { confirm as confirm11, isCancel as isCancel12, log as log23 } from "@clack/prompts";
|
|
7913
7970
|
import { Command as Command19 } from "commander";
|
|
7914
7971
|
function tagChange(line) {
|
|
7915
7972
|
const sp = line.indexOf(" ");
|
|
@@ -7927,15 +7984,15 @@ var downloadConfigCommand = new Command19("config").description("Download agentb
|
|
|
7927
7984
|
const box = await resolveBoxOrExit(idOrName);
|
|
7928
7985
|
const insp = await inspectBox(box.id);
|
|
7929
7986
|
if (insp.state === "paused") {
|
|
7930
|
-
|
|
7987
|
+
log23.info("box is paused; unpausing");
|
|
7931
7988
|
await unpauseBox(box.id);
|
|
7932
7989
|
} else if (insp.state === "stopped") {
|
|
7933
|
-
|
|
7990
|
+
log23.info("box is stopped; starting");
|
|
7934
7991
|
await startBox(box.id);
|
|
7935
7992
|
} else if (insp.state === "missing") {
|
|
7936
7993
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
7937
7994
|
}
|
|
7938
|
-
|
|
7995
|
+
log23.info(`agentbox.yaml bypasses gitignore and copies directly into ${box.workspacePath}`);
|
|
7939
7996
|
const preview = await pullToHost(box, {
|
|
7940
7997
|
dryRun: true,
|
|
7941
7998
|
respectGitignore: false,
|
|
@@ -7963,7 +8020,7 @@ var downloadConfigCommand = new Command19("config").description("Download agentb
|
|
|
7963
8020
|
initialValue: false
|
|
7964
8021
|
});
|
|
7965
8022
|
if (isCancel12(ok) || !ok) {
|
|
7966
|
-
|
|
8023
|
+
log23.info("cancelled");
|
|
7967
8024
|
return;
|
|
7968
8025
|
}
|
|
7969
8026
|
}
|
|
@@ -7985,7 +8042,7 @@ var downloadConfigCommand = new Command19("config").description("Download agentb
|
|
|
7985
8042
|
});
|
|
7986
8043
|
|
|
7987
8044
|
// src/commands/download-env.ts
|
|
7988
|
-
import { confirm as confirm12, isCancel as isCancel13, log as
|
|
8045
|
+
import { confirm as confirm12, isCancel as isCancel13, log as log24 } from "@clack/prompts";
|
|
7989
8046
|
import { Command as Command20 } from "commander";
|
|
7990
8047
|
function tagChange2(line) {
|
|
7991
8048
|
const sp = line.indexOf(" ");
|
|
@@ -8009,15 +8066,15 @@ var downloadEnvCommand = new Command20("env").description(
|
|
|
8009
8066
|
const box = await resolveBoxOrExit(idOrName);
|
|
8010
8067
|
const insp = await inspectBox(box.id);
|
|
8011
8068
|
if (insp.state === "paused") {
|
|
8012
|
-
|
|
8069
|
+
log24.info("box is paused; unpausing");
|
|
8013
8070
|
await unpauseBox(box.id);
|
|
8014
8071
|
} else if (insp.state === "stopped") {
|
|
8015
|
-
|
|
8072
|
+
log24.info("box is stopped; starting");
|
|
8016
8073
|
await startBox(box.id);
|
|
8017
8074
|
} else if (insp.state === "missing") {
|
|
8018
8075
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
8019
8076
|
}
|
|
8020
|
-
|
|
8077
|
+
log24.info(
|
|
8021
8078
|
`env/config files bypass gitignore and copy directly into ${box.workspacePath}`
|
|
8022
8079
|
);
|
|
8023
8080
|
const patterns = [...DEFAULT_ENV_PATTERNS, ...opts.pattern];
|
|
@@ -8048,7 +8105,7 @@ var downloadEnvCommand = new Command20("env").description(
|
|
|
8048
8105
|
initialValue: false
|
|
8049
8106
|
});
|
|
8050
8107
|
if (isCancel13(ok) || !ok) {
|
|
8051
|
-
|
|
8108
|
+
log24.info("cancelled");
|
|
8052
8109
|
return;
|
|
8053
8110
|
}
|
|
8054
8111
|
}
|
|
@@ -8096,7 +8153,7 @@ var downloadCommand = new Command21("download").enablePositionalOptions().descri
|
|
|
8096
8153
|
throw new Error("cloud download does not yet support --dry-run; omit to bulk-pull /workspace.");
|
|
8097
8154
|
}
|
|
8098
8155
|
if (!opts.respectGitignore || opts.includeNodeModules || opts.withEnv || opts.pattern.length > 0) {
|
|
8099
|
-
|
|
8156
|
+
log25.warn(
|
|
8100
8157
|
"cloud download ignores gitignore/--with-env/--pattern filters in v1 \u2014 pulling the whole /workspace tree (Phase 6 polish)."
|
|
8101
8158
|
);
|
|
8102
8159
|
}
|
|
@@ -8106,7 +8163,7 @@ var downloadCommand = new Command21("download").enablePositionalOptions().descri
|
|
|
8106
8163
|
initialValue: false
|
|
8107
8164
|
});
|
|
8108
8165
|
if (isCancel14(ok) || !ok) {
|
|
8109
|
-
|
|
8166
|
+
log25.info("cancelled");
|
|
8110
8167
|
return;
|
|
8111
8168
|
}
|
|
8112
8169
|
}
|
|
@@ -8125,17 +8182,17 @@ var downloadCommand = new Command21("download").enablePositionalOptions().descri
|
|
|
8125
8182
|
}
|
|
8126
8183
|
const insp = await inspectBox(box.id);
|
|
8127
8184
|
if (insp.state === "paused") {
|
|
8128
|
-
|
|
8185
|
+
log25.info("box is paused; unpausing");
|
|
8129
8186
|
await unpauseBox(box.id);
|
|
8130
8187
|
} else if (insp.state === "stopped") {
|
|
8131
|
-
|
|
8188
|
+
log25.info("box is stopped; starting");
|
|
8132
8189
|
await startBox(box.id);
|
|
8133
8190
|
} else if (insp.state === "missing") {
|
|
8134
8191
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
8135
8192
|
}
|
|
8136
8193
|
const rootWorktree = box.gitWorktrees?.find((w) => w.kind === "root");
|
|
8137
8194
|
if (rootWorktree) {
|
|
8138
|
-
|
|
8195
|
+
log25.warn(
|
|
8139
8196
|
`This box has been committing to branch \`${rootWorktree.branch}\` in a separate worktree.
|
|
8140
8197
|
For a git-aware merge instead of a file copy, run from your checkout:
|
|
8141
8198
|
git merge ${rootWorktree.branch}
|
|
@@ -8171,7 +8228,7 @@ Continuing with rsync into ${box.workspacePath}`
|
|
|
8171
8228
|
initialValue: false
|
|
8172
8229
|
});
|
|
8173
8230
|
if (isCancel14(ok) || !ok) {
|
|
8174
|
-
|
|
8231
|
+
log25.info("cancelled");
|
|
8175
8232
|
return;
|
|
8176
8233
|
}
|
|
8177
8234
|
}
|
|
@@ -8199,7 +8256,7 @@ downloadCommand.addCommand(downloadOpencodeCommand);
|
|
|
8199
8256
|
downloadCommand.addCommand(downloadConfigCommand);
|
|
8200
8257
|
|
|
8201
8258
|
// src/commands/drive.ts
|
|
8202
|
-
import { log as
|
|
8259
|
+
import { log as log26 } from "@clack/prompts";
|
|
8203
8260
|
import { Command as Command22 } from "commander";
|
|
8204
8261
|
|
|
8205
8262
|
// src/lib/drive/keys.ts
|
|
@@ -8485,7 +8542,7 @@ var driveWaitCommand = new Command22("wait").description("Block until --text app
|
|
|
8485
8542
|
}) + "\n"
|
|
8486
8543
|
);
|
|
8487
8544
|
} else {
|
|
8488
|
-
|
|
8545
|
+
log26.error(`text not found within ${String(timeoutMs)}ms: ${opts.text}`);
|
|
8489
8546
|
}
|
|
8490
8547
|
process.exit(1);
|
|
8491
8548
|
} catch (err) {
|
|
@@ -8512,8 +8569,8 @@ driveCommand.addCommand(driveWaitCommand);
|
|
|
8512
8569
|
driveCommand.addCommand(driveResizeCommand);
|
|
8513
8570
|
function handleDriveError(err) {
|
|
8514
8571
|
if (err instanceof SessionNotFoundError) {
|
|
8515
|
-
|
|
8516
|
-
|
|
8572
|
+
log26.error(err.message);
|
|
8573
|
+
log26.info("start an agent first (e.g. `agentbox claude <box>`) or pass --session.");
|
|
8517
8574
|
process.exit(2);
|
|
8518
8575
|
}
|
|
8519
8576
|
handleLifecycleError(err);
|
|
@@ -8537,10 +8594,10 @@ function sleep2(ms) {
|
|
|
8537
8594
|
}
|
|
8538
8595
|
|
|
8539
8596
|
// src/commands/fork.ts
|
|
8540
|
-
import { log as
|
|
8597
|
+
import { log as log27 } from "@clack/prompts";
|
|
8541
8598
|
import { Command as Command23 } from "commander";
|
|
8542
8599
|
import { existsSync as existsSync4, readdirSync, statSync } from "fs";
|
|
8543
|
-
import { homedir as
|
|
8600
|
+
import { homedir as homedir11 } from "os";
|
|
8544
8601
|
import { join as join12 } from "path";
|
|
8545
8602
|
var FORK_AGENTS = ["claude", "codex", "opencode"];
|
|
8546
8603
|
var AGENT_COMMAND = {
|
|
@@ -8561,7 +8618,7 @@ function resolveSessionArgs(agent, opts) {
|
|
|
8561
8618
|
}
|
|
8562
8619
|
if (opts.session) return ["--resume", opts.session];
|
|
8563
8620
|
if (agent === "codex") return ["--continue"];
|
|
8564
|
-
const dir = join12(
|
|
8621
|
+
const dir = join12(homedir11(), ".claude", "projects", encodeClaudeProjectsDir(opts.workspace));
|
|
8565
8622
|
if (!existsSync4(dir)) return ["--continue"];
|
|
8566
8623
|
const now = Date.now();
|
|
8567
8624
|
const recent = readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
@@ -8606,19 +8663,19 @@ var forkCommand = new Command23("fork").description(
|
|
|
8606
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)"
|
|
8607
8664
|
).action(async (opts) => {
|
|
8608
8665
|
if ((process.env.AGENTBOX_RELAY_URL ?? "").trim().length > 0) {
|
|
8609
|
-
|
|
8666
|
+
log27.error(
|
|
8610
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."
|
|
8611
8668
|
);
|
|
8612
8669
|
process.exit(2);
|
|
8613
8670
|
}
|
|
8614
8671
|
const agent = opts.agent?.trim() || "claude";
|
|
8615
8672
|
if (!FORK_AGENTS.includes(agent)) {
|
|
8616
|
-
|
|
8673
|
+
log27.error(`--agent: expected one of ${FORK_AGENTS.join(", ")}, got "${opts.agent ?? ""}"`);
|
|
8617
8674
|
process.exit(2);
|
|
8618
8675
|
}
|
|
8619
8676
|
const attachIn = opts.attachIn ?? "tab";
|
|
8620
8677
|
if (!FORK_ATTACH_VALUES.includes(attachIn)) {
|
|
8621
|
-
|
|
8678
|
+
log27.error(`--attach-in: expected one of ${FORK_ATTACH_VALUES.join(", ")}, got "${attachIn}"`);
|
|
8622
8679
|
process.exit(2);
|
|
8623
8680
|
}
|
|
8624
8681
|
const provider = opts.provider?.trim();
|
|
@@ -8626,7 +8683,7 @@ var forkCommand = new Command23("fork").description(
|
|
|
8626
8683
|
try {
|
|
8627
8684
|
sessionArgs = resolveSessionArgs(agent, opts);
|
|
8628
8685
|
} catch (err) {
|
|
8629
|
-
|
|
8686
|
+
log27.error(err instanceof Error ? err.message : String(err));
|
|
8630
8687
|
process.exit(2);
|
|
8631
8688
|
}
|
|
8632
8689
|
const subArgv = [
|
|
@@ -8644,16 +8701,16 @@ var forkCommand = new Command23("fork").description(
|
|
|
8644
8701
|
});
|
|
8645
8702
|
|
|
8646
8703
|
// src/commands/install.ts
|
|
8647
|
-
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";
|
|
8648
8705
|
import { Command as Command25 } from "commander";
|
|
8649
8706
|
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8650
|
-
import { homedir as
|
|
8707
|
+
import { homedir as homedir14 } from "os";
|
|
8651
8708
|
import { dirname as dirname2, join as join15, resolve as resolve2 } from "path";
|
|
8652
8709
|
import { fileURLToPath } from "url";
|
|
8653
8710
|
|
|
8654
8711
|
// src/lib/doctor-checks.ts
|
|
8655
8712
|
import { accessSync, constants as fsConstants, mkdirSync as mkdirSync3 } from "fs";
|
|
8656
|
-
import { homedir as
|
|
8713
|
+
import { homedir as homedir12 } from "os";
|
|
8657
8714
|
import { join as join13 } from "path";
|
|
8658
8715
|
import { execa as execa2 } from "execa";
|
|
8659
8716
|
var ALL_PROVIDERS = ["docker", "daytona", "hetzner", "vercel"];
|
|
@@ -8693,10 +8750,16 @@ function checkNode() {
|
|
|
8693
8750
|
};
|
|
8694
8751
|
}
|
|
8695
8752
|
function checkPlatform() {
|
|
8696
|
-
|
|
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
|
+
};
|
|
8697
8760
|
}
|
|
8698
8761
|
function checkAgentboxHome() {
|
|
8699
|
-
const dir = join13(
|
|
8762
|
+
const dir = join13(homedir12(), ".agentbox");
|
|
8700
8763
|
try {
|
|
8701
8764
|
mkdirSync3(dir, { recursive: true });
|
|
8702
8765
|
accessSync(dir, fsConstants.W_OK);
|
|
@@ -8733,6 +8796,7 @@ async function runSystemChecks() {
|
|
|
8733
8796
|
return [checkNode(), checkPlatform(), checkAgentboxHome(), git, ssh];
|
|
8734
8797
|
}
|
|
8735
8798
|
async function dockerChecks() {
|
|
8799
|
+
const linux = process.platform === "linux";
|
|
8736
8800
|
const cli = await probeVersion("docker");
|
|
8737
8801
|
if (!cli) {
|
|
8738
8802
|
return [
|
|
@@ -8740,25 +8804,34 @@ async function dockerChecks() {
|
|
|
8740
8804
|
label: "docker cli",
|
|
8741
8805
|
status: "warn",
|
|
8742
8806
|
detail: "not found",
|
|
8743
|
-
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"
|
|
8744
8808
|
}
|
|
8745
8809
|
];
|
|
8746
8810
|
}
|
|
8747
8811
|
const cliRes = { label: "docker cli", status: "ok", detail: cli };
|
|
8748
8812
|
const info = await execa2("docker", ["info"], { reject: false });
|
|
8749
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
|
+
}
|
|
8750
8823
|
return [
|
|
8751
8824
|
cliRes,
|
|
8752
8825
|
{
|
|
8753
8826
|
label: "docker daemon",
|
|
8754
8827
|
status: "warn",
|
|
8755
|
-
detail: "unreachable",
|
|
8756
|
-
hint
|
|
8828
|
+
detail: permDenied ? "permission denied" : "unreachable",
|
|
8829
|
+
hint
|
|
8757
8830
|
}
|
|
8758
8831
|
];
|
|
8759
8832
|
}
|
|
8760
8833
|
const daemonRes = { label: "docker daemon", status: "ok", detail: "reachable" };
|
|
8761
|
-
const mod = await import("./dist-
|
|
8834
|
+
const mod = await import("./dist-ZEGIMYWZ.js");
|
|
8762
8835
|
let imgRes;
|
|
8763
8836
|
try {
|
|
8764
8837
|
const img = await mod.imageInfo(mod.DEFAULT_BOX_IMAGE);
|
|
@@ -8789,7 +8862,7 @@ async function dockerChecks() {
|
|
|
8789
8862
|
}
|
|
8790
8863
|
async function daytonaChecks() {
|
|
8791
8864
|
try {
|
|
8792
|
-
const mod = await import("./dist-
|
|
8865
|
+
const mod = await import("./dist-PJFJNXO2.js");
|
|
8793
8866
|
const status = await mod.getDaytonaStatus();
|
|
8794
8867
|
if (!status.configured) {
|
|
8795
8868
|
return [
|
|
@@ -8825,7 +8898,7 @@ async function daytonaChecks() {
|
|
|
8825
8898
|
}
|
|
8826
8899
|
async function hetznerChecks() {
|
|
8827
8900
|
try {
|
|
8828
|
-
const mod = await import("./dist-
|
|
8901
|
+
const mod = await import("./dist-4SUIXKSD.js");
|
|
8829
8902
|
const cred = mod.readHetznerCredStatus();
|
|
8830
8903
|
const credRes = cred.source === "none" ? {
|
|
8831
8904
|
label: "credentials",
|
|
@@ -8857,7 +8930,7 @@ async function hetznerChecks() {
|
|
|
8857
8930
|
}
|
|
8858
8931
|
async function vercelChecks() {
|
|
8859
8932
|
try {
|
|
8860
|
-
const mod = await import("./dist-
|
|
8933
|
+
const mod = await import("./dist-HT2YV6PB.js");
|
|
8861
8934
|
const cred = mod.readVercelCredStatus();
|
|
8862
8935
|
const credRes = cred.auth === "none" ? {
|
|
8863
8936
|
label: "credentials",
|
|
@@ -8984,11 +9057,11 @@ function formatDetailed(groups) {
|
|
|
8984
9057
|
|
|
8985
9058
|
// src/lib/first-run.ts
|
|
8986
9059
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
8987
|
-
import { homedir as
|
|
9060
|
+
import { homedir as homedir13 } from "os";
|
|
8988
9061
|
import { dirname, join as join14 } from "path";
|
|
8989
9062
|
var MARKER_VERSION = 1;
|
|
8990
9063
|
function setupMarkerPath() {
|
|
8991
|
-
return join14(
|
|
9064
|
+
return join14(homedir13(), ".agentbox", "setup-complete.json");
|
|
8992
9065
|
}
|
|
8993
9066
|
function isFirstRun() {
|
|
8994
9067
|
return !existsSync5(setupMarkerPath());
|
|
@@ -9005,7 +9078,7 @@ function markSetupComplete(provider) {
|
|
|
9005
9078
|
}
|
|
9006
9079
|
|
|
9007
9080
|
// src/commands/prepare.ts
|
|
9008
|
-
import { intro as intro5, log as
|
|
9081
|
+
import { intro as intro5, log as log28, spinner as spinner7 } from "@clack/prompts";
|
|
9009
9082
|
import { Command as Command24 } from "commander";
|
|
9010
9083
|
async function dockerStatus() {
|
|
9011
9084
|
let img;
|
|
@@ -9068,7 +9141,7 @@ async function renderDocker(status) {
|
|
|
9068
9141
|
}
|
|
9069
9142
|
async function daytonaStatus() {
|
|
9070
9143
|
try {
|
|
9071
|
-
const mod = await import("./dist-
|
|
9144
|
+
const mod = await import("./dist-PJFJNXO2.js");
|
|
9072
9145
|
return await mod.getDaytonaStatus();
|
|
9073
9146
|
} catch (err) {
|
|
9074
9147
|
return {
|
|
@@ -9142,7 +9215,7 @@ async function runPrepare(providerName, opts = {}) {
|
|
|
9142
9215
|
}
|
|
9143
9216
|
const provider = await getProvider(providerName);
|
|
9144
9217
|
if (typeof provider.prepare !== "function") {
|
|
9145
|
-
|
|
9218
|
+
log28.error(`provider '${providerName}' does not implement prepare`);
|
|
9146
9219
|
process.exit(1);
|
|
9147
9220
|
}
|
|
9148
9221
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -9162,10 +9235,10 @@ async function runPrepare(providerName, opts = {}) {
|
|
|
9162
9235
|
sp.stop(`prepared ${providerName}: snapshot '${result.snapshotName}'`);
|
|
9163
9236
|
try {
|
|
9164
9237
|
const written = await setConfigValue("project", "box.image", result.snapshotName, cwd);
|
|
9165
|
-
|
|
9238
|
+
log28.success(`box.image = ${result.snapshotName} (written to ${written.path})`);
|
|
9166
9239
|
} catch (err) {
|
|
9167
9240
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9168
|
-
|
|
9241
|
+
log28.warn(
|
|
9169
9242
|
`prepared snapshot '${result.snapshotName}', but failed to pin it into the project config: ${msg}
|
|
9170
9243
|
Run \`agentbox config set --project box.image ${result.snapshotName}\` manually.`
|
|
9171
9244
|
);
|
|
@@ -9178,7 +9251,7 @@ Run \`agentbox config set --project box.image ${result.snapshotName}\` manually.
|
|
|
9178
9251
|
await showStatus({ onlyProvider: providerName });
|
|
9179
9252
|
}
|
|
9180
9253
|
if (!opts.suppressTip) {
|
|
9181
|
-
|
|
9254
|
+
log28.info(
|
|
9182
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"
|
|
9183
9256
|
);
|
|
9184
9257
|
}
|
|
@@ -9310,7 +9383,7 @@ ${LOGO_L2}\x1B[0m` + SYNC_END2);
|
|
|
9310
9383
|
}
|
|
9311
9384
|
var LEGACY_INFO_MARKER = "Drive AgentBox from the host:";
|
|
9312
9385
|
function installTargets() {
|
|
9313
|
-
const home =
|
|
9386
|
+
const home = homedir14();
|
|
9314
9387
|
const claudeSkills = join15(home, ".claude", "skills");
|
|
9315
9388
|
return [
|
|
9316
9389
|
{ src: join15("agentbox", "SKILL.md"), dest: join15(claudeSkills, "agentbox", "SKILL.md") },
|
|
@@ -9361,20 +9434,20 @@ function installHostSkills(opts = {}) {
|
|
|
9361
9434
|
for (const t of installTargets()) {
|
|
9362
9435
|
const src = join15(srcDir, t.src);
|
|
9363
9436
|
if (!existsSync6(src)) {
|
|
9364
|
-
if (!quiet)
|
|
9437
|
+
if (!quiet) log29.warn(`bundled file missing (skipped): ${src}`);
|
|
9365
9438
|
skipped++;
|
|
9366
9439
|
continue;
|
|
9367
9440
|
}
|
|
9368
9441
|
if (t.gateDir && !existsSync6(t.gateDir)) continue;
|
|
9369
9442
|
const reason = writableReason(t.dest, force);
|
|
9370
9443
|
if (reason === "skip") {
|
|
9371
|
-
if (!quiet)
|
|
9444
|
+
if (!quiet) log29.warn(`user-modified file at ${t.dest}, skipping; pass --force to overwrite`);
|
|
9372
9445
|
blocked.push(t.dest);
|
|
9373
9446
|
skipped++;
|
|
9374
9447
|
continue;
|
|
9375
9448
|
}
|
|
9376
9449
|
if (dryRun) {
|
|
9377
|
-
if (!quiet)
|
|
9450
|
+
if (!quiet) log29.info(`would write ${t.dest} (${reason})`);
|
|
9378
9451
|
written.push(t.dest);
|
|
9379
9452
|
continue;
|
|
9380
9453
|
}
|
|
@@ -9406,10 +9479,10 @@ function ensureTty() {
|
|
|
9406
9479
|
async function runProviderLogin(name) {
|
|
9407
9480
|
if (name === "docker") return true;
|
|
9408
9481
|
if (name === "daytona") {
|
|
9409
|
-
const mod2 = await import("./dist-
|
|
9482
|
+
const mod2 = await import("./dist-PJFJNXO2.js");
|
|
9410
9483
|
const status2 = await mod2.getDaytonaStatus();
|
|
9411
9484
|
if (status2.configured) {
|
|
9412
|
-
|
|
9485
|
+
log29.info("daytona: already configured");
|
|
9413
9486
|
const rotate = await confirm14({ message: "Re-authenticate Daytona?", initialValue: false });
|
|
9414
9487
|
if (isCancel15(rotate)) return false;
|
|
9415
9488
|
if (rotate) await mod2.ensureDaytonaCredentials({ force: true });
|
|
@@ -9419,10 +9492,10 @@ async function runProviderLogin(name) {
|
|
|
9419
9492
|
return true;
|
|
9420
9493
|
}
|
|
9421
9494
|
if (name === "hetzner") {
|
|
9422
|
-
const mod2 = await import("./dist-
|
|
9495
|
+
const mod2 = await import("./dist-4SUIXKSD.js");
|
|
9423
9496
|
const status2 = mod2.readHetznerCredStatus();
|
|
9424
9497
|
if (status2.source !== "none") {
|
|
9425
|
-
|
|
9498
|
+
log29.info("hetzner: already configured");
|
|
9426
9499
|
const rotate = await confirm14({ message: "Re-authenticate Hetzner?", initialValue: false });
|
|
9427
9500
|
if (isCancel15(rotate)) return false;
|
|
9428
9501
|
if (rotate) await mod2.ensureHetznerCredentials({ force: true });
|
|
@@ -9431,10 +9504,10 @@ async function runProviderLogin(name) {
|
|
|
9431
9504
|
await mod2.ensureHetznerCredentials();
|
|
9432
9505
|
return true;
|
|
9433
9506
|
}
|
|
9434
|
-
const mod = await import("./dist-
|
|
9507
|
+
const mod = await import("./dist-HT2YV6PB.js");
|
|
9435
9508
|
const status = mod.readVercelCredStatus();
|
|
9436
9509
|
if (status.auth !== "none") {
|
|
9437
|
-
|
|
9510
|
+
log29.info(`vercel: already configured (${status.auth})`);
|
|
9438
9511
|
const rotate = await confirm14({ message: "Re-authenticate Vercel?", initialValue: false });
|
|
9439
9512
|
if (isCancel15(rotate)) return false;
|
|
9440
9513
|
if (rotate) await mod.ensureVercelCredentials({ force: true });
|
|
@@ -9465,11 +9538,11 @@ async function runInstallWizard(opts = {}) {
|
|
|
9465
9538
|
process.stdout.write(" " + formatCompact([sysGroup]) + "\n");
|
|
9466
9539
|
const hardFail = sysResults.find((r) => r.status === "fail");
|
|
9467
9540
|
if (hardFail) {
|
|
9468
|
-
|
|
9469
|
-
|
|
9541
|
+
log29.error(`system check failed: ${hardFail.label} \u2014 ${hardFail.detail}`);
|
|
9542
|
+
log29.info("run `agentbox doctor` for full detail");
|
|
9470
9543
|
const cont = await confirm14({ message: "Continue anyway?", initialValue: false });
|
|
9471
9544
|
if (isCancel15(cont) || !cont) {
|
|
9472
|
-
|
|
9545
|
+
outro5("aborted");
|
|
9473
9546
|
return false;
|
|
9474
9547
|
}
|
|
9475
9548
|
}
|
|
@@ -9477,7 +9550,7 @@ async function runInstallWizard(opts = {}) {
|
|
|
9477
9550
|
if (opts.provider) {
|
|
9478
9551
|
const candidate = opts.provider.trim();
|
|
9479
9552
|
if (!isProviderName(candidate)) {
|
|
9480
|
-
|
|
9553
|
+
log29.error(`unknown --provider: ${candidate}`);
|
|
9481
9554
|
return false;
|
|
9482
9555
|
}
|
|
9483
9556
|
providerName = candidate;
|
|
@@ -9492,7 +9565,7 @@ async function runInstallWizard(opts = {}) {
|
|
|
9492
9565
|
}))
|
|
9493
9566
|
});
|
|
9494
9567
|
if (isCancel15(picked)) {
|
|
9495
|
-
|
|
9568
|
+
outro5("cancelled");
|
|
9496
9569
|
return false;
|
|
9497
9570
|
}
|
|
9498
9571
|
providerName = picked;
|
|
@@ -9500,14 +9573,14 @@ async function runInstallWizard(opts = {}) {
|
|
|
9500
9573
|
if (providerName !== "docker") {
|
|
9501
9574
|
const loggedIn = await runProviderLogin(providerName);
|
|
9502
9575
|
if (!loggedIn) {
|
|
9503
|
-
|
|
9576
|
+
outro5("cancelled");
|
|
9504
9577
|
return false;
|
|
9505
9578
|
}
|
|
9506
9579
|
}
|
|
9507
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)`;
|
|
9508
9581
|
const wantPrepare = opts.yes ? true : await confirm14({ message: prepareMsg, initialValue: true });
|
|
9509
9582
|
if (isCancel15(wantPrepare)) {
|
|
9510
|
-
|
|
9583
|
+
outro5("cancelled");
|
|
9511
9584
|
return false;
|
|
9512
9585
|
}
|
|
9513
9586
|
if (wantPrepare) {
|
|
@@ -9519,12 +9592,12 @@ async function runInstallWizard(opts = {}) {
|
|
|
9519
9592
|
suppressTip: true
|
|
9520
9593
|
});
|
|
9521
9594
|
} catch (err) {
|
|
9522
|
-
|
|
9595
|
+
log29.warn(
|
|
9523
9596
|
`prepare failed: ${err instanceof Error ? err.message : String(err)} \u2014 you can rerun \`agentbox prepare --provider ${providerName}\` later`
|
|
9524
9597
|
);
|
|
9525
9598
|
}
|
|
9526
9599
|
} else {
|
|
9527
|
-
|
|
9600
|
+
log29.info(
|
|
9528
9601
|
`skipped \u2014 the ${providerName} base will build lazily on first \`agentbox ${providerName === "docker" ? "" : providerName + " "}create\``
|
|
9529
9602
|
);
|
|
9530
9603
|
}
|
|
@@ -9539,20 +9612,20 @@ async function runInstallWizard(opts = {}) {
|
|
|
9539
9612
|
sp.stop(`Agentbox Skills: nothing to write (${String(skillRes.skipped)} skipped)`);
|
|
9540
9613
|
}
|
|
9541
9614
|
if (skillRes.blocked.length > 0) {
|
|
9542
|
-
|
|
9615
|
+
log29.warn(
|
|
9543
9616
|
`user-modified host skill file(s) left in place: ${skillRes.blocked.join(", ")}
|
|
9544
9617
|
pass \`agentbox install --skills-only --force\` to overwrite`
|
|
9545
9618
|
);
|
|
9546
9619
|
}
|
|
9547
9620
|
} catch (err) {
|
|
9548
9621
|
sp.stop("Agentbox Skills: failed");
|
|
9549
|
-
|
|
9622
|
+
log29.warn(err instanceof Error ? err.message : String(err));
|
|
9550
9623
|
}
|
|
9551
9624
|
markSetupComplete(providerName);
|
|
9552
9625
|
const providerGroup = await runProviderChecks(providerName);
|
|
9553
9626
|
process.stdout.write(" " + formatCompact([sysGroup, providerGroup]) + "\n");
|
|
9554
|
-
|
|
9555
|
-
|
|
9627
|
+
note2(tutorialBody(providerName), "Next steps");
|
|
9628
|
+
outro5(
|
|
9556
9629
|
opts.fromAutoTrigger ? "\u2728 Setup complete \u2014 continuing with your command\u2026" : "\u2728 Setup complete"
|
|
9557
9630
|
);
|
|
9558
9631
|
return true;
|
|
@@ -9572,20 +9645,20 @@ var installCommand = new Command25("install").description(
|
|
|
9572
9645
|
try {
|
|
9573
9646
|
res = installHostSkills({ force: opts.force, dryRun: opts.dryRun });
|
|
9574
9647
|
} catch (err) {
|
|
9575
|
-
|
|
9648
|
+
log29.error(err instanceof Error ? err.message : String(err));
|
|
9576
9649
|
process.exit(1);
|
|
9577
9650
|
}
|
|
9578
9651
|
if (opts.dryRun) {
|
|
9579
|
-
|
|
9652
|
+
outro5(
|
|
9580
9653
|
`dry-run: ${String(res.written.length)} file(s) would be written, ${String(res.skipped)} skipped`
|
|
9581
9654
|
);
|
|
9582
9655
|
return;
|
|
9583
9656
|
}
|
|
9584
9657
|
if (res.written.length === 0) {
|
|
9585
|
-
|
|
9658
|
+
outro5(`nothing installed (${String(res.skipped)} skipped)`);
|
|
9586
9659
|
return;
|
|
9587
9660
|
}
|
|
9588
|
-
|
|
9661
|
+
outro5(`installed: ${res.written.join(", ")}`);
|
|
9589
9662
|
return;
|
|
9590
9663
|
}
|
|
9591
9664
|
const ok = await runInstallWizard({
|
|
@@ -9777,7 +9850,7 @@ for (const op of GH_PR_OPS) {
|
|
|
9777
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);
|
|
9778
9851
|
|
|
9779
9852
|
// src/commands/list.ts
|
|
9780
|
-
import { log as
|
|
9853
|
+
import { log as log30 } from "@clack/prompts";
|
|
9781
9854
|
import { Command as Command28 } from "commander";
|
|
9782
9855
|
import { pathToFileURL } from "url";
|
|
9783
9856
|
|
|
@@ -9992,7 +10065,7 @@ var listCommand2 = withWatchOptions(
|
|
|
9992
10065
|
)
|
|
9993
10066
|
).action(async (opts) => {
|
|
9994
10067
|
if (opts.json && opts.watch) {
|
|
9995
|
-
|
|
10068
|
+
log30.error("cannot combine --json with --watch");
|
|
9996
10069
|
process.exit(2);
|
|
9997
10070
|
}
|
|
9998
10071
|
const all = opts.global ?? false;
|
|
@@ -10010,7 +10083,7 @@ var listCommand2 = withWatchOptions(
|
|
|
10010
10083
|
});
|
|
10011
10084
|
|
|
10012
10085
|
// src/commands/logs.ts
|
|
10013
|
-
import { log as
|
|
10086
|
+
import { log as log31 } from "@clack/prompts";
|
|
10014
10087
|
import { Command as Command29 } from "commander";
|
|
10015
10088
|
import { spawn as spawn3 } from "child_process";
|
|
10016
10089
|
var DAEMON_LOG_PATH = "/var/log/agentbox/ctl-daemon.log";
|
|
@@ -10032,9 +10105,9 @@ var logsCommand = new Command29("logs").description("Print recent log lines from
|
|
|
10032
10105
|
service = boxArg;
|
|
10033
10106
|
}
|
|
10034
10107
|
if (!service && !opts.daemon) {
|
|
10035
|
-
|
|
10036
|
-
|
|
10037
|
-
|
|
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]");
|
|
10038
10111
|
process.exit(2);
|
|
10039
10112
|
}
|
|
10040
10113
|
const box = await resolveBoxOrExit(idOrName);
|
|
@@ -10045,7 +10118,7 @@ var logsCommand = new Command29("logs").description("Print recent log lines from
|
|
|
10045
10118
|
if (!opts.follow) {
|
|
10046
10119
|
const proc = await provider.exec(box, args, { user: "vscode" });
|
|
10047
10120
|
if (proc.exitCode !== 0) {
|
|
10048
|
-
|
|
10121
|
+
log31.error(
|
|
10049
10122
|
`${opts.daemon ? "daemon log" : "agentbox-ctl logs"} failed: ${proc.stderr || proc.stdout}`
|
|
10050
10123
|
);
|
|
10051
10124
|
process.exit(1);
|
|
@@ -10101,10 +10174,10 @@ var logsCommand = new Command29("logs").description("Print recent log lines from
|
|
|
10101
10174
|
});
|
|
10102
10175
|
|
|
10103
10176
|
// src/commands/open.ts
|
|
10104
|
-
import { log as
|
|
10177
|
+
import { log as log32 } from "@clack/prompts";
|
|
10105
10178
|
import { execa as execa3 } from "execa";
|
|
10106
10179
|
import { existsSync as existsSync7, mkdirSync as mkdirSync6 } from "fs";
|
|
10107
|
-
import { homedir as
|
|
10180
|
+
import { homedir as homedir15 } from "os";
|
|
10108
10181
|
import { join as join16 } from "path";
|
|
10109
10182
|
import { Command as Command30 } from "commander";
|
|
10110
10183
|
|
|
@@ -10167,7 +10240,7 @@ var openCommand = new Command30("open").description("Open a box's /workspace in
|
|
|
10167
10240
|
}
|
|
10168
10241
|
});
|
|
10169
10242
|
async function runCloudOpen(box, provider, opts) {
|
|
10170
|
-
const mountRoot = join16(
|
|
10243
|
+
const mountRoot = join16(homedir15(), ".agentbox", "mounts", box.name);
|
|
10171
10244
|
if (opts.unmount) {
|
|
10172
10245
|
const ok = await tryUnmount(mountRoot);
|
|
10173
10246
|
if (ok) process.stdout.write(`unmounted ${mountRoot}
|
|
@@ -10207,10 +10280,10 @@ async function runCloudOpen(box, provider, opts) {
|
|
|
10207
10280
|
if (!existsSync7(mountRoot)) {
|
|
10208
10281
|
mkdirSync6(mountRoot, { recursive: true, mode: 493 });
|
|
10209
10282
|
} else if (await isMounted(mountRoot)) {
|
|
10210
|
-
|
|
10283
|
+
log32.info(`re-mounting (stale mount detected at ${mountRoot})`);
|
|
10211
10284
|
await tryUnmount(mountRoot);
|
|
10212
10285
|
}
|
|
10213
|
-
|
|
10286
|
+
log32.info(`mounting ${alias}:/workspace at ${mountRoot}`);
|
|
10214
10287
|
const mount = await execa3(
|
|
10215
10288
|
sshfsBin,
|
|
10216
10289
|
[
|
|
@@ -10230,7 +10303,7 @@ async function runCloudOpen(box, provider, opts) {
|
|
|
10230
10303
|
if (mount.exitCode !== 0) {
|
|
10231
10304
|
throw new Error(`sshfs mount failed (exit ${String(mount.exitCode)}): ${mount.stderr || mount.stdout}`);
|
|
10232
10305
|
}
|
|
10233
|
-
await execa3(
|
|
10306
|
+
await execa3(hostOpenCommand(), [mountRoot], { reject: false });
|
|
10234
10307
|
process.stdout.write(`opened ${mountRoot}
|
|
10235
10308
|
`);
|
|
10236
10309
|
process.stdout.write(`unmount later with: agentbox open ${box.name} --unmount
|
|
@@ -10281,7 +10354,7 @@ var pauseCommand = new Command31("pause").description(
|
|
|
10281
10354
|
});
|
|
10282
10355
|
|
|
10283
10356
|
// src/commands/prune.ts
|
|
10284
|
-
import { confirm as confirm15, isCancel as isCancel16, log as
|
|
10357
|
+
import { confirm as confirm15, isCancel as isCancel16, log as log33 } from "@clack/prompts";
|
|
10285
10358
|
import { Command as Command32 } from "commander";
|
|
10286
10359
|
function totalRemovals(r, projectConfigs) {
|
|
10287
10360
|
return r.removedRecords.length + r.removedContainers.length + r.removedVolumes.length + r.removedSnapshotDirs.length + r.removedBoxDirs.length + projectConfigs.length;
|
|
@@ -10341,7 +10414,7 @@ var pruneCommand = new Command32("prune").description("Clean up orphan state.jso
|
|
|
10341
10414
|
return;
|
|
10342
10415
|
}
|
|
10343
10416
|
if (opts.provider !== void 0 && opts.provider !== "docker") {
|
|
10344
|
-
|
|
10417
|
+
log33.error(`unknown provider '${opts.provider}'; expected docker, daytona, hetzner, or vercel`);
|
|
10345
10418
|
process.exit(2);
|
|
10346
10419
|
}
|
|
10347
10420
|
const dryRun = opts.dryRun ?? false;
|
|
@@ -10354,13 +10427,13 @@ var pruneCommand = new Command32("prune").description("Clean up orphan state.jso
|
|
|
10354
10427
|
process.stdout.write("nothing to prune\n");
|
|
10355
10428
|
return;
|
|
10356
10429
|
}
|
|
10357
|
-
|
|
10430
|
+
log33.info(`would remove:
|
|
10358
10431
|
${summary(preview, previewProjects)}`);
|
|
10359
10432
|
if (dryRun) return;
|
|
10360
10433
|
if (!opts.yes) {
|
|
10361
10434
|
const ok = await confirm15({ message: "Proceed with prune?", initialValue: true });
|
|
10362
10435
|
if (isCancel16(ok) || !ok) {
|
|
10363
|
-
|
|
10436
|
+
log33.info("cancelled");
|
|
10364
10437
|
return;
|
|
10365
10438
|
}
|
|
10366
10439
|
}
|
|
@@ -10381,7 +10454,7 @@ async function pruneCloud(provider, opts) {
|
|
|
10381
10454
|
const dryRun = opts.dryRun ?? false;
|
|
10382
10455
|
const backend = await cloudBackendForProvider(provider);
|
|
10383
10456
|
if (!backend.list) {
|
|
10384
|
-
|
|
10457
|
+
log33.error(`${provider} backend doesn't expose \`list()\`; cannot enumerate sandboxes for prune`);
|
|
10385
10458
|
process.exit(2);
|
|
10386
10459
|
}
|
|
10387
10460
|
const [remote, state] = await Promise.all([backend.list(), readState()]);
|
|
@@ -10401,7 +10474,7 @@ async function pruneCloud(provider, opts) {
|
|
|
10401
10474
|
`);
|
|
10402
10475
|
return;
|
|
10403
10476
|
}
|
|
10404
|
-
|
|
10477
|
+
log33.info(`found ${String(orphans.length)} ${provider} sandbox(es) not in this CLI's state:`);
|
|
10405
10478
|
for (const sb of orphans) {
|
|
10406
10479
|
const parts = [sb.sandboxId];
|
|
10407
10480
|
if (sb.name) parts.push(sb.name);
|
|
@@ -10417,7 +10490,7 @@ async function pruneCloud(provider, opts) {
|
|
|
10417
10490
|
initialValue: false
|
|
10418
10491
|
});
|
|
10419
10492
|
if (isCancel16(ok) || !ok) {
|
|
10420
|
-
|
|
10493
|
+
log33.info("cancelled");
|
|
10421
10494
|
return;
|
|
10422
10495
|
}
|
|
10423
10496
|
}
|
|
@@ -10429,7 +10502,7 @@ async function pruneCloud(provider, opts) {
|
|
|
10429
10502
|
deleted++;
|
|
10430
10503
|
} catch (err) {
|
|
10431
10504
|
failed++;
|
|
10432
|
-
|
|
10505
|
+
log33.warn(
|
|
10433
10506
|
`delete ${sb.sandboxId} failed: ${err instanceof Error ? err.message : String(err)}`
|
|
10434
10507
|
);
|
|
10435
10508
|
}
|
|
@@ -10442,7 +10515,7 @@ async function pruneCloud(provider, opts) {
|
|
|
10442
10515
|
|
|
10443
10516
|
// src/commands/queue.ts
|
|
10444
10517
|
import { readFile as readFile4, stat as stat5 } from "fs/promises";
|
|
10445
|
-
import { intro as intro7, log as
|
|
10518
|
+
import { intro as intro7, log as log34, outro as outro6 } from "@clack/prompts";
|
|
10446
10519
|
import { Command as Command33 } from "commander";
|
|
10447
10520
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["done", "failed", "cancelled"]);
|
|
10448
10521
|
var queueCommand = new Command33("queue").description("Inspect and manage background `agentbox claude|codex|opencode -i` jobs");
|
|
@@ -10451,8 +10524,8 @@ var queueListCommand = new Command33("list").description("List queued, running,
|
|
|
10451
10524
|
const cfg = await loadQueueConfig();
|
|
10452
10525
|
const visible = opts.all === true ? jobs : jobs.filter((j) => !TERMINAL_STATUSES.has(j.status));
|
|
10453
10526
|
if (visible.length === 0) {
|
|
10454
|
-
|
|
10455
|
-
|
|
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)})`);
|
|
10456
10529
|
return;
|
|
10457
10530
|
}
|
|
10458
10531
|
const rows = visible.map((j) => ({
|
|
@@ -10477,12 +10550,12 @@ var queueListCommand = new Command33("list").description("List queued, running,
|
|
|
10477
10550
|
headers.map((h, i) => pad4(String(r[h]), widths[i])).join(" ") + "\n"
|
|
10478
10551
|
);
|
|
10479
10552
|
}
|
|
10480
|
-
|
|
10553
|
+
log34.info(`queue.maxConcurrent = ${String(cfg.maxConcurrent)} (queue.enabled=${String(cfg.enabled)})`);
|
|
10481
10554
|
});
|
|
10482
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) => {
|
|
10483
10556
|
const job = await readJob(id);
|
|
10484
10557
|
if (!job) {
|
|
10485
|
-
|
|
10558
|
+
log34.error(`no job with id ${id}`);
|
|
10486
10559
|
process.exit(1);
|
|
10487
10560
|
}
|
|
10488
10561
|
process.stdout.write(JSON.stringify(job, null, 2) + "\n");
|
|
@@ -10498,18 +10571,18 @@ var queueShowCommand = new Command33("show").description("Dump a job manifest an
|
|
|
10498
10571
|
process.stdout.write(slice.join("\n"));
|
|
10499
10572
|
if (!slice.join("\n").endsWith("\n")) process.stdout.write("\n");
|
|
10500
10573
|
} catch {
|
|
10501
|
-
|
|
10574
|
+
log34.info(`(no log at ${job.logPath} yet)`);
|
|
10502
10575
|
}
|
|
10503
10576
|
});
|
|
10504
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) => {
|
|
10505
10578
|
intro7(`Cancelling queue job ${id}...`);
|
|
10506
10579
|
const job = await readJob(id);
|
|
10507
10580
|
if (!job) {
|
|
10508
|
-
|
|
10581
|
+
log34.error(`no job with id ${id}`);
|
|
10509
10582
|
process.exit(1);
|
|
10510
10583
|
}
|
|
10511
10584
|
if (job.status !== "queued") {
|
|
10512
|
-
|
|
10585
|
+
log34.error(
|
|
10513
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.` : "")
|
|
10514
10587
|
);
|
|
10515
10588
|
process.exit(1);
|
|
@@ -10521,7 +10594,7 @@ var queueCancelCommand = new Command33("cancel").description("Cancel a queued jo
|
|
|
10521
10594
|
reason: "cancelled by user"
|
|
10522
10595
|
};
|
|
10523
10596
|
await writeJob(cancelled);
|
|
10524
|
-
|
|
10597
|
+
outro6(`job ${id} cancelled`);
|
|
10525
10598
|
});
|
|
10526
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) => {
|
|
10527
10600
|
const targets = /* @__PURE__ */ new Set();
|
|
@@ -10529,7 +10602,7 @@ var queueClearCommand = new Command33("clear").description("Sweep terminal-state
|
|
|
10529
10602
|
if (opts.all === true || opts.failed === true) targets.add("failed");
|
|
10530
10603
|
if (opts.all === true || opts.cancelled === true) targets.add("cancelled");
|
|
10531
10604
|
if (targets.size === 0) {
|
|
10532
|
-
|
|
10605
|
+
log34.error("pick at least one of: --done, --failed, --cancelled, --all");
|
|
10533
10606
|
process.exit(2);
|
|
10534
10607
|
}
|
|
10535
10608
|
const jobs = await loadQueue();
|
|
@@ -10539,7 +10612,7 @@ var queueClearCommand = new Command33("clear").description("Sweep terminal-state
|
|
|
10539
10612
|
await deleteJob(j.id);
|
|
10540
10613
|
removed += 1;
|
|
10541
10614
|
}
|
|
10542
|
-
|
|
10615
|
+
log34.success(`removed ${String(removed)} manifest${removed === 1 ? "" : "s"}`);
|
|
10543
10616
|
});
|
|
10544
10617
|
var QUEUE_WAIT_EVENTS = [
|
|
10545
10618
|
"new-box",
|
|
@@ -10556,7 +10629,7 @@ var queueWaitForCommand = new Command33("wait-for").description(
|
|
|
10556
10629
|
`Block until a queue / box event fires. <event> one of: ${QUEUE_WAIT_EVENTS.join(" | ")}.`
|
|
10557
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) => {
|
|
10558
10631
|
if (!QUEUE_WAIT_EVENTS.includes(eventRaw)) {
|
|
10559
|
-
|
|
10632
|
+
log34.error(`unknown event '${eventRaw}' (one of: ${QUEUE_WAIT_EVENTS.join(", ")})`);
|
|
10560
10633
|
process.exit(2);
|
|
10561
10634
|
}
|
|
10562
10635
|
const event = eventRaw;
|
|
@@ -10576,11 +10649,11 @@ var queueWaitForCommand = new Command33("wait-for").description(
|
|
|
10576
10649
|
if (opts.json === true) {
|
|
10577
10650
|
process.stdout.write(JSON.stringify({ matched: false, event, elapsedMs }) + "\n");
|
|
10578
10651
|
} else {
|
|
10579
|
-
|
|
10652
|
+
log34.error(`'${event}' did not occur within ${String(timeoutMs)}ms`);
|
|
10580
10653
|
}
|
|
10581
10654
|
process.exit(1);
|
|
10582
10655
|
}
|
|
10583
|
-
|
|
10656
|
+
log34.error(err instanceof Error ? err.message : String(err));
|
|
10584
10657
|
process.exit(1);
|
|
10585
10658
|
}
|
|
10586
10659
|
});
|
|
@@ -10677,7 +10750,7 @@ function truncate(s, max) {
|
|
|
10677
10750
|
}
|
|
10678
10751
|
|
|
10679
10752
|
// src/commands/relay.ts
|
|
10680
|
-
import { log as
|
|
10753
|
+
import { log as log35, spinner as spinner9 } from "@clack/prompts";
|
|
10681
10754
|
import { Command as Command34 } from "commander";
|
|
10682
10755
|
async function rehydrateFromState() {
|
|
10683
10756
|
const state = await readState();
|
|
@@ -10771,7 +10844,7 @@ var restartSub = new Command34("restart").description("Stop then start the host
|
|
|
10771
10844
|
s2.stop(`relay running on ${ep.hostUrl}`);
|
|
10772
10845
|
} catch (err) {
|
|
10773
10846
|
s2.stop("relay start failed");
|
|
10774
|
-
|
|
10847
|
+
log35.warn(err instanceof Error ? err.message : String(err));
|
|
10775
10848
|
throw err;
|
|
10776
10849
|
}
|
|
10777
10850
|
} catch (err) {
|
|
@@ -10783,17 +10856,17 @@ var relayCommand = new Command34("relay").description("Manage the host relay pro
|
|
|
10783
10856
|
// src/commands/_run-queued-job.ts
|
|
10784
10857
|
import { Command as Command35 } from "commander";
|
|
10785
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) => {
|
|
10786
|
-
const
|
|
10787
|
-
|
|
10859
|
+
const log44 = openCommandLog(`queue-${id}`);
|
|
10860
|
+
log44.write(`worker pid=${String(process.pid)} starting for job ${id}`);
|
|
10788
10861
|
let job = null;
|
|
10789
10862
|
try {
|
|
10790
10863
|
job = await readJob(id);
|
|
10791
10864
|
if (!job) {
|
|
10792
|
-
|
|
10793
|
-
|
|
10865
|
+
log44.write(`FATAL: no manifest at id=${id}`);
|
|
10866
|
+
log44.close();
|
|
10794
10867
|
process.exit(64);
|
|
10795
10868
|
}
|
|
10796
|
-
await runDockerJob(job,
|
|
10869
|
+
await runDockerJob(job, log44, (boxId) => {
|
|
10797
10870
|
if (job) job = { ...job, boxId };
|
|
10798
10871
|
});
|
|
10799
10872
|
const done = {
|
|
@@ -10803,12 +10876,12 @@ var runQueuedJobCommand = new Command35("_run-queued-job").description("internal
|
|
|
10803
10876
|
exitCode: 0
|
|
10804
10877
|
};
|
|
10805
10878
|
await writeJob(done);
|
|
10806
|
-
|
|
10807
|
-
|
|
10879
|
+
log44.write(`done`);
|
|
10880
|
+
log44.close();
|
|
10808
10881
|
process.exit(0);
|
|
10809
10882
|
} catch (err) {
|
|
10810
10883
|
const msg = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
10811
|
-
|
|
10884
|
+
log44.write(`FAIL: ${msg}`);
|
|
10812
10885
|
if (job) {
|
|
10813
10886
|
try {
|
|
10814
10887
|
const failed = {
|
|
@@ -10822,11 +10895,11 @@ var runQueuedJobCommand = new Command35("_run-queued-job").description("internal
|
|
|
10822
10895
|
} catch {
|
|
10823
10896
|
}
|
|
10824
10897
|
}
|
|
10825
|
-
|
|
10898
|
+
log44.close();
|
|
10826
10899
|
process.exit(1);
|
|
10827
10900
|
}
|
|
10828
10901
|
});
|
|
10829
|
-
async function runDockerJob(job,
|
|
10902
|
+
async function runDockerJob(job, log44, onBoxCreated) {
|
|
10830
10903
|
const opts = job.createOpts;
|
|
10831
10904
|
const cfg = await loadEffectiveConfig(opts.workspace, {
|
|
10832
10905
|
cliOverrides: buildOverridesFromJob(job)
|
|
@@ -10841,7 +10914,7 @@ async function runDockerJob(job, log45, onBoxCreated) {
|
|
|
10841
10914
|
const useSnapshot = opts.hostSnapshot === false ? false : opts.hostSnapshot === true ? true : cfg.effective.box.hostSnapshot ?? false;
|
|
10842
10915
|
const resolved = job.agent === "claude-code" ? await resolveClaudeAuth(process.env) : null;
|
|
10843
10916
|
const withPlaywright = cfg.effective.box.withPlaywright || cfg.effective.browser.default !== "agent-browser";
|
|
10844
|
-
|
|
10917
|
+
log44.write(`creating box for agent=${job.agent}`);
|
|
10845
10918
|
const result = await createBox({
|
|
10846
10919
|
workspacePath: opts.workspace,
|
|
10847
10920
|
name: opts.name && opts.name.length > 0 ? opts.name : void 0,
|
|
@@ -10863,19 +10936,19 @@ async function runDockerJob(job, log45, onBoxCreated) {
|
|
|
10863
10936
|
portlessStateDir: cfg.effective.portless.stateDir || void 0,
|
|
10864
10937
|
limits: resolveLimits(cfg.effective.box, opts),
|
|
10865
10938
|
projectRoot,
|
|
10866
|
-
onLog: (line) =>
|
|
10939
|
+
onLog: (line) => log44.write(line)
|
|
10867
10940
|
});
|
|
10868
|
-
|
|
10941
|
+
log44.write(`box created: ${result.record.container}`);
|
|
10869
10942
|
onBoxCreated(result.record.id);
|
|
10870
10943
|
await writeJob({ ...job, boxId: result.record.id });
|
|
10871
10944
|
const promptedArgs = buildPromptArgs(job.agent, job.prompt, job.agentArgs);
|
|
10872
10945
|
if (job.agent === "claude-code") {
|
|
10873
|
-
|
|
10946
|
+
log44.write(`checking plugin native deps`);
|
|
10874
10947
|
await rebuildPluginNativeDeps(result.record.container, {
|
|
10875
10948
|
volume: result.record.claudeConfigVolume ?? SHARED_CLAUDE_VOLUME,
|
|
10876
|
-
onProgress: (line) =>
|
|
10949
|
+
onProgress: (line) => log44.write(line)
|
|
10877
10950
|
});
|
|
10878
|
-
|
|
10951
|
+
log44.write(`starting claude session`);
|
|
10879
10952
|
await startClaudeSession({
|
|
10880
10953
|
container: result.record.container,
|
|
10881
10954
|
claudeArgs: applyClaudeSkipPermissions(promptedArgs, cfg.effective),
|
|
@@ -10883,22 +10956,22 @@ async function runDockerJob(job, log45, onBoxCreated) {
|
|
|
10883
10956
|
boxName: result.record.name
|
|
10884
10957
|
});
|
|
10885
10958
|
} else if (job.agent === "codex") {
|
|
10886
|
-
|
|
10959
|
+
log44.write(`checking codex`);
|
|
10887
10960
|
await ensureCodexInstalled(result.record.container, {
|
|
10888
|
-
onProgress: (line) =>
|
|
10961
|
+
onProgress: (line) => log44.write(line)
|
|
10889
10962
|
});
|
|
10890
|
-
|
|
10963
|
+
log44.write(`starting codex session`);
|
|
10891
10964
|
await startCodexSession({
|
|
10892
10965
|
container: result.record.container,
|
|
10893
10966
|
codexArgs: applyCodexSkipPermissions(promptedArgs, cfg.effective),
|
|
10894
10967
|
sessionName: cfg.effective.codex.sessionName
|
|
10895
10968
|
});
|
|
10896
10969
|
} else if (job.agent === "opencode") {
|
|
10897
|
-
|
|
10970
|
+
log44.write(`checking opencode`);
|
|
10898
10971
|
await ensureOpencodeInstalled(result.record.container, {
|
|
10899
|
-
onProgress: (line) =>
|
|
10972
|
+
onProgress: (line) => log44.write(line)
|
|
10900
10973
|
});
|
|
10901
|
-
|
|
10974
|
+
log44.write(`starting opencode session`);
|
|
10902
10975
|
await startOpencodeSession({
|
|
10903
10976
|
container: result.record.container,
|
|
10904
10977
|
opencodeArgs: promptedArgs,
|
|
@@ -10937,7 +11010,7 @@ function buildOverridesFromJob(job) {
|
|
|
10937
11010
|
|
|
10938
11011
|
// src/commands/screen.ts
|
|
10939
11012
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
10940
|
-
import { log as
|
|
11013
|
+
import { log as log36 } from "@clack/prompts";
|
|
10941
11014
|
import { Command as Command36 } from "commander";
|
|
10942
11015
|
var SIGNED_URL_TTL_MIN = 1;
|
|
10943
11016
|
var SIGNED_URL_TTL_MAX = 86400;
|
|
@@ -10968,10 +11041,10 @@ var screenCommand = new Command36("screen").description("Open a box's VNC (noVNC
|
|
|
10968
11041
|
if (provider === "docker") {
|
|
10969
11042
|
const insp = await inspectBox(box.id);
|
|
10970
11043
|
if (insp.state === "paused") {
|
|
10971
|
-
|
|
11044
|
+
log36.info("box is paused; unpausing");
|
|
10972
11045
|
await unpauseBox(box.id);
|
|
10973
11046
|
} else if (insp.state === "stopped") {
|
|
10974
|
-
|
|
11047
|
+
log36.info("box is stopped; starting");
|
|
10975
11048
|
await startBox(box.id);
|
|
10976
11049
|
} else if (insp.state === "missing") {
|
|
10977
11050
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
@@ -10981,13 +11054,13 @@ var screenCommand = new Command36("screen").description("Open a box's VNC (noVNC
|
|
|
10981
11054
|
const inBoxUrl = exposePort !== void 0 ? box.portlessUrl ?? `http://localhost:${String(exposePort)}` : "about:blank";
|
|
10982
11055
|
const br = await ensureBoxBrowser(box.container, void 0, inBoxUrl);
|
|
10983
11056
|
if (br.up && !br.alreadyRunning) {
|
|
10984
|
-
|
|
11057
|
+
log36.info(
|
|
10985
11058
|
exposePort !== void 0 ? `opened ${inBoxUrl} in the in-box browser (visible in the VNC view)` : "started in-box browser"
|
|
10986
11059
|
);
|
|
10987
11060
|
} else if (br.alreadyRunning) {
|
|
10988
|
-
|
|
11061
|
+
log36.info("in-box browser already running; left it untouched");
|
|
10989
11062
|
} else {
|
|
10990
|
-
|
|
11063
|
+
log36.warn(`could not start in-box browser: ${br.reason ?? "unknown"}`);
|
|
10991
11064
|
}
|
|
10992
11065
|
const engine = await detectEngine();
|
|
10993
11066
|
const urls = buildVncUrls(box, engine);
|
|
@@ -11008,10 +11081,10 @@ var screenCommand = new Command36("screen").description("Open a box's VNC (noVNC
|
|
|
11008
11081
|
const p = await providerForBox(box);
|
|
11009
11082
|
const state = await p.probeState(box);
|
|
11010
11083
|
if (state === "paused") {
|
|
11011
|
-
|
|
11084
|
+
log36.info("box is paused; resuming");
|
|
11012
11085
|
await p.resume(box);
|
|
11013
11086
|
} else if (state === "stopped") {
|
|
11014
|
-
|
|
11087
|
+
log36.info("box is stopped; starting");
|
|
11015
11088
|
await p.start(box);
|
|
11016
11089
|
} else if (state === "missing") {
|
|
11017
11090
|
throw new Error(`cloud sandbox for ${box.name} is missing; was it deleted?`);
|
|
@@ -11026,14 +11099,14 @@ var screenCommand = new Command36("screen").description("Open a box's VNC (noVNC
|
|
|
11026
11099
|
user: "vscode"
|
|
11027
11100
|
});
|
|
11028
11101
|
if (br.exitCode === 0) {
|
|
11029
|
-
|
|
11102
|
+
log36.info(`opened ${webUrl} in the in-box browser (visible in the VNC view)`);
|
|
11030
11103
|
} else {
|
|
11031
|
-
|
|
11104
|
+
log36.warn(
|
|
11032
11105
|
`could not open in-box browser (continuing): ${br.stderr.trim() || br.stdout.trim() || `exit ${String(br.exitCode)}`}`
|
|
11033
11106
|
);
|
|
11034
11107
|
}
|
|
11035
11108
|
} catch (err) {
|
|
11036
|
-
|
|
11109
|
+
log36.warn(
|
|
11037
11110
|
`in-box browser skipped: ${err instanceof Error ? err.message : String(err)}`
|
|
11038
11111
|
);
|
|
11039
11112
|
}
|
|
@@ -11046,7 +11119,7 @@ var screenCommand = new Command36("screen").description("Open a box's VNC (noVNC
|
|
|
11046
11119
|
`);
|
|
11047
11120
|
return;
|
|
11048
11121
|
}
|
|
11049
|
-
const opened = spawnSync3(
|
|
11122
|
+
const opened = spawnSync3(hostOpenCommand(), [url], { stdio: "inherit" });
|
|
11050
11123
|
if (opened.status !== 0) {
|
|
11051
11124
|
throw new Error(`open ${url} failed (exit ${String(opened.status ?? "n/a")})`);
|
|
11052
11125
|
}
|
|
@@ -11059,18 +11132,18 @@ var screenCommand = new Command36("screen").description("Open a box's VNC (noVNC
|
|
|
11059
11132
|
|
|
11060
11133
|
// src/commands/shell.ts
|
|
11061
11134
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
11062
|
-
import { log as
|
|
11135
|
+
import { log as log38 } from "@clack/prompts";
|
|
11063
11136
|
import { Command as Command37 } from "commander";
|
|
11064
11137
|
|
|
11065
11138
|
// src/commands/_provider-guard.ts
|
|
11066
|
-
import { log as
|
|
11139
|
+
import { log as log37 } from "@clack/prompts";
|
|
11067
11140
|
function requireDockerProvider(box, commandName) {
|
|
11068
11141
|
const provider = box.provider ?? "docker";
|
|
11069
11142
|
if (provider === "docker") return;
|
|
11070
|
-
|
|
11143
|
+
log37.error(
|
|
11071
11144
|
`\`agentbox ${commandName}\` doesn't yet support cloud boxes (this box's provider is '${provider}').`
|
|
11072
11145
|
);
|
|
11073
|
-
|
|
11146
|
+
log37.info(
|
|
11074
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."
|
|
11075
11148
|
);
|
|
11076
11149
|
process.exit(2);
|
|
@@ -11110,10 +11183,10 @@ function fmtAgo2(iso) {
|
|
|
11110
11183
|
async function ensureBoxRunning(box) {
|
|
11111
11184
|
const insp = await inspectBox(box.id);
|
|
11112
11185
|
if (insp.state === "paused") {
|
|
11113
|
-
|
|
11186
|
+
log38.info("box is paused; unpausing");
|
|
11114
11187
|
await unpauseBox(box.id);
|
|
11115
11188
|
} else if (insp.state === "stopped") {
|
|
11116
|
-
|
|
11189
|
+
log38.info("box is stopped; starting");
|
|
11117
11190
|
await startBox(box.id);
|
|
11118
11191
|
} else if (insp.state === "missing") {
|
|
11119
11192
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
@@ -11152,7 +11225,7 @@ async function startOrAttachShell(box, cfg) {
|
|
|
11152
11225
|
const label = shellLabel(cfg.sessionName);
|
|
11153
11226
|
const info = await shellSessionInfo(box.container, cfg.sessionName, cfg.user);
|
|
11154
11227
|
if (info.running) {
|
|
11155
|
-
|
|
11228
|
+
log38.info(`reattaching to shell "${label}" \u2014 Control+a d to detach`);
|
|
11156
11229
|
} else {
|
|
11157
11230
|
await startShellSession({
|
|
11158
11231
|
container: box.container,
|
|
@@ -11160,7 +11233,7 @@ async function startOrAttachShell(box, cfg) {
|
|
|
11160
11233
|
user: cfg.user,
|
|
11161
11234
|
login: cfg.login
|
|
11162
11235
|
});
|
|
11163
|
-
|
|
11236
|
+
log38.info(`shell "${label}" \u2014 Control+a d to detach, leaves it running`);
|
|
11164
11237
|
}
|
|
11165
11238
|
const code = await runWrappedAttach({
|
|
11166
11239
|
container: box.container,
|
|
@@ -11313,11 +11386,11 @@ var shellLsCommand = new Command37("ls").description("List the shell tmux sessio
|
|
|
11313
11386
|
requireDockerProvider(box, "shell");
|
|
11314
11387
|
const insp = await inspectBox(box.id);
|
|
11315
11388
|
if (insp.state !== "running") {
|
|
11316
|
-
|
|
11389
|
+
log38.info(`box ${box.name} is ${insp.state} \u2014 no live shell sessions`);
|
|
11317
11390
|
return;
|
|
11318
11391
|
}
|
|
11319
11392
|
if (insp.shellSessions.length === 0) {
|
|
11320
|
-
|
|
11393
|
+
log38.info(
|
|
11321
11394
|
`no shell sessions in ${box.name} \u2014 start one with: agentbox shell ${reattachRef4(box)}`
|
|
11322
11395
|
);
|
|
11323
11396
|
return;
|
|
@@ -11337,25 +11410,25 @@ var shellKillCommand = new Command37("kill").description("Kill a shell tmux sess
|
|
|
11337
11410
|
requireDockerProvider(box, "shell");
|
|
11338
11411
|
const insp = await inspectBox(box.id);
|
|
11339
11412
|
if (insp.state !== "running") {
|
|
11340
|
-
|
|
11413
|
+
log38.info(`box ${box.name} is ${insp.state} \u2014 no shell sessions to kill`);
|
|
11341
11414
|
return;
|
|
11342
11415
|
}
|
|
11343
11416
|
if (opts.all) {
|
|
11344
11417
|
if (insp.shellSessions.length === 0) {
|
|
11345
|
-
|
|
11418
|
+
log38.info(`no shell sessions in ${box.name}`);
|
|
11346
11419
|
return;
|
|
11347
11420
|
}
|
|
11348
11421
|
let killed = 0;
|
|
11349
11422
|
for (const s of insp.shellSessions) {
|
|
11350
11423
|
if (await killShellSession(box.container, s.sessionName)) killed++;
|
|
11351
11424
|
}
|
|
11352
|
-
|
|
11425
|
+
log38.success(`killed ${String(killed)} shell session${killed === 1 ? "" : "s"} in ${box.name}`);
|
|
11353
11426
|
return;
|
|
11354
11427
|
}
|
|
11355
11428
|
const target = shellSessionName(opts.name);
|
|
11356
11429
|
const ok = await killShellSession(box.container, target);
|
|
11357
|
-
if (ok)
|
|
11358
|
-
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?)`);
|
|
11359
11432
|
} catch (err) {
|
|
11360
11433
|
handleLifecycleError(err);
|
|
11361
11434
|
}
|
|
@@ -11389,7 +11462,7 @@ var startCommand = new Command38("start").description(
|
|
|
11389
11462
|
});
|
|
11390
11463
|
|
|
11391
11464
|
// src/commands/status.ts
|
|
11392
|
-
import { log as
|
|
11465
|
+
import { log as log40 } from "@clack/prompts";
|
|
11393
11466
|
import { Command as Command39 } from "commander";
|
|
11394
11467
|
|
|
11395
11468
|
// src/endpoints-render.ts
|
|
@@ -11420,7 +11493,7 @@ function renderEndpointLines(endpoints, stream) {
|
|
|
11420
11493
|
}
|
|
11421
11494
|
|
|
11422
11495
|
// src/commands/inspect.ts
|
|
11423
|
-
import { log as
|
|
11496
|
+
import { log as log39 } from "@clack/prompts";
|
|
11424
11497
|
function fmtLimit(n, unit) {
|
|
11425
11498
|
return n && n > 0 ? `${String(n)}${unit}` : "unlimited";
|
|
11426
11499
|
}
|
|
@@ -11565,7 +11638,7 @@ function renderCodexActivityCloud(persisted) {
|
|
|
11565
11638
|
async function runInspect(box, opts) {
|
|
11566
11639
|
try {
|
|
11567
11640
|
if (opts.json && opts.watch) {
|
|
11568
|
-
|
|
11641
|
+
log39.error("cannot combine --json with --watch");
|
|
11569
11642
|
process.exit(2);
|
|
11570
11643
|
}
|
|
11571
11644
|
const isCloud = (box.provider ?? "docker") !== "docker";
|
|
@@ -11609,7 +11682,7 @@ var statusCommand2 = withWatchOptions(
|
|
|
11609
11682
|
).action(async (idOrName, opts) => {
|
|
11610
11683
|
try {
|
|
11611
11684
|
if (opts.json && opts.watch) {
|
|
11612
|
-
|
|
11685
|
+
log40.error("cannot combine --json with --watch");
|
|
11613
11686
|
process.exit(2);
|
|
11614
11687
|
}
|
|
11615
11688
|
const box = await resolveBoxOrExit(idOrName);
|
|
@@ -11938,7 +12011,7 @@ var unpauseCommand = new Command42("unpause").description(
|
|
|
11938
12011
|
|
|
11939
12012
|
// src/commands/update.ts
|
|
11940
12013
|
import { spawn as spawn4 } from "child_process";
|
|
11941
|
-
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";
|
|
11942
12015
|
import { Command as Command43 } from "commander";
|
|
11943
12016
|
|
|
11944
12017
|
// src/exec-method.ts
|
|
@@ -11993,7 +12066,7 @@ var updateCommand = new Command43("self-update").description(
|
|
|
11993
12066
|
});
|
|
11994
12067
|
intro8("agentbox self-update");
|
|
11995
12068
|
const selfStep = opts.skipSelf ? "self-update: skipped (--skip-self)" : describeSelfUpdate(method);
|
|
11996
|
-
|
|
12069
|
+
log41.info(
|
|
11997
12070
|
[
|
|
11998
12071
|
"plan:",
|
|
11999
12072
|
` ${selfStep}`,
|
|
@@ -12002,31 +12075,31 @@ var updateCommand = new Command43("self-update").description(
|
|
|
12002
12075
|
].join("\n")
|
|
12003
12076
|
);
|
|
12004
12077
|
if (opts.dryRun) {
|
|
12005
|
-
|
|
12078
|
+
outro7("dry run \u2014 nothing changed");
|
|
12006
12079
|
return;
|
|
12007
12080
|
}
|
|
12008
12081
|
if (!opts.yes) {
|
|
12009
12082
|
const ok = await confirm16({ message: "Proceed with update?", initialValue: true });
|
|
12010
12083
|
if (isCancel17(ok) || !ok) {
|
|
12011
|
-
|
|
12084
|
+
log41.info("cancelled");
|
|
12012
12085
|
return;
|
|
12013
12086
|
}
|
|
12014
12087
|
}
|
|
12015
12088
|
let selfUpdated = false;
|
|
12016
12089
|
if (opts.skipSelf) {
|
|
12017
|
-
|
|
12090
|
+
log41.info("skipping self-update (--skip-self)");
|
|
12018
12091
|
} else {
|
|
12019
12092
|
const cmd = selfUpdateCommand(method);
|
|
12020
12093
|
if (cmd === null) {
|
|
12021
|
-
|
|
12094
|
+
log41.info(describeSelfUpdate(method));
|
|
12022
12095
|
} else {
|
|
12023
|
-
|
|
12096
|
+
log41.info(`running: ${cmd.cmd} ${cmd.args.join(" ")}`);
|
|
12024
12097
|
const code = await runInherit(cmd.cmd, cmd.args);
|
|
12025
12098
|
if (code !== 0) {
|
|
12026
12099
|
throw new Error(`${cmd.cmd} exited with code ${String(code)}`);
|
|
12027
12100
|
}
|
|
12028
12101
|
selfUpdated = true;
|
|
12029
|
-
|
|
12102
|
+
log41.success(`updated ${PKG} via ${cmd.cmd}`);
|
|
12030
12103
|
}
|
|
12031
12104
|
}
|
|
12032
12105
|
const s = spinner10();
|
|
@@ -12042,7 +12115,7 @@ var updateCommand = new Command43("self-update").description(
|
|
|
12042
12115
|
stop.stopped ? `stopped relay (pid ${String(stop.pid)})` : "relay was not running"
|
|
12043
12116
|
);
|
|
12044
12117
|
if (selfUpdated) {
|
|
12045
|
-
|
|
12118
|
+
log41.info(
|
|
12046
12119
|
"relay will restart automatically (with the updated build) on your next `agentbox create` / `agentbox claude`"
|
|
12047
12120
|
);
|
|
12048
12121
|
} else {
|
|
@@ -12053,12 +12126,12 @@ var updateCommand = new Command43("self-update").description(
|
|
|
12053
12126
|
sr2.stop(`relay back up on ${ep.hostUrl}`);
|
|
12054
12127
|
} catch (err) {
|
|
12055
12128
|
sr2.stop("relay restart failed");
|
|
12056
|
-
|
|
12129
|
+
log41.warn(
|
|
12057
12130
|
`${err instanceof Error ? err.message : String(err)} \u2014 it will retry on the next box command`
|
|
12058
12131
|
);
|
|
12059
12132
|
}
|
|
12060
12133
|
}
|
|
12061
|
-
|
|
12134
|
+
outro7("update complete");
|
|
12062
12135
|
} catch (err) {
|
|
12063
12136
|
handleLifecycleError(err);
|
|
12064
12137
|
}
|
|
@@ -12066,7 +12139,7 @@ var updateCommand = new Command43("self-update").description(
|
|
|
12066
12139
|
|
|
12067
12140
|
// src/commands/url.ts
|
|
12068
12141
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
12069
|
-
import { log as
|
|
12142
|
+
import { log as log42 } from "@clack/prompts";
|
|
12070
12143
|
import { Command as Command44 } from "commander";
|
|
12071
12144
|
var SIGNED_URL_TTL_MIN2 = 1;
|
|
12072
12145
|
var SIGNED_URL_TTL_MAX2 = 86400;
|
|
@@ -12099,10 +12172,10 @@ var urlCommand = new Command44("url").description(
|
|
|
12099
12172
|
if (provider === "docker") {
|
|
12100
12173
|
const insp = await inspectBox(box.id);
|
|
12101
12174
|
if (insp.state === "paused") {
|
|
12102
|
-
|
|
12175
|
+
log42.info("box is paused; unpausing");
|
|
12103
12176
|
await unpauseBox(box.id);
|
|
12104
12177
|
} else if (insp.state === "stopped") {
|
|
12105
|
-
|
|
12178
|
+
log42.info("box is stopped; starting");
|
|
12106
12179
|
await startBox(box.id);
|
|
12107
12180
|
} else if (insp.state === "missing") {
|
|
12108
12181
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
@@ -12131,10 +12204,10 @@ var urlCommand = new Command44("url").description(
|
|
|
12131
12204
|
const p = await providerForBox(box);
|
|
12132
12205
|
const state = await p.probeState(box);
|
|
12133
12206
|
if (state === "paused") {
|
|
12134
|
-
|
|
12207
|
+
log42.info("box is paused; resuming");
|
|
12135
12208
|
await p.resume(box);
|
|
12136
12209
|
} else if (state === "stopped") {
|
|
12137
|
-
|
|
12210
|
+
log42.info("box is stopped; starting");
|
|
12138
12211
|
await p.start(box);
|
|
12139
12212
|
} else if (state === "missing") {
|
|
12140
12213
|
throw new Error(`cloud sandbox for ${box.name} is missing; was it deleted?`);
|
|
@@ -12146,7 +12219,7 @@ var urlCommand = new Command44("url").description(
|
|
|
12146
12219
|
`);
|
|
12147
12220
|
return;
|
|
12148
12221
|
}
|
|
12149
|
-
const opened = spawnSync5(
|
|
12222
|
+
const opened = spawnSync5(hostOpenCommand(), [url], { stdio: "inherit" });
|
|
12150
12223
|
if (opened.status !== 0) {
|
|
12151
12224
|
throw new Error(`open ${url} failed (exit ${String(opened.status ?? "n/a")})`);
|
|
12152
12225
|
}
|
|
@@ -12158,7 +12231,7 @@ var urlCommand = new Command44("url").description(
|
|
|
12158
12231
|
});
|
|
12159
12232
|
|
|
12160
12233
|
// src/commands/wait.ts
|
|
12161
|
-
import { log as
|
|
12234
|
+
import { log as log43 } from "@clack/prompts";
|
|
12162
12235
|
import { Command as Command45 } from "commander";
|
|
12163
12236
|
var waitCommand = new Command45("wait").description("Block until the box reports all autostart units ready").argument(
|
|
12164
12237
|
"[box]",
|
|
@@ -12176,7 +12249,7 @@ var waitCommand = new Command45("wait").description("Block until the box reports
|
|
|
12176
12249
|
try {
|
|
12177
12250
|
parsed = JSON.parse(proc.stdout);
|
|
12178
12251
|
} catch {
|
|
12179
|
-
|
|
12252
|
+
log43.error(`agentbox-ctl wait-ready failed: ${proc.stderr || proc.stdout}`);
|
|
12180
12253
|
process.exit(1);
|
|
12181
12254
|
}
|
|
12182
12255
|
if (opts.json) {
|