Spaces:
Running
Running
| import * as fs from 'fs'; | |
| import { promises as pfs } from 'fs'; | |
| import { INPUT_SYSTEM_PROMPT_FILE, MODEL_PROVIDER } from '../utils/common.js'; | |
| export let CONFIG = {}; // Make CONFIG exportable | |
| export let PROMPT_LOG_FILENAME = ''; // Make PROMPT_LOG_FILENAME exportable | |
| const ALL_MODEL_PROVIDERS = Object.values(MODEL_PROVIDER); | |
| function normalizeConfiguredProviders(config) { | |
| const fallbackProvider = MODEL_PROVIDER.GEMINI_CLI; | |
| const dedupedProviders = []; | |
| const addProvider = (value) => { | |
| if (typeof value !== 'string') { | |
| return; | |
| } | |
| const trimmed = value.trim(); | |
| if (!trimmed) { | |
| return; | |
| } | |
| const matched = ALL_MODEL_PROVIDERS.find((provider) => provider.toLowerCase() === trimmed.toLowerCase()); | |
| if (!matched) { | |
| console.warn(`[Config Warning] Unknown model provider '${trimmed}'. This entry will be ignored.`); | |
| return; | |
| } | |
| if (!dedupedProviders.includes(matched)) { | |
| dedupedProviders.push(matched); | |
| } | |
| }; | |
| const rawValue = config.MODEL_PROVIDER; | |
| if (Array.isArray(rawValue)) { | |
| rawValue.forEach((entry) => addProvider(typeof entry === 'string' ? entry : String(entry))); | |
| } else if (typeof rawValue === 'string') { | |
| rawValue.split(',').forEach(addProvider); | |
| } else if (rawValue != null) { | |
| addProvider(String(rawValue)); | |
| } | |
| if (dedupedProviders.length === 0) { | |
| dedupedProviders.push(fallbackProvider); | |
| } | |
| config.DEFAULT_MODEL_PROVIDERS = dedupedProviders; | |
| config.MODEL_PROVIDER = dedupedProviders[0]; | |
| } | |
| /** | |
| * Initializes the server configuration from config.json and command-line arguments. | |
| * @param {string[]} args - Command-line arguments. | |
| * @param {string} [configFilePath='configs/config.json'] - Path to the configuration file. | |
| * @returns {Object} The initialized configuration object. | |
| */ | |
| export async function initializeConfig(args = process.argv.slice(2), configFilePath = 'configs/config.json') { | |
| let currentConfig = {}; | |
| try { | |
| const configData = fs.readFileSync(configFilePath, 'utf8'); | |
| currentConfig = JSON.parse(configData); | |
| console.log('[Config] Loaded configuration from configs/config.json'); | |
| } catch (error) { | |
| console.error('[Config Error] Failed to load configs/config.json:', error.message); | |
| // Fallback to default values if config.json is not found or invalid | |
| currentConfig = { | |
| REQUIRED_API_KEY: "123456", | |
| SERVER_PORT: 3000, | |
| HOST: '0.0.0.0', | |
| MODEL_PROVIDER: MODEL_PROVIDER.GEMINI_CLI, | |
| SYSTEM_PROMPT_FILE_PATH: INPUT_SYSTEM_PROMPT_FILE, // Default value | |
| SYSTEM_PROMPT_MODE: 'append', | |
| PROXY_URL: null, // HTTP/HTTPS/SOCKS5 代理地址,如 http://127.0.0.1:7890 或 socks5://127.0.0.1:1080 | |
| PROXY_ENABLED_PROVIDERS: [], // 启用代理的提供商列表,如 ['gemini-cli-oauth', 'claude-kiro-oauth'] | |
| PROMPT_LOG_BASE_NAME: "prompt_log", | |
| PROMPT_LOG_MODE: "none", | |
| REQUEST_MAX_RETRIES: 3, | |
| REQUEST_BASE_DELAY: 1000, | |
| CREDENTIAL_SWITCH_MAX_RETRIES: 5, // 坏凭证切换最大重试次数(用于认证错误后切换凭证) | |
| CRON_NEAR_MINUTES: 15, | |
| CRON_REFRESH_TOKEN: false, | |
| PROVIDER_POOLS_FILE_PATH: null, // 新增号池配置文件路径 | |
| MAX_ERROR_COUNT: 10, // 提供商最大错误次数 | |
| providerFallbackChain: {} // 跨类型 Fallback 链配置 | |
| }; | |
| console.log('[Config] Using default configuration.'); | |
| } | |
| // Parse command-line arguments | |
| for (let i = 0; i < args.length; i++) { | |
| if (args[i] === '--api-key') { | |
| if (i + 1 < args.length) { | |
| currentConfig.REQUIRED_API_KEY = args[i + 1]; | |
| i++; | |
| } else { | |
| console.warn(`[Config Warning] --api-key flag requires a value.`); | |
| } | |
| } else if (args[i] === '--log-prompts') { | |
| if (i + 1 < args.length) { | |
| const mode = args[i + 1]; | |
| if (mode === 'console' || mode === 'file') { | |
| currentConfig.PROMPT_LOG_MODE = mode; | |
| } else { | |
| console.warn(`[Config Warning] Invalid mode for --log-prompts. Expected 'console' or 'file'. Prompt logging is disabled.`); | |
| } | |
| i++; | |
| } else { | |
| console.warn(`[Config Warning] --log-prompts flag requires a value.`); | |
| } | |
| } else if (args[i] === '--port') { | |
| if (i + 1 < args.length) { | |
| currentConfig.SERVER_PORT = parseInt(args[i + 1], 10); | |
| i++; | |
| } else { | |
| console.warn(`[Config Warning] --port flag requires a value.`); | |
| } | |
| } else if (args[i] === '--model-provider') { | |
| if (i + 1 < args.length) { | |
| currentConfig.MODEL_PROVIDER = args[i + 1]; | |
| i++; | |
| } else { | |
| console.warn(`[Config Warning] --model-provider flag requires a value.`); | |
| } | |
| } else if (args[i] === '--system-prompt-file') { | |
| if (i + 1 < args.length) { | |
| currentConfig.SYSTEM_PROMPT_FILE_PATH = args[i + 1]; | |
| i++; | |
| } else { | |
| console.warn(`[Config Warning] --system-prompt-file flag requires a value.`); | |
| } | |
| } else if (args[i] === '--system-prompt-mode') { | |
| if (i + 1 < args.length) { | |
| const mode = args[i + 1]; | |
| if (mode === 'overwrite' || mode === 'append') { | |
| currentConfig.SYSTEM_PROMPT_MODE = mode; | |
| } else { | |
| console.warn(`[Config Warning] Invalid mode for --system-prompt-mode. Expected 'overwrite' or 'append'. Using default 'overwrite'.`); | |
| } | |
| i++; | |
| } else { | |
| console.warn(`[Config Warning] --system-prompt-mode flag requires a value.`); | |
| } | |
| } else if (args[i] === '--host') { | |
| if (i + 1 < args.length) { | |
| currentConfig.HOST = args[i + 1]; | |
| i++; | |
| } else { | |
| console.warn(`[Config Warning] --host flag requires a value.`); | |
| } | |
| } else if (args[i] === '--prompt-log-base-name') { | |
| if (i + 1 < args.length) { | |
| currentConfig.PROMPT_LOG_BASE_NAME = args[i + 1]; | |
| i++; | |
| } else { | |
| console.warn(`[Config Warning] --prompt-log-base-name flag requires a value.`); | |
| } | |
| } else if (args[i] === '--cron-near-minutes') { | |
| if (i + 1 < args.length) { | |
| currentConfig.CRON_NEAR_MINUTES = parseInt(args[i + 1], 10); | |
| i++; | |
| } else { | |
| console.warn(`[Config Warning] --cron-near-minutes flag requires a value.`); | |
| } | |
| } else if (args[i] === '--cron-refresh-token') { | |
| if (i + 1 < args.length) { | |
| currentConfig.CRON_REFRESH_TOKEN = args[i + 1].toLowerCase() === 'true'; | |
| i++; | |
| } else { | |
| console.warn(`[Config Warning] --cron-refresh-token flag requires a value.`); | |
| } | |
| } else if (args[i] === '--provider-pools-file') { | |
| if (i + 1 < args.length) { | |
| currentConfig.PROVIDER_POOLS_FILE_PATH = args[i + 1]; | |
| i++; | |
| } else { | |
| console.warn(`[Config Warning] --provider-pools-file flag requires a value.`); | |
| } | |
| } else if (args[i] === '--max-error-count') { | |
| if (i + 1 < args.length) { | |
| currentConfig.MAX_ERROR_COUNT = parseInt(args[i + 1], 10); | |
| i++; | |
| } else { | |
| console.warn(`[Config Warning] --max-error-count flag requires a value.`); | |
| } | |
| } | |
| } | |
| normalizeConfiguredProviders(currentConfig); | |
| if (!currentConfig.SYSTEM_PROMPT_FILE_PATH) { | |
| currentConfig.SYSTEM_PROMPT_FILE_PATH = INPUT_SYSTEM_PROMPT_FILE; | |
| } | |
| currentConfig.SYSTEM_PROMPT_CONTENT = await getSystemPromptFileContent(currentConfig.SYSTEM_PROMPT_FILE_PATH); | |
| // 加载号池配置 | |
| if (!currentConfig.PROVIDER_POOLS_FILE_PATH) { | |
| currentConfig.PROVIDER_POOLS_FILE_PATH = 'configs/provider_pools.json'; | |
| } | |
| if (currentConfig.PROVIDER_POOLS_FILE_PATH) { | |
| try { | |
| const poolsData = await pfs.readFile(currentConfig.PROVIDER_POOLS_FILE_PATH, 'utf8'); | |
| currentConfig.providerPools = JSON.parse(poolsData); | |
| console.log(`[Config] Loaded provider pools from ${currentConfig.PROVIDER_POOLS_FILE_PATH}`); | |
| } catch (error) { | |
| console.error(`[Config Error] Failed to load provider pools from ${currentConfig.PROVIDER_POOLS_FILE_PATH}: ${error.message}`); | |
| currentConfig.providerPools = {}; | |
| } | |
| } else { | |
| currentConfig.providerPools = {}; | |
| } | |
| // Set PROMPT_LOG_FILENAME based on the determined config | |
| if (currentConfig.PROMPT_LOG_MODE === 'file') { | |
| const now = new Date(); | |
| const pad = (num) => String(num).padStart(2, '0'); | |
| const timestamp = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`; | |
| PROMPT_LOG_FILENAME = `${currentConfig.PROMPT_LOG_BASE_NAME}-${timestamp}.log`; | |
| } else { | |
| PROMPT_LOG_FILENAME = ''; // Clear if not logging to file | |
| } | |
| // Assign to the exported CONFIG | |
| Object.assign(CONFIG, currentConfig); | |
| return CONFIG; | |
| } | |
| /** | |
| * Gets system prompt content from the specified file path. | |
| * @param {string} filePath - Path to the system prompt file. | |
| * @returns {Promise<string|null>} File content, or null if the file does not exist, is empty, or an error occurs. | |
| */ | |
| export async function getSystemPromptFileContent(filePath) { | |
| try { | |
| await pfs.access(filePath, pfs.constants.F_OK); | |
| } catch (error) { | |
| if (error.code === 'ENOENT') { | |
| console.warn(`[System Prompt] Specified system prompt file not found: ${filePath}`); | |
| } else { | |
| console.error(`[System Prompt] Error accessing system prompt file ${filePath}: ${error.message}`); | |
| } | |
| return null; | |
| } | |
| try { | |
| const content = await pfs.readFile(filePath, 'utf8'); | |
| if (!content.trim()) { | |
| return null; | |
| } | |
| console.log(`[System Prompt] Loaded system prompt from ${filePath}`); | |
| return content; | |
| } catch (error) { | |
| console.error(`[System Prompt] Error reading system prompt file ${filePath}: ${error.message}`); | |
| return null; | |
| } | |
| } | |
| export { ALL_MODEL_PROVIDERS }; | |