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