import { makeOptInRateLimiter, clientKey } from '../common/rate-limit.js'; // Built LAZILY on first request, NOT at module load: `.env` values are applied to process.env later // (the plugin config hook calls loadEnv → process.env, AFTER this module is imported), so reading // process.env here at import time would always see them unset and silently stay unlimited even when // configured via .env. Building on first request (like the OPENAI_API_KEY reads) sees the loaded env; // the result is cached so the limiter's per-IP window state persists. `null` = unlimited (default). let _openAiRateLimiter; /** OpenAI cost endpoints (realtime/token + hud-summary). Null = unlimited (default). */ function openAiRateLimiter() { if (_openAiRateLimiter === undefined) _openAiRateLimiter = makeOptInRateLimiter( process.env.GEV_RATELIMIT_OPENAI_PER_MIN, ); return _openAiRateLimiter; } /** * Apply an opt-in limiter to a request, writing a 429 when over the cap. * When `limiter` is null (unlimited, the default) this is a no-op returning * `true`, so the handler proceeds exactly as before. * * @param {((key:string)=>boolean)|null} limiter * @param {import('http').IncomingMessage} req * @param {import('http').ServerResponse} res * @returns {boolean} True if the request may proceed; false if a 429 was sent. */ function enforceOptInRateLimit(limiter, req, res) { if (!limiter) return true; // unlimited (default) — no behavior change if (limiter(clientKey(req))) return true; res.statusCode = 429; res.setHeader('Content-Type', 'application/json'); res.setHeader('Retry-After', '5'); res.end(JSON.stringify({ error: 'Rate limit exceeded' })); return false; } export { enforceOptInRateLimit, openAiRateLimiter };