@madarco/agentbox 0.12.0 → 0.14.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 (74) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +21 -7
  3. package/dist/{_cloud-attach-XKO4SHR3.js → _cloud-attach-GUBB5RH2.js} +4 -4
  4. package/dist/{chunk-R5XIDQFR.js → chunk-BKU34KYY.js} +170 -6
  5. package/dist/chunk-BKU34KYY.js.map +1 -0
  6. package/dist/{chunk-HFV6THYG.js → chunk-BYCLD6D6.js} +308 -36
  7. package/dist/chunk-BYCLD6D6.js.map +1 -0
  8. package/dist/chunk-LDMYHWUS.js +346 -0
  9. package/dist/chunk-LDMYHWUS.js.map +1 -0
  10. package/dist/{chunk-2LF5YILI.js → chunk-RSKG7AFU.js} +80 -6
  11. package/dist/chunk-RSKG7AFU.js.map +1 -0
  12. package/dist/{chunk-DHJ7OMIP.js → chunk-TBSIJVSN.js} +149 -47
  13. package/dist/chunk-TBSIJVSN.js.map +1 -0
  14. package/dist/{chunk-IZXPJPPV.js → chunk-TCS5HXJX.js} +389 -176
  15. package/dist/chunk-TCS5HXJX.js.map +1 -0
  16. package/dist/{chunk-ECLLV5JH.js → chunk-VATTS2MR.js} +156 -5
  17. package/dist/chunk-VATTS2MR.js.map +1 -0
  18. package/dist/{chunk-SNTHHWKY.js → chunk-XKH7NTT7.js} +80 -22
  19. package/dist/chunk-XKH7NTT7.js.map +1 -0
  20. package/dist/dist-34RKQ74M.js +662 -0
  21. package/dist/dist-34RKQ74M.js.map +1 -0
  22. package/dist/{dist-47LVLYUV.js → dist-3IMQNTTV.js} +14 -69
  23. package/dist/dist-3IMQNTTV.js.map +1 -0
  24. package/dist/{dist-RZZSSUNB.js → dist-4DPOL5A7.js} +5 -3
  25. package/dist/{dist-24PY2ZMO.js → dist-57M6ZA7H.js} +25 -177
  26. package/dist/dist-57M6ZA7H.js.map +1 -0
  27. package/dist/{dist-SWUOU34W.js → dist-J2IHD5T7.js} +37 -226
  28. package/dist/dist-J2IHD5T7.js.map +1 -0
  29. package/dist/index.js +1524 -921
  30. package/dist/index.js.map +1 -1
  31. package/dist/{prepared-state-MQHD3M5F-KE4DT3GX.js → prepared-state-MQHD3M5F-Q27AZU53.js} +2 -2
  32. package/package.json +9 -7
  33. package/runtime/docker/Dockerfile.box +21 -26
  34. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +37 -1
  35. package/runtime/docker/packages/ctl/dist/bin.cjs +46 -17
  36. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +17 -6
  37. package/runtime/docker/packages/sandbox-docker/scripts/chromium-resolver +57 -0
  38. package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +2 -1
  39. package/runtime/e2b/agentbox-checkpoint-cleanup +52 -0
  40. package/runtime/e2b/agentbox-codex-hooks.json +68 -0
  41. package/runtime/e2b/agentbox-open +28 -0
  42. package/runtime/e2b/agentbox-setup-skill.md +233 -0
  43. package/runtime/e2b/agentbox-vnc-start +102 -0
  44. package/runtime/e2b/attach-helper.cjs +167 -0
  45. package/runtime/e2b/claude-managed-settings.json +116 -0
  46. package/runtime/e2b/ctl.cjs +23864 -0
  47. package/runtime/e2b/custom-system-CLAUDE.md +46 -0
  48. package/runtime/e2b/gh-shim +344 -0
  49. package/runtime/e2b/git-shim +131 -0
  50. package/runtime/e2b/scripts/build-template.sh +295 -0
  51. package/runtime/hetzner/agentbox-setup-skill.md +37 -1
  52. package/runtime/hetzner/agentbox-vnc-start +17 -6
  53. package/runtime/hetzner/claude-managed-settings.json +2 -1
  54. package/runtime/hetzner/ctl.cjs +46 -17
  55. package/runtime/relay/bin.cjs +305 -230
  56. package/runtime/vercel/agentbox-setup-skill.md +37 -1
  57. package/runtime/vercel/agentbox-vnc-start +17 -6
  58. package/runtime/vercel/claude-managed-settings.json +2 -1
  59. package/runtime/vercel/ctl.cjs +46 -17
  60. package/share/agentbox-setup/SKILL.md +37 -1
  61. package/share/host-skills/agentbox-info/SKILL.md +26 -34
  62. package/dist/chunk-2LF5YILI.js.map +0 -1
  63. package/dist/chunk-DHJ7OMIP.js.map +0 -1
  64. package/dist/chunk-ECLLV5JH.js.map +0 -1
  65. package/dist/chunk-HFV6THYG.js.map +0 -1
  66. package/dist/chunk-IZXPJPPV.js.map +0 -1
  67. package/dist/chunk-R5XIDQFR.js.map +0 -1
  68. package/dist/chunk-SNTHHWKY.js.map +0 -1
  69. package/dist/dist-24PY2ZMO.js.map +0 -1
  70. package/dist/dist-47LVLYUV.js.map +0 -1
  71. package/dist/dist-SWUOU34W.js.map +0 -1
  72. /package/dist/{_cloud-attach-XKO4SHR3.js.map → _cloud-attach-GUBB5RH2.js.map} +0 -0
  73. /package/dist/{dist-RZZSSUNB.js.map → dist-4DPOL5A7.js.map} +0 -0
  74. /package/dist/{prepared-state-MQHD3M5F-KE4DT3GX.js.map → prepared-state-MQHD3M5F-Q27AZU53.js.map} +0 -0
@@ -4,7 +4,6 @@ import {
4
4
  GitWorktreeError,
5
5
  STATE_DIR,
6
6
  STATE_FILE,
7
- allocateProjectIndex,
8
7
  computeDockerContextFingerprint,
9
8
  detectGitRepos,
10
9
  ensureImage,
@@ -18,13 +17,14 @@ import {
18
17
  readPreparedDockerState,
19
18
  readState,
20
19
  recordBox,
21
- removeBoxRecord
22
- } from "./chunk-SNTHHWKY.js";
20
+ removeBoxRecord,
21
+ reserveProjectIndex
22
+ } from "./chunk-XKH7NTT7.js";
23
23
 
24
24
  // ../../packages/sandbox-docker/dist/index.js
25
25
  import { mkdir as mkdir7, stat as stat8 } from "fs/promises";
26
26
  import { homedir as homedir10 } from "os";
27
- import { basename as basename4, join as join11, resolve as resolve3 } from "path";
27
+ import { basename as basename4, join as join12, resolve as resolve3 } from "path";
28
28
  import { execa as execa14 } from "execa";
29
29
 
30
30
  // ../../packages/ctl/dist/index.js
