@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.
Files changed (75) hide show
  1. package/dist/_cloud-attach-T727ZPRV.js +13 -0
  2. package/dist/chunk-67N47KUS.js +1640 -0
  3. package/dist/chunk-67N47KUS.js.map +1 -0
  4. package/dist/chunk-6OZDFNBF.js +8114 -0
  5. package/dist/chunk-6OZDFNBF.js.map +1 -0
  6. package/dist/chunk-BGK32PZE.js +455 -0
  7. package/dist/chunk-BGK32PZE.js.map +1 -0
  8. package/dist/chunk-FODMEHD3.js +1200 -0
  9. package/dist/chunk-FODMEHD3.js.map +1 -0
  10. package/dist/chunk-G3H2L3O2.js +288 -0
  11. package/dist/chunk-G3H2L3O2.js.map +1 -0
  12. package/dist/chunk-I24B6AXR.js +600 -0
  13. package/dist/chunk-I24B6AXR.js.map +1 -0
  14. package/dist/chunk-LEV3KICD.js +738 -0
  15. package/dist/chunk-LEV3KICD.js.map +1 -0
  16. package/dist/cloud-poller-SUNA6ZQC-2RG5WPRN.js +10 -0
  17. package/dist/dist-L4LCG5SJ.js +293 -0
  18. package/dist/dist-L4LCG5SJ.js.map +1 -0
  19. package/dist/dist-LOZBWMBF.js +447 -0
  20. package/dist/dist-ZODPD2I6.js +1407 -0
  21. package/dist/dist-ZODPD2I6.js.map +1 -0
  22. package/dist/index.js +7281 -2134
  23. package/dist/index.js.map +1 -1
  24. package/dist/prepared-state-CL4CWXQA-ME4HSKDE.js +18 -0
  25. package/package.json +8 -3
  26. package/runtime/daytona/custom-system-CLAUDE.md +39 -0
  27. package/runtime/docker/Dockerfile.box +120 -14
  28. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +15 -8
  29. package/runtime/docker/packages/ctl/dist/bin.cjs +11310 -816
  30. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json +68 -0
  31. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-open +9 -9
  32. package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +62 -1
  33. package/runtime/docker/packages/sandbox-docker/scripts/custom-system-CLAUDE.md +15 -4
  34. package/runtime/docker/packages/sandbox-docker/scripts/gh-shim +263 -0
  35. package/runtime/docker/packages/sandbox-docker/scripts/git-shim +131 -0
  36. package/runtime/docker/packages/sandbox-docker/scripts/opencode-agentbox-plugin.js +76 -0
  37. package/runtime/hetzner/agentbox-checkpoint-cleanup +52 -0
  38. package/runtime/hetzner/agentbox-codex-hooks.json +68 -0
  39. package/runtime/hetzner/agentbox-dockerd-start +132 -0
  40. package/runtime/hetzner/agentbox-open +28 -0
  41. package/runtime/hetzner/agentbox-setup-skill.md +196 -0
  42. package/runtime/hetzner/agentbox-vnc-start +77 -0
  43. package/runtime/hetzner/claude-managed-settings.json +115 -0
  44. package/runtime/hetzner/ctl.cjs +23397 -0
  45. package/runtime/hetzner/custom-system-CLAUDE.md +39 -0
  46. package/runtime/hetzner/gh-shim +263 -0
  47. package/runtime/hetzner/git-shim +131 -0
  48. package/runtime/hetzner/opencode-agentbox-plugin.js +76 -0
  49. package/runtime/hetzner/scripts/install-box.sh +374 -0
  50. package/runtime/relay/bin.cjs +10017 -817
  51. package/share/agentbox-setup/SKILL.md +15 -8
  52. package/share/host-skills/agentbox/SKILL.md +29 -0
  53. package/share/host-skills/agentbox-info/SKILL.md +211 -0
  54. package/share/host-skills/codex/agentbox.md +35 -0
  55. package/share/host-skills/opencode/agentbox.md +26 -0
  56. package/dist/chunk-BBZMA2K6.js +0 -238
  57. package/dist/chunk-BBZMA2K6.js.map +0 -1
  58. package/dist/chunk-HHMWQNLF.js +0 -1709
  59. package/dist/chunk-HHMWQNLF.js.map +0 -1
  60. package/dist/chunk-HPZMD5DE.js +0 -106
  61. package/dist/chunk-HPZMD5DE.js.map +0 -1
  62. package/dist/chunk-HTTKML3C.js +0 -2655
  63. package/dist/chunk-HTTKML3C.js.map +0 -1
  64. package/dist/chunk-KJNZP6I3.js +0 -586
  65. package/dist/chunk-KJNZP6I3.js.map +0 -1
  66. package/dist/chunk-M7I247BK.js +0 -525
  67. package/dist/chunk-M7I247BK.js.map +0 -1
  68. package/dist/create-6PWXI6HO-OWAMHBAK.js +0 -15
  69. package/dist/lifecycle-EMXR46DI-DUVBXNTV.js +0 -38
  70. package/dist/state-KD7M46ZP-KHFTHFUS.js +0 -26
  71. package/dist/stats-SZXOJE3D-N7OODCHW.js +0 -19
  72. /package/dist/{create-6PWXI6HO-OWAMHBAK.js.map → _cloud-attach-T727ZPRV.js.map} +0 -0
  73. /package/dist/{lifecycle-EMXR46DI-DUVBXNTV.js.map → cloud-poller-SUNA6ZQC-2RG5WPRN.js.map} +0 -0
  74. /package/dist/{state-KD7M46ZP-KHFTHFUS.js.map → dist-LOZBWMBF.js.map} +0 -0
  75. /package/dist/{stats-SZXOJE3D-N7OODCHW.js.map → prepared-state-CL4CWXQA-ME4HSKDE.js.map} +0 -0
