spb/autoscholar
Public
1import { z } from "zod";23const Env = z.object({4 NODE_ENV: z.enum(["development", "production", "test"]).default("development"),5 APP_BASE_URL: z.string().url().default("http://localhost:8470"),6 DATABASE_URL: z.string().min(1),7 ARTIFACT_ROOT: z.string().min(1).default("./data/artifacts"),8 SESSION_SECRET: z.string().min(32, "SESSION_SECRET must be at least 32 characters"),9 ADMIN_BOOTSTRAP_USERNAME: z.string().min(1).default("admin"),10 ADMIN_BOOTSTRAP_PASSWORD: z.string().optional(),11 WORKER_ENROLLMENT_SECRET: z.string().min(16).optional(),12 INTERNAL_API_ALLOWED_CIDRS: z.string().default("127.0.0.1/32,::1/128,192.168.2.0/24"),13 TRUST_PROXY: z.coerce.number().int().min(0).max(1).default(0),14 LOG_LEVEL: z.string().default("info"),15 SCHEDULER_TICK_MS: z.coerce.number().int().min(500).default(5000),16 LEASE_TTL_SECONDS: z.coerce.number().int().min(30).default(120),17 DEFAULT_MAX_BUDGET_USD: z.coerce.number().positive().default(25),18 DEFAULT_MAX_RUNTIME_MINUTES: z.coerce.number().int().positive().default(240),19 PUBLISHING_ENABLED: z.coerce.number().int().min(0).max(1).default(1),20 DISPATCH_ENABLED: z.coerce.number().int().min(0).max(1).default(1),21 STAGING_MODE: z.coerce.number().int().min(0).max(1).default(0),22 ANTHROPIC_MODEL: z.string().default("claude-opus-5-5"),23 ANTHROPIC_REVIEWER_MODEL: z.string().default("claude-opus-5-5"),24});25export type Env = z.infer<typeof Env>;2627let cached: Env | undefined;28export function getEnv(): Env {29 if (cached) return cached;30 const parsed = Env.safeParse(process.env);31 if (!parsed.success) {32 const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");33 throw new Error(`invalid environment: ${msg}`);34 }35 cached = parsed.data;36 return cached;37}38export function resetEnvCache(): void {39 cached = undefined;40}41