import { z } from "zod"; import { danger } from "./globals.js"; import { defaultRuntime, type RuntimeEnv } from "./runtime.js"; export type AuthMode = | { accountSid: string; authToken: string } | { accountSid: string; apiKey: string; apiSecret: string }; export type EnvConfig = { accountSid: string; whatsappFrom: string; whatsappSenderSid?: string; auth: AuthMode; }; const EnvSchema = z .object({ TWILIO_ACCOUNT_SID: z.string().min(1, "TWILIO_ACCOUNT_SID required"), TWILIO_WHATSAPP_FROM: z.string().min(1, "TWILIO_WHATSAPP_FROM required"), TWILIO_SENDER_SID: z.string().optional(), TWILIO_AUTH_TOKEN: z.string().optional(), TWILIO_API_KEY: z.string().optional(), TWILIO_API_SECRET: z.string().optional(), }) .superRefine((val, ctx) => { if (val.TWILIO_API_KEY && !val.TWILIO_API_SECRET) { ctx.addIssue({ code: "custom", message: "TWILIO_API_SECRET required when TWILIO_API_KEY is set", }); } if (val.TWILIO_API_SECRET && !val.TWILIO_API_KEY) { ctx.addIssue({ code: "custom", message: "TWILIO_API_KEY required when TWILIO_API_SECRET is set", }); } if ( !val.TWILIO_AUTH_TOKEN && !(val.TWILIO_API_KEY && val.TWILIO_API_SECRET) ) { ctx.addIssue({ code: "custom", message: "Provide TWILIO_AUTH_TOKEN or both TWILIO_API_KEY and TWILIO_API_SECRET", }); } }); export function readEnv(runtime: RuntimeEnv = defaultRuntime): EnvConfig { // Load and validate Twilio auth + sender configuration from env. const parsed = EnvSchema.safeParse(process.env); if (!parsed.success) { runtime.error("Invalid environment configuration:"); parsed.error.issues.forEach((iss) => { runtime.error(`- ${iss.message}`); }); runtime.exit(1); } const { TWILIO_ACCOUNT_SID: accountSid, TWILIO_WHATSAPP_FROM: whatsappFrom, TWILIO_SENDER_SID: whatsappSenderSid, TWILIO_AUTH_TOKEN: authToken, TWILIO_API_KEY: apiKey, TWILIO_API_SECRET: apiSecret, } = parsed.data; let auth: AuthMode; if (apiKey && apiSecret) { auth = { accountSid, apiKey, apiSecret }; } else if (authToken) { auth = { accountSid, authToken }; } else { runtime.error("Missing Twilio auth configuration"); runtime.exit(1); throw new Error("unreachable"); } return { accountSid, whatsappFrom, whatsappSenderSid, auth, }; } export function ensureTwilioEnv(runtime: RuntimeEnv = defaultRuntime) { // Guardrails: fail fast when Twilio env vars are missing or incomplete. const required = ["TWILIO_ACCOUNT_SID", "TWILIO_WHATSAPP_FROM"]; const missing = required.filter((k) => !process.env[k]); const hasToken = Boolean(process.env.TWILIO_AUTH_TOKEN); const hasKey = Boolean( process.env.TWILIO_API_KEY && process.env.TWILIO_API_SECRET, ); if (missing.length > 0 || (!hasToken && !hasKey)) { runtime.error( danger( `Missing Twilio env: ${missing.join(", ") || "auth token or api key/secret"}. Set them in .env before using provider=twilio.`, ), ); runtime.exit(1); } }