import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import type { AgentConfig, ProviderType } from '../types/index.js'; import { providerRegistry, resolveApiKey } from '../provider/index.js'; const CONFIG_DIR = path.join(os.homedir(), '.ai-terminal-assistant'); const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json'); interface StoredConfig { provider?: ProviderType; model?: string; maxTokens?: number; tavilyApiKey?: string; /** 自定义 API 基础 URL(用于 OpenAI 兼容服务,如阿里云百炼) */ baseUrl?: string; // Vision 配置 visionProvider?: ProviderType; visionModel?: string; /** Vision 专用的 Base URL(用于 OpenAI 兼容的 Vision 服务) */ visionBaseUrl?: string; } // Vision 配置接口 export interface VisionConfig { provider: ProviderType; apiKey: string; model: string; /** 自定义 Base URL(用于 OpenAI 兼容的 Vision 服务) */ baseUrl?: string; } // 默认模型配置 const DEFAULT_MODELS: Record = { anthropic: 'claude-sonnet-4-20250514', deepseek: 'deepseek-chat', openai: 'gpt-4o', }; // 默认 Vision 模型(需要支持图片理解) const DEFAULT_VISION_MODELS: Record = { anthropic: 'claude-sonnet-4-20250514', deepseek: 'deepseek-chat', // DeepSeek 暂不支持 vision,占位用 openai: 'gpt-4o', }; // 默认系统提示词 const DEFAULT_SYSTEM_PROMPT = `你是一个运行在终端中的 AI 编程助手。你可以帮助用户: - 读取和写入文件 - 执行 bash 命令 - 搜索代码和文件 - 回答编程问题 使用工具时请注意: 1. 在修改文件前,先读取文件内容 2. 执行可能有风险的命令前,先向用户确认 3. 给出清晰、简洁的回答 重要的工具使用规则: - 创建或修改文件时,必须使用 write_file 或 edit_file 工具,不要使用 bash 命令(如 cat、echo 等) - write_file 和 edit_file 工具集成了代码诊断功能,可以在写入后自动检查代码错误 - bash 工具仅用于运行命令、安装依赖、执行脚本等操作,不要用于文件内容的创建和修改 当前工作目录: ${process.cwd()} 操作系统: ${process.platform}`; // 获取原始配置(包含所有字段) export function getConfig(): StoredConfig { if (fs.existsSync(CONFIG_FILE)) { try { const content = fs.readFileSync(CONFIG_FILE, 'utf-8'); return JSON.parse(content); } catch { return {}; } } return {}; } // 加载配置 export function loadConfig(): AgentConfig { // 从配置文件读取 const storedConfig = getConfig(); // 确定最终的 provider const finalProvider = storedConfig.provider || 'anthropic'; // 通过 ProviderRegistry 获取 API Key const providerConfig = providerRegistry.getConfig(finalProvider); const finalApiKey = resolveApiKey(providerConfig); if (!finalApiKey) { console.error(`❌ 错误: 未配置 API Key`); console.error(`请在设置中配置 ${finalProvider} 的 API Key`); process.exit(1); } // 确定模型 const finalModel = storedConfig.model || DEFAULT_MODELS[finalProvider]; // 确定 baseUrl const finalBaseUrl = storedConfig.baseUrl || providerConfig?.baseUrl; return { provider: finalProvider, apiKey: finalApiKey, model: finalModel, maxTokens: storedConfig.maxTokens || 4096, systemPrompt: DEFAULT_SYSTEM_PROMPT, baseUrl: finalBaseUrl, }; } /** * 加载 Vision 配置 * Vision 用于图片理解,当主模型不支持 vision 时使用 * 通过 ProviderRegistry 获取 API Key */ export function loadVisionConfig(): VisionConfig | null { // 从配置文件读取 const storedConfig = getConfig(); // 确定 vision provider(默认使用 anthropic,因为 Claude 支持 vision) const finalProvider = storedConfig.visionProvider || 'anthropic'; // 通过 ProviderRegistry 获取 API Key const providerConfig = providerRegistry.getConfig(finalProvider); const finalApiKey = resolveApiKey(providerConfig); // 如果没有 API Key,返回 null if (!finalApiKey) { return null; } // 确定模型 const finalModel = storedConfig.visionModel || DEFAULT_VISION_MODELS[finalProvider]; // 确定 baseUrl const finalBaseUrl = storedConfig.visionBaseUrl || providerConfig?.baseUrl; return { provider: finalProvider, apiKey: finalApiKey, model: finalModel, baseUrl: finalBaseUrl, }; } // 保存配置 export function saveConfig(config: Partial): void { // 确保目录存在 if (!fs.existsSync(CONFIG_DIR)) { fs.mkdirSync(CONFIG_DIR, { recursive: true }); } // 读取现有配置 let existingConfig: StoredConfig = {}; if (fs.existsSync(CONFIG_FILE)) { try { const content = fs.readFileSync(CONFIG_FILE, 'utf-8'); existingConfig = JSON.parse(content); } catch { // 忽略 } } // 合并并保存 const newConfig = { ...existingConfig, ...config }; fs.writeFileSync(CONFIG_FILE, JSON.stringify(newConfig, null, 2)); }