xiaozhi-client 1.4.0-beta.3 → 1.4.0

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.
@@ -23,12 +23,16 @@ interface ConnectionConfig {
23
23
  interface ModelScopeConfig {
24
24
  apiKey?: string;
25
25
  }
26
+ interface WebUIConfig {
27
+ port?: number;
28
+ }
26
29
  interface AppConfig {
27
30
  mcpEndpoint: string;
28
31
  mcpServers: Record<string, MCPServerConfig>;
29
32
  mcpServerConfig?: Record<string, MCPServerToolsConfig>;
30
33
  connection?: ConnectionConfig;
31
34
  modelscope?: ModelScopeConfig;
35
+ webUI?: WebUIConfig;
32
36
  }
33
37
  /**
34
38
  * 配置管理类
@@ -173,7 +177,23 @@ declare class ConfigManager {
173
177
  * 设置 ModelScope API Key
174
178
  */
175
179
  setModelScopeApiKey(apiKey: string): void;
180
+ /**
181
+ * 获取 Web UI 配置
182
+ */
183
+ getWebUIConfig(): Readonly<WebUIConfig>;
184
+ /**
185
+ * 获取 Web UI 端口号
186
+ */
187
+ getWebUIPort(): number;
188
+ /**
189
+ * 更新 Web UI 配置
190
+ */
191
+ updateWebUIConfig(webUIConfig: Partial<WebUIConfig>): void;
192
+ /**
193
+ * 设置 Web UI 端口号
194
+ */
195
+ setWebUIPort(port: number): void;
176
196
  }
177
197
  declare const configManager: ConfigManager;
178
198
 
