@madarco/agentbox 0.8.0 → 0.10.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 (51) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/README.md +161 -0
  3. package/dist/{_cloud-attach-T727ZPRV.js → _cloud-attach-O6NYTLES.js} +4 -4
  4. package/dist/{chunk-67N47KUS.js → chunk-2GPORKYF.js} +349 -182
  5. package/dist/chunk-2GPORKYF.js.map +1 -0
  6. package/dist/{chunk-6OZDFNBF.js → chunk-7UIAO7PC.js} +401 -82
  7. package/dist/chunk-7UIAO7PC.js.map +1 -0
  8. package/dist/{chunk-BGK32PZE.js → chunk-KL36BRN4.js} +2 -2
  9. package/dist/chunk-KL36BRN4.js.map +1 -0
  10. package/dist/chunk-MTVI44DW.js +662 -0
  11. package/dist/chunk-MTVI44DW.js.map +1 -0
  12. package/dist/{chunk-FODMEHD3.js → chunk-R4O5WPHW.js} +705 -77
  13. package/dist/chunk-R4O5WPHW.js.map +1 -0
  14. package/dist/{dist-ZODPD2I6.js → dist-5FQGYRW5.js} +20 -10
  15. package/dist/dist-5FQGYRW5.js.map +1 -0
  16. package/dist/{dist-LOZBWMBF.js → dist-BQNX7RQE.js} +19 -3
  17. package/dist/dist-PZW3GWWU.js +874 -0
  18. package/dist/dist-PZW3GWWU.js.map +1 -0
  19. package/dist/{dist-L4LCG5SJ.js → dist-TMHSUVTP.js} +4 -4
  20. package/dist/index.js +2385 -842
  21. package/dist/index.js.map +1 -1
  22. package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js → prepared-state-CL4CWXQA-H5THETIM.js} +2 -2
  23. package/package.json +11 -7
  24. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +9 -8
  25. package/runtime/docker/packages/ctl/dist/bin.cjs +129 -31
  26. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
  27. package/runtime/hetzner/agentbox-setup-skill.md +9 -8
  28. package/runtime/hetzner/agentbox-vnc-start +15 -1
  29. package/runtime/hetzner/ctl.cjs +129 -31
  30. package/runtime/relay/bin.cjs +260 -39
  31. package/runtime/vercel/agentbox-checkpoint-cleanup +52 -0
  32. package/runtime/vercel/agentbox-codex-hooks.json +68 -0
  33. package/runtime/vercel/agentbox-open +28 -0
  34. package/runtime/vercel/agentbox-setup-skill.md +197 -0
  35. package/runtime/vercel/agentbox-vnc-start +91 -0
  36. package/runtime/vercel/claude-managed-settings.json +115 -0
  37. package/runtime/vercel/ctl.cjs +23495 -0
  38. package/runtime/vercel/custom-system-CLAUDE.md +47 -0
  39. package/runtime/vercel/gh-shim +263 -0
  40. package/runtime/vercel/git-shim +131 -0
  41. package/runtime/vercel/scripts/provision.sh +314 -0
  42. package/share/agentbox-setup/SKILL.md +9 -8
  43. package/dist/chunk-67N47KUS.js.map +0 -1
  44. package/dist/chunk-6OZDFNBF.js.map +0 -1
  45. package/dist/chunk-BGK32PZE.js.map +0 -1
  46. package/dist/chunk-FODMEHD3.js.map +0 -1
  47. package/dist/dist-ZODPD2I6.js.map +0 -1
  48. /package/dist/{_cloud-attach-T727ZPRV.js.map → _cloud-attach-O6NYTLES.js.map} +0 -0
  49. /package/dist/{dist-LOZBWMBF.js.map → dist-BQNX7RQE.js.map} +0 -0
  50. /package/dist/{dist-L4LCG5SJ.js.map → dist-TMHSUVTP.js.map} +0 -0
  51. /package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js.map → prepared-state-CL4CWXQA-H5THETIM.js.map} +0 -0
@@ -19,13 +19,13 @@ import {
19
19
  recordBox,
20
20
  removeBoxRecord,
21
21
  writePreparedDockerState
22
- } from "./chunk-BGK32PZE.js";
22
+ } from "./chunk-KL36BRN4.js";
23
23
 
24
24
  // ../../packages/sandbox-docker/dist/index.js
25
25
  import { randomBytes as randomBytes3 } from "crypto";
26
26
  import { mkdir as mkdir7, stat as stat6 } from "fs/promises";
27
27
  import { homedir as homedir9 } from "os";
28
- import { basename as basename2, join as join10, resolve as resolve3 } from "path";
28
+ import { basename as basename3, join as join10, resolve as resolve3 } from "path";
29
29
  import { execa as execa13 } from "execa";
30
30
 
31
31
  // ../../packages/ctl/dist/index.js
