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 = { 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', jobImage: process.env.SPOON_AGENT_JOB_IMAGE?.trim() ?? 'spoon-agent-job:latest', workdir: process.env.SPOON_AGENT_WORKDIR?.trim() ?? '.local/agent-work', network: process.env.SPOON_AGENT_NETWORK?.trim(), pollMs: intEnv('SPOON_AGENT_POLL_MS', 5_000), maxConcurrentJobs: intEnv('SPOON_AGENT_MAX_CONCURRENT_JOBS', 1), jobTimeoutMs: intEnv('SPOON_AGENT_JOB_TIMEOUT_MS', 1_800_000), githubAppId: requiredEnv('GITHUB_APP_ID'), githubPrivateKey: requiredEnv('GITHUB_APP_PRIVATE_KEY').replaceAll( '\\n', '\n', ), };