@madarco/agentbox 0.8.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 (46) hide show
  1. package/dist/{_cloud-attach-T727ZPRV.js → _cloud-attach-ZXBCNWJX.js} +4 -4
  2. package/dist/{chunk-67N47KUS.js → chunk-BXQMIEHC.js} +106 -31
  3. package/dist/chunk-BXQMIEHC.js.map +1 -0
  4. package/dist/{chunk-FODMEHD3.js → chunk-GU5LW4B5.js} +341 -25
  5. package/dist/chunk-GU5LW4B5.js.map +1 -0
  6. package/dist/{chunk-BGK32PZE.js → chunk-KL36BRN4.js} +2 -2
  7. package/dist/chunk-KL36BRN4.js.map +1 -0
  8. package/dist/chunk-MTVI44DW.js +662 -0
  9. package/dist/chunk-MTVI44DW.js.map +1 -0
  10. package/dist/{chunk-6OZDFNBF.js → chunk-NCJP5MTN.js} +201 -44
  11. package/dist/chunk-NCJP5MTN.js.map +1 -0
  12. package/dist/{dist-LOZBWMBF.js → dist-32EZBYG4.js} +9 -3
  13. package/dist/{dist-L4LCG5SJ.js → dist-CX5CGVEB.js} +4 -4
  14. package/dist/{dist-ZODPD2I6.js → dist-GDHP34ZK.js} +8 -10
  15. package/dist/dist-GDHP34ZK.js.map +1 -0
  16. package/dist/dist-XML54CNB.js +849 -0
  17. package/dist/dist-XML54CNB.js.map +1 -0
  18. package/dist/index.js +636 -340
  19. package/dist/index.js.map +1 -1
  20. package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js → prepared-state-CL4CWXQA-H5THETIM.js} +2 -2
  21. package/package.json +7 -5
  22. package/runtime/docker/packages/ctl/dist/bin.cjs +98 -29
  23. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
  24. package/runtime/hetzner/agentbox-vnc-start +15 -1
  25. package/runtime/hetzner/ctl.cjs +98 -29
  26. package/runtime/relay/bin.cjs +229 -37
  27. package/runtime/vercel/agentbox-checkpoint-cleanup +52 -0
  28. package/runtime/vercel/agentbox-codex-hooks.json +68 -0
  29. package/runtime/vercel/agentbox-open +28 -0
  30. package/runtime/vercel/agentbox-setup-skill.md +196 -0
  31. package/runtime/vercel/agentbox-vnc-start +91 -0
  32. package/runtime/vercel/claude-managed-settings.json +115 -0
  33. package/runtime/vercel/ctl.cjs +23466 -0
  34. package/runtime/vercel/custom-system-CLAUDE.md +50 -0
  35. package/runtime/vercel/gh-shim +263 -0
  36. package/runtime/vercel/git-shim +131 -0
  37. package/runtime/vercel/scripts/provision.sh +274 -0
  38. package/dist/chunk-67N47KUS.js.map +0 -1
  39. package/dist/chunk-6OZDFNBF.js.map +0 -1
  40. package/dist/chunk-BGK32PZE.js.map +0 -1
  41. package/dist/chunk-FODMEHD3.js.map +0 -1
  42. package/dist/dist-ZODPD2I6.js.map +0 -1
  43. /package/dist/{_cloud-attach-T727ZPRV.js.map → _cloud-attach-ZXBCNWJX.js.map} +0 -0
  44. /package/dist/{dist-LOZBWMBF.js.map → dist-32EZBYG4.js.map} +0 -0
  45. /package/dist/{dist-L4LCG5SJ.js.map → dist-CX5CGVEB.js.map} +0 -0
  46. /package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js.map → prepared-state-CL4CWXQA-H5THETIM.js.map} +0 -0
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  buildCloudAttachInnerCommand,
4
4
  cloudAgentAttach
5
- } from "./chunk-FODMEHD3.js";
6
- import "./chunk-6OZDFNBF.js";
7
- import "./chunk-BGK32PZE.js";
5
+ } from "./chunk-GU5LW4B5.js";
6
+ import "./chunk-NCJP5MTN.js";
7
+ import "./chunk-KL36BRN4.js";
8
8
  import "./chunk-G3H2L3O2.js";
9
9
  export {
10
10
  buildCloudAttachInnerCommand,
11
11
  cloudAgentAttach
12
12
  };
13
- //# sourceMappingURL=_cloud-attach-T727ZPRV.js.map
13
+ //# sourceMappingURL=_cloud-attach-ZXBCNWJX.js.map
@@ -1,7 +1,10 @@
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
9
  buildHostEnvFindArgs,
