const intEnv = (name: string, fallback: number) => { const value = process.env[name]; if (!value) return fallback; const parsed = Number.parseInt(value, 10); return Number.isFinite(parsed) ? parsed : fallback; }; const requiredEnv = (name: string) => { const value = process.env[name]?.trim(); if (!value) throw new Error(`${name} is required.`); return value; }; export const env = { buildSha: process.env.SPOON_BUILD_SHA?.trim() ?? 'development', buildCreatedAt: process.env.SPOON_BUILD_CREATED_AT?.trim() ?? 'unknown', convexUrl: process.env.NEXT_PUBLIC_CONVEX_URL?.trim() ?? process.env.CONVEX_SELF_HOSTED_URL?.trim() ?? 'http://localhost:3210', workerToken: requiredEnv('SPOON_WORKER_TOKEN'), workerId: process.env.SPOON_AGENT_WORKER_ID?.trim() ?? 'local-worker', runtime: process.env.SPOON_AGENT_RUNTIME?.trim() ?? 'docker', containerRuntime: process.env.SPOON_AGENT_CONTAINER_RUNTIME?.trim() ?? process.env.SPOON_CONTAINER_RUNTIME?.trim() ?? 'docker', containerVolumeOptions: process.env.SPOON_AGENT_CONTAINER_VOLUME_OPTIONS?.trim(), jobImage: process.env.SPOON_AGENT_JOB_IMAGE?.trim() ?? 'spoon-agent-job:latest', // Secret shared with the Next app for verifying interactive terminal tokens. terminalSecret: process.env.SPOON_AGENT_TERMINAL_SECRET?.trim() ?? process.env.SPOON_AGENT_WORKER_INTERNAL_TOKEN?.trim() ?? process.env.SPOON_WORKER_TOKEN?.trim() ?? '', // How long a per-user box container survives with no active jobs/terminals. boxIdleMs: intEnv('SPOON_AGENT_BOX_IDLE_MS', 1_800_000), // Dev-only: exit if the parent dev runner dies, so the worker never orphans // and holds port 3921 across restarts. Set by scripts/dev-agent-worker. devWatchdog: process.env.SPOON_AGENT_DEV_WATCHDOG === '1', workdir: process.env.SPOON_AGENT_WORKDIR?.trim() ?? '.local/agent-work', hostWorkdir: process.env.SPOON_AGENT_HOST_WORKDIR?.trim(), network: process.env.SPOON_AGENT_NETWORK?.trim(), pollMs: intEnv('SPOON_AGENT_POLL_MS', 5_000), httpPort: intEnv('SPOON_AGENT_WORKER_HTTP_PORT', 3921), internalToken: process.env.SPOON_AGENT_WORKER_INTERNAL_TOKEN?.trim() ?? process.env.SPOON_WORKER_TOKEN?.trim() ?? '', jobTimeoutMs: intEnv('SPOON_AGENT_JOB_TIMEOUT_MS', 1_800_000), githubAppId: requiredEnv('GITHUB_APP_ID'), githubPrivateKey: requiredEnv('GITHUB_APP_PRIVATE_KEY').replaceAll( '\\n', '\n', ), };