@madarco/agentbox 0.7.0 → 0.9.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 (77) hide show
  1. package/dist/_cloud-attach-ZXBCNWJX.js +13 -0
  2. package/dist/{chunk-NW5NYTQM.js → chunk-BXQMIEHC.js} +459 -110
  3. package/dist/chunk-BXQMIEHC.js.map +1 -0
  4. package/dist/{chunk-UK72UQ5U.js → chunk-G3H2L3O2.js} +55 -4
  5. package/dist/chunk-G3H2L3O2.js.map +1 -0
  6. package/dist/{chunk-7KOEFGN2.js → chunk-GU5LW4B5.js} +385 -31
  7. package/dist/chunk-GU5LW4B5.js.map +1 -0
  8. package/dist/chunk-KL36BRN4.js +455 -0
  9. package/dist/chunk-KL36BRN4.js.map +1 -0
  10. package/dist/{chunk-V5KZGB5V.js → chunk-LEV3KICD.js} +18 -2
  11. package/dist/chunk-LEV3KICD.js.map +1 -0
  12. package/dist/chunk-MTVI44DW.js +662 -0
  13. package/dist/chunk-MTVI44DW.js.map +1 -0
  14. package/dist/{chunk-NAVL4R34.js → chunk-NCJP5MTN.js} +1281 -556
  15. package/dist/chunk-NCJP5MTN.js.map +1 -0
  16. package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js → cloud-poller-SUNA6ZQC-2RG5WPRN.js} +2 -2
  17. package/dist/{dist-ETCFRVPA.js → dist-32EZBYG4.js} +50 -20
  18. package/dist/{dist-R67WMLCF.js → dist-CX5CGVEB.js} +120 -10
  19. package/dist/dist-CX5CGVEB.js.map +1 -0
  20. package/dist/{dist-QZGJIBT5.js → dist-GDHP34ZK.js} +141 -75
  21. package/dist/dist-GDHP34ZK.js.map +1 -0
  22. package/dist/dist-XML54CNB.js +849 -0
  23. package/dist/dist-XML54CNB.js.map +1 -0
  24. package/dist/index.js +3881 -867
  25. package/dist/index.js.map +1 -1
  26. package/dist/prepared-state-CL4CWXQA-H5THETIM.js +18 -0
  27. package/dist/prepared-state-CL4CWXQA-H5THETIM.js.map +1 -0
  28. package/package.json +7 -5
  29. package/runtime/daytona/custom-system-CLAUDE.md +39 -0
  30. package/runtime/docker/Dockerfile.box +22 -0
  31. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +1 -1
  32. package/runtime/docker/packages/ctl/dist/bin.cjs +1214 -98
  33. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json +66 -35
  34. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
  35. package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +62 -1
  36. package/runtime/docker/packages/sandbox-docker/scripts/custom-system-CLAUDE.md +15 -4
  37. package/runtime/docker/packages/sandbox-docker/scripts/gh-shim +263 -0
  38. package/runtime/docker/packages/sandbox-docker/scripts/git-shim +131 -0
  39. package/runtime/docker/packages/sandbox-docker/scripts/opencode-agentbox-plugin.js +76 -0
  40. package/runtime/hetzner/agentbox-codex-hooks.json +66 -35
  41. package/runtime/hetzner/agentbox-setup-skill.md +1 -1
  42. package/runtime/hetzner/agentbox-vnc-start +15 -1
  43. package/runtime/hetzner/claude-managed-settings.json +62 -1
  44. package/runtime/hetzner/ctl.cjs +1214 -98
  45. package/runtime/hetzner/custom-system-CLAUDE.md +26 -14
  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 +11 -2
  50. package/runtime/relay/bin.cjs +1146 -63
  51. package/runtime/vercel/agentbox-checkpoint-cleanup +52 -0
  52. package/runtime/vercel/agentbox-codex-hooks.json +68 -0
  53. package/runtime/vercel/agentbox-open +28 -0
  54. package/runtime/vercel/agentbox-setup-skill.md +196 -0
  55. package/runtime/vercel/agentbox-vnc-start +91 -0
  56. package/runtime/vercel/claude-managed-settings.json +115 -0
  57. package/runtime/vercel/ctl.cjs +23466 -0
  58. package/runtime/vercel/custom-system-CLAUDE.md +50 -0
  59. package/runtime/vercel/gh-shim +263 -0
  60. package/runtime/vercel/git-shim +131 -0
  61. package/runtime/vercel/scripts/provision.sh +274 -0
  62. package/share/agentbox-setup/SKILL.md +1 -1
  63. package/share/host-skills/agentbox/SKILL.md +29 -0
  64. package/share/host-skills/agentbox-info/SKILL.md +211 -0
  65. package/share/host-skills/codex/agentbox.md +35 -0
  66. package/share/host-skills/opencode/agentbox.md +26 -0
  67. package/dist/_cloud-attach-DMVH6GWO.js +0 -12
  68. package/dist/chunk-7KOEFGN2.js.map +0 -1
  69. package/dist/chunk-NAVL4R34.js.map +0 -1
  70. package/dist/chunk-NW5NYTQM.js.map +0 -1
  71. package/dist/chunk-UK72UQ5U.js.map +0 -1
  72. package/dist/chunk-V5KZGB5V.js.map +0 -1
  73. package/dist/dist-QZGJIBT5.js.map +0 -1
  74. package/dist/dist-R67WMLCF.js.map +0 -1
  75. /package/dist/{_cloud-attach-DMVH6GWO.js.map → _cloud-attach-ZXBCNWJX.js.map} +0 -0
  76. /package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js.map → cloud-poller-SUNA6ZQC-2RG5WPRN.js.map} +0 -0
  77. /package/dist/{dist-ETCFRVPA.js.map → dist-32EZBYG4.js.map} +0 -0
@@ -1,50 +1,64 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CLAUDE_FORWARDED_ENV_KEYS,
4
+ CODEX_CREDENTIALS_BACKUP_FILE,
4
5
  CODEX_FORWARDED_ENV_KEYS,
6
+ CREDENTIALS_BACKUP_FILE,
7
+ OPENCODE_CREDENTIALS_BACKUP_FILE,
5
8
  OPENCODE_FORWARDED_ENV_KEYS,
6
- allocateProjectIndex,
7
9
  buildHostEnvFindArgs,
8
- detectGitRepos,
10
+ buildTmuxConfigShellSnippet,
9
11
  ensureRelay,
10
12
  forgetBoxFromRelay,
11
13
  generateRelayToken,
12
14
  generateVncPassword,
13
15
  hashProjectPath,
16
+ isRealAgentCredential,
14
17
  portlessAlias,
15
18
  portlessGetUrl,
16
19
  portlessUnalias,
17
20
  projectDirSegment,
18
- readState,
19
- recordBox,
20
21
  registerBoxWithRelay,
21
- removeBoxRecord,
22
22
  sanitizeMnemonic,
23
23
  stageClaudeCredentialsForUpload,
24
24
  stageClaudeStaticForUpload,
25
25
  stageCodexCredentialsForUpload,
26
26
  stageCodexStaticForUpload,
27
27
  stageOpencodeCredentialsForUpload,
28
+ stageOpencodeStateForUpload,
28
29
  stageOpencodeStaticForUpload
29
- } from "./chunk-NAVL4R34.js";
30
+ } from "./chunk-NCJP5MTN.js";
31
+ import {
32
+ allocateProjectIndex,
33
+ detectGitRepos,
34
+ readState,
35
+ recordBox,
36
+ removeBoxRecord
37
+ } from "./chunk-KL36BRN4.js";
30
38
 
31
39
  // ../../packages/sandbox-cloud/dist/index.js
32
40
  import { randomBytes } from "crypto";
33
41
  import { basename as basename2 } from "path";
