import { z } from "zod"; import * as dotenv from "dotenv"; dotenv.config(); const envSchema = z.object({ // ─── LLM (All on NVIDIA NIM — FREE) ──────────────────────── NVIDIA_API_KEY: z.string().min(5), NVIDIA_NIM_BASE_URL: z.string().url().default("https://integrate.api.nvidia.com/v1"), // ─── Supabase ────────────────────────────────────────────── SUPABASE_URL: z.string().url(), SUPABASE_SERVICE_ROLE_KEY: z.string().min(10), // ─── Trigger.dev ─────────────────────────────────────────── TRIGGER_DEV_API_KEY: z.string().min(5), TRIGGER_DEV_PROJECT_ID: z.string().min(3), // ─── Web Research ────────────────────────────────────────── SERPER_API_KEY: z.string().min(5), // ─── Email Finding ───────────────────────────────────────── HUNTER_API_KEY: z.string().min(5), // ─── Email Verification ──────────────────────────────────── REOON_API_KEY: z.string().min(5), // ─── Slack ───────────────────────────────────────────────── SLACK_BOT_TOKEN: z.string().startsWith("xoxb-"), SLACK_SIGNING_SECRET: z.string().min(5), SLACK_ALERT_CHANNEL_ID: z.string(), SLACK_REVIEW_CHANNEL_ID: z.string(), // ─── Python AI Service ───────────────────────────────────── PYTHON_AI_SERVICE_URL: z.string().url().default("http://localhost:8000"), PYTHON_AI_SERVICE_SECRET: z.string().min(10), // ─── System Config ───────────────────────────────────────── NODE_ENV: z.enum(["development", "staging", "production"]).default("development"), LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"), DAILY_LEAD_QUOTA: z.coerce.number().default(10), QUALITY_SCORE_THRESHOLD: z.coerce.number().default(70), HUMAN_REVIEW_ENABLED: z.string().transform((v) => v === "true").default("true"), DAILY_EMAIL_LIMIT: z.coerce.number().default(50), DAILY_LINKEDIN_LIMIT: z.coerce.number().default(25), SCHEDULE_START_HOUR_UTC: z.coerce.number().default(4), }); type Env = z.infer; let _env: Env; export function getEnv(): Env { if (!_env) { const result = envSchema.safeParse(process.env); if (!result.success) { console.error("❌ Invalid environment configuration:"); result.error.errors.forEach((e) => { console.error(` ${e.path.join(".")}: ${e.message}`); }); // If we are in build/indexing phase (SUPABASE_URL is missing during CLI build), bypass crash to allow deploy to succeed if (!process.env.SUPABASE_URL || process.env.TRIGGER_BUILD === "true") { console.warn("⚠️ Warning: Missing environment variables during build/indexing phase. Bypassing crash with mock defaults to allow deployment."); // We parse mock values with the schema to get all the default values! const parsedDefaults = envSchema.parse({ NODE_ENV: "production", // Set to production to bypass pino-pretty build crash NVIDIA_API_KEY: "mock_nvidia_key", SUPABASE_URL: "https://mock.supabase.co", SUPABASE_SERVICE_ROLE_KEY: "mock_service_role_key_long_enough", TRIGGER_DEV_API_KEY: "mock_trigger_dev_api_key", TRIGGER_DEV_PROJECT_ID: "mock_proj_id", SERPER_API_KEY: "mock_serper_key", HUNTER_API_KEY: "mock_hunter_key", REOON_API_KEY: "mock_reoon_key", SLACK_BOT_TOKEN: "xoxb-mock-bot-token", SLACK_SIGNING_SECRET: "mock_signing_secret", SLACK_ALERT_CHANNEL_ID: "mock_channel", SLACK_REVIEW_CHANNEL_ID: "mock_channel", PYTHON_AI_SERVICE_SECRET: "mock_python_secret_key_long_enough", }); _env = { ...parsedDefaults, ...process.env } as any; return _env; } process.exit(1); } _env = result.data; } return _env; }