@madarco/agentbox 0.14.0 → 0.15.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/{_cloud-attach-GUBB5RH2.js → _cloud-attach-R6TRWG5L.js} +3 -3
  3. package/dist/{chunk-BYCLD6D6.js → chunk-43Q5GWP6.js} +98 -54
  4. package/dist/chunk-43Q5GWP6.js.map +1 -0
  5. package/dist/{chunk-VATTS2MR.js → chunk-72CJTXN6.js} +2 -2
  6. package/dist/{chunk-TBSIJVSN.js → chunk-E7CHS7ZR.js} +21 -13
  7. package/dist/chunk-E7CHS7ZR.js.map +1 -0
  8. package/dist/{chunk-LDMYHWUS.js → chunk-MCOU6CZS.js} +2 -2
  9. package/dist/{chunk-TCS5HXJX.js → chunk-MLMFNN4T.js} +396 -324
  10. package/dist/chunk-MLMFNN4T.js.map +1 -0
  11. package/dist/{dist-J2IHD5T7.js → dist-AGTIA7AD.js} +3 -3
  12. package/dist/{dist-3IMQNTTV.js → dist-FIFEFKJ7.js} +3 -3
  13. package/dist/{dist-34RKQ74M.js → dist-JZ3XO6EB.js} +4 -4
  14. package/dist/{dist-4DPOL5A7.js → dist-OGJGZETZ.js} +2 -2
  15. package/dist/{dist-57M6ZA7H.js → dist-S4XR4ACV.js} +4 -4
  16. package/dist/index.js +868 -300
  17. package/dist/index.js.map +1 -1
  18. package/package.json +5 -5
  19. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +30 -0
  20. package/runtime/docker/packages/ctl/dist/bin.cjs +321 -27
  21. package/runtime/e2b/agentbox-setup-skill.md +30 -0
  22. package/runtime/e2b/ctl.cjs +321 -27
  23. package/runtime/hetzner/agentbox-setup-skill.md +30 -0
  24. package/runtime/hetzner/ctl.cjs +321 -27
  25. package/runtime/relay/bin.cjs +83 -5
  26. package/runtime/vercel/agentbox-setup-skill.md +30 -0
  27. package/runtime/vercel/ctl.cjs +321 -27
  28. package/share/agentbox-setup/SKILL.md +30 -0
  29. package/share/host-skills/agentbox-info/SKILL.md +21 -1
  30. package/dist/chunk-BYCLD6D6.js.map +0 -1
  31. package/dist/chunk-TBSIJVSN.js.map +0 -1
  32. package/dist/chunk-TCS5HXJX.js.map +0 -1
  33. /package/dist/{_cloud-attach-GUBB5RH2.js.map → _cloud-attach-R6TRWG5L.js.map} +0 -0
  34. /package/dist/{chunk-VATTS2MR.js.map → chunk-72CJTXN6.js.map} +0 -0
  35. /package/dist/{chunk-LDMYHWUS.js.map → chunk-MCOU6CZS.js.map} +0 -0
  36. /package/dist/{dist-J2IHD5T7.js.map → dist-AGTIA7AD.js.map} +0 -0
  37. /package/dist/{dist-3IMQNTTV.js.map → dist-FIFEFKJ7.js.map} +0 -0
  38. /package/dist/{dist-34RKQ74M.js.map → dist-JZ3XO6EB.js.map} +0 -0
  39. /package/dist/{dist-4DPOL5A7.js.map → dist-OGJGZETZ.js.map} +0 -0
  40. /package/dist/{dist-57M6ZA7H.js.map → dist-S4XR4ACV.js.map} +0 -0
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  clipboardCaptureAvailable,
9
9
  cloudAgentAttach,
10
10
  cloudAgentStartDetached,
11
+ cmuxBinary,
11
12
  createMenuLines,
12
13
  detectHostTerminal,
13
14
  getProvider,
