xiaozhi-client 1.6.0-beta.2 → 1.6.0-beta.5

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.
Files changed (45) hide show
  1. package/README.md +98 -0
  2. package/dist/adaptiveMCPPipe.js +10 -10
  3. package/dist/adaptiveMCPPipe.js.map +1 -1
  4. package/dist/autoCompletion.js +1 -1
  5. package/dist/autoCompletion.js.map +1 -1
  6. package/dist/cli.js +19 -19
  7. package/dist/cli.js.map +1 -1
  8. package/dist/configManager.d.ts +12 -1
  9. package/dist/configManager.js +1 -1
  10. package/dist/configManager.js.map +1 -1
  11. package/dist/mcpCommands.js +1 -1
  12. package/dist/mcpCommands.js.map +1 -1
  13. package/dist/mcpServerProxy.js +7 -7
  14. package/dist/mcpServerProxy.js.map +1 -1
  15. package/dist/modelScopeMCPClient.js +2 -2
  16. package/dist/modelScopeMCPClient.js.map +1 -1
  17. package/dist/multiEndpointMCPPipe.js +6 -6
  18. package/dist/multiEndpointMCPPipe.js.map +1 -1
  19. package/dist/package.json +14 -2
  20. package/dist/services/mcpServer.js +9 -9
  21. package/dist/services/mcpServer.js.map +1 -1
  22. package/dist/streamableHttpMCPClient.js +2 -2
  23. package/dist/streamableHttpMCPClient.js.map +1 -1
  24. package/dist/templates/docker/mcpServers/calculator.js +106 -0
  25. package/dist/templates/docker/mcpServers/datetime.js +390 -0
  26. package/dist/templates/docker/package.json +13 -0
  27. package/dist/templates/docker/xiaozhi.config.json +21 -0
  28. package/dist/templates/modelscope/xiaozhi.config.json +20 -0
  29. package/dist/webServer.d.ts +6 -2
  30. package/dist/webServer.js +19 -19
  31. package/dist/webServer.js.map +1 -1
  32. package/docs/images/web-ui-preview.png +0 -0
  33. package/package.json +12 -2
  34. package/templates/docker/mcpServers/calculator.js +106 -0
  35. package/templates/docker/mcpServers/datetime.js +390 -0
  36. package/templates/docker/package.json +13 -0
  37. package/templates/docker/xiaozhi.config.json +21 -0
  38. package/templates/modelscope/xiaozhi.config.json +20 -0
  39. package/web/dist/assets/index-Da0jgqOv.css +1 -0
  40. package/web/dist/assets/index-DaMLY0Ce.js +236 -0
  41. package/web/dist/assets/index-DaMLY0Ce.js.map +1 -0
  42. package/web/dist/index.html +2 -2
  43. package/web/dist/assets/index-LtUI_xx4.css +0 -1
  44. package/web/dist/assets/index-lAiGa4ms.js +0 -169
  45. package/web/dist/assets/index-lAiGa4ms.js.map +0 -1