@@ -1,586 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- ConfigError,
4
- VNC_CONTAINER_PORT,
5
- WEB_CONTAINER_PORT,
6
- bindWorktrees,
7
- buildClaudeMounts,
8
- buildIdeMounts,
9
- chownGitBindParents,
10
- collectRepoCarryOver,
11
- createSnapshot,
12
- cursorServerVolumeName,
13
- detectGitRepos,
14
- dockerVolumeName,
15
- ensureClaudeVolume,
16
- ensureHomeOwnedByVscode,
17
- ensureIdeVolumes,
18
- ensureRelay,
19
- generateRelayToken,
20
- generateVncPassword,
21
- gitWorktreePathFor,
22
- launchCtlDaemon,
23
- launchDockerdDaemon,
24
- launchVncDaemon,
25
- loadConfig,
26
- pickFreshBranch,
27
- registerBoxWithRelay,
28
- rehydrateRelayRegistry,
29
- repairIdeOwnership,
30
- resolveClaudeVolume,
31
- seedSetupSkillIntoVolume,
32
- seedWorkspace,
33
- seedWorkspaceFromDir,
34
- snapshotPathFor,
35
- vscodeServerVolumeName
36
- } from "./chunk-HTTKML3C.js";
37
- import {
38
- STATE_DIR,
39
- allocateProjectIndex,
40
- readState,
41
- recordBox
42
- } from "./chunk-HPZMD5DE.js";
43
- import {
44
- CONTAINER_EXPORT_MERGED,
45
- DEFAULT_BOX_IMAGE,
46
- DEFAULT_ENV_PATTERNS,
47
- boxRunDirFor,
48
- containerExists,
49
- copyHostEnvFilesToBox,
50
- copyHostFilesToBox,
51
- dockerInfo,
52
- dockerStorageDriver,
53
- ensureImage,
54
- ensureVolume,
55
- publishedHostPort,
56
- resolveCheckpoint,
57
- runBox
58
- } from "./chunk-HHMWQNLF.js";
59
-
60
- // ../../packages/sandbox-docker/dist/chunk-A4F6SVSK.js
61
- import { randomBytes } from "crypto";
62
- import { mkdir as mkdir2, stat } from "fs/promises";
63
- import { homedir } from "os";
64
- import { basename, join as join2, resolve } from "path";
65
- import { execa as execa3 } from "execa";
66
- import { chmod, mkdir, readFile } from "fs/promises";
67
- import { join } from "path";
68
- import { execa } from "execa";
69
- import { execa as execa2 } from "execa";
70
- var CREDENTIALS_BACKUP_FILE = join(STATE_DIR, "claude-credentials.json");
71
- async function hostBackupHasCredentials(path = CREDENTIALS_BACKUP_FILE) {
72
- try {
73
- const parsed = JSON.parse(await readFile(path, "utf8"));
74
- const rt = parsed?.claudeAiOauth?.refreshToken;
75
- return typeof rt === "string" && rt.length > 0;
76
- } catch {
77
- return false;
78
- }
79
- }
80
- function parseSyncResult(stdout) {
81
- const volumeHasCredentials = /\bVOLREAL=yes\b/.test(stdout);
82
- if (/\bEXTRACTED=yes\b/.test(stdout)) return { direction: "extracted", volumeHasCredentials };
83
- if (/\bSEEDED=yes\b/.test(stdout)) return { direction: "seeded", volumeHasCredentials };
84
- return { direction: "noop", volumeHasCredentials };
85
- }
86
- var SYNC_SCRIPT = `
87
- EXTRACTED=no
88
- SEEDED=no
89
- VOL=/dst/.credentials.json
90
- HOST=/host-state/claude-credentials.json
91
- if [ -f "$VOL" ] && jq -e '(.claudeAiOauth.refreshToken // "") | length > 0' "$VOL" >/dev/null 2>&1; then VOL_REAL=yes; else VOL_REAL=no; fi
92
- if [ -f "$HOST" ] && jq -e '(.claudeAiOauth.refreshToken // "") | length > 0' "$HOST" >/dev/null 2>&1; then HOST_REAL=yes; else HOST_REAL=no; fi
93
- if [ "$VOL_REAL" = yes ] && [ "$ISOLATE" != yes ]; then
94
- cp -a "$VOL" "$HOST" && chmod 600 "$HOST" && EXTRACTED=yes
95
- elif [ "$VOL_REAL" = no ] && [ "$HOST_REAL" = yes ]; then
96
- cp -a "$HOST" "$VOL" && chown 1000:1000 "$VOL" && chmod 600 "$VOL" && SEEDED=yes && VOL_REAL=yes
97
- fi
98
- echo "EXTRACTED=$EXTRACTED SEEDED=$SEEDED VOLREAL=$VOL_REAL"
99
- `;
100
- async function syncClaudeCredentials(spec, opts) {
101
- try {
102
- await mkdir(STATE_DIR, { recursive: true });
103
- const { stdout } = await execa("docker", [
104
- "run",
105
- "--rm",
106
- "--user",
107
- "0",
108
- "-v",
109
- `${spec.volume}:/dst`,
110
- "-v",
111
- `${STATE_DIR}:/host-state`,
112
- "-e",
113
- `ISOLATE=${opts.isolate ? "yes" : "no"}`,
114
- opts.image,
115
- "sh",
116
- "-c",
117
- SYNC_SCRIPT
118
- ]);
119
- const result = parseSyncResult(stdout);
120
- if (result.direction === "extracted") {
121
- await chmod(CREDENTIALS_BACKUP_FILE, 384).catch(() => {
122
- });
123
- }
124
- return result;
125
- } catch {
126
- return { direction: "noop", volumeHasCredentials: false };
127
- }
128
- }
129
- async function writeBoxEnvFile(container, env) {
130
- const body = formatBoxEnvBody(env);
131
- const result = await execa2(
132
- "docker",
133
- ["exec", "--user", "root", "-i", container, "sh", "-c", "umask 022 && cat > /etc/agentbox/box.env"],
134
- { input: body, reject: false }
135
- );
136
- if (result.exitCode !== 0) {
137
- return {
138
- ok: false,
139
- reason: `docker exec failed (exit ${String(result.exitCode)}): ${(result.stderr ?? "").toString().slice(0, 400)}`
140
- };
141
- }
142
- return { ok: true };
143
- }
144
- function formatBoxEnvBody(env) {
145
- const lines = [];
146
- for (const [k, v] of Object.entries(env)) {
147
- lines.push(`${k}=${shellSingleQuote(v)}`);
148
- }
149
- return lines.join("\n") + "\n";
150
- }
151
- function shellSingleQuote(s) {
152
- return `'${s.replace(/'/g, `'\\''`)}'`;
153
- }
154
- function persistableLimits(lim) {
155
- if (!lim) return void 0;
156
- const out = {};
157
- if (lim.memoryBytes && lim.memoryBytes > 0) out.memoryBytes = Math.floor(lim.memoryBytes);
158
- if (lim.cpus && lim.cpus > 0) out.cpus = lim.cpus;
159
- if (lim.pidsLimit && lim.pidsLimit > 0) out.pidsLimit = Math.floor(lim.pidsLimit);
160
- if (lim.disk) out.disk = lim.disk;
161
- return Object.keys(out).length > 0 ? out : void 0;
162
- }
163
- function generateBoxId() {
164
- return randomBytes(4).toString("hex");
165
- }
166
- function sanitizeBasename(workspacePath) {
167
- const raw = basename(resolve(workspacePath));
168
- return raw.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-._]+|[-._]+$/g, "").slice(0, 30).replace(/[-._]+$/, "");
169
- }
170
- function defaultBoxName(workspacePath, id) {
171
- const base = sanitizeBasename(workspacePath);
172
- return base.length > 0 ? `${base}-${id}` : id;
173
- }
174
- async function pathExists(p) {
175
- try {
176
- await stat(p);
177
- return true;
178
- } catch {
179
- return false;
180
- }
181
- }
182
- async function buildIdentityMounts() {
183
- const home = homedir();
184
- const candidates = [
185
- { src: join2(home, ".codex"), dst: "/home/vscode/.codex", readOnly: false },
186
- { src: join2(home, ".gitconfig"), dst: "/home/vscode/.gitconfig", readOnly: true }
187
- ];
188
- const out = [];
189
- for (const c of candidates) {
190
- if (await pathExists(c.src)) {
191
- out.push(`${c.src}:${c.dst}${c.readOnly ? ":ro" : ""}`);
192
- }
193
- }
194
- return out;
195
- }
196
- async function createBox(opts) {
197
- const log = opts.onLog ?? (() => {
198
- });
199
- const workspace = resolve(opts.workspacePath);
200
- if (!await pathExists(workspace)) {
201
- throw new Error(`workspace does not exist: ${workspace}`);
202
- }
203
- const cfgPath = join2(workspace, "agentbox.yaml");
204
- if (await pathExists(cfgPath)) {
205
- try {
206
- const cfg = await loadConfig(cfgPath);
207
- log(`agentbox.yaml validated (${String(cfg.services.length)} service(s))`);
208
- } catch (err) {
209
- if (err instanceof ConfigError) {
210
- throw new Error(`agentbox.yaml validation failed:
211
- ${err.message}`);
212
- }
213
- throw err;
214
- }
215
- }
216
- await dockerInfo();
217
- log("docker daemon reachable");
218
- let checkpointImage;
219
- let checkpointSource;
220
- let restoredWorktrees;
221
- if (opts.checkpointRef) {
222
- const projectRootForCkpt = opts.projectRoot ?? workspace;
223
- const head = await resolveCheckpoint(projectRootForCkpt, opts.checkpointRef);
224
- if (!head) {
225
- throw new Error(`checkpoint not found: ${opts.checkpointRef}`);
226
- }
227
- checkpointImage = head.manifest.image;
228
- const chain = [head.name, ...head.manifest.parents];
229
- checkpointSource = { ref: opts.checkpointRef, type: head.manifest.type, chain };
230
- restoredWorktrees = head.manifest.worktrees;
231
- log(
232
- `starting from checkpoint ${opts.checkpointRef} (${head.manifest.type}, ${String(chain.length)} layer(s), image ${head.manifest.image})`
233
- );
234
- }
235
- const imageRef = checkpointImage ?? opts.image ?? DEFAULT_BOX_IMAGE;
236
- const ensureRef = checkpointImage ? opts.image ?? DEFAULT_BOX_IMAGE : imageRef;
237
- const { built } = await ensureImage(ensureRef, {
238
- onProgress: (line) => log(`[image] ${line}`)
239
- });
240
- log(built ? `built image ${ensureRef}` : `using cached image ${imageRef}`);
241
- let relayUp = false;
242
- try {
243
- await ensureRelay({ onLog: log });
244
- const existing = await readState();
245
- await rehydrateRelayRegistry(existing.boxes);
246
- relayUp = true;
247
- } catch (err) {
248
- log(`relay unavailable: ${err instanceof Error ? err.message : String(err)}`);
249
- }
250
- const id = generateBoxId();
251
- const name = opts.name ?? defaultBoxName(workspace, id);
252
- const containerName = `agentbox-${name}`;
253
- const createdAt = (/* @__PURE__ */ new Date()).toISOString();
254
- if (await containerExists(containerName)) {
255
- throw new Error(`container ${containerName} already exists; remove it first`);
256
- }
257
- let projectIndex;
258
- if (opts.projectRoot) {
259
- projectIndex = allocateProjectIndex(await readState(), opts.projectRoot);
260
- }
261
- const repoCarryOvers = [];
262
- const gitWorktreeRecords = [];
263
- if (checkpointImage && restoredWorktrees && restoredWorktrees.length > 0) {
264
- gitWorktreeRecords.push(...restoredWorktrees);
265
- }
266
- if (!checkpointImage) {
267
- const repos = await detectGitRepos(workspace);
268
- if (repos.length > 0) {
269
- log(
270
- `detected ${String(repos.length)} git repo(s): ` + repos.map((r) => `${r.kind}${r.relPathFromWorkspace ? "@" + r.relPathFromWorkspace : ""}`).join(", ")
271
- );
272
- }
273
- for (const r of repos) {
274
- const branchBase = r.kind === "root" ? `agentbox/${name}` : `agentbox/${name}--${r.relPathFromWorkspace.replace(/[^A-Za-z0-9._-]+/g, "_")}`;
275
- const branch = await pickFreshBranch(r.hostMainRepo, branchBase);
276
- const containerPath = r.kind === "root" ? "/workspace" : `/workspace/${r.relPathFromWorkspace}`;
277
- const gitWorktreePath = gitWorktreePathFor(branch);
278
- const carry = await collectRepoCarryOver(r, branch, containerPath, gitWorktreePath);
279
- repoCarryOvers.push(carry);
280
- gitWorktreeRecords.push({
281
- kind: r.kind,
282
- hostMainRepo: r.hostMainRepo,
283
- containerPath,
284
- gitWorktreePath,
285
- branch,
286
- relPathFromWorkspace: r.relPathFromWorkspace
287
- });
288
- }
289
- }
290
- let snapshotDir = null;
291
- const snapshotIsUseful = !checkpointImage && repoCarryOvers.length === 0;
292
- if (opts.useSnapshot && snapshotIsUseful) {
293
- snapshotDir = snapshotPathFor({ id, name, projectIndex });
294
- log(`cloning workspace to ${snapshotDir} (APFS clone where available)`);
295
- const snap = await createSnapshot({ source: workspace, destination: snapshotDir });
296
- log(`pruned ${snap.prunedPaths.length} platform-dependent dirs from snapshot`);
297
- } else if (opts.useSnapshot && !checkpointImage) {
298
- log("skipping --host-snapshot: git worktree path reads content from .git, not from a workspace clone");
299
- }
300
- await ensureIdeVolumes(id);
301
- const dockerCacheShared = opts.docker?.sharedCache === true;
302
- const dockerVolume = dockerVolumeName(id, dockerCacheShared);
303
- await ensureVolume(dockerVolume);
304
- log(`prepared volumes ${vscodeServerVolumeName(id)}, ${cursorServerVolumeName(id)}, ${dockerVolume}`);
305
- const ide = buildIdeMounts(id);
306
- const claudeSpec = resolveClaudeVolume({
307
- isolate: opts.claudeConfig?.isolate ?? false,
308
- boxId: id
309
- });
310
- const claudeEnsured = await ensureClaudeVolume(claudeSpec, {
311
- syncFromHost: true,
312
- image: ensureRef,
313
- hostWorkspace: workspace
314
- });
315
- if (claudeEnsured.synced) {
316
- log(`synced ${claudeSpec.volume} from ~/.claude`);
317
- if ((claudeEnsured.filteredHookCount ?? 0) > 0) {
318
- log(
319
- `filtered ${String(claudeEnsured.filteredHookCount)} host-path hook(s) (paths under ~/)`
320
- );
321
- }
322
- if (claudeEnsured.installMethodFixed) {
323
- log("set installMethod=native in synced .claude.json (matches box native install)");
324
- }
325
- if (claudeEnsured.aliasedProjectKey) {
326
- log(`aliased project state for ${workspace} -> /workspace in synced .claude.json`);
327
- }
328
- if (claudeEnsured.workspaceTrusted) {
329
- log("pre-trusted /workspace in synced .claude.json (skips the trust dialog)");
330
- }
331
- } else if (claudeEnsured.created) {
332
- log(`created empty volume ${claudeSpec.volume} (no host ~/.claude to sync)`);
333
- } else {
334
- log(`reusing volume ${claudeSpec.volume} (no host ~/.claude to sync)`);
335
- }
336
- const seeded = await seedSetupSkillIntoVolume(claudeSpec.volume, ensureRef);
337
- if (seeded.seeded) log(`refreshed /agentbox-setup skill into ${claudeSpec.volume}`);
338
- const credSync = await syncClaudeCredentials(claudeSpec, {
339
- image: ensureRef,
340
- isolate: opts.claudeConfig?.isolate ?? false
341
- });
342
- if (credSync.direction === "extracted") {
343
- log("extracted box claude credentials to host backup");
344
- } else if (credSync.direction === "seeded") {
345
- log(`seeded claude credentials into ${claudeSpec.volume} from host backup`);
346
- }
347
- const claudeMounts = buildClaudeMounts(claudeSpec, process.env);
348
- const boxDir = boxRunDirFor({ id, name, projectIndex });
349
- const socketDir = join2(boxDir, "run");
350
- const socketPath = join2(socketDir, "ctl.sock");
351
- const mergedExportDir = join2(boxDir, "workspace");
352
- await mkdir2(socketDir, { recursive: true });
353
- await mkdir2(mergedExportDir, { recursive: true });
354
- const extraVolumes = await buildIdentityMounts();
355
- extraVolumes.push(...claudeMounts.extraVolumes);
356
- extraVolumes.push(...ide.extraVolumes);
357
- extraVolumes.push(`${socketDir}:/run/agentbox`);
358
- extraVolumes.push(`${mergedExportDir}:${CONTAINER_EXPORT_MERGED}`);
359
- extraVolumes.push(`${dockerVolume}:/var/lib/docker`);
360
- for (const w of gitWorktreeRecords) {
361
- extraVolumes.push(`${w.hostMainRepo}/.git:${w.hostMainRepo}/.git`);
362
- }
363
- for (const v of extraVolumes) log(`mounting agent dir: ${v}`);
364
- const relayToken = generateRelayToken();
365
- if (relayUp) {
366
- try {
367
- await registerBoxWithRelay({
368
- boxId: id,
369
- token: relayToken,
370
- name,
371
- containerName,
372
- createdAt,
373
- projectIndex,
374
- worktrees: gitWorktreeRecords
375
- });
376
- log(`registered box token with relay`);
377
- } catch (err) {
378
- log(`relay register failed: ${err instanceof Error ? err.message : String(err)}`);
379
- relayUp = false;
380
- }
381
- }
382
- const relayEnv = relayUp ? {
383
- // host.docker.internal resolves to the host (where the relay node
384
- // process is running). The matching `--add-host` is set in runBox.
385
- AGENTBOX_RELAY_URL: `http://host.docker.internal:8787`,
386
- AGENTBOX_RELAY_TOKEN: relayToken
387
- } : {};
388
- const vncEnabled = opts.vnc?.enabled !== false;
389
- const vncPassword = vncEnabled ? generateVncPassword() : void 0;
390
- const vncEnv = vncEnabled && vncPassword ? { AGENTBOX_VNC_PASSWORD: vncPassword } : {};
391
- const vncPortMappings = vncEnabled ? [{ hostPort: 0, containerPort: VNC_CONTAINER_PORT, hostIp: "127.0.0.1" }] : [];
392
- const webPortMappings = [
393
- { hostPort: 0, containerPort: WEB_CONTAINER_PORT, hostIp: "127.0.0.1" }
394
- ];
395
- const agentboxEnv = {
396
- AGENTBOX: "1",
397
- AGENTBOX_BOX_NAME: name,
398
- AGENTBOX_HOST_WORKSPACE: workspace,
399
- ...opts.projectRoot ? { AGENTBOX_PROJECT_ROOT: opts.projectRoot } : {},
400
- ...projectIndex !== void 0 ? { AGENTBOX_PROJECT_INDEX: String(projectIndex) } : {}
401
- };
402
- const boxEnvForFile = {
403
- AGENTBOX_BOX_ID: id,
404
- ...agentboxEnv
405
- };
406
- const appliedLimits = opts.limits;
407
- let effectiveLimits = appliedLimits;
408
- if (appliedLimits?.disk) {
409
- const driver = await dockerStorageDriver();
410
- if (!/^(devicemapper|btrfs|zfs|windowsfilter)$/.test(driver)) {
411
- log(
412
- `warning: --disk/box.disk is a no-op on this engine (storage-driver=${driver || "unknown"}); ignoring`
413
- );
414
- effectiveLimits = { ...appliedLimits, disk: null };
415
- }
416
- }
417
- await runBox({
418
- name: containerName,
419
- image: imageRef,
420
- extraVolumes,
421
- limits: effectiveLimits,
422
- portMappings: [...vncPortMappings, ...webPortMappings],
423
- env: {
424
- AGENTBOX_BOX_ID: id,
425
- ...agentboxEnv,
426
- ...claudeMounts.env,
427
- ...relayEnv,
428
- ...vncEnv,
429
- ...opts.claudeEnv ?? {}
430
- }
431
- });
432
- log(`container ${containerName} started`);
433
- if (gitWorktreeRecords.length > 0) {
434
- await chownGitBindParents({
435
- container: containerName,
436
- hostMainRepos: gitWorktreeRecords.map((w) => w.hostMainRepo),
437
- onLog: log
438
- });
439
- }
440
- const boxEnv = await writeBoxEnvFile(containerName, boxEnvForFile);
441
- if (boxEnv.ok) log("wrote /etc/agentbox/box.env");
442
- else log(`writing /etc/agentbox/box.env failed: ${boxEnv.reason}`);
443
- await ensureHomeOwnedByVscode(containerName);
444
- if (!checkpointImage) {
445
- if (repoCarryOvers.length > 0) {
446
- try {
447
- await seedWorkspace({ container: containerName, repos: repoCarryOvers, onLog: log });
448
- log("seeded /workspace from in-container git worktree(s)");
449
- } catch (err) {
450
- log(
451
- `seedWorkspace failed; leaving ${containerName} running so you can inspect it`
452
- );
453
- throw err;
454
- }
455
- } else {
456
- const source = snapshotDir ?? workspace;
457
- await seedWorkspaceFromDir({ container: containerName, hostSource: source, onLog: log });
458
- }
459
- } else if (restoredWorktrees && restoredWorktrees.length > 0) {
460
- await bindWorktrees(
461
- containerName,
462
- restoredWorktrees.map((w) => ({
463
- kind: w.kind,
464
- containerPath: w.containerPath,
465
- gitWorktreePath: w.gitWorktreePath
466
- })),
467
- log
468
- );
469
- log("re-bound /workspace from checkpoint image");
470
- } else {
471
- log("using /workspace from checkpoint image (no worktrees recorded; no rebind)");
472
- }
473
- await repairIdeOwnership(containerName);
474
- log(".vscode-server + .cursor-server ownership verified");
475
- const ctl = await launchCtlDaemon(containerName, socketPath);
476
- if (ctl.up) log("agentbox-ctl daemon up");
477
- else log(`agentbox-ctl daemon did not become reachable: ${ctl.reason}`);
478
- const dockerd = await launchDockerdDaemon(containerName);
479
- if (dockerd.up) {
480
- log(`dockerd up (data root=${dockerVolume})`);
481
- } else {
482
- log(`dockerd did not become ready: ${dockerd.reason}`);
483
- }
484
- if (opts.withPlaywright) {
485
- log("installing @playwright/cli@latest (--with-playwright)");
486
- const result = await execa3(
487
- "docker",
488
- [
489
- "exec",
490
- "--user",
491
- "root",
492
- containerName,
493
- "bash",
494
- "-lc",
495
- "npm install -g @playwright/cli@latest 2>&1"
496
- ],
497
- { reject: false }
498
- );
499
- for (const line of (result.stdout ?? "").split("\n")) {
500
- if (line.trim().length > 0) log(`[playwright] ${line}`);
501
- }
502
- if (result.exitCode !== 0) {
503
- throw new Error(
504
- `failed to install @playwright/cli (exit ${String(result.exitCode)}): ${(result.stderr ?? "").toString().slice(0, 400)}`
505
- );
506
- }
507
- log("@playwright/cli installed");
508
- }
509
- if (opts.withEnv) {
510
- log("copying host env/config files into /workspace (--with-env)");
511
- const { copied } = await copyHostEnvFilesToBox({
512
- container: containerName,
513
- workspaceDir: workspace,
514
- patterns: DEFAULT_ENV_PATTERNS,
515
- onLog: log
516
- });
517
- log(copied > 0 ? `copied ${String(copied)} env/config file(s)` : "no env/config files found");
518
- }
519
- if (opts.envFilesToImport && opts.envFilesToImport.length > 0) {
520
- log(`copying ${String(opts.envFilesToImport.length)} selected env/config file(s) into /workspace`);
521
- const { copied } = await copyHostFilesToBox({
522
- container: containerName,
523
- workspaceDir: workspace,
524
- files: opts.envFilesToImport,
525
- onLog: log
526
- });
527
- if (copied !== opts.envFilesToImport.length) {
528
- log(`copied ${String(copied)}/${String(opts.envFilesToImport.length)} selected env/config file(s)`);
529
- }
530
- }
531
- let vncHostPort = null;
532
- if (vncEnabled) {
533
- const vnc = await launchVncDaemon(containerName);
534
- if (vnc.up) log("vnc stack up (Xvnc + websockify + noVNC)");
535
- else log(`vnc stack did not become reachable: ${vnc.reason}`);
536
- vncHostPort = await publishedHostPort(containerName, VNC_CONTAINER_PORT);
537
- if (vncHostPort) log(`vnc web on host 127.0.0.1:${String(vncHostPort)}`);
538
- }
539
- const webHostPort = await publishedHostPort(containerName, WEB_CONTAINER_PORT);
540
- if (webHostPort) {
541
- log(
542
- `web port reserved on host 127.0.0.1:${String(webHostPort)} (forwards to the web service once agentbox.yaml sets a service expose:)`
543
- );
544
- }
545
- const record = {
546
- id,
547
- name,
548
- container: containerName,
549
- image: imageRef,
550
- workspacePath: workspace,
551
- snapshotDir,
552
- socketPath,
553
- claudeConfigVolume: claudeSpec.volume,
554
- vscodeServerVolume: vscodeServerVolumeName(id),
555
- cursorServerVolume: cursorServerVolumeName(id),
556
- relayToken: relayUp ? relayToken : void 0,
557
- gitWorktrees: gitWorktreeRecords.length > 0 ? gitWorktreeRecords : void 0,
558
- withPlaywright: opts.withPlaywright ? true : void 0,
559
- withEnv: opts.withEnv ? true : void 0,
560
- vncEnabled: vncEnabled ? true : void 0,
561
- vncContainerPort: vncEnabled ? VNC_CONTAINER_PORT : void 0,
562
- vncHostPort: vncHostPort ?? void 0,
563
- vncPassword,
564
- webContainerPort: WEB_CONTAINER_PORT,
565
- webHostPort: webHostPort ?? void 0,
566
- dockerVolume,
567
- dockerCacheShared: dockerCacheShared || void 0,
568
- projectRoot: opts.projectRoot,
569
- projectIndex,
570
- checkpointImage,
571
- checkpointSource,
572
- resourceLimits: persistableLimits(effectiveLimits),
573
- createdAt
574
- };
575
- await recordBox(record);
576
- return { record, imageBuilt: built };
577
- }
578
-
579
- export {
580
- hostBackupHasCredentials,
581
- syncClaudeCredentials,
582
- sanitizeBasename,
583
- defaultBoxName,
584
- createBox
585
- };
586
- //# sourceMappingURL=chunk-KJNZP6I3.js.map