@madarco/agentbox 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/{_cloud-attach-T727ZPRV.js → _cloud-attach-ZXBCNWJX.js} +4 -4
  2. package/dist/{chunk-67N47KUS.js → chunk-BXQMIEHC.js} +106 -31
  3. package/dist/chunk-BXQMIEHC.js.map +1 -0
  4. package/dist/{chunk-FODMEHD3.js → chunk-GU5LW4B5.js} +341 -25
  5. package/dist/chunk-GU5LW4B5.js.map +1 -0
  6. package/dist/{chunk-BGK32PZE.js → chunk-KL36BRN4.js} +2 -2
  7. package/dist/chunk-KL36BRN4.js.map +1 -0
  8. package/dist/chunk-MTVI44DW.js +662 -0
  9. package/dist/chunk-MTVI44DW.js.map +1 -0
  10. package/dist/{chunk-6OZDFNBF.js → chunk-NCJP5MTN.js} +201 -44
  11. package/dist/chunk-NCJP5MTN.js.map +1 -0
  12. package/dist/{dist-LOZBWMBF.js → dist-32EZBYG4.js} +9 -3
  13. package/dist/{dist-L4LCG5SJ.js → dist-CX5CGVEB.js} +4 -4
  14. package/dist/{dist-ZODPD2I6.js → dist-GDHP34ZK.js} +8 -10
  15. package/dist/dist-GDHP34ZK.js.map +1 -0
  16. package/dist/dist-XML54CNB.js +849 -0
  17. package/dist/dist-XML54CNB.js.map +1 -0
  18. package/dist/index.js +636 -340
  19. package/dist/index.js.map +1 -1
  20. package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js → prepared-state-CL4CWXQA-H5THETIM.js} +2 -2
  21. package/package.json +7 -5
  22. package/runtime/docker/packages/ctl/dist/bin.cjs +98 -29
  23. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
  24. package/runtime/hetzner/agentbox-vnc-start +15 -1
  25. package/runtime/hetzner/ctl.cjs +98 -29
  26. package/runtime/relay/bin.cjs +229 -37
  27. package/runtime/vercel/agentbox-checkpoint-cleanup +52 -0
  28. package/runtime/vercel/agentbox-codex-hooks.json +68 -0
  29. package/runtime/vercel/agentbox-open +28 -0
  30. package/runtime/vercel/agentbox-setup-skill.md +196 -0
  31. package/runtime/vercel/agentbox-vnc-start +91 -0
  32. package/runtime/vercel/claude-managed-settings.json +115 -0
  33. package/runtime/vercel/ctl.cjs +23466 -0
  34. package/runtime/vercel/custom-system-CLAUDE.md +50 -0
  35. package/runtime/vercel/gh-shim +263 -0
  36. package/runtime/vercel/git-shim +131 -0
  37. package/runtime/vercel/scripts/provision.sh +274 -0
  38. package/dist/chunk-67N47KUS.js.map +0 -1
  39. package/dist/chunk-6OZDFNBF.js.map +0 -1
  40. package/dist/chunk-BGK32PZE.js.map +0 -1
  41. package/dist/chunk-FODMEHD3.js.map +0 -1
  42. package/dist/dist-ZODPD2I6.js.map +0 -1
  43. /package/dist/{_cloud-attach-T727ZPRV.js.map → _cloud-attach-ZXBCNWJX.js.map} +0 -0
  44. /package/dist/{dist-LOZBWMBF.js.map → dist-32EZBYG4.js.map} +0 -0
  45. /package/dist/{dist-L4LCG5SJ.js.map → dist-CX5CGVEB.js.map} +0 -0
  46. /package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js.map → prepared-state-CL4CWXQA-H5THETIM.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../packages/sandbox-vercel/src/env-loader.ts","../../../packages/sandbox-vercel/src/sbx-cli.ts","../../../packages/sandbox-vercel/src/credentials.ts","../../../packages/sandbox-vercel/src/cli-store.ts","../../../packages/sandbox-vercel/src/sdk.ts","../../../packages/sandbox-vercel/src/vercel-rest.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { resolve } from 'node:path';\n\n/**\n * Vercel env auto-loader. The `@vercel/sandbox` SDK reads `VERCEL_OIDC_TOKEN`\n * from `process.env`; for the access-token path we read `VERCEL_TOKEN` +\n * `VERCEL_TEAM_ID` + `VERCEL_PROJECT_ID` and thread them into every SDK call as\n * explicit `Credentials`. We pull all of these in from `~/.agentbox/secrets.env`\n * (written by `agentbox vercel login`) so the SDK Just Works after a one-time\n * login — exactly the daytona/hetzner model.\n *\n * Lookup order (first wins; process.env is never overwritten):\n * 1. `process.env` (already set in the shell).\n * 2. `~/.agentbox/secrets.env` — written by `agentbox vercel login`.\n *\n * Project-level `.env` / `.env.local` are intentionally NOT consulted: those\n * files belong to the app code being developed, and a `VERCEL_*` value there\n * (e.g. a `vercel env pull` OIDC token, or the app's own deploy token) is meant\n * for in-box code, not for the host CLI to harvest and provision sandboxes with.\n * Put host credentials in `~/.agentbox/secrets.env` (or the shell env).\n *\n * Only Vercel-prefixed keys are imported; the rest of the file is left alone.\n * Idempotent and side-effect-free after the first call.\n */\nconst VERCEL_KEYS = [\n 'VERCEL_OIDC_TOKEN',\n 'VERCEL_TOKEN',\n 'VERCEL_TEAM_ID',\n 'VERCEL_PROJECT_ID',\n // Marker for CLI-login mode (`agentbox vercel login` → `sandbox login`). The\n // access token is NOT stored here — it's read live from the Vercel CLI store;\n // only this marker + team/project ids are persisted.\n 'VERCEL_AUTH_SOURCE',\n] as const;\n\nlet loaded = false;\n\nexport function ensureVercelEnvLoaded(): void {\n if (loaded) return;\n loaded = true;\n importVercelFromFile(resolve(homedir(), '.agentbox', 'secrets.env'), VERCEL_KEYS);\n}\n\n/**\n * Force a re-read of `~/.agentbox/secrets.env`. Used by the interactive\n * `agentbox vercel login` flow after it persists the credential trio, so the\n * same process can pick it up without a restart.\n */\nexport function reloadVercelEnv(): void {\n loaded = false;\n ensureVercelEnvLoaded();\n}\n\nfunction importVercelFromFile(path: string, keys: readonly string[]): void {\n if (!existsSync(path)) return;\n let body: string;\n try {\n body = readFileSync(path, 'utf8');\n } catch {\n return;\n }\n const parsed = parseEnvFile(body);\n for (const key of keys) {\n if (process.env[key] !== undefined) continue;\n const value = parsed[key];\n if (typeof value === 'string') {\n process.env[key] = value;\n }\n }\n}\n\n/**\n * Minimal `.env` parser: handles `KEY=value`, `KEY=\"value\"`, `KEY='value'`,\n * `export KEY=value`, blank lines, and `#` comments. No variable\n * interpolation — predictable over feature-complete (matches the daytona\n * loader's behavior).\n */\nexport function parseEnvFile(body: string): Record<string, string> {\n const out: Record<string, string> = {};\n for (const rawLine of body.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (line.length === 0 || line.startsWith('#')) continue;\n const stripped = line.startsWith('export ') ? line.slice('export '.length) : line;\n const eq = stripped.indexOf('=');\n if (eq <= 0) continue;\n const key = stripped.slice(0, eq).trim();\n let value = stripped.slice(eq + 1).trim();\n if (\n value.length >= 2 &&\n ((value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\")))\n ) {\n value = value.slice(1, -1);\n }\n out[key] = value;\n }\n return out;\n}\n","/**\n * Driver for the official Vercel Sandbox CLI (`sandbox` / `sbx`, installed via\n * `npm i -g sandbox`). AgentBox's CLI-login auth mode uses it for two things:\n * - `loginSbx` — run `sandbox login` interactively so the user completes\n * the browser OAuth and the CLI writes its own credential store.\n * - `refreshSbxToken` — run a cheap read command (`sandbox list`) which makes\n * the CLI lazily refresh its access token from its stored refresh token\n * when the token is stale. Verified non-interactive: a stale token is\n * refreshed without opening a browser.\n *\n * Mirrors the probe/install patterns in sandbox-docker/src/portless.ts: execa\n * with `reject:false` for never-throw probes, spawnSync with inherited stdio for\n * the interactive login.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { execa } from 'execa';\n\n/**\n * Binaries the Sandbox CLI installs. `sbx` is the short alias; we prefer it but\n * fall back to `sandbox`. Pinned here so a future rename is a one-line fix.\n */\nconst SBX_BINS = ['sbx', 'sandbox'] as const;\n\nexport interface SbxState {\n /** A Sandbox CLI binary resolved on PATH and answered `--version`. */\n installed: boolean;\n /** The binary name that answered (`sbx` or `sandbox`), when installed. */\n bin?: string;\n /** Version string, when installed. */\n version?: string;\n}\n\nlet cached: SbxState | null = null;\n\n/**\n * Probe the host for the Sandbox CLI. Cached per-process (install state can't\n * change mid-command); `resetSbxCache` clears it after an install or for tests.\n */\nexport async function detectSbx(): Promise<SbxState> {\n if (cached !== null) return cached;\n for (const bin of SBX_BINS) {\n try {\n const r = await execa(bin, ['--version'], { reject: false });\n if (r.exitCode === 0) {\n cached = { installed: true, bin, version: (r.stdout ?? '').trim() || undefined };\n return cached;\n }\n } catch {\n // try the next bin name\n }\n }\n cached = { installed: false };\n return cached;\n}\n\n/** Drop the per-process probe cache so the next `detectSbx()` re-probes. */\nexport function resetSbxCache(): void {\n cached = null;\n}\n\n/** Command the user should run to install the Sandbox CLI. */\nexport function installSbxHint(): string {\n return 'npm install -g sandbox';\n}\n\n/** Install the Sandbox CLI globally (`npm install -g sandbox`). Never throws. */\nexport async function installSbx(): Promise<boolean> {\n try {\n const r = await execa('npm', ['install', '-g', 'sandbox'], { reject: false });\n return r.exitCode === 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Run `sandbox login` with inherited stdio so the user sees the CLI's own\n * browser-OAuth output and can interact with it. Blocking, like the interactive\n * agent launch path. Returns the exit status (0 = success).\n */\nexport function loginSbx(bin: string): number {\n const r = spawnSync(bin, ['login'], { stdio: 'inherit' });\n return r.status ?? 1;\n}\n\n/**\n * Trigger the CLI's lazy token refresh by running a cheap read command. The CLI\n * refreshes its access token from the stored refresh token when the token is\n * stale and leaves a still-valid token untouched, so this is safe to call\n * eagerly. Non-interactive (stdin from /dev/null); returns true on exit 0.\n */\nexport async function refreshSbxToken(bin: string): Promise<boolean> {\n try {\n const r = await execa(bin, ['list'], {\n reject: false,\n timeout: 30_000,\n stdin: 'ignore',\n });\n return r.exitCode === 0;\n } catch {\n return false;\n }\n}\n","import {\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n writeFileSync,\n} from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, resolve } from 'node:path';\nimport {\n confirm,\n isCancel,\n intro,\n log,\n note,\n outro,\n password,\n select,\n spinner,\n text,\n} from '@clack/prompts';\nimport { ensureVercelEnvLoaded, reloadVercelEnv } from './env-loader.js';\nimport { hasUsableCredentials } from './sdk.js';\nimport { cliStorePaths, isNearExpiry, readCliAuth, readCliCurrentTeam } from './cli-store.js';\nimport { detectSbx, installSbx, installSbxHint, loginSbx, resetSbxCache } from './sbx-cli.js';\nimport { createProject, getUser, listProjects, type VercelProject } from './vercel-rest.js';\n\nconst DASHBOARD_TOKENS_URL = 'https://vercel.com/account/settings/tokens';\n\n/**\n * Keys we manage in `~/.agentbox/secrets.env`. On reconfigure we strip prior\n * values for these before appending so the file never accumulates duplicates.\n * `VERCEL_AUTH_SOURCE` is the CLI-login marker; the access token itself is never\n * stored here in that mode (it's read live from the Vercel CLI store).\n */\nconst MANAGED_KEYS = [\n 'VERCEL_OIDC_TOKEN',\n 'VERCEL_TOKEN',\n 'VERCEL_TEAM_ID',\n 'VERCEL_PROJECT_ID',\n 'VERCEL_AUTH_SOURCE',\n] as const;\n\nexport interface EnsureVercelCredentialsOptions {\n /** Re-prompt even when valid credentials are already present (`agentbox vercel login`). */\n force?: boolean;\n}\n\n/**\n * First-run interactive setup for Vercel credentials. The access-token path\n * persists a `VERCEL_TOKEN` trio to `~/.agentbox/secrets.env` (the canonical\n * store, matching daytona/hetzner). OIDC is also supported, but the token must\n * be present in the shell env or in `~/.agentbox/secrets.env` — agentbox does\n * NOT harvest `.env.local` (that file belongs to the app being developed).\n *\n * No-op when credentials are already configured. Silent no-op when stdin isn't\n * a TTY so scripted/CI callers get the SDK's \"not configured\" error instead of\n * a hung prompt.\n */\nexport async function ensureVercelCredentials(\n opts: EnsureVercelCredentialsOptions = {},\n): Promise<void> {\n ensureVercelEnvLoaded();\n\n if (!opts.force && hasUsableCredentials()) return;\n if (!process.stdin.isTTY) return;\n\n intro('Vercel setup');\n note(\n `AgentBox needs Vercel credentials to provision sandboxes.\\n` +\n `Sign in with Vercel (recommended): drives the Vercel \\`sandbox\\` CLI through a browser login, then reads the token from the CLI's own store and keeps it fresh — no token to paste.\\n` +\n `Access token (best for CI / headless): personal access token + team id + project id, saved to \\`~/.agentbox/secrets.env\\`.\\n` +\n `OIDC (short interactive work): export VERCEL_OIDC_TOKEN in your shell or add it to \\`~/.agentbox/secrets.env\\` (dev token expires ~12h, no headless refresh).`,\n 'Credentials required',\n );\n\n const mode = await select({\n message: 'How do you want to authenticate?',\n options: [\n { value: 'cli', label: 'Sign in with Vercel (browser) — recommended for interactive use' },\n { value: 'token', label: 'Access token (VERCEL_TOKEN + team + project) — best for CI / headless' },\n { value: 'oidc', label: 'OIDC token (VERCEL_OIDC_TOKEN in env / secrets.env) — short interactive work' },\n ],\n initialValue: 'cli',\n });\n if (isCancel(mode)) {\n log.warn('Vercel setup cancelled — re-run `agentbox vercel login` when ready.');\n return;\n }\n\n if (mode === 'cli') {\n await runCliLogin();\n return;\n }\n\n if (mode === 'oidc') {\n note(\n `Get an OIDC token with \\`vercel link\\` then \\`vercel env pull\\`, then make it visible to AgentBox by either:\\n` +\n ` export VERCEL_OIDC_TOKEN=<token> # in this shell\\n` +\n ` echo \"VERCEL_OIDC_TOKEN=<token>\" >> ~/.agentbox/secrets.env\\n` +\n `Re-do every ~12h; the dev token expires. AgentBox does not harvest .env.local.`,\n 'OIDC setup',\n );\n // Re-read in case the user already added the token to secrets.env.\n reloadVercelEnv();\n if (process.env.VERCEL_OIDC_TOKEN) {\n log.success('Found VERCEL_OIDC_TOKEN — Vercel is configured.');\n await ensureSbxInstalled();\n outro('Setup complete.');\n } else {\n log.warn('No VERCEL_OIDC_TOKEN found yet — set it as above, then re-run `agentbox vercel login`.');\n }\n return;\n }\n\n const creds = await promptForTokenTrio();\n if (creds === null) return;\n persistCredentials(creds);\n log.success(`Vercel credentials saved to ${secretsPath()}`);\n await ensureSbxInstalled();\n outro('Setup complete.');\n}\n\ninterface TokenTrio {\n token: string;\n teamId: string;\n projectId: string;\n}\n\nasync function promptForTokenTrio(): Promise<TokenTrio | null> {\n const openIt = await confirm({\n message: `Open ${DASHBOARD_TOKENS_URL} to create a token?`,\n initialValue: true,\n });\n if (isCancel(openIt)) return null;\n if (openIt) openDashboard();\n\n const token = await password({\n message: 'Paste your Vercel access token',\n validate: (v) => (v && v.trim().length > 0 ? undefined : 'Cannot be empty'),\n });\n if (isCancel(token)) {\n log.warn('Vercel setup cancelled.');\n return null;\n }\n const teamId = await text({\n message: 'Team ID (team settings → General)',\n placeholder: 'team_...',\n validate: (v) => (v && v.trim().length > 0 ? undefined : 'Cannot be empty'),\n });\n if (isCancel(teamId)) {\n log.warn('Vercel setup cancelled.');\n return null;\n }\n const projectId = await text({\n message: 'Project ID (project settings → General)',\n placeholder: 'prj_...',\n validate: (v) => (v && v.trim().length > 0 ? undefined : 'Cannot be empty'),\n });\n if (isCancel(projectId)) {\n log.warn('Vercel setup cancelled.');\n return null;\n }\n return { token: token.trim(), teamId: teamId.trim(), projectId: projectId.trim() };\n}\n\nfunction persistCredentials(creds: TokenTrio): void {\n writeManaged({\n VERCEL_TOKEN: creds.token,\n VERCEL_TEAM_ID: creds.teamId,\n VERCEL_PROJECT_ID: creds.projectId,\n });\n}\n\n/**\n * Persist the CLI-login marker + cached stable ids. The access token is\n * deliberately omitted — it's read live from the Vercel CLI store on each call\n * and refreshed there, so the only thing we cache is the team/project scope.\n */\nfunction persistCliCredentials(ids: { teamId: string; projectId: string }): void {\n writeManaged({\n VERCEL_AUTH_SOURCE: 'cli',\n VERCEL_TEAM_ID: ids.teamId,\n VERCEL_PROJECT_ID: ids.projectId,\n });\n}\n\n/**\n * Atomically rewrite the managed Vercel keys in `~/.agentbox/secrets.env`:\n * strip every prior value for a `MANAGED_KEYS` entry, then append exactly the\n * keys in `record` (mode 0600, temp-file + rename). Also mirrors the record\n * into `process.env` (and clears the other managed keys there) so the current\n * run uses the new values immediately.\n */\nfunction writeManaged(record: Record<string, string>): void {\n for (const k of MANAGED_KEYS) delete process.env[k];\n for (const [k, v] of Object.entries(record)) process.env[k] = v;\n\n const path = secretsPath();\n mkdirSync(dirname(path), { recursive: true });\n\n let existing = '';\n if (existsSync(path)) {\n try {\n existing = readFileSync(path, 'utf8');\n } catch {\n existing = '';\n }\n }\n const kept = existing\n .split(/\\r?\\n/)\n .filter((line) => {\n const stripped = line.startsWith('export ') ? line.slice('export '.length) : line;\n const eq = stripped.indexOf('=');\n if (eq <= 0) return true;\n const key = stripped.slice(0, eq).trim();\n return !(MANAGED_KEYS as readonly string[]).includes(key);\n })\n .join('\\n')\n .replace(/\\s+$/u, '');\n\n const lines = Object.entries(record).map(([k, v]) => `${k}=${v}`);\n const body = (kept ? `${kept}\\n` : '') + lines.join('\\n') + '\\n';\n\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, body, { mode: 0o600 });\n try {\n chmodSync(tmp, 0o600);\n } catch {\n // chmod best-effort; writeFileSync mode already covers most filesystems.\n }\n renameSync(tmp, path);\n try {\n chmodSync(path, 0o600);\n } catch {\n // ignore — already attempted above\n }\n}\n\n/**\n * The full CLI-login flow: make sure the Vercel `sandbox` CLI is installed (offer\n * to install it), run its browser OAuth, harvest the team id from the CLI store,\n * let the user pick a project to scope sandboxes to, and persist the marker +\n * ids. The access token is never stored — `resolveCredentials` reads it live.\n */\n/**\n * Make sure the Vercel `sandbox` CLI is on PATH, offering to install it. Every\n * login mode ensures it because interactive attach (`agentbox shell|claude|\n * codex|opencode` on a vercel box) drives `sbx exec` for a real PTY. Returns the\n * resolved bin, or null if absent / declined / install failed (callers warn).\n */\nasync function ensureSbxInstalled(): Promise<{ bin: string } | null> {\n let det = await detectSbx();\n if (!det.installed) {\n const doInstall = await confirm({\n message: `The Vercel sandbox CLI (needed for interactive attach) isn't installed. Install it now? (${installSbxHint()})`,\n initialValue: true,\n });\n if (isCancel(doInstall) || !doInstall) {\n log.warn(\n `Install it with \\`${installSbxHint()}\\` to use \\`agentbox shell|claude|codex|opencode\\` on Vercel boxes.`,\n );\n return null;\n }\n const sp = spinner();\n sp.start('Installing the Vercel sandbox CLI…');\n const ok = await installSbx();\n resetSbxCache();\n det = await detectSbx();\n if (!ok || !det.installed || !det.bin) {\n sp.stop('Install failed.');\n log.warn(`Could not install the sandbox CLI — run \\`${installSbxHint()}\\` manually.`);\n return null;\n }\n sp.stop(`Installed sandbox CLI${det.version ? ` ${det.version}` : ''}.`);\n }\n return det.bin ? { bin: det.bin } : null;\n}\n\nasync function runCliLogin(): Promise<void> {\n const det = await ensureSbxInstalled();\n if (!det) {\n log.warn('The Vercel sandbox CLI is required to sign in this way — install it, then re-run `agentbox vercel login`.');\n return;\n }\n\n note('A browser window will open to sign in to Vercel.', 'Vercel sign-in');\n const status = loginSbx(det.bin);\n if (status !== 0) {\n log.warn('Vercel sign-in did not complete — re-run `agentbox vercel login` to try again.');\n return;\n }\n\n const harvested = harvestCliCredentials();\n if (!harvested) {\n log.warn('Sign-in finished but no credentials were found in the Vercel CLI store. Try again.');\n return;\n }\n\n // Validate the token early so a bad/expired session fails here, not mid-op.\n try {\n await getUser(harvested.token);\n } catch (err) {\n log.warn(\n `The Vercel session looks invalid (${err instanceof Error ? err.message : String(err)}). ` +\n 'Re-run `agentbox vercel login`.',\n );\n return;\n }\n\n const projectId = await resolveProjectId(harvested.token, harvested.teamId);\n if (projectId === null) {\n log.warn('No project selected — re-run `agentbox vercel login` to finish setup.');\n return;\n }\n\n persistCliCredentials({ teamId: harvested.teamId, projectId });\n reloadVercelEnv();\n log.success(`Signed in with Vercel — credentials managed by the sandbox CLI (saved scope to ${secretsPath()}).`);\n outro('Setup complete.');\n}\n\n/**\n * Read the live token + team id from the Vercel CLI store. teamId prefers an\n * already-cached `VERCEL_TEAM_ID` (e.g. from a prior login) and falls back to\n * the CLI's `currentTeam`. Null when the CLI isn't logged in.\n */\nfunction harvestCliCredentials(): { token: string; teamId: string } | null {\n const auth = readCliAuth();\n if (!auth) return null;\n const teamId = process.env.VERCEL_TEAM_ID ?? readCliCurrentTeam();\n if (!teamId) return null;\n return { token: auth.token, teamId };\n}\n\n/**\n * Pick the Vercel project sandboxes run under. Lists the team's projects in a\n * clack select (pre-selecting an existing `agentbox` / sandbox-default project),\n * plus a \"create a new project\" entry. Returns the project id, or null if the\n * user cancelled. Non-interactive callers reuse/create an `agentbox` project.\n */\nasync function resolveProjectId(token: string, teamId: string): Promise<string | null> {\n let projects: VercelProject[] = [];\n const sp = spinner();\n sp.start('Loading your Vercel projects…');\n try {\n projects = await listProjects(token, teamId);\n sp.stop(`Found ${projects.length} project${projects.length === 1 ? '' : 's'}.`);\n } catch (err) {\n sp.stop('Could not list projects.');\n log.warn(`Failed to list Vercel projects: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n\n const CREATE = '__create__';\n const preferred =\n projects.find((p) => p.name === 'agentbox') ??\n projects.find((p) => p.name === 'vercel-sandbox-default-project');\n const choice = await select({\n message: 'Which Vercel project should sandboxes run under?',\n options: [\n ...projects.map((p) => ({ value: p.id, label: p.name })),\n { value: CREATE, label: 'Create a new project…' },\n ],\n initialValue: preferred ? preferred.id : CREATE,\n });\n if (isCancel(choice)) return null;\n\n if (choice !== CREATE) return choice;\n\n const name = await text({\n message: 'New project name',\n placeholder: 'agentbox',\n defaultValue: 'agentbox',\n validate: (v) => (v && v.trim().length > 0 ? undefined : 'Cannot be empty'),\n });\n if (isCancel(name)) return null;\n try {\n const created = await createProject(token, teamId, name.trim() || 'agentbox');\n return created.id;\n } catch (err) {\n log.warn(`Could not create the project: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n}\n\nfunction openDashboard(): void {\n // Lazy import keeps node:child_process out of the module's load cost.\n import('node:child_process')\n .then(({ spawnSync }) => {\n const r = spawnSync('open', [DASHBOARD_TOKENS_URL], { stdio: 'ignore' });\n if (r.status !== 0) {\n log.warn(`Could not auto-open the browser — visit ${DASHBOARD_TOKENS_URL} manually.`);\n }\n })\n .catch(() => {\n log.warn(`Could not auto-open the browser — visit ${DASHBOARD_TOKENS_URL} manually.`);\n });\n}\n\nexport function secretsPath(): string {\n return resolve(homedir(), '.agentbox', 'secrets.env');\n}\n\nexport interface VercelCredStatus {\n /** Which auth mode is configured. */\n auth: 'oidc' | 'cli' | 'token' | 'none';\n /** Legacy alias kept for callers that branch on OIDC. */\n oidc: boolean;\n token?: string;\n teamId?: string;\n projectId?: string;\n source: 'env' | 'secrets.env' | 'cli-store' | 'none';\n /**\n * CLI mode only: details about the live Vercel CLI session. Whether the\n * `sandbox` CLI is *installed* needs an async probe (`detectSbx`) and is not\n * reported here — the status printer probes it separately.\n */\n cli?: {\n /** A logged-in session was found in the CLI store. */\n loggedIn: boolean;\n /** Unix seconds the live access token expires at, when known. */\n expiresAt?: number;\n /** True when the token is at/near expiry (a refresh would fire). */\n nearExpiry?: boolean;\n /** Path to the CLI's `auth.json`. */\n authPath: string;\n };\n}\n\nexport function readVercelCredStatus(): VercelCredStatus {\n const shellHad = !!process.env.VERCEL_OIDC_TOKEN || !!process.env.VERCEL_TOKEN;\n ensureVercelEnvLoaded();\n const oidc = !!process.env.VERCEL_OIDC_TOKEN;\n const teamId = process.env.VERCEL_TEAM_ID;\n const projectId = process.env.VERCEL_PROJECT_ID;\n\n if (oidc) {\n return { auth: 'oidc', oidc: true, teamId, projectId, source: shellHad ? 'env' : 'secrets.env' };\n }\n\n if (process.env.VERCEL_AUTH_SOURCE === 'cli') {\n const auth = readCliAuth();\n return {\n auth: 'cli',\n oidc: false,\n token: auth?.token,\n teamId,\n projectId,\n source: 'cli-store',\n cli: {\n loggedIn: !!auth,\n expiresAt: auth?.expiresAt,\n nearExpiry: auth ? isNearExpiry(auth) : undefined,\n authPath: cliStorePaths().authPath,\n },\n };\n }\n\n const token = process.env.VERCEL_TOKEN;\n if (!token) return { auth: 'none', oidc: false, source: 'none' };\n return {\n auth: 'token',\n oidc: false,\n token,\n teamId,\n projectId,\n source: shellHad ? 'env' : 'secrets.env',\n };\n}\n\nexport function maskKey(value: string): string {\n if (value.length <= 8) return '*'.repeat(value.length);\n return `${value.slice(0, 4)}…${'*'.repeat(8)}${value.slice(-4)}`;\n}\n","/**\n * Reader for the Vercel CLI's own credential store — written by `sandbox login`\n * / `vercel login` (the `sandbox`/`sbx` CLI and `vercel` CLI share one store).\n *\n * AgentBox's \"CLI-login\" auth mode (see credentials.ts) drives that CLI for the\n * browser OAuth, then reads the resulting OAuth access token live from here on\n * every SDK call rather than copying it into `secrets.env`: the token is a\n * short-lived, opaque `vca_…` access token that the CLI refreshes lazily from\n * its stored refresh token, so the CLI store is the single self-refreshing\n * source of truth. We only cache the stable bits (team/project id + a marker)\n * in `secrets.env`.\n *\n * Pure FS + path logic — no clack, no execa — so it stays trivially testable.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/** The shape of the CLI's `auth.json` (only the fields we consume). */\nexport interface VercelCliAuth {\n /** Opaque OAuth access token (`vca_…`). NOT a JWT — expiry is `expiresAt`. */\n token: string;\n /** Unix seconds. Absent on some CLI versions → treated as near-expiry. */\n expiresAt?: number;\n /** Present but unused by us; the CLI uses it to self-refresh `token`. */\n refreshToken?: string;\n}\n\n/**\n * Resolve the `com.vercel.cli` data directory the way the CLI itself does\n * (xdg-app-paths-style), per platform:\n * - macOS: ~/Library/Application Support/com.vercel.cli\n * - Windows: %APPDATA%\\com.vercel.cli\n * - else: $XDG_DATA_HOME/com.vercel.cli (default ~/.local/share/...)\n *\n * `AGENTBOX_VERCEL_CLI_DIR` overrides outright — for tests and the rare install\n * that relocates the store.\n */\nexport function vercelCliDir(): string {\n const override = process.env.AGENTBOX_VERCEL_CLI_DIR;\n if (override && override.trim().length > 0) return override.trim();\n\n const name = 'com.vercel.cli';\n if (process.platform === 'darwin') {\n return join(homedir(), 'Library', 'Application Support', name);\n }\n if (process.platform === 'win32') {\n const appData = process.env.APPDATA;\n if (appData && appData.trim().length > 0) return join(appData, name);\n return join(homedir(), 'AppData', 'Roaming', name);\n }\n const xdg = process.env.XDG_DATA_HOME;\n const base = xdg && xdg.trim().length > 0 ? xdg.trim() : join(homedir(), '.local', 'share');\n return join(base, name);\n}\n\n/** Absolute paths to the CLI store files, for status/diagnostics. */\nexport function cliStorePaths(): { authPath: string; configPath: string } {\n const dir = vercelCliDir();\n return { authPath: join(dir, 'auth.json'), configPath: join(dir, 'config.json') };\n}\n\nfunction readJson(path: string): unknown {\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, 'utf8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Read the live access token from the CLI store. Returns null when the store is\n * missing / unparseable / has no token (CLI never logged in, or logged out) so\n * callers can surface a clear \"run `agentbox vercel login`\" error.\n */\nexport function readCliAuth(): VercelCliAuth | null {\n const raw = readJson(cliStorePaths().authPath) as Record<string, unknown> | null;\n if (!raw || typeof raw.token !== 'string' || raw.token.length === 0) return null;\n return {\n token: raw.token,\n expiresAt: typeof raw.expiresAt === 'number' ? raw.expiresAt : undefined,\n refreshToken: typeof raw.refreshToken === 'string' ? raw.refreshToken : undefined,\n };\n}\n\n/** Read the CLI's currently-selected team (`config.json` `currentTeam`). */\nexport function readCliCurrentTeam(): string | null {\n const raw = readJson(cliStorePaths().configPath) as Record<string, unknown> | null;\n return raw && typeof raw.currentTeam === 'string' && raw.currentTeam.length > 0\n ? raw.currentTeam\n : null;\n}\n\n/**\n * Whether the access token is at/near expiry and should be refreshed before\n * use. A missing `expiresAt` is treated as near-expiry so we always probe a\n * refresh rather than ship a token of unknown age. `skewSec` is the safety\n * window: refresh if the token expires within that many seconds.\n */\nexport function isNearExpiry(auth: VercelCliAuth, skewSec = 120): boolean {\n if (auth.expiresAt === undefined) return true;\n return auth.expiresAt * 1000 < Date.now() + skewSec * 1000;\n}\n","/**\n * Thin loader around `@vercel/sandbox`. Resolves the auth credentials once and\n * threads them into every SDK call.\n *\n * Three auth modes (in precedence order):\n * - OIDC: `VERCEL_OIDC_TOKEN` in env → decode the JWT for owner/project and\n * pass `{ token, teamId, projectId }` explicitly.\n * - CLI-login: `VERCEL_AUTH_SOURCE=cli` → read the live OAuth access token\n * from the Vercel CLI's own store (`auth.json`) and the cached team/project\n * ids from `secrets.env`. The token is never copied to `secrets.env`; the\n * CLI store is the self-refreshing source of truth. `ensureFreshCredentials`\n * refreshes it (via the `sbx` CLI) before use when it's near expiry.\n * - Access token: `VERCEL_TOKEN` + `VERCEL_TEAM_ID` + `VERCEL_PROJECT_ID` →\n * passed explicitly as `{ token, teamId, projectId }` on each call, since\n * the SDK does NOT read those from env automatically.\n */\n\nimport { ensureVercelEnvLoaded } from './env-loader.js';\nimport { isNearExpiry, readCliAuth, readCliCurrentTeam } from './cli-store.js';\nimport { detectSbx, refreshSbxToken } from './sbx-cli.js';\n\nexport interface VercelCredentials {\n token: string;\n teamId: string;\n projectId: string;\n}\n\n/**\n * Resolve the credentials to thread into SDK calls. Throws when nothing is\n * configured (or an OIDC token has expired) so callers get a clear, actionable\n * error instead of an opaque SDK auth failure.\n *\n * For OIDC we do NOT return `{}` and let the SDK read the env var: the SDK's\n * env-OIDC path (`@vercel/oidc`) tries to *refresh* the token via the Vercel\n * CLI's `.vercel/project.json` + cached auth, which an agentbox box doesn't\n * have, so it fails with \"Could not get credentials from OIDC context\". Instead\n * we decode the OIDC JWT — which embeds `owner_id` (teamId) and `project_id` —\n * and pass `{ token, teamId, projectId }` explicitly, which uses the SDK's\n * direct-credentials path (the OIDC token is itself a valid API bearer).\n */\nexport function resolveCredentials(): VercelCredentials {\n ensureVercelEnvLoaded();\n const oidc = process.env.VERCEL_OIDC_TOKEN;\n if (oidc) {\n const claims = decodeOidcClaims(oidc);\n if (!claims) {\n throw new Error(\n 'VERCEL_OIDC_TOKEN is set but could not be decoded (not a valid Vercel OIDC JWT). ' +\n 'Re-run `vercel env pull`, or use the VERCEL_TOKEN + VERCEL_TEAM_ID + VERCEL_PROJECT_ID trio.',\n );\n }\n if (claims.exp !== undefined && claims.exp * 1000 < Date.now()) {\n throw new Error(\n 'VERCEL_OIDC_TOKEN has expired (Vercel dev OIDC tokens last ~12h). ' +\n 'Re-run `vercel env pull` to refresh it, then retry.',\n );\n }\n return { token: oidc, teamId: claims.teamId, projectId: claims.projectId };\n }\n if (process.env.VERCEL_AUTH_SOURCE === 'cli') {\n const auth = readCliAuth();\n if (!auth) {\n throw new Error(\n 'Vercel CLI session not found — run `agentbox vercel login` (or `sbx login`) to sign in again.',\n );\n }\n const teamId = process.env.VERCEL_TEAM_ID ?? readCliCurrentTeam() ?? undefined;\n const projectId = process.env.VERCEL_PROJECT_ID;\n if (!teamId || !projectId) {\n throw new Error(\n 'Vercel CLI auth is missing the team/project id — re-run `agentbox vercel login`.',\n );\n }\n // Live token straight from the CLI store; nothing cached on disk.\n return { token: auth.token, teamId, projectId };\n }\n const token = process.env.VERCEL_TOKEN;\n const teamId = process.env.VERCEL_TEAM_ID;\n const projectId = process.env.VERCEL_PROJECT_ID;\n if (token && teamId && projectId) return { token, teamId, projectId };\n throw new Error(\n 'Vercel credentials not configured.\\n' +\n 'Either run `vercel link && vercel env pull` to get a VERCEL_OIDC_TOKEN, ' +\n 'or set VERCEL_TOKEN + VERCEL_TEAM_ID + VERCEL_PROJECT_ID ' +\n '(see `agentbox vercel login`).',\n );\n}\n\ninterface OidcClaims {\n teamId: string;\n projectId: string;\n exp?: number;\n}\n\n/** Decode the `owner_id`/`project_id`/`exp` claims from a Vercel OIDC JWT. */\nfunction decodeOidcClaims(token: string): OidcClaims | null {\n const parts = token.split('.');\n if (parts.length < 2 || !parts[1]) return null;\n try {\n const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8')) as {\n owner_id?: unknown;\n project_id?: unknown;\n exp?: unknown;\n };\n if (typeof payload.owner_id !== 'string' || typeof payload.project_id !== 'string') return null;\n return {\n teamId: payload.owner_id,\n projectId: payload.project_id,\n exp: typeof payload.exp === 'number' ? payload.exp : undefined,\n };\n } catch {\n return null;\n }\n}\n\n/** True when any auth mode is configured. Used by the credential gate. */\nexport function hasUsableCredentials(): boolean {\n ensureVercelEnvLoaded();\n if (process.env.VERCEL_OIDC_TOKEN) return true;\n if (\n process.env.VERCEL_AUTH_SOURCE === 'cli' &&\n process.env.VERCEL_TEAM_ID &&\n process.env.VERCEL_PROJECT_ID &&\n readCliAuth()\n ) {\n return true;\n }\n return Boolean(\n process.env.VERCEL_TOKEN && process.env.VERCEL_TEAM_ID && process.env.VERCEL_PROJECT_ID,\n );\n}\n\n/**\n * Refresh the CLI-login access token before use when it's near expiry. No-op\n * for the OIDC and access-token modes (nothing to refresh). For CLI mode: if the\n * live token in the CLI store is within the safety window, run a cheap `sbx`\n * read command, which makes the CLI rotate its own token from the stored refresh\n * token, then re-read `secrets.env`. Throws an actionable error when the CLI is\n * gone or the refresh fails (e.g. the refresh token itself expired).\n *\n * Call this once at the top of each backend operation, BEFORE the (sync)\n * `resolveCredentials()` reads the token. An in-process single-flight collapses\n * concurrent ops onto one refresh; cross-process races are harmless (the CLI\n * writes its store atomically and a fresh-token refresh is a no-op).\n */\nlet inflightRefresh: Promise<void> | null = null;\n\nexport function ensureFreshCredentials(): Promise<void> {\n ensureVercelEnvLoaded();\n if (process.env.VERCEL_AUTH_SOURCE !== 'cli') return Promise.resolve();\n if (inflightRefresh) return inflightRefresh;\n inflightRefresh = refreshCliToken().finally(() => {\n inflightRefresh = null;\n });\n return inflightRefresh;\n}\n\nasync function refreshCliToken(): Promise<void> {\n const auth = readCliAuth();\n if (!auth) return; // resolveCredentials() will throw the clear \"logged out\" error\n if (!isNearExpiry(auth)) return; // still valid — no work\n\n const det = await detectSbx();\n if (!det.installed || !det.bin) {\n throw new Error(\n 'Vercel access token is near expiry and the `sandbox` CLI is no longer installed — ' +\n 'reinstall it (`npm install -g sandbox`) or run `agentbox vercel login`.',\n );\n }\n const ok = await refreshSbxToken(det.bin);\n if (!ok) {\n throw new Error(\n 'Vercel token refresh failed — run `agentbox vercel login` (the refresh token may have expired).',\n );\n }\n // The token lives in the CLI store, not secrets.env — refreshSbxToken rotated\n // auth.json in place, so the next readCliAuth() returns the fresh token.\n const fresh = readCliAuth();\n if (!fresh || isNearExpiry(fresh, 0)) {\n throw new Error(\n 'Vercel token is still stale after a refresh attempt — run `agentbox vercel login`.',\n );\n }\n}\n\n// Re-export the SDK surface we use so the rest of the package imports from one\n// place (and tests can mock `./sdk.js` instead of the package).\nexport { Sandbox, Snapshot } from '@vercel/sandbox';\nexport type { Sandbox as SandboxType } from '@vercel/sandbox';\n","/**\n * Minimal Vercel REST helpers used by the CLI-login auth flow. The Sandbox SDK\n * requires a projectId on every call, but the OAuth access token harvested from\n * the CLI is team-scoped with no project — so after login we list the team's\n * projects (and optionally create one) to resolve a project to scope sandboxes\n * to. Plain `fetch`; no SDK. Each call takes the harvested `(token, teamId)`.\n */\n\nconst API = 'https://api.vercel.com';\n\nexport interface VercelProject {\n id: string;\n name: string;\n}\n\nclass VercelApiError extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = 'VercelApiError';\n }\n}\n\nasync function api(\n token: string,\n path: string,\n init?: { method?: string; body?: unknown },\n): Promise<unknown> {\n const res = await fetch(`${API}${path}`, {\n method: init?.method ?? 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n ...(init?.body ? { 'Content-Type': 'application/json' } : {}),\n },\n body: init?.body ? JSON.stringify(init.body) : undefined,\n });\n const text = await res.text();\n let json: unknown = null;\n try {\n json = text ? JSON.parse(text) : null;\n } catch {\n json = null;\n }\n if (!res.ok) {\n const detail =\n (json as { error?: { message?: string } } | null)?.error?.message ??\n text ??\n res.statusText;\n throw new VercelApiError(res.status, `Vercel API ${res.status}: ${detail}`);\n }\n return json;\n}\n\n/** Validate the token and return the authenticated user (probe / status). */\nexport async function getUser(token: string): Promise<{ id: string; username?: string }> {\n const json = (await api(token, '/v2/user')) as {\n user?: { id: string; username?: string };\n };\n const user = json.user;\n if (!user?.id) throw new Error('Vercel /v2/user returned no user');\n return user;\n}\n\n/** List the team's projects (id + name). Paginates up to `limit` (default 100). */\nexport async function listProjects(\n token: string,\n teamId: string,\n limit = 100,\n): Promise<VercelProject[]> {\n const json = (await api(\n token,\n `/v9/projects?teamId=${encodeURIComponent(teamId)}&limit=${limit}`,\n )) as { projects?: Array<{ id: string; name: string }> };\n return (json.projects ?? []).map((p) => ({ id: p.id, name: p.name }));\n}\n\n/**\n * Create a project under the team. On a 409 (name already taken) re-list and\n * return the existing project of that name, so the caller treats create as\n * idempotent.\n */\nexport async function createProject(\n token: string,\n teamId: string,\n name: string,\n): Promise<VercelProject> {\n try {\n const json = (await api(token, `/v9/projects?teamId=${encodeURIComponent(teamId)}`, {\n method: 'POST',\n body: { name },\n })) as { id: string; name: string };\n return { id: json.id, name: json.name };\n } catch (err) {\n if (err instanceof VercelApiError && err.status === 409) {\n const existing = (await listProjects(token, teamId)).find((p) => p.name === name);\n if (existing) return existing;\n }\n throw err;\n }\n}\n"],"mappings":"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","names":["existsSync","readFileSync","homedir","resolve","teamId","projectId","text","spawnSync"]}
@@ -19,7 +19,7 @@ import {
19
19
  recordBox,
20
20
  removeBoxRecord,
21
21
  writePreparedDockerState
22
- } from "./chunk-BGK32PZE.js";
22
+ } from "./chunk-KL36BRN4.js";
23
23
 
