@madarco/agentbox 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/{_cloud-attach-GUBB5RH2.js → _cloud-attach-R6TRWG5L.js} +3 -3
  3. package/dist/{chunk-BYCLD6D6.js → chunk-43Q5GWP6.js} +98 -54
  4. package/dist/chunk-43Q5GWP6.js.map +1 -0
  5. package/dist/{chunk-VATTS2MR.js → chunk-72CJTXN6.js} +2 -2
  6. package/dist/{chunk-TBSIJVSN.js → chunk-E7CHS7ZR.js} +21 -13
  7. package/dist/chunk-E7CHS7ZR.js.map +1 -0
  8. package/dist/{chunk-LDMYHWUS.js → chunk-MCOU6CZS.js} +2 -2
  9. package/dist/{chunk-TCS5HXJX.js → chunk-MLMFNN4T.js} +396 -324
  10. package/dist/chunk-MLMFNN4T.js.map +1 -0
  11. package/dist/{dist-J2IHD5T7.js → dist-AGTIA7AD.js} +3 -3
  12. package/dist/{dist-3IMQNTTV.js → dist-FIFEFKJ7.js} +3 -3
  13. package/dist/{dist-34RKQ74M.js → dist-JZ3XO6EB.js} +4 -4
  14. package/dist/{dist-4DPOL5A7.js → dist-OGJGZETZ.js} +2 -2
  15. package/dist/{dist-57M6ZA7H.js → dist-S4XR4ACV.js} +4 -4
  16. package/dist/index.js +868 -300
  17. package/dist/index.js.map +1 -1
  18. package/package.json +5 -5
  19. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +30 -0
  20. package/runtime/docker/packages/ctl/dist/bin.cjs +321 -27
  21. package/runtime/e2b/agentbox-setup-skill.md +30 -0
  22. package/runtime/e2b/ctl.cjs +321 -27
  23. package/runtime/hetzner/agentbox-setup-skill.md +30 -0
  24. package/runtime/hetzner/ctl.cjs +321 -27
  25. package/runtime/relay/bin.cjs +83 -5
  26. package/runtime/vercel/agentbox-setup-skill.md +30 -0
  27. package/runtime/vercel/ctl.cjs +321 -27
  28. package/share/agentbox-setup/SKILL.md +30 -0
  29. package/share/host-skills/agentbox-info/SKILL.md +21 -1
  30. package/dist/chunk-BYCLD6D6.js.map +0 -1
  31. package/dist/chunk-TBSIJVSN.js.map +0 -1
  32. package/dist/chunk-TCS5HXJX.js.map +0 -1
  33. /package/dist/{_cloud-attach-GUBB5RH2.js.map → _cloud-attach-R6TRWG5L.js.map} +0 -0
  34. /package/dist/{chunk-VATTS2MR.js.map → chunk-72CJTXN6.js.map} +0 -0
  35. /package/dist/{chunk-LDMYHWUS.js.map → chunk-MCOU6CZS.js.map} +0 -0
  36. /package/dist/{dist-J2IHD5T7.js.map → dist-AGTIA7AD.js.map} +0 -0
  37. /package/dist/{dist-3IMQNTTV.js.map → dist-FIFEFKJ7.js.map} +0 -0
  38. /package/dist/{dist-34RKQ74M.js.map → dist-JZ3XO6EB.js.map} +0 -0
  39. /package/dist/{dist-4DPOL5A7.js.map → dist-OGJGZETZ.js.map} +0 -0
  40. /package/dist/{dist-57M6ZA7H.js.map → dist-S4XR4ACV.js.map} +0 -0
@@ -24,8 +24,8 @@ import {
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 join12, resolve as resolve3 } from "path";
28
- import { execa as execa14 } from "execa";
27
+ import { basename as basename5, join as join12, resolve as resolve4 } from "path";
28
+ import { execa as execa15 } from "execa";
29
29
 
30
30
  // ../../packages/ctl/dist/index.js
31
31
  import { readFile } from "fs/promises";
@@ -517,7 +517,21 @@ var CarryConfigError = class extends Error {
517
517
  this.name = "CarryConfigError";
518
518
  }
519
519
  };
