@madarco/agentbox 0.16.0 → 0.17.1

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 (41) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/{_cloud-attach-5KJWOASL.js → _cloud-attach-RUD4W33P.js} +5 -4
  3. package/dist/{chunk-QXFNLKJJ.js → chunk-3JXSW6PN.js} +36 -24
  4. package/dist/chunk-3JXSW6PN.js.map +1 -0
  5. package/dist/{chunk-3WCEB6RE.js → chunk-46NXCJ6Z.js} +45 -34
  6. package/dist/{chunk-3WCEB6RE.js.map → chunk-46NXCJ6Z.js.map} +1 -1
  7. package/dist/{chunk-DBBUDKKB.js → chunk-6QFPYU4Z.js} +3 -3
  8. package/dist/{chunk-GXJNJUEV.js → chunk-HFQZJO73.js} +2 -2
  9. package/dist/{chunk-SENASAU4.js → chunk-IA6YHH52.js} +89 -81
  10. package/dist/chunk-IA6YHH52.js.map +1 -0
  11. package/dist/{chunk-PIK47622.js → chunk-KTDJ5JTE.js} +3 -3
  12. package/dist/chunk-WJFZJZIM.js +24 -0
  13. package/dist/{chunk-NW2UZQV6.js → chunk-XWBFWUY2.js} +23 -19
  14. package/dist/chunk-XWBFWUY2.js.map +1 -0
  15. package/dist/{chunk-SB4QTF2T.js → chunk-YUGLNTT2.js} +7 -7
  16. package/dist/{cloud-poller-SUNA6ZQC-2RG5WPRN.js → cloud-poller-SUNA6ZQC-7M5LJHHE.js} +2 -1
  17. package/dist/{dist-4IQFJJQI.js → dist-BO2R55FX.js} +6 -5
  18. package/dist/{dist-4IQFJJQI.js.map → dist-BO2R55FX.js.map} +1 -1
  19. package/dist/{dist-VHI5QOSQ.js → dist-CNABE32V.js} +6 -5
  20. package/dist/{dist-VHI5QOSQ.js.map → dist-CNABE32V.js.map} +1 -1
  21. package/dist/{dist-XC47DSCR.js → dist-CROGTJ7N.js} +6 -5
  22. package/dist/{dist-XC47DSCR.js.map → dist-CROGTJ7N.js.map} +1 -1
  23. package/dist/{dist-SL2QSMBE.js → dist-TEKY3GFT.js} +6 -5
  24. package/dist/{dist-SL2QSMBE.js.map → dist-TEKY3GFT.js.map} +1 -1
  25. package/dist/{dist-7YB7BMNG.js → dist-VAATGBAR.js} +4 -3
  26. package/dist/index.js +2254 -2064
  27. package/dist/index.js.map +1 -1
  28. package/dist/{prepared-state-MQHD3M5F-2LANTRL7.js → prepared-state-MQHD3M5F-OVABNV66.js} +3 -2
  29. package/dist/prepared-state-MQHD3M5F-OVABNV66.js.map +1 -0
  30. package/package.json +3 -3
  31. package/dist/chunk-NW2UZQV6.js.map +0 -1
  32. package/dist/chunk-QXFNLKJJ.js.map +0 -1
  33. package/dist/chunk-SENASAU4.js.map +0 -1
  34. /package/dist/{_cloud-attach-5KJWOASL.js.map → _cloud-attach-RUD4W33P.js.map} +0 -0
  35. /package/dist/{chunk-DBBUDKKB.js.map → chunk-6QFPYU4Z.js.map} +0 -0
  36. /package/dist/{chunk-GXJNJUEV.js.map → chunk-HFQZJO73.js.map} +0 -0
  37. /package/dist/{chunk-PIK47622.js.map → chunk-KTDJ5JTE.js.map} +0 -0
  38. /package/dist/{cloud-poller-SUNA6ZQC-2RG5WPRN.js.map → chunk-WJFZJZIM.js.map} +0 -0
  39. /package/dist/{chunk-SB4QTF2T.js.map → chunk-YUGLNTT2.js.map} +0 -0
  40. /package/dist/{dist-7YB7BMNG.js.map → cloud-poller-SUNA6ZQC-7M5LJHHE.js.map} +0 -0
  41. /package/dist/{prepared-state-MQHD3M5F-2LANTRL7.js.map → dist-VAATGBAR.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../packages/sandbox-vercel/src/index.ts","../../../packages/sandbox-vercel/src/backend.ts","../../../packages/sandbox-vercel/src/retry.ts","../../../packages/sandbox-vercel/src/prepare.ts","../../../packages/sandbox-vercel/src/build-attach.ts"],"sourcesContent":["/**\n * The Vercel Sandbox provider. A thin `CloudBackend` over `@vercel/sandbox`,\n * composed via `@agentbox/sandbox-cloud`'s `createCloudProvider` for everything\n * provider-agnostic (workspace seeding, ctl/VNC launch, state, relay polling).\n *\n * Three capabilities are overridden on top of the cloud scaffold:\n * - `prepare` — bake the base snapshot (Vercel can't build from a Dockerfile).\n * - `buildAttach` — SDK-streaming tmux bridge (Vercel has no SSH).\n * - `checkpoint` — store the Vercel snapshot *id* in the manifest so restore\n * boots from it (Vercel snapshots are id-addressed, not name-addressed).\n *\n * `launchDockerd: false` because Vercel Sandbox can't run nested containers.\n */\n\nimport type { BoxRecord, Provider, ProviderCheckpoint } from '@agentbox/core';\nimport {\n createCloudProvider,\n currentCloudBaseFingerprint,\n listCloudCheckpoints,\n removeCloudCheckpointDir,\n resolveCloudCheckpoint,\n writeCloudCheckpointManifest,\n} from '@agentbox/sandbox-cloud';\nimport {\n vercelBackend,\n snapshotVercelSandbox,\n deleteVercelSnapshot,\n DEFAULT_BOX_IMAGE_REF,\n} from './backend.js';\nimport { readCliStamp, recordBox } from '@agentbox/sandbox-core';\nimport { prepareVercelProvider } from './prepare.js';\nimport { buildVercelAttach } from './build-attach.js';\nimport { currentVercelBaseFingerprintLive } from './prepared-state.js';\n\nconst BACKEND_NAME = 'vercel';\n\nconst cloudProvider = createCloudProvider(vercelBackend, {\n // Vercel couples RAM to vCPU at 2048 MB/vCPU; disk is a fixed 32 GB NVMe.\n defaultResources: { cpu: 2, memory: 4, disk: 32 },\n launchDockerd: false,\n});\n\n/**\n * Vercel-specific checkpoint capability. Unlike the scaffold's default (which\n * stores a caller-chosen snapshot *name*), we capture the opaque Vercel\n * snapshot id and store THAT in the manifest's `snapshotName` field — the cloud\n * create flow passes `manifest.snapshotName` straight to\n * `provision({ snapshot })`, and the Vercel backend boots from it as a snapshot\n * id. (The scaffold's `cloudSnapshotName` project-scoping isn't needed — Vercel\n * snapshot ids are already globally unique.)\n */\nconst vercelCheckpoint: ProviderCheckpoint = {\n async create(box: BoxRecord, name: string) {\n if (!box.projectRoot) {\n throw new Error(\n 'cloud checkpoint requires the box to have a project root (run `agentbox checkpoint` from inside the project)',\n );\n }\n if (!box.cloud?.sandboxId) {\n throw new Error(`vercel box ${box.name} has no sandboxId — record is malformed`);\n }\n // NOTE: snapshotting stops the source sandbox; persistent mode resumes it\n // on the next call. Surfaced to the user in `agentbox checkpoint` docs.\n const snapshotId = await snapshotVercelSandbox(box.cloud.sandboxId);\n // The box is now stopped — persist it so the fast `agentbox list` path\n // doesn't show a stale `running` after a checkpoint. Best-effort.\n try {\n await recordBox({ ...box, cloud: { ...box.cloud, lastState: 'paused' } });\n } catch {\n // not worth failing the checkpoint over a state-record write\n }\n const info = await writeCloudCheckpointManifest(box.projectRoot, BACKEND_NAME, name, {\n snapshotName: snapshotId,\n sourceBoxId: box.id,\n sourceBoxName: box.name,\n baseProvider: BACKEND_NAME,\n baseFingerprint: currentCloudBaseFingerprint(BACKEND_NAME),\n cliVersion: readCliStamp().cliVersion,\n });\n return { ref: info.name };\n },\n async list(projectRoot: string) {\n const entries = await listCloudCheckpoints(projectRoot, BACKEND_NAME);\n return entries.map((e) => ({ ref: e.name, createdAt: e.manifest.createdAt }));\n },\n async remove(projectRoot: string, ref: string) {\n const entry = await resolveCloudCheckpoint(projectRoot, BACKEND_NAME, ref);\n if (!entry) return;\n try {\n await deleteVercelSnapshot(entry.manifest.snapshotName);\n } catch {\n // best-effort: drop the local manifest even if the remote delete failed\n // (network/perms/already-gone) so the user isn't left with a dead pointer.\n }\n await removeCloudCheckpointDir(projectRoot, BACKEND_NAME, ref);\n },\n};\n\nexport const vercelProvider: Provider = {\n ...cloudProvider,\n prepare: prepareVercelProvider,\n buildAttach: buildVercelAttach,\n checkpoint: vercelCheckpoint,\n baseFingerprint: () => currentVercelBaseFingerprintLive(),\n};\n\nexport { vercelBackend, DEFAULT_BOX_IMAGE_REF };\nexport { ensureVercelEnvLoaded, reloadVercelEnv } from './env-loader.js';\nexport { ensureVercelCredentials } from './credentials.js';\nexport type { EnsureVercelCredentialsOptions } from './credentials.js';\nexport {\n readVercelCredStatus,\n secretsPath,\n maskKey,\n type VercelCredStatus,\n} from './credentials.js';\nexport {\n prepareVercel,\n prepareVercelProvider,\n type PrepareVercelOptions,\n type PrepareVercelResult,\n} from './prepare.js';\nexport {\n currentVercelBaseFingerprintLive,\n ensureVercelBaseSnapshot,\n preparedStatePath,\n readPreparedState,\n writePreparedState,\n updatePreparedState,\n type PreparedVercelState,\n type PreparedVercelBase,\n} from './prepared-state.js';\nexport {\n RUNTIME_ASSETS,\n candidatesFor,\n resolveRuntimeAssets,\n findStagedCliRuntimeRoot,\n type RuntimeAsset,\n type ResolvedAsset,\n} from './runtime-assets.js';\nexport { buildVercelAttach } from './build-attach.js';\n","/**\n * Vercel `CloudBackend` — maps the provider-neutral cloud primitives onto\n * `@vercel/sandbox` v2 (Firecracker microVMs + snapshots). Composed into a full\n * `Provider` by `@agentbox/sandbox-cloud`'s `createCloudProvider`.\n *\n * Platform shape this backend is built around (see docs/cloud-providers.md):\n * - No custom image — sandboxes boot from a Vercel snapshot baked once by\n * `agentbox prepare --provider vercel`. `provision` always needs a snapshot\n * id (the prepared base, or a cloud-checkpoint snapshot).\n * - No SSH — `attachArgv` is intentionally omitted; the provider overrides\n * `buildAttach` with a Vercel-SDK-streaming helper instead.\n * - No nested containers — dockerd is disabled at the provider level.\n * - Persistent sandboxes auto-snapshot on stop and auto-resume on the next\n * `Sandbox.get({ resume: true })`, which is how pause/resume map cleanly.\n * - The sandbox's native user is `vercel-sandbox`; agentbox standardizes on\n * `vscode` (uid 1000), created by provision.sh. So `exec` drops privileges\n * to `vscode` (root → `sudo -u vscode`) unless the caller asks for root,\n * and `uploadFile` chowns to uid 1000 after the SDK writes as\n * `vercel-sandbox`.\n * - Max 4 exposed ports: we use 80 (WebProxy), 6080 (noVNC), 8788 (relay/ctl\n * bridge). One slot is left free for a future per-service expose.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport type {\n CloudBackend,\n CloudExecOptions,\n CloudExecResult,\n CloudFileEntry,\n CloudHandle,\n CloudPreviewUrl,\n CloudProvisionRequest,\n CloudSandboxSummary,\n CloudState,\n} from '@agentbox/core';\nimport type { NetworkPolicy } from '@vercel/sandbox';\nimport {\n ensureFreshCredentials,\n resolveCredentials,\n Sandbox,\n Snapshot,\n type SandboxType,\n} from './sdk.js';\nimport { withVercelRetry } from './retry.js';\nimport { readPreparedState } from './prepared-state.js';\n\n/** Sentinel image ref the cloud-provider hands us when no --image was passed. */\nexport const DEFAULT_BOX_IMAGE_REF = 'agentbox/box:dev';\n\n/** Box user agentbox standardizes on. provision.sh creates it (uid auto-assigned —\n * the Vercel default user may already hold 1000, and there are no bind mounts so\n * the exact uid is irrelevant). chown targets it by name, not number. */\nconst BOX_USER = 'vscode';\nconst BOX_OWNER = 'vscode:vscode';\n\n/**\n * Base ports exposed at create. Vercel REJECTS privileged ports (<1024) with a\n * 400, so we cannot expose the scaffold's WebProxy on :80. Instead the in-box\n * WebProxy binds 8080 (set via `webProxyPort` → AGENTBOX_WEB_PROXY_PORT) and we\n * expose 8080 here so `sandbox.domain(8080)` routes to it → the in-box `expose:`\n * service. Ports are fixed at create (update can't add a routable port to a\n * running sandbox — verified), so 8080 must be in this base set. The other two\n * base ports are 6080 (noVNC) and 8788 (the relay/ctl bridge the host poller\n * reaches via `sandbox.domain(8788)`). Remaining slots (up to VERCEL_MAX_PORTS)\n * are filled at create from `agentbox.yaml` `expose:` ports (see buildExposedPorts).\n */\nexport const VERCEL_EXPOSED_PORTS = [8080, 6080, 8788] as const;\n\n/** Vercel's hard per-sandbox exposed-port cap. */\nexport const VERCEL_MAX_PORTS = 4;\n\n/**\n * Merge requested `expose:` service ports into the base set: drop privileged\n * (<1024 — Vercel 400s) and out-of-range ports + dupes, preserve order, and cap\n * at Vercel's 4-port limit. A preview URL only routes to a port declared here at\n * create time, so this is what makes `services.*.expose` reachable on Vercel.\n */\nexport function buildExposedPorts(extra: readonly number[] | undefined): number[] {\n const ports = [...VERCEL_EXPOSED_PORTS] as number[];\n const seen = new Set<number>(ports);\n for (const p of extra ?? []) {\n if (ports.length >= VERCEL_MAX_PORTS) break;\n if (Number.isInteger(p) && p >= 1024 && p < 65_536 && !seen.has(p)) {\n ports.push(p);\n seen.add(p);\n }\n }\n return ports;\n}\n\n/**\n * Parse the `box.vercelNetworkPolicy` config string into a Vercel\n * `NetworkPolicy`. `''`/unset → undefined (SDK default = allow-all). The\n * literals `allow-all` / `deny-all` pass through; anything else is treated as a\n * comma-separated domain allowlist `{ allow: [...] }` (everything else denied).\n */\nexport function parseNetworkPolicy(raw: string | undefined): NetworkPolicy | undefined {\n const v = (raw ?? '').trim();\n if (v === '') return undefined;\n if (v === 'allow-all' || v === 'deny-all') return v;\n const allow = v\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return allow.length > 0 ? { allow } : undefined;\n}\n\n/**\n * Default per-session timeout. 45 min is the Hobby ceiling, so it's safe across\n * all plans; persistent mode makes a hit transparent (the VM auto-snapshots and\n * auto-resumes on the next SDK call). Pro/Enterprise users who want a longer\n * single session can rely on `extendTimeout` / future config.\n */\nconst DEFAULT_TIMEOUT_MS = 45 * 60_000;\n\n/**\n * Per-box snapshot retention. Keep one auto-snapshot, never expiring, so a\n * paused box can always resume; `destroy` purges a box's own snapshot explicitly.\n *\n * `deleteEvicted: false` is load-bearing, NOT a tweak. A box boots from a shared\n * snapshot (the prepared base, or a `setup` checkpoint), and Vercel reports that\n * source as the box's `currentSnapshotId` until it takes its first auto-snapshot\n * — i.e. the source is the first member of this box's retention window. With\n * `deleteEvicted: true`, the box's first stop/snapshot evicts the source and\n * DELETES it, nuking the shared base/checkpoint every other box depends on, so\n * every later `create` 410s with \"Snapshot expired or deleted.\" (Same hazard the\n * `destroy` guard already dodges, but eviction is automatic and bypasses it.)\n * `false` keeps evicted snapshots around (they fall back to `snapshotExpiration`,\n * which we pin to 0 = never at create) — trading a little snapshot accumulation\n * for never deleting a snapshot another box boots from.\n */\nconst KEEP_LAST_SNAPSHOTS = { count: 1, expiration: 0, deleteEvicted: false } as const;\n\nfunction creds(): Partial<{ token: string; teamId: string; projectId: string }> {\n return resolveCredentials();\n}\n\n/** Single-quote a string for safe embedding inside a `bash -lc '<…>'`. */\nfunction shq(s: string): string {\n return \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n\nasync function getSandbox(id: string): Promise<SandboxType> {\n // resume:false — plain handle resolution; lifecycle methods opt into resume.\n return Sandbox.get({ name: id, resume: false, ...creds() });\n}\n\nasync function maybeGetSandbox(id: string): Promise<SandboxType | null> {\n try {\n return await getSandbox(id);\n } catch {\n return null;\n }\n}\n\n/**\n * Map Vercel's session status onto our 4-value `CloudState`. Transitional\n * states report as 'running' so callers don't ping-pong; 'stopped' maps to\n * 'paused' because a persistent sandbox keeps an auto-snapshot and resumes on\n * the next call (our pause semantics). 'aborted'/'failed' → 'missing'.\n */\nfunction mapState(s: string | undefined): CloudState {\n switch (s) {\n case 'running':\n return 'running';\n case 'pending':\n case 'stopping':\n case 'snapshotting':\n return 'running';\n case 'stopped':\n return 'paused';\n case 'aborted':\n case 'failed':\n default:\n return 'missing';\n }\n}\n\n/**\n * Build a `runCommand` invocation that runs `cmd` (already a shell string) as\n * the box user (`vscode`) by default, or as root when requested. Always starts\n * the SDK command as root (`sudo: true`) so the inner `sudo -u vscode` is\n * reliably passwordless, then drops privileges. cwd + env are applied inside\n * the dropped shell so they land in the right user/home context.\n */\nfunction buildRunCommand(\n cmd: string,\n opts?: CloudExecOptions,\n): { cmd: string; args: string[]; sudo: boolean } {\n const prelude: string[] = [];\n if (opts?.cwd) prelude.push(`cd ${shq(opts.cwd)}`);\n for (const [k, v] of Object.entries(opts?.env ?? {})) {\n // The value is shell-quoted, but the key is interpolated bare into a\n // `bash -lc` string that runs as root — reject anything that isn't a POSIX\n // env-var name so a key like `x;rm -rf /` can't inject a command.\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) {\n throw new Error(`vercel exec: invalid env var name ${JSON.stringify(k)}`);\n }\n prelude.push(`export ${k}=${shq(v)}`);\n }\n const inner = [...prelude, cmd].join('\\n');\n const user = opts?.user ?? BOX_USER;\n if (user === 'root') {\n return { cmd: 'bash', args: ['-lc', inner], sudo: true };\n }\n return {\n cmd: 'bash',\n args: ['-lc', `sudo -u ${user} -H bash -lc ${shq(inner)}`],\n sudo: true,\n };\n}\n\nexport const vercelBackend: CloudBackend = {\n name: 'vercel',\n\n // Vercel rejects privileged ports (<1024) and can't add a routable port to a\n // running sandbox (update registers a route that 502s — verified). So the\n // in-box WebProxy binds 8080 (exposed at create via VERCEL_EXPOSED_PORTS) and\n // `agentbox url` resolves sandbox.domain(8080) → WebProxy → the in-box service.\n webProxyPort: 8080,\n\n async provision(req: CloudProvisionRequest): Promise<CloudHandle> {\n await ensureFreshCredentials();\n // Resolve the snapshot to boot from: an explicit cloud-checkpoint snapshot\n // (req.snapshot) wins, else the prepared base. Vercel can't build from a\n // Dockerfile, so there is no image fallback — fail loud with the fix.\n const snapshotId = req.snapshot ?? readPreparedState().base?.snapshotId;\n if (!snapshotId) {\n throw new Error(\n 'no Vercel base snapshot found.\\n' +\n 'Run `agentbox prepare --provider vercel` first — Vercel cannot build images ' +\n 'from a Dockerfile, so the base snapshot is a one-time prerequisite.',\n );\n }\n const networkPolicy = parseNetworkPolicy(req.networkPolicy);\n // No-retry: Sandbox.create is billable and non-idempotent — a timeout after\n // the request reached the origin could leave a duplicate sandbox we can't\n // reference for cleanup.\n const handle = await withVercelRetry(\n { method: 'provision', retryOnAmbiguous: false, attemptTimeoutMs: 900_000, backoffMs: [] },\n async () => {\n const sb = await Sandbox.create({\n name: req.name,\n source: { type: 'snapshot', snapshotId },\n resources: { vcpus: req.resources?.cpu ?? 2 },\n ports: buildExposedPorts(req.exposePorts),\n timeout: req.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n env: req.env,\n tags: { agentbox: 'true', 'agentbox.name': req.name },\n persistent: true,\n // Pin the sandbox-default expiration to never. Evicted snapshots (see\n // KEEP_LAST_SNAPSHOTS) fall back to this, so the shared base/checkpoint\n // a box boots from is never re-stamped with a finite expiry on eviction.\n snapshotExpiration: 0,\n keepLastSnapshots: { ...KEEP_LAST_SNAPSHOTS },\n ...(networkPolicy ? { networkPolicy } : {}),\n ...creds(),\n });\n return { sandboxId: sb.name };\n },\n );\n // Agent credentials are seeded by `createCloudProvider`'s unified\n // post-provision step (`seedAgentVolumesIfFresh`) via `uploadFile` + `exec`\n // — the symlinks baked into provision.sh route ~/.claude/.credentials.json\n // etc. through to `~/.agentbox-creds/<agent>/`.\n return handle;\n },\n\n async get(sandboxId: string): Promise<CloudHandle | null> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'get', retryOnAmbiguous: true }, async () => {\n const sb = await maybeGetSandbox(sandboxId);\n return sb ? { sandboxId: sb.name } : null;\n });\n },\n\n async list(): Promise<CloudSandboxSummary[]> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'list', retryOnAmbiguous: true }, async () => {\n const page = await Sandbox.list({ ...creds() });\n const items = await page.toArray();\n return items\n .filter((sb) => sb.tags?.['agentbox'] === 'true')\n .map((sb): CloudSandboxSummary => {\n const summary: CloudSandboxSummary = { sandboxId: sb.name };\n const friendly = sb.tags?.['agentbox.name'] ?? sb.name;\n if (friendly) summary.name = friendly;\n if (typeof sb.createdAt === 'number') {\n summary.createdAt = new Date(sb.createdAt).toISOString();\n }\n summary.state = mapState(sb.status);\n return summary;\n });\n });\n },\n\n async start(h: CloudHandle): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'start', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n // resume:true auto-resumes a persistent sandbox from its current snapshot.\n await Sandbox.get({ name: h.sandboxId, resume: true, ...creds() });\n },\n );\n },\n\n async stop(h: CloudHandle): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'stop', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n const sb = await getSandbox(h.sandboxId);\n // For a persistent sandbox this captures an auto-snapshot and shuts the\n // VM down — resume happens lazily on the next Sandbox.get.\n await sb.stop();\n },\n );\n },\n\n // pause == stop on Vercel (the auto-snapshot IS the cold-storage state).\n async pause(h: CloudHandle): Promise<void> {\n await this.stop(h);\n },\n\n async resume(h: CloudHandle): Promise<void> {\n await this.start(h);\n },\n\n async destroy(h: CloudHandle): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'destroy', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n const sb = await maybeGetSandbox(h.sandboxId);\n if (!sb) return; // already gone — destroy is idempotent\n // Purge only a snapshot THIS box created (its own stop-time auto-\n // snapshot), never the shared base/source it booted from. A fresh box\n // has currentSnapshotId === sourceSnapshotId === the prepared base, and\n // deleting that would nuke the base snapshot every other box depends on.\n const snapId = sb.currentSnapshotId;\n const source = sb.sourceSnapshotId;\n const base = readPreparedState().base?.snapshotId;\n const ownSnapshot =\n snapId !== undefined && snapId !== source && snapId !== base;\n await sb.delete();\n if (ownSnapshot) {\n try {\n const snap = await Snapshot.get({ snapshotId: snapId, ...creds() });\n await snap.delete();\n } catch {\n // best-effort: a snapshot already gone is fine; the user can clean\n // stragglers from the Vercel dashboard.\n }\n }\n },\n );\n },\n\n async state(h: CloudHandle): Promise<CloudState> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'state', retryOnAmbiguous: true }, async () => {\n const sb = await maybeGetSandbox(h.sandboxId);\n if (!sb) return 'missing';\n return mapState(sb.status);\n });\n },\n\n async exec(h: CloudHandle, cmd: string, opts?: CloudExecOptions): Promise<CloudExecResult> {\n await ensureFreshCredentials();\n return withVercelRetry(\n {\n method: 'exec',\n retryOnAmbiguous: opts?.noRetry ? false : true,\n attemptTimeoutMs: opts?.attemptTimeoutMs ?? 120_000,\n backoffMs: opts?.noRetry ? [] : undefined,\n },\n async () => {\n const sb = await getSandbox(h.sandboxId);\n const r = await sb.runCommand(buildRunCommand(cmd, opts));\n const [stdout, stderr] = await Promise.all([r.stdout(), r.stderr()]);\n return { exitCode: r.exitCode, stdout, stderr };\n },\n );\n },\n\n async uploadFile(h: CloudHandle, localPath: string, remotePath: string): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'uploadFile', retryOnAmbiguous: true, attemptTimeoutMs: 300_000 },\n async () => {\n const content = await readFile(localPath);\n const sb = await getSandbox(h.sandboxId);\n await sb.writeFiles([{ path: remotePath, content }]);\n // writeFiles writes as `vercel-sandbox`; chown to the box user so the\n // scaffold's vscode-context reads/extractions succeed. Best-effort —\n // a chown failure on a world-readable /tmp staging file is harmless.\n try {\n await sb.runCommand({ cmd: 'chown', args: [BOX_OWNER, remotePath], sudo: true });\n } catch {\n // ignore — file is at least present and readable\n }\n },\n );\n },\n\n async downloadFile(h: CloudHandle, remotePath: string, localPath: string): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'downloadFile', retryOnAmbiguous: true, attemptTimeoutMs: 300_000 },\n async () => {\n const sb = await getSandbox(h.sandboxId);\n const written = await sb.downloadFile(\n { path: remotePath },\n { path: localPath },\n { mkdirRecursive: true },\n );\n if (written === null) {\n throw new Error(`vercel downloadFile: source not found: ${remotePath}`);\n }\n },\n );\n },\n\n async listFiles(h: CloudHandle, remoteDir: string): Promise<CloudFileEntry[]> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'listFiles', retryOnAmbiguous: true }, async () => {\n const sb = await getSandbox(h.sandboxId);\n const entries = await sb.fs.readdir(remoteDir, { withFileTypes: true });\n return entries.map((e) => ({ name: e.name, isDir: e.isDirectory() }));\n });\n },\n\n async previewUrl(h: CloudHandle, port: number): Promise<CloudPreviewUrl> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'previewUrl', retryOnAmbiguous: true }, async () => {\n const sb = await getSandbox(h.sandboxId);\n // sb.domain(port) is a public HTTPS URL (no header token needed).\n return { url: sb.domain(port), token: undefined };\n });\n },\n\n // Fewer params than the interface's (h, port, expiresInSeconds) is fine —\n // Vercel sandbox domains are already public + browser-usable, so the signed\n // URL is just the standard one (the TTL is governed by the sandbox session\n // lifetime, not a per-URL signature, so the expiry arg is irrelevant here).\n async signedPreviewUrl(h: CloudHandle, port: number): Promise<CloudPreviewUrl> {\n return this.previewUrl(h, port);\n },\n\n async snapshotExists(snapshotName: string): Promise<boolean> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'snapshotExists', retryOnAmbiguous: true }, async () => {\n try {\n const snap = await Snapshot.get({ snapshotId: snapshotName, ...creds() });\n // `Snapshot.get` resolves deleted/failed tombstones (status field) rather\n // than throwing, so \"didn't throw\" wrongly passes a dead snapshot. Only a\n // 'created' snapshot can actually boot a sandbox.\n return snap.status === 'created';\n } catch {\n return false;\n }\n });\n },\n\n // NOTE: no `createSnapshot`/`deleteSnapshot` here. Vercel snapshots are\n // addressed by an opaque id (not a caller-chosen name), which doesn't fit the\n // CloudBackend `createSnapshot(handle, name): void` contract — the provider\n // needs the id back to store it in the checkpoint manifest. The Vercel\n // provider therefore overrides the whole `checkpoint` capability in index.ts\n // using `snapshotVercelSandbox` / `deleteVercelSnapshot` below.\n};\n\n/**\n * Snapshot a running sandbox and return the resulting Vercel snapshot id.\n * `sb.snapshot()` stops the source sandbox as part of capture; persistent mode\n * resumes it on the next SDK call, so the box comes back automatically.\n */\nexport async function snapshotVercelSandbox(sandboxId: string): Promise<string> {\n await ensureFreshCredentials();\n return withVercelRetry(\n { method: 'createSnapshot', retryOnAmbiguous: false, attemptTimeoutMs: 900_000, backoffMs: [] },\n async () => {\n const sb = await getSandbox(sandboxId);\n const snap = await sb.snapshot({ expiration: 0 });\n return snap.snapshotId;\n },\n );\n}\n\n/** Delete a Vercel snapshot by id. Idempotent — a missing snapshot is success. */\nexport async function deleteVercelSnapshot(snapshotId: string): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry({ method: 'deleteSnapshot', retryOnAmbiguous: true }, async () => {\n try {\n const snap = await Snapshot.get({ snapshotId, ...creds() });\n await snap.delete();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (/not.?found|404/i.test(msg)) return; // idempotent\n throw err;\n }\n });\n}\n","/**\n * Bounded retry wrapper for Vercel Sandbox SDK calls — mirrors\n * `withDaytonaRetry` / `withHetznerRetry` in shape and intent. The Vercel\n * control plane rate-limits (429) and can return transient 5xx during\n * incidents; without bounded retries those propagate as wedges in the calling\n * lifecycle code.\n *\n * Non-idempotent ops (`provision`/`Sandbox.create`, `createSnapshot`) pass\n * `retryOnAmbiguous: false` so a timeout after the request reached the origin\n * doesn't create a duplicate billable sandbox/snapshot.\n */\n\nexport interface WithRetryOptions {\n method: string;\n /** Per-attempt timeout (ms). Default 30_000. */\n attemptTimeoutMs?: number;\n /** Backoff before attempts 2, 3, … (ms). Default [1000, 2000, 4000]. */\n backoffMs?: readonly number[];\n /**\n * Retry on errors where we can't be sure the server applied the request\n * (connection failures, per-attempt timeouts, 5xx). Set false for\n * non-idempotent operations where a retry could create a duplicate resource.\n */\n retryOnAmbiguous: boolean;\n /** Override the default stderr retry sink (used by tests). */\n onRetry?: (line: string) => void;\n}\n\nconst DEFAULT_BACKOFF: readonly number[] = [1000, 2000, 4000];\nconst DEFAULT_ATTEMPT_TIMEOUT_MS = 30_000;\n\nclass AttemptTimeoutError extends Error {\n constructor(method: string, ms: number) {\n super(`vercel ${method}: per-attempt timeout after ${String(ms)}ms`);\n this.name = 'AttemptTimeoutError';\n }\n}\n\nexport function isAttemptTimeout(err: unknown): err is AttemptTimeoutError {\n return err instanceof AttemptTimeoutError;\n}\n\n/** HTTP status code dug out of whatever error shape the SDK throws. */\nfunction statusCodeOf(err: unknown): number | undefined {\n if (!err || typeof err !== 'object') return undefined;\n for (const key of ['statusCode', 'status', 'code'] as const) {\n const v = (err as Record<string, unknown>)[key];\n if (typeof v === 'number') return v;\n }\n const resp = (err as { response?: { status?: unknown } }).response;\n if (resp && typeof resp.status === 'number') return resp.status;\n return undefined;\n}\n\nexport function isRetriable(err: unknown, allowAmbiguous: boolean): boolean {\n if (err instanceof AttemptTimeoutError) return allowAmbiguous;\n\n const status = statusCodeOf(err);\n if (status !== undefined) {\n if (status === 429) return true; // rate limited — the server told us to wait\n if (status >= 500 && status <= 599) return allowAmbiguous;\n return false; // 4xx (auth, validation, not_found) — permanent\n }\n\n // Raw fetch / undici errors. Node wraps low-level errors in `{ cause }`.\n if (err && typeof err === 'object') {\n const candidates: unknown[] = [err, (err as { cause?: unknown }).cause];\n for (const c of candidates) {\n if (!c || typeof c !== 'object') continue;\n const code = (c as { code?: unknown }).code;\n if (\n code === 'ECONNRESET' ||\n code === 'ETIMEDOUT' ||\n code === 'ECONNABORTED' ||\n code === 'EAI_AGAIN' ||\n code === 'ECONNREFUSED' ||\n code === 'ENOTFOUND' ||\n code === 'UND_ERR_SOCKET' ||\n code === 'UND_ERR_CONNECT_TIMEOUT'\n ) {\n return allowAmbiguous;\n }\n }\n }\n return false;\n}\n\nexport async function withVercelRetry<T>(\n opts: WithRetryOptions,\n fn: () => Promise<T>,\n): Promise<T> {\n const backoff = opts.backoffMs ?? DEFAULT_BACKOFF;\n const maxAttempts = backoff.length + 1;\n const timeoutMs = opts.attemptTimeoutMs ?? DEFAULT_ATTEMPT_TIMEOUT_MS;\n const log = opts.onRetry ?? defaultRetryLog;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await raceTimeout(fn(), timeoutMs, opts.method);\n } catch (err) {\n const last = attempt === maxAttempts;\n if (last || !isRetriable(err, opts.retryOnAmbiguous)) throw err;\n const delay = backoff[attempt - 1] ?? backoff[backoff.length - 1] ?? 4000;\n log(\n `vercel ${opts.method}: attempt ${String(attempt)} failed (${errorSummary(err)}); retrying in ${String(delay)}ms`,\n );\n await sleep(delay);\n }\n }\n throw new Error(`withVercelRetry: exhausted attempts for ${opts.method}`);\n}\n\nfunction defaultRetryLog(line: string): void {\n process.stderr.write(`\\n[vercel-retry] ${line}\\n`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nasync function raceTimeout<T>(p: Promise<T>, ms: number, method: string): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n p,\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(new AttemptTimeoutError(method, ms)), ms);\n }),\n ]);\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n}\n\nfunction errorSummary(err: unknown): string {\n if (err instanceof Error) {\n const status = statusCodeOf(err);\n return status !== undefined\n ? `${err.name}(${String(status)}): ${truncate(err.message)}`\n : `${err.name}: ${truncate(err.message)}`;\n }\n return truncate(String(err));\n}\n\nfunction truncate(s: string, max = 160): string {\n return s.length > max ? `${s.slice(0, max)}…` : s;\n}\n","/**\n * `agentbox prepare --provider vercel` — bake the per-team Vercel base\n * snapshot. Vercel can't build an image from a Dockerfile, so (like hetzner)\n * we boot a fresh sandbox, run an installer, and snapshot the result. That\n * snapshot id is what every per-box `create` boots from.\n *\n * Flow:\n * 1. Resolve runtime assets + fingerprint the build context. Skip the bake\n * when an up-to-date base snapshot already exists (unless --force).\n * 2. `Sandbox.create({ runtime: 'node24', persistent: false })` — fresh AL2023.\n * 3. `writeFiles` the assets (ctl bundle, helpers, baked configs, provision.sh).\n * 4. Run provision.sh as root, streaming output to the prepare log.\n * 5. Stage host agent static config (claude/codex/opencode) into the snapshot.\n * 6. `sandbox.snapshot({ expiration: 0 })` → the never-expiring base snapshot.\n * 7. Persist the snapshot id into ~/.agentbox/vercel-prepared.json.\n * 8. Delete the builder sandbox.\n *\n * Step 8 is safe: a Vercel snapshot is an independent, id-addressed resource\n * that survives its source sandbox's deletion (verified live — snapshot stays\n * `status: 'created'` and boots a fresh sandbox after the builder is deleted).\n * We delete it best-effort *after* the snapshot id is persisted, so a delete\n * failure only leaves a lingering sandbox for Vercel's reaper, never a broken\n * bake.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { Writable } from 'node:stream';\nimport type { Provider } from '@agentbox/core';\nimport { computeContextSha256, readCliStamp } from '@agentbox/sandbox-core';\nimport {\n stageClaudeStaticForUpload,\n stageCodexStaticForUpload,\n stageOpencodeStaticForUpload,\n type StageResult,\n} from '@agentbox/sandbox-cloud';\nimport { ensureVercelCredentials } from './credentials.js';\nimport {\n ensureFreshCredentials,\n resolveCredentials,\n Sandbox,\n Snapshot,\n type SandboxType,\n} from './sdk.js';\nimport {\n preparedStatePath,\n readPreparedState,\n writePreparedState,\n} from './prepared-state.js';\nimport {\n findStagedCliRuntimeRoot,\n resolveRuntimeAssets,\n type ResolvedAsset,\n} from './runtime-assets.js';\n\nexport interface PrepareVercelOptions {\n name?: string;\n hostWorkspace?: string;\n /** Force re-bake even when an up-to-date base snapshot is recorded. */\n force?: boolean;\n /** vCPUs for the builder sandbox (default 4 for a fast bake). */\n vcpus?: number;\n /** CLI runtime tree (set by the CLI to its dist neighbor). */\n cliRuntimeRoot?: string;\n /** Repo root for the dev fallback (defaults to a cwd-walk). */\n repoRoot?: string;\n onLog?: (line: string) => void;\n}\n\nexport interface PrepareVercelResult {\n snapshotName?: string;\n}\n\nconst BUILDER_TIMEOUT_MS = 25 * 60_000;\nconst SHELL = '/bin/bash';\n\nexport async function prepareVercel(\n opts: PrepareVercelOptions = {},\n): Promise<PrepareVercelResult> {\n await ensureVercelCredentials();\n await ensureFreshCredentials();\n const creds = resolveCredentials();\n const log = opts.onLog ?? (() => {});\n const progress = (s: string) => log(`prepare-vercel: ${s}`);\n\n const assets = resolveRuntimeAssets({\n cliRuntimeRoot: opts.cliRuntimeRoot ?? findStagedCliRuntimeRoot(),\n repoRoot: opts.repoRoot,\n });\n const contextSha = await computeContextSha256(\n assets.map((a) => ({ rel: a.name, abs: a.localPath })),\n );\n\n // Skip-fast: existing base snapshot still on Vercel + matching fingerprint.\n const existing = readPreparedState();\n if (!opts.force && existing.base) {\n const stillThere = await snapshotExists(existing.base.snapshotId, creds);\n if (stillThere && existing.base.contextSha256 === contextSha) {\n progress(\n `base snapshot ${existing.base.snapshotId} already exists (fingerprint ${contextSha.slice(0, 12)} matches); skipping (pass --force to rebuild)`,\n );\n return { snapshotName: existing.base.snapshotId };\n }\n if (!stillThere) {\n progress(`recorded base snapshot ${existing.base.snapshotId} is gone on Vercel; rebuilding`);\n } else {\n progress(\n `build context changed (was ${existing.base.contextSha256?.slice(0, 12) ?? '<none>'}, now ${contextSha.slice(0, 12)}); rebuilding`,\n );\n }\n }\n\n progress(`creating builder sandbox (node24, ${String(opts.vcpus ?? 4)} vcpus)`);\n const sb = await Sandbox.create({\n runtime: 'node24',\n resources: { vcpus: opts.vcpus ?? 4 },\n timeout: BUILDER_TIMEOUT_MS,\n tags: { agentbox: 'true', 'agentbox.role': 'prepare' },\n persistent: false,\n ...creds,\n });\n progress(`builder sandbox ${sb.name} up`);\n\n // 3. Upload assets.\n progress(`uploading ${String(assets.length)} runtime asset(s)`);\n await sb.writeFiles(\n await Promise.all(\n assets.map(async (a: ResolvedAsset) => ({\n path: a.remotePath,\n content: await readFile(a.localPath),\n mode: a.remoteMode,\n })),\n ),\n );\n\n // 4. Run provision.sh as root, streaming output.\n progress('running provision.sh (this takes a few minutes)');\n const install = await sb.runCommand({\n cmd: SHELL,\n args: ['-lc', 'bash /tmp/agentbox-provision.sh 2>&1'],\n sudo: true,\n stdout: lineSink((l) => log(`[provision] ${l}`)),\n stderr: lineSink((l) => log(`[provision] ${l}`)),\n });\n if (install.exitCode !== 0) {\n throw new Error(`provision.sh failed on the builder sandbox (exit ${String(install.exitCode)})`);\n }\n progress('provision.sh complete');\n\n // 5. Stage host agent static config into the snapshot (best-effort).\n await stageAgentConfig(sb, opts.hostWorkspace, log);\n\n // 6. Snapshot (never expires). NOTE: this stops the builder sandbox.\n progress('creating base snapshot (expiration: never)');\n const snap = await sb.snapshot({ expiration: 0 });\n progress(`snapshot created: ${snap.snapshotId}`);\n\n // 7. Persist.\n const cliStamp = readCliStamp();\n writePreparedState({\n schema: 1,\n base: {\n snapshotId: snap.snapshotId,\n contextSha256: contextSha,\n cliVersion: cliStamp.cliVersion,\n cliCommit: cliStamp.cliCommit,\n createdAt: new Date().toISOString(),\n },\n });\n progress(`wrote ${preparedStatePath()}`);\n\n // 8. Delete the builder. The snapshot is an independent resource that\n // survives this (verified live), and its id is already persisted above, so\n // this is best-effort: a failure just leaves the sandbox for Vercel's reaper.\n progress('deleting builder sandbox');\n try {\n await sb.delete();\n progress('builder sandbox deleted');\n } catch (err) {\n progress(\n `builder delete failed (left for Vercel reaper): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n progress(`prepare complete — base snapshot ${snap.snapshotId}`);\n return { snapshotName: snap.snapshotId };\n}\n\nasync function snapshotExists(\n snapshotId: string,\n creds: Partial<{ token: string; teamId: string; projectId: string }>,\n): Promise<boolean> {\n try {\n const snap = await Snapshot.get({ snapshotId, ...creds });\n // `Snapshot.get` resolves even for a deleted/failed snapshot (status field),\n // so a bare \"didn't throw\" wrongly skip-passes a tombstone. Only a 'created'\n // snapshot is bootable — anything else means rebuild.\n return snap.status === 'created';\n } catch {\n return false;\n }\n}\n\nasync function stageAgentConfig(\n sb: SandboxType,\n hostWorkspace: string | undefined,\n log: (line: string) => void,\n): Promise<void> {\n const progress = (s: string) => log(`prepare-vercel: ${s}`);\n progress('staging host agent static config');\n const stagings: Array<{ kind: 'claude' | 'codex' | 'opencode'; tar: StageResult; dest: string }> = [];\n try {\n const claudeTar = await stageClaudeStaticForUpload({ hostWorkspace });\n for (const w of claudeTar.warnings) progress(w);\n if (claudeTar.tarballPath) stagings.push({ kind: 'claude', tar: claudeTar, dest: '/home/vscode/.claude' });\n else await claudeTar.cleanup();\n\n const codexTar = await stageCodexStaticForUpload();\n for (const w of codexTar.warnings) progress(w);\n if (codexTar.tarballPath) stagings.push({ kind: 'codex', tar: codexTar, dest: '/home/vscode/.codex' });\n else await codexTar.cleanup();\n\n const opencodeTar = await stageOpencodeStaticForUpload();\n for (const w of opencodeTar.warnings) progress(w);\n if (opencodeTar.tarballPath) stagings.push({ kind: 'opencode', tar: opencodeTar, dest: '/home/vscode/.local/share/opencode' });\n else await opencodeTar.cleanup();\n\n for (const s of stagings) {\n const remote = `/tmp/agentbox-${s.kind}-static.tar.gz`;\n progress(`uploading ${s.kind} static config`);\n await sb.writeFiles([{ path: remote, content: await readFile(s.tar.tarballPath as string) }]);\n // Extract as vscode so files land owned by the box user. The dest dir\n // already exists (provision.sh's credential-pivot step) — extract into it.\n const extract =\n `sudo -u vscode mkdir -p ${s.dest} && ` +\n `sudo -u vscode tar -xzf ${remote} -C ${s.dest} --no-same-permissions --no-same-owner -m && ` +\n `rm -f ${remote}`;\n const r = await sb.runCommand({ cmd: SHELL, args: ['-lc', extract], sudo: true });\n if (r.exitCode !== 0) {\n progress(`WARN: ${s.kind} static extract failed (exit ${String(r.exitCode)}) — continuing`);\n } else {\n progress(`baked ${s.kind} static config into snapshot`);\n }\n }\n } finally {\n for (const s of stagings) await s.tar.cleanup();\n }\n}\n\n/**\n * Adapt a line-callback to the `Writable` the SDK's `runCommand` streams into.\n * Buffers partial lines so each `onLine` gets a complete line.\n */\nfunction lineSink(onLine: (line: string) => void): Writable {\n let buf = '';\n return new Writable({\n write(chunk: Buffer, _enc: BufferEncoding, cb: () => void) {\n buf += chunk.toString('utf8');\n let nl: number;\n while ((nl = buf.indexOf('\\n')) !== -1) {\n onLine(buf.slice(0, nl));\n buf = buf.slice(nl + 1);\n }\n cb();\n },\n final(cb: () => void) {\n if (buf.length > 0) onLine(buf);\n cb();\n },\n });\n}\n\n/** Provider-level binding used by the CLI's `prepare` command. */\nexport const prepareVercelProvider: NonNullable<Provider['prepare']> = (req) =>\n prepareVercel({\n name: req.name,\n hostWorkspace: req.hostWorkspace ?? process.cwd(),\n force: req.force,\n onLog: req.onLog,\n });\n","/**\n * `buildVercelAttach` — the Vercel provider's override of `Provider.buildAttach`.\n *\n * Vercel has no SSH, so the cloud scaffold's `ssh … -t '<cmd>'` argv is unusable.\n * Instead we drive the official Vercel Sandbox CLI (`sbx`/`sandbox`), which has a\n * real interactive PTY (`sbx exec -i`) and streams non-interactive output live —\n * giving a proper terminal with none of the old send-keys/capture-pane polling.\n *\n * Argv shape (validated against sbx 3.0.1):\n * sbx exec --sudo [-i] --project <p> --scope <team> <name>\n * -- sudo -u vscode -H bash -lc '<inner>'\n *\n * Notes:\n * - The sandbox's default exec user is `vercel-sandbox`; we pass `--sudo` (runs\n * as root) and then `sudo -u vscode -H` so tmux/agents run as the box user in\n * /workspace. Passing `sudo -u vscode …` directly as sbx's argv (not wrapped\n * in an outer `bash -lc`) avoids a double-`bash -lc` re-parse.\n * - `-i` only for interactive shell/agent attaches; detached pre-start and logs\n * run non-interactively (live stdout stream).\n * - The access token is passed via the child env (`VERCEL_AUTH_TOKEN`), never in\n * argv, so it can't leak through `ps`. project/scope are not secret → flags.\n * - `<inner>` is the shared cloud `renderInnerCommand` (same tmux ensure +\n * footer-aware config + `exec tmux attach` used by hetzner/daytona).\n */\n\nimport {\n type AttachKind,\n type AttachSpec,\n type BoxRecord,\n type BuildAttachOptions,\n} from '@agentbox/core';\nimport { renderInnerCommand } from '@agentbox/sandbox-cloud';\nimport { detectSbx } from './sbx-cli.js';\nimport { ensureFreshCredentials, resolveCredentials } from './sdk.js';\n\nexport async function buildVercelAttach(\n box: BoxRecord,\n kind: AttachKind,\n opts?: BuildAttachOptions,\n): Promise<AttachSpec> {\n const sandboxId = box.cloud?.sandboxId;\n if (!sandboxId) {\n throw new Error(`vercel box ${box.name} has no sandboxId — record is malformed`);\n }\n\n const det = await detectSbx();\n if (!det.installed || !det.bin) {\n throw new Error(\n 'Vercel interactive attach needs the Vercel `sandbox` CLI — run ' +\n '`agentbox vercel login` (it installs it) or `npm install -g sandbox`.',\n );\n }\n\n await ensureFreshCredentials();\n const { token, teamId, projectId } = resolveCredentials();\n\n // Interactive (real PTY) only for live shell/agent attaches. Detached\n // pre-start and logs stream non-interactively.\n const interactive = (kind === 'shell' || kind === 'agent') && !opts?.detached;\n\n // `sbx exec` (unlike `ssh -t`) forwards neither TERM nor the locale, so the\n // box session lands in TERM=unknown + an ASCII (POSIX) locale — tmux then\n // collapses Claude Code's Unicode glyphs (logo, spinner, box-drawing) to `_`.\n // Force a UTF-8 locale + a 256color TERM (matching the host PTY wrapper) so\n // the tmux server + the agent it spawns render correctly.\n const envPrelude = 'export LANG=C.UTF-8 LC_ALL=C.UTF-8 TERM=xterm-256color; ';\n const inner = envPrelude + renderInnerCommand(kind, opts);\n\n const argv = [\n det.bin,\n 'exec',\n '--sudo',\n ...(interactive ? ['-i'] : []),\n '--project',\n projectId,\n '--scope',\n teamId,\n sandboxId,\n '--',\n 'sudo',\n '-u',\n 'vscode',\n '-H',\n 'bash',\n '-lc',\n inner,\n ];\n\n return { argv, env: { VERCEL_AUTH_TOKEN: token } };\n}\n"],"mappings":"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","names":["readFile","creds","readFile","readCliStamp"]}
1
+ {"version":3,"sources":["../../../packages/sandbox-vercel/src/index.ts","../../../packages/sandbox-vercel/src/backend.ts","../../../packages/sandbox-vercel/src/retry.ts","../../../packages/sandbox-vercel/src/prepare.ts","../../../packages/sandbox-vercel/src/build-attach.ts"],"sourcesContent":["/**\n * The Vercel Sandbox provider. A thin `CloudBackend` over `@vercel/sandbox`,\n * composed via `@agentbox/sandbox-cloud`'s `createCloudProvider` for everything\n * provider-agnostic (workspace seeding, ctl/VNC launch, state, relay polling).\n *\n * Three capabilities are overridden on top of the cloud scaffold:\n * - `prepare` — bake the base snapshot (Vercel can't build from a Dockerfile).\n * - `buildAttach` — SDK-streaming tmux bridge (Vercel has no SSH).\n * - `checkpoint` — store the Vercel snapshot *id* in the manifest so restore\n * boots from it (Vercel snapshots are id-addressed, not name-addressed).\n *\n * `launchDockerd: false` because Vercel Sandbox can't run nested containers.\n */\n\nimport type { BoxRecord, Provider, ProviderCheckpoint } from '@agentbox/core';\nimport {\n createCloudProvider,\n currentCloudBaseFingerprint,\n listCloudCheckpoints,\n removeCloudCheckpointDir,\n resolveCloudCheckpoint,\n writeCloudCheckpointManifest,\n} from '@agentbox/sandbox-cloud';\nimport {\n vercelBackend,\n snapshotVercelSandbox,\n deleteVercelSnapshot,\n DEFAULT_BOX_IMAGE_REF,\n} from './backend.js';\nimport { readCliStamp, recordBox } from '@agentbox/sandbox-core';\nimport { prepareVercelProvider } from './prepare.js';\nimport { buildVercelAttach } from './build-attach.js';\nimport { currentVercelBaseFingerprintLive } from './prepared-state.js';\n\nconst BACKEND_NAME = 'vercel';\n\nconst cloudProvider = createCloudProvider(vercelBackend, {\n // Vercel couples RAM to vCPU at 2048 MB/vCPU; disk is a fixed 32 GB NVMe.\n defaultResources: { cpu: 2, memory: 4, disk: 32 },\n launchDockerd: false,\n});\n\n/**\n * Vercel-specific checkpoint capability. Unlike the scaffold's default (which\n * stores a caller-chosen snapshot *name*), we capture the opaque Vercel\n * snapshot id and store THAT in the manifest's `snapshotName` field — the cloud\n * create flow passes `manifest.snapshotName` straight to\n * `provision({ snapshot })`, and the Vercel backend boots from it as a snapshot\n * id. (The scaffold's `cloudSnapshotName` project-scoping isn't needed — Vercel\n * snapshot ids are already globally unique.)\n */\nconst vercelCheckpoint: ProviderCheckpoint = {\n async create(box: BoxRecord, name: string) {\n if (!box.projectRoot) {\n throw new Error(\n 'cloud checkpoint requires the box to have a project root (run `agentbox checkpoint` from inside the project)',\n );\n }\n if (!box.cloud?.sandboxId) {\n throw new Error(`vercel box ${box.name} has no sandboxId — record is malformed`);\n }\n // NOTE: snapshotting stops the source sandbox; persistent mode resumes it\n // on the next call. Surfaced to the user in `agentbox checkpoint` docs.\n const snapshotId = await snapshotVercelSandbox(box.cloud.sandboxId);\n // The box is now stopped — persist it so the fast `agentbox list` path\n // doesn't show a stale `running` after a checkpoint. Best-effort.\n try {\n await recordBox({ ...box, cloud: { ...box.cloud, lastState: 'paused' } });\n } catch {\n // not worth failing the checkpoint over a state-record write\n }\n const info = await writeCloudCheckpointManifest(box.projectRoot, BACKEND_NAME, name, {\n snapshotName: snapshotId,\n sourceBoxId: box.id,\n sourceBoxName: box.name,\n baseProvider: BACKEND_NAME,\n baseFingerprint: currentCloudBaseFingerprint(BACKEND_NAME),\n cliVersion: readCliStamp().cliVersion,\n });\n return { ref: info.name };\n },\n async list(projectRoot: string) {\n const entries = await listCloudCheckpoints(projectRoot, BACKEND_NAME);\n return entries.map((e) => ({ ref: e.name, createdAt: e.manifest.createdAt }));\n },\n async remove(projectRoot: string, ref: string) {\n const entry = await resolveCloudCheckpoint(projectRoot, BACKEND_NAME, ref);\n if (!entry) return;\n try {\n await deleteVercelSnapshot(entry.manifest.snapshotName);\n } catch {\n // best-effort: drop the local manifest even if the remote delete failed\n // (network/perms/already-gone) so the user isn't left with a dead pointer.\n }\n await removeCloudCheckpointDir(projectRoot, BACKEND_NAME, ref);\n },\n};\n\nexport const vercelProvider: Provider = {\n ...cloudProvider,\n prepare: prepareVercelProvider,\n buildAttach: buildVercelAttach,\n checkpoint: vercelCheckpoint,\n baseFingerprint: () => currentVercelBaseFingerprintLive(),\n};\n\nexport { vercelBackend, DEFAULT_BOX_IMAGE_REF };\nexport { ensureVercelEnvLoaded, reloadVercelEnv } from './env-loader.js';\nexport { ensureVercelCredentials } from './credentials.js';\nexport type { EnsureVercelCredentialsOptions } from './credentials.js';\nexport {\n readVercelCredStatus,\n secretsPath,\n maskKey,\n type VercelCredStatus,\n} from './credentials.js';\nexport {\n prepareVercel,\n prepareVercelProvider,\n type PrepareVercelOptions,\n type PrepareVercelResult,\n} from './prepare.js';\nexport {\n currentVercelBaseFingerprintLive,\n ensureVercelBaseSnapshot,\n preparedStatePath,\n readPreparedState,\n writePreparedState,\n updatePreparedState,\n type PreparedVercelState,\n type PreparedVercelBase,\n} from './prepared-state.js';\nexport {\n RUNTIME_ASSETS,\n candidatesFor,\n resolveRuntimeAssets,\n findStagedCliRuntimeRoot,\n type RuntimeAsset,\n type ResolvedAsset,\n} from './runtime-assets.js';\nexport { buildVercelAttach } from './build-attach.js';\n","/**\n * Vercel `CloudBackend` — maps the provider-neutral cloud primitives onto\n * `@vercel/sandbox` v2 (Firecracker microVMs + snapshots). Composed into a full\n * `Provider` by `@agentbox/sandbox-cloud`'s `createCloudProvider`.\n *\n * Platform shape this backend is built around (see docs/cloud-providers.md):\n * - No custom image — sandboxes boot from a Vercel snapshot baked once by\n * `agentbox prepare --provider vercel`. `provision` always needs a snapshot\n * id (the prepared base, or a cloud-checkpoint snapshot).\n * - No SSH — `attachArgv` is intentionally omitted; the provider overrides\n * `buildAttach` with a Vercel-SDK-streaming helper instead.\n * - No nested containers — dockerd is disabled at the provider level.\n * - Persistent sandboxes auto-snapshot on stop and auto-resume on the next\n * `Sandbox.get({ resume: true })`, which is how pause/resume map cleanly.\n * - The sandbox's native user is `vercel-sandbox`; agentbox standardizes on\n * `vscode` (uid 1000), created by provision.sh. So `exec` drops privileges\n * to `vscode` (root → `sudo -u vscode`) unless the caller asks for root,\n * and `uploadFile` chowns to uid 1000 after the SDK writes as\n * `vercel-sandbox`.\n * - Max 4 exposed ports: we use 80 (WebProxy), 6080 (noVNC), 8788 (relay/ctl\n * bridge). One slot is left free for a future per-service expose.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport type {\n CloudBackend,\n CloudExecOptions,\n CloudExecResult,\n CloudFileEntry,\n CloudHandle,\n CloudPreviewUrl,\n CloudProvisionRequest,\n CloudSandboxSummary,\n CloudState,\n} from '@agentbox/core';\nimport type { NetworkPolicy } from '@vercel/sandbox';\nimport {\n ensureFreshCredentials,\n resolveCredentials,\n Sandbox,\n Snapshot,\n type SandboxType,\n} from './sdk.js';\nimport { withVercelRetry } from './retry.js';\nimport { readPreparedState } from './prepared-state.js';\n\n/** Sentinel image ref the cloud-provider hands us when no --image was passed. */\nexport const DEFAULT_BOX_IMAGE_REF = 'agentbox/box:dev';\n\n/** Box user agentbox standardizes on. provision.sh creates it (uid auto-assigned —\n * the Vercel default user may already hold 1000, and there are no bind mounts so\n * the exact uid is irrelevant). chown targets it by name, not number. */\nconst BOX_USER = 'vscode';\nconst BOX_OWNER = 'vscode:vscode';\n\n/**\n * Base ports exposed at create. Vercel REJECTS privileged ports (<1024) with a\n * 400, so we cannot expose the scaffold's WebProxy on :80. Instead the in-box\n * WebProxy binds 8080 (set via `webProxyPort` → AGENTBOX_WEB_PROXY_PORT) and we\n * expose 8080 here so `sandbox.domain(8080)` routes to it → the in-box `expose:`\n * service. Ports are fixed at create (update can't add a routable port to a\n * running sandbox — verified), so 8080 must be in this base set. The other two\n * base ports are 6080 (noVNC) and 8788 (the relay/ctl bridge the host poller\n * reaches via `sandbox.domain(8788)`). Remaining slots (up to VERCEL_MAX_PORTS)\n * are filled at create from `agentbox.yaml` `expose:` ports (see buildExposedPorts).\n */\nexport const VERCEL_EXPOSED_PORTS = [8080, 6080, 8788] as const;\n\n/** Vercel's hard per-sandbox exposed-port cap. */\nexport const VERCEL_MAX_PORTS = 4;\n\n/**\n * Merge requested `expose:` service ports into the base set: drop privileged\n * (<1024 — Vercel 400s) and out-of-range ports + dupes, preserve order, and cap\n * at Vercel's 4-port limit. A preview URL only routes to a port declared here at\n * create time, so this is what makes `services.*.expose` reachable on Vercel.\n */\nexport function buildExposedPorts(extra: readonly number[] | undefined): number[] {\n const ports = [...VERCEL_EXPOSED_PORTS] as number[];\n const seen = new Set<number>(ports);\n for (const p of extra ?? []) {\n if (ports.length >= VERCEL_MAX_PORTS) break;\n if (Number.isInteger(p) && p >= 1024 && p < 65_536 && !seen.has(p)) {\n ports.push(p);\n seen.add(p);\n }\n }\n return ports;\n}\n\n/**\n * Parse the `box.vercelNetworkPolicy` config string into a Vercel\n * `NetworkPolicy`. `''`/unset → undefined (SDK default = allow-all). The\n * literals `allow-all` / `deny-all` pass through; anything else is treated as a\n * comma-separated domain allowlist `{ allow: [...] }` (everything else denied).\n */\nexport function parseNetworkPolicy(raw: string | undefined): NetworkPolicy | undefined {\n const v = (raw ?? '').trim();\n if (v === '') return undefined;\n if (v === 'allow-all' || v === 'deny-all') return v;\n const allow = v\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return allow.length > 0 ? { allow } : undefined;\n}\n\n/**\n * Default per-session timeout. 45 min is the Hobby ceiling, so it's safe across\n * all plans; persistent mode makes a hit transparent (the VM auto-snapshots and\n * auto-resumes on the next SDK call). Pro/Enterprise users who want a longer\n * single session can rely on `extendTimeout` / future config.\n */\nconst DEFAULT_TIMEOUT_MS = 45 * 60_000;\n\n/**\n * Per-box snapshot retention. Keep one auto-snapshot, never expiring, so a\n * paused box can always resume; `destroy` purges a box's own snapshot explicitly.\n *\n * `deleteEvicted: false` is load-bearing, NOT a tweak. A box boots from a shared\n * snapshot (the prepared base, or a `setup` checkpoint), and Vercel reports that\n * source as the box's `currentSnapshotId` until it takes its first auto-snapshot\n * — i.e. the source is the first member of this box's retention window. With\n * `deleteEvicted: true`, the box's first stop/snapshot evicts the source and\n * DELETES it, nuking the shared base/checkpoint every other box depends on, so\n * every later `create` 410s with \"Snapshot expired or deleted.\" (Same hazard the\n * `destroy` guard already dodges, but eviction is automatic and bypasses it.)\n * `false` keeps evicted snapshots around (they fall back to `snapshotExpiration`,\n * which we pin to 0 = never at create) — trading a little snapshot accumulation\n * for never deleting a snapshot another box boots from.\n */\nconst KEEP_LAST_SNAPSHOTS = { count: 1, expiration: 0, deleteEvicted: false } as const;\n\nfunction creds(): Partial<{ token: string; teamId: string; projectId: string }> {\n return resolveCredentials();\n}\n\n/** Single-quote a string for safe embedding inside a `bash -lc '<…>'`. */\nfunction shq(s: string): string {\n return \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n\nasync function getSandbox(id: string): Promise<SandboxType> {\n // resume:false — plain handle resolution; lifecycle methods opt into resume.\n return Sandbox.get({ name: id, resume: false, ...creds() });\n}\n\nasync function maybeGetSandbox(id: string): Promise<SandboxType | null> {\n try {\n return await getSandbox(id);\n } catch {\n return null;\n }\n}\n\n/**\n * Map Vercel's session status onto our 4-value `CloudState`. Transitional\n * states report as 'running' so callers don't ping-pong; 'stopped' maps to\n * 'paused' because a persistent sandbox keeps an auto-snapshot and resumes on\n * the next call (our pause semantics). 'aborted'/'failed' → 'missing'.\n */\nfunction mapState(s: string | undefined): CloudState {\n switch (s) {\n case 'running':\n return 'running';\n case 'pending':\n case 'stopping':\n case 'snapshotting':\n return 'running';\n case 'stopped':\n return 'paused';\n case 'aborted':\n case 'failed':\n default:\n return 'missing';\n }\n}\n\n/**\n * Build a `runCommand` invocation that runs `cmd` (already a shell string) as\n * the box user (`vscode`) by default, or as root when requested. Always starts\n * the SDK command as root (`sudo: true`) so the inner `sudo -u vscode` is\n * reliably passwordless, then drops privileges. cwd + env are applied inside\n * the dropped shell so they land in the right user/home context.\n */\nfunction buildRunCommand(\n cmd: string,\n opts?: CloudExecOptions,\n): { cmd: string; args: string[]; sudo: boolean } {\n const prelude: string[] = [];\n if (opts?.cwd) prelude.push(`cd ${shq(opts.cwd)}`);\n for (const [k, v] of Object.entries(opts?.env ?? {})) {\n // The value is shell-quoted, but the key is interpolated bare into a\n // `bash -lc` string that runs as root — reject anything that isn't a POSIX\n // env-var name so a key like `x;rm -rf /` can't inject a command.\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) {\n throw new Error(`vercel exec: invalid env var name ${JSON.stringify(k)}`);\n }\n prelude.push(`export ${k}=${shq(v)}`);\n }\n const inner = [...prelude, cmd].join('\\n');\n const user = opts?.user ?? BOX_USER;\n if (user === 'root') {\n return { cmd: 'bash', args: ['-lc', inner], sudo: true };\n }\n return {\n cmd: 'bash',\n args: ['-lc', `sudo -u ${user} -H bash -lc ${shq(inner)}`],\n sudo: true,\n };\n}\n\nexport const vercelBackend: CloudBackend = {\n name: 'vercel',\n\n // Vercel rejects privileged ports (<1024) and can't add a routable port to a\n // running sandbox (update registers a route that 502s — verified). So the\n // in-box WebProxy binds 8080 (exposed at create via VERCEL_EXPOSED_PORTS) and\n // `agentbox url` resolves sandbox.domain(8080) → WebProxy → the in-box service.\n webProxyPort: 8080,\n\n async provision(req: CloudProvisionRequest): Promise<CloudHandle> {\n await ensureFreshCredentials();\n // Resolve the snapshot to boot from: an explicit cloud-checkpoint snapshot\n // (req.snapshot) wins, else the prepared base. Vercel can't build from a\n // Dockerfile, so there is no image fallback — fail loud with the fix.\n const snapshotId = req.snapshot ?? readPreparedState().base?.snapshotId;\n if (!snapshotId) {\n throw new Error(\n 'no Vercel base snapshot found.\\n' +\n 'Run `agentbox prepare --provider vercel` first — Vercel cannot build images ' +\n 'from a Dockerfile, so the base snapshot is a one-time prerequisite.',\n );\n }\n const networkPolicy = parseNetworkPolicy(req.networkPolicy);\n // No-retry: Sandbox.create is billable and non-idempotent — a timeout after\n // the request reached the origin could leave a duplicate sandbox we can't\n // reference for cleanup.\n const handle = await withVercelRetry(\n { method: 'provision', retryOnAmbiguous: false, attemptTimeoutMs: 900_000, backoffMs: [] },\n async () => {\n const sb = await Sandbox.create({\n name: req.name,\n source: { type: 'snapshot', snapshotId },\n resources: { vcpus: req.resources?.cpu ?? 2 },\n ports: buildExposedPorts(req.exposePorts),\n timeout: req.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n env: req.env,\n tags: { agentbox: 'true', 'agentbox.name': req.name },\n persistent: true,\n // Pin the sandbox-default expiration to never. Evicted snapshots (see\n // KEEP_LAST_SNAPSHOTS) fall back to this, so the shared base/checkpoint\n // a box boots from is never re-stamped with a finite expiry on eviction.\n snapshotExpiration: 0,\n keepLastSnapshots: { ...KEEP_LAST_SNAPSHOTS },\n ...(networkPolicy ? { networkPolicy } : {}),\n ...creds(),\n });\n return { sandboxId: sb.name };\n },\n );\n // Agent credentials are seeded by `createCloudProvider`'s unified\n // post-provision step (`seedAgentVolumesIfFresh`) via `uploadFile` + `exec`\n // — the symlinks baked into provision.sh route ~/.claude/.credentials.json\n // etc. through to `~/.agentbox-creds/<agent>/`.\n return handle;\n },\n\n async get(sandboxId: string): Promise<CloudHandle | null> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'get', retryOnAmbiguous: true }, async () => {\n const sb = await maybeGetSandbox(sandboxId);\n return sb ? { sandboxId: sb.name } : null;\n });\n },\n\n async list(): Promise<CloudSandboxSummary[]> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'list', retryOnAmbiguous: true }, async () => {\n const page = await Sandbox.list({ ...creds() });\n const items = await page.toArray();\n return items\n .filter((sb) => sb.tags?.['agentbox'] === 'true')\n .map((sb): CloudSandboxSummary => {\n const summary: CloudSandboxSummary = { sandboxId: sb.name };\n const friendly = sb.tags?.['agentbox.name'] ?? sb.name;\n if (friendly) summary.name = friendly;\n if (typeof sb.createdAt === 'number') {\n summary.createdAt = new Date(sb.createdAt).toISOString();\n }\n summary.state = mapState(sb.status);\n return summary;\n });\n });\n },\n\n async start(h: CloudHandle): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'start', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n // resume:true auto-resumes a persistent sandbox from its current snapshot.\n await Sandbox.get({ name: h.sandboxId, resume: true, ...creds() });\n },\n );\n },\n\n async stop(h: CloudHandle): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'stop', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n const sb = await getSandbox(h.sandboxId);\n // For a persistent sandbox this captures an auto-snapshot and shuts the\n // VM down — resume happens lazily on the next Sandbox.get.\n await sb.stop();\n },\n );\n },\n\n // pause == stop on Vercel (the auto-snapshot IS the cold-storage state).\n async pause(h: CloudHandle): Promise<void> {\n await this.stop(h);\n },\n\n async resume(h: CloudHandle): Promise<void> {\n await this.start(h);\n },\n\n async destroy(h: CloudHandle): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'destroy', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n const sb = await maybeGetSandbox(h.sandboxId);\n if (!sb) return; // already gone — destroy is idempotent\n // Purge only a snapshot THIS box created (its own stop-time auto-\n // snapshot), never the shared base/source it booted from. A fresh box\n // has currentSnapshotId === sourceSnapshotId === the prepared base, and\n // deleting that would nuke the base snapshot every other box depends on.\n const snapId = sb.currentSnapshotId;\n const source = sb.sourceSnapshotId;\n const base = readPreparedState().base?.snapshotId;\n const ownSnapshot =\n snapId !== undefined && snapId !== source && snapId !== base;\n await sb.delete();\n if (ownSnapshot) {\n try {\n const snap = await Snapshot.get({ snapshotId: snapId, ...creds() });\n await snap.delete();\n } catch {\n // best-effort: a snapshot already gone is fine; the user can clean\n // stragglers from the Vercel dashboard.\n }\n }\n },\n );\n },\n\n async state(h: CloudHandle): Promise<CloudState> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'state', retryOnAmbiguous: true }, async () => {\n const sb = await maybeGetSandbox(h.sandboxId);\n if (!sb) return 'missing';\n return mapState(sb.status);\n });\n },\n\n async exec(h: CloudHandle, cmd: string, opts?: CloudExecOptions): Promise<CloudExecResult> {\n await ensureFreshCredentials();\n return withVercelRetry(\n {\n method: 'exec',\n retryOnAmbiguous: opts?.noRetry ? false : true,\n attemptTimeoutMs: opts?.attemptTimeoutMs ?? 120_000,\n backoffMs: opts?.noRetry ? [] : undefined,\n },\n async () => {\n const sb = await getSandbox(h.sandboxId);\n const r = await sb.runCommand(buildRunCommand(cmd, opts));\n const [stdout, stderr] = await Promise.all([r.stdout(), r.stderr()]);\n return { exitCode: r.exitCode, stdout, stderr };\n },\n );\n },\n\n async uploadFile(h: CloudHandle, localPath: string, remotePath: string): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'uploadFile', retryOnAmbiguous: true, attemptTimeoutMs: 300_000 },\n async () => {\n const content = await readFile(localPath);\n const sb = await getSandbox(h.sandboxId);\n await sb.writeFiles([{ path: remotePath, content }]);\n // writeFiles writes as `vercel-sandbox`; chown to the box user so the\n // scaffold's vscode-context reads/extractions succeed. Best-effort —\n // a chown failure on a world-readable /tmp staging file is harmless.\n try {\n await sb.runCommand({ cmd: 'chown', args: [BOX_OWNER, remotePath], sudo: true });\n } catch {\n // ignore — file is at least present and readable\n }\n },\n );\n },\n\n async downloadFile(h: CloudHandle, remotePath: string, localPath: string): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'downloadFile', retryOnAmbiguous: true, attemptTimeoutMs: 300_000 },\n async () => {\n const sb = await getSandbox(h.sandboxId);\n const written = await sb.downloadFile(\n { path: remotePath },\n { path: localPath },\n { mkdirRecursive: true },\n );\n if (written === null) {\n throw new Error(`vercel downloadFile: source not found: ${remotePath}`);\n }\n },\n );\n },\n\n async listFiles(h: CloudHandle, remoteDir: string): Promise<CloudFileEntry[]> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'listFiles', retryOnAmbiguous: true }, async () => {\n const sb = await getSandbox(h.sandboxId);\n const entries = await sb.fs.readdir(remoteDir, { withFileTypes: true });\n return entries.map((e) => ({ name: e.name, isDir: e.isDirectory() }));\n });\n },\n\n async previewUrl(h: CloudHandle, port: number): Promise<CloudPreviewUrl> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'previewUrl', retryOnAmbiguous: true }, async () => {\n const sb = await getSandbox(h.sandboxId);\n // sb.domain(port) is a public HTTPS URL (no header token needed).\n return { url: sb.domain(port), token: undefined };\n });\n },\n\n // Fewer params than the interface's (h, port, expiresInSeconds) is fine —\n // Vercel sandbox domains are already public + browser-usable, so the signed\n // URL is just the standard one (the TTL is governed by the sandbox session\n // lifetime, not a per-URL signature, so the expiry arg is irrelevant here).\n async signedPreviewUrl(h: CloudHandle, port: number): Promise<CloudPreviewUrl> {\n return this.previewUrl(h, port);\n },\n\n async snapshotExists(snapshotName: string): Promise<boolean> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'snapshotExists', retryOnAmbiguous: true }, async () => {\n try {\n const snap = await Snapshot.get({ snapshotId: snapshotName, ...creds() });\n // `Snapshot.get` resolves deleted/failed tombstones (status field) rather\n // than throwing, so \"didn't throw\" wrongly passes a dead snapshot. Only a\n // 'created' snapshot can actually boot a sandbox.\n return snap.status === 'created';\n } catch {\n return false;\n }\n });\n },\n\n // NOTE: no `createSnapshot`/`deleteSnapshot` here. Vercel snapshots are\n // addressed by an opaque id (not a caller-chosen name), which doesn't fit the\n // CloudBackend `createSnapshot(handle, name): void` contract — the provider\n // needs the id back to store it in the checkpoint manifest. The Vercel\n // provider therefore overrides the whole `checkpoint` capability in index.ts\n // using `snapshotVercelSandbox` / `deleteVercelSnapshot` below.\n};\n\n/**\n * Snapshot a running sandbox and return the resulting Vercel snapshot id.\n * `sb.snapshot()` stops the source sandbox as part of capture; persistent mode\n * resumes it on the next SDK call, so the box comes back automatically.\n */\nexport async function snapshotVercelSandbox(sandboxId: string): Promise<string> {\n await ensureFreshCredentials();\n return withVercelRetry(\n { method: 'createSnapshot', retryOnAmbiguous: false, attemptTimeoutMs: 900_000, backoffMs: [] },\n async () => {\n const sb = await getSandbox(sandboxId);\n const snap = await sb.snapshot({ expiration: 0 });\n return snap.snapshotId;\n },\n );\n}\n\n/** Delete a Vercel snapshot by id. Idempotent — a missing snapshot is success. */\nexport async function deleteVercelSnapshot(snapshotId: string): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry({ method: 'deleteSnapshot', retryOnAmbiguous: true }, async () => {\n try {\n const snap = await Snapshot.get({ snapshotId, ...creds() });\n await snap.delete();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (/not.?found|404/i.test(msg)) return; // idempotent\n throw err;\n }\n });\n}\n","/**\n * Bounded retry wrapper for Vercel Sandbox SDK calls — mirrors\n * `withDaytonaRetry` / `withHetznerRetry` in shape and intent. The Vercel\n * control plane rate-limits (429) and can return transient 5xx during\n * incidents; without bounded retries those propagate as wedges in the calling\n * lifecycle code.\n *\n * Non-idempotent ops (`provision`/`Sandbox.create`, `createSnapshot`) pass\n * `retryOnAmbiguous: false` so a timeout after the request reached the origin\n * doesn't create a duplicate billable sandbox/snapshot.\n */\n\nexport interface WithRetryOptions {\n method: string;\n /** Per-attempt timeout (ms). Default 30_000. */\n attemptTimeoutMs?: number;\n /** Backoff before attempts 2, 3, … (ms). Default [1000, 2000, 4000]. */\n backoffMs?: readonly number[];\n /**\n * Retry on errors where we can't be sure the server applied the request\n * (connection failures, per-attempt timeouts, 5xx). Set false for\n * non-idempotent operations where a retry could create a duplicate resource.\n */\n retryOnAmbiguous: boolean;\n /** Override the default stderr retry sink (used by tests). */\n onRetry?: (line: string) => void;\n}\n\nconst DEFAULT_BACKOFF: readonly number[] = [1000, 2000, 4000];\nconst DEFAULT_ATTEMPT_TIMEOUT_MS = 30_000;\n\nclass AttemptTimeoutError extends Error {\n constructor(method: string, ms: number) {\n super(`vercel ${method}: per-attempt timeout after ${String(ms)}ms`);\n this.name = 'AttemptTimeoutError';\n }\n}\n\nexport function isAttemptTimeout(err: unknown): err is AttemptTimeoutError {\n return err instanceof AttemptTimeoutError;\n}\n\n/** HTTP status code dug out of whatever error shape the SDK throws. */\nfunction statusCodeOf(err: unknown): number | undefined {\n if (!err || typeof err !== 'object') return undefined;\n for (const key of ['statusCode', 'status', 'code'] as const) {\n const v = (err as Record<string, unknown>)[key];\n if (typeof v === 'number') return v;\n }\n const resp = (err as { response?: { status?: unknown } }).response;\n if (resp && typeof resp.status === 'number') return resp.status;\n return undefined;\n}\n\nexport function isRetriable(err: unknown, allowAmbiguous: boolean): boolean {\n if (err instanceof AttemptTimeoutError) return allowAmbiguous;\n\n const status = statusCodeOf(err);\n if (status !== undefined) {\n if (status === 429) return true; // rate limited — the server told us to wait\n if (status >= 500 && status <= 599) return allowAmbiguous;\n return false; // 4xx (auth, validation, not_found) — permanent\n }\n\n // Raw fetch / undici errors. Node wraps low-level errors in `{ cause }`.\n if (err && typeof err === 'object') {\n const candidates: unknown[] = [err, (err as { cause?: unknown }).cause];\n for (const c of candidates) {\n if (!c || typeof c !== 'object') continue;\n const code = (c as { code?: unknown }).code;\n if (\n code === 'ECONNRESET' ||\n code === 'ETIMEDOUT' ||\n code === 'ECONNABORTED' ||\n code === 'EAI_AGAIN' ||\n code === 'ECONNREFUSED' ||\n code === 'ENOTFOUND' ||\n code === 'UND_ERR_SOCKET' ||\n code === 'UND_ERR_CONNECT_TIMEOUT'\n ) {\n return allowAmbiguous;\n }\n }\n }\n return false;\n}\n\nexport async function withVercelRetry<T>(\n opts: WithRetryOptions,\n fn: () => Promise<T>,\n): Promise<T> {\n const backoff = opts.backoffMs ?? DEFAULT_BACKOFF;\n const maxAttempts = backoff.length + 1;\n const timeoutMs = opts.attemptTimeoutMs ?? DEFAULT_ATTEMPT_TIMEOUT_MS;\n const log = opts.onRetry ?? defaultRetryLog;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await raceTimeout(fn(), timeoutMs, opts.method);\n } catch (err) {\n const last = attempt === maxAttempts;\n if (last || !isRetriable(err, opts.retryOnAmbiguous)) throw err;\n const delay = backoff[attempt - 1] ?? backoff[backoff.length - 1] ?? 4000;\n log(\n `vercel ${opts.method}: attempt ${String(attempt)} failed (${errorSummary(err)}); retrying in ${String(delay)}ms`,\n );\n await sleep(delay);\n }\n }\n throw new Error(`withVercelRetry: exhausted attempts for ${opts.method}`);\n}\n\nfunction defaultRetryLog(line: string): void {\n process.stderr.write(`\\n[vercel-retry] ${line}\\n`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nasync function raceTimeout<T>(p: Promise<T>, ms: number, method: string): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n p,\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(new AttemptTimeoutError(method, ms)), ms);\n }),\n ]);\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n}\n\nfunction errorSummary(err: unknown): string {\n if (err instanceof Error) {\n const status = statusCodeOf(err);\n return status !== undefined\n ? `${err.name}(${String(status)}): ${truncate(err.message)}`\n : `${err.name}: ${truncate(err.message)}`;\n }\n return truncate(String(err));\n}\n\nfunction truncate(s: string, max = 160): string {\n return s.length > max ? `${s.slice(0, max)}…` : s;\n}\n","/**\n * `agentbox prepare --provider vercel` — bake the per-team Vercel base\n * snapshot. Vercel can't build an image from a Dockerfile, so (like hetzner)\n * we boot a fresh sandbox, run an installer, and snapshot the result. That\n * snapshot id is what every per-box `create` boots from.\n *\n * Flow:\n * 1. Resolve runtime assets + fingerprint the build context. Skip the bake\n * when an up-to-date base snapshot already exists (unless --force).\n * 2. `Sandbox.create({ runtime: 'node24', persistent: false })` — fresh AL2023.\n * 3. `writeFiles` the assets (ctl bundle, helpers, baked configs, provision.sh).\n * 4. Run provision.sh as root, streaming output to the prepare log.\n * 5. Stage host agent static config (claude/codex/opencode) into the snapshot.\n * 6. `sandbox.snapshot({ expiration: 0 })` → the never-expiring base snapshot.\n * 7. Persist the snapshot id into ~/.agentbox/vercel-prepared.json.\n * 8. Delete the builder sandbox.\n *\n * Step 8 is safe: a Vercel snapshot is an independent, id-addressed resource\n * that survives its source sandbox's deletion (verified live — snapshot stays\n * `status: 'created'` and boots a fresh sandbox after the builder is deleted).\n * We delete it best-effort *after* the snapshot id is persisted, so a delete\n * failure only leaves a lingering sandbox for Vercel's reaper, never a broken\n * bake.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { Writable } from 'node:stream';\nimport type { Provider } from '@agentbox/core';\nimport { computeContextSha256, readCliStamp } from '@agentbox/sandbox-core';\nimport {\n stageClaudeStaticForUpload,\n stageCodexStaticForUpload,\n stageOpencodeStaticForUpload,\n type StageResult,\n} from '@agentbox/sandbox-cloud';\nimport { ensureVercelCredentials } from './credentials.js';\nimport {\n ensureFreshCredentials,\n resolveCredentials,\n Sandbox,\n Snapshot,\n type SandboxType,\n} from './sdk.js';\nimport {\n preparedStatePath,\n readPreparedState,\n writePreparedState,\n} from './prepared-state.js';\nimport {\n findStagedCliRuntimeRoot,\n resolveRuntimeAssets,\n type ResolvedAsset,\n} from './runtime-assets.js';\n\nexport interface PrepareVercelOptions {\n name?: string;\n hostWorkspace?: string;\n /** Force re-bake even when an up-to-date base snapshot is recorded. */\n force?: boolean;\n /** vCPUs for the builder sandbox (default 4 for a fast bake). */\n vcpus?: number;\n /** CLI runtime tree (set by the CLI to its dist neighbor). */\n cliRuntimeRoot?: string;\n /** Repo root for the dev fallback (defaults to a cwd-walk). */\n repoRoot?: string;\n onLog?: (line: string) => void;\n}\n\nexport interface PrepareVercelResult {\n snapshotName?: string;\n}\n\nconst BUILDER_TIMEOUT_MS = 25 * 60_000;\nconst SHELL = '/bin/bash';\n\nexport async function prepareVercel(\n opts: PrepareVercelOptions = {},\n): Promise<PrepareVercelResult> {\n await ensureVercelCredentials();\n await ensureFreshCredentials();\n const creds = resolveCredentials();\n const log = opts.onLog ?? (() => {});\n const progress = (s: string) => log(`prepare-vercel: ${s}`);\n\n const assets = resolveRuntimeAssets({\n cliRuntimeRoot: opts.cliRuntimeRoot ?? findStagedCliRuntimeRoot(),\n repoRoot: opts.repoRoot,\n });\n const contextSha = await computeContextSha256(\n assets.map((a) => ({ rel: a.name, abs: a.localPath })),\n );\n\n // Skip-fast: existing base snapshot still on Vercel + matching fingerprint.\n const existing = readPreparedState();\n if (!opts.force && existing.base) {\n const stillThere = await snapshotExists(existing.base.snapshotId, creds);\n if (stillThere && existing.base.contextSha256 === contextSha) {\n progress(\n `base snapshot ${existing.base.snapshotId} already exists (fingerprint ${contextSha.slice(0, 12)} matches); skipping (pass --force to rebuild)`,\n );\n return { snapshotName: existing.base.snapshotId };\n }\n if (!stillThere) {\n progress(`recorded base snapshot ${existing.base.snapshotId} is gone on Vercel; rebuilding`);\n } else {\n progress(\n `build context changed (was ${existing.base.contextSha256?.slice(0, 12) ?? '<none>'}, now ${contextSha.slice(0, 12)}); rebuilding`,\n );\n }\n }\n\n progress(`creating builder sandbox (node24, ${String(opts.vcpus ?? 4)} vcpus)`);\n const sb = await Sandbox.create({\n runtime: 'node24',\n resources: { vcpus: opts.vcpus ?? 4 },\n timeout: BUILDER_TIMEOUT_MS,\n tags: { agentbox: 'true', 'agentbox.role': 'prepare' },\n persistent: false,\n ...creds,\n });\n progress(`builder sandbox ${sb.name} up`);\n\n // 3. Upload assets.\n progress(`uploading ${String(assets.length)} runtime asset(s)`);\n await sb.writeFiles(\n await Promise.all(\n assets.map(async (a: ResolvedAsset) => ({\n path: a.remotePath,\n content: await readFile(a.localPath),\n mode: a.remoteMode,\n })),\n ),\n );\n\n // 4. Run provision.sh as root, streaming output.\n progress('running provision.sh (this takes a few minutes)');\n const install = await sb.runCommand({\n cmd: SHELL,\n args: ['-lc', 'bash /tmp/agentbox-provision.sh 2>&1'],\n sudo: true,\n stdout: lineSink((l) => log(`[provision] ${l}`)),\n stderr: lineSink((l) => log(`[provision] ${l}`)),\n });\n if (install.exitCode !== 0) {\n throw new Error(`provision.sh failed on the builder sandbox (exit ${String(install.exitCode)})`);\n }\n progress('provision.sh complete');\n\n // 5. Stage host agent static config into the snapshot (best-effort).\n await stageAgentConfig(sb, opts.hostWorkspace, log);\n\n // 6. Snapshot (never expires). NOTE: this stops the builder sandbox.\n progress('creating base snapshot (expiration: never)');\n const snap = await sb.snapshot({ expiration: 0 });\n progress(`snapshot created: ${snap.snapshotId}`);\n\n // 7. Persist.\n const cliStamp = readCliStamp();\n writePreparedState({\n schema: 1,\n base: {\n snapshotId: snap.snapshotId,\n contextSha256: contextSha,\n cliVersion: cliStamp.cliVersion,\n cliCommit: cliStamp.cliCommit,\n createdAt: new Date().toISOString(),\n },\n });\n progress(`wrote ${preparedStatePath()}`);\n\n // 8. Delete the builder. The snapshot is an independent resource that\n // survives this (verified live), and its id is already persisted above, so\n // this is best-effort: a failure just leaves the sandbox for Vercel's reaper.\n progress('deleting builder sandbox');\n try {\n await sb.delete();\n progress('builder sandbox deleted');\n } catch (err) {\n progress(\n `builder delete failed (left for Vercel reaper): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n progress(`prepare complete — base snapshot ${snap.snapshotId}`);\n return { snapshotName: snap.snapshotId };\n}\n\nasync function snapshotExists(\n snapshotId: string,\n creds: Partial<{ token: string; teamId: string; projectId: string }>,\n): Promise<boolean> {\n try {\n const snap = await Snapshot.get({ snapshotId, ...creds });\n // `Snapshot.get` resolves even for a deleted/failed snapshot (status field),\n // so a bare \"didn't throw\" wrongly skip-passes a tombstone. Only a 'created'\n // snapshot is bootable — anything else means rebuild.\n return snap.status === 'created';\n } catch {\n return false;\n }\n}\n\nasync function stageAgentConfig(\n sb: SandboxType,\n hostWorkspace: string | undefined,\n log: (line: string) => void,\n): Promise<void> {\n const progress = (s: string) => log(`prepare-vercel: ${s}`);\n progress('staging host agent static config');\n const stagings: Array<{ kind: 'claude' | 'codex' | 'opencode'; tar: StageResult; dest: string }> = [];\n try {\n const claudeTar = await stageClaudeStaticForUpload({ hostWorkspace });\n for (const w of claudeTar.warnings) progress(w);\n if (claudeTar.tarballPath) stagings.push({ kind: 'claude', tar: claudeTar, dest: '/home/vscode/.claude' });\n else await claudeTar.cleanup();\n\n const codexTar = await stageCodexStaticForUpload();\n for (const w of codexTar.warnings) progress(w);\n if (codexTar.tarballPath) stagings.push({ kind: 'codex', tar: codexTar, dest: '/home/vscode/.codex' });\n else await codexTar.cleanup();\n\n const opencodeTar = await stageOpencodeStaticForUpload();\n for (const w of opencodeTar.warnings) progress(w);\n if (opencodeTar.tarballPath) stagings.push({ kind: 'opencode', tar: opencodeTar, dest: '/home/vscode/.local/share/opencode' });\n else await opencodeTar.cleanup();\n\n for (const s of stagings) {\n const remote = `/tmp/agentbox-${s.kind}-static.tar.gz`;\n progress(`uploading ${s.kind} static config`);\n await sb.writeFiles([{ path: remote, content: await readFile(s.tar.tarballPath as string) }]);\n // Extract as vscode so files land owned by the box user. The dest dir\n // already exists (provision.sh's credential-pivot step) — extract into it.\n const extract =\n `sudo -u vscode mkdir -p ${s.dest} && ` +\n `sudo -u vscode tar -xzf ${remote} -C ${s.dest} --no-same-permissions --no-same-owner -m && ` +\n `rm -f ${remote}`;\n const r = await sb.runCommand({ cmd: SHELL, args: ['-lc', extract], sudo: true });\n if (r.exitCode !== 0) {\n progress(`WARN: ${s.kind} static extract failed (exit ${String(r.exitCode)}) — continuing`);\n } else {\n progress(`baked ${s.kind} static config into snapshot`);\n }\n }\n } finally {\n for (const s of stagings) await s.tar.cleanup();\n }\n}\n\n/**\n * Adapt a line-callback to the `Writable` the SDK's `runCommand` streams into.\n * Buffers partial lines so each `onLine` gets a complete line.\n */\nfunction lineSink(onLine: (line: string) => void): Writable {\n let buf = '';\n return new Writable({\n write(chunk: Buffer, _enc: BufferEncoding, cb: () => void) {\n buf += chunk.toString('utf8');\n let nl: number;\n while ((nl = buf.indexOf('\\n')) !== -1) {\n onLine(buf.slice(0, nl));\n buf = buf.slice(nl + 1);\n }\n cb();\n },\n final(cb: () => void) {\n if (buf.length > 0) onLine(buf);\n cb();\n },\n });\n}\n\n/** Provider-level binding used by the CLI's `prepare` command. */\nexport const prepareVercelProvider: NonNullable<Provider['prepare']> = (req) =>\n prepareVercel({\n name: req.name,\n hostWorkspace: req.hostWorkspace ?? process.cwd(),\n force: req.force,\n onLog: req.onLog,\n });\n","/**\n * `buildVercelAttach` — the Vercel provider's override of `Provider.buildAttach`.\n *\n * Vercel has no SSH, so the cloud scaffold's `ssh … -t '<cmd>'` argv is unusable.\n * Instead we drive the official Vercel Sandbox CLI (`sbx`/`sandbox`), which has a\n * real interactive PTY (`sbx exec -i`) and streams non-interactive output live —\n * giving a proper terminal with none of the old send-keys/capture-pane polling.\n *\n * Argv shape (validated against sbx 3.0.1):\n * sbx exec --sudo [-i] --project <p> --scope <team> <name>\n * -- sudo -u vscode -H bash -lc '<inner>'\n *\n * Notes:\n * - The sandbox's default exec user is `vercel-sandbox`; we pass `--sudo` (runs\n * as root) and then `sudo -u vscode -H` so tmux/agents run as the box user in\n * /workspace. Passing `sudo -u vscode …` directly as sbx's argv (not wrapped\n * in an outer `bash -lc`) avoids a double-`bash -lc` re-parse.\n * - `-i` only for interactive shell/agent attaches; detached pre-start and logs\n * run non-interactively (live stdout stream).\n * - The access token is passed via the child env (`VERCEL_AUTH_TOKEN`), never in\n * argv, so it can't leak through `ps`. project/scope are not secret → flags.\n * - `<inner>` is the shared cloud `renderInnerCommand` (same tmux ensure +\n * footer-aware config + `exec tmux attach` used by hetzner/daytona).\n */\n\nimport {\n type AttachKind,\n type AttachSpec,\n type BoxRecord,\n type BuildAttachOptions,\n} from '@agentbox/core';\nimport { renderInnerCommand } from '@agentbox/sandbox-cloud';\nimport { detectSbx } from './sbx-cli.js';\nimport { ensureFreshCredentials, resolveCredentials } from './sdk.js';\n\nexport async function buildVercelAttach(\n box: BoxRecord,\n kind: AttachKind,\n opts?: BuildAttachOptions,\n): Promise<AttachSpec> {\n const sandboxId = box.cloud?.sandboxId;\n if (!sandboxId) {\n throw new Error(`vercel box ${box.name} has no sandboxId — record is malformed`);\n }\n\n const det = await detectSbx();\n if (!det.installed || !det.bin) {\n throw new Error(\n 'Vercel interactive attach needs the Vercel `sandbox` CLI — run ' +\n '`agentbox vercel login` (it installs it) or `npm install -g sandbox`.',\n );\n }\n\n await ensureFreshCredentials();\n const { token, teamId, projectId } = resolveCredentials();\n\n // Interactive (real PTY) only for live shell/agent attaches. Detached\n // pre-start and logs stream non-interactively.\n const interactive = (kind === 'shell' || kind === 'agent') && !opts?.detached;\n\n // `sbx exec` (unlike `ssh -t`) forwards neither TERM nor the locale, so the\n // box session lands in TERM=unknown + an ASCII (POSIX) locale — tmux then\n // collapses Claude Code's Unicode glyphs (logo, spinner, box-drawing) to `_`.\n // Force a UTF-8 locale + a 256color TERM (matching the host PTY wrapper) so\n // the tmux server + the agent it spawns render correctly.\n const envPrelude = 'export LANG=C.UTF-8 LC_ALL=C.UTF-8 TERM=xterm-256color; ';\n const inner = envPrelude + renderInnerCommand(kind, opts);\n\n const argv = [\n det.bin,\n 'exec',\n '--sudo',\n ...(interactive ? ['-i'] : []),\n '--project',\n projectId,\n '--scope',\n teamId,\n sandboxId,\n '--',\n 'sudo',\n '-u',\n 'vscode',\n '-H',\n 'bash',\n '-lc',\n inner,\n ];\n\n return { argv, env: { VERCEL_AUTH_TOKEN: token } };\n}\n"],"mappings":"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","names":["readFile","creds","readFile","readCliStamp"]}
@@ -12,17 +12,18 @@ import {
12
12
  resolveDaytonaCustomClaudeMd,
13
13
  resolveDockerfileContext,
14
14
  writePreparedDaytonaState
15
- } from "./chunk-3WCEB6RE.js";
15
+ } from "./chunk-46NXCJ6Z.js";
16
16
  import {
17
17
  createCloudProvider
18
- } from "./chunk-PIK47622.js";
18
+ } from "./chunk-KTDJ5JTE.js";
19
19
  import {
20
20
  stageClaudeStaticForUpload,
21
21
  stageCodexStaticForUpload,
22
22
  stageOpencodeStaticForUpload
23
- } from "./chunk-GXJNJUEV.js";
24
- import "./chunk-DBBUDKKB.js";
23
+ } from "./chunk-HFQZJO73.js";
24
+ import "./chunk-6QFPYU4Z.js";
25
25
  import "./chunk-G3H2L3O2.js";
