xiaozhi-client 1.4.0-beta.3 → 1.4.0-beta.4
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/autoCompletion.js +1 -1
- package/dist/autoCompletion.js.map +1 -1
- package/dist/cli.js +7 -6
- package/dist/cli.js.map +1 -1
- package/dist/configManager.d.ts +21 -1
- 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/mcpPipe.js +5 -5
- package/dist/mcpPipe.js.map +1 -1
- package/dist/mcpServerProxy.js +4 -4
- package/dist/mcpServerProxy.js.map +1 -1
- package/dist/templates/hello-world/xiaozhi.config.json +3 -0
- package/dist/webServer.js +3 -3
- package/dist/webServer.js.map +1 -1
- package/dist/xiaozhi.config.default.json +3 -0
- package/docs/images/web-ui-preview.png +0 -0
- package/package.json +1 -1
- package/templates/hello-world/xiaozhi.config.json +3 -0
- package/web/dist/assets/{index-BUBCwPqC.js → index-FD98vGpI.js} +2 -2
- package/web/dist/assets/{index-BUBCwPqC.js.map → index-FD98vGpI.js.map} +1 -1
- package/web/dist/index.html +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcpServerProxy.ts","../src/configManager.ts","../src/logger.ts","../src/modelScopeMCPClient.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * MCP Server Proxy - JavaScript Implementation\n * Provides a proxy to aggregate multiple MCP servers dynamically from configuration\n * Version: 0.3.0 - Manual JSON-RPC 2.0 implementation (no SDK) with dynamic config\n */\n\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n type LocalMCPServerConfig,\n type MCPServerConfig,\n type MCPToolConfig,\n type SSEMCPServerConfig,\n configManager,\n} from \"./configManager\";\nimport { logger as globalLogger } from \"./logger\";\nimport { ModelScopeMCPClient } from \"./modelScopeMCPClient\";\n\n// ESM 兼容的 __dirname\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// 为 MCPProxy 创建带标签的 logger\nconst logger = globalLogger.withTag(\"MCPProxy\");\n\n// 如果在守护进程模式下运行,初始化日志文件\nif (process.env.XIAOZHI_DAEMON === \"true\" && process.env.XIAOZHI_CONFIG_DIR) {\n globalLogger.initLogFile(process.env.XIAOZHI_CONFIG_DIR);\n globalLogger.enableFileLogging(true);\n}\n\n// Type definitions\n\ninterface Tool {\n name: string;\n description?: string;\n inputSchema?: any;\n}\n\ninterface PendingRequest {\n resolve: (value: any) => void;\n reject: (reason: any) => void;\n}\n\n// 定义 MCP 客户端接口\nexport interface IMCPClient {\n initialized: boolean;\n tools: Tool[];\n originalTools: Tool[];\n start(): Promise<void>;\n refreshTools(): Promise<void>;\n callTool(toolName: string, arguments_: any): Promise<any>;\n stop(): void | Promise<void>;\n getOriginalToolName(prefixedToolName: string): string | null;\n}\n\n/**\n * MCP Client for communicating with child MCP servers\n */\nexport class MCPClient implements IMCPClient {\n private name: string;\n private config: LocalMCPServerConfig;\n private process: ChildProcess | null;\n public initialized: boolean;\n public tools: Tool[];\n public originalTools: Tool[]; // 存储原始工具名称\n private requestId: number;\n private pendingRequests: Map<number, PendingRequest>;\n private messageBuffer: string;\n\n constructor(name: string, config: LocalMCPServerConfig) {\n this.name = name;\n this.config = config;\n this.process = null;\n this.initialized = false;\n this.tools = [];\n this.originalTools = [];\n this.requestId = 1;\n this.pendingRequests = new Map();\n this.messageBuffer = \"\";\n }\n\n /**\n * Resolve command for cross-platform execution\n * On Windows, npm/npx/uvx commands need special handling\n */\n public resolveCommand(\n command: string,\n args: string[]\n ): { resolvedCommand: string; resolvedArgs: string[] } {\n if (process.platform === \"win32\") {\n // On Windows, npm, npx, and uvx are .cmd files or .exe files\n if (command === \"npm\" || command === \"npx\") {\n return {\n resolvedCommand: `${command}.cmd`,\n resolvedArgs: args,\n };\n }\n // uvx is typically installed as uvx.bat on Windows (via pyenv)\n if (command === \"uvx\") {\n return {\n resolvedCommand: \"uvx.bat\",\n resolvedArgs: args,\n };\n }\n }\n\n return {\n resolvedCommand: command,\n resolvedArgs: args,\n };\n }\n\n /**\n * 生成带前缀的工具名称\n * 将服务器名称中的中划线替换为下划线,并添加 xzcli 前缀\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 async start() {\n logger.info(`正在启动 MCP 客户端:${this.name}`);\n\n const { command, args, env } = this.config;\n\n // Handle cross-platform command execution\n const { resolvedCommand, resolvedArgs } = this.resolveCommand(\n command,\n args\n );\n\n const spawnOptions: any = {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n };\n\n // Set working directory to user's current working directory\n // This ensures relative paths in MCP server configs are resolved correctly\n const userWorkingDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n spawnOptions.cwd = userWorkingDir;\n\n // Add environment variables if specified\n if (env) {\n spawnOptions.env = { ...process.env, ...env };\n } else {\n spawnOptions.env = { ...process.env };\n }\n\n // On Windows, we need to set shell: true for npm/npx/uvx commands\n if (\n process.platform === \"win32\" &&\n (command === \"npm\" || command === \"npx\" || command === \"uvx\")\n ) {\n spawnOptions.shell = true;\n }\n\n logger.debug(\n `${this.name} 正在生成进程:${resolvedCommand} ${resolvedArgs.join(\" \")} 工作目录:${spawnOptions.cwd}`\n );\n logger.debug(\n `${this.name} 平台:${process.platform},shell 模式:${spawnOptions.shell || false}`\n );\n\n this.process = spawn(resolvedCommand, resolvedArgs, spawnOptions);\n\n // Handle process stdout - parse JSON-RPC messages\n this.process.stdout?.on(\"data\", (data: Buffer) => {\n this.handleStdoutData(data.toString());\n });\n\n // Handle process stderr - log errors\n this.process.stderr?.on(\"data\", (data: Buffer) => {\n logger.debug(`${this.name} 标准错误输出:${data.toString().trim()}`);\n });\n\n // Handle process exit\n this.process.on(\n \"exit\",\n (code: number | null, signal: NodeJS.Signals | null) => {\n logger.error(\n `${this.name} 进程已退出,退出码:${code},信号:${signal}`\n );\n this.initialized = false;\n }\n );\n\n // Handle process error\n this.process.on(\"error\", (error: Error) => {\n logger.error(`${this.name} 进程错误:${error.message}`);\n this.initialized = false;\n });\n\n // Initialize the MCP connection\n await this.initialize();\n }\n\n handleStdoutData(data: string): void {\n this.messageBuffer = `${this.messageBuffer}${data}`;\n\n // Split by newlines and process complete messages\n const lines = this.messageBuffer.split(\"\\n\");\n this.messageBuffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (line.trim()) {\n try {\n const message = JSON.parse(line.trim());\n this.handleMessage(message);\n } catch (error) {\n logger.error(`${this.name} 解析消息失败:${line.trim()}`);\n }\n }\n }\n }\n\n handleMessage(message: any): void {\n logger.debug(\n `${this.name} 收到消息:${JSON.stringify(message).substring(0, 200)}...`\n );\n\n if (message.id && this.pendingRequests.has(message.id)) {\n // This is a response to our request\n const pendingRequest = this.pendingRequests.get(message.id);\n if (pendingRequest) {\n const { resolve, reject } = pendingRequest;\n this.pendingRequests.delete(message.id);\n\n if (message.error) {\n reject(\n new Error(`${message.error.message} (code: ${message.error.code})`)\n );\n } else {\n resolve(message.result);\n }\n }\n }\n // Note: We don't handle notifications or requests from child servers in this proxy\n }\n\n async sendRequest(method: string, params: any = {}): Promise<any> {\n if (!this.process || !this.process.stdin) {\n throw new Error(`${this.name} 进程不可用`);\n }\n\n const id = this.requestId++;\n const request = {\n jsonrpc: \"2.0\",\n id: id,\n method: method,\n params: params,\n };\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, { resolve, reject });\n\n // Set timeout for request\n setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id);\n reject(new Error(`请求超时:${method}`));\n }\n }, 30000); // 30 second timeout\n\n const message = `${JSON.stringify(request)}\\n`;\n logger.debug(`${this.name} 正在发送:${message.trim()}`);\n this.process?.stdin?.write(message);\n });\n }\n\n async initialize() {\n try {\n // Send initialize request\n const initResult = await this.sendRequest(\"initialize\", {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: {\n name: \"MCPProxy\",\n version: \"0.3.0\",\n },\n });\n\n logger.info(\n `${this.name} 已初始化,能力:${JSON.stringify((initResult as any).capabilities)}`\n );\n\n // Send initialized notification\n const notification = {\n jsonrpc: \"2.0\",\n method: \"notifications/initialized\",\n };\n this.process?.stdin?.write(`${JSON.stringify(notification)}\\n`);\n\n // Get tools list\n await this.refreshTools();\n\n this.initialized = true;\n logger.info(`${this.name} 客户端已就绪,共 ${this.tools.length} 个工具`);\n } catch (error) {\n logger.error(\n `初始化 ${this.name} 失败:${error instanceof Error ? error.message : String(error)}`\n );\n throw error;\n }\n }\n\n async refreshTools(): Promise<void> {\n try {\n const result = await this.sendRequest(\"tools/list\");\n this.originalTools = (result as any).tools || [];\n\n // 为每个工具生成带前缀的名称,并应用过滤\n const allPrefixedTools = this.originalTools.map((tool) => ({\n ...tool,\n name: this.generatePrefixedToolName(tool.name),\n }));\n\n // 根据配置过滤工具\n this.tools = this.filterEnabledTools(allPrefixedTools);\n\n // 更新配置文件中的工具列表(如果需要)\n await this.updateToolsConfig();\n\n logger.info(\n `${this.name} 加载了 ${this.originalTools.length} 个工具:${this.originalTools.map((t) => t.name).join(\", \")}`\n );\n logger.info(\n `${this.name} 启用了 ${this.tools.length} 个工具:${this.tools.map((t) => t.name).join(\", \")}`\n );\n } catch (error) {\n logger.error(\n `从 ${this.name} 获取工具失败:${error instanceof Error ? error.message : String(error)}`\n );\n this.tools = [];\n this.originalTools = [];\n }\n }\n\n /**\n * 过滤启用的工具\n */\n private filterEnabledTools(allTools: Tool[]): Tool[] {\n return allTools.filter((tool) => {\n const originalName = this.getOriginalToolName(tool.name);\n if (!originalName) return true; // 如果无法解析原始名称,默认启用\n\n return configManager.isToolEnabled(this.name, originalName);\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} 的工具配置失败:${error instanceof Error ? error.message : String(error)}`\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 return result;\n } catch (error) {\n logger.error(\n `在 ${this.name} 上调用工具 ${prefixedName} (原始名称:${this.getOriginalToolName(prefixedName)}) 失败:${error instanceof Error ? error.message : String(error)}`\n );\n throw error;\n }\n }\n\n stop(): void {\n if (this.process) {\n logger.info(`正在停止 ${this.name} 客户端`);\n this.process.kill(\"SIGTERM\");\n this.process = null;\n }\n this.initialized = false;\n }\n}\n\n/**\n * Configuration loader for MCP servers\n */\nexport function loadMCPConfig(): Record<string, MCPServerConfig> {\n try {\n // 首先尝试从配置管理器读取\n if (configManager.configExists()) {\n const mcpServers = configManager.getMcpServers();\n logger.info(\n `从配置文件加载了 ${Object.keys(mcpServers).length} 个 MCP 服务`\n );\n return mcpServers as Record<string, MCPServerConfig>;\n }\n\n // 如果配置文件不存在,尝试从旧的 mcp_server.json 读取(向后兼容)\n const legacyConfigPath = resolve(__dirname, \"mcp_server.json\");\n if (readFileSync) {\n // 检查是否可以读取文件\n try {\n const configData = readFileSync(legacyConfigPath, \"utf8\");\n const config = JSON.parse(configData);\n\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n throw new Error(\"无效的配置:mcpServers 部分未找到或无效\");\n }\n\n logger.info(\n `从旧配置文件加载了 ${Object.keys(config.mcpServers).length} 个 MCP 服务(建议迁移到新配置格式)`\n );\n return config.mcpServers;\n } catch (legacyError) {\n // 旧配置文件也不存在或无效\n logger.error('配置文件不存在,请运行 \"xiaozhi init\" 初始化配置');\n throw new Error('配置文件不存在,请运行 \"xiaozhi init\" 初始化配置');\n }\n }\n\n throw new Error('配置文件不存在,请运行 \"xiaozhi init\" 初始化配置');\n } catch (error) {\n logger.error(\n `加载 MCP 配置失败:${error instanceof Error ? error.message : String(error)}`\n );\n throw error;\n }\n}\n\n/**\n * MCP Server Proxy - Main proxy server implementation\n */\nexport class MCPServerProxy {\n private clients: Map<string, IMCPClient>;\n private toolMap: Map<string, string>; // Maps tool name to client name\n public initialized: boolean;\n private config: Record<string, MCPServerConfig> | null;\n\n constructor() {\n this.clients = new Map();\n this.toolMap = new Map(); // Maps tool name to client name\n this.initialized = false;\n this.config = null;\n }\n\n async start() {\n logger.info(\"正在启动 MCP 服务代理\");\n\n // Load configuration\n this.config = loadMCPConfig();\n\n // Initialize child MCP clients from configuration\n const clientPromises = [];\n\n for (const [serverName, serverConfig] of Object.entries(this.config)) {\n logger.info(`正在初始化 MCP 客户端:${serverName}`);\n\n let client: IMCPClient;\n\n // 判断是 SSE 类型还是本地类型\n if (\"type\" in serverConfig && serverConfig.type === \"sse\") {\n // ModelScope SSE MCP 服务\n client = new ModelScopeMCPClient(serverName, serverConfig);\n } else {\n // 本地 MCP 服务\n client = new MCPClient(\n serverName,\n serverConfig as LocalMCPServerConfig\n );\n }\n\n this.clients.set(serverName, client);\n clientPromises.push(client.start());\n }\n\n // Start all clients\n try {\n const results = await Promise.allSettled(clientPromises);\n\n // Check for failed clients\n let successCount = 0;\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n const serverName = Object.keys(this.config)[i];\n\n if (result.status === \"fulfilled\") {\n successCount++;\n logger.info(`成功启动 MCP 客户端:${serverName}`);\n } else {\n logger.error(\n `启动 MCP 客户端 ${serverName} 失败:${result.reason.message}`\n );\n // Remove failed client from clients map\n this.clients.delete(serverName);\n }\n }\n\n if (successCount === 0) {\n throw new Error(\"没有成功启动任何 MCP 客户端\");\n }\n\n // Build tool mapping\n this.buildToolMap();\n this.initialized = true;\n\n logger.info(\n `MCP 服务代理初始化成功,启动了 ${successCount}/${Object.keys(this.config).length} 个客户端`\n );\n } catch (error) {\n logger.error(\n `启动 MCP 客户端失败:${error instanceof Error ? error.message : String(error)}`\n );\n throw error;\n }\n }\n\n buildToolMap() {\n this.toolMap.clear();\n\n for (const [clientName, client] of this.clients) {\n for (const tool of client.tools) {\n // 工具名称现在已经带有前缀,应该不会有重复\n // 但我们仍然检查以防万一\n if (this.toolMap.has(tool.name)) {\n logger.error(\n `重复的工具名称:${tool.name} (来自 ${clientName} 和 ${this.toolMap.get(tool.name)})`\n );\n } else {\n this.toolMap.set(tool.name, clientName);\n }\n }\n }\n\n logger.info(`已构建工具映射,共 ${this.toolMap.size} 个工具`);\n logger.debug(`工具映射:${Array.from(this.toolMap.keys()).join(\", \")}`);\n }\n\n getAllTools(): Tool[] {\n const allTools: Tool[] = [];\n for (const client of this.clients.values()) {\n allTools.push(...client.tools);\n }\n return allTools;\n }\n\n /**\n * 获取所有服务器的信息\n */\n getAllServers(): Array<{\n name: string;\n toolCount: number;\n enabledToolCount: number;\n }> {\n const servers: Array<{\n name: string;\n toolCount: number;\n enabledToolCount: number;\n }> = [];\n\n for (const [serverName, client] of this.clients) {\n servers.push({\n name: serverName,\n toolCount: client.originalTools.length,\n enabledToolCount: client.tools.length,\n });\n }\n\n return servers;\n }\n\n /**\n * 获取指定服务器的工具信息\n */\n getServerTools(\n serverName: string\n ): Array<{ name: string; description: string; enabled: boolean }> {\n const client = this.clients.get(serverName);\n if (!client) {\n throw new Error(`未找到服务器 ${serverName}`);\n }\n\n return client.originalTools.map((tool) => ({\n name: tool.name,\n description: tool.description || \"\",\n enabled: configManager.isToolEnabled(serverName, tool.name),\n }));\n }\n\n /**\n * 刷新指定服务器的工具列表\n */\n async refreshServerTools(serverName: string): Promise<void> {\n const client = this.clients.get(serverName);\n if (!client) {\n throw new Error(`未找到服务器 ${serverName}`);\n }\n\n await client.refreshTools();\n this.buildToolMap(); // 重新构建工具映射\n }\n\n /**\n * 刷新所有服务器的工具列表\n */\n async refreshAllTools(): Promise<void> {\n const refreshPromises = Array.from(this.clients.values()).map((client) =>\n client.refreshTools()\n );\n await Promise.allSettled(refreshPromises);\n this.buildToolMap(); // 重新构建工具映射\n }\n\n async callTool(toolName: string, arguments_: any): Promise<any> {\n const clientName = this.toolMap.get(toolName);\n if (!clientName) {\n throw new Error(`未知的工具:${toolName}`);\n }\n\n const client = this.clients.get(clientName);\n if (!client || !client.initialized) {\n throw new Error(`客户端 ${clientName} 不可用`);\n }\n\n return await client.callTool(toolName, arguments_);\n }\n\n stop() {\n logger.info(\"正在停止 MCP 服务代理\");\n for (const client of this.clients.values()) {\n client.stop();\n }\n this.initialized = false;\n }\n}\n\n/**\n * Manual JSON-RPC 2.0 Server Implementation\n */\nexport class JSONRPCServer {\n private proxy: MCPServerProxy;\n private requestId: number;\n\n constructor(proxy: MCPServerProxy) {\n this.proxy = proxy;\n this.requestId = 1;\n }\n\n async handleMessage(message: string): Promise<string | null> {\n try {\n const parsedMessage = JSON.parse(message);\n logger.debug(\n `收到请求:${JSON.stringify(parsedMessage).substring(0, 200)}...`\n );\n\n if (parsedMessage.method) {\n if (parsedMessage.id !== undefined) {\n // This is a request\n const response = await this.handleRequest(parsedMessage);\n return JSON.stringify(response);\n }\n // This is a notification\n await this.handleNotification(parsedMessage);\n return null; // No response for notifications\n }\n throw new Error(\"无效的 JSON-RPC 消息\");\n } catch (error) {\n logger.error(\n `处理消息时出错:${error instanceof Error ? error.message : String(error)}`\n );\n return JSON.stringify({\n jsonrpc: \"2.0\",\n id: null,\n error: {\n code: -32700,\n message: \"解析错误\",\n },\n });\n }\n }\n\n async handleRequest(request: any): Promise<any> {\n const { id, method, params = {} } = request;\n\n try {\n let result: any;\n\n switch (method) {\n case \"initialize\":\n result = await this.handleInitialize(params);\n break;\n case \"tools/list\":\n result = await this.handleToolsList(params);\n break;\n case \"tools/call\":\n result = await this.handleToolsCall(params);\n break;\n case \"ping\":\n result = await this.handlePing(params);\n break;\n default:\n throw new Error(`未知的方法:${method}`);\n }\n\n return {\n jsonrpc: \"2.0\",\n id: id,\n result: result,\n };\n } catch (error) {\n logger.error(\n `处理请求 ${method} 时出错:${error instanceof Error ? error.message : String(error)}`\n );\n return {\n jsonrpc: \"2.0\",\n id: id,\n error: {\n code: -32603,\n message: error instanceof Error ? error.message : String(error),\n },\n };\n }\n }\n\n async handleNotification(notification: any): Promise<void> {\n const { method } = notification;\n\n switch (method) {\n case \"notifications/initialized\":\n logger.info(\"客户端发送了初始化通知\");\n break;\n default:\n logger.debug(`收到通知:${method}`);\n break;\n }\n }\n\n async handleInitialize(params: any): Promise<any> {\n logger.info(`收到客户端的初始化请求:${JSON.stringify(params.clientInfo)}`);\n\n return {\n protocolVersion: \"2024-11-05\",\n capabilities: {\n tools: {\n listChanged: false,\n },\n },\n serverInfo: {\n name: \"MCPServerProxy\",\n version: \"0.3.0\",\n },\n };\n }\n\n async handleToolsList(_params: any): Promise<any> {\n if (!this.proxy.initialized) {\n throw new Error(\"代理未初始化\");\n }\n\n const tools = this.proxy.getAllTools();\n logger.info(`返回 ${tools.length} 个工具`);\n\n return {\n tools: tools,\n };\n }\n\n async handleToolsCall(params: any): Promise<any> {\n const { name, arguments: args } = params;\n\n if (!name) {\n throw new Error(\"工具名称是必需的\");\n }\n\n logger.info(`调用工具:${name},参数:${JSON.stringify(args)}`);\n\n const result = await this.proxy.callTool(name, args || {});\n\n // 添加调试日志\n logger.info(`工具调用结果类型: ${typeof result}`);\n logger.info(`工具调用结果: ${JSON.stringify(result).substring(0, 500)}...`);\n\n return result;\n }\n\n async handlePing(_params: any): Promise<any> {\n logger.debug(\"收到 ping 请求\");\n return {}; // Empty response for ping\n }\n}\n\n/**\n * Main function to start the MCP Server Proxy\n */\nasync function main() {\n logger.info(\"正在启动 MCP 服务代理\");\n\n // Create and start the proxy\n const proxy = new MCPServerProxy();\n const jsonrpcServer = new JSONRPCServer(proxy);\n\n // Setup graceful shutdown\n const cleanup = () => {\n logger.info(\"正在关闭 MCP 服务代理\");\n proxy.stop();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n\n try {\n // Start the proxy (initialize child clients)\n await proxy.start();\n\n // Handle stdin/stdout communication\n process.stdin.setEncoding(\"utf8\");\n\n let messageBuffer = \"\";\n\n process.stdin.on(\"data\", async (data) => {\n messageBuffer = `${messageBuffer}${data}`;\n\n // Split by newlines and process complete messages\n const lines = messageBuffer.split(\"\\n\");\n messageBuffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (line.trim()) {\n try {\n const response = await jsonrpcServer.handleMessage(line.trim());\n if (response) {\n process.stdout.write(`${response}\\n`);\n }\n } catch (error) {\n logger.error(\n `处理消息时出错:${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n }\n });\n\n process.stdin.on(\"end\", () => {\n logger.info(\"标准输入已关闭,正在关闭\");\n cleanup();\n });\n\n logger.info(\"MCP 服务代理正在通过 stdio 运行\");\n } catch (error) {\n logger.error(\n `启动 MCP 服务代理失败:${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n }\n}\n\n// Run the server if this file is executed directly\n// Use fileURLToPath to properly handle Windows paths\nconst currentFileUrl = import.meta.url;\nconst scriptPath = fileURLToPath(currentFileUrl);\nconst argv1Path = process.argv[1];\n\nif (scriptPath === argv1Path) {\n main().catch((error) => {\n logger.error(\n `未处理的错误:${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n });\n}\n","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// ModelScope SSE MCP 服务配置\nexport interface SSEMCPServerConfig {\n type: \"sse\";\n url: string;\n}\n\n// 统一的 MCP 服务配置\nexport type MCPServerConfig = LocalMCPServerConfig | SSEMCPServerConfig;\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 AppConfig {\n mcpEndpoint: string;\n mcpServers: Record<string, MCPServerConfig>;\n mcpServerConfig?: Record<string, MCPServerToolsConfig>;\n connection?: ConnectionConfig; // 连接配置(可选,用于向后兼容)\n modelscope?: ModelScopeConfig; // ModelScope 配置(可选)\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 || typeof configObj.mcpEndpoint !== \"string\") {\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 // 检查是否是 SSE 类型\n if (sc.type === \"sse\") {\n // SSE 类型的验证\n if (!sc.url || typeof sc.url !== \"string\") {\n throw new Error(\n `配置文件格式错误:mcpServers.${serverName}.url 无效`\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 */\n public getMcpEndpoint(): string {\n const config = this.getConfig();\n return 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): void {\n if (!endpoint || typeof endpoint !== \"string\") {\n throw new Error(\"MCP 端点必须是非空字符串\");\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 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// 导出单例实例\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 { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { SSEClientTransport } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport { EventSource } from \"eventsource\";\nimport { type SSEMCPServerConfig, configManager } from \"./configManager\";\nimport { logger as globalLogger } from \"./logger\";\nimport type { IMCPClient } from \"./mcpServerProxy\";\n\n// 全局 polyfill EventSource\n(global as any).EventSource = EventSource;\n\n// 为 ModelScope MCP 创建带标签的 logger\nconst logger = globalLogger.withTag(\"ModelScopeMCP\");\n\ninterface Tool {\n name: string;\n description?: string;\n inputSchema?: any;\n}\n\n/**\n * ModelScope MCP Client for SSE connections\n */\nexport class ModelScopeMCPClient implements IMCPClient {\n private name: string;\n private config: SSEMCPServerConfig;\n private client: Client | null = null;\n private transport: SSEClientTransport | null = null;\n public initialized = false;\n public tools: Tool[] = [];\n public originalTools: Tool[] = [];\n\n constructor(name: string, config: SSEMCPServerConfig) {\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 async start() {\n logger.info(`正在启动 ModelScope MCP 客户端:${this.name}`);\n\n try {\n // 从配置或环境变量获取 API Token\n const token = configManager.getModelScopeApiKey();\n if (!token || token === \"\") {\n throw new Error(\n \"未设置 ModelScope API Key。请在配置文件中设置 modelscope.apiKey 或设置 MODELSCOPE_API_TOKEN 环境变量。\"\n );\n }\n\n // 创建 SSE 传输层\n const sseOptions = {\n eventSourceInit: {\n fetch: async (url: string | URL | Request, init?: RequestInit) => {\n // 添加认证头\n const headers = {\n ...init?.headers,\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(url, { ...init, headers });\n },\n },\n requestInit: {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n },\n };\n\n this.transport = new SSEClientTransport(\n new URL(this.config.url),\n sseOptions\n );\n\n // 创建 MCP 客户端\n this.client = new Client(\n {\n name: \"xiaozhi-modelscope-client\",\n version: \"1.0.0\",\n },\n {\n capabilities: {},\n }\n );\n\n // 连接到服务器\n logger.info(`正在连接到 ${this.config.url}`);\n await this.client.connect(this.transport);\n logger.info(`成功连接到 ModelScope MCP 服务器:${this.name}`);\n\n // 获取工具列表\n await this.refreshTools();\n\n this.initialized = true;\n logger.info(\n `${this.name} ModelScope 客户端已就绪,共 ${this.tools.length} 个工具`\n );\n } catch (error) {\n logger.error(\n `启动 ModelScope MCP 客户端 ${this.name} 失败:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n throw error;\n }\n }\n\n async refreshTools(): Promise<void> {\n try {\n if (!this.client) {\n throw new Error(\"客户端未初始化\");\n }\n\n const result = await this.client.listTools();\n this.originalTools = result.tools || [];\n\n // 为每个工具生成带前缀的名称\n this.tools = this.originalTools.map((tool) => ({\n ...tool,\n name: this.generatePrefixedToolName(tool.name),\n }));\n\n logger.info(\n `${this.name} 加载了 ${this.originalTools.length} 个工具:${this.originalTools\n .map((t) => t.name)\n .join(\", \")}`\n );\n } catch (error) {\n logger.error(\n `从 ${this.name} 获取工具失败:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n this.tools = [];\n this.originalTools = [];\n }\n }\n\n async callTool(prefixedName: string, arguments_: any): Promise<any> {\n try {\n if (!this.client) {\n throw new Error(\"客户端未初始化\");\n }\n\n // 将前缀名称转换回原始名称\n const originalName = this.getOriginalToolName(prefixedName);\n if (!originalName) {\n throw new Error(`无效的工具名称格式:${prefixedName}`);\n }\n\n logger.info(\n `调用 ModelScope 工具 ${originalName},参数:${JSON.stringify(\n arguments_\n )}`\n );\n\n const result = await this.client.callTool({\n name: originalName,\n arguments: arguments_,\n });\n\n logger.info(\n `ModelScope 工具调用返回: ${JSON.stringify(result).substring(0, 500)}...`\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 async stop(): Promise<void> {\n if (this.client) {\n logger.info(`正在停止 ${this.name} ModelScope 客户端`);\n try {\n await this.client.close();\n } catch (error) {\n logger.error(\n `关闭客户端时出错:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n this.client = null;\n this.transport = null;\n }\n this.initialized = false;\n }\n}\n"],"mappings":"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","names":["spawn","readFileSync","dirname","resolve","process","fileURLToPath","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","serverName","serverConfig","sc","toolName","endpoint","newConfig","localConfig","newMcpServers","toolsConfig","enabled","description","configJson","writeFileSync","connectionConfig","newConnectionConfig","interval","timeout","modelScopeConfig","newModelScopeConfig","apiKey","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","Client","SSEClientTransport","EventSource","EventSource","logger","ModelScopeMCPClient","__name","name","config","originalToolName","prefixedToolName","prefix","token","configManager","sseOptions","url","init","headers","SSEClientTransport","Client","error","result","tool","prefixedName","arguments_","originalName","__dirname","dirname","fileURLToPath","logger","process","MCPClient","__name","name","config","command","args","originalToolName","prefixedToolName","prefix","env","resolvedCommand","resolvedArgs","spawnOptions","userWorkingDir","spawn","data","code","signal","error","lines","line","message","pendingRequest","resolve","reject","method","params","id","request","initResult","notification","result","allPrefixedTools","tool","t","allTools","originalName","configManager","currentConfig","toolsConfig","existingConfig","toolName","existing","newConfig","prefixedName","arguments_","loadMCPConfig","mcpServers","legacyConfigPath","readFileSync","configData","MCPServerProxy","clientPromises","serverName","serverConfig","client","ModelScopeMCPClient","results","successCount","clientName","servers","refreshPromises","JSONRPCServer","proxy","parsedMessage","response","_params","tools","main","jsonrpcServer","cleanup","messageBuffer","currentFileUrl","scriptPath","argv1Path"]}
|
|
1
|
+
{"version":3,"sources":["../src/mcpServerProxy.ts","../src/configManager.ts","../src/logger.ts","../src/modelScopeMCPClient.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * MCP Server Proxy - JavaScript Implementation\n * Provides a proxy to aggregate multiple MCP servers dynamically from configuration\n * Version: 0.3.0 - Manual JSON-RPC 2.0 implementation (no SDK) with dynamic config\n */\n\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n type LocalMCPServerConfig,\n type MCPServerConfig,\n type MCPToolConfig,\n type SSEMCPServerConfig,\n configManager,\n} from \"./configManager\";\nimport { logger as globalLogger } from \"./logger\";\nimport { ModelScopeMCPClient } from \"./modelScopeMCPClient\";\n\n// ESM 兼容的 __dirname\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// 为 MCPProxy 创建带标签的 logger\nconst logger = globalLogger.withTag(\"MCPProxy\");\n\n// 如果在守护进程模式下运行,初始化日志文件\nif (process.env.XIAOZHI_DAEMON === \"true\" && process.env.XIAOZHI_CONFIG_DIR) {\n globalLogger.initLogFile(process.env.XIAOZHI_CONFIG_DIR);\n globalLogger.enableFileLogging(true);\n}\n\n// Type definitions\n\ninterface Tool {\n name: string;\n description?: string;\n inputSchema?: any;\n}\n\ninterface PendingRequest {\n resolve: (value: any) => void;\n reject: (reason: any) => void;\n}\n\n// 定义 MCP 客户端接口\nexport interface IMCPClient {\n initialized: boolean;\n tools: Tool[];\n originalTools: Tool[];\n start(): Promise<void>;\n refreshTools(): Promise<void>;\n callTool(toolName: string, arguments_: any): Promise<any>;\n stop(): void | Promise<void>;\n getOriginalToolName(prefixedToolName: string): string | null;\n}\n\n/**\n * MCP Client for communicating with child MCP servers\n */\nexport class MCPClient implements IMCPClient {\n private name: string;\n private config: LocalMCPServerConfig;\n private process: ChildProcess | null;\n public initialized: boolean;\n public tools: Tool[];\n public originalTools: Tool[]; // 存储原始工具名称\n private requestId: number;\n private pendingRequests: Map<number, PendingRequest>;\n private messageBuffer: string;\n\n constructor(name: string, config: LocalMCPServerConfig) {\n this.name = name;\n this.config = config;\n this.process = null;\n this.initialized = false;\n this.tools = [];\n this.originalTools = [];\n this.requestId = 1;\n this.pendingRequests = new Map();\n this.messageBuffer = \"\";\n }\n\n /**\n * Resolve command for cross-platform execution\n * On Windows, npm/npx/uvx commands need special handling\n */\n public resolveCommand(\n command: string,\n args: string[]\n ): { resolvedCommand: string; resolvedArgs: string[] } {\n if (process.platform === \"win32\") {\n // On Windows, npm, npx, and uvx are .cmd files or .exe files\n if (command === \"npm\" || command === \"npx\") {\n return {\n resolvedCommand: `${command}.cmd`,\n resolvedArgs: args,\n };\n }\n // uvx is typically installed as uvx.bat on Windows (via pyenv)\n if (command === \"uvx\") {\n return {\n resolvedCommand: \"uvx.bat\",\n resolvedArgs: args,\n };\n }\n }\n\n return {\n resolvedCommand: command,\n resolvedArgs: args,\n };\n }\n\n /**\n * 生成带前缀的工具名称\n * 将服务器名称中的中划线替换为下划线,并添加 xzcli 前缀\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 async start() {\n logger.info(`正在启动 MCP 客户端:${this.name}`);\n\n const { command, args, env } = this.config;\n\n // Handle cross-platform command execution\n const { resolvedCommand, resolvedArgs } = this.resolveCommand(\n command,\n args\n );\n\n const spawnOptions: any = {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n };\n\n // Set working directory to user's current working directory\n // This ensures relative paths in MCP server configs are resolved correctly\n const userWorkingDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();\n spawnOptions.cwd = userWorkingDir;\n\n // Add environment variables if specified\n if (env) {\n spawnOptions.env = { ...process.env, ...env };\n } else {\n spawnOptions.env = { ...process.env };\n }\n\n // On Windows, we need to set shell: true for npm/npx/uvx commands\n if (\n process.platform === \"win32\" &&\n (command === \"npm\" || command === \"npx\" || command === \"uvx\")\n ) {\n spawnOptions.shell = true;\n }\n\n logger.debug(\n `${this.name} 正在生成进程:${resolvedCommand} ${resolvedArgs.join(\" \")} 工作目录:${spawnOptions.cwd}`\n );\n logger.debug(\n `${this.name} 平台:${process.platform},shell 模式:${spawnOptions.shell || false}`\n );\n\n this.process = spawn(resolvedCommand, resolvedArgs, spawnOptions);\n\n // Handle process stdout - parse JSON-RPC messages\n this.process.stdout?.on(\"data\", (data: Buffer) => {\n this.handleStdoutData(data.toString());\n });\n\n // Handle process stderr - log errors\n this.process.stderr?.on(\"data\", (data: Buffer) => {\n logger.debug(`${this.name} 标准错误输出:${data.toString().trim()}`);\n });\n\n // Handle process exit\n this.process.on(\n \"exit\",\n (code: number | null, signal: NodeJS.Signals | null) => {\n logger.error(\n `${this.name} 进程已退出,退出码:${code},信号:${signal}`\n );\n this.initialized = false;\n }\n );\n\n // Handle process error\n this.process.on(\"error\", (error: Error) => {\n logger.error(`${this.name} 进程错误:${error.message}`);\n this.initialized = false;\n });\n\n // Initialize the MCP connection\n await this.initialize();\n }\n\n handleStdoutData(data: string): void {\n this.messageBuffer = `${this.messageBuffer}${data}`;\n\n // Split by newlines and process complete messages\n const lines = this.messageBuffer.split(\"\\n\");\n this.messageBuffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (line.trim()) {\n try {\n const message = JSON.parse(line.trim());\n this.handleMessage(message);\n } catch (error) {\n logger.error(`${this.name} 解析消息失败:${line.trim()}`);\n }\n }\n }\n }\n\n handleMessage(message: any): void {\n logger.debug(\n `${this.name} 收到消息:${JSON.stringify(message).substring(0, 200)}...`\n );\n\n if (message.id && this.pendingRequests.has(message.id)) {\n // This is a response to our request\n const pendingRequest = this.pendingRequests.get(message.id);\n if (pendingRequest) {\n const { resolve, reject } = pendingRequest;\n this.pendingRequests.delete(message.id);\n\n if (message.error) {\n reject(\n new Error(`${message.error.message} (code: ${message.error.code})`)\n );\n } else {\n resolve(message.result);\n }\n }\n }\n // Note: We don't handle notifications or requests from child servers in this proxy\n }\n\n async sendRequest(method: string, params: any = {}): Promise<any> {\n if (!this.process || !this.process.stdin) {\n throw new Error(`${this.name} 进程不可用`);\n }\n\n const id = this.requestId++;\n const request = {\n jsonrpc: \"2.0\",\n id: id,\n method: method,\n params: params,\n };\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, { resolve, reject });\n\n // Set timeout for request\n setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id);\n reject(new Error(`请求超时:${method}`));\n }\n }, 30000); // 30 second timeout\n\n const message = `${JSON.stringify(request)}\\n`;\n logger.debug(`${this.name} 正在发送:${message.trim()}`);\n this.process?.stdin?.write(message);\n });\n }\n\n async initialize() {\n try {\n // Send initialize request\n const initResult = await this.sendRequest(\"initialize\", {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: {\n name: \"MCPProxy\",\n version: \"0.3.0\",\n },\n });\n\n logger.info(\n `${this.name} 已初始化,能力:${JSON.stringify((initResult as any).capabilities)}`\n );\n\n // Send initialized notification\n const notification = {\n jsonrpc: \"2.0\",\n method: \"notifications/initialized\",\n };\n this.process?.stdin?.write(`${JSON.stringify(notification)}\\n`);\n\n // Get tools list\n await this.refreshTools();\n\n this.initialized = true;\n logger.info(`${this.name} 客户端已就绪,共 ${this.tools.length} 个工具`);\n } catch (error) {\n logger.error(\n `初始化 ${this.name} 失败:${error instanceof Error ? error.message : String(error)}`\n );\n throw error;\n }\n }\n\n async refreshTools(): Promise<void> {\n try {\n const result = await this.sendRequest(\"tools/list\");\n this.originalTools = (result as any).tools || [];\n\n // 为每个工具生成带前缀的名称,并应用过滤\n const allPrefixedTools = this.originalTools.map((tool) => ({\n ...tool,\n name: this.generatePrefixedToolName(tool.name),\n }));\n\n // 根据配置过滤工具\n this.tools = this.filterEnabledTools(allPrefixedTools);\n\n // 更新配置文件中的工具列表(如果需要)\n await this.updateToolsConfig();\n\n logger.info(\n `${this.name} 加载了 ${this.originalTools.length} 个工具:${this.originalTools.map((t) => t.name).join(\", \")}`\n );\n logger.info(\n `${this.name} 启用了 ${this.tools.length} 个工具:${this.tools.map((t) => t.name).join(\", \")}`\n );\n } catch (error) {\n logger.error(\n `从 ${this.name} 获取工具失败:${error instanceof Error ? error.message : String(error)}`\n );\n this.tools = [];\n this.originalTools = [];\n }\n }\n\n /**\n * 过滤启用的工具\n */\n private filterEnabledTools(allTools: Tool[]): Tool[] {\n return allTools.filter((tool) => {\n const originalName = this.getOriginalToolName(tool.name);\n if (!originalName) return true; // 如果无法解析原始名称,默认启用\n\n return configManager.isToolEnabled(this.name, originalName);\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} 的工具配置失败:${error instanceof Error ? error.message : String(error)}`\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 return result;\n } catch (error) {\n logger.error(\n `在 ${this.name} 上调用工具 ${prefixedName} (原始名称:${this.getOriginalToolName(prefixedName)}) 失败:${error instanceof Error ? error.message : String(error)}`\n );\n throw error;\n }\n }\n\n stop(): void {\n if (this.process) {\n logger.info(`正在停止 ${this.name} 客户端`);\n this.process.kill(\"SIGTERM\");\n this.process = null;\n }\n this.initialized = false;\n }\n}\n\n/**\n * Configuration loader for MCP servers\n */\nexport function loadMCPConfig(): Record<string, MCPServerConfig> {\n try {\n // 首先尝试从配置管理器读取\n if (configManager.configExists()) {\n const mcpServers = configManager.getMcpServers();\n logger.info(\n `从配置文件加载了 ${Object.keys(mcpServers).length} 个 MCP 服务`\n );\n return mcpServers as Record<string, MCPServerConfig>;\n }\n\n // 如果配置文件不存在,尝试从旧的 mcp_server.json 读取(向后兼容)\n const legacyConfigPath = resolve(__dirname, \"mcp_server.json\");\n if (readFileSync) {\n // 检查是否可以读取文件\n try {\n const configData = readFileSync(legacyConfigPath, \"utf8\");\n const config = JSON.parse(configData);\n\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n throw new Error(\"无效的配置:mcpServers 部分未找到或无效\");\n }\n\n logger.info(\n `从旧配置文件加载了 ${Object.keys(config.mcpServers).length} 个 MCP 服务(建议迁移到新配置格式)`\n );\n return config.mcpServers;\n } catch (legacyError) {\n // 旧配置文件也不存在或无效\n logger.error('配置文件不存在,请运行 \"xiaozhi init\" 初始化配置');\n throw new Error('配置文件不存在,请运行 \"xiaozhi init\" 初始化配置');\n }\n }\n\n throw new Error('配置文件不存在,请运行 \"xiaozhi init\" 初始化配置');\n } catch (error) {\n logger.error(\n `加载 MCP 配置失败:${error instanceof Error ? error.message : String(error)}`\n );\n throw error;\n }\n}\n\n/**\n * MCP Server Proxy - Main proxy server implementation\n */\nexport class MCPServerProxy {\n private clients: Map<string, IMCPClient>;\n private toolMap: Map<string, string>; // Maps tool name to client name\n public initialized: boolean;\n private config: Record<string, MCPServerConfig> | null;\n\n constructor() {\n this.clients = new Map();\n this.toolMap = new Map(); // Maps tool name to client name\n this.initialized = false;\n this.config = null;\n }\n\n async start() {\n logger.info(\"正在启动 MCP 服务代理\");\n\n // Load configuration\n this.config = loadMCPConfig();\n\n // Initialize child MCP clients from configuration\n const clientPromises = [];\n\n for (const [serverName, serverConfig] of Object.entries(this.config)) {\n logger.info(`正在初始化 MCP 客户端:${serverName}`);\n\n let client: IMCPClient;\n\n // 判断是 SSE 类型还是本地类型\n if (\"type\" in serverConfig && serverConfig.type === \"sse\") {\n // ModelScope SSE MCP 服务\n client = new ModelScopeMCPClient(serverName, serverConfig);\n } else {\n // 本地 MCP 服务\n client = new MCPClient(\n serverName,\n serverConfig as LocalMCPServerConfig\n );\n }\n\n this.clients.set(serverName, client);\n clientPromises.push(client.start());\n }\n\n // Start all clients\n try {\n const results = await Promise.allSettled(clientPromises);\n\n // Check for failed clients\n let successCount = 0;\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n const serverName = Object.keys(this.config)[i];\n\n if (result.status === \"fulfilled\") {\n successCount++;\n logger.info(`成功启动 MCP 客户端:${serverName}`);\n } else {\n logger.error(\n `启动 MCP 客户端 ${serverName} 失败:${result.reason.message}`\n );\n // Remove failed client from clients map\n this.clients.delete(serverName);\n }\n }\n\n if (successCount === 0) {\n throw new Error(\"没有成功启动任何 MCP 客户端\");\n }\n\n // Build tool mapping\n this.buildToolMap();\n this.initialized = true;\n\n logger.info(\n `MCP 服务代理初始化成功,启动了 ${successCount}/${Object.keys(this.config).length} 个客户端`\n );\n } catch (error) {\n logger.error(\n `启动 MCP 客户端失败:${error instanceof Error ? error.message : String(error)}`\n );\n throw error;\n }\n }\n\n buildToolMap() {\n this.toolMap.clear();\n\n for (const [clientName, client] of this.clients) {\n for (const tool of client.tools) {\n // 工具名称现在已经带有前缀,应该不会有重复\n // 但我们仍然检查以防万一\n if (this.toolMap.has(tool.name)) {\n logger.error(\n `重复的工具名称:${tool.name} (来自 ${clientName} 和 ${this.toolMap.get(tool.name)})`\n );\n } else {\n this.toolMap.set(tool.name, clientName);\n }\n }\n }\n\n logger.info(`已构建工具映射,共 ${this.toolMap.size} 个工具`);\n logger.debug(`工具映射:${Array.from(this.toolMap.keys()).join(\", \")}`);\n }\n\n getAllTools(): Tool[] {\n const allTools: Tool[] = [];\n for (const client of this.clients.values()) {\n allTools.push(...client.tools);\n }\n return allTools;\n }\n\n /**\n * 获取所有服务器的信息\n */\n getAllServers(): Array<{\n name: string;\n toolCount: number;\n enabledToolCount: number;\n }> {\n const servers: Array<{\n name: string;\n toolCount: number;\n enabledToolCount: number;\n }> = [];\n\n for (const [serverName, client] of this.clients) {\n servers.push({\n name: serverName,\n toolCount: client.originalTools.length,\n enabledToolCount: client.tools.length,\n });\n }\n\n return servers;\n }\n\n /**\n * 获取指定服务器的工具信息\n */\n getServerTools(\n serverName: string\n ): Array<{ name: string; description: string; enabled: boolean }> {\n const client = this.clients.get(serverName);\n if (!client) {\n throw new Error(`未找到服务器 ${serverName}`);\n }\n\n return client.originalTools.map((tool) => ({\n name: tool.name,\n description: tool.description || \"\",\n enabled: configManager.isToolEnabled(serverName, tool.name),\n }));\n }\n\n /**\n * 刷新指定服务器的工具列表\n */\n async refreshServerTools(serverName: string): Promise<void> {\n const client = this.clients.get(serverName);\n if (!client) {\n throw new Error(`未找到服务器 ${serverName}`);\n }\n\n await client.refreshTools();\n this.buildToolMap(); // 重新构建工具映射\n }\n\n /**\n * 刷新所有服务器的工具列表\n */\n async refreshAllTools(): Promise<void> {\n const refreshPromises = Array.from(this.clients.values()).map((client) =>\n client.refreshTools()\n );\n await Promise.allSettled(refreshPromises);\n this.buildToolMap(); // 重新构建工具映射\n }\n\n async callTool(toolName: string, arguments_: any): Promise<any> {\n const clientName = this.toolMap.get(toolName);\n if (!clientName) {\n throw new Error(`未知的工具:${toolName}`);\n }\n\n const client = this.clients.get(clientName);\n if (!client || !client.initialized) {\n throw new Error(`客户端 ${clientName} 不可用`);\n }\n\n return await client.callTool(toolName, arguments_);\n }\n\n stop() {\n logger.info(\"正在停止 MCP 服务代理\");\n for (const client of this.clients.values()) {\n client.stop();\n }\n this.initialized = false;\n }\n}\n\n/**\n * Manual JSON-RPC 2.0 Server Implementation\n */\nexport class JSONRPCServer {\n private proxy: MCPServerProxy;\n private requestId: number;\n\n constructor(proxy: MCPServerProxy) {\n this.proxy = proxy;\n this.requestId = 1;\n }\n\n async handleMessage(message: string): Promise<string | null> {\n try {\n const parsedMessage = JSON.parse(message);\n logger.debug(\n `收到请求:${JSON.stringify(parsedMessage).substring(0, 200)}...`\n );\n\n if (parsedMessage.method) {\n if (parsedMessage.id !== undefined) {\n // This is a request\n const response = await this.handleRequest(parsedMessage);\n return JSON.stringify(response);\n }\n // This is a notification\n await this.handleNotification(parsedMessage);\n return null; // No response for notifications\n }\n throw new Error(\"无效的 JSON-RPC 消息\");\n } catch (error) {\n logger.error(\n `处理消息时出错:${error instanceof Error ? error.message : String(error)}`\n );\n return JSON.stringify({\n jsonrpc: \"2.0\",\n id: null,\n error: {\n code: -32700,\n message: \"解析错误\",\n },\n });\n }\n }\n\n async handleRequest(request: any): Promise<any> {\n const { id, method, params = {} } = request;\n\n try {\n let result: any;\n\n switch (method) {\n case \"initialize\":\n result = await this.handleInitialize(params);\n break;\n case \"tools/list\":\n result = await this.handleToolsList(params);\n break;\n case \"tools/call\":\n result = await this.handleToolsCall(params);\n break;\n case \"ping\":\n result = await this.handlePing(params);\n break;\n default:\n throw new Error(`未知的方法:${method}`);\n }\n\n return {\n jsonrpc: \"2.0\",\n id: id,\n result: result,\n };\n } catch (error) {\n logger.error(\n `处理请求 ${method} 时出错:${error instanceof Error ? error.message : String(error)}`\n );\n return {\n jsonrpc: \"2.0\",\n id: id,\n error: {\n code: -32603,\n message: error instanceof Error ? error.message : String(error),\n },\n };\n }\n }\n\n async handleNotification(notification: any): Promise<void> {\n const { method } = notification;\n\n switch (method) {\n case \"notifications/initialized\":\n logger.info(\"客户端发送了初始化通知\");\n break;\n default:\n logger.debug(`收到通知:${method}`);\n break;\n }\n }\n\n async handleInitialize(params: any): Promise<any> {\n logger.info(`收到客户端的初始化请求:${JSON.stringify(params.clientInfo)}`);\n\n return {\n protocolVersion: \"2024-11-05\",\n capabilities: {\n tools: {\n listChanged: false,\n },\n },\n serverInfo: {\n name: \"MCPServerProxy\",\n version: \"0.3.0\",\n },\n };\n }\n\n async handleToolsList(_params: any): Promise<any> {\n if (!this.proxy.initialized) {\n throw new Error(\"代理未初始化\");\n }\n\n const tools = this.proxy.getAllTools();\n logger.info(`返回 ${tools.length} 个工具`);\n\n return {\n tools: tools,\n };\n }\n\n async handleToolsCall(params: any): Promise<any> {\n const { name, arguments: args } = params;\n\n if (!name) {\n throw new Error(\"工具名称是必需的\");\n }\n\n logger.info(`调用工具:${name},参数:${JSON.stringify(args)}`);\n\n const result = await this.proxy.callTool(name, args || {});\n\n // 添加调试日志\n logger.info(`工具调用结果类型: ${typeof result}`);\n logger.info(`工具调用结果: ${JSON.stringify(result).substring(0, 500)}...`);\n\n return result;\n }\n\n async handlePing(_params: any): Promise<any> {\n logger.debug(\"收到 ping 请求\");\n return {}; // Empty response for ping\n }\n}\n\n/**\n * Main function to start the MCP Server Proxy\n */\nasync function main() {\n logger.info(\"正在启动 MCP 服务代理\");\n\n // Create and start the proxy\n const proxy = new MCPServerProxy();\n const jsonrpcServer = new JSONRPCServer(proxy);\n\n // Setup graceful shutdown\n const cleanup = () => {\n logger.info(\"正在关闭 MCP 服务代理\");\n proxy.stop();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n\n try {\n // Start the proxy (initialize child clients)\n await proxy.start();\n\n // Handle stdin/stdout communication\n process.stdin.setEncoding(\"utf8\");\n\n let messageBuffer = \"\";\n\n process.stdin.on(\"data\", async (data) => {\n messageBuffer = `${messageBuffer}${data}`;\n\n // Split by newlines and process complete messages\n const lines = messageBuffer.split(\"\\n\");\n messageBuffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (line.trim()) {\n try {\n const response = await jsonrpcServer.handleMessage(line.trim());\n if (response) {\n process.stdout.write(`${response}\\n`);\n }\n } catch (error) {\n logger.error(\n `处理消息时出错:${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n }\n });\n\n process.stdin.on(\"end\", () => {\n logger.info(\"标准输入已关闭,正在关闭\");\n cleanup();\n });\n\n logger.info(\"MCP 服务代理正在通过 stdio 运行\");\n } catch (error) {\n logger.error(\n `启动 MCP 服务代理失败:${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n }\n}\n\n// Run the server if this file is executed directly\n// Use fileURLToPath to properly handle Windows paths\nconst currentFileUrl = import.meta.url;\nconst scriptPath = fileURLToPath(currentFileUrl);\nconst argv1Path = process.argv[1];\n\nif (scriptPath === argv1Path) {\n main().catch((error) => {\n logger.error(\n `未处理的错误:${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n });\n}\n","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// ModelScope SSE MCP 服务配置\nexport interface SSEMCPServerConfig {\n type: \"sse\";\n url: string;\n}\n\n// 统一的 MCP 服务配置\nexport type MCPServerConfig = LocalMCPServerConfig | SSEMCPServerConfig;\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}\n\nexport interface AppConfig {\n mcpEndpoint: 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 || typeof configObj.mcpEndpoint !== \"string\") {\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 // 检查是否是 SSE 类型\n if (sc.type === \"sse\") {\n // SSE 类型的验证\n if (!sc.url || typeof sc.url !== \"string\") {\n throw new Error(\n `配置文件格式错误:mcpServers.${serverName}.url 无效`\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 */\n public getMcpEndpoint(): string {\n const config = this.getConfig();\n return 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): void {\n if (!endpoint || typeof endpoint !== \"string\") {\n throw new Error(\"MCP 端点必须是非空字符串\");\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 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 { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { SSEClientTransport } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport { EventSource } from \"eventsource\";\nimport { type SSEMCPServerConfig, configManager } from \"./configManager\";\nimport { logger as globalLogger } from \"./logger\";\nimport type { IMCPClient } from \"./mcpServerProxy\";\n\n// 全局 polyfill EventSource\n(global as any).EventSource = EventSource;\n\n// 为 ModelScope MCP 创建带标签的 logger\nconst logger = globalLogger.withTag(\"ModelScopeMCP\");\n\ninterface Tool {\n name: string;\n description?: string;\n inputSchema?: any;\n}\n\n/**\n * ModelScope MCP Client for SSE connections\n */\nexport class ModelScopeMCPClient implements IMCPClient {\n private name: string;\n private config: SSEMCPServerConfig;\n private client: Client | null = null;\n private transport: SSEClientTransport | null = null;\n public initialized = false;\n public tools: Tool[] = [];\n public originalTools: Tool[] = [];\n\n constructor(name: string, config: SSEMCPServerConfig) {\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 async start() {\n logger.info(`正在启动 ModelScope MCP 客户端:${this.name}`);\n\n try {\n // 从配置或环境变量获取 API Token\n const token = configManager.getModelScopeApiKey();\n if (!token || token === \"\") {\n throw new Error(\n \"未设置 ModelScope API Key。请在配置文件中设置 modelscope.apiKey 或设置 MODELSCOPE_API_TOKEN 环境变量。\"\n );\n }\n\n // 创建 SSE 传输层\n const sseOptions = {\n eventSourceInit: {\n fetch: async (url: string | URL | Request, init?: RequestInit) => {\n // 添加认证头\n const headers = {\n ...init?.headers,\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(url, { ...init, headers });\n },\n },\n requestInit: {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n },\n };\n\n this.transport = new SSEClientTransport(\n new URL(this.config.url),\n sseOptions\n );\n\n // 创建 MCP 客户端\n this.client = new Client(\n {\n name: \"xiaozhi-modelscope-client\",\n version: \"1.0.0\",\n },\n {\n capabilities: {},\n }\n );\n\n // 连接到服务器\n logger.info(`正在连接到 ${this.config.url}`);\n await this.client.connect(this.transport);\n logger.info(`成功连接到 ModelScope MCP 服务器:${this.name}`);\n\n // 获取工具列表\n await this.refreshTools();\n\n this.initialized = true;\n logger.info(\n `${this.name} ModelScope 客户端已就绪,共 ${this.tools.length} 个工具`\n );\n } catch (error) {\n logger.error(\n `启动 ModelScope MCP 客户端 ${this.name} 失败:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n throw error;\n }\n }\n\n async refreshTools(): Promise<void> {\n try {\n if (!this.client) {\n throw new Error(\"客户端未初始化\");\n }\n\n const result = await this.client.listTools();\n this.originalTools = result.tools || [];\n\n // 为每个工具生成带前缀的名称\n this.tools = this.originalTools.map((tool) => ({\n ...tool,\n name: this.generatePrefixedToolName(tool.name),\n }));\n\n logger.info(\n `${this.name} 加载了 ${this.originalTools.length} 个工具:${this.originalTools\n .map((t) => t.name)\n .join(\", \")}`\n );\n } catch (error) {\n logger.error(\n `从 ${this.name} 获取工具失败:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n this.tools = [];\n this.originalTools = [];\n }\n }\n\n async callTool(prefixedName: string, arguments_: any): Promise<any> {\n try {\n if (!this.client) {\n throw new Error(\"客户端未初始化\");\n }\n\n // 将前缀名称转换回原始名称\n const originalName = this.getOriginalToolName(prefixedName);\n if (!originalName) {\n throw new Error(`无效的工具名称格式:${prefixedName}`);\n }\n\n logger.info(\n `调用 ModelScope 工具 ${originalName},参数:${JSON.stringify(\n arguments_\n )}`\n );\n\n const result = await this.client.callTool({\n name: originalName,\n arguments: arguments_,\n });\n\n logger.info(\n `ModelScope 工具调用返回: ${JSON.stringify(result).substring(0, 500)}...`\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 async stop(): Promise<void> {\n if (this.client) {\n logger.info(`正在停止 ${this.name} ModelScope 客户端`);\n try {\n await this.client.close();\n } catch (error) {\n logger.error(\n `关闭客户端时出错:${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n this.client = null;\n this.transport = null;\n }\n this.initialized = false;\n }\n}\n"],"mappings":"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","names":["spawn","readFileSync","dirname","resolve","process","fileURLToPath","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","serverName","serverConfig","sc","toolName","endpoint","newConfig","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","Client","SSEClientTransport","EventSource","EventSource","logger","ModelScopeMCPClient","__name","name","config","originalToolName","prefixedToolName","prefix","token","configManager","sseOptions","url","init","headers","SSEClientTransport","Client","error","result","tool","prefixedName","arguments_","originalName","__dirname","dirname","fileURLToPath","logger","process","MCPClient","__name","name","config","command","args","originalToolName","prefixedToolName","prefix","env","resolvedCommand","resolvedArgs","spawnOptions","userWorkingDir","spawn","data","code","signal","error","lines","line","message","pendingRequest","resolve","reject","method","params","id","request","initResult","notification","result","allPrefixedTools","tool","t","allTools","originalName","configManager","currentConfig","toolsConfig","existingConfig","toolName","existing","newConfig","prefixedName","arguments_","loadMCPConfig","mcpServers","legacyConfigPath","readFileSync","configData","MCPServerProxy","clientPromises","serverName","serverConfig","client","ModelScopeMCPClient","results","successCount","clientName","servers","refreshPromises","JSONRPCServer","proxy","parsedMessage","response","_params","tools","main","jsonrpcServer","cleanup","messageBuffer","currentFileUrl","scriptPath","argv1Path"]}
|
package/dist/webServer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)}}enableFileLogging(t){t&&!this.writeStream&&this.logFilePath?this.writeStream=u.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"}):!t&&this.writeStream&&(this.writeStream.end(),this.writeStream=null)}info(t,...e){this.consolaInstance.info(t,...e),this.logToFile("info",t,...e)}success(t,...e){this.consolaInstance.success(t,...e),this.logToFile("success",t,...e)}warn(t,...e){this.consolaInstance.warn(t,...e),this.logToFile("warn",t,...e)}error(t,...e){this.consolaInstance.error(t,...e),this.logToFile("error",t,...e)}debug(t,...e){this.consolaInstance.debug(t,...e),this.logToFile("debug",t,...e)}log(t,...e){this.consolaInstance.log(t,...e),this.logToFile("log",t,...e)}withTag(t){return this}close(){this.writeStream&&(this.writeStream.end(),this.writeStream=null)}},V=new d;var
|
|
1
|
+
var M=Object.defineProperty;var g=(s,t)=>M(s,"name",{value:t,configurable:!0});import{existsSync as v}from"fs";import{readFile as I}from"fs/promises";import{createServer as N}from"http";import{dirname as $,join as p}from"path";import{parse as J}from"url";import{fileURLToPath as D}from"url";import{WebSocketServer as k}from"ws";import{copyFileSync as O,existsSync as w,readFileSync as x,writeFileSync as U}from"fs";import{dirname as F,resolve as y}from"path";import{fileURLToPath as R}from"url";var H=F(R(import.meta.url)),S={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},b=class s{static{g(this,"ConfigManager")}static instance;defaultConfigPath;config=null;constructor(){this.defaultConfigPath=y(H,"xiaozhi.config.default.json")}getConfigFilePath(){let t=process.env.XIAOZHI_CONFIG_DIR||process.cwd();return y(t,"xiaozhi.config.json")}static getInstance(){return s.instance||(s.instance=new s),s.instance}configExists(){let t=this.getConfigFilePath();return w(t)}initConfig(){if(!w(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 t=this.getConfigFilePath();O(this.defaultConfigPath,t),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 t=this.getConfigFilePath(),e=x(t,"utf8"),o=JSON.parse(e);return this.validateConfig(o),o}catch(t){throw t instanceof SyntaxError?new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF: ${t.message}`):t}}validateConfig(t){if(!t||typeof t!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A\u6839\u5BF9\u8C61\u65E0\u6548");let e=t;if(!e.mcpEndpoint||typeof e.mcpEndpoint!="string")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5B57\u6BB5\u65E0\u6548");if(!e.mcpServers||typeof e.mcpServers!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers \u5B57\u6BB5\u65E0\u6548");for(let[o,i]of Object.entries(e.mcpServers)){if(!i||typeof i!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o} \u65E0\u6548`);let n=i;if(n.type==="sse"){if(!n.url||typeof n.url!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.url \u65E0\u6548`)}else{if(!n.command||typeof n.command!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.command \u65E0\u6548`);if(!Array.isArray(n.args))throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.args \u5FC5\u987B\u662F\u6570\u7EC4`);if(n.env&&typeof n.env!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.env \u5FC5\u987B\u662F\u5BF9\u8C61`)}}}getConfig(){return this.config||(this.config=this.loadConfig()),JSON.parse(JSON.stringify(this.config))}getMcpEndpoint(){return this.getConfig().mcpEndpoint}getMcpServers(){return this.getConfig().mcpServers}getMcpServerConfig(){return this.getConfig().mcpServerConfig||{}}getServerToolsConfig(t){return this.getMcpServerConfig()[t]?.tools||{}}isToolEnabled(t,e){return this.getServerToolsConfig(t)[e]?.enable!==!1}updateMcpEndpoint(t){if(!t||typeof t!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let o={...this.getConfig(),mcpEndpoint:t};this.saveConfig(o)}updateMcpServer(t,e){if(!t||typeof t!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if("type"in e&&e.type==="sse"){if(!e.url||typeof e.url!="string")throw new Error("SSE \u670D\u52A1\u914D\u7F6E\u7684 url \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else{let n=e;if(!n.command||typeof n.command!="string")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 command \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(!Array.isArray(n.args))throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 args \u5B57\u6BB5\u5FC5\u987B\u662F\u6570\u7EC4");if(n.env&&typeof n.env!="object")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 env \u5B57\u6BB5\u5FC5\u987B\u662F\u5BF9\u8C61")}let o=this.getConfig(),i={...o,mcpServers:{...o.mcpServers,[t]:e}};this.saveConfig(i)}removeMcpServer(t){if(!t||typeof t!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let e=this.getConfig();if(!e.mcpServers[t])throw new Error(`\u670D\u52A1 ${t} \u4E0D\u5B58\u5728`);let o={...e.mcpServers};delete o[t];let i={...e,mcpServers:o};this.saveConfig(i)}updateServerToolsConfig(t,e){let i={...this.getConfig()};i.mcpServerConfig||(i.mcpServerConfig={}),i.mcpServerConfig[t]={tools:e},this.saveConfig(i)}setToolEnabled(t,e,o,i){let r={...this.getConfig()};r.mcpServerConfig||(r.mcpServerConfig={}),r.mcpServerConfig[t]||(r.mcpServerConfig[t]={tools:{}}),r.mcpServerConfig[t].tools[e]={enable:o,...i&&{description:i}},this.saveConfig(r)}saveConfig(t){try{this.validateConfig(t);let e=this.getConfigFilePath(),o=JSON.stringify(t,null,2);U(e,o,"utf8"),this.config=t}catch(e){throw new Error(`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}reloadConfig(){this.config=null}getConfigPath(){return this.getConfigFilePath()}getDefaultConfigPath(){return this.defaultConfigPath}getConnectionConfig(){let e=this.getConfig().connection||{};return{heartbeatInterval:e.heartbeatInterval??S.heartbeatInterval,heartbeatTimeout:e.heartbeatTimeout??S.heartbeatTimeout,reconnectInterval:e.reconnectInterval??S.reconnectInterval}}getHeartbeatInterval(){return this.getConnectionConfig().heartbeatInterval}getHeartbeatTimeout(){return this.getConnectionConfig().heartbeatTimeout}getReconnectInterval(){return this.getConnectionConfig().reconnectInterval}updateConnectionConfig(t){let e=this.getConfig(),i={...e.connection||{},...t},n={...e,connection:i};this.saveConfig(n)}setHeartbeatInterval(t){if(t<=0)throw new Error("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatInterval:t})}setHeartbeatTimeout(t){if(t<=0)throw new Error("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatTimeout:t})}setReconnectInterval(t){if(t<=0)throw new Error("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({reconnectInterval:t})}getModelScopeConfig(){return this.getConfig().modelscope||{}}getModelScopeApiKey(){return this.getModelScopeConfig().apiKey||process.env.MODELSCOPE_API_TOKEN}updateModelScopeConfig(t){let e=this.getConfig(),i={...e.modelscope||{},...t},n={...e,modelscope:i};this.saveConfig(n)}setModelScopeApiKey(t){if(!t||typeof t!="string")throw new Error("API Key \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");this.updateModelScopeConfig({apiKey:t})}getWebUIConfig(){return this.getConfig().webUI||{}}getWebUIPort(){return this.getWebUIConfig().port??9999}updateWebUIConfig(t){let e=this.getConfig(),i={...e.webUI||{},...t},n={...e,webUI:i};this.saveConfig(n)}setWebUIPort(t){if(!Number.isInteger(t)||t<=0||t>65535)throw new Error("\u7AEF\u53E3\u53F7\u5FC5\u987B\u662F 1-65535 \u4E4B\u95F4\u7684\u6574\u6570");this.updateWebUIConfig({port:t})}},a=b.getInstance();import u from"fs";import A from"path";import l from"chalk";import{createConsola as W}from"consola";function j(s){let t=s.getFullYear(),e=String(s.getMonth()+1).padStart(2,"0"),o=String(s.getDate()).padStart(2,"0"),i=String(s.getHours()).padStart(2,"0"),n=String(s.getMinutes()).padStart(2,"0"),r=String(s.getSeconds()).padStart(2,"0");return`${t}-${e}-${o} ${i}:${n}:${r}`}g(j,"formatDateTime");var d=class{static{g(this,"Logger")}logFilePath=null;writeStream=null;consolaInstance;isDaemonMode;constructor(){this.isDaemonMode=process.env.XIAOZHI_DAEMON==="true",this.consolaInstance=W({formatOptions:{date:!1,colors:!0,compact:!0},fancy:!1});let t=this.isDaemonMode;this.consolaInstance.setReporters([{log:g(e=>{let o={info:"INFO",success:"SUCCESS",warn:"WARN",error:"ERROR",debug:"DEBUG",log:"LOG"},i={info:l.blue,success:l.green,warn:l.yellow,error:l.red,debug:l.gray,log:g(f=>f,"log")},n=o[e.type]||e.type.toUpperCase(),r=i[e.type]||(f=>f),c=j(new Date),C=r(`[${n}]`),m=`[${c}] ${C} ${e.args.join(" ")}`;if(!t)try{console.error(m)}catch(f){if(f instanceof Error&&f.message?.includes("EPIPE"))return;throw f}},"log")}])}initLogFile(t){this.logFilePath=A.join(t,"xiaozhi.log"),u.existsSync(this.logFilePath)||u.writeFileSync(this.logFilePath,""),this.writeStream=u.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"})}logToFile(t,e,...o){if(this.writeStream){let n=`[${new Date().toISOString()}] [${t.toUpperCase()}] ${e}`,r=o.length>0?`${n} ${o.map(c=>typeof c=="object"?JSON.stringify(c):String(c)).join(" ")}`:n;this.writeStream.write(`${r}
|
|
2
|
+
`)}}enableFileLogging(t){t&&!this.writeStream&&this.logFilePath?this.writeStream=u.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"}):!t&&this.writeStream&&(this.writeStream.end(),this.writeStream=null)}info(t,...e){this.consolaInstance.info(t,...e),this.logToFile("info",t,...e)}success(t,...e){this.consolaInstance.success(t,...e),this.logToFile("success",t,...e)}warn(t,...e){this.consolaInstance.warn(t,...e),this.logToFile("warn",t,...e)}error(t,...e){this.consolaInstance.error(t,...e),this.logToFile("error",t,...e)}debug(t,...e){this.consolaInstance.debug(t,...e),this.logToFile("debug",t,...e)}log(t,...e){this.consolaInstance.log(t,...e),this.logToFile("log",t,...e)}withTag(t){return this}close(){this.writeStream&&(this.writeStream.end(),this.writeStream=null)}},V=new d;var T=class{static{g(this,"WebServer")}httpServer;wss;logger;port;clientInfo={status:"disconnected",mcpEndpoint:"",activeMCPServers:[]};heartbeatTimeout;HEARTBEAT_TIMEOUT=35e3;constructor(t){if(t===void 0)try{this.port=a.getWebUIPort()}catch{this.port=9999}else this.port=t;this.logger=new d,this.httpServer=N((e,o)=>{this.handleHttpRequest(e,o)}),this.wss=new k({server:this.httpServer}),this.setupWebSocket()}async handleHttpRequest(t,e){let{pathname:o}=J(t.url||"",!0);if(e.setHeader("Access-Control-Allow-Origin","*"),e.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, OPTIONS"),e.setHeader("Access-Control-Allow-Headers","Content-Type"),t.method==="OPTIONS"){e.writeHead(200),e.end();return}try{if(t.method==="GET"&&!o?.startsWith("/api/")){await this.serveStaticFile(o||"/",e);return}if(o==="/api/config"&&t.method==="GET"){let i=a.getConfig();e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify(i))}else if(o==="/api/config"&&t.method==="PUT"){let i="";t.on("data",n=>{i+=n.toString()}),t.on("end",async()=>{try{let n=JSON.parse(i);this.updateConfig(n),e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify({success:!0})),this.broadcastConfigUpdate(n)}catch(n){e.writeHead(400,{"Content-Type":"application/json"}),e.end(JSON.stringify({error:n instanceof Error?n.message:String(n)}))}})}else o==="/api/status"&&t.method==="GET"?(e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify(this.clientInfo))):(e.writeHead(404),e.end("Not Found"))}catch(i){this.logger.error("HTTP request error:",i),e.writeHead(500,{"Content-Type":"application/json"}),e.end(JSON.stringify({error:"Internal Server Error"}))}}async serveStaticFile(t,e){try{let o=$(D(import.meta.url)),n=[p(o,"..","web","dist"),p(o,"..","web"),p(process.cwd(),"web","dist"),p(process.cwd(),"web")].find(h=>v(h));if(!n){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(`
|
|
3
3
|
<!DOCTYPE html>
|
|
4
4
|
<html>
|
|
5
5
|
<head>
|
|
@@ -19,5 +19,5 @@ var I=Object.defineProperty;var g=(s,t)=>I(s,"name",{value:t,configurable:!0});i
|
|
|
19
19
|
</div>
|
|
20
20
|
</body>
|
|
21
21
|
</html>
|
|
22
|
-
`);return}let r=t;if(r==="/"&&(r="/index.html"),r.includes("..")){e.writeHead(403),e.end("Forbidden");return}let c=p(n,r);if(!
|
|
22
|
+
`);return}let r=t;if(r==="/"&&(r="/index.html"),r.includes("..")){e.writeHead(403),e.end("Forbidden");return}let c=p(n,r);if(!v(c)){let h=p(n,"index.html");if(v(h)){let P=await I(h);e.writeHead(200,{"Content-Type":"text/html"}),e.end(P)}else e.writeHead(404),e.end("Not Found");return}let C=await I(c),m=c.split(".").pop()?.toLowerCase(),E={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";e.writeHead(200,{"Content-Type":E}),e.end(C)}catch(o){this.logger.error("Serve static file error:",o),e.writeHead(500),e.end("Internal Server Error")}}setupWebSocket(){this.wss.on("connection",t=>{this.logger.debug("WebSocket client connected"),t.on("message",async e=>{try{let o=JSON.parse(e.toString());await this.handleWebSocketMessage(t,o)}catch(o){this.logger.error("WebSocket message error:",o),t.send(JSON.stringify({type:"error",error:o instanceof Error?o.message:String(o)}))}}),t.on("close",()=>{this.logger.debug("WebSocket client disconnected")}),this.sendInitialData(t)})}async handleWebSocketMessage(t,e){switch(e.type){case"getConfig":{let o=a.getConfig();t.send(JSON.stringify({type:"config",data:o}));break}case"updateConfig":this.updateConfig(e.config),this.broadcastConfigUpdate(e.config);break;case"getStatus":t.send(JSON.stringify({type:"status",data:this.clientInfo}));break;case"clientStatus":this.updateClientInfo(e.data),this.broadcastStatusUpdate();break}}async sendInitialData(t){let e=a.getConfig();t.send(JSON.stringify({type:"config",data:e})),t.send(JSON.stringify({type:"status",data:this.clientInfo}))}broadcastConfigUpdate(t){let e=JSON.stringify({type:"configUpdate",data:t});for(let o of this.wss.clients)o.readyState===1&&o.send(e)}broadcastStatusUpdate(){let t=JSON.stringify({type:"statusUpdate",data:this.clientInfo});for(let e of this.wss.clients)e.readyState===1&&e.send(t)}updateClientInfo(t){this.clientInfo={...this.clientInfo,...t},t.lastHeartbeat&&(this.clientInfo.lastHeartbeat=Date.now()),t.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(t){t.mcpEndpoint!==a.getMcpEndpoint()&&a.updateMcpEndpoint(t.mcpEndpoint);let e=a.getMcpServers();for(let[o,i]of Object.entries(t.mcpServers))JSON.stringify(e[o])!==JSON.stringify(i)&&a.updateMcpServer(o,i);for(let o of Object.keys(e))o in t.mcpServers||a.removeMcpServer(o);if(t.connection&&a.updateConnectionConfig(t.connection),t.modelscope&&a.updateModelScopeConfig(t.modelscope),t.webUI&&a.updateWebUIConfig(t.webUI),t.mcpServerConfig)for(let[o,i]of Object.entries(t.mcpServerConfig))for(let[n,r]of Object.entries(i.tools))a.setToolEnabled(o,n,r.enable)}updateStatus(t){this.updateClientInfo(t),this.broadcastStatusUpdate()}start(){return new Promise((t,e)=>{this.httpServer.listen(this.port,()=>{this.logger.info(`Web server listening on http://localhost:${this.port}`),t()}).on("error",e)})}stop(){return new Promise(t=>{this.heartbeatTimeout&&(clearTimeout(this.heartbeatTimeout),this.heartbeatTimeout=void 0);for(let e of this.wss.clients)e.terminate();this.wss.close(()=>{this.httpServer.close(()=>{this.logger.info("Web server stopped"),t()}),setTimeout(()=>{this.logger.info("Web server force stopped"),t()},2e3)})})}};export{T as WebServer};
|
|
23
23
|
//# sourceMappingURL=webServer.js.map
|