34
- import { mkdir, readFile, readdir, rm, writeFile } from "fs/promises";
42
+ import { chmod, mkdir, writeFile } from "fs/promises";
43
+ import { dirname } from "path";
44
+ import { mkdir as mkdir2, readFile, readdir, rm, writeFile as writeFile2 } from "fs/promises";
35
45
  import { homedir } from "os";
36
46
  import { basename, join } from "path";
37
- import { mkdtemp, rm as rm2, writeFile as writeFile2 } from "fs/promises";
47
+ import { mkdtemp, rm as rm2, writeFile as writeFile3 } from "fs/promises";
38
48
  import { tmpdir } from "os";
39
49
  import { join as join2 } from "path";
40
50
  import { execa } from "execa";
41
- import { readFile as readFile2 } from "fs/promises";
51
+ import { mkdtemp as mkdtemp2, rm as rm3 } from "fs/promises";
52
+ import { tmpdir as tmpdir2 } from "os";
42
53
  import { join as join3 } from "path";
43
- import { parse as parseYaml } from "yaml";
44
54
  import { execa as execa2 } from "execa";
55
+ import { readFile as readFile2 } from "fs/promises";
56
+ import { join as join4 } from "path";
57
+ import { parse as parseYaml } from "yaml";
58
+ import { execa as execa3 } from "execa";
45
59
  import { existsSync, mkdirSync, renameSync, statSync } from "fs";
