import { readFileSync } from "node:fs"; import { rootPath } from "./src/config"; function assertCondition(condition: unknown, message: string, detail: unknown = {}): void { if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`); } const configPath = rootPath("config", "platform-infra", "sub2api-codex-pool.yaml"); const parsed = Bun.YAML.parse(readFileSync(configPath, "utf8")) as { pool?: { defaultAccountCapacity?: number; defaultAccountLoadFactor?: number; minOwnerConcurrency?: number; defaultTempUnschedulable?: { enabled?: boolean; rules?: Array<{ statusCode?: number; keywords?: string[]; durationMinutes?: number }>; }; }; profiles?: { entries?: Array<{ profile?: string; accountName?: string; capacity?: number; loadFactor?: number; openaiResponsesWebSocketsV2Mode?: string | null }> }; localCodex?: { responsesSmokeModel?: string }; }; const entries = parsed.profiles?.entries ?? []; const rules = parsed.pool?.defaultTempUnschedulable?.rules ?? []; const defaultCapacity = parsed.pool?.defaultAccountCapacity ?? 0; const defaultLoadFactor = parsed.pool?.defaultAccountLoadFactor ?? 0; const desiredCapacity = entries.reduce((total, entry) => total + (entry.capacity ?? defaultCapacity), 0); const allowedWebSocketModes = new Set(["off", "ctx_pool", "passthrough"]); assertCondition(entries.length > 0, "Codex pool must declare YAML-managed profile entries", parsed.profiles); assertCondition(Number.isInteger(defaultCapacity) && defaultCapacity > 0, "defaultAccountCapacity must be a positive integer", parsed.pool); assertCondition(Number.isInteger(defaultLoadFactor) && defaultLoadFactor > 0, "defaultAccountLoadFactor must be a positive integer", parsed.pool); assertCondition(entries.every((entry) => typeof entry.profile === "string" && entry.profile.length > 0), "profile entries must declare profile names", entries); assertCondition(entries.every((entry) => typeof entry.accountName === "string" && entry.accountName.length > 0), "profile entries must declare account names", entries); assertCondition(entries.every((entry) => entry.capacity === undefined || (Number.isInteger(entry.capacity) && entry.capacity > 0)), "profile capacity overrides must be positive integers when declared", entries); assertCondition(entries.every((entry) => entry.loadFactor === undefined || (Number.isInteger(entry.loadFactor) && entry.loadFactor > 0)), "profile load factor overrides must be positive integers when declared", entries); assertCondition( entries.every((entry) => entry.openaiResponsesWebSocketsV2Mode === undefined || entry.openaiResponsesWebSocketsV2Mode === null || allowedWebSocketModes.has(entry.openaiResponsesWebSocketsV2Mode)), "profile WebSocket mode overrides must use supported values when declared", entries, ); assertCondition((parsed.pool?.minOwnerConcurrency ?? 0) >= desiredCapacity, "pool owner concurrency must not bottleneck the declared account capacity set", { minOwnerConcurrency: parsed.pool?.minOwnerConcurrency, desiredCapacity }); if (parsed.pool?.defaultTempUnschedulable?.enabled === true) { assertCondition(rules.length > 0, "enabled temporary unschedulable policy must declare rules", parsed.pool?.defaultTempUnschedulable); assertCondition(rules.every((rule) => Number.isInteger(rule.statusCode) && (rule.statusCode ?? 0) >= 100 && (rule.statusCode ?? 0) <= 599), "temporary unschedulable rules must declare valid HTTP status codes", rules); assertCondition(rules.every((rule) => Array.isArray(rule.keywords) && rule.keywords.length > 0), "temporary unschedulable rules must declare non-empty keywords", rules); assertCondition(rules.every((rule) => Number.isInteger(rule.durationMinutes) && (rule.durationMinutes ?? 0) > 0), "temporary unschedulable rules must declare positive cooldown durations", rules); const gateway502Rule = rules.find((rule) => rule.statusCode === 502); const gateway502Keywords = new Set((gateway502Rule?.keywords ?? []).map((keyword) => keyword.toLowerCase())); assertCondition(gateway502Keywords.has("recovered upstream error"), "502 temporary-unschedulable rule must catch recovered upstream error wrappers", gateway502Rule); const accountState403Rule = rules.find((rule) => rule.statusCode === 403); const quota429Rule = rules.find((rule) => rule.statusCode === 429); const serviceUnavailable503Rule = rules.find((rule) => rule.statusCode === 503); const accountState403Keywords = new Set((accountState403Rule?.keywords ?? []).map((keyword) => keyword.toLowerCase())); const quota429Keywords = new Set((quota429Rule?.keywords ?? []).map((keyword) => keyword.toLowerCase())); const serviceUnavailable503Keywords = new Set((serviceUnavailable503Rule?.keywords ?? []).map((keyword) => keyword.toLowerCase())); for (const keyword of ["weekly limit", "less than 10% of your weekly limit left", "run /status for a breakdown"]) { assertCondition(accountState403Keywords.has(keyword), "403 temporary-unschedulable rule must catch Codex weekly-limit account-state prompts", { keyword, accountState403Rule }); assertCondition(quota429Keywords.has(keyword), "429 temporary-unschedulable rule must catch Codex weekly-limit quota prompts", { keyword, quota429Rule }); } for (const keyword of ["model_not_found", "no available channel for model"]) { assertCondition(serviceUnavailable503Keywords.has(keyword), "503 temporary-unschedulable rule must catch upstream model-routing failures", { keyword, serviceUnavailable503Rule }); } } assertCondition(typeof parsed.localCodex?.responsesSmokeModel === "string" && parsed.localCodex.responsesSmokeModel.length > 0, "localCodex.responsesSmokeModel must be declared for Responses smoke validation", parsed.localCodex); console.log(JSON.stringify({ ok: true, checks: [ "routing config is schema-valid without profile-specific test gates", "pool owner concurrency covers the YAML account capacity set", "profile load factor overrides are YAML-controlled positive integers", "optional WebSocket mode overrides use supported values", "temporary unschedulable rules are structurally valid when enabled", "generic recovered upstream error wrappers are caught by cooldown rules", "Codex weekly-limit prompts are caught by account-state and quota cooldown rules", "upstream model-routing failures are caught by the 503 cooldown rule", "Responses smoke model is YAML-declared", ], }));