@madarco/agentbox 0.12.0 → 0.14.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.
- package/CHANGELOG.md +96 -0
- package/README.md +21 -7
- package/dist/{_cloud-attach-XKO4SHR3.js → _cloud-attach-GUBB5RH2.js} +4 -4
- package/dist/{chunk-R5XIDQFR.js → chunk-BKU34KYY.js} +170 -6
- package/dist/chunk-BKU34KYY.js.map +1 -0
- package/dist/{chunk-HFV6THYG.js → chunk-BYCLD6D6.js} +308 -36
- package/dist/chunk-BYCLD6D6.js.map +1 -0
- package/dist/chunk-LDMYHWUS.js +346 -0
- package/dist/chunk-LDMYHWUS.js.map +1 -0
- package/dist/{chunk-2LF5YILI.js → chunk-RSKG7AFU.js} +80 -6
- package/dist/chunk-RSKG7AFU.js.map +1 -0
- package/dist/{chunk-DHJ7OMIP.js → chunk-TBSIJVSN.js} +149 -47
- package/dist/chunk-TBSIJVSN.js.map +1 -0
- package/dist/{chunk-IZXPJPPV.js → chunk-TCS5HXJX.js} +389 -176
- package/dist/chunk-TCS5HXJX.js.map +1 -0
- package/dist/{chunk-ECLLV5JH.js → chunk-VATTS2MR.js} +156 -5
- package/dist/chunk-VATTS2MR.js.map +1 -0
- package/dist/{chunk-SNTHHWKY.js → chunk-XKH7NTT7.js} +80 -22
- package/dist/chunk-XKH7NTT7.js.map +1 -0
- package/dist/dist-34RKQ74M.js +662 -0
- package/dist/dist-34RKQ74M.js.map +1 -0
- package/dist/{dist-47LVLYUV.js → dist-3IMQNTTV.js} +14 -69
- package/dist/dist-3IMQNTTV.js.map +1 -0
- package/dist/{dist-RZZSSUNB.js → dist-4DPOL5A7.js} +5 -3
- package/dist/{dist-24PY2ZMO.js → dist-57M6ZA7H.js} +25 -177
- package/dist/dist-57M6ZA7H.js.map +1 -0
- package/dist/{dist-SWUOU34W.js → dist-J2IHD5T7.js} +37 -226
- package/dist/dist-J2IHD5T7.js.map +1 -0
- package/dist/index.js +1524 -921
- package/dist/index.js.map +1 -1
- package/dist/{prepared-state-MQHD3M5F-KE4DT3GX.js → prepared-state-MQHD3M5F-Q27AZU53.js} +2 -2
- package/package.json +9 -7
- package/runtime/docker/Dockerfile.box +21 -26
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +37 -1
- package/runtime/docker/packages/ctl/dist/bin.cjs +46 -17
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +17 -6
- package/runtime/docker/packages/sandbox-docker/scripts/chromium-resolver +57 -0
- package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +2 -1
- package/runtime/e2b/agentbox-checkpoint-cleanup +52 -0
- package/runtime/e2b/agentbox-codex-hooks.json +68 -0
- package/runtime/e2b/agentbox-open +28 -0
- package/runtime/e2b/agentbox-setup-skill.md +233 -0
- package/runtime/e2b/agentbox-vnc-start +102 -0
- package/runtime/e2b/attach-helper.cjs +167 -0
- package/runtime/e2b/claude-managed-settings.json +116 -0
- package/runtime/e2b/ctl.cjs +23864 -0
- package/runtime/e2b/custom-system-CLAUDE.md +46 -0
- package/runtime/e2b/gh-shim +344 -0
- package/runtime/e2b/git-shim +131 -0
- package/runtime/e2b/scripts/build-template.sh +295 -0
- package/runtime/hetzner/agentbox-setup-skill.md +37 -1
- package/runtime/hetzner/agentbox-vnc-start +17 -6
- package/runtime/hetzner/claude-managed-settings.json +2 -1
- package/runtime/hetzner/ctl.cjs +46 -17
- package/runtime/relay/bin.cjs +305 -230
- package/runtime/vercel/agentbox-setup-skill.md +37 -1
- package/runtime/vercel/agentbox-vnc-start +17 -6
- package/runtime/vercel/claude-managed-settings.json +2 -1
- package/runtime/vercel/ctl.cjs +46 -17
- package/share/agentbox-setup/SKILL.md +37 -1
- package/share/host-skills/agentbox-info/SKILL.md +26 -34
- package/dist/chunk-2LF5YILI.js.map +0 -1
- package/dist/chunk-DHJ7OMIP.js.map +0 -1
- package/dist/chunk-ECLLV5JH.js.map +0 -1
- package/dist/chunk-HFV6THYG.js.map +0 -1
- package/dist/chunk-IZXPJPPV.js.map +0 -1
- package/dist/chunk-R5XIDQFR.js.map +0 -1
- package/dist/chunk-SNTHHWKY.js.map +0 -1
- package/dist/dist-24PY2ZMO.js.map +0 -1
- package/dist/dist-47LVLYUV.js.map +0 -1
- package/dist/dist-SWUOU34W.js.map +0 -1
- /package/dist/{_cloud-attach-XKO4SHR3.js.map → _cloud-attach-GUBB5RH2.js.map} +0 -0
- /package/dist/{dist-RZZSSUNB.js.map → dist-4DPOL5A7.js.map} +0 -0
- /package/dist/{prepared-state-MQHD3M5F-KE4DT3GX.js.map → prepared-state-MQHD3M5F-Q27AZU53.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 { hostOpenCommand } from '@agentbox/sandbox-core';\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(hostOpenCommand(), [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":";;;;;;AAAA,SAAS,YAAY,oBAAoB;AACzC,SAAS,eAAe;AACxB,SAAS,eAAe;AEFxB,SAAS,iBAAiB;AAE1B;EACE;EACA,cAAAA;EACA;EACA,gBAAAC;EACA;EACA;OACK;AACP,SAAS,WAAAC,gBAAe;AACxB,SAAS,SAAS,WAAAC,gBAAe;AACjC,SAAS,SAAS,UAAU,OAAO,KAAK,MAAM,OAAO,UAAU,eAAe;AFU9E,IAAM,eAAe,CAAC,gBAAgB,iBAAiB;AAEvD,IAAI,SAAS;AAEN,SAAS,yBAA+B;AAC7C,MAAI,OAAQ;AACZ,WAAS;AACT,wBAAsB,QAAQ,QAAQ,GAAG,aAAa,aAAa,CAAC;AACtE;AAEA,SAAS,sBAAsB,MAAoB;AACjD,MAAI,CAAC,WAAW,IAAI,EAAG;AACvB,MAAI;AACJ,MAAI;AACF,WAAO,aAAa,MAAM,MAAM;EAClC,QAAQ;AACN;EACF;AACA,QAAM,SAAS,aAAa,IAAI;AAChC,aAAW,OAAO,cAAc;AAC9B,QAAI,QAAQ,IAAI,GAAG,MAAM,OAAW;AACpC,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,OAAO,UAAU,UAAU;AAC7B,cAAQ,IAAI,GAAG,IAAI;IACrB;EACF;AACF;AASO,SAAS,aAAa,MAAsC;AACjE,QAAM,MAA8B,CAAC;AACrC,aAAW,WAAW,KAAK,MAAM,OAAO,GAAG;AACzC,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,KAAK,WAAW,KAAK,KAAK,WAAW,GAAG,EAAG;AAC/C,UAAM,WAAW,KAAK,WAAW,SAAS,IAAI,KAAK,MAAM,UAAU,MAAM,IAAI;AAC7E,UAAM,KAAK,SAAS,QAAQ,GAAG;AAC/B,QAAI,MAAM,EAAG;AACb,UAAM,MAAM,SAAS,MAAM,GAAG,EAAE,EAAE,KAAK;AACvC,QAAI,QAAQ,SAAS,MAAM,KAAK,CAAC,EAAE,KAAK;AACxC,QACE,MAAM,UAAU,MACd,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,KAC1C,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,IAC9C;AACA,cAAQ,MAAM,MAAM,GAAG,EAAE;IAC3B;AACA,QAAI,GAAG,IAAI;EACb;AACA,SAAO;AACT;ACxDO,IAAM,0BAA0B;AA6GhC,IAAM,kBAAN,cAA8B,MAAM;EAChC;EACA;EACA;EACT,YAAY,YAAoB,MAAc,SAAiB,SAAmB;AAChF,UAAM,WAAW,OAAO,UAAU,CAAC,IAAI,IAAI,KAAK,OAAO,EAAE;AACzD,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,OAAO;AACZ,SAAK,UAAU;EACjB;AACF;AAsEO,SAAS,kBAAkB,OAA0B,CAAC,GAAkB;AAC7E,yBAAuB;AACvB,QAAM,WAAW,KAAK,SAAS,QAAQ,IAAI;AAC3C,MAAI,CAAC,YAAY,SAAS,KAAK,EAAE,WAAW,GAAG;AAC7C,UAAM,IAAI;MACR;IAEF;EACF;AAGA,QAAM,QAAgB,SAAS,KAAK;AACpC,QAAM,YAAY,KAAK,YAAY,QAAQ,IAAI,mBAAmB,yBAAyB,QAAQ,OAAO,EAAE;AAC5G,QAAM,YAAY,KAAK,aAAa;AAEpC,iBAAe,IACb,QACA,MACA,MACmB;AACnB,UAAM,MAAM,GAAG,QAAQ,GAAG,IAAI;AAC9B,UAAM,OAAoB;MACxB;MACA,SAAS;QACP,eAAe,UAAU,KAAK;QAC9B,GAAI,SAAS,SAAY,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;MACrE;MACA,GAAI,SAAS,SAAY,EAAE,MAAM,KAAK,UAAU,IAAI,EAAE,IAAI,CAAC;IAC7D;AACA,UAAM,MAAM,MAAM,UAAU,KAAK,IAAI;AACrC,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,QAAI,CAAC,IAAI,IAAI;AACX,UAAI,SAA6E,CAAC;AAClF,UAAI;AACF,iBAAU,MAAM,IAAI,KAAK;MAC3B,QAAQ;MAER;AACA,YAAM,OAAO,OAAO,OAAO,QAAQ,QAAQ,OAAO,IAAI,MAAM,CAAC;AAC7D,YAAM,MAAM,OAAO,OAAO,WAAW,IAAI,cAAc;AACvD,YAAM,IAAI,gBAAgB,IAAI,QAAQ,MAAM,KAAK,OAAO,OAAO,OAAO;IACxE;AACA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,WAAO,KAAK,MAAM,IAAI;EACxB;AAEA,iBAAe,UACb,QACA,MACA,MACY;AACZ,UAAM,MAAM,MAAM,IAAO,QAAQ,MAAM,IAAI;AAC3C,QAAI,QAAQ,MAAM;AAChB,YAAM,IAAI,gBAAgB,GAAG,kBAAkB,wBAAwB,MAAM,IAAI,IAAI,EAAE;IACzF;AACA,WAAO;EACT;AAEA,SAAO;IACL,MAAM,UAAU,IAAI;AAClB,YAAM,IAAI,MAAM,IAA+B,OAAO,YAAY,OAAO,EAAE,CAAC,EAAE;AAC9E,aAAO,GAAG,UAAU;IACtB;IACA,MAAM,aAAa,SAAS;AAC1B,YAAM,IAAI,MAAM;QACd;QACA;QACA;MACF;AACA,aAAO,EAAE,QAAQ,EAAE,QAAQ,QAAQ,EAAE,OAAO;IAC9C;IACA,MAAM,YAAYC,OAAM;AACtB,YAAM,SAAS,IAAI,gBAAgB;AACnC,UAAIA,OAAM,eAAgB,QAAO,IAAI,kBAAkBA,MAAK,cAAc;AAC1E,aAAO,IAAI,YAAY,IAAI;AAC3B,YAAM,MAAuB,CAAC;AAC9B,UAAI,UAAU;AACd,aAAO,MAAM;AACX,eAAO,IAAI,QAAQ,OAAO,OAAO,CAAC;AAClC,cAAM,IAAI,MAAM,UAGb,OAAO,YAAY,OAAO,SAAS,CAAC,EAAE;AACzC,YAAI,KAAK,GAAG,EAAE,OAAO;AACrB,cAAM,OAAO,EAAE,MAAM,YAAY;AACjC,YAAI,OAAO,SAAS,SAAU;AAC9B,kBAAU;MACZ;AACA,aAAO;IACT;IACA,MAAM,aAAa,IAAI;AACrB,YAAM,IAAI,MAAM,IAA+B,UAAU,YAAY,OAAO,EAAE,CAAC,EAAE;AACjF,aAAO,GAAG,UAAU;IACtB;IACA,MAAM,QAAQ,IAAI;AAChB,YAAM,IAAI,MAAM;QACd;QACA,YAAY,OAAO,EAAE,CAAC;MACxB;AACA,aAAO,EAAE;IACX;IACA,MAAM,SAAS,IAAI;AACjB,YAAM,IAAI,MAAM;QACd;QACA,YAAY,OAAO,EAAE,CAAC;MACxB;AACA,aAAO,EAAE;IACX;IACA,MAAM,SAAS,IAAI;AACjB,YAAM,IAAI,MAAM;QACd;QACA,YAAY,OAAO,EAAE,CAAC;MACxB;AACA,aAAO,EAAE;IACX;IACA,MAAM,YAAY,IAAI,MAAM;AAC1B,YAAM,IAAI,MAAM;QACd;QACA,YAAY,OAAO,EAAE,CAAC;QACtB;MACF;AACA,aAAO,EAAE,OAAO,EAAE,OAAO,QAAQ,EAAE,OAAO;IAC5C;IACA,MAAM,SAAS,IAAI;AACjB,YAAM,IAAI,MAAM,IAA6B,OAAO,WAAW,OAAO,EAAE,CAAC,EAAE;AAC3E,aAAO,GAAG,SAAS;IACrB;IACA,MAAM,WAAWA,OAAM;AACrB,YAAM,SAAS,IAAI,gBAAgB;AACnC,UAAIA,OAAM,KAAM,QAAO,IAAI,QAAQA,MAAK,IAAI;AAC5C,UAAIA,OAAM,eAAgB,QAAO,IAAI,kBAAkBA,MAAK,cAAc;AAC1E,UAAIA,OAAM,KAAM,QAAO,IAAI,QAAQA,MAAK,IAAI;AAC5C,aAAO,IAAI,YAAY,IAAI;AAC3B,YAAM,MAAsB,CAAC;AAC7B,UAAI,UAAU;AACd,aAAO,MAAM;AACX,eAAO,IAAI,QAAQ,OAAO,OAAO,CAAC;AAClC,cAAM,IAAI,MAAM,UAGb,OAAO,WAAW,OAAO,SAAS,CAAC,EAAE;AACxC,YAAI,KAAK,GAAG,EAAE,MAAM;AACpB,cAAM,OAAO,EAAE,MAAM,YAAY;AACjC,YAAI,OAAO,SAAS,SAAU;AAC9B,kBAAU;MACZ;AACA,aAAO;IACT;IACA,MAAM,YAAY,IAAI;AACpB,YAAM,IAAa,UAAU,WAAW,OAAO,EAAE,CAAC,EAAE;IACtD;IACA,MAAM,eAAe,SAAS;AAC5B,YAAM,IAAI,MAAM,UAAyC,QAAQ,cAAc,OAAO;AACtF,aAAO,EAAE;IACX;IACA,MAAM,iBAAiB,IAAI,OAAO;AAChC,YAAM,IAAI,MAAM;QACd;QACA,cAAc,OAAO,EAAE,CAAC;QACxB,EAAE,MAAM;MACV;AACA,aAAO,EAAE;IACX;IACA,MAAM,YAAY,IAAI;AACpB,YAAM,IAAI,MAAM,IAAmC,OAAO,cAAc,OAAO,EAAE,CAAC,EAAE;AACpF,aAAO,GAAG,YAAY;IACxB;IACA,MAAM,eAAe,IAAI;AACvB,YAAM,IAAa,UAAU,cAAc,OAAO,EAAE,CAAC,EAAE;IACzD;IACA,MAAM,gBAAgB;AACpB,YAAM,IAAI,MAAM,UAEb,OAAO,YAAY;AACtB,aAAO,EAAE;IACX;EACF;AACF;ACtXA,IAAM,qBAAqB;AAQ3B,IAAM,eAAe,CAAC,gBAAgB,iBAAiB;AAoBvD,eAAsB,yBACpB,OAAwC,CAAC,GAC1B;AACf,yBAAuB;AAEvB,MAAI,CAAC,KAAK,SAAS,qBAAqB,EAAG;AAC3C,MAAI,CAAC,QAAQ,MAAM,MAAO;AAE1B,QAAM,qBAAqB;AAC3B;IACE;;UACa,kBAAkB;;;IAG/B;EACF;AAEA,QAAM,OAAO,MAAM,QAAQ;IACzB,SAAS,QAAQ,kBAAkB;IACnC,cAAc;EAChB,CAAC;AACD,MAAI,SAAS,IAAI,GAAG;AAClB,QAAI,KAAK,4EAAuE;AAChF;EACF;AACA,MAAI,KAAM,eAAc;AAGxB,WAAS,UAAU,GAAG,UAAU,GAAG,WAAW;AAC5C,UAAM,QAAQ,MAAM,qBAAqB;AACzC,QAAI,UAAU,KAAM;AAEpB,UAAM,SAAS,MAAM,oBAAoB,KAAK;AAC9C,QAAI,OAAO,IAAI;AACb,yBAAmB,KAAK;AACxB,UAAI,QAAQ,gCAAgC,YAAY,CAAC,EAAE;AAC3D,YAAM,iBAAiB;AACvB;IACF;AACA,QAAI,OAAO,SAAS,UAAU,YAAY,GAAG;AAC3C,UAAI,MAAM,uCAAuC,OAAO,OAAO,EAAE;AACjE,UAAI,KAAK,uCAAuC;AAChD;IACF;AACA,QAAI,OAAO,SAAS,WAAW;AAC7B,UAAI,KAAK,wCAAwC,OAAO,OAAO,yBAAoB;AACnF,yBAAmB,KAAK;AACxB,UAAI,QAAQ,gCAAgC,YAAY,CAAC,EAAE;AAC3D,YAAM,+BAA+B;AACrC;IACF;AACA,UAAM,IAAI,MAAM,iCAAiC,OAAO,OAAO,EAAE;EACnE;AACF;AAEA,SAAS,uBAAgC;AACvC,SAAO,OAAO,QAAQ,IAAI,iBAAiB,YAAY,QAAQ,IAAI,aAAa,SAAS;AAC3F;AAOA,eAAe,uBAAoD;AACjE,QAAM,QAAQ,MAAM,SAAS;IAC3B,SAAS;IACT,SAAS,GAAG;AACV,UAAI,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAG,QAAO;AACxC,aAAO;IACT;EACF,CAAC;AACD,MAAI,SAAS,KAAK,GAAG;AACnB,QAAI,KAAK,0BAA0B;AACnC,WAAO;EACT;AACA,SAAO,EAAE,OAAO,MAAM,KAAK,EAAE;AAC/B;AAOA,eAAe,oBAAoB,OAA+C;AAChF,QAAM,IAAI,QAAQ;AAClB,IAAE,MAAM,qCAAqC;AAE7C,MAAI;AACF,UAAM,SAAS,kBAAkB,EAAE,OAAO,MAAM,OAAO,UAAU,MAAM,SAAS,CAAC;AAGjF,UAAM,OAAO,cAAc;AAC3B,MAAE,KAAK,8BAA8B;AACrC,WAAO,EAAE,IAAI,KAAK;EACpB,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,MAAE,KAAK,kCAAkC;AACzC,QAAI,4CAA4C,KAAK,OAAO,GAAG;AAC7D,aAAO,EAAE,IAAI,OAAO,MAAM,QAAQ,QAAQ;IAC5C;AACA,WAAO,EAAE,IAAI,OAAO,MAAM,WAAW,QAAQ;EAC/C;AACF;AAEA,SAAS,mBAAmB,OAA0B;AACpD,UAAQ,IAAI,eAAe,MAAM;AACjC,MAAI,MAAM,SAAU,SAAQ,IAAI,kBAAkB,MAAM;AACxD,QAAM,OAAO,YAAY;AACzB,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAE5C,MAAI,WAAW;AACf,MAAIJ,YAAW,IAAI,GAAG;AACpB,QAAI;AACF,iBAAWC,cAAa,MAAM,MAAM;IACtC,QAAQ;AACN,iBAAW;IACb;EACF;AAEA,QAAM,OAAO,SACV,MAAM,OAAO,EACb,OAAO,CAAC,SAAS;AAChB,UAAM,WAAW,KAAK,WAAW,SAAS,IAAI,KAAK,MAAM,UAAU,MAAM,IAAI;AAC7E,UAAM,KAAK,SAAS,QAAQ,GAAG;AAC/B,QAAI,MAAM,EAAG,QAAO;AACpB,UAAM,MAAM,SAAS,MAAM,GAAG,EAAE,EAAE,KAAK;AACvC,WAAO,CAAE,aAAmC,SAAS,GAAG;EAC1D,CAAC,EACA,KAAK,IAAI,EACT,QAAQ,SAAS,EAAE;AAEtB,QAAM,QAAkB,CAAC,gBAAgB,MAAM,KAAK,EAAE;AACtD,MAAI,MAAM,SAAU,OAAM,KAAK,mBAAmB,MAAM,QAAQ,EAAE;AAElE,QAAM,QAAQ,OAAO,GAAG,IAAI;IAAO,MAAM,MAAM,KAAK,IAAI,IAAI;AAE5D,QAAM,MAAM,GAAG,IAAI;AACnB,gBAAc,KAAK,MAAM,EAAE,MAAM,IAAM,CAAC;AACxC,MAAI;AACF,cAAU,KAAK,GAAK;EACtB,QAAQ;EAER;AACA,aAAW,KAAK,IAAI;AACpB,MAAI;AACF,cAAU,MAAM,GAAK;EACvB,QAAQ;EAER;AACF;AAEA,SAAS,gBAAsB;AAC7B,MAAI;AACF,UAAM,IAAI,UAAU,gBAAgB,GAAG,CAAC,kBAAkB,GAAG,EAAE,OAAO,SAAS,CAAC;AAChF,QAAI,EAAE,WAAW,GAAG;AAClB,UAAI,KAAK,gDAA2C,kBAAkB,YAAY;IACpF;EACF,QAAQ;AACN,QAAI,KAAK,gDAA2C,kBAAkB,YAAY;EACpF;AACF;AAEO,SAAS,cAAsB;AACpC,SAAOE,SAAQD,SAAQ,GAAG,aAAa,aAAa;AACtD;AAQO,SAAS,wBAA2C;AACzD,QAAM,gBAAgB,CAAC,CAAC,QAAQ,IAAI;AACpC,yBAAuB;AACvB,QAAM,QAAQ,QAAQ,IAAI;AAC1B,QAAM,WAAW,QAAQ,IAAI;AAC7B,MAAI,CAAC,MAAO,QAAO,EAAE,QAAQ,OAAO;AACpC,SAAO;IACL;IACA;IACA,QAAQ,gBAAgB,QAAQ;EAClC;AACF;AAEO,SAAS,QAAQ,OAAuB;AAC7C,MAAI,MAAM,UAAU,EAAG,QAAO,IAAI,OAAO,MAAM,MAAM;AACrD,SAAO,GAAG,MAAM,MAAM,GAAG,CAAC,CAAC,SAAI,IAAI,OAAO,CAAC,CAAC,GAAG,MAAM,MAAM,EAAE,CAAC;AAChE;AC9NA,IAAM,SAAS;EACb;EACA;EACA;AACF;AAEA,IAAM,aAAa;AAEnB,IAAM,UAAU;AAChB,IAAM,UAAU;AAqBhB,eAAsB,eAAe,OAA8B,CAAC,GAAoB;AACtF,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,UAAU,KAAK,aAAa;AAClC,QAAM,YAAY,KAAK,aAAa;AACpC,QAAM,SAAmB,CAAC;AAE1B,aAAW,OAAO,QAAQ;AACxB,QAAI;AACF,YAAM,KAAK,MAAM,YAAY,MAAM,KAAK,SAAS,GAAG,OAAO;AAC3D,UAAI,IAAI;AACN,aAAK,QAAQ,uBAAuB,EAAE,QAAQ,GAAG,EAAE;AACnD,eAAO;MACT;AACA,aAAO,KAAK,GAAG,GAAG,0BAA0B;IAC9C,SAAS,KAAK;AACZ,aAAO,KAAK,GAAG,GAAG,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;IAC3E;EACF;AAEA,QAAM,IAAI;IACR,yDAAoD,OAAO,OAAO,MAAM,CAAC;IACvE,OAAO,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI,IACvC;;EACJ;AACF;AAEA,eAAe,MAAM,KAAa,WAAiD;AACjF,QAAM,MAAM,MAAM,UAAU,KAAK,EAAE,QAAQ,MAAM,CAAC;AAClD,MAAI,CAAC,IAAI,GAAI,QAAO;AACpB,QAAM,QAAQ,MAAM,IAAI,KAAK,GAAG,KAAK;AACrC,MAAI,QAAQ,KAAK,IAAI,GAAG;AAEtB,UAAM,QAAQ,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,OAAO,SAAS,GAAG,EAAE,CAAC;AAC/D,QAAI,MAAM,MAAM,CAAC,MAAM,KAAK,KAAK,KAAK,GAAG,EAAG,QAAO;AACnD,WAAO;EACT;AAKA,MAAI,QAAQ,KAAK,IAAI,KAAK,KAAK,SAAS,GAAG,EAAG,QAAO;AACrD,SAAO;AACT;AAEA,eAAe,YAAe,GAAe,IAAwB;AACnE,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK;MACxB;MACA,IAAI,QAAe,CAAC,UAAU,WAAW;AACvC,gBAAQ,WAAW,MAAM,OAAO,IAAI,MAAM,yBAAyB,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;MACzF,CAAC;IACH,CAAC;EACH,UAAA;AACE,QAAI,UAAU,OAAW,cAAa,KAAK;EAC7C;AACF;ACjEA,IAAM,kBAAqC,CAAC,KAAM,KAAM,GAAI;AAC5D,IAAM,6BAA6B;AAEnC,IAAM,sBAAN,cAAkC,MAAM;EACtC,YAAY,QAAgB,IAAY;AACtC,UAAM,WAAW,MAAM,+BAA+B,OAAO,EAAE,CAAC,IAAI;AACpE,SAAK,OAAO;EACd;AACF;AAEO,SAAS,iBAAiB,KAA0C;AACzE,SAAO,eAAe;AACxB;AAOO,SAAS,YAAY,KAAc,gBAAkC;AAC1E,MAAI,eAAe,iBAAiB;AAElC,QAAI,IAAI,eAAe,OAAO,IAAI,SAAS,sBAAuB,QAAO;AAEzE,QAAI,IAAI,cAAc,OAAO,IAAI,cAAc,IAAK,QAAO;AAI3D,QAAI,IAAI,SAAS,YAAY,IAAI,SAAS,WAAY,QAAO;AAE7D,WAAO;EACT;AAEA,MAAI,eAAe,oBAAqB,QAAO;AAI/C,MAAI,OAAO,OAAO,QAAQ,UAAU;AAClC,UAAM,aAAwB,CAAC,KAAM,IAA4B,KAAK;AACtE,eAAW,KAAK,YAAY;AAC1B,UAAI,CAAC,KAAK,OAAO,MAAM,SAAU;AACjC,YAAM,OAAQ,EAAyB;AACvC,UACE,SAAS,gBACT,SAAS,eACT,SAAS,kBACT,SAAS,eACT,SAAS,kBACT,SAAS,eACT,SAAS,oBACT,SAAS,2BACT;AACA,eAAO;MACT;IACF;EACF;AAEA,SAAO;AACT;AAOA,eAAsB,iBACpB,MACA,IACY;AACZ,QAAM,UAAU,KAAK,aAAa;AAClC,QAAM,cAAc,QAAQ,SAAS;AACrC,QAAM,YAAY,KAAK,oBAAoB;AAC3C,QAAMG,OAAM,KAAK,WAAW;AAE5B,WAAS,UAAU,GAAG,WAAW,aAAa,WAAW;AACvD,QAAI;AACF,aAAO,MAAMC,aAAY,GAAG,GAAG,WAAW,KAAK,MAAM;IACvD,SAAS,KAAK;AACZ,YAAM,OAAO,YAAY;AACzB,UAAI,QAAQ,CAAC,YAAY,KAAK,KAAK,gBAAgB,EAAG,OAAM;AAC5D,YAAM,QAAQ,QAAQ,UAAU,CAAC,KAAK,QAAQ,QAAQ,SAAS,CAAC,KAAK;AACrED;QACE,WAAW,KAAK,MAAM,aAAa,OAAO,OAAO,CAAC,YAAY,aAAa,GAAG,CAAC,kBAAkB,OAAO,KAAK,CAAC;MAChH;AACA,YAAM,MAAM,KAAK;IACnB;EACF;AACA,QAAM,IAAI,MAAM,4CAA4C,KAAK,MAAM,EAAE;AAC3E;AAEA,SAAS,gBAAgB,MAAoB;AAC3C,UAAQ,OAAO,MAAM;kBAAqB,IAAI;CAAI;AACpD;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAACF,aAAY,WAAWA,UAAS,EAAE,CAAC;AACzD;AAEA,eAAeG,aAAe,GAAe,IAAY,QAA4B;AACnF,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK;MACxB;MACA,IAAI,QAAe,CAAC,UAAU,WAAW;AACvC,gBAAQ,WAAW,MAAM,OAAO,IAAI,oBAAoB,QAAQ,EAAE,CAAC,GAAG,EAAE;MAC1E,CAAC;IACH,CAAC;EACH,UAAA;AACE,QAAI,UAAU,OAAW,cAAa,KAAK;EAC7C;AACF;AAEA,SAAS,aAAa,KAAsB;AAC1C,MAAI,eAAe,iBAAiB;AAClC,WAAO,mBAAmB,OAAO,IAAI,UAAU,CAAC,IAAI,IAAI,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC;EACxF;AACA,MAAI,eAAe,OAAO;AACxB,UAAM,OAAQ,IAA2B;AACzC,WAAO,SAAS,SACZ,GAAG,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,SAAS,IAAI,OAAO,CAAC,KACtD,GAAG,IAAI,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC;EAC3C;AACA,SAAO,SAAS,OAAO,GAAG,CAAC;AAC7B;AAEA,SAAS,SAAS,GAAW,MAAM,KAAa;AAC9C,SAAO,EAAE,SAAS,MAAM,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,WAAM;AAClD;ACtIO,SAAS,mBAAmB,YAA2C;AAC5E,SAAO;IACL;MACE,WAAW;MACX,UAAU;MACV,MAAM;MACN,YAAY,CAAC,UAAU;MACvB,aAAa;IACf;EACF;AACF;AAgBA,eAAsB,qBACpB,QACA,MAC0B;AAC1B,SAAO;IACL,EAAE,QAAQ,kBAAkB,kBAAkB,OAAO,kBAAkB,IAAO;IAC9E,MACE,OAAO,eAAe;MACpB,MAAM,KAAK;MACX,OAAO,mBAAmB,KAAK,UAAU;MACzC,QAAQ;QACN,oBAAoB;QACpB,iBAAiB;QACjB,GAAG,KAAK;MACV;IACF,CAAC;EACL;AACF;AAWA,eAAsB,mBACpB,QACA,YACA,YACe;AACf,QAAM;IACJ,EAAE,QAAQ,oBAAoB,kBAAkB,MAAM,kBAAkB,IAAO;IAC/E,MAAM,OAAO,iBAAiB,YAAY,mBAAmB,UAAU,CAAC;EAC1E;AACF;AAcA,eAAsB,qBACpB,QACA,YACA,OAAkC,CAAC,GACpB;AACf,QAAM,WAAW,KAAK,IAAI,KAAK,KAAK,gBAAgB;AACpD,MAAI,WAAW;AACf,SAAO,MAAM;AACX,QAAI;AACF,YAAM;QACJ,EAAE,QAAQ,kBAAkB,kBAAkB,MAAM,kBAAkB,IAAO;QAC7E,MAAM,OAAO,eAAe,UAAU;MACxC;AACA;IACF,SAAS,KAAK;AACZ,UAAI,eAAe,oBAAoB,IAAI,eAAe,OAAO,IAAI,SAAS,cAAc;AAC1F;MACF;AACA,YAAM,gBACJ,eAAe,oBACd,IAAI,eAAe,OAClB,IAAI,SAAS,cACb,IAAI,SAAS;AACjB,UAAI,iBAAiB,KAAK,IAAI,IAAI,UAAU;AAC1C,cAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC;AAChD,mBAAW,KAAK,IAAI,WAAW,GAAG,GAAK;AACvC;MACF;AACA,YAAM;IACR;EACF;AACF;AAYO,SAAS,oBAAoB,KAAqB;AACvD,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,QAAQ,SAAS,GAAG,EAAG,QAAO;AAClC,MAAI,QAAQ,SAAS,GAAG,EAAG,QAAO,GAAG,OAAO;AAC5C,SAAO,GAAG,OAAO;AACnB;","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/host-open.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 * The host command that opens a URL or file path in the OS default handler.\n *\n * macOS ships `open`; Linux uses `xdg-open` (from `xdg-utils`, present on any\n * desktop install). We deliberately return only the binary name and let each\n * call site keep its own spawn semantics (sync/async, stdio, detached) — the\n * single platform decision lives here so adding a host platform is a one-line\n * change. Callers already treat a non-zero exit / ENOENT as \"couldn't\n * auto-open\" and print the target, so an absent `xdg-open` degrades cleanly.\n */\nexport function hostOpenCommand(): string {\n return process.platform === 'linux' ? 'xdg-open' : 'open';\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\n/**\n * Public registry repo the box image is published to (see\n * `.github/workflows/box-image.yml`). The CLI pulls a fingerprint-tagged\n * image from here on first use instead of building locally — a multi-minute\n * build collapses to a `docker pull`. An empty registry (config override)\n * disables pulling and always builds.\n */\nexport const BOX_IMAGE_REGISTRY = 'ghcr.io/madarco/agentbox/box';\n\n/**\n * The pull target for a given build-context fingerprint. The tag *is* the\n * content identity: a local staged context that matches a published build\n * has the same sha, so a pull hit can be retagged to `agentbox/box:dev` and\n * stamped into docker-prepared.json without risk of a stale image (a locally\n * edited context has a different sha, its tag 404s, and we build instead).\n */\nexport function registryRefForSha(sha: string, registry: string = BOX_IMAGE_REGISTRY): string {\n return `${registry}:sha-${sha.slice(0, 16)}`;\n}\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\n/**\n * Attempt `docker pull <target>`. Returns true on success, false on any\n * failure (missing tag, offline, auth) — callers fall back to a local build.\n * Never throws. Single attempt: a missing tag is the expected \"build locally\"\n * signal, not a transient error worth retrying.\n */\nexport async function pullImage(\n target: string,\n opts: { onProgress?: (line: string) => void } = {},\n): Promise<boolean> {\n const subprocess = execa('docker', ['pull', target], {\n stderr: 'pipe',\n stdout: 'pipe',\n reject: false,\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 const result = await subprocess;\n return result.exitCode === 0;\n}\n\nexport async function tagImage(source: string, target: string): Promise<void> {\n await execa('docker', ['tag', source, target]);\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 PullOrBuildOptions {\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 staged runtime / monorepo root. */\n contextDir?: string;\n /** Try the registry before building. Defaults to true. */\n allowPull?: boolean;\n /** Registry repo to pull from. Defaults to `BOX_IMAGE_REGISTRY`; empty disables pulling. */\n registry?: string;\n}\n\n/**\n * Make `ref` present locally, preferring a registry pull over a local build.\n *\n * When `fingerprint` is non-null and pulling is allowed, pull the\n * fingerprint-tagged image and retag it to `ref`; on a miss (or when pulling\n * is disabled / unfingerprintable) build from the staged context. Either way,\n * a known fingerprint is stamped into docker-prepared.json so the next\n * `ensureImage()` treats this as a cache hit.\n */\nexport async function pullOrBuild(\n ref: string,\n fingerprint: { contextSha256: string } | null,\n opts: PullOrBuildOptions = {},\n): Promise<{ source: 'pulled' | 'built' }> {\n const { writePreparedDockerState } = await import('./prepared-state.js');\n const registry = opts.registry ?? BOX_IMAGE_REGISTRY;\n const allowPull = opts.allowPull !== false;\n\n if (allowPull && registry && fingerprint) {\n const target = registryRefForSha(fingerprint.contextSha256, registry);\n opts.onProgress?.(`[image] pulling ${target}`);\n if (await pullImage(target, { onProgress: opts.onProgress })) {\n await tagImage(target, ref);\n writePreparedDockerState({ imageRef: ref, contextSha256: fingerprint.contextSha256 });\n opts.onProgress?.(`[image] pulled ${target} -> ${ref}`);\n return { source: 'pulled' };\n }\n opts.onProgress?.(`[image] registry miss, building ${ref} locally`);\n }\n\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 { source: 'built' };\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 /** Try the registry before building. Defaults to true. */\n allowPull?: boolean;\n /** Registry repo to pull from. Defaults to `BOX_IMAGE_REGISTRY`; empty disables pulling. */\n registry?: 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 { computeDockerContextFingerprint, readPreparedDockerState, 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] ${ref}: ${reason}`);\n const { source } = await pullOrBuild(ref, fingerprint, {\n onProgress: opts.onProgress,\n dockerfile: opts.dockerfile,\n contextDir: opts.contextDir,\n allowPull: opts.allowPull,\n registry: opts.registry,\n });\n return { ref, built: source === 'built', reason };\n}\n\n"],"mappings":"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","names":["join","readFile","homedir","dirname","dirname","resolve","fileURLToPath","execa","existsSync","dirname","writePreparedDockerState","computeDockerContextFingerprint","readPreparedDockerState","preparedMatches","here","fileURLToPath","resolve"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../packages/sandbox-vercel/src/index.ts","../../../packages/sandbox-vercel/src/backend.ts","../../../packages/sandbox-vercel/src/retry.ts","../../../packages/sandbox-vercel/src/prepared-state.ts","../../../packages/sandbox-vercel/src/prepare.ts","../../../packages/sandbox-vercel/src/runtime-assets.ts","../../../packages/sandbox-vercel/src/build-attach.ts"],"sourcesContent":["/**\n * The Vercel Sandbox provider. A thin `CloudBackend` over `@vercel/sandbox`,\n * composed via `@agentbox/sandbox-cloud`'s `createCloudProvider` for everything\n * provider-agnostic (workspace seeding, ctl/VNC launch, state, relay polling).\n *\n * Three capabilities are overridden on top of the cloud scaffold:\n * - `prepare` — bake the base snapshot (Vercel can't build from a Dockerfile).\n * - `buildAttach` — SDK-streaming tmux bridge (Vercel has no SSH).\n * - `checkpoint` — store the Vercel snapshot *id* in the manifest so restore\n * boots from it (Vercel snapshots are id-addressed, not name-addressed).\n *\n * `launchDockerd: false` because Vercel Sandbox can't run nested containers.\n */\n\nimport type { BoxRecord, Provider, ProviderCheckpoint } from '@agentbox/core';\nimport {\n createCloudProvider,\n listCloudCheckpoints,\n removeCloudCheckpointDir,\n resolveCloudCheckpoint,\n writeCloudCheckpointManifest,\n} from '@agentbox/sandbox-cloud';\nimport {\n vercelBackend,\n snapshotVercelSandbox,\n deleteVercelSnapshot,\n DEFAULT_BOX_IMAGE_REF,\n} from './backend.js';\nimport { recordBox } from '@agentbox/sandbox-core';\nimport { prepareVercelProvider } from './prepare.js';\nimport { buildVercelAttach } from './build-attach.js';\n\nconst BACKEND_NAME = 'vercel';\n\nconst cloudProvider = createCloudProvider(vercelBackend, {\n // Vercel couples RAM to vCPU at 2048 MB/vCPU; disk is a fixed 32 GB NVMe.\n defaultResources: { cpu: 2, memory: 4, disk: 32 },\n launchDockerd: false,\n});\n\n/**\n * Vercel-specific checkpoint capability. Unlike the scaffold's default (which\n * stores a caller-chosen snapshot *name*), we capture the opaque Vercel\n * snapshot id and store THAT in the manifest's `snapshotName` field — the cloud\n * create flow passes `manifest.snapshotName` straight to\n * `provision({ snapshot })`, and the Vercel backend boots from it as a snapshot\n * id. (The scaffold's `cloudSnapshotName` project-scoping isn't needed — Vercel\n * snapshot ids are already globally unique.)\n */\nconst vercelCheckpoint: ProviderCheckpoint = {\n async create(box: BoxRecord, name: string) {\n if (!box.projectRoot) {\n throw new Error(\n 'cloud checkpoint requires the box to have a project root (run `agentbox checkpoint` from inside the project)',\n );\n }\n if (!box.cloud?.sandboxId) {\n throw new Error(`vercel box ${box.name} has no sandboxId — record is malformed`);\n }\n // NOTE: snapshotting stops the source sandbox; persistent mode resumes it\n // on the next call. Surfaced to the user in `agentbox checkpoint` docs.\n const snapshotId = await snapshotVercelSandbox(box.cloud.sandboxId);\n // The box is now stopped — persist it so the fast `agentbox list` path\n // doesn't show a stale `running` after a checkpoint. Best-effort.\n try {\n await recordBox({ ...box, cloud: { ...box.cloud, lastState: 'paused' } });\n } catch {\n // not worth failing the checkpoint over a state-record write\n }\n const info = await writeCloudCheckpointManifest(box.projectRoot, BACKEND_NAME, name, {\n snapshotName: snapshotId,\n sourceBoxId: box.id,\n sourceBoxName: box.name,\n });\n return { ref: info.name };\n },\n async list(projectRoot: string) {\n const entries = await listCloudCheckpoints(projectRoot, BACKEND_NAME);\n return entries.map((e) => ({ ref: e.name, createdAt: e.manifest.createdAt }));\n },\n async remove(projectRoot: string, ref: string) {\n const entry = await resolveCloudCheckpoint(projectRoot, BACKEND_NAME, ref);\n if (!entry) return;\n try {\n await deleteVercelSnapshot(entry.manifest.snapshotName);\n } catch {\n // best-effort: drop the local manifest even if the remote delete failed\n // (network/perms/already-gone) so the user isn't left with a dead pointer.\n }\n await removeCloudCheckpointDir(projectRoot, BACKEND_NAME, ref);\n },\n};\n\nexport const vercelProvider: Provider = {\n ...cloudProvider,\n prepare: prepareVercelProvider,\n buildAttach: buildVercelAttach,\n checkpoint: vercelCheckpoint,\n};\n\nexport { vercelBackend, DEFAULT_BOX_IMAGE_REF };\nexport { ensureVercelEnvLoaded, reloadVercelEnv } from './env-loader.js';\nexport { ensureVercelCredentials } from './credentials.js';\nexport type { EnsureVercelCredentialsOptions } from './credentials.js';\nexport {\n readVercelCredStatus,\n secretsPath,\n maskKey,\n type VercelCredStatus,\n} from './credentials.js';\nexport {\n prepareVercel,\n prepareVercelProvider,\n type PrepareVercelOptions,\n type PrepareVercelResult,\n} from './prepare.js';\nexport {\n ensureVercelBaseSnapshot,\n preparedStatePath,\n readPreparedState,\n writePreparedState,\n updatePreparedState,\n type PreparedVercelState,\n type PreparedVercelBase,\n} from './prepared-state.js';\nexport {\n RUNTIME_ASSETS,\n candidatesFor,\n resolveRuntimeAssets,\n findStagedCliRuntimeRoot,\n type RuntimeAsset,\n type ResolvedAsset,\n} from './runtime-assets.js';\nexport { buildVercelAttach } from './build-attach.js';\n","/**\n * Vercel `CloudBackend` — maps the provider-neutral cloud primitives onto\n * `@vercel/sandbox` v2 (Firecracker microVMs + snapshots). Composed into a full\n * `Provider` by `@agentbox/sandbox-cloud`'s `createCloudProvider`.\n *\n * Platform shape this backend is built around (see docs/cloud-providers.md):\n * - No custom image — sandboxes boot from a Vercel snapshot baked once by\n * `agentbox prepare --provider vercel`. `provision` always needs a snapshot\n * id (the prepared base, or a cloud-checkpoint snapshot).\n * - No SSH — `attachArgv` is intentionally omitted; the provider overrides\n * `buildAttach` with a Vercel-SDK-streaming helper instead.\n * - No nested containers — dockerd is disabled at the provider level.\n * - Persistent sandboxes auto-snapshot on stop and auto-resume on the next\n * `Sandbox.get({ resume: true })`, which is how pause/resume map cleanly.\n * - The sandbox's native user is `vercel-sandbox`; agentbox standardizes on\n * `vscode` (uid 1000), created by provision.sh. So `exec` drops privileges\n * to `vscode` (root → `sudo -u vscode`) unless the caller asks for root,\n * and `uploadFile` chowns to uid 1000 after the SDK writes as\n * `vercel-sandbox`.\n * - Max 4 exposed ports: we use 80 (WebProxy), 6080 (noVNC), 8788 (relay/ctl\n * bridge). One slot is left free for a future per-service expose.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport type {\n CloudBackend,\n CloudExecOptions,\n CloudExecResult,\n CloudFileEntry,\n CloudHandle,\n CloudPreviewUrl,\n CloudProvisionRequest,\n CloudSandboxSummary,\n CloudState,\n} from '@agentbox/core';\nimport type { NetworkPolicy } from '@vercel/sandbox';\nimport {\n ensureFreshCredentials,\n resolveCredentials,\n Sandbox,\n Snapshot,\n type SandboxType,\n} from './sdk.js';\nimport {\n stageClaudeCredentialsForUpload,\n stageCodexCredentialsForUpload,\n stageOpencodeCredentialsForUpload,\n type StageResult,\n} from '@agentbox/sandbox-cloud';\nimport { withVercelRetry } from './retry.js';\nimport { readPreparedState } from './prepared-state.js';\n\n/** Sentinel image ref the cloud-provider hands us when no --image was passed. */\nexport const DEFAULT_BOX_IMAGE_REF = 'agentbox/box:dev';\n\n/** Box user agentbox standardizes on. provision.sh creates it (uid auto-assigned —\n * the Vercel default user may already hold 1000, and there are no bind mounts so\n * the exact uid is irrelevant). chown targets it by name, not number. */\nconst BOX_USER = 'vscode';\nconst BOX_OWNER = 'vscode:vscode';\n\n/**\n * Base ports exposed at create. Vercel REJECTS privileged ports (<1024) with a\n * 400, so we cannot expose the scaffold's WebProxy on :80. Instead the in-box\n * WebProxy binds 8080 (set via `webProxyPort` → AGENTBOX_WEB_PROXY_PORT) and we\n * expose 8080 here so `sandbox.domain(8080)` routes to it → the in-box `expose:`\n * service. Ports are fixed at create (update can't add a routable port to a\n * running sandbox — verified), so 8080 must be in this base set. The other two\n * base ports are 6080 (noVNC) and 8788 (the relay/ctl bridge the host poller\n * reaches via `sandbox.domain(8788)`). Remaining slots (up to VERCEL_MAX_PORTS)\n * are filled at create from `agentbox.yaml` `expose:` ports (see buildExposedPorts).\n */\nexport const VERCEL_EXPOSED_PORTS = [8080, 6080, 8788] as const;\n\n/** Vercel's hard per-sandbox exposed-port cap. */\nexport const VERCEL_MAX_PORTS = 4;\n\n/**\n * Merge requested `expose:` service ports into the base set: drop privileged\n * (<1024 — Vercel 400s) and out-of-range ports + dupes, preserve order, and cap\n * at Vercel's 4-port limit. A preview URL only routes to a port declared here at\n * create time, so this is what makes `services.*.expose` reachable on Vercel.\n */\nexport function buildExposedPorts(extra: readonly number[] | undefined): number[] {\n const ports = [...VERCEL_EXPOSED_PORTS] as number[];\n const seen = new Set<number>(ports);\n for (const p of extra ?? []) {\n if (ports.length >= VERCEL_MAX_PORTS) break;\n if (Number.isInteger(p) && p >= 1024 && p < 65_536 && !seen.has(p)) {\n ports.push(p);\n seen.add(p);\n }\n }\n return ports;\n}\n\n/**\n * Parse the `box.vercelNetworkPolicy` config string into a Vercel\n * `NetworkPolicy`. `''`/unset → undefined (SDK default = allow-all). The\n * literals `allow-all` / `deny-all` pass through; anything else is treated as a\n * comma-separated domain allowlist `{ allow: [...] }` (everything else denied).\n */\nexport function parseNetworkPolicy(raw: string | undefined): NetworkPolicy | undefined {\n const v = (raw ?? '').trim();\n if (v === '') return undefined;\n if (v === 'allow-all' || v === 'deny-all') return v;\n const allow = v\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return allow.length > 0 ? { allow } : undefined;\n}\n\n/**\n * Default per-session timeout. 45 min is the Hobby ceiling, so it's safe across\n * all plans; persistent mode makes a hit transparent (the VM auto-snapshots and\n * auto-resumes on the next SDK call). Pro/Enterprise users who want a longer\n * single session can rely on `extendTimeout` / future config.\n */\nconst DEFAULT_TIMEOUT_MS = 45 * 60_000;\n\n/**\n * Per-box snapshot retention. Keep one auto-snapshot, never expiring, so a\n * paused box can always resume; `destroy` purges a box's own snapshot explicitly.\n *\n * `deleteEvicted: false` is load-bearing, NOT a tweak. A box boots from a shared\n * snapshot (the prepared base, or a `setup` checkpoint), and Vercel reports that\n * source as the box's `currentSnapshotId` until it takes its first auto-snapshot\n * — i.e. the source is the first member of this box's retention window. With\n * `deleteEvicted: true`, the box's first stop/snapshot evicts the source and\n * DELETES it, nuking the shared base/checkpoint every other box depends on, so\n * every later `create` 410s with \"Snapshot expired or deleted.\" (Same hazard the\n * `destroy` guard already dodges, but eviction is automatic and bypasses it.)\n * `false` keeps evicted snapshots around (they fall back to `snapshotExpiration`,\n * which we pin to 0 = never at create) — trading a little snapshot accumulation\n * for never deleting a snapshot another box boots from.\n */\nconst KEEP_LAST_SNAPSHOTS = { count: 1, expiration: 0, deleteEvicted: false } as const;\n\nfunction creds(): Partial<{ token: string; teamId: string; projectId: string }> {\n return resolveCredentials();\n}\n\n/** Single-quote a string for safe embedding inside a `bash -lc '<…>'`. */\nfunction shq(s: string): string {\n return \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n\nasync function getSandbox(id: string): Promise<SandboxType> {\n // resume:false — plain handle resolution; lifecycle methods opt into resume.\n return Sandbox.get({ name: id, resume: false, ...creds() });\n}\n\nasync function maybeGetSandbox(id: string): Promise<SandboxType | null> {\n try {\n return await getSandbox(id);\n } catch {\n return null;\n }\n}\n\n/**\n * Map Vercel's session status onto our 4-value `CloudState`. Transitional\n * states report as 'running' so callers don't ping-pong; 'stopped' maps to\n * 'paused' because a persistent sandbox keeps an auto-snapshot and resumes on\n * the next call (our pause semantics). 'aborted'/'failed' → 'missing'.\n */\nfunction mapState(s: string | undefined): CloudState {\n switch (s) {\n case 'running':\n return 'running';\n case 'pending':\n case 'stopping':\n case 'snapshotting':\n return 'running';\n case 'stopped':\n return 'paused';\n case 'aborted':\n case 'failed':\n default:\n return 'missing';\n }\n}\n\n/**\n * Build a `runCommand` invocation that runs `cmd` (already a shell string) as\n * the box user (`vscode`) by default, or as root when requested. Always starts\n * the SDK command as root (`sudo: true`) so the inner `sudo -u vscode` is\n * reliably passwordless, then drops privileges. cwd + env are applied inside\n * the dropped shell so they land in the right user/home context.\n */\nfunction buildRunCommand(\n cmd: string,\n opts?: CloudExecOptions,\n): { cmd: string; args: string[]; sudo: boolean } {\n const prelude: string[] = [];\n if (opts?.cwd) prelude.push(`cd ${shq(opts.cwd)}`);\n for (const [k, v] of Object.entries(opts?.env ?? {})) {\n // The value is shell-quoted, but the key is interpolated bare into a\n // `bash -lc` string that runs as root — reject anything that isn't a POSIX\n // env-var name so a key like `x;rm -rf /` can't inject a command.\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) {\n throw new Error(`vercel exec: invalid env var name ${JSON.stringify(k)}`);\n }\n prelude.push(`export ${k}=${shq(v)}`);\n }\n const inner = [...prelude, cmd].join('\\n');\n const user = opts?.user ?? BOX_USER;\n if (user === 'root') {\n return { cmd: 'bash', args: ['-lc', inner], sudo: true };\n }\n return {\n cmd: 'bash',\n args: ['-lc', `sudo -u ${user} -H bash -lc ${shq(inner)}`],\n sudo: true,\n };\n}\n\n/**\n * Push the host's renewable agent credentials (`.credentials.json` for claude,\n * `auth.json` for codex/opencode) into `/home/vscode/.agentbox-creds/<agent>/`\n * per-box at create time. Vercel has no shared-volume primitive, so the cloud\n * credential-volume path (`seedAgentVolumesIfFresh`) is a no-op here — this is\n * the equivalent of Hetzner's scp push (`pushHetznerAgentCredentials`), over the\n * Vercel SDK instead. The credential-pivot symlinks baked into the snapshot by\n * provision.sh route `~/.claude/.credentials.json` etc. through to this dir, so\n * the in-box agents find their tokens at the expected paths. Pushes on every\n * create (tokens are renewable). Best-effort: a failure means the in-box agent\n * falls back to interactive login, so the caller logs + continues.\n */\nasync function pushVercelAgentCredentials(\n sb: SandboxType,\n log: (line: string) => void,\n): Promise<void> {\n const specs: Array<{\n kind: 'claude' | 'codex' | 'opencode';\n stage: () => Promise<StageResult>;\n dest: string;\n }> = [\n { kind: 'claude', stage: stageClaudeCredentialsForUpload, dest: '/home/vscode/.agentbox-creds/claude' },\n { kind: 'codex', stage: stageCodexCredentialsForUpload, dest: '/home/vscode/.agentbox-creds/codex' },\n { kind: 'opencode', stage: stageOpencodeCredentialsForUpload, dest: '/home/vscode/.agentbox-creds/opencode' },\n ];\n for (const spec of specs) {\n const staged = await spec.stage();\n for (const w of staged.warnings) log(`vercel: [${spec.kind}-creds] ${w}`);\n try {\n if (!staged.tarballPath) {\n log(`vercel: ${spec.kind}: no host credentials to push (skipping)`);\n continue;\n }\n const remote = `/tmp/agentbox-${spec.kind}-creds.tar.gz`;\n await sb.writeFiles([{ path: remote, content: await readFile(staged.tarballPath) }]);\n // Extract as vscode into the dest dir (created + chown'd by provision.sh's\n // credential-pivot step). Plain mkdir/tar/rm — no $()/loops — so the exec\n // wrapping is irrelevant.\n const extract =\n `sudo -u vscode mkdir -p ${spec.dest} && ` +\n `sudo -u vscode tar -xzf ${remote} -C ${spec.dest} --no-same-permissions --no-same-owner -m && ` +\n `rm -f ${remote}`;\n const r = await sb.runCommand({ cmd: 'bash', args: ['-lc', extract], sudo: true });\n if (r.exitCode !== 0) {\n log(`vercel: WARN — ${spec.kind} credential extract failed (exit ${String(r.exitCode)})`);\n } else {\n log(`vercel: ${spec.kind}: credentials pushed`);\n }\n } finally {\n await staged.cleanup();\n }\n }\n}\n\nexport const vercelBackend: CloudBackend = {\n name: 'vercel',\n\n // Vercel rejects privileged ports (<1024) and can't add a routable port to a\n // running sandbox (update registers a route that 502s — verified). So the\n // in-box WebProxy binds 8080 (exposed at create via VERCEL_EXPOSED_PORTS) and\n // `agentbox url` resolves sandbox.domain(8080) → WebProxy → the in-box service.\n webProxyPort: 8080,\n\n async provision(req: CloudProvisionRequest): Promise<CloudHandle> {\n await ensureFreshCredentials();\n // Resolve the snapshot to boot from: an explicit cloud-checkpoint snapshot\n // (req.snapshot) wins, else the prepared base. Vercel can't build from a\n // Dockerfile, so there is no image fallback — fail loud with the fix.\n const snapshotId = req.snapshot ?? readPreparedState().base?.snapshotId;\n if (!snapshotId) {\n throw new Error(\n 'no Vercel base snapshot found.\\n' +\n 'Run `agentbox prepare --provider vercel` first — Vercel cannot build images ' +\n 'from a Dockerfile, so the base snapshot is a one-time prerequisite.',\n );\n }\n const networkPolicy = parseNetworkPolicy(req.networkPolicy);\n const log = req.onLog ?? (() => {});\n // No-retry: Sandbox.create is billable and non-idempotent — a timeout after\n // the request reached the origin could leave a duplicate sandbox we can't\n // reference for cleanup.\n const handle = await withVercelRetry(\n { method: 'provision', retryOnAmbiguous: false, attemptTimeoutMs: 900_000, backoffMs: [] },\n async () => {\n const sb = await Sandbox.create({\n name: req.name,\n source: { type: 'snapshot', snapshotId },\n resources: { vcpus: req.resources?.cpu ?? 2 },\n ports: buildExposedPorts(req.exposePorts),\n timeout: req.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n env: req.env,\n tags: { agentbox: 'true', 'agentbox.name': req.name },\n persistent: true,\n // Pin the sandbox-default expiration to never. Evicted snapshots (see\n // KEEP_LAST_SNAPSHOTS) fall back to this, so the shared base/checkpoint\n // a box boots from is never re-stamped with a finite expiry on eviction.\n snapshotExpiration: 0,\n keepLastSnapshots: { ...KEEP_LAST_SNAPSHOTS },\n ...(networkPolicy ? { networkPolicy } : {}),\n ...creds(),\n });\n return { sandboxId: sb.name };\n },\n );\n // Push renewable agent credentials per-box (outside the billable, no-retry\n // create block so a push failure never affects create semantics).\n try {\n const sb = await getSandbox(handle.sandboxId);\n await pushVercelAgentCredentials(sb, log);\n } catch (err) {\n log(\n `vercel: WARN — agent credential push failed (${err instanceof Error ? err.message : String(err)}); ` +\n 'in-box claude/codex/opencode will prompt for interactive login',\n );\n }\n return handle;\n },\n\n async get(sandboxId: string): Promise<CloudHandle | null> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'get', retryOnAmbiguous: true }, async () => {\n const sb = await maybeGetSandbox(sandboxId);\n return sb ? { sandboxId: sb.name } : null;\n });\n },\n\n async list(): Promise<CloudSandboxSummary[]> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'list', retryOnAmbiguous: true }, async () => {\n const page = await Sandbox.list({ ...creds() });\n const items = await page.toArray();\n return items\n .filter((sb) => sb.tags?.['agentbox'] === 'true')\n .map((sb): CloudSandboxSummary => {\n const summary: CloudSandboxSummary = { sandboxId: sb.name };\n const friendly = sb.tags?.['agentbox.name'] ?? sb.name;\n if (friendly) summary.name = friendly;\n if (typeof sb.createdAt === 'number') {\n summary.createdAt = new Date(sb.createdAt).toISOString();\n }\n summary.state = mapState(sb.status);\n return summary;\n });\n });\n },\n\n async start(h: CloudHandle): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'start', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n // resume:true auto-resumes a persistent sandbox from its current snapshot.\n await Sandbox.get({ name: h.sandboxId, resume: true, ...creds() });\n },\n );\n },\n\n async stop(h: CloudHandle): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'stop', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n const sb = await getSandbox(h.sandboxId);\n // For a persistent sandbox this captures an auto-snapshot and shuts the\n // VM down — resume happens lazily on the next Sandbox.get.\n await sb.stop();\n },\n );\n },\n\n // pause == stop on Vercel (the auto-snapshot IS the cold-storage state).\n async pause(h: CloudHandle): Promise<void> {\n await this.stop(h);\n },\n\n async resume(h: CloudHandle): Promise<void> {\n await this.start(h);\n },\n\n async destroy(h: CloudHandle): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'destroy', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n const sb = await maybeGetSandbox(h.sandboxId);\n if (!sb) return; // already gone — destroy is idempotent\n // Purge only a snapshot THIS box created (its own stop-time auto-\n // snapshot), never the shared base/source it booted from. A fresh box\n // has currentSnapshotId === sourceSnapshotId === the prepared base, and\n // deleting that would nuke the base snapshot every other box depends on.\n const snapId = sb.currentSnapshotId;\n const source = sb.sourceSnapshotId;\n const base = readPreparedState().base?.snapshotId;\n const ownSnapshot =\n snapId !== undefined && snapId !== source && snapId !== base;\n await sb.delete();\n if (ownSnapshot) {\n try {\n const snap = await Snapshot.get({ snapshotId: snapId, ...creds() });\n await snap.delete();\n } catch {\n // best-effort: a snapshot already gone is fine; the user can clean\n // stragglers from the Vercel dashboard.\n }\n }\n },\n );\n },\n\n async state(h: CloudHandle): Promise<CloudState> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'state', retryOnAmbiguous: true }, async () => {\n const sb = await maybeGetSandbox(h.sandboxId);\n if (!sb) return 'missing';\n return mapState(sb.status);\n });\n },\n\n async exec(h: CloudHandle, cmd: string, opts?: CloudExecOptions): Promise<CloudExecResult> {\n await ensureFreshCredentials();\n return withVercelRetry(\n {\n method: 'exec',\n retryOnAmbiguous: opts?.noRetry ? false : true,\n attemptTimeoutMs: opts?.attemptTimeoutMs ?? 120_000,\n backoffMs: opts?.noRetry ? [] : undefined,\n },\n async () => {\n const sb = await getSandbox(h.sandboxId);\n const r = await sb.runCommand(buildRunCommand(cmd, opts));\n const [stdout, stderr] = await Promise.all([r.stdout(), r.stderr()]);\n return { exitCode: r.exitCode, stdout, stderr };\n },\n );\n },\n\n async uploadFile(h: CloudHandle, localPath: string, remotePath: string): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'uploadFile', retryOnAmbiguous: true, attemptTimeoutMs: 300_000 },\n async () => {\n const content = await readFile(localPath);\n const sb = await getSandbox(h.sandboxId);\n await sb.writeFiles([{ path: remotePath, content }]);\n // writeFiles writes as `vercel-sandbox`; chown to the box user so the\n // scaffold's vscode-context reads/extractions succeed. Best-effort —\n // a chown failure on a world-readable /tmp staging file is harmless.\n try {\n await sb.runCommand({ cmd: 'chown', args: [BOX_OWNER, remotePath], sudo: true });\n } catch {\n // ignore — file is at least present and readable\n }\n },\n );\n },\n\n async downloadFile(h: CloudHandle, remotePath: string, localPath: string): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry(\n { method: 'downloadFile', retryOnAmbiguous: true, attemptTimeoutMs: 300_000 },\n async () => {\n const sb = await getSandbox(h.sandboxId);\n const written = await sb.downloadFile(\n { path: remotePath },\n { path: localPath },\n { mkdirRecursive: true },\n );\n if (written === null) {\n throw new Error(`vercel downloadFile: source not found: ${remotePath}`);\n }\n },\n );\n },\n\n async listFiles(h: CloudHandle, remoteDir: string): Promise<CloudFileEntry[]> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'listFiles', retryOnAmbiguous: true }, async () => {\n const sb = await getSandbox(h.sandboxId);\n const entries = await sb.fs.readdir(remoteDir, { withFileTypes: true });\n return entries.map((e) => ({ name: e.name, isDir: e.isDirectory() }));\n });\n },\n\n async previewUrl(h: CloudHandle, port: number): Promise<CloudPreviewUrl> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'previewUrl', retryOnAmbiguous: true }, async () => {\n const sb = await getSandbox(h.sandboxId);\n // sb.domain(port) is a public HTTPS URL (no header token needed).\n return { url: sb.domain(port), token: undefined };\n });\n },\n\n // Fewer params than the interface's (h, port, expiresInSeconds) is fine —\n // Vercel sandbox domains are already public + browser-usable, so the signed\n // URL is just the standard one (the TTL is governed by the sandbox session\n // lifetime, not a per-URL signature, so the expiry arg is irrelevant here).\n async signedPreviewUrl(h: CloudHandle, port: number): Promise<CloudPreviewUrl> {\n return this.previewUrl(h, port);\n },\n\n async snapshotExists(snapshotName: string): Promise<boolean> {\n await ensureFreshCredentials();\n return withVercelRetry({ method: 'snapshotExists', retryOnAmbiguous: true }, async () => {\n try {\n const snap = await Snapshot.get({ snapshotId: snapshotName, ...creds() });\n // `Snapshot.get` resolves deleted/failed tombstones (status field) rather\n // than throwing, so \"didn't throw\" wrongly passes a dead snapshot. Only a\n // 'created' snapshot can actually boot a sandbox.\n return snap.status === 'created';\n } catch {\n return false;\n }\n });\n },\n\n // NOTE: no `createSnapshot`/`deleteSnapshot` here. Vercel snapshots are\n // addressed by an opaque id (not a caller-chosen name), which doesn't fit the\n // CloudBackend `createSnapshot(handle, name): void` contract — the provider\n // needs the id back to store it in the checkpoint manifest. The Vercel\n // provider therefore overrides the whole `checkpoint` capability in index.ts\n // using `snapshotVercelSandbox` / `deleteVercelSnapshot` below.\n};\n\n/**\n * Snapshot a running sandbox and return the resulting Vercel snapshot id.\n * `sb.snapshot()` stops the source sandbox as part of capture; persistent mode\n * resumes it on the next SDK call, so the box comes back automatically.\n */\nexport async function snapshotVercelSandbox(sandboxId: string): Promise<string> {\n await ensureFreshCredentials();\n return withVercelRetry(\n { method: 'createSnapshot', retryOnAmbiguous: false, attemptTimeoutMs: 900_000, backoffMs: [] },\n async () => {\n const sb = await getSandbox(sandboxId);\n const snap = await sb.snapshot({ expiration: 0 });\n return snap.snapshotId;\n },\n );\n}\n\n/** Delete a Vercel snapshot by id. Idempotent — a missing snapshot is success. */\nexport async function deleteVercelSnapshot(snapshotId: string): Promise<void> {\n await ensureFreshCredentials();\n await withVercelRetry({ method: 'deleteSnapshot', retryOnAmbiguous: true }, async () => {\n try {\n const snap = await Snapshot.get({ snapshotId, ...creds() });\n await snap.delete();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (/not.?found|404/i.test(msg)) return; // idempotent\n throw err;\n }\n });\n}\n","/**\n * Bounded retry wrapper for Vercel Sandbox SDK calls — mirrors\n * `withDaytonaRetry` / `withHetznerRetry` in shape and intent. The Vercel\n * control plane rate-limits (429) and can return transient 5xx during\n * incidents; without bounded retries those propagate as wedges in the calling\n * lifecycle code.\n *\n * Non-idempotent ops (`provision`/`Sandbox.create`, `createSnapshot`) pass\n * `retryOnAmbiguous: false` so a timeout after the request reached the origin\n * doesn't create a duplicate billable sandbox/snapshot.\n */\n\nexport interface WithRetryOptions {\n method: string;\n /** Per-attempt timeout (ms). Default 30_000. */\n attemptTimeoutMs?: number;\n /** Backoff before attempts 2, 3, … (ms). Default [1000, 2000, 4000]. */\n backoffMs?: readonly number[];\n /**\n * Retry on errors where we can't be sure the server applied the request\n * (connection failures, per-attempt timeouts, 5xx). Set false for\n * non-idempotent operations where a retry could create a duplicate resource.\n */\n retryOnAmbiguous: boolean;\n /** Override the default stderr retry sink (used by tests). */\n onRetry?: (line: string) => void;\n}\n\nconst DEFAULT_BACKOFF: readonly number[] = [1000, 2000, 4000];\nconst DEFAULT_ATTEMPT_TIMEOUT_MS = 30_000;\n\nclass AttemptTimeoutError extends Error {\n constructor(method: string, ms: number) {\n super(`vercel ${method}: per-attempt timeout after ${String(ms)}ms`);\n this.name = 'AttemptTimeoutError';\n }\n}\n\nexport function isAttemptTimeout(err: unknown): err is AttemptTimeoutError {\n return err instanceof AttemptTimeoutError;\n}\n\n/** HTTP status code dug out of whatever error shape the SDK throws. */\nfunction statusCodeOf(err: unknown): number | undefined {\n if (!err || typeof err !== 'object') return undefined;\n for (const key of ['statusCode', 'status', 'code'] as const) {\n const v = (err as Record<string, unknown>)[key];\n if (typeof v === 'number') return v;\n }\n const resp = (err as { response?: { status?: unknown } }).response;\n if (resp && typeof resp.status === 'number') return resp.status;\n return undefined;\n}\n\nexport function isRetriable(err: unknown, allowAmbiguous: boolean): boolean {\n if (err instanceof AttemptTimeoutError) return allowAmbiguous;\n\n const status = statusCodeOf(err);\n if (status !== undefined) {\n if (status === 429) return true; // rate limited — the server told us to wait\n if (status >= 500 && status <= 599) return allowAmbiguous;\n return false; // 4xx (auth, validation, not_found) — permanent\n }\n\n // Raw fetch / undici errors. Node wraps low-level errors in `{ cause }`.\n if (err && typeof err === 'object') {\n const candidates: unknown[] = [err, (err as { cause?: unknown }).cause];\n for (const c of candidates) {\n if (!c || typeof c !== 'object') continue;\n const code = (c as { code?: unknown }).code;\n if (\n code === 'ECONNRESET' ||\n code === 'ETIMEDOUT' ||\n code === 'ECONNABORTED' ||\n code === 'EAI_AGAIN' ||\n code === 'ECONNREFUSED' ||\n code === 'ENOTFOUND' ||\n code === 'UND_ERR_SOCKET' ||\n code === 'UND_ERR_CONNECT_TIMEOUT'\n ) {\n return allowAmbiguous;\n }\n }\n }\n return false;\n}\n\nexport async function withVercelRetry<T>(\n opts: WithRetryOptions,\n fn: () => Promise<T>,\n): Promise<T> {\n const backoff = opts.backoffMs ?? DEFAULT_BACKOFF;\n const maxAttempts = backoff.length + 1;\n const timeoutMs = opts.attemptTimeoutMs ?? DEFAULT_ATTEMPT_TIMEOUT_MS;\n const log = opts.onRetry ?? defaultRetryLog;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await raceTimeout(fn(), timeoutMs, opts.method);\n } catch (err) {\n const last = attempt === maxAttempts;\n if (last || !isRetriable(err, opts.retryOnAmbiguous)) throw err;\n const delay = backoff[attempt - 1] ?? backoff[backoff.length - 1] ?? 4000;\n log(\n `vercel ${opts.method}: attempt ${String(attempt)} failed (${errorSummary(err)}); retrying in ${String(delay)}ms`,\n );\n await sleep(delay);\n }\n }\n throw new Error(`withVercelRetry: exhausted attempts for ${opts.method}`);\n}\n\nfunction defaultRetryLog(line: string): void {\n process.stderr.write(`\\n[vercel-retry] ${line}\\n`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nasync function raceTimeout<T>(p: Promise<T>, ms: number, method: string): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n p,\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(new AttemptTimeoutError(method, ms)), ms);\n }),\n ]);\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n}\n\nfunction errorSummary(err: unknown): string {\n if (err instanceof Error) {\n const status = statusCodeOf(err);\n return status !== undefined\n ? `${err.name}(${String(status)}): ${truncate(err.message)}`\n : `${err.name}: ${truncate(err.message)}`;\n }\n return truncate(String(err));\n}\n\nfunction truncate(s: string, max = 160): string {\n return s.length > max ? `${s.slice(0, max)}…` : s;\n}\n","/**\n * Persisted record of what `agentbox prepare --provider vercel` has built.\n * Lives at `~/.agentbox/vercel-prepared.json` so the auto-prepare gate\n * (`ensureVercelBaseSnapshot()`) and `backend.provision` can resolve the base\n * snapshot to boot every box from.\n *\n * Single tier for now — the shared base snapshot (AL2023 + deps + agentbox-ctl\n * + agents). A per-project snapshot tier (matching the hetzner/daytona shape)\n * is a future optimization tracked in docs/vercel-backlog.md.\n *\n * Schema versioned so future shape changes can migrate; only `schema: 1` is\n * accepted today.\n */\n\nimport { readPreparedStateRaw, writePreparedStateRaw, preparedStatePathFor } from '@agentbox/sandbox-core';\n\nconst SCHEMA = 1 as const;\n\nexport interface PreparedVercelBase {\n /** Vercel snapshot id (opaque). The thing `Sandbox.create({ source }) ` boots from. */\n snapshotId: string;\n /** Deterministic SHA-256 of the prepare build context (provision.sh + assets). */\n contextSha256?: string;\n /** CLI version that produced this snapshot (informational). */\n cliVersion?: string;\n /** Git short SHA of the CLI build (informational). */\n cliCommit?: string;\n /** ISO timestamp of bake completion. */\n createdAt: string;\n}\n\nexport interface PreparedVercelState {\n schema: typeof SCHEMA;\n /** The shared base snapshot. Absent until first `agentbox prepare`. */\n base?: PreparedVercelBase;\n}\n\nexport function preparedStatePath(): string {\n return preparedStatePathFor('vercel');\n}\n\nexport function readPreparedState(): PreparedVercelState {\n const raw = readPreparedStateRaw('vercel');\n if (raw === null || typeof raw !== 'object') return { schema: SCHEMA };\n const parsed = raw as Partial<PreparedVercelState>;\n if (parsed.schema !== SCHEMA) {\n // Unknown/missing schema: refuse to read — the next prepare overwrites it.\n return { schema: SCHEMA };\n }\n return { schema: SCHEMA, base: parsed.base };\n}\n\nexport function writePreparedState(state: PreparedVercelState): void {\n writePreparedStateRaw('vercel', state);\n}\n\n/** Update one field of the state without forcing callers to read/merge/write. */\nexport function updatePreparedState(mutate: (s: PreparedVercelState) => void): void {\n const s = readPreparedState();\n mutate(s);\n writePreparedState(s);\n}\n\n/**\n * First-use gate. If no base snapshot is recorded, throw an actionable error\n * pointing at `agentbox prepare --provider vercel`. Called by `backend.provision`\n * (indirectly via the snapshot resolution) and usable by the CLI.\n */\nexport function ensureVercelBaseSnapshot(): void {\n const state = readPreparedState();\n if (state.base !== undefined) return;\n throw new Error(\n 'no Vercel base snapshot found.\\n' +\n 'Run `agentbox prepare --provider vercel` first — Vercel cannot build images ' +\n 'from a Dockerfile, so the base snapshot is a one-time prerequisite for cloud boxes.',\n );\n}\n","/**\n * `agentbox prepare --provider vercel` — bake the per-team Vercel base\n * snapshot. Vercel can't build an image from a Dockerfile, so (like hetzner)\n * we boot a fresh sandbox, run an installer, and snapshot the result. That\n * snapshot id is what every per-box `create` boots from.\n *\n * Flow:\n * 1. Resolve runtime assets + fingerprint the build context. Skip the bake\n * when an up-to-date base snapshot already exists (unless --force).\n * 2. `Sandbox.create({ runtime: 'node24', persistent: false })` — fresh AL2023.\n * 3. `writeFiles` the assets (ctl bundle, helpers, baked configs, provision.sh).\n * 4. Run provision.sh as root, streaming output to the prepare log.\n * 5. Stage host agent static config (claude/codex/opencode) into the snapshot.\n * 6. `sandbox.snapshot({ expiration: 0 })` → the never-expiring base snapshot.\n * 7. Persist the snapshot id into ~/.agentbox/vercel-prepared.json.\n * 8. Delete the builder sandbox.\n *\n * Step 8 is safe: a Vercel snapshot is an independent, id-addressed resource\n * that survives its source sandbox's deletion (verified live — snapshot stays\n * `status: 'created'` and boots a fresh sandbox after the builder is deleted).\n * We delete it best-effort *after* the snapshot id is persisted, so a delete\n * failure only leaves a lingering sandbox for Vercel's reaper, never a broken\n * bake.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { Writable } from 'node:stream';\nimport type { Provider } from '@agentbox/core';\nimport { computeContextSha256, readCliStamp } from '@agentbox/sandbox-core';\nimport {\n stageClaudeStaticForUpload,\n stageCodexStaticForUpload,\n stageOpencodeStaticForUpload,\n type StageResult,\n} from '@agentbox/sandbox-cloud';\nimport { ensureVercelCredentials } from './credentials.js';\nimport {\n ensureFreshCredentials,\n resolveCredentials,\n Sandbox,\n Snapshot,\n type SandboxType,\n} from './sdk.js';\nimport {\n preparedStatePath,\n readPreparedState,\n writePreparedState,\n} from './prepared-state.js';\nimport {\n findStagedCliRuntimeRoot,\n resolveRuntimeAssets,\n type ResolvedAsset,\n} from './runtime-assets.js';\n\nexport interface PrepareVercelOptions {\n name?: string;\n hostWorkspace?: string;\n /** Force re-bake even when an up-to-date base snapshot is recorded. */\n force?: boolean;\n /** vCPUs for the builder sandbox (default 4 for a fast bake). */\n vcpus?: number;\n /** CLI runtime tree (set by the CLI to its dist neighbor). */\n cliRuntimeRoot?: string;\n /** Repo root for the dev fallback (defaults to a cwd-walk). */\n repoRoot?: string;\n onLog?: (line: string) => void;\n}\n\nexport interface PrepareVercelResult {\n snapshotName?: string;\n}\n\nconst BUILDER_TIMEOUT_MS = 25 * 60_000;\nconst SHELL = '/bin/bash';\n\nexport async function prepareVercel(\n opts: PrepareVercelOptions = {},\n): Promise<PrepareVercelResult> {\n await ensureVercelCredentials();\n await ensureFreshCredentials();\n const creds = resolveCredentials();\n const log = opts.onLog ?? (() => {});\n const progress = (s: string) => log(`prepare-vercel: ${s}`);\n\n const assets = resolveRuntimeAssets({\n cliRuntimeRoot: opts.cliRuntimeRoot ?? findStagedCliRuntimeRoot(),\n repoRoot: opts.repoRoot,\n });\n const contextSha = await computeContextSha256(\n assets.map((a) => ({ rel: a.name, abs: a.localPath })),\n );\n\n // Skip-fast: existing base snapshot still on Vercel + matching fingerprint.\n const existing = readPreparedState();\n if (!opts.force && existing.base) {\n const stillThere = await snapshotExists(existing.base.snapshotId, creds);\n if (stillThere && existing.base.contextSha256 === contextSha) {\n progress(\n `base snapshot ${existing.base.snapshotId} already exists (fingerprint ${contextSha.slice(0, 12)} matches); skipping (pass --force to rebuild)`,\n );\n return { snapshotName: existing.base.snapshotId };\n }\n if (!stillThere) {\n progress(`recorded base snapshot ${existing.base.snapshotId} is gone on Vercel; rebuilding`);\n } else {\n progress(\n `build context changed (was ${existing.base.contextSha256?.slice(0, 12) ?? '<none>'}, now ${contextSha.slice(0, 12)}); rebuilding`,\n );\n }\n }\n\n progress(`creating builder sandbox (node24, ${String(opts.vcpus ?? 4)} vcpus)`);\n const sb = await Sandbox.create({\n runtime: 'node24',\n resources: { vcpus: opts.vcpus ?? 4 },\n timeout: BUILDER_TIMEOUT_MS,\n tags: { agentbox: 'true', 'agentbox.role': 'prepare' },\n persistent: false,\n ...creds,\n });\n progress(`builder sandbox ${sb.name} up`);\n\n // 3. Upload assets.\n progress(`uploading ${String(assets.length)} runtime asset(s)`);\n await sb.writeFiles(\n await Promise.all(\n assets.map(async (a: ResolvedAsset) => ({\n path: a.remotePath,\n content: await readFile(a.localPath),\n mode: a.remoteMode,\n })),\n ),\n );\n\n // 4. Run provision.sh as root, streaming output.\n progress('running provision.sh (this takes a few minutes)');\n const install = await sb.runCommand({\n cmd: SHELL,\n args: ['-lc', 'bash /tmp/agentbox-provision.sh 2>&1'],\n sudo: true,\n stdout: lineSink((l) => log(`[provision] ${l}`)),\n stderr: lineSink((l) => log(`[provision] ${l}`)),\n });\n if (install.exitCode !== 0) {\n throw new Error(`provision.sh failed on the builder sandbox (exit ${String(install.exitCode)})`);\n }\n progress('provision.sh complete');\n\n // 5. Stage host agent static config into the snapshot (best-effort).\n await stageAgentConfig(sb, opts.hostWorkspace, log);\n\n // 6. Snapshot (never expires). NOTE: this stops the builder sandbox.\n progress('creating base snapshot (expiration: never)');\n const snap = await sb.snapshot({ expiration: 0 });\n progress(`snapshot created: ${snap.snapshotId}`);\n\n // 7. Persist.\n const cliStamp = readCliStamp();\n writePreparedState({\n schema: 1,\n base: {\n snapshotId: snap.snapshotId,\n contextSha256: contextSha,\n cliVersion: cliStamp.cliVersion,\n cliCommit: cliStamp.cliCommit,\n createdAt: new Date().toISOString(),\n },\n });\n progress(`wrote ${preparedStatePath()}`);\n\n // 8. Delete the builder. The snapshot is an independent resource that\n // survives this (verified live), and its id is already persisted above, so\n // this is best-effort: a failure just leaves the sandbox for Vercel's reaper.\n progress('deleting builder sandbox');\n try {\n await sb.delete();\n progress('builder sandbox deleted');\n } catch (err) {\n progress(\n `builder delete failed (left for Vercel reaper): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n progress(`prepare complete — base snapshot ${snap.snapshotId}`);\n return { snapshotName: snap.snapshotId };\n}\n\nasync function snapshotExists(\n snapshotId: string,\n creds: Partial<{ token: string; teamId: string; projectId: string }>,\n): Promise<boolean> {\n try {\n const snap = await Snapshot.get({ snapshotId, ...creds });\n // `Snapshot.get` resolves even for a deleted/failed snapshot (status field),\n // so a bare \"didn't throw\" wrongly skip-passes a tombstone. Only a 'created'\n // snapshot is bootable — anything else means rebuild.\n return snap.status === 'created';\n } catch {\n return false;\n }\n}\n\nasync function stageAgentConfig(\n sb: SandboxType,\n hostWorkspace: string | undefined,\n log: (line: string) => void,\n): Promise<void> {\n const progress = (s: string) => log(`prepare-vercel: ${s}`);\n progress('staging host agent static config');\n const stagings: Array<{ kind: 'claude' | 'codex' | 'opencode'; tar: StageResult; dest: string }> = [];\n try {\n const claudeTar = await stageClaudeStaticForUpload({ hostWorkspace });\n for (const w of claudeTar.warnings) progress(w);\n if (claudeTar.tarballPath) stagings.push({ kind: 'claude', tar: claudeTar, dest: '/home/vscode/.claude' });\n else await claudeTar.cleanup();\n\n const codexTar = await stageCodexStaticForUpload();\n for (const w of codexTar.warnings) progress(w);\n if (codexTar.tarballPath) stagings.push({ kind: 'codex', tar: codexTar, dest: '/home/vscode/.codex' });\n else await codexTar.cleanup();\n\n const opencodeTar = await stageOpencodeStaticForUpload();\n for (const w of opencodeTar.warnings) progress(w);\n if (opencodeTar.tarballPath) stagings.push({ kind: 'opencode', tar: opencodeTar, dest: '/home/vscode/.local/share/opencode' });\n else await opencodeTar.cleanup();\n\n for (const s of stagings) {\n const remote = `/tmp/agentbox-${s.kind}-static.tar.gz`;\n progress(`uploading ${s.kind} static config`);\n await sb.writeFiles([{ path: remote, content: await readFile(s.tar.tarballPath as string) }]);\n // Extract as vscode so files land owned by the box user. The dest dir\n // already exists (provision.sh's credential-pivot step) — extract into it.\n const extract =\n `sudo -u vscode mkdir -p ${s.dest} && ` +\n `sudo -u vscode tar -xzf ${remote} -C ${s.dest} --no-same-permissions --no-same-owner -m && ` +\n `rm -f ${remote}`;\n const r = await sb.runCommand({ cmd: SHELL, args: ['-lc', extract], sudo: true });\n if (r.exitCode !== 0) {\n progress(`WARN: ${s.kind} static extract failed (exit ${String(r.exitCode)}) — continuing`);\n } else {\n progress(`baked ${s.kind} static config into snapshot`);\n }\n }\n } finally {\n for (const s of stagings) await s.tar.cleanup();\n }\n}\n\n/**\n * Adapt a line-callback to the `Writable` the SDK's `runCommand` streams into.\n * Buffers partial lines so each `onLine` gets a complete line.\n */\nfunction lineSink(onLine: (line: string) => void): Writable {\n let buf = '';\n return new Writable({\n write(chunk: Buffer, _enc: BufferEncoding, cb: () => void) {\n buf += chunk.toString('utf8');\n let nl: number;\n while ((nl = buf.indexOf('\\n')) !== -1) {\n onLine(buf.slice(0, nl));\n buf = buf.slice(nl + 1);\n }\n cb();\n },\n final(cb: () => void) {\n if (buf.length > 0) onLine(buf);\n cb();\n },\n });\n}\n\n/** Provider-level binding used by the CLI's `prepare` command. */\nexport const prepareVercelProvider: NonNullable<Provider['prepare']> = (req) =>\n prepareVercel({\n name: req.name,\n hostWorkspace: req.hostWorkspace ?? process.cwd(),\n force: req.force,\n onLog: req.onLog,\n });\n","/**\n * Resolver for the on-disk files shipped into a fresh Vercel sandbox during\n * `prepareVercel()`. Same idea as the hetzner resolver: a flat list of files to\n * upload via `sandbox.writeFiles`, each resolved from either the staged CLI\n * runtime tree or the monorepo source tree.\n *\n * Lookup order per file:\n * 1. The CLI's staged runtime tree: `<cliRoot>/runtime/vercel/...`.\n * 2. The monorepo source tree (dev fallback) under `packages/`.\n *\n * Any missing file throws a clear error naming the paths tried. Note: no\n * dockerd helper — Vercel can't run nested containers.\n */\n\nimport { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst SELF = dirname(fileURLToPath(import.meta.url));\n\nexport function findStagedCliRuntimeRoot(): string | undefined {\n const candidates = [\n resolve(SELF, '..', 'runtime'),\n resolve(SELF, '..', '..', 'runtime'),\n ];\n for (const c of candidates) {\n if (existsSync(resolve(c, 'vercel', 'scripts', 'provision.sh'))) return c;\n }\n return undefined;\n}\n\nexport interface RuntimeAsset {\n /** Logical name (used in error messages + log lines). */\n name: string;\n /** Absolute path on the box (writeFiles target). */\n remotePath: string;\n /** File mode to apply after upload. */\n remoteMode: number;\n}\n\n/**\n * Where each asset lands inside the sandbox. provision.sh reads them from these\n * fixed paths. The agent/runtime helpers go straight to /usr/local/bin; baked\n * config files to /tmp for provision.sh to `install` into place.\n */\nexport const RUNTIME_ASSETS: readonly RuntimeAsset[] = [\n { name: 'provision.sh', remotePath: '/tmp/agentbox-provision.sh', remoteMode: 0o755 },\n { name: 'agentbox-ctl', remotePath: '/tmp/agentbox-ctl', remoteMode: 0o755 },\n { name: 'agentbox-vnc-start', remotePath: '/tmp/agentbox-vnc-start', remoteMode: 0o755 },\n { name: 'agentbox-checkpoint-cleanup', remotePath: '/tmp/agentbox-checkpoint-cleanup', remoteMode: 0o755 },\n { name: 'agentbox-open', remotePath: '/tmp/agentbox-open', remoteMode: 0o755 },\n { name: 'gh-shim', remotePath: '/tmp/agentbox-gh-shim', remoteMode: 0o755 },\n { name: 'git-shim', remotePath: '/tmp/agentbox-git-shim', remoteMode: 0o755 },\n { name: 'custom-system-CLAUDE.md', remotePath: '/tmp/agentbox-custom-CLAUDE.md', remoteMode: 0o644 },\n { name: 'claude-managed-settings.json', remotePath: '/tmp/agentbox-managed-settings.json', remoteMode: 0o644 },\n { name: 'agentbox-codex-hooks.json', remotePath: '/tmp/agentbox-codex-hooks.json', remoteMode: 0o644 },\n { name: 'agentbox-setup-skill.md', remotePath: '/tmp/agentbox-setup-skill.md', remoteMode: 0o644 },\n] as const;\n\nexport interface ResolvedAsset extends RuntimeAsset {\n localPath: string;\n}\n\nexport function candidatesFor(\n name: string,\n opts: { cliRuntimeRoot?: string; repoRoot?: string } = {},\n): string[] {\n const cliRoot = opts.cliRuntimeRoot;\n const monorepo = opts.repoRoot ?? guessRepoRoot();\n\n const monorepoRelative: Record<string, string[]> = {\n 'provision.sh': ['packages/sandbox-vercel/scripts/provision.sh'],\n 'agentbox-ctl': ['packages/ctl/dist/bin.cjs'],\n 'agentbox-vnc-start': ['packages/sandbox-docker/scripts/agentbox-vnc-start'],\n 'agentbox-checkpoint-cleanup': ['packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup'],\n 'agentbox-open': ['packages/sandbox-docker/scripts/agentbox-open'],\n 'gh-shim': ['packages/sandbox-docker/scripts/gh-shim'],\n 'git-shim': ['packages/sandbox-docker/scripts/git-shim'],\n 'custom-system-CLAUDE.md': ['packages/sandbox-vercel/scripts/custom-system-CLAUDE.md'],\n 'claude-managed-settings.json': ['packages/sandbox-docker/scripts/claude-managed-settings.json'],\n 'agentbox-codex-hooks.json': ['packages/sandbox-docker/scripts/agentbox-codex-hooks.json'],\n 'agentbox-setup-skill.md': ['apps/cli/share/agentbox-setup/SKILL.md'],\n };\n\n const cliRelative: Record<string, string[]> = {\n 'provision.sh': ['vercel/scripts/provision.sh'],\n 'agentbox-ctl': ['vercel/ctl.cjs'],\n 'agentbox-vnc-start': ['vercel/agentbox-vnc-start', 'docker/packages/sandbox-docker/scripts/agentbox-vnc-start'],\n 'agentbox-checkpoint-cleanup': ['vercel/agentbox-checkpoint-cleanup', 'docker/packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup'],\n 'agentbox-open': ['vercel/agentbox-open', 'docker/packages/sandbox-docker/scripts/agentbox-open'],\n 'gh-shim': ['vercel/gh-shim', 'docker/packages/sandbox-docker/scripts/gh-shim'],\n 'git-shim': ['vercel/git-shim', 'docker/packages/sandbox-docker/scripts/git-shim'],\n 'custom-system-CLAUDE.md': ['vercel/custom-system-CLAUDE.md'],\n 'claude-managed-settings.json': ['vercel/claude-managed-settings.json', 'docker/packages/sandbox-docker/scripts/claude-managed-settings.json'],\n 'agentbox-codex-hooks.json': ['vercel/agentbox-codex-hooks.json', 'docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json'],\n 'agentbox-setup-skill.md': ['vercel/agentbox-setup-skill.md', 'docker/apps/cli/share/agentbox-setup/SKILL.md'],\n };\n\n const out: string[] = [];\n if (cliRoot) {\n for (const rel of cliRelative[name] ?? []) out.push(resolve(cliRoot, rel));\n }\n for (const rel of monorepoRelative[name] ?? []) out.push(resolve(monorepo, rel));\n return out;\n}\n\nexport function resolveRuntimeAssets(\n opts: { cliRuntimeRoot?: string; repoRoot?: string } = {},\n): ResolvedAsset[] {\n const out: ResolvedAsset[] = [];\n const missing: Array<{ name: string; tried: string[] }> = [];\n for (const asset of RUNTIME_ASSETS) {\n const cands = candidatesFor(asset.name, opts);\n const hit = cands.find((p) => existsSync(p));\n if (!hit) {\n missing.push({ name: asset.name, tried: cands });\n continue;\n }\n out.push({ ...asset, localPath: hit });\n }\n if (missing.length > 0) {\n const lines = missing.flatMap((m) => [` - ${m.name}: tried`, ...m.tried.map((p) => ` ${p}`)]);\n throw new Error(\n `vercel: could not resolve runtime assets needed to bake the base snapshot:\\n` +\n lines.join('\\n') +\n `\\n\\nIf running from the monorepo, ensure \\`pnpm -w build\\` has run so packages/ctl/dist/bin.cjs exists.`,\n );\n }\n return out;\n}\n\nfunction guessRepoRoot(): string {\n let cur = SELF;\n for (let i = 0; i < 8; i++) {\n if (existsSync(resolve(cur, 'pnpm-workspace.yaml'))) return cur;\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n return SELF;\n}\n","/**\n * `buildVercelAttach` — the Vercel provider's override of `Provider.buildAttach`.\n *\n * Vercel has no SSH, so the cloud scaffold's `ssh … -t '<cmd>'` argv is unusable.\n * Instead we drive the official Vercel Sandbox CLI (`sbx`/`sandbox`), which has a\n * real interactive PTY (`sbx exec -i`) and streams non-interactive output live —\n * giving a proper terminal with none of the old send-keys/capture-pane polling.\n *\n * Argv shape (validated against sbx 3.0.1):\n * sbx exec --sudo [-i] --project <p> --scope <team> <name>\n * -- sudo -u vscode -H bash -lc '<inner>'\n *\n * Notes:\n * - The sandbox's default exec user is `vercel-sandbox`; we pass `--sudo` (runs\n * as root) and then `sudo -u vscode -H` so tmux/agents run as the box user in\n * /workspace. Passing `sudo -u vscode …` directly as sbx's argv (not wrapped\n * in an outer `bash -lc`) avoids a double-`bash -lc` re-parse.\n * - `-i` only for interactive shell/agent attaches; detached pre-start and logs\n * run non-interactively (live stdout stream).\n * - The access token is passed via the child env (`VERCEL_AUTH_TOKEN`), never in\n * argv, so it can't leak through `ps`. project/scope are not secret → flags.\n * - `<inner>` is the shared cloud `renderInnerCommand` (same tmux ensure +\n * footer-aware config + `exec tmux attach` used by hetzner/daytona).\n */\n\nimport {\n type AttachKind,\n type AttachSpec,\n type BoxRecord,\n type BuildAttachOptions,\n} from '@agentbox/core';\nimport { renderInnerCommand } from '@agentbox/sandbox-cloud';\nimport { detectSbx } from './sbx-cli.js';\nimport { ensureFreshCredentials, resolveCredentials } from './sdk.js';\n\nexport async function buildVercelAttach(\n box: BoxRecord,\n kind: AttachKind,\n opts?: BuildAttachOptions,\n): Promise<AttachSpec> {\n const sandboxId = box.cloud?.sandboxId;\n if (!sandboxId) {\n throw new Error(`vercel box ${box.name} has no sandboxId — record is malformed`);\n }\n\n const det = await detectSbx();\n if (!det.installed || !det.bin) {\n throw new Error(\n 'Vercel interactive attach needs the Vercel `sandbox` CLI — run ' +\n '`agentbox vercel login` (it installs it) or `npm install -g sandbox`.',\n );\n }\n\n await ensureFreshCredentials();\n const { token, teamId, projectId } = resolveCredentials();\n\n // Interactive (real PTY) only for live shell/agent attaches. Detached\n // pre-start and logs stream non-interactively.\n const interactive = (kind === 'shell' || kind === 'agent') && !opts?.detached;\n\n // `sbx exec` (unlike `ssh -t`) forwards neither TERM nor the locale, so the\n // box session lands in TERM=unknown + an ASCII (POSIX) locale — tmux then\n // collapses Claude Code's Unicode glyphs (logo, spinner, box-drawing) to `_`.\n // Force a UTF-8 locale + a 256color TERM (matching the host PTY wrapper) so\n // the tmux server + the agent it spawns render correctly.\n const envPrelude = 'export LANG=C.UTF-8 LC_ALL=C.UTF-8 TERM=xterm-256color; ';\n const inner = envPrelude + renderInnerCommand(kind, opts);\n\n const argv = [\n det.bin,\n 'exec',\n '--sudo',\n ...(interactive ? ['-i'] : []),\n '--project',\n projectId,\n '--scope',\n teamId,\n sandboxId,\n '--',\n 'sudo',\n '-u',\n 'vscode',\n '-H',\n 'bash',\n '-lc',\n inner,\n ];\n\n return { argv, env: { VERCEL_AUTH_TOKEN: token } };\n}\n"],"mappings":"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","names":["readFile","creds","readFile"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../packages/sandbox-daytona/src/index.ts","../../../packages/sandbox-daytona/src/prepare.ts","../../../packages/sandbox-daytona/src/prepared-state.ts","../../../packages/sandbox-daytona/src/status.ts"],"sourcesContent":["/**\n * The Daytona Cloud sandbox provider. A thin `CloudBackend` over\n * `@daytonaio/sdk`, composed via `@agentbox/sandbox-cloud`'s `createCloudProvider`\n * for everything provider-agnostic (workspace seeding, ctl launch, state).\n */\n\nimport type { Provider } from '@agentbox/core';\nimport { createCloudProvider } from '@agentbox/sandbox-cloud';\nimport { daytonaBackend, DEFAULT_BOX_IMAGE_REF } from './backend.js';\nimport { prepareDaytona } from './prepare.js';\n\nconst cloudProvider = createCloudProvider(daytonaBackend, {\n defaultResources: { cpu: 2, memory: 4, disk: 8 },\n});\n\nexport const daytonaProvider: Provider = {\n ...cloudProvider,\n prepare: prepareDaytona,\n};\n\nexport { daytonaBackend, DEFAULT_BOX_IMAGE_REF };\nexport { resolveDockerfileContext, type DockerfileContext } from './dockerfile-context.js';\nexport { ensureDaytonaEnvLoaded } from './env-loader.js';\n// Called by the CLI provider registry to gate first-run interactive setup.\n// Plain async function — no commander surface — so adding it here doesn't\n// pull commander/clack into consumers' type graphs. The full CLI command\n// lives at the `./cli` subpath export.\nexport { ensureDaytonaCredentials } from './credentials.js';\nexport type { EnsureDaytonaCredentialsOptions } from './credentials.js';\nexport {\n getDaytonaStatus,\n type DaytonaStatus,\n type DaytonaSnapshotSummary,\n type DaytonaVolumeSummary,\n} from './status.js';\n","/**\n * Daytona-side implementation of the `Provider.prepare` hook (`agentbox\n * prepare --provider daytona`). One-time, user-triggered:\n *\n * 1. Stage filtered tarballs of the host's `~/.claude`, `~/.codex`, and\n * `~/.local/share/opencode` static config (no auth tokens — those go on\n * the per-org `agentbox-credentials` volume at create time).\n * 2. Build a layered Daytona `Image`: start from `Dockerfile.box`, then\n * `.addLocalFile()` each staged tarball + `.runCommands()` to extract\n * them into the right paths inside the image.\n * 3. Call `daytona.snapshot.create({ name, image }, { onLogs })` — Daytona\n * runs the build server-side, registers the result as an org-scoped\n * named snapshot, and returns when it's `active`.\n *\n * Replaces the old `agentbox daytona publish-snapshot` flow that\n * provisioned a sandbox + ran an in-sandbox bake + called the broken\n * `_experimental_createSnapshot`. The new path never provisions a sandbox.\n *\n * Source of truth for the public API:\n * https://www.daytona.io/docs/en/snapshots/\n */\n\nimport { Image } from '@daytonaio/sdk';\nimport type { PrepareOptions, PrepareResult } from '@agentbox/core';\nimport {\n stageClaudeStaticForUpload,\n stageCodexStaticForUpload,\n stageOpencodeStaticForUpload,\n type StageResult,\n} from '@agentbox/sandbox-cloud';\nimport { getClient } from './backend.js';\nimport { resolveDaytonaCustomClaudeMd, resolveDockerfileContext } from './dockerfile-context.js';\nimport { ensureDaytonaEnvLoaded } from './env-loader.js';\nimport {\n computeDaytonaContextFingerprint,\n preparedMatches,\n readPreparedDaytonaState,\n writePreparedDaytonaState,\n} from './prepared-state.js';\n\n/**\n * Default snapshot name. Keyed on the first 12 chars of the build-context\n * fingerprint so identical content produces the same snapshot name across\n * machines / CLI runs (idempotent): if the named snapshot already exists\n * on Daytona, prepare can short-circuit without uploading the build\n * context again. Falls back to a timestamp when fingerprinting fails\n * (partial dev rebuild).\n */\nfunction defaultSnapshotName(fingerprint: string | null): string {\n if (fingerprint) return `agentbox-base-${fingerprint.slice(0, 12)}`;\n return `agentbox-base-${Math.floor(Date.now() / 1000).toString()}`;\n}\n\ninterface AgentStage {\n kind: 'claude' | 'codex' | 'opencode';\n /** Path inside the image build that the tarball is uploaded to. */\n remoteTar: string;\n /** Path the image build extracts the tarball into. */\n extractDir: string;\n staged: StageResult;\n}\n\n/**\n * Stage the three agents' static tarballs in parallel. Each `StageResult`'s\n * `cleanup()` must be called by the caller, after the image build picks the\n * file up.\n */\nasync function stageAllAgentStatic(opts: { hostWorkspace?: string }): Promise<AgentStage[]> {\n const [claudeStaged, codexStaged, opencodeStaged] = await Promise.all([\n stageClaudeStaticForUpload({ hostWorkspace: opts.hostWorkspace }),\n stageCodexStaticForUpload(),\n stageOpencodeStaticForUpload(),\n ]);\n return [\n {\n kind: 'claude',\n remoteTar: '/tmp/agentbox-seed-claude.tar.gz',\n extractDir: '/home/vscode/.claude',\n staged: claudeStaged,\n },\n {\n kind: 'codex',\n remoteTar: '/tmp/agentbox-seed-codex.tar.gz',\n extractDir: '/home/vscode/.codex',\n staged: codexStaged,\n },\n {\n kind: 'opencode',\n remoteTar: '/tmp/agentbox-seed-opencode.tar.gz',\n extractDir: '/home/vscode/.local/share/opencode',\n staged: opencodeStaged,\n },\n ];\n}\n\n/**\n * Run `agentbox prepare --provider daytona`. Returns `{ snapshotName }` on\n * success so the CLI can pin it into the project config.\n */\nexport async function prepareDaytona(opts: PrepareOptions): Promise<PrepareResult> {\n ensureDaytonaEnvLoaded();\n const log = opts.onLog ?? (() => {});\n\n // Fingerprint the build context first so we can (a) name the snapshot\n // deterministically and (b) detect cache hits against the recorded\n // prepared state. Computed before staging so an early `null` (partial\n // dev rebuild) doesn't waste a tar staging cycle.\n const fingerprint = await computeDaytonaContextFingerprint();\n const snapshotName =\n opts.name ?? defaultSnapshotName(fingerprint?.contextSha256 ?? null);\n\n const prepared = readPreparedDaytonaState();\n if (\n !opts.force &&\n fingerprint &&\n preparedMatches(prepared, fingerprint.contextSha256)\n ) {\n // Confirm the snapshot still exists on Daytona before short-circuiting.\n // A \"yes locally, no on the server\" mismatch must rebuild.\n try {\n const existing = await getClient().snapshot.get(\n prepared?.base?.imageRef ?? snapshotName,\n );\n if (existing?.name) {\n log(\n `daytona snapshot '${existing.name}' up to date ` +\n `(fingerprint ${fingerprint.contextSha256.slice(0, 12)}) — skipping rebuild ` +\n `(pass --force to override)`,\n );\n return { snapshotName: existing.name };\n }\n log(\n `recorded snapshot '${prepared?.base?.imageRef ?? snapshotName}' not found on Daytona; rebuilding`,\n );\n } catch {\n log(\n `recorded snapshot lookup failed; rebuilding (pass --force to silence)`,\n );\n }\n } else if (!opts.force && fingerprint && prepared?.base?.contextSha256) {\n log(\n `daytona build context changed (was ${prepared.base.contextSha256.slice(0, 12)}, ` +\n `now ${fingerprint.contextSha256.slice(0, 12)}); rebuilding snapshot`,\n );\n }\n\n const ctx = resolveDockerfileContext();\n if (!ctx) {\n throw new Error(\n 'could not locate AgentBox Dockerfile.box build context for the Daytona snapshot. ' +\n 'Set AGENTBOX_DOCKER_CONTEXT to the directory containing Dockerfile.box.',\n );\n }\n\n const daytonaClaudeMd = resolveDaytonaCustomClaudeMd();\n if (!daytonaClaudeMd) {\n throw new Error(\n 'could not locate packages/sandbox-daytona/scripts/custom-system-CLAUDE.md ' +\n '(or its staged runtime/daytona/ copy). Ensure `pnpm -w build` ran so the ' +\n 'CLI staging populated runtime/daytona/.',\n );\n }\n\n const stages = await stageAllAgentStatic({ hostWorkspace: opts.hostWorkspace });\n // Surface staging warnings (codex Keychain landmine, etc.) before the\n // longer build kicks off.\n for (const s of stages) {\n for (const w of s.staged.warnings) log(w);\n }\n\n try {\n let image: Image = Image.fromDockerfile(ctx.dockerfile);\n\n // Overlay the daytona-specific /etc/claude-code/CLAUDE.md on top of the\n // docker-shaped one baked by Dockerfile.box. Daytona boxes have no host\n // .git/ bind-mount, so the in-box hint needs daytona-specific git wording.\n image = image.addLocalFile(daytonaClaudeMd, '/tmp/agentbox-custom-CLAUDE.md');\n const extractCmds: string[] = [\n 'install -m 0644 /tmp/agentbox-custom-CLAUDE.md /etc/claude-code/CLAUDE.md',\n 'rm -f /tmp/agentbox-custom-CLAUDE.md',\n ];\n\n // For each agent whose stage produced a tarball, add the file to the\n // image build context and append a single tar-extract + chown.\n const usable = stages.filter((s) => s.staged.tarballPath !== null);\n for (const s of usable) {\n image = image.addLocalFile(s.staged.tarballPath as string, s.remoteTar);\n extractCmds.push(`mkdir -p ${s.extractDir}`);\n extractCmds.push(`tar -xzf ${s.remoteTar} -C ${s.extractDir}`);\n }\n if (usable.length > 0) {\n // One final pass: own the extracted trees as the box user, then drop the\n // staging tarballs (no point shipping them twice in the image layer).\n extractCmds.push(\n 'chown -R vscode:vscode /home/vscode/.claude /home/vscode/.codex /home/vscode/.local',\n );\n extractCmds.push('rm -f /tmp/agentbox-seed-*.tar.gz');\n }\n // Dockerfile.box ends with `USER vscode`. Switch to root for the\n // install/tar/chown/rm pass — COPYed files are root-owned in /tmp (sticky\n // bit), chown -R on /home/vscode/.* only works as root, and\n // /etc/claude-code is root-owned. Switch back to vscode so the image\n // keeps its default-user invariant.\n image = image\n .dockerfileCommands(['USER root'])\n .runCommands(...extractCmds)\n .dockerfileCommands(['USER vscode']);\n\n const client = getClient();\n log(`creating Daytona snapshot '${snapshotName}'…`);\n const snapshot = await client.snapshot.create(\n { name: snapshotName, image },\n {\n onLogs: (chunk: string) => log(String(chunk).split('\\n').filter(Boolean).join(' ')),\n },\n );\n log(`snapshot '${snapshot.name}' is ${snapshot.state ?? 'created'}`);\n if (fingerprint) {\n writePreparedDaytonaState({\n snapshotName: snapshot.name ?? snapshotName,\n contextSha256: fingerprint.contextSha256,\n });\n log(\n `recorded daytona-prepared.json (fingerprint ${fingerprint.contextSha256.slice(0, 12)})`,\n );\n }\n return { snapshotName: snapshot.name ?? snapshotName };\n } finally {\n await Promise.all(stages.map((s) => s.staged.cleanup()));\n }\n}\n","/**\n * Daytona provider's `~/.agentbox/daytona-prepared.json` reader/writer +\n * build-context fingerprinting for the org-scoped base snapshot.\n *\n * The daytona prepare bakes the docker `Dockerfile.box` plus a daytona-\n * specific `custom-system-CLAUDE.md` overlay. The fingerprint covers both\n * — same canonical file map as the docker provider for the dockerfile\n * inputs, plus one extra entry for the daytona overlay.\n */\n\nimport { existsSync } from 'node:fs';\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 { resolveDaytonaCustomClaudeMd, resolveDockerfileContext } from './dockerfile-context.js';\n\nconst SCHEMA = 1 as const;\n\nexport type PreparedDaytonaState = PreparedBaseSnapshot<string, never>;\n\n/**\n * Resolve every file that influences the daytona base snapshot: the docker\n * build context (shared map from sandbox-core) plus the daytona-specific\n * CLAUDE.md overlay added by `Image.addLocalFile` in `prepare.ts`.\n *\n * Returns `null` if any file is missing — callers degrade to \"always\n * rebuild\" rather than stamp a misleading fingerprint.\n */\nexport function resolveDaytonaContextFiles(): ContextFile[] | null {\n const ctx = resolveDockerfileContext();\n if (!ctx) return null;\n // sandbox-daytona's package root = parent of src/ or parent of dist/.\n // Mirrors the `resolve(here, '..', '..', '..')` walk in dockerfile-context.ts.\n const here = dirname(fileURLToPath(import.meta.url));\n const packageRoot = resolve(here, '..');\n const monorepoRoot = resolve(here, '..', '..', '..');\n // Docker's dev fallback is anchored at sandbox-docker's root, not\n // sandbox-daytona's, so we pass the monorepo root and prefix the dev\n // paths to walk into packages/sandbox-docker/.\n //\n // Simpler: just point devRoot at sandbox-docker's package root when it\n // exists (legacy monorepo layout).\n const dockerPackageRoot = resolve(monorepoRoot, 'packages', 'sandbox-docker');\n const docker = resolveContextFilesFrom(DOCKER_CONTEXT_FILE_MAP, {\n contextDir: ctx.context,\n devRoot: existsSync(dockerPackageRoot) ? dockerPackageRoot : packageRoot,\n });\n if (!docker) return null;\n const overlay = resolveDaytonaCustomClaudeMd();\n if (!overlay) return null;\n return [\n ...docker,\n // Daytona-specific overlay: separate logical name so a docker/daytona\n // CLAUDE.md drift produces different fingerprints (the daytona snapshot\n // contains both files in distinct locations).\n { rel: 'daytona/custom-system-CLAUDE.md', abs: overlay },\n ];\n}\n\nexport interface DaytonaFingerprint {\n contextSha256: string;\n files: ContextFile[];\n}\n\nexport async function computeDaytonaContextFingerprint(): Promise<DaytonaFingerprint | null> {\n const files = resolveDaytonaContextFiles();\n if (!files) return null;\n return { contextSha256: await computeContextSha256(files), files };\n}\n\nexport function readPreparedDaytonaState(): PreparedDaytonaState | null {\n const raw = readPreparedStateRaw('daytona');\n if (raw === null || typeof raw !== 'object') return null;\n const parsed = raw as Partial<PreparedDaytonaState>;\n if (parsed.schema !== SCHEMA) return null;\n return { schema: SCHEMA, base: parsed.base };\n}\n\nexport function writePreparedDaytonaState(opts: {\n snapshotName: string;\n contextSha256: string;\n}): void {\n const stamp = readCliStamp();\n const state: PreparedDaytonaState = {\n schema: SCHEMA,\n base: {\n imageRef: opts.snapshotName,\n contextSha256: opts.contextSha256,\n cliVersion: stamp.cliVersion,\n cliCommit: stamp.cliCommit,\n createdAt: new Date().toISOString(),\n },\n };\n writePreparedStateRaw('daytona', state);\n}\n\nexport function preparedMatches(\n state: PreparedDaytonaState | null,\n current: string,\n): boolean {\n return state?.base?.contextSha256 === current;\n}\n","/**\n * Read-only status helpers for `agentbox prepare` (no-args mode). Surfaces\n * the user-facing inventory of agentbox-owned base images / snapshots /\n * volumes on the configured Daytona org so the user can see at a glance\n * what's already prepared and what isn't.\n *\n * Daytona-side state lives in two places:\n * - **Snapshots** — built by `agentbox prepare --provider daytona`. Listed\n * filtered to `agentbox*` so we don't surface unrelated org snapshots.\n * - **Volumes** — the per-org `agentbox-credentials` volume created lazily\n * by `ensureAgentVolumesForCloud` on first `agentbox create --provider\n * daytona`.\n *\n * All calls swallow auth/network errors and return an empty section — the\n * status command must work for users who don't have Daytona configured.\n */\n\nimport { ensureDaytonaEnvLoaded } from './env-loader.js';\nimport { getClient } from './backend.js';\n\nexport interface DaytonaSnapshotSummary {\n name: string;\n state?: string;\n /** Snapshot size in GB, as reported by Daytona (may be undefined for non-`active` states). */\n sizeGb?: number;\n createdAt?: string;\n errorReason?: string;\n}\n\nexport interface DaytonaVolumeSummary {\n name: string;\n id: string;\n state?: string;\n createdAt?: string;\n lastUsedAt?: string;\n}\n\nexport interface DaytonaStatus {\n /** True when Daytona credentials are present + the SDK could connect. */\n configured: boolean;\n /** Snapshots whose name starts with `agentbox` (case-insensitive). */\n snapshots: DaytonaSnapshotSummary[];\n /** Volumes whose name starts with `agentbox` (case-insensitive). */\n volumes: DaytonaVolumeSummary[];\n /** Non-fatal explanation when `configured` is false. */\n reason?: string;\n}\n\nfunction isAgentboxName(name: unknown): boolean {\n return typeof name === 'string' && name.toLowerCase().startsWith('agentbox');\n}\n\n/**\n * Collect a read-only summary of agentbox-owned snapshots + volumes on the\n * Daytona org. Never throws — failure paths return `configured: false` with\n * a one-line reason.\n */\nexport async function getDaytonaStatus(): Promise<DaytonaStatus> {\n try {\n ensureDaytonaEnvLoaded();\n } catch (err) {\n return {\n configured: false,\n snapshots: [],\n volumes: [],\n reason: err instanceof Error ? err.message : String(err),\n };\n }\n\n let client;\n try {\n client = getClient();\n } catch (err) {\n return {\n configured: false,\n snapshots: [],\n volumes: [],\n reason: err instanceof Error ? err.message.split('\\n')[0] : String(err),\n };\n }\n\n const snapshots: DaytonaSnapshotSummary[] = [];\n const volumes: DaytonaVolumeSummary[] = [];\n let reason: string | undefined;\n\n try {\n const list = await client.snapshot.list();\n const items = (list as { items?: unknown[] }).items ?? (Array.isArray(list) ? list : []);\n for (const s of items) {\n const dto = s as { name?: unknown; state?: unknown; size?: unknown; createdAt?: unknown; errorReason?: unknown };\n if (!isAgentboxName(dto.name)) continue;\n snapshots.push({\n name: dto.name as string,\n state: typeof dto.state === 'string' ? dto.state : undefined,\n sizeGb: typeof dto.size === 'number' ? dto.size : undefined,\n createdAt: typeof dto.createdAt === 'string' ? dto.createdAt : undefined,\n errorReason: typeof dto.errorReason === 'string' ? dto.errorReason : undefined,\n });\n }\n } catch (err) {\n reason = `snapshot list failed: ${err instanceof Error ? err.message.split('\\n')[0] : String(err)}`;\n }\n\n try {\n const list = await client.volume.list();\n const items: unknown[] = Array.isArray(list)\n ? list\n : ((list as { items?: unknown[] }).items ?? []);\n for (const v of items) {\n const dto = v as { name?: unknown; id?: unknown; state?: unknown; createdAt?: unknown; lastUsedAt?: unknown };\n if (!isAgentboxName(dto.name)) continue;\n volumes.push({\n name: dto.name as string,\n id: typeof dto.id === 'string' ? dto.id : '',\n state: typeof dto.state === 'string' ? dto.state : undefined,\n createdAt: typeof dto.createdAt === 'string' ? dto.createdAt : undefined,\n lastUsedAt: typeof dto.lastUsedAt === 'string' ? dto.lastUsedAt : undefined,\n });\n }\n } catch (err) {\n const msg = `volume list failed: ${err instanceof Error ? err.message.split('\\n')[0] : String(err)}`;\n reason = reason ? `${reason}; ${msg}` : msg;\n }\n\n return {\n configured: true,\n snapshots: snapshots.sort((a, b) => (b.createdAt ?? '').localeCompare(a.createdAt ?? '')),\n volumes: volumes.sort((a, b) => a.name.localeCompare(b.name)),\n reason,\n };\n}\n"],"mappings":"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","names":[]}
|