7
10
  buildTmuxConfigShellSnippet,
@@ -10,6 +13,7 @@ import {
10
13
  generateRelayToken,
11
14
  generateVncPassword,
12
15
  hashProjectPath,
16
+ isRealAgentCredential,
13
17
  portlessAlias,
14
18
  portlessGetUrl,
15
19
  portlessUnalias,
@@ -23,22 +27,24 @@ import {
23
27
  stageOpencodeCredentialsForUpload,
24
28
  stageOpencodeStateForUpload,
25
29
  stageOpencodeStaticForUpload
26
- } from "./chunk-6OZDFNBF.js";
30
+ } from "./chunk-NCJP5MTN.js";
27
31
  import {
28
32
  allocateProjectIndex,
29
33
  detectGitRepos,
30
34
  readState,
31
35
  recordBox,
32
36
  removeBoxRecord
33
- } from "./chunk-BGK32PZE.js";
37
+ } from "./chunk-KL36BRN4.js";
34
38
 
35
39
  // ../../packages/sandbox-cloud/dist/index.js
36
40
  import { randomBytes } from "crypto";
37
41
  import { basename as basename2 } from "path";
38
- 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";
39
45
  import { homedir } from "os";
40
46
  import { basename, join } from "path";
41
- import { mkdtemp, rm as rm2, writeFile as writeFile2 } from "fs/promises";
47
+ import { mkdtemp, rm as rm2, writeFile as writeFile3 } from "fs/promises";
42
48
  import { tmpdir } from "os";
43
49
  import { join as join2 } from "path";
44
50
  import { execa } from "execa";
@@ -228,6 +234,39 @@ function agentSpecsForCloud() {
228
234
  credentialsSubpath: s.credentialsSubpath
229
235
  }));
230
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
+ }
231
270
  var CLOUD_CHECKPOINTS_ROOT = join(homedir(), ".agentbox", "cloud-checkpoints");
232
271
  var CLOUD_SNAPSHOT_NAME_PREFIX = "agentbox-ckpt-";
