function firstNonEmpty(values) { return values.find(value => typeof value === 'string' && value.trim())?.trim() || ''; } function positiveNumber(values, fallback) { const value = Number(firstNonEmpty(values.map(item => item == null ? '' : String(item)))); return Number.isFinite(value) && value > 0 ? value : fallback; } export function readNewApiConfig(options = {}) { return { // API key may be injected by trusted application code/tests, but MCP tool schemas // never expose it. End users should configure one of the environment variables. apiKey: firstNonEmpty([options.apiKey, process.env.FMODE_LLM_API_KEY, process.env.LLM_API_KEY]), // Never accept a caller-controlled endpoint field from an MCP invocation. The // `trustedBaseUrl` option exists only for direct dependency injection in tests. baseUrl: firstNonEmpty([options.trustedBaseUrl, process.env.FMODE_LLM_BASE_URL, process.env.LLM_BASE_URL]), allowInsecureHttp: options.allowInsecureHttp === true || /^true$/i.test(process.env.FMODE_ALLOW_INSECURE_HTTP || ''), timeoutMs: positiveNumber([options.timeoutMs, process.env.FMODE_LLM_TIMEOUT_MS], 180000), usdToCnyRate: positiveNumber([process.env.FMODE_USD_TO_CNY_RATE], 6.8), geminiGenerationReserveUsd: positiveNumber([ process.env.FMODE_GEMINI_GENERATION_RESERVE_USD, process.env.FMODE_GEMINI_GENERATION_RESERVE ], 0.10), geminiVisionReserveUsd: positiveNumber([ process.env.FMODE_GEMINI_VISION_RESERVE_USD, process.env.FMODE_GEMINI_VISION_RESERVE ], 0.01), seedreamImagePriceUsd: positiveNumber([ process.env.FMODE_SEEDREAM_IMAGE_PRICE_USD, process.env.FMODE_SEEDREAM_IMAGE_PRICE ], 0.03) }; } export function asBearerToken(token) { const value = String(token || '').trim(); if (!value) return ''; return /^Bearer\s+/i.test(value) ? value : `Bearer ${value}`; }