46
- import { mkdtemp as mkdtemp2, rm as rm3 } from "fs/promises";
47
- import { tmpdir as tmpdir2 } from "os";
60
+ import { mkdtemp as mkdtemp3, rm as rm4 } from "fs/promises";
61
+ import { tmpdir as tmpdir3 } from "os";
48
62
  import {
49
63
  basename as hostBasename,
50
64
  dirname as hostDirname,
@@ -52,10 +66,10 @@ import {
52
66
  resolve as hostResolve
53
67
  } from "path";
54
68
  import { posix } from "path";
55
- import { execa as execa3 } from "execa";
56
- import { mkdtemp as mkdtemp3, rm as rm4 } from "fs/promises";
57
- import { tmpdir as tmpdir3 } from "os";
58
- import { join as join4 } from "path";
69
+ import { execa as execa4 } from "execa";
70
+ import { mkdtemp as mkdtemp4, rm as rm5, stat } from "fs/promises";
71
+ import { tmpdir as tmpdir4 } from "os";
72
+ import { join as join5 } from "path";
59
73
  var CREDENTIALS_VOLUME = "agentbox-credentials";
60
74
  var AGENT_SPECS = [
61
75
  {
@@ -185,6 +199,33 @@ async function seedCredentialsOne(backend, handle, spec, opts) {
185
199
  await staged.cleanup();
186
200
  }
187
201
  }
202
+ var OPENCODE_STATE_DIR = "/home/vscode/.local/state/opencode";
203
+ async function seedOpencodeModelState(backend, handle, opts = {}) {
204
+ const log = opts.onLog ?? (() => {
205
+ });
206
+ const staged = await stageOpencodeStateForUpload();
207
+ if (staged.tarballPath === null) {
208
+ log("opencode: no host model selection to seed");
209
+ return;
210
+ }
211
+ try {
212
+ const remoteTar = "/tmp/agentbox-opencode-state.tar.gz";
213
+ await backend.uploadFile(handle, staged.tarballPath, remoteTar);
214
+ const res = await backend.exec(
215
+ handle,
216
+ `set -e; mkdir -p ${OPENCODE_STATE_DIR}; tar -xzf ${remoteTar} -C ${OPENCODE_STATE_DIR}; chown -R vscode:vscode ${OPENCODE_STATE_DIR} 2>/dev/null || true; rm -f ${remoteTar}`
217
+ );
218
+ if (res.exitCode !== 0) {
219
+ log(
220
+ `opencode: model-state seed failed (exit ${String(res.exitCode)}); box falls back to OpenCode's default model. stderr: ${res.stderr.slice(-200)}`
221
+ );
222
+ return;
223
+ }
224
+ log("opencode: model selection seeded \u2713");
225
+ } finally {
226
+ await staged.cleanup();
227
+ }
228
+ }
188
229
  function agentSpecsForCloud() {
189
230
  return AGENT_SPECS.map((s) => ({
190
231
  kind: s.kind,
@@ -193,6 +234,39 @@ function agentSpecsForCloud() {
193
234
  credentialsSubpath: s.credentialsSubpath
194
235
  }));
195
236
  }
237
+ var EXTRACT_SPECS = [
238
+ { kind: "claude", boxPath: "/home/vscode/.claude/.credentials.json", hostBackup: CREDENTIALS_BACKUP_FILE },
239
+ { kind: "codex", boxPath: "/home/vscode/.codex/auth.json", hostBackup: CODEX_CREDENTIALS_BACKUP_FILE },
240
+ {
241
+ kind: "opencode",
242
+ boxPath: "/home/vscode/.local/share/opencode/auth.json",
243
+ hostBackup: OPENCODE_CREDENTIALS_BACKUP_FILE
244
+ }
245
+ ];
246
+ async function extractCloudAgentCredentials(backend, handle, opts = {}) {
247
+ const log = opts.onLog ?? (() => {
248
+ });
249
+ const extracted = [];
250
+ for (const spec of EXTRACT_SPECS) {
251
+ const hostBackup = opts.backups?.[spec.kind] ?? spec.hostBackup;
252
+ try {
253
+ const r = await backend.exec(handle, `cat ${spec.boxPath} 2>/dev/null`, { noRetry: true });
254
+ const text = r.stdout;
255
+ if (r.exitCode !== 0 || !text || !isRealAgentCredential(spec.kind, text)) continue;
256
+ await mkdir(dirname(hostBackup), { recursive: true });
257
+ await writeFile(hostBackup, text, { mode: 384 });
258
+ await chmod(hostBackup, 384).catch(() => {
259
+ });
260
+ extracted.push(spec.kind);
261
+ log(`extracted ${spec.kind} login from box to ${hostBackup}`);
262
+ } catch (err) {
263
+ log(
264
+ `WARN: ${spec.kind} credential extract failed (${err instanceof Error ? err.message : String(err)}) \u2014 skipping`
265
+ );
266
+ }
267
+ }
268
+ return extracted;
269
+ }
196
270
  var CLOUD_CHECKPOINTS_ROOT = join(homedir(), ".agentbox", "cloud-checkpoints");
197
271
  var CLOUD_SNAPSHOT_NAME_PREFIX = "agentbox-ckpt-";
198
272
  function cloudSnapshotName(projectRoot, name) {
@@ -240,7 +314,7 @@ async function resolveCloudCheckpoint(projectRoot, backend, ref) {
240
314
  }
241
315
  async function writeCloudCheckpointManifest(projectRoot, backend, name, fields) {
242
316
  const dir = checkpointDir(backend, projectRoot, name);
243
- await mkdir(dir, { recursive: true });
317
+ await mkdir2(dir, { recursive: true });
244
318
  const manifest = {
245
319
  schema: 1,
246
320
  name,
@@ -250,7 +324,7 @@ async function writeCloudCheckpointManifest(projectRoot, backend, name, fields)
250
324
  sourceBoxName: fields.sourceBoxName,
251
325
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
252
326
  };
253
- await writeFile(join(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf8");
327
+ await writeFile2(join(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf8");
254
328
  return { name, dir, manifest };
255
329
  }
256
330
  async function removeCloudCheckpointDir(projectRoot, backend, name) {
@@ -289,7 +363,7 @@ async function uploadEnvFiles(args) {
289
363
  log(`env-file tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
290
364
  return { copied: 0 };
291
365
  }
292
- await writeFile2(join2(stage, ".marker"), "").catch(() => {
366
+ await writeFile3(join2(stage, ".marker"), "").catch(() => {
293
367
  });
294
368
  await args.backend.uploadFile(args.handle, localTar, REMOTE_TAR_PATH);
295
369
  const extract = await args.backend.exec(
@@ -307,10 +381,106 @@ async function uploadEnvFiles(args) {
307
381
  }
308
382
  return { copied: list.length };
309
383
  }
384
+ var BOX_HOME = "/home/vscode";
385
+ async function uploadCarryPaths(args) {
386
+ const log = args.onLog ?? (() => {
387
+ });
388
+ if (args.entries.length === 0) {
389
+ return { copied: 0, errors: [], applied: [] };
390
+ }
391
+ const stage = await mkdtemp2(join3(tmpdir2(), "agentbox-carry-"));
392
+ const errors = [];
393
+ const applied = [];
394
+ let copied = 0;
395
+ try {
396
+ for (const [i, entry] of args.entries.entries()) {
397
+ const where = `carry[${String(i)}] "${entry.rawSrc}"`;
398
+ if (entry.kind === "missing") {
399
+ log(`${where}: skipped (missing on host, optional)`);
400
+ continue;
401
+ }
402
+ try {
403
+ await uploadOneEntry({
404
+ backend: args.backend,
405
+ handle: args.handle,
406
+ entry,
407
+ stageDir: stage,
408
+ index: i
409
+ });
410
+ copied += 1;
411
+ applied.push({ src: entry.absSrc, dest: entry.absDest, bytes: entry.bytes ?? 0 });
412
+ } catch (err) {
413
+ const msg = err instanceof Error ? err.message : String(err);
414
+ errors.push(`${where}: ${msg}`);
415
+ log(`${where}: failed: ${msg}`);
416
+ }
417
+ }
418
+ } finally {
419
+ await rm3(stage, { recursive: true, force: true });
420
+ }
421
+ return { copied, errors, applied };
422
+ }
423
+ async function uploadOneEntry(args) {
424
+ const { entry } = args;
425
+ if (entry.kind === "missing") return;
426
+ const boxDest = entry.absDest.startsWith("~/") ? `${BOX_HOME}/${entry.absDest.slice(2)}` : entry.absDest;
427
+ const isDir = entry.kind === "dir";
428
+ const parentDir = isDir ? boxDest : dirnameUnix(boxDest);
429
+ const localTar = join3(args.stageDir, `carry-${String(args.index)}.tar`);
430
+ const tarArgs = isDir ? ["-C", entry.absSrc, "-cf", localTar, "."] : ["-C", dirnameUnix(entry.absSrc), "-cf", localTar, basenameUnix(entry.absSrc)];
431
+ const packed = await execa2("tar", tarArgs, { reject: false });
432
+ if (packed.exitCode !== 0) {
433
+ throw new Error(`tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
434
+ }
435
+ const remoteTar = `/tmp/agentbox-carry-${String(args.index)}.tar`;
436
+ await args.backend.uploadFile(args.handle, localTar, remoteTar);
437
+ const mode = entry.mode !== void 0 ? entry.mode.toString(8).padStart(4, "0") : "";
438
+ const uid = entry.user ?? 1e3;
439
+ const fileBase = !isDir ? basenameUnix(entry.absSrc) : "";
440
+ const destBase = !isDir ? basenameUnix(boxDest) : "";
441
+ const renameNeeded = !isDir && fileBase !== destBase;
442
+ const parts = [
443
+ `mkdir -p ${shellQuote(parentDir)}`,
444
+ isDir ? `tar -xf ${remoteTar} -C ${shellQuote(boxDest)} --no-same-permissions --no-same-owner -m` : `tar -xf ${remoteTar} -C ${shellQuote(parentDir)} --no-same-permissions --no-same-owner -m`
445
+ ];
446
+ if (renameNeeded) {
447
+ parts.push(
448
+ `mv ${shellQuote(`${parentDir}/${fileBase}`)} ${shellQuote(boxDest)}`
449
+ );
450
+ }
451
+ if (mode) parts.push(`chmod -R ${mode} ${shellQuote(boxDest)}`);
452
+ parts.push(`chown -R ${String(uid)}:${String(uid)} ${shellQuote(boxDest)}`);
453
+ if (boxDest.startsWith(BOX_HOME + "/") && parentDir !== BOX_HOME) {
454
+ parts.push(
455
+ `parent=$(dirname ${shellQuote(boxDest)}); while [ "$parent" != "${BOX_HOME}" ] && [ "$parent" != "/" ]; do chown ${String(uid)}:${String(uid)} "$parent"; parent=$(dirname "$parent"); done`
456
+ );
457
+ }
458
+ parts.push(`rm -f ${remoteTar}`);
459
+ const cmd = parts.join(" && ");
460
+ const execOpts = args.backend.name === "vercel" ? { user: "root" } : void 0;
461
+ const res = await args.backend.exec(args.handle, cmd, execOpts);
462
+ if (res.exitCode !== 0) {
463
+ throw new Error(
464
+ `in-box extract failed (exit ${String(res.exitCode)}): ${(res.stderr || res.stdout).slice(-300)}`
465
+ );
466
+ }
467
+ }
468
+ function dirnameUnix(p) {
469
+ const i = p.lastIndexOf("/");
470
+ if (i <= 0) return "/";
471
+ return p.slice(0, i);
472
+ }
473
+ function basenameUnix(p) {
474
+ const i = p.lastIndexOf("/");
475
+ return i < 0 ? p : p.slice(i + 1);
476
+ }
477
+ function shellQuote(s) {
478
+ return `'${s.replace(/'/g, `'\\''`)}'`;
479
+ }
310
480
  async function readExposedServicePorts(workspacePath) {
311
481
  let text;
312
482
  try {
313
- text = await readFile2(join3(workspacePath, "agentbox.yaml"), "utf8");
483
+ text = await readFile2(join4(workspacePath, "agentbox.yaml"), "utf8");
314
484
  } catch {
315
485
  return [];
316
486
  }
@@ -366,10 +536,10 @@ async function uploadToCloudBox(backend, handle, hostSrc, boxDst) {
366
536
  finalName = posix.basename(boxDst);
367
537
  }
368
538
  const finalPath = boxParent === "/" ? `/${finalName}` : `${boxParent}/${finalName}`;
369
- const stage = await mkdtemp2(hostJoin(tmpdir2(), "agentbox-cp-up-"));
539
+ const stage = await mkdtemp3(hostJoin(tmpdir3(), "agentbox-cp-up-"));
370
540
  const localTar = hostJoin(stage, "payload.tar.gz");
371
541
  try {
372
- await execa2("tar", ["-C", srcParent, "-czf", localTar, srcBasename], {
542
+ await execa3("tar", ["-C", srcParent, "-czf", localTar, srcBasename], {
373
543
  env: { ...process.env, COPYFILE_DISABLE: "1" }
374
544
  });
375
545
  await backend.uploadFile(handle, localTar, REMOTE_UP_TAR);
@@ -395,14 +565,14 @@ async function uploadToCloudBox(backend, handle, hostSrc, boxDst) {
395
565
  throw new Error(`cloud upload extract failed: ${r.stderr || r.stdout}`);
396
566
  }
397
567
  } finally {
398
- await rm3(stage, { recursive: true, force: true });
568
+ await rm4(stage, { recursive: true, force: true });
399
569
  }
400
570
  return { finalPath };
401
571
  }
402
572
  async function pullCloudDirContents(backend, handle, boxSrcDir, hostDstDir) {
403
573
  const dstAbs = hostResolve(hostDstDir);
404
574
  mkdirSync(dstAbs, { recursive: true });
405
- const stage = await mkdtemp2(hostJoin(tmpdir2(), "agentbox-pull-"));
575
+ const stage = await mkdtemp3(hostJoin(tmpdir3(), "agentbox-pull-"));
406
576
  const localTar = hostJoin(stage, "payload.tar.gz");
407
577
  try {
408
578
  const packScript = [
@@ -415,11 +585,11 @@ async function pullCloudDirContents(backend, handle, boxSrcDir, hostDstDir) {
415
585
  throw new Error(`cloud workspace pack failed: ${r.stderr || r.stdout}`);
416
586
  }
417
587
  await backend.downloadFile(handle, REMOTE_DOWN_TAR, localTar);
418
- await execa2("tar", ["-xzf", localTar, "-C", dstAbs]);
588
+ await execa3("tar", ["-xzf", localTar, "-C", dstAbs]);
419
589
  await backend.exec(handle, `rm -f ${quoteShellArg(REMOTE_DOWN_TAR)}`).catch(() => {
420
590
  });
421
591
  } finally {
422
- await rm3(stage, { recursive: true, force: true });
592
+ await rm4(stage, { recursive: true, force: true });
423
593
  }
424
594
  return { finalPath: dstAbs };
425
595
  }
@@ -439,7 +609,7 @@ async function downloadFromCloudBox(backend, handle, boxSrc, hostDst) {
439
609
  }
440
610
  mkdirSync(hostParent, { recursive: true });
441
611
  const finalPath = hostJoin(hostParent, finalName);
442
- const stage = await mkdtemp2(hostJoin(tmpdir2(), "agentbox-cp-down-"));
612
+ const stage = await mkdtemp3(hostJoin(tmpdir3(), "agentbox-cp-down-"));
443
613
  const localTar = hostJoin(stage, "payload.tar.gz");
444
614
  try {
445
615
  const packScript = [
@@ -452,14 +622,14 @@ async function downloadFromCloudBox(backend, handle, boxSrc, hostDst) {
452
622
  throw new Error(`cloud download pack failed: ${r.stderr || r.stdout}`);
453
623
  }
454
624
  await backend.downloadFile(handle, REMOTE_DOWN_TAR, localTar);
455
- await execa2("tar", ["-xzf", localTar, "-C", hostParent]);
625
+ await execa3("tar", ["-xzf", localTar, "-C", hostParent]);
456
626
  if (finalName !== srcBasename) {
457
627
  renameSync(hostJoin(hostParent, srcBasename), finalPath);
458
628
  }
459
629
  await backend.exec(handle, `rm -f ${quoteShellArg(REMOTE_DOWN_TAR)}`).catch(() => {
460
630
  });
461
631
  } finally {
462
- await rm3(stage, { recursive: true, force: true });
632
+ await rm4(stage, { recursive: true, force: true });
463
633
  }
464
634
  return { finalPath };
465
635
  }
@@ -547,24 +717,30 @@ async function seedCloudWorkspace(args) {
547
717
  const nested = repos.filter((r) => r.kind === "nested");
548
718
  if (root) {
549
719
  log(
550
- nested.length > 0 ? `seeding /workspace from git bundle (+${String(nested.length)} nested repo${nested.length === 1 ? "" : "s"})` : "seeding /workspace from git bundle"
720
+ nested.length > 0 ? `seeding /workspace from shallow git clone (+${String(nested.length)} nested repo${nested.length === 1 ? "" : "s"})` : "seeding /workspace from shallow git clone"
551
721
  );
552
- await seedFromGitBundle({
722
+ await seedFromGitClone({
553
723
  backend: args.backend,
554
724
  handle: args.handle,
555
725
  hostRepo: root.hostMainRepo,
556
726
  branch: args.branch,
557
- workspaceDir
727
+ workspaceDir,
728
+ bundleDepth: args.bundleDepth,
729
+ fromBranch: args.fromBranch,
730
+ useBranch: args.useBranch,
731
+ onLog: log
558
732
  });
559
733
  for (const r of nested) {
560
734
  const sub = `${workspaceDir}/${r.relPathFromWorkspace}`;
561
- log(`seeding nested repo ${r.relPathFromWorkspace} from git bundle`);
562
- await seedFromGitBundle({
735
+ log(`seeding nested repo ${r.relPathFromWorkspace} from shallow git clone`);
736
+ await seedFromGitClone({
563
737
  backend: args.backend,
564
738
  handle: args.handle,
565
739
  hostRepo: r.hostMainRepo,
566
740
  branch: args.branch,
567
- workspaceDir: sub
741
+ workspaceDir: sub,
742
+ bundleDepth: args.bundleDepth,
743
+ onLog: log
568
744
  });
569
745
  }
570
746
  return { fromGit: true, branch: args.branch };
@@ -580,41 +756,53 @@ async function seedCloudWorkspace(args) {
580
756
  }
581
757
  var STASH_CARRYOVER_REF = "refs/agentbox-carryover/stash";
582
758
  var REMOTE_UNTRACKED_TAR = "/tmp/agentbox-carryover-untracked.tar.gz";
583
- async function seedFromGitBundle(args) {
584
- const stage = await mkdtemp3(join4(tmpdir3(), "agentbox-bundle-"));
585
- const bundlePath = join4(stage, "workspace.bundle");
586
- const untrackedTarPath = join4(stage, "untracked.tar.gz");
587
- const stashSha = await safeStashCreate(args.hostRepo);
588
- const untrackedSize = await maybeBuildUntrackedTar(args.hostRepo, untrackedTarPath);
759
+ var DEFAULT_BUNDLE_DEPTH = 200;
760
+ var LARGE_BUNDLE_DEPTH = 100;
761
+ var LARGE_BUNDLE_THRESHOLD_BYTES = 20 * 1024 * 1024;
762
+ async function seedFromGitClone(args) {
763
+ const log = args.onLog ?? (() => {
764
+ });
765
+ const stage = await mkdtemp4(join5(tmpdir4(), "agentbox-clone-"));
766
+ const cloneDir = join5(stage, "clone");
767
+ const tarPath = join5(stage, "workspace.tar.gz");
768
+ const untrackedTarPath = join5(stage, "untracked.tar.gz");
769
+ const stashSha = args.useBranch ? null : await safeStashCreate(args.hostRepo);
770
+ const untrackedSize = args.useBranch ? 0 : await maybeBuildUntrackedTar(args.hostRepo, untrackedTarPath);
589
771
  let stashRefCreated = false;
590
772
  try {
591
773
  if (stashSha) {
592
- const ref = await execa3(
774
+ const ref = await execa4(
593
775
  "git",
594
776
  ["-C", args.hostRepo, "update-ref", STASH_CARRYOVER_REF, stashSha],
595
777
  { reject: false }
596
778
  );
597
779
  stashRefCreated = ref.exitCode === 0;
598
780
  }
599
- const depthRaw = process.env["AGENTBOX_BUNDLE_DEPTH"];
600
- const depth = depthRaw ? Number.parseInt(depthRaw, 10) : NaN;
601
- const bundleArgs = ["-C", args.hostRepo, "bundle", "create", bundlePath];
602
- if (Number.isFinite(depth) && depth > 0) {
603
- bundleArgs.push(`--depth=${String(depth)}`, "HEAD");
604
- } else {
605
- bundleArgs.push("--all");
606
- }
607
- if (stashRefCreated) bundleArgs.push(STASH_CARRYOVER_REF);
608
- await execa3("git", bundleArgs);
609
- if (stashRefCreated) {
610
- await execa3("git", ["-C", args.hostRepo, "update-ref", "-d", STASH_CARRYOVER_REF], {
611
- reject: false
612
- });
613
- stashRefCreated = false;
781
+ const configured = args.bundleDepth;
782
+ const adaptive = configured === void 0;
783
+ const initialDepth = adaptive ? DEFAULT_BUNDLE_DEPTH : configured === 0 ? null : configured;
784
+ log(
785
+ adaptive ? `clone: depth=${String(DEFAULT_BUNDLE_DEPTH)} (default, adaptive)` : initialDepth === null ? "clone: depth=full (configured)" : `clone: depth=${String(initialDepth)} (configured)`
786
+ );
787
+ const cloneBranch = args.useBranch ?? args.fromBranch;
788
+ await runShallowClone(args.hostRepo, cloneDir, initialDepth, stashRefCreated, cloneBranch);
789
+ await tarCloneDir(cloneDir, tarPath);
790
+ if (adaptive && initialDepth !== null) {
791
+ const size = await safeFileSize(tarPath);
792
+ if (size > LARGE_BUNDLE_THRESHOLD_BYTES) {
793
+ const mb = (size / (1024 * 1024)).toFixed(1);
794
+ log(
795
+ `clone tar exceeded ${String(LARGE_BUNDLE_THRESHOLD_BYTES / (1024 * 1024))} MB at depth ${String(DEFAULT_BUNDLE_DEPTH)} (${mb} MB), rebuilding at depth ${String(LARGE_BUNDLE_DEPTH)}`
796
+ );
797
+ await rm5(cloneDir, { recursive: true, force: true });
798
+ await rm5(tarPath, { force: true });
799
+ await runShallowClone(args.hostRepo, cloneDir, LARGE_BUNDLE_DEPTH, stashRefCreated, cloneBranch);
800
+ await tarCloneDir(cloneDir, tarPath);
801
+ }
614
802
  }
615
803
  const remoteUrl = await readOriginUrl(args.hostRepo);
616
- const remoteBundle = "/tmp/agentbox-workspace.bundle";
617
- await args.backend.uploadFile(args.handle, bundlePath, remoteBundle);
804
+ const remoteTar = "/tmp/agentbox-workspace.tar.gz";
805
+ await args.backend.uploadFile(args.handle, tarPath, remoteTar);
618
806
  if (untrackedSize > 0) {
619
807
  await args.backend.uploadFile(args.handle, untrackedTarPath, REMOTE_UNTRACKED_TAR);
620
808
  }
@@ -633,50 +821,80 @@ async function seedFromGitBundle(args) {
633
821
  // Move out of any cwd we might inherit from Daytona's executeCommand
634
822
  // before we delete /workspace. The agentbox image bakes WORKDIR
635
823
  // /workspace; if the shell's cwd is /workspace when we `rm -rf` it,
636
- // the next process inherits a stale cwd FD and git-clone's child
637
- // (index-pack) fails with "Unable to read current working directory".
824
+ // the next process inherits a stale cwd FD and tar's children fail
825
+ // with "Unable to read current working directory".
638
826
  `cd /tmp`,
639
827
  SUDO,
640
- // rm -rf only the directory we're about to clone into — for nested
828
+ // rm -rf only the directory we're about to extract into — for nested
641
829
  // repos this is just `/workspace/<rel>`, so the root clone (already
642
830
  // at `/workspace`) is preserved.
643
831
  `$SUDO rm -rf ${quoteShellArgv([args.workspaceDir])}`,
644
832
  `$SUDO mkdir -p ${quoteShellArgv([args.workspaceDir])}`,
645
833
  `$SUDO chown "$(id -un):$(id -gn)" ${quoteShellArgv([args.workspaceDir])}`,
646
- `git clone ${quoteShellArgv([remoteBundle, args.workspaceDir])}`,
834
+ `tar -C ${quoteShellArgv([args.workspaceDir])} -xzf ${quoteShellArgv([remoteTar])}`,
647
835
  setOrigin,
648
- `git -C ${quoteShellArgv([args.workspaceDir])} fetch ${quoteShellArgv([remoteBundle])} --tags '+refs/heads/*:refs/remotes/bundle/*' || true`,
649
- `git -C ${quoteShellArgv([args.workspaceDir])} checkout -B ${quoteShellArgv([args.branch])}`,
836
+ // reuse: the clone already landed on `<branch>` (pinned via `--branch`);
837
+ // a plain checkout materializes the working tree without resetting the
838
+ // ref. fork: `-B` (re)points `<branch>` at the clone HEAD.
839
+ args.useBranch ? `git -C ${quoteShellArgv([args.workspaceDir])} checkout ${quoteShellArgv([args.branch])}` : `git -C ${quoteShellArgv([args.workspaceDir])} checkout -B ${quoteShellArgv([args.branch])}`,
650
840
  ...carryOverSteps,
651
- `rm -f ${quoteShellArgv([remoteBundle])}`
841
+ `rm -f ${quoteShellArgv([remoteTar])}`
652
842
  ].join("\n");
653
843
  const r = await args.backend.exec(args.handle, bashScript(script));
654
844
  if (r.exitCode !== 0) {
655
- throw new Error(`workspace seed (bundle) failed: ${r.stderr || r.stdout}`);
845
+ throw new Error(`workspace seed (clone) failed: ${r.stderr || r.stdout}`);
656
846
  }
657
847
  } finally {
658
848
  if (stashRefCreated) {
659
- await execa3("git", ["-C", args.hostRepo, "update-ref", "-d", STASH_CARRYOVER_REF], {
849
+ await execa4("git", ["-C", args.hostRepo, "update-ref", "-d", STASH_CARRYOVER_REF], {
660
850
  reject: false
661
851
  });
662
852
  }
663
- await rm4(stage, { recursive: true, force: true });
853
+ await rm5(stage, { recursive: true, force: true });
854
+ }
855
+ }
856
+ async function runShallowClone(hostRepo, cloneDir, depth, includeStashRef, fromBranch) {
857
+ const cloneArgs = ["clone", "--no-checkout", "--quiet"];
858
+ if (depth !== null) cloneArgs.push(`--depth=${String(depth)}`);
859
+ if (fromBranch) cloneArgs.push("--branch", fromBranch);
860
+ cloneArgs.push(`file://${hostRepo}`, cloneDir);
861
+ await execa4("git", cloneArgs);
862
+ if (includeStashRef) {
863
+ const fetchArgs = ["-C", cloneDir, "fetch", "--quiet"];
864
+ if (depth !== null) fetchArgs.push(`--depth=${String(depth)}`);
865
+ fetchArgs.push(
866
+ `file://${hostRepo}`,
867
+ `+${STASH_CARRYOVER_REF}:refs/remotes/origin/agentbox-carryover/stash`
868
+ );
869
+ await execa4("git", fetchArgs, { reject: false });
870
+ }
871
+ }
872
+ async function tarCloneDir(cloneDir, outPath) {
873
+ await execa4("tar", ["-C", cloneDir, "-czf", outPath, "."], {
874
+ env: { ...process.env, COPYFILE_DISABLE: "1" }
875
+ });
876
+ }
877
+ async function safeFileSize(path) {
878
+ try {
879
+ return (await stat(path)).size;
880
+ } catch {
881
+ return 0;
664
882
  }
665
883
  }
666
884
  async function safeStashCreate(hostRepo) {
667
- const r = await execa3("git", ["-C", hostRepo, "stash", "create"], { reject: false });
885
+ const r = await execa4("git", ["-C", hostRepo, "stash", "create"], { reject: false });
668
886
  if (r.exitCode !== 0) return null;
669
887
  const sha = r.stdout.trim();
670
888
  return sha.length > 0 ? sha : null;
671
889
  }
672
890
  async function maybeBuildUntrackedTar(hostRepo, outPath) {
673
- const list = await execa3(
891
+ const list = await execa4(
674
892
  "git",
675
893
  ["-C", hostRepo, "ls-files", "--others", "--exclude-standard", "-z"],
676
894
  { reject: false }
677
895
  );
678
896
  if (list.exitCode !== 0 || list.stdout.length === 0) return 0;
679
- const tar = await execa3(
897
+ const tar = await execa4(
680
898
  "tar",
681
899
  ["-C", hostRepo, "--null", "-T", "-", "-czf", outPath],
682
900
  {
@@ -687,24 +905,24 @@ async function maybeBuildUntrackedTar(hostRepo, outPath) {
687
905
  );
688
906
  if (tar.exitCode !== 0) return 0;
689
907
  try {
690
- const { stat } = await import("fs/promises");
691
- const s = await stat(outPath);
908
+ const { stat: stat2 } = await import("fs/promises");
909
+ const s = await stat2(outPath);
692
910
  return s.size;
693
911
  } catch {
694
912
  return 0;
695
913
  }
696
914
  }
697
915
  async function readOriginUrl(hostRepo) {
698
- const r = await execa3("git", ["-C", hostRepo, "remote", "get-url", "origin"], { reject: false });
916
+ const r = await execa4("git", ["-C", hostRepo, "remote", "get-url", "origin"], { reject: false });
699
917
  if (r.exitCode !== 0) return null;
700
918
  const out = (r.stdout ?? "").trim();
701
919
  return out.length > 0 ? out : null;
702
920
  }
703
921
  async function seedFromTar(args) {
704
- const stage = await mkdtemp3(join4(tmpdir3(), "agentbox-tar-"));
705
- const tarPath = join4(stage, "workspace.tar.gz");
922
+ const stage = await mkdtemp4(join5(tmpdir4(), "agentbox-tar-"));
923
+ const tarPath = join5(stage, "workspace.tar.gz");
706
924
  try {
707
- await execa3("tar", ["-C", args.hostDir, "-czf", tarPath, "."]);
925
+ await execa4("tar", ["-C", args.hostDir, "-czf", tarPath, "."]);
708
926
  const remoteTar = "/tmp/agentbox-workspace.tar.gz";
709
927
  await args.backend.uploadFile(args.handle, tarPath, remoteTar);
710
928
  const SUDO = `if command -v sudo >/dev/null 2>&1; then SUDO='sudo -n'; else SUDO=''; fi`;
@@ -728,7 +946,7 @@ async function seedFromTar(args) {
728
946
  throw new Error(`workspace seed (tar) failed: ${r.stderr || r.stdout}`);
729
947
  }
730
948
  } finally {
731
- await rm4(stage, { recursive: true, force: true });
949
+ await rm5(stage, { recursive: true, force: true });
732
950
  }
733
951
  }
734
952
  var CLOUD_WORKSPACE_DIR = "/workspace";
@@ -759,18 +977,28 @@ function parseLoopbackPort(url) {
759
977
  return void 0;
760
978
  }
761
979
  }
762
- async function bootstrapPortlessForCloudBox(backend, handle, args) {
763
- const localPort = parseLoopbackPort(args.webPreviewUrl);
980
+ async function registerHostPortlessAlias(args) {
981
+ const localPort = parseLoopbackPort(args.previewUrl);
764
982
  if (localPort === void 0) return void 0;
765
- const ok = await portlessAlias(args.boxName, localPort);
983
+ const ok = await portlessAlias(args.alias, localPort);
766
984
  if (!ok) {
767
985
  args.onLog(
768
- `portless: alias not registered (portless CLI missing or not running) \u2014 host URL stays http://127.0.0.1:${String(localPort)}`
986
+ `portless: ${args.label} alias not registered (portless CLI missing or not running) \u2014 host URL stays http://127.0.0.1:${String(localPort)}`
769
987
  );
770
988
  return void 0;
771
989
  }
772
- const url = await portlessGetUrl(args.boxName);
990
+ const url = await portlessGetUrl(args.alias);
773
991
  args.onLog(`portless alias ${url} -> 127.0.0.1:${String(localPort)}`);
992
+ return url;
993
+ }
994
+ async function bootstrapPortlessForCloudBox(backend, handle, args) {
995
+ const url = await registerHostPortlessAlias({
996
+ alias: args.boxName,
997
+ previewUrl: args.webPreviewUrl,
998
+ label: "web",
999
+ onLog: args.onLog
1000
+ });
1001
+ if (!url) return void 0;
774
1002
  if (backend.startInBoxPortless) {
775
1003
  const mode = parsePortlessUrl(url) ?? { proxyPort: DEFAULT_PORTLESS_PROXY_PORT, tls: false };
776
1004
  try {
@@ -806,7 +1034,9 @@ function createCloudProvider(backend, opts = {}) {
806
1034
  return {
807
1035
  id,
808
1036
  name,
809
- branch: `agentbox/${name}`
1037
+ // --use-branch reuses the named branch directly; otherwise fork a fresh
1038
+ // per-box branch. The CLI validated `useBranch` exists host-side.
1039
+ branch: req.useBranch ?? `agentbox/${name}`
810
1040
  };
811
1041
  }
812
1042
  async function probe(box) {
@@ -825,7 +1055,13 @@ function createCloudProvider(backend, opts = {}) {
825
1055
  });
826
1056
  const { id, name, branch } = mintBox(req);
827
1057
  const image = opts.provisionImage ? await opts.provisionImage(req) : req.image ?? FALLBACK_IMAGE;
828
- const resources = opts.defaultResources ?? { cpu: 2, memory: 4, disk: 8 };
1058
+ const baseResources = opts.defaultResources ?? { cpu: 2, memory: 4, disk: 8 };
1059
+ const vcpuOverride = req.providerOptions?.["vcpus"];
1060
+ const resources = typeof vcpuOverride === "number" && vcpuOverride > 0 ? { ...baseResources, cpu: vcpuOverride } : baseResources;
1061
+ const timeoutOverride = req.providerOptions?.["timeoutMs"];
1062
+ const timeoutMs = typeof timeoutOverride === "number" && timeoutOverride > 0 ? timeoutOverride : void 0;
1063
+ const networkPolicyOpt = req.providerOptions?.["networkPolicy"];
1064
+ const networkPolicy = typeof networkPolicyOpt === "string" && networkPolicyOpt.trim() !== "" ? networkPolicyOpt.trim() : void 0;
829
1065
  const relayToken = generateRelayToken();
830
1066
  const bridgeToken = generateRelayToken();
831
1067
  try {
@@ -848,6 +1084,7 @@ function createCloudProvider(backend, opts = {}) {
848
1084
  }
849
1085
  }
850
1086
  const agentVolumes = await ensureAgentVolumesForCloud(backend, { onLog: log });
1087
+ const exposeServicePorts = await readExposedServicePorts(req.workspacePath);
851
1088
  log(
852
1089
  snapshotName ? `provisioning ${providerName} sandbox from snapshot` : `provisioning ${providerName} sandbox`
853
1090
  );
@@ -856,12 +1093,17 @@ function createCloudProvider(backend, opts = {}) {
856
1093
  image,
857
1094
  snapshot: snapshotName,
858
1095
  resources,
1096
+ timeoutMs,
1097
+ exposePorts: exposeServicePorts,
1098
+ networkPolicy,
859
1099
  env: {
860
1100
  AGENTBOX_BOX_ID: id,
861
1101
  AGENTBOX_BOX_NAME: name,
862
1102
  AGENTBOX_BOX_KIND: "cloud",
863
- // In-sandbox relay is on the box's loopback at the default port.
864
- AGENTBOX_RELAY_URL: `http://127.0.0.1:${String(8787)}`,
1103
+ // In-sandbox relay is on the box's loopback at the in-box port.
1104
+ // 8788 is distinct from the host relay's 8787 so a nested agentbox
1105
+ // run inside the box can claim :8787 without colliding.
1106
+ AGENTBOX_RELAY_URL: `http://127.0.0.1:${String(8788)}`,
865
1107
  AGENTBOX_RELAY_TOKEN: relayToken,
866
1108
  AGENTBOX_BRIDGE_TOKEN: bridgeToken,
867
1109
  ...agentVolumes.env
@@ -879,6 +1121,9 @@ function createCloudProvider(backend, opts = {}) {
879
1121
  workspacePath: req.workspacePath,
880
1122
  branch,
881
1123
  workspaceDir: CLOUD_WORKSPACE_DIR,
1124
+ bundleDepth: req.bundleDepth,
1125
+ fromBranch: req.fromBranch,
1126
+ useBranch: req.useBranch,
882
1127
  onLog: log
883
1128
  });
884
1129
  }
@@ -889,6 +1134,7 @@ function createCloudProvider(backend, opts = {}) {
889
1134
  onLog: log
890
1135
  });
891
1136
  }
1137
+ await seedOpencodeModelState(backend, handle, { onLog: log });
892
1138
  if (req.envFilesToImport && req.envFilesToImport.length > 0) {
893
1139
  const { copied } = await uploadEnvFiles({
894
1140
  backend,
@@ -900,22 +1146,39 @@ function createCloudProvider(backend, opts = {}) {
900
1146
  });
901
1147
  if (copied > 0) log(`copied ${String(copied)} env/config file(s) into /workspace`);
902
1148
  }
1149
+ let carrySummary;
1150
+ if (req.carry && req.carry.length > 0) {
1151
+ log(`carry: copying ${String(req.carry.length)} host path(s) into the box`);
1152
+ const result = await uploadCarryPaths({
1153
+ backend,
1154
+ handle,
1155
+ entries: req.carry,
1156
+ onLog: log
1157
+ });
1158
+ log(`carry: copied ${String(result.copied)}/${String(req.carry.length)} entry/entries`);
1159
+ for (const err of result.errors) log(`carry: ${err}`);
1160
+ if (result.applied.length > 0) {
1161
+ carrySummary = { count: result.applied.length, entries: result.applied };
1162
+ }
1163
+ }
903
1164
  log("launching agentbox-ctl daemon");
904
1165
  await launchCloudCtlDaemon({
905
1166
  backend,
906
1167
  handle,
907
1168
  boxId: id,
908
1169
  boxName: name,
909
- relayUrl: `http://127.0.0.1:${String(8787)}`,
1170
+ relayUrl: `http://127.0.0.1:${String(8788)}`,
910
1171
  relayToken,
911
1172
  bridgeToken
912
1173
  });
913
- log("launching in-box dockerd");
914
- try {
915
- const dockerd = await launchCloudDockerdDaemon({ backend, handle, timeoutMs: 6e4 });
916
- if (!dockerd.up) log(`dockerd did not become ready (continuing): ${dockerd.reason ?? "unknown"}`);
917
- } catch (err) {
918
- log(`dockerd daemon launch failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
1174
+ if (opts.launchDockerd !== false) {
1175
+ log("launching in-box dockerd");
1176
+ try {
1177
+ const dockerd = await launchCloudDockerdDaemon({ backend, handle, timeoutMs: 6e4 });
1178
+ if (!dockerd.up) log(`dockerd did not become ready (continuing): ${dockerd.reason ?? "unknown"}`);
1179
+ } catch (err) {
1180
+ log(`dockerd daemon launch failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
1181
+ }
919
1182
  }
920
1183
  const vncEnabled = req.vnc?.enabled !== false;
921
1184
  const vncPassword = vncEnabled ? generateVncPassword() : void 0;
@@ -950,7 +1213,30 @@ function createCloudProvider(backend, opts = {}) {
950
1213
  portlessUrlResolved = r.url;
951
1214
  }
952
1215
  }
953
- const servicePorts = await readExposedServicePorts(req.workspacePath);
1216
+ let vncPreview;
1217
+ if (portlessOpt && vncEnabled) {
1218
+ try {
1219
+ vncPreview = await backend.previewUrl(handle, CLOUD_VNC_PORT);
1220
+ } catch {
1221
+ vncPreview = void 0;
1222
+ }
1223
+ }
1224
+ let portlessVncAliasName;
1225
+ let portlessVncUrlResolved;
1226
+ if (portlessOpt && vncPreview) {
1227
+ const vncAlias = `vnc-${name}`;
1228
+ const url = await registerHostPortlessAlias({
1229
+ alias: vncAlias,
1230
+ previewUrl: vncPreview.url,
1231
+ label: "vnc",
1232
+ onLog: log
1233
+ });
1234
+ if (url) {
1235
+ portlessVncAliasName = vncAlias;
1236
+ portlessVncUrlResolved = url;
1237
+ }
1238
+ }
1239
+ const servicePorts = exposeServicePorts;
954
1240
  const servicePreviews = {};
955
1241
  for (const port of servicePorts) {
956
1242
  if (port === CLOUD_WEB_PROXY_PORT) continue;
@@ -962,16 +1248,19 @@ function createCloudProvider(backend, opts = {}) {
962
1248
  }
963
1249
  let relayPreview;
964
1250
  try {
965
- relayPreview = await backend.previewUrl(handle, 8787);
1251
+ relayPreview = await backend.previewUrl(handle, 8788);
966
1252
  } catch {
967
1253
  relayPreview = void 0;
968
1254
  }
1255
+ const state = await readState();
1256
+ const projectIndex = req.projectRoot ? allocateProjectIndex(state, req.projectRoot) : void 0;
969
1257
  if (relayPreview) {
970
1258
  try {
971
1259
  await registerBoxWithRelay({
972
1260
  boxId: id,
973
1261
  token: relayToken,
974
1262
  name,
1263
+ projectIndex,
975
1264
  kind: "cloud",
976
1265
  backend: backend.name,
977
1266
  previewUrl: relayPreview.url,
@@ -985,8 +1274,6 @@ function createCloudProvider(backend, opts = {}) {
985
1274
  );
986
1275
  }
987
1276
  }
988
- const state = await readState();
989
- const projectIndex = req.projectRoot ? allocateProjectIndex(state, req.projectRoot) : void 0;
990
1277
  const record = {
991
1278
  id,
992
1279
  name,
@@ -1005,8 +1292,11 @@ function createCloudProvider(backend, opts = {}) {
1005
1292
  relayToken,
1006
1293
  withPlaywright: req.withPlaywright,
1007
1294
  withEnv: req.withEnv,
1295
+ carry: carrySummary,
1008
1296
  portlessAlias: portlessAliasName,
1009
1297
  portlessUrl: portlessUrlResolved,
1298
+ portlessVncAlias: portlessVncAliasName,
1299
+ portlessVncUrl: portlessVncUrlResolved,
1010
1300
  vncEnabled,
1011
1301
  vncPassword,
1012
1302
  vncContainerPort: vncEnabled ? CLOUD_VNC_PORT : void 0,
@@ -1069,7 +1359,7 @@ function createCloudProvider(backend, opts = {}) {
1069
1359
  }
1070
1360
  let relayPreview;
1071
1361
  try {
1072
- relayPreview = await backend.previewUrl(h, 8787);
1362
+ relayPreview = await backend.previewUrl(h, 8788);
1073
1363
  } catch {
1074
1364
  relayPreview = box.cloud?.relayPreviewUrl ? { url: box.cloud.relayPreviewUrl, token: box.cloud.relayPreviewToken } : void 0;
1075
1365
  }
@@ -1093,10 +1383,31 @@ function createCloudProvider(backend, opts = {}) {
1093
1383
  portlessUrlResolved = r.url;
1094
1384
  }
1095
1385
  }
1386
+ let portlessVncAliasName = box.portlessVncAlias;
1387
+ let portlessVncUrlResolved = box.portlessVncUrl;
1388
+ if (box.portlessVncAlias && box.vncEnabled) {
1389
+ try {
1390
+ const vncPreview = await backend.previewUrl(h, CLOUD_VNC_PORT);
1391
+ const url = await registerHostPortlessAlias({
1392
+ alias: box.portlessVncAlias,
1393
+ previewUrl: vncPreview.url,
1394
+ label: "vnc",
1395
+ onLog: () => {
1396
+ }
1397
+ });
1398
+ if (url) {
1399
+ portlessVncAliasName = box.portlessVncAlias;
1400
+ portlessVncUrlResolved = url;
1401
+ }
1402
+ } catch {
1403
+ }
1404
+ }
1096
1405
  const next = {
1097
1406
  ...box,
1098
1407
  portlessAlias: portlessAliasName,
1099
1408
  portlessUrl: portlessUrlResolved,
1409
+ portlessVncAlias: portlessVncAliasName,
1410
+ portlessVncUrl: portlessVncUrlResolved,
1100
1411
  cloud: {
1101
1412
  ...box.cloud ?? { backend: providerName, sandboxId: h.sandboxId },
1102
1413
  webPort,
@@ -1111,15 +1422,17 @@ function createCloudProvider(backend, opts = {}) {
1111
1422
  handle: h,
1112
1423
  boxId: box.id,
1113
1424
  boxName: box.name,
1114
- relayUrl: `http://127.0.0.1:${String(8787)}`,
1425
+ relayUrl: `http://127.0.0.1:${String(8788)}`,
1115
1426
  relayToken: box.relayToken ?? "",
1116
1427
  bridgeToken: box.cloud?.bridgeToken
1117
1428
  });
1118
- try {
1119
- const dockerd = await launchCloudDockerdDaemon({ backend, handle: h, timeoutMs: 6e4 });
1120
- if (!dockerd.up) {
1429
+ if (opts.launchDockerd !== false) {
1430
+ try {
1431
+ const dockerd = await launchCloudDockerdDaemon({ backend, handle: h, timeoutMs: 6e4 });
1432
+ if (!dockerd.up) {
1433
+ }
1434
+ } catch {
1121
1435
  }
1122
- } catch {
1123
1436
  }
1124
1437
  if (box.vncEnabled && box.vncPassword) {
1125
1438
  try {
@@ -1168,6 +1481,12 @@ function createCloudProvider(backend, opts = {}) {
1168
1481
  } catch {
1169
1482
  }
1170
1483
  }
1484
+ if (box.portlessVncAlias) {
1485
+ try {
1486
+ await portlessUnalias(box.portlessVncAlias);
1487
+ } catch {
1488
+ }
1489
+ }
1171
1490
  try {
1172
1491
  await forgetBoxFromRelay(box.id);
1173
1492
  } catch {
@@ -1232,7 +1551,7 @@ function createCloudProvider(backend, opts = {}) {
1232
1551
  const handle = handleFor(box);
1233
1552
  const baseArgv = await backend.attachArgv(handle);
1234
1553
  const inner = renderInnerCommand(kind, opts2);
1235
- const argv = [...baseArgv.slice(1), "-t", inner];
1554
+ const argv = opts2?.detached ? [...baseArgv.slice(1), inner] : [...baseArgv.slice(1), "-t", inner];
1236
1555
  const fullArgv = [baseArgv[0], ...argv];
1237
1556
  const cleanup = backend.revokeAttachToken ? async () => {
1238
1557
  await backend.revokeAttachToken(handle, baseArgv);
@@ -1251,6 +1570,14 @@ function createCloudProvider(backend, opts = {}) {
1251
1570
  async resolveUrl(box, opts2) {
1252
1571
  const h = handleFor(box);
1253
1572
  const kind = opts2?.kind ?? "web";
1573
+ if (!opts2?.loopback) {
1574
+ if (kind === "web" && box.portlessAlias) {
1575
+ return box.portlessUrl ?? `https://${box.portlessAlias}.localhost`;
1576
+ }
1577
+ if (kind === "vnc" && box.portlessVncAlias) {
1578
+ return box.portlessVncUrl ?? `https://${box.portlessVncAlias}.localhost`;
1579
+ }
1580
+ }
1254
1581
  const port = kind === "vnc" ? CLOUD_VNC_PORT : box.cloud?.webPort ?? CLOUD_WEB_PROXY_PORT;
1255
1582
  if (backend.signedPreviewUrl) {
1256
1583
  const ttl = opts2?.ttl ?? DEFAULT_SIGNED_URL_TTL_SECONDS;
@@ -1266,7 +1593,16 @@ function createCloudProvider(backend, opts = {}) {
1266
1593
  // capability stub whose methods throw — the CLI's `agentbox checkpoint
1267
1594
  // create` then surfaces a clean "not supported" error rather than a
1268
1595
  // silent no-op.
1269
- checkpoint: makeCloudCheckpoint(backend)
1596
+ checkpoint: makeCloudCheckpoint(backend),
1597
+ // Extract the box's agent login(s) back to the host (~/.agentbox) so the
1598
+ // next box inherits the login. Lives on the base cloud provider (not inside
1599
+ // `checkpoint.create`) so it works even for providers that override the
1600
+ // whole `checkpoint` capability (vercel). The CLI calls this on
1601
+ // `checkpoint create --set-default`, while the box is guaranteed running.
1602
+ async extractAgentCredentials(box) {
1603
+ if (!box.cloud?.sandboxId) return [];
1604
+ return extractCloudAgentCredentials(backend, { sandboxId: box.cloud.sandboxId });
1605
+ }
1270
1606
  // stats is provider-optional; cloud backends without a metrics API just
1271
1607
  // omit it. Backends that have one can decorate the returned provider.
1272
1608
  };
@@ -1322,7 +1658,17 @@ function renderInnerCommand(kind, opts) {
1322
1658
  if (opts?.noTmux) {
1323
1659
  return fallback;
1324
1660
  }
1325
- return `command -v tmux >/dev/null || { echo "tmux not installed in sandbox"; exit 127; }; exec tmux new-session -A -c ${shellSingle(CLOUD_WORKSPACE_DIR)} -s ${shellSingle(sessionName)} ${shellSingle(fallback)}`;
1661
+ const sessionQ = shellSingle(sessionName);
1662
+ const cwdQ = shellSingle(CLOUD_WORKSPACE_DIR);
1663
+ const fallbackQ = shellSingle(fallback);
1664
+ const configSnippet = buildTmuxConfigShellSnippet(sessionName);
1665
+ const lines = [
1666
+ `command -v tmux >/dev/null || { echo "tmux not installed in sandbox"; exit 127; }`,
1667
+ `tmux has-session -t ${sessionQ} 2>/dev/null || tmux new-session -d -c ${cwdQ} -s ${sessionQ} ${fallbackQ}`,
1668
+ configSnippet
1669
+ ];
1670
+ if (opts?.detached) return lines.join("; ");
1671
+ return [...lines, `exec tmux attach -t ${sessionQ}`].join("; ");
1326
1672
  }
1327
1673
  function defaultSessionName(kind) {
1328
1674
  switch (kind) {
@@ -1361,6 +1707,9 @@ export {
1361
1707
  agentSpecsForCloud,
1362
1708
  listCloudCheckpoints,
1363
1709
  resolveCloudCheckpoint,
1364
- createCloudProvider
1710
+ writeCloudCheckpointManifest,
1711
+ removeCloudCheckpointDir,
1712
+ createCloudProvider,
1713
+ renderInnerCommand
1365
1714
  };
1366
- //# sourceMappingURL=chunk-NW5NYTQM.js.map
1715
+ //# sourceMappingURL=chunk-BXQMIEHC.js.map