233
272
  function cloudSnapshotName(projectRoot, name) {
@@ -275,7 +314,7 @@ async function resolveCloudCheckpoint(projectRoot, backend, ref) {
275
314
  }
276
315
  async function writeCloudCheckpointManifest(projectRoot, backend, name, fields) {
277
316
  const dir = checkpointDir(backend, projectRoot, name);
278
- await mkdir(dir, { recursive: true });
317
+ await mkdir2(dir, { recursive: true });
279
318
  const manifest = {
280
319
  schema: 1,
281
320
  name,
@@ -285,7 +324,7 @@ async function writeCloudCheckpointManifest(projectRoot, backend, name, fields)
285
324
  sourceBoxName: fields.sourceBoxName,
286
325
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
287
326
  };
288
- 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");
289
328
  return { name, dir, manifest };
290
329
  }
291
330
  async function removeCloudCheckpointDir(projectRoot, backend, name) {
@@ -324,7 +363,7 @@ async function uploadEnvFiles(args) {
324
363
  log(`env-file tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
325
364
  return { copied: 0 };
326
365
  }
327
- await writeFile2(join2(stage, ".marker"), "").catch(() => {
366
+ await writeFile3(join2(stage, ".marker"), "").catch(() => {
328
367
  });
329
368
  await args.backend.uploadFile(args.handle, localTar, REMOTE_TAR_PATH);
330
369
  const extract = await args.backend.exec(
@@ -418,7 +457,8 @@ async function uploadOneEntry(args) {
418
457
  }
419
458
  parts.push(`rm -f ${remoteTar}`);
420
459
  const cmd = parts.join(" && ");
421
- const res = await args.backend.exec(args.handle, cmd);
460
+ const execOpts = args.backend.name === "vercel" ? { user: "root" } : void 0;
461
+ const res = await args.backend.exec(args.handle, cmd, execOpts);
422
462
  if (res.exitCode !== 0) {
423
463
  throw new Error(
424
464
  `in-box extract failed (exit ${String(res.exitCode)}): ${(res.stderr || res.stdout).slice(-300)}`
@@ -687,6 +727,7 @@ async function seedCloudWorkspace(args) {
687
727
  workspaceDir,
688
728
  bundleDepth: args.bundleDepth,
689
729
  fromBranch: args.fromBranch,
730
+ useBranch: args.useBranch,
690
731
  onLog: log
691
732
  });
692
733
  for (const r of nested) {
@@ -725,8 +766,8 @@ async function seedFromGitClone(args) {
725
766
  const cloneDir = join5(stage, "clone");
726
767
  const tarPath = join5(stage, "workspace.tar.gz");
727
768
  const untrackedTarPath = join5(stage, "untracked.tar.gz");
728
- const stashSha = await safeStashCreate(args.hostRepo);
729
- const untrackedSize = await maybeBuildUntrackedTar(args.hostRepo, untrackedTarPath);
769
+ const stashSha = args.useBranch ? null : await safeStashCreate(args.hostRepo);
770
+ const untrackedSize = args.useBranch ? 0 : await maybeBuildUntrackedTar(args.hostRepo, untrackedTarPath);
730
771
  let stashRefCreated = false;
731
772
  try {
732
773
  if (stashSha) {
@@ -743,7 +784,8 @@ async function seedFromGitClone(args) {
743
784
  log(
744
785
  adaptive ? `clone: depth=${String(DEFAULT_BUNDLE_DEPTH)} (default, adaptive)` : initialDepth === null ? "clone: depth=full (configured)" : `clone: depth=${String(initialDepth)} (configured)`
745
786
  );
746
- await runShallowClone(args.hostRepo, cloneDir, initialDepth, stashRefCreated, args.fromBranch);
787
+ const cloneBranch = args.useBranch ?? args.fromBranch;
788
+ await runShallowClone(args.hostRepo, cloneDir, initialDepth, stashRefCreated, cloneBranch);
747
789
  await tarCloneDir(cloneDir, tarPath);
748
790
  if (adaptive && initialDepth !== null) {
749
791
  const size = await safeFileSize(tarPath);
@@ -754,7 +796,7 @@ async function seedFromGitClone(args) {
754
796
  );
755
797
  await rm5(cloneDir, { recursive: true, force: true });
756
798
  await rm5(tarPath, { force: true });
757
- await runShallowClone(args.hostRepo, cloneDir, LARGE_BUNDLE_DEPTH, stashRefCreated, args.fromBranch);
799
+ await runShallowClone(args.hostRepo, cloneDir, LARGE_BUNDLE_DEPTH, stashRefCreated, cloneBranch);
758
800
  await tarCloneDir(cloneDir, tarPath);
759
801
  }
760
802
  }
@@ -791,7 +833,10 @@ async function seedFromGitClone(args) {
791
833
  `$SUDO chown "$(id -un):$(id -gn)" ${quoteShellArgv([args.workspaceDir])}`,
792
834
  `tar -C ${quoteShellArgv([args.workspaceDir])} -xzf ${quoteShellArgv([remoteTar])}`,
793
835
  setOrigin,
794
- `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])}`,
795
840
  ...carryOverSteps,
796
841
  `rm -f ${quoteShellArgv([remoteTar])}`
797
842
  ].join("\n");
@@ -989,7 +1034,9 @@ function createCloudProvider(backend, opts = {}) {
989
1034
  return {
990
1035
  id,
991
1036
  name,
992
- 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}`
993
1040
  };
994
1041
  }
995
1042
  async function probe(box) {
@@ -1008,7 +1055,13 @@ function createCloudProvider(backend, opts = {}) {
1008
1055
  });
1009
1056
  const { id, name, branch } = mintBox(req);
1010
1057
  const image = opts.provisionImage ? await opts.provisionImage(req) : req.image ?? FALLBACK_IMAGE;
1011
- 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;
1012
1065
  const relayToken = generateRelayToken();
1013
1066
  const bridgeToken = generateRelayToken();
1014
1067
  try {
@@ -1031,6 +1084,7 @@ function createCloudProvider(backend, opts = {}) {
1031
1084
  }
1032
1085
  }
1033
1086
  const agentVolumes = await ensureAgentVolumesForCloud(backend, { onLog: log });
1087
+ const exposeServicePorts = await readExposedServicePorts(req.workspacePath);
1034
1088
  log(
1035
1089
  snapshotName ? `provisioning ${providerName} sandbox from snapshot` : `provisioning ${providerName} sandbox`
1036
1090
  );