@@ -26,10 +27,11 @@ import {
26
27
  runWrappedAttach,
27
28
  setTerminalTitle,
28
29
  sidebarLines,
30
+ spawnInNewTerminal,
29
31
  statusLine,
30
32
  stripTitleGlyph,
31
33
  subscribePrompts
32
- } from "./chunk-BYCLD6D6.js";
34
+ } from "./chunk-43Q5GWP6.js";
33
35
  import {
34
36
  daytonaBackend,
35
37
  ensureDaytonaCredentials,
@@ -57,14 +59,14 @@ import {
57
59
  readPreparedState as readPreparedState2,
58
60
  readVercelCredStatus,
59
61
  secretsPath as secretsPath3
60
- } from "./chunk-VATTS2MR.js";
62
+ } from "./chunk-72CJTXN6.js";
61
63
  import {
62
64
  ensureE2bCredentials,
63
65
  maskKey as maskKey4,
64
66
  readE2bCredStatus,
65
67
  readPreparedState as readPreparedState3,
66
68
  secretsPath as secretsPath4
67
- } from "./chunk-LDMYHWUS.js";
69
+ } from "./chunk-MCOU6CZS.js";
68
70
  import {
69
71
  agentSpecsForCloud,
70
72
  currentCloudBaseFingerprint,
@@ -74,10 +76,11 @@ import {
74
76
  probeCloudCheckpoint,
75
77
  resolveCloudCheckpoint,
76
78
  seedAgentVolumesIfFresh
77
- } from "./chunk-TBSIJVSN.js";
79
+ } from "./chunk-E7CHS7ZR.js";
78
80
  import {
79
81
  AmbiguousBoxError,
80
82
  BOX_STATUS_EVENT,
83
+ BUILT_IN_DEFAULTS,
81
84
  BoxNotFoundError,
82
85
  CODEX_CREDENTIALS_BACKUP_FILE,
83
86
  ClaudeSessionError,
@@ -229,7 +232,7 @@ import {
229
232
  waitForTmuxPaneContent,
230
233
  warmUpClaudeCredentials,
231
234
  writeJob
232
- } from "./chunk-TCS5HXJX.js";
235
+ } from "./chunk-MLMFNN4T.js";
233
236
  import {
234
237
  DEFAULT_BOX_IMAGE,
235
238
  STATE_DIR,
@@ -246,8 +249,8 @@ import {
246
249
  import "./chunk-G3H2L3O2.js";
247
250
 
248
251
  // src/version.ts
249
- var AGENTBOX_VERSION = true ? "0.14.0" : "0.0.0-dev";
250
- var AGENTBOX_COMMIT = true ? "38f96cffd" : "dev";
252
+ var AGENTBOX_VERSION = true ? "0.15.0" : "0.0.0-dev";
253
+ var AGENTBOX_COMMIT = true ? "dfe136d17" : "dev";
251
254
 
252
255
  // src/index.ts
253
256
  import { Command as Command48 } from "commander";
@@ -430,6 +433,178 @@ function derivedAgentState(claude) {
430
433
  return claude.state;
431
434
  }
432
435
 
436
+ // src/lib/agent-answer.ts
437
+ import { createHash } from "crypto";
438
+ var TUI_ID_PREFIX = "tui";
439
+ function inTuiKind(claude) {
440
+ if (claude.state === "working" || claude.state === "compacting") return null;
441
+ if (claude.plan !== void 0) return "plan";
442
+ if (claude.question !== void 0) return "question";
443
+ if (claude.state === "waiting") return "permission";
444
+ return null;
445
+ }
446
+ function digestForBlock(claude, kind) {
447
+ let material;
448
+ if (kind === "plan") {
449
+ material = `plan|${claude.plan?.capturedAt ?? ""}|${claude.plan?.plan ?? ""}`;
450
+ } else if (kind === "question") {
451
+ material = `question|${claude.question?.capturedAt ?? ""}|${JSON.stringify(claude.question?.questions ?? [])}`;
452
+ } else {
453
+ material = `permission|${claude.updatedAt ?? ""}`;
454
+ }
455
+ return createHash("sha256").update(material).digest("hex").slice(0, 12);
456
+ }
457
+ function mintTuiId(boxId, claude) {
458
+ const kind = inTuiKind(claude);
459
+ if (kind === null) return null;
460
+ return { id: `${TUI_ID_PREFIX}:${boxId}:${kind}:${digestForBlock(claude, kind)}`, kind };
461
+ }
462
+ function parseTuiId(id) {
463
+ const parts = id.split(":");
464
+ if (parts.length !== 4 || parts[0] !== TUI_ID_PREFIX) return null;
465
+ const [, boxId, kind, digest] = parts;
466
+ if (!boxId || !digest) return null;
467
+ if (kind !== "plan" && kind !== "question" && kind !== "permission") return null;
468
+ return { boxId, kind, digest };
469
+ }
470
+ function isTuiId(id) {
471
+ return id.startsWith(`${TUI_ID_PREFIX}:`);
472
+ }
473
+ var OPTION_ENTER_DELAY_MS = 150;
474
+ function answerKeystrokes(_agent, _kind, decision) {
475
+ if (decision.deny === true) {
476
+ return [{ type: "key", value: "Escape" }];
477
+ }
478
+ if (decision.option !== void 0) {
479
+ return [
480
+ { type: "literal", value: String(decision.option) },
481
+ { type: "delay", ms: OPTION_ENTER_DELAY_MS },
482
+ { type: "key", value: "Enter" }
483
+ ];
484
+ }
485
+ return [{ type: "key", value: "Enter" }];
486
+ }
487
+ function resolveQuestionOption(claude, raw) {
488
+ const options = claude.question?.questions?.[0]?.options ?? [];
489
+ const asNum = Number.parseInt(raw, 10);
490
+ if (Number.isFinite(asNum) && String(asNum) === raw.trim()) {
491
+ return asNum >= 1 && asNum <= options.length ? asNum : null;
492
+ }
493
+ const needle = raw.trim().toLowerCase();
494
+ const exact = options.findIndex((o) => o.label.toLowerCase() === needle);
495
+ if (exact !== -1) return exact + 1;
496
+ const prefix = options.findIndex((o) => o.label.toLowerCase().startsWith(needle));
497
+ return prefix !== -1 ? prefix + 1 : null;
498
+ }
499
+
500
+ // src/lib/drive/tmux.ts
501
+ var TMUX_USER = "vscode";
502
+ async function captureSession(provider, box, session, opts = {}) {
503
+ const argv2 = ["tmux", "capture-pane", opts.ansi ? "-pe" : "-p", "-t", session];
504
+ if (opts.rows) {
505
+ argv2.push("-S", String(opts.rows.from), "-E", String(opts.rows.to));
506
+ }
507
+ const res = await provider.exec(box, argv2, { user: TMUX_USER });
508
+ if (res.exitCode !== 0) {
509
+ throw new Error(failure("capture-pane", session, res.stderr || res.stdout));
510
+ }
511
+ return res.stdout.replace(/\n$/, "");
512
+ }
513
+ async function paneInfo(provider, box, session) {
514
+ const fmt = "#{pane_width},#{pane_height},#{cursor_x},#{cursor_y}";
515
+ const res = await provider.exec(box, ["tmux", "display-message", "-p", "-t", session, fmt], {
516
+ user: TMUX_USER
517
+ });
518
+ if (res.exitCode !== 0) {
519
+ throw new Error(failure("display-message", session, res.stderr || res.stdout));
520
+ }
521
+ const m = /^(\d+),(\d+),(\d+),(\d+)/.exec(res.stdout.trim());
522
+ if (!m) throw new Error(`tmux display-message returned unexpected output: ${res.stdout}`);
523
+ return {
524
+ cols: Number(m[1]),
525
+ rows: Number(m[2]),
526
+ cursor: { x: Number(m[3]), y: Number(m[4]) }
527
+ };
528
+ }
529
+ async function sendLiteral(provider, box, session, literal) {
530
+ if (literal.length === 0) return;
531
+ const res = await provider.exec(box, ["tmux", "send-keys", "-t", session, "-l", "--", literal], {
532
+ user: TMUX_USER
533
+ });
534
+ if (res.exitCode !== 0) {
535
+ throw new Error(failure("send-keys -l", session, res.stderr || res.stdout));
536
+ }
537
+ }
538
+ async function sendKey(provider, box, session, key) {
539
+ const res = await provider.exec(box, ["tmux", "send-keys", "-t", session, key], {
540
+ user: TMUX_USER
541
+ });
542
+ if (res.exitCode !== 0) {
543
+ throw new Error(failure("send-keys", session, res.stderr || res.stdout));
544
+ }
545
+ }
546
+ async function resizeWindow(provider, box, session, cols, rows) {
547
+ const res = await provider.exec(
548
+ box,
549
+ ["tmux", "resize-window", "-t", session, "-x", String(cols), "-y", String(rows)],
550
+ { user: TMUX_USER }
551
+ );
552
+ if (res.exitCode !== 0) {
553
+ throw new Error(failure("resize-window", session, res.stderr || res.stdout));
554
+ }
555
+ }
556
+ async function listSessions(provider, box) {
557
+ const res = await provider.exec(
558
+ box,
559
+ ["tmux", "list-sessions", "-F", "#{session_name}"],
560
+ { user: TMUX_USER }
561
+ );
562
+ if (res.exitCode !== 0) return [];
563
+ return res.stdout.split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
564
+ }
565
+ function failure(op, session, detail) {
566
+ const tail = detail.trim();
567
+ return `tmux ${op} failed for session '${session}'${tail ? `: ${tail}` : ""}`;
568
+ }
569
+
570
+ // src/lib/drive/session.ts
571
+ var AGENT_SESSION_PRIORITY = ["claude", "codex", "opencode"];
572
+ async function resolveDriveSession(provider, box, explicit) {
573
+ if (await provider.probeState(box) === "paused") {
574
+ process.stderr.write(`drive: box ${box.name} is paused; unpausing
575
+ `);
576
+ await provider.resume(box);
577
+ }
578
+ const sessions = await listSessions(provider, box);
579
+ if (explicit !== void 0 && explicit !== "") {
580
+ if (!sessions.includes(explicit)) {
581
+ throw new SessionNotFoundError(explicit, sessions);
582
+ }
583
+ return { name: explicit, available: sessions };
584
+ }
585
+ for (const candidate of AGENT_SESSION_PRIORITY) {
586
+ if (sessions.includes(candidate)) {
587
+ return { name: candidate, available: sessions };
588
+ }
589
+ }
590
+ if (sessions.length === 1 && sessions[0]) {
591
+ return { name: sessions[0], available: sessions };
592
+ }
593
+ throw new SessionNotFoundError(void 0, sessions);
594
+ }
595
+ var SessionNotFoundError = class extends Error {
596
+ wanted;
597
+ available;
598
+ constructor(wanted, available) {
599
+ const head = wanted ? `no tmux session '${wanted}' in this box` : "no agent tmux session running in this box";
600
+ const tail = available.length ? ` (running: ${available.join(", ")})` : " (tmux server not running or no sessions)";
601
+ super(head + tail);
602
+ this.name = "SessionNotFoundError";
603
+ this.wanted = wanted;
604
+ this.available = available;
605
+ }
606
+ };
607
+
433
608
  // src/lib/wait/events.ts
434
609
  var POLL_INTERVAL_MS = 500;
435
610
  var WaitTimeoutError = class extends Error {
@@ -606,9 +781,209 @@ var agentGetPlanQuestionCommand = new Command("get-plan-question").description("
606
781
  handleLifecycleError(err);
607
782
  }
608
783
  });
784
+ var agentApprovalsCommand = new Command("approvals").description(
785
+ "List everything a box is blocked on: relay host-action approvals (git push, cp host<->box, gh PR writes, checkpoint) AND the agent's in-TUI prompts (plan approval, question, tool permission). Each row carries an id to pass to `agent approve`."
786
+ ).argument("[box]", "box ref (default: only box in this project)").option("--json", "emit the pending approvals as a JSON array").option(
787
+ "--wait <ms>",
788
+ "block until at least one approval is pending (or this wall-clock cap elapses), then print"
789
+ ).action(async (boxRef, opts) => {
790
+ try {
791
+ const box = await resolveBoxOrExit(boxRef);
792
+ const relayUrl = (await ensureRelay()).hostUrl;
793
+ const waitMs = opts.wait !== void 0 ? parsePositiveInt(opts.wait, "--wait") : void 0;
794
+ let rows = await gatherApprovals(relayUrl, box);
795
+ if (waitMs !== void 0 && rows.length === 0) {
796
+ const start = Date.now();
797
+ while (rows.length === 0 && Date.now() - start < waitMs) {
798
+ await sleep2(Math.min(500, waitMs - (Date.now() - start)));
799
+ rows = await gatherApprovals(relayUrl, box);
800
+ }
801
+ }
802
+ if (opts.json === true) {
803
+ process.stdout.write(JSON.stringify(rows) + "\n");
804
+ return;
805
+ }
806
+ if (rows.length === 0) {
807
+ log3.info("nothing pending for this box (no relay approvals, agent not parked on a prompt)");
808
+ return;
809
+ }
810
+ for (const row2 of rows) {
811
+ process.stdout.write(approvalDisplay(row2) + "\n");
812
+ }
813
+ } catch (err) {
814
+ handleLifecycleError(err);
815
+ }
816
+ });
817
+ var agentApproveCommand = new Command("approve").description(
818
+ "Answer a pending approval by id (see `agent approvals`). The id is a safety token: you answer the exact prompt you inspected, and if a different one has since taken its place the approve is refused. Works for both relay host-action approvals and the agent's in-TUI prompts (plan / question / tool permission). Approves by default; --deny rejects."
819
+ ).argument("<id>", "approval id from `agent approvals` (relay UUID or a tui:... id)").option("--deny", "reject instead of approving").option("--cancel", "relay approvals only: dismiss (treated as denied; marks it cancelled)").option(
820
+ "--option <n|label>",
821
+ "in-TUI question/permission: pick this 1-based option (or match its label) instead of the default"
822
+ ).action(async (id, opts) => {
823
+ try {
824
+ if (isTuiId(id)) {
825
+ await approveInTui(id, opts);
826
+ return;
827
+ }
828
+ await approveRelay(id, opts);
829
+ } catch (err) {
830
+ handleLifecycleError(err);
831
+ }
832
+ });
833
+ async function approveRelay(id, opts) {
834
+ const relayUrl = (await ensureRelay()).hostUrl;
835
+ const cancelled = opts.cancel === true;
836
+ const answer = opts.deny === true || cancelled ? "n" : "y";
837
+ const url = new URL("/admin/prompts/answer", relayUrl);
838
+ const res = await fetch(url, {
839
+ method: "POST",
840
+ headers: { "Content-Type": "application/json" },
841
+ body: JSON.stringify({ id, answer, cancelled: cancelled || void 0 })
842
+ });
843
+ if (res.status === 204) {
844
+ log3.success(`approval ${id}: ${answer === "y" ? "approved" : "denied"}`);
845
+ return;
846
+ }
847
+ if (res.status === 404) {
848
+ log3.info(`approval ${id} already resolved (or expired)`);
849
+ return;
850
+ }
851
+ log3.error(`relay /admin/prompts/answer: HTTP ${String(res.status)}`);
852
+ process.exit(1);
853
+ }
854
+ async function approveInTui(id, opts) {
855
+ const parsed = parseTuiId(id);
856
+ if (!parsed) {
857
+ log3.error(`malformed in-TUI approval id: ${id}`);
858
+ process.exit(2);
859
+ }
860
+ if (opts.cancel === true) {
861
+ log3.error("--cancel applies to relay approvals only; use --deny for in-TUI prompts");
862
+ process.exit(2);
863
+ }
864
+ const box = await resolveBoxOrExit(parsed.boxId);
865
+ const status = await readBoxStatus(box);
866
+ const claude = status?.claude;
867
+ const current = claude ? mintTuiId(box.id, claude) : null;
868
+ if (!current || current.id !== id) {
869
+ log3.error(
870
+ `approval ${id} is no longer the pending prompt for ${box.name} (it changed or was answered) \u2014 re-run \`agentbox agent approvals ${box.name}\``
871
+ );
872
+ process.exit(1);
873
+ }
874
+ let option;
875
+ if (opts.option !== void 0) {
876
+ if (parsed.kind === "question" && claude) {
877
+ const resolved = resolveQuestionOption(claude, opts.option);
878
+ if (resolved === null) {
879
+ const labels = (claude.question?.questions?.[0]?.options ?? []).map((o) => o.label);
880
+ log3.error(`--option '${opts.option}' did not match an option (have: ${labels.join(" | ")})`);
881
+ process.exit(2);
882
+ }
883
+ option = resolved;
884
+ } else {
885
+ const n = Number.parseInt(opts.option, 10);
886
+ if (!Number.isFinite(n) || n < 1) {
887
+ log3.error(`--option must be a 1-based number for a ${parsed.kind} prompt (got: ${opts.option})`);
888
+ process.exit(2);
889
+ }
890
+ option = n;
891
+ }
892
+ }
893
+ const provider = await providerForBox(box);
894
+ const session = await resolveDriveSession(provider, box, void 0);
895
+ const agent = agentKindForSession(session.name);
896
+ const steps = answerKeystrokes(agent, parsed.kind, { option, deny: opts.deny });
897
+ await runAnswerSteps(provider, box, session.name, steps);
898
+ const verb = opts.deny === true ? "denied" : option !== void 0 ? `answered (option ${String(option)})` : "approved";
899
+ log3.success(`${parsed.kind} prompt on ${box.name}: ${verb}`);
900
+ }
901
+ function agentKindForSession(session) {
902
+ if (session === "codex") return "codex";
903
+ if (session === "opencode") return "opencode";
904
+ return "claude";
905
+ }
906
+ async function runAnswerSteps(provider, box, session, steps) {
907
+ for (const step of steps) {
908
+ if (step.type === "literal") await sendLiteral(provider, box, session, step.value);
909
+ else if (step.type === "key") await sendKey(provider, box, session, step.value);
910
+ else await sleep2(step.ms);
911
+ }
912
+ }
609
913
  agentCommand.addCommand(agentStateCommand);
610
914
  agentCommand.addCommand(agentWaitForCommand);
611
915
  agentCommand.addCommand(agentGetPlanQuestionCommand);
916
+ agentCommand.addCommand(agentApprovalsCommand);
917
+ agentCommand.addCommand(agentApproveCommand);
918
+ async function gatherApprovals(relayUrl, box) {
919
+ const rows = [];
920
+ const relay = await fetchRelayApprovals(relayUrl, box.id);
921
+ for (const ev of relay) {
922
+ rows.push({
923
+ id: ev.id,
924
+ kind: "host-action",
925
+ command: ev.context?.command,
926
+ argv: ev.context?.argv,
927
+ cwd: ev.context?.cwd,
928
+ message: ev.message,
929
+ detail: ev.detail,
930
+ defaultAnswer: ev.defaultAnswer
931
+ });
932
+ }
933
+ const claude = (await readBoxStatus(box))?.claude;
934
+ const tui = claude ? mintTuiId(box.id, claude) : null;
935
+ if (claude && tui) {
936
+ if (tui.kind === "plan") {
937
+ rows.push({ id: tui.id, kind: "plan", message: "Approve plan?", plan: claude.plan?.plan ?? "" });
938
+ } else if (tui.kind === "question") {
939
+ const q = claude.question?.questions?.[0];
940
+ rows.push({
941
+ id: tui.id,
942
+ kind: "question",
943
+ message: q?.question ?? "Answer question?",
944
+ options: (q?.options ?? []).map((o) => o.label)
945
+ });
946
+ } else {
947
+ rows.push({
948
+ id: tui.id,
949
+ kind: "permission",
950
+ message: "Tool-permission prompt (screen-driven; inspect with `agentbox drive snapshot`)",
951
+ state: claude.state
952
+ });
953
+ }
954
+ }
955
+ return rows;
956
+ }
957
+ async function fetchRelayApprovals(relayUrl, boxId) {
958
+ const url = new URL("/admin/prompts", relayUrl);
959
+ url.searchParams.set("boxId", boxId);
960
+ const res = await fetch(url);
961
+ if (!res.ok) throw new Error(`relay /admin/prompts: HTTP ${String(res.status)}`);
962
+ const body = await res.json();
963
+ return body.prompts ?? [];
964
+ }
965
+ function approvalDisplay(row2) {
966
+ if (row2.kind === "host-action") {
967
+ const cmd = row2.command ?? row2.message;
968
+ const argv2 = row2.argv?.length ? ` ${row2.argv.join(" ")}` : "";
969
+ const detail = row2.detail ? ` (${row2.detail})` : "";
970
+ return `${row2.id} [host-action] ${cmd}${argv2}${detail}`;
971
+ }
972
+ if (row2.kind === "plan") {
973
+ return `${row2.id} [plan] ${firstLine(row2.plan)}`;
974
+ }
975
+ if (row2.kind === "question") {
976
+ return `${row2.id} [question] ${row2.message} {${row2.options.join(" | ")}}`;
977
+ }
978
+ return `${row2.id} [permission] ${row2.message}`;
979
+ }
980
+ function firstLine(s) {
981
+ const line = s.split("\n", 1)[0] ?? "";
982
+ return line.length > 100 ? line.slice(0, 99) + "\u2026" : line;
983
+ }
984
+ function sleep2(ms) {
985
+ return new Promise((r) => setTimeout(r, ms));
986
+ }
612
987
  function emitMatch(claude, asJson) {
613
988
  if (asJson) {
614
989
  process.stdout.write(JSON.stringify(claude) + "\n");
@@ -727,18 +1102,141 @@ function buildPromptArgs(agentKind, prompt, userArgs) {
727
1102
  }
728
1103
 
729
1104
  // src/lib/carry-resync.ts
730
- import { join as join4 } from "path";
1105
+ import { join as join5 } from "path";
731
1106
 
732
1107
  // src/lib/carry-resolve.ts
733
- import { realpath, stat as stat2 } from "fs/promises";
1108
+ import { realpath, stat as stat3 } from "fs/promises";
734
1109
  import { homedir as homedir2 } from "os";
735
- import { isAbsolute, join as join3, normalize, resolve } from "path";
736
- var DEFAULT_MAX_BYTES = 50 * 1024 * 1024;
1110
+ import { isAbsolute, join as join4, normalize, relative, resolve } from "path";
1111
+
1112
+ // src/lib/dir-breakdown.ts
1113
+ import { readdir, stat as stat2 } from "fs/promises";
1114
+ import { join as join3 } from "path";
1115
+ var DEFAULT_CP_EXCLUDES = [
1116
+ ".git",
1117
+ "node_modules",
1118
+ "bin",
1119
+ "obj",
1120
+ "packages",
1121
+ "dist",
1122
+ ".next",
1123
+ "target"
1124
+ ];
1125
+ function isBareName(token) {
1126
+ return !token.includes("/") && !token.includes("*") && !token.includes("?");
1127
+ }
1128
+ function toTarExcludes(tokens) {
1129
+ const out = [];
1130
+ for (const t of tokens) {
1131
+ if (isBareName(t)) {
1132
+ out.push(`*/${t}`, t);
1133
+ } else {
1134
+ out.push(t);
1135
+ }
1136
+ }
1137
+ return out;
1138
+ }
1139
+ function effectiveExcludes(userTokens, useDefaults) {
1140
+ const seen = /* @__PURE__ */ new Set();
1141
+ const out = [];
1142
+ const add = (t) => {
1143
+ if (t && !seen.has(t)) {
1144
+ seen.add(t);
1145
+ out.push(t);
1146
+ }
1147
+ };
1148
+ if (useDefaults) DEFAULT_CP_EXCLUDES.forEach(add);
1149
+ userTokens.forEach(add);
1150
+ return out;
1151
+ }
1152
+ function globToRegExp(glob) {
1153
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1154
+ return new RegExp(`^${escaped}$`);
1155
+ }
1156
+ function isPathExcluded(relPath, tokens) {
1157
+ const segs = relPath.split("/");
1158
+ for (const t of tokens) {
1159
+ if (isBareName(t)) {
1160
+ if (segs.includes(t)) return true;
1161
+ } else if (globToRegExp(t).test(relPath)) {
1162
+ return true;
1163
+ }
1164
+ }
1165
+ return false;
1166
+ }
1167
+ async function buildNode(abs, rel, tokens, seen) {
1168
+ let bytes = 0;
1169
+ const children = [];
1170
+ let entries;
1171
+ try {
1172
+ entries = await readdir(abs);
1173
+ } catch {
1174
+ return { path: rel || ".", bytes: 0, children };
1175
+ }
1176
+ for (const name of entries) {
1177
+ const childRel = rel ? `${rel}/${name}` : name;
1178
+ if (isPathExcluded(childRel, tokens)) continue;
1179
+ const full = join3(abs, name);
1180
+ let st;
1181
+ try {
1182
+ st = await stat2(full);
1183
+ } catch {
1184
+ continue;
1185
+ }
1186
+ const key = `${String(st.dev)}:${String(st.ino)}`;
1187
+ if (seen.has(key)) continue;
1188
+ seen.add(key);
1189
+ if (st.isDirectory()) {
1190
+ const child = await buildNode(full, childRel, tokens, seen);
1191
+ bytes += child.bytes;
1192
+ children.push(child);
1193
+ } else if (st.isFile()) {
1194
+ bytes += st.size;
1195
+ }
1196
+ }
1197
+ return { path: rel || ".", bytes, children };
1198
+ }
1199
+ async function measureCopy(absSrc, tokens, opts = {}) {
1200
+ const st = await stat2(absSrc);
1201
+ if (!st.isDirectory()) {
1202
+ return { totalBytes: st.size, isDir: false, treeLines: [], topChildren: [] };
1203
+ }
1204
+ const root = await buildNode(absSrc, "", tokens, /* @__PURE__ */ new Set());
1205
+ const maxDepth = opts.maxDepth ?? 3;
1206
+ const floor = opts.floorBytes ?? 10 * 1024 * 1024;
1207
+ const perLevel = opts.perLevel ?? 8;
1208
+ const lines = [];
1209
+ const render = (node, depth, label) => {
1210
+ const pad4 = " ".repeat(depth);
1211
+ lines.push(` ${fmtBytes(node.bytes).padStart(8)} ${pad4}${label}`);
1212
+ if (depth >= maxDepth) return;
1213
+ const kids = [...node.children].sort((a, b) => b.bytes - a.bytes).filter((c) => c.bytes >= floor).slice(0, perLevel);
1214
+ for (const kid of kids) {
1215
+ render(kid, depth + 1, `./${kid.path}`);
1216
+ }
1217
+ };
1218
+ render(root, 0, "./");
1219
+ const topChildren = [...root.children].sort((a, b) => b.bytes - a.bytes).map((c) => ({ path: c.path, bytes: c.bytes }));
1220
+ return { totalBytes: root.bytes, isDir: true, treeLines: lines, topChildren };
1221
+ }
1222
+ function fmtBytes(n) {
1223
+ if (n < 1024) return `${String(n)} B`;
1224
+ const units = ["KB", "MB", "GB", "TB"];
1225
+ let v = n / 1024;
1226
+ let i = 0;
1227
+ while (v >= 1024 && i < units.length - 1) {
1228
+ v /= 1024;
1229
+ i += 1;
1230
+ }
1231
+ return `${v >= 10 || Number.isInteger(v) ? v.toFixed(0) : v.toFixed(1)} ${units[i]}`;
1232
+ }
1233
+
1234
+ // src/lib/carry-resolve.ts
737
1235
  var DENYLIST_DEST_PREFIXES = ["/proc", "/sys", "/dev"];
738
1236
  var DENYLIST_DEST_EXACT = /* @__PURE__ */ new Set(["/etc/passwd", "/etc/shadow"]);
739
1237
  async function resolveCarry(items, opts) {
740
1238
  const home = opts.homeDir ?? homedir2();
741
- const cap = opts.maxBytes ?? readMaxBytesFromEnv() ?? DEFAULT_MAX_BYTES;
1239
+ const cap = opts.maxBytes ?? BUILT_IN_DEFAULTS.box.cpMaxBytes;
742
1240
  const projectRoot = opts.projectRoot;
743
1241
  const entries = [];
744
1242
  const errors = [];
@@ -753,13 +1251,6 @@ async function resolveCarry(items, opts) {
753
1251
  }
754
1252
  return { entries, errors };
755
1253
  }
756
- function readMaxBytesFromEnv() {
757
- const raw = process.env.AGENTBOX_CARRY_MAX_BYTES;
758
- if (!raw) return void 0;
759
- const n = Number(raw);
760
- if (!Number.isFinite(n) || n <= 0) return void 0;
761
- return Math.floor(n);
762
- }
763
1254
  async function resolveOne(item, ctx) {
764
1255
  const absSrc = expandHostPath(item.src, ctx);
765
1256
  if (containsDotDot(absSrc)) {
@@ -772,7 +1263,7 @@ async function resolveOne(item, ctx) {
772
1263
  const absDest = item.dest;
773
1264
  let st;
774
1265
  try {
775
- st = await stat2(absSrc);
1266
+ st = await stat3(absSrc);
776
1267
  } catch (err) {
777
1268
  if (err.code === "ENOENT") {
778
1269
  if (optional) {
@@ -806,10 +1297,12 @@ async function resolveOne(item, ctx) {
806
1297
  } catch {
807
1298
  }
808
1299
  if (st.isDirectory()) {
809
- const bytes = await dirSizeCapped(absSrc, ctx.cap);
1300
+ const tokens = effectiveExcludes(item.exclude ?? [], true);
1301
+ const tarPatterns = toTarExcludes(tokens);
1302
+ const bytes = await dirSizeCapped(absSrc, ctx.cap, tokens);
810
1303
  if (bytes > ctx.cap) {
811
1304
  throw new Error(
812
- `${ctx.where}: dir "${absSrc}" exceeds ${String(ctx.cap)} bytes (set AGENTBOX_CARRY_MAX_BYTES to raise the cap or narrow the path)`
1305
+ `${ctx.where}: dir "${absSrc}" exceeds ${String(ctx.cap)} bytes after excludes (add carry exclude: patterns, raise box.cpMaxBytes, or narrow the path)`
813
1306
  );
814
1307
  }
815
1308
  return {
@@ -821,6 +1314,7 @@ async function resolveOne(item, ctx) {
821
1314
  bytes,
822
1315
  ...item.mode !== void 0 ? { mode: item.mode } : {},
823
1316
  ...item.user !== void 0 ? { user: item.user } : {},
1317
+ ...tarPatterns.length > 0 ? { exclude: tarPatterns } : {},
824
1318
  optional,
825
1319
  ...symlinkInfo ? { symlinkInfo } : {}
826
1320
  };
@@ -828,7 +1322,7 @@ async function resolveOne(item, ctx) {
828
1322
  if (st.isFile()) {
829
1323
  if (st.size > ctx.cap) {
830
1324
  throw new Error(
831
- `${ctx.where}: file "${absSrc}" is ${String(st.size)} bytes, exceeds cap ${String(ctx.cap)} (set AGENTBOX_CARRY_MAX_BYTES to raise)`
1325
+ `${ctx.where}: file "${absSrc}" is ${String(st.size)} bytes, exceeds cap ${String(ctx.cap)} (raise box.cpMaxBytes)`
832
1326
  );
833
1327
  }
834
1328
  return {
@@ -893,24 +1387,26 @@ async function realpathSafe(p) {
893
1387
  return resolve(p);
894
1388
  }
895
1389
  }
896
- async function dirSizeCapped(dir, cap) {
1390
+ async function dirSizeCapped(dir, cap, exclude = []) {
897
1391
  let total = 0;
898
1392
  const seen = /* @__PURE__ */ new Set();
899
1393
  async function walk(d) {
900
1394
  if (total > cap) return;
901
- const { readdir: readdir3 } = await import("fs/promises");
1395
+ const { readdir: readdir4 } = await import("fs/promises");
902
1396
  let names;
903
1397
  try {
904
- names = await readdir3(d);
1398
+ names = await readdir4(d);
905
1399
  } catch {
906
1400
  return;
907
1401
  }
908
1402
  for (const name of names) {
909
1403
  if (total > cap) return;
910
- const full = join3(d, name);
1404
+ const full = join4(d, name);
1405
+ const rel = relative(dir, full).split(/[\\/]/).join("/");
1406
+ if (exclude.length > 0 && isPathExcluded(rel, exclude)) continue;
911
1407
  let st;
912
1408
  try {
913
- st = await stat2(full);
1409
+ st = await stat3(full);
914
1410
  } catch {
915
1411
  continue;
916
1412
  }
@@ -935,9 +1431,13 @@ async function resyncCarryFiles(args) {
935
1431
  });
936
1432
  const prior = args.box.carry?.entries ?? [];
937
1433
  if (prior.length === 0) return { recopied: 0, skippedNew: 0 };
938
- const items = await loadCarrySection(join4(args.projectRoot, "agentbox.yaml"));
1434
+ const items = await loadCarrySection(join5(args.projectRoot, "agentbox.yaml"));
939
1435
  if (items.length === 0) return { recopied: 0, skippedNew: 0 };
940
- const resolved = await resolveCarry(items, { projectRoot: args.projectRoot });
1436
+ const cfg = await loadEffectiveConfig(args.projectRoot);
1437
+ const resolved = await resolveCarry(items, {
1438
+ projectRoot: args.projectRoot,
1439
+ maxBytes: cfg.effective.box.cpMaxBytes
1440
+ });
941
1441
  if (resolved.errors.length > 0) {
942
1442
  log47(`carry: resync skipped (resolve errors: ${resolved.errors.length})`);
943
1443
  return { recopied: 0, skippedNew: 0 };
@@ -1072,6 +1572,7 @@ async function submitQueueJob(input) {
1072
1572
  createOpts: input.createOpts,
1073
1573
  maxConcurrent: ceiling,
1074
1574
  ...maxWorking !== void 0 ? { maxWorking } : {},
1575
+ ...input.openTerminal !== void 0 ? { openTerminal: input.openTerminal } : {},
1075
1576
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1076
1577
  logPath: queueLogPath(id)
1077
1578
  };
@@ -1126,6 +1627,58 @@ function postEnqueue(id) {
1126
1627
  });
1127
1628
  }
1128
1629
 
1630
+ // src/terminal/queue-open.ts
1631
+ function captureOpenTerminalContext(mode, env = process.env, cwd = process.cwd()) {
1632
+ if (mode === "none") return void 0;
1633
+ const host = detectHostTerminal(env);
1634
+ if (host === "unknown") return void 0;
1635
+ const base = { host, mode, cwd };
1636
+ if (host === "tmux") {
1637
+ return { ...base, host, tmuxSocket: env["TMUX"], tmuxPane: env["TMUX_PANE"] };
1638
+ }
1639
+ if (host === "cmux") {
1640
+ return {
1641
+ ...base,
1642
+ host,
1643
+ cmuxSocket: env["CMUX_SOCKET_PATH"],
1644
+ cmuxBundledCli: cmuxBinary(env),
1645
+ cmuxSurfaceId: env["CMUX_SURFACE_ID"],
1646
+ cmuxWorkspaceId: env["CMUX_WORKSPACE_ID"]
1647
+ };
1648
+ }
1649
+ return { ...base, host };
1650
+ }
1651
+ function spawnQueuedOpenTerminal(ctx, argv2, title) {
1652
+ if (ctx.host === "tmux") {
1653
+ return spawnInNewTerminal({
1654
+ host: "tmux",
1655
+ mode: ctx.mode,
1656
+ argv: argv2,
1657
+ cwd: ctx.cwd,
1658
+ title,
1659
+ env: ctx.tmuxSocket ? { ...process.env, TMUX: ctx.tmuxSocket } : process.env,
1660
+ tmuxTarget: ctx.tmuxPane
1661
+ });
1662
+ }
1663
+ if (ctx.host === "cmux") {
1664
+ const env = { ...process.env };
1665
+ if (ctx.cmuxSocket) env["CMUX_SOCKET_PATH"] = ctx.cmuxSocket;
1666
+ if (ctx.cmuxBundledCli) env["CMUX_BUNDLED_CLI_PATH"] = ctx.cmuxBundledCli;
1667
+ return spawnInNewTerminal({
1668
+ host: "cmux",
1669
+ mode: ctx.mode,
1670
+ argv: argv2,
1671
+ cwd: ctx.cwd,
1672
+ title,
1673
+ env,
1674
+ cmuxTargetSurface: ctx.cmuxSurfaceId,
1675
+ cmuxTargetWorkspace: ctx.cmuxWorkspaceId,
1676
+ cmuxWorkspaceFallback: true
1677
+ });
1678
+ }
1679
+ return spawnInNewTerminal({ host: "iterm2", mode: ctx.mode, argv: argv2, cwd: ctx.cwd, title });
1680
+ }
1681
+
1129
1682
  // src/commands/_attach-in.ts
1130
1683
  var VALUES = ["split", "window", "tab", "same"];
1131
1684
  var ATTACH_IN_HELP = "where to open the attached session: split | window | tab | same (default from attach.openIn, built-in: split). Only effective when running inside tmux or iTerm2; falls back to inline attach otherwise.";
@@ -1366,14 +1919,14 @@ async function cloudAgentCreate(args) {
1366
1919
  }
1367
1920
 
1368
1921
  // src/lib/carry-gate.ts
1369
- import { join as join5 } from "path";
1922
+ import { join as join6 } from "path";
1370
1923
  import { log as log5 } from "@clack/prompts";
1371
1924
 
1372
1925
  // src/carry-prompt.ts
1373
1926
  import { isCancel, log as log4, select } from "@clack/prompts";
1374
1927
 
1375
1928
  // src/fmt.ts
1376
- function fmtBytes(n) {
1929
+ function fmtBytes2(n) {
1377
1930
  if (n === null || n === void 0) return "n/a";
1378
1931
  if (n < 1024) return `${String(n)} B`;
1379
1932
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KiB`;
@@ -1449,7 +2002,7 @@ function printSummary(entries) {
1449
2002
  if (e.mode !== void 0) flags.push(`mode ${e.mode.toString(8).padStart(4, "0")}`);
1450
2003
  if (e.user !== void 0 && e.user !== 1e3) flags.push(`user ${String(e.user)}`);
1451
2004
  if (e.symlinkInfo === "outside-home") flags.push("symlink \u2192 outside $HOME!");
1452
- const size = e.kind === "missing" ? "\u2014" : fmtBytes(e.bytes ?? 0);
2005
+ const size = e.kind === "missing" ? "\u2014" : fmtBytes2(e.bytes ?? 0);
1453
2006
  rows.push(
1454
2007
  ` ${pad(e.rawSrc, srcW)} \u2192 ${pad(e.rawDest, destW)} ${pad(size, 9)} ${flags.join(", ")}`
1455
2008
  );
@@ -1465,10 +2018,14 @@ function pad(s, w) {
1465
2018
  async function runCarryGate(args) {
1466
2019
  const emit = args.onLog ?? (() => {
1467
2020
  });
1468
- const yamlPath = join5(args.projectRoot, "agentbox.yaml");
2021
+ const yamlPath = join6(args.projectRoot, "agentbox.yaml");
1469
2022
  const items = await loadCarrySection(yamlPath);
1470
2023
  if (items.length === 0) return { decision: "approve", entries: [] };
1471
- const resolved = await resolveCarry(items, { projectRoot: args.projectRoot });
2024
+ const cfg = await loadEffectiveConfig(args.projectRoot);
2025
+ const resolved = await resolveCarry(items, {
2026
+ projectRoot: args.projectRoot,
2027
+ maxBytes: cfg.effective.box.cpMaxBytes
2028
+ });
1472
2029
  if (resolved.errors.length > 0) {
1473
2030
  const msg = ["carry: refused to proceed:", ...resolved.errors.map((e) => ` - ${e}`)].join("\n");
1474
2031
  throw new Error(msg);
@@ -1513,10 +2070,10 @@ async function runQueuedCarryGate(args) {
1513
2070
  }
1514
2071
 
1515
2072
  // src/session-teleport/claude.ts
1516
- import { mkdir, mkdtemp, readdir, readFile as readFile2, stat as stat3, writeFile } from "fs/promises";
2073
+ import { mkdir, mkdtemp, readdir as readdir2, readFile as readFile2, stat as stat4, writeFile } from "fs/promises";
1517
2074
  import { existsSync } from "fs";
1518
2075
  import { homedir as homedir4, tmpdir } from "os";
1519
- import { join as join6 } from "path";
2076
+ import { join as join7 } from "path";
1520
2077
 
1521
2078
  // src/session-teleport/cwd-encoding.ts
1522
2079
  function encodeClaudeProjectsDir(absPath) {
@@ -1537,7 +2094,7 @@ var TeleportError = class extends Error {
1537
2094
  var BOX_CLAUDE_PROJECTS_DIR = `/home/vscode/.claude/projects/${BOX_WORKSPACE_ENCODED}`;
1538
2095
  async function resolveClaudeTeleport(opts) {
1539
2096
  const hostHome = opts.hostHome ?? homedir4();
1540
- const projectDir = join6(
2097
+ const projectDir = join7(
1541
2098
  hostHome,
1542
2099
  ".claude",
1543
2100
  "projects",
@@ -1550,8 +2107,8 @@ async function resolveClaudeTeleport(opts) {
1550
2107
  }
1551
2108
  const sessionPath = await pickSessionFile(projectDir, opts.mode);
1552
2109
  const sessionId = sessionPath.replace(/\.jsonl$/u, "").split("/").pop() ?? "";
1553
- const stage = await mkdtemp(join6(tmpdir(), "agentbox-teleport-claude-"));
1554
- const stagedFile = join6(stage, `${sessionId}.jsonl`);
2110
+ const stage = await mkdtemp(join7(tmpdir(), "agentbox-teleport-claude-"));
2111
+ const stagedFile = join7(stage, `${sessionId}.jsonl`);
1555
2112
  await rewriteSessionFile(sessionPath, stagedFile, opts.hostCwd);
1556
2113
  opts.log?.(`teleport: claude session ${sessionId} staged for upload`);
1557
2114
  return {
@@ -1565,7 +2122,7 @@ async function resolveClaudeTeleport(opts) {
1565
2122
  }
1566
2123
  async function pickSessionFile(projectDir, mode) {
1567
2124
  if (mode.kind === "resume") {
1568
- const target = join6(projectDir, `${mode.id}.jsonl`);
2125
+ const target = join7(projectDir, `${mode.id}.jsonl`);
1569
2126
  if (!existsSync(target)) {
1570
2127
  throw new TeleportError(
1571
2128
  `Claude session "${mode.id}" not found in ${projectDir}. List available sessions with: ls "${projectDir}"`
@@ -1573,7 +2130,7 @@ async function pickSessionFile(projectDir, mode) {
1573
2130
  }
1574
2131
  return target;
1575
2132
  }
1576
- const entries = await readdir(projectDir);
2133
+ const entries = await readdir2(projectDir);
1577
2134
  const jsonl = entries.filter((e) => e.endsWith(".jsonl"));
1578
2135
  if (jsonl.length === 0) {
1579
2136
  throw new TeleportError(
@@ -1582,8 +2139,8 @@ async function pickSessionFile(projectDir, mode) {
1582
2139
  }
1583
2140
  const stats = await Promise.all(
1584
2141
  jsonl.map(async (name) => {
1585
- const full = join6(projectDir, name);
1586
- const s = await stat3(full);
2142
+ const full = join7(projectDir, name);
2143
+ const s = await stat4(full);
1587
2144
  return { full, mtimeMs: s.mtimeMs };
1588
2145
  })
1589
2146
  );
@@ -1616,18 +2173,18 @@ async function rewriteSessionFile(src, dst, hostCwd) {
1616
2173
  }
1617
2174
  out.push(line);
1618
2175
  }
1619
- await mkdir(join6(dst, ".."), { recursive: true });
2176
+ await mkdir(join7(dst, ".."), { recursive: true });
1620
2177
  await writeFile(dst, out.join("\n"), "utf8");
1621
2178
  }
1622
2179
 
1623
2180
  // src/session-teleport/codex.ts
1624
- import { mkdtemp as mkdtemp2, readdir as readdir2, readFile as readFile3, stat as stat4, writeFile as writeFile2 } from "fs/promises";
2181
+ import { mkdtemp as mkdtemp2, readdir as readdir3, readFile as readFile3, stat as stat5, writeFile as writeFile2 } from "fs/promises";
1625
2182
  import { existsSync as existsSync2 } from "fs";
1626
2183
  import { homedir as homedir5, tmpdir as tmpdir2 } from "os";
1627
- import { join as join7 } from "path";
2184
+ import { join as join8 } from "path";
1628
2185
  async function resolveCodexTeleport(opts) {
1629
2186
  const hostHome = opts.hostHome ?? homedir5();
1630
- const sessionsRoot = join7(hostHome, ".codex", "sessions");
2187
+ const sessionsRoot = join8(hostHome, ".codex", "sessions");
1631
2188
  if (!existsSync2(sessionsRoot)) {
1632
2189
  throw new TeleportError(
1633
2190
  `no Codex session history found on the host (expected at ${sessionsRoot}). Run \`codex\` at least once before using -c / --resume.`
@@ -1669,8 +2226,8 @@ async function resolveCodexTeleport(opts) {
1669
2226
  matching.sort((a, b) => b.mtimeMs - a.mtimeMs);
1670
2227
  picked = matching[0];
1671
2228
  }
1672
- const stage = await mkdtemp2(join7(tmpdir2(), "agentbox-teleport-codex-"));
1673
- const stagedFile = join7(stage, posixBasename(picked.relPath));
2229
+ const stage = await mkdtemp2(join8(tmpdir2(), "agentbox-teleport-codex-"));
2230
+ const stagedFile = join8(stage, posixBasename(picked.relPath));
1674
2231
  await rewriteCodexSession(picked.hostPath, stagedFile, opts.hostCwd);
1675
2232
  opts.log?.(`teleport: codex session ${picked.uuid} staged for upload`);
1676
2233
  const boxParentDir = `/home/vscode/.codex/sessions/${posixDirname(picked.relPath)}`;
@@ -1696,24 +2253,24 @@ async function listCodexSessions(sessionsRoot) {
1696
2253
  const years = await safeReaddir(sessionsRoot);
1697
2254
  for (const y of years) {
1698
2255
  if (!/^\d{4}$/u.test(y)) continue;
1699
- const yDir = join7(sessionsRoot, y);
2256
+ const yDir = join8(sessionsRoot, y);
1700
2257
  const months = await safeReaddir(yDir);
1701
2258
  for (const m of months) {
1702
2259
  if (!/^\d{2}$/u.test(m)) continue;
1703
- const mDir = join7(yDir, m);
2260
+ const mDir = join8(yDir, m);
1704
2261
  const days = await safeReaddir(mDir);
1705
2262
  for (const d of days) {
1706
2263
  if (!/^\d{2}$/u.test(d)) continue;
1707
- const dDir = join7(mDir, d);
2264
+ const dDir = join8(mDir, d);
1708
2265
  const files = await safeReaddir(dDir);
1709
2266
  for (const name of files) {
1710
2267
  if (!name.startsWith("rollout-") || !name.endsWith(".jsonl")) continue;
1711
2268
  const uuid = extractCodexUuid(name);
1712
2269
  if (uuid === null) continue;
1713
- const hostPath = join7(dDir, name);
2270
+ const hostPath = join8(dDir, name);
1714
2271
  let mtimeMs = 0;
1715
2272
  try {
1716
- mtimeMs = (await stat4(hostPath)).mtimeMs;
2273
+ mtimeMs = (await stat5(hostPath)).mtimeMs;
1717
2274
  } catch {
1718
2275
  continue;
1719
2276
  }
@@ -1733,7 +2290,7 @@ async function listCodexSessions(sessionsRoot) {
1733
2290
  }
1734
2291
  async function safeReaddir(dir) {
1735
2292
  try {
1736
- return await readdir2(dir);
2293
+ return await readdir3(dir);
1737
2294
  } catch {
1738
2295
  return [];
1739
2296
  }
@@ -1745,16 +2302,16 @@ function extractCodexUuid(filename) {
1745
2302
  return m ? m[1] : null;
1746
2303
  }
1747
2304
  async function peekCodexCwd(file) {
1748
- let firstLine2;
2305
+ let firstLine3;
1749
2306
  try {
1750
2307
  const buf = await readFile3(file, "utf8");
1751
2308
  const nl = buf.indexOf("\n");
1752
- firstLine2 = nl === -1 ? buf : buf.slice(0, nl);
2309
+ firstLine3 = nl === -1 ? buf : buf.slice(0, nl);
1753
2310
  } catch {
1754
2311
  return null;
1755
2312
  }
1756
2313
  try {
1757
- const parsed = JSON.parse(firstLine2);
2314
+ const parsed = JSON.parse(firstLine3);
1758
2315
  if (parsed.type === "session_meta" && typeof parsed.payload?.cwd === "string") {
1759
2316
  return parsed.payload.cwd;
1760
2317
  }
@@ -1836,11 +2393,11 @@ async function uploadTeleport(input) {
1836
2393
  import { mkdtemp as mkdtemp3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
1837
2394
  import { existsSync as existsSync3 } from "fs";
1838
2395
  import { homedir as homedir6, tmpdir as tmpdir3 } from "os";
1839
- import { basename, isAbsolute as isAbsolute2, join as join8, resolve as resolve2 } from "path";
2396
+ import { basename, isAbsolute as isAbsolute2, join as join9, resolve as resolve2 } from "path";
1840
2397
  var BOX_CLAUDE_PLANS_DIR = "/home/vscode/.claude/plans";
1841
2398
  function expandHome(p, hostHome) {
1842
2399
  if (p === "~") return hostHome;
1843
- if (p.startsWith("~/")) return join8(hostHome, p.slice(2));
2400
+ if (p.startsWith("~/")) return join9(hostHome, p.slice(2));
1844
2401
  return isAbsolute2(p) ? p : resolve2(p);
1845
2402
  }
1846
2403
  async function resolvePlanTeleport(opts) {
@@ -1852,8 +2409,8 @@ async function resolvePlanTeleport(opts) {
1852
2409
  );
1853
2410
  }
1854
2411
  const name = basename(planFile);
1855
- const stage = await mkdtemp3(join8(tmpdir3(), "agentbox-teleport-plan-"));
1856
- const stagedFile = join8(stage, name);
2412
+ const stage = await mkdtemp3(join9(tmpdir3(), "agentbox-teleport-plan-"));
2413
+ const stagedFile = join9(stage, name);
1857
2414
  const raw = await readFile4(planFile, "utf8");
1858
2415
  const absCwd = opts.hostCwd ? resolve2(expandHome(opts.hostCwd, hostHome)) : "";
1859
2416
  const rewritten = absCwd ? raw.split(absCwd).join(BOX_WORKSPACE) : raw;
@@ -1873,14 +2430,14 @@ async function resolvePlanTeleport(opts) {
1873
2430
  import { log as log6 } from "@clack/prompts";
1874
2431
  import { existsSync as existsSync4, mkdirSync, writeFileSync } from "fs";
1875
2432
  import { homedir as homedir7 } from "os";
1876
- import { join as join9 } from "path";
2433
+ import { join as join10 } from "path";
1877
2434
  function maybeShowInstallHint() {
1878
2435
  try {
1879
- const skill = join9(homedir7(), ".claude", "skills", "agentbox", "SKILL.md");
2436
+ const skill = join10(homedir7(), ".claude", "skills", "agentbox", "SKILL.md");
1880
2437
  if (existsSync4(skill)) return;
1881
- const marker = join9(homedir7(), ".agentbox", "install-hint-shown");
2438
+ const marker = join10(homedir7(), ".agentbox", "install-hint-shown");
1882
2439
  if (existsSync4(marker)) return;
1883
- mkdirSync(join9(homedir7(), ".agentbox"), { recursive: true });
2440
+ mkdirSync(join10(homedir7(), ".agentbox"), { recursive: true });
1884
2441
  writeFileSync(marker, "");
1885
2442
  log6.info("tip: run 'agentbox install' to enable the /agentbox fork command in host Claude");
1886
2443
  } catch {
@@ -1890,18 +2447,18 @@ function maybeShowInstallHint() {
1890
2447
  // src/lib/log-file.ts
1891
2448
  import { closeSync, mkdirSync as mkdirSync2, openSync, renameSync, symlinkSync, unlinkSync, writeFileSync as writeFileSync2, writeSync } from "fs";
1892
2449
  import { homedir as homedir8 } from "os";
1893
- import { join as join10 } from "path";
2450
+ import { join as join11 } from "path";
1894
2451
  function stateDir() {
1895
- return process.env.AGENTBOX_HOME ?? join10(homedir8(), ".agentbox");
2452
+ return process.env.AGENTBOX_HOME ?? join11(homedir8(), ".agentbox");
1896
2453
  }
1897
2454
  function logsDir() {
1898
- return join10(stateDir(), "logs");
2455
+ return join11(stateDir(), "logs");
1899
2456
  }
1900
2457
  function openCommandLog(command) {
1901
2458
  const dir = logsDir();
1902
2459
  mkdirSync2(dir, { recursive: true });
1903
- const path = join10(dir, `${command}.log`);
1904
- const prev = join10(dir, `${command}.log.prev`);
2460
+ const path = join11(dir, `${command}.log`);
2461
+ const prev = join11(dir, `${command}.log.prev`);
1905
2462
  try {
1906
2463
  renameSync(path, prev);
1907
2464
  } catch {
@@ -1961,7 +2518,7 @@ function openCommandLog(command) {
1961
2518
  };
1962
2519
  }
1963
2520
  function updateLatestSymlink(dir, target) {
1964
- const link = join10(dir, "latest.log");
2521
+ const link = join11(dir, "latest.log");
1965
2522
  try {
1966
2523
  unlinkSync(link);
1967
2524
  } catch {
@@ -2075,13 +2632,13 @@ import { basename as basename2 } from "path";
2075
2632
  async function cloudBackendForProvider(provider) {
2076
2633
  switch (provider) {
2077
2634
  case "daytona":
2078
- return (await import("./dist-3IMQNTTV.js")).daytonaBackend;
2635
+ return (await import("./dist-FIFEFKJ7.js")).daytonaBackend;
2079
2636
  case "hetzner":
2080
- return (await import("./dist-J2IHD5T7.js")).hetznerBackend;
2637
+ return (await import("./dist-AGTIA7AD.js")).hetznerBackend;
2081
2638
  case "vercel":
2082
- return (await import("./dist-57M6ZA7H.js")).vercelBackend;
2639
+ return (await import("./dist-S4XR4ACV.js")).vercelBackend;
2083
2640
  case "e2b":
2084
- return (await import("./dist-34RKQ74M.js")).e2bBackend;
2641
+ return (await import("./dist-JZ3XO6EB.js")).e2bBackend;
2085
2642
  default:
2086
2643
  return null;
2087
2644
  }
@@ -2089,13 +2646,13 @@ async function cloudBackendForProvider(provider) {
2089
2646
  async function currentCloudBaseFingerprintLive(provider) {
2090
2647
  switch (provider) {
2091
2648
  case "daytona":
2092
- return (await import("./dist-3IMQNTTV.js")).currentDaytonaBaseFingerprintLive();
2649
+ return (await import("./dist-FIFEFKJ7.js")).currentDaytonaBaseFingerprintLive();
2093
2650
  case "hetzner":
2094
- return (await import("./dist-J2IHD5T7.js")).currentHetznerBaseFingerprintLive();
2651
+ return (await import("./dist-AGTIA7AD.js")).currentHetznerBaseFingerprintLive();
2095
2652
  case "vercel":
2096
- return (await import("./dist-57M6ZA7H.js")).currentVercelBaseFingerprintLive();
2653
+ return (await import("./dist-S4XR4ACV.js")).currentVercelBaseFingerprintLive();
2097
2654
  case "e2b":
2098
- return (await import("./dist-34RKQ74M.js")).currentE2bBaseFingerprintLive();
2655
+ return (await import("./dist-JZ3XO6EB.js")).currentE2bBaseFingerprintLive();
2099
2656
  default:
2100
2657
  return void 0;
2101
2658
  }
@@ -2173,18 +2730,30 @@ async function evaluateBaseFreshness(provider) {
2173
2730
 
2174
2731
  // src/wizard.ts
2175
2732
  var IN_BOX_SETUP_GUIDE_PATH = "/usr/local/share/agentbox/setup-guide.md";
2176
- function buildSetupInitialPrompt(workspace) {
2733
+ function buildSetupInitialPrompt(workspace, hasAgentboxYaml = false) {
2177
2734
  const name = basename2(workspace);
2735
+ if (hasAgentboxYaml) {
2736
+ return `The user reopened the agentbox sandbox for "${name}". The workspace already has a /workspace/agentbox.yaml, but the previous snapshot was stale so the box was rebuilt from a fresh base image \u2014 its dependencies and system setup are NOT installed yet. Please run the /agentbox-setup skill (or read ${IN_BOX_SETUP_GUIDE_PATH} if the skill is not loaded), then reuse the existing /workspace/agentbox.yaml: verify the declared tasks and services still fit the project, (re)install dependencies and any system packages the setup needs on this fresh base, and update agentbox.yaml only if something is missing or wrong \u2014 do not regenerate it from scratch. Then run \`agentbox-ctl reload\` from inside the box so the already-running supervisor re-applies the config and immediately runs the declared tasks and autostarts the services (no box restart needed). When the box is warm, capture it with \`agentbox checkpoint --set-default\` so future boxes skip this rebuild. Finally, summarise what you verified and changed, and remind the user how to land any agentbox.yaml change on the host (commit through the bind-mounted .git, or "agentbox download env" on the host).`;
2737
+ }
2178
2738
  return `The user just opened a new agentbox sandbox for "${name}" but the workspace has no agentbox.yaml yet. Please run the /agentbox-setup skill (or read ${IN_BOX_SETUP_GUIDE_PATH} if the skill is not loaded), then explore /workspace and propose an agentbox.yaml. Save the file to /workspace/agentbox.yaml. Then run \`agentbox-ctl reload\` from inside the box so the already-running supervisor applies the new config and immediately runs the declared tasks and autostarts the services (no box restart needed). When done, summarise what services and tasks you declared, and remind the user how to land the file on the host (commit through the bind-mounted .git, or "agentbox download env" on the host).`;
2179
2739
  }
2180
2740
  var WIZARD_AUTOLAUNCH_ENV = "AGENTBOX_WIZARD_AUTOLAUNCH";
2181
2741
  var WIZARD_ENV_FILES_ENV = "AGENTBOX_WIZARD_ENV_FILES";
2742
+ var WIZARD_RECREATE_ENV = "AGENTBOX_WIZARD_RECREATE";
2182
2743
  var WIZARD_ENV_SCAN_PATTERNS = DEFAULT_ENV_PATTERNS.filter((p) => p !== "agentbox.yaml");
2183
2744
  async function maybeRunSetupWizard(args) {
2184
2745
  if (process.env[WIZARD_AUTOLAUNCH_ENV] === "1") {
2185
2746
  if (args.command !== "claude") return { action: "proceed" };
2186
2747
  const envFiles = parseEnvFilesFromEnv(process.env[WIZARD_ENV_FILES_ENV]);
2187
2748
  const proj2 = await findProjectRoot(args.workspace);
2749
+ if (process.env[WIZARD_RECREATE_ENV] === "1") {
2750
+ return {
2751
+ action: "launch-with-prompt",
2752
+ initialPrompt: buildSetupInitialPrompt(proj2.root, proj2.hasAgentboxYaml),
2753
+ envFilesToImport: envFiles,
2754
+ discardCheckpoint: true
2755
+ };
2756
+ }
2188
2757
  return nonInteractiveOutcome(args, proj2, await checkpointStatus(args, proj2.root), envFiles);
2189
2758
  }
2190
2759
  const proj = await findProjectRoot(args.workspace);
@@ -2218,13 +2787,6 @@ async function maybeRunSetupWizard(args) {
2218
2787
  };
2219
2788
  }
2220
2789
  }
2221
- if (proj.hasAgentboxYaml) {
2222
- return { action: "proceed", discardCheckpoint: discardOnMissing(status, fromDefault) };
2223
- }
2224
- if (!rebuildBase && (status.state === "fresh" || status.state === "stale" && !fromDefault)) {
2225
- log8.info(`starting from checkpoint "${args.checkpointRef}"; skipping agentbox.yaml setup`);
2226
- return { action: "proceed" };
2227
- }
2228
2790
  let discardCheckpoint = rebuildBase;
2229
2791
  let recreateChosen = rebuildBase;
2230
2792
  if (!rebuildBase && status.state === "stale" && fromDefault) {
@@ -2237,7 +2799,15 @@ async function maybeRunSetupWizard(args) {
2237
2799
  }
2238
2800
  discardCheckpoint = true;
2239
2801
  recreateChosen = true;
2240
- } else if (!rebuildBase && status.state === "missing") {
2802
+ }
2803
+ if (proj.hasAgentboxYaml && !recreateChosen) {
2804
+ return { action: "proceed", discardCheckpoint: discardOnMissing(status, fromDefault) };
2805
+ }
2806
+ if (!rebuildBase && !recreateChosen && (status.state === "fresh" || status.state === "stale" && !fromDefault)) {
2807
+ log8.info(`starting from checkpoint "${args.checkpointRef}"; skipping agentbox.yaml setup`);
2808
+ return { action: "proceed" };
2809
+ }
2810
+ if (!rebuildBase && !recreateChosen && status.state === "missing") {
2241
2811
  discardCheckpoint = discardOnMissing(status, fromDefault) ?? false;
2242
2812
  }
2243
2813
  let envFilesToImport;
@@ -2274,15 +2844,17 @@ async function maybeRunSetupWizard(args) {
2274
2844
  action: "switch-to-claude",
2275
2845
  envFilesToImport,
2276
2846
  discardCheckpoint: discardCheckpoint || void 0,
2277
- rebuildBase: rebuildBase || void 0
2847
+ rebuildBase: rebuildBase || void 0,
2848
+ recreate: recreateChosen || void 0
2278
2849
  };
2279
2850
  }
2280
2851
  return {
2281
2852
  action: "launch-with-prompt",
2282
- initialPrompt: buildSetupInitialPrompt(proj.root),
2853
+ initialPrompt: buildSetupInitialPrompt(proj.root, proj.hasAgentboxYaml),
2283
2854
  envFilesToImport,
2284
2855
  discardCheckpoint: discardCheckpoint || void 0,
2285
- rebuildBase: rebuildBase || void 0
2856
+ rebuildBase: rebuildBase || void 0,
2857
+ recreate: recreateChosen || void 0
2286
2858
  };
2287
2859
  }
2288
2860
  function rebuildMinutesFor(provider) {
@@ -2323,7 +2895,7 @@ function nonInteractiveOutcome(args, proj, status, envFilesToImport) {
2323
2895
  if (args.command === "claude") {
2324
2896
  return {
2325
2897
  action: "launch-with-prompt",
2326
- initialPrompt: buildSetupInitialPrompt(proj.root),
2898
+ initialPrompt: buildSetupInitialPrompt(proj.root, proj.hasAgentboxYaml),
2327
2899
  envFilesToImport,
2328
2900
  discardCheckpoint
2329
2901
  };
@@ -2419,7 +2991,7 @@ async function renderDocker(status) {
2419
2991
  }
2420
2992
  async function daytonaStatus() {
2421
2993
  try {
2422
- const mod = await import("./dist-3IMQNTTV.js");
2994
+ const mod = await import("./dist-FIFEFKJ7.js");
2423
2995
  return await mod.getDaytonaStatus();
2424
2996
  } catch (err) {
2425
2997
  return {
@@ -2430,7 +3002,7 @@ async function daytonaStatus() {
2430
3002
  }
2431
3003
  async function e2bStatus() {
2432
3004
  try {
2433
- const mod = await import("./dist-34RKQ74M.js");
3005
+ const mod = await import("./dist-JZ3XO6EB.js");
2434
3006
  const cred = mod.readE2bCredStatus();
2435
3007
  if (cred.auth === "none") {
2436
3008
  return { configured: false, reason: "not configured \u2014 run `agentbox e2b login`" };
@@ -2944,7 +3516,8 @@ var claudeCommand = new Command3("claude").description("Create a sandboxed box a
2944
3516
  agentArgs: claudeArgs,
2945
3517
  createOpts: { ...pickCreateOpts(opts), carry: carryForQueue },
2946
3518
  maxRunningOverride,
2947
- maxWorkingOverride
3519
+ maxWorkingOverride,
3520
+ openTerminal: captureOpenTerminalContext(cfg.effective.queue.openIn)
2948
3521
  });
2949
3522
  outro(
2950
3523
  `job ${result.job.id} queued (${String(result.runningCount)}/${String(result.maxConcurrent)} running); log: ${result.job.logPath}`
@@ -3572,13 +4145,13 @@ var CLOUD_BACKENDS = ["daytona", "hetzner", "vercel", "e2b"];
3572
4145
  async function cloudProviderFor(backend) {
3573
4146
  switch (backend) {
3574
4147
  case "daytona":
3575
- return (await import("./dist-3IMQNTTV.js")).daytonaProvider;
4148
+ return (await import("./dist-FIFEFKJ7.js")).daytonaProvider;
3576
4149
  case "hetzner":
3577
- return (await import("./dist-J2IHD5T7.js")).hetznerProvider;
4150
+ return (await import("./dist-AGTIA7AD.js")).hetznerProvider;
3578
4151
  case "vercel":
3579
- return (await import("./dist-57M6ZA7H.js")).vercelProvider;
4152
+ return (await import("./dist-S4XR4ACV.js")).vercelProvider;
3580
4153
  case "e2b":
3581
- return (await import("./dist-34RKQ74M.js")).e2bProvider;
4154
+ return (await import("./dist-JZ3XO6EB.js")).e2bProvider;
3582
4155
  }
3583
4156
  }
3584
4157
  var CHECKPOINT_NOTICE = "Checkpoint in progress \u2014 the box will be unresponsive for a moment";
@@ -3948,9 +4521,9 @@ import { Command as Command5, InvalidArgumentError } from "commander";
3948
4521
  // src/ssh-config.ts
3949
4522
  import { promises as fs } from "fs";
3950
4523
  import { homedir as homedir9 } from "os";
3951
- import { join as join11 } from "path";
4524
+ import { join as join12 } from "path";
3952
4525
  function sshConfigPath() {
3953
- return join11(homedir9(), ".ssh", "config");
4526
+ return join12(homedir9(), ".ssh", "config");
3954
4527
  }
3955
4528
  function beginMarker(alias) {
3956
4529
  return `# BEGIN agentbox cloud box ${alias}`;
@@ -4001,7 +4574,7 @@ function buildBlock(opts) {
4001
4574
  }
4002
4575
  async function writeAgentboxSshAlias(opts) {
4003
4576
  const path = sshConfigPath();
4004
- await fs.mkdir(join11(homedir9(), ".ssh"), { recursive: true, mode: 448 });
4577
+ await fs.mkdir(join12(homedir9(), ".ssh"), { recursive: true, mode: 448 });
4005
4578
  const existing = await readConfig();
4006
4579
  const stripped = stripBlock(existing, opts.alias);
4007
4580
  const separator = stripped.length === 0 || stripped.endsWith("\n") ? "" : "\n";
@@ -4231,10 +4804,10 @@ async function launchOne(flavor, folderUri) {
4231
4804
  return { code: fallback, flavor, via: "open" };
4232
4805
  }
4233
4806
  function spawnCommand(cmd, args) {
4234
- return new Promise((resolve4) => {
4807
+ return new Promise((resolve5) => {
4235
4808
  const child = spawn(cmd, args, { stdio: "ignore" });
4236
- child.once("error", () => resolve4(127));
4237
- child.once("exit", (code) => resolve4(code ?? -1));
4809
+ child.once("error", () => resolve5(127));
4810
+ child.once("exit", (code) => resolve5(code ?? -1));
4238
4811
  });
4239
4812
  }
4240
4813
  async function fetchServiceNamesDocker(container) {
@@ -4253,7 +4826,7 @@ async function fetchServiceNamesDocker(container) {
4253
4826
  // src/commands/codex.ts
4254
4827
  import { access } from "fs/promises";
4255
4828
  import { homedir as homedir10 } from "os";
4256
- import { join as join12 } from "path";
4829
+ import { join as join13 } from "path";
4257
4830
  import { confirm as confirm5, intro as intro3, isCancel as isCancel6, log as log13, outro as outro2, spinner as spinner5 } from "@clack/prompts";
4258
4831
  import { Command as Command6 } from "commander";
4259
4832
  function reattachRef2(r) {
@@ -4350,7 +4923,7 @@ async function maybeRunCodexLogin(args) {
4350
4923
  }
4351
4924
  async function cloudCodexCredAvailable(env = process.env) {
4352
4925
  if ((env["OPENAI_API_KEY"] ?? "").length > 0) return true;
4353
- for (const p of [CODEX_CREDENTIALS_BACKUP_FILE, join12(homedir10(), ".codex", "auth.json")]) {
4926
+ for (const p of [CODEX_CREDENTIALS_BACKUP_FILE, join13(homedir10(), ".codex", "auth.json")]) {
4354
4927
  try {
4355
4928
  await access(p);
4356
4929
  return true;
@@ -4518,7 +5091,8 @@ var codexCommand = new Command6("codex").description("Create a sandboxed box and
4518
5091
  agentArgs: codexArgs,
4519
5092
  createOpts: { ...pickCodexCreateOpts(opts), carry: carryForQueue },
4520
5093
  maxRunningOverride,
4521
- maxWorkingOverride
5094
+ maxWorkingOverride,
5095
+ openTerminal: captureOpenTerminalContext(cfg.effective.queue.openIn)
4522
5096
  });
4523
5097
  outro2(
4524
5098
  `job ${result.job.id} queued (${String(result.runningCount)}/${String(result.maxConcurrent)} running); log: ${result.job.logPath}`
@@ -5008,7 +5582,7 @@ codexCommand.addCommand(codexLoginCommand);
5008
5582
  // src/commands/opencode.ts
5009
5583
  import { access as access2 } from "fs/promises";
5010
5584
  import { homedir as homedir11 } from "os";
5011
- import { join as join13 } from "path";
5585
+ import { join as join14 } from "path";
5012
5586
  import { confirm as confirm6, intro as intro4, isCancel as isCancel7, log as log14, outro as outro3, spinner as spinner6 } from "@clack/prompts";
5013
5587
  import { Command as Command7 } from "commander";
5014
5588
  function reattachRef3(r) {
@@ -5107,7 +5681,7 @@ async function cloudOpencodeCredAvailable(env = process.env) {
5107
5681
  for (const k of OPENCODE_FORWARDED_ENV_KEYS) {
5108
5682
  if ((env[k] ?? "").length > 0) return true;
5109
5683
  }
5110
- for (const p of [OPENCODE_CREDENTIALS_BACKUP_FILE, join13(homedir11(), ".local", "share", "opencode", "auth.json")]) {
5684
+ for (const p of [OPENCODE_CREDENTIALS_BACKUP_FILE, join14(homedir11(), ".local", "share", "opencode", "auth.json")]) {
5111
5685
  try {
5112
5686
  await access2(p);
5113
5687
  return true;
@@ -5257,7 +5831,8 @@ var opencodeCommand = new Command7("opencode").description("Create a sandboxed b
5257
5831
  agentArgs: opencodeArgs,
5258
5832
  createOpts: { ...pickOpencodeCreateOpts(opts), carry: carryForQueue },
5259
5833
  maxRunningOverride,
5260
- maxWorkingOverride
5834
+ maxWorkingOverride,
5835
+ openTerminal: captureOpenTerminalContext(cfg.effective.queue.openIn)
5261
5836
  });
5262
5837
  outro3(
5263
5838
  `job ${result.job.id} queued (${String(result.runningCount)}/${String(result.maxConcurrent)} running); log: ${result.job.logPath}`
@@ -5756,11 +6331,19 @@ var setCommand = new Command8("set").description("Set a config key in the global
5756
6331
  } else {
5757
6332
  process.stdout.write(`${key} = ${fmtValue(r.coerced)} (wrote ${r.path})
5758
6333
  `);
6334
+ warnOnSet(key, r.coerced);
5759
6335
  }
5760
6336
  } catch (err) {
5761
6337
  handleError(err);
5762
6338
  }
5763
6339
  });
6340
+ function warnOnSet(key, value) {
6341
+ if (key === "queue.openIn" && value !== "none") {
6342
+ process.stderr.write(
6343
+ "note: queue.openIn only opens a terminal when you submit the `-i` job from inside tmux, cmux, or iTerm2.\n cmux: the box is opened by the relay's queue worker, a cmux-external process, so cmux's default\n `socketControlMode: cmuxOnly` blocks it. Set `socketControlMode` to `automation` (or `password`)\n in ~/.config/cmux/cmux.json and run `cmux reload-config` for cmux opens to work.\n"
6344
+ );
6345
+ }
6346
+ }
5764
6347
  var unsetCommand = new Command8("unset").description("Remove a config key from the global or per-project file (default: --project)").argument("<key>", "dot-path key (e.g. box.hostSnapshot)").option("--global", "edit ~/.agentbox/config.yaml").option("--project", "edit ~/.agentbox/projects/<hash>/config.yaml (default)").action(async (key, opts) => {
5765
6348
  const scope = resolveWriteScope(opts);
5766
6349
  try {
@@ -5898,8 +6481,51 @@ function handleError(err) {
5898
6481
  var configCommand = new Command8("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);
5899
6482
 
5900
6483
  // src/commands/cp.ts
6484
+ import { resolve as resolve3 } from "path";
5901
6485
  import { log as log15 } from "@clack/prompts";
5902
6486
  import { Command as Command9 } from "commander";
6487
+ function collectExclude(val, acc) {
6488
+ acc.push(val);
6489
+ return acc;
6490
+ }
6491
+ async function guardUploadSize(hostSrc, boxDst, opts) {
6492
+ const tokens = effectiveExcludes(opts.exclude, opts.defaultExcludes);
6493
+ const tarPatterns = toTarExcludes(tokens);
6494
+ const cfg = await loadEffectiveConfig(process.cwd());
6495
+ const maxBytes = cfg.effective.box.cpMaxBytes;
6496
+ const absSrc = resolve3(hostSrc);
6497
+ const measured = await measureCopy(absSrc, tokens);
6498
+ if (opts.yes || measured.totalBytes <= maxBytes) {
6499
+ return tarPatterns;
6500
+ }
6501
+ const dropped = measured.isDir && opts.defaultExcludes ? ` after default excludes (${effectiveExcludes([], true).join(", ")})` : "";
6502
+ const lines = [
6503
+ `${hostSrc} is ${fmtBytes(measured.totalBytes)}${dropped}, over the ${fmtBytes(maxBytes)} per-copy limit.`
6504
+ ];
6505
+ if (measured.isDir) {
6506
+ lines.push(
6507
+ "Biggest remaining folders/subfolders:",
6508
+ ...measured.treeLines,
6509
+ "Each copy must be under the limit. To proceed, EITHER:",
6510
+ " - copy the heavy folders one at a time, e.g.:"
6511
+ );
6512
+ for (const child of measured.topChildren.slice(0, 3)) {
6513
+ lines.push(` agentbox cp ${hostSrc}/${child.path} ${boxDst}`);
6514
+ }
6515
+ lines.push(
6516
+ " - or drop what you do not need:",
6517
+ ` agentbox cp ${hostSrc} ${boxDst} --exclude=<dir>`,
6518
+ " - or copy the whole thing anyway:",
6519
+ ` agentbox cp ${hostSrc} ${boxDst} --yes`
6520
+ );
6521
+ } else {
6522
+ lines.push(
6523
+ "A single file cannot be split or trimmed. To copy it anyway:",
6524
+ ` agentbox cp ${hostSrc} ${boxDst} --yes`
6525
+ );
6526
+ }
6527
+ throw new Error(lines.join("\n"));
6528
+ }
5903
6529
  function parseBoxArg(arg) {
5904
6530
  const idx = arg.indexOf(":");
5905
6531
  if (idx === -1) return null;
@@ -5944,7 +6570,15 @@ function parseArgs(src, dst) {
5944
6570
  var cpCommand = new Command9("cp").description("Copy files between host and box (like `docker cp`; direction picked by `name:` prefix)").argument("<src>", "`box:/path` (download) or host path (upload)").argument(
5945
6571
  "[dst]",
5946
6572
  "`box:/path` (upload) or host path (download); defaults to cwd when downloading"
5947
- ).addHelpText(
6573
+ ).option(
6574
+ "--exclude <pattern>",
6575
+ 'exclude paths matching <pattern> (repeatable; tar glob like "*/foo" or a bare dir name)',
6576
+ collectExclude,
6577
+ []
6578
+ ).option(
6579
+ "--no-default-excludes",
6580
+ `keep the heavy dirs cp drops by default (${effectiveExcludes([], true).join(", ")})`
6581
+ ).option("-y, --yes", "copy even if the source is over the box.cpMaxBytes size limit").addHelpText(
5948
6582
  "after",
5949
6583
  [
5950
6584
  "",
@@ -5952,27 +6586,30 @@ var cpCommand = new Command9("cp").description("Copy files between host and box
5952
6586
  " agentbox cp mybox:/etc/foo ./foo # download (host path optional)",
5953
6587
  " agentbox cp mybox:/workspace/.env # download into cwd",
5954
6588
  " agentbox cp ./local.txt mybox:/workspace/ # upload (host path required)",
5955
- " agentbox cp ./dir mybox:/workspace/ # upload directory (recursive)"
6589
+ " agentbox cp ./dir mybox:/workspace/ # upload directory (recursive)",
6590
+ ' agentbox cp ./dir mybox:/workspace/ --exclude=.git --exclude="*/cache"'
5956
6591
  ].join("\n")
5957
- ).action(async (src, dst) => {
6592
+ ).action(async (src, dst, opts) => {
5958
6593
  try {
5959
6594
  const parsed = parseArgs(src, dst);
5960
6595
  const box = await resolveBoxOrExit(parsed.boxRef);
5961
6596
  const isCloud = (box.provider ?? "docker") !== "docker";
6597
+ const tarPatterns = parsed.direction === "upload" ? await guardUploadSize(parsed.hostPath, `${parsed.boxRef}:${parsed.boxPath}`, opts) : toTarExcludes(effectiveExcludes(opts.exclude, opts.defaultExcludes));
5962
6598
  if (isCloud) {
5963
6599
  const provider = await providerForBox(box);
5964
6600
  if (!provider.uploadPath || !provider.downloadPath) {
5965
6601
  throw new Error(`provider '${provider.name}' does not support cp`);
5966
6602
  }
5967
6603
  if (parsed.direction === "upload") {
5968
- const result = await provider.uploadPath(box, parsed.hostPath, parsed.boxPath);
6604
+ const result = await provider.uploadPath(box, parsed.hostPath, parsed.boxPath, tarPatterns);
5969
6605
  process.stdout.write(`copied to ${box.name}:${result.finalPath}
5970
6606
  `);
5971
6607
  } else {
5972
6608
  const result = await provider.downloadPath(
5973
6609
  box,
5974
6610
  parsed.boxPath,
5975
- parsed.hostPath ?? process.cwd()
6611
+ parsed.hostPath ?? process.cwd(),
6612
+ tarPatterns
5976
6613
  );
5977
6614
  process.stdout.write(`copied to ${result.finalPath}
5978
6615
  `);
@@ -5990,7 +6627,7 @@ var cpCommand = new Command9("cp").description("Copy files between host and box
5990
6627
  throw new Error(`box ${box.name} has no container; was it destroyed?`);
5991
6628
  }
5992
6629
  if (parsed.direction === "upload") {
5993
- const result = await uploadToBox(box, parsed.hostPath, parsed.boxPath);
6630
+ const result = await uploadToBox(box, parsed.hostPath, parsed.boxPath, tarPatterns);
5994
6631
  if (result.warn) {
5995
6632
  log15.warn(`copied to ${box.name}:${result.finalPath}, but ${result.warn}`);
5996
6633
  } else {
@@ -5998,7 +6635,12 @@ var cpCommand = new Command9("cp").description("Copy files between host and box
5998
6635
  `);
5999
6636
  }
6000
6637
  } else {
6001
- const result = await downloadFromBox(box, parsed.boxPath, parsed.hostPath ?? process.cwd());
6638
+ const result = await downloadFromBox(
6639
+ box,
6640
+ parsed.boxPath,
6641
+ parsed.hostPath ?? process.cwd(),
6642
+ tarPatterns
6643
+ );
6002
6644
  process.stdout.write(`copied to ${result.finalPath}
6003
6645
  `);
6004
6646
  }
@@ -6189,12 +6831,14 @@ var createCommand = new Command10("create").description(
6189
6831
  const effectiveCheckpointRef = wiz.discardCheckpoint ? void 0 : checkpointRef;
6190
6832
  if (wiz.action === "switch-to-claude" && isDocker) {
6191
6833
  process.env[WIZARD_AUTOLAUNCH_ENV] = "1";
6834
+ if (wiz.recreate) process.env[WIZARD_RECREATE_ENV] = "1";
6192
6835
  const serialized = serializeEnvFilesForEnv(wiz.envFilesToImport);
6193
6836
  if (serialized !== void 0) process.env[WIZARD_ENV_FILES_ENV] = serialized;
6194
6837
  try {
6195
6838
  await claudeCommand.parseAsync(passthroughFlags(opts), { from: "user" });
6196
6839
  } finally {
6197
6840
  delete process.env[WIZARD_AUTOLAUNCH_ENV];
6841
+ delete process.env[WIZARD_RECREATE_ENV];
6198
6842
  delete process.env[WIZARD_ENV_FILES_ENV];
6199
6843
  }
6200
6844
  return;
@@ -6320,7 +6964,7 @@ var createCommand = new Command10("create").description(
6320
6964
  }
6321
6965
  outro4("done");
6322
6966
  if (attachClaudeAfter) {
6323
- const { cloudAgentAttach: cloudAgentAttach2 } = await import("./_cloud-attach-GUBB5RH2.js");
6967
+ const { cloudAgentAttach: cloudAgentAttach2 } = await import("./_cloud-attach-R6TRWG5L.js");
6324
6968
  await cloudAgentAttach2({
6325
6969
  box: result.record,
6326
6970
  binary: "claude",
@@ -6953,8 +7597,8 @@ var Compositor = class {
6953
7597
  this.drawChrome();
6954
7598
  this.scheduleRender();
6955
7599
  this.pollTimer = setInterval(() => void this.poll(), POLL_MS);
6956
- await new Promise((resolve4) => {
6957
- this.resolveDone = resolve4;
7600
+ await new Promise((resolve5) => {
7601
+ this.resolveDone = resolve5;
6958
7602
  });
6959
7603
  }
6960
7604
  async refreshBoxes() {
@@ -9332,114 +9976,6 @@ function resolveToken(raw) {
9332
9976
  return `<${raw}>`;
9333
9977
  }
9334
9978
 
9335
- // src/lib/drive/tmux.ts
9336
- var TMUX_USER = "vscode";
9337
- async function captureSession(provider, box, session, opts = {}) {
9338
- const argv2 = ["tmux", "capture-pane", opts.ansi ? "-pe" : "-p", "-t", session];
9339
- if (opts.rows) {
9340
- argv2.push("-S", String(opts.rows.from), "-E", String(opts.rows.to));
9341
- }
9342
- const res = await provider.exec(box, argv2, { user: TMUX_USER });
9343
- if (res.exitCode !== 0) {
9344
- throw new Error(failure("capture-pane", session, res.stderr || res.stdout));
9345
- }
9346
- return res.stdout.replace(/\n$/, "");
9347
- }
9348
- async function paneInfo(provider, box, session) {
9349
- const fmt = "#{pane_width},#{pane_height},#{cursor_x},#{cursor_y}";
9350
- const res = await provider.exec(box, ["tmux", "display-message", "-p", "-t", session, fmt], {
9351
- user: TMUX_USER
9352
- });
9353
- if (res.exitCode !== 0) {
9354
- throw new Error(failure("display-message", session, res.stderr || res.stdout));
9355
- }
9356
- const m = /^(\d+),(\d+),(\d+),(\d+)/.exec(res.stdout.trim());
9357
- if (!m) throw new Error(`tmux display-message returned unexpected output: ${res.stdout}`);
9358
- return {
9359
- cols: Number(m[1]),
9360
- rows: Number(m[2]),
9361
- cursor: { x: Number(m[3]), y: Number(m[4]) }
9362
- };
9363
- }
9364
- async function sendLiteral(provider, box, session, literal) {
9365
- if (literal.length === 0) return;
9366
- const res = await provider.exec(box, ["tmux", "send-keys", "-t", session, "-l", "--", literal], {
9367
- user: TMUX_USER
9368
- });
9369
- if (res.exitCode !== 0) {
9370
- throw new Error(failure("send-keys -l", session, res.stderr || res.stdout));
9371
- }
9372
- }
9373
- async function sendKey(provider, box, session, key) {
9374
- const res = await provider.exec(box, ["tmux", "send-keys", "-t", session, key], {
9375
- user: TMUX_USER
9376
- });
9377
- if (res.exitCode !== 0) {
9378
- throw new Error(failure("send-keys", session, res.stderr || res.stdout));
9379
- }
9380
- }
9381
- async function resizeWindow(provider, box, session, cols, rows) {
9382
- const res = await provider.exec(
9383
- box,
9384
- ["tmux", "resize-window", "-t", session, "-x", String(cols), "-y", String(rows)],
9385
- { user: TMUX_USER }
9386
- );
9387
- if (res.exitCode !== 0) {
9388
- throw new Error(failure("resize-window", session, res.stderr || res.stdout));
9389
- }
9390
- }
9391
- async function listSessions(provider, box) {
9392
- const res = await provider.exec(
9393
- box,
9394
- ["tmux", "list-sessions", "-F", "#{session_name}"],
9395
- { user: TMUX_USER }
9396
- );
9397
- if (res.exitCode !== 0) return [];
9398
- return res.stdout.split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
9399
- }
9400
- function failure(op, session, detail) {
9401
- const tail = detail.trim();
9402
- return `tmux ${op} failed for session '${session}'${tail ? `: ${tail}` : ""}`;
9403
- }
9404
-
9405
- // src/lib/drive/session.ts
9406
- var AGENT_SESSION_PRIORITY = ["claude", "codex", "opencode"];
9407
- async function resolveDriveSession(provider, box, explicit) {
9408
- if (await provider.probeState(box) === "paused") {
9409
- process.stderr.write(`drive: box ${box.name} is paused; unpausing
9410
- `);
9411
- await provider.resume(box);
9412
- }
9413
- const sessions = await listSessions(provider, box);
9414
- if (explicit !== void 0 && explicit !== "") {
9415
- if (!sessions.includes(explicit)) {
9416
- throw new SessionNotFoundError(explicit, sessions);
9417
- }
9418
- return { name: explicit, available: sessions };
9419
- }
9420
- for (const candidate of AGENT_SESSION_PRIORITY) {
9421
- if (sessions.includes(candidate)) {
9422
- return { name: candidate, available: sessions };
9423
- }
9424
- }
9425
- if (sessions.length === 1 && sessions[0]) {
9426
- return { name: sessions[0], available: sessions };
9427
- }
9428
- throw new SessionNotFoundError(void 0, sessions);
9429
- }
9430
- var SessionNotFoundError = class extends Error {
9431
- wanted;
9432
- available;
9433
- constructor(wanted, available) {
9434
- const head = wanted ? `no tmux session '${wanted}' in this box` : "no agent tmux session running in this box";
9435
- const tail = available.length ? ` (running: ${available.join(", ")})` : " (tmux server not running or no sessions)";
9436
- super(head + tail);
9437
- this.name = "SessionNotFoundError";
9438
- this.wanted = wanted;
9439
- this.available = available;
9440
- }
9441
- };
9442
-
9443
9979
  // src/commands/drive.ts
9444
9980
  var PROMPT_ENTER_DELAY_MS = 200;
9445
9981
  var POLL_INTERVAL_MS2 = 250;
@@ -9507,7 +10043,7 @@ var drivePromptCommand = new Command24("prompt").description('Type text into the
9507
10043
  const session = await resolveDriveSession(provider, box, opts.session);
9508
10044
  const delay = opts.delay !== void 0 ? parsePositiveInt2(opts.delay, "--delay") : PROMPT_ENTER_DELAY_MS;
9509
10045
  await sendLiteral(provider, box, session.name, text);
9510
- if (delay > 0) await sleep2(delay);
10046
+ if (delay > 0) await sleep3(delay);
9511
10047
  await sendKey(provider, box, session.name, "Enter");
9512
10048
  } catch (err) {
9513
10049
  handleDriveError(err);
@@ -9533,7 +10069,7 @@ var driveWaitCommand = new Command24("wait").description("Block until --text app
9533
10069
  }
9534
10070
  return;
9535
10071
  }
9536
- await sleep2(POLL_INTERVAL_MS2);
10072
+ await sleep3(POLL_INTERVAL_MS2);
9537
10073
  }
9538
10074
  const elapsedMs = Date.now() - start;
9539
10075
  if (opts.json === true) {
@@ -9593,7 +10129,7 @@ function parsePositiveInt2(raw, label) {
9593
10129
  }
9594
10130
  return n;
9595
10131
  }
9596
- function sleep2(ms) {
10132
+ function sleep3(ms) {
9597
10133
  return new Promise((r) => setTimeout(r, ms));
9598
10134
  }
9599
10135
 
@@ -9602,7 +10138,7 @@ import { log as log30 } from "@clack/prompts";
9602
10138
  import { Command as Command25 } from "commander";
9603
10139
  import { existsSync as existsSync5, readdirSync, statSync } from "fs";
9604
10140
  import { homedir as homedir12 } from "os";
9605
- import { join as join14 } from "path";
10141
+ import { join as join15 } from "path";
9606
10142
  var FORK_AGENTS = ["claude", "codex", "opencode"];
9607
10143
  var AGENT_COMMAND = {
9608
10144
  claude: claudeCommand,
@@ -9622,12 +10158,12 @@ function resolveSessionArgs(agent, opts) {
9622
10158
  }
9623
10159
  if (opts.session) return ["--resume", opts.session];
9624
10160
  if (agent === "codex") return ["--continue"];
9625
- const dir = join14(homedir12(), ".claude", "projects", encodeClaudeProjectsDir(opts.workspace));
10161
+ const dir = join15(homedir12(), ".claude", "projects", encodeClaudeProjectsDir(opts.workspace));
9626
10162
  if (!existsSync5(dir)) return ["--continue"];
9627
10163
  const now = Date.now();
9628
10164
  const recent = readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => {
9629
10165
  try {
9630
- return statSync(join14(dir, f)).mtimeMs;
10166
+ return statSync(join15(dir, f)).mtimeMs;
9631
10167
  } catch {
9632
10168
  return 0;
9633
10169
  }
@@ -9738,13 +10274,13 @@ import {
9738
10274
  writeFileSync as writeFileSync5
9739
10275
  } from "fs";
9740
10276
  import { homedir as homedir16 } from "os";
9741
- import { dirname as dirname3, join as join18, resolve as resolve3, sep } from "path";
10277
+ import { dirname as dirname3, join as join19, resolve as resolve4, sep } from "path";
9742
10278
  import { fileURLToPath } from "url";
9743
10279
 
9744
10280
  // src/lib/doctor-checks.ts
9745
10281
  import { accessSync, constants as fsConstants, mkdirSync as mkdirSync3 } from "fs";
9746
10282
  import { homedir as homedir13 } from "os";
9747
- import { join as join15 } from "path";
10283
+ import { join as join16 } from "path";
9748
10284
  import { execa as execa3 } from "execa";
9749
10285
  var ALL_PROVIDERS = ["docker", "daytona", "hetzner", "vercel", "e2b"];
9750
10286
  var NODE_MIN_MAJOR = 20;
@@ -9764,12 +10300,12 @@ function parseNodeMajorMinor(v) {
9764
10300
  if (!m) return [0, 0];
9765
10301
  return [Number(m[1]), Number(m[2])];
9766
10302
  }
9767
- function firstLine(s) {
10303
+ function firstLine2(s) {
9768
10304
  const i = s.indexOf("\n");
9769
10305
  return i === -1 ? s : s.slice(0, i);
9770
10306
  }
9771
10307
  function errSummary(err) {
9772
- return err instanceof Error ? firstLine(err.message) : String(err);
10308
+ return err instanceof Error ? firstLine2(err.message) : String(err);
9773
10309
  }
9774
10310
  function checkNode() {
9775
10311
  const v = process.versions.node;
@@ -9792,7 +10328,7 @@ function checkPlatform() {
9792
10328
  };
9793
10329
  }
9794
10330
  function checkAgentboxHome() {
9795
- const dir = join15(homedir13(), ".agentbox");
10331
+ const dir = join16(homedir13(), ".agentbox");
9796
10332
  try {
9797
10333
  mkdirSync3(dir, { recursive: true });
9798
10334
  accessSync(dir, fsConstants.W_OK);
@@ -9864,7 +10400,7 @@ async function dockerChecks() {
9864
10400
  ];
9865
10401
  }
9866
10402
  const daemonRes = { label: "docker daemon", status: "ok", detail: "reachable" };
9867
- const mod = await import("./dist-4DPOL5A7.js");
10403
+ const mod = await import("./dist-OGJGZETZ.js");
9868
10404
  let imgRes;
9869
10405
  try {
9870
10406
  const img = await mod.imageInfo(mod.DEFAULT_BOX_IMAGE);
@@ -9895,7 +10431,7 @@ async function dockerChecks() {
9895
10431
  }
9896
10432
  async function daytonaChecks() {
9897
10433
  try {
9898
- const mod = await import("./dist-3IMQNTTV.js");
10434
+ const mod = await import("./dist-FIFEFKJ7.js");
9899
10435
  const status = await mod.getDaytonaStatus();
9900
10436
  if (!status.configured) {
9901
10437
  return [
@@ -9931,7 +10467,7 @@ async function daytonaChecks() {
9931
10467
  }
9932
10468
  async function hetznerChecks() {
9933
10469
  try {
9934
- const mod = await import("./dist-J2IHD5T7.js");
10470
+ const mod = await import("./dist-AGTIA7AD.js");
9935
10471
  const cred = mod.readHetznerCredStatus();
9936
10472
  const credRes = cred.source === "none" ? {
9937
10473
  label: "credentials",
@@ -9963,7 +10499,7 @@ async function hetznerChecks() {
9963
10499
  }
9964
10500
  async function vercelChecks() {
9965
10501
  try {
9966
- const mod = await import("./dist-57M6ZA7H.js");
10502
+ const mod = await import("./dist-S4XR4ACV.js");
9967
10503
  const cred = mod.readVercelCredStatus();
9968
10504
  const credRes = cred.auth === "none" ? {
9969
10505
  label: "credentials",
@@ -9999,7 +10535,7 @@ async function vercelChecks() {
9999
10535
  }
10000
10536
  async function e2bChecks() {
10001
10537
  try {
10002
- const mod = await import("./dist-34RKQ74M.js");
10538
+ const mod = await import("./dist-JZ3XO6EB.js");
10003
10539
  const cred = mod.readE2bCredStatus();
10004
10540
  const credRes = cred.auth === "none" ? {
10005
10541
  label: "credentials",
@@ -10130,10 +10666,10 @@ function formatDetailed(groups) {
10130
10666
  // src/lib/first-run.ts
10131
10667
  import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
10132
10668
  import { homedir as homedir14 } from "os";
10133
- import { dirname, join as join16 } from "path";
10669
+ import { dirname, join as join17 } from "path";
10134
10670
  var MARKER_VERSION = 1;
10135
10671
  function setupMarkerPath() {
10136
- return join16(homedir14(), ".agentbox", "setup-complete.json");
10672
+ return join17(homedir14(), ".agentbox", "setup-complete.json");
10137
10673
  }
10138
10674
  function isFirstRun() {
10139
10675
  return !existsSync6(setupMarkerPath());
@@ -10154,12 +10690,12 @@ import { intro as intro6, log as log31, note as note2, outro as outro5 } from "@
10154
10690
  import { Command as Command26 } from "commander";
10155
10691
  import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "fs";
10156
10692
  import { homedir as homedir15 } from "os";
10157
- import { dirname as dirname2, join as join17 } from "path";
10693
+ import { dirname as dirname2, join as join18 } from "path";
10158
10694
  var CONTROL_ID = "agentbox";
10159
10695
  function cmuxDockPath(env = process.env) {
10160
10696
  const xdg = env["XDG_CONFIG_HOME"];
10161
- const base = xdg && xdg.length > 0 ? xdg : join17(homedir15(), ".config");
10162
- return join17(base, "cmux", "dock.json");
10697
+ const base = xdg && xdg.length > 0 ? xdg : join18(homedir15(), ".config");
10698
+ return join18(base, "cmux", "dock.json");
10163
10699
  }
10164
10700
  function upsertAgentboxControl(doc, opts) {
10165
10701
  const controls = Array.isArray(doc.controls) ? doc.controls : [];
@@ -10251,7 +10787,7 @@ var SYNC_END2 = "\x1B[?2026l";
10251
10787
  var HIDE_CURSOR = "\x1B[?25l";
10252
10788
  var SHOW_CURSOR = "\x1B[?25h";
10253
10789
  var SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
10254
- var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
10790
+ var sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
10255
10791
  function shineColor(dist) {
10256
10792
  const d = Math.abs(dist);
10257
10793
  if (d === 0) return 231;
@@ -10301,11 +10837,11 @@ ${HIDE_CURSOR}`);
10301
10837
  statusLine2(spin) + "\x1B[3A\r" + // back up to the logo's first row
10302
10838
  SYNC_END2;
10303
10839
  process.stdout.write(frame);
10304
- await sleep3(frameMs);
10840
+ await sleep4(frameMs);
10305
10841
  }
10306
10842
  process.stdout.write(SYNC_BEGIN2 + `\x1B[38;5;39m${LOGO_L1}
10307
10843
  ${LOGO_L2}\x1B[0m` + SYNC_END2);
10308
- await sleep3(250);
10844
+ await sleep4(250);
10309
10845
  process.stdout.write(SYNC_BEGIN2 + "\n\x1B[2K\n\x1B[2K" + SHOW_CURSOR + SYNC_END2);
10310
10846
  process.removeListener("exit", restoreCursor);
10311
10847
  process.removeListener("SIGINT", onSigint);
@@ -10313,30 +10849,30 @@ ${LOGO_L2}\x1B[0m` + SYNC_END2);
10313
10849
  var LEGACY_INFO_MARKER = "Drive AgentBox from the host:";
10314
10850
  function installTargets() {
10315
10851
  const home = homedir16();
10316
- const claudeSkills = join18(home, ".claude", "skills");
10852
+ const claudeSkills = join19(home, ".claude", "skills");
10317
10853
  return [
10318
- { src: join18("agentbox", "SKILL.md"), dest: join18(claudeSkills, "agentbox", "SKILL.md") },
10854
+ { src: join19("agentbox", "SKILL.md"), dest: join19(claudeSkills, "agentbox", "SKILL.md") },
10319
10855
  {
10320
- src: join18("agentbox-info", "SKILL.md"),
10321
- dest: join18(claudeSkills, "agentbox-info", "SKILL.md")
10856
+ src: join19("agentbox-info", "SKILL.md"),
10857
+ dest: join19(claudeSkills, "agentbox-info", "SKILL.md")
10322
10858
  },
10323
10859
  {
10324
- src: join18("codex", "agentbox.md"),
10325
- dest: join18(home, ".codex", "prompts", "agentbox.md"),
10326
- gateDir: join18(home, ".codex")
10860
+ src: join19("codex", "agentbox.md"),
10861
+ dest: join19(home, ".codex", "prompts", "agentbox.md"),
10862
+ gateDir: join19(home, ".codex")
10327
10863
  },
10328
10864
  {
10329
- src: join18("opencode", "agentbox.md"),
10330
- dest: join18(home, ".config", "opencode", "commands", "agentbox.md"),
10331
- gateDir: join18(home, ".config", "opencode")
10865
+ src: join19("opencode", "agentbox.md"),
10866
+ dest: join19(home, ".config", "opencode", "commands", "agentbox.md"),
10867
+ gateDir: join19(home, ".config", "opencode")
10332
10868
  }
10333
10869
  ];
10334
10870
  }
10335
10871
  function resolveHostSkillsDir() {
10336
10872
  const here = dirname3(fileURLToPath(import.meta.url));
10337
10873
  const candidates = [
10338
- resolve3(here, "..", "share", "host-skills"),
10339
- resolve3(here, "..", "..", "share", "host-skills")
10874
+ resolve4(here, "..", "share", "host-skills"),
10875
+ resolve4(here, "..", "..", "share", "host-skills")
10340
10876
  ];
10341
10877
  for (const c of candidates) {
10342
10878
  if (existsSync8(c)) return c;
@@ -10375,7 +10911,7 @@ function installHostSkills(opts = {}) {
10375
10911
  const blocked = [];
10376
10912
  let skipped = 0;
10377
10913
  for (const t of installTargets()) {
10378
- const src = join18(srcDir, t.src);
10914
+ const src = join19(srcDir, t.src);
10379
10915
  if (!existsSync8(src)) {
10380
10916
  if (!quiet) log32.warn(`bundled file missing (skipped): ${src}`);
10381
10917
  skipped++;
@@ -10397,7 +10933,7 @@ function installHostSkills(opts = {}) {
10397
10933
  mkdirSync6(dirname3(t.dest), { recursive: true });
10398
10934
  if (link) {
10399
10935
  rmSync(t.dest, { force: true });
10400
- symlinkSync2(resolve3(srcDir, t.src), t.dest);
10936
+ symlinkSync2(resolve4(srcDir, t.src), t.dest);
10401
10937
  } else {
10402
10938
  if (isSymlink(t.dest)) rmSync(t.dest, { force: true });
10403
10939
  writeFileSync5(t.dest, readFileSync2(src, "utf8"));
@@ -10430,7 +10966,7 @@ function ensureTty() {
10430
10966
  async function runProviderLogin(name) {
10431
10967
  if (name === "docker") return true;
10432
10968
  if (name === "daytona") {
10433
- const mod2 = await import("./dist-3IMQNTTV.js");
10969
+ const mod2 = await import("./dist-FIFEFKJ7.js");
10434
10970
  const status2 = await mod2.getDaytonaStatus();
10435
10971
  if (status2.configured) {
10436
10972
  log32.info("daytona: already configured");
@@ -10443,7 +10979,7 @@ async function runProviderLogin(name) {
10443
10979
  return true;
10444
10980
  }
10445
10981
  if (name === "hetzner") {
10446
- const mod2 = await import("./dist-J2IHD5T7.js");
10982
+ const mod2 = await import("./dist-AGTIA7AD.js");
10447
10983
  const status2 = mod2.readHetznerCredStatus();
10448
10984
  if (status2.source !== "none") {
10449
10985
  log32.info("hetzner: already configured");
@@ -10456,7 +10992,7 @@ async function runProviderLogin(name) {
10456
10992
  return true;
10457
10993
  }
10458
10994
  if (name === "vercel") {
10459
- const mod2 = await import("./dist-57M6ZA7H.js");
10995
+ const mod2 = await import("./dist-S4XR4ACV.js");
10460
10996
  const status2 = mod2.readVercelCredStatus();
10461
10997
  if (status2.auth !== "none") {
10462
10998
  log32.info(`vercel: already configured (${status2.auth})`);
@@ -10468,7 +11004,7 @@ async function runProviderLogin(name) {
10468
11004
  await mod2.ensureVercelCredentials();
10469
11005
  return true;
10470
11006
  }
10471
- const mod = await import("./dist-34RKQ74M.js");
11007
+ const mod = await import("./dist-JZ3XO6EB.js");
10472
11008
  const status = mod.readE2bCredStatus();
10473
11009
  if (status.auth !== "none") {
10474
11010
  log32.info(`e2b: already configured (${status.auth})`);
@@ -10868,17 +11404,17 @@ async function applyLiveCloudStates(boxes) {
10868
11404
  );
10869
11405
  }
10870
11406
  function withTimeout(p, ms) {
10871
- return new Promise((resolve4) => {
10872
- const t = setTimeout(() => resolve4(null), ms);
11407
+ return new Promise((resolve5) => {
11408
+ const t = setTimeout(() => resolve5(null), ms);
10873
11409
  if (typeof t.unref === "function") t.unref();
10874
11410
  p.then(
10875
11411
  (v) => {
10876
11412
  clearTimeout(t);
10877
- resolve4(v);
11413
+ resolve5(v);
10878
11414
  },
10879
11415
  () => {
10880
11416
  clearTimeout(t);
10881
- resolve4(null);
11417
+ resolve5(null);
10882
11418
  }
10883
11419
  );
10884
11420
  });
@@ -10932,7 +11468,7 @@ async function watchRender(produce, rawInterval, opts = {}) {
10932
11468
  process.once("SIGINT", () => process.exit(0));
10933
11469
  }
10934
11470
  const hint = interactive ? "q or Ctrl-C to exit" : "Ctrl-C to exit";
10935
- const sleep5 = (d) => new Promise((r) => {
11471
+ const sleep6 = (d) => new Promise((r) => {
10936
11472
  const t = setTimeout(() => {
10937
11473
  wake = null;
10938
11474
  r();
@@ -10963,7 +11499,7 @@ async function watchRender(produce, rawInterval, opts = {}) {
10963
11499
  ${trimmed}
10964
11500
  `);
10965
11501
  }
10966
- await sleep5(ms);
11502
+ await sleep6(ms);
10967
11503
  if (exiting) {
10968
11504
  restore();
10969
11505
  process.exit(0);
@@ -11338,7 +11874,7 @@ import { log as log35 } from "@clack/prompts";
11338
11874
  import { execa as execa4 } from "execa";
11339
11875
  import { existsSync as existsSync9, mkdirSync as mkdirSync7 } from "fs";
11340
11876
  import { homedir as homedir17 } from "os";
11341
- import { join as join19 } from "path";
11877
+ import { join as join20 } from "path";
11342
11878
  import { Command as Command32 } from "commander";
11343
11879
 
11344
11880
  // src/commands/path.ts
@@ -11400,7 +11936,7 @@ var openCommand = new Command32("open").description("Open a box's /workspace in
11400
11936
  }
11401
11937
  });
11402
11938
  async function runCloudOpen(box, provider, opts) {
11403
- const mountRoot = join19(homedir17(), ".agentbox", "mounts", box.name);
11939
+ const mountRoot = join20(homedir17(), ".agentbox", "mounts", box.name);
11404
11940
  if (opts.unmount) {
11405
11941
  const ok = await tryUnmount(mountRoot);
11406
11942
  if (ok) process.stdout.write(`unmounted ${mountRoot}
@@ -11674,7 +12210,7 @@ async function pruneCloud(provider, opts) {
11674
12210
  }
11675
12211
 
11676
12212
  // src/commands/queue.ts
11677
- import { readFile as readFile5, stat as stat5 } from "fs/promises";
12213
+ import { readFile as readFile5, stat as stat6 } from "fs/promises";
11678
12214
  import { intro as intro8, log as log37, outro as outro7 } from "@clack/prompts";
11679
12215
  import { Command as Command35 } from "commander";
11680
12216
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["done", "failed", "cancelled"]);
@@ -11721,7 +12257,7 @@ var queueShowCommand = new Command35("show").description("Dump a job manifest an
11721
12257
  process.stdout.write(JSON.stringify(job, null, 2) + "\n");
11722
12258
  const tailN = Number.parseInt(opts.tail, 10) || 50;
11723
12259
  try {
11724
- await stat5(job.logPath);
12260
+ await stat6(job.logPath);
11725
12261
  const text = await readFile5(job.logPath, "utf8");
11726
12262
  const lines = text.split(/\r?\n/);
11727
12263
  const slice = lines.slice(Math.max(0, lines.length - tailN - 1));
@@ -11873,11 +12409,11 @@ async function pollUntil(deadline, probe) {
11873
12409
  if (result !== void 0) return result;
11874
12410
  const remaining = deadline - Date.now();
11875
12411
  if (remaining <= 0) break;
11876
- await sleep4(Math.min(QUEUE_POLL_INTERVAL_MS, remaining));
12412
+ await sleep5(Math.min(QUEUE_POLL_INTERVAL_MS, remaining));
11877
12413
  }
11878
12414
  throw new QueueWaitTimeout();
11879
12415
  }
11880
- function sleep4(ms) {
12416
+ function sleep5(ms) {
11881
12417
  return new Promise((r) => setTimeout(r, ms));
11882
12418
  }
11883
12419
  function parsePositiveInt3(raw, label) {
@@ -11927,7 +12463,8 @@ async function rehydrateFromState() {
11927
12463
  cloudBackend: b.cloud?.backend,
11928
12464
  relayPreviewUrl: b.cloud?.relayPreviewUrl,
11929
12465
  relayPreviewToken: b.cloud?.relayPreviewToken,
11930
- bridgeToken: b.cloud?.bridgeToken
12466
+ bridgeToken: b.cloud?.bridgeToken,
12467
+ autoApproveHostActions: b.autoApproveHostActions
11931
12468
  }))
11932
12469
  );
11933
12470
  }
@@ -12149,6 +12686,36 @@ async function runDockerJob(job, log47, onBoxCreated) {
12149
12686
  } else {
12150
12687
  throw new Error(`unknown agent kind: ${String(job.agent)}`);
12151
12688
  }
12689
+ await maybeOpenQueuedTerminal(job, result.record.name, log47);
12690
+ }
12691
+ function agentBinaryName(agent) {
12692
+ return agent === "claude-code" ? "claude" : agent;
12693
+ }
12694
+ async function maybeOpenQueuedTerminal(job, boxName, log47) {
12695
+ const ctx = job.openTerminal;
12696
+ if (!ctx) return;
12697
+ const cliEntry = process.env["AGENTBOX_CLI_ENTRY"];
12698
+ if (!cliEntry) {
12699
+ log47.write("queue.openIn: AGENTBOX_CLI_ENTRY unset; cannot open terminal");
12700
+ return;
12701
+ }
12702
+ const argv2 = [
12703
+ process.execPath,
12704
+ cliEntry,
12705
+ agentBinaryName(job.agent),
12706
+ "attach",
12707
+ boxName,
12708
+ "--attach-in",
12709
+ "same"
12710
+ ];
12711
+ try {
12712
+ const r = await spawnQueuedOpenTerminal(ctx, argv2, boxName);
12713
+ log47.write(
12714
+ r.launched ? `queue.openIn: ${r.note}` : `queue.openIn: open failed: ${r.error ?? ""}`
12715
+ );
12716
+ } catch (err) {
12717
+ log47.write(`queue.openIn: open threw: ${err instanceof Error ? err.message : String(err)}`);
12718
+ }
12152
12719
  }
12153
12720
  async function runCloudJob(job, log47, onBoxCreated) {
12154
12721
  const opts = job.createOpts;
@@ -12206,6 +12773,7 @@ async function runCloudJob(job, log47, onBoxCreated) {
12206
12773
  sessionName,
12207
12774
  extraArgs
12208
12775
  });
12776
+ await maybeOpenQueuedTerminal(job, result.record.name, log47);
12209
12777
  }
12210
12778
  function buildOverridesFromJob(job) {
12211
12779
  const opts = job.createOpts;
@@ -12757,12 +13325,12 @@ async function renderText(i) {
12757
13325
  `env files ${i.record.withEnv ? "yes" : "no"}`,
12758
13326
  "endpoints",
12759
13327
  ...renderEndpoints(i),
12760
- `mem limit ${lim?.memoryBytes ? fmtBytes(lim.memoryBytes) : "unlimited"}`,
13328
+ `mem limit ${lim?.memoryBytes ? fmtBytes2(lim.memoryBytes) : "unlimited"}`,
12761
13329
  `cpu limit ${fmtLimit(lim?.cpus, "")}`,
12762
13330
  `pids limit ${fmtLimit(lim?.pidsLimit, "")}`,
12763
13331
  `disk limit ${lim?.disk ? `${lim.disk} (best-effort; no-op on overlay2/macOS)` : "unlimited"}`,
12764
13332
  `snapshot dir ${i.record.snapshotDir ?? "(none)"}`,
12765
- `snapshot size ${fmtBytes(i.snapshotSizeBytes)}`,
13333
+ `snapshot size ${fmtBytes2(i.snapshotSizeBytes)}`,
12766
13334
  `checkpoint ${renderCheckpoint(i, ckptBytes)}`,
12767
13335
  `host export ${i.hostPaths.mergedExport} (run \`agentbox open\` to refresh)`,
12768
13336
  `created ${i.record.createdAt}`
@@ -12772,7 +13340,7 @@ async function renderText(i) {
12772
13340
  function renderCheckpoint(i, sizeBytes) {
12773
13341
  const src = i.record.checkpointSource;
12774
13342
  if (!src || !i.record.checkpointImage) return "(none)";
12775
- const sizePart = sizeBytes !== null ? ` ${fmtBytes(sizeBytes)}` : "";
13343
+ const sizePart = sizeBytes !== null ? ` ${fmtBytes2(sizeBytes)}` : "";
12776
13344
  return `${src.ref} (${src.type}, chain ${src.chain.length}) \u2192 ${i.record.checkpointImage}${sizePart}`;
12777
13345
  }
12778
13346
  function renderClaudeSession(i) {
@@ -12843,7 +13411,7 @@ async function renderCloudText(box) {
12843
13411
  `bridge token ${box.cloud?.bridgeToken ? "(set)" : "(unset)"}`,
12844
13412
  `playwright ${box.withPlaywright ? "yes" : "no"}`,
12845
13413
  `env files ${box.withEnv ? "yes" : "no"}`,
12846
- `mem limit ${lim?.memoryBytes ? fmtBytes(lim.memoryBytes) : "unlimited"}`,
13414
+ `mem limit ${lim?.memoryBytes ? fmtBytes2(lim.memoryBytes) : "unlimited"}`,
12847
13415
  `cpu limit ${fmtLimit(lim?.cpus, "")}`,
12848
13416
  `pids limit ${fmtLimit(lim?.pidsLimit, "")}`,
12849
13417
  `persisted ${persisted ? `${persisted.timestamp} (${String(persisted.services.length)} svc, ${String(persisted.tasks.length)} tasks, ${String(persisted.ports.length)} ports)` : "(none)"}`,
@@ -13004,20 +13572,20 @@ function renderResources(s) {
13004
13572
  if (s.live) {
13005
13573
  seg.push(`cpu ${fmtPercent(s.cpuPercent)}${lim(s.limits.cpus)}`);
13006
13574
  seg.push(
13007
- `mem ${fmtBytes(s.memUsedBytes)} / ${fmtBytes(s.memLimitBytes)} (${fmtPercent(s.memPercent)})${lim(s.limits.memoryBytes ? fmtBytes(s.limits.memoryBytes) : null)}`
13575
+ `mem ${fmtBytes2(s.memUsedBytes)} / ${fmtBytes2(s.memLimitBytes)} (${fmtPercent(s.memPercent)})${lim(s.limits.memoryBytes ? fmtBytes2(s.limits.memoryBytes) : null)}`
13008
13576
  );
13009
13577
  seg.push(`pids ${s.pids === null ? "\u2014" : String(s.pids)}${lim(s.limits.pidsLimit)}`);
13010
13578
  } else {
13011
13579
  seg.push("not running");
13012
- if (s.limits.memoryBytes) seg.push(`mem limit ${fmtBytes(s.limits.memoryBytes)}`);
13580
+ if (s.limits.memoryBytes) seg.push(`mem limit ${fmtBytes2(s.limits.memoryBytes)}`);
13013
13581
  if (s.limits.cpus) seg.push(`cpu limit ${String(s.limits.cpus)}`);
13014
13582
  if (s.limits.pidsLimit) seg.push(`pids limit ${String(s.limits.pidsLimit)}`);
13015
13583
  }
13016
13584
  seg.push(
13017
- `disk ${fmtBytes(s.diskUsedBytes)}${s.limits.disk ? ` (limit ${s.limits.disk}, no-op on overlay2/macOS)` : ""}`
13585
+ `disk ${fmtBytes2(s.diskUsedBytes)}${s.limits.disk ? ` (limit ${s.limits.disk}, no-op on overlay2/macOS)` : ""}`
13018
13586
  );
13019
- if (s.snapshotDiskBytes !== null) seg.push(`snapshot ${fmtBytes(s.snapshotDiskBytes)}`);
13020
- if (s.checkpointVolumeBytes !== null) seg.push(`ckpt ${fmtBytes(s.checkpointVolumeBytes)}`);
13587
+ if (s.snapshotDiskBytes !== null) seg.push(`snapshot ${fmtBytes2(s.snapshotDiskBytes)}`);
13588
+ if (s.checkpointVolumeBytes !== null) seg.push(`ckpt ${fmtBytes2(s.checkpointVolumeBytes)}`);
13021
13589
  let line = ` ${seg.join(" ")}`;
13022
13590
  for (const w of s.warnings) line += `
13023
13591
  note: ${w}`;
@@ -13114,8 +13682,8 @@ restart with: agentbox start ${box.name}
13114
13682
  import { Command as Command43 } from "commander";
13115
13683
  var COLS = ["BOX", "STATE", "CPU%", "MEM USAGE / LIMIT", "MEM%", "PIDS", "DISK", "NET I/O"];
13116
13684
  function row(name, state, s) {
13117
- const mem = `${fmtBytes(s.memUsedBytes)} / ${fmtBytes(s.memLimitBytes)}`;
13118
- const net = s.netRxBytes === null && s.netTxBytes === null ? "\u2014" : `${fmtBytes(s.netRxBytes)} / ${fmtBytes(s.netTxBytes)}`;
13685
+ const mem = `${fmtBytes2(s.memUsedBytes)} / ${fmtBytes2(s.memLimitBytes)}`;
13686
+ const net = s.netRxBytes === null && s.netTxBytes === null ? "\u2014" : `${fmtBytes2(s.netRxBytes)} / ${fmtBytes2(s.netTxBytes)}`;
13119
13687
  return [
13120
13688
  name,
13121
13689
  state,
@@ -13123,7 +13691,7 @@ function row(name, state, s) {
13123
13691
  s.live ? mem : "\u2014",
13124
13692
  fmtPercent(s.memPercent),
13125
13693
  s.pids === null ? "\u2014" : String(s.pids),
13126
- fmtBytes(s.diskUsedBytes),
13694
+ fmtBytes2(s.diskUsedBytes),
13127
13695
  s.live ? net : "\u2014"
13128
13696
  ];
13129
13697
  }
@@ -13179,8 +13747,8 @@ async function renderProjectFooters() {
13179
13747
  allCheckpointImagesBytes(),
13180
13748
  agentboxHomeBytes()
13181
13749
  ]);
13182
- if (home !== null) parts.push(`~/.agentbox: ${fmtBytes(home)}`);
13183
- if (ckpt !== null) parts.push(`checkpoints: ${fmtBytes(ckpt)}`);
13750
+ if (home !== null) parts.push(`~/.agentbox: ${fmtBytes2(home)}`);
13751
+ if (ckpt !== null) parts.push(`checkpoints: ${fmtBytes2(ckpt)}`);
13184
13752
  return parts.length > 0 ? `
13185
13753
 
13186
13754
  SYSTEM: ${parts.join(" - ")}` : "";