@@ -736,6 +736,7 @@ var BUILT_IN_DEFAULTS = {
736
736
  defaultCheckpointDocker: "",
737
737
  defaultCheckpointDaytona: "",
738
738
  defaultCheckpointHetzner: "",
739
+ defaultCheckpointVercel: "",
739
740
  withPlaywright: false,
740
741
  withEnv: false,
741
742
  vnc: true,
@@ -748,16 +749,21 @@ var BUILT_IN_DEFAULTS = {
748
749
  cpus: 0,
749
750
  pidsLimit: 0,
750
751
  disk: "",
751
- bundleDepth: void 0
752
+ bundleDepth: void 0,
753
+ vercelVcpus: 2,
754
+ vercelTimeoutMs: 27e5,
755
+ vercelNetworkPolicy: ""
752
756
  },
753
757
  checkpoint: {
754
758
  maxLayers: 3
755
759
  },
756
760
  claude: {
757
- sessionName: "claude"
761
+ sessionName: "claude",
762
+ dangerouslySkipPermissions: true
758
763
  },
759
764
  codex: {
760
- sessionName: "codex"
765
+ sessionName: "codex",
766
+ dangerouslySkipPermissions: true
761
767
  },
762
768
  opencode: {
763
769
  sessionName: "opencode"
@@ -799,7 +805,12 @@ var BUILT_IN_DEFAULTS = {
799
805
  },
800
806
  queue: {
801
807
  enabled: true,
802
- maxConcurrent: 5
808
+ maxConcurrent: 5,
809
+ maxWorking: 0,
810
+ idleGraceSeconds: 15
811
+ },
812
+ cloud: {
813
+ useCurrentBranch: false
803
814
  },
804
815
  maintenance: {
805
816
  pruneProjectConfigs: true,
@@ -810,8 +821,8 @@ var KEY_REGISTRY = [
810
821
  {
811
822
  key: "box.provider",
812
823
  type: "enum",
813
- enumValues: ["docker", "daytona", "hetzner"],
814
- description: "Sandbox backend new boxes are created on: local Docker containers, Daytona Cloud sandboxes, or Hetzner Cloud VPSes."
824
+ enumValues: ["docker", "daytona", "hetzner", "vercel"],
825
+ description: "Sandbox backend new boxes are created on: local Docker containers, Daytona Cloud sandboxes, Hetzner Cloud VPSes, or Vercel Sandboxes."
815
826
  },
816
827
  {
817
828
  key: "box.hostSnapshot",
@@ -841,6 +852,12 @@ var KEY_REGISTRY = [
841
852
  description: "Per-provider override of `box.defaultCheckpoint` for hetzner. Wins over the global when set; set via `agentbox checkpoint set-default --provider hetzner`.",
842
853
  advanced: true
843
854
  },
855
+ {
856
+ key: "box.defaultCheckpointVercel",
857
+ type: "string",
858
+ description: "Per-provider override of `box.defaultCheckpoint` for vercel. Wins over the global when set; set via `agentbox checkpoint set-default --provider vercel`.",
859
+ advanced: true
860
+ },
844
861
  {
845
862
  key: "checkpoint.maxLayers",
846
863
  type: "int",
@@ -914,16 +931,41 @@ var KEY_REGISTRY = [
914
931
  type: "int",
915
932
  description: "Cap git bundle history shipped to cloud sandboxes (daytona, hetzner). 0 = full history. Unset = adaptive default (last 200 commits; re-bundle at 100 if the bundle exceeds 20 MB). Ignored for docker (which bind-mounts .git/)."
916
933
  },
934
+ {
935
+ key: "box.vercelVcpus",
936
+ type: "int",
937
+ description: "vCPUs for new --provider vercel boxes (Vercel couples RAM at 2048 MB/vCPU). Default 2. Vercel only accepts specific counts (e.g. 1, 2, 4, 8) \u2014 an unsupported value fails create with a 400. Vercel-only; ignored by other providers."
938
+ },
939
+ {
940
+ key: "box.vercelTimeoutMs",
941
+ type: "int",
942
+ description: "Max session length (ms) for new --provider vercel boxes before the VM auto-snapshots; persistent mode auto-resumes on the next call. Default 2700000 (45 min, the Hobby ceiling). Vercel-only."
943
+ },
944
+ {
945
+ key: "box.vercelNetworkPolicy",
946
+ type: "string",
947
+ description: "Egress lock for new --provider vercel boxes: 'allow-all' (default, unset), 'deny-all', or a comma-separated domain allowlist (e.g. 'github.com,*.npmjs.org') that denies everything else. Vercel-only; ignored by other providers."
948
+ },
917
949
  {
918
950
  key: "claude.sessionName",
919
951
  type: "string",
920
952
  description: "tmux session name for `agentbox claude`."
921
953
  },
954
+ {
955
+ key: "claude.dangerouslySkipPermissions",
956
+ type: "bool",
957
+ description: "Launch claude in new boxes with --dangerously-skip-permissions (auto-accept tool use). Safe because boxes are isolated; on by default. Override per-box with --no-dangerously-skip-permissions."
958
+ },
922
959
  {
923
960
  key: "codex.sessionName",
924
961
  type: "string",
925
962
  description: "tmux session name for `agentbox codex`."
926
963
  },
964
+ {
965
+ key: "codex.dangerouslySkipPermissions",
966
+ type: "bool",
967
+ description: "Launch codex in new boxes with --dangerously-bypass-approvals-and-sandbox (never prompt for approval). Safe because boxes are isolated; on by default. Override per-box with --no-dangerously-skip-permissions."
968
+ },
927
969
  {
928
970
  key: "opencode.sessionName",
929
971
  type: "string",
@@ -1031,6 +1073,21 @@ var KEY_REGISTRY = [
1031
1073
  type: "int",
1032
1074
  description: "Max number of simultaneously-running boxes (across providers) before background `-i` jobs queue up instead of starting immediately. Per-invocation override: `--max-running <n>`."
1033
1075
  },
1076
+ {
1077
+ key: "queue.maxWorking",
1078
+ type: "int",
1079
+ description: "Max agents actively working/thinking (quota-consuming) at once before background `-i` jobs queue. 0 = disabled (use the queue.maxConcurrent running-box gate). Counts all boxes, foreground + queued. Per-invocation override: `--max-working <n>`."
1080
+ },
1081
+ {
1082
+ key: "queue.idleGraceSeconds",
1083
+ type: "int",
1084
+ description: "Seconds an agent must stay non-working before it frees its working slot (debounce against brief idle flaps between turns). Only used when queue.maxWorking > 0."
1085
+ },
1086
+ {
1087
+ key: "cloud.useCurrentBranch",
1088
+ type: "bool",
1089
+ description: "On cloud providers (daytona/hetzner), start new boxes on the host's current branch instead of forking a new agentbox/<box-name> branch. Overridden by an explicit --use-branch / --from-branch."
1090
+ },
1034
1091
  {
1035
1092
  key: "maintenance.pruneProjectConfigs",
1036
1093
  type: "bool",
@@ -1386,7 +1443,7 @@ function writeLeaf(obj, branch, leaf, value) {
1386
1443
  b[leaf] = value;
1387
1444
  }
1388
1445
  function resolveDefaultCheckpoint(cfg, provider) {
1389
- const perProvider = provider === "daytona" ? cfg.box.defaultCheckpointDaytona : provider === "hetzner" ? cfg.box.defaultCheckpointHetzner : cfg.box.defaultCheckpointDocker;
1446
+ const perProvider = provider === "daytona" ? cfg.box.defaultCheckpointDaytona : provider === "hetzner" ? cfg.box.defaultCheckpointHetzner : provider === "vercel" ? cfg.box.defaultCheckpointVercel : cfg.box.defaultCheckpointDocker;
1390
1447
  if (perProvider && perProvider.length > 0) return perProvider;
1391
1448
  return cfg.box.defaultCheckpoint;
1392
1449
  }
@@ -1394,6 +1451,7 @@ function defaultCheckpointConfigKey(provider) {
1394
1451
  if (provider === "docker") return "box.defaultCheckpointDocker";
1395
1452
  if (provider === "daytona") return "box.defaultCheckpointDaytona";
1396
1453
  if (provider === "hetzner") return "box.defaultCheckpointHetzner";
1454
+ if (provider === "vercel") return "box.defaultCheckpointVercel";
1397
1455
  return "box.defaultCheckpoint";
1398
1456
  }
1399
1457
  async function setConfigValue(scope, key, value, cwd, opts = {}) {
@@ -1591,7 +1649,7 @@ async function touchProjectMeta(absPath) {
1591
1649
 
1592
1650
  // ../../packages/sandbox-docker/dist/index.js
1593
1651
  import { chmod, mkdir as mkdir32, readFile as readFile32 } from "fs/promises";
1594
- import { join as join32 } from "path";
1652
+ import { basename as basename2, join as join32 } from "path";
1595
1653
  import { execa as execa4 } from "execa";
1596
1654
  import { spawnSync as spawnSync2 } from "child_process";
1597
1655
  import { stat as stat22 } from "fs/promises";
@@ -1616,7 +1674,7 @@ import { homedir as homedir6, platform } from "os";
1616
1674
  import { join as join7, resolve as resolve2 } from "path";
1617
1675
  import { mkdir as mkdir5, mkdtemp as mkdtemp2, readFile as readFile5, readdir as readdir32, rm as rm3, writeFile as writeFile22 } from "fs/promises";
1618
1676
  import { homedir as homedir7, tmpdir as tmpdir2 } from "os";
1619
- import { basename as basename3, join as join8 } from "path";
1677
+ import { basename as basename22, join as join8 } from "path";
1620
1678
  import { execa as execa10 } from "execa";
1621
1679
  import { stat as stat5 } from "fs/promises";
1622
1680
  import { execa as execa11 } from "execa";
@@ -1624,10 +1682,11 @@ import { execa as execa12 } from "execa";
1624
1682
  import { spawn } from "child_process";
1625
1683
  import { randomBytes as randomBytes22 } from "crypto";
1626
1684
  import { existsSync as existsSync2, openSync } from "fs";
1627
- import { mkdir as mkdir6, readFile as readFile6, unlink as unlink2, writeFile as writeFile32 } from "fs/promises";
1685
+ import { cp, mkdir as mkdir6, readFile as readFile6, readdir as readdir4, rename as rename3, rm as rm4, unlink as unlink2, writeFile as writeFile32 } from "fs/promises";
1628
1686
  import { request as httpRequest } from "http";
1687
+ import { createRequire } from "module";
1629
1688
  import { homedir as homedir8 } from "os";
1630
- import { dirname as dirname3, join as join9, resolve as resolve22 } from "path";
1689
+ import { dirname as dirname3, join as join9, resolve as resolve22, sep } from "path";
1631
1690
  import { setTimeout as delay2 } from "timers/promises";
1632
1691
  import { fileURLToPath } from "url";
1633
1692
 
@@ -1676,6 +1735,15 @@ var GH_PR_OPS = [
1676
1735
  "close",
1677
1736
  "reopen"
1678
1737
  ];
1738
+ function injectPrCreateHead(op, branch, args) {
1739
+ if (op !== "create") return args;
1740
+ if (!branch || branch === "HEAD") return args;
1741
+ if (hasHeadArg(args)) return args;
1742
+ return ["--head", branch, ...args];
1743
+ }
1744
+ function hasHeadArg(args) {
1745
+ return args.some((a) => a === "--head" || a.startsWith("--head=") || a.startsWith("-H"));
1746
+ }
1679
1747
  var MAX_BODY_BYTES = 1024 * 1024;
1680
1748
  var QUEUE_DIR = join3(STATE_DIR, "queue");
1681
1749
  async function loadQueueConfig() {
@@ -1688,7 +1756,9 @@ async function loadQueueConfig() {
1688
1756
  const q = global.queue ?? {};
1689
1757
  return {
1690
1758
  enabled: q.enabled ?? d.enabled,
1691
- maxConcurrent: q.maxConcurrent ?? d.maxConcurrent
1759
+ maxConcurrent: q.maxConcurrent ?? d.maxConcurrent,
1760
+ maxWorking: q.maxWorking ?? d.maxWorking,
1761
+ idleGraceMs: (q.idleGraceSeconds ?? d.idleGraceSeconds) * 1e3
1692
1762
  };
1693
1763
  }
1694
1764
  async function writeJob(job) {
@@ -1807,7 +1877,7 @@ function queueLogPath(id) {
1807
1877
 
1808
1878
  // ../../packages/sandbox-docker/dist/index.js
1809
1879
  import { execa as execa15 } from "execa";
1810
- import { readdir as readdir4, rm as rm4, stat as stat7 } from "fs/promises";
1880
+ import { readdir as readdir5, rm as rm5, stat as stat7 } from "fs/promises";
1811
1881
  import { join as join12 } from "path";
1812
1882
 
1813
1883
  // ../../packages/core/dist/index.js
@@ -1868,11 +1938,11 @@ import { homedir as homedir10 } from "os";
1868
1938
  import { join as join13 } from "path";
1869
1939
  import { execa as execa16 } from "execa";
1870
1940
  import { existsSync as existsSync3, mkdirSync, renameSync, statSync } from "fs";
1871
- import { basename as basename32, dirname as dirname22, posix, resolve as resolve4 } from "path";
1941
+ import { basename as basename4, dirname as dirname22, posix, resolve as resolve4 } from "path";
1872
1942
  import { execa as execa17 } from "execa";
1873
- import { copyFile, mkdtemp as mkdtemp3, readdir as readdir5, readFile as readFile7, rm as rm5, stat as stat8, writeFile as writeFile4 } from "fs/promises";
1943
+ import { copyFile, mkdtemp as mkdtemp3, readdir as readdir6, readFile as readFile7, rm as rm6, stat as stat8, writeFile as writeFile4 } from "fs/promises";
1874
1944
  import { homedir as homedir11, tmpdir as tmpdir3 } from "os";
1875
- import { basename as basename4, join as join14, relative as relative2 } from "path";
1945
+ import { basename as basename5, join as join14, relative as relative2 } from "path";
1876
1946
  import { execa as execa18 } from "execa";
1877
1947
  function isHostPathHookCommand(command, hostHome) {
1878
1948
  if (typeof command !== "string" || command.length === 0) return false;
@@ -2671,13 +2741,13 @@ async function copyOneEntry(container, entry) {
2671
2741
  throw new Error(`mkdir -p ${parentDir} failed: ${String(mkdir8.stderr).slice(0, 300)}`);
2672
2742
  }
2673
2743
  if (entry.kind === "file") {
2674
- const cp = await execa22(
2744
+ const cp2 = await execa22(
2675
2745
  "docker",
2676
2746
  ["cp", entry.absSrc, `${container}:${boxDest}`],
2677
2747
  { reject: false }
2678
2748
  );
2679
- if (cp.exitCode !== 0) {
2680
- throw new Error(`docker cp failed: ${String(cp.stderr).slice(0, 300)}`);
2749
+ if (cp2.exitCode !== 0) {
2750
+ throw new Error(`docker cp failed: ${String(cp2.stderr).slice(0, 300)}`);
2681
2751
  }
2682
2752
  } else {
2683
2753
  const packed = await execa22(
@@ -3671,6 +3741,70 @@ async function pullClaudeExtras(spec, opts) {
3671
3741
  return { newItems, mergedRegistries };
3672
3742
  }
3673
3743
  var CREDENTIALS_BACKUP_FILE = join32(STATE_DIR, "claude-credentials.json");
3744
+ var CODEX_CREDENTIALS_BACKUP_FILE = join32(STATE_DIR, "codex-credentials.json");
3745
+ var OPENCODE_CREDENTIALS_BACKUP_FILE = join32(STATE_DIR, "opencode-credentials.json");
3746
+ function isRealAgentCredential(agent, text) {
3747
+ let parsed;
3748
+ try {
3749
+ parsed = JSON.parse(text);
3750
+ } catch {
3751
+ return false;
3752
+ }
3753
+ if (typeof parsed !== "object" || parsed === null) return false;
3754
+ if (agent === "claude") {
3755
+ const rt = parsed.claudeAiOauth?.refreshToken;
3756
+ return typeof rt === "string" && rt.length > 0;
3757
+ }
3758
+ return Object.keys(parsed).length > 0;
3759
+ }
3760
+ async function hostClaudeBackupExpired(path = CREDENTIALS_BACKUP_FILE, now = Date.now()) {
3761
+ try {
3762
+ const parsed = JSON.parse(await readFile32(path, "utf8"));
3763
+ const exp = parsed?.claudeAiOauth?.expiresAt;
3764
+ return typeof exp === "number" && Number.isFinite(exp) && exp < now;
3765
+ } catch {
3766
+ return false;
3767
+ }
3768
+ }
3769
+ function parseExtractResult(stdout) {
3770
+ return { copied: /\bCOPIED=yes\b/.test(stdout) };
3771
+ }
3772
+ async function extractVolumeAuthToBackup(opts) {
3773
+ try {
3774
+ await mkdir32(STATE_DIR, { recursive: true });
3775
+ const script = 'COPIED=no; if [ -s /dst/auth.json ]; then cp -a /dst/auth.json "/host-state/$DEST" && COPIED=yes; fi; echo "COPIED=$COPIED"';
3776
+ const { stdout } = await execa4("docker", [
3777
+ "run",
3778
+ "--rm",
3779
+ "--user",
3780
+ "0",
3781
+ "-v",
3782
+ `${opts.volume}:/dst`,
3783
+ "-v",
3784
+ `${STATE_DIR}:/host-state`,
3785
+ "-e",
3786
+ // Pass the destination filename via env so the path isn't interpolated
3787
+ // into the script string (keeps the docker arg list static + injection-safe).
3788
+ `DEST=${basename2(opts.backupFile)}`,
3789
+ opts.image,
3790
+ "sh",
3791
+ "-c",
3792
+ script
3793
+ ]);
3794
+ const result = parseExtractResult(stdout);
3795
+ if (result.copied) await chmod(opts.backupFile, 384).catch(() => {
3796
+ });
3797
+ return result;
3798
+ } catch {
3799
+ return { copied: false };
3800
+ }
3801
+ }
3802
+ function extractCodexCredentials(volume, image) {
3803
+ return extractVolumeAuthToBackup({ volume, image, backupFile: CODEX_CREDENTIALS_BACKUP_FILE });
3804
+ }
3805
+ function extractOpencodeCredentials(volume, image) {
3806
+ return extractVolumeAuthToBackup({ volume, image, backupFile: OPENCODE_CREDENTIALS_BACKUP_FILE });
3807
+ }
3674
3808
  async function hostBackupHasCredentials(path = CREDENTIALS_BACKUP_FILE) {
3675
3809
  try {
3676
3810
  const parsed = JSON.parse(await readFile32(path, "utf8"));
@@ -4571,23 +4705,10 @@ async function seedWorkspace(opts) {
4571
4705
  const main = r.repo.hostMainRepo;
4572
4706
  const wt = r.gitWorktreePath;
4573
4707
  const baseRef = r.repo.kind === "root" ? opts.fromBranch ?? "HEAD" : "HEAD";
4708
+ const addArgs = r.reuseBranch ? ["worktree", "add", wt, r.branch] : ["worktree", "add", "-b", r.branch, wt, baseRef];
4574
4709
  const add = await execa7(
4575
4710
  "docker",
4576
- [
4577
- "exec",
4578
- "--user",
4579
- "vscode",
4580
- opts.container,
4581
- "git",
4582
- "-C",
4583
- main,
4584
- "worktree",
4585
- "add",
4586
- "-b",
4587
- r.branch,
4588
- wt,
4589
- baseRef
4590
- ],
4711
+ ["exec", "--user", "vscode", opts.container, "git", "-C", main, ...addArgs],
4591
4712
  { reject: false }
4592
4713
  );
4593
4714
  if (add.exitCode !== 0) {
@@ -4938,7 +5059,7 @@ async function createSnapshot(opts) {
4938
5059
  var CHECKPOINTS_ROOT = join8(homedir7(), ".agentbox", "checkpoints");
4939
5060
  var CHECKPOINT_IMAGE_PREFIX = "agentbox-ckpt-";
4940
5061
  function checkpointImageTag(projectRoot, name) {
4941
- const mnemonic = sanitizeMnemonic(basename3(projectRoot));
5062
+ const mnemonic = sanitizeMnemonic(basename22(projectRoot));
4942
5063
  return `${CHECKPOINT_IMAGE_PREFIX}${hashProjectPath(projectRoot)}_${mnemonic}:${name}`;
4943
5064
  }
4944
5065
  function projectCheckpointsDir(projectRoot) {
@@ -5288,6 +5409,7 @@ async function ensureHomeOwnedByVscode(container) {
5288
5409
  var STATE_DIR22 = join9(homedir8(), ".agentbox");
5289
5410
  var PID_FILE = join9(STATE_DIR22, "relay.pid");
5290
5411
  var LOG_FILE = join9(STATE_DIR22, "relay.log");
5412
+ var RELAY_HOME_DIR = join9(STATE_DIR22, "relay");
5291
5413
  var PORT = DEFAULT_RELAY_PORT;
5292
5414
  var ENDPOINT = {
5293
5415
  // host.docker.internal is the Docker Desktop / OrbStack-supplied alias for
@@ -5298,6 +5420,13 @@ var ENDPOINT = {
5298
5420
  hostUrl: `http://127.0.0.1:${String(PORT)}`,
5299
5421
  port: PORT
5300
5422
  };
5423
+ function shouldReclaimForVersion(health, currentVersion) {
5424
+ if (health.cliEntry === false) return true;
5425
+ if (typeof health.version === "string" && health.version.length > 0 && typeof currentVersion === "string" && currentVersion.length > 0 && health.version !== currentVersion) {
5426
+ return true;
5427
+ }
5428
+ return false;
5429
+ }
5301
5430
  async function ensureRelay(opts = {}) {
5302
5431
  const log = opts.onLog ?? (() => {
5303
5432
  });
@@ -5306,25 +5435,80 @@ async function ensureRelay(opts = {}) {
5306
5435
  await removeContainer(RELAY_CONTAINER_NAME);
5307
5436
  log(`removed legacy relay container ${RELAY_CONTAINER_NAME}`);
5308
5437
  }
5309
- if (await pingHealthz(500)) {
5310
- return ENDPOINT;
5311
- }
5312
- const existingPid = await readPidFile();
5313
- if (existingPid !== null && await processAlive(existingPid)) {
5314
- for (let i = 0; i < 10; i++) {
5315
- if (await pingHealthz(300)) return ENDPOINT;
5316
- await delay2(200);
5438
+ const currentVersion = process.env.AGENTBOX_CLI_VERSION;
5439
+ const health = await fetchHealthz(500);
5440
+ if (health !== null) {
5441
+ if (!shouldReclaimForVersion(health, currentVersion)) {
5442
+ return ENDPOINT;
5443
+ }
5444
+ if (health.cliEntry === false) {
5445
+ log("relay is alive but lacks AGENTBOX_CLI_ENTRY (cp/download/checkpoint would fail) \u2014 reclaiming");
5446
+ } else {
5447
+ log(
5448
+ `relay was spawned by agentbox ${health.version ?? "?"} but this CLI is ${currentVersion ?? "?"} \u2014 reclaiming to keep the relay version-consistent`
5449
+ );
5450
+ }
5451
+ await reclaimRelay(health.pid, log);
5452
+ } else {
5453
+ const existingPid = await readPidFile();
5454
+ if (existingPid !== null && await processAlive(existingPid)) {
5455
+ for (let i = 0; i < 10; i++) {
5456
+ if (await pingHealthz(300)) return ENDPOINT;
5457
+ await delay2(200);
5458
+ }
5459
+ log(`relay pid ${String(existingPid)} alive but /healthz unresponsive \u2014 proceeding anyway`);
5460
+ return ENDPOINT;
5461
+ }
5462
+ if (existingPid !== null) {
5463
+ await unlink2(PID_FILE).catch(() => {
5464
+ });
5317
5465
  }
5318
- log(`relay pid ${String(existingPid)} alive but /healthz unresponsive \u2014 proceeding anyway`);
5319
- return ENDPOINT;
5320
5466
  }
5321
- if (existingPid !== null) {
5322
- await unlink2(PID_FILE).catch(() => {
5323
- });
5467
+ const staged = await stageRelayHome(currentVersion ?? "", log).catch(() => null);
5468
+ const relayBin = staged?.relayBin ?? resolveRelayBin();
5469
+ const cliEntry = staged?.cliEntry ?? resolveCliEntry();
5470
+ if (cliEntry === null) {
5471
+ throw new Error(
5472
+ "cannot start the host relay: agentbox CLI entry not found (is the build complete / dist present?). Set AGENTBOX_CLI_ENTRY to override."
5473
+ );
5474
+ }
5475
+ return spawnRelay(relayBin, cliEntry, log);
5476
+ }
5477
+ async function reclaimRelay(reportedPid, log) {
5478
+ const pidFromFile = await readPidFile();
5479
+ const seen = /* @__PURE__ */ new Set();
5480
+ for (const pid of [reportedPid, pidFromFile]) {
5481
+ if (typeof pid !== "number" || pid <= 0 || seen.has(pid)) continue;
5482
+ seen.add(pid);
5483
+ if (!await processAlive(pid)) continue;
5484
+ log(`stopping crippled relay pid ${String(pid)}`);
5485
+ await killPid(pid);
5486
+ }
5487
+ await unlink2(PID_FILE).catch(() => {
5488
+ });
5489
+ if (await pingHealthz(300)) {
5490
+ throw new Error(
5491
+ `a relay is still listening on :${String(PORT)} and could not be stopped (reported pid ${String(reportedPid ?? "unknown")}); kill it manually and retry`
5492
+ );
5493
+ }
5494
+ }
5495
+ async function killPid(pid) {
5496
+ try {
5497
+ process.kill(pid, "SIGTERM");
5498
+ } catch {
5499
+ return;
5500
+ }
5501
+ for (let i = 0; i < 20; i++) {
5502
+ if (!await processAlive(pid)) return;
5503
+ await delay2(100);
5504
+ }
5505
+ try {
5506
+ process.kill(pid, "SIGKILL");
5507
+ } catch {
5324
5508
  }
5325
- const relayBin = resolveRelayBin();
5509
+ }
5510
+ async function spawnRelay(relayBin, cliEntry, log) {
5326
5511
  const logFd = openSync(LOG_FILE, "a");
5327
- const cliEntry = resolveCliEntry();
5328
5512
  const child = spawn(
5329
5513
  process.execPath,
5330
5514
  [relayBin, "serve", "--port", String(PORT), "--host", "0.0.0.0"],
@@ -5333,7 +5517,7 @@ async function ensureRelay(opts = {}) {
5333
5517
  stdio: ["ignore", logFd, logFd],
5334
5518
  env: {
5335
5519
  ...process.env,
5336
- ...cliEntry ? { AGENTBOX_CLI_ENTRY: cliEntry } : {}
5520
+ AGENTBOX_CLI_ENTRY: cliEntry
5337
5521
  }
5338
5522
  }
5339
5523
  );
@@ -5387,6 +5571,80 @@ function resolveCliEntry() {
5387
5571
  }
5388
5572
  return null;
5389
5573
  }
5574
+ async function stageRelayHome(version, log) {
5575
+ if (!version || version === "0.0.0-dev") return null;
5576
+ if (process.env.AGENTBOX_RELAY_BIN || process.env.AGENTBOX_CLI_ENTRY) return null;
5577
+ const cliRoot = findCliRoot(dirname3(fileURLToPath(import.meta.url)));
5578
+ if (cliRoot === null) return null;
5579
+ const homeDir = join9(RELAY_HOME_DIR, version);
5580
+ const stagedEntry = join9(homeDir, "dist", "index.js");
5581
+ const stagedBin = join9(homeDir, "runtime", "relay", "bin.cjs");
5582
+ if (existsSync2(stagedEntry) && existsSync2(stagedBin)) {
5583
+ return { relayBin: stagedBin, cliEntry: stagedEntry };
5584
+ }
5585
+ const nodeModules = resolveDepRoot(join9(cliRoot, "dist", "index.js"));
5586
+ if (nodeModules === null) return null;
5587
+ const tmpDir = `${homeDir}.tmp-${String(process.pid)}`;
5588
+ try {
5589
+ await mkdir6(RELAY_HOME_DIR, { recursive: true });
5590
+ await rm4(tmpDir, { recursive: true, force: true });
5591
+ await mkdir6(tmpDir, { recursive: true });
5592
+ for (const sub of ["dist", "runtime", "share"]) {
5593
+ const src = join9(cliRoot, sub);
5594
+ if (existsSync2(src)) await cp(src, join9(tmpDir, sub), { recursive: true });
5595
+ }
5596
+ await cp(nodeModules, join9(tmpDir, "node_modules"), { recursive: true, dereference: true });
5597
+ await rm4(homeDir, { recursive: true, force: true });
5598
+ await rename3(tmpDir, homeDir);
5599
+ } catch (err) {
5600
+ await rm4(tmpDir, { recursive: true, force: true }).catch(() => {
5601
+ });
5602
+ if (existsSync2(stagedEntry) && existsSync2(stagedBin)) {
5603
+ return { relayBin: stagedBin, cliEntry: stagedEntry };
5604
+ }
5605
+ log(`relay home staging failed (${err instanceof Error ? err.message : String(err)}); using bundle paths`);
5606
+ return null;
5607
+ }
5608
+ log(`staged relay home for ${version} at ${homeDir}`);
5609
+ await gcOldRelayHomes(version).catch(() => {
5610
+ });
5611
+ return { relayBin: stagedBin, cliEntry: stagedEntry };
5612
+ }
5613
+ function findCliRoot(moduleDir) {
5614
+ for (const root of [resolve22(moduleDir, ".."), resolve22(moduleDir, "..", "..")]) {
5615
+ if (existsSync2(join9(root, "dist", "index.js")) && existsSync2(join9(root, "runtime", "relay", "bin.cjs"))) {
5616
+ return root;
5617
+ }
5618
+ }
5619
+ return null;
5620
+ }
5621
+ function resolveDepRoot(fromFile) {
5622
+ try {
5623
+ const req = createRequire(fromFile);
5624
+ const main = req.resolve("commander");
5625
+ if (main.includes(`${sep}.pnpm${sep}`)) return null;
5626
+ const marker = `${sep}node_modules${sep}`;
5627
+ const idx = main.lastIndexOf(marker);
5628
+ if (idx === -1) return null;
5629
+ const nm = main.slice(0, idx + marker.length - 1);
5630
+ return existsSync2(join9(nm, "commander")) ? nm : null;
5631
+ } catch {
5632
+ return null;
5633
+ }
5634
+ }
5635
+ async function gcOldRelayHomes(keepVersion) {
5636
+ let entries;
5637
+ try {
5638
+ entries = await readdir4(RELAY_HOME_DIR);
5639
+ } catch {
5640
+ return;
5641
+ }
5642
+ for (const name of entries) {
5643
+ if (name === keepVersion) continue;
5644
+ await rm4(join9(RELAY_HOME_DIR, name), { recursive: true, force: true }).catch(() => {
5645
+ });
5646
+ }
5647
+ }
5390
5648
  async function stopRelay() {
5391
5649
  const pid = await readPidFile();
5392
5650
  if (pid === null) {
@@ -5425,7 +5683,7 @@ async function getRelayStatus() {
5425
5683
  pidAlive: pidAlive2,
5426
5684
  port: PORT,
5427
5685
  endpoint: ENDPOINT,
5428
- health: health === null ? null : { boxes: health.boxes, events: health.events },
5686
+ health: health === null ? null : { boxes: health.boxes, events: health.events, version: health.version, commit: health.commit },
5429
5687
  pidFile: PID_FILE,
5430
5688
  logFile: LOG_FILE
5431
5689
  };
@@ -5465,7 +5723,15 @@ function fetchHealthz(timeoutMs) {
5465
5723
  try {
5466
5724
  const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
5467
5725
  if (typeof parsed.ok === "boolean" && typeof parsed.boxes === "number" && typeof parsed.events === "number") {
5468
- resolveP({ ok: parsed.ok, boxes: parsed.boxes, events: parsed.events });
5726
+ resolveP({
5727
+ ok: parsed.ok,
5728
+ boxes: parsed.boxes,
5729
+ events: parsed.events,
5730
+ pid: typeof parsed.pid === "number" ? parsed.pid : void 0,
5731
+ cliEntry: typeof parsed.cliEntry === "boolean" ? parsed.cliEntry : void 0,
5732
+ version: typeof parsed.version === "string" && parsed.version.length > 0 ? parsed.version : void 0,
5733
+ commit: typeof parsed.commit === "string" && parsed.commit.length > 0 ? parsed.commit : void 0
5734
+ });
5469
5735
  } else {
5470
5736
  resolveP(null);
5471
5737
  }
@@ -5817,7 +6083,7 @@ function generateBoxId() {
5817
6083
  return randomBytes3(4).toString("hex");
5818
6084
  }
5819
6085
  function sanitizeBasename(workspacePath) {
5820
- const raw = basename2(resolve3(workspacePath));
6086
+ const raw = basename3(resolve3(workspacePath));
5821
6087
  return raw.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-._]+|[-._]+$/g, "").slice(0, 30).replace(/[-._]+$/, "");
5822
6088
  }
5823
6089
  function defaultBoxName(workspacePath, id) {
@@ -5944,9 +6210,33 @@ async function createBox(opts) {
5944
6210
  );
5945
6211
  }
5946
6212
  for (const r of repos) {
6213
+ const containerPath = r.kind === "root" ? "/workspace" : `/workspace/${r.relPathFromWorkspace}`;
6214
+ const reuseBranch = r.kind === "root" && opts.useBranch !== void 0;
6215
+ if (reuseBranch) {
6216
+ const branch2 = opts.useBranch;
6217
+ const gitWorktreePath2 = gitWorktreePathFor(branch2);
6218
+ repoCarryOvers.push({
6219
+ repo: r,
6220
+ containerPath,
6221
+ gitWorktreePath: gitWorktreePath2,
6222
+ branch: branch2,
6223
+ stashSha: null,
6224
+ untrackedNul: "",
6225
+ hostSource: r.hostMainRepo,
6226
+ reuseBranch: true
6227
+ });
6228
+ gitWorktreeRecords.push({
6229
+ kind: r.kind,
6230
+ hostMainRepo: r.hostMainRepo,
6231
+ containerPath,
6232
+ gitWorktreePath: gitWorktreePath2,
6233
+ branch: branch2,
6234
+ relPathFromWorkspace: r.relPathFromWorkspace
6235
+ });
6236
+ continue;
6237
+ }
5947
6238
  const branchBase = r.kind === "root" ? `agentbox/${name}` : `agentbox/${name}--${r.relPathFromWorkspace.replace(/[^A-Za-z0-9._-]+/g, "_")}`;
5948
6239
  const branch = await pickFreshBranch(r.hostMainRepo, branchBase);
5949
- const containerPath = r.kind === "root" ? "/workspace" : `/workspace/${r.relPathFromWorkspace}`;
5950
6240
  const gitWorktreePath = gitWorktreePathFor(branch);
5951
6241
  const carry = await collectRepoCarryOver(r, branch, containerPath, gitWorktreePath);
5952
6242
  repoCarryOvers.push(carry);
@@ -6191,9 +6481,18 @@ async function createBox(opts) {
6191
6481
  });
6192
6482
  log("seeded /workspace from in-container git worktree(s)");
6193
6483
  } catch (err) {
6194
- log(
6195
- `seedWorkspace failed; leaving ${containerName} running so you can inspect it`
6196
- );
6484
+ if (opts.useBranch !== void 0) {
6485
+ log(`seedWorkspace failed for --use-branch ${opts.useBranch}; cleaning up the box`);
6486
+ await execa13("docker", ["rm", "-f", containerName], { reject: false });
6487
+ for (const w of gitWorktreeRecords) {
6488
+ await removeInBoxWorktree({
6489
+ hostMainRepo: w.hostMainRepo,
6490
+ gitWorktreePath: w.gitWorktreePath
6491
+ });
6492
+ }
6493
+ } else {
6494
+ log(`seedWorkspace failed; leaving ${containerName} running so you can inspect it`);
6495
+ }
6197
6496
  throw err;
6198
6497
  }
6199
6498
  } else {
@@ -6625,10 +6924,11 @@ async function listBoxes() {
6625
6924
  };
6626
6925
  return {
6627
6926
  ...b,
6628
- state: "running",
6927
+ state: b.cloud?.lastState ?? "running",
6629
6928
  endpoints: endpoints2,
6630
6929
  claudeActivity: persisted2?.claude.state,
6631
6930
  claudeSessionTitle: persisted2?.claude.sessionTitle,
6931
+ claudeQuestion: persisted2?.claude.state === "question" ? persisted2.claude.question : void 0,
6632
6932
  codexActivity: persisted2?.codex?.state,
6633
6933
  codexSessionTitle: persisted2?.codex?.sessionTitle,
6634
6934
  opencodeSessionTitle: persisted2?.opencode?.sessionTitle,
@@ -6649,6 +6949,7 @@ async function listBoxes() {
6649
6949
  endpoints,
6650
6950
  claudeActivity: persisted?.claude.state,
6651
6951
  claudeSessionTitle: persisted?.claude.sessionTitle,
6952
+ claudeQuestion: persisted?.claude.state === "question" ? persisted.claude.question : void 0,
6652
6953
  codexActivity: persisted?.codex?.state,
6653
6954
  codexSessionTitle: persisted?.codex?.sessionTitle,
6654
6955
  opencodeSessionTitle: persisted?.opencode?.sessionTitle,
@@ -6919,14 +7220,14 @@ async function destroyBox(idOrName, opts = {}) {
6919
7220
  let removedSnapshot = null;
6920
7221
  if (box.snapshotDir && !opts.keepSnapshot) {
6921
7222
  try {
6922
- await rm4(box.snapshotDir, { recursive: true, force: true });
7223
+ await rm5(box.snapshotDir, { recursive: true, force: true });
6923
7224
  removedSnapshot = box.snapshotDir;
6924
7225
  } catch {
6925
7226
  removedSnapshot = null;
6926
7227
  }
6927
7228
  }
6928
7229
  try {
6929
- await rm4(boxRunDirFor(box), { recursive: true, force: true });
7230
+ await rm5(boxRunDirFor(box), { recursive: true, force: true });
6930
7231
  } catch {
6931
7232
  }
6932
7233
  await removeBoxRecord(box.id);
@@ -6934,7 +7235,7 @@ async function destroyBox(idOrName, opts = {}) {
6934
7235
  }
6935
7236
  async function listSnapshotDirs() {
6936
7237
  try {
6937
- const entries = await readdir4(SNAPSHOTS_ROOT, { withFileTypes: true });
7238
+ const entries = await readdir5(SNAPSHOTS_ROOT, { withFileTypes: true });
6938
7239
  return entries.filter((e) => e.isDirectory()).map((e) => join12(SNAPSHOTS_ROOT, e.name));
6939
7240
  } catch {
6940
7241
  return [];
@@ -6942,7 +7243,7 @@ async function listSnapshotDirs() {
6942
7243
  }
6943
7244
  async function listBoxDirs() {
6944
7245
  try {
6945
- const entries = await readdir4(BOXES_ROOT, { withFileTypes: true });
7246
+ const entries = await readdir5(BOXES_ROOT, { withFileTypes: true });
6946
7247
  return entries.filter((e) => e.isDirectory()).map((e) => join12(BOXES_ROOT, e.name));
6947
7248
  } catch {
6948
7249
  return [];
@@ -7038,13 +7339,13 @@ async function pruneBoxes(opts = {}) {
7038
7339
  for (const v of orphanVolumes) await removeVolume(v);
7039
7340
  for (const d of orphanSnapshots) {
7040
7341
  try {
7041
- await rm4(d, { recursive: true, force: true });
7342
+ await rm5(d, { recursive: true, force: true });
7042
7343
  } catch {
7043
7344
  }
7044
7345
  }
7045
7346
  for (const d of orphanBoxDirs) {
7046
7347
  try {
7047
- await rm4(d, { recursive: true, force: true });
7348
+ await rm5(d, { recursive: true, force: true });
7048
7349
  } catch {
7049
7350
  }
7050
7351
  }
@@ -7309,7 +7610,7 @@ function asText(s) {
7309
7610
  async function uploadToBox(box, hostSrc, boxDst) {
7310
7611
  const srcAbs = resolve4(hostSrc);
7311
7612
  if (!existsSync3(srcAbs)) throw new Error(`source not found: ${hostSrc}`);
7312
- const srcBasename = basename32(srcAbs);
7613
+ const srcBasename = basename4(srcAbs);
7313
7614
  const srcParent = dirname22(srcAbs);
7314
7615
  let boxParent;
7315
7616
  let finalName;
@@ -7393,7 +7694,7 @@ async function downloadFromBox(box, boxSrc, hostDst) {
7393
7694
  finalName = srcBasename;
7394
7695
  } else {
7395
7696
  hostParent = dirname22(dstAbs);
7396
- finalName = basename32(dstAbs);
7697
+ finalName = basename4(dstAbs);
7397
7698
  }
7398
7699
  mkdirSync(hostParent, { recursive: true });
7399
7700
  const finalPath = posix.join(hostParent, finalName);
@@ -7427,6 +7728,7 @@ var dockerProvider = {
7427
7728
  useSnapshot: po.useSnapshot ?? false,
7428
7729
  checkpointRef: req.checkpointRef,
7429
7730
  fromBranch: req.fromBranch,
7731
+ useBranch: req.useBranch,
7430
7732
  image: req.image,
7431
7733
  onLog: req.onLog,
7432
7734
  claudeConfig: po.claudeConfig,
@@ -7561,7 +7863,7 @@ async function findBrokenSymlinks2(root) {
7561
7863
  async function walk(dir) {
7562
7864
  let entries;
7563
7865
  try {
7564
- entries = await readdir5(dir, { withFileTypes: true });
7866
+ entries = await readdir6(dir, { withFileTypes: true });
7565
7867
  } catch {
7566
7868
  return;
7567
7869
  }
@@ -7589,7 +7891,7 @@ function emptyResult(warnings = []) {
7589
7891
  }, warnings };
7590
7892
  }
7591
7893
  async function tarballFromDir(stageDir, agent) {
7592
- const tarballPath = join14(tmpdir3(), `agentbox-${agent}-${basename4(stageDir)}.tar.gz`);
7894
+ const tarballPath = join14(tmpdir3(), `agentbox-${agent}-${basename5(stageDir)}.tar.gz`);
7593
7895
  await execa18("tar", ["-czf", tarballPath, "-C", stageDir, "."], {
7594
7896
  env: { ...process.env, COPYFILE_DISABLE: "1" }
7595
7897
  });
@@ -7598,7 +7900,7 @@ async function tarballFromDir(stageDir, agent) {
7598
7900
  function makeCleanup(paths) {
7599
7901
  return async () => {
7600
7902
  for (const p of paths) {
7601
- await rm5(p, { recursive: true, force: true });
7903
+ await rm6(p, { recursive: true, force: true });
7602
7904
  }
7603
7905
  };
7604
7906
  }
@@ -7614,8 +7916,8 @@ async function stageSingleFileTarball(agent, sourcePath, tarballEntryName) {
7614
7916
  warnings: []
7615
7917
  };
7616
7918
  } catch (err) {
7617
- await rm5(stageDir, { recursive: true, force: true });
7618
- if (tarballPath) await rm5(tarballPath, { force: true });
7919
+ await rm6(stageDir, { recursive: true, force: true });
7920
+ if (tarballPath) await rm6(tarballPath, { force: true });
7619
7921
  throw err;
7620
7922
  }
7621
7923
  }
@@ -7698,7 +8000,7 @@ async function stageClaudeStaticForUpload(opts = {}) {
7698
8000
  const pluginsDir = join14(stageDir, "plugins");
7699
8001
  if (await pathExists7(pluginsDir)) {
7700
8002
  try {
7701
- const entries = await readdir5(pluginsDir, { withFileTypes: true });
8003
+ const entries = await readdir6(pluginsDir, { withFileTypes: true });
7702
8004
  for (const ent of entries) {
7703
8005
  if (!ent.isFile() || !ent.name.endsWith(".json")) continue;
7704
8006
  const file = join14(pluginsDir, ent.name);
@@ -7716,8 +8018,8 @@ async function stageClaudeStaticForUpload(opts = {}) {
7716
8018
  warnings: []
7717
8019
  };
7718
8020
  } catch (err) {
7719
- await rm5(stageDir, { recursive: true, force: true });
7720
- if (tarballPath) await rm5(tarballPath, { force: true });
8021
+ await rm6(stageDir, { recursive: true, force: true });
8022
+ if (tarballPath) await rm6(tarballPath, { force: true });
7721
8023
  throw err;
7722
8024
  }
7723
8025
  }
@@ -7777,13 +8079,17 @@ async function stageCodexStaticForUpload(opts = {}) {
7777
8079
  warnings: []
7778
8080
  };
7779
8081
  } catch (err) {
7780
- await rm5(stageDir, { recursive: true, force: true });
7781
- if (tarballPath) await rm5(tarballPath, { force: true });
8082
+ await rm6(stageDir, { recursive: true, force: true });
8083
+ if (tarballPath) await rm6(tarballPath, { force: true });
7782
8084
  throw err;
7783
8085
  }
7784
8086
  }
7785
8087
  async function stageCodexCredentialsForUpload(opts = {}) {
7786
8088
  const hostHome = opts.hostHome ?? homedir11();
8089
+ const cloudBackup = join14(hostHome, ".agentbox", "codex-credentials.json");
8090
+ if (await pathExists7(cloudBackup)) {
8091
+ return stageSingleFileTarball("codex-creds", cloudBackup, "auth.json");
8092
+ }
7787
8093
  const hostAuth = join14(hostHome, ".codex", "auth.json");
7788
8094
  if (!await pathExists7(hostAuth)) return emptyResult([CODEX_KEYCHAIN_WARNING]);
7789
8095
  return stageSingleFileTarball("codex-creds", hostAuth, "auth.json");
@@ -7841,13 +8147,17 @@ async function stageOpencodeStaticForUpload(opts = {}) {
7841
8147
  warnings: []
7842
8148
  };
7843
8149
  } catch (err) {
7844
- await rm5(stageDir, { recursive: true, force: true });
7845
- if (tarballPath) await rm5(tarballPath, { force: true });
8150
+ await rm6(stageDir, { recursive: true, force: true });
8151
+ if (tarballPath) await rm6(tarballPath, { force: true });
7846
8152
  throw err;
7847
8153
  }
7848
8154
  }
7849
8155
  async function stageOpencodeCredentialsForUpload(opts = {}) {
7850
8156
  const hostHome = opts.hostHome ?? homedir11();
8157
+ const cloudBackup = join14(hostHome, ".agentbox", "opencode-credentials.json");
8158
+ if (await pathExists7(cloudBackup)) {
8159
+ return stageSingleFileTarball("opencode-creds", cloudBackup, "auth.json");
8160
+ }
7851
8161
  const hostAuth = join14(hostHome, ".local", "share", "opencode", "auth.json");
7852
8162
  if (!await pathExists7(hostAuth)) return emptyResult();
7853
8163
  return stageSingleFileTarball("opencode-creds", hostAuth, "auth.json");
@@ -7908,6 +8218,7 @@ export {
7908
8218
  RELAY_IMAGE_REF,
7909
8219
  hashRpcParams,
7910
8220
  GH_PR_OPS,
8221
+ injectPrCreateHead,
7911
8222
  loadQueueConfig,
7912
8223
  writeJob,
7913
8224
  readJob,
@@ -7966,6 +8277,14 @@ export {
7966
8277
  claudeSessionInfo,
7967
8278
  pullClaudeExtras,
7968
8279
  CREDENTIALS_BACKUP_FILE,
8280
+ CODEX_CREDENTIALS_BACKUP_FILE,
8281
+ OPENCODE_CREDENTIALS_BACKUP_FILE,
8282
+ isRealAgentCredential,
8283
+ hostClaudeBackupExpired,
8284
+ parseExtractResult,
8285
+ extractVolumeAuthToBackup,
8286
+ extractCodexCredentials,
8287
+ extractOpencodeCredentials,
7969
8288
  hostBackupHasCredentials,
7970
8289
  parseSyncResult,
7971
8290
  syncClaudeCredentials,
@@ -8111,4 +8430,4 @@ export {
8111
8430
  browserSessionActive,
8112
8431
  ensureBoxBrowser
8113
8432
  };
8114
- //# sourceMappingURL=chunk-6OZDFNBF.js.map
8433
+ //# sourceMappingURL=chunk-7UIAO7PC.js.map