26
+ import "./chunk-WJFZJZIM.js";
26
27
 
27
28
  // ../../packages/sandbox-daytona/dist/index.js
28
29
  import { Image } from "@daytonaio/sdk";
@@ -235,4 +236,4 @@ export {
235
236
  getDaytonaStatus,
236
237
  resolveDockerfileContext
237
238
  };
238
- //# sourceMappingURL=dist-SL2QSMBE.js.map
239
+ //# sourceMappingURL=dist-TEKY3GFT.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../packages/sandbox-daytona/src/index.ts","../../../packages/sandbox-daytona/src/prepare.ts","../../../packages/sandbox-daytona/src/status.ts"],"sourcesContent":["/**\n * The Daytona Cloud sandbox provider. A thin `CloudBackend` over\n * `@daytonaio/sdk`, composed via `@agentbox/sandbox-cloud`'s `createCloudProvider`\n * for everything provider-agnostic (workspace seeding, ctl launch, state).\n */\n\nimport type { Provider } from '@agentbox/core';\nimport { createCloudProvider } from '@agentbox/sandbox-cloud';\nimport { daytonaBackend, DEFAULT_BOX_IMAGE_REF } from './backend.js';\nimport { prepareDaytona } from './prepare.js';\nimport { currentDaytonaBaseFingerprintLive } from './prepared-state.js';\n\nconst cloudProvider = createCloudProvider(daytonaBackend, {\n defaultResources: { cpu: 2, memory: 4, disk: 8 },\n});\n\nexport const daytonaProvider: Provider = {\n ...cloudProvider,\n prepare: prepareDaytona,\n baseFingerprint: () => currentDaytonaBaseFingerprintLive(),\n};\n\nexport { daytonaBackend, DEFAULT_BOX_IMAGE_REF };\nexport { resolveDockerfileContext, type DockerfileContext } from './dockerfile-context.js';\nexport { ensureDaytonaEnvLoaded } from './env-loader.js';\nexport { currentDaytonaBaseFingerprintLive } from './prepared-state.js';\n// Called by the CLI provider registry to gate first-run interactive setup.\n// Plain async function — no commander surface — so adding it here doesn't\n// pull commander/clack into consumers' type graphs. The full CLI command\n// lives at the `./cli` subpath export.\nexport { ensureDaytonaCredentials } from './credentials.js';\nexport type { EnsureDaytonaCredentialsOptions } from './credentials.js';\nexport {\n getDaytonaStatus,\n type DaytonaStatus,\n type DaytonaSnapshotSummary,\n type DaytonaVolumeSummary,\n} from './status.js';\n","/**\n * Daytona-side implementation of the `Provider.prepare` hook (`agentbox\n * prepare --provider daytona`). One-time, user-triggered:\n *\n * 1. Stage filtered tarballs of the host's `~/.claude`, `~/.codex`, and\n * `~/.local/share/opencode` static config (no auth tokens — those go on\n * the per-org `agentbox-credentials` volume at create time).\n * 2. Build a layered Daytona `Image`: start from `Dockerfile.box`, then\n * `.addLocalFile()` each staged tarball + `.runCommands()` to extract\n * them into the right paths inside the image.\n * 3. Call `daytona.snapshot.create({ name, image }, { onLogs })` — Daytona\n * runs the build server-side, registers the result as an org-scoped\n * named snapshot, and returns when it's `active`.\n *\n * Replaces the old `agentbox daytona publish-snapshot` flow that\n * provisioned a sandbox + ran an in-sandbox bake + called the broken\n * `_experimental_createSnapshot`. The new path never provisions a sandbox.\n *\n * Source of truth for the public API:\n * https://www.daytona.io/docs/en/snapshots/\n */\n\nimport { Image } from '@daytonaio/sdk';\nimport type { PrepareOptions, PrepareResult } from '@agentbox/core';\nimport {\n stageClaudeStaticForUpload,\n stageCodexStaticForUpload,\n stageOpencodeStaticForUpload,\n type StageResult,\n} from '@agentbox/sandbox-cloud';\nimport { getClient } from './backend.js';\nimport { resolveDaytonaCustomClaudeMd, resolveDockerfileContext } from './dockerfile-context.js';\nimport { ensureDaytonaEnvLoaded } from './env-loader.js';\nimport {\n computeDaytonaContextFingerprint,\n preparedMatches,\n readPreparedDaytonaState,\n writePreparedDaytonaState,\n} from './prepared-state.js';\n\n/**\n * Default snapshot name. Keyed on the first 12 chars of the build-context\n * fingerprint so identical content produces the same snapshot name across\n * machines / CLI runs (idempotent): if the named snapshot already exists\n * on Daytona, prepare can short-circuit without uploading the build\n * context again. Falls back to a timestamp when fingerprinting fails\n * (partial dev rebuild).\n */\nfunction defaultSnapshotName(fingerprint: string | null): string {\n if (fingerprint) return `agentbox-base-${fingerprint.slice(0, 12)}`;\n return `agentbox-base-${Math.floor(Date.now() / 1000).toString()}`;\n}\n\ninterface AgentStage {\n kind: 'claude' | 'codex' | 'opencode';\n /** Path inside the image build that the tarball is uploaded to. */\n remoteTar: string;\n /** Path the image build extracts the tarball into. */\n extractDir: string;\n staged: StageResult;\n}\n\n/**\n * Stage the three agents' static tarballs in parallel. Each `StageResult`'s\n * `cleanup()` must be called by the caller, after the image build picks the\n * file up.\n */\nasync function stageAllAgentStatic(opts: { hostWorkspace?: string }): Promise<AgentStage[]> {\n const [claudeStaged, codexStaged, opencodeStaged] = await Promise.all([\n stageClaudeStaticForUpload({ hostWorkspace: opts.hostWorkspace }),\n stageCodexStaticForUpload(),\n stageOpencodeStaticForUpload(),\n ]);\n return [\n {\n kind: 'claude',\n remoteTar: '/tmp/agentbox-seed-claude.tar.gz',\n extractDir: '/home/vscode/.claude',\n staged: claudeStaged,\n },\n {\n kind: 'codex',\n remoteTar: '/tmp/agentbox-seed-codex.tar.gz',\n extractDir: '/home/vscode/.codex',\n staged: codexStaged,\n },\n {\n kind: 'opencode',\n remoteTar: '/tmp/agentbox-seed-opencode.tar.gz',\n extractDir: '/home/vscode/.local/share/opencode',\n staged: opencodeStaged,\n },\n ];\n}\n\n/**\n * Run `agentbox prepare --provider daytona`. Returns `{ snapshotName }` on\n * success so the CLI can pin it into the project config.\n */\nexport async function prepareDaytona(opts: PrepareOptions): Promise<PrepareResult> {\n ensureDaytonaEnvLoaded();\n const log = opts.onLog ?? (() => {});\n\n // Fingerprint the build context first so we can (a) name the snapshot\n // deterministically and (b) detect cache hits against the recorded\n // prepared state. Computed before staging so an early `null` (partial\n // dev rebuild) doesn't waste a tar staging cycle.\n const fingerprint = await computeDaytonaContextFingerprint();\n const snapshotName =\n opts.name ?? defaultSnapshotName(fingerprint?.contextSha256 ?? null);\n\n const prepared = readPreparedDaytonaState();\n if (\n !opts.force &&\n fingerprint &&\n preparedMatches(prepared, fingerprint.contextSha256)\n ) {\n // Confirm the snapshot still exists on Daytona before short-circuiting.\n // A \"yes locally, no on the server\" mismatch must rebuild.\n try {\n const existing = await getClient().snapshot.get(\n prepared?.base?.imageRef ?? snapshotName,\n );\n if (existing?.name) {\n log(\n `daytona snapshot '${existing.name}' up to date ` +\n `(fingerprint ${fingerprint.contextSha256.slice(0, 12)}) — skipping rebuild ` +\n `(pass --force to override)`,\n );\n return { snapshotName: existing.name };\n }\n log(\n `recorded snapshot '${prepared?.base?.imageRef ?? snapshotName}' not found on Daytona; rebuilding`,\n );\n } catch {\n log(\n `recorded snapshot lookup failed; rebuilding (pass --force to silence)`,\n );\n }\n } else if (!opts.force && fingerprint && prepared?.base?.contextSha256) {\n log(\n `daytona build context changed (was ${prepared.base.contextSha256.slice(0, 12)}, ` +\n `now ${fingerprint.contextSha256.slice(0, 12)}); rebuilding snapshot`,\n );\n }\n\n const ctx = resolveDockerfileContext();\n if (!ctx) {\n throw new Error(\n 'could not locate AgentBox Dockerfile.box build context for the Daytona snapshot. ' +\n 'Set AGENTBOX_DOCKER_CONTEXT to the directory containing Dockerfile.box.',\n );\n }\n\n const daytonaClaudeMd = resolveDaytonaCustomClaudeMd();\n if (!daytonaClaudeMd) {\n throw new Error(\n 'could not locate packages/sandbox-daytona/scripts/custom-system-CLAUDE.md ' +\n '(or its staged runtime/daytona/ copy). Ensure `pnpm -w build` ran so the ' +\n 'CLI staging populated runtime/daytona/.',\n );\n }\n\n const stages = await stageAllAgentStatic({ hostWorkspace: opts.hostWorkspace });\n // Surface staging warnings (codex Keychain landmine, etc.) before the\n // longer build kicks off.\n for (const s of stages) {\n for (const w of s.staged.warnings) log(w);\n }\n\n try {\n let image: Image = Image.fromDockerfile(ctx.dockerfile);\n\n // Overlay the daytona-specific /etc/claude-code/CLAUDE.md on top of the\n // docker-shaped one baked by Dockerfile.box. Daytona boxes have no host\n // .git/ bind-mount, so the in-box hint needs daytona-specific git wording.\n image = image.addLocalFile(daytonaClaudeMd, '/tmp/agentbox-custom-CLAUDE.md');\n const extractCmds: string[] = [\n 'install -m 0644 /tmp/agentbox-custom-CLAUDE.md /etc/claude-code/CLAUDE.md',\n 'rm -f /tmp/agentbox-custom-CLAUDE.md',\n ];\n\n // For each agent whose stage produced a tarball, add the file to the\n // image build context and append a single tar-extract + chown.\n const usable = stages.filter((s) => s.staged.tarballPath !== null);\n for (const s of usable) {\n image = image.addLocalFile(s.staged.tarballPath as string, s.remoteTar);\n extractCmds.push(`mkdir -p ${s.extractDir}`);\n extractCmds.push(`tar -xzf ${s.remoteTar} -C ${s.extractDir}`);\n }\n if (usable.length > 0) {\n // One final pass: own the extracted trees as the box user, then drop the\n // staging tarballs (no point shipping them twice in the image layer).\n extractCmds.push(\n 'chown -R vscode:vscode /home/vscode/.claude /home/vscode/.codex /home/vscode/.local',\n );\n extractCmds.push('rm -f /tmp/agentbox-seed-*.tar.gz');\n }\n // Dockerfile.box ends with `USER vscode`. Switch to root for the\n // install/tar/chown/rm pass — COPYed files are root-owned in /tmp (sticky\n // bit), chown -R on /home/vscode/.* only works as root, and\n // /etc/claude-code is root-owned. Switch back to vscode so the image\n // keeps its default-user invariant.\n image = image\n .dockerfileCommands(['USER root'])\n .runCommands(...extractCmds)\n .dockerfileCommands(['USER vscode']);\n\n const client = getClient();\n log(`creating Daytona snapshot '${snapshotName}'…`);\n const snapshot = await client.snapshot.create(\n { name: snapshotName, image },\n {\n onLogs: (chunk: string) => log(String(chunk).split('\\n').filter(Boolean).join(' ')),\n },\n );\n log(`snapshot '${snapshot.name}' is ${snapshot.state ?? 'created'}`);\n if (fingerprint) {\n writePreparedDaytonaState({\n snapshotName: snapshot.name ?? snapshotName,\n contextSha256: fingerprint.contextSha256,\n });\n log(\n `recorded daytona-prepared.json (fingerprint ${fingerprint.contextSha256.slice(0, 12)})`,\n );\n }\n return { snapshotName: snapshot.name ?? snapshotName };\n } finally {\n await Promise.all(stages.map((s) => s.staged.cleanup()));\n }\n}\n","/**\n * Read-only status helpers for `agentbox prepare` (no-args mode). Surfaces\n * the user-facing inventory of agentbox-owned base images / snapshots /\n * volumes on the configured Daytona org so the user can see at a glance\n * what's already prepared and what isn't.\n *\n * Daytona-side state lives in two places:\n * - **Snapshots** — built by `agentbox prepare --provider daytona`. Listed\n * filtered to `agentbox*` so we don't surface unrelated org snapshots.\n * - **Volumes** — the per-org `agentbox-credentials` volume created lazily\n * by `ensureAgentVolumesForCloud` on first `agentbox create --provider\n * daytona`.\n *\n * All calls swallow auth/network errors and return an empty section — the\n * status command must work for users who don't have Daytona configured.\n */\n\nimport { ensureDaytonaEnvLoaded } from './env-loader.js';\nimport { getClient } from './backend.js';\n\nexport interface DaytonaSnapshotSummary {\n name: string;\n state?: string;\n /** Snapshot size in GB, as reported by Daytona (may be undefined for non-`active` states). */\n sizeGb?: number;\n createdAt?: string;\n errorReason?: string;\n}\n\nexport interface DaytonaVolumeSummary {\n name: string;\n id: string;\n state?: string;\n createdAt?: string;\n lastUsedAt?: string;\n}\n\nexport interface DaytonaStatus {\n /** True when Daytona credentials are present + the SDK could connect. */\n configured: boolean;\n /** Snapshots whose name starts with `agentbox` (case-insensitive). */\n snapshots: DaytonaSnapshotSummary[];\n /** Volumes whose name starts with `agentbox` (case-insensitive). */\n volumes: DaytonaVolumeSummary[];\n /** Non-fatal explanation when `configured` is false. */\n reason?: string;\n}\n\nfunction isAgentboxName(name: unknown): boolean {\n return typeof name === 'string' && name.toLowerCase().startsWith('agentbox');\n}\n\n/**\n * Collect a read-only summary of agentbox-owned snapshots + volumes on the\n * Daytona org. Never throws — failure paths return `configured: false` with\n * a one-line reason.\n */\nexport async function getDaytonaStatus(): Promise<DaytonaStatus> {\n try {\n ensureDaytonaEnvLoaded();\n } catch (err) {\n return {\n configured: false,\n snapshots: [],\n volumes: [],\n reason: err instanceof Error ? err.message : String(err),\n };\n }\n\n let client;\n try {\n client = getClient();\n } catch (err) {\n return {\n configured: false,\n snapshots: [],\n volumes: [],\n reason: err instanceof Error ? err.message.split('\\n')[0] : String(err),\n };\n }\n\n const snapshots: DaytonaSnapshotSummary[] = [];\n const volumes: DaytonaVolumeSummary[] = [];\n let reason: string | undefined;\n\n try {\n const list = await client.snapshot.list();\n const items = (list as { items?: unknown[] }).items ?? (Array.isArray(list) ? list : []);\n for (const s of items) {\n const dto = s as { name?: unknown; state?: unknown; size?: unknown; createdAt?: unknown; errorReason?: unknown };\n if (!isAgentboxName(dto.name)) continue;\n snapshots.push({\n name: dto.name as string,\n state: typeof dto.state === 'string' ? dto.state : undefined,\n sizeGb: typeof dto.size === 'number' ? dto.size : undefined,\n createdAt: typeof dto.createdAt === 'string' ? dto.createdAt : undefined,\n errorReason: typeof dto.errorReason === 'string' ? dto.errorReason : undefined,\n });\n }\n } catch (err) {\n reason = `snapshot list failed: ${err instanceof Error ? err.message.split('\\n')[0] : String(err)}`;\n }\n\n try {\n const list = await client.volume.list();\n const items: unknown[] = Array.isArray(list)\n ? list\n : ((list as { items?: unknown[] }).items ?? []);\n for (const v of items) {\n const dto = v as { name?: unknown; id?: unknown; state?: unknown; createdAt?: unknown; lastUsedAt?: unknown };\n if (!isAgentboxName(dto.name)) continue;\n volumes.push({\n name: dto.name as string,\n id: typeof dto.id === 'string' ? dto.id : '',\n state: typeof dto.state === 'string' ? dto.state : undefined,\n createdAt: typeof dto.createdAt === 'string' ? dto.createdAt : undefined,\n lastUsedAt: typeof dto.lastUsedAt === 'string' ? dto.lastUsedAt : undefined,\n });\n }\n } catch (err) {\n const msg = `volume list failed: ${err instanceof Error ? err.message.split('\\n')[0] : String(err)}`;\n reason = reason ? `${reason}; ${msg}` : msg;\n }\n\n return {\n configured: true,\n snapshots: snapshots.sort((a, b) => (b.createdAt ?? '').localeCompare(a.createdAt ?? '')),\n volumes: volumes.sort((a, b) => a.name.localeCompare(b.name)),\n reason,\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../packages/sandbox-daytona/src/index.ts","../../../packages/sandbox-daytona/src/prepare.ts","../../../packages/sandbox-daytona/src/status.ts"],"sourcesContent":["/**\n * The Daytona Cloud sandbox provider. A thin `CloudBackend` over\n * `@daytonaio/sdk`, composed via `@agentbox/sandbox-cloud`'s `createCloudProvider`\n * for everything provider-agnostic (workspace seeding, ctl launch, state).\n */\n\nimport type { Provider } from '@agentbox/core';\nimport { createCloudProvider } from '@agentbox/sandbox-cloud';\nimport { daytonaBackend, DEFAULT_BOX_IMAGE_REF } from './backend.js';\nimport { prepareDaytona } from './prepare.js';\nimport { currentDaytonaBaseFingerprintLive } from './prepared-state.js';\n\nconst cloudProvider = createCloudProvider(daytonaBackend, {\n defaultResources: { cpu: 2, memory: 4, disk: 8 },\n});\n\nexport const daytonaProvider: Provider = {\n ...cloudProvider,\n prepare: prepareDaytona,\n baseFingerprint: () => currentDaytonaBaseFingerprintLive(),\n};\n\nexport { daytonaBackend, DEFAULT_BOX_IMAGE_REF };\nexport { resolveDockerfileContext, type DockerfileContext } from './dockerfile-context.js';\nexport { ensureDaytonaEnvLoaded } from './env-loader.js';\nexport { currentDaytonaBaseFingerprintLive } from './prepared-state.js';\n// Called by the CLI provider registry to gate first-run interactive setup.\n// Plain async function — no commander surface — so adding it here doesn't\n// pull commander/clack into consumers' type graphs. The full CLI command\n// lives at the `./cli` subpath export.\nexport { ensureDaytonaCredentials } from './credentials.js';\nexport type { EnsureDaytonaCredentialsOptions } from './credentials.js';\nexport {\n getDaytonaStatus,\n type DaytonaStatus,\n type DaytonaSnapshotSummary,\n type DaytonaVolumeSummary,\n} from './status.js';\n","/**\n * Daytona-side implementation of the `Provider.prepare` hook (`agentbox\n * prepare --provider daytona`). One-time, user-triggered:\n *\n * 1. Stage filtered tarballs of the host's `~/.claude`, `~/.codex`, and\n * `~/.local/share/opencode` static config (no auth tokens — those go on\n * the per-org `agentbox-credentials` volume at create time).\n * 2. Build a layered Daytona `Image`: start from `Dockerfile.box`, then\n * `.addLocalFile()` each staged tarball + `.runCommands()` to extract\n * them into the right paths inside the image.\n * 3. Call `daytona.snapshot.create({ name, image }, { onLogs })` — Daytona\n * runs the build server-side, registers the result as an org-scoped\n * named snapshot, and returns when it's `active`.\n *\n * Replaces the old `agentbox daytona publish-snapshot` flow that\n * provisioned a sandbox + ran an in-sandbox bake + called the broken\n * `_experimental_createSnapshot`. The new path never provisions a sandbox.\n *\n * Source of truth for the public API:\n * https://www.daytona.io/docs/en/snapshots/\n */\n\nimport { Image } from '@daytonaio/sdk';\nimport type { PrepareOptions, PrepareResult } from '@agentbox/core';\nimport {\n stageClaudeStaticForUpload,\n stageCodexStaticForUpload,\n stageOpencodeStaticForUpload,\n type StageResult,\n} from '@agentbox/sandbox-cloud';\nimport { getClient } from './backend.js';\nimport { resolveDaytonaCustomClaudeMd, resolveDockerfileContext } from './dockerfile-context.js';\nimport { ensureDaytonaEnvLoaded } from './env-loader.js';\nimport {\n computeDaytonaContextFingerprint,\n preparedMatches,\n readPreparedDaytonaState,\n writePreparedDaytonaState,\n} from './prepared-state.js';\n\n/**\n * Default snapshot name. Keyed on the first 12 chars of the build-context\n * fingerprint so identical content produces the same snapshot name across\n * machines / CLI runs (idempotent): if the named snapshot already exists\n * on Daytona, prepare can short-circuit without uploading the build\n * context again. Falls back to a timestamp when fingerprinting fails\n * (partial dev rebuild).\n */\nfunction defaultSnapshotName(fingerprint: string | null): string {\n if (fingerprint) return `agentbox-base-${fingerprint.slice(0, 12)}`;\n return `agentbox-base-${Math.floor(Date.now() / 1000).toString()}`;\n}\n\ninterface AgentStage {\n kind: 'claude' | 'codex' | 'opencode';\n /** Path inside the image build that the tarball is uploaded to. */\n remoteTar: string;\n /** Path the image build extracts the tarball into. */\n extractDir: string;\n staged: StageResult;\n}\n\n/**\n * Stage the three agents' static tarballs in parallel. Each `StageResult`'s\n * `cleanup()` must be called by the caller, after the image build picks the\n * file up.\n */\nasync function stageAllAgentStatic(opts: { hostWorkspace?: string }): Promise<AgentStage[]> {\n const [claudeStaged, codexStaged, opencodeStaged] = await Promise.all([\n stageClaudeStaticForUpload({ hostWorkspace: opts.hostWorkspace }),\n stageCodexStaticForUpload(),\n stageOpencodeStaticForUpload(),\n ]);\n return [\n {\n kind: 'claude',\n remoteTar: '/tmp/agentbox-seed-claude.tar.gz',\n extractDir: '/home/vscode/.claude',\n staged: claudeStaged,\n },\n {\n kind: 'codex',\n remoteTar: '/tmp/agentbox-seed-codex.tar.gz',\n extractDir: '/home/vscode/.codex',\n staged: codexStaged,\n },\n {\n kind: 'opencode',\n remoteTar: '/tmp/agentbox-seed-opencode.tar.gz',\n extractDir: '/home/vscode/.local/share/opencode',\n staged: opencodeStaged,\n },\n ];\n}\n\n/**\n * Run `agentbox prepare --provider daytona`. Returns `{ snapshotName }` on\n * success so the CLI can pin it into the project config.\n */\nexport async function prepareDaytona(opts: PrepareOptions): Promise<PrepareResult> {\n ensureDaytonaEnvLoaded();\n const log = opts.onLog ?? (() => {});\n\n // Fingerprint the build context first so we can (a) name the snapshot\n // deterministically and (b) detect cache hits against the recorded\n // prepared state. Computed before staging so an early `null` (partial\n // dev rebuild) doesn't waste a tar staging cycle.\n const fingerprint = await computeDaytonaContextFingerprint();\n const snapshotName =\n opts.name ?? defaultSnapshotName(fingerprint?.contextSha256 ?? null);\n\n const prepared = readPreparedDaytonaState();\n if (\n !opts.force &&\n fingerprint &&\n preparedMatches(prepared, fingerprint.contextSha256)\n ) {\n // Confirm the snapshot still exists on Daytona before short-circuiting.\n // A \"yes locally, no on the server\" mismatch must rebuild.\n try {\n const existing = await getClient().snapshot.get(\n prepared?.base?.imageRef ?? snapshotName,\n );\n if (existing?.name) {\n log(\n `daytona snapshot '${existing.name}' up to date ` +\n `(fingerprint ${fingerprint.contextSha256.slice(0, 12)}) — skipping rebuild ` +\n `(pass --force to override)`,\n );\n return { snapshotName: existing.name };\n }\n log(\n `recorded snapshot '${prepared?.base?.imageRef ?? snapshotName}' not found on Daytona; rebuilding`,\n );\n } catch {\n log(\n `recorded snapshot lookup failed; rebuilding (pass --force to silence)`,\n );\n }\n } else if (!opts.force && fingerprint && prepared?.base?.contextSha256) {\n log(\n `daytona build context changed (was ${prepared.base.contextSha256.slice(0, 12)}, ` +\n `now ${fingerprint.contextSha256.slice(0, 12)}); rebuilding snapshot`,\n );\n }\n\n const ctx = resolveDockerfileContext();\n if (!ctx) {\n throw new Error(\n 'could not locate AgentBox Dockerfile.box build context for the Daytona snapshot. ' +\n 'Set AGENTBOX_DOCKER_CONTEXT to the directory containing Dockerfile.box.',\n );\n }\n\n const daytonaClaudeMd = resolveDaytonaCustomClaudeMd();\n if (!daytonaClaudeMd) {\n throw new Error(\n 'could not locate packages/sandbox-daytona/scripts/custom-system-CLAUDE.md ' +\n '(or its staged runtime/daytona/ copy). Ensure `pnpm -w build` ran so the ' +\n 'CLI staging populated runtime/daytona/.',\n );\n }\n\n const stages = await stageAllAgentStatic({ hostWorkspace: opts.hostWorkspace });\n // Surface staging warnings (codex Keychain landmine, etc.) before the\n // longer build kicks off.\n for (const s of stages) {\n for (const w of s.staged.warnings) log(w);\n }\n\n try {\n let image: Image = Image.fromDockerfile(ctx.dockerfile);\n\n // Overlay the daytona-specific /etc/claude-code/CLAUDE.md on top of the\n // docker-shaped one baked by Dockerfile.box. Daytona boxes have no host\n // .git/ bind-mount, so the in-box hint needs daytona-specific git wording.\n image = image.addLocalFile(daytonaClaudeMd, '/tmp/agentbox-custom-CLAUDE.md');\n const extractCmds: string[] = [\n 'install -m 0644 /tmp/agentbox-custom-CLAUDE.md /etc/claude-code/CLAUDE.md',\n 'rm -f /tmp/agentbox-custom-CLAUDE.md',\n ];\n\n // For each agent whose stage produced a tarball, add the file to the\n // image build context and append a single tar-extract + chown.\n const usable = stages.filter((s) => s.staged.tarballPath !== null);\n for (const s of usable) {\n image = image.addLocalFile(s.staged.tarballPath as string, s.remoteTar);\n extractCmds.push(`mkdir -p ${s.extractDir}`);\n extractCmds.push(`tar -xzf ${s.remoteTar} -C ${s.extractDir}`);\n }\n if (usable.length > 0) {\n // One final pass: own the extracted trees as the box user, then drop the\n // staging tarballs (no point shipping them twice in the image layer).\n extractCmds.push(\n 'chown -R vscode:vscode /home/vscode/.claude /home/vscode/.codex /home/vscode/.local',\n );\n extractCmds.push('rm -f /tmp/agentbox-seed-*.tar.gz');\n }\n // Dockerfile.box ends with `USER vscode`. Switch to root for the\n // install/tar/chown/rm pass — COPYed files are root-owned in /tmp (sticky\n // bit), chown -R on /home/vscode/.* only works as root, and\n // /etc/claude-code is root-owned. Switch back to vscode so the image\n // keeps its default-user invariant.\n image = image\n .dockerfileCommands(['USER root'])\n .runCommands(...extractCmds)\n .dockerfileCommands(['USER vscode']);\n\n const client = getClient();\n log(`creating Daytona snapshot '${snapshotName}'…`);\n const snapshot = await client.snapshot.create(\n { name: snapshotName, image },\n {\n onLogs: (chunk: string) => log(String(chunk).split('\\n').filter(Boolean).join(' ')),\n },\n );\n log(`snapshot '${snapshot.name}' is ${snapshot.state ?? 'created'}`);\n if (fingerprint) {\n writePreparedDaytonaState({\n snapshotName: snapshot.name ?? snapshotName,\n contextSha256: fingerprint.contextSha256,\n });\n log(\n `recorded daytona-prepared.json (fingerprint ${fingerprint.contextSha256.slice(0, 12)})`,\n );\n }\n return { snapshotName: snapshot.name ?? snapshotName };\n } finally {\n await Promise.all(stages.map((s) => s.staged.cleanup()));\n }\n}\n","/**\n * Read-only status helpers for `agentbox prepare` (no-args mode). Surfaces\n * the user-facing inventory of agentbox-owned base images / snapshots /\n * volumes on the configured Daytona org so the user can see at a glance\n * what's already prepared and what isn't.\n *\n * Daytona-side state lives in two places:\n * - **Snapshots** — built by `agentbox prepare --provider daytona`. Listed\n * filtered to `agentbox*` so we don't surface unrelated org snapshots.\n * - **Volumes** — the per-org `agentbox-credentials` volume created lazily\n * by `ensureAgentVolumesForCloud` on first `agentbox create --provider\n * daytona`.\n *\n * All calls swallow auth/network errors and return an empty section — the\n * status command must work for users who don't have Daytona configured.\n */\n\nimport { ensureDaytonaEnvLoaded } from './env-loader.js';\nimport { getClient } from './backend.js';\n\nexport interface DaytonaSnapshotSummary {\n name: string;\n state?: string;\n /** Snapshot size in GB, as reported by Daytona (may be undefined for non-`active` states). */\n sizeGb?: number;\n createdAt?: string;\n errorReason?: string;\n}\n\nexport interface DaytonaVolumeSummary {\n name: string;\n id: string;\n state?: string;\n createdAt?: string;\n lastUsedAt?: string;\n}\n\nexport interface DaytonaStatus {\n /** True when Daytona credentials are present + the SDK could connect. */\n configured: boolean;\n /** Snapshots whose name starts with `agentbox` (case-insensitive). */\n snapshots: DaytonaSnapshotSummary[];\n /** Volumes whose name starts with `agentbox` (case-insensitive). */\n volumes: DaytonaVolumeSummary[];\n /** Non-fatal explanation when `configured` is false. */\n reason?: string;\n}\n\nfunction isAgentboxName(name: unknown): boolean {\n return typeof name === 'string' && name.toLowerCase().startsWith('agentbox');\n}\n\n/**\n * Collect a read-only summary of agentbox-owned snapshots + volumes on the\n * Daytona org. Never throws — failure paths return `configured: false` with\n * a one-line reason.\n */\nexport async function getDaytonaStatus(): Promise<DaytonaStatus> {\n try {\n ensureDaytonaEnvLoaded();\n } catch (err) {\n return {\n configured: false,\n snapshots: [],\n volumes: [],\n reason: err instanceof Error ? err.message : String(err),\n };\n }\n\n let client;\n try {\n client = getClient();\n } catch (err) {\n return {\n configured: false,\n snapshots: [],\n volumes: [],\n reason: err instanceof Error ? err.message.split('\\n')[0] : String(err),\n };\n }\n\n const snapshots: DaytonaSnapshotSummary[] = [];\n const volumes: DaytonaVolumeSummary[] = [];\n let reason: string | undefined;\n\n try {\n const list = await client.snapshot.list();\n const items = (list as { items?: unknown[] }).items ?? (Array.isArray(list) ? list : []);\n for (const s of items) {\n const dto = s as { name?: unknown; state?: unknown; size?: unknown; createdAt?: unknown; errorReason?: unknown };\n if (!isAgentboxName(dto.name)) continue;\n snapshots.push({\n name: dto.name as string,\n state: typeof dto.state === 'string' ? dto.state : undefined,\n sizeGb: typeof dto.size === 'number' ? dto.size : undefined,\n createdAt: typeof dto.createdAt === 'string' ? dto.createdAt : undefined,\n errorReason: typeof dto.errorReason === 'string' ? dto.errorReason : undefined,\n });\n }\n } catch (err) {\n reason = `snapshot list failed: ${err instanceof Error ? err.message.split('\\n')[0] : String(err)}`;\n }\n\n try {\n const list = await client.volume.list();\n const items: unknown[] = Array.isArray(list)\n ? list\n : ((list as { items?: unknown[] }).items ?? []);\n for (const v of items) {\n const dto = v as { name?: unknown; id?: unknown; state?: unknown; createdAt?: unknown; lastUsedAt?: unknown };\n if (!isAgentboxName(dto.name)) continue;\n volumes.push({\n name: dto.name as string,\n id: typeof dto.id === 'string' ? dto.id : '',\n state: typeof dto.state === 'string' ? dto.state : undefined,\n createdAt: typeof dto.createdAt === 'string' ? dto.createdAt : undefined,\n lastUsedAt: typeof dto.lastUsedAt === 'string' ? dto.lastUsedAt : undefined,\n });\n }\n } catch (err) {\n const msg = `volume list failed: ${err instanceof Error ? err.message.split('\\n')[0] : String(err)}`;\n reason = reason ? `${reason}; ${msg}` : msg;\n }\n\n return {\n configured: true,\n snapshots: snapshots.sort((a, b) => (b.createdAt ?? '').localeCompare(a.createdAt ?? '')),\n volumes: volumes.sort((a, b) => a.name.localeCompare(b.name)),\n reason,\n };\n}\n"],"mappings":"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","names":[]}
@@ -218,7 +218,7 @@ import {
218
218
  vscodeServerVolumeName,
219
219
  waitForTmuxPaneContent,
220
220
  warmUpClaudeCredentials
221
- } from "./chunk-GXJNJUEV.js";
221
+ } from "./chunk-HFQZJO73.js";
222
222
  import {
223
223
  AmbiguousBoxError,
224
224
  BOX_IMAGE_REGISTRY,
@@ -248,8 +248,9 @@ import {
248
248
  resolveContextFiles,
249
249
  tagImage,
250
250
  writePreparedDockerState
251
- } from "./chunk-DBBUDKKB.js";
251
+ } from "./chunk-6QFPYU4Z.js";
252
252
  import "./chunk-G3H2L3O2.js";
253
+ import "./chunk-WJFZJZIM.js";
253
254
  export {
254
255
  AmbiguousBoxError,
255
256
  BOXES_ROOT,
@@ -498,4 +499,4 @@ export {
498
499
  warmUpClaudeCredentials,
499
500
  writePreparedDockerState
500
501
  };
501
- //# sourceMappingURL=dist-7YB7BMNG.js.map
502
+ //# sourceMappingURL=dist-VAATGBAR.js.map