@madarco/agentbox 0.6.0 → 0.8.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/_cloud-attach-T727ZPRV.js +13 -0
- package/dist/chunk-67N47KUS.js +1640 -0
- package/dist/chunk-67N47KUS.js.map +1 -0
- package/dist/chunk-6OZDFNBF.js +8114 -0
- package/dist/chunk-6OZDFNBF.js.map +1 -0
- package/dist/chunk-BGK32PZE.js +455 -0
- package/dist/chunk-BGK32PZE.js.map +1 -0
- package/dist/chunk-FODMEHD3.js +1200 -0
- package/dist/chunk-FODMEHD3.js.map +1 -0
- package/dist/chunk-G3H2L3O2.js +288 -0
- package/dist/chunk-G3H2L3O2.js.map +1 -0
- package/dist/chunk-I24B6AXR.js +600 -0
- package/dist/chunk-I24B6AXR.js.map +1 -0
- package/dist/chunk-LEV3KICD.js +738 -0
- package/dist/chunk-LEV3KICD.js.map +1 -0
- package/dist/cloud-poller-SUNA6ZQC-2RG5WPRN.js +10 -0
- package/dist/dist-L4LCG5SJ.js +293 -0
- package/dist/dist-L4LCG5SJ.js.map +1 -0
- package/dist/dist-LOZBWMBF.js +447 -0
- package/dist/dist-ZODPD2I6.js +1407 -0
- package/dist/dist-ZODPD2I6.js.map +1 -0
- package/dist/index.js +7281 -2134
- package/dist/index.js.map +1 -1
- package/dist/prepared-state-CL4CWXQA-ME4HSKDE.js +18 -0
- package/package.json +8 -3
- package/runtime/daytona/custom-system-CLAUDE.md +39 -0
- package/runtime/docker/Dockerfile.box +120 -14
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +15 -8
- package/runtime/docker/packages/ctl/dist/bin.cjs +11310 -816
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json +68 -0
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-open +9 -9
- package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +62 -1
- package/runtime/docker/packages/sandbox-docker/scripts/custom-system-CLAUDE.md +15 -4
- package/runtime/docker/packages/sandbox-docker/scripts/gh-shim +263 -0
- package/runtime/docker/packages/sandbox-docker/scripts/git-shim +131 -0
- package/runtime/docker/packages/sandbox-docker/scripts/opencode-agentbox-plugin.js +76 -0
- package/runtime/hetzner/agentbox-checkpoint-cleanup +52 -0
- package/runtime/hetzner/agentbox-codex-hooks.json +68 -0
- package/runtime/hetzner/agentbox-dockerd-start +132 -0
- package/runtime/hetzner/agentbox-open +28 -0
- package/runtime/hetzner/agentbox-setup-skill.md +196 -0
- package/runtime/hetzner/agentbox-vnc-start +77 -0
- package/runtime/hetzner/claude-managed-settings.json +115 -0
- package/runtime/hetzner/ctl.cjs +23397 -0
- package/runtime/hetzner/custom-system-CLAUDE.md +39 -0
- package/runtime/hetzner/gh-shim +263 -0
- package/runtime/hetzner/git-shim +131 -0
- package/runtime/hetzner/opencode-agentbox-plugin.js +76 -0
- package/runtime/hetzner/scripts/install-box.sh +374 -0
- package/runtime/relay/bin.cjs +10017 -817
- package/share/agentbox-setup/SKILL.md +15 -8
- package/share/host-skills/agentbox/SKILL.md +29 -0
- package/share/host-skills/agentbox-info/SKILL.md +211 -0
- package/share/host-skills/codex/agentbox.md +35 -0
- package/share/host-skills/opencode/agentbox.md +26 -0
- package/dist/chunk-BBZMA2K6.js +0 -238
- package/dist/chunk-BBZMA2K6.js.map +0 -1
- package/dist/chunk-HHMWQNLF.js +0 -1709
- package/dist/chunk-HHMWQNLF.js.map +0 -1
- package/dist/chunk-HPZMD5DE.js +0 -106
- package/dist/chunk-HPZMD5DE.js.map +0 -1
- package/dist/chunk-HTTKML3C.js +0 -2655
- package/dist/chunk-HTTKML3C.js.map +0 -1
- package/dist/chunk-KJNZP6I3.js +0 -586
- package/dist/chunk-KJNZP6I3.js.map +0 -1
- package/dist/chunk-M7I247BK.js +0 -525
- package/dist/chunk-M7I247BK.js.map +0 -1
- package/dist/create-6PWXI6HO-OWAMHBAK.js +0 -15
- package/dist/lifecycle-EMXR46DI-DUVBXNTV.js +0 -38
- package/dist/state-KD7M46ZP-KHFTHFUS.js +0 -26
- package/dist/stats-SZXOJE3D-N7OODCHW.js +0 -19
- /package/dist/{create-6PWXI6HO-OWAMHBAK.js.map → _cloud-attach-T727ZPRV.js.map} +0 -0
- /package/dist/{lifecycle-EMXR46DI-DUVBXNTV.js.map → cloud-poller-SUNA6ZQC-2RG5WPRN.js.map} +0 -0
- /package/dist/{state-KD7M46ZP-KHFTHFUS.js.map → dist-LOZBWMBF.js.map} +0 -0
- /package/dist/{stats-SZXOJE3D-N7OODCHW.js.map → prepared-state-CL4CWXQA-ME4HSKDE.js.map} +0 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ../../packages/sandbox-core/dist/index.js
|
|
4
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { dirname, join } from "path";
|
|
7
|
+
import { execa } from "execa";
|
|
8
|
+
import { readdir, stat } from "fs/promises";
|
|
9
|
+
import { join as join2 } from "path";
|
|
10
|
+
import { createHash } from "crypto";
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
12
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
13
|
+
import { homedir as homedir2 } from "os";
|
|
14
|
+
import { dirname as dirname2, resolve as pathResolve } from "path";
|
|
15
|
+
var STATE_DIR = join(homedir(), ".agentbox");
|
|
16
|
+
var STATE_FILE = join(STATE_DIR, "state.json");
|
|
17
|
+
var EMPTY = { version: 1, boxes: [] };
|
|
18
|
+
async function readState(path = STATE_FILE) {
|
|
19
|
+
try {
|
|
20
|
+
const raw = await readFile(path, "utf8");
|
|
21
|
+
const parsed = JSON.parse(raw);
|
|
22
|
+
if (parsed.version !== 1 || !Array.isArray(parsed.boxes)) {
|
|
23
|
+
throw new Error(`unrecognized state file shape at ${path}`);
|
|
24
|
+
}
|
|
25
|
+
for (const b of parsed.boxes) {
|
|
26
|
+
b.provider ??= "docker";
|
|
27
|
+
if ((b.provider ?? "docker") === "docker" && !b.docker) {
|
|
28
|
+
b.docker = projectDockerFields(b);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return parsed;
|
|
32
|
+
} catch (err) {
|
|
33
|
+
if (err.code === "ENOENT") {
|
|
34
|
+
return { ...EMPTY };
|
|
35
|
+
}
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function writeState(state, path = STATE_FILE) {
|
|
40
|
+
await mkdir(dirname(path), { recursive: true });
|
|
41
|
+
await writeFile(path, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
42
|
+
}
|
|
43
|
+
async function recordBox(box, path = STATE_FILE) {
|
|
44
|
+
const toWrite = (box.provider ?? "docker") === "docker" && !box.docker ? { ...box, docker: projectDockerFields(box) } : box;
|
|
45
|
+
const state = await readState(path);
|
|
46
|
+
const next = {
|
|
47
|
+
version: 1,
|
|
48
|
+
boxes: [...state.boxes.filter((b) => b.id !== toWrite.id), toWrite]
|
|
49
|
+
};
|
|
50
|
+
await writeState(next, path);
|
|
51
|
+
}
|
|
52
|
+
function projectDockerFields(box) {
|
|
53
|
+
return {
|
|
54
|
+
container: box.container,
|
|
55
|
+
image: box.image,
|
|
56
|
+
snapshotDir: box.snapshotDir ?? null,
|
|
57
|
+
socketPath: box.socketPath,
|
|
58
|
+
claudeConfigVolume: box.claudeConfigVolume,
|
|
59
|
+
codexConfigVolume: box.codexConfigVolume,
|
|
60
|
+
opencodeConfigVolume: box.opencodeConfigVolume,
|
|
61
|
+
vscodeServerVolume: box.vscodeServerVolume,
|
|
62
|
+
cursorServerVolume: box.cursorServerVolume,
|
|
63
|
+
vncHostPort: box.vncHostPort,
|
|
64
|
+
webHostPort: box.webHostPort,
|
|
65
|
+
portlessAlias: box.portlessAlias,
|
|
66
|
+
portlessUrl: box.portlessUrl,
|
|
67
|
+
portlessVncAlias: box.portlessVncAlias,
|
|
68
|
+
portlessVncUrl: box.portlessVncUrl,
|
|
69
|
+
dockerVolume: box.dockerVolume,
|
|
70
|
+
dockerCacheShared: box.dockerCacheShared,
|
|
71
|
+
checkpointImage: box.checkpointImage
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function removeBoxRecord(id, path = STATE_FILE) {
|
|
75
|
+
const state = await readState(path);
|
|
76
|
+
const before = state.boxes.length;
|
|
77
|
+
const next = {
|
|
78
|
+
version: 1,
|
|
79
|
+
boxes: state.boxes.filter((b) => b.id !== id)
|
|
80
|
+
};
|
|
81
|
+
if (next.boxes.length === before) return false;
|
|
82
|
+
await writeState(next, path);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
function findBox(idOrName, state) {
|
|
86
|
+
const q = idOrName.trim();
|
|
87
|
+
if (q.length === 0) return { kind: "none" };
|
|
88
|
+
const exactId = state.boxes.find((b) => b.id === q);
|
|
89
|
+
if (exactId) return { kind: "ok", box: exactId };
|
|
90
|
+
const prefixMatches = state.boxes.filter((b) => b.id.startsWith(q));
|
|
91
|
+
if (prefixMatches.length === 1) return { kind: "ok", box: prefixMatches[0] };
|
|
92
|
+
if (prefixMatches.length > 1) return { kind: "ambiguous", matches: prefixMatches };
|
|
93
|
+
const byName = state.boxes.find((b) => b.name === q);
|
|
94
|
+
if (byName) return { kind: "ok", box: byName };
|
|
95
|
+
const byContainer = state.boxes.find((b) => b.container === q);
|
|
96
|
+
if (byContainer) return { kind: "ok", box: byContainer };
|
|
97
|
+
return { kind: "none" };
|
|
98
|
+
}
|
|
99
|
+
function allocateProjectIndex(state, projectRoot) {
|
|
100
|
+
let max = 0;
|
|
101
|
+
for (const b of state.boxes) {
|
|
102
|
+
if (b.projectRoot !== projectRoot) continue;
|
|
103
|
+
if (typeof b.projectIndex === "number" && b.projectIndex > max) {
|
|
104
|
+
max = b.projectIndex;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return max + 1;
|
|
108
|
+
}
|
|
109
|
+
function autoPickProjectBox(state, projectRoot) {
|
|
110
|
+
const matches = state.boxes.filter((b) => b.projectRoot === projectRoot);
|
|
111
|
+
if (matches.length === 0) return { kind: "none" };
|
|
112
|
+
if (matches.length === 1) return { kind: "ok", box: matches[0] };
|
|
113
|
+
return { kind: "ambiguous", matches };
|
|
114
|
+
}
|
|
115
|
+
function resolveBoxRef(ref, state, projectRoot) {
|
|
116
|
+
if (ref === void 0) {
|
|
117
|
+
if (projectRoot === void 0) return { kind: "none" };
|
|
118
|
+
return autoPickProjectBox(state, projectRoot);
|
|
119
|
+
}
|
|
120
|
+
const trimmed = ref.trim();
|
|
121
|
+
if (projectRoot !== void 0 && /^[1-9][0-9]*$/.test(trimmed)) {
|
|
122
|
+
const idx = Number.parseInt(trimmed, 10);
|
|
123
|
+
const hit = state.boxes.find(
|
|
124
|
+
(b) => b.projectRoot === projectRoot && b.projectIndex === idx
|
|
125
|
+
);
|
|
126
|
+
return hit ? { kind: "ok", box: hit } : { kind: "none" };
|
|
127
|
+
}
|
|
128
|
+
return findBox(trimmed, state);
|
|
129
|
+
}
|
|
130
|
+
async function detectGitRepos(workspace) {
|
|
131
|
+
const out = [];
|
|
132
|
+
if (await isGitDir(join2(workspace, ".git"))) {
|
|
133
|
+
out.push({ kind: "root", hostMainRepo: workspace, relPathFromWorkspace: "" });
|
|
134
|
+
}
|
|
135
|
+
let entries;
|
|
136
|
+
try {
|
|
137
|
+
entries = await readdir(workspace, { withFileTypes: true });
|
|
138
|
+
} catch {
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
for (const e of entries) {
|
|
142
|
+
if (!e.isDirectory() || e.name.startsWith(".")) continue;
|
|
143
|
+
const sub = join2(workspace, e.name);
|
|
144
|
+
if (await isGitDir(join2(sub, ".git"))) {
|
|
145
|
+
out.push({ kind: "nested", hostMainRepo: sub, relPathFromWorkspace: e.name });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
async function isGitDir(path) {
|
|
151
|
+
try {
|
|
152
|
+
const s = await stat(path);
|
|
153
|
+
return s.isDirectory();
|
|
154
|
+
} catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function pickFreshBranch(hostMainRepo, base) {
|
|
159
|
+
let candidate = base;
|
|
160
|
+
let suffix = 2;
|
|
161
|
+
while (await branchExists(hostMainRepo, candidate)) {
|
|
162
|
+
candidate = `${base}-${String(suffix++)}`;
|
|
163
|
+
if (suffix > 100) throw new GitWorktreeError(`could not find a free branch name near ${base}`);
|
|
164
|
+
}
|
|
165
|
+
return candidate;
|
|
166
|
+
}
|
|
167
|
+
async function branchExists(hostMainRepo, name) {
|
|
168
|
+
const result = await execa(
|
|
169
|
+
"git",
|
|
170
|
+
["-C", hostMainRepo, "show-ref", "--verify", "--quiet", `refs/heads/${name}`],
|
|
171
|
+
{ reject: false }
|
|
172
|
+
);
|
|
173
|
+
return result.exitCode === 0;
|
|
174
|
+
}
|
|
175
|
+
var GitWorktreeError = class extends Error {
|
|
176
|
+
constructor(message) {
|
|
177
|
+
super(message);
|
|
178
|
+
this.name = "GitWorktreeError";
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
function preparedStatePathFor(provider) {
|
|
182
|
+
return pathResolve(homedir2(), ".agentbox", `${provider}-prepared.json`);
|
|
183
|
+
}
|
|
184
|
+
function readPreparedStateRaw(provider) {
|
|
185
|
+
const path = preparedStatePathFor(provider);
|
|
186
|
+
if (!existsSync(path)) return null;
|
|
187
|
+
try {
|
|
188
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
189
|
+
} catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function writePreparedStateRaw(provider, state) {
|
|
194
|
+
const path = preparedStatePathFor(provider);
|
|
195
|
+
mkdirSync(dirname2(path), { recursive: true });
|
|
196
|
+
const body = JSON.stringify(state, null, 2) + "\n";
|
|
197
|
+
const tmp = `${path}.tmp`;
|
|
198
|
+
writeFileSync(tmp, body, { mode: 384 });
|
|
199
|
+
renameSync(tmp, path);
|
|
200
|
+
}
|
|
201
|
+
async function sha256OfFile(path) {
|
|
202
|
+
const buf = await readFile2(path);
|
|
203
|
+
return createHash("sha256").update(buf).digest("hex");
|
|
204
|
+
}
|
|
205
|
+
async function computeContextSha256(files) {
|
|
206
|
+
const sorted = [...files].sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0);
|
|
207
|
+
const outer = createHash("sha256");
|
|
208
|
+
for (const f of sorted) {
|
|
209
|
+
const inner = await sha256OfFile(f.abs);
|
|
210
|
+
outer.update(`${f.rel}\0${inner}
|
|
211
|
+
`);
|
|
212
|
+
}
|
|
213
|
+
return outer.digest("hex");
|
|
214
|
+
}
|
|
215
|
+
function readCliStamp() {
|
|
216
|
+
return {
|
|
217
|
+
cliVersion: process.env.AGENTBOX_CLI_VERSION ?? "unknown",
|
|
218
|
+
cliCommit: process.env.AGENTBOX_CLI_COMMIT ?? "unknown"
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
var DOCKER_CONTEXT_FILE_MAP = {
|
|
222
|
+
"Dockerfile.box": { staged: "Dockerfile.box", dev: "Dockerfile.box" },
|
|
223
|
+
"ctl/bin.cjs": {
|
|
224
|
+
staged: "packages/ctl/dist/bin.cjs",
|
|
225
|
+
dev: "../ctl/dist/bin.cjs"
|
|
226
|
+
},
|
|
227
|
+
"share/agentbox-setup/SKILL.md": {
|
|
228
|
+
staged: "apps/cli/share/agentbox-setup/SKILL.md",
|
|
229
|
+
dev: "../../apps/cli/share/agentbox-setup/SKILL.md"
|
|
230
|
+
},
|
|
231
|
+
"scripts/agentbox-vnc-start": {
|
|
232
|
+
staged: "packages/sandbox-docker/scripts/agentbox-vnc-start",
|
|
233
|
+
dev: "scripts/agentbox-vnc-start"
|
|
234
|
+
},
|
|
235
|
+
"scripts/agentbox-dockerd-start": {
|
|
236
|
+
staged: "packages/sandbox-docker/scripts/agentbox-dockerd-start",
|
|
237
|
+
dev: "scripts/agentbox-dockerd-start"
|
|
238
|
+
},
|
|
239
|
+
"scripts/agentbox-checkpoint-cleanup": {
|
|
240
|
+
staged: "packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup",
|
|
241
|
+
dev: "scripts/agentbox-checkpoint-cleanup"
|
|
242
|
+
},
|
|
243
|
+
"scripts/agentbox-open": {
|
|
244
|
+
staged: "packages/sandbox-docker/scripts/agentbox-open",
|
|
245
|
+
dev: "scripts/agentbox-open"
|
|
246
|
+
},
|
|
247
|
+
"scripts/custom-system-CLAUDE.md": {
|
|
248
|
+
staged: "packages/sandbox-docker/scripts/custom-system-CLAUDE.md",
|
|
249
|
+
dev: "scripts/custom-system-CLAUDE.md"
|
|
250
|
+
},
|
|
251
|
+
"scripts/claude-managed-settings.json": {
|
|
252
|
+
staged: "packages/sandbox-docker/scripts/claude-managed-settings.json",
|
|
253
|
+
dev: "scripts/claude-managed-settings.json"
|
|
254
|
+
},
|
|
255
|
+
"scripts/agentbox-codex-hooks.json": {
|
|
256
|
+
staged: "packages/sandbox-docker/scripts/agentbox-codex-hooks.json",
|
|
257
|
+
dev: "scripts/agentbox-codex-hooks.json"
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
function resolveContextFilesFrom(fileMap, opts) {
|
|
261
|
+
const out = [];
|
|
262
|
+
for (const [rel, paths] of Object.entries(fileMap)) {
|
|
263
|
+
const candidates = [
|
|
264
|
+
pathResolve(opts.contextDir, paths.staged),
|
|
265
|
+
pathResolve(opts.devRoot, paths.dev)
|
|
266
|
+
];
|
|
267
|
+
const hit = candidates.find((p) => existsSync(p));
|
|
268
|
+
if (!hit) return null;
|
|
269
|
+
out.push({ rel, abs: hit });
|
|
270
|
+
}
|
|
271
|
+
return out;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ../../packages/sandbox-docker/dist/chunk-OZU3LVKX.js
|
|
275
|
+
import { dirname as dirname22, resolve as resolve2 } from "path";
|
|
276
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
277
|
+
import { execa as execa2 } from "execa";
|
|
278
|
+
import { existsSync as existsSync2 } from "fs";
|
|
279
|
+
import { fileURLToPath } from "url";
|
|
280
|
+
import { dirname as dirname3, resolve } from "path";
|
|
281
|
+
var DEFAULT_BOX_IMAGE = "agentbox/box:dev";
|
|
282
|
+
var here = dirname3(fileURLToPath(import.meta.url));
|
|
283
|
+
function resolveDockerBuild() {
|
|
284
|
+
const override = process.env.AGENTBOX_DOCKER_CONTEXT;
|
|
285
|
+
if (override && existsSync2(resolve(override, "Dockerfile.box"))) {
|
|
286
|
+
return { dockerfile: resolve(override, "Dockerfile.box"), context: override };
|
|
287
|
+
}
|
|
288
|
+
const staged = resolve(here, "..", "runtime", "docker");
|
|
289
|
+
if (existsSync2(resolve(staged, "Dockerfile.box"))) {
|
|
290
|
+
return { dockerfile: resolve(staged, "Dockerfile.box"), context: staged };
|
|
291
|
+
}
|
|
292
|
+
const packageRoot = resolve(here, "..");
|
|
293
|
+
return {
|
|
294
|
+
dockerfile: resolve(packageRoot, "Dockerfile.box"),
|
|
295
|
+
context: resolve(packageRoot, "..", "..")
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
var { dockerfile: DOCKERFILE_PATH_RESOLVED, context: BUILD_CONTEXT_DIR_RESOLVED } = resolveDockerBuild();
|
|
299
|
+
var DOCKERFILE_PATH = DOCKERFILE_PATH_RESOLVED;
|
|
300
|
+
var BUILD_CONTEXT_DIR = BUILD_CONTEXT_DIR_RESOLVED;
|
|
301
|
+
async function imageExists(ref) {
|
|
302
|
+
const result = await execa2("docker", ["image", "inspect", ref], { reject: false });
|
|
303
|
+
return result.exitCode === 0;
|
|
304
|
+
}
|
|
305
|
+
async function imageInfo(ref = DEFAULT_BOX_IMAGE) {
|
|
306
|
+
const result = await execa2(
|
|
307
|
+
"docker",
|
|
308
|
+
["image", "inspect", "--format", "{{.Size}}|{{.Created}}", ref],
|
|
309
|
+
{ reject: false }
|
|
310
|
+
);
|
|
311
|
+
if (result.exitCode !== 0) return { ref, exists: false };
|
|
312
|
+
const [sizeStr, createdAt] = result.stdout.trim().split("|");
|
|
313
|
+
const sizeBytes = sizeStr ? Number.parseInt(sizeStr, 10) : NaN;
|
|
314
|
+
return {
|
|
315
|
+
ref,
|
|
316
|
+
exists: true,
|
|
317
|
+
sizeBytes: Number.isFinite(sizeBytes) ? sizeBytes : void 0,
|
|
318
|
+
createdAt: createdAt && createdAt.length > 0 ? createdAt : void 0
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
async function buildImage(opts = {}) {
|
|
322
|
+
const ref = opts.ref ?? DEFAULT_BOX_IMAGE;
|
|
323
|
+
const dockerfile = opts.dockerfile ?? DOCKERFILE_PATH;
|
|
324
|
+
const contextDir = opts.contextDir ?? BUILD_CONTEXT_DIR;
|
|
325
|
+
const args = ["build", "-t", ref, "-f", dockerfile, contextDir];
|
|
326
|
+
if (process.env.AGENTBOX === "1") {
|
|
327
|
+
args.splice(1, 0, "--network=host");
|
|
328
|
+
}
|
|
329
|
+
const subprocess = execa2("docker", args, {
|
|
330
|
+
stderr: "pipe",
|
|
331
|
+
stdout: "pipe"
|
|
332
|
+
});
|
|
333
|
+
if (opts.onProgress) {
|
|
334
|
+
const forward = (chunk) => {
|
|
335
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
336
|
+
for (const line of text.split(/\r?\n/)) {
|
|
337
|
+
if (line.length > 0) opts.onProgress?.(line);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
subprocess.stdout?.on("data", forward);
|
|
341
|
+
subprocess.stderr?.on("data", forward);
|
|
342
|
+
}
|
|
343
|
+
await subprocess;
|
|
344
|
+
return ref;
|
|
345
|
+
}
|
|
346
|
+
async function ensureImage(ref = DEFAULT_BOX_IMAGE, opts = {}) {
|
|
347
|
+
const {
|
|
348
|
+
computeDockerContextFingerprint: computeDockerContextFingerprint2,
|
|
349
|
+
readPreparedDockerState: readPreparedDockerState2,
|
|
350
|
+
writePreparedDockerState: writePreparedDockerState2,
|
|
351
|
+
preparedMatches: preparedMatches2
|
|
352
|
+
} = await import("./prepared-state-CL4CWXQA-ME4HSKDE.js");
|
|
353
|
+
const fingerprint = await computeDockerContextFingerprint2({
|
|
354
|
+
contextDir: opts.contextDir
|
|
355
|
+
});
|
|
356
|
+
const prepared = readPreparedDockerState2();
|
|
357
|
+
const exists = await imageExists(ref);
|
|
358
|
+
let reason;
|
|
359
|
+
if (!exists) {
|
|
360
|
+
reason = `image ${ref} not present`;
|
|
361
|
+
} else if (!fingerprint) {
|
|
362
|
+
return { ref, built: false, reason: "image present (fingerprint skipped)" };
|
|
363
|
+
} else if (!prepared) {
|
|
364
|
+
reason = "no docker-prepared.json on disk";
|
|
365
|
+
} else if (!preparedMatches2(prepared, fingerprint.contextSha256)) {
|
|
366
|
+
reason = `build context changed (was ${prepared.base?.contextSha256?.slice(0, 12) ?? "<none>"}, now ${fingerprint.contextSha256.slice(0, 12)})`;
|
|
367
|
+
}
|
|
368
|
+
if (!reason) {
|
|
369
|
+
return { ref, built: false, reason: "image up to date" };
|
|
370
|
+
}
|
|
371
|
+
opts.onProgress?.(`[image] rebuilding ${ref}: ${reason}`);
|
|
372
|
+
await buildImage({
|
|
373
|
+
ref,
|
|
374
|
+
dockerfile: opts.dockerfile,
|
|
375
|
+
contextDir: opts.contextDir,
|
|
376
|
+
onProgress: opts.onProgress
|
|
377
|
+
});
|
|
378
|
+
if (fingerprint) {
|
|
379
|
+
writePreparedDockerState2({ imageRef: ref, contextSha256: fingerprint.contextSha256 });
|
|
380
|
+
}
|
|
381
|
+
return { ref, built: true, reason };
|
|
382
|
+
}
|
|
383
|
+
var SCHEMA = 1;
|
|
384
|
+
function resolveContextFiles(opts = {}) {
|
|
385
|
+
const ctx = opts.contextDir ?? BUILD_CONTEXT_DIR;
|
|
386
|
+
const here2 = dirname22(fileURLToPath2(import.meta.url));
|
|
387
|
+
const packageRoot = resolve2(here2, "..");
|
|
388
|
+
return resolveContextFilesFrom(DOCKER_CONTEXT_FILE_MAP, {
|
|
389
|
+
contextDir: ctx,
|
|
390
|
+
devRoot: packageRoot
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
async function computeDockerContextFingerprint(opts = {}) {
|
|
394
|
+
const files = resolveContextFiles(opts);
|
|
395
|
+
if (!files) return null;
|
|
396
|
+
return { contextSha256: await computeContextSha256(files), files };
|
|
397
|
+
}
|
|
398
|
+
function readPreparedDockerState() {
|
|
399
|
+
const raw = readPreparedStateRaw("docker");
|
|
400
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
401
|
+
const parsed = raw;
|
|
402
|
+
if (parsed.schema !== SCHEMA) return null;
|
|
403
|
+
return { schema: SCHEMA, base: parsed.base };
|
|
404
|
+
}
|
|
405
|
+
function writePreparedDockerState(opts) {
|
|
406
|
+
const stamp = readCliStamp();
|
|
407
|
+
const state = {
|
|
408
|
+
schema: SCHEMA,
|
|
409
|
+
base: {
|
|
410
|
+
imageRef: opts.imageRef ?? DEFAULT_BOX_IMAGE,
|
|
411
|
+
contextSha256: opts.contextSha256,
|
|
412
|
+
cliVersion: stamp.cliVersion,
|
|
413
|
+
cliCommit: stamp.cliCommit,
|
|
414
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
writePreparedStateRaw("docker", state);
|
|
418
|
+
}
|
|
419
|
+
function preparedMatches(state, current) {
|
|
420
|
+
return state?.base?.contextSha256 === current;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export {
|
|
424
|
+
STATE_DIR,
|
|
425
|
+
STATE_FILE,
|
|
426
|
+
readState,
|
|
427
|
+
recordBox,
|
|
428
|
+
removeBoxRecord,
|
|
429
|
+
findBox,
|
|
430
|
+
allocateProjectIndex,
|
|
431
|
+
autoPickProjectBox,
|
|
432
|
+
resolveBoxRef,
|
|
433
|
+
detectGitRepos,
|
|
434
|
+
pickFreshBranch,
|
|
435
|
+
GitWorktreeError,
|
|
436
|
+
preparedStatePathFor,
|
|
437
|
+
readPreparedStateRaw,
|
|
438
|
+
writePreparedStateRaw,
|
|
439
|
+
computeContextSha256,
|
|
440
|
+
readCliStamp,
|
|
441
|
+
DOCKER_CONTEXT_FILE_MAP,
|
|
442
|
+
resolveContextFilesFrom,
|
|
443
|
+
DEFAULT_BOX_IMAGE,
|
|
444
|
+
DOCKERFILE_PATH,
|
|
445
|
+
imageExists,
|
|
446
|
+
imageInfo,
|
|
447
|
+
buildImage,
|
|
448
|
+
ensureImage,
|
|
449
|
+
resolveContextFiles,
|
|
450
|
+
computeDockerContextFingerprint,
|
|
451
|
+
readPreparedDockerState,
|
|
452
|
+
writePreparedDockerState,
|
|
453
|
+
preparedMatches
|
|
454
|
+
};
|
|
455
|
+
//# sourceMappingURL=chunk-BGK32PZE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../packages/sandbox-core/src/state.ts","../../../packages/sandbox-core/src/git-detect.ts","../../../packages/sandbox-core/src/prepared-state.ts","../../../packages/sandbox-docker/src/prepared-state.ts","../../../packages/sandbox-docker/src/image.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport type { BoxRecord, DockerBoxFields, FindBoxResult, StateFile } from '@agentbox/core';\n\nexport const STATE_DIR = join(homedir(), '.agentbox');\nexport const STATE_FILE = join(STATE_DIR, 'state.json');\n\nconst EMPTY: StateFile = { version: 1, boxes: [] };\n\nexport async function readState(path: string = STATE_FILE): Promise<StateFile> {\n try {\n const raw = await readFile(path, 'utf8');\n const parsed = JSON.parse(raw) as StateFile;\n if (parsed.version !== 1 || !Array.isArray(parsed.boxes)) {\n throw new Error(`unrecognized state file shape at ${path}`);\n }\n // Migrate-on-read: records written before the multi-provider split carry no\n // `provider` field — they are all Docker boxes. Default it so every\n // consumer (provider registry, `findBox`) sees a discriminated record.\n // Also backfill `box.docker` from the flat fields for Docker records so\n // forward-looking readers (7.1) see the nested shape without waiting\n // for the box to be re-recorded.\n for (const b of parsed.boxes) {\n b.provider ??= 'docker';\n if ((b.provider ?? 'docker') === 'docker' && !b.docker) {\n b.docker = projectDockerFields(b);\n }\n }\n return parsed;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return { ...EMPTY };\n }\n throw err;\n }\n}\n\nexport async function writeState(state: StateFile, path: string = STATE_FILE): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, JSON.stringify(state, null, 2) + '\\n', 'utf8');\n}\n\nexport async function recordBox(box: BoxRecord, path: string = STATE_FILE): Promise<void> {\n // Forward-looking shape: every Docker write also mirrors the flat\n // docker-specific fields into `box.docker` so readers can move to the\n // nested form opportunistically (7.1). Cloud records skip the mirror —\n // the discriminator is `box.provider !== 'docker'`.\n const toWrite: BoxRecord =\n (box.provider ?? 'docker') === 'docker' && !box.docker\n ? { ...box, docker: projectDockerFields(box) }\n : box;\n const state = await readState(path);\n const next: StateFile = {\n version: 1,\n boxes: [...state.boxes.filter((b) => b.id !== toWrite.id), toWrite],\n };\n await writeState(next, path);\n}\n\n/**\n * Build a `DockerBoxFields` payload from the flat Docker-specific fields\n * still living on `BoxRecord` for back-compat. Pure function, no\n * filesystem; safe for both `readState` migration and `recordBox` mirror.\n *\n * Once every reader uses `box.docker?.<field>` (the rest of 7.1), the\n * flat fields can be dropped and this projection becomes the canonical\n * shape. Until then, every write produces both shapes from the same\n * source so they can't drift.\n */\nfunction projectDockerFields(box: BoxRecord): DockerBoxFields {\n return {\n container: box.container,\n image: box.image,\n snapshotDir: box.snapshotDir ?? null,\n socketPath: box.socketPath,\n claudeConfigVolume: box.claudeConfigVolume,\n codexConfigVolume: box.codexConfigVolume,\n opencodeConfigVolume: box.opencodeConfigVolume,\n vscodeServerVolume: box.vscodeServerVolume,\n cursorServerVolume: box.cursorServerVolume,\n vncHostPort: box.vncHostPort,\n webHostPort: box.webHostPort,\n portlessAlias: box.portlessAlias,\n portlessUrl: box.portlessUrl,\n portlessVncAlias: box.portlessVncAlias,\n portlessVncUrl: box.portlessVncUrl,\n dockerVolume: box.dockerVolume,\n dockerCacheShared: box.dockerCacheShared,\n checkpointImage: box.checkpointImage,\n };\n}\n\nexport async function removeBoxRecord(id: string, path: string = STATE_FILE): Promise<boolean> {\n const state = await readState(path);\n const before = state.boxes.length;\n const next: StateFile = {\n version: 1,\n boxes: state.boxes.filter((b) => b.id !== id),\n };\n if (next.boxes.length === before) return false;\n await writeState(next, path);\n return true;\n}\n\n/**\n * Resolve a user-supplied identifier against the state file. Matching\n * precedence mirrors `docker`'s container reference resolution:\n *\n * 1. exact id\n * 2. unique id prefix\n * 3. exact name\n * 4. exact container name\n *\n * Returns `'ambiguous'` if step 2 finds more than one match (steps 1, 3, 4\n * are exact-match so they cannot be ambiguous on their own).\n */\nexport function findBox(idOrName: string, state: StateFile): FindBoxResult {\n const q = idOrName.trim();\n if (q.length === 0) return { kind: 'none' };\n\n const exactId = state.boxes.find((b) => b.id === q);\n if (exactId) return { kind: 'ok', box: exactId };\n\n const prefixMatches = state.boxes.filter((b) => b.id.startsWith(q));\n if (prefixMatches.length === 1) return { kind: 'ok', box: prefixMatches[0]! };\n if (prefixMatches.length > 1) return { kind: 'ambiguous', matches: prefixMatches };\n\n const byName = state.boxes.find((b) => b.name === q);\n if (byName) return { kind: 'ok', box: byName };\n\n // For docker records `container` is the docker container name; for cloud\n // records it's `cloud:<sandboxId>` (post 7.2 — no more synthetic\n // agentbox-cloud-* prefix). Either form is a valid byContainer lookup\n // key for `findBox`.\n const byContainer = state.boxes.find((b) => b.container === q);\n if (byContainer) return { kind: 'ok', box: byContainer };\n\n return { kind: 'none' };\n}\n\n/**\n * Next monotonic 1-based index for the given project. Reads only `state.boxes`\n * — caller is responsible for persisting the assignment. Boxes without\n * `projectRoot` are ignored (legacy records); boxes in *other* projects are\n * also ignored. Indices are never recycled, so a destroyed #2 leaves a gap.\n */\nexport function allocateProjectIndex(state: StateFile, projectRoot: string): number {\n let max = 0;\n for (const b of state.boxes) {\n if (b.projectRoot !== projectRoot) continue;\n if (typeof b.projectIndex === 'number' && b.projectIndex > max) {\n max = b.projectIndex;\n }\n }\n return max + 1;\n}\n\n/**\n * Auto-pick when a command's `[box]` argument is omitted. Returns the unique\n * box for `projectRoot`, an `ambiguous` carrying all candidates so the CLI can\n * print a chooser, or `none`.\n */\nexport function autoPickProjectBox(state: StateFile, projectRoot: string): FindBoxResult {\n const matches = state.boxes.filter((b) => b.projectRoot === projectRoot);\n if (matches.length === 0) return { kind: 'none' };\n if (matches.length === 1) return { kind: 'ok', box: matches[0]! };\n return { kind: 'ambiguous', matches };\n}\n\n/**\n * Top-level resolver every CLI command goes through. Combines numeric-index\n * lookup with the legacy `findBox` matcher:\n *\n * - `ref === undefined` and `projectRoot` known → autoPickProjectBox.\n * - `ref` is a pure positive integer and `projectRoot` known → resolve as\n * project index. **Never** falls through to `findBox` on miss, so\n * `agentbox open 3` is reserved for the index and won't accidentally\n * match a hex id like `3abc…`.\n * - Otherwise → `findBox` (id → prefix → name → container).\n */\nexport function resolveBoxRef(\n ref: string | undefined,\n state: StateFile,\n projectRoot: string | undefined,\n): FindBoxResult {\n if (ref === undefined) {\n if (projectRoot === undefined) return { kind: 'none' };\n return autoPickProjectBox(state, projectRoot);\n }\n const trimmed = ref.trim();\n if (projectRoot !== undefined && /^[1-9][0-9]*$/.test(trimmed)) {\n const idx = Number.parseInt(trimmed, 10);\n const hit = state.boxes.find(\n (b) => b.projectRoot === projectRoot && b.projectIndex === idx,\n );\n return hit ? { kind: 'ok', box: hit } : { kind: 'none' };\n }\n return findBox(trimmed, state);\n}\n","import { execa } from 'execa';\nimport { readdir, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface DetectedGitRepo {\n kind: 'root' | 'nested';\n /** Absolute host path of the repo working tree (== `<workspace>` for root). */\n hostMainRepo: string;\n /** Path relative to the workspace where the repo lives. Empty string for root. */\n relPathFromWorkspace: string;\n}\n\n/**\n * Look for `.git` directories at the workspace root and at every 1st-level\n * subdirectory. Worktree-form `.git` files (regular file containing\n * `gitdir: …`) are intentionally skipped — turning an existing worktree into\n * another worktree gets weird, and the user case for it is rare.\n *\n * Pure host-side detection: it only tells callers where the repos are. Docker\n * boxes create the worktree inside the container against the bind-mounted\n * `.git/`; cloud boxes clone from a bundle. Either way this is the host probe.\n */\nexport async function detectGitRepos(workspace: string): Promise<DetectedGitRepo[]> {\n const out: DetectedGitRepo[] = [];\n if (await isGitDir(join(workspace, '.git'))) {\n out.push({ kind: 'root', hostMainRepo: workspace, relPathFromWorkspace: '' });\n }\n let entries: Array<{ name: string; isDirectory: () => boolean }>;\n try {\n entries = await readdir(workspace, { withFileTypes: true });\n } catch {\n return out;\n }\n for (const e of entries) {\n if (!e.isDirectory() || e.name.startsWith('.')) continue;\n const sub = join(workspace, e.name);\n if (await isGitDir(join(sub, '.git'))) {\n out.push({ kind: 'nested', hostMainRepo: sub, relPathFromWorkspace: e.name });\n }\n }\n return out;\n}\n\nasync function isGitDir(path: string): Promise<boolean> {\n try {\n const s = await stat(path);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Pick `<base>`, `<base>-2`, `<base>-3`, … until git reports no such branch\n * exists. Avoids collision when the user reruns `agentbox create -n same-name`\n * after destroying — the destroyed box's branch still lives in the host repo.\n */\nexport async function pickFreshBranch(hostMainRepo: string, base: string): Promise<string> {\n let candidate = base;\n let suffix = 2;\n while (await branchExists(hostMainRepo, candidate)) {\n candidate = `${base}-${String(suffix++)}`;\n if (suffix > 100) throw new GitWorktreeError(`could not find a free branch name near ${base}`);\n }\n return candidate;\n}\n\nasync function branchExists(hostMainRepo: string, name: string): Promise<boolean> {\n const result = await execa(\n 'git',\n ['-C', hostMainRepo, 'show-ref', '--verify', '--quiet', `refs/heads/${name}`],\n { reject: false },\n );\n return result.exitCode === 0;\n}\n\nexport class GitWorktreeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'GitWorktreeError';\n }\n}\n","/**\n * Cross-provider versioning primitives for `~/.agentbox/<provider>-prepared.json`.\n *\n * Each provider records what it has baked (docker image / hetzner snapshot /\n * daytona snapshot) under a per-provider JSON file with a shared `base.*`\n * substructure so the CLI can detect when the on-disk artifact is stale\n * relative to the current CLI's build context.\n *\n * The invalidation key is `base.contextSha256`: a deterministic SHA-256\n * over every file in the build context (Dockerfile + scripts + baked\n * config), keyed by the file's relative path. Two CLIs with the same\n * staged runtime tree produce the same hash; an edit to any baked asset\n * — even a one-byte tweak to `custom-system-CLAUDE.md` — flips it.\n *\n * Checkpoints embed the captured `contextSha256` so restoring an older\n * checkpoint can warn the user that the baked layers predate the current\n * base image.\n */\n\nimport { createHash } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, resolve as pathResolve } from 'node:path';\n\nexport type PreparedProviderKind = 'docker' | 'daytona' | 'hetzner';\n\n/**\n * The cross-provider record. `TImage` is the provider's opaque image\n * identifier: a string tag for docker/daytona, a numeric image id for\n * hetzner. The `TExtra` slot lets a provider attach provider-specific\n * fields (e.g. hetzner's `description` and `projects[]`) without forking\n * the whole shape.\n */\nexport interface PreparedBaseSnapshot<TImage = string, TExtra = unknown> {\n /** Schema version. Bumped when the on-disk shape changes incompatibly. */\n schema: number;\n base?: {\n /** Provider-opaque image identifier (docker tag | hetzner imageId | daytona snapshot name). */\n imageRef: TImage;\n /** Deterministic SHA-256 of the build context — the invalidation key. */\n contextSha256: string;\n /** Informational: CLI version that produced this artifact. */\n cliVersion: string;\n /** Informational: git short SHA injected at CLI build time (or 'dev'). */\n cliCommit?: string;\n /** ISO timestamp of bake completion. */\n createdAt: string;\n };\n /** Provider-specific extras (e.g. hetzner's per-project snapshot tier). */\n extras?: TExtra;\n}\n\nexport function preparedStatePathFor(provider: PreparedProviderKind): string {\n return pathResolve(homedir(), '.agentbox', `${provider}-prepared.json`);\n}\n\n/**\n * Read the prepared-state file for `provider`. Returns `null` when the file\n * is missing, malformed, or carries a schema this code doesn't recognise —\n * callers treat all three as \"rebuild needed\". Sync so it can run from\n * non-async setup paths (mirrors the hetzner helper it generalises).\n */\nexport function readPreparedStateRaw(provider: PreparedProviderKind): unknown {\n const path = preparedStatePathFor(provider);\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, 'utf8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Atomic write: write to `<path>.tmp` then rename. `mode: 0o600` because\n * the file is informational but lives alongside `secrets.env` — same dir,\n * same permissions hygiene.\n */\nexport function writePreparedStateRaw(provider: PreparedProviderKind, state: unknown): void {\n const path = preparedStatePathFor(provider);\n mkdirSync(dirname(path), { recursive: true });\n const body = JSON.stringify(state, null, 2) + '\\n';\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, body, { mode: 0o600 });\n renameSync(tmp, path);\n}\n\nexport async function sha256OfFile(path: string): Promise<string> {\n const buf = await readFile(path);\n return createHash('sha256').update(buf).digest('hex');\n}\n\nexport interface ContextFile {\n /**\n * Logical relative path. Used as the canonical key for hash determinism\n * — two stagings with identical contents but different absolute paths\n * must hash the same.\n */\n rel: string;\n /** Absolute path the file is read from. */\n abs: string;\n}\n\n/**\n * Deterministic hash over a set of context files. Entries are sorted by\n * `rel` then hashed as `<rel>\\0<sha256(file)>\\n` lines into a final SHA-256.\n *\n * - Sort order = determinism (the caller can pass files in any order).\n * - NUL separator = no collision between a `rel` ending in hex and the\n * following digest.\n * - Trailing newline per record = stable framing.\n *\n * Missing files raise — silently skipping would let a partial dev rebuild\n * stamp a hash that doesn't represent what's actually in the image.\n */\nexport async function computeContextSha256(files: ContextFile[]): Promise<string> {\n const sorted = [...files].sort((a, b) => (a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0));\n const outer = createHash('sha256');\n for (const f of sorted) {\n const inner = await sha256OfFile(f.abs);\n outer.update(`${f.rel}\\0${inner}\\n`);\n }\n return outer.digest('hex');\n}\n\n/** Short form for log lines — first 12 hex chars of a sha256. */\nexport function shortFingerprint(sha: string): string {\n return sha.slice(0, 12);\n}\n\n/**\n * CLI version stamps set by `apps/cli/src/index.ts` at startup via env vars\n * (the values themselves come from tsup's build-time `define`). Providers\n * record them onto prepared-state files and checkpoint manifests so a stale\n * artifact carries a human-readable hint about which CLI built it.\n *\n * Fallbacks cover the unit-test and unbundled-dev paths (the CLI never\n * loaded, env unset). `unknown` is a sentinel — never a real version.\n */\nexport interface CliStamp {\n cliVersion: string;\n cliCommit: string;\n}\n\nexport function readCliStamp(): CliStamp {\n return {\n cliVersion: process.env.AGENTBOX_CLI_VERSION ?? 'unknown',\n cliCommit: process.env.AGENTBOX_CLI_COMMIT ?? 'unknown',\n };\n}\n\n/**\n * Canonical map of files that go into the Docker base image build context\n * — every file `Dockerfile.box` COPYs, plus the Dockerfile itself. Two\n * layouts resolve the same logical entries:\n *\n * - staged: `<contextDir>/<staged>` (production CLI runtime + dev with `apps/cli/runtime/docker`)\n * - dev: `<sandboxDockerRoot>/<dev>` (workspace dev, no staged tree)\n *\n * Shared across providers because:\n * - sandbox-docker uses it to fingerprint its locally-built image.\n * - sandbox-daytona uses it to fingerprint the snapshot it bakes from the\n * same Dockerfile.box + the daytona-specific CLAUDE.md overlay.\n *\n * If you add a COPY line to `Dockerfile.box`, add the file here AND in\n * `apps/cli/scripts/stage-runtime.mjs` — failure to do so means the image\n * won't get re-built when that file changes.\n */\nexport const DOCKER_CONTEXT_FILE_MAP: Record<string, { staged: string; dev: string }> = {\n 'Dockerfile.box': { staged: 'Dockerfile.box', dev: 'Dockerfile.box' },\n 'ctl/bin.cjs': {\n staged: 'packages/ctl/dist/bin.cjs',\n dev: '../ctl/dist/bin.cjs',\n },\n 'share/agentbox-setup/SKILL.md': {\n staged: 'apps/cli/share/agentbox-setup/SKILL.md',\n dev: '../../apps/cli/share/agentbox-setup/SKILL.md',\n },\n 'scripts/agentbox-vnc-start': {\n staged: 'packages/sandbox-docker/scripts/agentbox-vnc-start',\n dev: 'scripts/agentbox-vnc-start',\n },\n 'scripts/agentbox-dockerd-start': {\n staged: 'packages/sandbox-docker/scripts/agentbox-dockerd-start',\n dev: 'scripts/agentbox-dockerd-start',\n },\n 'scripts/agentbox-checkpoint-cleanup': {\n staged: 'packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup',\n dev: 'scripts/agentbox-checkpoint-cleanup',\n },\n 'scripts/agentbox-open': {\n staged: 'packages/sandbox-docker/scripts/agentbox-open',\n dev: 'scripts/agentbox-open',\n },\n 'scripts/custom-system-CLAUDE.md': {\n staged: 'packages/sandbox-docker/scripts/custom-system-CLAUDE.md',\n dev: 'scripts/custom-system-CLAUDE.md',\n },\n 'scripts/claude-managed-settings.json': {\n staged: 'packages/sandbox-docker/scripts/claude-managed-settings.json',\n dev: 'scripts/claude-managed-settings.json',\n },\n 'scripts/agentbox-codex-hooks.json': {\n staged: 'packages/sandbox-docker/scripts/agentbox-codex-hooks.json',\n dev: 'scripts/agentbox-codex-hooks.json',\n },\n};\n\n/**\n * Resolve every entry in `fileMap` to an absolute path. Tries `<contextDir>/<staged>`\n * first; falls back to `<devRoot>/<dev>`. Returns `null` if any required file\n * is missing — callers treat that as \"can't fingerprint\" and skip the\n * cache-hit shortcut. Pure (no I/O beyond `existsSync`), so safe for use\n * from the provider's prepare path.\n */\nexport function resolveContextFilesFrom(\n fileMap: Record<string, { staged: string; dev: string }>,\n opts: { contextDir: string; devRoot: string },\n): ContextFile[] | null {\n const out: ContextFile[] = [];\n for (const [rel, paths] of Object.entries(fileMap)) {\n const candidates = [\n pathResolve(opts.contextDir, paths.staged),\n pathResolve(opts.devRoot, paths.dev),\n ];\n const hit = candidates.find((p) => existsSync(p));\n if (!hit) return null;\n out.push({ rel, abs: hit });\n }\n return out;\n}\n","/**\n * Docker provider's `~/.agentbox/docker-prepared.json` reader/writer + the\n * build-context fingerprint that drives base-image invalidation.\n *\n * The fingerprint is a SHA-256 over every file `docker build` would COPY\n * into the image — Dockerfile + scripts + baked config files. Two CLIs\n * with identical staged runtime trees produce the same hash; a one-byte\n * edit to any baked asset flips it, which is the signal `ensureImage()`\n * uses to rebuild instead of reusing the cached image.\n */\n\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n computeContextSha256,\n DOCKER_CONTEXT_FILE_MAP,\n readCliStamp,\n readPreparedStateRaw,\n resolveContextFilesFrom,\n writePreparedStateRaw,\n type ContextFile,\n type PreparedBaseSnapshot,\n} from '@agentbox/sandbox-core';\nimport { BUILD_CONTEXT_DIR, DEFAULT_BOX_IMAGE, DOCKERFILE_PATH } from './image.js';\n\nconst SCHEMA = 1 as const;\n\nexport type PreparedDockerState = PreparedBaseSnapshot<string, never>;\n\n/**\n * Resolve every fingerprint input to an absolute path. The canonical file\n * list lives in `@agentbox/sandbox-core` (DOCKER_CONTEXT_FILE_MAP) so the\n * daytona provider can hash the same inputs without depending on this\n * package. Two layouts are tried in order, mirroring `resolveDockerBuild()`\n * in `image.ts`:\n * 1. Build context dir (staged runtime / env override).\n * 2. Sandbox-docker package root (dev fallback).\n *\n * Returns `null` when *any* required file is missing — callers treat that\n * as \"can't fingerprint\" and skip the cache-hit shortcut (always rebuild).\n */\nexport function resolveContextFiles(opts: { contextDir?: string } = {}): ContextFile[] | null {\n const ctx = opts.contextDir ?? BUILD_CONTEXT_DIR;\n const here = dirname(fileURLToPath(import.meta.url));\n // sandbox-docker's package root = parent of src/ or parent of dist/.\n const packageRoot = resolve(here, '..');\n return resolveContextFilesFrom(DOCKER_CONTEXT_FILE_MAP, {\n contextDir: ctx,\n devRoot: packageRoot,\n });\n}\n\nexport interface ResolvedFingerprint {\n contextSha256: string;\n /** Files that fed the hash (in canonical sorted order). */\n files: ContextFile[];\n}\n\nexport async function computeDockerContextFingerprint(opts: {\n contextDir?: string;\n} = {}): Promise<ResolvedFingerprint | null> {\n const files = resolveContextFiles(opts);\n if (!files) return null;\n return { contextSha256: await computeContextSha256(files), files };\n}\n\nexport function readPreparedDockerState(): PreparedDockerState | null {\n const raw = readPreparedStateRaw('docker');\n if (raw === null || typeof raw !== 'object') return null;\n const parsed = raw as Partial<PreparedDockerState>;\n if (parsed.schema !== SCHEMA) return null;\n return { schema: SCHEMA, base: parsed.base };\n}\n\nexport function writePreparedDockerState(opts: {\n imageRef?: string;\n contextSha256: string;\n}): void {\n const stamp = readCliStamp();\n const state: PreparedDockerState = {\n schema: SCHEMA,\n base: {\n imageRef: opts.imageRef ?? DEFAULT_BOX_IMAGE,\n contextSha256: opts.contextSha256,\n cliVersion: stamp.cliVersion,\n cliCommit: stamp.cliCommit,\n createdAt: new Date().toISOString(),\n },\n };\n writePreparedStateRaw('docker', state);\n}\n\n/** Convenience for `ensureImage` and `prepare` — true when the stamped fingerprint matches. */\nexport function preparedMatches(state: PreparedDockerState | null, current: string): boolean {\n return state?.base?.contextSha256 === current;\n}\n\n/** Re-export so callers don't reach into image.ts just for the Dockerfile path. */\nexport { DOCKERFILE_PATH };\n","import { execa } from 'execa';\nimport { existsSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, resolve } from 'node:path';\n\nexport const DEFAULT_BOX_IMAGE = 'agentbox/box:dev';\n\nconst here = dirname(fileURLToPath(import.meta.url));\n\n// The Dockerfile's COPY lines reference monorepo-relative paths\n// (packages/ctl/dist/bin.cjs, apps/cli/share/..., packages/sandbox-docker/scripts/*),\n// so the build context must be a dir containing that tree.\n//\n// Resolution order:\n// 0. AGENTBOX_DOCKER_CONTEXT env override (dir holding Dockerfile.box).\n// 1. Staged context shipped with the bundled `agent-box` package: this\n// module is bundled into the CLI at <root>/dist, the stage step mirrors\n// the COPY tree at <root>/runtime/docker (sibling of dist/, uniform in\n// dev and when installed).\n// 2. Legacy monorepo: Dockerfile.box at the sandbox-docker package root,\n// build context = monorepo root.\nfunction resolveDockerBuild(): { dockerfile: string; context: string } {\n const override = process.env.AGENTBOX_DOCKER_CONTEXT;\n if (override && existsSync(resolve(override, 'Dockerfile.box'))) {\n return { dockerfile: resolve(override, 'Dockerfile.box'), context: override };\n }\n const staged = resolve(here, '..', 'runtime', 'docker');\n if (existsSync(resolve(staged, 'Dockerfile.box'))) {\n return { dockerfile: resolve(staged, 'Dockerfile.box'), context: staged };\n }\n // Legacy: src/ (or the unbundled package dist/) is one level under the\n // package root; the monorepo root is two more up.\n const packageRoot = resolve(here, '..');\n return {\n dockerfile: resolve(packageRoot, 'Dockerfile.box'),\n context: resolve(packageRoot, '..', '..'),\n };\n}\n\nconst { dockerfile: DOCKERFILE_PATH_RESOLVED, context: BUILD_CONTEXT_DIR_RESOLVED } =\n resolveDockerBuild();\nexport const DOCKERFILE_PATH = DOCKERFILE_PATH_RESOLVED;\nexport const BUILD_CONTEXT_DIR = BUILD_CONTEXT_DIR_RESOLVED;\n\nexport async function imageExists(ref: string): Promise<boolean> {\n const result = await execa('docker', ['image', 'inspect', ref], { reject: false });\n return result.exitCode === 0;\n}\n\nexport interface ImageInfo {\n /** Image ref (e.g. `agentbox/box:dev`). */\n ref: string;\n /** True when the engine has the image locally. */\n exists: boolean;\n /** Image size in bytes, when known. */\n sizeBytes?: number;\n /** ISO-8601 creation time, when known. */\n createdAt?: string;\n}\n\n/**\n * Read-only inspect of a Docker image. Used by `agentbox prepare` (no-args\n * status mode) to surface base-image state. Never throws — returns\n * `{ exists: false }` on any error so the status command works even when\n * the docker daemon is unreachable.\n */\nexport async function imageInfo(ref: string = DEFAULT_BOX_IMAGE): Promise<ImageInfo> {\n const result = await execa(\n 'docker',\n ['image', 'inspect', '--format', '{{.Size}}|{{.Created}}', ref],\n { reject: false },\n );\n if (result.exitCode !== 0) return { ref, exists: false };\n const [sizeStr, createdAt] = result.stdout.trim().split('|');\n const sizeBytes = sizeStr ? Number.parseInt(sizeStr, 10) : NaN;\n return {\n ref,\n exists: true,\n sizeBytes: Number.isFinite(sizeBytes) ? sizeBytes : undefined,\n createdAt: createdAt && createdAt.length > 0 ? createdAt : undefined,\n };\n}\n\nexport interface BuildImageOptions {\n ref?: string;\n dockerfile?: string;\n contextDir?: string;\n onProgress?: (line: string) => void;\n}\n\nexport async function buildImage(opts: BuildImageOptions = {}): Promise<string> {\n const ref = opts.ref ?? DEFAULT_BOX_IMAGE;\n const dockerfile = opts.dockerfile ?? DOCKERFILE_PATH;\n const contextDir = opts.contextDir ?? BUILD_CONTEXT_DIR;\n\n // Dogfood path: when building from inside an agentbox (docker-in-docker),\n // the default bridge network can't bind-mount /proc/<pid>/ns/net for the\n // build container, breaking any RUN that needs network (e.g. apt, curl).\n // Falling back to host networking sidesteps the missing capability.\n const args = ['build', '-t', ref, '-f', dockerfile, contextDir];\n if (process.env.AGENTBOX === '1') {\n args.splice(1, 0, '--network=host');\n }\n\n const subprocess = execa('docker', args, {\n stderr: 'pipe',\n stdout: 'pipe',\n });\n\n if (opts.onProgress) {\n const forward = (chunk: Buffer | string): void => {\n const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');\n for (const line of text.split(/\\r?\\n/)) {\n if (line.length > 0) opts.onProgress?.(line);\n }\n };\n subprocess.stdout?.on('data', forward);\n subprocess.stderr?.on('data', forward);\n }\n\n await subprocess;\n return ref;\n}\n\nexport interface EnsureImageOptions {\n onProgress?: (line: string) => void;\n /** Dockerfile path. Defaults to `Dockerfile.box` next to this package. */\n dockerfile?: string;\n /** Build context directory. Defaults to the monorepo root. */\n contextDir?: string;\n}\n\nexport async function ensureImage(\n ref: string = DEFAULT_BOX_IMAGE,\n opts: EnsureImageOptions = {},\n): Promise<{ ref: string; built: boolean; reason?: string }> {\n // Lazy import: prepared-state imports back into image.ts for the default\n // DOCKERFILE_PATH/BUILD_CONTEXT_DIR constants, so loading it at top-level\n // would create a circular ESM init order.\n const {\n computeDockerContextFingerprint,\n readPreparedDockerState,\n writePreparedDockerState,\n preparedMatches,\n } = await import('./prepared-state.js');\n\n const fingerprint = await computeDockerContextFingerprint({\n contextDir: opts.contextDir,\n });\n const prepared = readPreparedDockerState();\n const exists = await imageExists(ref);\n\n let reason: string | undefined;\n if (!exists) {\n reason = `image ${ref} not present`;\n } else if (!fingerprint) {\n // Couldn't enumerate the context (partial dev rebuild?). Don't rebuild\n // unconditionally — that would surprise users mid-iteration. Trust the\n // image-exists check and leave the prepared file untouched.\n return { ref, built: false, reason: 'image present (fingerprint skipped)' };\n } else if (!prepared) {\n reason = 'no docker-prepared.json on disk';\n } else if (!preparedMatches(prepared, fingerprint.contextSha256)) {\n reason =\n `build context changed (was ${prepared.base?.contextSha256?.slice(0, 12) ?? '<none>'}, ` +\n `now ${fingerprint.contextSha256.slice(0, 12)})`;\n }\n\n if (!reason) {\n return { ref, built: false, reason: 'image up to date' };\n }\n\n opts.onProgress?.(`[image] rebuilding ${ref}: ${reason}`);\n await buildImage({\n ref,\n dockerfile: opts.dockerfile,\n contextDir: opts.contextDir,\n onProgress: opts.onProgress,\n });\n if (fingerprint) {\n writePreparedDockerState({ imageRef: ref, contextSha256: fingerprint.contextSha256 });\n }\n return { ref, built: true, reason };\n}\n\n"],"mappings":"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","names":["join","readFile","homedir","dirname","dirname","resolve","fileURLToPath","execa","existsSync","dirname","computeDockerContextFingerprint","readPreparedDockerState","writePreparedDockerState","preparedMatches","here","fileURLToPath","resolve"]}
|