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