@madarco/agentbox 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/{_cloud-attach-O6NYTLES.js → _cloud-attach-6C5NMOHD.js} +4 -4
  3. package/dist/{chunk-2GPORKYF.js → chunk-D4Q2RUQI.js} +5 -5
  4. package/dist/chunk-D4Q2RUQI.js.map +1 -0
  5. package/dist/{chunk-MTVI44DW.js → chunk-ECLLV5JH.js} +6 -3
  6. package/dist/chunk-ECLLV5JH.js.map +1 -0
  7. package/dist/{chunk-7UIAO7PC.js → chunk-GUNUBIRB.js} +94 -75
  8. package/dist/chunk-GUNUBIRB.js.map +1 -0
  9. package/dist/{chunk-R4O5WPHW.js → chunk-NVSRGC5W.js} +23 -11
  10. package/dist/chunk-NVSRGC5W.js.map +1 -0
  11. package/dist/{chunk-I24B6AXR.js → chunk-R5XIDQFR.js} +6 -3
  12. package/dist/chunk-R5XIDQFR.js.map +1 -0
  13. package/dist/{chunk-KL36BRN4.js → chunk-SNTHHWKY.js} +70 -17
  14. package/dist/chunk-SNTHHWKY.js.map +1 -0
  15. package/dist/{chunk-LEV3KICD.js → chunk-ZGVMN54V.js} +6 -3
  16. package/dist/{chunk-LEV3KICD.js.map → chunk-ZGVMN54V.js.map} +1 -1
  17. package/dist/{dist-5FQGYRW5.js → dist-4SUIXKSD.js} +5 -5
  18. package/dist/{dist-PZW3GWWU.js → dist-HT2YV6PB.js} +5 -5
  19. package/dist/{dist-TMHSUVTP.js → dist-PJFJNXO2.js} +5 -5
  20. package/dist/{dist-BQNX7RQE.js → dist-ZEGIMYWZ.js} +13 -3
  21. package/dist/index.js +632 -547
  22. package/dist/index.js.map +1 -1
  23. package/dist/{prepared-state-CL4CWXQA-H5THETIM.js → prepared-state-MQHD3M5F-KE4DT3GX.js} +2 -2
  24. package/package.json +5 -5
  25. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +1 -1
  26. package/runtime/docker/packages/ctl/dist/bin.cjs +11 -2
  27. package/runtime/hetzner/agentbox-setup-skill.md +1 -1
  28. package/runtime/hetzner/ctl.cjs +11 -2
  29. package/runtime/relay/bin.cjs +14 -2
  30. package/runtime/vercel/agentbox-setup-skill.md +1 -1
  31. package/runtime/vercel/ctl.cjs +11 -2
  32. package/runtime/vercel/scripts/provision.sh +20 -0
  33. package/share/agentbox-setup/SKILL.md +1 -1
  34. package/dist/chunk-2GPORKYF.js.map +0 -1
  35. package/dist/chunk-7UIAO7PC.js.map +0 -1
  36. package/dist/chunk-I24B6AXR.js.map +0 -1
  37. package/dist/chunk-KL36BRN4.js.map +0 -1
  38. package/dist/chunk-MTVI44DW.js.map +0 -1
  39. package/dist/chunk-R4O5WPHW.js.map +0 -1
  40. /package/dist/{_cloud-attach-O6NYTLES.js.map → _cloud-attach-6C5NMOHD.js.map} +0 -0
  41. /package/dist/{dist-5FQGYRW5.js.map → dist-4SUIXKSD.js.map} +0 -0
  42. /package/dist/{dist-PZW3GWWU.js.map → dist-HT2YV6PB.js.map} +0 -0
  43. /package/dist/{dist-TMHSUVTP.js.map → dist-PJFJNXO2.js.map} +0 -0
  44. /package/dist/{dist-BQNX7RQE.js.map → dist-ZEGIMYWZ.js.map} +0 -0
  45. /package/dist/{prepared-state-CL4CWXQA-H5THETIM.js.map → prepared-state-MQHD3M5F-KE4DT3GX.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../packages/sandbox-hetzner/src/env-loader.ts","../../../packages/sandbox-hetzner/src/client.ts","../../../packages/sandbox-hetzner/src/credentials.ts","../../../packages/sandbox-hetzner/src/egress-ip.ts","../../../packages/sandbox-hetzner/src/retry.ts","../../../packages/sandbox-hetzner/src/firewall.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { resolve } from 'node:path';\n\n/**\n * Hetzner env auto-loader — mirrors `ensureDaytonaEnvLoaded()`. The Hetzner\n * REST client reads `HCLOUD_TOKEN` from `process.env`. We pull it in from\n * `~/.agentbox/secrets.env` so the client Just Works after the user runs\n * `agentbox hetzner login` once.\n *\n * Lookup order (first wins; process.env is never overwritten):\n * 1. `process.env` (already set in the shell).\n * 2. `~/.agentbox/secrets.env` — written by `agentbox hetzner login`.\n *\n * Project-level `.env` / `.env.local` are intentionally NOT consulted: those\n * files belong to the app code being developed, and a `HCLOUD_TOKEN` there\n * is typically meant for in-box infrastructure work, not for the host CLI to\n * harvest and provision VPSes with.\n *\n * Only Hetzner-prefixed keys are imported. Idempotent + side-effect-free\n * after the first call.\n */\nconst HETZNER_KEYS = ['HCLOUD_TOKEN', 'HCLOUD_ENDPOINT'] as const;\n\nlet loaded = false;\n\nexport function ensureHetznerEnvLoaded(): void {\n if (loaded) return;\n loaded = true;\n importHetznerFromFile(resolve(homedir(), '.agentbox', 'secrets.env'));\n}\n\nfunction importHetznerFromFile(path: string): void {\n if (!existsSync(path)) return;\n let body: string;\n try {\n body = readFileSync(path, 'utf8');\n } catch {\n return;\n }\n const parsed = parseEnvFile(body);\n for (const key of HETZNER_KEYS) {\n if (process.env[key] !== undefined) continue;\n const value = parsed[key];\n if (typeof value === 'string') {\n process.env[key] = value;\n }\n }\n}\n\n/**\n * Minimal `.env` parser: handles `KEY=value`, `KEY=\"value with spaces\"`,\n * `KEY='value with $special chars'`, `export KEY=value`, blank lines, and\n * `#` comments. Same shape as the daytona env-loader's parser — kept local\n * here rather than imported across packages to avoid the cycle (daytona\n * doesn't import from hetzner and shouldn't start now).\n */\nexport function parseEnvFile(body: string): Record<string, string> {\n const out: Record<string, string> = {};\n for (const rawLine of body.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (line.length === 0 || line.startsWith('#')) continue;\n const stripped = line.startsWith('export ') ? line.slice('export '.length) : line;\n const eq = stripped.indexOf('=');\n if (eq <= 0) continue;\n const key = stripped.slice(0, eq).trim();\n let value = stripped.slice(eq + 1).trim();\n if (\n value.length >= 2 &&\n ((value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\")))\n ) {\n value = value.slice(1, -1);\n }\n out[key] = value;\n }\n return out;\n}\n","/**\n * Hetzner Cloud REST API client — hand-rolled fetch wrapper.\n *\n * Why not an SDK: the Hetzner SDK options are limited (no official JS SDK\n * with strict types at the time of writing), and the subset of the API we\n * need is small (servers, images, firewalls, plus a handful of read-only\n * lookups). A hand-rolled client gives us strict typing of just the fields\n * we touch, no heavy dep tree, and full control over the retry wrapper.\n *\n * Auth: bearer token in `HCLOUD_TOKEN` env. The env-loader pulls it from\n * `~/.agentbox/secrets.env` so the user only sets it once via\n * `agentbox hetzner login`.\n *\n * Errors: REST responses get unwrapped into typed `HetznerApiError`s that\n * carry the response `status` + the API's `error.code` / `error.message`.\n * Network failures bubble up as raw `Error`s with a `code` property\n * (ECONNRESET, ETIMEDOUT, …) — the retry wrapper classifies both shapes.\n */\n\nimport { ensureHetznerEnvLoaded } from './env-loader.js';\n\nexport const DEFAULT_HCLOUD_ENDPOINT = 'https://api.hetzner.cloud/v1';\n\n/**\n * Coarse Hetzner Cloud Server lifecycle states we care about. Hetzner has a\n * dozen finer-grained ones (`initializing`, `migrating`, `rebuilding`, …);\n * we map them in `backend.ts` to the four-value `CloudState` everyone else\n * consumes. Listed here so the client return types stay narrow.\n */\nexport type HetznerServerStatus =\n | 'running'\n | 'initializing'\n | 'starting'\n | 'stopping'\n | 'off'\n | 'deleting'\n | 'migrating'\n | 'rebuilding'\n | 'unknown';\n\nexport interface HetznerServer {\n id: number;\n name: string;\n status: HetznerServerStatus;\n created: string;\n public_net: {\n ipv4: { ip: string; blocked: boolean } | null;\n ipv6: { ip: string; blocked: boolean } | null;\n };\n server_type: { name: string; cores: number; memory: number; disk: number };\n image: { id: number; name?: string; description?: string; type: string } | null;\n labels: Record<string, string>;\n}\n\nexport interface HetznerAction {\n id: number;\n command: string;\n status: 'running' | 'success' | 'error';\n progress: number;\n error?: { code: string; message: string };\n}\n\nexport interface HetznerImage {\n id: number;\n type: 'system' | 'snapshot' | 'backup' | 'app';\n status: 'available' | 'creating' | 'unavailable';\n name?: string;\n description: string;\n image_size?: number;\n disk_size: number;\n created: string;\n labels: Record<string, string>;\n bound_to?: number;\n}\n\nexport interface HetznerFirewall {\n id: number;\n name: string;\n rules: HetznerFirewallRule[];\n applied_to: Array<{ type: 'server'; server: { id: number } }>;\n}\n\nexport interface HetznerFirewallRule {\n direction: 'in' | 'out';\n protocol: 'tcp' | 'udp' | 'icmp' | 'esp' | 'gre';\n port?: string;\n source_ips?: string[];\n destination_ips?: string[];\n description?: string;\n}\n\nexport interface HetznerSshKey {\n id: number;\n name: string;\n fingerprint: string;\n public_key: string;\n labels: Record<string, string>;\n}\n\nexport interface CreateServerRequest {\n name: string;\n server_type: string;\n image: string | number;\n location?: string;\n datacenter?: string;\n user_data?: string;\n ssh_keys?: Array<string | number>;\n firewalls?: Array<{ firewall: number }>;\n labels?: Record<string, string>;\n start_after_create?: boolean;\n public_net?: {\n enable_ipv4?: boolean;\n enable_ipv6?: boolean;\n };\n}\n\nexport interface CreateFirewallRequest {\n name: string;\n rules: HetznerFirewallRule[];\n labels?: Record<string, string>;\n apply_to?: Array<{ type: 'server'; server: { id: number } }>;\n}\n\n/**\n * Strongly-typed Hetzner API error. The Hetzner API consistently returns\n * `{ error: { code, message, details? } }` for 4xx/5xx (https://docs.hetzner.cloud/#errors).\n * We unwrap that into this class so callers can do `instanceof\n * HetznerApiError` and inspect `.code` / `.statusCode` without parsing the\n * body again.\n */\nexport class HetznerApiError extends Error {\n readonly statusCode: number;\n readonly code: string;\n readonly details?: unknown;\n constructor(statusCode: number, code: string, message: string, details?: unknown) {\n super(`hetzner ${String(statusCode)} ${code}: ${message}`);\n this.name = 'HetznerApiError';\n this.statusCode = statusCode;\n this.code = code;\n this.details = details;\n }\n}\n\n/**\n * Subset of the Hetzner Cloud API the agentbox provider talks to. Methods\n * map 1:1 to REST endpoints; each operation is small + idempotent-where-the-\n * API-is-idempotent. The retry wrapper around the provider methods handles\n * transient 5xx / connection failures.\n */\nexport interface HetznerClient {\n /** GET /servers/{id}. Returns null on 404 so callers don't have to try/catch. */\n getServer(id: number): Promise<HetznerServer | null>;\n /** POST /servers. Returns the created server + the create action handle. */\n createServer(req: CreateServerRequest): Promise<{ server: HetznerServer; action: HetznerAction }>;\n /** GET /servers (with optional label selector). */\n listServers(opts?: { label_selector?: string }): Promise<HetznerServer[]>;\n /** DELETE /servers/{id}. Returns the action handle. Idempotent on 404. */\n deleteServer(id: number): Promise<HetznerAction | null>;\n /** POST /servers/{id}/actions/poweron. */\n powerOn(id: number): Promise<HetznerAction>;\n /** POST /servers/{id}/actions/poweroff. */\n powerOff(id: number): Promise<HetznerAction>;\n /** POST /servers/{id}/actions/shutdown — graceful, sends ACPI. */\n shutdown(id: number): Promise<HetznerAction>;\n /** POST /servers/{id}/actions/create_image — snapshot of the live disk. */\n createImage(\n id: number,\n body: { type: 'snapshot' | 'backup'; description?: string; labels?: Record<string, string> },\n ): Promise<{ image: HetznerImage; action: HetznerAction }>;\n /** GET /images/{id}. Returns null on 404. */\n getImage(id: number): Promise<HetznerImage | null>;\n /** GET /images (filterable). */\n listImages(opts?: {\n type?: 'system' | 'snapshot' | 'backup' | 'app';\n label_selector?: string;\n name?: string;\n }): Promise<HetznerImage[]>;\n /** DELETE /images/{id}. Idempotent on 404. */\n deleteImage(id: number): Promise<void>;\n /** POST /firewalls. */\n createFirewall(req: CreateFirewallRequest): Promise<HetznerFirewall>;\n /** POST /firewalls/{id}/actions/set_rules. Replaces the entire rule set. */\n setFirewallRules(id: number, rules: HetznerFirewallRule[]): Promise<HetznerAction[]>;\n /** GET /firewalls/{id}. Returns null on 404. */\n getFirewall(id: number): Promise<HetznerFirewall | null>;\n /** DELETE /firewalls/{id}. Idempotent on 404. */\n deleteFirewall(id: number): Promise<void>;\n /**\n * GET /locations — used by `agentbox hetzner login` to validate the token\n * with a cheap unauthenticated-shape call (the endpoint requires a valid\n * token but returns a small, stable response).\n */\n listLocations(): Promise<Array<{ id: number; name: string; city: string; country: string }>>;\n}\n\ninterface MakeClientOptions {\n /** Override the bearer token (else read from `HCLOUD_TOKEN`). */\n token?: string;\n /** Override the API base URL (else read from `HCLOUD_ENDPOINT` or use the default). */\n endpoint?: string;\n /** Per-request fetch impl (tests inject this). */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * Build a Hetzner Cloud client bound to the current `HCLOUD_TOKEN`. The token\n * is resolved at construction time, so re-running `agentbox hetzner login` in\n * the middle of a long-lived process won't pick up the new token without a\n * fresh `makeHetznerClient()` call (we accept this — the CLI re-imports the\n * provider on each invocation).\n */\nexport function makeHetznerClient(opts: MakeClientOptions = {}): HetznerClient {\n ensureHetznerEnvLoaded();\n const rawToken = opts.token ?? process.env.HCLOUD_TOKEN;\n if (!rawToken || rawToken.trim().length === 0) {\n throw new Error(\n 'Hetzner credentials not configured: HCLOUD_TOKEN is empty.\\n' +\n 'Run `agentbox hetzner login` interactively, or set HCLOUD_TOKEN in the environment.',\n );\n }\n // Bind to a const so the type narrows for the closures below — without\n // this the `req()` closure sees the original `string | undefined` shape.\n const token: string = rawToken.trim();\n const endpoint = (opts.endpoint ?? process.env.HCLOUD_ENDPOINT ?? DEFAULT_HCLOUD_ENDPOINT).replace(/\\/$/, '');\n const fetchImpl = opts.fetchImpl ?? fetch;\n\n async function req<T>(\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n body?: unknown,\n ): Promise<T | null> {\n const url = `${endpoint}${path}`;\n const init: RequestInit = {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n };\n const res = await fetchImpl(url, init);\n if (res.status === 204) return null;\n if (res.status === 404) return null;\n if (!res.ok) {\n let parsed: { error?: { code?: string; message?: string; details?: unknown } } = {};\n try {\n parsed = (await res.json()) as typeof parsed;\n } catch {\n // body wasn't json\n }\n const code = parsed.error?.code ?? `http_${String(res.status)}`;\n const msg = parsed.error?.message ?? res.statusText ?? 'unknown error';\n throw new HetznerApiError(res.status, code, msg, parsed.error?.details);\n }\n const text = await res.text();\n if (text.length === 0) return null;\n return JSON.parse(text) as T;\n }\n\n async function reqExpect<T>(\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n body?: unknown,\n ): Promise<T> {\n const out = await req<T>(method, path, body);\n if (out === null) {\n throw new HetznerApiError(0, 'empty_response', `expected a body from ${method} ${path}`);\n }\n return out;\n }\n\n return {\n async getServer(id) {\n const r = await req<{ server: HetznerServer }>('GET', `/servers/${String(id)}`);\n return r?.server ?? null;\n },\n async createServer(reqBody) {\n const r = await reqExpect<{ server: HetznerServer; action: HetznerAction }>(\n 'POST',\n '/servers',\n reqBody,\n );\n return { server: r.server, action: r.action };\n },\n async listServers(opts) {\n const params = new URLSearchParams();\n if (opts?.label_selector) params.set('label_selector', opts.label_selector);\n params.set('per_page', '50');\n const all: HetznerServer[] = [];\n let pageNum = 1;\n while (true) {\n params.set('page', String(pageNum));\n const r = await reqExpect<{\n servers: HetznerServer[];\n meta?: { pagination?: { next_page?: number | null } };\n }>('GET', `/servers?${params.toString()}`);\n all.push(...r.servers);\n const next = r.meta?.pagination?.next_page;\n if (typeof next !== 'number') break;\n pageNum = next;\n }\n return all;\n },\n async deleteServer(id) {\n const r = await req<{ action: HetznerAction }>('DELETE', `/servers/${String(id)}`);\n return r?.action ?? null;\n },\n async powerOn(id) {\n const r = await reqExpect<{ action: HetznerAction }>(\n 'POST',\n `/servers/${String(id)}/actions/poweron`,\n );\n return r.action;\n },\n async powerOff(id) {\n const r = await reqExpect<{ action: HetznerAction }>(\n 'POST',\n `/servers/${String(id)}/actions/poweroff`,\n );\n return r.action;\n },\n async shutdown(id) {\n const r = await reqExpect<{ action: HetznerAction }>(\n 'POST',\n `/servers/${String(id)}/actions/shutdown`,\n );\n return r.action;\n },\n async createImage(id, body) {\n const r = await reqExpect<{ image: HetznerImage; action: HetznerAction }>(\n 'POST',\n `/servers/${String(id)}/actions/create_image`,\n body,\n );\n return { image: r.image, action: r.action };\n },\n async getImage(id) {\n const r = await req<{ image: HetznerImage }>('GET', `/images/${String(id)}`);\n return r?.image ?? null;\n },\n async listImages(opts) {\n const params = new URLSearchParams();\n if (opts?.type) params.set('type', opts.type);\n if (opts?.label_selector) params.set('label_selector', opts.label_selector);\n if (opts?.name) params.set('name', opts.name);\n params.set('per_page', '50');\n const all: HetznerImage[] = [];\n let pageNum = 1;\n while (true) {\n params.set('page', String(pageNum));\n const r = await reqExpect<{\n images: HetznerImage[];\n meta?: { pagination?: { next_page?: number | null } };\n }>('GET', `/images?${params.toString()}`);\n all.push(...r.images);\n const next = r.meta?.pagination?.next_page;\n if (typeof next !== 'number') break;\n pageNum = next;\n }\n return all;\n },\n async deleteImage(id) {\n await req<unknown>('DELETE', `/images/${String(id)}`);\n },\n async createFirewall(reqBody) {\n const r = await reqExpect<{ firewall: HetznerFirewall }>('POST', '/firewalls', reqBody);\n return r.firewall;\n },\n async setFirewallRules(id, rules) {\n const r = await reqExpect<{ actions: HetznerAction[] }>(\n 'POST',\n `/firewalls/${String(id)}/actions/set_rules`,\n { rules },\n );\n return r.actions;\n },\n async getFirewall(id) {\n const r = await req<{ firewall: HetznerFirewall }>('GET', `/firewalls/${String(id)}`);\n return r?.firewall ?? null;\n },\n async deleteFirewall(id) {\n await req<unknown>('DELETE', `/firewalls/${String(id)}`);\n },\n async listLocations() {\n const r = await reqExpect<{\n locations: Array<{ id: number; name: string; city: string; country: string }>;\n }>('GET', '/locations');\n return r.locations;\n },\n };\n}\n","import { spawnSync } from 'node:child_process';\nimport {\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n writeFileSync,\n} from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, resolve } from 'node:path';\nimport { confirm, isCancel, intro, log, note, outro, password, spinner } from '@clack/prompts';\nimport { makeHetznerClient } from './client.js';\nimport { ensureHetznerEnvLoaded } from './env-loader.js';\n\nconst DASHBOARD_KEYS_URL = 'https://console.hetzner.cloud/projects';\n\n/**\n * Keys we manage in `~/.agentbox/secrets.env`. When the user reconfigures\n * we strip prior values before appending so the file never accumulates\n * duplicates. `HCLOUD_ENDPOINT` is honored but we don't prompt for it\n * (default endpoint covers 100% of users).\n */\nconst MANAGED_KEYS = ['HCLOUD_TOKEN', 'HCLOUD_ENDPOINT'] as const;\ntype ManagedKey = (typeof MANAGED_KEYS)[number];\n\nexport interface EnsureHetznerCredentialsOptions {\n /** Re-prompt even when valid credentials are already present (used by `agentbox hetzner login`). */\n force?: boolean;\n}\n\n/**\n * First-run interactive setup for Hetzner credentials. Walks the user\n * through creating a project API token, pasting it, validating, and\n * persisting to `~/.agentbox/secrets.env`.\n *\n * No-op when credentials are already configured (env var or our secrets\n * file). Silent no-op when stdin isn't a TTY so scripted/CI callers get\n * the API \"401 unauthorized\" error instead of a hung prompt.\n *\n * Mirrors `ensureDaytonaCredentials()` in shape so the registry's first-\n * run gate stays uniform across providers.\n */\nexport async function ensureHetznerCredentials(\n opts: EnsureHetznerCredentialsOptions = {},\n): Promise<void> {\n ensureHetznerEnvLoaded();\n\n if (!opts.force && hasUsableCredentials()) return;\n if (!process.stdin.isTTY) return;\n\n intro('Hetzner Cloud setup');\n note(\n `AgentBox needs a Hetzner Cloud API token (project-scoped) to provision VPSes.\\n\\n` +\n `1. Open ${DASHBOARD_KEYS_URL}\\n` +\n `2. Pick a project (or create one).\\n` +\n `3. Security → API Tokens → Generate API Token (Read + Write).`,\n 'API token required',\n );\n\n const open = await confirm({\n message: `Open ${DASHBOARD_KEYS_URL} in your browser?`,\n initialValue: true,\n });\n if (isCancel(open)) {\n log.warn('Hetzner setup cancelled — re-run `agentbox hetzner login` when ready.');\n return;\n }\n if (open) openDashboard();\n\n // One retry on auth failure (typos / expired token are the common case).\n for (let attempt = 0; attempt < 2; attempt++) {\n const creds = await promptForCredentials();\n if (creds === null) return;\n\n const result = await validateCredentials(creds);\n if (result.ok) {\n persistCredentials(creds);\n log.success(`Hetzner credentials saved to ${secretsPath()}`);\n outro('Setup complete.');\n return;\n }\n if (result.kind === 'auth' && attempt === 0) {\n log.error(`That token was rejected by Hetzner: ${result.message}`);\n log.info('Try again, or press Ctrl-C to cancel.');\n continue;\n }\n if (result.kind === 'network') {\n log.warn(`Could not reach Hetzner to validate (${result.message}) — saving anyway.`);\n persistCredentials(creds);\n log.success(`Hetzner credentials saved to ${secretsPath()}`);\n outro('Setup complete (unvalidated).');\n return;\n }\n throw new Error(`Hetzner credentials rejected: ${result.message}`);\n }\n}\n\nfunction hasUsableCredentials(): boolean {\n return typeof process.env.HCLOUD_TOKEN === 'string' && process.env.HCLOUD_TOKEN.length > 0;\n}\n\ninterface Credentials {\n token: string;\n endpoint?: string;\n}\n\nasync function promptForCredentials(): Promise<Credentials | null> {\n const token = await password({\n message: 'Paste your Hetzner Cloud API token',\n validate(v) {\n if (!v || v.trim().length === 0) return 'Cannot be empty';\n return undefined;\n },\n });\n if (isCancel(token)) {\n log.warn('Hetzner setup cancelled.');\n return null;\n }\n return { token: token.trim() };\n}\n\ntype ValidationResult =\n | { ok: true }\n | { ok: false; kind: 'auth'; message: string }\n | { ok: false; kind: 'network'; message: string };\n\nasync function validateCredentials(creds: Credentials): Promise<ValidationResult> {\n const s = spinner();\n s.start('Validating credentials with Hetzner');\n\n try {\n const client = makeHetznerClient({ token: creds.token, endpoint: creds.endpoint });\n // `listLocations()` is a cheap, deterministic call that exercises auth +\n // basic API reachability without provisioning anything.\n await client.listLocations();\n s.stop('Hetzner credentials accepted');\n return { ok: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n s.stop('Hetzner credentials check failed');\n if (/401|403|unauthor|forbidden|invalid|token/i.test(message)) {\n return { ok: false, kind: 'auth', message };\n }\n return { ok: false, kind: 'network', message };\n }\n}\n\nfunction persistCredentials(creds: Credentials): void {\n process.env.HCLOUD_TOKEN = creds.token;\n if (creds.endpoint) process.env.HCLOUD_ENDPOINT = creds.endpoint;\n const path = secretsPath();\n mkdirSync(dirname(path), { recursive: true });\n\n let existing = '';\n if (existsSync(path)) {\n try {\n existing = readFileSync(path, 'utf8');\n } catch {\n existing = '';\n }\n }\n\n const kept = existing\n .split(/\\r?\\n/)\n .filter((line) => {\n const stripped = line.startsWith('export ') ? line.slice('export '.length) : line;\n const eq = stripped.indexOf('=');\n if (eq <= 0) return true;\n const key = stripped.slice(0, eq).trim();\n return !(MANAGED_KEYS as readonly string[]).includes(key);\n })\n .join('\\n')\n .replace(/\\s+$/u, '');\n\n const lines: string[] = [`HCLOUD_TOKEN=${creds.token}`];\n if (creds.endpoint) lines.push(`HCLOUD_ENDPOINT=${creds.endpoint}`);\n\n const body = (kept ? `${kept}\\n` : '') + lines.join('\\n') + '\\n';\n\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, body, { mode: 0o600 });\n try {\n chmodSync(tmp, 0o600);\n } catch {\n // chmod best-effort; writeFileSync mode already covers most filesystems.\n }\n renameSync(tmp, path);\n try {\n chmodSync(path, 0o600);\n } catch {\n // ignore — already attempted above.\n }\n}\n\nfunction openDashboard(): void {\n try {\n const r = spawnSync('open', [DASHBOARD_KEYS_URL], { stdio: 'ignore' });\n if (r.status !== 0) {\n log.warn(`Could not auto-open the browser — visit ${DASHBOARD_KEYS_URL} manually.`);\n }\n } catch {\n log.warn(`Could not auto-open the browser — visit ${DASHBOARD_KEYS_URL} manually.`);\n }\n}\n\nexport function secretsPath(): string {\n return resolve(homedir(), '.agentbox', 'secrets.env');\n}\n\nexport interface HetznerCredStatus {\n token?: string;\n endpoint?: string;\n source: 'env' | 'secrets.env' | 'none';\n}\n\nexport function readHetznerCredStatus(): HetznerCredStatus {\n const shellHadToken = !!process.env.HCLOUD_TOKEN;\n ensureHetznerEnvLoaded();\n const token = process.env.HCLOUD_TOKEN;\n const endpoint = process.env.HCLOUD_ENDPOINT;\n if (!token) return { source: 'none' };\n return {\n token,\n endpoint,\n source: shellHadToken ? 'env' : 'secrets.env',\n };\n}\n\nexport function maskKey(value: string): string {\n if (value.length <= 8) return '*'.repeat(value.length);\n return `${value.slice(0, 4)}…${'*'.repeat(8)}${value.slice(-4)}`;\n}\n\n/** Snapshot of the managed env keys (used by tests around `applyToEnv`). */\nexport function snapshotManagedEnv(): Record<ManagedKey, string | undefined> {\n const out = {} as Record<ManagedKey, string | undefined>;\n for (const k of MANAGED_KEYS) out[k] = process.env[k];\n return out;\n}\n\nexport function restoreManagedEnv(snap: Record<ManagedKey, string | undefined>): void {\n for (const k of MANAGED_KEYS) {\n if (snap[k] === undefined) delete process.env[k];\n else process.env[k] = snap[k];\n }\n}\n","/**\n * Host egress-IP detection for the Hetzner firewall lock-down. Probes three\n * independent providers in sequence; first 3s success wins. Fails loud\n * (throws) if all three fail — we do **not** silently fall back to\n * `0.0.0.0/0`, because that would defeat the safe-by-default firewall.\n *\n * The user can always override the auto-detect via\n * `--firewall-source <cidr>` (or `--firewall-source 0.0.0.0/0` for the\n * explicit dynamic-IP opt-in).\n */\n\nconst PROBES = [\n 'https://api.ipify.org',\n 'https://ifconfig.io/ip',\n 'https://icanhazip.com',\n] as const;\n\nconst TIMEOUT_MS = 3_000;\n\nconst IPV4_RE = /^(?:\\d{1,3}\\.){3}\\d{1,3}$/;\nconst IPV6_RE = /^[0-9a-fA-F:]+$/;\n\nexport interface DetectEgressIpOptions {\n /** Override the probe list (tests inject this). */\n probes?: readonly string[];\n /** Per-probe timeout in ms (default 3_000). */\n timeoutMs?: number;\n /** Override `fetch` (tests inject this). */\n fetchImpl?: typeof fetch;\n /** Best-effort logger for probe attempts. */\n onLog?: (line: string) => void;\n}\n\n/**\n * Detect the host's egress IP. Returns the bare IP string (no `/32`); the\n * caller composes the CIDR.\n *\n * Throws when no probe responded. The error message lists each probe that\n * was tried so the user can see whether their network is blocking a\n * specific provider.\n */\nexport async function detectEgressIp(opts: DetectEgressIpOptions = {}): Promise<string> {\n const probes = opts.probes ?? PROBES;\n const timeout = opts.timeoutMs ?? TIMEOUT_MS;\n const fetchImpl = opts.fetchImpl ?? fetch;\n const errors: string[] = [];\n\n for (const url of probes) {\n try {\n const ip = await raceTimeout(probe(url, fetchImpl), timeout);\n if (ip) {\n opts.onLog?.(`egress-ip: detected ${ip} via ${url}`);\n return ip;\n }\n errors.push(`${url}: empty/invalid response`);\n } catch (err) {\n errors.push(`${url}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n throw new Error(\n `could not auto-detect the host's egress IP — all ${String(probes.length)} probes failed:\\n` +\n errors.map((e) => ` - ${e}`).join('\\n') +\n `\\nOverride with --firewall-source <cidr> (e.g. --firewall-source 0.0.0.0/0 for the explicit-open opt-in).`,\n );\n}\n\nasync function probe(url: string, fetchImpl: typeof fetch): Promise<string | null> {\n const res = await fetchImpl(url, { method: 'GET' });\n if (!res.ok) return null;\n const body = (await res.text()).trim();\n if (IPV4_RE.test(body)) {\n // Cheap sanity: each octet in 0–255.\n const parts = body.split('.').map((p) => Number.parseInt(p, 10));\n if (parts.every((p) => p >= 0 && p <= 255)) return body;\n return null;\n }\n // We do not currently use IPv6 for firewall rules (Hetzner accepts them\n // but the rest of the provider talks IPv4), but accept the probe answer\n // so a v6-only network surfaces an actionable error rather than a silent\n // empty result. Composing the CIDR is the caller's job.\n if (IPV6_RE.test(body) && body.includes(':')) return body;\n return null;\n}\n\nasync function raceTimeout<T>(p: Promise<T>, ms: number): 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 Error(`probe timed out after ${String(ms)}ms`)), ms);\n }),\n ]);\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n}\n","/**\n * Bounded retry wrapper for Hetzner Cloud API calls — mirrors\n * `withDaytonaRetry` in shape and intent. Hetzner is generally well-behaved\n * but the public API does rate-limit (429) and occasionally returns 502/504\n * during regional incidents; without bounded retries those propagate as\n * wedges in the calling lifecycle code.\n *\n * Non-idempotent ops (`provision`, `createImage`) pass\n * `retryOnAmbiguous: false` so a 504 after the request reached the origin\n * doesn't create a duplicate billable resource.\n */\n\nimport { HetznerApiError } from './client.js';\n\nexport interface WithRetryOptions {\n /** Method name, used in retry log lines. */\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 * Whether to retry on errors where we can't be sure the server applied\n * the request — connection failures, per-attempt timeouts, and 5xx\n * responses. Set false for non-idempotent operations (e.g. `provision`,\n * `createImage`) where a retry could create a duplicate resource.\n */\n retryOnAmbiguous: boolean;\n /** Override the default `process.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(`hetzner ${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/**\n * Classify an error as retriable or not. `allowAmbiguous` gates the cases\n * where the server may or may not have applied the request — the caller\n * decides based on idempotency.\n */\nexport function isRetriable(err: unknown, allowAmbiguous: boolean): boolean {\n if (err instanceof HetznerApiError) {\n // Rate limit: always back off — the server told us to.\n if (err.statusCode === 429 || err.code === 'rate_limit_exceeded') return true;\n // 5xx: ambiguous (the API may or may not have applied the change).\n if (err.statusCode >= 500 && err.statusCode <= 599) return allowAmbiguous;\n // Hetzner conflict / locked errors: the API tells us to wait — same as\n // rate-limit semantically. `conflict` is what `delete_server` returns\n // when another action (e.g. our own poweroff) is still in flight.\n if (err.code === 'locked' || err.code === 'conflict') return true;\n // Everything else is a permanent client error (auth, validation, not_found).\n return false;\n }\n\n if (err instanceof AttemptTimeoutError) return allowAmbiguous;\n\n // Raw fetch / undici errors. The Node fetch impl wraps low-level errors in\n // `{ cause }`; we check both shapes for portability.\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\n return false;\n}\n\n/**\n * Run `fn`, retrying on transient failures with capped exponential backoff.\n * Each attempt is bounded by `attemptTimeoutMs` via Promise.race; total\n * wall-clock = sum(backoffMs) + maxAttempts * attemptTimeoutMs.\n */\nexport async function withHetznerRetry<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 `hetzner ${opts.method}: attempt ${String(attempt)} failed (${errorSummary(err)}); retrying in ${String(delay)}ms`,\n );\n await sleep(delay);\n }\n }\n throw new Error(`withHetznerRetry: exhausted attempts for ${opts.method}`);\n}\n\nfunction defaultRetryLog(line: string): void {\n process.stderr.write(`\\n[hetzner-retry] ${line}\\n`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, 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 HetznerApiError) {\n return `HetznerApiError ${String(err.statusCode)} ${err.code}: ${truncate(err.message)}`;\n }\n if (err instanceof Error) {\n const code = (err as { code?: unknown }).code;\n return code !== undefined\n ? `${err.name}(${String(code)}): ${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 * Hetzner Cloud Firewall provisioning + drift sync.\n *\n * Defense-in-depth model (recapped from\n * ~/.claude/plans/how-to-safely-create-parallel-pebble.md §\"The safety model\"):\n *\n * 1. In-VPS services bind to loopback (the load-bearing layer).\n * 2. Hetzner Cloud Firewall locks SSH to the host's egress IP — applied\n * here at provision time, before the VPS first boots. Everything else\n * is denied inbound; outbound is unrestricted.\n * 3. sshd hardening (PasswordAuthentication no, AllowUsers vscode, …)\n * written by cloud-init at first boot.\n *\n * Layer 2 is what this module provisions. The firewall is per-box (1:1 with\n * the VPS) so an egress-IP-drift on one box doesn't affect siblings, and a\n * destroy cleanly removes everything we created.\n */\n\nimport { HetznerApiError, type HetznerClient, type HetznerFirewall, type HetznerFirewallRule } from './client.js';\nimport { withHetznerRetry } from './retry.js';\n\n/**\n * Build the SSH-only inbound rule for a given source CIDR. Outbound is\n * left unrestricted (empty rules array = \"no inbound besides this one\").\n */\nexport function sshOnlyInboundRule(sourceCidr: string): HetznerFirewallRule[] {\n return [\n {\n direction: 'in',\n protocol: 'tcp',\n port: '22',\n source_ips: [sourceCidr],\n description: 'agentbox: SSH from host egress IP only',\n },\n ];\n}\n\nexport interface CreateFirewallOptions {\n /** Human-readable name persisted with the firewall (visible in the Hetzner dashboard). */\n name: string;\n /** Source CIDR (e.g. `1.2.3.4/32`). The caller is responsible for normalizing the suffix. */\n sourceCidr: string;\n /** Labels merged onto the firewall (we always add `agentbox.managed=true`). */\n labels?: Record<string, string>;\n}\n\n/**\n * Provision a fresh per-box firewall locked to the given source CIDR.\n * Returns the created `HetznerFirewall` so the caller can persist\n * `firewallId` on the box record.\n */\nexport async function createPerBoxFirewall(\n client: HetznerClient,\n opts: CreateFirewallOptions,\n): Promise<HetznerFirewall> {\n return withHetznerRetry(\n { method: 'createFirewall', retryOnAmbiguous: false, attemptTimeoutMs: 60_000 },\n () =>\n client.createFirewall({\n name: opts.name,\n rules: sshOnlyInboundRule(opts.sourceCidr),\n labels: {\n 'agentbox.managed': 'true',\n 'agentbox.role': 'box',\n ...opts.labels,\n },\n }),\n );\n}\n\n/**\n * Re-detect the egress IP and replace the firewall's rule set with the new\n * source. Used by `agentbox hetzner firewall sync <box>` after the host\n * laptop moves networks. Cheap operation — no VPS restart involved.\n *\n * Idempotent on the API: setting the same rules again is a no-op from the\n * user's point of view (the API still returns an action handle, but it\n * resolves instantly).\n */\nexport async function syncFirewallSource(\n client: HetznerClient,\n firewallId: number,\n sourceCidr: string,\n): Promise<void> {\n await withHetznerRetry(\n { method: 'setFirewallRules', retryOnAmbiguous: true, attemptTimeoutMs: 60_000 },\n () => client.setFirewallRules(firewallId, sshOnlyInboundRule(sourceCidr)),\n );\n}\n\n/**\n * Delete a per-box firewall. Idempotent on 404 (the API surfaces it as a\n * `not_found` error which the retry classifier won't retry; we swallow it\n * here so destroy paths don't need a special-case).\n *\n * Hetzner returns 409 `conflict` if the firewall is still attached to a\n * server when we try to delete it — `deleteServer()` returns as soon as the\n * delete action is *enqueued*, not after the server's firewall attachment\n * is torn down, so a quick subsequent `deleteFirewall()` will collide.\n * We poll for a short window (default 60s, intervals doubled to 8s) to\n * cover the typical 5–15s detach lag before giving up.\n */\nexport async function deletePerBoxFirewall(\n client: HetznerClient,\n firewallId: number,\n opts: { detachWaitMs?: number } = {},\n): Promise<void> {\n const deadline = Date.now() + (opts.detachWaitMs ?? 60_000);\n let interval = 1_000;\n while (true) {\n try {\n await withHetznerRetry(\n { method: 'deleteFirewall', retryOnAmbiguous: true, attemptTimeoutMs: 30_000 },\n () => client.deleteFirewall(firewallId),\n );\n return;\n } catch (err) {\n if (err instanceof HetznerApiError && (err.statusCode === 404 || err.code === 'not_found')) {\n return;\n }\n const stillAttached =\n err instanceof HetznerApiError &&\n (err.statusCode === 409 ||\n err.code === 'conflict' ||\n err.code === 'resource_in_use');\n if (stillAttached && Date.now() < deadline) {\n await new Promise((r) => setTimeout(r, interval));\n interval = Math.min(interval * 2, 8_000);\n continue;\n }\n throw err;\n }\n }\n}\n\n/**\n * Normalize a source spec into a CIDR. Accepts:\n * - bare IPv4 → appends `/32`\n * - bare IPv6 → appends `/128`\n * - already-CIDR (anything with `/`) → returned as-is\n *\n * Whitespace is trimmed. Does **not** validate the address itself — that's\n * either the API's job (it'll reject bad CIDRs with a clear `validation`\n * error) or `detectEgressIp`'s job (it only returns valid IPv4/IPv6).\n */\nexport function normalizeSourceCidr(raw: string): string {\n const trimmed = raw.trim();\n if (trimmed.includes('/')) return trimmed;\n if (trimmed.includes(':')) return `${trimmed}/128`;\n return `${trimmed}/32`;\n}\n"],"mappings":"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","names":["existsSync","readFileSync","homedir","resolve","opts","log","raceTimeout"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../packages/sandbox-core/src/state.ts","../../../packages/sandbox-core/src/git-detect.ts","../../../packages/sandbox-core/src/prepared-state.ts","../../../packages/sandbox-docker/src/prepared-state.ts","../../../packages/sandbox-docker/src/image.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport type { BoxRecord, DockerBoxFields, FindBoxResult, StateFile } from '@agentbox/core';\n\nexport const STATE_DIR = join(homedir(), '.agentbox');\nexport const STATE_FILE = join(STATE_DIR, 'state.json');\n\nconst EMPTY: StateFile = { version: 1, boxes: [] };\n\nexport async function readState(path: string = STATE_FILE): Promise<StateFile> {\n try {\n const raw = await readFile(path, 'utf8');\n const parsed = JSON.parse(raw) as StateFile;\n if (parsed.version !== 1 || !Array.isArray(parsed.boxes)) {\n throw new Error(`unrecognized state file shape at ${path}`);\n }\n // Migrate-on-read: records written before the multi-provider split carry no\n // `provider` field — they are all Docker boxes. Default it so every\n // consumer (provider registry, `findBox`) sees a discriminated record.\n // Also backfill `box.docker` from the flat fields for Docker records so\n // forward-looking readers (7.1) see the nested shape without waiting\n // for the box to be re-recorded.\n for (const b of parsed.boxes) {\n b.provider ??= 'docker';\n if ((b.provider ?? 'docker') === 'docker' && !b.docker) {\n b.docker = projectDockerFields(b);\n }\n }\n return parsed;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return { ...EMPTY };\n }\n throw err;\n }\n}\n\nexport async function writeState(state: StateFile, path: string = STATE_FILE): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, JSON.stringify(state, null, 2) + '\\n', 'utf8');\n}\n\nexport async function recordBox(box: BoxRecord, path: string = STATE_FILE): Promise<void> {\n // Forward-looking shape: every Docker write also mirrors the flat\n // docker-specific fields into `box.docker` so readers can move to the\n // nested form opportunistically (7.1). Cloud records skip the mirror —\n // the discriminator is `box.provider !== 'docker'`.\n const toWrite: BoxRecord =\n (box.provider ?? 'docker') === 'docker' && !box.docker\n ? { ...box, docker: projectDockerFields(box) }\n : box;\n const state = await readState(path);\n const next: StateFile = {\n version: 1,\n boxes: [...state.boxes.filter((b) => b.id !== toWrite.id), toWrite],\n };\n await writeState(next, path);\n}\n\n/**\n * Build a `DockerBoxFields` payload from the flat Docker-specific fields\n * still living on `BoxRecord` for back-compat. Pure function, no\n * filesystem; safe for both `readState` migration and `recordBox` mirror.\n *\n * Once every reader uses `box.docker?.<field>` (the rest of 7.1), the\n * flat fields can be dropped and this projection becomes the canonical\n * shape. Until then, every write produces both shapes from the same\n * source so they can't drift.\n */\nfunction projectDockerFields(box: BoxRecord): DockerBoxFields {\n return {\n container: box.container,\n image: box.image,\n snapshotDir: box.snapshotDir ?? null,\n socketPath: box.socketPath,\n claudeConfigVolume: box.claudeConfigVolume,\n codexConfigVolume: box.codexConfigVolume,\n opencodeConfigVolume: box.opencodeConfigVolume,\n vscodeServerVolume: box.vscodeServerVolume,\n cursorServerVolume: box.cursorServerVolume,\n vncHostPort: box.vncHostPort,\n webHostPort: box.webHostPort,\n portlessAlias: box.portlessAlias,\n portlessUrl: box.portlessUrl,\n portlessVncAlias: box.portlessVncAlias,\n portlessVncUrl: box.portlessVncUrl,\n dockerVolume: box.dockerVolume,\n dockerCacheShared: box.dockerCacheShared,\n checkpointImage: box.checkpointImage,\n };\n}\n\nexport async function removeBoxRecord(id: string, path: string = STATE_FILE): Promise<boolean> {\n const state = await readState(path);\n const before = state.boxes.length;\n const next: StateFile = {\n version: 1,\n boxes: state.boxes.filter((b) => b.id !== id),\n };\n if (next.boxes.length === before) return false;\n await writeState(next, path);\n return true;\n}\n\n/**\n * Resolve a user-supplied identifier against the state file. Matching\n * precedence mirrors `docker`'s container reference resolution:\n *\n * 1. exact id\n * 2. unique id prefix\n * 3. exact name\n * 4. exact container name\n *\n * Returns `'ambiguous'` if step 2 finds more than one match (steps 1, 3, 4\n * are exact-match so they cannot be ambiguous on their own).\n */\nexport function findBox(idOrName: string, state: StateFile): FindBoxResult {\n const q = idOrName.trim();\n if (q.length === 0) return { kind: 'none' };\n\n const exactId = state.boxes.find((b) => b.id === q);\n if (exactId) return { kind: 'ok', box: exactId };\n\n const prefixMatches = state.boxes.filter((b) => b.id.startsWith(q));\n if (prefixMatches.length === 1) return { kind: 'ok', box: prefixMatches[0]! };\n if (prefixMatches.length > 1) return { kind: 'ambiguous', matches: prefixMatches };\n\n const byName = state.boxes.find((b) => b.name === q);\n if (byName) return { kind: 'ok', box: byName };\n\n // For docker records `container` is the docker container name; for cloud\n // records it's `cloud:<sandboxId>` (post 7.2 — no more synthetic\n // agentbox-cloud-* prefix). Either form is a valid byContainer lookup\n // key for `findBox`.\n const byContainer = state.boxes.find((b) => b.container === q);\n if (byContainer) return { kind: 'ok', box: byContainer };\n\n return { kind: 'none' };\n}\n\n/**\n * Next monotonic 1-based index for the given project. Reads only `state.boxes`\n * — caller is responsible for persisting the assignment. Boxes without\n * `projectRoot` are ignored (legacy records); boxes in *other* projects are\n * also ignored. Indices are never recycled, so a destroyed #2 leaves a gap.\n */\nexport function allocateProjectIndex(state: StateFile, projectRoot: string): number {\n let max = 0;\n for (const b of state.boxes) {\n if (b.projectRoot !== projectRoot) continue;\n if (typeof b.projectIndex === 'number' && b.projectIndex > max) {\n max = b.projectIndex;\n }\n }\n return max + 1;\n}\n\n/**\n * Auto-pick when a command's `[box]` argument is omitted. Returns the unique\n * box for `projectRoot`, an `ambiguous` carrying all candidates so the CLI can\n * print a chooser, or `none`.\n */\nexport function autoPickProjectBox(state: StateFile, projectRoot: string): FindBoxResult {\n const matches = state.boxes.filter((b) => b.projectRoot === projectRoot);\n if (matches.length === 0) return { kind: 'none' };\n if (matches.length === 1) return { kind: 'ok', box: matches[0]! };\n return { kind: 'ambiguous', matches };\n}\n\n/**\n * Top-level resolver every CLI command goes through. Combines numeric-index\n * lookup with the legacy `findBox` matcher:\n *\n * - `ref === undefined` and `projectRoot` known → autoPickProjectBox.\n * - `ref` is a pure positive integer and `projectRoot` known → resolve as\n * project index. **Never** falls through to `findBox` on miss, so\n * `agentbox open 3` is reserved for the index and won't accidentally\n * match a hex id like `3abc…`.\n * - Otherwise → `findBox` (id → prefix → name → container).\n */\nexport function resolveBoxRef(\n ref: string | undefined,\n state: StateFile,\n projectRoot: string | undefined,\n): FindBoxResult {\n if (ref === undefined) {\n if (projectRoot === undefined) return { kind: 'none' };\n return autoPickProjectBox(state, projectRoot);\n }\n const trimmed = ref.trim();\n if (projectRoot !== undefined && /^[1-9][0-9]*$/.test(trimmed)) {\n const idx = Number.parseInt(trimmed, 10);\n const hit = state.boxes.find(\n (b) => b.projectRoot === projectRoot && b.projectIndex === idx,\n );\n return hit ? { kind: 'ok', box: hit } : { kind: 'none' };\n }\n return findBox(trimmed, state);\n}\n","import { execa } from 'execa';\nimport { readdir, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface DetectedGitRepo {\n kind: 'root' | 'nested';\n /** Absolute host path of the repo working tree (== `<workspace>` for root). */\n hostMainRepo: string;\n /** Path relative to the workspace where the repo lives. Empty string for root. */\n relPathFromWorkspace: string;\n}\n\n/**\n * Look for `.git` directories at the workspace root and at every 1st-level\n * subdirectory. Worktree-form `.git` files (regular file containing\n * `gitdir: …`) are intentionally skipped — turning an existing worktree into\n * another worktree gets weird, and the user case for it is rare.\n *\n * Pure host-side detection: it only tells callers where the repos are. Docker\n * boxes create the worktree inside the container against the bind-mounted\n * `.git/`; cloud boxes clone from a bundle. Either way this is the host probe.\n */\nexport async function detectGitRepos(workspace: string): Promise<DetectedGitRepo[]> {\n const out: DetectedGitRepo[] = [];\n if (await isGitDir(join(workspace, '.git'))) {\n out.push({ kind: 'root', hostMainRepo: workspace, relPathFromWorkspace: '' });\n }\n let entries: Array<{ name: string; isDirectory: () => boolean }>;\n try {\n entries = await readdir(workspace, { withFileTypes: true });\n } catch {\n return out;\n }\n for (const e of entries) {\n if (!e.isDirectory() || e.name.startsWith('.')) continue;\n const sub = join(workspace, e.name);\n if (await isGitDir(join(sub, '.git'))) {\n out.push({ kind: 'nested', hostMainRepo: sub, relPathFromWorkspace: e.name });\n }\n }\n return out;\n}\n\nasync function isGitDir(path: string): Promise<boolean> {\n try {\n const s = await stat(path);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Pick `<base>`, `<base>-2`, `<base>-3`, … until git reports no such branch\n * exists. Avoids collision when the user reruns `agentbox create -n same-name`\n * after destroying — the destroyed box's branch still lives in the host repo.\n */\nexport async function pickFreshBranch(hostMainRepo: string, base: string): Promise<string> {\n let candidate = base;\n let suffix = 2;\n while (await branchExists(hostMainRepo, candidate)) {\n candidate = `${base}-${String(suffix++)}`;\n if (suffix > 100) throw new GitWorktreeError(`could not find a free branch name near ${base}`);\n }\n return candidate;\n}\n\nasync function branchExists(hostMainRepo: string, name: string): Promise<boolean> {\n const result = await execa(\n 'git',\n ['-C', hostMainRepo, 'show-ref', '--verify', '--quiet', `refs/heads/${name}`],\n { reject: false },\n );\n return result.exitCode === 0;\n}\n\nexport class GitWorktreeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'GitWorktreeError';\n }\n}\n","/**\n * Cross-provider versioning primitives for `~/.agentbox/<provider>-prepared.json`.\n *\n * Each provider records what it has baked (docker image / hetzner snapshot /\n * daytona snapshot) under a per-provider JSON file with a shared `base.*`\n * substructure so the CLI can detect when the on-disk artifact is stale\n * relative to the current CLI's build context.\n *\n * The invalidation key is `base.contextSha256`: a deterministic SHA-256\n * over every file in the build context (Dockerfile + scripts + baked\n * config), keyed by the file's relative path. Two CLIs with the same\n * staged runtime tree produce the same hash; an edit to any baked asset\n * — even a one-byte tweak to `custom-system-CLAUDE.md` — flips it.\n *\n * Checkpoints embed the captured `contextSha256` so restoring an older\n * checkpoint can warn the user that the baked layers predate the current\n * base image.\n */\n\nimport { createHash } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, resolve as pathResolve } from 'node:path';\n\nexport type PreparedProviderKind = 'docker' | 'daytona' | 'hetzner' | 'vercel';\n\n/**\n * The cross-provider record. `TImage` is the provider's opaque image\n * identifier: a string tag for docker/daytona, a numeric image id for\n * hetzner. The `TExtra` slot lets a provider attach provider-specific\n * fields (e.g. hetzner's `description` and `projects[]`) without forking\n * the whole shape.\n */\nexport interface PreparedBaseSnapshot<TImage = string, TExtra = unknown> {\n /** Schema version. Bumped when the on-disk shape changes incompatibly. */\n schema: number;\n base?: {\n /** Provider-opaque image identifier (docker tag | hetzner imageId | daytona snapshot name). */\n imageRef: TImage;\n /** Deterministic SHA-256 of the build context — the invalidation key. */\n contextSha256: string;\n /** Informational: CLI version that produced this artifact. */\n cliVersion: string;\n /** Informational: git short SHA injected at CLI build time (or 'dev'). */\n cliCommit?: string;\n /** ISO timestamp of bake completion. */\n createdAt: string;\n };\n /** Provider-specific extras (e.g. hetzner's per-project snapshot tier). */\n extras?: TExtra;\n}\n\nexport function preparedStatePathFor(provider: PreparedProviderKind): string {\n return pathResolve(homedir(), '.agentbox', `${provider}-prepared.json`);\n}\n\n/**\n * Read the prepared-state file for `provider`. Returns `null` when the file\n * is missing, malformed, or carries a schema this code doesn't recognise —\n * callers treat all three as \"rebuild needed\". Sync so it can run from\n * non-async setup paths (mirrors the hetzner helper it generalises).\n */\nexport function readPreparedStateRaw(provider: PreparedProviderKind): unknown {\n const path = preparedStatePathFor(provider);\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, 'utf8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Atomic write: write to `<path>.tmp` then rename. `mode: 0o600` because\n * the file is informational but lives alongside `secrets.env` — same dir,\n * same permissions hygiene.\n */\nexport function writePreparedStateRaw(provider: PreparedProviderKind, state: unknown): void {\n const path = preparedStatePathFor(provider);\n mkdirSync(dirname(path), { recursive: true });\n const body = JSON.stringify(state, null, 2) + '\\n';\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, body, { mode: 0o600 });\n renameSync(tmp, path);\n}\n\nexport async function sha256OfFile(path: string): Promise<string> {\n const buf = await readFile(path);\n return createHash('sha256').update(buf).digest('hex');\n}\n\nexport interface ContextFile {\n /**\n * Logical relative path. Used as the canonical key for hash determinism\n * — two stagings with identical contents but different absolute paths\n * must hash the same.\n */\n rel: string;\n /** Absolute path the file is read from. */\n abs: string;\n}\n\n/**\n * Deterministic hash over a set of context files. Entries are sorted by\n * `rel` then hashed as `<rel>\\0<sha256(file)>\\n` lines into a final SHA-256.\n *\n * - Sort order = determinism (the caller can pass files in any order).\n * - NUL separator = no collision between a `rel` ending in hex and the\n * following digest.\n * - Trailing newline per record = stable framing.\n *\n * Missing files raise — silently skipping would let a partial dev rebuild\n * stamp a hash that doesn't represent what's actually in the image.\n */\nexport async function computeContextSha256(files: ContextFile[]): Promise<string> {\n const sorted = [...files].sort((a, b) => (a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0));\n const outer = createHash('sha256');\n for (const f of sorted) {\n const inner = await sha256OfFile(f.abs);\n outer.update(`${f.rel}\\0${inner}\\n`);\n }\n return outer.digest('hex');\n}\n\n/** Short form for log lines — first 12 hex chars of a sha256. */\nexport function shortFingerprint(sha: string): string {\n return sha.slice(0, 12);\n}\n\n/**\n * CLI version stamps set by `apps/cli/src/index.ts` at startup via env vars\n * (the values themselves come from tsup's build-time `define`). Providers\n * record them onto prepared-state files and checkpoint manifests so a stale\n * artifact carries a human-readable hint about which CLI built it.\n *\n * Fallbacks cover the unit-test and unbundled-dev paths (the CLI never\n * loaded, env unset). `unknown` is a sentinel — never a real version.\n */\nexport interface CliStamp {\n cliVersion: string;\n cliCommit: string;\n}\n\nexport function readCliStamp(): CliStamp {\n return {\n cliVersion: process.env.AGENTBOX_CLI_VERSION ?? 'unknown',\n cliCommit: process.env.AGENTBOX_CLI_COMMIT ?? 'unknown',\n };\n}\n\n/**\n * Canonical map of files that go into the Docker base image build context\n * — every file `Dockerfile.box` COPYs, plus the Dockerfile itself. Two\n * layouts resolve the same logical entries:\n *\n * - staged: `<contextDir>/<staged>` (production CLI runtime + dev with `apps/cli/runtime/docker`)\n * - dev: `<sandboxDockerRoot>/<dev>` (workspace dev, no staged tree)\n *\n * Shared across providers because:\n * - sandbox-docker uses it to fingerprint its locally-built image.\n * - sandbox-daytona uses it to fingerprint the snapshot it bakes from the\n * same Dockerfile.box + the daytona-specific CLAUDE.md overlay.\n *\n * If you add a COPY line to `Dockerfile.box`, add the file here AND in\n * `apps/cli/scripts/stage-runtime.mjs` — failure to do so means the image\n * won't get re-built when that file changes.\n */\nexport const DOCKER_CONTEXT_FILE_MAP: Record<string, { staged: string; dev: string }> = {\n 'Dockerfile.box': { staged: 'Dockerfile.box', dev: 'Dockerfile.box' },\n 'ctl/bin.cjs': {\n staged: 'packages/ctl/dist/bin.cjs',\n dev: '../ctl/dist/bin.cjs',\n },\n 'share/agentbox-setup/SKILL.md': {\n staged: 'apps/cli/share/agentbox-setup/SKILL.md',\n dev: '../../apps/cli/share/agentbox-setup/SKILL.md',\n },\n 'scripts/agentbox-vnc-start': {\n staged: 'packages/sandbox-docker/scripts/agentbox-vnc-start',\n dev: 'scripts/agentbox-vnc-start',\n },\n 'scripts/agentbox-dockerd-start': {\n staged: 'packages/sandbox-docker/scripts/agentbox-dockerd-start',\n dev: 'scripts/agentbox-dockerd-start',\n },\n 'scripts/agentbox-checkpoint-cleanup': {\n staged: 'packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup',\n dev: 'scripts/agentbox-checkpoint-cleanup',\n },\n 'scripts/agentbox-open': {\n staged: 'packages/sandbox-docker/scripts/agentbox-open',\n dev: 'scripts/agentbox-open',\n },\n 'scripts/custom-system-CLAUDE.md': {\n staged: 'packages/sandbox-docker/scripts/custom-system-CLAUDE.md',\n dev: 'scripts/custom-system-CLAUDE.md',\n },\n 'scripts/claude-managed-settings.json': {\n staged: 'packages/sandbox-docker/scripts/claude-managed-settings.json',\n dev: 'scripts/claude-managed-settings.json',\n },\n 'scripts/agentbox-codex-hooks.json': {\n staged: 'packages/sandbox-docker/scripts/agentbox-codex-hooks.json',\n dev: 'scripts/agentbox-codex-hooks.json',\n },\n};\n\n/**\n * Resolve every entry in `fileMap` to an absolute path. Tries `<contextDir>/<staged>`\n * first; falls back to `<devRoot>/<dev>`. Returns `null` if any required file\n * is missing — callers treat that as \"can't fingerprint\" and skip the\n * cache-hit shortcut. Pure (no I/O beyond `existsSync`), so safe for use\n * from the provider's prepare path.\n */\nexport function resolveContextFilesFrom(\n fileMap: Record<string, { staged: string; dev: string }>,\n opts: { contextDir: string; devRoot: string },\n): ContextFile[] | null {\n const out: ContextFile[] = [];\n for (const [rel, paths] of Object.entries(fileMap)) {\n const candidates = [\n pathResolve(opts.contextDir, paths.staged),\n pathResolve(opts.devRoot, paths.dev),\n ];\n const hit = candidates.find((p) => existsSync(p));\n if (!hit) return null;\n out.push({ rel, abs: hit });\n }\n return out;\n}\n","/**\n * Docker provider's `~/.agentbox/docker-prepared.json` reader/writer + the\n * build-context fingerprint that drives base-image invalidation.\n *\n * The fingerprint is a SHA-256 over every file `docker build` would COPY\n * into the image — Dockerfile + scripts + baked config files. Two CLIs\n * with identical staged runtime trees produce the same hash; a one-byte\n * edit to any baked asset flips it, which is the signal `ensureImage()`\n * uses to rebuild instead of reusing the cached image.\n */\n\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n computeContextSha256,\n DOCKER_CONTEXT_FILE_MAP,\n readCliStamp,\n readPreparedStateRaw,\n resolveContextFilesFrom,\n writePreparedStateRaw,\n type ContextFile,\n type PreparedBaseSnapshot,\n} from '@agentbox/sandbox-core';\nimport { BUILD_CONTEXT_DIR, DEFAULT_BOX_IMAGE, DOCKERFILE_PATH } from './image.js';\n\nconst SCHEMA = 1 as const;\n\nexport type PreparedDockerState = PreparedBaseSnapshot<string, never>;\n\n/**\n * Resolve every fingerprint input to an absolute path. The canonical file\n * list lives in `@agentbox/sandbox-core` (DOCKER_CONTEXT_FILE_MAP) so the\n * daytona provider can hash the same inputs without depending on this\n * package. Two layouts are tried in order, mirroring `resolveDockerBuild()`\n * in `image.ts`:\n * 1. Build context dir (staged runtime / env override).\n * 2. Sandbox-docker package root (dev fallback).\n *\n * Returns `null` when *any* required file is missing — callers treat that\n * as \"can't fingerprint\" and skip the cache-hit shortcut (always rebuild).\n */\nexport function resolveContextFiles(opts: { contextDir?: string } = {}): ContextFile[] | null {\n const ctx = opts.contextDir ?? BUILD_CONTEXT_DIR;\n const here = dirname(fileURLToPath(import.meta.url));\n // sandbox-docker's package root = parent of src/ or parent of dist/.\n const packageRoot = resolve(here, '..');\n return resolveContextFilesFrom(DOCKER_CONTEXT_FILE_MAP, {\n contextDir: ctx,\n devRoot: packageRoot,\n });\n}\n\nexport interface ResolvedFingerprint {\n contextSha256: string;\n /** Files that fed the hash (in canonical sorted order). */\n files: ContextFile[];\n}\n\nexport async function computeDockerContextFingerprint(opts: {\n contextDir?: string;\n} = {}): Promise<ResolvedFingerprint | null> {\n const files = resolveContextFiles(opts);\n if (!files) return null;\n return { contextSha256: await computeContextSha256(files), files };\n}\n\nexport function readPreparedDockerState(): PreparedDockerState | null {\n const raw = readPreparedStateRaw('docker');\n if (raw === null || typeof raw !== 'object') return null;\n const parsed = raw as Partial<PreparedDockerState>;\n if (parsed.schema !== SCHEMA) return null;\n return { schema: SCHEMA, base: parsed.base };\n}\n\nexport function writePreparedDockerState(opts: {\n imageRef?: string;\n contextSha256: string;\n}): void {\n const stamp = readCliStamp();\n const state: PreparedDockerState = {\n schema: SCHEMA,\n base: {\n imageRef: opts.imageRef ?? DEFAULT_BOX_IMAGE,\n contextSha256: opts.contextSha256,\n cliVersion: stamp.cliVersion,\n cliCommit: stamp.cliCommit,\n createdAt: new Date().toISOString(),\n },\n };\n writePreparedStateRaw('docker', state);\n}\n\n/** Convenience for `ensureImage` and `prepare` — true when the stamped fingerprint matches. */\nexport function preparedMatches(state: PreparedDockerState | null, current: string): boolean {\n return state?.base?.contextSha256 === current;\n}\n\n/** Re-export so callers don't reach into image.ts just for the Dockerfile path. */\nexport { DOCKERFILE_PATH };\n","import { execa } from 'execa';\nimport { existsSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, resolve } from 'node:path';\n\nexport const DEFAULT_BOX_IMAGE = 'agentbox/box:dev';\n\nconst here = dirname(fileURLToPath(import.meta.url));\n\n// The Dockerfile's COPY lines reference monorepo-relative paths\n// (packages/ctl/dist/bin.cjs, apps/cli/share/..., packages/sandbox-docker/scripts/*),\n// so the build context must be a dir containing that tree.\n//\n// Resolution order:\n// 0. AGENTBOX_DOCKER_CONTEXT env override (dir holding Dockerfile.box).\n// 1. Staged context shipped with the bundled `agent-box` package: this\n// module is bundled into the CLI at <root>/dist, the stage step mirrors\n// the COPY tree at <root>/runtime/docker (sibling of dist/, uniform in\n// dev and when installed).\n// 2. Legacy monorepo: Dockerfile.box at the sandbox-docker package root,\n// build context = monorepo root.\nfunction resolveDockerBuild(): { dockerfile: string; context: string } {\n const override = process.env.AGENTBOX_DOCKER_CONTEXT;\n if (override && existsSync(resolve(override, 'Dockerfile.box'))) {\n return { dockerfile: resolve(override, 'Dockerfile.box'), context: override };\n }\n const staged = resolve(here, '..', 'runtime', 'docker');\n if (existsSync(resolve(staged, 'Dockerfile.box'))) {\n return { dockerfile: resolve(staged, 'Dockerfile.box'), context: staged };\n }\n // Legacy: src/ (or the unbundled package dist/) is one level under the\n // package root; the monorepo root is two more up.\n const packageRoot = resolve(here, '..');\n return {\n dockerfile: resolve(packageRoot, 'Dockerfile.box'),\n context: resolve(packageRoot, '..', '..'),\n };\n}\n\nconst { dockerfile: DOCKERFILE_PATH_RESOLVED, context: BUILD_CONTEXT_DIR_RESOLVED } =\n resolveDockerBuild();\nexport const DOCKERFILE_PATH = DOCKERFILE_PATH_RESOLVED;\nexport const BUILD_CONTEXT_DIR = BUILD_CONTEXT_DIR_RESOLVED;\n\nexport async function imageExists(ref: string): Promise<boolean> {\n const result = await execa('docker', ['image', 'inspect', ref], { reject: false });\n return result.exitCode === 0;\n}\n\nexport interface ImageInfo {\n /** Image ref (e.g. `agentbox/box:dev`). */\n ref: string;\n /** True when the engine has the image locally. */\n exists: boolean;\n /** Image size in bytes, when known. */\n sizeBytes?: number;\n /** ISO-8601 creation time, when known. */\n createdAt?: string;\n}\n\n/**\n * Read-only inspect of a Docker image. Used by `agentbox prepare` (no-args\n * status mode) to surface base-image state. Never throws — returns\n * `{ exists: false }` on any error so the status command works even when\n * the docker daemon is unreachable.\n */\nexport async function imageInfo(ref: string = DEFAULT_BOX_IMAGE): Promise<ImageInfo> {\n const result = await execa(\n 'docker',\n ['image', 'inspect', '--format', '{{.Size}}|{{.Created}}', ref],\n { reject: false },\n );\n if (result.exitCode !== 0) return { ref, exists: false };\n const [sizeStr, createdAt] = result.stdout.trim().split('|');\n const sizeBytes = sizeStr ? Number.parseInt(sizeStr, 10) : NaN;\n return {\n ref,\n exists: true,\n sizeBytes: Number.isFinite(sizeBytes) ? sizeBytes : undefined,\n createdAt: createdAt && createdAt.length > 0 ? createdAt : undefined,\n };\n}\n\nexport interface BuildImageOptions {\n ref?: string;\n dockerfile?: string;\n contextDir?: string;\n onProgress?: (line: string) => void;\n}\n\nexport async function buildImage(opts: BuildImageOptions = {}): Promise<string> {\n const ref = opts.ref ?? DEFAULT_BOX_IMAGE;\n const dockerfile = opts.dockerfile ?? DOCKERFILE_PATH;\n const contextDir = opts.contextDir ?? BUILD_CONTEXT_DIR;\n\n // Dogfood path: when building from inside an agentbox (docker-in-docker),\n // the default bridge network can't bind-mount /proc/<pid>/ns/net for the\n // build container, breaking any RUN that needs network (e.g. apt, curl).\n // Falling back to host networking sidesteps the missing capability.\n const args = ['build', '-t', ref, '-f', dockerfile, contextDir];\n if (process.env.AGENTBOX === '1') {\n args.splice(1, 0, '--network=host');\n }\n\n const subprocess = execa('docker', args, {\n stderr: 'pipe',\n stdout: 'pipe',\n });\n\n if (opts.onProgress) {\n const forward = (chunk: Buffer | string): void => {\n const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');\n for (const line of text.split(/\\r?\\n/)) {\n if (line.length > 0) opts.onProgress?.(line);\n }\n };\n subprocess.stdout?.on('data', forward);\n subprocess.stderr?.on('data', forward);\n }\n\n await subprocess;\n return ref;\n}\n\nexport interface EnsureImageOptions {\n onProgress?: (line: string) => void;\n /** Dockerfile path. Defaults to `Dockerfile.box` next to this package. */\n dockerfile?: string;\n /** Build context directory. Defaults to the monorepo root. */\n contextDir?: string;\n}\n\nexport async function ensureImage(\n ref: string = DEFAULT_BOX_IMAGE,\n opts: EnsureImageOptions = {},\n): Promise<{ ref: string; built: boolean; reason?: string }> {\n // Lazy import: prepared-state imports back into image.ts for the default\n // DOCKERFILE_PATH/BUILD_CONTEXT_DIR constants, so loading it at top-level\n // would create a circular ESM init order.\n const {\n computeDockerContextFingerprint,\n readPreparedDockerState,\n writePreparedDockerState,\n preparedMatches,\n } = await import('./prepared-state.js');\n\n const fingerprint = await computeDockerContextFingerprint({\n contextDir: opts.contextDir,\n });\n const prepared = readPreparedDockerState();\n const exists = await imageExists(ref);\n\n let reason: string | undefined;\n if (!exists) {\n reason = `image ${ref} not present`;\n } else if (!fingerprint) {\n // Couldn't enumerate the context (partial dev rebuild?). Don't rebuild\n // unconditionally — that would surprise users mid-iteration. Trust the\n // image-exists check and leave the prepared file untouched.\n return { ref, built: false, reason: 'image present (fingerprint skipped)' };\n } else if (!prepared) {\n reason = 'no docker-prepared.json on disk';\n } else if (!preparedMatches(prepared, fingerprint.contextSha256)) {\n reason =\n `build context changed (was ${prepared.base?.contextSha256?.slice(0, 12) ?? '<none>'}, ` +\n `now ${fingerprint.contextSha256.slice(0, 12)})`;\n }\n\n if (!reason) {\n return { ref, built: false, reason: 'image up to date' };\n }\n\n opts.onProgress?.(`[image] rebuilding ${ref}: ${reason}`);\n await buildImage({\n ref,\n dockerfile: opts.dockerfile,\n contextDir: opts.contextDir,\n onProgress: opts.onProgress,\n });\n if (fingerprint) {\n writePreparedDockerState({ imageRef: ref, contextSha256: fingerprint.contextSha256 });\n }\n return { ref, built: true, reason };\n}\n\n"],"mappings":"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","names":["join","readFile","homedir","dirname","dirname","resolve","fileURLToPath","execa","existsSync","dirname","computeDockerContextFingerprint","readPreparedDockerState","writePreparedDockerState","preparedMatches","here","fileURLToPath","resolve"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../packages/sandbox-vercel/src/env-loader.ts","../../../packages/sandbox-vercel/src/sbx-cli.ts","../../../packages/sandbox-vercel/src/credentials.ts","../../../packages/sandbox-vercel/src/cli-store.ts","../../../packages/sandbox-vercel/src/sdk.ts","../../../packages/sandbox-vercel/src/vercel-rest.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { resolve } from 'node:path';\n\n/**\n * Vercel env auto-loader. The `@vercel/sandbox` SDK reads `VERCEL_OIDC_TOKEN`\n * from `process.env`; for the access-token path we read `VERCEL_TOKEN` +\n * `VERCEL_TEAM_ID` + `VERCEL_PROJECT_ID` and thread them into every SDK call as\n * explicit `Credentials`. We pull all of these in from `~/.agentbox/secrets.env`\n * (written by `agentbox vercel login`) so the SDK Just Works after a one-time\n * login — exactly the daytona/hetzner model.\n *\n * Lookup order (first wins; process.env is never overwritten):\n * 1. `process.env` (already set in the shell).\n * 2. `~/.agentbox/secrets.env` — written by `agentbox vercel login`.\n *\n * Project-level `.env` / `.env.local` are intentionally NOT consulted: those\n * files belong to the app code being developed, and a `VERCEL_*` value there\n * (e.g. a `vercel env pull` OIDC token, or the app's own deploy token) is meant\n * for in-box code, not for the host CLI to harvest and provision sandboxes with.\n * Put host credentials in `~/.agentbox/secrets.env` (or the shell env).\n *\n * Only Vercel-prefixed keys are imported; the rest of the file is left alone.\n * Idempotent and side-effect-free after the first call.\n */\nconst VERCEL_KEYS = [\n 'VERCEL_OIDC_TOKEN',\n 'VERCEL_TOKEN',\n 'VERCEL_TEAM_ID',\n 'VERCEL_PROJECT_ID',\n // Marker for CLI-login mode (`agentbox vercel login` → `sandbox login`). The\n // access token is NOT stored here — it's read live from the Vercel CLI store;\n // only this marker + team/project ids are persisted.\n 'VERCEL_AUTH_SOURCE',\n] as const;\n\nlet loaded = false;\n\nexport function ensureVercelEnvLoaded(): void {\n if (loaded) return;\n loaded = true;\n importVercelFromFile(resolve(homedir(), '.agentbox', 'secrets.env'), VERCEL_KEYS);\n}\n\n/**\n * Force a re-read of `~/.agentbox/secrets.env`. Used by the interactive\n * `agentbox vercel login` flow after it persists the credential trio, so the\n * same process can pick it up without a restart.\n */\nexport function reloadVercelEnv(): void {\n loaded = false;\n ensureVercelEnvLoaded();\n}\n\nfunction importVercelFromFile(path: string, keys: readonly string[]): void {\n if (!existsSync(path)) return;\n let body: string;\n try {\n body = readFileSync(path, 'utf8');\n } catch {\n return;\n }\n const parsed = parseEnvFile(body);\n for (const key of keys) {\n if (process.env[key] !== undefined) continue;\n const value = parsed[key];\n if (typeof value === 'string') {\n process.env[key] = value;\n }\n }\n}\n\n/**\n * Minimal `.env` parser: handles `KEY=value`, `KEY=\"value\"`, `KEY='value'`,\n * `export KEY=value`, blank lines, and `#` comments. No variable\n * interpolation — predictable over feature-complete (matches the daytona\n * loader's behavior).\n */\nexport function parseEnvFile(body: string): Record<string, string> {\n const out: Record<string, string> = {};\n for (const rawLine of body.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (line.length === 0 || line.startsWith('#')) continue;\n const stripped = line.startsWith('export ') ? line.slice('export '.length) : line;\n const eq = stripped.indexOf('=');\n if (eq <= 0) continue;\n const key = stripped.slice(0, eq).trim();\n let value = stripped.slice(eq + 1).trim();\n if (\n value.length >= 2 &&\n ((value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\")))\n ) {\n value = value.slice(1, -1);\n }\n out[key] = value;\n }\n return out;\n}\n","/**\n * Driver for the official Vercel Sandbox CLI (`sandbox` / `sbx`, installed via\n * `npm i -g sandbox`). AgentBox's CLI-login auth mode uses it for two things:\n * - `loginSbx` — run `sandbox login` interactively so the user completes\n * the browser OAuth and the CLI writes its own credential store.\n * - `refreshSbxToken` — run a cheap read command (`sandbox list`) which makes\n * the CLI lazily refresh its access token from its stored refresh token\n * when the token is stale. Verified non-interactive: a stale token is\n * refreshed without opening a browser.\n *\n * Mirrors the probe/install patterns in sandbox-docker/src/portless.ts: execa\n * with `reject:false` for never-throw probes, spawnSync with inherited stdio for\n * the interactive login.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { execa } from 'execa';\n\n/**\n * Binaries the Sandbox CLI installs. `sbx` is the short alias; we prefer it but\n * fall back to `sandbox`. Pinned here so a future rename is a one-line fix.\n */\nconst SBX_BINS = ['sbx', 'sandbox'] as const;\n\nexport interface SbxState {\n /** A Sandbox CLI binary resolved on PATH and answered `--version`. */\n installed: boolean;\n /** The binary name that answered (`sbx` or `sandbox`), when installed. */\n bin?: string;\n /** Version string, when installed. */\n version?: string;\n}\n\nlet cached: SbxState | null = null;\n\n/**\n * Probe the host for the Sandbox CLI. Cached per-process (install state can't\n * change mid-command); `resetSbxCache` clears it after an install or for tests.\n */\nexport async function detectSbx(): Promise<SbxState> {\n if (cached !== null) return cached;\n for (const bin of SBX_BINS) {\n try {\n const r = await execa(bin, ['--version'], { reject: false });\n if (r.exitCode === 0) {\n cached = { installed: true, bin, version: (r.stdout ?? '').trim() || undefined };\n return cached;\n }\n } catch {\n // try the next bin name\n }\n }\n cached = { installed: false };\n return cached;\n}\n\n/** Drop the per-process probe cache so the next `detectSbx()` re-probes. */\nexport function resetSbxCache(): void {\n cached = null;\n}\n\n/** Command the user should run to install the Sandbox CLI. */\nexport function installSbxHint(): string {\n return 'npm install -g sandbox';\n}\n\n/** Install the Sandbox CLI globally (`npm install -g sandbox`). Never throws. */\nexport async function installSbx(): Promise<boolean> {\n try {\n const r = await execa('npm', ['install', '-g', 'sandbox'], { reject: false });\n return r.exitCode === 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Run `sandbox login` with inherited stdio so the user sees the CLI's own\n * browser-OAuth output and can interact with it. Blocking, like the interactive\n * agent launch path. Returns the exit status (0 = success).\n */\nexport function loginSbx(bin: string): number {\n const r = spawnSync(bin, ['login'], { stdio: 'inherit' });\n return r.status ?? 1;\n}\n\n/**\n * Trigger the CLI's lazy token refresh by running a cheap read command. The CLI\n * refreshes its access token from the stored refresh token when the token is\n * stale and leaves a still-valid token untouched, so this is safe to call\n * eagerly. Non-interactive (stdin from /dev/null); returns true on exit 0.\n */\nexport async function refreshSbxToken(bin: string): Promise<boolean> {\n try {\n const r = await execa(bin, ['list'], {\n reject: false,\n timeout: 30_000,\n stdin: 'ignore',\n });\n return r.exitCode === 0;\n } catch {\n return false;\n }\n}\n","import {\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n writeFileSync,\n} from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, resolve } from 'node:path';\nimport {\n confirm,\n isCancel,\n intro,\n log,\n note,\n outro,\n password,\n select,\n spinner,\n text,\n} from '@clack/prompts';\nimport { ensureVercelEnvLoaded, reloadVercelEnv } from './env-loader.js';\nimport { hasUsableCredentials } from './sdk.js';\nimport { cliStorePaths, isNearExpiry, readCliAuth, readCliCurrentTeam } from './cli-store.js';\nimport { detectSbx, installSbx, installSbxHint, loginSbx, resetSbxCache } from './sbx-cli.js';\nimport { createProject, getUser, listProjects, type VercelProject } from './vercel-rest.js';\n\nconst DASHBOARD_TOKENS_URL = 'https://vercel.com/account/settings/tokens';\n\n/**\n * Keys we manage in `~/.agentbox/secrets.env`. On reconfigure we strip prior\n * values for these before appending so the file never accumulates duplicates.\n * `VERCEL_AUTH_SOURCE` is the CLI-login marker; the access token itself is never\n * stored here in that mode (it's read live from the Vercel CLI store).\n */\nconst MANAGED_KEYS = [\n 'VERCEL_OIDC_TOKEN',\n 'VERCEL_TOKEN',\n 'VERCEL_TEAM_ID',\n 'VERCEL_PROJECT_ID',\n 'VERCEL_AUTH_SOURCE',\n] as const;\n\nexport interface EnsureVercelCredentialsOptions {\n /** Re-prompt even when valid credentials are already present (`agentbox vercel login`). */\n force?: boolean;\n}\n\n/**\n * First-run interactive setup for Vercel credentials. The access-token path\n * persists a `VERCEL_TOKEN` trio to `~/.agentbox/secrets.env` (the canonical\n * store, matching daytona/hetzner). OIDC is also supported, but the token must\n * be present in the shell env or in `~/.agentbox/secrets.env` — agentbox does\n * NOT harvest `.env.local` (that file belongs to the app being developed).\n *\n * No-op when credentials are already configured. Silent no-op when stdin isn't\n * a TTY so scripted/CI callers get the SDK's \"not configured\" error instead of\n * a hung prompt.\n */\nexport async function ensureVercelCredentials(\n opts: EnsureVercelCredentialsOptions = {},\n): Promise<void> {\n ensureVercelEnvLoaded();\n\n if (!opts.force && hasUsableCredentials()) return;\n if (!process.stdin.isTTY) return;\n\n intro('Vercel setup');\n note(\n `AgentBox needs Vercel credentials to provision sandboxes.\\n` +\n `Sign in with Vercel (recommended): drives the Vercel \\`sandbox\\` CLI through a browser login, then reads the token from the CLI's own store and keeps it fresh — no token to paste.\\n` +\n `Access token (best for CI / headless): personal access token + team id + project id, saved to \\`~/.agentbox/secrets.env\\`.\\n` +\n `OIDC (short interactive work): export VERCEL_OIDC_TOKEN in your shell or add it to \\`~/.agentbox/secrets.env\\` (dev token expires ~12h, no headless refresh).`,\n 'Credentials required',\n );\n\n const mode = await select({\n message: 'How do you want to authenticate?',\n options: [\n { value: 'cli', label: 'Sign in with Vercel (browser) — recommended for interactive use' },\n { value: 'token', label: 'Access token (VERCEL_TOKEN + team + project) — best for CI / headless' },\n { value: 'oidc', label: 'OIDC token (VERCEL_OIDC_TOKEN in env / secrets.env) — short interactive work' },\n ],\n initialValue: 'cli',\n });\n if (isCancel(mode)) {\n log.warn('Vercel setup cancelled — re-run `agentbox vercel login` when ready.');\n return;\n }\n\n if (mode === 'cli') {\n await runCliLogin();\n return;\n }\n\n if (mode === 'oidc') {\n note(\n `Get an OIDC token with \\`vercel link\\` then \\`vercel env pull\\`, then make it visible to AgentBox by either:\\n` +\n ` export VERCEL_OIDC_TOKEN=<token> # in this shell\\n` +\n ` echo \"VERCEL_OIDC_TOKEN=<token>\" >> ~/.agentbox/secrets.env\\n` +\n `Re-do every ~12h; the dev token expires. AgentBox does not harvest .env.local.`,\n 'OIDC setup',\n );\n // Re-read in case the user already added the token to secrets.env.\n reloadVercelEnv();\n if (process.env.VERCEL_OIDC_TOKEN) {\n log.success('Found VERCEL_OIDC_TOKEN — Vercel is configured.');\n await ensureSbxInstalled();\n outro('Setup complete.');\n } else {\n log.warn('No VERCEL_OIDC_TOKEN found yet — set it as above, then re-run `agentbox vercel login`.');\n }\n return;\n }\n\n const creds = await promptForTokenTrio();\n if (creds === null) return;\n persistCredentials(creds);\n log.success(`Vercel credentials saved to ${secretsPath()}`);\n await ensureSbxInstalled();\n outro('Setup complete.');\n}\n\ninterface TokenTrio {\n token: string;\n teamId: string;\n projectId: string;\n}\n\nasync function promptForTokenTrio(): Promise<TokenTrio | null> {\n const openIt = await confirm({\n message: `Open ${DASHBOARD_TOKENS_URL} to create a token?`,\n initialValue: true,\n });\n if (isCancel(openIt)) return null;\n if (openIt) openDashboard();\n\n const token = await password({\n message: 'Paste your Vercel access token',\n validate: (v) => (v && v.trim().length > 0 ? undefined : 'Cannot be empty'),\n });\n if (isCancel(token)) {\n log.warn('Vercel setup cancelled.');\n return null;\n }\n const teamId = await text({\n message: 'Team ID (team settings → General)',\n placeholder: 'team_...',\n validate: (v) => (v && v.trim().length > 0 ? undefined : 'Cannot be empty'),\n });\n if (isCancel(teamId)) {\n log.warn('Vercel setup cancelled.');\n return null;\n }\n const projectId = await text({\n message: 'Project ID (project settings → General)',\n placeholder: 'prj_...',\n validate: (v) => (v && v.trim().length > 0 ? undefined : 'Cannot be empty'),\n });\n if (isCancel(projectId)) {\n log.warn('Vercel setup cancelled.');\n return null;\n }\n return { token: token.trim(), teamId: teamId.trim(), projectId: projectId.trim() };\n}\n\nfunction persistCredentials(creds: TokenTrio): void {\n writeManaged({\n VERCEL_TOKEN: creds.token,\n VERCEL_TEAM_ID: creds.teamId,\n VERCEL_PROJECT_ID: creds.projectId,\n });\n}\n\n/**\n * Persist the CLI-login marker + cached stable ids. The access token is\n * deliberately omitted — it's read live from the Vercel CLI store on each call\n * and refreshed there, so the only thing we cache is the team/project scope.\n */\nfunction persistCliCredentials(ids: { teamId: string; projectId: string }): void {\n writeManaged({\n VERCEL_AUTH_SOURCE: 'cli',\n VERCEL_TEAM_ID: ids.teamId,\n VERCEL_PROJECT_ID: ids.projectId,\n });\n}\n\n/**\n * Atomically rewrite the managed Vercel keys in `~/.agentbox/secrets.env`:\n * strip every prior value for a `MANAGED_KEYS` entry, then append exactly the\n * keys in `record` (mode 0600, temp-file + rename). Also mirrors the record\n * into `process.env` (and clears the other managed keys there) so the current\n * run uses the new values immediately.\n */\nfunction writeManaged(record: Record<string, string>): void {\n for (const k of MANAGED_KEYS) delete process.env[k];\n for (const [k, v] of Object.entries(record)) process.env[k] = v;\n\n const path = secretsPath();\n mkdirSync(dirname(path), { recursive: true });\n\n let existing = '';\n if (existsSync(path)) {\n try {\n existing = readFileSync(path, 'utf8');\n } catch {\n existing = '';\n }\n }\n const kept = existing\n .split(/\\r?\\n/)\n .filter((line) => {\n const stripped = line.startsWith('export ') ? line.slice('export '.length) : line;\n const eq = stripped.indexOf('=');\n if (eq <= 0) return true;\n const key = stripped.slice(0, eq).trim();\n return !(MANAGED_KEYS as readonly string[]).includes(key);\n })\n .join('\\n')\n .replace(/\\s+$/u, '');\n\n const lines = Object.entries(record).map(([k, v]) => `${k}=${v}`);\n const body = (kept ? `${kept}\\n` : '') + lines.join('\\n') + '\\n';\n\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, body, { mode: 0o600 });\n try {\n chmodSync(tmp, 0o600);\n } catch {\n // chmod best-effort; writeFileSync mode already covers most filesystems.\n }\n renameSync(tmp, path);\n try {\n chmodSync(path, 0o600);\n } catch {\n // ignore — already attempted above\n }\n}\n\n/**\n * The full CLI-login flow: make sure the Vercel `sandbox` CLI is installed (offer\n * to install it), run its browser OAuth, harvest the team id from the CLI store,\n * let the user pick a project to scope sandboxes to, and persist the marker +\n * ids. The access token is never stored — `resolveCredentials` reads it live.\n */\n/**\n * Make sure the Vercel `sandbox` CLI is on PATH, offering to install it. Every\n * login mode ensures it because interactive attach (`agentbox shell|claude|\n * codex|opencode` on a vercel box) drives `sbx exec` for a real PTY. Returns the\n * resolved bin, or null if absent / declined / install failed (callers warn).\n */\nasync function ensureSbxInstalled(): Promise<{ bin: string } | null> {\n let det = await detectSbx();\n if (!det.installed) {\n const doInstall = await confirm({\n message: `The Vercel sandbox CLI (needed for interactive attach) isn't installed. Install it now? (${installSbxHint()})`,\n initialValue: true,\n });\n if (isCancel(doInstall) || !doInstall) {\n log.warn(\n `Install it with \\`${installSbxHint()}\\` to use \\`agentbox shell|claude|codex|opencode\\` on Vercel boxes.`,\n );\n return null;\n }\n const sp = spinner();\n sp.start('Installing the Vercel sandbox CLI…');\n const ok = await installSbx();\n resetSbxCache();\n det = await detectSbx();\n if (!ok || !det.installed || !det.bin) {\n sp.stop('Install failed.');\n log.warn(`Could not install the sandbox CLI — run \\`${installSbxHint()}\\` manually.`);\n return null;\n }\n sp.stop(`Installed sandbox CLI${det.version ? ` ${det.version}` : ''}.`);\n }\n return det.bin ? { bin: det.bin } : null;\n}\n\nasync function runCliLogin(): Promise<void> {\n const det = await ensureSbxInstalled();\n if (!det) {\n log.warn('The Vercel sandbox CLI is required to sign in this way — install it, then re-run `agentbox vercel login`.');\n return;\n }\n\n note('A browser window will open to sign in to Vercel.', 'Vercel sign-in');\n const status = loginSbx(det.bin);\n if (status !== 0) {\n log.warn('Vercel sign-in did not complete — re-run `agentbox vercel login` to try again.');\n return;\n }\n\n const harvested = harvestCliCredentials();\n if (!harvested) {\n log.warn('Sign-in finished but no credentials were found in the Vercel CLI store. Try again.');\n return;\n }\n\n // Validate the token early so a bad/expired session fails here, not mid-op.\n try {\n await getUser(harvested.token);\n } catch (err) {\n log.warn(\n `The Vercel session looks invalid (${err instanceof Error ? err.message : String(err)}). ` +\n 'Re-run `agentbox vercel login`.',\n );\n return;\n }\n\n const projectId = await resolveProjectId(harvested.token, harvested.teamId);\n if (projectId === null) {\n log.warn('No project selected — re-run `agentbox vercel login` to finish setup.');\n return;\n }\n\n persistCliCredentials({ teamId: harvested.teamId, projectId });\n reloadVercelEnv();\n log.success(`Signed in with Vercel — credentials managed by the sandbox CLI (saved scope to ${secretsPath()}).`);\n outro('Setup complete.');\n}\n\n/**\n * Read the live token + team id from the Vercel CLI store. teamId prefers an\n * already-cached `VERCEL_TEAM_ID` (e.g. from a prior login) and falls back to\n * the CLI's `currentTeam`. Null when the CLI isn't logged in.\n */\nfunction harvestCliCredentials(): { token: string; teamId: string } | null {\n const auth = readCliAuth();\n if (!auth) return null;\n const teamId = process.env.VERCEL_TEAM_ID ?? readCliCurrentTeam();\n if (!teamId) return null;\n return { token: auth.token, teamId };\n}\n\n/**\n * Pick the Vercel project sandboxes run under. Lists the team's projects in a\n * clack select (pre-selecting an existing `agentbox` / sandbox-default project),\n * plus a \"create a new project\" entry. Returns the project id, or null if the\n * user cancelled. Non-interactive callers reuse/create an `agentbox` project.\n */\nasync function resolveProjectId(token: string, teamId: string): Promise<string | null> {\n let projects: VercelProject[] = [];\n const sp = spinner();\n sp.start('Loading your Vercel projects…');\n try {\n projects = await listProjects(token, teamId);\n sp.stop(`Found ${projects.length} project${projects.length === 1 ? '' : 's'}.`);\n } catch (err) {\n sp.stop('Could not list projects.');\n log.warn(`Failed to list Vercel projects: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n\n const CREATE = '__create__';\n const preferred =\n projects.find((p) => p.name === 'agentbox') ??\n projects.find((p) => p.name === 'vercel-sandbox-default-project');\n const choice = await select({\n message: 'Which Vercel project should sandboxes run under?',\n options: [\n ...projects.map((p) => ({ value: p.id, label: p.name })),\n { value: CREATE, label: 'Create a new project…' },\n ],\n initialValue: preferred ? preferred.id : CREATE,\n });\n if (isCancel(choice)) return null;\n\n if (choice !== CREATE) return choice;\n\n const name = await text({\n message: 'New project name',\n placeholder: 'agentbox',\n defaultValue: 'agentbox',\n validate: (v) => (v && v.trim().length > 0 ? undefined : 'Cannot be empty'),\n });\n if (isCancel(name)) return null;\n try {\n const created = await createProject(token, teamId, name.trim() || 'agentbox');\n return created.id;\n } catch (err) {\n log.warn(`Could not create the project: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n}\n\nfunction openDashboard(): void {\n // Lazy import keeps node:child_process out of the module's load cost.\n import('node:child_process')\n .then(({ spawnSync }) => {\n const r = spawnSync('open', [DASHBOARD_TOKENS_URL], { stdio: 'ignore' });\n if (r.status !== 0) {\n log.warn(`Could not auto-open the browser — visit ${DASHBOARD_TOKENS_URL} manually.`);\n }\n })\n .catch(() => {\n log.warn(`Could not auto-open the browser — visit ${DASHBOARD_TOKENS_URL} manually.`);\n });\n}\n\nexport function secretsPath(): string {\n return resolve(homedir(), '.agentbox', 'secrets.env');\n}\n\nexport interface VercelCredStatus {\n /** Which auth mode is configured. */\n auth: 'oidc' | 'cli' | 'token' | 'none';\n /** Legacy alias kept for callers that branch on OIDC. */\n oidc: boolean;\n token?: string;\n teamId?: string;\n projectId?: string;\n source: 'env' | 'secrets.env' | 'cli-store' | 'none';\n /**\n * CLI mode only: details about the live Vercel CLI session. Whether the\n * `sandbox` CLI is *installed* needs an async probe (`detectSbx`) and is not\n * reported here — the status printer probes it separately.\n */\n cli?: {\n /** A logged-in session was found in the CLI store. */\n loggedIn: boolean;\n /** Unix seconds the live access token expires at, when known. */\n expiresAt?: number;\n /** True when the token is at/near expiry (a refresh would fire). */\n nearExpiry?: boolean;\n /** Path to the CLI's `auth.json`. */\n authPath: string;\n };\n}\n\nexport function readVercelCredStatus(): VercelCredStatus {\n const shellHad = !!process.env.VERCEL_OIDC_TOKEN || !!process.env.VERCEL_TOKEN;\n ensureVercelEnvLoaded();\n const oidc = !!process.env.VERCEL_OIDC_TOKEN;\n const teamId = process.env.VERCEL_TEAM_ID;\n const projectId = process.env.VERCEL_PROJECT_ID;\n\n if (oidc) {\n return { auth: 'oidc', oidc: true, teamId, projectId, source: shellHad ? 'env' : 'secrets.env' };\n }\n\n if (process.env.VERCEL_AUTH_SOURCE === 'cli') {\n const auth = readCliAuth();\n return {\n auth: 'cli',\n oidc: false,\n token: auth?.token,\n teamId,\n projectId,\n source: 'cli-store',\n cli: {\n loggedIn: !!auth,\n expiresAt: auth?.expiresAt,\n nearExpiry: auth ? isNearExpiry(auth) : undefined,\n authPath: cliStorePaths().authPath,\n },\n };\n }\n\n const token = process.env.VERCEL_TOKEN;\n if (!token) return { auth: 'none', oidc: false, source: 'none' };\n return {\n auth: 'token',\n oidc: false,\n token,\n teamId,\n projectId,\n source: shellHad ? 'env' : 'secrets.env',\n };\n}\n\nexport function maskKey(value: string): string {\n if (value.length <= 8) return '*'.repeat(value.length);\n return `${value.slice(0, 4)}…${'*'.repeat(8)}${value.slice(-4)}`;\n}\n","/**\n * Reader for the Vercel CLI's own credential store — written by `sandbox login`\n * / `vercel login` (the `sandbox`/`sbx` CLI and `vercel` CLI share one store).\n *\n * AgentBox's \"CLI-login\" auth mode (see credentials.ts) drives that CLI for the\n * browser OAuth, then reads the resulting OAuth access token live from here on\n * every SDK call rather than copying it into `secrets.env`: the token is a\n * short-lived, opaque `vca_…` access token that the CLI refreshes lazily from\n * its stored refresh token, so the CLI store is the single self-refreshing\n * source of truth. We only cache the stable bits (team/project id + a marker)\n * in `secrets.env`.\n *\n * Pure FS + path logic — no clack, no execa — so it stays trivially testable.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/** The shape of the CLI's `auth.json` (only the fields we consume). */\nexport interface VercelCliAuth {\n /** Opaque OAuth access token (`vca_…`). NOT a JWT — expiry is `expiresAt`. */\n token: string;\n /** Unix seconds. Absent on some CLI versions → treated as near-expiry. */\n expiresAt?: number;\n /** Present but unused by us; the CLI uses it to self-refresh `token`. */\n refreshToken?: string;\n}\n\n/**\n * Resolve the `com.vercel.cli` data directory the way the CLI itself does\n * (xdg-app-paths-style), per platform:\n * - macOS: ~/Library/Application Support/com.vercel.cli\n * - Windows: %APPDATA%\\com.vercel.cli\n * - else: $XDG_DATA_HOME/com.vercel.cli (default ~/.local/share/...)\n *\n * `AGENTBOX_VERCEL_CLI_DIR` overrides outright — for tests and the rare install\n * that relocates the store.\n */\nexport function vercelCliDir(): string {\n const override = process.env.AGENTBOX_VERCEL_CLI_DIR;\n if (override && override.trim().length > 0) return override.trim();\n\n const name = 'com.vercel.cli';\n if (process.platform === 'darwin') {\n return join(homedir(), 'Library', 'Application Support', name);\n }\n if (process.platform === 'win32') {\n const appData = process.env.APPDATA;\n if (appData && appData.trim().length > 0) return join(appData, name);\n return join(homedir(), 'AppData', 'Roaming', name);\n }\n const xdg = process.env.XDG_DATA_HOME;\n const base = xdg && xdg.trim().length > 0 ? xdg.trim() : join(homedir(), '.local', 'share');\n return join(base, name);\n}\n\n/** Absolute paths to the CLI store files, for status/diagnostics. */\nexport function cliStorePaths(): { authPath: string; configPath: string } {\n const dir = vercelCliDir();\n return { authPath: join(dir, 'auth.json'), configPath: join(dir, 'config.json') };\n}\n\nfunction readJson(path: string): unknown {\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, 'utf8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Read the live access token from the CLI store. Returns null when the store is\n * missing / unparseable / has no token (CLI never logged in, or logged out) so\n * callers can surface a clear \"run `agentbox vercel login`\" error.\n */\nexport function readCliAuth(): VercelCliAuth | null {\n const raw = readJson(cliStorePaths().authPath) as Record<string, unknown> | null;\n if (!raw || typeof raw.token !== 'string' || raw.token.length === 0) return null;\n return {\n token: raw.token,\n expiresAt: typeof raw.expiresAt === 'number' ? raw.expiresAt : undefined,\n refreshToken: typeof raw.refreshToken === 'string' ? raw.refreshToken : undefined,\n };\n}\n\n/** Read the CLI's currently-selected team (`config.json` `currentTeam`). */\nexport function readCliCurrentTeam(): string | null {\n const raw = readJson(cliStorePaths().configPath) as Record<string, unknown> | null;\n return raw && typeof raw.currentTeam === 'string' && raw.currentTeam.length > 0\n ? raw.currentTeam\n : null;\n}\n\n/**\n * Whether the access token is at/near expiry and should be refreshed before\n * use. A missing `expiresAt` is treated as near-expiry so we always probe a\n * refresh rather than ship a token of unknown age. `skewSec` is the safety\n * window: refresh if the token expires within that many seconds.\n */\nexport function isNearExpiry(auth: VercelCliAuth, skewSec = 120): boolean {\n if (auth.expiresAt === undefined) return true;\n return auth.expiresAt * 1000 < Date.now() + skewSec * 1000;\n}\n","/**\n * Thin loader around `@vercel/sandbox`. Resolves the auth credentials once and\n * threads them into every SDK call.\n *\n * Three auth modes (in precedence order):\n * - OIDC: `VERCEL_OIDC_TOKEN` in env → decode the JWT for owner/project and\n * pass `{ token, teamId, projectId }` explicitly.\n * - CLI-login: `VERCEL_AUTH_SOURCE=cli` → read the live OAuth access token\n * from the Vercel CLI's own store (`auth.json`) and the cached team/project\n * ids from `secrets.env`. The token is never copied to `secrets.env`; the\n * CLI store is the self-refreshing source of truth. `ensureFreshCredentials`\n * refreshes it (via the `sbx` CLI) before use when it's near expiry.\n * - Access token: `VERCEL_TOKEN` + `VERCEL_TEAM_ID` + `VERCEL_PROJECT_ID` →\n * passed explicitly as `{ token, teamId, projectId }` on each call, since\n * the SDK does NOT read those from env automatically.\n */\n\nimport { ensureVercelEnvLoaded } from './env-loader.js';\nimport { isNearExpiry, readCliAuth, readCliCurrentTeam } from './cli-store.js';\nimport { detectSbx, refreshSbxToken } from './sbx-cli.js';\n\nexport interface VercelCredentials {\n token: string;\n teamId: string;\n projectId: string;\n}\n\n/**\n * Resolve the credentials to thread into SDK calls. Throws when nothing is\n * configured (or an OIDC token has expired) so callers get a clear, actionable\n * error instead of an opaque SDK auth failure.\n *\n * For OIDC we do NOT return `{}` and let the SDK read the env var: the SDK's\n * env-OIDC path (`@vercel/oidc`) tries to *refresh* the token via the Vercel\n * CLI's `.vercel/project.json` + cached auth, which an agentbox box doesn't\n * have, so it fails with \"Could not get credentials from OIDC context\". Instead\n * we decode the OIDC JWT — which embeds `owner_id` (teamId) and `project_id` —\n * and pass `{ token, teamId, projectId }` explicitly, which uses the SDK's\n * direct-credentials path (the OIDC token is itself a valid API bearer).\n */\nexport function resolveCredentials(): VercelCredentials {\n ensureVercelEnvLoaded();\n const oidc = process.env.VERCEL_OIDC_TOKEN;\n if (oidc) {\n const claims = decodeOidcClaims(oidc);\n if (!claims) {\n throw new Error(\n 'VERCEL_OIDC_TOKEN is set but could not be decoded (not a valid Vercel OIDC JWT). ' +\n 'Re-run `vercel env pull`, or use the VERCEL_TOKEN + VERCEL_TEAM_ID + VERCEL_PROJECT_ID trio.',\n );\n }\n if (claims.exp !== undefined && claims.exp * 1000 < Date.now()) {\n throw new Error(\n 'VERCEL_OIDC_TOKEN has expired (Vercel dev OIDC tokens last ~12h). ' +\n 'Re-run `vercel env pull` to refresh it, then retry.',\n );\n }\n return { token: oidc, teamId: claims.teamId, projectId: claims.projectId };\n }\n if (process.env.VERCEL_AUTH_SOURCE === 'cli') {\n const auth = readCliAuth();\n if (!auth) {\n throw new Error(\n 'Vercel CLI session not found — run `agentbox vercel login` (or `sbx login`) to sign in again.',\n );\n }\n const teamId = process.env.VERCEL_TEAM_ID ?? readCliCurrentTeam() ?? undefined;\n const projectId = process.env.VERCEL_PROJECT_ID;\n if (!teamId || !projectId) {\n throw new Error(\n 'Vercel CLI auth is missing the team/project id — re-run `agentbox vercel login`.',\n );\n }\n // Live token straight from the CLI store; nothing cached on disk.\n return { token: auth.token, teamId, projectId };\n }\n const token = process.env.VERCEL_TOKEN;\n const teamId = process.env.VERCEL_TEAM_ID;\n const projectId = process.env.VERCEL_PROJECT_ID;\n if (token && teamId && projectId) return { token, teamId, projectId };\n throw new Error(\n 'Vercel credentials not configured.\\n' +\n 'Either run `vercel link && vercel env pull` to get a VERCEL_OIDC_TOKEN, ' +\n 'or set VERCEL_TOKEN + VERCEL_TEAM_ID + VERCEL_PROJECT_ID ' +\n '(see `agentbox vercel login`).',\n );\n}\n\ninterface OidcClaims {\n teamId: string;\n projectId: string;\n exp?: number;\n}\n\n/** Decode the `owner_id`/`project_id`/`exp` claims from a Vercel OIDC JWT. */\nfunction decodeOidcClaims(token: string): OidcClaims | null {\n const parts = token.split('.');\n if (parts.length < 2 || !parts[1]) return null;\n try {\n const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8')) as {\n owner_id?: unknown;\n project_id?: unknown;\n exp?: unknown;\n };\n if (typeof payload.owner_id !== 'string' || typeof payload.project_id !== 'string') return null;\n return {\n teamId: payload.owner_id,\n projectId: payload.project_id,\n exp: typeof payload.exp === 'number' ? payload.exp : undefined,\n };\n } catch {\n return null;\n }\n}\n\n/** True when any auth mode is configured. Used by the credential gate. */\nexport function hasUsableCredentials(): boolean {\n ensureVercelEnvLoaded();\n if (process.env.VERCEL_OIDC_TOKEN) return true;\n if (\n process.env.VERCEL_AUTH_SOURCE === 'cli' &&\n process.env.VERCEL_TEAM_ID &&\n process.env.VERCEL_PROJECT_ID &&\n readCliAuth()\n ) {\n return true;\n }\n return Boolean(\n process.env.VERCEL_TOKEN && process.env.VERCEL_TEAM_ID && process.env.VERCEL_PROJECT_ID,\n );\n}\n\n/**\n * Refresh the CLI-login access token before use when it's near expiry. No-op\n * for the OIDC and access-token modes (nothing to refresh). For CLI mode: if the\n * live token in the CLI store is within the safety window, run a cheap `sbx`\n * read command, which makes the CLI rotate its own token from the stored refresh\n * token, then re-read `secrets.env`. Throws an actionable error when the CLI is\n * gone or the refresh fails (e.g. the refresh token itself expired).\n *\n * Call this once at the top of each backend operation, BEFORE the (sync)\n * `resolveCredentials()` reads the token. An in-process single-flight collapses\n * concurrent ops onto one refresh; cross-process races are harmless (the CLI\n * writes its store atomically and a fresh-token refresh is a no-op).\n */\nlet inflightRefresh: Promise<void> | null = null;\n\nexport function ensureFreshCredentials(): Promise<void> {\n ensureVercelEnvLoaded();\n if (process.env.VERCEL_AUTH_SOURCE !== 'cli') return Promise.resolve();\n if (inflightRefresh) return inflightRefresh;\n inflightRefresh = refreshCliToken().finally(() => {\n inflightRefresh = null;\n });\n return inflightRefresh;\n}\n\nasync function refreshCliToken(): Promise<void> {\n const auth = readCliAuth();\n if (!auth) return; // resolveCredentials() will throw the clear \"logged out\" error\n if (!isNearExpiry(auth)) return; // still valid — no work\n\n const det = await detectSbx();\n if (!det.installed || !det.bin) {\n throw new Error(\n 'Vercel access token is near expiry and the `sandbox` CLI is no longer installed — ' +\n 'reinstall it (`npm install -g sandbox`) or run `agentbox vercel login`.',\n );\n }\n const ok = await refreshSbxToken(det.bin);\n if (!ok) {\n throw new Error(\n 'Vercel token refresh failed — run `agentbox vercel login` (the refresh token may have expired).',\n );\n }\n // The token lives in the CLI store, not secrets.env — refreshSbxToken rotated\n // auth.json in place, so the next readCliAuth() returns the fresh token.\n const fresh = readCliAuth();\n if (!fresh || isNearExpiry(fresh, 0)) {\n throw new Error(\n 'Vercel token is still stale after a refresh attempt — run `agentbox vercel login`.',\n );\n }\n}\n\n// Re-export the SDK surface we use so the rest of the package imports from one\n// place (and tests can mock `./sdk.js` instead of the package).\nexport { Sandbox, Snapshot } from '@vercel/sandbox';\nexport type { Sandbox as SandboxType } from '@vercel/sandbox';\n","/**\n * Minimal Vercel REST helpers used by the CLI-login auth flow. The Sandbox SDK\n * requires a projectId on every call, but the OAuth access token harvested from\n * the CLI is team-scoped with no project — so after login we list the team's\n * projects (and optionally create one) to resolve a project to scope sandboxes\n * to. Plain `fetch`; no SDK. Each call takes the harvested `(token, teamId)`.\n */\n\nconst API = 'https://api.vercel.com';\n\nexport interface VercelProject {\n id: string;\n name: string;\n}\n\nclass VercelApiError extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = 'VercelApiError';\n }\n}\n\nasync function api(\n token: string,\n path: string,\n init?: { method?: string; body?: unknown },\n): Promise<unknown> {\n const res = await fetch(`${API}${path}`, {\n method: init?.method ?? 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n ...(init?.body ? { 'Content-Type': 'application/json' } : {}),\n },\n body: init?.body ? JSON.stringify(init.body) : undefined,\n });\n const text = await res.text();\n let json: unknown = null;\n try {\n json = text ? JSON.parse(text) : null;\n } catch {\n json = null;\n }\n if (!res.ok) {\n const detail =\n (json as { error?: { message?: string } } | null)?.error?.message ??\n text ??\n res.statusText;\n throw new VercelApiError(res.status, `Vercel API ${res.status}: ${detail}`);\n }\n return json;\n}\n\n/** Validate the token and return the authenticated user (probe / status). */\nexport async function getUser(token: string): Promise<{ id: string; username?: string }> {\n const json = (await api(token, '/v2/user')) as {\n user?: { id: string; username?: string };\n };\n const user = json.user;\n if (!user?.id) throw new Error('Vercel /v2/user returned no user');\n return user;\n}\n\n/** List the team's projects (id + name). Paginates up to `limit` (default 100). */\nexport async function listProjects(\n token: string,\n teamId: string,\n limit = 100,\n): Promise<VercelProject[]> {\n const json = (await api(\n token,\n `/v9/projects?teamId=${encodeURIComponent(teamId)}&limit=${limit}`,\n )) as { projects?: Array<{ id: string; name: string }> };\n return (json.projects ?? []).map((p) => ({ id: p.id, name: p.name }));\n}\n\n/**\n * Create a project under the team. On a 409 (name already taken) re-list and\n * return the existing project of that name, so the caller treats create as\n * idempotent.\n */\nexport async function createProject(\n token: string,\n teamId: string,\n name: string,\n): Promise<VercelProject> {\n try {\n const json = (await api(token, `/v9/projects?teamId=${encodeURIComponent(teamId)}`, {\n method: 'POST',\n body: { name },\n })) as { id: string; name: string };\n return { id: json.id, name: json.name };\n } catch (err) {\n if (err instanceof VercelApiError && err.status === 409) {\n const existing = (await listProjects(token, teamId)).find((p) => p.name === name);\n if (existing) return existing;\n }\n throw err;\n }\n}\n"],"mappings":"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","names":["existsSync","readFileSync","homedir","resolve","teamId","projectId","text","spawnSync"]}