import { z } from "zod"; const optionalUrl = z.preprocess((value) => (value === "" ? undefined : value), z.string().url().optional()); const optionalSecret = z.preprocess((value) => (value === "" ? undefined : value), z.string().min(1).optional()); const npmPackageSpec = z.string().min(1).max(214).regex(/^\S+$/, "must not contain whitespace"); const configSchema = z.object({ NODE_ENV: z.enum(["development", "test", "production"]).default("development"), HOST: z.string().min(1).default("0.0.0.0"), PORT: z.coerce.number().int().min(1).max(65535).default(4100), LOG_LEVEL: z.enum(["fatal", "error", "warn", "info", "debug", "trace", "silent"]).default("info"), NODEDC_AGENT_GATEWAY_PUBLIC_URL: z.string().url().default("http://ops-agents.local.nodedc"), NODEDC_AGENT_GATEWAY_INTERNAL_TOKEN: z.string().min(1).optional(), NODEDC_OPS_CODEX_INSTALL_CHANNEL: z.enum(["tarball", "registry"]).default("tarball"), NODEDC_OPS_CODEX_NPM_SPEC: npmPackageSpec.default("@nodedc/ops-codex"), NODEDC_AI_WORKSPACE_RUN_TOKEN_TTL_SECONDS: z.coerce.number().int().min(300).max(86_400).default(43_200), NODEDC_LAUNCHER_INTERNAL_URL: z.string().url().default("http://launcher.local.nodedc"), NODEDC_TASKER_INTERNAL_URL: z.string().url().default("http://task.local.nodedc"), NODEDC_ENGINE_INTERNAL_URL: z.string().url().default("http://172.22.0.222:3001"), NODEDC_ONTOLOGY_CORE_URL: z.string().url().default("http://172.22.0.222:18104"), NODEDC_ONTOLOGY_CORE_ACCESS_TOKEN: optionalSecret, NODEDC_INTERNAL_ACCESS_TOKEN: z.string().min(1).optional(), DATABASE_URL: optionalUrl, }); export type AppConfig = z.infer; export function loadConfig(env: NodeJS.ProcessEnv = process.env): AppConfig { const parsed = configSchema.safeParse(env); if (!parsed.success) { const details = parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; "); throw new Error(`Invalid Agent Gateway configuration: ${details}`); } return parsed.data; }