@madarco/agentbox 0.7.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 (60) hide show
  1. package/dist/_cloud-attach-T727ZPRV.js +13 -0
  2. package/dist/{chunk-NW5NYTQM.js → chunk-67N47KUS.js} +359 -85
  3. package/dist/chunk-67N47KUS.js.map +1 -0
  4. package/dist/{chunk-NAVL4R34.js → chunk-6OZDFNBF.js} +1084 -516
  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-7KOEFGN2.js → chunk-FODMEHD3.js} +52 -14
  9. package/dist/chunk-FODMEHD3.js.map +1 -0
  10. package/dist/{chunk-UK72UQ5U.js → chunk-G3H2L3O2.js} +55 -4
  11. package/dist/chunk-G3H2L3O2.js.map +1 -0
  12. package/dist/{chunk-V5KZGB5V.js → chunk-LEV3KICD.js} +18 -2
  13. package/dist/chunk-LEV3KICD.js.map +1 -0
  14. package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js → cloud-poller-SUNA6ZQC-2RG5WPRN.js} +2 -2
  15. package/dist/{dist-R67WMLCF.js → dist-L4LCG5SJ.js} +120 -10
  16. package/dist/dist-L4LCG5SJ.js.map +1 -0
  17. package/dist/{dist-ETCFRVPA.js → dist-LOZBWMBF.js} +44 -20
  18. package/dist/{dist-QZGJIBT5.js → dist-ZODPD2I6.js} +142 -74
  19. package/dist/dist-ZODPD2I6.js.map +1 -0
  20. package/dist/index.js +3563 -845
  21. package/dist/index.js.map +1 -1
  22. package/dist/prepared-state-CL4CWXQA-ME4HSKDE.js +18 -0
  23. package/dist/prepared-state-CL4CWXQA-ME4HSKDE.js.map +1 -0
  24. package/package.json +4 -4
  25. package/runtime/daytona/custom-system-CLAUDE.md +39 -0
  26. package/runtime/docker/Dockerfile.box +22 -0
  27. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +1 -1
  28. package/runtime/docker/packages/ctl/dist/bin.cjs +1118 -71
  29. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json +66 -35
  30. package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +62 -1
  31. package/runtime/docker/packages/sandbox-docker/scripts/custom-system-CLAUDE.md +15 -4
  32. package/runtime/docker/packages/sandbox-docker/scripts/gh-shim +263 -0
  33. package/runtime/docker/packages/sandbox-docker/scripts/git-shim +131 -0
  34. package/runtime/docker/packages/sandbox-docker/scripts/opencode-agentbox-plugin.js +76 -0
  35. package/runtime/hetzner/agentbox-codex-hooks.json +66 -35
  36. package/runtime/hetzner/agentbox-setup-skill.md +1 -1
  37. package/runtime/hetzner/claude-managed-settings.json +62 -1
  38. package/runtime/hetzner/ctl.cjs +1118 -71
  39. package/runtime/hetzner/custom-system-CLAUDE.md +26 -14
  40. package/runtime/hetzner/gh-shim +263 -0
  41. package/runtime/hetzner/git-shim +131 -0
  42. package/runtime/hetzner/opencode-agentbox-plugin.js +76 -0
  43. package/runtime/hetzner/scripts/install-box.sh +11 -2
  44. package/runtime/relay/bin.cjs +927 -36
  45. package/share/agentbox-setup/SKILL.md +1 -1
  46. package/share/host-skills/agentbox/SKILL.md +29 -0
  47. package/share/host-skills/agentbox-info/SKILL.md +211 -0
  48. package/share/host-skills/codex/agentbox.md +35 -0
  49. package/share/host-skills/opencode/agentbox.md +26 -0
  50. package/dist/_cloud-attach-DMVH6GWO.js +0 -12
  51. package/dist/chunk-7KOEFGN2.js.map +0 -1
  52. package/dist/chunk-NAVL4R34.js.map +0 -1
  53. package/dist/chunk-NW5NYTQM.js.map +0 -1
  54. package/dist/chunk-UK72UQ5U.js.map +0 -1
  55. package/dist/chunk-V5KZGB5V.js.map +0 -1
  56. package/dist/dist-QZGJIBT5.js.map +0 -1
  57. package/dist/dist-R67WMLCF.js.map +0 -1
  58. /package/dist/{_cloud-attach-DMVH6GWO.js.map → _cloud-attach-T727ZPRV.js.map} +0 -0
  59. /package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js.map → cloud-poller-SUNA6ZQC-2RG5WPRN.js.map} +0 -0
  60. /package/dist/{dist-ETCFRVPA.js.map → dist-LOZBWMBF.js.map} +0 -0
@@ -3,9 +3,8 @@ import {
3
3
  CLAUDE_FORWARDED_ENV_KEYS,
4
4
  CODEX_FORWARDED_ENV_KEYS,
5
5
  OPENCODE_FORWARDED_ENV_KEYS,
6
- allocateProjectIndex,
7
6
  buildHostEnvFindArgs,
8
- detectGitRepos,
7
+ buildTmuxConfigShellSnippet,
9
8
  ensureRelay,
10
9
  forgetBoxFromRelay,
11
10
  generateRelayToken,
@@ -15,18 +14,23 @@ import {
15
14
  portlessGetUrl,
16
15
  portlessUnalias,
17
16
  projectDirSegment,
18
- readState,
19
- recordBox,
20
17
  registerBoxWithRelay,
21
- removeBoxRecord,
22
18
  sanitizeMnemonic,
23
19
  stageClaudeCredentialsForUpload,
24
20
  stageClaudeStaticForUpload,
25
21
  stageCodexCredentialsForUpload,
26
22
  stageCodexStaticForUpload,
27
23
  stageOpencodeCredentialsForUpload,
24
+ stageOpencodeStateForUpload,
28
25
  stageOpencodeStaticForUpload
29
- } from "./chunk-NAVL4R34.js";
26
+ } from "./chunk-6OZDFNBF.js";
27
+ import {
28
+ allocateProjectIndex,
29
+ detectGitRepos,
30
+ readState,
31
+ recordBox,
32
+ removeBoxRecord
33
+ } from "./chunk-BGK32PZE.js";
30
34
 
