@madarco/agentbox 0.4.1 → 0.5.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/dist/{chunk-WR5FFGE5.js → chunk-6VTAPD4H.js} +123 -112
- package/dist/chunk-6VTAPD4H.js.map +1 -0
- package/dist/{chunk-J35IH7W5.js → chunk-7J5AJLWG.js} +61 -23
- package/dist/chunk-7J5AJLWG.js.map +1 -0
- package/dist/{chunk-FQD6ZWYW.js → chunk-FJNIFTWK.js} +66 -65
- package/dist/chunk-FJNIFTWK.js.map +1 -0
- package/dist/{chunk-IDR4HVIC.js → chunk-HPZMD5DE.js} +2 -2
- package/dist/chunk-HPZMD5DE.js.map +1 -0
- package/dist/{chunk-NSIECUCS.js → chunk-PXUBE5KS.js} +365 -258
- package/dist/chunk-PXUBE5KS.js.map +1 -0
- package/dist/{chunk-SOMIKEN2.js → chunk-RFC5F5HR.js} +272 -214
- package/dist/chunk-RFC5F5HR.js.map +1 -0
- package/dist/create-AHZ3GVEZ-TGEDL7UX.js +15 -0
- package/dist/index.js +2757 -1854
- package/dist/index.js.map +1 -1
- package/dist/{lifecycle-LURNDNYO-UWQYPNPX.js → lifecycle-LFOL6YFM-TCHDX3J5.js} +5 -5
- package/dist/{state-ZSP3ORXW-WI6KOIG3.js → state-KD7M46ZP-KHFTHFUS.js} +2 -2
- package/dist/stats-Z4BVJODD-HEC4TMUZ.js +19 -0
- package/package.json +5 -4
- package/runtime/docker/Dockerfile.box +47 -19
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +39 -50
- package/runtime/docker/packages/ctl/dist/bin.cjs +219 -148
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup +42 -0
- package/runtime/docker/packages/sandbox-docker/scripts/custom-system-CLAUDE.md +26 -15
- package/runtime/relay/bin.cjs +288 -12
- package/share/agentbox-setup/SKILL.md +39 -50
- package/dist/chunk-FQD6ZWYW.js.map +0 -1
- package/dist/chunk-IDR4HVIC.js.map +0 -1
- package/dist/chunk-J35IH7W5.js.map +0 -1
- package/dist/chunk-NSIECUCS.js.map +0 -1
- package/dist/chunk-SOMIKEN2.js.map +0 -1
- package/dist/chunk-WR5FFGE5.js.map +0 -1
- package/dist/create-4BQY2UYU-CGSW3RGE.js +0 -15
- package/dist/stats-GZFLPYTU-DBJ2DVBJ.js +0 -19
- /package/dist/{create-4BQY2UYU-CGSW3RGE.js.map → create-AHZ3GVEZ-TGEDL7UX.js.map} +0 -0
- /package/dist/{lifecycle-LURNDNYO-UWQYPNPX.js.map → lifecycle-LFOL6YFM-TCHDX3J5.js.map} +0 -0
- /package/dist/{state-ZSP3ORXW-WI6KOIG3.js.map → state-KD7M46ZP-KHFTHFUS.js.map} +0 -0
- /package/dist/{stats-GZFLPYTU-DBJ2DVBJ.js.map → stats-Z4BVJODD-HEC4TMUZ.js.map} +0 -0
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
execInBox,
|
|
7
7
|
orbstackVolumePath,
|
|
8
8
|
removeContainer,
|
|
9
|
+
sanitizeMnemonic,
|
|
9
10
|
volumeExists
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-RFC5F5HR.js";
|
|
11
12
|
|
|
12
|
-
// ../../packages/sandbox-docker/dist/chunk-
|
|
13
|
-
import { spawnSync } from "child_process";
|
|
13
|
+
// ../../packages/sandbox-docker/dist/chunk-LGNJND37.js
|
|
14
14
|
import { mkdir, mkdtemp, readdir, readFile, rm, stat, writeFile } from "fs/promises";
|
|
15
15
|
import { homedir, tmpdir } from "os";
|
|
16
16
|
import { join, relative } from "path";
|
|
@@ -20,6 +20,7 @@ import { execa as execa2 } from "execa";
|
|
|
20
20
|
import { readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
21
21
|
import { join as join2 } from "path";
|
|
22
22
|
import { execa as execa3 } from "execa";
|
|
23
|
+
import { execa as execa4 } from "execa";
|
|
23
24
|
import { mkdir as mkdir2, readdir as readdir3, rm as rm2, stat as stat3 } from "fs/promises";
|
|
24
25
|
import { homedir as homedir2, platform } from "os";
|
|
25
26
|
import { join as join3, resolve } from "path";
|
|
@@ -41,7 +42,7 @@ var RELAY_NETWORK_NAME = "agentbox-net";
|
|
|
41
42
|
var RELAY_IMAGE_REF = "agentbox/relay:dev";
|
|
42
43
|
var MAX_BODY_BYTES = 1024 * 1024;
|
|
43
44
|
|
|
44
|
-
// ../../packages/sandbox-docker/dist/chunk-
|
|
45
|
+
// ../../packages/sandbox-docker/dist/chunk-LGNJND37.js
|
|
45
46
|
function isHostPathHookCommand(command, hostHome) {
|
|
46
47
|
if (typeof command !== "string" || command.length === 0) return false;
|
|
47
48
|
if (hostHome.length === 0) return false;
|
|
@@ -193,6 +194,8 @@ var DEFAULT_CLAUDE_SESSION = "claude";
|
|
|
193
194
|
var CONTAINER_CLAUDE_DIR = "/home/vscode/.claude";
|
|
194
195
|
var CONTAINER_USER = "vscode";
|
|
195
196
|
var CONTAINER_WORKSPACE = "/workspace";
|
|
197
|
+
var IN_BOX_SETUP_GUIDE_PATH = "/usr/local/share/agentbox/setup-guide.md";
|
|
198
|
+
var SETUP_SKILL_DST = "/dst/skills/agentbox-setup/SKILL.md";
|
|
196
199
|
function resolveClaudeVolume(opts) {
|
|
197
200
|
if (opts.isolate) {
|
|
198
201
|
return { volume: `${SHARED_CLAUDE_VOLUME}-${opts.boxId}` };
|
|
@@ -334,6 +337,28 @@ async function ensureClaudeVolume(spec, opts) {
|
|
|
334
337
|
}
|
|
335
338
|
return { created, synced: true, filteredHookCount, clearedInstallMethod, aliasedProjectKey };
|
|
336
339
|
}
|
|
340
|
+
async function seedSetupSkillIntoVolume(volume, image) {
|
|
341
|
+
try {
|
|
342
|
+
const { stdout } = await execa("docker", [
|
|
343
|
+
"run",
|
|
344
|
+
"--rm",
|
|
345
|
+
"--user",
|
|
346
|
+
"0",
|
|
347
|
+
"-v",
|
|
348
|
+
`${volume}:/dst`,
|
|
349
|
+
image,
|
|
350
|
+
"sh",
|
|
351
|
+
"-c",
|
|
352
|
+
// Prints SEEDED only when it actually copies, so the caller can log
|
|
353
|
+
// accurately. The whole thing is `|| true` so an already-present skill
|
|
354
|
+
// (or missing image asset) is a clean no-op, never a non-zero exit.
|
|
355
|
+
`{ [ ! -e /dst/skills/agentbox-setup ] && [ -f ${IN_BOX_SETUP_GUIDE_PATH} ] && mkdir -p /dst/skills/agentbox-setup && cp -a ${IN_BOX_SETUP_GUIDE_PATH} ${SETUP_SKILL_DST} && chown -R 1000:1000 /dst/skills/agentbox-setup && echo SEEDED; } || true`
|
|
356
|
+
]);
|
|
357
|
+
return { seeded: stdout.includes("SEEDED") };
|
|
358
|
+
} catch {
|
|
359
|
+
return { seeded: false };
|
|
360
|
+
}
|
|
361
|
+
}
|
|
337
362
|
async function maybeFilterTo(src, dest, hostHome, opts = {}) {
|
|
338
363
|
let parsed;
|
|
339
364
|
try {
|
|
@@ -564,7 +589,6 @@ function shQuote(arg) {
|
|
|
564
589
|
}
|
|
565
590
|
async function startClaudeSession(opts) {
|
|
566
591
|
const sessionName = opts.sessionName ?? DEFAULT_CLAUDE_SESSION;
|
|
567
|
-
const boxName = opts.boxName ?? opts.container.replace(/^agentbox-/, "");
|
|
568
592
|
const cmd = ["claude", ...opts.claudeArgs].map(shQuote).join(" ");
|
|
569
593
|
const term = process.env["TERM"] ?? "xterm-256color";
|
|
570
594
|
const envFlags = ["-e", `TERM=${term}`];
|
|
@@ -586,7 +610,7 @@ async function startClaudeSession(opts) {
|
|
|
586
610
|
"-s",
|
|
587
611
|
sessionName,
|
|
588
612
|
cmd,
|
|
589
|
-
...buildClaudeStatusBarArgs(sessionName
|
|
613
|
+
...buildClaudeStatusBarArgs(sessionName)
|
|
590
614
|
],
|
|
591
615
|
{ reject: false }
|
|
592
616
|
);
|
|
@@ -660,13 +684,15 @@ function buildClaudeDashboardAttachArgv(container, sessionName) {
|
|
|
660
684
|
dash
|
|
661
685
|
];
|
|
662
686
|
}
|
|
663
|
-
function buildClaudeStatusBarArgs(sessionName
|
|
687
|
+
function buildClaudeStatusBarArgs(sessionName) {
|
|
664
688
|
const s = sessionName;
|
|
665
|
-
const name = boxName;
|
|
666
689
|
return [
|
|
667
|
-
// Server-global (no -t):
|
|
668
|
-
//
|
|
669
|
-
//
|
|
690
|
+
// Server-global (no -t): primary prefix Ctrl+a (dashboard parity), keep
|
|
691
|
+
// tmux's default Ctrl+b as a secondary prefix so users with existing
|
|
692
|
+
// muscle memory / integrations aren't broken. `q` is the same key under
|
|
693
|
+
// both prefixes (single key table) -> Ctrl+a q AND Ctrl+b q both detach.
|
|
694
|
+
// `send-prefix` / `send-prefix -2` let a double-tap of either prefix
|
|
695
|
+
// reach Claude as that literal key.
|
|
670
696
|
";",
|
|
671
697
|
"set",
|
|
672
698
|
"-g",
|
|
@@ -676,9 +702,6 @@ function buildClaudeStatusBarArgs(sessionName, boxName) {
|
|
|
676
702
|
"set",
|
|
677
703
|
"-g",
|
|
678
704
|
"prefix2",
|
|
679
|
-
"None",
|
|
680
|
-
";",
|
|
681
|
-
"unbind-key",
|
|
682
705
|
"C-b",
|
|
683
706
|
";",
|
|
684
707
|
"bind-key",
|
|
@@ -686,62 +709,23 @@ function buildClaudeStatusBarArgs(sessionName, boxName) {
|
|
|
686
709
|
"send-prefix",
|
|
687
710
|
";",
|
|
688
711
|
"bind-key",
|
|
712
|
+
"C-b",
|
|
713
|
+
"send-prefix",
|
|
714
|
+
"-2",
|
|
715
|
+
";",
|
|
716
|
+
"bind-key",
|
|
689
717
|
"q",
|
|
690
718
|
"detach-client",
|
|
719
|
+
// Hide the inner tmux status bar — the wrapped-pty footer (for
|
|
720
|
+
// `agentbox claude` / `agentbox shell`) and the dashboard's own status
|
|
721
|
+
// row already show the box name + detach hint; without `status off`
|
|
722
|
+
// they double up.
|
|
691
723
|
";",
|
|
692
724
|
"set",
|
|
693
725
|
"-t",
|
|
694
726
|
s,
|
|
695
|
-
"status
|
|
696
|
-
"
|
|
697
|
-
";",
|
|
698
|
-
"set",
|
|
699
|
-
"-t",
|
|
700
|
-
s,
|
|
701
|
-
"status-justify",
|
|
702
|
-
"left",
|
|
703
|
-
";",
|
|
704
|
-
"set",
|
|
705
|
-
"-t",
|
|
706
|
-
s,
|
|
707
|
-
"status-style",
|
|
708
|
-
"bg=colour236,fg=colour250",
|
|
709
|
-
";",
|
|
710
|
-
"set",
|
|
711
|
-
"-t",
|
|
712
|
-
s,
|
|
713
|
-
"status-left-length",
|
|
714
|
-
"60",
|
|
715
|
-
";",
|
|
716
|
-
"set",
|
|
717
|
-
"-t",
|
|
718
|
-
s,
|
|
719
|
-
"status-left",
|
|
720
|
-
`#[fg=colour16,bg=colour39,bold] agentbox \u25B8 ${name} #[default] `,
|
|
721
|
-
";",
|
|
722
|
-
"set",
|
|
723
|
-
"-t",
|
|
724
|
-
s,
|
|
725
|
-
"status-right-length",
|
|
726
|
-
"30",
|
|
727
|
-
";",
|
|
728
|
-
"set",
|
|
729
|
-
"-t",
|
|
730
|
-
s,
|
|
731
|
-
"status-right",
|
|
732
|
-
"#[fg=colour255]Control+a q#[fg=colour245]: detach ",
|
|
733
|
-
";",
|
|
734
|
-
"set",
|
|
735
|
-
"-t",
|
|
736
|
-
s,
|
|
737
|
-
"window-status-format",
|
|
738
|
-
"",
|
|
739
|
-
";",
|
|
740
|
-
"set",
|
|
741
|
-
"-t",
|
|
742
|
-
s,
|
|
743
|
-
"window-status-current-format",
|
|
744
|
-
""
|
|
727
|
+
"status",
|
|
728
|
+
"off"
|
|
745
729
|
];
|
|
746
730
|
}
|
|
747
731
|
function buildShellArgv(container) {
|
|
@@ -751,16 +735,6 @@ function buildShellArgv(container) {
|
|
|
751
735
|
function formatDetachNotice(ref) {
|
|
752
736
|
return `Session detached. Reattach with: agentbox claude attach ${ref}`;
|
|
753
737
|
}
|
|
754
|
-
function attachClaudeSession(container, sessionName, reattachRef) {
|
|
755
|
-
const child = spawnSync("docker", buildClaudeAttachArgv(container, sessionName), {
|
|
756
|
-
stdio: "inherit"
|
|
757
|
-
});
|
|
758
|
-
const code = child.status ?? 0;
|
|
759
|
-
if (reattachRef && code === 0) {
|
|
760
|
-
process.stdout.write("\x1B[1A\x1B[2K\r" + formatDetachNotice(reattachRef) + "\n");
|
|
761
|
-
}
|
|
762
|
-
process.exit(code);
|
|
763
|
-
}
|
|
764
738
|
async function claudeSessionInfo(container, sessionName) {
|
|
765
739
|
const name = sessionName ?? DEFAULT_CLAUDE_SESSION;
|
|
766
740
|
const has = await execa(
|
|
@@ -1053,211 +1027,281 @@ async function isGitDir(path) {
|
|
|
1053
1027
|
return false;
|
|
1054
1028
|
}
|
|
1055
1029
|
}
|
|
1056
|
-
async function
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
);
|
|
1068
|
-
const untrackedList = untracked.exitCode === 0 && untracked.stdout.length > 0 ? untracked.stdout.split("\0").filter((s) => s.length > 0) : [];
|
|
1069
|
-
const branchName = await pickFreshBranch(args.hostMainRepo, args.branchName);
|
|
1070
|
-
const wadd = await execa2(
|
|
1030
|
+
async function pickFreshBranch(hostMainRepo, base) {
|
|
1031
|
+
let candidate = base;
|
|
1032
|
+
let suffix = 2;
|
|
1033
|
+
while (await branchExists(hostMainRepo, candidate)) {
|
|
1034
|
+
candidate = `${base}-${String(suffix++)}`;
|
|
1035
|
+
if (suffix > 100) throw new GitWorktreeError(`could not find a free branch name near ${base}`);
|
|
1036
|
+
}
|
|
1037
|
+
return candidate;
|
|
1038
|
+
}
|
|
1039
|
+
async function branchExists(hostMainRepo, name) {
|
|
1040
|
+
const result = await execa2(
|
|
1071
1041
|
"git",
|
|
1072
|
-
["-C",
|
|
1042
|
+
["-C", hostMainRepo, "show-ref", "--verify", "--quiet", `refs/heads/${name}`],
|
|
1073
1043
|
{ reject: false }
|
|
1074
1044
|
);
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1045
|
+
return result.exitCode === 0;
|
|
1046
|
+
}
|
|
1047
|
+
var GitWorktreeError = class extends Error {
|
|
1048
|
+
constructor(message) {
|
|
1049
|
+
super(message);
|
|
1050
|
+
this.name = "GitWorktreeError";
|
|
1079
1051
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1052
|
+
};
|
|
1053
|
+
var WORKTREE_ROOT = "/home/vscode/.agentbox-worktrees";
|
|
1054
|
+
function fsSafeBranch(branch) {
|
|
1055
|
+
return branch.replace(/[^A-Za-z0-9._-]+/g, "_");
|
|
1056
|
+
}
|
|
1057
|
+
function gitWorktreePathFor(branch) {
|
|
1058
|
+
return `${WORKTREE_ROOT}/${fsSafeBranch(branch)}`;
|
|
1059
|
+
}
|
|
1060
|
+
async function collectRepoCarryOver(repo, branch, containerPath, gitWorktreePath) {
|
|
1061
|
+
const stash = await execa3("git", ["-C", repo.hostMainRepo, "stash", "create"], { reject: false });
|
|
1062
|
+
const stashSha = stash.exitCode === 0 ? stash.stdout.trim() || null : null;
|
|
1063
|
+
const untracked = await execa3(
|
|
1082
1064
|
"git",
|
|
1083
|
-
["-C",
|
|
1065
|
+
["-C", repo.hostMainRepo, "ls-files", "--others", "--exclude-standard", "-z"],
|
|
1084
1066
|
{ reject: false }
|
|
1085
1067
|
);
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1068
|
+
const untrackedNul = untracked.exitCode === 0 ? untracked.stdout : "";
|
|
1069
|
+
return {
|
|
1070
|
+
repo,
|
|
1071
|
+
containerPath,
|
|
1072
|
+
gitWorktreePath,
|
|
1073
|
+
branch,
|
|
1074
|
+
stashSha,
|
|
1075
|
+
untrackedNul,
|
|
1076
|
+
hostSource: repo.hostMainRepo
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
async function dexec(container, argv, user = "vscode", cwd = "/") {
|
|
1080
|
+
const r = await execa3(
|
|
1081
|
+
"docker",
|
|
1082
|
+
["exec", "-w", cwd, "--user", user, container, ...argv],
|
|
1089
1083
|
{ reject: false }
|
|
1090
1084
|
);
|
|
1091
|
-
if (
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1085
|
+
if (r.exitCode !== 0) {
|
|
1086
|
+
throw new GitWorktreeError(`${argv.join(" ")} failed: ${r.stderr || r.stdout}`);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
async function chownGitBindParents(args) {
|
|
1090
|
+
const log = args.onLog ?? (() => {
|
|
1091
|
+
});
|
|
1092
|
+
const repos = Array.from(new Set(args.hostMainRepos));
|
|
1093
|
+
for (const repo of repos) {
|
|
1094
|
+
const result = await execInBox(args.container, ["chown", "vscode:vscode", repo], {
|
|
1095
|
+
user: "root"
|
|
1096
|
+
});
|
|
1097
|
+
if (result.exitCode === 0) {
|
|
1098
|
+
log(`chowned ${repo} to vscode:vscode (parent of bind-mounted .git)`);
|
|
1099
|
+
} else {
|
|
1100
|
+
const msg = (result.stderr || result.stdout || `exit ${result.exitCode}`).trim();
|
|
1101
|
+
log(`chown ${repo} failed (best-effort, ignoring): ${msg}`);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
async function bindWorktrees(container, binds, onLog) {
|
|
1106
|
+
const log = onLog ?? (() => {
|
|
1107
|
+
});
|
|
1108
|
+
const ordered = [...binds].sort(
|
|
1109
|
+
(a, b) => a.kind === "root" && b.kind !== "root" ? -1 : a.kind !== "root" && b.kind === "root" ? 1 : 0
|
|
1110
|
+
);
|
|
1111
|
+
for (const b of ordered) {
|
|
1112
|
+
await execa3(
|
|
1113
|
+
"docker",
|
|
1114
|
+
["exec", "-w", "/", "--user", "root", container, "sh", "-c", `mountpoint -q ${b.containerPath} && umount ${b.containerPath} || true`],
|
|
1115
|
+
{ reject: false }
|
|
1116
|
+
);
|
|
1117
|
+
if (b.kind === "nested") {
|
|
1118
|
+
await dexec(container, ["mkdir", "-p", ctParent(b.containerPath)], "root");
|
|
1119
|
+
await dexec(container, ["mkdir", "-p", b.containerPath], "root");
|
|
1120
|
+
}
|
|
1121
|
+
await dexec(container, ["mount", "--bind", b.gitWorktreePath, b.containerPath], "root");
|
|
1122
|
+
log(`bind-mounted ${b.containerPath} <- ${b.gitWorktreePath}`);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
async function seedWorkspace(opts) {
|
|
1126
|
+
const log = opts.onLog ?? (() => {
|
|
1127
|
+
});
|
|
1128
|
+
await dexec(opts.container, ["mkdir", "-p", WORKTREE_ROOT]);
|
|
1129
|
+
for (const r of opts.repos) {
|
|
1130
|
+
const main = r.repo.hostMainRepo;
|
|
1131
|
+
const wt = r.gitWorktreePath;
|
|
1132
|
+
const add = await execa3(
|
|
1133
|
+
"docker",
|
|
1134
|
+
[
|
|
1135
|
+
"exec",
|
|
1136
|
+
"--user",
|
|
1137
|
+
"vscode",
|
|
1138
|
+
opts.container,
|
|
1139
|
+
"git",
|
|
1140
|
+
"-C",
|
|
1141
|
+
main,
|
|
1142
|
+
"worktree",
|
|
1143
|
+
"add",
|
|
1144
|
+
"-b",
|
|
1145
|
+
r.branch,
|
|
1146
|
+
wt,
|
|
1147
|
+
"HEAD"
|
|
1148
|
+
],
|
|
1149
|
+
{ reject: false }
|
|
1150
|
+
);
|
|
1151
|
+
if (add.exitCode !== 0) {
|
|
1152
|
+
throw new GitWorktreeError(
|
|
1153
|
+
`git worktree add ${wt} (branch ${r.branch}) failed: ${add.stderr || add.stdout}`
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
log(`worktree ${wt} on branch ${r.branch} (host main ${main})`);
|
|
1157
|
+
await execa3(
|
|
1158
|
+
"docker",
|
|
1159
|
+
[
|
|
1160
|
+
"exec",
|
|
1161
|
+
"--user",
|
|
1162
|
+
"vscode",
|
|
1163
|
+
opts.container,
|
|
1164
|
+
"git",
|
|
1165
|
+
"-C",
|
|
1166
|
+
main,
|
|
1167
|
+
"config",
|
|
1168
|
+
"extensions.worktreeConfig",
|
|
1169
|
+
"true"
|
|
1170
|
+
],
|
|
1095
1171
|
{ reject: false }
|
|
1096
1172
|
);
|
|
1097
|
-
|
|
1098
|
-
|
|
1173
|
+
await execa3(
|
|
1174
|
+
"docker",
|
|
1175
|
+
[
|
|
1176
|
+
"exec",
|
|
1177
|
+
"--user",
|
|
1178
|
+
"vscode",
|
|
1179
|
+
opts.container,
|
|
1099
1180
|
"git",
|
|
1100
|
-
|
|
1181
|
+
"-C",
|
|
1182
|
+
wt,
|
|
1183
|
+
"config",
|
|
1184
|
+
"--worktree",
|
|
1185
|
+
"commit.gpgsign",
|
|
1186
|
+
"false"
|
|
1187
|
+
],
|
|
1188
|
+
{ reject: false }
|
|
1189
|
+
);
|
|
1190
|
+
}
|
|
1191
|
+
await bindWorktrees(
|
|
1192
|
+
opts.container,
|
|
1193
|
+
opts.repos.map((r) => ({
|
|
1194
|
+
kind: r.repo.kind,
|
|
1195
|
+
containerPath: r.containerPath,
|
|
1196
|
+
gitWorktreePath: r.gitWorktreePath
|
|
1197
|
+
})),
|
|
1198
|
+
log
|
|
1199
|
+
);
|
|
1200
|
+
for (const r of opts.repos) {
|
|
1201
|
+
const ct = r.containerPath;
|
|
1202
|
+
if (r.stashSha) {
|
|
1203
|
+
const withIndex = await execa3(
|
|
1204
|
+
"docker",
|
|
1205
|
+
[
|
|
1206
|
+
"exec",
|
|
1207
|
+
"--user",
|
|
1208
|
+
"vscode",
|
|
1209
|
+
opts.container,
|
|
1210
|
+
"git",
|
|
1211
|
+
"-C",
|
|
1212
|
+
ct,
|
|
1213
|
+
"stash",
|
|
1214
|
+
"apply",
|
|
1215
|
+
"--index",
|
|
1216
|
+
r.stashSha
|
|
1217
|
+
],
|
|
1101
1218
|
{ reject: false }
|
|
1102
1219
|
);
|
|
1103
|
-
if (
|
|
1104
|
-
|
|
1105
|
-
|
|
1220
|
+
if (withIndex.exitCode !== 0) {
|
|
1221
|
+
const noIndex = await execa3(
|
|
1222
|
+
"docker",
|
|
1223
|
+
[
|
|
1224
|
+
"exec",
|
|
1225
|
+
"--user",
|
|
1226
|
+
"vscode",
|
|
1227
|
+
opts.container,
|
|
1228
|
+
"git",
|
|
1229
|
+
"-C",
|
|
1230
|
+
ct,
|
|
1231
|
+
"stash",
|
|
1232
|
+
"apply",
|
|
1233
|
+
r.stashSha
|
|
1234
|
+
],
|
|
1235
|
+
{ reject: false }
|
|
1106
1236
|
);
|
|
1237
|
+
if (noIndex.exitCode !== 0) {
|
|
1238
|
+
log(
|
|
1239
|
+
`warning: stash apply failed in ${ct} (${withIndex.stderr || withIndex.stdout || "no message"})`
|
|
1240
|
+
);
|
|
1241
|
+
} else {
|
|
1242
|
+
log(`applied tracked changes (without index \u2014 staged state lost) in ${ct}`);
|
|
1243
|
+
}
|
|
1107
1244
|
} else {
|
|
1108
|
-
log(`applied tracked changes
|
|
1245
|
+
log(`applied tracked changes from host main into ${ct}`);
|
|
1109
1246
|
}
|
|
1110
|
-
} else {
|
|
1111
|
-
log(`applied tracked changes from host main`);
|
|
1112
1247
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
"tar",
|
|
1117
|
-
["-C", args.hostMainRepo, "--null", "-T", "-", "-cf", "-"],
|
|
1118
|
-
{
|
|
1119
|
-
input: untrackedList.join("\0"),
|
|
1248
|
+
if (r.untrackedNul.length > 0) {
|
|
1249
|
+
const tarOut = await execa3("tar", ["-C", r.hostSource, "--null", "-T", "-", "-cf", "-"], {
|
|
1250
|
+
input: r.untrackedNul.replace(/\0$/, ""),
|
|
1120
1251
|
encoding: "buffer",
|
|
1121
1252
|
reject: false
|
|
1122
|
-
}
|
|
1123
|
-
);
|
|
1124
|
-
if (tarOut.exitCode === 0) {
|
|
1125
|
-
const tarIn = await execa2("tar", ["-C", args.worktreeDir, "-xf", "-"], {
|
|
1126
|
-
input: tarOut.stdout,
|
|
1127
|
-
reject: false
|
|
1128
1253
|
});
|
|
1254
|
+
if (tarOut.exitCode !== 0) {
|
|
1255
|
+
log(`warning: tar of untracked files for ${r.repo.hostMainRepo} failed: ${tarOut.stderr}`);
|
|
1256
|
+
continue;
|
|
1257
|
+
}
|
|
1258
|
+
const tarIn = await execa3(
|
|
1259
|
+
"docker",
|
|
1260
|
+
["exec", "-i", "--user", "vscode", opts.container, "tar", "-C", ct, "-xf", "-"],
|
|
1261
|
+
{ input: tarOut.stdout, reject: false }
|
|
1262
|
+
);
|
|
1129
1263
|
if (tarIn.exitCode !== 0) {
|
|
1130
|
-
log(`warning: untracked-file copy into
|
|
1264
|
+
log(`warning: untracked-file copy into ${ct} failed: ${tarIn.stderr}`);
|
|
1131
1265
|
} else {
|
|
1132
|
-
|
|
1266
|
+
const count = r.untrackedNul.split("\0").filter((s) => s.length > 0).length;
|
|
1267
|
+
log(`copied ${String(count)} untracked file(s) into ${ct}`);
|
|
1133
1268
|
}
|
|
1134
|
-
} else {
|
|
1135
|
-
log(`warning: tar of untracked files failed: ${tarOut.stderr}`);
|
|
1136
1269
|
}
|
|
1137
1270
|
}
|
|
1138
|
-
return { branchName, stashSha, untrackedCount: untrackedList.length };
|
|
1139
1271
|
}
|
|
1140
|
-
async function
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1272
|
+
async function seedWorkspaceFromDir(opts) {
|
|
1273
|
+
const log = opts.onLog ?? (() => {
|
|
1274
|
+
});
|
|
1275
|
+
const tarOut = await execa3("tar", ["-C", opts.hostSource, "-cf", "-", "."], {
|
|
1276
|
+
encoding: "buffer",
|
|
1277
|
+
reject: false
|
|
1278
|
+
});
|
|
1279
|
+
if (tarOut.exitCode !== 0) {
|
|
1280
|
+
throw new GitWorktreeError(`tar of ${opts.hostSource} failed: ${tarOut.stderr}`);
|
|
1146
1281
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
"git",
|
|
1152
|
-
["-C", hostMainRepo, "show-ref", "--verify", "--quiet", `refs/heads/${name}`],
|
|
1153
|
-
{ reject: false }
|
|
1282
|
+
const tarIn = await execa3(
|
|
1283
|
+
"docker",
|
|
1284
|
+
["exec", "-i", "--user", "1000:1000", opts.container, "tar", "-C", "/workspace", "-xf", "-"],
|
|
1285
|
+
{ input: tarOut.stdout, reject: false }
|
|
1154
1286
|
);
|
|
1155
|
-
|
|
1287
|
+
if (tarIn.exitCode !== 0) {
|
|
1288
|
+
throw new GitWorktreeError(`tar extract into /workspace failed: ${tarIn.stderr}`);
|
|
1289
|
+
}
|
|
1290
|
+
log(`seeded /workspace from ${opts.hostSource}`);
|
|
1156
1291
|
}
|
|
1157
|
-
async function
|
|
1158
|
-
const remove = await
|
|
1292
|
+
async function removeInBoxWorktree(args) {
|
|
1293
|
+
const remove = await execa3(
|
|
1159
1294
|
"git",
|
|
1160
|
-
["-C", args.hostMainRepo, "worktree", "remove", "--force", args.
|
|
1295
|
+
["-C", args.hostMainRepo, "worktree", "remove", "--force", args.gitWorktreePath],
|
|
1161
1296
|
{ reject: false }
|
|
1162
1297
|
);
|
|
1163
1298
|
if (remove.exitCode === 0) return;
|
|
1164
|
-
await
|
|
1165
|
-
await execa2("git", ["-C", args.hostMainRepo, "worktree", "prune"], { reject: false });
|
|
1166
|
-
}
|
|
1167
|
-
var GitWorktreeError = class extends Error {
|
|
1168
|
-
constructor(message) {
|
|
1169
|
-
super(message);
|
|
1170
|
-
this.name = "GitWorktreeError";
|
|
1171
|
-
}
|
|
1172
|
-
};
|
|
1173
|
-
var DEFAULT_LOWER_DIRS = ["/host-src"];
|
|
1174
|
-
var BOX_USER_UID = 1e3;
|
|
1175
|
-
var BOX_USER_GID = 1e3;
|
|
1176
|
-
async function mountOverlay(container, opts = {}) {
|
|
1177
|
-
const lowerDirs = opts.lowerDirs && opts.lowerDirs.length > 0 ? opts.lowerDirs : ["/host-src"];
|
|
1178
|
-
const mountOpts = [
|
|
1179
|
-
`lowerdir=${lowerDirs.join(":")}`,
|
|
1180
|
-
"upperdir=/upper/upper",
|
|
1181
|
-
"workdir=/upper/work",
|
|
1182
|
-
`squash_to_uid=${String(BOX_USER_UID)}`,
|
|
1183
|
-
`squash_to_gid=${String(BOX_USER_GID)}`
|
|
1184
|
-
].join(",");
|
|
1185
|
-
const lines = [
|
|
1186
|
-
"set -euo pipefail",
|
|
1187
|
-
"mkdir -p /upper/upper /upper/work /workspace",
|
|
1188
|
-
// Idempotent — if a previous attempt left a stale overlay, unmount first.
|
|
1189
|
-
"mountpoint -q /workspace && fusermount3 -u /workspace || true",
|
|
1190
|
-
`fuse-overlayfs -o ${mountOpts} /workspace`,
|
|
1191
|
-
"mountpoint -q /workspace"
|
|
1192
|
-
];
|
|
1193
|
-
for (const w of opts.nestedWorktrees ?? []) {
|
|
1194
|
-
lines.push(
|
|
1195
|
-
`mkdir -p ${shellQuote(w.containerPath)}`,
|
|
1196
|
-
`mountpoint -q ${shellQuote(w.containerPath)} && umount ${shellQuote(w.containerPath)} || true`,
|
|
1197
|
-
`mount --bind ${shellQuote(w.mountFromPath)} ${shellQuote(w.containerPath)}`,
|
|
1198
|
-
`mountpoint -q ${shellQuote(w.containerPath)}`
|
|
1199
|
-
);
|
|
1200
|
-
}
|
|
1201
|
-
const result = await execInBox(container, ["bash", "-lc", lines.join("\n")], { user: "root" });
|
|
1202
|
-
if (result.exitCode !== 0) {
|
|
1203
|
-
throw new OverlayError(
|
|
1204
|
-
`failed to mount FUSE overlay in ${container}`,
|
|
1205
|
-
result.stdout,
|
|
1206
|
-
result.stderr
|
|
1207
|
-
);
|
|
1208
|
-
}
|
|
1209
|
-
return { upperWritePath: "/upper/upper" };
|
|
1299
|
+
await execa3("git", ["-C", args.hostMainRepo, "worktree", "prune"], { reject: false });
|
|
1210
1300
|
}
|
|
1211
|
-
function
|
|
1212
|
-
|
|
1301
|
+
function ctParent(p) {
|
|
1302
|
+
const i = p.lastIndexOf("/");
|
|
1303
|
+
return i <= 0 ? "/" : p.slice(0, i);
|
|
1213
1304
|
}
|
|
1214
|
-
async function verifyOverlay(container, lowerDirs = DEFAULT_LOWER_DIRS) {
|
|
1215
|
-
const sentinel = ".agentbox-overlay-check";
|
|
1216
|
-
const checks = [];
|
|
1217
|
-
const ls = await execInBox(container, ["bash", "-lc", `ls -A /workspace | head -1`], {
|
|
1218
|
-
user: "root"
|
|
1219
|
-
});
|
|
1220
|
-
checks.push({
|
|
1221
|
-
name: "workspace lists lower contents",
|
|
1222
|
-
ok: ls.exitCode === 0,
|
|
1223
|
-
detail: ls.exitCode === 0 ? `first entry: ${ls.stdout.trim() || "(empty)"}` : ls.stderr.trim()
|
|
1224
|
-
});
|
|
1225
|
-
const write = await execInBox(container, ["bash", "-lc", `touch /workspace/${sentinel}`], {
|
|
1226
|
-
user: "root"
|
|
1227
|
-
});
|
|
1228
|
-
checks.push({
|
|
1229
|
-
name: "write through overlay succeeds",
|
|
1230
|
-
ok: write.exitCode === 0,
|
|
1231
|
-
detail: write.exitCode === 0 ? `created /workspace/${sentinel}` : write.stderr.trim()
|
|
1232
|
-
});
|
|
1233
|
-
const upper = await execInBox(container, ["bash", "-lc", `test -f /upper/upper/${sentinel}`], {
|
|
1234
|
-
user: "root"
|
|
1235
|
-
});
|
|
1236
|
-
checks.push({
|
|
1237
|
-
name: "write lands in /upper (cow target)",
|
|
1238
|
-
ok: upper.exitCode === 0,
|
|
1239
|
-
detail: upper.exitCode === 0 ? `/upper/upper/${sentinel} exists` : `expected /upper/upper/${sentinel} to exist`
|
|
1240
|
-
});
|
|
1241
|
-
const lowerProbe = lowerDirs.map((d) => `test ! -e ${shellQuote(d)}/${sentinel}`).join(" && ");
|
|
1242
|
-
const lower = await execInBox(container, ["bash", "-lc", lowerProbe], { user: "root" });
|
|
1243
|
-
checks.push({
|
|
1244
|
-
name: `lower untouched (${lowerDirs.join(", ")})`,
|
|
1245
|
-
ok: lower.exitCode === 0,
|
|
1246
|
-
detail: lower.exitCode === 0 ? `${sentinel} absent from every lower` : `${sentinel} leaked into a lower layer`
|
|
1247
|
-
});
|
|
1248
|
-
await execInBox(container, ["bash", "-lc", `rm -f /workspace/${sentinel}`], { user: "root" });
|
|
1249
|
-
return checks;
|
|
1250
|
-
}
|
|
1251
|
-
var OverlayError = class extends Error {
|
|
1252
|
-
constructor(message, stdout, stderr) {
|
|
1253
|
-
super(message);
|
|
1254
|
-
this.stdout = stdout;
|
|
1255
|
-
this.stderr = stderr;
|
|
1256
|
-
this.name = "OverlayError";
|
|
1257
|
-
}
|
|
1258
|
-
stdout;
|
|
1259
|
-
stderr;
|
|
1260
|
-
};
|
|
1261
1305
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
1262
1306
|
"node_modules",
|
|
1263
1307
|
".next",
|
|
@@ -1274,8 +1318,11 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
|
1274
1318
|
".parcel-cache"
|
|
1275
1319
|
]);
|
|
1276
1320
|
var SNAPSHOTS_ROOT = join3(homedir2(), ".agentbox", "snapshots");
|
|
1277
|
-
function snapshotPathFor(
|
|
1278
|
-
|
|
1321
|
+
function snapshotPathFor(box) {
|
|
1322
|
+
const mnemonic = sanitizeMnemonic(box.name);
|
|
1323
|
+
const n = box.projectIndex;
|
|
1324
|
+
const segment = typeof n === "number" && Number.isFinite(n) && n > 0 ? `${box.id}-${String(n)}-${mnemonic}` : `${box.id}-${mnemonic}`;
|
|
1325
|
+
return join3(SNAPSHOTS_ROOT, segment);
|
|
1279
1326
|
}
|
|
1280
1327
|
async function findExcludedDirs(root, excluded = EXCLUDE_DIRS) {
|
|
1281
1328
|
const matches = [];
|
|
@@ -1305,7 +1352,7 @@ async function createSnapshot(opts) {
|
|
|
1305
1352
|
const excluded = opts.excluded ?? EXCLUDE_DIRS;
|
|
1306
1353
|
await mkdir2(SNAPSHOTS_ROOT, { recursive: true });
|
|
1307
1354
|
const cpArgs = platform() === "darwin" ? ["-cR"] : ["-R"];
|
|
1308
|
-
await
|
|
1355
|
+
await execa4("cp", [...cpArgs, `${source}/`, destination]);
|
|
1309
1356
|
const toPrune = await findExcludedDirs(destination, excluded);
|
|
1310
1357
|
await Promise.all(toPrune.map((p) => rm2(p, { recursive: true, force: true })));
|
|
1311
1358
|
return { destination, prunedPaths: toPrune };
|
|
@@ -1466,6 +1513,21 @@ async function stopRelay() {
|
|
|
1466
1513
|
});
|
|
1467
1514
|
return { stopped: true, pid };
|
|
1468
1515
|
}
|
|
1516
|
+
async function getRelayStatus() {
|
|
1517
|
+
const pid = await readPidFile();
|
|
1518
|
+
const pidAlive = pid !== null && await processAlive(pid);
|
|
1519
|
+
const health = await fetchHealthz(300);
|
|
1520
|
+
return {
|
|
1521
|
+
running: health !== null,
|
|
1522
|
+
pid,
|
|
1523
|
+
pidAlive,
|
|
1524
|
+
port: PORT,
|
|
1525
|
+
endpoint: ENDPOINT,
|
|
1526
|
+
health: health === null ? null : { boxes: health.boxes, events: health.events },
|
|
1527
|
+
pidFile: PID_FILE,
|
|
1528
|
+
logFile: LOG_FILE
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1469
1531
|
function pingHealthz(timeoutMs) {
|
|
1470
1532
|
return new Promise((resolveP) => {
|
|
1471
1533
|
const req = httpRequest(
|
|
@@ -1484,6 +1546,42 @@ function pingHealthz(timeoutMs) {
|
|
|
1484
1546
|
req.end();
|
|
1485
1547
|
});
|
|
1486
1548
|
}
|
|
1549
|
+
function fetchHealthz(timeoutMs) {
|
|
1550
|
+
return new Promise((resolveP) => {
|
|
1551
|
+
const req = httpRequest(
|
|
1552
|
+
{ host: "127.0.0.1", port: PORT, method: "GET", path: "/healthz", timeout: timeoutMs },
|
|
1553
|
+
(res) => {
|
|
1554
|
+
const status = res.statusCode ?? 0;
|
|
1555
|
+
if (status < 200 || status >= 300) {
|
|
1556
|
+
res.resume();
|
|
1557
|
+
resolveP(null);
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
const chunks = [];
|
|
1561
|
+
res.on("data", (c) => chunks.push(c));
|
|
1562
|
+
res.on("end", () => {
|
|
1563
|
+
try {
|
|
1564
|
+
const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
1565
|
+
if (typeof parsed.ok === "boolean" && typeof parsed.boxes === "number" && typeof parsed.events === "number") {
|
|
1566
|
+
resolveP({ ok: parsed.ok, boxes: parsed.boxes, events: parsed.events });
|
|
1567
|
+
} else {
|
|
1568
|
+
resolveP(null);
|
|
1569
|
+
}
|
|
1570
|
+
} catch {
|
|
1571
|
+
resolveP(null);
|
|
1572
|
+
}
|
|
1573
|
+
});
|
|
1574
|
+
res.on("error", () => resolveP(null));
|
|
1575
|
+
}
|
|
1576
|
+
);
|
|
1577
|
+
req.on("error", () => resolveP(null));
|
|
1578
|
+
req.on("timeout", () => {
|
|
1579
|
+
req.destroy();
|
|
1580
|
+
resolveP(null);
|
|
1581
|
+
});
|
|
1582
|
+
req.end();
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1487
1585
|
async function readPidFile() {
|
|
1488
1586
|
try {
|
|
1489
1587
|
const text = await readFile2(PID_FILE, "utf8");
|
|
@@ -1507,7 +1605,7 @@ function generateRelayToken() {
|
|
|
1507
1605
|
async function registerBoxWithRelay(args) {
|
|
1508
1606
|
const worktrees = (args.worktrees ?? []).map((w) => ({
|
|
1509
1607
|
containerPath: w.containerPath,
|
|
1510
|
-
|
|
1608
|
+
hostMainRepo: w.hostMainRepo,
|
|
1511
1609
|
branch: w.branch
|
|
1512
1610
|
}));
|
|
1513
1611
|
await adminPost("/admin/register-box", {
|
|
@@ -1516,6 +1614,7 @@ async function registerBoxWithRelay(args) {
|
|
|
1516
1614
|
name: args.name,
|
|
1517
1615
|
containerName: args.containerName,
|
|
1518
1616
|
createdAt: args.createdAt,
|
|
1617
|
+
projectIndex: args.projectIndex,
|
|
1519
1618
|
worktrees
|
|
1520
1619
|
});
|
|
1521
1620
|
}
|
|
@@ -1573,6 +1672,7 @@ async function rehydrateRelayRegistry(boxes) {
|
|
|
1573
1672
|
name: b.name,
|
|
1574
1673
|
containerName: b.container,
|
|
1575
1674
|
createdAt: b.createdAt,
|
|
1675
|
+
projectIndex: b.projectIndex,
|
|
1576
1676
|
worktrees: b.gitWorktrees
|
|
1577
1677
|
});
|
|
1578
1678
|
} catch {
|
|
@@ -1682,10 +1782,10 @@ async function ensureAgentboxTasksFile(container, services, opts = {}) {
|
|
|
1682
1782
|
return { status: "wrote" };
|
|
1683
1783
|
}
|
|
1684
1784
|
async function writeFileInBox(container, path, content) {
|
|
1685
|
-
const { execa:
|
|
1686
|
-
const result = await
|
|
1785
|
+
const { execa: execa5 } = await import("execa");
|
|
1786
|
+
const result = await execa5(
|
|
1687
1787
|
"docker",
|
|
1688
|
-
["exec", "-i", "--user", "vscode", container, "sh", "-c", `cat > ${
|
|
1788
|
+
["exec", "-i", "--user", "vscode", container, "sh", "-c", `cat > ${shellQuote(path)}`],
|
|
1689
1789
|
{ input: content, reject: false }
|
|
1690
1790
|
);
|
|
1691
1791
|
return {
|
|
@@ -1694,7 +1794,7 @@ async function writeFileInBox(container, path, content) {
|
|
|
1694
1794
|
stderr: result.stderr ?? ""
|
|
1695
1795
|
};
|
|
1696
1796
|
}
|
|
1697
|
-
function
|
|
1797
|
+
function shellQuote(s) {
|
|
1698
1798
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
1699
1799
|
}
|
|
1700
1800
|
|
|
@@ -2181,19 +2281,22 @@ async function loadConfig(path) {
|
|
|
2181
2281
|
}
|
|
2182
2282
|
|
|
2183
2283
|
export {
|
|
2284
|
+
DEFAULT_RELAY_PORT,
|
|
2184
2285
|
RELAY_CONTAINER_NAME,
|
|
2185
2286
|
RELAY_NETWORK_NAME,
|
|
2186
2287
|
RELAY_IMAGE_REF,
|
|
2187
2288
|
SHARED_CLAUDE_VOLUME,
|
|
2188
2289
|
resolveClaudeVolume,
|
|
2189
2290
|
ensureClaudeVolume,
|
|
2291
|
+
seedSetupSkillIntoVolume,
|
|
2190
2292
|
buildClaudeMounts,
|
|
2191
2293
|
rebuildPluginNativeDeps,
|
|
2192
2294
|
ClaudeSessionError,
|
|
2193
2295
|
startClaudeSession,
|
|
2296
|
+
buildClaudeAttachArgv,
|
|
2194
2297
|
buildClaudeDashboardAttachArgv,
|
|
2195
2298
|
buildShellArgv,
|
|
2196
|
-
|
|
2299
|
+
formatDetachNotice,
|
|
2197
2300
|
claudeSessionInfo,
|
|
2198
2301
|
pullClaudeExtras,
|
|
2199
2302
|
SHARED_DOCKER_CACHE_VOLUME,
|
|
@@ -2205,17 +2308,21 @@ export {
|
|
|
2205
2308
|
buildVncUrls,
|
|
2206
2309
|
WEB_CONTAINER_PORT,
|
|
2207
2310
|
detectGitRepos,
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2311
|
+
pickFreshBranch,
|
|
2312
|
+
gitWorktreePathFor,
|
|
2313
|
+
collectRepoCarryOver,
|
|
2314
|
+
chownGitBindParents,
|
|
2315
|
+
bindWorktrees,
|
|
2316
|
+
seedWorkspace,
|
|
2317
|
+
seedWorkspaceFromDir,
|
|
2318
|
+
removeInBoxWorktree,
|
|
2213
2319
|
SNAPSHOTS_ROOT,
|
|
2214
2320
|
snapshotPathFor,
|
|
2215
2321
|
createSnapshot,
|
|
2216
2322
|
launchCtlDaemon,
|
|
2217
2323
|
ensureRelay,
|
|
2218
2324
|
stopRelay,
|
|
2325
|
+
getRelayStatus,
|
|
2219
2326
|
generateRelayToken,
|
|
2220
2327
|
registerBoxWithRelay,
|
|
2221
2328
|
forgetBoxFromRelay,
|
|
@@ -2236,4 +2343,4 @@ export {
|
|
|
2236
2343
|
ConfigError,
|
|
2237
2344
|
loadConfig
|
|
2238
2345
|
};
|
|
2239
|
-
//# sourceMappingURL=chunk-
|
|
2346
|
+
//# sourceMappingURL=chunk-PXUBE5KS.js.map
|