xiaozhi-client 1.5.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adaptiveMCPPipe.js +7 -7
- package/dist/adaptiveMCPPipe.js.map +1 -1
- package/dist/autoCompletion.js +1 -1
- package/dist/autoCompletion.js.map +1 -1
- package/dist/cli.js +5 -5
- package/dist/cli.js.map +1 -1
- package/dist/configManager.d.ts +4 -0
- package/dist/configManager.js +1 -1
- package/dist/configManager.js.map +1 -1
- package/dist/mcpCommands.js +1 -1
- package/dist/mcpCommands.js.map +1 -1
- package/dist/mcpServerProxy.d.ts +5 -0
- package/dist/mcpServerProxy.js +4 -4
- package/dist/mcpServerProxy.js.map +1 -1
- package/dist/modelScopeMCPClient.js +2 -2
- package/dist/modelScopeMCPClient.js.map +1 -1
- package/dist/multiEndpointMCPPipe.js +2 -2
- package/dist/multiEndpointMCPPipe.js.map +1 -1
- package/dist/package.json +7 -2
- package/dist/streamableHttpMCPClient.js +2 -2
- package/dist/streamableHttpMCPClient.js.map +1 -1
- package/dist/webServer.js +4 -4
- package/dist/webServer.js.map +1 -1
- package/docs/Architecture.md +68 -0
- package/package.json +7 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/configManager.ts","../src/logger.ts","../src/streamableHttpMCPClient.ts"],"sourcesContent":["import { copyFileSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\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}\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\n private constructor() {\n this.defaultConfigPath = resolve(__dirname, \"xiaozhi.config.default.json\");\n }\n\n /**\n * 获取配置文件路径(动态计算)\n */\n private getConfigFilePath(): string {\n // 配置文件路径 - 优先使用环境变量指定的目录,否则使用当前工作目录\n const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n return resolve(configDir, \"xiaozhi.config.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 const configPath = this.getConfigFilePath();\n return existsSync(configPath);\n }\n\n /**\n * 初始化配置文件\n * 从 config.default.json 复制到 config.json\n */\n public initConfig(): void {\n if (!existsSync(this.defaultConfigPath)) {\n throw new Error(\"默认配置文件 xiaozhi.config.default.json 不存在\");\n }\n\n if (this.configExists()) {\n throw new Error(\"配置文件 xiaozhi.config.json 已存在,无需重复初始化\");\n }\n\n const configPath = this.getConfigFilePath();\n copyFileSync(this.defaultConfigPath, configPath);\n this.config = null; // 重置缓存\n }\n\n /**\n * 加载配置文件\n */\n private loadConfig(): AppConfig {\n if (!this.configExists()) {\n throw new Error(\n \"配置文件 xiaozhi.config.json 不存在,请先运行 xiaozhi init 初始化配置\"\n );\n }\n\n try {\n const configPath = this.getConfigFilePath();\n const configData = readFileSync(configPath, \"utf8\");\n const config = JSON.parse(configData) as AppConfig;\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 const sc = serverConfig as Record<string, unknown>;\n\n // 检查服务类型\n if (sc.url && typeof sc.url === \"string\") {\n // URL 类型的服务(SSE 或 Streamable HTTP)\n // type 字段是可选的,可以是 \"sse\" 或 \"streamable-http\"\n if (sc.type && sc.type !== \"sse\" && sc.type !== \"streamable-http\") {\n throw new Error(\n `配置文件格式错误:mcpServers.${serverName}.type 必须是 \"sse\" 或 \"streamable-http\"`\n );\n }\n } else {\n // 本地类型的验证\n if (!sc.command || typeof sc.command !== \"string\") {\n throw new Error(\n `配置文件格式错误:mcpServers.${serverName}.command 无效`\n );\n }\n\n if (!Array.isArray(sc.args)) {\n throw new Error(\n `配置文件格式错误:mcpServers.${serverName}.args 必须是数组`\n );\n }\n\n if (sc.env && typeof sc.env !== \"object\") {\n throw new Error(\n `配置文件格式错误:mcpServers.${serverName}.env 必须是对象`\n );\n }\n }\n }\n }\n\n /**\n * 获取配置(只读)\n */\n public getConfig(): Readonly<AppConfig> {\n if (!this.config) {\n this.config = this.loadConfig();\n }\n\n // 返回深度只读副本\n return JSON.parse(JSON.stringify(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.getConfig();\n const newConfig = { ...config, mcpEndpoint: endpoint };\n this.saveConfig(newConfig);\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.getConfig();\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 const newConfig = { ...config, mcpEndpoint: newEndpoints };\n this.saveConfig(newConfig);\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.getConfig();\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 const newConfig = { ...config, mcpEndpoint: newEndpoints };\n this.saveConfig(newConfig);\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 if (\"type\" in serverConfig && serverConfig.type === \"sse\") {\n // SSE 类型的验证\n if (!serverConfig.url || typeof serverConfig.url !== \"string\") {\n throw new Error(\"SSE 服务配置的 url 字段必须是非空字符串\");\n }\n } else {\n // 本地类型的验证\n const localConfig = serverConfig as LocalMCPServerConfig;\n if (!localConfig.command || typeof localConfig.command !== \"string\") {\n throw new Error(\"服务配置的 command 字段必须是非空字符串\");\n }\n\n if (!Array.isArray(localConfig.args)) {\n throw new Error(\"服务配置的 args 字段必须是数组\");\n }\n\n if (localConfig.env && typeof localConfig.env !== \"object\") {\n throw new Error(\"服务配置的 env 字段必须是对象\");\n }\n }\n\n const config = this.getConfig();\n const newConfig = {\n ...config,\n mcpServers: {\n ...config.mcpServers,\n [serverName]: serverConfig,\n },\n };\n this.saveConfig(newConfig);\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.getConfig();\n const newConfig = { ...config };\n\n // 确保 mcpServerConfig 存在\n if (!newConfig.mcpServerConfig) {\n newConfig.mcpServerConfig = {};\n }\n\n // 更新指定服务的工具配置\n newConfig.mcpServerConfig[serverName] = {\n tools: toolsConfig,\n };\n\n this.saveConfig(newConfig);\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.getConfig();\n const newConfig = { ...config };\n\n // 确保 mcpServerConfig 存在\n if (!newConfig.mcpServerConfig) {\n newConfig.mcpServerConfig = {};\n }\n\n // 确保服务配置存在\n if (!newConfig.mcpServerConfig[serverName]) {\n newConfig.mcpServerConfig[serverName] = { tools: {} };\n }\n\n // 更新工具配置\n newConfig.mcpServerConfig[serverName].tools[toolName] = {\n enable: enabled,\n ...(description && { description }),\n };\n\n this.saveConfig(newConfig);\n }\n\n /**\n * 保存配置到文件\n */\n private saveConfig(config: AppConfig): void {\n try {\n // 验证配置\n this.validateConfig(config);\n\n // 格式化 JSON 并保存\n const configPath = this.getConfigFilePath();\n const configJson = JSON.stringify(config, null, 2);\n writeFileSync(configPath, configJson, \"utf8\");\n\n // 更新缓存\n this.config = config;\n } catch (error) {\n throw new Error(\n `保存配置失败: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n /**\n * 重新加载配置(清除缓存)\n */\n public reloadConfig(): void {\n this.config = null;\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.getConfig();\n const currentConnectionConfig = config.connection || {};\n\n const newConnectionConfig = {\n ...currentConnectionConfig,\n ...connectionConfig,\n };\n\n const newConfig = {\n ...config,\n connection: newConnectionConfig,\n };\n\n this.saveConfig(newConfig);\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.getConfig();\n const currentModelScopeConfig = config.modelscope || {};\n\n const newModelScopeConfig = {\n ...currentModelScopeConfig,\n ...modelScopeConfig,\n };\n\n const newConfig = {\n ...config,\n modelscope: newModelScopeConfig,\n };\n\n this.saveConfig(newConfig);\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 UI 配置\n */\n public updateWebUIConfig(webUIConfig: Partial<WebUIConfig>): void {\n const config = this.getConfig();\n const currentWebUIConfig = config.webUI || {};\n\n const newWebUIConfig = {\n ...currentWebUIConfig,\n ...webUIConfig,\n };\n\n const newConfig = {\n ...config,\n webUI: newWebUIConfig,\n };\n\n this.saveConfig(newConfig);\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 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 {\n type MCPToolConfig,\n type StreamableHTTPMCPServerConfig,\n configManager,\n} from \"./configManager\";\nimport { logger as globalLogger } from \"./logger\";\nimport type { IMCPClient } from \"./mcpServerProxy\";\n\n// 为 StreamableHTTP MCP 创建带标签的 logger\nconst logger = globalLogger.withTag(\"StreamableHTTPMCP\");\n\ninterface Tool {\n name: string;\n description?: string;\n inputSchema?: any;\n}\n\ninterface JSONRPCRequest {\n jsonrpc: \"2.0\";\n method: string;\n params?: any;\n id: number | string;\n}\n\ninterface JSONRPCResponse {\n jsonrpc: \"2.0\";\n result?: any;\n error?: {\n code: number;\n message: string;\n data?: any;\n };\n id: number | string;\n}\n\n/**\n * Streamable HTTP MCP Client\n * 用于连接基于 HTTP 的 MCP 服务,如高德地图 MCP\n */\nexport class StreamableHTTPMCPClient implements IMCPClient {\n private name: string;\n private config: StreamableHTTPMCPServerConfig;\n private requestId = 1;\n public initialized = false;\n public tools: Tool[] = [];\n public originalTools: Tool[] = [];\n\n constructor(name: string, config: StreamableHTTPMCPServerConfig) {\n this.name = name;\n this.config = config;\n }\n\n /**\n * 生成带前缀的工具名称\n */\n private generatePrefixedToolName(originalToolName: string): string {\n const normalizedServerName = this.name.replace(/-/g, \"_\");\n return `${normalizedServerName}_xzcli_${originalToolName}`;\n }\n\n /**\n * 根据前缀工具名称获取原始工具名称\n */\n getOriginalToolName(prefixedToolName: string): string | null {\n const normalizedServerName = this.name.replace(/-/g, \"_\");\n const prefix = `${normalizedServerName}_xzcli_`;\n\n if (prefixedToolName.startsWith(prefix)) {\n return prefixedToolName.substring(prefix.length);\n }\n\n return null;\n }\n\n /**\n * 发送 JSON-RPC 请求到 HTTP 端点\n */\n private async sendRequest(method: string, params?: any): Promise<any> {\n const request: JSONRPCRequest = {\n jsonrpc: \"2.0\",\n method,\n params,\n id: this.requestId++,\n };\n\n logger.debug(`发送请求到 ${this.name}: ${JSON.stringify(request)}`);\n\n try {\n // 动态导入 node-fetch\n const fetch = (await import(\"node-fetch\")).default;\n\n const response = await fetch(this.config.url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json, text/event-stream\",\n },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result = (await response.json()) as JSONRPCResponse;\n\n if (result.error) {\n throw new Error(\n `JSON-RPC error: ${result.error.message} (code: ${result.error.code})`\n );\n }\n\n return result.result;\n } catch (error) {\n logger.error(\n `请求失败 (${method}): ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n throw error;\n }\n }\n\n /**\n * 发送 JSON-RPC 通知到 HTTP 端点(不包含 id 字段,不期望响应)\n */\n private async sendNotification(method: string, params?: any): Promise<void> {\n const notification = {\n jsonrpc: \"2.0\",\n method,\n params,\n // 注意:通知不包含 id 字段\n };\n\n logger.debug(`发送通知到 ${this.name}: ${JSON.stringify(notification)}`);\n\n try {\n // 动态导入 node-fetch\n const fetch = (await import(\"node-fetch\")).default;\n\n const response = await fetch(this.config.url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json, text/event-stream\",\n },\n body: JSON.stringify(notification),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n // 对于通知,我们不期望有意义的响应,只要没有HTTP错误就认为成功\n logger.debug(`通知 ${method} 发送成功`);\n } catch (error) {\n logger.debug(\n `通知发送失败 (${method}): ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n throw error;\n }\n }\n\n async start() {\n logger.info(`正在启动 Streamable HTTP MCP 客户端:${this.name}`);\n\n try {\n // 初始化连接\n await this.sendRequest(\"initialize\", {\n protocolVersion: \"2024-11-05\",\n capabilities: {\n tools: {},\n },\n clientInfo: {\n name: \"xiaozhi-streamable-http-client\",\n version: \"1.0.0\",\n },\n });\n\n // 发送初始化完成通知(作为通知发送,不是请求)\n try {\n await this.sendNotification(\"notifications/initialized\");\n } catch (error) {\n // 忽略此错误,因为某些服务(如高德地图)不支持此通知\n logger.debug(`${this.name} 不支持 notifications/initialized: ${error}`);\n }\n\n // 获取工具列表\n const listToolsResult = await this.sendRequest(\"tools/list\");\n\n if (listToolsResult?.tools) {\n this.originalTools = listToolsResult.tools;\n\n // 更新配置文件中的工具列表\n await this.updateToolsConfig();\n\n // 生成带前缀的工具名称\n this.tools = this.originalTools\n .filter((tool) => configManager.isToolEnabled(this.name, tool.name))\n .map((tool) => ({\n ...tool,\n name: this.generatePrefixedToolName(tool.name),\n }));\n\n logger.info(\n `Streamable HTTP MCP 客户端 ${this.name} 已初始化,包含 ${this.tools.length}/${this.originalTools.length} 个已启用的工具`\n );\n }\n\n this.initialized = true;\n } catch (error) {\n logger.error(\n `启动 Streamable HTTP MCP 客户端 ${this.name} 失败:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n throw error;\n }\n }\n\n async refreshTools() {\n try {\n const listToolsResult = await this.sendRequest(\"tools/list\");\n\n if (listToolsResult?.tools) {\n this.originalTools = listToolsResult.tools;\n\n // 更新配置文件中的工具列表\n await this.updateToolsConfig();\n\n // 重新生成带前缀的工具名称\n this.tools = this.originalTools\n .filter((tool) => configManager.isToolEnabled(this.name, tool.name))\n .map((tool) => ({\n ...tool,\n name: this.generatePrefixedToolName(tool.name),\n }));\n\n logger.info(\n `已刷新 ${this.name} 的工具列表,包含 ${this.tools.length}/${this.originalTools.length} 个已启用的工具`\n );\n }\n } catch (error) {\n logger.error(\n `刷新 ${this.name} 的工具列表失败:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n async callTool(prefixedName: string, arguments_: any): Promise<any> {\n try {\n // 将前缀名称转换回原始名称\n const originalName = this.getOriginalToolName(prefixedName);\n if (!originalName) {\n throw new Error(`无效的工具名称格式:${prefixedName}`);\n }\n\n const result = await this.sendRequest(\"tools/call\", {\n name: originalName,\n arguments: arguments_,\n });\n\n return result;\n } catch (error) {\n logger.error(\n `在 ${this.name} 上调用工具 ${prefixedName} 失败:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n throw error;\n }\n }\n\n /**\n * 更新配置文件中的工具列表\n */\n private async updateToolsConfig(): Promise<void> {\n try {\n const currentConfig = configManager.getServerToolsConfig(this.name);\n const toolsConfig: Record<string, MCPToolConfig> = {};\n\n // 为每个工具创建配置项\n for (const tool of this.originalTools) {\n const existingConfig = currentConfig[tool.name];\n toolsConfig[tool.name] = {\n description: tool.description || \"\",\n enable: existingConfig?.enable !== false, // 默认启用\n };\n }\n\n // 只有当配置发生变化时才更新\n const hasChanges = Object.keys(toolsConfig).some((toolName) => {\n const existing = currentConfig[toolName];\n const newConfig = toolsConfig[toolName];\n return (\n !existing ||\n existing.enable !== newConfig.enable ||\n existing.description !== newConfig.description\n );\n });\n\n if (hasChanges || Object.keys(currentConfig).length === 0) {\n configManager.updateServerToolsConfig(this.name, toolsConfig);\n logger.info(`${this.name} 已更新工具配置`);\n }\n } catch (error) {\n logger.error(\n `更新 ${this.name} 的工具配置失败:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n async stop() {\n logger.info(`正在停止 ${this.name} 客户端`);\n this.initialized = false;\n }\n}\n"],"mappings":"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","names":["copyFileSync","existsSync","readFileSync","writeFileSync","dirname","resolve","fileURLToPath","__dirname","dirname","fileURLToPath","DEFAULT_CONNECTION_CONFIG","ConfigManager","_ConfigManager","__name","resolve","configDir","configPath","existsSync","copyFileSync","configData","readFileSync","config","error","configObj","endpoint","serverName","serverConfig","sc","toolName","ep","newConfig","currentEndpoints","newEndpoints","localConfig","newMcpServers","toolsConfig","enabled","description","configJson","writeFileSync","connectionConfig","newConnectionConfig","interval","timeout","modelScopeConfig","newModelScopeConfig","apiKey","webUIConfig","newWebUIConfig","port","configManager","fs","path","chalk","createConsola","formatDateTime","date","year","month","day","hours","minutes","seconds","__name","Logger","createConsola","isDaemonMode","logObj","levelMap","colorMap","chalk","text","level","colorFn","timestamp","coloredLevel","message","error","projectDir","path","fs","args","formattedMessage","fullMessage","arg","enable","tag","logger","logger","StreamableHTTPMCPClient","__name","name","config","originalToolName","prefixedToolName","prefix","method","params","request","fetch","response","result","error","notification","listToolsResult","tool","configManager","prefixedName","arguments_","originalName","currentConfig","toolsConfig","existingConfig","toolName","existing","newConfig"]}
|
|
1
|
+
{"version":3,"sources":["../src/configManager.ts","../src/logger.ts","../src/streamableHttpMCPClient.ts"],"sourcesContent":["import { copyFileSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\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}\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\n private constructor() {\n this.defaultConfigPath = resolve(__dirname, \"xiaozhi.config.default.json\");\n }\n\n /**\n * 获取配置文件路径(动态计算)\n */\n private getConfigFilePath(): string {\n // 配置文件路径 - 优先使用环境变量指定的目录,否则使用当前工作目录\n const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n return resolve(configDir, \"xiaozhi.config.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 const configPath = this.getConfigFilePath();\n return existsSync(configPath);\n }\n\n /**\n * 初始化配置文件\n * 从 config.default.json 复制到 config.json\n */\n public initConfig(): void {\n if (!existsSync(this.defaultConfigPath)) {\n throw new Error(\"默认配置文件 xiaozhi.config.default.json 不存在\");\n }\n\n if (this.configExists()) {\n throw new Error(\"配置文件 xiaozhi.config.json 已存在,无需重复初始化\");\n }\n\n const configPath = this.getConfigFilePath();\n copyFileSync(this.defaultConfigPath, configPath);\n this.config = null; // 重置缓存\n }\n\n /**\n * 加载配置文件\n */\n private loadConfig(): AppConfig {\n if (!this.configExists()) {\n throw new Error(\n \"配置文件 xiaozhi.config.json 不存在,请先运行 xiaozhi init 初始化配置\"\n );\n }\n\n try {\n const configPath = this.getConfigFilePath();\n const configData = readFileSync(configPath, \"utf8\");\n const config = JSON.parse(configData) as AppConfig;\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 const sc = serverConfig as Record<string, unknown>;\n\n // 检查服务类型\n if (sc.url && typeof sc.url === \"string\") {\n // URL 类型的服务(SSE 或 Streamable HTTP)\n // type 字段是可选的,可以是 \"sse\" 或 \"streamable-http\"\n if (sc.type && sc.type !== \"sse\" && sc.type !== \"streamable-http\") {\n throw new Error(\n `配置文件格式错误:mcpServers.${serverName}.type 必须是 \"sse\" 或 \"streamable-http\"`\n );\n }\n } else {\n // 本地类型的验证\n if (!sc.command || typeof sc.command !== \"string\") {\n throw new Error(\n `配置文件格式错误:mcpServers.${serverName}.command 无效`\n );\n }\n\n if (!Array.isArray(sc.args)) {\n throw new Error(\n `配置文件格式错误:mcpServers.${serverName}.args 必须是数组`\n );\n }\n\n if (sc.env && typeof sc.env !== \"object\") {\n throw new Error(\n `配置文件格式错误:mcpServers.${serverName}.env 必须是对象`\n );\n }\n }\n }\n }\n\n /**\n * 获取配置(只读)\n */\n public getConfig(): Readonly<AppConfig> {\n if (!this.config) {\n this.config = this.loadConfig();\n }\n\n // 返回深度只读副本\n return JSON.parse(JSON.stringify(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.getConfig();\n const newConfig = { ...config, mcpEndpoint: endpoint };\n this.saveConfig(newConfig);\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.getConfig();\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 const newConfig = { ...config, mcpEndpoint: newEndpoints };\n this.saveConfig(newConfig);\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.getConfig();\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 const newConfig = { ...config, mcpEndpoint: newEndpoints };\n this.saveConfig(newConfig);\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 if (\"type\" in serverConfig && serverConfig.type === \"sse\") {\n // SSE 类型的验证\n if (!serverConfig.url || typeof serverConfig.url !== \"string\") {\n throw new Error(\"SSE 服务配置的 url 字段必须是非空字符串\");\n }\n } else {\n // 本地类型的验证\n const localConfig = serverConfig as LocalMCPServerConfig;\n if (!localConfig.command || typeof localConfig.command !== \"string\") {\n throw new Error(\"服务配置的 command 字段必须是非空字符串\");\n }\n\n if (!Array.isArray(localConfig.args)) {\n throw new Error(\"服务配置的 args 字段必须是数组\");\n }\n\n if (localConfig.env && typeof localConfig.env !== \"object\") {\n throw new Error(\"服务配置的 env 字段必须是对象\");\n }\n }\n\n const config = this.getConfig();\n const newConfig = {\n ...config,\n mcpServers: {\n ...config.mcpServers,\n [serverName]: serverConfig,\n },\n };\n this.saveConfig(newConfig);\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.getConfig();\n const newConfig = { ...config };\n\n // 确保 mcpServerConfig 存在\n if (!newConfig.mcpServerConfig) {\n newConfig.mcpServerConfig = {};\n }\n\n // 如果 toolsConfig 为空对象,则删除该服务的配置\n if (Object.keys(toolsConfig).length === 0) {\n delete newConfig.mcpServerConfig[serverName];\n } else {\n // 更新指定服务的工具配置\n newConfig.mcpServerConfig[serverName] = {\n tools: toolsConfig,\n };\n }\n\n this.saveConfig(newConfig);\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.getConfig();\n const newConfig = { ...config };\n\n // 确保 mcpServerConfig 存在\n if (!newConfig.mcpServerConfig) {\n newConfig.mcpServerConfig = {};\n }\n\n // 确保服务配置存在\n if (!newConfig.mcpServerConfig[serverName]) {\n newConfig.mcpServerConfig[serverName] = { tools: {} };\n }\n\n // 更新工具配置\n newConfig.mcpServerConfig[serverName].tools[toolName] = {\n enable: enabled,\n ...(description && { description }),\n };\n\n this.saveConfig(newConfig);\n }\n\n /**\n * 保存配置到文件\n */\n private saveConfig(config: AppConfig): void {\n try {\n // 验证配置\n this.validateConfig(config);\n\n // 格式化 JSON 并保存\n const configPath = this.getConfigFilePath();\n const configJson = JSON.stringify(config, null, 2);\n writeFileSync(configPath, configJson, \"utf8\");\n\n // 更新缓存\n this.config = config;\n } catch (error) {\n throw new Error(\n `保存配置失败: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n /**\n * 重新加载配置(清除缓存)\n */\n public reloadConfig(): void {\n this.config = null;\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.getConfig();\n const currentConnectionConfig = config.connection || {};\n\n const newConnectionConfig = {\n ...currentConnectionConfig,\n ...connectionConfig,\n };\n\n const newConfig = {\n ...config,\n connection: newConnectionConfig,\n };\n\n this.saveConfig(newConfig);\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.getConfig();\n const currentModelScopeConfig = config.modelscope || {};\n\n const newModelScopeConfig = {\n ...currentModelScopeConfig,\n ...modelScopeConfig,\n };\n\n const newConfig = {\n ...config,\n modelscope: newModelScopeConfig,\n };\n\n this.saveConfig(newConfig);\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 UI 配置\n */\n public updateWebUIConfig(webUIConfig: Partial<WebUIConfig>): void {\n const config = this.getConfig();\n const currentWebUIConfig = config.webUI || {};\n\n const newWebUIConfig = {\n ...currentWebUIConfig,\n ...webUIConfig,\n };\n\n const newConfig = {\n ...config,\n webUI: newWebUIConfig,\n };\n\n this.saveConfig(newConfig);\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 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 {\n type MCPToolConfig,\n type StreamableHTTPMCPServerConfig,\n configManager,\n} from \"./configManager\";\nimport { logger as globalLogger } from \"./logger\";\nimport type { IMCPClient } from \"./mcpServerProxy\";\n\n// 为 StreamableHTTP MCP 创建带标签的 logger\nconst logger = globalLogger.withTag(\"StreamableHTTPMCP\");\n\ninterface Tool {\n name: string;\n description?: string;\n inputSchema?: any;\n}\n\ninterface JSONRPCRequest {\n jsonrpc: \"2.0\";\n method: string;\n params?: any;\n id: number | string;\n}\n\ninterface JSONRPCResponse {\n jsonrpc: \"2.0\";\n result?: any;\n error?: {\n code: number;\n message: string;\n data?: any;\n };\n id: number | string;\n}\n\n/**\n * Streamable HTTP MCP Client\n * 用于连接基于 HTTP 的 MCP 服务,如高德地图 MCP\n */\nexport class StreamableHTTPMCPClient implements IMCPClient {\n private name: string;\n private config: StreamableHTTPMCPServerConfig;\n private requestId = 1;\n public initialized = false;\n public tools: Tool[] = [];\n public originalTools: Tool[] = [];\n\n constructor(name: string, config: StreamableHTTPMCPServerConfig) {\n this.name = name;\n this.config = config;\n }\n\n /**\n * 生成带前缀的工具名称\n */\n private generatePrefixedToolName(originalToolName: string): string {\n const normalizedServerName = this.name.replace(/-/g, \"_\");\n return `${normalizedServerName}_xzcli_${originalToolName}`;\n }\n\n /**\n * 根据前缀工具名称获取原始工具名称\n */\n getOriginalToolName(prefixedToolName: string): string | null {\n const normalizedServerName = this.name.replace(/-/g, \"_\");\n const prefix = `${normalizedServerName}_xzcli_`;\n\n if (prefixedToolName.startsWith(prefix)) {\n return prefixedToolName.substring(prefix.length);\n }\n\n return null;\n }\n\n /**\n * 发送 JSON-RPC 请求到 HTTP 端点\n */\n private async sendRequest(method: string, params?: any): Promise<any> {\n const request: JSONRPCRequest = {\n jsonrpc: \"2.0\",\n method,\n params,\n id: this.requestId++,\n };\n\n logger.debug(`发送请求到 ${this.name}: ${JSON.stringify(request)}`);\n\n try {\n // 动态导入 node-fetch\n const fetch = (await import(\"node-fetch\")).default;\n\n const response = await fetch(this.config.url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json, text/event-stream\",\n },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result = (await response.json()) as JSONRPCResponse;\n\n if (result.error) {\n throw new Error(\n `JSON-RPC error: ${result.error.message} (code: ${result.error.code})`\n );\n }\n\n return result.result;\n } catch (error) {\n logger.error(\n `请求失败 (${method}): ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n throw error;\n }\n }\n\n /**\n * 发送 JSON-RPC 通知到 HTTP 端点(不包含 id 字段,不期望响应)\n */\n private async sendNotification(method: string, params?: any): Promise<void> {\n const notification = {\n jsonrpc: \"2.0\",\n method,\n params,\n // 注意:通知不包含 id 字段\n };\n\n logger.debug(`发送通知到 ${this.name}: ${JSON.stringify(notification)}`);\n\n try {\n // 动态导入 node-fetch\n const fetch = (await import(\"node-fetch\")).default;\n\n const response = await fetch(this.config.url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json, text/event-stream\",\n },\n body: JSON.stringify(notification),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n // 对于通知,我们不期望有意义的响应,只要没有HTTP错误就认为成功\n logger.debug(`通知 ${method} 发送成功`);\n } catch (error) {\n logger.debug(\n `通知发送失败 (${method}): ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n throw error;\n }\n }\n\n async start() {\n logger.info(`正在启动 Streamable HTTP MCP 客户端:${this.name}`);\n\n try {\n // 初始化连接\n await this.sendRequest(\"initialize\", {\n protocolVersion: \"2024-11-05\",\n capabilities: {\n tools: {},\n },\n clientInfo: {\n name: \"xiaozhi-streamable-http-client\",\n version: \"1.0.0\",\n },\n });\n\n // 发送初始化完成通知(作为通知发送,不是请求)\n try {\n await this.sendNotification(\"notifications/initialized\");\n } catch (error) {\n // 忽略此错误,因为某些服务(如高德地图)不支持此通知\n logger.debug(`${this.name} 不支持 notifications/initialized: ${error}`);\n }\n\n // 获取工具列表\n const listToolsResult = await this.sendRequest(\"tools/list\");\n\n if (listToolsResult?.tools) {\n this.originalTools = listToolsResult.tools;\n\n // 更新配置文件中的工具列表\n await this.updateToolsConfig();\n\n // 生成带前缀的工具名称\n this.tools = this.originalTools\n .filter((tool) => configManager.isToolEnabled(this.name, tool.name))\n .map((tool) => ({\n ...tool,\n name: this.generatePrefixedToolName(tool.name),\n }));\n\n logger.info(\n `Streamable HTTP MCP 客户端 ${this.name} 已初始化,包含 ${this.tools.length}/${this.originalTools.length} 个已启用的工具`\n );\n }\n\n this.initialized = true;\n } catch (error) {\n logger.error(\n `启动 Streamable HTTP MCP 客户端 ${this.name} 失败:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n throw error;\n }\n }\n\n async refreshTools() {\n try {\n const listToolsResult = await this.sendRequest(\"tools/list\");\n\n if (listToolsResult?.tools) {\n this.originalTools = listToolsResult.tools;\n\n // 更新配置文件中的工具列表\n await this.updateToolsConfig();\n\n // 重新生成带前缀的工具名称\n this.tools = this.originalTools\n .filter((tool) => configManager.isToolEnabled(this.name, tool.name))\n .map((tool) => ({\n ...tool,\n name: this.generatePrefixedToolName(tool.name),\n }));\n\n logger.info(\n `已刷新 ${this.name} 的工具列表,包含 ${this.tools.length}/${this.originalTools.length} 个已启用的工具`\n );\n }\n } catch (error) {\n logger.error(\n `刷新 ${this.name} 的工具列表失败:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n async callTool(prefixedName: string, arguments_: any): Promise<any> {\n try {\n // 将前缀名称转换回原始名称\n const originalName = this.getOriginalToolName(prefixedName);\n if (!originalName) {\n throw new Error(`无效的工具名称格式:${prefixedName}`);\n }\n\n const result = await this.sendRequest(\"tools/call\", {\n name: originalName,\n arguments: arguments_,\n });\n\n return result;\n } catch (error) {\n logger.error(\n `在 ${this.name} 上调用工具 ${prefixedName} 失败:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n throw error;\n }\n }\n\n /**\n * 更新配置文件中的工具列表\n */\n private async updateToolsConfig(): Promise<void> {\n try {\n const currentConfig = configManager.getServerToolsConfig(this.name);\n const toolsConfig: Record<string, MCPToolConfig> = {};\n\n // 为每个工具创建配置项\n for (const tool of this.originalTools) {\n const existingConfig = currentConfig[tool.name];\n toolsConfig[tool.name] = {\n description: tool.description || \"\",\n enable: existingConfig?.enable !== false, // 默认启用\n };\n }\n\n // 只有当配置发生变化时才更新\n const hasChanges = Object.keys(toolsConfig).some((toolName) => {\n const existing = currentConfig[toolName];\n const newConfig = toolsConfig[toolName];\n return (\n !existing ||\n existing.enable !== newConfig.enable ||\n existing.description !== newConfig.description\n );\n });\n\n if (hasChanges || Object.keys(currentConfig).length === 0) {\n configManager.updateServerToolsConfig(this.name, toolsConfig);\n logger.info(`${this.name} 已更新工具配置`);\n }\n } catch (error) {\n logger.error(\n `更新 ${this.name} 的工具配置失败:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n async stop() {\n logger.info(`正在停止 ${this.name} 客户端`);\n this.initialized = false;\n }\n}\n"],"mappings":"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","names":["copyFileSync","existsSync","readFileSync","writeFileSync","dirname","resolve","fileURLToPath","__dirname","dirname","fileURLToPath","DEFAULT_CONNECTION_CONFIG","ConfigManager","_ConfigManager","__name","resolve","configDir","configPath","existsSync","copyFileSync","configData","readFileSync","config","error","configObj","endpoint","serverName","serverConfig","sc","toolName","ep","newConfig","currentEndpoints","newEndpoints","localConfig","newMcpServers","toolsConfig","enabled","description","configJson","writeFileSync","connectionConfig","newConnectionConfig","interval","timeout","modelScopeConfig","newModelScopeConfig","apiKey","webUIConfig","newWebUIConfig","port","configManager","fs","path","chalk","createConsola","formatDateTime","date","year","month","day","hours","minutes","seconds","__name","Logger","createConsola","isDaemonMode","logObj","levelMap","colorMap","chalk","text","level","colorFn","timestamp","coloredLevel","message","error","projectDir","path","fs","args","formattedMessage","fullMessage","arg","enable","tag","logger","logger","StreamableHTTPMCPClient","__name","name","config","originalToolName","prefixedToolName","prefix","method","params","request","fetch","response","result","error","notification","listToolsResult","tool","configManager","prefixedName","arguments_","originalName","currentConfig","toolsConfig","existingConfig","toolName","existing","newConfig"]}
|
package/dist/webServer.js
CHANGED
|
@@ -8,15 +8,15 @@ data: /messages?sessionId=${r}
|
|
|
8
8
|
`);this.responseBuffer=o.pop()||"";for(let t of o)if(t.trim())try{let r=JSON.parse(t);if(y.debug(`Received response from proxy: ${t.substring(0,200)}...`),r.id!==void 0&&this.pendingRequests.has(r.id)){let i=this.pendingRequests.get(r.id);clearTimeout(i.timeoutId),this.pendingRequests.delete(r.id),i.resolve(r)}}catch{y.debug(`Non-JSON line from proxy: ${t}`)}}catch(o){y.error("Error handling proxy response:",o)}}sendToClient(e,o){try{let t=`event: message
|
|
9
9
|
data: ${JSON.stringify(o)}
|
|
10
10
|
|
|
11
|
-
`;e.response.write(t)}catch(t){y.error(`Failed to send to client ${e.id}:`,t),this.clients.delete(e.sessionId)}}broadcastToClients(e){for(let o of this.clients.values())this.sendToClient(o,e)}async start(){try{await this.startMCPProxy(),this.server=this.app.listen(this.port,()=>{y.info(`MCP Server listening on port ${this.port}`),y.info(`SSE endpoint: http://localhost:${this.port}/sse`),y.info(`Messages endpoint: http://localhost:${this.port}/messages`),y.info(`RPC endpoint: http://localhost:${this.port}/rpc`)}),this.emit("started")}catch(e){throw y.error("Failed to start MCP server:",e),e}}async startMCPProxy(){let e=ne(import.meta.url),o=T.dirname(e),t=null;for(let r=0;r<5;r++){let i=T.join(o,"mcpServerProxy.js"),c=await import("fs");if(c.existsSync(i)){t=i;break}let a=T.join(o,"dist","mcpServerProxy.js");if(c.existsSync(a)){t=a;break}o=T.dirname(o)}if(!t)throw new Error("Could not find mcpServerProxy.js in the project structure");y.info(`Starting MCP proxy from: ${t}`),this.mcpProxy=Me("node",[t],{stdio:["pipe","pipe","pipe"],env:{...process.env,MCP_SERVER_MODE:"true",XIAOZHI_CONFIG_DIR:process.env.XIAOZHI_CONFIG_DIR||process.cwd()}}),this.mcpProxy.on("error",r=>{y.error("MCP proxy error:",r)}),this.mcpProxy.on("exit",(r,i)=>{y.warn(`MCP proxy exited with code ${r}, signal ${i}`),this.mcpProxy=null}),this.mcpProxy.stderr&&this.mcpProxy.stderr.on("data",r=>{let i=r.toString().trim();i.includes("[ERROR]")||i.includes("Error:")||i.includes("Failed")?y.error("MCP proxy stderr:",i):y.info("MCP proxy output:",i)}),this.mcpProxy.stdout&&this.mcpProxy.stdout.on("data",r=>{this.handleProxyResponse(r)}),await new Promise((r,i)=>{let c=setTimeout(()=>{i(new Error("MCP proxy startup timeout"))},1e4),a=g(d=>{let m=d.toString();(m.includes("MCP proxy ready")||m.includes("started"))&&(clearTimeout(c),this.mcpProxy?.stdout?.removeListener("data",a),r())},"dataHandler");this.mcpProxy?.stdout?.on("data",a)}),y.info("MCP proxy started successfully")}async stop(){y.info("Stopping MCP server...");for(let e of this.clients.values())try{e.response.end()}catch{}this.clients.clear(),this.server&&(await new Promise(e=>{this.server.close(()=>e())}),this.server=null),this.mcpProxy&&(this.mcpProxy.kill("SIGTERM"),await new Promise(e=>{this.mcpProxy.on("exit",()=>e()),setTimeout(()=>{this.mcpProxy?.kill("SIGKILL"),e()},5e3)}),this.mcpProxy=null),this.emit("stopped"),y.info("MCP server stopped")}}});import{spawn as fe}from"child_process";import{existsSync as J}from"fs";import{readFile as de}from"fs/promises";import{createServer as qe}from"http";import{dirname as Ke,join as I}from"path";import{parse as Ye}from"url";import{fileURLToPath as Qe}from"url";import{WebSocketServer as eo}from"ws";import{spawn as H}from"child_process";import u from"fs";import f from"path";import{fileURLToPath as P}from"url";import s from"chalk";import{Command as Oe}from"commander";import b from"ora";import be from"omelette";import{copyFileSync as he,existsSync as Z,readFileSync as ue,writeFileSync as ye}from"fs";import{dirname as Ce,resolve as B}from"path";import{fileURLToPath as ve}from"url";var Se=Ce(ve(import.meta.url)),k={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},A=class n{static{g(this,"ConfigManager")}static instance;defaultConfigPath;config=null;constructor(){this.defaultConfigPath=B(Se,"xiaozhi.config.default.json")}getConfigFilePath(){let e=process.env.XIAOZHI_CONFIG_DIR||process.cwd();return B(e,"xiaozhi.config.json")}static getInstance(){return n.instance||(n.instance=new n),n.instance}configExists(){let e=this.getConfigFilePath();return Z(e)}initConfig(){if(!Z(this.defaultConfigPath))throw new Error("\u9ED8\u8BA4\u914D\u7F6E\u6587\u4EF6 xiaozhi.config.default.json \u4E0D\u5B58\u5728");if(this.configExists())throw new Error("\u914D\u7F6E\u6587\u4EF6 xiaozhi.config.json \u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u91CD\u590D\u521D\u59CB\u5316");let e=this.getConfigFilePath();he(this.defaultConfigPath,e),this.config=null}loadConfig(){if(!this.configExists())throw new Error("\u914D\u7F6E\u6587\u4EF6 xiaozhi.config.json \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u8FD0\u884C xiaozhi init \u521D\u59CB\u5316\u914D\u7F6E");try{let e=this.getConfigFilePath(),o=ue(e,"utf8"),t=JSON.parse(o);return this.validateConfig(t),t}catch(e){throw e instanceof SyntaxError?new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF: ${e.message}`):e}}validateConfig(e){if(!e||typeof e!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A\u6839\u5BF9\u8C61\u65E0\u6548");let o=e;if(o.mcpEndpoint===void 0||o.mcpEndpoint===null)throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5B57\u6BB5\u65E0\u6548");if(typeof o.mcpEndpoint!="string")if(Array.isArray(o.mcpEndpoint)){if(o.mcpEndpoint.length===0)throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u6570\u7EC4\u4E0D\u80FD\u4E3A\u7A7A");for(let t of o.mcpEndpoint)if(typeof t!="string"||t.trim()==="")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u6570\u7EC4\u4E2D\u7684\u6BCF\u4E2A\u5143\u7D20\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6216\u5B57\u7B26\u4E32\u6570\u7EC4");if(!o.mcpServers||typeof o.mcpServers!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers \u5B57\u6BB5\u65E0\u6548");for(let[t,r]of Object.entries(o.mcpServers)){if(!r||typeof r!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${t} \u65E0\u6548`);let i=r;if(i.url&&typeof i.url=="string"){if(i.type&&i.type!=="sse"&&i.type!=="streamable-http")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${t}.type \u5FC5\u987B\u662F "sse" \u6216 "streamable-http"`)}else{if(!i.command||typeof i.command!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${t}.command \u65E0\u6548`);if(!Array.isArray(i.args))throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${t}.args \u5FC5\u987B\u662F\u6570\u7EC4`);if(i.env&&typeof i.env!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${t}.env \u5FC5\u987B\u662F\u5BF9\u8C61`)}}}getConfig(){return this.config||(this.config=this.loadConfig()),JSON.parse(JSON.stringify(this.config))}getMcpEndpoint(){let e=this.getConfig();return Array.isArray(e.mcpEndpoint)?e.mcpEndpoint[0]||"":e.mcpEndpoint}getMcpEndpoints(){let e=this.getConfig();return Array.isArray(e.mcpEndpoint)?[...e.mcpEndpoint]:e.mcpEndpoint?[e.mcpEndpoint]:[]}getMcpServers(){return this.getConfig().mcpServers}getMcpServerConfig(){return this.getConfig().mcpServerConfig||{}}getServerToolsConfig(e){return this.getMcpServerConfig()[e]?.tools||{}}isToolEnabled(e,o){return this.getServerToolsConfig(e)[o]?.enable!==!1}updateMcpEndpoint(e){if(Array.isArray(e)){if(e.length===0)throw new Error("MCP \u7AEF\u70B9\u6570\u7EC4\u4E0D\u80FD\u4E3A\u7A7A");for(let r of e)if(!r||typeof r!="string")throw new Error("MCP \u7AEF\u70B9\u6570\u7EC4\u4E2D\u7684\u6BCF\u4E2A\u5143\u7D20\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let t={...this.getConfig(),mcpEndpoint:e};this.saveConfig(t)}addMcpEndpoint(e){if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let o=this.getConfig(),t=this.getMcpEndpoints();if(t.includes(e))throw new Error(`MCP \u7AEF\u70B9 ${e} \u5DF2\u5B58\u5728`);let r=[...t,e],i={...o,mcpEndpoint:r};this.saveConfig(i)}removeMcpEndpoint(e){if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let o=this.getConfig(),t=this.getMcpEndpoints();if(t.indexOf(e)===-1)throw new Error(`MCP \u7AEF\u70B9 ${e} \u4E0D\u5B58\u5728`);if(t.length===1)throw new Error("\u4E0D\u80FD\u5220\u9664\u6700\u540E\u4E00\u4E2A MCP \u7AEF\u70B9");let i=t.filter(a=>a!==e),c={...o,mcpEndpoint:i};this.saveConfig(c)}updateMcpServer(e,o){if(!e||typeof e!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if("type"in o&&o.type==="sse"){if(!o.url||typeof o.url!="string")throw new Error("SSE \u670D\u52A1\u914D\u7F6E\u7684 url \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else{let i=o;if(!i.command||typeof i.command!="string")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 command \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(!Array.isArray(i.args))throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 args \u5B57\u6BB5\u5FC5\u987B\u662F\u6570\u7EC4");if(i.env&&typeof i.env!="object")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 env \u5B57\u6BB5\u5FC5\u987B\u662F\u5BF9\u8C61")}let t=this.getConfig(),r={...t,mcpServers:{...t.mcpServers,[e]:o}};this.saveConfig(r)}removeMcpServer(e){if(!e||typeof e!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let o=this.getConfig();if(!o.mcpServers[e])throw new Error(`\u670D\u52A1 ${e} \u4E0D\u5B58\u5728`);let t={...o.mcpServers};delete t[e];let r={...o,mcpServers:t};this.saveConfig(r)}updateServerToolsConfig(e,o){let r={...this.getConfig()};r.mcpServerConfig||(r.mcpServerConfig={}),r.mcpServerConfig[e]={tools:o},this.saveConfig(r)}setToolEnabled(e,o,t,r){let c={...this.getConfig()};c.mcpServerConfig||(c.mcpServerConfig={}),c.mcpServerConfig[e]||(c.mcpServerConfig[e]={tools:{}}),c.mcpServerConfig[e].tools[o]={enable:t,...r&&{description:r}},this.saveConfig(c)}saveConfig(e){try{this.validateConfig(e);let o=this.getConfigFilePath(),t=JSON.stringify(e,null,2);ye(o,t,"utf8"),this.config=e}catch(o){throw new Error(`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`)}}reloadConfig(){this.config=null}getConfigPath(){return this.getConfigFilePath()}getDefaultConfigPath(){return this.defaultConfigPath}getConnectionConfig(){let o=this.getConfig().connection||{};return{heartbeatInterval:o.heartbeatInterval??k.heartbeatInterval,heartbeatTimeout:o.heartbeatTimeout??k.heartbeatTimeout,reconnectInterval:o.reconnectInterval??k.reconnectInterval}}getHeartbeatInterval(){return this.getConnectionConfig().heartbeatInterval}getHeartbeatTimeout(){return this.getConnectionConfig().heartbeatTimeout}getReconnectInterval(){return this.getConnectionConfig().reconnectInterval}updateConnectionConfig(e){let o=this.getConfig(),r={...o.connection||{},...e},i={...o,connection:r};this.saveConfig(i)}setHeartbeatInterval(e){if(e<=0)throw new Error("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatInterval:e})}setHeartbeatTimeout(e){if(e<=0)throw new Error("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatTimeout:e})}setReconnectInterval(e){if(e<=0)throw new Error("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({reconnectInterval:e})}getModelScopeConfig(){return this.getConfig().modelscope||{}}getModelScopeApiKey(){return this.getModelScopeConfig().apiKey||process.env.MODELSCOPE_API_TOKEN}updateModelScopeConfig(e){let o=this.getConfig(),r={...o.modelscope||{},...e},i={...o,modelscope:r};this.saveConfig(i)}setModelScopeApiKey(e){if(!e||typeof e!="string")throw new Error("API Key \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");this.updateModelScopeConfig({apiKey:e})}getWebUIConfig(){return this.getConfig().webUI||{}}getWebUIPort(){return this.getWebUIConfig().port??9999}updateWebUIConfig(e){let o=this.getConfig(),r={...o.webUI||{},...e},i={...o,webUI:r};this.saveConfig(i)}setWebUIPort(e){if(!Number.isInteger(e)||e<=0||e>65535)throw new Error("\u7AEF\u53E3\u53F7\u5FC5\u987B\u662F 1-65535 \u4E4B\u95F4\u7684\u6574\u6570");this.updateWebUIConfig({port:e})}},l=A.getInstance();function we(){try{if(!l.configExists())return[];let n=l.getMcpServers();return Object.keys(n)}catch{return[]}}g(we,"getMcpServerNames");function Pe(n){try{if(!l.configExists())return[];let e=l.getServerToolsConfig(n);return Object.keys(e)}catch{return[]}}g(Pe,"getServerToolNames");function V(){let n=be("xiaozhi <command>");if(n.on("command",({reply:e})=>{e(["create","init","config","start","stop","status","attach","restart","mcp","completion"])}),n.on("complete",(e,{line:o,before:t,reply:r})=>{process.env.XIAOZHI_DEBUG_COMPLETION&&console.error(`Debug completion - line: "${o}", before: "${t}", fragment: "${e}"`);let i=o.trim().split(/\s+/),a=o!==o.trim()?i.length:i.length-1;if(i[1]==="mcp"){let d=i[2];if(a===2){let m=["list","server","tool"],h=i[2]||"",C=m.filter(v=>v.startsWith(h));r(C);return}if(a===3){switch(d){case"list":{let m=["--tools"],h=i[3]||"",C=m.filter(v=>v.startsWith(h));r(C);break}case"server":case"tool":{let m=we(),h=i[3]||"",C=m.filter(v=>v.startsWith(h));r(C);break}default:r([])}return}if(a===4&&d==="tool"){let m=i[3],h=Pe(m),C=i[4]||"",v=h.filter(E=>E.startsWith(C));r(v);return}if(a===5&&d==="tool"){let m=["enable","disable"],h=i[5]||"",C=m.filter(v=>v.startsWith(h));r(C);return}}if(a===2){switch(i[1]){case"create":r(["--template","-t"]);break;case"start":case"restart":r(["--daemon","-d"]);break;case"completion":r(["install","uninstall"]);break;default:r([])}return}r([])}),process.argv.includes("--completion")){try{console.log(n.setupShellInitFile())}catch(e){console.error("\u751F\u6210\u81EA\u52A8\u8865\u5168\u811A\u672C\u65F6\u51FA\u9519:",e)}process.exit(0)}process.argv.includes("--completion-fish")&&(console.log(n.setupShellInitFile("fish")),process.exit(0)),process.argv.includes("--compzsh")||process.argv.includes("--compbash"),n.init()}g(V,"setupAutoCompletion");function q(){console.log("\u{1F680} xiaozhi \u81EA\u52A8\u8865\u5168\u8BBE\u7F6E"),console.log(),console.log("\u8981\u542F\u7528\u81EA\u52A8\u8865\u5168\uFF0C\u8BF7\u6839\u636E\u4F60\u7684shell\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\uFF1A"),console.log(),console.log("\u{1F4DD} Zsh (\u63A8\u8350):"),console.log(" xiaozhi --completion >> ~/.xiaozhi-completion.zsh"),console.log(" echo 'source ~/.xiaozhi-completion.zsh' >> ~/.zshrc"),console.log(" source ~/.zshrc"),console.log(),console.log("\u{1F4DD} Bash:"),console.log(" xiaozhi --completion >> ~/.xiaozhi-completion.bash"),console.log(" echo 'source ~/.xiaozhi-completion.bash' >> ~/.bash_profile"),console.log(" source ~/.bash_profile"),console.log(),console.log("\u{1F4DD} Fish:"),console.log(" xiaozhi --completion-fish >> ~/.config/fish/completions/xiaozhi.fish"),console.log(),console.log("\u2728 \u8BBE\u7F6E\u5B8C\u6210\u540E\uFF0C\u4F60\u5C31\u53EF\u4EE5\u4F7F\u7528 Tab \u952E\u8FDB\u884C\u81EA\u52A8\u8865\u5168\u4E86\uFF01"),console.log(),console.log("\u{1F4A1} \u793A\u4F8B:"),console.log(" xiaozhi m<Tab> # \u2192 mcp"),console.log(" xiaozhi mcp l<Tab> # \u2192 list"),console.log(" xiaozhi mcp tool <Tab> # \u2192 \u663E\u793A\u6240\u6709\u670D\u52A1\u5668\u540D\u79F0")}g(q,"showCompletionHelp");z();import p from"chalk";import K from"cli-table3";import F from"ora";function Y(n){let e=0;for(let o of n)/[\u4e00-\u9fff\u3400-\u4dbf\uff00-\uffef]/.test(o)?e+=2:e+=1;return e}g(Y,"getDisplayWidth");function Q(n,e){if(Y(n)<=e)return n;if(e<=3)return"";let o="",t=0,r=!1;for(let i of n){let c=/[\u4e00-\u9fff\u3400-\u4dbf\uff00-\uffef]/.test(i)?2:1;if(t+c>e-3){if(!r)return"";o+="...";break}o+=i,t+=c,r=!0}return o}g(Q,"truncateToWidth");async function ee(n={}){let e=F("\u83B7\u53D6 MCP \u670D\u52A1\u5217\u8868...").start();try{let o=l.getMcpServers(),t=Object.keys(o);if(t.length===0){e.warn("\u672A\u914D\u7F6E\u4EFB\u4F55 MCP \u670D\u52A1"),console.log(p.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi config' \u547D\u4EE4\u914D\u7F6E MCP \u670D\u52A1"));return}if(e.succeed(`\u627E\u5230 ${t.length} \u4E2A MCP \u670D\u52A1`),n.tools){console.log(),console.log(p.bold("MCP \u670D\u52A1\u5DE5\u5177\u5217\u8868:")),console.log();let r=8,i=[];for(let a of t){let d=l.getServerToolsConfig(a),m=Object.keys(d);i.push(...m)}for(let a of i){let d=Y(a);d>r&&(r=d)}r=Math.max(10,Math.min(r+2,30));let c=new K({head:[p.bold("MCP"),p.bold("\u5DE5\u5177\u540D\u79F0"),p.bold("\u72B6\u6001"),p.bold("\u63CF\u8FF0")],colWidths:[15,r,8,40],wordWrap:!0,style:{head:[],border:[]}});for(let a of t){let d=l.getServerToolsConfig(a),m=Object.keys(d);if(m.length===0)c.push([p.gray(a),p.gray("-"),p.gray("-"),p.gray("\u6682\u672A\u8BC6\u522B\u5230\u76F8\u5173\u5DE5\u5177")]);else{c.length>0&&c.push([{colSpan:4,content:""}]);for(let h of m){let C=d[h],v=C.enable?p.green("\u542F\u7528"):p.red("\u7981\u7528"),E=Q(C.description||"",32);c.push([a,h,v,E])}}}console.log(c.toString())}else{console.log(),console.log(p.bold("MCP \u670D\u52A1\u5217\u8868:")),console.log();for(let r of t){let i=o[r],c=l.getServerToolsConfig(r),a=Object.keys(c).length,d=Object.values(c).filter(m=>m.enable!==!1).length;console.log(`${p.cyan("\u2022")} ${p.bold(r)}`),"url"in i?("type"in i&&i.type==="sse"?console.log(` \u7C7B\u578B: ${p.gray("SSE")}`):console.log(` \u7C7B\u578B: ${p.gray("Streamable HTTP")}`),console.log(` URL: ${p.gray(i.url)}`)):console.log(` \u547D\u4EE4: ${p.gray(i.command)} ${p.gray(i.args.join(" "))}`),a>0?console.log(` \u5DE5\u5177: ${p.green(d)} \u542F\u7528 / ${p.yellow(a)} \u603B\u8BA1`):console.log(` \u5DE5\u5177: ${p.gray("\u672A\u626B\u63CF (\u8BF7\u5148\u542F\u52A8\u670D\u52A1)")}`),console.log()}}console.log(p.gray("\u{1F4A1} \u63D0\u793A:")),console.log(p.gray(" - \u4F7F\u7528 'xiaozhi mcp list --tools' \u67E5\u770B\u6240\u6709\u5DE5\u5177")),console.log(p.gray(" - \u4F7F\u7528 'xiaozhi mcp <\u670D\u52A1\u540D> list' \u67E5\u770B\u6307\u5B9A\u670D\u52A1\u7684\u5DE5\u5177")),console.log(p.gray(" - \u4F7F\u7528 'xiaozhi mcp <\u670D\u52A1\u540D> <\u5DE5\u5177\u540D> enable/disable' \u542F\u7528/\u7981\u7528\u5DE5\u5177"))}catch(o){e.fail("\u83B7\u53D6 MCP \u670D\u52A1\u5217\u8868\u5931\u8D25"),console.error(p.red(`\u9519\u8BEF: ${o instanceof Error?o.message:String(o)}`)),process.exit(1)}}g(ee,"listMcpServers");async function oe(n){let e=F(`\u83B7\u53D6 ${n} \u670D\u52A1\u7684\u5DE5\u5177\u5217\u8868...`).start();try{if(!l.getMcpServers()[n]){e.fail(`\u670D\u52A1 '${n}' \u4E0D\u5B58\u5728`),console.log(p.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp list' \u67E5\u770B\u6240\u6709\u53EF\u7528\u670D\u52A1"));return}let t=l.getServerToolsConfig(n),r=Object.keys(t);if(r.length===0){e.warn(`\u670D\u52A1 '${n}' \u6682\u65E0\u5DE5\u5177\u4FE1\u606F`),console.log(p.yellow("\u{1F4A1} \u63D0\u793A: \u8BF7\u5148\u542F\u52A8\u670D\u52A1\u4EE5\u626B\u63CF\u5DE5\u5177\u5217\u8868"));return}e.succeed(`\u670D\u52A1 '${n}' \u5171\u6709 ${r.length} \u4E2A\u5DE5\u5177`),console.log(),console.log(p.bold(`${n} \u670D\u52A1\u5DE5\u5177\u5217\u8868:`)),console.log();let i=new K({head:[p.bold("\u5DE5\u5177\u540D\u79F0"),p.bold("\u72B6\u6001"),p.bold("\u63CF\u8FF0")],colWidths:[30,8,50],wordWrap:!0,style:{head:[],border:[]}});for(let c of r){let a=t[c],d=a.enable?p.green("\u542F\u7528"):p.red("\u7981\u7528"),m=Q(a.description||"",40);i.push([c,d,m])}console.log(i.toString()),console.log(),console.log(p.gray("\u{1F4A1} \u63D0\u793A:")),console.log(p.gray(` - \u4F7F\u7528 'xiaozhi mcp ${n} <\u5DE5\u5177\u540D> enable' \u542F\u7528\u5DE5\u5177`)),console.log(p.gray(` - \u4F7F\u7528 'xiaozhi mcp ${n} <\u5DE5\u5177\u540D> disable' \u7981\u7528\u5DE5\u5177`))}catch(o){e.fail("\u83B7\u53D6\u5DE5\u5177\u5217\u8868\u5931\u8D25"),console.error(p.red(`\u9519\u8BEF: ${o instanceof Error?o.message:String(o)}`)),process.exit(1)}}g(oe,"listServerTools");async function te(n,e,o){let t=o?"\u542F\u7528":"\u7981\u7528",r=F(`${t}\u5DE5\u5177 ${n}/${e}...`).start();try{if(!l.getMcpServers()[n]){r.fail(`\u670D\u52A1 '${n}' \u4E0D\u5B58\u5728`),console.log(p.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp list' \u67E5\u770B\u6240\u6709\u53EF\u7528\u670D\u52A1"));return}let c=l.getServerToolsConfig(n);if(!c[e]){r.fail(`\u5DE5\u5177 '${e}' \u5728\u670D\u52A1 '${n}' \u4E2D\u4E0D\u5B58\u5728`),console.log(p.yellow(`\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp ${n} list' \u67E5\u770B\u8BE5\u670D\u52A1\u7684\u6240\u6709\u5DE5\u5177`));return}l.setToolEnabled(n,e,o,c[e].description),r.succeed(`\u6210\u529F${t}\u5DE5\u5177 ${p.cyan(n)}/${p.cyan(e)}`),console.log(),console.log(p.gray("\u{1F4A1} \u63D0\u793A: \u5DE5\u5177\u72B6\u6001\u66F4\u6539\u5C06\u5728\u4E0B\u6B21\u542F\u52A8\u670D\u52A1\u65F6\u751F\u6548"))}catch(i){r.fail(`${t}\u5DE5\u5177\u5931\u8D25`),console.error(p.red(`\u9519\u8BEF: ${i instanceof Error?i.message:String(i)}`)),process.exit(1)}}g(te,"setToolEnabled");var S=new Oe,Re="xiaozhi-mcp-service";function ae(){try{let n=P(import.meta.url),e=f.dirname(n),o=[f.join(e,"..","package.json"),f.join(e,"..","package.json"),f.join(e,"..","..","package.json"),f.join(e,"package.json")];for(let t of o)if(u.existsSync(t)){let r=JSON.parse(u.readFileSync(t,"utf8"));if(r.version)return r.version}return"unknown"}catch(n){return console.warn("\u65E0\u6CD5\u4ECE package.json \u8BFB\u53D6\u7248\u672C\u4FE1\u606F:",n),"unknown"}}g(ae,"getVersion");var _=g(()=>{let n=process.env.XIAOZHI_CONFIG_DIR||process.cwd();return f.join(n,`.${Re}.pid`)},"getPidFile");function x(){try{let n=_();if(!u.existsSync(n))return{running:!1};let e=u.readFileSync(n,"utf8").trim(),[o,t,r]=e.split("|"),i=Number.parseInt(o);if(Number.isNaN(i))return u.unlinkSync(n),{running:!1};try{process.kill(i,0);let c=Number.parseInt(t),a=ze(Date.now()-c);return{running:!0,pid:i,uptime:a,mode:r||"foreground"}}catch{return u.unlinkSync(n),{running:!1}}}catch{return{running:!1}}}g(x,"getServiceStatus");function ze(n){let e=Math.floor(n/1e3),o=Math.floor(e/60),t=Math.floor(o/60),r=Math.floor(t/24);return r>0?`${r}\u5929 ${t%24}\u5C0F\u65F6 ${o%60}\u5206\u949F`:t>0?`${t}\u5C0F\u65F6 ${o%60}\u5206\u949F`:o>0?`${o}\u5206\u949F ${e%60}\u79D2`:`${e}\u79D2`}g(ze,"formatUptime");function W(n,e){let o=`${n}|${Date.now()}|${e}`;u.writeFileSync(_(),o)}g(W,"savePidInfo");function j(){try{let n=_();u.existsSync(n)&&u.unlinkSync(n)}catch{}}g(j,"cleanupPidFile");function Ne(){if(!l.configExists())return console.error(s.red("\u274C \u9519\u8BEF: \u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728")),console.log(s.yellow('\u{1F4A1} \u63D0\u793A: \u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E')),!1;try{let n=l.getMcpEndpoint();return!n||n.includes("<\u8BF7\u586B\u5199")?(console.error(s.red("\u274C \u9519\u8BEF: MCP \u7AEF\u70B9\u672A\u914D\u7F6E")),console.log(s.yellow('\u{1F4A1} \u63D0\u793A: \u8BF7\u8FD0\u884C "xiaozhi config mcpEndpoint <your-endpoint-url>" \u8BBE\u7F6E\u7AEF\u70B9')),!1):!0}catch(n){return console.error(s.red(`\u274C \u9519\u8BEF: \u914D\u7F6E\u6587\u4EF6\u65E0\u6548 - ${n instanceof Error?n.message:String(n)}`)),console.log(s.yellow('\u{1F4A1} \u63D0\u793A: \u8BF7\u8FD0\u884C "xiaozhi init" \u91CD\u65B0\u521D\u59CB\u5316\u914D\u7F6E')),!1}}g(Ne,"checkEnvironment");function ke(){let n=f.dirname(P(import.meta.url)),e;return n.includes("js-demo/dist")?e=n:e=[f.join(n,"..","js-demo","dist"),f.join(n,"..","..","js-demo","dist"),f.join(n,"..","..","..","js-demo","dist"),f.join(process.cwd(),"js-demo","dist"),f.join(process.cwd(),"dist")].find(t=>u.existsSync(f.join(t,"adaptiveMCPPipe.js"))&&u.existsSync(f.join(t,"mcpServerProxy.js")))||n,{command:"node",args:["adaptiveMCPPipe.js","mcpServerProxy.js"],cwd:e}}g(ke,"getServiceCommand");async function se(){try{if(!l.configExists()){console.log(s.yellow("\u{1F4A1} \u63D0\u793A: \u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7 Web UI \u542F\u52A8"));return}let n=new O;await n.start();let e=l.getWebUIPort();console.log(s.green(`\u2705 Web UI \u5DF2\u542F\u52A8: http://localhost:${e}`));let{spawn:o}=await import("child_process"),t=`http://localhost:${e}`;try{process.platform==="darwin"?o("open",[t],{detached:!0,stdio:"ignore"}).unref():process.platform==="win32"?o("cmd",["/c","start",t],{detached:!0,stdio:"ignore"}).unref():o("xdg-open",[t],{detached:!0,stdio:"ignore"}).unref()}catch{}global.__webServer=n}catch(n){console.log(s.yellow(`\u26A0\uFE0F Web UI \u542F\u52A8\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`))}}g(se,"startWebUIInBackground");async function le(n=!1,e=!1){let o=b("\u68C0\u67E5\u670D\u52A1\u72B6\u6001...").start();try{let t=x();if(t.running){o.fail(`\u670D\u52A1\u5DF2\u7ECF\u5728\u8FD0\u884C (PID: ${t.pid})`);return}if(o.text="\u68C0\u67E5\u73AF\u5883\u914D\u7F6E...",!Ne()){o.fail("\u73AF\u5883\u914D\u7F6E\u68C0\u67E5\u5931\u8D25");return}let{command:r,args:i,cwd:c}=ke();if(o.text=`\u542F\u52A8\u670D\u52A1 (${n?"\u540E\u53F0\u6A21\u5F0F":"\u524D\u53F0\u6A21\u5F0F"})...`,n){let a=H(r,i,{cwd:c,detached:!0,stdio:["ignore","pipe","pipe"],env:{...process.env,XIAOZHI_CONFIG_DIR:process.cwd(),XIAOZHI_DAEMON:"true"}});W(a.pid,"daemon");let d=process.cwd();w.initLogFile(d),w.enableFileLogging(!0);let m=f.join(d,"xiaozhi.log"),h=u.createWriteStream(m,{flags:"a"});a.stdout?.pipe(h),a.stderr?.pipe(h),a.on("exit",(C,v)=>{C!==0&&C!==null&&w.error(`\u540E\u53F0\u670D\u52A1\u5F02\u5E38\u9000\u51FA (\u4EE3\u7801: ${C}, \u4FE1\u53F7: ${v})`),j()}),a.on("error",C=>{w.error(`\u540E\u53F0\u670D\u52A1\u542F\u52A8\u9519\u8BEF: ${C.message}`),j(),o.fail(`\u540E\u53F0\u670D\u52A1\u542F\u52A8\u5931\u8D25: ${C.message}`)}),a.unref(),o.succeed(`\u670D\u52A1\u5DF2\u5728\u540E\u53F0\u542F\u52A8 (PID: ${a.pid})`),console.log(s.gray(`\u65E5\u5FD7\u6587\u4EF6: ${m}`)),console.log(s.gray("\u4F7F\u7528 'xiaozhi attach' \u53EF\u4EE5\u67E5\u770B\u5B9E\u65F6\u65E5\u5FD7")),e&&await se()}else{o.succeed("\u670D\u52A1\u542F\u52A8\u4E2D...");let a=H(r,i,{cwd:c,stdio:"inherit",env:{...process.env,XIAOZHI_CONFIG_DIR:process.cwd()}});W(a.pid,"foreground"),a.on("exit",(d,m)=>{j(),console.log(d!==0?s.red(`
|
|
11
|
+
`;e.response.write(t)}catch(t){y.error(`Failed to send to client ${e.id}:`,t),this.clients.delete(e.sessionId)}}broadcastToClients(e){for(let o of this.clients.values())this.sendToClient(o,e)}async start(){try{await this.startMCPProxy(),this.server=this.app.listen(this.port,()=>{y.info(`MCP Server listening on port ${this.port}`),y.info(`SSE endpoint: http://localhost:${this.port}/sse`),y.info(`Messages endpoint: http://localhost:${this.port}/messages`),y.info(`RPC endpoint: http://localhost:${this.port}/rpc`)}),this.emit("started")}catch(e){throw y.error("Failed to start MCP server:",e),e}}async startMCPProxy(){let e=ne(import.meta.url),o=T.dirname(e),t=null;for(let r=0;r<5;r++){let i=T.join(o,"mcpServerProxy.js"),c=await import("fs");if(c.existsSync(i)){t=i;break}let a=T.join(o,"dist","mcpServerProxy.js");if(c.existsSync(a)){t=a;break}o=T.dirname(o)}if(!t)throw new Error("Could not find mcpServerProxy.js in the project structure");y.info(`Starting MCP proxy from: ${t}`),this.mcpProxy=Me("node",[t],{stdio:["pipe","pipe","pipe"],env:{...process.env,MCP_SERVER_MODE:"true",XIAOZHI_CONFIG_DIR:process.env.XIAOZHI_CONFIG_DIR||process.cwd()}}),this.mcpProxy.on("error",r=>{y.error("MCP proxy error:",r)}),this.mcpProxy.on("exit",(r,i)=>{y.warn(`MCP proxy exited with code ${r}, signal ${i}`),this.mcpProxy=null}),this.mcpProxy.stderr&&this.mcpProxy.stderr.on("data",r=>{let i=r.toString().trim();i.includes("[ERROR]")||i.includes("Error:")||i.includes("Failed")?y.error("MCP proxy stderr:",i):y.info("MCP proxy output:",i)}),this.mcpProxy.stdout&&this.mcpProxy.stdout.on("data",r=>{this.handleProxyResponse(r)}),await new Promise((r,i)=>{let c=setTimeout(()=>{i(new Error("MCP proxy startup timeout"))},1e4),a=g(d=>{let m=d.toString();(m.includes("MCP proxy ready")||m.includes("started"))&&(clearTimeout(c),this.mcpProxy?.stdout?.removeListener("data",a),r())},"dataHandler");this.mcpProxy?.stdout?.on("data",a)}),y.info("MCP proxy started successfully")}async stop(){y.info("Stopping MCP server...");for(let e of this.clients.values())try{e.response.end()}catch{}this.clients.clear(),this.server&&(await new Promise(e=>{this.server.close(()=>e())}),this.server=null),this.mcpProxy&&(this.mcpProxy.kill("SIGTERM"),await new Promise(e=>{this.mcpProxy.on("exit",()=>e()),setTimeout(()=>{this.mcpProxy?.kill("SIGKILL"),e()},5e3)}),this.mcpProxy=null),this.emit("stopped"),y.info("MCP server stopped")}}});import{spawn as fe}from"child_process";import{existsSync as J}from"fs";import{readFile as de}from"fs/promises";import{createServer as qe}from"http";import{dirname as Ke,join as I}from"path";import{parse as Ye}from"url";import{fileURLToPath as Qe}from"url";import{WebSocketServer as eo}from"ws";import{spawn as H}from"child_process";import u from"fs";import f from"path";import{fileURLToPath as P}from"url";import s from"chalk";import{Command as Oe}from"commander";import b from"ora";import be from"omelette";import{copyFileSync as he,existsSync as Z,readFileSync as ue,writeFileSync as ye}from"fs";import{dirname as ve,resolve as B}from"path";import{fileURLToPath as Ce}from"url";var Se=ve(Ce(import.meta.url)),N={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},A=class n{static{g(this,"ConfigManager")}static instance;defaultConfigPath;config=null;constructor(){this.defaultConfigPath=B(Se,"xiaozhi.config.default.json")}getConfigFilePath(){let e=process.env.XIAOZHI_CONFIG_DIR||process.cwd();return B(e,"xiaozhi.config.json")}static getInstance(){return n.instance||(n.instance=new n),n.instance}configExists(){let e=this.getConfigFilePath();return Z(e)}initConfig(){if(!Z(this.defaultConfigPath))throw new Error("\u9ED8\u8BA4\u914D\u7F6E\u6587\u4EF6 xiaozhi.config.default.json \u4E0D\u5B58\u5728");if(this.configExists())throw new Error("\u914D\u7F6E\u6587\u4EF6 xiaozhi.config.json \u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u91CD\u590D\u521D\u59CB\u5316");let e=this.getConfigFilePath();he(this.defaultConfigPath,e),this.config=null}loadConfig(){if(!this.configExists())throw new Error("\u914D\u7F6E\u6587\u4EF6 xiaozhi.config.json \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u8FD0\u884C xiaozhi init \u521D\u59CB\u5316\u914D\u7F6E");try{let e=this.getConfigFilePath(),o=ue(e,"utf8"),t=JSON.parse(o);return this.validateConfig(t),t}catch(e){throw e instanceof SyntaxError?new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF: ${e.message}`):e}}validateConfig(e){if(!e||typeof e!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A\u6839\u5BF9\u8C61\u65E0\u6548");let o=e;if(o.mcpEndpoint===void 0||o.mcpEndpoint===null)throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5B57\u6BB5\u65E0\u6548");if(typeof o.mcpEndpoint!="string")if(Array.isArray(o.mcpEndpoint)){if(o.mcpEndpoint.length===0)throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u6570\u7EC4\u4E0D\u80FD\u4E3A\u7A7A");for(let t of o.mcpEndpoint)if(typeof t!="string"||t.trim()==="")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u6570\u7EC4\u4E2D\u7684\u6BCF\u4E2A\u5143\u7D20\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6216\u5B57\u7B26\u4E32\u6570\u7EC4");if(!o.mcpServers||typeof o.mcpServers!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers \u5B57\u6BB5\u65E0\u6548");for(let[t,r]of Object.entries(o.mcpServers)){if(!r||typeof r!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${t} \u65E0\u6548`);let i=r;if(i.url&&typeof i.url=="string"){if(i.type&&i.type!=="sse"&&i.type!=="streamable-http")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${t}.type \u5FC5\u987B\u662F "sse" \u6216 "streamable-http"`)}else{if(!i.command||typeof i.command!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${t}.command \u65E0\u6548`);if(!Array.isArray(i.args))throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${t}.args \u5FC5\u987B\u662F\u6570\u7EC4`);if(i.env&&typeof i.env!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${t}.env \u5FC5\u987B\u662F\u5BF9\u8C61`)}}}getConfig(){return this.config||(this.config=this.loadConfig()),JSON.parse(JSON.stringify(this.config))}getMcpEndpoint(){let e=this.getConfig();return Array.isArray(e.mcpEndpoint)?e.mcpEndpoint[0]||"":e.mcpEndpoint}getMcpEndpoints(){let e=this.getConfig();return Array.isArray(e.mcpEndpoint)?[...e.mcpEndpoint]:e.mcpEndpoint?[e.mcpEndpoint]:[]}getMcpServers(){return this.getConfig().mcpServers}getMcpServerConfig(){return this.getConfig().mcpServerConfig||{}}getServerToolsConfig(e){return this.getMcpServerConfig()[e]?.tools||{}}isToolEnabled(e,o){return this.getServerToolsConfig(e)[o]?.enable!==!1}updateMcpEndpoint(e){if(Array.isArray(e)){if(e.length===0)throw new Error("MCP \u7AEF\u70B9\u6570\u7EC4\u4E0D\u80FD\u4E3A\u7A7A");for(let r of e)if(!r||typeof r!="string")throw new Error("MCP \u7AEF\u70B9\u6570\u7EC4\u4E2D\u7684\u6BCF\u4E2A\u5143\u7D20\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let t={...this.getConfig(),mcpEndpoint:e};this.saveConfig(t)}addMcpEndpoint(e){if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let o=this.getConfig(),t=this.getMcpEndpoints();if(t.includes(e))throw new Error(`MCP \u7AEF\u70B9 ${e} \u5DF2\u5B58\u5728`);let r=[...t,e],i={...o,mcpEndpoint:r};this.saveConfig(i)}removeMcpEndpoint(e){if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let o=this.getConfig(),t=this.getMcpEndpoints();if(t.indexOf(e)===-1)throw new Error(`MCP \u7AEF\u70B9 ${e} \u4E0D\u5B58\u5728`);if(t.length===1)throw new Error("\u4E0D\u80FD\u5220\u9664\u6700\u540E\u4E00\u4E2A MCP \u7AEF\u70B9");let i=t.filter(a=>a!==e),c={...o,mcpEndpoint:i};this.saveConfig(c)}updateMcpServer(e,o){if(!e||typeof e!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if("type"in o&&o.type==="sse"){if(!o.url||typeof o.url!="string")throw new Error("SSE \u670D\u52A1\u914D\u7F6E\u7684 url \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else{let i=o;if(!i.command||typeof i.command!="string")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 command \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(!Array.isArray(i.args))throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 args \u5B57\u6BB5\u5FC5\u987B\u662F\u6570\u7EC4");if(i.env&&typeof i.env!="object")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 env \u5B57\u6BB5\u5FC5\u987B\u662F\u5BF9\u8C61")}let t=this.getConfig(),r={...t,mcpServers:{...t.mcpServers,[e]:o}};this.saveConfig(r)}removeMcpServer(e){if(!e||typeof e!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let o=this.getConfig();if(!o.mcpServers[e])throw new Error(`\u670D\u52A1 ${e} \u4E0D\u5B58\u5728`);let t={...o.mcpServers};delete t[e];let r={...o,mcpServers:t};this.saveConfig(r)}updateServerToolsConfig(e,o){let r={...this.getConfig()};r.mcpServerConfig||(r.mcpServerConfig={}),Object.keys(o).length===0?delete r.mcpServerConfig[e]:r.mcpServerConfig[e]={tools:o},this.saveConfig(r)}removeServerToolsConfig(e){let t={...this.getConfig()};t.mcpServerConfig&&(delete t.mcpServerConfig[e],this.saveConfig(t))}setToolEnabled(e,o,t,r){let c={...this.getConfig()};c.mcpServerConfig||(c.mcpServerConfig={}),c.mcpServerConfig[e]||(c.mcpServerConfig[e]={tools:{}}),c.mcpServerConfig[e].tools[o]={enable:t,...r&&{description:r}},this.saveConfig(c)}saveConfig(e){try{this.validateConfig(e);let o=this.getConfigFilePath(),t=JSON.stringify(e,null,2);ye(o,t,"utf8"),this.config=e}catch(o){throw new Error(`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`)}}reloadConfig(){this.config=null}getConfigPath(){return this.getConfigFilePath()}getDefaultConfigPath(){return this.defaultConfigPath}getConnectionConfig(){let o=this.getConfig().connection||{};return{heartbeatInterval:o.heartbeatInterval??N.heartbeatInterval,heartbeatTimeout:o.heartbeatTimeout??N.heartbeatTimeout,reconnectInterval:o.reconnectInterval??N.reconnectInterval}}getHeartbeatInterval(){return this.getConnectionConfig().heartbeatInterval}getHeartbeatTimeout(){return this.getConnectionConfig().heartbeatTimeout}getReconnectInterval(){return this.getConnectionConfig().reconnectInterval}updateConnectionConfig(e){let o=this.getConfig(),r={...o.connection||{},...e},i={...o,connection:r};this.saveConfig(i)}setHeartbeatInterval(e){if(e<=0)throw new Error("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatInterval:e})}setHeartbeatTimeout(e){if(e<=0)throw new Error("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatTimeout:e})}setReconnectInterval(e){if(e<=0)throw new Error("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({reconnectInterval:e})}getModelScopeConfig(){return this.getConfig().modelscope||{}}getModelScopeApiKey(){return this.getModelScopeConfig().apiKey||process.env.MODELSCOPE_API_TOKEN}updateModelScopeConfig(e){let o=this.getConfig(),r={...o.modelscope||{},...e},i={...o,modelscope:r};this.saveConfig(i)}setModelScopeApiKey(e){if(!e||typeof e!="string")throw new Error("API Key \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");this.updateModelScopeConfig({apiKey:e})}getWebUIConfig(){return this.getConfig().webUI||{}}getWebUIPort(){return this.getWebUIConfig().port??9999}updateWebUIConfig(e){let o=this.getConfig(),r={...o.webUI||{},...e},i={...o,webUI:r};this.saveConfig(i)}setWebUIPort(e){if(!Number.isInteger(e)||e<=0||e>65535)throw new Error("\u7AEF\u53E3\u53F7\u5FC5\u987B\u662F 1-65535 \u4E4B\u95F4\u7684\u6574\u6570");this.updateWebUIConfig({port:e})}},l=A.getInstance();function we(){try{if(!l.configExists())return[];let n=l.getMcpServers();return Object.keys(n)}catch{return[]}}g(we,"getMcpServerNames");function Pe(n){try{if(!l.configExists())return[];let e=l.getServerToolsConfig(n);return Object.keys(e)}catch{return[]}}g(Pe,"getServerToolNames");function V(){let n=be("xiaozhi <command>");if(n.on("command",({reply:e})=>{e(["create","init","config","start","stop","status","attach","restart","mcp","completion"])}),n.on("complete",(e,{line:o,before:t,reply:r})=>{process.env.XIAOZHI_DEBUG_COMPLETION&&console.error(`Debug completion - line: "${o}", before: "${t}", fragment: "${e}"`);let i=o.trim().split(/\s+/),a=o!==o.trim()?i.length:i.length-1;if(i[1]==="mcp"){let d=i[2];if(a===2){let m=["list","server","tool"],h=i[2]||"",v=m.filter(C=>C.startsWith(h));r(v);return}if(a===3){switch(d){case"list":{let m=["--tools"],h=i[3]||"",v=m.filter(C=>C.startsWith(h));r(v);break}case"server":case"tool":{let m=we(),h=i[3]||"",v=m.filter(C=>C.startsWith(h));r(v);break}default:r([])}return}if(a===4&&d==="tool"){let m=i[3],h=Pe(m),v=i[4]||"",C=h.filter(E=>E.startsWith(v));r(C);return}if(a===5&&d==="tool"){let m=["enable","disable"],h=i[5]||"",v=m.filter(C=>C.startsWith(h));r(v);return}}if(a===2){switch(i[1]){case"create":r(["--template","-t"]);break;case"start":case"restart":r(["--daemon","-d"]);break;case"completion":r(["install","uninstall"]);break;default:r([])}return}r([])}),process.argv.includes("--completion")){try{console.log(n.setupShellInitFile())}catch(e){console.error("\u751F\u6210\u81EA\u52A8\u8865\u5168\u811A\u672C\u65F6\u51FA\u9519:",e)}process.exit(0)}process.argv.includes("--completion-fish")&&(console.log(n.setupShellInitFile("fish")),process.exit(0)),process.argv.includes("--compzsh")||process.argv.includes("--compbash"),n.init()}g(V,"setupAutoCompletion");function q(){console.log("\u{1F680} xiaozhi \u81EA\u52A8\u8865\u5168\u8BBE\u7F6E"),console.log(),console.log("\u8981\u542F\u7528\u81EA\u52A8\u8865\u5168\uFF0C\u8BF7\u6839\u636E\u4F60\u7684shell\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\uFF1A"),console.log(),console.log("\u{1F4DD} Zsh (\u63A8\u8350):"),console.log(" xiaozhi --completion >> ~/.xiaozhi-completion.zsh"),console.log(" echo 'source ~/.xiaozhi-completion.zsh' >> ~/.zshrc"),console.log(" source ~/.zshrc"),console.log(),console.log("\u{1F4DD} Bash:"),console.log(" xiaozhi --completion >> ~/.xiaozhi-completion.bash"),console.log(" echo 'source ~/.xiaozhi-completion.bash' >> ~/.bash_profile"),console.log(" source ~/.bash_profile"),console.log(),console.log("\u{1F4DD} Fish:"),console.log(" xiaozhi --completion-fish >> ~/.config/fish/completions/xiaozhi.fish"),console.log(),console.log("\u2728 \u8BBE\u7F6E\u5B8C\u6210\u540E\uFF0C\u4F60\u5C31\u53EF\u4EE5\u4F7F\u7528 Tab \u952E\u8FDB\u884C\u81EA\u52A8\u8865\u5168\u4E86\uFF01"),console.log(),console.log("\u{1F4A1} \u793A\u4F8B:"),console.log(" xiaozhi m<Tab> # \u2192 mcp"),console.log(" xiaozhi mcp l<Tab> # \u2192 list"),console.log(" xiaozhi mcp tool <Tab> # \u2192 \u663E\u793A\u6240\u6709\u670D\u52A1\u5668\u540D\u79F0")}g(q,"showCompletionHelp");z();import p from"chalk";import K from"cli-table3";import F from"ora";function Y(n){let e=0;for(let o of n)/[\u4e00-\u9fff\u3400-\u4dbf\uff00-\uffef]/.test(o)?e+=2:e+=1;return e}g(Y,"getDisplayWidth");function Q(n,e){if(Y(n)<=e)return n;if(e<=3)return"";let o="",t=0,r=!1;for(let i of n){let c=/[\u4e00-\u9fff\u3400-\u4dbf\uff00-\uffef]/.test(i)?2:1;if(t+c>e-3){if(!r)return"";o+="...";break}o+=i,t+=c,r=!0}return o}g(Q,"truncateToWidth");async function ee(n={}){let e=F("\u83B7\u53D6 MCP \u670D\u52A1\u5217\u8868...").start();try{let o=l.getMcpServers(),t=Object.keys(o);if(t.length===0){e.warn("\u672A\u914D\u7F6E\u4EFB\u4F55 MCP \u670D\u52A1"),console.log(p.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi config' \u547D\u4EE4\u914D\u7F6E MCP \u670D\u52A1"));return}if(e.succeed(`\u627E\u5230 ${t.length} \u4E2A MCP \u670D\u52A1`),n.tools){console.log(),console.log(p.bold("MCP \u670D\u52A1\u5DE5\u5177\u5217\u8868:")),console.log();let r=8,i=[];for(let a of t){let d=l.getServerToolsConfig(a),m=Object.keys(d);i.push(...m)}for(let a of i){let d=Y(a);d>r&&(r=d)}r=Math.max(10,Math.min(r+2,30));let c=new K({head:[p.bold("MCP"),p.bold("\u5DE5\u5177\u540D\u79F0"),p.bold("\u72B6\u6001"),p.bold("\u63CF\u8FF0")],colWidths:[15,r,8,40],wordWrap:!0,style:{head:[],border:[]}});for(let a of t){let d=l.getServerToolsConfig(a),m=Object.keys(d);if(m.length===0)c.push([p.gray(a),p.gray("-"),p.gray("-"),p.gray("\u6682\u672A\u8BC6\u522B\u5230\u76F8\u5173\u5DE5\u5177")]);else{c.length>0&&c.push([{colSpan:4,content:""}]);for(let h of m){let v=d[h],C=v.enable?p.green("\u542F\u7528"):p.red("\u7981\u7528"),E=Q(v.description||"",32);c.push([a,h,C,E])}}}console.log(c.toString())}else{console.log(),console.log(p.bold("MCP \u670D\u52A1\u5217\u8868:")),console.log();for(let r of t){let i=o[r],c=l.getServerToolsConfig(r),a=Object.keys(c).length,d=Object.values(c).filter(m=>m.enable!==!1).length;console.log(`${p.cyan("\u2022")} ${p.bold(r)}`),"url"in i?("type"in i&&i.type==="sse"?console.log(` \u7C7B\u578B: ${p.gray("SSE")}`):console.log(` \u7C7B\u578B: ${p.gray("Streamable HTTP")}`),console.log(` URL: ${p.gray(i.url)}`)):console.log(` \u547D\u4EE4: ${p.gray(i.command)} ${p.gray(i.args.join(" "))}`),a>0?console.log(` \u5DE5\u5177: ${p.green(d)} \u542F\u7528 / ${p.yellow(a)} \u603B\u8BA1`):console.log(` \u5DE5\u5177: ${p.gray("\u672A\u626B\u63CF (\u8BF7\u5148\u542F\u52A8\u670D\u52A1)")}`),console.log()}}console.log(p.gray("\u{1F4A1} \u63D0\u793A:")),console.log(p.gray(" - \u4F7F\u7528 'xiaozhi mcp list --tools' \u67E5\u770B\u6240\u6709\u5DE5\u5177")),console.log(p.gray(" - \u4F7F\u7528 'xiaozhi mcp <\u670D\u52A1\u540D> list' \u67E5\u770B\u6307\u5B9A\u670D\u52A1\u7684\u5DE5\u5177")),console.log(p.gray(" - \u4F7F\u7528 'xiaozhi mcp <\u670D\u52A1\u540D> <\u5DE5\u5177\u540D> enable/disable' \u542F\u7528/\u7981\u7528\u5DE5\u5177"))}catch(o){e.fail("\u83B7\u53D6 MCP \u670D\u52A1\u5217\u8868\u5931\u8D25"),console.error(p.red(`\u9519\u8BEF: ${o instanceof Error?o.message:String(o)}`)),process.exit(1)}}g(ee,"listMcpServers");async function oe(n){let e=F(`\u83B7\u53D6 ${n} \u670D\u52A1\u7684\u5DE5\u5177\u5217\u8868...`).start();try{if(!l.getMcpServers()[n]){e.fail(`\u670D\u52A1 '${n}' \u4E0D\u5B58\u5728`),console.log(p.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp list' \u67E5\u770B\u6240\u6709\u53EF\u7528\u670D\u52A1"));return}let t=l.getServerToolsConfig(n),r=Object.keys(t);if(r.length===0){e.warn(`\u670D\u52A1 '${n}' \u6682\u65E0\u5DE5\u5177\u4FE1\u606F`),console.log(p.yellow("\u{1F4A1} \u63D0\u793A: \u8BF7\u5148\u542F\u52A8\u670D\u52A1\u4EE5\u626B\u63CF\u5DE5\u5177\u5217\u8868"));return}e.succeed(`\u670D\u52A1 '${n}' \u5171\u6709 ${r.length} \u4E2A\u5DE5\u5177`),console.log(),console.log(p.bold(`${n} \u670D\u52A1\u5DE5\u5177\u5217\u8868:`)),console.log();let i=new K({head:[p.bold("\u5DE5\u5177\u540D\u79F0"),p.bold("\u72B6\u6001"),p.bold("\u63CF\u8FF0")],colWidths:[30,8,50],wordWrap:!0,style:{head:[],border:[]}});for(let c of r){let a=t[c],d=a.enable?p.green("\u542F\u7528"):p.red("\u7981\u7528"),m=Q(a.description||"",40);i.push([c,d,m])}console.log(i.toString()),console.log(),console.log(p.gray("\u{1F4A1} \u63D0\u793A:")),console.log(p.gray(` - \u4F7F\u7528 'xiaozhi mcp ${n} <\u5DE5\u5177\u540D> enable' \u542F\u7528\u5DE5\u5177`)),console.log(p.gray(` - \u4F7F\u7528 'xiaozhi mcp ${n} <\u5DE5\u5177\u540D> disable' \u7981\u7528\u5DE5\u5177`))}catch(o){e.fail("\u83B7\u53D6\u5DE5\u5177\u5217\u8868\u5931\u8D25"),console.error(p.red(`\u9519\u8BEF: ${o instanceof Error?o.message:String(o)}`)),process.exit(1)}}g(oe,"listServerTools");async function te(n,e,o){let t=o?"\u542F\u7528":"\u7981\u7528",r=F(`${t}\u5DE5\u5177 ${n}/${e}...`).start();try{if(!l.getMcpServers()[n]){r.fail(`\u670D\u52A1 '${n}' \u4E0D\u5B58\u5728`),console.log(p.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp list' \u67E5\u770B\u6240\u6709\u53EF\u7528\u670D\u52A1"));return}let c=l.getServerToolsConfig(n);if(!c[e]){r.fail(`\u5DE5\u5177 '${e}' \u5728\u670D\u52A1 '${n}' \u4E2D\u4E0D\u5B58\u5728`),console.log(p.yellow(`\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp ${n} list' \u67E5\u770B\u8BE5\u670D\u52A1\u7684\u6240\u6709\u5DE5\u5177`));return}l.setToolEnabled(n,e,o,c[e].description),r.succeed(`\u6210\u529F${t}\u5DE5\u5177 ${p.cyan(n)}/${p.cyan(e)}`),console.log(),console.log(p.gray("\u{1F4A1} \u63D0\u793A: \u5DE5\u5177\u72B6\u6001\u66F4\u6539\u5C06\u5728\u4E0B\u6B21\u542F\u52A8\u670D\u52A1\u65F6\u751F\u6548"))}catch(i){r.fail(`${t}\u5DE5\u5177\u5931\u8D25`),console.error(p.red(`\u9519\u8BEF: ${i instanceof Error?i.message:String(i)}`)),process.exit(1)}}g(te,"setToolEnabled");var S=new Oe,Re="xiaozhi-mcp-service";function ae(){try{let n=P(import.meta.url),e=f.dirname(n),o=[f.join(e,"..","package.json"),f.join(e,"..","package.json"),f.join(e,"..","..","package.json"),f.join(e,"package.json")];for(let t of o)if(u.existsSync(t)){let r=JSON.parse(u.readFileSync(t,"utf8"));if(r.version)return r.version}return"unknown"}catch(n){return console.warn("\u65E0\u6CD5\u4ECE package.json \u8BFB\u53D6\u7248\u672C\u4FE1\u606F:",n),"unknown"}}g(ae,"getVersion");var _=g(()=>{let n=process.env.XIAOZHI_CONFIG_DIR||process.cwd();return f.join(n,`.${Re}.pid`)},"getPidFile");function x(){try{let n=_();if(!u.existsSync(n))return{running:!1};let e=u.readFileSync(n,"utf8").trim(),[o,t,r]=e.split("|"),i=Number.parseInt(o);if(Number.isNaN(i))return u.unlinkSync(n),{running:!1};try{process.kill(i,0);let c=Number.parseInt(t),a=ze(Date.now()-c);return{running:!0,pid:i,uptime:a,mode:r||"foreground"}}catch{return u.unlinkSync(n),{running:!1}}}catch{return{running:!1}}}g(x,"getServiceStatus");function ze(n){let e=Math.floor(n/1e3),o=Math.floor(e/60),t=Math.floor(o/60),r=Math.floor(t/24);return r>0?`${r}\u5929 ${t%24}\u5C0F\u65F6 ${o%60}\u5206\u949F`:t>0?`${t}\u5C0F\u65F6 ${o%60}\u5206\u949F`:o>0?`${o}\u5206\u949F ${e%60}\u79D2`:`${e}\u79D2`}g(ze,"formatUptime");function W(n,e){let o=`${n}|${Date.now()}|${e}`;u.writeFileSync(_(),o)}g(W,"savePidInfo");function j(){try{let n=_();u.existsSync(n)&&u.unlinkSync(n)}catch{}}g(j,"cleanupPidFile");function ke(){if(!l.configExists())return console.error(s.red("\u274C \u9519\u8BEF: \u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728")),console.log(s.yellow('\u{1F4A1} \u63D0\u793A: \u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E')),!1;try{let n=l.getMcpEndpoint();return!n||n.includes("<\u8BF7\u586B\u5199")?(console.error(s.red("\u274C \u9519\u8BEF: MCP \u7AEF\u70B9\u672A\u914D\u7F6E")),console.log(s.yellow('\u{1F4A1} \u63D0\u793A: \u8BF7\u8FD0\u884C "xiaozhi config mcpEndpoint <your-endpoint-url>" \u8BBE\u7F6E\u7AEF\u70B9')),!1):!0}catch(n){return console.error(s.red(`\u274C \u9519\u8BEF: \u914D\u7F6E\u6587\u4EF6\u65E0\u6548 - ${n instanceof Error?n.message:String(n)}`)),console.log(s.yellow('\u{1F4A1} \u63D0\u793A: \u8BF7\u8FD0\u884C "xiaozhi init" \u91CD\u65B0\u521D\u59CB\u5316\u914D\u7F6E')),!1}}g(ke,"checkEnvironment");function Ne(){let n=f.dirname(P(import.meta.url)),e;return n.includes("js-demo/dist")?e=n:e=[f.join(n,"..","js-demo","dist"),f.join(n,"..","..","js-demo","dist"),f.join(n,"..","..","..","js-demo","dist"),f.join(process.cwd(),"js-demo","dist"),f.join(process.cwd(),"dist")].find(t=>u.existsSync(f.join(t,"adaptiveMCPPipe.js"))&&u.existsSync(f.join(t,"mcpServerProxy.js")))||n,{command:"node",args:["adaptiveMCPPipe.js","mcpServerProxy.js"],cwd:e}}g(Ne,"getServiceCommand");async function se(){try{if(!l.configExists()){console.log(s.yellow("\u{1F4A1} \u63D0\u793A: \u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7 Web UI \u542F\u52A8"));return}let n=new O;await n.start();let e=l.getWebUIPort();console.log(s.green(`\u2705 Web UI \u5DF2\u542F\u52A8: http://localhost:${e}`));let{spawn:o}=await import("child_process"),t=`http://localhost:${e}`;try{process.platform==="darwin"?o("open",[t],{detached:!0,stdio:"ignore"}).unref():process.platform==="win32"?o("cmd",["/c","start",t],{detached:!0,stdio:"ignore"}).unref():o("xdg-open",[t],{detached:!0,stdio:"ignore"}).unref()}catch{}global.__webServer=n}catch(n){console.log(s.yellow(`\u26A0\uFE0F Web UI \u542F\u52A8\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`))}}g(se,"startWebUIInBackground");async function le(n=!1,e=!1){let o=b("\u68C0\u67E5\u670D\u52A1\u72B6\u6001...").start();try{let t=x();if(t.running){o.fail(`\u670D\u52A1\u5DF2\u7ECF\u5728\u8FD0\u884C (PID: ${t.pid})`);return}if(o.text="\u68C0\u67E5\u73AF\u5883\u914D\u7F6E...",!ke()){o.fail("\u73AF\u5883\u914D\u7F6E\u68C0\u67E5\u5931\u8D25");return}let{command:r,args:i,cwd:c}=Ne();if(o.text=`\u542F\u52A8\u670D\u52A1 (${n?"\u540E\u53F0\u6A21\u5F0F":"\u524D\u53F0\u6A21\u5F0F"})...`,n){let a=H(r,i,{cwd:c,detached:!0,stdio:["ignore","pipe","pipe"],env:{...process.env,XIAOZHI_CONFIG_DIR:process.cwd(),XIAOZHI_DAEMON:"true"}});W(a.pid,"daemon");let d=process.cwd();w.initLogFile(d),w.enableFileLogging(!0);let m=f.join(d,"xiaozhi.log"),h=u.createWriteStream(m,{flags:"a"});a.stdout?.pipe(h),a.stderr?.pipe(h),a.on("exit",(v,C)=>{v!==0&&v!==null&&w.error(`\u540E\u53F0\u670D\u52A1\u5F02\u5E38\u9000\u51FA (\u4EE3\u7801: ${v}, \u4FE1\u53F7: ${C})`),j()}),a.on("error",v=>{w.error(`\u540E\u53F0\u670D\u52A1\u542F\u52A8\u9519\u8BEF: ${v.message}`),j(),o.fail(`\u540E\u53F0\u670D\u52A1\u542F\u52A8\u5931\u8D25: ${v.message}`)}),a.unref(),o.succeed(`\u670D\u52A1\u5DF2\u5728\u540E\u53F0\u542F\u52A8 (PID: ${a.pid})`),console.log(s.gray(`\u65E5\u5FD7\u6587\u4EF6: ${m}`)),console.log(s.gray("\u4F7F\u7528 'xiaozhi attach' \u53EF\u4EE5\u67E5\u770B\u5B9E\u65F6\u65E5\u5FD7")),e&&await se()}else{o.succeed("\u670D\u52A1\u542F\u52A8\u4E2D...");let a=H(r,i,{cwd:c,stdio:"inherit",env:{...process.env,XIAOZHI_CONFIG_DIR:process.cwd()}});W(a.pid,"foreground"),a.on("exit",(d,m)=>{j(),console.log(d!==0?s.red(`
|
|
12
12
|
\u670D\u52A1\u5F02\u5E38\u9000\u51FA (\u4EE3\u7801: ${d}, \u4FE1\u53F7: ${m})`):s.green(`
|
|
13
13
|
\u670D\u52A1\u5DF2\u505C\u6B62`))}),e&&setTimeout(()=>{se()},1e3),process.on("SIGINT",async()=>{if(console.log(s.yellow(`
|
|
14
14
|
\u6B63\u5728\u505C\u6B62\u670D\u52A1...`)),a.kill("SIGTERM"),global.__webServer)try{await global.__webServer.stop(),console.log(s.green("Web UI \u5DF2\u505C\u6B62"))}catch{}}),process.on("SIGTERM",async()=>{if(a.kill("SIGTERM"),global.__webServer)try{await global.__webServer.stop()}catch{}})}}catch(t){o.fail(`\u542F\u52A8\u670D\u52A1\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`)}}g(le,"startService");async function ge(){let n=b("\u68C0\u67E5\u670D\u52A1\u72B6\u6001...").start();try{let e=x();if(!e.running){n.warn("\u670D\u52A1\u672A\u5728\u8FD0\u884C");return}n.text=`\u505C\u6B62\u670D\u52A1 (PID: ${e.pid})...`;try{process.kill(e.pid,"SIGTERM");let o=0,t=30;for(;o<t;){await new Promise(r=>setTimeout(r,100));try{process.kill(e.pid,0),o++}catch{break}}try{process.kill(e.pid,0),n.text="\u5F3A\u5236\u505C\u6B62\u670D\u52A1...",process.kill(e.pid,"SIGKILL"),await new Promise(r=>setTimeout(r,500))}catch{}j(),n.succeed("\u670D\u52A1\u5DF2\u505C\u6B62")}catch(o){j(),n.fail(`\u505C\u6B62\u670D\u52A1\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`)}}catch(e){n.fail(`\u505C\u6B62\u670D\u52A1\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}g(ge,"stopService");async function Ae(){let n=b("\u68C0\u67E5\u670D\u52A1\u72B6\u6001...").start();try{let e=x();if(e.running){if(n.succeed("\u670D\u52A1\u72B6\u6001"),console.log(s.green("\u2705 \u670D\u52A1\u6B63\u5728\u8FD0\u884C")),console.log(s.gray(` PID: ${e.pid}`)),console.log(s.gray(` \u8FD0\u884C\u65F6\u95F4: ${e.uptime}`)),console.log(s.gray(` \u8FD0\u884C\u6A21\u5F0F: ${e.mode==="daemon"?"\u540E\u53F0\u6A21\u5F0F":"\u524D\u53F0\u6A21\u5F0F"}`)),e.mode==="daemon"){let o=f.join(process.cwd(),"xiaozhi.log");console.log(s.gray(` \u65E5\u5FD7\u6587\u4EF6: ${o}`))}}else n.succeed("\u670D\u52A1\u72B6\u6001"),console.log(s.red("\u274C \u670D\u52A1\u672A\u8FD0\u884C"))}catch(e){n.fail(`\u68C0\u67E5\u72B6\u6001\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}g(Ae,"checkStatus");async function Fe(){let n=b("\u68C0\u67E5\u670D\u52A1\u72B6\u6001...").start();try{let e=x();if(!e.running){n.fail("\u670D\u52A1\u672A\u5728\u8FD0\u884C");return}if(e.mode!=="daemon"){n.fail("\u670D\u52A1\u4E0D\u662F\u5728\u540E\u53F0\u6A21\u5F0F\u8FD0\u884C");return}n.succeed("\u8FDE\u63A5\u5230\u540E\u53F0\u670D\u52A1..."),console.log(s.green(`\u5DF2\u8FDE\u63A5\u5230\u670D\u52A1 (PID: ${e.pid})`)),console.log(s.gray("\u6309 Ctrl+C \u53EF\u4EE5\u65AD\u5F00\u8FDE\u63A5\uFF08\u4E0D\u4F1A\u505C\u6B62\u670D\u52A1\uFF09")),console.log(s.gray("=".repeat(50)));let o=f.join(process.cwd(),"xiaozhi.log");if(u.existsSync(o))if(process.platform==="win32"){let{spawn:t}=await import("child_process"),r=t("powershell",["-Command",`Get-Content -Path "${o}" -Wait`],{stdio:"inherit"});process.on("SIGINT",()=>{console.log(s.yellow(`
|
|
15
15
|
\u65AD\u5F00\u8FDE\u63A5\uFF0C\u670D\u52A1\u7EE7\u7EED\u5728\u540E\u53F0\u8FD0\u884C`)),r.kill(),process.exit(0)}),r.on("exit",()=>{process.exit(0)})}else{let{spawn:t}=await import("child_process"),r=t("tail",["-f",o],{stdio:"inherit"});process.on("SIGINT",()=>{console.log(s.yellow(`
|
|
16
16
|
\u65AD\u5F00\u8FDE\u63A5\uFF0C\u670D\u52A1\u7EE7\u7EED\u5728\u540E\u53F0\u8FD0\u884C`)),r.kill(),process.exit(0)}),r.on("exit",()=>{process.exit(0)})}else console.log(s.yellow("\u65E5\u5FD7\u6587\u4EF6\u4E0D\u5B58\u5728"))}catch(e){n.fail(`\u8FDE\u63A5\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}g(Fe,"attachService");async function Ue(n=!1,e=!1){console.log(s.blue("\u{1F504} \u91CD\u542F\u670D\u52A1...")),await ge(),await new Promise(o=>setTimeout(o,1e3)),await le(n,e)}g(Ue,"restartService");async function De(n,e=!1){let o=b("\u542F\u52A8 MCP Server \u6A21\u5F0F...").start();try{if(!l.configExists()){o.fail("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728"),console.log(s.yellow('\u{1F4A1} \u63D0\u793A: \u8BF7\u5148\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E'));return}let{MCPServer:t}=await Promise.resolve().then(()=>(ie(),re));if(e){let r=P(import.meta.url),i=f.dirname(r),c=H("node",[f.join(i,"cli.js"),"start","--server",n.toString()],{detached:!0,stdio:["ignore","pipe","pipe"],env:{...process.env,XIAOZHI_CONFIG_DIR:process.cwd(),XIAOZHI_DAEMON:"true",MCP_SERVER_MODE:"true"}});W(c.pid,"daemon");let a=f.join(process.cwd(),"xiaozhi-mcp-server.log"),d=u.createWriteStream(a,{flags:"a"});c.stdout?.pipe(d),c.stderr?.pipe(d),c.unref(),o.succeed(`MCP Server \u5DF2\u5728\u540E\u53F0\u542F\u52A8 (PID: ${c.pid}, Port: ${n})`),console.log(s.gray(`\u65E5\u5FD7\u6587\u4EF6: ${a}`))}else{let r=new t(n),i=g(async()=>{console.log(s.yellow(`
|
|
17
|
-
\u6B63\u5728\u505C\u6B62 MCP Server...`)),await r.stop(),process.exit(0)},"cleanup");process.on("SIGINT",i),process.on("SIGTERM",i),await r.start(),o.succeed("MCP Server \u5DF2\u542F\u52A8"),console.log(s.green(`\u2705 SSE endpoint: http://localhost:${n}/sse`)),console.log(s.green(`\u2705 Messages endpoint: http://localhost:${n}/messages`)),console.log(s.green(`\u2705 RPC endpoint: http://localhost:${n}/rpc`)),console.log(s.yellow("\u{1F4A1} \u63D0\u793A: \u6309 Ctrl+C \u505C\u6B62\u670D\u52A1"))}}catch(t){o.fail(`\u542F\u52A8 MCP Server \u5931\u8D25: ${t instanceof Error?t.message:String(t)}`)}}g(De,"startMCPServerMode");function He(){let n=ae();console.log(s.blue(`xiaozhi v${n}`)),console.log(s.gray("MCP Calculator Service CLI Tool")),console.log(s.gray("Built with Node.js and TypeScript")),console.log(s.gray(`Node.js: ${process.version}`)),console.log(s.gray(`Platform: ${process.platform} ${process.arch}`))}g(He,"showDetailedInfo");async function We(){let n=b("\u521D\u59CB\u5316\u914D\u7F6E...").start();try{if(l.configExists()){n.warn("\u914D\u7F6E\u6587\u4EF6\u5DF2\u5B58\u5728"),console.log(s.yellow("\u5982\u9700\u91CD\u65B0\u521D\u59CB\u5316\uFF0C\u8BF7\u5148\u5220\u9664\u73B0\u6709\u7684 xiaozhi.config.json \u6587\u4EF6"));return}l.initConfig(),n.succeed("\u914D\u7F6E\u6587\u4EF6\u521D\u59CB\u5316\u6210\u529F"),console.log(s.green("\u2705 \u914D\u7F6E\u6587\u4EF6\u5DF2\u521B\u5EFA: xiaozhi.config.json")),console.log(s.yellow("\u{1F4DD} \u8BF7\u7F16\u8F91\u914D\u7F6E\u6587\u4EF6\u8BBE\u7F6E\u4F60\u7684 MCP \u7AEF\u70B9:")),console.log(s.gray(` \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84: ${l.getConfigPath()}`)),console.log(s.yellow("\u{1F4A1} \u6216\u8005\u4F7F\u7528\u547D\u4EE4\u8BBE\u7F6E:")),console.log(s.gray(" xiaozhi config mcpEndpoint <your-endpoint-url>"))}catch(e){n.fail(`\u521D\u59CB\u5316\u914D\u7F6E\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}g(We,"initConfig");function _e(){let n=f.dirname(P(import.meta.url)),o=[f.join(n,"..","templates"),f.join(n,"templates"),f.join(n,"..","..","templates")].find(t=>u.existsSync(t));return o?u.readdirSync(o).filter(t=>{let r=f.join(o,t);return u.statSync(r).isDirectory()}):[]}g(_e,"getAvailableTemplates");function ce(n,e){let o=n.length,t=e.length,r=Array(o+1).fill(null).map(()=>Array(t+1).fill(0));for(let c=0;c<=o;c++)r[c][0]=c;for(let c=0;c<=t;c++)r[0][c]=c;for(let c=1;c<=o;c++)for(let a=1;a<=t;a++)n[c-1]===e[a-1]?r[c][a]=r[c-1][a-1]:r[c][a]=Math.min(r[c-1][a]+1,r[c][a-1]+1,r[c-1][a-1]+1);let i=Math.max(o,t);return i===0?1:(i-r[o][t])/i}g(ce,"calculateSimilarity");function Ge(n,e){if(e.length===0)return null;let o=e[0],t=ce(n.toLowerCase(),o.toLowerCase());for(let r of e.slice(1)){let i=ce(n.toLowerCase(),r.toLowerCase());i>t&&(t=i,o=r)}return t>.5?o:null}g(Ge,"findSimilarTemplate");async function Je(n){if(!process.stdin.isTTY)return console.log("n (\u975E\u4EA4\u4E92\u5F0F\u73AF\u5883)"),!1;let e=await import("readline");return new Promise(o=>{process.stdout.write(n);let t=e.createInterface({input:process.stdin,output:process.stdout}),r=g(i=>{let c=i.trim().toLowerCase();c==="y"||c==="yes"?(t.close(),o(!0)):c==="n"||c==="no"||c===""?(t.close(),o(!1)):process.stdout.write("\u8BF7\u8F93\u5165 y \u6216 n: ")},"handleInput");t.on("line",r),t.on("SIGINT",()=>{t.close(),o(!1)})})}g(Je,"askUserConfirmation");function Le(n){let e={mcpEndpoint:"<\u8BF7\u586B\u5199\u4F60\u7684\u63A5\u5165\u70B9\u5730\u5740\uFF08\u83B7\u53D6\u5730\u5740\u5728 xiaozhi.me\uFF09>",mcpServers:{}},o=f.join(n,"xiaozhi.config.json");u.writeFileSync(o,JSON.stringify(e,null,2),"utf8")}g(Le,"createBasicConfig");async function Xe(n,e){let o=b("\u521D\u59CB\u5316\u9879\u76EE...").start();try{let t=f.join(process.cwd(),n);if(u.existsSync(t)){o.fail(`\u76EE\u5F55 "${n}" \u5DF2\u5B58\u5728`),console.log(s.yellow("\u{1F4A1} \u63D0\u793A: \u8BF7\u9009\u62E9\u4E0D\u540C\u7684\u9879\u76EE\u540D\u79F0\u6216\u5220\u9664\u73B0\u6709\u76EE\u5F55"));return}if(e.template){o.text="\u68C0\u67E5\u6A21\u677F...";let r=_e();if(r.length===0){o.fail("\u627E\u4E0D\u5230 templates \u76EE\u5F55"),console.log(s.yellow("\u{1F4A1} \u63D0\u793A: \u8BF7\u786E\u4FDD xiaozhi-client \u6B63\u786E\u5B89\u88C5"));return}if(!r.includes(e.template)){o.fail(`\u6A21\u677F "${e.template}" \u4E0D\u5B58\u5728`);let h=Ge(e.template,r);if(h)if(console.log(s.yellow(`\u{1F4A1} \u4F60\u662F\u60F3\u4F7F\u7528\u6A21\u677F "${h}" \u5417\uFF1F`)),await Je(s.cyan("\u786E\u8BA4\u4F7F\u7528\u6B64\u6A21\u677F\uFF1F(y/n): ")))e.template=h;else{console.log(s.yellow("\u53EF\u7528\u7684\u6A21\u677F:"));for(let
|
|
17
|
+
\u6B63\u5728\u505C\u6B62 MCP Server...`)),await r.stop(),process.exit(0)},"cleanup");process.on("SIGINT",i),process.on("SIGTERM",i),await r.start(),o.succeed("MCP Server \u5DF2\u542F\u52A8"),console.log(s.green(`\u2705 SSE endpoint: http://localhost:${n}/sse`)),console.log(s.green(`\u2705 Messages endpoint: http://localhost:${n}/messages`)),console.log(s.green(`\u2705 RPC endpoint: http://localhost:${n}/rpc`)),console.log(s.yellow("\u{1F4A1} \u63D0\u793A: \u6309 Ctrl+C \u505C\u6B62\u670D\u52A1"))}}catch(t){o.fail(`\u542F\u52A8 MCP Server \u5931\u8D25: ${t instanceof Error?t.message:String(t)}`)}}g(De,"startMCPServerMode");function He(){let n=ae();console.log(s.blue(`xiaozhi v${n}`)),console.log(s.gray("MCP Calculator Service CLI Tool")),console.log(s.gray("Built with Node.js and TypeScript")),console.log(s.gray(`Node.js: ${process.version}`)),console.log(s.gray(`Platform: ${process.platform} ${process.arch}`))}g(He,"showDetailedInfo");async function We(){let n=b("\u521D\u59CB\u5316\u914D\u7F6E...").start();try{if(l.configExists()){n.warn("\u914D\u7F6E\u6587\u4EF6\u5DF2\u5B58\u5728"),console.log(s.yellow("\u5982\u9700\u91CD\u65B0\u521D\u59CB\u5316\uFF0C\u8BF7\u5148\u5220\u9664\u73B0\u6709\u7684 xiaozhi.config.json \u6587\u4EF6"));return}l.initConfig(),n.succeed("\u914D\u7F6E\u6587\u4EF6\u521D\u59CB\u5316\u6210\u529F"),console.log(s.green("\u2705 \u914D\u7F6E\u6587\u4EF6\u5DF2\u521B\u5EFA: xiaozhi.config.json")),console.log(s.yellow("\u{1F4DD} \u8BF7\u7F16\u8F91\u914D\u7F6E\u6587\u4EF6\u8BBE\u7F6E\u4F60\u7684 MCP \u7AEF\u70B9:")),console.log(s.gray(` \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84: ${l.getConfigPath()}`)),console.log(s.yellow("\u{1F4A1} \u6216\u8005\u4F7F\u7528\u547D\u4EE4\u8BBE\u7F6E:")),console.log(s.gray(" xiaozhi config mcpEndpoint <your-endpoint-url>"))}catch(e){n.fail(`\u521D\u59CB\u5316\u914D\u7F6E\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}g(We,"initConfig");function _e(){let n=f.dirname(P(import.meta.url)),o=[f.join(n,"..","templates"),f.join(n,"templates"),f.join(n,"..","..","templates")].find(t=>u.existsSync(t));return o?u.readdirSync(o).filter(t=>{let r=f.join(o,t);return u.statSync(r).isDirectory()}):[]}g(_e,"getAvailableTemplates");function ce(n,e){let o=n.length,t=e.length,r=Array(o+1).fill(null).map(()=>Array(t+1).fill(0));for(let c=0;c<=o;c++)r[c][0]=c;for(let c=0;c<=t;c++)r[0][c]=c;for(let c=1;c<=o;c++)for(let a=1;a<=t;a++)n[c-1]===e[a-1]?r[c][a]=r[c-1][a-1]:r[c][a]=Math.min(r[c-1][a]+1,r[c][a-1]+1,r[c-1][a-1]+1);let i=Math.max(o,t);return i===0?1:(i-r[o][t])/i}g(ce,"calculateSimilarity");function Ge(n,e){if(e.length===0)return null;let o=e[0],t=ce(n.toLowerCase(),o.toLowerCase());for(let r of e.slice(1)){let i=ce(n.toLowerCase(),r.toLowerCase());i>t&&(t=i,o=r)}return t>.5?o:null}g(Ge,"findSimilarTemplate");async function Je(n){if(!process.stdin.isTTY)return console.log("n (\u975E\u4EA4\u4E92\u5F0F\u73AF\u5883)"),!1;let e=await import("readline");return new Promise(o=>{process.stdout.write(n);let t=e.createInterface({input:process.stdin,output:process.stdout}),r=g(i=>{let c=i.trim().toLowerCase();c==="y"||c==="yes"?(t.close(),o(!0)):c==="n"||c==="no"||c===""?(t.close(),o(!1)):process.stdout.write("\u8BF7\u8F93\u5165 y \u6216 n: ")},"handleInput");t.on("line",r),t.on("SIGINT",()=>{t.close(),o(!1)})})}g(Je,"askUserConfirmation");function Le(n){let e={mcpEndpoint:"<\u8BF7\u586B\u5199\u4F60\u7684\u63A5\u5165\u70B9\u5730\u5740\uFF08\u83B7\u53D6\u5730\u5740\u5728 xiaozhi.me\uFF09>",mcpServers:{}},o=f.join(n,"xiaozhi.config.json");u.writeFileSync(o,JSON.stringify(e,null,2),"utf8")}g(Le,"createBasicConfig");async function Xe(n,e){let o=b("\u521D\u59CB\u5316\u9879\u76EE...").start();try{let t=f.join(process.cwd(),n);if(u.existsSync(t)){o.fail(`\u76EE\u5F55 "${n}" \u5DF2\u5B58\u5728`),console.log(s.yellow("\u{1F4A1} \u63D0\u793A: \u8BF7\u9009\u62E9\u4E0D\u540C\u7684\u9879\u76EE\u540D\u79F0\u6216\u5220\u9664\u73B0\u6709\u76EE\u5F55"));return}if(e.template){o.text="\u68C0\u67E5\u6A21\u677F...";let r=_e();if(r.length===0){o.fail("\u627E\u4E0D\u5230 templates \u76EE\u5F55"),console.log(s.yellow("\u{1F4A1} \u63D0\u793A: \u8BF7\u786E\u4FDD xiaozhi-client \u6B63\u786E\u5B89\u88C5"));return}if(!r.includes(e.template)){o.fail(`\u6A21\u677F "${e.template}" \u4E0D\u5B58\u5728`);let h=Ge(e.template,r);if(h)if(console.log(s.yellow(`\u{1F4A1} \u4F60\u662F\u60F3\u4F7F\u7528\u6A21\u677F "${h}" \u5417\uFF1F`)),await Je(s.cyan("\u786E\u8BA4\u4F7F\u7528\u6B64\u6A21\u677F\uFF1F(y/n): ")))e.template=h;else{console.log(s.yellow("\u53EF\u7528\u7684\u6A21\u677F:"));for(let C of r)console.log(s.gray(` - ${C}`));return}else{console.log(s.yellow("\u53EF\u7528\u7684\u6A21\u677F:"));for(let v of r)console.log(s.gray(` - ${v}`));return}}let i=f.dirname(P(import.meta.url)),a=[f.join(i,"..","templates"),f.join(i,"templates"),f.join(i,"..","..","templates")].find(h=>u.existsSync(h)),d=f.join(a,e.template);o.text=`\u4ECE\u6A21\u677F "${e.template}" \u521B\u5EFA\u9879\u76EE "${n}"...`,pe(d,t,["node_modules",".pnpm-debug.log","pnpm-lock.yaml"]);let m=f.join(t,"xiaozhi.log");u.existsSync(m)||u.writeFileSync(m,"","utf8"),o.succeed(`\u9879\u76EE "${n}" \u521B\u5EFA\u6210\u529F`),console.log(s.green("\u2705 \u9879\u76EE\u521B\u5EFA\u5B8C\u6210!")),console.log(s.yellow("\u{1F4DD} \u63A5\u4E0B\u6765\u7684\u6B65\u9AA4:")),console.log(s.gray(` cd ${n}`)),console.log(s.gray(" pnpm install # \u5B89\u88C5\u4F9D\u8D56")),console.log(s.gray(" # \u7F16\u8F91 xiaozhi.config.json \u8BBE\u7F6E\u4F60\u7684 MCP \u7AEF\u70B9")),console.log(s.gray(" xiaozhi start # \u542F\u52A8\u670D\u52A1"))}else{o.text=`\u521B\u5EFA\u57FA\u672C\u9879\u76EE "${n}"...`,u.mkdirSync(t,{recursive:!0}),Le(t);let r=f.join(t,"xiaozhi.log");u.writeFileSync(r,"","utf8"),o.succeed(`\u9879\u76EE "${n}" \u521B\u5EFA\u6210\u529F`),console.log(s.green("\u2705 \u57FA\u672C\u9879\u76EE\u521B\u5EFA\u5B8C\u6210!")),console.log(s.yellow("\u{1F4DD} \u63A5\u4E0B\u6765\u7684\u6B65\u9AA4:")),console.log(s.gray(` cd ${n}`)),console.log(s.gray(" # \u7F16\u8F91 xiaozhi.config.json \u8BBE\u7F6E\u4F60\u7684 MCP \u7AEF\u70B9\u548C\u670D\u52A1")),console.log(s.gray(" xiaozhi start # \u542F\u52A8\u670D\u52A1")),console.log(s.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 --template \u9009\u9879\u53EF\u4EE5\u4ECE\u6A21\u677F\u521B\u5EFA\u9879\u76EE"))}}catch(t){o.fail(`\u521B\u5EFA\u9879\u76EE\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`)}}g(Xe,"createProject");function pe(n,e,o=[]){u.existsSync(e)||u.mkdirSync(e,{recursive:!0});let t=u.readdirSync(n);for(let r of t){if(o.some(d=>r.includes(d)))continue;let i=f.join(n,r),c=f.join(e,r);u.statSync(i).isDirectory()?pe(i,c,o):u.copyFileSync(i,c)}}g(pe,"copyDirectory");async function Ze(){let n=b("\u542F\u52A8 UI \u670D\u52A1...").start();try{if(!l.configExists()){n.fail("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728"),console.log(s.yellow('\u{1F4A1} \u63D0\u793A: \u8BF7\u5148\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E'));return}let e=new O;await e.start(),n.succeed("UI \u670D\u52A1\u5DF2\u542F\u52A8");let o=l.getWebUIPort();console.log(s.green(`\u2705 \u914D\u7F6E\u7BA1\u7406\u7F51\u9875\u5DF2\u542F\u52A8: http://localhost:${o}`)),console.log(s.yellow("\u{1F4A1} \u63D0\u793A: \u6309 Ctrl+C \u505C\u6B62\u670D\u52A1"));let{spawn:t}=await import("child_process"),r=`http://localhost:${o}`;try{process.platform==="darwin"?t("open",[r],{detached:!0,stdio:"ignore"}).unref():process.platform==="win32"?t("cmd",["/c","start",r],{detached:!0,stdio:"ignore"}).unref():t("xdg-open",[r],{detached:!0,stdio:"ignore"}).unref()}catch{}let i=!1;process.on("SIGINT",async()=>{i&&(console.log(s.red(`
|
|
18
18
|
\u5F3A\u5236\u9000\u51FA...`)),process.exit(1)),i=!0,console.log(s.yellow(`
|
|
19
|
-
\u6B63\u5728\u505C\u6B62 UI \u670D\u52A1...`));try{await e.stop(),console.log(s.green("UI \u670D\u52A1\u5DF2\u505C\u6B62"))}catch{console.log(s.red("\u505C\u6B62\u670D\u52A1\u65F6\u51FA\u9519\uFF0C\u5F3A\u5236\u9000\u51FA"))}process.exit(0)}),process.on("SIGTERM",async()=>{i=!0,await e.stop(),process.exit(0)})}catch(e){n.fail(`\u542F\u52A8 UI \u670D\u52A1\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}g(Ze,"startUIService");async function Be(n,e){let o=b("\u66F4\u65B0\u914D\u7F6E...").start();try{if(!l.configExists()){o.fail("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728"),console.log(s.yellow('\u{1F4A1} \u63D0\u793A: \u8BF7\u5148\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E'));return}if(e)switch(n){case"mcpEndpoint":l.updateMcpEndpoint(e),o.succeed(`MCP \u7AEF\u70B9\u5DF2\u66F4\u65B0\u4E3A: ${e}`);break;case"heartbeatInterval":{let t=Number.parseInt(e,10);if(Number.isNaN(t)||t<=0){o.fail("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u662F\u5927\u4E8E0\u7684\u6570\u5B57\uFF08\u6BEB\u79D2\uFF09");return}l.setHeartbeatInterval(t),o.succeed(`\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5DF2\u66F4\u65B0\u4E3A: ${t}ms`);break}case"heartbeatTimeout":{let t=Number.parseInt(e,10);if(Number.isNaN(t)||t<=0){o.fail("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u662F\u5927\u4E8E0\u7684\u6570\u5B57\uFF08\u6BEB\u79D2\uFF09");return}l.setHeartbeatTimeout(t),o.succeed(`\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5DF2\u66F4\u65B0\u4E3A: ${t}ms`);break}case"reconnectInterval":{let t=Number.parseInt(e,10);if(Number.isNaN(t)||t<=0){o.fail("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u662F\u5927\u4E8E0\u7684\u6570\u5B57\uFF08\u6BEB\u79D2\uFF09");return}l.setReconnectInterval(t),o.succeed(`\u91CD\u8FDE\u95F4\u9694\u5DF2\u66F4\u65B0\u4E3A: ${t}ms`);break}default:o.fail(`\u914D\u7F6E\u9879 ${n} \u4E0D\u652F\u6301\u901A\u8FC7\u547D\u4EE4\u884C\u8BBE\u7F6E`),console.log(s.yellow("\u652F\u6301\u8BBE\u7F6E\u7684\u914D\u7F6E\u9879: mcpEndpoint, heartbeatInterval, heartbeatTimeout, reconnectInterval"));return}else{o.text="\u8BFB\u53D6\u914D\u7F6E...";let t=l.getConfig();switch(n){case"mcpEndpoint":{o.succeed("\u914D\u7F6E\u4FE1\u606F");let r=l.getMcpEndpoints();r.length===0?console.log(s.yellow("\u672A\u914D\u7F6E\u4EFB\u4F55 MCP \u7AEF\u70B9")):r.length===1?console.log(s.green(`MCP \u7AEF\u70B9: ${r[0]}`)):(console.log(s.green(`MCP \u7AEF\u70B9 (${r.length} \u4E2A):`)),r.forEach((i,c)=>{console.log(s.gray(` ${c+1}. ${i}`))}));break}case"mcpServers":o.succeed("\u914D\u7F6E\u4FE1\u606F"),console.log(s.green("MCP \u670D\u52A1:"));for(let[r,i]of Object.entries(t.mcpServers))"type"in i&&i.type==="sse"?console.log(s.gray(` ${r}: [SSE] ${i.url}`)):console.log(s.gray(` ${r}: ${i.command} ${i.args.join(" ")}`));break;case"connection":{o.succeed("\u914D\u7F6E\u4FE1\u606F");let r=l.getConnectionConfig();console.log(s.green("\u8FDE\u63A5\u914D\u7F6E:")),console.log(s.gray(` \u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694: ${r.heartbeatInterval}ms`)),console.log(s.gray(` \u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4: ${r.heartbeatTimeout}ms`)),console.log(s.gray(` \u91CD\u8FDE\u95F4\u9694: ${r.reconnectInterval}ms`));break}case"heartbeatInterval":o.succeed("\u914D\u7F6E\u4FE1\u606F"),console.log(s.green(`\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694: ${l.getHeartbeatInterval()}ms`));break;case"heartbeatTimeout":o.succeed("\u914D\u7F6E\u4FE1\u606F"),console.log(s.green(`\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4: ${l.getHeartbeatTimeout()}ms`));break;case"reconnectInterval":o.succeed("\u914D\u7F6E\u4FE1\u606F"),console.log(s.green(`\u91CD\u8FDE\u95F4\u9694: ${l.getReconnectInterval()}ms`));break;default:o.fail(`\u672A\u77E5\u7684\u914D\u7F6E\u9879: ${n}`),console.log(s.yellow("\u652F\u6301\u7684\u914D\u7F6E\u9879: mcpEndpoint, mcpServers, connection, heartbeatInterval, heartbeatTimeout, reconnectInterval"));return}}}catch(t){o.fail(`\u914D\u7F6E\u64CD\u4F5C\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`)}}g(Be,"configCommand");function Ve(){console.log(s.blue.bold("xiaozhi - MCP Calculator Service CLI")),console.log(),console.log(s.yellow("\u4F7F\u7528\u65B9\u6CD5:")),console.log(" xiaozhi <command> [options]"),console.log(),console.log(s.yellow("\u547D\u4EE4:")),console.log(" create <projectName> \u521B\u5EFA\u9879\u76EE"),console.log(" init \u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6"),console.log(" config <key> [value] \u67E5\u770B\u6216\u8BBE\u7F6E\u914D\u7F6E"),console.log(" start [--daemon] [--ui] [--server] \u542F\u52A8\u670D\u52A1 (--daemon \u540E\u53F0\u8FD0\u884C, --ui \u540C\u65F6\u542F\u52A8 Web UI, --server MCP Server \u6A21\u5F0F)"),console.log(" stop \u505C\u6B62\u670D\u52A1"),console.log(" status \u68C0\u67E5\u670D\u52A1\u72B6\u6001"),console.log(" attach \u8FDE\u63A5\u5230\u540E\u53F0\u670D\u52A1\u67E5\u770B\u65E5\u5FD7"),console.log(" restart [--daemon] [--ui] \u91CD\u542F\u670D\u52A1 (--daemon \u540E\u53F0\u8FD0\u884C, --ui \u540C\u65F6\u542F\u52A8 Web UI)"),console.log(" ui \u542F\u52A8\u914D\u7F6E\u7BA1\u7406\u7F51\u9875"),console.log(" completion \u663E\u793A\u81EA\u52A8\u8865\u5168\u8BBE\u7F6E\u8BF4\u660E"),console.log(),console.log(s.yellow("\u9009\u9879:")),console.log(" -v, --version \u663E\u793A\u7248\u672C\u4FE1\u606F"),console.log(" -V \u663E\u793A\u8BE6\u7EC6\u4FE1\u606F"),console.log(" -h, --help \u663E\u793A\u5E2E\u52A9\u4FE1\u606F"),console.log(" -t, --template <name> \u6307\u5B9A\u6A21\u677F\u540D\u79F0\uFF08\u7528\u4E8E create \u547D\u4EE4\uFF09"),console.log(),console.log(s.yellow("\u9879\u76EE\u793A\u4F8B:")),console.log(" xiaozhi create my-app # \u521B\u5EFA\u57FA\u672C\u9879\u76EE"),console.log(" xiaozhi create my-app -t hello-world # \u4F7F\u7528 hello-world \u6A21\u677F"),console.log(" xiaozhi create my-app --template hello-world # \u540C\u4E0A\uFF0C\u5B8C\u6574\u9009\u9879\u540D"),console.log(),console.log(s.yellow("\u914D\u7F6E\u793A\u4F8B:")),console.log(" xiaozhi init # \u521D\u59CB\u5316\u914D\u7F6E"),console.log(" xiaozhi config mcpEndpoint # \u67E5\u770B MCP \u7AEF\u70B9"),console.log(" xiaozhi config mcpEndpoint wss://... # \u8BBE\u7F6E MCP \u7AEF\u70B9"),console.log(),console.log(s.yellow("\u670D\u52A1\u793A\u4F8B:")),console.log(" xiaozhi start # \u524D\u53F0\u542F\u52A8\u670D\u52A1"),console.log(" xiaozhi start --daemon # \u540E\u53F0\u542F\u52A8\u670D\u52A1"),console.log(" xiaozhi start --ui # \u542F\u52A8\u670D\u52A1\u5E76\u540C\u65F6\u542F\u52A8 Web UI"),console.log(" xiaozhi start -d -u # \u540E\u53F0\u542F\u52A8\u670D\u52A1\u5E76\u540C\u65F6\u542F\u52A8 Web UI"),console.log(" xiaozhi start --server # \u4EE5 MCP Server \u6A21\u5F0F\u542F\u52A8 (\u7AEF\u53E3 3000)"),console.log(" xiaozhi start -s 8080 # \u4EE5 MCP Server \u6A21\u5F0F\u542F\u52A8 (\u7AEF\u53E3 8080)"),console.log(" xiaozhi start -s -d # \u540E\u53F0\u8FD0\u884C MCP Server"),console.log(" xiaozhi status # \u68C0\u67E5\u670D\u52A1\u72B6\u6001"),console.log(" xiaozhi attach # \u67E5\u770B\u540E\u53F0\u670D\u52A1\u65E5\u5FD7"),console.log(" xiaozhi stop # \u505C\u6B62\u670D\u52A1"),console.log(),console.log(s.yellow("MCP \u7BA1\u7406\u793A\u4F8B:")),console.log(" xiaozhi mcp list # \u5217\u51FA\u6240\u6709 MCP \u670D\u52A1"),console.log(" xiaozhi mcp list --tools # \u5217\u51FA\u6240\u6709\u670D\u52A1\u7684\u5DE5\u5177"),console.log(" xiaozhi mcp server <name> # \u5217\u51FA\u6307\u5B9A\u670D\u52A1\u7684\u5DE5\u5177"),console.log(" xiaozhi mcp tool <server> <tool> enable # \u542F\u7528\u5DE5\u5177"),console.log(" xiaozhi mcp tool <server> <tool> disable # \u7981\u7528\u5DE5\u5177"),console.log(),console.log(s.yellow("\u81EA\u52A8\u8865\u5168:")),console.log(" xiaozhi completion # \u663E\u793A\u81EA\u52A8\u8865\u5168\u8BBE\u7F6E\u8BF4\u660E"),console.log(" # \u8BBE\u7F6E\u540E\u53EF\u4F7F\u7528 Tab \u952E\u8FDB\u884C\u547D\u4EE4\u3001\u53C2\u6570\u81EA\u52A8\u8865\u5168")}g(Ve,"showHelp");S.name("xiaozhi").description("MCP Calculator Service CLI Tool").version(ae(),"-v, --version","\u663E\u793A\u7248\u672C\u4FE1\u606F").helpOption("-h, --help","\u663E\u793A\u5E2E\u52A9\u4FE1\u606F");S.command("create <projectName>").description("\u521B\u5EFA\u9879\u76EE").option("-t, --template <templateName>","\u4F7F\u7528\u6307\u5B9A\u6A21\u677F\u521B\u5EFA\u9879\u76EE").action(async(n,e)=>{await Xe(n,e)});S.command("init").description("\u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6").action(async()=>{await We()});S.command("config <key> [value]").description("\u67E5\u770B\u6216\u8BBE\u7F6E\u914D\u7F6E").action(async(n,e)=>{await Be(n,e)});S.command("start").description("\u542F\u52A8\u670D\u52A1").option("-d, --daemon","\u5728\u540E\u53F0\u8FD0\u884C\u670D\u52A1").option("-u, --ui","\u540C\u65F6\u542F\u52A8 Web UI \u670D\u52A1").option("-s, --server [port]","\u4EE5 MCP Server \u6A21\u5F0F\u542F\u52A8 (\u53EF\u9009\u6307\u5B9A\u7AEF\u53E3\uFF0C\u9ED8\u8BA4 3000)").option("--stdio","\u4EE5 stdio \u6A21\u5F0F\u8FD0\u884C MCP Server (\u7528\u4E8E Cursor \u7B49\u5BA2\u6237\u7AEF)").action(async n=>{if(n.stdio){let{spawn:e}=await import("child_process"),o=P(import.meta.url),t=f.dirname(o),r=f.join(t,"mcpServerProxy.js");e("node",[r],{stdio:"inherit",env:{...process.env,XIAOZHI_CONFIG_DIR:process.env.XIAOZHI_CONFIG_DIR||process.cwd()}})}else if(n.server){let e=typeof n.server=="string"?Number.parseInt(n.server):3e3;await De(e,n.daemon)}else await le(n.daemon,n.ui)});S.command("stop").description("\u505C\u6B62\u670D\u52A1").action(async()=>{await ge()});S.command("status").description("\u68C0\u67E5\u670D\u52A1\u72B6\u6001").action(async()=>{await Ae()});S.command("attach").description("\u8FDE\u63A5\u5230\u540E\u53F0\u670D\u52A1\u67E5\u770B\u65E5\u5FD7").action(async()=>{await Fe()});S.command("restart").description("\u91CD\u542F\u670D\u52A1").option("-d, --daemon","\u5728\u540E\u53F0\u8FD0\u884C\u670D\u52A1").option("-u, --ui","\u540C\u65F6\u542F\u52A8 Web UI \u670D\u52A1").action(async n=>{await Ue(n.daemon,n.ui)});var G=S.command("mcp").description("MCP \u670D\u52A1\u548C\u5DE5\u5177\u7BA1\u7406");G.command("list").description("\u5217\u51FA MCP \u670D\u52A1").option("--tools","\u663E\u793A\u6240\u6709\u670D\u52A1\u7684\u5DE5\u5177\u5217\u8868").action(async n=>{await ee(n)});G.command("server <serverName>").description("\u7BA1\u7406\u6307\u5B9A\u7684 MCP \u670D\u52A1").action(async n=>{await oe(n)});G.command("tool <serverName> <toolName> <action>").description("\u542F\u7528\u6216\u7981\u7528\u6307\u5B9A\u670D\u52A1\u7684\u5DE5\u5177").action(async(n,e,o)=>{o!=="enable"&&o!=="disable"&&(console.error(s.red("\u9519\u8BEF: \u64CD\u4F5C\u5FC5\u987B\u662F 'enable' \u6216 'disable'")),process.exit(1)),await te(n,e,o==="enable")});var N=S.command("endpoint").description("\u7BA1\u7406 MCP \u7AEF\u70B9");N.command("list").description("\u5217\u51FA\u6240\u6709 MCP \u7AEF\u70B9").action(async()=>{let n=b("\u8BFB\u53D6\u7AEF\u70B9\u914D\u7F6E...").start();try{let e=l.getMcpEndpoints();n.succeed("\u7AEF\u70B9\u5217\u8868"),e.length===0?console.log(s.yellow("\u672A\u914D\u7F6E\u4EFB\u4F55 MCP \u7AEF\u70B9")):(console.log(s.green(`\u5171 ${e.length} \u4E2A\u7AEF\u70B9:`)),e.forEach((o,t)=>{console.log(s.gray(` ${t+1}. ${o}`))}))}catch(e){n.fail(`\u8BFB\u53D6\u7AEF\u70B9\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}});N.command("add <url>").description("\u6DFB\u52A0\u65B0\u7684 MCP \u7AEF\u70B9").action(async n=>{let e=b("\u6DFB\u52A0\u7AEF\u70B9...").start();try{l.addMcpEndpoint(n),e.succeed(`\u6210\u529F\u6DFB\u52A0\u7AEF\u70B9: ${n}`);let o=l.getMcpEndpoints();console.log(s.gray(`\u5F53\u524D\u5171 ${o.length} \u4E2A\u7AEF\u70B9`))}catch(o){e.fail(`\u6DFB\u52A0\u7AEF\u70B9\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`)}});N.command("remove <url>").description("\u79FB\u9664\u6307\u5B9A\u7684 MCP \u7AEF\u70B9").action(async n=>{let e=b("\u79FB\u9664\u7AEF\u70B9...").start();try{l.removeMcpEndpoint(n),e.succeed(`\u6210\u529F\u79FB\u9664\u7AEF\u70B9: ${n}`);let o=l.getMcpEndpoints();console.log(s.gray(`\u5F53\u524D\u5269\u4F59 ${o.length} \u4E2A\u7AEF\u70B9`))}catch(o){e.fail(`\u79FB\u9664\u7AEF\u70B9\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`)}});N.command("set <urls...>").description("\u8BBE\u7F6E MCP \u7AEF\u70B9\uFF08\u53EF\u4EE5\u662F\u5355\u4E2A\u6216\u591A\u4E2A\uFF09").action(async n=>{let e=b("\u8BBE\u7F6E\u7AEF\u70B9...").start();try{if(n.length===1)l.updateMcpEndpoint(n[0]),e.succeed(`\u6210\u529F\u8BBE\u7F6E\u7AEF\u70B9: ${n[0]}`);else{l.updateMcpEndpoint(n),e.succeed(`\u6210\u529F\u8BBE\u7F6E ${n.length} \u4E2A\u7AEF\u70B9`);for(let[o,t]of n.entries())console.log(s.gray(` ${o+1}. ${t}`))}}catch(o){e.fail(`\u8BBE\u7F6E\u7AEF\u70B9\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`)}});S.command("ui").description("\u542F\u52A8\u914D\u7F6E\u7BA1\u7406\u7F51\u9875").action(async()=>{await Ze()});S.command("completion").description("\u663E\u793A\u81EA\u52A8\u8865\u5168\u8BBE\u7F6E\u8BF4\u660E").action(async()=>{q()});S.option("-V","\u663E\u793A\u8BE6\u7EC6\u4FE1\u606F").action(n=>{n.V&&(He(),process.exit(0))});V();process.argv.length<=2&&(Ve(),process.exit(0));S.parse(process.argv);z();var O=class{static{g(this,"WebServer")}httpServer;wss;logger;port;clientInfo={status:"disconnected",mcpEndpoint:"",activeMCPServers:[]};heartbeatTimeout;HEARTBEAT_TIMEOUT=35e3;constructor(e){if(e===void 0)try{this.port=l.getWebUIPort()}catch{this.port=9999}else this.port=e;this.logger=new $,this.httpServer=qe((o,t)=>{this.handleHttpRequest(o,t)}),this.wss=new eo({server:this.httpServer}),this.setupWebSocket()}async handleHttpRequest(e,o){let{pathname:t}=Ye(e.url||"",!0);if(o.setHeader("Access-Control-Allow-Origin","*"),o.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, OPTIONS"),o.setHeader("Access-Control-Allow-Headers","Content-Type"),e.method==="OPTIONS"){o.writeHead(200),o.end();return}try{if(e.method==="GET"&&!t?.startsWith("/api/")){await this.serveStaticFile(t||"/",o);return}if(t==="/api/config"&&e.method==="GET"){let r=l.getConfig();o.writeHead(200,{"Content-Type":"application/json"}),o.end(JSON.stringify(r))}else if(t==="/api/config"&&e.method==="PUT"){let r="";e.on("data",i=>{r+=i.toString()}),e.on("end",async()=>{try{let i=JSON.parse(r);this.updateConfig(i),this.broadcastConfigUpdate(i),o.writeHead(200,{"Content-Type":"application/json"}),o.end(JSON.stringify({success:!0})),this.logger.info("\u914D\u7F6E\u5DF2\u66F4\u65B0")}catch(i){o.writeHead(400,{"Content-Type":"application/json"}),o.end(JSON.stringify({error:i instanceof Error?i.message:String(i)}))}})}else t==="/api/status"&&e.method==="GET"?(o.writeHead(200,{"Content-Type":"application/json"}),o.end(JSON.stringify(this.clientInfo))):(o.writeHead(404),o.end("Not Found"))}catch(r){this.logger.error("HTTP request error:",r),o.writeHead(500,{"Content-Type":"application/json"}),o.end(JSON.stringify({error:"Internal Server Error"}))}}async serveStaticFile(e,o){try{let t=Ke(Qe(import.meta.url)),i=[I(t,"..","web","dist"),I(t,"..","web"),I(process.cwd(),"web","dist"),I(process.cwd(),"web")].find(v=>J(v));if(!i){o.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),o.end(`
|
|
19
|
+
\u6B63\u5728\u505C\u6B62 UI \u670D\u52A1...`));try{await e.stop(),console.log(s.green("UI \u670D\u52A1\u5DF2\u505C\u6B62"))}catch{console.log(s.red("\u505C\u6B62\u670D\u52A1\u65F6\u51FA\u9519\uFF0C\u5F3A\u5236\u9000\u51FA"))}process.exit(0)}),process.on("SIGTERM",async()=>{i=!0,await e.stop(),process.exit(0)})}catch(e){n.fail(`\u542F\u52A8 UI \u670D\u52A1\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}g(Ze,"startUIService");async function Be(n,e){let o=b("\u66F4\u65B0\u914D\u7F6E...").start();try{if(!l.configExists()){o.fail("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728"),console.log(s.yellow('\u{1F4A1} \u63D0\u793A: \u8BF7\u5148\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E'));return}if(e)switch(n){case"mcpEndpoint":l.updateMcpEndpoint(e),o.succeed(`MCP \u7AEF\u70B9\u5DF2\u66F4\u65B0\u4E3A: ${e}`);break;case"heartbeatInterval":{let t=Number.parseInt(e,10);if(Number.isNaN(t)||t<=0){o.fail("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u662F\u5927\u4E8E0\u7684\u6570\u5B57\uFF08\u6BEB\u79D2\uFF09");return}l.setHeartbeatInterval(t),o.succeed(`\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5DF2\u66F4\u65B0\u4E3A: ${t}ms`);break}case"heartbeatTimeout":{let t=Number.parseInt(e,10);if(Number.isNaN(t)||t<=0){o.fail("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u662F\u5927\u4E8E0\u7684\u6570\u5B57\uFF08\u6BEB\u79D2\uFF09");return}l.setHeartbeatTimeout(t),o.succeed(`\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5DF2\u66F4\u65B0\u4E3A: ${t}ms`);break}case"reconnectInterval":{let t=Number.parseInt(e,10);if(Number.isNaN(t)||t<=0){o.fail("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u662F\u5927\u4E8E0\u7684\u6570\u5B57\uFF08\u6BEB\u79D2\uFF09");return}l.setReconnectInterval(t),o.succeed(`\u91CD\u8FDE\u95F4\u9694\u5DF2\u66F4\u65B0\u4E3A: ${t}ms`);break}default:o.fail(`\u914D\u7F6E\u9879 ${n} \u4E0D\u652F\u6301\u901A\u8FC7\u547D\u4EE4\u884C\u8BBE\u7F6E`),console.log(s.yellow("\u652F\u6301\u8BBE\u7F6E\u7684\u914D\u7F6E\u9879: mcpEndpoint, heartbeatInterval, heartbeatTimeout, reconnectInterval"));return}else{o.text="\u8BFB\u53D6\u914D\u7F6E...";let t=l.getConfig();switch(n){case"mcpEndpoint":{o.succeed("\u914D\u7F6E\u4FE1\u606F");let r=l.getMcpEndpoints();r.length===0?console.log(s.yellow("\u672A\u914D\u7F6E\u4EFB\u4F55 MCP \u7AEF\u70B9")):r.length===1?console.log(s.green(`MCP \u7AEF\u70B9: ${r[0]}`)):(console.log(s.green(`MCP \u7AEF\u70B9 (${r.length} \u4E2A):`)),r.forEach((i,c)=>{console.log(s.gray(` ${c+1}. ${i}`))}));break}case"mcpServers":o.succeed("\u914D\u7F6E\u4FE1\u606F"),console.log(s.green("MCP \u670D\u52A1:"));for(let[r,i]of Object.entries(t.mcpServers))"type"in i&&i.type==="sse"?console.log(s.gray(` ${r}: [SSE] ${i.url}`)):console.log(s.gray(` ${r}: ${i.command} ${i.args.join(" ")}`));break;case"connection":{o.succeed("\u914D\u7F6E\u4FE1\u606F");let r=l.getConnectionConfig();console.log(s.green("\u8FDE\u63A5\u914D\u7F6E:")),console.log(s.gray(` \u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694: ${r.heartbeatInterval}ms`)),console.log(s.gray(` \u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4: ${r.heartbeatTimeout}ms`)),console.log(s.gray(` \u91CD\u8FDE\u95F4\u9694: ${r.reconnectInterval}ms`));break}case"heartbeatInterval":o.succeed("\u914D\u7F6E\u4FE1\u606F"),console.log(s.green(`\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694: ${l.getHeartbeatInterval()}ms`));break;case"heartbeatTimeout":o.succeed("\u914D\u7F6E\u4FE1\u606F"),console.log(s.green(`\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4: ${l.getHeartbeatTimeout()}ms`));break;case"reconnectInterval":o.succeed("\u914D\u7F6E\u4FE1\u606F"),console.log(s.green(`\u91CD\u8FDE\u95F4\u9694: ${l.getReconnectInterval()}ms`));break;default:o.fail(`\u672A\u77E5\u7684\u914D\u7F6E\u9879: ${n}`),console.log(s.yellow("\u652F\u6301\u7684\u914D\u7F6E\u9879: mcpEndpoint, mcpServers, connection, heartbeatInterval, heartbeatTimeout, reconnectInterval"));return}}}catch(t){o.fail(`\u914D\u7F6E\u64CD\u4F5C\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`)}}g(Be,"configCommand");function Ve(){console.log(s.blue.bold("xiaozhi - MCP Calculator Service CLI")),console.log(),console.log(s.yellow("\u4F7F\u7528\u65B9\u6CD5:")),console.log(" xiaozhi <command> [options]"),console.log(),console.log(s.yellow("\u547D\u4EE4:")),console.log(" create <projectName> \u521B\u5EFA\u9879\u76EE"),console.log(" init \u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6"),console.log(" config <key> [value] \u67E5\u770B\u6216\u8BBE\u7F6E\u914D\u7F6E"),console.log(" start [--daemon] [--ui] [--server] \u542F\u52A8\u670D\u52A1 (--daemon \u540E\u53F0\u8FD0\u884C, --ui \u540C\u65F6\u542F\u52A8 Web UI, --server MCP Server \u6A21\u5F0F)"),console.log(" stop \u505C\u6B62\u670D\u52A1"),console.log(" status \u68C0\u67E5\u670D\u52A1\u72B6\u6001"),console.log(" attach \u8FDE\u63A5\u5230\u540E\u53F0\u670D\u52A1\u67E5\u770B\u65E5\u5FD7"),console.log(" restart [--daemon] [--ui] \u91CD\u542F\u670D\u52A1 (--daemon \u540E\u53F0\u8FD0\u884C, --ui \u540C\u65F6\u542F\u52A8 Web UI)"),console.log(" ui \u542F\u52A8\u914D\u7F6E\u7BA1\u7406\u7F51\u9875"),console.log(" completion \u663E\u793A\u81EA\u52A8\u8865\u5168\u8BBE\u7F6E\u8BF4\u660E"),console.log(),console.log(s.yellow("\u9009\u9879:")),console.log(" -v, --version \u663E\u793A\u7248\u672C\u4FE1\u606F"),console.log(" -V \u663E\u793A\u8BE6\u7EC6\u4FE1\u606F"),console.log(" -h, --help \u663E\u793A\u5E2E\u52A9\u4FE1\u606F"),console.log(" -t, --template <name> \u6307\u5B9A\u6A21\u677F\u540D\u79F0\uFF08\u7528\u4E8E create \u547D\u4EE4\uFF09"),console.log(),console.log(s.yellow("\u9879\u76EE\u793A\u4F8B:")),console.log(" xiaozhi create my-app # \u521B\u5EFA\u57FA\u672C\u9879\u76EE"),console.log(" xiaozhi create my-app -t hello-world # \u4F7F\u7528 hello-world \u6A21\u677F"),console.log(" xiaozhi create my-app --template hello-world # \u540C\u4E0A\uFF0C\u5B8C\u6574\u9009\u9879\u540D"),console.log(),console.log(s.yellow("\u914D\u7F6E\u793A\u4F8B:")),console.log(" xiaozhi init # \u521D\u59CB\u5316\u914D\u7F6E"),console.log(" xiaozhi config mcpEndpoint # \u67E5\u770B MCP \u7AEF\u70B9"),console.log(" xiaozhi config mcpEndpoint wss://... # \u8BBE\u7F6E MCP \u7AEF\u70B9"),console.log(),console.log(s.yellow("\u670D\u52A1\u793A\u4F8B:")),console.log(" xiaozhi start # \u524D\u53F0\u542F\u52A8\u670D\u52A1"),console.log(" xiaozhi start --daemon # \u540E\u53F0\u542F\u52A8\u670D\u52A1"),console.log(" xiaozhi start --ui # \u542F\u52A8\u670D\u52A1\u5E76\u540C\u65F6\u542F\u52A8 Web UI"),console.log(" xiaozhi start -d -u # \u540E\u53F0\u542F\u52A8\u670D\u52A1\u5E76\u540C\u65F6\u542F\u52A8 Web UI"),console.log(" xiaozhi start --server # \u4EE5 MCP Server \u6A21\u5F0F\u542F\u52A8 (\u7AEF\u53E3 3000)"),console.log(" xiaozhi start -s 8080 # \u4EE5 MCP Server \u6A21\u5F0F\u542F\u52A8 (\u7AEF\u53E3 8080)"),console.log(" xiaozhi start -s -d # \u540E\u53F0\u8FD0\u884C MCP Server"),console.log(" xiaozhi status # \u68C0\u67E5\u670D\u52A1\u72B6\u6001"),console.log(" xiaozhi attach # \u67E5\u770B\u540E\u53F0\u670D\u52A1\u65E5\u5FD7"),console.log(" xiaozhi stop # \u505C\u6B62\u670D\u52A1"),console.log(),console.log(s.yellow("MCP \u7BA1\u7406\u793A\u4F8B:")),console.log(" xiaozhi mcp list # \u5217\u51FA\u6240\u6709 MCP \u670D\u52A1"),console.log(" xiaozhi mcp list --tools # \u5217\u51FA\u6240\u6709\u670D\u52A1\u7684\u5DE5\u5177"),console.log(" xiaozhi mcp server <name> # \u5217\u51FA\u6307\u5B9A\u670D\u52A1\u7684\u5DE5\u5177"),console.log(" xiaozhi mcp tool <server> <tool> enable # \u542F\u7528\u5DE5\u5177"),console.log(" xiaozhi mcp tool <server> <tool> disable # \u7981\u7528\u5DE5\u5177"),console.log(),console.log(s.yellow("\u81EA\u52A8\u8865\u5168:")),console.log(" xiaozhi completion # \u663E\u793A\u81EA\u52A8\u8865\u5168\u8BBE\u7F6E\u8BF4\u660E"),console.log(" # \u8BBE\u7F6E\u540E\u53EF\u4F7F\u7528 Tab \u952E\u8FDB\u884C\u547D\u4EE4\u3001\u53C2\u6570\u81EA\u52A8\u8865\u5168")}g(Ve,"showHelp");S.name("xiaozhi").description("MCP Calculator Service CLI Tool").version(ae(),"-v, --version","\u663E\u793A\u7248\u672C\u4FE1\u606F").helpOption("-h, --help","\u663E\u793A\u5E2E\u52A9\u4FE1\u606F");S.command("create <projectName>").description("\u521B\u5EFA\u9879\u76EE").option("-t, --template <templateName>","\u4F7F\u7528\u6307\u5B9A\u6A21\u677F\u521B\u5EFA\u9879\u76EE").action(async(n,e)=>{await Xe(n,e)});S.command("init").description("\u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6").action(async()=>{await We()});S.command("config <key> [value]").description("\u67E5\u770B\u6216\u8BBE\u7F6E\u914D\u7F6E").action(async(n,e)=>{await Be(n,e)});S.command("start").description("\u542F\u52A8\u670D\u52A1").option("-d, --daemon","\u5728\u540E\u53F0\u8FD0\u884C\u670D\u52A1").option("-u, --ui","\u540C\u65F6\u542F\u52A8 Web UI \u670D\u52A1").option("-s, --server [port]","\u4EE5 MCP Server \u6A21\u5F0F\u542F\u52A8 (\u53EF\u9009\u6307\u5B9A\u7AEF\u53E3\uFF0C\u9ED8\u8BA4 3000)").option("--stdio","\u4EE5 stdio \u6A21\u5F0F\u8FD0\u884C MCP Server (\u7528\u4E8E Cursor \u7B49\u5BA2\u6237\u7AEF)").action(async n=>{if(n.stdio){let{spawn:e}=await import("child_process"),o=P(import.meta.url),t=f.dirname(o),r=f.join(t,"mcpServerProxy.js");e("node",[r],{stdio:"inherit",env:{...process.env,XIAOZHI_CONFIG_DIR:process.env.XIAOZHI_CONFIG_DIR||process.cwd()}})}else if(n.server){let e=typeof n.server=="string"?Number.parseInt(n.server):3e3;await De(e,n.daemon)}else await le(n.daemon,n.ui)});S.command("stop").description("\u505C\u6B62\u670D\u52A1").action(async()=>{await ge()});S.command("status").description("\u68C0\u67E5\u670D\u52A1\u72B6\u6001").action(async()=>{await Ae()});S.command("attach").description("\u8FDE\u63A5\u5230\u540E\u53F0\u670D\u52A1\u67E5\u770B\u65E5\u5FD7").action(async()=>{await Fe()});S.command("restart").description("\u91CD\u542F\u670D\u52A1").option("-d, --daemon","\u5728\u540E\u53F0\u8FD0\u884C\u670D\u52A1").option("-u, --ui","\u540C\u65F6\u542F\u52A8 Web UI \u670D\u52A1").action(async n=>{await Ue(n.daemon,n.ui)});var G=S.command("mcp").description("MCP \u670D\u52A1\u548C\u5DE5\u5177\u7BA1\u7406");G.command("list").description("\u5217\u51FA MCP \u670D\u52A1").option("--tools","\u663E\u793A\u6240\u6709\u670D\u52A1\u7684\u5DE5\u5177\u5217\u8868").action(async n=>{await ee(n)});G.command("server <serverName>").description("\u7BA1\u7406\u6307\u5B9A\u7684 MCP \u670D\u52A1").action(async n=>{await oe(n)});G.command("tool <serverName> <toolName> <action>").description("\u542F\u7528\u6216\u7981\u7528\u6307\u5B9A\u670D\u52A1\u7684\u5DE5\u5177").action(async(n,e,o)=>{o!=="enable"&&o!=="disable"&&(console.error(s.red("\u9519\u8BEF: \u64CD\u4F5C\u5FC5\u987B\u662F 'enable' \u6216 'disable'")),process.exit(1)),await te(n,e,o==="enable")});var k=S.command("endpoint").description("\u7BA1\u7406 MCP \u7AEF\u70B9");k.command("list").description("\u5217\u51FA\u6240\u6709 MCP \u7AEF\u70B9").action(async()=>{let n=b("\u8BFB\u53D6\u7AEF\u70B9\u914D\u7F6E...").start();try{let e=l.getMcpEndpoints();n.succeed("\u7AEF\u70B9\u5217\u8868"),e.length===0?console.log(s.yellow("\u672A\u914D\u7F6E\u4EFB\u4F55 MCP \u7AEF\u70B9")):(console.log(s.green(`\u5171 ${e.length} \u4E2A\u7AEF\u70B9:`)),e.forEach((o,t)=>{console.log(s.gray(` ${t+1}. ${o}`))}))}catch(e){n.fail(`\u8BFB\u53D6\u7AEF\u70B9\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}});k.command("add <url>").description("\u6DFB\u52A0\u65B0\u7684 MCP \u7AEF\u70B9").action(async n=>{let e=b("\u6DFB\u52A0\u7AEF\u70B9...").start();try{l.addMcpEndpoint(n),e.succeed(`\u6210\u529F\u6DFB\u52A0\u7AEF\u70B9: ${n}`);let o=l.getMcpEndpoints();console.log(s.gray(`\u5F53\u524D\u5171 ${o.length} \u4E2A\u7AEF\u70B9`))}catch(o){e.fail(`\u6DFB\u52A0\u7AEF\u70B9\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`)}});k.command("remove <url>").description("\u79FB\u9664\u6307\u5B9A\u7684 MCP \u7AEF\u70B9").action(async n=>{let e=b("\u79FB\u9664\u7AEF\u70B9...").start();try{l.removeMcpEndpoint(n),e.succeed(`\u6210\u529F\u79FB\u9664\u7AEF\u70B9: ${n}`);let o=l.getMcpEndpoints();console.log(s.gray(`\u5F53\u524D\u5269\u4F59 ${o.length} \u4E2A\u7AEF\u70B9`))}catch(o){e.fail(`\u79FB\u9664\u7AEF\u70B9\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`)}});k.command("set <urls...>").description("\u8BBE\u7F6E MCP \u7AEF\u70B9\uFF08\u53EF\u4EE5\u662F\u5355\u4E2A\u6216\u591A\u4E2A\uFF09").action(async n=>{let e=b("\u8BBE\u7F6E\u7AEF\u70B9...").start();try{if(n.length===1)l.updateMcpEndpoint(n[0]),e.succeed(`\u6210\u529F\u8BBE\u7F6E\u7AEF\u70B9: ${n[0]}`);else{l.updateMcpEndpoint(n),e.succeed(`\u6210\u529F\u8BBE\u7F6E ${n.length} \u4E2A\u7AEF\u70B9`);for(let[o,t]of n.entries())console.log(s.gray(` ${o+1}. ${t}`))}}catch(o){e.fail(`\u8BBE\u7F6E\u7AEF\u70B9\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`)}});S.command("ui").description("\u542F\u52A8\u914D\u7F6E\u7BA1\u7406\u7F51\u9875").action(async()=>{await Ze()});S.command("completion").description("\u663E\u793A\u81EA\u52A8\u8865\u5168\u8BBE\u7F6E\u8BF4\u660E").action(async()=>{q()});S.option("-V","\u663E\u793A\u8BE6\u7EC6\u4FE1\u606F").action(n=>{n.V&&(He(),process.exit(0))});V();process.argv.length<=2&&(Ve(),process.exit(0));S.parse(process.argv);z();var O=class{static{g(this,"WebServer")}httpServer;wss;logger;port;clientInfo={status:"disconnected",mcpEndpoint:"",activeMCPServers:[]};heartbeatTimeout;HEARTBEAT_TIMEOUT=35e3;constructor(e){if(e===void 0)try{this.port=l.getWebUIPort()}catch{this.port=9999}else this.port=e;this.logger=new $,this.httpServer=qe((o,t)=>{this.handleHttpRequest(o,t)}),this.wss=new eo({server:this.httpServer}),this.setupWebSocket()}async handleHttpRequest(e,o){let{pathname:t}=Ye(e.url||"",!0);if(o.setHeader("Access-Control-Allow-Origin","*"),o.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, OPTIONS"),o.setHeader("Access-Control-Allow-Headers","Content-Type"),e.method==="OPTIONS"){o.writeHead(200),o.end();return}try{if(e.method==="GET"&&!t?.startsWith("/api/")){await this.serveStaticFile(t||"/",o);return}if(t==="/api/config"&&e.method==="GET"){let r=l.getConfig();o.writeHead(200,{"Content-Type":"application/json"}),o.end(JSON.stringify(r))}else if(t==="/api/config"&&e.method==="PUT"){let r="";e.on("data",i=>{r+=i.toString()}),e.on("end",async()=>{try{let i=JSON.parse(r);this.updateConfig(i),this.broadcastConfigUpdate(i),o.writeHead(200,{"Content-Type":"application/json"}),o.end(JSON.stringify({success:!0})),this.logger.info("\u914D\u7F6E\u5DF2\u66F4\u65B0")}catch(i){o.writeHead(400,{"Content-Type":"application/json"}),o.end(JSON.stringify({error:i instanceof Error?i.message:String(i)}))}})}else t==="/api/status"&&e.method==="GET"?(o.writeHead(200,{"Content-Type":"application/json"}),o.end(JSON.stringify(this.clientInfo))):(o.writeHead(404),o.end("Not Found"))}catch(r){this.logger.error("HTTP request error:",r),o.writeHead(500,{"Content-Type":"application/json"}),o.end(JSON.stringify({error:"Internal Server Error"}))}}async serveStaticFile(e,o){try{let t=Ke(Qe(import.meta.url)),i=[I(t,"..","web","dist"),I(t,"..","web"),I(process.cwd(),"web","dist"),I(process.cwd(),"web")].find(C=>J(C));if(!i){o.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),o.end(`
|
|
20
20
|
<!DOCTYPE html>
|
|
21
21
|
<html>
|
|
22
22
|
<head>
|
|
@@ -36,5 +36,5 @@ data: ${JSON.stringify(o)}
|
|
|
36
36
|
</div>
|
|
37
37
|
</body>
|
|
38
38
|
</html>
|
|
39
|
-
`);return}let c=e;if(c==="/"&&(c="/index.html"),c.includes("..")){o.writeHead(403),o.end("Forbidden");return}let a=I(i,c);if(!J(a)){let
|
|
39
|
+
`);return}let c=e;if(c==="/"&&(c="/index.html"),c.includes("..")){o.writeHead(403),o.end("Forbidden");return}let a=I(i,c);if(!J(a)){let C=I(i,"index.html");if(J(C)){let E=await de(C);o.writeHead(200,{"Content-Type":"text/html"}),o.end(E)}else o.writeHead(404),o.end("Not Found");return}let d=await de(a),m=a.split(".").pop()?.toLowerCase(),v={html:"text/html",js:"application/javascript",css:"text/css",json:"application/json",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",svg:"image/svg+xml",ico:"image/x-icon"}[m||""]||"application/octet-stream";o.writeHead(200,{"Content-Type":v}),o.end(d)}catch(t){this.logger.error("Serve static file error:",t),o.writeHead(500),o.end("Internal Server Error")}}setupWebSocket(){this.wss.on("connection",e=>{this.logger.debug("WebSocket client connected"),e.on("message",async o=>{try{let t=JSON.parse(o.toString());await this.handleWebSocketMessage(e,t)}catch(t){this.logger.error("WebSocket message error:",t),e.send(JSON.stringify({type:"error",error:t instanceof Error?t.message:String(t)}))}}),e.on("close",()=>{this.logger.debug("WebSocket client disconnected")}),this.sendInitialData(e)})}async handleWebSocketMessage(e,o){switch(o.type){case"getConfig":{let t=l.getConfig();e.send(JSON.stringify({type:"config",data:t}));break}case"updateConfig":this.updateConfig(o.config),this.broadcastConfigUpdate(o.config);break;case"getStatus":e.send(JSON.stringify({type:"status",data:this.clientInfo}));break;case"clientStatus":this.updateClientInfo(o.data),this.broadcastStatusUpdate();break;case"restartService":this.logger.info("\u6536\u5230\u624B\u52A8\u91CD\u542F\u670D\u52A1\u8BF7\u6C42"),this.broadcastRestartStatus("restarting"),setTimeout(async()=>{try{await this.restartService(),setTimeout(()=>{this.broadcastRestartStatus("completed")},5e3)}catch(t){this.logger.error(`\u624B\u52A8\u91CD\u542F\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`),this.broadcastRestartStatus("failed",t instanceof Error?t.message:"\u672A\u77E5\u9519\u8BEF")}},500);break}}async sendInitialData(e){let o=l.getConfig();e.send(JSON.stringify({type:"config",data:o})),e.send(JSON.stringify({type:"status",data:this.clientInfo}))}broadcastConfigUpdate(e){let o=JSON.stringify({type:"configUpdate",data:e});for(let t of this.wss.clients)t.readyState===1&&t.send(o)}broadcastRestartStatus(e,o){let t=JSON.stringify({type:"restartStatus",data:{status:e,error:o,timestamp:Date.now()}});for(let r of this.wss.clients)r.readyState===1&&r.send(t)}broadcastStatusUpdate(){let e=JSON.stringify({type:"statusUpdate",data:this.clientInfo});for(let o of this.wss.clients)o.readyState===1&&o.send(e)}updateClientInfo(e){this.clientInfo={...this.clientInfo,...e},e.lastHeartbeat&&(this.clientInfo.lastHeartbeat=Date.now()),e.status==="connected"&&this.resetHeartbeatTimeout()}resetHeartbeatTimeout(){this.heartbeatTimeout&&clearTimeout(this.heartbeatTimeout),this.heartbeatTimeout=setTimeout(()=>{this.logger.warn("\u5BA2\u6237\u7AEF\u5FC3\u8DF3\u8D85\u65F6\uFF0C\u6807\u8BB0\u4E3A\u65AD\u5F00\u8FDE\u63A5"),this.updateClientInfo({status:"disconnected"}),this.broadcastStatusUpdate()},this.HEARTBEAT_TIMEOUT)}updateConfig(e){e.mcpEndpoint!==l.getMcpEndpoint()&&l.updateMcpEndpoint(e.mcpEndpoint);let o=l.getMcpServers();for(let[t,r]of Object.entries(e.mcpServers))JSON.stringify(o[t])!==JSON.stringify(r)&&l.updateMcpServer(t,r);for(let t of Object.keys(o))t in e.mcpServers||(l.removeMcpServer(t),l.removeServerToolsConfig(t));if(e.connection&&l.updateConnectionConfig(e.connection),e.modelscope&&l.updateModelScopeConfig(e.modelscope),e.webUI&&l.updateWebUIConfig(e.webUI),e.mcpServerConfig)for(let[t,r]of Object.entries(e.mcpServerConfig))for(let[i,c]of Object.entries(r.tools))l.setToolEnabled(t,i,c.enable)}async restartService(){this.logger.info("\u6B63\u5728\u91CD\u542F MCP \u670D\u52A1..."),this.heartbeatTimeout&&(clearTimeout(this.heartbeatTimeout),this.heartbeatTimeout=void 0);try{let e=x();if(!e.running){this.logger.warn("MCP \u670D\u52A1\u672A\u8FD0\u884C\uFF0C\u5C1D\u8BD5\u542F\u52A8\u670D\u52A1"),fe("xiaozhi",["start","--daemon"],{detached:!0,stdio:"ignore",env:{...process.env,XIAOZHI_CONFIG_DIR:process.env.XIAOZHI_CONFIG_DIR||process.cwd()}}).unref(),this.logger.info("MCP \u670D\u52A1\u542F\u52A8\u547D\u4EE4\u5DF2\u53D1\u9001");return}let o=e.mode==="daemon",t=["restart"];o&&t.push("--daemon"),fe("xiaozhi",t,{detached:!0,stdio:"ignore",env:{...process.env,XIAOZHI_CONFIG_DIR:process.env.XIAOZHI_CONFIG_DIR||process.cwd()}}).unref(),this.logger.info("MCP \u670D\u52A1\u91CD\u542F\u547D\u4EE4\u5DF2\u53D1\u9001"),this.resetHeartbeatTimeout()}catch(e){throw this.logger.error(`\u91CD\u542F\u670D\u52A1\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`),this.resetHeartbeatTimeout(),e}}updateStatus(e){this.updateClientInfo(e),this.broadcastStatusUpdate()}start(){return new Promise((e,o)=>{this.httpServer.listen(this.port,()=>{this.logger.info(`Web server listening on http://localhost:${this.port}`),e()}).on("error",o)})}stop(){return new Promise(e=>{this.heartbeatTimeout&&(clearTimeout(this.heartbeatTimeout),this.heartbeatTimeout=void 0);for(let o of this.wss.clients)o.terminate();this.wss.close(()=>{this.httpServer.close(()=>{this.logger.info("Web server stopped"),e()}),setTimeout(()=>{this.logger.info("Web server force stopped"),e()},2e3)})})}};export{O as WebServer};
|
|
40
40
|
//# sourceMappingURL=webServer.js.map
|