xiaozhi-client 1.6.0-beta.7 → 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 +114 -11
- package/dist/adaptiveMCPPipe.js +9 -9
- package/dist/adaptiveMCPPipe.js.map +1 -1
- package/dist/autoCompletion.js +2 -1
- package/dist/autoCompletion.js.map +1 -1
- package/dist/cli.js +11 -11
- package/dist/cli.js.map +1 -1
- package/dist/configManager.d.ts +9 -0
- package/dist/configManager.js +2 -1
- package/dist/configManager.js.map +1 -1
- package/dist/mcpCommands.js +2 -1
- package/dist/mcpCommands.js.map +1 -1
- package/dist/mcpServerProxy.d.ts +7 -0
- package/dist/mcpServerProxy.js +7 -7
- package/dist/mcpServerProxy.js.map +1 -1
- package/dist/modelScopeMCPClient.js +2 -2
- package/dist/modelScopeMCPClient.js.map +1 -1
- package/dist/multiEndpointMCPPipe.js +6 -6
- package/dist/multiEndpointMCPPipe.js.map +1 -1
- package/dist/package.json +1 -0
- package/dist/services/mcpServer.js +5 -5
- package/dist/services/mcpServer.js.map +1 -1
- package/dist/streamableHttpMCPClient.js +2 -2
- package/dist/streamableHttpMCPClient.js.map +1 -1
- package/dist/webServer.js +11 -11
- package/dist/webServer.js.map +1 -1
- package/package.json +2 -1
- package/web/dist/assets/{index-DkEK7Tiu.js → index-Jy5aLSeZ.js} +42 -42
- package/web/dist/assets/{index-DkEK7Tiu.js.map → index-Jy5aLSeZ.js.map} +1 -1
- package/web/dist/assets/index-o2NfdFal.css +1 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-Da0jgqOv.css +0 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://github.com/shenjingnan/xiaozhi-client/actions)
|
|
6
6
|
[](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="
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/dist/adaptiveMCPPipe.js
CHANGED
|
@@ -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.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,
|
|
5
|
-
`);t.stdoutBuffer=o.pop()||"";for(let
|
|
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=
|
|
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");
|
|
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(): ${
|
|
10
|
-
`),c.stderr.write(`[DEBUG] configManager.configExists(): ${
|
|
11
|
-
`)}catch{}if(
|
|
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
|