24
24
  // ../../packages/sandbox-docker/dist/index.js
25
25
  import { randomBytes as randomBytes3 } from "crypto";
@@ -736,6 +736,7 @@ var BUILT_IN_DEFAULTS = {
736
736
  defaultCheckpointDocker: "",
737
737
  defaultCheckpointDaytona: "",
738
738
  defaultCheckpointHetzner: "",
739
+ defaultCheckpointVercel: "",
739
740
  withPlaywright: false,
740
741
  withEnv: false,
741
742
  vnc: true,
@@ -748,7 +749,10 @@ var BUILT_IN_DEFAULTS = {
748
749
  cpus: 0,
749
750
  pidsLimit: 0,
750
751
  disk: "",
751
- bundleDepth: void 0
752
+ bundleDepth: void 0,
753
+ vercelVcpus: 2,
754
+ vercelTimeoutMs: 27e5,
755
+ vercelNetworkPolicy: ""
752
756
  },
753
757
  checkpoint: {
754
758
  maxLayers: 3
@@ -799,7 +803,12 @@ var BUILT_IN_DEFAULTS = {
799
803
  },
800
804
  queue: {
801
805
  enabled: true,
802
- maxConcurrent: 5
806
+ maxConcurrent: 5,
807
+ maxWorking: 0,
808
+ idleGraceSeconds: 15
809
+ },
810
+ cloud: {
811
+ useCurrentBranch: false
803
812
  },
804
813
  maintenance: {
805
814
  pruneProjectConfigs: true,
@@ -810,8 +819,8 @@ var KEY_REGISTRY = [
810
819
  {
811
820
  key: "box.provider",
812
821
  type: "enum",
813
- enumValues: ["docker", "daytona", "hetzner"],
814
- description: "Sandbox backend new boxes are created on: local Docker containers, Daytona Cloud sandboxes, or Hetzner Cloud VPSes."
822
+ enumValues: ["docker", "daytona", "hetzner", "vercel"],
823
+ description: "Sandbox backend new boxes are created on: local Docker containers, Daytona Cloud sandboxes, Hetzner Cloud VPSes, or Vercel Sandboxes."
815
824
  },
816
825
  {
817
826
  key: "box.hostSnapshot",
@@ -841,6 +850,12 @@ var KEY_REGISTRY = [
841
850
  description: "Per-provider override of `box.defaultCheckpoint` for hetzner. Wins over the global when set; set via `agentbox checkpoint set-default --provider hetzner`.",
842
851
  advanced: true
843
852
  },
853
+ {
854
+ key: "box.defaultCheckpointVercel",
855
+ type: "string",
856
+ description: "Per-provider override of `box.defaultCheckpoint` for vercel. Wins over the global when set; set via `agentbox checkpoint set-default --provider vercel`.",
857
+ advanced: true
858
+ },
844
859
  {
845
860
  key: "checkpoint.maxLayers",
846
861
  type: "int",
@@ -914,6 +929,21 @@ var KEY_REGISTRY = [
914
929
  type: "int",
915
930
  description: "Cap git bundle history shipped to cloud sandboxes (daytona, hetzner). 0 = full history. Unset = adaptive default (last 200 commits; re-bundle at 100 if the bundle exceeds 20 MB). Ignored for docker (which bind-mounts .git/)."
916
931
  },
932
+ {
933
+ key: "box.vercelVcpus",
934
+ type: "int",
935
+ description: "vCPUs for new --provider vercel boxes (Vercel couples RAM at 2048 MB/vCPU). Default 2. Vercel only accepts specific counts (e.g. 1, 2, 4, 8) \u2014 an unsupported value fails create with a 400. Vercel-only; ignored by other providers."
936
+ },
937
+ {
938
+ key: "box.vercelTimeoutMs",
939
+ type: "int",
940
+ description: "Max session length (ms) for new --provider vercel boxes before the VM auto-snapshots; persistent mode auto-resumes on the next call. Default 2700000 (45 min, the Hobby ceiling). Vercel-only."
941
+ },
942
+ {
943
+ key: "box.vercelNetworkPolicy",
944
+ type: "string",
945
+ description: "Egress lock for new --provider vercel boxes: 'allow-all' (default, unset), 'deny-all', or a comma-separated domain allowlist (e.g. 'github.com,*.npmjs.org') that denies everything else. Vercel-only; ignored by other providers."
946
+ },
917
947
  {
918
948
  key: "claude.sessionName",
919
949
  type: "string",
@@ -1031,6 +1061,21 @@ var KEY_REGISTRY = [
1031
1061
  type: "int",
1032
1062
  description: "Max number of simultaneously-running boxes (across providers) before background `-i` jobs queue up instead of starting immediately. Per-invocation override: `--max-running <n>`."
1033
1063
  },
1064
+ {
1065
+ key: "queue.maxWorking",
1066
+ type: "int",
1067
+ description: "Max agents actively working/thinking (quota-consuming) at once before background `-i` jobs queue. 0 = disabled (use the queue.maxConcurrent running-box gate). Counts all boxes, foreground + queued. Per-invocation override: `--max-working <n>`."
1068
+ },
1069
+ {
1070
+ key: "queue.idleGraceSeconds",
1071
+ type: "int",
1072
+ description: "Seconds an agent must stay non-working before it frees its working slot (debounce against brief idle flaps between turns). Only used when queue.maxWorking > 0."
1073
+ },
1074
+ {
1075
+ key: "cloud.useCurrentBranch",
1076
+ type: "bool",
1077
+ description: "On cloud providers (daytona/hetzner), start new boxes on the host's current branch instead of forking a new agentbox/<box-name> branch. Overridden by an explicit --use-branch / --from-branch."
1078
+ },
1034
1079
  {
1035
1080
  key: "maintenance.pruneProjectConfigs",
1036
1081
  type: "bool",
@@ -1386,7 +1431,7 @@ function writeLeaf(obj, branch, leaf, value) {
1386
1431
  b[leaf] = value;
1387
1432
  }
1388
1433
  function resolveDefaultCheckpoint(cfg, provider) {
1389
- const perProvider = provider === "daytona" ? cfg.box.defaultCheckpointDaytona : provider === "hetzner" ? cfg.box.defaultCheckpointHetzner : cfg.box.defaultCheckpointDocker;
1434
+ const perProvider = provider === "daytona" ? cfg.box.defaultCheckpointDaytona : provider === "hetzner" ? cfg.box.defaultCheckpointHetzner : provider === "vercel" ? cfg.box.defaultCheckpointVercel : cfg.box.defaultCheckpointDocker;
1390
1435
  if (perProvider && perProvider.length > 0) return perProvider;
1391
1436
  return cfg.box.defaultCheckpoint;
1392
1437
  }
@@ -1394,6 +1439,7 @@ function defaultCheckpointConfigKey(provider) {
1394
1439
  if (provider === "docker") return "box.defaultCheckpointDocker";
1395
1440
  if (provider === "daytona") return "box.defaultCheckpointDaytona";
1396
1441
  if (provider === "hetzner") return "box.defaultCheckpointHetzner";
1442
+ if (provider === "vercel") return "box.defaultCheckpointVercel";
1397
1443
  return "box.defaultCheckpoint";
1398
1444
  }
1399
1445
  async function setConfigValue(scope, key, value, cwd, opts = {}) {
@@ -1676,6 +1722,15 @@ var GH_PR_OPS = [
1676
1722
  "close",
1677
1723
  "reopen"
1678
1724
  ];
1725
+ function injectPrCreateHead(op, branch, args) {
1726
+ if (op !== "create") return args;
1727
+ if (!branch || branch === "HEAD") return args;
1728
+ if (hasHeadArg(args)) return args;
1729
+ return ["--head", branch, ...args];
1730
+ }
1731
+ function hasHeadArg(args) {
1732
+ return args.some((a) => a === "--head" || a.startsWith("--head=") || a.startsWith("-H"));
1733
+ }
1679
1734
  var MAX_BODY_BYTES = 1024 * 1024;
1680
1735
  var QUEUE_DIR = join3(STATE_DIR, "queue");
1681
1736
  async function loadQueueConfig() {
@@ -1688,7 +1743,9 @@ async function loadQueueConfig() {
1688
1743
  const q = global.queue ?? {};
1689
1744
  return {
1690
1745
  enabled: q.enabled ?? d.enabled,
1691
- maxConcurrent: q.maxConcurrent ?? d.maxConcurrent
1746
+ maxConcurrent: q.maxConcurrent ?? d.maxConcurrent,
1747
+ maxWorking: q.maxWorking ?? d.maxWorking,
1748
+ idleGraceMs: (q.idleGraceSeconds ?? d.idleGraceSeconds) * 1e3
1692
1749
  };
1693
1750
  }
1694
1751
  async function writeJob(job) {
@@ -3671,6 +3728,22 @@ async function pullClaudeExtras(spec, opts) {
3671
3728
  return { newItems, mergedRegistries };
3672
3729
  }
3673
3730
  var CREDENTIALS_BACKUP_FILE = join32(STATE_DIR, "claude-credentials.json");
3731
+ var CODEX_CREDENTIALS_BACKUP_FILE = join32(STATE_DIR, "codex-credentials.json");
3732
+ var OPENCODE_CREDENTIALS_BACKUP_FILE = join32(STATE_DIR, "opencode-credentials.json");
3733
+ function isRealAgentCredential(agent, text) {
3734
+ let parsed;
3735
+ try {
3736
+ parsed = JSON.parse(text);
3737
+ } catch {
3738
+ return false;
3739
+ }
3740
+ if (typeof parsed !== "object" || parsed === null) return false;
3741
+ if (agent === "claude") {
3742
+ const rt = parsed.claudeAiOauth?.refreshToken;
3743
+ return typeof rt === "string" && rt.length > 0;
3744
+ }
3745
+ return Object.keys(parsed).length > 0;
3746
+ }
3674
3747
  async function hostBackupHasCredentials(path = CREDENTIALS_BACKUP_FILE) {
3675
3748
  try {
3676
3749
  const parsed = JSON.parse(await readFile32(path, "utf8"));
@@ -4571,23 +4644,10 @@ async function seedWorkspace(opts) {
4571
4644
  const main = r.repo.hostMainRepo;
4572
4645
  const wt = r.gitWorktreePath;
4573
4646
  const baseRef = r.repo.kind === "root" ? opts.fromBranch ?? "HEAD" : "HEAD";
4647
+ const addArgs = r.reuseBranch ? ["worktree", "add", wt, r.branch] : ["worktree", "add", "-b", r.branch, wt, baseRef];
4574
4648
  const add = await execa7(
4575
4649
  "docker",
4576
- [
4577
- "exec",
4578
- "--user",
4579
- "vscode",
4580
- opts.container,
4581
- "git",
4582
- "-C",
4583
- main,
4584
- "worktree",
4585
- "add",
4586
- "-b",
4587
- r.branch,
4588
- wt,
4589
- baseRef
4590
- ],
4650
+ ["exec", "--user", "vscode", opts.container, "git", "-C", main, ...addArgs],
4591
4651
  { reject: false }
4592
4652
  );
4593
4653
  if (add.exitCode !== 0) {
@@ -5306,25 +5366,72 @@ async function ensureRelay(opts = {}) {
5306
5366
  await removeContainer(RELAY_CONTAINER_NAME);
5307
5367
  log(`removed legacy relay container ${RELAY_CONTAINER_NAME}`);
5308
5368
  }
5309
- if (await pingHealthz(500)) {
5310
- return ENDPOINT;
5311
- }
5312
- const existingPid = await readPidFile();
5313
- if (existingPid !== null && await processAlive(existingPid)) {
5314
- for (let i = 0; i < 10; i++) {
5315
- if (await pingHealthz(300)) return ENDPOINT;
5316
- await delay2(200);
5369
+ const health = await fetchHealthz(500);
5370
+ if (health !== null) {
5371
+ if (health.cliEntry !== false) {
5372
+ return ENDPOINT;
5373
+ }
5374
+ log("relay is alive but lacks AGENTBOX_CLI_ENTRY (cp/download/checkpoint would fail) \u2014 reclaiming");
5375
+ await reclaimRelay(health.pid, log);
5376
+ } else {
5377
+ const existingPid = await readPidFile();
5378
+ if (existingPid !== null && await processAlive(existingPid)) {
5379
+ for (let i = 0; i < 10; i++) {
5380
+ if (await pingHealthz(300)) return ENDPOINT;
5381
+ await delay2(200);
5382
+ }
5383
+ log(`relay pid ${String(existingPid)} alive but /healthz unresponsive \u2014 proceeding anyway`);
5384
+ return ENDPOINT;
5385
+ }
5386
+ if (existingPid !== null) {
5387
+ await unlink2(PID_FILE).catch(() => {
5388
+ });
5317
5389
  }
5318
- log(`relay pid ${String(existingPid)} alive but /healthz unresponsive \u2014 proceeding anyway`);
5319
- return ENDPOINT;
5320
- }
5321
- if (existingPid !== null) {
5322
- await unlink2(PID_FILE).catch(() => {
5323
- });
5324
5390
  }
5325
5391
  const relayBin = resolveRelayBin();
5326
- const logFd = openSync(LOG_FILE, "a");
5327
5392
  const cliEntry = resolveCliEntry();
5393
+ if (cliEntry === null) {
5394
+ throw new Error(
5395
+ "cannot start the host relay: agentbox CLI entry not found (is the build complete / dist present?). Set AGENTBOX_CLI_ENTRY to override."
5396
+ );
5397
+ }
5398
+ return spawnRelay(relayBin, cliEntry, log);
5399
+ }
5400
+ async function reclaimRelay(reportedPid, log) {
5401
+ const pidFromFile = await readPidFile();
5402
+ const seen = /* @__PURE__ */ new Set();
5403
+ for (const pid of [reportedPid, pidFromFile]) {
5404
+ if (typeof pid !== "number" || pid <= 0 || seen.has(pid)) continue;
5405
+ seen.add(pid);
5406
+ if (!await processAlive(pid)) continue;
5407
+ log(`stopping crippled relay pid ${String(pid)}`);
5408
+ await killPid(pid);
5409
+ }
5410
+ await unlink2(PID_FILE).catch(() => {
5411
+ });
5412
+ if (await pingHealthz(300)) {
5413
+ throw new Error(
5414
+ `a relay without AGENTBOX_CLI_ENTRY is still listening on :${String(PORT)} and could not be stopped (reported pid ${String(reportedPid ?? "unknown")}); kill it manually and retry`
5415
+ );
5416
+ }
5417
+ }
5418
+ async function killPid(pid) {
5419
+ try {
5420
+ process.kill(pid, "SIGTERM");
5421
+ } catch {
5422
+ return;
5423
+ }
5424
+ for (let i = 0; i < 20; i++) {
5425
+ if (!await processAlive(pid)) return;
5426
+ await delay2(100);
5427
+ }
5428
+ try {
5429
+ process.kill(pid, "SIGKILL");
5430
+ } catch {
5431
+ }
5432
+ }
5433
+ async function spawnRelay(relayBin, cliEntry, log) {
5434
+ const logFd = openSync(LOG_FILE, "a");
5328
5435
  const child = spawn(
5329
5436
  process.execPath,
5330
5437
  [relayBin, "serve", "--port", String(PORT), "--host", "0.0.0.0"],
@@ -5333,7 +5440,7 @@ async function ensureRelay(opts = {}) {
5333
5440
  stdio: ["ignore", logFd, logFd],
5334
5441
  env: {
5335
5442
  ...process.env,
5336
- ...cliEntry ? { AGENTBOX_CLI_ENTRY: cliEntry } : {}
5443
+ AGENTBOX_CLI_ENTRY: cliEntry
5337
5444
  }
5338
5445
  }
5339
5446
  );
@@ -5465,7 +5572,13 @@ function fetchHealthz(timeoutMs) {
5465
5572
  try {
5466
5573
  const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
5467
5574
  if (typeof parsed.ok === "boolean" && typeof parsed.boxes === "number" && typeof parsed.events === "number") {
5468
- resolveP({ ok: parsed.ok, boxes: parsed.boxes, events: parsed.events });
5575
+ resolveP({
5576
+ ok: parsed.ok,
5577
+ boxes: parsed.boxes,
5578
+ events: parsed.events,
5579
+ pid: typeof parsed.pid === "number" ? parsed.pid : void 0,
5580
+ cliEntry: typeof parsed.cliEntry === "boolean" ? parsed.cliEntry : void 0
5581
+ });
5469
5582
  } else {
5470
5583
  resolveP(null);
5471
5584
  }
@@ -5944,9 +6057,33 @@ async function createBox(opts) {
5944
6057
  );
5945
6058
  }
5946
6059
  for (const r of repos) {
6060
+ const containerPath = r.kind === "root" ? "/workspace" : `/workspace/${r.relPathFromWorkspace}`;
6061
+ const reuseBranch = r.kind === "root" && opts.useBranch !== void 0;
6062
+ if (reuseBranch) {
6063
+ const branch2 = opts.useBranch;
6064
+ const gitWorktreePath2 = gitWorktreePathFor(branch2);
6065
+ repoCarryOvers.push({
6066
+ repo: r,
6067
+ containerPath,
6068
+ gitWorktreePath: gitWorktreePath2,
6069
+ branch: branch2,
6070
+ stashSha: null,
6071
+ untrackedNul: "",
6072
+ hostSource: r.hostMainRepo,
6073
+ reuseBranch: true
6074
+ });
6075
+ gitWorktreeRecords.push({
6076
+ kind: r.kind,
6077
+ hostMainRepo: r.hostMainRepo,
6078
+ containerPath,
6079
+ gitWorktreePath: gitWorktreePath2,
6080
+ branch: branch2,
6081
+ relPathFromWorkspace: r.relPathFromWorkspace
6082
+ });
6083
+ continue;
6084
+ }
5947
6085
  const branchBase = r.kind === "root" ? `agentbox/${name}` : `agentbox/${name}--${r.relPathFromWorkspace.replace(/[^A-Za-z0-9._-]+/g, "_")}`;
5948
6086
  const branch = await pickFreshBranch(r.hostMainRepo, branchBase);
5949
- const containerPath = r.kind === "root" ? "/workspace" : `/workspace/${r.relPathFromWorkspace}`;
5950
6087
  const gitWorktreePath = gitWorktreePathFor(branch);
5951
6088
  const carry = await collectRepoCarryOver(r, branch, containerPath, gitWorktreePath);
5952
6089
  repoCarryOvers.push(carry);
@@ -6191,9 +6328,18 @@ async function createBox(opts) {
6191
6328
  });
6192
6329
  log("seeded /workspace from in-container git worktree(s)");
6193
6330
  } catch (err) {
6194
- log(
6195
- `seedWorkspace failed; leaving ${containerName} running so you can inspect it`
6196
- );
6331
+ if (opts.useBranch !== void 0) {
6332
+ log(`seedWorkspace failed for --use-branch ${opts.useBranch}; cleaning up the box`);
6333
+ await execa13("docker", ["rm", "-f", containerName], { reject: false });
6334
+ for (const w of gitWorktreeRecords) {
6335
+ await removeInBoxWorktree({
6336
+ hostMainRepo: w.hostMainRepo,
6337
+ gitWorktreePath: w.gitWorktreePath
6338
+ });
6339
+ }
6340
+ } else {
6341
+ log(`seedWorkspace failed; leaving ${containerName} running so you can inspect it`);
6342
+ }
6197
6343
  throw err;
6198
6344
  }
6199
6345
  } else {
@@ -7427,6 +7573,7 @@ var dockerProvider = {
7427
7573
  useSnapshot: po.useSnapshot ?? false,
7428
7574
  checkpointRef: req.checkpointRef,
7429
7575
  fromBranch: req.fromBranch,
7576
+ useBranch: req.useBranch,
7430
7577
  image: req.image,
7431
7578
  onLog: req.onLog,
7432
7579
  claudeConfig: po.claudeConfig,
@@ -7783,6 +7930,9 @@ async function stageCodexStaticForUpload(opts = {}) {
7783
7930
  }
7784
7931
  }
7785
7932
  async function stageCodexCredentialsForUpload(opts = {}) {
7933
+ if (await pathExists7(CODEX_CREDENTIALS_BACKUP_FILE)) {
7934
+ return stageSingleFileTarball("codex-creds", CODEX_CREDENTIALS_BACKUP_FILE, "auth.json");
7935
+ }
7786
7936
  const hostHome = opts.hostHome ?? homedir11();
7787
7937
  const hostAuth = join14(hostHome, ".codex", "auth.json");
7788
7938
  if (!await pathExists7(hostAuth)) return emptyResult([CODEX_KEYCHAIN_WARNING]);
@@ -7847,6 +7997,9 @@ async function stageOpencodeStaticForUpload(opts = {}) {
7847
7997
  }
7848
7998
  }
7849
7999
  async function stageOpencodeCredentialsForUpload(opts = {}) {
8000
+ if (await pathExists7(OPENCODE_CREDENTIALS_BACKUP_FILE)) {
8001
+ return stageSingleFileTarball("opencode-creds", OPENCODE_CREDENTIALS_BACKUP_FILE, "auth.json");
8002
+ }
7850
8003
  const hostHome = opts.hostHome ?? homedir11();
7851
8004
  const hostAuth = join14(hostHome, ".local", "share", "opencode", "auth.json");
7852
8005
  if (!await pathExists7(hostAuth)) return emptyResult();
@@ -7908,6 +8061,7 @@ export {
7908
8061
  RELAY_IMAGE_REF,
7909
8062
  hashRpcParams,
7910
8063
  GH_PR_OPS,
8064
+ injectPrCreateHead,
7911
8065
  loadQueueConfig,
7912
8066
  writeJob,
7913
8067
  readJob,
@@ -7966,6 +8120,9 @@ export {
7966
8120
  claudeSessionInfo,
7967
8121
  pullClaudeExtras,
7968
8122
  CREDENTIALS_BACKUP_FILE,
8123
+ CODEX_CREDENTIALS_BACKUP_FILE,
8124
+ OPENCODE_CREDENTIALS_BACKUP_FILE,
8125
+ isRealAgentCredential,
7969
8126
  hostBackupHasCredentials,
7970
8127
  parseSyncResult,
7971
8128
  syncClaudeCredentials,
@@ -8111,4 +8268,4 @@ export {
8111
8268
  browserSessionActive,
8112
8269
  ensureBoxBrowser
8113
8270
  };
8114
- //# sourceMappingURL=chunk-6OZDFNBF.js.map
8271
+ //# sourceMappingURL=chunk-NCJP5MTN.js.map