520
- var ITEM_KEYS = /* @__PURE__ */ new Set(["src", "dest", "mode", "user", "optional"]);
520
+ var ITEM_KEYS = /* @__PURE__ */ new Set(["src", "dest", "mode", "user", "exclude", "optional"]);
521
+ function parseExclude(raw, where) {
522
+ if (raw === void 0 || raw === null) return void 0;
523
+ if (!Array.isArray(raw)) {
524
+ throw new CarryConfigError(`${where}.exclude must be a list of glob/name strings`);
525
+ }
526
+ const out = [];
527
+ for (const [i, v] of raw.entries()) {
528
+ if (typeof v !== "string" || v.trim().length === 0) {
529
+ throw new CarryConfigError(`${where}.exclude[${String(i)}] must be a non-empty string`);
530
+ }
531
+ out.push(v.trim());
532
+ }
533
+ return out.length > 0 ? out : void 0;
534
+ }
521
535
  function parseUser(raw, where) {
522
536
  if (raw === void 0 || raw === null) return void 0;
523
537
  let n;
@@ -646,6 +660,7 @@ function parseMapping(raw, where) {
646
660
  assertDestShape(dest, where);
647
661
  const mode = parseMode(raw.mode, where);
648
662
  const user = parseUser(raw.user, where);
663
+ const exclude = parseExclude(raw.exclude, where);
649
664
  let optional = false;
650
665
  if (raw.optional !== void 0 && raw.optional !== null) {
651
666
  if (typeof raw.optional !== "boolean") {
@@ -656,6 +671,7 @@ function parseMapping(raw, where) {
656
671
  const out = { src, dest, optional };
657
672
  if (mode !== void 0) out.mode = mode;
658
673
  if (user !== void 0) out.user = user;
674
+ if (exclude !== void 0) out.exclude = exclude;
659
675
  return out;
660
676
  }
661
677
  function parseCarryRaw(raw) {
@@ -702,20 +718,6 @@ async function loadCarrySection(path) {
702
718
  return parseCarrySection(text);
703
719
  }
704
720
 
705
- // ../../packages/sandbox-docker/dist/index.js
706
- import { spawnSync } from "child_process";
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
- import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
709
- import { isAbsolute as isAbsolute2, join as join4, relative as relative2 } from "path";
710
- import { setTimeout as delay } from "timers/promises";
711
- import { execa as execa5 } from "execa";
712
- import { execa as execa2 } from "execa";
713
- import { mkdir as mkdir4, readFile as readFile5, readdir as readdir4, stat as stat4 } from "fs/promises";
714
- import { createHash as createHash3 } from "crypto";
715
- import { homedir as homedir2 } from "os";
716
- import { join as join5 } from "path";
717
- import { execa as execa22 } from "execa";
718
-
719
721
  // ../../packages/config/dist/index.js
720
722
  import { parse as parseYaml3 } from "yaml";
721
723
  import { createHash } from "crypto";
@@ -748,6 +750,7 @@ var BUILT_IN_DEFAULTS = {
748
750
  withEnv: false,
749
751
  resyncOnStart: true,
750
752
  vnc: true,
753
+ autoApproveHostActions: false,
751
754
  isolateClaudeConfig: false,
752
755
  isolateCodexConfig: false,
753
756
  isolateOpencodeConfig: false,
@@ -768,7 +771,8 @@ var BUILT_IN_DEFAULTS = {
768
771
  bundleDepth: void 0,
769
772
  vercelVcpus: 2,
770
773
  vercelTimeoutMs: 27e5,
771
- vercelNetworkPolicy: ""
774
+ vercelNetworkPolicy: "",
775
+ cpMaxBytes: 100 * 1024 * 1024
772
776
  },
773
777
  checkpoint: {
774
778
  maxLayers: 3
@@ -824,7 +828,8 @@ var BUILT_IN_DEFAULTS = {
824
828
  enabled: true,
825
829
  maxConcurrent: 5,
826
830
  maxWorking: 0,
827
- idleGraceSeconds: 15
831
+ idleGraceSeconds: 15,
832
+ openIn: "none"
828
833
  },
829
834
  cloud: {
830
835
  useCurrentBranch: false
@@ -942,6 +947,11 @@ var KEY_REGISTRY = [
942
947
  type: "bool",
943
948
  description: "Run the per-box Xvnc + noVNC stack."
944
949
  },
950
+ {
951
+ key: "box.autoApproveHostActions",
952
+ type: "bool",
953
+ description: "Auto-approve host-action confirmations (git push, cp host<->box, gh PR writes, checkpoint) for this box without an interactive prompt. Off by default; intended for unattended orchestration of trusted boxes. Each auto-approval is recorded as a relay event (visible in `agentbox agent` / the dashboard)."
954
+ },
945
955
  {
946
956
  key: "box.isolateClaudeConfig",
947
957
  type: "bool",
@@ -1040,6 +1050,12 @@ var KEY_REGISTRY = [
1040
1050
  type: "int",
1041
1051
  description: "Max session length (ms) for new --provider vercel boxes before the VM auto-snapshots; persistent mode auto-resumes on the next call. Default 2700000 (45 min, the Hobby ceiling). Vercel-only."
1042
1052
  },
1053
+ {
1054
+ key: "box.cpMaxBytes",
1055
+ type: "int",
1056
+ description: "Max bytes a single host\u2192box copy may transfer after excludes, shared by `agentbox cp` (blocked with a size breakdown unless --yes) and each `carry:` entry (rejected at resolve time). Default 104857600 (100 MiB).",
1057
+ advanced: true
1058
+ },
1043
1059
  {
1044
1060
  key: "box.vercelNetworkPolicy",
1045
1061
  type: "string",
@@ -1187,6 +1203,12 @@ var KEY_REGISTRY = [
1187
1203
  type: "int",
1188
1204
  description: "Seconds an agent must stay non-working before it frees its working slot (debounce against brief idle flaps between turns). Only used when queue.maxWorking > 0."
1189
1205
  },
1206
+ {
1207
+ key: "queue.openIn",
1208
+ type: "enum",
1209
+ enumValues: ["none", "split", "window", "tab"],
1210
+ description: "When a background `-i` job finishes creating its box, where the host relay opens an attached terminal onto it: `none` (default \u2014 open nothing, just queue), `split`, `window`, or `tab`. Honored only when the submitting shell runs inside tmux, cmux, or iTerm2 (the targeting is captured at submit time). Under cmux, `split` splits the pane you submitted from (falling back to the parent workspace, then a new workspace), `tab` adds a tab in the parent workspace, and `window` opens a separate workspace; iTerm2 opens relative to the frontmost window. Unlike `attach.openIn` there is no `same` mode \u2014 the box is created asynchronously, so it is always a fresh terminal."
1211
+ },
1190
1212
  {
1191
1213
  key: "cloud.useCurrentBranch",
1192
1214
  type: "bool",
@@ -1771,33 +1793,48 @@ async function touchProjectMeta(absPath) {
1771
1793
  }
1772
1794
 
1773
1795
  // ../../packages/sandbox-docker/dist/index.js
1796
+ import { spawnSync } from "child_process";
1797
+ 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";
1798
+ import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
1799
+ import { isAbsolute as isAbsolute2, join as join4, relative as relative2 } from "path";
1800
+ import { setTimeout as delay } from "timers/promises";
1801
+ import { execa as execa6 } from "execa";
1802
+ import { execa as execa2 } from "execa";
1803
+ import { mkdir as mkdir4, readFile as readFile5, readdir as readdir4, stat as stat4 } from "fs/promises";
1804
+ import { createHash as createHash3 } from "crypto";
1805
+ import { homedir as homedir2 } from "os";
1806
+ import { join as join5 } from "path";
1807
+ import { execa as execa3 } from "execa";
1808
+ import { existsSync, mkdirSync, renameSync, statSync } from "fs";
1809
+ import { basename as basename2, dirname as dirname3, posix, resolve as resolve2 } from "path";
1810
+ import { execa as execa22 } from "execa";
1774
1811
  import { copyFile, mkdtemp, readdir as readdir22, readFile as readFile32, rm as rm3, stat as stat22, writeFile as writeFile3 } from "fs/promises";
1775
1812
  import { homedir as homedir22, tmpdir } from "os";
1776
- import { basename as basename2, join as join32, relative } from "path";
1777
- import { execa as execa4 } from "execa";
1813
+ import { basename as basename3, join as join32, relative } from "path";
1814
+ import { execa as execa5 } from "execa";
1778
1815
  import { chmod, mkdir as mkdir22, readFile as readFile23 } from "fs/promises";
1779
- import { basename as basename3, join as join22 } from "path";
1780
- import { execa as execa3 } from "execa";
1816
+ import { basename as basename22, join as join22 } from "path";
1817
+ import { execa as execa4 } from "execa";
1781
1818
  import { spawnSync as spawnSync2 } from "child_process";
1782
1819
  import { stat as stat42 } from "fs/promises";
1783
1820
  import { homedir as homedir4 } from "os";
1784
1821
  import { join as join52 } from "path";
1785
- import { execa as execa6 } from "execa";
1822
+ import { execa as execa7 } from "execa";
1786
1823
  import { spawnSync as spawnSync3 } from "child_process";
1787
1824
  import { stat as stat5 } from "fs/promises";
1788
1825
  import { homedir as homedir5 } from "os";
1789
1826
  import { join as join6 } from "path";
1790
- import { execa as execa7 } from "execa";
1827
+ import { execa as execa8 } from "execa";
1791
1828
  import { randomBytes as randomBytes3 } from "crypto";
1792
1829
  import { createHash as createHash22 } from "crypto";
1793
1830
  import { readFile as readFile52 } from "fs/promises";
1794
1831
  import { join as join7 } from "path";
1795
- import { execa as execa8 } from "execa";
1796
- import { existsSync } from "fs";
1832
+ import { execa as execa9 } from "execa";
1833
+ import { existsSync as existsSync2 } from "fs";
1797
1834
  import { readFile as readFile6 } from "fs/promises";
1798
1835
  import { homedir as homedir6 } from "os";
1799
1836
  import { join as join8 } from "path";
1800
- import { execa as execa9 } from "execa";
1837
+ import { execa as execa10 } from "execa";
1801
1838
 
1802
1839
  // ../../packages/core/dist/index.js
1803
1840
  import { randomBytes } from "crypto";
@@ -1862,25 +1899,25 @@ function generateBoxId() {
1862
1899
  }
1863
1900
 
1864
1901
  // ../../packages/sandbox-docker/dist/index.js
1865
- import { execa as execa10 } from "execa";
1902
+ import { execa as execa11 } from "execa";
1866
1903
  import { mkdir as mkdir42, readdir as readdir42, rm as rm32, stat as stat6 } from "fs/promises";
1867
1904
  import { homedir as homedir7, platform } from "os";
1868
- import { join as join9, resolve as resolve2 } from "path";
1905
+ import { join as join9, resolve as resolve22 } from "path";
1869
1906
  import { mkdir as mkdir5, mkdtemp as mkdtemp3, readFile as readFile7, readdir as readdir5, rm as rm4, writeFile as writeFile32 } from "fs/promises";
1870
1907
  import { homedir as homedir8, tmpdir as tmpdir3 } from "os";
1871
- import { basename as basename32, join as join10 } from "path";
1872
- import { execa as execa11 } from "execa";
1873
- import { stat as stat7 } from "fs/promises";
1908
+ import { basename as basename4, join as join10 } from "path";
1874
1909
  import { execa as execa12 } from "execa";
1910
+ import { stat as stat7 } from "fs/promises";
1875
1911
  import { execa as execa13 } from "execa";
1912
+ import { execa as execa14 } from "execa";
1876
1913
  import { spawn } from "child_process";
1877
1914
  import { randomBytes as randomBytes22 } from "crypto";
1878
- import { existsSync as existsSync2, openSync } from "fs";
1915
+ import { existsSync as existsSync3, openSync } from "fs";
1879
1916
  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";
1880
1917
  import { request as httpRequest } from "http";
1881
1918
  import { createRequire } from "module";
1882
1919
  import { homedir as homedir9 } from "os";
1883
- import { dirname as dirname3, join as join11, resolve as resolve22, sep } from "path";
1920
+ import { dirname as dirname22, join as join11, resolve as resolve3, sep } from "path";
1884
1921
  import { setTimeout as delay2 } from "timers/promises";
1885
1922
  import { fileURLToPath } from "url";
1886
1923
 
@@ -2110,16 +2147,13 @@ function queueLogPath(id) {
2110
2147
  }
2111
2148
 
2112
2149
  // ../../packages/sandbox-docker/dist/index.js
2113
- import { execa as execa16 } from "execa";
2150
+ import { execa as execa17 } from "execa";
2114
2151
  import { readdir as readdir7, rm as rm6, stat as stat9 } from "fs/promises";
2115
2152
  import { join as join14 } from "path";
2116
- import { execa as execa15 } from "execa";
2153
+ import { execa as execa16 } from "execa";
2117
2154
  import { join as join13 } from "path";
2118
2155
  import { homedir as homedir11 } from "os";
2119
2156
  import { join as join15 } from "path";
2120
- import { execa as execa17 } from "execa";
2121
- import { existsSync as existsSync3, mkdirSync, renameSync, statSync } from "fs";
2122
- import { basename as basename5, dirname as dirname22, posix, resolve as resolve4 } from "path";
2123
2157
  import { execa as execa18 } from "execa";
2124
2158
  import { createHash as createHash32 } from "crypto";
2125
2159
  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";
@@ -2520,11 +2554,144 @@ async function listAgentboxVolumes() {
2520
2554
  if (result.exitCode !== 0) return [];
2521
2555
  return (result.stdout ?? "").split("\n").map((s) => s.trim()).filter((s) => s.startsWith(AGENTBOX_PREFIX));
2522
2556
  }
2557
+ function posixDirname(p) {
2558
+ return posix.dirname(p) || "/";
2559
+ }
2560
+ function asText(s) {
2561
+ if (s === void 0) return "";
2562
+ if (typeof s === "string") return s;
2563
+ return Buffer.from(s).toString("utf8");
2564
+ }
2565
+ function tarExcludeArgs(exclude) {
2566
+ return (exclude ?? []).map((p) => `--exclude=${p}`);
2567
+ }
2568
+ async function streamTarPipe(producerFile, producerArgs, consumerFile, consumerArgs, producerEnv) {
2569
+ const producer = execa22(producerFile, producerArgs, {
2570
+ reject: false,
2571
+ buffer: { stdout: false },
2572
+ ...producerEnv ? { env: producerEnv } : {}
2573
+ });
2574
+ const consumer = execa22(consumerFile, consumerArgs, {
2575
+ reject: false,
2576
+ buffer: { stdout: false }
2577
+ });
2578
+ producer.stdout?.pipe(consumer.stdin);
2579
+ const [p, c] = await Promise.all([producer, consumer]);
2580
+ return [
2581
+ { exitCode: p.exitCode, stderr: p.stderr },
2582
+ { exitCode: c.exitCode, stderr: c.stderr }
2583
+ ];
2584
+ }
2585
+ async function uploadToBox(box, hostSrc, boxDst, exclude) {
2586
+ const srcAbs = resolve2(hostSrc);
2587
+ if (!existsSync(srcAbs)) throw new Error(`source not found: ${hostSrc}`);
2588
+ const srcBasename = basename2(srcAbs);
2589
+ const srcParent = dirname3(srcAbs);
2590
+ let boxParent;
2591
+ let finalName;
2592
+ if (boxDst.endsWith("/")) {
2593
+ boxParent = boxDst.replace(/\/+$/, "") || "/";
2594
+ finalName = srcBasename;
2595
+ } else {
2596
+ const isDir2 = await execa22(
2597
+ "docker",
2598
+ ["exec", box.container, "test", "-d", boxDst],
2599
+ { reject: false }
2600
+ );
2601
+ if (isDir2.exitCode === 0) {
2602
+ boxParent = boxDst.replace(/\/+$/, "") || "/";
2603
+ finalName = srcBasename;
2604
+ } else {
2605
+ boxParent = posixDirname(boxDst);
2606
+ finalName = posix.basename(boxDst);
2607
+ }
2608
+ }
2609
+ const finalPath = boxParent === "/" ? `/${finalName}` : `${boxParent}/${finalName}`;
2610
+ const mk = await execa22(
2611
+ "docker",
2612
+ ["exec", "--user", "root", box.container, "mkdir", "-p", boxParent],
2613
+ { reject: false }
2614
+ );
2615
+ if (mk.exitCode !== 0) {
2616
+ throw new Error(`mkdir -p ${boxParent} in box failed: ${asText(mk.stderr).slice(0, 300)}`);
2617
+ }
2618
+ const [packed, extracted] = await streamTarPipe(
2619
+ "tar",
2620
+ ["-C", srcParent, "-cf", "-", ...tarExcludeArgs(exclude), srcBasename],
2621
+ "docker",
2622
+ ["exec", "-i", "--user", "root", box.container, "tar", "-xf", "-", "-C", boxParent],
2623
+ { ...process.env, COPYFILE_DISABLE: "1" }
2624
+ );
2625
+ if (packed.exitCode !== 0) {
2626
+ throw new Error(`tar pack failed: ${asText(packed.stderr).slice(0, 300)}`);
2627
+ }
2628
+ if (extracted.exitCode !== 0) {
2629
+ throw new Error(`tar extract in box failed: ${asText(extracted.stderr).slice(0, 300)}`);
2630
+ }
2631
+ if (finalName !== srcBasename) {
2632
+ const initial = boxParent === "/" ? `/${srcBasename}` : `${boxParent}/${srcBasename}`;
2633
+ const mv = await execa22(
2634
+ "docker",
2635
+ ["exec", "--user", "root", box.container, "mv", initial, finalPath],
2636
+ { reject: false }
2637
+ );
2638
+ if (mv.exitCode !== 0) {
2639
+ throw new Error(
2640
+ `rename ${initial} -> ${finalPath} in box failed: ${asText(mv.stderr).slice(0, 300)}`
2641
+ );
2642
+ }
2643
+ }
2644
+ const chown = await execa22(
2645
+ "docker",
2646
+ ["exec", "--user", "root", box.container, "chown", "-R", "1000:1000", finalPath],
2647
+ { reject: false }
2648
+ );
2649
+ if (chown.exitCode !== 0) {
2650
+ return {
2651
+ finalPath,
2652
+ warn: `chown ${finalPath} to vscode (uid 1000) failed; ownership inside the box may be root.`
2653
+ };
2654
+ }
2655
+ return { finalPath };
2656
+ }
2657
+ async function downloadFromBox(box, boxSrc, hostDst, exclude) {
2658
+ const srcBasename = posix.basename(boxSrc);
2659
+ const srcParent = posixDirname(boxSrc);
2660
+ const dstAbs = resolve2(hostDst);
2661
+ let hostParent;
2662
+ let finalName;
2663
+ const dstExists = existsSync(dstAbs);
2664
+ if (hostDst.endsWith("/") || dstExists && statSync(dstAbs).isDirectory()) {
2665
+ hostParent = dstAbs;
2666
+ finalName = srcBasename;
2667
+ } else {
2668
+ hostParent = dirname3(dstAbs);
2669
+ finalName = basename2(dstAbs);
2670
+ }
2671
+ mkdirSync(hostParent, { recursive: true });
2672
+ const finalPath = posix.join(hostParent, finalName);
2673
+ const [packed, extracted] = await streamTarPipe(
2674
+ "docker",
2675
+ ["exec", box.container, "tar", "-C", srcParent, "-cf", "-", ...tarExcludeArgs(exclude), srcBasename],
2676
+ "tar",
2677
+ ["-xf", "-", "-C", hostParent]
2678
+ );
2679
+ if (packed.exitCode !== 0) {
2680
+ throw new Error(`tar pack in box failed: ${asText(packed.stderr).slice(0, 300)}`);
2681
+ }
2682
+ if (extracted.exitCode !== 0) {
2683
+ throw new Error(`tar extract on host failed: ${asText(extracted.stderr).slice(0, 300)}`);
2684
+ }
2685
+ if (finalName !== srcBasename) {
2686
+ renameSync(posix.join(hostParent, srcBasename), finalPath);
2687
+ }
2688
+ return { finalPath };
2689
+ }
2523
2690
  var CONTAINER_EXPORT_MERGED = "/host-export";
2524
2691
  var cachedEngine = null;
2525
2692
  async function detectEngine() {
2526
2693
  if (cachedEngine !== null) return cachedEngine;
2527
- const result = await execa22("docker", ["info", "--format", "{{.OperatingSystem}}"], {
2694
+ const result = await execa3("docker", ["info", "--format", "{{.OperatingSystem}}"], {
2528
2695
  reject: false
2529
2696
  });
2530
2697
  const os = (result.stdout ?? "").trim().toLowerCase();
@@ -2537,7 +2704,7 @@ function setEngineOverride(engine) {
2537
2704
  cachedEngine = engine;
2538
2705
  }
2539
2706
  async function getDockerContext() {
2540
- const result = await execa22("docker", ["context", "show"], { reject: false });
2707
+ const result = await execa3("docker", ["context", "show"], { reject: false });
2541
2708
  if (result.exitCode !== 0) return void 0;
2542
2709
  const ctx = (result.stdout ?? "").trim();
2543
2710
  return ctx.length > 0 ? ctx : void 0;
@@ -2597,7 +2764,7 @@ async function refreshExport(record, opts = {}) {
2597
2764
  return { hostPath: paths.mergedExport, copied: true, usedFallback: false };
2598
2765
  }
2599
2766
  const excludes = excludeNodeModules ? ["--exclude=node_modules"] : [];
2600
- const result = await execa22(
2767
+ const result = await execa3(
2601
2768
  "docker",
2602
2769
  ["exec", "--user", "root", record.container, "tar", "-cf", "-", ...excludes, "-C", "/workspace", "."],
2603
2770
  { reject: false, encoding: "buffer" }
@@ -2609,7 +2776,7 @@ async function refreshExport(record, opts = {}) {
2609
2776
  typeof result.stderr === "string" ? result.stderr : result.stderr.toString("utf8")
2610
2777
  );
2611
2778
  }
2612
- const extract = await execa22("tar", ["-xf", "-", "-C", paths.mergedExport], {
2779
+ const extract = await execa3("tar", ["-xf", "-", "-C", paths.mergedExport], {
2613
2780
  input: result.stdout,
2614
2781
  reject: false
2615
2782
  });
@@ -2704,7 +2871,7 @@ function buildHostEnvFindArgs(patterns) {
2704
2871
  async function copyHostEnvFilesToBox(opts) {
2705
2872
  const log = opts.onLog ?? (() => {
2706
2873
  });
2707
- const found = await execa22("find", buildHostEnvFindArgs(opts.patterns).slice(1), {
2874
+ const found = await execa3("find", buildHostEnvFindArgs(opts.patterns).slice(1), {
2708
2875
  cwd: opts.workspaceDir,
2709
2876
  reject: false
2710
2877
  });
@@ -2714,7 +2881,7 @@ async function copyHostEnvFilesToBox(opts) {
2714
2881
  }
2715
2882
  const list = String(found.stdout).split("\0").filter((p) => p.length > 0);
2716
2883
  if (list.length === 0) return { copied: 0 };
2717
- const packed = await execa22("tar", ["-C", opts.workspaceDir, "--null", "-T", "-", "-cf", "-"], {
2884
+ const packed = await execa3("tar", ["-C", opts.workspaceDir, "--null", "-T", "-", "-cf", "-"], {
2718
2885
  input: list.join("\0"),
2719
2886
  encoding: "buffer",
2720
2887
  reject: false
@@ -2723,7 +2890,7 @@ async function copyHostEnvFilesToBox(opts) {
2723
2890
  log(`warning: env-file tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
2724
2891
  return { copied: 0 };
2725
2892
  }
2726
- const extract = await execa22(
2893
+ const extract = await execa3(
2727
2894
  "docker",
2728
2895
  ["exec", "-i", "--user", "1000:1000", opts.container, "tar", "-xf", "-", "-C", "/workspace"],
2729
2896
  { input: packed.stdout, reject: false }
@@ -2736,7 +2903,7 @@ async function copyHostEnvFilesToBox(opts) {
2736
2903
  }
2737
2904
  async function scanHostEnvFiles(workspaceDir, patterns) {
2738
2905
  if (patterns.length === 0) return [];
2739
- const found = await execa22("find", buildHostEnvFindArgs(patterns).slice(1), {
2906
+ const found = await execa3("find", buildHostEnvFindArgs(patterns).slice(1), {
2740
2907
  cwd: workspaceDir,
2741
2908
  reject: false
2742
2909
  });
@@ -2748,7 +2915,7 @@ async function copyHostFilesToBox(opts) {
2748
2915
  });
2749
2916
  const list = opts.files.map((p) => p.replace(/^\.\//, "")).filter((p) => p.length > 0);
2750
2917
  if (list.length === 0) return { copied: 0 };
2751
- const packed = await execa22("tar", ["-C", opts.workspaceDir, "--null", "-T", "-", "-cf", "-"], {
2918
+ const packed = await execa3("tar", ["-C", opts.workspaceDir, "--null", "-T", "-", "-cf", "-"], {
2752
2919
  input: list.join("\0"),
2753
2920
  encoding: "buffer",
2754
2921
  reject: false
@@ -2757,7 +2924,7 @@ async function copyHostFilesToBox(opts) {
2757
2924
  log(`warning: env-file tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
2758
2925
  return { copied: 0 };
2759
2926
  }
2760
- const extract = await execa22(
2927
+ const extract = await execa3(
2761
2928
  "docker",
2762
2929
  ["exec", "-i", "--user", "1000:1000", opts.container, "tar", "-xf", "-", "-C", "/workspace"],
2763
2930
  { input: packed.stdout, reject: false }
@@ -2831,7 +2998,7 @@ async function pullToHost(record, opts = {}) {
2831
2998
  }
2832
2999
  const src = `${scratchDir}/`;
2833
3000
  const dst = `${record.workspacePath}/`;
2834
- const dry = await execa22("rsync", [...baseArgs, "--dry-run", "-i", src, dst], {
3001
+ const dry = await execa3("rsync", [...baseArgs, "--dry-run", "-i", src, dst], {
2835
3002
  reject: false,
2836
3003
  input: fileList !== null ? fileList : void 0
2837
3004
  });
@@ -2842,7 +3009,7 @@ async function pullToHost(record, opts = {}) {
2842
3009
  if (opts.dryRun) {
2843
3010
  return { hostPath: record.workspacePath, changes, applied: false, usedGitignore };
2844
3011
  }
2845
- const real = await execa22("rsync", [...baseArgs, src, dst], {
3012
+ const real = await execa3("rsync", [...baseArgs, src, dst], {
2846
3013
  reject: false,
2847
3014
  input: fileList !== null ? fileList : void 0
2848
3015
  });
@@ -2867,7 +3034,7 @@ async function openInFinder(record, opts) {
2867
3034
  usedFallback = refreshed.usedFallback;
2868
3035
  }
2869
3036
  if (!opts.noOpen) {
2870
- const opened = await execa22(hostOpenCommand(), [hostPath], { reject: false });
3037
+ const opened = await execa3(hostOpenCommand(), [hostPath], { reject: false });
2871
3038
  if (opened.exitCode !== 0) {
2872
3039
  throw new ExportError(`open ${hostPath} failed`, opened.stdout, opened.stderr);
2873
3040
  }
@@ -2890,12 +3057,14 @@ async function carrySourceHash(entry) {
2890
3057
  if (entry.kind === "file") {
2891
3058
  return createHash3("sha256").update(await readFile5(entry.absSrc)).digest("hex");
2892
3059
  }
3060
+ const exclude = entry.exclude ?? [];
2893
3061
  const h = createHash3("sha256");
2894
3062
  const walk = async (dir, rel) => {
2895
3063
  const names = (await readdir4(dir)).sort();
2896
3064
  for (const name of names) {
2897
- const abs = join5(dir, name);
2898
3065
  const relPath = rel ? `${rel}/${name}` : name;
3066
+ if (carryRelExcluded(relPath, exclude)) continue;
3067
+ const abs = join5(dir, name);
2899
3068
  const st = await stat4(abs);
2900
3069
  if (st.isDirectory()) {
2901
3070
  h.update(`d\0${relPath}
@@ -2914,6 +3083,24 @@ async function carrySourceHash(entry) {
2914
3083
  return void 0;
2915
3084
  }
2916
3085
  }
3086
+ function carryRelExcluded(relPath, patterns) {
3087
+ if (patterns.length === 0) return false;
3088
+ const segs = relPath.split("/");
3089
+ for (const p of patterns) {
3090
+ const isGlob = p.includes("*") || p.includes("?");
3091
+ if (!isGlob) {
3092
+ if (segs.includes(p)) return true;
3093
+ } else if (p.startsWith("*/") && !/[*?/]/.test(p.slice(2))) {
3094
+ if (segs.includes(p.slice(2))) return true;
3095
+ } else {
3096
+ const re = new RegExp(
3097
+ `^${p.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`
3098
+ );
3099
+ if (re.test(relPath)) return true;
3100
+ }
3101
+ }
3102
+ return false;
3103
+ }
2917
3104
  async function copyCarryPathsToBox(opts) {
2918
3105
  const log = opts.onLog ?? (() => {
2919
3106
  });
@@ -2949,7 +3136,7 @@ async function copyOneEntry(container, entry) {
2949
3136
  const boxDest = entry.absDest.startsWith("~/") ? `${BOX_HOME}/${entry.absDest.slice(2)}` : entry.absDest;
2950
3137
  const boxDestParent = boxDest.endsWith("/") ? boxDest.slice(0, -1) : boxDest;
2951
3138
  const parentDir = entry.kind === "dir" ? boxDestParent : dirnameUnix(boxDestParent);
2952
- const mkdir9 = await execa22(
3139
+ const mkdir9 = await execa3(
2953
3140
  "docker",
2954
3141
  ["exec", "--user", "0:0", container, "mkdir", "-p", parentDir],
2955
3142
  { reject: false }
@@ -2958,7 +3145,7 @@ async function copyOneEntry(container, entry) {
2958
3145
  throw new Error(`mkdir -p ${parentDir} failed: ${String(mkdir9.stderr).slice(0, 300)}`);
2959
3146
  }
2960
3147
  if (entry.kind === "file") {
2961
- const cp2 = await execa22(
3148
+ const cp2 = await execa3(
2962
3149
  "docker",
2963
3150
  ["cp", entry.absSrc, `${container}:${boxDest}`],
2964
3151
  { reject: false }
@@ -2967,15 +3154,10 @@ async function copyOneEntry(container, entry) {
2967
3154
  throw new Error(`docker cp failed: ${String(cp2.stderr).slice(0, 300)}`);
2968
3155
  }
2969
3156
  } else {
2970
- const packed = await execa22(
3157
+ const excludeArgs = (entry.exclude ?? []).map((p) => `--exclude=${p}`);
3158
+ const [packed, extract] = await streamTarPipe(
2971
3159
  "tar",
2972
- ["-C", entry.absSrc, "-cf", "-", "."],
2973
- { encoding: "buffer", reject: false }
2974
- );
2975
- if (packed.exitCode !== 0) {
2976
- throw new Error(`tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
2977
- }
2978
- const extract = await execa22(
3160
+ ["-C", entry.absSrc, "-cf", "-", ...excludeArgs, "."],
2979
3161
  "docker",
2980
3162
  [
2981
3163
  "exec",
@@ -2991,16 +3173,18 @@ async function copyOneEntry(container, entry) {
2991
3173
  "--no-same-permissions",
2992
3174
  "--no-same-owner",
2993
3175
  "-m"
2994
- ],
2995
- { input: packed.stdout, reject: false }
3176
+ ]
2996
3177
  );
3178
+ if (packed.exitCode !== 0) {
3179
+ throw new Error(`tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
3180
+ }
2997
3181
  if (extract.exitCode !== 0) {
2998
3182
  throw new Error(`tar extract failed: ${String(extract.stderr).slice(0, 300)}`);
2999
3183
  }
3000
3184
  }
3001
3185
  if (entry.mode !== void 0) {
3002
3186
  const modeStr = entry.mode.toString(8).padStart(4, "0");
3003
- const chmod2 = await execa22(
3187
+ const chmod2 = await execa3(
3004
3188
  "docker",
3005
3189
  ["exec", "--user", "0:0", container, "chmod", "-R", modeStr, boxDest],
3006
3190
  { reject: false }
@@ -3010,7 +3194,7 @@ async function copyOneEntry(container, entry) {
3010
3194
  }
3011
3195
  }
3012
3196
  const uid = entry.user ?? 1e3;
3013
- const chown = await execa22(
3197
+ const chown = await execa3(
3014
3198
  "docker",
3015
3199
  ["exec", "--user", "0:0", container, "chown", "-R", `${String(uid)}:${String(uid)}`, boxDest],
3016
3200
  { reject: false }
@@ -3021,7 +3205,7 @@ async function copyOneEntry(container, entry) {
3021
3205
  if (boxDest.startsWith(BOX_HOME + "/") && dirnameUnix(boxDest) !== BOX_HOME) {
3022
3206
  const safeDest = boxDest.replace(/'/g, `'\\''`);
3023
3207
  const script = `set -e; parent="$(dirname '${safeDest}')"; while [ "$parent" != "${BOX_HOME}" ] && [ "$parent" != "/" ]; do chown ${String(uid)}:${String(uid)} "$parent"; parent="$(dirname "$parent")"; done`;
3024
- const chownParents = await execa22(
3208
+ const chownParents = await execa3(
3025
3209
  "docker",
3026
3210
  ["exec", "--user", "0:0", container, "bash", "-c", script],
3027
3211
  { reject: false }
@@ -3069,7 +3253,7 @@ async function extractVolumeAuthToBackup(opts) {
3069
3253
  try {
3070
3254
  await mkdir22(STATE_DIR, { recursive: true });
3071
3255
  const script = 'COPIED=no; if [ -s /dst/auth.json ]; then cp -a /dst/auth.json "/host-state/$DEST" && COPIED=yes; fi; echo "COPIED=$COPIED"';
3072
- const { stdout } = await execa3("docker", [
3256
+ const { stdout } = await execa4("docker", [
3073
3257
  "run",
3074
3258
  "--rm",
3075
3259
  "--user",
@@ -3081,7 +3265,7 @@ async function extractVolumeAuthToBackup(opts) {
3081
3265
  "-e",
3082
3266
  // Pass the destination filename via env so the path isn't interpolated
3083
3267
  // into the script string (keeps the docker arg list static + injection-safe).
3084
- `DEST=${basename3(opts.backupFile)}`,
3268
+ `DEST=${basename22(opts.backupFile)}`,
3085
3269
  opts.image,
3086
3270
  "sh",
3087
3271
  "-c",
@@ -3133,7 +3317,7 @@ echo "EXTRACTED=$EXTRACTED SEEDED=$SEEDED VOLREAL=$VOL_REAL"
3133
3317
  async function syncClaudeCredentials(spec, opts) {
3134
3318
  try {
3135
3319
  await mkdir22(STATE_DIR, { recursive: true });
3136
- const { stdout } = await execa3("docker", [
3320
+ const { stdout } = await execa4("docker", [
3137
3321
  "run",
3138
3322
  "--rm",
3139
3323
  "--user",
@@ -3201,8 +3385,8 @@ function emptyResult(warnings = []) {
3201
3385
  }, warnings };
3202
3386
  }
3203
3387
  async function tarballFromDir(stageDir, agent) {
3204
- const tarballPath = join32(tmpdir(), `agentbox-${agent}-${basename2(stageDir)}.tar.gz`);
3205
- await execa4("tar", ["-czf", tarballPath, "-C", stageDir, "."], {
3388
+ const tarballPath = join32(tmpdir(), `agentbox-${agent}-${basename3(stageDir)}.tar.gz`);
3389
+ await execa5("tar", ["-czf", tarballPath, "-C", stageDir, "."], {
3206
3390
  env: { ...process.env, COPYFILE_DISABLE: "1" }
3207
3391
  });
3208
3392
  return tarballPath;
@@ -3347,7 +3531,7 @@ async function stageClaudeStaticForUpload(opts = {}) {
3347
3531
  ...CLAUDE_RUNTIME_EXCLUDES.map((p) => `--exclude=${p}`),
3348
3532
  ...broken.map((r) => `--exclude=/${r}`)
3349
3533
  ];
3350
- await execa4("rsync", [
3534
+ await execa5("rsync", [
3351
3535
  "-a",
3352
3536
  "--copy-unsafe-links",
3353
3537
  ...excludes,
@@ -3436,7 +3620,7 @@ async function stageCodexStaticForUpload(opts = {}) {
3436
3620
  let tarballPath = null;
3437
3621
  try {
3438
3622
  const codexBroken = await findBrokenSymlinks(hostCodex);
3439
- await execa4("rsync", [
3623
+ await execa5("rsync", [
3440
3624
  "-a",
3441
3625
  "-L",
3442
3626
  ...codexBroken.map((r) => `--exclude=/${r}`),
@@ -3492,7 +3676,7 @@ async function stageOpencodeStaticForUpload(opts = {}) {
3492
3676
  try {
3493
3677
  if (hasData) {
3494
3678
  const dataBroken = await findBrokenSymlinks(hostData);
3495
- await execa4("rsync", [
3679
+ await execa5("rsync", [
3496
3680
  "-a",
3497
3681
  "-L",
3498
3682
  ...dataBroken.map((r) => `--exclude=/${r}`),
@@ -3505,7 +3689,7 @@ async function stageOpencodeStaticForUpload(opts = {}) {
3505
3689
  if (hasConfig) {
3506
3690
  const configStage = join32(stageDir, "config");
3507
3691
  const cfgBroken = await findBrokenSymlinks(hostConfig);
3508
- await execa4("rsync", [
3692
+ await execa5("rsync", [
3509
3693
  "-a",
3510
3694
  "-L",
3511
3695
  ...cfgBroken.map((r) => `--exclude=/${r}`),
@@ -3563,7 +3747,7 @@ async function pathExists2(p) {
3563
3747
  }
3564
3748
  }
3565
3749
  async function volumeHasClaudeJson(volume, image) {
3566
- const res = await execa5(
3750
+ const res = await execa6(
3567
3751
  "docker",
3568
3752
  ["run", "--rm", "-v", `${volume}:/dst`, image, "sh", "-c", "test -e /dst/_claude.json"],
3569
3753
  { reject: false }
@@ -3733,7 +3917,7 @@ async function ensureClaudeVolume(spec, opts) {
3733
3917
  // linux/amd64 node_modules on the next `agentbox claude`.
3734
3918
  `{ [ ! -f /dst/.agentbox-cleaned-nm-v1 ] && find /dst -name node_modules -type d -prune -exec rm -rf {} + && touch /dst/.agentbox-cleaned-nm-v1; true; } && rsync ${rsyncFlags} /src-claude/ /dst/ && { [ -f /src-claude-json ] && cp -a /src-claude-json /dst/_claude.json; true; } && { [ -f /src-filter/settings.json ] && cp -a /src-filter/settings.json /dst/settings.json; true; } && { [ -f /src-filter/_claude.json ] && cp -a /src-filter/_claude.json /dst/_claude.json; true; } && { [ -d /dst/plugins ] && [ -n "$HOST_HOME" ] && find /dst/plugins -maxdepth 1 -type f -name "*.json" -exec sed -i "s|$HOST_HOME/.claude/plugins/|/home/vscode/.claude/plugins/|g" {} +; true; }` + memoryRekeyStep + " && chown -R 1000:1000 /dst"
3735
3919
  );
3736
- await execa5("docker", args);
3920
+ await execa6("docker", args);
3737
3921
  } finally {
3738
3922
  await rm2(filterDir, { recursive: true, force: true });
3739
3923
  }
@@ -3748,7 +3932,7 @@ async function ensureClaudeVolume(spec, opts) {
3748
3932
  }
3749
3933
  async function seedSetupSkillIntoVolume(volume, image) {
3750
3934
  try {
3751
- const { stdout } = await execa5("docker", [
3935
+ const { stdout } = await execa6("docker", [
3752
3936
  "run",
3753
3937
  "--rm",
3754
3938
  "--user",
@@ -3913,7 +4097,7 @@ async function resolveClaudeCacheLiveOnHost(volume) {
3913
4097
  return orbstackVolumePath(volume, "plugins", "cache");
3914
4098
  }
3915
4099
  async function readBoxReferencedPluginKeys(container) {
3916
- const res = await execa5(
4100
+ const res = await execa6(
3917
4101
  "docker",
3918
4102
  [
3919
4103
  "exec",
@@ -4031,7 +4215,7 @@ while IFS= read -r dir; do
4031
4215
  done < "$WORK/dirs"
4032
4216
  rm -rf "$WORK"
4033
4217
  `;
4034
- const result = await execa5(
4218
+ const result = await execa6(
4035
4219
  "docker",
4036
4220
  ["exec", "--user", CONTAINER_USER, container, "sh", "-c", script],
4037
4221
  { reject: false }
@@ -4092,7 +4276,7 @@ async function startClaudeSession(opts) {
4092
4276
  const v = process.env[k];
4093
4277
  if (typeof v === "string" && v.length > 0) envFlags.push("-e", `${k}=${v}`);
4094
4278
  }
4095
- const result = await execa5(
4279
+ const result = await execa6(
4096
4280
  "docker",
4097
4281
  [
4098
4282
  "exec",
@@ -4183,7 +4367,7 @@ function buildDashboardAttachArgv(container, sessionName) {
4183
4367
  async function waitForTmuxPaneContent(container, sessionName, timeoutMs = 2e4) {
4184
4368
  const deadline = Date.now() + timeoutMs;
4185
4369
  while (Date.now() < deadline) {
4186
- const res = await execa5(
4370
+ const res = await execa6(
4187
4371
  "docker",
4188
4372
  ["exec", "--user", CONTAINER_USER, container, "tmux", "capture-pane", "-p", "-t", sessionName],
4189
4373
  { reject: false }
@@ -4251,7 +4435,7 @@ async function warmUpClaudeCredentials(volume, image, opts = {}) {
4251
4435
  const SLEEP_MS = 5e3;
4252
4436
  for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
4253
4437
  opts.onProgress?.(`checking credentials... ${attempt}/${MAX_ATTEMPTS}`);
4254
- const res = await execa5(
4438
+ const res = await execa6(
4255
4439
  "docker",
4256
4440
  [
4257
4441
  "run",
@@ -4293,7 +4477,7 @@ function attachClaudeSession(container, sessionName, reattachRef) {
4293
4477
  }
4294
4478
  async function claudeSessionInfo(container, sessionName) {
4295
4479
  const name = sessionName ?? DEFAULT_CLAUDE_SESSION;
4296
- const has = await execa5(
4480
+ const has = await execa6(
4297
4481
  "docker",
4298
4482
  ["exec", "--user", CONTAINER_USER, container, "tmux", "has-session", "-t", name],
4299
4483
  { reject: false }
@@ -4301,7 +4485,7 @@ async function claudeSessionInfo(container, sessionName) {
4301
4485
  if (has.exitCode !== 0) {
4302
4486
  return { running: false, sessionName: name, startedAt: null };
4303
4487
  }
4304
- const ts = await execa5(
4488
+ const ts = await execa6(
4305
4489
  "docker",
4306
4490
  [
4307
4491
  "exec",
@@ -4367,7 +4551,7 @@ async function pullClaudeExtras(spec, opts) {
4367
4551
  ' printf "\\n";',
4368
4552
  "done"
4369
4553
  ].join(" ");
4370
- const inv = await execa5(
4554
+ const inv = await execa6(
4371
4555
  "docker",
4372
4556
  ["run", "--rm", "--user", "0", "-v", `${spec.volume}:/src:ro`, opts.image, "sh", "-c", inventoryScript],
4373
4557
  { reject: false }
@@ -4440,7 +4624,7 @@ async function pullClaudeExtras(spec, opts) {
4440
4624
  const parent = dest.slice(0, dest.lastIndexOf("/"));
4441
4625
  return `mkdir -p '${parent}' && rsync -a --ignore-existing --exclude=node_modules '${src}/' '${dest}/'`;
4442
4626
  });
4443
- const apply = await execa5(
4627
+ const apply = await execa6(
4444
4628
  "docker",
4445
4629
  [
4446
4630
  "run",
@@ -4513,7 +4697,7 @@ async function ensureCodexVolume(spec, opts) {
4513
4697
  const hostCodex = join52(homedir4(), ".codex");
4514
4698
  const willSync = opts.syncFromHost && await pathExists3(hostCodex);
4515
4699
  if (willSync) {
4516
- await execa6("docker", [
4700
+ await execa7("docker", [
4517
4701
  "run",
4518
4702
  "--rm",
4519
4703
  "--user",
@@ -4531,7 +4715,7 @@ async function ensureCodexVolume(spec, opts) {
4531
4715
  ]);
4532
4716
  return { created, synced: true };
4533
4717
  }
4534
- await execa6(
4718
+ await execa7(
4535
4719
  "docker",
4536
4720
  [
4537
4721
  "run",
@@ -4551,7 +4735,7 @@ async function ensureCodexVolume(spec, opts) {
4551
4735
  }
4552
4736
  async function seedCodexHooks(volume, image) {
4553
4737
  try {
4554
- const { stdout } = await execa6("docker", [
4738
+ const { stdout } = await execa7("docker", [
4555
4739
  "run",
4556
4740
  "--rm",
4557
4741
  "--user",
@@ -4588,14 +4772,14 @@ var CodexSessionError = class extends Error {
4588
4772
  }
4589
4773
  };
4590
4774
  async function ensureCodexInstalled(container, opts = {}) {
4591
- const probe = await execa6(
4775
+ const probe = await execa7(
4592
4776
  "docker",
4593
4777
  ["exec", "--user", CONTAINER_USER, container, "sh", "-c", "command -v codex"],
4594
4778
  { reject: false }
4595
4779
  );
4596
4780
  if (probe.exitCode === 0) return { installed: false };
4597
4781
  opts.onProgress?.("installing codex (absent from this box image)");
4598
- const install = await execa6(
4782
+ const install = await execa7(
4599
4783
  "docker",
4600
4784
  ["exec", "--user", "root", container, "bash", "-lc", "npm install -g @openai/codex 2>&1"],
4601
4785
  { reject: false }
@@ -4618,7 +4802,7 @@ async function startCodexSession(opts) {
4618
4802
  const v = process.env[k];
4619
4803
  if (typeof v === "string" && v.length > 0) envFlags.push("-e", `${k}=${v}`);
4620
4804
  }
4621
- const result = await execa6(
4805
+ const result = await execa7(
4622
4806
  "docker",
4623
4807
  [
4624
4808
  "exec",
@@ -4700,7 +4884,7 @@ function runInteractiveCodexLogin(dockerArgv) {
4700
4884
  return { exitCode: child.status ?? 1 };
4701
4885
  }
4702
4886
  async function volumeHasCodexAuth(volume, image) {
4703
- const res = await execa6(
4887
+ const res = await execa7(
4704
4888
  "docker",
4705
4889
  ["run", "--rm", "-v", `${volume}:/dst`, image, "sh", "-c", "test -e /dst/auth.json"],
4706
4890
  { reject: false }
@@ -4709,7 +4893,7 @@ async function volumeHasCodexAuth(volume, image) {
4709
4893
  }
4710
4894
  async function codexSessionInfo(container, sessionName) {
4711
4895
  const name = sessionName ?? DEFAULT_CODEX_SESSION;
4712
- const has = await execa6(
4896
+ const has = await execa7(
4713
4897
  "docker",
4714
4898
  ["exec", "--user", CONTAINER_USER, container, "tmux", "has-session", "-t", name],
4715
4899
  { reject: false }
@@ -4717,7 +4901,7 @@ async function codexSessionInfo(container, sessionName) {
4717
4901
  if (has.exitCode !== 0) {
4718
4902
  return { running: false, sessionName: name, startedAt: null };
4719
4903
  }
4720
- const ts = await execa6(
4904
+ const ts = await execa7(
4721
4905
  "docker",
4722
4906
  [
4723
4907
  "exec",
@@ -4743,7 +4927,7 @@ async function codexSessionInfo(container, sessionName) {
4743
4927
  var CODEX_PULL_ITEMS = ["config.toml", "auth.json", "prompts"];
4744
4928
  async function pullCodexConfig(spec, opts) {
4745
4929
  const hostCodex = join52(homedir4(), ".codex");
4746
- const inv = await execa6(
4930
+ const inv = await execa7(
4747
4931
  "docker",
4748
4932
  [
4749
4933
  "run",
@@ -4777,7 +4961,7 @@ async function pullCodexConfig(spec, opts) {
4777
4961
  const uid = process.getuid?.() ?? 0;
4778
4962
  const gid = process.getgid?.() ?? 0;
4779
4963
  const cmds = newItems.map((it) => `cp -a '/src/${it}' '/dst/${it}'`);
4780
- const apply = await execa6(
4964
+ const apply = await execa7(
4781
4965
  "docker",
4782
4966
  [
4783
4967
  "run",
@@ -4859,10 +5043,10 @@ async function ensureOpencodeVolume(spec, opts) {
4859
5043
  }
4860
5044
  steps.push("chown -R 1000:1000 /dst");
4861
5045
  args.push(opts.image, "sh", "-c", steps.join(" && "));
4862
- await execa7("docker", args);
5046
+ await execa8("docker", args);
4863
5047
  return { created, synced: true };
4864
5048
  }
4865
- await execa7(
5049
+ await execa8(
4866
5050
  "docker",
4867
5051
  [
4868
5052
  "run",
@@ -4882,7 +5066,7 @@ async function ensureOpencodeVolume(spec, opts) {
4882
5066
  }
4883
5067
  async function seedOpencodePlugin(volume, image) {
4884
5068
  try {
4885
- const { stdout } = await execa7("docker", [
5069
+ const { stdout } = await execa8("docker", [
4886
5070
  "run",
4887
5071
  "--rm",
4888
5072
  "--user",
@@ -4930,14 +5114,14 @@ var OpencodeSessionError = class extends Error {
4930
5114
  }
4931
5115
  };
4932
5116
  async function ensureOpencodeInstalled(container, opts = {}) {
4933
- const probe = await execa7(
5117
+ const probe = await execa8(
4934
5118
  "docker",
4935
5119
  ["exec", "--user", CONTAINER_USER, container, "sh", "-c", "command -v opencode"],
4936
5120
  { reject: false }
4937
5121
  );
4938
5122
  if (probe.exitCode === 0) return { installed: false };
4939
5123
  opts.onProgress?.("installing opencode (absent from this box image)");
4940
- const install = await execa7(
5124
+ const install = await execa8(
4941
5125
  "docker",
4942
5126
  ["exec", "--user", "root", container, "bash", "-lc", "npm install -g opencode-ai 2>&1"],
4943
5127
  { reject: false }
@@ -4959,7 +5143,7 @@ async function startOpencodeSession(opts) {
4959
5143
  const v = process.env[k];
4960
5144
  if (typeof v === "string" && v.length > 0) envFlags.push("-e", `${k}=${v}`);
4961
5145
  }
4962
- const result = await execa7(
5146
+ const result = await execa8(
4963
5147
  "docker",
4964
5148
  [
4965
5149
  "exec",
@@ -5045,7 +5229,7 @@ function runInteractiveOpencodeLogin(dockerArgv) {
5045
5229
  return { exitCode: child.status ?? 1 };
5046
5230
  }
5047
5231
  async function volumeHasOpencodeAuth(volume, image) {
5048
- const res = await execa7(
5232
+ const res = await execa8(
5049
5233
  "docker",
5050
5234
  ["run", "--rm", "-v", `${volume}:/dst`, image, "sh", "-c", "test -e /dst/auth.json"],
5051
5235
  { reject: false }
@@ -5054,7 +5238,7 @@ async function volumeHasOpencodeAuth(volume, image) {
5054
5238
  }
5055
5239
  async function opencodeSessionInfo(container, sessionName) {
5056
5240
  const name = sessionName ?? DEFAULT_OPENCODE_SESSION;
5057
- const has = await execa7(
5241
+ const has = await execa8(
5058
5242
  "docker",
5059
5243
  ["exec", "--user", CONTAINER_USER, container, "tmux", "has-session", "-t", name],
5060
5244
  { reject: false }
@@ -5062,7 +5246,7 @@ async function opencodeSessionInfo(container, sessionName) {
5062
5246
  if (has.exitCode !== 0) {
5063
5247
  return { running: false, sessionName: name, startedAt: null };
5064
5248
  }
5065
- const ts = await execa7(
5249
+ const ts = await execa8(
5066
5250
  "docker",
5067
5251
  [
5068
5252
  "exec",
@@ -5100,7 +5284,7 @@ var OPENCODE_PULL_CONFIG_ITEMS = [
5100
5284
  async function pullOpencodeConfig(spec, opts) {
5101
5285
  const hostData = join6(homedir5(), ".local", "share", "opencode");
5102
5286
  const hostConfig = join6(homedir5(), ".config", "opencode");
5103
- const inv = await execa7(
5287
+ const inv = await execa8(
5104
5288
  "docker",
5105
5289
  [
5106
5290
  "run",
@@ -5142,7 +5326,7 @@ async function pullOpencodeConfig(spec, opts) {
5142
5326
  const cmds = newItems.map(
5143
5327
  (i) => `cp -a '${i.src}' '${i.hostDst === "data" ? "/dst-data" : "/dst-config"}/${i.name}'`
5144
5328
  );
5145
- const apply = await execa7(
5329
+ const apply = await execa8(
5146
5330
  "docker",
5147
5331
  [
5148
5332
  "run",
@@ -5253,9 +5437,9 @@ function gitWorktreePathFor(branch) {
5253
5437
  return `${WORKTREE_ROOT}/${fsSafeBranch(branch)}`;
5254
5438
  }
5255
5439
  async function collectRepoCarryOver(repo, branch, containerPath, gitWorktreePath) {
5256
- const stash = await execa8("git", ["-C", repo.hostMainRepo, "stash", "create"], { reject: false });
5440
+ const stash = await execa9("git", ["-C", repo.hostMainRepo, "stash", "create"], { reject: false });
5257
5441
  const stashSha = stash.exitCode === 0 ? stash.stdout.trim() || null : null;
5258
- const untracked = await execa8(
5442
+ const untracked = await execa9(
5259
5443
  "git",
5260
5444
  ["-C", repo.hostMainRepo, "ls-files", "--others", "--exclude-standard", "-z"],
5261
5445
  { reject: false }
@@ -5272,7 +5456,7 @@ async function collectRepoCarryOver(repo, branch, containerPath, gitWorktreePath
5272
5456
  };
5273
5457
  }
5274
5458
  async function dexec(container, argv, user = "vscode", cwd = "/") {
5275
- const r = await execa8(
5459
+ const r = await execa9(
5276
5460
  "docker",
5277
5461
  ["exec", "-w", cwd, "--user", user, container, ...argv],
5278
5462
  { reject: false }
@@ -5304,7 +5488,7 @@ async function bindWorktrees(container, binds, onLog) {
5304
5488
  (a, b) => a.kind === "root" && b.kind !== "root" ? -1 : a.kind !== "root" && b.kind === "root" ? 1 : 0
5305
5489
  );
5306
5490
  for (const b of ordered) {
5307
- await execa8(
5491
+ await execa9(
5308
5492
  "docker",
5309
5493
  ["exec", "-w", "/", "--user", "root", container, "sh", "-c", `mountpoint -q ${b.containerPath} && umount ${b.containerPath} || true`],
5310
5494
  { reject: false }
@@ -5326,7 +5510,7 @@ async function seedWorkspace(opts) {
5326
5510
  const wt = r.gitWorktreePath;
5327
5511
  const baseRef = r.repo.kind === "root" ? opts.fromBranch ?? "HEAD" : "HEAD";
5328
5512
  const addArgs = r.reuseBranch ? ["worktree", "add", wt, r.branch] : ["worktree", "add", "-b", r.branch, wt, baseRef];
5329
- const add = await execa8(
5513
+ const add = await execa9(
5330
5514
  "docker",
5331
5515
  ["exec", "--user", "vscode", opts.container, "git", "-C", main, ...addArgs],
5332
5516
  { reject: false }
@@ -5337,7 +5521,7 @@ async function seedWorkspace(opts) {
5337
5521
  );
5338
5522
  }
5339
5523
  log(`worktree ${wt} on branch ${r.branch} (host main ${main})`);
5340
- await execa8(
5524
+ await execa9(
5341
5525
  "docker",
5342
5526
  [
5343
5527
  "exec",
@@ -5353,7 +5537,7 @@ async function seedWorkspace(opts) {
5353
5537
  ],
5354
5538
  { reject: false }
5355
5539
  );
5356
- await execa8(
5540
+ await execa9(
5357
5541
  "docker",
5358
5542
  [
5359
5543
  "exec",
@@ -5383,7 +5567,7 @@ async function seedWorkspace(opts) {
5383
5567
  for (const r of opts.repos) {
5384
5568
  const ct = r.containerPath;
5385
5569
  if (r.stashSha) {
5386
- const withIndex = await execa8(
5570
+ const withIndex = await execa9(
5387
5571
  "docker",
5388
5572
  [
5389
5573
  "exec",
@@ -5401,7 +5585,7 @@ async function seedWorkspace(opts) {
5401
5585
  { reject: false }
5402
5586
  );
5403
5587
  if (withIndex.exitCode !== 0) {
5404
- const noIndex = await execa8(
5588
+ const noIndex = await execa9(
5405
5589
  "docker",
5406
5590
  [
5407
5591
  "exec",
@@ -5429,7 +5613,7 @@ async function seedWorkspace(opts) {
5429
5613
  }
5430
5614
  }
5431
5615
  if (r.untrackedNul.length > 0) {
5432
- const tarOut = await execa8("tar", ["-C", r.hostSource, "--null", "-T", "-", "-cf", "-"], {
5616
+ const tarOut = await execa9("tar", ["-C", r.hostSource, "--null", "-T", "-", "-cf", "-"], {
5433
5617
  input: r.untrackedNul.replace(/\0$/, ""),
5434
5618
  encoding: "buffer",
5435
5619
  reject: false
@@ -5438,7 +5622,7 @@ async function seedWorkspace(opts) {
5438
5622
  log(`warning: tar of untracked files for ${r.repo.hostMainRepo} failed: ${tarOut.stderr}`);
5439
5623
  continue;
5440
5624
  }
5441
- const tarIn = await execa8(
5625
+ const tarIn = await execa9(
5442
5626
  "docker",
5443
5627
  ["exec", "-i", "--user", "vscode", opts.container, "tar", "-C", ct, "-xf", "-"],
5444
5628
  { input: tarOut.stdout, reject: false }
@@ -5491,7 +5675,7 @@ async function regenerateRestoredWorktrees(opts) {
5491
5675
  for (const p of opts.plans) {
5492
5676
  const baseRef = p.kind === "root" ? opts.fromBranch ?? "HEAD" : "HEAD";
5493
5677
  const metaDir = `${p.hostMainRepo}/.git/worktrees/${fsSafeBranch(p.freshBranch)}`;
5494
- const r = await execa8(
5678
+ const r = await execa9(
5495
5679
  "docker",
5496
5680
  [
5497
5681
  "exec",
@@ -5524,14 +5708,14 @@ async function regenerateRestoredWorktrees(opts) {
5524
5708
  async function seedWorkspaceFromDir(opts) {
5525
5709
  const log = opts.onLog ?? (() => {
5526
5710
  });
5527
- const tarOut = await execa8("tar", ["-C", opts.hostSource, "-cf", "-", "."], {
5711
+ const tarOut = await execa9("tar", ["-C", opts.hostSource, "-cf", "-", "."], {
5528
5712
  encoding: "buffer",
5529
5713
  reject: false
5530
5714
  });
5531
5715
  if (tarOut.exitCode !== 0) {
5532
5716
  throw new GitWorktreeError(`tar of ${opts.hostSource} failed: ${tarOut.stderr}`);
5533
5717
  }
5534
- const tarIn = await execa8(
5718
+ const tarIn = await execa9(
5535
5719
  "docker",
5536
5720
  ["exec", "-i", "--user", "1000:1000", opts.container, "tar", "-C", "/workspace", "-xf", "-"],
5537
5721
  { input: tarOut.stdout, reject: false }
@@ -5542,13 +5726,13 @@ async function seedWorkspaceFromDir(opts) {
5542
5726
  log(`seeded /workspace from ${opts.hostSource}`);
5543
5727
  }
5544
5728
  async function removeInBoxWorktree(args) {
5545
- const remove = await execa8(
5729
+ const remove = await execa9(
5546
5730
  "git",
5547
5731
  ["-C", args.hostMainRepo, "worktree", "remove", "--force", args.gitWorktreePath],
5548
5732
  { reject: false }
5549
5733
  );
5550
5734
  if (remove.exitCode === 0) return;
5551
- await execa8("git", ["-C", args.hostMainRepo, "worktree", "prune"], { reject: false });
5735
+ await execa9("git", ["-C", args.hostMainRepo, "worktree", "prune"], { reject: false });
5552
5736
  }
5553
5737
  function ctParent(p) {
5554
5738
  const i = p.lastIndexOf("/");
@@ -5579,20 +5763,20 @@ async function resyncWorkspaceFromHost(opts) {
5579
5763
  const hostMain = w.hostMainRepo;
5580
5764
  const boxBranch = w.branch;
5581
5765
  const res = { containerPath: ct, mergeConflicts: [], overlaySkipped: [] };
5582
- const hostBranchProbe = await execa8(
5766
+ const hostBranchProbe = await execa9(
5583
5767
  "git",
5584
5768
  ["-C", hostMain, "symbolic-ref", "--short", "-q", "HEAD"],
5585
5769
  { reject: false }
5586
5770
  );
5587
- const hostRef = hostBranchProbe.exitCode === 0 && hostBranchProbe.stdout.trim() ? hostBranchProbe.stdout.trim() : (await execa8("git", ["-C", hostMain, "rev-parse", "HEAD"], { reject: false })).stdout.trim();
5771
+ const hostRef = hostBranchProbe.exitCode === 0 && hostBranchProbe.stdout.trim() ? hostBranchProbe.stdout.trim() : (await execa9("git", ["-C", hostMain, "rev-parse", "HEAD"], { reject: false })).stdout.trim();
5588
5772
  if (!hostRef) {
5589
5773
  log(`resync: ${ct}: could not resolve host ref; skipping`);
5590
5774
  results.push(res);
5591
5775
  continue;
5592
5776
  }
5593
- const stash = await execa8("git", ["-C", hostMain, "stash", "create"], { reject: false });
5777
+ const stash = await execa9("git", ["-C", hostMain, "stash", "create"], { reject: false });
5594
5778
  const hostStashSha = stash.exitCode === 0 ? stash.stdout.trim() || null : null;
5595
- const untracked = await execa8(
5779
+ const untracked = await execa9(
5596
5780
  "git",
5597
5781
  ["-C", hostMain, "ls-files", "--others", "--exclude-standard", "-z"],
5598
5782
  { reject: false }
@@ -5662,7 +5846,7 @@ async function resyncWorkspaceFromHost(opts) {
5662
5846
  }
5663
5847
  }
5664
5848
  if (hostUntracked.length > 0) {
5665
- const probe = await execa8(
5849
+ const probe = await execa9(
5666
5850
  "docker",
5667
5851
  [
5668
5852
  "exec",
@@ -5709,13 +5893,13 @@ async function resyncWorkspaceFromHost(opts) {
5709
5893
  log(`resync: ${ct}: ${String(identical)} untracked host file(s) already identical in box (no-op)`);
5710
5894
  }
5711
5895
  if (toCopy.length > 0) {
5712
- const tarOut = await execa8("tar", ["-C", hostMain, "--null", "-T", "-", "-cf", "-"], {
5896
+ const tarOut = await execa9("tar", ["-C", hostMain, "--null", "-T", "-", "-cf", "-"], {
5713
5897
  input: toCopy.join("\0"),
5714
5898
  encoding: "buffer",
5715
5899
  reject: false
5716
5900
  });
5717
5901
  if (tarOut.exitCode === 0) {
5718
- await execa8(
5902
+ await execa9(
5719
5903
  "docker",
5720
5904
  ["exec", "-i", "--user", "vscode", opts.container, "tar", "-C", ct, "-xf", "-"],
5721
5905
  { input: tarOut.stdout, reject: false }
@@ -5738,7 +5922,7 @@ var cached = null;
5738
5922
  async function detectPortless() {
5739
5923
  if (cached !== null) return cached;
5740
5924
  try {
5741
- const ver = await execa9(PORTLESS_BIN, SUB_VERSION, { reject: false });
5925
+ const ver = await execa10(PORTLESS_BIN, SUB_VERSION, { reject: false });
5742
5926
  if (ver.exitCode !== 0) {
5743
5927
  cached = { installed: false, proxyRunning: false };
5744
5928
  return cached;
@@ -5758,7 +5942,7 @@ function resetPortlessCache() {
5758
5942
  }
5759
5943
  async function portlessAlias(name, port) {
5760
5944
  try {
5761
- const r = await execa9(PORTLESS_BIN, [SUB_ALIAS, name, String(port)], { reject: false });
5945
+ const r = await execa10(PORTLESS_BIN, [SUB_ALIAS, name, String(port)], { reject: false });
5762
5946
  return r.exitCode === 0;
5763
5947
  } catch {
5764
5948
  return false;
@@ -5766,7 +5950,7 @@ async function portlessAlias(name, port) {
5766
5950
  }
5767
5951
  async function portlessUnalias(name) {
5768
5952
  try {
5769
- const r = await execa9(PORTLESS_BIN, [SUB_ALIAS, SUB_ALIAS_REMOVE, name], { reject: false });
5953
+ const r = await execa10(PORTLESS_BIN, [SUB_ALIAS, SUB_ALIAS_REMOVE, name], { reject: false });
5770
5954
  return r.exitCode === 0;
5771
5955
  } catch {
5772
5956
  return false;
@@ -5775,7 +5959,7 @@ async function portlessUnalias(name) {
5775
5959
  async function portlessGetUrl(name) {
5776
5960
  const fallback = `https://${name}.localhost`;
5777
5961
  try {
5778
- const r = await execa9(PORTLESS_BIN, [SUB_GET, name], { reject: false });
5962
+ const r = await execa10(PORTLESS_BIN, [SUB_GET, name], { reject: false });
5779
5963
  const out = (r.stdout ?? "").trim();
5780
5964
  if (r.exitCode === 0 && /^https?:\/\//.test(out)) return out;
5781
5965
  } catch {
@@ -5796,7 +5980,7 @@ function portlessBrowserEnv(boxName, opts) {
5796
5980
  }
5797
5981
  async function installPortless() {
5798
5982
  try {
5799
- const r = await execa9("npm", ["install", "-g", "portless"], { reject: false });
5983
+ const r = await execa10("npm", ["install", "-g", "portless"], { reject: false });
5800
5984
  return r.exitCode === 0;
5801
5985
  } catch {
5802
5986
  return false;
@@ -5804,7 +5988,7 @@ async function installPortless() {
5804
5988
  }
5805
5989
  async function startPortlessProxy() {
5806
5990
  try {
5807
- const r = await execa9(
5991
+ const r = await execa10(
5808
5992
  PORTLESS_BIN,
5809
5993
  ["proxy", "start", "--no-tls", "-p", String(PORTLESS_PROXY_PORT)],
5810
5994
  { reject: false }
@@ -5851,14 +6035,14 @@ async function resolvePortlessHostStateDir(override) {
5851
6035
  const live = await findLivePortlessStateDir();
5852
6036
  if (live) return live;
5853
6037
  const home = join8(homedir6(), ".portless");
5854
- if (existsSync(home)) return home;
5855
- if (existsSync("/tmp/portless")) return "/tmp/portless";
6038
+ if (existsSync2(home)) return home;
6039
+ if (existsSync2("/tmp/portless")) return "/tmp/portless";
5856
6040
  return home;
5857
6041
  }
5858
6042
  async function isProxyRunning() {
5859
6043
  if (await findLivePortlessStateDir() !== null) return true;
5860
6044
  try {
5861
- const r = await execa9("pgrep", ["-f", "portless proxy"], { reject: false });
6045
+ const r = await execa10("pgrep", ["-f", "portless proxy"], { reject: false });
5862
6046
  return r.exitCode === 0 && (r.stdout ?? "").trim().length > 0;
5863
6047
  } catch {
5864
6048
  return false;
@@ -5909,12 +6093,12 @@ async function findExcludedDirs(root, excluded = EXCLUDE_DIRS) {
5909
6093
  return matches;
5910
6094
  }
5911
6095
  async function createSnapshot(opts) {
5912
- const source = resolve2(opts.source);
5913
- const destination = resolve2(opts.destination);
6096
+ const source = resolve22(opts.source);
6097
+ const destination = resolve22(opts.destination);
5914
6098
  const excluded = opts.excluded ?? EXCLUDE_DIRS;
5915
6099
  await mkdir42(SNAPSHOTS_ROOT, { recursive: true });
5916
6100
  const cpArgs = platform() === "darwin" ? ["-cR"] : ["-R"];
5917
- await execa10("cp", [...cpArgs, `${source}/`, destination]);
6101
+ await execa11("cp", [...cpArgs, `${source}/`, destination]);
5918
6102
  const toPrune = await findExcludedDirs(destination, excluded);
5919
6103
  await Promise.all(toPrune.map((p) => rm32(p, { recursive: true, force: true })));
5920
6104
  return { destination, prunedPaths: toPrune };
@@ -5922,7 +6106,7 @@ async function createSnapshot(opts) {
5922
6106
  var CHECKPOINTS_ROOT = join10(homedir8(), ".agentbox", "checkpoints");
5923
6107
  var CHECKPOINT_IMAGE_PREFIX = "agentbox-ckpt-";
5924
6108
  function checkpointImageTag(projectRoot, name) {
5925
- const mnemonic = sanitizeMnemonic(basename32(projectRoot));
6109
+ const mnemonic = sanitizeMnemonic(basename4(projectRoot));
5926
6110
  return `${CHECKPOINT_IMAGE_PREFIX}${hashProjectPath(projectRoot)}_${mnemonic}:${name}`;
5927
6111
  }
5928
6112
  function projectCheckpointsDir(projectRoot) {
@@ -6039,7 +6223,7 @@ async function runCleanup(container, log) {
6039
6223
  }
6040
6224
  }
6041
6225
  async function inspectImageConfig(imageRef) {
6042
- const r = await execa11("docker", ["image", "inspect", imageRef], { reject: false });
6226
+ const r = await execa12("docker", ["image", "inspect", imageRef], { reject: false });
6043
6227
  if (r.exitCode !== 0) {
6044
6228
  throw new CheckpointError(`docker image inspect ${imageRef} failed`, r.stdout, r.stderr);
6045
6229
  }
@@ -6115,14 +6299,14 @@ async function createCheckpoint(opts) {
6115
6299
  await runCleanup(box.container, log);
6116
6300
  if (type === "layered") {
6117
6301
  log(`docker commit ${box.container} -> ${tag} (layered)`);
6118
- const r = await execa11("docker", ["commit", box.container, tag], { reject: false });
6302
+ const r = await execa12("docker", ["commit", box.container, tag], { reject: false });
6119
6303
  if (r.exitCode !== 0) {
6120
6304
  throw new CheckpointError(`docker commit failed for ${box.container}`, r.stdout, r.stderr);
6121
6305
  }
6122
6306
  } else {
6123
6307
  log(`docker commit ${box.container} -> <intermediate> (flattened path)`);
6124
6308
  const intermediate = `${tag}-intermediate`;
6125
- const commit = await execa11("docker", ["commit", box.container, intermediate], {
6309
+ const commit = await execa12("docker", ["commit", box.container, intermediate], {
6126
6310
  reject: false
6127
6311
  });
6128
6312
  if (commit.exitCode !== 0) {
@@ -6167,7 +6351,7 @@ async function createCheckpoint(opts) {
6167
6351
  }
6168
6352
  async function flattenImage(sourceTag, destTag, log) {
6169
6353
  const tmpName = `agentbox-flatten-${Date.now().toString(36)}`;
6170
- const create = await execa11(
6354
+ const create = await execa12(
6171
6355
  "docker",
6172
6356
  ["create", "--name", tmpName, sourceTag, "sleep", "0"],
6173
6357
  { reject: false }
@@ -6179,7 +6363,7 @@ async function flattenImage(sourceTag, destTag, log) {
6179
6363
  try {
6180
6364
  const rootfsPath = join10(scratch, "rootfs.tar");
6181
6365
  log(`exporting rootfs of ${sourceTag} to ${rootfsPath}`);
6182
- const exp = await execa11("docker", ["export", "-o", rootfsPath, tmpName], { reject: false });
6366
+ const exp = await execa12("docker", ["export", "-o", rootfsPath, tmpName], { reject: false });
6183
6367
  if (exp.exitCode !== 0) {
6184
6368
  throw new CheckpointError(`docker export failed`, exp.stdout, exp.stderr);
6185
6369
  }
@@ -6192,7 +6376,7 @@ async function flattenImage(sourceTag, destTag, log) {
6192
6376
  ];
6193
6377
  await writeFile32(join10(scratch, "Dockerfile"), lines.join("\n") + "\n", "utf8");
6194
6378
  log(`building flattened ${destTag} from rootfs.tar (FROM scratch)`);
6195
- const build = await execa11(
6379
+ const build = await execa12(
6196
6380
  "docker",
6197
6381
  ["build", "-t", destTag, "-f", join10(scratch, "Dockerfile"), scratch],
6198
6382
  { reject: false }
@@ -6201,7 +6385,7 @@ async function flattenImage(sourceTag, destTag, log) {
6201
6385
  throw new CheckpointError(`flatten docker build failed`, build.stdout, build.stderr);
6202
6386
  }
6203
6387
  } finally {
6204
- await execa11("docker", ["rm", "-f", tmpName], { reject: false });
6388
+ await execa12("docker", ["rm", "-f", tmpName], { reject: false });
6205
6389
  await rm4(scratch, { recursive: true, force: true });
6206
6390
  }
6207
6391
  }
@@ -6245,7 +6429,7 @@ async function pathExists5(p) {
6245
6429
  }
6246
6430
  async function writeBoxEnvFile(container, env) {
6247
6431
  const body = formatBoxEnvBody(env);
6248
- const result = await execa12(
6432
+ const result = await execa13(
6249
6433
  "docker",
6250
6434
  ["exec", "--user", "root", "-i", container, "sh", "-c", "umask 022 && cat > /etc/agentbox/box.env"],
6251
6435
  { input: body, reject: false }
@@ -6269,7 +6453,7 @@ function shellSingleQuote(s) {
6269
6453
  return `'${s.replace(/'/g, `'\\''`)}'`;
6270
6454
  }
6271
6455
  async function ensureHomeOwnedByVscode(container) {
6272
- await execa13(
6456
+ await execa14(
6273
6457
  "docker",
6274
6458
  [
6275
6459
  "exec",
@@ -6418,16 +6602,16 @@ async function spawnRelay(relayBin, cliEntry, log) {
6418
6602
  }
6419
6603
  function resolveRelayBin() {
6420
6604
  const override = process.env.AGENTBOX_RELAY_BIN;
6421
- if (override && existsSync2(override)) return override;
6422
- const here = dirname3(fileURLToPath(import.meta.url));
6605
+ if (override && existsSync3(override)) return override;
6606
+ const here = dirname22(fileURLToPath(import.meta.url));
6423
6607
  const candidates = [
6424
- resolve22(here, "..", "runtime", "relay", "bin.cjs"),
6425
- resolve22(here, "..", "..", "relay", "dist", "bin.cjs"),
6426
- resolve22(here, "..", "..", "..", "@agentbox", "relay", "dist", "bin.cjs"),
6427
- resolve22(here, "..", "..", "node_modules", "@agentbox", "relay", "dist", "bin.cjs")
6608
+ resolve3(here, "..", "runtime", "relay", "bin.cjs"),
6609
+ resolve3(here, "..", "..", "relay", "dist", "bin.cjs"),
6610
+ resolve3(here, "..", "..", "..", "@agentbox", "relay", "dist", "bin.cjs"),
6611
+ resolve3(here, "..", "..", "node_modules", "@agentbox", "relay", "dist", "bin.cjs")
6428
6612
  ];
6429
6613
  for (const c of candidates) {
6430
- if (existsSync2(c)) return c;
6614
+ if (existsSync3(c)) return c;
6431
6615
  }
6432
6616
  throw new Error(
6433
6617
  `could not locate @agentbox/relay bin; tried:
@@ -6436,29 +6620,29 @@ function resolveRelayBin() {
6436
6620
  }
6437
6621
  function resolveCliEntry() {
6438
6622
  const override = process.env.AGENTBOX_CLI_ENTRY;
6439
- if (override && existsSync2(override)) return override;
6440
- const here = dirname3(fileURLToPath(import.meta.url));
6623
+ if (override && existsSync3(override)) return override;
6624
+ const here = dirname22(fileURLToPath(import.meta.url));
6441
6625
  const candidates = [
6442
6626
  // Bundled CLI (dev + published): this module IS bundled into the CLI
6443
6627
  // entry, so the entry is index.js next to this file.
6444
- resolve22(here, "index.js"),
6445
- resolve22(here, "..", "..", "..", "apps", "cli", "dist", "index.js"),
6446
- resolve22(here, "..", "..", "..", "..", "dist", "index.js")
6628
+ resolve3(here, "index.js"),
6629
+ resolve3(here, "..", "..", "..", "apps", "cli", "dist", "index.js"),
6630
+ resolve3(here, "..", "..", "..", "..", "dist", "index.js")
6447
6631
  ];
6448
6632
  for (const c of candidates) {
6449
- if (existsSync2(c)) return c;
6633
+ if (existsSync3(c)) return c;
6450
6634
  }
6451
6635
  return null;
6452
6636
  }
6453
6637
  async function stageRelayHome(version, log) {
6454
6638
  if (!version || version === "0.0.0-dev") return null;
6455
6639
  if (process.env.AGENTBOX_RELAY_BIN || process.env.AGENTBOX_CLI_ENTRY) return null;
6456
- const cliRoot = findCliRoot(dirname3(fileURLToPath(import.meta.url)));
6640
+ const cliRoot = findCliRoot(dirname22(fileURLToPath(import.meta.url)));
6457
6641
  if (cliRoot === null) return null;
6458
6642
  const homeDir = join11(RELAY_HOME_DIR, version);
6459
6643
  const stagedEntry = join11(homeDir, "dist", "index.js");
6460
6644
  const stagedBin = join11(homeDir, "runtime", "relay", "bin.cjs");
6461
- if (existsSync2(stagedEntry) && existsSync2(stagedBin)) {
6645
+ if (existsSync3(stagedEntry) && existsSync3(stagedBin)) {
6462
6646
  return { relayBin: stagedBin, cliEntry: stagedEntry };
6463
6647
  }
6464
6648
  const nodeModules = resolveDepRoot(join11(cliRoot, "dist", "index.js"));
@@ -6470,7 +6654,7 @@ async function stageRelayHome(version, log) {
6470
6654
  await mkdir6(tmpDir, { recursive: true });
6471
6655
  for (const sub of ["dist", "runtime", "share"]) {
6472
6656
  const src = join11(cliRoot, sub);
6473
- if (existsSync2(src)) await cp(src, join11(tmpDir, sub), { recursive: true });
6657
+ if (existsSync3(src)) await cp(src, join11(tmpDir, sub), { recursive: true });
6474
6658
  }
6475
6659
  await cp(nodeModules, join11(tmpDir, "node_modules"), { recursive: true, dereference: true });
6476
6660
  await rm5(homeDir, { recursive: true, force: true });
@@ -6478,7 +6662,7 @@ async function stageRelayHome(version, log) {
6478
6662
  } catch (err) {
6479
6663
  await rm5(tmpDir, { recursive: true, force: true }).catch(() => {
6480
6664
  });
6481
- if (existsSync2(stagedEntry) && existsSync2(stagedBin)) {
6665
+ if (existsSync3(stagedEntry) && existsSync3(stagedBin)) {
6482
6666
  return { relayBin: stagedBin, cliEntry: stagedEntry };
6483
6667
  }
6484
6668
  log(`relay home staging failed (${err instanceof Error ? err.message : String(err)}); using bundle paths`);
@@ -6490,8 +6674,8 @@ async function stageRelayHome(version, log) {
6490
6674
  return { relayBin: stagedBin, cliEntry: stagedEntry };
6491
6675
  }
6492
6676
  function findCliRoot(moduleDir) {
6493
- for (const root of [resolve22(moduleDir, ".."), resolve22(moduleDir, "..", "..")]) {
6494
- if (existsSync2(join11(root, "dist", "index.js")) && existsSync2(join11(root, "runtime", "relay", "bin.cjs"))) {
6677
+ for (const root of [resolve3(moduleDir, ".."), resolve3(moduleDir, "..", "..")]) {
6678
+ if (existsSync3(join11(root, "dist", "index.js")) && existsSync3(join11(root, "runtime", "relay", "bin.cjs"))) {
6495
6679
  return root;
6496
6680
  }
6497
6681
  }
@@ -6506,7 +6690,7 @@ function resolveDepRoot(fromFile) {
6506
6690
  const idx = main.lastIndexOf(marker);
6507
6691
  if (idx === -1) return null;
6508
6692
  const nm = main.slice(0, idx + marker.length - 1);
6509
- return existsSync2(join11(nm, "commander")) ? nm : null;
6693
+ return existsSync3(join11(nm, "commander")) ? nm : null;
6510
6694
  } catch {
6511
6695
  return null;
6512
6696
  }
@@ -6667,7 +6851,8 @@ async function registerBoxWithRelay(args) {
6667
6851
  worktrees,
6668
6852
  previewUrl: args.previewUrl,
6669
6853
  previewToken: args.previewToken,
6670
- bridgeToken: args.bridgeToken
6854
+ bridgeToken: args.bridgeToken,
6855
+ autoApproveHostActions: args.autoApproveHostActions
6671
6856
  });
6672
6857
  }
6673
6858
  async function forgetBoxFromRelay(boxId) {
@@ -6808,7 +6993,8 @@ async function rehydrateRelayRegistry(boxes) {
6808
6993
  worktrees: b.gitWorktrees,
6809
6994
  previewUrl: b.relayPreviewUrl,
6810
6995
  previewToken: b.relayPreviewToken,
6811
- bridgeToken: b.bridgeToken
6996
+ bridgeToken: b.bridgeToken,
6997
+ autoApproveHostActions: b.autoApproveHostActions
6812
6998
  });
6813
6999
  } catch {
6814
7000
  }
@@ -6959,7 +7145,7 @@ function persistableLimits(lim) {
6959
7145
  return Object.keys(out).length > 0 ? out : void 0;
6960
7146
  }
6961
7147
  function sanitizeBasename(workspacePath) {
6962
- const raw = basename4(resolve3(workspacePath));
7148
+ const raw = basename5(resolve4(workspacePath));
6963
7149
  return raw.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-._]+|[-._]+$/g, "").slice(0, 30).replace(/[-._]+$/, "");
6964
7150
  }
6965
7151
  function defaultBoxName(workspacePath, id) {
@@ -6990,7 +7176,7 @@ async function buildIdentityMounts() {
6990
7176
  async function createBox(opts) {
6991
7177
  const log = opts.onLog ?? (() => {
6992
7178
  });
6993
- const workspace = resolve3(opts.workspacePath);
7179
+ const workspace = resolve4(opts.workspacePath);
6994
7180
  if (!await pathExists6(workspace)) {
6995
7181
  throw new Error(`workspace does not exist: ${workspace}`);
6996
7182
  }
@@ -7283,6 +7469,7 @@ async function createBox(opts) {
7283
7469
  }
7284
7470
  }
7285
7471
  for (const v of extraVolumes) log(`mounting agent dir: ${v}`);
7472
+ const autoApproveHostActions = (await loadEffectiveConfig(opts.projectRoot ?? workspace)).effective.box.autoApproveHostActions;
7286
7473
  const relayToken = generateRelayToken();
7287
7474
  if (relayUp) {
7288
7475
  try {
@@ -7293,7 +7480,8 @@ async function createBox(opts) {
7293
7480
  containerName,
7294
7481
  createdAt,
7295
7482
  projectIndex,
7296
- worktrees: gitWorktreeRecords
7483
+ worktrees: gitWorktreeRecords,
7484
+ autoApproveHostActions
7297
7485
  });
7298
7486
  log(`registered box token with relay`);
7299
7487
  } catch (err) {
@@ -7359,6 +7547,7 @@ async function createBox(opts) {
7359
7547
  gitWorktrees: gitWorktreeRecords.length > 0 ? gitWorktreeRecords : void 0,
7360
7548
  withPlaywright: opts.withPlaywright ? true : void 0,
7361
7549
  withEnv: opts.withEnv ? true : void 0,
7550
+ autoApproveHostActions: autoApproveHostActions ? true : void 0,
7362
7551
  vncEnabled: vncEnabled ? true : void 0,
7363
7552
  vncContainerPort: vncEnabled ? VNC_CONTAINER_PORT : void 0,
7364
7553
  vncPassword,
@@ -7416,7 +7605,7 @@ async function createBox(opts) {
7416
7605
  } catch (err) {
7417
7606
  if (opts.useBranch !== void 0) {
7418
7607
  log(`seedWorkspace failed for --use-branch ${opts.useBranch}; cleaning up the box`);
7419
- await execa14("docker", ["rm", "-f", containerName], { reject: false });
7608
+ await execa15("docker", ["rm", "-f", containerName], { reject: false });
7420
7609
  await removeBoxRecord(id);
7421
7610
  for (const w of gitWorktreeRecords) {
7422
7611
  await removeInBoxWorktree({
@@ -7479,7 +7668,7 @@ async function createBox(opts) {
7479
7668
  else log(`agentbox-ctl daemon did not become reachable: ${ctl.reason}`);
7480
7669
  if (opts.withPlaywright) {
7481
7670
  log("installing @playwright/cli@latest (--with-playwright)");
7482
- const result = await execa14(
7671
+ const result = await execa15(
7483
7672
  "docker",
7484
7673
  [
7485
7674
  "exec",
@@ -7653,7 +7842,7 @@ function parseShellSessionList(stdout) {
7653
7842
  return out;
7654
7843
  }
7655
7844
  async function listShellSessions(container, user) {
7656
- const res = await execa15(
7845
+ const res = await execa16(
7657
7846
  "docker",
7658
7847
  [
7659
7848
  "exec",
@@ -7677,7 +7866,7 @@ async function startShellSession(opts) {
7677
7866
  const login = opts.login !== false;
7678
7867
  const term = process.env["TERM"] ?? "xterm-256color";
7679
7868
  const cmd = login ? "bash -l" : "bash";
7680
- const result = await execa15(
7869
+ const result = await execa16(
7681
7870
  "docker",
7682
7871
  [
7683
7872
  "exec",
@@ -7726,7 +7915,7 @@ function buildShellSessionAttachArgv(container, sessionName, user) {
7726
7915
  }
7727
7916
  async function shellSessionInfo(container, sessionName, user) {
7728
7917
  const name = sessionName ?? DEFAULT_SHELL_SESSION;
7729
- const has = await execa15(
7918
+ const has = await execa16(
7730
7919
  "docker",
7731
7920
  ["exec", "--user", user ?? CONTAINER_USER, container, "tmux", "has-session", "-t", name],
7732
7921
  { reject: false }
@@ -7734,7 +7923,7 @@ async function shellSessionInfo(container, sessionName, user) {
7734
7923
  return { running: has.exitCode === 0, sessionName: name };
7735
7924
  }
7736
7925
  async function killShellSession(container, sessionName, user) {
7737
- const res = await execa15(
7926
+ const res = await execa16(
7738
7927
  "docker",
7739
7928
  [
7740
7929
  "exec",
@@ -8017,7 +8206,8 @@ async function startBox(idOrName) {
8017
8206
  containerName: box.container,
8018
8207
  createdAt: box.createdAt,
8019
8208
  projectIndex: box.projectIndex,
8020
- worktrees: box.gitWorktrees
8209
+ worktrees: box.gitWorktrees,
8210
+ autoApproveHostActions: box.autoApproveHostActions
8021
8211
  });
8022
8212
  } catch {
8023
8213
  }
@@ -8036,7 +8226,7 @@ async function getBoxHostPaths(idOrName) {
8036
8226
  }
8037
8227
  async function dirSizeBytes(path) {
8038
8228
  try {
8039
- const result = await execa16("du", ["-sk", path], { reject: false });
8229
+ const result = await execa17("du", ["-sk", path], { reject: false });
8040
8230
  if (result.exitCode !== 0) return null;
8041
8231
  const sizeKb = Number.parseInt((result.stdout ?? "").split(/\s+/)[0] ?? "", 10);
8042
8232
  if (Number.isNaN(sizeKb)) return null;
@@ -8181,7 +8371,7 @@ async function listBoxDirs() {
8181
8371
  }
8182
8372
  }
8183
8373
  async function listCheckpointImageTags() {
8184
- const r = await execa16(
8374
+ const r = await execa17(
8185
8375
  "docker",
8186
8376
  ["image", "ls", "--format", "{{.Repository}}:{{.Tag}}", `${CHECKPOINT_IMAGE_PREFIX}*`],
8187
8377
  { reject: false }
@@ -8289,7 +8479,7 @@ async function pruneBoxes(opts = {}) {
8289
8479
  } catch {
8290
8480
  }
8291
8481
  try {
8292
- await execa16("docker", ["image", "rm", RELAY_IMAGE_REF], { reject: false });
8482
+ await execa17("docker", ["image", "rm", RELAY_IMAGE_REF], { reject: false });
8293
8483
  } catch {
8294
8484
  }
8295
8485
  try {
@@ -8351,7 +8541,7 @@ function splitPair(raw) {
8351
8541
  return [parts[0].trim(), parts[1].trim()];
8352
8542
  }
8353
8543
  async function duBytes(path) {
8354
- const result = await execa17("du", ["-sk", path], { reject: false });
8544
+ const result = await execa18("du", ["-sk", path], { reject: false });
8355
8545
  if (result.exitCode !== 0) return null;
8356
8546
  const kb = Number.parseInt((result.stdout ?? "").split(/\s+/)[0] ?? "", 10);
8357
8547
  return Number.isNaN(kb) ? null : kb * 1024;
@@ -8364,7 +8554,7 @@ async function volumeSizeBytes(name) {
8364
8554
  const sz = await duBytes(live);
8365
8555
  if (sz !== null) return sz;
8366
8556
  }
8367
- const df = await execa17(
8557
+ const df = await execa18(
8368
8558
  "docker",
8369
8559
  ["system", "df", "-v", "--format", "{{json .Volumes}}"],
8370
8560
  { reject: false }
@@ -8385,7 +8575,7 @@ async function volumeSizeBytes(name) {
8385
8575
  return null;
8386
8576
  }
8387
8577
  async function imageBytes(tag) {
8388
- const r = await execa17("docker", ["image", "inspect", tag, "--format", "{{.Size}}"], {
8578
+ const r = await execa18("docker", ["image", "inspect", tag, "--format", "{{.Size}}"], {
8389
8579
  reject: false
8390
8580
  });
8391
8581
  if (r.exitCode !== 0) return null;
@@ -8396,7 +8586,7 @@ async function projectCheckpointImageBytes(projectRoot, name) {
8396
8586
  return imageBytes(checkpointImageTag(projectRoot, name));
8397
8587
  }
8398
8588
  async function allCheckpointImagesBytes() {
8399
- const r = await execa17(
8589
+ const r = await execa18(
8400
8590
  "docker",
8401
8591
  [
8402
8592
  "image",
@@ -8448,7 +8638,7 @@ function reconcileLimits(persisted, dockerJson) {
8448
8638
  };
8449
8639
  }
8450
8640
  async function containerWritableBytes(container) {
8451
- const r = await execa17(
8641
+ const r = await execa18(
8452
8642
  "docker",
8453
8643
  ["ps", "-a", "--filter", `name=^${container}$`, "--format", "{{.Size}}", "--size"],
8454
8644
  { reject: false }
@@ -8495,7 +8685,7 @@ async function boxResourceStats(record) {
8495
8685
  if (await inspectContainerStatus(record.container) !== "running") {
8496
8686
  return base;
8497
8687
  }
8498
- const proc = await execa17(
8688
+ const proc = await execa18(
8499
8689
  "docker",
8500
8690
  ["stats", "--no-stream", "--format", "{{json .}}", record.container],
8501
8691
  { reject: false }
@@ -8530,125 +8720,6 @@ async function boxResourceStats(record) {
8530
8720
  blockWriteBytes: blkPair ? parseDockerSize(blkPair[1]) : null
8531
8721
  };
8532
8722
  }
8533
- function posixDirname(p) {
8534
- return posix.dirname(p) || "/";
8535
- }
8536
- function asText(s) {
8537
- if (s === void 0) return "";
8538
- if (typeof s === "string") return s;
8539
- return Buffer.from(s).toString("utf8");
8540
- }
8541
- async function uploadToBox(box, hostSrc, boxDst) {
8542
- const srcAbs = resolve4(hostSrc);
8543
- if (!existsSync3(srcAbs)) throw new Error(`source not found: ${hostSrc}`);
8544
- const srcBasename = basename5(srcAbs);
8545
- const srcParent = dirname22(srcAbs);
8546
- let boxParent;
8547
- let finalName;
8548
- if (boxDst.endsWith("/")) {
8549
- boxParent = boxDst.replace(/\/+$/, "") || "/";
8550
- finalName = srcBasename;
8551
- } else {
8552
- const isDir2 = await execa18(
8553
- "docker",
8554
- ["exec", box.container, "test", "-d", boxDst],
8555
- { reject: false }
8556
- );
8557
- if (isDir2.exitCode === 0) {
8558
- boxParent = boxDst.replace(/\/+$/, "") || "/";
8559
- finalName = srcBasename;
8560
- } else {
8561
- boxParent = posixDirname(boxDst);
8562
- finalName = posix.basename(boxDst);
8563
- }
8564
- }
8565
- const finalPath = boxParent === "/" ? `/${finalName}` : `${boxParent}/${finalName}`;
8566
- const mk = await execa18(
8567
- "docker",
8568
- ["exec", "--user", "root", box.container, "mkdir", "-p", boxParent],
8569
- { reject: false }
8570
- );
8571
- if (mk.exitCode !== 0) {
8572
- throw new Error(`mkdir -p ${boxParent} in box failed: ${asText(mk.stderr).slice(0, 300)}`);
8573
- }
8574
- const packed = await execa18("tar", ["-C", srcParent, "-cf", "-", srcBasename], {
8575
- encoding: "buffer",
8576
- reject: false,
8577
- env: { ...process.env, COPYFILE_DISABLE: "1" }
8578
- });
8579
- if (packed.exitCode !== 0) {
8580
- throw new Error(`tar pack failed: ${asText(packed.stderr).slice(0, 300)}`);
8581
- }
8582
- const extract = await execa18(
8583
- "docker",
8584
- ["exec", "-i", "--user", "root", box.container, "tar", "-xf", "-", "-C", boxParent],
8585
- { input: packed.stdout, reject: false }
8586
- );
8587
- if (extract.exitCode !== 0) {
8588
- throw new Error(`tar extract in box failed: ${asText(extract.stderr).slice(0, 300)}`);
8589
- }
8590
- if (finalName !== srcBasename) {
8591
- const initial = boxParent === "/" ? `/${srcBasename}` : `${boxParent}/${srcBasename}`;
8592
- const mv = await execa18(
8593
- "docker",
8594
- ["exec", "--user", "root", box.container, "mv", initial, finalPath],
8595
- { reject: false }
8596
- );
8597
- if (mv.exitCode !== 0) {
8598
- throw new Error(
8599
- `rename ${initial} -> ${finalPath} in box failed: ${asText(mv.stderr).slice(0, 300)}`
8600
- );
8601
- }
8602
- }
8603
- const chown = await execa18(
8604
- "docker",
8605
- ["exec", "--user", "root", box.container, "chown", "-R", "1000:1000", finalPath],
8606
- { reject: false }
8607
- );
8608
- if (chown.exitCode !== 0) {
8609
- return {
8610
- finalPath,
8611
- warn: `chown ${finalPath} to vscode (uid 1000) failed; ownership inside the box may be root.`
8612
- };
8613
- }
8614
- return { finalPath };
8615
- }
8616
- async function downloadFromBox(box, boxSrc, hostDst) {
8617
- const srcBasename = posix.basename(boxSrc);
8618
- const srcParent = posixDirname(boxSrc);
8619
- const dstAbs = resolve4(hostDst);
8620
- let hostParent;
8621
- let finalName;
8622
- const dstExists = existsSync3(dstAbs);
8623
- if (hostDst.endsWith("/") || dstExists && statSync(dstAbs).isDirectory()) {
8624
- hostParent = dstAbs;
8625
- finalName = srcBasename;
8626
- } else {
8627
- hostParent = dirname22(dstAbs);
8628
- finalName = basename5(dstAbs);
8629
- }
8630
- mkdirSync(hostParent, { recursive: true });
8631
- const finalPath = posix.join(hostParent, finalName);
8632
- const packed = await execa18(
8633
- "docker",
8634
- ["exec", box.container, "tar", "-C", srcParent, "-cf", "-", srcBasename],
8635
- { encoding: "buffer", reject: false }
8636
- );
8637
- if (packed.exitCode !== 0) {
8638
- throw new Error(`tar pack in box failed: ${asText(packed.stderr).slice(0, 300)}`);
8639
- }
8640
- const extract = await execa18("tar", ["-xf", "-", "-C", hostParent], {
8641
- input: packed.stdout,
8642
- reject: false
8643
- });
8644
- if (extract.exitCode !== 0) {
8645
- throw new Error(`tar extract on host failed: ${asText(extract.stderr).slice(0, 300)}`);
8646
- }
8647
- if (finalName !== srcBasename) {
8648
- renameSync(posix.join(hostParent, srcBasename), finalPath);
8649
- }
8650
- return { finalPath };
8651
- }
8652
8723
  var dockerProvider = {
8653
8724
  name: "docker",
8654
8725
  async create(req) {
@@ -8724,12 +8795,12 @@ var dockerProvider = {
8724
8795
  const r = await execInBox(box.container, argv, opts?.user ? { user: opts.user } : {});
8725
8796
  return { exitCode: r.exitCode, stdout: r.stdout, stderr: r.stderr };
8726
8797
  },
8727
- async uploadPath(box, hostSrc, boxDst) {
8728
- const r = await uploadToBox(box, hostSrc, boxDst);
8798
+ async uploadPath(box, hostSrc, boxDst, exclude) {
8799
+ const r = await uploadToBox(box, hostSrc, boxDst, exclude);
8729
8800
  return { finalPath: r.finalPath };
8730
8801
  },
8731
- async downloadPath(box, boxSrc, hostDst) {
8732
- const r = await downloadFromBox(box, boxSrc, hostDst);
8802
+ async downloadPath(box, boxSrc, hostDst, exclude) {
8803
+ const r = await downloadFromBox(box, boxSrc, hostDst, exclude);
8733
8804
  return { finalPath: r.finalPath };
8734
8805
  },
8735
8806
  async resolveUrl(box, opts) {
@@ -8928,6 +8999,7 @@ async function ensureBoxBrowser(container, timeoutMs = 8e3, targetUrl = "about:b
8928
8999
  }
8929
9000
 
8930
9001
  export {
9002
+ BUILT_IN_DEFAULTS,
8931
9003
  KEY_REGISTRY,
8932
9004
  lookupKey,
8933
9005
  UserConfigError,
@@ -8974,6 +9046,8 @@ export {
8974
9046
  execInBox,
8975
9047
  removeImage,
8976
9048
  volumeExists,
9049
+ uploadToBox,
9050
+ downloadFromBox,
8977
9051
  CONTAINER_EXPORT_MERGED,
8978
9052
  detectEngine,
8979
9053
  setEngineOverride,
@@ -9174,8 +9248,6 @@ export {
9174
9248
  allCheckpointImagesBytes,
9175
9249
  agentboxHomeBytes,
9176
9250
  boxResourceStats,
9177
- uploadToBox,
9178
- downloadFromBox,
9179
9251
  dockerProvider,
9180
9252
  BOX_WORKFLOWS_DIR,
9181
9253
  BOX_DYNAMIC_SYNC_MANIFEST,
@@ -9186,4 +9258,4 @@ export {
9186
9258
  browserSessionActive,
9187
9259
  ensureBoxBrowser
9188
9260
  };
9189
- //# sourceMappingURL=chunk-TCS5HXJX.js.map
9261
+ //# sourceMappingURL=chunk-MLMFNN4T.js.map