@@ -704,7 +704,7 @@ async function loadCarrySection(path) {
704
704
 
705
705
  // ../../packages/sandbox-docker/dist/index.js
706
706
  import { spawnSync } from "child_process";
707
- import { mkdir as mkdir3, mkdtemp as mkdtemp2, readdir as readdir3, readFile as readFile4, realpath as realpath2, rm as rm2, stat as stat3, writeFile as writeFile22 } from "fs/promises";
707
+ import { mkdir as mkdir3, mkdtemp as mkdtemp2, readdir as readdir3, readFile as readFile42, realpath as realpath2, rm as rm2, stat as stat3, writeFile as writeFile22 } from "fs/promises";
708
708
  import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
709
709
  import { isAbsolute as isAbsolute2, join as join4, relative as relative2 } from "path";
710
710
  import { setTimeout as delay } from "timers/promises";
@@ -737,11 +737,13 @@ var BUILT_IN_DEFAULTS = {
737
737
  defaultCheckpointDaytona: "",
738
738
  defaultCheckpointHetzner: "",
739
739
  defaultCheckpointVercel: "",
740
+ defaultCheckpointE2b: "",
740
741
  size: "",
741
742
  sizeDocker: "",
742
743
  sizeDaytona: "",
743
744
  sizeHetzner: "",
744
745
  sizeVercel: "",
746
+ sizeE2b: "",
745
747
  withPlaywright: false,
746
748
  withEnv: false,
747
749
  resyncOnStart: true,
@@ -754,6 +756,7 @@ var BUILT_IN_DEFAULTS = {
754
756
  imageDaytona: "",
755
757
  imageHetzner: "",
756
758
  imageVercel: "",
759
+ imageE2b: "",
757
760
  // Mirrors BOX_IMAGE_REGISTRY in @agentbox/sandbox-docker. Empty disables the
758
761
  // registry pull (always build the docker base image locally).
759
762
  imageRegistry: "ghcr.io/madarco/agentbox/box",
@@ -782,7 +785,8 @@ var BUILT_IN_DEFAULTS = {
782
785
  sessionName: "opencode"
783
786
  },
784
787
  attach: {
785
- openIn: "split"
788
+ openIn: "split",
789
+ cmuxStatus: true
786
790
  },
787
791
  code: {
788
792
  ide: "auto",
@@ -834,8 +838,8 @@ var KEY_REGISTRY = [
834
838
  {
835
839
  key: "box.provider",
836
840
  type: "enum",
837
- enumValues: ["docker", "daytona", "hetzner", "vercel"],
838
- description: "Sandbox backend new boxes are created on: local Docker containers, Daytona Cloud sandboxes, Hetzner Cloud VPSes, or Vercel Sandboxes."
841
+ enumValues: ["docker", "daytona", "hetzner", "vercel", "e2b"],
842
+ description: "Sandbox backend new boxes are created on: local Docker containers, Daytona Cloud sandboxes, Hetzner Cloud VPSes, Vercel Sandboxes, or E2B microVMs."
839
843
  },
840
844
  {
841
845
  key: "box.hostSnapshot",
@@ -871,6 +875,12 @@ var KEY_REGISTRY = [
871
875
  description: "Per-provider override of `box.defaultCheckpoint` for vercel. Wins over the global when set; set via `agentbox checkpoint set-default --provider vercel`.",
872
876
  advanced: true
873
877
  },
878
+ {
879
+ key: "box.defaultCheckpointE2b",
880
+ type: "string",
881
+ description: "Per-provider override of `box.defaultCheckpoint` for e2b. Wins over the global when set; set via `agentbox checkpoint set-default --provider e2b`.",
882
+ advanced: true
883
+ },
874
884
  {
875
885
  key: "box.size",
876
886
  type: "string",
@@ -900,6 +910,12 @@ var KEY_REGISTRY = [
900
910
  description: "Per-provider override of `box.size` for vercel. Reserved \u2014 vercel sizing is controlled via `box.vercelVcpus`.",
901
911
  advanced: true
902
912
  },
913
+ {
914
+ key: "box.sizeE2b",
915
+ type: "string",
916
+ description: "Per-provider override of `box.size` for e2b. Reserved \u2014 e2b sizing is template-level (set at `agentbox prepare --provider e2b` time via --vcpus / --memory).",
917
+ advanced: true
918
+ },
903
919
  {
904
920
  key: "checkpoint.maxLayers",
905
921
  type: "int",
@@ -971,6 +987,12 @@ var KEY_REGISTRY = [
971
987
  description: "Per-provider override of `box.image` for vercel (snapshot id, e.g. `snap_\u2026`). Written by `agentbox prepare --provider vercel`.",
972
988
  advanced: true
973
989
  },
990
+ {
991
+ key: "box.imageE2b",
992
+ type: "string",
993
+ description: "Per-provider override of `box.image` for e2b (template id or `name:tag`, e.g. `agentbox-base:latest`). Written by `agentbox prepare --provider e2b`.",
994
+ advanced: true
995
+ },
974
996
  {
975
997
  key: "box.imageRegistry",
976
998
  type: "string",
@@ -1052,7 +1074,12 @@ var KEY_REGISTRY = [
1052
1074
  key: "attach.openIn",
1053
1075
  type: "enum",
1054
1076
  enumValues: ["split", "window", "tab", "same"],
1055
- description: "Where `agentbox claude|codex|opencode` opens the attached session when run from tmux or iTerm2: `split` (tmux split-window / iTerm2 vertical split, default), `window` (tmux new-window / new iTerm2 window), `tab` (tmux new-window / new iTerm2 tab), or `same` (attach inline in the current terminal). Outside tmux/iTerm2 every value behaves like `same`."
1077
+ description: "Where `agentbox claude|codex|opencode` opens the attached session when run from tmux, cmux, or iTerm2: `split` (tmux split-window / cmux new-split / iTerm2 vertical split, default \u2014 same workspace), `window` (tmux new-window / cmux new-workspace / new iTerm2 window), `tab` (tmux new-window / cmux new-surface tab in the current pane, same workspace / new iTerm2 tab), or `same` (attach inline in the current terminal). Outside tmux/cmux/iTerm2 every value behaves like `same`."
1078
+ },
1079
+ {
1080
+ key: "attach.cmuxStatus",
1081
+ type: "bool",
1082
+ description: "When attached inside cmux, reflect the box agent's live activity on its cmux workspace (colour + description: blue=working, amber=needs input, idle clears; restored on detach) and, when the agent needs input, flag the box's own tab via a cmux notification (tab badge + reorder + desktop notification) so it stands out among sibling tabs. cmux only; no-op in other terminals."
1056
1083
  },
1057
1084
  {
1058
1085
  key: "code.ide",
@@ -1520,7 +1547,7 @@ function writeLeaf(obj, branch, leaf, value) {
1520
1547
  b[leaf] = value;
1521
1548
  }
1522
1549
  function resolveDefaultCheckpoint(cfg, provider) {
1523
- const perProvider = provider === "daytona" ? cfg.box.defaultCheckpointDaytona : provider === "hetzner" ? cfg.box.defaultCheckpointHetzner : provider === "vercel" ? cfg.box.defaultCheckpointVercel : cfg.box.defaultCheckpointDocker;
1550
+ const perProvider = provider === "daytona" ? cfg.box.defaultCheckpointDaytona : provider === "hetzner" ? cfg.box.defaultCheckpointHetzner : provider === "vercel" ? cfg.box.defaultCheckpointVercel : provider === "e2b" ? cfg.box.defaultCheckpointE2b : cfg.box.defaultCheckpointDocker;
1524
1551
  if (perProvider && perProvider.length > 0) return perProvider;
1525
1552
  return cfg.box.defaultCheckpoint;
1526
1553
  }
@@ -1529,15 +1556,16 @@ function defaultCheckpointConfigKey(provider) {
1529
1556
  if (provider === "daytona") return "box.defaultCheckpointDaytona";
1530
1557
  if (provider === "hetzner") return "box.defaultCheckpointHetzner";
1531
1558
  if (provider === "vercel") return "box.defaultCheckpointVercel";
1559
+ if (provider === "e2b") return "box.defaultCheckpointE2b";
1532
1560
  return "box.defaultCheckpoint";
1533
1561
  }
1534
1562
  function resolveBoxSize(cfg, provider) {
1535
- const perProvider = provider === "daytona" ? cfg.box.sizeDaytona : provider === "hetzner" ? cfg.box.sizeHetzner : provider === "vercel" ? cfg.box.sizeVercel : cfg.box.sizeDocker;
1563
+ const perProvider = provider === "daytona" ? cfg.box.sizeDaytona : provider === "hetzner" ? cfg.box.sizeHetzner : provider === "vercel" ? cfg.box.sizeVercel : provider === "e2b" ? cfg.box.sizeE2b : cfg.box.sizeDocker;
1536
1564
  if (perProvider && perProvider.length > 0) return perProvider;
1537
1565
  return cfg.box.size;
1538
1566
  }
1539
1567
  function resolveBoxImage(cfg, provider) {
1540
- const perProvider = provider === "daytona" ? cfg.box.imageDaytona : provider === "hetzner" ? cfg.box.imageHetzner : provider === "vercel" ? cfg.box.imageVercel : cfg.box.imageDocker;
1568
+ const perProvider = provider === "daytona" ? cfg.box.imageDaytona : provider === "hetzner" ? cfg.box.imageHetzner : provider === "vercel" ? cfg.box.imageVercel : provider === "e2b" ? cfg.box.imageE2b : cfg.box.imageDocker;
1541
1569
  if (perProvider && perProvider.length > 0) return perProvider;
1542
1570
  return cfg.box.image;
1543
1571
  }
@@ -1546,6 +1574,7 @@ function boxImageConfigKey(provider) {
1546
1574
  if (provider === "daytona") return "box.imageDaytona";
1547
1575
  if (provider === "hetzner") return "box.imageHetzner";
1548
1576
  if (provider === "vercel") return "box.imageVercel";
1577
+ if (provider === "e2b") return "box.imageE2b";
1549
1578
  return "box.image";
1550
1579
  }
1551
1580
  async function setConfigValue(scope, key, value, cwd, opts = {}) {
@@ -1746,7 +1775,7 @@ import { copyFile, mkdtemp, readdir as readdir22, readFile as readFile32, rm as
1746
1775
  import { homedir as homedir22, tmpdir } from "os";
1747
1776
  import { basename as basename2, join as join32, relative } from "path";
1748
1777
  import { execa as execa4 } from "execa";
1749
- import { chmod, mkdir as mkdir22, readFile as readFile24 } from "fs/promises";
1778
+ import { chmod, mkdir as mkdir22, readFile as readFile23 } from "fs/promises";
1750
1779
  import { basename as basename3, join as join22 } from "path";
1751
1780
  import { execa as execa3 } from "execa";
1752
1781
  import { spawnSync as spawnSync2 } from "child_process";
@@ -1760,11 +1789,14 @@ import { homedir as homedir5 } from "os";
1760
1789
  import { join as join6 } from "path";
1761
1790
  import { execa as execa7 } from "execa";
1762
1791
  import { randomBytes as randomBytes3 } from "crypto";
1792
+ import { createHash as createHash22 } from "crypto";
1793
+ import { readFile as readFile52 } from "fs/promises";
1794
+ import { join as join7 } from "path";
1763
1795
  import { execa as execa8 } from "execa";
1764
1796
  import { existsSync } from "fs";
1765
- import { readFile as readFile52 } from "fs/promises";
1797
+ import { readFile as readFile6 } from "fs/promises";
1766
1798
  import { homedir as homedir6 } from "os";
1767
- import { join as join7 } from "path";
1799
+ import { join as join8 } from "path";
1768
1800
  import { execa as execa9 } from "execa";
1769
1801
 
1770
1802
  // ../../packages/core/dist/index.js
@@ -1799,6 +1831,12 @@ function resolveAgentLauncher(kind) {
1799
1831
  if (kind === "opencode") return opencodeLauncher;
1800
1832
  throw new Error(`unknown agent kind: ${String(kind)}`);
1801
1833
  }
1834
+ var UserFacingError = class extends Error {
1835
+ constructor(message) {
1836
+ super(message);
1837
+ this.name = "UserFacingError";
1838
+ }
1839
+ };
1802
1840
  var BoxNotFoundError = class extends Error {
1803
1841
  constructor(query) {
1804
1842
  super(`no agentbox matches "${query}"`);
@@ -1827,10 +1865,10 @@ function generateBoxId() {
1827
1865
  import { execa as execa10 } from "execa";
1828
1866
  import { mkdir as mkdir42, readdir as readdir42, rm as rm32, stat as stat6 } from "fs/promises";
1829
1867
  import { homedir as homedir7, platform } from "os";
1830
- import { join as join8, resolve as resolve2 } from "path";
1831
- import { mkdir as mkdir5, mkdtemp as mkdtemp3, readFile as readFile6, readdir as readdir5, rm as rm4, writeFile as writeFile32 } from "fs/promises";
1868
+ import { join as join9, resolve as resolve2 } from "path";
1869
+ import { mkdir as mkdir5, mkdtemp as mkdtemp3, readFile as readFile7, readdir as readdir5, rm as rm4, writeFile as writeFile32 } from "fs/promises";
1832
1870
  import { homedir as homedir8, tmpdir as tmpdir3 } from "os";
1833
- import { basename as basename32, join as join9 } from "path";
1871
+ import { basename as basename32, join as join10 } from "path";
1834
1872
  import { execa as execa11 } from "execa";
1835
1873
  import { stat as stat7 } from "fs/promises";
1836
1874
  import { execa as execa12 } from "execa";
@@ -1838,22 +1876,22 @@ import { execa as execa13 } from "execa";
1838
1876
  import { spawn } from "child_process";
1839
1877
  import { randomBytes as randomBytes22 } from "crypto";
1840
1878
  import { existsSync as existsSync2, openSync } from "fs";
1841
- import { cp, mkdir as mkdir6, readFile as readFile7, readdir as readdir6, rename as rename3, rm as rm5, unlink as unlink2, writeFile as writeFile4 } from "fs/promises";
1879
+ import { cp, mkdir as mkdir6, readFile as readFile8, readdir as readdir6, rename as rename3, rm as rm5, unlink as unlink2, writeFile as writeFile4 } from "fs/promises";
1842
1880
  import { request as httpRequest } from "http";
1843
1881
  import { createRequire } from "module";
1844
1882
  import { homedir as homedir9 } from "os";
1845
- import { dirname as dirname3, join as join10, resolve as resolve22, sep } from "path";
1883
+ import { dirname as dirname3, join as join11, resolve as resolve22, sep } from "path";
1846
1884
  import { setTimeout as delay2 } from "timers/promises";
1847
1885
  import { fileURLToPath } from "url";
1848
1886
 
1849
1887
  // ../../packages/relay/dist/index.js
1850
1888
  import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
1851
1889
  import { execa } from "execa";
1852
- import { spawn as spawn4 } from "child_process";
1890
+ import { spawn as spawn3 } from "child_process";
1853
1891
  import {
1854
1892
  mkdir as mkdir2,
1855
1893
  readdir as readdir2,
1856
- readFile as readFile23,
1894
+ readFile as readFile4,
1857
1895
  rename as rename2,
1858
1896
  unlink,
1859
1897
  writeFile as writeFile2
@@ -1915,7 +1953,7 @@ async function loadQueueConfig() {
1915
1953
  const d = BUILT_IN_DEFAULTS.queue;
1916
1954
  let global = {};
1917
1955
  try {
1918
- global = parseUserConfig(await readFile23(GLOBAL_CONFIG_FILE, "utf8"), GLOBAL_CONFIG_FILE);
1956
+ global = parseUserConfig(await readFile4(GLOBAL_CONFIG_FILE, "utf8"), GLOBAL_CONFIG_FILE);
1919
1957
  } catch {
1920
1958
  }
1921
1959
  const q = global.queue ?? {};
@@ -1935,7 +1973,7 @@ async function writeJob(job) {
1935
1973
  }
1936
1974
  async function readJob(id) {
1937
1975
  try {
1938
- const raw = await readFile23(join3(QUEUE_DIR, `${id}.json`), "utf8");
1976
+ const raw = await readFile4(join3(QUEUE_DIR, `${id}.json`), "utf8");
1939
1977
  return JSON.parse(raw);
1940
1978
  } catch (err) {
1941
1979
  if (err.code === "ENOENT") return null;
@@ -1961,7 +1999,7 @@ async function loadQueue() {
1961
1999
  for (const name of entries) {
1962
2000
  if (!name.endsWith(".json")) continue;
1963
2001
  try {
1964
- const raw = await readFile23(join3(QUEUE_DIR, name), "utf8");
2002
+ const raw = await readFile4(join3(QUEUE_DIR, name), "utf8");
1965
2003
  out.push(JSON.parse(raw));
1966
2004
  } catch {
1967
2005
  }
@@ -1969,6 +2007,8 @@ async function loadQueue() {
1969
2007
  out.sort((a, b) => a.createdAt < b.createdAt ? -1 : a.createdAt > b.createdAt ? 1 : 0);
1970
2008
  return out;
1971
2009
  }
2010
+ var TERMINAL_RETENTION_MS = 60 * 60 * 1e3;
2011
+ var SWEEP_INTERVAL_MS = 60 * 1e3;
1972
2012
  function processAlive(pid) {
1973
2013
  try {
1974
2014
  process.kill(pid, 0);
@@ -2037,7 +2077,7 @@ async function uncachedBoxStateCount() {
2037
2077
  }
2038
2078
  function inspectDockerState(containerName) {
2039
2079
  return new Promise((resolveP) => {
2040
- const child = spawn4("docker", ["inspect", "--format", "{{.State.Status}}", containerName], {
2080
+ const child = spawn3("docker", ["inspect", "--format", "{{.State.Status}}", containerName], {
2041
2081
  stdio: ["ignore", "pipe", "pipe"]
2042
2082
  });
2043
2083
  let out = "";
@@ -2072,19 +2112,19 @@ function queueLogPath(id) {
2072
2112
  // ../../packages/sandbox-docker/dist/index.js
2073
2113
  import { execa as execa16 } from "execa";
2074
2114
  import { readdir as readdir7, rm as rm6, stat as stat9 } from "fs/promises";
2075
- import { join as join13 } from "path";
2115
+ import { join as join14 } from "path";
2076
2116
  import { execa as execa15 } from "execa";
2077
- import { join as join12 } from "path";
2117
+ import { join as join13 } from "path";
2078
2118
  import { homedir as homedir11 } from "os";
2079
- import { join as join14 } from "path";
2119
+ import { join as join15 } from "path";
2080
2120
  import { execa as execa17 } from "execa";
2081
2121
  import { existsSync as existsSync3, mkdirSync, renameSync, statSync } from "fs";
2082
2122
  import { basename as basename5, dirname as dirname22, posix, resolve as resolve4 } from "path";
2083
2123
  import { execa as execa18 } from "execa";
2084
- import { createHash as createHash22 } from "crypto";
2085
- import { copyFile as copyFile2, mkdir as mkdir8, mkdtemp as mkdtemp4, readdir as readdir8, readFile as readFile8, rm as rm7, stat as stat10 } from "fs/promises";
2124
+ import { createHash as createHash32 } from "crypto";
2125
+ import { copyFile as copyFile2, mkdir as mkdir8, mkdtemp as mkdtemp4, readdir as readdir8, readFile as readFile9, rm as rm7, stat as stat10 } from "fs/promises";
2086
2126
  import { homedir as homedir12, tmpdir as tmpdir4 } from "os";
2087
- import { basename as basename6, dirname as dirname32, join as join15 } from "path";
2127
+ import { basename as basename6, dirname as dirname32, join as join16 } from "path";
2088
2128
  import { execa as execa19 } from "execa";
2089
2129
  function isHostPathHookCommand(command, hostHome) {
2090
2130
  if (typeof command !== "string" || command.length === 0) return false;
@@ -3015,7 +3055,7 @@ function isRealAgentCredential(agent, text) {
3015
3055
  }
3016
3056
  async function hostClaudeBackupExpired(path = CREDENTIALS_BACKUP_FILE, now = Date.now()) {
3017
3057
  try {
3018
- const parsed = JSON.parse(await readFile24(path, "utf8"));
3058
+ const parsed = JSON.parse(await readFile23(path, "utf8"));
3019
3059
  const exp = parsed?.claudeAiOauth?.expiresAt;
3020
3060
  return typeof exp === "number" && Number.isFinite(exp) && exp < now;
3021
3061
  } catch {
@@ -3063,7 +3103,7 @@ function extractOpencodeCredentials(volume, image) {
3063
3103
  }
3064
3104
  async function hostBackupHasCredentials(path = CREDENTIALS_BACKUP_FILE) {
3065
3105
  try {
3066
- const parsed = JSON.parse(await readFile24(path, "utf8"));
3106
+ const parsed = JSON.parse(await readFile23(path, "utf8"));
3067
3107
  const rt = parsed?.claudeAiOauth?.refreshToken;
3068
3108
  return typeof rt === "string" && rt.length > 0;
3069
3109
  } catch {
@@ -3235,6 +3275,64 @@ async function resolveClaudeMemoryDir(hostWorkspace, hostHome = homedir22()) {
3235
3275
  }
3236
3276
  return memDir;
3237
3277
  }
3278
+ async function buildBoxClaudeJsonFromHost(opts) {
3279
+ const { hostHome, hostWorkspace } = opts;
3280
+ const hostClaudeJson = join32(hostHome, ".claude.json");
3281
+ let working;
3282
+ if (await pathExists(hostClaudeJson)) {
3283
+ try {
3284
+ working = JSON.parse(await readFile32(hostClaudeJson, "utf8"));
3285
+ } catch {
3286
+ working = null;
3287
+ }
3288
+ }
3289
+ if (working === void 0 || working === null) {
3290
+ working = {
3291
+ installMethod: "native",
3292
+ autoUpdates: false,
3293
+ autoUpdatesProtectedForNative: true,
3294
+ // Pre-accept onboarding so the in-box Claude doesn't show the theme
3295
+ // picker on first run. AgentBox installing implies the user has
3296
+ // already used Claude Code on the host.
3297
+ hasCompletedOnboarding: true,
3298
+ projects: { [CLOUD_WORKSPACE]: { hasTrustDialogAccepted: true } }
3299
+ };
3300
+ } else {
3301
+ working = filterHostHooks(working, hostHome).data;
3302
+ working = setInstallMethodNative(working).data;
3303
+ if (hostWorkspace) {
3304
+ working = addProjectAlias(working, hostWorkspace, CLOUD_WORKSPACE).data;
3305
+ }
3306
+ working = trustWorkspace(working, CLOUD_WORKSPACE).data;
3307
+ if (typeof working === "object" && working !== null) {
3308
+ const w = working;
3309
+ if (w["hasCompletedOnboarding"] !== true) w["hasCompletedOnboarding"] = true;
3310
+ }
3311
+ }
3312
+ return working;
3313
+ }
3314
+ async function stageClaudeJsonOnlyForUpload(opts = {}) {
3315
+ const hostHome = opts.hostHome ?? homedir22();
3316
+ const stageDir = await mkStageDir("claude-json-only");
3317
+ let tarballPath = null;
3318
+ try {
3319
+ const claudeJson = await buildBoxClaudeJsonFromHost({
3320
+ hostHome,
3321
+ hostWorkspace: opts.hostWorkspace
3322
+ });
3323
+ await writeFile3(join32(stageDir, "_claude.json"), JSON.stringify(claudeJson, null, 2));
3324
+ tarballPath = await tarballFromDir(stageDir, "claude-json-only");
3325
+ return {
3326
+ tarballPath,
3327
+ cleanup: makeCleanup([stageDir, tarballPath]),
3328
+ warnings: []
3329
+ };
3330
+ } catch (err) {
3331
+ await rm3(stageDir, { recursive: true, force: true });
3332
+ if (tarballPath) await rm3(tarballPath, { force: true });
3333
+ throw err;
3334
+ }
3335
+ }
3238
3336
  async function stageClaudeStaticForUpload(opts = {}) {
3239
3337
  const hostHome = opts.hostHome ?? homedir22();
3240
3338
  const hostClaude = join32(hostHome, ".claude");
@@ -3267,31 +3365,11 @@ async function stageClaudeStaticForUpload(opts = {}) {
3267
3365
  } catch {
3268
3366
  }
3269
3367
  }
3270
- const hostClaudeJson = join32(hostHome, ".claude.json");
3271
- let working;
3272
- if (await pathExists(hostClaudeJson)) {
3273
- try {
3274
- working = JSON.parse(await readFile32(hostClaudeJson, "utf8"));
3275
- } catch {
3276
- working = null;
3277
- }
3278
- }
3279
- if (working === void 0 || working === null) {
3280
- working = {
3281
- installMethod: "native",
3282
- autoUpdates: false,
3283
- autoUpdatesProtectedForNative: true,
3284
- projects: { [CLOUD_WORKSPACE]: { hasTrustDialogAccepted: true } }
3285
- };
3286
- } else {
3287
- working = filterHostHooks(working, hostHome).data;
3288
- working = setInstallMethodNative(working).data;
3289
- if (opts.hostWorkspace) {
3290
- working = addProjectAlias(working, opts.hostWorkspace, CLOUD_WORKSPACE).data;
3291
- }
3292
- working = trustWorkspace(working, CLOUD_WORKSPACE).data;
3293
- }
3294
- await writeFile3(join32(stageDir, "_claude.json"), JSON.stringify(working, null, 2));
3368
+ const claudeJson = await buildBoxClaudeJsonFromHost({
3369
+ hostHome,
3370
+ hostWorkspace: opts.hostWorkspace
3371
+ });
3372
+ await writeFile3(join32(stageDir, "_claude.json"), JSON.stringify(claudeJson, null, 2));
3295
3373
  const pluginsDir = join32(stageDir, "plugins");
3296
3374
  if (await pathExists(pluginsDir)) {
3297
3375
  try {
@@ -3699,7 +3777,7 @@ async function maybeFilterTo(src, dest, hostHome, opts = {}) {
3699
3777
  };
3700
3778
  let parsed;
3701
3779
  try {
3702
- parsed = JSON.parse(await readFile4(src, "utf8"));
3780
+ parsed = JSON.parse(await readFile42(src, "utf8"));
3703
3781
  } catch {
3704
3782
  return zero;
3705
3783
  }
@@ -3782,7 +3860,7 @@ async function isDir(p) {
3782
3860
  }
3783
3861
  async function readReferencedPluginKeys(installedPluginsJsonPath) {
3784
3862
  try {
3785
- const raw = await readFile4(installedPluginsJsonPath, "utf8");
3863
+ const raw = await readFile42(installedPluginsJsonPath, "utf8");
3786
3864
  return referencedPluginVersionKeys(JSON.parse(raw));
3787
3865
  } catch {
3788
3866
  return /* @__PURE__ */ new Set();
@@ -4257,7 +4335,7 @@ async function listChildDirs(dir) {
4257
4335
  }
4258
4336
  async function readJsonFile(path) {
4259
4337
  try {
4260
- return JSON.parse(await readFile4(path, "utf8"));
4338
+ return JSON.parse(await readFile42(path, "utf8"));
4261
4339
  } catch {
4262
4340
  return void 0;
4263
4341
  }
@@ -5374,6 +5452,75 @@ async function seedWorkspace(opts) {
5374
5452
  }
5375
5453
  }
5376
5454
  }
5455
+ async function regenerateRestoredWorktrees(opts) {
5456
+ const log = opts.onLog ?? (() => {
5457
+ });
5458
+ const script = [
5459
+ "set -e",
5460
+ 'OLD="$1"; NEW="$2"; MAIN="$3"; BR="$4"; BASE="$5"; META="$6"',
5461
+ // Rename baked content to the fresh unique path (rename, not copy).
5462
+ '[ "$OLD" = "$NEW" ] || mv "$OLD" "$NEW"',
5463
+ // Fresh branch at the base ref; -f is safe because pickFreshBranch already
5464
+ // guaranteed BR is unused on the host.
5465
+ 'git -C "$MAIN" branch -f "$BR" "$BASE"',
5466
+ // Author worktree metadata under the bind-mounted host .git.
5467
+ 'mkdir -p "$META"',
5468
+ 'printf "../..\\n" > "$META/commondir"',
5469
+ 'printf "%s\\n" "$NEW/.git" > "$META/gitdir"',
5470
+ 'printf "ref: refs/heads/%s\\n" "$BR" > "$META/HEAD"',
5471
+ // Point the worktree's gitfile at the fresh metadata dir.
5472
+ 'printf "gitdir: %s\\n" "$META" > "$NEW/.git"',
5473
+ // Reset tracked files to the fork base so the box starts CLEAN at the host
5474
+ // base ref — same git state as a fresh create. The baked tree was captured
5475
+ // on the source box's (possibly divergent/older) branch, so its tracked
5476
+ // deviations are staleness, not work; resetting drops that noise. The
5477
+ // gitignored warm artifacts (node_modules, .next, build caches) are
5478
+ // untouched by reset --hard — that warm state is the checkpoint's value.
5479
+ 'git -C "$NEW" reset -q --hard HEAD',
5480
+ // Boxes carry no signing key; mirror seedWorkspace's per-worktree config so
5481
+ // host commit.gpgsign=true doesn't break in-box commits. extensions.
5482
+ // worktreeConfig writes the SHARED (bind-mounted) .git/config, so concurrent
5483
+ // boxes race on .git/config.lock — retry through the contention rather than
5484
+ // aborting (best-effort, like seedWorkspace). The per-worktree gpgsign write
5485
+ // needs the extension enabled first, so it follows the retry.
5486
+ "set +e",
5487
+ 'for i in 1 2 3 4 5 6 7 8; do git -C "$MAIN" config extensions.worktreeConfig true && break; sleep 0.25; done',
5488
+ 'git -C "$NEW" config --worktree commit.gpgsign false',
5489
+ "exit 0"
5490
+ ].join("\n");
5491
+ for (const p of opts.plans) {
5492
+ const baseRef = p.kind === "root" ? opts.fromBranch ?? "HEAD" : "HEAD";
5493
+ const metaDir = `${p.hostMainRepo}/.git/worktrees/${fsSafeBranch(p.freshBranch)}`;
5494
+ const r = await execa8(
5495
+ "docker",
5496
+ [
5497
+ "exec",
5498
+ "--user",
5499
+ "vscode",
5500
+ opts.container,
5501
+ "bash",
5502
+ "-c",
5503
+ script,
5504
+ "bash",
5505
+ p.bakedGitWorktreePath,
5506
+ p.freshGitWorktreePath,
5507
+ p.hostMainRepo,
5508
+ p.freshBranch,
5509
+ baseRef,
5510
+ metaDir
5511
+ ],
5512
+ { reject: false }
5513
+ );
5514
+ if (r.exitCode !== 0) {
5515
+ throw new GitWorktreeError(
5516
+ `regenerate worktree for ${p.freshBranch} failed: ${r.stderr || r.stdout}`
5517
+ );
5518
+ }
5519
+ log(
5520
+ `restored worktree ${p.freshGitWorktreePath} on fresh branch ${p.freshBranch} (base ${baseRef})`
5521
+ );
5522
+ }
5523
+ }
5377
5524
  async function seedWorkspaceFromDir(opts) {
5378
5525
  const log = opts.onLog ?? (() => {
5379
5526
  });
@@ -5413,6 +5560,12 @@ async function gitIn(container, ct, args) {
5413
5560
  function splitNul(s) {
5414
5561
  return s.split("\0").filter((p) => p.length > 0);
5415
5562
  }
5563
+ var NON_REGULAR_TOKEN = "-";
5564
+ function classifyUntrackedOverlay(boxToken, hostHash) {
5565
+ if (boxToken === void 0) return "copy";
5566
+ if (boxToken === NON_REGULAR_TOKEN) return "conflict";
5567
+ return boxToken === hostHash ? "identical" : "conflict";
5568
+ }
5416
5569
  async function unmergedPaths(container, ct) {
5417
5570
  const r = await gitIn(container, ct, ["diff", "--name-only", "--diff-filter=U", "-z"]);
5418
5571
  return r.exitCode === 0 ? splitNul(r.stdout) : [];
@@ -5509,7 +5662,7 @@ async function resyncWorkspaceFromHost(opts) {
5509
5662
  }
5510
5663
  }
5511
5664
  if (hostUntracked.length > 0) {
5512
- const filter = await execa8(
5665
+ const probe = await execa8(
5513
5666
  "docker",
5514
5667
  [
5515
5668
  "exec",
@@ -5519,15 +5672,42 @@ async function resyncWorkspaceFromHost(opts) {
5519
5672
  opts.container,
5520
5673
  "bash",
5521
5674
  "-c",
5522
- 'cd "$1" && while IFS= read -r -d "" f; do [ -e "$f" ] && printf "%s\\0" "$f"; done',
5675
+ 'cd "$1" && while IFS= read -r -d "" f; do if [ -f "$f" ] && [ ! -L "$f" ]; then printf "%s\\0%s\\0" "$(sha256sum < "$f" | cut -d" " -f1)" "$f"; elif [ -e "$f" ]; then printf "%s\\0%s\\0" "-" "$f"; fi; done',
5523
5676
  "bash",
5524
5677
  ct
5525
5678
  ],
5526
5679
  { input: hostUntracked.join("\0"), reject: false }
5527
5680
  );
5528
- const existing = new Set(filter.exitCode === 0 ? splitNul(filter.stdout) : []);
5529
- const toCopy = hostUntracked.filter((p) => !existing.has(p));
5530
- for (const p of hostUntracked) if (existing.has(p)) res.overlaySkipped.push(p);
5681
+ const boxTokens = /* @__PURE__ */ new Map();
5682
+ if (probe.exitCode === 0) {
5683
+ const flat = splitNul(probe.stdout);
5684
+ for (let i = 0; i + 1 < flat.length; i += 2) {
5685
+ const token = flat[i];
5686
+ const path = flat[i + 1];
5687
+ if (token !== void 0 && path !== void 0) boxTokens.set(path, token);
5688
+ }
5689
+ }
5690
+ const toCopy = [];
5691
+ let identical = 0;
5692
+ for (const p of hostUntracked) {
5693
+ const boxToken = boxTokens.get(p);
5694
+ let hostHash = "";
5695
+ if (boxToken !== void 0 && boxToken !== NON_REGULAR_TOKEN) {
5696
+ try {
5697
+ hostHash = createHash22("sha256").update(await readFile52(join7(hostMain, p))).digest("hex");
5698
+ } catch {
5699
+ identical++;
5700
+ continue;
5701
+ }
5702
+ }
5703
+ const verdict = classifyUntrackedOverlay(boxToken, hostHash);
5704
+ if (verdict === "copy") toCopy.push(p);
5705
+ else if (verdict === "conflict") res.overlaySkipped.push(p);
5706
+ else identical++;
5707
+ }
5708
+ if (identical > 0) {
5709
+ log(`resync: ${ct}: ${String(identical)} untracked host file(s) already identical in box (no-op)`);
5710
+ }
5531
5711
  if (toCopy.length > 0) {
5532
5712
  const tarOut = await execa8("tar", ["-C", hostMain, "--null", "-T", "-", "-cf", "-"], {
5533
5713
  input: toCopy.join("\0"),
@@ -5637,7 +5817,7 @@ async function startPortlessProxy() {
5637
5817
  function portlessStateDirCandidates() {
5638
5818
  const env = process.env["PORTLESS_STATE_DIR"];
5639
5819
  if (env && env.trim().length > 0) return [env.trim()];
5640
- return ["/tmp/portless", join7(homedir6(), ".portless")];
5820
+ return ["/tmp/portless", join8(homedir6(), ".portless")];
5641
5821
  }
5642
5822
  function pidAlive(pid) {
5643
5823
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -5650,7 +5830,7 @@ function pidAlive(pid) {
5650
5830
  }
5651
5831
  async function readProxyPid(dir) {
5652
5832
  try {
5653
- const raw = await readFile52(join7(dir, "proxy.pid"), "utf8");
5833
+ const raw = await readFile6(join8(dir, "proxy.pid"), "utf8");
5654
5834
  const pid = Number.parseInt(raw.trim(), 10);
5655
5835
  return Number.isFinite(pid) && pid > 0 ? pid : null;
5656
5836
  } catch {
@@ -5670,7 +5850,7 @@ async function resolvePortlessHostStateDir(override) {
5670
5850
  if (env && env.trim().length > 0) return env.trim();
5671
5851
  const live = await findLivePortlessStateDir();
5672
5852
  if (live) return live;
5673
- const home = join7(homedir6(), ".portless");
5853
+ const home = join8(homedir6(), ".portless");
5674
5854
  if (existsSync(home)) return home;
5675
5855
  if (existsSync("/tmp/portless")) return "/tmp/portless";
5676
5856
  return home;
@@ -5699,12 +5879,12 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
5699
5879
  ".cache",
5700
5880
  ".parcel-cache"
5701
5881
  ]);
5702
- var SNAPSHOTS_ROOT = join8(homedir7(), ".agentbox", "snapshots");
5882
+ var SNAPSHOTS_ROOT = join9(homedir7(), ".agentbox", "snapshots");
5703
5883
  function snapshotPathFor(box) {
5704
5884
  const mnemonic = sanitizeMnemonic(box.name);
5705
5885
  const n = box.projectIndex;
5706
5886
  const segment = typeof n === "number" && Number.isFinite(n) && n > 0 ? `${box.id}-${String(n)}-${mnemonic}` : `${box.id}-${mnemonic}`;
5707
- return join8(SNAPSHOTS_ROOT, segment);
5887
+ return join9(SNAPSHOTS_ROOT, segment);
5708
5888
  }
5709
5889
  async function findExcludedDirs(root, excluded = EXCLUDE_DIRS) {
5710
5890
  const matches = [];
@@ -5717,7 +5897,7 @@ async function findExcludedDirs(root, excluded = EXCLUDE_DIRS) {
5717
5897
  }
5718
5898
  for (const entry of entries) {
5719
5899
  if (!entry.isDirectory()) continue;
5720
- const abs = join8(dir, entry.name);
5900
+ const abs = join9(dir, entry.name);
5721
5901
  if (excluded.has(entry.name)) {
5722
5902
  matches.push(abs);
5723
5903
  continue;
@@ -5739,21 +5919,21 @@ async function createSnapshot(opts) {
5739
5919
  await Promise.all(toPrune.map((p) => rm32(p, { recursive: true, force: true })));
5740
5920
  return { destination, prunedPaths: toPrune };
5741
5921
  }
5742
- var CHECKPOINTS_ROOT = join9(homedir8(), ".agentbox", "checkpoints");
5922
+ var CHECKPOINTS_ROOT = join10(homedir8(), ".agentbox", "checkpoints");
5743
5923
  var CHECKPOINT_IMAGE_PREFIX = "agentbox-ckpt-";
5744
5924
  function checkpointImageTag(projectRoot, name) {
5745
5925
  const mnemonic = sanitizeMnemonic(basename32(projectRoot));
5746
5926
  return `${CHECKPOINT_IMAGE_PREFIX}${hashProjectPath(projectRoot)}_${mnemonic}:${name}`;
5747
5927
  }
5748
5928
  function projectCheckpointsDir(projectRoot) {
5749
- return join9(CHECKPOINTS_ROOT, projectDirSegment(projectRoot));
5929
+ return join10(CHECKPOINTS_ROOT, projectDirSegment(projectRoot));
5750
5930
  }
5751
5931
  function checkpointDir(projectRoot, name) {
5752
- return join9(projectCheckpointsDir(projectRoot), name);
5932
+ return join10(projectCheckpointsDir(projectRoot), name);
5753
5933
  }
5754
5934
  async function readManifest(dir) {
5755
5935
  try {
5756
- const raw = await readFile6(join9(dir, "manifest.json"), "utf8");
5936
+ const raw = await readFile7(join10(dir, "manifest.json"), "utf8");
5757
5937
  const m = JSON.parse(raw);
5758
5938
  if (m.schema !== 2 && m.schema !== 3) return null;
5759
5939
  return m;
@@ -5770,7 +5950,7 @@ async function listCheckpointsInDir(root) {
5770
5950
  }
5771
5951
  const out = [];
5772
5952
  for (const name of entries) {
5773
- const dir = join9(root, name);
5953
+ const dir = join10(root, name);
5774
5954
  const manifest = await readManifest(dir);
5775
5955
  if (manifest) out.push({ name, dir, manifest });
5776
5956
  }
@@ -5789,7 +5969,7 @@ async function listAllCheckpoints() {
5789
5969
  }
5790
5970
  const out = [];
5791
5971
  for (const segment of segments) {
5792
- const items = await listCheckpointsInDir(join9(CHECKPOINTS_ROOT, segment));
5972
+ const items = await listCheckpointsInDir(join10(CHECKPOINTS_ROOT, segment));
5793
5973
  if (items.length > 0) out.push({ segment, items });
5794
5974
  }
5795
5975
  return out;
@@ -5809,7 +5989,7 @@ async function listAllCheckpointImages() {
5809
5989
  }
5810
5990
  const out = /* @__PURE__ */ new Set();
5811
5991
  for (const proj of projectDirs) {
5812
- const projPath = join9(CHECKPOINTS_ROOT, proj);
5992
+ const projPath = join10(CHECKPOINTS_ROOT, proj);
5813
5993
  let names;
5814
5994
  try {
5815
5995
  names = (await readdir5(projPath, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
@@ -5817,7 +5997,7 @@ async function listAllCheckpointImages() {
5817
5997
  continue;
5818
5998
  }
5819
5999
  for (const name of names) {
5820
- const manifest = await readManifest(join9(projPath, name));
6000
+ const manifest = await readManifest(join10(projPath, name));
5821
6001
  if (manifest) out.add(manifest.image);
5822
6002
  }
5823
6003
  }
@@ -5978,7 +6158,7 @@ async function createCheckpoint(opts) {
5978
6158
  cliVersion: stamp.cliVersion,
5979
6159
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
5980
6160
  };
5981
- await writeFile32(join9(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf8");
6161
+ await writeFile32(join10(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf8");
5982
6162
  if (opts.setDefault) {
5983
6163
  await setConfigValue("project", "box.defaultCheckpointDocker", name, opts.projectRoot);
5984
6164
  log(`set project default checkpoint (box.defaultCheckpointDocker) -> ${name}`);
@@ -5995,9 +6175,9 @@ async function flattenImage(sourceTag, destTag, log) {
5995
6175
  if (create.exitCode !== 0) {
5996
6176
  throw new CheckpointError(`docker create for flatten failed`, create.stdout, create.stderr);
5997
6177
  }
5998
- const scratch = await mkdtemp3(join9(tmpdir3(), "agentbox-flatten-"));
6178
+ const scratch = await mkdtemp3(join10(tmpdir3(), "agentbox-flatten-"));
5999
6179
  try {
6000
- const rootfsPath = join9(scratch, "rootfs.tar");
6180
+ const rootfsPath = join10(scratch, "rootfs.tar");
6001
6181
  log(`exporting rootfs of ${sourceTag} to ${rootfsPath}`);
6002
6182
  const exp = await execa11("docker", ["export", "-o", rootfsPath, tmpName], { reject: false });
6003
6183
  if (exp.exitCode !== 0) {
@@ -6010,11 +6190,11 @@ async function flattenImage(sourceTag, destTag, log) {
6010
6190
  "ADD rootfs.tar /",
6011
6191
  ...renderConfigDirectives(cfg)
6012
6192
  ];
6013
- await writeFile32(join9(scratch, "Dockerfile"), lines.join("\n") + "\n", "utf8");
6193
+ await writeFile32(join10(scratch, "Dockerfile"), lines.join("\n") + "\n", "utf8");
6014
6194
  log(`building flattened ${destTag} from rootfs.tar (FROM scratch)`);
6015
6195
  const build = await execa11(
6016
6196
  "docker",
6017
- ["build", "-t", destTag, "-f", join9(scratch, "Dockerfile"), scratch],
6197
+ ["build", "-t", destTag, "-f", join10(scratch, "Dockerfile"), scratch],
6018
6198
  { reject: false }
6019
6199
  );
6020
6200
  if (build.exitCode !== 0) {
@@ -6105,10 +6285,10 @@ async function ensureHomeOwnedByVscode(container) {
6105
6285
  { reject: false }
6106
6286
  );
6107
6287
  }
6108
- var STATE_DIR22 = join10(homedir9(), ".agentbox");
6109
- var PID_FILE = join10(STATE_DIR22, "relay.pid");
6110
- var LOG_FILE = join10(STATE_DIR22, "relay.log");
6111
- var RELAY_HOME_DIR = join10(STATE_DIR22, "relay");
6288
+ var STATE_DIR22 = join11(homedir9(), ".agentbox");
6289
+ var PID_FILE = join11(STATE_DIR22, "relay.pid");
6290
+ var LOG_FILE = join11(STATE_DIR22, "relay.log");
6291
+ var RELAY_HOME_DIR = join11(STATE_DIR22, "relay");
6112
6292
  var PORT = DEFAULT_RELAY_PORT;
6113
6293
  var ENDPOINT = {
6114
6294
  // host.docker.internal is the Docker Desktop / OrbStack-supplied alias for
@@ -6275,13 +6455,13 @@ async function stageRelayHome(version, log) {
6275
6455
  if (process.env.AGENTBOX_RELAY_BIN || process.env.AGENTBOX_CLI_ENTRY) return null;
6276
6456
  const cliRoot = findCliRoot(dirname3(fileURLToPath(import.meta.url)));
6277
6457
  if (cliRoot === null) return null;
6278
- const homeDir = join10(RELAY_HOME_DIR, version);
6279
- const stagedEntry = join10(homeDir, "dist", "index.js");
6280
- const stagedBin = join10(homeDir, "runtime", "relay", "bin.cjs");
6458
+ const homeDir = join11(RELAY_HOME_DIR, version);
6459
+ const stagedEntry = join11(homeDir, "dist", "index.js");
6460
+ const stagedBin = join11(homeDir, "runtime", "relay", "bin.cjs");
6281
6461
  if (existsSync2(stagedEntry) && existsSync2(stagedBin)) {
6282
6462
  return { relayBin: stagedBin, cliEntry: stagedEntry };
6283
6463
  }
6284
- const nodeModules = resolveDepRoot(join10(cliRoot, "dist", "index.js"));
6464
+ const nodeModules = resolveDepRoot(join11(cliRoot, "dist", "index.js"));
6285
6465
  if (nodeModules === null) return null;
6286
6466
  const tmpDir = `${homeDir}.tmp-${String(process.pid)}`;
6287
6467
  try {
@@ -6289,10 +6469,10 @@ async function stageRelayHome(version, log) {
6289
6469
  await rm5(tmpDir, { recursive: true, force: true });
6290
6470
  await mkdir6(tmpDir, { recursive: true });
6291
6471
  for (const sub of ["dist", "runtime", "share"]) {
6292
- const src = join10(cliRoot, sub);
6293
- if (existsSync2(src)) await cp(src, join10(tmpDir, sub), { recursive: true });
6472
+ const src = join11(cliRoot, sub);
6473
+ if (existsSync2(src)) await cp(src, join11(tmpDir, sub), { recursive: true });
6294
6474
  }
6295
- await cp(nodeModules, join10(tmpDir, "node_modules"), { recursive: true, dereference: true });
6475
+ await cp(nodeModules, join11(tmpDir, "node_modules"), { recursive: true, dereference: true });
6296
6476
  await rm5(homeDir, { recursive: true, force: true });
6297
6477
  await rename3(tmpDir, homeDir);
6298
6478
  } catch (err) {
@@ -6311,7 +6491,7 @@ async function stageRelayHome(version, log) {
6311
6491
  }
6312
6492
  function findCliRoot(moduleDir) {
6313
6493
  for (const root of [resolve22(moduleDir, ".."), resolve22(moduleDir, "..", "..")]) {
6314
- if (existsSync2(join10(root, "dist", "index.js")) && existsSync2(join10(root, "runtime", "relay", "bin.cjs"))) {
6494
+ if (existsSync2(join11(root, "dist", "index.js")) && existsSync2(join11(root, "runtime", "relay", "bin.cjs"))) {
6315
6495
  return root;
6316
6496
  }
6317
6497
  }
@@ -6326,7 +6506,7 @@ function resolveDepRoot(fromFile) {
6326
6506
  const idx = main.lastIndexOf(marker);
6327
6507
  if (idx === -1) return null;
6328
6508
  const nm = main.slice(0, idx + marker.length - 1);
6329
- return existsSync2(join10(nm, "commander")) ? nm : null;
6509
+ return existsSync2(join11(nm, "commander")) ? nm : null;
6330
6510
  } catch {
6331
6511
  return null;
6332
6512
  }
@@ -6340,7 +6520,7 @@ async function gcOldRelayHomes(keepVersion) {
6340
6520
  }
6341
6521
  for (const name of entries) {
6342
6522
  if (name === keepVersion) continue;
6343
- await rm5(join10(RELAY_HOME_DIR, name), { recursive: true, force: true }).catch(() => {
6523
+ await rm5(join11(RELAY_HOME_DIR, name), { recursive: true, force: true }).catch(() => {
6344
6524
  });
6345
6525
  }
6346
6526
  }
@@ -6451,7 +6631,7 @@ function fetchHealthz(timeoutMs) {
6451
6631
  }
6452
6632
  async function readPidFile() {
6453
6633
  try {
6454
- const text = await readFile7(PID_FILE, "utf8");
6634
+ const text = await readFile8(PID_FILE, "utf8");
6455
6635
  const pid = Number.parseInt(text.trim(), 10);
6456
6636
  return Number.isFinite(pid) && pid > 0 ? pid : null;
6457
6637
  } catch {
@@ -6797,7 +6977,7 @@ async function pathExists6(p) {
6797
6977
  async function buildIdentityMounts() {
6798
6978
  const home = homedir10();
6799
6979
  const candidates = [
6800
- { src: join11(home, ".gitconfig"), dst: "/home/vscode/.gitconfig", readOnly: true }
6980
+ { src: join12(home, ".gitconfig"), dst: "/home/vscode/.gitconfig", readOnly: true }
6801
6981
  ];
6802
6982
  const out = [];
6803
6983
  for (const c of candidates) {
@@ -6814,7 +6994,7 @@ async function createBox(opts) {
6814
6994
  if (!await pathExists6(workspace)) {
6815
6995
  throw new Error(`workspace does not exist: ${workspace}`);
6816
6996
  }
6817
- const cfgPath = join11(workspace, "agentbox.yaml");
6997
+ const cfgPath = join12(workspace, "agentbox.yaml");
6818
6998
  if (await pathExists6(cfgPath)) {
6819
6999
  try {
6820
7000
  const cfg = await loadConfig(cfgPath);
@@ -6894,12 +7074,35 @@ async function createBox(opts) {
6894
7074
  }
6895
7075
  let projectIndex;
6896
7076
  if (opts.projectRoot) {
6897
- projectIndex = allocateProjectIndex(await readState(), opts.projectRoot);
7077
+ projectIndex = await reserveProjectIndex(
7078
+ { id, name, container: containerName, image: imageRef, workspacePath: workspace, createdAt },
7079
+ opts.projectRoot
7080
+ );
6898
7081
  }
6899
7082
  const repoCarryOvers = [];
6900
7083
  const gitWorktreeRecords = [];
7084
+ const restoreWorktreePlans = [];
6901
7085
  if (checkpointImage && restoredWorktrees && restoredWorktrees.length > 0) {
6902
- gitWorktreeRecords.push(...restoredWorktrees);
7086
+ for (const w of restoredWorktrees) {
7087
+ const branchBase = w.kind === "root" ? `agentbox/${name}` : `agentbox/${name}--${w.relPathFromWorkspace.replace(/[^A-Za-z0-9._-]+/g, "_")}`;
7088
+ const freshBranch = await pickFreshBranch(w.hostMainRepo, branchBase);
7089
+ const freshGitWorktreePath = gitWorktreePathFor(freshBranch);
7090
+ restoreWorktreePlans.push({
7091
+ hostMainRepo: w.hostMainRepo,
7092
+ kind: w.kind,
7093
+ bakedGitWorktreePath: w.gitWorktreePath,
7094
+ freshBranch,
7095
+ freshGitWorktreePath
7096
+ });
7097
+ gitWorktreeRecords.push({
7098
+ kind: w.kind,
7099
+ hostMainRepo: w.hostMainRepo,
7100
+ containerPath: w.containerPath,
7101
+ gitWorktreePath: freshGitWorktreePath,
7102
+ branch: freshBranch,
7103
+ relPathFromWorkspace: w.relPathFromWorkspace
7104
+ });
7105
+ }
6903
7106
  }
6904
7107
  if (!checkpointImage) {
6905
7108
  const repos = await detectGitRepos(workspace);
@@ -7007,7 +7210,7 @@ async function createBox(opts) {
7007
7210
  log(`seeded claude credentials into ${claudeSpec.volume} from host backup`);
7008
7211
  }
7009
7212
  const claudeMounts = buildClaudeMounts(claudeSpec, process.env);
7010
- const wantCodex = opts.codexConfig !== void 0 || await pathExists6(join11(homedir10(), ".codex"));
7213
+ const wantCodex = opts.codexConfig !== void 0 || await pathExists6(join12(homedir10(), ".codex"));
7011
7214
  let codexMounts;
7012
7215
  let codexConfigVolume;
7013
7216
  if (wantCodex) {
@@ -7027,7 +7230,7 @@ async function createBox(opts) {
7027
7230
  codexMounts = buildCodexMounts(codexSpec, process.env);
7028
7231
  codexConfigVolume = codexSpec.volume;
7029
7232
  }
7030
- const wantOpencode = opts.opencodeConfig !== void 0 || await pathExists6(join11(homedir10(), ".config", "opencode")) || await pathExists6(join11(homedir10(), ".local", "share", "opencode"));
7233
+ const wantOpencode = opts.opencodeConfig !== void 0 || await pathExists6(join12(homedir10(), ".config", "opencode")) || await pathExists6(join12(homedir10(), ".local", "share", "opencode"));
7031
7234
  let opencodeMounts;
7032
7235
  let opencodeConfigVolume;
7033
7236
  if (wantOpencode) {
@@ -7048,9 +7251,9 @@ async function createBox(opts) {
7048
7251
  opencodeConfigVolume = opencodeSpec.volume;
7049
7252
  }
7050
7253
  const boxDir = boxRunDirFor({ id, name, projectIndex });
7051
- const socketDir = join11(boxDir, "run");
7052
- const socketPath = join11(socketDir, "ctl.sock");
7053
- const mergedExportDir = join11(boxDir, "workspace");
7254
+ const socketDir = join12(boxDir, "run");
7255
+ const socketPath = join12(socketDir, "ctl.sock");
7256
+ const mergedExportDir = join12(boxDir, "workspace");
7054
7257
  await mkdir7(socketDir, { recursive: true });
7055
7258
  await mkdir7(mergedExportDir, { recursive: true });
7056
7259
  const extraVolumes = await buildIdentityMounts();
@@ -7139,6 +7342,36 @@ async function createBox(opts) {
7139
7342
  effectiveLimits = { ...appliedLimits, disk: null };
7140
7343
  }
7141
7344
  }
7345
+ const baseRecord = {
7346
+ id,
7347
+ name,
7348
+ container: containerName,
7349
+ image: imageRef,
7350
+ workspacePath: workspace,
7351
+ snapshotDir,
7352
+ socketPath,
7353
+ claudeConfigVolume: claudeSpec.volume,
7354
+ codexConfigVolume,
7355
+ opencodeConfigVolume,
7356
+ vscodeServerVolume: vscodeServerVolumeName(id),
7357
+ cursorServerVolume: cursorServerVolumeName(id),
7358
+ relayToken: relayUp ? relayToken : void 0,
7359
+ gitWorktrees: gitWorktreeRecords.length > 0 ? gitWorktreeRecords : void 0,
7360
+ withPlaywright: opts.withPlaywright ? true : void 0,
7361
+ withEnv: opts.withEnv ? true : void 0,
7362
+ vncEnabled: vncEnabled ? true : void 0,
7363
+ vncContainerPort: vncEnabled ? VNC_CONTAINER_PORT : void 0,
7364
+ vncPassword,
7365
+ webContainerPort: WEB_CONTAINER_PORT,
7366
+ dockerVolume,
7367
+ dockerCacheShared: dockerCacheShared || void 0,
7368
+ projectRoot: opts.projectRoot,
7369
+ projectIndex,
7370
+ checkpointImage,
7371
+ checkpointSource,
7372
+ resourceLimits: persistableLimits(effectiveLimits),
7373
+ createdAt
7374
+ };
7142
7375
  await runBox({
7143
7376
  name: containerName,
7144
7377
  image: imageRef,
@@ -7158,6 +7391,7 @@ async function createBox(opts) {
7158
7391
  }
7159
7392
  });
7160
7393
  log(`container ${containerName} started`);
7394
+ await recordBox(baseRecord);
7161
7395
  if (gitWorktreeRecords.length > 0) {
7162
7396
  await chownGitBindParents({
7163
7397
  container: containerName,
@@ -7183,6 +7417,7 @@ async function createBox(opts) {
7183
7417
  if (opts.useBranch !== void 0) {
7184
7418
  log(`seedWorkspace failed for --use-branch ${opts.useBranch}; cleaning up the box`);
7185
7419
  await execa14("docker", ["rm", "-f", containerName], { reject: false });
7420
+ await removeBoxRecord(id);
7186
7421
  for (const w of gitWorktreeRecords) {
7187
7422
  await removeInBoxWorktree({
7188
7423
  hostMainRepo: w.hostMainRepo,
@@ -7198,21 +7433,27 @@ async function createBox(opts) {
7198
7433
  const source = snapshotDir ?? workspace;
7199
7434
  await seedWorkspaceFromDir({ container: containerName, hostSource: source, onLog: log });
7200
7435
  }
7201
- } else if (restoredWorktrees && restoredWorktrees.length > 0) {
7436
+ } else if (restoreWorktreePlans.length > 0) {
7437
+ await regenerateRestoredWorktrees({
7438
+ container: containerName,
7439
+ plans: restoreWorktreePlans,
7440
+ fromBranch: opts.fromBranch,
7441
+ onLog: log
7442
+ });
7202
7443
  await bindWorktrees(
7203
7444
  containerName,
7204
- restoredWorktrees.map((w) => ({
7445
+ gitWorktreeRecords.map((w) => ({
7205
7446
  kind: w.kind,
7206
7447
  containerPath: w.containerPath,
7207
7448
  gitWorktreePath: w.gitWorktreePath
7208
7449
  })),
7209
7450
  log
7210
7451
  );
7211
- log("re-bound /workspace from checkpoint image");
7452
+ log("re-bound /workspace from checkpoint image (fresh per-box worktree)");
7212
7453
  if (opts.resyncOnStart !== false) {
7213
7454
  const repos = await resyncWorkspaceFromHost({
7214
7455
  container: containerName,
7215
- worktrees: restoredWorktrees,
7456
+ worktrees: gitWorktreeRecords,
7216
7457
  onLog: log
7217
7458
  });
7218
7459
  resyncResult = {
@@ -7227,15 +7468,15 @@ async function createBox(opts) {
7227
7468
  }
7228
7469
  await repairIdeOwnership(containerName);
7229
7470
  log(".vscode-server + .cursor-server ownership verified");
7230
- const ctl = await launchCtlDaemon(containerName, socketPath);
7231
- if (ctl.up) log("agentbox-ctl daemon up");
7232
- else log(`agentbox-ctl daemon did not become reachable: ${ctl.reason}`);
7233
7471
  const dockerd = await launchDockerdDaemon(containerName);
7234
7472
  if (dockerd.up) {
7235
7473
  log(`dockerd up (data root=${dockerVolume})`);
7236
7474
  } else {
7237
7475
  log(`dockerd did not become ready: ${dockerd.reason}`);
7238
7476
  }
7477
+ const ctl = await launchCtlDaemon(containerName, socketPath);
7478
+ if (ctl.up) log("agentbox-ctl daemon up");
7479
+ else log(`agentbox-ctl daemon did not become reachable: ${ctl.reason}`);
7239
7480
  if (opts.withPlaywright) {
7240
7481
  log("installing @playwright/cli@latest (--with-playwright)");
7241
7482
  const result = await execa14(
@@ -7354,41 +7595,14 @@ async function createBox(opts) {
7354
7595
  }
7355
7596
  }
7356
7597
  const record = {
7357
- id,
7358
- name,
7359
- container: containerName,
7360
- image: imageRef,
7361
- workspacePath: workspace,
7362
- snapshotDir,
7363
- socketPath,
7364
- claudeConfigVolume: claudeSpec.volume,
7365
- codexConfigVolume,
7366
- opencodeConfigVolume,
7367
- vscodeServerVolume: vscodeServerVolumeName(id),
7368
- cursorServerVolume: cursorServerVolumeName(id),
7369
- relayToken: relayUp ? relayToken : void 0,
7370
- gitWorktrees: gitWorktreeRecords.length > 0 ? gitWorktreeRecords : void 0,
7371
- withPlaywright: opts.withPlaywright ? true : void 0,
7372
- withEnv: opts.withEnv ? true : void 0,
7598
+ ...baseRecord,
7373
7599
  carry: carrySummary,
7374
- vncEnabled: vncEnabled ? true : void 0,
7375
- vncContainerPort: vncEnabled ? VNC_CONTAINER_PORT : void 0,
7376
7600
  vncHostPort: vncHostPort ?? void 0,
7377
- vncPassword,
7378
- webContainerPort: WEB_CONTAINER_PORT,
7379
7601
  webHostPort: webHostPort ?? void 0,
7380
7602
  portlessAlias: portlessAliasName,
7381
7603
  portlessUrl,
7382
7604
  portlessVncAlias: portlessVncAliasName,
7383
- portlessVncUrl,
7384
- dockerVolume,
7385
- dockerCacheShared: dockerCacheShared || void 0,
7386
- projectRoot: opts.projectRoot,
7387
- projectIndex,
7388
- checkpointImage,
7389
- checkpointSource,
7390
- resourceLimits: persistableLimits(effectiveLimits),
7391
- createdAt
7605
+ portlessVncUrl
7392
7606
  };
7393
7607
  await recordBox(record);
7394
7608
  return { record, imageBuilt: built, resync: resyncResult };
@@ -7574,7 +7788,7 @@ async function getBoxEndpoints(record, engine, persisted) {
7574
7788
  for (const svc of persistedServices) pushService(svc.name, svc.port);
7575
7789
  } else {
7576
7790
  try {
7577
- const cfg = await loadConfig(join12(record.workspacePath, "agentbox.yaml"));
7791
+ const cfg = await loadConfig(join13(record.workspacePath, "agentbox.yaml"));
7578
7792
  if (!webServiceName) {
7579
7793
  webServiceName = cfg.services.find((s) => s.expose)?.name ?? null;
7580
7794
  }
@@ -7725,9 +7939,9 @@ async function resyncBox(idOrName, onLog) {
7725
7939
  async function startBox(idOrName) {
7726
7940
  const box = await resolveBox(idOrName);
7727
7941
  for (const w of box.gitWorktrees ?? []) {
7728
- if (!await pathExists7(join13(w.hostMainRepo, ".git"))) {
7942
+ if (!await pathExists7(join14(w.hostMainRepo, ".git"))) {
7729
7943
  throw new Error(
7730
- `main repo for box worktree missing: ${join13(w.hostMainRepo, ".git")} (recreate the box)`
7944
+ `main repo for box worktree missing: ${join14(w.hostMainRepo, ".git")} (recreate the box)`
7731
7945
  );
7732
7946
  }
7733
7947
  }
@@ -7743,12 +7957,12 @@ async function startBox(idOrName) {
7743
7957
  );
7744
7958
  }
7745
7959
  await ensureHomeOwnedByVscode(box.container);
7746
- if (box.socketPath) {
7747
- await launchCtlDaemon(box.container, box.socketPath);
7748
- }
7749
7960
  if (box.dockerVolume) {
7750
7961
  await launchDockerdDaemon(box.container);
7751
7962
  }
7963
+ if (box.socketPath) {
7964
+ await launchCtlDaemon(box.container, box.socketPath);
7965
+ }
7752
7966
  if (box.vncEnabled) {
7753
7967
  await launchVncDaemon(box.container);
7754
7968
  const freshHostPort = await publishedHostPort(box.container, VNC_CONTAINER_PORT);
@@ -7896,16 +8110,13 @@ async function destroyBox(idOrName, opts = {}) {
7896
8110
  } catch {
7897
8111
  }
7898
8112
  }
7899
- const ownsWorktrees = !box.checkpointImage;
7900
- if (ownsWorktrees) {
7901
- for (const w of box.gitWorktrees ?? []) {
7902
- try {
7903
- await removeInBoxWorktree({
7904
- hostMainRepo: w.hostMainRepo,
7905
- gitWorktreePath: w.gitWorktreePath
7906
- });
7907
- } catch {
7908
- }
8113
+ for (const w of box.gitWorktrees ?? []) {
8114
+ try {
8115
+ await removeInBoxWorktree({
8116
+ hostMainRepo: w.hostMainRepo,
8117
+ gitWorktreePath: w.gitWorktreePath
8118
+ });
8119
+ } catch {
7909
8120
  }
7910
8121
  }
7911
8122
  const beforeContainer = await inspectContainerStatus(box.container);
@@ -7956,7 +8167,7 @@ async function destroyBox(idOrName, opts = {}) {
7956
8167
  async function listSnapshotDirs() {
7957
8168
  try {
7958
8169
  const entries = await readdir7(SNAPSHOTS_ROOT, { withFileTypes: true });
7959
- return entries.filter((e) => e.isDirectory()).map((e) => join13(SNAPSHOTS_ROOT, e.name));
8170
+ return entries.filter((e) => e.isDirectory()).map((e) => join14(SNAPSHOTS_ROOT, e.name));
7960
8171
  } catch {
7961
8172
  return [];
7962
8173
  }
@@ -7964,7 +8175,7 @@ async function listSnapshotDirs() {
7964
8175
  async function listBoxDirs() {
7965
8176
  try {
7966
8177
  const entries = await readdir7(BOXES_ROOT, { withFileTypes: true });
7967
- return entries.filter((e) => e.isDirectory()).map((e) => join13(BOXES_ROOT, e.name));
8178
+ return entries.filter((e) => e.isDirectory()).map((e) => join14(BOXES_ROOT, e.name));
7968
8179
  } catch {
7969
8180
  return [];
7970
8181
  }
@@ -8149,7 +8360,7 @@ async function volumeSizeBytes(name) {
8149
8360
  if (!name) return null;
8150
8361
  const engine = await detectEngine();
8151
8362
  if (engine === "orbstack") {
8152
- const live = join14(homedir11(), "OrbStack", "docker", "volumes", name);
8363
+ const live = join15(homedir11(), "OrbStack", "docker", "volumes", name);
8153
8364
  const sz = await duBytes(live);
8154
8365
  if (sz !== null) return sz;
8155
8366
  }
@@ -8212,7 +8423,7 @@ async function allCheckpointImagesBytes() {
8212
8423
  return any ? total : null;
8213
8424
  }
8214
8425
  async function agentboxHomeBytes() {
8215
- return duBytes(join14(homedir11(), ".agentbox"));
8426
+ return duBytes(join15(homedir11(), ".agentbox"));
8216
8427
  }
8217
8428
  function limitsFromRecord(record) {
8218
8429
  const r = record.resourceLimits;
@@ -8598,7 +8809,7 @@ async function walkFiles(root, prefix = "") {
8598
8809
  const out = [];
8599
8810
  for (const ent of entries) {
8600
8811
  const rel = prefix ? `${prefix}/${ent.name}` : ent.name;
8601
- const full = join15(root, ent.name);
8812
+ const full = join16(root, ent.name);
8602
8813
  if (ent.isDirectory()) {
8603
8814
  out.push(...await walkFiles(full, rel));
8604
8815
  } else if (ent.isFile()) {
@@ -8614,20 +8825,20 @@ async function walkFiles(root, prefix = "") {
8614
8825
  return out;
8615
8826
  }
8616
8827
  async function hashFile(absPath) {
8617
- const buf = await readFile8(absPath);
8618
- return createHash22("sha256").update(buf).digest("hex");
8828
+ const buf = await readFile9(absPath);
8829
+ return createHash32("sha256").update(buf).digest("hex");
8619
8830
  }
8620
8831
  async function buildHostSet(name, hostDir, boxDst) {
8621
8832
  if (hostDir === null) return { dst: boxDst, files: {}, hostDir: null };
8622
8833
  const rels = await walkFiles(hostDir);
8623
8834
  const files = {};
8624
8835
  for (const rel of rels) {
8625
- files[rel] = await hashFile(join15(hostDir, rel));
8836
+ files[rel] = await hashFile(join16(hostDir, rel));
8626
8837
  }
8627
8838
  return { dst: boxDst, files, hostDir };
8628
8839
  }
8629
8840
  async function buildHostSyncManifest(workspacePath, hostHome = homedir12()) {
8630
- const workflowsDir = join15(hostHome, ".claude", "workflows");
8841
+ const workflowsDir = join16(hostHome, ".claude", "workflows");
8631
8842
  const workflowsHost = await pathExists8(workflowsDir) ? workflowsDir : null;
8632
8843
  const memoryHost = await resolveClaudeMemoryDir(workspacePath, hostHome);
8633
8844
  const [workflows, memory] = await Promise.all([
@@ -8649,7 +8860,7 @@ function computeSyncDelta(host, box) {
8649
8860
  uploads.push({
8650
8861
  set: name,
8651
8862
  rel,
8652
- absSrc: join15(hostSet.hostDir, rel),
8863
+ absSrc: join16(hostSet.hostDir, rel),
8653
8864
  dst: `${hostSet.dst}/${rel}`
8654
8865
  });
8655
8866
  }
@@ -8668,15 +8879,15 @@ async function stageDynamicSyncTarball(uploads) {
8668
8879
  return { tarballPath: null, cleanup: async () => {
8669
8880
  } };
8670
8881
  }
8671
- const stageDir = await mkdtemp4(join15(tmpdir4(), "agentbox-dynsync-stage-"));
8882
+ const stageDir = await mkdtemp4(join16(tmpdir4(), "agentbox-dynsync-stage-"));
8672
8883
  let tarballPath = null;
8673
8884
  try {
8674
8885
  for (const up of uploads) {
8675
- const target = join15(stageDir, up.set, up.rel);
8886
+ const target = join16(stageDir, up.set, up.rel);
8676
8887
  await mkdir8(dirname32(target), { recursive: true });
8677
8888
  await copyFile2(up.absSrc, target);
8678
8889
  }
8679
- tarballPath = join15(tmpdir4(), `agentbox-dynsync-${basename6(stageDir)}.tar.gz`);
8890
+ tarballPath = join16(tmpdir4(), `agentbox-dynsync-${basename6(stageDir)}.tar.gz`);
8680
8891
  await execa19("tar", ["-czf", tarballPath, "-C", stageDir, "."], {
8681
8892
  env: { ...process.env, COPYFILE_DISABLE: "1" }
8682
8893
  });
@@ -8742,6 +8953,7 @@ export {
8742
8953
  renderPortsTable,
8743
8954
  loadCarrySection,
8744
8955
  resolveAgentLauncher,
8956
+ UserFacingError,
8745
8957
  BoxNotFoundError,
8746
8958
  AmbiguousBoxError,
8747
8959
  generateBoxId,
@@ -8798,6 +9010,7 @@ export {
8798
9010
  encodeClaudeProjectsKey,
8799
9011
  BOX_CLAUDE_PROJECT_DIR,
8800
9012
  resolveClaudeMemoryDir,
9013
+ stageClaudeJsonOnlyForUpload,
8801
9014
  stageClaudeStaticForUpload,
8802
9015
  stageClaudeCredentialsForUpload,
8803
9016
  stageCodexStaticForUpload,
@@ -8973,4 +9186,4 @@ export {
8973
9186
  browserSessionActive,
8974
9187
  ensureBoxBrowser
8975
9188
  };
8976
- //# sourceMappingURL=chunk-IZXPJPPV.js.map
9189
+ //# sourceMappingURL=chunk-TCS5HXJX.js.map