179
- export { type AppConfig, ConfigManager, type ConnectionConfig, type LocalMCPServerConfig, type MCPServerConfig, type MCPServerToolsConfig, type MCPToolConfig, type ModelScopeConfig, type SSEMCPServerConfig, configManager };
199
+ export { type AppConfig, ConfigManager, type ConnectionConfig, type LocalMCPServerConfig, type MCPServerConfig, type MCPServerToolsConfig, type MCPToolConfig, type ModelScopeConfig, type SSEMCPServerConfig, type WebUIConfig, configManager };
@@ -1,2 +1,2 @@
1
- var C=Object.defineProperty;var f=(r,o)=>C(r,"name",{value:o,configurable:!0});import{copyFileSync as l,existsSync as a,readFileSync as h,writeFileSync as u}from"fs";import{dirname as v,resolve as p}from"path";import{fileURLToPath as d}from"url";var S=v(d(import.meta.url)),g={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},s=class r{static{f(this,"ConfigManager")}static instance;defaultConfigPath;config=null;constructor(){this.defaultConfigPath=p(S,"xiaozhi.config.default.json")}getConfigFilePath(){let o=process.env.XIAOZHI_CONFIG_DIR||process.cwd();return p(o,"xiaozhi.config.json")}static getInstance(){return r.instance||(r.instance=new r),r.instance}configExists(){let o=this.getConfigFilePath();return a(o)}initConfig(){if(!a(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 o=this.getConfigFilePath();l(this.defaultConfigPath,o),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 o=this.getConfigFilePath(),n=h(o,"utf8"),e=JSON.parse(n);return this.validateConfig(e),e}catch(o){throw o instanceof SyntaxError?new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF: ${o.message}`):o}}validateConfig(o){if(!o||typeof o!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A\u6839\u5BF9\u8C61\u65E0\u6548");let n=o;if(!n.mcpEndpoint||typeof n.mcpEndpoint!="string")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5B57\u6BB5\u65E0\u6548");if(!n.mcpServers||typeof n.mcpServers!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers \u5B57\u6BB5\u65E0\u6548");for(let[e,t]of Object.entries(n.mcpServers)){if(!t||typeof t!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${e} \u65E0\u6548`);let i=t;if(i.type==="sse"){if(!i.url||typeof i.url!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${e}.url \u65E0\u6548`)}else{if(!i.command||typeof i.command!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${e}.command \u65E0\u6548`);if(!Array.isArray(i.args))throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${e}.args \u5FC5\u987B\u662F\u6570\u7EC4`);if(i.env&&typeof i.env!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${e}.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(o){return this.getMcpServerConfig()[o]?.tools||{}}isToolEnabled(o,n){return this.getServerToolsConfig(o)[n]?.enable!==!1}updateMcpEndpoint(o){if(!o||typeof o!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let e={...this.getConfig(),mcpEndpoint:o};this.saveConfig(e)}updateMcpServer(o,n){if(!o||typeof o!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if("type"in n&&n.type==="sse"){if(!n.url||typeof n.url!="string")throw new Error("SSE \u670D\u52A1\u914D\u7F6E\u7684 url \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else{let i=n;if(!i.command||typeof i.command!="string")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 command \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(!Array.isArray(i.args))throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 args \u5B57\u6BB5\u5FC5\u987B\u662F\u6570\u7EC4");if(i.env&&typeof i.env!="object")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 env \u5B57\u6BB5\u5FC5\u987B\u662F\u5BF9\u8C61")}let e=this.getConfig(),t={...e,mcpServers:{...e.mcpServers,[o]:n}};this.saveConfig(t)}removeMcpServer(o){if(!o||typeof o!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let n=this.getConfig();if(!n.mcpServers[o])throw new Error(`\u670D\u52A1 ${o} \u4E0D\u5B58\u5728`);let e={...n.mcpServers};delete e[o];let t={...n,mcpServers:e};this.saveConfig(t)}updateServerToolsConfig(o,n){let t={...this.getConfig()};t.mcpServerConfig||(t.mcpServerConfig={}),t.mcpServerConfig[o]={tools:n},this.saveConfig(t)}setToolEnabled(o,n,e,t){let c={...this.getConfig()};c.mcpServerConfig||(c.mcpServerConfig={}),c.mcpServerConfig[o]||(c.mcpServerConfig[o]={tools:{}}),c.mcpServerConfig[o].tools[n]={enable:e,...t&&{description:t}},this.saveConfig(c)}saveConfig(o){try{this.validateConfig(o);let n=this.getConfigFilePath(),e=JSON.stringify(o,null,2);u(n,e,"utf8"),this.config=o}catch(n){throw new Error(`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`)}}reloadConfig(){this.config=null}getConfigPath(){return this.getConfigFilePath()}getDefaultConfigPath(){return this.defaultConfigPath}getConnectionConfig(){let n=this.getConfig().connection||{};return{heartbeatInterval:n.heartbeatInterval??g.heartbeatInterval,heartbeatTimeout:n.heartbeatTimeout??g.heartbeatTimeout,reconnectInterval:n.reconnectInterval??g.reconnectInterval}}getHeartbeatInterval(){return this.getConnectionConfig().heartbeatInterval}getHeartbeatTimeout(){return this.getConnectionConfig().heartbeatTimeout}getReconnectInterval(){return this.getConnectionConfig().reconnectInterval}updateConnectionConfig(o){let n=this.getConfig(),t={...n.connection||{},...o},i={...n,connection:t};this.saveConfig(i)}setHeartbeatInterval(o){if(o<=0)throw new Error("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatInterval:o})}setHeartbeatTimeout(o){if(o<=0)throw new Error("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatTimeout:o})}setReconnectInterval(o){if(o<=0)throw new Error("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({reconnectInterval:o})}getModelScopeConfig(){return this.getConfig().modelscope||{}}getModelScopeApiKey(){return this.getModelScopeConfig().apiKey||process.env.MODELSCOPE_API_TOKEN}updateModelScopeConfig(o){let n=this.getConfig(),t={...n.modelscope||{},...o},i={...n,modelscope:t};this.saveConfig(i)}setModelScopeApiKey(o){if(!o||typeof o!="string")throw new Error("API Key \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");this.updateModelScopeConfig({apiKey:o})}},y=s.getInstance();export{s as ConfigManager,y as configManager};
1
+ var p=Object.defineProperty;var s=(r,n)=>p(r,"name",{value:n,configurable:!0});import{copyFileSync as l,existsSync as a,readFileSync as u,writeFileSync as h}from"fs";import{dirname as v,resolve as C}from"path";import{fileURLToPath as d}from"url";var b=v(d(import.meta.url)),g={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},f=class r{static{s(this,"ConfigManager")}static instance;defaultConfigPath;config=null;constructor(){this.defaultConfigPath=C(b,"xiaozhi.config.default.json")}getConfigFilePath(){let n=process.env.XIAOZHI_CONFIG_DIR||process.cwd();return C(n,"xiaozhi.config.json")}static getInstance(){return r.instance||(r.instance=new r),r.instance}configExists(){let n=this.getConfigFilePath();return a(n)}initConfig(){if(!a(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 n=this.getConfigFilePath();l(this.defaultConfigPath,n),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 n=this.getConfigFilePath(),o=u(n,"utf8"),e=JSON.parse(o);return this.validateConfig(e),e}catch(n){throw n instanceof SyntaxError?new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF: ${n.message}`):n}}validateConfig(n){if(!n||typeof n!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A\u6839\u5BF9\u8C61\u65E0\u6548");let o=n;if(!o.mcpEndpoint||typeof o.mcpEndpoint!="string")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5B57\u6BB5\u65E0\u6548");if(!o.mcpServers||typeof o.mcpServers!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers \u5B57\u6BB5\u65E0\u6548");for(let[e,t]of Object.entries(o.mcpServers)){if(!t||typeof t!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${e} \u65E0\u6548`);let i=t;if(i.type==="sse"){if(!i.url||typeof i.url!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${e}.url \u65E0\u6548`)}else{if(!i.command||typeof i.command!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${e}.command \u65E0\u6548`);if(!Array.isArray(i.args))throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${e}.args \u5FC5\u987B\u662F\u6570\u7EC4`);if(i.env&&typeof i.env!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${e}.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(n){return this.getMcpServerConfig()[n]?.tools||{}}isToolEnabled(n,o){return this.getServerToolsConfig(n)[o]?.enable!==!1}updateMcpEndpoint(n){if(!n||typeof n!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let e={...this.getConfig(),mcpEndpoint:n};this.saveConfig(e)}updateMcpServer(n,o){if(!n||typeof n!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if("type"in o&&o.type==="sse"){if(!o.url||typeof o.url!="string")throw new Error("SSE \u670D\u52A1\u914D\u7F6E\u7684 url \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else{let i=o;if(!i.command||typeof i.command!="string")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 command \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(!Array.isArray(i.args))throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 args \u5B57\u6BB5\u5FC5\u987B\u662F\u6570\u7EC4");if(i.env&&typeof i.env!="object")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 env \u5B57\u6BB5\u5FC5\u987B\u662F\u5BF9\u8C61")}let e=this.getConfig(),t={...e,mcpServers:{...e.mcpServers,[n]:o}};this.saveConfig(t)}removeMcpServer(n){if(!n||typeof n!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let o=this.getConfig();if(!o.mcpServers[n])throw new Error(`\u670D\u52A1 ${n} \u4E0D\u5B58\u5728`);let e={...o.mcpServers};delete e[n];let t={...o,mcpServers:e};this.saveConfig(t)}updateServerToolsConfig(n,o){let t={...this.getConfig()};t.mcpServerConfig||(t.mcpServerConfig={}),t.mcpServerConfig[n]={tools:o},this.saveConfig(t)}setToolEnabled(n,o,e,t){let c={...this.getConfig()};c.mcpServerConfig||(c.mcpServerConfig={}),c.mcpServerConfig[n]||(c.mcpServerConfig[n]={tools:{}}),c.mcpServerConfig[n].tools[o]={enable:e,...t&&{description:t}},this.saveConfig(c)}saveConfig(n){try{this.validateConfig(n);let o=this.getConfigFilePath(),e=JSON.stringify(n,null,2);h(o,e,"utf8"),this.config=n}catch(o){throw new Error(`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`)}}reloadConfig(){this.config=null}getConfigPath(){return this.getConfigFilePath()}getDefaultConfigPath(){return this.defaultConfigPath}getConnectionConfig(){let o=this.getConfig().connection||{};return{heartbeatInterval:o.heartbeatInterval??g.heartbeatInterval,heartbeatTimeout:o.heartbeatTimeout??g.heartbeatTimeout,reconnectInterval:o.reconnectInterval??g.reconnectInterval}}getHeartbeatInterval(){return this.getConnectionConfig().heartbeatInterval}getHeartbeatTimeout(){return this.getConnectionConfig().heartbeatTimeout}getReconnectInterval(){return this.getConnectionConfig().reconnectInterval}updateConnectionConfig(n){let o=this.getConfig(),t={...o.connection||{},...n},i={...o,connection:t};this.saveConfig(i)}setHeartbeatInterval(n){if(n<=0)throw new Error("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatInterval:n})}setHeartbeatTimeout(n){if(n<=0)throw new Error("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatTimeout:n})}setReconnectInterval(n){if(n<=0)throw new Error("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({reconnectInterval:n})}getModelScopeConfig(){return this.getConfig().modelscope||{}}getModelScopeApiKey(){return this.getModelScopeConfig().apiKey||process.env.MODELSCOPE_API_TOKEN}updateModelScopeConfig(n){let o=this.getConfig(),t={...o.modelscope||{},...n},i={...o,modelscope:t};this.saveConfig(i)}setModelScopeApiKey(n){if(!n||typeof n!="string")throw new Error("API Key \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");this.updateModelScopeConfig({apiKey:n})}getWebUIConfig(){return this.getConfig().webUI||{}}getWebUIPort(){return this.getWebUIConfig().port??9999}updateWebUIConfig(n){let o=this.getConfig(),t={...o.webUI||{},...n},i={...o,webUI:t};this.saveConfig(i)}setWebUIPort(n){if(!Number.isInteger(n)||n<=0||n>65535)throw new Error("\u7AEF\u53E3\u53F7\u5FC5\u987B\u662F 1-65535 \u4E4B\u95F4\u7684\u6574\u6570");this.updateWebUIConfig({port:n})}},I=f.getInstance();export{f as ConfigManager,I as configManager};
2
2
  //# sourceMappingURL=configManager.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/configManager.ts"],"sourcesContent":["import { copyFileSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// 在 ESM 中,需要从 import.meta.url 获取当前文件目录\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// 默认连接配置\nconst DEFAULT_CONNECTION_CONFIG: Required<ConnectionConfig> = {\n heartbeatInterval: 30000, // 30秒心跳间隔\n heartbeatTimeout: 10000, // 10秒心跳超时\n reconnectInterval: 5000, // 5秒重连间隔\n};\n\n// 配置文件接口定义\n// 本地 MCP 服务配置\nexport interface LocalMCPServerConfig {\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\n// 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"],"mappings":"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","names":["copyFileSync","existsSync","readFileSync","writeFileSync","dirname","resolve","fileURLToPath","__dirname","dirname","fileURLToPath","DEFAULT_CONNECTION_CONFIG","ConfigManager","_ConfigManager","__name","resolve","configDir","configPath","existsSync","copyFileSync","configData","readFileSync","config","error","configObj","serverName","serverConfig","sc","toolName","endpoint","newConfig","localConfig","newMcpServers","toolsConfig","enabled","description","configJson","writeFileSync","connectionConfig","newConnectionConfig","interval","timeout","modelScopeConfig","newModelScopeConfig","apiKey","configManager"]}
1
+ {"version":3,"sources":["../src/configManager.ts"],"sourcesContent":["import { copyFileSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// 在 ESM 中,需要从 import.meta.url 获取当前文件目录\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// 默认连接配置\nconst DEFAULT_CONNECTION_CONFIG: Required<ConnectionConfig> = {\n heartbeatInterval: 30000, // 30秒心跳间隔\n heartbeatTimeout: 10000, // 10秒心跳超时\n reconnectInterval: 5000, // 5秒重连间隔\n};\n\n// 配置文件接口定义\n// 本地 MCP 服务配置\nexport interface LocalMCPServerConfig {\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\n// 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"],"mappings":"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","names":["copyFileSync","existsSync","readFileSync","writeFileSync","dirname","resolve","fileURLToPath","__dirname","dirname","fileURLToPath","DEFAULT_CONNECTION_CONFIG","ConfigManager","_ConfigManager","__name","resolve","configDir","configPath","existsSync","copyFileSync","configData","readFileSync","config","error","configObj","serverName","serverConfig","sc","toolName","endpoint","newConfig","localConfig","newMcpServers","toolsConfig","enabled","description","configJson","writeFileSync","connectionConfig","newConnectionConfig","interval","timeout","modelScopeConfig","newModelScopeConfig","apiKey","webUIConfig","newWebUIConfig","port","configManager"]}
@@ -1,2 +1,2 @@
1
- var P=Object.defineProperty;var p=(c,o)=>P(c,"name",{value:o,configurable:!0});import n from"chalk";import m from"cli-table3";import u from"ora";import{copyFileSync as $,existsSync as v,readFileSync as x,writeFileSync as T}from"fs";import{dirname as I,resolve as S}from"path";import{fileURLToPath as R}from"url";var O=I(R(import.meta.url)),C={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},h=class c{static{p(this,"ConfigManager")}static instance;defaultConfigPath;config=null;constructor(){this.defaultConfigPath=S(O,"xiaozhi.config.default.json")}getConfigFilePath(){let o=process.env.XIAOZHI_CONFIG_DIR||process.cwd();return S(o,"xiaozhi.config.json")}static getInstance(){return c.instance||(c.instance=new c),c.instance}configExists(){let o=this.getConfigFilePath();return v(o)}initConfig(){if(!v(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 o=this.getConfigFilePath();$(this.defaultConfigPath,o),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 o=this.getConfigFilePath(),e=x(o,"utf8"),r=JSON.parse(e);return this.validateConfig(r),r}catch(o){throw o instanceof SyntaxError?new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF: ${o.message}`):o}}validateConfig(o){if(!o||typeof o!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A\u6839\u5BF9\u8C61\u65E0\u6548");let e=o;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[r,t]of Object.entries(e.mcpServers)){if(!t||typeof t!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${r} \u65E0\u6548`);let i=t;if(i.type==="sse"){if(!i.url||typeof i.url!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${r}.url \u65E0\u6548`)}else{if(!i.command||typeof i.command!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${r}.command \u65E0\u6548`);if(!Array.isArray(i.args))throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${r}.args \u5FC5\u987B\u662F\u6570\u7EC4`);if(i.env&&typeof i.env!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${r}.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(o){return this.getMcpServerConfig()[o]?.tools||{}}isToolEnabled(o,e){return this.getServerToolsConfig(o)[e]?.enable!==!1}updateMcpEndpoint(o){if(!o||typeof o!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let r={...this.getConfig(),mcpEndpoint:o};this.saveConfig(r)}updateMcpServer(o,e){if(!o||typeof o!="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 i=e;if(!i.command||typeof i.command!="string")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 command \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(!Array.isArray(i.args))throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 args \u5B57\u6BB5\u5FC5\u987B\u662F\u6570\u7EC4");if(i.env&&typeof i.env!="object")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 env \u5B57\u6BB5\u5FC5\u987B\u662F\u5BF9\u8C61")}let r=this.getConfig(),t={...r,mcpServers:{...r.mcpServers,[o]:e}};this.saveConfig(t)}removeMcpServer(o){if(!o||typeof o!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let e=this.getConfig();if(!e.mcpServers[o])throw new Error(`\u670D\u52A1 ${o} \u4E0D\u5B58\u5728`);let r={...e.mcpServers};delete r[o];let t={...e,mcpServers:r};this.saveConfig(t)}updateServerToolsConfig(o,e){let t={...this.getConfig()};t.mcpServerConfig||(t.mcpServerConfig={}),t.mcpServerConfig[o]={tools:e},this.saveConfig(t)}setToolEnabled(o,e,r,t){let s={...this.getConfig()};s.mcpServerConfig||(s.mcpServerConfig={}),s.mcpServerConfig[o]||(s.mcpServerConfig[o]={tools:{}}),s.mcpServerConfig[o].tools[e]={enable:r,...t&&{description:t}},this.saveConfig(s)}saveConfig(o){try{this.validateConfig(o);let e=this.getConfigFilePath(),r=JSON.stringify(o,null,2);T(e,r,"utf8"),this.config=o}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??C.heartbeatInterval,heartbeatTimeout:e.heartbeatTimeout??C.heartbeatTimeout,reconnectInterval:e.reconnectInterval??C.reconnectInterval}}getHeartbeatInterval(){return this.getConnectionConfig().heartbeatInterval}getHeartbeatTimeout(){return this.getConnectionConfig().heartbeatTimeout}getReconnectInterval(){return this.getConnectionConfig().reconnectInterval}updateConnectionConfig(o){let e=this.getConfig(),t={...e.connection||{},...o},i={...e,connection:t};this.saveConfig(i)}setHeartbeatInterval(o){if(o<=0)throw new Error("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatInterval:o})}setHeartbeatTimeout(o){if(o<=0)throw new Error("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatTimeout:o})}setReconnectInterval(o){if(o<=0)throw new Error("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({reconnectInterval:o})}getModelScopeConfig(){return this.getConfig().modelscope||{}}getModelScopeApiKey(){return this.getModelScopeConfig().apiKey||process.env.MODELSCOPE_API_TOKEN}updateModelScopeConfig(o){let e=this.getConfig(),t={...e.modelscope||{},...o},i={...e,modelscope:t};this.saveConfig(i)}setModelScopeApiKey(o){if(!o||typeof o!="string")throw new Error("API Key \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");this.updateModelScopeConfig({apiKey:o})}},f=h.getInstance();function y(c){let o=0;for(let e of c)/[\u4e00-\u9fff\u3400-\u4dbf\uff00-\uffef]/.test(e)?o+=2:o+=1;return o}p(y,"getDisplayWidth");function w(c,o){if(y(c)<=o)return c;if(o<=3)return"";let e="",r=0,t=!1;for(let i of c){let s=/[\u4e00-\u9fff\u3400-\u4dbf\uff00-\uffef]/.test(i)?2:1;if(r+s>o-3){if(!t)return"";e+="...";break}e+=i,r+=s,t=!0}return e}p(w,"truncateToWidth");async function K(c={}){let o=u("\u83B7\u53D6 MCP \u670D\u52A1\u5217\u8868...").start();try{let e=f.getMcpServers(),r=Object.keys(e);if(r.length===0){o.warn("\u672A\u914D\u7F6E\u4EFB\u4F55 MCP \u670D\u52A1"),console.log(n.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi config' \u547D\u4EE4\u914D\u7F6E MCP \u670D\u52A1"));return}if(o.succeed(`\u627E\u5230 ${r.length} \u4E2A MCP \u670D\u52A1`),c.tools){console.log(),console.log(n.bold("MCP \u670D\u52A1\u5DE5\u5177\u5217\u8868:")),console.log();let t=8,i=[];for(let g of r){let l=f.getServerToolsConfig(g),a=Object.keys(l);i.push(...a)}for(let g of i){let l=y(g);l>t&&(t=l)}t=Math.max(10,Math.min(t+2,30));let s=new m({head:[n.bold("MCP"),n.bold("\u5DE5\u5177\u540D\u79F0"),n.bold("\u72B6\u6001"),n.bold("\u63CF\u8FF0")],colWidths:[15,t,8,40],wordWrap:!0,style:{head:[],border:[]}});for(let g of r){let l=f.getServerToolsConfig(g),a=Object.keys(l);if(a.length===0)s.push([n.gray(g),n.gray("(\u65E0\u5DE5\u5177)"),n.gray("-"),n.gray("\u8BF7\u5148\u542F\u52A8\u670D\u52A1\u626B\u63CF\u5DE5\u5177")]);else{s.length>0&&s.push([{colSpan:4,content:""}]);for(let d of a){let b=l[d],M=b.enable?n.green("\u542F\u7528"):n.red("\u7981\u7528"),E=w(b.description||"",32);s.push([g,d,M,E])}}}console.log(s.toString())}else{console.log(),console.log(n.bold("MCP \u670D\u52A1\u5217\u8868:")),console.log();for(let t of r){let i=e[t],s=f.getServerToolsConfig(t),g=Object.keys(s).length,l=Object.values(s).filter(a=>a.enable!==!1).length;console.log(`${n.cyan("\u2022")} ${n.bold(t)}`),"type"in i&&i.type==="sse"?(console.log(` \u7C7B\u578B: ${n.gray("SSE")}`),console.log(` URL: ${n.gray(i.url)}`)):console.log(` \u547D\u4EE4: ${n.gray(i.command)} ${n.gray(i.args.join(" "))}`),g>0?console.log(` \u5DE5\u5177: ${n.green(l)} \u542F\u7528 / ${n.yellow(g)} \u603B\u8BA1`):console.log(` \u5DE5\u5177: ${n.gray("\u672A\u626B\u63CF (\u8BF7\u5148\u542F\u52A8\u670D\u52A1)")}`),console.log()}}console.log(n.gray("\u{1F4A1} \u63D0\u793A:")),console.log(n.gray(" - \u4F7F\u7528 'xiaozhi mcp list --tools' \u67E5\u770B\u6240\u6709\u5DE5\u5177")),console.log(n.gray(" - \u4F7F\u7528 'xiaozhi mcp <\u670D\u52A1\u540D> list' \u67E5\u770B\u6307\u5B9A\u670D\u52A1\u7684\u5DE5\u5177")),console.log(n.gray(" - \u4F7F\u7528 'xiaozhi mcp <\u670D\u52A1\u540D> <\u5DE5\u5177\u540D> enable/disable' \u542F\u7528/\u7981\u7528\u5DE5\u5177"))}catch(e){o.fail("\u83B7\u53D6 MCP \u670D\u52A1\u5217\u8868\u5931\u8D25"),console.error(n.red(`\u9519\u8BEF: ${e instanceof Error?e.message:String(e)}`)),process.exit(1)}}p(K,"listMcpServers");async function _(c){let o=u(`\u83B7\u53D6 ${c} \u670D\u52A1\u7684\u5DE5\u5177\u5217\u8868...`).start();try{if(!f.getMcpServers()[c]){o.fail(`\u670D\u52A1 '${c}' \u4E0D\u5B58\u5728`),console.log(n.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp list' \u67E5\u770B\u6240\u6709\u53EF\u7528\u670D\u52A1"));return}let r=f.getServerToolsConfig(c),t=Object.keys(r);if(t.length===0){o.warn(`\u670D\u52A1 '${c}' \u6682\u65E0\u5DE5\u5177\u4FE1\u606F`),console.log(n.yellow("\u{1F4A1} \u63D0\u793A: \u8BF7\u5148\u542F\u52A8\u670D\u52A1\u4EE5\u626B\u63CF\u5DE5\u5177\u5217\u8868"));return}o.succeed(`\u670D\u52A1 '${c}' \u5171\u6709 ${t.length} \u4E2A\u5DE5\u5177`),console.log(),console.log(n.bold(`${c} \u670D\u52A1\u5DE5\u5177\u5217\u8868:`)),console.log();let i=new m({head:[n.bold("\u5DE5\u5177\u540D\u79F0"),n.bold("\u72B6\u6001"),n.bold("\u63CF\u8FF0")],colWidths:[30,8,50],wordWrap:!0,style:{head:[],border:[]}});for(let s of t){let g=r[s],l=g.enable?n.green("\u542F\u7528"):n.red("\u7981\u7528"),a=w(g.description||"",40);i.push([s,l,a])}console.log(i.toString()),console.log(),console.log(n.gray("\u{1F4A1} \u63D0\u793A:")),console.log(n.gray(` - \u4F7F\u7528 'xiaozhi mcp ${c} <\u5DE5\u5177\u540D> enable' \u542F\u7528\u5DE5\u5177`)),console.log(n.gray(` - \u4F7F\u7528 'xiaozhi mcp ${c} <\u5DE5\u5177\u540D> disable' \u7981\u7528\u5DE5\u5177`))}catch(e){o.fail("\u83B7\u53D6\u5DE5\u5177\u5217\u8868\u5931\u8D25"),console.error(n.red(`\u9519\u8BEF: ${e instanceof Error?e.message:String(e)}`)),process.exit(1)}}p(_,"listServerTools");async function U(c,o,e){let r=e?"\u542F\u7528":"\u7981\u7528",t=u(`${r}\u5DE5\u5177 ${c}/${o}...`).start();try{if(!f.getMcpServers()[c]){t.fail(`\u670D\u52A1 '${c}' \u4E0D\u5B58\u5728`),console.log(n.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp list' \u67E5\u770B\u6240\u6709\u53EF\u7528\u670D\u52A1"));return}let s=f.getServerToolsConfig(c);if(!s[o]){t.fail(`\u5DE5\u5177 '${o}' \u5728\u670D\u52A1 '${c}' \u4E2D\u4E0D\u5B58\u5728`),console.log(n.yellow(`\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp ${c} list' \u67E5\u770B\u8BE5\u670D\u52A1\u7684\u6240\u6709\u5DE5\u5177`));return}f.setToolEnabled(c,o,e,s[o].description),t.succeed(`\u6210\u529F${r}\u5DE5\u5177 ${n.cyan(c)}/${n.cyan(o)}`),console.log(),console.log(n.gray("\u{1F4A1} \u63D0\u793A: \u5DE5\u5177\u72B6\u6001\u66F4\u6539\u5C06\u5728\u4E0B\u6B21\u542F\u52A8\u670D\u52A1\u65F6\u751F\u6548"))}catch(i){t.fail(`${r}\u5DE5\u5177\u5931\u8D25`),console.error(n.red(`\u9519\u8BEF: ${i instanceof Error?i.message:String(i)}`)),process.exit(1)}}p(U,"setToolEnabled");export{y as getDisplayWidth,K as listMcpServers,_ as listServerTools,U as setToolEnabled,w as truncateToWidth};
1
+ var P=Object.defineProperty;var p=(c,o)=>P(c,"name",{value:o,configurable:!0});import n from"chalk";import S from"cli-table3";import h from"ora";import{copyFileSync as I,existsSync as v,readFileSync as $,writeFileSync as x}from"fs";import{dirname as T,resolve as m}from"path";import{fileURLToPath as R}from"url";var U=T(R(import.meta.url)),C={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},u=class c{static{p(this,"ConfigManager")}static instance;defaultConfigPath;config=null;constructor(){this.defaultConfigPath=m(U,"xiaozhi.config.default.json")}getConfigFilePath(){let o=process.env.XIAOZHI_CONFIG_DIR||process.cwd();return m(o,"xiaozhi.config.json")}static getInstance(){return c.instance||(c.instance=new c),c.instance}configExists(){let o=this.getConfigFilePath();return v(o)}initConfig(){if(!v(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 o=this.getConfigFilePath();I(this.defaultConfigPath,o),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 o=this.getConfigFilePath(),e=$(o,"utf8"),r=JSON.parse(e);return this.validateConfig(r),r}catch(o){throw o instanceof SyntaxError?new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF: ${o.message}`):o}}validateConfig(o){if(!o||typeof o!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A\u6839\u5BF9\u8C61\u65E0\u6548");let e=o;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[r,t]of Object.entries(e.mcpServers)){if(!t||typeof t!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${r} \u65E0\u6548`);let i=t;if(i.type==="sse"){if(!i.url||typeof i.url!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${r}.url \u65E0\u6548`)}else{if(!i.command||typeof i.command!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${r}.command \u65E0\u6548`);if(!Array.isArray(i.args))throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${r}.args \u5FC5\u987B\u662F\u6570\u7EC4`);if(i.env&&typeof i.env!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${r}.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(o){return this.getMcpServerConfig()[o]?.tools||{}}isToolEnabled(o,e){return this.getServerToolsConfig(o)[e]?.enable!==!1}updateMcpEndpoint(o){if(!o||typeof o!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let r={...this.getConfig(),mcpEndpoint:o};this.saveConfig(r)}updateMcpServer(o,e){if(!o||typeof o!="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 i=e;if(!i.command||typeof i.command!="string")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 command \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(!Array.isArray(i.args))throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 args \u5B57\u6BB5\u5FC5\u987B\u662F\u6570\u7EC4");if(i.env&&typeof i.env!="object")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 env \u5B57\u6BB5\u5FC5\u987B\u662F\u5BF9\u8C61")}let r=this.getConfig(),t={...r,mcpServers:{...r.mcpServers,[o]:e}};this.saveConfig(t)}removeMcpServer(o){if(!o||typeof o!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let e=this.getConfig();if(!e.mcpServers[o])throw new Error(`\u670D\u52A1 ${o} \u4E0D\u5B58\u5728`);let r={...e.mcpServers};delete r[o];let t={...e,mcpServers:r};this.saveConfig(t)}updateServerToolsConfig(o,e){let t={...this.getConfig()};t.mcpServerConfig||(t.mcpServerConfig={}),t.mcpServerConfig[o]={tools:e},this.saveConfig(t)}setToolEnabled(o,e,r,t){let s={...this.getConfig()};s.mcpServerConfig||(s.mcpServerConfig={}),s.mcpServerConfig[o]||(s.mcpServerConfig[o]={tools:{}}),s.mcpServerConfig[o].tools[e]={enable:r,...t&&{description:t}},this.saveConfig(s)}saveConfig(o){try{this.validateConfig(o);let e=this.getConfigFilePath(),r=JSON.stringify(o,null,2);x(e,r,"utf8"),this.config=o}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??C.heartbeatInterval,heartbeatTimeout:e.heartbeatTimeout??C.heartbeatTimeout,reconnectInterval:e.reconnectInterval??C.reconnectInterval}}getHeartbeatInterval(){return this.getConnectionConfig().heartbeatInterval}getHeartbeatTimeout(){return this.getConnectionConfig().heartbeatTimeout}getReconnectInterval(){return this.getConnectionConfig().reconnectInterval}updateConnectionConfig(o){let e=this.getConfig(),t={...e.connection||{},...o},i={...e,connection:t};this.saveConfig(i)}setHeartbeatInterval(o){if(o<=0)throw new Error("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatInterval:o})}setHeartbeatTimeout(o){if(o<=0)throw new Error("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatTimeout:o})}setReconnectInterval(o){if(o<=0)throw new Error("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({reconnectInterval:o})}getModelScopeConfig(){return this.getConfig().modelscope||{}}getModelScopeApiKey(){return this.getModelScopeConfig().apiKey||process.env.MODELSCOPE_API_TOKEN}updateModelScopeConfig(o){let e=this.getConfig(),t={...e.modelscope||{},...o},i={...e,modelscope:t};this.saveConfig(i)}setModelScopeApiKey(o){if(!o||typeof o!="string")throw new Error("API Key \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");this.updateModelScopeConfig({apiKey:o})}getWebUIConfig(){return this.getConfig().webUI||{}}getWebUIPort(){return this.getWebUIConfig().port??9999}updateWebUIConfig(o){let e=this.getConfig(),t={...e.webUI||{},...o},i={...e,webUI:t};this.saveConfig(i)}setWebUIPort(o){if(!Number.isInteger(o)||o<=0||o>65535)throw new Error("\u7AEF\u53E3\u53F7\u5FC5\u987B\u662F 1-65535 \u4E4B\u95F4\u7684\u6574\u6570");this.updateWebUIConfig({port:o})}},l=u.getInstance();function w(c){let o=0;for(let e of c)/[\u4e00-\u9fff\u3400-\u4dbf\uff00-\uffef]/.test(e)?o+=2:o+=1;return o}p(w,"getDisplayWidth");function y(c,o){if(w(c)<=o)return c;if(o<=3)return"";let e="",r=0,t=!1;for(let i of c){let s=/[\u4e00-\u9fff\u3400-\u4dbf\uff00-\uffef]/.test(i)?2:1;if(r+s>o-3){if(!t)return"";e+="...";break}e+=i,r+=s,t=!0}return e}p(y,"truncateToWidth");async function J(c={}){let o=h("\u83B7\u53D6 MCP \u670D\u52A1\u5217\u8868...").start();try{let e=l.getMcpServers(),r=Object.keys(e);if(r.length===0){o.warn("\u672A\u914D\u7F6E\u4EFB\u4F55 MCP \u670D\u52A1"),console.log(n.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi config' \u547D\u4EE4\u914D\u7F6E MCP \u670D\u52A1"));return}if(o.succeed(`\u627E\u5230 ${r.length} \u4E2A MCP \u670D\u52A1`),c.tools){console.log(),console.log(n.bold("MCP \u670D\u52A1\u5DE5\u5177\u5217\u8868:")),console.log();let t=8,i=[];for(let g of r){let f=l.getServerToolsConfig(g),a=Object.keys(f);i.push(...a)}for(let g of i){let f=w(g);f>t&&(t=f)}t=Math.max(10,Math.min(t+2,30));let s=new S({head:[n.bold("MCP"),n.bold("\u5DE5\u5177\u540D\u79F0"),n.bold("\u72B6\u6001"),n.bold("\u63CF\u8FF0")],colWidths:[15,t,8,40],wordWrap:!0,style:{head:[],border:[]}});for(let g of r){let f=l.getServerToolsConfig(g),a=Object.keys(f);if(a.length===0)s.push([n.gray(g),n.gray("(\u65E0\u5DE5\u5177)"),n.gray("-"),n.gray("\u8BF7\u5148\u542F\u52A8\u670D\u52A1\u626B\u63CF\u5DE5\u5177")]);else{s.length>0&&s.push([{colSpan:4,content:""}]);for(let d of a){let b=f[d],M=b.enable?n.green("\u542F\u7528"):n.red("\u7981\u7528"),E=y(b.description||"",32);s.push([g,d,M,E])}}}console.log(s.toString())}else{console.log(),console.log(n.bold("MCP \u670D\u52A1\u5217\u8868:")),console.log();for(let t of r){let i=e[t],s=l.getServerToolsConfig(t),g=Object.keys(s).length,f=Object.values(s).filter(a=>a.enable!==!1).length;console.log(`${n.cyan("\u2022")} ${n.bold(t)}`),"type"in i&&i.type==="sse"?(console.log(` \u7C7B\u578B: ${n.gray("SSE")}`),console.log(` URL: ${n.gray(i.url)}`)):console.log(` \u547D\u4EE4: ${n.gray(i.command)} ${n.gray(i.args.join(" "))}`),g>0?console.log(` \u5DE5\u5177: ${n.green(f)} \u542F\u7528 / ${n.yellow(g)} \u603B\u8BA1`):console.log(` \u5DE5\u5177: ${n.gray("\u672A\u626B\u63CF (\u8BF7\u5148\u542F\u52A8\u670D\u52A1)")}`),console.log()}}console.log(n.gray("\u{1F4A1} \u63D0\u793A:")),console.log(n.gray(" - \u4F7F\u7528 'xiaozhi mcp list --tools' \u67E5\u770B\u6240\u6709\u5DE5\u5177")),console.log(n.gray(" - \u4F7F\u7528 'xiaozhi mcp <\u670D\u52A1\u540D> list' \u67E5\u770B\u6307\u5B9A\u670D\u52A1\u7684\u5DE5\u5177")),console.log(n.gray(" - \u4F7F\u7528 'xiaozhi mcp <\u670D\u52A1\u540D> <\u5DE5\u5177\u540D> enable/disable' \u542F\u7528/\u7981\u7528\u5DE5\u5177"))}catch(e){o.fail("\u83B7\u53D6 MCP \u670D\u52A1\u5217\u8868\u5931\u8D25"),console.error(n.red(`\u9519\u8BEF: ${e instanceof Error?e.message:String(e)}`)),process.exit(1)}}p(J,"listMcpServers");async function K(c){let o=h(`\u83B7\u53D6 ${c} \u670D\u52A1\u7684\u5DE5\u5177\u5217\u8868...`).start();try{if(!l.getMcpServers()[c]){o.fail(`\u670D\u52A1 '${c}' \u4E0D\u5B58\u5728`),console.log(n.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp list' \u67E5\u770B\u6240\u6709\u53EF\u7528\u670D\u52A1"));return}let r=l.getServerToolsConfig(c),t=Object.keys(r);if(t.length===0){o.warn(`\u670D\u52A1 '${c}' \u6682\u65E0\u5DE5\u5177\u4FE1\u606F`),console.log(n.yellow("\u{1F4A1} \u63D0\u793A: \u8BF7\u5148\u542F\u52A8\u670D\u52A1\u4EE5\u626B\u63CF\u5DE5\u5177\u5217\u8868"));return}o.succeed(`\u670D\u52A1 '${c}' \u5171\u6709 ${t.length} \u4E2A\u5DE5\u5177`),console.log(),console.log(n.bold(`${c} \u670D\u52A1\u5DE5\u5177\u5217\u8868:`)),console.log();let i=new S({head:[n.bold("\u5DE5\u5177\u540D\u79F0"),n.bold("\u72B6\u6001"),n.bold("\u63CF\u8FF0")],colWidths:[30,8,50],wordWrap:!0,style:{head:[],border:[]}});for(let s of t){let g=r[s],f=g.enable?n.green("\u542F\u7528"):n.red("\u7981\u7528"),a=y(g.description||"",40);i.push([s,f,a])}console.log(i.toString()),console.log(),console.log(n.gray("\u{1F4A1} \u63D0\u793A:")),console.log(n.gray(` - \u4F7F\u7528 'xiaozhi mcp ${c} <\u5DE5\u5177\u540D> enable' \u542F\u7528\u5DE5\u5177`)),console.log(n.gray(` - \u4F7F\u7528 'xiaozhi mcp ${c} <\u5DE5\u5177\u540D> disable' \u7981\u7528\u5DE5\u5177`))}catch(e){o.fail("\u83B7\u53D6\u5DE5\u5177\u5217\u8868\u5931\u8D25"),console.error(n.red(`\u9519\u8BEF: ${e instanceof Error?e.message:String(e)}`)),process.exit(1)}}p(K,"listServerTools");async function _(c,o,e){let r=e?"\u542F\u7528":"\u7981\u7528",t=h(`${r}\u5DE5\u5177 ${c}/${o}...`).start();try{if(!l.getMcpServers()[c]){t.fail(`\u670D\u52A1 '${c}' \u4E0D\u5B58\u5728`),console.log(n.yellow("\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp list' \u67E5\u770B\u6240\u6709\u53EF\u7528\u670D\u52A1"));return}let s=l.getServerToolsConfig(c);if(!s[o]){t.fail(`\u5DE5\u5177 '${o}' \u5728\u670D\u52A1 '${c}' \u4E2D\u4E0D\u5B58\u5728`),console.log(n.yellow(`\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 'xiaozhi mcp ${c} list' \u67E5\u770B\u8BE5\u670D\u52A1\u7684\u6240\u6709\u5DE5\u5177`));return}l.setToolEnabled(c,o,e,s[o].description),t.succeed(`\u6210\u529F${r}\u5DE5\u5177 ${n.cyan(c)}/${n.cyan(o)}`),console.log(),console.log(n.gray("\u{1F4A1} \u63D0\u793A: \u5DE5\u5177\u72B6\u6001\u66F4\u6539\u5C06\u5728\u4E0B\u6B21\u542F\u52A8\u670D\u52A1\u65F6\u751F\u6548"))}catch(i){t.fail(`${r}\u5DE5\u5177\u5931\u8D25`),console.error(n.red(`\u9519\u8BEF: ${i instanceof Error?i.message:String(i)}`)),process.exit(1)}}p(_,"setToolEnabled");export{w as getDisplayWidth,J as listMcpServers,K as listServerTools,_ as setToolEnabled,y as truncateToWidth};
2
2
  //# sourceMappingURL=mcpCommands.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcpCommands.ts","../src/configManager.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport ora from \"ora\";\nimport { configManager } from \"./configManager\";\n\n/**\n * MCP 相关的命令行功能\n */\n\n/**\n * 计算字符串的显示宽度(中文字符占2个宽度,英文字符占1个宽度)\n */\nexport function getDisplayWidth(str: string): number {\n let width = 0;\n for (const char of str) {\n // 判断是否为中文字符(包括中文标点符号)\n if (/[\\u4e00-\\u9fff\\u3400-\\u4dbf\\uff00-\\uffef]/.test(char)) {\n width += 2;\n } else {\n width += 1;\n }\n }\n return width;\n}\n\n/**\n * 截断字符串到指定的显示宽度\n */\nexport function truncateToWidth(str: string, maxWidth: number): string {\n if (getDisplayWidth(str) <= maxWidth) {\n return str;\n }\n\n // 如果最大宽度小于等于省略号的宽度,返回空字符串\n if (maxWidth <= 3) {\n return \"\";\n }\n\n let result = \"\";\n let currentWidth = 0;\n let hasAddedChar = false;\n\n for (const char of str) {\n const charWidth = /[\\u4e00-\\u9fff\\u3400-\\u4dbf\\uff00-\\uffef]/.test(char)\n ? 2\n : 1;\n\n // 如果加上当前字符会超出限制\n if (currentWidth + charWidth > maxWidth - 3) {\n // 如果还没有添加任何字符,说明连一个字符都放不下,返回空字符串\n if (!hasAddedChar) {\n return \"\";\n }\n // 否则添加省略号并退出\n result += \"...\";\n break;\n }\n\n result += char;\n currentWidth += charWidth;\n hasAddedChar = true;\n }\n\n return result;\n}\n\n/**\n * 列出所有 MCP 服务\n */\nexport async function listMcpServers(\n options: { tools?: boolean } = {}\n): Promise<void> {\n const spinner = ora(\"获取 MCP 服务列表...\").start();\n\n try {\n const mcpServers = configManager.getMcpServers();\n const serverNames = Object.keys(mcpServers);\n\n if (serverNames.length === 0) {\n spinner.warn(\"未配置任何 MCP 服务\");\n console.log(\n chalk.yellow(\"💡 提示: 使用 'xiaozhi config' 命令配置 MCP 服务\")\n );\n return;\n }\n\n spinner.succeed(`找到 ${serverNames.length} 个 MCP 服务`);\n\n if (options.tools) {\n // 显示所有服务的工具列表\n console.log();\n console.log(chalk.bold(\"MCP 服务工具列表:\"));\n console.log();\n\n // 计算所有工具名称的最大长度,用于动态设置列宽\n let maxToolNameWidth = 8; // 默认最小宽度\n const allToolNames: string[] = [];\n\n for (const serverName of serverNames) {\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolNames = Object.keys(toolsConfig);\n allToolNames.push(...toolNames);\n }\n\n // 计算最长工具名称的显示宽度\n for (const toolName of allToolNames) {\n const width = getDisplayWidth(toolName);\n if (width > maxToolNameWidth) {\n maxToolNameWidth = width;\n }\n }\n\n // 确保工具名称列宽度至少为10,最多为30\n maxToolNameWidth = Math.max(10, Math.min(maxToolNameWidth + 2, 30));\n\n // 使用 cli-table3 创建表格\n const table = new Table({\n head: [\n chalk.bold(\"MCP\"),\n chalk.bold(\"工具名称\"),\n chalk.bold(\"状态\"),\n chalk.bold(\"描述\"),\n ],\n colWidths: [15, maxToolNameWidth, 8, 40], // MCP | 工具名称 | 状态 | 描述\n wordWrap: true,\n style: {\n head: [],\n border: [],\n },\n });\n\n for (const serverName of serverNames) {\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolNames = Object.keys(toolsConfig);\n\n if (toolNames.length === 0) {\n // 服务没有工具时显示提示信息\n table.push([\n chalk.gray(serverName),\n chalk.gray(\"(无工具)\"),\n chalk.gray(\"-\"),\n chalk.gray(\"请先启动服务扫描工具\"),\n ]);\n } else {\n // 添加服务分隔行\n if (table.length > 0) {\n table.push([{ colSpan: 4, content: \"\" }]);\n }\n\n for (const toolName of toolNames) {\n const toolConfig = toolsConfig[toolName];\n const status = toolConfig.enable\n ? chalk.green(\"启用\")\n : chalk.red(\"禁用\");\n\n // 截断描述到最大32个字符宽度(约16个中文字符)\n const description = truncateToWidth(\n toolConfig.description || \"\",\n 32\n );\n\n // 只显示工具名称,不包含服务名前缀\n table.push([serverName, toolName, status, description]);\n }\n }\n }\n\n console.log(table.toString());\n } else {\n // 只显示服务列表\n console.log();\n console.log(chalk.bold(\"MCP 服务列表:\"));\n console.log();\n\n for (const serverName of serverNames) {\n const serverConfig = mcpServers[serverName];\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolCount = Object.keys(toolsConfig).length;\n const enabledCount = Object.values(toolsConfig).filter(\n (t) => t.enable !== false\n ).length;\n\n console.log(`${chalk.cyan(\"•\")} ${chalk.bold(serverName)}`);\n\n // 检查是否是 SSE 类型\n if (\"type\" in serverConfig && serverConfig.type === \"sse\") {\n console.log(` 类型: ${chalk.gray(\"SSE\")}`);\n console.log(` URL: ${chalk.gray(serverConfig.url)}`);\n } else {\n console.log(\n ` 命令: ${chalk.gray((serverConfig as any).command)} ${chalk.gray(\n (serverConfig as any).args.join(\" \")\n )}`\n );\n }\n if (toolCount > 0) {\n console.log(\n ` 工具: ${chalk.green(enabledCount)} 启用 / ${chalk.yellow(\n toolCount\n )} 总计`\n );\n } else {\n console.log(` 工具: ${chalk.gray(\"未扫描 (请先启动服务)\")}`);\n }\n console.log();\n }\n }\n\n console.log(chalk.gray(\"💡 提示:\"));\n console.log(chalk.gray(\" - 使用 'xiaozhi mcp list --tools' 查看所有工具\"));\n console.log(\n chalk.gray(\" - 使用 'xiaozhi mcp <服务名> list' 查看指定服务的工具\")\n );\n console.log(\n chalk.gray(\n \" - 使用 'xiaozhi mcp <服务名> <工具名> enable/disable' 启用/禁用工具\"\n )\n );\n } catch (error) {\n spinner.fail(\"获取 MCP 服务列表失败\");\n console.error(\n chalk.red(\n `错误: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n process.exit(1);\n }\n}\n\n/**\n * 列出指定服务的工具\n */\nexport async function listServerTools(serverName: string): Promise<void> {\n const spinner = ora(`获取 ${serverName} 服务的工具列表...`).start();\n\n try {\n const mcpServers = configManager.getMcpServers();\n\n if (!mcpServers[serverName]) {\n spinner.fail(`服务 '${serverName}' 不存在`);\n console.log(\n chalk.yellow(\"💡 提示: 使用 'xiaozhi mcp list' 查看所有可用服务\")\n );\n return;\n }\n\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolNames = Object.keys(toolsConfig);\n\n if (toolNames.length === 0) {\n spinner.warn(`服务 '${serverName}' 暂无工具信息`);\n console.log(chalk.yellow(\"💡 提示: 请先启动服务以扫描工具列表\"));\n return;\n }\n\n spinner.succeed(`服务 '${serverName}' 共有 ${toolNames.length} 个工具`);\n\n console.log();\n console.log(chalk.bold(`${serverName} 服务工具列表:`));\n console.log();\n\n // 使用 cli-table3 创建表格\n const table = new Table({\n head: [chalk.bold(\"工具名称\"), chalk.bold(\"状态\"), chalk.bold(\"描述\")],\n colWidths: [30, 8, 50], // 工具名称 | 状态 | 描述\n wordWrap: true,\n style: {\n head: [],\n border: [],\n },\n });\n\n for (const toolName of toolNames) {\n const toolConfig = toolsConfig[toolName];\n const status = toolConfig.enable\n ? chalk.green(\"启用\")\n : chalk.red(\"禁用\");\n\n // 截断描述到最大40个字符宽度(约20个中文字符)\n const description = truncateToWidth(toolConfig.description || \"\", 40);\n\n table.push([toolName, status, description]);\n }\n\n console.log(table.toString());\n\n console.log();\n console.log(chalk.gray(\"💡 提示:\"));\n console.log(\n chalk.gray(\n ` - 使用 'xiaozhi mcp ${serverName} <工具名> enable' 启用工具`\n )\n );\n console.log(\n chalk.gray(\n ` - 使用 'xiaozhi mcp ${serverName} <工具名> disable' 禁用工具`\n )\n );\n } catch (error) {\n spinner.fail(\"获取工具列表失败\");\n console.error(\n chalk.red(\n `错误: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n process.exit(1);\n }\n}\n\n/**\n * 启用或禁用工具\n */\nexport async function setToolEnabled(\n serverName: string,\n toolName: string,\n enabled: boolean\n): Promise<void> {\n const action = enabled ? \"启用\" : \"禁用\";\n const spinner = ora(`${action}工具 ${serverName}/${toolName}...`).start();\n\n try {\n const mcpServers = configManager.getMcpServers();\n\n if (!mcpServers[serverName]) {\n spinner.fail(`服务 '${serverName}' 不存在`);\n console.log(\n chalk.yellow(\"💡 提示: 使用 'xiaozhi mcp list' 查看所有可用服务\")\n );\n return;\n }\n\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n\n if (!toolsConfig[toolName]) {\n spinner.fail(`工具 '${toolName}' 在服务 '${serverName}' 中不存在`);\n console.log(\n chalk.yellow(\n `💡 提示: 使用 'xiaozhi mcp ${serverName} list' 查看该服务的所有工具`\n )\n );\n return;\n }\n\n // 更新工具状态\n configManager.setToolEnabled(\n serverName,\n toolName,\n enabled,\n toolsConfig[toolName].description\n );\n\n spinner.succeed(\n `成功${action}工具 ${chalk.cyan(serverName)}/${chalk.cyan(toolName)}`\n );\n\n console.log();\n console.log(chalk.gray(\"💡 提示: 工具状态更改将在下次启动服务时生效\"));\n } catch (error) {\n spinner.fail(`${action}工具失败`);\n console.error(\n chalk.red(\n `错误: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n process.exit(1);\n }\n}\n","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"],"mappings":"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","names":["chalk","Table","ora","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","getDisplayWidth","str","width","char","__name","truncateToWidth","maxWidth","result","currentWidth","hasAddedChar","charWidth","listMcpServers","options","spinner","ora","mcpServers","configManager","serverNames","chalk","maxToolNameWidth","allToolNames","serverName","toolsConfig","toolNames","toolName","table","Table","toolConfig","status","description","serverConfig","toolCount","enabledCount","t","error","listServerTools","setToolEnabled","enabled","action"]}
1
+ {"version":3,"sources":["../src/mcpCommands.ts","../src/configManager.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport ora from \"ora\";\nimport { configManager } from \"./configManager\";\n\n/**\n * MCP 相关的命令行功能\n */\n\n/**\n * 计算字符串的显示宽度(中文字符占2个宽度,英文字符占1个宽度)\n */\nexport function getDisplayWidth(str: string): number {\n let width = 0;\n for (const char of str) {\n // 判断是否为中文字符(包括中文标点符号)\n if (/[\\u4e00-\\u9fff\\u3400-\\u4dbf\\uff00-\\uffef]/.test(char)) {\n width += 2;\n } else {\n width += 1;\n }\n }\n return width;\n}\n\n/**\n * 截断字符串到指定的显示宽度\n */\nexport function truncateToWidth(str: string, maxWidth: number): string {\n if (getDisplayWidth(str) <= maxWidth) {\n return str;\n }\n\n // 如果最大宽度小于等于省略号的宽度,返回空字符串\n if (maxWidth <= 3) {\n return \"\";\n }\n\n let result = \"\";\n let currentWidth = 0;\n let hasAddedChar = false;\n\n for (const char of str) {\n const charWidth = /[\\u4e00-\\u9fff\\u3400-\\u4dbf\\uff00-\\uffef]/.test(char)\n ? 2\n : 1;\n\n // 如果加上当前字符会超出限制\n if (currentWidth + charWidth > maxWidth - 3) {\n // 如果还没有添加任何字符,说明连一个字符都放不下,返回空字符串\n if (!hasAddedChar) {\n return \"\";\n }\n // 否则添加省略号并退出\n result += \"...\";\n break;\n }\n\n result += char;\n currentWidth += charWidth;\n hasAddedChar = true;\n }\n\n return result;\n}\n\n/**\n * 列出所有 MCP 服务\n */\nexport async function listMcpServers(\n options: { tools?: boolean } = {}\n): Promise<void> {\n const spinner = ora(\"获取 MCP 服务列表...\").start();\n\n try {\n const mcpServers = configManager.getMcpServers();\n const serverNames = Object.keys(mcpServers);\n\n if (serverNames.length === 0) {\n spinner.warn(\"未配置任何 MCP 服务\");\n console.log(\n chalk.yellow(\"💡 提示: 使用 'xiaozhi config' 命令配置 MCP 服务\")\n );\n return;\n }\n\n spinner.succeed(`找到 ${serverNames.length} 个 MCP 服务`);\n\n if (options.tools) {\n // 显示所有服务的工具列表\n console.log();\n console.log(chalk.bold(\"MCP 服务工具列表:\"));\n console.log();\n\n // 计算所有工具名称的最大长度,用于动态设置列宽\n let maxToolNameWidth = 8; // 默认最小宽度\n const allToolNames: string[] = [];\n\n for (const serverName of serverNames) {\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolNames = Object.keys(toolsConfig);\n allToolNames.push(...toolNames);\n }\n\n // 计算最长工具名称的显示宽度\n for (const toolName of allToolNames) {\n const width = getDisplayWidth(toolName);\n if (width > maxToolNameWidth) {\n maxToolNameWidth = width;\n }\n }\n\n // 确保工具名称列宽度至少为10,最多为30\n maxToolNameWidth = Math.max(10, Math.min(maxToolNameWidth + 2, 30));\n\n // 使用 cli-table3 创建表格\n const table = new Table({\n head: [\n chalk.bold(\"MCP\"),\n chalk.bold(\"工具名称\"),\n chalk.bold(\"状态\"),\n chalk.bold(\"描述\"),\n ],\n colWidths: [15, maxToolNameWidth, 8, 40], // MCP | 工具名称 | 状态 | 描述\n wordWrap: true,\n style: {\n head: [],\n border: [],\n },\n });\n\n for (const serverName of serverNames) {\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolNames = Object.keys(toolsConfig);\n\n if (toolNames.length === 0) {\n // 服务没有工具时显示提示信息\n table.push([\n chalk.gray(serverName),\n chalk.gray(\"(无工具)\"),\n chalk.gray(\"-\"),\n chalk.gray(\"请先启动服务扫描工具\"),\n ]);\n } else {\n // 添加服务分隔行\n if (table.length > 0) {\n table.push([{ colSpan: 4, content: \"\" }]);\n }\n\n for (const toolName of toolNames) {\n const toolConfig = toolsConfig[toolName];\n const status = toolConfig.enable\n ? chalk.green(\"启用\")\n : chalk.red(\"禁用\");\n\n // 截断描述到最大32个字符宽度(约16个中文字符)\n const description = truncateToWidth(\n toolConfig.description || \"\",\n 32\n );\n\n // 只显示工具名称,不包含服务名前缀\n table.push([serverName, toolName, status, description]);\n }\n }\n }\n\n console.log(table.toString());\n } else {\n // 只显示服务列表\n console.log();\n console.log(chalk.bold(\"MCP 服务列表:\"));\n console.log();\n\n for (const serverName of serverNames) {\n const serverConfig = mcpServers[serverName];\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolCount = Object.keys(toolsConfig).length;\n const enabledCount = Object.values(toolsConfig).filter(\n (t) => t.enable !== false\n ).length;\n\n console.log(`${chalk.cyan(\"•\")} ${chalk.bold(serverName)}`);\n\n // 检查是否是 SSE 类型\n if (\"type\" in serverConfig && serverConfig.type === \"sse\") {\n console.log(` 类型: ${chalk.gray(\"SSE\")}`);\n console.log(` URL: ${chalk.gray(serverConfig.url)}`);\n } else {\n console.log(\n ` 命令: ${chalk.gray((serverConfig as any).command)} ${chalk.gray(\n (serverConfig as any).args.join(\" \")\n )}`\n );\n }\n if (toolCount > 0) {\n console.log(\n ` 工具: ${chalk.green(enabledCount)} 启用 / ${chalk.yellow(\n toolCount\n )} 总计`\n );\n } else {\n console.log(` 工具: ${chalk.gray(\"未扫描 (请先启动服务)\")}`);\n }\n console.log();\n }\n }\n\n console.log(chalk.gray(\"💡 提示:\"));\n console.log(chalk.gray(\" - 使用 'xiaozhi mcp list --tools' 查看所有工具\"));\n console.log(\n chalk.gray(\" - 使用 'xiaozhi mcp <服务名> list' 查看指定服务的工具\")\n );\n console.log(\n chalk.gray(\n \" - 使用 'xiaozhi mcp <服务名> <工具名> enable/disable' 启用/禁用工具\"\n )\n );\n } catch (error) {\n spinner.fail(\"获取 MCP 服务列表失败\");\n console.error(\n chalk.red(\n `错误: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n process.exit(1);\n }\n}\n\n/**\n * 列出指定服务的工具\n */\nexport async function listServerTools(serverName: string): Promise<void> {\n const spinner = ora(`获取 ${serverName} 服务的工具列表...`).start();\n\n try {\n const mcpServers = configManager.getMcpServers();\n\n if (!mcpServers[serverName]) {\n spinner.fail(`服务 '${serverName}' 不存在`);\n console.log(\n chalk.yellow(\"💡 提示: 使用 'xiaozhi mcp list' 查看所有可用服务\")\n );\n return;\n }\n\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n const toolNames = Object.keys(toolsConfig);\n\n if (toolNames.length === 0) {\n spinner.warn(`服务 '${serverName}' 暂无工具信息`);\n console.log(chalk.yellow(\"💡 提示: 请先启动服务以扫描工具列表\"));\n return;\n }\n\n spinner.succeed(`服务 '${serverName}' 共有 ${toolNames.length} 个工具`);\n\n console.log();\n console.log(chalk.bold(`${serverName} 服务工具列表:`));\n console.log();\n\n // 使用 cli-table3 创建表格\n const table = new Table({\n head: [chalk.bold(\"工具名称\"), chalk.bold(\"状态\"), chalk.bold(\"描述\")],\n colWidths: [30, 8, 50], // 工具名称 | 状态 | 描述\n wordWrap: true,\n style: {\n head: [],\n border: [],\n },\n });\n\n for (const toolName of toolNames) {\n const toolConfig = toolsConfig[toolName];\n const status = toolConfig.enable\n ? chalk.green(\"启用\")\n : chalk.red(\"禁用\");\n\n // 截断描述到最大40个字符宽度(约20个中文字符)\n const description = truncateToWidth(toolConfig.description || \"\", 40);\n\n table.push([toolName, status, description]);\n }\n\n console.log(table.toString());\n\n console.log();\n console.log(chalk.gray(\"💡 提示:\"));\n console.log(\n chalk.gray(\n ` - 使用 'xiaozhi mcp ${serverName} <工具名> enable' 启用工具`\n )\n );\n console.log(\n chalk.gray(\n ` - 使用 'xiaozhi mcp ${serverName} <工具名> disable' 禁用工具`\n )\n );\n } catch (error) {\n spinner.fail(\"获取工具列表失败\");\n console.error(\n chalk.red(\n `错误: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n process.exit(1);\n }\n}\n\n/**\n * 启用或禁用工具\n */\nexport async function setToolEnabled(\n serverName: string,\n toolName: string,\n enabled: boolean\n): Promise<void> {\n const action = enabled ? \"启用\" : \"禁用\";\n const spinner = ora(`${action}工具 ${serverName}/${toolName}...`).start();\n\n try {\n const mcpServers = configManager.getMcpServers();\n\n if (!mcpServers[serverName]) {\n spinner.fail(`服务 '${serverName}' 不存在`);\n console.log(\n chalk.yellow(\"💡 提示: 使用 'xiaozhi mcp list' 查看所有可用服务\")\n );\n return;\n }\n\n const toolsConfig = configManager.getServerToolsConfig(serverName);\n\n if (!toolsConfig[toolName]) {\n spinner.fail(`工具 '${toolName}' 在服务 '${serverName}' 中不存在`);\n console.log(\n chalk.yellow(\n `💡 提示: 使用 'xiaozhi mcp ${serverName} list' 查看该服务的所有工具`\n )\n );\n return;\n }\n\n // 更新工具状态\n configManager.setToolEnabled(\n serverName,\n toolName,\n enabled,\n toolsConfig[toolName].description\n );\n\n spinner.succeed(\n `成功${action}工具 ${chalk.cyan(serverName)}/${chalk.cyan(toolName)}`\n );\n\n console.log();\n console.log(chalk.gray(\"💡 提示: 工具状态更改将在下次启动服务时生效\"));\n } catch (error) {\n spinner.fail(`${action}工具失败`);\n console.error(\n chalk.red(\n `错误: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n process.exit(1);\n }\n}\n","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"],"mappings":"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","names":["chalk","Table","ora","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","getDisplayWidth","str","width","char","__name","truncateToWidth","maxWidth","result","currentWidth","hasAddedChar","charWidth","listMcpServers","options","spinner","ora","mcpServers","configManager","serverNames","chalk","maxToolNameWidth","allToolNames","serverName","toolsConfig","toolNames","toolName","table","Table","toolConfig","status","description","serverConfig","toolCount","enabledCount","t","error","listServerTools","setToolEnabled","enabled","action"]}
package/dist/mcpPipe.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- var y=Object.defineProperty;var f=(c,e)=>y(c,"name",{value:e,configurable:!0});import{spawn as N}from"child_process";import i from"process";import{fileURLToPath as U}from"url";import{config as _}from"dotenv";import d from"ws";import{copyFileSync as M,existsSync as P,readFileSync as $,writeFileSync as R}from"fs";import{dirname as x,resolve as I}from"path";import{fileURLToPath as k}from"url";var O=x(k(import.meta.url)),C={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},v=class c{static{f(this,"ConfigManager")}static instance;defaultConfigPath;config=null;constructor(){this.defaultConfigPath=I(O,"xiaozhi.config.default.json")}getConfigFilePath(){let e=process.env.XIAOZHI_CONFIG_DIR||process.cwd();return I(e,"xiaozhi.config.json")}static getInstance(){return c.instance||(c.instance=new c),c.instance}configExists(){let e=this.getConfigFilePath();return P(e)}initConfig(){if(!P(this.defaultConfigPath))throw new Error("\u9ED8\u8BA4\u914D\u7F6E\u6587\u4EF6 xiaozhi.config.default.json \u4E0D\u5B58\u5728");if(this.configExists())throw new Error("\u914D\u7F6E\u6587\u4EF6 xiaozhi.config.json \u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u91CD\u590D\u521D\u59CB\u5316");let e=this.getConfigFilePath();M(this.defaultConfigPath,e),this.config=null}loadConfig(){if(!this.configExists())throw new Error("\u914D\u7F6E\u6587\u4EF6 xiaozhi.config.json \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u8FD0\u884C xiaozhi init \u521D\u59CB\u5316\u914D\u7F6E");try{let e=this.getConfigFilePath(),t=$(e,"utf8"),o=JSON.parse(t);return this.validateConfig(o),o}catch(e){throw e instanceof SyntaxError?new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF: ${e.message}`):e}}validateConfig(e){if(!e||typeof e!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A\u6839\u5BF9\u8C61\u65E0\u6548");let t=e;if(!t.mcpEndpoint||typeof t.mcpEndpoint!="string")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5B57\u6BB5\u65E0\u6548");if(!t.mcpServers||typeof t.mcpServers!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers \u5B57\u6BB5\u65E0\u6548");for(let[o,r]of Object.entries(t.mcpServers)){if(!r||typeof r!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o} \u65E0\u6548`);let s=r;if(s.type==="sse"){if(!s.url||typeof s.url!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.url \u65E0\u6548`)}else{if(!s.command||typeof s.command!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.command \u65E0\u6548`);if(!Array.isArray(s.args))throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.args \u5FC5\u987B\u662F\u6570\u7EC4`);if(s.env&&typeof s.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(e){return this.getMcpServerConfig()[e]?.tools||{}}isToolEnabled(e,t){return this.getServerToolsConfig(e)[t]?.enable!==!1}updateMcpEndpoint(e){if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let o={...this.getConfig(),mcpEndpoint:e};this.saveConfig(o)}updateMcpServer(e,t){if(!e||typeof e!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if("type"in t&&t.type==="sse"){if(!t.url||typeof t.url!="string")throw new Error("SSE \u670D\u52A1\u914D\u7F6E\u7684 url \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else{let s=t;if(!s.command||typeof s.command!="string")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 command \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(!Array.isArray(s.args))throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 args \u5B57\u6BB5\u5FC5\u987B\u662F\u6570\u7EC4");if(s.env&&typeof s.env!="object")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 env \u5B57\u6BB5\u5FC5\u987B\u662F\u5BF9\u8C61")}let o=this.getConfig(),r={...o,mcpServers:{...o.mcpServers,[e]:t}};this.saveConfig(r)}removeMcpServer(e){if(!e||typeof e!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let t=this.getConfig();if(!t.mcpServers[e])throw new Error(`\u670D\u52A1 ${e} \u4E0D\u5B58\u5728`);let o={...t.mcpServers};delete o[e];let r={...t,mcpServers:o};this.saveConfig(r)}updateServerToolsConfig(e,t){let r={...this.getConfig()};r.mcpServerConfig||(r.mcpServerConfig={}),r.mcpServerConfig[e]={tools:t},this.saveConfig(r)}setToolEnabled(e,t,o,r){let a={...this.getConfig()};a.mcpServerConfig||(a.mcpServerConfig={}),a.mcpServerConfig[e]||(a.mcpServerConfig[e]={tools:{}}),a.mcpServerConfig[e].tools[t]={enable:o,...r&&{description:r}},this.saveConfig(a)}saveConfig(e){try{this.validateConfig(e);let t=this.getConfigFilePath(),o=JSON.stringify(e,null,2);R(t,o,"utf8"),this.config=e}catch(t){throw new Error(`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`)}}reloadConfig(){this.config=null}getConfigPath(){return this.getConfigFilePath()}getDefaultConfigPath(){return this.defaultConfigPath}getConnectionConfig(){let t=this.getConfig().connection||{};return{heartbeatInterval:t.heartbeatInterval??C.heartbeatInterval,heartbeatTimeout:t.heartbeatTimeout??C.heartbeatTimeout,reconnectInterval:t.reconnectInterval??C.reconnectInterval}}getHeartbeatInterval(){return this.getConnectionConfig().heartbeatInterval}getHeartbeatTimeout(){return this.getConnectionConfig().heartbeatTimeout}getReconnectInterval(){return this.getConnectionConfig().reconnectInterval}updateConnectionConfig(e){let t=this.getConfig(),r={...t.connection||{},...e},s={...t,connection:r};this.saveConfig(s)}setHeartbeatInterval(e){if(e<=0)throw new Error("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatInterval:e})}setHeartbeatTimeout(e){if(e<=0)throw new Error("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatTimeout:e})}setReconnectInterval(e){if(e<=0)throw new Error("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({reconnectInterval:e})}getModelScopeConfig(){return this.getConfig().modelscope||{}}getModelScopeApiKey(){return this.getModelScopeConfig().apiKey||process.env.MODELSCOPE_API_TOKEN}updateModelScopeConfig(e){let t=this.getConfig(),r={...t.modelscope||{},...e},s={...t,modelscope:r};this.saveConfig(s)}setModelScopeApiKey(e){if(!e||typeof e!="string")throw new Error("API Key \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");this.updateModelScopeConfig({apiKey:e})}},l=v.getInstance();import u from"fs";import F from"path";import p from"chalk";import{createConsola as A}from"consola";function D(c){let e=c.getFullYear(),t=String(c.getMonth()+1).padStart(2,"0"),o=String(c.getDate()).padStart(2,"0"),r=String(c.getHours()).padStart(2,"0"),s=String(c.getMinutes()).padStart(2,"0"),a=String(c.getSeconds()).padStart(2,"0");return`${e}-${t}-${o} ${r}:${s}:${a}`}f(D,"formatDateTime");var S=class{static{f(this,"Logger")}logFilePath=null;writeStream=null;consolaInstance;isDaemonMode;constructor(){this.isDaemonMode=process.env.XIAOZHI_DAEMON==="true",this.consolaInstance=A({formatOptions:{date:!1,colors:!0,compact:!0},fancy:!1});let e=this.isDaemonMode;this.consolaInstance.setReporters([{log:f(t=>{let o={info:"INFO",success:"SUCCESS",warn:"WARN",error:"ERROR",debug:"DEBUG",log:"LOG"},r={info:p.blue,success:p.green,warn:p.yellow,error:p.red,debug:p.gray,log:f(g=>g,"log")},s=o[t.type]||t.type.toUpperCase(),a=r[t.type]||(g=>g),h=D(new Date),T=a(`[${s}]`),E=`[${h}] ${T} ${t.args.join(" ")}`;if(!e)try{console.error(E)}catch(g){if(g instanceof Error&&g.message?.includes("EPIPE"))return;throw g}},"log")}])}initLogFile(e){this.logFilePath=F.join(e,"xiaozhi.log"),u.existsSync(this.logFilePath)||u.writeFileSync(this.logFilePath,""),this.writeStream=u.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"})}logToFile(e,t,...o){if(this.writeStream){let s=`[${new Date().toISOString()}] [${e.toUpperCase()}] ${t}`,a=o.length>0?`${s} ${o.map(h=>typeof h=="object"?JSON.stringify(h):String(h)).join(" ")}`:s;this.writeStream.write(`${a}
3
- `)}}enableFileLogging(e){e&&!this.writeStream&&this.logFilePath?this.writeStream=u.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"}):!e&&this.writeStream&&(this.writeStream.end(),this.writeStream=null)}info(e,...t){this.consolaInstance.info(e,...t),this.logToFile("info",e,...t)}success(e,...t){this.consolaInstance.success(e,...t),this.logToFile("success",e,...t)}warn(e,...t){this.consolaInstance.warn(e,...t),this.logToFile("warn",e,...t)}error(e,...t){this.consolaInstance.error(e,...t),this.logToFile("error",e,...t)}debug(e,...t){this.consolaInstance.debug(e,...t),this.logToFile("debug",e,...t)}log(e,...t){this.consolaInstance.log(e,...t),this.logToFile("log",e,...t)}withTag(e){return this}close(){this.writeStream&&(this.writeStream.end(),this.writeStream=null)}},m=new S;_();var n=m.withTag("MCP_PIPE");i.env.XIAOZHI_DAEMON==="true"&&i.env.XIAOZHI_CONFIG_DIR&&(m.initLogFile(i.env.XIAOZHI_CONFIG_DIR),m.enableFileLogging(!0));var b=0,w=class{static{f(this,"MCPPipe")}mcpScript;endpointUrl;process;websocket;shouldReconnect;isConnected;shutdownResolve;heartbeatTimer;heartbeatTimeoutTimer;reconnectTimer;mcpProcessRestartAttempts;stdoutBuffer;connectionConfig;constructor(e,t){this.mcpScript=e,this.endpointUrl=t,this.process=null,this.websocket=null,this.shouldReconnect=!0,this.isConnected=!1,this.mcpProcessRestartAttempts=0,this.stdoutBuffer="";try{this.connectionConfig=l.getConnectionConfig(),n.info(`\u8FDE\u63A5\u914D\u7F6E: \u5FC3\u8DF3\u95F4\u9694=${this.connectionConfig.heartbeatInterval}ms, \u5FC3\u8DF3\u8D85\u65F6=${this.connectionConfig.heartbeatTimeout}ms, \u91CD\u8FDE\u95F4\u9694=${this.connectionConfig.reconnectInterval}ms`)}catch(o){this.connectionConfig={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},n.warn(`\u65E0\u6CD5\u83B7\u53D6\u8FDE\u63A5\u914D\u7F6E\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C: ${o instanceof Error?o.message:String(o)}`)}}async start(){return this.startMCPProcess(),await this.connectToServer(),this.reportStatusToWebUI(),new Promise(e=>{this.shutdownResolve=e})}async connectToServer(){this.isConnected||(n.info("\u6B63\u5728\u8FDE\u63A5 WebSocket \u670D\u52A1\u5668..."),this.websocket=new d(this.endpointUrl),this.websocket.on("open",()=>{n.info("\u6210\u529F\u8FDE\u63A5\u5230 WebSocket \u670D\u52A1\u5668"),this.isConnected=!0,this.reportStatusToWebUI(),b=0,this.mcpProcessRestartAttempts=0,this.startHeartbeat()}),this.websocket.on("message",e=>{let t=e.toString();n.info(`<< WebSocket\u6536\u5230\u6D88\u606F: ${t}`),this.process?.stdin&&!this.process.stdin.destroyed&&this.process.stdin.write(`${t}
4
- `)}),this.websocket.on("close",(e,t)=>{n.error(`WebSocket \u8FDE\u63A5\u5DF2\u5173\u95ED: ${e} ${t}`),this.isConnected=!1,this.websocket=null,this.stopHeartbeat(),this.reportStatusToWebUI(),this.shouldReconnect&&e!==4004&&this.scheduleReconnect()}),this.websocket.on("error",e=>{n.error(`WebSocket \u9519\u8BEF: ${e.message}`),this.isConnected=!1,this.stopHeartbeat()}),this.websocket.on("pong",()=>{this.heartbeatTimeoutTimer&&(clearTimeout(this.heartbeatTimeoutTimer),this.heartbeatTimeoutTimer=void 0)}))}scheduleReconnect(){this.shouldReconnect&&(this.reconnectTimer&&clearTimeout(this.reconnectTimer),b++,n.info(`\u8BA1\u5212\u5728 ${(this.connectionConfig.reconnectInterval/1e3).toFixed(2)} \u79D2\u540E\u8FDB\u884C\u7B2C ${b} \u6B21\u91CD\u8FDE\u5C1D\u8BD5...`),this.reconnectTimer=setTimeout(()=>{this.shouldReconnect&&((!this.process||this.process.killed)&&(n.info("MCP \u8FDB\u7A0B\u672A\u8FD0\u884C\uFF0C\u6B63\u5728\u5C1D\u8BD5\u91CD\u542F..."),this.restartMCPProcess()),this.connectToServer())},this.connectionConfig.reconnectInterval))}startHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=setInterval(()=>{this.websocket&&this.websocket.readyState===d.OPEN&&(this.websocket.ping(),this.heartbeatTimeoutTimer=setTimeout(()=>{n.warn("\u5FC3\u8DF3\u8D85\u65F6\uFF0C\u8FDE\u63A5\u53EF\u80FD\u5DF2\u65AD\u5F00"),this.websocket&&this.websocket.terminate()},this.connectionConfig.heartbeatTimeout),this.reportStatusToWebUI())},this.connectionConfig.heartbeatInterval)}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=void 0),this.heartbeatTimeoutTimer&&(clearTimeout(this.heartbeatTimeoutTimer),this.heartbeatTimeoutTimer=void 0)}restartMCPProcess(){if(this.mcpProcessRestartAttempts>=3){n.error("MCP \u8FDB\u7A0B\u91CD\u542F\u5C1D\u8BD5\u6B21\u6570\u8D85\u9650\uFF0C\u653E\u5F03\u91CD\u542F");return}if(this.mcpProcessRestartAttempts++,n.info(`\u6B63\u5728\u5C1D\u8BD5\u91CD\u542F MCP \u8FDB\u7A0B\uFF08\u7B2C ${this.mcpProcessRestartAttempts} \u6B21\u5C1D\u8BD5\uFF09`),this.process){try{this.process.kill("SIGTERM")}catch(e){n.warn(`\u7EC8\u6B62\u73B0\u6709 MCP \u8FDB\u7A0B\u65F6\u51FA\u9519: ${e}`)}this.process=null}this.startMCPProcess()}startMCPProcess(){if(this.process){n.info(`${this.mcpScript} \u8FDB\u7A0B\u5DF2\u5728\u8FD0\u884C`);return}n.info(`\u6B63\u5728\u542F\u52A8 ${this.mcpScript} \u8FDB\u7A0B`),this.process=N("node",[this.mcpScript],{stdio:["pipe","pipe","pipe"]}),this.process.stdout?.on("data",e=>{this.stdoutBuffer+=e.toString();let t=this.stdoutBuffer.split(`
2
+ var y=Object.defineProperty;var g=(c,e)=>y(c,"name",{value:e,configurable:!0});import{spawn as A}from"child_process";import i from"process";import{fileURLToPath as D}from"url";import{config as N}from"dotenv";import d from"ws";import{copyFileSync as M,existsSync as I,readFileSync as $,writeFileSync as R}from"fs";import{dirname as U,resolve as P}from"path";import{fileURLToPath as x}from"url";var k=U(x(import.meta.url)),C={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},b=class c{static{g(this,"ConfigManager")}static instance;defaultConfigPath;config=null;constructor(){this.defaultConfigPath=P(k,"xiaozhi.config.default.json")}getConfigFilePath(){let e=process.env.XIAOZHI_CONFIG_DIR||process.cwd();return P(e,"xiaozhi.config.json")}static getInstance(){return c.instance||(c.instance=new c),c.instance}configExists(){let e=this.getConfigFilePath();return I(e)}initConfig(){if(!I(this.defaultConfigPath))throw new Error("\u9ED8\u8BA4\u914D\u7F6E\u6587\u4EF6 xiaozhi.config.default.json \u4E0D\u5B58\u5728");if(this.configExists())throw new Error("\u914D\u7F6E\u6587\u4EF6 xiaozhi.config.json \u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u91CD\u590D\u521D\u59CB\u5316");let e=this.getConfigFilePath();M(this.defaultConfigPath,e),this.config=null}loadConfig(){if(!this.configExists())throw new Error("\u914D\u7F6E\u6587\u4EF6 xiaozhi.config.json \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u8FD0\u884C xiaozhi init \u521D\u59CB\u5316\u914D\u7F6E");try{let e=this.getConfigFilePath(),t=$(e,"utf8"),o=JSON.parse(t);return this.validateConfig(o),o}catch(e){throw e instanceof SyntaxError?new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF: ${e.message}`):e}}validateConfig(e){if(!e||typeof e!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A\u6839\u5BF9\u8C61\u65E0\u6548");let t=e;if(!t.mcpEndpoint||typeof t.mcpEndpoint!="string")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5B57\u6BB5\u65E0\u6548");if(!t.mcpServers||typeof t.mcpServers!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers \u5B57\u6BB5\u65E0\u6548");for(let[o,r]of Object.entries(t.mcpServers)){if(!r||typeof r!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o} \u65E0\u6548`);let s=r;if(s.type==="sse"){if(!s.url||typeof s.url!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.url \u65E0\u6548`)}else{if(!s.command||typeof s.command!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.command \u65E0\u6548`);if(!Array.isArray(s.args))throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.args \u5FC5\u987B\u662F\u6570\u7EC4`);if(s.env&&typeof s.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(e){return this.getMcpServerConfig()[e]?.tools||{}}isToolEnabled(e,t){return this.getServerToolsConfig(e)[t]?.enable!==!1}updateMcpEndpoint(e){if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let o={...this.getConfig(),mcpEndpoint:e};this.saveConfig(o)}updateMcpServer(e,t){if(!e||typeof e!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if("type"in t&&t.type==="sse"){if(!t.url||typeof t.url!="string")throw new Error("SSE \u670D\u52A1\u914D\u7F6E\u7684 url \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else{let s=t;if(!s.command||typeof s.command!="string")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 command \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(!Array.isArray(s.args))throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 args \u5B57\u6BB5\u5FC5\u987B\u662F\u6570\u7EC4");if(s.env&&typeof s.env!="object")throw new Error("\u670D\u52A1\u914D\u7F6E\u7684 env \u5B57\u6BB5\u5FC5\u987B\u662F\u5BF9\u8C61")}let o=this.getConfig(),r={...o,mcpServers:{...o.mcpServers,[e]:t}};this.saveConfig(r)}removeMcpServer(e){if(!e||typeof e!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let t=this.getConfig();if(!t.mcpServers[e])throw new Error(`\u670D\u52A1 ${e} \u4E0D\u5B58\u5728`);let o={...t.mcpServers};delete o[e];let r={...t,mcpServers:o};this.saveConfig(r)}updateServerToolsConfig(e,t){let r={...this.getConfig()};r.mcpServerConfig||(r.mcpServerConfig={}),r.mcpServerConfig[e]={tools:t},this.saveConfig(r)}setToolEnabled(e,t,o,r){let a={...this.getConfig()};a.mcpServerConfig||(a.mcpServerConfig={}),a.mcpServerConfig[e]||(a.mcpServerConfig[e]={tools:{}}),a.mcpServerConfig[e].tools[t]={enable:o,...r&&{description:r}},this.saveConfig(a)}saveConfig(e){try{this.validateConfig(e);let t=this.getConfigFilePath(),o=JSON.stringify(e,null,2);R(t,o,"utf8"),this.config=e}catch(t){throw new Error(`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`)}}reloadConfig(){this.config=null}getConfigPath(){return this.getConfigFilePath()}getDefaultConfigPath(){return this.defaultConfigPath}getConnectionConfig(){let t=this.getConfig().connection||{};return{heartbeatInterval:t.heartbeatInterval??C.heartbeatInterval,heartbeatTimeout:t.heartbeatTimeout??C.heartbeatTimeout,reconnectInterval:t.reconnectInterval??C.reconnectInterval}}getHeartbeatInterval(){return this.getConnectionConfig().heartbeatInterval}getHeartbeatTimeout(){return this.getConnectionConfig().heartbeatTimeout}getReconnectInterval(){return this.getConnectionConfig().reconnectInterval}updateConnectionConfig(e){let t=this.getConfig(),r={...t.connection||{},...e},s={...t,connection:r};this.saveConfig(s)}setHeartbeatInterval(e){if(e<=0)throw new Error("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatInterval:e})}setHeartbeatTimeout(e){if(e<=0)throw new Error("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatTimeout:e})}setReconnectInterval(e){if(e<=0)throw new Error("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({reconnectInterval:e})}getModelScopeConfig(){return this.getConfig().modelscope||{}}getModelScopeApiKey(){return this.getModelScopeConfig().apiKey||process.env.MODELSCOPE_API_TOKEN}updateModelScopeConfig(e){let t=this.getConfig(),r={...t.modelscope||{},...e},s={...t,modelscope:r};this.saveConfig(s)}setModelScopeApiKey(e){if(!e||typeof e!="string")throw new Error("API Key \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");this.updateModelScopeConfig({apiKey:e})}getWebUIConfig(){return this.getConfig().webUI||{}}getWebUIPort(){return this.getWebUIConfig().port??9999}updateWebUIConfig(e){let t=this.getConfig(),r={...t.webUI||{},...e},s={...t,webUI:r};this.saveConfig(s)}setWebUIPort(e){if(!Number.isInteger(e)||e<=0||e>65535)throw new Error("\u7AEF\u53E3\u53F7\u5FC5\u987B\u662F 1-65535 \u4E4B\u95F4\u7684\u6574\u6570");this.updateWebUIConfig({port:e})}},l=b.getInstance();import u from"fs";import O from"path";import p from"chalk";import{createConsola as F}from"consola";function W(c){let e=c.getFullYear(),t=String(c.getMonth()+1).padStart(2,"0"),o=String(c.getDate()).padStart(2,"0"),r=String(c.getHours()).padStart(2,"0"),s=String(c.getMinutes()).padStart(2,"0"),a=String(c.getSeconds()).padStart(2,"0");return`${e}-${t}-${o} ${r}:${s}:${a}`}g(W,"formatDateTime");var v=class{static{g(this,"Logger")}logFilePath=null;writeStream=null;consolaInstance;isDaemonMode;constructor(){this.isDaemonMode=process.env.XIAOZHI_DAEMON==="true",this.consolaInstance=F({formatOptions:{date:!1,colors:!0,compact:!0},fancy:!1});let e=this.isDaemonMode;this.consolaInstance.setReporters([{log:g(t=>{let o={info:"INFO",success:"SUCCESS",warn:"WARN",error:"ERROR",debug:"DEBUG",log:"LOG"},r={info:p.blue,success:p.green,warn:p.yellow,error:p.red,debug:p.gray,log:g(f=>f,"log")},s=o[t.type]||t.type.toUpperCase(),a=r[t.type]||(f=>f),h=W(new Date),T=a(`[${s}]`),E=`[${h}] ${T} ${t.args.join(" ")}`;if(!e)try{console.error(E)}catch(f){if(f instanceof Error&&f.message?.includes("EPIPE"))return;throw f}},"log")}])}initLogFile(e){this.logFilePath=O.join(e,"xiaozhi.log"),u.existsSync(this.logFilePath)||u.writeFileSync(this.logFilePath,""),this.writeStream=u.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"})}logToFile(e,t,...o){if(this.writeStream){let s=`[${new Date().toISOString()}] [${e.toUpperCase()}] ${t}`,a=o.length>0?`${s} ${o.map(h=>typeof h=="object"?JSON.stringify(h):String(h)).join(" ")}`:s;this.writeStream.write(`${a}
3
+ `)}}enableFileLogging(e){e&&!this.writeStream&&this.logFilePath?this.writeStream=u.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"}):!e&&this.writeStream&&(this.writeStream.end(),this.writeStream=null)}info(e,...t){this.consolaInstance.info(e,...t),this.logToFile("info",e,...t)}success(e,...t){this.consolaInstance.success(e,...t),this.logToFile("success",e,...t)}warn(e,...t){this.consolaInstance.warn(e,...t),this.logToFile("warn",e,...t)}error(e,...t){this.consolaInstance.error(e,...t),this.logToFile("error",e,...t)}debug(e,...t){this.consolaInstance.debug(e,...t),this.logToFile("debug",e,...t)}log(e,...t){this.consolaInstance.log(e,...t),this.logToFile("log",e,...t)}withTag(e){return this}close(){this.writeStream&&(this.writeStream.end(),this.writeStream=null)}},m=new v;N();var n=m.withTag("MCP_PIPE");i.env.XIAOZHI_DAEMON==="true"&&i.env.XIAOZHI_CONFIG_DIR&&(m.initLogFile(i.env.XIAOZHI_CONFIG_DIR),m.enableFileLogging(!0));var S=0,w=class{static{g(this,"MCPPipe")}mcpScript;endpointUrl;process;websocket;shouldReconnect;isConnected;shutdownResolve;heartbeatTimer;heartbeatTimeoutTimer;reconnectTimer;mcpProcessRestartAttempts;stdoutBuffer;connectionConfig;constructor(e,t){this.mcpScript=e,this.endpointUrl=t,this.process=null,this.websocket=null,this.shouldReconnect=!0,this.isConnected=!1,this.mcpProcessRestartAttempts=0,this.stdoutBuffer="";try{this.connectionConfig=l.getConnectionConfig(),n.info(`\u8FDE\u63A5\u914D\u7F6E: \u5FC3\u8DF3\u95F4\u9694=${this.connectionConfig.heartbeatInterval}ms, \u5FC3\u8DF3\u8D85\u65F6=${this.connectionConfig.heartbeatTimeout}ms, \u91CD\u8FDE\u95F4\u9694=${this.connectionConfig.reconnectInterval}ms`)}catch(o){this.connectionConfig={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},n.warn(`\u65E0\u6CD5\u83B7\u53D6\u8FDE\u63A5\u914D\u7F6E\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C: ${o instanceof Error?o.message:String(o)}`)}}async start(){return this.startMCPProcess(),await this.connectToServer(),this.reportStatusToWebUI(),new Promise(e=>{this.shutdownResolve=e})}async connectToServer(){this.isConnected||(n.info("\u6B63\u5728\u8FDE\u63A5 WebSocket \u670D\u52A1\u5668..."),this.websocket=new d(this.endpointUrl),this.websocket.on("open",()=>{n.info("\u6210\u529F\u8FDE\u63A5\u5230 WebSocket \u670D\u52A1\u5668"),this.isConnected=!0,this.reportStatusToWebUI(),S=0,this.mcpProcessRestartAttempts=0,this.startHeartbeat()}),this.websocket.on("message",e=>{let t=e.toString();n.info(`<< WebSocket\u6536\u5230\u6D88\u606F: ${t}`),this.process?.stdin&&!this.process.stdin.destroyed&&this.process.stdin.write(`${t}
4
+ `)}),this.websocket.on("close",(e,t)=>{n.error(`WebSocket \u8FDE\u63A5\u5DF2\u5173\u95ED: ${e} ${t}`),this.isConnected=!1,this.websocket=null,this.stopHeartbeat(),this.reportStatusToWebUI(),this.shouldReconnect&&e!==4004&&this.scheduleReconnect()}),this.websocket.on("error",e=>{n.error(`WebSocket \u9519\u8BEF: ${e.message}`),this.isConnected=!1,this.stopHeartbeat()}),this.websocket.on("pong",()=>{this.heartbeatTimeoutTimer&&(clearTimeout(this.heartbeatTimeoutTimer),this.heartbeatTimeoutTimer=void 0)}))}scheduleReconnect(){this.shouldReconnect&&(this.reconnectTimer&&clearTimeout(this.reconnectTimer),S++,n.info(`\u8BA1\u5212\u5728 ${(this.connectionConfig.reconnectInterval/1e3).toFixed(2)} \u79D2\u540E\u8FDB\u884C\u7B2C ${S} \u6B21\u91CD\u8FDE\u5C1D\u8BD5...`),this.reconnectTimer=setTimeout(()=>{this.shouldReconnect&&((!this.process||this.process.killed)&&(n.info("MCP \u8FDB\u7A0B\u672A\u8FD0\u884C\uFF0C\u6B63\u5728\u5C1D\u8BD5\u91CD\u542F..."),this.restartMCPProcess()),this.connectToServer())},this.connectionConfig.reconnectInterval))}startHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=setInterval(()=>{this.websocket&&this.websocket.readyState===d.OPEN&&(this.websocket.ping(),this.heartbeatTimeoutTimer=setTimeout(()=>{n.warn("\u5FC3\u8DF3\u8D85\u65F6\uFF0C\u8FDE\u63A5\u53EF\u80FD\u5DF2\u65AD\u5F00"),this.websocket&&this.websocket.terminate()},this.connectionConfig.heartbeatTimeout),this.reportStatusToWebUI())},this.connectionConfig.heartbeatInterval)}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=void 0),this.heartbeatTimeoutTimer&&(clearTimeout(this.heartbeatTimeoutTimer),this.heartbeatTimeoutTimer=void 0)}restartMCPProcess(){if(this.mcpProcessRestartAttempts>=3){n.error("MCP \u8FDB\u7A0B\u91CD\u542F\u5C1D\u8BD5\u6B21\u6570\u8D85\u9650\uFF0C\u653E\u5F03\u91CD\u542F");return}if(this.mcpProcessRestartAttempts++,n.info(`\u6B63\u5728\u5C1D\u8BD5\u91CD\u542F MCP \u8FDB\u7A0B\uFF08\u7B2C ${this.mcpProcessRestartAttempts} \u6B21\u5C1D\u8BD5\uFF09`),this.process){try{this.process.kill("SIGTERM")}catch(e){n.warn(`\u7EC8\u6B62\u73B0\u6709 MCP \u8FDB\u7A0B\u65F6\u51FA\u9519: ${e}`)}this.process=null}this.startMCPProcess()}startMCPProcess(){if(this.process){n.info(`${this.mcpScript} \u8FDB\u7A0B\u5DF2\u5728\u8FD0\u884C`);return}n.info(`\u6B63\u5728\u542F\u52A8 ${this.mcpScript} \u8FDB\u7A0B`),this.process=A("node",[this.mcpScript],{stdio:["pipe","pipe","pipe"]}),this.process.stdout?.on("data",e=>{this.stdoutBuffer+=e.toString();let t=this.stdoutBuffer.split(`
5
5
  `);this.stdoutBuffer=t.pop()||"";for(let o of t)if(o.trim()&&(n.info(`>> mcpServerProxy\u53D1\u9001\u6D88\u606F\u957F\u5EA6: ${o.length} \u5B57\u8282`),n.info(`>> mcpServerProxy\u53D1\u9001\u6D88\u606F: ${o.substring(0,500)}...`),this.websocket&&this.websocket.readyState===d.OPEN))try{this.websocket.send(`${o}
6
- `),n.info(">> \u6210\u529F\u53D1\u9001\u6D88\u606F\u5230 WebSocket")}catch(r){n.error(`>> \u53D1\u9001\u6D88\u606F\u5230 WebSocket \u5931\u8D25: ${r}`)}}),this.process.stderr?.on("data",e=>{if(i.env.XIAOZHI_DAEMON!=="true")try{i.stderr.write(e)}catch{}}),this.process.on("exit",(e,t)=>{n.warn(`${this.mcpScript} \u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${e}, \u4FE1\u53F7: ${t}`),this.process=null,this.shouldReconnect&&t!=="SIGTERM"&&t!=="SIGKILL"&&n.info("MCP \u8FDB\u7A0B\u610F\u5916\u9000\u51FA\uFF0C\u5C06\u5728\u4E0B\u6B21\u91CD\u8FDE\u65F6\u5C1D\u8BD5\u91CD\u542F")}),this.process.on("error",e=>{n.error(`\u8FDB\u7A0B\u9519\u8BEF: ${e.message}`),this.process=null,this.shouldReconnect&&n.info("MCP \u8FDB\u7A0B\u53D1\u751F\u9519\u8BEF\uFF0C\u5C06\u5728\u4E0B\u6B21\u91CD\u8FDE\u65F6\u5C1D\u8BD5\u91CD\u542F")})}cleanup(){if(this.stopHeartbeat(),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0),this.stdoutBuffer="",this.process){n.info(`\u6B63\u5728\u7EC8\u6B62 ${this.mcpScript} \u8FDB\u7A0B`);try{this.process.kill("SIGTERM"),setTimeout(()=>{this.process&&!this.process.killed&&this.process.kill("SIGKILL")},5e3)}catch(e){n.error(`\u7EC8\u6B62\u8FDB\u7A0B\u65F6\u51FA\u9519: ${e instanceof Error?e.message:String(e)}`)}this.process=null}if(this.websocket){try{this.websocket.close()}catch(e){n.warn(`\u5173\u95ED WebSocket \u65F6\u51FA\u9519: ${e}`)}this.websocket=null}this.isConnected=!1}sleep(e){return new Promise(t=>setTimeout(t,e))}shutdown(){n.info("\u6B63\u5728\u5173\u95ED MCP Pipe..."),this.shouldReconnect=!1,this.isConnected=!1,this.reportStatusToWebUI(),this.cleanup(),this.websocket&&this.websocket.close(),this.shutdownResolve&&this.shutdownResolve(),setTimeout(()=>{i.exit(0)},100)}async reportStatusToWebUI(){if(!(i.env.NODE_ENV==="test"||i.env.VITEST==="true"))try{let e=new d("ws://localhost:9999");e.on("open",()=>{let t={type:"clientStatus",data:{status:this.isConnected?"connected":"disconnected",mcpEndpoint:this.endpointUrl,activeMCPServers:[],lastHeartbeat:Date.now()}};e.send(JSON.stringify(t)),n.info("\u5DF2\u5411 Web UI \u62A5\u544A\u72B6\u6001"),setTimeout(()=>{e.close()},1e3)}),e.on("error",t=>{n.debug(`Web UI \u8FDE\u63A5\u5931\u8D25\uFF08\u53EF\u80FD\u672A\u8FD0\u884C\uFF09: ${t.message}`)})}catch(e){n.debug(`\u5411 Web UI \u62A5\u544A\u72B6\u6001\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}};function H(c){let e=i.env.XIAOZHI_DAEMON==="true";i.on("SIGINT",()=>{n.info("\u6536\u5230\u4E2D\u65AD\u4FE1\u53F7\uFF0C\u6B63\u5728\u5173\u95ED..."),c.shutdown()}),i.on("SIGTERM",()=>{n.info("\u6536\u5230\u7EC8\u6B62\u4FE1\u53F7\uFF0C\u6B63\u5728\u5173\u95ED..."),c.shutdown()}),e&&(i.on("SIGHUP",()=>{n.info("\u6536\u5230 SIGHUP \u4FE1\u53F7\uFF08\u7EC8\u7AEF\u5DF2\u5173\u95ED\uFF09\uFF0C\u7EE7\u7EED\u5728\u5B88\u62A4\u8FDB\u7A0B\u6A21\u5F0F\u4E0B\u8FD0\u884C...")}),i.on("uncaughtException",t=>{if(!t.message?.includes("EPIPE"))try{n.error(`\u5B88\u62A4\u8FDB\u7A0B\u6A21\u5F0F\u4E0B\u7684\u672A\u6355\u83B7\u5F02\u5E38: ${t.message}`),n.error(t.stack||"No stack trace available")}catch{}}),i.on("unhandledRejection",(t,o)=>{n.error(`\u5B88\u62A4\u8FDB\u7A0B\u6A21\u5F0F\u4E0B\u7684\u672A\u5904\u7406 Promise \u62D2\u7EDD: ${t}`),n.error(`Promise: ${o}`)}),n.info("\u5B88\u62A4\u8FDB\u7A0B\u6A21\u5F0F\u4FE1\u53F7\u5904\u7406\u5668\u5DF2\u521D\u59CB\u5316"))}f(H,"setupSignalHandlers");async function W(){i.argv.length<3&&(n.error("\u7528\u6CD5: node mcp_pipe.js <mcp_script>"),i.exit(1));let c=i.argv[2],e;try{if(i.env.XIAOZHI_DAEMON!=="true")try{i.stderr.write(`[DEBUG] XIAOZHI_CONFIG_DIR: ${i.env.XIAOZHI_CONFIG_DIR}
6
+ `),n.info(">> \u6210\u529F\u53D1\u9001\u6D88\u606F\u5230 WebSocket")}catch(r){n.error(`>> \u53D1\u9001\u6D88\u606F\u5230 WebSocket \u5931\u8D25: ${r}`)}}),this.process.stderr?.on("data",e=>{if(i.env.XIAOZHI_DAEMON!=="true")try{i.stderr.write(e)}catch{}}),this.process.on("exit",(e,t)=>{n.warn(`${this.mcpScript} \u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${e}, \u4FE1\u53F7: ${t}`),this.process=null,this.shouldReconnect&&t!=="SIGTERM"&&t!=="SIGKILL"&&n.info("MCP \u8FDB\u7A0B\u610F\u5916\u9000\u51FA\uFF0C\u5C06\u5728\u4E0B\u6B21\u91CD\u8FDE\u65F6\u5C1D\u8BD5\u91CD\u542F")}),this.process.on("error",e=>{n.error(`\u8FDB\u7A0B\u9519\u8BEF: ${e.message}`),this.process=null,this.shouldReconnect&&n.info("MCP \u8FDB\u7A0B\u53D1\u751F\u9519\u8BEF\uFF0C\u5C06\u5728\u4E0B\u6B21\u91CD\u8FDE\u65F6\u5C1D\u8BD5\u91CD\u542F")})}cleanup(){if(this.stopHeartbeat(),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0),this.stdoutBuffer="",this.process){n.info(`\u6B63\u5728\u7EC8\u6B62 ${this.mcpScript} \u8FDB\u7A0B`);try{this.process.kill("SIGTERM"),setTimeout(()=>{this.process&&!this.process.killed&&this.process.kill("SIGKILL")},5e3)}catch(e){n.error(`\u7EC8\u6B62\u8FDB\u7A0B\u65F6\u51FA\u9519: ${e instanceof Error?e.message:String(e)}`)}this.process=null}if(this.websocket){try{this.websocket.close()}catch(e){n.warn(`\u5173\u95ED WebSocket \u65F6\u51FA\u9519: ${e}`)}this.websocket=null}this.isConnected=!1}sleep(e){return new Promise(t=>setTimeout(t,e))}shutdown(){n.info("\u6B63\u5728\u5173\u95ED MCP Pipe..."),this.shouldReconnect=!1,this.isConnected=!1,this.reportStatusToWebUI(),this.cleanup(),this.websocket&&this.websocket.close(),this.shutdownResolve&&this.shutdownResolve(),setTimeout(()=>{i.exit(0)},100)}async reportStatusToWebUI(){if(!(i.env.NODE_ENV==="test"||i.env.VITEST==="true"))try{let e=l.getWebUIPort(),t=new d(`ws://localhost:${e}`);t.on("open",()=>{let o={type:"clientStatus",data:{status:this.isConnected?"connected":"disconnected",mcpEndpoint:this.endpointUrl,activeMCPServers:[],lastHeartbeat:Date.now()}};t.send(JSON.stringify(o)),n.debug("\u5DF2\u5411 Web UI \u62A5\u544A\u72B6\u6001"),setTimeout(()=>{t.close()},1e3)}),t.on("error",o=>{n.debug(`Web UI \u8FDE\u63A5\u5931\u8D25\uFF08\u53EF\u80FD\u672A\u8FD0\u884C\uFF09: ${o.message}`)})}catch(e){n.debug(`\u5411 Web UI \u62A5\u544A\u72B6\u6001\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}};function _(c){let e=i.env.XIAOZHI_DAEMON==="true";i.on("SIGINT",()=>{n.info("\u6536\u5230\u4E2D\u65AD\u4FE1\u53F7\uFF0C\u6B63\u5728\u5173\u95ED..."),c.shutdown()}),i.on("SIGTERM",()=>{n.info("\u6536\u5230\u7EC8\u6B62\u4FE1\u53F7\uFF0C\u6B63\u5728\u5173\u95ED..."),c.shutdown()}),e&&(i.on("SIGHUP",()=>{n.info("\u6536\u5230 SIGHUP \u4FE1\u53F7\uFF08\u7EC8\u7AEF\u5DF2\u5173\u95ED\uFF09\uFF0C\u7EE7\u7EED\u5728\u5B88\u62A4\u8FDB\u7A0B\u6A21\u5F0F\u4E0B\u8FD0\u884C...")}),i.on("uncaughtException",t=>{if(!t.message?.includes("EPIPE"))try{n.error(`\u5B88\u62A4\u8FDB\u7A0B\u6A21\u5F0F\u4E0B\u7684\u672A\u6355\u83B7\u5F02\u5E38: ${t.message}`),n.error(t.stack||"No stack trace available")}catch{}}),i.on("unhandledRejection",(t,o)=>{n.error(`\u5B88\u62A4\u8FDB\u7A0B\u6A21\u5F0F\u4E0B\u7684\u672A\u5904\u7406 Promise \u62D2\u7EDD: ${t}`),n.error(`Promise: ${o}`)}),n.info("\u5B88\u62A4\u8FDB\u7A0B\u6A21\u5F0F\u4FE1\u53F7\u5904\u7406\u5668\u5DF2\u521D\u59CB\u5316"))}g(_,"setupSignalHandlers");async function H(){i.argv.length<3&&(n.error("\u7528\u6CD5: node mcp_pipe.js <mcp_script>"),i.exit(1));let c=i.argv[2],e;try{if(i.env.XIAOZHI_DAEMON!=="true")try{i.stderr.write(`[DEBUG] XIAOZHI_CONFIG_DIR: ${i.env.XIAOZHI_CONFIG_DIR}
7
7
  `),i.stderr.write(`[DEBUG] process.cwd(): ${i.cwd()}
8
8
  `),i.stderr.write(`[DEBUG] configManager.getConfigPath(): ${l.getConfigPath()}
9
9
  `),i.stderr.write(`[DEBUG] configManager.configExists(): ${l.configExists()}
10
- `)}catch{}l.configExists()?(e=l.getMcpEndpoint(),n.info("\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6\u4E2D\u7684 MCP \u7AEF\u70B9")):(e=i.env.MCP_ENDPOINT||"",e||(n.error("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\u4E14\u672A\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF"),n.error('\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF'),i.exit(1)),n.info("\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684 MCP \u7AEF\u70B9\uFF08\u5EFA\u8BAE\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6\uFF09"))}catch(o){n.error(`\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`),e=i.env.MCP_ENDPOINT||"",e||(n.error('\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF'),i.exit(1)),n.info("\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684 MCP \u7AEF\u70B9\u4F5C\u4E3A\u5907\u7528\u65B9\u6848")}(!e||e.includes("<\u8BF7\u586B\u5199"))&&(n.error("MCP \u7AEF\u70B9\u672A\u914D\u7F6E\u6216\u914D\u7F6E\u65E0\u6548"),n.error('\u8BF7\u8FD0\u884C "xiaozhi config mcpEndpoint <your-endpoint-url>" \u8BBE\u7F6E\u7AEF\u70B9'),i.exit(1));let t=new w(c,e);H(t);try{await t.start()}catch(o){n.error(`\u7A0B\u5E8F\u6267\u884C\u9519\u8BEF: ${o instanceof Error?o.message:String(o)}`),i.exit(1)}}f(W,"main");var G=import.meta.url,L=U(G),j=i.argv[1];L===j&&W().catch(c=>{n.error(`\u672A\u5904\u7406\u7684\u9519\u8BEF: ${c instanceof Error?c.message:String(c)}`),i.exit(1)});export{w as MCPPipe,H as setupSignalHandlers};
10
+ `)}catch{}l.configExists()?(e=l.getMcpEndpoint(),n.info("\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6\u4E2D\u7684 MCP \u7AEF\u70B9")):(e=i.env.MCP_ENDPOINT||"",e||(n.error("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\u4E14\u672A\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF"),n.error('\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF'),i.exit(1)),n.info("\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684 MCP \u7AEF\u70B9\uFF08\u5EFA\u8BAE\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6\uFF09"))}catch(o){n.error(`\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`),e=i.env.MCP_ENDPOINT||"",e||(n.error('\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF'),i.exit(1)),n.info("\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684 MCP \u7AEF\u70B9\u4F5C\u4E3A\u5907\u7528\u65B9\u6848")}(!e||e.includes("<\u8BF7\u586B\u5199"))&&(n.error("MCP \u7AEF\u70B9\u672A\u914D\u7F6E\u6216\u914D\u7F6E\u65E0\u6548"),n.error('\u8BF7\u8FD0\u884C "xiaozhi config mcpEndpoint <your-endpoint-url>" \u8BBE\u7F6E\u7AEF\u70B9'),i.exit(1));let t=new w(c,e);_(t);try{await t.start()}catch(o){n.error(`\u7A0B\u5E8F\u6267\u884C\u9519\u8BEF: ${o instanceof Error?o.message:String(o)}`),i.exit(1)}}g(H,"main");var G=import.meta.url,L=D(G),j=i.argv[1];L===j&&H().catch(c=>{n.error(`\u672A\u5904\u7406\u7684\u9519\u8BEF: ${c instanceof Error?c.message:String(c)}`),i.exit(1)});export{w as MCPPipe,_ as setupSignalHandlers};
11
11
  //# sourceMappingURL=mcpPipe.js.map