@madarco/agentbox 0.7.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 (77) hide show
  1. package/dist/_cloud-attach-ZXBCNWJX.js +13 -0
  2. package/dist/{chunk-NW5NYTQM.js → chunk-BXQMIEHC.js} +459 -110
  3. package/dist/chunk-BXQMIEHC.js.map +1 -0
  4. package/dist/{chunk-UK72UQ5U.js → chunk-G3H2L3O2.js} +55 -4
  5. package/dist/chunk-G3H2L3O2.js.map +1 -0
  6. package/dist/{chunk-7KOEFGN2.js → chunk-GU5LW4B5.js} +385 -31
  7. package/dist/chunk-GU5LW4B5.js.map +1 -0
  8. package/dist/chunk-KL36BRN4.js +455 -0
  9. package/dist/chunk-KL36BRN4.js.map +1 -0
  10. package/dist/{chunk-V5KZGB5V.js → chunk-LEV3KICD.js} +18 -2
  11. package/dist/chunk-LEV3KICD.js.map +1 -0
  12. package/dist/chunk-MTVI44DW.js +662 -0
  13. package/dist/chunk-MTVI44DW.js.map +1 -0
  14. package/dist/{chunk-NAVL4R34.js → chunk-NCJP5MTN.js} +1281 -556
  15. package/dist/chunk-NCJP5MTN.js.map +1 -0
  16. package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js → cloud-poller-SUNA6ZQC-2RG5WPRN.js} +2 -2
  17. package/dist/{dist-ETCFRVPA.js → dist-32EZBYG4.js} +50 -20
  18. package/dist/{dist-R67WMLCF.js → dist-CX5CGVEB.js} +120 -10
  19. package/dist/dist-CX5CGVEB.js.map +1 -0
  20. package/dist/{dist-QZGJIBT5.js → dist-GDHP34ZK.js} +141 -75
  21. package/dist/dist-GDHP34ZK.js.map +1 -0
  22. package/dist/dist-XML54CNB.js +849 -0
  23. package/dist/dist-XML54CNB.js.map +1 -0
  24. package/dist/index.js +3881 -867
  25. package/dist/index.js.map +1 -1
  26. package/dist/prepared-state-CL4CWXQA-H5THETIM.js +18 -0
  27. package/dist/prepared-state-CL4CWXQA-H5THETIM.js.map +1 -0
  28. package/package.json +7 -5
  29. package/runtime/daytona/custom-system-CLAUDE.md +39 -0
  30. package/runtime/docker/Dockerfile.box +22 -0
  31. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +1 -1
  32. package/runtime/docker/packages/ctl/dist/bin.cjs +1214 -98
  33. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json +66 -35
  34. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
  35. package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +62 -1
  36. package/runtime/docker/packages/sandbox-docker/scripts/custom-system-CLAUDE.md +15 -4
  37. package/runtime/docker/packages/sandbox-docker/scripts/gh-shim +263 -0
  38. package/runtime/docker/packages/sandbox-docker/scripts/git-shim +131 -0
  39. package/runtime/docker/packages/sandbox-docker/scripts/opencode-agentbox-plugin.js +76 -0
  40. package/runtime/hetzner/agentbox-codex-hooks.json +66 -35
  41. package/runtime/hetzner/agentbox-setup-skill.md +1 -1
  42. package/runtime/hetzner/agentbox-vnc-start +15 -1
  43. package/runtime/hetzner/claude-managed-settings.json +62 -1
  44. package/runtime/hetzner/ctl.cjs +1214 -98
  45. package/runtime/hetzner/custom-system-CLAUDE.md +26 -14
  46. package/runtime/hetzner/gh-shim +263 -0
  47. package/runtime/hetzner/git-shim +131 -0
  48. package/runtime/hetzner/opencode-agentbox-plugin.js +76 -0
  49. package/runtime/hetzner/scripts/install-box.sh +11 -2
  50. package/runtime/relay/bin.cjs +1146 -63
  51. package/runtime/vercel/agentbox-checkpoint-cleanup +52 -0
  52. package/runtime/vercel/agentbox-codex-hooks.json +68 -0
  53. package/runtime/vercel/agentbox-open +28 -0
  54. package/runtime/vercel/agentbox-setup-skill.md +196 -0
  55. package/runtime/vercel/agentbox-vnc-start +91 -0
  56. package/runtime/vercel/claude-managed-settings.json +115 -0
  57. package/runtime/vercel/ctl.cjs +23466 -0
  58. package/runtime/vercel/custom-system-CLAUDE.md +50 -0
  59. package/runtime/vercel/gh-shim +263 -0
  60. package/runtime/vercel/git-shim +131 -0
  61. package/runtime/vercel/scripts/provision.sh +274 -0
  62. package/share/agentbox-setup/SKILL.md +1 -1
  63. package/share/host-skills/agentbox/SKILL.md +29 -0
  64. package/share/host-skills/agentbox-info/SKILL.md +211 -0
  65. package/share/host-skills/codex/agentbox.md +35 -0
  66. package/share/host-skills/opencode/agentbox.md +26 -0
  67. package/dist/_cloud-attach-DMVH6GWO.js +0 -12
  68. package/dist/chunk-7KOEFGN2.js.map +0 -1
  69. package/dist/chunk-NAVL4R34.js.map +0 -1
  70. package/dist/chunk-NW5NYTQM.js.map +0 -1
  71. package/dist/chunk-UK72UQ5U.js.map +0 -1
  72. package/dist/chunk-V5KZGB5V.js.map +0 -1
  73. package/dist/dist-QZGJIBT5.js.map +0 -1
  74. package/dist/dist-R67WMLCF.js.map +0 -1
  75. /package/dist/{_cloud-attach-DMVH6GWO.js.map → _cloud-attach-ZXBCNWJX.js.map} +0 -0
  76. /package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js.map → cloud-poller-SUNA6ZQC-2RG5WPRN.js.map} +0 -0
  77. /package/dist/{dist-ETCFRVPA.js.map → dist-32EZBYG4.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../packages/sandbox-cloud/src/cloud-provider.ts","../../../packages/sandbox-cloud/src/agent-credentials.ts","../../../packages/sandbox-cloud/src/checkpoint.ts","../../../packages/sandbox-cloud/src/env-files.ts","../../../packages/sandbox-cloud/src/expose-ports.ts","../../../packages/sandbox-cloud/src/cloud-cp.ts","../../../packages/sandbox-cloud/src/shell.ts","../../../packages/sandbox-cloud/src/ctl-launch.ts","../../../packages/sandbox-cloud/src/dockerd-launch.ts","../../../packages/sandbox-cloud/src/vnc-launch.ts","../../../packages/sandbox-cloud/src/workspace-seed.ts","../../../packages/sandbox-cloud/src/mock-backend.ts","../../../packages/sandbox-cloud/src/index.ts"],"sourcesContent":["/**\n * `createCloudProvider(backend)` — composes a full `Provider` from a thin\n * `CloudBackend`. Every cloud backend (Daytona, future Vercel, …) reuses this\n * scaffolding instead of re-implementing workspace seeding, ctl launch, state\n * persistence, URL resolution, etc.\n *\n * v0 covers create / lifecycle / probe / exec / resolveUrl on top of the\n * `CloudBackend` primitives. The host-poller comms layer (queued git.push,\n * status event mirroring, prompts) is Phase 4 — until then a cloud box's\n * `agentbox-ctl` runs without a relay, and host-only RPCs surface a clear\n * \"no relay configured\" error.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport { basename } from 'node:path';\nimport type {\n AttachKind,\n AttachSpec,\n BoxRecord,\n BoxResourceStats,\n BoxRuntimeState,\n BuildAttachOptions,\n CloudBackend,\n CloudHandle,\n CreateBoxRequest,\n CreatedBox,\n ExecOptions,\n ExecResult,\n InspectedBox,\n Provider,\n ProviderCheckpoint,\n} from '@agentbox/core';\nimport { allocateProjectIndex, readState, recordBox, removeBoxRecord } from '@agentbox/sandbox-core';\nimport {\n ensureRelay,\n forgetBoxFromRelay,\n generateRelayToken,\n generateVncPassword,\n portlessAlias,\n portlessGetUrl,\n portlessUnalias,\n registerBoxWithRelay,\n} from '@agentbox/sandbox-docker';\nimport {\n ensureAgentVolumesForCloud,\n seedAgentVolumesIfFresh,\n} from './agent-credentials.js';\nimport {\n cloudSnapshotName,\n listCloudCheckpoints,\n removeCloudCheckpointDir,\n resolveCloudCheckpoint,\n writeCloudCheckpointManifest,\n} from './checkpoint.js';\nimport { uploadEnvFiles } from './env-files.js';\nimport { readExposedServicePorts } from './expose-ports.js';\nimport {\n downloadFromCloudBox,\n pullCloudDirContents,\n uploadToCloudBox,\n} from './cloud-cp.js';\nimport { launchCloudCtlDaemon } from './ctl-launch.js';\nimport { launchCloudDockerdDaemon } from './dockerd-launch.js';\nimport { quoteShellArgv } from './shell.js';\nimport { launchCloudVncDaemon } from './vnc-launch.js';\nimport { seedCloudWorkspace } from './workspace-seed.js';\n\n/** Workspace mount path inside every cloud sandbox. Matches the Docker model. */\nexport const CLOUD_WORKSPACE_DIR = '/workspace';\n/**\n * In-box port the supervisor's WebProxy binds to. Matches `RESERVED_WEB_PORT`\n * in `@agentbox/ctl` (the only container port AgentBox publishes) — node has\n * `cap_net_bind_service` set in both the Docker and Hetzner base images so the\n * bind to <1024 needs no root. Daytona's base image inherits the same setcap\n * via Dockerfile.box.\n */\nexport const CLOUD_WEB_PROXY_PORT = 80;\n/** In-box port the noVNC viewer (websockify) serves on — fixed by Dockerfile.box. */\nexport const CLOUD_VNC_PORT = 6080;\n/**\n * Default expiry for browser-bound signed preview URLs. 1h matches Daytona's\n * docs recommendation: long enough for one CLI invocation + browser session,\n * short enough that a stale link doesn't outlive the box. Override with the\n * CLI `--ttl` flag when sharing or running long-lived sessions.\n */\nexport const DEFAULT_SIGNED_URL_TTL_SECONDS = 3600;\n\n/**\n * Provider-neutral image selector. Cloud backends typically resolve it to a\n * snapshot ref (Daytona) or a registry image. Backends may translate it via\n * `provisionImage` (see `CreateCloudProviderOptions`).\n */\nexport interface CreateCloudProviderOptions {\n /**\n * Translate the request's image to a backend-specific image / snapshot ref.\n * When omitted the backend is handed `req.image` verbatim (or the v0 default\n * `agentbox/box:dev`, which most cloud backends won't be able to resolve —\n * Daytona uses its snapshot helper here).\n */\n provisionImage?(req: CreateBoxRequest): Promise<string>;\n /**\n * Per-create cloud resource ceiling. Default: 2 cpu / 4 GiB / 8 GiB disk.\n */\n defaultResources?: { cpu?: number; memory?: number; disk?: number };\n}\n\nconst FALLBACK_IMAGE = 'agentbox/box:dev';\n\n/** Default Portless no-TLS proxy port — matches the host wizard's choice. */\nconst DEFAULT_PORTLESS_PROXY_PORT = 1355;\n\n/**\n * Parse a host portless preview URL like `http://my-box.localhost:1355` /\n * `https://my-box.localhost` into the proxy mode the in-VPS mirror should\n * match. Returns `undefined` when the URL isn't a `*.localhost` portless URL\n * (e.g. portless isn't installed and we fell back to a loopback URL).\n */\nfunction parsePortlessUrl(url: string): { proxyPort: number; tls: boolean } | undefined {\n try {\n const u = new URL(url);\n if (!u.hostname.endsWith('.localhost')) return undefined;\n const tls = u.protocol === 'https:';\n const proxyPort = u.port\n ? Number.parseInt(u.port, 10)\n : tls\n ? 443\n : 80;\n if (!Number.isFinite(proxyPort)) return undefined;\n return { proxyPort, tls };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Parse a loopback `http://127.0.0.1:<N>` URL into its port. Returns\n * `undefined` for any non-loopback URL (Daytona-style public URLs naturally\n * skip the Portless alias path).\n */\nfunction parseLoopbackPort(url: string): number | undefined {\n try {\n const u = new URL(url);\n if (u.hostname !== '127.0.0.1' && u.hostname !== 'localhost') return undefined;\n const port = Number.parseInt(u.port, 10);\n return Number.isFinite(port) ? port : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Register the host Portless alias for `<boxName>.localhost -> <webPreviewUrl>`\n * and bring up the in-VPS mirror proxy so the same URL works from inside the\n * box. Best-effort: every step is gated on a previous step's success, and any\n * failure logs but doesn't throw. Returns `{ alias, url }` when the host alias\n * landed (so the caller can persist it on the BoxRecord), `undefined` otherwise.\n */\nasync function bootstrapPortlessForCloudBox(\n backend: CloudBackend,\n handle: CloudHandle,\n args: { boxName: string; webPreviewUrl: string; webPort: number; onLog: (line: string) => void },\n): Promise<{ alias: string; url: string } | undefined> {\n const localPort = parseLoopbackPort(args.webPreviewUrl);\n if (localPort === undefined) return undefined;\n const ok = await portlessAlias(args.boxName, localPort);\n if (!ok) {\n args.onLog(\n `portless: alias not registered (portless CLI missing or not running) — host URL stays http://127.0.0.1:${String(localPort)}`,\n );\n return undefined;\n }\n const url = await portlessGetUrl(args.boxName);\n args.onLog(`portless alias ${url} -> 127.0.0.1:${String(localPort)}`);\n if (backend.startInBoxPortless) {\n const mode = parsePortlessUrl(url) ?? { proxyPort: DEFAULT_PORTLESS_PROXY_PORT, tls: false };\n try {\n await backend.startInBoxPortless(handle, {\n boxName: args.boxName,\n proxyPort: mode.proxyPort,\n tls: mode.tls,\n webPort: args.webPort,\n });\n args.onLog(\n `portless: in-box mirror up on 127.0.0.1:${String(mode.proxyPort)} (${mode.tls ? 'https' : 'http'})`,\n );\n } catch (err) {\n args.onLog(\n `portless: in-box mirror failed (continuing): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n return { alias: args.boxName, url };\n}\n\nexport function createCloudProvider(\n backend: CloudBackend,\n opts: CreateCloudProviderOptions = {},\n): Provider {\n const providerName = backend.name;\n\n function handleFor(box: BoxRecord): CloudHandle {\n const sandboxId = box.cloud?.sandboxId;\n if (!sandboxId) {\n throw new Error(`cloud box ${box.name} has no sandboxId — record is malformed`);\n }\n return { sandboxId };\n }\n\n /** Resolve a fresh per-cloud-box id + name + branch. */\n function mintBox(req: CreateBoxRequest): {\n id: string;\n name: string;\n branch: string;\n } {\n const id = randomBytes(4).toString('hex');\n const name = req.name ?? `${basename(req.workspacePath)}-${id}`;\n return {\n id,\n name,\n branch: `agentbox/${name}`,\n };\n }\n\n async function probe(box: BoxRecord): Promise<BoxRuntimeState> {\n try {\n const h = handleFor(box);\n const state = await backend.state(h);\n // CloudState aligns with BoxRuntimeState by construction.\n return state;\n } catch {\n return 'missing';\n }\n }\n\n return {\n name: providerName,\n\n async create(req: CreateBoxRequest): Promise<CreatedBox> {\n const log = req.onLog ?? (() => {});\n const { id, name, branch } = mintBox(req);\n const image = opts.provisionImage ? await opts.provisionImage(req) : (req.image ?? FALLBACK_IMAGE);\n const resources = opts.defaultResources ?? { cpu: 2, memory: 4, disk: 8 };\n\n // Per-box tokens: `relayToken` authenticates the in-box agent to its\n // in-sandbox relay (`/events`, `/rpc` bearer); `bridgeToken` separately\n // authenticates the HOST poller to the in-sandbox relay's `/bridge/*`\n // routes. Distinct so a compromised agent can't impersonate the host.\n const relayToken = generateRelayToken();\n const bridgeToken = generateRelayToken();\n\n // Bring the host relay up before we provision — registering the box\n // (and starting its CloudBoxPoller) happens at the bottom of this\n // function, and the loop wants the host relay reachable.\n try {\n await ensureRelay({ onLog: log });\n } catch (err) {\n log(`relay ensure failed (continuing): ${err instanceof Error ? err.message : String(err)}`);\n }\n\n // Resolve any cloud checkpoint the caller requested. When found, we\n // boot from the snapshot (which already carries /workspace + any\n // installed deps) instead of from the base image, and skip the\n // workspace-seeding step below. A `checkpointRef` set for a checkpoint\n // that doesn't exist for THIS backend is logged and silently dropped —\n // matches the wizard's provider-aware behavior (the user may have a\n // Docker checkpoint with the same name; that's not our store).\n let snapshotName: string | undefined;\n let resolvedCheckpointRef: string | undefined;\n if (req.checkpointRef && req.projectRoot) {\n const found = await resolveCloudCheckpoint(req.projectRoot, backend.name, req.checkpointRef);\n if (found) {\n snapshotName = found.manifest.snapshotName;\n resolvedCheckpointRef = found.name;\n log(`provisioning from cloud checkpoint '${found.name}' (snapshot ${snapshotName})`);\n } else {\n log(\n `cloud checkpoint '${req.checkpointRef}' not found for ${backend.name}; provisioning from base image`,\n );\n }\n }\n\n // Reserve per-agent credential volumes (Claude / Codex / OpenCode)\n // before provision so we can pass them as mounts in the same SDK call —\n // Daytona only attaches volumes at create time, not after. Backends\n // without a volume primitive return an empty list and we degrade to\n // \"user logs in inside the box\" the way cloud worked before.\n const agentVolumes = await ensureAgentVolumesForCloud(backend, { onLog: log });\n\n log(\n snapshotName\n ? `provisioning ${providerName} sandbox from snapshot`\n : `provisioning ${providerName} sandbox`,\n );\n const handle = await backend.provision({\n name,\n image,\n snapshot: snapshotName,\n resources,\n env: {\n AGENTBOX_BOX_ID: id,\n AGENTBOX_BOX_NAME: name,\n AGENTBOX_BOX_KIND: 'cloud',\n // In-sandbox relay is on the box's loopback at the default port.\n AGENTBOX_RELAY_URL: `http://127.0.0.1:${String(8787)}`,\n AGENTBOX_RELAY_TOKEN: relayToken,\n AGENTBOX_BRIDGE_TOKEN: bridgeToken,\n ...agentVolumes.env,\n },\n volumes: agentVolumes.mounts,\n onLog: log,\n });\n\n try {\n if (snapshotName) {\n // Snapshot already carries /workspace (captured by the source box's\n // `agentbox checkpoint create`). Re-seeding would clobber the\n // user's setup state. Match Docker's `applyCheckpointRef` behavior.\n log('skipping workspace seed — snapshot already contains /workspace');\n } else {\n await seedCloudWorkspace({\n backend,\n handle,\n workspacePath: req.workspacePath,\n branch,\n workspaceDir: CLOUD_WORKSPACE_DIR,\n onLog: log,\n });\n }\n\n // After the sandbox is up with the credential volumes mounted, seed\n // any volume that doesn't already carry a `.agentbox-seeded-at`\n // marker from the host's filtered ~/.claude / ~/.codex /\n // opencode tree. Idempotent per agent — subsequent boxes find the\n // marker and skip the upload entirely.\n if (agentVolumes.agents.length > 0) {\n await seedAgentVolumesIfFresh(backend, handle, {\n agents: agentVolumes.agents,\n hostWorkspace: req.workspacePath,\n onLog: log,\n });\n }\n\n // Copy the env/config files the setup wizard collected (`.env`,\n // `secrets.toml`, `agentbox.yaml`, …) into `/workspace`. The Docker\n // provider does the same via copyHostEnvFilesToBox; before this hook\n // these files were silently dropped on the cloud path.\n if (req.envFilesToImport && req.envFilesToImport.length > 0) {\n const { copied } = await uploadEnvFiles({\n backend,\n handle,\n workspacePath: req.workspacePath,\n files: req.envFilesToImport,\n workspaceDir: CLOUD_WORKSPACE_DIR,\n onLog: log,\n });\n if (copied > 0) log(`copied ${String(copied)} env/config file(s) into /workspace`);\n }\n\n log('launching agentbox-ctl daemon');\n await launchCloudCtlDaemon({\n backend,\n handle,\n boxId: id,\n boxName: name,\n relayUrl: `http://127.0.0.1:${String(8787)}`,\n relayToken,\n bridgeToken,\n });\n\n // Always-on in-box dockerd, matching the Docker provider\n // (packages/sandbox-docker/src/create.ts:788). The image already bakes\n // /usr/local/bin/agentbox-dockerd-start; Daytona sandboxes ship with\n // CAP_SYS_ADMIN so it starts cleanly. Best-effort — a slow or failed\n // start shouldn't fail create; `agentbox start` re-launches it on\n // resume because dockerd dies with the sandbox.\n log('launching in-box dockerd');\n try {\n const dockerd = await launchCloudDockerdDaemon({ backend, handle, timeoutMs: 60_000 });\n if (!dockerd.up) log(`dockerd did not become ready (continuing): ${dockerd.reason ?? 'unknown'}`);\n } catch (err) {\n log(`dockerd daemon launch failed (continuing): ${err instanceof Error ? err.message : String(err)}`);\n }\n\n // Mint the per-box VNC password and start the in-sandbox VNC stack\n // when VNC is opted in (default-on, matching Docker). Best-effort —\n // a failure logs but doesn't fail create; `agentbox screen` will\n // surface \"daemon may not be up\" if the URL stays 502.\n const vncEnabled = req.vnc?.enabled !== false;\n const vncPassword = vncEnabled ? generateVncPassword() : undefined;\n if (vncEnabled && vncPassword) {\n log('launching VNC stack (Xvnc + websockify + noVNC)');\n try {\n await launchCloudVncDaemon({ backend, handle, vncPassword });\n } catch (err) {\n log(\n `VNC daemon launch failed (continuing): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n // The web preview URL is best-effort at create — most boxes won't\n // have a service on CLOUD_WEB_PROXY_PORT until the supervisor schedules\n // the `expose:` service. `agentbox url` re-resolves on demand.\n let webPreview: { url: string; token?: string } | undefined;\n try {\n webPreview = await backend.previewUrl(handle, CLOUD_WEB_PROXY_PORT);\n } catch {\n webPreview = undefined;\n }\n\n // Portless host alias + in-VPS mirror. Default-on for backends whose\n // `previewUrl()` returns a loopback URL (Hetzner); naturally skipped\n // for public-URL backends (Daytona) because the URL doesn't parse as\n // 127.0.0.1. Caller can opt out via `providerOptions.portless: false`.\n // Best-effort: every step logs + continues on failure.\n const portlessOpt = (req.providerOptions?.['portless'] as boolean | undefined) ?? true;\n let portlessAliasName: string | undefined;\n let portlessUrlResolved: string | undefined;\n if (portlessOpt && webPreview) {\n const r = await bootstrapPortlessForCloudBox(backend, handle, {\n boxName: name,\n webPreviewUrl: webPreview.url,\n webPort: CLOUD_WEB_PROXY_PORT,\n onLog: log,\n });\n if (r) {\n portlessAliasName = r.alias;\n portlessUrlResolved = r.url;\n }\n }\n // Per-service preview URLs. Each `services.*.expose.port` from\n // `agentbox.yaml` gets a direct preview URL alongside the main\n // WebProxy URL — lets users hit services without going through the\n // WebProxy. Best-effort: a failed `previewUrl` for a given port\n // just omits it from the map.\n const servicePorts = await readExposedServicePorts(req.workspacePath);\n const servicePreviews: Record<number, string> = {};\n for (const port of servicePorts) {\n if (port === CLOUD_WEB_PROXY_PORT) continue;\n try {\n const p = await backend.previewUrl(handle, port);\n servicePreviews[port] = p.url;\n } catch {\n // skip this port; the user can still hit the service via the\n // WebProxy if the YAML wires `expose.as: 80`.\n }\n }\n\n // The bridge preview URL is critical: it's how the host CloudBoxPoller\n // reaches the in-sandbox relay. The ctl daemon binds 0.0.0.0:8787 in\n // box mode (cloud), so the Daytona preview proxy can route to it.\n let relayPreview: { url: string; token?: string } | undefined;\n try {\n relayPreview = await backend.previewUrl(handle, 8787);\n } catch {\n relayPreview = undefined;\n }\n\n // Tell the host relay about this cloud box so it spawns a poller.\n // Best-effort: a failed register doesn't break create (status / git\n // push just won't reach the host until a later register).\n if (relayPreview) {\n try {\n await registerBoxWithRelay({\n boxId: id,\n token: relayToken,\n name,\n kind: 'cloud',\n backend: backend.name,\n previewUrl: relayPreview.url,\n previewToken: relayPreview.token,\n bridgeToken,\n createdAt: new Date().toISOString(),\n });\n } catch (err) {\n log(\n `register with host relay failed (continuing): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n const state = await readState();\n const projectIndex = req.projectRoot\n ? allocateProjectIndex(state, req.projectRoot)\n : undefined;\n\n const record: BoxRecord = {\n id,\n name,\n provider: providerName,\n // `container` carries the sandbox id with a `cloud:` prefix —\n // unique within state, never collides with a real docker\n // container, and grepping for `agentbox-cloud-*` (the old\n // synthetic value) finds nothing now. `image` mirrors the\n // resolved cloud image so `BoxRecord.image: string` stays\n // required without docker-internal readers seeing `undefined`.\n container: `cloud:${handle.sandboxId}`,\n image,\n workspacePath: req.workspacePath,\n projectRoot: req.projectRoot,\n projectIndex,\n relayToken,\n withPlaywright: req.withPlaywright,\n withEnv: req.withEnv,\n portlessAlias: portlessAliasName,\n portlessUrl: portlessUrlResolved,\n vncEnabled,\n vncPassword,\n vncContainerPort: vncEnabled ? CLOUD_VNC_PORT : undefined,\n resourceLimits: req.limits\n ? {\n memoryBytes: req.limits.memoryBytes ?? undefined,\n cpus: req.limits.cpus ?? undefined,\n pidsLimit: req.limits.pidsLimit ?? undefined,\n disk: req.limits.disk ?? undefined,\n }\n : undefined,\n cloud: {\n backend: backend.name,\n sandboxId: handle.sandboxId,\n image,\n webPort: CLOUD_WEB_PROXY_PORT,\n previewUrls: ((): Record<number, string> | undefined => {\n const m: Record<number, string> = { ...servicePreviews };\n if (webPreview) m[CLOUD_WEB_PROXY_PORT] = webPreview.url;\n return Object.keys(m).length > 0 ? m : undefined;\n })(),\n relayPreviewUrl: relayPreview?.url,\n relayPreviewToken: relayPreview?.token,\n bridgeToken,\n snapshotRef: resolvedCheckpointRef,\n },\n createdAt: new Date().toISOString(),\n };\n await recordBox(record);\n return { record, imageBuilt: false };\n } catch (err) {\n // Best-effort teardown of the half-provisioned sandbox so a failed\n // create doesn't leave the user paying for an inert box.\n try {\n await backend.destroy(handle);\n } catch {\n // The user is going to see the original error; suppressing this\n // secondary failure keeps the message clean.\n }\n throw err;\n }\n },\n\n async start(box: BoxRecord): Promise<BoxRecord> {\n const h = handleFor(box);\n await backend.start(h);\n // Preview URLs (and their tokens) can rotate across stop/start — refresh\n // the web + relay preview URLs and persist so `agentbox url` and the\n // host poller see the live values.\n const webPort = box.cloud?.webPort ?? CLOUD_WEB_PROXY_PORT;\n let webPreview: { url: string; token?: string } | undefined;\n try {\n webPreview = await backend.previewUrl(h, webPort);\n } catch {\n const cached = box.cloud?.previewUrls?.[webPort];\n webPreview = cached ? { url: cached } : undefined;\n }\n // Re-mint per-service preview URLs from `agentbox.yaml`. Daytona's\n // preview URLs rotate when a sandbox restarts, so any cached ports\n // need to be re-resolved against the live handle.\n const servicePreviews: Record<number, string> = {};\n try {\n const ports = await readExposedServicePorts(box.workspacePath);\n for (const port of ports) {\n if (port === webPort) continue;\n try {\n const p = await backend.previewUrl(h, port);\n servicePreviews[port] = p.url;\n } catch {\n // skip — falls back to cached value below if any\n }\n }\n } catch {\n // workspace path missing / yaml unreadable: keep cached previewUrls\n }\n let relayPreview: { url: string; token?: string } | undefined;\n try {\n relayPreview = await backend.previewUrl(h, 8787);\n } catch {\n relayPreview = box.cloud?.relayPreviewUrl\n ? { url: box.cloud.relayPreviewUrl, token: box.cloud.relayPreviewToken }\n : undefined;\n }\n // Build the refreshed preview map: keep cached values for ports we\n // couldn't re-resolve, overlay fresh URLs from this start.\n const mergedPreviews: Record<number, string> = {\n ...(box.cloud?.previewUrls ?? {}),\n ...servicePreviews,\n };\n if (webPreview !== undefined) mergedPreviews[webPort] = webPreview.url;\n\n // Portless: the `ssh -L` local port is fresh after `agentbox start`\n // (pickFreePort picks again), and the in-VPS portless proxy died with\n // the VPS. Re-register the host alias against the new local port and\n // bring the in-box mirror back up. Skipped when no host portless alias\n // was set originally (user opted out at create) or when the URL is\n // non-loopback (Daytona).\n let portlessAliasName: string | undefined = box.portlessAlias;\n let portlessUrlResolved: string | undefined = box.portlessUrl;\n if (box.portlessAlias && webPreview) {\n const r = await bootstrapPortlessForCloudBox(backend, h, {\n boxName: box.name,\n webPreviewUrl: webPreview.url,\n webPort,\n onLog: () => {},\n });\n if (r) {\n portlessAliasName = r.alias;\n portlessUrlResolved = r.url;\n }\n }\n\n const next: BoxRecord = {\n ...box,\n portlessAlias: portlessAliasName,\n portlessUrl: portlessUrlResolved,\n cloud: {\n ...(box.cloud ?? { backend: providerName, sandboxId: h.sandboxId }),\n webPort,\n previewUrls: Object.keys(mergedPreviews).length > 0 ? mergedPreviews : undefined,\n relayPreviewUrl: relayPreview?.url ?? box.cloud?.relayPreviewUrl,\n relayPreviewToken: relayPreview?.token ?? box.cloud?.relayPreviewToken,\n },\n };\n await recordBox(next);\n // Re-launch the ctl daemon — it dies with the sandbox.\n await launchCloudCtlDaemon({\n backend,\n handle: h,\n boxId: box.id,\n boxName: box.name,\n relayUrl: `http://127.0.0.1:${String(8787)}`,\n relayToken: box.relayToken ?? '',\n bridgeToken: box.cloud?.bridgeToken,\n });\n // Re-launch in-box dockerd — also dies with the sandbox. Best-effort,\n // mirrors the docker provider's lifecycle.ts:276 relaunch.\n try {\n const dockerd = await launchCloudDockerdDaemon({ backend, handle: h, timeoutMs: 60_000 });\n if (!dockerd.up) {\n // swallowed; surface only on follow-up `docker info`\n }\n } catch {\n // best-effort\n }\n // Re-launch the VNC stack — Xvnc + websockify die with the sandbox.\n // Best-effort: a failure here shouldn't block start; `agentbox screen`\n // surfaces the missing daemon with a clear error.\n if (box.vncEnabled && box.vncPassword) {\n try {\n await launchCloudVncDaemon({ backend, handle: h, vncPassword: box.vncPassword });\n } catch {\n // swallowed; user-visible error comes from `agentbox screen` if it\n // can't reach websockify after a few retries.\n }\n }\n // Re-register with the host relay so its CloudBoxPoller picks up the\n // fresh preview URL/token.\n if (relayPreview && box.relayToken && box.cloud?.bridgeToken) {\n try {\n await registerBoxWithRelay({\n boxId: box.id,\n token: box.relayToken,\n name: box.name,\n kind: 'cloud',\n backend: backend.name,\n previewUrl: relayPreview.url,\n previewToken: relayPreview.token,\n bridgeToken: box.cloud.bridgeToken,\n createdAt: box.createdAt,\n projectIndex: box.projectIndex,\n });\n } catch {\n // best-effort\n }\n }\n return next;\n },\n\n async pause(box: BoxRecord): Promise<void> {\n await backend.pause(handleFor(box));\n },\n\n async resume(box: BoxRecord): Promise<void> {\n await backend.resume(handleFor(box));\n },\n\n async stop(box: BoxRecord): Promise<void> {\n await backend.stop(handleFor(box));\n },\n\n async destroy(box: BoxRecord): Promise<void> {\n try {\n await backend.destroy(handleFor(box));\n } catch (err) {\n // Surface but don't block state cleanup — a \"missing\" sandbox should\n // still let `agentbox destroy` drop the local record.\n const msg = err instanceof Error ? err.message : String(err);\n if (!/not.?found|missing/i.test(msg)) throw err;\n }\n // Best-effort: drop the host Portless alias so `<box>.localhost` stops\n // pointing at a dead ssh -L. The in-VPS portless dies with the VPS.\n if (box.portlessAlias) {\n try {\n await portlessUnalias(box.portlessAlias);\n } catch {\n // portlessUnalias swallows already; paranoid catch in case.\n }\n }\n // Best-effort: stop the host poller and drop the registration.\n try {\n await forgetBoxFromRelay(box.id);\n } catch {\n // forgetBoxFromRelay already swallows; this catch is paranoid.\n }\n await removeBoxRecord(box.id);\n },\n\n async probeState(box: BoxRecord): Promise<BoxRuntimeState> {\n return probe(box);\n },\n\n async inspect(box: BoxRecord): Promise<InspectedBox> {\n const state = await probe(box);\n const webPort = box.cloud?.webPort ?? CLOUD_WEB_PROXY_PORT;\n // Prefer the stable Portless URL for the `web` endpoint when one was\n // registered — matches Docker's endpoint shape (sandbox-docker/src/\n // endpoints.ts:108-117) so `<name>.localhost` shows up uniformly in\n // `agentbox list` / `inspect`. Falls back to the ephemeral preview URL\n // when Portless wasn't enabled or didn't take.\n const portlessWebUrl =\n box.portlessAlias !== undefined\n ? (box.portlessUrl ?? `https://${box.portlessAlias}.localhost`)\n : undefined;\n const cachedWebUrl = box.cloud?.previewUrls?.[webPort];\n const webUrl = portlessWebUrl ?? cachedWebUrl;\n // Surface each per-service preview URL alongside the main WebProxy.\n // Naming is `service-<port>` because we don't track the YAML name\n // -> port map on the record (avoids extra wire shape just for\n // display).\n const endpoints: Array<{\n kind: 'web';\n name: string;\n containerPort: number;\n url: string;\n reachable: boolean;\n }> = [];\n if (webUrl) {\n endpoints.push({\n kind: 'web',\n name: 'web',\n containerPort: webPort,\n url: webUrl,\n reachable: true,\n });\n }\n for (const [portStr, url] of Object.entries(box.cloud?.previewUrls ?? {})) {\n const port = Number.parseInt(portStr, 10);\n if (!Number.isFinite(port) || port === webPort) continue;\n endpoints.push({\n kind: 'web',\n name: `service-${String(port)}`,\n containerPort: port,\n url,\n reachable: true,\n });\n }\n return {\n record: box,\n state,\n endpoints: {\n domain: webUrl ? new URL(webUrl).host : '',\n domainIsOrb: false,\n endpoints,\n },\n raw: undefined,\n };\n },\n\n async exec(box: BoxRecord, argv: string[], opts?: ExecOptions): Promise<ExecResult> {\n const r = await backend.exec(handleFor(box), quoteShellArgv(argv), {\n cwd: opts?.cwd,\n env: opts?.env,\n user: opts?.user,\n });\n return { exitCode: r.exitCode, stdout: r.stdout, stderr: r.stderr };\n },\n\n async buildAttach(\n box: BoxRecord,\n kind: AttachKind,\n opts?: BuildAttachOptions,\n ): Promise<AttachSpec> {\n if (!backend.attachArgv) {\n throw new Error(\n `cloud backend '${backend.name}' does not implement attachArgv — interactive attach not supported`,\n );\n }\n const handle = handleFor(box);\n const baseArgv = await backend.attachArgv(handle);\n const inner = renderInnerCommand(kind, opts);\n // -t forces TTY allocation on the remote side (the SSH default of\n // skipping TTY when a command is provided would break tmux + readline).\n const argv = [...baseArgv.slice(1), '-t', inner];\n // Keep argv[0] = the program name (ssh) so callers can split.\n const fullArgv = [baseArgv[0]!, ...argv];\n const cleanup = backend.revokeAttachToken\n ? async (): Promise<void> => {\n await backend.revokeAttachToken!(handle, baseArgv);\n }\n : undefined;\n return { argv: fullArgv, cleanup };\n },\n\n async uploadPath(\n box: BoxRecord,\n hostSrc: string,\n boxDst: string,\n ): Promise<{ finalPath: string }> {\n return uploadToCloudBox(backend, handleFor(box), hostSrc, boxDst);\n },\n\n async downloadPath(\n box: BoxRecord,\n boxSrc: string,\n hostDst: string,\n ): Promise<{ finalPath: string }> {\n return downloadFromCloudBox(backend, handleFor(box), boxSrc, hostDst);\n },\n\n async downloadDirContents(\n box: BoxRecord,\n boxSrc: string,\n hostDst: string,\n ): Promise<{ finalPath: string }> {\n return pullCloudDirContents(backend, handleFor(box), boxSrc, hostDst);\n },\n\n async resolveUrl(\n box: BoxRecord,\n opts?: { loopback?: boolean; kind?: 'web' | 'vnc'; ttl?: number },\n ): Promise<string> {\n const h = handleFor(box);\n const kind = opts?.kind ?? 'web';\n // VNC port is fixed by Dockerfile.box (websockify serves noVNC on :6080).\n const port = kind === 'vnc' ? CLOUD_VNC_PORT : (box.cloud?.webPort ?? CLOUD_WEB_PROXY_PORT);\n // Always re-resolve through the SDK — cached URLs on the record may be\n // from a previous start whose token has rotated. Prefer signed URLs\n // because the user is about to hand the URL to a browser (no way to\n // attach an `x-daytona-preview-token` header from a click).\n if (backend.signedPreviewUrl) {\n const ttl = opts?.ttl ?? DEFAULT_SIGNED_URL_TTL_SECONDS;\n const signed = await backend.signedPreviewUrl(h, port, ttl);\n return signed.url;\n }\n // No signed-URL primitive: fall back to the header-token URL, but fail\n // loudly so the caller sees this isn't usable in a browser as-is.\n const p = await backend.previewUrl(h, port);\n throw new Error(\n `cloud backend '${backend.name}' does not support signed preview URLs; ` +\n `the standard URL (${p.url}) requires a header token (e.g. x-daytona-preview-token: ${p.token ?? '<unset>'}) ` +\n `that browsers can't attach from a click. Use a programmatic client or wait for backend support.`,\n );\n },\n\n // Cloud checkpoint capability. Backends without `createSnapshot` get a\n // capability stub whose methods throw — the CLI's `agentbox checkpoint\n // create` then surfaces a clean \"not supported\" error rather than a\n // silent no-op.\n checkpoint: makeCloudCheckpoint(backend),\n\n // stats is provider-optional; cloud backends without a metrics API just\n // omit it. Backends that have one can decorate the returned provider.\n };\n}\n\n/**\n * Build the `Provider.checkpoint` capability for a cloud backend.\n *\n * - `create(box, name)` captures the live sandbox via `backend.createSnapshot`\n * and persists a thin manifest on the host (`~/.agentbox/cloud-checkpoints/…`).\n * - `list(projectRoot)` reads the on-disk manifest store.\n * - `remove(projectRoot, ref)` deletes the Daytona snapshot best-effort and\n * removes the local manifest unconditionally so a remote-only failure\n * doesn't leave the user with a dead pointer.\n */\nfunction makeCloudCheckpoint(backend: CloudBackend): ProviderCheckpoint {\n return {\n async create(box: BoxRecord, name: string) {\n if (!backend.createSnapshot) {\n throw new Error(\n `cloud backend '${backend.name}' doesn't support snapshots — \\`agentbox checkpoint\\` unavailable`,\n );\n }\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(`cloud box ${box.name} has no sandboxId — record is malformed`);\n }\n const snapshotName = cloudSnapshotName(box.projectRoot, name);\n await backend.createSnapshot({ sandboxId: box.cloud.sandboxId }, snapshotName);\n const info = await writeCloudCheckpointManifest(box.projectRoot, backend.name, name, {\n snapshotName,\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 if (backend.deleteSnapshot) {\n try {\n await backend.deleteSnapshot(entry.manifest.snapshotName);\n } catch {\n // Best-effort: even if the remote delete fails (network, perms,\n // or already-gone), drop the local manifest so the user isn't\n // stuck with a pointer to nothing. They can clean up the orphan\n // snapshot from the Daytona dashboard.\n }\n }\n await removeCloudCheckpointDir(projectRoot, backend.name, ref);\n },\n };\n}\n\n/**\n * Build the inner shell command tmux runs inside the cloud sandbox for an\n * attach. The string is later embedded in `ssh ... -t '<cmd>'`, so it must\n * be a single shell-safe phrase (the SSH client passes it to the remote\n * `sshd` which feeds it to the user's login shell).\n *\n * `tmux new-session -A` attaches if a session with the given name exists,\n * otherwise creates a fresh one running the fallback command. Matches the\n * Docker shell command's tmux semantics so the UX feels the same.\n */\nfunction renderInnerCommand(kind: AttachKind, opts?: BuildAttachOptions): string {\n const sessionName = opts?.sessionName ?? defaultSessionName(kind);\n const fallback = opts?.command ?? defaultCommand(kind, opts);\n if (kind === 'logs') {\n // logs always tails; tmux makes no sense here.\n return fallback;\n }\n if (opts?.noTmux) {\n return fallback;\n }\n // Single-quote the inner cmd so tmux gets exactly one argv element. Use\n // `command -v tmux` so a missing tmux fails fast with a clear error.\n // `-c /workspace` starts the session in the box's workspace dir so claude/\n // codex/opencode see /workspace as their cwd (otherwise tmux inherits the\n // SSH login shell's $HOME and the agents prompt for workspace-trust).\n return `command -v tmux >/dev/null || { echo \"tmux not installed in sandbox\"; exit 127; }; exec tmux new-session -A -c ${shellSingle(CLOUD_WORKSPACE_DIR)} -s ${shellSingle(sessionName)} ${shellSingle(fallback)}`;\n}\n\nfunction defaultSessionName(kind: AttachKind): string {\n switch (kind) {\n case 'shell':\n return 'shell';\n case 'agent':\n return 'agent';\n case 'logs':\n return 'logs';\n }\n}\n\nfunction defaultCommand(kind: AttachKind, opts?: BuildAttachOptions): string {\n switch (kind) {\n case 'shell':\n return 'bash -l';\n case 'agent':\n // Caller didn't tell us which agent — fall back to a login shell;\n // claude/codex/opencode wrappers pass an explicit `command`.\n return 'bash -l';\n case 'logs': {\n if (!opts?.service) {\n return 'echo \"no service specified — set BuildAttachOptions.service\"';\n }\n // Prefer `agentbox-ctl logs` so the cloud follow stream matches the\n // docker format (timestamps + stream marker, ring-buffered tail). The\n // ctl daemon binds the unix socket regardless of how we exec in.\n const tail = opts.tail !== undefined ? String(opts.tail) : '200';\n const args = [`--tail ${shellSingle(tail)}`];\n if (opts.follow !== false) args.push('--follow');\n return `/usr/local/bin/agentbox-ctl logs ${shellSingle(opts.service)} ${args.join(' ')}`;\n }\n }\n}\n\n/** Wrap an arbitrary string in single quotes for embedding in a shell command. */\nfunction shellSingle(s: string): string {\n return \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n\n/** Helper: returns a BoxResourceStats stub so callers needn't unwrap optional. */\nexport function emptyCloudStats(provider: string): BoxResourceStats {\n return {\n source: provider,\n live: false,\n cpuPercent: null,\n memUsedBytes: null,\n memLimitBytes: null,\n memPercent: null,\n pids: null,\n diskUsedBytes: null,\n snapshotDiskBytes: null,\n checkpointVolumeBytes: null,\n netRxBytes: null,\n netTxBytes: null,\n blockReadBytes: null,\n blockWriteBytes: null,\n limits: { memoryBytes: null, cpus: null, pidsLimit: null, disk: null },\n warnings: [],\n };\n}\n","/**\n * Cloud-provider **credentials volume** management — the per-create half of\n * the agent state split. The other half (static config: plugins, skills,\n * settings, marketplaces, codex `config.toml`, opencode `config/`) is\n * layered into the published Daytona snapshot at\n * `agentbox prepare --provider daytona` time via the documented\n * `daytona.snapshot.create({ name, image })` API + `Image.fromDockerfile(...)\n * .addLocalFile(...).runCommands(...)` (see\n * `packages/sandbox-daytona/src/prepare.ts`).\n *\n * What lives here is the runtime side: credentials (`.credentials.json` for\n * claude, `auth.json` for codex/opencode) live on a single per-org\n * `agentbox-credentials` Daytona volume, mounted three times via `subpath`\n * at `/home/vscode/.agentbox-creds/{claude,codex,opencode}/`. Symlinks\n * baked into the box image route the agent-expected paths\n * (`~/.claude/.credentials.json` etc.) through to the volume.\n *\n * `seedAgentVolumesIfFresh` runs at every `agentbox create --provider\n * daytona`: it probes the per-agent marker file in the credentials volume\n * and uploads a tiny credentials-only tarball when missing. Re-seeding is\n * **explicit only** — vanilla create never overwrites credentials it\n * already finds. `agentbox daytona resync` passes `force: true` to refresh\n * after a host re-auth.\n */\n\nimport {\n stageClaudeStaticForUpload,\n stageClaudeCredentialsForUpload,\n stageCodexStaticForUpload,\n stageCodexCredentialsForUpload,\n stageOpencodeStaticForUpload,\n stageOpencodeCredentialsForUpload,\n type StageResult,\n} from '@agentbox/sandbox-docker';\nimport type { CloudBackend, CloudHandle, CloudVolumeMount } from '@agentbox/core';\n\n/** Identifier for one of the three agents we sync into cloud sandboxes. */\nexport type CloudAgentKind = 'claude' | 'codex' | 'opencode';\n\n/**\n * Single per-org volume that holds all three agents' credentials. Mounted\n * three times via `subpath` so each agent's tokens get their own dir without\n * the volumes-API churn of registering three separate volumes.\n */\nconst CREDENTIALS_VOLUME = 'agentbox-credentials';\n\n/**\n * Per-agent metadata. `staticMountPath` is where the snapshot-baked static\n * config lives in the sandbox FS; `credentialsMountPath` is where the\n * credentials volume's per-agent subpath gets attached at runtime.\n * `credentialsSubpath` is the subdir inside the shared volume.\n */\ninterface AgentSpec {\n kind: CloudAgentKind;\n /** Where stage*Static tarballs extract (sandbox FS, snapshot-captured). */\n staticMountPath: string;\n /** Where the credentials-volume subpath gets mounted at runtime. */\n credentialsMountPath: string;\n /** Subdir of the shared credentials volume for this agent. */\n credentialsSubpath: string;\n stageStatic: (opts: { hostWorkspace?: string }) => Promise<StageResult>;\n stageCredentials: () => Promise<StageResult>;\n}\n\nconst AGENT_SPECS: AgentSpec[] = [\n {\n kind: 'claude',\n staticMountPath: '/home/vscode/.claude',\n credentialsMountPath: '/home/vscode/.agentbox-creds/claude',\n credentialsSubpath: 'claude/',\n stageStatic: (opts) => stageClaudeStaticForUpload({ hostWorkspace: opts.hostWorkspace }),\n stageCredentials: () => stageClaudeCredentialsForUpload(),\n },\n {\n kind: 'codex',\n staticMountPath: '/home/vscode/.codex',\n credentialsMountPath: '/home/vscode/.agentbox-creds/codex',\n credentialsSubpath: 'codex/',\n stageStatic: () => stageCodexStaticForUpload(),\n stageCredentials: () => stageCodexCredentialsForUpload(),\n },\n {\n kind: 'opencode',\n staticMountPath: '/home/vscode/.local/share/opencode',\n credentialsMountPath: '/home/vscode/.agentbox-creds/opencode',\n credentialsSubpath: 'opencode/',\n stageStatic: () => stageOpencodeStaticForUpload(),\n stageCredentials: () => stageOpencodeCredentialsForUpload(),\n },\n];\n\nimport {\n CLAUDE_FORWARDED_ENV_KEYS,\n CODEX_FORWARDED_ENV_KEYS,\n OPENCODE_FORWARDED_ENV_KEYS,\n} from '@agentbox/sandbox-docker';\n\n/**\n * Marker filename inside each agent's credentials subpath that records when\n * we last seeded the credentials. Single ISO-8601 timestamp on disk. Absent\n * marker = first time → upload.\n */\nconst SEED_MARKER = '.agentbox-seeded-at';\n\n/** Result of `ensureAgentVolumesForCloud` — pass `.mounts` straight into `provision({ volumes })`. */\nexport interface EnsureAgentVolumesResult {\n /** Volume mounts ready for `CloudProvisionRequest.volumes`. */\n mounts: CloudVolumeMount[];\n /**\n * Env vars to merge into the sandbox env at provision time. Includes\n * `OPENCODE_CONFIG_DIR` (so the in-box opencode reads its config from the\n * snapshot-baked `config/` subdir of its data dir) and any forwarded\n * provider API keys present in the host env.\n */\n env: Record<string, string>;\n /**\n * Agents we successfully reserved credentials mounts for. Pass back into\n * `seedAgentVolumesIfFresh` so it doesn't redo the kind list.\n */\n agents: CloudAgentKind[];\n}\n\n/**\n * Reserve the shared `agentbox-credentials` volume and return three subpath\n * mounts ready to thread into `CloudProvisionRequest.volumes`. Backends that\n * don't implement `ensureVolume` get an empty mount list (and a one-line log)\n * — the in-box agents fall back to interactive login.\n *\n * Idempotent: every call is a fast lookup for the already-created volume.\n * Safe to call on every `create`.\n */\nexport async function ensureAgentVolumesForCloud(\n backend: CloudBackend,\n opts: { onLog?: (line: string) => void } = {},\n): Promise<EnsureAgentVolumesResult> {\n const log = opts.onLog ?? (() => {});\n if (typeof backend.ensureVolume !== 'function') {\n log(\n `cloud backend '${backend.name}' has no volume primitive — agent credentials will not persist across boxes`,\n );\n return { mounts: [], env: buildForwardedEnv([]), agents: [] };\n }\n\n let volumeId: string;\n try {\n const ensured = await backend.ensureVolume(CREDENTIALS_VOLUME);\n volumeId = ensured.volumeId;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n log(`ensureVolume(${CREDENTIALS_VOLUME}) failed (skipping credentials seed): ${msg}`);\n return { mounts: [], env: buildForwardedEnv([]), agents: [] };\n }\n\n const mounts: CloudVolumeMount[] = AGENT_SPECS.map((spec) => ({\n volumeId,\n mountPath: spec.credentialsMountPath,\n subpath: spec.credentialsSubpath,\n }));\n const agents = AGENT_SPECS.map((s) => s.kind);\n return { mounts, env: buildForwardedEnv(agents), agents };\n}\n\nfunction buildForwardedEnv(agents: CloudAgentKind[]): Record<string, string> {\n const env: Record<string, string> = {};\n // OpenCode reads its config dir from $OPENCODE_CONFIG_DIR; the snapshot\n // bake puts the config files at <data dir>/config/ to match what the\n // Docker provider does (see buildOpencodeMounts).\n if (agents.includes('opencode')) {\n env['OPENCODE_CONFIG_DIR'] = '/home/vscode/.local/share/opencode/config';\n }\n // Forward provider API keys from the host process env into the sandbox.\n // For agents authenticated via env-var (ANTHROPIC_API_KEY etc.) rather\n // than a stored auth file, this is the only way the in-box agent finds\n // its credentials. Mirrors the Docker provider's per-agent forwarding.\n const forwardedKeys = new Set<string>([\n ...CLAUDE_FORWARDED_ENV_KEYS,\n ...CODEX_FORWARDED_ENV_KEYS,\n ...OPENCODE_FORWARDED_ENV_KEYS,\n ]);\n for (const k of forwardedKeys) {\n const v = process.env[k];\n if (typeof v === 'string' && v.length > 0) env[k] = v;\n }\n return env;\n}\n\nexport interface SeedAgentVolumesOptions {\n /** Which agents to consider seeding. Defaults to all three. */\n agents?: CloudAgentKind[];\n /**\n * The host-absolute workspace path being mounted at `/workspace` inside\n * the box. Currently unused by the credentials-only seed (no `_claude.json`\n * involved), but kept on the interface for symmetry with the bake step\n * and forward compat with future credential-side rewrites.\n */\n hostWorkspace?: string;\n /**\n * When true, ignore the in-volume seed marker and re-upload. Used by\n * `agentbox daytona resync` and by the host-login flow. Default: false\n * (vanilla create never overwrites a seeded volume).\n */\n force?: boolean;\n onLog?: (line: string) => void;\n}\n\n/**\n * For each enabled agent: probe the credentials-subpath\n * `<credentialsMountPath>/.agentbox-seeded-at` marker via `backend.exec`. If\n * absent (or `force: true`), stage a credentials-only tarball, upload via\n * `backend.uploadFile`, extract inside the sandbox into the credentials\n * mount.\n *\n * Idempotent and safe to call on every `create`. Warnings from staging (e.g.\n * codex Keychain landmine) are forwarded via `onLog`. The payload is a\n * single small file per agent, so the FUSE-volume pathology that plagues\n * the static seed is irrelevant here.\n */\nexport async function seedAgentVolumesIfFresh(\n backend: CloudBackend,\n handle: CloudHandle,\n opts: SeedAgentVolumesOptions = {},\n): Promise<void> {\n const wanted = new Set<CloudAgentKind>(opts.agents ?? AGENT_SPECS.map((s) => s.kind));\n const specs = AGENT_SPECS.filter((s) => wanted.has(s.kind));\n await Promise.all(specs.map((spec) => seedCredentialsOne(backend, handle, spec, opts)));\n}\n\nasync function seedCredentialsOne(\n backend: CloudBackend,\n handle: CloudHandle,\n spec: AgentSpec,\n opts: SeedAgentVolumesOptions,\n): Promise<void> {\n const log = opts.onLog ?? (() => {});\n\n if (!opts.force) {\n const probe = await backend.exec(\n handle,\n `test -f ${spec.credentialsMountPath}/${SEED_MARKER}`,\n );\n if (probe.exitCode === 0) {\n log(`${spec.kind}: credentials already seeded — mounting only`);\n return;\n }\n }\n\n log(`${spec.kind}: staging host credentials`);\n const staged = await spec.stageCredentials();\n for (const w of staged.warnings) log(w);\n try {\n if (staged.tarballPath === null) {\n log(`${spec.kind}: no credentials to seed`);\n return;\n }\n\n let tarSize = 0;\n try {\n const { statSync } = await import('node:fs');\n tarSize = statSync(staged.tarballPath).size;\n } catch {\n /* best-effort */\n }\n const sizeKB = (tarSize / 1024).toFixed(1);\n log(`${spec.kind}: uploading ${sizeKB} KB credentials tarball`);\n process.stderr.write(`[agent-creds] ${spec.kind}: uploading ${sizeKB} KB...\\n`);\n const t0 = Date.now();\n const remoteTar = `/tmp/agentbox-${spec.kind}-creds.tar.gz`;\n await backend.uploadFile(handle, staged.tarballPath, remoteTar);\n const upDt = ((Date.now() - t0) / 1000).toFixed(1);\n process.stderr.write(`[agent-creds] ${spec.kind}: upload done in ${upDt}s\\n`);\n\n // Daytona volumes are S3-backed FUSE and reject chmod/utime. The\n // credentials payload is one small file, so we extract straight into\n // the mount with `cp` (not tar — tar would chmod the parent dir during\n // delayed-set-stat and abort with EPERM). Two-step: tar into a local-fs\n // staging dir, then cp the file across.\n const stageDir = `/tmp/agentbox-creds-stage-${spec.kind}`;\n const extractCmd =\n `set -e; ` +\n `rm -rf ${stageDir}; ` +\n `mkdir -p ${stageDir}; ` +\n `tar -xzf ${remoteTar} -C ${stageDir}; ` +\n `cp -r ${stageDir}/. ${spec.credentialsMountPath}/; ` +\n `rm -rf ${stageDir}; ` +\n `date -u +%FT%TZ > ${spec.credentialsMountPath}/${SEED_MARKER}; ` +\n `rm -f ${remoteTar}`;\n const extract = await backend.exec(handle, extractCmd);\n if (extract.exitCode !== 0) {\n const msg =\n `${spec.kind}: credentials extract failed (exit ${String(extract.exitCode)}); ` +\n `agent falls back to interactive login. ` +\n `stdout: ${extract.stdout.slice(-200)} stderr: ${extract.stderr.slice(-200)}`;\n log(msg);\n process.stderr.write(`[agent-creds] ${msg}\\n`);\n return;\n }\n log(`${spec.kind}: credentials seeded ✓`);\n process.stderr.write(`[agent-creds] ${spec.kind}: credentials seeded\\n`);\n } finally {\n await staged.cleanup();\n }\n}\n\n/**\n * Spec for a single agent — for callers that need the mount path or volume\n * name outside the create flow.\n */\nexport function agentSpecsForCloud(): Array<{\n kind: CloudAgentKind;\n staticMountPath: string;\n credentialsMountPath: string;\n credentialsSubpath: string;\n}> {\n return AGENT_SPECS.map((s) => ({\n kind: s.kind,\n staticMountPath: s.staticMountPath,\n credentialsMountPath: s.credentialsMountPath,\n credentialsSubpath: s.credentialsSubpath,\n }));\n}\n","/**\n * Cloud checkpoint store — parallel to `packages/sandbox-docker/src/checkpoint.ts`\n * but backed by provider-native snapshots (Daytona's `_experimental_createSnapshot`)\n * instead of local Docker image tags.\n *\n * Each user-facing checkpoint (`setup`, `with-deps`, …) is scoped to a project\n * and stored as a thin manifest on the host:\n *\n * ~/.agentbox/cloud-checkpoints/<backend>/<projectHash-mnemonic>/<name>/manifest.json\n *\n * The manifest maps the project-scoped name to a provider-unique snapshot name\n * — Daytona snapshots are org-wide, so the snapshot name is prefixed with the\n * project hash to avoid collisions across projects (and across users in the\n * same org).\n */\n\nimport { mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, join } from 'node:path';\nimport { hashProjectPath, projectDirSegment, sanitizeMnemonic } from '@agentbox/config';\n\nexport const CLOUD_CHECKPOINTS_ROOT = join(homedir(), '.agentbox', 'cloud-checkpoints');\n\n/**\n * All cloud snapshot names share this prefix so a stray `agentbox-ckpt-*`\n * snapshot in the Daytona dashboard is clearly recognisable as AgentBox's.\n * Mirrors `CHECKPOINT_IMAGE_PREFIX` from the docker package.\n */\nexport const CLOUD_SNAPSHOT_NAME_PREFIX = 'agentbox-ckpt-';\n\nexport interface CloudCheckpointManifest {\n schema: 1;\n /** User-facing, project-scoped name (e.g. \"setup\"). */\n name: string;\n /** Cloud backend the snapshot lives in (e.g. \"daytona\"). */\n backend: string;\n /**\n * Provider-unique snapshot name — what the backend's `createSnapshot()` was\n * called with, and what gets passed to `provision({ snapshot })` on restore.\n */\n snapshotName: string;\n sourceBoxId: string;\n sourceBoxName: string;\n createdAt: string;\n}\n\nexport interface CloudCheckpointInfo {\n name: string;\n /** Host dir holding `manifest.json`. */\n dir: string;\n manifest: CloudCheckpointManifest;\n}\n\n/**\n * Deterministic provider-unique snapshot name for a project checkpoint. The\n * project-hash prefix prevents collisions across projects (and across users\n * in the same Daytona org); the mnemonic suffix keeps the Daytona dashboard\n * readable. The leading `agentbox-ckpt-` lets cleanup scripts recognise our\n * snapshots.\n */\nexport function cloudSnapshotName(projectRoot: string, name: string): string {\n const mnemonic = sanitizeMnemonic(basename(projectRoot));\n return `${CLOUD_SNAPSHOT_NAME_PREFIX}${hashProjectPath(projectRoot)}_${mnemonic}-${name}`;\n}\n\nfunction backendDir(backend: string, projectRoot: string): string {\n return join(CLOUD_CHECKPOINTS_ROOT, backend, projectDirSegment(projectRoot));\n}\n\nfunction checkpointDir(backend: string, projectRoot: string, name: string): string {\n return join(backendDir(backend, projectRoot), name);\n}\n\nasync function readManifest(dir: string): Promise<CloudCheckpointManifest | null> {\n try {\n const raw = await readFile(join(dir, 'manifest.json'), 'utf8');\n const m = JSON.parse(raw) as CloudCheckpointManifest;\n if (m.schema !== 1) return null;\n return m;\n } catch {\n return null;\n }\n}\n\nexport async function listCloudCheckpoints(\n projectRoot: string,\n backend: string,\n): Promise<CloudCheckpointInfo[]> {\n const root = backendDir(backend, projectRoot);\n let entries: string[];\n try {\n entries = (await readdir(root, { withFileTypes: true }))\n .filter((e) => e.isDirectory())\n .map((e) => e.name);\n } catch {\n return [];\n }\n const out: CloudCheckpointInfo[] = [];\n for (const name of entries) {\n const dir = join(root, name);\n const manifest = await readManifest(dir);\n if (manifest) out.push({ name, dir, manifest });\n }\n out.sort((a, b) => a.manifest.createdAt.localeCompare(b.manifest.createdAt));\n return out;\n}\n\nexport async function resolveCloudCheckpoint(\n projectRoot: string,\n backend: string,\n ref: string,\n): Promise<CloudCheckpointInfo | null> {\n const dir = checkpointDir(backend, projectRoot, ref);\n const manifest = await readManifest(dir);\n if (!manifest) return null;\n return { name: ref, dir, manifest };\n}\n\nexport interface WriteCloudManifestFields {\n snapshotName: string;\n sourceBoxId: string;\n sourceBoxName: string;\n}\n\nexport async function writeCloudCheckpointManifest(\n projectRoot: string,\n backend: string,\n name: string,\n fields: WriteCloudManifestFields,\n): Promise<CloudCheckpointInfo> {\n const dir = checkpointDir(backend, projectRoot, name);\n await mkdir(dir, { recursive: true });\n const manifest: CloudCheckpointManifest = {\n schema: 1,\n name,\n backend,\n snapshotName: fields.snapshotName,\n sourceBoxId: fields.sourceBoxId,\n sourceBoxName: fields.sourceBoxName,\n createdAt: new Date().toISOString(),\n };\n await writeFile(join(dir, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\\n', 'utf8');\n return { name, dir, manifest };\n}\n\nexport async function removeCloudCheckpointDir(\n projectRoot: string,\n backend: string,\n name: string,\n): Promise<boolean> {\n const dir = checkpointDir(backend, projectRoot, name);\n const existed = (await readManifest(dir)) !== null;\n if (!existed) return false;\n await rm(dir, { recursive: true, force: true });\n return true;\n}\n","/**\n * Copy host env/config files (`.env`, `secrets.toml`, `agentbox.yaml`, …) into\n * a cloud sandbox's `/workspace` at create time. The cloud counterpart of\n * `copyHostEnvFilesToBox` in sandbox-docker — same `find` + `tar --null -T -`\n * packer on the host, swaps `docker exec tar -xf` for `backend.uploadFile` +\n * `backend.exec(tar -xf)`.\n *\n * The wizard collects `envFilesToImport` in apps/cli/src/wizard.ts (the same\n * field the Docker provider already honours). This module makes the cloud\n * provider honour it too.\n *\n * Best-effort: a scan/pack failure or empty match set logs and returns the\n * count rather than throwing — a missing optional secret mustn't abort an\n * otherwise healthy box.\n */\n\nimport { mkdtemp, rm, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { execa } from 'execa';\nimport { buildHostEnvFindArgs } from '@agentbox/sandbox-docker';\nimport type { CloudBackend, CloudHandle } from '@agentbox/core';\n\nexport interface UploadEnvFilesArgs {\n backend: CloudBackend;\n handle: CloudHandle;\n /** Absolute host workspace path the find runs against. */\n workspacePath: string;\n /** Glob patterns selected by the wizard (e.g. `.env`, `secrets.toml`). */\n files: string[];\n /** In-sandbox `/workspace` mount. Always `/workspace` today; configurable for tests. */\n workspaceDir?: string;\n onLog?: (line: string) => void;\n}\n\nexport interface UploadEnvFilesResult {\n /** Number of files written into the sandbox. 0 when nothing matched. */\n copied: number;\n}\n\nconst WORKSPACE_DIR_DEFAULT = '/workspace';\nconst REMOTE_TAR_PATH = '/tmp/agentbox-envfiles.tar';\n\nexport async function uploadEnvFiles(args: UploadEnvFilesArgs): Promise<UploadEnvFilesResult> {\n const log = args.onLog ?? (() => {});\n if (args.files.length === 0) return { copied: 0 };\n const workspaceDir = args.workspaceDir ?? WORKSPACE_DIR_DEFAULT;\n\n // 1. Enumerate matched files on the host with `find -print0` (NUL-delimited)\n // using exactly the same arg builder Docker uses, so the patterns + the\n // prune set behave identically across providers.\n const found = await execa('find', buildHostEnvFindArgs(args.files).slice(1), {\n cwd: args.workspacePath,\n reject: false,\n });\n if (found.exitCode !== 0) {\n log(`env-file scan failed: ${String(found.stderr).slice(0, 300)}`);\n return { copied: 0 };\n }\n const list = String(found.stdout)\n .split('\\0')\n .filter((p) => p.length > 0);\n if (list.length === 0) return { copied: 0 };\n\n // 2. Pack the matched files into a tar on the host. `--null -T -` reads the\n // file list NUL-delimited from stdin so paths with whitespace survive.\n // We write to a tempfile (rather than streaming) because backend.uploadFile\n // takes a local file path, not a stream.\n const stage = await mkdtemp(join(tmpdir(), 'agentbox-envfiles-'));\n const localTar = join(stage, 'envfiles.tar');\n try {\n const packed = await execa(\n 'tar',\n ['-C', args.workspacePath, '--null', '-T', '-', '-cf', localTar],\n { input: list.join('\\0'), reject: false },\n );\n if (packed.exitCode !== 0) {\n log(`env-file tar pack failed: ${String(packed.stderr).slice(0, 300)}`);\n return { copied: 0 };\n }\n // Ensure the file exists even when tar packed nothing useful (best-effort).\n await writeFile(join(stage, '.marker'), '').catch(() => {});\n\n // 3. Upload the tar into the sandbox + extract under /workspace. Same\n // `--no-same-*` + `-m` flags we use for the credential extracts: future-\n // proofs against ever putting /workspace on Daytona's FUSE volume tier\n // (chmod/utime would otherwise EPERM) and is harmless on regular disk.\n // `rm -f` cleans the staging tar so the sandbox /tmp doesn't accumulate\n // leftovers across multiple creates against the same handle (cheap).\n await args.backend.uploadFile(args.handle, localTar, REMOTE_TAR_PATH);\n const extract = await args.backend.exec(\n args.handle,\n `tar -xf ${REMOTE_TAR_PATH} -C ${workspaceDir} --no-same-permissions --no-same-owner -m && rm -f ${REMOTE_TAR_PATH}`,\n );\n if (extract.exitCode !== 0) {\n log(\n `env-file extract failed (exit ${String(extract.exitCode)}); ` +\n `stdout: ${extract.stdout.slice(-200)} stderr: ${extract.stderr.slice(-200)}`,\n );\n return { copied: 0 };\n }\n } finally {\n await rm(stage, { recursive: true, force: true });\n }\n return { copied: list.length };\n}\n","/**\n * Extract the internal `expose.port` values from a workspace's\n * `agentbox.yaml`. The cloud provider mints a preview URL per port so\n * users can hit services directly (the WebProxy on port 8080 also\n * stays — this is a side channel for raw access).\n *\n * Intentionally minimal: we don't validate the rest of the schema. The\n * supervisor enforces correctness at run-time; here we just need the\n * numeric `services.*.expose.port` values. Errors (missing file, bad\n * YAML, unexpected shape) silently degrade to \"no extra ports\", which\n * matches the existing \"URL is best-effort at create\" semantics.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\n\nexport async function readExposedServicePorts(workspacePath: string): Promise<number[]> {\n let text: string;\n try {\n text = await readFile(join(workspacePath, 'agentbox.yaml'), 'utf8');\n } catch {\n return [];\n }\n let doc: unknown;\n try {\n doc = parseYaml(text);\n } catch {\n return [];\n }\n if (!isPlainObject(doc)) return [];\n const services = doc['services'];\n if (!isPlainObject(services)) return [];\n const out = new Set<number>();\n for (const value of Object.values(services)) {\n if (!isPlainObject(value)) continue;\n const expose = value['expose'];\n if (!isPlainObject(expose)) continue;\n const port = expose['port'];\n if (typeof port === 'number' && Number.isInteger(port) && port > 0 && port < 65_536) {\n out.add(port);\n }\n }\n return [...out].sort((a, b) => a - b);\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n","/**\n * `cp`-style file transfer for cloud boxes — symmetric host↔box uploads and\n * downloads that mirror `docker cp` semantics. The Docker provider tar-pipes\n * over `docker exec`; clouds with no exec stream can't, so we stage to a\n * single tar file in `/tmp`, transfer it via `backend.uploadFile` /\n * `downloadFile`, and unpack on the other side. Handles files and\n * directories uniformly.\n *\n * Path semantics match `docker cp`'s pragmatic subset: trailing `/` on the\n * destination means \"treat as a directory, land the source under it\"; no\n * trailing `/` means \"destination is the full target path\" (rename during\n * extraction). The in-box `test -d` probe the Docker provider uses to detect\n * existing directories isn't replicated here — it'd cost an extra exec\n * round-trip per call and the trailing-`/` convention is the explicit form\n * anyway.\n */\n\nimport { execa } from 'execa';\nimport { existsSync, mkdirSync, renameSync, statSync } from 'node:fs';\nimport { mkdtemp, rm } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport {\n basename as hostBasename,\n dirname as hostDirname,\n join as hostJoin,\n resolve as hostResolve,\n} from 'node:path';\nimport { posix } from 'node:path';\nimport type { CloudBackend, CloudHandle } from '@agentbox/core';\nimport { bashScript, quoteShellArg } from './shell.js';\n\nconst REMOTE_UP_TAR = '/tmp/agentbox-cp-up.tar.gz';\nconst REMOTE_DOWN_TAR = '/tmp/agentbox-cp-down.tar.gz';\n\nexport interface CloudCpResult {\n /** Final landed path on the receiving side. */\n finalPath: string;\n}\n\nexport async function uploadToCloudBox(\n backend: CloudBackend,\n handle: CloudHandle,\n hostSrc: string,\n boxDst: string,\n): Promise<CloudCpResult> {\n const srcAbs = hostResolve(hostSrc);\n if (!existsSync(srcAbs)) throw new Error(`source not found: ${hostSrc}`);\n const srcBasename = hostBasename(srcAbs);\n const srcParent = hostDirname(srcAbs);\n\n // Resolve `boxParent` + final filename per docker cp rules — see file\n // header. We can't probe `test -d` cheaply, so the user opts into the\n // dir-vs-file ambiguity with the trailing slash.\n let boxParent: string;\n let finalName: string;\n if (boxDst.endsWith('/')) {\n boxParent = boxDst.replace(/\\/+$/, '') || '/';\n finalName = srcBasename;\n } else {\n boxParent = posix.dirname(boxDst);\n finalName = posix.basename(boxDst);\n }\n const finalPath = boxParent === '/' ? `/${finalName}` : `${boxParent}/${finalName}`;\n\n const stage = await mkdtemp(hostJoin(tmpdir(), 'agentbox-cp-up-'));\n const localTar = hostJoin(stage, 'payload.tar.gz');\n try {\n // COPYFILE_DISABLE silences macOS BSD tar's `._*` resource-fork stubs\n // that would otherwise litter the box's filesystem on every upload.\n await execa('tar', ['-C', srcParent, '-czf', localTar, srcBasename], {\n env: { ...process.env, COPYFILE_DISABLE: '1' },\n });\n await backend.uploadFile(handle, localTar, REMOTE_UP_TAR);\n\n // Sudo for the dir ops because `boxParent` may be outside the user's\n // writable area (e.g. /etc/foo); devcontainers/base grants vscode\n // passwordless sudo and the SUDO=:'' fallback makes it a no-op when\n // sudo isn't available (test sandboxes).\n const initialPath = boxParent === '/' ? `/${srcBasename}` : `${boxParent}/${srcBasename}`;\n // Daytona's S3-backed FUSE volumes return ENOSYS for rename(2), so `mv`\n // fails when the destination crosses the mount boundary. `cp -f` + `rm`\n // works on every backend (and on the regular sandbox disk is no slower\n // than mv for a single file).\n const renameStep =\n finalName !== srcBasename\n ? `$SUDO cp -f ${quoteShellArg(initialPath)} ${quoteShellArg(finalPath)} && $SUDO rm -f ${quoteShellArg(initialPath)}`\n : ': # no rename';\n const script = [\n `set -euo pipefail`,\n `if command -v sudo >/dev/null 2>&1; then SUDO='sudo -n'; else SUDO=''; fi`,\n `$SUDO mkdir -p ${quoteShellArg(boxParent)}`,\n // --no-same-permissions / --no-same-owner / -m: Daytona's S3-backed\n // FUSE volumes reject chmod/utime/chown; skipping them lets the extract\n // complete on a mounted-volume destination. Harmless no-op on the\n // sandbox's regular disk. Same flags as the credential-seed extract.\n `$SUDO tar -xzf ${quoteShellArg(REMOTE_UP_TAR)} -C ${quoteShellArg(boxParent)} --no-same-permissions --no-same-owner -m`,\n renameStep,\n // chown only the landed path — anything we mkdir'd through stays at\n // its existing ownership. Tolerate failure (chown bad on read-only mounts).\n `$SUDO chown -R \"$(id -un):$(id -gn)\" ${quoteShellArg(finalPath)} || true`,\n `rm -f ${quoteShellArg(REMOTE_UP_TAR)}`,\n ].join('\\n');\n const r = await backend.exec(handle, bashScript(script));\n if (r.exitCode !== 0) {\n throw new Error(`cloud upload extract failed: ${r.stderr || r.stdout}`);\n }\n } finally {\n await rm(stage, { recursive: true, force: true });\n }\n return { finalPath };\n}\n\n/**\n * Pull the *contents* of an in-sandbox directory into a host directory —\n * `/workspace/*` → `<hostDst>/*`, not `<hostDst>/<srcBasename>/*`. Used by\n * `agentbox download` (the bulk workspace pull); `downloadFromCloudBox`\n * preserves the source basename for `docker cp` parity.\n */\nexport async function pullCloudDirContents(\n backend: CloudBackend,\n handle: CloudHandle,\n boxSrcDir: string,\n hostDstDir: string,\n): Promise<CloudCpResult> {\n const dstAbs = hostResolve(hostDstDir);\n mkdirSync(dstAbs, { recursive: true });\n\n const stage = await mkdtemp(hostJoin(tmpdir(), 'agentbox-pull-'));\n const localTar = hostJoin(stage, 'payload.tar.gz');\n try {\n // `tar -C <dir> -czf <tarball> .` packs the contents (no leading\n // basename component) so extraction lands them in dstAbs directly.\n const packScript = [\n `set -euo pipefail`,\n `cd ${quoteShellArg(boxSrcDir)}`,\n `tar -czf ${quoteShellArg(REMOTE_DOWN_TAR)} .`,\n ].join('\\n');\n const r = await backend.exec(handle, bashScript(packScript));\n if (r.exitCode !== 0) {\n throw new Error(`cloud workspace pack failed: ${r.stderr || r.stdout}`);\n }\n await backend.downloadFile(handle, REMOTE_DOWN_TAR, localTar);\n await execa('tar', ['-xzf', localTar, '-C', dstAbs]);\n await backend\n .exec(handle, `rm -f ${quoteShellArg(REMOTE_DOWN_TAR)}`)\n .catch(() => {\n /* best-effort */\n });\n } finally {\n await rm(stage, { recursive: true, force: true });\n }\n return { finalPath: dstAbs };\n}\n\nexport async function downloadFromCloudBox(\n backend: CloudBackend,\n handle: CloudHandle,\n boxSrc: string,\n hostDst: string,\n): Promise<CloudCpResult> {\n const srcBasename = posix.basename(boxSrc);\n const srcParent = posix.dirname(boxSrc);\n\n const dstAbs = hostResolve(hostDst);\n let hostParent: string;\n let finalName: string;\n const dstExists = existsSync(dstAbs);\n if (hostDst.endsWith('/') || (dstExists && statSync(dstAbs).isDirectory())) {\n hostParent = dstAbs;\n finalName = srcBasename;\n } else {\n hostParent = hostDirname(dstAbs);\n finalName = hostBasename(dstAbs);\n }\n mkdirSync(hostParent, { recursive: true });\n const finalPath = hostJoin(hostParent, finalName);\n\n const stage = await mkdtemp(hostJoin(tmpdir(), 'agentbox-cp-down-'));\n const localTar = hostJoin(stage, 'payload.tar.gz');\n try {\n const packScript = [\n `set -euo pipefail`,\n `cd ${quoteShellArg(srcParent)}`,\n `tar -czf ${quoteShellArg(REMOTE_DOWN_TAR)} ${quoteShellArg(srcBasename)}`,\n ].join('\\n');\n const r = await backend.exec(handle, bashScript(packScript));\n if (r.exitCode !== 0) {\n throw new Error(`cloud download pack failed: ${r.stderr || r.stdout}`);\n }\n await backend.downloadFile(handle, REMOTE_DOWN_TAR, localTar);\n await execa('tar', ['-xzf', localTar, '-C', hostParent]);\n if (finalName !== srcBasename) {\n renameSync(hostJoin(hostParent, srcBasename), finalPath);\n }\n // Best-effort cleanup; tolerate failure (sandbox may have ephemeral /tmp).\n await backend\n .exec(handle, `rm -f ${quoteShellArg(REMOTE_DOWN_TAR)}`)\n .catch(() => {\n /* best-effort */\n });\n } finally {\n await rm(stage, { recursive: true, force: true });\n }\n return { finalPath };\n}\n","/**\n * Build a single shell-safe command string from an argv array. Used everywhere\n * we hand a cloud backend's `exec(handle, cmd, ...)` an argv that originated\n * from CLI input — single-quoting every arg neutralises shell metacharacters.\n */\nexport function quoteShellArgv(argv: readonly string[]): string {\n return argv.map(quoteShellArg).join(' ');\n}\n\nexport function quoteShellArg(arg: string): string {\n if (arg.length === 0) return \"''\";\n // Fast-path for safe identifiers, paths, simple options: avoid quotes for\n // readability in logs. Anything else falls through to single-quoting.\n if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(arg)) return arg;\n return \"'\" + arg.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n\n/**\n * Wrap a multi-line shell script body so it runs under `bash -c` regardless\n * of what `/bin/sh` points at in the sandbox. Necessary on Daytona's images\n * where `executeCommand` shells out via `dash`, which rejects bash idioms\n * like `set -o pipefail`. Use this for any script that isn't trivially\n * POSIX-only.\n */\nexport function bashScript(body: string): string {\n return `bash -c ${quoteShellArg(body)}`;\n}\n","import type { CloudBackend, CloudHandle } from '@agentbox/core';\nimport { bashScript, quoteShellArgv } from './shell.js';\n\n/**\n * Launch the in-box `agentbox-ctl daemon` inside a cloud sandbox. The image\n * (built from `Dockerfile.box`) already bakes `/usr/local/bin/agentbox-ctl`,\n * so this only `exec`s it detached with the per-box env wired in.\n *\n * The relay env is intentionally `undefined` in v0: the in-sandbox relay box-\n * mode + host poller (Phase 4) is not wired yet. With those env vars absent\n * the supervisor's `RelayClient` short-circuits to a no-op, the supervisor\n * still schedules `agentbox.yaml` tasks/services, and `agentbox-ctl git push`\n * surfaces a clear \"no relay configured\" error.\n */\nexport interface LaunchCloudCtlArgs {\n backend: CloudBackend;\n handle: CloudHandle;\n boxId: string;\n boxName: string;\n /** When set, exported as AGENTBOX_RELAY_URL inside the box. v0 leaves unset. */\n relayUrl?: string;\n /** When set, exported as AGENTBOX_RELAY_TOKEN inside the box. v0 leaves unset. */\n relayToken?: string;\n /** When set, exported as AGENTBOX_BRIDGE_TOKEN inside the box. v0 leaves unset. */\n bridgeToken?: string;\n}\n\nexport async function launchCloudCtlDaemon(args: LaunchCloudCtlArgs): Promise<void> {\n const env: string[] = [\n `AGENTBOX_BOX_ID=${quoteShellArgv([args.boxId])}`,\n `AGENTBOX_BOX_NAME=${quoteShellArgv([args.boxName])}`,\n `AGENTBOX_BOX_KIND=cloud`,\n ];\n if (args.relayUrl) env.push(`AGENTBOX_RELAY_URL=${quoteShellArgv([args.relayUrl])}`);\n if (args.relayToken) env.push(`AGENTBOX_RELAY_TOKEN=${quoteShellArgv([args.relayToken])}`);\n if (args.bridgeToken) env.push(`AGENTBOX_BRIDGE_TOKEN=${quoteShellArgv([args.bridgeToken])}`);\n\n // nohup + & detaches the daemon from the exec channel; logs go to the file\n // the daemon already uses for Docker boxes so debugging is uniform.\n // /run and /var/log are root-owned — the non-root sandbox user needs sudo\n // to create the per-box dirs (devcontainers/base grants vscode passwordless\n // sudo). The daemon itself can run as the current user.\n const script = [\n `set -e`,\n `if command -v sudo >/dev/null 2>&1; then SUDO='sudo -n'; else SUDO=''; fi`,\n `$SUDO mkdir -p /run/agentbox /var/log/agentbox`,\n `$SUDO chown \"$(id -un):$(id -gn)\" /run/agentbox /var/log/agentbox`,\n `export ${env.join(' ')}`,\n `nohup /usr/local/bin/agentbox-ctl daemon >> /var/log/agentbox/ctl-daemon.log 2>&1 &`,\n `disown`,\n `echo started`,\n ].join('\\n');\n\n const r = await args.backend.exec(args.handle, bashScript(script));\n if (r.exitCode !== 0) {\n throw new Error(`agentbox-ctl daemon launch failed: ${r.stderr || r.stdout}`);\n }\n}\n","/**\n * Launch the in-sandbox `dockerd` daemon for a cloud box. Mirrors what\n * `launchDockerdDaemon` does for Docker (calls\n * `/usr/local/bin/agentbox-dockerd-start` detached, then polls for the\n * `/var/run/docker.sock` to accept connections), adapted for cloud via\n * `backend.exec`.\n *\n * Daytona sandboxes ship with `CAP_SYS_ADMIN` available (validated by the\n * earlier DinD PoC), so the in-sandbox `dockerd` works once the bundled\n * helper script runs. This is opt-in for cloud — most users don't need\n * an in-sandbox docker.\n */\n\nimport type { CloudBackend, CloudHandle } from '@agentbox/core';\nimport { bashScript } from './shell.js';\n\nexport interface CloudDockerdLaunchResult {\n up: boolean;\n reason?: string;\n}\n\nexport async function launchCloudDockerdDaemon(args: {\n backend: CloudBackend;\n handle: CloudHandle;\n timeoutMs?: number;\n}): Promise<CloudDockerdLaunchResult> {\n const timeoutMs = args.timeoutMs ?? 60_000;\n // Spawn detached via `nohup ... &` so the exec's stdout/stderr aren't\n // tied to dockerd's lifetime. Redirect both to the standard agentbox\n // log path so `agentbox logs --daemon` style introspection can pick\n // it up.\n const startScript = [\n `set -euo pipefail`,\n `mkdir -p /var/log/agentbox`,\n `nohup sudo -n /usr/local/bin/agentbox-dockerd-start >> /var/log/agentbox/dockerd.log 2>&1 &`,\n `echo \"spawned dockerd\"`,\n ].join('\\n');\n const launch = await args.backend.exec(args.handle, bashScript(startScript));\n if (launch.exitCode !== 0) {\n return {\n up: false,\n reason: `dockerd launch failed: ${launch.stderr || launch.stdout}`,\n };\n }\n\n // Poll for the socket. The launch script returns immediately (nohup &);\n // dockerd needs ~5-15s on Daytona to initialize iptables + the storage\n // graphdriver.\n const probeCmd =\n '[ -S /var/run/docker.sock ] && docker -H unix:///var/run/docker.sock info >/dev/null 2>&1';\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n const probe = await args.backend.exec(args.handle, probeCmd);\n if (probe.exitCode === 0) return { up: true };\n await new Promise((r) => setTimeout(r, 500));\n }\n return {\n up: false,\n reason: `dockerd did not become ready within ${String(timeoutMs)}ms`,\n };\n}\n","import type { CloudBackend, CloudHandle } from '@agentbox/core';\nimport { bashScript, quoteShellArg } from './shell.js';\n\n/**\n * Launch the in-box VNC stack (Xvnc + websockify + noVNC) inside a cloud\n * sandbox. The image (built from `Dockerfile.box`) bakes\n * `/usr/local/bin/agentbox-vnc-start`, which is generic bash that reads\n * `AGENTBOX_VNC_PASSWORD` from env and is idempotent — re-running while the\n * daemons are alive is a no-op, so `start()` can blindly call us again.\n *\n * The password is injected inline at every exec call rather than baked into\n * the sandbox's provision-time `envVars`, so a Daytona stop/start doesn't\n * depend on Daytona preserving env across lifecycle transitions. Same shape\n * as `launchCloudCtlDaemon`.\n */\nexport interface LaunchCloudVncArgs {\n backend: CloudBackend;\n handle: CloudHandle;\n vncPassword: string;\n}\n\nexport async function launchCloudVncDaemon(args: LaunchCloudVncArgs): Promise<void> {\n // The script binds Xvnc on :5901 (loopback) and websockify on 0.0.0.0:6080.\n // We poll 6080 from inside the sandbox via bash's /dev/tcp pseudo-device —\n // the host can't reach the sandbox port directly (signed preview URLs are\n // public but high-latency and rate-limited, not the right path for a\n // readiness probe). Bound the probe so a wedged daemon surfaces an error.\n //\n // `cd /home/vscode` is load-bearing: Daytona's `executeCommand` runs us\n // from a transient CWD that doesn't necessarily exist by the time the\n // backgrounded websockify (Python) reads `os.getcwd()` for its default\n // cert path resolution. A stable CWD inside the sandbox user's home\n // avoids a `FileNotFoundError` at websockify startup.\n const script = [\n `set -e`,\n `cd /home/vscode`,\n `export AGENTBOX_VNC_PASSWORD=${quoteShellArg(args.vncPassword)}`,\n `mkdir -p /var/log/agentbox 2>/dev/null || true`,\n `nohup /usr/local/bin/agentbox-vnc-start >> /var/log/agentbox/vnc-start.log 2>&1 &`,\n `disown`,\n // Match the host-side probe in packages/sandbox-docker/src/vnc.ts: poll for\n // ~5s, then give up. The script itself waits for Xvnc internally; this\n // outer probe confirms websockify's public port came up too.\n `for _ in $(seq 1 50); do`,\n ` if (echo > /dev/tcp/127.0.0.1/6080) 2>/dev/null; then echo ready; exit 0; fi`,\n ` sleep 0.1`,\n `done`,\n `echo \"websockify did not bind 6080 within 5s\" >&2`,\n `exit 1`,\n ].join('\\n');\n\n const r = await args.backend.exec(args.handle, bashScript(script));\n if (r.exitCode !== 0) {\n throw new Error(`agentbox-vnc-start failed: ${r.stderr || r.stdout}`);\n }\n}\n","import { execa } from 'execa';\nimport { mkdtemp, rm } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport type { CloudBackend, CloudHandle } from '@agentbox/core';\nimport { detectGitRepos } from '@agentbox/sandbox-core';\nimport { bashScript, quoteShellArgv } from './shell.js';\n\n/**\n * Seed `/workspace` inside a cloud sandbox from the host workspace. Mirrors\n * what `seedWorkspace` does for the Docker provider, adapted for the cloud\n * channel (`backend.uploadFile` + `backend.exec`):\n *\n * - Git workspace: `git bundle create --all` on the host, upload the bundle,\n * `git clone` it inside the sandbox, repoint `origin`, check out the\n * per-box branch `agentbox/<box-name>`. Repeats for every nested repo\n * (1st-level subdir with its own `.git/`) so monorepos seed correctly.\n * - Non-git workspace: tar the host workspace, upload, extract.\n *\n * Host-uncommitted-carry-over (stash + untracked) is the remaining gap\n * tracked in Phase 6.\n */\nexport interface SeedCloudWorkspaceArgs {\n backend: CloudBackend;\n handle: CloudHandle;\n /** Absolute host path the user passed via `-w`. */\n workspacePath: string;\n /** Branch name to check out inside the sandbox (`agentbox/<box-name>`). */\n branch: string;\n /** In-sandbox destination; defaults to `/workspace`. */\n workspaceDir?: string;\n onLog?: (line: string) => void;\n}\n\nexport interface SeedCloudWorkspaceResult {\n /** True when a git repo was found at the workspace root and a bundle was used. */\n fromGit: boolean;\n /** Resolved branch (matches `branch` arg). */\n branch: string;\n}\n\nconst WORKSPACE_DIR_DEFAULT = '/workspace';\n\nexport async function seedCloudWorkspace(\n args: SeedCloudWorkspaceArgs,\n): Promise<SeedCloudWorkspaceResult> {\n const workspaceDir = args.workspaceDir ?? WORKSPACE_DIR_DEFAULT;\n const log = args.onLog ?? (() => {});\n const repos = await detectGitRepos(args.workspacePath);\n const root = repos.find((r) => r.kind === 'root');\n const nested = repos.filter((r) => r.kind === 'nested');\n\n if (root) {\n log(\n nested.length > 0\n ? `seeding /workspace from git bundle (+${String(nested.length)} nested repo${nested.length === 1 ? '' : 's'})`\n : 'seeding /workspace from git bundle',\n );\n await seedFromGitBundle({\n backend: args.backend,\n handle: args.handle,\n hostRepo: root.hostMainRepo,\n branch: args.branch,\n workspaceDir,\n });\n // Each nested repo gets its own bundle + clone at /workspace/<rel>. We\n // do these after the root clone because the root clone wipes\n // /workspace; a nested dir created during the root checkout (if\n // tracked) would be replaced when we clone over it.\n for (const r of nested) {\n const sub = `${workspaceDir}/${r.relPathFromWorkspace}`;\n log(`seeding nested repo ${r.relPathFromWorkspace} from git bundle`);\n await seedFromGitBundle({\n backend: args.backend,\n handle: args.handle,\n hostRepo: r.hostMainRepo,\n branch: args.branch,\n workspaceDir: sub,\n });\n }\n return { fromGit: true, branch: args.branch };\n }\n\n log('seeding /workspace from workspace tarball (no git detected)');\n await seedFromTar({\n backend: args.backend,\n handle: args.handle,\n hostDir: args.workspacePath,\n workspaceDir,\n });\n return { fromGit: false, branch: args.branch };\n}\n\ninterface SeedFromGitBundleArgs {\n backend: CloudBackend;\n handle: CloudHandle;\n hostRepo: string;\n branch: string;\n workspaceDir: string;\n}\n\n/**\n * Temporary host ref used to carry the `git stash create` commit into the\n * bundle so the in-sandbox repo can apply it. Lives only for the duration\n * of one `git bundle create` invocation — set, bundle, delete. Lands inside\n * the cloned repo as `refs/remotes/origin/<this name>`, which we delete\n * after applying the stash so it doesn't pollute branch lists.\n */\nconst STASH_CARRYOVER_REF = 'refs/agentbox-carryover/stash';\nconst REMOTE_UNTRACKED_TAR = '/tmp/agentbox-carryover-untracked.tar.gz';\n\nasync function seedFromGitBundle(args: SeedFromGitBundleArgs): Promise<void> {\n const stage = await mkdtemp(join(tmpdir(), 'agentbox-bundle-'));\n const bundlePath = join(stage, 'workspace.bundle');\n const untrackedTarPath = join(stage, 'untracked.tar.gz');\n // Per-repo carry-over (mirrors `collectRepoCarryOver` from sandbox-docker):\n // - `git stash create` captures every staged + tracked-modified change\n // (including deletes/renames) as a one-off commit.\n // - untracked files get tarred separately because `stash create` (no -u\n // option) doesn't capture them.\n // The stash commit travels in the bundle via a temp ref the host owns\n // for the duration of `git bundle create`. The untracked tar uploads on\n // the side and the in-sandbox script untars it after the clone.\n const stashSha = await safeStashCreate(args.hostRepo);\n const untrackedSize = await maybeBuildUntrackedTar(args.hostRepo, untrackedTarPath);\n let stashRefCreated = false;\n try {\n if (stashSha) {\n const ref = await execa(\n 'git',\n ['-C', args.hostRepo, 'update-ref', STASH_CARRYOVER_REF, stashSha],\n { reject: false },\n );\n stashRefCreated = ref.exitCode === 0;\n }\n // Default: `--all` captures every ref + full history so the sandbox gets\n // a real clone with the user's local commits and tags. Monorepos with\n // deep history make that a slow + big upload — opt out via\n // `AGENTBOX_BUNDLE_DEPTH=N` to ship only the last N commits of HEAD\n // (shallow clone semantics; `git push` from inside the box still works\n // because the remote knows the merge base). 0 / empty / non-numeric →\n // full history. The stash ref (if any) is included explicitly so it\n // rides along with either bundle mode.\n const depthRaw = process.env['AGENTBOX_BUNDLE_DEPTH'];\n const depth = depthRaw ? Number.parseInt(depthRaw, 10) : NaN;\n const bundleArgs: string[] = ['-C', args.hostRepo, 'bundle', 'create', bundlePath];\n if (Number.isFinite(depth) && depth > 0) {\n bundleArgs.push(`--depth=${String(depth)}`, 'HEAD');\n } else {\n bundleArgs.push('--all');\n }\n if (stashRefCreated) bundleArgs.push(STASH_CARRYOVER_REF);\n await execa('git', bundleArgs);\n if (stashRefCreated) {\n await execa('git', ['-C', args.hostRepo, 'update-ref', '-d', STASH_CARRYOVER_REF], {\n reject: false,\n });\n stashRefCreated = false;\n }\n const remoteUrl = await readOriginUrl(args.hostRepo);\n const remoteBundle = '/tmp/agentbox-workspace.bundle';\n await args.backend.uploadFile(args.handle, bundlePath, remoteBundle);\n if (untrackedSize > 0) {\n await args.backend.uploadFile(args.handle, untrackedTarPath, REMOTE_UNTRACKED_TAR);\n }\n const setOrigin = remoteUrl\n ? `git -C ${quoteShellArgv([args.workspaceDir])} remote set-url origin ${quoteShellArgv([remoteUrl])}`\n : ': # no host origin to copy';\n // Clone from the bundle (the bundle stands in for a remote), then repoint\n // `origin` to the real upstream so future fetch/push target the actual\n // remote — `git push` itself will travel back through the host relay in a\n // later phase. Finally check out the per-box branch from current HEAD.\n // /workspace lives at the root in the snapshot — root-owned by default\n // (Dockerfile.box never chowns it). The sandbox runs non-root, so the\n // dir ops need sudo. The devcontainers/base image grants passwordless\n // sudo to `vscode`; SUDO is a no-op when sudo isn't needed/available.\n const SUDO = `if command -v sudo >/dev/null 2>&1; then SUDO='sudo -n'; else SUDO=''; fi`;\n // The stash apply step is best-effort — applying onto a possibly\n // shallow clone can hit \"needs merge\" conflicts in pathological cases\n // (e.g. host had local changes against a commit that's now outside\n // the depth window). Soft-failure is better than blocking provision;\n // any unapplied changes can be re-derived from the host as a fallback.\n const carryOverSteps: string[] = stashSha\n ? [\n `if git -C ${quoteShellArgv([args.workspaceDir])} rev-parse --verify ${quoteShellArgv([`refs/remotes/origin/agentbox-carryover/stash`])} >/dev/null 2>&1; then ` +\n `git -C ${quoteShellArgv([args.workspaceDir])} stash apply ${quoteShellArgv([`refs/remotes/origin/agentbox-carryover/stash`])} || ` +\n `echo \"agentbox: stash apply soft-failed; carry-over may be incomplete\" >&2 ; ` +\n `git -C ${quoteShellArgv([args.workspaceDir])} update-ref -d ${quoteShellArgv([`refs/remotes/origin/agentbox-carryover/stash`])} || true ; ` +\n `fi`,\n ]\n : [];\n if (untrackedSize > 0) {\n carryOverSteps.push(\n `if [ -f ${quoteShellArgv([REMOTE_UNTRACKED_TAR])} ]; then ` +\n `tar -C ${quoteShellArgv([args.workspaceDir])} -xzf ${quoteShellArgv([REMOTE_UNTRACKED_TAR])} && ` +\n `rm -f ${quoteShellArgv([REMOTE_UNTRACKED_TAR])} ; ` +\n `fi`,\n );\n }\n const script = [\n `set -euo pipefail`,\n // Move out of any cwd we might inherit from Daytona's executeCommand\n // before we delete /workspace. The agentbox image bakes WORKDIR\n // /workspace; if the shell's cwd is /workspace when we `rm -rf` it,\n // the next process inherits a stale cwd FD and git-clone's child\n // (index-pack) fails with \"Unable to read current working directory\".\n `cd /tmp`,\n SUDO,\n // rm -rf only the directory we're about to clone into — for nested\n // repos this is just `/workspace/<rel>`, so the root clone (already\n // at `/workspace`) is preserved.\n `$SUDO rm -rf ${quoteShellArgv([args.workspaceDir])}`,\n `$SUDO mkdir -p ${quoteShellArgv([args.workspaceDir])}`,\n `$SUDO chown \"$(id -un):$(id -gn)\" ${quoteShellArgv([args.workspaceDir])}`,\n `git clone ${quoteShellArgv([remoteBundle, args.workspaceDir])}`,\n setOrigin,\n `git -C ${quoteShellArgv([args.workspaceDir])} fetch ${quoteShellArgv([remoteBundle])} --tags '+refs/heads/*:refs/remotes/bundle/*' || true`,\n `git -C ${quoteShellArgv([args.workspaceDir])} checkout -B ${quoteShellArgv([args.branch])}`,\n ...carryOverSteps,\n `rm -f ${quoteShellArgv([remoteBundle])}`,\n ].join('\\n');\n // Daytona's executeCommand shells out via dash (`/bin/sh`), which rejects\n // bash idioms like `set -o pipefail`. Wrap in `bash -c` so the script\n // runs in bash regardless of what `/bin/sh` points at.\n const r = await args.backend.exec(args.handle, bashScript(script));\n if (r.exitCode !== 0) {\n throw new Error(`workspace seed (bundle) failed: ${r.stderr || r.stdout}`);\n }\n } finally {\n // Defensive cleanup — in the happy path the stashRefCreated flag was\n // flipped off after we deleted the ref. If we threw between updates,\n // the ref may still be on the host; delete it so re-runs don't accrue\n // refs/agentbox-carryover/* entries.\n if (stashRefCreated) {\n await execa('git', ['-C', args.hostRepo, 'update-ref', '-d', STASH_CARRYOVER_REF], {\n reject: false,\n });\n }\n await rm(stage, { recursive: true, force: true });\n }\n}\n\n/**\n * Best-effort `git stash create` on the host repo. Returns the stash SHA\n * (or `null` when the worktree is clean / git is missing / the call fails).\n * Mirrors the docker provider's `collectRepoCarryOver` shape — pure host\n * git, no side effects on the working tree.\n */\nasync function safeStashCreate(hostRepo: string): Promise<string | null> {\n const r = await execa('git', ['-C', hostRepo, 'stash', 'create'], { reject: false });\n if (r.exitCode !== 0) return null;\n const sha = r.stdout.trim();\n return sha.length > 0 ? sha : null;\n}\n\n/**\n * Tar the repo's untracked-not-ignored files into `outPath`. Returns the\n * tar size in bytes (0 when there's nothing to tar, so callers can skip\n * the upload). `git stash create` doesn't capture untracked, so the carry-\n * over needs this side channel — matches docker's behavior.\n */\nasync function maybeBuildUntrackedTar(hostRepo: string, outPath: string): Promise<number> {\n const list = await execa(\n 'git',\n ['-C', hostRepo, 'ls-files', '--others', '--exclude-standard', '-z'],\n { reject: false },\n );\n if (list.exitCode !== 0 || list.stdout.length === 0) return 0;\n // Feed NUL-delimited paths to `tar --null -T -` so spaces / quotes /\n // newlines in filenames survive. Use COPYFILE_DISABLE=1 to suppress\n // macOS' AppleDouble `._<name>` sidecars (same hardening as the\n // agent-credential tarballs).\n const tar = await execa(\n 'tar',\n ['-C', hostRepo, '--null', '-T', '-', '-czf', outPath],\n {\n input: list.stdout,\n env: { ...process.env, COPYFILE_DISABLE: '1' },\n reject: false,\n },\n );\n if (tar.exitCode !== 0) return 0;\n try {\n const { stat } = await import('node:fs/promises');\n const s = await stat(outPath);\n return s.size;\n } catch {\n return 0;\n }\n}\n\nasync function readOriginUrl(hostRepo: string): Promise<string | null> {\n const r = await execa('git', ['-C', hostRepo, 'remote', 'get-url', 'origin'], { reject: false });\n if (r.exitCode !== 0) return null;\n const out = (r.stdout ?? '').trim();\n return out.length > 0 ? out : null;\n}\n\ninterface SeedFromTarArgs {\n backend: CloudBackend;\n handle: CloudHandle;\n hostDir: string;\n workspaceDir: string;\n}\n\nasync function seedFromTar(args: SeedFromTarArgs): Promise<void> {\n const stage = await mkdtemp(join(tmpdir(), 'agentbox-tar-'));\n const tarPath = join(stage, 'workspace.tar.gz');\n try {\n await execa('tar', ['-C', args.hostDir, '-czf', tarPath, '.']);\n const remoteTar = '/tmp/agentbox-workspace.tar.gz';\n await args.backend.uploadFile(args.handle, tarPath, remoteTar);\n const SUDO = `if command -v sudo >/dev/null 2>&1; then SUDO='sudo -n'; else SUDO=''; fi`;\n const script = [\n `set -euo pipefail`,\n // Move out of any cwd we might inherit from Daytona's executeCommand\n // before we delete /workspace. The agentbox image bakes WORKDIR\n // /workspace; if the shell's cwd is /workspace when we `rm -rf` it,\n // the next process inherits a stale cwd FD and git-clone's child\n // (index-pack) fails with \"Unable to read current working directory\".\n `cd /tmp`,\n SUDO,\n `$SUDO rm -rf ${quoteShellArgv([args.workspaceDir])}`,\n `$SUDO mkdir -p ${quoteShellArgv([args.workspaceDir])}`,\n `$SUDO chown \"$(id -un):$(id -gn)\" ${quoteShellArgv([args.workspaceDir])}`,\n `tar -C ${quoteShellArgv([args.workspaceDir])} -xzf ${quoteShellArgv([remoteTar])}`,\n `rm -f ${quoteShellArgv([remoteTar])}`,\n ].join('\\n');\n const r = await args.backend.exec(args.handle, bashScript(script));\n if (r.exitCode !== 0) {\n throw new Error(`workspace seed (tar) failed: ${r.stderr || r.stdout}`);\n }\n } finally {\n await rm(stage, { recursive: true, force: true });\n }\n}\n","/**\n * `MockCloudBackend` — a fully in-memory implementation of `CloudBackend` for\n * tests and as a reference. New cloud backends (Vercel, Fly.io, …) can use\n * the contract suite in `test/contract.ts` to validate that their backend\n * behaves the way `createCloudProvider` expects, and look at this file as a\n * minimal example.\n *\n * Behavior is intentionally simple — every method records the call (so\n * tests can assert ordering) and resolves with deterministic values. No\n * I/O. The implementation covers every required method on `CloudBackend`\n * plus every optional one, so contract tests can exercise both branches.\n */\n\nimport type {\n CloudBackend,\n CloudExecOptions,\n CloudExecResult,\n CloudFileEntry,\n CloudHandle,\n CloudPreviewUrl,\n CloudProvisionRequest,\n CloudSandboxSummary,\n CloudState,\n} from '@agentbox/core';\n\ninterface SandboxRecord {\n id: string;\n name: string;\n state: CloudState;\n createdAt: string;\n image: string;\n /** Pseudo-file content keyed by remote path. */\n files: Map<string, Uint8Array>;\n}\n\nexport interface MockCloudBackendOptions {\n /** Name reported by `backend.name`. Defaults to `'mock'`. */\n name?: string;\n /** Initial sandboxes pre-loaded into the backend (e.g. for list() tests). */\n preloaded?: Array<{ id: string; name?: string; state?: CloudState; createdAt?: string }>;\n /**\n * Optional hook fired before every method runs. Tests can use this to\n * inject failures (`throw new Error(...)` or a typed Daytona-shaped error\n * to exercise the retry wrapper).\n */\n beforeCall?: (method: string, args: unknown[]) => void | Promise<void>;\n}\n\nexport interface MockCloudBackend extends CloudBackend {\n /** Method names captured in invocation order — useful for assertions. */\n readonly calls: ReadonlyArray<{ method: string; args: unknown[] }>;\n /** Sandboxes currently present in the backend (post any provision/destroy). */\n readonly sandboxes: ReadonlyArray<Readonly<SandboxRecord>>;\n}\n\n/** Make a fresh mock backend. Always returns a brand-new internal state. */\nexport function makeMockCloudBackend(opts: MockCloudBackendOptions = {}): MockCloudBackend {\n const name = opts.name ?? 'mock';\n const calls: Array<{ method: string; args: unknown[] }> = [];\n const sandboxes = new Map<string, SandboxRecord>();\n const snapshots = new Map<string, { id: string }>();\n\n for (const pre of opts.preloaded ?? []) {\n sandboxes.set(pre.id, {\n id: pre.id,\n name: pre.name ?? pre.id,\n state: pre.state ?? 'running',\n createdAt: pre.createdAt ?? new Date().toISOString(),\n image: 'mock/preloaded',\n files: new Map(),\n });\n }\n\n const record = async (method: string, args: unknown[]): Promise<void> => {\n calls.push({ method, args });\n if (opts.beforeCall) await opts.beforeCall(method, args);\n };\n const requireSandbox = (id: string): SandboxRecord => {\n const sb = sandboxes.get(id);\n if (!sb) throw new Error(`mock backend: sandbox ${id} not found`);\n return sb;\n };\n\n const backend: MockCloudBackend = {\n name,\n get calls() {\n return calls;\n },\n get sandboxes() {\n return Array.from(sandboxes.values());\n },\n\n async provision(req: CloudProvisionRequest): Promise<CloudHandle> {\n await record('provision', [req]);\n const id = `mock-${String(sandboxes.size + 1)}-${req.name}`;\n sandboxes.set(id, {\n id,\n name: req.name,\n state: 'running',\n createdAt: new Date().toISOString(),\n image: req.snapshot ?? req.image,\n files: new Map(),\n });\n return { sandboxId: id };\n },\n\n async get(sandboxId: string): Promise<CloudHandle | null> {\n await record('get', [sandboxId]);\n return sandboxes.has(sandboxId) ? { sandboxId } : null;\n },\n\n async list(): Promise<CloudSandboxSummary[]> {\n await record('list', []);\n return Array.from(sandboxes.values()).map((sb) => ({\n sandboxId: sb.id,\n name: sb.name,\n createdAt: sb.createdAt,\n state: sb.state,\n }));\n },\n\n async start(h: CloudHandle): Promise<void> {\n await record('start', [h]);\n const sb = requireSandbox(h.sandboxId);\n sb.state = 'running';\n },\n async stop(h: CloudHandle): Promise<void> {\n await record('stop', [h]);\n const sb = requireSandbox(h.sandboxId);\n sb.state = 'stopped';\n },\n async pause(h: CloudHandle): Promise<void> {\n await record('pause', [h]);\n const sb = requireSandbox(h.sandboxId);\n sb.state = 'paused';\n },\n async resume(h: CloudHandle): Promise<void> {\n await record('resume', [h]);\n const sb = requireSandbox(h.sandboxId);\n sb.state = 'running';\n },\n async destroy(h: CloudHandle): Promise<void> {\n await record('destroy', [h]);\n sandboxes.delete(h.sandboxId);\n },\n async state(h: CloudHandle): Promise<CloudState> {\n await record('state', [h]);\n const sb = sandboxes.get(h.sandboxId);\n return sb ? sb.state : 'missing';\n },\n\n async exec(\n h: CloudHandle,\n cmd: string,\n opts?: CloudExecOptions,\n ): Promise<CloudExecResult> {\n await record('exec', [h, cmd, opts]);\n requireSandbox(h.sandboxId);\n return { exitCode: 0, stdout: '', stderr: '' };\n },\n\n async uploadFile(h: CloudHandle, localPath: string, remotePath: string): Promise<void> {\n await record('uploadFile', [h, localPath, remotePath]);\n const sb = requireSandbox(h.sandboxId);\n sb.files.set(remotePath, new Uint8Array([0]));\n },\n async downloadFile(h: CloudHandle, remotePath: string, localPath: string): Promise<void> {\n await record('downloadFile', [h, remotePath, localPath]);\n const sb = requireSandbox(h.sandboxId);\n if (!sb.files.has(remotePath)) {\n throw new Error(`mock backend: ${remotePath} not in sandbox ${h.sandboxId}`);\n }\n },\n async listFiles(h: CloudHandle, remoteDir: string): Promise<CloudFileEntry[]> {\n await record('listFiles', [h, remoteDir]);\n const sb = requireSandbox(h.sandboxId);\n const prefix = remoteDir.endsWith('/') ? remoteDir : `${remoteDir}/`;\n const out: CloudFileEntry[] = [];\n for (const p of sb.files.keys()) {\n if (p.startsWith(prefix)) {\n out.push({ name: p.slice(prefix.length), isDir: false });\n }\n }\n return out;\n },\n\n async previewUrl(h: CloudHandle, port: number): Promise<CloudPreviewUrl> {\n await record('previewUrl', [h, port]);\n requireSandbox(h.sandboxId);\n return { url: `https://${String(port)}-${h.sandboxId}.mock.preview`, token: 'mock-token' };\n },\n\n async signedPreviewUrl(\n h: CloudHandle,\n port: number,\n expiresInSeconds: number,\n ): Promise<CloudPreviewUrl> {\n await record('signedPreviewUrl', [h, port, expiresInSeconds]);\n requireSandbox(h.sandboxId);\n return {\n url: `https://${String(port)}-${h.sandboxId}-signed${String(expiresInSeconds)}.mock.preview`,\n };\n },\n\n async attachArgv(h: CloudHandle): Promise<string[]> {\n await record('attachArgv', [h]);\n requireSandbox(h.sandboxId);\n return ['ssh', '-o', 'StrictHostKeyChecking=no', `mock-token@${h.sandboxId}.mock.ssh`];\n },\n\n async revokeAttachToken(h: CloudHandle, argv: string[]): Promise<void> {\n await record('revokeAttachToken', [h, argv]);\n },\n\n async ensureVolume(volumeName: string): Promise<{ volumeId: string }> {\n await record('ensureVolume', [volumeName]);\n return { volumeId: `vol-${volumeName}` };\n },\n\n async createSnapshot(h: CloudHandle, snapshotName: string): Promise<void> {\n await record('createSnapshot', [h, snapshotName]);\n requireSandbox(h.sandboxId);\n snapshots.set(snapshotName, { id: snapshotName });\n },\n\n async deleteSnapshot(snapshotName: string): Promise<void> {\n await record('deleteSnapshot', [snapshotName]);\n snapshots.delete(snapshotName);\n },\n };\n\n return backend;\n}\n","export {\n CLOUD_WEB_PROXY_PORT,\n CLOUD_WORKSPACE_DIR,\n createCloudProvider,\n emptyCloudStats,\n type CreateCloudProviderOptions,\n} from './cloud-provider.js';\nexport {\n launchCloudCtlDaemon,\n type LaunchCloudCtlArgs,\n} from './ctl-launch.js';\nexport {\n launchCloudDockerdDaemon,\n type CloudDockerdLaunchResult,\n} from './dockerd-launch.js';\nexport {\n seedCloudWorkspace,\n type SeedCloudWorkspaceArgs,\n type SeedCloudWorkspaceResult,\n} from './workspace-seed.js';\nexport {\n agentSpecsForCloud,\n ensureAgentVolumesForCloud,\n seedAgentVolumesIfFresh,\n type CloudAgentKind,\n type EnsureAgentVolumesResult,\n type SeedAgentVolumesOptions,\n} from './agent-credentials.js';\nexport {\n uploadEnvFiles,\n type UploadEnvFilesArgs,\n type UploadEnvFilesResult,\n} from './env-files.js';\nexport { bashScript, quoteShellArg, quoteShellArgv } from './shell.js';\nexport {\n makeMockCloudBackend,\n type MockCloudBackend,\n type MockCloudBackendOptions,\n} from './mock-backend.js';\nexport {\n downloadFromCloudBox,\n pullCloudDirContents,\n uploadToCloudBox,\n type CloudCpResult,\n} from './cloud-cp.js';\nexport {\n CLOUD_CHECKPOINTS_ROOT,\n CLOUD_SNAPSHOT_NAME_PREFIX,\n cloudSnapshotName,\n listCloudCheckpoints,\n removeCloudCheckpointDir,\n resolveCloudCheckpoint,\n writeCloudCheckpointManifest,\n type CloudCheckpointInfo,\n type CloudCheckpointManifest,\n type WriteCloudManifestFields,\n} from './checkpoint.js';\n// Re-export host-side agent-config staging from sandbox-docker so cloud\n// providers (sandbox-daytona, future cloud backends) can use them without\n// taking a direct sandbox-docker dep (which would bend the provider-isolation\n// rule). The implementations live in sandbox-docker for historical reasons:\n// they were originally built for the docker rsync-into-volume flow and stayed\n// there when the cloud path adopted them.\nexport {\n stageClaudeStaticForUpload,\n stageClaudeCredentialsForUpload,\n stageCodexStaticForUpload,\n stageCodexCredentialsForUpload,\n stageOpencodeStaticForUpload,\n stageOpencodeCredentialsForUpload,\n type StageClaudeOptions,\n type StageCodexOptions,\n type StageOpencodeOptions,\n type StageResult,\n} from '@agentbox/sandbox-docker';\n// Portless helpers — same re-export pattern as the stage* helpers above.\n// Lives in sandbox-docker for historical reasons (the file predates the\n// hetzner provider), surfaced here so non-docker providers (sandbox-hetzner,\n// any future SSH-tunneled backend) don't need a direct sandbox-docker dep.\n// Phase 1 of the hetzner provider work: `portlessBrowserEnv` now takes a\n// `{ mapTarget }` option so the in-box Chromium remap targets the right host\n// gateway per provider (`host.docker.internal` for docker, `127.0.0.1` for\n// hetzner where the box is the VPS).\nexport {\n detectPortless,\n installPortless,\n portlessAlias,\n portlessBrowserEnv,\n portlessGetUrl,\n portlessInstallHint,\n portlessStartHint,\n portlessUnalias,\n resetPortlessCache,\n resolvePortlessHostStateDir,\n startPortlessProxy,\n PORTLESS_PROXY_PORT,\n type PortlessBrowserEnvOptions,\n type PortlessState,\n} from '@agentbox/sandbox-docker';\n"],"mappings":"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","names":["basename","rm","writeFile","join","readFile","join","execa","mkdtemp","rm","tmpdir","statSync","join","writeFile","rm","readFile","mkdtemp","tmpdir","execa","WORKSPACE_DIR_DEFAULT","basename","opts"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../packages/relay/src/cloud-poller.ts"],"sourcesContent":["/**\n * `CloudBoxPoller` — host-resident loop that drains a cloud box's in-sandbox\n * relay over its Daytona preview URL. One poller per cloud box, owned by the\n * host relay process (so status / autopause behave the same whether or not a\n * CLI is attached, matching the Docker model).\n *\n * v0 responsibility: pull `/bridge/poll`, forward events + status into the\n * host relay's stores. Executing drained `HostAction`s (real `git push` on\n * the host, with `askPrompt` gating) and posting results back is the next\n * layer, tracked alongside Phase 4's host-RPC execution.\n */\n\nimport { request as httpRequest } from 'node:http';\nimport { request as httpsRequest } from 'node:https';\nimport { setTimeout as delay } from 'node:timers/promises';\nimport type {\n BridgeActionResultBody,\n BridgePollResponse,\n HostAction,\n HostActionResult,\n RelayEvent,\n} from './types.js';\n\nexport interface CloudBoxPollerDeps {\n boxId: string;\n /** Preview URL of the in-sandbox relay's `/bridge/*` surface. */\n previewUrl: string;\n /** Bearer for `/bridge/*` auth (Daytona's `x-daytona-preview-token` is added separately when known). */\n bridgeToken: string;\n /** Optional Daytona preview token header for the proxy layer. */\n previewToken?: string;\n /** Fired for each batch of new events drained from the box. */\n onEvents?: (events: RelayEvent[]) => void;\n /** Fired when a new box-status snapshot is observed. */\n onStatus?: (status: unknown) => void;\n /**\n * Fired for each parked host action the box wants the host to execute.\n * The `respond` callback POSTs back to `/bridge/action-result` and\n * unblocks the in-box `/rpc` caller — the handler MUST call it (with a\n * non-zero exitCode and stderr message even when the host can't execute\n * the action) so the box never hangs.\n */\n onAction?: (\n action: HostAction,\n respond: (result: HostActionResult) => Promise<void>,\n ) => Promise<void> | void;\n logger?: (line: string) => void;\n}\n\nconst BACKOFF_BASE_MS = 1500;\nconst BACKOFF_MAX_MS = 30_000;\n/**\n * Default request timeout. Daytona's CloudFront edge sits in front of the\n * in-sandbox relay and 504s once its idle window expires (~25-30s in\n * practice). Keep the default a hair below that so the poller's timer\n * fires before CloudFront's does, then short-cycle aggressively when we\n * actually see a 504 — see {@link FAST_REQUEST_TIMEOUT_MS}.\n */\nconst REQUEST_TIMEOUT_MS = 25_000;\n/**\n * Faster timeout used after a recent 504 response. Cuts the request short\n * enough that we round-trip multiple times in the same window where the\n * edge proxy would otherwise wedge — recovers throughput quickly when the\n * box is healthy but the edge is flaky.\n */\nconst FAST_REQUEST_TIMEOUT_MS = 8_000;\n/**\n * Number of consecutive non-504 polls before reverting from the fast\n * timeout back to the default. Decays naturally; no separate timer.\n */\nconst FAST_MODE_DECAY_POLLS = 5;\nconst STOPPED_TICK_MS = 250;\n\nexport class CloudBoxPoller {\n private stopped = false;\n private cursor = 0;\n private currentBackoffMs = 0;\n private loopPromise: Promise<void> | null = null;\n /**\n * Counts down from {@link FAST_MODE_DECAY_POLLS} after each 504. While > 0\n * the next poll uses {@link FAST_REQUEST_TIMEOUT_MS}. Successful polls\n * decrement, so a flaky edge converges back to the default timeout\n * within ~5 successful round-trips.\n */\n private fastModePolls = 0;\n\n constructor(private readonly deps: CloudBoxPollerDeps) {}\n\n start(): void {\n if (this.loopPromise) return;\n this.loopPromise = this.run().catch((err) => {\n this.log(`poller crashed: ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n\n async stop(): Promise<void> {\n this.stopped = true;\n if (this.loopPromise) await this.loopPromise;\n }\n\n /**\n * Post a `HostActionResult` back to the in-sandbox relay's\n * `/bridge/action-result`. The matching parked `/rpc` Promise resolves\n * and the in-box agent finally sees the answer.\n */\n async respond(actionId: string, result: HostActionResult): Promise<void> {\n const base = this.deps.previewUrl.replace(/\\/+$/, '');\n const url = new URL(`${base}/bridge/action-result`);\n const isHttps = url.protocol === 'https:';\n const transport = isHttps ? httpsRequest : httpRequest;\n const port = url.port.length > 0 ? Number.parseInt(url.port, 10) : isHttps ? 443 : 80;\n const body: BridgeActionResultBody = {\n id: actionId,\n exitCode: result.exitCode,\n stdout: result.stdout,\n stderr: result.stderr,\n };\n const payload = JSON.stringify(body);\n await new Promise<void>((resolve, reject) => {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(payload).toString(),\n Authorization: `Bearer ${this.deps.bridgeToken}`,\n };\n if (this.deps.previewToken) {\n headers['x-daytona-preview-token'] = this.deps.previewToken;\n }\n const req = transport(\n {\n host: url.hostname,\n port,\n method: 'POST',\n path: url.pathname,\n headers,\n timeout: REQUEST_TIMEOUT_MS,\n },\n (res) => {\n res.resume();\n if ((res.statusCode ?? 0) >= 200 && (res.statusCode ?? 0) < 300) resolve();\n else reject(new Error(`bridge/action-result → ${String(res.statusCode)}`));\n },\n );\n req.on('error', reject);\n req.on('timeout', () => {\n req.destroy();\n reject(new Error('bridge/action-result timeout'));\n });\n req.write(payload);\n req.end();\n });\n }\n\n private log(line: string): void {\n this.deps.logger?.(`[cloud-poller ${this.deps.boxId}] ${line}`);\n }\n\n private async run(): Promise<void> {\n while (!this.stopped) {\n try {\n const body = await this.pollOnce();\n this.currentBackoffMs = 0;\n if (body.events.length > 0) this.deps.onEvents?.(body.events);\n if (body.status != null) this.deps.onStatus?.(body.status);\n for (const action of body.actions) {\n // Fire-and-forget — the action executor either runs inline (v0) or\n // hands the action to its own queue (Phase 4 executor). Failures\n // there are not the poller's concern (but they MUST still respond\n // so the in-box RPC unblocks, hence the fallback below).\n try {\n const respond = (r: HostActionResult): Promise<void> => this.respond(action.id, r);\n if (this.deps.onAction) {\n await this.deps.onAction(action, respond);\n } else {\n await respond({\n exitCode: 1,\n stdout: '',\n stderr: `host has no executor for action '${action.method}'\\n`,\n });\n }\n } catch (err) {\n this.log(`action handler error: ${err instanceof Error ? err.message : String(err)}`);\n // Best-effort: tell the box we failed so its /rpc unblocks.\n try {\n await this.respond(action.id, {\n exitCode: 1,\n stdout: '',\n stderr: `host action handler crashed: ${err instanceof Error ? err.message : String(err)}\\n`,\n });\n } catch {\n // ignore\n }\n }\n }\n if (typeof body.cursor === 'number' && body.cursor > this.cursor) {\n this.cursor = body.cursor;\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes('→ 504') || msg.includes('504:')) {\n // Edge proxy timeout — the box is likely fine; arm fast-mode so the\n // next polls clip the request short of the next 504 window.\n this.fastModePolls = FAST_MODE_DECAY_POLLS;\n }\n this.log(`poll error: ${msg}`);\n await this.backoff();\n }\n // Successful poll decays fast-mode toward the default timeout.\n if (this.currentBackoffMs === 0 && this.fastModePolls > 0) {\n this.fastModePolls -= 1;\n }\n // Tight loop only when we got something fresh — otherwise yield briefly\n // so we don't spin under a misbehaving preview proxy.\n if (this.currentBackoffMs === 0) await delay(STOPPED_TICK_MS);\n }\n }\n\n private async backoff(): Promise<void> {\n this.currentBackoffMs =\n this.currentBackoffMs === 0\n ? BACKOFF_BASE_MS\n : Math.min(this.currentBackoffMs * 2, BACKOFF_MAX_MS);\n await delay(this.currentBackoffMs);\n }\n\n private async pollOnce(): Promise<BridgePollResponse> {\n const base = this.deps.previewUrl.replace(/\\/+$/, '');\n const url = new URL(`${base}/bridge/poll?since=${String(this.cursor)}`);\n const isHttps = url.protocol === 'https:';\n const transport = isHttps ? httpsRequest : httpRequest;\n const port = url.port.length > 0 ? Number.parseInt(url.port, 10) : isHttps ? 443 : 80;\n const timeoutMs = this.fastModePolls > 0 ? FAST_REQUEST_TIMEOUT_MS : REQUEST_TIMEOUT_MS;\n\n return new Promise<BridgePollResponse>((resolve, reject) => {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.deps.bridgeToken}`,\n Accept: 'application/json',\n };\n if (this.deps.previewToken) {\n headers['x-daytona-preview-token'] = this.deps.previewToken;\n }\n const req = transport(\n {\n host: url.hostname,\n port,\n method: 'GET',\n path: `${url.pathname}${url.search}`,\n headers,\n timeout: timeoutMs,\n },\n (res) => {\n const status = res.statusCode ?? 0;\n const chunks: Buffer[] = [];\n res.on('data', (c: Buffer) => chunks.push(c));\n res.on('end', () => {\n const text = Buffer.concat(chunks).toString('utf8');\n if (status < 200 || status >= 300) {\n reject(new Error(`bridge/poll → ${String(status)}: ${text.slice(0, 200)}`));\n return;\n }\n try {\n const parsed = JSON.parse(text) as Partial<BridgePollResponse>;\n resolve({\n actions: Array.isArray(parsed.actions) ? (parsed.actions as HostAction[]) : [],\n events: Array.isArray(parsed.events) ? (parsed.events as RelayEvent[]) : [],\n status: parsed.status ?? null,\n cursor: typeof parsed.cursor === 'number' ? parsed.cursor : this.cursor,\n });\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n res.on('error', reject);\n },\n );\n req.on('error', reject);\n req.on('timeout', () => {\n req.destroy();\n reject(new Error('bridge/poll timeout'));\n });\n req.end();\n });\n }\n}\n\n/**\n * Lifecycle helper: keep one poller per cloud box, start on register, stop on\n * forget. The host relay's `BoxRegistry` knows nothing about pollers; this\n * type sits alongside it.\n */\nexport class CloudBoxPollers {\n private readonly map = new Map<string, CloudBoxPoller>();\n\n /** Idempotent: replacing an existing poller for a box stops the old one first. */\n start(boxId: string, deps: CloudBoxPollerDeps): void {\n const existing = this.map.get(boxId);\n if (existing) {\n void existing.stop();\n }\n const poller = new CloudBoxPoller(deps);\n this.map.set(boxId, poller);\n poller.start();\n }\n\n async stop(boxId: string): Promise<void> {\n const p = this.map.get(boxId);\n if (!p) return;\n this.map.delete(boxId);\n await p.stop();\n }\n\n async stopAll(): Promise<void> {\n const ps = [...this.map.values()];\n this.map.clear();\n await Promise.all(ps.map((p) => p.stop()));\n }\n\n size(): number {\n return this.map.size;\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../packages/sandbox-daytona/src/dockerfile-context.ts","../../../packages/sandbox-daytona/src/env-loader.ts","../../../packages/sandbox-daytona/src/backend.ts","../../../packages/sandbox-daytona/src/retry.ts","../../../packages/sandbox-daytona/src/credentials.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Locate `Dockerfile.box` + its build context so Daytona can `Image.fromDockerfile`\n * the same image the Docker provider builds locally. The Dockerfile COPYs from\n * the monorepo (packages/ctl/dist/bin.cjs, apps/cli/share/..., scripts/), so\n * the context dir must contain that tree.\n *\n * Mirrors `@agentbox/sandbox-docker`'s `resolveDockerBuild`, intentionally\n * inlined: sandbox-daytona must not depend on sandbox-docker (cross-provider\n * dep would defeat the point of `@agentbox/sandbox-cloud`).\n *\n * Resolution order:\n * 0. AGENTBOX_DOCKER_CONTEXT env override.\n * 1. Staged context shipped with the bundled `agent-box` package (sibling\n * of dist/, uniform in dev + installed).\n * 2. Legacy monorepo layout: Dockerfile.box at sandbox-docker's package\n * root, context = monorepo root.\n */\nexport interface DockerfileContext {\n dockerfile: string;\n context: string;\n}\n\nexport function resolveDockerfileContext(): DockerfileContext | null {\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 here = dirname(fileURLToPath(import.meta.url));\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 monorepo: this module is at packages/sandbox-daytona/dist; the\n // Dockerfile lives at packages/sandbox-docker/Dockerfile.box; the build\n // context is the monorepo root.\n const monorepoRoot = resolve(here, '..', '..', '..');\n const dockerfile = resolve(monorepoRoot, 'packages', 'sandbox-docker', 'Dockerfile.box');\n if (existsSync(dockerfile)) {\n return { dockerfile, context: monorepoRoot };\n }\n return null;\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { resolve } from 'node:path';\n\n/**\n * Daytona env auto-loader. The SDK reads `DAYTONA_API_KEY` /\n * `DAYTONA_JWT_TOKEN` + `DAYTONA_ORGANIZATION_ID` from `process.env`. We pull\n * those keys in from `~/.agentbox/secrets.env` so the SDK Just Works after\n * the user runs `agentbox daytona 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 daytona login`.\n *\n * Project-level `.env` / `.env.local` are intentionally NOT consulted: those\n * files belong to the app code being developed, and a `DAYTONA_API_KEY`\n * there is typically meant for in-box code execution, not for the host CLI\n * to harvest and provision sandboxes with.\n *\n * Only Daytona-prefixed keys are imported; the rest of the file is left\n * alone. The loader is idempotent and side-effect-free after the first call.\n */\nconst DAYTONA_KEYS = [\n 'DAYTONA_API_KEY',\n 'DAYTONA_JWT_TOKEN',\n 'DAYTONA_ORGANIZATION_ID',\n 'DAYTONA_API_URL',\n 'DAYTONA_TARGET',\n] as const;\n\nlet loaded = false;\n\nexport function ensureDaytonaEnvLoaded(): void {\n if (loaded) return;\n loaded = true;\n importDaytonaFromFile(resolve(homedir(), '.agentbox', 'secrets.env'));\n}\n\nfunction importDaytonaFromFile(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 DAYTONA_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. Doesn't do variable interpolation — that's surprising to\n * users coming from full dotenv, but secrets typically don't reference each\n * other and we'd rather be predictable.\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 // Strip surrounding quotes (single or double).\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 * Daytona `CloudBackend` — maps the provider-neutral cloud primitives onto\n * `@daytonaio/sdk`. Lazy SDK client + lazy sandbox handle resolution so\n * importing this module costs nothing until a daytona-tagged box does something.\n */\n\nimport { Daytona, DaytonaNotFoundError, Image, SandboxState, type Sandbox } from '@daytonaio/sdk';\nimport type { CloudSandboxSummary } from '@agentbox/core';\nimport type {\n CloudBackend,\n CloudExecOptions,\n CloudExecResult,\n CloudFileEntry,\n CloudHandle,\n CloudPreviewUrl,\n CloudProvisionRequest,\n CloudState,\n CloudVolumeMount,\n} from '@agentbox/core';\nimport { resolveDockerfileContext } from './dockerfile-context.js';\nimport { ensureDaytonaEnvLoaded } from './env-loader.js';\nimport { withDaytonaRetry } from './retry.js';\n\n/**\n * Thin shorthand for `withDaytonaRetry` with our defaults. Most methods are\n * idempotent and use `retryOnAmbiguous: true`; the few that aren't override.\n */\nfunction retry<T>(\n method: string,\n fn: () => Promise<T>,\n opts: {\n attemptTimeoutMs?: number;\n retryOnAmbiguous?: boolean;\n /** When true, single-shot — no backoff list, no retries. */\n noRetry?: boolean;\n } = {},\n): Promise<T> {\n return withDaytonaRetry(\n {\n method,\n retryOnAmbiguous: opts.retryOnAmbiguous ?? true,\n attemptTimeoutMs: opts.attemptTimeoutMs,\n backoffMs: opts.noRetry === true ? [] : undefined,\n },\n fn,\n );\n}\n\n/**\n * Sentinel image ref the cloud-provider hands to us when the user didn't pass\n * `--image`. We translate it to `Image.fromDockerfile(...)` so Daytona builds\n * the same box image the Docker provider builds locally.\n */\nexport const DEFAULT_BOX_IMAGE_REF = 'agentbox/box:dev';\n\nlet client: Daytona | null = null;\nexport function getClient(): Daytona {\n if (!client) {\n // Pull DAYTONA_* keys from `.env.local` / `.env` / `~/.agentbox/secrets.env`\n // into process.env first — the SDK reads from process.env and most users\n // keep secrets in a project file rather than their shell rc.\n ensureDaytonaEnvLoaded();\n try {\n // Daytona() reads DAYTONA_API_KEY / DAYTONA_JWT_TOKEN + DAYTONA_ORGANIZATION_ID\n // from env.\n client = new Daytona();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // The interactive prompt in `agentbox daytona login` handles first-run\n // setup; this error path is for non-TTY callers (CI, scripts) where the\n // prompt was skipped.\n throw new Error(\n `Daytona credentials not configured: ${msg}\\n` +\n `Run \\`agentbox daytona login\\` interactively, or set DAYTONA_API_KEY in the environment.`,\n );\n }\n }\n return client;\n}\n\nasync function getSandbox(id: string): Promise<Sandbox> {\n return getClient().get(id);\n}\n\nasync function maybeGetSandbox(id: string): Promise<Sandbox | null> {\n try {\n return await getClient().get(id);\n } catch {\n return null;\n }\n}\n\n/**\n * Map Daytona's `SandboxState` (16 fine-grained values incl. transitional ones)\n * onto our 4-value `CloudState`. Transitional states ('starting', 'creating')\n * are reported as 'running' so callers don't ping-pong; 'archived' maps to\n * 'paused' (our pause is Daytona's archive).\n */\nfunction mapState(s: SandboxState | string | undefined): CloudState {\n switch (s) {\n case SandboxState.STARTED:\n return 'running';\n case SandboxState.STARTING:\n case SandboxState.CREATING:\n case SandboxState.RESTORING:\n case SandboxState.BUILDING_SNAPSHOT:\n case SandboxState.PULLING_SNAPSHOT:\n case SandboxState.PENDING_BUILD:\n case SandboxState.STOPPING:\n return 'running';\n case SandboxState.STOPPED:\n return 'stopped';\n case SandboxState.ARCHIVED:\n case SandboxState.ARCHIVING:\n return 'paused';\n case SandboxState.DESTROYED:\n case SandboxState.DESTROYING:\n case SandboxState.ERROR:\n case SandboxState.BUILD_FAILED:\n case SandboxState.UNKNOWN:\n default:\n return 'missing';\n }\n}\n\n/**\n * Translate our provider-neutral `CloudVolumeMount` into the SDK shape Daytona\n * expects. The SDK's `VolumeMount` carries `volumeId` + `mountPath` (+ optional\n * `subpath` for S3-prefix mounts); a 1:1 mapping with our type.\n */\nfunction toDaytonaVolumeMount(v: CloudVolumeMount): {\n volumeId: string;\n mountPath: string;\n subpath?: string;\n} {\n return {\n volumeId: v.volumeId,\n mountPath: v.mountPath,\n ...(v.subpath ? { subpath: v.subpath } : {}),\n };\n}\n\n/** Translate the request's image ref into something Daytona's `create` accepts. */\nfunction resolveImage(ref: string): string | Image {\n if (ref !== DEFAULT_BOX_IMAGE_REF) return ref;\n const ctx = resolveDockerfileContext();\n if (!ctx) {\n throw new Error(\n \"could not locate the AgentBox Dockerfile.box build context for the Daytona snapshot. \" +\n \"Set AGENTBOX_DOCKER_CONTEXT to a directory containing Dockerfile.box, or pass --image <ref> with a Daytona-compatible image.\",\n );\n }\n // Image.fromDockerfile bundles the directory the Dockerfile lives in and\n // ships it to Daytona to build a snapshot. The Dockerfile.box COPYs from\n // the monorepo tree; the staged `runtime/docker` context already mirrors\n // that tree, so the build resolves COPY paths correctly.\n return Image.fromDockerfile(ctx.dockerfile);\n}\n\nexport const daytonaBackend: CloudBackend = {\n name: 'daytona',\n\n async provision(req: CloudProvisionRequest): Promise<CloudHandle> {\n // No-retry: provision is non-idempotent — a 504 after the request reaches\n // the origin could create a duplicate billable sandbox we can't reference\n // for cleanup. The wrapper still bounds wall-clock at 900s (matching the\n // existing inline SDK timeout) so a wedged connection fails cleanly.\n return retry(\n 'provision',\n async () => {\n // Two SDK overloads:\n // - `CreateSandboxFromSnapshotParams` takes `snapshot:` and no\n // `onSnapshotCreateLogs` (the snapshot already exists, nothing to build).\n // - `CreateSandboxFromImageParams` takes `image:` and accepts\n // `onSnapshotCreateLogs` for streaming the Dockerfile build.\n // TypeScript can't infer the right overload from a union literal, so\n // split the call.\n const baseParams = {\n ...(req.resources ? { resources: req.resources } : {}),\n envVars: req.env,\n ...(req.volumes && req.volumes.length > 0\n ? { volumes: req.volumes.map(toDaytonaVolumeMount) }\n : {}),\n labels: { 'agentbox.name': req.name },\n };\n const client = getClient();\n // The first-time Dockerfile.box snapshot build is ~41 layers and pulls\n // Chromium — comfortably 5+ minutes wall time. Daytona's default ready\n // timeout is too short for that; override with 15 min so a cold build\n // doesn't fail mid-snapshot. Cached snapshots and snapshot-based\n // creates come up in seconds.\n // Resolve `req.image` against Daytona's snapshot registry first when\n // it's set to a non-default value: `agentbox prepare --provider\n // daytona` registers a named snapshot and writes `box.image:\n // <name>` into project config; subsequent creates should boot from\n // that snapshot, not try to pull `<name>:latest` from Docker Hub.\n // Default ref (agentbox/box:dev) skips the lookup and goes through\n // resolveImage (Image.fromDockerfile). Explicit `req.snapshot` always\n // wins (cloud checkpoint path).\n let snapshotName = req.snapshot;\n if (!snapshotName && req.image && req.image !== DEFAULT_BOX_IMAGE_REF) {\n try {\n const snap = await client.snapshot.get(req.image);\n if (snap && snap.name) snapshotName = snap.name;\n } catch {\n // Not a known snapshot — fall through and treat as a Docker image ref.\n }\n }\n // Daytona rejects `resources` on the snapshot path — the snapshot's\n // own params encode them. Strip resources only for the snapshot\n // branch; the image branch keeps them.\n const snapshotParams: Record<string, unknown> = { ...baseParams };\n delete snapshotParams.resources;\n const sandbox = snapshotName\n ? await client.create({ snapshot: snapshotName, ...snapshotParams }, { timeout: 900 })\n : await client.create(\n { image: resolveImage(req.image), ...baseParams },\n {\n timeout: 900,\n ...(req.onLog ? { onSnapshotCreateLogs: req.onLog } : {}),\n },\n );\n return { sandboxId: sandbox.id };\n },\n { retryOnAmbiguous: false, attemptTimeoutMs: 900_000 },\n );\n },\n\n async ensureVolume(name: string): Promise<{ volumeId: string }> {\n // Daytona's `volume.get(name, create=true)` returns the existing volume or\n // initiates creation on first call. Critically, a freshly-created volume\n // comes back in `creating`/`pending_create` state — passing such a volume\n // into `Daytona.create({ volumes: […] })` is rejected with\n // \"Volume is not in a ready state. Current state: creating\". So poll\n // `volume.get` until the state lands on `ready` (or a terminal failure).\n //\n // Volumes are org-scoped on Daytona — every sandbox in the same Daytona\n // organization sees the same id, which is what we want for sharing agent\n // credentials across all of a user's boxes.\n //\n // Each individual `volume.get` call is retry-wrapped so a transient edge\n // hiccup mid-poll doesn't fail the whole ensure.\n const client = getClient();\n let vol = await retry('volume.get(create)', () => client.volume.get(name, true));\n // Volumes typically transition from creating → ready within a few seconds.\n // Allow up to 60s in case of slow control-plane operations.\n const deadline = Date.now() + 60_000;\n while (vol.state !== 'ready') {\n if (vol.state === 'error' || vol.state === 'deleted' || vol.state === 'deleting') {\n throw new Error(\n `Daytona volume '${name}' is in unrecoverable state '${vol.state}'. ` +\n `Delete it from the Daytona dashboard and retry.`,\n );\n }\n if (Date.now() >= deadline) {\n throw new Error(\n `Daytona volume '${name}' did not become ready within 60s (state: ${vol.state}). ` +\n `Try again — the Daytona control plane may be slow.`,\n );\n }\n await new Promise((r) => setTimeout(r, 1000));\n vol = await retry('volume.get(poll)', () => client.volume.get(name));\n }\n return { volumeId: vol.id };\n },\n\n async get(sandboxId: string): Promise<CloudHandle | null> {\n return retry('get', async () => {\n const sb = await maybeGetSandbox(sandboxId);\n return sb ? { sandboxId: sb.id } : null;\n });\n },\n\n async list(): Promise<CloudSandboxSummary[]> {\n return retry('list', async () => {\n const client = getClient();\n // `client.list()` returns `PaginatedSandboxes { items: Sandbox[] }`\n // (page 1 by default). For prune we don't need multi-page traversal\n // yet — sandboxes per org are bounded; if that changes, loop on page.\n const page = await client.list();\n const items = Array.isArray(page) ? page : (page.items ?? []);\n return items.map((sb): CloudSandboxSummary => {\n const summary: CloudSandboxSummary = { sandboxId: sb.id };\n const raw = sb as unknown as {\n name?: string;\n labels?: Record<string, string>;\n state?: string;\n createdAt?: string;\n };\n const friendly = raw.labels?.['agentbox.name'] ?? raw.name;\n if (friendly) summary.name = friendly;\n if (raw.createdAt) summary.createdAt = raw.createdAt;\n if (typeof raw.state === 'string') summary.state = mapState(raw.state);\n return summary;\n });\n });\n },\n\n async start(h: CloudHandle): Promise<void> {\n return retry(\n 'start',\n async () => {\n const sb = await getSandbox(h.sandboxId);\n await sb.start();\n },\n { attemptTimeoutMs: 60_000 },\n );\n },\n\n async stop(h: CloudHandle): Promise<void> {\n return retry(\n 'stop',\n async () => {\n const sb = await getSandbox(h.sandboxId);\n await sb.stop();\n },\n { attemptTimeoutMs: 60_000 },\n );\n },\n\n async pause(h: CloudHandle): Promise<void> {\n // Our pause == cold storage (Daytona archive). The tradeoff is documented\n // in CloudBackend's interface comment.\n return retry(\n 'pause',\n async () => {\n const sb = await getSandbox(h.sandboxId);\n await sb.archive();\n },\n { attemptTimeoutMs: 60_000 },\n );\n },\n\n async resume(h: CloudHandle): Promise<void> {\n return retry(\n 'resume',\n async () => {\n const sb = await getSandbox(h.sandboxId);\n await sb.start();\n },\n { attemptTimeoutMs: 60_000 },\n );\n },\n\n async destroy(h: CloudHandle): Promise<void> {\n return retry(\n 'destroy',\n async () => {\n const sb = await maybeGetSandbox(h.sandboxId);\n if (!sb) return; // already gone — destroy is idempotent\n // Daytona's `delete()` on a running sandbox is queued, not synchronous —\n // observed in practice: `delete()` returns ok, the sandbox stays in\n // 'started' for tens of seconds, then eventually disappears. Stopping\n // first makes the delete synchronous so callers (and the dashboard) see\n // it gone immediately. Swallow stop errors — if the sandbox is already\n // stopped/archived, delete still works.\n try {\n await sb.stop(60);\n } catch {\n /* best-effort */\n }\n try {\n await sb.delete(60);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Already deleted between stop and delete — fine.\n if (!/not found/i.test(msg)) throw err;\n }\n },\n { attemptTimeoutMs: 120_000 },\n );\n },\n\n async state(h: CloudHandle): Promise<CloudState> {\n return retry('state', async () => {\n const sb = await maybeGetSandbox(h.sandboxId);\n if (!sb) return 'missing';\n return mapState(sb.state);\n });\n },\n\n async exec(\n h: CloudHandle,\n cmd: string,\n opts?: CloudExecOptions,\n ): Promise<CloudExecResult> {\n return retry(\n 'exec',\n async () => {\n const sb = await getSandbox(h.sandboxId);\n // Daytona's ExecuteResponse returns combined output in `result` with no\n // separate stderr stream. Surface it as stdout and leave stderr empty —\n // callers that need split streams must redirect inside `cmd` itself.\n const r = await sb.process.executeCommand(cmd, opts?.cwd, opts?.env);\n return { exitCode: r.exitCode, stdout: r.result, stderr: '' };\n },\n { attemptTimeoutMs: opts?.attemptTimeoutMs ?? 120_000, noRetry: opts?.noRetry },\n );\n },\n\n async uploadFile(h: CloudHandle, localPath: string, remotePath: string): Promise<void> {\n return retry(\n 'uploadFile',\n async () => {\n const sb = await getSandbox(h.sandboxId);\n await sb.fs.uploadFile(localPath, remotePath);\n },\n { attemptTimeoutMs: 300_000 },\n );\n },\n\n async downloadFile(h: CloudHandle, remotePath: string, localPath: string): Promise<void> {\n return retry(\n 'downloadFile',\n async () => {\n const sb = await getSandbox(h.sandboxId);\n await sb.fs.downloadFile(remotePath, localPath);\n },\n { attemptTimeoutMs: 300_000 },\n );\n },\n\n async listFiles(h: CloudHandle, remoteDir: string): Promise<CloudFileEntry[]> {\n return retry('listFiles', async () => {\n const sb = await getSandbox(h.sandboxId);\n const files = await sb.fs.listFiles(remoteDir);\n return files.map((f) => ({\n name: f.name,\n isDir: Boolean((f as { isDir?: boolean }).isDir),\n }));\n });\n },\n\n async previewUrl(h: CloudHandle, port: number): Promise<CloudPreviewUrl> {\n return retry('previewUrl', async () => {\n const sb = await getSandbox(h.sandboxId);\n const p = await sb.getPreviewLink(port);\n // The host CloudBoxPoller attaches `token` as `x-daytona-preview-token`\n // for every /bridge call. Browser-bound URLs use `signedPreviewUrl` below\n // instead (the two token kinds are not interchangeable on Daytona).\n return { url: p.url, token: p.token };\n });\n },\n\n async signedPreviewUrl(\n h: CloudHandle,\n port: number,\n expiresInSeconds: number,\n ): Promise<CloudPreviewUrl> {\n return retry('signedPreviewUrl', async () => {\n const sb = await getSandbox(h.sandboxId);\n const s = await sb.getSignedPreviewUrl(port, expiresInSeconds);\n return { url: s.url, token: s.token };\n });\n },\n\n async attachArgv(h: CloudHandle): Promise<string[]> {\n return retry('attachArgv', async () => {\n const sb = await getSandbox(h.sandboxId);\n // 60 min default expiry matches the SDK default; an interactive session\n // longer than that is rare. `sandbox-cloud`'s buildAttach appends\n // `-t '<inner cmd>'` for the per-session tmux attach.\n const ssh = await sb.createSshAccess(60);\n return [\n 'ssh',\n // First-connect to a never-seen host fingerprint should be silent in a\n // PTY — the user already authenticated via Daytona's API.\n '-o', 'StrictHostKeyChecking=accept-new',\n // Daytona's SSH gateway terminates per-token; no key file, no port.\n `${ssh.token}@ssh.app.daytona.io`,\n ];\n });\n },\n\n async revokeAttachToken(h: CloudHandle, argv: string[]): Promise<void> {\n // argv[3] = `${token}@ssh.app.daytona.io`; pull the token off the front.\n const userhost = argv[argv.length - 1] ?? '';\n const atIdx = userhost.indexOf('@');\n if (atIdx <= 0) return;\n const token = userhost.slice(0, atIdx);\n if (token.length === 0) return;\n try {\n await retry('revokeAttachToken', async () => {\n const sb = await getSandbox(h.sandboxId);\n await sb.revokeSshAccess(token);\n });\n } catch {\n // Best-effort — tokens auto-expire after 60 min anyway.\n }\n },\n\n async createSnapshot(h: CloudHandle, snapshotName: string): Promise<void> {\n // Daytona's `_experimental_createSnapshot` puts the sandbox into the\n // `snapshotting` state, captures its filesystem, then returns. The\n // resulting snapshot is org-scoped and visible via the Daytona dashboard\n // and `client.snapshot.list()`. We give it a generous timeout (15min,\n // matching `provision`) because a large `/workspace` plus warmed agent\n // volumes can take a while to snapshot.\n //\n // No retry on ambiguous failures: a 504 mid-snapshot could leave a\n // half-built named snapshot in Daytona that a retry would collide on.\n // Matches `provision`'s policy.\n return retry(\n 'createSnapshot',\n async () => {\n const sb = await getSandbox(h.sandboxId);\n await sb._experimental_createSnapshot(snapshotName);\n },\n { attemptTimeoutMs: 900_000, retryOnAmbiguous: false },\n );\n },\n\n async deleteSnapshot(snapshotName: string): Promise<void> {\n return retry('deleteSnapshot', async () => {\n try {\n const client = getClient();\n const snapshot = await client.snapshot.get(snapshotName);\n await client.snapshot.delete(snapshot);\n } catch (err) {\n // Idempotent: a snapshot that's already gone is success from the\n // caller's perspective (mirrors `destroy()`'s \"not found\" handling).\n if (err instanceof DaytonaNotFoundError) return;\n const msg = err instanceof Error ? err.message : String(err);\n if (/not found/i.test(msg)) return;\n throw err;\n }\n });\n },\n};\n","/**\n * Bounded retry wrapper for `daytonaBackend` SDK calls. Daytona's CloudFront\n * edge intermittently 504s on `executeCommand` and other API calls (backlog\n * item 6.1) — without bounded retries an edge hiccup propagates as an\n * unbounded wedge in the calling code. This helper classifies transient\n * failures vs. permanent ones using the SDK's typed error classes, bounds\n * each attempt with a timeout, and caps the total wall-clock cost.\n *\n * Non-idempotent ops (`provision`) pass `retryOnAmbiguous: false` so a 504\n * after the request reached the origin doesn't create a duplicate sandbox.\n */\n\nimport {\n DaytonaAuthenticationError,\n DaytonaAuthorizationError,\n DaytonaConflictError,\n DaytonaConnectionError,\n DaytonaError,\n DaytonaNotFoundError,\n DaytonaRateLimitError,\n DaytonaTimeoutError,\n DaytonaValidationError,\n} from '@daytonaio/sdk';\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 (since 504 from CloudFront can mean \"origin still processing\").\n * Set false for non-idempotent operations (e.g. `provision`) where a\n * retry could create a duplicate.\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\n/** Internal sentinel used by the per-attempt timeout race. */\nclass AttemptTimeoutError extends Error {\n constructor(method: string, ms: number) {\n super(`daytona ${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 // Rate-limit responses always carry an intent from the server: back off.\n if (err instanceof DaytonaRateLimitError) return true;\n\n // Permanent client-side failures: never retry — the next call will get\n // the same answer and we'd just be wasting wall-clock.\n if (\n err instanceof DaytonaNotFoundError ||\n err instanceof DaytonaAuthenticationError ||\n err instanceof DaytonaAuthorizationError ||\n err instanceof DaytonaValidationError ||\n err instanceof DaytonaConflictError\n ) {\n return false;\n }\n\n // Connection / per-attempt timeout: the request may not have reached\n // the server. Gated by allowAmbiguous so non-idempotent callers can opt\n // out of double-execute risk.\n if (\n err instanceof DaytonaConnectionError ||\n err instanceof DaytonaTimeoutError ||\n err instanceof AttemptTimeoutError\n ) {\n return allowAmbiguous;\n }\n\n // Base DaytonaError: branch on statusCode. 5xx is ambiguous; 4xx we\n // didn't catch above is a permanent failure we hadn't seen before.\n if (err instanceof DaytonaError) {\n const status = err.statusCode;\n if (typeof status === 'number' && status >= 500 && status <= 599) {\n return allowAmbiguous;\n }\n return false;\n }\n\n // Axios-style fallback for raw errors that leak through without an SDK\n // wrapper. Match the same shape the SDK uses internally.\n if (err && typeof err === 'object') {\n const code = (err 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 ) {\n return allowAmbiguous;\n }\n const status =\n (err as { response?: { status?: unknown } }).response?.status ??\n (err as { status?: unknown }).status ??\n (err as { statusCode?: unknown }).statusCode;\n if (typeof status === 'number' && status >= 500 && status <= 599) {\n return allowAmbiguous;\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 withDaytonaRetry<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 `daytona ${opts.method}: attempt ${String(attempt)} failed (${errorSummary(err)}); retrying in ${String(delay)}ms`,\n );\n await sleep(delay);\n }\n }\n // Unreachable: the loop above either returns or throws.\n throw new Error(`withDaytonaRetry: exhausted attempts for ${opts.method}`);\n}\n\nfunction defaultRetryLog(line: string): void {\n // Prefix so log scrapers + users can distinguish retry chatter from real\n // CLI output. `\\n` before is intentional — many CLI surfaces use clack\n // spinners on stdout, and stderr lines without a leading newline can\n // collide with a redraw.\n process.stderr.write(`\\n[daytona-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 DaytonaError) {\n const status = err.statusCode;\n const cls = err.constructor.name;\n return `${cls}${typeof status === 'number' ? ` ${String(status)}` : ''}: ${truncate(err.message)}`;\n }\n if (err instanceof Error) {\n const code = (err as { code?: unknown }).code;\n return code !== undefined ? `${err.name}(${String(code)}): ${truncate(err.message)}` : `${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","import { spawnSync } from 'node:child_process';\nimport {\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n writeFileSync,\n} from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, resolve } from 'node:path';\nimport { confirm, isCancel, intro, log, note, outro, password, spinner, text } from '@clack/prompts';\nimport { ensureDaytonaEnvLoaded } from './env-loader.js';\n\nconst DASHBOARD_KEYS_URL = 'https://app.daytona.io/dashboard/keys';\n\n/**\n * Keys we manage in `~/.agentbox/secrets.env`. When the user reconfigures we\n * strip any prior values for these keys before appending the new ones so the\n * file never accumulates duplicates.\n */\nconst MANAGED_KEYS = ['DAYTONA_API_KEY', 'DAYTONA_JWT_TOKEN', 'DAYTONA_ORGANIZATION_ID'] as const;\ntype ManagedKey = (typeof MANAGED_KEYS)[number];\n\nexport interface EnsureDaytonaCredentialsOptions {\n /** Re-prompt even when valid credentials are already present (used by `agentbox daytona login`). */\n force?: boolean;\n}\n\n/**\n * First-run interactive setup for Daytona credentials. Walks the user through\n * opening the dashboard, pasting an API key (or JWT + organization ID), and\n * persists the result to `~/.agentbox/secrets.env` — which the env-loader\n * already picks up for every cloud command.\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 the\n * \"credentials not configured\" error from the SDK instead of a hung prompt.\n */\nexport async function ensureDaytonaCredentials(\n opts: EnsureDaytonaCredentialsOptions = {},\n): Promise<void> {\n ensureDaytonaEnvLoaded();\n\n if (!opts.force && hasUsableCredentials()) return;\n if (!process.stdin.isTTY) return;\n\n intro('Daytona setup');\n note(\n `AgentBox needs a Daytona API key to provision cloud boxes.\\n` +\n `Generate one at ${DASHBOARD_KEYS_URL}`,\n 'API key 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('Daytona setup cancelled — re-run `agentbox daytona login` when ready.');\n return;\n }\n if (open) openDashboard();\n\n // One retry on auth failure (typos are the common case). Beyond that we bail\n // and surface the validation error; the user can re-run `agentbox daytona login`.\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(`Daytona credentials saved to ${secretsPath()}`);\n outro('Setup complete.');\n return;\n }\n if (result.kind === 'auth' && attempt === 0) {\n log.error(`That key was rejected by Daytona: ${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 Daytona to validate (${result.message}) — saving anyway.`);\n persistCredentials(creds);\n log.success(`Daytona credentials saved to ${secretsPath()}`);\n outro('Setup complete (unvalidated).');\n return;\n }\n throw new Error(`Daytona credentials rejected: ${result.message}`);\n }\n}\n\nfunction hasUsableCredentials(): boolean {\n if (process.env.DAYTONA_API_KEY) return true;\n if (process.env.DAYTONA_JWT_TOKEN && process.env.DAYTONA_ORGANIZATION_ID) return true;\n return false;\n}\n\ninterface Credentials {\n apiKey?: string;\n jwtToken?: string;\n organizationId?: string;\n}\n\nasync function promptForCredentials(): Promise<Credentials | null> {\n const key = await password({\n message: 'Paste your Daytona API key (or JWT token)',\n validate(v) {\n if (!v || v.trim().length === 0) return 'Cannot be empty';\n return undefined;\n },\n });\n if (isCancel(key)) {\n log.warn('Daytona setup cancelled.');\n return null;\n }\n const trimmed = key.trim();\n\n // JWTs start with `eyJ` (base64-encoded `{\"`). API keys don't, and don't need\n // an org ID — the SDK derives it from the key. Only ask for org ID for JWTs.\n if (trimmed.startsWith('eyJ')) {\n const org = await text({\n message: 'Paste your Daytona organization ID',\n placeholder: 'org_...',\n validate(v) {\n if (!v || v.trim().length === 0) return 'Cannot be empty';\n return undefined;\n },\n });\n if (isCancel(org)) {\n log.warn('Daytona setup cancelled.');\n return null;\n }\n return { jwtToken: trimmed, organizationId: org.trim() };\n }\n\n return { apiKey: trimmed };\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 Daytona');\n\n // Snapshot existing env so we can restore on failure — never poison\n // process.env with a bad key.\n const snapshot = snapshotManagedEnv();\n applyToEnv(creds);\n\n try {\n // Dynamic import so the SDK only loads when we actually need it (keeps the\n // Docker hot path lean, same reason as the provider registry).\n const { Daytona } = await import('@daytonaio/sdk');\n const client = new Daytona();\n await client.list();\n s.stop('Daytona credentials accepted');\n return { ok: true };\n } catch (err) {\n restoreManagedEnv(snapshot);\n const message = err instanceof Error ? err.message : String(err);\n s.stop('Daytona credentials check failed');\n if (/401|403|unauthor|forbidden|invalid/i.test(message)) {\n return { ok: false, kind: 'auth', message };\n }\n return { ok: false, kind: 'network', message };\n }\n}\n\nfunction 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\nfunction 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\nfunction applyToEnv(creds: Credentials): void {\n // Wipe the other auth method so the SDK doesn't get confused by stale env\n // (e.g. an old JWT lingering from a previous shell export).\n for (const k of MANAGED_KEYS) delete process.env[k];\n if (creds.apiKey) process.env.DAYTONA_API_KEY = creds.apiKey;\n if (creds.jwtToken) process.env.DAYTONA_JWT_TOKEN = creds.jwtToken;\n if (creds.organizationId) process.env.DAYTONA_ORGANIZATION_ID = creds.organizationId;\n}\n\nfunction persistCredentials(creds: Credentials): void {\n applyToEnv(creds);\n const path = secretsPath();\n mkdirSync(dirname(path), { recursive: true });\n\n // Read existing file, strip any managed keys, append fresh values. Keeps\n // unrelated DAYTONA_API_URL / DAYTONA_TARGET (or anything else the user\n // dropped here) untouched.\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[] = [];\n if (creds.apiKey) lines.push(`DAYTONA_API_KEY=${creds.apiKey}`);\n if (creds.jwtToken) lines.push(`DAYTONA_JWT_TOKEN=${creds.jwtToken}`);\n if (creds.organizationId) lines.push(`DAYTONA_ORGANIZATION_ID=${creds.organizationId}`);\n\n const body = (kept ? `${kept}\\n` : '') + lines.join('\\n') + '\\n';\n\n // Atomic write — rename(2) is atomic on the same filesystem, so partially\n // written secrets can't be left behind on a crash.\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, body, { mode: 0o600 });\n try {\n chmodSync(tmp, 0o600);\n } catch {\n // chmod best-effort; writeFileSync mode already covers most filesystems.\n }\n renameSync(tmp, path);\n try {\n chmodSync(path, 0o600);\n } catch {\n // ignore — already attempted above\n }\n}\n\nfunction openDashboard(): void {\n try {\n const r = spawnSync('open', [DASHBOARD_KEYS_URL], { stdio: 'ignore' });\n if (r.status !== 0) {\n log.warn(`Could not auto-open the browser — visit ${DASHBOARD_KEYS_URL} manually.`);\n }\n } catch {\n log.warn(`Could not auto-open the browser — visit ${DASHBOARD_KEYS_URL} manually.`);\n }\n}\n\nexport function secretsPath(): string {\n return resolve(homedir(), '.agentbox', 'secrets.env');\n}\n\n/** What's currently configured. Used by `daytona login --status`. */\nexport interface DaytonaCredStatus {\n apiKey?: string;\n jwtToken?: string;\n organizationId?: string;\n source: 'env' | 'secrets.env' | 'none';\n}\n\nexport function readDaytonaCredStatus(): DaytonaCredStatus {\n // Snapshot what the shell already had before the loader runs so we can\n // distinguish env-from-shell from env-loaded-from-secrets.env.\n const shellHadKey = !!process.env.DAYTONA_API_KEY || !!process.env.DAYTONA_JWT_TOKEN;\n ensureDaytonaEnvLoaded();\n const apiKey = process.env.DAYTONA_API_KEY;\n const jwtToken = process.env.DAYTONA_JWT_TOKEN;\n const organizationId = process.env.DAYTONA_ORGANIZATION_ID;\n if (!apiKey && !jwtToken) return { source: 'none' };\n return {\n apiKey,\n jwtToken,\n organizationId,\n source: shellHadKey ? '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"],"mappings":"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","names":["existsSync","resolve","DaytonaNotFoundError","readFileSync","homedir","dirname","log","client","Daytona"]}