@madarco/agentbox 0.4.0 → 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-3NCUES35.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-3JKQNOXP.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-MOC54XL6.js → chunk-PXUBE5KS.js} +376 -245
- 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 +2760 -1857
- package/dist/index.js.map +1 -1
- package/dist/{lifecycle-YTMZYKOE-TD5S5FTS.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 +3 -2
- package/runtime/docker/Dockerfile.box +53 -20
- 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-3JKQNOXP.js.map +0 -1
- package/dist/chunk-3NCUES35.js.map +0 -1
- package/dist/chunk-IDR4HVIC.js.map +0 -1
- package/dist/chunk-J35IH7W5.js.map +0 -1
- package/dist/chunk-MOC54XL6.js.map +0 -1
- package/dist/chunk-SOMIKEN2.js.map +0 -1
- package/dist/create-SE6H4B5U-IWAZHJHV.js +0 -15
- package/dist/stats-GZFLPYTU-DBJ2DVBJ.js +0 -19
- /package/dist/{create-SE6H4B5U-IWAZHJHV.js.map → create-AHZ3GVEZ-TGEDL7UX.js.map} +0 -0
- /package/dist/{lifecycle-YTMZYKOE-TD5S5FTS.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,64 +684,48 @@ 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 [
|
|
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.
|
|
667
696
|
";",
|
|
668
697
|
"set",
|
|
669
|
-
"-
|
|
670
|
-
|
|
671
|
-
"
|
|
672
|
-
"60",
|
|
698
|
+
"-g",
|
|
699
|
+
"prefix",
|
|
700
|
+
"C-a",
|
|
673
701
|
";",
|
|
674
702
|
"set",
|
|
675
|
-
"-
|
|
676
|
-
|
|
677
|
-
"
|
|
678
|
-
"left",
|
|
703
|
+
"-g",
|
|
704
|
+
"prefix2",
|
|
705
|
+
"C-b",
|
|
679
706
|
";",
|
|
680
|
-
"
|
|
681
|
-
"-
|
|
682
|
-
|
|
683
|
-
"status-style",
|
|
684
|
-
"bg=colour236,fg=colour250",
|
|
685
|
-
";",
|
|
686
|
-
"set",
|
|
687
|
-
"-t",
|
|
688
|
-
s,
|
|
689
|
-
"status-left-length",
|
|
690
|
-
"60",
|
|
707
|
+
"bind-key",
|
|
708
|
+
"C-a",
|
|
709
|
+
"send-prefix",
|
|
691
710
|
";",
|
|
692
|
-
"
|
|
693
|
-
"-
|
|
694
|
-
|
|
695
|
-
"
|
|
696
|
-
`#[fg=colour16,bg=colour39,bold] agentbox \u25B8 ${name} #[default] `,
|
|
711
|
+
"bind-key",
|
|
712
|
+
"C-b",
|
|
713
|
+
"send-prefix",
|
|
714
|
+
"-2",
|
|
697
715
|
";",
|
|
698
|
-
"
|
|
699
|
-
"
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
716
|
+
"bind-key",
|
|
717
|
+
"q",
|
|
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.
|
|
703
723
|
";",
|
|
704
724
|
"set",
|
|
705
725
|
"-t",
|
|
706
726
|
s,
|
|
707
|
-
"status
|
|
708
|
-
"
|
|
709
|
-
";",
|
|
710
|
-
"set",
|
|
711
|
-
"-t",
|
|
712
|
-
s,
|
|
713
|
-
"window-status-format",
|
|
714
|
-
"",
|
|
715
|
-
";",
|
|
716
|
-
"set",
|
|
717
|
-
"-t",
|
|
718
|
-
s,
|
|
719
|
-
"window-status-current-format",
|
|
720
|
-
""
|
|
727
|
+
"status",
|
|
728
|
+
"off"
|
|
721
729
|
];
|
|
722
730
|
}
|
|
723
731
|
function buildShellArgv(container) {
|
|
@@ -727,16 +735,6 @@ function buildShellArgv(container) {
|
|
|
727
735
|
function formatDetachNotice(ref) {
|
|
728
736
|
return `Session detached. Reattach with: agentbox claude attach ${ref}`;
|
|
729
737
|
}
|
|
730
|
-
function attachClaudeSession(container, sessionName, reattachRef) {
|
|
731
|
-
const child = spawnSync("docker", buildClaudeAttachArgv(container, sessionName), {
|
|
732
|
-
stdio: "inherit"
|
|
733
|
-
});
|
|
734
|
-
const code = child.status ?? 0;
|
|
735
|
-
if (reattachRef && code === 0) {
|
|
736
|
-
process.stdout.write("\x1B[1A\x1B[2K\r" + formatDetachNotice(reattachRef) + "\n");
|
|
737
|
-
}
|
|
738
|
-
process.exit(code);
|
|
739
|
-
}
|
|
740
738
|
async function claudeSessionInfo(container, sessionName) {
|
|
741
739
|
const name = sessionName ?? DEFAULT_CLAUDE_SESSION;
|
|
742
740
|
const has = await execa(
|
|
@@ -1029,211 +1027,281 @@ async function isGitDir(path) {
|
|
|
1029
1027
|
return false;
|
|
1030
1028
|
}
|
|
1031
1029
|
}
|
|
1032
|
-
async function
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
);
|
|
1044
|
-
const untrackedList = untracked.exitCode === 0 && untracked.stdout.length > 0 ? untracked.stdout.split("\0").filter((s) => s.length > 0) : [];
|
|
1045
|
-
const branchName = await pickFreshBranch(args.hostMainRepo, args.branchName);
|
|
1046
|
-
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(
|
|
1047
1041
|
"git",
|
|
1048
|
-
["-C",
|
|
1042
|
+
["-C", hostMainRepo, "show-ref", "--verify", "--quiet", `refs/heads/${name}`],
|
|
1049
1043
|
{ reject: false }
|
|
1050
1044
|
);
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1045
|
+
return result.exitCode === 0;
|
|
1046
|
+
}
|
|
1047
|
+
var GitWorktreeError = class extends Error {
|
|
1048
|
+
constructor(message) {
|
|
1049
|
+
super(message);
|
|
1050
|
+
this.name = "GitWorktreeError";
|
|
1055
1051
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
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(
|
|
1058
1064
|
"git",
|
|
1059
|
-
["-C",
|
|
1065
|
+
["-C", repo.hostMainRepo, "ls-files", "--others", "--exclude-standard", "-z"],
|
|
1060
1066
|
{ reject: false }
|
|
1061
1067
|
);
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
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],
|
|
1065
1083
|
{ reject: false }
|
|
1066
1084
|
);
|
|
1067
|
-
if (
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
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
|
+
],
|
|
1071
1171
|
{ reject: false }
|
|
1072
1172
|
);
|
|
1073
|
-
|
|
1074
|
-
|
|
1173
|
+
await execa3(
|
|
1174
|
+
"docker",
|
|
1175
|
+
[
|
|
1176
|
+
"exec",
|
|
1177
|
+
"--user",
|
|
1178
|
+
"vscode",
|
|
1179
|
+
opts.container,
|
|
1075
1180
|
"git",
|
|
1076
|
-
|
|
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
|
+
],
|
|
1077
1218
|
{ reject: false }
|
|
1078
1219
|
);
|
|
1079
|
-
if (
|
|
1080
|
-
|
|
1081
|
-
|
|
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 }
|
|
1082
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
|
+
}
|
|
1083
1244
|
} else {
|
|
1084
|
-
log(`applied tracked changes
|
|
1245
|
+
log(`applied tracked changes from host main into ${ct}`);
|
|
1085
1246
|
}
|
|
1086
|
-
} else {
|
|
1087
|
-
log(`applied tracked changes from host main`);
|
|
1088
1247
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
"tar",
|
|
1093
|
-
["-C", args.hostMainRepo, "--null", "-T", "-", "-cf", "-"],
|
|
1094
|
-
{
|
|
1095
|
-
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$/, ""),
|
|
1096
1251
|
encoding: "buffer",
|
|
1097
1252
|
reject: false
|
|
1098
|
-
}
|
|
1099
|
-
);
|
|
1100
|
-
if (tarOut.exitCode === 0) {
|
|
1101
|
-
const tarIn = await execa2("tar", ["-C", args.worktreeDir, "-xf", "-"], {
|
|
1102
|
-
input: tarOut.stdout,
|
|
1103
|
-
reject: false
|
|
1104
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
|
+
);
|
|
1105
1263
|
if (tarIn.exitCode !== 0) {
|
|
1106
|
-
log(`warning: untracked-file copy into
|
|
1264
|
+
log(`warning: untracked-file copy into ${ct} failed: ${tarIn.stderr}`);
|
|
1107
1265
|
} else {
|
|
1108
|
-
|
|
1266
|
+
const count = r.untrackedNul.split("\0").filter((s) => s.length > 0).length;
|
|
1267
|
+
log(`copied ${String(count)} untracked file(s) into ${ct}`);
|
|
1109
1268
|
}
|
|
1110
|
-
} else {
|
|
1111
|
-
log(`warning: tar of untracked files failed: ${tarOut.stderr}`);
|
|
1112
1269
|
}
|
|
1113
1270
|
}
|
|
1114
|
-
return { branchName, stashSha, untrackedCount: untrackedList.length };
|
|
1115
1271
|
}
|
|
1116
|
-
async function
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
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}`);
|
|
1122
1281
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
"git",
|
|
1128
|
-
["-C", hostMainRepo, "show-ref", "--verify", "--quiet", `refs/heads/${name}`],
|
|
1129
|
-
{ 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 }
|
|
1130
1286
|
);
|
|
1131
|
-
|
|
1287
|
+
if (tarIn.exitCode !== 0) {
|
|
1288
|
+
throw new GitWorktreeError(`tar extract into /workspace failed: ${tarIn.stderr}`);
|
|
1289
|
+
}
|
|
1290
|
+
log(`seeded /workspace from ${opts.hostSource}`);
|
|
1132
1291
|
}
|
|
1133
|
-
async function
|
|
1134
|
-
const remove = await
|
|
1292
|
+
async function removeInBoxWorktree(args) {
|
|
1293
|
+
const remove = await execa3(
|
|
1135
1294
|
"git",
|
|
1136
|
-
["-C", args.hostMainRepo, "worktree", "remove", "--force", args.
|
|
1295
|
+
["-C", args.hostMainRepo, "worktree", "remove", "--force", args.gitWorktreePath],
|
|
1137
1296
|
{ reject: false }
|
|
1138
1297
|
);
|
|
1139
1298
|
if (remove.exitCode === 0) return;
|
|
1140
|
-
await
|
|
1141
|
-
await execa2("git", ["-C", args.hostMainRepo, "worktree", "prune"], { reject: false });
|
|
1142
|
-
}
|
|
1143
|
-
var GitWorktreeError = class extends Error {
|
|
1144
|
-
constructor(message) {
|
|
1145
|
-
super(message);
|
|
1146
|
-
this.name = "GitWorktreeError";
|
|
1147
|
-
}
|
|
1148
|
-
};
|
|
1149
|
-
var DEFAULT_LOWER_DIRS = ["/host-src"];
|
|
1150
|
-
var BOX_USER_UID = 1e3;
|
|
1151
|
-
var BOX_USER_GID = 1e3;
|
|
1152
|
-
async function mountOverlay(container, opts = {}) {
|
|
1153
|
-
const lowerDirs = opts.lowerDirs && opts.lowerDirs.length > 0 ? opts.lowerDirs : ["/host-src"];
|
|
1154
|
-
const mountOpts = [
|
|
1155
|
-
`lowerdir=${lowerDirs.join(":")}`,
|
|
1156
|
-
"upperdir=/upper/upper",
|
|
1157
|
-
"workdir=/upper/work",
|
|
1158
|
-
`squash_to_uid=${String(BOX_USER_UID)}`,
|
|
1159
|
-
`squash_to_gid=${String(BOX_USER_GID)}`
|
|
1160
|
-
].join(",");
|
|
1161
|
-
const lines = [
|
|
1162
|
-
"set -euo pipefail",
|
|
1163
|
-
"mkdir -p /upper/upper /upper/work /workspace",
|
|
1164
|
-
// Idempotent — if a previous attempt left a stale overlay, unmount first.
|
|
1165
|
-
"mountpoint -q /workspace && fusermount3 -u /workspace || true",
|
|
1166
|
-
`fuse-overlayfs -o ${mountOpts} /workspace`,
|
|
1167
|
-
"mountpoint -q /workspace"
|
|
1168
|
-
];
|
|
1169
|
-
for (const w of opts.nestedWorktrees ?? []) {
|
|
1170
|
-
lines.push(
|
|
1171
|
-
`mkdir -p ${shellQuote(w.containerPath)}`,
|
|
1172
|
-
`mountpoint -q ${shellQuote(w.containerPath)} && umount ${shellQuote(w.containerPath)} || true`,
|
|
1173
|
-
`mount --bind ${shellQuote(w.mountFromPath)} ${shellQuote(w.containerPath)}`,
|
|
1174
|
-
`mountpoint -q ${shellQuote(w.containerPath)}`
|
|
1175
|
-
);
|
|
1176
|
-
}
|
|
1177
|
-
const result = await execInBox(container, ["bash", "-lc", lines.join("\n")], { user: "root" });
|
|
1178
|
-
if (result.exitCode !== 0) {
|
|
1179
|
-
throw new OverlayError(
|
|
1180
|
-
`failed to mount FUSE overlay in ${container}`,
|
|
1181
|
-
result.stdout,
|
|
1182
|
-
result.stderr
|
|
1183
|
-
);
|
|
1184
|
-
}
|
|
1185
|
-
return { upperWritePath: "/upper/upper" };
|
|
1299
|
+
await execa3("git", ["-C", args.hostMainRepo, "worktree", "prune"], { reject: false });
|
|
1186
1300
|
}
|
|
1187
|
-
function
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
async function verifyOverlay(container, lowerDirs = DEFAULT_LOWER_DIRS) {
|
|
1191
|
-
const sentinel = ".agentbox-overlay-check";
|
|
1192
|
-
const checks = [];
|
|
1193
|
-
const ls = await execInBox(container, ["bash", "-lc", `ls -A /workspace | head -1`], {
|
|
1194
|
-
user: "root"
|
|
1195
|
-
});
|
|
1196
|
-
checks.push({
|
|
1197
|
-
name: "workspace lists lower contents",
|
|
1198
|
-
ok: ls.exitCode === 0,
|
|
1199
|
-
detail: ls.exitCode === 0 ? `first entry: ${ls.stdout.trim() || "(empty)"}` : ls.stderr.trim()
|
|
1200
|
-
});
|
|
1201
|
-
const write = await execInBox(container, ["bash", "-lc", `touch /workspace/${sentinel}`], {
|
|
1202
|
-
user: "root"
|
|
1203
|
-
});
|
|
1204
|
-
checks.push({
|
|
1205
|
-
name: "write through overlay succeeds",
|
|
1206
|
-
ok: write.exitCode === 0,
|
|
1207
|
-
detail: write.exitCode === 0 ? `created /workspace/${sentinel}` : write.stderr.trim()
|
|
1208
|
-
});
|
|
1209
|
-
const upper = await execInBox(container, ["bash", "-lc", `test -f /upper/upper/${sentinel}`], {
|
|
1210
|
-
user: "root"
|
|
1211
|
-
});
|
|
1212
|
-
checks.push({
|
|
1213
|
-
name: "write lands in /upper (cow target)",
|
|
1214
|
-
ok: upper.exitCode === 0,
|
|
1215
|
-
detail: upper.exitCode === 0 ? `/upper/upper/${sentinel} exists` : `expected /upper/upper/${sentinel} to exist`
|
|
1216
|
-
});
|
|
1217
|
-
const lowerProbe = lowerDirs.map((d) => `test ! -e ${shellQuote(d)}/${sentinel}`).join(" && ");
|
|
1218
|
-
const lower = await execInBox(container, ["bash", "-lc", lowerProbe], { user: "root" });
|
|
1219
|
-
checks.push({
|
|
1220
|
-
name: `lower untouched (${lowerDirs.join(", ")})`,
|
|
1221
|
-
ok: lower.exitCode === 0,
|
|
1222
|
-
detail: lower.exitCode === 0 ? `${sentinel} absent from every lower` : `${sentinel} leaked into a lower layer`
|
|
1223
|
-
});
|
|
1224
|
-
await execInBox(container, ["bash", "-lc", `rm -f /workspace/${sentinel}`], { user: "root" });
|
|
1225
|
-
return checks;
|
|
1301
|
+
function ctParent(p) {
|
|
1302
|
+
const i = p.lastIndexOf("/");
|
|
1303
|
+
return i <= 0 ? "/" : p.slice(0, i);
|
|
1226
1304
|
}
|
|
1227
|
-
var OverlayError = class extends Error {
|
|
1228
|
-
constructor(message, stdout, stderr) {
|
|
1229
|
-
super(message);
|
|
1230
|
-
this.stdout = stdout;
|
|
1231
|
-
this.stderr = stderr;
|
|
1232
|
-
this.name = "OverlayError";
|
|
1233
|
-
}
|
|
1234
|
-
stdout;
|
|
1235
|
-
stderr;
|
|
1236
|
-
};
|
|
1237
1305
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
1238
1306
|
"node_modules",
|
|
1239
1307
|
".next",
|
|
@@ -1250,8 +1318,11 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
|
1250
1318
|
".parcel-cache"
|
|
1251
1319
|
]);
|
|
1252
1320
|
var SNAPSHOTS_ROOT = join3(homedir2(), ".agentbox", "snapshots");
|
|
1253
|
-
function snapshotPathFor(
|
|
1254
|
-
|
|
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);
|
|
1255
1326
|
}
|
|
1256
1327
|
async function findExcludedDirs(root, excluded = EXCLUDE_DIRS) {
|
|
1257
1328
|
const matches = [];
|
|
@@ -1281,7 +1352,7 @@ async function createSnapshot(opts) {
|
|
|
1281
1352
|
const excluded = opts.excluded ?? EXCLUDE_DIRS;
|
|
1282
1353
|
await mkdir2(SNAPSHOTS_ROOT, { recursive: true });
|
|
1283
1354
|
const cpArgs = platform() === "darwin" ? ["-cR"] : ["-R"];
|
|
1284
|
-
await
|
|
1355
|
+
await execa4("cp", [...cpArgs, `${source}/`, destination]);
|
|
1285
1356
|
const toPrune = await findExcludedDirs(destination, excluded);
|
|
1286
1357
|
await Promise.all(toPrune.map((p) => rm2(p, { recursive: true, force: true })));
|
|
1287
1358
|
return { destination, prunedPaths: toPrune };
|
|
@@ -1442,6 +1513,21 @@ async function stopRelay() {
|
|
|
1442
1513
|
});
|
|
1443
1514
|
return { stopped: true, pid };
|
|
1444
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
|
+
}
|
|
1445
1531
|
function pingHealthz(timeoutMs) {
|
|
1446
1532
|
return new Promise((resolveP) => {
|
|
1447
1533
|
const req = httpRequest(
|
|
@@ -1460,6 +1546,42 @@ function pingHealthz(timeoutMs) {
|
|
|
1460
1546
|
req.end();
|
|
1461
1547
|
});
|
|
1462
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
|
+
}
|
|
1463
1585
|
async function readPidFile() {
|
|
1464
1586
|
try {
|
|
1465
1587
|
const text = await readFile2(PID_FILE, "utf8");
|
|
@@ -1483,7 +1605,7 @@ function generateRelayToken() {
|
|
|
1483
1605
|
async function registerBoxWithRelay(args) {
|
|
1484
1606
|
const worktrees = (args.worktrees ?? []).map((w) => ({
|
|
1485
1607
|
containerPath: w.containerPath,
|
|
1486
|
-
|
|
1608
|
+
hostMainRepo: w.hostMainRepo,
|
|
1487
1609
|
branch: w.branch
|
|
1488
1610
|
}));
|
|
1489
1611
|
await adminPost("/admin/register-box", {
|
|
@@ -1492,6 +1614,7 @@ async function registerBoxWithRelay(args) {
|
|
|
1492
1614
|
name: args.name,
|
|
1493
1615
|
containerName: args.containerName,
|
|
1494
1616
|
createdAt: args.createdAt,
|
|
1617
|
+
projectIndex: args.projectIndex,
|
|
1495
1618
|
worktrees
|
|
1496
1619
|
});
|
|
1497
1620
|
}
|
|
@@ -1549,6 +1672,7 @@ async function rehydrateRelayRegistry(boxes) {
|
|
|
1549
1672
|
name: b.name,
|
|
1550
1673
|
containerName: b.container,
|
|
1551
1674
|
createdAt: b.createdAt,
|
|
1675
|
+
projectIndex: b.projectIndex,
|
|
1552
1676
|
worktrees: b.gitWorktrees
|
|
1553
1677
|
});
|
|
1554
1678
|
} catch {
|
|
@@ -1658,10 +1782,10 @@ async function ensureAgentboxTasksFile(container, services, opts = {}) {
|
|
|
1658
1782
|
return { status: "wrote" };
|
|
1659
1783
|
}
|
|
1660
1784
|
async function writeFileInBox(container, path, content) {
|
|
1661
|
-
const { execa:
|
|
1662
|
-
const result = await
|
|
1785
|
+
const { execa: execa5 } = await import("execa");
|
|
1786
|
+
const result = await execa5(
|
|
1663
1787
|
"docker",
|
|
1664
|
-
["exec", "-i", "--user", "vscode", container, "sh", "-c", `cat > ${
|
|
1788
|
+
["exec", "-i", "--user", "vscode", container, "sh", "-c", `cat > ${shellQuote(path)}`],
|
|
1665
1789
|
{ input: content, reject: false }
|
|
1666
1790
|
);
|
|
1667
1791
|
return {
|
|
@@ -1670,7 +1794,7 @@ async function writeFileInBox(container, path, content) {
|
|
|
1670
1794
|
stderr: result.stderr ?? ""
|
|
1671
1795
|
};
|
|
1672
1796
|
}
|
|
1673
|
-
function
|
|
1797
|
+
function shellQuote(s) {
|
|
1674
1798
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
1675
1799
|
}
|
|
1676
1800
|
|
|
@@ -2157,19 +2281,22 @@ async function loadConfig(path) {
|
|
|
2157
2281
|
}
|
|
2158
2282
|
|
|
2159
2283
|
export {
|
|
2284
|
+
DEFAULT_RELAY_PORT,
|
|
2160
2285
|
RELAY_CONTAINER_NAME,
|
|
2161
2286
|
RELAY_NETWORK_NAME,
|
|
2162
2287
|
RELAY_IMAGE_REF,
|
|
2163
2288
|
SHARED_CLAUDE_VOLUME,
|
|
2164
2289
|
resolveClaudeVolume,
|
|
2165
2290
|
ensureClaudeVolume,
|
|
2291
|
+
seedSetupSkillIntoVolume,
|
|
2166
2292
|
buildClaudeMounts,
|
|
2167
2293
|
rebuildPluginNativeDeps,
|
|
2168
2294
|
ClaudeSessionError,
|
|
2169
2295
|
startClaudeSession,
|
|
2296
|
+
buildClaudeAttachArgv,
|
|
2170
2297
|
buildClaudeDashboardAttachArgv,
|
|
2171
2298
|
buildShellArgv,
|
|
2172
|
-
|
|
2299
|
+
formatDetachNotice,
|
|
2173
2300
|
claudeSessionInfo,
|
|
2174
2301
|
pullClaudeExtras,
|
|
2175
2302
|
SHARED_DOCKER_CACHE_VOLUME,
|
|
@@ -2181,17 +2308,21 @@ export {
|
|
|
2181
2308
|
buildVncUrls,
|
|
2182
2309
|
WEB_CONTAINER_PORT,
|
|
2183
2310
|
detectGitRepos,
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2311
|
+
pickFreshBranch,
|
|
2312
|
+
gitWorktreePathFor,
|
|
2313
|
+
collectRepoCarryOver,
|
|
2314
|
+
chownGitBindParents,
|
|
2315
|
+
bindWorktrees,
|
|
2316
|
+
seedWorkspace,
|
|
2317
|
+
seedWorkspaceFromDir,
|
|
2318
|
+
removeInBoxWorktree,
|
|
2189
2319
|
SNAPSHOTS_ROOT,
|
|
2190
2320
|
snapshotPathFor,
|
|
2191
2321
|
createSnapshot,
|
|
2192
2322
|
launchCtlDaemon,
|
|
2193
2323
|
ensureRelay,
|
|
2194
2324
|
stopRelay,
|
|
2325
|
+
getRelayStatus,
|
|
2195
2326
|
generateRelayToken,
|
|
2196
2327
|
registerBoxWithRelay,
|
|
2197
2328
|
forgetBoxFromRelay,
|
|
@@ -2212,4 +2343,4 @@ export {
|
|
|
2212
2343
|
ConfigError,
|
|
2213
2344
|
loadConfig
|
|
2214
2345
|
};
|
|
2215
|
-
//# sourceMappingURL=chunk-
|
|
2346
|
+
//# sourceMappingURL=chunk-PXUBE5KS.js.map
|