xiaozhi-client 1.6.0-beta.8 → 1.6.0-rc.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.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![CI](https://github.com/shenjingnan/xiaozhi-client/workflows/Release/badge.svg)](https://github.com/shenjingnan/xiaozhi-client/actions)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- <img src="https://raw.githubusercontent.com/shenjingnan/xiaozhi-client/main/docs/images/qq-group-qrcode.jpg" alt="QQ群" width="200"/>
8
+ <img src="https://raw.githubusercontent.com/shenjingnan/xiaozhi-client/main/docs/images/qq-group-qrcode.jpg" alt="QQ群" width="300"/>
9
9
 
10
10
  小智 AI 客户端,目前主要用于 MCP 的对接
11
11
 
@@ -71,10 +71,22 @@ npx -y xiaozhi-client start
71
71
 
72
72
  我们提供了预配置的 Docker 镜像,可以快速启动 xiaozhi-client 环境。
73
73
 
74
+ #### 前置要求
75
+
76
+ - 已安装 Docker
77
+ - 已获取小智接入点地址(参见下方"[获取小智接入点地址](#获取小智接入点地址)"部分)
78
+
74
79
  #### 快速启动
75
80
 
76
81
  **方式一:使用启动脚本(推荐)**
77
82
 
83
+ 这个脚本会自动完成以下操作:
84
+
85
+ - 创建工作目录 `~/xiaozhi-client`
86
+ - 拉取最新的 Docker 镜像
87
+ - 停止并删除已存在的容器(如果有)
88
+ - 启动新的容器并配置端口映射
89
+
78
90
  ```bash
79
91
  # 下载并运行启动脚本
80
92
  curl -fsSL https://raw.githubusercontent.com/shenjingnan/xiaozhi-client/main/docker-start.sh | bash
@@ -82,7 +94,12 @@ curl -fsSL https://raw.githubusercontent.com/shenjingnan/xiaozhi-client/main/doc
82
94
 
83
95
  **方式二:使用 Docker Compose**
84
96
 
97
+ 首先获取 docker-compose.yml 文件:
98
+
85
99
  ```bash
100
+ # 下载 docker-compose.yml 文件
101
+ curl -O https://raw.githubusercontent.com/shenjingnan/xiaozhi-client/main/docker-compose.yml
102
+
86
103
  # 使用 Docker Compose 启动
87
104
  docker-compose up -d
88
105
 
@@ -96,19 +113,38 @@ docker-compose down
96
113
  **方式三:手动启动**
97
114
 
98
115
  ```bash
116
+ # 创建工作目录(用于持久化配置文件)
117
+ mkdir -p ~/xiaozhi-client
118
+
99
119
  # 拉取并运行 Docker 镜像(后台运行)
100
120
  docker run -d \
101
121
  --name xiaozhi-client \
102
122
  -p 9999:9999 \
103
123
  -p 3000:3000 \
104
124
  -v ~/xiaozhi-client:/workspaces \
125
+ --restart unless-stopped \
105
126
  shenjingnan/xiaozhi-client
106
127
  ```
107
128
 
108
- **端口说明**:
129
+ **参数说明**:
130
+
131
+ - `-d`:后台运行
132
+ - `--name xiaozhi-client`:容器名称
133
+ - `-p 9999:9999`:Web UI 配置界面端口
134
+ - `-p 3000:3000`:HTTP Server 模式端口(用于与其他 MCP 客户端集成)
135
+ - `-v ~/xiaozhi-client:/workspaces`:挂载本地目录用于持久化配置文件和数据
136
+ - `--restart unless-stopped`:容器自动重启策略
137
+
138
+ #### 获取小智接入点地址
139
+
140
+ 在配置 xiaozhi-client 之前,您需要先获取小智接入点地址:
141
+
142
+ 1. 访问 [xiaozhi.me](https://xiaozhi.me) 并登录
143
+ 2. 进入 MCP 配置页面
144
+ 3. 创建新的接入点或使用现有接入点
145
+ 4. 复制接入点地址(格式类似:`wss://api.xiaozhi.me/mcp/your-endpoint-id`)
109
146
 
110
- - `9999`:Web UI 配置界面端口
111
- - `3000`:HTTP Server 模式端口(用于与其他 MCP 客户端集成)
147
+ 详细配置说明请参考:[小智 AI 配置 MCP 接入点使用说明](https://ccnphfhqs21z.feishu.cn/wiki/HiPEwZ37XiitnwktX13cEM5KnSb)
112
148
 
113
149
  #### 配置服务
114
150
 
@@ -122,21 +158,41 @@ docker run -d \
122
158
 
123
159
  ##### 方式二:直接编辑配置文件
124
160
 
125
- 1. 编辑配置文件:
161
+ 1. 首次启动后,容器会在 `~/xiaozhi-client` 目录中创建默认配置文件。如果文件不存在,可以手动创建:
126
162
 
127
163
  ```bash
128
- # 配置文件位于挂载的工作目录中
164
+ # 创建配置文件
165
+ cat > ~/xiaozhi-client/xiaozhi.config.json << 'EOF'
166
+ {
167
+ "mcpEndpoint": "",
168
+ "mcpServers": {},
169
+ "modelscope": {
170
+ "apiKey": ""
171
+ },
172
+ "connection": {
173
+ "heartbeatInterval": 30000,
174
+ "heartbeatTimeout": 10000,
175
+ "reconnectInterval": 5000
176
+ },
177
+ "webUI": {
178
+ "port": 9999
179
+ }
180
+ }
181
+ EOF
182
+ ```
183
+
184
+ 2. 编辑配置文件,修改 `mcpEndpoint` 字段:
185
+
186
+ ```bash
187
+ # 编辑配置文件
129
188
  vim ~/xiaozhi-client/xiaozhi.config.json
130
189
  ```
131
190
 
132
- 2. 修改 `mcpEndpoint` 字段:
191
+ `mcpEndpoint` 修改为您的实际接入点地址:
133
192
 
134
193
  ```json
135
194
  {
136
- "mcpEndpoint": "wss://api.xiaozhi.me/mcp/your-endpoint-id",
137
- "mcpServers": {
138
- // ... 其他配置
139
- }
195
+ "mcpEndpoint": "wss://api.xiaozhi.me/mcp/your-actual-endpoint-id"
140
196
  }
141
197
  ```
142
198
 
@@ -174,6 +230,53 @@ docker exec -it xiaozhi-client xiaozhi mcp list
174
230
  docker exec -it xiaozhi-client xiaozhi mcp list --tools
175
231
  ```
176
232
 
233
+ #### 故障排除
234
+
235
+ **问题 1:容器启动失败**
236
+
237
+ ```bash
238
+ # 检查容器状态
239
+ docker ps -a | grep xiaozhi-client
240
+
241
+ # 查看详细错误日志
242
+ docker logs xiaozhi-client
243
+ ```
244
+
245
+ **问题 2:无法访问 Web UI (http://localhost:9999)**
246
+
247
+ - 检查容器是否正在运行:`docker ps | grep xiaozhi-client`
248
+ - 检查端口是否被占用:`lsof -i :9999` (macOS/Linux) 或 `netstat -ano | findstr :9999` (Windows)
249
+ - 确认防火墙设置允许访问 9999 端口
250
+
251
+ **问题 3:配置文件不生效**
252
+
253
+ - 确认配置文件路径:`ls -la ~/xiaozhi-client/xiaozhi.config.json`
254
+ - 检查配置文件格式是否正确(JSON 语法)
255
+ - 重启容器:`docker restart xiaozhi-client`
256
+
257
+ **问题 4:连接小智服务器失败**
258
+
259
+ - 检查接入点地址是否正确
260
+ - 确认网络连接正常
261
+ - 查看容器日志:`docker logs -f xiaozhi-client`
262
+
263
+ **问题 5:端口冲突**
264
+
265
+ 如果默认端口被占用,可以修改端口映射:
266
+
267
+ ```bash
268
+ # 使用不同的端口启动
269
+ docker run -d \
270
+ --name xiaozhi-client \
271
+ -p 8888:9999 \
272
+ -p 3001:3000 \
273
+ -v ~/xiaozhi-client:/workspaces \
274
+ --restart unless-stopped \
275
+ shenjingnan/xiaozhi-client
276
+ ```
277
+
278
+ 然后访问 <http://localhost:8888> 进行配置。
279
+
177
280
  ## 可用命令
178
281
 
179
282
  ```bash
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- var F=Object.defineProperty;var f=(r,e)=>F(r,"name",{value:e,configurable:!0});import c from"process";import{fileURLToPath as Z}from"url";import{config as z}from"dotenv";import{copyFileSync as W,existsSync as I,readFileSync as j,writeFileSync as D}from"fs";import{dirname as N,resolve as m}from"path";import{fileURLToPath as _}from"url";import*as S from"comment-json";import E from"json5";import*as $ from"json5-writer";function k(r){if(!r||typeof r!="object")throw new Error("\u670D\u52A1\u914D\u7F6E\u5FC5\u987B\u662F\u4E00\u4E2A\u6709\u6548\u7684\u5BF9\u8C61");if("command"in r&&typeof r.command=="string")return"stdio";if("type"in r&&r.type==="sse")return"sse";if("type"in r&&r.type==="streamable-http"||"url"in r&&typeof r.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(k,"getMcpServerCommunicationType");function P(r,e){if(!e||typeof e!="object")return{valid:!1,error:`\u670D\u52A1 "${r}" \u7684\u914D\u7F6E\u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61`};try{switch(k(e)){case"stdio":if(!e.command||typeof e.command!="string")return{valid:!1,error:`\u670D\u52A1 "${r}" \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 "${r}" \u7684 args \u5B57\u6BB5\u5FC5\u987B\u662F\u6570\u7EC4`};if(e.env&&typeof e.env!="object")return{valid:!1,error:`\u670D\u52A1 "${r}" \u7684 env \u5B57\u6BB5\u5FC5\u987B\u662F\u5BF9\u8C61`};break;case"sse":if(e.type!=="sse")return{valid:!1,error:`\u670D\u52A1 "${r}" \u7684 type \u5B57\u6BB5\u5FC5\u987B\u662F "sse"`};if(!e.url||typeof e.url!="string")return{valid:!1,error:`\u670D\u52A1 "${r}" \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 "${r}" \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 "${r}" \u7684 type \u5B57\u6BB5\u5982\u679C\u5B58\u5728\uFF0C\u5FC5\u987B\u662F "streamable-http"`};break;default:return{valid:!1,error:`\u670D\u52A1 "${r}" \u7684\u914D\u7F6E\u7C7B\u578B\u65E0\u6CD5\u8BC6\u522B`}}return{valid:!0}}catch(t){return{valid:!1,error:`\u670D\u52A1 "${r}" \u7684\u914D\u7F6E\u65E0\u6548: ${t instanceof Error?t.message:"\u672A\u77E5\u9519\u8BEF"}`}}}f(P,"validateMcpServerConfig");var H=N(_(import.meta.url)),M={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},y=class r{static{f(this,"ConfigManager")}static instance;defaultConfigPath;config=null;currentConfigPath=null;json5Writer=null;constructor(){this.defaultConfigPath=m(H,"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 n of t){let o=m(e,n);if(I(o))return o}return m(e,"xiaozhi.config.json")}getConfigFileFormat(e){return e.endsWith(".json5")?"json5":e.endsWith(".jsonc")?"jsonc":"json"}static getInstance(){return r.instance||(r.instance=new r),r.instance}configExists(){let e=process.env.XIAOZHI_CONFIG_DIR||process.cwd(),t=["xiaozhi.config.json5","xiaozhi.config.jsonc","xiaozhi.config.json"];for(let n of t){let o=m(e,n);if(I(o))return!0}return!1}initConfig(e="json"){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\u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u91CD\u590D\u521D\u59CB\u5316");let t=process.env.XIAOZHI_CONFIG_DIR||process.cwd(),n=`xiaozhi.config.${e}`,o=m(t,n);W(this.defaultConfigPath,o),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),n=j(e,"utf8"),o;switch(t){case"json5":o=E.parse(n),this.json5Writer=$.load(n);break;case"jsonc":o=S.parse(n);break;default:o=JSON.parse(n);break}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===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 n of t.mcpEndpoint)if(typeof n!="string"||n.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[n,o]of Object.entries(t.mcpServers)){if(!o||typeof o!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${n} \u65E0\u6548`);let i=P(n,o);if(!i.valid)throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A${i.error}`)}}getConfig(){return 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 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 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(),n=this.getMcpEndpoints();if(n.includes(e))throw new Error(`MCP \u7AEF\u70B9 ${e} \u5DF2\u5B58\u5728`);let o=[...n,e];t.mcpEndpoint=o,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(),n=this.getMcpEndpoints();if(n.indexOf(e)===-1)throw new Error(`MCP \u7AEF\u70B9 ${e} \u4E0D\u5B58\u5728`);if(n.length===1)throw new Error("\u4E0D\u80FD\u5220\u9664\u6700\u540E\u4E00\u4E2A MCP \u7AEF\u70B9");let i=n.filter(g=>g!==e);t.mcpEndpoint=i,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 n=P(e,t);if(!n.valid)throw new Error(n.error||"\u670D\u52A1\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");let o=this.getMutableConfig();o.mcpServers[e]=t,this.saveConfig(o)}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 n={...t.mcpServers};delete n[e];let o={...t,mcpServers:n};this.saveConfig(o)}updateServerToolsConfig(e,t){let n=this.getMutableConfig();n.mcpServerConfig||(n.mcpServerConfig={}),Object.keys(t).length===0?delete n.mcpServerConfig[e]:n.mcpServerConfig[e]={tools:t},this.saveConfig(n)}removeServerToolsConfig(e){let n={...this.getConfig()};n.mcpServerConfig&&(delete n.mcpServerConfig[e],this.saveConfig(n))}setToolEnabled(e,t,n,o){let i=this.getMutableConfig();i.mcpServerConfig||(i.mcpServerConfig={}),i.mcpServerConfig[e]||(i.mcpServerConfig[e]={tools:{}}),i.mcpServerConfig[e].tools[t]={...i.mcpServerConfig[e].tools[t],enable:n,...o&&{description:o}},this.saveConfig(i)}saveConfig(e){try{this.validateConfig(e);let t;this.currentConfigPath?t=this.currentConfigPath:(t=this.getConfigFilePath(),this.currentConfigPath=t);let n=this.getConfigFileFormat(t),o;switch(n){case"json5":try{this.json5Writer?(this.json5Writer.write(e),o=this.json5Writer.toSource()):(console.warn("\u6CA1\u6709 json5Writer \u5B9E\u4F8B\uFF0C\u56DE\u9000\u5230\u6807\u51C6 JSON5 \u683C\u5F0F"),o=E.stringify(e,null,2))}catch(i){console.warn("\u4F7F\u7528 json5-writer \u4FDD\u5B58\u5931\u8D25\uFF0C\u56DE\u9000\u5230\u6807\u51C6 JSON5 \u683C\u5F0F:",i),o=E.stringify(e,null,2)}break;case"jsonc":try{o=S.stringify(e,null,2)}catch(i){console.warn("\u4F7F\u7528 comment-json \u4FDD\u5B58\u5931\u8D25\uFF0C\u56DE\u9000\u5230\u6807\u51C6 JSON \u683C\u5F0F:",i),o=JSON.stringify(e,null,2)}break;default:o=JSON.stringify(e,null,2);break}D(t,o,"utf8"),this.config=e,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??M.heartbeatInterval,heartbeatTimeout:t.heartbeatTimeout??M.heartbeatTimeout,reconnectInterval:t.reconnectInterval??M.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})}},p=y.getInstance();import v from"fs";import G from"path";import C from"chalk";import{createConsola as L}from"consola";function J(r){let e=r.getFullYear(),t=String(r.getMonth()+1).padStart(2,"0"),n=String(r.getDate()).padStart(2,"0"),o=String(r.getHours()).padStart(2,"0"),i=String(r.getMinutes()).padStart(2,"0"),g=String(r.getSeconds()).padStart(2,"0");return`${e}-${t}-${n} ${o}:${i}:${g}`}f(J,"formatDateTime");var T=class{static{f(this,"Logger")}logFilePath=null;writeStream=null;consolaInstance;isDaemonMode;constructor(){this.isDaemonMode=process.env.XIAOZHI_DAEMON==="true",this.consolaInstance=L({formatOptions:{date:!1,colors:!0,compact:!0},fancy:!1});let e=this.isDaemonMode;this.consolaInstance.setReporters([{log:f(t=>{let n={info:"INFO",success:"SUCCESS",warn:"WARN",error:"ERROR",debug:"DEBUG",log:"LOG"},o={info:C.blue,success:C.green,warn:C.yellow,error:C.red,debug:C.gray,log:f(h=>h,"log")},i=n[t.type]||t.type.toUpperCase(),g=o[t.type]||(h=>h),d=J(new Date),O=g(`[${i}]`),A=`[${d}] ${O} ${t.args.join(" ")}`;if(!e)try{console.error(A)}catch(h){if(h instanceof Error&&h.message?.includes("EPIPE"))return;throw h}},"log")}])}initLogFile(e){this.logFilePath=G.join(e,"xiaozhi.log"),v.existsSync(this.logFilePath)||v.writeFileSync(this.logFilePath,""),this.writeStream=v.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"})}logToFile(e,t,...n){if(this.writeStream){let i=`[${new Date().toISOString()}] [${e.toUpperCase()}] ${t}`,g=n.length>0?`${i} ${n.map(d=>typeof d=="object"?JSON.stringify(d):String(d)).join(" ")}`:i;this.writeStream.write(`${g}
3
- `)}}enableFileLogging(e){e&&!this.writeStream&&this.logFilePath?this.writeStream=v.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)}},u=new T;import{spawn as X}from"child_process";import l from"process";import b from"ws";var x=f(()=>l.env.NODE_ENV==="test"||l.env.VITEST==="true","isTestEnvironment"),s=u.withTag("MULTI_MCP_PIPE");l.env.XIAOZHI_DAEMON==="true"&&l.env.XIAOZHI_CONFIG_DIR&&(u.initLogFile(l.env.XIAOZHI_CONFIG_DIR),u.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 n of t)this.endpoints.set(n,{url:n,websocket:null,isConnected:!1,reconnectAttempt:0,maxReconnectAttempts:5,process:null,stdoutBuffer:""});try{this.connectionConfig=p.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(n){this.connectionConfig={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},s.warn(`\u65E0\u6CD5\u83B7\u53D6\u8FDE\u63A5\u914D\u7F6E\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C: ${n instanceof Error?n.message:String(n)}`)}}async start(){return await this.connectToAllEndpoints(),this.reportStatusToWebUI(),new Promise(e=>{this.shutdownResolve=e})}async connectToAllEndpoints(){let e=[];for(let[t,n]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 n=new b(e);t.websocket=n,n.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)}),n.on("message",o=>{let i=o.toString();s.info(`<< [${e}] WebSocket\u6536\u5230\u6D88\u606F: ${i}`);try{let g=JSON.parse(i);(g.method==="notifications/initialized"||g.method==="tools/list"&&g.id||g?.result?.tools)&&setTimeout(()=>{this.reportStatusToWebUI()},1e3)}catch{}t.process?.stdin&&!t.process.stdin.destroyed&&t.process.stdin.write(`${i}
4
- `)}),n.on("close",(o,i)=>{s.error(`[${e}] WebSocket \u8FDE\u63A5\u5DF2\u5173\u95ED: ${o} ${i}`),t.isConnected=!1,t.websocket=null,this.stopHeartbeat(e),this.reportStatusToWebUI(),this.shouldReconnect&&(o===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))}),n.on("error",o=>{s.error(`[${e}] WebSocket \u9519\u8BEF: ${o.message}`),t.isConnected=!1,this.stopHeartbeat(e)}),n.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 n=this.connectionConfig.reconnectInterval,i=Math.min(n*2**(t.reconnectAttempt-1),6e4);s.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)&&s.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){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=X("node",[this.mcpScript],{stdio:["pipe","pipe","pipe"]}),t.process.stdout?.on("data",n=>{t.stdoutBuffer+=n.toString();let o=t.stdoutBuffer.split(`
5
- `);t.stdoutBuffer=o.pop()||"";for(let i of o)i.trim()&&this.handleMCPMessage(e,i)}),t.process.stderr?.on("data",n=>{if(l.env.XIAOZHI_DAEMON!=="true")try{l.stderr.write(n)}catch{}}),t.process.on("exit",(n,o)=>{s.warn(`[${e}] MCP \u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${n}, \u4FE1\u53F7: ${o}`),t.process=null,this.shouldReconnect&&o!=="SIGTERM"&&o!=="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",n=>{s.error(`[${e}] \u8FDB\u7A0B\u9519\u8BEF: ${n.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 n=this.endpoints.get(e);if(!n||!n.websocket||n.websocket.readyState!==b.OPEN){s.warn(`[${e}] \u7AEF\u70B9\u4E0D\u53EF\u7528\uFF0C\u6D88\u606F\u65E0\u6CD5\u53D1\u9001`);return}try{n.websocket.send(`${t}
6
- `),s.info(`>> [${e}] \u6210\u529F\u53D1\u9001\u6D88\u606F\u5230 WebSocket`)}catch(o){s.error(`>> [${e}] \u53D1\u9001\u6D88\u606F\u5230 WebSocket \u5931\u8D25: ${o}`)}}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(n){s.debug(`[${e}] \u5173\u95ED WebSocket \u65F6\u51FA\u9519: ${n}`)}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(n=>{let o=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")),n()},2e3);t.process?.on("exit",()=>{clearTimeout(o),n()})})}catch(n){s.warn(`[${e}] \u7EC8\u6B62 MCP \u8FDB\u7A0B\u65F6\u51FA\u9519: ${n}`)}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(n){s.error(`[${e}] \u7EC8\u6B62\u8FDB\u7A0B\u65F6\u51FA\u9519: ${n instanceof Error?n.message:String(n)}`)}t.process=null}if(t.websocket){try{t.websocket.close()}catch(n){s.warn(`[${e}] \u5173\u95ED WebSocket \u65F6\u51FA\u9519: ${n}`)}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(),x()||setTimeout(()=>{l.exit(0)},100)}async reportStatusToWebUI(){if(!x())try{let e=p.getWebUIPort(),t=new b(`ws://localhost:${e}`);t.on("open",()=>{let n=[];for(let[i,g]of this.endpoints)n.push({url:i,connected:g.isConnected});let o={type:"clientStatus",data:{status:this.hasAnyConnection()?"connected":"disconnected",mcpEndpoints:n,activeMCPServers:[],lastHeartbeat:Date.now()}};t.send(JSON.stringify(o)),s.debug("\u5DF2\u5411 Web UI \u62A5\u544A\u72B6\u6001"),setTimeout(()=>{t.close()},1e3)}),t.on("error",n=>{s.debug(`Web UI \u8FDE\u63A5\u5931\u8D25\uFF08\u53EF\u80FD\u672A\u8FD0\u884C\uFF09: ${n.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}};function R(r){let e=l.env.XIAOZHI_DAEMON==="true";l.on("SIGINT",()=>{s.info("\u6536\u5230\u4E2D\u65AD\u4FE1\u53F7\uFF0C\u6B63\u5728\u5173\u95ED..."),r.shutdown()}),l.on("SIGTERM",()=>{s.info("\u6536\u5230\u7EC8\u6B62\u4FE1\u53F7\uFF0C\u6B63\u5728\u5173\u95ED..."),r.shutdown()}),e&&(l.on("SIGHUP",()=>{s.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...")}),l.on("uncaughtException",t=>{t.message?.includes("EPIPE")||s.error(`\u672A\u6355\u83B7\u7684\u5F02\u5E38: ${t.message||t}
7
- ${t.stack||""}`)}),l.on("unhandledRejection",(t,n)=>{s.error(`\u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD: ${t instanceof Error?t.message:String(t)}`)}))}f(R,"setupSignalHandlers");z();var a=u.withTag("ADAPTIVE_MCP_PIPE");c.env.XIAOZHI_DAEMON==="true"&&c.env.XIAOZHI_CONFIG_DIR&&(u.initLogFile(c.env.XIAOZHI_CONFIG_DIR),u.enableFileLogging(!0));async function B(){c.argv.length<3&&(a.error("\u7528\u6CD5: node adaptiveMCPPipe.js <mcp_script>"),c.exit(1));let r=c.argv[2],e;try{if(c.env.XIAOZHI_DAEMON!=="true")try{c.stderr.write(`[DEBUG] XIAOZHI_CONFIG_DIR: ${c.env.XIAOZHI_CONFIG_DIR}
2
+ var F=Object.defineProperty;var f=(i,e)=>F(i,"name",{value:e,configurable:!0});import c from"process";import{fileURLToPath as z}from"url";import{config as B}from"dotenv";import{copyFileSync as H,existsSync as E,readFileSync as _,writeFileSync as G}from"fs";import{dirname as N,resolve as C}from"path";import{fileURLToPath as L}from"url";import*as v from"comment-json";import J from"dayjs";import M from"json5";import*as $ from"json5-writer";import S from"fs";import k from"path";import m from"chalk";import{createConsola as D}from"consola";function j(i){let e=i.getFullYear(),t=String(i.getMonth()+1).padStart(2,"0"),n=String(i.getDate()).padStart(2,"0"),o=String(i.getHours()).padStart(2,"0"),r=String(i.getMinutes()).padStart(2,"0"),g=String(i.getSeconds()).padStart(2,"0");return`${e}-${t}-${n} ${o}:${r}:${g}`}f(j,"formatDateTime");var P=class{static{f(this,"Logger")}logFilePath=null;writeStream=null;consolaInstance;isDaemonMode;constructor(){this.isDaemonMode=process.env.XIAOZHI_DAEMON==="true",this.consolaInstance=D({formatOptions:{date:!1,colors:!0,compact:!0},fancy:!1});let e=this.isDaemonMode;this.consolaInstance.setReporters([{log:f(t=>{let n={info:"INFO",success:"SUCCESS",warn:"WARN",error:"ERROR",debug:"DEBUG",log:"LOG"},o={info:m.blue,success:m.green,warn:m.yellow,error:m.red,debug:m.gray,log:f(d=>d,"log")},r=n[t.type]||t.type.toUpperCase(),g=o[t.type]||(d=>d),u=j(new Date),O=g(`[${r}]`),A=`[${u}] ${O} ${t.args.join(" ")}`;if(!e)try{console.error(A)}catch(d){if(d instanceof Error&&d.message?.includes("EPIPE"))return;throw d}},"log")}])}initLogFile(e){this.logFilePath=k.join(e,"xiaozhi.log"),S.existsSync(this.logFilePath)||S.writeFileSync(this.logFilePath,""),this.writeStream=S.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"})}logToFile(e,t,...n){if(this.writeStream){let r=`[${new Date().toISOString()}] [${e.toUpperCase()}] ${t}`,g=n.length>0?`${r} ${n.map(u=>typeof u=="object"?JSON.stringify(u):String(u)).join(" ")}`:r;this.writeStream.write(`${g}
3
+ `)}}enableFileLogging(e){e&&!this.writeStream&&this.logFilePath?this.writeStream=S.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)}},p=new P;function W(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(W,"getMcpServerCommunicationType");function I(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(W(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(I,"validateMcpServerConfig");var X=N(L(import.meta.url)),y={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},T=class i{static{f(this,"ConfigManager")}static instance;defaultConfigPath;config=null;currentConfigPath=null;json5Writer=null;constructor(){this.defaultConfigPath=C(X,"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 n of t){let o=C(e,n);if(E(o))return o}return C(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 n of t){let o=C(e,n);if(E(o))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(),n=`xiaozhi.config.${e}`,o=C(t,n);H(this.defaultConfigPath,o),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=_(e,"utf8").replace(/^\uFEFF/,""),r;switch(t){case"json5":r=M.parse(o),this.json5Writer=$.load(o);break;case"jsonc":r=v.parse(o);break;default:r=JSON.parse(o);break}return this.validateConfig(r),r}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 n of t.mcpEndpoint)if(typeof n!="string"||n.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[n,o]of Object.entries(t.mcpServers)){if(!o||typeof o!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${n} \u65E0\u6548`);let r=I(n,o);if(!r.valid)throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A${r.error}`)}}getConfig(){return 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 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 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(),n=this.getMcpEndpoints();if(n.includes(e))throw new Error(`MCP \u7AEF\u70B9 ${e} \u5DF2\u5B58\u5728`);let o=[...n,e];t.mcpEndpoint=o,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(),n=this.getMcpEndpoints();if(n.indexOf(e)===-1)throw new Error(`MCP \u7AEF\u70B9 ${e} \u4E0D\u5B58\u5728`);if(n.length===1)throw new Error("\u4E0D\u80FD\u5220\u9664\u6700\u540E\u4E00\u4E2A MCP \u7AEF\u70B9");let r=n.filter(g=>g!==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 n=I(e,t);if(!n.valid)throw new Error(n.error||"\u670D\u52A1\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");let o=this.getMutableConfig();o.mcpServers[e]=t,this.saveConfig(o)}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 n={...t.mcpServers};delete n[e];let o={...t,mcpServers:n};this.saveConfig(o)}updateServerToolsConfig(e,t){let n=this.getMutableConfig();n.mcpServerConfig||(n.mcpServerConfig={}),Object.keys(t).length===0?delete n.mcpServerConfig[e]:n.mcpServerConfig[e]={tools:t},this.saveConfig(n)}removeServerToolsConfig(e){let n={...this.getConfig()};n.mcpServerConfig&&(delete n.mcpServerConfig[e],this.saveConfig(n))}setToolEnabled(e,t,n,o){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:n,...o&&{description:o}},this.saveConfig(r)}saveConfig(e){try{this.validateConfig(e);let t;this.currentConfigPath?t=this.currentConfigPath:(t=this.getConfigFilePath(),this.currentConfigPath=t);let n=this.getConfigFileFormat(t),o;switch(n){case"json5":try{this.json5Writer?(this.json5Writer.write(e),o=this.json5Writer.toSource()):(console.warn("\u6CA1\u6709 json5Writer \u5B9E\u4F8B\uFF0C\u56DE\u9000\u5230\u6807\u51C6 JSON5 \u683C\u5F0F"),o=M.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),o=M.stringify(e,null,2)}break;case"jsonc":try{o=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),o=JSON.stringify(e,null,2)}break;default:o=JSON.stringify(e,null,2);break}G(t,o,"utf8"),this.config=e,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??y.heartbeatInterval,heartbeatTimeout:t.heartbeatTimeout??y.heartbeatTimeout,reconnectInterval:t.reconnectInterval??y.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)}async updateToolUsageStats(e,t,n){try{let o=this.getMutableConfig();o.mcpServerConfig||(o.mcpServerConfig={}),o.mcpServerConfig[e]||(o.mcpServerConfig[e]={tools:{}}),o.mcpServerConfig[e].tools[t]||(o.mcpServerConfig[e].tools[t]={enable:!0});let r=o.mcpServerConfig[e].tools[t],g=r.usageCount||0,u=r.lastUsedTime;r.usageCount=g+1,(!u||new Date(n)>new Date(u))&&(r.lastUsedTime=J(n).format("YYYY-MM-DD HH:mm:ss")),this.saveConfig(o),p.debug(`\u5DE5\u5177\u4F7F\u7528\u7EDF\u8BA1\u5DF2\u66F4\u65B0: ${e}/${t}, \u4F7F\u7528\u6B21\u6570: ${r.usageCount}`)}catch(o){p.error(`\u66F4\u65B0\u5DE5\u5177\u4F7F\u7528\u7EDF\u8BA1\u5931\u8D25 (${e}/${t}): ${o instanceof Error?o.message:String(o)}`)}}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})}},h=T.getInstance();import{spawn as Z}from"child_process";import l from"process";import b from"ws";var x=f(()=>l.env.NODE_ENV==="test"||l.env.VITEST==="true","isTestEnvironment"),s=p.withTag("MULTI_MCP_PIPE");l.env.XIAOZHI_DAEMON==="true"&&l.env.XIAOZHI_CONFIG_DIR&&(p.initLogFile(l.env.XIAOZHI_CONFIG_DIR),p.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 n of t)this.endpoints.set(n,{url:n,websocket:null,isConnected:!1,reconnectAttempt:0,maxReconnectAttempts:5,process:null,stdoutBuffer:""});try{this.connectionConfig=h.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(n){this.connectionConfig={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},s.warn(`\u65E0\u6CD5\u83B7\u53D6\u8FDE\u63A5\u914D\u7F6E\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C: ${n instanceof Error?n.message:String(n)}`)}}async start(){return await this.connectToAllEndpoints(),this.reportStatusToWebUI(),new Promise(e=>{this.shutdownResolve=e})}async connectToAllEndpoints(){let e=[];for(let[t,n]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 n=new b(e);t.websocket=n,n.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)}),n.on("message",o=>{let r=o.toString();s.info(`<< [${e}] WebSocket\u6536\u5230\u6D88\u606F: ${r}`);try{let g=JSON.parse(r);(g.method==="notifications/initialized"||g.method==="tools/list"&&g.id||g?.result?.tools)&&setTimeout(()=>{this.reportStatusToWebUI()},1e3)}catch{}t.process?.stdin&&!t.process.stdin.destroyed&&t.process.stdin.write(`${r}
4
+ `)}),n.on("close",(o,r)=>{s.error(`[${e}] WebSocket \u8FDE\u63A5\u5DF2\u5173\u95ED: ${o} ${r}`),t.isConnected=!1,t.websocket=null,this.stopHeartbeat(e),this.reportStatusToWebUI(),this.shouldReconnect&&(o===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))}),n.on("error",o=>{s.error(`[${e}] WebSocket \u9519\u8BEF: ${o.message}`),t.isConnected=!1,this.stopHeartbeat(e)}),n.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 n=this.connectionConfig.reconnectInterval,r=Math.min(n*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=Z("node",[this.mcpScript],{stdio:["pipe","pipe","pipe"]}),t.process.stdout?.on("data",n=>{t.stdoutBuffer+=n.toString();let o=t.stdoutBuffer.split(`
5
+ `);t.stdoutBuffer=o.pop()||"";for(let r of o)r.trim()&&this.handleMCPMessage(e,r)}),t.process.stderr?.on("data",n=>{if(l.env.XIAOZHI_DAEMON!=="true")try{l.stderr.write(n)}catch{}}),t.process.on("exit",(n,o)=>{s.warn(`[${e}] MCP \u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${n}, \u4FE1\u53F7: ${o}`),t.process=null,this.shouldReconnect&&o!=="SIGTERM"&&o!=="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",n=>{s.error(`[${e}] \u8FDB\u7A0B\u9519\u8BEF: ${n.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 n=this.endpoints.get(e);if(!n||!n.websocket||n.websocket.readyState!==b.OPEN){s.warn(`[${e}] \u7AEF\u70B9\u4E0D\u53EF\u7528\uFF0C\u6D88\u606F\u65E0\u6CD5\u53D1\u9001`);return}try{n.websocket.send(`${t}
6
+ `),s.info(`>> [${e}] \u6210\u529F\u53D1\u9001\u6D88\u606F\u5230 WebSocket`)}catch(o){s.error(`>> [${e}] \u53D1\u9001\u6D88\u606F\u5230 WebSocket \u5931\u8D25: ${o}`)}}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(n){s.debug(`[${e}] \u5173\u95ED WebSocket \u65F6\u51FA\u9519: ${n}`)}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(n=>{let o=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")),n()},2e3);t.process?.on("exit",()=>{clearTimeout(o),n()})})}catch(n){s.warn(`[${e}] \u7EC8\u6B62 MCP \u8FDB\u7A0B\u65F6\u51FA\u9519: ${n}`)}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(n){s.error(`[${e}] \u7EC8\u6B62\u8FDB\u7A0B\u65F6\u51FA\u9519: ${n instanceof Error?n.message:String(n)}`)}t.process=null}if(t.websocket){try{t.websocket.close()}catch(n){s.warn(`[${e}] \u5173\u95ED WebSocket \u65F6\u51FA\u9519: ${n}`)}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(),x()||setTimeout(()=>{l.exit(0)},100)}async reportStatusToWebUI(){if(!x())try{let e=h.getWebUIPort(),t=new b(`ws://localhost:${e}`);t.on("open",()=>{let n=[];for(let[r,g]of this.endpoints)n.push({url:r,connected:g.isConnected});let o={type:"clientStatus",data:{status:this.hasAnyConnection()?"connected":"disconnected",mcpEndpoints:n,activeMCPServers:[],lastHeartbeat:Date.now()}};t.send(JSON.stringify(o)),s.debug("\u5DF2\u5411 Web UI \u62A5\u544A\u72B6\u6001"),setTimeout(()=>{t.close()},1e3)}),t.on("error",n=>{s.debug(`Web UI \u8FDE\u63A5\u5931\u8D25\uFF08\u53EF\u80FD\u672A\u8FD0\u884C\uFF09: ${n.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}};function R(i){let e=l.env.XIAOZHI_DAEMON==="true";l.on("SIGINT",()=>{s.info("\u6536\u5230\u4E2D\u65AD\u4FE1\u53F7\uFF0C\u6B63\u5728\u5173\u95ED..."),i.shutdown()}),l.on("SIGTERM",()=>{s.info("\u6536\u5230\u7EC8\u6B62\u4FE1\u53F7\uFF0C\u6B63\u5728\u5173\u95ED..."),i.shutdown()}),e&&(l.on("SIGHUP",()=>{s.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...")}),l.on("uncaughtException",t=>{t.message?.includes("EPIPE")||s.error(`\u672A\u6355\u83B7\u7684\u5F02\u5E38: ${t.message||t}
7
+ ${t.stack||""}`)}),l.on("unhandledRejection",(t,n)=>{s.error(`\u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD: ${t instanceof Error?t.message:String(t)}`)}))}f(R,"setupSignalHandlers");B();var a=p.withTag("ADAPTIVE_MCP_PIPE");c.env.XIAOZHI_DAEMON==="true"&&c.env.XIAOZHI_CONFIG_DIR&&(p.initLogFile(c.env.XIAOZHI_CONFIG_DIR),p.enableFileLogging(!0));async function U(){c.argv.length<3&&(a.error("\u7528\u6CD5: node adaptiveMCPPipe.js <mcp_script>"),c.exit(1));let i=c.argv[2],e;try{if(c.env.XIAOZHI_DAEMON!=="true")try{c.stderr.write(`[DEBUG] XIAOZHI_CONFIG_DIR: ${c.env.XIAOZHI_CONFIG_DIR}
8
8
  `),c.stderr.write(`[DEBUG] process.cwd(): ${c.cwd()}
9
- `),c.stderr.write(`[DEBUG] configManager.getConfigPath(): ${p.getConfigPath()}
10
- `),c.stderr.write(`[DEBUG] configManager.configExists(): ${p.configExists()}
11
- `)}catch{}if(p.configExists())e=p.getMcpEndpoints(),a.info(`\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6\u4E2D\u7684 MCP \u7AEF\u70B9\uFF08${e.length} \u4E2A\uFF09`);else{let o=c.env.MCP_ENDPOINT;o||(a.error("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\u4E14\u672A\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF"),a.error('\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF'),c.exit(1)),e=[o],a.info("\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684 MCP \u7AEF\u70B9\uFF08\u5EFA\u8BAE\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6\uFF09")}}catch(o){a.error(`\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`);let i=c.env.MCP_ENDPOINT;i||(a.error('\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF'),c.exit(1)),e=[i],a.info("\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684 MCP \u7AEF\u70B9\u4F5C\u4E3A\u5907\u7528\u65B9\u6848")}let t=e.filter(o=>!o||o.includes("<\u8BF7\u586B\u5199")?(a.warn(`\u8DF3\u8FC7\u65E0\u6548\u7AEF\u70B9: ${o}`),!1):!0);if(t.length===0){a.warn("\u6CA1\u6709\u6709\u6548\u7684 MCP \u7AEF\u70B9\uFF0C\u5C06\u8DF3\u8FC7\u5C0F\u667A\u670D\u52A1\u7AEF\u8FDE\u63A5"),a.info("MCP \u670D\u52A1\u5668\u529F\u80FD\u4ECD\u7136\u53EF\u7528\uFF0C\u53EF\u901A\u8FC7 Web \u754C\u9762\u914D\u7F6E\u7AEF\u70B9\u540E\u91CD\u542F\u670D\u52A1"),a.info('\u63D0\u793A: \u8BF7\u8FD0\u884C "xiaozhi config mcpEndpoint <your-endpoint-url>" \u8BBE\u7F6E\u7AEF\u70B9'),await K(r);return}a.info(t.length===1?"\u542F\u52A8\u5355\u7AEF\u70B9\u8FDE\u63A5":`\u542F\u52A8\u591A\u7AEF\u70B9\u8FDE\u63A5\uFF08${t.length} \u4E2A\u7AEF\u70B9\uFF09`);let n=new w(r,t);R(n);try{await n.start()}catch(o){a.error(`\u7A0B\u5E8F\u6267\u884C\u9519\u8BEF: ${o instanceof Error?o.message:String(o)}`),c.exit(1)}}f(B,"main");async function K(r){a.info("\u542F\u52A8 MCP \u670D\u52A1\u5668\u4EE3\u7406\uFF08\u65E0\u5C0F\u667A\u670D\u52A1\u7AEF\u8FDE\u63A5\uFF09");let{spawn:e}=await import("child_process"),t=e("node",[r],{stdio:["pipe","inherit","inherit"],env:{...c.env,XIAOZHI_CONFIG_DIR:c.env.XIAOZHI_CONFIG_DIR||c.cwd()}}),n=f(()=>{a.info("\u6B63\u5728\u5173\u95ED MCP \u670D\u52A1\u5668\u4EE3\u7406..."),t&&!t.killed&&(t.kill("SIGTERM"),setTimeout(()=>{t.killed||t.kill("SIGKILL")},5e3)),c.exit(0)},"cleanup");return c.on("SIGINT",n),c.on("SIGTERM",n),t.on("exit",(o,i)=>{a.warn(`MCP \u670D\u52A1\u5668\u4EE3\u7406\u5DF2\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${o}, \u4FE1\u53F7: ${i}`),i!=="SIGTERM"&&i!=="SIGKILL"&&(a.error("MCP \u670D\u52A1\u5668\u4EE3\u7406\u610F\u5916\u9000\u51FA"),c.exit(1))}),t.on("error",o=>{a.error(`MCP \u670D\u52A1\u5668\u4EE3\u7406\u9519\u8BEF: ${o.message}`),c.exit(1)}),a.info("MCP \u670D\u52A1\u5668\u4EE3\u7406\u5DF2\u542F\u52A8\uFF0C\u7B49\u5F85\u8FDE\u63A5..."),new Promise(()=>{})}f(K,"startMCPServerProxyOnly");var U=import.meta.url,V=Z(U),q=c.argv[1];V===q&&B().catch(r=>{a.error(`\u672A\u5904\u7406\u7684\u9519\u8BEF: ${r instanceof Error?r.message:String(r)}`),c.exit(1)});export{B as main};
9
+ `),c.stderr.write(`[DEBUG] configManager.getConfigPath(): ${h.getConfigPath()}
10
+ `),c.stderr.write(`[DEBUG] configManager.configExists(): ${h.configExists()}
11
+ `)}catch{}if(h.configExists())e=h.getMcpEndpoints(),a.info(`\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6\u4E2D\u7684 MCP \u7AEF\u70B9\uFF08${e.length} \u4E2A\uFF09`);else{let o=c.env.MCP_ENDPOINT;o||(a.error("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\u4E14\u672A\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF"),a.error('\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF'),c.exit(1)),e=[o],a.info("\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684 MCP \u7AEF\u70B9\uFF08\u5EFA\u8BAE\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6\uFF09")}}catch(o){a.error(`\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`);let r=c.env.MCP_ENDPOINT;r||(a.error('\u8BF7\u8FD0\u884C "xiaozhi init" \u521D\u59CB\u5316\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E MCP_ENDPOINT \u73AF\u5883\u53D8\u91CF'),c.exit(1)),e=[r],a.info("\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684 MCP \u7AEF\u70B9\u4F5C\u4E3A\u5907\u7528\u65B9\u6848")}let t=e.filter(o=>!o||o.includes("<\u8BF7\u586B\u5199")?(a.warn(`\u8DF3\u8FC7\u65E0\u6548\u7AEF\u70B9: ${o}`),!1):!0);if(t.length===0){a.warn("\u6CA1\u6709\u6709\u6548\u7684 MCP \u7AEF\u70B9\uFF0C\u5C06\u8DF3\u8FC7\u5C0F\u667A\u670D\u52A1\u7AEF\u8FDE\u63A5"),a.info("MCP \u670D\u52A1\u5668\u529F\u80FD\u4ECD\u7136\u53EF\u7528\uFF0C\u53EF\u901A\u8FC7 Web \u754C\u9762\u914D\u7F6E\u7AEF\u70B9\u540E\u91CD\u542F\u670D\u52A1"),a.info('\u63D0\u793A: \u8BF7\u8FD0\u884C "xiaozhi config mcpEndpoint <your-endpoint-url>" \u8BBE\u7F6E\u7AEF\u70B9'),await K(i);return}a.info(t.length===1?"\u542F\u52A8\u5355\u7AEF\u70B9\u8FDE\u63A5":`\u542F\u52A8\u591A\u7AEF\u70B9\u8FDE\u63A5\uFF08${t.length} \u4E2A\u7AEF\u70B9\uFF09`);let n=new w(i,t);R(n);try{await n.start()}catch(o){a.error(`\u7A0B\u5E8F\u6267\u884C\u9519\u8BEF: ${o instanceof Error?o.message:String(o)}`),c.exit(1)}}f(U,"main");async function K(i){a.info("\u542F\u52A8 MCP \u670D\u52A1\u5668\u4EE3\u7406\uFF08\u65E0\u5C0F\u667A\u670D\u52A1\u7AEF\u8FDE\u63A5\uFF09");let{spawn:e}=await import("child_process"),t=e("node",[i],{stdio:["pipe","inherit","inherit"],env:{...c.env,XIAOZHI_CONFIG_DIR:c.env.XIAOZHI_CONFIG_DIR||c.cwd()}}),n=f(()=>{a.info("\u6B63\u5728\u5173\u95ED MCP \u670D\u52A1\u5668\u4EE3\u7406..."),t&&!t.killed&&(t.kill("SIGTERM"),setTimeout(()=>{t.killed||t.kill("SIGKILL")},5e3)),c.exit(0)},"cleanup");return c.on("SIGINT",n),c.on("SIGTERM",n),t.on("exit",(o,r)=>{a.warn(`MCP \u670D\u52A1\u5668\u4EE3\u7406\u5DF2\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${o}, \u4FE1\u53F7: ${r}`),r!=="SIGTERM"&&r!=="SIGKILL"&&(a.error("MCP \u670D\u52A1\u5668\u4EE3\u7406\u610F\u5916\u9000\u51FA"),c.exit(1))}),t.on("error",o=>{a.error(`MCP \u670D\u52A1\u5668\u4EE3\u7406\u9519\u8BEF: ${o.message}`),c.exit(1)}),a.info("MCP \u670D\u52A1\u5668\u4EE3\u7406\u5DF2\u542F\u52A8\uFF0C\u7B49\u5F85\u8FDE\u63A5..."),new Promise(()=>{})}f(K,"startMCPServerProxyOnly");var Y=import.meta.url,V=z(Y),q=c.argv[1];V===q&&U().catch(i=>{a.error(`\u672A\u5904\u7406\u7684\u9519\u8BEF: ${i instanceof Error?i.message:String(i)}`),c.exit(1)});export{U as main};
12
12
  //# sourceMappingURL=adaptiveMCPPipe.js.map