@runcofactor/cli 0.46.0 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-IJTIWAY6.js → chunk-BYKQ5N5L.js} +65 -21
- package/dist/chunk-BYKQ5N5L.js.map +1 -0
- package/dist/{chunk-E6K4PAID.js → chunk-SQXLD7QP.js} +2 -2
- package/dist/chunk-SQXLD7QP.js.map +1 -0
- package/dist/index.js +5 -5
- package/dist/{landing-NJZS5JDT.js → landing-3TY7TLVZ.js} +2 -2
- package/dist/{sandbox-OIC6CDHG.js → sandbox-5Y4HALKW.js} +160 -41
- package/dist/sandbox-5Y4HALKW.js.map +1 -0
- package/dist/{skill-G2654GLA.js → skill-A4MN7UIT.js} +327 -14
- package/dist/skill-A4MN7UIT.js.map +1 -0
- package/dist/{workflow-NTSXBZR3.js → workflow-QRCK7U6G.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-E6K4PAID.js.map +0 -1
- package/dist/chunk-IJTIWAY6.js.map +0 -1
- package/dist/sandbox-OIC6CDHG.js.map +0 -1
- package/dist/skill-G2654GLA.js.map +0 -1
- /package/dist/{landing-NJZS5JDT.js.map → landing-3TY7TLVZ.js.map} +0 -0
- /package/dist/{workflow-NTSXBZR3.js.map → workflow-QRCK7U6G.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sandbox.ts","../../content/src/sandbox-v2-guide/overview.txt","../../content/src/sandbox-v2-guide/definition.txt","../../content/src/sandbox-v2-guide/environments.txt","../../content/src/sandbox-v2-guide/seeds-and-actions.txt","../../content/src/sandbox-v2-guide/personas.txt","../../content/src/sandbox-v2-guide/authentication.txt","../../content/src/sandbox-v2-guide/env.txt","../../content/src/sandbox-v2-guide/emulation.txt","../../content/src/sandbox-v2-guide/object-storage.txt","../../content/src/sandbox-v2-guide/ports.txt","../../content/src/sandbox-v2-guide/verify.txt","../../content/src/sandbox-v2-guide/cli.txt","../../content/src/sandbox-v2-guide/upload.txt","../../content/src/sandbox-v2-guide/index.ts","../src/sandbox-emulation.ts","../src/sandbox-logs.ts","../src/sandbox-preview.ts","../src/sandbox-v2.ts","../../sandbox-helper/src/manifest.ts","../../sandbox-helper/src/object-storage.ts","../src/sandbox-push.ts","../../sandbox-helper/src/image-archive.ts"],"sourcesContent":["/**\n * CLI Sandbox Management\n *\n * Create, provision, and manage sandbox templates and instances.\n * Drive template authoring through artifact uploads, command execution,\n * and diagnostics — designed for Claude Code to iterate on sandbox setup.\n */\n\nimport process from 'node:process';\nimport { readFileSync } from 'node:fs';\nimport { resolveConfig, getArgValue, hasFlag } from './auth.js';\nimport { apiRequest, getArgValues, getGuideSectionArg, selectProperty } from './cli-helpers.js';\nimport { apiFetch, isTerminalFailure, exitOnFailure } from './api-fetch.js';\nimport { SANDBOX_GUIDE_SECTIONS, printGuide } from '@chatsdet/content';\nimport { SANDBOX_V2_GUIDE_SECTIONS } from '@chatsdet/content/sandbox-v2-guide';\nimport {\n INSTANCE_SIZES,\n INSTANCE_SIZE_SPEC,\n SANDBOX_EGRESS_MODES,\n parseInstanceSize,\n type InstanceSize,\n type SandboxEgressAllowlist,\n type SandboxEgressMode,\n} from '@chatsdet/types';\nimport { getPluginIds } from '@chatsdet/emulation/plugins';\nimport { handleEmulation } from './sandbox-emulation.js';\nimport { handleLogs } from './sandbox-logs.js';\nimport { handlePreview } from './sandbox-preview.js';\nimport {\n handleSandboxClaim,\n handleSandboxConnect,\n handleSandboxDestroy,\n handleSandboxEnvs,\n handleSandboxRun,\n handleSandboxShow,\n handleSandboxValidate,\n handleSandboxVerifyEnv,\n rejectUnknownSandboxV2Args,\n} from './sandbox-v2.js';\nimport { handleSandboxPush } from './sandbox-push.js';\nimport { writeRawOutput } from './brand.js';\nexport {\n withEmulationMaster,\n withPluginConfig,\n withPluginEnabled,\n withoutPlugin,\n} from './sandbox-emulation.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Template = {\n id: string;\n name: string;\n propertyId: string | null;\n repositoryFullName: string | null;\n branch: string;\n subdirectory: string | null;\n status: string;\n checklistProfile: string | null;\n configSchema: Array<{ key: string; description?: string; required: boolean; sensitive: boolean }> | null;\n configDefaults: Record<string, string> | null;\n configuredSensitiveKeys?: string[];\n configuredSensitiveAutoConfigKeys?: string[];\n createdAt: string;\n updatedAt: string;\n};\n\ntype GitHubRepo = {\n id: string;\n repoFullName: string;\n repoName: string;\n defaultBranch: string;\n rootDir: string | null;\n agentHint: string | null;\n};\n\ntype Instance = {\n id: string;\n templateId: string;\n name: string;\n status: string;\n hostUrl: string | null;\n isPrivate: boolean;\n expiresAt: string | null;\n createdAt: string;\n /**\n * What the instance is for. `flow_authoring` is the org-shared instance the\n * Flow Designer auto-spins and that sandbox workflow runs reuse — surfaced in\n * `sandbox list` so agents can find/warm it.\n */\n purpose?: string | null;\n};\n\nexport const parseEgressMode = (value: string | undefined): SandboxEgressMode => {\n if (!value) return 'off';\n if ((SANDBOX_EGRESS_MODES as readonly string[]).includes(value)) {\n return value as SandboxEgressMode;\n }\n console.error(\n `Error: invalid --egress-mode '${value}'. Must be one of: ${SANDBOX_EGRESS_MODES.join(', ')}`\n );\n process.exit(1);\n};\n\nexport const parseEgressAllowlist = (argv: string[]): SandboxEgressAllowlist => {\n const rawEntries = getArgValues(argv, '--egress-allow')\n .flatMap((value) => value.split(','))\n .map((value) => value.trim())\n .filter(Boolean);\n\n return rawEntries.map((entry) => {\n const separator = entry.indexOf(':');\n if (separator <= 0) {\n console.error(\n `Error: invalid --egress-allow '${entry}'. Use domain:example.com or cidr:203.0.113.0/24`\n );\n process.exit(1);\n }\n const kind = entry.slice(0, separator);\n const value = entry.slice(separator + 1);\n if (kind !== 'domain' && kind !== 'cidr') {\n console.error(`Error: invalid --egress-allow kind '${kind}'. Must be domain or cidr.`);\n process.exit(1);\n }\n return { kind, value };\n });\n};\n\ntype Artifact = {\n id: string;\n taskId: string;\n artifactType: string;\n filename: string;\n content?: string;\n createdAt: string;\n updatedAt: string;\n};\n\ntype ChecklistItem = {\n id: string;\n label: string;\n description: string;\n dependsOn: string[];\n producesArtifacts: string[];\n agentAutomatable: boolean;\n itemStatus: { status: string; startedAt?: string; completedAt?: string; summary?: string; error?: string };\n actionable: boolean;\n artifacts: Array<{ artifactType: string; filename: string }>;\n};\n\ntype CommandOrError =\n | { stdout: string; stderr: string; exitCode: number }\n | { error: string; code?: string };\n\ntype DiagnosticsResult = {\n instance: Record<string, unknown>;\n lifecycle?: {\n databaseStatus: string;\n poolStatus: string | null;\n ec2InstanceState: string | null;\n live: boolean;\n message: string | null;\n };\n docker: CommandOrError;\n git: CommandOrError;\n disk: CommandOrError;\n resources?: CommandOrError;\n agentHealth: CommandOrError;\n appHealth: CommandOrError;\n provision?: {\n phase: string;\n jobStatus: string;\n startedAt?: string;\n elapsedSeconds: number | null;\n lastError?: string;\n };\n};\n\ntype ServiceRoute = { service: string; route: string; port: string };\n\nasync function fetchInstanceRoutes(apiUrl: string, token: string, instanceId: string): Promise<ServiceRoute[]> {\n const res = await apiRequest<{ ok: boolean; error?: string; data: { routes: ServiceRoute[] } }>(\n apiUrl, token, 'GET', `/org/environments/instances/${instanceId}/routes`,\n undefined,\n { timeoutMs: 30_000 }\n );\n return res.ok ? res.data.routes : [];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst STATUS_ICONS: Record<string, string> = {\n completed: '[done]',\n running: '[....]',\n pending: '[ ]',\n failed: '[FAIL]',\n skipped: '[skip]',\n};\n\nfunction formatDate(iso: string): string {\n return new Date(iso).toLocaleString();\n}\n\nfunction pad(s: string, n: number): string {\n return s.padEnd(n);\n}\n\nconst INSTANCE_SIZE_HELP_LINES = INSTANCE_SIZES.map((size) => {\n const spec = INSTANCE_SIZE_SPEC[size];\n return ` ${pad(size, 8)} ${spec.ec2InstanceType} · ${spec.vcpu} vCPU · ${spec.memoryMiB / 1024} GiB`;\n});\n\n// ---------------------------------------------------------------------------\n// Help\n// ---------------------------------------------------------------------------\n\nfunction printHelp(): void {\n console.log(\n [\n 'cofactor sandbox: Live, seeded copies of an application (Sandboxes v2), plus the legacy v1 templates and instances',\n '',\n 'Sandboxes v2 (start here: an application\\'s cofactor.sandbox.yaml — see \"cofactor sandbox guide v2\"):',\n ' validate [file] [--upload] Offline schema + cross-check (default ./cofactor.sandbox.yaml; --upload: for pushed images)',\n ' connect --application <a> --repo <owner/name> [--dir <path>] Build the application\\'s sandboxes from a GitHub repo',\n ' push --application <a> <image> [options] Push a built image as the application\\'s next version (see \"Push options\" below)',\n ' envs --application <a> The application\\'s delivery, version, environments, actions, operations',\n ' claim --application <a> [options] Claim a sandbox (see \"Claim options\" below)',\n ' show <id> Status, URLs, logins, seed outputs, actions and operations',\n ' run <id> <action|plugin.op> [options] Run a repo action or emulator operation (see \"Run options\" below)',\n ' destroy <id> Tear a claimed sandbox down',\n ' verify-env --application <a> [options] Claim/wait/destroy every environment of the current version in turn',\n ' preview --application <a> --pr <n> A pull request\\'s preview and its logins',\n ' guide v2 [topic] Show the Sandboxes v2 guide',\n ` topics: ${SANDBOX_V2_GUIDE_SECTIONS.map((section) => section.slug).join(', ')}`,\n ' (pushing a built image: start with `guide v2 upload`)',\n '',\n 'Push options (Sandboxes v2):',\n ' --definition <file> The cofactor.sandbox.yaml to push with the image (default: ./cofactor.sandbox.yaml)',\n ' --tag <tag> Name this push (repeatable); a tag always names the newest push carrying it',\n ' --wait Block until Cofactor has imported (booted) the image',\n '',\n 'Claim options (Sandboxes v2):',\n ' --environment <name> Environment to claim (default: \"default\")',\n ' --at <version> A commit SHA, or a push id or tag (default: the default branch head / newest push)',\n ' --wait Block until the sandbox is ready or failed, then print `show`',\n '',\n 'Run options (Sandboxes v2):',\n ' --input <key>=<value> Set an input (repeatable), coerced to the action/operation\\'s declared type',\n ' --json-input \\'{...}\\' Full input as JSON (overrides --input)',\n '',\n 'Verify-env options (Sandboxes v2):',\n ' --environment <name> Verify only this environment (default: every environment in the definition)',\n '',\n 'Sandboxes v1 (legacy templates and instances):',\n ' guide [section] Show the v1 sandbox authoring guide (v2: `guide v2` above)',\n ' list List templates and instances',\n ' template [id] Show template details',\n ' repo list --template <id> List repos linked to a template',\n ' repo add --template <id> --property-github-repo-id <uuid> [--role <r>] [--spec <r=owner/name@branch:subdir>]',\n ' repo update --template <id> --link-id <uuid> [--role <r>] [--branch <b>] [--subdirectory <s>] [--service-name <n>]',\n ' repo remove --template <id> --link-id <uuid>',\n ' checklist <template-id> Show checklist state',\n ' checklist skip <template-id> <item-id> Skip a checklist item',\n ' checklist complete <template-id> <item-id> [options] Mark item complete from CLI',\n ' artifacts <template-id> List artifacts',\n ' artifact get <template-id> <type> Get artifact content',\n ' artifact create <template-id> <type> <file> Upload artifact (use --path for file type)',\n ' create [property] [options] Create template from property (triggers provisioning)',\n ' provision <template-id> [options] Create a dedicated instance from a verified template',\n ' warm <template-id> [--wait] [--timeout <sec>] Warm the org-shared flow-authoring instance',\n ' (the one the flow designer auto-spins; sandbox runs reuse it)',\n ' verify <template-id> Verify an authoring template (provision + probe + flip to verified)',\n ' set-size <template-id> <size> Set EC2 size for next provision (see \"Instance sizes\" below)',\n ' pool set-size <template-id> <count> Set the warm stopped-pool size for a template (0 = off)',\n ' build <instance-id> Build and start app services on sandbox',\n ' run-command <instance-id> <command...> Execute shell command on sandbox',\n ' --service <name> Run inside a Docker container instead of on the host VM',\n ' diagnostics <instance-id> Show sandbox diagnostics',\n ' logs <instance-id> [options] Show container logs (live if running, else the captured failure snapshot)',\n ' terminate <instance-id> Terminate an instance',\n ' config list <template-id> Show config schema and defaults',\n ' config set <template-id> <key> <value> Set a config default value',\n ' config unset <template-id> <key> Remove a config default value',\n ' emulation get <template-id> Show emulation config + available plugins',\n ' emulation enable/disable <template-id> [pluginId] Toggle master switch (or one plugin)',\n ' emulation set <template-id> <pluginId> [opts] Configure a plugin entry',\n ' emulation remove <template-id> <pluginId> Drop a plugin entry',\n ' action <list|get|create|delete|execute|executions> Manage template actions (see \"Action sub-commands\" below)',\n ' delete <template-id> Delete template and all instances',\n '',\n 'Create options:',\n ' --property <name|id> Property (prompted if omitted, auto-selected if only one)',\n ' --branch <branch> Branch (default: from linked repo)',\n ' --name <name> Template name (default: repo name)',\n ' --subdirectory <path> Subdirectory (default: from linked repo rootDir)',\n ' --size <size> EC2 size for first authoring sandbox (default: medium; see \"Instance sizes\" below)',\n '',\n 'Provision options:',\n ' --name <name> Instance name',\n ' --ttl <hours> Time to live in hours (default: 8)',\n ' --with-agent Start with sandbox agent',\n ' --private Skip public ALB routing',\n ' --egress-mode <mode> off|observe (observe logs unmatched egress only; default: off)',\n ' --egress-allow <kind:value> Repeat or comma-separate domain:/cidr: entries',\n '',\n 'Artifact get options:',\n ' --output <file> Write to file instead of stdout',\n '',\n 'Logs options:',\n ' --service <name> Service name (default: all services; live path only)',\n ' --tail <n> Number of lines (default: 200; live path only)',\n ' --failure Force the captured failure snapshot (full logs + deploy.log)',\n ' Works for failed/terminated instances after the host is gone.',\n ' Running instances auto-use live logs; failed ones auto-fall back',\n ' to the snapshot when the live tail can\\'t run.',\n '',\n 'Guide sections (v1):',\n ' goal, research, artifacts, actions, verify, checklist',\n '',\n 'Instance sizes:',\n ...INSTANCE_SIZE_HELP_LINES,\n '',\n 'Action sub-commands:',\n ' action list <template-id> List actions for a template',\n ' action get <template-id> <slug> Show action definition + script',\n ' action create <template-id> <slug> [opts] Create or update an action',\n ' action delete <template-id> <slug> Delete an action',\n ' action execute <instance-id> <slug> [opts] Execute action on a running instance',\n ' action executions <instance-id> [opts] List execution history',\n ' action execution get <inst-id> <exec-id> Show one execution (advances a background run)',\n ' action execution cancel <inst-id> <exec-id> Stop a background execution',\n '',\n 'Action execute options:',\n ' --param key=value Set an input param (repeatable)',\n ' --params-file <file> JSON file of input params (--param overrides)',\n ' --async Return once the script is running, without waiting.',\n ' Required for anything longer than the 300s',\n ' synchronous ceiling. Poll `action execution get`',\n ' to reach a terminal status.',\n ' --timeout-ms <ms> Remote ceiling for an --async run (default 1h, max 48h)',\n '',\n 'Action create options:',\n ' --label <label> Display name (default: slug → Title Case)',\n ' --description <text> Description',\n ' --icon <icon> Phosphor icon name',\n ' --script <file> Path to script file (bash)',\n ' --input-schema <file> Path to JSON Schema file for input params',\n ' --output-schema <file> Path to JSON Schema file for output',\n ' --credential-output-config <file> JSON file with ActionCredentialOutputConfig (auto-creates a Canary credential from the action result).',\n ' --output-config <file> JSON file with an array of output slots the action hands back (test-results / artifacts-archive).',\n ' Supports `variableMapping` (persona var name → $output.* token) to fill the persona\\'s declared variables ($var.USER.<NAME>)',\n ' --credential-template <id> Credential template this action provisions; login nodes using it auto-provision',\n ' --provisioning-mode <shared|unique> shared = provision once / reused (default); unique = one credential per run (script must accept a runId input param)',\n ' --run-on-provision Base seed: run automatically once per provisioned instance (not login-triggered). Its --output-schema keys are exposed to flows as $var.SEED.<key>',\n ' --provision-order <n> Order for multiple base seeds on a template (ascending; unset runs first)',\n ' --timeout <ms> Timeout in ms (default: 55000)',\n ' --encrypt-script Encrypt script content at rest',\n '',\n 'Action execute options:',\n ' --param <key>=<value> Set parameter (repeatable)',\n ' --params-file <file> JSON file with all params',\n '',\n 'Emulation sub-commands (third-party service emulation: Slack/Jira/Google-OAuth/email):',\n ' emulation get <template-id> Master switch, configured plugins, available catalog',\n ' emulation enable <template-id> [pluginId] Turn on emulation (or enable a single plugin)',\n ' emulation disable <template-id> [pluginId] Turn off emulation (or disable a single plugin)',\n ' emulation set <template-id> <pluginId> [opts] Create/update a plugin entry (does not flip master switch)',\n ' emulation remove <template-id> <pluginId> Remove a plugin entry',\n ` Available plugins: ${getPluginIds().join(', ')}`,\n '',\n 'Emulation set options:',\n ' --config <file> JSON file with plugin-specific config (e.g. { \"tenant\": \"acme\" })',\n ' --fixture <file> JSON file with seed/fixture data for the plugin',\n ' --env-mapping <file> JSON file mapping logical env-contract keys to the app env var(s)',\n '',\n 'Global options:',\n ' --json Output raw JSON',\n ' --env <env> Environment (prod, dev, local)',\n ' -h, --help Show help',\n ].join('\\n')\n );\n}\n\n// ---------------------------------------------------------------------------\n// Sub-command handlers\n// ---------------------------------------------------------------------------\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n\n const [templatesRes, instancesRes] = await Promise.all([\n apiRequest<{ ok: boolean; error?: string; data: Template[] }>(\n apiUrl, token, 'GET', '/org/environments/templates'\n ),\n apiRequest<{ ok: boolean; error?: string; data: Instance[] }>(\n apiUrl, token, 'GET', '/org/environments/instances'\n ),\n ]);\n\n if (!templatesRes.ok) {\n console.error(`Error: ${templatesRes.error}`);\n process.exit(1);\n }\n if (!instancesRes.ok) {\n console.error(`Error loading instances: ${instancesRes.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify({ templates: templatesRes.data, instances: instancesRes.data }, null, 2));\n return;\n }\n\n const templates = templatesRes.data ?? [];\n const instances = instancesRes.data ?? [];\n\n console.log('Templates:');\n if (templates.length === 0) {\n console.log(' (none)');\n } else {\n console.log(` ${pad('ID', 38)} ${pad('Name', 24)} ${pad('Status', 14)} ${pad('Profile', 12)} Repository`);\n for (const t of templates) {\n console.log(` ${pad(t.id, 38)} ${pad(t.name, 24)} ${pad(t.status, 14)} ${pad(t.checklistProfile ?? '-', 12)} ${t.repositoryFullName ? `${t.repositoryFullName}:${t.branch}` : '(no repository)'}`);\n }\n }\n\n console.log('');\n console.log('Instances:');\n if (instances.length === 0) {\n console.log(' (none)');\n } else {\n console.log(` ${pad('ID', 38)} ${pad('Status', 14)} ${pad('Purpose', 16)} ${pad('Access', 10)} ${pad('Template', 38)} Expires`);\n for (const i of instances) {\n const access = i.isPrivate ? 'private' : 'public';\n // The org-shared flow-authoring instance is the one workflow runs reuse —\n // flag it so it's easy to spot (and warm via `sandbox warm <template>`).\n const purpose =\n i.purpose === 'flow_authoring' ? 'flow_authoring*' : (i.purpose ?? '-');\n console.log(` ${pad(i.id, 38)} ${pad(i.status, 14)} ${pad(purpose, 16)} ${pad(access, 10)} ${pad(i.templateId, 38)} ${i.expiresAt ? formatDate(i.expiresAt) : '-'}`);\n }\n console.log('');\n console.log(' * flow_authoring = org-shared instance the flow designer auto-spins and');\n console.log(' sandbox workflow runs reuse. Warm it with: canary sandbox warm <template-id>');\n }\n}\n\nasync function handleTemplate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = argv.filter((a) => !a.startsWith('-'));\n const json = hasFlag(argv, '--json');\n\n if (!id) {\n await handleList(argv, apiUrl, token);\n return;\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: Template }>(\n apiUrl, token, 'GET', `/org/environments/templates/${id}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const t = res.data;\n console.log(`Template: ${t.name}`);\n console.log(` ID: ${t.id}`);\n console.log(` Repository: ${t.repositoryFullName ? `${t.repositoryFullName}:${t.branch}` : '(none)'}`);\n if (t.subdirectory) console.log(` Subdir: ${t.subdirectory}`);\n console.log(` Status: ${t.status}`);\n console.log(` Profile: ${t.checklistProfile ?? 'full-stack (default)'}`);\n console.log(` Created: ${formatDate(t.createdAt)}`);\n console.log(` Updated: ${formatDate(t.updatedAt)}`);\n\n if (t.configSchema && t.configSchema.length > 0) {\n console.log('');\n console.log(' Config Schema:');\n for (const c of t.configSchema) {\n const flags = [c.required ? 'required' : 'optional', c.sensitive ? 'sensitive' : ''].filter(Boolean).join(', ');\n writeRawOutput(` ${c.key} (${flags})${c.description ? ` — ${c.description}` : ''}`);\n }\n }\n}\n\nasync function handleArtifacts(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [templateId] = argv.filter((a) => !a.startsWith('-'));\n const json = hasFlag(argv, '--json');\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox artifacts <template-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: Artifact[] }>(\n apiUrl, token, 'GET', `/org/environments/templates/${templateId}/artifacts`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const artifacts = res.data ?? [];\n if (artifacts.length === 0) {\n console.log('No artifacts found.');\n return;\n }\n\n console.log(`${pad('Type', 22)} ${pad('Filename', 30)} ${pad('Task', 24)} Created`);\n for (const a of artifacts) {\n console.log(`${pad(a.artifactType, 22)} ${pad(a.filename, 30)} ${pad(a.taskId, 24)} ${formatDate(a.createdAt)}`);\n }\n}\n\nasync function handleChecklist(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const json = hasFlag(argv, '--json');\n\n // Handle checklist sub-commands: skip, complete\n if (positional[0] === 'skip') {\n await handleChecklistSkip(argv.slice(1), apiUrl, token);\n return;\n }\n if (positional[0] === 'complete') {\n await handleChecklistComplete(argv.slice(1), apiUrl, token);\n return;\n }\n\n const templateId = positional[0];\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox checklist <template-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: ChecklistItem[] }>(\n apiUrl, token, 'GET', `/org/environments/templates/${templateId}/checklist`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const items = res.data ?? [];\n if (items.length === 0) {\n console.log('No checklist items.');\n return;\n }\n\n console.log('Checklist:');\n for (const item of items) {\n const icon = STATUS_ICONS[item.itemStatus.status] ?? '[????]';\n const actionable = item.actionable ? ' *' : '';\n console.log(` ${icon} ${item.label}${actionable}`);\n if (item.itemStatus.summary) {\n console.log(` ${item.itemStatus.summary}`);\n }\n if (item.itemStatus.error) {\n console.log(` ERROR: ${item.itemStatus.error}`);\n }\n if (item.artifacts.length > 0) {\n console.log(` Artifacts: ${item.artifacts.map((a) => a.artifactType).join(', ')}`);\n }\n }\n console.log('');\n console.log(' * = actionable (deps met, ready to run)');\n}\n\nasync function handleChecklistSkip(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const itemId = positional[1];\n\n if (!templateId || !itemId) {\n console.error('Error: both template ID and item ID required. Usage: canary sandbox checklist skip <template-id> <item-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl, token, 'POST', `/org/environments/templates/${templateId}/checklist/${itemId}/skip`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Skipped checklist item: ${itemId}`);\n}\n\nasync function handleChecklistComplete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const itemId = positional[1];\n\n const json = hasFlag(argv, '--json');\n\n if (!templateId || !itemId) {\n console.error('Error: both template ID and item ID required.');\n console.error('Usage: canary sandbox checklist complete <template-id> <item-id> [--status success|failed] [--summary \"...\"]');\n process.exit(1);\n }\n\n const status = getArgValue(argv, '--status') ?? 'success';\n const summary = getArgValue(argv, '--summary');\n const configSchemaArg = getArgValue(argv, '--config-schema');\n const healthCheckPath = getArgValue(argv, '--health-check-path');\n\n let configSchema: unknown;\n if (configSchemaArg) {\n // Accept inline JSON or a file path\n try {\n configSchema = JSON.parse(configSchemaArg);\n } catch {\n try {\n configSchema = JSON.parse(readFileSync(configSchemaArg, 'utf-8'));\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(`Error reading config schema from \"${configSchemaArg}\": ${msg}`);\n process.exit(1);\n }\n }\n }\n\n const body: Record<string, unknown> = {\n status: status === 'failed' ? 'failed' : 'completed',\n ...(summary ? { summary } : {}),\n ...(configSchema ? { configSchema } : {}),\n ...(healthCheckPath ? { healthCheckPath } : {}),\n };\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: { itemId: string; status: string } }>(\n apiUrl, token, 'POST', `/org/environments/templates/${templateId}/checklist/${itemId}/complete`, body\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n console.log(`Completed checklist item: ${itemId} (${res.data?.status})`);\n}\n\nasync function handleConfigList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox config list <template-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: Template }>(\n apiUrl, token, 'GET', `/org/environments/templates/${templateId}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n const t = res.data;\n const schema = t.configSchema ?? [];\n const defaults = t.configDefaults ?? {};\n const configuredSensitiveKeys = t.configuredSensitiveKeys ?? [];\n const configuredSensitiveKeySet = new Set(configuredSensitiveKeys);\n\n if (json) {\n writeRawOutput(JSON.stringify(\n { configSchema: schema, configDefaults: defaults, configuredSensitiveKeys },\n null,\n 2\n )\n );\n return;\n }\n\n if (schema.length === 0) {\n console.log('No config schema defined for this template.');\n return;\n }\n\n console.log('Config Schema:');\n for (const item of schema) {\n const value = defaults[item.key];\n const display = item.sensitive\n ? configuredSensitiveKeySet.has(item.key)\n ? '•••••••• (configured)'\n : '(not set)'\n : (value ?? '(not set)');\n const required = item.required ? ' (required)' : '';\n const sensitive = item.sensitive ? ' [sensitive]' : '';\n console.log(` ${item.key}${required}${sensitive}`);\n if (item.description) writeRawOutput(` ${item.description}`);\n console.log(` Value: ${display}`);\n }\n}\n\nexport const configSetPayload = (key: string, value: string) => ({\n configDefaults: { [key]: value },\n unsetKeys: [] as string[],\n});\n\nexport const configUnsetPayload = (key: string) => ({\n configDefaults: {} as Record<string, string>,\n unsetKeys: [key],\n});\n\nasync function handleConfigSet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const fromStdin = hasFlag(argv, '--value-stdin');\n const templateId = positional[0];\n const key = positional[1];\n let value = positional[2];\n\n if (!templateId || !key) {\n console.error('Error: template ID and key required.');\n console.error('Usage: canary sandbox config set <template-id> <key> <value>');\n console.error(' For sensitive values: echo \"sk-ant-...\" | canary sandbox config set <template-id> <key> --value-stdin');\n process.exit(1);\n }\n\n if (fromStdin) {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(chunk as Buffer);\n value = Buffer.concat(chunks).toString('utf-8').trim();\n }\n\n if (value === undefined) {\n console.error('Error: value required. Pass as argument or use --value-stdin');\n process.exit(1);\n }\n\n // The server may take longer than 30s to acknowledge config writes\n // (encrypted columns, audit log). Even when it does the write succeeds —\n // bump the CLI ceiling so the user sees the success rather than a\n // misleading timeout.\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl, token, 'PATCH', `/org/environments/templates/${templateId}/config`, {\n ...configSetPayload(key, value),\n },\n { timeoutMs: 90_000 }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Config set: ${key}`);\n}\n\nasync function handleConfigUnset(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [templateId, key] = argv.filter((arg) => !arg.startsWith('-'));\n if (!templateId || !key) {\n console.error('Usage: canary sandbox config unset <template-id> <key>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'PATCH',\n `/org/environments/templates/${templateId}/config`,\n configUnsetPayload(key),\n { timeoutMs: 90_000 }\n );\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Config unset: ${key}`);\n}\n\nasync function handleSetSize(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [templateId, sizeArg] = argv.filter((a) => !a.startsWith('-'));\n\n if (!templateId || !sizeArg) {\n console.error('Error: template ID and size required.');\n console.error(`Usage: canary sandbox set-size <template-id> <${INSTANCE_SIZES.join('|')}>`);\n process.exit(1);\n }\n\n const size = parseInstanceSize(sizeArg);\n if (!size) {\n console.error(`Error: invalid size '${sizeArg}'. Must be one of: ${INSTANCE_SIZES.join(', ')}`);\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; instanceSize?: InstanceSize }>(\n apiUrl,\n token,\n 'PATCH',\n `/org/environments/templates/${templateId}/instance-size`,\n { instanceSize: size }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Instance size set to ${size}`);\n}\n\nasync function handlePoolSetSize(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [templateId, countArg] = argv.filter((a) => !a.startsWith('-'));\n\n if (!templateId || countArg === undefined) {\n console.error('Error: template ID and count required.');\n console.error('Usage: canary sandbox pool set-size <template-id> <count>');\n process.exit(1);\n }\n\n // Strict decimal-integer parse — reject '1e2', '0x10', '3.5', etc. that\n // Number() would silently accept, so the count the operator typed is exactly\n // what the server receives.\n const poolMinReady = /^\\d+$/.test(countArg) ? Number(countArg) : Number.NaN;\n if (!Number.isInteger(poolMinReady)) {\n console.error(`Error: invalid count '${countArg}'. Must be a non-negative integer.`);\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; poolMinReady?: number }>(\n apiUrl,\n token,\n 'PATCH',\n `/org/environments/templates/${templateId}/pool-min-ready`,\n { poolMinReady }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Warm stopped-pool size set to ${poolMinReady}`);\n}\n\nasync function handleCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const json = hasFlag(argv, '--json');\n\n // Resolve property — positional arg or --property flag, prompted if omitted\n const propertyHint = positional[0] ?? getArgValue(argv, '--property');\n const property = await selectProperty(apiUrl, token, propertyHint ?? undefined);\n if (!property) process.exit(1);\n\n // Fetch linked repos for the property\n const reposRes = await apiRequest<{ ok: boolean; error?: string; items: GitHubRepo[] }>(\n apiUrl, token, 'GET', `/org/properties/${property.id}/github-repos`\n );\n if (!reposRes.ok || !reposRes.items?.length) {\n console.error(`No GitHub repos linked to property \"${property.name}\". Link one first in the web app.`);\n process.exit(1);\n }\n\n const linkedRepo = reposRes.items[0];\n const branch = getArgValue(argv, '--branch') ?? linkedRepo.defaultBranch ?? 'main';\n const name = getArgValue(argv, '--name') ?? linkedRepo.repoName ?? linkedRepo.repoFullName.split('/').pop() ?? property.name;\n const subdirectory = getArgValue(argv, '--subdirectory') ?? linkedRepo.rootDir ?? undefined;\n\n const sizeArg = getArgValue(argv, '--size');\n const instanceSize = sizeArg ? parseInstanceSize(sizeArg) : undefined;\n if (sizeArg && !instanceSize) {\n console.error(`Error: invalid --size '${sizeArg}'. Must be one of: ${INSTANCE_SIZES.join(', ')}`);\n process.exit(1);\n }\n\n const body: Record<string, unknown> = {\n name,\n propertyId: property.id,\n repositoryFullName: linkedRepo.repoFullName,\n branch,\n };\n if (subdirectory) body.subdirectory = subdirectory;\n if (linkedRepo.agentHint) body.hints = linkedRepo.agentHint;\n if (instanceSize) body.instanceSize = instanceSize;\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: Template }>(\n apiUrl, token, 'POST', '/org/environments/templates', body\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n console.log(`Template created: ${res.data.id}`);\n console.log(` Property: ${property.name}`);\n console.log(` Repository: ${res.data.repositoryFullName ? `${res.data.repositoryFullName}:${res.data.branch}` : '(none)'}`);\n console.log(` Status: ${res.data.status}`);\n console.log('');\n console.log('Authoring job enqueued. Poll status with:');\n console.log(` canary sandbox template ${res.data.id}`);\n}\n\nasync function handleProvision(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox provision <template-id> [--ttl 8]');\n process.exit(1);\n }\n\n const name = getArgValue(argv, '--name') ?? `instance-${Date.now()}`;\n const ttl = parseInt(getArgValue(argv, '--ttl') ?? '8', 10);\n const withAgent = hasFlag(argv, '--with-agent');\n const isPrivate = hasFlag(argv, '--private');\n const egressMode = parseEgressMode(getArgValue(argv, '--egress-mode'));\n const egressAllowlist = parseEgressAllowlist(argv);\n\n const body: Record<string, unknown> = {\n templateId,\n name,\n expiresInHours: ttl,\n userConfig: {},\n withAgent,\n isPrivate,\n egressMode,\n egressAllowlist,\n };\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: Instance }>(\n apiUrl, token, 'POST', '/org/environments/instances', body\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n console.log(`Instance created: ${res.data.id}`);\n console.log(` Status: ${res.data.status}`);\n if (isPrivate) console.log(` Access: private (no public URL)`);\n if (egressMode !== 'off') {\n console.log(` Egress: ${egressMode} (${egressAllowlist.length} allowlist entries)`);\n }\n console.log(` Expires: ${res.data.expiresAt ? formatDate(res.data.expiresAt) : '-'}`);\n}\n\ntype AuthoringInstance = {\n instanceId: string;\n status: string;\n url: string | null;\n expiresAt: string | null;\n createdAt: string | null;\n created: boolean;\n};\n\n/**\n * Warm (get-or-create) the org-shared flow-authoring instance for a template —\n * the same instance the Flow Designer auto-spins and that a sandbox workflow run\n * reuses. Pre-warming avoids the first run blocking on a cold provision.\n *\n * `--wait` polls the idempotent ensure endpoint (safe: it reuses the live\n * instance and just slides the TTL) until the instance is running, fails, or\n * the timeout elapses.\n */\nasync function handleWarm(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const json = hasFlag(argv, '--json');\n const wait = hasFlag(argv, '--wait');\n const timeoutSec = parseInt(getArgValue(argv, '--timeout') ?? '600', 10);\n\n if (!templateId) {\n console.error(\n 'Error: template ID required. Usage: canary sandbox warm <template-id> [--wait] [--timeout <sec>]'\n );\n console.error('Find the template id with: canary sandbox list');\n process.exit(1);\n }\n\n const ensure = (): Promise<{ ok: boolean; error?: string; data?: AuthoringInstance }> =>\n apiRequest<{ ok: boolean; error?: string; data?: AuthoringInstance }>(\n apiUrl,\n token,\n 'POST',\n `/org/environments/templates/${templateId}/authoring-instance`\n );\n\n const first = await ensure();\n if (!first.ok || !first.data) {\n if (json) {\n writeRawOutput(JSON.stringify(first, null, 2));\n } else {\n console.error(`Error: ${first.error ?? 'Failed to warm sandbox'}`);\n }\n process.exit(1);\n }\n\n const terminalOk = new Set(['running']);\n const terminalFail = new Set(['failed', 'terminated', 'stopping']);\n\n let current = first.data;\n if (wait && !terminalOk.has(current.status) && !terminalFail.has(current.status)) {\n const deadline = Date.now() + timeoutSec * 1000;\n if (!json) console.log(`Warming flow-authoring sandbox (${current.status})...`);\n while (Date.now() < deadline) {\n await new Promise((r) => setTimeout(r, 5000));\n const poll = await ensure();\n if (!poll.ok || !poll.data) {\n console.error(`Error polling sandbox: ${poll.error ?? 'Unknown error'}`);\n process.exit(1);\n }\n current = poll.data;\n if (terminalOk.has(current.status) || terminalFail.has(current.status)) break;\n }\n }\n\n if (json) {\n writeRawOutput(JSON.stringify({ ok: true, data: current }, null, 2));\n } else {\n console.log(`Sandbox ${current.created ? 'created' : 'reused'}: ${current.instanceId}`);\n console.log(` Status: ${current.status}`);\n if (current.url) writeRawOutput(` URL: ${current.url}`);\n if (current.expiresAt) console.log(` Expires: ${formatDate(current.expiresAt)}`);\n if (current.status === 'running') {\n console.log('\\nReady. Run a workflow against it with:');\n console.log(' canary workflow run <workflowId> [--environment <name|id>] --stream');\n } else if (wait && terminalFail.has(current.status)) {\n console.log('\\nSandbox did not reach a ready state.');\n } else if (wait) {\n console.log('\\nTimed out waiting for the sandbox to become ready. Re-run to keep waiting.');\n } else {\n console.log('\\nWarming in the background. Poll with: canary sandbox warm ' + templateId + ' --wait');\n }\n }\n\n if (terminalFail.has(current.status)) process.exit(1);\n if (wait && current.status !== 'running') process.exit(1);\n}\n\nexport interface ContainerLine {\n name: string;\n status: string;\n state: 'up-healthy' | 'up' | 'up-unhealthy' | 'restarting' | 'exited' | 'other';\n}\n\n/**\n * Parse `docker ps --format '{{.Names}}\\t{{.Status}}\\t{{.Ports}}'` output\n * into a structured list. State classification is coarse — the verify loop\n * only needs to know \"is this container healthy / exited / restart-looping\".\n */\nexport function parseDockerPs(stdout: string): ContainerLine[] {\n const lines = stdout.split('\\n').map((l) => l.trim()).filter(Boolean);\n return lines.map((line) => {\n const [name = '', status = ''] = line.split('\\t');\n const lower = status.toLowerCase();\n let state: ContainerLine['state'] = 'other';\n if (lower.startsWith('restarting')) state = 'restarting';\n else if (lower.startsWith('exited')) state = 'exited';\n else if (lower.startsWith('up') && lower.includes('unhealthy')) state = 'up-unhealthy';\n else if (lower.startsWith('up') && lower.includes('healthy')) state = 'up-healthy';\n else if (lower.startsWith('up')) state = 'up';\n return { name, status, state };\n });\n}\n\n/** Containers that aren't cleanly up. Used to decide what to dump logs for. */\nexport function isBrokenContainer(c: ContainerLine): boolean {\n return c.state === 'restarting' || c.state === 'exited' || c.state === 'up-unhealthy';\n}\n\n/**\n * Map a container name back to its compose service.\n *\n * docker-compose names containers `<project>-<service>-<replica>`. The\n * sandbox platform sets the project to `workspace` via\n * `--project-directory /workspace` (see `SANDBOX_COMPOSE_BASE` in\n * apps/api/.../sandbox-cli/service.ts), so the common case is\n * `workspace-<svc>-<n>`. Fall back to stripping the trailing replica\n * index only, so attribution still works if the project name ever\n * changes or someone uses an explicit `container_name:` override.\n */\nexport function containerNameToService(containerName: string): string {\n const withProject = containerName.match(/^workspace-(.+)-\\d+$/);\n if (withProject) return withProject[1]!;\n const withoutProject = containerName.match(/^(.+)-\\d+$/);\n if (withoutProject) return withoutProject[1]!;\n return containerName;\n}\n\nasync function fetchServiceLogs(\n apiUrl: string,\n token: string,\n instanceId: string,\n service: string,\n tail: number\n): Promise<string> {\n const res = await apiRequest<{ ok: boolean; error?: string; data: { logs: string } }>(\n apiUrl,\n token,\n 'POST',\n `/org/environments/instances/${instanceId}/docker/logs`,\n { service, tail }\n );\n if (!res.ok) return `(log fetch failed: ${res.error ?? 'unknown'})`;\n return res.data?.logs ?? '';\n}\n\nasync function handleVerify(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const json = hasFlag(argv, '--json');\n // --keep-instance is kept as a success-path alias (existing behavior).\n // On failure the probe is now preserved by default so the user can debug;\n // pass --auto-terminate-on-failure for CI where you want cleanup anyway.\n const keepInstance = hasFlag(argv, '--keep-instance');\n const autoTerminateOnFailure = hasFlag(argv, '--auto-terminate-on-failure');\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox verify <template-id>');\n process.exit(1);\n }\n\n console.log(`Verifying template ${templateId}...`);\n\n // Kick off a verification instance — server bypasses the \"verified\" gate\n // via verificationMode but otherwise runs the full provisioning flow.\n const verifyRes = await apiRequest<{ ok: boolean; error?: string; data: { instanceId: string; expiresAt: string } }>(\n apiUrl, token, 'POST', `/org/environments/templates/${templateId}/verify`, {}\n );\n if (!verifyRes.ok) {\n console.error(`Error: ${verifyRes.error}`);\n process.exit(1);\n }\n const instanceId = verifyRes.data.instanceId;\n console.log(` Verification instance: ${instanceId}`);\n\n // Poll diagnostics until instance reaches 'running' or a terminal state.\n const POLL_INTERVAL_MS = 10_000;\n const TIMEOUT_MS = 600_000; // 10 minutes\n const DETAIL_DUMP_INTERVAL_MS = 30_000; // rate-limit noisy dumps\n const LOG_TAIL_LINES = 40;\n const startTime = Date.now();\n let finalStatus = '';\n let publicUrl: string | undefined;\n let lastDetailDumpAt = 0;\n let routes: ServiceRoute[] = [];\n let routesFetched = false;\n\n while (Date.now() - startTime < TIMEOUT_MS) {\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n\n const diag = await apiRequest<{ ok: boolean; error?: string; data: DiagnosticsResult }>(\n apiUrl, token, 'GET', `/org/environments/instances/${instanceId}/diagnostics`,\n undefined,\n { timeoutMs: 60_000 }\n );\n if (!diag.ok) continue;\n\n const now = Date.now();\n const elapsed = Math.round((now - startTime) / 1000);\n const remaining = Math.max(0, Math.round((TIMEOUT_MS - (now - startTime)) / 1000));\n const status = String((diag.data.instance as { status?: string; publicUrl?: string }).status ?? '');\n publicUrl = (diag.data.instance as { publicUrl?: string }).publicUrl;\n const appOutput = 'stdout' in diag.data.appHealth ? diag.data.appHealth.stdout : '';\n const appUp = appOutput.length > 0 && !appOutput.includes('UNREACHABLE');\n const dockerOutput = 'stdout' in diag.data.docker ? diag.data.docker.stdout : '';\n const containers = parseDockerPs(dockerOutput);\n const broken = containers.filter(isBrokenContainer);\n\n // Compact progress line (overwriting, like before) — adds remaining time.\n const ttl = remaining > 60 ? `${elapsed}s elapsed` : `${remaining}s remaining`;\n process.stdout.write(\n `\\r [${ttl}] status: ${status}, app: ${appUp ? 'up' : 'waiting'}, containers: ${containers.length}, unhealthy: ${broken.length} `\n );\n\n if (status === 'running' && appUp) {\n process.stdout.write('\\n');\n finalStatus = 'running';\n break;\n }\n if (status === 'failed' || status === 'terminated') {\n process.stdout.write('\\n');\n finalStatus = status;\n break;\n }\n\n // When the app is still down and at least one container is broken,\n // dump details + log tails once every DETAIL_DUMP_INTERVAL_MS so the\n // user actually sees what's failing instead of staring at a spinner.\n const shouldDump =\n !appUp &&\n broken.length > 0 &&\n now - lastDetailDumpAt >= DETAIL_DUMP_INTERVAL_MS;\n if (shouldDump) {\n lastDetailDumpAt = now;\n process.stdout.write('\\n');\n console.log('');\n console.log(` App unreachable after ${elapsed}s. Container state:`);\n for (const c of containers) {\n console.log(` ${c.name.padEnd(40)} ${c.status}`);\n }\n console.log('');\n // Per-broken-container log tails. Cap to MAX_LOG_CONTAINERS to avoid\n // SSM call amplification on pathological cases (a template with 6\n // restart-looping services would otherwise issue 6 sequential SSM\n // calls every 30s). Fetch in parallel — these are independent.\n const MAX_LOG_CONTAINERS = 3;\n const toFetch = broken.slice(0, MAX_LOG_CONTAINERS);\n const overflow = broken.length - toFetch.length;\n const fetched = await Promise.all(\n toFetch.map((c) =>\n fetchServiceLogs(apiUrl, token, instanceId, containerNameToService(c.name), LOG_TAIL_LINES)\n .then((logs) => ({ c, logs }))\n )\n );\n for (const { c, logs } of fetched) {\n const tail = logs.trim().split('\\n').slice(-LOG_TAIL_LINES).join('\\n ');\n console.log(` --- ${c.name} (${c.state}) ---`);\n console.log(` ${tail || '(no logs)'}`);\n console.log('');\n }\n if (overflow > 0) {\n console.log(\n ` (+${overflow} more broken container${overflow === 1 ? '' : 's'} — re-run with: canary sandbox logs ${instanceId} --service <name>)`\n );\n console.log('');\n }\n // Attribution: if nginx-proxy is looping because of a labeled upstream\n // that's exited, call that out so the user doesn't chase nginx's\n // \"host not found in upstream\" as the root cause.\n if (!routesFetched) {\n routes = await fetchInstanceRoutes(apiUrl, token, instanceId);\n routesFetched = true;\n }\n const nginxProxy = containers.find((c) => containerNameToService(c.name) === 'nginx-proxy');\n if (nginxProxy?.state === 'restarting' && routes.length > 0) {\n const failedUpstreams = routes\n .map((r) => ({\n route: r.route,\n service: r.service,\n container: broken.find((c) => containerNameToService(c.name) === r.service),\n }))\n .filter((x) => x.container);\n if (failedUpstreams.length > 0) {\n for (const fu of failedUpstreams) {\n console.log(\n ` >> nginx-proxy is restart-looping because upstream '${fu.service}' (route '${fu.route}') is ${fu.container!.state}. Read that service's logs above, not nginx's.`\n );\n }\n console.log('');\n }\n }\n }\n }\n\n if (finalStatus !== 'running') {\n console.error('');\n console.error(`Verification failed — instance ended in status '${finalStatus || 'timeout'}'.`);\n if (publicUrl) console.error(` Instance URL: ${publicUrl}`);\n console.error(` Instance ID: ${instanceId}`);\n console.error(` Debug with: canary sandbox diagnostics ${instanceId}`);\n console.error(` canary sandbox logs ${instanceId} --service <name>`);\n console.error(` canary sandbox run-command ${instanceId} 'docker ps -a'`);\n if (autoTerminateOnFailure) {\n await apiRequest(apiUrl, token, 'POST', `/org/environments/instances/${instanceId}/terminate`);\n console.error(` Terminated (--auto-terminate-on-failure).`);\n } else {\n // Default flip (was: auto-terminate). Call it out so CI users don't\n // silently accumulate orphan instances.\n console.error(` Instance preserved by default for debugging. Pass --auto-terminate-on-failure in CI.`);\n console.error(` Terminate manually with:`);\n console.error(` canary sandbox terminate ${instanceId}`);\n }\n process.exit(1);\n }\n\n // We just observed the instance reach status === 'running' && appUp.\n // That's the same observation that backs the server-running and\n // server-accessible checklist items, so flip them too — best-effort\n // (templates without those items in their checklist 404 silently).\n const evidenceSummary = `Verified via canary sandbox verify — fresh instance ${instanceId} came up healthy.`;\n for (const itemId of ['server-running', 'server-accessible']) {\n await apiRequest(\n apiUrl,\n token,\n 'POST',\n `/org/environments/templates/${templateId}/checklist/${itemId}/complete`,\n { status: 'completed', summary: evidenceSummary }\n ).catch(() => {\n // Item not present on this checklist profile — fine.\n });\n }\n\n // Mark the template-verified checklist item complete — this is what flips\n // template.status to 'verified' (via ChecklistService.completeChecklistItem).\n const completeRes = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'POST',\n `/org/environments/templates/${templateId}/checklist/template-verified/complete`,\n {\n status: 'completed',\n summary: evidenceSummary,\n }\n );\n if (!completeRes.ok) {\n console.error(`Failed to record verification: ${completeRes.error}`);\n console.error(`Instance ${instanceId} left running for inspection.`);\n process.exit(1);\n }\n\n if (!keepInstance) {\n await apiRequest(apiUrl, token, 'POST', `/org/environments/instances/${instanceId}/terminate`);\n console.log(` Terminated verification instance.`);\n } else {\n console.log(` --keep-instance: instance ${instanceId} left running${publicUrl ? ` at ${publicUrl}` : ''}.`);\n }\n\n // Confirm the template actually flipped to verified. The status flip only\n // happens when every item in the checklist profile is terminal\n // (completed or skipped); if other items are still pending, the probe\n // succeeded but the template stays in its prior status.\n const templateAfter = await apiRequest<{ ok: boolean; data?: { status?: string } }>(\n apiUrl,\n token,\n 'GET',\n `/org/environments/templates/${templateId}`\n ).catch(() => ({ ok: false }) as { ok: boolean; data?: { status?: string } });\n const templateStatus = templateAfter.data?.status;\n const verified = templateStatus === 'verified';\n\n if (json) {\n writeRawOutput(JSON.stringify({ templateId, verified, status: templateStatus, instanceId, publicUrl }, null, 2));\n return;\n }\n\n if (verified) {\n console.log('Template verified.');\n return;\n }\n\n // Probe passed but status didn't flip — surface the pending items so the\n // user knows what to complete/skip before re-running verify.\n console.log(`Verification probe passed (instance ${instanceId} came up healthy),`);\n console.log(`but template status is still '${templateStatus ?? 'unknown'}'.`);\n type ChecklistItemSummary = { id: string; label: string; itemStatus?: { status?: string } };\n const checklistRes = await apiRequest<{ ok: boolean; data?: ChecklistItemSummary[] }>(\n apiUrl,\n token,\n 'GET',\n `/org/environments/templates/${templateId}/checklist`\n ).catch(() => ({ ok: false }) as { ok: boolean; data?: ChecklistItemSummary[] });\n const pending = (checklistRes.data ?? []).filter((it) => {\n const s = it.itemStatus?.status ?? 'pending';\n return s !== 'completed' && s !== 'skipped';\n });\n if (pending.length > 0) {\n console.log('Pending checklist items must be completed or skipped:');\n for (const item of pending) {\n console.log(` - ${item.label} (${item.id})`);\n }\n console.log('');\n console.log('Complete: canary sandbox checklist complete <template-id> <item-id> --status success --summary \"...\"');\n console.log('Skip: canary sandbox checklist skip <template-id> <item-id>');\n }\n process.exit(1);\n}\n\nasync function handleArtifactGet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const type = positional[1];\n const outputFile = getArgValue(argv, '--output');\n\n if (!templateId || !type) {\n console.error('Error: template ID and artifact type required. Usage: canary sandbox artifact get <template-id> <type>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: Artifact }>(\n apiUrl, token, 'GET', `/org/environments/templates/${templateId}/artifacts/${type}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (outputFile) {\n const { writeFileSync } = await import('node:fs');\n writeFileSync(outputFile, res.data.content ?? '', 'utf-8');\n console.log(`Written to ${outputFile}`);\n } else {\n process.stdout.write(res.data.content ?? '');\n }\n}\n\nasync function handleArtifactCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const type = positional[1];\n const filePath = positional[2];\n const json = hasFlag(argv, '--json');\n\n if (!templateId || !type || !filePath) {\n console.error('Error: all args required. Usage: canary sandbox artifact create <template-id> <type> <file>');\n process.exit(1);\n }\n\n // Keep in sync with WellKnownArtifactType in packages/types/src/sandbox-tasks.ts + 'file' (custom with --path)\n const WELL_KNOWN_TYPES = ['docker-compose', 'dockerfile', 'startup-script', 'setup-script', 'seed-script', 'environment-config', 'file'];\n if (!WELL_KNOWN_TYPES.includes(type)) {\n console.error(`Error: invalid artifact type \"${type}\". Valid types: ${WELL_KNOWN_TYPES.join(', ')}`);\n process.exit(1);\n }\n\n // File artifacts require --path <destination>\n const destinationPath = getArgValue(argv, '--path');\n if (type === 'file' && !destinationPath) {\n console.error('Error: file artifacts require --path <destination>. Example: --path /workspace/.canary/vite.sandbox.config.ts');\n process.exit(1);\n }\n if (destinationPath && !destinationPath.startsWith('/workspace/.canary/')) {\n const suggestedName = destinationPath.split('/').filter(Boolean).pop() ?? 'yourfile';\n console.error(\n 'Error: --path must be under /workspace/.canary/ (the repo tree is user-owned — sandbox files live in .canary/).\\n' +\n ` try: --path /workspace/.canary/${suggestedName}`\n );\n process.exit(1);\n }\n\n let content: string;\n try {\n content = readFileSync(filePath, 'utf-8');\n } catch (err) {\n console.error(`Error reading file: ${err}`);\n process.exit(1);\n }\n\n // Derive filename from path\n const filename = filePath.split('/').pop() ?? filePath;\n\n // Artifact upload writes through to the live authoring instance, which on\n // first push (or a large file) can outlast 30s easily. 90s lines up with\n // the slowest legitimate upload we've observed.\n const res = await apiRequest<{\n ok: boolean;\n error?: string;\n data?: { kind: 'created' | 'replaced'; previousPath?: string };\n }>(\n apiUrl, token, 'PUT', `/org/environments/templates/${templateId}/artifacts`, {\n artifactType: type,\n filename,\n content,\n ...(destinationPath ? { destinationPath } : {}),\n },\n { timeoutMs: 90_000 }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const kind = res.data?.kind;\n const marker = kind === 'replaced' ? ' (replaced)' : kind === 'created' ? ' (created)' : '';\n console.log(`Artifact uploaded: ${type} (${filename}, ${content.length} bytes)${marker}`);\n if (kind === 'replaced' && res.data?.previousPath) {\n console.log(` previous destination removed: ${res.data.previousPath}`);\n }\n}\n\nasync function handleBuild(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const json = hasFlag(argv, '--json');\n const noWait = hasFlag(argv, '--no-wait');\n const instanceId = positional[0];\n\n if (!instanceId) {\n console.error('Error: instance ID required. Usage: canary sandbox build <instance-id>');\n process.exit(1);\n }\n\n console.log('Building sandbox (this takes a few minutes)...');\n\n // The build is async by design: a single HTTP connection cannot reliably\n // span a multi-minute build (ALB caps, our own client timeouts, flaky\n // networks). Kick the build off with a short timeout — the server has\n // already started the work by the time it would otherwise reply — then\n // move to polling diagnostics for the truth.\n const KICKOFF_TIMEOUT_MS = 8_000;\n\n let buildCompleted = false;\n let buildResult: { stdout: string; stderr: string; exitCode: number } | null = null;\n\n const result = await apiFetch(`${apiUrl}/org/environments/instances/${instanceId}/build`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n token,\n timeoutMs: KICKOFF_TIMEOUT_MS,\n });\n\n if (result.ok) {\n // Fast path: server responded within the kickoff window (cached/no-op build).\n const data = (await result.response.json().catch(() => null)) as\n | { ok: boolean; error?: string; data?: { stdout: string; stderr: string; exitCode: number } }\n | null;\n if (data?.ok && data.data) {\n buildCompleted = true;\n buildResult = data.data;\n } else if (data && !data.ok) {\n console.error(`Error: ${data.error ?? 'build failed'}`);\n process.exit(1);\n }\n } else {\n const { failure } = result;\n // network = our kickoff timeout firing; 502/504 = gateway giving up while\n // the build runs on. Both mean \"watch via polling, not an error\".\n const isExpectedAsync =\n failure.kind === 'network' ||\n (failure.kind === 'http-error' && (failure.status === 502 || failure.status === 504));\n if (!isExpectedAsync) {\n if (isTerminalFailure(failure)) exitOnFailure(failure);\n console.error(`Error: ${failure.message}`);\n process.exit(1);\n }\n }\n\n // If build completed synchronously, print results and exit\n if (buildCompleted && buildResult) {\n if (json) {\n writeRawOutput(JSON.stringify(buildResult, null, 2));\n return;\n }\n\n if (buildResult.stdout) {\n process.stdout.write(buildResult.stdout);\n if (!buildResult.stdout.endsWith('\\n')) process.stdout.write('\\n');\n }\n if (buildResult.stderr) {\n process.stderr.write(`STDERR: ${buildResult.stderr}`);\n if (!buildResult.stderr.endsWith('\\n')) process.stderr.write('\\n');\n }\n\n if (buildResult.exitCode === 0) {\n console.log('Build complete.');\n } else {\n console.error(`Build failed with exit code ${buildResult.exitCode}`);\n process.exit(1);\n }\n return;\n }\n\n // Build is async — poll diagnostics until the app is reachable or timeout\n if (noWait) {\n console.log('Build started. Use diagnostics to monitor progress:');\n console.log(` canary sandbox diagnostics ${instanceId}`);\n return;\n }\n\n console.log('Build started, watching for services...');\n\n const BUILD_POLL_TIMEOUT_MS = 300_000; // 5 minutes\n const POLL_INTERVAL_MS = 10_000;\n const startTime = Date.now();\n\n while (Date.now() - startTime < BUILD_POLL_TIMEOUT_MS) {\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n\n const diag = await apiRequest<{ ok: boolean; error?: string; data: DiagnosticsResult }>(\n apiUrl, token, 'GET', `/org/environments/instances/${instanceId}/diagnostics`,\n undefined,\n { timeoutMs: 60_000 }\n );\n\n if (!diag.ok) continue;\n\n const elapsed = Math.round((Date.now() - startTime) / 1000);\n const dockerOutput = 'stdout' in diag.data.docker ? diag.data.docker.stdout : '';\n const appOutput = 'stdout' in diag.data.appHealth ? diag.data.appHealth.stdout : '';\n const appUp = appOutput.length > 0 && !appOutput.includes('UNREACHABLE');\n\n // Show a compact progress line\n const containerCount = (dockerOutput.match(/\\n/g) || []).length + (dockerOutput ? 1 : 0);\n const healthyCount = (dockerOutput.match(/\\(healthy\\)/g) || []).length;\n process.stdout.write(`\\r [${elapsed}s] containers: ${containerCount}, healthy: ${healthyCount}, app: ${appUp ? 'up' : 'waiting'} `);\n\n // Trust the HTTP probe: if nginx is serving the app on :3000 or :8000, we're done.\n // Compose healthchecks are optional and most user services don't declare them.\n if (appUp) {\n process.stdout.write('\\n');\n console.log('Build complete.');\n return;\n }\n }\n\n process.stdout.write('\\n');\n console.error('Build timed out waiting for the app to respond. Check diagnostics and logs:');\n console.error(` canary sandbox diagnostics ${instanceId}`);\n console.error(` canary sandbox logs ${instanceId} --service <name>`);\n process.exit(1);\n}\n\nasync function handleRunCommand(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const service = getArgValue(argv, '--service');\n const timeoutFlag = getArgValue(argv, '--timeout');\n\n // Strip our flags (--json, --service <value>, --timeout <value>) so they\n // don't leak into the remote command.\n const filtered = argv.filter((a, i, arr) => {\n if (a === '--json') return false;\n if (a === '--service' || a === '--timeout') return false;\n if (i > 0 && (arr[i - 1] === '--service' || arr[i - 1] === '--timeout')) return false;\n return true;\n });\n\n // First non-flag arg is the instance ID; everything after it is the command\n // (including flags like -la that belong to the remote command, not to us)\n const firstNonFlag = filtered.findIndex((a) => !a.startsWith('-'));\n const instanceId = firstNonFlag >= 0 ? filtered[firstNonFlag] : undefined;\n const command = firstNonFlag >= 0 ? filtered.slice(firstNonFlag + 1).join(' ') : '';\n\n if (!instanceId || !command) {\n console.error('Error: instance ID and command required. Usage: canary sandbox run-command <instance-id> [--service <name>] [--timeout <ms>] <command...>');\n process.exit(1);\n }\n\n // Server-side run-command caps at 55s (SANDBOX_COMMAND_TIMEOUT). Default\n // the CLI just past that so we surface the server's typed timeout rather\n // than an opaque client-side cancel. For longer ops (test suites,\n // playwright), pass a higher value or use first-class actions.\n const timeoutMs = timeoutFlag ? Math.max(parseInt(timeoutFlag, 10), 1000) : 60_000;\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: { stdout: string; stderr: string; exitCode: number } }>(\n apiUrl, token, 'POST', `/org/environments/instances/${instanceId}/run-command`,\n { command, ...(service ? { service } : {}) },\n { timeoutMs }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n if (res.data.stdout) {\n process.stdout.write(res.data.stdout);\n if (!res.data.stdout.endsWith('\\n')) process.stdout.write('\\n');\n }\n if (res.data.stderr) {\n process.stderr.write(`STDERR: ${res.data.stderr}`);\n if (!res.data.stderr.endsWith('\\n')) process.stderr.write('\\n');\n }\n if (res.data.exitCode !== 0) {\n console.log(`Exit code: ${res.data.exitCode}`);\n }\n}\n\nasync function handleDiagnostics(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!instanceId) {\n console.error('Error: instance ID required. Usage: canary sandbox diagnostics <instance-id>');\n process.exit(1);\n }\n\n // Server-side DIAGNOSTICS_TIMEOUT_MS is 30s; the client needs a bit more\n // headroom than that or we race the server and report a misleading client\n // timeout for a server-side timeout.\n const res = await apiRequest<{ ok: boolean; error?: string; data: DiagnosticsResult }>(\n apiUrl, token, 'GET', `/org/environments/instances/${instanceId}/diagnostics`,\n undefined,\n { timeoutMs: 60_000 }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n const routes = await fetchInstanceRoutes(apiUrl, token, instanceId);\n const data = { ...res.data, routes };\n\n if (json) {\n writeRawOutput(JSON.stringify(data, null, 2));\n return;\n }\n\n const d = data;\n\n console.log('=== Instance ===');\n for (const [key, value] of Object.entries(d.instance)) {\n console.log(` ${key}: ${value}`);\n }\n\n if (d.lifecycle) {\n console.log('');\n console.log('=== Lifecycle ===');\n console.log(` databaseStatus: ${d.lifecycle.databaseStatus}`);\n console.log(` poolStatus: ${d.lifecycle.poolStatus ?? 'missing'}`);\n console.log(` ec2InstanceState: ${d.lifecycle.ec2InstanceState ?? 'unknown'}`);\n console.log(` live: ${d.lifecycle.live}`);\n if (d.lifecycle.message) writeRawOutput(` message: ${d.lifecycle.message}`);\n }\n\n if (d.provision) {\n console.log('');\n console.log('=== Provisioning ===');\n console.log(` phase: ${d.provision.phase}`);\n console.log(` jobStatus: ${d.provision.jobStatus}`);\n if (d.provision.startedAt) console.log(` startedAt: ${d.provision.startedAt}`);\n if (d.provision.elapsedSeconds !== null) console.log(` elapsed: ${d.provision.elapsedSeconds}s`);\n if (d.provision.lastError) console.log(` lastError: ${d.provision.lastError}`);\n }\n\n const sections: Array<[string, CommandOrError]> = [\n ['Docker Containers', d.docker],\n ['Git Status', d.git],\n ['Disk Usage', d.disk],\n ['Agent Health', d.agentHealth],\n ['App Health', d.appHealth],\n ];\n if (d.resources) sections.splice(3, 0, ['Resource Usage', d.resources]);\n\n for (const [title, result] of sections) {\n console.log('');\n console.log(`=== ${title} ===`);\n if ('error' in result) {\n writeRawOutput(` Error${result.code ? ` [${result.code}]` : ''}: ${result.error}`);\n } else {\n if (result.stdout) writeRawOutput(result.stdout);\n if (result.stderr) writeRawOutput(` STDERR: ${result.stderr}`);\n }\n }\n}\n\nasync function handleTerminate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n\n if (!instanceId) {\n console.error('Error: instance ID required. Usage: canary sandbox terminate <instance-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl, token, 'POST', `/org/environments/instances/${instanceId}/terminate`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Instance ${instanceId} termination initiated.`);\n}\n\nasync function handleDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox delete <template-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl, token, 'DELETE', `/org/environments/templates/${templateId}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Template ${templateId} deleted.`);\n}\n\n// ---------------------------------------------------------------------------\n// Action handlers\n// ---------------------------------------------------------------------------\n\ntype ActionDef = {\n id: string;\n slug: string;\n label: string;\n description: string | null;\n icon: string | null;\n enabled: boolean;\n inputSchema: Record<string, unknown>;\n outputSchema: Record<string, unknown> | null;\n /** Declared handover slots — what this action hands back, and of what kind. */\n outputConfig?: Array<Record<string, unknown>> | null;\n scriptContent: string;\n timeoutMs: number;\n createdAt: string;\n updatedAt: string;\n};\n\ntype ActionExecution = {\n id: string;\n actionId: string;\n instanceId: string;\n source: string;\n status: string;\n result: Record<string, unknown> | null;\n stdout: string | null;\n stderr: string | null;\n exitCode: number | null;\n durationMs: number | null;\n error: string | null;\n createdAt: string;\n};\n\nasync function handleActionList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox action list <template-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: ActionDef[] }>(\n apiUrl, token, 'GET', `/org/environments/templates/${templateId}/actions`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const actions = res.data ?? [];\n if (actions.length === 0) {\n console.log('No actions defined for this template.');\n return;\n }\n\n console.log(`Actions (${actions.length}):`);\n for (const a of actions) {\n const status = a.enabled ? '' : ' [disabled]';\n console.log(` ${pad(a.slug, 30)} ${pad(a.label, 25)}${status}`);\n }\n}\n\nasync function handleActionGet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const slug = positional[1];\n const json = hasFlag(argv, '--json');\n\n if (!templateId || !slug) {\n console.error('Error: template ID and slug required. Usage: canary sandbox action get <template-id> <slug>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: ActionDef }>(\n apiUrl, token, 'GET', `/org/environments/templates/${templateId}/actions/${slug}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const a = res.data;\n console.log(`Action: ${a.slug}`);\n console.log(` Label: ${a.label}`);\n writeRawOutput(` Description: ${a.description ?? '(none)'}`);\n console.log(` Icon: ${a.icon ?? '(none)'}`);\n console.log(` Enabled: ${a.enabled}`);\n console.log(` Timeout: ${a.timeoutMs}ms`);\n console.log(` Input Schema:`);\n console.log(` ${JSON.stringify(a.inputSchema, null, 2).split('\\n').join('\\n ')}`);\n if (a.outputSchema) {\n console.log(` Output Schema:`);\n console.log(` ${JSON.stringify(a.outputSchema, null, 2).split('\\n').join('\\n ')}`);\n }\n // Printed unconditionally when present: \"did the handover slot actually save\"\n // is the first thing to check when a run reports no results.\n if (a.outputConfig && a.outputConfig.length > 0) {\n console.log(` Output Config (handover slots):`);\n console.log(` ${JSON.stringify(a.outputConfig, null, 2).split('\\n').join('\\n ')}`);\n }\n console.log(` Script:`);\n console.log(` ${a.scriptContent.split('\\n').join('\\n ')}`);\n}\n\nasync function handleActionCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const slug = positional[1];\n const json = hasFlag(argv, '--json');\n\n if (!templateId || !slug) {\n console.error('Error: template ID and slug required. Usage: canary sandbox action create <template-id> <slug> [options]');\n process.exit(1);\n }\n\n const scriptPath = getArgValue(argv, '--script');\n const inputSchemaPath = getArgValue(argv, '--input-schema');\n const outputSchemaPath = getArgValue(argv, '--output-schema');\n const credentialOutputConfigPath = getArgValue(argv, '--credential-output-config');\n const outputConfigPath = getArgValue(argv, '--output-config');\n const label = getArgValue(argv, '--label') ?? slug.replace(/-/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n const description = getArgValue(argv, '--description');\n const icon = getArgValue(argv, '--icon');\n const timeoutStr = getArgValue(argv, '--timeout');\n const encryptScript = hasFlag(argv, '--encrypt-script');\n const credentialTemplateId = getArgValue(argv, '--credential-template');\n const provisioningMode = getArgValue(argv, '--provisioning-mode');\n const runOnProvision = hasFlag(argv, '--run-on-provision');\n const provisionOrderStr = getArgValue(argv, '--provision-order');\n\n if (provisioningMode && provisioningMode !== 'shared' && provisioningMode !== 'unique') {\n console.error(\"Error: --provisioning-mode must be 'shared' or 'unique'\");\n process.exit(1);\n }\n\n if (provisionOrderStr && !/^-?\\d+$/.test(provisionOrderStr)) {\n console.error('Error: --provision-order must be an integer');\n process.exit(1);\n }\n\n if (!scriptPath) {\n console.error('Error: --script <file> is required');\n process.exit(1);\n }\n if (!inputSchemaPath) {\n console.error('Error: --input-schema <file> is required');\n process.exit(1);\n }\n\n let scriptContent: string;\n try {\n scriptContent = readFileSync(scriptPath, 'utf-8');\n } catch (err) {\n console.error(`Error reading script file: ${err}`);\n process.exit(1);\n }\n\n let inputSchema: Record<string, unknown>;\n try {\n inputSchema = JSON.parse(readFileSync(inputSchemaPath, 'utf-8'));\n } catch (err) {\n console.error(`Error reading input schema file: ${err}`);\n process.exit(1);\n }\n\n let outputSchema: Record<string, unknown> | undefined;\n if (outputSchemaPath) {\n try {\n outputSchema = JSON.parse(readFileSync(outputSchemaPath, 'utf-8'));\n } catch (err) {\n console.error(`Error reading output schema file: ${err}`);\n process.exit(1);\n }\n }\n\n let credentialOutputConfig: Record<string, unknown> | undefined;\n if (credentialOutputConfigPath) {\n try {\n credentialOutputConfig = JSON.parse(readFileSync(credentialOutputConfigPath, 'utf-8'));\n } catch (err) {\n console.error(`Error reading credential output config file: ${err}`);\n process.exit(1);\n }\n }\n\n // The declared handover: which files this action leaves behind and what kind\n // each one is. Without this flag the column is only reachable through a raw\n // API call, which would make a shipped feature unusable from the tools an\n // operator actually has. Validated server-side against the output-kind\n // registry, so a bad slot is rejected at save time rather than mid-run.\n let outputConfig: unknown;\n if (outputConfigPath) {\n try {\n outputConfig = JSON.parse(readFileSync(outputConfigPath, 'utf-8'));\n } catch (err) {\n console.error(`Error reading output config file: ${err}`);\n process.exit(1);\n }\n if (!Array.isArray(outputConfig)) {\n console.error('Error: --output-config must contain a JSON array of output slots');\n process.exit(1);\n }\n }\n\n const body: Record<string, unknown> = {\n label,\n scriptContent,\n inputSchema,\n ...(description ? { description } : {}),\n ...(icon ? { icon } : {}),\n ...(outputSchema ? { outputSchema } : {}),\n ...(credentialOutputConfig ? { credentialOutputConfig } : {}),\n ...(outputConfig ? { outputConfig } : {}),\n ...(credentialTemplateId ? { credentialTemplateId } : {}),\n ...(provisioningMode ? { provisioningMode } : {}),\n ...(runOnProvision ? { runOnProvision: true } : {}),\n ...(provisionOrderStr ? { provisionOrder: parseInt(provisionOrderStr, 10) } : {}),\n ...(timeoutStr ? { timeoutMs: parseInt(timeoutStr, 10) } : {}),\n ...(encryptScript ? { scriptEncrypted: true } : {}),\n };\n\n const res = await apiRequest<{ ok: boolean; error?: string; data?: ActionDef }>(\n apiUrl, token, 'PUT', `/org/environments/templates/${templateId}/actions/${slug}`, body\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n console.log(`Action '${slug}' saved (${scriptContent.length} bytes script, label: \"${label}\")`);\n}\n\nasync function handleActionDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const slug = positional[1];\n\n if (!templateId || !slug) {\n console.error('Error: template ID and slug required. Usage: canary sandbox action delete <template-id> <slug>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl, token, 'DELETE', `/org/environments/templates/${templateId}/actions/${slug}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Action '${slug}' deleted.`);\n}\n\nasync function handleActionExecute(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n const slug = positional[1];\n const json = hasFlag(argv, '--json');\n\n if (!instanceId || !slug) {\n console.error('Error: instance ID and slug required. Usage: canary sandbox action execute <instance-id> <slug> [options]');\n process.exit(1);\n }\n\n // Collect params from --param key=value flags\n let params: Record<string, unknown> = {};\n\n const paramsFile = getArgValue(argv, '--params-file');\n if (paramsFile) {\n try {\n params = JSON.parse(readFileSync(paramsFile, 'utf-8'));\n } catch (err) {\n console.error(`Error reading params file: ${err}`);\n process.exit(1);\n }\n }\n\n // --param flags override params-file\n for (let i = 0; i < argv.length; i++) {\n if (argv[i] === '--param' && argv[i + 1]) {\n const [key, ...valueParts] = argv[i + 1]!.split('=');\n if (key && valueParts.length > 0) {\n params[key] = valueParts.join('=');\n }\n i++; // skip value\n }\n }\n\n interface ActionExecuteResponse {\n executionId: string;\n status: string;\n result: unknown;\n stdout: string | null;\n stderr: string | null;\n exitCode: number | null;\n durationMs: number | null;\n error: string | null;\n credentialId?: string | null;\n }\n\n const runAsync = hasFlag(argv, '--async');\n const timeoutArg = getArgValue(argv, '--timeout-ms');\n let executionTimeoutMs: number | undefined;\n if (timeoutArg) {\n const parsed = Number(timeoutArg);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n console.error(`Error: --timeout-ms must be a positive number, got '${timeoutArg}'`);\n process.exit(1);\n }\n if (!runAsync) {\n // The server ignores it without --async, so accepting it quietly would\n // leave the caller believing a long ceiling was applied when it was not.\n console.error('Error: --timeout-ms only applies with --async');\n process.exit(1);\n }\n executionTimeoutMs = parsed;\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data?: ActionExecuteResponse }>(\n apiUrl, token, 'POST', `/org/environments/instances/${instanceId}/actions/${slug}/execute`, {\n params,\n source: 'cli',\n ...(runAsync ? { async: true } : {}),\n ...(executionTimeoutMs !== undefined ? { executionTimeoutMs } : {}),\n }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const exec = res.data!;\n\n // A detached run has no outcome yet, so the completion summary below would\n // print a misleading \"exit ?\" line. Tell the caller how to follow it instead.\n if (runAsync) {\n console.log(`[started] Action '${slug}' running in the background.`);\n console.log(`Execution: ${exec.executionId}`);\n console.log(\n `Follow: cofactor sandbox action execution get ${instanceId} ${exec.executionId}`\n );\n console.log(\n `Cancel: cofactor sandbox action execution cancel ${instanceId} ${exec.executionId}`\n );\n return;\n }\n const statusIcon = exec.status === 'completed' ? '[done]' : exec.status === 'failed' ? '[FAIL]' : `[${exec.status}]`;\n console.log(`${statusIcon} Action '${slug}' ${exec.status} (${exec.durationMs ?? 0}ms, exit ${exec.exitCode ?? '?'})`);\n if (exec.result) {\n console.log(`Result: ${JSON.stringify(exec.result, null, 2)}`);\n }\n if (exec.credentialId) {\n console.log(`Credential: ${exec.credentialId} (linked in property vault)`);\n }\n if (exec.status === 'failed' && exec.stderr) {\n // Cap by bytes first so a single multi-MB line doesn't blow up the terminal,\n // then by line count for readability.\n const tail = exec.stderr.slice(-10_000).trim().split('\\n').slice(-20).join('\\n');\n console.log(`Stderr (last 20 lines):\\n${tail}`);\n }\n if (exec.error) {\n console.log(`Error: ${exec.error}`);\n }\n}\n\nasync function handleActionExecutions(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!instanceId) {\n console.error('Error: instance ID required. Usage: canary sandbox action executions <instance-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: ActionExecution[] }>(\n apiUrl, token, 'GET', `/org/environments/instances/${instanceId}/actions/executions`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const execs = res.data ?? [];\n if (execs.length === 0) {\n console.log('No executions yet.');\n return;\n }\n\n console.log('Executions:');\n for (const e of execs) {\n const icon = STATUS_ICONS[e.status] ?? `[${e.status}]`;\n const dur = e.durationMs !== null && e.durationMs !== undefined ? `${e.durationMs}ms` : '?ms';\n console.log(` ${icon} ${pad(e.id.slice(0, 8), 10)} ${pad(e.source, 6)} ${pad(e.status, 12)} ${pad(dur, 8)} ${formatDate(e.createdAt)}`);\n }\n}\n\n/** Print one execution the same way `action execute` prints a finished run. */\nfunction printExecutionDetail(exec: ActionExecution): void {\n const icon = STATUS_ICONS[exec.status] ?? `[${exec.status}]`;\n const dur = exec.durationMs !== null && exec.durationMs !== undefined ? `${exec.durationMs}ms` : '?ms';\n console.log(`${icon} ${exec.status} (${dur}, exit ${exec.exitCode ?? '?'})`);\n console.log(`Execution: ${exec.id}`);\n if (exec.result) {\n console.log(`Result: ${JSON.stringify(exec.result, null, 2)}`);\n }\n if (exec.stderr) {\n const tail = exec.stderr.slice(-10_000).trim().split('\\n').slice(-20).join('\\n');\n if (tail) console.log(`Stderr (last 20 lines):\\n${tail}`);\n }\n if (exec.error) {\n console.log(`Error: ${exec.error}`);\n }\n}\n\n/**\n * Read one execution. For a detached run this is also what advances it: the\n * server reconciles a still-`running` row against SSM on read, so polling this\n * is how a background action reaches a terminal state.\n */\nasync function handleActionExecutionGet(\n argv: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n const executionId = positional[1];\n const json = hasFlag(argv, '--json');\n\n if (!instanceId || !executionId) {\n console.error(\n 'Error: instance ID and execution ID required. Usage: cofactor sandbox action execution get <instance-id> <execution-id>'\n );\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data?: ActionExecution }>(\n apiUrl,\n token,\n 'GET',\n `/org/environments/instances/${instanceId}/actions/executions/${executionId}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n printExecutionDetail(res.data!);\n}\n\n/** Stop a detached execution. Idempotent; a finished run is reported as-is. */\nasync function handleActionExecutionCancel(\n argv: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n const executionId = positional[1];\n const json = hasFlag(argv, '--json');\n\n if (!instanceId || !executionId) {\n console.error(\n 'Error: instance ID and execution ID required. Usage: cofactor sandbox action execution cancel <instance-id> <execution-id>'\n );\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data?: ActionExecution }>(\n apiUrl,\n token,\n 'POST',\n `/org/environments/instances/${instanceId}/actions/executions/${executionId}/cancel`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n printExecutionDetail(res.data!);\n}\n\n// ---------------------------------------------------------------------------\n// Multi-repo: manage repos linked to a sandbox template\n// ---------------------------------------------------------------------------\n\ntype TemplateRepoLink = {\n id: string;\n templateId: string;\n propertyGithubRepoId: string;\n role: string | null;\n branch: string;\n subdirectory: string | null;\n serviceName: string;\n};\n\n/**\n * Parse `role=owner/name@branch:subdir` (branch and subdir optional) into the\n * pieces the API expects. Used by `canary sandbox repo add --spec ...`.\n */\nfunction parseRepoSpec(spec: string): {\n role?: string;\n repoFullName: string;\n branch?: string;\n subdirectory?: string;\n} {\n let rest = spec;\n let role: string | undefined;\n const eq = rest.indexOf('=');\n if (eq > -1 && !rest.slice(0, eq).includes('/')) {\n role = rest.slice(0, eq);\n rest = rest.slice(eq + 1);\n }\n let subdirectory: string | undefined;\n const colon = rest.indexOf(':');\n if (colon > -1) {\n subdirectory = rest.slice(colon + 1);\n rest = rest.slice(0, colon);\n }\n let branch: string | undefined;\n const at = rest.indexOf('@');\n if (at > -1) {\n branch = rest.slice(at + 1);\n rest = rest.slice(0, at);\n }\n return { role, repoFullName: rest, branch, subdirectory };\n}\n\nasync function handleRepoList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const templateId = getArgValue(argv, '--template');\n if (!templateId) {\n console.error('Error: --template is required.');\n process.exit(1);\n }\n\n const result = await apiRequest<{ ok: boolean; items?: TemplateRepoLink[]; error?: string }>(\n apiUrl,\n token,\n 'GET',\n `/org/environments/templates/${templateId}/repos`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error ?? 'Unknown error'}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(result.items ?? [], null, 2));\n return;\n }\n\n const items = result.items ?? [];\n if (items.length === 0) {\n console.log('No repos linked to this template.');\n return;\n }\n for (const r of items) {\n const role = r.role ? ` [${r.role}]` : '';\n const subdir = r.subdirectory ? `:${r.subdirectory}` : '';\n console.log(` ${r.serviceName} → ${r.propertyGithubRepoId}@${r.branch}${subdir}${role} (link=${r.id})`);\n }\n}\n\nasync function handleRepoAdd(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const templateId = getArgValue(argv, '--template');\n const propertyGithubRepoId = getArgValue(argv, '--property-github-repo-id');\n const spec = getArgValue(argv, '--spec');\n const role = getArgValue(argv, '--role');\n const branch = getArgValue(argv, '--branch');\n const subdirectory = getArgValue(argv, '--subdirectory');\n const serviceName = getArgValue(argv, '--service-name');\n\n if (!templateId || !propertyGithubRepoId) {\n console.error('Error: --template and --property-github-repo-id are required.');\n console.error('Tip: link the repo to the property first via `canary property repo link`, then pass its id here.');\n process.exit(1);\n }\n\n const parsed: ReturnType<typeof parseRepoSpec> | { role?: undefined; branch?: undefined; subdirectory?: undefined } = spec ? parseRepoSpec(spec) : {};\n\n const body: Record<string, unknown> = { propertyGithubRepoId };\n const finalRole = role ?? parsed.role;\n const finalBranch = branch ?? parsed.branch;\n const finalSubdir = subdirectory ?? parsed.subdirectory;\n if (finalRole !== undefined) body.role = finalRole;\n if (finalBranch !== undefined) body.branch = finalBranch;\n if (finalSubdir !== undefined) body.subdirectory = finalSubdir;\n if (serviceName !== undefined) body.serviceName = serviceName;\n\n const result = await apiRequest<{ ok: boolean; data?: TemplateRepoLink; error?: string }>(\n apiUrl,\n token,\n 'POST',\n `/org/environments/templates/${templateId}/repos`,\n body\n );\n\n if (!result.ok) {\n if (json) writeRawOutput(JSON.stringify(result, null, 2));\n else console.error(`Error: ${result.error ?? 'Unknown error'}`);\n process.exit(1);\n }\n if (json) writeRawOutput(JSON.stringify(result.data, null, 2));\n else console.log(`Linked repo to template ${templateId} (link=${result.data?.id})`);\n}\n\nasync function handleRepoUpdate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const templateId = getArgValue(argv, '--template');\n const linkId = getArgValue(argv, '--link-id');\n const role = getArgValue(argv, '--role');\n const branch = getArgValue(argv, '--branch');\n const subdirectory = getArgValue(argv, '--subdirectory');\n const serviceName = getArgValue(argv, '--service-name');\n\n if (!templateId || !linkId) {\n console.error('Error: --template and --link-id are required.');\n process.exit(1);\n }\n\n const body: Record<string, unknown> = {};\n if (role !== undefined) body.role = role;\n if (branch !== undefined) body.branch = branch;\n if (subdirectory !== undefined) body.subdirectory = subdirectory;\n if (serviceName !== undefined) body.serviceName = serviceName;\n\n const result = await apiRequest<{ ok: boolean; data?: TemplateRepoLink; error?: string }>(\n apiUrl,\n token,\n 'PATCH',\n `/org/environments/templates/${templateId}/repos/${linkId}`,\n body\n );\n\n if (!result.ok) {\n if (json) writeRawOutput(JSON.stringify(result, null, 2));\n else console.error(`Error: ${result.error ?? 'Unknown error'}`);\n process.exit(1);\n }\n if (json) writeRawOutput(JSON.stringify(result.data, null, 2));\n else console.log(`Updated repo link ${linkId}`);\n}\n\nasync function handleRepoRemove(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const templateId = getArgValue(argv, '--template');\n const linkId = getArgValue(argv, '--link-id');\n if (!templateId || !linkId) {\n console.error('Error: --template and --link-id are required.');\n process.exit(1);\n }\n const result = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'DELETE',\n `/org/environments/templates/${templateId}/repos/${linkId}`\n );\n if (!result.ok) {\n if (json) writeRawOutput(JSON.stringify(result, null, 2));\n else console.error(`Error: ${result.error ?? 'Unknown error'}`);\n process.exit(1);\n }\n if (json) writeRawOutput(JSON.stringify(result, null, 2));\n else console.log(`Removed repo link ${linkId} from template ${templateId}`);\n}\n\nasync function handleRepo(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [action, ...rest] = argv;\n switch (action) {\n case 'list':\n await handleRepoList(rest, apiUrl, token);\n break;\n case 'add':\n await handleRepoAdd(rest, apiUrl, token);\n break;\n case 'update':\n await handleRepoUpdate(rest, apiUrl, token);\n break;\n case 'remove':\n case 'rm':\n await handleRepoRemove(rest, apiUrl, token);\n break;\n default:\n console.error(\n `Unknown repo action: ${action ?? '(none)'}. Use list/add/update/remove.\\n` +\n 'Examples:\\n' +\n ' canary sandbox repo list --template <template-id>\\n' +\n ' canary sandbox repo add --template <template-id> --property-github-repo-id <uuid> --role frontend\\n' +\n ' canary sandbox repo add --template <template-id> --property-github-repo-id <uuid> --spec frontend=owner/web@main:/apps/web'\n );\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Main dispatcher\n// ---------------------------------------------------------------------------\n\nexport async function runSandbox(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === 'help' || hasFlag(argv, '--help', '-h')) {\n printHelp();\n return;\n }\n\n // Guide is purely local — no auth needed. `guide v2 [topic]` serves the\n // Sandboxes v2 guide; plain `guide [section]` keeps serving the v1 guide.\n if (subCommand === 'guide') {\n if (rest[0] === 'v2') {\n const v2Rest = rest.slice(1);\n const section = getGuideSectionArg(v2Rest);\n const json = hasFlag(v2Rest, '--json');\n printGuide(SANDBOX_V2_GUIDE_SECTIONS, section, json);\n return;\n }\n const section = getGuideSectionArg(rest);\n const json = hasFlag(rest, '--json');\n printGuide(SANDBOX_GUIDE_SECTIONS, section, json);\n return;\n }\n\n // Sandboxes v2 `validate` is purely local (offline schema + cross-check) —\n // no auth needed, same as `guide`.\n if (subCommand === 'validate') {\n handleSandboxValidate(rest);\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(argv);\n\n // Strip global flags consumed by resolveConfig so handlers don't see them as positional args.\n // `--app-url` travels with `--api-url` (a worktree or self-hosted target); the CI push\n // workflow passed it and every v2 command refused it (2026-10-04).\n const globalFlags = new Set(['--env', '--api-url', '--app-url', '--token', '--org']);\n const args = rest.filter((arg, i) => {\n if (globalFlags.has(arg)) return false;\n if (i > 0 && globalFlags.has(rest[i - 1])) return false;\n return true;\n });\n rejectUnknownSandboxV2Args(subCommand, args);\n\n switch (subCommand) {\n case 'list':\n await handleList(args, apiUrl, token);\n break;\n case 'template':\n await handleTemplate(args, apiUrl, token);\n break;\n case 'repo':\n await handleRepo(args, apiUrl, token);\n break;\n case 'artifacts':\n await handleArtifacts(args, apiUrl, token);\n break;\n case 'checklist':\n await handleChecklist(args, apiUrl, token);\n break;\n case 'create':\n await handleCreate(args, apiUrl, token);\n break;\n case 'provision':\n await handleProvision(args, apiUrl, token);\n break;\n case 'warm':\n await handleWarm(args, apiUrl, token);\n break;\n case 'verify':\n await handleVerify(args, apiUrl, token);\n break;\n case 'set-size':\n await handleSetSize(args, apiUrl, token);\n break;\n case 'pool': {\n const [poolAction, ...poolRest] = args;\n if (poolAction === 'set-size') {\n await handlePoolSetSize(poolRest, apiUrl, token);\n } else {\n console.error(`Unknown pool action: ${poolAction}. Use \"set-size\".`);\n process.exit(1);\n }\n break;\n }\n case 'artifact': {\n const [action, ...actionRest] = args;\n if (action === 'get') {\n await handleArtifactGet(actionRest, apiUrl, token);\n } else if (action === 'create') {\n await handleArtifactCreate(actionRest, apiUrl, token);\n } else {\n console.error(`Unknown artifact action: ${action}. Use \"get\" or \"create\".`);\n process.exit(1);\n }\n break;\n }\n case 'config': {\n const [configAction, ...configRest] = args;\n if (configAction === 'list') {\n await handleConfigList(configRest, apiUrl, token);\n } else if (configAction === 'set') {\n await handleConfigSet(configRest, apiUrl, token);\n } else if (configAction === 'unset') {\n await handleConfigUnset(configRest, apiUrl, token);\n } else {\n console.error(`Unknown config action: ${configAction}. Use \"list\", \"set\", or \"unset\".`);\n process.exit(1);\n }\n break;\n }\n case 'emulation':\n await handleEmulation(args, apiUrl, token);\n break;\n case 'action': {\n const [actionSub, ...actionRest] = args;\n switch (actionSub) {\n case 'list':\n await handleActionList(actionRest, apiUrl, token);\n break;\n case 'get':\n await handleActionGet(actionRest, apiUrl, token);\n break;\n case 'create':\n await handleActionCreate(actionRest, apiUrl, token);\n break;\n case 'delete':\n await handleActionDelete(actionRest, apiUrl, token);\n break;\n case 'execute':\n await handleActionExecute(actionRest, apiUrl, token);\n break;\n case 'executions':\n await handleActionExecutions(actionRest, apiUrl, token);\n break;\n case 'execution': {\n const [execSub, ...execRest] = actionRest;\n if (execSub === 'get') {\n await handleActionExecutionGet(execRest, apiUrl, token);\n } else if (execSub === 'cancel') {\n await handleActionExecutionCancel(execRest, apiUrl, token);\n } else {\n console.error(`Unknown execution sub-command: ${execSub}. Use \"get\" or \"cancel\".`);\n process.exit(1);\n }\n break;\n }\n default:\n console.error(`Unknown action sub-command: ${actionSub}. Use \"list\", \"get\", \"create\", \"delete\", \"execute\", \"executions\", or \"execution\".`);\n process.exit(1);\n }\n break;\n }\n case 'build':\n await handleBuild(args, apiUrl, token);\n break;\n case 'run-command':\n await handleRunCommand(args, apiUrl, token);\n break;\n case 'diagnostics':\n await handleDiagnostics(args, apiUrl, token);\n break;\n case 'logs':\n await handleLogs(args, apiUrl, token);\n break;\n case 'preview':\n await handlePreview(args, apiUrl, token);\n break;\n case 'envs':\n await handleSandboxEnvs(args, apiUrl, token);\n break;\n case 'connect':\n await handleSandboxConnect(args, apiUrl, token);\n break;\n case 'push':\n await handleSandboxPush(args, apiUrl, token);\n break;\n case 'claim':\n await handleSandboxClaim(args, apiUrl, token);\n break;\n case 'show':\n await handleSandboxShow(args, apiUrl, token);\n break;\n case 'run':\n await handleSandboxRun(args, apiUrl, token);\n break;\n case 'verify-env':\n await handleSandboxVerifyEnv(args, apiUrl, token);\n break;\n case 'destroy':\n await handleSandboxDestroy(args, apiUrl, token);\n break;\n case 'terminate':\n await handleTerminate(args, apiUrl, token);\n break;\n case 'delete':\n await handleDelete(args, apiUrl, token);\n break;\n default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printHelp();\n process.exit(1);\n }\n}\n","A Sandboxes v2 sandbox is a VM restored from a stack of snapshot layers,\nnot a container stood up from scratch each time. That's what makes claims\nfast: the work (installing the toolchain and dependencies, building the\napp, seeding its data) happens once per commit and environment and is\nbaked into a snapshot, and every claim restores its own copy.\n\nEvery example in this guide uses one invented app: a project-tracking\nSaaS with workspaces, their members (roles: owner, member, viewer),\nprojects and tasks, a REST API with cookie sessions, and a Postgres\ndatabase. Substitute your own app's objects, roles and endpoints.\n\nLAYERS\n\n platform Cofactor's sandbox helper, which runs your services,\n prepare commands and actions. Rebuilt only when the\n helper's contract with Cofactor changes.\n\n toolchain `setup` from cofactor.sandbox.yaml, plus `resources` and the\n declared `ports`. Rebuilt only when those change — this is\n the expensive layer (apt packages, language runtimes,\n database clusters).\n\n repo base The repository checked out and built at a commit:\n `lifecycle.build` steps, each rerun only when the git\n pathspecs in its `inputs` changed since the layer this one\n is stacked on. This is what makes \"the same commit, again\"\n nearly free.\n\n ready One commit and one environment: the repo base moved to the\n snapshot commit, prepared, and the environment's seeds run — then\n captured while its services are running. Built once, the\n first time anything asks for that commit and environment;\n every claim is a copy of it.\n\nA GitHub-delivered sandbox is identified by **repository + definition\npath**, not by repository alone, so a monorepo can hold several (see\nbelow). Pushed images have no repository or build layers: the image is\nthe base, and each push gets its own ready snapshots (see the `upload`\ntopic).\n\nTHE CLAIM LIFECYCLE\n\n 1. snapshot The claim needs the ready snapshot for its commit and\n environment. If there isn't one yet, it is built first\n (once, however many claims are waiting on it):\n - the nearest layer (toolchain or repo base) is\n restored;\n - the outstanding `lifecycle.build` steps bring it to\n the commit (only the steps whose `inputs` changed);\n - `prepare` runs (see step 3);\n - the environment's seed chain runs, in order (parent's\n seeds before its own — see the `environments` topic);\n each step is an action or an emulator operation,\n and its output can be referenced by later steps;\n - the VM is captured with its services running.\n 2. restore A copy of the ready snapshot is restored. Nothing is\n built or seeded: the copy starts exactly where the\n snapshot left off, data included.\n 3. prepare Runs `lifecycle.prepare` on the copy. This is where it\n becomes *this* sandbox: port params get its own public\n URLs (secret params keep the snapshot's), personas get\n freshly generated passwords (COFACTOR_PERSONAS), and —\n when the definition declares `emulation` — the emulator\n gets a fresh config and restarts.\n 4. ready The sandbox reports its port URLs, persona logins, and the\n seed outputs (the same for every copy of the snapshot).\n From here it behaves like a real running instance of the\n app until it's destroyed or its lease expires. A copy is\n never reused.\n\n`cofactor sandbox show` breaks the claim's time down into these phases.\n\nA failure while building the snapshot (a build step, `prepare` or a seed)\nfails the claims waiting on it; the next claim of that commit and\nenvironment builds the snapshot again, so a failure that happens every\ntime needs a fix and a new commit or push. See the `verify` topic for what to do\nwhen that happens.\n\nAPPLICATIONS, AND WHERE THEIR CODE COMES FROM\n\n A sandbox is always a sandbox of a Cofactor application. How the\n application's code reaches its sandboxes is its own setting:\n\n - GitHub (`cofactor sandbox connect --application A --repo owner/name\n [--dir D]`): the `cofactor.sandbox.yaml` under `D` at each commit\n installs (`setup`) and builds (`lifecycle.build`) a checkout. A\n monorepo can connect one application per definition directory.\n Commands run **from that definition's directory** — an action's\n `run:` path, build steps and services resolve relative to where the\n `cofactor.sandbox.yaml` lives, not the repo root.\n - Pushed images (`cofactor sandbox push`), for a team that gives\n Cofactor no repository access: see the `upload` topic.\n\n Either way, each environment in the definition is one sandbox\n environment of the application, and each persona one of its personas, so\n test runs on that environment sign in with the sandbox's own logins.\n\nHOW A TEST AGENT USES THIS\n\n 1. Browse what's available: `cofactor sandbox envs --application <name>`\n lists the definition's environments, actions, emulator operations,\n ports and personas.\n 2. Claim one: `cofactor sandbox claim --application <name>\n [--environment <name>] --wait`. Without `--environment` you get\n `default`.\n 3. Write the test against the claimed sandbox: drive the app through its\n port URLs, call actions to set up data the test needs\n (`cofactor sandbox run <id> <action>`), and call emulator operations\n to inspect or drive third parties (`cofactor sandbox run <id>\n <plugin.op>`).\n 4. Destroy it when done: `cofactor sandbox destroy <id>`.\n","Everything about a sandbox lives in one file: `cofactor.sandbox.yaml`, at the\nrepository root or under a definition directory for a monorepo (for pushed\nimages, wherever you keep it — see the `upload` topic). The examples below\nare fragments of one file for the guide's example app: a project tracker\nwith a `web` front end, an `api` server and Postgres.\n`cofactor sandbox validate` checks every field below.\n\n Required: `version`, `resources`, `ports`, `params`, `env`, `services`,\n `lifecycle` and `environments`; write `{}` for one you don't use (e.g.\n `params: {}`, `lifecycle: { prepare: [] }`). `personas`, `actions`, `emulation` and `object_storage`\n may be left out.\n Names of ports, services, personas, actions, environments and plugins\n are lowercase letters, digits and `-` (up to 32 characters, starting\n with a letter); a seed step's `id` is lowercase letters, digits and `_`\n (it is used in `${<id>.<output>}`, so no `-`).\n\nversion\n Always `1`. The schema version of the file itself.\n\nresources\n The machine every sandbox of this application gets.\n resources: { vcpus: 2, memory_mib: 4096 }\n\nsetup\n (GitHub-delivered only.) A root shell script that installs the\n toolchain: packages, language runtimes, database clusters. Runs once per\n toolchain layer (keyed by this script's text plus `resources` and\n `ports` — see the `overview` topic).\n setup: |\n apt-get update -qq\n apt-get install -y -qq postgresql-16 nodejs npm jq curl >/dev/null\n\nports\n Named ports the app listens on, each becoming its own public URL. Several\n are normal — bundle web and API together so one sandbox serves both UI\n flows and API sequences. `access: public` gets a routable URL; `private`\n stays internal. At most 8 total, including the implicit `emulation` port\n added automatically when `emulation` is declared (and `object-storage`\n when `object_storage` is) — see the `ports` topic.\n ports:\n web: { port: 3000, access: public }\n api: { port: 3001, access: public }\n\nparams\n Per-sandbox values, filled in by `prepare`, referenced from `env` as\n `${params.name}`. Three shapes:\n - a plain string: a fixed value, same in every sandbox.\n - `{port: <name>}`: the sandbox's own public URL for that port.\n - `{secret: true}`: a random value made when the version's ready\n snapshot is built, so every sandbox of that version and environment\n shares it (e.g. a session-signing key); not unique per claim.\n params:\n web_url: { port: web }\n api_url: { port: api }\n session_secret: { secret: true }\n\nenv\n Environment variables the services, prepare commands and actions run\n with. `${params.name}` is substituted with that param's value. An\n environment can add its own on top (see the `env` topic).\n env:\n DATABASE_URL: postgres://app@127.0.0.1:5432/app\n PUBLIC_URL: ${params.web_url}\n API_URL: ${params.api_url}\n SESSION_SECRET: ${params.session_secret}\n\nservices\n Long-running processes.\n services:\n db:\n run: exec sandbox/start-postgres.sh\n health: { command: pg_isready -h 127.0.0.1 }\n api:\n run: cd api && exec npm start\n health: { http: 3001, path: /health }\n after_build: true # don't start until lifecycle.build has produced its output\n reload_on_prepare: true # restarted by prepare, so it sees per-sandbox env\n web:\n run: cd web && exec npm run serve\n health: { http: 3000, path: / }\n after_build: true\n reload_on_prepare: true # reads PUBLIC_URL, API_URL and FEATURE_FLAGS\n\n `health` is either `{ command: <shell> }` (checked by exit code) or\n `{ http: <port>, path: </...> }`. `after_build` is for GitHub-delivered\n definitions only.\n\npersonas\n Named login identities declared here; `prepare` generates each one's\n password per sandbox (see the `personas` topic). Only `email` is declared\n in the definition.\n personas:\n owner: { email: owner@example.test }\n member: { email: member@example.test }\n viewer: { email: viewer@example.test }\n\nlifecycle\n build (GitHub-delivered only.) Ordered steps that produce the\n repo-base layer. Each has `name`, `run` (a shell command) and\n `inputs` (git pathspecs, relative to the definition's\n directory) — a step reruns only when the blobs its `inputs`\n match changed, or an earlier step ran.\n lifecycle:\n build:\n - name: install\n run: npm ci\n inputs: [package-lock.json, \"**/package.json\"]\n\n prepare Shell scripts run every time a sandbox is prepared (fresh claim\n or an in-place move to a new commit), after services with\n `reload_on_prepare` have restarted with per-sandbox values. Each\n param is also available as `COFACTOR_PARAM_<NAME>`.\n lifecycle:\n prepare:\n - bash sandbox/personas.sh\n\nactions\n Typed scripts — the one way data gets into a sandbox (seeds, tests,\n `cofactor sandbox run`). See the `seeds-and-actions` topic for\n what makes a good one; the field shapes are:\n actions:\n create-workspace:\n description: A new workspace, created by the given persona, who becomes its owner.\n run: sandbox/actions/create-workspace.sh\n input:\n name: { type: string, required: true }\n as: { type: persona, required: true, description: Who creates it }\n output:\n workspace_id: { type: string }\n timeout: 60 # seconds, defaults to 60, max 1800\n add-member:\n description: Adds a persona to a workspace with a role.\n run: sandbox/actions/add-member.sh\n input:\n workspace_id: { type: string, required: true }\n persona: { type: persona, required: true, description: Who is added }\n role: { type: string, required: true, enum: [owner, member, viewer] }\n as: { type: persona, required: true, description: Who adds them }\n\n Input properties: `type` (string | number | integer | boolean |\n persona — a persona's name, checked against `personas`), `description`,\n `required`, `default`, `enum`, `sensitive` (masked wherever input is\n logged or returned).\n Output properties: `type`, `description`.\n\nemulation\n Plugin id → its config (often `{}`). Declaring any plugin here adds the\n implicit, public, port-80 `emulation` port automatically — you don't\n declare it yourself. See the `emulation` topic for what each plugin\n intercepts.\n emulation:\n email: {}\n\nobject_storage\n An emulated S3 or GCS server with these buckets in every sandbox, its\n standard client env given to everything that runs, and an implicit\n public `object-storage` port. See the `object-storage` topic.\n object_storage: { provider: s3, buckets: [attachments] }\n\nenvironments\n Named flavors of the same sandbox — different starting data and config.\n Required, must include `default`. See the `environments` topic for the\n full model (`extends`, seed steps, `${id.key}` references, `env`).\n environments:\n default:\n description: |\n Workspace \"Acme\", owned by the owner persona. No projects.\n seeds:\n - { id: acme, action: create-workspace, input: { name: Acme, as: owner } }\n\nMULTI-LINE TEXT\n\n YAML `|` keeps line breaks (use it for multi-paragraph `description`s and\n the `setup` script). YAML `>-` folds lines into one string with no\n trailing newline (use it for a single long sentence, such as a long\n action `description`).\n","An environment (a \"flavor\") is the same sandbox — same code, same toolchain\n— started with different data and config. `default` is required; every\ndefinition must declare it. Previews use `default`, and so does `cofactor\nsandbox claim` when you don't pass `--environment`.\n\nWHEN TO ADD A FLAVOR VS. PASSING INPUTS IN THE TEST\n\n Rule of thumb: add a flavor when many tests need the same starting state.\n Call actions from inside the test when only that test needs the data.\n\n In the example app, `default` seeds one workspace with an owner, a\n member and a viewer, because nearly every test signs in to a workspace\n as one of those roles. A test about archiving a project, on the other\n hand, calls `create-project` itself (see the worked example in\n `seeds-and-actions`) rather than getting a new flavor. If you find\n yourself writing a flavor whose seeds only exist to serve one test,\n that data belongs in the test's own action calls instead.\n\n Add a second flavor only when real tests need a starting state `default`\n can't give them cheaply — typically a different plan or set of feature\n flags (see \"WHAT TO BUILD FIRST\" in `seeds-and-actions`). Don't add one\n speculatively.\n\nDESCRIPTIONS ARE FOR THE READER\n\n `description` is required and can be multi-line (YAML `|`). Write it for\n the agent choosing between environments — say what's already there and\n who can do what, not just a label:\n environments:\n default:\n description: |\n Workspace \"Acme\": owner is its owner, member a member,\n viewer a read-only viewer. No projects yet; create them\n with create-project.\n\n A vague description (\"the default setup\") gives an agent nothing to\n decide with. Say what exists, in terms of the ids and roles a test will\n actually use.\n\nEXTENDS\n\n `extends: <other-environment-name>` composes one environment on top of\n another. The child's snapshot runs the **parent's seeds first, then the\n child's own**, in that order. `env` merges the same way: parent's keys\n first, the child's last (so a child can override a parent's value).\n Chains can be more than two deep; a cycle is rejected by `validate`.\n\n Step ids must be **unique across the whole resolved chain** — a parent\n and its child can't reuse the same id, since the child's steps can\n reference the parent's outputs.\n\n An environment carries its own `description`, `extends`, `seeds` and\n `env`. It has no `params` or `personas` of its own: those belong to the\n definition and are the same in every environment.\n\nSEED STEPS AND `${id.key}`\n\n Each `seeds` entry is `{id, action, input}`:\n - `id`: unique within the resolved chain; later steps reference this\n step's output as `${id.key}`.\n - `action`: a declared action's slug, or `<plugin>.<op>` for an\n emulator operation (e.g. `email.sent-emails`).\n - `input`: literal values, or `${id.key}` references to an earlier\n step's output.\n\n A reference that is the **entire** value keeps the output's real type:\n input: { workspace_id: \"${acme.workspace_id}\" } # exactly acme's workspace_id\n\n A reference **embedded** in a longer string becomes a string\n interpolation instead:\n input: { name: \"Board for ${acme.workspace_id}\" } # becomes a string\n\n `${id.key}` must point at a step that ran earlier in the chain and at one\n of that step's *declared* output keys — `cofactor sandbox validate`\n checks both and reports the path.\n\n The example app's environments, in full:\n environments:\n default:\n description: |\n Workspace \"Acme\": owner is its owner, member a member,\n viewer a read-only viewer. No projects yet.\n seeds:\n - id: acme\n action: create-workspace\n input: { name: Acme, as: owner }\n - id: add_member\n action: add-member\n input: { workspace_id: \"${acme.workspace_id}\", persona: member, role: member, as: owner }\n - id: add_viewer\n action: add-member\n input: { workspace_id: \"${acme.workspace_id}\", persona: viewer, role: viewer, as: owner }\n free-plan:\n extends: default\n description: Same as default, but Acme is moved to the Free plan (3-project limit).\n seeds:\n - id: downgrade\n action: set-plan\n input: { workspace_id: \"${acme.workspace_id}\", plan: free }\n beta-features:\n extends: default\n description: Same as default, with the timeline view turned on.\n env:\n FEATURE_FLAGS: timeline\n\n `add_member` couldn't run before `acme` — its `workspace_id` input\n depends on `acme`'s output. Seeds always run in the order they're\n written. `free-plan` sets its plan with a seed action because the app\n stores plans in its database; `beta-features` uses `env` because the\n app reads its flags from an environment variable (see the `env` topic\n for which processes see it).\n","An action is a script with a typed input and output schema. It is the one\nway data gets into a sandbox — seeds call it, and so do tests and\n`cofactor sandbox run`. Every environment's `seeds` list is\njust an ordered sequence of action (and emulator operation) calls. For\nthe script contract (`INPUT_<NAME>`, the result line), see the `upload`\ntopic's worked example; it is the same for GitHub-delivered apps.\n\nWHAT TO BUILD FIRST\n\n 1. One persona per role your tests need. The example app has three\n roles, so it declares `owner`, `member` and `viewer`. A persona in\n the definition is only a login (prepare makes it work — see\n `personas`); its role comes from a seed action such as `add-member`,\n or from prepare itself when the persona is an account your app\n already has with the right role. With one persona per role, tests can\n sign in as each and check what that role may and may not do.\n\n 2. One environment per plan or feature-flag combination your tests care\n about: `default`, `free-plan`, `beta-features`. Set each with a seed\n action that goes through your app's admin API or database (the\n example's `set-plan`), or with the environment's `env:` when your app\n reads the flag from an environment variable (the example's\n `FEATURE_FLAGS`; see the `env` topic for which processes see it).\n Params are per definition, not per environment, so they can't vary a\n flag between environments. See the `environments` topic.\n\n 3. Actions for the few core objects nearly every test needs, each taking\n `as: <persona>`: in the example, `create-workspace`, `add-member`,\n `create-project`, `create-task`. Keep the shared seeds thin; tests\n create the rest themselves with these actions.\n\n 4. If your app stores files, declare `object_storage` and seed the few\n files every test needs (a logo, an import template) with an action;\n see the `object-storage` topic.\n\nSEEDS VS. ACTIONS A TEST CALLS ITSELF\n\n Seeds are shared, thin, generic base data: the handful of things nearly\n every test needs before it can start — a workspace and its members, one\n per role. A shared seed stays thin and generic; it does not try to\n anticipate every test's needs.\n\n An action a test calls itself is data specific to that one test. If a\n test's job is \"archive a project,\" the project it archives should not\n live in a shared seed — a shared seed that every test's setup depends on\n becomes something every other test can break by changing it, and it\n bloats claim time for tests that never needed it. The test creates its\n own project by calling `create-project`, then operates on the id it got\n back.\n\n Rule of thumb: if removing this data would break tests that aren't about\n it, it's a seed. If only one test's own scenario needs it, it's an action\n call inside that test.\n\nWHAT MAKES A GOOD ACTION\n\n - Typed inputs and outputs. The input schema is what a test (or an\n agent) uses to call it correctly; the output schema is what the next\n step gets to chain from. Untyped free-form data defeats the ability to\n validate `${id.key}` references and CLI input ahead of time.\n - Fast. Seeds run once per version, while its snapshot is built; calls\n run inline in a test — slow seeds slow every new version's first\n claim, and slow calls slow every test.\n - Returns the ids a test needs next. `create-project` returns\n `project_id`, not just \"ok\" — that id is what the test's next action\n or assertion needs.\n - Safe to call more than once, or unique by its own input. Either the\n action is idempotent (`set-plan` re-run with the same plan changes\n nothing), or a distinct call naturally produces a distinct result\n (`create-project` with a new name makes a new project) rather than\n silently colliding with a previous call's data.\n - Explicit about who acts: the `as` persona pattern. Every action that\n performs a real business operation (creating a project, inviting a\n member) takes an explicit `as: <persona>` input naming which persona\n performs it, rather than assuming \"the admin\" or \"whoever is logged\n in.\" The script signs in as exactly that persona (its login is in\n `COFACTOR_PERSONAS`) and acts in the workspace it was given; the\n persona must already have a role there that allows the operation. So\n an action has **no hidden dependency** on some other seed having run\n first to establish who's \"in charge\" — the caller says so, in the\n input.\n - Goes through the app's own API when the data is encrypted or has side\n effects. `invite-user` calls the real invite endpoint so the\n invitation email is actually sent (and can be observed later with\n `email.sent-emails`); a raw insert would silently skip the send. The\n same goes for anything the app encrypts or hashes on write.\n - Goes through the database only for rows with no encryption and no side\n effects, and only when there is no API for it — e.g. `set-plan`\n updating a workspace's plan column, which in production only an\n internal billing process sets.\n\nANTI-PATTERNS\n\n - A giant do-everything seed. One seed step that creates a workspace,\n three members, two projects and their tasks in one script is harder to\n reuse, harder to debug when it fails partway, and forces every test\n that wants only part of it to take all of it. Break it into separate\n actions and chain them with `${id.key}`, as `default` does.\n - An action that silently acts as an admin. If an action doesn't take\n `as`, and instead assumes some fixed identity internally, a test\n writer has no way to know (or override) who actually performed the\n operation, and the action can't be reused for a test that needs a\n viewer to attempt (and be denied) the same thing.\n - Hardcoded ids. An action or seed that references a specific workspace\n or project id baked into the script defeats the whole point of\n chaining `${id.key}` — every id a later step needs should come from an\n earlier step's declared output.\n - An action that depends on seed ordering it doesn't declare. A script\n that assumes some other seed already ran (e.g. that a particular\n workspace exists) without that dependency showing up as an input is a\n hidden coupling — if the environment's seed order ever changes, the\n action breaks with no local sign of why. Take everything you need as\n an explicit input instead (as `create-project` takes `workspace_id`\n and `as`).\n - Writing output anywhere but the last `{`-starting stdout line. The\n action's result is exactly the *last* line of stdout that starts with\n `{`. Send every progress message, debugging `echo` and intermediate\n curl response to stderr — printing one to stdout by mistake silently\n corrupts the parsed output (or an earlier `{`-line gets picked up as a\n decoy if you print the wrong one last).\n\nWORKED EXAMPLE: CREATE THEN ARCHIVE A PROJECT\n\n Workspace \"Acme\" already exists (seeded by `default`'s `acme` step); the\n test supplies its own project rather than relying on a seeded one,\n because creating and archiving a project are the test's own subject.\n\n 1. Claim a sandbox on `default` and note `acme`'s output:\n cofactor sandbox claim --application \"Project Tracker\" --wait\n The claim's seed outputs include `acme.workspace_id`.\n\n 2. Create the project the test will operate on, acting as the member:\n cofactor sandbox run <id> create-project \\\n --input workspace_id=<acme.workspace_id> \\\n --input name=Temp \\\n --input as=member\n Returns `{\"project_id\": \"<id>\"}`.\n\n 3. Drive the app (via its `web`/`api` port URLs) to verify the project\n shows up wherever the product surfaces it, using the `project_id`\n from step 2 to find the right row.\n\n 4. Archive it the way the product's UI or API would — this is what the\n test is actually verifying, so it belongs in the test's own steps,\n not in a seed. Repeating it as `viewer` should be refused.\n\n 5. Destroy the sandbox:\n cofactor sandbox destroy <id>\n\n Nothing about the project this test created is shared with any other\n test, and nothing another seed does could break this test's setup.\n","A persona is a named login identity declared in `cofactor.sandbox.yaml`:\n personas:\n owner: { email: owner@example.test }\n member: { email: member@example.test }\n viewer: { email: viewer@example.test }\n\nDeclare one per role your tests need (see \"WHAT TO BUILD FIRST\" in\n`seeds-and-actions`). Only the email is fixed in the definition.\nEverything else about a persona is assigned per sandbox or per seed:\n\nPASSWORDS — SET BY PREPARE\n\n Each sandbox gets its own randomly generated password per persona,\n created during the `prepare` step of the claim lifecycle (see the\n `overview` topic). Those passwords never appear in the definition —\n they're generated fresh for that one sandbox and handed to\n `lifecycle.prepare` as `COFACTOR_PERSONAS`, a JSON map of persona name to\n `{email, password}`. Your prepare script reads it and makes each login\n work in your app: the example app's `sandbox/personas.sh` creates or\n updates one user per persona (email + password hash), with no workspace\n and no role — just the login itself. The `upload` topic covers the\n details (prepare runs while the app is up; logins must be read per\n sign-in, not once at startup).\n\n If your app gates sign-in behind a first-run step (accepting terms,\n initializing the instance, verifying the email address, finishing\n onboarding), clear it in prepare for each persona, the way you set the\n password: a seed or test signing in through the real login API should\n land signed in, not on the gate.\n\nROLES AND MEMBERSHIPS — SET BY SEEDS\n\n A persona existing (having a login) is separate from what it can do.\n Workspace membership and role are set by seed steps, not by the\n definition: `create-workspace` makes its `as` persona the owner of the\n new workspace, and `add-member` gives a persona a role in it. This split\n matters: the same persona can be \"just a login\" in one environment and\n a workspace owner in another, because that comes from which seeds\n actually ran. If a persona's email is an account your app already has\n with the right role, prepare setting its password is enough.\n\nHOW A SEED OR ACTION SIGNS IN AS A PERSONA\n\n Actions receive `COFACTOR_PERSONAS` too. An action that performs a real\n operation takes the acting persona as an input (`as`, of type\n `persona`, which arrives as `INPUT_AS` holding the persona's name),\n reads that persona's email and password from `COFACTOR_PERSONAS`, and\n signs in through the app's real login endpoint — the same way a user\n would — keeping the session cookie for its next calls:\n login=$(jq -c --arg p \"$INPUT_AS\" '.[$p] | {email, password}' <<<\"$COFACTOR_PERSONAS\")\n jar=$(mktemp)\n curl -fsS -c \"$jar\" -H 'content-type: application/json' \\\n -d \"$login\" \"$API_URL/api/auth/login\" >&2\n See the `seeds-and-actions` topic on why `as` is never assumed.\n\n An action runs inside the sandbox, so it can reach the app on\n localhost. If your app's session cookie is `Secure` (or `__Host-`\n prefixed) and your HTTP client won't send it back over plain http, call\n the app through its public port URL (here `API_URL`, from a\n `{port: api}` param) instead.\n\n Keep whatever the login returns that later calls need: the session\n cookie, a bearer token from the response body, or both (some apps bind\n a bearer token to an httpOnly cookie and refuse one without the other).\n\n Every action signs in from the same address, so a seed that runs several\n actions can trip the app's login rate limit (often around 10 attempts a\n minute). If your app has one, raise it for the sandbox through the\n definition's top-level `env:` (it reaches every service) rather than\n adding retries to your actions.\n\nWHERE LOGINS ARE SHOWN\n\n Persona logins (email + generated password) are surfaced by\n `cofactor sandbox show <id>` and by `cofactor sandbox preview` for PR\n previews. They are never printed in a PR comment — a PR comment only\n links to where they can be looked up, keeping generated passwords out of\n anything that ends up in GitHub's own history. Cofactor's test runs sign\n in with them automatically, as the application's persona of the same\n name.\n\n\nNATIVE WORKFLOW LOGIN NODES\n\n Sandboxes v2 uses `authentication.run` to mint a fresh browser session for\n the persona named by each native workflow login node. Read\n `cofactor sandbox guide v2 authentication` for the complete script contract,\n cookie and localStorage formats, and how to keep provider-login journeys\n clean and unauthenticated at the start.\n","Native workflow login nodes use the persona declared in this sandbox definition. Declare `authentication.run` when your sandbox needs native persona login. Existing definitions can use personas in prepare, seeds and actions without this script; a native login without it fails with `script_not_declared`. Each run gets its own copy, so Cofactor asks the copy's `authentication.run` script to create an application session for the requested persona. The script receives the prepared copy environment and `COFACTOR_AUTH_PERSONA`; it returns Playwright storage state as JSON on stdout. The cookies must be for the copy's public origin (sign in through the public URL, as below), not localhost. The example needs no language runtime: a POSIX shell, `curl`, `jq` and `awk`:\n\n```yaml\npersonas:\n admin: { email: admin@example.test }\n member: { email: member@example.test }\nauthentication:\n run: sandbox/auth.sh\n```\n\n```bash\n#!/bin/sh\nset -eu\n\npersona=${COFACTOR_AUTH_PERSONA:?missing persona}\n# This persona's email and generated password; fails if either is missing.\nlogin=$(printf '%s' \"$COFACTOR_PERSONAS\" | jq -ec --arg p \"$persona\" \\\n '.[$p] | {email, password} | select((.email | type) == \"string\" and (.password | type) == \"string\")')\nemail=$(printf '%s' \"$login\" | jq -r .email)\njar=$(mktemp)\ntrap 'rm -f \"$jar\"' EXIT\n\n# Sign in through the copy's public HTTPS API. This preserves Secure and\n# __Host- cookie behavior; localhost HTTP is not suitable for those cookies.\nprintf '%s' \"$login\" | curl -fsS -c \"$jar\" -H 'content-type: application/json' \\\n --data-binary @- \"$API_URL/api/auth/login\" >/dev/null\n\n# Check the resulting identity before giving the session to the browser.\ncurl -fsS -b \"$jar\" \"$API_URL/api/auth/me\" | jq -e --arg email \"$email\" '.user.email == $email' >/dev/null\n\n# Convert the cookie jar to Playwright storage-state JSON. Curl's Netscape\n# format does not carry SameSite, so adapt this field to your app's cookie.\nawk -F '\\t' '\n { httponly = \"false\" }\n /^#HttpOnly_/ { sub(/^#HttpOnly_/, \"\"); httponly = \"true\" }\n /^#/ || NF < 7 { next }\n { print $6 \"\\t\" $7 \"\\t\" $1 \"\\t\" $3 \"\\t\" $5 \"\\t\" httponly \"\\t\" $4 }\n' \"$jar\" | jq -Rn '{\n cookies: [inputs | split(\"\\t\") | {\n name: .[0], value: .[1], domain: .[2], path: .[3], expires: (.[4] | tonumber | if . == 0 then -1 else . end),\n httpOnly: (.[5] == \"true\"), secure: (.[6] == \"TRUE\"), sameSite: \"Lax\"\n }],\n origins: []\n}'\n```\n\nIf the app stores its access token in local storage, include\nan `origins` entry for the exact copy origin, with `{name, value}` pairs in\n`localStorage`. Do not print secrets to stderr, write state to a normal action,\nor use a fixed browser profile. Cofactor validates the returned state against\nthe current copy's public origins and keeps it private to that execution.\n\nTHIRD-PARTY IDENTITY PROVIDERS\n\nFor ordinary application tests, create the app's session directly. Use the\nsandbox-only login API, a signed test token accepted only by the copy, an SDK,\nor the provider's emulator-backed token exchange. The auth action should\nfinish with the application's own session cookie or local-storage token. A\nflow that tests login itself must begin clean: retain its application login\nnode with `performLogin: false` to select the app and environment, without\nseeding or preloading an app session. Then navigate to the app and traverse\nits real sign-in UI against the declared provider emulator.\n\nGoogle OAuth requires interception on both sides. Cofactor intercepts the\nbrowser authorization request, including an app HTTP redirect to Google,\nand returns an emulated code to the copy's callback. The declared sandbox\nemulator handles the app's token exchange and user-info requests. Verify\nthe complete callback and resulting app identity; an intercepted browser\nredirect alone does not prove the server handshake works.\n\nGOOGLE CALLBACK CONFIGURATION\n\n Supply the current copy's callback using the app's existing configuration:\n params:\n api_url: { port: api }\n env:\n GOOGLE_CLIENT_ID: sandbox\n GOOGLE_CLIENT_SECRET: sandbox\n GOOGLE_REDIRECT_URI: ${params.api_url}/auth/callback\n emulation:\n google-oauth: {}\n\n This is a fragment: declare the public api port, use your app's actual\n variable names and callback path, and set reload_on_prepare: true on the\n backend calling Google. An app whose callback comes from configuration\n can use this without auth code changes. A hardcoded production callback\n must become configurable. No per-copy Google Console registration is\n needed for the emulated path; real Google login keeps Google's rules.\n\n Do not add live: true merely to avoid service restarts. That optimization\n requires the app to re-read the live value per request; a normal {port}\n param with reload_on_prepare works for apps that read env at startup.\n\n Google needs both browser authorization interception and sandbox-side\n token/user-info emulation. Verify the app callback and a protected request,\n not just that the browser received an emulated code. App HTTP redirect\n interception has been proved in dev and is pending deployment; confirm\n support in the target environment before relying on it.\n\n The default Google profile is Test User (testuser@example.com). Repeated\n Google sign-ins in one copy use that identity; the Login node's persona\n does not select the Google user. Copies still isolate app sessions/data.\n The public definition has no per-persona Google identity setting today.\n\n If Google is reached through hosted Auth0, Clerk or another intermediary,\n this Google emulator does not emulate that intermediary. Inspect where\n token exchange occurs. Prove the customer's actual library and trust setup;\n do not disable token verification or alter production auth to pass a test.\n Non-Node clients must trust /etc/emulation/ca.crt through their runtime's\n supported mechanism. This native Login-node contract does not inject\n authentication into an external Playwright suite.\n\nREFERENCES\n\n https://docs.runcofactor.com/reference/sandbox-definition#authentication\n https://docs.runcofactor.com/reference/sandbox-emulation#google-oauth-and-third-party-login\n cofactor sandbox guide v2 personas\n cofactor sandbox guide v2 emulation\n cofactor sandbox guide v2 ports\n","An environment's `env` block (under `environments.<name>.env`) overrides\nenvironment variables for exactly three things:\n\n - running services that declare `reload_on_prepare: true` — they are\n restarted by `prepare` (and by an in-place move to a new commit), so\n they always see the current chain's merged env;\n - `lifecycle.prepare` commands, run fresh on every prepare;\n - actions, which run with the prepare env plus that call's\n `INPUT_<NAME>` values.\n\n environments:\n beta-features:\n extends: default\n description: Same as default, with the timeline view turned on.\n env:\n FEATURE_FLAGS: timeline\n\nIt does NOT reach:\n\n - `lifecycle.build` steps. Build steps run once into the repo-base layer,\n before any environment is chosen, so an environment's `env` does not\n exist at build time. A value your build bakes into its output (e.g. a\n front-end bundle's API URL or feature flags) can't vary by\n environment: read it at serve time instead, or set it with a seed\n action.\n - services that don't declare `reload_on_prepare`. A service without it\n only ever sees the definition's top-level `env` and layer-time param\n values (from when its layer was built) — it is not restarted when a\n sandbox is prepared, so a per-environment override would never take\n effect for it.\n\nAn environment's `env` values are used as written: `${params.name}` is\nsubstituted only in the definition's top-level `env`.\n\n`env` MERGE ORDER\n\n Composition follows the same parent-then-child rule as seeds (see\n `environments`): the resolved chain's env is the parent's `env` merged\n first, then the child's own, so a child environment can override a\n specific key its parent sets without having to repeat the rest.\n\nRELATION TO THE DEFINITION'S TOP-LEVEL `env`\n\n The definition's top-level `env` (outside any environment) is the base:\n fixed values and `${params.name}` substitutions that apply regardless of\n which environment is claimed — `DATABASE_URL`, `PUBLIC_URL` and so on in\n the example app. An environment's `env` is layered on top of that base\n for the reachable surfaces above, not a replacement for it.\n","Emulation makes third-party services deterministic and inspectable: instead\nof a sandbox's outbound calls to an email provider, Google, Jira or Slack\nleaving the VM, they're intercepted by an in-sandbox emulator that behaves\nlike the real thing well enough for tests, and lets a test look at what\nwas sent.\n\nPLUGINS\n\n Declaring a plugin id under `emulation:` turns on its interception and\n adds it to what `envs` reports.\n emulation:\n email: {}\n\n Available plugin ids:\n email Intercepts the HTTP send APIs of several providers\n (SES, SendGrid, Resend, Postmark, Mailgun) so outbound\n mail is captured instead of actually sent. SES is\n intercepted in us-east-1 unless you list others:\n `email: { sesRegions: [eu-west-1] }`; add other hosts\n with `extraDomains`. Mail through a host that isn't\n intercepted leaves the sandbox for real.\n SMTP (nodemailer, Django's SMTP backend, Action\n Mailer…) is not intercepted. Leave SMTP unconfigured\n in the sandbox so the app sends nothing, or ship an\n SMTP catcher such as Mailpit in the image, run it as\n one of your `services`, and point the app's SMTP host\n at 127.0.0.1.\n google-oauth Intercepts Google's OAuth endpoints, for testing login\n flows without a real Google account.\n jira Intercepts the Jira Cloud REST API of your site:\n `jira: { tenant: acme }` serves acme.atlassian.net.\n `tenant` is required (validate doesn't check it). It has\n no operations.\n slack Intercepts Slack's API for posted-message flows.\n\n `cofactor sandbox envs` lists each declared plugin's operations.\n\nHOW INTERCEPTION WORKS\n\n Inside the sandbox, the domains a plugin owns (e.g.\n `email.us-east-1.amazonaws.com` for the email plugin's SES routes) are\n resolved via `/etc/hosts` to the emulator instead of the real internet.\n The emulator serves those domains over real TLS, using a CA generated\n when the version's ready snapshot is built (every copy shares it). It\n survives an emulator restart, so services that read\n `NODE_EXTRA_CA_CERTS` once at startup don't break when the emulator\n restarts. Services with `reload_on_prepare: true`, prepare commands and\n actions get `NODE_EXTRA_CA_CERTS=/etc/emulation/ca.crt` so they trust\n that CA. A service WITHOUT `reload_on_prepare` never gets it (nor any\n emulation env): its calls to an emulated domain fail TLS. Any service that\n talks to an emulated provider must be `reload_on_prepare`.\n\n NO PASSTHROUGH. An unmatched path on an emulated domain doesn't\n fall through to the real provider — it returns 501 and the attempt is\n recorded, so a route the app calls that no plugin has implemented yet\n fails loudly and visibly instead of silently reaching a real third\n party.\n\n The emulator depends on `openssl` for certificate generation. For a\n GitHub-delivered app, the platform installs it before your `setup`\n runs, so a definition doesn't need to install it itself; a pushed image\n that declares `emulation` must include it (`apt-get install -y\n openssl`), or the emulator never turns healthy and prepare fails.\n\nOPERATIONS\n\n Each plugin declares typed operations — the same shape as an action\n (id, description, typed input, typed output) — that appear right alongside\n your definition's own actions, named `<plugin>.<op>`. They run exactly\n like an action: `cofactor sandbox run <id> <plugin.op>`, or as a seed\n step (`action: email.sent-emails`). Cofactor calls the emulator's control\n plane directly rather than shelling into the sandbox for these.\n\n Today: `email.sent-emails` — no input, returns every email captured since\n boot or the last reset. In the example app, it verifies that\n `invite-user` actually sent the invitation:\n cofactor sandbox run <id> invite-user \\\n --input workspace_id=<acme.workspace_id> \\\n --input email=new@example.test --input role=member --input as=owner\n cofactor sandbox run <id> email.sent-emails\n\n\nAUTHENTICATION AND PROVIDER LOGIN\n\n For normal persona-based flows, have `authentication.run` create the\n application's session; see `cofactor sandbox guide v2 authentication`.\n For a flow testing Google login, start without that pre-authenticated state\n and use the `google-oauth` emulator through the app's actual login button.\n This avoids treating a successful pre-authenticated landing as proof of the\n provider redirect and callback.\n\n For Google callback configuration, app-code compatibility and the default\n single fixture identity, read `cofactor sandbox guide v2 authentication`.\n Google browser authorization and the backend token/user-info handshake both\n need to work. App HTTP redirect interception is pending rollout; verify\n the full signed-out journey in the target environment. Hosted identity\n intermediaries are not covered by declaring google-oauth alone.\n\n Reference:\n https://docs.runcofactor.com/reference/sandbox-emulation#google-oauth-and-third-party-login\n","An app that stores files (uploads, attachments, exports) can have an\nemulated S3 or Google Cloud Storage server in every sandbox, instead of\nreaching a real bucket. You declare the provider and the buckets; the\nsandbox runs the server, creates the buckets, and gives your app the\nstandard client settings for that provider.\n\nDECLARING IT\n\n object_storage:\n provider: s3 # or gcs\n buckets: [attachments, exports]\n\n - One provider per app. Bucket names are 3–63 lowercase letters,\n digits, `-` and `.`, starting and ending with a letter or digit, with\n no `..` and not shaped like an IP address. A GCS bucket can't be\n named `storage`, `upload`, `download` or `batch`.\n - The buckets exist before your first prepare command runs.\n - Declaring it adds an implicit public port named `object-storage` (you\n don't declare it yourself, and you can't name a port of your own\n `object-storage`). It counts toward the 8-port limit.\n - The server listens on port 9000 (S3), or 4443 and 4444 (GCS);\n a port of your own can't use those numbers, and a service of your own\n can't be named `object-storage` or `object-storage-front`.\n\n For a pushed image, `cofactor sandbox push` adds the declared provider's\n server to the image; you don't install anything.\n\nYOUR DATA IN EACH SANDBOX\n\n Each sandbox gets its own copy of the buckets. Files your seeds put there\n (through an action, below) are captured in the ready snapshot with the\n rest of the sandbox, so every claim starts with them. A test that\n uploads a file changes only its own sandbox, and the data is gone when\n the sandbox is destroyed.\n\nTHE ENV YOUR APP GETS\n\n Every service, prepare command and action runs with the provider's\n standard client variables:\n\n s3 AWS_ENDPOINT_URL_S3 http://127.0.0.1:9000\n AWS_ACCESS_KEY_ID cofactor-sandbox\n AWS_SECRET_ACCESS_KEY cofactor-sandbox-secret\n AWS_REGION us-east-1\n\n gcs STORAGE_EMULATOR_HOST http://127.0.0.1:4443\n GOOGLE_CLOUD_PROJECT sandbox\n\n The credentials are fixed and the same in every sandbox. If your app\n reads different names (say `ATTACHMENTS_ENDPOINT`), set them in your\n definition's `env:`, which also overrides any of the above (an\n environment's `env:` too; your image's own ENV does not):\n env:\n ATTACHMENTS_ENDPOINT: http://127.0.0.1:9000\n ATTACHMENTS_BUCKET: attachments\n\nS3: CLIENT SETTINGS\n\n AWS SDK v3 (JavaScript) and boto3 1.28 or later read\n `AWS_ENDPOINT_URL_S3` themselves. Your app must use PATH-STYLE\n addressing (`http://host/bucket/key`, not `http://bucket.host/key`):\n\n // AWS SDK v3\n new S3Client({ forcePathStyle: true })\n\n # boto3\n boto3.client(\"s3\", config=Config(s3={\"addressing_style\": \"path\"}))\n\n Older SDKs that don't read `AWS_ENDPOINT_URL_S3`: pass it as the\n client's endpoint yourself.\n\nGCS: CLIENT SETTINGS\n\n Point your client at `STORAGE_EMULATOR_HOST`. Google's Node\n (`@google-cloud/storage`) and Python (`google-cloud-storage`) clients\n read it themselves and skip authentication when it is set, so `new\n Storage()` and `storage.Client()` work with no credentials. On an older\n Python client that doesn't, pass both yourself:\n storage.Client(\n credentials=AnonymousCredentials(),\n client_options={\"api_endpoint\": os.environ[\"STORAGE_EMULATOR_HOST\"]},\n )\n For another language, set your client's endpoint to\n `STORAGE_EMULATOR_HOST` and give it no (anonymous) credentials.\n\nLINKS A BROWSER OPENS: THE PUBLIC URL\n\n `STORAGE_EMULATOR_HOST` and `AWS_ENDPOINT_URL_S3` are only reachable\n inside the sandbox. A presigned (S3) or signed (GCS) URL your app hands\n to a browser must use the `object-storage` port's public URL instead.\n Its origin is a `{port}` param, which you pass to your app:\n params:\n storage_url: { port: object-storage }\n env:\n STORAGE_PUBLIC_URL: ${params.storage_url}\n\n The service that reads it needs `reload_on_prepare: true`, like any\n service that reads a param (see the `upload` topic).\n\n Anyone with that URL reaches the storage: S3 with the fixed credentials\n above, GCS with no authentication at all. It holds the sandbox's test\n data, like every other port of the sandbox, so never put real data in\n it.\n\n S3: sign the URL against the public origin with a second client used\n only for presigning (signing makes no network call), and keep every\n real read and write on the default one:\n const presigner = new S3Client({\n endpoint: process.env.STORAGE_PUBLIC_URL,\n forcePathStyle: true,\n });\n const url = await getSignedUrl(presigner,\n new GetObjectCommand({ Bucket: 'attachments', Key: key }));\n The name is yours: an app that already has such a setting (e.g.\n `S3_PRESIGN_PUBLIC_ENDPOINT: ${params.storage_url}`) just sets it. The\n public URL preserves the request's Host, so the signature holds, and\n browser uploads through a presigned PUT are allowed from any origin.\n\n GCS: generate signed URLs with the public origin as their host (Node:\n the `host` option of `file.getSignedUrl`; Python: the\n `api_access_endpoint` argument of `blob.generate_signed_url`). Signing\n still needs a credential that can sign, such as a service account key\n file; the emulator doesn't check signatures, so give the sandbox a\n throwaway key, never a real one. Signed downloads and signed uploads\n work. A browser must upload in ONE signed PUT: a resumable upload\n started from a browser is answered with an address inside the sandbox\n (127.0.0.1) that the browser can't reach, so resumable uploads work\n only from inside the sandbox (your app's server, an action).\n\nSEEDING FILES WITH AN ACTION\n\n An action can put files in a bucket, so a seed can start every claim\n with them. If your app records each file in its database (an attachment\n row pointing at the object), go through the app's own upload API\n instead, so the row exists too — the same rule as any other seed (see\n `seeds-and-actions`). For a file the app only reads (a logo, an import\n template), write it straight to the bucket. The examples need only\n curl (7.75 or later, for `--aws-sigv4`):\n\n #!/usr/bin/env bash\n # sandbox/actions/add-template.sh (S3)\n set -euo pipefail\n curl -fsS --aws-sigv4 \"aws:amz:$AWS_REGION:s3\" \\\n --user \"$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY\" \\\n -H 'content-type: text/csv' -T sandbox/files/import-template.csv \\\n \"$AWS_ENDPOINT_URL_S3/exports/templates/import-template.csv\" >&2\n echo '{\"key\": \"templates/import-template.csv\"}'\n\n #!/usr/bin/env bash\n # sandbox/actions/add-template.sh (GCS)\n set -euo pipefail\n query='uploadType=media&name=templates/import-template.csv'\n curl -fsS -X POST -H 'content-type: text/csv' \\\n --data-binary @sandbox/files/import-template.csv \\\n \"$STORAGE_EMULATOR_HOST/upload/storage/v1/b/exports/o?$query\" >&2\n echo '{\"key\": \"templates/import-template.csv\"}'\n\nLIMITS\n\n - One provider per app.\n - GCS: one upload request carries at most 128 MB, a signed PUT from a\n browser included.\n - GCS: a browser uploads only through a single signed PUT (above).\n - The data lives only in the sandbox: nothing is copied from or to a\n real bucket, and it goes when the sandbox is destroyed. It is\n reachable on the public `object-storage` URL: test data only.\n","A sandbox can declare several ports, and each one gets its own public URL:\n ports:\n web: { port: 3000, access: public }\n api: { port: 3001, access: public }\n\nWHY SEVERAL PORTS\n\n Bundling web and API into one sandbox means a single claimed environment\n serves both UI flows (driving the browser against `web`'s URL) and API\n sequences (calling `api`'s URL directly) against the *same* underlying\n data — the workspace, projects and tasks a UI test just created through\n the browser are the same rows an API test can immediately query.\n There's no separate \"API sandbox\" to keep in sync with the \"UI sandbox.\"\n\n`access`\n\n `public` gets a real, routable URL a test or a human can hit from\n outside the sandbox. `private` stays internal — reachable by other\n services in the sandbox, not from outside it.\n\n`{port}` PARAMS\n\n A `params` entry of the form `{port: <name>}` resolves to that port's own\n public URL, so the app (and its build/prepare steps) can be handed its\n own public origin rather than hardcoding it:\n params:\n web_url: { port: web }\n api_url: { port: api }\n env:\n PUBLIC_URL: ${params.web_url}\n API_URL: ${params.api_url}\n\n Each `{port}` param gives the app its own public URL for exactly the port\n it names — `web_url` always resolves to `web`'s URL, `api_url` to `api`'s,\n and so on for however many ports a definition declares.\n\nLIMITS\n\n At most 8 ports total per sandbox — a platform limit. That count\n includes the implicit `emulation` port that gets added automatically the\n moment any plugin is declared under `emulation:` (see the `emulation`\n topic) — it isn't optional and isn't something you declare yourself, but\n it does count against the 8. `emulation` is also a reserved port name: you\n can't declare your own port called `emulation`. The same goes for the\n implicit `object-storage` port that `object_storage` adds (see the\n `object-storage` topic). `cofactor sandbox\n validate` checks both the reservation and the 8-port cap.\n\n Ports 80 and 443 are reserved for the emulator whenever emulation is on —\n a definition can't put its own `web` or `api` port there in that case.\n\nOAUTH CALLBACKS\n\n Map the callback through the application's existing configuration, e.g.\n GOOGLE_REDIRECT_URI: ${params.api_url}/auth/callback. Use the port that\n serves the actual callback, not necessarily the web port. Services that\n read this environment variable at startup need reload_on_prepare: true.\n Read `cofactor sandbox guide v2 authentication` before using live: true or\n configuring Google emulation.\n\n https://docs.runcofactor.com/reference/sandbox-emulation#callback-configuration-and-app-changes\n","Two different checks, at two different costs.\n\n`cofactor sandbox validate [file]`\n\n Offline — no sandbox, no claim. Parses the YAML, runs the full\n schema, and cross-checks everything static analysis can catch: unknown\n actions or operations referenced by a seed, `${id.key}` references to a\n later step or an undeclared output key, an `extends` cycle, a duplicate\n step id across a chain, an unknown emulation plugin, port 80/443 declared\n while emulation is on, and more. Every issue is reported with a dotted\n path to exactly where it is, so a broken definition doesn't need a claim\n to find the problem:\n environments.default.seeds[0].input.workspace_id: references step \"acme\", which has not run yet (or does not exist)\n environments.free-plan.extends: unknown environment \"defualt\"\n ports.emulation: \"emulation\" is a reserved port name\n\n Run this before pushing any change to `cofactor.sandbox.yaml` — it's\n instant and catches most authoring mistakes before they cost a real\n claim.\n\n`cofactor sandbox verify-env --application A [--environment E]`\n\n Actually claims each requested environment of the application's current\n version (or every environment in the definition, if none is named) in turn:\n claim, wait for ready, report every seed step's output and timing, then\n destroy. This is the only way to prove an environment's seed chain\n actually runs end to end — `validate` can tell you a reference is\n well-formed, but only a real claim proves the referenced action produces\n the output at runtime.\n\nWHEN A SEED FAILS\n\n Seeds run once per version (commit or push) and environment, while its\n ready snapshot is built. Any step's failure fails that snapshot — the\n builder is destroyed, so there's no partially-seeded sandbox left around\n to poke at — and the claims waiting on it fail with that error. The next\n claim builds the snapshot again, so a seed that fails every time keeps\n failing until a fix is committed or pushed (an infrastructure failure,\n by contrast, is retried).\n\n To debug a failing seed:\n 1. Read the error — the claim (or `verify-env`) reports which step\n failed, its action, and the script's stderr (everything a script\n prints besides its final `{...}` result line goes to stderr, so\n this is where the real error message is).\n 2. Claim a sandbox **without** the failing environment (or on an\n environment that stops short of the failing step) so you have a\n live sandbox to work on.\n 3. Run the failing action by hand against that live sandbox with\n `cofactor sandbox run <id> <action> --input k=v ...`, using the\n same inputs the seed step would have resolved (substitute in the\n actual values for any `${id.key}` references by hand). This lets\n you iterate on the action's script without repeatedly re-claiming.\n 4. Once the action succeeds standalone, fix the seed definition or the\n action script and rerun `verify-env` to confirm the full chain\n works.\n","All commands are under `cofactor sandbox`, and every sandbox belongs to an\napplication (`--application <name|id>`). `--env` is the CLI's *global*\nflag for which Cofactor stage to talk to (`dev` / `prod`) — it has nothing\nto do with a sandbox's environment, which is always `--environment`.\n\ncofactor sandbox validate [file] [--upload]\n Offline schema + cross-check of a `cofactor.sandbox.yaml`. Defaults to\n the definition in the current directory if `file` is omitted. `--upload`\n checks a definition for pushed images (no `setup`, no `lifecycle.build`);\n without it, one built from a GitHub checkout. See the `verify` topic for\n the issue format.\n\ncofactor sandbox connect --application A --repo owner/name [--dir D]\n Builds the application's sandboxes from a GitHub repository: the\n `cofactor.sandbox.yaml` under `--dir` (default: the repo root) at each\n commit. Its environments become the application's sandbox environments\n and its personas the application's personas.\n\ncofactor sandbox push --application A <image> [--definition F] [--tag T]... [--wait]\n For an application that gives Cofactor no repository access: pushes a\n built image (with its `cofactor.sandbox.yaml`) as the application's next\n version. See the `upload` topic.\n\ncofactor sandbox envs --application A [--json]\n The application's delivery (GitHub or pushed images), its current\n version, recent pushes, sandbox environments, and what its definition\n offers: every environment (descriptions, seeds), ports, personas,\n actions and emulator operations (with their schemas). This is what an\n agent reads to decide which environment to claim and how to call each\n action once it has a sandbox.\n\ncofactor sandbox claim --application A [--environment E] [--at V] [--wait]\n Claims a sandbox: a fresh copy of the ready snapshot for the version and\n environment (built first — build, `prepare`, seeds — if it doesn't exist\n yet), given its own URLs and logins by `prepare`. A version is a commit\n SHA for a GitHub-delivered application, or a push id or tag for a pushed\n one. Without `--at`: for GitHub, the default branch's current ready\n snapshot of the environment (the periodic refresh keeps it within a few\n hours of the head while the environment is in use), else the head; for\n pushes, the newest push. `--environment` defaults to `default`. `--wait`\n blocks until the sandbox is ready (or failed) instead of returning\n immediately with a claim id you'd otherwise have to poll with `show`.\n\ncofactor sandbox show <id>\n Current status, per-port URLs, persona logins (decrypted — this is where\n they're meant to be read from, never a PR comment), seed outputs, and\n the available actions and operations.\n\ncofactor sandbox run <id> <action|plugin.op> [--input k=v ...] [--json-input '{...}']\n Runs a definition action or an emulator operation (named `<plugin>.<op>`,\n e.g. `email.sent-emails`) against a claimed sandbox, waits for it, and\n prints its output. `--input` is repeatable and coerced to the action/\n operation's declared type (string/number/integer/boolean/persona);\n `--json-input` passes the whole input as JSON, overriding `--input`.\n Invalid input prints the action or operation's input schema instead of\n running it, so you can see what's expected.\n\ncofactor sandbox destroy <id>\n Tears the sandbox down. Always do this when a claimed sandbox is no\n longer needed — it isn't reclaimed automatically mid-session.\n\ncofactor sandbox verify-env --application A [--environment E]\n Claim → wait → report seed outputs and timings → destroy, for each\n environment of the current version (or the one named). The end-to-end\n proof that an environment's seed chain actually works, not just that it's\n well-formed. See the `verify` topic.\n\ncofactor sandbox preview --application A --pr N\n A pull request's preview of a GitHub-delivered application: status, URLs\n and the logins its PR comment deliberately leaves out.\n\ncofactor sandbox guide v2 [topic]\n Prints this guide. Omit `topic` to print every section; name one (e.g.\n `upload`) to print just that topic.\n\nTHE CORE LOOP\n\n validate → (connect | push) → envs → claim --wait → run (actions/\n operations, as many as the test needs) → destroy. Always validate a\n changed definition before pushing it — see the `verify` topic for why.\n","PUSHED IMAGES: SANDBOXES WITHOUT REPOSITORY ACCESS\n\nAn application whose team gives Cofactor no access to its repository\ndelivers its sandboxes as built images: build your app's image as usual,\nthen push it with its `cofactor.sandbox.yaml` whenever you have a new\nbuild (nightly, per release, per merge — your cadence, your CI or your\nlaptop). Each push is a version of the application; claims use the newest\none unless they name another with `--at <push id or tag>`. A tag names the\nnewest ready push that carries it: pushing `--tag nightly` again moves the\ntag to the new push, and older pushes stay claimable by their id.\n\n docker buildx build --platform linux/amd64 -t tracker:sandbox --load .\n cofactor sandbox validate --upload cofactor.sandbox.yaml\n cofactor sandbox push --application \"Project Tracker\" tracker:sandbox \\\n --definition cofactor.sandbox.yaml --tag 2026-10-01 --wait\n cofactor sandbox envs --application \"Project Tracker\"\n cofactor sandbox claim --application \"Project Tracker\" --environment default --wait\n\n`push` adds Cofactor's layers to your image (its sandbox helper as the\nentrypoint, plus the object storage server when the definition declares\n`object_storage`) and a small layer with your definition, reads the image\nout of your local Docker (`docker save`), and uploads only the layers\nCofactor doesn't already have for your organization (see FAST RE-PUSHES).\nCofactor boots it once until every service is healthy (`--wait` waits for\nthat). Then each environment in the definition\nbecomes a sandbox environment of the application, and each persona one of\nits personas. The first claim of an environment builds its ready snapshot\n(prepare + seeds, about a minute); later claims copy it in seconds.\n\nWHAT THE IMAGE MUST BE\n\n - linux/amd64. On an Apple Silicon Mac, build with\n `--platform linux/amd64`; `push` refuses any other platform.\n - glibc-based with bash (Debian, Ubuntu, `python:*-slim`, `node:*`\n non-alpine…). Alpine/musl and distroless images do not work: the\n helper is a glibc binary and every command runs through `bash -lc`.\n If only your final stage is Alpine, keep the build stages and change\n that one `FROM` to its Debian equivalent (`node:24-alpine` →\n `node:24-bookworm-slim`, `python:3.12-alpine` → `python:3.12-slim`),\n with `apt-get install` in place of `apk add`. Native dependencies\n compiled on Alpine don't run on glibc: install them in a glibc stage\n rather than copying `node_modules` or wheels out of an Alpine one.\n - A WORKDIR other than `/`. Services, prepare commands and actions run\n from it, and the helper gives it to the sandbox user (below).\n - Built already. There is no build step in a pushed sandbox: everything\n the app needs (dependencies, compiled assets, a database server if it\n uses one) is in the image.\n\nFAST RE-PUSHES\n\n A push uploads only the layers that changed since an earlier push (in\n the last 30 days); `push` says how much it sent and how much Cofactor\n already had. Two things keep a re-push small:\n\n - Order the Dockerfile so what changes most comes last: system packages,\n then the dependency manifest and lockfile and the install step, then\n your source. A code change then rebuilds, and uploads, only the last\n layers:\n FROM python:3.12-slim\n RUN apt-get update && apt-get install -y --no-install-recommends curl jq\n WORKDIR /app\n COPY requirements.txt .\n RUN pip install -r requirements.txt\n COPY . .\n - In CI, keep the build cache between runs. A runner that starts with an\n empty cache rebuilds every step, and every rebuilt layer is new to\n Cofactor even when nothing in it changed. With GitHub Actions:\n docker buildx build --platform linux/amd64 --load -t app:sandbox \\\n --cache-from type=gha --cache-to type=gha,mode=max .\n (or `type=registry` with a cache image of your own).\n\n CI authenticates with an API key (`COFACTOR_API_TOKEN`). Give it only\n what the job does: `sandbox:read`, `sandbox:publish` and `property:read`\n to push; add `sandbox:create` and `sandbox:delete` to claim and destroy,\n and `sandbox:run` to run actions.\n\n For pull requests, push each with its own tag and claim it by that tag:\n cofactor sandbox push --application \"Project Tracker\" app:sandbox --tag pr-123 --wait\n cofactor sandbox claim --application \"Project Tracker\" --environment default --at pr-123 --wait\n A pull request's push shares every layer below the source with main's.\n\nHOW YOUR IMAGE RUNS IN A SANDBOX\n\n - Its ENTRYPOINT and CMD are NOT used. Start each process yourself with a\n `services.<name>.run` command (often your entrypoint script).\n - Its ENV and WORKDIR ARE applied, as `docker run` would. Except PATH:\n commands run through `bash -lc` as uid 1000, and Debian's\n `/etc/profile` resets a non-root user's PATH, so an `ENV PATH=...`\n line doesn't reach them. Call such tools by\n absolute path (Postgres: `/usr/lib/postgresql/<n>/bin/postgres`) or\n set PATH in the service's own script.\n - Leave the image's USER as root (or unset). The sandbox starts as root\n and drops to uid 1000 itself; an image that ended on another `USER`\n has failed to boot with a permission error on `/etc/cofactor`.\n - Everything — services, prepare commands, actions — runs as an\n unprivileged user with uid 1000 (the sandbox adds that account if your\n image has none). The WORKDIR tree is made theirs; anything the app\n writes must be under the WORKDIR or `/tmp`, not elsewhere in a root-\n owned path.\n - One machine, no Docker inside: a database is a process you run as a\n service (Postgres installed in the image, or SQLite), not a container.\n For S3 or GCS, declare `object_storage` and the sandbox runs the\n server for you (see the `object-storage` topic).\n - Each public port gets its own https URL, unique per sandbox. Its\n origin is a param — `params: { web_url: { port: web } }` — that you\n pass to the app through `env` (e.g. `PUBLIC_URL: ${params.web_url}`).\n\nTHE DEFINITION FOR PUSHED IMAGES\n\n The same `cofactor.sandbox.yaml` as for a repository (see the\n `definition` topic), minus everything about building: no `setup`, no\n `lifecycle.build`, no `after_build`. A minimal one for the example app,\n built as one image that serves its UI and API on port 8080:\n\n version: 1\n resources: { vcpus: 2, memory_mib: 4096 }\n ports:\n web: { port: 8080, access: public }\n params:\n web_url: { port: web }\n env:\n PUBLIC_URL: ${params.web_url}\n services:\n app:\n run: exec ./start.sh\n health: { http: 8080, path: /health }\n reload_on_prepare: true\n personas:\n owner: { email: owner@example.test }\n lifecycle:\n prepare:\n - bash sandbox/personas.sh\n actions:\n create-workspace:\n description: A new workspace, created by the given persona, who becomes its owner.\n run: sandbox/actions/create-workspace.sh\n input:\n name: { type: string, required: true }\n as: { type: persona, required: true }\n output:\n workspace_id: { type: string }\n environments:\n default:\n description: Workspace \"Acme\", owned by the owner persona.\n seeds:\n - id: acme\n action: create-workspace\n input: { name: Acme, as: owner }\n\n From here, add what your tests need, in the order \"WHAT TO BUILD FIRST\"\n in the `seeds-and-actions` topic gives: a persona per role, an\n environment per plan or flag combination, and actions for the core\n objects.\n\n Keep the file anywhere in your repo: `validate --upload <file>` and\n `push --definition <file>` read it from where you point them, and the\n push puts it in the image. Paths inside it (`run:` of actions, prepare\n commands) are relative to the WORKDIR, so ship your sandbox scripts in\n the image (e.g. `COPY sandbox/ sandbox/`).\n\n Services start when the image is first booted, BEFORE any sandbox exists,\n with params at placeholder values. A service that needs the sandbox's own\n values (its URL, a `{secret: true}` param) sets `reload_on_prepare: true`:\n it is started (and restarted) by each sandbox's prepare instead. In\n doubt, set it on your app's service.\n\n `health` passes when the http path answers below 400 (or the command\n exits 0) within 5 minutes of the service starting.\n\nPERSONAS: MAKING THE LOGINS WORK\n\n Cofactor generates a fresh password for every persona in every sandbox.\n Your `lifecycle.prepare` commands receive them as `COFACTOR_PERSONAS`, a\n JSON map `{ \"<persona>\": { \"email\": \"...\", \"password\": \"...\" } }`, and\n must make each login work in your app: create or update that user with\n that password (through your app's API, its CLI, or its database). A\n persona whose login doesn't work breaks every test that signs in as it.\n A persona's `email` is yours to choose: pick an account your app already\n has (its seed data) and set its password, or have prepare create it.\n Cofactor's test runs then sign in with these logins automatically, as\n the application's persona of the same name.\n\n Prepare runs while your app is up: the `reload_on_prepare` services are\n running and healthy (restarted first when their environment changed),\n the others have run since the image was booted. So write the logins\n somewhere the running app reads on every sign-in (its database, or its\n own API). If your app loads its accounts once at startup (a file or\n config read into memory), change it to read them per request, or the new\n passwords won't take. For an app whose logins are fixtures, a common fix\n is a small JSON file the prepare script writes from `COFACTOR_PERSONAS`\n and the app checks on each sign-in before its fixtures.\n\nACTIONS AND SEEDS\n\n An action is a script in the image with typed input and output (the\n `seeds-and-actions` topic covers designing them). Inputs arrive as\n `INPUT_<NAME>` env vars (a `persona` input carries the persona's name,\n e.g. `owner`), alongside the definition's `env:`, every param as\n `COFACTOR_PARAM_<NAME>`, and `COFACTOR_PERSONAS`. The script prints its\n result as a JSON object on the LAST stdout line that starts with `{`\n (send everything else to stderr). It runs inside the sandbox, so it\n reaches your app on localhost. An environment's `seeds` call actions in\n order, once per version, before the snapshot every claim copies;\n `${<step id>.<output>}` passes one step's output to a later step. Run\n one against a live sandbox with `cofactor sandbox run <id> <action>\n --input name=... --input as=owner`.\n\n An action that signs in as a persona and creates data through the app's\n own API (here a cookie-session JSON API on port 8080; the port, paths and\n JSON fields are the example app's, use your app's). It needs jq and\n curl, which slim base images lack: add them to your Dockerfile\n (`apt-get install -y --no-install-recommends jq curl`).\n\n #!/usr/bin/env bash\n # sandbox/actions/create-workspace.sh\n set -euo pipefail\n login=$(jq -c --arg p \"$INPUT_AS\" '.[$p] | {email, password}' <<<\"$COFACTOR_PERSONAS\")\n jar=$(mktemp)\n curl -fsS -c \"$jar\" -H 'content-type: application/json' \\\n -d \"$login\" http://127.0.0.1:8080/api/auth/login >&2\n id=$(curl -fsS -b \"$jar\" -H 'content-type: application/json' \\\n -d \"$(jq -nc --arg n \"$INPUT_NAME\" '{name: $n}')\" \\\n http://127.0.0.1:8080/api/workspaces | jq -er '.id')\n jq -nc --arg id \"$id\" '{workspace_id: $id}'\n\n\nTROUBLESHOOTING\n\n - `push` says the image is not linux/amd64, or its WORKDIR can't be\n used: rebuild as it says.\n - The push fails with \"Cofactor could not start this image\": a service\n exited or never turned healthy. The message ends with that service's\n log. To reproduce, open a shell in the image —\n docker run --rm -it --entrypoint bash <image>\n — and run that service's `run` command from the WORKDIR. In the\n sandbox it runs as uid 1000 with the WORKDIR made theirs; plain\n `docker run --user 1000` leaves the WORKDIR root-owned, so a write\n failure there is not the sandbox's.\n - A claim fails in prepare or a seed: the error carries the command's\n output. Fix the script, rebuild the image and push again.\n - To try prepare and actions before pushing, run the app as usual and\n call your scripts in it with the variables they read (a sandbox also\n sets the definition's `env:` and runs them as uid 1000):\n docker run -d --name try -p 8080:8080 <image>\n docker exec -e COFACTOR_PERSONAS='{\"owner\":{\"email\":\"owner@example.test\",\"password\":\"pw-1\"}}' try bash sandbox/personas.sh\n docker exec -e COFACTOR_PERSONAS='…' -e INPUT_NAME=Acme -e INPUT_AS=owner try bash sandbox/actions/create-workspace.sh\n (`docker exec` starts in the image's WORKDIR.)\n - `cofactor sandbox logs` and `diagnostics` are for the older sandboxes;\n for these, `cofactor sandbox show <id>` has the status and any error.\n - \"push the image again\": Cofactor's sandbox helper changed since that\n push; a fresh `cofactor sandbox push` picks up the current one.\n - A layer larger than 5 GB is refused; split or slim it.\n - Every push uploads the whole image: the build cache is missing (see\n FAST RE-PUSHES), or the Dockerfile copies the source before\n installing dependencies.\n","import type { GuideSection } from '../types.js';\n\nimport overview from './overview.txt';\nimport definition from './definition.txt';\nimport environments from './environments.txt';\nimport seedsAndActions from './seeds-and-actions.txt';\nimport personas from './personas.txt';\nimport authentication from './authentication.txt';\nimport env from './env.txt';\nimport emulation from './emulation.txt';\nimport objectStorage from './object-storage.txt';\nimport ports from './ports.txt';\nimport verify from './verify.txt';\nimport cli from './cli.txt';\nimport upload from './upload.txt';\n\nexport const SANDBOX_V2_GUIDE_SECTIONS: GuideSection[] = [\n { title: 'Sandboxes v2 Overview', slug: 'overview', content: overview },\n { title: 'Definition (cofactor.sandbox.yaml)', slug: 'definition', content: definition },\n { title: 'Environments (Flavors)', slug: 'environments', content: environments },\n { title: 'Seeds vs. Actions', slug: 'seeds-and-actions', content: seedsAndActions },\n { title: 'Personas', slug: 'personas', content: personas },\n { title: 'Authentication', slug: 'authentication', content: authentication },\n { title: 'Environment Variables (env)', slug: 'env', content: env },\n { title: 'Emulation', slug: 'emulation', content: emulation },\n { title: 'Object Storage (S3 or GCS)', slug: 'object-storage', content: objectStorage },\n { title: 'Ports', slug: 'ports', content: ports },\n { title: 'Pushed Images (No Repository Access)', slug: 'upload', content: upload },\n { title: 'Verification', slug: 'verify', content: verify },\n { title: 'CLI Reference', slug: 'cli', content: cli },\n];\n","import process from 'node:process';\nimport { readFileSync } from 'node:fs';\nimport type { EmulationPluginEntry, EmulationTemplateConfig } from '@chatsdet/emulation';\nimport { getAllPlugins, getPluginIds } from '@chatsdet/emulation/plugins';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest } from './cli-helpers.js';\nimport { writeRawOutput } from './brand.js';\n\n/** One configured plugin as enriched by GET .../emulation (descriptions only, never secret values). */\ntype EmulationCatalogEntry = {\n pluginId: string;\n name: string;\n version: string;\n enabled: boolean;\n envContract: Array<{\n logicalKey: string;\n strategy: string;\n sensitive: boolean;\n description?: string;\n }>;\n inboundEvents: Array<{ id: string; description?: string }>;\n actionInterfaces: Array<{ slug: string; implemented: boolean }>;\n};\n\nconst pad = (value: string, width: number): string => value.padEnd(width);\n\n/**\n * Read the template's stored emulation config + plugin catalog. The GET route\n * returns `{ ok, config, catalog }`, not a `data` wrapper. Falls back to the\n * empty default config when the template has none.\n */\nasync function fetchEmulation(\n apiUrl: string,\n token: string,\n templateId: string\n): Promise<{ config: EmulationTemplateConfig; catalog: EmulationCatalogEntry[] }> {\n const res = await apiRequest<{\n ok: boolean;\n error?: string;\n config?: EmulationTemplateConfig;\n catalog?: EmulationCatalogEntry[];\n }>(apiUrl, token, 'GET', `/org/environments/templates/${templateId}/emulation`);\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n // Fresh literal (not a shared module singleton) so callers/transforms can\n // never accidentally mutate a process-wide default.\n return { config: res.config ?? { enabled: false, plugins: [] }, catalog: res.catalog ?? [] };\n}\n\n/**\n * Write the full emulation config back. The PATCH route is a full replace of\n * `{ enabled, plugins[] }`, so every mutating command must read-modify-write\n * the whole object.\n */\nasync function patchEmulation(\n apiUrl: string,\n token: string,\n templateId: string,\n config: EmulationTemplateConfig\n): Promise<void> {\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'PATCH',\n `/org/environments/templates/${templateId}/emulation`,\n { enabled: config.enabled, plugins: config.plugins },\n { timeoutMs: 90_000 }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n}\n\n/** Reject a pluginId that is not a known emulation plugin, with the valid list. */\nfunction assertKnownPlugin(pluginId: string): void {\n const ids = getPluginIds();\n if (!ids.includes(pluginId)) {\n console.error(`Error: unknown plugin '${pluginId}'. Available: ${ids.join(', ')}`);\n process.exit(1);\n }\n}\n\nfunction readJsonFileOrExit(path: string, flag: string): unknown {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf-8');\n } catch (err) {\n console.error(\n `Error reading ${flag} file '${path}': ${err instanceof Error ? err.message : String(err)}`\n );\n process.exit(1);\n }\n try {\n return JSON.parse(raw);\n } catch (err) {\n console.error(\n `Error parsing ${flag} file '${path}' as JSON: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n process.exit(1);\n }\n}\n\n/**\n * Like `readJsonFileOrExit` but rejects anything that is not a plain JSON object\n * (arrays, strings, numbers, null all parse fine but would fail the server's\n * `z.record(...)` validation with a cryptic error). Used for `--config` and\n * `--env-mapping`; `--fixture` stays unrestricted since fixtures are arbitrary.\n */\nfunction readJsonObjectOrExit(path: string, flag: string): Record<string, unknown> {\n const parsed = readJsonFileOrExit(path, flag);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n console.error(\n `Error: ${flag} file '${path}' must contain a JSON object, not ${\n Array.isArray(parsed) ? 'an array' : typeof parsed\n }.`\n );\n process.exit(1);\n }\n return parsed as Record<string, unknown>;\n}\n\n// The PATCH route is a full replace, so each mutating command produces a fresh\n// EmulationTemplateConfig from the fetched one. These are pure so the\n// read-modify-write semantics can be tested without network/IO.\n\n/** Flip the master switch, leaving plugin entries untouched. */\nexport function withEmulationMaster(\n config: EmulationTemplateConfig,\n enabled: boolean\n): EmulationTemplateConfig {\n return { enabled, plugins: [...(config.plugins ?? [])] };\n}\n\n/**\n * Toggle one plugin entry's `enabled`. Enabling a not-yet-configured plugin\n * adds a bare entry; enabling any plugin also turns the master switch on.\n * Disabling assumes the entry exists (callers validate first for a friendly error).\n */\nexport function withPluginEnabled(\n config: EmulationTemplateConfig,\n pluginId: string,\n enabled: boolean\n): EmulationTemplateConfig {\n const plugins = [...(config.plugins ?? [])];\n const idx = plugins.findIndex((plugin) => plugin.pluginId === pluginId);\n if (idx === -1) {\n if (enabled) plugins.push({ pluginId, enabled: true, config: {} });\n } else {\n plugins[idx] = { ...plugins[idx], enabled };\n }\n return { enabled: enabled ? true : config.enabled, plugins };\n}\n\n/**\n * Upsert a plugin entry's config/fixture/envMapping, preserving any field not\n * provided in `patch` and the existing `enabled`. New entries default to\n * `enabled: false`: `set` only configures; activation is always explicit.\n */\nexport function withPluginConfig(\n config: EmulationTemplateConfig,\n pluginId: string,\n patch: {\n config?: Record<string, unknown>;\n fixture?: unknown;\n envMapping?: Record<string, string | string[]>;\n }\n): EmulationTemplateConfig {\n const plugins = [...(config.plugins ?? [])];\n const idx = plugins.findIndex((plugin) => plugin.pluginId === pluginId);\n const existing = idx === -1 ? undefined : plugins[idx];\n\n const merged: EmulationPluginEntry = {\n pluginId,\n enabled: existing?.enabled ?? false,\n config: patch.config !== undefined ? patch.config : (existing?.config ?? {}),\n };\n const fixture = patch.fixture !== undefined ? patch.fixture : existing?.fixture;\n if (fixture !== undefined) merged.fixture = fixture;\n const envMapping = patch.envMapping !== undefined ? patch.envMapping : existing?.envMapping;\n if (envMapping !== undefined) merged.envMapping = envMapping;\n\n if (idx === -1) plugins.push(merged);\n else plugins[idx] = merged;\n return { enabled: config.enabled, plugins };\n}\n\n/** Drop a plugin entry (callers validate existence first). */\nexport function withoutPlugin(\n config: EmulationTemplateConfig,\n pluginId: string\n): EmulationTemplateConfig {\n return {\n enabled: config.enabled,\n plugins: (config.plugins ?? []).filter((plugin) => plugin.pluginId !== pluginId),\n };\n}\n\nasync function handleEmulationGet(\n argv: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const positional = argv.filter((arg) => !arg.startsWith('-'));\n const templateId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox emulation get <template-id>');\n process.exit(1);\n }\n\n const { config, catalog } = await fetchEmulation(apiUrl, token, templateId);\n const available = getAllPlugins().map((plugin) => ({ id: plugin.id, name: plugin.name }));\n\n if (json) {\n writeRawOutput(JSON.stringify({ config, catalog, availablePlugins: available }, null, 2));\n return;\n }\n\n console.log(`Emulation: ${config.enabled ? 'ENABLED' : 'disabled'} (master switch)`);\n const configured = config.plugins ?? [];\n if (configured.length === 0) {\n console.log(' No plugins configured.');\n } else {\n console.log(` Configured plugins (${configured.length}):`);\n for (const plugin of configured) {\n const status = plugin.enabled ? '' : ' [disabled]';\n console.log(` ${pad(plugin.pluginId, 16)}${status}`);\n const catalogEntry = catalog.find((entry) => entry.pluginId === plugin.pluginId);\n if (catalogEntry && catalogEntry.envContract.length > 0) {\n console.log(\n ` env contract: ${catalogEntry.envContract.map((entry) => entry.logicalKey).join(', ')}`\n );\n }\n if (catalogEntry && catalogEntry.actionInterfaces.length > 0) {\n const implemented = catalogEntry.actionInterfaces.filter((action) => action.implemented).length;\n console.log(\n ` action interfaces: ${implemented}/${catalogEntry.actionInterfaces.length} implemented`\n );\n }\n const keys = Object.keys(plugin.config ?? {});\n if (keys.length > 0) console.log(` config keys: ${keys.join(', ')}`);\n const extras = [\n plugin.fixture !== undefined ? 'fixture' : null,\n plugin.envMapping ? 'envMapping' : null,\n ].filter(Boolean);\n if (extras.length > 0) console.log(` set: ${extras.join(', ')}`);\n }\n }\n console.log(` Available plugins: ${available.map((plugin) => plugin.id).join(', ')}`);\n}\n\n/** Shared enable/disable for the master switch or a single plugin entry. */\nasync function setEmulationEnabled(\n argv: string[],\n apiUrl: string,\n token: string,\n enable: boolean\n): Promise<void> {\n const positional = argv.filter((arg) => !arg.startsWith('-'));\n const templateId = positional[0];\n const pluginId = positional[1];\n const verb = enable ? 'enable' : 'disable';\n\n if (!templateId) {\n console.error(\n `Error: template ID required. Usage: canary sandbox emulation ${verb} <template-id> [pluginId]`\n );\n process.exit(1);\n }\n\n const { config } = await fetchEmulation(apiUrl, token, templateId);\n let next: EmulationTemplateConfig;\n\n if (!pluginId) {\n next = withEmulationMaster(config, enable);\n } else {\n assertKnownPlugin(pluginId);\n const exists = (config.plugins ?? []).some((plugin) => plugin.pluginId === pluginId);\n if (!enable && !exists) {\n console.error(`Error: plugin '${pluginId}' is not configured on this template.`);\n process.exit(1);\n }\n next = withPluginEnabled(config, pluginId, enable);\n }\n\n await patchEmulation(apiUrl, token, templateId, next);\n console.log(pluginId ? `Plugin ${pluginId} ${verb}d` : `Emulation ${verb}d`);\n}\n\nasync function handleEmulationSet(\n argv: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const positional = argv.filter((arg) => !arg.startsWith('-'));\n const templateId = positional[0];\n const pluginId = positional[1];\n\n if (!templateId || !pluginId) {\n console.error('Error: template ID and pluginId required.');\n console.error(\n 'Usage: canary sandbox emulation set <template-id> <pluginId> [--config <file>] [--fixture <file>] [--env-mapping <file>]'\n );\n process.exit(1);\n }\n assertKnownPlugin(pluginId);\n\n const configPath = getArgValue(argv, '--config');\n const fixturePath = getArgValue(argv, '--fixture');\n const envMappingPath = getArgValue(argv, '--env-mapping');\n\n if (!configPath && !fixturePath && !envMappingPath) {\n console.error('Error: provide at least one of --config, --fixture, --env-mapping');\n process.exit(1);\n }\n\n const pluginConfig = configPath ? readJsonObjectOrExit(configPath, '--config') : undefined;\n const fixture = fixturePath ? readJsonFileOrExit(fixturePath, '--fixture') : undefined;\n const envMapping = envMappingPath\n ? (readJsonObjectOrExit(envMappingPath, '--env-mapping') as Record<\n string,\n string | string[]\n >)\n : undefined;\n\n const { config } = await fetchEmulation(apiUrl, token, templateId);\n const next = withPluginConfig(config, pluginId, {\n ...(pluginConfig !== undefined ? { config: pluginConfig } : {}),\n ...(fixture !== undefined ? { fixture } : {}),\n ...(envMapping !== undefined ? { envMapping } : {}),\n });\n\n await patchEmulation(apiUrl, token, templateId, next);\n // A plugin only runs when BOTH the master switch and its own entry are on.\n // `set` never flips either, so point the user at `enable` unless it is already active.\n const entry = next.plugins.find((plugin) => plugin.pluginId === pluginId);\n const active = next.enabled && entry?.enabled;\n const hint = active ? '' : ` (inactive — run \"emulation enable ${templateId} ${pluginId}\" to activate)`;\n console.log(`Emulation plugin set: ${pluginId}${hint}`);\n}\n\nasync function handleEmulationRemove(\n argv: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const positional = argv.filter((arg) => !arg.startsWith('-'));\n const templateId = positional[0];\n const pluginId = positional[1];\n\n if (!templateId || !pluginId) {\n console.error(\n 'Error: template ID and pluginId required. Usage: canary sandbox emulation remove <template-id> <pluginId>'\n );\n process.exit(1);\n }\n\n const { config } = await fetchEmulation(apiUrl, token, templateId);\n if (!(config.plugins ?? []).some((plugin) => plugin.pluginId === pluginId)) {\n console.error(`Error: plugin '${pluginId}' is not configured on this template.`);\n process.exit(1);\n }\n\n const next = withoutPlugin(config, pluginId);\n\n await patchEmulation(apiUrl, token, templateId, next);\n console.log(`Emulation plugin removed: ${pluginId}`);\n}\n\nexport async function handleEmulation(\n argv: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const [action, ...rest] = argv;\n switch (action) {\n case 'get':\n await handleEmulationGet(rest, apiUrl, token);\n break;\n case 'enable':\n await setEmulationEnabled(rest, apiUrl, token, true);\n break;\n case 'disable':\n await setEmulationEnabled(rest, apiUrl, token, false);\n break;\n case 'set':\n await handleEmulationSet(rest, apiUrl, token);\n break;\n case 'remove':\n case 'rm':\n await handleEmulationRemove(rest, apiUrl, token);\n break;\n default:\n console.error(\n `Unknown emulation action: ${action ?? '(none)'}. Use get/enable/disable/set/remove.`\n );\n process.exit(1);\n }\n}\n","import process from 'node:process';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest } from './cli-helpers.js';\nimport { writeRawOutput } from './brand.js';\n\ntype FailureLogsUrlResponse = {\n ok: boolean;\n error?: string;\n data?: { url: string; key: string };\n};\n\ntype LiveLogsResponse = {\n ok: boolean;\n error?: string;\n data: { logs: string };\n};\n\nexport type FailureLogsBundle = { text: string; key: string } | { error: string };\n\ntype FailureLogsDependencies = {\n requestFailureLogsUrl?: (\n apiUrl: string,\n token: string,\n instanceId: string\n ) => Promise<FailureLogsUrlResponse>;\n fetch?: (url: string) => Promise<Response>;\n};\n\nconst requestFailureLogsUrl = (apiUrl: string, token: string, instanceId: string) =>\n apiRequest<FailureLogsUrlResponse>(\n apiUrl,\n token,\n 'GET',\n `/org/environments/instances/${instanceId}/logs/failure`\n );\n\nexport async function decodeFailureLogsBundle(buf: Buffer): Promise<string> {\n const { gunzipSync } = await import('node:zlib');\n try {\n return gunzipSync(buf).toString('utf8');\n } catch {\n return buf.toString('utf8');\n }\n}\n\n/**\n * Fetch a failed/terminated instance's captured full-log bundle: presign a GET\n * off the API, download the gzip from S3, and return the decompressed text.\n * Returns `{ error }` (never throws) so callers can fall back cleanly.\n */\nexport async function fetchFailureLogsBundle(\n apiUrl: string,\n token: string,\n instanceId: string,\n deps: FailureLogsDependencies = {}\n): Promise<FailureLogsBundle> {\n const res = await (deps.requestFailureLogsUrl ?? requestFailureLogsUrl)(apiUrl, token, instanceId);\n if (!res.ok || !res.data?.url) {\n return { error: res.error ?? 'No captured diagnostic logs' };\n }\n try {\n const resp = await (deps.fetch ?? fetch)(res.data.url);\n if (!resp.ok) return { error: `Failed to download bundle (HTTP ${resp.status})` };\n const buf = Buffer.from(await resp.arrayBuffer());\n return { text: await decodeFailureLogsBundle(buf), key: res.data.key };\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) };\n }\n}\n\nexport async function handleLogs(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!instanceId) {\n console.error('Error: instance ID required. Usage: canary sandbox logs <instance-id>');\n process.exit(1);\n }\n\n const service = getArgValue(argv, '--service');\n const tail = getArgValue(argv, '--tail') ?? '200';\n const source = getArgValue(argv, '--source');\n const forceFailure = hasFlag(argv, '--failure') || source === 'failure';\n\n // Failure snapshot: the durable full-log bundle captured to S3 when the\n // instance failed to provision. Works for `failed`/`terminated` instances\n // (live docker/logs needs a running host). Forced with --failure/--source\n // failure, and used as an automatic fallback when the live tail can't run.\n if (forceFailure) {\n const bundle = await fetchFailureLogsBundle(apiUrl, token, instanceId);\n if ('error' in bundle) {\n console.error(`Error: ${bundle.error}`);\n process.exit(1);\n }\n if (json) {\n writeRawOutput(JSON.stringify(bundle, null, 2));\n return;\n }\n process.stdout.write(bundle.text || '(empty bundle)');\n return;\n }\n\n // Live path: tail the running containers via SSM. Don't send `service: ''`\n // — the server's regex validator rejects empty strings even though the field\n // is optional, and the route then returns 500 instead of an all-services tail.\n const res = await apiRequest<LiveLogsResponse>(\n apiUrl,\n token,\n 'POST',\n `/org/environments/instances/${instanceId}/docker/logs`,\n {\n ...(service ? { service } : {}),\n tail: parseInt(tail, 10),\n }\n );\n\n if (!res.ok) {\n // The instance likely isn't running (failed/terminated). Fall back to the\n // captured failure snapshot so a dead provision is still diagnosable.\n const bundle = await fetchFailureLogsBundle(apiUrl, token, instanceId);\n if (!('error' in bundle)) {\n if (json) {\n writeRawOutput(JSON.stringify(bundle, null, 2));\n return;\n }\n console.error(`(live logs unavailable: ${res.error}; showing captured failure snapshot)\\n`);\n process.stdout.write(bundle.text || '(empty bundle)');\n return;\n }\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n process.stdout.write(res.data.logs ?? '(no logs)');\n}\n","import process from 'node:process';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest } from './cli-helpers.js';\nimport { writeRawOutput } from './brand.js';\nimport { resolveApplicationId } from './environment.js';\n\ntype PreviewResponse = {\n ok: boolean;\n error?: string;\n data: {\n applicationId: string;\n prNumber: number;\n status: string;\n headSha: string;\n appliedSha: string | null;\n portUrls: Record<string, string>;\n error: string | null;\n readyMs: number | null;\n leaseExpiresAt: string | null;\n personas: { name: string; email: string; password: string }[];\n };\n};\n\n/**\n * `cofactor sandbox preview --application A --pr N`: a pull request's\n * Sandboxes v2 preview of an application, with the logins its comment\n * deliberately leaves out.\n */\nexport async function handlePreview(argv: string[], apiUrl: string, token: string): Promise<void> {\n const pr = getArgValue(argv, '--pr');\n if (!pr) {\n console.error('Usage: cofactor sandbox preview --application <name|id> --pr <number> [--json]');\n process.exit(1);\n }\n const application = await resolveApplicationId(argv, apiUrl, token);\n const query = new URLSearchParams({ application, pr });\n const res = await apiRequest<PreviewResponse>(apiUrl, token, 'GET', `/org/sandboxes-v2/previews?${query}`);\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n const preview = res.data;\n if (hasFlag(argv, '--json')) {\n writeRawOutput(JSON.stringify(preview, null, 2));\n return;\n }\n const lines = [\n `#${preview.prNumber}: ${preview.status}`,\n ` commit ${(preview.appliedSha ?? preview.headSha).slice(0, 12)}${preview.appliedSha && preview.appliedSha !== preview.headSha ? ` (building ${preview.headSha.slice(0, 12)})` : ''}`,\n ...Object.entries(preview.portUrls).map(([name, url]) => ` ${name.padEnd(8)} ${url}`),\n ...preview.personas.map((p) => ` ${p.name.padEnd(8)} ${p.email} ${p.password}`),\n ];\n if (preview.leaseExpiresAt) lines.push(` expires ${preview.leaseExpiresAt}`);\n if (preview.error) lines.push('', preview.error);\n console.log(lines.join('\\n'));\n}\n","/**\n * Sandboxes v2 CLI, addressed by application: connect its repository (or\n * push images — `sandbox-push.ts`), browse what its `cofactor.sandbox.yaml`\n * offers, claim a sandbox, run its actions and emulator operations, and\n * prove an environment's seed chain end to end — all under `cofactor sandbox`.\n *\n * `--env` stays the CLI's global stage flag (dev/prod); a sandbox flavor is\n * always `--environment`. See `packages/content/src/sandbox-v2-guide/` for\n * the full guide (`cofactor sandbox guide v2`).\n *\n * @module\n */\n\nimport process from 'node:process';\nimport { readFileSync } from 'node:fs';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest, getArgValues } from './cli-helpers.js';\nimport { resolveApplicationId } from './environment.js';\nimport { writeRawOutput } from './brand.js';\nimport { validateDefinition, type DefinitionIssue } from '@chatsdet/sandbox-helper/manifest';\nimport { pluginOperations } from '@chatsdet/emulation';\n\n// ---------------------------------------------------------------------------\n// Shared types (mirroring apps/api/src/sandboxes-v2/routes.ts response shapes)\n// ---------------------------------------------------------------------------\n\ntype ActionInputProp = {\n type: 'string' | 'number' | 'integer' | 'boolean' | 'persona';\n description?: string;\n required?: boolean;\n default?: string | number | boolean;\n enum?: Array<string | number>;\n sensitive?: boolean;\n};\n\ntype ActionOutputProp = { type: 'string' | 'number' | 'integer' | 'boolean'; description?: string };\n\ntype ActionEntry = {\n slug: string;\n description: string;\n input: Record<string, ActionInputProp>;\n output: Record<string, ActionOutputProp>;\n timeout: number;\n};\n\ntype OperationEntry = {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n outputSchema: Record<string, unknown>;\n};\n\ntype EnvironmentEntry = {\n name: string;\n description: string;\n extends: string | null;\n seeds: Array<{ id: string; action: string; input: Record<string, unknown> }>;\n envKeys: string[];\n};\n\ntype DefinitionEntry = {\n environments: EnvironmentEntry[];\n actions: ActionEntry[];\n operations: OperationEntry[];\n personas: string[];\n ports: Array<{ name: string; access: string }>;\n emulation: string[];\n};\n\nexport type PushEntry = {\n id: string;\n status: 'uploading' | 'importing' | 'ready' | 'failed';\n tags: string[];\n error: string | null;\n createdAt: string;\n};\n\n/** `GET /org/applications/:id/sandbox`: the application's sandbox setting and its definition at the default version. */\ntype ApplicationSandbox = {\n applicationId: string;\n delivery: 'github' | 'upload';\n repo: string | null;\n definitionDir: string | null;\n sandboxEnvironments: Array<{ id: string; name: string; environment: string }>;\n /** An upload-delivered application's newest pushes; null for a GitHub-delivered one. */\n pushes: PushEntry[] | null;\n /** A commit SHA, or a push id; null before the first push is ready. */\n version: string | null;\n definition: DefinitionEntry | null;\n};\n\ntype ApplicationSandboxResponse = {\n ok: boolean;\n error?: string;\n code?: string;\n issues?: DefinitionIssue[];\n data: ApplicationSandbox;\n};\n\ntype SandboxData = {\n id: string;\n applicationId: string;\n environment: string;\n /** A commit SHA, or a push id. */\n version: string;\n status: 'claiming' | 'ready' | 'failed' | 'destroyed';\n error: string | null;\n portUrls: Record<string, string>;\n personas: Array<{ name: string; email: string; password: string }>;\n seedOutputs: Record<string, Record<string, unknown>>;\n leaseExpiresAt: string | null;\n expired: boolean;\n readyMs: number | null;\n /** Set when ready: waiting for the commit's ready snapshot, restoring the copy, and preparing it. */\n phases: { snapshotMs: number; restoreMs: number; prepareMs: number } | null;\n /** The ready snapshot this sandbox is a copy of: when it was built, and how long each part took. */\n snapshot: {\n builtAt: string;\n buildPhases: {\n buildMs: number;\n /** Absent on snapshots built before step timings were recorded. */\n build?: { steps: { name: string; ran: boolean; ms: number }[]; checkoutMs: number; restartMs: number };\n prepareMs: number;\n seeds: { id: string; action: string; ms: number }[];\n } | null;\n } | null;\n actions: ActionEntry[];\n operations: OperationEntry[];\n};\n\ntype SandboxResponse = { ok: boolean; error?: string; data: SandboxData };\n\ntype RunData = {\n id: string;\n sandboxId: string;\n action: string;\n seedStepId: string | null;\n input: Record<string, unknown>;\n status: 'running' | 'succeeded' | 'failed';\n output: Record<string, unknown> | null;\n exitCode: number | null;\n stdoutTail: string | null;\n stderrTail: string | null;\n error: string | null;\n durationMs: number | null;\n createdAt: string;\n completedAt: string | null;\n};\n\ntype RunResponse = { ok: boolean; error?: string; data: RunData };\n\ntype RunEnqueueResponse = {\n ok: boolean;\n error?: string;\n code?: string;\n issues?: Array<string | { path?: Array<string | number>; message?: string }>;\n data?: { id: string };\n};\n\n// ---------------------------------------------------------------------------\n// Formatting helpers\n// ---------------------------------------------------------------------------\n\nfunction pad(s: string, n: number): string {\n return s.padEnd(n);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction indentLines(text: string, indent: string): string {\n return text\n .split('\\n')\n .map((line) => `${indent}${line}`)\n .join('\\n');\n}\n\n/** `slug(input1, input2) → output1, output2`, shared by envs/show/run listings. */\nfunction formatActionSignature(a: { slug: string; input: Record<string, unknown>; output: Record<string, unknown> }): string {\n const inputs = Object.keys(a.input).join(', ');\n const outputs = Object.keys(a.output).join(', ');\n return `${a.slug}(${inputs})${outputs ? ` → ${outputs}` : ''}`;\n}\n\nfunction jsonSchemaPropTypes(schema: Record<string, unknown>): Record<string, string> {\n const properties = (schema as { properties?: Record<string, { type?: unknown }> }).properties ?? {};\n return Object.fromEntries(\n Object.entries(properties).map(([key, prop]) => [key, typeof prop.type === 'string' ? prop.type : 'string'])\n );\n}\n\nfunction formatOperationSignature(op: { name: string; inputSchema: Record<string, unknown>; outputSchema: Record<string, unknown> }): string {\n const inputs = Object.keys(jsonSchemaPropTypes(op.inputSchema)).join(', ');\n const outputs = Object.keys(jsonSchemaPropTypes(op.outputSchema)).join(', ');\n return `${op.name}(${inputs})${outputs ? ` → ${outputs}` : ''}`;\n}\n\nfunction printIssues(issues: DefinitionIssue[] | undefined, indent: string): void {\n for (const issue of issues ?? []) {\n console.log(`${indent}${issue.path || '(root)'}: ${issue.message}`);\n }\n}\n\n/** A version as people read it: a short commit, or a push id. */\nfunction shortVersion(version: string): string {\n return /^[0-9a-f]{40}$/.test(version) ? version.slice(0, 12) : version;\n}\n\nfunction printApplicationHuman(app: ApplicationSandbox): void {\n console.log(\n app.delivery === 'github'\n ? `Delivered from GitHub: ${app.repo}${app.definitionDir && app.definitionDir !== '.' ? `:${app.definitionDir}` : ''}`\n : 'Delivered as pushed images'\n );\n console.log(` version ${app.version ? shortVersion(app.version) : '(none yet: run `cofactor sandbox push`)'}`);\n if (app.pushes && app.pushes.length > 0) {\n console.log(' pushes:');\n for (const push of app.pushes) {\n const tags = push.tags.length > 0 ? ` [${push.tags.join(', ')}]` : '';\n console.log(` ${push.id} ${pad(push.status, 9)} ${push.createdAt}${tags}${push.error ? `\\n ${push.error}` : ''}`);\n }\n }\n if (app.sandboxEnvironments.length > 0) {\n console.log(' sandbox environments:');\n for (const env of app.sandboxEnvironments) console.log(` ${pad(env.environment, 16)} ${env.name}`);\n }\n const def = app.definition;\n if (!def) return;\n\n console.log(' Environments:');\n for (const env of def.environments) {\n console.log(` ${env.name}${env.extends ? ` (extends ${env.extends})` : ''}`);\n console.log(indentLines(env.description, ' '));\n if (env.seeds.length > 0) {\n console.log(' seeds:');\n for (const step of env.seeds) console.log(` ${step.id}: ${step.action}`);\n }\n if (env.envKeys.length > 0) console.log(` env: ${env.envKeys.join(', ')}`);\n }\n\n if (def.actions.length > 0) {\n console.log(' Actions:');\n for (const a of def.actions) {\n console.log(` ${formatActionSignature(a)}`);\n console.log(` ${a.description}`);\n }\n }\n\n if (def.operations.length > 0) {\n console.log(' Operations:');\n for (const op of def.operations) {\n console.log(` ${formatOperationSignature(op)}`);\n console.log(` ${op.description}`);\n }\n }\n\n console.log(` Personas: ${def.personas.join(', ') || '(none)'}`);\n console.log(` Ports: ${def.ports.map((p) => `${p.name} (${p.access})`).join(', ') || '(none)'}`);\n}\n\nfunction printSandbox(data: SandboxData): void {\n console.log(`Sandbox ${data.id}: ${data.status}${data.expired ? ' (expired)' : ''}`);\n console.log(` environment ${data.environment}`);\n console.log(` version ${shortVersion(data.version)}`);\n if (data.error) console.log(` error ${data.error}`);\n if (data.readyMs !== null) {\n const seconds = (ms: number) => `${(ms / 1000).toFixed(1)}s`;\n const phases = data.phases\n ? ` (snapshot ${seconds(data.phases.snapshotMs)}, restore ${seconds(data.phases.restoreMs)}, prepare ${seconds(data.phases.prepareMs)})`\n : '';\n console.log(` ready in ${seconds(data.readyMs)}${phases}`);\n }\n if (data.snapshot?.buildPhases) {\n const { buildMs, prepareMs, seeds } = data.snapshot.buildPhases;\n const seedsMs = seeds.reduce((total, step) => total + step.ms, 0);\n console.log(\n ` snapshot built ${data.snapshot.builtAt} (build ${(buildMs / 1000).toFixed(1)}s, prepare ${(prepareMs / 1000).toFixed(1)}s, ${seeds.length} seeds ${(seedsMs / 1000).toFixed(1)}s)`\n );\n const ran = data.snapshot.buildPhases.build?.steps.filter((step) => step.ran) ?? [];\n if (ran.length > 0) {\n console.log(` build steps: ${ran.map((step) => `${step.name} ${(step.ms / 1000).toFixed(1)}s`).join(', ')}`);\n }\n }\n if (data.leaseExpiresAt) console.log(` expires ${data.leaseExpiresAt}`);\n\n if (Object.keys(data.portUrls).length > 0) {\n console.log('');\n console.log(' URLs:');\n for (const [name, url] of Object.entries(data.portUrls)) writeRawOutput(` ${pad(name, 10)} ${url}`);\n }\n\n if (data.personas.length > 0) {\n console.log('');\n console.log(' Logins:');\n for (const p of data.personas) writeRawOutput(` ${pad(p.name, 14)} ${p.email} ${p.password}`);\n }\n\n const seedIds = Object.keys(data.seedOutputs);\n if (seedIds.length > 0) {\n console.log('');\n console.log(' Seed outputs:');\n for (const id of seedIds) {\n for (const [key, value] of Object.entries(data.seedOutputs[id]!)) {\n writeRawOutput(` ${id}.${key}: ${JSON.stringify(value)}`);\n }\n }\n }\n\n if (data.actions.length > 0) {\n console.log('');\n console.log(' Actions:');\n for (const a of data.actions) console.log(` ${formatActionSignature(a)}`);\n }\n\n if (data.operations.length > 0) {\n console.log('');\n console.log(' Operations:');\n for (const op of data.operations) console.log(` ${formatOperationSignature(op)}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// validate — offline, no auth\n// ---------------------------------------------------------------------------\n\n/**\n * `cofactor sandbox validate [file] [--upload]`: offline schema + cross-check,\n * no auth required. `--upload` checks a definition for pushed images (no\n * `setup` or build steps); otherwise one built from a GitHub checkout.\n */\nexport function handleSandboxValidate(argv: string[]): void {\n const file = argv.find((a) => !a.startsWith('-')) ?? './cofactor.sandbox.yaml';\n const json = hasFlag(argv, '--json');\n const delivery = hasFlag(argv, '--upload') ? 'upload' : 'github';\n\n let yamlText: string;\n try {\n yamlText = readFileSync(file, 'utf-8');\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (json) {\n writeRawOutput(JSON.stringify({ ok: false, issues: [{ path: '', message }] }, null, 2));\n } else {\n console.error(`Error reading \"${file}\": ${message}`);\n }\n process.exit(1);\n }\n\n const { definition, issues } = validateDefinition(yamlText, delivery);\n\n if (json) {\n writeRawOutput(JSON.stringify({ ok: issues.length === 0, issues }, null, 2));\n process.exit(issues.length === 0 ? 0 : 1);\n }\n\n if (issues.length > 0) {\n for (const issue of issues) console.error(`${issue.path || '(root)'}: ${issue.message}`);\n process.exit(1);\n }\n\n const environmentCount = Object.keys(definition!.environments).length;\n const actionCount = Object.keys(definition!.actions).length;\n const operationCount = Object.keys(definition!.emulation).reduce(\n (sum, pluginId) => sum + pluginOperations(pluginId).length,\n 0\n );\n console.log(`✓ ${file} is valid`);\n console.log(` ${environmentCount} environment(s), ${actionCount} action(s), ${operationCount} operation(s)`);\n}\n\n// ---------------------------------------------------------------------------\n// envs\n// ---------------------------------------------------------------------------\n\n/** The application's sandbox setting, or exit with the API's reason. */\nasync function fetchApplicationSandbox(apiUrl: string, token: string, applicationId: string): Promise<ApplicationSandbox> {\n const res = await apiRequest<ApplicationSandboxResponse>(apiUrl, token, 'GET', `/org/applications/${applicationId}/sandbox`);\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n if (res.code === 'SANDBOX_V2_DEFINITION_INVALID') printIssues(res.issues, ' ');\n process.exit(1);\n }\n return res.data;\n}\n\n/**\n * The options each Sandboxes v2 command takes (beyond the global ones the\n * dispatcher strips): a value option, a boolean flag, how many positionals.\n */\nconst V2_ARGS: Record<string, { values: string[]; flags: string[]; positionals: number }> = {\n envs: { values: ['--application', '--property'], flags: ['--json'], positionals: 0 },\n connect: { values: ['--application', '--property', '--repo', '--dir'], flags: ['--json'], positionals: 0 },\n push: { values: ['--application', '--property', '--definition', '--tag'], flags: ['--wait', '--json'], positionals: 1 },\n claim: { values: ['--application', '--property', '--environment', '--at'], flags: ['--wait', '--json'], positionals: 0 },\n show: { values: [], flags: ['--json'], positionals: 1 },\n run: { values: ['--input', '--json-input'], flags: ['--json'], positionals: 2 },\n 'verify-env': { values: ['--application', '--property', '--environment'], flags: ['--json'], positionals: 0 },\n destroy: { values: [], flags: [], positionals: 1 },\n};\n\n/**\n * Refuse an argument a Sandboxes v2 command doesn't take, rather than ignore\n * it: a `\"--at accept-a\"` quoted into one word would otherwise claim the\n * newest version without a word.\n */\nexport function rejectUnknownSandboxV2Args(command: string, argv: string[]): void {\n const spec = V2_ARGS[command];\n if (!spec) return;\n let positionals = 0;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (spec.values.includes(arg)) {\n i++;\n continue;\n }\n // `--tag=nightly`: only the repeatable options (`getArgValues`) read the `=` form;\n // `getArgValue` would ignore `--at=x`, so that one stays refused. `--debug` traces any command.\n if (['--input', '--tag'].some((value) => spec.values.includes(value) && arg.startsWith(`${value}=`))) continue;\n if (spec.flags.includes(arg) || arg === '--debug') continue;\n if (!arg.startsWith('-') && ++positionals <= spec.positionals) continue;\n console.error(`Error: \\`cofactor sandbox ${command}\\` does not take ${JSON.stringify(arg)}. See \\`cofactor sandbox --help\\`.`);\n process.exit(1);\n }\n}\n\n/** `cofactor sandbox envs --application A [--json]`: what the application's definition offers. */\nexport async function handleSandboxEnvs(argv: string[], apiUrl: string, token: string): Promise<void> {\n const applicationId = await resolveApplicationId(argv, apiUrl, token);\n const app = await fetchApplicationSandbox(apiUrl, token, applicationId);\n if (hasFlag(argv, '--json')) {\n writeRawOutput(JSON.stringify(app, null, 2));\n return;\n }\n printApplicationHuman(app);\n}\n\n/**\n * `cofactor sandbox connect --application A --repo owner/name [--dir D] [--json]`:\n * build the application's sandboxes from a GitHub repository directory.\n */\nexport async function handleSandboxConnect(argv: string[], apiUrl: string, token: string): Promise<void> {\n const repo = getArgValue(argv, '--repo');\n if (!repo) {\n console.error('Usage: cofactor sandbox connect --application <name|id> --repo <owner/name> [--dir <path>] [--json]');\n process.exit(1);\n }\n const applicationId = await resolveApplicationId(argv, apiUrl, token);\n const res = await apiRequest<ApplicationSandboxResponse>(apiUrl, token, 'PUT', `/org/applications/${applicationId}/sandbox`, {\n repo,\n definitionDir: getArgValue(argv, '--dir') ?? '.',\n });\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n if (res.code === 'SANDBOX_V2_DEFINITION_INVALID') printIssues(res.issues, ' ');\n process.exit(1);\n }\n if (hasFlag(argv, '--json')) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n printApplicationHuman(res.data);\n}\n\n// ---------------------------------------------------------------------------\n// claim / show — shared polling\n// ---------------------------------------------------------------------------\n\nconst CLAIM_POLL_INTERVAL_MS = 3_000;\nconst CLAIM_TIMEOUT_MS = 30 * 60 * 1000;\n\n/**\n * Wait for a claim to settle. A timeout or an API error comes back as\n * `{ error }` rather than exiting, so a caller that owns the sandbox\n * (`verify-env`) can still destroy it.\n */\nasync function pollSandboxUntilReady(\n apiUrl: string,\n token: string,\n id: string\n): Promise<{ data: SandboxData } | { error: string }> {\n const deadline = Date.now() + CLAIM_TIMEOUT_MS;\n while (Date.now() < deadline) {\n const res = await apiRequest<SandboxResponse>(apiUrl, token, 'GET', `/org/sandboxes-v2/sandboxes/${id}`);\n if (!res.ok) return { error: res.error ?? 'unknown error' };\n if (res.data.status === 'ready' || res.data.status === 'failed') return { data: res.data };\n await sleep(CLAIM_POLL_INTERVAL_MS);\n }\n return { error: `timed out waiting for sandbox ${id} to become ready (30 min)` };\n}\n\n/** `cofactor sandbox claim --application A [--environment E] [--at V] [--wait] [--json]`. */\nexport async function handleSandboxClaim(argv: string[], apiUrl: string, token: string): Promise<void> {\n const applicationId = await resolveApplicationId(argv, apiUrl, token);\n const environment = getArgValue(argv, '--environment') ?? 'default';\n // Not `--version`: the CLI answers that flag anywhere on the line with its own version.\n const version = getArgValue(argv, '--at');\n const wait = hasFlag(argv, '--wait');\n const json = hasFlag(argv, '--json');\n\n const body: Record<string, unknown> = { applicationId, environment };\n if (version) body.version = version;\n\n const res = await apiRequest<{\n ok: boolean;\n error?: string;\n code?: string;\n issues?: DefinitionIssue[];\n data: { id: string };\n }>(apiUrl, token, 'POST', '/org/sandboxes-v2/sandboxes', body);\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n // The definition of that version fails validation: say where, as `validate` does.\n if (res.code === 'SANDBOX_V2_DEFINITION_INVALID') printIssues(res.issues, ' ');\n process.exit(1);\n }\n const id = res.data.id;\n\n if (!wait) {\n if (json) {\n writeRawOutput(JSON.stringify({ id }, null, 2));\n } else {\n console.log(`Sandbox claiming: ${id}`);\n console.log(` Poll with: cofactor sandbox show ${id}`);\n }\n return;\n }\n\n if (!json) console.log(`Claiming sandbox ${id}...`);\n const waited = await pollSandboxUntilReady(apiUrl, token, id);\n if ('error' in waited) {\n // The sandbox stays yours (its lease still runs): `show` or `destroy` it.\n console.error(`Error: ${waited.error}`);\n process.exit(1);\n }\n const { data } = waited;\n if (json) {\n writeRawOutput(JSON.stringify(data, null, 2));\n } else {\n printSandbox(data);\n }\n if (data.status === 'failed') process.exit(1);\n}\n\n/** `cofactor sandbox show <id> [--json]`. */\nexport async function handleSandboxShow(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = argv.filter((a) => !a.startsWith('-'));\n if (!id) {\n console.error('Usage: cofactor sandbox show <id> [--json]');\n process.exit(1);\n }\n const json = hasFlag(argv, '--json');\n\n const res = await apiRequest<SandboxResponse>(apiUrl, token, 'GET', `/org/sandboxes-v2/sandboxes/${id}`);\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n printSandbox(res.data);\n}\n\n// ---------------------------------------------------------------------------\n// run\n// ---------------------------------------------------------------------------\n\nfunction coerceInputValue(raw: string, type: string): string | number | boolean {\n switch (type) {\n case 'integer': {\n if (!/^-?\\d+$/.test(raw)) throw new Error(`must be an integer, got \"${raw}\"`);\n return Number(raw);\n }\n case 'number': {\n const value = Number(raw);\n if (Number.isNaN(value)) throw new Error(`must be a number, got \"${raw}\"`);\n return value;\n }\n case 'boolean': {\n if (raw === 'true') return true;\n if (raw === 'false') return false;\n throw new Error(`must be \"true\" or \"false\", got \"${raw}\"`);\n }\n default:\n // string, persona\n return raw;\n }\n}\n\nfunction buildRunInputFromFlags(argv: string[], propTypes: Record<string, string>): Record<string, unknown> {\n const pairs = getArgValues(argv, '--input');\n const input: Record<string, unknown> = {};\n for (const pair of pairs) {\n const eq = pair.indexOf('=');\n if (eq <= 0) {\n console.error(`Error: invalid --input \"${pair}\". Use key=value.`);\n process.exit(1);\n }\n const key = pair.slice(0, eq);\n const raw = pair.slice(eq + 1);\n const type = propTypes[key];\n if (!type) {\n console.error(`Error: unknown input \"${key}\". Known: ${Object.keys(propTypes).join(', ') || '(none)'}`);\n process.exit(1);\n }\n try {\n input[key] = coerceInputValue(raw, type);\n } catch (error) {\n console.error(`Error: --input ${key}: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n }\n return input;\n}\n\n/** `cofactor sandbox run <id> <action|plugin.op> [--input k=v ...] [--json-input '{...}'] [--json]`. */\nexport async function handleSandboxRun(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const [id, actionRef] = positional;\n if (!id || !actionRef) {\n console.error('Usage: cofactor sandbox run <id> <action|plugin.op> [--input k=v ...] [--json-input \\'{...}\\'] [--json]');\n process.exit(1);\n }\n const json = hasFlag(argv, '--json');\n const jsonInput = getArgValue(argv, '--json-input');\n\n const showRes = await apiRequest<SandboxResponse>(apiUrl, token, 'GET', `/org/sandboxes-v2/sandboxes/${id}`);\n if (!showRes.ok) {\n console.error(`Error: ${showRes.error}`);\n process.exit(1);\n }\n const sandbox = showRes.data;\n const repoAction = sandbox.actions.find((a) => a.slug === actionRef);\n const operation = sandbox.operations.find((op) => op.name === actionRef);\n if (!repoAction && !operation) {\n console.error(`Error: unknown action or operation \"${actionRef}\".`);\n console.error(` Actions: ${sandbox.actions.map((a) => a.slug).join(', ') || '(none)'}`);\n console.error(` Operations: ${sandbox.operations.map((op) => op.name).join(', ') || '(none)'}`);\n process.exit(1);\n }\n\n let input: Record<string, unknown>;\n if (jsonInput !== undefined) {\n try {\n input = JSON.parse(jsonInput) as Record<string, unknown>;\n } catch (error) {\n console.error(`Error: invalid --json-input: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n } else if (repoAction) {\n const propTypes = Object.fromEntries(Object.entries(repoAction.input).map(([key, prop]) => [key, prop.type]));\n input = buildRunInputFromFlags(argv, propTypes);\n } else {\n input = buildRunInputFromFlags(argv, jsonSchemaPropTypes(operation!.inputSchema));\n }\n\n const enqueueRes = await apiRequest<RunEnqueueResponse>(\n apiUrl,\n token,\n 'POST',\n `/org/sandboxes-v2/sandboxes/${id}/runs`,\n { action: actionRef, input }\n );\n if (!enqueueRes.ok) {\n if (enqueueRes.code === 'SANDBOX_V2_ACTION_INVALID_INPUT') {\n console.error(`Error: invalid input for \"${actionRef}\":`);\n for (const issue of enqueueRes.issues ?? []) {\n if (typeof issue === 'string') console.error(` ${issue}`);\n else console.error(` ${(issue.path ?? []).join('.')}: ${issue.message ?? ''}`);\n }\n console.error('');\n console.error('Input schema:');\n writeRawOutput(JSON.stringify(repoAction ? repoAction.input : operation!.inputSchema, null, 2));\n } else {\n console.error(`Error: ${enqueueRes.error}`);\n }\n process.exit(1);\n }\n const runId = enqueueRes.data!.id;\n\n const timeoutMs = ((repoAction?.timeout ?? 60) + 60) * 1000;\n const deadline = Date.now() + timeoutMs;\n let run: RunData | undefined;\n while (Date.now() < deadline) {\n const pollRes = await apiRequest<RunResponse>(apiUrl, token, 'GET', `/org/sandboxes-v2/runs/${runId}`);\n if (!pollRes.ok) {\n console.error(`Error: ${pollRes.error}`);\n process.exit(1);\n }\n run = pollRes.data;\n if (run.status !== 'running') break;\n await sleep(2_000);\n }\n if (!run || run.status === 'running') {\n console.error(`Timed out waiting for run ${runId}.`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(run, null, 2));\n if (run.status === 'failed') process.exit(1);\n return;\n }\n\n if (run.status === 'failed') {\n console.error(`Run failed: ${run.error ?? 'unknown error'}`);\n if (run.stderrTail) {\n console.error('');\n console.error('stderr:');\n console.error(run.stderrTail);\n }\n process.exit(1);\n }\n\n writeRawOutput(JSON.stringify(run.output ?? {}, null, 2));\n}\n\n// ---------------------------------------------------------------------------\n// destroy\n// ---------------------------------------------------------------------------\n\n/** `cofactor sandbox destroy <id>`. */\nexport async function handleSandboxDestroy(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = argv.filter((a) => !a.startsWith('-'));\n if (!id) {\n console.error('Usage: cofactor sandbox destroy <id>');\n process.exit(1);\n }\n const res = await apiRequest<{ ok: boolean; error?: string; data: { id: string } }>(\n apiUrl,\n token,\n 'DELETE',\n `/org/sandboxes-v2/sandboxes/${id}`\n );\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n console.log(`Sandbox ${id} destroying.`);\n}\n\n// ---------------------------------------------------------------------------\n// verify-env\n// ---------------------------------------------------------------------------\n\n/** `cofactor sandbox verify-env --application A [--environment E] [--json]`. */\nexport async function handleSandboxVerifyEnv(argv: string[], apiUrl: string, token: string): Promise<void> {\n const applicationId = await resolveApplicationId(argv, apiUrl, token);\n const onlyEnvironment = getArgValue(argv, '--environment');\n const json = hasFlag(argv, '--json');\n // With --json, stdout carries only the result array; progress goes to stderr.\n const log = (line: string) => (json ? console.error(line) : console.log(line));\n\n const app = await fetchApplicationSandbox(apiUrl, token, applicationId);\n if (!app.version || !app.definition) {\n console.error('Error: this application has no version to verify yet: run `cofactor sandbox push` first.');\n process.exit(1);\n }\n const version = app.version;\n const allEnvironments = app.definition.environments.map((e) => e.name);\n if (onlyEnvironment && !allEnvironments.includes(onlyEnvironment)) {\n console.error(`Error: environment \"${onlyEnvironment}\" not found. Known: ${allEnvironments.join(', ')}`);\n process.exit(1);\n }\n const environments = onlyEnvironment ? [onlyEnvironment] : allEnvironments;\n\n type Result = { environment: string; ok: boolean; seconds: number; error?: string };\n const results: Result[] = [];\n\n // Sequential on purpose — the fleet's placer fails rather than queues.\n for (const environment of environments) {\n log(`\\n=== ${environment} ===`);\n const started = Date.now();\n const claimRes = await apiRequest<{ ok: boolean; error?: string; data: { id: string } }>(\n apiUrl,\n token,\n 'POST',\n '/org/sandboxes-v2/sandboxes',\n { applicationId, environment, version }\n );\n if (!claimRes.ok) {\n const seconds = (Date.now() - started) / 1000;\n console.error(` claim failed: ${claimRes.error}`);\n results.push({ environment, ok: false, seconds, error: claimRes.error });\n continue;\n }\n const id = claimRes.data.id;\n log(` claimed ${id}, waiting...`);\n const waited = await pollSandboxUntilReady(apiUrl, token, id);\n const seconds = (Date.now() - started) / 1000;\n if ('error' in waited || waited.data.status !== 'ready') {\n // A timeout or a failed claim still falls through to the destroy below.\n const error = 'error' in waited ? waited.error : (waited.data.error ?? 'failed');\n console.error(` failed: ${error}`);\n results.push({ environment, ok: false, seconds, error });\n } else {\n const { data } = waited;\n log(` ready in ${data.readyMs !== null ? (data.readyMs / 1000).toFixed(1) : '?'}s`);\n for (const [stepId, outputs] of Object.entries(data.seedOutputs)) {\n for (const [key, value] of Object.entries(outputs)) {\n log(` ${stepId}.${key}: ${JSON.stringify(value)}`);\n }\n }\n results.push({ environment, ok: true, seconds });\n }\n log(` destroying ${id}...`);\n await apiRequest(apiUrl, token, 'DELETE', `/org/sandboxes-v2/sandboxes/${id}`);\n }\n\n log('');\n log('Summary:');\n log(` ${pad('Environment', 20)} ${pad('Result', 6)} Seconds`);\n for (const r of results) {\n const errSuffix = r.error ? ` (${r.error})` : '';\n log(` ${pad(r.environment, 20)} ${pad(r.ok ? 'OK' : 'FAIL', 6)} ${r.seconds.toFixed(1)}${errSuffix}`);\n }\n\n if (json) writeRawOutput(JSON.stringify(results, null, 2));\n if (results.some((r) => !r.ok)) process.exit(1);\n}\n","import { createHash, randomBytes } from 'node:crypto';\nimport { parse } from 'yaml';\nimport { z } from 'zod';\nimport { findOperation, getPlugin } from '@chatsdet/emulation';\nimport {\n OBJECT_STORAGE_PORT,\n OBJECT_STORAGE_PORT_NAME,\n OBJECT_STORAGE_PROVIDERS,\n OBJECT_STORAGE_SERVICE_NAMES,\n type ObjectStorageProvider,\n RESERVED_STORAGE_PORTS,\n objectStorageServices,\n} from './object-storage';\n\n// For the API (the helper-layer route presigns each provider's layer; the app URL skips the storage port) and push.\nexport { OBJECT_STORAGE_PORT_NAME, OBJECT_STORAGE_PROVIDERS, type ObjectStorageProvider };\n\n/**\n * The sandbox definition: `cofactor.sandbox.yaml`, at a repository's root or\n * (for a monorepo) under a definition directory. Pure schema and derivations;\n * the guest-side paths live in runtime.ts, so the API can import this module\n * to validate a definition it read from GitHub.\n */\nexport const DEFINITION_FILE = 'cofactor.sandbox.yaml';\n\n/**\n * How an application's code reaches its sandboxes. `github`: Cofactor clones\n * the repository and the definition installs (`setup`) and builds\n * (`lifecycle.build`) it. `upload`: the customer pushes a built image with\n * `cofactor sandbox push`, so the definition has neither.\n */\nexport type Delivery = 'github' | 'upload';\n\n/**\n * An uploaded image's Cofactor files, layered in by `cofactor sandbox push`:\n * the definition and the image's own config (`ImageConfig`). Their presence\n * is what makes a sandbox an uploaded one.\n */\nexport const IMAGE_DIR = '/cofactor';\nexport const IMAGE_DEFINITION_FILE = `${IMAGE_DIR}/${DEFINITION_FILE}`;\nexport const IMAGE_CONFIG_FILE = `${IMAGE_DIR}/image.json`;\n\n/**\n * Where `image.sh` publishes the helper layer of a platform version, in the\n * API's bucket, for `cofactor sandbox push` to add to an uploaded image.\n * Republishing the same version replaces it.\n */\nexport function helperLayerKey(platformVersion: number): string {\n return `sandbox-v2/helper/v${platformVersion}.tar`;\n}\n\n/**\n * Where `image.sh` publishes a provider's object storage server (one binary\n * under /usr/local/bin) for a platform version, next to the helper layer.\n * `cofactor sandbox push` adds it only when the definition declares that\n * provider, so no push carries a server it doesn't use.\n */\nexport function objectStorageLayerKey(provider: ObjectStorageProvider, platformVersion: number): string {\n return `sandbox-v2/object-storage/${provider}/v${platformVersion}.tar`;\n}\n\n/**\n * The PATH the helper (as root) finds programs on, and every child starts\n * from before the image's and the definition's env. A Blackcat rootfs\n * import does not apply the image's Docker ENV.\n */\nexport const BASE_PATH = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin';\n\n/**\n * What root runs, and so what the sandbox account (uid 1000, given the\n * WORKDIR) must never own: a WORKDIR at or above any of these (the programs\n * root finds, the libraries and loader they link), or inside a directory\n * root reads its configuration from, would hand it the means to run code\n * as root.\n */\nconst ROOT_RUN_PATHS = [\n ...BASE_PATH.split(':'),\n '/lib',\n '/lib64',\n '/usr/lib',\n '/usr/lib64',\n '/usr/libexec',\n '/etc',\n // Devices and kernel interfaces, and root's own home.\n '/dev',\n '/proc',\n '/sys',\n '/root',\n IMAGE_DIR,\n];\nconst ROOT_CONFIG_DIRS = ['/etc', IMAGE_DIR];\n\n/** Whether the sandbox account may be given `dir` (an image's WORKDIR, as declared or as resolved). */\nexport function isSafeWorkdir(dir: string): boolean {\n // A canonical absolute path: no `.`/`..` segments, repeated or trailing slashes.\n const segments = dir.split('/').slice(1);\n if (segments.some((segment) => segment === '' || segment === '.' || segment === '..')) return false;\n if (ROOT_RUN_PATHS.some((path) => path === dir || path.startsWith(`${dir}/`))) return false;\n return !ROOT_CONFIG_DIRS.some((path) => dir.startsWith(`${path}/`));\n}\n\n/**\n * What an uploaded image's own config would have applied. Blackcat's import\n * keeps the filesystem but drops the image's ENV and WORKDIR, so\n * `cofactor sandbox push` records them at `/cofactor/image.json` and the\n * helper runs every service, prepare command and action with them.\n */\nexport const imageConfigSchema = z.strictObject({\n /** Where children run. The helper gives it to the sandbox account, so never `/` or a system directory. */\n workdir: z\n .string()\n .startsWith('/', 'the image needs an absolute WORKDIR')\n .refine(isSafeWorkdir, 'the image needs a WORKDIR of its own, such as /app (not /, nor a system directory)'),\n env: z.record(z.string(), z.string()),\n});\nexport type ImageConfig = z.infer<typeof imageConfigSchema>;\n\n/**\n * Bumped whenever the helper or its contract with the platform layer changes\n * incompatibly. Old layers keep rejecting the new schema until they are\n * rebuilt from a definition of this version, so it is part of both the\n * platform template's identity and the toolchain key.\n *\n * 3: `prepareInputSchema` gained a required `copy` field and the `port` param\n * variant gained `live` — an old helper's `strictObject`s reject both, so\n * every layer built from an older platform version (toolchain, base, ready)\n * must be rebuilt from this one before any `prepare` (full or copy) can\n * succeed. Bumping this forces that rebuild via `toolchainKey`/\n * `platformTemplateName`; the platform image itself must also be republished\n * (`packages/sandbox-helper/image.sh`) as part of rolling this out — see\n * specs/pending/sandboxes-v2-ready-snapshots.md.\n *\n * 4: the helper's contract grew `object_storage` (an old helper's\n * `strictObject` rejects the field, and it has neither the implicit storage\n * services nor the `object-storage-front` command). Same rollout as 3: layers\n * rebuild, the platform image (which now carries both providers' servers) is\n * republished, and every existing push must be pushed again — see\n * specs/pending/sandboxes-v2-object-storage.md.\n *\n * 5: Blackcat's Firecracker 1.17 (alpha.68+) moved the snapshot format (6.0.0\n * to 12.0.0) with no migration, so no template built before it restores. The\n * helper's contract is unchanged; the bump only retires every recorded template\n * row and names the rebuilt platform template anew (`clearStaleToolchain`).\n * Republish the platform image and helper layers with `image.sh` for each stage\n * as part of rolling this out — see docs/features/blackcat-execution-platform.md\n * (\"Rebuilding templates\").\n *\n * 6: the guest helper gained the private `auth` command. Native persona login\n * requires its script; rebuild and republish helper layers and\n * ready snapshots so old helpers cannot silently accept the new contract.\n */\nexport const PLATFORM_VERSION = 6;\n\n/** `cofactor-sandbox build`/`action` exit codes its callers act on. */\nexport const EXIT = {\n /** The commit's definition needs another toolchain: build it on a sandbox of that one. */\n TOOLCHAIN_MISMATCH: 3,\n /**\n * The commit declares personas or per-sandbox params (or a resolved\n * environment chain) this prepared sandbox was not given: prepare a new\n * sandbox for it instead.\n */\n PREPARE_MISMATCH: 4,\n /** The action script exited non-zero, or was killed at its timeout. */\n ACTION_FAILED: 5,\n /** The action is unknown, its input failed validation, or the sandbox has not been prepared. */\n INVALID_INPUT: 6,\n /** The action produced no parseable JSON result line, or its result failed the output schema. */\n INVALID_OUTPUT: 7,\n /** Authentication did not return valid, bounded storage state. */\n AUTH_FAILED: 8,\n} as const;\n\n// Every toolchain gets what `build` itself needs to clone and fetch.\nexport const SETUP_PRELUDE = `set -euo pipefail\nexport DEBIAN_FRONTEND=noninteractive\napt-get update -qq\napt-get install -y -qq --no-install-recommends git ca-certificates curl openssl >/dev/null\n`;\n\nconst NAME = /^[a-z][a-z0-9-]{0,31}$/;\nconst PARAM = /^[a-z][a-z0-9_]*$/;\nconst ENV_NAME = /^[A-Z_][A-Z0-9_]*$/;\nconst PARAM_REF = /\\$\\{params\\.([a-z][a-z0-9_]*)\\}/g;\n/** Action/operation input and output property names. */\nconst IO_NAME = /^[a-z][a-z0-9_]*$/;\n/** A seed step id, referenced as `${id.key}`. */\nconst STEP_ID = /^[a-z][a-z0-9_]*$/;\n/** A `${stepId.key}` reference, anywhere in a string. */\nconst STEP_REF = /\\$\\{([a-z][a-z0-9_]*)\\.([a-z][a-z0-9_]*)\\}/g;\n/** The whole string is exactly one reference — the value is the raw output, not a string interpolation. */\nconst EXACT_STEP_REF = /^\\$\\{([a-z][a-z0-9_]*)\\.([a-z][a-z0-9_]*)\\}$/;\n/** A bucket name both providers accept: 3–63 lowercase letters, digits, `-` and `.`, starting and ending alphanumeric. */\nconst BUCKET = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;\nconst GCS_RESERVED_BUCKETS = new Set(['storage', 'upload', 'download', 'batch']);\n\nconst health = z.union([\n z.strictObject({ command: z.string().min(1) }),\n z.strictObject({ http: z.number().int().min(1).max(65535), path: z.string().startsWith('/') }),\n]);\n\nconst service = z.strictObject({\n run: z.string().min(1),\n health,\n /** Not started until the build lifecycle has completed (it needs the build's output). */\n after_build: z.boolean().default(false),\n /**\n * Runs with per-sandbox values: never started in a layer (whose params are\n * the definition's defaults), started by the first `prepare`, restarted by\n * every later one.\n */\n reload_on_prepare: z.boolean().default(false),\n});\n\n/**\n * A per-sandbox value. A string is a fixed value. `{port}` is the sandbox's\n * public origin for that port; `{secret: true}` is a fresh random value per\n * sandbox (e.g. a session-signing key). Layers, which no one is handed, see a\n * local origin and a placeholder instead.\n *\n * A `port` param may set `live: true`: its value changes every time a\n * sandbox is restored as a fresh copy (each copy gets its own public URLs),\n * but nothing needs to restart for the sandbox's app to pick up the new\n * value — the app is expected to re-read it at request time instead (see\n * `resolveLiveEnv`). `prepare` writes every live param's resolved env to\n * `LIVE_ENV_FILE` (runtime.ts) on every call, and skips restarting\n * `reload_on_prepare` services when nothing else changed (`restartRequired`).\n * Not offered on `secret` params — see the `auth_secret` row of the per-copy\n * work table in specs/pending/sandboxes-v2-ready-snapshots.md for why.\n */\nconst param = z.union([\n z.string(),\n z.strictObject({ port: z.string().regex(NAME), live: z.boolean().optional() }),\n z.strictObject({ secret: z.literal(true) }),\n]);\n\nconst buildStep = z.strictObject({\n name: z.string().regex(NAME),\n run: z.string().min(1),\n /**\n * Git pathspecs over tracked files (`:(glob)` magic is applied), relative to\n * the definition's directory. The step reruns when the blob ids they match\n * change, or when an earlier step ran.\n */\n inputs: z.array(z.string().min(1)).min(1),\n});\n\nconst literalValue = z.union([z.string(), z.number(), z.boolean()]);\n\n/** A `persona` input takes one of the definition's declared persona names, as a string. */\nconst inputProp = z.strictObject({\n type: z.enum(['string', 'number', 'integer', 'boolean', 'persona']),\n description: z.string().optional(),\n required: z.boolean().optional(),\n default: literalValue.optional(),\n enum: z.array(z.union([z.string(), z.number()])).optional(),\n /** Never echoed back by anything that logs or returns action input. */\n sensitive: z.boolean().optional(),\n});\n\nconst outputProp = z.strictObject({\n type: z.enum(['string', 'number', 'integer', 'boolean']),\n description: z.string().optional(),\n});\n\n/** A repository action: a typed script, the one way data gets into a sandbox. */\nconst action = z.strictObject({\n description: z.string().min(1),\n /** Script path, relative to the definition's directory; run via `bash`. */\n run: z.string().min(1),\n input: z.record(z.string().regex(IO_NAME), inputProp).default({}),\n output: z.record(z.string().regex(IO_NAME), outputProp).default({}),\n timeout: z.number().int().min(1).max(1800).default(60),\n});\n\nconst seedStep = z.strictObject({\n id: z.string().regex(STEP_ID),\n /** A declared action's slug, or `<plugin>.<op>` for an emulator operation. */\n action: z.string().min(1),\n input: z.record(z.string(), literalValue).default({}),\n});\n\nconst environment = z.strictObject({\n description: z.string().min(1),\n extends: z.string().regex(NAME).optional(),\n seeds: z.array(seedStep).default([]),\n /** Merged parent-first, this environment's values last. */\n env: z.record(z.string().regex(ENV_NAME), z.string()).default({}),\n});\n\n/**\n * Run by `prepare` after the sandbox's services have restarted with its\n * values. Each param is also in their env as COFACTOR_PARAM_<NAME>.\n */\nconst prepareCommands = z.array(z.string().min(1));\n\n/** Every field both deliveries share; see `definitionSchema` and `githubDefinitionSchema`. */\nconst sharedFields = {\n version: z.literal(1),\n /** The machine every sandbox of this application gets; a promoted layer keeps it. */\n resources: z.strictObject({\n vcpus: z.number().int().min(1).max(32),\n memory_mib: z.number().int().min(1024).max(65536),\n }),\n ports: z.record(\n z.string().regex(NAME),\n z.strictObject({ port: z.number().int().min(1).max(65535), access: z.enum(['public', 'private']) })\n ),\n /**\n * Per-sandbox values, set by `prepare`. Build steps and layers see the\n * layer value (see `param`); `reload_on_prepare` services and prepare\n * commands see the sandbox's own.\n */\n params: z.record(z.string().regex(PARAM), param),\n /** `${params.NAME}` is replaced by the parameter's value. */\n env: z.record(z.string().regex(ENV_NAME), z.string()),\n services: z.record(z.string().regex(NAME), service),\n /**\n * The users a sandbox is seeded with, by name. Cofactor generates each\n * one's password per sandbox and hands it to `lifecycle.prepare` only (as\n * COFACTOR_PERSONAS, a JSON map of name to email and password).\n */\n personas: z.record(z.string().regex(NAME), z.strictObject({ email: z.email() })).default({}),\n /** Private script used to mint browser state for one declared persona. */\n authentication: z.strictObject({\n run: z.string().min(1).refine(\n (path) => !path.startsWith('/') && path.split('/').every((part) => part !== '' && part !== '.' && part !== '..'),\n 'must be a relative script path inside the definition directory'\n ),\n }).optional(),\n /** Typed scripts: the one way data gets into a sandbox (seeds, the CLI, agents). */\n actions: z.record(z.string().regex(NAME), action).default({}),\n /** Emulator plugin id -> its config. Declaring any plugin adds the implicit `emulation` port. */\n emulation: z.record(z.string().regex(NAME), z.record(z.string(), z.unknown())).default({}),\n /**\n * An emulated S3 or GCS server with these buckets, created before the first\n * prepare. Declaring it adds the implicit public `object-storage` port and\n * the services that run the server (`objectStorageServices`), and gives\n * every child the provider's standard client env (`objectStorageEnv`).\n */\n object_storage: z\n .strictObject({\n provider: z.enum(OBJECT_STORAGE_PROVIDERS),\n buckets: z\n .array(\n z\n .string()\n .regex(BUCKET, '3–63 lowercase letters, digits, \"-\" and \".\", starting and ending with a letter or digit')\n // Names the real services refuse too, so an app's buckets carry over.\n .refine((name) => !name.includes('..'), 'no consecutive dots')\n .refine((name) => !/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(name), 'not shaped like an IP address')\n )\n .min(1)\n .refine((buckets) => new Set(buckets).size === buckets.length, 'bucket names must be unique'),\n })\n // The GCS front reads `/<bucket>/…` as an XML-style upload unless it is a\n // JSON API path (`/storage/v1/…`, `/upload/storage/v1/…`): those first\n // segments can't be buckets.\n .refine(\n (storage) => storage.provider !== 'gcs' || !storage.buckets.some((name) => GCS_RESERVED_BUCKETS.has(name)),\n { message: `a GCS bucket can't be named ${[...GCS_RESERVED_BUCKETS].join(', ')}`, path: ['buckets'] }\n )\n .optional(),\n /**\n * Named flavors of the same sandbox (different starting data and config).\n * Must include `default`; cross-checked, not schema-enforced, so the issue\n * carries a path.\n */\n environments: z.record(z.string().regex(NAME), environment),\n};\n\n/** Playwright storage state returned by `cofactor-sandbox auth`. */\nexport const sandboxAuthResultSchema = z.strictObject({\n cookies: z.array(z.strictObject({\n name: z.string(), value: z.string(), domain: z.string(), path: z.string(),\n expires: z.number(), httpOnly: z.boolean(), secure: z.boolean(),\n sameSite: z.enum(['Strict', 'Lax', 'None']),\n })),\n origins: z.array(z.strictObject({\n origin: z.string(),\n localStorage: z.array(z.strictObject({ name: z.string(), value: z.string() })),\n })),\n}).refine(\n (state) => state.cookies.length > 0 || state.origins.some((origin) => origin.localStorage.length > 0),\n 'must contain at least one cookie or local-storage entry'\n);\nexport type SandboxAuthResult = z.infer<typeof sandboxAuthResultSchema>;\nexport const SANDBOX_AUTH_MAX_BYTES = 1024 * 1024;\n\n/** Reject malformed or overlarge helper results without including state values in diagnostics. */\nexport function parseSandboxAuthResult(value: string): SandboxAuthResult | null {\n if (Buffer.byteLength(value, 'utf8') > SANDBOX_AUTH_MAX_BYTES) return null;\n let parsed: unknown;\n try { parsed = JSON.parse(value); } catch { return null; }\n const result = sandboxAuthResultSchema.safeParse(parsed);\n return result.success ? result.data : null;\n}\n\n/**\n * What every delivery's definition has: how the sandbox runs, prepares and\n * seeds. An uploaded image is already built, so its definition is exactly this.\n */\nconst definitionSchema = z.strictObject({\n ...sharedFields,\n lifecycle: z.strictObject({ prepare: prepareCommands }),\n});\n\n/** A GitHub-delivered definition also installs and builds the checkout. */\nconst githubDefinitionSchema = definitionSchema.extend({\n /**\n * Root shell script that installs the toolchain (packages, runtimes,\n * database clusters). With the resources, it keys the toolchain layer.\n */\n setup: z.string().min(1),\n lifecycle: z.strictObject({\n build: z.array(buildStep).min(1),\n prepare: prepareCommands,\n }),\n});\n\n/** What every delivery's definition has; everything after the build takes this. */\nexport type Definition = z.infer<typeof definitionSchema>;\n/** A GitHub-delivered definition: a `Definition` plus how to install and build the checkout. */\nexport type GithubDefinition = z.infer<typeof githubDefinitionSchema>;\nexport type ServiceSpec = Definition['services'][string];\nexport type BuildStep = GithubDefinition['lifecycle']['build'][number];\nexport type ActionDef = Definition['actions'][string];\nexport type SeedStep = Definition['environments'][string]['seeds'][number];\nexport type Environment = Definition['environments'][string];\n\n/** One problem found while parsing or cross-checking a definition, with a dotted path to it. */\nexport interface DefinitionIssue {\n path: string;\n message: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nconst PROP_TYPES = ['string', 'number', 'integer', 'boolean', 'persona'] as const;\ntype PropType = (typeof PROP_TYPES)[number];\n\n/** Narrows a duck-typed `prop.type` (from raw YAML) to the four/five declared values. */\nfunction isPropType(value: unknown): value is PropType {\n return typeof value === 'string' && (PROP_TYPES as readonly string[]).includes(value);\n}\n\nfunction typeMatches(type: PropType, value: unknown): boolean {\n // A persona reference is a string; whether it names a declared persona is\n // checked separately (validateActionInput, checkSeedInputAgainstAction).\n if (type === 'string' || type === 'persona') return typeof value === 'string';\n if (type === 'boolean') return typeof value === 'boolean';\n if (type === 'integer') return typeof value === 'number' && Number.isInteger(value);\n return typeof value === 'number';\n}\n\nfunction isExactStepRef(value: unknown): boolean {\n return typeof value === 'string' && EXACT_STEP_REF.test(value);\n}\n\n/**\n * A step's declared outputs, keyed by property name, each mapped to whether\n * it is a primitive (string/number/boolean) that `${id.key}` may reference. A\n * repo action's outputs are always primitive (`outputProp.type` has no other\n * option); an emulator operation's may not be (e.g. `email.sent-emails`'s\n * `emails` array).\n */\ntype StepOutputs = Record<string, Record<string, boolean>>;\n\n/** Every `${stepId.key}` in `value` must reference an already-resolved, primitive output. */\nfunction checkStepReferences(value: unknown, outputs: StepOutputs, path: string, issues: DefinitionIssue[]): void {\n if (typeof value !== 'string') return;\n for (const match of value.matchAll(STEP_REF)) {\n const stepId = match[1]!;\n const key = match[2]!;\n const stepOutputs = outputs[stepId];\n if (!stepOutputs) {\n issues.push({ path, message: `references step \"${stepId}\", which has not run yet (or does not exist)` });\n continue;\n }\n if (!(key in stepOutputs)) {\n issues.push({ path, message: `references undeclared output \"${key}\" of step \"${stepId}\"` });\n continue;\n }\n if (!stepOutputs[key]) {\n issues.push({\n path,\n message: `output \"${key}\" of step \"${stepId}\" is not a string/number/boolean and cannot be referenced`,\n });\n }\n }\n}\n\n/** Resolve `environments[name]`'s parent chain (raw, duck-typed), parent-first. Throws on a cycle. */\nfunction resolveRawChain(environments: Record<string, unknown>, name: string): unknown[] {\n const order: string[] = [];\n let current: string | undefined = name;\n const seen = new Set<string>();\n while (current) {\n if (seen.has(current)) throw new Error(`extends cycle at \"${current}\"`);\n seen.add(current);\n order.unshift(current);\n const env: unknown = environments[current];\n current = isRecord(env) && typeof env.extends === 'string' ? env.extends : undefined;\n }\n return order.flatMap((envName) => {\n const env = environments[envName];\n return isRecord(env) && Array.isArray(env.seeds) ? env.seeds : [];\n });\n}\n\n/**\n * Validate a seed's static (literal, non-referencing) input values against a\n * repo action's declared input properties, and check every `${id.key}`\n * reference against outputs resolved so far.\n */\nfunction checkSeedInputAgainstAction(\n input: unknown,\n declaredAction: unknown,\n outputs: StepOutputs,\n personas: Set<string>,\n path: string,\n issues: DefinitionIssue[]\n): void {\n const given = isRecord(input) ? input : {};\n const declaredInputs = isRecord(declaredAction) && isRecord(declaredAction.input) ? declaredAction.input : {};\n for (const [key, value] of Object.entries(given)) {\n checkStepReferences(value, outputs, `${path}.${key}`, issues);\n if (!(key in declaredInputs)) issues.push({ path: `${path}.${key}`, message: `unknown input \"${key}\"` });\n }\n for (const [name, prop] of Object.entries(declaredInputs)) {\n if (!isRecord(prop)) continue;\n const hasValue = name in given;\n const hasDefault = prop.default !== undefined;\n if (!hasValue && prop.required === true && !hasDefault) {\n issues.push({ path: `${path}.${name}`, message: `missing required input \"${name}\"` });\n continue;\n }\n if (!hasValue) continue;\n const value = given[name];\n if (isExactStepRef(value)) continue; // resolved at claim time; skip the literal type/persona/enum check\n if (isPropType(prop.type) && !typeMatches(prop.type, value)) {\n issues.push({ path: `${path}.${name}`, message: `must be ${String(prop.type)}` });\n } else if (prop.type === 'persona' && typeof value === 'string' && !personas.has(value)) {\n issues.push({\n path: `${path}.${name}`,\n message: `must be a declared persona (one of ${[...personas].join(', ') || '(none declared)'})`,\n });\n } else if (Array.isArray(prop.enum) && !(prop.enum as unknown[]).includes(value)) {\n issues.push({ path: `${path}.${name}`, message: `must be one of ${prop.enum.join(', ')}` });\n }\n }\n}\n\n/** Same as above, but against an emulator operation's zod input schema. */\nfunction checkSeedInputAgainstOperation(\n input: unknown,\n operationInput: z.ZodType,\n outputs: StepOutputs,\n path: string,\n issues: DefinitionIssue[]\n): void {\n const given = isRecord(input) ? input : {};\n let anyReference = false;\n for (const [key, value] of Object.entries(given)) {\n checkStepReferences(value, outputs, `${path}.${key}`, issues);\n if (isExactStepRef(value)) anyReference = true;\n }\n // A step's value can only be validated once resolved; skip the schema check\n // when any input references an earlier step (references are still checked above).\n if (anyReference) return;\n const parsed = operationInput.safeParse(given);\n if (!parsed.success) {\n for (const issue of parsed.error.issues) {\n issues.push({ path: [path, ...issue.path.map(String)].join('.'), message: issue.message });\n }\n }\n}\n\nconst PRIMITIVE_JSON_TYPES = new Set(['string', 'number', 'integer', 'boolean']);\n\n/**\n * Declared output keys of an operation, each mapped to whether it is a\n * primitive `${id.key}` may reference (see `StepOutputs`). Read from the\n * output's JSON Schema, the same representation operation inputs are checked\n * against (packages/emulation operations.test.ts).\n */\nfunction operationOutputShape(outputSchema: z.ZodType): Record<string, boolean> {\n const { properties } = z.toJSONSchema(outputSchema) as { properties?: Record<string, { type?: unknown }> };\n if (!properties) return {};\n return Object.fromEntries(\n Object.entries(properties).map(([key, prop]) => [key, typeof prop.type === 'string' && PRIMITIVE_JSON_TYPES.has(prop.type)])\n );\n}\n\n/**\n * Cross-checks that zod cannot express, run against whatever shape parsed\n * (typed `Definition` when schema validation succeeded, or the raw YAML\n * object otherwise) so every problem is reported in one pass.\n */\nfunction crossCheck(data: unknown, delivery: Delivery, issues: DefinitionIssue[]): void {\n if (!isRecord(data)) return;\n const actions = isRecord(data.actions) ? data.actions : {};\n const emulation = isRecord(data.emulation) ? data.emulation : {};\n const environments = isRecord(data.environments) ? data.environments : {};\n const ports = isRecord(data.ports) ? data.ports : {};\n const personas = isRecord(data.personas) ? new Set(Object.keys(data.personas)) : new Set<string>();\n const hasEmulation = Object.keys(emulation).length > 0;\n\n if (!('default' in environments)) {\n issues.push({ path: 'environments', message: 'must include a \"default\" environment' });\n }\n\n // emulation plugin ids: every declared plugin must be registered. There is\n // no per-plugin config schema to validate against (EmulationPlugin has none).\n for (const pluginId of Object.keys(emulation)) {\n if (!getPlugin(pluginId)) issues.push({ path: `emulation.${pluginId}`, message: `unknown emulation plugin \"${pluginId}\"` });\n }\n\n // extends: existence and cycles. `broken` collects every environment whose\n // own or inherited extends chain is unresolvable, so the seed-chain pass\n // below skips it (its own seeds get no path to validate against).\n const broken = new Set<string>();\n for (const name of Object.keys(environments)) {\n const env = environments[name];\n if (!isRecord(env) || typeof env.extends !== 'string') continue;\n if (!(env.extends in environments)) {\n issues.push({ path: `environments.${name}.extends`, message: `unknown environment \"${env.extends}\"` });\n broken.add(name);\n continue;\n }\n try {\n resolveRawChain(environments, name);\n } catch (error) {\n issues.push({ path: `environments.${name}.extends`, message: error instanceof Error ? error.message : String(error) });\n broken.add(name);\n }\n }\n\n // env keys.\n for (const [name, env] of Object.entries(environments)) {\n if (!isRecord(env) || !isRecord(env.env)) continue;\n for (const key of Object.keys(env.env)) {\n if (!ENV_NAME.test(key)) issues.push({ path: `environments.${name}.env.${key}`, message: 'not a valid env var name' });\n }\n }\n\n /**\n * Validate one environment's OWN seeds (not its inherited ones), labeled\n * with its declaring environment name and local index, and only once no\n * matter how many descendants extend it. Returns the cumulative outputs and\n * step ids after this environment's own seeds, for a child to build on.\n */\n const resolved = new Map<string, { outputs: StepOutputs; seenIds: Set<string> }>();\n function resolveEnvSeeds(name: string): { outputs: StepOutputs; seenIds: Set<string> } {\n const cached = resolved.get(name);\n if (cached) return cached;\n const env = environments[name];\n let outputs: StepOutputs = {};\n let seenIds = new Set<string>();\n if (isRecord(env) && typeof env.extends === 'string' && env.extends in environments && !broken.has(name)) {\n const parent = resolveEnvSeeds(env.extends);\n outputs = { ...parent.outputs };\n seenIds = new Set(parent.seenIds);\n }\n const ownSeeds = isRecord(env) && Array.isArray(env.seeds) ? env.seeds : [];\n ownSeeds.forEach((step: unknown, index: number) => {\n if (!isRecord(step)) return;\n const path = `environments.${name}.seeds[${index}]`;\n const id = typeof step.id === 'string' ? step.id : undefined;\n if (id) {\n if (seenIds.has(id)) issues.push({ path: `${path}.id`, message: `duplicate step id \"${id}\"` });\n seenIds.add(id);\n }\n const actionRef = typeof step.action === 'string' ? step.action : undefined;\n let declaredOutputs: Record<string, boolean> | undefined;\n if (actionRef?.includes('.')) {\n const [pluginId, opId] = actionRef.split('.', 2) as [string, string];\n if (!(pluginId in emulation)) {\n issues.push({ path: `${path}.action`, message: `plugin \"${pluginId}\" is not declared in emulation` });\n } else {\n const op = findOperation(pluginId, opId);\n if (!op) {\n issues.push({ path: `${path}.action`, message: `unknown operation \"${actionRef}\"` });\n } else {\n declaredOutputs = operationOutputShape(op.output);\n checkSeedInputAgainstOperation(step.input, op.input, outputs, `${path}.input`, issues);\n }\n }\n } else if (actionRef !== undefined) {\n const declared = actions[actionRef];\n if (!declared) {\n issues.push({ path: `${path}.action`, message: `unknown action \"${actionRef}\"` });\n } else {\n const outputProps = isRecord(declared) && isRecord(declared.output) ? declared.output : {};\n declaredOutputs = Object.fromEntries(Object.keys(outputProps).map((key) => [key, true]));\n checkSeedInputAgainstAction(step.input, declared, outputs, personas, `${path}.input`, issues);\n }\n }\n if (id) outputs = { ...outputs, [id]: declaredOutputs ?? {} };\n });\n const result = { outputs, seenIds };\n resolved.set(name, result);\n return result;\n }\n\n // seed chains: duplicate ids, unknown actions/operations, static input and references.\n for (const name of Object.keys(environments)) {\n if (broken.has(name)) continue; // the cycle/unknown-parent is already reported above\n resolveEnvSeeds(name);\n }\n\n // ports: reserved names, and (with emulation or object storage on) no user\n // port on theirs, plus Blackcat's 8-port cap.\n if ('emulation' in ports) issues.push({ path: 'ports.emulation', message: '\"emulation\" is a reserved port name' });\n if (OBJECT_STORAGE_PORT_NAME in ports) {\n issues.push({ path: `ports.${OBJECT_STORAGE_PORT_NAME}`, message: `\"${OBJECT_STORAGE_PORT_NAME}\" is a reserved port name` });\n }\n const storage = isRecord(data.object_storage) ? data.object_storage : undefined;\n const provider = OBJECT_STORAGE_PROVIDERS.find((name) => name === storage?.provider);\n if (provider) {\n for (const [portName, portValue] of Object.entries(ports)) {\n if (!isRecord(portValue) || typeof portValue.port !== 'number') continue;\n if (RESERVED_STORAGE_PORTS[provider].includes(portValue.port)) {\n issues.push({\n path: `ports.${portName}.port`,\n message: `port ${portValue.port} is reserved for the ${provider} object storage server`,\n });\n }\n }\n }\n if (hasEmulation) {\n for (const [portName, portValue] of Object.entries(ports)) {\n if (portName === 'emulation' || !isRecord(portValue)) continue;\n if (portValue.port === 80 || portValue.port === 443) {\n issues.push({ path: `ports.${portName}.port`, message: 'ports 80 and 443 are reserved for the emulator' });\n }\n }\n }\n const impliedPortCount =\n Object.keys(ports).filter((p) => p !== 'emulation' && p !== OBJECT_STORAGE_PORT_NAME).length +\n (hasEmulation ? 1 : 0) +\n (storage ? 1 : 0);\n if (impliedPortCount > 8) {\n issues.push({ path: 'ports', message: `at most 8 ports are allowed (Blackcat's limit), got ${impliedPortCount}` });\n }\n\n const services = isRecord(data.services) ? data.services : {};\n for (const name of OBJECT_STORAGE_SERVICE_NAMES) {\n if (name in services) issues.push({ path: `services.${name}`, message: `\"${name}\" is a reserved service name` });\n }\n\n // build steps: duplicates (kept from before actions/environments existed).\n const lifecycle = isRecord(data.lifecycle) ? data.lifecycle : {};\n if (Array.isArray(lifecycle.build)) {\n const seen = new Set<string>();\n lifecycle.build.forEach((step: unknown, index: number) => {\n if (!isRecord(step) || typeof step.name !== 'string') return;\n if (seen.has(step.name)) {\n issues.push({ path: `lifecycle.build[${index}].name`, message: `build step \"${step.name}\" is declared twice` });\n }\n seen.add(step.name);\n });\n }\n\n // An uploaded image is already built: say so where a build is declared.\n if (delivery === 'upload') {\n if ('setup' in data) {\n issues.push({ path: 'setup', message: 'an uploaded image is already built: remove setup (install it in the image)' });\n }\n if (isRecord(data.lifecycle) && 'build' in data.lifecycle) {\n issues.push({\n path: 'lifecycle.build',\n message: 'an uploaded image is already built: remove lifecycle.build (build it in the image)',\n });\n }\n for (const [name, spec] of Object.entries(services)) {\n if (isRecord(spec) && spec.after_build === true) {\n issues.push({ path: `services.${name}.after_build`, message: 'an uploaded image has no build to wait for' });\n }\n }\n }\n\n // params referring to undeclared ports, and env referring to undeclared params.\n const params = isRecord(data.params) ? data.params : {};\n for (const [name, value] of Object.entries(params)) {\n if (\n isRecord(value) &&\n 'port' in value &&\n typeof value.port === 'string' &&\n !(value.port in ports) &&\n !(storage && value.port === OBJECT_STORAGE_PORT_NAME)\n ) {\n issues.push({ path: `params.${name}.port`, message: `refers to undeclared port \"${value.port}\"` });\n }\n }\n const env = isRecord(data.env) ? data.env : {};\n for (const [key, template] of Object.entries(env)) {\n if (typeof template !== 'string') continue;\n for (const match of template.matchAll(PARAM_REF)) {\n const paramName = match[1]!;\n if (!(paramName in params)) issues.push({ path: `env.${key}`, message: `references undeclared param \"${paramName}\"` });\n }\n }\n}\n\n/**\n * Adds what declarations imply: the public, port-80 `emulation` port once any\n * plugin is declared, and with `object_storage` the public `object-storage`\n * port and the services that run its server.\n */\nfunction withImplicitPieces<D extends Definition>(definition: D): D {\n let result = definition;\n if (Object.keys(definition.emulation).length > 0) {\n result = { ...result, ports: { ...result.ports, emulation: { port: 80, access: 'public' } } };\n }\n const storage = definition.object_storage;\n if (storage) {\n result = {\n ...result,\n ports: { ...result.ports, [OBJECT_STORAGE_PORT_NAME]: { port: OBJECT_STORAGE_PORT[storage.provider], access: 'public' } },\n services: { ...result.services, ...objectStorageServices(storage.provider) },\n };\n }\n return result;\n}\n\n/**\n * Parse and cross-check a definition delivered by `delivery`. Schema issues\n * (zod collects every one, not just the first) and cross-checks are both\n * returned with a dotted path; cross-checks also run against the raw YAML\n * object when schema parsing failed, so a structural problem (an extends\n * cycle, a duplicate step id) is still reported.\n */\nexport function validateDefinition(\n yamlText: string,\n delivery: 'github'\n): { definition?: GithubDefinition; issues: DefinitionIssue[] };\nexport function validateDefinition(\n yamlText: string,\n delivery: Delivery\n): { definition?: Definition; issues: DefinitionIssue[] };\nexport function validateDefinition(\n yamlText: string,\n delivery: Delivery\n): { definition?: Definition; issues: DefinitionIssue[] } {\n const issues: DefinitionIssue[] = [];\n let raw: unknown;\n try {\n raw = parse(yamlText);\n } catch (error) {\n return { issues: [{ path: '', message: `invalid YAML: ${error instanceof Error ? error.message : String(error)}` }] };\n }\n const parsed = (delivery === 'github' ? githubDefinitionSchema : definitionSchema).safeParse(raw);\n if (!parsed.success) {\n for (const issue of parsed.error.issues) {\n issues.push({ path: issue.path.map(String).join('.') || '(root)', message: issue.message });\n }\n crossCheck(raw, delivery, issues);\n return { issues };\n }\n crossCheck(parsed.data, delivery, issues);\n return { definition: withImplicitPieces(parsed.data), issues };\n}\n\n/** `validateDefinition`, throwing an error listing every issue (one per line, `path: message`). */\nexport function parseDefinition(yamlText: string, delivery: 'github'): GithubDefinition;\nexport function parseDefinition(yamlText: string, delivery: Delivery): Definition;\nexport function parseDefinition(yamlText: string, delivery: Delivery): Definition {\n const { definition, issues } = validateDefinition(yamlText, delivery);\n if (issues.length > 0) throw new Error(issues.map((issue) => `${issue.path}: ${issue.message}`).join('\\n'));\n return definition!;\n}\n\n/**\n * Identifies the toolchain layer a definition needs: everything fixed when a\n * layer is made (the platform version, the prelude, the machine, the setup\n * script, and the ports a template exposes). Anything else in the definition\n * is applied per commit and never needs a new toolchain.\n */\nexport function toolchainKey(definition: GithubDefinition): string {\n return createHash('sha256')\n .update(String(PLATFORM_VERSION))\n .update('\\0')\n .update(SETUP_PRELUDE)\n .update('\\0')\n .update(JSON.stringify(definition.resources))\n .update('\\0')\n .update(definition.setup)\n .update('\\0')\n .update(JSON.stringify(Object.entries(definition.ports).sort(([a], [b]) => a.localeCompare(b))))\n .digest('hex');\n}\n\n/** Param values inside layers: fixed values, local port origins and placeholders for secrets. */\nexport function layerParams(definition: Definition): Record<string, string> {\n return Object.fromEntries(\n Object.entries(definition.params).map(([name, value]) => {\n if (typeof value === 'string') return [name, value];\n if ('port' in value) return [name, `http://127.0.0.1:${definition.ports[value.port]!.port}`];\n return [name, `layer-placeholder-${name}`];\n })\n );\n}\n\n/**\n * Exactly what a prepared sandbox was given values for — never more:\n * personas, per-sandbox params, the chosen environment's name, the resolved\n * chain's ENV (not its seeds, which run once at claim time and are Cofactor's\n * to re-run, not the sandbox's own state), the emulation section and the\n * object storage (whose buckets prepare creates). Seeds\n * run after prepare; a PR that only changes seed data does not need a fresh\n * sandbox. A commit that would change any of this needs a newly prepared\n * sandbox instead (EXIT.PREPARE_MISMATCH).\n */\nexport function preparedShape(definition: Definition, environment: string): string {\n const sorted = <T>(record: Record<string, T>) =>\n Object.fromEntries(Object.entries(record).sort(([a], [b]) => a.localeCompare(b)));\n const perSandbox = Object.fromEntries(Object.entries(definition.params).filter(([, value]) => typeof value !== 'string'));\n const env = environmentChain(definition, environment).env;\n return JSON.stringify({\n personas: sorted(definition.personas),\n params: sorted(perSandbox),\n environment,\n env,\n emulation: sorted(definition.emulation),\n object_storage: definition.object_storage && {\n provider: definition.object_storage.provider,\n buckets: [...definition.object_storage.buckets].sort(),\n },\n });\n}\n\n/**\n * A new sandbox's own param values, for `prepare`: its public port origins\n * and fresh secrets. Fixed values keep the definition's.\n */\nexport function sandboxParams(definition: Definition, portUrls: Record<string, string>): Record<string, string> {\n const values: Record<string, string> = {};\n for (const [name, value] of Object.entries(definition.params)) {\n if (typeof value === 'string') continue;\n if ('port' in value) {\n const url = portUrls[value.port];\n if (!url) throw new Error(`the sandbox has no URL for port \"${value.port}\"`);\n values[name] = url;\n } else {\n values[name] = randomBytes(32).toString('hex');\n }\n }\n return values;\n}\n\nexport function resolveEnv(definition: Definition, params: Record<string, string>): Record<string, string> {\n const values = { ...layerParams(definition), ...params };\n return Object.fromEntries(\n Object.entries(definition.env).map(([key, template]) => [\n key,\n template.replace(PARAM_REF, (_, name: string) => values[name]!),\n ])\n );\n}\n\n/** The names of every `port` param, live or not: every one of these changes on every copy (each copy has its own port URLs). */\nexport function portParamNames(definition: Definition): Set<string> {\n return new Set(\n Object.entries(definition.params)\n .filter(([, value]) => typeof value !== 'string' && 'port' in value)\n .map(([name]) => name)\n );\n}\n\n/** The names of every param declared `live: true` (see `param`'s doc comment) — necessarily a subset of `portParamNames`. */\nfunction liveParamNames(definition: Definition): Set<string> {\n return new Set(\n Object.entries(definition.params)\n .filter(([, value]) => typeof value !== 'string' && 'port' in value && value.live === true)\n .map(([name]) => name)\n );\n}\n\n/**\n * `definition.env` keys whose template references at least one param and\n * every param it references is live: the subset of the resolved env a\n * sandbox's `reload_on_prepare` services never need a restart to pick up,\n * because `prepare` writes it to `LIVE_ENV_FILE` (runtime.ts) on every call\n * regardless of whether it restarts anything. A key that mixes a live and a\n * non-live param reference is deliberately excluded: it still needs a\n * restart whenever the non-live half changes, so the app cannot treat it as\n * always current.\n */\nfunction liveEnvKeys(definition: Definition): Set<string> {\n const live = liveParamNames(definition);\n const keys = new Set<string>();\n for (const [key, template] of Object.entries(definition.env)) {\n const refs = [...template.matchAll(PARAM_REF)].map((match) => match[1]!);\n if (refs.length > 0 && refs.every((name) => live.has(name))) keys.add(key);\n }\n return keys;\n}\n\n/**\n * The resolved env values a sandbox's live params currently determine — what\n * `prepare` writes to `LIVE_ENV_FILE` for the app to re-read at request time\n * instead of waiting for a restart.\n */\nexport function resolveLiveEnv(definition: Definition, params: Record<string, string>): Record<string, string> {\n const keys = liveEnvKeys(definition);\n const resolved = resolveEnv(definition, params);\n return Object.fromEntries(Object.entries(resolved).filter(([key]) => keys.has(key)));\n}\n\n/**\n * Whether a sandbox's `reload_on_prepare` services must restart for `next`'s\n * values to take effect, given what they were last prepared with (`previous`\n * is `undefined` the first time a sandbox is prepared, which always needs a\n * restart — the services have not started at all yet).\n *\n * The rule is the resolved env, not the raw params: restart iff the\n * environment, the repository secrets, or the emulation env's resolved\n * values differ, or `resolveEnv(definition, previous.params)` and\n * `resolveEnv(definition, next.params)` differ on any key that is NOT in\n * `liveEnvKeys`. Comparing resolved env keys (rather than looping over param\n * names and skipping the live ones) is what a *mixed* key needs: a key\n * templated from both a live and a non-live param is not in `liveEnvKeys`\n * (see its doc comment), so any change to either param — including the live\n * one alone — changes that key's resolved value and correctly forces a\n * restart; a per-param diff that simply skipped live param names would miss\n * this, since it never looks at the key the live param feeds into.\n *\n * Deliberately does not compare `personas` or `emulation.config` (the\n * control-plane token): neither reaches `reload_on_prepare` services' env —\n * `childEnv`'s `for: 'service'` branch never includes personas, and only\n * `emulation.env` (the plugins' env-contract values, currently always `{}`;\n * see `prepareInputSchema`), not `emulation.config`, is merged in. Rotating\n * persona passwords or the emulator's token on every copy is therefore free:\n * it reconfigures the emulator sidecar (`cofactor-sandbox prepare`'s\n * unconditional `/emulation` control call) and reruns `lifecycle.prepare`\n * (a DB write, not a restart), never the app services this function gates.\n */\nexport function restartRequired(\n definition: Definition,\n previous: PrepareInput | undefined,\n next: PrepareInput\n): boolean {\n if (!previous) return true;\n if (previous.environment !== next.environment) return true;\n if (JSON.stringify(previous.secrets) !== JSON.stringify(next.secrets)) return true;\n if (JSON.stringify(previous.emulation?.env ?? {}) !== JSON.stringify(next.emulation?.env ?? {})) return true;\n const live = liveEnvKeys(definition);\n const previousEnv = resolveEnv(definition, previous.params);\n const nextEnv = resolveEnv(definition, next.params);\n for (const key of Object.keys(definition.env)) {\n if (live.has(key)) continue;\n if (previousEnv[key] !== nextEnv[key]) return true;\n }\n return false;\n}\n\n/**\n * Resolve a copy prepare's full param set: every param the sandbox's previous\n * prepare had, with `given` (a copy's own `port` params — every one of them,\n * live or not: each copy has its own port URLs) applied over it. Any param\n * `given` omits, `secret` params among them, keeps the value the sandbox was\n * previously prepared with rather than a freshly generated one — necessary\n * for `auth_secret` (never reissued per copy) and harmless for a `live` port\n * param the caller happened to leave out (there is no real caller that\n * would: `copyPrepareInput` always sends every port param).\n *\n * Throws when `given` names a param the definition does not declare, or a\n * declared param that is not a `port` param (a `secret`, or a literal string)\n * — a copy prepare's caller (`copyPrepareInput`) must never attempt to change\n * one of those; the caller that wants to is not preparing a copy.\n *\n * Whether the result actually needs a restart is `restartRequired`'s call,\n * not this function's: a definition with no `live: true` params sends every\n * port param here exactly as before, and the resolved env then always\n * differs, so nothing here silently defeats an old definition's restart.\n */\nexport function resolveCopyParams(\n definition: Definition,\n previous: PrepareInput,\n given: Record<string, string>\n): Record<string, string> {\n const undeclared = Object.keys(given).filter((name) => !(name in definition.params));\n if (undeclared.length > 0) throw new Error(`not declared params: ${undeclared.join(', ')}`);\n const ports = portParamNames(definition);\n const notPort = Object.keys(given).filter((name) => !ports.has(name));\n if (notPort.length > 0) {\n throw new Error(`a copy prepare may only set port params, got: ${notPort.join(', ')}`);\n }\n return { ...previous.params, ...given };\n}\n\n/** The resolved seeds (parent first) and merged env (parent first, this environment last) of an environment chain. */\nexport interface ResolvedEnvironment {\n seeds: SeedStep[];\n env: Record<string, string>;\n}\n\nexport function environmentChain(definition: Definition, name: string): ResolvedEnvironment {\n const seen = new Set<string>();\n function resolve(envName: string): ResolvedEnvironment {\n if (seen.has(envName)) throw new Error(`environment \"${envName}\" has an extends cycle`);\n seen.add(envName);\n const env = definition.environments[envName];\n if (!env) throw new Error(`unknown environment \"${envName}\"`);\n const parent = env.extends ? resolve(env.extends) : { seeds: [], env: {} };\n return { seeds: [...parent.seeds, ...env.seeds], env: { ...parent.env, ...env.env } };\n }\n return resolve(name);\n}\n\n/**\n * Resolve one seed step's input against earlier steps' outputs: a value that\n * is exactly `${id.key}` becomes the raw output value, an embedded reference\n * is string-interpolated, and everything else passes through unchanged.\n */\nexport function resolveSeedInput(\n input: Record<string, string | number | boolean>,\n outputs: Record<string, Record<string, unknown>>\n): Record<string, unknown> {\n const resolveRef = (stepId: string, key: string): string | number | boolean => {\n const stepOutputs = outputs[stepId];\n if (!stepOutputs || !(key in stepOutputs)) throw new Error(`no output \"${key}\" from step \"${stepId}\"`);\n const value = stepOutputs[key];\n if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {\n throw new Error(`output \"${key}\" of step \"${stepId}\" is not a string/number/boolean and cannot be referenced`);\n }\n return value;\n };\n const resolved: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(input)) {\n if (typeof value !== 'string') {\n resolved[key] = value;\n continue;\n }\n const exact = EXACT_STEP_REF.exec(value);\n if (exact) {\n resolved[key] = resolveRef(exact[1]!, exact[2]!);\n continue;\n }\n resolved[key] = value.replace(STEP_REF, (_match, stepId: string, refKey: string) => String(resolveRef(stepId, refKey)));\n }\n return resolved;\n}\n\n/**\n * Apply an action's declared defaults/required/type/enum to raw input; report\n * every issue found. `personas` is the definition's declared persona names,\n * the only valid values for a `persona`-typed input.\n */\nexport function validateActionInput(\n action: ActionDef,\n input: Record<string, unknown>,\n personas: string[]\n): { values: Record<string, string | number | boolean> } | { issues: string[] } {\n const issues: string[] = [];\n for (const key of Object.keys(input)) {\n if (!(key in action.input)) issues.push(`unknown input \"${key}\"`);\n }\n const personaSet = new Set(personas);\n const values: Record<string, string | number | boolean> = {};\n for (const [name, prop] of Object.entries(action.input)) {\n const given = input[name];\n const value = given === undefined ? prop.default : given;\n if (value === undefined) {\n if (prop.required) issues.push(`missing required input \"${name}\"`);\n continue;\n }\n if (!typeMatches(prop.type, value)) {\n issues.push(`input \"${name}\" must be ${prop.type}`);\n continue;\n }\n if (prop.type === 'persona' && !personaSet.has(value as string)) {\n issues.push(`input \"${name}\" must be a declared persona (one of ${personas.join(', ') || '(none declared)'})`);\n continue;\n }\n if (prop.enum && !prop.enum.includes(value as string | number)) {\n issues.push(`input \"${name}\" must be one of ${prop.enum.join(', ')}`);\n continue;\n }\n values[name] = value as string | number | boolean;\n }\n return issues.length > 0 ? { issues } : { values };\n}\n\n/** v1's action-result contract: the last stdout line that starts with `{`, JSON-parsed as an object. */\nexport function parseActionResult(stdout: string): Record<string, unknown> | undefined {\n const lines = stdout.split('\\n');\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i]!.trim();\n if (!line.startsWith('{')) continue;\n try {\n const parsed: unknown = JSON.parse(line);\n return isRecord(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n }\n return undefined;\n}\n\n/**\n * What `cofactor-sandbox action` prints on stdout once the script's result has\n * validated: the result under `output`, plus the script's run time. The one\n * format both the helper (writer) and the API (reader) use.\n */\nexport interface ActionEnvelope {\n output: Record<string, unknown>;\n ms: number;\n}\n\nexport function formatActionEnvelope(envelope: ActionEnvelope): string {\n return JSON.stringify(envelope);\n}\n\nexport function parseActionEnvelope(stdout: string): ActionEnvelope | undefined {\n const line = parseActionResult(stdout);\n if (!line || !isRecord(line.output) || typeof line.ms !== 'number') return undefined;\n return { output: line.output, ms: line.ms };\n}\n\n/**\n * What `cofactor-sandbox build` prints on stdout (one JSON line): the commit it\n * built, each `lifecycle.build` step (skipped steps have `ran: false`), and the\n * checkout, reload and service-restart times. Written by the helper, read by\n * the API, which keeps it as a ready snapshot's build timings.\n */\nexport const buildReportSchema = z.object({\n sha: z.string(),\n steps: z.array(z.object({ name: z.string(), ran: z.boolean(), ms: z.number() })),\n checkoutMs: z.number(),\n reloadMs: z.number(),\n restartMs: z.number(),\n totalMs: z.number(),\n});\n\nexport type BuildReport = z.infer<typeof buildReportSchema>;\n\n/** The build report on `build`'s stdout; throws when there is none (the helper always prints one). */\nexport function parseBuildReport(stdout: string): BuildReport {\n const parsed = buildReportSchema.safeParse(parseActionResult(stdout));\n if (!parsed.success) throw new Error(`build printed no valid report: ${parsed.error.message}`);\n return parsed.data;\n}\n\n/** Every declared output key must be present with the right primitive type; extra keys are allowed. */\nexport function validateActionOutput(action: ActionDef, output: Record<string, unknown>): string[] {\n const issues: string[] = [];\n for (const [name, prop] of Object.entries(action.output)) {\n if (!(name in output)) {\n issues.push(`missing output \"${name}\"`);\n continue;\n }\n if (!typeMatches(prop.type, output[name])) issues.push(`output \"${name}\" must be ${prop.type}`);\n }\n return issues;\n}\n\n/** What a sandbox receives at `prepare`: declared params, its environment, persona passwords and repository secrets. */\nexport const prepareInputSchema = z.strictObject({\n /**\n * `false` for a sandbox's first prepare (a repo base moved to a commit, or\n * the builder a ready snapshot is captured from): `params` is every\n * declared param. `true` for a copy restored from an already-prepared\n * ready snapshot: `params` sets every `port` param (its own URLs — every\n * one of them differs per copy, `live: true` or not; see `portParamNames`)\n * but no `secret` param — those keep the value the snapshot itself was\n * prepared with (`resolveCopyParams`), which is what lets `restartRequired`\n * skip restarting `reload_on_prepare` services when the only params that\n * changed are `live` ones. A copy prepare requires a previous prepare to\n * copy from; there is no default because a caller that gets this wrong\n * needs a loud, specific error, not a silently-full or silently-partial\n * prepare.\n */\n copy: z.boolean(),\n params: z.record(z.string().regex(PARAM), z.string()),\n /** One password per declared persona, generated by Cofactor for this sandbox. */\n personas: z.record(z.string().regex(NAME), z.strictObject({ password: z.string().min(12) })),\n /**\n * Repository secrets (stored in Cofactor, never in the repo): runtime env for\n * `reload_on_prepare` services and prepare commands, overriding the\n * definition's env on a name clash. A value only a prepare command needs\n * belongs in a param instead.\n */\n secrets: z.record(z.string().regex(ENV_NAME), z.string()),\n /** The chosen flavor; its resolved chain's seeds ran at claim time. */\n environment: z.string().regex(NAME),\n /**\n * Present iff the definition declares `emulation`. `config` is the\n * `EmulationInstanceConfig` JSON Cofactor built for this sandbox; `env` is\n * the plugins' resolved env-contract values, mapped to this app's env vars.\n */\n emulation: z\n .strictObject({\n config: z.record(z.string(), z.unknown()),\n env: z.record(z.string().regex(ENV_NAME), z.string()),\n })\n .optional(),\n});\nexport type PrepareInput = z.infer<typeof prepareInputSchema>;\n\n/*\n * Blackcat template names. A template's name is its identity: the jobs and\n * the operator driver find a layer by name, so these are the one definition.\n * A GitHub-delivered application's toolchain and repo base are named by its\n * repository and definition directory, which both fold into their slug.\n */\n\n/** The platform layer for a machine shape; one per fleet, published by an operator. */\nexport function platformTemplateName(definition: Definition): string {\n return `sbv2-platform-v${PLATFORM_VERSION}-${definition.resources.vcpus}c-${definition.resources.memory_mib}m`;\n}\n\n/**\n * The definition's file, relative to the repo root: `cofactor.sandbox.yaml`\n * at the root, or `<dir>/cofactor.sandbox.yaml` for a monorepo definition.\n * `dir` must be `.` or a relative POSIX path with no leading/trailing slash\n * and no `..` segment.\n */\nexport function definitionFile(dir: string): string {\n if (dir === '.') return DEFINITION_FILE;\n if (dir === '' || dir.startsWith('/') || dir.endsWith('/') || dir.split('/').includes('..')) {\n throw new Error(`invalid definition directory \"${dir}\"`);\n }\n return `${dir}/${DEFINITION_FILE}`;\n}\n\n/** For '.' hashes the repo alone (existing root template names are unchanged); otherwise repo + ':' + dir. */\nfunction repoSlug(repoFullName: string, definitionDir: string): string {\n const key = definitionDir === '.' ? repoFullName : `${repoFullName}:${definitionDir}`;\n return createHash('sha256').update(key).digest('hex').slice(0, 8);\n}\n\nexport function toolchainTemplateName(repoFullName: string, definitionDir: string, definition: GithubDefinition): string {\n return `sbv2-${repoSlug(repoFullName, definitionDir)}-toolchain-${toolchainKey(definition).slice(0, 12)}`;\n}\n\n/**\n * A repo base at `sha`, built on `definition`'s toolchain. The toolchain is in\n * the name so a base built on an older toolchain (a platform bump, a change to\n * setup or resources) never shares a name with its replacement: the rebuild\n * can't adopt the old template, and deleting the old one can't remove the new.\n */\nexport function baseTemplateName(repoFullName: string, definitionDir: string, definition: GithubDefinition, sha: string): string {\n return `sbv2-${repoSlug(repoFullName, definitionDir)}-base-${toolchainKey(definition).slice(0, 12)}-${sha.slice(0, 12)}`;\n}\n\n/**\n * A ready snapshot: a version of an application (a commit, for a GitHub-\n * delivered one) built, prepared and seeded for one environment, captured\n * running. Its key on the template row is `readySnapshotKey`. Named by the\n * application, which is one org's: a ready snapshot holds an org's seeded\n * data, so no two orgs ever adopt or delete each other's. `nonce` is fixed\n * when the snapshot's row is first created: a rerun of the same build adopts\n * the template it promoted, but a row created after retention deleted the\n * last one never adopts a template left behind.\n */\nexport function readyTemplateName(applicationId: string, version: string, environment: string, nonce: string): string {\n const slug = createHash('sha256').update(applicationId).digest('hex').slice(0, 8);\n const versionSlug = createHash('sha256').update(version).digest('hex').slice(0, 12);\n return `sbv2-${slug}-ready-${versionSlug}-${environment}-${nonce}`;\n}\n\nexport function readySnapshotKey(version: string, environment: string): string {\n return `${version}/${environment}`;\n}\n\n/** The version a `readySnapshotKey` names. */\nexport function readySnapshotVersion(key: string): string {\n const slash = key.lastIndexOf('/');\n if (slash < 0) throw new Error(`\"${key}\" is not a ready snapshot key`);\n return key.slice(0, slash);\n}\n\n/** A pushed image of an upload-delivered application, imported into Blackcat. */\nexport function uploadTemplateName(applicationId: string, uploadId: string): string {\n const slug = createHash('sha256').update(applicationId).digest('hex').slice(0, 8);\n return `sbv2-${slug}-upload-${uploadId}`;\n}\n\n/**\n * What a pushed image's ready snapshots are compatible with: the helper the\n * CLI layered into it. An upload-delivered application's counterpart of\n * `toolchainKey`; a platform bump makes every older push unusable until it\n * is pushed again.\n */\nexport function uploadCompatibilityKey(platformVersion: number): string {\n return `upload-platform-v${platformVersion}`;\n}\n","/**\n * Built-in object storage (`object_storage` in the definition): one emulated\n * S3 or GCS server per sandbox, run by the supervisor as implicit services so\n * its data is in every snapshot. Pure values, shared by the schema\n * (manifest.ts), the env (runtime.ts) and the guest side\n * (object-storage-server.ts); see specs/pending/sandboxes-v2-object-storage.md.\n */\n\nexport const OBJECT_STORAGE_PROVIDERS = ['s3', 'gcs'] as const;\nexport type ObjectStorageProvider = (typeof OBJECT_STORAGE_PROVIDERS)[number];\n\nexport interface ObjectStorage {\n provider: ObjectStorageProvider;\n buckets: string[];\n}\n\n/** The implicit public port; reserved whether or not storage is declared. */\nexport const OBJECT_STORAGE_PORT_NAME = 'object-storage';\n/** The implicit services' names; reserved whether or not storage is declared. */\nconst OBJECT_STORAGE_SERVICE = 'object-storage';\nconst OBJECT_STORAGE_FRONT_SERVICE = 'object-storage-front';\nexport const OBJECT_STORAGE_SERVICE_NAMES = [OBJECT_STORAGE_SERVICE, OBJECT_STORAGE_FRONT_SERVICE];\n\n/** Where the server keeps buckets: under the helper's state dir, outside any WORKDIR or checkout, owned by the sandbox account. */\nexport const OBJECT_STORAGE_DIR = '/var/lib/cofactor/object-storage';\n\n/** What clients (and the public port) reach: versitygw itself, or the GCS front. */\nexport const OBJECT_STORAGE_PORT: Record<ObjectStorageProvider, number> = { s3: 9000, gcs: 4443 };\n/** fake-gcs-server's own, loopback-only port behind the front. */\nexport const FAKE_GCS_PORT = 4444;\n/** Ports a user port may not take while that provider is declared. */\nexport const RESERVED_STORAGE_PORTS: Record<ObjectStorageProvider, number[]> = {\n s3: [OBJECT_STORAGE_PORT.s3],\n gcs: [OBJECT_STORAGE_PORT.gcs, FAKE_GCS_PORT],\n};\n\n/** Each provider's server, from its per-provider layer (`objectStorageLayerKey`, manifest.ts). */\nexport const OBJECT_STORAGE_BINARY: Record<ObjectStorageProvider, string> = {\n s3: '/usr/local/bin/versitygw',\n gcs: '/usr/local/bin/fake-gcs-server',\n};\n\n/**\n * The S3 root credentials, the same in every sandbox: the server holds only\n * the sandbox's own emulated data, and apps get them through the env.\n */\nexport const S3_ACCESS_KEY = 'cofactor-sandbox';\nexport const S3_SECRET_KEY = 'cofactor-sandbox-secret';\nconst S3_REGION = 'us-east-1';\n/** versitygw's health route. No bucket can be named `_health` (bucket names have no `_`). */\nconst S3_HEALTH_PATH = '/_health';\n\n/**\n * The fixed host fake-gcs-server is told is its public one (`-public-host`):\n * it serves XML-style `/<bucket>/<object>` routes only for that Host, so the\n * front rewrites every request's Host to it, whichever URL the browser used.\n */\nexport const GCS_PUBLIC_HOST = 'storage.cofactor.internal';\nexport const GCS_PROJECT = 'sandbox';\n\nconst SERVICE_DEFAULTS = { after_build: false, reload_on_prepare: false } as const;\n\n/** The implicit services that run the declared provider, started like any service that needs neither a build nor a prepare. */\nexport function objectStorageServices(provider: ObjectStorageProvider) {\n if (provider === 's3') {\n const env = [\n `ROOT_ACCESS_KEY_ID=${S3_ACCESS_KEY}`,\n `ROOT_SECRET_ACCESS_KEY=${S3_SECRET_KEY}`,\n `VGW_REGION=${S3_REGION}`,\n // Browsers upload through presigned URLs on the public port, another\n // origin; without this the preflight 403s (spike, 2026-10-02).\n `VGW_CORS_ALLOW_ORIGIN='*'`,\n `VGW_HEALTH=${S3_HEALTH_PATH}`,\n ].join(' ');\n return {\n [OBJECT_STORAGE_SERVICE]: {\n run: `${env} exec ${OBJECT_STORAGE_BINARY.s3} --port :${OBJECT_STORAGE_PORT.s3} posix ${OBJECT_STORAGE_DIR}`,\n health: { http: OBJECT_STORAGE_PORT.s3, path: S3_HEALTH_PATH },\n ...SERVICE_DEFAULTS,\n },\n };\n }\n return {\n [OBJECT_STORAGE_SERVICE]: {\n run:\n `exec ${OBJECT_STORAGE_BINARY.gcs} -scheme http -host 127.0.0.1 -port ${FAKE_GCS_PORT}` +\n ` -backend filesystem -filesystem-root ${OBJECT_STORAGE_DIR} -public-host ${GCS_PUBLIC_HOST}` +\n // Upload Location headers point at the front (the port clients use), not fake-gcs's own.\n ` -external-url http://127.0.0.1:${OBJECT_STORAGE_PORT.gcs}`,\n health: { http: FAKE_GCS_PORT, path: '/storage/v1/b' },\n ...SERVICE_DEFAULTS,\n },\n [OBJECT_STORAGE_FRONT_SERVICE]: {\n run: 'exec /usr/local/bin/cofactor-sandbox object-storage-front',\n health: { http: OBJECT_STORAGE_PORT.gcs, path: '/storage/v1/b' },\n ...SERVICE_DEFAULTS,\n },\n };\n}\n\n/** The standard client env for the declared provider; a definition's own env overrides it (runtime.ts `childEnv`). */\nexport function objectStorageEnv(storage: ObjectStorage | undefined): Record<string, string> {\n if (!storage) return {};\n if (storage.provider === 's3') {\n return {\n AWS_ENDPOINT_URL_S3: `http://127.0.0.1:${OBJECT_STORAGE_PORT.s3}`,\n AWS_ACCESS_KEY_ID: S3_ACCESS_KEY,\n AWS_SECRET_ACCESS_KEY: S3_SECRET_KEY,\n AWS_REGION: S3_REGION,\n };\n }\n return { STORAGE_EMULATOR_HOST: `http://127.0.0.1:${OBJECT_STORAGE_PORT.gcs}`, GOOGLE_CLOUD_PROJECT: GCS_PROJECT };\n}\n","/**\n * `cofactor sandbox push`: deliver an application's sandboxes as a built\n * image, with no repository access. The team's image (any glibc\n * linux/amd64 image with a WORKDIR) gets Cofactor's layers on top — its\n * sandbox helper as the entrypoint, the declared object storage server —\n * named by digest and never uploaded, and one small layer of its own: the\n * application's `cofactor.sandbox.yaml` and the image's ENV and WORKDIR at\n * /cofactor. The image goes to Cofactor as content-addressed blobs read\n * straight out of `docker save`, and only the ones Cofactor doesn't hold yet\n * are uploaded, so a re-push sends the layers that changed. Cofactor imports\n * it into the sandbox fleet, where it boots until its services are healthy;\n * the definition's environments then sync onto the application.\n *\n * @module\n * @see specs/pending/sandboxes-v2-incremental-push.md\n */\n\nimport process from 'node:process';\nimport { spawnSync } from 'node:child_process';\nimport { createHash } from 'node:crypto';\nimport { createReadStream, mkdtempSync, openAsBlob, type ReadStream, readFileSync, rmSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest, getArgValues } from './cli-helpers.js';\nimport { writeRawOutput } from './brand.js';\nimport { resolveApplicationId } from './environment.js';\nimport type { PushEntry } from './sandbox-v2.js';\nimport {\n type DefinitionIssue,\n IMAGE_CONFIG_FILE,\n IMAGE_DEFINITION_FILE,\n type ImageConfig,\n imageConfigSchema,\n type ObjectStorageProvider,\n validateDefinition,\n} from '@chatsdet/sandbox-helper/manifest';\nimport { type ImageBlob, MAX_BLOB_BYTES, tarEntries, tarOf } from '@chatsdet/sandbox-helper/image-archive';\n\nconst USAGE =\n 'Usage: cofactor sandbox push --application <name|id> <image> [--definition <file>] [--tag <tag>]... [--wait] [--json]';\n\n/** Blobs uploaded at once. */\nconst UPLOAD_CONCURRENCY = 4;\nconst UPLOAD_ATTEMPTS = 3;\nconst HASH_CHUNK_BYTES = 8 * 1024 * 1024;\n\n/** How long `--wait` follows an import (it boots the image until healthy; a big image takes a while). */\nconst IMPORT_TIMEOUT_MS = 50 * 60_000;\nconst IMPORT_POLL_MS = 5_000;\n\ntype ApiResult<T> = { ok: boolean; error?: string; code?: string; issues?: DefinitionIssue[]; data: T };\n\n/** Why a push stopped; printed once, after the temporary files and image are cleaned up. */\nclass PushError extends Error {\n constructor(\n message: string,\n readonly issues: DefinitionIssue[] = []\n ) {\n super(message);\n }\n}\n\nfunction fail(message: string, issues?: DefinitionIssue[]): never {\n throw new PushError(message, issues);\n}\n\n/** Run docker, failing with its stderr. `quiet` keeps its progress off the terminal (for --json). */\nfunction docker(args: string[], options: { quiet: boolean }): string {\n const result = spawnSync('docker', args, {\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', options.quiet ? 'pipe' : 'inherit'],\n maxBuffer: 64 * 1024 * 1024,\n });\n if (result.error) fail(`could not run docker (${result.error.message}); is Docker installed and running?`);\n if (result.status !== 0) fail(`docker ${args.slice(0, 2).join(' ')} failed:\\n${result.stderr ?? ''}`.trimEnd());\n return result.stdout;\n}\n\ntype InspectedImage = {\n Architecture: string;\n Os: string;\n Config: { WorkingDir?: string; Env?: string[] | null };\n};\n\n/**\n * What the image's own config would have applied when run: its WORKDIR and\n * ENV, which an import into the fleet otherwise drops. Refuses an image the\n * fleet cannot run.\n */\nfunction imageConfigOf(image: string): ImageConfig {\n const result = spawnSync('docker', ['image', 'inspect', image], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });\n if (result.error) fail(`could not run docker (${result.error.message}); is Docker installed and running?`);\n const [inspected] = result.status === 0 ? (JSON.parse(result.stdout) as InspectedImage[]) : [];\n if (!inspected) fail(`Docker has no local image ${image}: build or pull it first`);\n if (inspected.Os !== 'linux' || inspected.Architecture !== 'amd64') {\n fail(\n `${image} is ${inspected.Os}/${inspected.Architecture}; sandboxes run linux/amd64. Rebuild it with\\n` +\n ` docker buildx build --platform linux/amd64 -t ${image} --load <context>`\n );\n }\n const env = Object.fromEntries(\n (inspected.Config.Env ?? []).map((entry) => {\n const separator = entry.indexOf('=');\n return [entry.slice(0, separator), entry.slice(separator + 1)];\n })\n );\n const parsed = imageConfigSchema.safeParse({ workdir: inspected.Config.WorkingDir ?? '', env });\n if (!parsed.success) {\n const workdir = inspected.Config.WorkingDir || '(none)';\n fail(\n `${image}'s WORKDIR ${workdir} can't be used: ${parsed.error.issues[0]?.message ?? 'invalid'}. ` +\n 'Set one for your app in its Dockerfile (e.g. `WORKDIR /app`): services, prepare commands and actions run from it, ' +\n 'and the sandbox makes it writable by the app.'\n );\n }\n return parsed.data;\n}\n\n/** A value for a shell command line, quoted only when it needs to be. */\nfunction shellWord(value: string): string {\n return /^[\\w@%+=:,./-]+$/.test(value) ? value : `'${value.replaceAll(\"'\", `'\\\\''`)}'`;\n}\n\nasync function apiOrFail<T>(result: Promise<ApiResult<T>>): Promise<T> {\n const res = await result;\n if (!res.ok) fail(res.error ?? 'unknown error', res.issues);\n return res.data;\n}\n\nconst mib = (bytes: number) => `${(bytes / 1024 / 1024).toFixed(bytes < 10 * 1024 * 1024 ? 1 : 0)} MB`;\n\n/**\n * `sha256:<hex>` of a slice of the saved archive, read a chunk at a time.\n * Not `bytes.stream()`: under Bun, streams of a file-backed blob never\n * settle when several run at once, and the process exits 0 mid-push (seen\n * 2026-10-04).\n */\nasync function digestOf(bytes: Blob): Promise<string> {\n const hash = createHash('sha256');\n for (let offset = 0; offset < bytes.size; offset += HASH_CHUNK_BYTES) {\n hash.update(new Uint8Array(await bytes.slice(offset, offset + HASH_CHUNK_BYTES).arrayBuffer()));\n }\n return `sha256:${hash.digest('hex')}`;\n}\n\n/**\n * A blob of the image as the API names it, and a fresh request body for\n * uploading it (a retry needs a new one).\n */\ntype Part = ImageBlob & { body: () => Blob | ReadStream };\n\n/** A blob held in memory: the config, the push's own layer. */\nasync function partOf(bytes: Blob): Promise<Part> {\n return { digest: await digestOf(bytes), size: bytes.size, body: () => bytes };\n}\n\n/**\n * PUT a blob to its presigned URL, again after a pause when the network or\n * storage fails. A refusal (4xx: a checksum mismatch, an expired URL) won't\n * change on a retry; a push run again uploads only what is still missing.\n */\nasync function putBlob(url: string, headers: Record<string, string>, part: Part): Promise<void> {\n const digest = part.digest;\n for (let attempt = 1; ; attempt++) {\n let response: Response;\n try {\n // A length, not chunked: S3 refuses a chunked presigned PUT.\n const init: RequestInit & { duplex: 'half' } = {\n method: 'PUT',\n headers: { ...headers, 'content-length': String(part.size) },\n body: part.body() as BodyInit,\n duplex: 'half',\n };\n response = await fetch(url, init);\n } catch (error) {\n if (attempt >= UPLOAD_ATTEMPTS) throw error;\n await new Promise((resolve) => setTimeout(resolve, 2_000 * attempt));\n continue;\n }\n if (response.ok) return;\n const detail = `${response.status}: ${(await response.text()).slice(0, 300)}`;\n if (response.status < 500 || attempt >= UPLOAD_ATTEMPTS) {\n fail(`uploading ${digest} was refused (HTTP ${detail}); run the push again to upload what is still missing`);\n }\n await new Promise((resolve) => setTimeout(resolve, 2_000 * attempt));\n }\n}\n\n/**\n * The image's config and layers as `docker save` wrote them, from its\n * manifest.json (Docker writes one in its OCI layout too). Layer paths are\n * whatever the layout uses; each is named by the digest of its bytes.\n */\nasync function savedImage(archive: string) {\n // Node's, not Bun's: the published CLI runs under Node (`bin.js` is `#!/usr/bin/env node`).\n const file = await openAsBlob(archive);\n const entries = await tarEntries(async (offset, length) => new Uint8Array(await file.slice(offset, offset + length).arrayBuffer()));\n const byName = new Map(entries.map((entry) => [entry.name.replace(/^\\.\\//, ''), entry]));\n const entryOf = (name: string) => {\n const entry = byName.get(name);\n if (!entry) fail(`the saved image has no ${name}; is this a \\`docker save\\` archive?`);\n return entry;\n };\n const slice = (name: string) => {\n const entry = entryOf(name);\n return file.slice(entry.offset, entry.offset + entry.size);\n };\n // A layer uploads straight from its range of the archive. Not the blob slice:\n // under Bun, several uploads of file-backed slices at once hang (seen 2026-10-04).\n const layerOf = async (name: string): Promise<Part> => {\n const entry = entryOf(name);\n const range = { start: entry.offset, end: entry.offset + entry.size - 1 };\n return { digest: await digestOf(slice(name)), size: entry.size, body: () => createReadStream(archive, range) };\n };\n const [manifest] = JSON.parse(await slice('manifest.json').text()) as { Config: string; Layers: string[] }[];\n if (!manifest) fail('the saved image lists no image');\n // One at a time: see `digestOf`.\n const layers: Part[] = [];\n for (const name of manifest.Layers) layers.push(await layerOf(name));\n return {\n config: JSON.parse(await slice(manifest.Config).text()) as SavedConfig,\n layers,\n };\n}\n\ntype SavedConfig = {\n config?: Record<string, unknown>;\n rootfs: { type: string; diff_ids: string[] };\n history?: Record<string, unknown>[];\n};\n\n/**\n * The image a push sends, from the team's saved image: its layers, then\n * Cofactor's (`cofactorLayers`, by digest), then the push's own layer with\n * what the helper reads from /cofactor; and its config, rewritten to name\n * those layers and run the helper. `blobs` holds the bytes of every blob the\n * push may have to upload (all but Cofactor's).\n */\nexport async function pushedImage(\n archive: string,\n cofactorLayers: ImageBlob[],\n definitionYaml: string,\n config: ImageConfig\n): Promise<{ config: ImageBlob; layers: ImageBlob[]; blobs: Map<string, Part> }> {\n const saved = await savedImage(archive);\n const tooLarge = saved.layers.find((layer) => layer.size > MAX_BLOB_BYTES);\n if (tooLarge) fail(`a layer of the image is ${(tooLarge.size / 1024 ** 3).toFixed(1)} GB; a push takes at most 5 GB per layer. Slim the image.`);\n const own = await partOf(\n new Blob([\n tarOf([\n { name: IMAGE_DEFINITION_FILE.slice(1), data: new TextEncoder().encode(definitionYaml) },\n { name: IMAGE_CONFIG_FILE.slice(1), data: new TextEncoder().encode(JSON.stringify(config)) },\n ]),\n ])\n );\n const added = [...cofactorLayers, own];\n // Each added layer is an uncompressed tar, so its digest is also its diff id.\n saved.config.rootfs.diff_ids.push(...added.map((layer) => layer.digest));\n saved.config.history = [\n ...(saved.config.history ?? []),\n ...added.map(() => ({ created: new Date().toISOString(), created_by: 'cofactor sandbox push' })),\n ];\n saved.config.config = { ...saved.config.config, Entrypoint: ['/usr/local/bin/cofactor-sandbox', 'boot'], Cmd: [] };\n const imageConfig = await partOf(new Blob([JSON.stringify(saved.config)]));\n const blob = ({ digest, size }: ImageBlob) => ({ digest, size });\n return {\n config: blob(imageConfig),\n layers: [...saved.layers, ...added].map(blob),\n blobs: new Map([imageConfig, ...saved.layers, own].map((part) => [part.digest, part])),\n };\n}\n\n/** `cofactor sandbox push --application A <image> [--definition F] [--tag T]... [--wait] [--json]`. */\nexport async function handleSandboxPush(argv: string[], apiUrl: string, token: string): Promise<void> {\n try {\n await push(argv, apiUrl, token);\n } catch (error) {\n if (!(error instanceof PushError)) throw error;\n console.error(`Error: ${error.message}`);\n for (const issue of error.issues) console.error(` ${issue.path || '(root)'}: ${issue.message}`);\n process.exit(1);\n }\n}\n\nasync function push(argv: string[], apiUrl: string, token: string): Promise<void> {\n const valueFlags = new Set(['--application', '--property', '--definition', '--tag', '--env', '--org', '--token', '--api-url', '--app-url']);\n const positional = argv.filter((arg, i) => !arg.startsWith('-') && !valueFlags.has(argv[i - 1] ?? ''));\n const image = positional[0];\n if (!image) fail(`an image is required\\n${USAGE}`);\n const definitionFile = getArgValue(argv, '--definition') ?? './cofactor.sandbox.yaml';\n const tags = getArgValues(argv, '--tag');\n // The server's rule, checked before a multi-minute build.\n const badTag = tags.find((tag) => !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(tag));\n if (badTag !== undefined) fail(`--tag ${badTag}: use letters, digits, '.', '_' and '-' (at most 64, starting with a letter or digit)`);\n if (tags.length > 10) fail('a push takes at most 10 tags');\n const wait = hasFlag(argv, '--wait');\n const json = hasFlag(argv, '--json');\n const log = (line: string) => (json ? console.error(line) : console.log(line));\n\n let definitionYaml: string;\n try {\n definitionYaml = readFileSync(definitionFile, 'utf8');\n } catch (error) {\n fail(`reading ${definitionFile}: ${error instanceof Error ? error.message : String(error)}`);\n }\n const { definition, issues } = validateDefinition(definitionYaml, 'upload');\n if (!definition || issues.length > 0) fail(`${definitionFile} is not a valid definition for pushed images:`, issues);\n const storageProvider = definition.object_storage?.provider;\n const applicationId = await resolveApplicationId(argv, apiUrl, token);\n const config = imageConfigOf(image);\n\n const work = mkdtempSync(join(tmpdir(), 'cofactor-push-'));\n const cleanUp = () => rmSync(work, { recursive: true, force: true });\n // A push interrupted mid-save or mid-upload would otherwise leave a multi-GB archive behind.\n const interrupted = (signal: NodeJS.Signals) => {\n cleanUp();\n process.exit(signal === 'SIGINT' ? 130 : 143);\n };\n process.once('SIGINT', interrupted);\n process.once('SIGTERM', interrupted);\n // Red until the push is through: if the event loop ever drains mid-push again\n // (it did under Bun, 2026-10-04), the process exits 1, not a silent 0.\n process.exitCode = 1;\n try {\n // Cofactor's layers for this version, by digest: added to the image, never uploaded.\n const ours = await apiOrFail(\n apiRequest<ApiResult<{ platformVersion: number; helper?: ImageBlob; objectStorage: Record<ObjectStorageProvider, ImageBlob> }>>(\n apiUrl,\n token,\n 'GET',\n '/org/sandboxes-v2/helper-layer'\n )\n );\n // A Cofactor deployed before incremental pushes answers with download URLs instead: the CLI\n // ships to npm ahead of a Cofactor's deploy (prod ran hundreds of commits behind, 2026-10-02).\n if (!ours.helper) fail('this Cofactor takes pushes from an older cofactor CLI; use the version it ships with');\n const cofactorLayers = [ours.helper, ...(storageProvider ? [ours.objectStorage[storageProvider]] : [])];\n\n const archive = join(work, 'image.tar');\n log('Saving the image...');\n docker(['save', '--output', archive, image], { quiet: json });\n const pushing = await pushedImage(archive, cofactorLayers, definitionYaml, config);\n const { layers } = pushing;\n\n const created = await apiOrFail(\n apiRequest<ApiResult<{ id: string; uploads: { digest: string; url: string; headers: Record<string, string> }[] }>>(\n apiUrl,\n token,\n 'POST',\n `/org/applications/${applicationId}/sandbox/uploads`,\n { definitionYaml, platformVersion: ours.platformVersion, tags, image: { config: pushing.config, layers } }\n )\n );\n const partsByDigest = pushing.blobs;\n const uploadBytes = created.uploads.reduce((total, upload) => total + (partsByDigest.get(upload.digest)?.size ?? 0), 0);\n const imageBytes = layers.reduce((total, layer) => total + layer.size, 0);\n log(`Pushing ${created.id}: uploading ${created.uploads.length} of ${layers.length + 1} blobs (${mib(uploadBytes)}); ${mib(imageBytes - uploadBytes)} already in Cofactor`);\n const queue = [...created.uploads];\n let sent = 0;\n await Promise.all(\n Array.from({ length: UPLOAD_CONCURRENCY }, async () => {\n for (let next = queue.shift(); next; next = queue.shift()) {\n const part = partsByDigest.get(next.digest);\n if (!part) fail(`Cofactor asked for ${next.digest}, which this image doesn't have`);\n await putBlob(next.url, next.headers, part);\n sent += part.size;\n if (part.size > 10 * 1024 * 1024) log(` ${mib(sent)} of ${mib(uploadBytes)}`);\n }\n })\n );\n rmSync(archive);\n let pushed = await apiOrFail(\n apiRequest<ApiResult<PushEntry>>(\n apiUrl,\n token,\n 'POST',\n `/org/applications/${applicationId}/sandbox/uploads/${created.id}/complete`\n )\n );\n\n if (wait) {\n log('Importing (Cofactor boots the image until its services are healthy)...');\n const deadline = Date.now() + IMPORT_TIMEOUT_MS;\n const importStarted = Date.now();\n let reported = importStarted;\n while ((pushed.status === 'importing' || pushed.status === 'uploading') && Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, IMPORT_POLL_MS));\n // A large image imports for minutes: say it is still going.\n if (Date.now() - reported >= 60_000) {\n reported = Date.now();\n log(` still importing (${Math.round((reported - importStarted) / 60_000)} min)`);\n }\n pushed = await apiOrFail(\n apiRequest<ApiResult<PushEntry>>(apiUrl, token, 'GET', `/org/applications/${applicationId}/sandbox/uploads/${created.id}`)\n );\n }\n }\n const uploaded = { blobs: created.uploads.length, of: layers.length + 1, bytes: uploadBytes, reusedBytes: imageBytes - uploadBytes };\n\n const application = shellWord(getArgValue(argv, '--application') ?? getArgValue(argv, '--property') ?? applicationId);\n // The dispatcher strips the global options from `argv`; the hints must carry them.\n const target = ['--env', '--org']\n .map((flag) => {\n const value = getArgValue(process.argv, flag);\n return value ? ` ${flag} ${shellWord(value)}` : '';\n })\n .join('');\n if (json) {\n writeRawOutput(JSON.stringify({ ...pushed, uploaded }, null, 2));\n } else {\n console.log(`Push ${pushed.id}: ${pushed.status}${pushed.tags.length > 0 ? ` [${pushed.tags.join(', ')}]` : ''}`);\n if (pushed.status === 'ready') {\n console.log(` Claim it: cofactor sandbox claim --application ${application} --environment default --wait${target}`);\n } else if (pushed.status !== 'failed') {\n console.log(` Importing; follow it with: cofactor sandbox envs --application ${application}${target}`);\n }\n }\n if (pushed.status === 'failed') fail(`push ${pushed.id} failed:\\n${pushed.error ?? 'no reason given'}`);\n process.exitCode = 0;\n } finally {\n process.off('SIGINT', interrupted);\n process.off('SIGTERM', interrupted);\n cleanUp();\n }\n}\n","/**\n * A pushed image as content-addressed blobs, and the tar that carries it.\n *\n * `cofactor sandbox push` reads the layers out of a `docker save` archive,\n * uploads only the blobs Cofactor doesn't hold yet, and describes the image\n * as an {@link ImageBlobs}: its config and its layers in order. The API's\n * import rebuilds a legacy `docker save` archive from those blobs for\n * Blackcat, whose `docker load` reads it like any other.\n *\n * @module sandbox-helper/image-archive\n * @see specs/pending/sandboxes-v2-incremental-push.md\n */\nimport { z } from 'zod';\n\n/** One presigned PUT carries a blob, and S3 takes at most 5 GB in one. */\nexport const MAX_BLOB_BYTES = 5 * 1024 ** 3;\n\n/** A blob by content: `sha256:<hex>` of its bytes, and their length. */\nexport const ImageBlob = z.object({\n digest: z.string().regex(/^sha256:[0-9a-f]{64}$/),\n size: z.number().int().nonnegative(),\n});\nexport type ImageBlob = z.infer<typeof ImageBlob>;\n\n/** An image: its config blob, and its layer blobs bottom to top. */\nexport const ImageBlobs = z.object({\n config: ImageBlob,\n // Docker allows at most 127 layers; each costs a storage lookup per push.\n layers: z.array(ImageBlob).min(1).max(128),\n});\nexport type ImageBlobs = z.infer<typeof ImageBlobs>;\n\nconst BLOCK = 512;\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\nconst hex = (digest: string) => digest.slice('sha256:'.length);\n\n/** A ustar header for a regular file (names here are short: a digest and a suffix). */\nfunction tarHeader(name: string, size: number, mode = 0o644): Uint8Array {\n if (encoder.encode(name).length > 100) throw new Error(`tar entry name too long: ${name}`);\n const header = new Uint8Array(BLOCK);\n const put = (offset: number, length: number, value: string) => header.set(encoder.encode(value).slice(0, length), offset);\n const octal = (offset: number, length: number, value: number) =>\n put(offset, length, `${value.toString(8).padStart(length - 1, '0')}\\0`);\n put(0, 100, name);\n octal(100, 8, mode);\n octal(108, 8, 0);\n octal(116, 8, 0);\n // Sizes past 8 GiB need the base-256 form; a blob is capped at 5 GB.\n octal(124, 12, size);\n octal(136, 12, 0);\n put(148, 8, ' ');\n put(156, 1, '0');\n put(257, 6, 'ustar\\0');\n put(263, 2, '00');\n const checksum = header.reduce((sum, byte) => sum + byte, 0);\n put(148, 8, `${checksum.toString(8).padStart(6, '0')}\\0 `);\n return header;\n}\n\n/** The zero bytes that round an entry of `size` up to a whole block. */\nfunction tarPadding(size: number): Uint8Array {\n return new Uint8Array((BLOCK - (size % BLOCK)) % BLOCK);\n}\n\n/** Two zero blocks: the end of an archive. */\nconst TAR_END = new Uint8Array(BLOCK * 2);\n\n/** A complete tar of small in-memory files, in order. */\nexport function tarOf(files: { name: string; data: Uint8Array; mode?: number }[]): Uint8Array<ArrayBuffer> {\n const parts = files.flatMap((file) => [tarHeader(file.name, file.data.length, file.mode), file.data, tarPadding(file.data.length)]);\n parts.push(TAR_END);\n const out = new Uint8Array(parts.reduce((total, part) => total + part.length, 0));\n let at = 0;\n for (const part of parts) {\n out.set(part, at);\n at += part.length;\n }\n return out;\n}\n\ntype TarEntry = { name: string; offset: number; size: number };\n\nconst field = (block: Uint8Array, offset: number, length: number) =>\n decoder.decode(block.subarray(offset, offset + length)).replace(/\\0.*$/s, '');\n\n/**\n * The regular files of a tar on disk, with where each one's bytes are, read\n * header by header so a multi-GB archive is never held in memory. Follows the\n * pax and GNU long-name records `docker save` may write.\n */\nexport async function tarEntries(read: (offset: number, length: number) => Promise<Uint8Array>): Promise<TarEntry[]> {\n const entries: TarEntry[] = [];\n let longName: string | null = null;\n for (let offset = 0; ; ) {\n const block = await read(offset, BLOCK);\n if (block.length < BLOCK || block.every((byte) => byte === 0)) return entries;\n const size = Number.parseInt(field(block, 124, 12).trim() || '0', 8);\n const type = field(block, 156, 1) || '0';\n const prefix = field(block, 345, 155);\n const name = longName ?? (prefix ? `${prefix}/${field(block, 0, 100)}` : field(block, 0, 100));\n const data = offset + BLOCK;\n longName = null;\n if (type === 'x' || type === 'L') {\n const text = decoder.decode(await read(data, size)).replace(/\\0+$/, '');\n longName = type === 'L' ? text : (/(?:^|\\n)\\d+ path=([^\\n]*)\\n/.exec(text)?.[1] ?? null);\n } else if (type === '0') {\n entries.push({ name, offset: data, size });\n }\n offset = data + size + tarPadding(size).length;\n }\n}\n\n/** One file of the archive {@link legacyArchive} lays out: either given bytes, or a blob to stream. */\ntype ArchivePart = { name: string; size: number } & ({ bytes: Uint8Array } | { blob: ImageBlob });\n\n/**\n * The files of a legacy `docker save` archive of `image`, in order: the\n * manifest, the config, then each distinct layer once. The image is untagged:\n * Blackcat refers to what it loads by the ID Docker reports.\n */\nfunction legacyArchive(image: ImageBlobs): ArchivePart[] {\n const configName = `${hex(image.config.digest)}.json`;\n const layerName = (blob: ImageBlob) => `${hex(blob.digest)}/layer.tar`;\n const manifest = encoder.encode(\n JSON.stringify([{ Config: configName, RepoTags: null, Layers: image.layers.map(layerName) }])\n );\n const layers = [...new Map(image.layers.map((blob) => [blob.digest, blob])).values()];\n return [\n { name: 'manifest.json', size: manifest.length, bytes: manifest },\n { name: configName, size: image.config.size, blob: image.config },\n ...layers.map((blob) => ({ name: layerName(blob), size: blob.size, blob })),\n ];\n}\n\n/**\n * `image` as one `docker save` archive ({@link legacyArchive}), each blob\n * read through `open` only as the reader asks for more, so the archive is\n * never held in memory. A blob shorter or longer than its recorded size fails\n * the stream: it would shift every entry after it.\n */\nexport function archiveStream(\n image: ImageBlobs,\n open: (blob: ImageBlob) => Promise<ReadableStream<Uint8Array>>\n): ReadableStream<Uint8Array> {\n async function* chunks(): AsyncGenerator<Uint8Array> {\n for (const part of legacyArchive(image)) {\n yield tarHeader(part.name, part.size);\n if ('bytes' in part) {\n yield part.bytes;\n } else {\n let written = 0;\n for await (const chunk of await open(part.blob)) {\n written += chunk.length;\n yield chunk;\n }\n if (written !== part.size) throw new Error(`blob ${part.blob.digest} is ${written} bytes, not ${part.size}`);\n }\n yield tarPadding(part.size);\n }\n yield TAR_END;\n }\n const source = chunks();\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n // Never an empty chunk: sent chunked, a zero-length chunk ends the request\n // body, and Blackcat stored a truncated archive (seen 2026-10-03: the\n // padding after a layer whose size is a multiple of 512 is empty).\n for (let next = await source.next(); ; next = await source.next()) {\n if (next.done) return controller.close();\n if (next.value.length > 0) return controller.enqueue(next.value);\n }\n },\n async cancel() {\n await source.return(undefined);\n },\n });\n}\n"],"mappings":"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AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,sDAAsD,0BAA0B,IAAI,CAAC,YAAY,QAAQ,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,MACzH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,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","names":["process","readFileSync","action","process","process","service","process","process","process","readFileSync","env","pad","push","readFileSync","process","environment","seconds","process","createHash","readFileSync","z","createHash","process","readFileSync","process","pad","readFileSync","service","a","action","section","json"]}
|