xiaozhi-client 1.6.0 → 1.6.1-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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/logger.ts","../src/ProxyMCPServer.ts","../src/services/TransportFactory.ts","../src/services/MCPService.ts","../src/adapters/ConfigAdapter.ts","../src/utils/mcpServerUtils.ts","../src/configManager.ts","../src/mcpCommands.ts","../src/services/MCPServiceManager.ts","../src/services/mcpServer.ts","../src/cli.ts","../src/services/MCPServiceManagerSingleton.ts","../src/services/XiaozhiConnectionManager.ts","../src/services/XiaozhiConnectionManagerSingleton.ts","../src/WebServer.ts","../src/webServerStandalone.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\nimport { type consola, createConsola } from \"consola\";\n\nfunction formatDateTime(date: Date) {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n const hours = String(date.getHours()).padStart(2, \"0\");\n const minutes = String(date.getMinutes()).padStart(2, \"0\");\n const seconds = String(date.getSeconds()).padStart(2, \"0\");\n\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;\n}\n\nexport class Logger {\n private logFilePath: string | null = null;\n private writeStream: fs.WriteStream | null = null;\n private consolaInstance: typeof consola;\n private isDaemonMode: boolean;\n\n constructor() {\n // 检查是否为守护进程模式\n this.isDaemonMode = process.env.XIAOZHI_DAEMON === \"true\";\n // 创建自定义的 consola 实例,禁用图标并自定义格式\n this.consolaInstance = createConsola({\n formatOptions: {\n date: false,\n colors: true,\n compact: true,\n },\n fancy: false,\n });\n\n // 保存对当前实例的引用,以便在闭包中访问\n const isDaemonMode = this.isDaemonMode;\n\n // 自定义格式化器\n this.consolaInstance.setReporters([\n {\n log: (logObj) => {\n const levelMap: Record<string, string> = {\n info: \"INFO\",\n success: \"SUCCESS\",\n warn: \"WARN\",\n error: \"ERROR\",\n debug: \"DEBUG\",\n log: \"LOG\",\n };\n\n const colorMap: Record<string, (text: string) => string> = {\n info: chalk.blue,\n success: chalk.green,\n warn: chalk.yellow,\n error: chalk.red,\n debug: chalk.gray,\n log: (text: string) => text,\n };\n\n const level = levelMap[logObj.type] || logObj.type.toUpperCase();\n const colorFn = colorMap[logObj.type] || ((text: string) => text);\n const timestamp = formatDateTime(new Date());\n\n // 为级别添加颜色\n const coloredLevel = colorFn(`[${level}]`);\n const message = `[${timestamp}] ${coloredLevel} ${logObj.args.join(\n \" \"\n )}`;\n\n // 守护进程模式下不输出到控制台,只写入文件\n if (!isDaemonMode) {\n // 输出到 stderr(与原来保持一致)\n try {\n console.error(message);\n } catch (error) {\n // 忽略 EPIPE 错误\n if (error instanceof Error && error.message?.includes(\"EPIPE\")) {\n return;\n }\n throw error;\n }\n }\n },\n },\n ]);\n }\n\n /**\n * 初始化日志文件\n * @param projectDir 项目目录\n */\n initLogFile(projectDir: string): void {\n this.logFilePath = path.join(projectDir, \"xiaozhi.log\");\n\n // 确保日志文件存在\n if (!fs.existsSync(this.logFilePath)) {\n fs.writeFileSync(this.logFilePath, \"\");\n }\n\n // 创建写入流,追加模式\n this.writeStream = fs.createWriteStream(this.logFilePath, {\n flags: \"a\",\n encoding: \"utf8\",\n });\n }\n\n /**\n * 记录日志到文件\n * @param level 日志级别\n * @param message 日志消息\n * @param args 额外参数\n */\n private logToFile(level: string, message: string, ...args: any[]): void {\n if (this.writeStream) {\n const timestamp = new Date().toISOString();\n const formattedMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`;\n const fullMessage =\n args.length > 0\n ? `${formattedMessage} ${args\n .map((arg) =>\n typeof arg === \"object\" ? JSON.stringify(arg) : String(arg)\n )\n .join(\" \")}`\n : formattedMessage;\n\n this.writeStream.write(`${fullMessage}\\n`);\n }\n }\n\n /**\n * 设置是否启用文件日志\n * @param enable 是否启用\n */\n enableFileLogging(enable: boolean): void {\n if (enable && !this.writeStream && this.logFilePath) {\n this.writeStream = fs.createWriteStream(this.logFilePath, {\n flags: \"a\",\n encoding: \"utf8\",\n });\n } else if (!enable && this.writeStream) {\n this.writeStream.end();\n this.writeStream = null;\n }\n }\n\n /**\n * 日志方法\n */\n info(message: string, ...args: any[]): void {\n this.consolaInstance.info(message, ...args);\n this.logToFile(\"info\", message, ...args);\n }\n\n success(message: string, ...args: any[]): void {\n this.consolaInstance.success(message, ...args);\n this.logToFile(\"success\", message, ...args);\n }\n\n warn(message: string, ...args: any[]): void {\n this.consolaInstance.warn(message, ...args);\n this.logToFile(\"warn\", message, ...args);\n }\n\n error(message: string, ...args: any[]): void {\n this.consolaInstance.error(message, ...args);\n this.logToFile(\"error\", message, ...args);\n }\n\n debug(message: string, ...args: any[]): void {\n this.consolaInstance.debug(message, ...args);\n this.logToFile(\"debug\", message, ...args);\n }\n\n log(message: string, ...args: any[]): void {\n this.consolaInstance.log(message, ...args);\n this.logToFile(\"log\", message, ...args);\n }\n\n /**\n * 创建一个带标签的日志实例(已废弃,直接返回原实例)\n * @param tag 标签(不再使用)\n * @deprecated 标签功能已移除\n */\n withTag(tag: string): Logger {\n // 不再添加标签,直接返回共享实例\n return this;\n }\n\n /**\n * 关闭日志文件流\n */\n close(): void {\n if (this.writeStream) {\n this.writeStream.end();\n this.writeStream = null;\n }\n }\n}\n\n// 导出单例实例\nexport const logger = new Logger();\n","import type { Tool } from \"@modelcontextprotocol/sdk/types.js\";\nimport WebSocket from \"ws\";\nimport { Logger } from \"./logger.js\";\n\nexport type { Tool };\n\n// MCP 消息接口\ninterface MCPMessage {\n jsonrpc: string;\n id?: number | string;\n method?: string;\n params?: any;\n result?: any;\n}\n\n// 连接状态枚举\nenum ConnectionState {\n DISCONNECTED = \"disconnected\",\n CONNECTING = \"connecting\",\n CONNECTED = \"connected\",\n RECONNECTING = \"reconnecting\",\n FAILED = \"failed\",\n}\n\n// 重连配置接口\ninterface ReconnectOptions {\n enabled: boolean; // 是否启用自动重连\n maxAttempts: number; // 最大重连次数\n initialInterval: number; // 初始重连间隔(ms)\n maxInterval: number; // 最大重连间隔(ms)\n backoffStrategy: \"linear\" | \"exponential\" | \"fixed\"; // 退避策略\n backoffMultiplier: number; // 退避倍数\n timeout: number; // 单次连接超时时间(ms)\n jitter: boolean; // 是否添加随机抖动\n}\n\n// 重连状态接口\ninterface ReconnectState {\n attempts: number; // 当前重连次数\n nextInterval: number; // 下次重连间隔\n timer: NodeJS.Timeout | null; // 重连定时器\n lastError: Error | null; // 最后一次错误\n isManualDisconnect: boolean; // 是否为主动断开\n}\n\n// 服务器选项接口\ninterface ProxyMCPServerOptions {\n reconnect?: Partial<ReconnectOptions>;\n}\n\n// 服务器状态接口\ninterface ProxyMCPServerStatus {\n connected: boolean;\n initialized: boolean;\n url: string;\n availableTools: number;\n connectionState: ConnectionState;\n reconnectAttempts: number;\n lastError: string | null;\n}\n\nexport class ProxyMCPServer {\n private endpointUrl: string;\n private ws: WebSocket | null = null;\n private logger: Logger;\n private isConnected = false;\n private serverInitialized = false;\n\n // 工具管理\n private tools: Map<string, Tool> = new Map();\n\n // 连接状态管理\n private connectionState: ConnectionState = ConnectionState.DISCONNECTED;\n\n // 重连配置\n private reconnectOptions: ReconnectOptions;\n\n // 重连状态\n private reconnectState: ReconnectState = {\n attempts: 0,\n nextInterval: 0,\n timer: null,\n lastError: null,\n isManualDisconnect: false,\n };\n\n // 连接超时定时器\n private connectionTimeout: NodeJS.Timeout | null = null;\n\n constructor(endpointUrl: string, options?: ProxyMCPServerOptions) {\n this.endpointUrl = endpointUrl;\n this.logger = new Logger();\n\n // 初始化重连配置\n this.reconnectOptions = {\n enabled: true,\n maxAttempts: 10,\n initialInterval: 3000,\n maxInterval: 30000,\n backoffStrategy: \"exponential\",\n backoffMultiplier: 1.5,\n timeout: 10000,\n jitter: true,\n ...options?.reconnect,\n };\n\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n }\n\n /**\n * 设置 MCPServiceManager 实例\n * @param serviceManager MCPServiceManager 实例\n */\n setServiceManager(serviceManager: any): void {\n // 临时存储在一个变量中,避免类型检查问题\n (this as any).serviceManager = serviceManager;\n this.logger.info(\"已设置 MCPServiceManager\");\n\n // 立即同步工具\n this.syncToolsFromServiceManager();\n }\n\n /**\n * 从 MCPServiceManager 同步工具\n * 优化版本:支持增量同步和错误恢复\n */\n syncToolsFromServiceManager(): void {\n const serviceManager = (this as any).serviceManager;\n if (!serviceManager) {\n this.logger.debug(\"MCPServiceManager 未设置,跳过工具同步\");\n return;\n }\n\n try {\n // 从 MCPServiceManager 获取所有工具\n const allTools = serviceManager.getAllTools();\n\n // 原子性更新:先构建新的工具映射,再替换\n const newTools = new Map<string, Tool>();\n\n for (const toolInfo of allTools) {\n newTools.set(toolInfo.name, {\n name: toolInfo.name,\n description: toolInfo.description,\n inputSchema: toolInfo.inputSchema,\n });\n }\n\n // 原子性替换\n this.tools = newTools;\n\n this.logger.info(`已从 MCPServiceManager 同步 ${this.tools.size} 个工具`);\n } catch (error) {\n this.logger.error(\n `同步工具失败: ${error instanceof Error ? error.message : String(error)}`\n );\n // 同步失败时保持现有工具不变,确保服务可用性\n }\n }\n\n /**\n * 添加单个工具\n * @param name 工具名称\n * @param tool 工具定义\n * @param options 工具选项(可选)\n * @returns 返回 this 支持链式调用\n */\n addTool(name: string, tool: Tool): this {\n this.validateTool(name, tool);\n this.tools.set(name, tool);\n this.logger.debug(`工具 '${name}' 已添加`);\n // TODO: 未来可以使用 options 参数来设置工具的启用状态、元数据等\n return this;\n }\n\n /**\n * 批量添加工具\n * @param tools 工具对象,键为工具名称,值为工具定义\n * @returns 返回 this 支持链式调用\n */\n addTools(tools: Record<string, Tool>): this {\n for (const [name, tool] of Object.entries(tools)) {\n this.addTool(name, tool);\n }\n return this;\n }\n\n /**\n * 移除单个工具\n * @param name 工具名称\n * @returns 返回 this 支持链式调用\n */\n removeTool(name: string): this {\n if (this.tools.delete(name)) {\n this.logger.debug(`工具 '${name}' 已移除`);\n } else {\n this.logger.warn(`尝试移除不存在的工具: '${name}'`);\n }\n return this;\n }\n\n /**\n * 获取当前所有工具列表\n * @returns 工具数组\n */\n getTools(): Tool[] {\n // 每次获取工具时都尝试从 MCPServiceManager 同步\n try {\n this.syncToolsFromServiceManager();\n } catch (error) {\n // 静默处理同步错误,不影响现有工具的返回\n }\n\n return Array.from(this.tools.values());\n }\n\n /**\n * 检查工具是否存在\n * @param name 工具名称\n * @returns 是否存在\n */\n hasTool(name: string): boolean {\n return this.tools.has(name);\n }\n\n /**\n * 验证工具的有效性\n * @param name 工具名称\n * @param tool 工具定义\n */\n private validateTool(name: string, tool: Tool): void {\n if (!name || typeof name !== \"string\" || name.trim() === \"\") {\n throw new Error(\"工具名称必须是非空字符串\");\n }\n\n if (this.tools.has(name)) {\n throw new Error(`工具 '${name}' 已存在`);\n }\n\n if (!tool || typeof tool !== \"object\") {\n throw new Error(\"工具必须是有效的对象\");\n }\n\n // 验证工具的必需字段\n if (!tool.name || typeof tool.name !== \"string\") {\n throw new Error(\"工具必须包含有效的 'name' 字段\");\n }\n\n if (!tool.description || typeof tool.description !== \"string\") {\n throw new Error(\"工具必须包含有效的 'description' 字段\");\n }\n\n if (!tool.inputSchema || typeof tool.inputSchema !== \"object\") {\n throw new Error(\"工具必须包含有效的 'inputSchema' 字段\");\n }\n\n // 验证 inputSchema 的基本结构\n if (!tool.inputSchema.type || !tool.inputSchema.properties) {\n throw new Error(\n \"工具的 inputSchema 必须包含 'type' 和 'properties' 字段\"\n );\n }\n }\n\n /**\n * 连接 MCP 接入点\n * @returns 连接成功后的 Promise\n */\n public async connect(): Promise<void> {\n // 连接前验证\n if (this.tools.size === 0) {\n throw new Error(\"未配置任何工具。请在连接前至少添加一个工具。\");\n }\n\n // 如果正在连接中,等待当前连接完成\n if (this.connectionState === ConnectionState.CONNECTING) {\n throw new Error(\"连接正在进行中,请等待连接完成\");\n }\n\n // 清理之前的连接\n this.cleanupConnection();\n\n // 重置手动断开标志\n this.reconnectState.isManualDisconnect = false;\n\n return this.attemptConnection();\n }\n\n /**\n * 尝试建立连接\n * @returns 连接成功后的 Promise\n */\n private async attemptConnection(): Promise<void> {\n this.connectionState = ConnectionState.CONNECTING;\n this.logger.info(\n `正在连接 MCP 接入点: ${this.endpointUrl} (尝试 ${\n this.reconnectState.attempts + 1\n }/${this.reconnectOptions.maxAttempts})`\n );\n\n return new Promise((resolve, reject) => {\n // 设置连接超时\n this.connectionTimeout = setTimeout(() => {\n const error = new Error(\n `连接超时 (${this.reconnectOptions.timeout}ms)`\n );\n this.handleConnectionError(error);\n reject(error);\n }, this.reconnectOptions.timeout);\n\n this.ws = new WebSocket(this.endpointUrl);\n\n this.ws.on(\"open\", () => {\n this.handleConnectionSuccess();\n resolve();\n });\n\n this.ws.on(\"message\", (data) => {\n try {\n const message: MCPMessage = JSON.parse(data.toString());\n this.handleMessage(message);\n } catch (error) {\n this.logger.error(\"MCP 消息解析错误:\", error);\n }\n });\n\n this.ws.on(\"close\", (code, reason) => {\n this.handleConnectionClose(code, reason.toString());\n });\n\n this.ws.on(\"error\", (error) => {\n this.handleConnectionError(error);\n reject(error);\n });\n });\n }\n\n /**\n * 处理连接成功\n */\n private handleConnectionSuccess(): void {\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n this.isConnected = true;\n this.connectionState = ConnectionState.CONNECTED;\n\n // 重置重连状态\n this.reconnectState.attempts = 0;\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n this.reconnectState.lastError = null;\n\n this.logger.info(\"MCP WebSocket 连接已建立\");\n }\n\n /**\n * 处理连接错误\n */\n private handleConnectionError(error: Error): void {\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n this.reconnectState.lastError = error;\n this.logger.error(\"MCP WebSocket 错误:\", error.message);\n\n // 清理当前连接\n this.cleanupConnection();\n }\n\n /**\n * 处理连接关闭\n */\n private handleConnectionClose(code: number, reason: string): void {\n this.isConnected = false;\n this.serverInitialized = false;\n this.logger.info(`MCP 连接已关闭 (代码: ${code}, 原因: ${reason})`);\n\n // 如果是手动断开,不进行重连\n if (this.reconnectState.isManualDisconnect) {\n this.connectionState = ConnectionState.DISCONNECTED;\n return;\n }\n\n // 检查是否需要重连\n if (this.shouldReconnect()) {\n this.scheduleReconnect();\n } else {\n this.connectionState = ConnectionState.FAILED;\n this.logger.warn(\n `已达到最大重连次数 (${this.reconnectOptions.maxAttempts}),停止重连`\n );\n }\n }\n\n /**\n * 检查是否应该重连\n */\n private shouldReconnect(): boolean {\n return (\n this.reconnectOptions.enabled &&\n this.reconnectState.attempts < this.reconnectOptions.maxAttempts &&\n !this.reconnectState.isManualDisconnect\n );\n }\n\n /**\n * 安排重连\n */\n private scheduleReconnect(): void {\n this.connectionState = ConnectionState.RECONNECTING;\n this.reconnectState.attempts++;\n\n // 计算下次重连间隔\n this.calculateNextInterval();\n\n this.logger.info(\n `将在 ${this.reconnectState.nextInterval}ms 后进行第 ${this.reconnectState.attempts} 次重连`\n );\n\n // 清理之前的重连定时器\n if (this.reconnectState.timer) {\n clearTimeout(this.reconnectState.timer);\n }\n\n // 设置重连定时器\n this.reconnectState.timer = setTimeout(async () => {\n try {\n await this.attemptConnection();\n } catch (error) {\n // 连接失败会触发 handleConnectionError,无需额外处理\n }\n }, this.reconnectState.nextInterval);\n }\n\n /**\n * 计算下次重连间隔\n */\n private calculateNextInterval(): void {\n let interval: number;\n\n switch (this.reconnectOptions.backoffStrategy) {\n case \"fixed\":\n interval = this.reconnectOptions.initialInterval;\n break;\n\n case \"linear\":\n interval =\n this.reconnectOptions.initialInterval +\n this.reconnectState.attempts *\n this.reconnectOptions.backoffMultiplier *\n 1000;\n break;\n\n case \"exponential\":\n interval =\n this.reconnectOptions.initialInterval *\n this.reconnectOptions.backoffMultiplier **\n (this.reconnectState.attempts - 1);\n break;\n\n default:\n interval = this.reconnectOptions.initialInterval;\n }\n\n // 限制最大间隔\n interval = Math.min(interval, this.reconnectOptions.maxInterval);\n\n // 添加随机抖动\n if (this.reconnectOptions.jitter) {\n const jitterRange = interval * 0.1; // 10% 抖动\n const jitter = (Math.random() - 0.5) * 2 * jitterRange;\n interval += jitter;\n }\n\n this.reconnectState.nextInterval = Math.max(interval, 1000); // 最小1秒\n }\n\n /**\n * 清理连接资源\n */\n private cleanupConnection(): void {\n // 清理 WebSocket\n if (this.ws) {\n // 移除所有事件监听器,防止在关闭时触发错误事件\n this.ws.removeAllListeners();\n\n // 安全关闭 WebSocket\n try {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, \"Cleaning up connection\");\n } else if (this.ws.readyState === WebSocket.CONNECTING) {\n this.ws.terminate(); // 强制终止正在连接的 WebSocket\n }\n } catch (error) {\n // 忽略关闭时的错误\n this.logger.debug(\"WebSocket 关闭时出现错误(已忽略):\", error);\n }\n\n this.ws = null;\n }\n\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n // 重置连接状态\n this.isConnected = false;\n this.serverInitialized = false;\n }\n\n /**\n * 停止重连\n */\n private stopReconnect(): void {\n if (this.reconnectState.timer) {\n clearTimeout(this.reconnectState.timer);\n this.reconnectState.timer = null;\n }\n }\n\n private handleMessage(message: MCPMessage): void {\n this.logger.debug(\"收到 MCP 消息:\", JSON.stringify(message, null, 2));\n\n if (message.method) {\n this.handleServerRequest(message);\n }\n }\n\n private handleServerRequest(request: MCPMessage): void {\n switch (request.method) {\n case \"initialize\":\n this.sendResponse(request.id, {\n protocolVersion: \"2024-11-05\",\n capabilities: {\n tools: { listChanged: true },\n logging: {},\n },\n serverInfo: {\n name: \"xiaozhi-mcp-server\",\n version: \"1.0.0\",\n },\n });\n this.serverInitialized = true;\n this.logger.info(\"MCP 服务器初始化完成\");\n break;\n\n case \"tools/list\": {\n const toolsList = this.getTools();\n this.sendResponse(request.id, { tools: toolsList });\n this.logger.info(`MCP 工具列表已发送 (${toolsList.length}个工具)`);\n break;\n }\n\n case \"ping\":\n this.sendResponse(request.id, {});\n this.logger.debug(\"回应 MCP ping 消息\");\n break;\n\n default:\n this.logger.warn(`未知的 MCP 请求: ${request.method}`);\n }\n }\n\n private sendResponse(id: number | string | undefined, result: any): void {\n if (this.isConnected && this.ws?.readyState === WebSocket.OPEN) {\n const response: MCPMessage = {\n jsonrpc: \"2.0\",\n id,\n result,\n };\n this.ws.send(JSON.stringify(response));\n }\n }\n\n /**\n * 获取 MCP 服务器状态\n * @returns 服务器状态\n */\n public getStatus(): ProxyMCPServerStatus {\n return {\n connected: this.isConnected,\n initialized: this.serverInitialized,\n url: this.endpointUrl,\n availableTools: this.tools.size,\n connectionState: this.connectionState,\n reconnectAttempts: this.reconnectState.attempts,\n lastError: this.reconnectState.lastError?.message || null,\n };\n }\n\n /**\n * 主动断开 MCP 连接\n */\n public disconnect(): void {\n this.logger.info(\"主动断开 MCP 连接\");\n\n // 标记为手动断开,阻止自动重连\n this.reconnectState.isManualDisconnect = true;\n\n // 停止重连定时器\n this.stopReconnect();\n\n // 清理连接资源\n this.cleanupConnection();\n\n // 设置状态为已断开\n this.connectionState = ConnectionState.DISCONNECTED;\n }\n\n /**\n * 手动重连 MCP 接入点\n */\n public async reconnect(): Promise<void> {\n this.logger.info(\"手动重连 MCP 接入点\");\n\n // 停止自动重连\n this.stopReconnect();\n\n // 重置重连状态\n this.reconnectState.attempts = 0;\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n this.reconnectState.isManualDisconnect = false;\n\n // 清理现有连接\n this.cleanupConnection();\n\n // 尝试连接\n await this.connect();\n }\n\n /**\n * 启用自动重连\n */\n public enableReconnect(): void {\n this.reconnectOptions.enabled = true;\n this.logger.info(\"自动重连已启用\");\n }\n\n /**\n * 禁用自动重连\n */\n public disableReconnect(): void {\n this.reconnectOptions.enabled = false;\n this.stopReconnect();\n this.logger.info(\"自动重连已禁用\");\n }\n\n /**\n * 更新重连配置\n */\n public updateReconnectOptions(options: Partial<ReconnectOptions>): void {\n this.reconnectOptions = { ...this.reconnectOptions, ...options };\n this.logger.info(\"重连配置已更新\", options);\n }\n\n /**\n * 获取重连配置\n */\n public getReconnectOptions(): ReconnectOptions {\n return { ...this.reconnectOptions };\n }\n\n /**\n * 重置重连状态\n */\n public resetReconnectState(): void {\n this.stopReconnect();\n this.reconnectState.attempts = 0;\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n this.reconnectState.lastError = null;\n this.logger.info(\"重连状态已重置\");\n }\n}\n","import {\n SSEClientTransport,\n type SSEClientTransportOptions,\n} from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\";\nimport {\n StreamableHTTPClientTransport,\n type StreamableHTTPClientTransportOptions,\n} from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport { EventSource } from \"eventsource\";\nimport { Logger } from \"../logger.js\";\nimport { type MCPServiceConfig, MCPTransportType } from \"./MCPService.js\";\n\n// 全局 polyfill EventSource(用于 SSE)\nif (typeof global !== \"undefined\" && !global.EventSource) {\n (global as any).EventSource = EventSource;\n}\n\n// Transport 基础接口\nexport interface Transport {\n connect?(): Promise<void>;\n close?(): Promise<void>;\n}\n\n// 创建 logger 实例\nfunction getLogger(): Logger {\n return new Logger().withTag(\"TransportFactory\");\n}\n\n/**\n * 创建 transport 实例\n * @param config MCP 服务配置\n * @returns transport 实例\n */\nexport function createTransport(config: MCPServiceConfig): any {\n const logger = getLogger();\n logger.info(`创建 ${config.type} transport for ${config.name}`);\n\n switch (config.type) {\n case MCPTransportType.STDIO:\n return createStdioTransport(config);\n\n case MCPTransportType.SSE:\n return createSSETransport(config);\n\n case MCPTransportType.MODELSCOPE_SSE:\n return createModelScopeSSETransport(config);\n\n case MCPTransportType.STREAMABLE_HTTP:\n return createStreamableHTTPTransport(config);\n\n default:\n throw new Error(`不支持的传输类型: ${config.type}`);\n }\n}\n\n/**\n * 创建 Stdio transport\n */\nfunction createStdioTransport(config: MCPServiceConfig): StdioClientTransport {\n if (!config.command) {\n throw new Error(\"stdio transport 需要 command 配置\");\n }\n\n return new StdioClientTransport({\n command: config.command,\n args: config.args || [],\n });\n}\n\n/**\n * 创建 SSE transport\n */\nfunction createSSETransport(config: MCPServiceConfig): SSEClientTransport {\n if (!config.url) {\n throw new Error(\"SSE transport 需要 URL 配置\");\n }\n\n const url = new URL(config.url);\n const options = createSSEOptions(config);\n\n return new SSEClientTransport(url, options);\n}\n\n/**\n * 创建 ModelScope SSE transport\n */\nfunction createModelScopeSSETransport(\n config: MCPServiceConfig\n): SSEClientTransport {\n if (!config.url) {\n throw new Error(\"ModelScope SSE transport 需要 URL 配置\");\n }\n\n if (!config.apiKey) {\n throw new Error(\"ModelScope SSE transport 需要 apiKey 配置\");\n }\n\n const url = new URL(config.url);\n const options = createModelScopeSSEOptions(config);\n\n return new SSEClientTransport(url, options);\n}\n\nfunction createStreamableHTTPTransport(\n config: MCPServiceConfig\n): StreamableHTTPClientTransport {\n if (!config.url) {\n throw new Error(\"StreamableHTTP transport 需要 URL 配置\");\n }\n\n const url = new URL(config.url);\n const options = createStreamableHTTPOptions(config);\n return new StreamableHTTPClientTransport(url, options);\n}\n\n/**\n * 创建 SSE 选项\n */\nfunction createSSEOptions(config: MCPServiceConfig): SSEClientTransportOptions {\n const options: any = {};\n\n // 添加认证头\n if (config.apiKey) {\n options.headers = {\n Authorization: `Bearer ${config.apiKey}`,\n ...config.headers,\n };\n } else if (config.headers) {\n options.headers = config.headers;\n }\n\n return options;\n}\n\n/**\n * 创建 ModelScope SSE 选项\n */\nfunction createModelScopeSSEOptions(config: MCPServiceConfig): any {\n const token = config.apiKey!; // 已在调用方验证过\n\n // 如果有自定义SSE选项,使用它们\n if (config.customSSEOptions) {\n return config.customSSEOptions;\n }\n\n // 默认的ModelScope SSE选项配置\n return {\n eventSourceInit: {\n fetch: async (url: string | URL | Request, init?: RequestInit) => {\n // 添加认证头\n const headers = {\n ...init?.headers,\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(url, { ...init, headers });\n },\n },\n requestInit: {\n headers: {\n Authorization: `Bearer ${token}`,\n ...config.headers,\n },\n },\n };\n}\n\nfunction createStreamableHTTPOptions(\n config: MCPServiceConfig\n): StreamableHTTPClientTransportOptions {\n const options: any = {};\n\n // 添加认证头\n if (config.apiKey) {\n options.headers = {\n Authorization: `Bearer ${config.apiKey}`,\n ...config.headers,\n };\n } else if (config.headers) {\n options.headers = config.headers;\n }\n\n return options;\n}\n\n/**\n * 验证配置\n */\nexport function validateConfig(config: MCPServiceConfig): void {\n if (!config.name || typeof config.name !== \"string\") {\n throw new Error(\"配置必须包含有效的 name 字段\");\n }\n\n if (!config.type) {\n throw new Error(\"配置必须包含 type 字段\");\n }\n\n switch (config.type) {\n case MCPTransportType.STDIO:\n if (!config.command) {\n throw new Error(\"stdio 类型需要 command 字段\");\n }\n break;\n\n case MCPTransportType.SSE:\n case MCPTransportType.STREAMABLE_HTTP:\n if (!config.url) {\n throw new Error(`${config.type} 类型需要 url 字段`);\n }\n break;\n\n case MCPTransportType.MODELSCOPE_SSE:\n if (!config.url) {\n throw new Error(\"modelscope-sse 类型需要 url 字段\");\n }\n if (!config.apiKey) {\n throw new Error(\n \"modelscope-sse 类型需要 apiKey 字段。请在配置文件中设置 modelscope.apiKey 或确保服务配置包含 apiKey\"\n );\n }\n break;\n\n default:\n throw new Error(`不支持的传输类型: ${config.type}`);\n }\n}\n\n/**\n * 获取支持的传输类型列表\n */\nexport function getSupportedTypes(): MCPTransportType[] {\n return [\n MCPTransportType.STDIO,\n MCPTransportType.SSE,\n MCPTransportType.MODELSCOPE_SSE,\n MCPTransportType.STREAMABLE_HTTP,\n ];\n}\n\n/**\n * Transport 工厂对象(保持 API 兼容性)\n */\nexport const TransportFactory = {\n create: createTransport,\n validateConfig,\n getSupportedTypes,\n};\n","import { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport type { Tool } from \"@modelcontextprotocol/sdk/types.js\";\nimport { Logger } from \"../logger.js\";\nimport { TransportFactory } from \"./TransportFactory.js\";\n\n// 通信方式枚举\nexport enum MCPTransportType {\n STDIO = \"stdio\",\n SSE = \"sse\",\n STREAMABLE_HTTP = \"streamable-http\",\n MODELSCOPE_SSE = \"modelscope-sse\",\n}\n\n// 连接状态枚举\nexport enum ConnectionState {\n DISCONNECTED = \"disconnected\",\n CONNECTING = \"connecting\",\n CONNECTED = \"connected\",\n RECONNECTING = \"reconnecting\",\n FAILED = \"failed\",\n}\n\n// 重连配置接口\nexport interface ReconnectOptions {\n enabled: boolean;\n maxAttempts: number;\n initialInterval: number;\n maxInterval: number;\n backoffStrategy: \"linear\" | \"exponential\" | \"fixed\";\n backoffMultiplier: number;\n timeout: number;\n jitter: boolean;\n}\n\n// Ping配置接口\nexport interface PingOptions {\n enabled: boolean;\n interval: number; // ping间隔(毫秒)\n timeout: number; // ping超时(毫秒)\n maxFailures: number; // 最大连续失败次数\n startDelay: number; // 连接成功后开始ping的延迟(毫秒)\n}\n\n// ModelScope SSE 自定义选项接口\nexport interface ModelScopeSSEOptions {\n eventSourceInit?: {\n fetch?: (\n url: string | URL | Request,\n init?: RequestInit\n ) => Promise<Response>;\n };\n requestInit?: RequestInit;\n}\n\n// MCPService 配置接口\nexport interface MCPServiceConfig {\n name: string;\n type: MCPTransportType;\n // stdio 配置\n command?: string;\n args?: string[];\n // 网络配置\n url?: string;\n // 认证配置\n apiKey?: string;\n headers?: Record<string, string>;\n // ModelScope 特有配置\n modelScopeAuth?: boolean;\n customSSEOptions?: ModelScopeSSEOptions;\n // 重连配置\n reconnect?: Partial<ReconnectOptions>;\n // ping配置\n ping?: Partial<PingOptions>;\n // 超时配置\n timeout?: number;\n // 重试配置\n retryAttempts?: number;\n}\n\n// MCPService 状态接口\nexport interface MCPServiceStatus {\n name: string;\n connected: boolean;\n initialized: boolean;\n transportType: MCPTransportType;\n toolCount: number;\n lastError?: string;\n reconnectAttempts: number;\n connectionState: ConnectionState;\n // ping状态\n pingEnabled: boolean;\n lastPingTime?: Date;\n pingFailureCount: number;\n isPinging: boolean;\n}\n\n// MCPService 选项接口\nexport interface MCPServiceOptions {\n reconnect?: Partial<ReconnectOptions>;\n}\n\n// 重连状态接口\ninterface ReconnectState {\n attempts: number;\n nextInterval: number;\n timer: NodeJS.Timeout | null;\n lastError: Error | null;\n isManualDisconnect: boolean;\n}\n\n// 工具调用结果接口\nexport interface ToolCallResult {\n content: Array<{\n type: string;\n text: string;\n }>;\n isError?: boolean;\n}\n\n/**\n * MCP 服务类\n * 负责管理单个 MCP 服务的连接、工具管理和调用\n */\nexport class MCPService {\n private config: MCPServiceConfig;\n private client: Client | null = null;\n private transport: any = null;\n private tools: Map<string, Tool> = new Map();\n private connectionState: ConnectionState = ConnectionState.DISCONNECTED;\n private reconnectOptions: ReconnectOptions;\n private reconnectState: ReconnectState;\n private logger: Logger;\n private connectionTimeout: NodeJS.Timeout | null = null;\n private initialized = false;\n\n // Ping相关属性\n private pingOptions: PingOptions;\n private pingTimer: NodeJS.Timeout | null = null;\n private pingFailureCount = 0;\n private lastPingTime: Date | null = null;\n private isPinging = false;\n\n constructor(config: MCPServiceConfig, options?: MCPServiceOptions) {\n this.config = config;\n this.logger = new Logger().withTag(`MCP-${config.name}`);\n\n // 验证配置\n this.validateConfig();\n\n // 初始化重连配置\n this.reconnectOptions = {\n enabled: true,\n maxAttempts: 10,\n initialInterval: 3000,\n maxInterval: 30000,\n backoffStrategy: \"exponential\",\n backoffMultiplier: 1.5,\n timeout: 10000,\n jitter: true,\n ...options?.reconnect,\n ...config.reconnect,\n };\n\n // 初始化ping配置\n this.pingOptions = {\n enabled: true, // 默认启用\n interval: 30000, // 30秒\n timeout: 5000, // 5秒超时\n maxFailures: 3, // 最大连续失败3次\n startDelay: 5000, // 连接成功后5秒开始ping\n ...config.ping,\n };\n\n // 初始化重连状态\n this.reconnectState = {\n attempts: 0,\n nextInterval: this.reconnectOptions.initialInterval,\n timer: null,\n lastError: null,\n isManualDisconnect: false,\n };\n }\n\n /**\n * 验证配置\n */\n private validateConfig(): void {\n // 使用 TransportFactory 进行配置验证\n TransportFactory.validateConfig(this.config);\n }\n\n /**\n * 连接到 MCP 服务\n */\n async connect(): Promise<void> {\n // 如果正在连接中,等待当前连接完成\n if (this.connectionState === ConnectionState.CONNECTING) {\n throw new Error(\"连接正在进行中,请等待连接完成\");\n }\n\n // 清理之前的连接\n this.cleanupConnection();\n\n // 重置手动断开标志\n this.reconnectState.isManualDisconnect = false;\n\n return this.attemptConnection();\n }\n\n /**\n * 尝试建立连接\n */\n private async attemptConnection(): Promise<void> {\n this.connectionState = ConnectionState.CONNECTING;\n this.logger.info(\n `正在连接 MCP 服务: ${this.config.name} (尝试 ${\n this.reconnectState.attempts + 1\n }/${this.reconnectOptions.maxAttempts})`\n );\n\n return new Promise((resolve, reject) => {\n // 设置连接超时\n this.connectionTimeout = setTimeout(() => {\n const error = new Error(\n `连接超时 (${this.reconnectOptions.timeout}ms)`\n );\n this.handleConnectionError(error);\n reject(error);\n }, this.reconnectOptions.timeout);\n\n try {\n this.client = new Client(\n {\n name: `xiaozhi-${this.config.name}-client`,\n version: \"1.0.0\",\n },\n {\n capabilities: {\n tools: {},\n },\n }\n );\n\n // 使用 TransportFactory 创建传输层\n this.transport = TransportFactory.create(this.config);\n\n // 连接到 MCP 服务\n this.client\n .connect(this.transport)\n .then(async () => {\n this.handleConnectionSuccess();\n\n // 获取工具列表\n await this.refreshTools();\n\n resolve();\n })\n .catch((error) => {\n this.handleConnectionError(error);\n reject(error);\n });\n } catch (error) {\n this.handleConnectionError(error as Error);\n reject(error);\n }\n });\n }\n\n /**\n * 处理连接成功\n */\n private handleConnectionSuccess(): void {\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n this.connectionState = ConnectionState.CONNECTED;\n this.initialized = true;\n\n // 重置重连状态\n this.reconnectState.attempts = 0;\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n this.reconnectState.lastError = null;\n\n // 重置ping状态\n this.resetPingState();\n\n this.logger.info(`MCP 服务 ${this.config.name} 连接已建立`);\n\n // 启动ping监控\n this.startPingMonitoring();\n }\n\n /**\n * 处理连接错误\n */\n private handleConnectionError(error: Error): void {\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n this.reconnectState.lastError = error;\n this.logger.error(`MCP 服务 ${this.config.name} 连接错误:`, error.message);\n\n // 清理当前连接\n this.cleanupConnection();\n\n // 检查是否需要重连\n if (this.shouldReconnect()) {\n this.scheduleReconnect();\n } else {\n this.connectionState = ConnectionState.FAILED;\n this.logger.warn(\n `${this.config.name} 已达到最大重连次数 (${this.reconnectOptions.maxAttempts}),停止重连`\n );\n }\n }\n\n /**\n * 检查是否应该重连\n */\n private shouldReconnect(): boolean {\n return (\n this.reconnectOptions.enabled &&\n this.reconnectState.attempts < this.reconnectOptions.maxAttempts &&\n !this.reconnectState.isManualDisconnect\n );\n }\n\n /**\n * 安排重连\n */\n private scheduleReconnect(): void {\n this.connectionState = ConnectionState.RECONNECTING;\n this.reconnectState.attempts++;\n\n // 计算下次重连间隔\n this.calculateNextInterval();\n\n this.logger.info(\n `${this.config.name} 将在 ${this.reconnectState.nextInterval}ms 后进行第 ${this.reconnectState.attempts} 次重连`\n );\n\n // 清理之前的重连定时器\n if (this.reconnectState.timer) {\n clearTimeout(this.reconnectState.timer);\n }\n\n // 设置重连定时器\n this.reconnectState.timer = setTimeout(async () => {\n try {\n await this.attemptConnection();\n } catch (error) {\n // 连接失败会触发 handleConnectionError,无需额外处理\n }\n }, this.reconnectState.nextInterval);\n }\n\n /**\n * 计算下次重连间隔\n */\n private calculateNextInterval(): void {\n let interval: number;\n\n switch (this.reconnectOptions.backoffStrategy) {\n case \"fixed\":\n interval = this.reconnectOptions.initialInterval;\n break;\n\n case \"linear\":\n interval =\n this.reconnectOptions.initialInterval +\n this.reconnectState.attempts *\n this.reconnectOptions.backoffMultiplier *\n 1000;\n break;\n\n case \"exponential\":\n interval =\n this.reconnectOptions.initialInterval *\n this.reconnectOptions.backoffMultiplier **\n (this.reconnectState.attempts - 1);\n break;\n\n default:\n interval = this.reconnectOptions.initialInterval;\n }\n\n // 限制最大间隔\n interval = Math.min(interval, this.reconnectOptions.maxInterval);\n\n // 添加随机抖动\n if (this.reconnectOptions.jitter) {\n const jitterRange = interval * 0.1; // 10% 抖动\n const jitter = (Math.random() - 0.5) * 2 * jitterRange;\n interval += jitter;\n }\n\n this.reconnectState.nextInterval = Math.max(interval, 1000); // 最小1秒\n }\n\n /**\n * 清理连接资源\n */\n private cleanupConnection(): void {\n // 停止ping监控\n this.stopPingMonitoring();\n\n // 清理客户端\n if (this.client) {\n try {\n this.client.close().catch(() => {\n // 忽略关闭时的错误\n });\n } catch (error) {\n // 忽略关闭时的错误\n }\n this.client = null;\n }\n\n // 清理传输层\n this.transport = null;\n\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n // 重置状态\n this.initialized = false;\n }\n\n /**\n * 停止重连\n */\n private stopReconnect(): void {\n if (this.reconnectState.timer) {\n clearTimeout(this.reconnectState.timer);\n this.reconnectState.timer = null;\n }\n }\n\n /**\n * 刷新工具列表\n */\n private async refreshTools(): Promise<void> {\n if (!this.client) {\n throw new Error(\"客户端未初始化\");\n }\n\n try {\n const toolsResult = await this.client.listTools();\n const tools: Tool[] = toolsResult.tools || [];\n\n // 清空现有工具\n this.tools.clear();\n\n // 注册工具到映射表\n for (const tool of tools) {\n this.tools.set(tool.name, tool);\n }\n\n this.logger.info(\n `${this.config.name} 服务加载了 ${tools.length} 个工具: ${tools\n .map((t) => t.name)\n .join(\", \")}`\n );\n } catch (error) {\n this.logger.error(\n `${this.config.name} 获取工具列表失败:`,\n error instanceof Error ? error.message : String(error)\n );\n throw error;\n }\n }\n\n /**\n * 断开连接\n */\n async disconnect(): Promise<void> {\n this.logger.info(`主动断开 MCP 服务 ${this.config.name} 连接`);\n\n // 标记为手动断开,阻止自动重连\n this.reconnectState.isManualDisconnect = true;\n\n // 停止ping监控\n this.stopPingMonitoring();\n\n // 停止重连定时器\n this.stopReconnect();\n\n // 清理连接资源\n this.cleanupConnection();\n\n // 设置状态为已断开\n this.connectionState = ConnectionState.DISCONNECTED;\n }\n\n /**\n * 手动重连\n */\n async reconnect(): Promise<void> {\n this.logger.info(`手动重连 MCP 服务 ${this.config.name}`);\n\n // 停止自动重连\n this.stopReconnect();\n\n // 重置重连状态\n this.reconnectState.attempts = 0;\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n this.reconnectState.isManualDisconnect = false;\n\n // 清理现有连接\n this.cleanupConnection();\n\n // 尝试连接\n await this.connect();\n }\n\n /**\n * 获取工具列表\n */\n getTools(): Tool[] {\n return Array.from(this.tools.values());\n }\n\n /**\n * 调用工具\n */\n async callTool(name: string, arguments_: any): Promise<ToolCallResult> {\n if (!this.client) {\n throw new Error(`服务 ${this.config.name} 未连接`);\n }\n\n if (!this.tools.has(name)) {\n throw new Error(`工具 ${name} 在服务 ${this.config.name} 中不存在`);\n }\n\n this.logger.info(\n `调用 ${this.config.name} 服务的工具 ${name},参数:`,\n JSON.stringify(arguments_)\n );\n\n try {\n const result = await this.client.callTool({\n name,\n arguments: arguments_ || {},\n });\n\n this.logger.info(\n `工具 ${name} 调用成功,结果:`,\n `${JSON.stringify(result).substring(0, 500)}...`\n );\n\n return result as ToolCallResult;\n } catch (error) {\n this.logger.error(\n `工具 ${name} 调用失败:`,\n error instanceof Error ? error.message : String(error)\n );\n throw error;\n }\n }\n\n /**\n * 获取服务配置\n */\n getConfig(): MCPServiceConfig {\n return this.config;\n }\n\n /**\n * 获取服务状态\n */\n getStatus(): MCPServiceStatus {\n return {\n name: this.config.name,\n connected: this.connectionState === ConnectionState.CONNECTED,\n initialized: this.initialized,\n transportType: this.config.type,\n toolCount: this.tools.size,\n lastError: this.reconnectState.lastError?.message,\n reconnectAttempts: this.reconnectState.attempts,\n connectionState: this.connectionState,\n // ping状态\n pingEnabled: this.pingOptions.enabled,\n lastPingTime: this.lastPingTime || undefined,\n pingFailureCount: this.pingFailureCount,\n isPinging: this.isPinging,\n };\n }\n\n /**\n * 检查是否已连接\n */\n isConnected(): boolean {\n return (\n this.connectionState === ConnectionState.CONNECTED && this.initialized\n );\n }\n\n /**\n * 启用自动重连\n */\n enableReconnect(): void {\n this.reconnectOptions.enabled = true;\n this.logger.info(`${this.config.name} 自动重连已启用`);\n }\n\n /**\n * 禁用自动重连\n */\n disableReconnect(): void {\n this.reconnectOptions.enabled = false;\n this.stopReconnect();\n this.logger.info(`${this.config.name} 自动重连已禁用`);\n }\n\n /**\n * 更新重连配置\n */\n updateReconnectOptions(options: Partial<ReconnectOptions>): void {\n this.reconnectOptions = { ...this.reconnectOptions, ...options };\n this.logger.info(`${this.config.name} 重连配置已更新`, options);\n }\n\n /**\n * 获取重连配置\n */\n getReconnectOptions(): ReconnectOptions {\n return { ...this.reconnectOptions };\n }\n\n /**\n * 重置重连状态\n */\n resetReconnectState(): void {\n this.stopReconnect();\n this.reconnectState.attempts = 0;\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n this.reconnectState.lastError = null;\n this.logger.info(`${this.config.name} 重连状态已重置`);\n }\n\n /**\n * 启动ping监控\n */\n private startPingMonitoring(): void {\n if (!this.pingOptions.enabled || this.pingTimer || !this.isConnected()) {\n return;\n }\n\n this.logger.info(\n `${this.config.name} 启动ping监控,间隔: ${this.pingOptions.interval}ms`\n );\n\n // 延迟启动ping,让连接稳定\n setTimeout(() => {\n if (this.isConnected() && !this.pingTimer) {\n this.pingTimer = setInterval(() => {\n this.performPing();\n }, this.pingOptions.interval);\n }\n }, this.pingOptions.startDelay);\n }\n\n /**\n * 停止ping监控\n */\n private stopPingMonitoring(): void {\n if (this.pingTimer) {\n clearInterval(this.pingTimer);\n this.pingTimer = null;\n this.logger.debug(`${this.config.name} 停止ping监控`);\n }\n }\n\n /**\n * 执行ping检查\n */\n private async performPing(): Promise<void> {\n if (!this.client || this.isPinging || !this.isConnected()) {\n return;\n }\n\n this.isPinging = true;\n const startTime = performance.now();\n\n try {\n this.logger.debug(\n `${this.config.name} 发送ping请求(通过listTools检测连接)`\n );\n\n // 使用Promise.race实现超时控制\n // 由于MCP SDK可能没有直接的ping方法,我们使用listTools作为连接检测\n // 这是一个轻量级的操作,可以有效检测连接状态\n const pingPromise = this.client.listTools();\n\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => {\n reject(new Error(`Ping超时 (${this.pingOptions.timeout}ms)`));\n }, this.pingOptions.timeout);\n });\n\n await Promise.race([pingPromise, timeoutPromise]);\n\n const duration = performance.now() - startTime;\n this.handlePingSuccess(duration);\n } catch (error) {\n const duration = performance.now() - startTime;\n this.handlePingFailure(error as Error, duration);\n } finally {\n this.isPinging = false;\n }\n }\n\n /**\n * 处理ping成功\n */\n private handlePingSuccess(duration: number): void {\n this.pingFailureCount = 0;\n this.lastPingTime = new Date();\n this.logger.debug(\n `${this.config.name} ping成功,延迟: ${duration.toFixed(2)}ms`\n );\n }\n\n /**\n * 处理ping失败\n */\n private handlePingFailure(error: Error, duration: number): void {\n this.pingFailureCount++;\n this.logger.warn(\n `${this.config.name} ping失败 (${this.pingFailureCount}/${this.pingOptions.maxFailures}),` +\n `延迟: ${duration.toFixed(2)}ms,错误: ${error.message}`\n );\n\n // 如果连续失败次数达到阈值,触发重连\n if (this.pingFailureCount >= this.pingOptions.maxFailures) {\n this.logger.error(\n `${this.config.name} 连续ping失败达到阈值,触发重连机制`\n );\n\n // 停止ping监控,避免干扰重连过程\n this.stopPingMonitoring();\n\n // 创建连接错误并触发现有的重连机制\n const connectionError = new Error(\n `Ping检测失败,连续失败${this.pingFailureCount}次,连接可能已断开`\n );\n this.handleConnectionError(connectionError);\n }\n }\n\n /**\n * 重置ping状态\n */\n private resetPingState(): void {\n this.pingFailureCount = 0;\n this.lastPingTime = null;\n this.isPinging = false;\n }\n\n /**\n * 启用ping监控\n */\n enablePing(): void {\n this.pingOptions.enabled = true;\n this.logger.info(`${this.config.name} ping监控已启用`);\n\n // 如果当前已连接,立即启动ping监控\n if (this.isConnected()) {\n this.startPingMonitoring();\n }\n }\n\n /**\n * 禁用ping监控\n */\n disablePing(): void {\n this.pingOptions.enabled = false;\n this.stopPingMonitoring();\n this.logger.info(`${this.config.name} ping监控已禁用`);\n }\n\n /**\n * 更新ping配置\n */\n updatePingOptions(options: Partial<PingOptions>): void {\n const wasEnabled = this.pingOptions.enabled;\n this.pingOptions = { ...this.pingOptions, ...options };\n\n this.logger.info(`${this.config.name} ping配置已更新`, options);\n\n // 如果启用状态发生变化,相应地启动或停止监控\n if (wasEnabled !== this.pingOptions.enabled) {\n if (this.pingOptions.enabled && this.isConnected()) {\n this.startPingMonitoring();\n } else if (!this.pingOptions.enabled) {\n this.stopPingMonitoring();\n }\n }\n }\n\n /**\n * 获取ping配置\n */\n getPingOptions(): PingOptions {\n return { ...this.pingOptions };\n }\n}\n","/**\n * 配置适配器\n * 将旧的配置格式转换为新的 MCPServiceConfig 格式,确保向后兼容性\n */\n\nimport type {\n LocalMCPServerConfig,\n MCPServerConfig,\n SSEMCPServerConfig,\n StreamableHTTPMCPServerConfig,\n} from \"../configManager.js\";\nimport { logger as globalLogger } from \"../logger.js\";\nimport type { MCPServiceConfig } from \"../services/MCPService.js\";\nimport { MCPTransportType } from \"../services/MCPService.js\";\n\n// 为配置适配器创建带标签的 logger\nconst logger = globalLogger.withTag(\"ConfigAdapter\");\n\n/**\n * 配置验证错误类\n */\nexport class ConfigValidationError extends Error {\n constructor(\n message: string,\n public readonly configName?: string\n ) {\n super(message);\n this.name = \"ConfigValidationError\";\n }\n}\n\n/**\n * 将旧的 MCPServerConfig 转换为新的 MCPServiceConfig\n */\nexport function convertLegacyToNew(\n serviceName: string,\n legacyConfig: MCPServerConfig\n): MCPServiceConfig {\n logger.debug(`转换配置: ${serviceName}`, legacyConfig);\n\n try {\n // 验证输入参数\n if (!serviceName || typeof serviceName !== \"string\") {\n throw new ConfigValidationError(\"服务名称必须是非空字符串\");\n }\n\n if (!legacyConfig || typeof legacyConfig !== \"object\") {\n throw new ConfigValidationError(\"配置对象不能为空\", serviceName);\n }\n\n // 根据配置类型进行转换\n const newConfig = convertByConfigType(serviceName, legacyConfig);\n\n // 验证转换后的配置\n validateNewConfig(newConfig);\n\n logger.info(`配置转换成功: ${serviceName} -> ${newConfig.type}`);\n return newConfig;\n } catch (error) {\n logger.error(`配置转换失败: ${serviceName}`, error);\n throw error instanceof ConfigValidationError\n ? error\n : new ConfigValidationError(\n `配置转换失败: ${error instanceof Error ? error.message : String(error)}`,\n serviceName\n );\n }\n}\n\n/**\n * 根据配置类型进行转换\n */\nfunction convertByConfigType(\n serviceName: string,\n legacyConfig: MCPServerConfig\n): MCPServiceConfig {\n // 检查是否为本地 stdio 配置\n if (isLocalConfig(legacyConfig)) {\n return convertLocalConfig(serviceName, legacyConfig);\n }\n\n // 检查是否为 SSE 配置\n if (isSSEConfig(legacyConfig)) {\n return convertSSEConfig(serviceName, legacyConfig);\n }\n\n // 检查是否为 Streamable HTTP 配置\n if (isStreamableHTTPConfig(legacyConfig)) {\n return convertStreamableHTTPConfig(serviceName, legacyConfig);\n }\n\n throw new ConfigValidationError(\"无法识别的配置类型\", serviceName);\n}\n\n/**\n * 转换本地 stdio 配置\n */\nfunction convertLocalConfig(\n serviceName: string,\n config: LocalMCPServerConfig\n): MCPServiceConfig {\n if (!config.command) {\n throw new ConfigValidationError(\n \"本地配置必须包含 command 字段\",\n serviceName\n );\n }\n\n return {\n name: serviceName,\n type: MCPTransportType.STDIO,\n command: config.command,\n args: config.args || [],\n // 默认重连配置\n reconnect: {\n enabled: true,\n maxAttempts: 5,\n initialInterval: 3000,\n maxInterval: 30000,\n backoffStrategy: \"exponential\" as const,\n backoffMultiplier: 1.5,\n timeout: 10000,\n jitter: true,\n },\n // 默认 ping 配置\n ping: {\n enabled: true,\n interval: 30000,\n timeout: 5000,\n maxFailures: 3,\n startDelay: 5000,\n },\n timeout: 30000,\n };\n}\n\n/**\n * 转换 SSE 配置\n */\nfunction convertSSEConfig(\n serviceName: string,\n config: SSEMCPServerConfig\n): MCPServiceConfig {\n if (!config.url) {\n throw new ConfigValidationError(\"SSE 配置必须包含 url 字段\", serviceName);\n }\n\n // 检查是否为 ModelScope 服务\n const isModelScope = isModelScopeURL(config.url);\n\n const baseConfig: MCPServiceConfig = {\n name: serviceName,\n type: isModelScope ? MCPTransportType.MODELSCOPE_SSE : MCPTransportType.SSE,\n url: config.url,\n // 默认重连配置\n reconnect: {\n enabled: true,\n maxAttempts: 10,\n initialInterval: 3000,\n maxInterval: 30000,\n backoffStrategy: \"exponential\" as const,\n backoffMultiplier: 1.5,\n timeout: 15000,\n jitter: true,\n },\n // 默认 ping 配置\n ping: {\n enabled: true,\n interval: 30000,\n timeout: 5000,\n maxFailures: 3,\n startDelay: 5000,\n },\n timeout: 30000,\n };\n\n // 如果是 ModelScope 服务,添加特殊配置\n if (isModelScope) {\n baseConfig.modelScopeAuth = true;\n }\n\n return baseConfig;\n}\n\n/**\n * 转换 Streamable HTTP 配置\n */\nfunction convertStreamableHTTPConfig(\n serviceName: string,\n config: StreamableHTTPMCPServerConfig\n): MCPServiceConfig {\n if (!config.url) {\n throw new ConfigValidationError(\n \"Streamable HTTP 配置必须包含 url 字段\",\n serviceName\n );\n }\n\n return {\n name: serviceName,\n type: MCPTransportType.STREAMABLE_HTTP,\n url: config.url,\n // 默认重连配置\n reconnect: {\n enabled: true,\n maxAttempts: 5,\n initialInterval: 3000,\n maxInterval: 30000,\n backoffStrategy: \"exponential\" as const,\n backoffMultiplier: 1.5,\n timeout: 15000,\n jitter: true,\n },\n // 默认 ping 配置\n ping: {\n enabled: false, // HTTP 连接通常不需要 ping\n interval: 60000,\n timeout: 10000,\n maxFailures: 3,\n startDelay: 10000,\n },\n timeout: 30000,\n };\n}\n\n/**\n * 批量转换配置\n */\nexport function convertLegacyConfigBatch(\n legacyConfigs: Record<string, MCPServerConfig>\n): Record<string, MCPServiceConfig> {\n const newConfigs: Record<string, MCPServiceConfig> = {};\n const errors: Array<{ serviceName: string; error: Error }> = [];\n\n for (const [serviceName, legacyConfig] of Object.entries(legacyConfigs)) {\n try {\n newConfigs[serviceName] = convertLegacyToNew(serviceName, legacyConfig);\n } catch (error) {\n errors.push({\n serviceName,\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(({ serviceName, error }) => `${serviceName}: ${error.message}`)\n .join(\"; \");\n throw new ConfigValidationError(`批量配置转换失败: ${errorMessages}`);\n }\n\n logger.info(\n `批量配置转换成功,共转换 ${Object.keys(newConfigs).length} 个服务`\n );\n return newConfigs;\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 * 检查是否为 SSE 配置\n */\nfunction isSSEConfig(config: MCPServerConfig): config is SSEMCPServerConfig {\n return \"type\" in config && config.type === \"sse\" && \"url\" in config;\n}\n\n/**\n * 检查是否为 Streamable HTTP 配置\n */\nfunction isStreamableHTTPConfig(\n config: MCPServerConfig\n): config is StreamableHTTPMCPServerConfig {\n return (\n \"url\" in config &&\n (!(\"type\" in config) || config.type === \"streamable-http\")\n );\n}\n\n/**\n * 检查是否为 ModelScope URL\n */\nfunction isModelScopeURL(url: string): boolean {\n return url.includes(\"modelscope.net\") || url.includes(\"modelscope.cn\");\n}\n\n/**\n * 验证新配置格式\n */\nfunction validateNewConfig(config: MCPServiceConfig): void {\n if (!config.name || typeof config.name !== \"string\") {\n throw new ConfigValidationError(\"配置必须包含有效的 name 字段\");\n }\n\n if (!Object.values(MCPTransportType).includes(config.type)) {\n throw new ConfigValidationError(`无效的传输类型: ${config.type}`);\n }\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 case MCPTransportType.MODELSCOPE_SSE:\n case MCPTransportType.STREAMABLE_HTTP:\n if (!config.url) {\n throw new ConfigValidationError(`${config.type} 配置必须包含 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 if (isSSEConfig(config)) {\n const isModelScope = isModelScopeURL(config.url);\n return `SSE${isModelScope ? \" (ModelScope)\" : \"\"} (${config.url})`;\n }\n if (isStreamableHTTPConfig(config)) {\n return `Streamable HTTP (${config.url})`;\n }\n return \"未知类型\";\n}\n","/**\n * MCP 服务工具函数 - 服务端版本\n * 用于判断 MCP 服务的通信类型和其他相关操作\n */\n\n// 定义通信类型\nexport type MCPCommunicationType = \"stdio\" | \"sse\" | \"streamable-http\";\n\n// 定义 MCP 服务配置类型(与客户端保持一致)\nexport interface LocalMCPServerConfig {\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\nexport interface SSEMCPServerConfig {\n type: \"sse\";\n url: string;\n}\n\nexport interface StreamableHTTPMCPServerConfig {\n type?: \"streamable-http\"; // 可选,因为默认就是 streamable-http\n url: string;\n}\n\nexport type MCPServerConfig =\n | LocalMCPServerConfig\n | SSEMCPServerConfig\n | StreamableHTTPMCPServerConfig;\n\n/**\n * 判断 MCP 服务的通信类型\n *\n * @param serverConfig MCP 服务配置对象\n * @returns 通信类型:'stdio' | 'sse' | 'streamable-http'\n *\n * 判断逻辑:\n * 1. 如果配置对象有 command 字段 → stdio\n * 2. 如果配置对象有 type 字段且值为 \"sse\" → sse\n * 3. 如果配置对象有 url 字段但没有 type 字段,或者 type 字段不是 \"sse\" → streamable-http\n *\n * @example\n * ```typescript\n * // stdio 类型\n * const stdioConfig = {\n * command: \"node\",\n * args: [\"./mcpServers/calculator.js\"]\n * };\n * getMcpServerCommunicationType(stdioConfig); // \"stdio\"\n *\n * // sse 类型\n * const sseConfig = {\n * type: \"sse\" as const,\n * url: \"https://mcp.api-inference.modelscope.net/d3cfd34529ae4e/sse\"\n * };\n * getMcpServerCommunicationType(sseConfig); // \"sse\"\n *\n * // streamable-http 类型\n * const httpConfig = {\n * url: \"https://mcp.amap.com/mcp?key=1ec31da021b2702787841ea4ee822de3\"\n * };\n * getMcpServerCommunicationType(httpConfig); // \"streamable-http\"\n * ```\n */\nexport function getMcpServerCommunicationType(\n serverConfig: MCPServerConfig | Record<string, any>\n): MCPCommunicationType {\n // 参数验证\n if (!serverConfig || typeof serverConfig !== \"object\") {\n throw new Error(\"服务配置必须是一个有效的对象\");\n }\n\n // 1. 检查是否为 stdio 类型(有 command 字段)\n if (\"command\" in serverConfig && typeof serverConfig.command === \"string\") {\n return \"stdio\";\n }\n\n // 2. 检查是否为 sse 类型(有 type: \"sse\" 字段)\n if (\"type\" in serverConfig && serverConfig.type === \"sse\") {\n return \"sse\";\n }\n\n // 3. 检查是否为 streamable-http 类型(有 type: \"streamable-http\" 字段或有 url 字段)\n if (\n (\"type\" in serverConfig && serverConfig.type === \"streamable-http\") ||\n (\"url\" in serverConfig && typeof serverConfig.url === \"string\")\n ) {\n return \"streamable-http\";\n }\n\n // 如果都不匹配,抛出错误\n throw new Error(\n \"无法识别的 MCP 服务配置类型。配置必须包含 command 字段(stdio)、type: 'sse' 字段(sse)或 url 字段(streamable-http)\"\n );\n}\n\n/**\n * 检查 MCP 服务配置是否为 stdio 类型\n */\nexport function isStdioMcpServer(\n serverConfig: MCPServerConfig | Record<string, any>\n): serverConfig is LocalMCPServerConfig {\n return getMcpServerCommunicationType(serverConfig) === \"stdio\";\n}\n\n/**\n * 检查 MCP 服务配置是否为 sse 类型\n */\nexport function isSSEMcpServer(\n serverConfig: MCPServerConfig | Record<string, any>\n): serverConfig is SSEMCPServerConfig {\n return getMcpServerCommunicationType(serverConfig) === \"sse\";\n}\n\n/**\n * 检查 MCP 服务配置是否为 streamable-http 类型\n */\nexport function isStreamableHTTPMcpServer(\n serverConfig: MCPServerConfig | Record<string, any>\n): serverConfig is StreamableHTTPMCPServerConfig {\n return getMcpServerCommunicationType(serverConfig) === \"streamable-http\";\n}\n\n/**\n * 获取 MCP 服务配置的显示名称\n * 用于在日志中显示更友好的通信类型名称\n */\nexport function getMcpServerTypeDisplayName(\n serverConfig: MCPServerConfig | Record<string, any>\n): string {\n const type = getMcpServerCommunicationType(serverConfig);\n\n switch (type) {\n case \"stdio\":\n return \"本地进程 (stdio)\";\n case \"sse\":\n return \"服务器推送 (SSE)\";\n case \"streamable-http\":\n return \"流式 HTTP\";\n default:\n return \"未知类型\";\n }\n}\n\n/**\n * 验证 MCP 服务配置的完整性\n * 根据不同的通信类型验证必需的字段\n */\nexport function validateMcpServerConfig(\n serverName: string,\n serverConfig: any\n): { valid: boolean; error?: string } {\n if (!serverConfig || typeof serverConfig !== \"object\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的配置必须是一个对象`,\n };\n }\n\n try {\n const communicationType = getMcpServerCommunicationType(serverConfig);\n\n switch (communicationType) {\n case \"stdio\":\n if (!serverConfig.command || typeof serverConfig.command !== \"string\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 缺少必需的 command 字段或字段类型不正确`,\n };\n }\n if (!Array.isArray(serverConfig.args)) {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的 args 字段必须是数组`,\n };\n }\n if (serverConfig.env && typeof serverConfig.env !== \"object\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的 env 字段必须是对象`,\n };\n }\n break;\n\n case \"sse\":\n if (serverConfig.type !== \"sse\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的 type 字段必须是 \"sse\"`,\n };\n }\n if (!serverConfig.url || typeof serverConfig.url !== \"string\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 缺少必需的 url 字段或字段类型不正确`,\n };\n }\n break;\n\n case \"streamable-http\":\n if (!serverConfig.url || typeof serverConfig.url !== \"string\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 缺少必需的 url 字段或字段类型不正确`,\n };\n }\n if (serverConfig.type && serverConfig.type !== \"streamable-http\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的 type 字段如果存在,必须是 \"streamable-http\"`,\n };\n }\n break;\n\n default:\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的配置类型无法识别`,\n };\n }\n\n return { valid: true };\n } catch (error) {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的配置无效: ${\n error instanceof Error ? error.message : \"未知错误\"\n }`,\n };\n }\n}\n","import { 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 JSON5 from \"json5\";\nimport * as json5Writer from \"json5-writer\";\nimport { logger } from \"./logger\";\nimport { validateMcpServerConfig } from \"./utils/mcpServerUtils\";\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}\n\n// Streamable HTTP MCP 服务配置\nexport interface StreamableHTTPMCPServerConfig {\n type?: \"streamable-http\"; // 可选,因为默认就是 streamable-http\n url: string;\n}\n\n// 统一的 MCP 服务配置\nexport type MCPServerConfig =\n | LocalMCPServerConfig\n | SSEMCPServerConfig\n | StreamableHTTPMCPServerConfig;\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\nexport interface AppConfig {\n mcpEndpoint: string | string[];\n mcpServers: Record<string, MCPServerConfig>;\n mcpServerConfig?: Record<string, MCPServerToolsConfig>;\n connection?: ConnectionConfig; // 连接配置(可选,用于向后兼容)\n modelscope?: ModelScopeConfig; // ModelScope 配置(可选)\n webUI?: WebUIConfig; // Web UI 配置(可选)\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: any = null; // json5-writer 实例,用于保留 JSON5 注释\n\n private constructor() {\n this.defaultConfigPath = resolve(__dirname, \"xiaozhi.config.default.json\");\n }\n\n /**\n * 获取配置文件路径(动态计算)\n * 支持多种配置文件格式:json5 > jsonc > json\n */\n private getConfigFilePath(): string {\n // 配置文件路径 - 优先使用环境变量指定的目录,否则使用当前工作目录\n const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n\n // 按优先级检查配置文件是否存在\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 = resolve(configDir, fileName);\n if (existsSync(filePath)) {\n return filePath;\n }\n }\n\n // 如果都不存在,返回默认的 JSON 文件路径\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 public configExists(): boolean {\n // 配置文件路径 - 优先使用环境变量指定的目录,否则使用当前工作目录\n const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n\n // 按优先级检查配置文件是否存在\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 = resolve(configDir, fileName);\n if (existsSync(filePath)) {\n return true;\n }\n }\n\n return false;\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(\"默认配置文件 xiaozhi.config.default.json 不存在\");\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 throw new Error(\"配置文件不存在,请先运行 xiaozhi init 初始化配置\");\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 解析配置对象,同时使用 json5-writer 保留注释信息\n config = JSON5.parse(configData) as AppConfig;\n // 创建 json5-writer 实例用于后续保存时保留注释\n this.json5Writer = json5Writer.load(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 if (error instanceof SyntaxError) {\n throw new Error(`配置文件格式错误: ${error.message}`);\n }\n throw error;\n }\n }\n\n /**\n * 验证配置文件结构\n */\n private 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 if (configObj.mcpEndpoint.length === 0) {\n throw new Error(\"配置文件格式错误:mcpEndpoint 数组不能为空\");\n }\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 const validation = validateMcpServerConfig(serverName, serverConfig);\n if (!validation.valid) {\n throw new Error(`配置文件格式错误:${validation.error}`);\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 if (endpoint.length === 0) {\n throw new Error(\"MCP 端点数组不能为空\");\n }\n for (const ep of endpoint) {\n if (!ep || typeof ep !== \"string\") {\n throw new Error(\"MCP 端点数组中的每个元素必须是非空字符串\");\n }\n }\n } else {\n if (!endpoint || typeof endpoint !== \"string\") {\n throw new Error(\"MCP 端点必须是非空字符串\");\n }\n }\n\n const config = this.getMutableConfig();\n config.mcpEndpoint = endpoint;\n this.saveConfig(config);\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 // 不允许删除最后一个端点\n if (currentEndpoints.length === 1) {\n throw new Error(\"不能删除最后一个 MCP 端点\");\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 // 使用统一的验证逻辑\n const validation = validateMcpServerConfig(serverName, serverConfig);\n if (!validation.valid) {\n throw new Error(validation.error || \"服务配置验证失败\");\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.getConfig();\n if (!config.mcpServers[serverName]) {\n throw new Error(`服务 ${serverName} 不存在`);\n }\n\n const newMcpServers = { ...config.mcpServers };\n delete newMcpServers[serverName];\n\n const newConfig = {\n ...config,\n mcpServers: newMcpServers,\n };\n this.saveConfig(newConfig);\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 /**\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 */\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 格式,使用 json5-writer 库保留注释\n try {\n if (this.json5Writer) {\n // 使用 json5-writer 更新配置并保留注释\n this.json5Writer.write(config);\n configContent = this.json5Writer.toSource();\n } else {\n // 如果没有 json5Writer 实例,回退到标准 JSON5\n console.warn(\"没有 json5Writer 实例,回退到标准 JSON5 格式\");\n configContent = JSON5.stringify(config, null, 2);\n }\n } catch (json5WriterError) {\n // 如果 json5-writer 序列化失败,回退到标准 JSON5\n console.warn(\n \"使用 json5-writer 保存失败,回退到标准 JSON5 格式:\",\n json5WriterError\n );\n configContent = JSON5.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 // 通知 Web 界面配置已更新(如果 Web 服务器正在运行)\n this.notifyConfigUpdate(config);\n } catch (error) {\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 /**\n * 更新工具使用统计信息\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 try {\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 toolConfig.usageCount = currentUsageCount + 1;\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 logger.debug(\n `工具使用统计已更新: ${serverName}/${toolName}, 使用次数: ${toolConfig.usageCount}`\n );\n } catch (error) {\n // 错误不应该影响主要的工具调用流程\n logger.error(\n `更新工具使用统计失败 (${serverName}/${toolName}): ${\n error instanceof Error ? error.message : String(error)\n }`\n );\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 /**\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 * 获取 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 = (global as any).__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 /**\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\n// 导出单例实例\nexport const configManager = ConfigManager.getInstance();\n","import chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport ora from \"ora\";\nimport { configManager } from \"./configManager\";\n\n/**\n * MCP 相关的命令行功能\n */\n\n/**\n * 计算字符串的显示宽度(中文字符占2个宽度,英文字符占1个宽度)\n */\nexport function getDisplayWidth(str: string): number {\n let width = 0;\n for (const char of str) {\n // 判断是否为中文字符(包括中文标点符号)\n if (/[\\u4e00-\\u9fff\\u3400-\\u4dbf\\uff00-\\uffef]/.test(char)) {\n width += 2;\n } else {\n width += 1;\n }\n }\n return width;\n}\n\n/**\n * 截断字符串到指定的显示宽度\n */\nexport function truncateToWidth(str: string, maxWidth: number): string {\n if (getDisplayWidth(str) <= maxWidth) {\n return str;\n }\n\n // 如果最大宽度小于等于省略号的宽度,返回空字符串\n if (maxWidth <= 3) {\n return \"\";\n }\n\n let result = \"\";\n let currentWidth = 0;\n let hasAddedChar = false;\n\n for (const char of str) {\n const charWidth = /[\\u4e00-\\u9fff\\u3400-\\u4dbf\\uff00-\\uffef]/.test(char)\n ? 2\n : 1;\n\n // 如果加上当前字符会超出限制\n if (currentWidth + charWidth > maxWidth - 3) {\n // 如果还没有添加任何字符,说明连一个字符都放不下,返回空字符串\n if (!hasAddedChar) {\n return \"\";\n }\n // 否则添加省略号并退出\n result += \"...\";\n break;\n }\n\n result += char;\n currentWidth += charWidth;\n hasAddedChar = true;\n }\n\n return result;\n}\n\n/**\n * 列出所有 MCP 服务\n */\nexport async function listMcpServers(\n options: { tools?: boolean } = {}\n): Promise<void> {\n const spinner = ora(\"获取 MCP 服务列表...\").start();\n\n try {\n const mcpServers = configManager.getMcpServers();\n const serverNames = Object.keys(mcpServers);\n\n if (serverNames.length === 0) {\n spinner.warn(\"未配置任何 MCP 服务\");\n console.log(\n chalk.yellow(\"💡 提示: 使用 'xiaozhi config' 命令配置 MCP 服务\")\n );\n return;\n }\n\n spinner.succeed(`找到 ${serverNames.length} 个 MCP 服务`);\n\n if (options.tools) {\n // 显示所有服务的工具列表\n console.log();\n console.log(chalk.bold(\"MCP 服务工具列表:\"));\n console.log();\n\n // 计算所有工具名称的最大长度,用于动态设置列宽\n let maxToolNameWidth = 8; // 默认最小宽度\n const allToolNames: string[] = [];\n\n for (const serverName of serverNames) {\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolNames = Object.keys(toolsConfig);\n allToolNames.push(...toolNames);\n }\n\n // 计算最长工具名称的显示宽度\n for (const toolName of allToolNames) {\n const width = getDisplayWidth(toolName);\n if (width > maxToolNameWidth) {\n maxToolNameWidth = width;\n }\n }\n\n // 确保工具名称列宽度至少为10,最多为30\n maxToolNameWidth = Math.max(10, Math.min(maxToolNameWidth + 2, 30));\n\n // 使用 cli-table3 创建表格\n const table = new Table({\n head: [\n chalk.bold(\"MCP\"),\n chalk.bold(\"工具名称\"),\n chalk.bold(\"状态\"),\n chalk.bold(\"描述\"),\n ],\n colWidths: [15, maxToolNameWidth, 8, 40], // MCP | 工具名称 | 状态 | 描述\n wordWrap: true,\n style: {\n head: [],\n border: [],\n },\n });\n\n for (const serverName of serverNames) {\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolNames = Object.keys(toolsConfig);\n\n if (toolNames.length === 0) {\n // 服务没有工具时显示提示信息\n table.push([\n chalk.gray(serverName),\n chalk.gray(\"-\"),\n chalk.gray(\"-\"),\n chalk.gray(\"暂未识别到相关工具\"),\n ]);\n } else {\n // 添加服务分隔行\n if (table.length > 0) {\n table.push([{ colSpan: 4, content: \"\" }]);\n }\n\n for (const toolName of toolNames) {\n const toolConfig = toolsConfig[toolName];\n const status = toolConfig.enable\n ? chalk.green(\"启用\")\n : chalk.red(\"禁用\");\n\n // 截断描述到最大32个字符宽度(约16个中文字符)\n const description = truncateToWidth(\n toolConfig.description || \"\",\n 32\n );\n\n // 只显示工具名称,不包含服务名前缀\n table.push([serverName, toolName, status, description]);\n }\n }\n }\n\n console.log(table.toString());\n } else {\n // 只显示服务列表\n console.log();\n console.log(chalk.bold(\"MCP 服务列表:\"));\n console.log();\n\n for (const serverName of serverNames) {\n const serverConfig = mcpServers[serverName];\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolCount = Object.keys(toolsConfig).length;\n const enabledCount = Object.values(toolsConfig).filter(\n (t) => t.enable !== false\n ).length;\n\n console.log(`${chalk.cyan(\"•\")} ${chalk.bold(serverName)}`);\n\n // 检查服务类型并显示相应信息\n if (\"url\" in serverConfig) {\n // URL 类型的服务(SSE 或 Streamable HTTP)\n if (\"type\" in serverConfig && serverConfig.type === \"sse\") {\n console.log(` 类型: ${chalk.gray(\"SSE\")}`);\n } else {\n console.log(` 类型: ${chalk.gray(\"Streamable HTTP\")}`);\n }\n console.log(` URL: ${chalk.gray(serverConfig.url)}`);\n } else {\n // 本地服务\n console.log(\n ` 命令: ${chalk.gray((serverConfig as any).command)} ${chalk.gray(\n (serverConfig as any).args.join(\" \")\n )}`\n );\n }\n if (toolCount > 0) {\n console.log(\n ` 工具: ${chalk.green(enabledCount)} 启用 / ${chalk.yellow(\n toolCount\n )} 总计`\n );\n } else {\n console.log(` 工具: ${chalk.gray(\"未扫描 (请先启动服务)\")}`);\n }\n console.log();\n }\n }\n\n console.log(chalk.gray(\"💡 提示:\"));\n console.log(chalk.gray(\" - 使用 'xiaozhi mcp list --tools' 查看所有工具\"));\n console.log(\n chalk.gray(\" - 使用 'xiaozhi mcp <服务名> list' 查看指定服务的工具\")\n );\n console.log(\n chalk.gray(\n \" - 使用 'xiaozhi mcp <服务名> <工具名> enable/disable' 启用/禁用工具\"\n )\n );\n } catch (error) {\n spinner.fail(\"获取 MCP 服务列表失败\");\n console.error(\n chalk.red(\n `错误: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n process.exit(1);\n }\n}\n\n/**\n * 列出指定服务的工具\n */\nexport async function listServerTools(serverName: string): Promise<void> {\n const spinner = ora(`获取 ${serverName} 服务的工具列表...`).start();\n\n try {\n const mcpServers = configManager.getMcpServers();\n\n if (!mcpServers[serverName]) {\n spinner.fail(`服务 '${serverName}' 不存在`);\n console.log(\n chalk.yellow(\"💡 提示: 使用 'xiaozhi mcp list' 查看所有可用服务\")\n );\n return;\n }\n\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolNames = Object.keys(toolsConfig);\n\n if (toolNames.length === 0) {\n spinner.warn(`服务 '${serverName}' 暂无工具信息`);\n console.log(chalk.yellow(\"💡 提示: 请先启动服务以扫描工具列表\"));\n return;\n }\n\n spinner.succeed(`服务 '${serverName}' 共有 ${toolNames.length} 个工具`);\n\n console.log();\n console.log(chalk.bold(`${serverName} 服务工具列表:`));\n console.log();\n\n // 使用 cli-table3 创建表格\n const table = new Table({\n head: [chalk.bold(\"工具名称\"), chalk.bold(\"状态\"), chalk.bold(\"描述\")],\n colWidths: [30, 8, 50], // 工具名称 | 状态 | 描述\n wordWrap: true,\n style: {\n head: [],\n border: [],\n },\n });\n\n for (const toolName of toolNames) {\n const toolConfig = toolsConfig[toolName];\n const status = toolConfig.enable\n ? chalk.green(\"启用\")\n : chalk.red(\"禁用\");\n\n // 截断描述到最大40个字符宽度(约20个中文字符)\n const description = truncateToWidth(toolConfig.description || \"\", 40);\n\n table.push([toolName, status, description]);\n }\n\n console.log(table.toString());\n\n console.log();\n console.log(chalk.gray(\"💡 提示:\"));\n console.log(\n chalk.gray(\n ` - 使用 'xiaozhi mcp ${serverName} <工具名> enable' 启用工具`\n )\n );\n console.log(\n chalk.gray(\n ` - 使用 'xiaozhi mcp ${serverName} <工具名> disable' 禁用工具`\n )\n );\n } catch (error) {\n spinner.fail(\"获取工具列表失败\");\n console.error(\n chalk.red(\n `错误: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n process.exit(1);\n }\n}\n\n/**\n * 启用或禁用工具\n */\nexport async function setToolEnabled(\n serverName: string,\n toolName: string,\n enabled: boolean\n): Promise<void> {\n const action = enabled ? \"启用\" : \"禁用\";\n const spinner = ora(`${action}工具 ${serverName}/${toolName}...`).start();\n\n try {\n const mcpServers = configManager.getMcpServers();\n\n if (!mcpServers[serverName]) {\n spinner.fail(`服务 '${serverName}' 不存在`);\n console.log(\n chalk.yellow(\"💡 提示: 使用 'xiaozhi mcp list' 查看所有可用服务\")\n );\n return;\n }\n\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n\n if (!toolsConfig[toolName]) {\n spinner.fail(`工具 '${toolName}' 在服务 '${serverName}' 中不存在`);\n console.log(\n chalk.yellow(\n `💡 提示: 使用 'xiaozhi mcp ${serverName} list' 查看该服务的所有工具`\n )\n );\n return;\n }\n\n // 更新工具状态\n configManager.setToolEnabled(\n serverName,\n toolName,\n enabled,\n toolsConfig[toolName].description\n );\n\n spinner.succeed(\n `成功${action}工具 ${chalk.cyan(serverName)}/${chalk.cyan(toolName)}`\n );\n\n console.log();\n console.log(chalk.gray(\"💡 提示: 工具状态更改将在下次启动服务时生效\"));\n } catch (error) {\n spinner.fail(`${action}工具失败`);\n console.error(\n chalk.red(\n `错误: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n process.exit(1);\n }\n}\n","#!/usr/bin/env node\n\n/**\n * MCP 服务管理器\n * 使用 MCPService 实例管理多个 MCP 服务\n * 专注于实例管理、工具聚合和路由调用\n */\n\nimport type { Tool } from \"@modelcontextprotocol/sdk/types.js\";\nimport { Logger } from \"../logger.js\";\nimport { MCPService, type MCPServiceConfig, MCPTransportType } from \"./MCPService.js\";\nimport { configManager } from \"../configManager.js\";\n\n// 工具信息接口(保持向后兼容)\ninterface ToolInfo {\n serviceName: string;\n originalName: string;\n tool: Tool;\n}\n\n// 服务状态接口(保持向后兼容)\ninterface ServiceStatus {\n connected: boolean;\n clientName: string;\n}\n\n// 管理器状态接口(保持向后兼容)\ninterface ManagerStatus {\n services: Record<string, ServiceStatus>;\n totalTools: number;\n availableTools: string[];\n}\n\n// 工具调用结果接口(保持向后兼容)\ninterface ToolCallResult {\n content: Array<{\n type: string;\n text: string;\n }>;\n isError?: boolean;\n}\n\nexport class MCPServiceManager {\n private services: Map<string, MCPService> = new Map();\n private configs: Record<string, MCPServiceConfig> = {};\n private logger: Logger;\n private tools: Map<string, ToolInfo> = new Map(); // 缓存工具信息,保持向后兼容\n\n /**\n * 创建 MCPServiceManager 实例\n * @param configs 可选的初始服务配置\n */\n constructor(configs?: Record<string, MCPServiceConfig>) {\n this.logger = new Logger().withTag(\"MCPManager\");\n this.configs = configs || {};\n }\n\n /**\n * 启动所有 MCP 服务\n */\n async startAllServices(): Promise<void> {\n this.logger.info(\"正在启动所有 MCP 服务...\");\n\n const configEntries = Object.entries(this.configs);\n if (configEntries.length === 0) {\n this.logger.warn(\n \"没有配置任何 MCP 服务,请使用 addServiceConfig() 添加服务配置\"\n );\n return;\n }\n\n for (const [serviceName] of configEntries) {\n await this.startService(serviceName);\n }\n\n this.logger.info(\"所有 MCP 服务启动完成\");\n }\n\n /**\n * 启动单个 MCP 服务\n */\n async startService(serviceName: string): Promise<void> {\n this.logger.info(`启动 MCP 服务: ${serviceName}`);\n\n const config = this.configs[serviceName];\n if (!config) {\n throw new Error(`未找到服务配置: ${serviceName}`);\n }\n\n try {\n // 如果服务已存在,先停止它\n if (this.services.has(serviceName)) {\n await this.stopService(serviceName);\n }\n\n // 创建 MCPService 实例\n const service = new MCPService(config);\n\n // 连接到服务\n await service.connect();\n\n // 存储服务实例\n this.services.set(serviceName, service);\n\n // 更新工具缓存\n await this.refreshToolsCache();\n\n const tools = service.getTools();\n this.logger.info(\n `${serviceName} 服务启动成功,加载了 ${tools.length} 个工具:`,\n tools.map((t) => t.name).join(\", \")\n );\n } catch (error) {\n this.logger.error(\n `启动 ${serviceName} 服务失败:`,\n (error as Error).message\n );\n throw error;\n }\n }\n\n /**\n * 停止单个服务\n */\n async stopService(serviceName: string): Promise<void> {\n this.logger.info(`停止 MCP 服务: ${serviceName}`);\n\n const service = this.services.get(serviceName);\n if (!service) {\n this.logger.warn(`服务 ${serviceName} 不存在或未启动`);\n return;\n }\n\n try {\n await service.disconnect();\n this.services.delete(serviceName);\n\n // 更新工具缓存\n await this.refreshToolsCache();\n\n this.logger.info(`${serviceName} 服务已停止`);\n } catch (error) {\n this.logger.error(\n `停止 ${serviceName} 服务失败:`,\n (error as Error).message\n );\n throw error;\n }\n }\n\n /**\n * 刷新工具缓存\n */\n private async refreshToolsCache(): Promise<void> {\n this.tools.clear();\n\n for (const [serviceName, service] of this.services) {\n if (service.isConnected()) {\n const tools = service.getTools();\n for (const tool of tools) {\n const toolKey = `${serviceName}__${tool.name}`;\n this.tools.set(toolKey, {\n serviceName,\n originalName: tool.name,\n tool,\n });\n }\n }\n }\n }\n\n /**\n * 获取所有可用工具\n */\n getAllTools(): Array<{\n name: string;\n description: string;\n inputSchema: any;\n serviceName: string;\n originalName: string;\n }> {\n const allTools: Array<{\n name: string;\n description: string;\n inputSchema: any;\n serviceName: string;\n originalName: string;\n }> = [];\n\n for (const [toolKey, toolInfo] of this.tools) {\n allTools.push({\n name: toolKey,\n description: toolInfo.tool.description || \"\",\n inputSchema: toolInfo.tool.inputSchema,\n serviceName: toolInfo.serviceName,\n originalName: toolInfo.originalName,\n });\n }\n return allTools;\n }\n\n /**\n * 调用 MCP 工具\n */\n async callTool(toolName: string, arguments_: any): Promise<ToolCallResult> {\n this.logger.info(`调用工具: ${toolName},参数:`, arguments_);\n\n const toolInfo = this.tools.get(toolName);\n if (!toolInfo) {\n throw new Error(`未找到工具: ${toolName}`);\n }\n\n const service = this.services.get(toolInfo.serviceName);\n if (!service) {\n throw new Error(`服务 ${toolInfo.serviceName} 不可用`);\n }\n\n if (!service.isConnected()) {\n throw new Error(`服务 ${toolInfo.serviceName} 未连接`);\n }\n\n try {\n const result = await service.callTool(\n toolInfo.originalName,\n arguments_ || {}\n );\n\n this.logger.info(`工具 ${toolName} 调用成功,结果:`, result);\n return result as ToolCallResult;\n } catch (error) {\n this.logger.error(`工具 ${toolName} 调用失败:`, (error as Error).message);\n throw error;\n }\n }\n\n /**\n * 停止所有服务\n */\n async stopAllServices(): Promise<void> {\n this.logger.info(\"正在停止所有 MCP 服务...\");\n\n // 停止所有服务实例\n for (const [serviceName, service] of this.services) {\n try {\n await service.disconnect();\n this.logger.info(`${serviceName} 服务已停止`);\n } catch (error) {\n this.logger.error(\n `停止 ${serviceName} 服务失败:`,\n (error as Error).message\n );\n }\n }\n\n this.services.clear();\n this.tools.clear();\n\n this.logger.info(\"所有 MCP 服务已停止\");\n }\n\n /**\n * 获取服务状态\n */\n getStatus(): ManagerStatus {\n const status: ManagerStatus = {\n services: {},\n totalTools: this.tools.size,\n availableTools: Array.from(this.tools.keys()),\n };\n\n for (const [serviceName, service] of this.services) {\n const serviceStatus = service.getStatus();\n status.services[serviceName] = {\n connected: serviceStatus.connected,\n clientName: `xiaozhi-${serviceName}-client`,\n };\n }\n\n return status;\n }\n\n /**\n * 获取指定服务实例\n */\n getService(name: string): MCPService | undefined {\n return this.services.get(name);\n }\n\n /**\n * 获取所有服务实例\n */\n getAllServices(): Map<string, MCPService> {\n return new Map(this.services);\n }\n\n /**\n * 增强服务配置\n * 根据服务类型添加必要的全局配置\n */\n private enhanceServiceConfig(config: MCPServiceConfig): MCPServiceConfig {\n const enhancedConfig = { ...config };\n\n try {\n // 处理 ModelScope SSE 服务\n if (config.type === MCPTransportType.MODELSCOPE_SSE) {\n const modelScopeApiKey = configManager.getModelScopeApiKey();\n if (modelScopeApiKey) {\n enhancedConfig.apiKey = modelScopeApiKey;\n this.logger.info(`为 ${config.name} 服务添加 ModelScope API Key`);\n } else {\n this.logger.warn(`${config.name} 服务需要 ModelScope API Key,但未在配置中找到`);\n throw new Error(`ModelScope SSE 服务 ${config.name} 需要 API Key,请在配置文件中设置 modelscope.apiKey`);\n }\n }\n\n return enhancedConfig;\n } catch (error) {\n this.logger.error(`配置增强失败: ${config.name}`, error);\n throw error;\n }\n }\n\n /**\n * 添加服务配置(重载方法以支持两种调用方式)\n */\n addServiceConfig(name: string, config: MCPServiceConfig): void;\n addServiceConfig(config: MCPServiceConfig): void;\n addServiceConfig(\n nameOrConfig: string | MCPServiceConfig,\n config?: MCPServiceConfig\n ): void {\n let finalConfig: MCPServiceConfig;\n let serviceName: string;\n\n if (typeof nameOrConfig === \"string\" && config) {\n // 两参数版本\n serviceName = nameOrConfig;\n finalConfig = config;\n } else if (typeof nameOrConfig === \"object\") {\n // 单参数版本\n serviceName = nameOrConfig.name;\n finalConfig = nameOrConfig;\n } else {\n throw new Error(\"Invalid arguments for addServiceConfig\");\n }\n\n // 增强配置\n const enhancedConfig = this.enhanceServiceConfig(finalConfig);\n\n // 存储增强后的配置\n this.configs[serviceName] = enhancedConfig;\n this.logger.info(`已添加并增强服务配置: ${serviceName}`);\n }\n\n /**\n * 更新服务配置\n */\n updateServiceConfig(name: string, config: MCPServiceConfig): void {\n // 增强配置\n const enhancedConfig = this.enhanceServiceConfig(config);\n\n // 存储增强后的配置\n this.configs[name] = enhancedConfig;\n this.logger.info(`已更新并增强服务配置: ${name}`);\n }\n\n /**\n * 移除服务配置\n */\n removeServiceConfig(name: string): void {\n delete this.configs[name];\n this.logger.info(`已移除服务配置: ${name}`);\n }\n}\n\nexport default MCPServiceManager;\n","import { type ChildProcess, spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { EventEmitter } from \"node:events\";\nimport fs from \"node:fs\";\nimport type { Server } from \"node:http\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport express from \"express\";\nimport { ProxyMCPServer } from \"../ProxyMCPServer.js\";\nimport { configManager } from \"../configManager.js\";\nimport { logger as globalLogger } from \"../logger.js\";\nimport { MCPServiceManager } from \"./MCPServiceManager.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nconst logger = globalLogger.withTag(\"mcp-server\");\nconst MCP_SERVER_PROXY_FILENAME = \"mcpServerProxy.js\";\nconst MAX_SEARCH_LEVELS = 5; // 查找 mcpServerProxy.js 文件时的最大目录层级\n\ninterface SSEClient {\n id: string;\n sessionId: string;\n response: express.Response;\n}\n\nexport class MCPServer extends EventEmitter {\n private app: express.Application;\n private server: Server | null = null;\n private clients: Map<string, SSEClient> = new Map();\n private mcpProxy: ChildProcess | null = null;\n private proxyMCPServer: ProxyMCPServer | null = null;\n private mcpServiceManager: MCPServiceManager | null = null;\n private mcpProxyPath: string | null = null; // 缓存MCP代理路径\n private port: number;\n\n constructor(port = 3000) {\n super();\n this.port = port;\n this.app = express();\n this.setupMiddleware();\n this.setupRoutes();\n }\n\n private setupMiddleware(): void {\n this.app.use(express.json());\n this.app.use(express.urlencoded({ extended: true }));\n\n // 为MCP客户端设置CORS\n this.app.use((req, res, next) => {\n res.header(\"Access-Control-Allow-Origin\", \"*\");\n res.header(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n res.header(\"Access-Control-Allow-Headers\", \"Content-Type, Accept\");\n res.header(\"Cache-Control\", \"no-cache\");\n next();\n });\n }\n\n private setupRoutes(): void {\n // MCP协议的SSE端点\n this.app.get(\"/sse\", (req, res) => {\n const clientId = Date.now().toString();\n const sessionId = randomUUID();\n\n // 设置SSE头部(匹配SDK实现)\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache, no-transform\");\n res.setHeader(\"Connection\", \"keep-alive\");\n res.setHeader(\"X-Accel-Buffering\", \"no\");\n\n // 使用sessionId注册客户端\n this.clients.set(sessionId, { id: clientId, sessionId, response: res });\n logger.info(`MCP客户端已连接: ${clientId} (会话: ${sessionId})`);\n\n // 首先发送端点事件(SDK标准)\n res.write(`event: endpoint\\ndata: /messages?sessionId=${sessionId}\\n\\n`);\n\n // 处理客户端断开连接\n req.on(\"close\", () => {\n this.clients.delete(sessionId);\n logger.info(`MCP客户端已断开连接: ${clientId} (会话: ${sessionId})`);\n });\n });\n\n // SSE传输的消息端点(MCP SDK标准)\n this.app.post(\"/messages\", async (req, res) => {\n try {\n const sessionId = req.query.sessionId as string;\n const message = req.body;\n\n logger.info(\n `通过SSE传输收到消息 (会话: ${sessionId}):`,\n JSON.stringify(message)\n );\n\n if (!sessionId || !this.clients.has(sessionId)) {\n res.status(400).json({\n jsonrpc: \"2.0\",\n error: {\n code: -32600,\n message: \"无效或缺少sessionId\",\n },\n id: message.id,\n });\n return;\n }\n\n if (!this.mcpProxy) {\n res.status(503).json({\n jsonrpc: \"2.0\",\n error: {\n code: -32603,\n message: \"MCP代理未运行\",\n },\n id: message.id,\n });\n return;\n }\n\n // 检查这是否是通知(没有id字段意味着这是通知)\n if (message.id === undefined) {\n // 这是通知,转发但不等待响应\n logger.info(`转发通知: ${message.method}`);\n this.mcpProxy!.stdin!.write(`${JSON.stringify(message)}\\n`);\n\n // 立即发送202 Accepted以响应通知\n res.status(202).send();\n } else {\n // 这是请求,转发并等待响应\n const response = await this.forwardToProxy(message);\n\n // 通过SSE将响应发送到特定客户端\n const client = this.clients.get(sessionId);\n if (client) {\n this.sendToClient(client, response);\n }\n\n // 发送202 Accepted以确认收到(SDK标准)\n res.status(202).send();\n }\n } catch (error) {\n logger.error(\"SSE消息错误:\", error);\n res.status(500).json({\n jsonrpc: \"2.0\",\n error: {\n code: -32603,\n message: error instanceof Error ? error.message : \"内部错误\",\n },\n id: req.body.id,\n });\n }\n });\n\n // 用于直接RPC通信的JSON-RPC端点(遗留)\n this.app.post(\"/rpc\", async (req, res) => {\n try {\n const message = req.body;\n logger.debug(\"收到RPC消息:\", message);\n\n if (!this.mcpProxy) {\n res.status(503).json({\n jsonrpc: \"2.0\",\n error: {\n code: -32603,\n message: \"MCP代理未运行\",\n },\n id: message.id,\n });\n return;\n }\n\n // 转发到mcpServerProxy\n const response = await this.forwardToProxy(message);\n res.json(response);\n } catch (error) {\n logger.error(\"RPC错误:\", error);\n res.status(500).json({\n jsonrpc: \"2.0\",\n error: {\n code: -32603,\n message: error instanceof Error ? error.message : \"内部错误\",\n },\n id: req.body.id,\n });\n }\n });\n\n // 健康检查\n this.app.get(\"/health\", (req, res) => {\n res.json({\n status: \"ok\",\n mode: \"mcp-server\",\n proxy: this.mcpProxy ? \"running\" : \"stopped\",\n clients: this.clients.size,\n });\n });\n }\n\n private responseBuffer = \"\";\n private pendingRequests = new Map<\n number | string,\n {\n resolve: (value: any) => void;\n reject: (error: any) => void;\n timeoutId: NodeJS.Timeout;\n }\n >();\n\n private async forwardToProxy(message: any): Promise<any> {\n return new Promise((resolve, reject) => {\n if (!this.mcpProxy || !this.mcpProxy.stdin || !this.mcpProxy.stdout) {\n reject(new Error(\"MCP代理不可用\"));\n return;\n }\n\n // 设置超时\n const timeoutId = setTimeout(() => {\n this.pendingRequests.delete(message.id);\n logger.warn(\n `消息超时 id: ${message.id}, 方法: ${message.method} - 如果响应已通过SSE发送,这是正常的`\n );\n // 不要以错误拒绝,而是用超时指示器解析\n resolve({\n jsonrpc: \"2.0\",\n id: message.id,\n result: {\n _timeout: true,\n message: \"响应可能已通过SSE发送\",\n },\n });\n }, 30000);\n\n // 存储待处理请求\n this.pendingRequests.set(message.id, { resolve, reject, timeoutId });\n\n // 记录正在发送的消息\n logger.info(`转发消息到代理: ${JSON.stringify(message)}`);\n this.mcpProxy.stdin.write(`${JSON.stringify(message)}\\n`);\n });\n }\n\n private handleProxyResponse(data: Buffer): void {\n try {\n // 在缓冲区中累积数据\n this.responseBuffer += data.toString();\n\n // 处理完整行\n const lines = this.responseBuffer.split(\"\\n\");\n this.responseBuffer = lines.pop() || \"\"; // 将不完整的行保留在缓冲区中\n\n for (const line of lines) {\n if (line.trim()) {\n try {\n const response = JSON.parse(line);\n logger.debug(`收到代理响应: ${line.substring(0, 200)}...`);\n\n // 检查这是否是对待处理请求的响应\n if (\n response.id !== undefined &&\n this.pendingRequests.has(response.id)\n ) {\n const pendingRequest = this.pendingRequests.get(response.id)!;\n clearTimeout(pendingRequest.timeoutId);\n this.pendingRequests.delete(response.id);\n pendingRequest.resolve(response);\n }\n } catch (e) {\n // 跳过无效的JSON行\n logger.debug(`来自代理的非JSON行: ${line}`);\n }\n }\n }\n } catch (error) {\n logger.error(\"处理代理响应时出错:\", error);\n }\n }\n\n private sendToClient(client: SSEClient, message: any): void {\n try {\n // 使用事件:消息格式(SDK标准)\n const data = `event: message\\ndata: ${JSON.stringify(message)}\\n\\n`;\n client.response.write(data);\n } catch (error) {\n logger.error(`发送到客户端 ${client.id} 失败:`, error);\n this.clients.delete(client.sessionId);\n }\n }\n\n private broadcastToClients(message: any): void {\n for (const client of this.clients.values()) {\n this.sendToClient(client, message);\n }\n }\n\n public async start(): Promise<void> {\n try {\n // 并行启动mcpServerProxy和HTTP服务器\n // 在接受其他客户端连接之前,我们不需要等待MCP客户端连接到xiaozhi.me\n const results = await Promise.allSettled([\n this.startMCPProxy(),\n new Promise<void>((resolve) => {\n // 启动HTTP服务器\n this.server = this.app.listen(this.port, \"0.0.0.0\", () => {\n logger.info(`MCP服务器监听端口 ${this.port} (所有网络接口)`);\n logger.info(`SSE端点: http://0.0.0.0:${this.port}/sse`);\n logger.info(`消息端点: http://0.0.0.0:${this.port}/messages`);\n logger.info(`RPC端点: http://0.0.0.0:${this.port}/rpc`);\n logger.info(`本地访问: http://localhost:${this.port}`);\n resolve();\n });\n }),\n ]);\n\n // 检查 MCP 代理启动结果\n const [mcpProxyResult, httpServerResult] = results;\n if (mcpProxyResult.status === \"rejected\") {\n logger.error(\"MCP代理启动失败:\", mcpProxyResult.reason);\n }\n\n if (httpServerResult.status === \"rejected\") {\n logger.error(\"HTTP服务器启动失败:\", httpServerResult.reason);\n throw httpServerResult.reason;\n }\n\n // 启动MCP客户端连接到xiaozhi.me(不要阻塞服务器启动)\n this.startMCPClient().catch((error) => {\n logger.error(\"启动连接到xiaozhi.me的MCP客户端失败:\", error);\n });\n\n this.emit(\"started\");\n } catch (error) {\n logger.error(\"启动MCP服务器失败:\", error);\n throw error;\n }\n }\n\n private findMCPProxyPath(): string {\n // 如果已经缓存了路径,直接返回\n if (this.mcpProxyPath) {\n return this.mcpProxyPath;\n }\n\n // 由于 tsup 打包的原因,import.meta.url 可能不准确\n // 我们需要找到 mcpServerProxy.js 的正确位置\n\n // 首先尝试使用环境变量指定的路径(主要用于测试环境)\n // MCP_SERVER_PROXY_PATH: 指定 mcpServerProxy.js 文件的完整路径\n // 在测试环境中,可以设置此环境变量来直接指定文件位置\n if (process.env.MCP_SERVER_PROXY_PATH) {\n // 验证路径是否安全,防止路径遍历攻击\n const normalizedPath = path.normalize(process.env.MCP_SERVER_PROXY_PATH);\n const resolvedPath = path.resolve(normalizedPath);\n\n // 定义允许的目录白名单(仅限项目目录和系统临时目录)\n const allowedBaseDirectories = [\n __dirname, // 当前模块目录\n path.join(__dirname, \"..\"), // 上级目录\n path.join(__dirname, \"..\", \"..\"), // 项目根目录\n path.join(__dirname, \"..\", \"..\", \"dist\"), // 项目dist目录\n os.tmpdir(), // 系统临时目录\n ];\n\n // 确保路径在白名单目录内且具有正确的文件名\n if (\n fs.existsSync(resolvedPath) &&\n path.basename(resolvedPath) === MCP_SERVER_PROXY_FILENAME &&\n allowedBaseDirectories.some((dir) => resolvedPath.startsWith(dir))\n ) {\n this.mcpProxyPath = resolvedPath;\n return this.mcpProxyPath;\n }\n\n logger.warn(`MCP_SERVER_PROXY_PATH 路径不安全: ${normalizedPath}`);\n throw new Error(\n `指定的 MCP 代理路径不存在或不安全: ${process.env.MCP_SERVER_PROXY_PATH}`\n );\n }\n\n // 方法1:尝试从当前脚本的目录开始查找\n const currentScript = fileURLToPath(import.meta.url);\n let searchDir = path.dirname(currentScript);\n\n // 向上查找直到找到 mcpServerProxy.js\n let mcpProxyPath: string | null = null;\n for (let i = 0; i < MAX_SEARCH_LEVELS; i++) {\n // 最多向上查找MAX_SEARCH_LEVELS级\n const testPath = path.join(searchDir, MCP_SERVER_PROXY_FILENAME);\n if (fs.existsSync(testPath)) {\n mcpProxyPath = testPath;\n break;\n }\n // 也检查 dist 目录\n const distPath = path.join(searchDir, \"dist\", MCP_SERVER_PROXY_FILENAME);\n if (fs.existsSync(distPath)) {\n mcpProxyPath = distPath;\n break;\n }\n searchDir = path.dirname(searchDir);\n }\n\n // 如果还没找到,尝试从项目根目录查找\n if (!mcpProxyPath) {\n const projectRoot = path.resolve(__dirname, \"..\", \"..\");\n const rootDistPath = path.join(\n projectRoot,\n \"dist\",\n MCP_SERVER_PROXY_FILENAME\n );\n if (fs.existsSync(rootDistPath)) {\n mcpProxyPath = rootDistPath;\n }\n }\n\n if (!mcpProxyPath) {\n throw new Error(`在项目结构中找不到 ${MCP_SERVER_PROXY_FILENAME}`);\n }\n\n // 缓存并返回路径\n this.mcpProxyPath = mcpProxyPath;\n return this.mcpProxyPath;\n }\n\n private async startMCPProxy(): Promise<void> {\n const mcpProxyPath = this.findMCPProxyPath();\n logger.info(`正在启动MCP代理: ${mcpProxyPath}`);\n\n this.mcpProxy = spawn(\"node\", [mcpProxyPath], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: {\n ...process.env,\n MCP_SERVER_MODE: \"true\",\n XIAOZHI_CONFIG_DIR: process.env.XIAOZHI_CONFIG_DIR || process.cwd(),\n },\n });\n\n this.mcpProxy.on(\"error\", (error) => {\n logger.error(\"MCP代理错误:\", error);\n });\n\n this.mcpProxy.on(\"exit\", (code, signal) => {\n logger.warn(`MCP代理退出,代码 ${code},信号 ${signal}`);\n this.mcpProxy = null;\n });\n\n if (this.mcpProxy.stderr) {\n this.mcpProxy.stderr.on(\"data\", (data) => {\n const message = data.toString().trim();\n // mcpServerProxy 使用 logger 输出日志到 stderr,这些不是错误\n // 只有真正的错误信息才应该被标记为 ERROR\n if (\n message.includes(\"[ERROR]\") ||\n message.includes(\"Error:\") ||\n message.includes(\"Failed\")\n ) {\n logger.error(\"MCP代理stderr:\", message);\n } else {\n // 将正常的日志信息作为 info 级别输出\n logger.info(\"MCP代理输出:\", message);\n }\n });\n }\n\n // 为响应设置全局stdout处理器\n if (this.mcpProxy.stdout) {\n this.mcpProxy.stdout.on(\"data\", (data: Buffer) => {\n this.handleProxyResponse(data);\n });\n }\n\n // 等待代理准备就绪\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(\"MCP代理启动超时\"));\n }, 10000);\n\n const dataHandler = (data: Buffer) => {\n const message = data.toString();\n if (\n message.includes(\"MCP proxy ready\") ||\n message.includes(\"started\")\n ) {\n clearTimeout(timeout);\n this.mcpProxy?.stdout?.removeListener(\"data\", dataHandler);\n resolve();\n }\n };\n\n this.mcpProxy?.stdout?.on(\"data\", dataHandler);\n });\n\n logger.info(\"MCP代理启动成功\");\n }\n\n private async startMCPClient(): Promise<void> {\n try {\n // 初始化 MCPServiceManager\n this.mcpServiceManager = new MCPServiceManager();\n\n // 获取小智接入点配置\n let mcpEndpoint: string | null = null;\n try {\n if (configManager.configExists()) {\n const endpoints = configManager.getMcpEndpoints();\n mcpEndpoint =\n endpoints.find((ep) => ep && !ep.includes(\"<请填写\")) || null;\n }\n } catch (error) {\n logger.warn(\"从配置中读取小智接入点失败:\", error);\n }\n\n // 只有在配置了有效端点时才启动连接\n if (mcpEndpoint) {\n this.proxyMCPServer = new ProxyMCPServer(mcpEndpoint);\n this.proxyMCPServer.setServiceManager(this.mcpServiceManager);\n\n await this.proxyMCPServer.connect();\n logger.info(\"小智接入点连接成功\");\n } else {\n logger.info(\"未配置有效的小智接入点,跳过连接\");\n }\n } catch (error) {\n logger.error(\"启动MCP客户端失败:\", error);\n }\n }\n\n public async stop(): Promise<void> {\n logger.info(\"正在停止MCP服务器...\");\n\n // 关闭所有SSE连接\n for (const client of this.clients.values()) {\n try {\n client.response.end();\n } catch (error) {\n // 忽略关闭时的错误\n }\n }\n this.clients.clear();\n\n // 停止HTTP服务器\n if (this.server) {\n await new Promise<void>((resolve) => {\n this.server!.close(() => resolve());\n });\n this.server = null;\n }\n\n // 停止MCP代理\n if (this.mcpProxy) {\n this.mcpProxy.kill(\"SIGTERM\");\n await new Promise<void>((resolve) => {\n this.mcpProxy!.on(\"exit\", () => resolve());\n setTimeout(() => {\n this.mcpProxy?.kill(\"SIGKILL\");\n resolve();\n }, 5000);\n });\n this.mcpProxy = null;\n }\n\n // 停止小智接入点连接\n if (this.proxyMCPServer) {\n await this.proxyMCPServer.disconnect();\n this.proxyMCPServer = null;\n }\n\n // 停止 MCP 服务管理器\n if (this.mcpServiceManager) {\n await this.mcpServiceManager.stopAllServices();\n this.mcpServiceManager = null;\n }\n\n // 清除缓存的MCP代理路径\n this.mcpProxyPath = null;\n\n this.emit(\"stopped\");\n logger.info(\"MCP服务器已停止\");\n }\n}\n","#!/usr/bin/env node\n\nimport { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport ora from \"ora\";\n\nimport { WebServer } from \"./WebServer\";\nimport { configManager } from \"./configManager\";\nimport { logger } from \"./logger\";\nimport { listMcpServers, listServerTools, setToolEnabled } from \"./mcpCommands\";\n\nconst program = new Command();\nconst SERVICE_NAME = \"xiaozhi-mcp-service\";\n\n/**\n * 获取版本号\n */\nexport function getVersion(): string {\n try {\n // 在 ES 模块环境中获取当前目录\n const __filename = fileURLToPath(import.meta.url);\n const currentDir = path.dirname(__filename);\n\n // 尝试多个可能的 package.json 路径\n const possiblePaths = [\n // 开发环境:src/cli.ts -> package.json\n path.join(currentDir, \"..\", \"package.json\"),\n // 构建后环境:dist/cli.js -> package.json\n path.join(currentDir, \"..\", \"package.json\"),\n // 全局安装环境\n path.join(currentDir, \"..\", \"..\", \"package.json\"),\n // 如果 package.json 被复制到 dist 目录\n path.join(currentDir, \"package.json\"),\n ];\n\n for (const packagePath of possiblePaths) {\n if (fs.existsSync(packagePath)) {\n const packageJson = JSON.parse(fs.readFileSync(packagePath, \"utf8\"));\n if (packageJson.version) {\n return packageJson.version;\n }\n }\n }\n\n // 如果都找不到,返回默认版本\n return \"unknown\";\n } catch (error) {\n console.warn(\"无法从 package.json 读取版本信息:\", error);\n return \"unknown\";\n }\n}\n\n// PID 文件路径 - 使用项目目录下的 PID 文件,支持多实例运行\nconst getPidFile = () => {\n // 优先使用环境变量中的配置目录,否则使用当前工作目录\n const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n return path.join(configDir, `.${SERVICE_NAME}.pid`);\n};\n\ninterface ServiceStatus {\n running: boolean;\n pid?: number;\n uptime?: string;\n mode?: \"foreground\" | \"daemon\";\n}\n\n/**\n * 获取服务状态\n */\nexport function getServiceStatus(): ServiceStatus {\n try {\n const pidFile = getPidFile();\n if (!fs.existsSync(pidFile)) {\n return { running: false };\n }\n\n const pidContent = fs.readFileSync(pidFile, \"utf8\").trim();\n const [pidStr, startTime, mode] = pidContent.split(\"|\");\n const pid = Number.parseInt(pidStr);\n\n if (Number.isNaN(pid)) {\n // PID 文件损坏,删除它\n fs.unlinkSync(pidFile);\n return { running: false };\n }\n\n // 检查进程是否还在运行\n try {\n process.kill(pid, 0); // 发送信号 0 来检查进程是否存在\n\n // 计算运行时间\n const start = Number.parseInt(startTime);\n const uptime = formatUptime(Date.now() - start);\n\n return {\n running: true,\n pid,\n uptime,\n mode: (mode as \"foreground\" | \"daemon\") || \"foreground\",\n };\n } catch (error) {\n // 进程不存在,删除 PID 文件\n fs.unlinkSync(pidFile);\n return { running: false };\n }\n } catch (error) {\n return { running: false };\n }\n}\n\n/**\n * 格式化运行时间\n */\nexport function formatUptime(ms: number): string {\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n\n if (days > 0) {\n return `${days}天 ${hours % 24}小时 ${minutes % 60}分钟`;\n }\n if (hours > 0) {\n return `${hours}小时 ${minutes % 60}分钟`;\n }\n if (minutes > 0) {\n return `${minutes}分钟 ${seconds % 60}秒`;\n }\n return `${seconds}秒`;\n}\n\n/**\n * 保存 PID 信息\n */\nfunction savePidInfo(pid: number, mode: \"foreground\" | \"daemon\") {\n const pidInfo = `${pid}|${Date.now()}|${mode}`;\n fs.writeFileSync(getPidFile(), pidInfo);\n}\n\n/**\n * 清理 PID 文件\n */\nfunction cleanupPidFile() {\n try {\n const pidFile = getPidFile();\n if (fs.existsSync(pidFile)) {\n fs.unlinkSync(pidFile);\n }\n } catch (error) {\n // 忽略清理错误\n }\n}\n\n/**\n * 检查配置文件和环境\n */\nexport function checkEnvironment(): boolean {\n // 首先检查配置文件是否存在\n if (!configManager.configExists()) {\n console.error(chalk.red(\"❌ 错误: 配置文件不存在\"));\n console.log(chalk.yellow('💡 提示: 请运行 \"xiaozhi init\" 初始化配置'));\n return false;\n }\n\n try {\n // 检查配置是否有效\n const endpoints = configManager.getMcpEndpoints();\n const validEndpoints = endpoints.filter(\n (endpoint) => endpoint && !endpoint.includes(\"<请填写\")\n );\n\n if (validEndpoints.length === 0) {\n console.log(chalk.yellow(\"⚠️ 警告: MCP 端点未配置\"));\n console.log(\n chalk.yellow(\n '💡 提示: 服务将启动但无法连接小智服务端,请运行 \"xiaozhi config mcpEndpoint <your-endpoint-url>\" 设置端点'\n )\n );\n console.log(\n chalk.gray(\n \" MCP 服务器功能仍然可用,可通过 Web 界面配置端点后重启服务\"\n )\n );\n } else {\n console.log(\n chalk.green(`✅ 已配置 ${validEndpoints.length} 个有效的 MCP 端点`)\n );\n }\n return true;\n } catch (error) {\n console.error(\n chalk.red(\n `❌ 错误: 配置文件无效 - ${\n error instanceof Error ? error.message : String(error)\n }`\n )\n );\n console.log(chalk.yellow('💡 提示: 请运行 \"xiaozhi init\" 重新初始化配置'));\n return false;\n }\n}\n\n/**\n * 启动服务(重构后的统一启动逻辑)\n */\nasync function startService(daemon = false, ui = false): Promise<void> {\n const spinner = ora(\"检查服务状态...\").start();\n\n try {\n // 检查服务是否已经在运行\n const status = getServiceStatus();\n if (status.running) {\n spinner.fail(`服务已经在运行 (PID: ${status.pid})`);\n return;\n }\n\n // 检查环境配置\n spinner.text = \"检查环境配置...\";\n if (!checkEnvironment()) {\n spinner.fail(\"环境配置检查失败\");\n return;\n }\n\n // 新的统一启动逻辑:直接启动 WebServer\n spinner.text = `启动服务 (${daemon ? \"后台模式\" : \"前台模式\"})...`;\n\n if (daemon) {\n await startWebServerInDaemon(ui);\n spinner.succeed(\"服务已在后台启动\");\n } else {\n await startWebServerInForeground(ui);\n spinner.succeed(\"服务已启动\");\n }\n } catch (error) {\n spinner.fail(\n `启动服务失败: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * 后台模式启动 WebServer\n */\nasync function startWebServerInDaemon(openBrowser = false): Promise<void> {\n const { spawn } = await import(\"node:child_process\");\n\n // 获取当前脚本所在目录\n const scriptDir = path.dirname(fileURLToPath(import.meta.url));\n\n // 构建启动命令\n const command = \"node\";\n const args = [\n path.join(scriptDir, \"webServerStandalone.js\"), // 新的独立启动脚本\n openBrowser ? \"--open-browser\" : \"\",\n ].filter(Boolean);\n\n const child = spawn(command, args, {\n detached: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n env: {\n ...process.env,\n XIAOZHI_CONFIG_DIR: process.env.FORCE_CONFIG_DIR || process.cwd(),\n XIAOZHI_DAEMON: \"true\",\n },\n });\n\n // 保存 PID 信息\n savePidInfo(child.pid!, \"daemon\");\n\n // 初始化日志文件\n const projectDir = process.cwd();\n logger.initLogFile(projectDir);\n logger.enableFileLogging(true);\n\n // 设置日志输出到文件\n const logFilePath = path.join(projectDir, \"xiaozhi.log\");\n const logStream = fs.createWriteStream(logFilePath, { flags: \"a\" });\n child.stdout?.pipe(logStream);\n child.stderr?.pipe(logStream);\n\n // 监听进程异常退出\n child.on(\"exit\", (code, signal) => {\n if (code !== 0 && code !== null) {\n logger.error(`后台服务异常退出 (代码: ${code}, 信号: ${signal})`);\n }\n cleanupPidFile();\n });\n\n // 监听进程错误\n child.on(\"error\", (error) => {\n logger.error(`后台服务启动错误: ${error.message}`);\n cleanupPidFile();\n throw error;\n });\n\n // 分离进程\n child.unref();\n\n console.log(chalk.green(`✅ 服务已在后台启动 (PID: ${child.pid})`));\n console.log(chalk.gray(`日志文件: ${logFilePath}`));\n console.log(chalk.gray(`使用 'xiaozhi attach' 可以查看实时日志`));\n\n if (openBrowser) {\n console.log(chalk.green(\"🌐 浏览器将自动打开\"));\n }\n}\n\n/**\n * 前台模式启动 WebServer\n */\nasync function startWebServerInForeground(openBrowser = false): Promise<void> {\n const webServer = new WebServer();\n\n // 处理退出信号\n const cleanup = async () => {\n console.log(chalk.yellow(\"\\n正在停止服务...\"));\n await webServer.stop();\n cleanupPidFile();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n\n // 保存 PID 信息\n savePidInfo(process.pid, \"foreground\");\n\n await webServer.start();\n\n if (openBrowser) {\n const port = configManager.getWebUIPort();\n const url = `http://localhost:${port}`;\n // 自动打开浏览器逻辑\n await openBrowserUrl(url);\n }\n}\n\n/**\n * 打开浏览器URL\n */\nasync function openBrowserUrl(url: string): Promise<void> {\n try {\n const { spawn } = await import(\"node:child_process\");\n const platform = process.platform;\n\n let command: string;\n let args: string[];\n\n if (platform === \"darwin\") {\n command = \"open\";\n args = [url];\n } else if (platform === \"win32\") {\n command = \"start\";\n args = [\"\", url];\n } else {\n command = \"xdg-open\";\n args = [url];\n }\n\n spawn(command, args, { detached: true, stdio: \"ignore\" });\n console.log(chalk.green(`🌐 已尝试打开浏览器: ${url}`));\n } catch (error) {\n console.log(chalk.yellow(`⚠️ 自动打开浏览器失败,请手动访问: ${url}`));\n }\n}\n\n/**\n * 停止服务\n */\nasync function stopService(): Promise<void> {\n const spinner = ora(\"检查服务状态...\").start();\n\n try {\n const status = getServiceStatus();\n\n if (!status.running) {\n spinner.warn(\"服务未在运行\");\n return;\n }\n\n spinner.text = `停止服务 (PID: ${status.pid})...`;\n\n try {\n // 尝试优雅停止\n process.kill(status.pid!, \"SIGTERM\");\n\n // 等待进程停止\n let attempts = 0;\n const maxAttempts = 30; // 3秒超时\n\n while (attempts < maxAttempts) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n\n try {\n process.kill(status.pid!, 0);\n attempts++;\n } catch {\n // 进程已停止\n break;\n }\n }\n\n // 检查是否还在运行\n try {\n process.kill(status.pid!, 0);\n // 如果还在运行,强制停止\n spinner.text = \"强制停止服务...\";\n process.kill(status.pid!, \"SIGKILL\");\n await new Promise((resolve) => setTimeout(resolve, 500));\n } catch {\n // 进程已停止\n }\n\n cleanupPidFile();\n spinner.succeed(\"服务已停止\");\n } catch (error) {\n cleanupPidFile();\n spinner.fail(\n `停止服务失败: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n } catch (error) {\n spinner.fail(\n `停止服务失败: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * 检查服务状态\n */\nasync function checkStatus(): Promise<void> {\n const spinner = ora(\"检查服务状态...\").start();\n\n try {\n const status = getServiceStatus();\n\n if (status.running) {\n spinner.succeed(\"服务状态\");\n console.log(chalk.green(\"✅ 服务正在运行\"));\n console.log(chalk.gray(` PID: ${status.pid}`));\n console.log(chalk.gray(` 运行时间: ${status.uptime}`));\n console.log(\n chalk.gray(\n ` 运行模式: ${status.mode === \"daemon\" ? \"后台模式\" : \"前台模式\"}`\n )\n );\n\n if (status.mode === \"daemon\") {\n const logFilePath = path.join(process.cwd(), \"xiaozhi.log\");\n console.log(chalk.gray(` 日志文件: ${logFilePath}`));\n }\n } else {\n spinner.succeed(\"服务状态\");\n console.log(chalk.red(\"❌ 服务未运行\"));\n }\n } catch (error) {\n spinner.fail(\n `检查状态失败: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * 附加到后台服务\n */\nasync function attachService(): Promise<void> {\n const spinner = ora(\"检查服务状态...\").start();\n\n try {\n const status = getServiceStatus();\n\n if (!status.running) {\n spinner.fail(\"服务未在运行\");\n return;\n }\n\n if (status.mode !== \"daemon\") {\n spinner.fail(\"服务不是在后台模式运行\");\n return;\n }\n\n spinner.succeed(\"连接到后台服务...\");\n console.log(chalk.green(`已连接到服务 (PID: ${status.pid})`));\n console.log(chalk.gray(\"按 Ctrl+C 可以断开连接(不会停止服务)\"));\n console.log(chalk.gray(\"=\".repeat(50)));\n\n // 显示日志文件内容\n const logFilePath = path.join(process.cwd(), \"xiaozhi.log\");\n if (fs.existsSync(logFilePath)) {\n // 跨平台的日志查看实现\n if (process.platform === \"win32\") {\n // Windows 使用 PowerShell 的 Get-Content -Wait\n const { spawn } = await import(\"node:child_process\");\n const tail = spawn(\n \"powershell\",\n [\"-Command\", `Get-Content -Path \"${logFilePath}\" -Wait`],\n { stdio: \"inherit\" }\n );\n\n // 处理中断信号\n process.on(\"SIGINT\", () => {\n console.log(chalk.yellow(\"\\n断开连接,服务继续在后台运行\"));\n tail.kill();\n process.exit(0);\n });\n\n tail.on(\"exit\", () => {\n process.exit(0);\n });\n } else {\n // Unix/Linux/macOS 使用 tail -f\n const { spawn } = await import(\"node:child_process\");\n const tail = spawn(\"tail\", [\"-f\", logFilePath], { stdio: \"inherit\" });\n\n // 处理中断信号\n process.on(\"SIGINT\", () => {\n console.log(chalk.yellow(\"\\n断开连接,服务继续在后台运行\"));\n tail.kill();\n process.exit(0);\n });\n\n tail.on(\"exit\", () => {\n process.exit(0);\n });\n }\n } else {\n console.log(chalk.yellow(\"日志文件不存在\"));\n }\n } catch (error) {\n spinner.fail(\n `连接失败: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * 重启服务\n */\nasync function restartService(daemon = false, ui = false): Promise<void> {\n console.log(chalk.blue(\"🔄 重启服务...\"));\n\n // 先停止服务\n await stopService();\n\n // 等待一下确保完全停止\n await new Promise((resolve) => setTimeout(resolve, 1000));\n\n // 重新启动服务\n await startService(daemon, ui);\n}\n\n/**\n * 以 MCP Server 模式启动服务\n */\nasync function startMCPServerMode(port: number, daemon = false): Promise<void> {\n const spinner = ora(\"启动 MCP Server 模式...\").start();\n\n try {\n // 检查配置是否存在\n if (!configManager.configExists()) {\n spinner.fail(\"配置文件不存在\");\n console.log(chalk.yellow('💡 提示: 请先运行 \"xiaozhi init\" 初始化配置'));\n return;\n }\n\n // 导入 MCPServer\n const { MCPServer } = await import(\"./services/mcpServer.js\");\n\n if (daemon) {\n // 后台模式 - 创建子进程\n const scriptPath = fileURLToPath(import.meta.url);\n const distDir = path.dirname(scriptPath);\n\n const child = spawn(\n \"node\",\n [path.join(distDir, \"cli.js\"), \"start\", \"--server\", port.toString()],\n {\n detached: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n env: {\n ...process.env,\n XIAOZHI_CONFIG_DIR: process.cwd(),\n XIAOZHI_DAEMON: \"true\",\n MCP_SERVER_MODE: \"true\",\n },\n }\n );\n\n // 保存 PID 信息\n savePidInfo(child.pid!, \"daemon\");\n\n // 设置日志输出\n const logFilePath = path.join(process.cwd(), \"xiaozhi-mcp-server.log\");\n const logStream = fs.createWriteStream(logFilePath, { flags: \"a\" });\n child.stdout?.pipe(logStream);\n child.stderr?.pipe(logStream);\n\n child.unref();\n\n spinner.succeed(\n `MCP Server 已在后台启动 (PID: ${child.pid}, Port: ${port})`\n );\n console.log(chalk.gray(`日志文件: ${logFilePath}`));\n } else {\n // 前台模式\n const server = new MCPServer(port);\n\n // 处理退出信号\n const cleanup = async () => {\n console.log(chalk.yellow(\"\\n正在停止 MCP Server...\"));\n await server.stop();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n\n await server.start();\n\n spinner.succeed(\"MCP Server 已启动\");\n console.log(chalk.green(\"✅ MCP Server 端点已启动,可通过以下地址访问:\"));\n console.log(chalk.green(` SSE endpoint: http://localhost:${port}/sse`));\n console.log(\n chalk.green(` Messages endpoint: http://localhost:${port}/messages`)\n );\n console.log(chalk.green(` RPC endpoint: http://localhost:${port}/rpc`));\n console.log(chalk.green(\" 网络访问: 将 localhost 替换为你的IP地址\"));\n console.log(chalk.yellow(\"💡 提示: 按 Ctrl+C 停止服务\"));\n }\n } catch (error) {\n spinner.fail(\n `启动 MCP Server 失败: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n}\n\n/**\n * 显示详细信息\n */\nfunction showDetailedInfo(): void {\n const version = getVersion();\n console.log(chalk.blue(`xiaozhi v${version}`));\n console.log(chalk.gray(\"MCP Calculator Service CLI Tool\"));\n console.log(chalk.gray(\"Built with Node.js and TypeScript\"));\n console.log(chalk.gray(`Node.js: ${process.version}`));\n console.log(chalk.gray(`Platform: ${process.platform} ${process.arch}`));\n}\n\n/**\n * 初始化配置\n * @param format 配置文件格式,默认为 json\n */\nasync function initConfig(\n format: \"json\" | \"json5\" | \"jsonc\" = \"json\"\n): Promise<void> {\n const spinner = ora(\"初始化配置...\").start();\n\n try {\n if (configManager.configExists()) {\n spinner.warn(\"配置文件已存在\");\n console.log(chalk.yellow(\"如需重新初始化,请先删除现有的配置文件\"));\n return;\n }\n\n configManager.initConfig(format);\n spinner.succeed(\"配置文件初始化成功\");\n\n // 获取实际创建的配置文件路径\n const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n const configFileName = `xiaozhi.config.${format}`;\n const configPath = path.join(configDir, configFileName);\n\n console.log(chalk.green(`✅ 配置文件已创建: ${configFileName}`));\n console.log(chalk.yellow(\"📝 请编辑配置文件设置你的 MCP 端点:\"));\n console.log(chalk.gray(` 配置文件路径: ${configPath}`));\n console.log(chalk.yellow(\"💡 或者使用命令设置:\"));\n console.log(\n chalk.gray(\" xiaozhi config mcpEndpoint <your-endpoint-url>\")\n );\n } catch (error) {\n spinner.fail(\n `初始化配置失败: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n}\n\n/**\n * 获取可用模板列表\n */\nfunction getAvailableTemplates(): string[] {\n const scriptDir = path.dirname(fileURLToPath(import.meta.url));\n const possiblePaths = [\n path.join(scriptDir, \"..\", \"templates\"), // 开发环境\n path.join(scriptDir, \"templates\"), // 打包后的环境\n path.join(scriptDir, \"..\", \"..\", \"templates\"), // npm 全局安装\n ];\n\n const templatesDir = possiblePaths.find((p) => fs.existsSync(p));\n if (!templatesDir) {\n return [];\n }\n\n return fs.readdirSync(templatesDir).filter((item) => {\n const itemPath = path.join(templatesDir, item);\n return fs.statSync(itemPath).isDirectory();\n });\n}\n\n/**\n * 计算字符串相似度(简单的编辑距离算法)\n */\nexport function calculateSimilarity(str1: string, str2: string): number {\n const len1 = str1.length;\n const len2 = str2.length;\n const matrix = Array(len1 + 1)\n .fill(null)\n .map(() => Array(len2 + 1).fill(0));\n\n for (let i = 0; i <= len1; i++) matrix[i][0] = i;\n for (let j = 0; j <= len2; j++) matrix[0][j] = j;\n\n for (let i = 1; i <= len1; i++) {\n for (let j = 1; j <= len2; j++) {\n if (str1[i - 1] === str2[j - 1]) {\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n matrix[i][j] = Math.min(\n matrix[i - 1][j] + 1,\n matrix[i][j - 1] + 1,\n matrix[i - 1][j - 1] + 1\n );\n }\n }\n }\n\n const maxLen = Math.max(len1, len2);\n return maxLen === 0 ? 1 : (maxLen - matrix[len1][len2]) / maxLen;\n}\n\n/**\n * 查找最相似的模板\n */\nfunction findSimilarTemplate(\n input: string,\n templates: string[]\n): string | null {\n if (templates.length === 0) return null;\n\n let bestMatch = templates[0];\n let bestSimilarity = calculateSimilarity(\n input.toLowerCase(),\n bestMatch.toLowerCase()\n );\n\n for (const template of templates.slice(1)) {\n const similarity = calculateSimilarity(\n input.toLowerCase(),\n template.toLowerCase()\n );\n if (similarity > bestSimilarity) {\n bestSimilarity = similarity;\n bestMatch = template;\n }\n }\n\n // 只有相似度超过 0.5 才认为是可能的匹配\n return bestSimilarity > 0.5 ? bestMatch : null;\n}\n\n/**\n * 询问用户确认\n */\nasync function askUserConfirmation(question: string): Promise<boolean> {\n // 检查是否在交互式终端中\n if (!process.stdin.isTTY) {\n // 非交互式环境,默认返回 false\n console.log(\"n (非交互式环境)\");\n return false;\n }\n\n // 使用 readline 接口处理用户输入\n const readline = await import(\"node:readline\");\n\n return new Promise((resolve) => {\n process.stdout.write(question);\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const handleInput = (input: string) => {\n const char = input.trim().toLowerCase();\n if (char === \"y\" || char === \"yes\") {\n rl.close();\n resolve(true);\n } else if (char === \"n\" || char === \"no\" || char === \"\") {\n rl.close();\n resolve(false);\n } else {\n // 无效输入,重新询问\n process.stdout.write(\"请输入 y 或 n: \");\n }\n };\n\n rl.on(\"line\", handleInput);\n rl.on(\"SIGINT\", () => {\n rl.close();\n resolve(false);\n });\n });\n}\n\n/**\n * 创建基本的 xiaozhi.config.json 文件\n */\nfunction createBasicConfig(projectPath: string): void {\n const configContent = {\n mcpEndpoint: \"<请填写你的接入点地址(获取地址在 xiaozhi.me)>\",\n mcpServers: {},\n };\n\n const configPath = path.join(projectPath, \"xiaozhi.config.json\");\n fs.writeFileSync(configPath, JSON.stringify(configContent, null, 2), \"utf8\");\n}\n\n/**\n * 创建项目命令\n */\nasync function createProject(\n projectName: string,\n options: { template?: string }\n): Promise<void> {\n const spinner = ora(\"初始化项目...\").start();\n\n try {\n // 确定目标目录\n const targetPath = path.join(process.cwd(), projectName);\n\n // 检查目标目录是否已存在\n if (fs.existsSync(targetPath)) {\n spinner.fail(`目录 \"${projectName}\" 已存在`);\n console.log(chalk.yellow(\"💡 提示: 请选择不同的项目名称或删除现有目录\"));\n return;\n }\n\n if (options.template) {\n // 使用模板创建项目\n spinner.text = \"检查模板...\";\n\n // 获取可用模板列表\n const availableTemplates = getAvailableTemplates();\n\n if (availableTemplates.length === 0) {\n spinner.fail(\"找不到 templates 目录\");\n console.log(chalk.yellow(\"💡 提示: 请确保 xiaozhi-client 正确安装\"));\n return;\n }\n\n // 检查模板是否存在\n if (!availableTemplates.includes(options.template)) {\n spinner.fail(`模板 \"${options.template}\" 不存在`);\n\n // 尝试找到相似的模板\n const similarTemplate = findSimilarTemplate(\n options.template,\n availableTemplates\n );\n\n if (similarTemplate) {\n console.log(\n chalk.yellow(`💡 你是想使用模板 \"${similarTemplate}\" 吗?`)\n );\n const confirmed = await askUserConfirmation(\n chalk.cyan(\"确认使用此模板?(y/n): \")\n );\n\n if (confirmed) {\n options.template = similarTemplate;\n } else {\n console.log(chalk.yellow(\"可用的模板:\"));\n for (const template of availableTemplates) {\n console.log(chalk.gray(` - ${template}`));\n }\n return;\n }\n } else {\n console.log(chalk.yellow(\"可用的模板:\"));\n for (const template of availableTemplates) {\n console.log(chalk.gray(` - ${template}`));\n }\n return;\n }\n }\n\n // 获取模板路径 (ESM 环境)\n const scriptDir = path.dirname(fileURLToPath(import.meta.url));\n const possiblePaths = [\n path.join(scriptDir, \"..\", \"templates\"), // 开发环境\n path.join(scriptDir, \"templates\"), // 打包后的环境\n path.join(scriptDir, \"..\", \"..\", \"templates\"), // npm 全局安装\n ];\n const templatesDir = possiblePaths.find((p) => fs.existsSync(p))!;\n const templatePath = path.join(templatesDir, options.template);\n\n spinner.text = `从模板 \"${options.template}\" 创建项目 \"${projectName}\"...`;\n\n // 复制模板到目标目录\n copyDirectory(templatePath, targetPath, [\n \"node_modules\",\n \".pnpm-debug.log\",\n \"pnpm-lock.yaml\",\n ]);\n\n // 创建日志文件\n const logFilePath = path.join(targetPath, \"xiaozhi.log\");\n if (!fs.existsSync(logFilePath)) {\n fs.writeFileSync(logFilePath, \"\", \"utf8\");\n }\n\n spinner.succeed(`项目 \"${projectName}\" 创建成功`);\n\n console.log(chalk.green(\"✅ 项目创建完成!\"));\n console.log(chalk.yellow(\"📝 接下来的步骤:\"));\n console.log(chalk.gray(` cd ${projectName}`));\n console.log(chalk.gray(\" pnpm install # 安装依赖\"));\n console.log(\n chalk.gray(\" # 编辑 xiaozhi.config.json 设置你的 MCP 端点\")\n );\n console.log(chalk.gray(\" xiaozhi start # 启动服务\"));\n } else {\n // 创建基本项目(只有配置文件)\n spinner.text = `创建基本项目 \"${projectName}\"...`;\n\n // 创建项目目录\n fs.mkdirSync(targetPath, { recursive: true });\n\n // 创建基本的 xiaozhi.config.json\n createBasicConfig(targetPath);\n\n // 创建日志文件\n const logFilePath = path.join(targetPath, \"xiaozhi.log\");\n fs.writeFileSync(logFilePath, \"\", \"utf8\");\n\n spinner.succeed(`项目 \"${projectName}\" 创建成功`);\n\n console.log(chalk.green(\"✅ 基本项目创建完成!\"));\n console.log(chalk.yellow(\"📝 接下来的步骤:\"));\n console.log(chalk.gray(` cd ${projectName}`));\n console.log(\n chalk.gray(\" # 编辑 xiaozhi.config.json 设置你的 MCP 端点和服务\")\n );\n console.log(chalk.gray(\" xiaozhi start # 启动服务\"));\n console.log(\n chalk.yellow(\"💡 提示: 使用 --template 选项可以从模板创建项目\")\n );\n }\n } catch (error) {\n spinner.fail(\n `创建项目失败: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * 递归复制目录\n */\nfunction copyDirectory(\n src: string,\n dest: string,\n excludePatterns: string[] = []\n): void {\n // 创建目标目录\n if (!fs.existsSync(dest)) {\n fs.mkdirSync(dest, { recursive: true });\n }\n\n const items = fs.readdirSync(src);\n\n for (const item of items) {\n // 检查是否应该排除此项\n if (excludePatterns.some((pattern) => item.includes(pattern))) {\n continue;\n }\n\n const srcPath = path.join(src, item);\n const destPath = path.join(dest, item);\n const stat = fs.statSync(srcPath);\n\n if (stat.isDirectory()) {\n copyDirectory(srcPath, destPath, excludePatterns);\n } else {\n fs.copyFileSync(srcPath, destPath);\n }\n }\n}\n\n/**\n * 启动 UI 服务\n */\nasync function startUIService(): Promise<void> {\n const spinner = ora(\"启动 UI 服务...\").start();\n\n try {\n // 检查配置是否存在\n if (!configManager.configExists()) {\n spinner.fail(\"配置文件不存在\");\n console.log(chalk.yellow('💡 提示: 请先运行 \"xiaozhi init\" 初始化配置'));\n return;\n }\n\n // 启动 Web 服务器\n const webServer = new WebServer();\n await webServer.start();\n\n spinner.succeed(\"UI 服务已启动\");\n\n // 从配置获取端口号\n const port = configManager.getWebUIPort();\n console.log(chalk.green(\"✅ 配置管理网页已启动,可通过以下地址访问:\"));\n console.log(chalk.green(` 本地访问: http://localhost:${port}`));\n console.log(chalk.green(` 网络访问: http://<你的IP地址>:${port}`));\n console.log(chalk.yellow(\"💡 提示: 按 Ctrl+C 停止服务\"));\n\n // 自动打开浏览器\n const { spawn } = await import(\"node:child_process\");\n const url = `http://localhost:${port}`;\n\n // 根据不同平台打开浏览器\n try {\n let browserProcess: ReturnType<typeof spawn>;\n if (process.platform === \"darwin\") {\n browserProcess = spawn(\"open\", [url], {\n detached: true,\n stdio: \"ignore\",\n });\n } else if (process.platform === \"win32\") {\n browserProcess = spawn(\"cmd\", [\"/c\", \"start\", url], {\n detached: true,\n stdio: \"ignore\",\n });\n } else {\n browserProcess = spawn(\"xdg-open\", [url], {\n detached: true,\n stdio: \"ignore\",\n });\n }\n\n // 处理spawn错误,避免程序崩溃\n browserProcess.on(\"error\", () => {\n // 静默处理浏览器启动错误,不影响主程序\n console.log(\n chalk.gray(`💡 提示: 无法自动打开浏览器,请手动访问: ${url}`)\n );\n });\n\n browserProcess.unref();\n } catch (error) {\n // 忽略打开浏览器的错误\n console.log(\n chalk.gray(`💡 提示: 无法自动打开浏览器,请手动访问: ${url}`)\n );\n }\n\n // 处理退出信号\n let isExiting = false;\n process.on(\"SIGINT\", async () => {\n if (isExiting) {\n console.log(chalk.red(\"\\n强制退出...\"));\n process.exit(1);\n }\n isExiting = true;\n console.log(chalk.yellow(\"\\n正在停止 UI 服务...\"));\n try {\n await webServer.stop();\n console.log(chalk.green(\"UI 服务已停止\"));\n } catch (error) {\n console.log(chalk.red(\"停止服务时出错,强制退出\"));\n }\n process.exit(0);\n });\n\n process.on(\"SIGTERM\", async () => {\n isExiting = true;\n await webServer.stop();\n process.exit(0);\n });\n } catch (error) {\n spinner.fail(\n `启动 UI 服务失败: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n}\n\n/**\n * 配置管理命令\n */\nasync function configCommand(key: string, value?: string): Promise<void> {\n const spinner = ora(\"更新配置...\").start();\n\n try {\n if (!configManager.configExists()) {\n spinner.fail(\"配置文件不存在\");\n console.log(chalk.yellow('💡 提示: 请先运行 \"xiaozhi init\" 初始化配置'));\n return;\n }\n\n if (!value) {\n // 显示配置值\n spinner.text = \"读取配置...\";\n const config = configManager.getConfig();\n\n switch (key) {\n case \"mcpEndpoint\": {\n spinner.succeed(\"配置信息\");\n const endpoints = configManager.getMcpEndpoints();\n if (endpoints.length === 0) {\n console.log(chalk.yellow(\"未配置任何 MCP 端点\"));\n } else if (endpoints.length === 1) {\n console.log(chalk.green(`MCP 端点: ${endpoints[0]}`));\n } else {\n console.log(chalk.green(`MCP 端点 (${endpoints.length} 个):`));\n endpoints.forEach((ep, index) => {\n console.log(chalk.gray(` ${index + 1}. ${ep}`));\n });\n }\n break;\n }\n case \"mcpServers\":\n spinner.succeed(\"配置信息\");\n console.log(chalk.green(\"MCP 服务:\"));\n for (const [name, serverConfig] of Object.entries(\n config.mcpServers\n )) {\n // 检查是否是 SSE 类型\n if (\"type\" in serverConfig && serverConfig.type === \"sse\") {\n console.log(chalk.gray(` ${name}: [SSE] ${serverConfig.url}`));\n } else {\n console.log(\n chalk.gray(\n ` ${name}: ${(serverConfig as any).command} ${(serverConfig as any).args.join(\" \")}`\n )\n );\n }\n }\n break;\n case \"connection\": {\n spinner.succeed(\"配置信息\");\n const connectionConfig = configManager.getConnectionConfig();\n console.log(chalk.green(\"连接配置:\"));\n console.log(\n chalk.gray(\n ` 心跳检测间隔: ${connectionConfig.heartbeatInterval}ms`\n )\n );\n console.log(\n chalk.gray(` 心跳超时时间: ${connectionConfig.heartbeatTimeout}ms`)\n );\n console.log(\n chalk.gray(` 重连间隔: ${connectionConfig.reconnectInterval}ms`)\n );\n break;\n }\n case \"heartbeatInterval\":\n spinner.succeed(\"配置信息\");\n console.log(\n chalk.green(\n `心跳检测间隔: ${configManager.getHeartbeatInterval()}ms`\n )\n );\n break;\n case \"heartbeatTimeout\":\n spinner.succeed(\"配置信息\");\n console.log(\n chalk.green(\n `心跳超时时间: ${configManager.getHeartbeatTimeout()}ms`\n )\n );\n break;\n case \"reconnectInterval\":\n spinner.succeed(\"配置信息\");\n console.log(\n chalk.green(`重连间隔: ${configManager.getReconnectInterval()}ms`)\n );\n break;\n default:\n spinner.fail(`未知的配置项: ${key}`);\n console.log(\n chalk.yellow(\n \"支持的配置项: mcpEndpoint, mcpServers, connection, heartbeatInterval, heartbeatTimeout, reconnectInterval\"\n )\n );\n return;\n }\n } else {\n // 设置配置值\n switch (key) {\n case \"mcpEndpoint\":\n configManager.updateMcpEndpoint(value);\n spinner.succeed(`MCP 端点已更新为: ${value}`);\n break;\n case \"heartbeatInterval\": {\n const heartbeatInterval = Number.parseInt(value, 10);\n if (Number.isNaN(heartbeatInterval) || heartbeatInterval <= 0) {\n spinner.fail(\"心跳检测间隔必须是大于0的数字(毫秒)\");\n return;\n }\n configManager.setHeartbeatInterval(heartbeatInterval);\n spinner.succeed(`心跳检测间隔已更新为: ${heartbeatInterval}ms`);\n break;\n }\n case \"heartbeatTimeout\": {\n const heartbeatTimeout = Number.parseInt(value, 10);\n if (Number.isNaN(heartbeatTimeout) || heartbeatTimeout <= 0) {\n spinner.fail(\"心跳超时时间必须是大于0的数字(毫秒)\");\n return;\n }\n configManager.setHeartbeatTimeout(heartbeatTimeout);\n spinner.succeed(`心跳超时时间已更新为: ${heartbeatTimeout}ms`);\n break;\n }\n case \"reconnectInterval\": {\n const reconnectInterval = Number.parseInt(value, 10);\n if (Number.isNaN(reconnectInterval) || reconnectInterval <= 0) {\n spinner.fail(\"重连间隔必须是大于0的数字(毫秒)\");\n return;\n }\n configManager.setReconnectInterval(reconnectInterval);\n spinner.succeed(`重连间隔已更新为: ${reconnectInterval}ms`);\n break;\n }\n default:\n spinner.fail(`配置项 ${key} 不支持通过命令行设置`);\n console.log(\n chalk.yellow(\n \"支持设置的配置项: mcpEndpoint, heartbeatInterval, heartbeatTimeout, reconnectInterval\"\n )\n );\n return;\n }\n }\n } catch (error) {\n spinner.fail(\n `配置操作失败: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * 显示帮助信息\n */\nfunction showHelp(): void {\n console.log(chalk.blue.bold(\"xiaozhi - MCP Calculator Service CLI\"));\n console.log();\n console.log(chalk.yellow(\"使用方法:\"));\n console.log(\" xiaozhi <command> [options]\");\n console.log();\n console.log(chalk.yellow(\"命令:\"));\n console.log(\" create <projectName> 创建项目\");\n console.log(\" init 初始化配置文件\");\n console.log(\" config <key> [value] 查看或设置配置\");\n console.log(\n \" start [--daemon] [--ui] [--server] 启动服务 (--daemon 后台运行, --ui 同时启动 Web UI, --server MCP Server 模式)\"\n );\n console.log(\" stop 停止服务\");\n console.log(\" status 检查服务状态\");\n console.log(\" attach 连接到后台服务查看日志\");\n console.log(\n \" restart [--daemon] [--ui] 重启服务 (--daemon 后台运行, --ui 同时启动 Web UI)\"\n );\n console.log(\" ui 启动配置管理网页\");\n console.log();\n console.log(chalk.yellow(\"选项:\"));\n console.log(\" -v, --version 显示版本信息\");\n console.log(\" -V 显示详细信息\");\n console.log(\" -h, --help 显示帮助信息\");\n console.log(\" -t, --template <name> 指定模板名称(用于 create 命令)\");\n console.log();\n console.log(chalk.yellow(\"项目示例:\"));\n console.log(\" xiaozhi create my-app # 创建基本项目\");\n console.log(\n \" xiaozhi create my-app -t hello-world # 使用 hello-world 模板\"\n );\n console.log(\n \" xiaozhi create my-app --template hello-world # 同上,完整选项名\"\n );\n console.log();\n console.log(chalk.yellow(\"配置示例:\"));\n console.log(\" xiaozhi init # 初始化配置\");\n console.log(\" xiaozhi config mcpEndpoint # 查看 MCP 端点\");\n console.log(\" xiaozhi config mcpEndpoint wss://... # 设置 MCP 端点\");\n console.log();\n console.log(chalk.yellow(\"服务示例:\"));\n console.log(\" xiaozhi start # 前台启动服务\");\n console.log(\" xiaozhi start --daemon # 后台启动服务\");\n console.log(\" xiaozhi start --ui # 启动服务并同时启动 Web UI\");\n console.log(\" xiaozhi start -d -u # 后台启动服务并同时启动 Web UI\");\n console.log(\n \" xiaozhi start --server # 以 MCP Server 模式启动 (端口 3000)\"\n );\n console.log(\n \" xiaozhi start -s 8080 # 以 MCP Server 模式启动 (端口 8080)\"\n );\n console.log(\" xiaozhi start -s -d # 后台运行 MCP Server\");\n console.log(\" xiaozhi status # 检查服务状态\");\n console.log(\" xiaozhi attach # 查看后台服务日志\");\n console.log(\" xiaozhi stop # 停止服务\");\n console.log();\n console.log(chalk.yellow(\"MCP 管理示例:\"));\n console.log(\" xiaozhi mcp list # 列出所有 MCP 服务\");\n console.log(\" xiaozhi mcp list --tools # 列出所有服务的工具\");\n console.log(\" xiaozhi mcp server <name> # 列出指定服务的工具\");\n console.log(\" xiaozhi mcp tool <server> <tool> enable # 启用工具\");\n console.log(\" xiaozhi mcp tool <server> <tool> disable # 禁用工具\");\n console.log();\n}\n\n// 配置 Commander 程序\nprogram\n .name(\"xiaozhi\")\n .description(\"MCP Calculator Service CLI Tool\")\n .version(getVersion(), \"-v, --version\", \"显示版本信息\")\n .helpOption(\"-h, --help\", \"显示帮助信息\");\n\n// create 命令\nprogram\n .command(\"create <projectName>\")\n .description(\"创建项目\")\n .option(\"-t, --template <templateName>\", \"使用指定模板创建项目\")\n .action(async (projectName, options) => {\n await createProject(projectName, options);\n });\n\n// init 命令\nprogram\n .command(\"init\")\n .description(\"初始化配置文件\")\n .option(\"-f, --format <format>\", \"配置文件格式 (json, json5, jsonc)\", \"json\")\n .action(async (options) => {\n const format = options.format as \"json\" | \"json5\" | \"jsonc\";\n if (format !== \"json\" && format !== \"json5\" && format !== \"jsonc\") {\n console.error(chalk.red(\"错误: 格式必须是 json, json5 或 jsonc\"));\n process.exit(1);\n }\n await initConfig(format);\n });\n\n// config 命令\nprogram\n .command(\"config <key> [value]\")\n .description(\"查看或设置配置\")\n .action(async (key, value) => {\n await configCommand(key, value);\n });\n\n// start 命令\nprogram\n .command(\"start\")\n .description(\"启动服务\")\n .option(\"-d, --daemon\", \"在后台运行服务\")\n .option(\"-u, --ui\", \"同时启动 Web UI 服务\")\n .option(\n \"-s, --server [port]\",\n \"以 MCP Server 模式启动 (可选指定端口,默认 3000)\"\n )\n .option(\"--stdio\", \"以 stdio 模式运行 MCP Server (用于 Cursor 等客户端)\")\n .action(async (options) => {\n if (options.stdio) {\n // stdio 模式 - 直接运行 mcpServerProxy\n const { spawn } = await import(\"node:child_process\");\n const scriptPath = fileURLToPath(import.meta.url);\n const distDir = path.dirname(scriptPath);\n const mcpProxyPath = path.join(distDir, \"mcpServerProxy.js\");\n\n // 直接执行 mcpServerProxy,它已经支持 stdio\n spawn(\"node\", [mcpProxyPath], {\n stdio: \"inherit\",\n env: {\n ...process.env,\n // 如果用户没有设置 XIAOZHI_CONFIG_DIR,则使用当前工作目录\n XIAOZHI_CONFIG_DIR: process.env.XIAOZHI_CONFIG_DIR || process.cwd(),\n },\n });\n } else if (options.server) {\n // MCP Server 模式\n const port =\n typeof options.server === \"string\"\n ? Number.parseInt(options.server)\n : 3000;\n await startMCPServerMode(port, options.daemon);\n } else {\n // 传统模式\n await startService(options.daemon, options.ui);\n }\n });\n\n// stop 命令\nprogram\n .command(\"stop\")\n .description(\"停止服务\")\n .action(async () => {\n await stopService();\n });\n\n// status 命令\nprogram\n .command(\"status\")\n .description(\"检查服务状态\")\n .action(async () => {\n await checkStatus();\n });\n\n// attach 命令\nprogram\n .command(\"attach\")\n .description(\"连接到后台服务查看日志\")\n .action(async () => {\n await attachService();\n });\n\n// restart 命令\nprogram\n .command(\"restart\")\n .description(\"重启服务\")\n .option(\"-d, --daemon\", \"在后台运行服务\")\n .option(\"-u, --ui\", \"同时启动 Web UI 服务\")\n .action(async (options) => {\n await restartService(options.daemon, options.ui);\n });\n\n// mcp 命令组\nconst mcpCommand = program.command(\"mcp\").description(\"MCP 服务和工具管理\");\n\n// mcp list 命令\nmcpCommand\n .command(\"list\")\n .description(\"列出 MCP 服务\")\n .option(\"--tools\", \"显示所有服务的工具列表\")\n .action(async (options) => {\n await listMcpServers(options);\n });\n\n// mcp <server> list 命令\nmcpCommand\n .command(\"server <serverName>\")\n .description(\"管理指定的 MCP 服务\")\n .action(async (serverName) => {\n await listServerTools(serverName);\n });\n\n// mcp <server> <tool> enable/disable 命令\nmcpCommand\n .command(\"tool <serverName> <toolName> <action>\")\n .description(\"启用或禁用指定服务的工具\")\n .action(async (serverName, toolName, action) => {\n if (action !== \"enable\" && action !== \"disable\") {\n console.error(chalk.red(\"错误: 操作必须是 'enable' 或 'disable'\"));\n process.exit(1);\n }\n\n const enabled = action === \"enable\";\n await setToolEnabled(serverName, toolName, enabled);\n });\n\n// endpoint 命令组\nconst endpointCommand = program\n .command(\"endpoint\")\n .description(\"管理 MCP 端点\");\n\n// endpoint list 命令\nendpointCommand\n .command(\"list\")\n .description(\"列出所有 MCP 端点\")\n .action(async () => {\n const spinner = ora(\"读取端点配置...\").start();\n try {\n const endpoints = configManager.getMcpEndpoints();\n spinner.succeed(\"端点列表\");\n\n if (endpoints.length === 0) {\n console.log(chalk.yellow(\"未配置任何 MCP 端点\"));\n } else {\n console.log(chalk.green(`共 ${endpoints.length} 个端点:`));\n endpoints.forEach((ep, index) => {\n console.log(chalk.gray(` ${index + 1}. ${ep}`));\n });\n }\n } catch (error) {\n spinner.fail(\n `读取端点失败: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n });\n\n// endpoint add 命令\nendpointCommand\n .command(\"add <url>\")\n .description(\"添加新的 MCP 端点\")\n .action(async (url) => {\n const spinner = ora(\"添加端点...\").start();\n try {\n configManager.addMcpEndpoint(url);\n spinner.succeed(`成功添加端点: ${url}`);\n\n const endpoints = configManager.getMcpEndpoints();\n console.log(chalk.gray(`当前共 ${endpoints.length} 个端点`));\n } catch (error) {\n spinner.fail(\n `添加端点失败: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n });\n\n// endpoint remove 命令\nendpointCommand\n .command(\"remove <url>\")\n .description(\"移除指定的 MCP 端点\")\n .action(async (url) => {\n const spinner = ora(\"移除端点...\").start();\n try {\n configManager.removeMcpEndpoint(url);\n spinner.succeed(`成功移除端点: ${url}`);\n\n const endpoints = configManager.getMcpEndpoints();\n console.log(chalk.gray(`当前剩余 ${endpoints.length} 个端点`));\n } catch (error) {\n spinner.fail(\n `移除端点失败: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n });\n\n// endpoint set 命令\nendpointCommand\n .command(\"set <urls...>\")\n .description(\"设置 MCP 端点(可以是单个或多个)\")\n .action(async (urls) => {\n const spinner = ora(\"设置端点...\").start();\n try {\n if (urls.length === 1) {\n configManager.updateMcpEndpoint(urls[0]);\n spinner.succeed(`成功设置端点: ${urls[0]}`);\n } else {\n configManager.updateMcpEndpoint(urls);\n spinner.succeed(`成功设置 ${urls.length} 个端点`);\n for (const [index, url] of urls.entries()) {\n console.log(chalk.gray(` ${index + 1}. ${url}`));\n }\n }\n } catch (error) {\n spinner.fail(\n `设置端点失败: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n });\n\n// ui 命令\nprogram\n .command(\"ui\")\n .description(\"启动配置管理网页\")\n .action(async () => {\n await startUIService();\n });\n\n// -V 选项 (详细信息)\nprogram.option(\"-V\", \"显示详细信息\").action((options) => {\n if (options.V) {\n showDetailedInfo();\n process.exit(0);\n }\n});\n\n// 处理无参数情况,显示帮助\nif (process.argv.length <= 2) {\n showHelp();\n process.exit(0);\n}\n\n// 解析命令行参数\nprogram.parse(process.argv);\n","/**\n * MCP 服务管理器单例\n * 提供全局唯一的 MCPServiceManager 实例,解决多实例资源冲突问题\n */\n\nimport MCPServiceManager from \"./MCPServiceManager.js\";\n\n// 重新导出相关类型,便于外部使用\nexport type { Tool } from \"@modelcontextprotocol/sdk/types.js\";\nexport type { LocalMCPServerConfig } from \"../configManager.js\";\n\n// 单例状态枚举\nenum SingletonState {\n NOT_INITIALIZED = \"not_initialized\",\n INITIALIZING = \"initializing\",\n INITIALIZED = \"initialized\",\n FAILED = \"failed\",\n CLEANUP = \"cleanup\",\n}\n\n// 单例状态接口\ninterface SingletonStatus {\n state: SingletonState;\n initializationTime?: Date;\n lastError?: Error;\n instanceId?: string;\n}\n\n// 单例状态管理变量\nlet instance: MCPServiceManager | null = null;\nlet initPromise: Promise<MCPServiceManager> | null = null;\nlet state: SingletonState = SingletonState.NOT_INITIALIZED;\nlet lastError: Error | null = null;\nlet instanceId: string | null = null;\n\n/**\n * 创建 MCPServiceManager 实例(私有函数)\n */\nasync function createInstance(): Promise<MCPServiceManager> {\n console.log(\"🚀 正在初始化 MCPServiceManager 单例...\");\n\n const manager = new MCPServiceManager();\n\n return manager;\n}\n\n/**\n * 获取 MCPServiceManager 单例实例\n *\n * @returns Promise<MCPServiceManager> 管理器实例\n * @throws Error 如果初始化失败\n */\nasync function getInstance(): Promise<MCPServiceManager> {\n // 如果已经初始化完成,直接返回实例\n if (instance && state === SingletonState.INITIALIZED) {\n return instance;\n }\n\n // 如果正在初始化中,等待同一个初始化Promise\n if (initPromise && state === SingletonState.INITIALIZING) {\n return initPromise;\n }\n\n // 如果之前初始化失败,重置状态准备重试\n if (state === SingletonState.FAILED) {\n reset();\n }\n\n // 开始新的初始化过程\n state = SingletonState.INITIALIZING;\n initPromise = createInstance();\n\n try {\n instance = await initPromise;\n state = SingletonState.INITIALIZED;\n instanceId = `mcp-manager-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\n lastError = null;\n\n console.log(`✅ MCPServiceManager 单例初始化成功,实例ID: ${instanceId}`);\n return instance;\n } catch (error) {\n state = SingletonState.FAILED;\n lastError = error as Error;\n initPromise = null;\n\n console.error(\n \"❌ MCPServiceManager 单例初始化失败:\",\n (error as Error).message\n );\n throw error;\n }\n}\n\n/**\n * 清理单例资源\n *\n * @returns Promise<void>\n */\nasync function cleanup(): Promise<void> {\n if (state === SingletonState.CLEANUP) {\n console.log(\"⚠️ MCPServiceManager 单例已在清理中,跳过重复清理\");\n return;\n }\n\n console.log(\"🧹 正在清理 MCPServiceManager 单例资源...\");\n state = SingletonState.CLEANUP;\n\n try {\n // 清理初始化Promise\n if (initPromise) {\n try {\n const instanceFromPromise = await initPromise;\n await instanceFromPromise.stopAllServices();\n } catch (error) {\n console.error(\"清理初始化中的实例失败:\", (error as Error).message);\n }\n initPromise = null;\n }\n\n // 清理已初始化的实例\n if (instance) {\n await instance.stopAllServices();\n instance = null;\n }\n\n state = SingletonState.NOT_INITIALIZED;\n lastError = null;\n instanceId = null;\n\n console.log(\"✅ MCPServiceManager 单例资源清理完成\");\n } catch (error) {\n console.error(\n \"❌ MCPServiceManager 单例清理失败:\",\n (error as Error).message\n );\n // 即使清理失败,也要重置状态,避免永久锁定\n reset();\n throw error;\n }\n}\n\n/**\n * 重置单例状态(用于错误恢复)\n *\n * 注意:这个方法不会清理资源,只是重置状态\n * 如果需要清理资源,请使用 cleanup() 方法\n */\nfunction reset(): void {\n console.log(\"🔄 重置 MCPServiceManager 单例状态\");\n\n instance = null;\n initPromise = null;\n state = SingletonState.NOT_INITIALIZED;\n lastError = null;\n instanceId = null;\n}\n\n/**\n * 检查单例是否已初始化\n *\n * @returns boolean 是否已初始化\n */\nfunction isInitialized(): boolean {\n return state === SingletonState.INITIALIZED && instance !== null;\n}\n\n/**\n * 获取单例状态信息\n *\n * @returns SingletonStatus 状态信息\n */\nfunction getStatus(): SingletonStatus {\n return {\n state: state,\n initializationTime: instanceId ? new Date() : undefined,\n lastError: lastError || undefined,\n instanceId: instanceId || undefined,\n };\n}\n\n/**\n * 强制重新初始化单例\n *\n * 这个方法会先清理现有资源,然后重新初始化\n *\n * @returns Promise<MCPServiceManager> 新的管理器实例\n */\nasync function forceReinitialize(): Promise<MCPServiceManager> {\n console.log(\"🔄 强制重新初始化 MCPServiceManager 单例...\");\n\n await cleanup();\n return getInstance();\n}\n\n/**\n * 获取当前实例(同步方法,仅在确定已初始化时使用)\n *\n * @returns MCPServiceManager | null 当前实例或null\n */\nfunction getCurrentInstance(): MCPServiceManager | null {\n return instance;\n}\n\n/**\n * 等待初始化完成(如果正在初始化中)\n *\n * @returns Promise<boolean> 是否成功初始化\n */\nasync function waitForInitialization(): Promise<boolean> {\n if (state === SingletonState.INITIALIZED) {\n return true;\n }\n\n if (state === SingletonState.INITIALIZING && initPromise) {\n try {\n await initPromise;\n return true;\n } catch (error) {\n return false;\n }\n }\n\n return false;\n}\n\n/**\n * MCPServiceManager 全局单例管理器\n *\n * 使用对象包装模块级函数,保持原有API接口不变\n */\nexport const MCPServiceManagerSingleton = {\n getInstance,\n cleanup,\n reset,\n isInitialized,\n getStatus,\n forceReinitialize,\n getCurrentInstance,\n waitForInitialization,\n} as const;\n\n// 导出默认实例(便于使用)\nexport default MCPServiceManagerSingleton;\n\n// 进程退出时自动清理资源\nprocess.on(\"exit\", () => {\n if (MCPServiceManagerSingleton.isInitialized()) {\n console.log(\"🔄 进程退出,正在清理 MCPServiceManager 单例...\");\n // 注意:这里不能使用 await,因为 exit 事件是同步的\n MCPServiceManagerSingleton.reset();\n }\n});\n\n// 处理未捕获的异常\nprocess.on(\"uncaughtException\", async (error) => {\n console.error(\"💥 未捕获的异常,清理 MCPServiceManager 单例:\", error);\n try {\n await MCPServiceManagerSingleton.cleanup();\n } catch (cleanupError) {\n console.error(\"清理过程中发生错误:\", cleanupError);\n }\n});\n\n// 处理未处理的Promise拒绝\nprocess.on(\"unhandledRejection\", async (reason) => {\n console.error(\"💥 未处理的Promise拒绝,清理 MCPServiceManager 单例:\", reason);\n try {\n await MCPServiceManagerSingleton.cleanup();\n } catch (cleanupError) {\n console.error(\"清理过程中发生错误:\", cleanupError);\n }\n});\n","import { EventEmitter } from \"node:events\";\nimport type { Tool } from \"@modelcontextprotocol/sdk/types.js\";\nimport { ProxyMCPServer } from \"../ProxyMCPServer.js\";\nimport { Logger } from \"../logger.js\";\n\n// 使用接口定义避免循环依赖\ninterface IMCPServiceManager {\n getAllTools(): Tool[];\n}\n\n// 配置变更事件类型\nexport interface ConfigChangeEvent {\n type:\n | \"endpoints_added\"\n | \"endpoints_removed\"\n | \"endpoints_updated\"\n | \"options_updated\";\n data: {\n added?: string[];\n removed?: string[];\n updated?: string[];\n oldOptions?: Partial<XiaozhiConnectionOptions>;\n newOptions?: Partial<XiaozhiConnectionOptions>;\n };\n timestamp: Date;\n}\n\n// 重连策略枚举\nexport enum ReconnectStrategy {\n EXPONENTIAL_BACKOFF = \"exponential_backoff\",\n LINEAR_BACKOFF = \"linear_backoff\",\n FIXED_INTERVAL = \"fixed_interval\",\n ADAPTIVE = \"adaptive\",\n}\n\n// 错误类型枚举\nexport enum ConnectionErrorType {\n NETWORK_ERROR = \"network_error\",\n AUTHENTICATION_ERROR = \"authentication_error\",\n SERVER_ERROR = \"server_error\",\n TIMEOUT_ERROR = \"timeout_error\",\n UNKNOWN_ERROR = \"unknown_error\",\n}\n\n// 连接选项接口\nexport interface XiaozhiConnectionOptions {\n healthCheckInterval?: number; // 健康检查间隔(毫秒),默认 30000\n reconnectInterval?: number; // 重连间隔(毫秒),默认 5000\n maxReconnectAttempts?: number; // 最大重连次数,默认 10\n loadBalanceStrategy?: \"round-robin\" | \"random\" | \"health-based\"; // 负载均衡策略,默认 'round-robin'\n connectionTimeout?: number; // 连接超时时间(毫秒),默认 10000\n reconnectStrategy?: ReconnectStrategy; // 重连策略,默认 exponential_backoff\n maxReconnectDelay?: number; // 最大重连延迟(毫秒),默认 30000\n reconnectBackoffMultiplier?: number; // 退避乘数,默认 2\n jitterEnabled?: boolean; // 是否启用抖动,默认 true\n}\n\n// 连接状态接口\nexport interface ConnectionStatus {\n endpoint: string;\n connected: boolean;\n initialized: boolean;\n lastConnected?: Date;\n lastError?: string;\n reconnectAttempts: number;\n healthScore: number; // 健康度评分 (0-100)\n lastHealthCheck?: Date;\n responseTime?: number; // 响应时间(毫秒)\n consecutiveFailures: number; // 连续失败次数\n totalRequests: number; // 总请求次数\n successfulRequests: number; // 成功请求次数\n lastSuccessTime?: Date; // 最后成功时间\n healthCheckEnabled: boolean; // 是否启用健康检查\n // 重连相关状态\n isReconnecting: boolean; // 是否正在重连\n nextReconnectTime?: Date; // 下次重连时间\n lastReconnectAttempt?: Date; // 最后重连尝试时间\n reconnectDelay: number; // 当前重连延迟(毫秒)\n errorType?: ConnectionErrorType; // 错误类型\n reconnectHistory: Array<{\n // 重连历史\n timestamp: Date;\n success: boolean;\n error?: string;\n delay: number;\n }>;\n}\n\n// 连接状态枚举\nexport enum XiaozhiConnectionState {\n DISCONNECTED = \"disconnected\",\n CONNECTING = \"connecting\",\n CONNECTED = \"connected\",\n RECONNECTING = \"reconnecting\",\n FAILED = \"failed\",\n}\n\n// 默认配置\nconst DEFAULT_OPTIONS: Required<XiaozhiConnectionOptions> = {\n healthCheckInterval: 30000,\n reconnectInterval: 5000,\n maxReconnectAttempts: 10,\n loadBalanceStrategy: \"round-robin\",\n connectionTimeout: 10000,\n reconnectStrategy: ReconnectStrategy.EXPONENTIAL_BACKOFF,\n maxReconnectDelay: 30000,\n reconnectBackoffMultiplier: 2,\n jitterEnabled: true,\n};\n\n/**\n * 小智连接管理器\n * 负责管理多个小智接入点的连接,提供统一的连接管理、健康检查、负载均衡等功能\n */\nexport class XiaozhiConnectionManager extends EventEmitter {\n // 连接实例管理\n private connections: Map<string, ProxyMCPServer> = new Map();\n private connectionStates: Map<string, ConnectionStatus> = new Map();\n\n // 核心依赖\n private mcpServiceManager: IMCPServiceManager | null = null;\n private logger: Logger;\n\n // 状态管理\n private isInitialized = false;\n private isConnecting = false;\n\n // 配置选项\n private options: Required<XiaozhiConnectionOptions>;\n\n // 健康检查和重连管理\n private healthCheckInterval: NodeJS.Timeout | null = null;\n private reconnectTimers: Map<string, NodeJS.Timeout> = new Map();\n\n // 负载均衡状态\n private roundRobinIndex = 0;\n private lastSelectedEndpoint: string | null = null;\n\n // 性能监控\n private performanceMetrics = {\n connectionStartTime: 0,\n totalConnectionTime: 0,\n averageConnectionTime: 0,\n connectionCount: 0,\n memoryUsage: {\n initial: 0,\n current: 0,\n peak: 0,\n },\n prewarmedConnections: new Set<string>(),\n };\n\n constructor(options?: XiaozhiConnectionOptions) {\n super();\n this.logger = new Logger();\n this.options = { ...DEFAULT_OPTIONS, ...options };\n\n this.logger.info(\"XiaozhiConnectionManager 实例已创建\");\n this.logger.debug(\"配置选项:\", this.options);\n }\n\n /**\n * 初始化连接管理器\n * @param endpoints 小智接入点列表\n * @param tools 工具列表\n */\n async initialize(endpoints: string[], tools: Tool[]): Promise<void> {\n if (this.isInitialized) {\n this.logger.warn(\"XiaozhiConnectionManager 已经初始化,跳过重复初始化\");\n return;\n }\n\n this.logger.info(\n `开始初始化 XiaozhiConnectionManager,端点数量: ${endpoints.length}`\n );\n\n try {\n // 验证输入参数\n this.validateInitializeParams(endpoints, tools);\n\n // 清理现有连接(如果有)\n await this.cleanup();\n\n // 为每个端点创建连接实例\n for (const endpoint of endpoints) {\n await this.createConnection(endpoint, tools);\n }\n\n this.isInitialized = true;\n\n // 记录初始内存使用\n this.performanceMetrics.memoryUsage.initial =\n process.memoryUsage().heapUsed;\n this.performanceMetrics.memoryUsage.current =\n this.performanceMetrics.memoryUsage.initial;\n this.performanceMetrics.memoryUsage.peak =\n this.performanceMetrics.memoryUsage.initial;\n\n this.logger.info(\n `XiaozhiConnectionManager 初始化完成,管理 ${this.connections.size} 个连接`\n );\n } catch (error) {\n this.logger.error(\"XiaozhiConnectionManager 初始化失败:\", error);\n await this.cleanup(); // 清理部分创建的连接\n throw error;\n }\n }\n\n /**\n * 连接所有端点\n */\n async connect(): Promise<void> {\n if (!this.isInitialized) {\n throw new Error(\n \"XiaozhiConnectionManager 未初始化,请先调用 initialize()\"\n );\n }\n\n if (this.isConnecting) {\n this.logger.warn(\"连接操作正在进行中,请等待完成\");\n return;\n }\n\n this.isConnecting = true;\n this.performanceMetrics.connectionStartTime = Date.now();\n this.logger.info(`开始连接所有端点,总数: ${this.connections.size}`);\n\n try {\n const connectionPromises: Promise<void>[] = [];\n\n // 并发连接所有端点\n for (const [endpoint, proxyServer] of this.connections) {\n connectionPromises.push(\n this.connectSingleEndpoint(endpoint, proxyServer)\n );\n }\n\n // 等待所有连接完成(允许部分失败)\n const results = await Promise.allSettled(connectionPromises);\n\n // 统计连接结果\n const successCount = results.filter(\n (result) => result.status === \"fulfilled\"\n ).length;\n const failureCount = results.length - successCount;\n\n // 更新性能指标\n const connectionTime =\n Date.now() - this.performanceMetrics.connectionStartTime;\n this.performanceMetrics.totalConnectionTime += connectionTime;\n this.performanceMetrics.connectionCount++;\n this.performanceMetrics.averageConnectionTime =\n this.performanceMetrics.totalConnectionTime /\n this.performanceMetrics.connectionCount;\n\n this.logger.info(\n `连接完成 - 成功: ${successCount}, 失败: ${failureCount}, 耗时: ${connectionTime}ms`\n );\n\n // 如果所有连接都失败,抛出错误\n if (successCount === 0) {\n throw new Error(\"所有小智接入点连接失败\");\n }\n\n // 启动健康检查\n this.startHealthCheck();\n } finally {\n this.isConnecting = false;\n }\n }\n\n /**\n * 断开所有连接\n */\n async disconnect(): Promise<void> {\n this.logger.info(\"开始断开所有连接\");\n\n // 停止健康检查\n this.stopHealthCheck();\n\n // 清理重连定时器\n this.clearAllReconnectTimers();\n\n // 断开所有连接\n const disconnectPromises: Promise<void>[] = [];\n for (const [endpoint, proxyServer] of this.connections) {\n disconnectPromises.push(\n this.disconnectSingleEndpoint(endpoint, proxyServer)\n );\n }\n\n await Promise.allSettled(disconnectPromises);\n this.logger.info(\"所有连接已断开\");\n }\n\n /**\n * 动态添加端点\n * @param endpoint 端点地址\n */\n async addEndpoint(endpoint: string): Promise<void> {\n if (!this.isInitialized) {\n throw new Error(\"XiaozhiConnectionManager 未初始化\");\n }\n\n if (this.connections.has(endpoint)) {\n this.logger.warn(`端点 ${endpoint} 已存在,跳过添加`);\n return;\n }\n\n this.logger.info(`动态添加端点: ${endpoint}`);\n\n try {\n // 获取当前工具列表\n const tools = this.getCurrentTools();\n\n // 创建新连接\n await this.createConnection(endpoint, tools);\n\n // 如果管理器已连接,则连接新端点\n if (this.isAnyConnected()) {\n const proxyServer = this.connections.get(endpoint)!;\n await this.connectSingleEndpoint(endpoint, proxyServer);\n }\n\n this.logger.info(`端点 ${endpoint} 添加成功`);\n } catch (error) {\n this.logger.error(`添加端点 ${endpoint} 失败:`, error);\n // 清理失败的连接\n this.connections.delete(endpoint);\n this.connectionStates.delete(endpoint);\n throw error;\n }\n }\n\n /**\n * 动态移除端点\n * @param endpoint 端点地址\n */\n async removeEndpoint(endpoint: string): Promise<void> {\n if (!this.connections.has(endpoint)) {\n this.logger.warn(`端点 ${endpoint} 不存在,跳过移除`);\n return;\n }\n\n this.logger.info(`动态移除端点: ${endpoint}`);\n\n try {\n const proxyServer = this.connections.get(endpoint)!;\n\n // 断开连接\n await this.disconnectSingleEndpoint(endpoint, proxyServer);\n\n // 清理资源\n this.connections.delete(endpoint);\n this.connectionStates.delete(endpoint);\n\n // 清理重连定时器\n const timer = this.reconnectTimers.get(endpoint);\n if (timer) {\n clearTimeout(timer);\n this.reconnectTimers.delete(endpoint);\n }\n\n this.logger.info(`端点 ${endpoint} 移除成功`);\n } catch (error) {\n this.logger.error(`移除端点 ${endpoint} 失败:`, error);\n throw error;\n }\n }\n\n /**\n * 获取健康的连接列表\n */\n getHealthyConnections(): ProxyMCPServer[] {\n const healthyConnections: ProxyMCPServer[] = [];\n\n for (const [endpoint, proxyServer] of this.connections) {\n const status = this.connectionStates.get(endpoint);\n if (status?.connected && status.healthScore > 50) {\n healthyConnections.push(proxyServer);\n }\n }\n\n return healthyConnections;\n }\n\n /**\n * 获取所有连接状态\n */\n getConnectionStatus(): ConnectionStatus[] {\n return Array.from(this.connectionStates.values());\n }\n\n /**\n * 检查是否有任何连接处于连接状态\n */\n isAnyConnected(): boolean {\n for (const status of this.connectionStates.values()) {\n if (status.connected) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * 设置 MCP 服务管理器\n * @param manager MCP 服务管理器实例\n */\n setServiceManager(manager: IMCPServiceManager): void {\n this.mcpServiceManager = manager;\n this.logger.info(\"已设置 MCPServiceManager\");\n\n // 如果已有连接,同步工具到所有连接\n if (this.connections.size > 0) {\n this.syncToolsToAllConnections();\n }\n }\n\n /**\n * 启用/禁用指定端点的健康检查\n * @param endpoint 端点地址\n * @param enabled 是否启用\n */\n setHealthCheckEnabled(endpoint: string, enabled: boolean): void {\n const status = this.connectionStates.get(endpoint);\n if (status) {\n status.healthCheckEnabled = enabled;\n this.logger.info(\n `端点 ${endpoint} 健康检查已${enabled ? \"启用\" : \"禁用\"}`\n );\n } else {\n this.logger.warn(`端点 ${endpoint} 不存在,无法设置健康检查状态`);\n }\n }\n\n /**\n * 获取健康检查统计信息\n */\n getHealthCheckStats(): Record<\n string,\n {\n endpoint: string;\n healthScore: number;\n successRate: number;\n averageResponseTime: number;\n consecutiveFailures: number;\n lastHealthCheck?: Date;\n lastSuccessTime?: Date;\n }\n > {\n const stats: Record<string, any> = {};\n\n for (const [endpoint, status] of this.connectionStates) {\n const successRate =\n status.totalRequests > 0\n ? (status.successfulRequests / status.totalRequests) * 100\n : 0;\n\n stats[endpoint] = {\n endpoint,\n healthScore: status.healthScore,\n successRate: Math.round(successRate * 100) / 100,\n averageResponseTime: status.responseTime || 0,\n consecutiveFailures: status.consecutiveFailures,\n lastHealthCheck: status.lastHealthCheck,\n lastSuccessTime: status.lastSuccessTime,\n };\n }\n\n return stats;\n }\n\n /**\n * 手动触发健康检查\n */\n async triggerHealthCheck(): Promise<void> {\n this.logger.info(\"手动触发健康检查\");\n await this.performHealthCheck();\n }\n\n /**\n * 手动触发指定端点的重连\n * @param endpoint 端点地址\n */\n async triggerReconnect(endpoint: string): Promise<void> {\n const status = this.connectionStates.get(endpoint);\n if (!status) {\n throw new Error(`端点 ${endpoint} 不存在`);\n }\n\n if (status.connected) {\n this.logger.warn(`端点 ${endpoint} 已连接,无需重连`);\n return;\n }\n\n this.logger.info(`手动触发重连: ${endpoint}`);\n\n // 清理现有的重连定时器\n const existingTimer = this.reconnectTimers.get(endpoint);\n if (existingTimer) {\n clearTimeout(existingTimer);\n this.reconnectTimers.delete(endpoint);\n }\n\n // 立即执行重连\n await this.performReconnect(endpoint);\n }\n\n /**\n * 停止指定端点的重连\n * @param endpoint 端点地址\n */\n stopReconnect(endpoint: string): void {\n const status = this.connectionStates.get(endpoint);\n if (!status) {\n this.logger.warn(`端点 ${endpoint} 不存在`);\n return;\n }\n\n const timer = this.reconnectTimers.get(endpoint);\n if (timer) {\n clearTimeout(timer);\n this.reconnectTimers.delete(endpoint);\n status.isReconnecting = false;\n status.nextReconnectTime = undefined;\n this.logger.info(`已停止端点 ${endpoint} 的重连`);\n }\n }\n\n /**\n * 停止所有端点的重连\n */\n stopAllReconnects(): void {\n this.logger.info(\"停止所有端点的重连\");\n\n for (const [endpoint] of this.reconnectTimers) {\n this.stopReconnect(endpoint);\n }\n }\n\n /**\n * 获取重连统计信息\n */\n getReconnectStats(): Record<\n string,\n {\n endpoint: string;\n reconnectAttempts: number;\n isReconnecting: boolean;\n nextReconnectTime?: Date;\n lastReconnectAttempt?: Date;\n reconnectDelay: number;\n errorType?: ConnectionErrorType;\n recentReconnectHistory: Array<{\n timestamp: Date;\n success: boolean;\n error?: string;\n delay: number;\n }>;\n }\n > {\n const stats: Record<string, any> = {};\n\n for (const [endpoint, status] of this.connectionStates) {\n stats[endpoint] = {\n endpoint,\n reconnectAttempts: status.reconnectAttempts,\n isReconnecting: status.isReconnecting,\n nextReconnectTime: status.nextReconnectTime,\n lastReconnectAttempt: status.lastReconnectAttempt,\n reconnectDelay: status.reconnectDelay,\n errorType: status.errorType,\n recentReconnectHistory: status.reconnectHistory.slice(-5), // 最近5次\n };\n }\n\n return stats;\n }\n\n /**\n * 验证端点配置\n */\n private validateEndpoints(endpoints: string[]): {\n valid: string[];\n invalid: string[];\n } {\n const valid: string[] = [];\n const invalid: string[] = [];\n\n for (const endpoint of endpoints) {\n if (!endpoint || typeof endpoint !== \"string\") {\n invalid.push(endpoint);\n continue;\n }\n\n if (!endpoint.startsWith(\"ws://\") && !endpoint.startsWith(\"wss://\")) {\n invalid.push(endpoint);\n continue;\n }\n\n // 检查是否是有效的 URL\n try {\n new URL(endpoint);\n valid.push(endpoint);\n } catch {\n invalid.push(endpoint);\n }\n }\n\n return { valid, invalid };\n }\n\n /**\n * 验证连接选项\n */\n private validateOptions(options: Partial<XiaozhiConnectionOptions>): {\n valid: boolean;\n errors: string[];\n } {\n const errors: string[] = [];\n\n if (options.healthCheckInterval !== undefined) {\n if (\n typeof options.healthCheckInterval !== \"number\" ||\n options.healthCheckInterval < 1000\n ) {\n errors.push(\"healthCheckInterval 必须是大于等于 1000 的数字\");\n }\n }\n\n if (options.reconnectInterval !== undefined) {\n if (\n typeof options.reconnectInterval !== \"number\" ||\n options.reconnectInterval < 100\n ) {\n errors.push(\"reconnectInterval 必须是大于等于 100 的数字\");\n }\n }\n\n if (options.maxReconnectAttempts !== undefined) {\n if (\n typeof options.maxReconnectAttempts !== \"number\" ||\n options.maxReconnectAttempts < 0\n ) {\n errors.push(\"maxReconnectAttempts 必须是大于等于 0 的数字\");\n }\n }\n\n if (options.connectionTimeout !== undefined) {\n if (\n typeof options.connectionTimeout !== \"number\" ||\n options.connectionTimeout < 1000\n ) {\n errors.push(\"connectionTimeout 必须是大于等于 1000 的数字\");\n }\n }\n\n if (options.maxReconnectDelay !== undefined) {\n if (\n typeof options.maxReconnectDelay !== \"number\" ||\n options.maxReconnectDelay < 1000\n ) {\n errors.push(\"maxReconnectDelay 必须是大于等于 1000 的数字\");\n }\n }\n\n if (options.reconnectBackoffMultiplier !== undefined) {\n if (\n typeof options.reconnectBackoffMultiplier !== \"number\" ||\n options.reconnectBackoffMultiplier < 1\n ) {\n errors.push(\"reconnectBackoffMultiplier 必须是大于等于 1 的数字\");\n }\n }\n\n if (options.loadBalanceStrategy !== undefined) {\n const validStrategies = [\"round-robin\", \"random\", \"health-based\"];\n if (!validStrategies.includes(options.loadBalanceStrategy)) {\n errors.push(\n `loadBalanceStrategy 必须是以下值之一: ${validStrategies.join(\", \")}`\n );\n }\n }\n\n if (options.reconnectStrategy !== undefined) {\n const validStrategies = Object.values(ReconnectStrategy);\n if (!validStrategies.includes(options.reconnectStrategy)) {\n errors.push(\n `reconnectStrategy 必须是以下值之一: ${validStrategies.join(\", \")}`\n );\n }\n }\n\n return { valid: errors.length === 0, errors };\n }\n\n /**\n * 更新端点配置\n * @param newEndpoints 新的端点列表\n * @param tools 工具列表\n */\n async updateEndpoints(\n newEndpoints: string[],\n tools: Tool[] = []\n ): Promise<void> {\n if (!this.isInitialized) {\n throw new Error(\"XiaozhiConnectionManager 未初始化\");\n }\n\n this.logger.info(`更新端点配置,新端点数量: ${newEndpoints.length}`);\n\n // 验证新端点\n const { valid: validEndpoints, invalid: invalidEndpoints } =\n this.validateEndpoints(newEndpoints);\n\n if (invalidEndpoints.length > 0) {\n this.logger.warn(`发现无效端点: ${invalidEndpoints.join(\", \")}`);\n }\n\n if (validEndpoints.length === 0) {\n throw new Error(\"没有有效的端点\");\n }\n\n // 计算变更\n const currentEndpoints = Array.from(this.connections.keys());\n const toAdd = validEndpoints.filter((ep) => !currentEndpoints.includes(ep));\n const toRemove = currentEndpoints.filter(\n (ep) => !validEndpoints.includes(ep)\n );\n const toKeep = currentEndpoints.filter((ep) => validEndpoints.includes(ep));\n\n this.logger.info(\n `端点变更 - 添加: ${toAdd.length}, 移除: ${toRemove.length}, 保持: ${toKeep.length}`\n );\n\n try {\n // 移除不需要的端点\n for (const endpoint of toRemove) {\n await this.removeEndpoint(endpoint);\n }\n\n // 添加新端点\n for (const endpoint of toAdd) {\n await this.addEndpoint(endpoint);\n }\n\n // 发送配置变更事件\n const changeEvent: ConfigChangeEvent = {\n type:\n toAdd.length > 0 && toRemove.length > 0\n ? \"endpoints_updated\"\n : toAdd.length > 0\n ? \"endpoints_added\"\n : \"endpoints_removed\",\n data: {\n added: toAdd.length > 0 ? toAdd : undefined,\n removed: toRemove.length > 0 ? toRemove : undefined,\n updated:\n toAdd.length > 0 && toRemove.length > 0\n ? validEndpoints\n : undefined,\n },\n timestamp: new Date(),\n };\n\n this.emit(\"configChange\", changeEvent);\n this.logger.info(\"端点配置更新完成\");\n } catch (error) {\n this.logger.error(\"端点配置更新失败:\", error);\n throw error;\n }\n }\n\n /**\n * 更新连接选项\n * @param newOptions 新的连接选项\n */\n updateOptions(newOptions: Partial<XiaozhiConnectionOptions>): void {\n this.logger.info(\"更新连接选项\");\n\n // 验证新选项\n const { valid, errors } = this.validateOptions(newOptions);\n\n if (!valid) {\n throw new Error(`无效的连接选项: ${errors.join(\", \")}`);\n }\n\n const oldOptions = { ...this.options };\n\n // 更新选项\n this.options = { ...this.options, ...newOptions };\n\n // 发送配置变更事件\n const changeEvent: ConfigChangeEvent = {\n type: \"options_updated\",\n data: {\n oldOptions,\n newOptions,\n },\n timestamp: new Date(),\n };\n\n this.emit(\"configChange\", changeEvent);\n this.logger.info(\"连接选项更新完成\");\n this.logger.debug(\"新的配置选项:\", this.options);\n }\n\n /**\n * 获取当前配置\n */\n getCurrentConfig(): {\n endpoints: string[];\n options: Required<XiaozhiConnectionOptions>;\n } {\n return {\n endpoints: Array.from(this.connections.keys()),\n options: { ...this.options },\n };\n }\n\n /**\n * 热重载配置\n * @param config 新配置\n */\n async reloadConfig(config: {\n endpoints?: string[];\n options?: Partial<XiaozhiConnectionOptions>;\n tools?: Tool[];\n }): Promise<void> {\n this.logger.info(\"开始热重载配置\");\n\n try {\n // 更新选项(如果提供)\n if (config.options) {\n this.updateOptions(config.options);\n }\n\n // 更新端点(如果提供)\n if (config.endpoints) {\n await this.updateEndpoints(config.endpoints, config.tools || []);\n }\n\n this.logger.info(\"配置热重载完成\");\n } catch (error) {\n this.logger.error(\"配置热重载失败:\", error);\n throw error;\n }\n }\n\n /**\n * 根据负载均衡策略选择最佳连接\n * @param excludeEndpoints 要排除的端点列表\n */\n selectBestConnection(excludeEndpoints: string[] = []): ProxyMCPServer | null {\n const healthyConnections = this.getHealthyConnections();\n\n if (healthyConnections.length === 0) {\n this.logger.warn(\"没有健康的连接可用\");\n return null;\n }\n\n // 过滤掉要排除的端点\n const availableConnections = healthyConnections.filter((connection) => {\n const endpoint = this.getEndpointByConnection(connection);\n return endpoint && !excludeEndpoints.includes(endpoint);\n });\n\n if (availableConnections.length === 0) {\n this.logger.warn(\"没有可用的连接(排除指定端点后)\");\n return null;\n }\n\n let selectedConnection: ProxyMCPServer;\n\n switch (this.options.loadBalanceStrategy) {\n case \"round-robin\":\n selectedConnection = this.selectRoundRobin(availableConnections);\n break;\n\n case \"random\":\n selectedConnection = this.selectRandom(availableConnections);\n break;\n\n case \"health-based\":\n selectedConnection = this.selectHealthBased(availableConnections);\n break;\n\n default:\n selectedConnection = this.selectRoundRobin(availableConnections);\n }\n\n // 更新最后选择的端点\n this.lastSelectedEndpoint =\n this.getEndpointByConnection(selectedConnection);\n\n return selectedConnection;\n }\n\n /**\n * 轮询算法选择连接\n */\n private selectRoundRobin(connections: ProxyMCPServer[]): ProxyMCPServer {\n if (connections.length === 0) {\n throw new Error(\"没有可用的连接\");\n }\n\n const connection = connections[this.roundRobinIndex % connections.length];\n this.roundRobinIndex = (this.roundRobinIndex + 1) % connections.length;\n\n return connection;\n }\n\n /**\n * 随机算法选择连接\n */\n private selectRandom(connections: ProxyMCPServer[]): ProxyMCPServer {\n if (connections.length === 0) {\n throw new Error(\"没有可用的连接\");\n }\n\n const randomIndex = Math.floor(Math.random() * connections.length);\n return connections[randomIndex];\n }\n\n /**\n * 基于健康度的选择算法\n */\n private selectHealthBased(connections: ProxyMCPServer[]): ProxyMCPServer {\n if (connections.length === 0) {\n throw new Error(\"没有可用的连接\");\n }\n\n // 获取所有连接的健康度信息\n const connectionHealths = connections.map((connection) => {\n const endpoint = this.getEndpointByConnection(connection);\n const status = endpoint ? this.connectionStates.get(endpoint) : null;\n return {\n connection,\n endpoint,\n healthScore: status?.healthScore || 0,\n responseTime: status?.responseTime || Number.POSITIVE_INFINITY,\n successRate:\n status && status.totalRequests > 0\n ? (status.successfulRequests / status.totalRequests) * 100\n : 0,\n };\n });\n\n // 按健康度排序(健康度高、响应时间短、成功率高的优先)\n connectionHealths.sort((a, b) => {\n // 首先按健康度排序\n if (a.healthScore !== b.healthScore) {\n return b.healthScore - a.healthScore;\n }\n\n // 健康度相同时,按成功率排序\n if (a.successRate !== b.successRate) {\n return b.successRate - a.successRate;\n }\n\n // 成功率相同时,按响应时间排序\n return a.responseTime - b.responseTime;\n });\n\n // 使用加权随机选择,健康度高的连接被选中的概率更大\n const totalWeight = connectionHealths.reduce(\n (sum, item) => sum + (item.healthScore + 1),\n 0\n );\n let randomWeight = Math.random() * totalWeight;\n\n for (const item of connectionHealths) {\n randomWeight -= item.healthScore + 1;\n if (randomWeight <= 0) {\n return item.connection;\n }\n }\n\n // 如果没有选中任何连接,返回健康度最高的\n return connectionHealths[0].connection;\n }\n\n /**\n * 根据连接实例获取端点地址\n */\n private getEndpointByConnection(connection: ProxyMCPServer): string | null {\n for (const [endpoint, conn] of this.connections) {\n if (conn === connection) {\n return endpoint;\n }\n }\n return null;\n }\n\n /**\n * 获取负载均衡统计信息\n */\n getLoadBalanceStats(): {\n strategy: string;\n totalConnections: number;\n healthyConnections: number;\n lastSelectedEndpoint: string | null;\n roundRobinIndex: number;\n connectionWeights: Record<\n string,\n {\n healthScore: number;\n responseTime: number;\n successRate: number;\n weight: number;\n }\n >;\n } {\n const healthyConnections = this.getHealthyConnections();\n const connectionWeights: Record<string, any> = {};\n\n for (const [endpoint, status] of this.connectionStates) {\n const successRate =\n status.totalRequests > 0\n ? (status.successfulRequests / status.totalRequests) * 100\n : 0;\n\n // 计算权重(用于健康度选择算法)\n const weight = status.healthScore + 1;\n\n connectionWeights[endpoint] = {\n healthScore: status.healthScore,\n responseTime: status.responseTime || 0,\n successRate: Math.round(successRate * 100) / 100,\n weight,\n };\n }\n\n return {\n strategy: this.options.loadBalanceStrategy,\n totalConnections: this.connections.size,\n healthyConnections: healthyConnections.length,\n lastSelectedEndpoint: this.lastSelectedEndpoint,\n roundRobinIndex: this.roundRobinIndex,\n connectionWeights,\n };\n }\n\n /**\n * 切换负载均衡策略\n * @param strategy 新的负载均衡策略\n */\n setLoadBalanceStrategy(\n strategy: \"round-robin\" | \"random\" | \"health-based\"\n ): void {\n const oldStrategy = this.options.loadBalanceStrategy;\n this.options.loadBalanceStrategy = strategy;\n\n // 重置轮询索引\n if (strategy === \"round-robin\") {\n this.roundRobinIndex = 0;\n }\n\n this.logger.info(`负载均衡策略已从 ${oldStrategy} 切换到 ${strategy}`);\n\n // 发送配置变更事件\n const changeEvent: ConfigChangeEvent = {\n type: \"options_updated\",\n data: {\n oldOptions: { loadBalanceStrategy: oldStrategy },\n newOptions: { loadBalanceStrategy: strategy },\n },\n timestamp: new Date(),\n };\n\n this.emit(\"configChange\", changeEvent);\n }\n\n /**\n * 执行故障转移\n * @param failedEndpoint 失败的端点\n */\n async performFailover(\n failedEndpoint: string\n ): Promise<ProxyMCPServer | null> {\n this.logger.warn(`执行故障转移,失败端点: ${failedEndpoint}`);\n\n // 选择备用连接(排除失败的端点)\n const backupConnection = this.selectBestConnection([failedEndpoint]);\n\n if (!backupConnection) {\n this.logger.error(\"故障转移失败:没有可用的备用连接\");\n return null;\n }\n\n const backupEndpoint = this.getEndpointByConnection(backupConnection);\n this.logger.info(`故障转移成功,切换到端点: ${backupEndpoint}`);\n\n return backupConnection;\n }\n\n /**\n * 连接预热机制\n * @param endpoints 要预热的端点列表\n */\n async prewarmConnections(endpoints: string[] = []): Promise<void> {\n const targetEndpoints =\n endpoints.length > 0 ? endpoints : Array.from(this.connections.keys());\n\n this.logger.info(`开始预热连接,端点数量: ${targetEndpoints.length}`);\n\n const prewarmPromises = targetEndpoints.map(async (endpoint) => {\n if (this.performanceMetrics.prewarmedConnections.has(endpoint)) {\n this.logger.debug(`端点 ${endpoint} 已预热,跳过`);\n return;\n }\n\n try {\n const connection = this.connections.get(endpoint);\n if (connection) {\n // 执行预热操作(例如建立连接、验证状态等)\n await this.performHealthCheck();\n this.performanceMetrics.prewarmedConnections.add(endpoint);\n this.logger.debug(`端点 ${endpoint} 预热完成`);\n }\n } catch (error) {\n this.logger.warn(`端点 ${endpoint} 预热失败:`, error);\n }\n });\n\n await Promise.all(prewarmPromises);\n this.logger.info(\n `连接预热完成,成功预热 ${this.performanceMetrics.prewarmedConnections.size} 个端点`\n );\n }\n\n /**\n * 更新性能指标\n */\n private updatePerformanceMetrics(): void {\n const currentMemory = process.memoryUsage().heapUsed;\n this.performanceMetrics.memoryUsage.current = currentMemory;\n\n if (currentMemory > this.performanceMetrics.memoryUsage.peak) {\n this.performanceMetrics.memoryUsage.peak = currentMemory;\n }\n }\n\n /**\n * 获取性能指标\n */\n getPerformanceMetrics(): {\n connectionTime: {\n total: number;\n average: number;\n count: number;\n };\n memoryUsage: {\n initial: number;\n current: number;\n peak: number;\n growth: number;\n growthPercentage: number;\n };\n prewarmedConnections: number;\n totalConnections: number;\n healthyConnections: number;\n } {\n this.updatePerformanceMetrics();\n\n const memoryGrowth =\n this.performanceMetrics.memoryUsage.current -\n this.performanceMetrics.memoryUsage.initial;\n const growthPercentage =\n this.performanceMetrics.memoryUsage.initial > 0\n ? (memoryGrowth / this.performanceMetrics.memoryUsage.initial) * 100\n : 0;\n\n return {\n connectionTime: {\n total: this.performanceMetrics.totalConnectionTime,\n average: this.performanceMetrics.averageConnectionTime,\n count: this.performanceMetrics.connectionCount,\n },\n memoryUsage: {\n initial: this.performanceMetrics.memoryUsage.initial,\n current: this.performanceMetrics.memoryUsage.current,\n peak: this.performanceMetrics.memoryUsage.peak,\n growth: memoryGrowth,\n growthPercentage: Math.round(growthPercentage * 100) / 100,\n },\n prewarmedConnections: this.performanceMetrics.prewarmedConnections.size,\n totalConnections: this.connections.size,\n healthyConnections: this.getHealthyConnections().length,\n };\n }\n\n /**\n * 优化内存使用\n */\n optimizeMemoryUsage(): void {\n this.logger.info(\"开始内存优化\");\n\n // 清理过期的重连历史记录和健康检查历史\n for (const [, status] of this.connectionStates) {\n if (status.reconnectHistory && status.reconnectHistory.length > 10) {\n // 只保留最近10次记录\n status.reconnectHistory = status.reconnectHistory.slice(-10);\n }\n\n // 清理过期的健康检查历史\n // 重置一些累积的统计数据以防止无限增长\n if (status.totalRequests > 10000) {\n // 重置计数器,但保持比率\n const successRate = status.successfulRequests / status.totalRequests;\n status.totalRequests = 1000;\n status.successfulRequests = Math.round(successRate * 1000);\n }\n }\n\n // 强制垃圾回收(如果可用)\n if (global.gc) {\n global.gc();\n }\n\n this.updatePerformanceMetrics();\n this.logger.info(\"内存优化完成\");\n }\n\n /**\n * 资源清理\n */\n async cleanup(): Promise<void> {\n this.logger.info(\"开始清理 XiaozhiConnectionManager 资源\");\n\n try {\n // 断开所有连接\n await this.disconnect();\n\n // 清理连接实例\n this.connections.clear();\n this.connectionStates.clear();\n\n // 重置状态\n this.isInitialized = false;\n this.isConnecting = false;\n this.roundRobinIndex = 0;\n this.lastSelectedEndpoint = null;\n\n this.logger.info(\"XiaozhiConnectionManager 资源清理完成\");\n } catch (error) {\n this.logger.error(\"XiaozhiConnectionManager 资源清理失败:\", error);\n throw error;\n }\n }\n\n // ==================== 私有方法 ====================\n\n /**\n * 验证初始化参数\n */\n private validateInitializeParams(endpoints: string[], tools: Tool[]): void {\n if (!Array.isArray(endpoints) || endpoints.length === 0) {\n throw new Error(\"端点列表不能为空\");\n }\n\n if (!Array.isArray(tools)) {\n throw new Error(\"工具列表必须是数组\");\n }\n\n // 验证端点格式\n for (const endpoint of endpoints) {\n if (!endpoint || typeof endpoint !== \"string\") {\n throw new Error(`无效的端点地址: ${endpoint}`);\n }\n\n if (!endpoint.startsWith(\"ws://\") && !endpoint.startsWith(\"wss://\")) {\n throw new Error(`端点地址必须是 WebSocket URL: ${endpoint}`);\n }\n }\n }\n\n /**\n * 创建单个连接\n */\n private async createConnection(\n endpoint: string,\n tools: Tool[]\n ): Promise<void> {\n this.logger.debug(`创建连接实例: ${endpoint}`);\n\n try {\n // 创建 ProxyMCPServer 实例\n const proxyServer = new ProxyMCPServer(endpoint);\n\n // 设置 MCP 服务管理器\n if (this.mcpServiceManager) {\n proxyServer.setServiceManager(this.mcpServiceManager);\n }\n\n // 存储连接实例\n this.connections.set(endpoint, proxyServer);\n\n // 初始化连接状态\n this.connectionStates.set(endpoint, {\n endpoint,\n connected: false,\n initialized: false,\n reconnectAttempts: 0,\n healthScore: 100, // 初始健康度为满分\n consecutiveFailures: 0,\n totalRequests: 0,\n successfulRequests: 0,\n healthCheckEnabled: true,\n isReconnecting: false,\n reconnectDelay: this.options.reconnectInterval,\n reconnectHistory: [],\n });\n\n this.logger.debug(`连接实例创建成功: ${endpoint}`);\n } catch (error) {\n this.logger.error(`创建连接实例失败 ${endpoint}:`, error);\n throw error;\n }\n }\n\n /**\n * 连接单个端点\n */\n private async connectSingleEndpoint(\n endpoint: string,\n proxyServer: ProxyMCPServer\n ): Promise<void> {\n const status = this.connectionStates.get(endpoint);\n if (!status) {\n throw new Error(`端点状态不存在: ${endpoint}`);\n }\n\n this.logger.debug(`连接端点: ${endpoint}`);\n\n try {\n // 更新状态为连接中\n status.connected = false;\n status.initialized = false;\n\n // 执行连接\n await proxyServer.connect();\n\n // 更新连接成功状态\n status.connected = true;\n status.initialized = true;\n status.lastConnected = new Date();\n status.lastError = undefined;\n status.reconnectAttempts = 0;\n status.healthScore = 100;\n\n this.logger.info(`端点连接成功: ${endpoint}`);\n } catch (error) {\n // 更新连接失败状态\n status.connected = false;\n status.initialized = false;\n status.lastError = error instanceof Error ? error.message : String(error);\n status.reconnectAttempts++;\n status.healthScore = Math.max(0, status.healthScore - 20);\n\n this.logger.error(`端点连接失败 ${endpoint}:`, error);\n\n // 启动重连(如果未超过最大重连次数)\n this.scheduleReconnect(endpoint);\n\n throw error;\n }\n }\n\n /**\n * 断开单个端点\n */\n private async disconnectSingleEndpoint(\n endpoint: string,\n proxyServer: ProxyMCPServer\n ): Promise<void> {\n const status = this.connectionStates.get(endpoint);\n if (!status) {\n return;\n }\n\n this.logger.debug(`断开端点: ${endpoint}`);\n\n try {\n // 执行断开连接(ProxyMCPServer.disconnect 是同步方法)\n proxyServer.disconnect();\n\n // 更新状态\n status.connected = false;\n status.initialized = false;\n\n this.logger.debug(`端点断开成功: ${endpoint}`);\n } catch (error) {\n this.logger.error(`端点断开失败 ${endpoint}:`, error);\n // 即使断开失败,也要更新状态\n status.connected = false;\n status.initialized = false;\n }\n }\n\n /**\n * 获取当前工具列表\n */\n private getCurrentTools(): Tool[] {\n if (!this.mcpServiceManager) {\n return [];\n }\n\n try {\n return this.mcpServiceManager.getAllTools();\n } catch (error) {\n this.logger.error(\"获取工具列表失败:\", error);\n return [];\n }\n }\n\n /**\n * 同步工具到所有连接\n */\n private syncToolsToAllConnections(): void {\n if (!this.mcpServiceManager) {\n return;\n }\n\n this.logger.debug(\"同步工具到所有连接\");\n\n for (const [endpoint, proxyServer] of this.connections) {\n try {\n proxyServer.setServiceManager(this.mcpServiceManager);\n this.logger.debug(`工具同步成功: ${endpoint}`);\n } catch (error) {\n this.logger.error(`工具同步失败 ${endpoint}:`, error);\n }\n }\n }\n\n /**\n * 启动健康检查\n */\n private startHealthCheck(): void {\n if (this.healthCheckInterval) {\n return; // 已经启动\n }\n\n this.logger.debug(\n `启动健康检查,间隔: ${this.options.healthCheckInterval}ms`\n );\n\n this.healthCheckInterval = setInterval(() => {\n this.performHealthCheck();\n }, this.options.healthCheckInterval);\n }\n\n /**\n * 停止健康检查\n */\n private stopHealthCheck(): void {\n if (this.healthCheckInterval) {\n clearInterval(this.healthCheckInterval);\n this.healthCheckInterval = null;\n this.logger.debug(\"健康检查已停止\");\n }\n }\n\n /**\n * 执行健康检查\n */\n private async performHealthCheck(): Promise<void> {\n this.logger.debug(\"执行健康检查\");\n\n const healthCheckPromises: Promise<void>[] = [];\n\n for (const [endpoint, status] of this.connectionStates) {\n if (!status.healthCheckEnabled) {\n continue;\n }\n\n healthCheckPromises.push(this.performSingleHealthCheck(endpoint, status));\n }\n\n // 并发执行所有健康检查\n await Promise.allSettled(healthCheckPromises);\n }\n\n /**\n * 执行单个端点的健康检查\n */\n private async performSingleHealthCheck(\n endpoint: string,\n status: ConnectionStatus\n ): Promise<void> {\n const startTime = Date.now();\n status.lastHealthCheck = new Date();\n status.totalRequests++;\n\n try {\n const proxyServer = this.connections.get(endpoint);\n if (!proxyServer) {\n throw new Error(`连接实例不存在: ${endpoint}`);\n }\n\n // 执行健康检查(这里使用简单的连接状态检查)\n // 在实际实现中,可以调用 ProxyMCPServer 的 ping 方法或其他健康检查方法\n await this.checkConnectionHealth(proxyServer, endpoint);\n\n // 健康检查成功\n const responseTime = Date.now() - startTime;\n this.handleHealthCheckSuccess(status, responseTime);\n } catch (error) {\n // 健康检查失败\n const responseTime = Date.now() - startTime;\n this.handleHealthCheckFailure(status, error as Error, responseTime);\n }\n }\n\n /**\n * 检查连接健康状态\n */\n private async checkConnectionHealth(\n proxyServer: any,\n endpoint: string\n ): Promise<void> {\n // 检查基本连接状态\n if (!proxyServer) {\n throw new Error(\"连接实例不存在\");\n }\n\n // 这里可以扩展更复杂的健康检查逻辑\n // 例如:发送 ping 请求、检查工具列表等\n\n // 模拟健康检查延迟\n await new Promise((resolve) => setTimeout(resolve, 10));\n\n // 如果连接状态为断开,抛出错误\n const status = this.connectionStates.get(endpoint);\n if (!status?.connected) {\n throw new Error(\"连接已断开\");\n }\n }\n\n /**\n * 处理健康检查成功\n */\n private handleHealthCheckSuccess(\n status: ConnectionStatus,\n responseTime: number\n ): void {\n status.successfulRequests++;\n status.consecutiveFailures = 0;\n status.responseTime = responseTime;\n status.lastSuccessTime = new Date();\n\n // 更新健康度评分\n this.updateHealthScore(status, true, responseTime);\n\n this.logger.debug(\n `健康检查成功: ${status.endpoint}, 响应时间: ${responseTime}ms, 健康度: ${status.healthScore}`\n );\n }\n\n /**\n * 处理健康检查失败\n */\n private handleHealthCheckFailure(\n status: ConnectionStatus,\n error: Error,\n responseTime: number\n ): void {\n status.consecutiveFailures++;\n status.responseTime = responseTime;\n status.lastError = error.message;\n\n // 更新健康度评分\n this.updateHealthScore(status, false, responseTime);\n\n this.logger.warn(\n `健康检查失败: ${status.endpoint}, 错误: ${error.message}, 连续失败: ${status.consecutiveFailures}, 健康度: ${status.healthScore}`\n );\n\n // 如果连续失败次数过多,考虑触发重连\n if (status.consecutiveFailures >= 3 && status.connected) {\n this.logger.warn(\n `端点 ${status.endpoint} 连续失败 ${status.consecutiveFailures} 次,可能需要重连`\n );\n // 这里可以触发重连逻辑\n }\n }\n\n /**\n * 更新健康度评分\n */\n private updateHealthScore(\n status: ConnectionStatus,\n success: boolean,\n responseTime: number\n ): void {\n const baseScore = status.healthScore;\n\n if (success) {\n // 成功时增加健康度\n let increment = 5;\n\n // 根据响应时间调整增量\n if (responseTime < 100) {\n increment = 10; // 响应快,增量大\n } else if (responseTime < 500) {\n increment = 7; // 响应中等\n } else if (responseTime < 1000) {\n increment = 5; // 响应慢\n } else {\n increment = 2; // 响应很慢\n }\n\n status.healthScore = Math.min(100, baseScore + increment);\n } else {\n // 失败时减少健康度\n let decrement = 15;\n\n // 根据连续失败次数调整减量\n if (status.consecutiveFailures >= 5) {\n decrement = 30; // 连续失败多次,减量大\n } else if (status.consecutiveFailures >= 3) {\n decrement = 20;\n }\n\n status.healthScore = Math.max(0, baseScore - decrement);\n }\n\n // 计算成功率并调整健康度\n if (status.totalRequests > 0) {\n const successRate = status.successfulRequests / status.totalRequests;\n\n // 如果成功率低于 50%,进一步降低健康度\n if (successRate < 0.5) {\n status.healthScore = Math.max(0, status.healthScore - 10);\n }\n // 如果成功率高于 90%,适当提升健康度\n else if (successRate > 0.9) {\n status.healthScore = Math.min(100, status.healthScore + 5);\n }\n }\n }\n\n /**\n * 分类连接错误类型\n */\n private classifyConnectionError(error: Error): ConnectionErrorType {\n const errorMessage = error.message.toLowerCase();\n\n if (\n errorMessage.includes(\"timeout\") ||\n errorMessage.includes(\"timed out\")\n ) {\n return ConnectionErrorType.TIMEOUT_ERROR;\n }\n\n if (\n errorMessage.includes(\"network\") ||\n errorMessage.includes(\"connection refused\") ||\n errorMessage.includes(\"econnrefused\") ||\n errorMessage.includes(\"enotfound\")\n ) {\n return ConnectionErrorType.NETWORK_ERROR;\n }\n\n if (\n errorMessage.includes(\"auth\") ||\n errorMessage.includes(\"unauthorized\") ||\n errorMessage.includes(\"forbidden\") ||\n errorMessage.includes(\"401\") ||\n errorMessage.includes(\"403\")\n ) {\n return ConnectionErrorType.AUTHENTICATION_ERROR;\n }\n\n if (\n errorMessage.includes(\"server\") ||\n errorMessage.includes(\"500\") ||\n errorMessage.includes(\"502\") ||\n errorMessage.includes(\"503\") ||\n errorMessage.includes(\"504\")\n ) {\n return ConnectionErrorType.SERVER_ERROR;\n }\n\n return ConnectionErrorType.UNKNOWN_ERROR;\n }\n\n /**\n * 计算重连延迟\n */\n private calculateReconnectDelay(status: ConnectionStatus): number {\n const baseDelay = this.options.reconnectInterval;\n const maxDelay = this.options.maxReconnectDelay;\n const multiplier = this.options.reconnectBackoffMultiplier;\n const attempts = status.reconnectAttempts;\n\n let delay: number;\n\n switch (this.options.reconnectStrategy) {\n case ReconnectStrategy.FIXED_INTERVAL:\n delay = baseDelay;\n break;\n\n case ReconnectStrategy.LINEAR_BACKOFF:\n delay = baseDelay * (attempts + 1);\n break;\n\n case ReconnectStrategy.EXPONENTIAL_BACKOFF:\n delay = baseDelay * multiplier ** attempts;\n break;\n\n case ReconnectStrategy.ADAPTIVE:\n // 自适应策略:根据错误类型和历史成功率调整\n delay = this.calculateAdaptiveDelay(\n status,\n baseDelay,\n multiplier,\n attempts\n );\n break;\n\n default:\n delay = baseDelay * multiplier ** attempts;\n }\n\n // 限制最大延迟\n delay = Math.min(delay, maxDelay);\n\n // 添加抖动以避免雷群效应\n if (this.options.jitterEnabled) {\n const jitter = delay * 0.1 * Math.random(); // 10% 的随机抖动\n delay += jitter;\n }\n\n return Math.round(delay);\n }\n\n /**\n * 计算自适应重连延迟\n */\n private calculateAdaptiveDelay(\n status: ConnectionStatus,\n baseDelay: number,\n multiplier: number,\n attempts: number\n ): number {\n let delay = baseDelay;\n\n // 根据错误类型调整\n switch (status.errorType) {\n case ConnectionErrorType.NETWORK_ERROR:\n // 网络错误,使用指数退避\n delay = baseDelay * multiplier ** attempts;\n break;\n\n case ConnectionErrorType.AUTHENTICATION_ERROR:\n // 认证错误,延迟更长\n delay = baseDelay * multiplier ** attempts * 2;\n break;\n\n case ConnectionErrorType.SERVER_ERROR:\n // 服务器错误,适中延迟\n delay = baseDelay * (1 + attempts);\n break;\n\n case ConnectionErrorType.TIMEOUT_ERROR:\n // 超时错误,线性增长\n delay = baseDelay * (1 + attempts * 0.5);\n break;\n\n default:\n delay = baseDelay * multiplier ** attempts;\n }\n\n // 根据历史成功率调整\n if (status.reconnectHistory.length > 0) {\n const recentHistory = status.reconnectHistory.slice(-5); // 最近5次\n const successRate =\n recentHistory.filter((h) => h.success).length / recentHistory.length;\n\n if (successRate < 0.2) {\n // 成功率很低,增加延迟\n delay *= 1.5;\n } else if (successRate > 0.8) {\n // 成功率很高,减少延迟\n delay *= 0.8;\n }\n }\n\n return delay;\n }\n\n /**\n * 检查是否应该重连\n */\n private shouldReconnect(status: ConnectionStatus): boolean {\n // 检查重连次数限制\n if (status.reconnectAttempts >= this.options.maxReconnectAttempts) {\n this.logger.warn(\n `端点 ${status.endpoint} 已达到最大重连次数 ${this.options.maxReconnectAttempts}`\n );\n return false;\n }\n\n // 检查错误类型\n if (status.errorType === ConnectionErrorType.AUTHENTICATION_ERROR) {\n // 认证错误通常不应该重连太多次\n if (status.reconnectAttempts >= 3) {\n this.logger.warn(`端点 ${status.endpoint} 认证错误,停止重连`);\n return false;\n }\n }\n\n // 检查连续失败次数\n if (status.consecutiveFailures >= 10) {\n this.logger.warn(`端点 ${status.endpoint} 连续失败次数过多,停止重连`);\n return false;\n }\n\n return true;\n }\n\n /**\n * 安排重连\n */\n private scheduleReconnect(endpoint: string): void {\n const status = this.connectionStates.get(endpoint);\n if (!status) {\n return;\n }\n\n // 分类错误类型\n if (status.lastError) {\n status.errorType = this.classifyConnectionError(\n new Error(status.lastError)\n );\n }\n\n // 检查是否应该重连\n if (!this.shouldReconnect(status)) {\n status.isReconnecting = false;\n return;\n }\n\n // 清理现有的重连定时器\n const existingTimer = this.reconnectTimers.get(endpoint);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n // 计算重连延迟\n const delay = this.calculateReconnectDelay(status);\n status.reconnectDelay = delay;\n status.isReconnecting = true;\n status.nextReconnectTime = new Date(Date.now() + delay);\n\n this.logger.info(\n `安排重连 ${endpoint},延迟: ${delay}ms,尝试次数: ${status.reconnectAttempts + 1},错误类型: ${status.errorType}`\n );\n\n const timer = setTimeout(async () => {\n this.reconnectTimers.delete(endpoint);\n await this.performReconnect(endpoint);\n }, delay);\n\n this.reconnectTimers.set(endpoint, timer);\n }\n\n /**\n * 执行重连\n */\n private async performReconnect(endpoint: string): Promise<void> {\n const status = this.connectionStates.get(endpoint);\n const proxyServer = this.connections.get(endpoint);\n\n if (!status || !proxyServer) {\n return;\n }\n\n status.lastReconnectAttempt = new Date();\n status.isReconnecting = true;\n\n this.logger.info(\n `开始重连 ${endpoint},第 ${status.reconnectAttempts + 1} 次尝试`\n );\n\n try {\n await this.connectSingleEndpoint(endpoint, proxyServer);\n\n // 重连成功\n status.isReconnecting = false;\n status.reconnectHistory.push({\n timestamp: new Date(),\n success: true,\n delay: status.reconnectDelay,\n });\n\n this.logger.info(`重连成功 ${endpoint}`);\n } catch (error) {\n // 重连失败\n status.isReconnecting = false;\n status.reconnectHistory.push({\n timestamp: new Date(),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n delay: status.reconnectDelay,\n });\n\n this.logger.error(`重连失败 ${endpoint}:`, error);\n\n // 继续安排下次重连\n this.scheduleReconnect(endpoint);\n }\n\n // 限制重连历史记录数量\n if (status.reconnectHistory.length > 20) {\n status.reconnectHistory = status.reconnectHistory.slice(-20);\n }\n }\n\n /**\n * 清理所有重连定时器\n */\n private clearAllReconnectTimers(): void {\n for (const [, timer] of this.reconnectTimers) {\n clearTimeout(timer);\n }\n this.reconnectTimers.clear();\n }\n}\n","/**\n * 小智连接管理器单例\n * 提供全局唯一的 XiaozhiConnectionManager 实例,解决多实例资源冲突问题\n */\n\nimport {\n XiaozhiConnectionManager,\n type XiaozhiConnectionOptions,\n} from \"./XiaozhiConnectionManager.js\";\n\n// 重新导出相关类型,便于外部使用\nexport type { Tool } from \"@modelcontextprotocol/sdk/types.js\";\nexport type {\n XiaozhiConnectionOptions,\n ConnectionStatus,\n} from \"./XiaozhiConnectionManager.js\";\n\n// 单例状态枚举\nenum SingletonState {\n NOT_INITIALIZED = \"not_initialized\",\n INITIALIZING = \"initializing\",\n INITIALIZED = \"initialized\",\n FAILED = \"failed\",\n CLEANUP = \"cleanup\",\n}\n\n// 单例状态接口\ninterface SingletonStatus {\n state: SingletonState;\n initializationTime?: Date;\n lastError?: Error;\n instanceId?: string;\n}\n\n// 单例状态管理变量\nlet instance: XiaozhiConnectionManager | null = null;\nlet initPromise: Promise<XiaozhiConnectionManager> | null = null;\nlet state: SingletonState = SingletonState.NOT_INITIALIZED;\nlet lastError: Error | null = null;\nlet instanceId: string | null = null;\n\n/**\n * 创建 XiaozhiConnectionManager 实例(私有函数)\n */\nasync function createInstance(\n options?: XiaozhiConnectionOptions\n): Promise<XiaozhiConnectionManager> {\n console.log(\"🚀 正在初始化 XiaozhiConnectionManager 单例...\");\n\n const manager = new XiaozhiConnectionManager(options);\n\n return manager;\n}\n\n/**\n * 获取 XiaozhiConnectionManager 单例实例\n *\n * @param options 连接选项(仅在首次创建时生效)\n * @returns Promise<XiaozhiConnectionManager> 管理器实例\n * @throws Error 如果初始化失败\n */\nasync function getInstance(\n options?: XiaozhiConnectionOptions\n): Promise<XiaozhiConnectionManager> {\n // 如果已经初始化完成,直接返回实例\n if (instance && state === SingletonState.INITIALIZED) {\n return instance;\n }\n\n // 如果正在初始化中,等待同一个初始化Promise\n if (initPromise && state === SingletonState.INITIALIZING) {\n return initPromise;\n }\n\n // 如果之前初始化失败,重置状态准备重试\n if (state === SingletonState.FAILED) {\n reset();\n }\n\n // 开始新的初始化过程\n state = SingletonState.INITIALIZING;\n initPromise = createInstance(options);\n\n try {\n instance = await initPromise;\n state = SingletonState.INITIALIZED;\n instanceId = `xiaozhi-connection-manager-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\n lastError = null;\n\n console.log(\n `✅ XiaozhiConnectionManager 单例初始化成功,实例ID: ${instanceId}`\n );\n return instance;\n } catch (error) {\n state = SingletonState.FAILED;\n lastError = error as Error;\n initPromise = null;\n\n console.error(\n \"❌ XiaozhiConnectionManager 单例初始化失败:\",\n (error as Error).message\n );\n throw error;\n }\n}\n\n/**\n * 清理单例资源\n *\n * @returns Promise<void>\n */\nasync function cleanup(): Promise<void> {\n if (state === SingletonState.CLEANUP) {\n console.log(\"⚠️ XiaozhiConnectionManager 单例已在清理中,跳过重复清理\");\n return;\n }\n\n console.log(\"🧹 正在清理 XiaozhiConnectionManager 单例资源...\");\n state = SingletonState.CLEANUP;\n\n try {\n // 清理初始化Promise\n if (initPromise) {\n try {\n const instanceFromPromise = await initPromise;\n await instanceFromPromise.cleanup();\n } catch (error) {\n console.error(\"清理初始化中的实例失败:\", (error as Error).message);\n }\n initPromise = null;\n }\n\n // 清理已初始化的实例\n if (instance) {\n await instance.cleanup();\n instance = null;\n }\n\n state = SingletonState.NOT_INITIALIZED;\n lastError = null;\n instanceId = null;\n\n console.log(\"✅ XiaozhiConnectionManager 单例资源清理完成\");\n } catch (error) {\n console.error(\n \"❌ XiaozhiConnectionManager 单例清理失败:\",\n (error as Error).message\n );\n // 即使清理失败,也要重置状态,避免永久锁定\n reset();\n throw error;\n }\n}\n\n/**\n * 重置单例状态(不进行清理)\n *\n * 这个方法只重置内部状态变量,不调用实例的清理方法\n * 主要用于错误恢复和测试场景\n */\nfunction reset(): void {\n console.log(\"🔄 重置 XiaozhiConnectionManager 单例状态...\");\n\n // 清理定时器(如果有)\n if (initPromise) {\n initPromise = null;\n }\n\n // 重置状态变量\n instance = null;\n state = SingletonState.NOT_INITIALIZED;\n lastError = null;\n instanceId = null;\n\n console.log(\"✅ XiaozhiConnectionManager 单例状态已重置\");\n}\n\n/**\n * 检查单例是否已初始化\n *\n * @returns boolean 是否已初始化\n */\nfunction isInitialized(): boolean {\n return state === SingletonState.INITIALIZED && instance !== null;\n}\n\n/**\n * 获取单例状态信息\n *\n * @returns SingletonStatus 状态信息\n */\nfunction getStatus(): SingletonStatus {\n return {\n state,\n initializationTime: instanceId ? new Date() : undefined,\n lastError: lastError || undefined,\n instanceId: instanceId || undefined,\n };\n}\n\n/**\n * 强制重新初始化单例\n *\n * 这个方法会先清理现有资源,然后重新初始化\n *\n * @param options 连接选项\n * @returns Promise<XiaozhiConnectionManager> 新的管理器实例\n */\nasync function forceReinitialize(\n options?: XiaozhiConnectionOptions\n): Promise<XiaozhiConnectionManager> {\n console.log(\"🔄 强制重新初始化 XiaozhiConnectionManager 单例...\");\n\n await cleanup();\n return getInstance(options);\n}\n\n/**\n * 获取当前实例(同步方法,仅在确定已初始化时使用)\n *\n * @returns XiaozhiConnectionManager | null 当前实例或null\n */\nfunction getCurrentInstance(): XiaozhiConnectionManager | null {\n return instance;\n}\n\n/**\n * 等待初始化完成(如果正在初始化中)\n *\n * @returns Promise<boolean> 是否成功初始化\n */\nasync function waitForInitialization(): Promise<boolean> {\n if (state === SingletonState.INITIALIZED) {\n return true;\n }\n\n if (state === SingletonState.INITIALIZING && initPromise) {\n try {\n await initPromise;\n return true;\n } catch (error) {\n return false;\n }\n }\n\n return false;\n}\n\n/**\n * XiaozhiConnectionManager 全局单例管理器\n *\n * 使用对象包装模块级函数,保持原有API接口不变\n */\nexport const XiaozhiConnectionManagerSingleton = {\n getInstance,\n cleanup,\n reset,\n isInitialized,\n getStatus,\n forceReinitialize,\n getCurrentInstance,\n waitForInitialization,\n} as const;\n\n// 导出默认实例(便于使用)\nexport default XiaozhiConnectionManagerSingleton;\n\n// 进程退出时自动清理资源\nprocess.on(\"exit\", () => {\n if (XiaozhiConnectionManagerSingleton.isInitialized()) {\n console.log(\"🔄 进程退出,正在清理 XiaozhiConnectionManager 单例...\");\n // 注意:这里不能使用 await,因为 exit 事件是同步的\n XiaozhiConnectionManagerSingleton.reset();\n }\n});\n\n// 处理未捕获的异常\nprocess.on(\"uncaughtException\", async (error) => {\n console.error(\"💥 未捕获的异常,清理 XiaozhiConnectionManager 单例:\", error);\n try {\n await XiaozhiConnectionManagerSingleton.cleanup();\n } catch (cleanupError) {\n console.error(\"清理过程中发生错误:\", cleanupError);\n }\n});\n\n// 处理未处理的Promise拒绝\nprocess.on(\"unhandledRejection\", async (reason) => {\n console.error(\n \"💥 未处理的Promise拒绝,清理 XiaozhiConnectionManager 单例:\",\n reason\n );\n try {\n await XiaozhiConnectionManagerSingleton.cleanup();\n } catch (cleanupError) {\n console.error(\"清理过程中发生错误:\", cleanupError);\n }\n});\n","import { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { createServer } from \"node:http\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { serve } from \"@hono/node-server\";\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\nimport { WebSocketServer } from \"ws\";\nimport { ProxyMCPServer, type Tool } from \"./ProxyMCPServer.js\";\nimport { convertLegacyToNew } from \"./adapters/ConfigAdapter.js\";\nimport { getServiceStatus } from \"./cli.js\";\nimport { configManager } from \"./configManager.js\";\nimport type { AppConfig, MCPServerConfig } from \"./configManager.js\";\nimport { Logger } from \"./logger.js\";\n// MCPTransportType 已移除,不再需要导入\nimport type { MCPServiceManager } from \"./services/MCPServiceManager.js\";\nimport { MCPServiceManagerSingleton } from \"./services/MCPServiceManagerSingleton.js\";\nimport type { XiaozhiConnectionManager } from \"./services/XiaozhiConnectionManager.js\";\nimport { XiaozhiConnectionManagerSingleton } from \"./services/XiaozhiConnectionManagerSingleton.js\";\n\n// 硬编码常量已移除,改为配置驱动\ninterface ClientInfo {\n status: \"connected\" | \"disconnected\";\n mcpEndpoint: string;\n activeMCPServers: string[];\n lastHeartbeat?: number;\n}\n\nexport class WebServer {\n private app: Hono;\n private httpServer: any = null;\n private wss: WebSocketServer | null = null;\n private logger: Logger;\n private port: number;\n private clientInfo: ClientInfo = {\n status: \"disconnected\",\n mcpEndpoint: \"\",\n activeMCPServers: [],\n };\n private heartbeatTimeout?: NodeJS.Timeout;\n private readonly HEARTBEAT_TIMEOUT = 35000; // 35 seconds (slightly more than client's 30s interval)\n private proxyMCPServer: ProxyMCPServer | undefined; // 保留用于向后兼容\n private xiaozhiConnectionManager: XiaozhiConnectionManager | undefined;\n private mcpServiceManager: MCPServiceManager | undefined;\n\n constructor(port?: number) {\n // 端口配置\n this.port = port ?? configManager.getWebUIPort() ?? 9999;\n this.logger = new Logger();\n\n // 延迟初始化,在 start() 方法中进行连接管理\n // 移除硬编码的 MCP 服务和工具配置\n\n // 初始化 Hono 应用\n this.app = new Hono();\n this.setupMiddleware();\n this.setupRoutes();\n\n // HTTP 服务器和 WebSocket 服务器将在 start() 方法中初始化\n }\n\n /**\n * 初始化所有连接(配置驱动)\n */\n private async initializeConnections(): Promise<void> {\n try {\n this.logger.info(\"开始初始化连接...\");\n\n // 1. 读取配置\n const config = await this.loadConfiguration();\n\n // 2. 初始化 MCP 服务管理器\n this.mcpServiceManager = await MCPServiceManagerSingleton.getInstance();\n\n // 3. 从配置加载 MCP 服务\n await this.loadMCPServicesFromConfig(config.mcpServers);\n\n // 4. 获取工具列表\n const tools = this.mcpServiceManager.getAllTools();\n this.logger.info(`已加载 ${tools.length} 个工具`);\n\n // 5. 初始化小智接入点连接\n await this.initializeXiaozhiConnection(config.mcpEndpoint, tools);\n\n this.logger.info(\"所有连接初始化完成\");\n } catch (error) {\n this.logger.error(\"连接初始化失败:\", error);\n throw error;\n }\n }\n\n /**\n * 加载配置文件\n */\n private async loadConfiguration(): Promise<{\n mcpEndpoint: string | string[];\n mcpServers: Record<string, MCPServerConfig>;\n webUIPort: number;\n }> {\n if (!configManager.configExists()) {\n throw new Error(\"配置文件不存在,请先运行 'xiaozhi init' 初始化配置\");\n }\n\n const config = configManager.getConfig();\n\n return {\n mcpEndpoint: config.mcpEndpoint,\n mcpServers: config.mcpServers,\n webUIPort: config.webUI?.port ?? 9999,\n };\n }\n\n /**\n * 从配置加载 MCP 服务\n */\n private async loadMCPServicesFromConfig(\n mcpServers: Record<string, MCPServerConfig>\n ): Promise<void> {\n if (!this.mcpServiceManager) {\n throw new Error(\"MCPServiceManager 未初始化\");\n }\n\n for (const [name, config] of Object.entries(mcpServers)) {\n this.logger.info(`添加 MCP 服务配置: ${name}`);\n // 使用配置适配器转换配置格式\n const serviceConfig = convertLegacyToNew(name, config);\n this.mcpServiceManager.addServiceConfig(name, serviceConfig);\n }\n\n await this.mcpServiceManager.startAllServices();\n this.logger.info(\"所有 MCP 服务已启动\");\n }\n\n /**\n * 初始化小智接入点连接\n */\n private async initializeXiaozhiConnection(\n mcpEndpoint: string | string[],\n tools: Tool[]\n ): Promise<void> {\n // 处理多端点配置\n const endpoints = Array.isArray(mcpEndpoint) ? mcpEndpoint : [mcpEndpoint];\n const validEndpoints = endpoints.filter(\n (ep) => ep && !ep.includes(\"<请填写\")\n );\n\n if (validEndpoints.length === 0) {\n this.logger.warn(\"未配置有效的小智接入点,跳过连接\");\n return;\n }\n\n this.logger.info(\n `初始化小智接入点连接管理器,端点数量: ${validEndpoints.length}`\n );\n this.logger.debug(\"有效端点列表:\", validEndpoints);\n\n try {\n // 获取小智连接管理器单例\n this.xiaozhiConnectionManager =\n await XiaozhiConnectionManagerSingleton.getInstance({\n healthCheckInterval: 30000,\n reconnectInterval: 5000,\n maxReconnectAttempts: 10,\n loadBalanceStrategy: \"round-robin\",\n connectionTimeout: 10000,\n });\n\n // 设置 MCP 服务管理器\n if (this.mcpServiceManager) {\n this.xiaozhiConnectionManager.setServiceManager(this.mcpServiceManager);\n }\n\n // 初始化连接管理器\n await this.xiaozhiConnectionManager.initialize(validEndpoints, tools);\n\n // 连接所有端点\n await this.xiaozhiConnectionManager.connect();\n\n // 设置配置变更监听器\n this.xiaozhiConnectionManager.on(\"configChange\", (event: any) => {\n this.logger.info(`小智连接配置变更: ${event.type}`, event.data);\n });\n\n this.logger.info(\n `小智接入点连接管理器初始化完成,管理 ${validEndpoints.length} 个端点`\n );\n } catch (error) {\n this.logger.error(\"小智接入点连接管理器初始化失败:\", error);\n\n // 如果新的连接管理器失败,回退到原有的单连接模式(向后兼容)\n this.logger.warn(\"回退到单连接模式\");\n const validEndpoint = validEndpoints[0];\n\n this.logger.info(`初始化单个小智接入点连接: ${validEndpoint}`);\n this.proxyMCPServer = new ProxyMCPServer(validEndpoint);\n\n if (this.mcpServiceManager) {\n this.proxyMCPServer.setServiceManager(this.mcpServiceManager);\n }\n\n // 使用重连机制连接到小智接入点\n await this.connectWithRetry(\n () => this.proxyMCPServer!.connect(),\n \"小智接入点连接\"\n );\n this.logger.info(\"小智接入点连接成功(单连接模式)\");\n }\n }\n\n /**\n * 获取最佳的小智连接(用于向后兼容)\n */\n private getBestXiaozhiConnection(): ProxyMCPServer | null {\n if (this.xiaozhiConnectionManager) {\n return this.xiaozhiConnectionManager.selectBestConnection();\n }\n return this.proxyMCPServer || null;\n }\n\n /**\n * 获取小智连接状态信息\n */\n getXiaozhiConnectionStatus(): any {\n if (this.xiaozhiConnectionManager) {\n return {\n type: \"multi-endpoint\",\n manager: {\n healthyConnections:\n this.xiaozhiConnectionManager.getHealthyConnections().length,\n totalConnections:\n this.xiaozhiConnectionManager.getConnectionStatus().length,\n loadBalanceStats: this.xiaozhiConnectionManager.getLoadBalanceStats(),\n healthCheckStats: this.xiaozhiConnectionManager.getHealthCheckStats(),\n reconnectStats: this.xiaozhiConnectionManager.getReconnectStats(),\n },\n connections: this.xiaozhiConnectionManager.getConnectionStatus(),\n };\n }\n\n if (this.proxyMCPServer) {\n return {\n type: \"single-endpoint\",\n connected: true,\n endpoint: \"unknown\",\n };\n }\n\n return {\n type: \"none\",\n connected: false,\n };\n }\n\n /**\n * 带重试的连接方法\n */\n private async connectWithRetry<T>(\n connectionFn: () => Promise<T>,\n context: string,\n maxAttempts = 5,\n initialDelay = 1000,\n maxDelay = 30000,\n backoffMultiplier = 2\n ): Promise<T> {\n let lastError: Error | null = null;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n this.logger.info(`${context} - 尝试连接 (${attempt}/${maxAttempts})`);\n return await connectionFn();\n } catch (error) {\n lastError = error as Error;\n this.logger.warn(`${context} - 连接失败:`, error);\n\n if (attempt < maxAttempts) {\n const delay = Math.min(\n initialDelay * backoffMultiplier ** (attempt - 1),\n maxDelay\n );\n this.logger.info(`${context} - ${delay}ms 后重试...`);\n await this.sleep(delay);\n }\n }\n }\n\n throw new Error(\n `${context} - 连接失败,已达到最大重试次数: ${lastError?.message}`\n );\n }\n\n /**\n * 延迟工具方法\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n private setupMiddleware() {\n // CORS 中间件\n this.app?.use(\n \"*\",\n cors({\n origin: \"*\",\n allowMethods: [\"GET\", \"POST\", \"PUT\", \"OPTIONS\"],\n allowHeaders: [\"Content-Type\"],\n })\n );\n\n // 错误处理中间件\n this.app?.onError((err, c) => {\n this.logger.error(\"HTTP request error:\", err);\n return c.json({ error: \"Internal Server Error\" }, 500);\n });\n }\n\n private setupRoutes() {\n // API 路由\n this.app?.get(\"/api/config\", async (c) => {\n const config = configManager.getConfig();\n return c.json(config);\n });\n\n this.app?.put(\"/api/config\", async (c) => {\n try {\n const newConfig: AppConfig = await c.req.json();\n this.updateConfig(newConfig);\n\n // 广播配置更新\n this.broadcastConfigUpdate(newConfig);\n\n // 直接返回成功,不再自动重启\n this.logger.info(\"配置已更新\");\n return c.json({ success: true });\n } catch (error) {\n return c.json(\n {\n error: error instanceof Error ? error.message : String(error),\n },\n 400\n );\n }\n });\n\n this.app?.get(\"/api/status\", async (c) => {\n const mcpStatus = this.proxyMCPServer?.getStatus();\n return c.json({\n ...this.clientInfo,\n mcpConnection: mcpStatus,\n });\n });\n\n // 处理未知的 API 路由\n this.app?.all(\"/api/*\", async (c) => {\n return c.text(\"Not Found\", 404);\n });\n\n // 静态文件服务 - 放在最后作为回退\n this.app.get(\"*\", async (c) => {\n return this.serveStaticFile(c);\n });\n }\n\n private async serveStaticFile(c: any) {\n const pathname = new URL(c.req.url).pathname;\n try {\n // 获取当前文件所在目录\n const __dirname = dirname(fileURLToPath(import.meta.url));\n\n // 确定web目录路径\n const possibleWebPaths = [\n join(__dirname, \"..\", \"web\", \"dist\"), // 构建后的目录\n join(__dirname, \"..\", \"web\"), // 开发目录\n join(process.cwd(), \"web\", \"dist\"), // 当前工作目录\n join(process.cwd(), \"web\"),\n ];\n\n const webPath = possibleWebPaths.find((p) => existsSync(p));\n\n if (!webPath) {\n // 如果找不到 web 目录,返回简单的 HTML 页面\n const errorHtml = `\n <!DOCTYPE html>\n <html>\n <head>\n <title>小智配置管理</title>\n <meta charset=\"utf-8\">\n <style>\n body { font-family: sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }\n .error { color: #e53e3e; background: #fed7d7; padding: 20px; border-radius: 8px; }\n </style>\n </head>\n <body>\n <h1>小智配置管理</h1>\n <div class=\"error\">\n <p>错误:找不到前端资源文件。</p>\n <p>请先构建前端项目:</p>\n <pre>cd web && pnpm install && pnpm build</pre>\n </div>\n </body>\n </html>\n `;\n return c.html(errorHtml);\n }\n\n // 处理路径\n let filePath = pathname;\n if (filePath === \"/\") {\n filePath = \"/index.html\";\n }\n\n // 安全性检查:防止路径遍历\n if (filePath.includes(\"..\")) {\n return c.text(\"Forbidden\", 403);\n }\n\n const fullPath = join(webPath, filePath);\n\n // 检查文件是否存在\n if (!existsSync(fullPath)) {\n // 对于 SPA,返回 index.html\n const indexPath = join(webPath, \"index.html\");\n if (existsSync(indexPath)) {\n const content = await readFile(indexPath);\n return c.html(content.toString());\n }\n return c.text(\"Not Found\", 404);\n }\n\n // 读取文件\n const content = await readFile(fullPath);\n\n // 设置正确的 Content-Type\n const ext = fullPath.split(\".\").pop()?.toLowerCase();\n const contentTypes: Record<string, string> = {\n html: \"text/html\",\n js: \"application/javascript\",\n css: \"text/css\",\n json: \"application/json\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n svg: \"image/svg+xml\",\n ico: \"image/x-icon\",\n };\n\n const contentType = contentTypes[ext || \"\"] || \"application/octet-stream\";\n\n // 对于文本文件,返回字符串;对于二进制文件,返回 ArrayBuffer\n if (\n contentType.startsWith(\"text/\") ||\n contentType.includes(\"javascript\") ||\n contentType.includes(\"json\")\n ) {\n return c.text(content.toString(), 200, { \"Content-Type\": contentType });\n }\n return c.body(content, 200, { \"Content-Type\": contentType });\n } catch (error) {\n this.logger.error(\"Serve static file error:\", error);\n return c.text(\"Internal Server Error\", 500);\n }\n }\n\n private setupWebSocket() {\n if (!this.wss) return;\n\n this.wss.on(\"connection\", (ws) => {\n // 只在调试模式下输出连接日志\n this.logger.debug(\"WebSocket client connected\");\n\n ws.on(\"message\", async (message) => {\n try {\n const data = JSON.parse(message.toString());\n await this.handleWebSocketMessage(ws, data);\n } catch (error) {\n this.logger.error(\"WebSocket message error:\", error);\n ws.send(\n JSON.stringify({\n type: \"error\",\n error: error instanceof Error ? error.message : String(error),\n })\n );\n }\n });\n\n ws.on(\"close\", () => {\n // 只在调试模式下输出断开日志\n this.logger.debug(\"WebSocket client disconnected\");\n });\n\n this.sendInitialData(ws);\n });\n }\n\n private async handleWebSocketMessage(ws: any, data: any) {\n switch (data.type) {\n case \"getConfig\": {\n const config = configManager.getConfig();\n this.logger.debug(\"getConfig ws getConfig\", config);\n ws.send(JSON.stringify({ type: \"config\", data: config }));\n break;\n }\n\n case \"updateConfig\":\n this.updateConfig(data.config);\n this.broadcastConfigUpdate(data.config);\n break;\n\n case \"getStatus\":\n ws.send(JSON.stringify({ type: \"status\", data: this.clientInfo }));\n break;\n\n case \"clientStatus\": {\n this.updateClientInfo(data.data);\n this.broadcastStatusUpdate();\n // 每次客户端状态更新时,也发送最新的配置\n const latestConfig = configManager.getConfig();\n ws.send(JSON.stringify({ type: \"configUpdate\", data: latestConfig }));\n break;\n }\n\n case \"restartService\":\n // 处理手动重启请求\n this.logger.info(\"收到手动重启服务请求\");\n this.broadcastRestartStatus(\"restarting\");\n\n // 延迟执行重启\n setTimeout(async () => {\n try {\n await this.restartService();\n // 服务重启需要一些时间,延迟发送成功状态\n setTimeout(() => {\n this.broadcastRestartStatus(\"completed\");\n }, 5000);\n } catch (error) {\n this.logger.error(\n `手动重启失败: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n this.broadcastRestartStatus(\n \"failed\",\n error instanceof Error ? error.message : \"未知错误\"\n );\n }\n }, 500);\n break;\n }\n }\n\n private async sendInitialData(ws: any) {\n const config = configManager.getConfig();\n ws.send(JSON.stringify({ type: \"config\", data: config }));\n ws.send(JSON.stringify({ type: \"status\", data: this.clientInfo }));\n\n // 延迟发送配置更新,确保 MCP Server Proxy 有足够时间完成工具列表更新\n setTimeout(() => {\n const updatedConfig = configManager.getConfig();\n ws.send(JSON.stringify({ type: \"configUpdate\", data: updatedConfig }));\n }, 2000); // 2秒延迟\n }\n\n public broadcastConfigUpdate(config: AppConfig) {\n if (!this.wss) return;\n\n const message = JSON.stringify({ type: \"configUpdate\", data: config });\n for (const client of this.wss.clients) {\n if (client.readyState === 1) {\n client.send(message);\n }\n }\n }\n\n private broadcastRestartStatus(\n status: \"restarting\" | \"completed\" | \"failed\",\n error?: string\n ) {\n if (!this.wss) return;\n\n const message = JSON.stringify({\n type: \"restartStatus\",\n data: {\n status,\n error,\n timestamp: Date.now(),\n },\n });\n for (const client of this.wss.clients) {\n if (client.readyState === 1) {\n client.send(message);\n }\n }\n }\n\n private broadcastStatusUpdate() {\n if (!this.wss) return;\n\n const message = JSON.stringify({\n type: \"statusUpdate\",\n data: this.clientInfo,\n });\n for (const client of this.wss.clients) {\n if (client.readyState === 1) {\n client.send(message);\n }\n }\n }\n\n private updateClientInfo(info: Partial<ClientInfo>) {\n this.clientInfo = { ...this.clientInfo, ...info };\n if (info.lastHeartbeat) {\n this.clientInfo.lastHeartbeat = Date.now();\n }\n\n // Reset heartbeat timeout when receiving client status\n if (info.status === \"connected\") {\n this.resetHeartbeatTimeout();\n }\n }\n\n private resetHeartbeatTimeout() {\n // Clear existing timeout\n if (this.heartbeatTimeout) {\n clearTimeout(this.heartbeatTimeout);\n }\n\n // Set new timeout\n this.heartbeatTimeout = setTimeout(() => {\n this.logger.warn(\"客户端心跳超时,标记为断开连接\");\n this.updateClientInfo({ status: \"disconnected\" });\n this.broadcastStatusUpdate();\n }, this.HEARTBEAT_TIMEOUT);\n }\n\n private updateConfig(newConfig: AppConfig) {\n // 更新 MCP 端点\n if (newConfig.mcpEndpoint !== configManager.getMcpEndpoint()) {\n configManager.updateMcpEndpoint(newConfig.mcpEndpoint);\n }\n\n // 更新 MCP 服务\n const currentServers = configManager.getMcpServers();\n for (const [name, config] of Object.entries(newConfig.mcpServers)) {\n if (JSON.stringify(currentServers[name]) !== JSON.stringify(config)) {\n configManager.updateMcpServer(name, config);\n }\n }\n\n // 删除不存在的服务\n for (const name of Object.keys(currentServers)) {\n if (!(name in newConfig.mcpServers)) {\n configManager.removeMcpServer(name);\n\n // 同时清理该服务在 mcpServerConfig 中的工具配置\n configManager.removeServerToolsConfig(name);\n }\n }\n\n // 更新连接配置\n if (newConfig.connection) {\n configManager.updateConnectionConfig(newConfig.connection);\n }\n\n // 更新 ModelScope 配置\n if (newConfig.modelscope) {\n configManager.updateModelScopeConfig(newConfig.modelscope);\n }\n\n // 更新 Web UI 配置\n if (newConfig.webUI) {\n configManager.updateWebUIConfig(newConfig.webUI);\n }\n\n // 更新服务工具配置\n if (newConfig.mcpServerConfig) {\n for (const [serverName, toolsConfig] of Object.entries(\n newConfig.mcpServerConfig\n )) {\n for (const [toolName, toolConfig] of Object.entries(\n toolsConfig.tools\n )) {\n configManager.setToolEnabled(serverName, toolName, toolConfig.enable);\n // 注释:configManager 不支持直接设置工具描述,描述作为工具配置的一部分保存\n }\n }\n }\n }\n\n private async restartService(): Promise<void> {\n this.logger.info(\"正在重启 MCP 服务...\");\n\n // 清除心跳超时定时器,避免重启过程中误报断开连接\n if (this.heartbeatTimeout) {\n clearTimeout(this.heartbeatTimeout);\n this.heartbeatTimeout = undefined;\n }\n\n try {\n // 获取当前服务状态\n const status = getServiceStatus();\n if (!status.running) {\n this.logger.warn(\"MCP 服务未运行,尝试启动服务\");\n\n // 如果服务未运行,尝试启动服务\n const startArgs = [\"start\", \"--daemon\"];\n const child = spawn(\"xiaozhi\", startArgs, {\n detached: true,\n stdio: \"ignore\",\n env: {\n ...process.env,\n XIAOZHI_CONFIG_DIR: process.env.XIAOZHI_CONFIG_DIR || process.cwd(),\n },\n });\n child.unref();\n this.logger.info(\"MCP 服务启动命令已发送\");\n return;\n }\n\n // 获取服务运行模式\n const isDaemon = status.mode === \"daemon\";\n\n // 执行重启命令\n const restartArgs = [\"restart\"];\n if (isDaemon) {\n restartArgs.push(\"--daemon\");\n }\n\n // 在子进程中执行重启命令\n const child = spawn(\"xiaozhi\", restartArgs, {\n detached: true,\n stdio: \"ignore\",\n env: {\n ...process.env,\n XIAOZHI_CONFIG_DIR: process.env.XIAOZHI_CONFIG_DIR || process.cwd(),\n },\n });\n\n child.unref();\n\n this.logger.info(\"MCP 服务重启命令已发送\");\n\n // 重启后重新设置心跳超时\n this.resetHeartbeatTimeout();\n } catch (error) {\n this.logger.error(\n `重启服务失败: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n // 失败时也要重新设置心跳超时\n this.resetHeartbeatTimeout();\n throw error;\n }\n }\n\n public updateStatus(info: Partial<ClientInfo>) {\n this.updateClientInfo(info);\n this.broadcastStatusUpdate();\n }\n\n public async start(): Promise<void> {\n // 1. 启动 HTTP 服务器\n const server = serve({\n fetch: this.app.fetch,\n port: this.port,\n hostname: \"0.0.0.0\", // 绑定到所有网络接口,支持 Docker 部署\n createServer,\n });\n\n // 保存服务器实例\n this.httpServer = server;\n\n // 设置 WebSocket 服务器\n this.wss = new WebSocketServer({ server: this.httpServer });\n this.setupWebSocket();\n\n this.logger.info(`Web server listening on http://0.0.0.0:${this.port}`);\n this.logger.info(`Local access: http://localhost:${this.port}`);\n\n // 2. 初始化所有连接(配置驱动)\n try {\n await this.initializeConnections();\n this.logger.info(\"所有连接初始化完成\");\n } catch (error) {\n this.logger.error(\"连接初始化失败,但 Web 服务器继续运行:\", error);\n // 连接失败不影响 Web 服务器启动,用户可以通过界面查看错误信息\n }\n }\n\n public stop(): Promise<void> {\n return new Promise((resolve) => {\n let resolved = false;\n\n const doResolve = () => {\n if (!resolved) {\n resolved = true;\n resolve();\n }\n };\n\n // 停止 MCP 客户端\n this.proxyMCPServer?.disconnect();\n\n // Clear heartbeat timeout\n if (this.heartbeatTimeout) {\n clearTimeout(this.heartbeatTimeout);\n this.heartbeatTimeout = undefined;\n }\n\n // 强制断开所有 WebSocket 客户端连接\n if (this.wss) {\n for (const client of this.wss.clients) {\n client.terminate();\n }\n\n // 关闭 WebSocket 服务器\n this.wss.close(() => {\n // 强制关闭 HTTP 服务器,不等待现有连接\n if (this.httpServer) {\n this.httpServer.close(() => {\n this.logger.info(\"Web server stopped\");\n doResolve();\n });\n } else {\n this.logger.info(\"Web server stopped\");\n doResolve();\n }\n\n // 设置超时,如果 2 秒内没有关闭则强制退出\n setTimeout(() => {\n this.logger.info(\"Web server force stopped\");\n doResolve();\n }, 2000);\n });\n } else {\n this.logger.info(\"Web server stopped\");\n doResolve();\n }\n });\n }\n}\n","#!/usr/bin/env node\n\n/**\n * WebServer 独立启动脚本\n * 用于后台模式启动,替代原有的 adaptiveMCPPipe 启动方式\n */\n\n// 动态导入避免 CLI 代码执行\nasync function importModules() {\n const webServerModule = await import(\"./WebServer.js\");\n const configModule = await import(\"./configManager.js\");\n const loggerModule = await import(\"./logger.js\");\n return {\n WebServer: webServerModule.WebServer,\n configManager: configModule.configManager,\n Logger: loggerModule.Logger,\n };\n}\n\nimport { spawn } from \"node:child_process\";\n\nasync function main() {\n const args = process.argv.slice(2);\n const openBrowser = args.includes(\"--open-browser\");\n\n try {\n // 动态导入模块\n const { WebServer, configManager, Logger } = await importModules();\n\n const logger = new Logger().withTag(\"WEBSERVER_STANDALONE\");\n\n // 初始化日志\n if (process.env.XIAOZHI_CONFIG_DIR) {\n logger.initLogFile(process.env.XIAOZHI_CONFIG_DIR);\n logger.enableFileLogging(true);\n }\n\n // 启动 WebServer\n const webServer = new WebServer();\n await webServer.start();\n\n logger.info(\"WebServer 启动成功\");\n\n // 自动打开浏览器\n if (openBrowser) {\n const port = configManager.getWebUIPort();\n const url = `http://localhost:${port}`;\n await openBrowserUrl(url);\n }\n\n // 处理退出信号\n const cleanup = async () => {\n logger.info(\"正在停止 WebServer...\");\n await webServer.stop();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n } catch (error) {\n console.error(\"WebServer 启动失败:\", error);\n process.exit(1);\n }\n}\n\n/**\n * 打开浏览器URL\n */\nasync function openBrowserUrl(url: string): Promise<void> {\n try {\n const platform = process.platform;\n\n let command: string;\n let args: string[];\n\n if (platform === \"darwin\") {\n command = \"open\";\n args = [url];\n } else if (platform === \"win32\") {\n command = \"start\";\n args = [\"\", url];\n } else {\n command = \"xdg-open\";\n args = [url];\n }\n\n spawn(command, args, { detached: true, stdio: \"ignore\" });\n console.log(`已尝试打开浏览器: ${url}`);\n } catch (error) {\n console.warn(\"自动打开浏览器失败:\", error);\n }\n}\n\n// 检查是否为直接执行\nif (import.meta.url === `file://${process.argv[1]}`) {\n main();\n}\n"],"mappings":"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","names":["logger_exports","__export","Logger","logger","fs","path","chalk","createConsola","formatDateTime","date","year","month","day","hours","minutes","seconds","init_logger","__esmMin","__name","isDaemonMode","logObj","levelMap","colorMap","text","level","colorFn","timestamp","coloredLevel","message","error","projectDir","args","formattedMessage","fullMessage","arg","enable","tag","WebSocket","ProxyMCPServer","init_ProxyMCPServer","__esmMin","init_logger","__name","endpointUrl","options","Logger","serviceManager","allTools","newTools","toolInfo","error","name","tool","tools","resolve","reject","data","message","code","reason","interval","jitterRange","jitter","request","toolsList","id","result","response","SSEClientTransport","StdioClientTransport","StreamableHTTPClientTransport","EventSource","getLogger","Logger","createTransport","config","createStdioTransport","createSSETransport","createModelScopeSSETransport","createStreamableHTTPTransport","url","options","createSSEOptions","createModelScopeSSEOptions","createStreamableHTTPOptions","token","__name","init","headers","validateConfig","getSupportedTypes","TransportFactory","init_TransportFactory","__esmMin","init_logger","init_MCPService","Client","MCPTransportType","MCPService","init_MCPService","__esmMin","init_logger","init_TransportFactory","__name","config","options","Logger","TransportFactory","resolve","reject","error","interval","jitterRange","jitter","tools","tool","t","name","arguments_","result","startTime","pingPromise","timeoutPromise","_","duration","connectionError","wasEnabled","convertLegacyToNew","serviceName","legacyConfig","logger","ConfigValidationError","newConfig","convertByConfigType","validateNewConfig","error","isLocalConfig","convertLocalConfig","isSSEConfig","convertSSEConfig","isStreamableHTTPConfig","convertStreamableHTTPConfig","config","isModelScope","isModelScopeURL","baseConfig","url","MCPTransportType","init_ConfigAdapter","__esmMin","init_logger","init_MCPService","message","configName","__name","getMcpServerCommunicationType","serverConfig","validateMcpServerConfig","serverName","error","init_mcpServerUtils","__esmMin","__name","configManager_exports","__export","ConfigManager","configManager","copyFileSync","existsSync","readFileSync","writeFileSync","dirname","resolve","fileURLToPath","commentJson","dayjs","JSON5","json5Writer","__dirname","DEFAULT_CONNECTION_CONFIG","init_configManager","__esmMin","init_logger","init_mcpServerUtils","_ConfigManager","__name","configDir","configFileNames","fileName","filePath","format","targetFileName","configPath","configFileFormat","configData","config","error","configObj","endpoint","serverName","serverConfig","validation","validateMcpServerConfig","toolName","ep","currentEndpoints","newEndpoints","newMcpServers","newConfig","toolsConfig","enabled","description","configContent","json5WriterError","commentJsonError","connectionConfig","callTime","toolConfig","currentUsageCount","currentLastUsedTime","logger","interval","timeout","modelScopeConfig","apiKey","webServer","webUIConfig","port","chalk","Table","ora","getDisplayWidth","str","width","char","truncateToWidth","maxWidth","result","currentWidth","hasAddedChar","charWidth","listMcpServers","options","spinner","mcpServers","configManager","serverNames","maxToolNameWidth","allToolNames","serverName","toolsConfig","toolNames","toolName","table","toolConfig","status","description","serverConfig","toolCount","enabledCount","t","error","listServerTools","setToolEnabled","enabled","action","init_mcpCommands","__esmMin","init_configManager","__name","MCPServiceManager","MCPServiceManager_default","init_MCPServiceManager","__esmMin","init_logger","init_MCPService","init_configManager","__name","configs","Logger","configEntries","serviceName","config","service","MCPService","tools","t","error","tool","toolKey","allTools","toolInfo","toolName","arguments_","result","status","serviceStatus","name","enhancedConfig","modelScopeApiKey","configManager","nameOrConfig","finalConfig","mcpServer_exports","__export","MCPServer","spawn","randomUUID","EventEmitter","fs","os","path","fileURLToPath","express","__filename","__dirname","logger","MCP_SERVER_PROXY_FILENAME","MAX_SEARCH_LEVELS","init_mcpServer","__esmMin","init_ProxyMCPServer","init_configManager","init_logger","init_MCPServiceManager","__name","port","req","res","next","clientId","sessionId","message","response","client","error","resolve","reject","timeoutId","data","lines","line","pendingRequest","results","mcpProxyResult","httpServerResult","normalizedPath","resolvedPath","allowedBaseDirectories","dir","currentScript","searchDir","mcpProxyPath","i","testPath","distPath","projectRoot","rootDistPath","code","signal","timeout","dataHandler","MCPServiceManager","mcpEndpoint","configManager","ep","ProxyMCPServer","spawn","fs","path","fileURLToPath","chalk","Command","ora","getVersion","__filename","currentDir","possiblePaths","packagePath","packageJson","error","getServiceStatus","pidFile","getPidFile","pidContent","pidStr","startTime","mode","pid","start","uptime","formatUptime","ms","seconds","minutes","hours","days","savePidInfo","pidInfo","cleanupPidFile","checkEnvironment","configManager","validEndpoints","endpoint","startService","daemon","ui","spinner","status","startWebServerInDaemon","startWebServerInForeground","openBrowser","scriptDir","command","args","child","projectDir","logger","logFilePath","logStream","code","signal","webServer","WebServer","cleanup","__name","url","openBrowserUrl","platform","stopService","attempts","maxAttempts","resolve","checkStatus","attachService","tail","restartService","startMCPServerMode","port","MCPServer","scriptPath","distDir","server","showDetailedInfo","version","initConfig","format","configDir","configFileName","configPath","getAvailableTemplates","templatesDir","p","item","itemPath","calculateSimilarity","str1","str2","len1","len2","matrix","i","j","maxLen","findSimilarTemplate","input","templates","bestMatch","bestSimilarity","template","similarity","askUserConfirmation","question","readline","rl","handleInput","char","createBasicConfig","projectPath","configContent","createProject","projectName","options","targetPath","availableTemplates","similarTemplate","templatePath","copyDirectory","src","dest","excludePatterns","items","pattern","srcPath","destPath","startUIService","browserProcess","isExiting","configCommand","key","value","heartbeatInterval","heartbeatTimeout","reconnectInterval","config","endpoints","ep","index","name","serverConfig","connectionConfig","showHelp","program","SERVICE_NAME","mcpCommand","endpointCommand","init_cli","__esmMin","init_WebServer","init_configManager","init_logger","init_mcpCommands","mcpProxyPath","listMcpServers","serverName","listServerTools","toolName","action","setToolEnabled","urls","createInstance","MCPServiceManager_default","getInstance","instance","state","initPromise","reset","instanceId","lastError","error","cleanup","isInitialized","getStatus","forceReinitialize","getCurrentInstance","waitForInitialization","MCPServiceManagerSingleton","init_MCPServiceManagerSingleton","__esmMin","init_MCPServiceManager","__name","cleanupError","reason","EventEmitter","ReconnectStrategy","DEFAULT_OPTIONS","XiaozhiConnectionManager","init_XiaozhiConnectionManager","__esmMin","init_ProxyMCPServer","init_logger","__name","options","Logger","endpoints","tools","endpoint","error","connectionPromises","proxyServer","results","successCount","result","failureCount","connectionTime","disconnectPromises","timer","healthyConnections","status","manager","enabled","stats","successRate","existingTimer","valid","invalid","errors","validStrategies","newEndpoints","validEndpoints","invalidEndpoints","currentEndpoints","toAdd","ep","toRemove","toKeep","changeEvent","newOptions","oldOptions","config","excludeEndpoints","availableConnections","connection","selectedConnection","connections","randomIndex","connectionHealths","a","b","totalWeight","sum","item","randomWeight","conn","connectionWeights","weight","strategy","oldStrategy","failedEndpoint","backupConnection","backupEndpoint","targetEndpoints","prewarmPromises","currentMemory","memoryGrowth","growthPercentage","ProxyMCPServer","healthCheckPromises","startTime","responseTime","resolve","success","baseScore","increment","decrement","errorMessage","baseDelay","maxDelay","multiplier","attempts","delay","jitter","recentHistory","h","createInstance","options","XiaozhiConnectionManager","getInstance","instance","state","initPromise","reset","instanceId","lastError","error","cleanup","isInitialized","getStatus","forceReinitialize","getCurrentInstance","waitForInitialization","XiaozhiConnectionManagerSingleton","init_XiaozhiConnectionManagerSingleton","__esmMin","init_XiaozhiConnectionManager","__name","cleanupError","reason","WebServer_exports","__export","WebServer","spawn","existsSync","readFile","createServer","dirname","join","fileURLToPath","serve","Hono","cors","WebSocketServer","init_WebServer","__esmMin","init_ProxyMCPServer","init_ConfigAdapter","init_cli","init_configManager","init_logger","init_MCPServiceManagerSingleton","init_XiaozhiConnectionManagerSingleton","__name","port","configManager","Logger","config","MCPServiceManagerSingleton","tools","error","mcpServers","name","serviceConfig","convertLegacyToNew","mcpEndpoint","validEndpoints","ep","XiaozhiConnectionManagerSingleton","event","validEndpoint","ProxyMCPServer","connectionFn","context","maxAttempts","initialDelay","maxDelay","backoffMultiplier","lastError","attempt","delay","ms","resolve","err","c","newConfig","mcpStatus","pathname","__dirname","webPath","p","filePath","fullPath","indexPath","content","ext","contentType","ws","message","data","latestConfig","updatedConfig","client","status","info","currentServers","serverName","toolsConfig","toolName","toolConfig","getServiceStatus","isDaemon","restartArgs","server","resolved","doResolve","spawn","importModules","webServerModule","configModule","loggerModule","__name","main","openBrowser","WebServer","configManager","Logger","logger","webServer","url","openBrowserUrl","cleanup","error","platform","command","args","spawn"]}