31
35
  // ../../packages/sandbox-cloud/dist/index.js
32
36
  import { randomBytes } from "crypto";
@@ -38,13 +42,17 @@ import { mkdtemp, rm as rm2, writeFile as writeFile2 } from "fs/promises";
38
42
  import { tmpdir } from "os";
39
43
  import { join as join2 } from "path";
40
44
  import { execa } from "execa";
41
- import { readFile as readFile2 } from "fs/promises";
45
+ import { mkdtemp as mkdtemp2, rm as rm3 } from "fs/promises";
46
+ import { tmpdir as tmpdir2 } from "os";
42
47
  import { join as join3 } from "path";
43
- import { parse as parseYaml } from "yaml";
44
48
  import { execa as execa2 } from "execa";
49
+ import { readFile as readFile2 } from "fs/promises";
50
+ import { join as join4 } from "path";
51
+ import { parse as parseYaml } from "yaml";
52
+ import { execa as execa3 } from "execa";
45
53
  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";
54
+ import { mkdtemp as mkdtemp3, rm as rm4 } from "fs/promises";
55
+ import { tmpdir as tmpdir3 } from "os";
48
56
  import {
49
57
  basename as hostBasename,
50
58
  dirname as hostDirname,
@@ -52,10 +60,10 @@ import {
52
60
  resolve as hostResolve
53
61
  } from "path";
54
62
  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";
63
+ import { execa as execa4 } from "execa";
64
+ import { mkdtemp as mkdtemp4, rm as rm5, stat } from "fs/promises";
65
+ import { tmpdir as tmpdir4 } from "os";
66
+ import { join as join5 } from "path";
59
67
  var CREDENTIALS_VOLUME = "agentbox-credentials";
60
68
  var AGENT_SPECS = [
61
69
  {
@@ -185,6 +193,33 @@ async function seedCredentialsOne(backend, handle, spec, opts) {
185
193
  await staged.cleanup();
186
194
  }
187
195
  }
196
+ var OPENCODE_STATE_DIR = "/home/vscode/.local/state/opencode";
197
+ async function seedOpencodeModelState(backend, handle, opts = {}) {
198
+ const log = opts.onLog ?? (() => {
199
+ });
200
+ const staged = await stageOpencodeStateForUpload();
201
+ if (staged.tarballPath === null) {
202
+ log("opencode: no host model selection to seed");
203
+ return;
204
+ }
205
+ try {
206
+ const remoteTar = "/tmp/agentbox-opencode-state.tar.gz";
207
+ await backend.uploadFile(handle, staged.tarballPath, remoteTar);
208
+ const res = await backend.exec(
209
+ handle,
210
+ `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}`
211
+ );
212
+ if (res.exitCode !== 0) {
213
+ log(
214
+ `opencode: model-state seed failed (exit ${String(res.exitCode)}); box falls back to OpenCode's default model. stderr: ${res.stderr.slice(-200)}`
215
+ );
216
+ return;
217
+ }
218
+ log("opencode: model selection seeded \u2713");
219
+ } finally {
220
+ await staged.cleanup();
221
+ }
222
+ }
188
223
  function agentSpecsForCloud() {
189
224
  return AGENT_SPECS.map((s) => ({
190
225
  kind: s.kind,
@@ -307,10 +342,105 @@ async function uploadEnvFiles(args) {
307
342
  }
308
343
  return { copied: list.length };
309
344
  }
345
+ var BOX_HOME = "/home/vscode";
346
+ async function uploadCarryPaths(args) {
347
+ const log = args.onLog ?? (() => {
348
+ });
349
+ if (args.entries.length === 0) {
350
+ return { copied: 0, errors: [], applied: [] };
351
+ }
352
+ const stage = await mkdtemp2(join3(tmpdir2(), "agentbox-carry-"));
353
+ const errors = [];
354
+ const applied = [];
355
+ let copied = 0;
356
+ try {
357
+ for (const [i, entry] of args.entries.entries()) {
358
+ const where = `carry[${String(i)}] "${entry.rawSrc}"`;
359
+ if (entry.kind === "missing") {
360
+ log(`${where}: skipped (missing on host, optional)`);
361
+ continue;
362
+ }
363
+ try {
364
+ await uploadOneEntry({
365
+ backend: args.backend,
366
+ handle: args.handle,
367
+ entry,
368
+ stageDir: stage,
369
+ index: i
370
+ });
371
+ copied += 1;
372
+ applied.push({ src: entry.absSrc, dest: entry.absDest, bytes: entry.bytes ?? 0 });
373
+ } catch (err) {
374
+ const msg = err instanceof Error ? err.message : String(err);
375
+ errors.push(`${where}: ${msg}`);
376
+ log(`${where}: failed: ${msg}`);
377
+ }
378
+ }
379
+ } finally {
380
+ await rm3(stage, { recursive: true, force: true });
381
+ }
382
+ return { copied, errors, applied };
383
+ }
384
+ async function uploadOneEntry(args) {
385
+ const { entry } = args;
386
+ if (entry.kind === "missing") return;
387
+ const boxDest = entry.absDest.startsWith("~/") ? `${BOX_HOME}/${entry.absDest.slice(2)}` : entry.absDest;
388
+ const isDir = entry.kind === "dir";
389
+ const parentDir = isDir ? boxDest : dirnameUnix(boxDest);
390
+ const localTar = join3(args.stageDir, `carry-${String(args.index)}.tar`);
391
+ const tarArgs = isDir ? ["-C", entry.absSrc, "-cf", localTar, "."] : ["-C", dirnameUnix(entry.absSrc), "-cf", localTar, basenameUnix(entry.absSrc)];
392
+ const packed = await execa2("tar", tarArgs, { reject: false });
393
+ if (packed.exitCode !== 0) {
394
+ throw new Error(`tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
395
+ }
396
+ const remoteTar = `/tmp/agentbox-carry-${String(args.index)}.tar`;
397
+ await args.backend.uploadFile(args.handle, localTar, remoteTar);
398
+ const mode = entry.mode !== void 0 ? entry.mode.toString(8).padStart(4, "0") : "";
399
+ const uid = entry.user ?? 1e3;
400
+ const fileBase = !isDir ? basenameUnix(entry.absSrc) : "";
401
+ const destBase = !isDir ? basenameUnix(boxDest) : "";
402
+ const renameNeeded = !isDir && fileBase !== destBase;
403
+ const parts = [
404
+ `mkdir -p ${shellQuote(parentDir)}`,
405
+ 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`
406
+ ];
407
+ if (renameNeeded) {
408
+ parts.push(
409
+ `mv ${shellQuote(`${parentDir}/${fileBase}`)} ${shellQuote(boxDest)}`
410
+ );
411
+ }
412
+ if (mode) parts.push(`chmod -R ${mode} ${shellQuote(boxDest)}`);
413
+ parts.push(`chown -R ${String(uid)}:${String(uid)} ${shellQuote(boxDest)}`);
414
+ if (boxDest.startsWith(BOX_HOME + "/") && parentDir !== BOX_HOME) {
415
+ parts.push(
416
+ `parent=$(dirname ${shellQuote(boxDest)}); while [ "$parent" != "${BOX_HOME}" ] && [ "$parent" != "/" ]; do chown ${String(uid)}:${String(uid)} "$parent"; parent=$(dirname "$parent"); done`
417
+ );
418
+ }
419
+ parts.push(`rm -f ${remoteTar}`);
420
+ const cmd = parts.join(" && ");
421
+ const res = await args.backend.exec(args.handle, cmd);
422
+ if (res.exitCode !== 0) {
423
+ throw new Error(
424
+ `in-box extract failed (exit ${String(res.exitCode)}): ${(res.stderr || res.stdout).slice(-300)}`
425
+ );
426
+ }
427
+ }
428
+ function dirnameUnix(p) {
429
+ const i = p.lastIndexOf("/");
430
+ if (i <= 0) return "/";
431
+ return p.slice(0, i);
432
+ }
433
+ function basenameUnix(p) {
434
+ const i = p.lastIndexOf("/");
435
+ return i < 0 ? p : p.slice(i + 1);
436
+ }
437
+ function shellQuote(s) {
438
+ return `'${s.replace(/'/g, `'\\''`)}'`;
439
+ }
310
440
  async function readExposedServicePorts(workspacePath) {
311
441
  let text;
312
442
  try {
313
- text = await readFile2(join3(workspacePath, "agentbox.yaml"), "utf8");
443
+ text = await readFile2(join4(workspacePath, "agentbox.yaml"), "utf8");
314
444
  } catch {
315
445
  return [];
316
446
  }
@@ -366,10 +496,10 @@ async function uploadToCloudBox(backend, handle, hostSrc, boxDst) {
366
496
  finalName = posix.basename(boxDst);
367
497
  }
368
498
  const finalPath = boxParent === "/" ? `/${finalName}` : `${boxParent}/${finalName}`;
369
- const stage = await mkdtemp2(hostJoin(tmpdir2(), "agentbox-cp-up-"));
499
+ const stage = await mkdtemp3(hostJoin(tmpdir3(), "agentbox-cp-up-"));
370
500
  const localTar = hostJoin(stage, "payload.tar.gz");
371
501
  try {
372
- await execa2("tar", ["-C", srcParent, "-czf", localTar, srcBasename], {
502
+ await execa3("tar", ["-C", srcParent, "-czf", localTar, srcBasename], {
373
503
  env: { ...process.env, COPYFILE_DISABLE: "1" }
374
504
  });
375
505
  await backend.uploadFile(handle, localTar, REMOTE_UP_TAR);
@@ -395,14 +525,14 @@ async function uploadToCloudBox(backend, handle, hostSrc, boxDst) {
395
525
  throw new Error(`cloud upload extract failed: ${r.stderr || r.stdout}`);
396
526
  }
397
527
  } finally {
398
- await rm3(stage, { recursive: true, force: true });
528
+ await rm4(stage, { recursive: true, force: true });
399
529
  }
400
530
  return { finalPath };
401
531
  }
402
532
  async function pullCloudDirContents(backend, handle, boxSrcDir, hostDstDir) {
403
533
  const dstAbs = hostResolve(hostDstDir);
404
534
  mkdirSync(dstAbs, { recursive: true });
405
- const stage = await mkdtemp2(hostJoin(tmpdir2(), "agentbox-pull-"));
535
+ const stage = await mkdtemp3(hostJoin(tmpdir3(), "agentbox-pull-"));
406
536
  const localTar = hostJoin(stage, "payload.tar.gz");
407
537
  try {
408
538
  const packScript = [
@@ -415,11 +545,11 @@ async function pullCloudDirContents(backend, handle, boxSrcDir, hostDstDir) {
415
545
  throw new Error(`cloud workspace pack failed: ${r.stderr || r.stdout}`);
416
546
  }
417
547
  await backend.downloadFile(handle, REMOTE_DOWN_TAR, localTar);
418
- await execa2("tar", ["-xzf", localTar, "-C", dstAbs]);
548
+ await execa3("tar", ["-xzf", localTar, "-C", dstAbs]);
419
549
  await backend.exec(handle, `rm -f ${quoteShellArg(REMOTE_DOWN_TAR)}`).catch(() => {
420
550
  });
421
551
  } finally {
422
- await rm3(stage, { recursive: true, force: true });
552
+ await rm4(stage, { recursive: true, force: true });
423
553
  }
424
554
  return { finalPath: dstAbs };
425
555
  }
@@ -439,7 +569,7 @@ async function downloadFromCloudBox(backend, handle, boxSrc, hostDst) {
439
569
  }
440
570
  mkdirSync(hostParent, { recursive: true });
441
571
  const finalPath = hostJoin(hostParent, finalName);
442
- const stage = await mkdtemp2(hostJoin(tmpdir2(), "agentbox-cp-down-"));
572
+ const stage = await mkdtemp3(hostJoin(tmpdir3(), "agentbox-cp-down-"));
443
573
  const localTar = hostJoin(stage, "payload.tar.gz");
444
574
  try {
445
575
  const packScript = [
@@ -452,14 +582,14 @@ async function downloadFromCloudBox(backend, handle, boxSrc, hostDst) {
452
582
  throw new Error(`cloud download pack failed: ${r.stderr || r.stdout}`);
453
583
  }
454
584
  await backend.downloadFile(handle, REMOTE_DOWN_TAR, localTar);
455
- await execa2("tar", ["-xzf", localTar, "-C", hostParent]);
585
+ await execa3("tar", ["-xzf", localTar, "-C", hostParent]);
456
586
  if (finalName !== srcBasename) {
457
587
  renameSync(hostJoin(hostParent, srcBasename), finalPath);
458
588
  }
459
589
  await backend.exec(handle, `rm -f ${quoteShellArg(REMOTE_DOWN_TAR)}`).catch(() => {
460
590
  });
461
591
  } finally {
462
- await rm3(stage, { recursive: true, force: true });
592
+ await rm4(stage, { recursive: true, force: true });
463
593
  }
464
594
  return { finalPath };
465
595
  }
@@ -547,24 +677,29 @@ async function seedCloudWorkspace(args) {
547
677
  const nested = repos.filter((r) => r.kind === "nested");
548
678
  if (root) {
549
679
  log(
550
- nested.length > 0 ? `seeding /workspace from git bundle (+${String(nested.length)} nested repo${nested.length === 1 ? "" : "s"})` : "seeding /workspace from git bundle"
680
+ 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
681
  );
552
- await seedFromGitBundle({
682
+ await seedFromGitClone({
553
683
  backend: args.backend,
554
684
  handle: args.handle,
555
685
  hostRepo: root.hostMainRepo,
556
686
  branch: args.branch,
557
- workspaceDir
687
+ workspaceDir,
688
+ bundleDepth: args.bundleDepth,
689
+ fromBranch: args.fromBranch,
690
+ onLog: log
558
691
  });
559
692
  for (const r of nested) {
560
693
  const sub = `${workspaceDir}/${r.relPathFromWorkspace}`;
561
- log(`seeding nested repo ${r.relPathFromWorkspace} from git bundle`);
562
- await seedFromGitBundle({
694
+ log(`seeding nested repo ${r.relPathFromWorkspace} from shallow git clone`);
695
+ await seedFromGitClone({
563
696
  backend: args.backend,
564
697
  handle: args.handle,
565
698
  hostRepo: r.hostMainRepo,
566
699
  branch: args.branch,
567
- workspaceDir: sub
700
+ workspaceDir: sub,
701
+ bundleDepth: args.bundleDepth,
702
+ onLog: log
568
703
  });
569
704
  }
570
705
  return { fromGit: true, branch: args.branch };
@@ -580,41 +715,52 @@ async function seedCloudWorkspace(args) {
580
715
  }
581
716
  var STASH_CARRYOVER_REF = "refs/agentbox-carryover/stash";
582
717
  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");
718
+ var DEFAULT_BUNDLE_DEPTH = 200;
719
+ var LARGE_BUNDLE_DEPTH = 100;
720
+ var LARGE_BUNDLE_THRESHOLD_BYTES = 20 * 1024 * 1024;
721
+ async function seedFromGitClone(args) {
722
+ const log = args.onLog ?? (() => {
723
+ });
724
+ const stage = await mkdtemp4(join5(tmpdir4(), "agentbox-clone-"));
725
+ const cloneDir = join5(stage, "clone");
726
+ const tarPath = join5(stage, "workspace.tar.gz");
727
+ const untrackedTarPath = join5(stage, "untracked.tar.gz");
587
728
  const stashSha = await safeStashCreate(args.hostRepo);
588
729
  const untrackedSize = await maybeBuildUntrackedTar(args.hostRepo, untrackedTarPath);
589
730
  let stashRefCreated = false;
590
731
  try {
591
732
  if (stashSha) {
592
- const ref = await execa3(
733
+ const ref = await execa4(
593
734
  "git",
594
735
  ["-C", args.hostRepo, "update-ref", STASH_CARRYOVER_REF, stashSha],
595
736
  { reject: false }
596
737
  );
597
738
  stashRefCreated = ref.exitCode === 0;
598
739
  }
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;
740
+ const configured = args.bundleDepth;
741
+ const adaptive = configured === void 0;
742
+ const initialDepth = adaptive ? DEFAULT_BUNDLE_DEPTH : configured === 0 ? null : configured;
743
+ log(
744
+ adaptive ? `clone: depth=${String(DEFAULT_BUNDLE_DEPTH)} (default, adaptive)` : initialDepth === null ? "clone: depth=full (configured)" : `clone: depth=${String(initialDepth)} (configured)`
745
+ );
746
+ await runShallowClone(args.hostRepo, cloneDir, initialDepth, stashRefCreated, args.fromBranch);
747
+ await tarCloneDir(cloneDir, tarPath);
748
+ if (adaptive && initialDepth !== null) {
749
+ const size = await safeFileSize(tarPath);
750
+ if (size > LARGE_BUNDLE_THRESHOLD_BYTES) {
751
+ const mb = (size / (1024 * 1024)).toFixed(1);
752
+ log(
753
+ `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)}`
754
+ );
755
+ await rm5(cloneDir, { recursive: true, force: true });
756
+ await rm5(tarPath, { force: true });
757
+ await runShallowClone(args.hostRepo, cloneDir, LARGE_BUNDLE_DEPTH, stashRefCreated, args.fromBranch);
758
+ await tarCloneDir(cloneDir, tarPath);
759
+ }
614
760
  }
615
761
  const remoteUrl = await readOriginUrl(args.hostRepo);
616
- const remoteBundle = "/tmp/agentbox-workspace.bundle";
617
- await args.backend.uploadFile(args.handle, bundlePath, remoteBundle);
762
+ const remoteTar = "/tmp/agentbox-workspace.tar.gz";
763
+ await args.backend.uploadFile(args.handle, tarPath, remoteTar);
618
764
  if (untrackedSize > 0) {
619
765
  await args.backend.uploadFile(args.handle, untrackedTarPath, REMOTE_UNTRACKED_TAR);
620
766
  }
@@ -633,50 +779,77 @@ async function seedFromGitBundle(args) {
633
779
  // Move out of any cwd we might inherit from Daytona's executeCommand
634
780
  // before we delete /workspace. The agentbox image bakes WORKDIR
635
781
  // /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".
782
+ // the next process inherits a stale cwd FD and tar's children fail
783
+ // with "Unable to read current working directory".
638
784
  `cd /tmp`,
639
785
  SUDO,
640
- // rm -rf only the directory we're about to clone into — for nested
786
+ // rm -rf only the directory we're about to extract into — for nested
641
787
  // repos this is just `/workspace/<rel>`, so the root clone (already
642
788
  // at `/workspace`) is preserved.
643
789
  `$SUDO rm -rf ${quoteShellArgv([args.workspaceDir])}`,
644
790
  `$SUDO mkdir -p ${quoteShellArgv([args.workspaceDir])}`,
645
791
  `$SUDO chown "$(id -un):$(id -gn)" ${quoteShellArgv([args.workspaceDir])}`,
646
- `git clone ${quoteShellArgv([remoteBundle, args.workspaceDir])}`,
792
+ `tar -C ${quoteShellArgv([args.workspaceDir])} -xzf ${quoteShellArgv([remoteTar])}`,
647
793
  setOrigin,
648
- `git -C ${quoteShellArgv([args.workspaceDir])} fetch ${quoteShellArgv([remoteBundle])} --tags '+refs/heads/*:refs/remotes/bundle/*' || true`,
649
794
  `git -C ${quoteShellArgv([args.workspaceDir])} checkout -B ${quoteShellArgv([args.branch])}`,
650
795
  ...carryOverSteps,
651
- `rm -f ${quoteShellArgv([remoteBundle])}`
796
+ `rm -f ${quoteShellArgv([remoteTar])}`
652
797
  ].join("\n");
653
798
  const r = await args.backend.exec(args.handle, bashScript(script));
654
799
  if (r.exitCode !== 0) {
655
- throw new Error(`workspace seed (bundle) failed: ${r.stderr || r.stdout}`);
800
+ throw new Error(`workspace seed (clone) failed: ${r.stderr || r.stdout}`);
656
801
  }
657
802
  } finally {
658
803
  if (stashRefCreated) {
659
- await execa3("git", ["-C", args.hostRepo, "update-ref", "-d", STASH_CARRYOVER_REF], {
804
+ await execa4("git", ["-C", args.hostRepo, "update-ref", "-d", STASH_CARRYOVER_REF], {
660
805
  reject: false
661
806
  });
662
807
  }
663
- await rm4(stage, { recursive: true, force: true });
808
+ await rm5(stage, { recursive: true, force: true });
809
+ }
810
+ }
811
+ async function runShallowClone(hostRepo, cloneDir, depth, includeStashRef, fromBranch) {
812
+ const cloneArgs = ["clone", "--no-checkout", "--quiet"];
813
+ if (depth !== null) cloneArgs.push(`--depth=${String(depth)}`);
814
+ if (fromBranch) cloneArgs.push("--branch", fromBranch);
815
+ cloneArgs.push(`file://${hostRepo}`, cloneDir);
816
+ await execa4("git", cloneArgs);
817
+ if (includeStashRef) {
818
+ const fetchArgs = ["-C", cloneDir, "fetch", "--quiet"];
819
+ if (depth !== null) fetchArgs.push(`--depth=${String(depth)}`);
820
+ fetchArgs.push(
821
+ `file://${hostRepo}`,
822
+ `+${STASH_CARRYOVER_REF}:refs/remotes/origin/agentbox-carryover/stash`
823
+ );
824
+ await execa4("git", fetchArgs, { reject: false });
825
+ }
826
+ }
827
+ async function tarCloneDir(cloneDir, outPath) {
828
+ await execa4("tar", ["-C", cloneDir, "-czf", outPath, "."], {
829
+ env: { ...process.env, COPYFILE_DISABLE: "1" }
830
+ });
831
+ }
832
+ async function safeFileSize(path) {
833
+ try {
834
+ return (await stat(path)).size;
835
+ } catch {
836
+ return 0;
664
837
  }
665
838
  }
666
839
  async function safeStashCreate(hostRepo) {
667
- const r = await execa3("git", ["-C", hostRepo, "stash", "create"], { reject: false });
840
+ const r = await execa4("git", ["-C", hostRepo, "stash", "create"], { reject: false });
668
841
  if (r.exitCode !== 0) return null;
669
842
  const sha = r.stdout.trim();
670
843
  return sha.length > 0 ? sha : null;
671
844
  }
672
845
  async function maybeBuildUntrackedTar(hostRepo, outPath) {
673
- const list = await execa3(
846
+ const list = await execa4(
674
847
  "git",
675
848
  ["-C", hostRepo, "ls-files", "--others", "--exclude-standard", "-z"],
676
849
  { reject: false }
677
850
  );
678
851
  if (list.exitCode !== 0 || list.stdout.length === 0) return 0;
679
- const tar = await execa3(
852
+ const tar = await execa4(
680
853
  "tar",
681
854
  ["-C", hostRepo, "--null", "-T", "-", "-czf", outPath],
682
855
  {
@@ -687,24 +860,24 @@ async function maybeBuildUntrackedTar(hostRepo, outPath) {
687
860
  );
688
861
  if (tar.exitCode !== 0) return 0;
689
862
  try {
690
- const { stat } = await import("fs/promises");
691
- const s = await stat(outPath);
863
+ const { stat: stat2 } = await import("fs/promises");
864
+ const s = await stat2(outPath);
692
865
  return s.size;
693
866
  } catch {
694
867
  return 0;
695
868
  }
696
869
  }
697
870
  async function readOriginUrl(hostRepo) {
698
- const r = await execa3("git", ["-C", hostRepo, "remote", "get-url", "origin"], { reject: false });
871
+ const r = await execa4("git", ["-C", hostRepo, "remote", "get-url", "origin"], { reject: false });
699
872
  if (r.exitCode !== 0) return null;
700
873
  const out = (r.stdout ?? "").trim();
701
874
  return out.length > 0 ? out : null;
702
875
  }
703
876
  async function seedFromTar(args) {
704
- const stage = await mkdtemp3(join4(tmpdir3(), "agentbox-tar-"));
705
- const tarPath = join4(stage, "workspace.tar.gz");
877
+ const stage = await mkdtemp4(join5(tmpdir4(), "agentbox-tar-"));
878
+ const tarPath = join5(stage, "workspace.tar.gz");
706
879
  try {
707
- await execa3("tar", ["-C", args.hostDir, "-czf", tarPath, "."]);
880
+ await execa4("tar", ["-C", args.hostDir, "-czf", tarPath, "."]);
708
881
  const remoteTar = "/tmp/agentbox-workspace.tar.gz";
709
882
  await args.backend.uploadFile(args.handle, tarPath, remoteTar);
710
883
  const SUDO = `if command -v sudo >/dev/null 2>&1; then SUDO='sudo -n'; else SUDO=''; fi`;
@@ -728,7 +901,7 @@ async function seedFromTar(args) {
728
901
  throw new Error(`workspace seed (tar) failed: ${r.stderr || r.stdout}`);
729
902
  }
730
903
  } finally {
731
- await rm4(stage, { recursive: true, force: true });
904
+ await rm5(stage, { recursive: true, force: true });
732
905
  }
733
906
  }
734
907
  var CLOUD_WORKSPACE_DIR = "/workspace";
@@ -759,18 +932,28 @@ function parseLoopbackPort(url) {
759
932
  return void 0;
760
933
  }
761
934
  }
762
- async function bootstrapPortlessForCloudBox(backend, handle, args) {
763
- const localPort = parseLoopbackPort(args.webPreviewUrl);
935
+ async function registerHostPortlessAlias(args) {
936
+ const localPort = parseLoopbackPort(args.previewUrl);
764
937
  if (localPort === void 0) return void 0;
765
- const ok = await portlessAlias(args.boxName, localPort);
938
+ const ok = await portlessAlias(args.alias, localPort);
766
939
  if (!ok) {
767
940
  args.onLog(
768
- `portless: alias not registered (portless CLI missing or not running) \u2014 host URL stays http://127.0.0.1:${String(localPort)}`
941
+ `portless: ${args.label} alias not registered (portless CLI missing or not running) \u2014 host URL stays http://127.0.0.1:${String(localPort)}`
769
942
  );
770
943
  return void 0;
771
944
  }
772
- const url = await portlessGetUrl(args.boxName);
945
+ const url = await portlessGetUrl(args.alias);
773
946
  args.onLog(`portless alias ${url} -> 127.0.0.1:${String(localPort)}`);
947
+ return url;
948
+ }
949
+ async function bootstrapPortlessForCloudBox(backend, handle, args) {
950
+ const url = await registerHostPortlessAlias({
951
+ alias: args.boxName,
952
+ previewUrl: args.webPreviewUrl,
953
+ label: "web",
954
+ onLog: args.onLog
955
+ });
956
+ if (!url) return void 0;
774
957
  if (backend.startInBoxPortless) {
775
958
  const mode = parsePortlessUrl(url) ?? { proxyPort: DEFAULT_PORTLESS_PROXY_PORT, tls: false };
776
959
  try {
@@ -860,8 +1043,10 @@ function createCloudProvider(backend, opts = {}) {
860
1043
  AGENTBOX_BOX_ID: id,
861
1044
  AGENTBOX_BOX_NAME: name,
862
1045
  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)}`,
1046
+ // In-sandbox relay is on the box's loopback at the in-box port.
1047
+ // 8788 is distinct from the host relay's 8787 so a nested agentbox
1048
+ // run inside the box can claim :8787 without colliding.
1049
+ AGENTBOX_RELAY_URL: `http://127.0.0.1:${String(8788)}`,
865
1050
  AGENTBOX_RELAY_TOKEN: relayToken,
866
1051
  AGENTBOX_BRIDGE_TOKEN: bridgeToken,
867
1052
  ...agentVolumes.env
@@ -879,6 +1064,8 @@ function createCloudProvider(backend, opts = {}) {
879
1064
  workspacePath: req.workspacePath,
880
1065
  branch,
881
1066
  workspaceDir: CLOUD_WORKSPACE_DIR,
1067
+ bundleDepth: req.bundleDepth,
1068
+ fromBranch: req.fromBranch,
882
1069
  onLog: log
883
1070
  });
884
1071
  }
@@ -889,6 +1076,7 @@ function createCloudProvider(backend, opts = {}) {
889
1076
  onLog: log
890
1077
  });
891
1078
  }
1079
+ await seedOpencodeModelState(backend, handle, { onLog: log });
892
1080
  if (req.envFilesToImport && req.envFilesToImport.length > 0) {
893
1081
  const { copied } = await uploadEnvFiles({
894
1082
  backend,
@@ -900,13 +1088,28 @@ function createCloudProvider(backend, opts = {}) {
900
1088
  });
901
1089
  if (copied > 0) log(`copied ${String(copied)} env/config file(s) into /workspace`);
902
1090
  }
1091
+ let carrySummary;
1092
+ if (req.carry && req.carry.length > 0) {
1093
+ log(`carry: copying ${String(req.carry.length)} host path(s) into the box`);
1094
+ const result = await uploadCarryPaths({
1095
+ backend,
1096
+ handle,
1097
+ entries: req.carry,
1098
+ onLog: log
1099
+ });
1100
+ log(`carry: copied ${String(result.copied)}/${String(req.carry.length)} entry/entries`);
1101
+ for (const err of result.errors) log(`carry: ${err}`);
1102
+ if (result.applied.length > 0) {
1103
+ carrySummary = { count: result.applied.length, entries: result.applied };
1104
+ }
1105
+ }
903
1106
  log("launching agentbox-ctl daemon");
904
1107
  await launchCloudCtlDaemon({
905
1108
  backend,
906
1109
  handle,
907
1110
  boxId: id,
908
1111
  boxName: name,
909
- relayUrl: `http://127.0.0.1:${String(8787)}`,
1112
+ relayUrl: `http://127.0.0.1:${String(8788)}`,
910
1113
  relayToken,
911
1114
  bridgeToken
912
1115
  });
@@ -950,6 +1153,29 @@ function createCloudProvider(backend, opts = {}) {
950
1153
  portlessUrlResolved = r.url;
951
1154
  }
952
1155
  }
1156
+ let vncPreview;
1157
+ if (portlessOpt && vncEnabled) {
1158
+ try {
1159
+ vncPreview = await backend.previewUrl(handle, CLOUD_VNC_PORT);
1160
+ } catch {
1161
+ vncPreview = void 0;
1162
+ }
1163
+ }
1164
+ let portlessVncAliasName;
1165
+ let portlessVncUrlResolved;
1166
+ if (portlessOpt && vncPreview) {
1167
+ const vncAlias = `vnc-${name}`;
1168
+ const url = await registerHostPortlessAlias({
1169
+ alias: vncAlias,
1170
+ previewUrl: vncPreview.url,
1171
+ label: "vnc",
1172
+ onLog: log
1173
+ });
1174
+ if (url) {
1175
+ portlessVncAliasName = vncAlias;
1176
+ portlessVncUrlResolved = url;
1177
+ }
1178
+ }
953
1179
  const servicePorts = await readExposedServicePorts(req.workspacePath);
954
1180
  const servicePreviews = {};
955
1181
  for (const port of servicePorts) {
@@ -962,7 +1188,7 @@ function createCloudProvider(backend, opts = {}) {
962
1188
  }
963
1189
  let relayPreview;
964
1190
  try {
965
- relayPreview = await backend.previewUrl(handle, 8787);
1191
+ relayPreview = await backend.previewUrl(handle, 8788);
966
1192
  } catch {
967
1193
  relayPreview = void 0;
968
1194
  }
@@ -1005,8 +1231,11 @@ function createCloudProvider(backend, opts = {}) {
1005
1231
  relayToken,
1006
1232
  withPlaywright: req.withPlaywright,
1007
1233
  withEnv: req.withEnv,
1234
+ carry: carrySummary,
1008
1235
  portlessAlias: portlessAliasName,
1009
1236
  portlessUrl: portlessUrlResolved,
1237
+ portlessVncAlias: portlessVncAliasName,
1238
+ portlessVncUrl: portlessVncUrlResolved,
1010
1239
  vncEnabled,
1011
1240
  vncPassword,
1012
1241
  vncContainerPort: vncEnabled ? CLOUD_VNC_PORT : void 0,
@@ -1069,7 +1298,7 @@ function createCloudProvider(backend, opts = {}) {
1069
1298
  }
1070
1299
  let relayPreview;
1071
1300
  try {
1072
- relayPreview = await backend.previewUrl(h, 8787);
1301
+ relayPreview = await backend.previewUrl(h, 8788);
1073
1302
  } catch {
1074
1303
  relayPreview = box.cloud?.relayPreviewUrl ? { url: box.cloud.relayPreviewUrl, token: box.cloud.relayPreviewToken } : void 0;
1075
1304
  }
@@ -1093,10 +1322,31 @@ function createCloudProvider(backend, opts = {}) {
1093
1322
  portlessUrlResolved = r.url;
1094
1323
  }
1095
1324
  }
1325
+ let portlessVncAliasName = box.portlessVncAlias;
1326
+ let portlessVncUrlResolved = box.portlessVncUrl;
1327
+ if (box.portlessVncAlias && box.vncEnabled) {
1328
+ try {
1329
+ const vncPreview = await backend.previewUrl(h, CLOUD_VNC_PORT);
1330
+ const url = await registerHostPortlessAlias({
1331
+ alias: box.portlessVncAlias,
1332
+ previewUrl: vncPreview.url,
1333
+ label: "vnc",
1334
+ onLog: () => {
1335
+ }
1336
+ });
1337
+ if (url) {
1338
+ portlessVncAliasName = box.portlessVncAlias;
1339
+ portlessVncUrlResolved = url;
1340
+ }
1341
+ } catch {
1342
+ }
1343
+ }
1096
1344
  const next = {
1097
1345
  ...box,
1098
1346
  portlessAlias: portlessAliasName,
1099
1347
  portlessUrl: portlessUrlResolved,
1348
+ portlessVncAlias: portlessVncAliasName,
1349
+ portlessVncUrl: portlessVncUrlResolved,
1100
1350
  cloud: {
1101
1351
  ...box.cloud ?? { backend: providerName, sandboxId: h.sandboxId },
1102
1352
  webPort,
@@ -1111,7 +1361,7 @@ function createCloudProvider(backend, opts = {}) {
1111
1361
  handle: h,
1112
1362
  boxId: box.id,
1113
1363
  boxName: box.name,
1114
- relayUrl: `http://127.0.0.1:${String(8787)}`,
1364
+ relayUrl: `http://127.0.0.1:${String(8788)}`,
1115
1365
  relayToken: box.relayToken ?? "",
1116
1366
  bridgeToken: box.cloud?.bridgeToken
1117
1367
  });
@@ -1168,6 +1418,12 @@ function createCloudProvider(backend, opts = {}) {
1168
1418
  } catch {
1169
1419
  }
1170
1420
  }
1421
+ if (box.portlessVncAlias) {
1422
+ try {
1423
+ await portlessUnalias(box.portlessVncAlias);
1424
+ } catch {
1425
+ }
1426
+ }
1171
1427
  try {
1172
1428
  await forgetBoxFromRelay(box.id);
1173
1429
  } catch {
@@ -1232,7 +1488,7 @@ function createCloudProvider(backend, opts = {}) {
1232
1488
  const handle = handleFor(box);
1233
1489
  const baseArgv = await backend.attachArgv(handle);
1234
1490
  const inner = renderInnerCommand(kind, opts2);
1235
- const argv = [...baseArgv.slice(1), "-t", inner];
1491
+ const argv = opts2?.detached ? [...baseArgv.slice(1), inner] : [...baseArgv.slice(1), "-t", inner];
1236
1492
  const fullArgv = [baseArgv[0], ...argv];
1237
1493
  const cleanup = backend.revokeAttachToken ? async () => {
1238
1494
  await backend.revokeAttachToken(handle, baseArgv);
@@ -1251,6 +1507,14 @@ function createCloudProvider(backend, opts = {}) {
1251
1507
  async resolveUrl(box, opts2) {
1252
1508
  const h = handleFor(box);
1253
1509
  const kind = opts2?.kind ?? "web";
1510
+ if (!opts2?.loopback) {
1511
+ if (kind === "web" && box.portlessAlias) {
1512
+ return box.portlessUrl ?? `https://${box.portlessAlias}.localhost`;
1513
+ }
1514
+ if (kind === "vnc" && box.portlessVncAlias) {
1515
+ return box.portlessVncUrl ?? `https://${box.portlessVncAlias}.localhost`;
1516
+ }
1517
+ }
1254
1518
  const port = kind === "vnc" ? CLOUD_VNC_PORT : box.cloud?.webPort ?? CLOUD_WEB_PROXY_PORT;
1255
1519
  if (backend.signedPreviewUrl) {
1256
1520
  const ttl = opts2?.ttl ?? DEFAULT_SIGNED_URL_TTL_SECONDS;
@@ -1322,7 +1586,17 @@ function renderInnerCommand(kind, opts) {
1322
1586
  if (opts?.noTmux) {
1323
1587
  return fallback;
1324
1588
  }
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)}`;
1589
+ const sessionQ = shellSingle(sessionName);
1590
+ const cwdQ = shellSingle(CLOUD_WORKSPACE_DIR);
1591
+ const fallbackQ = shellSingle(fallback);
1592
+ const configSnippet = buildTmuxConfigShellSnippet(sessionName);
1593
+ const lines = [
1594
+ `command -v tmux >/dev/null || { echo "tmux not installed in sandbox"; exit 127; }`,
1595
+ `tmux has-session -t ${sessionQ} 2>/dev/null || tmux new-session -d -c ${cwdQ} -s ${sessionQ} ${fallbackQ}`,
1596
+ configSnippet
1597
+ ];
1598
+ if (opts?.detached) return lines.join("; ");
1599
+ return [...lines, `exec tmux attach -t ${sessionQ}`].join("; ");
1326
1600
  }
1327
1601
  function defaultSessionName(kind) {
1328
1602
  switch (kind) {
@@ -1363,4 +1637,4 @@ export {
1363
1637
  resolveCloudCheckpoint,
1364
1638
  createCloudProvider
1365
1639
  };
1366
- //# sourceMappingURL=chunk-NW5NYTQM.js.map
1640
+ //# sourceMappingURL=chunk-67N47KUS.js.map