@madarco/agentbox 0.13.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 +65 -0
  2. package/README.md +11 -8
  3. package/dist/{_cloud-attach-HJC672UR.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-QYRK5H6Q.js → chunk-BYCLD6D6.js} +17 -9
  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-4NQXNQ53.js → chunk-TBSIJVSN.js} +149 -47
  13. package/dist/chunk-TBSIJVSN.js.map +1 -0
  14. package/dist/{chunk-B4QG2MCW.js → chunk-TCS5HXJX.js} +381 -174
  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-OPIBZ7XM.js → dist-3IMQNTTV.js} +14 -69
  23. package/dist/dist-3IMQNTTV.js.map +1 -0
  24. package/dist/{dist-OG6NW6SM.js → dist-4DPOL5A7.js} +5 -3
  25. package/dist/{dist-JAN5VABY.js → dist-57M6ZA7H.js} +25 -177
  26. package/dist/dist-57M6ZA7H.js.map +1 -0
  27. package/dist/{dist-7KVUIKJX.js → dist-J2IHD5T7.js} +37 -226
  28. package/dist/dist-J2IHD5T7.js.map +1 -0
  29. package/dist/index.js +1376 -1029
  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 +8 -6
  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 +40 -16
  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 +40 -16
  55. package/runtime/relay/bin.cjs +297 -228
  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 +40 -16
  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-4NQXNQ53.js.map +0 -1
  64. package/dist/chunk-B4QG2MCW.js.map +0 -1
  65. package/dist/chunk-ECLLV5JH.js.map +0 -1
  66. package/dist/chunk-QYRK5H6Q.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-7KVUIKJX.js.map +0 -1
  70. package/dist/dist-JAN5VABY.js.map +0 -1
  71. package/dist/dist-OPIBZ7XM.js.map +0 -1
  72. /package/dist/{_cloud-attach-HJC672UR.js.map → _cloud-attach-GUBB5RH2.js.map} +0 -0
  73. /package/dist/{dist-OG6NW6SM.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",
@@ -835,8 +838,8 @@ var KEY_REGISTRY = [
835
838
  {
836
839
  key: "box.provider",
837
840
  type: "enum",
838
- enumValues: ["docker", "daytona", "hetzner", "vercel"],
839
- 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."
840
843
  },
841
844
  {
842
845
  key: "box.hostSnapshot",
@@ -872,6 +875,12 @@ var KEY_REGISTRY = [
872
875
  description: "Per-provider override of `box.defaultCheckpoint` for vercel. Wins over the global when set; set via `agentbox checkpoint set-default --provider vercel`.",
873
876
  advanced: true
874
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
+ },
875
884
  {
876
885
  key: "box.size",
877
886
  type: "string",
@@ -901,6 +910,12 @@ var KEY_REGISTRY = [
901
910
  description: "Per-provider override of `box.size` for vercel. Reserved \u2014 vercel sizing is controlled via `box.vercelVcpus`.",
902
911
  advanced: true
903
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
+ },
904
919
  {
905
920
  key: "checkpoint.maxLayers",
906
921
  type: "int",
@@ -972,6 +987,12 @@ var KEY_REGISTRY = [
972
987
  description: "Per-provider override of `box.image` for vercel (snapshot id, e.g. `snap_\u2026`). Written by `agentbox prepare --provider vercel`.",
973
988
  advanced: true
974
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
+ },
975
996
  {
976
997
  key: "box.imageRegistry",
977
998
  type: "string",
@@ -1526,7 +1547,7 @@ function writeLeaf(obj, branch, leaf, value) {
1526
1547
  b[leaf] = value;
1527
1548
  }
1528
1549
  function resolveDefaultCheckpoint(cfg, provider) {
1529
- 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;
1530
1551
  if (perProvider && perProvider.length > 0) return perProvider;
1531
1552
  return cfg.box.defaultCheckpoint;
1532
1553
  }
@@ -1535,15 +1556,16 @@ function defaultCheckpointConfigKey(provider) {
1535
1556
  if (provider === "daytona") return "box.defaultCheckpointDaytona";
1536
1557
  if (provider === "hetzner") return "box.defaultCheckpointHetzner";
1537
1558
  if (provider === "vercel") return "box.defaultCheckpointVercel";
1559
+ if (provider === "e2b") return "box.defaultCheckpointE2b";
1538
1560
  return "box.defaultCheckpoint";
1539
1561
  }
1540
1562
  function resolveBoxSize(cfg, provider) {
1541
- 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;
1542
1564
  if (perProvider && perProvider.length > 0) return perProvider;
1543
1565
  return cfg.box.size;
1544
1566
  }
1545
1567
  function resolveBoxImage(cfg, provider) {
1546
- 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;
1547
1569
  if (perProvider && perProvider.length > 0) return perProvider;
1548
1570
  return cfg.box.image;
1549
1571
  }
@@ -1552,6 +1574,7 @@ function boxImageConfigKey(provider) {
1552
1574
  if (provider === "daytona") return "box.imageDaytona";
1553
1575
  if (provider === "hetzner") return "box.imageHetzner";
1554
1576
  if (provider === "vercel") return "box.imageVercel";
1577
+ if (provider === "e2b") return "box.imageE2b";
1555
1578
  return "box.image";
1556
1579
  }
1557
1580
  async function setConfigValue(scope, key, value, cwd, opts = {}) {
@@ -1752,7 +1775,7 @@ import { copyFile, mkdtemp, readdir as readdir22, readFile as readFile32, rm as
1752
1775
  import { homedir as homedir22, tmpdir } from "os";
1753
1776
  import { basename as basename2, join as join32, relative } from "path";
1754
1777
  import { execa as execa4 } from "execa";
1755
- import { chmod, mkdir as mkdir22, readFile as readFile24 } from "fs/promises";
1778
+ import { chmod, mkdir as mkdir22, readFile as readFile23 } from "fs/promises";
1756
1779
  import { basename as basename3, join as join22 } from "path";
1757
1780
  import { execa as execa3 } from "execa";
1758
1781
  import { spawnSync as spawnSync2 } from "child_process";
@@ -1766,11 +1789,14 @@ import { homedir as homedir5 } from "os";
1766
1789
  import { join as join6 } from "path";
1767
1790
  import { execa as execa7 } from "execa";
1768
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";
1769
1795
  import { execa as execa8 } from "execa";
1770
1796
  import { existsSync } from "fs";
1771
- import { readFile as readFile52 } from "fs/promises";
1797
+ import { readFile as readFile6 } from "fs/promises";
1772
1798
  import { homedir as homedir6 } from "os";
1773
- import { join as join7 } from "path";
1799
+ import { join as join8 } from "path";
1774
1800
  import { execa as execa9 } from "execa";
1775
1801
 
1776
1802
  // ../../packages/core/dist/index.js
@@ -1805,6 +1831,12 @@ function resolveAgentLauncher(kind) {
1805
1831
  if (kind === "opencode") return opencodeLauncher;
1806
1832
  throw new Error(`unknown agent kind: ${String(kind)}`);
1807
1833
  }
1834
+ var UserFacingError = class extends Error {
1835
+ constructor(message) {
1836
+ super(message);
1837
+ this.name = "UserFacingError";
1838
+ }
1839
+ };
1808
1840
  var BoxNotFoundError = class extends Error {
1809
1841
  constructor(query) {
1810
1842
  super(`no agentbox matches "${query}"`);
@@ -1833,10 +1865,10 @@ function generateBoxId() {
1833
1865
  import { execa as execa10 } from "execa";
1834
1866
  import { mkdir as mkdir42, readdir as readdir42, rm as rm32, stat as stat6 } from "fs/promises";
1835
1867
  import { homedir as homedir7, platform } from "os";
1836
- import { join as join8, resolve as resolve2 } from "path";
1837
- 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";
1838
1870
  import { homedir as homedir8, tmpdir as tmpdir3 } from "os";
1839
- import { basename as basename32, join as join9 } from "path";
1871
+ import { basename as basename32, join as join10 } from "path";
1840
1872
  import { execa as execa11 } from "execa";
1841
1873
  import { stat as stat7 } from "fs/promises";
1842
1874
  import { execa as execa12 } from "execa";
@@ -1844,22 +1876,22 @@ import { execa as execa13 } from "execa";
1844
1876
  import { spawn } from "child_process";
1845
1877
  import { randomBytes as randomBytes22 } from "crypto";
1846
1878
  import { existsSync as existsSync2, openSync } from "fs";
1847
- 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";
1848
1880
  import { request as httpRequest } from "http";
1849
1881
  import { createRequire } from "module";
1850
1882
  import { homedir as homedir9 } from "os";
1851
- 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";
1852
1884
  import { setTimeout as delay2 } from "timers/promises";
1853
1885
  import { fileURLToPath } from "url";
1854
1886
 
1855
1887
  // ../../packages/relay/dist/index.js
1856
1888
  import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
1857
1889
  import { execa } from "execa";
1858
- import { spawn as spawn4 } from "child_process";
1890
+ import { spawn as spawn3 } from "child_process";
1859
1891
  import {
1860
1892
  mkdir as mkdir2,
1861
1893
  readdir as readdir2,
1862
- readFile as readFile23,
1894
+ readFile as readFile4,
1863
1895
  rename as rename2,
1864
1896
  unlink,
1865
1897
  writeFile as writeFile2
@@ -1921,7 +1953,7 @@ async function loadQueueConfig() {
1921
1953
  const d = BUILT_IN_DEFAULTS.queue;
1922
1954
  let global = {};
1923
1955
  try {
1924
- global = parseUserConfig(await readFile23(GLOBAL_CONFIG_FILE, "utf8"), GLOBAL_CONFIG_FILE);
1956
+ global = parseUserConfig(await readFile4(GLOBAL_CONFIG_FILE, "utf8"), GLOBAL_CONFIG_FILE);
1925
1957
  } catch {
1926
1958
  }
1927
1959
  const q = global.queue ?? {};
@@ -1941,7 +1973,7 @@ async function writeJob(job) {
1941
1973
  }
1942
1974
  async function readJob(id) {
1943
1975
  try {
1944
- const raw = await readFile23(join3(QUEUE_DIR, `${id}.json`), "utf8");
1976
+ const raw = await readFile4(join3(QUEUE_DIR, `${id}.json`), "utf8");
1945
1977
  return JSON.parse(raw);
1946
1978
  } catch (err) {
1947
1979
  if (err.code === "ENOENT") return null;
@@ -1967,7 +1999,7 @@ async function loadQueue() {
1967
1999
  for (const name of entries) {
1968
2000
  if (!name.endsWith(".json")) continue;
1969
2001
  try {
1970
- const raw = await readFile23(join3(QUEUE_DIR, name), "utf8");
2002
+ const raw = await readFile4(join3(QUEUE_DIR, name), "utf8");
1971
2003
  out.push(JSON.parse(raw));
1972
2004
  } catch {
1973
2005
  }
@@ -1975,6 +2007,8 @@ async function loadQueue() {
1975
2007
  out.sort((a, b) => a.createdAt < b.createdAt ? -1 : a.createdAt > b.createdAt ? 1 : 0);
1976
2008
  return out;
1977
2009
  }
2010
+ var TERMINAL_RETENTION_MS = 60 * 60 * 1e3;
2011
+ var SWEEP_INTERVAL_MS = 60 * 1e3;
1978
2012
  function processAlive(pid) {
1979
2013
  try {
1980
2014
  process.kill(pid, 0);
@@ -2043,7 +2077,7 @@ async function uncachedBoxStateCount() {
2043
2077
  }
2044
2078
  function inspectDockerState(containerName) {
2045
2079
  return new Promise((resolveP) => {
2046
- const child = spawn4("docker", ["inspect", "--format", "{{.State.Status}}", containerName], {
2080
+ const child = spawn3("docker", ["inspect", "--format", "{{.State.Status}}", containerName], {
2047
2081
  stdio: ["ignore", "pipe", "pipe"]
2048
2082
  });
2049
2083
  let out = "";
@@ -2078,19 +2112,19 @@ function queueLogPath(id) {
2078
2112
  // ../../packages/sandbox-docker/dist/index.js
2079
2113
  import { execa as execa16 } from "execa";
2080
2114
  import { readdir as readdir7, rm as rm6, stat as stat9 } from "fs/promises";
2081
- import { join as join13 } from "path";
2115
+ import { join as join14 } from "path";
2082
2116
  import { execa as execa15 } from "execa";
2083
- import { join as join12 } from "path";
2117
+ import { join as join13 } from "path";
2084
2118
  import { homedir as homedir11 } from "os";
2085
- import { join as join14 } from "path";
2119
+ import { join as join15 } from "path";
2086
2120
  import { execa as execa17 } from "execa";
2087
2121
  import { existsSync as existsSync3, mkdirSync, renameSync, statSync } from "fs";
2088
2122
  import { basename as basename5, dirname as dirname22, posix, resolve as resolve4 } from "path";
2089
2123
  import { execa as execa18 } from "execa";
2090
- import { createHash as createHash22 } from "crypto";
2091
- 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";
2092
2126
  import { homedir as homedir12, tmpdir as tmpdir4 } from "os";
2093
- 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";
2094
2128
  import { execa as execa19 } from "execa";
2095
2129
  function isHostPathHookCommand(command, hostHome) {
2096
2130
  if (typeof command !== "string" || command.length === 0) return false;
@@ -3021,7 +3055,7 @@ function isRealAgentCredential(agent, text) {
3021
3055
  }
3022
3056
  async function hostClaudeBackupExpired(path = CREDENTIALS_BACKUP_FILE, now = Date.now()) {
3023
3057
  try {
3024
- const parsed = JSON.parse(await readFile24(path, "utf8"));
3058
+ const parsed = JSON.parse(await readFile23(path, "utf8"));
3025
3059
  const exp = parsed?.claudeAiOauth?.expiresAt;
3026
3060
  return typeof exp === "number" && Number.isFinite(exp) && exp < now;
3027
3061
  } catch {
@@ -3069,7 +3103,7 @@ function extractOpencodeCredentials(volume, image) {
3069
3103
  }
3070
3104
  async function hostBackupHasCredentials(path = CREDENTIALS_BACKUP_FILE) {
3071
3105
  try {
3072
- const parsed = JSON.parse(await readFile24(path, "utf8"));
3106
+ const parsed = JSON.parse(await readFile23(path, "utf8"));
3073
3107
  const rt = parsed?.claudeAiOauth?.refreshToken;
3074
3108
  return typeof rt === "string" && rt.length > 0;
3075
3109
  } catch {
@@ -3241,6 +3275,64 @@ async function resolveClaudeMemoryDir(hostWorkspace, hostHome = homedir22()) {
3241
3275
  }
3242
3276
  return memDir;
3243
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
+ }
3244
3336
  async function stageClaudeStaticForUpload(opts = {}) {
3245
3337
  const hostHome = opts.hostHome ?? homedir22();
3246
3338
  const hostClaude = join32(hostHome, ".claude");
@@ -3273,31 +3365,11 @@ async function stageClaudeStaticForUpload(opts = {}) {
3273
3365
  } catch {
3274
3366
  }
3275
3367
  }
3276
- const hostClaudeJson = join32(hostHome, ".claude.json");
3277
- let working;
3278
- if (await pathExists(hostClaudeJson)) {
3279
- try {
3280
- working = JSON.parse(await readFile32(hostClaudeJson, "utf8"));
3281
- } catch {
3282
- working = null;
3283
- }
3284
- }
3285
- if (working === void 0 || working === null) {
3286
- working = {
3287
- installMethod: "native",
3288
- autoUpdates: false,
3289
- autoUpdatesProtectedForNative: true,
3290
- projects: { [CLOUD_WORKSPACE]: { hasTrustDialogAccepted: true } }
3291
- };
3292
- } else {
3293
- working = filterHostHooks(working, hostHome).data;
3294
- working = setInstallMethodNative(working).data;
3295
- if (opts.hostWorkspace) {
3296
- working = addProjectAlias(working, opts.hostWorkspace, CLOUD_WORKSPACE).data;
3297
- }
3298
- working = trustWorkspace(working, CLOUD_WORKSPACE).data;
3299
- }
3300
- 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));
3301
3373
  const pluginsDir = join32(stageDir, "plugins");
3302
3374
  if (await pathExists(pluginsDir)) {
3303
3375
  try {
@@ -3705,7 +3777,7 @@ async function maybeFilterTo(src, dest, hostHome, opts = {}) {
3705
3777
  };
3706
3778
  let parsed;
3707
3779
  try {
3708
- parsed = JSON.parse(await readFile4(src, "utf8"));
3780
+ parsed = JSON.parse(await readFile42(src, "utf8"));
3709
3781
  } catch {
3710
3782
  return zero;
3711
3783
  }
@@ -3788,7 +3860,7 @@ async function isDir(p) {
3788
3860
  }
3789
3861
  async function readReferencedPluginKeys(installedPluginsJsonPath) {
3790
3862
  try {
3791
- const raw = await readFile4(installedPluginsJsonPath, "utf8");
3863
+ const raw = await readFile42(installedPluginsJsonPath, "utf8");
3792
3864
  return referencedPluginVersionKeys(JSON.parse(raw));
3793
3865
  } catch {
3794
3866
  return /* @__PURE__ */ new Set();
@@ -4263,7 +4335,7 @@ async function listChildDirs(dir) {
4263
4335
  }
4264
4336
  async function readJsonFile(path) {
4265
4337
  try {
4266
- return JSON.parse(await readFile4(path, "utf8"));
4338
+ return JSON.parse(await readFile42(path, "utf8"));
4267
4339
  } catch {
4268
4340
  return void 0;
4269
4341
  }
@@ -5380,6 +5452,75 @@ async function seedWorkspace(opts) {
5380
5452
  }
5381
5453
  }
5382
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
+ }
5383
5524
  async function seedWorkspaceFromDir(opts) {
5384
5525
  const log = opts.onLog ?? (() => {
5385
5526
  });
@@ -5419,6 +5560,12 @@ async function gitIn(container, ct, args) {
5419
5560
  function splitNul(s) {
5420
5561
  return s.split("\0").filter((p) => p.length > 0);
5421
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
+ }
5422
5569
  async function unmergedPaths(container, ct) {
5423
5570
  const r = await gitIn(container, ct, ["diff", "--name-only", "--diff-filter=U", "-z"]);
5424
5571
  return r.exitCode === 0 ? splitNul(r.stdout) : [];
@@ -5515,7 +5662,7 @@ async function resyncWorkspaceFromHost(opts) {
5515
5662
  }
5516
5663
  }
5517
5664
  if (hostUntracked.length > 0) {
5518
- const filter = await execa8(
5665
+ const probe = await execa8(
5519
5666
  "docker",
5520
5667
  [
5521
5668
  "exec",
@@ -5525,15 +5672,42 @@ async function resyncWorkspaceFromHost(opts) {
5525
5672
  opts.container,
5526
5673
  "bash",
5527
5674
  "-c",
5528
- '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',
5529
5676
  "bash",
5530
5677
  ct
5531
5678
  ],
5532
5679
  { input: hostUntracked.join("\0"), reject: false }
5533
5680
  );
5534
- const existing = new Set(filter.exitCode === 0 ? splitNul(filter.stdout) : []);
5535
- const toCopy = hostUntracked.filter((p) => !existing.has(p));
5536
- 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
+ }
5537
5711
  if (toCopy.length > 0) {
5538
5712
  const tarOut = await execa8("tar", ["-C", hostMain, "--null", "-T", "-", "-cf", "-"], {
5539
5713
  input: toCopy.join("\0"),
@@ -5643,7 +5817,7 @@ async function startPortlessProxy() {
5643
5817
  function portlessStateDirCandidates() {
5644
5818
  const env = process.env["PORTLESS_STATE_DIR"];
5645
5819
  if (env && env.trim().length > 0) return [env.trim()];
5646
- return ["/tmp/portless", join7(homedir6(), ".portless")];
5820
+ return ["/tmp/portless", join8(homedir6(), ".portless")];
5647
5821
  }
5648
5822
  function pidAlive(pid) {
5649
5823
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -5656,7 +5830,7 @@ function pidAlive(pid) {
5656
5830
  }
5657
5831
  async function readProxyPid(dir) {
5658
5832
  try {
5659
- const raw = await readFile52(join7(dir, "proxy.pid"), "utf8");
5833
+ const raw = await readFile6(join8(dir, "proxy.pid"), "utf8");
5660
5834
  const pid = Number.parseInt(raw.trim(), 10);
5661
5835
  return Number.isFinite(pid) && pid > 0 ? pid : null;
5662
5836
  } catch {
@@ -5676,7 +5850,7 @@ async function resolvePortlessHostStateDir(override) {
5676
5850
  if (env && env.trim().length > 0) return env.trim();
5677
5851
  const live = await findLivePortlessStateDir();
5678
5852
  if (live) return live;
5679
- const home = join7(homedir6(), ".portless");
5853
+ const home = join8(homedir6(), ".portless");
5680
5854
  if (existsSync(home)) return home;
5681
5855
  if (existsSync("/tmp/portless")) return "/tmp/portless";
5682
5856
  return home;
@@ -5705,12 +5879,12 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
5705
5879
  ".cache",
5706
5880
  ".parcel-cache"
5707
5881
  ]);
5708
- var SNAPSHOTS_ROOT = join8(homedir7(), ".agentbox", "snapshots");
5882
+ var SNAPSHOTS_ROOT = join9(homedir7(), ".agentbox", "snapshots");
5709
5883
  function snapshotPathFor(box) {
5710
5884
  const mnemonic = sanitizeMnemonic(box.name);
5711
5885
  const n = box.projectIndex;
5712
5886
  const segment = typeof n === "number" && Number.isFinite(n) && n > 0 ? `${box.id}-${String(n)}-${mnemonic}` : `${box.id}-${mnemonic}`;
5713
- return join8(SNAPSHOTS_ROOT, segment);
5887
+ return join9(SNAPSHOTS_ROOT, segment);
5714
5888
  }
5715
5889
  async function findExcludedDirs(root, excluded = EXCLUDE_DIRS) {
5716
5890
  const matches = [];
@@ -5723,7 +5897,7 @@ async function findExcludedDirs(root, excluded = EXCLUDE_DIRS) {
5723
5897
  }
5724
5898
  for (const entry of entries) {
5725
5899
  if (!entry.isDirectory()) continue;
5726
- const abs = join8(dir, entry.name);
5900
+ const abs = join9(dir, entry.name);
5727
5901
  if (excluded.has(entry.name)) {
5728
5902
  matches.push(abs);
5729
5903
  continue;
@@ -5745,21 +5919,21 @@ async function createSnapshot(opts) {
5745
5919
  await Promise.all(toPrune.map((p) => rm32(p, { recursive: true, force: true })));
5746
5920
  return { destination, prunedPaths: toPrune };
5747
5921
  }
5748
- var CHECKPOINTS_ROOT = join9(homedir8(), ".agentbox", "checkpoints");
5922
+ var CHECKPOINTS_ROOT = join10(homedir8(), ".agentbox", "checkpoints");
5749
5923
  var CHECKPOINT_IMAGE_PREFIX = "agentbox-ckpt-";
5750
5924
  function checkpointImageTag(projectRoot, name) {
5751
5925
  const mnemonic = sanitizeMnemonic(basename32(projectRoot));
5752
5926
  return `${CHECKPOINT_IMAGE_PREFIX}${hashProjectPath(projectRoot)}_${mnemonic}:${name}`;
5753
5927
  }
5754
5928
  function projectCheckpointsDir(projectRoot) {
5755
- return join9(CHECKPOINTS_ROOT, projectDirSegment(projectRoot));
5929
+ return join10(CHECKPOINTS_ROOT, projectDirSegment(projectRoot));
5756
5930
  }
5757
5931
  function checkpointDir(projectRoot, name) {
5758
- return join9(projectCheckpointsDir(projectRoot), name);
5932
+ return join10(projectCheckpointsDir(projectRoot), name);
5759
5933
  }
5760
5934
  async function readManifest(dir) {
5761
5935
  try {
5762
- const raw = await readFile6(join9(dir, "manifest.json"), "utf8");
5936
+ const raw = await readFile7(join10(dir, "manifest.json"), "utf8");
5763
5937
  const m = JSON.parse(raw);
5764
5938
  if (m.schema !== 2 && m.schema !== 3) return null;
5765
5939
  return m;
@@ -5776,7 +5950,7 @@ async function listCheckpointsInDir(root) {
5776
5950
  }
5777
5951
  const out = [];
5778
5952
  for (const name of entries) {
5779
- const dir = join9(root, name);
5953
+ const dir = join10(root, name);
5780
5954
  const manifest = await readManifest(dir);
5781
5955
  if (manifest) out.push({ name, dir, manifest });
5782
5956
  }
@@ -5795,7 +5969,7 @@ async function listAllCheckpoints() {
5795
5969
  }
5796
5970
  const out = [];
5797
5971
  for (const segment of segments) {
5798
- const items = await listCheckpointsInDir(join9(CHECKPOINTS_ROOT, segment));
5972
+ const items = await listCheckpointsInDir(join10(CHECKPOINTS_ROOT, segment));
5799
5973
  if (items.length > 0) out.push({ segment, items });
5800
5974
  }
5801
5975
  return out;
@@ -5815,7 +5989,7 @@ async function listAllCheckpointImages() {
5815
5989
  }
5816
5990
  const out = /* @__PURE__ */ new Set();
5817
5991
  for (const proj of projectDirs) {
5818
- const projPath = join9(CHECKPOINTS_ROOT, proj);
5992
+ const projPath = join10(CHECKPOINTS_ROOT, proj);
5819
5993
  let names;
5820
5994
  try {
5821
5995
  names = (await readdir5(projPath, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
@@ -5823,7 +5997,7 @@ async function listAllCheckpointImages() {
5823
5997
  continue;
5824
5998
  }
5825
5999
  for (const name of names) {
5826
- const manifest = await readManifest(join9(projPath, name));
6000
+ const manifest = await readManifest(join10(projPath, name));
5827
6001
  if (manifest) out.add(manifest.image);
5828
6002
  }
5829
6003
  }
@@ -5984,7 +6158,7 @@ async function createCheckpoint(opts) {
5984
6158
  cliVersion: stamp.cliVersion,
5985
6159
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
5986
6160
  };
5987
- 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");
5988
6162
  if (opts.setDefault) {
5989
6163
  await setConfigValue("project", "box.defaultCheckpointDocker", name, opts.projectRoot);
5990
6164
  log(`set project default checkpoint (box.defaultCheckpointDocker) -> ${name}`);
@@ -6001,9 +6175,9 @@ async function flattenImage(sourceTag, destTag, log) {
6001
6175
  if (create.exitCode !== 0) {
6002
6176
  throw new CheckpointError(`docker create for flatten failed`, create.stdout, create.stderr);
6003
6177
  }
6004
- const scratch = await mkdtemp3(join9(tmpdir3(), "agentbox-flatten-"));
6178
+ const scratch = await mkdtemp3(join10(tmpdir3(), "agentbox-flatten-"));
6005
6179
  try {
6006
- const rootfsPath = join9(scratch, "rootfs.tar");
6180
+ const rootfsPath = join10(scratch, "rootfs.tar");
6007
6181
  log(`exporting rootfs of ${sourceTag} to ${rootfsPath}`);
6008
6182
  const exp = await execa11("docker", ["export", "-o", rootfsPath, tmpName], { reject: false });
6009
6183
  if (exp.exitCode !== 0) {
@@ -6016,11 +6190,11 @@ async function flattenImage(sourceTag, destTag, log) {
6016
6190
  "ADD rootfs.tar /",
6017
6191
  ...renderConfigDirectives(cfg)
6018
6192
  ];
6019
- await writeFile32(join9(scratch, "Dockerfile"), lines.join("\n") + "\n", "utf8");
6193
+ await writeFile32(join10(scratch, "Dockerfile"), lines.join("\n") + "\n", "utf8");
6020
6194
  log(`building flattened ${destTag} from rootfs.tar (FROM scratch)`);
6021
6195
  const build = await execa11(
6022
6196
  "docker",
6023
- ["build", "-t", destTag, "-f", join9(scratch, "Dockerfile"), scratch],
6197
+ ["build", "-t", destTag, "-f", join10(scratch, "Dockerfile"), scratch],
6024
6198
  { reject: false }
6025
6199
  );
6026
6200
  if (build.exitCode !== 0) {
@@ -6111,10 +6285,10 @@ async function ensureHomeOwnedByVscode(container) {
6111
6285
  { reject: false }
6112
6286
  );
6113
6287
  }
6114
- var STATE_DIR22 = join10(homedir9(), ".agentbox");
6115
- var PID_FILE = join10(STATE_DIR22, "relay.pid");
6116
- var LOG_FILE = join10(STATE_DIR22, "relay.log");
6117
- 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");
6118
6292
  var PORT = DEFAULT_RELAY_PORT;
6119
6293
  var ENDPOINT = {
6120
6294
  // host.docker.internal is the Docker Desktop / OrbStack-supplied alias for
@@ -6281,13 +6455,13 @@ async function stageRelayHome(version, log) {
6281
6455
  if (process.env.AGENTBOX_RELAY_BIN || process.env.AGENTBOX_CLI_ENTRY) return null;
6282
6456
  const cliRoot = findCliRoot(dirname3(fileURLToPath(import.meta.url)));
6283
6457
  if (cliRoot === null) return null;
6284
- const homeDir = join10(RELAY_HOME_DIR, version);
6285
- const stagedEntry = join10(homeDir, "dist", "index.js");
6286
- 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");
6287
6461
  if (existsSync2(stagedEntry) && existsSync2(stagedBin)) {
6288
6462
  return { relayBin: stagedBin, cliEntry: stagedEntry };
6289
6463
  }
6290
- const nodeModules = resolveDepRoot(join10(cliRoot, "dist", "index.js"));
6464
+ const nodeModules = resolveDepRoot(join11(cliRoot, "dist", "index.js"));
6291
6465
  if (nodeModules === null) return null;
6292
6466
  const tmpDir = `${homeDir}.tmp-${String(process.pid)}`;
6293
6467
  try {
@@ -6295,10 +6469,10 @@ async function stageRelayHome(version, log) {
6295
6469
  await rm5(tmpDir, { recursive: true, force: true });
6296
6470
  await mkdir6(tmpDir, { recursive: true });
6297
6471
  for (const sub of ["dist", "runtime", "share"]) {
6298
- const src = join10(cliRoot, sub);
6299
- 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 });
6300
6474
  }
6301
- await cp(nodeModules, join10(tmpDir, "node_modules"), { recursive: true, dereference: true });
6475
+ await cp(nodeModules, join11(tmpDir, "node_modules"), { recursive: true, dereference: true });
6302
6476
  await rm5(homeDir, { recursive: true, force: true });
6303
6477
  await rename3(tmpDir, homeDir);
6304
6478
  } catch (err) {
@@ -6317,7 +6491,7 @@ async function stageRelayHome(version, log) {
6317
6491
  }
6318
6492
  function findCliRoot(moduleDir) {
6319
6493
  for (const root of [resolve22(moduleDir, ".."), resolve22(moduleDir, "..", "..")]) {
6320
- 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"))) {
6321
6495
  return root;
6322
6496
  }
6323
6497
  }
@@ -6332,7 +6506,7 @@ function resolveDepRoot(fromFile) {
6332
6506
  const idx = main.lastIndexOf(marker);
6333
6507
  if (idx === -1) return null;
6334
6508
  const nm = main.slice(0, idx + marker.length - 1);
6335
- return existsSync2(join10(nm, "commander")) ? nm : null;
6509
+ return existsSync2(join11(nm, "commander")) ? nm : null;
6336
6510
  } catch {
6337
6511
  return null;
6338
6512
  }
@@ -6346,7 +6520,7 @@ async function gcOldRelayHomes(keepVersion) {
6346
6520
  }
6347
6521
  for (const name of entries) {
6348
6522
  if (name === keepVersion) continue;
6349
- 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(() => {
6350
6524
  });
6351
6525
  }
6352
6526
  }
@@ -6457,7 +6631,7 @@ function fetchHealthz(timeoutMs) {
6457
6631
  }
6458
6632
  async function readPidFile() {
6459
6633
  try {
6460
- const text = await readFile7(PID_FILE, "utf8");
6634
+ const text = await readFile8(PID_FILE, "utf8");
6461
6635
  const pid = Number.parseInt(text.trim(), 10);
6462
6636
  return Number.isFinite(pid) && pid > 0 ? pid : null;
6463
6637
  } catch {
@@ -6803,7 +6977,7 @@ async function pathExists6(p) {
6803
6977
  async function buildIdentityMounts() {
6804
6978
  const home = homedir10();
6805
6979
  const candidates = [
6806
- { src: join11(home, ".gitconfig"), dst: "/home/vscode/.gitconfig", readOnly: true }
6980
+ { src: join12(home, ".gitconfig"), dst: "/home/vscode/.gitconfig", readOnly: true }
6807
6981
  ];
6808
6982
  const out = [];
6809
6983
  for (const c of candidates) {
@@ -6820,7 +6994,7 @@ async function createBox(opts) {
6820
6994
  if (!await pathExists6(workspace)) {
6821
6995
  throw new Error(`workspace does not exist: ${workspace}`);
6822
6996
  }
6823
- const cfgPath = join11(workspace, "agentbox.yaml");
6997
+ const cfgPath = join12(workspace, "agentbox.yaml");
6824
6998
  if (await pathExists6(cfgPath)) {
6825
6999
  try {
6826
7000
  const cfg = await loadConfig(cfgPath);
@@ -6900,12 +7074,35 @@ async function createBox(opts) {
6900
7074
  }
6901
7075
  let projectIndex;
6902
7076
  if (opts.projectRoot) {
6903
- projectIndex = allocateProjectIndex(await readState(), opts.projectRoot);
7077
+ projectIndex = await reserveProjectIndex(
7078
+ { id, name, container: containerName, image: imageRef, workspacePath: workspace, createdAt },
7079
+ opts.projectRoot
7080
+ );
6904
7081
  }
6905
7082
  const repoCarryOvers = [];
6906
7083
  const gitWorktreeRecords = [];
7084
+ const restoreWorktreePlans = [];
6907
7085
  if (checkpointImage && restoredWorktrees && restoredWorktrees.length > 0) {
6908
- 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
+ }
6909
7106
  }
6910
7107
  if (!checkpointImage) {
6911
7108
  const repos = await detectGitRepos(workspace);
@@ -7013,7 +7210,7 @@ async function createBox(opts) {
7013
7210
  log(`seeded claude credentials into ${claudeSpec.volume} from host backup`);
7014
7211
  }
7015
7212
  const claudeMounts = buildClaudeMounts(claudeSpec, process.env);
7016
- const wantCodex = opts.codexConfig !== void 0 || await pathExists6(join11(homedir10(), ".codex"));
7213
+ const wantCodex = opts.codexConfig !== void 0 || await pathExists6(join12(homedir10(), ".codex"));
7017
7214
  let codexMounts;
7018
7215
  let codexConfigVolume;
7019
7216
  if (wantCodex) {
@@ -7033,7 +7230,7 @@ async function createBox(opts) {
7033
7230
  codexMounts = buildCodexMounts(codexSpec, process.env);
7034
7231
  codexConfigVolume = codexSpec.volume;
7035
7232
  }
7036
- 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"));
7037
7234
  let opencodeMounts;
7038
7235
  let opencodeConfigVolume;
7039
7236
  if (wantOpencode) {
@@ -7054,9 +7251,9 @@ async function createBox(opts) {
7054
7251
  opencodeConfigVolume = opencodeSpec.volume;
7055
7252
  }
7056
7253
  const boxDir = boxRunDirFor({ id, name, projectIndex });
7057
- const socketDir = join11(boxDir, "run");
7058
- const socketPath = join11(socketDir, "ctl.sock");
7059
- const mergedExportDir = join11(boxDir, "workspace");
7254
+ const socketDir = join12(boxDir, "run");
7255
+ const socketPath = join12(socketDir, "ctl.sock");
7256
+ const mergedExportDir = join12(boxDir, "workspace");
7060
7257
  await mkdir7(socketDir, { recursive: true });
7061
7258
  await mkdir7(mergedExportDir, { recursive: true });
7062
7259
  const extraVolumes = await buildIdentityMounts();
@@ -7145,6 +7342,36 @@ async function createBox(opts) {
7145
7342
  effectiveLimits = { ...appliedLimits, disk: null };
7146
7343
  }
7147
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
+ };
7148
7375
  await runBox({
7149
7376
  name: containerName,
7150
7377
  image: imageRef,
@@ -7164,6 +7391,7 @@ async function createBox(opts) {
7164
7391
  }
7165
7392
  });
7166
7393
  log(`container ${containerName} started`);
7394
+ await recordBox(baseRecord);
7167
7395
  if (gitWorktreeRecords.length > 0) {
7168
7396
  await chownGitBindParents({
7169
7397
  container: containerName,
@@ -7189,6 +7417,7 @@ async function createBox(opts) {
7189
7417
  if (opts.useBranch !== void 0) {
7190
7418
  log(`seedWorkspace failed for --use-branch ${opts.useBranch}; cleaning up the box`);
7191
7419
  await execa14("docker", ["rm", "-f", containerName], { reject: false });
7420
+ await removeBoxRecord(id);
7192
7421
  for (const w of gitWorktreeRecords) {
7193
7422
  await removeInBoxWorktree({
7194
7423
  hostMainRepo: w.hostMainRepo,
@@ -7204,21 +7433,27 @@ async function createBox(opts) {
7204
7433
  const source = snapshotDir ?? workspace;
7205
7434
  await seedWorkspaceFromDir({ container: containerName, hostSource: source, onLog: log });
7206
7435
  }
7207
- } 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
+ });
7208
7443
  await bindWorktrees(
7209
7444
  containerName,
7210
- restoredWorktrees.map((w) => ({
7445
+ gitWorktreeRecords.map((w) => ({
7211
7446
  kind: w.kind,
7212
7447
  containerPath: w.containerPath,
7213
7448
  gitWorktreePath: w.gitWorktreePath
7214
7449
  })),
7215
7450
  log
7216
7451
  );
7217
- log("re-bound /workspace from checkpoint image");
7452
+ log("re-bound /workspace from checkpoint image (fresh per-box worktree)");
7218
7453
  if (opts.resyncOnStart !== false) {
7219
7454
  const repos = await resyncWorkspaceFromHost({
7220
7455
  container: containerName,
7221
- worktrees: restoredWorktrees,
7456
+ worktrees: gitWorktreeRecords,
7222
7457
  onLog: log
7223
7458
  });
7224
7459
  resyncResult = {
@@ -7233,15 +7468,15 @@ async function createBox(opts) {
7233
7468
  }
7234
7469
  await repairIdeOwnership(containerName);
7235
7470
  log(".vscode-server + .cursor-server ownership verified");
7236
- const ctl = await launchCtlDaemon(containerName, socketPath);
7237
- if (ctl.up) log("agentbox-ctl daemon up");
7238
- else log(`agentbox-ctl daemon did not become reachable: ${ctl.reason}`);
7239
7471
  const dockerd = await launchDockerdDaemon(containerName);
7240
7472
  if (dockerd.up) {
7241
7473
  log(`dockerd up (data root=${dockerVolume})`);
7242
7474
  } else {
7243
7475
  log(`dockerd did not become ready: ${dockerd.reason}`);
7244
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}`);
7245
7480
  if (opts.withPlaywright) {
7246
7481
  log("installing @playwright/cli@latest (--with-playwright)");
7247
7482
  const result = await execa14(
@@ -7360,41 +7595,14 @@ async function createBox(opts) {
7360
7595
  }
7361
7596
  }
7362
7597
  const record = {
7363
- id,
7364
- name,
7365
- container: containerName,
7366
- image: imageRef,
7367
- workspacePath: workspace,
7368
- snapshotDir,
7369
- socketPath,
7370
- claudeConfigVolume: claudeSpec.volume,
7371
- codexConfigVolume,
7372
- opencodeConfigVolume,
7373
- vscodeServerVolume: vscodeServerVolumeName(id),
7374
- cursorServerVolume: cursorServerVolumeName(id),
7375
- relayToken: relayUp ? relayToken : void 0,
7376
- gitWorktrees: gitWorktreeRecords.length > 0 ? gitWorktreeRecords : void 0,
7377
- withPlaywright: opts.withPlaywright ? true : void 0,
7378
- withEnv: opts.withEnv ? true : void 0,
7598
+ ...baseRecord,
7379
7599
  carry: carrySummary,
7380
- vncEnabled: vncEnabled ? true : void 0,
7381
- vncContainerPort: vncEnabled ? VNC_CONTAINER_PORT : void 0,
7382
7600
  vncHostPort: vncHostPort ?? void 0,
7383
- vncPassword,
7384
- webContainerPort: WEB_CONTAINER_PORT,
7385
7601
  webHostPort: webHostPort ?? void 0,
7386
7602
  portlessAlias: portlessAliasName,
7387
7603
  portlessUrl,
7388
7604
  portlessVncAlias: portlessVncAliasName,
7389
- portlessVncUrl,
7390
- dockerVolume,
7391
- dockerCacheShared: dockerCacheShared || void 0,
7392
- projectRoot: opts.projectRoot,
7393
- projectIndex,
7394
- checkpointImage,
7395
- checkpointSource,
7396
- resourceLimits: persistableLimits(effectiveLimits),
7397
- createdAt
7605
+ portlessVncUrl
7398
7606
  };
7399
7607
  await recordBox(record);
7400
7608
  return { record, imageBuilt: built, resync: resyncResult };
@@ -7580,7 +7788,7 @@ async function getBoxEndpoints(record, engine, persisted) {
7580
7788
  for (const svc of persistedServices) pushService(svc.name, svc.port);
7581
7789
  } else {
7582
7790
  try {
7583
- const cfg = await loadConfig(join12(record.workspacePath, "agentbox.yaml"));
7791
+ const cfg = await loadConfig(join13(record.workspacePath, "agentbox.yaml"));
7584
7792
  if (!webServiceName) {
7585
7793
  webServiceName = cfg.services.find((s) => s.expose)?.name ?? null;
7586
7794
  }
@@ -7731,9 +7939,9 @@ async function resyncBox(idOrName, onLog) {
7731
7939
  async function startBox(idOrName) {
7732
7940
  const box = await resolveBox(idOrName);
7733
7941
  for (const w of box.gitWorktrees ?? []) {
7734
- if (!await pathExists7(join13(w.hostMainRepo, ".git"))) {
7942
+ if (!await pathExists7(join14(w.hostMainRepo, ".git"))) {
7735
7943
  throw new Error(
7736
- `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)`
7737
7945
  );
7738
7946
  }
7739
7947
  }
@@ -7749,12 +7957,12 @@ async function startBox(idOrName) {
7749
7957
  );
7750
7958
  }
7751
7959
  await ensureHomeOwnedByVscode(box.container);
7752
- if (box.socketPath) {
7753
- await launchCtlDaemon(box.container, box.socketPath);
7754
- }
7755
7960
  if (box.dockerVolume) {
7756
7961
  await launchDockerdDaemon(box.container);
7757
7962
  }
7963
+ if (box.socketPath) {
7964
+ await launchCtlDaemon(box.container, box.socketPath);
7965
+ }
7758
7966
  if (box.vncEnabled) {
7759
7967
  await launchVncDaemon(box.container);
7760
7968
  const freshHostPort = await publishedHostPort(box.container, VNC_CONTAINER_PORT);
@@ -7902,16 +8110,13 @@ async function destroyBox(idOrName, opts = {}) {
7902
8110
  } catch {
7903
8111
  }
7904
8112
  }
7905
- const ownsWorktrees = !box.checkpointImage;
7906
- if (ownsWorktrees) {
7907
- for (const w of box.gitWorktrees ?? []) {
7908
- try {
7909
- await removeInBoxWorktree({
7910
- hostMainRepo: w.hostMainRepo,
7911
- gitWorktreePath: w.gitWorktreePath
7912
- });
7913
- } catch {
7914
- }
8113
+ for (const w of box.gitWorktrees ?? []) {
8114
+ try {
8115
+ await removeInBoxWorktree({
8116
+ hostMainRepo: w.hostMainRepo,
8117
+ gitWorktreePath: w.gitWorktreePath
8118
+ });
8119
+ } catch {
7915
8120
  }
7916
8121
  }
7917
8122
  const beforeContainer = await inspectContainerStatus(box.container);
@@ -7962,7 +8167,7 @@ async function destroyBox(idOrName, opts = {}) {
7962
8167
  async function listSnapshotDirs() {
7963
8168
  try {
7964
8169
  const entries = await readdir7(SNAPSHOTS_ROOT, { withFileTypes: true });
7965
- 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));
7966
8171
  } catch {
7967
8172
  return [];
7968
8173
  }
@@ -7970,7 +8175,7 @@ async function listSnapshotDirs() {
7970
8175
  async function listBoxDirs() {
7971
8176
  try {
7972
8177
  const entries = await readdir7(BOXES_ROOT, { withFileTypes: true });
7973
- 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));
7974
8179
  } catch {
7975
8180
  return [];
7976
8181
  }
@@ -8155,7 +8360,7 @@ async function volumeSizeBytes(name) {
8155
8360
  if (!name) return null;
8156
8361
  const engine = await detectEngine();
8157
8362
  if (engine === "orbstack") {
8158
- const live = join14(homedir11(), "OrbStack", "docker", "volumes", name);
8363
+ const live = join15(homedir11(), "OrbStack", "docker", "volumes", name);
8159
8364
  const sz = await duBytes(live);
8160
8365
  if (sz !== null) return sz;
8161
8366
  }
@@ -8218,7 +8423,7 @@ async function allCheckpointImagesBytes() {
8218
8423
  return any ? total : null;
8219
8424
  }
8220
8425
  async function agentboxHomeBytes() {
8221
- return duBytes(join14(homedir11(), ".agentbox"));
8426
+ return duBytes(join15(homedir11(), ".agentbox"));
8222
8427
  }
8223
8428
  function limitsFromRecord(record) {
8224
8429
  const r = record.resourceLimits;
@@ -8604,7 +8809,7 @@ async function walkFiles(root, prefix = "") {
8604
8809
  const out = [];
8605
8810
  for (const ent of entries) {
8606
8811
  const rel = prefix ? `${prefix}/${ent.name}` : ent.name;
8607
- const full = join15(root, ent.name);
8812
+ const full = join16(root, ent.name);
8608
8813
  if (ent.isDirectory()) {
8609
8814
  out.push(...await walkFiles(full, rel));
8610
8815
  } else if (ent.isFile()) {
@@ -8620,20 +8825,20 @@ async function walkFiles(root, prefix = "") {
8620
8825
  return out;
8621
8826
  }
8622
8827
  async function hashFile(absPath) {
8623
- const buf = await readFile8(absPath);
8624
- return createHash22("sha256").update(buf).digest("hex");
8828
+ const buf = await readFile9(absPath);
8829
+ return createHash32("sha256").update(buf).digest("hex");
8625
8830
  }
8626
8831
  async function buildHostSet(name, hostDir, boxDst) {
8627
8832
  if (hostDir === null) return { dst: boxDst, files: {}, hostDir: null };
8628
8833
  const rels = await walkFiles(hostDir);
8629
8834
  const files = {};
8630
8835
  for (const rel of rels) {
8631
- files[rel] = await hashFile(join15(hostDir, rel));
8836
+ files[rel] = await hashFile(join16(hostDir, rel));
8632
8837
  }
8633
8838
  return { dst: boxDst, files, hostDir };
8634
8839
  }
8635
8840
  async function buildHostSyncManifest(workspacePath, hostHome = homedir12()) {
8636
- const workflowsDir = join15(hostHome, ".claude", "workflows");
8841
+ const workflowsDir = join16(hostHome, ".claude", "workflows");
8637
8842
  const workflowsHost = await pathExists8(workflowsDir) ? workflowsDir : null;
8638
8843
  const memoryHost = await resolveClaudeMemoryDir(workspacePath, hostHome);
8639
8844
  const [workflows, memory] = await Promise.all([
@@ -8655,7 +8860,7 @@ function computeSyncDelta(host, box) {
8655
8860
  uploads.push({
8656
8861
  set: name,
8657
8862
  rel,
8658
- absSrc: join15(hostSet.hostDir, rel),
8863
+ absSrc: join16(hostSet.hostDir, rel),
8659
8864
  dst: `${hostSet.dst}/${rel}`
8660
8865
  });
8661
8866
  }
@@ -8674,15 +8879,15 @@ async function stageDynamicSyncTarball(uploads) {
8674
8879
  return { tarballPath: null, cleanup: async () => {
8675
8880
  } };
8676
8881
  }
8677
- const stageDir = await mkdtemp4(join15(tmpdir4(), "agentbox-dynsync-stage-"));
8882
+ const stageDir = await mkdtemp4(join16(tmpdir4(), "agentbox-dynsync-stage-"));
8678
8883
  let tarballPath = null;
8679
8884
  try {
8680
8885
  for (const up of uploads) {
8681
- const target = join15(stageDir, up.set, up.rel);
8886
+ const target = join16(stageDir, up.set, up.rel);
8682
8887
  await mkdir8(dirname32(target), { recursive: true });
8683
8888
  await copyFile2(up.absSrc, target);
8684
8889
  }
8685
- tarballPath = join15(tmpdir4(), `agentbox-dynsync-${basename6(stageDir)}.tar.gz`);
8890
+ tarballPath = join16(tmpdir4(), `agentbox-dynsync-${basename6(stageDir)}.tar.gz`);
8686
8891
  await execa19("tar", ["-czf", tarballPath, "-C", stageDir, "."], {
8687
8892
  env: { ...process.env, COPYFILE_DISABLE: "1" }
8688
8893
  });
@@ -8748,6 +8953,7 @@ export {
8748
8953
  renderPortsTable,
8749
8954
  loadCarrySection,
8750
8955
  resolveAgentLauncher,
8956
+ UserFacingError,
8751
8957
  BoxNotFoundError,
8752
8958
  AmbiguousBoxError,
8753
8959
  generateBoxId,
@@ -8804,6 +9010,7 @@ export {
8804
9010
  encodeClaudeProjectsKey,
8805
9011
  BOX_CLAUDE_PROJECT_DIR,
8806
9012
  resolveClaudeMemoryDir,
9013
+ stageClaudeJsonOnlyForUpload,
8807
9014
  stageClaudeStaticForUpload,
8808
9015
  stageClaudeCredentialsForUpload,
8809
9016
  stageCodexStaticForUpload,
@@ -8979,4 +9186,4 @@ export {
8979
9186
  browserSessionActive,
8980
9187
  ensureBoxBrowser
8981
9188
  };
8982
- //# sourceMappingURL=chunk-B4QG2MCW.js.map
9189
+ //# sourceMappingURL=chunk-TCS5HXJX.js.map