Spaces:
Running
Running
| import fs from 'node:fs/promises'; | |
| import os from 'node:os'; | |
| import path from 'node:path'; | |
| import { CODEX_CONFIG_PATH } from './codex-config.js'; | |
| export const DEFAULT_OPENAI_COMPATIBLE_BASE_URL = 'http://127.0.0.1:8317/v1'; | |
| function stripQuotes(value) { | |
| const trimmed = String(value || '').trim(); | |
| if ( | |
| (trimmed.startsWith('"') && trimmed.endsWith('"')) || | |
| (trimmed.startsWith("'") && trimmed.endsWith("'")) | |
| ) { | |
| return trimmed.slice(1, -1); | |
| } | |
| return trimmed; | |
| } | |
| export function normalizeBaseUrl(value, fallback = DEFAULT_OPENAI_COMPATIBLE_BASE_URL) { | |
| return String(value || fallback).replace(/\/+$/, ''); | |
| } | |
| export async function readCodexProviderBaseUrl() { | |
| let raw = ''; | |
| try { | |
| raw = await fs.readFile(CODEX_CONFIG_PATH, 'utf8'); | |
| } catch { | |
| return null; | |
| } | |
| let provider = 'cliproxyapi'; | |
| let currentProvider = null; | |
| const baseUrls = new Map(); | |
| for (const rawLine of raw.split(/\r?\n/)) { | |
| const line = rawLine.trim(); | |
| if (!line || line.startsWith('#')) { | |
| continue; | |
| } | |
| const providerMatch = line.match(/^\[model_providers\.(?:'([^']+)'|"([^"]+)"|([^\]]+))\]$/); | |
| if (providerMatch) { | |
| currentProvider = stripQuotes(providerMatch[1] || providerMatch[2] || providerMatch[3]); | |
| continue; | |
| } | |
| if (line.startsWith('[')) { | |
| currentProvider = null; | |
| continue; | |
| } | |
| const assignment = line.match(/^([A-Za-z0-9_]+)\s*=\s*(.+)$/); | |
| if (!assignment) { | |
| continue; | |
| } | |
| const key = assignment[1]; | |
| const value = stripQuotes(assignment[2]); | |
| if (!currentProvider && key === 'model_provider') { | |
| provider = value; | |
| } else if (currentProvider && key === 'base_url') { | |
| baseUrls.set(currentProvider, value); | |
| } | |
| } | |
| return baseUrls.get(provider) || null; | |
| } | |
| export async function readCliProxyApiKeys(extraKeys = []) { | |
| const keys = [ | |
| ...extraKeys, | |
| process.env.CLIPROXYAPI_API_KEY, | |
| process.env.CLI_PROXY_API_KEY | |
| ].filter(Boolean); | |
| const candidates = [ | |
| process.env.CLIPROXYAPI_CONFIG, | |
| process.platform === 'win32' ? 'D:\\CLIProxyAPI\\config.yaml' : '', | |
| path.join(os.homedir(), '.cli-proxy-api', 'config.yaml') | |
| ].filter(Boolean); | |
| for (const configPath of candidates) { | |
| let raw = ''; | |
| try { | |
| raw = await fs.readFile(configPath, 'utf8'); | |
| } catch { | |
| continue; | |
| } | |
| let inApiKeys = false; | |
| for (const rawLine of raw.split(/\r?\n/)) { | |
| const line = rawLine.trimEnd(); | |
| if (/^api-keys\s*:\s*$/.test(line.trim())) { | |
| inApiKeys = true; | |
| continue; | |
| } | |
| if (inApiKeys && /^\S/.test(line)) { | |
| break; | |
| } | |
| const match = inApiKeys ? line.match(/^\s*-\s*(.+?)\s*(?:#.*)?$/) : null; | |
| if (match) { | |
| const key = stripQuotes(match[1]); | |
| if (key && !keys.includes(key)) { | |
| keys.push(key); | |
| } | |
| } | |
| } | |
| } | |
| if (process.env.OPENAI_API_KEY && !keys.includes(process.env.OPENAI_API_KEY)) { | |
| keys.push(process.env.OPENAI_API_KEY); | |
| } | |
| return keys; | |
| } | |
| export async function openAICompatibleConfig({ | |
| baseUrl, | |
| defaultBaseUrl = DEFAULT_OPENAI_COMPATIBLE_BASE_URL, | |
| apiKeys = [] | |
| } = {}) { | |
| const resolvedBaseUrl = baseUrl || (await readCodexProviderBaseUrl()) || defaultBaseUrl; | |
| return { | |
| baseUrl: normalizeBaseUrl(resolvedBaseUrl, defaultBaseUrl), | |
| apiKeys: await readCliProxyApiKeys(apiKeys) | |
| }; | |
| } | |