256 lines
10 KiB
JavaScript
256 lines
10 KiB
JavaScript
import * as fs from 'fs';
|
|
import { promises as pfs } from 'fs';
|
|
import { INPUT_SYSTEM_PROMPT_FILE, MODEL_PROVIDER } from './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,
|
|
CRON_NEAR_MINUTES: 15,
|
|
CRON_REFRESH_TOKEN: false,
|
|
PROVIDER_POOLS_FILE_PATH: null, // 新增号池配置文件路径
|
|
MAX_ERROR_COUNT: 3, // 提供商最大错误次数
|
|
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 };
|
|
|