@@ -1,15 +1,15 @@
1
- var F=Object.defineProperty;var f=(a,e)=>F(a,"name",{value:e,configurable:!0});import{spawn as Z}from"child_process";import{randomUUID as q}from"crypto";import{EventEmitter as K}from"events";import y from"fs";import V from"os";import p from"path";import{fileURLToPath as O}from"url";import x from"express";import{copyFileSync as _,existsSync as E,readFileSync as N,writeFileSync as D}from"fs";import{dirname as W,resolve as d}from"path";import{fileURLToPath as H}from"url";import L from"json5";import*as $ from"jsonc-parser";var G=W(H(import.meta.url)),I={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},M=class a{static{f(this,"ConfigManager")}static instance;defaultConfigPath;config=null;constructor(){this.defaultConfigPath=d(G,"xiaozhi.config.default.json")}getConfigFilePath(){let e=process.env.XIAOZHI_CONFIG_DIR||process.cwd(),t=["xiaozhi.config.json5","xiaozhi.config.jsonc","xiaozhi.config.json"];for(let o of t){let n=d(e,o);if(E(n))return n}return d(e,"xiaozhi.config.json")}getConfigFileFormat(e){return e.endsWith(".json5")?"json5":e.endsWith(".jsonc")?"jsonc":"json"}static getInstance(){return a.instance||(a.instance=new a),a.instance}configExists(){let e=process.env.XIAOZHI_CONFIG_DIR||process.cwd(),t=["xiaozhi.config.json5","xiaozhi.config.jsonc","xiaozhi.config.json"];for(let o of t){let n=d(e,o);if(E(n))return!0}return!1}initConfig(e="json"){if(!E(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\u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u91CD\u590D\u521D\u59CB\u5316");let t=process.env.XIAOZHI_CONFIG_DIR||process.cwd(),o=`xiaozhi.config.${e}`,n=d(t,o);_(this.defaultConfigPath,n),this.config=null}loadConfig(){if(!this.configExists())throw new Error("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u8FD0\u884C xiaozhi init \u521D\u59CB\u5316\u914D\u7F6E");try{let e=this.getConfigFilePath(),t=this.getConfigFileFormat(e),o=N(e,"utf8"),n;switch(t){case"json5":n=L.parse(o);break;case"jsonc":n=$.parse(o);break;default:n=JSON.parse(o);break}return this.validateConfig(n),n}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===void 0||t.mcpEndpoint===null)throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5B57\u6BB5\u65E0\u6548");if(typeof t.mcpEndpoint!="string")if(Array.isArray(t.mcpEndpoint)){if(t.mcpEndpoint.length===0)throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u6570\u7EC4\u4E0D\u80FD\u4E3A\u7A7A");for(let o of t.mcpEndpoint)if(typeof o!="string"||o.trim()==="")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u6570\u7EC4\u4E2D\u7684\u6BCF\u4E2A\u5143\u7D20\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6216\u5B57\u7B26\u4E32\u6570\u7EC4");if(!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,n]of Object.entries(t.mcpServers)){if(!n||typeof n!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o} \u65E0\u6548`);let i=n;if(i.url&&typeof i.url=="string"){if(i.type&&i.type!=="sse"&&i.type!=="streamable-http")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.type \u5FC5\u987B\u662F "sse" \u6216 "streamable-http"`)}else{if(!i.command||typeof i.command!="string")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.command \u65E0\u6548`);if(!Array.isArray(i.args))throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o}.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.${o}.env \u5FC5\u987B\u662F\u5BF9\u8C61`)}}}getConfig(){return this.config||(this.config=this.loadConfig()),JSON.parse(JSON.stringify(this.config))}getMcpEndpoint(){let e=this.getConfig();return Array.isArray(e.mcpEndpoint)?e.mcpEndpoint[0]||"":e.mcpEndpoint}getMcpEndpoints(){let e=this.getConfig();return Array.isArray(e.mcpEndpoint)?[...e.mcpEndpoint]:e.mcpEndpoint?[e.mcpEndpoint]:[]}getMcpServers(){return this.getConfig().mcpServers}getMcpServerConfig(){return this.getConfig().mcpServerConfig||{}}getServerToolsConfig(e){return this.getMcpServerConfig()[e]?.tools||{}}isToolEnabled(e,t){return this.getServerToolsConfig(e)[t]?.enable!==!1}updateMcpEndpoint(e){if(Array.isArray(e)){if(e.length===0)throw new Error("MCP \u7AEF\u70B9\u6570\u7EC4\u4E0D\u80FD\u4E3A\u7A7A");for(let n of e)if(!n||typeof n!="string")throw new Error("MCP \u7AEF\u70B9\u6570\u7EC4\u4E2D\u7684\u6BCF\u4E2A\u5143\u7D20\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let o={...this.getConfig(),mcpEndpoint:e};this.saveConfig(o)}addMcpEndpoint(e){if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let t=this.getConfig(),o=this.getMcpEndpoints();if(o.includes(e))throw new Error(`MCP \u7AEF\u70B9 ${e} \u5DF2\u5B58\u5728`);let n=[...o,e],i={...t,mcpEndpoint:n};this.saveConfig(i)}removeMcpEndpoint(e){if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let t=this.getConfig(),o=this.getMcpEndpoints();if(o.indexOf(e)===-1)throw new Error(`MCP \u7AEF\u70B9 ${e} \u4E0D\u5B58\u5728`);if(o.length===1)throw new Error("\u4E0D\u80FD\u5220\u9664\u6700\u540E\u4E00\u4E2A MCP \u7AEF\u70B9");let i=o.filter(g=>g!==e),c={...t,mcpEndpoint:i};this.saveConfig(c)}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 i=t;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 o=this.getConfig(),n={...o,mcpServers:{...o.mcpServers,[e]:t}};this.saveConfig(n)}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 n={...t,mcpServers:o};this.saveConfig(n)}updateServerToolsConfig(e,t){let n={...this.getConfig()};n.mcpServerConfig||(n.mcpServerConfig={}),Object.keys(t).length===0?delete n.mcpServerConfig[e]:n.mcpServerConfig[e]={tools:t},this.saveConfig(n)}removeServerToolsConfig(e){let o={...this.getConfig()};o.mcpServerConfig&&(delete o.mcpServerConfig[e],this.saveConfig(o))}setToolEnabled(e,t,o,n){let c={...this.getConfig()};c.mcpServerConfig||(c.mcpServerConfig={}),c.mcpServerConfig[e]||(c.mcpServerConfig[e]={tools:{}}),c.mcpServerConfig[e].tools[t]={enable:o,...n&&{description:n}},this.saveConfig(c)}saveConfig(e){try{this.validateConfig(e);let t=process.env.XIAOZHI_CONFIG_DIR||process.cwd(),o=d(t,"xiaozhi.config.json"),n=JSON.stringify(e,null,2);D(o,n,"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??I.heartbeatInterval,heartbeatTimeout:t.heartbeatTimeout??I.heartbeatTimeout,reconnectInterval:t.reconnectInterval??I.reconnectInterval}}getHeartbeatInterval(){return this.getConnectionConfig().heartbeatInterval}getHeartbeatTimeout(){return this.getConnectionConfig().heartbeatTimeout}getReconnectInterval(){return this.getConnectionConfig().reconnectInterval}updateConnectionConfig(e){let t=this.getConfig(),n={...t.connection||{},...e},i={...t,connection:n};this.saveConfig(i)}setHeartbeatInterval(e){if(e<=0)throw new Error("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatInterval:e})}setHeartbeatTimeout(e){if(e<=0)throw new Error("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({heartbeatTimeout:e})}setReconnectInterval(e){if(e<=0)throw new Error("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");this.updateConnectionConfig({reconnectInterval:e})}getModelScopeConfig(){return this.getConfig().modelscope||{}}getModelScopeApiKey(){return this.getModelScopeConfig().apiKey||process.env.MODELSCOPE_API_TOKEN}updateModelScopeConfig(e){let t=this.getConfig(),n={...t.modelscope||{},...e},i={...t,modelscope:n};this.saveConfig(i)}setModelScopeApiKey(e){if(!e||typeof e!="string")throw new Error("API Key \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");this.updateModelScopeConfig({apiKey:e})}getWebUIConfig(){return this.getConfig().webUI||{}}getWebUIPort(){return this.getWebUIConfig().port??9999}updateWebUIConfig(e){let t=this.getConfig(),n={...t.webUI||{},...e},i={...t,webUI:n};this.saveConfig(i)}setWebUIPort(e){if(!Number.isInteger(e)||e<=0||e>65535)throw new Error("\u7AEF\u53E3\u53F7\u5FC5\u987B\u662F 1-65535 \u4E4B\u95F4\u7684\u6574\u6570");this.updateWebUIConfig({port:e})}},u=M.getInstance();import w from"fs";import J from"path";import C from"chalk";import{createConsola as B}from"consola";function z(a){let e=a.getFullYear(),t=String(a.getMonth()+1).padStart(2,"0"),o=String(a.getDate()).padStart(2,"0"),n=String(a.getHours()).padStart(2,"0"),i=String(a.getMinutes()).padStart(2,"0"),c=String(a.getSeconds()).padStart(2,"0");return`${e}-${t}-${o} ${n}:${i}:${c}`}f(z,"formatDateTime");var T=class{static{f(this,"Logger")}logFilePath=null;writeStream=null;consolaInstance;isDaemonMode;constructor(){this.isDaemonMode=process.env.XIAOZHI_DAEMON==="true",this.consolaInstance=B({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"},n={info:C.blue,success:C.green,warn:C.yellow,error:C.red,debug:C.gray,log:f(h=>h,"log")},i=o[t.type]||t.type.toUpperCase(),c=n[t.type]||(h=>h),g=z(new Date),j=c(`[${i}]`),k=`[${g}] ${j} ${t.args.join(" ")}`;if(!e)try{console.error(k)}catch(h){if(h instanceof Error&&h.message?.includes("EPIPE"))return;throw h}},"log")}])}initLogFile(e){this.logFilePath=J.join(e,"xiaozhi.log"),w.existsSync(this.logFilePath)||w.writeFileSync(this.logFilePath,""),this.writeStream=w.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"})}logToFile(e,t,...o){if(this.writeStream){let i=`[${new Date().toISOString()}] [${e.toUpperCase()}] ${t}`,c=o.length>0?`${i} ${o.map(g=>typeof g=="object"?JSON.stringify(g):String(g)).join(" ")}`:i;this.writeStream.write(`${c}
2
- `)}}enableFileLogging(e){e&&!this.writeStream&&this.logFilePath?this.writeStream=w.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 T;import{spawn as X}from"child_process";import l from"process";import v from"ws";var R=f(()=>l.env.NODE_ENV==="test"||l.env.VITEST==="true","isTestEnvironment"),r=m.withTag("MULTI_MCP_PIPE");l.env.XIAOZHI_DAEMON==="true"&&l.env.XIAOZHI_CONFIG_DIR&&(m.initLogFile(l.env.XIAOZHI_CONFIG_DIR),m.enableFileLogging(!0));var S=class{static{f(this,"MultiEndpointMCPPipe")}mcpScript;endpoints;shouldReconnect;shutdownResolve;connectionConfig;constructor(e,t){this.mcpScript=e,this.endpoints=new Map,this.shouldReconnect=!0,r.info(t.length===1?`\u521D\u59CB\u5316\u5355\u7AEF\u70B9\u8FDE\u63A5: ${t[0]}`:`\u521D\u59CB\u5316\u591A\u7AEF\u70B9\u8FDE\u63A5\uFF08${t.length} \u4E2A\u7AEF\u70B9\uFF09`);for(let o of t)this.endpoints.set(o,{url:o,websocket:null,isConnected:!1,reconnectAttempt:0,maxReconnectAttempts:5,process:null,stdoutBuffer:""});try{this.connectionConfig=u.getConnectionConfig(),r.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},r.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 await this.connectToAllEndpoints(),this.reportStatusToWebUI(),new Promise(e=>{this.shutdownResolve=e})}async connectToAllEndpoints(){let e=[];for(let[t,o]of this.endpoints)e.push(this.connectToEndpoint(t));await Promise.allSettled(e)}async connectToEndpoint(e){let t=this.endpoints.get(e);if(!t||t.isConnected)return;this.startMCPProcessForEndpoint(e),r.info(`\u6B63\u5728\u8FDE\u63A5\u5230 WebSocket \u670D\u52A1\u5668: ${e}`);let o=new v(e);t.websocket=o,o.on("open",()=>{r.info(`\u6210\u529F\u8FDE\u63A5\u5230 WebSocket \u670D\u52A1\u5668: ${e}`),t.isConnected=!0,t.reconnectAttempt=0,t.reconnectTimer&&(clearTimeout(t.reconnectTimer),t.reconnectTimer=void 0),this.reportStatusToWebUI(),this.startHeartbeat(e)}),o.on("message",n=>{let i=n.toString();r.info(`<< [${e}] WebSocket\u6536\u5230\u6D88\u606F: ${i}`);try{let c=JSON.parse(i);(c.method==="notifications/initialized"||c.method==="tools/list"&&c.id||c?.result?.tools)&&setTimeout(()=>{this.reportStatusToWebUI()},1e3)}catch{}t.process?.stdin&&!t.process.stdin.destroyed&&t.process.stdin.write(`${i}
3
- `)}),o.on("close",(n,i)=>{r.error(`[${e}] WebSocket \u8FDE\u63A5\u5DF2\u5173\u95ED: ${n} ${i}`),t.isConnected=!1,t.websocket=null,this.stopHeartbeat(e),this.reportStatusToWebUI(),this.shouldReconnect&&(n===4004?t.reconnectAttempt<t.maxReconnectAttempts?(r.warn(`[${e}] \u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF(4004)\uFF0C\u5C06\u8FDB\u884C\u7B2C ${t.reconnectAttempt+1} \u6B21\u91CD\u8FDE\u5C1D\u8BD5\uFF08\u6700\u591A ${t.maxReconnectAttempts} \u6B21\uFF09`),this.scheduleReconnect(e)):r.error(`[${e}] \u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF(4004)\uFF0C\u5DF2\u8FBE\u5230\u6700\u5927\u91CD\u8FDE\u6B21\u6570(${t.maxReconnectAttempts})\uFF0C\u505C\u6B62\u91CD\u8FDE`):this.scheduleReconnect(e))}),o.on("error",n=>{r.error(`[${e}] WebSocket \u9519\u8BEF: ${n.message}`),t.isConnected=!1,this.stopHeartbeat(e)}),o.on("pong",()=>{t.heartbeatTimeoutTimer&&(clearTimeout(t.heartbeatTimeoutTimer),t.heartbeatTimeoutTimer=void 0)})}scheduleReconnect(e){let t=this.endpoints.get(e);if(!t||!this.shouldReconnect)return;t.reconnectTimer&&clearTimeout(t.reconnectTimer),t.reconnectAttempt++;let o=this.connectionConfig.reconnectInterval,i=Math.min(o*2**(t.reconnectAttempt-1),6e4);r.info(`[${e}] \u8BA1\u5212\u5728 ${(i/1e3).toFixed(2)} \u79D2\u540E\u8FDB\u884C\u7B2C ${t.reconnectAttempt} \u6B21\u91CD\u8FDE\u5C1D\u8BD5...`),t.reconnectTimer=setTimeout(async()=>{this.shouldReconnect&&(await this.cleanupEndpointResources(e),(!t.process||t.process.killed)&&r.info(`[${e}] MCP \u8FDB\u7A0B\u672A\u8FD0\u884C\uFF0C\u5C06\u5728\u91CD\u8FDE\u65F6\u542F\u52A8...`),this.connectToEndpoint(e))},i)}startMCPProcessForEndpoint(e){let t=this.endpoints.get(e);if(!t){r.error(`\u7AEF\u70B9\u4E0D\u5B58\u5728: ${e}`);return}if(t.process){r.info(`[${e}] MCP \u8FDB\u7A0B\u5DF2\u5728\u8FD0\u884C`);return}r.info(`[${e}] \u6B63\u5728\u542F\u52A8 MCP \u8FDB\u7A0B`),t.process=X("node",[this.mcpScript],{stdio:["pipe","pipe","pipe"]}),t.process.stdout?.on("data",o=>{t.stdoutBuffer+=o.toString();let n=t.stdoutBuffer.split(`
4
- `);t.stdoutBuffer=n.pop()||"";for(let i of n)i.trim()&&this.handleMCPMessage(e,i)}),t.process.stderr?.on("data",o=>{if(l.env.XIAOZHI_DAEMON!=="true")try{l.stderr.write(o)}catch{}}),t.process.on("exit",(o,n)=>{r.warn(`[${e}] MCP \u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${o}, \u4FE1\u53F7: ${n}`),t.process=null,this.shouldReconnect&&n!=="SIGTERM"&&n!=="SIGKILL"&&r.info(`[${e}] MCP \u8FDB\u7A0B\u610F\u5916\u9000\u51FA\uFF0C\u5C06\u5728\u4E0B\u6B21\u91CD\u8FDE\u65F6\u5C1D\u8BD5\u91CD\u542F`)}),t.process.on("error",o=>{r.error(`[${e}] \u8FDB\u7A0B\u9519\u8BEF: ${o.message}`),t.process=null,this.shouldReconnect&&r.info(`[${e}] MCP \u8FDB\u7A0B\u53D1\u751F\u9519\u8BEF\uFF0C\u5C06\u5728\u4E0B\u6B21\u91CD\u8FDE\u65F6\u5C1D\u8BD5\u91CD\u542F`)})}handleMCPMessage(e,t){r.info(`>> [${e}] mcpServerProxy\u53D1\u9001\u6D88\u606F\u957F\u5EA6: ${t.length} \u5B57\u8282`),r.info(`>> [${e}] mcpServerProxy\u53D1\u9001\u6D88\u606F: ${t.substring(0,500)}...`),this.sendToEndpoint(e,t)}sendToEndpoint(e,t){let o=this.endpoints.get(e);if(!o||!o.websocket||o.websocket.readyState!==v.OPEN){r.warn(`[${e}] \u7AEF\u70B9\u4E0D\u53EF\u7528\uFF0C\u6D88\u606F\u65E0\u6CD5\u53D1\u9001`);return}try{o.websocket.send(`${t}
5
- `),r.info(`>> [${e}] \u6210\u529F\u53D1\u9001\u6D88\u606F\u5230 WebSocket`)}catch(n){r.error(`>> [${e}] \u53D1\u9001\u6D88\u606F\u5230 WebSocket \u5931\u8D25: ${n}`)}}startHeartbeat(e){let t=this.endpoints.get(e);t&&(this.stopHeartbeat(e),t.heartbeatTimer=setInterval(()=>{t.websocket&&t.websocket.readyState===v.OPEN&&(t.websocket.ping(),t.heartbeatTimeoutTimer=setTimeout(()=>{r.warn(`[${e}] \u5FC3\u8DF3\u8D85\u65F6\uFF0C\u65AD\u5F00\u8FDE\u63A5`),t.websocket?.close()},this.connectionConfig.heartbeatTimeout),this.reportStatusToWebUI())},this.connectionConfig.heartbeatInterval))}stopHeartbeat(e){let t=this.endpoints.get(e);t&&(t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=void 0),t.heartbeatTimeoutTimer&&(clearTimeout(t.heartbeatTimeoutTimer),t.heartbeatTimeoutTimer=void 0))}async cleanupEndpointResources(e){let t=this.endpoints.get(e);if(t){if(r.debug(`[${e}] \u6E05\u7406\u7AEF\u70B9\u8D44\u6E90...`),this.stopHeartbeat(e),t.reconnectTimer&&(clearTimeout(t.reconnectTimer),t.reconnectTimer=void 0),t.websocket){try{t.websocket.readyState===v.OPEN&&t.websocket.close()}catch(o){r.debug(`[${e}] \u5173\u95ED WebSocket \u65F6\u51FA\u9519: ${o}`)}t.websocket=null}if(t.process&&!t.process.killed){try{r.debug(`[${e}] \u7EC8\u6B62 MCP \u8FDB\u7A0B...`),t.process.kill("SIGTERM"),await new Promise(o=>{let n=setTimeout(()=>{t.process&&!t.process.killed&&(r.warn(`[${e}] MCP \u8FDB\u7A0B\u672A\u80FD\u6B63\u5E38\u9000\u51FA\uFF0C\u5F3A\u5236\u7EC8\u6B62`),t.process.kill("SIGKILL")),o()},2e3);t.process?.on("exit",()=>{clearTimeout(n),o()})})}catch(o){r.warn(`[${e}] \u7EC8\u6B62 MCP \u8FDB\u7A0B\u65F6\u51FA\u9519: ${o}`)}t.process=null}t.stdoutBuffer="",t.isConnected=!1,r.debug(`[${e}] \u7AEF\u70B9\u8D44\u6E90\u6E05\u7406\u5B8C\u6210`)}}cleanup(){for(let e of this.endpoints.keys())this.stopHeartbeat(e);for(let[e,t]of this.endpoints){if(t.reconnectTimer&&(clearTimeout(t.reconnectTimer),t.reconnectTimer=void 0),t.stdoutBuffer="",t.process){r.info(`[${e}] \u6B63\u5728\u7EC8\u6B62 MCP \u8FDB\u7A0B`);try{t.process.kill("SIGTERM"),setTimeout(()=>{t.process&&!t.process.killed&&t.process.kill("SIGKILL")},5e3)}catch(o){r.error(`[${e}] \u7EC8\u6B62\u8FDB\u7A0B\u65F6\u51FA\u9519: ${o instanceof Error?o.message:String(o)}`)}t.process=null}if(t.websocket){try{t.websocket.close()}catch(o){r.warn(`[${e}] \u5173\u95ED WebSocket \u65F6\u51FA\u9519: ${o}`)}t.websocket=null}}}shutdown(){r.info("\u6B63\u5728\u5173\u95ED Multi-Endpoint MCP Pipe..."),this.shouldReconnect=!1;for(let e of this.endpoints.values())e.isConnected=!1;this.reportStatusToWebUI(),this.cleanup(),this.shutdownResolve&&this.shutdownResolve(),R()||setTimeout(()=>{l.exit(0)},100)}async reportStatusToWebUI(){if(!R())try{let e=u.getWebUIPort(),t=new v(`ws://localhost:${e}`);t.on("open",()=>{let o=[];for(let[i,c]of this.endpoints)o.push({url:i,connected:c.isConnected});let n={type:"clientStatus",data:{status:this.hasAnyConnection()?"connected":"disconnected",mcpEndpoints:o,activeMCPServers:[],lastHeartbeat:Date.now()}};t.send(JSON.stringify(n)),r.debug("\u5DF2\u5411 Web UI \u62A5\u544A\u72B6\u6001"),setTimeout(()=>{t.close()},1e3)}),t.on("error",o=>{r.debug(`Web UI \u8FDE\u63A5\u5931\u8D25\uFF08\u53EF\u80FD\u672A\u8FD0\u884C\uFF09: ${o.message}`)})}catch(e){r.debug(`\u5411 Web UI \u62A5\u544A\u72B6\u6001\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}hasAnyConnection(){for(let e of this.endpoints.values())if(e.isConnected)return!0;return!1}};var Y=O(import.meta.url),P=p.dirname(Y),s=m.withTag("mcp-server"),b="mcpServerProxy.js",A=class extends K{static{f(this,"MCPServer")}app;server=null;clients=new Map;mcpProxy=null;mcpClient=null;mcpProxyPath=null;port;constructor(e=3e3){super(),this.port=e,this.app=x(),this.setupMiddleware(),this.setupRoutes()}setupMiddleware(){this.app.use(x.json()),this.app.use(x.urlencoded({extended:!0})),this.app.use((e,t,o)=>{t.header("Access-Control-Allow-Origin","*"),t.header("Access-Control-Allow-Methods","GET, POST, OPTIONS"),t.header("Access-Control-Allow-Headers","Content-Type, Accept"),t.header("Cache-Control","no-cache"),o()})}setupRoutes(){this.app.get("/sse",(e,t)=>{let o=Date.now().toString(),n=q();t.setHeader("Content-Type","text/event-stream"),t.setHeader("Cache-Control","no-cache, no-transform"),t.setHeader("Connection","keep-alive"),t.setHeader("X-Accel-Buffering","no"),this.clients.set(n,{id:o,sessionId:n,response:t}),s.info(`MCP\u5BA2\u6237\u7AEF\u5DF2\u8FDE\u63A5: ${o} (\u4F1A\u8BDD: ${n})`),t.write(`event: endpoint
1
+ var H=Object.defineProperty;var f=(i,e)=>H(i,"name",{value:e,configurable:!0});import{spawn as K}from"child_process";import{randomUUID as Y}from"crypto";import{EventEmitter as U}from"events";import M from"fs";import Q from"os";import p from"path";import{fileURLToPath as F}from"url";import j from"express";import{copyFileSync as N,existsSync as T,readFileSync as L,writeFileSync as J}from"fs";import{dirname as G,resolve as m}from"path";import{fileURLToPath as B}from"url";import*as v from"comment-json";import I from"json5";import*as A from"json5-writer";function D(i){if(!i||typeof i!="object")throw new Error("\u670D\u52A1\u914D\u7F6E\u5FC5\u987B\u662F\u4E00\u4E2A\u6709\u6548\u7684\u5BF9\u8C61");if("command"in i&&typeof i.command=="string")return"stdio";if("type"in i&&i.type==="sse")return"sse";if("type"in i&&i.type==="streamable-http"||"url"in i&&typeof i.url=="string")return"streamable-http";throw new Error("\u65E0\u6CD5\u8BC6\u522B\u7684 MCP \u670D\u52A1\u914D\u7F6E\u7C7B\u578B\u3002\u914D\u7F6E\u5FC5\u987B\u5305\u542B command \u5B57\u6BB5\uFF08stdio\uFF09\u3001type: 'sse' \u5B57\u6BB5\uFF08sse\uFF09\u6216 url \u5B57\u6BB5\uFF08streamable-http\uFF09")}f(D,"getMcpServerCommunicationType");function E(i,e){if(!e||typeof e!="object")return{valid:!1,error:`\u670D\u52A1 "${i}" \u7684\u914D\u7F6E\u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61`};try{switch(D(e)){case"stdio":if(!e.command||typeof e.command!="string")return{valid:!1,error:`\u670D\u52A1 "${i}" \u7F3A\u5C11\u5FC5\u9700\u7684 command \u5B57\u6BB5\u6216\u5B57\u6BB5\u7C7B\u578B\u4E0D\u6B63\u786E`};if(!Array.isArray(e.args))return{valid:!1,error:`\u670D\u52A1 "${i}" \u7684 args \u5B57\u6BB5\u5FC5\u987B\u662F\u6570\u7EC4`};if(e.env&&typeof e.env!="object")return{valid:!1,error:`\u670D\u52A1 "${i}" \u7684 env \u5B57\u6BB5\u5FC5\u987B\u662F\u5BF9\u8C61`};break;case"sse":if(e.type!=="sse")return{valid:!1,error:`\u670D\u52A1 "${i}" \u7684 type \u5B57\u6BB5\u5FC5\u987B\u662F "sse"`};if(!e.url||typeof e.url!="string")return{valid:!1,error:`\u670D\u52A1 "${i}" \u7F3A\u5C11\u5FC5\u9700\u7684 url \u5B57\u6BB5\u6216\u5B57\u6BB5\u7C7B\u578B\u4E0D\u6B63\u786E`};break;case"streamable-http":if(!e.url||typeof e.url!="string")return{valid:!1,error:`\u670D\u52A1 "${i}" \u7F3A\u5C11\u5FC5\u9700\u7684 url \u5B57\u6BB5\u6216\u5B57\u6BB5\u7C7B\u578B\u4E0D\u6B63\u786E`};if(e.type&&e.type!=="streamable-http")return{valid:!1,error:`\u670D\u52A1 "${i}" \u7684 type \u5B57\u6BB5\u5982\u679C\u5B58\u5728\uFF0C\u5FC5\u987B\u662F "streamable-http"`};break;default:return{valid:!1,error:`\u670D\u52A1 "${i}" \u7684\u914D\u7F6E\u7C7B\u578B\u65E0\u6CD5\u8BC6\u522B`}}return{valid:!0}}catch(t){return{valid:!1,error:`\u670D\u52A1 "${i}" \u7684\u914D\u7F6E\u65E0\u6548: ${t instanceof Error?t.message:"\u672A\u77E5\u9519\u8BEF"}`}}}f(E,"validateMcpServerConfig");var z=G(B(import.meta.url)),x={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},$=class i{static{f(this,"ConfigManager")}static instance;defaultConfigPath;config=null;currentConfigPath=null;json5Writer=null;constructor(){this.defaultConfigPath=m(z,"xiaozhi.config.default.json")}getConfigFilePath(){let e=process.env.XIAOZHI_CONFIG_DIR||process.cwd(),t=["xiaozhi.config.json5","xiaozhi.config.jsonc","xiaozhi.config.json"];for(let o of t){let n=m(e,o);if(T(n))return n}return m(e,"xiaozhi.config.json")}getConfigFileFormat(e){return e.endsWith(".json5")?"json5":e.endsWith(".jsonc")?"jsonc":"json"}static getInstance(){return i.instance||(i.instance=new i),i.instance}configExists(){let e=process.env.XIAOZHI_CONFIG_DIR||process.cwd(),t=["xiaozhi.config.json5","xiaozhi.config.jsonc","xiaozhi.config.json"];for(let o of t){let n=m(e,o);if(T(n))return!0}return!1}initConfig(e="json"){if(!T(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\u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u91CD\u590D\u521D\u59CB\u5316");let t=process.env.XIAOZHI_CONFIG_DIR||process.cwd(),o=`xiaozhi.config.${e}`,n=m(t,o);N(this.defaultConfigPath,n),this.config=null,this.json5Writer=null}loadConfig(){if(!this.configExists())throw new Error("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u8FD0\u884C xiaozhi init \u521D\u59CB\u5316\u914D\u7F6E");try{let e=this.getConfigFilePath();this.currentConfigPath=e;let t=this.getConfigFileFormat(e),o=L(e,"utf8"),n;switch(t){case"json5":n=I.parse(o),this.json5Writer=A.load(o);break;case"jsonc":n=v.parse(o);break;default:n=JSON.parse(o);break}return this.validateConfig(n),n}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===void 0||t.mcpEndpoint===null)throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5B57\u6BB5\u65E0\u6548");if(typeof t.mcpEndpoint!="string")if(Array.isArray(t.mcpEndpoint)){if(t.mcpEndpoint.length===0)throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u6570\u7EC4\u4E0D\u80FD\u4E3A\u7A7A");for(let o of t.mcpEndpoint)if(typeof o!="string"||o.trim()==="")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u6570\u7EC4\u4E2D\u7684\u6BCF\u4E2A\u5143\u7D20\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6216\u5B57\u7B26\u4E32\u6570\u7EC4");if(!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,n]of Object.entries(t.mcpServers)){if(!n||typeof n!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o} \u65E0\u6548`);let r=E(o,n);if(!r.valid)throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A${r.error}`)}}getConfig(){return this.config||(this.config=this.loadConfig()),JSON.parse(JSON.stringify(this.config))}getMutableConfig(){return this.config||(this.config=this.loadConfig()),this.config}getMcpEndpoint(){let e=this.getConfig();return Array.isArray(e.mcpEndpoint)?e.mcpEndpoint[0]||"":e.mcpEndpoint}getMcpEndpoints(){let e=this.getConfig();return Array.isArray(e.mcpEndpoint)?[...e.mcpEndpoint]:e.mcpEndpoint?[e.mcpEndpoint]:[]}getMcpServers(){return this.getConfig().mcpServers}getMcpServerConfig(){return this.getConfig().mcpServerConfig||{}}getServerToolsConfig(e){return this.getMcpServerConfig()[e]?.tools||{}}isToolEnabled(e,t){return this.getServerToolsConfig(e)[t]?.enable!==!1}updateMcpEndpoint(e){if(Array.isArray(e)){if(e.length===0)throw new Error("MCP \u7AEF\u70B9\u6570\u7EC4\u4E0D\u80FD\u4E3A\u7A7A");for(let o of e)if(!o||typeof o!="string")throw new Error("MCP \u7AEF\u70B9\u6570\u7EC4\u4E2D\u7684\u6BCF\u4E2A\u5143\u7D20\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let t=this.getMutableConfig();t.mcpEndpoint=e,this.saveConfig(t)}addMcpEndpoint(e){if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let t=this.getMutableConfig(),o=this.getMcpEndpoints();if(o.includes(e))throw new Error(`MCP \u7AEF\u70B9 ${e} \u5DF2\u5B58\u5728`);let n=[...o,e];t.mcpEndpoint=n,this.saveConfig(t)}removeMcpEndpoint(e){if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let t=this.getMutableConfig(),o=this.getMcpEndpoints();if(o.indexOf(e)===-1)throw new Error(`MCP \u7AEF\u70B9 ${e} \u4E0D\u5B58\u5728`);if(o.length===1)throw new Error("\u4E0D\u80FD\u5220\u9664\u6700\u540E\u4E00\u4E2A MCP \u7AEF\u70B9");let r=o.filter(a=>a!==e);t.mcpEndpoint=r,this.saveConfig(t)}updateMcpServer(e,t){if(!e||typeof e!="string")throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let o=E(e,t);if(!o.valid)throw new Error(o.error||"\u670D\u52A1\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");let n=this.getMutableConfig();n.mcpServers[e]=t,this.saveConfig(n)}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 n={...t,mcpServers:o};this.saveConfig(n)}updateServerToolsConfig(e,t){let o=this.getMutableConfig();o.mcpServerConfig||(o.mcpServerConfig={}),Object.keys(t).length===0?delete o.mcpServerConfig[e]:o.mcpServerConfig[e]={tools:t},this.saveConfig(o)}removeServerToolsConfig(e){let o={...this.getConfig()};o.mcpServerConfig&&(delete o.mcpServerConfig[e],this.saveConfig(o))}setToolEnabled(e,t,o,n){let r=this.getMutableConfig();r.mcpServerConfig||(r.mcpServerConfig={}),r.mcpServerConfig[e]||(r.mcpServerConfig[e]={tools:{}}),r.mcpServerConfig[e].tools[t]={...r.mcpServerConfig[e].tools[t],enable:o,...n&&{description:n}},this.saveConfig(r)}saveConfig(e){try{this.validateConfig(e);let t;this.currentConfigPath?t=this.currentConfigPath:(t=this.getConfigFilePath(),this.currentConfigPath=t);let o=this.getConfigFileFormat(t),n;switch(o){case"json5":try{this.json5Writer?(this.json5Writer.write(e),n=this.json5Writer.toSource()):(console.warn("\u6CA1\u6709 json5Writer \u5B9E\u4F8B\uFF0C\u56DE\u9000\u5230\u6807\u51C6 JSON5 \u683C\u5F0F"),n=I.stringify(e,null,2))}catch(r){console.warn("\u4F7F\u7528 json5-writer \u4FDD\u5B58\u5931\u8D25\uFF0C\u56DE\u9000\u5230\u6807\u51C6 JSON5 \u683C\u5F0F:",r),n=I.stringify(e,null,2)}break;case"jsonc":try{n=v.stringify(e,null,2)}catch(r){console.warn("\u4F7F\u7528 comment-json \u4FDD\u5B58\u5931\u8D25\uFF0C\u56DE\u9000\u5230\u6807\u51C6 JSON \u683C\u5F0F:",r),n=JSON.stringify(e,null,2)}break;default:n=JSON.stringify(e,null,2);break}J(t,n,"utf8"),this.config=e,logger.info("saveConfig",this.config),this.notifyConfigUpdate(e)}catch(t){throw new Error(`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`)}}reloadConfig(){this.config=null,this.currentConfigPath=null,this.json5Writer=null}getConfigPath(){return this.getConfigFilePath()}getDefaultConfigPath(){return this.defaultConfigPath}getConnectionConfig(){let t=this.getConfig().connection||{};return{heartbeatInterval:t.heartbeatInterval??x.heartbeatInterval,heartbeatTimeout:t.heartbeatTimeout??x.heartbeatTimeout,reconnectInterval:t.reconnectInterval??x.reconnectInterval}}getHeartbeatInterval(){return this.getConnectionConfig().heartbeatInterval}getHeartbeatTimeout(){return this.getConnectionConfig().heartbeatTimeout}getReconnectInterval(){return this.getConnectionConfig().reconnectInterval}updateConnectionConfig(e){let t=this.getMutableConfig();t.connection||(t.connection={}),Object.assign(t.connection,e),this.saveConfig(t)}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.getMutableConfig();t.modelscope||(t.modelscope={}),Object.assign(t.modelscope,e),this.saveConfig(t)}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}notifyConfigUpdate(e){try{let t=global.__webServer;t&&typeof t.broadcastConfigUpdate=="function"&&(t.broadcastConfigUpdate(e),console.log("\u5DF2\u901A\u8FC7 WebSocket \u5E7F\u64AD\u914D\u7F6E\u66F4\u65B0"))}catch(t){console.warn("\u901A\u77E5 Web \u754C\u9762\u914D\u7F6E\u66F4\u65B0\u5931\u8D25:",t instanceof Error?t.message:String(t))}}updateWebUIConfig(e){let t=this.getMutableConfig();t.webUI||(t.webUI={}),Object.assign(t.webUI,e),this.saveConfig(t)}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})}},u=$.getInstance();import y from"fs";import X from"path";import C from"chalk";import{createConsola as Z}from"consola";function q(i){let e=i.getFullYear(),t=String(i.getMonth()+1).padStart(2,"0"),o=String(i.getDate()).padStart(2,"0"),n=String(i.getHours()).padStart(2,"0"),r=String(i.getMinutes()).padStart(2,"0"),a=String(i.getSeconds()).padStart(2,"0");return`${e}-${t}-${o} ${n}:${r}:${a}`}f(q,"formatDateTime");var R=class{static{f(this,"Logger")}logFilePath=null;writeStream=null;consolaInstance;isDaemonMode;constructor(){this.isDaemonMode=process.env.XIAOZHI_DAEMON==="true",this.consolaInstance=Z({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"},n={info:C.blue,success:C.green,warn:C.yellow,error:C.red,debug:C.gray,log:f(h=>h,"log")},r=o[t.type]||t.type.toUpperCase(),a=n[t.type]||(h=>h),l=q(new Date),W=a(`[${r}]`),_=`[${l}] ${W} ${t.args.join(" ")}`;if(!e)try{console.error(_)}catch(h){if(h instanceof Error&&h.message?.includes("EPIPE"))return;throw h}},"log")}])}initLogFile(e){this.logFilePath=X.join(e,"xiaozhi.log"),y.existsSync(this.logFilePath)||y.writeFileSync(this.logFilePath,""),this.writeStream=y.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"})}logToFile(e,t,...o){if(this.writeStream){let r=`[${new Date().toISOString()}] [${e.toUpperCase()}] ${t}`,a=o.length>0?`${r} ${o.map(l=>typeof l=="object"?JSON.stringify(l):String(l)).join(" ")}`:r;this.writeStream.write(`${a}
2
+ `)}}enableFileLogging(e){e&&!this.writeStream&&this.logFilePath?this.writeStream=y.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)}},d=new R;import{spawn as V}from"child_process";import g from"process";import b from"ws";var O=f(()=>g.env.NODE_ENV==="test"||g.env.VITEST==="true","isTestEnvironment"),s=d.withTag("MULTI_MCP_PIPE");g.env.XIAOZHI_DAEMON==="true"&&g.env.XIAOZHI_CONFIG_DIR&&(d.initLogFile(g.env.XIAOZHI_CONFIG_DIR),d.enableFileLogging(!0));var w=class{static{f(this,"MultiEndpointMCPPipe")}mcpScript;endpoints;shouldReconnect;shutdownResolve;connectionConfig;constructor(e,t){this.mcpScript=e,this.endpoints=new Map,this.shouldReconnect=!0,s.info(t.length===1?`\u521D\u59CB\u5316\u5355\u7AEF\u70B9\u8FDE\u63A5: ${t[0]}`:`\u521D\u59CB\u5316\u591A\u7AEF\u70B9\u8FDE\u63A5\uFF08${t.length} \u4E2A\u7AEF\u70B9\uFF09`);for(let o of t)this.endpoints.set(o,{url:o,websocket:null,isConnected:!1,reconnectAttempt:0,maxReconnectAttempts:5,process:null,stdoutBuffer:""});try{this.connectionConfig=u.getConnectionConfig(),s.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},s.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 await this.connectToAllEndpoints(),this.reportStatusToWebUI(),new Promise(e=>{this.shutdownResolve=e})}async connectToAllEndpoints(){let e=[];for(let[t,o]of this.endpoints)e.push(this.connectToEndpoint(t));await Promise.allSettled(e)}async connectToEndpoint(e){let t=this.endpoints.get(e);if(!t||t.isConnected)return;this.startMCPProcessForEndpoint(e),s.info(`\u6B63\u5728\u8FDE\u63A5\u5230 WebSocket \u670D\u52A1\u5668: ${e}`);let o=new b(e);t.websocket=o,o.on("open",()=>{s.info(`\u6210\u529F\u8FDE\u63A5\u5230 WebSocket \u670D\u52A1\u5668: ${e}`),t.isConnected=!0,t.reconnectAttempt=0,t.reconnectTimer&&(clearTimeout(t.reconnectTimer),t.reconnectTimer=void 0),this.reportStatusToWebUI(),this.startHeartbeat(e)}),o.on("message",n=>{let r=n.toString();s.info(`<< [${e}] WebSocket\u6536\u5230\u6D88\u606F: ${r}`);try{let a=JSON.parse(r);(a.method==="notifications/initialized"||a.method==="tools/list"&&a.id||a?.result?.tools)&&setTimeout(()=>{this.reportStatusToWebUI()},1e3)}catch{}t.process?.stdin&&!t.process.stdin.destroyed&&t.process.stdin.write(`${r}
3
+ `)}),o.on("close",(n,r)=>{s.error(`[${e}] WebSocket \u8FDE\u63A5\u5DF2\u5173\u95ED: ${n} ${r}`),t.isConnected=!1,t.websocket=null,this.stopHeartbeat(e),this.reportStatusToWebUI(),this.shouldReconnect&&(n===4004?t.reconnectAttempt<t.maxReconnectAttempts?(s.warn(`[${e}] \u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF(4004)\uFF0C\u5C06\u8FDB\u884C\u7B2C ${t.reconnectAttempt+1} \u6B21\u91CD\u8FDE\u5C1D\u8BD5\uFF08\u6700\u591A ${t.maxReconnectAttempts} \u6B21\uFF09`),this.scheduleReconnect(e)):s.error(`[${e}] \u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF(4004)\uFF0C\u5DF2\u8FBE\u5230\u6700\u5927\u91CD\u8FDE\u6B21\u6570(${t.maxReconnectAttempts})\uFF0C\u505C\u6B62\u91CD\u8FDE`):this.scheduleReconnect(e))}),o.on("error",n=>{s.error(`[${e}] WebSocket \u9519\u8BEF: ${n.message}`),t.isConnected=!1,this.stopHeartbeat(e)}),o.on("pong",()=>{t.heartbeatTimeoutTimer&&(clearTimeout(t.heartbeatTimeoutTimer),t.heartbeatTimeoutTimer=void 0)})}scheduleReconnect(e){let t=this.endpoints.get(e);if(!t||!this.shouldReconnect)return;t.reconnectTimer&&clearTimeout(t.reconnectTimer),t.reconnectAttempt++;let o=this.connectionConfig.reconnectInterval,r=Math.min(o*2**(t.reconnectAttempt-1),6e4);s.info(`[${e}] \u8BA1\u5212\u5728 ${(r/1e3).toFixed(2)} \u79D2\u540E\u8FDB\u884C\u7B2C ${t.reconnectAttempt} \u6B21\u91CD\u8FDE\u5C1D\u8BD5...`),t.reconnectTimer=setTimeout(async()=>{this.shouldReconnect&&(await this.cleanupEndpointResources(e),(!t.process||t.process.killed)&&s.info(`[${e}] MCP \u8FDB\u7A0B\u672A\u8FD0\u884C\uFF0C\u5C06\u5728\u91CD\u8FDE\u65F6\u542F\u52A8...`),this.connectToEndpoint(e))},r)}startMCPProcessForEndpoint(e){let t=this.endpoints.get(e);if(!t){s.error(`\u7AEF\u70B9\u4E0D\u5B58\u5728: ${e}`);return}if(t.process){s.info(`[${e}] MCP \u8FDB\u7A0B\u5DF2\u5728\u8FD0\u884C`);return}s.info(`[${e}] \u6B63\u5728\u542F\u52A8 MCP \u8FDB\u7A0B`),t.process=V("node",[this.mcpScript],{stdio:["pipe","pipe","pipe"]}),t.process.stdout?.on("data",o=>{t.stdoutBuffer+=o.toString();let n=t.stdoutBuffer.split(`
4
+ `);t.stdoutBuffer=n.pop()||"";for(let r of n)r.trim()&&this.handleMCPMessage(e,r)}),t.process.stderr?.on("data",o=>{if(g.env.XIAOZHI_DAEMON!=="true")try{g.stderr.write(o)}catch{}}),t.process.on("exit",(o,n)=>{s.warn(`[${e}] MCP \u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${o}, \u4FE1\u53F7: ${n}`),t.process=null,this.shouldReconnect&&n!=="SIGTERM"&&n!=="SIGKILL"&&s.info(`[${e}] MCP \u8FDB\u7A0B\u610F\u5916\u9000\u51FA\uFF0C\u5C06\u5728\u4E0B\u6B21\u91CD\u8FDE\u65F6\u5C1D\u8BD5\u91CD\u542F`)}),t.process.on("error",o=>{s.error(`[${e}] \u8FDB\u7A0B\u9519\u8BEF: ${o.message}`),t.process=null,this.shouldReconnect&&s.info(`[${e}] MCP \u8FDB\u7A0B\u53D1\u751F\u9519\u8BEF\uFF0C\u5C06\u5728\u4E0B\u6B21\u91CD\u8FDE\u65F6\u5C1D\u8BD5\u91CD\u542F`)})}handleMCPMessage(e,t){s.info(`>> [${e}] mcpServerProxy\u53D1\u9001\u6D88\u606F\u957F\u5EA6: ${t.length} \u5B57\u8282`),s.info(`>> [${e}] mcpServerProxy\u53D1\u9001\u6D88\u606F: ${t.substring(0,500)}...`),this.sendToEndpoint(e,t)}sendToEndpoint(e,t){let o=this.endpoints.get(e);if(!o||!o.websocket||o.websocket.readyState!==b.OPEN){s.warn(`[${e}] \u7AEF\u70B9\u4E0D\u53EF\u7528\uFF0C\u6D88\u606F\u65E0\u6CD5\u53D1\u9001`);return}try{o.websocket.send(`${t}
5
+ `),s.info(`>> [${e}] \u6210\u529F\u53D1\u9001\u6D88\u606F\u5230 WebSocket`)}catch(n){s.error(`>> [${e}] \u53D1\u9001\u6D88\u606F\u5230 WebSocket \u5931\u8D25: ${n}`)}}startHeartbeat(e){let t=this.endpoints.get(e);t&&(this.stopHeartbeat(e),t.heartbeatTimer=setInterval(()=>{t.websocket&&t.websocket.readyState===b.OPEN&&(t.websocket.ping(),t.heartbeatTimeoutTimer=setTimeout(()=>{s.warn(`[${e}] \u5FC3\u8DF3\u8D85\u65F6\uFF0C\u65AD\u5F00\u8FDE\u63A5`),t.websocket?.close()},this.connectionConfig.heartbeatTimeout),this.reportStatusToWebUI())},this.connectionConfig.heartbeatInterval))}stopHeartbeat(e){let t=this.endpoints.get(e);t&&(t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=void 0),t.heartbeatTimeoutTimer&&(clearTimeout(t.heartbeatTimeoutTimer),t.heartbeatTimeoutTimer=void 0))}async cleanupEndpointResources(e){let t=this.endpoints.get(e);if(t){if(s.debug(`[${e}] \u6E05\u7406\u7AEF\u70B9\u8D44\u6E90...`),this.stopHeartbeat(e),t.reconnectTimer&&(clearTimeout(t.reconnectTimer),t.reconnectTimer=void 0),t.websocket){try{t.websocket.readyState===b.OPEN&&t.websocket.close()}catch(o){s.debug(`[${e}] \u5173\u95ED WebSocket \u65F6\u51FA\u9519: ${o}`)}t.websocket=null}if(t.process&&!t.process.killed){try{s.debug(`[${e}] \u7EC8\u6B62 MCP \u8FDB\u7A0B...`),t.process.kill("SIGTERM"),await new Promise(o=>{let n=setTimeout(()=>{t.process&&!t.process.killed&&(s.warn(`[${e}] MCP \u8FDB\u7A0B\u672A\u80FD\u6B63\u5E38\u9000\u51FA\uFF0C\u5F3A\u5236\u7EC8\u6B62`),t.process.kill("SIGKILL")),o()},2e3);t.process?.on("exit",()=>{clearTimeout(n),o()})})}catch(o){s.warn(`[${e}] \u7EC8\u6B62 MCP \u8FDB\u7A0B\u65F6\u51FA\u9519: ${o}`)}t.process=null}t.stdoutBuffer="",t.isConnected=!1,s.debug(`[${e}] \u7AEF\u70B9\u8D44\u6E90\u6E05\u7406\u5B8C\u6210`)}}cleanup(){for(let e of this.endpoints.keys())this.stopHeartbeat(e);for(let[e,t]of this.endpoints){if(t.reconnectTimer&&(clearTimeout(t.reconnectTimer),t.reconnectTimer=void 0),t.stdoutBuffer="",t.process){s.info(`[${e}] \u6B63\u5728\u7EC8\u6B62 MCP \u8FDB\u7A0B`);try{t.process.kill("SIGTERM"),setTimeout(()=>{t.process&&!t.process.killed&&t.process.kill("SIGKILL")},5e3)}catch(o){s.error(`[${e}] \u7EC8\u6B62\u8FDB\u7A0B\u65F6\u51FA\u9519: ${o instanceof Error?o.message:String(o)}`)}t.process=null}if(t.websocket){try{t.websocket.close()}catch(o){s.warn(`[${e}] \u5173\u95ED WebSocket \u65F6\u51FA\u9519: ${o}`)}t.websocket=null}}}shutdown(){s.info("\u6B63\u5728\u5173\u95ED Multi-Endpoint MCP Pipe..."),this.shouldReconnect=!1;for(let e of this.endpoints.values())e.isConnected=!1;this.reportStatusToWebUI(),this.cleanup(),this.shutdownResolve&&this.shutdownResolve(),O()||setTimeout(()=>{g.exit(0)},100)}async reportStatusToWebUI(){if(!O())try{let e=u.getWebUIPort(),t=new b(`ws://localhost:${e}`);t.on("open",()=>{let o=[];for(let[r,a]of this.endpoints)o.push({url:r,connected:a.isConnected});let n={type:"clientStatus",data:{status:this.hasAnyConnection()?"connected":"disconnected",mcpEndpoints:o,activeMCPServers:[],lastHeartbeat:Date.now()}};t.send(JSON.stringify(n)),s.debug("\u5DF2\u5411 Web UI \u62A5\u544A\u72B6\u6001"),setTimeout(()=>{t.close()},1e3)}),t.on("error",o=>{s.debug(`Web UI \u8FDE\u63A5\u5931\u8D25\uFF08\u53EF\u80FD\u672A\u8FD0\u884C\uFF09: ${o.message}`)})}catch(e){s.debug(`\u5411 Web UI \u62A5\u544A\u72B6\u6001\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}hasAnyConnection(){for(let e of this.endpoints.values())if(e.isConnected)return!0;return!1}};var ee=F(import.meta.url),S=p.dirname(ee),c=d.withTag("mcp-server"),P="mcpServerProxy.js",te=5,k=class extends U{static{f(this,"MCPServer")}app;server=null;clients=new Map;mcpProxy=null;mcpClient=null;mcpProxyPath=null;port;constructor(e=3e3){super(),this.port=e,this.app=j(),this.setupMiddleware(),this.setupRoutes()}setupMiddleware(){this.app.use(j.json()),this.app.use(j.urlencoded({extended:!0})),this.app.use((e,t,o)=>{t.header("Access-Control-Allow-Origin","*"),t.header("Access-Control-Allow-Methods","GET, POST, OPTIONS"),t.header("Access-Control-Allow-Headers","Content-Type, Accept"),t.header("Cache-Control","no-cache"),o()})}setupRoutes(){this.app.get("/sse",(e,t)=>{let o=Date.now().toString(),n=Y();t.setHeader("Content-Type","text/event-stream"),t.setHeader("Cache-Control","no-cache, no-transform"),t.setHeader("Connection","keep-alive"),t.setHeader("X-Accel-Buffering","no"),this.clients.set(n,{id:o,sessionId:n,response:t}),c.info(`MCP\u5BA2\u6237\u7AEF\u5DF2\u8FDE\u63A5: ${o} (\u4F1A\u8BDD: ${n})`),t.write(`event: endpoint
6
6
  data: /messages?sessionId=${n}
7
7
 
8
- `),e.on("close",()=>{this.clients.delete(n),s.info(`MCP\u5BA2\u6237\u7AEF\u5DF2\u65AD\u5F00\u8FDE\u63A5: ${o} (\u4F1A\u8BDD: ${n})`)})}),this.app.post("/messages",async(e,t)=>{try{let o=e.query.sessionId,n=e.body;if(s.info(`\u901A\u8FC7SSE\u4F20\u8F93\u6536\u5230\u6D88\u606F (\u4F1A\u8BDD: ${o}):`,JSON.stringify(n)),!o||!this.clients.has(o)){t.status(400).json({jsonrpc:"2.0",error:{code:-32600,message:"\u65E0\u6548\u6216\u7F3A\u5C11sessionId"},id:n.id});return}if(!this.mcpProxy){t.status(503).json({jsonrpc:"2.0",error:{code:-32603,message:"MCP\u4EE3\u7406\u672A\u8FD0\u884C"},id:n.id});return}if(n.id===void 0)s.info(`\u8F6C\u53D1\u901A\u77E5: ${n.method}`),this.mcpProxy.stdin.write(`${JSON.stringify(n)}
9
- `),t.status(202).send();else{let i=await this.forwardToProxy(n),c=this.clients.get(o);c&&this.sendToClient(c,i),t.status(202).send()}}catch(o){s.error("SSE\u6D88\u606F\u9519\u8BEF:",o),t.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:o instanceof Error?o.message:"\u5185\u90E8\u9519\u8BEF"},id:e.body.id})}}),this.app.post("/rpc",async(e,t)=>{try{let o=e.body;if(s.debug("\u6536\u5230RPC\u6D88\u606F:",o),!this.mcpProxy){t.status(503).json({jsonrpc:"2.0",error:{code:-32603,message:"MCP\u4EE3\u7406\u672A\u8FD0\u884C"},id:o.id});return}let n=await this.forwardToProxy(o);t.json(n)}catch(o){s.error("RPC\u9519\u8BEF:",o),t.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:o instanceof Error?o.message:"\u5185\u90E8\u9519\u8BEF"},id:e.body.id})}}),this.app.get("/health",(e,t)=>{t.json({status:"ok",mode:"mcp-server",proxy:this.mcpProxy?"running":"stopped",clients:this.clients.size})})}responseBuffer="";pendingRequests=new Map;async forwardToProxy(e){return new Promise((t,o)=>{if(!this.mcpProxy||!this.mcpProxy.stdin||!this.mcpProxy.stdout){o(new Error("MCP\u4EE3\u7406\u4E0D\u53EF\u7528"));return}let n=setTimeout(()=>{this.pendingRequests.delete(e.id),s.warn(`\u6D88\u606F\u8D85\u65F6 id: ${e.id}, \u65B9\u6CD5: ${e.method} - \u5982\u679C\u54CD\u5E94\u5DF2\u901A\u8FC7SSE\u53D1\u9001\uFF0C\u8FD9\u662F\u6B63\u5E38\u7684`),t({jsonrpc:"2.0",id:e.id,result:{_timeout:!0,message:"\u54CD\u5E94\u53EF\u80FD\u5DF2\u901A\u8FC7SSE\u53D1\u9001"}})},3e4);this.pendingRequests.set(e.id,{resolve:t,reject:o,timeoutId:n}),s.info(`\u8F6C\u53D1\u6D88\u606F\u5230\u4EE3\u7406: ${JSON.stringify(e)}`),this.mcpProxy.stdin.write(`${JSON.stringify(e)}
8
+ `),e.on("close",()=>{this.clients.delete(n),c.info(`MCP\u5BA2\u6237\u7AEF\u5DF2\u65AD\u5F00\u8FDE\u63A5: ${o} (\u4F1A\u8BDD: ${n})`)})}),this.app.post("/messages",async(e,t)=>{try{let o=e.query.sessionId,n=e.body;if(c.info(`\u901A\u8FC7SSE\u4F20\u8F93\u6536\u5230\u6D88\u606F (\u4F1A\u8BDD: ${o}):`,JSON.stringify(n)),!o||!this.clients.has(o)){t.status(400).json({jsonrpc:"2.0",error:{code:-32600,message:"\u65E0\u6548\u6216\u7F3A\u5C11sessionId"},id:n.id});return}if(!this.mcpProxy){t.status(503).json({jsonrpc:"2.0",error:{code:-32603,message:"MCP\u4EE3\u7406\u672A\u8FD0\u884C"},id:n.id});return}if(n.id===void 0)c.info(`\u8F6C\u53D1\u901A\u77E5: ${n.method}`),this.mcpProxy.stdin.write(`${JSON.stringify(n)}
9
+ `),t.status(202).send();else{let r=await this.forwardToProxy(n),a=this.clients.get(o);a&&this.sendToClient(a,r),t.status(202).send()}}catch(o){c.error("SSE\u6D88\u606F\u9519\u8BEF:",o),t.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:o instanceof Error?o.message:"\u5185\u90E8\u9519\u8BEF"},id:e.body.id})}}),this.app.post("/rpc",async(e,t)=>{try{let o=e.body;if(c.debug("\u6536\u5230RPC\u6D88\u606F:",o),!this.mcpProxy){t.status(503).json({jsonrpc:"2.0",error:{code:-32603,message:"MCP\u4EE3\u7406\u672A\u8FD0\u884C"},id:o.id});return}let n=await this.forwardToProxy(o);t.json(n)}catch(o){c.error("RPC\u9519\u8BEF:",o),t.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:o instanceof Error?o.message:"\u5185\u90E8\u9519\u8BEF"},id:e.body.id})}}),this.app.get("/health",(e,t)=>{t.json({status:"ok",mode:"mcp-server",proxy:this.mcpProxy?"running":"stopped",clients:this.clients.size})})}responseBuffer="";pendingRequests=new Map;async forwardToProxy(e){return new Promise((t,o)=>{if(!this.mcpProxy||!this.mcpProxy.stdin||!this.mcpProxy.stdout){o(new Error("MCP\u4EE3\u7406\u4E0D\u53EF\u7528"));return}let n=setTimeout(()=>{this.pendingRequests.delete(e.id),c.warn(`\u6D88\u606F\u8D85\u65F6 id: ${e.id}, \u65B9\u6CD5: ${e.method} - \u5982\u679C\u54CD\u5E94\u5DF2\u901A\u8FC7SSE\u53D1\u9001\uFF0C\u8FD9\u662F\u6B63\u5E38\u7684`),t({jsonrpc:"2.0",id:e.id,result:{_timeout:!0,message:"\u54CD\u5E94\u53EF\u80FD\u5DF2\u901A\u8FC7SSE\u53D1\u9001"}})},3e4);this.pendingRequests.set(e.id,{resolve:t,reject:o,timeoutId:n}),c.info(`\u8F6C\u53D1\u6D88\u606F\u5230\u4EE3\u7406: ${JSON.stringify(e)}`),this.mcpProxy.stdin.write(`${JSON.stringify(e)}
10
10
  `)})}handleProxyResponse(e){try{this.responseBuffer+=e.toString();let t=this.responseBuffer.split(`
11
- `);this.responseBuffer=t.pop()||"";for(let o of t)if(o.trim())try{let n=JSON.parse(o);if(s.debug(`\u6536\u5230\u4EE3\u7406\u54CD\u5E94: ${o.substring(0,200)}...`),n.id!==void 0&&this.pendingRequests.has(n.id)){let i=this.pendingRequests.get(n.id);clearTimeout(i.timeoutId),this.pendingRequests.delete(n.id),i.resolve(n)}}catch{s.debug(`\u6765\u81EA\u4EE3\u7406\u7684\u975EJSON\u884C: ${o}`)}}catch(t){s.error("\u5904\u7406\u4EE3\u7406\u54CD\u5E94\u65F6\u51FA\u9519:",t)}}sendToClient(e,t){try{let o=`event: message
11
+ `);this.responseBuffer=t.pop()||"";for(let o of t)if(o.trim())try{let n=JSON.parse(o);if(c.debug(`\u6536\u5230\u4EE3\u7406\u54CD\u5E94: ${o.substring(0,200)}...`),n.id!==void 0&&this.pendingRequests.has(n.id)){let r=this.pendingRequests.get(n.id);clearTimeout(r.timeoutId),this.pendingRequests.delete(n.id),r.resolve(n)}}catch{c.debug(`\u6765\u81EA\u4EE3\u7406\u7684\u975EJSON\u884C: ${o}`)}}catch(t){c.error("\u5904\u7406\u4EE3\u7406\u54CD\u5E94\u65F6\u51FA\u9519:",t)}}sendToClient(e,t){try{let o=`event: message
12
12
  data: ${JSON.stringify(t)}
13
13
 
14
- `;e.response.write(o)}catch(o){s.error(`\u53D1\u9001\u5230\u5BA2\u6237\u7AEF ${e.id} \u5931\u8D25:`,o),this.clients.delete(e.sessionId)}}broadcastToClients(e){for(let t of this.clients.values())this.sendToClient(t,e)}async start(){try{let e=await Promise.allSettled([this.startMCPProxy(),new Promise(n=>{this.server=this.app.listen(this.port,()=>{s.info(`MCP\u670D\u52A1\u5668\u76D1\u542C\u7AEF\u53E3 ${this.port}`),s.info(`SSE\u7AEF\u70B9: http://localhost:${this.port}/sse`),s.info(`\u6D88\u606F\u7AEF\u70B9: http://localhost:${this.port}/messages`),s.info(`RPC\u7AEF\u70B9: http://localhost:${this.port}/rpc`),n()})})]),[t,o]=e;if(t.status==="rejected"&&s.error("MCP\u4EE3\u7406\u542F\u52A8\u5931\u8D25:",t.reason),o.status==="rejected")throw s.error("HTTP\u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25:",o.reason),o.reason;this.startMCPClient().catch(n=>{s.error("\u542F\u52A8\u8FDE\u63A5\u5230xiaozhi.me\u7684MCP\u5BA2\u6237\u7AEF\u5931\u8D25:",n)}),this.emit("started")}catch(e){throw s.error("\u542F\u52A8MCP\u670D\u52A1\u5668\u5931\u8D25:",e),e}}findMCPProxyPath(){if(this.mcpProxyPath)return this.mcpProxyPath;if(process.env.MCP_SERVER_PROXY_PATH){let n=p.normalize(process.env.MCP_SERVER_PROXY_PATH),i=p.resolve(n),c=[P,p.join(P,".."),p.join(P,"..",".."),p.join(P,"..","..","dist"),V.tmpdir()];if(y.existsSync(i)&&p.basename(i)===b&&c.some(g=>i.startsWith(g)))return this.mcpProxyPath=i,this.mcpProxyPath;throw s.warn(`MCP_SERVER_PROXY_PATH \u8DEF\u5F84\u4E0D\u5B89\u5168: ${n}`),new Error(`\u6307\u5B9A\u7684 MCP \u4EE3\u7406\u8DEF\u5F84\u4E0D\u5B58\u5728\u6216\u4E0D\u5B89\u5168: ${process.env.MCP_SERVER_PROXY_PATH}`)}let e=O(import.meta.url),t=p.dirname(e),o=null;for(let n=0;n<5;n++){let i=p.join(t,b);if(y.existsSync(i)){o=i;break}let c=p.join(t,"dist",b);if(y.existsSync(c)){o=c;break}t=p.dirname(t)}if(!o){let n=p.resolve(P,"..",".."),i=p.join(n,"dist",b);y.existsSync(i)&&(o=i)}if(!o)throw new Error(`\u5728\u9879\u76EE\u7ED3\u6784\u4E2D\u627E\u4E0D\u5230 ${b}`);return this.mcpProxyPath=o,this.mcpProxyPath}async startMCPProxy(){let e=this.findMCPProxyPath();s.info(`\u6B63\u5728\u542F\u52A8MCP\u4EE3\u7406: ${e}`),this.mcpProxy=Z("node",[e],{stdio:["pipe","pipe","pipe"],env:{...process.env,MCP_SERVER_MODE:"true",XIAOZHI_CONFIG_DIR:process.env.XIAOZHI_CONFIG_DIR||process.cwd()}}),this.mcpProxy.on("error",t=>{s.error("MCP\u4EE3\u7406\u9519\u8BEF:",t)}),this.mcpProxy.on("exit",(t,o)=>{s.warn(`MCP\u4EE3\u7406\u9000\u51FA\uFF0C\u4EE3\u7801 ${t}\uFF0C\u4FE1\u53F7 ${o}`),this.mcpProxy=null}),this.mcpProxy.stderr&&this.mcpProxy.stderr.on("data",t=>{let o=t.toString().trim();o.includes("[ERROR]")||o.includes("Error:")||o.includes("Failed")?s.error("MCP\u4EE3\u7406stderr:",o):s.info("MCP\u4EE3\u7406\u8F93\u51FA:",o)}),this.mcpProxy.stdout&&this.mcpProxy.stdout.on("data",t=>{this.handleProxyResponse(t)}),await new Promise((t,o)=>{let n=setTimeout(()=>{o(new Error("MCP\u4EE3\u7406\u542F\u52A8\u8D85\u65F6"))},1e4),i=f(c=>{let g=c.toString();(g.includes("MCP proxy ready")||g.includes("started"))&&(clearTimeout(n),this.mcpProxy?.stdout?.removeListener("data",i),t())},"dataHandler");this.mcpProxy?.stdout?.on("data",i)}),s.info("MCP\u4EE3\u7406\u542F\u52A8\u6210\u529F")}async startMCPClient(){let e=[];try{u.configExists()&&(e=u.getMcpEndpoints())}catch(t){s.warn("\u4ECE\u914D\u7F6E\u4E2D\u8BFB\u53D6MCP\u7AEF\u70B9\u5931\u8D25:",t)}if(e.length>0){let t=this.findMCPProxyPath();this.mcpClient=new S(t,e),await this.mcpClient.start(),s.info("MCP\u5BA2\u6237\u7AEF\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5\u5230 xiaozhi.me")}else s.info("\u672A\u914D\u7F6EMCP\u7AEF\u70B9\uFF0C\u8DF3\u8FC7\u5BA2\u6237\u7AEF\u8FDE\u63A5")}async stop(){s.info("\u6B63\u5728\u505C\u6B62MCP\u670D\u52A1\u5668...");for(let e of this.clients.values())try{e.response.end()}catch{}this.clients.clear(),this.server&&(await new Promise(e=>{this.server.close(()=>e())}),this.server=null),this.mcpProxy&&(this.mcpProxy.kill("SIGTERM"),await new Promise(e=>{this.mcpProxy.on("exit",()=>e()),setTimeout(()=>{this.mcpProxy?.kill("SIGKILL"),e()},5e3)}),this.mcpProxy=null),this.mcpClient&&(this.mcpClient.shutdown(),this.mcpClient=null),this.mcpProxyPath=null,this.emit("stopped"),s.info("MCP\u670D\u52A1\u5668\u5DF2\u505C\u6B62")}};export{A as MCPServer};
14
+ `;e.response.write(o)}catch(o){c.error(`\u53D1\u9001\u5230\u5BA2\u6237\u7AEF ${e.id} \u5931\u8D25:`,o),this.clients.delete(e.sessionId)}}broadcastToClients(e){for(let t of this.clients.values())this.sendToClient(t,e)}async start(){try{let e=await Promise.allSettled([this.startMCPProxy(),new Promise(n=>{this.server=this.app.listen(this.port,"0.0.0.0",()=>{c.info(`MCP\u670D\u52A1\u5668\u76D1\u542C\u7AEF\u53E3 ${this.port} (\u6240\u6709\u7F51\u7EDC\u63A5\u53E3)`),c.info(`SSE\u7AEF\u70B9: http://0.0.0.0:${this.port}/sse`),c.info(`\u6D88\u606F\u7AEF\u70B9: http://0.0.0.0:${this.port}/messages`),c.info(`RPC\u7AEF\u70B9: http://0.0.0.0:${this.port}/rpc`),c.info(`\u672C\u5730\u8BBF\u95EE: http://localhost:${this.port}`),n()})})]),[t,o]=e;if(t.status==="rejected"&&c.error("MCP\u4EE3\u7406\u542F\u52A8\u5931\u8D25:",t.reason),o.status==="rejected")throw c.error("HTTP\u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25:",o.reason),o.reason;this.startMCPClient().catch(n=>{c.error("\u542F\u52A8\u8FDE\u63A5\u5230xiaozhi.me\u7684MCP\u5BA2\u6237\u7AEF\u5931\u8D25:",n)}),this.emit("started")}catch(e){throw c.error("\u542F\u52A8MCP\u670D\u52A1\u5668\u5931\u8D25:",e),e}}findMCPProxyPath(){if(this.mcpProxyPath)return this.mcpProxyPath;if(process.env.MCP_SERVER_PROXY_PATH){let n=p.normalize(process.env.MCP_SERVER_PROXY_PATH),r=p.resolve(n),a=[S,p.join(S,".."),p.join(S,"..",".."),p.join(S,"..","..","dist"),Q.tmpdir()];if(M.existsSync(r)&&p.basename(r)===P&&a.some(l=>r.startsWith(l)))return this.mcpProxyPath=r,this.mcpProxyPath;throw c.warn(`MCP_SERVER_PROXY_PATH \u8DEF\u5F84\u4E0D\u5B89\u5168: ${n}`),new Error(`\u6307\u5B9A\u7684 MCP \u4EE3\u7406\u8DEF\u5F84\u4E0D\u5B58\u5728\u6216\u4E0D\u5B89\u5168: ${process.env.MCP_SERVER_PROXY_PATH}`)}let e=F(import.meta.url),t=p.dirname(e),o=null;for(let n=0;n<te;n++){let r=p.join(t,P);if(M.existsSync(r)){o=r;break}let a=p.join(t,"dist",P);if(M.existsSync(a)){o=a;break}t=p.dirname(t)}if(!o){let n=p.resolve(S,"..",".."),r=p.join(n,"dist",P);M.existsSync(r)&&(o=r)}if(!o)throw new Error(`\u5728\u9879\u76EE\u7ED3\u6784\u4E2D\u627E\u4E0D\u5230 ${P}`);return this.mcpProxyPath=o,this.mcpProxyPath}async startMCPProxy(){let e=this.findMCPProxyPath();c.info(`\u6B63\u5728\u542F\u52A8MCP\u4EE3\u7406: ${e}`),this.mcpProxy=K("node",[e],{stdio:["pipe","pipe","pipe"],env:{...process.env,MCP_SERVER_MODE:"true",XIAOZHI_CONFIG_DIR:process.env.XIAOZHI_CONFIG_DIR||process.cwd()}}),this.mcpProxy.on("error",t=>{c.error("MCP\u4EE3\u7406\u9519\u8BEF:",t)}),this.mcpProxy.on("exit",(t,o)=>{c.warn(`MCP\u4EE3\u7406\u9000\u51FA\uFF0C\u4EE3\u7801 ${t}\uFF0C\u4FE1\u53F7 ${o}`),this.mcpProxy=null}),this.mcpProxy.stderr&&this.mcpProxy.stderr.on("data",t=>{let o=t.toString().trim();o.includes("[ERROR]")||o.includes("Error:")||o.includes("Failed")?c.error("MCP\u4EE3\u7406stderr:",o):c.info("MCP\u4EE3\u7406\u8F93\u51FA:",o)}),this.mcpProxy.stdout&&this.mcpProxy.stdout.on("data",t=>{this.handleProxyResponse(t)}),await new Promise((t,o)=>{let n=setTimeout(()=>{o(new Error("MCP\u4EE3\u7406\u542F\u52A8\u8D85\u65F6"))},1e4),r=f(a=>{let l=a.toString();(l.includes("MCP proxy ready")||l.includes("started"))&&(clearTimeout(n),this.mcpProxy?.stdout?.removeListener("data",r),t())},"dataHandler");this.mcpProxy?.stdout?.on("data",r)}),c.info("MCP\u4EE3\u7406\u542F\u52A8\u6210\u529F")}async startMCPClient(){let e=[];try{u.configExists()&&(e=u.getMcpEndpoints())}catch(t){c.warn("\u4ECE\u914D\u7F6E\u4E2D\u8BFB\u53D6MCP\u7AEF\u70B9\u5931\u8D25:",t)}if(e.length>0){let t=this.findMCPProxyPath();this.mcpClient=new w(t,e),await this.mcpClient.start(),c.info("MCP\u5BA2\u6237\u7AEF\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5\u5230 xiaozhi.me")}else c.info("\u672A\u914D\u7F6EMCP\u7AEF\u70B9\uFF0C\u8DF3\u8FC7\u5BA2\u6237\u7AEF\u8FDE\u63A5")}async stop(){c.info("\u6B63\u5728\u505C\u6B62MCP\u670D\u52A1\u5668...");for(let e of this.clients.values())try{e.response.end()}catch{}this.clients.clear(),this.server&&(await new Promise(e=>{this.server.close(()=>e())}),this.server=null),this.mcpProxy&&(this.mcpProxy.kill("SIGTERM"),await new Promise(e=>{this.mcpProxy.on("exit",()=>e()),setTimeout(()=>{this.mcpProxy?.kill("SIGKILL"),e()},5e3)}),this.mcpProxy=null),this.mcpClient&&(this.mcpClient.shutdown(),this.mcpClient=null),this.mcpProxyPath=null,this.emit("stopped"),c.info("MCP\u670D\u52A1\u5668\u5DF2\u505C\u6B62")}};export{k as MCPServer};
15
15
  //# sourceMappingURL=mcpServer.js.map