@xiaozhi-client/config 1.10.9 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +44 -1
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
- package/src/manager.ts +93 -1
- package/tsup.config.ts +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,26 @@ interface WebUIConfig {
|
|
|
37
37
|
port?: number;
|
|
38
38
|
autoRestart?: boolean;
|
|
39
39
|
}
|
|
40
|
+
interface TTSConfig {
|
|
41
|
+
appid?: string;
|
|
42
|
+
accessToken?: string;
|
|
43
|
+
voice_type?: string;
|
|
44
|
+
encoding?: string;
|
|
45
|
+
cluster?: string;
|
|
46
|
+
endpoint?: string;
|
|
47
|
+
}
|
|
48
|
+
interface ASRConfig {
|
|
49
|
+
appid?: string;
|
|
50
|
+
accessToken?: string;
|
|
51
|
+
cluster?: string;
|
|
52
|
+
wsUrl?: string;
|
|
53
|
+
}
|
|
54
|
+
interface LLMConfig {
|
|
55
|
+
model: string;
|
|
56
|
+
apiKey: string;
|
|
57
|
+
baseURL: string;
|
|
58
|
+
prompt?: string;
|
|
59
|
+
}
|
|
40
60
|
interface ToolCallLogConfig {
|
|
41
61
|
maxRecords?: number;
|
|
42
62
|
logFilePath?: string;
|
|
@@ -146,6 +166,9 @@ interface AppConfig {
|
|
|
146
166
|
webUI?: WebUIConfig;
|
|
147
167
|
platforms?: PlatformsConfig;
|
|
148
168
|
toolCallLog?: ToolCallLogConfig;
|
|
169
|
+
tts?: TTSConfig;
|
|
170
|
+
asr?: ASRConfig;
|
|
171
|
+
llm?: LLMConfig;
|
|
149
172
|
}
|
|
150
173
|
/**
|
|
151
174
|
* 配置管理类
|
|
@@ -540,6 +563,26 @@ declare class ConfigManager {
|
|
|
540
563
|
* 获取配置目录路径(与配置文件同级目录)
|
|
541
564
|
*/
|
|
542
565
|
getConfigDir(): string;
|
|
566
|
+
/**
|
|
567
|
+
* 获取 TTS 配置
|
|
568
|
+
*/
|
|
569
|
+
getTTSConfig(): Readonly<TTSConfig>;
|
|
570
|
+
/**
|
|
571
|
+
* 获取 ASR 配置
|
|
572
|
+
*/
|
|
573
|
+
getASRConfig(): Readonly<ASRConfig>;
|
|
574
|
+
/**
|
|
575
|
+
* 获取 LLM 配置
|
|
576
|
+
*/
|
|
577
|
+
getLLMConfig(): LLMConfig | null;
|
|
578
|
+
/**
|
|
579
|
+
* 检查 LLM 配置是否有效
|
|
580
|
+
*/
|
|
581
|
+
isLLMConfigValid(): boolean;
|
|
582
|
+
/**
|
|
583
|
+
* 更新 TTS 配置
|
|
584
|
+
*/
|
|
585
|
+
updateTTSConfig(ttsConfig: Partial<TTSConfig>): void;
|
|
543
586
|
}
|
|
544
587
|
declare const configManager: ConfigManager;
|
|
545
588
|
|
|
@@ -659,4 +702,4 @@ declare class ConfigInitializer {
|
|
|
659
702
|
private static getDefaultTemplateDir;
|
|
660
703
|
}
|
|
661
704
|
|
|
662
|
-
export { type AppConfig, type ChainHandlerConfig, ConfigInitializer, ConfigManager, ConfigResolver, ConfigValidationError, type ConnectionConfig, type CozePlatformConfig, type CustomMCPConfig, type CustomMCPTool, type FunctionHandlerConfig, type HTTPMCPServerConfig, type HandlerConfig, type HttpHandlerConfig, type LocalMCPServerConfig, type MCPHandlerConfig, type MCPServerConfig, type MCPServerToolsConfig, type MCPServiceConfig, type MCPToolConfig, MCPTransportType, type ModelScopeConfig, type PlatformConfig, type PlatformsConfig, type ProxyHandlerConfig, type SSEMCPServerConfig, type ScriptHandlerConfig, type StreamableHTTPMCPServerConfig, type ToolCallLogConfig, type WebServerInstance, type WebUIConfig, configManager, getConfigTypeDescription, isModelScopeURL, normalizeServiceConfig, normalizeServiceConfigBatch };
|
|
705
|
+
export { type ASRConfig, type AppConfig, type ChainHandlerConfig, ConfigInitializer, ConfigManager, ConfigResolver, ConfigValidationError, type ConnectionConfig, type CozePlatformConfig, type CustomMCPConfig, type CustomMCPTool, type FunctionHandlerConfig, type HTTPMCPServerConfig, type HandlerConfig, type HttpHandlerConfig, type LLMConfig, type LocalMCPServerConfig, type MCPHandlerConfig, type MCPServerConfig, type MCPServerToolsConfig, type MCPServiceConfig, type MCPToolConfig, MCPTransportType, type ModelScopeConfig, type PlatformConfig, type PlatformsConfig, type ProxyHandlerConfig, type SSEMCPServerConfig, type ScriptHandlerConfig, type StreamableHTTPMCPServerConfig, type TTSConfig, type ToolCallLogConfig, type WebServerInstance, type WebUIConfig, configManager, getConfigTypeDescription, isModelScopeURL, normalizeServiceConfig, normalizeServiceConfigBatch };
|
package/dist/index.js
CHANGED
|
@@ -1574,6 +1574,49 @@ var ConfigManager = class _ConfigManager {
|
|
|
1574
1574
|
getConfigDir() {
|
|
1575
1575
|
return process.env.XIAOZHI_CONFIG_DIR || process.cwd();
|
|
1576
1576
|
}
|
|
1577
|
+
/**
|
|
1578
|
+
* 获取 TTS 配置
|
|
1579
|
+
*/
|
|
1580
|
+
getTTSConfig() {
|
|
1581
|
+
const config = this.getConfig();
|
|
1582
|
+
return config.tts || {};
|
|
1583
|
+
}
|
|
1584
|
+
/**
|
|
1585
|
+
* 获取 ASR 配置
|
|
1586
|
+
*/
|
|
1587
|
+
getASRConfig() {
|
|
1588
|
+
const config = this.getConfig();
|
|
1589
|
+
return config.asr || {};
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* 获取 LLM 配置
|
|
1593
|
+
*/
|
|
1594
|
+
getLLMConfig() {
|
|
1595
|
+
const config = this.getConfig();
|
|
1596
|
+
return config.llm || null;
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* 检查 LLM 配置是否有效
|
|
1600
|
+
*/
|
|
1601
|
+
isLLMConfigValid() {
|
|
1602
|
+
const llmConfig = this.getLLMConfig();
|
|
1603
|
+
return llmConfig !== null && typeof llmConfig.model === "string" && llmConfig.model.trim() !== "" && typeof llmConfig.apiKey === "string" && llmConfig.apiKey.trim() !== "" && typeof llmConfig.baseURL === "string" && llmConfig.baseURL.trim() !== "";
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* 更新 TTS 配置
|
|
1607
|
+
*/
|
|
1608
|
+
updateTTSConfig(ttsConfig) {
|
|
1609
|
+
const config = this.getMutableConfig();
|
|
1610
|
+
if (!config.tts) {
|
|
1611
|
+
config.tts = {};
|
|
1612
|
+
}
|
|
1613
|
+
Object.assign(config.tts, ttsConfig);
|
|
1614
|
+
this.saveConfig(config);
|
|
1615
|
+
this.emitEvent("config:updated", {
|
|
1616
|
+
type: "tts",
|
|
1617
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1577
1620
|
};
|
|
1578
1621
|
var configManager = ConfigManager.getInstance();
|
|
1579
1622
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/manager.ts","../src/json5-adapter.ts","../src/resolver.ts","../src/adapter.ts","../src/initializer.ts"],"sourcesContent":["/**\n * 配置管理器\n *\n * 核心配置管理模块,负责:\n * - 配置文件的读取和解析(支持 JSON、JSON5、JSONC 格式)\n * - 配置验证和类型检查\n * - 配置更新和持久化\n * - 配置变更事件通知\n * - 配置文件路径解析\n *\n * @example\n * ```typescript\n * import { configManager } from '@xiaozhi-client/config';\n *\n * // 获取配置\n * const config = configManager.getConfig();\n *\n * // 更新配置\n * configManager.updateConfig({ mcpEndpoint: 'wss://...' });\n *\n * // 监听配置更新事件\n * configManager.on('config:updated', (payload) => {\n * // payload 示例结构:\n * // {\n * // type: 'endpoint' | 'customMCP' | 'config' | 'serverTools' | 'connection' | 'modelscope' | 'webui' | 'platform';\n * // timestamp: Date;\n * // serviceName?: string;\n * // platformName?: string;\n * // }\n * console.log('配置已更新事件:', payload);\n *\n * // 如果需要获取最新的完整配置对象,可在回调中调用 getConfig()\n * const latestConfig = configManager.getConfig();\n * console.log('最新配置对象:', latestConfig);\n * });\n * ```\n */\nimport { copyFileSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as commentJson from \"comment-json\";\nimport dayjs from \"dayjs\";\nimport { createJson5Writer, parseJson5 } from \"./json5-adapter.js\";\nimport { ConfigResolver } from \"./resolver.js\";\n\n// 在 ESM 中,需要从 import.meta.url 获取当前文件目录\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// 默认连接配置\nconst DEFAULT_CONNECTION_CONFIG: Required<ConnectionConfig> = {\n heartbeatInterval: 30000, // 30秒心跳间隔\n heartbeatTimeout: 10000, // 10秒心跳超时\n reconnectInterval: 5000, // 5秒重连间隔\n};\n\n// 配置文件接口定义\n// 本地 MCP 服务配置\nexport interface LocalMCPServerConfig {\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\n// SSE MCP 服务配置\nexport interface SSEMCPServerConfig {\n type: \"sse\";\n url: string;\n headers?: Record<string, string>;\n}\n\n// HTTP MCP 服务配置\nexport interface HTTPMCPServerConfig {\n type?: \"http\" | \"streamable-http\"; // 可选,默认就是 http\n url: string;\n headers?: Record<string, string>;\n}\n\n// 向后兼容的别名\n/** @deprecated 使用 HTTPMCPServerConfig 代替 */\nexport type StreamableHTTPMCPServerConfig = HTTPMCPServerConfig;\n\n// 统一的 MCP 服务配置\nexport type MCPServerConfig =\n | LocalMCPServerConfig\n | SSEMCPServerConfig\n | HTTPMCPServerConfig;\n\nexport interface MCPToolConfig {\n description?: string;\n enable: boolean;\n usageCount?: number; // 工具使用次数\n lastUsedTime?: string; // 最后使用时间(ISO 8601 格式)\n}\n\nexport interface MCPServerToolsConfig {\n tools: Record<string, MCPToolConfig>;\n}\n\nexport interface ConnectionConfig {\n heartbeatInterval?: number; // 心跳检测间隔(毫秒),默认30000\n heartbeatTimeout?: number; // 心跳超时时间(毫秒),默认10000\n reconnectInterval?: number; // 重连间隔(毫秒),默认5000\n}\n\nexport interface ModelScopeConfig {\n apiKey?: string; // ModelScope API 密钥\n}\n\nexport interface WebUIConfig {\n port?: number; // Web UI 端口号,默认 9999\n autoRestart?: boolean; // 是否在配置更新后自动重启服务,默认 true\n}\n\n// 工具调用日志配置接口\nexport interface ToolCallLogConfig {\n maxRecords?: number; // 最大记录条数,默认 100\n logFilePath?: string; // 自定义日志文件路径(可选)\n}\n\n// CustomMCP 相关接口定义\n\n// 代理处理器配置\nexport interface ProxyHandlerConfig {\n type: \"proxy\";\n platform: \"coze\" | \"openai\" | \"anthropic\" | \"custom\";\n config: {\n // Coze 平台配置\n workflow_id?: string;\n bot_id?: string;\n api_key?: string;\n base_url?: string;\n // 通用配置\n timeout?: number;\n retry_count?: number;\n retry_delay?: number;\n headers?: Record<string, string>;\n params?: Record<string, unknown>;\n };\n}\n\n// HTTP 处理器配置\nexport interface HttpHandlerConfig {\n type: \"http\";\n url: string;\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n headers?: Record<string, string>;\n timeout?: number;\n retry_count?: number;\n retry_delay?: number;\n auth?: {\n type: \"bearer\" | \"basic\" | \"api_key\";\n token?: string;\n username?: string;\n password?: string;\n api_key?: string;\n api_key_header?: string;\n };\n body_template?: string; // 支持模板变量替换\n response_mapping?: {\n success_path?: string; // JSONPath 表达式\n error_path?: string;\n data_path?: string;\n };\n}\n\n// 函数处理器配置\nexport interface FunctionHandlerConfig {\n type: \"function\";\n module: string; // 模块路径\n function: string; // 函数名\n timeout?: number;\n context?: Record<string, unknown>; // 函数执行上下文\n}\n\n// 脚本处理器配置\nexport interface ScriptHandlerConfig {\n type: \"script\";\n script: string; // 脚本内容或文件路径\n interpreter?: \"node\" | \"python\" | \"bash\";\n timeout?: number;\n env?: Record<string, string>; // 环境变量\n}\n\n// 链式处理器配置\nexport interface ChainHandlerConfig {\n type: \"chain\";\n tools: string[]; // 要链式调用的工具名称\n mode: \"sequential\" | \"parallel\"; // 执行模式\n error_handling: \"stop\" | \"continue\" | \"retry\"; // 错误处理策略\n}\n\n// MCP 处理器配置(用于同步的工具)\nexport interface MCPHandlerConfig {\n type: \"mcp\";\n config: {\n serviceName: string;\n toolName: string;\n };\n}\n\nexport type HandlerConfig =\n | ProxyHandlerConfig\n | HttpHandlerConfig\n | FunctionHandlerConfig\n | ScriptHandlerConfig\n | ChainHandlerConfig\n | MCPHandlerConfig;\n\n// CustomMCP 工具接口\n// TODO: 注意:此定义应与 @xiaozhi-client/shared-types 中的 CustomMCPToolConfig 保持一致\n// 未来将迁移到从 shared-types 导入\nexport interface CustomMCPTool {\n // 确保必填字段\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n handler: HandlerConfig;\n\n // 使用统计信息(可选)\n stats?: {\n usageCount?: number; // 工具使用次数\n lastUsedTime?: string; // 最后使用时间(ISO 8601格式)\n };\n}\n\n// CustomMCP 配置接口\nexport interface CustomMCPConfig {\n tools: CustomMCPTool[];\n}\n\n// Web 服务器实例接口(用于配置更新通知)\nexport interface WebServerInstance {\n broadcastConfigUpdate(config: AppConfig): void;\n}\n\nexport interface PlatformsConfig {\n [platformName: string]: PlatformConfig;\n}\n\nexport interface PlatformConfig {\n token?: string;\n}\n\n/**\n * 扣子平台配置接口\n */\nexport interface CozePlatformConfig extends PlatformConfig {\n /** 扣子 API Token */\n token: string;\n}\n\nexport interface AppConfig {\n mcpEndpoint: string | string[];\n mcpServers: Record<string, MCPServerConfig>;\n mcpServerConfig?: Record<string, MCPServerToolsConfig>;\n customMCP?: CustomMCPConfig; // 新增 customMCP 配置支持\n connection?: ConnectionConfig; // 连接配置(可选,用于向后兼容)\n modelscope?: ModelScopeConfig; // ModelScope 配置(可选)\n webUI?: WebUIConfig; // Web UI 配置(可选)\n platforms?: PlatformsConfig; // 平台配置(可选)\n toolCallLog?: ToolCallLogConfig; // 工具调用日志配置(可选)\n}\n\n/**\n * 配置管理类\n * 负责管理应用配置,提供只读访问和安全的配置更新功能\n */\nexport class ConfigManager {\n private static instance: ConfigManager;\n private defaultConfigPath: string;\n private config: AppConfig | null = null;\n private currentConfigPath: string | null = null; // 跟踪当前使用的配置文件路径\n private json5Writer: {\n write(data: unknown): void;\n toSource(): string;\n } | null = null; // json5-writer 实例,用于保留 JSON5 注释\n\n // 统计更新并发控制\n private statsUpdateLocks: Map<string, Promise<void>> = new Map();\n private statsUpdateLockTimeouts: Map<string, NodeJS.Timeout> = new Map();\n private readonly STATS_UPDATE_TIMEOUT = 5000; // 5秒超时\n\n // 事件回调(用于解耦 EventBus 依赖)\n private eventCallbacks: Map<string, Array<(data: unknown) => void>> = new Map();\n\n private constructor() {\n // 使用模板目录中的默认配置文件\n // 在不同环境中尝试不同的路径\n const possiblePaths = [\n // 构建后的环境:dist/configManager.js -> dist/templates/default/xiaozhi.config.json\n resolve(__dirname, \"templates\", \"default\", \"xiaozhi.config.json\"),\n // 开发环境:src/configManager.ts -> templates/default/xiaozhi.config.json\n resolve(__dirname, \"..\", \"templates\", \"default\", \"xiaozhi.config.json\"),\n // 测试环境或其他情况\n resolve(process.cwd(), \"templates\", \"default\", \"xiaozhi.config.json\"),\n ];\n\n // 找到第一个存在的路径\n this.defaultConfigPath =\n possiblePaths.find((path) => existsSync(path)) || possiblePaths[0];\n }\n\n /**\n * 注册事件监听器\n */\n public on(eventName: string, callback: (data: unknown) => void): void {\n if (!this.eventCallbacks.has(eventName)) {\n this.eventCallbacks.set(eventName, []);\n }\n this.eventCallbacks.get(eventName)?.push(callback);\n }\n\n /**\n * 发射事件\n */\n private emitEvent(eventName: string, data: unknown): void {\n const callbacks = this.eventCallbacks.get(eventName);\n if (callbacks) {\n for (const callback of callbacks) {\n try {\n callback(data);\n } catch (error) {\n console.error(`事件回调执行失败 [${eventName}]:`, error);\n }\n }\n }\n }\n\n /**\n * 获取配置文件路径(动态计算)\n * 支持多种配置文件格式:json5 > jsonc > json\n *\n * 查找优先级:\n * 1. 环境变量 XIAOZHI_CONFIG_DIR 指定的目录\n * 2. 当前工作目录\n * 3. 用户家目录/.xiaozhi-client/\n */\n private getConfigFilePath(): string {\n // 优先使用 ConfigResolver 解析配置路径\n const resolvedPath = ConfigResolver.resolveConfigPath();\n\n if (resolvedPath) {\n return resolvedPath;\n }\n\n // 如果都找不到,返回用户家目录的默认路径\n const defaultDir = ConfigResolver.getDefaultConfigDir();\n if (defaultDir) {\n return resolve(defaultDir, \"xiaozhi.config.json\");\n }\n\n // 最后回退到当前目录\n const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n return resolve(configDir, \"xiaozhi.config.json\");\n }\n\n /**\n * 获取配置文件格式\n */\n private getConfigFileFormat(filePath: string): \"json5\" | \"jsonc\" | \"json\" {\n if (filePath.endsWith(\".json5\")) {\n return \"json5\";\n }\n\n if (filePath.endsWith(\".jsonc\")) {\n return \"jsonc\";\n }\n\n return \"json\";\n }\n\n /**\n * 获取配置管理器单例实例\n */\n public static getInstance(): ConfigManager {\n if (!ConfigManager.instance) {\n ConfigManager.instance = new ConfigManager();\n }\n return ConfigManager.instance;\n }\n\n /**\n * 检查配置文件是否存在\n *\n * 按优先级检查配置文件是否存在:\n * 1. 环境变量 XIAOZHI_CONFIG_DIR 指定的目录\n * 2. 当前工作目录\n * 3. 用户家目录/.xiaozhi-client/\n */\n public configExists(): boolean {\n return ConfigResolver.resolveConfigPath() !== null;\n }\n\n /**\n * 初始化配置文件\n * 从 config.default.json 复制到 config.json\n * @param format 配置文件格式,默认为 json\n */\n public initConfig(format: \"json\" | \"json5\" | \"jsonc\" = \"json\"): void {\n if (!existsSync(this.defaultConfigPath)) {\n throw new Error(`默认配置模板文件不存在: ${this.defaultConfigPath}`);\n }\n\n // 检查是否已有任何格式的配置文件\n if (this.configExists()) {\n throw new Error(\"配置文件已存在,无需重复初始化\");\n }\n\n // 确定目标配置文件路径\n const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n const targetFileName = `xiaozhi.config.${format}`;\n const configPath = resolve(configDir, targetFileName);\n\n // 复制默认配置文件\n copyFileSync(this.defaultConfigPath, configPath);\n this.config = null; // 重置缓存\n this.json5Writer = null; // 重置 json5Writer 实例\n }\n\n /**\n * 加载配置文件\n */\n private loadConfig(): AppConfig {\n if (!this.configExists()) {\n const error = new Error(\n \"配置文件不存在,请先运行 xiaozhi init 初始化配置\"\n );\n this.emitEvent(\"config:error\", {\n error,\n operation: \"loadConfig\",\n });\n throw error;\n }\n\n try {\n const configPath = this.getConfigFilePath();\n this.currentConfigPath = configPath; // 记录当前使用的配置文件路径\n const configFileFormat = this.getConfigFileFormat(configPath);\n const rawConfigData = readFileSync(configPath, \"utf8\");\n\n // 移除可能存在的UTF-8 BOM字符(\\uFEFF)\n // BOM字符在某些编辑器中不可见,但会导致JSON解析失败\n // 这个过滤确保即使文件包含BOM字符也能正常解析\n const configData = rawConfigData.replace(/^\\uFEFF/, \"\");\n\n let config: AppConfig;\n\n // 根据文件格式使用相应的解析器\n switch (configFileFormat) {\n case \"json5\":\n // 使用 JSON5 解析配置对象,同时使用适配器保留注释信息\n config = parseJson5(configData) as AppConfig;\n // 创建适配器实例用于后续保存时保留注释\n this.json5Writer = createJson5Writer(configData);\n break;\n case \"jsonc\":\n // 使用 comment-json 解析 JSONC 格式,保留注释信息\n config = commentJson.parse(configData) as unknown as AppConfig;\n break;\n default:\n config = JSON.parse(configData) as AppConfig;\n break;\n }\n\n // 验证配置结构\n this.validateConfig(config);\n\n return config;\n } catch (error) {\n // 发射配置错误事件\n this.emitEvent(\"config:error\", {\n error: error instanceof Error ? error : new Error(String(error)),\n operation: \"loadConfig\",\n });\n if (error instanceof SyntaxError) {\n throw new Error(`配置文件格式错误: ${error.message}`);\n }\n throw error;\n }\n }\n\n /**\n * 验证配置文件结构\n */\n public validateConfig(config: unknown): void {\n if (!config || typeof config !== \"object\") {\n throw new Error(\"配置文件格式错误:根对象无效\");\n }\n\n const configObj = config as Record<string, unknown>;\n\n if (configObj.mcpEndpoint === undefined || configObj.mcpEndpoint === null) {\n throw new Error(\"配置文件格式错误:mcpEndpoint 字段无效\");\n }\n\n // 验证 mcpEndpoint 类型(字符串或字符串数组)\n if (typeof configObj.mcpEndpoint === \"string\") {\n // 空字符串是允许的,getMcpEndpoints 会返回空数组\n } else if (Array.isArray(configObj.mcpEndpoint)) {\n for (const endpoint of configObj.mcpEndpoint) {\n if (typeof endpoint !== \"string\" || endpoint.trim() === \"\") {\n throw new Error(\n \"配置文件格式错误:mcpEndpoint 数组中的每个元素必须是非空字符串\"\n );\n }\n }\n } else {\n throw new Error(\"配置文件格式错误:mcpEndpoint 必须是字符串或字符串数组\");\n }\n\n if (!configObj.mcpServers || typeof configObj.mcpServers !== \"object\") {\n throw new Error(\"配置文件格式错误:mcpServers 字段无效\");\n }\n\n // 验证每个 MCP 服务配置\n for (const [serverName, serverConfig] of Object.entries(\n configObj.mcpServers as Record<string, unknown>\n )) {\n if (!serverConfig || typeof serverConfig !== \"object\") {\n throw new Error(`配置文件格式错误:mcpServers.${serverName} 无效`);\n }\n\n // 基本验证:确保配置有效\n // 更详细的验证应该由调用方完成\n }\n }\n\n /**\n * 获取配置(只读)\n */\n public getConfig(): Readonly<AppConfig> {\n this.config = this.loadConfig();\n\n // 返回深度只读副本\n return JSON.parse(JSON.stringify(this.config));\n }\n\n /**\n * 获取可修改的配置对象(内部使用,保留注释信息)\n */\n private getMutableConfig(): AppConfig {\n if (!this.config) {\n this.config = this.loadConfig();\n }\n return this.config;\n }\n\n /**\n * 获取 MCP 端点(向后兼容)\n * @deprecated 使用 getMcpEndpoints() 获取所有端点\n */\n public getMcpEndpoint(): string {\n const config = this.getConfig();\n if (Array.isArray(config.mcpEndpoint)) {\n return config.mcpEndpoint[0] || \"\";\n }\n return config.mcpEndpoint;\n }\n\n /**\n * 获取所有 MCP 端点\n */\n public getMcpEndpoints(): string[] {\n const config = this.getConfig();\n if (Array.isArray(config.mcpEndpoint)) {\n return [...config.mcpEndpoint];\n }\n return config.mcpEndpoint ? [config.mcpEndpoint] : [];\n }\n\n /**\n * 获取 MCP 服务配置\n */\n public getMcpServers(): Readonly<Record<string, MCPServerConfig>> {\n const config = this.getConfig();\n return config.mcpServers;\n }\n\n /**\n * 获取 MCP 服务工具配置\n */\n public getMcpServerConfig(): Readonly<Record<string, MCPServerToolsConfig>> {\n const config = this.getConfig();\n return config.mcpServerConfig || {};\n }\n\n /**\n * 获取指定服务的工具配置\n */\n public getServerToolsConfig(\n serverName: string\n ): Readonly<Record<string, MCPToolConfig>> {\n const serverConfig = this.getMcpServerConfig();\n return serverConfig[serverName]?.tools || {};\n }\n\n /**\n * 检查工具是否启用\n */\n public isToolEnabled(serverName: string, toolName: string): boolean {\n const toolsConfig = this.getServerToolsConfig(serverName);\n const toolConfig = toolsConfig[toolName];\n return toolConfig?.enable !== false; // 默认启用\n }\n\n /**\n * 更新 MCP 端点(支持字符串或数组)\n */\n public updateMcpEndpoint(endpoint: string | string[]): void {\n if (Array.isArray(endpoint)) {\n for (const ep of endpoint) {\n if (!ep || typeof ep !== \"string\") {\n throw new Error(\"MCP 端点数组中的每个元素必须是非空字符串\");\n }\n }\n }\n\n const config = this.getMutableConfig();\n config.mcpEndpoint = endpoint;\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"endpoint\",\n timestamp: new Date(),\n });\n }\n\n /**\n * 添加 MCP 端点\n */\n public addMcpEndpoint(endpoint: string): void {\n if (!endpoint || typeof endpoint !== \"string\") {\n throw new Error(\"MCP 端点必须是非空字符串\");\n }\n\n const config = this.getMutableConfig();\n const currentEndpoints = this.getMcpEndpoints();\n\n // 检查是否已存在\n if (currentEndpoints.includes(endpoint)) {\n throw new Error(`MCP 端点 ${endpoint} 已存在`);\n }\n\n const newEndpoints = [...currentEndpoints, endpoint];\n config.mcpEndpoint = newEndpoints;\n this.saveConfig(config);\n }\n\n /**\n * 移除 MCP 端点\n */\n public removeMcpEndpoint(endpoint: string): void {\n if (!endpoint || typeof endpoint !== \"string\") {\n throw new Error(\"MCP 端点必须是非空字符串\");\n }\n\n const config = this.getMutableConfig();\n const currentEndpoints = this.getMcpEndpoints();\n\n // 检查是否存在\n const index = currentEndpoints.indexOf(endpoint);\n if (index === -1) {\n throw new Error(`MCP 端点 ${endpoint} 不存在`);\n }\n\n const newEndpoints = currentEndpoints.filter((ep) => ep !== endpoint);\n config.mcpEndpoint = newEndpoints;\n this.saveConfig(config);\n }\n\n /**\n * 更新 MCP 服务配置\n */\n public updateMcpServer(\n serverName: string,\n serverConfig: MCPServerConfig\n ): void {\n if (!serverName || typeof serverName !== \"string\") {\n throw new Error(\"服务名称必须是非空字符串\");\n }\n\n const config = this.getMutableConfig();\n // 直接修改配置对象以保留注释信息\n config.mcpServers[serverName] = serverConfig;\n this.saveConfig(config);\n }\n\n /**\n * 删除 MCP 服务配置\n */\n public removeMcpServer(serverName: string): void {\n if (!serverName || typeof serverName !== \"string\") {\n throw new Error(\"服务名称必须是非空字符串\");\n }\n\n const config = this.getMutableConfig();\n\n // 检查服务是否存在\n if (!config.mcpServers[serverName]) {\n throw new Error(`服务 ${serverName} 不存在`);\n }\n\n // 1. 清理 mcpServers 字段(现有逻辑)\n delete config.mcpServers[serverName];\n\n // 2. 清理 mcpServerConfig 字段(复用现有方法)\n if (config.mcpServerConfig?.[serverName]) {\n delete config.mcpServerConfig[serverName];\n }\n\n // 3. 清理 customMCP 字段中相关的工具定义\n if (config.customMCP?.tools) {\n // 查找与该服务相关的 CustomMCP 工具\n const relatedTools = config.customMCP.tools.filter(\n (tool) =>\n tool.handler?.type === \"mcp\" &&\n tool.handler.config?.serviceName === serverName\n );\n\n // 移除相关工具\n for (const tool of relatedTools) {\n const toolIndex = config.customMCP.tools.findIndex(\n (t) => t.name === tool.name\n );\n if (toolIndex !== -1) {\n config.customMCP.tools.splice(toolIndex, 1);\n }\n }\n\n // 如果没有工具了,可以清理整个 customMCP 对象\n if (config.customMCP.tools.length === 0) {\n config.customMCP = undefined;\n }\n }\n\n // 4. 保存配置(单次原子性操作)\n this.saveConfig(config);\n\n // 5. 发射配置更新事件,通知 CustomMCPHandler 重新初始化\n this.emitEvent(\"config:updated\", {\n type: \"customMCP\",\n timestamp: new Date(),\n });\n\n // 记录清理结果\n console.log(\"成功移除 MCP 服务\", { serverName });\n }\n\n /**\n * 批量更新配置(由 Handler 调用)\n */\n public updateConfig(newConfig: Partial<AppConfig>): void {\n const config = this.getMutableConfig();\n\n // 更新 MCP 端点\n if (newConfig.mcpEndpoint !== undefined) {\n config.mcpEndpoint = newConfig.mcpEndpoint;\n }\n\n // 更新 MCP 服务\n if (newConfig.mcpServers) {\n const currentServers = { ...config.mcpServers };\n for (const [name, serverConfig] of Object.entries(newConfig.mcpServers)) {\n config.mcpServers[name] = serverConfig;\n }\n // 删除不存在的服务\n for (const name of Object.keys(currentServers)) {\n if (!(name in newConfig.mcpServers)) {\n delete config.mcpServers[name];\n // 同时清理工具配置\n if (config.mcpServerConfig?.[name]) {\n delete config.mcpServerConfig[name];\n }\n }\n }\n }\n\n // 更新连接配置\n if (newConfig.connection) {\n if (!config.connection) {\n config.connection = {};\n }\n Object.assign(config.connection, newConfig.connection);\n }\n\n // 更新 ModelScope 配置\n if (newConfig.modelscope) {\n if (!config.modelscope) {\n config.modelscope = {};\n }\n Object.assign(config.modelscope, newConfig.modelscope);\n }\n\n // 更新 Web UI 配置\n if (newConfig.webUI) {\n if (!config.webUI) {\n config.webUI = {};\n }\n Object.assign(config.webUI, newConfig.webUI);\n }\n\n // 更新服务工具配置\n if (newConfig.mcpServerConfig) {\n for (const [serverName, toolsConfig] of Object.entries(\n newConfig.mcpServerConfig\n )) {\n if (config.mcpServerConfig?.[serverName]) {\n config.mcpServerConfig[serverName] = toolsConfig;\n }\n }\n }\n\n // 更新平台配置\n if (newConfig.platforms) {\n for (const [platformName, platformConfig] of Object.entries(\n newConfig.platforms\n )) {\n if (!config.platforms) {\n config.platforms = {};\n }\n config.platforms[platformName] = platformConfig;\n }\n }\n\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"config\",\n timestamp: new Date(),\n });\n }\n\n /**\n * 更新服务工具配置\n */\n public updateServerToolsConfig(\n serverName: string,\n toolsConfig: Record<string, MCPToolConfig>\n ): void {\n const config = this.getMutableConfig();\n\n // 确保 mcpServerConfig 存在\n if (!config.mcpServerConfig) {\n config.mcpServerConfig = {};\n }\n\n // 如果 toolsConfig 为空对象,则删除该服务的配置\n if (Object.keys(toolsConfig).length === 0) {\n delete config.mcpServerConfig[serverName];\n } else {\n // 更新指定服务的工具配置\n config.mcpServerConfig[serverName] = {\n tools: toolsConfig,\n };\n }\n\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"serverTools\",\n serviceName: serverName,\n timestamp: new Date(),\n });\n }\n\n /**\n * 删除指定服务器的工具配置\n */\n public removeServerToolsConfig(serverName: string): void {\n const config = this.getConfig();\n const newConfig = { ...config };\n\n // 确保 mcpServerConfig 存在\n if (newConfig.mcpServerConfig) {\n // 删除指定服务的工具配置\n delete newConfig.mcpServerConfig[serverName];\n this.saveConfig(newConfig);\n }\n }\n\n /**\n * 清理无效的服务器工具配置\n * 删除在 mcpServerConfig 中存在但在 mcpServers 中不存在的服务配置\n */\n public cleanupInvalidServerToolsConfig(): void {\n const config = this.getMutableConfig();\n\n // 如果没有 mcpServerConfig,无需清理\n if (!config.mcpServerConfig) {\n return;\n }\n\n const validServerNames = Object.keys(config.mcpServers);\n const configuredServerNames = Object.keys(config.mcpServerConfig);\n\n // 找出需要清理的服务名称\n const invalidServerNames = configuredServerNames.filter(\n (serverName) => !validServerNames.includes(serverName)\n );\n\n if (invalidServerNames.length > 0) {\n // 删除无效的服务配置\n for (const serverName of invalidServerNames) {\n delete config.mcpServerConfig[serverName];\n }\n\n this.saveConfig(config);\n\n console.log(\"已清理无效的服务工具配置\", {\n count: invalidServerNames.length,\n serverNames: invalidServerNames,\n });\n }\n }\n\n /**\n * 设置工具启用状态\n */\n public setToolEnabled(\n serverName: string,\n toolName: string,\n enabled: boolean,\n description?: string\n ): void {\n const config = this.getMutableConfig();\n\n // 确保 mcpServerConfig 存在\n if (!config.mcpServerConfig) {\n config.mcpServerConfig = {};\n }\n\n // 确保服务配置存在\n if (!config.mcpServerConfig[serverName]) {\n config.mcpServerConfig[serverName] = { tools: {} };\n }\n\n // 更新工具配置\n config.mcpServerConfig[serverName].tools[toolName] = {\n ...config.mcpServerConfig[serverName].tools[toolName],\n enable: enabled,\n ...(description && { description }),\n };\n\n this.saveConfig(config);\n }\n\n /**\n * 保存配置到文件\n * 保存到原始配置文件路径,保持文件格式一致性\n */\n private saveConfig(config: AppConfig): void {\n try {\n // 验证配置\n this.validateConfig(config);\n\n // 确定保存路径 - 优先使用当前配置文件路径,否则使用默认路径\n let configPath: string;\n if (this.currentConfigPath) {\n configPath = this.currentConfigPath;\n } else {\n // 如果没有当前路径,使用 getConfigFilePath 获取\n configPath = this.getConfigFilePath();\n this.currentConfigPath = configPath;\n }\n\n // 根据文件格式选择序列化方法\n const configFileFormat = this.getConfigFileFormat(configPath);\n let configContent: string;\n\n switch (configFileFormat) {\n case \"json5\":\n // 对于 JSON5 格式,使用适配器保留注释\n try {\n if (this.json5Writer) {\n // 使用适配器更新配置并保留注释\n this.json5Writer.write(config);\n configContent = this.json5Writer.toSource();\n } else {\n // 如果没有适配器实例,回退到 comment-json 序列化\n console.warn(\"没有 JSON5 适配器实例,使用 comment-json 序列化\");\n configContent = commentJson.stringify(config, null, 2);\n }\n } catch (json5Error) {\n // 如果适配器序列化失败,回退到 comment-json 序列化\n console.warn(\n \"使用 JSON5 适配器保存失败,回退到 comment-json 序列化:\",\n json5Error\n );\n configContent = commentJson.stringify(config, null, 2);\n }\n break;\n case \"jsonc\":\n // 对于 JSONC 格式,使用 comment-json 库保留注释\n try {\n // 直接使用 comment-json 的 stringify 方法\n // 如果 config 是通过 comment-json.parse 解析的,注释信息会被保留\n configContent = commentJson.stringify(config, null, 2);\n } catch (commentJsonError) {\n // 如果 comment-json 序列化失败,回退到标准 JSON\n console.warn(\n \"使用 comment-json 保存失败,回退到标准 JSON 格式:\",\n commentJsonError\n );\n configContent = JSON.stringify(config, null, 2);\n }\n break;\n default:\n configContent = JSON.stringify(config, null, 2);\n break;\n }\n\n // 保存到文件\n writeFileSync(configPath, configContent, \"utf8\");\n\n // 更新缓存\n this.config = config;\n\n console.log(\"配置保存成功\");\n\n // 通知 Web 界面配置已更新(如果 Web 服务器正在运行)\n this.notifyConfigUpdate(config);\n } catch (error) {\n // 发射配置错误事件\n this.emitEvent(\"config:error\", {\n error: error instanceof Error ? error : new Error(String(error)),\n operation: \"saveConfig\",\n });\n throw new Error(\n `保存配置失败: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n /**\n * 重新加载配置(清除缓存)\n */\n public reloadConfig(): void {\n this.config = null;\n this.currentConfigPath = null; // 清除配置文件路径缓存\n this.json5Writer = null; // 清除 json5Writer 实例\n }\n\n /**\n * 获取配置文件路径\n */\n public getConfigPath(): string {\n return this.getConfigFilePath();\n }\n\n /**\n * 获取默认配置文件路径\n */\n public getDefaultConfigPath(): string {\n return this.defaultConfigPath;\n }\n\n /**\n * 获取连接配置(包含默认值)\n */\n public getConnectionConfig(): Required<ConnectionConfig> {\n const config = this.getConfig();\n const connectionConfig = config.connection || {};\n\n return {\n heartbeatInterval:\n connectionConfig.heartbeatInterval ??\n DEFAULT_CONNECTION_CONFIG.heartbeatInterval,\n heartbeatTimeout:\n connectionConfig.heartbeatTimeout ??\n DEFAULT_CONNECTION_CONFIG.heartbeatTimeout,\n reconnectInterval:\n connectionConfig.reconnectInterval ??\n DEFAULT_CONNECTION_CONFIG.reconnectInterval,\n };\n }\n\n /**\n * 获取心跳检测间隔(毫秒)\n */\n public getHeartbeatInterval(): number {\n return this.getConnectionConfig().heartbeatInterval;\n }\n\n /**\n * 获取心跳超时时间(毫秒)\n */\n public getHeartbeatTimeout(): number {\n return this.getConnectionConfig().heartbeatTimeout;\n }\n\n /**\n * 获取重连间隔(毫秒)\n */\n public getReconnectInterval(): number {\n return this.getConnectionConfig().reconnectInterval;\n }\n\n /**\n * 更新连接配置\n */\n public updateConnectionConfig(\n connectionConfig: Partial<ConnectionConfig>\n ): void {\n const config = this.getMutableConfig();\n\n // 确保 connection 对象存在\n if (!config.connection) {\n config.connection = {};\n }\n\n // 直接修改现有的 connection 对象以保留注释\n Object.assign(config.connection, connectionConfig);\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"connection\",\n timestamp: new Date(),\n });\n }\n\n /**\n * 更新工具使用统计信息(MCP 服务工具)\n * @param serverName 服务名称\n * @param toolName 工具名称\n * @param callTime 调用时间(ISO 8601 格式)\n */\n public async updateToolUsageStats(\n serverName: string,\n toolName: string,\n callTime: string\n ): Promise<void>;\n\n /**\n * 更新工具使用统计信息(CustomMCP 工具)\n * @param toolName 工具名称(customMCP 工具名称)\n * @param incrementUsageCount 是否增加使用计数,默认为 true\n */\n public async updateToolUsageStats(\n toolName: string,\n incrementUsageCount?: boolean\n ): Promise<void>;\n\n /**\n * 更新工具使用统计信息的实现\n */\n public async updateToolUsageStats(\n arg1: string,\n arg2: string | boolean | undefined,\n arg3?: string\n ): Promise<void> {\n try {\n // 判断参数类型来区分不同的重载\n if (typeof arg2 === \"string\" && arg3) {\n // 三个参数的情况:updateToolUsageStats(serverName, toolName, callTime)\n const serverName = arg1;\n const toolName = arg2;\n const callTime = arg3;\n\n // 双写机制:同时更新 mcpServerConfig 和 customMCP 中的统计信息\n await Promise.all([\n this._updateMCPServerToolStats(serverName, toolName, callTime),\n this.updateCustomMCPToolStats(serverName, toolName, callTime),\n ]);\n\n console.log(\"工具使用统计已更新\", { serverName, toolName });\n } else {\n // 两个参数的情况:updateToolUsageStats(toolName, incrementUsageCount)\n const toolName = arg1;\n const incrementUsageCount = arg2 as boolean;\n const callTime = new Date().toISOString();\n\n // 只更新 customMCP 中的统计信息\n await this.updateCustomMCPToolStats(\n toolName,\n callTime,\n incrementUsageCount\n );\n\n console.log(\"CustomMCP 工具使用统计已更新\", { toolName });\n }\n } catch (error) {\n // 错误不应该影响主要的工具调用流程\n if (typeof arg2 === \"string\" && arg3) {\n const serverName = arg1;\n const toolName = arg2;\n console.error(\"更新工具使用统计失败\", { serverName, toolName, error });\n } else {\n const toolName = arg1;\n console.error(\"更新 CustomMCP 工具使用统计失败\", { toolName, error });\n }\n }\n }\n\n /**\n * 更新 MCP 服务工具统计信息(重载方法)\n * @param serviceName 服务名称\n * @param toolName 工具名称\n * @param callTime 调用时间(ISO 8601 格式)\n * @param incrementUsageCount 是否增加使用计数,默认为 true\n */\n public async updateMCPServerToolStats(\n serviceName: string,\n toolName: string,\n callTime: string,\n incrementUsageCount = true\n ): Promise<void> {\n await this._updateMCPServerToolStats(\n serviceName,\n toolName,\n callTime,\n incrementUsageCount\n );\n }\n\n /**\n * 设置心跳检测间隔\n */\n public setHeartbeatInterval(interval: number): void {\n if (interval <= 0) {\n throw new Error(\"心跳检测间隔必须大于0\");\n }\n this.updateConnectionConfig({ heartbeatInterval: interval });\n }\n\n /**\n * 设置心跳超时时间\n */\n public setHeartbeatTimeout(timeout: number): void {\n if (timeout <= 0) {\n throw new Error(\"心跳超时时间必须大于0\");\n }\n this.updateConnectionConfig({ heartbeatTimeout: timeout });\n }\n\n /**\n * 设置重连间隔\n */\n public setReconnectInterval(interval: number): void {\n if (interval <= 0) {\n throw new Error(\"重连间隔必须大于0\");\n }\n this.updateConnectionConfig({ reconnectInterval: interval });\n }\n\n /**\n * 获取 ModelScope 配置\n */\n public getModelScopeConfig(): Readonly<ModelScopeConfig> {\n const config = this.getConfig();\n return config.modelscope || {};\n }\n\n /**\n * 获取 ModelScope API Key\n * 优先从配置文件读取,其次从环境变量读取\n */\n public getModelScopeApiKey(): string | undefined {\n const modelScopeConfig = this.getModelScopeConfig();\n return modelScopeConfig.apiKey || process.env.MODELSCOPE_API_TOKEN;\n }\n\n /**\n * 更新 ModelScope 配置\n */\n public updateModelScopeConfig(\n modelScopeConfig: Partial<ModelScopeConfig>\n ): void {\n const config = this.getMutableConfig();\n\n // 确保 modelscope 对象存在\n if (!config.modelscope) {\n config.modelscope = {};\n }\n\n // 直接修改现有的 modelscope 对象以保留注释\n Object.assign(config.modelscope, modelScopeConfig);\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"modelscope\",\n timestamp: new Date(),\n });\n }\n\n /**\n * 设置 ModelScope API Key\n */\n public setModelScopeApiKey(apiKey: string): void {\n if (!apiKey || typeof apiKey !== \"string\") {\n throw new Error(\"API Key 必须是非空字符串\");\n }\n this.updateModelScopeConfig({ apiKey });\n }\n\n /**\n * 获取 customMCP 配置\n */\n public getCustomMCPConfig(): CustomMCPConfig | null {\n const config = this.getConfig();\n return config.customMCP || null;\n }\n\n /**\n * 获取 customMCP 工具列表\n */\n public getCustomMCPTools(): CustomMCPTool[] {\n const customMCPConfig = this.getCustomMCPConfig();\n if (!customMCPConfig || !customMCPConfig.tools) {\n return [];\n }\n\n return customMCPConfig.tools;\n }\n\n /**\n * 验证 customMCP 工具配置\n */\n public validateCustomMCPTools(tools: CustomMCPTool[]): boolean {\n if (!Array.isArray(tools)) {\n return false;\n }\n\n for (const tool of tools) {\n // 检查必需字段\n if (!tool.name || typeof tool.name !== \"string\") {\n console.warn(\"CustomMCP 工具缺少有效的 name 字段\", { tool });\n return false;\n }\n\n if (!tool.description || typeof tool.description !== \"string\") {\n console.warn(\"CustomMCP 工具缺少有效的 description 字段\", {\n toolName: tool.name,\n });\n return false;\n }\n\n if (!tool.inputSchema || typeof tool.inputSchema !== \"object\") {\n console.warn(\"CustomMCP 工具缺少有效的 inputSchema 字段\", {\n toolName: tool.name,\n });\n return false;\n }\n\n if (!tool.handler || typeof tool.handler !== \"object\") {\n console.warn(\"CustomMCP 工具缺少有效的 handler 字段\", {\n toolName: tool.name,\n });\n return false;\n }\n\n // 检查 handler 类型\n if (\n ![\"proxy\", \"function\", \"http\", \"script\", \"chain\", \"mcp\"].includes(\n tool.handler.type\n )\n ) {\n console.warn(\"CustomMCP 工具的 handler.type 类型无效\", {\n toolName: tool.name,\n type: tool.handler.type,\n });\n return false;\n }\n\n // 根据处理器类型进行特定验证\n if (!this.validateHandlerConfig(tool.name, tool.handler)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * 验证处理器配置\n */\n private validateHandlerConfig(\n toolName: string,\n handler: HandlerConfig\n ): boolean {\n switch (handler.type) {\n case \"proxy\":\n return this.validateProxyHandler(toolName, handler);\n case \"http\":\n return this.validateHttpHandler(toolName, handler);\n case \"function\":\n return this.validateFunctionHandler(toolName, handler);\n case \"script\":\n return this.validateScriptHandler(toolName, handler);\n case \"chain\":\n return this.validateChainHandler(toolName, handler);\n case \"mcp\":\n return this.validateMCPHandler(toolName, handler);\n default:\n console.warn(\"CustomMCP 工具使用了未知的处理器类型\", {\n toolName,\n handlerType: (handler as HandlerConfig).type,\n });\n return false;\n }\n }\n\n /**\n * 验证代理处理器配置\n */\n private validateProxyHandler(\n toolName: string,\n handler: ProxyHandlerConfig\n ): boolean {\n if (!handler.platform) {\n console.warn(\"CustomMCP 工具的 proxy 处理器缺少 platform 字段\", {\n toolName,\n });\n return false;\n }\n\n if (![\"coze\", \"openai\", \"anthropic\", \"custom\"].includes(handler.platform)) {\n console.warn(\"CustomMCP 工具的 proxy 处理器使用了不支持的平台\", {\n toolName,\n platform: handler.platform,\n });\n return false;\n }\n\n if (!handler.config || typeof handler.config !== \"object\") {\n console.warn(\"CustomMCP 工具的 proxy 处理器缺少 config 字段\", {\n toolName,\n });\n return false;\n }\n\n // Coze 平台特定验证\n if (handler.platform === \"coze\") {\n if (!handler.config.workflow_id && !handler.config.bot_id) {\n console.warn(\n \"CustomMCP 工具的 Coze 处理器必须提供 workflow_id 或 bot_id\",\n { toolName }\n );\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * 验证 HTTP 处理器配置\n */\n private validateHttpHandler(\n toolName: string,\n handler: HttpHandlerConfig\n ): boolean {\n if (!handler.url || typeof handler.url !== \"string\") {\n console.warn(\"CustomMCP 工具的 http 处理器缺少有效的 url 字段\", {\n toolName,\n });\n return false;\n }\n\n try {\n new URL(handler.url);\n } catch {\n console.warn(\"CustomMCP 工具的 http 处理器 url 格式无效\", {\n toolName,\n url: handler.url,\n });\n return false;\n }\n\n if (\n handler.method &&\n ![\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\"].includes(handler.method)\n ) {\n console.warn(\"CustomMCP 工具的 http 处理器使用了不支持的 HTTP 方法\", {\n toolName,\n method: handler.method,\n });\n return false;\n }\n\n return true;\n }\n\n /**\n * 验证函数处理器配置\n */\n private validateFunctionHandler(\n toolName: string,\n handler: FunctionHandlerConfig\n ): boolean {\n if (!handler.module || typeof handler.module !== \"string\") {\n console.warn(\"CustomMCP 工具的 function 处理器缺少有效的 module 字段\", {\n toolName,\n });\n return false;\n }\n\n if (!handler.function || typeof handler.function !== \"string\") {\n console.warn(\"CustomMCP 工具的 function 处理器缺少有效的 function 字段\", {\n toolName,\n });\n return false;\n }\n\n return true;\n }\n\n /**\n * 验证脚本处理器配置\n */\n private validateScriptHandler(\n toolName: string,\n handler: ScriptHandlerConfig\n ): boolean {\n if (!handler.script || typeof handler.script !== \"string\") {\n console.warn(\"CustomMCP 工具的 script 处理器缺少有效的 script 字段\", {\n toolName,\n });\n return false;\n }\n\n if (\n handler.interpreter &&\n ![\"node\", \"python\", \"bash\"].includes(handler.interpreter)\n ) {\n console.warn(\"CustomMCP 工具的 script 处理器使用了不支持的解释器\", {\n toolName,\n interpreter: handler.interpreter,\n });\n return false;\n }\n\n return true;\n }\n\n /**\n * 验证链式处理器配置\n */\n private validateChainHandler(\n toolName: string,\n handler: ChainHandlerConfig\n ): boolean {\n if (\n !handler.tools ||\n !Array.isArray(handler.tools) ||\n handler.tools.length === 0\n ) {\n console.warn(\"CustomMCP 工具的 chain 处理器缺少有效的 tools 数组\", {\n toolName,\n });\n return false;\n }\n\n if (![\"sequential\", \"parallel\"].includes(handler.mode)) {\n console.warn(\"CustomMCP 工具的 chain 处理器使用了不支持的执行模式\", {\n toolName,\n mode: handler.mode,\n });\n return false;\n }\n\n if (![\"stop\", \"continue\", \"retry\"].includes(handler.error_handling)) {\n console.warn(\"CustomMCP 工具的 chain 处理器使用了不支持的错误处理策略\", {\n toolName,\n errorHandling: handler.error_handling,\n });\n return false;\n }\n\n return true;\n }\n\n /**\n * 验证 MCP 处理器配置\n */\n private validateMCPHandler(\n toolName: string,\n handler: MCPHandlerConfig\n ): boolean {\n if (!handler.config || typeof handler.config !== \"object\") {\n console.warn(\"CustomMCP 工具的 mcp 处理器缺少 config 字段\", { toolName });\n return false;\n }\n\n if (\n !handler.config.serviceName ||\n typeof handler.config.serviceName !== \"string\"\n ) {\n console.warn(\"CustomMCP 工具的 mcp 处理器缺少有效的 serviceName\", {\n toolName,\n });\n return false;\n }\n\n if (\n !handler.config.toolName ||\n typeof handler.config.toolName !== \"string\"\n ) {\n console.warn(\"CustomMCP 工具的 mcp 处理器缺少有效的 toolName\", {\n toolName,\n });\n return false;\n }\n\n return true;\n }\n\n /**\n * 检查是否配置了有效的 customMCP 工具\n */\n public hasValidCustomMCPTools(): boolean {\n try {\n const tools = this.getCustomMCPTools();\n if (tools.length === 0) {\n return false;\n }\n\n return this.validateCustomMCPTools(tools);\n } catch (error) {\n console.error(\"检查 customMCP 工具配置时出错\", { error });\n return false;\n }\n }\n\n /**\n * 添加自定义 MCP 工具\n */\n public addCustomMCPTool(tool: CustomMCPTool): void {\n if (!tool || typeof tool !== \"object\") {\n throw new Error(\"工具配置不能为空\");\n }\n\n const config = this.getMutableConfig();\n\n // 确保 customMCP 配置存在\n if (!config.customMCP) {\n config.customMCP = { tools: [] };\n }\n\n // 检查工具名称是否已存在\n const existingTool = config.customMCP.tools.find(\n (t) => t.name === tool.name\n );\n if (existingTool) {\n throw new Error(`工具 \"${tool.name}\" 已存在`);\n }\n\n // 验证工具配置\n if (!this.validateCustomMCPTools([tool])) {\n throw new Error(\"工具配置验证失败\");\n }\n\n // 添加工具\n config.customMCP.tools.unshift(tool);\n this.saveConfig(config);\n\n console.log(\"成功添加自定义 MCP 工具\", { toolName: tool.name });\n }\n\n /**\n * 批量添加自定义 MCP 工具\n * @param tools 要添加的工具数组\n */\n public async addCustomMCPTools(tools: CustomMCPTool[]): Promise<void> {\n if (!Array.isArray(tools)) {\n throw new Error(\"工具配置必须是数组\");\n }\n\n if (tools.length === 0) {\n return; // 空数组,无需处理\n }\n\n const config = this.getMutableConfig();\n\n // 确保 customMCP 配置存在\n if (!config.customMCP) {\n config.customMCP = { tools: [] };\n }\n\n // 添加新工具,避免重复\n const existingNames = new Set(\n config.customMCP.tools.map((tool) => tool.name)\n );\n const newTools = tools.filter((tool) => !existingNames.has(tool.name));\n\n if (newTools.length > 0) {\n // 验证新工具配置\n if (!this.validateCustomMCPTools(newTools)) {\n throw new Error(\"工具配置验证失败\");\n }\n\n // 添加工具\n config.customMCP.tools.push(...newTools);\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"customMCP\",\n timestamp: new Date(),\n });\n\n console.log(\"成功批量添加自定义 MCP 工具\", {\n count: newTools.length,\n toolNames: newTools.map((t) => t.name),\n });\n }\n }\n\n /**\n * 删除自定义 MCP 工具\n */\n public removeCustomMCPTool(toolName: string): void {\n if (!toolName || typeof toolName !== \"string\") {\n throw new Error(\"工具名称不能为空\");\n }\n\n const config = this.getMutableConfig();\n\n if (!config.customMCP || !config.customMCP.tools) {\n throw new Error(\"未配置自定义 MCP 工具\");\n }\n\n const toolIndex = config.customMCP.tools.findIndex(\n (t) => t.name === toolName\n );\n if (toolIndex === -1) {\n throw new Error(`工具 \"${toolName}\" 不存在`);\n }\n\n // 删除工具\n config.customMCP.tools.splice(toolIndex, 1);\n this.saveConfig(config);\n\n console.log(\"成功删除自定义 MCP 工具\", { toolName });\n }\n\n /**\n * 更新单个自定义 MCP 工具配置\n * @param toolName 工具名称\n * @param updatedTool 更新后的工具配置\n */\n public updateCustomMCPTool(\n toolName: string,\n updatedTool: CustomMCPTool\n ): void {\n if (!toolName || typeof toolName !== \"string\") {\n throw new Error(\"工具名称不能为空\");\n }\n if (!updatedTool || typeof updatedTool !== \"object\") {\n throw new Error(\"更新后的工具配置不能为空\");\n }\n\n const config = this.getMutableConfig();\n\n if (!config.customMCP || !config.customMCP.tools) {\n throw new Error(\"未配置自定义 MCP 工具\");\n }\n\n const toolIndex = config.customMCP.tools.findIndex(\n (t) => t.name === toolName\n );\n if (toolIndex === -1) {\n throw new Error(`工具 \"${toolName}\" 不存在`);\n }\n\n // 验证更新后的工具配置\n if (!this.validateCustomMCPTools([updatedTool])) {\n throw new Error(\"更新后的工具配置验证失败\");\n }\n\n // 更新工具配置\n config.customMCP.tools[toolIndex] = updatedTool;\n this.saveConfig(config);\n\n console.log(\"成功更新自定义 MCP 工具\", { toolName });\n }\n\n /**\n * 更新自定义 MCP 工具配置\n */\n public updateCustomMCPTools(tools: CustomMCPTool[]): void {\n if (!Array.isArray(tools)) {\n throw new Error(\"工具配置必须是数组\");\n }\n\n // 验证工具配置\n if (!this.validateCustomMCPTools(tools)) {\n throw new Error(\"工具配置验证失败\");\n }\n\n const config = this.getMutableConfig();\n\n // 确保 customMCP 配置存在\n if (!config.customMCP) {\n config.customMCP = { tools: [] };\n }\n\n config.customMCP.tools = tools;\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"customMCP\",\n timestamp: new Date(),\n });\n\n console.log(\"成功更新自定义 MCP 工具配置\", { count: tools.length });\n }\n\n /**\n * 获取 Web UI 配置\n */\n public getWebUIConfig(): Readonly<WebUIConfig> {\n const config = this.getConfig();\n return config.webUI || {};\n }\n\n /**\n * 获取 Web UI 端口号\n */\n public getWebUIPort(): number {\n const webUIConfig = this.getWebUIConfig();\n return webUIConfig.port ?? 9999; // 默认端口 9999\n }\n\n /**\n * 通知 Web 界面配置已更新\n * 如果 Web 服务器正在运行,通过 WebSocket 广播配置更新\n */\n private notifyConfigUpdate(config: AppConfig): void {\n try {\n // 检查是否有全局的 webServer 实例(当使用 --ui 参数启动时会设置)\n const webServer = (\n global as typeof global & { __webServer?: WebServerInstance }\n ).__webServer;\n if (webServer && typeof webServer.broadcastConfigUpdate === \"function\") {\n // 调用 webServer 的 broadcastConfigUpdate 方法来通知所有连接的客户端\n webServer.broadcastConfigUpdate(config);\n console.log(\"已通过 WebSocket 广播配置更新\");\n }\n } catch (error) {\n // 静默处理错误,不影响配置保存的主要功能\n console.warn(\n \"通知 Web 界面配置更新失败:\",\n error instanceof Error ? error.message : String(error)\n );\n }\n }\n\n /**\n * 更新 Web UI 配置\n */\n public updateWebUIConfig(webUIConfig: Partial<WebUIConfig>): void {\n const config = this.getMutableConfig();\n\n // 确保 webUI 对象存在\n if (!config.webUI) {\n config.webUI = {};\n }\n\n // 直接修改现有的 webUI 对象以保留注释\n Object.assign(config.webUI, webUIConfig);\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"webui\",\n timestamp: new Date(),\n });\n }\n\n /**\n * 设置 Web UI 端口号\n */\n public setWebUIPort(port: number): void {\n if (!Number.isInteger(port) || port <= 0 || port > 65535) {\n throw new Error(\"端口号必须是 1-65535 之间的整数\");\n }\n this.updateWebUIConfig({ port });\n }\n\n public updatePlatformConfig(\n platformName: string,\n platformConfig: PlatformConfig\n ): void {\n const config = this.getMutableConfig();\n if (!config.platforms) {\n config.platforms = {};\n }\n config.platforms[platformName] = platformConfig;\n // 注意:Web UI 可能需要刷新才能看到更新后的数据\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"platform\",\n platformName,\n timestamp: new Date(),\n });\n }\n\n /**\n * 获取扣子平台配置\n */\n public getCozePlatformConfig(): CozePlatformConfig | null {\n const config = this.getConfig();\n const cozeConfig = config.platforms?.coze;\n\n if (!cozeConfig || !cozeConfig.token) {\n return null;\n }\n\n return {\n token: cozeConfig.token,\n };\n }\n\n /**\n * 获取扣子 API Token\n */\n public getCozeToken(): string | null {\n const cozeConfig = this.getCozePlatformConfig();\n return cozeConfig?.token || null;\n }\n\n /**\n * 设置扣子平台配置\n */\n public setCozePlatformConfig(config: CozePlatformConfig): void {\n if (\n !config.token ||\n typeof config.token !== \"string\" ||\n config.token.trim() === \"\"\n ) {\n throw new Error(\"扣子 API Token 不能为空\");\n }\n\n this.updatePlatformConfig(\"coze\", {\n token: config.token.trim(),\n });\n }\n\n /**\n * 检查扣子平台配置是否有效\n */\n public isCozeConfigValid(): boolean {\n const cozeConfig = this.getCozePlatformConfig();\n return (\n cozeConfig !== null &&\n typeof cozeConfig.token === \"string\" &&\n cozeConfig.token.trim() !== \"\"\n );\n }\n\n /**\n * 更新 mcpServerConfig 中的工具使用统计信息(内部实现)\n * @param serverName 服务名称\n * @param toolName 工具名称\n * @param callTime 调用时间(ISO 8601 格式)\n * @param incrementUsageCount 是否增加使用计数\n * @private\n */\n private async _updateMCPServerToolStats(\n serverName: string,\n toolName: string,\n callTime: string,\n incrementUsageCount = true\n ): Promise<void> {\n const config = this.getMutableConfig();\n\n // 确保 mcpServerConfig 存在\n if (!config.mcpServerConfig) {\n config.mcpServerConfig = {};\n }\n\n // 确保服务配置存在\n if (!config.mcpServerConfig[serverName]) {\n config.mcpServerConfig[serverName] = { tools: {} };\n }\n\n // 确保工具配置存在\n if (!config.mcpServerConfig[serverName].tools[toolName]) {\n config.mcpServerConfig[serverName].tools[toolName] = {\n enable: true, // 默认启用\n };\n }\n\n const toolConfig = config.mcpServerConfig[serverName].tools[toolName];\n const currentUsageCount = toolConfig.usageCount || 0;\n const currentLastUsedTime = toolConfig.lastUsedTime;\n\n // 根据参数决定是否更新使用次数\n if (incrementUsageCount) {\n toolConfig.usageCount = currentUsageCount + 1;\n }\n\n // 时间校验:只有新时间晚于现有时间才更新 lastUsedTime\n if (\n !currentLastUsedTime ||\n new Date(callTime) > new Date(currentLastUsedTime)\n ) {\n // 使用 dayjs 格式化时间为更易读的格式\n toolConfig.lastUsedTime = dayjs(callTime).format(\"YYYY-MM-DD HH:mm:ss\");\n }\n\n // 保存配置\n this.saveConfig(config);\n }\n\n /**\n * 更新 customMCP 中的工具使用统计信息(服务名+工具名版本)\n * @param serverName 服务名称\n * @param toolName 工具名称\n * @param callTime 调用时间(ISO 8601 格式)\n * @private\n */\n private async updateCustomMCPToolStats(\n serverName: string,\n toolName: string,\n callTime: string\n ): Promise<void>;\n\n /**\n * 更新 customMCP 中的工具使用统计信息(工具名版本)\n * @param toolName 工具名称(customMCP 工具名称)\n * @param callTime 调用时间(ISO 8601 格式)\n * @param incrementUsageCount 是否增加使用计数,默认为 true\n * @private\n */\n private async updateCustomMCPToolStats(\n toolName: string,\n callTime: string,\n incrementUsageCount?: boolean\n ): Promise<void>;\n\n /**\n * 更新 customMCP 工具使用统计信息的实现\n * @private\n */\n private async updateCustomMCPToolStats(\n arg1: string,\n arg2: string,\n arg3?: string | boolean\n ): Promise<void> {\n try {\n let toolName: string;\n let callTime: string;\n let incrementUsageCount = true;\n let logPrefix: string;\n\n // 判断参数类型来区分不同的重载\n if (typeof arg3 === \"string\") {\n // 三个字符串参数的情况:updateCustomMCPToolStats(serverName, toolName, callTime)\n const serverName = arg1;\n toolName = `${serverName}__${arg2}`;\n callTime = arg3;\n logPrefix = `${serverName}/${arg2}`;\n } else {\n // 两个或三个参数的情况:updateCustomMCPToolStats(toolName, callTime, incrementUsageCount?)\n toolName = arg1;\n callTime = arg2;\n incrementUsageCount = (arg3 as boolean) || true;\n logPrefix = toolName;\n }\n\n const customTools = this.getCustomMCPTools();\n const toolIndex = customTools.findIndex((tool) => tool.name === toolName);\n\n if (toolIndex === -1) {\n // 如果 customMCP 中没有对应的工具,跳过更新\n return;\n }\n\n const updatedTools = [...customTools];\n const tool = updatedTools[toolIndex];\n\n // 确保 stats 对象存在\n if (!tool.stats) {\n tool.stats = {};\n }\n\n const currentUsageCount = tool.stats.usageCount || 0;\n const currentLastUsedTime = tool.stats.lastUsedTime;\n\n // 根据参数决定是否更新使用次数\n if (incrementUsageCount) {\n tool.stats.usageCount = currentUsageCount + 1;\n }\n\n // 时间校验:只有新时间晚于现有时间才更新 lastUsedTime\n if (\n !currentLastUsedTime ||\n new Date(callTime) > new Date(currentLastUsedTime)\n ) {\n tool.stats.lastUsedTime = dayjs(callTime).format(\"YYYY-MM-DD HH:mm:ss\");\n }\n\n // 保存更新后的工具配置\n await this.updateCustomMCPTools(updatedTools);\n } catch (error) {\n // 根据参数类型决定错误日志的前缀\n if (typeof arg3 === \"string\") {\n const serverName = arg1;\n const toolName = arg2;\n console.error(\"更新 customMCP 工具统计信息失败\", {\n serverName,\n toolName,\n error,\n });\n } else {\n const toolName = arg1;\n console.error(\"更新 customMCP 工具统计信息失败\", { toolName, error });\n }\n // customMCP 统计更新失败不应该影响主要流程\n }\n }\n\n /**\n * 获取统计更新锁(确保同一工具的统计更新串行执行)\n * @param toolKey 工具键\n * @private\n */\n private async acquireStatsUpdateLock(toolKey: string): Promise<boolean> {\n if (this.statsUpdateLocks.has(toolKey)) {\n console.log(\"工具统计更新正在进行中,跳过本次更新\", { toolKey });\n return false;\n }\n\n const updatePromise = new Promise<void>((resolve) => {\n // 锁定逻辑在调用者中实现\n });\n\n this.statsUpdateLocks.set(toolKey, updatePromise);\n\n // 设置超时自动释放锁\n const timeout = setTimeout(() => {\n this.releaseStatsUpdateLock(toolKey);\n }, this.STATS_UPDATE_TIMEOUT);\n\n this.statsUpdateLockTimeouts.set(toolKey, timeout);\n\n return true;\n }\n\n /**\n * 释放统计更新锁\n * @param toolKey 工具键\n * @private\n */\n private releaseStatsUpdateLock(toolKey: string): void {\n this.statsUpdateLocks.delete(toolKey);\n\n const timeout = this.statsUpdateLockTimeouts.get(toolKey);\n if (timeout) {\n clearTimeout(timeout);\n this.statsUpdateLockTimeouts.delete(toolKey);\n }\n\n console.log(\"已释放工具的统计更新锁\", { toolKey });\n }\n\n /**\n * 带并发控制的工具统计更新(CustomMCP 工具)\n * @param toolName 工具名称\n * @param incrementUsageCount 是否增加使用计数\n */\n public async updateToolUsageStatsWithLock(\n toolName: string,\n incrementUsageCount = true\n ): Promise<void> {\n const toolKey = `custommcp_${toolName}`;\n\n if (!(await this.acquireStatsUpdateLock(toolKey))) {\n return; // 已有其他更新在进行\n }\n\n try {\n await this.updateToolUsageStats(toolName, incrementUsageCount);\n console.log(\"工具统计更新完成\", { toolName });\n } catch (error) {\n console.error(\"工具统计更新失败\", { toolName, error });\n throw error;\n } finally {\n this.releaseStatsUpdateLock(toolKey);\n }\n }\n\n /**\n * 带并发控制的工具统计更新(MCP 服务工具)\n * @param serviceName 服务名称\n * @param toolName 工具名称\n * @param callTime 调用时间\n * @param incrementUsageCount 是否增加使用计数\n */\n public async updateMCPServerToolStatsWithLock(\n serviceName: string,\n toolName: string,\n callTime: string,\n incrementUsageCount = true\n ): Promise<void> {\n const toolKey = `mcpserver_${serviceName}_${toolName}`;\n\n if (!(await this.acquireStatsUpdateLock(toolKey))) {\n return; // 已有其他更新在进行\n }\n\n try {\n await this.updateMCPServerToolStats(\n serviceName,\n toolName,\n callTime,\n incrementUsageCount\n );\n console.log(\"MCP 服务工具统计更新完成\", { serviceName, toolName });\n } catch (error) {\n console.error(\"MCP 服务工具统计更新失败\", {\n serviceName,\n toolName,\n error,\n });\n throw error;\n } finally {\n this.releaseStatsUpdateLock(toolKey);\n }\n }\n\n /**\n * 清理所有统计更新锁(用于异常恢复)\n */\n public clearAllStatsUpdateLocks(): void {\n const lockCount = this.statsUpdateLocks.size;\n this.statsUpdateLocks.clear();\n\n // 清理所有超时定时器\n for (const timeout of this.statsUpdateLockTimeouts.values()) {\n clearTimeout(timeout);\n }\n this.statsUpdateLockTimeouts.clear();\n\n if (lockCount > 0) {\n console.log(\"已清理统计更新锁\", { count: lockCount });\n }\n }\n\n /**\n * 获取统计更新锁状态(用于调试和监控)\n */\n public getStatsUpdateLocks(): string[] {\n return Array.from(this.statsUpdateLocks.keys());\n }\n\n /**\n * 获取工具调用日志配置\n */\n public getToolCallLogConfig(): Readonly<ToolCallLogConfig> {\n const config = this.getConfig();\n return config.toolCallLog || {};\n }\n\n /**\n * 更新工具调用日志配置\n */\n public updateToolCallLogConfig(\n toolCallLogConfig: Partial<ToolCallLogConfig>\n ): void {\n const config = this.getMutableConfig();\n\n // 确保 toolCallLog 对象存在\n if (!config.toolCallLog) {\n config.toolCallLog = {};\n }\n\n // 直接修改现有的 toolCallLog 对象以保留注释\n Object.assign(config.toolCallLog, toolCallLogConfig);\n this.saveConfig(config);\n }\n\n /**\n * 获取配置目录路径(与配置文件同级目录)\n */\n public getConfigDir(): string {\n // 配置文件路径 - 优先使用环境变量指定的目录,否则使用当前工作目录\n return process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n }\n}\n\n// 导出单例实例\nexport const configManager = ConfigManager.getInstance();\n","/**\n * JSON5 注释保留适配器\n * 使用 comment-json 实现 JSON5/JSONC 注释保留功能\n *\n * 注意:为了使用 comment-json 保留注释,JSON5 配置文件的键需要带引号。\n * 这与 JSON5 标准语法允许不带引号的键略有不同,但能实现注释保留功能。\n */\nimport * as commentJson from \"comment-json\";\n\n/**\n * JSON5 写入器适配器接口\n * 保持与 json5-writer 兼容的 API\n */\nexport interface Json5WriterAdapter {\n write(data: unknown): void;\n toSource(): string;\n}\n\n/**\n * 创建 JSON5 写入器适配器\n * @param content 原始 JSON5 内容字符串\n * @returns Json5WriterAdapter 实例\n */\nexport function createJson5Writer(content: string): Json5WriterAdapter {\n // 使用 comment-json 解析原始内容\n // comment-json 会保留注释信息在返回的对象中\n const parsedData = commentJson.parse(content) as Record<string, unknown>;\n\n return {\n write(data: unknown): void {\n // 通过 Object.assign 合并新数据\n if (parsedData && typeof parsedData === \"object\" && data) {\n Object.assign(parsedData, data);\n }\n },\n\n toSource(): string {\n // 使用 comment-json 序列化,保留注释和格式\n return commentJson.stringify(parsedData, null, 2);\n },\n };\n}\n\n/**\n * 解析 JSON5 内容(带注释保留)\n * @param content JSON5 内容字符串\n * @returns 解析后的对象\n */\nexport function parseJson5(content: string): unknown {\n // 使用 comment-json 解析,支持注释保留\n return commentJson.parse(content);\n}\n","/**\n * 配置解析器\n * 负责按优先级查找配置文件\n */\n\nimport path from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\n/**\n * 配置解析器类\n * 实现配置文件查找优先级逻辑\n */\nexport class ConfigResolver {\n /**\n * 按优先级解析配置文件路径\n *\n * 优先级顺序:\n * 1. 环境变量 XIAOZHI_CONFIG_DIR 指定的目录\n * 2. 当前工作目录\n * 3. 用户家目录/.xiaozhi-client/\n *\n * @returns 找到的配置文件路径,如果都不存在则返回 null\n */\n static resolveConfigPath(): string | null {\n // 优先级 1: 环境变量指定(向后兼容)\n if (process.env.XIAOZHI_CONFIG_DIR) {\n const configPath = this.findConfigInDir(process.env.XIAOZHI_CONFIG_DIR);\n if (configPath) {\n return configPath;\n }\n }\n\n // 优先级 2: 当前目录\n const currentDirConfig = this.findConfigInDir(process.cwd());\n if (currentDirConfig) {\n return currentDirConfig;\n }\n\n // 优先级 3: 用户家目录/.xiaozhi-client/\n const homeDir = process.env.HOME || process.env.USERPROFILE;\n if (homeDir) {\n const xiaozhiClientDir = path.join(homeDir, \".xiaozhi-client\");\n const homeDirConfig = this.findConfigInDir(xiaozhiClientDir);\n if (homeDirConfig) {\n return homeDirConfig;\n }\n }\n\n return null;\n }\n\n /**\n * 在指定目录中查找配置文件\n *\n * 按优先级查找:xiaozhi.config.json5 > xiaozhi.config.jsonc > xiaozhi.config.json\n *\n * @param dir - 要搜索的目录\n * @returns 找到的配置文件路径,如果不存在则返回 null\n */\n static findConfigInDir(dir: string): string | null {\n const configFileNames = [\n \"xiaozhi.config.json5\",\n \"xiaozhi.config.jsonc\",\n \"xiaozhi.config.json\",\n ];\n\n for (const fileName of configFileNames) {\n const filePath = path.join(dir, fileName);\n if (existsSync(filePath)) {\n return filePath;\n }\n }\n\n return null;\n }\n\n /**\n * 获取默认配置目录路径\n *\n * @returns 用户家目录下的 .xiaozhi-client 目录路径,如果无法获取家目录则返回 null\n */\n static getDefaultConfigDir(): string | null {\n const homeDir = process.env.HOME || process.env.USERPROFILE;\n if (!homeDir) {\n return null;\n }\n return path.join(homeDir, \".xiaozhi-client\");\n }\n}\n","/**\n * 配置适配器\n * 将旧的配置格式转换为新的 MCPServiceConfig 格式,确保向后兼容性\n */\n\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport type {\n HTTPMCPServerConfig,\n LocalMCPServerConfig,\n MCPServerConfig,\n SSEMCPServerConfig,\n} from \"./manager.js\";\nimport { ConfigResolver } from \"./resolver.js\";\n\n// 从外部导入 MCP 类型(这些类型将在运行时从 backend 包解析)\n// 为了避免循环依赖,这里使用动态导入的方式\n// 在实际使用时,adapter 将作为 config 包的一部分被使用\n\n/**\n * 配置验证错误类\n */\nexport class ConfigValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigValidationError\";\n }\n}\n\n// 定义简化的 MCP 传输类型\nexport enum MCPTransportType {\n STDIO = \"stdio\",\n SSE = \"sse\",\n HTTP = \"http\",\n}\n\n// 定义简化的 MCPServiceConfig 接口\nexport interface MCPServiceConfig {\n type: MCPTransportType;\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n url?: string;\n headers?: Record<string, string>;\n}\n\n/**\n * URL 类型推断函数\n * 基于 URL 路径末尾推断传输类型\n */\nfunction inferTransportTypeFromUrl(url: string): MCPTransportType {\n try {\n const parsedUrl = new URL(url);\n const pathname = parsedUrl.pathname;\n\n // 检查路径末尾\n if (pathname.endsWith(\"/sse\")) {\n return MCPTransportType.SSE;\n }\n if (pathname.endsWith(\"/mcp\")) {\n return MCPTransportType.HTTP;\n }\n\n // 默认类型\n return MCPTransportType.HTTP;\n } catch (error) {\n // URL 解析失败时使用默认类型\n return MCPTransportType.HTTP;\n }\n}\n\n/**\n * 将各种配置格式标准化为统一的服务配置格式\n */\nexport function normalizeServiceConfig(\n config: MCPServerConfig\n): MCPServiceConfig {\n console.log(\"转换配置\", { config });\n\n try {\n // 验证输入参数\n if (!config || typeof config !== \"object\") {\n throw new ConfigValidationError(\"配置对象不能为空\");\n }\n\n // 根据配置类型进行转换\n const newConfig = convertByConfigType(config);\n\n // 验证转换后的配置\n validateNewConfig(newConfig);\n\n console.log(\"配置转换成功\", { type: newConfig.type });\n return newConfig;\n } catch (error) {\n console.error(\"配置转换失败\", { error });\n throw error instanceof ConfigValidationError\n ? error\n : new ConfigValidationError(\n `配置转换失败: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * 根据配置类型进行转换\n */\nfunction convertByConfigType(config: MCPServerConfig): MCPServiceConfig {\n // 检查是否为本地 stdio 配置(最高优先级)\n if (isLocalConfig(config)) {\n return convertLocalConfig(config);\n }\n\n // 检查是否有显式指定的类型\n if (\"type\" in config) {\n switch (config.type) {\n case \"sse\":\n return convertSSEConfig(config);\n case \"http\":\n case \"streamable-http\": // 向后兼容\n return convertHTTPConfig(config);\n default:\n throw new ConfigValidationError(`不支持的传输类型: ${config.type}`);\n }\n }\n\n // 检查是否为网络配置(自动推断类型)\n if (\"url\" in config) {\n // 如果 URL 是 undefined 或 null,抛出错误\n if (config.url === undefined || config.url === null) {\n throw new ConfigValidationError(\"网络配置必须包含有效的 url 字段\");\n }\n\n // 先推断类型,然后根据推断的类型选择正确的转换函数\n const inferredType = inferTransportTypeFromUrl(config.url || \"\");\n\n if (inferredType === MCPTransportType.SSE) {\n // 为SSE类型添加显式type字段\n const sseConfig = { ...config, type: \"sse\" as const };\n return convertSSEConfig(sseConfig);\n }\n // 为HTTP类型添加显式type字段\n const httpConfig = { ...config, type: \"http\" as const };\n return convertHTTPConfig(httpConfig);\n }\n\n throw new ConfigValidationError(\"无法识别的配置类型\");\n}\n\n/**\n * 转换本地 stdio 配置\n */\nfunction convertLocalConfig(config: MCPServerConfig): MCPServiceConfig {\n // 类型守卫:确保是 LocalMCPServerConfig\n if (!isLocalConfig(config)) {\n throw new ConfigValidationError(\"无效的本地配置类型\");\n }\n\n const { command, args, env } = config;\n\n if (!command) {\n throw new ConfigValidationError(\"本地配置必须包含 command 字段\");\n }\n\n // 获取配置文件所在目录作为工作目录\n // 优先使用环境变量,否则查找配置文件所在目录,最后回退到当前工作目录\n let workingDir: string;\n if (process.env.XIAOZHI_CONFIG_DIR) {\n workingDir = process.env.XIAOZHI_CONFIG_DIR;\n } else {\n // 使用 ConfigResolver 查找配置文件路径\n const configPath = ConfigResolver.resolveConfigPath();\n if (configPath) {\n // 获取配置文件所在目录\n workingDir = dirname(configPath);\n } else {\n // 回退到当前工作目录\n workingDir = process.cwd();\n }\n }\n\n // 解析 command 中的相对路径\n let resolvedCommand = command;\n if (isRelativePath(command)) {\n resolvedCommand = resolve(workingDir, command);\n console.log(\"解析 command 相对路径\", {\n command,\n resolvedCommand,\n workingDir,\n });\n }\n\n // 解析 args 中的相对路径\n const resolvedArgs = (args || []).map((arg: string) => {\n // 检查是否为相对路径(以 ./ 开头或不以 / 开头且包含文件扩展名)\n if (isRelativePath(arg)) {\n const resolvedPath = resolve(workingDir, arg);\n console.log(\"解析相对路径\", { arg, resolvedPath, workingDir });\n return resolvedPath;\n }\n return arg;\n });\n\n return {\n type: MCPTransportType.STDIO,\n command: resolvedCommand,\n args: resolvedArgs,\n ...(env !== undefined && { env }), // 只在 env 存在时添加该字段\n };\n}\n\n/**\n * 转换 SSE 配置\n */\nfunction convertSSEConfig(config: MCPServerConfig): MCPServiceConfig {\n // 使用类型守卫确保 config 包含必要的属性\n if (!isURLConfig(config)) {\n throw new ConfigValidationError(\"SSE 配置必须包含 url 字段\");\n }\n\n const url = config.url;\n const type = \"type\" in config ? config.type : undefined;\n const headers = \"headers\" in config ? config.headers : undefined;\n\n // 优先使用显式指定的类型,如果没有则进行推断\n const inferredType =\n type === \"sse\"\n ? MCPTransportType.SSE\n : inferTransportTypeFromUrl(url || \"\");\n const isModelScope = url ? isModelScopeURL(url) : false;\n\n console.log(\"SSE配置转换\", {\n url,\n inferredType,\n isModelScope,\n });\n\n return {\n type: inferredType,\n url,\n headers,\n };\n}\n\n/**\n * 转换 HTTP 配置\n */\nfunction convertHTTPConfig(config: MCPServerConfig): MCPServiceConfig {\n // 使用类型守卫确保 config 包含必要的属性\n if (!isURLConfig(config)) {\n throw new ConfigValidationError(\"HTTP 配置必须包含 url 字段\");\n }\n\n const url = config.url;\n const headers = \"headers\" in config ? config.headers : undefined;\n\n return {\n type: MCPTransportType.HTTP,\n url: url || \"\",\n headers,\n };\n}\n\n/**\n * 批量标准化配置\n */\nexport function normalizeServiceConfigBatch(\n legacyConfigs: Record<string, MCPServerConfig>\n): Record<string, MCPServiceConfig> {\n const newConfigs: Record<string, MCPServiceConfig> = {};\n const errors: Array<{ error: Error }> = [];\n\n for (const [name, config] of Object.entries(legacyConfigs)) {\n try {\n newConfigs[name] = normalizeServiceConfig(config);\n } catch (error) {\n errors.push({\n error: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n\n if (errors.length > 0) {\n const errorMessages = errors\n .map(({ error }, index) => `[${index}]: ${error.message}`)\n .join(\"; \");\n throw new ConfigValidationError(`批量配置转换失败: ${errorMessages}`);\n }\n\n console.log(\"批量配置转换成功\", { count: Object.keys(newConfigs).length });\n return newConfigs;\n}\n\n/**\n * 检查是否为相对路径\n */\nfunction isRelativePath(path: string): boolean {\n // 使用 Node.js 的 path.isAbsolute() 来正确检测绝对路径\n // 这个方法能够正确处理 Windows、macOS、Linux 三个平台的路径格式\n if (isAbsolute(path)) {\n return false; // 绝对路径不是相对路径\n }\n\n // 检查是否为相对路径的条件:\n // 1. 以 ./ 或 ../ 开头\n // 2. 包含常见的脚本文件扩展名(且不是绝对路径)\n if (path.startsWith(\"./\") || path.startsWith(\"../\")) {\n return true;\n }\n\n // 如果包含文件扩展名且不是绝对路径,也认为是相对路径\n if (/\\.(js|py|ts|mjs|cjs)$/i.test(path)) {\n return true;\n }\n\n return false;\n}\n\n/**\n * 检查是否为本地配置\n */\nfunction isLocalConfig(\n config: MCPServerConfig\n): config is LocalMCPServerConfig {\n return \"command\" in config && typeof config.command === \"string\";\n}\n\n/**\n * 检查是否为 URL 配置(SSE 或 HTTP)\n * 类型守卫函数,用于验证配置包含 url 属性\n */\nfunction isURLConfig(\n config: MCPServerConfig\n): config is (SSEMCPServerConfig | HTTPMCPServerConfig) & { url: string } {\n return \"url\" in config && typeof config.url === \"string\";\n}\n\n/**\n * 检查是否为 ModelScope URL\n * 使用 URL hostname 检查而非简单的字符串包含检查,防止安全绕过\n */\nexport function isModelScopeURL(url: string): boolean {\n try {\n const parsedUrl = new URL(url);\n const hostname = parsedUrl.hostname.toLowerCase();\n return (\n hostname.endsWith(\".modelscope.net\") ||\n hostname.endsWith(\".modelscope.cn\") ||\n hostname === \"modelscope.net\" ||\n hostname === \"modelscope.cn\"\n );\n } catch {\n return false;\n }\n}\n\n/**\n * 验证新配置格式\n */\nfunction validateNewConfig(config: MCPServiceConfig): void {\n if (config.type && !Object.values(MCPTransportType).includes(config.type)) {\n throw new ConfigValidationError(`无效的传输类型: ${config.type}`);\n }\n\n // 根据传输类型验证必需字段\n if (!config.type) {\n throw new ConfigValidationError(\"传输类型未指定,请检查配置或启用自动推断\");\n }\n\n switch (config.type) {\n case MCPTransportType.STDIO:\n if (!config.command) {\n throw new ConfigValidationError(\"STDIO 配置必须包含 command 字段\");\n }\n break;\n\n case MCPTransportType.SSE:\n // SSE 配置必须有 URL(即使是空字符串也会被推断为 HTTP)\n if (config.url === undefined || config.url === null) {\n throw new ConfigValidationError(\"SSE 配置必须包含 url 字段\");\n }\n break;\n\n case MCPTransportType.HTTP:\n // HTTP 配置允许空 URL,会在后续处理中设置默认值\n // 只有当 URL 完全不存在时才报错\n if (config.url === undefined || config.url === null) {\n throw new ConfigValidationError(\"HTTP 配置必须包含 url 字段\");\n }\n break;\n\n default:\n throw new ConfigValidationError(`不支持的传输类型: ${config.type}`);\n }\n}\n\n/**\n * 获取配置类型描述\n */\nexport function getConfigTypeDescription(config: MCPServerConfig): string {\n if (isLocalConfig(config)) {\n return `本地进程 (${config.command})`;\n }\n\n if (\"url\" in config) {\n // 检查是否为显式 http 配置\n if (\n \"type\" in config &&\n (config.type === \"http\" || config.type === \"streamable-http\")\n ) {\n return `HTTP (${config.url})`;\n }\n\n // 检查是否为显式 sse 配置\n if (\"type\" in config && config.type === \"sse\") {\n const isModelScope = isModelScopeURL(config.url);\n return `SSE${isModelScope ? \" (ModelScope)\" : \"\"} (${config.url})`;\n }\n\n // 对于只有 url 的配置,根据路径推断类型\n const inferredType = inferTransportTypeFromUrl(config.url);\n const isModelScope = isModelScopeURL(config.url);\n\n if (inferredType === MCPTransportType.SSE) {\n return `SSE${isModelScope ? \" (ModelScope)\" : \"\"} (${config.url})`;\n }\n return `HTTP (${config.url})`;\n }\n\n return \"未知类型\";\n}\n","/**\n * 配置初始化器\n * 负责在用户家目录创建默认配置\n */\n\nimport path from \"node:path\";\nimport { mkdirSync, existsSync, rmSync, readdirSync, statSync, copyFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n/**\n * 配置初始化器类\n * 负责在用户家目录创建完整的默认项目目录\n */\nexport class ConfigInitializer {\n /**\n * 初始化默认配置\n *\n * 复制整个默认模板目录到用户家目录的 .xiaozhi-client\n * 这包括 mcpServers/ 目录和其他必要文件\n *\n * @returns 创建的项目目录路径\n * @throws 如果无法获取用户家目录或默认配置模板不存在\n */\n static async initializeDefaultConfig(): Promise<string> {\n const homeDir = process.env.HOME || process.env.USERPROFILE;\n if (!homeDir) {\n throw new Error(\"无法获取用户家目录\");\n }\n\n const xiaozhiClientDir = path.join(homeDir, \".xiaozhi-client\");\n\n // 如果目录已存在,直接使用现有配置目录,避免删除用户数据\n if (existsSync(xiaozhiClientDir)) {\n return xiaozhiClientDir;\n }\n\n // 创建目录\n mkdirSync(xiaozhiClientDir, { recursive: true });\n\n // 获取默认模板目录路径\n const defaultTemplateDir = this.getDefaultTemplateDir();\n if (!defaultTemplateDir) {\n throw new Error(\n \"默认配置模板不存在,请检查项目模板文件是否存在\"\n );\n }\n\n // 复制整个模板目录\n this.copyDirectoryRecursive(defaultTemplateDir, xiaozhiClientDir, [\n \"template.json\",\n \".git\",\n \"node_modules\",\n ]);\n\n return xiaozhiClientDir;\n }\n\n /**\n * 递归复制目录\n *\n * @param srcDir 源目录\n * @param destDir 目标目录\n * @param exclude 要排除的文件/目录列表\n */\n private static copyDirectoryRecursive(\n srcDir: string,\n destDir: string,\n exclude: string[] = []\n ): void {\n const items = readdirSync(srcDir);\n\n for (const item of items) {\n // 跳过排除列表中的项\n if (exclude.includes(item)) {\n continue;\n }\n\n const srcPath = path.join(srcDir, item);\n const destPath = path.join(destDir, item);\n const stat = statSync(srcPath);\n\n if (stat.isDirectory()) {\n // 递归复制子目录\n mkdirSync(destPath, { recursive: true });\n this.copyDirectoryRecursive(srcPath, destPath, exclude);\n } else {\n // 复制文件\n copyFileSync(srcPath, destPath);\n }\n }\n }\n\n /**\n * 获取默认模板目录路径\n *\n * 在多个可能的路径中查找默认模板目录\n *\n * @returns 找到的默认模板目录路径,如果都不存在则返回 null\n */\n private static getDefaultTemplateDir(): string | null {\n const possiblePaths = [\n // 开发环境:packages/config/src 目录\n resolve(__dirname, \"templates\", \"default\"),\n // 开发环境:packages/config 目录\n resolve(__dirname, \"..\", \"templates\", \"default\"),\n // 项目根目录的 templates\n resolve(process.cwd(), \"templates\", \"default\"),\n // dist 目录(从 packages/config/dist 配置目录)\n resolve(__dirname, \"..\", \"..\", \"..\", \"templates\", \"default\"),\n // 全局安装的 node_modules 目录\n resolve(__dirname, \"..\", \"..\", \"..\", \"..\", \"templates\", \"default\"),\n ];\n\n for (const p of possiblePaths) {\n if (existsSync(p)) {\n return p;\n }\n }\n\n return null;\n }\n}\n"],"mappings":"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","names":["existsSync","commentJson","path","existsSync","tool","resolve","dirname","resolve","MCPTransportType","dirname","resolve","path","isModelScope","path","existsSync","copyFileSync","dirname","resolve","fileURLToPath","__dirname","dirname","fileURLToPath","path","existsSync","copyFileSync","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../src/manager.ts","../src/json5-adapter.ts","../src/resolver.ts","../src/adapter.ts","../src/initializer.ts"],"sourcesContent":["/**\n * 配置管理器\n *\n * 核心配置管理模块,负责:\n * - 配置文件的读取和解析(支持 JSON、JSON5、JSONC 格式)\n * - 配置验证和类型检查\n * - 配置更新和持久化\n * - 配置变更事件通知\n * - 配置文件路径解析\n *\n * @example\n * ```typescript\n * import { configManager } from '@xiaozhi-client/config';\n *\n * // 获取配置\n * const config = configManager.getConfig();\n *\n * // 更新配置\n * configManager.updateConfig({ mcpEndpoint: 'wss://...' });\n *\n * // 监听配置更新事件\n * configManager.on('config:updated', (payload) => {\n * // payload 示例结构:\n * // {\n * // type: 'endpoint' | 'customMCP' | 'config' | 'serverTools' | 'connection' | 'modelscope' | 'webui' | 'platform';\n * // timestamp: Date;\n * // serviceName?: string;\n * // platformName?: string;\n * // }\n * console.log('配置已更新事件:', payload);\n *\n * // 如果需要获取最新的完整配置对象,可在回调中调用 getConfig()\n * const latestConfig = configManager.getConfig();\n * console.log('最新配置对象:', latestConfig);\n * });\n * ```\n */\nimport { copyFileSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as commentJson from \"comment-json\";\nimport dayjs from \"dayjs\";\nimport { createJson5Writer, parseJson5 } from \"./json5-adapter.js\";\nimport { ConfigResolver } from \"./resolver.js\";\n\n// 在 ESM 中,需要从 import.meta.url 获取当前文件目录\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// 默认连接配置\nconst DEFAULT_CONNECTION_CONFIG: Required<ConnectionConfig> = {\n heartbeatInterval: 30000, // 30秒心跳间隔\n heartbeatTimeout: 10000, // 10秒心跳超时\n reconnectInterval: 5000, // 5秒重连间隔\n};\n\n// 配置文件接口定义\n// 本地 MCP 服务配置\nexport interface LocalMCPServerConfig {\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\n// SSE MCP 服务配置\nexport interface SSEMCPServerConfig {\n type: \"sse\";\n url: string;\n headers?: Record<string, string>;\n}\n\n// HTTP MCP 服务配置\nexport interface HTTPMCPServerConfig {\n type?: \"http\" | \"streamable-http\"; // 可选,默认就是 http\n url: string;\n headers?: Record<string, string>;\n}\n\n// 向后兼容的别名\n/** @deprecated 使用 HTTPMCPServerConfig 代替 */\nexport type StreamableHTTPMCPServerConfig = HTTPMCPServerConfig;\n\n// 统一的 MCP 服务配置\nexport type MCPServerConfig =\n | LocalMCPServerConfig\n | SSEMCPServerConfig\n | HTTPMCPServerConfig;\n\nexport interface MCPToolConfig {\n description?: string;\n enable: boolean;\n usageCount?: number; // 工具使用次数\n lastUsedTime?: string; // 最后使用时间(ISO 8601 格式)\n}\n\nexport interface MCPServerToolsConfig {\n tools: Record<string, MCPToolConfig>;\n}\n\nexport interface ConnectionConfig {\n heartbeatInterval?: number; // 心跳检测间隔(毫秒),默认30000\n heartbeatTimeout?: number; // 心跳超时时间(毫秒),默认10000\n reconnectInterval?: number; // 重连间隔(毫秒),默认5000\n}\n\nexport interface ModelScopeConfig {\n apiKey?: string; // ModelScope API 密钥\n}\n\nexport interface WebUIConfig {\n port?: number; // Web UI 端口号,默认 9999\n autoRestart?: boolean; // 是否在配置更新后自动重启服务,默认 true\n}\n\n// 工具调用日志配置接口\n// TTS 配置接口\nexport interface TTSConfig {\n appid?: string; // 应用 ID\n accessToken?: string; // 访问令牌\n voice_type?: string; // 声音类型\n encoding?: string; // 编码格式(默认 wav)\n cluster?: string; // 集群类型\n endpoint?: string; // WebSocket 端点\n}\n\n// ASR 配置接口\nexport interface ASRConfig {\n appid?: string; // 应用 ID\n accessToken?: string; // 访问令牌\n cluster?: string; // 集群类型(默认:volcengine_streaming_common)\n wsUrl?: string; // WebSocket 端点\n}\n\n// LLM 配置接口\nexport interface LLMConfig {\n model: string; // 模型名称\n apiKey: string; // API 密钥\n baseURL: string; // API 基础地址\n prompt?: string; // 自定义系统提示词(支持纯字符串或文件路径)\n}\n\nexport interface ToolCallLogConfig {\n maxRecords?: number; // 最大记录条数,默认 100\n logFilePath?: string; // 自定义日志文件路径(可选)\n}\n\n// CustomMCP 相关接口定义\n\n// 代理处理器配置\nexport interface ProxyHandlerConfig {\n type: \"proxy\";\n platform: \"coze\" | \"openai\" | \"anthropic\" | \"custom\";\n config: {\n // Coze 平台配置\n workflow_id?: string;\n bot_id?: string;\n api_key?: string;\n base_url?: string;\n // 通用配置\n timeout?: number;\n retry_count?: number;\n retry_delay?: number;\n headers?: Record<string, string>;\n params?: Record<string, unknown>;\n };\n}\n\n// HTTP 处理器配置\nexport interface HttpHandlerConfig {\n type: \"http\";\n url: string;\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n headers?: Record<string, string>;\n timeout?: number;\n retry_count?: number;\n retry_delay?: number;\n auth?: {\n type: \"bearer\" | \"basic\" | \"api_key\";\n token?: string;\n username?: string;\n password?: string;\n api_key?: string;\n api_key_header?: string;\n };\n body_template?: string; // 支持模板变量替换\n response_mapping?: {\n success_path?: string; // JSONPath 表达式\n error_path?: string;\n data_path?: string;\n };\n}\n\n// 函数处理器配置\nexport interface FunctionHandlerConfig {\n type: \"function\";\n module: string; // 模块路径\n function: string; // 函数名\n timeout?: number;\n context?: Record<string, unknown>; // 函数执行上下文\n}\n\n// 脚本处理器配置\nexport interface ScriptHandlerConfig {\n type: \"script\";\n script: string; // 脚本内容或文件路径\n interpreter?: \"node\" | \"python\" | \"bash\";\n timeout?: number;\n env?: Record<string, string>; // 环境变量\n}\n\n// 链式处理器配置\nexport interface ChainHandlerConfig {\n type: \"chain\";\n tools: string[]; // 要链式调用的工具名称\n mode: \"sequential\" | \"parallel\"; // 执行模式\n error_handling: \"stop\" | \"continue\" | \"retry\"; // 错误处理策略\n}\n\n// MCP 处理器配置(用于同步的工具)\nexport interface MCPHandlerConfig {\n type: \"mcp\";\n config: {\n serviceName: string;\n toolName: string;\n };\n}\n\nexport type HandlerConfig =\n | ProxyHandlerConfig\n | HttpHandlerConfig\n | FunctionHandlerConfig\n | ScriptHandlerConfig\n | ChainHandlerConfig\n | MCPHandlerConfig;\n\n// CustomMCP 工具接口\n// TODO: 注意:此定义应与 @xiaozhi-client/shared-types 中的 CustomMCPToolConfig 保持一致\n// 未来将迁移到从 shared-types 导入\nexport interface CustomMCPTool {\n // 确保必填字段\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n handler: HandlerConfig;\n\n // 使用统计信息(可选)\n stats?: {\n usageCount?: number; // 工具使用次数\n lastUsedTime?: string; // 最后使用时间(ISO 8601格式)\n };\n}\n\n// CustomMCP 配置接口\nexport interface CustomMCPConfig {\n tools: CustomMCPTool[];\n}\n\n// Web 服务器实例接口(用于配置更新通知)\nexport interface WebServerInstance {\n broadcastConfigUpdate(config: AppConfig): void;\n}\n\nexport interface PlatformsConfig {\n [platformName: string]: PlatformConfig;\n}\n\nexport interface PlatformConfig {\n token?: string;\n}\n\n/**\n * 扣子平台配置接口\n */\nexport interface CozePlatformConfig extends PlatformConfig {\n /** 扣子 API Token */\n token: string;\n}\n\nexport interface AppConfig {\n mcpEndpoint: string | string[];\n mcpServers: Record<string, MCPServerConfig>;\n mcpServerConfig?: Record<string, MCPServerToolsConfig>;\n customMCP?: CustomMCPConfig; // 新增 customMCP 配置支持\n connection?: ConnectionConfig; // 连接配置(可选,用于向后兼容)\n modelscope?: ModelScopeConfig; // ModelScope 配置(可选)\n webUI?: WebUIConfig; // Web UI 配置(可选)\n platforms?: PlatformsConfig; // 平台配置(可选)\n toolCallLog?: ToolCallLogConfig; // 工具调用日志配置(可选)\n tts?: TTSConfig; // TTS 配置(可选)\n asr?: ASRConfig; // ASR 配置(可选)\n llm?: LLMConfig; // LLM 配置(可选)\n}\n\n/**\n * 配置管理类\n * 负责管理应用配置,提供只读访问和安全的配置更新功能\n */\nexport class ConfigManager {\n private static instance: ConfigManager;\n private defaultConfigPath: string;\n private config: AppConfig | null = null;\n private currentConfigPath: string | null = null; // 跟踪当前使用的配置文件路径\n private json5Writer: {\n write(data: unknown): void;\n toSource(): string;\n } | null = null; // json5-writer 实例,用于保留 JSON5 注释\n\n // 统计更新并发控制\n private statsUpdateLocks: Map<string, Promise<void>> = new Map();\n private statsUpdateLockTimeouts: Map<string, NodeJS.Timeout> = new Map();\n private readonly STATS_UPDATE_TIMEOUT = 5000; // 5秒超时\n\n // 事件回调(用于解耦 EventBus 依赖)\n private eventCallbacks: Map<string, Array<(data: unknown) => void>> =\n new Map();\n\n private constructor() {\n // 使用模板目录中的默认配置文件\n // 在不同环境中尝试不同的路径\n const possiblePaths = [\n // 构建后的环境:dist/configManager.js -> dist/templates/default/xiaozhi.config.json\n resolve(__dirname, \"templates\", \"default\", \"xiaozhi.config.json\"),\n // 开发环境:src/configManager.ts -> templates/default/xiaozhi.config.json\n resolve(__dirname, \"..\", \"templates\", \"default\", \"xiaozhi.config.json\"),\n // 测试环境或其他情况\n resolve(process.cwd(), \"templates\", \"default\", \"xiaozhi.config.json\"),\n ];\n\n // 找到第一个存在的路径\n this.defaultConfigPath =\n possiblePaths.find((path) => existsSync(path)) || possiblePaths[0];\n }\n\n /**\n * 注册事件监听器\n */\n public on(eventName: string, callback: (data: unknown) => void): void {\n if (!this.eventCallbacks.has(eventName)) {\n this.eventCallbacks.set(eventName, []);\n }\n this.eventCallbacks.get(eventName)?.push(callback);\n }\n\n /**\n * 发射事件\n */\n private emitEvent(eventName: string, data: unknown): void {\n const callbacks = this.eventCallbacks.get(eventName);\n if (callbacks) {\n for (const callback of callbacks) {\n try {\n callback(data);\n } catch (error) {\n console.error(`事件回调执行失败 [${eventName}]:`, error);\n }\n }\n }\n }\n\n /**\n * 获取配置文件路径(动态计算)\n * 支持多种配置文件格式:json5 > jsonc > json\n *\n * 查找优先级:\n * 1. 环境变量 XIAOZHI_CONFIG_DIR 指定的目录\n * 2. 当前工作目录\n * 3. 用户家目录/.xiaozhi-client/\n */\n private getConfigFilePath(): string {\n // 优先使用 ConfigResolver 解析配置路径\n const resolvedPath = ConfigResolver.resolveConfigPath();\n\n if (resolvedPath) {\n return resolvedPath;\n }\n\n // 如果都找不到,返回用户家目录的默认路径\n const defaultDir = ConfigResolver.getDefaultConfigDir();\n if (defaultDir) {\n return resolve(defaultDir, \"xiaozhi.config.json\");\n }\n\n // 最后回退到当前目录\n const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n return resolve(configDir, \"xiaozhi.config.json\");\n }\n\n /**\n * 获取配置文件格式\n */\n private getConfigFileFormat(filePath: string): \"json5\" | \"jsonc\" | \"json\" {\n if (filePath.endsWith(\".json5\")) {\n return \"json5\";\n }\n\n if (filePath.endsWith(\".jsonc\")) {\n return \"jsonc\";\n }\n\n return \"json\";\n }\n\n /**\n * 获取配置管理器单例实例\n */\n public static getInstance(): ConfigManager {\n if (!ConfigManager.instance) {\n ConfigManager.instance = new ConfigManager();\n }\n return ConfigManager.instance;\n }\n\n /**\n * 检查配置文件是否存在\n *\n * 按优先级检查配置文件是否存在:\n * 1. 环境变量 XIAOZHI_CONFIG_DIR 指定的目录\n * 2. 当前工作目录\n * 3. 用户家目录/.xiaozhi-client/\n */\n public configExists(): boolean {\n return ConfigResolver.resolveConfigPath() !== null;\n }\n\n /**\n * 初始化配置文件\n * 从 config.default.json 复制到 config.json\n * @param format 配置文件格式,默认为 json\n */\n public initConfig(format: \"json\" | \"json5\" | \"jsonc\" = \"json\"): void {\n if (!existsSync(this.defaultConfigPath)) {\n throw new Error(`默认配置模板文件不存在: ${this.defaultConfigPath}`);\n }\n\n // 检查是否已有任何格式的配置文件\n if (this.configExists()) {\n throw new Error(\"配置文件已存在,无需重复初始化\");\n }\n\n // 确定目标配置文件路径\n const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n const targetFileName = `xiaozhi.config.${format}`;\n const configPath = resolve(configDir, targetFileName);\n\n // 复制默认配置文件\n copyFileSync(this.defaultConfigPath, configPath);\n this.config = null; // 重置缓存\n this.json5Writer = null; // 重置 json5Writer 实例\n }\n\n /**\n * 加载配置文件\n */\n private loadConfig(): AppConfig {\n if (!this.configExists()) {\n const error = new Error(\n \"配置文件不存在,请先运行 xiaozhi init 初始化配置\"\n );\n this.emitEvent(\"config:error\", {\n error,\n operation: \"loadConfig\",\n });\n throw error;\n }\n\n try {\n const configPath = this.getConfigFilePath();\n this.currentConfigPath = configPath; // 记录当前使用的配置文件路径\n const configFileFormat = this.getConfigFileFormat(configPath);\n const rawConfigData = readFileSync(configPath, \"utf8\");\n\n // 移除可能存在的UTF-8 BOM字符(\\uFEFF)\n // BOM字符在某些编辑器中不可见,但会导致JSON解析失败\n // 这个过滤确保即使文件包含BOM字符也能正常解析\n const configData = rawConfigData.replace(/^\\uFEFF/, \"\");\n\n let config: AppConfig;\n\n // 根据文件格式使用相应的解析器\n switch (configFileFormat) {\n case \"json5\":\n // 使用 JSON5 解析配置对象,同时使用适配器保留注释信息\n config = parseJson5(configData) as AppConfig;\n // 创建适配器实例用于后续保存时保留注释\n this.json5Writer = createJson5Writer(configData);\n break;\n case \"jsonc\":\n // 使用 comment-json 解析 JSONC 格式,保留注释信息\n config = commentJson.parse(configData) as unknown as AppConfig;\n break;\n default:\n config = JSON.parse(configData) as AppConfig;\n break;\n }\n\n // 验证配置结构\n this.validateConfig(config);\n\n return config;\n } catch (error) {\n // 发射配置错误事件\n this.emitEvent(\"config:error\", {\n error: error instanceof Error ? error : new Error(String(error)),\n operation: \"loadConfig\",\n });\n if (error instanceof SyntaxError) {\n throw new Error(`配置文件格式错误: ${error.message}`);\n }\n throw error;\n }\n }\n\n /**\n * 验证配置文件结构\n */\n public validateConfig(config: unknown): void {\n if (!config || typeof config !== \"object\") {\n throw new Error(\"配置文件格式错误:根对象无效\");\n }\n\n const configObj = config as Record<string, unknown>;\n\n if (configObj.mcpEndpoint === undefined || configObj.mcpEndpoint === null) {\n throw new Error(\"配置文件格式错误:mcpEndpoint 字段无效\");\n }\n\n // 验证 mcpEndpoint 类型(字符串或字符串数组)\n if (typeof configObj.mcpEndpoint === \"string\") {\n // 空字符串是允许的,getMcpEndpoints 会返回空数组\n } else if (Array.isArray(configObj.mcpEndpoint)) {\n for (const endpoint of configObj.mcpEndpoint) {\n if (typeof endpoint !== \"string\" || endpoint.trim() === \"\") {\n throw new Error(\n \"配置文件格式错误:mcpEndpoint 数组中的每个元素必须是非空字符串\"\n );\n }\n }\n } else {\n throw new Error(\"配置文件格式错误:mcpEndpoint 必须是字符串或字符串数组\");\n }\n\n if (!configObj.mcpServers || typeof configObj.mcpServers !== \"object\") {\n throw new Error(\"配置文件格式错误:mcpServers 字段无效\");\n }\n\n // 验证每个 MCP 服务配置\n for (const [serverName, serverConfig] of Object.entries(\n configObj.mcpServers as Record<string, unknown>\n )) {\n if (!serverConfig || typeof serverConfig !== \"object\") {\n throw new Error(`配置文件格式错误:mcpServers.${serverName} 无效`);\n }\n\n // 基本验证:确保配置有效\n // 更详细的验证应该由调用方完成\n }\n }\n\n /**\n * 获取配置(只读)\n */\n public getConfig(): Readonly<AppConfig> {\n this.config = this.loadConfig();\n\n // 返回深度只读副本\n return JSON.parse(JSON.stringify(this.config));\n }\n\n /**\n * 获取可修改的配置对象(内部使用,保留注释信息)\n */\n private getMutableConfig(): AppConfig {\n if (!this.config) {\n this.config = this.loadConfig();\n }\n return this.config;\n }\n\n /**\n * 获取 MCP 端点(向后兼容)\n * @deprecated 使用 getMcpEndpoints() 获取所有端点\n */\n public getMcpEndpoint(): string {\n const config = this.getConfig();\n if (Array.isArray(config.mcpEndpoint)) {\n return config.mcpEndpoint[0] || \"\";\n }\n return config.mcpEndpoint;\n }\n\n /**\n * 获取所有 MCP 端点\n */\n public getMcpEndpoints(): string[] {\n const config = this.getConfig();\n if (Array.isArray(config.mcpEndpoint)) {\n return [...config.mcpEndpoint];\n }\n return config.mcpEndpoint ? [config.mcpEndpoint] : [];\n }\n\n /**\n * 获取 MCP 服务配置\n */\n public getMcpServers(): Readonly<Record<string, MCPServerConfig>> {\n const config = this.getConfig();\n return config.mcpServers;\n }\n\n /**\n * 获取 MCP 服务工具配置\n */\n public getMcpServerConfig(): Readonly<Record<string, MCPServerToolsConfig>> {\n const config = this.getConfig();\n return config.mcpServerConfig || {};\n }\n\n /**\n * 获取指定服务的工具配置\n */\n public getServerToolsConfig(\n serverName: string\n ): Readonly<Record<string, MCPToolConfig>> {\n const serverConfig = this.getMcpServerConfig();\n return serverConfig[serverName]?.tools || {};\n }\n\n /**\n * 检查工具是否启用\n */\n public isToolEnabled(serverName: string, toolName: string): boolean {\n const toolsConfig = this.getServerToolsConfig(serverName);\n const toolConfig = toolsConfig[toolName];\n return toolConfig?.enable !== false; // 默认启用\n }\n\n /**\n * 更新 MCP 端点(支持字符串或数组)\n */\n public updateMcpEndpoint(endpoint: string | string[]): void {\n if (Array.isArray(endpoint)) {\n for (const ep of endpoint) {\n if (!ep || typeof ep !== \"string\") {\n throw new Error(\"MCP 端点数组中的每个元素必须是非空字符串\");\n }\n }\n }\n\n const config = this.getMutableConfig();\n config.mcpEndpoint = endpoint;\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"endpoint\",\n timestamp: new Date(),\n });\n }\n\n /**\n * 添加 MCP 端点\n */\n public addMcpEndpoint(endpoint: string): void {\n if (!endpoint || typeof endpoint !== \"string\") {\n throw new Error(\"MCP 端点必须是非空字符串\");\n }\n\n const config = this.getMutableConfig();\n const currentEndpoints = this.getMcpEndpoints();\n\n // 检查是否已存在\n if (currentEndpoints.includes(endpoint)) {\n throw new Error(`MCP 端点 ${endpoint} 已存在`);\n }\n\n const newEndpoints = [...currentEndpoints, endpoint];\n config.mcpEndpoint = newEndpoints;\n this.saveConfig(config);\n }\n\n /**\n * 移除 MCP 端点\n */\n public removeMcpEndpoint(endpoint: string): void {\n if (!endpoint || typeof endpoint !== \"string\") {\n throw new Error(\"MCP 端点必须是非空字符串\");\n }\n\n const config = this.getMutableConfig();\n const currentEndpoints = this.getMcpEndpoints();\n\n // 检查是否存在\n const index = currentEndpoints.indexOf(endpoint);\n if (index === -1) {\n throw new Error(`MCP 端点 ${endpoint} 不存在`);\n }\n\n const newEndpoints = currentEndpoints.filter((ep) => ep !== endpoint);\n config.mcpEndpoint = newEndpoints;\n this.saveConfig(config);\n }\n\n /**\n * 更新 MCP 服务配置\n */\n public updateMcpServer(\n serverName: string,\n serverConfig: MCPServerConfig\n ): void {\n if (!serverName || typeof serverName !== \"string\") {\n throw new Error(\"服务名称必须是非空字符串\");\n }\n\n const config = this.getMutableConfig();\n // 直接修改配置对象以保留注释信息\n config.mcpServers[serverName] = serverConfig;\n this.saveConfig(config);\n }\n\n /**\n * 删除 MCP 服务配置\n */\n public removeMcpServer(serverName: string): void {\n if (!serverName || typeof serverName !== \"string\") {\n throw new Error(\"服务名称必须是非空字符串\");\n }\n\n const config = this.getMutableConfig();\n\n // 检查服务是否存在\n if (!config.mcpServers[serverName]) {\n throw new Error(`服务 ${serverName} 不存在`);\n }\n\n // 1. 清理 mcpServers 字段(现有逻辑)\n delete config.mcpServers[serverName];\n\n // 2. 清理 mcpServerConfig 字段(复用现有方法)\n if (config.mcpServerConfig?.[serverName]) {\n delete config.mcpServerConfig[serverName];\n }\n\n // 3. 清理 customMCP 字段中相关的工具定义\n if (config.customMCP?.tools) {\n // 查找与该服务相关的 CustomMCP 工具\n const relatedTools = config.customMCP.tools.filter(\n (tool) =>\n tool.handler?.type === \"mcp\" &&\n tool.handler.config?.serviceName === serverName\n );\n\n // 移除相关工具\n for (const tool of relatedTools) {\n const toolIndex = config.customMCP.tools.findIndex(\n (t) => t.name === tool.name\n );\n if (toolIndex !== -1) {\n config.customMCP.tools.splice(toolIndex, 1);\n }\n }\n\n // 如果没有工具了,可以清理整个 customMCP 对象\n if (config.customMCP.tools.length === 0) {\n config.customMCP = undefined;\n }\n }\n\n // 4. 保存配置(单次原子性操作)\n this.saveConfig(config);\n\n // 5. 发射配置更新事件,通知 CustomMCPHandler 重新初始化\n this.emitEvent(\"config:updated\", {\n type: \"customMCP\",\n timestamp: new Date(),\n });\n\n // 记录清理结果\n console.log(\"成功移除 MCP 服务\", { serverName });\n }\n\n /**\n * 批量更新配置(由 Handler 调用)\n */\n public updateConfig(newConfig: Partial<AppConfig>): void {\n const config = this.getMutableConfig();\n\n // 更新 MCP 端点\n if (newConfig.mcpEndpoint !== undefined) {\n config.mcpEndpoint = newConfig.mcpEndpoint;\n }\n\n // 更新 MCP 服务\n if (newConfig.mcpServers) {\n const currentServers = { ...config.mcpServers };\n for (const [name, serverConfig] of Object.entries(newConfig.mcpServers)) {\n config.mcpServers[name] = serverConfig;\n }\n // 删除不存在的服务\n for (const name of Object.keys(currentServers)) {\n if (!(name in newConfig.mcpServers)) {\n delete config.mcpServers[name];\n // 同时清理工具配置\n if (config.mcpServerConfig?.[name]) {\n delete config.mcpServerConfig[name];\n }\n }\n }\n }\n\n // 更新连接配置\n if (newConfig.connection) {\n if (!config.connection) {\n config.connection = {};\n }\n Object.assign(config.connection, newConfig.connection);\n }\n\n // 更新 ModelScope 配置\n if (newConfig.modelscope) {\n if (!config.modelscope) {\n config.modelscope = {};\n }\n Object.assign(config.modelscope, newConfig.modelscope);\n }\n\n // 更新 Web UI 配置\n if (newConfig.webUI) {\n if (!config.webUI) {\n config.webUI = {};\n }\n Object.assign(config.webUI, newConfig.webUI);\n }\n\n // 更新服务工具配置\n if (newConfig.mcpServerConfig) {\n for (const [serverName, toolsConfig] of Object.entries(\n newConfig.mcpServerConfig\n )) {\n if (config.mcpServerConfig?.[serverName]) {\n config.mcpServerConfig[serverName] = toolsConfig;\n }\n }\n }\n\n // 更新平台配置\n if (newConfig.platforms) {\n for (const [platformName, platformConfig] of Object.entries(\n newConfig.platforms\n )) {\n if (!config.platforms) {\n config.platforms = {};\n }\n config.platforms[platformName] = platformConfig;\n }\n }\n\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"config\",\n timestamp: new Date(),\n });\n }\n\n /**\n * 更新服务工具配置\n */\n public updateServerToolsConfig(\n serverName: string,\n toolsConfig: Record<string, MCPToolConfig>\n ): void {\n const config = this.getMutableConfig();\n\n // 确保 mcpServerConfig 存在\n if (!config.mcpServerConfig) {\n config.mcpServerConfig = {};\n }\n\n // 如果 toolsConfig 为空对象,则删除该服务的配置\n if (Object.keys(toolsConfig).length === 0) {\n delete config.mcpServerConfig[serverName];\n } else {\n // 更新指定服务的工具配置\n config.mcpServerConfig[serverName] = {\n tools: toolsConfig,\n };\n }\n\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"serverTools\",\n serviceName: serverName,\n timestamp: new Date(),\n });\n }\n\n /**\n * 删除指定服务器的工具配置\n */\n public removeServerToolsConfig(serverName: string): void {\n const config = this.getConfig();\n const newConfig = { ...config };\n\n // 确保 mcpServerConfig 存在\n if (newConfig.mcpServerConfig) {\n // 删除指定服务的工具配置\n delete newConfig.mcpServerConfig[serverName];\n this.saveConfig(newConfig);\n }\n }\n\n /**\n * 清理无效的服务器工具配置\n * 删除在 mcpServerConfig 中存在但在 mcpServers 中不存在的服务配置\n */\n public cleanupInvalidServerToolsConfig(): void {\n const config = this.getMutableConfig();\n\n // 如果没有 mcpServerConfig,无需清理\n if (!config.mcpServerConfig) {\n return;\n }\n\n const validServerNames = Object.keys(config.mcpServers);\n const configuredServerNames = Object.keys(config.mcpServerConfig);\n\n // 找出需要清理的服务名称\n const invalidServerNames = configuredServerNames.filter(\n (serverName) => !validServerNames.includes(serverName)\n );\n\n if (invalidServerNames.length > 0) {\n // 删除无效的服务配置\n for (const serverName of invalidServerNames) {\n delete config.mcpServerConfig[serverName];\n }\n\n this.saveConfig(config);\n\n console.log(\"已清理无效的服务工具配置\", {\n count: invalidServerNames.length,\n serverNames: invalidServerNames,\n });\n }\n }\n\n /**\n * 设置工具启用状态\n */\n public setToolEnabled(\n serverName: string,\n toolName: string,\n enabled: boolean,\n description?: string\n ): void {\n const config = this.getMutableConfig();\n\n // 确保 mcpServerConfig 存在\n if (!config.mcpServerConfig) {\n config.mcpServerConfig = {};\n }\n\n // 确保服务配置存在\n if (!config.mcpServerConfig[serverName]) {\n config.mcpServerConfig[serverName] = { tools: {} };\n }\n\n // 更新工具配置\n config.mcpServerConfig[serverName].tools[toolName] = {\n ...config.mcpServerConfig[serverName].tools[toolName],\n enable: enabled,\n ...(description && { description }),\n };\n\n this.saveConfig(config);\n }\n\n /**\n * 保存配置到文件\n * 保存到原始配置文件路径,保持文件格式一致性\n */\n private saveConfig(config: AppConfig): void {\n try {\n // 验证配置\n this.validateConfig(config);\n\n // 确定保存路径 - 优先使用当前配置文件路径,否则使用默认路径\n let configPath: string;\n if (this.currentConfigPath) {\n configPath = this.currentConfigPath;\n } else {\n // 如果没有当前路径,使用 getConfigFilePath 获取\n configPath = this.getConfigFilePath();\n this.currentConfigPath = configPath;\n }\n\n // 根据文件格式选择序列化方法\n const configFileFormat = this.getConfigFileFormat(configPath);\n let configContent: string;\n\n switch (configFileFormat) {\n case \"json5\":\n // 对于 JSON5 格式,使用适配器保留注释\n try {\n if (this.json5Writer) {\n // 使用适配器更新配置并保留注释\n this.json5Writer.write(config);\n configContent = this.json5Writer.toSource();\n } else {\n // 如果没有适配器实例,回退到 comment-json 序列化\n console.warn(\"没有 JSON5 适配器实例,使用 comment-json 序列化\");\n configContent = commentJson.stringify(config, null, 2);\n }\n } catch (json5Error) {\n // 如果适配器序列化失败,回退到 comment-json 序列化\n console.warn(\n \"使用 JSON5 适配器保存失败,回退到 comment-json 序列化:\",\n json5Error\n );\n configContent = commentJson.stringify(config, null, 2);\n }\n break;\n case \"jsonc\":\n // 对于 JSONC 格式,使用 comment-json 库保留注释\n try {\n // 直接使用 comment-json 的 stringify 方法\n // 如果 config 是通过 comment-json.parse 解析的,注释信息会被保留\n configContent = commentJson.stringify(config, null, 2);\n } catch (commentJsonError) {\n // 如果 comment-json 序列化失败,回退到标准 JSON\n console.warn(\n \"使用 comment-json 保存失败,回退到标准 JSON 格式:\",\n commentJsonError\n );\n configContent = JSON.stringify(config, null, 2);\n }\n break;\n default:\n configContent = JSON.stringify(config, null, 2);\n break;\n }\n\n // 保存到文件\n writeFileSync(configPath, configContent, \"utf8\");\n\n // 更新缓存\n this.config = config;\n\n console.log(\"配置保存成功\");\n\n // 通知 Web 界面配置已更新(如果 Web 服务器正在运行)\n this.notifyConfigUpdate(config);\n } catch (error) {\n // 发射配置错误事件\n this.emitEvent(\"config:error\", {\n error: error instanceof Error ? error : new Error(String(error)),\n operation: \"saveConfig\",\n });\n throw new Error(\n `保存配置失败: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n /**\n * 重新加载配置(清除缓存)\n */\n public reloadConfig(): void {\n this.config = null;\n this.currentConfigPath = null; // 清除配置文件路径缓存\n this.json5Writer = null; // 清除 json5Writer 实例\n }\n\n /**\n * 获取配置文件路径\n */\n public getConfigPath(): string {\n return this.getConfigFilePath();\n }\n\n /**\n * 获取默认配置文件路径\n */\n public getDefaultConfigPath(): string {\n return this.defaultConfigPath;\n }\n\n /**\n * 获取连接配置(包含默认值)\n */\n public getConnectionConfig(): Required<ConnectionConfig> {\n const config = this.getConfig();\n const connectionConfig = config.connection || {};\n\n return {\n heartbeatInterval:\n connectionConfig.heartbeatInterval ??\n DEFAULT_CONNECTION_CONFIG.heartbeatInterval,\n heartbeatTimeout:\n connectionConfig.heartbeatTimeout ??\n DEFAULT_CONNECTION_CONFIG.heartbeatTimeout,\n reconnectInterval:\n connectionConfig.reconnectInterval ??\n DEFAULT_CONNECTION_CONFIG.reconnectInterval,\n };\n }\n\n /**\n * 获取心跳检测间隔(毫秒)\n */\n public getHeartbeatInterval(): number {\n return this.getConnectionConfig().heartbeatInterval;\n }\n\n /**\n * 获取心跳超时时间(毫秒)\n */\n public getHeartbeatTimeout(): number {\n return this.getConnectionConfig().heartbeatTimeout;\n }\n\n /**\n * 获取重连间隔(毫秒)\n */\n public getReconnectInterval(): number {\n return this.getConnectionConfig().reconnectInterval;\n }\n\n /**\n * 更新连接配置\n */\n public updateConnectionConfig(\n connectionConfig: Partial<ConnectionConfig>\n ): void {\n const config = this.getMutableConfig();\n\n // 确保 connection 对象存在\n if (!config.connection) {\n config.connection = {};\n }\n\n // 直接修改现有的 connection 对象以保留注释\n Object.assign(config.connection, connectionConfig);\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"connection\",\n timestamp: new Date(),\n });\n }\n\n /**\n * 更新工具使用统计信息(MCP 服务工具)\n * @param serverName 服务名称\n * @param toolName 工具名称\n * @param callTime 调用时间(ISO 8601 格式)\n */\n public async updateToolUsageStats(\n serverName: string,\n toolName: string,\n callTime: string\n ): Promise<void>;\n\n /**\n * 更新工具使用统计信息(CustomMCP 工具)\n * @param toolName 工具名称(customMCP 工具名称)\n * @param incrementUsageCount 是否增加使用计数,默认为 true\n */\n public async updateToolUsageStats(\n toolName: string,\n incrementUsageCount?: boolean\n ): Promise<void>;\n\n /**\n * 更新工具使用统计信息的实现\n */\n public async updateToolUsageStats(\n arg1: string,\n arg2: string | boolean | undefined,\n arg3?: string\n ): Promise<void> {\n try {\n // 判断参数类型来区分不同的重载\n if (typeof arg2 === \"string\" && arg3) {\n // 三个参数的情况:updateToolUsageStats(serverName, toolName, callTime)\n const serverName = arg1;\n const toolName = arg2;\n const callTime = arg3;\n\n // 双写机制:同时更新 mcpServerConfig 和 customMCP 中的统计信息\n await Promise.all([\n this._updateMCPServerToolStats(serverName, toolName, callTime),\n this.updateCustomMCPToolStats(serverName, toolName, callTime),\n ]);\n\n console.log(\"工具使用统计已更新\", { serverName, toolName });\n } else {\n // 两个参数的情况:updateToolUsageStats(toolName, incrementUsageCount)\n const toolName = arg1;\n const incrementUsageCount = arg2 as boolean;\n const callTime = new Date().toISOString();\n\n // 只更新 customMCP 中的统计信息\n await this.updateCustomMCPToolStats(\n toolName,\n callTime,\n incrementUsageCount\n );\n\n console.log(\"CustomMCP 工具使用统计已更新\", { toolName });\n }\n } catch (error) {\n // 错误不应该影响主要的工具调用流程\n if (typeof arg2 === \"string\" && arg3) {\n const serverName = arg1;\n const toolName = arg2;\n console.error(\"更新工具使用统计失败\", { serverName, toolName, error });\n } else {\n const toolName = arg1;\n console.error(\"更新 CustomMCP 工具使用统计失败\", { toolName, error });\n }\n }\n }\n\n /**\n * 更新 MCP 服务工具统计信息(重载方法)\n * @param serviceName 服务名称\n * @param toolName 工具名称\n * @param callTime 调用时间(ISO 8601 格式)\n * @param incrementUsageCount 是否增加使用计数,默认为 true\n */\n public async updateMCPServerToolStats(\n serviceName: string,\n toolName: string,\n callTime: string,\n incrementUsageCount = true\n ): Promise<void> {\n await this._updateMCPServerToolStats(\n serviceName,\n toolName,\n callTime,\n incrementUsageCount\n );\n }\n\n /**\n * 设置心跳检测间隔\n */\n public setHeartbeatInterval(interval: number): void {\n if (interval <= 0) {\n throw new Error(\"心跳检测间隔必须大于0\");\n }\n this.updateConnectionConfig({ heartbeatInterval: interval });\n }\n\n /**\n * 设置心跳超时时间\n */\n public setHeartbeatTimeout(timeout: number): void {\n if (timeout <= 0) {\n throw new Error(\"心跳超时时间必须大于0\");\n }\n this.updateConnectionConfig({ heartbeatTimeout: timeout });\n }\n\n /**\n * 设置重连间隔\n */\n public setReconnectInterval(interval: number): void {\n if (interval <= 0) {\n throw new Error(\"重连间隔必须大于0\");\n }\n this.updateConnectionConfig({ reconnectInterval: interval });\n }\n\n /**\n * 获取 ModelScope 配置\n */\n public getModelScopeConfig(): Readonly<ModelScopeConfig> {\n const config = this.getConfig();\n return config.modelscope || {};\n }\n\n /**\n * 获取 ModelScope API Key\n * 优先从配置文件读取,其次从环境变量读取\n */\n public getModelScopeApiKey(): string | undefined {\n const modelScopeConfig = this.getModelScopeConfig();\n return modelScopeConfig.apiKey || process.env.MODELSCOPE_API_TOKEN;\n }\n\n /**\n * 更新 ModelScope 配置\n */\n public updateModelScopeConfig(\n modelScopeConfig: Partial<ModelScopeConfig>\n ): void {\n const config = this.getMutableConfig();\n\n // 确保 modelscope 对象存在\n if (!config.modelscope) {\n config.modelscope = {};\n }\n\n // 直接修改现有的 modelscope 对象以保留注释\n Object.assign(config.modelscope, modelScopeConfig);\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"modelscope\",\n timestamp: new Date(),\n });\n }\n\n /**\n * 设置 ModelScope API Key\n */\n public setModelScopeApiKey(apiKey: string): void {\n if (!apiKey || typeof apiKey !== \"string\") {\n throw new Error(\"API Key 必须是非空字符串\");\n }\n this.updateModelScopeConfig({ apiKey });\n }\n\n /**\n * 获取 customMCP 配置\n */\n public getCustomMCPConfig(): CustomMCPConfig | null {\n const config = this.getConfig();\n return config.customMCP || null;\n }\n\n /**\n * 获取 customMCP 工具列表\n */\n public getCustomMCPTools(): CustomMCPTool[] {\n const customMCPConfig = this.getCustomMCPConfig();\n if (!customMCPConfig || !customMCPConfig.tools) {\n return [];\n }\n\n return customMCPConfig.tools;\n }\n\n /**\n * 验证 customMCP 工具配置\n */\n public validateCustomMCPTools(tools: CustomMCPTool[]): boolean {\n if (!Array.isArray(tools)) {\n return false;\n }\n\n for (const tool of tools) {\n // 检查必需字段\n if (!tool.name || typeof tool.name !== \"string\") {\n console.warn(\"CustomMCP 工具缺少有效的 name 字段\", { tool });\n return false;\n }\n\n if (!tool.description || typeof tool.description !== \"string\") {\n console.warn(\"CustomMCP 工具缺少有效的 description 字段\", {\n toolName: tool.name,\n });\n return false;\n }\n\n if (!tool.inputSchema || typeof tool.inputSchema !== \"object\") {\n console.warn(\"CustomMCP 工具缺少有效的 inputSchema 字段\", {\n toolName: tool.name,\n });\n return false;\n }\n\n if (!tool.handler || typeof tool.handler !== \"object\") {\n console.warn(\"CustomMCP 工具缺少有效的 handler 字段\", {\n toolName: tool.name,\n });\n return false;\n }\n\n // 检查 handler 类型\n if (\n ![\"proxy\", \"function\", \"http\", \"script\", \"chain\", \"mcp\"].includes(\n tool.handler.type\n )\n ) {\n console.warn(\"CustomMCP 工具的 handler.type 类型无效\", {\n toolName: tool.name,\n type: tool.handler.type,\n });\n return false;\n }\n\n // 根据处理器类型进行特定验证\n if (!this.validateHandlerConfig(tool.name, tool.handler)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * 验证处理器配置\n */\n private validateHandlerConfig(\n toolName: string,\n handler: HandlerConfig\n ): boolean {\n switch (handler.type) {\n case \"proxy\":\n return this.validateProxyHandler(toolName, handler);\n case \"http\":\n return this.validateHttpHandler(toolName, handler);\n case \"function\":\n return this.validateFunctionHandler(toolName, handler);\n case \"script\":\n return this.validateScriptHandler(toolName, handler);\n case \"chain\":\n return this.validateChainHandler(toolName, handler);\n case \"mcp\":\n return this.validateMCPHandler(toolName, handler);\n default:\n console.warn(\"CustomMCP 工具使用了未知的处理器类型\", {\n toolName,\n handlerType: (handler as HandlerConfig).type,\n });\n return false;\n }\n }\n\n /**\n * 验证代理处理器配置\n */\n private validateProxyHandler(\n toolName: string,\n handler: ProxyHandlerConfig\n ): boolean {\n if (!handler.platform) {\n console.warn(\"CustomMCP 工具的 proxy 处理器缺少 platform 字段\", {\n toolName,\n });\n return false;\n }\n\n if (![\"coze\", \"openai\", \"anthropic\", \"custom\"].includes(handler.platform)) {\n console.warn(\"CustomMCP 工具的 proxy 处理器使用了不支持的平台\", {\n toolName,\n platform: handler.platform,\n });\n return false;\n }\n\n if (!handler.config || typeof handler.config !== \"object\") {\n console.warn(\"CustomMCP 工具的 proxy 处理器缺少 config 字段\", {\n toolName,\n });\n return false;\n }\n\n // Coze 平台特定验证\n if (handler.platform === \"coze\") {\n if (!handler.config.workflow_id && !handler.config.bot_id) {\n console.warn(\n \"CustomMCP 工具的 Coze 处理器必须提供 workflow_id 或 bot_id\",\n { toolName }\n );\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * 验证 HTTP 处理器配置\n */\n private validateHttpHandler(\n toolName: string,\n handler: HttpHandlerConfig\n ): boolean {\n if (!handler.url || typeof handler.url !== \"string\") {\n console.warn(\"CustomMCP 工具的 http 处理器缺少有效的 url 字段\", {\n toolName,\n });\n return false;\n }\n\n try {\n new URL(handler.url);\n } catch {\n console.warn(\"CustomMCP 工具的 http 处理器 url 格式无效\", {\n toolName,\n url: handler.url,\n });\n return false;\n }\n\n if (\n handler.method &&\n ![\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\"].includes(handler.method)\n ) {\n console.warn(\"CustomMCP 工具的 http 处理器使用了不支持的 HTTP 方法\", {\n toolName,\n method: handler.method,\n });\n return false;\n }\n\n return true;\n }\n\n /**\n * 验证函数处理器配置\n */\n private validateFunctionHandler(\n toolName: string,\n handler: FunctionHandlerConfig\n ): boolean {\n if (!handler.module || typeof handler.module !== \"string\") {\n console.warn(\"CustomMCP 工具的 function 处理器缺少有效的 module 字段\", {\n toolName,\n });\n return false;\n }\n\n if (!handler.function || typeof handler.function !== \"string\") {\n console.warn(\"CustomMCP 工具的 function 处理器缺少有效的 function 字段\", {\n toolName,\n });\n return false;\n }\n\n return true;\n }\n\n /**\n * 验证脚本处理器配置\n */\n private validateScriptHandler(\n toolName: string,\n handler: ScriptHandlerConfig\n ): boolean {\n if (!handler.script || typeof handler.script !== \"string\") {\n console.warn(\"CustomMCP 工具的 script 处理器缺少有效的 script 字段\", {\n toolName,\n });\n return false;\n }\n\n if (\n handler.interpreter &&\n ![\"node\", \"python\", \"bash\"].includes(handler.interpreter)\n ) {\n console.warn(\"CustomMCP 工具的 script 处理器使用了不支持的解释器\", {\n toolName,\n interpreter: handler.interpreter,\n });\n return false;\n }\n\n return true;\n }\n\n /**\n * 验证链式处理器配置\n */\n private validateChainHandler(\n toolName: string,\n handler: ChainHandlerConfig\n ): boolean {\n if (\n !handler.tools ||\n !Array.isArray(handler.tools) ||\n handler.tools.length === 0\n ) {\n console.warn(\"CustomMCP 工具的 chain 处理器缺少有效的 tools 数组\", {\n toolName,\n });\n return false;\n }\n\n if (![\"sequential\", \"parallel\"].includes(handler.mode)) {\n console.warn(\"CustomMCP 工具的 chain 处理器使用了不支持的执行模式\", {\n toolName,\n mode: handler.mode,\n });\n return false;\n }\n\n if (![\"stop\", \"continue\", \"retry\"].includes(handler.error_handling)) {\n console.warn(\"CustomMCP 工具的 chain 处理器使用了不支持的错误处理策略\", {\n toolName,\n errorHandling: handler.error_handling,\n });\n return false;\n }\n\n return true;\n }\n\n /**\n * 验证 MCP 处理器配置\n */\n private validateMCPHandler(\n toolName: string,\n handler: MCPHandlerConfig\n ): boolean {\n if (!handler.config || typeof handler.config !== \"object\") {\n console.warn(\"CustomMCP 工具的 mcp 处理器缺少 config 字段\", { toolName });\n return false;\n }\n\n if (\n !handler.config.serviceName ||\n typeof handler.config.serviceName !== \"string\"\n ) {\n console.warn(\"CustomMCP 工具的 mcp 处理器缺少有效的 serviceName\", {\n toolName,\n });\n return false;\n }\n\n if (\n !handler.config.toolName ||\n typeof handler.config.toolName !== \"string\"\n ) {\n console.warn(\"CustomMCP 工具的 mcp 处理器缺少有效的 toolName\", {\n toolName,\n });\n return false;\n }\n\n return true;\n }\n\n /**\n * 检查是否配置了有效的 customMCP 工具\n */\n public hasValidCustomMCPTools(): boolean {\n try {\n const tools = this.getCustomMCPTools();\n if (tools.length === 0) {\n return false;\n }\n\n return this.validateCustomMCPTools(tools);\n } catch (error) {\n console.error(\"检查 customMCP 工具配置时出错\", { error });\n return false;\n }\n }\n\n /**\n * 添加自定义 MCP 工具\n */\n public addCustomMCPTool(tool: CustomMCPTool): void {\n if (!tool || typeof tool !== \"object\") {\n throw new Error(\"工具配置不能为空\");\n }\n\n const config = this.getMutableConfig();\n\n // 确保 customMCP 配置存在\n if (!config.customMCP) {\n config.customMCP = { tools: [] };\n }\n\n // 检查工具名称是否已存在\n const existingTool = config.customMCP.tools.find(\n (t) => t.name === tool.name\n );\n if (existingTool) {\n throw new Error(`工具 \"${tool.name}\" 已存在`);\n }\n\n // 验证工具配置\n if (!this.validateCustomMCPTools([tool])) {\n throw new Error(\"工具配置验证失败\");\n }\n\n // 添加工具\n config.customMCP.tools.unshift(tool);\n this.saveConfig(config);\n\n console.log(\"成功添加自定义 MCP 工具\", { toolName: tool.name });\n }\n\n /**\n * 批量添加自定义 MCP 工具\n * @param tools 要添加的工具数组\n */\n public async addCustomMCPTools(tools: CustomMCPTool[]): Promise<void> {\n if (!Array.isArray(tools)) {\n throw new Error(\"工具配置必须是数组\");\n }\n\n if (tools.length === 0) {\n return; // 空数组,无需处理\n }\n\n const config = this.getMutableConfig();\n\n // 确保 customMCP 配置存在\n if (!config.customMCP) {\n config.customMCP = { tools: [] };\n }\n\n // 添加新工具,避免重复\n const existingNames = new Set(\n config.customMCP.tools.map((tool) => tool.name)\n );\n const newTools = tools.filter((tool) => !existingNames.has(tool.name));\n\n if (newTools.length > 0) {\n // 验证新工具配置\n if (!this.validateCustomMCPTools(newTools)) {\n throw new Error(\"工具配置验证失败\");\n }\n\n // 添加工具\n config.customMCP.tools.push(...newTools);\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"customMCP\",\n timestamp: new Date(),\n });\n\n console.log(\"成功批量添加自定义 MCP 工具\", {\n count: newTools.length,\n toolNames: newTools.map((t) => t.name),\n });\n }\n }\n\n /**\n * 删除自定义 MCP 工具\n */\n public removeCustomMCPTool(toolName: string): void {\n if (!toolName || typeof toolName !== \"string\") {\n throw new Error(\"工具名称不能为空\");\n }\n\n const config = this.getMutableConfig();\n\n if (!config.customMCP || !config.customMCP.tools) {\n throw new Error(\"未配置自定义 MCP 工具\");\n }\n\n const toolIndex = config.customMCP.tools.findIndex(\n (t) => t.name === toolName\n );\n if (toolIndex === -1) {\n throw new Error(`工具 \"${toolName}\" 不存在`);\n }\n\n // 删除工具\n config.customMCP.tools.splice(toolIndex, 1);\n this.saveConfig(config);\n\n console.log(\"成功删除自定义 MCP 工具\", { toolName });\n }\n\n /**\n * 更新单个自定义 MCP 工具配置\n * @param toolName 工具名称\n * @param updatedTool 更新后的工具配置\n */\n public updateCustomMCPTool(\n toolName: string,\n updatedTool: CustomMCPTool\n ): void {\n if (!toolName || typeof toolName !== \"string\") {\n throw new Error(\"工具名称不能为空\");\n }\n if (!updatedTool || typeof updatedTool !== \"object\") {\n throw new Error(\"更新后的工具配置不能为空\");\n }\n\n const config = this.getMutableConfig();\n\n if (!config.customMCP || !config.customMCP.tools) {\n throw new Error(\"未配置自定义 MCP 工具\");\n }\n\n const toolIndex = config.customMCP.tools.findIndex(\n (t) => t.name === toolName\n );\n if (toolIndex === -1) {\n throw new Error(`工具 \"${toolName}\" 不存在`);\n }\n\n // 验证更新后的工具配置\n if (!this.validateCustomMCPTools([updatedTool])) {\n throw new Error(\"更新后的工具配置验证失败\");\n }\n\n // 更新工具配置\n config.customMCP.tools[toolIndex] = updatedTool;\n this.saveConfig(config);\n\n console.log(\"成功更新自定义 MCP 工具\", { toolName });\n }\n\n /**\n * 更新自定义 MCP 工具配置\n */\n public updateCustomMCPTools(tools: CustomMCPTool[]): void {\n if (!Array.isArray(tools)) {\n throw new Error(\"工具配置必须是数组\");\n }\n\n // 验证工具配置\n if (!this.validateCustomMCPTools(tools)) {\n throw new Error(\"工具配置验证失败\");\n }\n\n const config = this.getMutableConfig();\n\n // 确保 customMCP 配置存在\n if (!config.customMCP) {\n config.customMCP = { tools: [] };\n }\n\n config.customMCP.tools = tools;\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"customMCP\",\n timestamp: new Date(),\n });\n\n console.log(\"成功更新自定义 MCP 工具配置\", { count: tools.length });\n }\n\n /**\n * 获取 Web UI 配置\n */\n public getWebUIConfig(): Readonly<WebUIConfig> {\n const config = this.getConfig();\n return config.webUI || {};\n }\n\n /**\n * 获取 Web UI 端口号\n */\n public getWebUIPort(): number {\n const webUIConfig = this.getWebUIConfig();\n return webUIConfig.port ?? 9999; // 默认端口 9999\n }\n\n /**\n * 通知 Web 界面配置已更新\n * 如果 Web 服务器正在运行,通过 WebSocket 广播配置更新\n */\n private notifyConfigUpdate(config: AppConfig): void {\n try {\n // 检查是否有全局的 webServer 实例(当使用 --ui 参数启动时会设置)\n const webServer = (\n global as typeof global & { __webServer?: WebServerInstance }\n ).__webServer;\n if (webServer && typeof webServer.broadcastConfigUpdate === \"function\") {\n // 调用 webServer 的 broadcastConfigUpdate 方法来通知所有连接的客户端\n webServer.broadcastConfigUpdate(config);\n console.log(\"已通过 WebSocket 广播配置更新\");\n }\n } catch (error) {\n // 静默处理错误,不影响配置保存的主要功能\n console.warn(\n \"通知 Web 界面配置更新失败:\",\n error instanceof Error ? error.message : String(error)\n );\n }\n }\n\n /**\n * 更新 Web UI 配置\n */\n public updateWebUIConfig(webUIConfig: Partial<WebUIConfig>): void {\n const config = this.getMutableConfig();\n\n // 确保 webUI 对象存在\n if (!config.webUI) {\n config.webUI = {};\n }\n\n // 直接修改现有的 webUI 对象以保留注释\n Object.assign(config.webUI, webUIConfig);\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"webui\",\n timestamp: new Date(),\n });\n }\n\n /**\n * 设置 Web UI 端口号\n */\n public setWebUIPort(port: number): void {\n if (!Number.isInteger(port) || port <= 0 || port > 65535) {\n throw new Error(\"端口号必须是 1-65535 之间的整数\");\n }\n this.updateWebUIConfig({ port });\n }\n\n public updatePlatformConfig(\n platformName: string,\n platformConfig: PlatformConfig\n ): void {\n const config = this.getMutableConfig();\n if (!config.platforms) {\n config.platforms = {};\n }\n config.platforms[platformName] = platformConfig;\n // 注意:Web UI 可能需要刷新才能看到更新后的数据\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"platform\",\n platformName,\n timestamp: new Date(),\n });\n }\n\n /**\n * 获取扣子平台配置\n */\n public getCozePlatformConfig(): CozePlatformConfig | null {\n const config = this.getConfig();\n const cozeConfig = config.platforms?.coze;\n\n if (!cozeConfig || !cozeConfig.token) {\n return null;\n }\n\n return {\n token: cozeConfig.token,\n };\n }\n\n /**\n * 获取扣子 API Token\n */\n public getCozeToken(): string | null {\n const cozeConfig = this.getCozePlatformConfig();\n return cozeConfig?.token || null;\n }\n\n /**\n * 设置扣子平台配置\n */\n public setCozePlatformConfig(config: CozePlatformConfig): void {\n if (\n !config.token ||\n typeof config.token !== \"string\" ||\n config.token.trim() === \"\"\n ) {\n throw new Error(\"扣子 API Token 不能为空\");\n }\n\n this.updatePlatformConfig(\"coze\", {\n token: config.token.trim(),\n });\n }\n\n /**\n * 检查扣子平台配置是否有效\n */\n public isCozeConfigValid(): boolean {\n const cozeConfig = this.getCozePlatformConfig();\n return (\n cozeConfig !== null &&\n typeof cozeConfig.token === \"string\" &&\n cozeConfig.token.trim() !== \"\"\n );\n }\n\n /**\n * 更新 mcpServerConfig 中的工具使用统计信息(内部实现)\n * @param serverName 服务名称\n * @param toolName 工具名称\n * @param callTime 调用时间(ISO 8601 格式)\n * @param incrementUsageCount 是否增加使用计数\n * @private\n */\n private async _updateMCPServerToolStats(\n serverName: string,\n toolName: string,\n callTime: string,\n incrementUsageCount = true\n ): Promise<void> {\n const config = this.getMutableConfig();\n\n // 确保 mcpServerConfig 存在\n if (!config.mcpServerConfig) {\n config.mcpServerConfig = {};\n }\n\n // 确保服务配置存在\n if (!config.mcpServerConfig[serverName]) {\n config.mcpServerConfig[serverName] = { tools: {} };\n }\n\n // 确保工具配置存在\n if (!config.mcpServerConfig[serverName].tools[toolName]) {\n config.mcpServerConfig[serverName].tools[toolName] = {\n enable: true, // 默认启用\n };\n }\n\n const toolConfig = config.mcpServerConfig[serverName].tools[toolName];\n const currentUsageCount = toolConfig.usageCount || 0;\n const currentLastUsedTime = toolConfig.lastUsedTime;\n\n // 根据参数决定是否更新使用次数\n if (incrementUsageCount) {\n toolConfig.usageCount = currentUsageCount + 1;\n }\n\n // 时间校验:只有新时间晚于现有时间才更新 lastUsedTime\n if (\n !currentLastUsedTime ||\n new Date(callTime) > new Date(currentLastUsedTime)\n ) {\n // 使用 dayjs 格式化时间为更易读的格式\n toolConfig.lastUsedTime = dayjs(callTime).format(\"YYYY-MM-DD HH:mm:ss\");\n }\n\n // 保存配置\n this.saveConfig(config);\n }\n\n /**\n * 更新 customMCP 中的工具使用统计信息(服务名+工具名版本)\n * @param serverName 服务名称\n * @param toolName 工具名称\n * @param callTime 调用时间(ISO 8601 格式)\n * @private\n */\n private async updateCustomMCPToolStats(\n serverName: string,\n toolName: string,\n callTime: string\n ): Promise<void>;\n\n /**\n * 更新 customMCP 中的工具使用统计信息(工具名版本)\n * @param toolName 工具名称(customMCP 工具名称)\n * @param callTime 调用时间(ISO 8601 格式)\n * @param incrementUsageCount 是否增加使用计数,默认为 true\n * @private\n */\n private async updateCustomMCPToolStats(\n toolName: string,\n callTime: string,\n incrementUsageCount?: boolean\n ): Promise<void>;\n\n /**\n * 更新 customMCP 工具使用统计信息的实现\n * @private\n */\n private async updateCustomMCPToolStats(\n arg1: string,\n arg2: string,\n arg3?: string | boolean\n ): Promise<void> {\n try {\n let toolName: string;\n let callTime: string;\n let incrementUsageCount = true;\n let logPrefix: string;\n\n // 判断参数类型来区分不同的重载\n if (typeof arg3 === \"string\") {\n // 三个字符串参数的情况:updateCustomMCPToolStats(serverName, toolName, callTime)\n const serverName = arg1;\n toolName = `${serverName}__${arg2}`;\n callTime = arg3;\n logPrefix = `${serverName}/${arg2}`;\n } else {\n // 两个或三个参数的情况:updateCustomMCPToolStats(toolName, callTime, incrementUsageCount?)\n toolName = arg1;\n callTime = arg2;\n incrementUsageCount = (arg3 as boolean) || true;\n logPrefix = toolName;\n }\n\n const customTools = this.getCustomMCPTools();\n const toolIndex = customTools.findIndex((tool) => tool.name === toolName);\n\n if (toolIndex === -1) {\n // 如果 customMCP 中没有对应的工具,跳过更新\n return;\n }\n\n const updatedTools = [...customTools];\n const tool = updatedTools[toolIndex];\n\n // 确保 stats 对象存在\n if (!tool.stats) {\n tool.stats = {};\n }\n\n const currentUsageCount = tool.stats.usageCount || 0;\n const currentLastUsedTime = tool.stats.lastUsedTime;\n\n // 根据参数决定是否更新使用次数\n if (incrementUsageCount) {\n tool.stats.usageCount = currentUsageCount + 1;\n }\n\n // 时间校验:只有新时间晚于现有时间才更新 lastUsedTime\n if (\n !currentLastUsedTime ||\n new Date(callTime) > new Date(currentLastUsedTime)\n ) {\n tool.stats.lastUsedTime = dayjs(callTime).format(\"YYYY-MM-DD HH:mm:ss\");\n }\n\n // 保存更新后的工具配置\n await this.updateCustomMCPTools(updatedTools);\n } catch (error) {\n // 根据参数类型决定错误日志的前缀\n if (typeof arg3 === \"string\") {\n const serverName = arg1;\n const toolName = arg2;\n console.error(\"更新 customMCP 工具统计信息失败\", {\n serverName,\n toolName,\n error,\n });\n } else {\n const toolName = arg1;\n console.error(\"更新 customMCP 工具统计信息失败\", { toolName, error });\n }\n // customMCP 统计更新失败不应该影响主要流程\n }\n }\n\n /**\n * 获取统计更新锁(确保同一工具的统计更新串行执行)\n * @param toolKey 工具键\n * @private\n */\n private async acquireStatsUpdateLock(toolKey: string): Promise<boolean> {\n if (this.statsUpdateLocks.has(toolKey)) {\n console.log(\"工具统计更新正在进行中,跳过本次更新\", { toolKey });\n return false;\n }\n\n const updatePromise = new Promise<void>((resolve) => {\n // 锁定逻辑在调用者中实现\n });\n\n this.statsUpdateLocks.set(toolKey, updatePromise);\n\n // 设置超时自动释放锁\n const timeout = setTimeout(() => {\n this.releaseStatsUpdateLock(toolKey);\n }, this.STATS_UPDATE_TIMEOUT);\n\n this.statsUpdateLockTimeouts.set(toolKey, timeout);\n\n return true;\n }\n\n /**\n * 释放统计更新锁\n * @param toolKey 工具键\n * @private\n */\n private releaseStatsUpdateLock(toolKey: string): void {\n this.statsUpdateLocks.delete(toolKey);\n\n const timeout = this.statsUpdateLockTimeouts.get(toolKey);\n if (timeout) {\n clearTimeout(timeout);\n this.statsUpdateLockTimeouts.delete(toolKey);\n }\n\n console.log(\"已释放工具的统计更新锁\", { toolKey });\n }\n\n /**\n * 带并发控制的工具统计更新(CustomMCP 工具)\n * @param toolName 工具名称\n * @param incrementUsageCount 是否增加使用计数\n */\n public async updateToolUsageStatsWithLock(\n toolName: string,\n incrementUsageCount = true\n ): Promise<void> {\n const toolKey = `custommcp_${toolName}`;\n\n if (!(await this.acquireStatsUpdateLock(toolKey))) {\n return; // 已有其他更新在进行\n }\n\n try {\n await this.updateToolUsageStats(toolName, incrementUsageCount);\n console.log(\"工具统计更新完成\", { toolName });\n } catch (error) {\n console.error(\"工具统计更新失败\", { toolName, error });\n throw error;\n } finally {\n this.releaseStatsUpdateLock(toolKey);\n }\n }\n\n /**\n * 带并发控制的工具统计更新(MCP 服务工具)\n * @param serviceName 服务名称\n * @param toolName 工具名称\n * @param callTime 调用时间\n * @param incrementUsageCount 是否增加使用计数\n */\n public async updateMCPServerToolStatsWithLock(\n serviceName: string,\n toolName: string,\n callTime: string,\n incrementUsageCount = true\n ): Promise<void> {\n const toolKey = `mcpserver_${serviceName}_${toolName}`;\n\n if (!(await this.acquireStatsUpdateLock(toolKey))) {\n return; // 已有其他更新在进行\n }\n\n try {\n await this.updateMCPServerToolStats(\n serviceName,\n toolName,\n callTime,\n incrementUsageCount\n );\n console.log(\"MCP 服务工具统计更新完成\", { serviceName, toolName });\n } catch (error) {\n console.error(\"MCP 服务工具统计更新失败\", {\n serviceName,\n toolName,\n error,\n });\n throw error;\n } finally {\n this.releaseStatsUpdateLock(toolKey);\n }\n }\n\n /**\n * 清理所有统计更新锁(用于异常恢复)\n */\n public clearAllStatsUpdateLocks(): void {\n const lockCount = this.statsUpdateLocks.size;\n this.statsUpdateLocks.clear();\n\n // 清理所有超时定时器\n for (const timeout of this.statsUpdateLockTimeouts.values()) {\n clearTimeout(timeout);\n }\n this.statsUpdateLockTimeouts.clear();\n\n if (lockCount > 0) {\n console.log(\"已清理统计更新锁\", { count: lockCount });\n }\n }\n\n /**\n * 获取统计更新锁状态(用于调试和监控)\n */\n public getStatsUpdateLocks(): string[] {\n return Array.from(this.statsUpdateLocks.keys());\n }\n\n /**\n * 获取工具调用日志配置\n */\n public getToolCallLogConfig(): Readonly<ToolCallLogConfig> {\n const config = this.getConfig();\n return config.toolCallLog || {};\n }\n\n /**\n * 更新工具调用日志配置\n */\n public updateToolCallLogConfig(\n toolCallLogConfig: Partial<ToolCallLogConfig>\n ): void {\n const config = this.getMutableConfig();\n\n // 确保 toolCallLog 对象存在\n if (!config.toolCallLog) {\n config.toolCallLog = {};\n }\n\n // 直接修改现有的 toolCallLog 对象以保留注释\n Object.assign(config.toolCallLog, toolCallLogConfig);\n this.saveConfig(config);\n }\n\n /**\n * 获取配置目录路径(与配置文件同级目录)\n */\n public getConfigDir(): string {\n // 配置文件路径 - 优先使用环境变量指定的目录,否则使用当前工作目录\n return process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n }\n\n /**\n * 获取 TTS 配置\n */\n public getTTSConfig(): Readonly<TTSConfig> {\n const config = this.getConfig();\n return config.tts || {};\n }\n\n /**\n * 获取 ASR 配置\n */\n public getASRConfig(): Readonly<ASRConfig> {\n const config = this.getConfig();\n return config.asr || {};\n }\n\n /**\n * 获取 LLM 配置\n */\n public getLLMConfig(): LLMConfig | null {\n const config = this.getConfig();\n return config.llm || null;\n }\n\n /**\n * 检查 LLM 配置是否有效\n */\n public isLLMConfigValid(): boolean {\n const llmConfig = this.getLLMConfig();\n return (\n llmConfig !== null &&\n typeof llmConfig.model === \"string\" &&\n llmConfig.model.trim() !== \"\" &&\n typeof llmConfig.apiKey === \"string\" &&\n llmConfig.apiKey.trim() !== \"\" &&\n typeof llmConfig.baseURL === \"string\" &&\n llmConfig.baseURL.trim() !== \"\"\n );\n }\n\n /**\n * 更新 TTS 配置\n */\n public updateTTSConfig(ttsConfig: Partial<TTSConfig>): void {\n const config = this.getMutableConfig();\n\n // 确保 tts 对象存在\n if (!config.tts) {\n config.tts = {};\n }\n\n // 直接修改现有的 tts 对象以保留注释\n Object.assign(config.tts, ttsConfig);\n this.saveConfig(config);\n\n // 发射配置更新事件\n this.emitEvent(\"config:updated\", {\n type: \"tts\",\n timestamp: new Date(),\n });\n }\n}\n\n// 导出单例实例\nexport const configManager = ConfigManager.getInstance();\n","/**\n * JSON5 注释保留适配器\n * 使用 comment-json 实现 JSON5/JSONC 注释保留功能\n *\n * 注意:为了使用 comment-json 保留注释,JSON5 配置文件的键需要带引号。\n * 这与 JSON5 标准语法允许不带引号的键略有不同,但能实现注释保留功能。\n */\nimport * as commentJson from \"comment-json\";\n\n/**\n * JSON5 写入器适配器接口\n * 保持与 json5-writer 兼容的 API\n */\nexport interface Json5WriterAdapter {\n write(data: unknown): void;\n toSource(): string;\n}\n\n/**\n * 创建 JSON5 写入器适配器\n * @param content 原始 JSON5 内容字符串\n * @returns Json5WriterAdapter 实例\n */\nexport function createJson5Writer(content: string): Json5WriterAdapter {\n // 使用 comment-json 解析原始内容\n // comment-json 会保留注释信息在返回的对象中\n const parsedData = commentJson.parse(content) as Record<string, unknown>;\n\n return {\n write(data: unknown): void {\n // 通过 Object.assign 合并新数据\n if (parsedData && typeof parsedData === \"object\" && data) {\n Object.assign(parsedData, data);\n }\n },\n\n toSource(): string {\n // 使用 comment-json 序列化,保留注释和格式\n return commentJson.stringify(parsedData, null, 2);\n },\n };\n}\n\n/**\n * 解析 JSON5 内容(带注释保留)\n * @param content JSON5 内容字符串\n * @returns 解析后的对象\n */\nexport function parseJson5(content: string): unknown {\n // 使用 comment-json 解析,支持注释保留\n return commentJson.parse(content);\n}\n","/**\n * 配置解析器\n * 负责按优先级查找配置文件\n */\n\nimport path from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\n/**\n * 配置解析器类\n * 实现配置文件查找优先级逻辑\n */\nexport class ConfigResolver {\n /**\n * 按优先级解析配置文件路径\n *\n * 优先级顺序:\n * 1. 环境变量 XIAOZHI_CONFIG_DIR 指定的目录\n * 2. 当前工作目录\n * 3. 用户家目录/.xiaozhi-client/\n *\n * @returns 找到的配置文件路径,如果都不存在则返回 null\n */\n static resolveConfigPath(): string | null {\n // 优先级 1: 环境变量指定(向后兼容)\n if (process.env.XIAOZHI_CONFIG_DIR) {\n const configPath = this.findConfigInDir(process.env.XIAOZHI_CONFIG_DIR);\n if (configPath) {\n return configPath;\n }\n }\n\n // 优先级 2: 当前目录\n const currentDirConfig = this.findConfigInDir(process.cwd());\n if (currentDirConfig) {\n return currentDirConfig;\n }\n\n // 优先级 3: 用户家目录/.xiaozhi-client/\n const homeDir = process.env.HOME || process.env.USERPROFILE;\n if (homeDir) {\n const xiaozhiClientDir = path.join(homeDir, \".xiaozhi-client\");\n const homeDirConfig = this.findConfigInDir(xiaozhiClientDir);\n if (homeDirConfig) {\n return homeDirConfig;\n }\n }\n\n return null;\n }\n\n /**\n * 在指定目录中查找配置文件\n *\n * 按优先级查找:xiaozhi.config.json5 > xiaozhi.config.jsonc > xiaozhi.config.json\n *\n * @param dir - 要搜索的目录\n * @returns 找到的配置文件路径,如果不存在则返回 null\n */\n static findConfigInDir(dir: string): string | null {\n const configFileNames = [\n \"xiaozhi.config.json5\",\n \"xiaozhi.config.jsonc\",\n \"xiaozhi.config.json\",\n ];\n\n for (const fileName of configFileNames) {\n const filePath = path.join(dir, fileName);\n if (existsSync(filePath)) {\n return filePath;\n }\n }\n\n return null;\n }\n\n /**\n * 获取默认配置目录路径\n *\n * @returns 用户家目录下的 .xiaozhi-client 目录路径,如果无法获取家目录则返回 null\n */\n static getDefaultConfigDir(): string | null {\n const homeDir = process.env.HOME || process.env.USERPROFILE;\n if (!homeDir) {\n return null;\n }\n return path.join(homeDir, \".xiaozhi-client\");\n }\n}\n","/**\n * 配置适配器\n * 将旧的配置格式转换为新的 MCPServiceConfig 格式,确保向后兼容性\n */\n\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport type {\n HTTPMCPServerConfig,\n LocalMCPServerConfig,\n MCPServerConfig,\n SSEMCPServerConfig,\n} from \"./manager.js\";\nimport { ConfigResolver } from \"./resolver.js\";\n\n// 从外部导入 MCP 类型(这些类型将在运行时从 backend 包解析)\n// 为了避免循环依赖,这里使用动态导入的方式\n// 在实际使用时,adapter 将作为 config 包的一部分被使用\n\n/**\n * 配置验证错误类\n */\nexport class ConfigValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigValidationError\";\n }\n}\n\n// 定义简化的 MCP 传输类型\nexport enum MCPTransportType {\n STDIO = \"stdio\",\n SSE = \"sse\",\n HTTP = \"http\",\n}\n\n// 定义简化的 MCPServiceConfig 接口\nexport interface MCPServiceConfig {\n type: MCPTransportType;\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n url?: string;\n headers?: Record<string, string>;\n}\n\n/**\n * URL 类型推断函数\n * 基于 URL 路径末尾推断传输类型\n */\nfunction inferTransportTypeFromUrl(url: string): MCPTransportType {\n try {\n const parsedUrl = new URL(url);\n const pathname = parsedUrl.pathname;\n\n // 检查路径末尾\n if (pathname.endsWith(\"/sse\")) {\n return MCPTransportType.SSE;\n }\n if (pathname.endsWith(\"/mcp\")) {\n return MCPTransportType.HTTP;\n }\n\n // 默认类型\n return MCPTransportType.HTTP;\n } catch (error) {\n // URL 解析失败时使用默认类型\n return MCPTransportType.HTTP;\n }\n}\n\n/**\n * 将各种配置格式标准化为统一的服务配置格式\n */\nexport function normalizeServiceConfig(\n config: MCPServerConfig\n): MCPServiceConfig {\n console.log(\"转换配置\", { config });\n\n try {\n // 验证输入参数\n if (!config || typeof config !== \"object\") {\n throw new ConfigValidationError(\"配置对象不能为空\");\n }\n\n // 根据配置类型进行转换\n const newConfig = convertByConfigType(config);\n\n // 验证转换后的配置\n validateNewConfig(newConfig);\n\n console.log(\"配置转换成功\", { type: newConfig.type });\n return newConfig;\n } catch (error) {\n console.error(\"配置转换失败\", { error });\n throw error instanceof ConfigValidationError\n ? error\n : new ConfigValidationError(\n `配置转换失败: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * 根据配置类型进行转换\n */\nfunction convertByConfigType(config: MCPServerConfig): MCPServiceConfig {\n // 检查是否为本地 stdio 配置(最高优先级)\n if (isLocalConfig(config)) {\n return convertLocalConfig(config);\n }\n\n // 检查是否有显式指定的类型\n if (\"type\" in config) {\n switch (config.type) {\n case \"sse\":\n return convertSSEConfig(config);\n case \"http\":\n case \"streamable-http\": // 向后兼容\n return convertHTTPConfig(config);\n default:\n throw new ConfigValidationError(`不支持的传输类型: ${config.type}`);\n }\n }\n\n // 检查是否为网络配置(自动推断类型)\n if (\"url\" in config) {\n // 如果 URL 是 undefined 或 null,抛出错误\n if (config.url === undefined || config.url === null) {\n throw new ConfigValidationError(\"网络配置必须包含有效的 url 字段\");\n }\n\n // 先推断类型,然后根据推断的类型选择正确的转换函数\n const inferredType = inferTransportTypeFromUrl(config.url || \"\");\n\n if (inferredType === MCPTransportType.SSE) {\n // 为SSE类型添加显式type字段\n const sseConfig = { ...config, type: \"sse\" as const };\n return convertSSEConfig(sseConfig);\n }\n // 为HTTP类型添加显式type字段\n const httpConfig = { ...config, type: \"http\" as const };\n return convertHTTPConfig(httpConfig);\n }\n\n throw new ConfigValidationError(\"无法识别的配置类型\");\n}\n\n/**\n * 转换本地 stdio 配置\n */\nfunction convertLocalConfig(config: MCPServerConfig): MCPServiceConfig {\n // 类型守卫:确保是 LocalMCPServerConfig\n if (!isLocalConfig(config)) {\n throw new ConfigValidationError(\"无效的本地配置类型\");\n }\n\n const { command, args, env } = config;\n\n if (!command) {\n throw new ConfigValidationError(\"本地配置必须包含 command 字段\");\n }\n\n // 获取配置文件所在目录作为工作目录\n // 优先使用环境变量,否则查找配置文件所在目录,最后回退到当前工作目录\n let workingDir: string;\n if (process.env.XIAOZHI_CONFIG_DIR) {\n workingDir = process.env.XIAOZHI_CONFIG_DIR;\n } else {\n // 使用 ConfigResolver 查找配置文件路径\n const configPath = ConfigResolver.resolveConfigPath();\n if (configPath) {\n // 获取配置文件所在目录\n workingDir = dirname(configPath);\n } else {\n // 回退到当前工作目录\n workingDir = process.cwd();\n }\n }\n\n // 解析 command 中的相对路径\n let resolvedCommand = command;\n if (isRelativePath(command)) {\n resolvedCommand = resolve(workingDir, command);\n console.log(\"解析 command 相对路径\", {\n command,\n resolvedCommand,\n workingDir,\n });\n }\n\n // 解析 args 中的相对路径\n const resolvedArgs = (args || []).map((arg: string) => {\n // 检查是否为相对路径(以 ./ 开头或不以 / 开头且包含文件扩展名)\n if (isRelativePath(arg)) {\n const resolvedPath = resolve(workingDir, arg);\n console.log(\"解析相对路径\", { arg, resolvedPath, workingDir });\n return resolvedPath;\n }\n return arg;\n });\n\n return {\n type: MCPTransportType.STDIO,\n command: resolvedCommand,\n args: resolvedArgs,\n ...(env !== undefined && { env }), // 只在 env 存在时添加该字段\n };\n}\n\n/**\n * 转换 SSE 配置\n */\nfunction convertSSEConfig(config: MCPServerConfig): MCPServiceConfig {\n // 使用类型守卫确保 config 包含必要的属性\n if (!isURLConfig(config)) {\n throw new ConfigValidationError(\"SSE 配置必须包含 url 字段\");\n }\n\n const url = config.url;\n const type = \"type\" in config ? config.type : undefined;\n const headers = \"headers\" in config ? config.headers : undefined;\n\n // 优先使用显式指定的类型,如果没有则进行推断\n const inferredType =\n type === \"sse\"\n ? MCPTransportType.SSE\n : inferTransportTypeFromUrl(url || \"\");\n const isModelScope = url ? isModelScopeURL(url) : false;\n\n console.log(\"SSE配置转换\", {\n url,\n inferredType,\n isModelScope,\n });\n\n return {\n type: inferredType,\n url,\n headers,\n };\n}\n\n/**\n * 转换 HTTP 配置\n */\nfunction convertHTTPConfig(config: MCPServerConfig): MCPServiceConfig {\n // 使用类型守卫确保 config 包含必要的属性\n if (!isURLConfig(config)) {\n throw new ConfigValidationError(\"HTTP 配置必须包含 url 字段\");\n }\n\n const url = config.url;\n const headers = \"headers\" in config ? config.headers : undefined;\n\n return {\n type: MCPTransportType.HTTP,\n url: url || \"\",\n headers,\n };\n}\n\n/**\n * 批量标准化配置\n */\nexport function normalizeServiceConfigBatch(\n legacyConfigs: Record<string, MCPServerConfig>\n): Record<string, MCPServiceConfig> {\n const newConfigs: Record<string, MCPServiceConfig> = {};\n const errors: Array<{ error: Error }> = [];\n\n for (const [name, config] of Object.entries(legacyConfigs)) {\n try {\n newConfigs[name] = normalizeServiceConfig(config);\n } catch (error) {\n errors.push({\n error: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n\n if (errors.length > 0) {\n const errorMessages = errors\n .map(({ error }, index) => `[${index}]: ${error.message}`)\n .join(\"; \");\n throw new ConfigValidationError(`批量配置转换失败: ${errorMessages}`);\n }\n\n console.log(\"批量配置转换成功\", { count: Object.keys(newConfigs).length });\n return newConfigs;\n}\n\n/**\n * 检查是否为相对路径\n */\nfunction isRelativePath(path: string): boolean {\n // 使用 Node.js 的 path.isAbsolute() 来正确检测绝对路径\n // 这个方法能够正确处理 Windows、macOS、Linux 三个平台的路径格式\n if (isAbsolute(path)) {\n return false; // 绝对路径不是相对路径\n }\n\n // 检查是否为相对路径的条件:\n // 1. 以 ./ 或 ../ 开头\n // 2. 包含常见的脚本文件扩展名(且不是绝对路径)\n if (path.startsWith(\"./\") || path.startsWith(\"../\")) {\n return true;\n }\n\n // 如果包含文件扩展名且不是绝对路径,也认为是相对路径\n if (/\\.(js|py|ts|mjs|cjs)$/i.test(path)) {\n return true;\n }\n\n return false;\n}\n\n/**\n * 检查是否为本地配置\n */\nfunction isLocalConfig(\n config: MCPServerConfig\n): config is LocalMCPServerConfig {\n return \"command\" in config && typeof config.command === \"string\";\n}\n\n/**\n * 检查是否为 URL 配置(SSE 或 HTTP)\n * 类型守卫函数,用于验证配置包含 url 属性\n */\nfunction isURLConfig(\n config: MCPServerConfig\n): config is (SSEMCPServerConfig | HTTPMCPServerConfig) & { url: string } {\n return \"url\" in config && typeof config.url === \"string\";\n}\n\n/**\n * 检查是否为 ModelScope URL\n * 使用 URL hostname 检查而非简单的字符串包含检查,防止安全绕过\n */\nexport function isModelScopeURL(url: string): boolean {\n try {\n const parsedUrl = new URL(url);\n const hostname = parsedUrl.hostname.toLowerCase();\n return (\n hostname.endsWith(\".modelscope.net\") ||\n hostname.endsWith(\".modelscope.cn\") ||\n hostname === \"modelscope.net\" ||\n hostname === \"modelscope.cn\"\n );\n } catch {\n return false;\n }\n}\n\n/**\n * 验证新配置格式\n */\nfunction validateNewConfig(config: MCPServiceConfig): void {\n if (config.type && !Object.values(MCPTransportType).includes(config.type)) {\n throw new ConfigValidationError(`无效的传输类型: ${config.type}`);\n }\n\n // 根据传输类型验证必需字段\n if (!config.type) {\n throw new ConfigValidationError(\"传输类型未指定,请检查配置或启用自动推断\");\n }\n\n switch (config.type) {\n case MCPTransportType.STDIO:\n if (!config.command) {\n throw new ConfigValidationError(\"STDIO 配置必须包含 command 字段\");\n }\n break;\n\n case MCPTransportType.SSE:\n // SSE 配置必须有 URL(即使是空字符串也会被推断为 HTTP)\n if (config.url === undefined || config.url === null) {\n throw new ConfigValidationError(\"SSE 配置必须包含 url 字段\");\n }\n break;\n\n case MCPTransportType.HTTP:\n // HTTP 配置允许空 URL,会在后续处理中设置默认值\n // 只有当 URL 完全不存在时才报错\n if (config.url === undefined || config.url === null) {\n throw new ConfigValidationError(\"HTTP 配置必须包含 url 字段\");\n }\n break;\n\n default:\n throw new ConfigValidationError(`不支持的传输类型: ${config.type}`);\n }\n}\n\n/**\n * 获取配置类型描述\n */\nexport function getConfigTypeDescription(config: MCPServerConfig): string {\n if (isLocalConfig(config)) {\n return `本地进程 (${config.command})`;\n }\n\n if (\"url\" in config) {\n // 检查是否为显式 http 配置\n if (\n \"type\" in config &&\n (config.type === \"http\" || config.type === \"streamable-http\")\n ) {\n return `HTTP (${config.url})`;\n }\n\n // 检查是否为显式 sse 配置\n if (\"type\" in config && config.type === \"sse\") {\n const isModelScope = isModelScopeURL(config.url);\n return `SSE${isModelScope ? \" (ModelScope)\" : \"\"} (${config.url})`;\n }\n\n // 对于只有 url 的配置,根据路径推断类型\n const inferredType = inferTransportTypeFromUrl(config.url);\n const isModelScope = isModelScopeURL(config.url);\n\n if (inferredType === MCPTransportType.SSE) {\n return `SSE${isModelScope ? \" (ModelScope)\" : \"\"} (${config.url})`;\n }\n return `HTTP (${config.url})`;\n }\n\n return \"未知类型\";\n}\n","/**\n * 配置初始化器\n * 负责在用户家目录创建默认配置\n */\n\nimport path from \"node:path\";\nimport { mkdirSync, existsSync, rmSync, readdirSync, statSync, copyFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n/**\n * 配置初始化器类\n * 负责在用户家目录创建完整的默认项目目录\n */\nexport class ConfigInitializer {\n /**\n * 初始化默认配置\n *\n * 复制整个默认模板目录到用户家目录的 .xiaozhi-client\n * 这包括 mcpServers/ 目录和其他必要文件\n *\n * @returns 创建的项目目录路径\n * @throws 如果无法获取用户家目录或默认配置模板不存在\n */\n static async initializeDefaultConfig(): Promise<string> {\n const homeDir = process.env.HOME || process.env.USERPROFILE;\n if (!homeDir) {\n throw new Error(\"无法获取用户家目录\");\n }\n\n const xiaozhiClientDir = path.join(homeDir, \".xiaozhi-client\");\n\n // 如果目录已存在,直接使用现有配置目录,避免删除用户数据\n if (existsSync(xiaozhiClientDir)) {\n return xiaozhiClientDir;\n }\n\n // 创建目录\n mkdirSync(xiaozhiClientDir, { recursive: true });\n\n // 获取默认模板目录路径\n const defaultTemplateDir = this.getDefaultTemplateDir();\n if (!defaultTemplateDir) {\n throw new Error(\n \"默认配置模板不存在,请检查项目模板文件是否存在\"\n );\n }\n\n // 复制整个模板目录\n this.copyDirectoryRecursive(defaultTemplateDir, xiaozhiClientDir, [\n \"template.json\",\n \".git\",\n \"node_modules\",\n ]);\n\n return xiaozhiClientDir;\n }\n\n /**\n * 递归复制目录\n *\n * @param srcDir 源目录\n * @param destDir 目标目录\n * @param exclude 要排除的文件/目录列表\n */\n private static copyDirectoryRecursive(\n srcDir: string,\n destDir: string,\n exclude: string[] = []\n ): void {\n const items = readdirSync(srcDir);\n\n for (const item of items) {\n // 跳过排除列表中的项\n if (exclude.includes(item)) {\n continue;\n }\n\n const srcPath = path.join(srcDir, item);\n const destPath = path.join(destDir, item);\n const stat = statSync(srcPath);\n\n if (stat.isDirectory()) {\n // 递归复制子目录\n mkdirSync(destPath, { recursive: true });\n this.copyDirectoryRecursive(srcPath, destPath, exclude);\n } else {\n // 复制文件\n copyFileSync(srcPath, destPath);\n }\n }\n }\n\n /**\n * 获取默认模板目录路径\n *\n * 在多个可能的路径中查找默认模板目录\n *\n * @returns 找到的默认模板目录路径,如果都不存在则返回 null\n */\n private static getDefaultTemplateDir(): string | null {\n const possiblePaths = [\n // 开发环境:packages/config/src 目录\n resolve(__dirname, \"templates\", \"default\"),\n // 开发环境:packages/config 目录\n resolve(__dirname, \"..\", \"templates\", \"default\"),\n // 项目根目录的 templates\n resolve(process.cwd(), \"templates\", \"default\"),\n // dist 目录(从 packages/config/dist 配置目录)\n resolve(__dirname, \"..\", \"..\", \"..\", \"templates\", \"default\"),\n // 全局安装的 node_modules 目录\n resolve(__dirname, \"..\", \"..\", \"..\", \"..\", \"templates\", \"default\"),\n ];\n\n for (const p of possiblePaths) {\n if (existsSync(p)) {\n return p;\n }\n }\n\n return null;\n }\n}\n"],"mappings":"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","names":["existsSync","commentJson","path","existsSync","tool","resolve","dirname","resolve","MCPTransportType","dirname","resolve","path","isModelScope","path","existsSync","copyFileSync","dirname","resolve","fileURLToPath","__dirname","dirname","fileURLToPath","path","existsSync","copyFileSync","resolve"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiaozhi-client/config",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,8 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"comment-json": "^4.2.5",
|
|
24
|
-
"dayjs": "^1.11.13"
|
|
25
|
-
"json5": "^2.2.3"
|
|
24
|
+
"dayjs": "^1.11.13"
|
|
26
25
|
},
|
|
27
26
|
"devDependencies": {
|
|
28
27
|
"@types/node": "^24.3.0",
|
package/src/manager.ts
CHANGED
|
@@ -112,6 +112,32 @@ export interface WebUIConfig {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
// 工具调用日志配置接口
|
|
115
|
+
// TTS 配置接口
|
|
116
|
+
export interface TTSConfig {
|
|
117
|
+
appid?: string; // 应用 ID
|
|
118
|
+
accessToken?: string; // 访问令牌
|
|
119
|
+
voice_type?: string; // 声音类型
|
|
120
|
+
encoding?: string; // 编码格式(默认 wav)
|
|
121
|
+
cluster?: string; // 集群类型
|
|
122
|
+
endpoint?: string; // WebSocket 端点
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ASR 配置接口
|
|
126
|
+
export interface ASRConfig {
|
|
127
|
+
appid?: string; // 应用 ID
|
|
128
|
+
accessToken?: string; // 访问令牌
|
|
129
|
+
cluster?: string; // 集群类型(默认:volcengine_streaming_common)
|
|
130
|
+
wsUrl?: string; // WebSocket 端点
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// LLM 配置接口
|
|
134
|
+
export interface LLMConfig {
|
|
135
|
+
model: string; // 模型名称
|
|
136
|
+
apiKey: string; // API 密钥
|
|
137
|
+
baseURL: string; // API 基础地址
|
|
138
|
+
prompt?: string; // 自定义系统提示词(支持纯字符串或文件路径)
|
|
139
|
+
}
|
|
140
|
+
|
|
115
141
|
export interface ToolCallLogConfig {
|
|
116
142
|
maxRecords?: number; // 最大记录条数,默认 100
|
|
117
143
|
logFilePath?: string; // 自定义日志文件路径(可选)
|
|
@@ -259,6 +285,9 @@ export interface AppConfig {
|
|
|
259
285
|
webUI?: WebUIConfig; // Web UI 配置(可选)
|
|
260
286
|
platforms?: PlatformsConfig; // 平台配置(可选)
|
|
261
287
|
toolCallLog?: ToolCallLogConfig; // 工具调用日志配置(可选)
|
|
288
|
+
tts?: TTSConfig; // TTS 配置(可选)
|
|
289
|
+
asr?: ASRConfig; // ASR 配置(可选)
|
|
290
|
+
llm?: LLMConfig; // LLM 配置(可选)
|
|
262
291
|
}
|
|
263
292
|
|
|
264
293
|
/**
|
|
@@ -281,7 +310,8 @@ export class ConfigManager {
|
|
|
281
310
|
private readonly STATS_UPDATE_TIMEOUT = 5000; // 5秒超时
|
|
282
311
|
|
|
283
312
|
// 事件回调(用于解耦 EventBus 依赖)
|
|
284
|
-
private eventCallbacks: Map<string, Array<(data: unknown) => void>> =
|
|
313
|
+
private eventCallbacks: Map<string, Array<(data: unknown) => void>> =
|
|
314
|
+
new Map();
|
|
285
315
|
|
|
286
316
|
private constructor() {
|
|
287
317
|
// 使用模板目录中的默认配置文件
|
|
@@ -2288,6 +2318,68 @@ export class ConfigManager {
|
|
|
2288
2318
|
// 配置文件路径 - 优先使用环境变量指定的目录,否则使用当前工作目录
|
|
2289
2319
|
return process.env.XIAOZHI_CONFIG_DIR || process.cwd();
|
|
2290
2320
|
}
|
|
2321
|
+
|
|
2322
|
+
/**
|
|
2323
|
+
* 获取 TTS 配置
|
|
2324
|
+
*/
|
|
2325
|
+
public getTTSConfig(): Readonly<TTSConfig> {
|
|
2326
|
+
const config = this.getConfig();
|
|
2327
|
+
return config.tts || {};
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
/**
|
|
2331
|
+
* 获取 ASR 配置
|
|
2332
|
+
*/
|
|
2333
|
+
public getASRConfig(): Readonly<ASRConfig> {
|
|
2334
|
+
const config = this.getConfig();
|
|
2335
|
+
return config.asr || {};
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
/**
|
|
2339
|
+
* 获取 LLM 配置
|
|
2340
|
+
*/
|
|
2341
|
+
public getLLMConfig(): LLMConfig | null {
|
|
2342
|
+
const config = this.getConfig();
|
|
2343
|
+
return config.llm || null;
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
/**
|
|
2347
|
+
* 检查 LLM 配置是否有效
|
|
2348
|
+
*/
|
|
2349
|
+
public isLLMConfigValid(): boolean {
|
|
2350
|
+
const llmConfig = this.getLLMConfig();
|
|
2351
|
+
return (
|
|
2352
|
+
llmConfig !== null &&
|
|
2353
|
+
typeof llmConfig.model === "string" &&
|
|
2354
|
+
llmConfig.model.trim() !== "" &&
|
|
2355
|
+
typeof llmConfig.apiKey === "string" &&
|
|
2356
|
+
llmConfig.apiKey.trim() !== "" &&
|
|
2357
|
+
typeof llmConfig.baseURL === "string" &&
|
|
2358
|
+
llmConfig.baseURL.trim() !== ""
|
|
2359
|
+
);
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
/**
|
|
2363
|
+
* 更新 TTS 配置
|
|
2364
|
+
*/
|
|
2365
|
+
public updateTTSConfig(ttsConfig: Partial<TTSConfig>): void {
|
|
2366
|
+
const config = this.getMutableConfig();
|
|
2367
|
+
|
|
2368
|
+
// 确保 tts 对象存在
|
|
2369
|
+
if (!config.tts) {
|
|
2370
|
+
config.tts = {};
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
// 直接修改现有的 tts 对象以保留注释
|
|
2374
|
+
Object.assign(config.tts, ttsConfig);
|
|
2375
|
+
this.saveConfig(config);
|
|
2376
|
+
|
|
2377
|
+
// 发射配置更新事件
|
|
2378
|
+
this.emitEvent("config:updated", {
|
|
2379
|
+
type: "tts",
|
|
2380
|
+
timestamp: new Date(),
|
|
2381
|
+
});
|
|
2382
|
+
}
|
|
2291
2383
|
}
|
|
2292
2384
|
|
|
2293
2385
|
// 导出单例实例
|
package/tsup.config.ts
CHANGED