@@ -1039,6 +1093,9 @@ function createCloudProvider(backend, opts = {}) {
1039
1093
  image,
1040
1094
  snapshot: snapshotName,
1041
1095
  resources,
1096
+ timeoutMs,
1097
+ exposePorts: exposeServicePorts,
1098
+ networkPolicy,
1042
1099
  env: {
1043
1100
  AGENTBOX_BOX_ID: id,
1044
1101
  AGENTBOX_BOX_NAME: name,
@@ -1066,6 +1123,7 @@ function createCloudProvider(backend, opts = {}) {
1066
1123
  workspaceDir: CLOUD_WORKSPACE_DIR,
1067
1124
  bundleDepth: req.bundleDepth,
1068
1125
  fromBranch: req.fromBranch,
1126
+ useBranch: req.useBranch,
1069
1127
  onLog: log
1070
1128
  });
1071
1129
  }
@@ -1113,12 +1171,14 @@ function createCloudProvider(backend, opts = {}) {
1113
1171
  relayToken,
1114
1172
  bridgeToken
1115
1173
  });
1116
- log("launching in-box dockerd");
1117
- try {
1118
- const dockerd = await launchCloudDockerdDaemon({ backend, handle, timeoutMs: 6e4 });
1119
- if (!dockerd.up) log(`dockerd did not become ready (continuing): ${dockerd.reason ?? "unknown"}`);
1120
- } catch (err) {
1121
- 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
+ }
1122
1182
  }
1123
1183
  const vncEnabled = req.vnc?.enabled !== false;
1124
1184
  const vncPassword = vncEnabled ? generateVncPassword() : void 0;
@@ -1176,7 +1236,7 @@ function createCloudProvider(backend, opts = {}) {
1176
1236
  portlessVncUrlResolved = url;
1177
1237
  }
1178
1238
  }
1179
- const servicePorts = await readExposedServicePorts(req.workspacePath);
1239
+ const servicePorts = exposeServicePorts;
1180
1240
  const servicePreviews = {};
1181
1241
  for (const port of servicePorts) {
1182
1242
  if (port === CLOUD_WEB_PROXY_PORT) continue;
@@ -1192,12 +1252,15 @@ function createCloudProvider(backend, opts = {}) {
1192
1252
  } catch {
1193
1253
  relayPreview = void 0;
1194
1254
  }
1255
+ const state = await readState();
1256
+ const projectIndex = req.projectRoot ? allocateProjectIndex(state, req.projectRoot) : void 0;
1195
1257
  if (relayPreview) {
1196
1258
  try {
1197
1259
  await registerBoxWithRelay({
1198
1260
  boxId: id,
1199
1261
  token: relayToken,
1200
1262
  name,
1263
+ projectIndex,
1201
1264
  kind: "cloud",
1202
1265
  backend: backend.name,
1203
1266
  previewUrl: relayPreview.url,
@@ -1211,8 +1274,6 @@ function createCloudProvider(backend, opts = {}) {
1211
1274
  );
1212
1275
  }
1213
1276
  }
1214
- const state = await readState();
1215
- const projectIndex = req.projectRoot ? allocateProjectIndex(state, req.projectRoot) : void 0;
1216
1277
  const record = {
1217
1278
  id,
1218
1279
  name,
@@ -1365,11 +1426,13 @@ function createCloudProvider(backend, opts = {}) {
1365
1426
  relayToken: box.relayToken ?? "",
1366
1427
  bridgeToken: box.cloud?.bridgeToken
1367
1428
  });
1368
- try {
1369
- const dockerd = await launchCloudDockerdDaemon({ backend, handle: h, timeoutMs: 6e4 });
1370
- 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 {
1371
1435
  }
1372
- } catch {
1373
1436
  }
1374
1437
  if (box.vncEnabled && box.vncPassword) {
1375
1438
  try {
@@ -1530,7 +1593,16 @@ function createCloudProvider(backend, opts = {}) {
1530
1593
  // capability stub whose methods throw — the CLI's `agentbox checkpoint
1531
1594
  // create` then surfaces a clean "not supported" error rather than a
1532
1595
  // silent no-op.
1533
- 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
+ }
1534
1606
  // stats is provider-optional; cloud backends without a metrics API just
1535
1607
  // omit it. Backends that have one can decorate the returned provider.
1536
1608
  };
@@ -1635,6 +1707,9 @@ export {
1635
1707
  agentSpecsForCloud,
1636
1708
  listCloudCheckpoints,
1637
1709
  resolveCloudCheckpoint,
1638
- createCloudProvider
1710
+ writeCloudCheckpointManifest,
1711
+ removeCloudCheckpointDir,
1712
+ createCloudProvider,
1713
+ renderInnerCommand
1639
1714
  };
1640
- //# sourceMappingURL=chunk-67N47KUS.js.map
1715
+ //# sourceMappingURL=chunk-BXQMIEHC.js.map