@xiaozhi-client/config 1.9.4-beta.5 → 1.9.4-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1783
- package/dist/index.js.map +1 -1
- package/package.json +12 -7
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -1,1784 +1,2 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
-
|
|
4
|
-
// src/manager.ts
|
|
5
|
-
import { copyFileSync, existsSync, readFileSync, writeFileSync } from "fs";
|
|
6
|
-
import { dirname, resolve } from "path";
|
|
7
|
-
import { fileURLToPath } from "url";
|
|
8
|
-
import * as commentJson2 from "comment-json";
|
|
9
|
-
import dayjs from "dayjs";
|
|
10
|
-
|
|
11
|
-
// src/json5-adapter.ts
|
|
12
|
-
import * as commentJson from "comment-json";
|
|
13
|
-
function createJson5Writer(content) {
|
|
14
|
-
const parsedData = commentJson.parse(content);
|
|
15
|
-
return {
|
|
16
|
-
write(data) {
|
|
17
|
-
if (parsedData && typeof parsedData === "object" && data) {
|
|
18
|
-
Object.assign(parsedData, data);
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
toSource() {
|
|
22
|
-
return commentJson.stringify(parsedData, null, 2);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
__name(createJson5Writer, "createJson5Writer");
|
|
27
|
-
function parseJson5(content) {
|
|
28
|
-
return commentJson.parse(content);
|
|
29
|
-
}
|
|
30
|
-
__name(parseJson5, "parseJson5");
|
|
31
|
-
|
|
32
|
-
// src/manager.ts
|
|
33
|
-
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
34
|
-
var DEFAULT_CONNECTION_CONFIG = {
|
|
35
|
-
heartbeatInterval: 3e4,
|
|
36
|
-
// 30秒心跳间隔
|
|
37
|
-
heartbeatTimeout: 1e4,
|
|
38
|
-
// 10秒心跳超时
|
|
39
|
-
reconnectInterval: 5e3
|
|
40
|
-
// 5秒重连间隔
|
|
41
|
-
};
|
|
42
|
-
var ConfigManager = class _ConfigManager {
|
|
43
|
-
static {
|
|
44
|
-
__name(this, "ConfigManager");
|
|
45
|
-
}
|
|
46
|
-
static instance;
|
|
47
|
-
defaultConfigPath;
|
|
48
|
-
config = null;
|
|
49
|
-
currentConfigPath = null;
|
|
50
|
-
// 跟踪当前使用的配置文件路径
|
|
51
|
-
json5Writer = null;
|
|
52
|
-
// json5-writer 实例,用于保留 JSON5 注释
|
|
53
|
-
// 统计更新并发控制
|
|
54
|
-
statsUpdateLocks = /* @__PURE__ */ new Map();
|
|
55
|
-
statsUpdateLockTimeouts = /* @__PURE__ */ new Map();
|
|
56
|
-
STATS_UPDATE_TIMEOUT = 5e3;
|
|
57
|
-
// 5秒超时
|
|
58
|
-
// 事件回调(用于解耦 EventBus 依赖)
|
|
59
|
-
eventCallbacks = /* @__PURE__ */ new Map();
|
|
60
|
-
constructor() {
|
|
61
|
-
const possiblePaths = [
|
|
62
|
-
// 构建后的环境:dist/configManager.js -> dist/templates/default/xiaozhi.config.json
|
|
63
|
-
resolve(__dirname, "templates", "default", "xiaozhi.config.json"),
|
|
64
|
-
// 开发环境:src/configManager.ts -> templates/default/xiaozhi.config.json
|
|
65
|
-
resolve(__dirname, "..", "templates", "default", "xiaozhi.config.json"),
|
|
66
|
-
// 测试环境或其他情况
|
|
67
|
-
resolve(process.cwd(), "templates", "default", "xiaozhi.config.json")
|
|
68
|
-
];
|
|
69
|
-
this.defaultConfigPath = possiblePaths.find((path) => existsSync(path)) || possiblePaths[0];
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* 注册事件监听器
|
|
73
|
-
*/
|
|
74
|
-
on(eventName, callback) {
|
|
75
|
-
if (!this.eventCallbacks.has(eventName)) {
|
|
76
|
-
this.eventCallbacks.set(eventName, []);
|
|
77
|
-
}
|
|
78
|
-
this.eventCallbacks.get(eventName)?.push(callback);
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* 发射事件
|
|
82
|
-
*/
|
|
83
|
-
emitEvent(eventName, data) {
|
|
84
|
-
const callbacks = this.eventCallbacks.get(eventName);
|
|
85
|
-
if (callbacks) {
|
|
86
|
-
for (const callback of callbacks) {
|
|
87
|
-
try {
|
|
88
|
-
callback(data);
|
|
89
|
-
} catch (error) {
|
|
90
|
-
console.error(`\u4E8B\u4EF6\u56DE\u8C03\u6267\u884C\u5931\u8D25 [${eventName}]:`, error);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* 获取配置文件路径(动态计算)
|
|
97
|
-
* 支持多种配置文件格式:json5 > jsonc > json
|
|
98
|
-
*/
|
|
99
|
-
getConfigFilePath() {
|
|
100
|
-
const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();
|
|
101
|
-
const configFileNames = [
|
|
102
|
-
"xiaozhi.config.json5",
|
|
103
|
-
"xiaozhi.config.jsonc",
|
|
104
|
-
"xiaozhi.config.json"
|
|
105
|
-
];
|
|
106
|
-
for (const fileName of configFileNames) {
|
|
107
|
-
const filePath = resolve(configDir, fileName);
|
|
108
|
-
if (existsSync(filePath)) {
|
|
109
|
-
return filePath;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return resolve(configDir, "xiaozhi.config.json");
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* 获取配置文件格式
|
|
116
|
-
*/
|
|
117
|
-
getConfigFileFormat(filePath) {
|
|
118
|
-
if (filePath.endsWith(".json5")) {
|
|
119
|
-
return "json5";
|
|
120
|
-
}
|
|
121
|
-
if (filePath.endsWith(".jsonc")) {
|
|
122
|
-
return "jsonc";
|
|
123
|
-
}
|
|
124
|
-
return "json";
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* 获取配置管理器单例实例
|
|
128
|
-
*/
|
|
129
|
-
static getInstance() {
|
|
130
|
-
if (!_ConfigManager.instance) {
|
|
131
|
-
_ConfigManager.instance = new _ConfigManager();
|
|
132
|
-
}
|
|
133
|
-
return _ConfigManager.instance;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* 检查配置文件是否存在
|
|
137
|
-
*/
|
|
138
|
-
configExists() {
|
|
139
|
-
const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();
|
|
140
|
-
const configFileNames = [
|
|
141
|
-
"xiaozhi.config.json5",
|
|
142
|
-
"xiaozhi.config.jsonc",
|
|
143
|
-
"xiaozhi.config.json"
|
|
144
|
-
];
|
|
145
|
-
for (const fileName of configFileNames) {
|
|
146
|
-
const filePath = resolve(configDir, fileName);
|
|
147
|
-
if (existsSync(filePath)) {
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* 初始化配置文件
|
|
155
|
-
* 从 config.default.json 复制到 config.json
|
|
156
|
-
* @param format 配置文件格式,默认为 json
|
|
157
|
-
*/
|
|
158
|
-
initConfig(format = "json") {
|
|
159
|
-
if (!existsSync(this.defaultConfigPath)) {
|
|
160
|
-
throw new Error(`\u9ED8\u8BA4\u914D\u7F6E\u6A21\u677F\u6587\u4EF6\u4E0D\u5B58\u5728: ${this.defaultConfigPath}`);
|
|
161
|
-
}
|
|
162
|
-
if (this.configExists()) {
|
|
163
|
-
throw new Error("\u914D\u7F6E\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u91CD\u590D\u521D\u59CB\u5316");
|
|
164
|
-
}
|
|
165
|
-
const configDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();
|
|
166
|
-
const targetFileName = `xiaozhi.config.${format}`;
|
|
167
|
-
const configPath = resolve(configDir, targetFileName);
|
|
168
|
-
copyFileSync(this.defaultConfigPath, configPath);
|
|
169
|
-
this.config = null;
|
|
170
|
-
this.json5Writer = null;
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* 加载配置文件
|
|
174
|
-
*/
|
|
175
|
-
loadConfig() {
|
|
176
|
-
if (!this.configExists()) {
|
|
177
|
-
const error = new Error(
|
|
178
|
-
"\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u8FD0\u884C xiaozhi init \u521D\u59CB\u5316\u914D\u7F6E"
|
|
179
|
-
);
|
|
180
|
-
this.emitEvent("config:error", {
|
|
181
|
-
error,
|
|
182
|
-
operation: "loadConfig"
|
|
183
|
-
});
|
|
184
|
-
throw error;
|
|
185
|
-
}
|
|
186
|
-
try {
|
|
187
|
-
const configPath = this.getConfigFilePath();
|
|
188
|
-
this.currentConfigPath = configPath;
|
|
189
|
-
const configFileFormat = this.getConfigFileFormat(configPath);
|
|
190
|
-
const rawConfigData = readFileSync(configPath, "utf8");
|
|
191
|
-
const configData = rawConfigData.replace(/^\uFEFF/, "");
|
|
192
|
-
let config;
|
|
193
|
-
switch (configFileFormat) {
|
|
194
|
-
case "json5":
|
|
195
|
-
config = parseJson5(configData);
|
|
196
|
-
this.json5Writer = createJson5Writer(configData);
|
|
197
|
-
break;
|
|
198
|
-
case "jsonc":
|
|
199
|
-
config = commentJson2.parse(configData);
|
|
200
|
-
break;
|
|
201
|
-
default:
|
|
202
|
-
config = JSON.parse(configData);
|
|
203
|
-
break;
|
|
204
|
-
}
|
|
205
|
-
this.validateConfig(config);
|
|
206
|
-
return config;
|
|
207
|
-
} catch (error) {
|
|
208
|
-
this.emitEvent("config:error", {
|
|
209
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
210
|
-
operation: "loadConfig"
|
|
211
|
-
});
|
|
212
|
-
if (error instanceof SyntaxError) {
|
|
213
|
-
throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF: ${error.message}`);
|
|
214
|
-
}
|
|
215
|
-
throw error;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* 验证配置文件结构
|
|
220
|
-
*/
|
|
221
|
-
validateConfig(config) {
|
|
222
|
-
if (!config || typeof config !== "object") {
|
|
223
|
-
throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1A\u6839\u5BF9\u8C61\u65E0\u6548");
|
|
224
|
-
}
|
|
225
|
-
const configObj = config;
|
|
226
|
-
if (configObj.mcpEndpoint === void 0 || configObj.mcpEndpoint === null) {
|
|
227
|
-
throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5B57\u6BB5\u65E0\u6548");
|
|
228
|
-
}
|
|
229
|
-
if (typeof configObj.mcpEndpoint === "string") {
|
|
230
|
-
} else if (Array.isArray(configObj.mcpEndpoint)) {
|
|
231
|
-
for (const endpoint of configObj.mcpEndpoint) {
|
|
232
|
-
if (typeof endpoint !== "string" || endpoint.trim() === "") {
|
|
233
|
-
throw new Error(
|
|
234
|
-
"\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"
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
} else {
|
|
239
|
-
throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6216\u5B57\u7B26\u4E32\u6570\u7EC4");
|
|
240
|
-
}
|
|
241
|
-
if (!configObj.mcpServers || typeof configObj.mcpServers !== "object") {
|
|
242
|
-
throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers \u5B57\u6BB5\u65E0\u6548");
|
|
243
|
-
}
|
|
244
|
-
for (const [serverName, serverConfig] of Object.entries(
|
|
245
|
-
configObj.mcpServers
|
|
246
|
-
)) {
|
|
247
|
-
if (!serverConfig || typeof serverConfig !== "object") {
|
|
248
|
-
throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${serverName} \u65E0\u6548`);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* 获取配置(只读)
|
|
254
|
-
*/
|
|
255
|
-
getConfig() {
|
|
256
|
-
this.config = this.loadConfig();
|
|
257
|
-
return JSON.parse(JSON.stringify(this.config));
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* 获取可修改的配置对象(内部使用,保留注释信息)
|
|
261
|
-
*/
|
|
262
|
-
getMutableConfig() {
|
|
263
|
-
if (!this.config) {
|
|
264
|
-
this.config = this.loadConfig();
|
|
265
|
-
}
|
|
266
|
-
return this.config;
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* 获取 MCP 端点(向后兼容)
|
|
270
|
-
* @deprecated 使用 getMcpEndpoints() 获取所有端点
|
|
271
|
-
*/
|
|
272
|
-
getMcpEndpoint() {
|
|
273
|
-
const config = this.getConfig();
|
|
274
|
-
if (Array.isArray(config.mcpEndpoint)) {
|
|
275
|
-
return config.mcpEndpoint[0] || "";
|
|
276
|
-
}
|
|
277
|
-
return config.mcpEndpoint;
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* 获取所有 MCP 端点
|
|
281
|
-
*/
|
|
282
|
-
getMcpEndpoints() {
|
|
283
|
-
const config = this.getConfig();
|
|
284
|
-
if (Array.isArray(config.mcpEndpoint)) {
|
|
285
|
-
return [...config.mcpEndpoint];
|
|
286
|
-
}
|
|
287
|
-
return config.mcpEndpoint ? [config.mcpEndpoint] : [];
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* 获取 MCP 服务配置
|
|
291
|
-
*/
|
|
292
|
-
getMcpServers() {
|
|
293
|
-
const config = this.getConfig();
|
|
294
|
-
return config.mcpServers;
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* 获取 MCP 服务工具配置
|
|
298
|
-
*/
|
|
299
|
-
getMcpServerConfig() {
|
|
300
|
-
const config = this.getConfig();
|
|
301
|
-
return config.mcpServerConfig || {};
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* 获取指定服务的工具配置
|
|
305
|
-
*/
|
|
306
|
-
getServerToolsConfig(serverName) {
|
|
307
|
-
const serverConfig = this.getMcpServerConfig();
|
|
308
|
-
return serverConfig[serverName]?.tools || {};
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* 检查工具是否启用
|
|
312
|
-
*/
|
|
313
|
-
isToolEnabled(serverName, toolName) {
|
|
314
|
-
const toolsConfig = this.getServerToolsConfig(serverName);
|
|
315
|
-
const toolConfig = toolsConfig[toolName];
|
|
316
|
-
return toolConfig?.enable !== false;
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* 更新 MCP 端点(支持字符串或数组)
|
|
320
|
-
*/
|
|
321
|
-
updateMcpEndpoint(endpoint) {
|
|
322
|
-
if (Array.isArray(endpoint)) {
|
|
323
|
-
for (const ep of endpoint) {
|
|
324
|
-
if (!ep || typeof ep !== "string") {
|
|
325
|
-
throw new Error("MCP \u7AEF\u70B9\u6570\u7EC4\u4E2D\u7684\u6BCF\u4E2A\u5143\u7D20\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
const config = this.getMutableConfig();
|
|
330
|
-
config.mcpEndpoint = endpoint;
|
|
331
|
-
this.saveConfig(config);
|
|
332
|
-
this.emitEvent("config:updated", {
|
|
333
|
-
type: "endpoint",
|
|
334
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* 添加 MCP 端点
|
|
339
|
-
*/
|
|
340
|
-
addMcpEndpoint(endpoint) {
|
|
341
|
-
if (!endpoint || typeof endpoint !== "string") {
|
|
342
|
-
throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
343
|
-
}
|
|
344
|
-
const config = this.getMutableConfig();
|
|
345
|
-
const currentEndpoints = this.getMcpEndpoints();
|
|
346
|
-
if (currentEndpoints.includes(endpoint)) {
|
|
347
|
-
throw new Error(`MCP \u7AEF\u70B9 ${endpoint} \u5DF2\u5B58\u5728`);
|
|
348
|
-
}
|
|
349
|
-
const newEndpoints = [...currentEndpoints, endpoint];
|
|
350
|
-
config.mcpEndpoint = newEndpoints;
|
|
351
|
-
this.saveConfig(config);
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* 移除 MCP 端点
|
|
355
|
-
*/
|
|
356
|
-
removeMcpEndpoint(endpoint) {
|
|
357
|
-
if (!endpoint || typeof endpoint !== "string") {
|
|
358
|
-
throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
359
|
-
}
|
|
360
|
-
const config = this.getMutableConfig();
|
|
361
|
-
const currentEndpoints = this.getMcpEndpoints();
|
|
362
|
-
const index = currentEndpoints.indexOf(endpoint);
|
|
363
|
-
if (index === -1) {
|
|
364
|
-
throw new Error(`MCP \u7AEF\u70B9 ${endpoint} \u4E0D\u5B58\u5728`);
|
|
365
|
-
}
|
|
366
|
-
const newEndpoints = currentEndpoints.filter((ep) => ep !== endpoint);
|
|
367
|
-
config.mcpEndpoint = newEndpoints;
|
|
368
|
-
this.saveConfig(config);
|
|
369
|
-
}
|
|
370
|
-
/**
|
|
371
|
-
* 更新 MCP 服务配置
|
|
372
|
-
*/
|
|
373
|
-
updateMcpServer(serverName, serverConfig) {
|
|
374
|
-
if (!serverName || typeof serverName !== "string") {
|
|
375
|
-
throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
376
|
-
}
|
|
377
|
-
const config = this.getMutableConfig();
|
|
378
|
-
config.mcpServers[serverName] = serverConfig;
|
|
379
|
-
this.saveConfig(config);
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* 删除 MCP 服务配置
|
|
383
|
-
*/
|
|
384
|
-
removeMcpServer(serverName) {
|
|
385
|
-
if (!serverName || typeof serverName !== "string") {
|
|
386
|
-
throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
387
|
-
}
|
|
388
|
-
const config = this.getMutableConfig();
|
|
389
|
-
if (!config.mcpServers[serverName]) {
|
|
390
|
-
throw new Error(`\u670D\u52A1 ${serverName} \u4E0D\u5B58\u5728`);
|
|
391
|
-
}
|
|
392
|
-
delete config.mcpServers[serverName];
|
|
393
|
-
if (config.mcpServerConfig?.[serverName]) {
|
|
394
|
-
delete config.mcpServerConfig[serverName];
|
|
395
|
-
}
|
|
396
|
-
if (config.customMCP?.tools) {
|
|
397
|
-
const relatedTools = config.customMCP.tools.filter(
|
|
398
|
-
(tool) => tool.handler?.type === "mcp" && tool.handler.config?.serviceName === serverName
|
|
399
|
-
);
|
|
400
|
-
for (const tool of relatedTools) {
|
|
401
|
-
const toolIndex = config.customMCP.tools.findIndex(
|
|
402
|
-
(t) => t.name === tool.name
|
|
403
|
-
);
|
|
404
|
-
if (toolIndex !== -1) {
|
|
405
|
-
config.customMCP.tools.splice(toolIndex, 1);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
if (config.customMCP.tools.length === 0) {
|
|
409
|
-
config.customMCP = void 0;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
this.saveConfig(config);
|
|
413
|
-
this.emitEvent("config:updated", {
|
|
414
|
-
type: "customMCP",
|
|
415
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
416
|
-
});
|
|
417
|
-
console.log("\u6210\u529F\u79FB\u9664 MCP \u670D\u52A1", { serverName });
|
|
418
|
-
}
|
|
419
|
-
/**
|
|
420
|
-
* 批量更新配置(由 Handler 调用)
|
|
421
|
-
*/
|
|
422
|
-
updateConfig(newConfig) {
|
|
423
|
-
const config = this.getMutableConfig();
|
|
424
|
-
if (newConfig.mcpEndpoint !== void 0) {
|
|
425
|
-
config.mcpEndpoint = newConfig.mcpEndpoint;
|
|
426
|
-
}
|
|
427
|
-
if (newConfig.mcpServers) {
|
|
428
|
-
const currentServers = { ...config.mcpServers };
|
|
429
|
-
for (const [name, serverConfig] of Object.entries(newConfig.mcpServers)) {
|
|
430
|
-
config.mcpServers[name] = serverConfig;
|
|
431
|
-
}
|
|
432
|
-
for (const name of Object.keys(currentServers)) {
|
|
433
|
-
if (!(name in newConfig.mcpServers)) {
|
|
434
|
-
delete config.mcpServers[name];
|
|
435
|
-
if (config.mcpServerConfig?.[name]) {
|
|
436
|
-
delete config.mcpServerConfig[name];
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
if (newConfig.connection) {
|
|
442
|
-
if (!config.connection) {
|
|
443
|
-
config.connection = {};
|
|
444
|
-
}
|
|
445
|
-
Object.assign(config.connection, newConfig.connection);
|
|
446
|
-
}
|
|
447
|
-
if (newConfig.modelscope) {
|
|
448
|
-
if (!config.modelscope) {
|
|
449
|
-
config.modelscope = {};
|
|
450
|
-
}
|
|
451
|
-
Object.assign(config.modelscope, newConfig.modelscope);
|
|
452
|
-
}
|
|
453
|
-
if (newConfig.webUI) {
|
|
454
|
-
if (!config.webUI) {
|
|
455
|
-
config.webUI = {};
|
|
456
|
-
}
|
|
457
|
-
Object.assign(config.webUI, newConfig.webUI);
|
|
458
|
-
}
|
|
459
|
-
if (newConfig.mcpServerConfig) {
|
|
460
|
-
for (const [serverName, toolsConfig] of Object.entries(
|
|
461
|
-
newConfig.mcpServerConfig
|
|
462
|
-
)) {
|
|
463
|
-
if (config.mcpServerConfig?.[serverName]) {
|
|
464
|
-
config.mcpServerConfig[serverName] = toolsConfig;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
if (newConfig.platforms) {
|
|
469
|
-
for (const [platformName, platformConfig] of Object.entries(
|
|
470
|
-
newConfig.platforms
|
|
471
|
-
)) {
|
|
472
|
-
if (!config.platforms) {
|
|
473
|
-
config.platforms = {};
|
|
474
|
-
}
|
|
475
|
-
config.platforms[platformName] = platformConfig;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
this.saveConfig(config);
|
|
479
|
-
this.emitEvent("config:updated", {
|
|
480
|
-
type: "config",
|
|
481
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* 更新服务工具配置
|
|
486
|
-
*/
|
|
487
|
-
updateServerToolsConfig(serverName, toolsConfig) {
|
|
488
|
-
const config = this.getMutableConfig();
|
|
489
|
-
if (!config.mcpServerConfig) {
|
|
490
|
-
config.mcpServerConfig = {};
|
|
491
|
-
}
|
|
492
|
-
if (Object.keys(toolsConfig).length === 0) {
|
|
493
|
-
delete config.mcpServerConfig[serverName];
|
|
494
|
-
} else {
|
|
495
|
-
config.mcpServerConfig[serverName] = {
|
|
496
|
-
tools: toolsConfig
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
this.saveConfig(config);
|
|
500
|
-
this.emitEvent("config:updated", {
|
|
501
|
-
type: "serverTools",
|
|
502
|
-
serviceName: serverName,
|
|
503
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
/**
|
|
507
|
-
* 删除指定服务器的工具配置
|
|
508
|
-
*/
|
|
509
|
-
removeServerToolsConfig(serverName) {
|
|
510
|
-
const config = this.getConfig();
|
|
511
|
-
const newConfig = { ...config };
|
|
512
|
-
if (newConfig.mcpServerConfig) {
|
|
513
|
-
delete newConfig.mcpServerConfig[serverName];
|
|
514
|
-
this.saveConfig(newConfig);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* 清理无效的服务器工具配置
|
|
519
|
-
* 删除在 mcpServerConfig 中存在但在 mcpServers 中不存在的服务配置
|
|
520
|
-
*/
|
|
521
|
-
cleanupInvalidServerToolsConfig() {
|
|
522
|
-
const config = this.getMutableConfig();
|
|
523
|
-
if (!config.mcpServerConfig) {
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
const validServerNames = Object.keys(config.mcpServers);
|
|
527
|
-
const configuredServerNames = Object.keys(config.mcpServerConfig);
|
|
528
|
-
const invalidServerNames = configuredServerNames.filter(
|
|
529
|
-
(serverName) => !validServerNames.includes(serverName)
|
|
530
|
-
);
|
|
531
|
-
if (invalidServerNames.length > 0) {
|
|
532
|
-
for (const serverName of invalidServerNames) {
|
|
533
|
-
delete config.mcpServerConfig[serverName];
|
|
534
|
-
}
|
|
535
|
-
this.saveConfig(config);
|
|
536
|
-
console.log("\u5DF2\u6E05\u7406\u65E0\u6548\u7684\u670D\u52A1\u5DE5\u5177\u914D\u7F6E", {
|
|
537
|
-
count: invalidServerNames.length,
|
|
538
|
-
serverNames: invalidServerNames
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
/**
|
|
543
|
-
* 设置工具启用状态
|
|
544
|
-
*/
|
|
545
|
-
setToolEnabled(serverName, toolName, enabled, description) {
|
|
546
|
-
const config = this.getMutableConfig();
|
|
547
|
-
if (!config.mcpServerConfig) {
|
|
548
|
-
config.mcpServerConfig = {};
|
|
549
|
-
}
|
|
550
|
-
if (!config.mcpServerConfig[serverName]) {
|
|
551
|
-
config.mcpServerConfig[serverName] = { tools: {} };
|
|
552
|
-
}
|
|
553
|
-
config.mcpServerConfig[serverName].tools[toolName] = {
|
|
554
|
-
...config.mcpServerConfig[serverName].tools[toolName],
|
|
555
|
-
enable: enabled,
|
|
556
|
-
...description && { description }
|
|
557
|
-
};
|
|
558
|
-
this.saveConfig(config);
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
561
|
-
* 保存配置到文件
|
|
562
|
-
* 保存到原始配置文件路径,保持文件格式一致性
|
|
563
|
-
*/
|
|
564
|
-
saveConfig(config) {
|
|
565
|
-
try {
|
|
566
|
-
this.validateConfig(config);
|
|
567
|
-
let configPath;
|
|
568
|
-
if (this.currentConfigPath) {
|
|
569
|
-
configPath = this.currentConfigPath;
|
|
570
|
-
} else {
|
|
571
|
-
configPath = this.getConfigFilePath();
|
|
572
|
-
this.currentConfigPath = configPath;
|
|
573
|
-
}
|
|
574
|
-
const configFileFormat = this.getConfigFileFormat(configPath);
|
|
575
|
-
let configContent;
|
|
576
|
-
switch (configFileFormat) {
|
|
577
|
-
case "json5":
|
|
578
|
-
try {
|
|
579
|
-
if (this.json5Writer) {
|
|
580
|
-
this.json5Writer.write(config);
|
|
581
|
-
configContent = this.json5Writer.toSource();
|
|
582
|
-
} else {
|
|
583
|
-
console.warn("\u6CA1\u6709 JSON5 \u9002\u914D\u5668\u5B9E\u4F8B\uFF0C\u4F7F\u7528 comment-json \u5E8F\u5217\u5316");
|
|
584
|
-
configContent = commentJson2.stringify(config, null, 2);
|
|
585
|
-
}
|
|
586
|
-
} catch (json5Error) {
|
|
587
|
-
console.warn(
|
|
588
|
-
"\u4F7F\u7528 JSON5 \u9002\u914D\u5668\u4FDD\u5B58\u5931\u8D25\uFF0C\u56DE\u9000\u5230 comment-json \u5E8F\u5217\u5316:",
|
|
589
|
-
json5Error
|
|
590
|
-
);
|
|
591
|
-
configContent = commentJson2.stringify(config, null, 2);
|
|
592
|
-
}
|
|
593
|
-
break;
|
|
594
|
-
case "jsonc":
|
|
595
|
-
try {
|
|
596
|
-
configContent = commentJson2.stringify(config, null, 2);
|
|
597
|
-
} catch (commentJsonError) {
|
|
598
|
-
console.warn(
|
|
599
|
-
"\u4F7F\u7528 comment-json \u4FDD\u5B58\u5931\u8D25\uFF0C\u56DE\u9000\u5230\u6807\u51C6 JSON \u683C\u5F0F:",
|
|
600
|
-
commentJsonError
|
|
601
|
-
);
|
|
602
|
-
configContent = JSON.stringify(config, null, 2);
|
|
603
|
-
}
|
|
604
|
-
break;
|
|
605
|
-
default:
|
|
606
|
-
configContent = JSON.stringify(config, null, 2);
|
|
607
|
-
break;
|
|
608
|
-
}
|
|
609
|
-
writeFileSync(configPath, configContent, "utf8");
|
|
610
|
-
this.config = config;
|
|
611
|
-
console.log("\u914D\u7F6E\u4FDD\u5B58\u6210\u529F");
|
|
612
|
-
this.notifyConfigUpdate(config);
|
|
613
|
-
} catch (error) {
|
|
614
|
-
this.emitEvent("config:error", {
|
|
615
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
616
|
-
operation: "saveConfig"
|
|
617
|
-
});
|
|
618
|
-
throw new Error(
|
|
619
|
-
`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
|
|
620
|
-
);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
/**
|
|
624
|
-
* 重新加载配置(清除缓存)
|
|
625
|
-
*/
|
|
626
|
-
reloadConfig() {
|
|
627
|
-
this.config = null;
|
|
628
|
-
this.currentConfigPath = null;
|
|
629
|
-
this.json5Writer = null;
|
|
630
|
-
}
|
|
631
|
-
/**
|
|
632
|
-
* 获取配置文件路径
|
|
633
|
-
*/
|
|
634
|
-
getConfigPath() {
|
|
635
|
-
return this.getConfigFilePath();
|
|
636
|
-
}
|
|
637
|
-
/**
|
|
638
|
-
* 获取默认配置文件路径
|
|
639
|
-
*/
|
|
640
|
-
getDefaultConfigPath() {
|
|
641
|
-
return this.defaultConfigPath;
|
|
642
|
-
}
|
|
643
|
-
/**
|
|
644
|
-
* 获取连接配置(包含默认值)
|
|
645
|
-
*/
|
|
646
|
-
getConnectionConfig() {
|
|
647
|
-
const config = this.getConfig();
|
|
648
|
-
const connectionConfig = config.connection || {};
|
|
649
|
-
return {
|
|
650
|
-
heartbeatInterval: connectionConfig.heartbeatInterval ?? DEFAULT_CONNECTION_CONFIG.heartbeatInterval,
|
|
651
|
-
heartbeatTimeout: connectionConfig.heartbeatTimeout ?? DEFAULT_CONNECTION_CONFIG.heartbeatTimeout,
|
|
652
|
-
reconnectInterval: connectionConfig.reconnectInterval ?? DEFAULT_CONNECTION_CONFIG.reconnectInterval
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* 获取心跳检测间隔(毫秒)
|
|
657
|
-
*/
|
|
658
|
-
getHeartbeatInterval() {
|
|
659
|
-
return this.getConnectionConfig().heartbeatInterval;
|
|
660
|
-
}
|
|
661
|
-
/**
|
|
662
|
-
* 获取心跳超时时间(毫秒)
|
|
663
|
-
*/
|
|
664
|
-
getHeartbeatTimeout() {
|
|
665
|
-
return this.getConnectionConfig().heartbeatTimeout;
|
|
666
|
-
}
|
|
667
|
-
/**
|
|
668
|
-
* 获取重连间隔(毫秒)
|
|
669
|
-
*/
|
|
670
|
-
getReconnectInterval() {
|
|
671
|
-
return this.getConnectionConfig().reconnectInterval;
|
|
672
|
-
}
|
|
673
|
-
/**
|
|
674
|
-
* 更新连接配置
|
|
675
|
-
*/
|
|
676
|
-
updateConnectionConfig(connectionConfig) {
|
|
677
|
-
const config = this.getMutableConfig();
|
|
678
|
-
if (!config.connection) {
|
|
679
|
-
config.connection = {};
|
|
680
|
-
}
|
|
681
|
-
Object.assign(config.connection, connectionConfig);
|
|
682
|
-
this.saveConfig(config);
|
|
683
|
-
this.emitEvent("config:updated", {
|
|
684
|
-
type: "connection",
|
|
685
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
/**
|
|
689
|
-
* 更新工具使用统计信息的实现
|
|
690
|
-
*/
|
|
691
|
-
async updateToolUsageStats(arg1, arg2, arg3) {
|
|
692
|
-
try {
|
|
693
|
-
if (typeof arg2 === "string" && arg3) {
|
|
694
|
-
const serverName = arg1;
|
|
695
|
-
const toolName = arg2;
|
|
696
|
-
const callTime = arg3;
|
|
697
|
-
await Promise.all([
|
|
698
|
-
this._updateMCPServerToolStats(serverName, toolName, callTime),
|
|
699
|
-
this.updateCustomMCPToolStats(serverName, toolName, callTime)
|
|
700
|
-
]);
|
|
701
|
-
console.log("\u5DE5\u5177\u4F7F\u7528\u7EDF\u8BA1\u5DF2\u66F4\u65B0", { serverName, toolName });
|
|
702
|
-
} else {
|
|
703
|
-
const toolName = arg1;
|
|
704
|
-
const incrementUsageCount = arg2;
|
|
705
|
-
const callTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
706
|
-
await this.updateCustomMCPToolStats(
|
|
707
|
-
toolName,
|
|
708
|
-
callTime,
|
|
709
|
-
incrementUsageCount
|
|
710
|
-
);
|
|
711
|
-
console.log("CustomMCP \u5DE5\u5177\u4F7F\u7528\u7EDF\u8BA1\u5DF2\u66F4\u65B0", { toolName });
|
|
712
|
-
}
|
|
713
|
-
} catch (error) {
|
|
714
|
-
if (typeof arg2 === "string" && arg3) {
|
|
715
|
-
const serverName = arg1;
|
|
716
|
-
const toolName = arg2;
|
|
717
|
-
console.error("\u66F4\u65B0\u5DE5\u5177\u4F7F\u7528\u7EDF\u8BA1\u5931\u8D25", { serverName, toolName, error });
|
|
718
|
-
} else {
|
|
719
|
-
const toolName = arg1;
|
|
720
|
-
console.error("\u66F4\u65B0 CustomMCP \u5DE5\u5177\u4F7F\u7528\u7EDF\u8BA1\u5931\u8D25", { toolName, error });
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
/**
|
|
725
|
-
* 更新 MCP 服务工具统计信息(重载方法)
|
|
726
|
-
* @param serviceName 服务名称
|
|
727
|
-
* @param toolName 工具名称
|
|
728
|
-
* @param callTime 调用时间(ISO 8601 格式)
|
|
729
|
-
* @param incrementUsageCount 是否增加使用计数,默认为 true
|
|
730
|
-
*/
|
|
731
|
-
async updateMCPServerToolStats(serviceName, toolName, callTime, incrementUsageCount = true) {
|
|
732
|
-
await this._updateMCPServerToolStats(
|
|
733
|
-
serviceName,
|
|
734
|
-
toolName,
|
|
735
|
-
callTime,
|
|
736
|
-
incrementUsageCount
|
|
737
|
-
);
|
|
738
|
-
}
|
|
739
|
-
/**
|
|
740
|
-
* 设置心跳检测间隔
|
|
741
|
-
*/
|
|
742
|
-
setHeartbeatInterval(interval) {
|
|
743
|
-
if (interval <= 0) {
|
|
744
|
-
throw new Error("\u5FC3\u8DF3\u68C0\u6D4B\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");
|
|
745
|
-
}
|
|
746
|
-
this.updateConnectionConfig({ heartbeatInterval: interval });
|
|
747
|
-
}
|
|
748
|
-
/**
|
|
749
|
-
* 设置心跳超时时间
|
|
750
|
-
*/
|
|
751
|
-
setHeartbeatTimeout(timeout) {
|
|
752
|
-
if (timeout <= 0) {
|
|
753
|
-
throw new Error("\u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4\u5FC5\u987B\u5927\u4E8E0");
|
|
754
|
-
}
|
|
755
|
-
this.updateConnectionConfig({ heartbeatTimeout: timeout });
|
|
756
|
-
}
|
|
757
|
-
/**
|
|
758
|
-
* 设置重连间隔
|
|
759
|
-
*/
|
|
760
|
-
setReconnectInterval(interval) {
|
|
761
|
-
if (interval <= 0) {
|
|
762
|
-
throw new Error("\u91CD\u8FDE\u95F4\u9694\u5FC5\u987B\u5927\u4E8E0");
|
|
763
|
-
}
|
|
764
|
-
this.updateConnectionConfig({ reconnectInterval: interval });
|
|
765
|
-
}
|
|
766
|
-
/**
|
|
767
|
-
* 获取 ModelScope 配置
|
|
768
|
-
*/
|
|
769
|
-
getModelScopeConfig() {
|
|
770
|
-
const config = this.getConfig();
|
|
771
|
-
return config.modelscope || {};
|
|
772
|
-
}
|
|
773
|
-
/**
|
|
774
|
-
* 获取 ModelScope API Key
|
|
775
|
-
* 优先从配置文件读取,其次从环境变量读取
|
|
776
|
-
*/
|
|
777
|
-
getModelScopeApiKey() {
|
|
778
|
-
const modelScopeConfig = this.getModelScopeConfig();
|
|
779
|
-
return modelScopeConfig.apiKey || process.env.MODELSCOPE_API_TOKEN;
|
|
780
|
-
}
|
|
781
|
-
/**
|
|
782
|
-
* 更新 ModelScope 配置
|
|
783
|
-
*/
|
|
784
|
-
updateModelScopeConfig(modelScopeConfig) {
|
|
785
|
-
const config = this.getMutableConfig();
|
|
786
|
-
if (!config.modelscope) {
|
|
787
|
-
config.modelscope = {};
|
|
788
|
-
}
|
|
789
|
-
Object.assign(config.modelscope, modelScopeConfig);
|
|
790
|
-
this.saveConfig(config);
|
|
791
|
-
this.emitEvent("config:updated", {
|
|
792
|
-
type: "modelscope",
|
|
793
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
794
|
-
});
|
|
795
|
-
}
|
|
796
|
-
/**
|
|
797
|
-
* 设置 ModelScope API Key
|
|
798
|
-
*/
|
|
799
|
-
setModelScopeApiKey(apiKey) {
|
|
800
|
-
if (!apiKey || typeof apiKey !== "string") {
|
|
801
|
-
throw new Error("API Key \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
802
|
-
}
|
|
803
|
-
this.updateModelScopeConfig({ apiKey });
|
|
804
|
-
}
|
|
805
|
-
/**
|
|
806
|
-
* 获取 customMCP 配置
|
|
807
|
-
*/
|
|
808
|
-
getCustomMCPConfig() {
|
|
809
|
-
const config = this.getConfig();
|
|
810
|
-
return config.customMCP || null;
|
|
811
|
-
}
|
|
812
|
-
/**
|
|
813
|
-
* 获取 customMCP 工具列表
|
|
814
|
-
*/
|
|
815
|
-
getCustomMCPTools() {
|
|
816
|
-
const customMCPConfig = this.getCustomMCPConfig();
|
|
817
|
-
if (!customMCPConfig || !customMCPConfig.tools) {
|
|
818
|
-
return [];
|
|
819
|
-
}
|
|
820
|
-
return customMCPConfig.tools;
|
|
821
|
-
}
|
|
822
|
-
/**
|
|
823
|
-
* 验证 customMCP 工具配置
|
|
824
|
-
*/
|
|
825
|
-
validateCustomMCPTools(tools) {
|
|
826
|
-
if (!Array.isArray(tools)) {
|
|
827
|
-
return false;
|
|
828
|
-
}
|
|
829
|
-
for (const tool of tools) {
|
|
830
|
-
if (!tool.name || typeof tool.name !== "string") {
|
|
831
|
-
console.warn("CustomMCP \u5DE5\u5177\u7F3A\u5C11\u6709\u6548\u7684 name \u5B57\u6BB5", { tool });
|
|
832
|
-
return false;
|
|
833
|
-
}
|
|
834
|
-
if (!tool.description || typeof tool.description !== "string") {
|
|
835
|
-
console.warn("CustomMCP \u5DE5\u5177\u7F3A\u5C11\u6709\u6548\u7684 description \u5B57\u6BB5", {
|
|
836
|
-
toolName: tool.name
|
|
837
|
-
});
|
|
838
|
-
return false;
|
|
839
|
-
}
|
|
840
|
-
if (!tool.inputSchema || typeof tool.inputSchema !== "object") {
|
|
841
|
-
console.warn("CustomMCP \u5DE5\u5177\u7F3A\u5C11\u6709\u6548\u7684 inputSchema \u5B57\u6BB5", {
|
|
842
|
-
toolName: tool.name
|
|
843
|
-
});
|
|
844
|
-
return false;
|
|
845
|
-
}
|
|
846
|
-
if (!tool.handler || typeof tool.handler !== "object") {
|
|
847
|
-
console.warn("CustomMCP \u5DE5\u5177\u7F3A\u5C11\u6709\u6548\u7684 handler \u5B57\u6BB5", {
|
|
848
|
-
toolName: tool.name
|
|
849
|
-
});
|
|
850
|
-
return false;
|
|
851
|
-
}
|
|
852
|
-
if (!["proxy", "function", "http", "script", "chain", "mcp"].includes(
|
|
853
|
-
tool.handler.type
|
|
854
|
-
)) {
|
|
855
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 handler.type \u7C7B\u578B\u65E0\u6548", {
|
|
856
|
-
toolName: tool.name,
|
|
857
|
-
type: tool.handler.type
|
|
858
|
-
});
|
|
859
|
-
return false;
|
|
860
|
-
}
|
|
861
|
-
if (!this.validateHandlerConfig(tool.name, tool.handler)) {
|
|
862
|
-
return false;
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
return true;
|
|
866
|
-
}
|
|
867
|
-
/**
|
|
868
|
-
* 验证处理器配置
|
|
869
|
-
*/
|
|
870
|
-
validateHandlerConfig(toolName, handler) {
|
|
871
|
-
switch (handler.type) {
|
|
872
|
-
case "proxy":
|
|
873
|
-
return this.validateProxyHandler(toolName, handler);
|
|
874
|
-
case "http":
|
|
875
|
-
return this.validateHttpHandler(toolName, handler);
|
|
876
|
-
case "function":
|
|
877
|
-
return this.validateFunctionHandler(toolName, handler);
|
|
878
|
-
case "script":
|
|
879
|
-
return this.validateScriptHandler(toolName, handler);
|
|
880
|
-
case "chain":
|
|
881
|
-
return this.validateChainHandler(toolName, handler);
|
|
882
|
-
case "mcp":
|
|
883
|
-
return this.validateMCPHandler(toolName, handler);
|
|
884
|
-
default:
|
|
885
|
-
console.warn("CustomMCP \u5DE5\u5177\u4F7F\u7528\u4E86\u672A\u77E5\u7684\u5904\u7406\u5668\u7C7B\u578B", {
|
|
886
|
-
toolName,
|
|
887
|
-
handlerType: handler.type
|
|
888
|
-
});
|
|
889
|
-
return false;
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
/**
|
|
893
|
-
* 验证代理处理器配置
|
|
894
|
-
*/
|
|
895
|
-
validateProxyHandler(toolName, handler) {
|
|
896
|
-
if (!handler.platform) {
|
|
897
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 proxy \u5904\u7406\u5668\u7F3A\u5C11 platform \u5B57\u6BB5", {
|
|
898
|
-
toolName
|
|
899
|
-
});
|
|
900
|
-
return false;
|
|
901
|
-
}
|
|
902
|
-
if (!["coze", "openai", "anthropic", "custom"].includes(handler.platform)) {
|
|
903
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 proxy \u5904\u7406\u5668\u4F7F\u7528\u4E86\u4E0D\u652F\u6301\u7684\u5E73\u53F0", {
|
|
904
|
-
toolName,
|
|
905
|
-
platform: handler.platform
|
|
906
|
-
});
|
|
907
|
-
return false;
|
|
908
|
-
}
|
|
909
|
-
if (!handler.config || typeof handler.config !== "object") {
|
|
910
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 proxy \u5904\u7406\u5668\u7F3A\u5C11 config \u5B57\u6BB5", {
|
|
911
|
-
toolName
|
|
912
|
-
});
|
|
913
|
-
return false;
|
|
914
|
-
}
|
|
915
|
-
if (handler.platform === "coze") {
|
|
916
|
-
if (!handler.config.workflow_id && !handler.config.bot_id) {
|
|
917
|
-
console.warn(
|
|
918
|
-
"CustomMCP \u5DE5\u5177\u7684 Coze \u5904\u7406\u5668\u5FC5\u987B\u63D0\u4F9B workflow_id \u6216 bot_id",
|
|
919
|
-
{ toolName }
|
|
920
|
-
);
|
|
921
|
-
return false;
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
return true;
|
|
925
|
-
}
|
|
926
|
-
/**
|
|
927
|
-
* 验证 HTTP 处理器配置
|
|
928
|
-
*/
|
|
929
|
-
validateHttpHandler(toolName, handler) {
|
|
930
|
-
if (!handler.url || typeof handler.url !== "string") {
|
|
931
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 http \u5904\u7406\u5668\u7F3A\u5C11\u6709\u6548\u7684 url \u5B57\u6BB5", {
|
|
932
|
-
toolName
|
|
933
|
-
});
|
|
934
|
-
return false;
|
|
935
|
-
}
|
|
936
|
-
try {
|
|
937
|
-
new URL(handler.url);
|
|
938
|
-
} catch {
|
|
939
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 http \u5904\u7406\u5668 url \u683C\u5F0F\u65E0\u6548", {
|
|
940
|
-
toolName,
|
|
941
|
-
url: handler.url
|
|
942
|
-
});
|
|
943
|
-
return false;
|
|
944
|
-
}
|
|
945
|
-
if (handler.method && !["GET", "POST", "PUT", "DELETE", "PATCH"].includes(handler.method)) {
|
|
946
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 http \u5904\u7406\u5668\u4F7F\u7528\u4E86\u4E0D\u652F\u6301\u7684 HTTP \u65B9\u6CD5", {
|
|
947
|
-
toolName,
|
|
948
|
-
method: handler.method
|
|
949
|
-
});
|
|
950
|
-
return false;
|
|
951
|
-
}
|
|
952
|
-
return true;
|
|
953
|
-
}
|
|
954
|
-
/**
|
|
955
|
-
* 验证函数处理器配置
|
|
956
|
-
*/
|
|
957
|
-
validateFunctionHandler(toolName, handler) {
|
|
958
|
-
if (!handler.module || typeof handler.module !== "string") {
|
|
959
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 function \u5904\u7406\u5668\u7F3A\u5C11\u6709\u6548\u7684 module \u5B57\u6BB5", {
|
|
960
|
-
toolName
|
|
961
|
-
});
|
|
962
|
-
return false;
|
|
963
|
-
}
|
|
964
|
-
if (!handler.function || typeof handler.function !== "string") {
|
|
965
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 function \u5904\u7406\u5668\u7F3A\u5C11\u6709\u6548\u7684 function \u5B57\u6BB5", {
|
|
966
|
-
toolName
|
|
967
|
-
});
|
|
968
|
-
return false;
|
|
969
|
-
}
|
|
970
|
-
return true;
|
|
971
|
-
}
|
|
972
|
-
/**
|
|
973
|
-
* 验证脚本处理器配置
|
|
974
|
-
*/
|
|
975
|
-
validateScriptHandler(toolName, handler) {
|
|
976
|
-
if (!handler.script || typeof handler.script !== "string") {
|
|
977
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 script \u5904\u7406\u5668\u7F3A\u5C11\u6709\u6548\u7684 script \u5B57\u6BB5", {
|
|
978
|
-
toolName
|
|
979
|
-
});
|
|
980
|
-
return false;
|
|
981
|
-
}
|
|
982
|
-
if (handler.interpreter && !["node", "python", "bash"].includes(handler.interpreter)) {
|
|
983
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 script \u5904\u7406\u5668\u4F7F\u7528\u4E86\u4E0D\u652F\u6301\u7684\u89E3\u91CA\u5668", {
|
|
984
|
-
toolName,
|
|
985
|
-
interpreter: handler.interpreter
|
|
986
|
-
});
|
|
987
|
-
return false;
|
|
988
|
-
}
|
|
989
|
-
return true;
|
|
990
|
-
}
|
|
991
|
-
/**
|
|
992
|
-
* 验证链式处理器配置
|
|
993
|
-
*/
|
|
994
|
-
validateChainHandler(toolName, handler) {
|
|
995
|
-
if (!handler.tools || !Array.isArray(handler.tools) || handler.tools.length === 0) {
|
|
996
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 chain \u5904\u7406\u5668\u7F3A\u5C11\u6709\u6548\u7684 tools \u6570\u7EC4", {
|
|
997
|
-
toolName
|
|
998
|
-
});
|
|
999
|
-
return false;
|
|
1000
|
-
}
|
|
1001
|
-
if (!["sequential", "parallel"].includes(handler.mode)) {
|
|
1002
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 chain \u5904\u7406\u5668\u4F7F\u7528\u4E86\u4E0D\u652F\u6301\u7684\u6267\u884C\u6A21\u5F0F", {
|
|
1003
|
-
toolName,
|
|
1004
|
-
mode: handler.mode
|
|
1005
|
-
});
|
|
1006
|
-
return false;
|
|
1007
|
-
}
|
|
1008
|
-
if (!["stop", "continue", "retry"].includes(handler.error_handling)) {
|
|
1009
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 chain \u5904\u7406\u5668\u4F7F\u7528\u4E86\u4E0D\u652F\u6301\u7684\u9519\u8BEF\u5904\u7406\u7B56\u7565", {
|
|
1010
|
-
toolName,
|
|
1011
|
-
errorHandling: handler.error_handling
|
|
1012
|
-
});
|
|
1013
|
-
return false;
|
|
1014
|
-
}
|
|
1015
|
-
return true;
|
|
1016
|
-
}
|
|
1017
|
-
/**
|
|
1018
|
-
* 验证 MCP 处理器配置
|
|
1019
|
-
*/
|
|
1020
|
-
validateMCPHandler(toolName, handler) {
|
|
1021
|
-
if (!handler.config || typeof handler.config !== "object") {
|
|
1022
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 mcp \u5904\u7406\u5668\u7F3A\u5C11 config \u5B57\u6BB5", { toolName });
|
|
1023
|
-
return false;
|
|
1024
|
-
}
|
|
1025
|
-
if (!handler.config.serviceName || typeof handler.config.serviceName !== "string") {
|
|
1026
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 mcp \u5904\u7406\u5668\u7F3A\u5C11\u6709\u6548\u7684 serviceName", {
|
|
1027
|
-
toolName
|
|
1028
|
-
});
|
|
1029
|
-
return false;
|
|
1030
|
-
}
|
|
1031
|
-
if (!handler.config.toolName || typeof handler.config.toolName !== "string") {
|
|
1032
|
-
console.warn("CustomMCP \u5DE5\u5177\u7684 mcp \u5904\u7406\u5668\u7F3A\u5C11\u6709\u6548\u7684 toolName", {
|
|
1033
|
-
toolName
|
|
1034
|
-
});
|
|
1035
|
-
return false;
|
|
1036
|
-
}
|
|
1037
|
-
return true;
|
|
1038
|
-
}
|
|
1039
|
-
/**
|
|
1040
|
-
* 检查是否配置了有效的 customMCP 工具
|
|
1041
|
-
*/
|
|
1042
|
-
hasValidCustomMCPTools() {
|
|
1043
|
-
try {
|
|
1044
|
-
const tools = this.getCustomMCPTools();
|
|
1045
|
-
if (tools.length === 0) {
|
|
1046
|
-
return false;
|
|
1047
|
-
}
|
|
1048
|
-
return this.validateCustomMCPTools(tools);
|
|
1049
|
-
} catch (error) {
|
|
1050
|
-
console.error("\u68C0\u67E5 customMCP \u5DE5\u5177\u914D\u7F6E\u65F6\u51FA\u9519", { error });
|
|
1051
|
-
return false;
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
/**
|
|
1055
|
-
* 添加自定义 MCP 工具
|
|
1056
|
-
*/
|
|
1057
|
-
addCustomMCPTool(tool) {
|
|
1058
|
-
if (!tool || typeof tool !== "object") {
|
|
1059
|
-
throw new Error("\u5DE5\u5177\u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A");
|
|
1060
|
-
}
|
|
1061
|
-
const config = this.getMutableConfig();
|
|
1062
|
-
if (!config.customMCP) {
|
|
1063
|
-
config.customMCP = { tools: [] };
|
|
1064
|
-
}
|
|
1065
|
-
const existingTool = config.customMCP.tools.find(
|
|
1066
|
-
(t) => t.name === tool.name
|
|
1067
|
-
);
|
|
1068
|
-
if (existingTool) {
|
|
1069
|
-
throw new Error(`\u5DE5\u5177 "${tool.name}" \u5DF2\u5B58\u5728`);
|
|
1070
|
-
}
|
|
1071
|
-
if (!this.validateCustomMCPTools([tool])) {
|
|
1072
|
-
throw new Error("\u5DE5\u5177\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");
|
|
1073
|
-
}
|
|
1074
|
-
config.customMCP.tools.unshift(tool);
|
|
1075
|
-
this.saveConfig(config);
|
|
1076
|
-
console.log("\u6210\u529F\u6DFB\u52A0\u81EA\u5B9A\u4E49 MCP \u5DE5\u5177", { toolName: tool.name });
|
|
1077
|
-
}
|
|
1078
|
-
/**
|
|
1079
|
-
* 批量添加自定义 MCP 工具
|
|
1080
|
-
* @param tools 要添加的工具数组
|
|
1081
|
-
*/
|
|
1082
|
-
async addCustomMCPTools(tools) {
|
|
1083
|
-
if (!Array.isArray(tools)) {
|
|
1084
|
-
throw new Error("\u5DE5\u5177\u914D\u7F6E\u5FC5\u987B\u662F\u6570\u7EC4");
|
|
1085
|
-
}
|
|
1086
|
-
if (tools.length === 0) {
|
|
1087
|
-
return;
|
|
1088
|
-
}
|
|
1089
|
-
const config = this.getMutableConfig();
|
|
1090
|
-
if (!config.customMCP) {
|
|
1091
|
-
config.customMCP = { tools: [] };
|
|
1092
|
-
}
|
|
1093
|
-
const existingNames = new Set(
|
|
1094
|
-
config.customMCP.tools.map((tool) => tool.name)
|
|
1095
|
-
);
|
|
1096
|
-
const newTools = tools.filter((tool) => !existingNames.has(tool.name));
|
|
1097
|
-
if (newTools.length > 0) {
|
|
1098
|
-
if (!this.validateCustomMCPTools(newTools)) {
|
|
1099
|
-
throw new Error("\u5DE5\u5177\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");
|
|
1100
|
-
}
|
|
1101
|
-
config.customMCP.tools.push(...newTools);
|
|
1102
|
-
this.saveConfig(config);
|
|
1103
|
-
this.emitEvent("config:updated", {
|
|
1104
|
-
type: "customMCP",
|
|
1105
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
1106
|
-
});
|
|
1107
|
-
console.log("\u6210\u529F\u6279\u91CF\u6DFB\u52A0\u81EA\u5B9A\u4E49 MCP \u5DE5\u5177", {
|
|
1108
|
-
count: newTools.length,
|
|
1109
|
-
toolNames: newTools.map((t) => t.name)
|
|
1110
|
-
});
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
/**
|
|
1114
|
-
* 删除自定义 MCP 工具
|
|
1115
|
-
*/
|
|
1116
|
-
removeCustomMCPTool(toolName) {
|
|
1117
|
-
if (!toolName || typeof toolName !== "string") {
|
|
1118
|
-
throw new Error("\u5DE5\u5177\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
|
|
1119
|
-
}
|
|
1120
|
-
const config = this.getMutableConfig();
|
|
1121
|
-
if (!config.customMCP || !config.customMCP.tools) {
|
|
1122
|
-
throw new Error("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49 MCP \u5DE5\u5177");
|
|
1123
|
-
}
|
|
1124
|
-
const toolIndex = config.customMCP.tools.findIndex(
|
|
1125
|
-
(t) => t.name === toolName
|
|
1126
|
-
);
|
|
1127
|
-
if (toolIndex === -1) {
|
|
1128
|
-
throw new Error(`\u5DE5\u5177 "${toolName}" \u4E0D\u5B58\u5728`);
|
|
1129
|
-
}
|
|
1130
|
-
config.customMCP.tools.splice(toolIndex, 1);
|
|
1131
|
-
this.saveConfig(config);
|
|
1132
|
-
console.log("\u6210\u529F\u5220\u9664\u81EA\u5B9A\u4E49 MCP \u5DE5\u5177", { toolName });
|
|
1133
|
-
}
|
|
1134
|
-
/**
|
|
1135
|
-
* 更新单个自定义 MCP 工具配置
|
|
1136
|
-
* @param toolName 工具名称
|
|
1137
|
-
* @param updatedTool 更新后的工具配置
|
|
1138
|
-
*/
|
|
1139
|
-
updateCustomMCPTool(toolName, updatedTool) {
|
|
1140
|
-
if (!toolName || typeof toolName !== "string") {
|
|
1141
|
-
throw new Error("\u5DE5\u5177\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
|
|
1142
|
-
}
|
|
1143
|
-
if (!updatedTool || typeof updatedTool !== "object") {
|
|
1144
|
-
throw new Error("\u66F4\u65B0\u540E\u7684\u5DE5\u5177\u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A");
|
|
1145
|
-
}
|
|
1146
|
-
const config = this.getMutableConfig();
|
|
1147
|
-
if (!config.customMCP || !config.customMCP.tools) {
|
|
1148
|
-
throw new Error("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49 MCP \u5DE5\u5177");
|
|
1149
|
-
}
|
|
1150
|
-
const toolIndex = config.customMCP.tools.findIndex(
|
|
1151
|
-
(t) => t.name === toolName
|
|
1152
|
-
);
|
|
1153
|
-
if (toolIndex === -1) {
|
|
1154
|
-
throw new Error(`\u5DE5\u5177 "${toolName}" \u4E0D\u5B58\u5728`);
|
|
1155
|
-
}
|
|
1156
|
-
if (!this.validateCustomMCPTools([updatedTool])) {
|
|
1157
|
-
throw new Error("\u66F4\u65B0\u540E\u7684\u5DE5\u5177\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");
|
|
1158
|
-
}
|
|
1159
|
-
config.customMCP.tools[toolIndex] = updatedTool;
|
|
1160
|
-
this.saveConfig(config);
|
|
1161
|
-
console.log("\u6210\u529F\u66F4\u65B0\u81EA\u5B9A\u4E49 MCP \u5DE5\u5177", { toolName });
|
|
1162
|
-
}
|
|
1163
|
-
/**
|
|
1164
|
-
* 更新自定义 MCP 工具配置
|
|
1165
|
-
*/
|
|
1166
|
-
updateCustomMCPTools(tools) {
|
|
1167
|
-
if (!Array.isArray(tools)) {
|
|
1168
|
-
throw new Error("\u5DE5\u5177\u914D\u7F6E\u5FC5\u987B\u662F\u6570\u7EC4");
|
|
1169
|
-
}
|
|
1170
|
-
if (!this.validateCustomMCPTools(tools)) {
|
|
1171
|
-
throw new Error("\u5DE5\u5177\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");
|
|
1172
|
-
}
|
|
1173
|
-
const config = this.getMutableConfig();
|
|
1174
|
-
if (!config.customMCP) {
|
|
1175
|
-
config.customMCP = { tools: [] };
|
|
1176
|
-
}
|
|
1177
|
-
config.customMCP.tools = tools;
|
|
1178
|
-
this.saveConfig(config);
|
|
1179
|
-
this.emitEvent("config:updated", {
|
|
1180
|
-
type: "customMCP",
|
|
1181
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
1182
|
-
});
|
|
1183
|
-
console.log("\u6210\u529F\u66F4\u65B0\u81EA\u5B9A\u4E49 MCP \u5DE5\u5177\u914D\u7F6E", { count: tools.length });
|
|
1184
|
-
}
|
|
1185
|
-
/**
|
|
1186
|
-
* 获取 Web UI 配置
|
|
1187
|
-
*/
|
|
1188
|
-
getWebUIConfig() {
|
|
1189
|
-
const config = this.getConfig();
|
|
1190
|
-
return config.webUI || {};
|
|
1191
|
-
}
|
|
1192
|
-
/**
|
|
1193
|
-
* 获取 Web UI 端口号
|
|
1194
|
-
*/
|
|
1195
|
-
getWebUIPort() {
|
|
1196
|
-
const webUIConfig = this.getWebUIConfig();
|
|
1197
|
-
return webUIConfig.port ?? 9999;
|
|
1198
|
-
}
|
|
1199
|
-
/**
|
|
1200
|
-
* 通知 Web 界面配置已更新
|
|
1201
|
-
* 如果 Web 服务器正在运行,通过 WebSocket 广播配置更新
|
|
1202
|
-
*/
|
|
1203
|
-
notifyConfigUpdate(config) {
|
|
1204
|
-
try {
|
|
1205
|
-
const webServer = global.__webServer;
|
|
1206
|
-
if (webServer && typeof webServer.broadcastConfigUpdate === "function") {
|
|
1207
|
-
webServer.broadcastConfigUpdate(config);
|
|
1208
|
-
console.log("\u5DF2\u901A\u8FC7 WebSocket \u5E7F\u64AD\u914D\u7F6E\u66F4\u65B0");
|
|
1209
|
-
}
|
|
1210
|
-
} catch (error) {
|
|
1211
|
-
console.warn(
|
|
1212
|
-
"\u901A\u77E5 Web \u754C\u9762\u914D\u7F6E\u66F4\u65B0\u5931\u8D25:",
|
|
1213
|
-
error instanceof Error ? error.message : String(error)
|
|
1214
|
-
);
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
/**
|
|
1218
|
-
* 更新 Web UI 配置
|
|
1219
|
-
*/
|
|
1220
|
-
updateWebUIConfig(webUIConfig) {
|
|
1221
|
-
const config = this.getMutableConfig();
|
|
1222
|
-
if (!config.webUI) {
|
|
1223
|
-
config.webUI = {};
|
|
1224
|
-
}
|
|
1225
|
-
Object.assign(config.webUI, webUIConfig);
|
|
1226
|
-
this.saveConfig(config);
|
|
1227
|
-
this.emitEvent("config:updated", {
|
|
1228
|
-
type: "webui",
|
|
1229
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
1230
|
-
});
|
|
1231
|
-
}
|
|
1232
|
-
/**
|
|
1233
|
-
* 设置 Web UI 端口号
|
|
1234
|
-
*/
|
|
1235
|
-
setWebUIPort(port) {
|
|
1236
|
-
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
1237
|
-
throw new Error("\u7AEF\u53E3\u53F7\u5FC5\u987B\u662F 1-65535 \u4E4B\u95F4\u7684\u6574\u6570");
|
|
1238
|
-
}
|
|
1239
|
-
this.updateWebUIConfig({ port });
|
|
1240
|
-
}
|
|
1241
|
-
updatePlatformConfig(platformName, platformConfig) {
|
|
1242
|
-
const config = this.getMutableConfig();
|
|
1243
|
-
if (!config.platforms) {
|
|
1244
|
-
config.platforms = {};
|
|
1245
|
-
}
|
|
1246
|
-
config.platforms[platformName] = platformConfig;
|
|
1247
|
-
this.saveConfig(config);
|
|
1248
|
-
this.emitEvent("config:updated", {
|
|
1249
|
-
type: "platform",
|
|
1250
|
-
platformName,
|
|
1251
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
1252
|
-
});
|
|
1253
|
-
}
|
|
1254
|
-
/**
|
|
1255
|
-
* 获取扣子平台配置
|
|
1256
|
-
*/
|
|
1257
|
-
getCozePlatformConfig() {
|
|
1258
|
-
const config = this.getConfig();
|
|
1259
|
-
const cozeConfig = config.platforms?.coze;
|
|
1260
|
-
if (!cozeConfig || !cozeConfig.token) {
|
|
1261
|
-
return null;
|
|
1262
|
-
}
|
|
1263
|
-
return {
|
|
1264
|
-
token: cozeConfig.token
|
|
1265
|
-
};
|
|
1266
|
-
}
|
|
1267
|
-
/**
|
|
1268
|
-
* 获取扣子 API Token
|
|
1269
|
-
*/
|
|
1270
|
-
getCozeToken() {
|
|
1271
|
-
const cozeConfig = this.getCozePlatformConfig();
|
|
1272
|
-
return cozeConfig?.token || null;
|
|
1273
|
-
}
|
|
1274
|
-
/**
|
|
1275
|
-
* 设置扣子平台配置
|
|
1276
|
-
*/
|
|
1277
|
-
setCozePlatformConfig(config) {
|
|
1278
|
-
if (!config.token || typeof config.token !== "string" || config.token.trim() === "") {
|
|
1279
|
-
throw new Error("\u6263\u5B50 API Token \u4E0D\u80FD\u4E3A\u7A7A");
|
|
1280
|
-
}
|
|
1281
|
-
this.updatePlatformConfig("coze", {
|
|
1282
|
-
token: config.token.trim()
|
|
1283
|
-
});
|
|
1284
|
-
}
|
|
1285
|
-
/**
|
|
1286
|
-
* 检查扣子平台配置是否有效
|
|
1287
|
-
*/
|
|
1288
|
-
isCozeConfigValid() {
|
|
1289
|
-
const cozeConfig = this.getCozePlatformConfig();
|
|
1290
|
-
return cozeConfig !== null && typeof cozeConfig.token === "string" && cozeConfig.token.trim() !== "";
|
|
1291
|
-
}
|
|
1292
|
-
/**
|
|
1293
|
-
* 更新 mcpServerConfig 中的工具使用统计信息(内部实现)
|
|
1294
|
-
* @param serverName 服务名称
|
|
1295
|
-
* @param toolName 工具名称
|
|
1296
|
-
* @param callTime 调用时间(ISO 8601 格式)
|
|
1297
|
-
* @param incrementUsageCount 是否增加使用计数
|
|
1298
|
-
* @private
|
|
1299
|
-
*/
|
|
1300
|
-
async _updateMCPServerToolStats(serverName, toolName, callTime, incrementUsageCount = true) {
|
|
1301
|
-
const config = this.getMutableConfig();
|
|
1302
|
-
if (!config.mcpServerConfig) {
|
|
1303
|
-
config.mcpServerConfig = {};
|
|
1304
|
-
}
|
|
1305
|
-
if (!config.mcpServerConfig[serverName]) {
|
|
1306
|
-
config.mcpServerConfig[serverName] = { tools: {} };
|
|
1307
|
-
}
|
|
1308
|
-
if (!config.mcpServerConfig[serverName].tools[toolName]) {
|
|
1309
|
-
config.mcpServerConfig[serverName].tools[toolName] = {
|
|
1310
|
-
enable: true
|
|
1311
|
-
// 默认启用
|
|
1312
|
-
};
|
|
1313
|
-
}
|
|
1314
|
-
const toolConfig = config.mcpServerConfig[serverName].tools[toolName];
|
|
1315
|
-
const currentUsageCount = toolConfig.usageCount || 0;
|
|
1316
|
-
const currentLastUsedTime = toolConfig.lastUsedTime;
|
|
1317
|
-
if (incrementUsageCount) {
|
|
1318
|
-
toolConfig.usageCount = currentUsageCount + 1;
|
|
1319
|
-
}
|
|
1320
|
-
if (!currentLastUsedTime || new Date(callTime) > new Date(currentLastUsedTime)) {
|
|
1321
|
-
toolConfig.lastUsedTime = dayjs(callTime).format("YYYY-MM-DD HH:mm:ss");
|
|
1322
|
-
}
|
|
1323
|
-
this.saveConfig(config);
|
|
1324
|
-
}
|
|
1325
|
-
/**
|
|
1326
|
-
* 更新 customMCP 工具使用统计信息的实现
|
|
1327
|
-
* @private
|
|
1328
|
-
*/
|
|
1329
|
-
async updateCustomMCPToolStats(arg1, arg2, arg3) {
|
|
1330
|
-
try {
|
|
1331
|
-
let toolName;
|
|
1332
|
-
let callTime;
|
|
1333
|
-
let incrementUsageCount = true;
|
|
1334
|
-
let logPrefix;
|
|
1335
|
-
if (typeof arg3 === "string") {
|
|
1336
|
-
const serverName = arg1;
|
|
1337
|
-
toolName = `${serverName}__${arg2}`;
|
|
1338
|
-
callTime = arg3;
|
|
1339
|
-
logPrefix = `${serverName}/${arg2}`;
|
|
1340
|
-
} else {
|
|
1341
|
-
toolName = arg1;
|
|
1342
|
-
callTime = arg2;
|
|
1343
|
-
incrementUsageCount = arg3 || true;
|
|
1344
|
-
logPrefix = toolName;
|
|
1345
|
-
}
|
|
1346
|
-
const customTools = this.getCustomMCPTools();
|
|
1347
|
-
const toolIndex = customTools.findIndex((tool2) => tool2.name === toolName);
|
|
1348
|
-
if (toolIndex === -1) {
|
|
1349
|
-
return;
|
|
1350
|
-
}
|
|
1351
|
-
const updatedTools = [...customTools];
|
|
1352
|
-
const tool = updatedTools[toolIndex];
|
|
1353
|
-
if (!tool.stats) {
|
|
1354
|
-
tool.stats = {};
|
|
1355
|
-
}
|
|
1356
|
-
const currentUsageCount = tool.stats.usageCount || 0;
|
|
1357
|
-
const currentLastUsedTime = tool.stats.lastUsedTime;
|
|
1358
|
-
if (incrementUsageCount) {
|
|
1359
|
-
tool.stats.usageCount = currentUsageCount + 1;
|
|
1360
|
-
}
|
|
1361
|
-
if (!currentLastUsedTime || new Date(callTime) > new Date(currentLastUsedTime)) {
|
|
1362
|
-
tool.stats.lastUsedTime = dayjs(callTime).format("YYYY-MM-DD HH:mm:ss");
|
|
1363
|
-
}
|
|
1364
|
-
await this.updateCustomMCPTools(updatedTools);
|
|
1365
|
-
} catch (error) {
|
|
1366
|
-
if (typeof arg3 === "string") {
|
|
1367
|
-
const serverName = arg1;
|
|
1368
|
-
const toolName = arg2;
|
|
1369
|
-
console.error("\u66F4\u65B0 customMCP \u5DE5\u5177\u7EDF\u8BA1\u4FE1\u606F\u5931\u8D25", {
|
|
1370
|
-
serverName,
|
|
1371
|
-
toolName,
|
|
1372
|
-
error
|
|
1373
|
-
});
|
|
1374
|
-
} else {
|
|
1375
|
-
const toolName = arg1;
|
|
1376
|
-
console.error("\u66F4\u65B0 customMCP \u5DE5\u5177\u7EDF\u8BA1\u4FE1\u606F\u5931\u8D25", { toolName, error });
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
/**
|
|
1381
|
-
* 获取统计更新锁(确保同一工具的统计更新串行执行)
|
|
1382
|
-
* @param toolKey 工具键
|
|
1383
|
-
* @private
|
|
1384
|
-
*/
|
|
1385
|
-
async acquireStatsUpdateLock(toolKey) {
|
|
1386
|
-
if (this.statsUpdateLocks.has(toolKey)) {
|
|
1387
|
-
console.log("\u5DE5\u5177\u7EDF\u8BA1\u66F4\u65B0\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u8DF3\u8FC7\u672C\u6B21\u66F4\u65B0", { toolKey });
|
|
1388
|
-
return false;
|
|
1389
|
-
}
|
|
1390
|
-
const updatePromise = new Promise((resolve3) => {
|
|
1391
|
-
});
|
|
1392
|
-
this.statsUpdateLocks.set(toolKey, updatePromise);
|
|
1393
|
-
const timeout = setTimeout(() => {
|
|
1394
|
-
this.releaseStatsUpdateLock(toolKey);
|
|
1395
|
-
}, this.STATS_UPDATE_TIMEOUT);
|
|
1396
|
-
this.statsUpdateLockTimeouts.set(toolKey, timeout);
|
|
1397
|
-
return true;
|
|
1398
|
-
}
|
|
1399
|
-
/**
|
|
1400
|
-
* 释放统计更新锁
|
|
1401
|
-
* @param toolKey 工具键
|
|
1402
|
-
* @private
|
|
1403
|
-
*/
|
|
1404
|
-
releaseStatsUpdateLock(toolKey) {
|
|
1405
|
-
this.statsUpdateLocks.delete(toolKey);
|
|
1406
|
-
const timeout = this.statsUpdateLockTimeouts.get(toolKey);
|
|
1407
|
-
if (timeout) {
|
|
1408
|
-
clearTimeout(timeout);
|
|
1409
|
-
this.statsUpdateLockTimeouts.delete(toolKey);
|
|
1410
|
-
}
|
|
1411
|
-
console.log("\u5DF2\u91CA\u653E\u5DE5\u5177\u7684\u7EDF\u8BA1\u66F4\u65B0\u9501", { toolKey });
|
|
1412
|
-
}
|
|
1413
|
-
/**
|
|
1414
|
-
* 带并发控制的工具统计更新(CustomMCP 工具)
|
|
1415
|
-
* @param toolName 工具名称
|
|
1416
|
-
* @param incrementUsageCount 是否增加使用计数
|
|
1417
|
-
*/
|
|
1418
|
-
async updateToolUsageStatsWithLock(toolName, incrementUsageCount = true) {
|
|
1419
|
-
const toolKey = `custommcp_${toolName}`;
|
|
1420
|
-
if (!await this.acquireStatsUpdateLock(toolKey)) {
|
|
1421
|
-
return;
|
|
1422
|
-
}
|
|
1423
|
-
try {
|
|
1424
|
-
await this.updateToolUsageStats(toolName, incrementUsageCount);
|
|
1425
|
-
console.log("\u5DE5\u5177\u7EDF\u8BA1\u66F4\u65B0\u5B8C\u6210", { toolName });
|
|
1426
|
-
} catch (error) {
|
|
1427
|
-
console.error("\u5DE5\u5177\u7EDF\u8BA1\u66F4\u65B0\u5931\u8D25", { toolName, error });
|
|
1428
|
-
throw error;
|
|
1429
|
-
} finally {
|
|
1430
|
-
this.releaseStatsUpdateLock(toolKey);
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
/**
|
|
1434
|
-
* 带并发控制的工具统计更新(MCP 服务工具)
|
|
1435
|
-
* @param serviceName 服务名称
|
|
1436
|
-
* @param toolName 工具名称
|
|
1437
|
-
* @param callTime 调用时间
|
|
1438
|
-
* @param incrementUsageCount 是否增加使用计数
|
|
1439
|
-
*/
|
|
1440
|
-
async updateMCPServerToolStatsWithLock(serviceName, toolName, callTime, incrementUsageCount = true) {
|
|
1441
|
-
const toolKey = `mcpserver_${serviceName}_${toolName}`;
|
|
1442
|
-
if (!await this.acquireStatsUpdateLock(toolKey)) {
|
|
1443
|
-
return;
|
|
1444
|
-
}
|
|
1445
|
-
try {
|
|
1446
|
-
await this.updateMCPServerToolStats(
|
|
1447
|
-
serviceName,
|
|
1448
|
-
toolName,
|
|
1449
|
-
callTime,
|
|
1450
|
-
incrementUsageCount
|
|
1451
|
-
);
|
|
1452
|
-
console.log("MCP \u670D\u52A1\u5DE5\u5177\u7EDF\u8BA1\u66F4\u65B0\u5B8C\u6210", { serviceName, toolName });
|
|
1453
|
-
} catch (error) {
|
|
1454
|
-
console.error("MCP \u670D\u52A1\u5DE5\u5177\u7EDF\u8BA1\u66F4\u65B0\u5931\u8D25", {
|
|
1455
|
-
serviceName,
|
|
1456
|
-
toolName,
|
|
1457
|
-
error
|
|
1458
|
-
});
|
|
1459
|
-
throw error;
|
|
1460
|
-
} finally {
|
|
1461
|
-
this.releaseStatsUpdateLock(toolKey);
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
/**
|
|
1465
|
-
* 清理所有统计更新锁(用于异常恢复)
|
|
1466
|
-
*/
|
|
1467
|
-
clearAllStatsUpdateLocks() {
|
|
1468
|
-
const lockCount = this.statsUpdateLocks.size;
|
|
1469
|
-
this.statsUpdateLocks.clear();
|
|
1470
|
-
for (const timeout of this.statsUpdateLockTimeouts.values()) {
|
|
1471
|
-
clearTimeout(timeout);
|
|
1472
|
-
}
|
|
1473
|
-
this.statsUpdateLockTimeouts.clear();
|
|
1474
|
-
if (lockCount > 0) {
|
|
1475
|
-
console.log("\u5DF2\u6E05\u7406\u7EDF\u8BA1\u66F4\u65B0\u9501", { count: lockCount });
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
/**
|
|
1479
|
-
* 获取统计更新锁状态(用于调试和监控)
|
|
1480
|
-
*/
|
|
1481
|
-
getStatsUpdateLocks() {
|
|
1482
|
-
return Array.from(this.statsUpdateLocks.keys());
|
|
1483
|
-
}
|
|
1484
|
-
/**
|
|
1485
|
-
* 获取工具调用日志配置
|
|
1486
|
-
*/
|
|
1487
|
-
getToolCallLogConfig() {
|
|
1488
|
-
const config = this.getConfig();
|
|
1489
|
-
return config.toolCallLog || {};
|
|
1490
|
-
}
|
|
1491
|
-
/**
|
|
1492
|
-
* 更新工具调用日志配置
|
|
1493
|
-
*/
|
|
1494
|
-
updateToolCallLogConfig(toolCallLogConfig) {
|
|
1495
|
-
const config = this.getMutableConfig();
|
|
1496
|
-
if (!config.toolCallLog) {
|
|
1497
|
-
config.toolCallLog = {};
|
|
1498
|
-
}
|
|
1499
|
-
Object.assign(config.toolCallLog, toolCallLogConfig);
|
|
1500
|
-
this.saveConfig(config);
|
|
1501
|
-
}
|
|
1502
|
-
/**
|
|
1503
|
-
* 获取配置目录路径(与配置文件同级目录)
|
|
1504
|
-
*/
|
|
1505
|
-
getConfigDir() {
|
|
1506
|
-
return process.env.XIAOZHI_CONFIG_DIR || process.cwd();
|
|
1507
|
-
}
|
|
1508
|
-
};
|
|
1509
|
-
var configManager = ConfigManager.getInstance();
|
|
1510
|
-
|
|
1511
|
-
// src/adapter.ts
|
|
1512
|
-
import { isAbsolute, resolve as resolve2 } from "path";
|
|
1513
|
-
var ConfigValidationError = class extends Error {
|
|
1514
|
-
constructor(message, configName) {
|
|
1515
|
-
super(message);
|
|
1516
|
-
this.configName = configName;
|
|
1517
|
-
this.name = "ConfigValidationError";
|
|
1518
|
-
}
|
|
1519
|
-
static {
|
|
1520
|
-
__name(this, "ConfigValidationError");
|
|
1521
|
-
}
|
|
1522
|
-
};
|
|
1523
|
-
var MCPTransportType = /* @__PURE__ */ ((MCPTransportType2) => {
|
|
1524
|
-
MCPTransportType2["STDIO"] = "stdio";
|
|
1525
|
-
MCPTransportType2["SSE"] = "sse";
|
|
1526
|
-
MCPTransportType2["STREAMABLE_HTTP"] = "streamable-http";
|
|
1527
|
-
return MCPTransportType2;
|
|
1528
|
-
})(MCPTransportType || {});
|
|
1529
|
-
function inferTransportTypeFromUrl(url) {
|
|
1530
|
-
try {
|
|
1531
|
-
const parsedUrl = new URL(url);
|
|
1532
|
-
const pathname = parsedUrl.pathname;
|
|
1533
|
-
if (pathname.endsWith("/sse")) {
|
|
1534
|
-
return "sse" /* SSE */;
|
|
1535
|
-
}
|
|
1536
|
-
if (pathname.endsWith("/mcp")) {
|
|
1537
|
-
return "streamable-http" /* STREAMABLE_HTTP */;
|
|
1538
|
-
}
|
|
1539
|
-
return "streamable-http" /* STREAMABLE_HTTP */;
|
|
1540
|
-
} catch (error) {
|
|
1541
|
-
return "streamable-http" /* STREAMABLE_HTTP */;
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
|
-
__name(inferTransportTypeFromUrl, "inferTransportTypeFromUrl");
|
|
1545
|
-
function convertLegacyToNew(serviceName, legacyConfig) {
|
|
1546
|
-
console.log("\u8F6C\u6362\u914D\u7F6E", { serviceName, legacyConfig });
|
|
1547
|
-
try {
|
|
1548
|
-
if (!serviceName || typeof serviceName !== "string") {
|
|
1549
|
-
throw new ConfigValidationError("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
1550
|
-
}
|
|
1551
|
-
if (!legacyConfig || typeof legacyConfig !== "object") {
|
|
1552
|
-
throw new ConfigValidationError("\u914D\u7F6E\u5BF9\u8C61\u4E0D\u80FD\u4E3A\u7A7A", serviceName);
|
|
1553
|
-
}
|
|
1554
|
-
const newConfig = convertByConfigType(serviceName, legacyConfig);
|
|
1555
|
-
validateNewConfig(newConfig);
|
|
1556
|
-
console.log("\u914D\u7F6E\u8F6C\u6362\u6210\u529F", { serviceName, type: newConfig.type });
|
|
1557
|
-
return newConfig;
|
|
1558
|
-
} catch (error) {
|
|
1559
|
-
console.error("\u914D\u7F6E\u8F6C\u6362\u5931\u8D25", { serviceName, error });
|
|
1560
|
-
throw error instanceof ConfigValidationError ? error : new ConfigValidationError(
|
|
1561
|
-
`\u914D\u7F6E\u8F6C\u6362\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`,
|
|
1562
|
-
serviceName
|
|
1563
|
-
);
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
__name(convertLegacyToNew, "convertLegacyToNew");
|
|
1567
|
-
function convertByConfigType(serviceName, legacyConfig) {
|
|
1568
|
-
if (isLocalConfig(legacyConfig)) {
|
|
1569
|
-
return convertLocalConfig(serviceName, legacyConfig);
|
|
1570
|
-
}
|
|
1571
|
-
if ("type" in legacyConfig) {
|
|
1572
|
-
switch (legacyConfig.type) {
|
|
1573
|
-
case "sse":
|
|
1574
|
-
return convertSSEConfig(serviceName, legacyConfig);
|
|
1575
|
-
case "streamable-http":
|
|
1576
|
-
return convertStreamableHTTPConfig(serviceName, legacyConfig);
|
|
1577
|
-
default:
|
|
1578
|
-
throw new ConfigValidationError(
|
|
1579
|
-
`\u4E0D\u652F\u6301\u7684\u4F20\u8F93\u7C7B\u578B: ${legacyConfig.type}`,
|
|
1580
|
-
serviceName
|
|
1581
|
-
);
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
if ("url" in legacyConfig) {
|
|
1585
|
-
if (legacyConfig.url === void 0 || legacyConfig.url === null) {
|
|
1586
|
-
throw new ConfigValidationError(
|
|
1587
|
-
"\u7F51\u7EDC\u914D\u7F6E\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 url \u5B57\u6BB5",
|
|
1588
|
-
serviceName
|
|
1589
|
-
);
|
|
1590
|
-
}
|
|
1591
|
-
const inferredType = inferTransportTypeFromUrl(legacyConfig.url || "");
|
|
1592
|
-
if (inferredType === "sse" /* SSE */) {
|
|
1593
|
-
const sseConfig = { ...legacyConfig, type: "sse" };
|
|
1594
|
-
return convertSSEConfig(serviceName, sseConfig);
|
|
1595
|
-
}
|
|
1596
|
-
const httpConfig = { ...legacyConfig, type: "streamable-http" };
|
|
1597
|
-
return convertStreamableHTTPConfig(serviceName, httpConfig);
|
|
1598
|
-
}
|
|
1599
|
-
throw new ConfigValidationError("\u65E0\u6CD5\u8BC6\u522B\u7684\u914D\u7F6E\u7C7B\u578B", serviceName);
|
|
1600
|
-
}
|
|
1601
|
-
__name(convertByConfigType, "convertByConfigType");
|
|
1602
|
-
function convertLocalConfig(serviceName, config) {
|
|
1603
|
-
if (!config.command) {
|
|
1604
|
-
throw new ConfigValidationError(
|
|
1605
|
-
"\u672C\u5730\u914D\u7F6E\u5FC5\u987B\u5305\u542B command \u5B57\u6BB5",
|
|
1606
|
-
serviceName
|
|
1607
|
-
);
|
|
1608
|
-
}
|
|
1609
|
-
const workingDir = process.env.XIAOZHI_CONFIG_DIR || process.cwd();
|
|
1610
|
-
const resolvedArgs = (config.args || []).map((arg) => {
|
|
1611
|
-
if (isRelativePath(arg)) {
|
|
1612
|
-
const resolvedPath = resolve2(workingDir, arg);
|
|
1613
|
-
console.log("\u89E3\u6790\u76F8\u5BF9\u8DEF\u5F84", { arg, resolvedPath });
|
|
1614
|
-
return resolvedPath;
|
|
1615
|
-
}
|
|
1616
|
-
return arg;
|
|
1617
|
-
});
|
|
1618
|
-
return {
|
|
1619
|
-
name: serviceName,
|
|
1620
|
-
type: "stdio" /* STDIO */,
|
|
1621
|
-
command: config.command,
|
|
1622
|
-
args: resolvedArgs,
|
|
1623
|
-
env: config.env,
|
|
1624
|
-
// 传递环境变量
|
|
1625
|
-
timeout: 3e4
|
|
1626
|
-
};
|
|
1627
|
-
}
|
|
1628
|
-
__name(convertLocalConfig, "convertLocalConfig");
|
|
1629
|
-
function convertSSEConfig(serviceName, config) {
|
|
1630
|
-
if (config.url === void 0 || config.url === null) {
|
|
1631
|
-
throw new ConfigValidationError("SSE \u914D\u7F6E\u5FC5\u987B\u5305\u542B url \u5B57\u6BB5", serviceName);
|
|
1632
|
-
}
|
|
1633
|
-
const inferredType = config.type === "sse" ? "sse" /* SSE */ : inferTransportTypeFromUrl(config.url || "");
|
|
1634
|
-
const isModelScope = config.url ? isModelScopeURL(config.url) : false;
|
|
1635
|
-
const baseConfig = {
|
|
1636
|
-
name: serviceName,
|
|
1637
|
-
type: inferredType,
|
|
1638
|
-
url: config.url,
|
|
1639
|
-
timeout: 3e4,
|
|
1640
|
-
headers: config.headers
|
|
1641
|
-
};
|
|
1642
|
-
if (isModelScope) {
|
|
1643
|
-
baseConfig.modelScopeAuth = true;
|
|
1644
|
-
}
|
|
1645
|
-
console.log("SSE\u914D\u7F6E\u8F6C\u6362", {
|
|
1646
|
-
serviceName,
|
|
1647
|
-
url: config.url,
|
|
1648
|
-
inferredType,
|
|
1649
|
-
isModelScope
|
|
1650
|
-
});
|
|
1651
|
-
return baseConfig;
|
|
1652
|
-
}
|
|
1653
|
-
__name(convertSSEConfig, "convertSSEConfig");
|
|
1654
|
-
function convertStreamableHTTPConfig(serviceName, config) {
|
|
1655
|
-
if (config.url === void 0 || config.url === null) {
|
|
1656
|
-
throw new ConfigValidationError(
|
|
1657
|
-
"STREAMABLE_HTTP \u914D\u7F6E\u5FC5\u987B\u5305\u542B url \u5B57\u6BB5",
|
|
1658
|
-
serviceName
|
|
1659
|
-
);
|
|
1660
|
-
}
|
|
1661
|
-
const url = config.url || "";
|
|
1662
|
-
return {
|
|
1663
|
-
name: serviceName,
|
|
1664
|
-
type: "streamable-http" /* STREAMABLE_HTTP */,
|
|
1665
|
-
url,
|
|
1666
|
-
timeout: 3e4,
|
|
1667
|
-
headers: config.headers
|
|
1668
|
-
};
|
|
1669
|
-
}
|
|
1670
|
-
__name(convertStreamableHTTPConfig, "convertStreamableHTTPConfig");
|
|
1671
|
-
function convertLegacyConfigBatch(legacyConfigs) {
|
|
1672
|
-
const newConfigs = {};
|
|
1673
|
-
const errors = [];
|
|
1674
|
-
for (const [serviceName, legacyConfig] of Object.entries(legacyConfigs)) {
|
|
1675
|
-
try {
|
|
1676
|
-
newConfigs[serviceName] = convertLegacyToNew(serviceName, legacyConfig);
|
|
1677
|
-
} catch (error) {
|
|
1678
|
-
errors.push({
|
|
1679
|
-
serviceName,
|
|
1680
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
1681
|
-
});
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
if (errors.length > 0) {
|
|
1685
|
-
const errorMessages = errors.map(({ serviceName, error }) => `${serviceName}: ${error.message}`).join("; ");
|
|
1686
|
-
throw new ConfigValidationError(`\u6279\u91CF\u914D\u7F6E\u8F6C\u6362\u5931\u8D25: ${errorMessages}`);
|
|
1687
|
-
}
|
|
1688
|
-
console.log("\u6279\u91CF\u914D\u7F6E\u8F6C\u6362\u6210\u529F", { count: Object.keys(newConfigs).length });
|
|
1689
|
-
return newConfigs;
|
|
1690
|
-
}
|
|
1691
|
-
__name(convertLegacyConfigBatch, "convertLegacyConfigBatch");
|
|
1692
|
-
function isRelativePath(path) {
|
|
1693
|
-
if (isAbsolute(path)) {
|
|
1694
|
-
return false;
|
|
1695
|
-
}
|
|
1696
|
-
if (path.startsWith("./") || path.startsWith("../")) {
|
|
1697
|
-
return true;
|
|
1698
|
-
}
|
|
1699
|
-
if (/\.(js|py|ts|mjs|cjs)$/i.test(path)) {
|
|
1700
|
-
return true;
|
|
1701
|
-
}
|
|
1702
|
-
return false;
|
|
1703
|
-
}
|
|
1704
|
-
__name(isRelativePath, "isRelativePath");
|
|
1705
|
-
function isLocalConfig(config) {
|
|
1706
|
-
return "command" in config && typeof config.command === "string";
|
|
1707
|
-
}
|
|
1708
|
-
__name(isLocalConfig, "isLocalConfig");
|
|
1709
|
-
function isModelScopeURL(url) {
|
|
1710
|
-
try {
|
|
1711
|
-
const parsedUrl = new URL(url);
|
|
1712
|
-
const hostname = parsedUrl.hostname.toLowerCase();
|
|
1713
|
-
return hostname.endsWith(".modelscope.net") || hostname.endsWith(".modelscope.cn") || hostname === "modelscope.net" || hostname === "modelscope.cn";
|
|
1714
|
-
} catch {
|
|
1715
|
-
return false;
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
__name(isModelScopeURL, "isModelScopeURL");
|
|
1719
|
-
function validateNewConfig(config) {
|
|
1720
|
-
if (!config.name || typeof config.name !== "string") {
|
|
1721
|
-
throw new ConfigValidationError("\u914D\u7F6E\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 name \u5B57\u6BB5");
|
|
1722
|
-
}
|
|
1723
|
-
if (config.type && !Object.values(MCPTransportType).includes(config.type)) {
|
|
1724
|
-
throw new ConfigValidationError(`\u65E0\u6548\u7684\u4F20\u8F93\u7C7B\u578B: ${config.type}`);
|
|
1725
|
-
}
|
|
1726
|
-
if (!config.type) {
|
|
1727
|
-
throw new ConfigValidationError("\u4F20\u8F93\u7C7B\u578B\u672A\u6307\u5B9A\uFF0C\u8BF7\u68C0\u67E5\u914D\u7F6E\u6216\u542F\u7528\u81EA\u52A8\u63A8\u65AD");
|
|
1728
|
-
}
|
|
1729
|
-
switch (config.type) {
|
|
1730
|
-
case "stdio" /* STDIO */:
|
|
1731
|
-
if (!config.command) {
|
|
1732
|
-
throw new ConfigValidationError("STDIO \u914D\u7F6E\u5FC5\u987B\u5305\u542B command \u5B57\u6BB5");
|
|
1733
|
-
}
|
|
1734
|
-
break;
|
|
1735
|
-
case "sse" /* SSE */:
|
|
1736
|
-
if (config.url === void 0 || config.url === null) {
|
|
1737
|
-
throw new ConfigValidationError("SSE \u914D\u7F6E\u5FC5\u987B\u5305\u542B url \u5B57\u6BB5");
|
|
1738
|
-
}
|
|
1739
|
-
break;
|
|
1740
|
-
case "streamable-http" /* STREAMABLE_HTTP */:
|
|
1741
|
-
if (config.url === void 0 || config.url === null) {
|
|
1742
|
-
throw new ConfigValidationError(
|
|
1743
|
-
"STREAMABLE_HTTP \u914D\u7F6E\u5FC5\u987B\u5305\u542B url \u5B57\u6BB5"
|
|
1744
|
-
);
|
|
1745
|
-
}
|
|
1746
|
-
break;
|
|
1747
|
-
default:
|
|
1748
|
-
throw new ConfigValidationError(`\u4E0D\u652F\u6301\u7684\u4F20\u8F93\u7C7B\u578B: ${config.type}`);
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
__name(validateNewConfig, "validateNewConfig");
|
|
1752
|
-
function getConfigTypeDescription(config) {
|
|
1753
|
-
if (isLocalConfig(config)) {
|
|
1754
|
-
return `\u672C\u5730\u8FDB\u7A0B (${config.command})`;
|
|
1755
|
-
}
|
|
1756
|
-
if ("url" in config) {
|
|
1757
|
-
if ("type" in config && config.type === "streamable-http") {
|
|
1758
|
-
return `Streamable HTTP (${config.url})`;
|
|
1759
|
-
}
|
|
1760
|
-
if ("type" in config && config.type === "sse") {
|
|
1761
|
-
const isModelScope2 = isModelScopeURL(config.url);
|
|
1762
|
-
return `SSE${isModelScope2 ? " (ModelScope)" : ""} (${config.url})`;
|
|
1763
|
-
}
|
|
1764
|
-
const inferredType = inferTransportTypeFromUrl(config.url);
|
|
1765
|
-
const isModelScope = isModelScopeURL(config.url);
|
|
1766
|
-
if (inferredType === "sse" /* SSE */) {
|
|
1767
|
-
return `SSE${isModelScope ? " (ModelScope)" : ""} (${config.url})`;
|
|
1768
|
-
}
|
|
1769
|
-
return `Streamable HTTP (${config.url})`;
|
|
1770
|
-
}
|
|
1771
|
-
return "\u672A\u77E5\u7C7B\u578B";
|
|
1772
|
-
}
|
|
1773
|
-
__name(getConfigTypeDescription, "getConfigTypeDescription");
|
|
1774
|
-
export {
|
|
1775
|
-
ConfigManager,
|
|
1776
|
-
ConfigValidationError,
|
|
1777
|
-
MCPTransportType,
|
|
1778
|
-
configManager,
|
|
1779
|
-
convertLegacyConfigBatch,
|
|
1780
|
-
convertLegacyToNew,
|
|
1781
|
-
getConfigTypeDescription,
|
|
1782
|
-
isModelScopeURL
|
|
1783
|
-
};
|
|
1
|
+
var A=Object.defineProperty;var c=(r,e)=>A(r,"name",{value:e,configurable:!0});import{copyFileSync as N,existsSync as d,readFileSync as L,writeFileSync as _}from"fs";import{dirname as R,resolve as l}from"path";import{fileURLToPath as O}from"url";import*as g from"comment-json";import E from"dayjs";import*as u from"comment-json";function y(r){let e=u.parse(r);return{write(t){e&&typeof e=="object"&&t&&Object.assign(e,t)},toSource(){return u.stringify(e,null,2)}}}c(y,"createJson5Writer");function T(r){return u.parse(r)}c(T,"parseJson5");var k=R(O(import.meta.url)),h={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectInterval:5e3},v=class r{static{c(this,"ConfigManager")}static instance;defaultConfigPath;config=null;currentConfigPath=null;json5Writer=null;statsUpdateLocks=new Map;statsUpdateLockTimeouts=new Map;STATS_UPDATE_TIMEOUT=5e3;eventCallbacks=new Map;constructor(){let e=[l(k,"templates","default","xiaozhi.config.json"),l(k,"..","templates","default","xiaozhi.config.json"),l(process.cwd(),"templates","default","xiaozhi.config.json")];this.defaultConfigPath=e.find(t=>d(t))||e[0]}on(e,t){this.eventCallbacks.has(e)||this.eventCallbacks.set(e,[]),this.eventCallbacks.get(e)?.push(t)}emitEvent(e,t){let o=this.eventCallbacks.get(e);if(o)for(let n of o)try{n(t)}catch{}}getConfigFilePath(){let e=process.env.XIAOZHI_CONFIG_DIR||process.cwd(),t=["xiaozhi.config.json5","xiaozhi.config.jsonc","xiaozhi.config.json"];for(let o of t){let n=l(e,o);if(d(n))return n}return l(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 o of t){let n=l(e,o);if(d(n))return!0}return!1}initConfig(e="json"){if(!d(this.defaultConfigPath))throw new Error(`\u9ED8\u8BA4\u914D\u7F6E\u6A21\u677F\u6587\u4EF6\u4E0D\u5B58\u5728: ${this.defaultConfigPath}`);if(this.configExists())throw new Error("\u914D\u7F6E\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u91CD\u590D\u521D\u59CB\u5316");let t=process.env.XIAOZHI_CONFIG_DIR||process.cwd(),o=`xiaozhi.config.${e}`,n=l(t,o);N(this.defaultConfigPath,n),this.config=null,this.json5Writer=null}loadConfig(){if(!this.configExists()){let e=new Error("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u8FD0\u884C xiaozhi init \u521D\u59CB\u5316\u914D\u7F6E");throw this.emitEvent("config:error",{error:e,operation:"loadConfig"}),e}try{let e=this.getConfigFilePath();this.currentConfigPath=e;let t=this.getConfigFileFormat(e),n=L(e,"utf8").replace(/^\uFEFF/,""),i;switch(t){case"json5":i=T(n),this.json5Writer=y(n);break;case"jsonc":i=g.parse(n);break;default:i=JSON.parse(n);break}return this.validateConfig(i),i}catch(e){throw this.emitEvent("config:error",{error:e instanceof Error?e:new Error(String(e)),operation:"loadConfig"}),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)){for(let o of t.mcpEndpoint)if(typeof o!="string"||o.trim()==="")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u6570\u7EC4\u4E2D\u7684\u6BCF\u4E2A\u5143\u7D20\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}else throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpEndpoint \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6216\u5B57\u7B26\u4E32\u6570\u7EC4");if(!t.mcpServers||typeof t.mcpServers!="object")throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers \u5B57\u6BB5\u65E0\u6548");for(let[o,n]of Object.entries(t.mcpServers))if(!n||typeof n!="object")throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF\uFF1AmcpServers.${o} \u65E0\u6548`)}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)){for(let o of e)if(!o||typeof o!="string")throw new Error("MCP \u7AEF\u70B9\u6570\u7EC4\u4E2D\u7684\u6BCF\u4E2A\u5143\u7D20\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32")}let t=this.getMutableConfig();t.mcpEndpoint=e,this.saveConfig(t),this.emitEvent("config:updated",{type:"endpoint",timestamp:new Date})}addMcpEndpoint(e){if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let t=this.getMutableConfig(),o=this.getMcpEndpoints();if(o.includes(e))throw new Error(`MCP \u7AEF\u70B9 ${e} \u5DF2\u5B58\u5728`);let n=[...o,e];t.mcpEndpoint=n,this.saveConfig(t)}removeMcpEndpoint(e){if(!e||typeof e!="string")throw new Error("MCP \u7AEF\u70B9\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");let t=this.getMutableConfig(),o=this.getMcpEndpoints();if(o.indexOf(e)===-1)throw new Error(`MCP \u7AEF\u70B9 ${e} \u4E0D\u5B58\u5728`);let i=o.filter(s=>s!==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 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.getMutableConfig();if(!t.mcpServers[e])throw new Error(`\u670D\u52A1 ${e} \u4E0D\u5B58\u5728`);if(delete t.mcpServers[e],t.mcpServerConfig?.[e]&&delete t.mcpServerConfig[e],t.customMCP?.tools){let o=t.customMCP.tools.filter(n=>n.handler?.type==="mcp"&&n.handler.config?.serviceName===e);for(let n of o){let i=t.customMCP.tools.findIndex(s=>s.name===n.name);i!==-1&&t.customMCP.tools.splice(i,1)}t.customMCP.tools.length===0&&(t.customMCP=void 0)}this.saveConfig(t),this.emitEvent("config:updated",{type:"customMCP",timestamp:new Date})}updateConfig(e){let t=this.getMutableConfig();if(e.mcpEndpoint!==void 0&&(t.mcpEndpoint=e.mcpEndpoint),e.mcpServers){let o={...t.mcpServers};for(let[n,i]of Object.entries(e.mcpServers))t.mcpServers[n]=i;for(let n of Object.keys(o))n in e.mcpServers||(delete t.mcpServers[n],t.mcpServerConfig?.[n]&&delete t.mcpServerConfig[n])}if(e.connection&&(t.connection||(t.connection={}),Object.assign(t.connection,e.connection)),e.modelscope&&(t.modelscope||(t.modelscope={}),Object.assign(t.modelscope,e.modelscope)),e.webUI&&(t.webUI||(t.webUI={}),Object.assign(t.webUI,e.webUI)),e.mcpServerConfig)for(let[o,n]of Object.entries(e.mcpServerConfig))t.mcpServerConfig?.[o]&&(t.mcpServerConfig[o]=n);if(e.platforms)for(let[o,n]of Object.entries(e.platforms))t.platforms||(t.platforms={}),t.platforms[o]=n;this.saveConfig(t),this.emitEvent("config:updated",{type:"config",timestamp:new Date})}updateServerToolsConfig(e,t){let o=this.getMutableConfig();o.mcpServerConfig||(o.mcpServerConfig={}),Object.keys(t).length===0?delete o.mcpServerConfig[e]:o.mcpServerConfig[e]={tools:t},this.saveConfig(o),this.emitEvent("config:updated",{type:"serverTools",serviceName:e,timestamp:new Date})}removeServerToolsConfig(e){let o={...this.getConfig()};o.mcpServerConfig&&(delete o.mcpServerConfig[e],this.saveConfig(o))}cleanupInvalidServerToolsConfig(){let e=this.getMutableConfig();if(!e.mcpServerConfig)return;let t=Object.keys(e.mcpServers),n=Object.keys(e.mcpServerConfig).filter(i=>!t.includes(i));if(n.length>0){for(let i of n)delete e.mcpServerConfig[i];this.saveConfig(e)}}setToolEnabled(e,t,o,n){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:o,...n&&{description:n}},this.saveConfig(i)}saveConfig(e){try{this.validateConfig(e);let t;this.currentConfigPath?t=this.currentConfigPath:(t=this.getConfigFilePath(),this.currentConfigPath=t);let o=this.getConfigFileFormat(t),n;switch(o){case"json5":try{this.json5Writer?(this.json5Writer.write(e),n=this.json5Writer.toSource()):n=g.stringify(e,null,2)}catch{n=g.stringify(e,null,2)}break;case"jsonc":try{n=g.stringify(e,null,2)}catch{n=JSON.stringify(e,null,2)}break;default:n=JSON.stringify(e,null,2);break}_(t,n,"utf8"),this.config=e,this.notifyConfigUpdate(e)}catch(t){throw this.emitEvent("config:error",{error:t instanceof Error?t:new Error(String(t)),operation:"saveConfig"}),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??h.heartbeatInterval,heartbeatTimeout:t.heartbeatTimeout??h.heartbeatTimeout,reconnectInterval:t.reconnectInterval??h.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),this.emitEvent("config:updated",{type:"connection",timestamp:new Date})}async updateToolUsageStats(e,t,o){try{if(typeof t=="string"&&o){let n=e,i=t,s=o;await Promise.all([this._updateMCPServerToolStats(n,i,s),this.updateCustomMCPToolStats(n,i,s)])}else{let n=e,i=t,s=new Date().toISOString();await this.updateCustomMCPToolStats(n,s,i)}}catch{if(typeof t=="string"&&o){let i=e,s=t}else{let i=e}}}async updateMCPServerToolStats(e,t,o,n=!0){await this._updateMCPServerToolStats(e,t,o,n)}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),this.emitEvent("config:updated",{type:"modelscope",timestamp:new Date})}setModelScopeApiKey(e){if(!e||typeof e!="string")throw new Error("API Key \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");this.updateModelScopeConfig({apiKey:e})}getCustomMCPConfig(){return this.getConfig().customMCP||null}getCustomMCPTools(){let e=this.getCustomMCPConfig();return!e||!e.tools?[]:e.tools}validateCustomMCPTools(e){if(!Array.isArray(e))return!1;for(let t of e)if(!t.name||typeof t.name!="string"||!t.description||typeof t.description!="string"||!t.inputSchema||typeof t.inputSchema!="object"||!t.handler||typeof t.handler!="object"||!["proxy","function","http","script","chain","mcp"].includes(t.handler.type)||!this.validateHandlerConfig(t.name,t.handler))return!1;return!0}validateHandlerConfig(e,t){switch(t.type){case"proxy":return this.validateProxyHandler(e,t);case"http":return this.validateHttpHandler(e,t);case"function":return this.validateFunctionHandler(e,t);case"script":return this.validateScriptHandler(e,t);case"chain":return this.validateChainHandler(e,t);case"mcp":return this.validateMCPHandler(e,t);default:return!1}}validateProxyHandler(e,t){return!(!t.platform||!["coze","openai","anthropic","custom"].includes(t.platform)||!t.config||typeof t.config!="object"||t.platform==="coze"&&!t.config.workflow_id&&!t.config.bot_id)}validateHttpHandler(e,t){if(!t.url||typeof t.url!="string")return!1;try{new URL(t.url)}catch{return!1}return!(t.method&&!["GET","POST","PUT","DELETE","PATCH"].includes(t.method))}validateFunctionHandler(e,t){return!(!t.module||typeof t.module!="string"||!t.function||typeof t.function!="string")}validateScriptHandler(e,t){return!(!t.script||typeof t.script!="string"||t.interpreter&&!["node","python","bash"].includes(t.interpreter))}validateChainHandler(e,t){return!(!t.tools||!Array.isArray(t.tools)||t.tools.length===0||!["sequential","parallel"].includes(t.mode)||!["stop","continue","retry"].includes(t.error_handling))}validateMCPHandler(e,t){return!(!t.config||typeof t.config!="object"||!t.config.serviceName||typeof t.config.serviceName!="string"||!t.config.toolName||typeof t.config.toolName!="string")}hasValidCustomMCPTools(){try{let e=this.getCustomMCPTools();return e.length===0?!1:this.validateCustomMCPTools(e)}catch{return!1}}addCustomMCPTool(e){if(!e||typeof e!="object")throw new Error("\u5DE5\u5177\u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A");let t=this.getMutableConfig();if(t.customMCP||(t.customMCP={tools:[]}),t.customMCP.tools.find(n=>n.name===e.name))throw new Error(`\u5DE5\u5177 "${e.name}" \u5DF2\u5B58\u5728`);if(!this.validateCustomMCPTools([e]))throw new Error("\u5DE5\u5177\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");t.customMCP.tools.unshift(e),this.saveConfig(t)}async addCustomMCPTools(e){if(!Array.isArray(e))throw new Error("\u5DE5\u5177\u914D\u7F6E\u5FC5\u987B\u662F\u6570\u7EC4");if(e.length===0)return;let t=this.getMutableConfig();t.customMCP||(t.customMCP={tools:[]});let o=new Set(t.customMCP.tools.map(i=>i.name)),n=e.filter(i=>!o.has(i.name));if(n.length>0){if(!this.validateCustomMCPTools(n))throw new Error("\u5DE5\u5177\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");t.customMCP.tools.push(...n),this.saveConfig(t),this.emitEvent("config:updated",{type:"customMCP",timestamp:new Date})}}removeCustomMCPTool(e){if(!e||typeof e!="string")throw new Error("\u5DE5\u5177\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");let t=this.getMutableConfig();if(!t.customMCP||!t.customMCP.tools)throw new Error("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49 MCP \u5DE5\u5177");let o=t.customMCP.tools.findIndex(n=>n.name===e);if(o===-1)throw new Error(`\u5DE5\u5177 "${e}" \u4E0D\u5B58\u5728`);t.customMCP.tools.splice(o,1),this.saveConfig(t)}updateCustomMCPTool(e,t){if(!e||typeof e!="string")throw new Error("\u5DE5\u5177\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");if(!t||typeof t!="object")throw new Error("\u66F4\u65B0\u540E\u7684\u5DE5\u5177\u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A");let o=this.getMutableConfig();if(!o.customMCP||!o.customMCP.tools)throw new Error("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49 MCP \u5DE5\u5177");let n=o.customMCP.tools.findIndex(i=>i.name===e);if(n===-1)throw new Error(`\u5DE5\u5177 "${e}" \u4E0D\u5B58\u5728`);if(!this.validateCustomMCPTools([t]))throw new Error("\u66F4\u65B0\u540E\u7684\u5DE5\u5177\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");o.customMCP.tools[n]=t,this.saveConfig(o)}updateCustomMCPTools(e){if(!Array.isArray(e))throw new Error("\u5DE5\u5177\u914D\u7F6E\u5FC5\u987B\u662F\u6570\u7EC4");if(!this.validateCustomMCPTools(e))throw new Error("\u5DE5\u5177\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");let t=this.getMutableConfig();t.customMCP||(t.customMCP={tools:[]}),t.customMCP.tools=e,this.saveConfig(t),this.emitEvent("config:updated",{type:"customMCP",timestamp:new Date})}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)}catch{}}updateWebUIConfig(e){let t=this.getMutableConfig();t.webUI||(t.webUI={}),Object.assign(t.webUI,e),this.saveConfig(t),this.emitEvent("config:updated",{type:"webui",timestamp:new Date})}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})}updatePlatformConfig(e,t){let o=this.getMutableConfig();o.platforms||(o.platforms={}),o.platforms[e]=t,this.saveConfig(o),this.emitEvent("config:updated",{type:"platform",platformName:e,timestamp:new Date})}getCozePlatformConfig(){let t=this.getConfig().platforms?.coze;return!t||!t.token?null:{token:t.token}}getCozeToken(){return this.getCozePlatformConfig()?.token||null}setCozePlatformConfig(e){if(!e.token||typeof e.token!="string"||e.token.trim()==="")throw new Error("\u6263\u5B50 API Token \u4E0D\u80FD\u4E3A\u7A7A");this.updatePlatformConfig("coze",{token:e.token.trim()})}isCozeConfigValid(){let e=this.getCozePlatformConfig();return e!==null&&typeof e.token=="string"&&e.token.trim()!==""}async _updateMCPServerToolStats(e,t,o,n=!0){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:!0});let s=i.mcpServerConfig[e].tools[t],m=s.usageCount||0,p=s.lastUsedTime;n&&(s.usageCount=m+1),(!p||new Date(o)>new Date(p))&&(s.lastUsedTime=E(o).format("YYYY-MM-DD HH:mm:ss")),this.saveConfig(i)}async updateCustomMCPToolStats(e,t,o){try{let n,i,s=!0,m;if(typeof o=="string"){let C=e;n=`${C}__${t}`,i=o,m=`${C}/${t}`}else n=e,i=t,s=o||!0,m=n;let p=this.getCustomMCPTools(),S=p.findIndex(C=>C.name===n);if(S===-1)return;let M=[...p],f=M[S];f.stats||(f.stats={});let H=f.stats.usageCount||0,w=f.stats.lastUsedTime;s&&(f.stats.usageCount=H+1),(!w||new Date(i)>new Date(w))&&(f.stats.lastUsedTime=E(i).format("YYYY-MM-DD HH:mm:ss")),await this.updateCustomMCPTools(M)}catch{if(typeof o=="string"){let i=e,s=t}else{let i=e}}}async acquireStatsUpdateLock(e){if(this.statsUpdateLocks.has(e))return!1;let t=new Promise(n=>{});this.statsUpdateLocks.set(e,t);let o=setTimeout(()=>{this.releaseStatsUpdateLock(e)},this.STATS_UPDATE_TIMEOUT);return this.statsUpdateLockTimeouts.set(e,o),!0}releaseStatsUpdateLock(e){this.statsUpdateLocks.delete(e);let t=this.statsUpdateLockTimeouts.get(e);t&&(clearTimeout(t),this.statsUpdateLockTimeouts.delete(e))}async updateToolUsageStatsWithLock(e,t=!0){let o=`custommcp_${e}`;if(await this.acquireStatsUpdateLock(o))try{await this.updateToolUsageStats(e,t)}catch(n){throw n}finally{this.releaseStatsUpdateLock(o)}}async updateMCPServerToolStatsWithLock(e,t,o,n=!0){let i=`mcpserver_${e}_${t}`;if(await this.acquireStatsUpdateLock(i))try{await this.updateMCPServerToolStats(e,t,o,n)}catch(s){throw s}finally{this.releaseStatsUpdateLock(i)}}clearAllStatsUpdateLocks(){let e=this.statsUpdateLocks.size;this.statsUpdateLocks.clear();for(let t of this.statsUpdateLockTimeouts.values())clearTimeout(t);this.statsUpdateLockTimeouts.clear(),e>0}getStatsUpdateLocks(){return Array.from(this.statsUpdateLocks.keys())}getToolCallLogConfig(){return this.getConfig().toolCallLog||{}}updateToolCallLogConfig(e){let t=this.getMutableConfig();t.toolCallLog||(t.toolCallLog={}),Object.assign(t.toolCallLog,e),this.saveConfig(t)}getConfigDir(){return process.env.XIAOZHI_CONFIG_DIR||process.cwd()}},ee=v.getInstance();import{isAbsolute as D,resolve as F}from"path";var a=class extends Error{constructor(t,o){super(t);this.configName=o;this.name="ConfigValidationError"}static{c(this,"ConfigValidationError")}},U=(o=>(o.STDIO="stdio",o.SSE="sse",o.STREAMABLE_HTTP="streamable-http",o))(U||{});function b(r){try{let t=new URL(r).pathname;return t.endsWith("/sse")?"sse":(t.endsWith("/mcp"),"streamable-http")}catch{return"streamable-http"}}c(b,"inferTransportTypeFromUrl");function W(r,e){try{if(!r||typeof r!="string")throw new a("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(!e||typeof e!="object")throw new a("\u914D\u7F6E\u5BF9\u8C61\u4E0D\u80FD\u4E3A\u7A7A",r);let t=$(r,e);return B(t),t}catch(t){throw t instanceof a?t:new a(`\u914D\u7F6E\u8F6C\u6362\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`,r)}}c(W,"convertLegacyToNew");function $(r,e){if(j(e))return z(r,e);if("type"in e)switch(e.type){case"sse":return I(r,e);case"streamable-http":return x(r,e);default:throw new a(`\u4E0D\u652F\u6301\u7684\u4F20\u8F93\u7C7B\u578B: ${e.type}`,r)}if("url"in e){if(e.url===void 0||e.url===null)throw new a("\u7F51\u7EDC\u914D\u7F6E\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 url \u5B57\u6BB5",r);if(b(e.url||"")==="sse"){let n={...e,type:"sse"};return I(r,n)}let o={...e,type:"streamable-http"};return x(r,o)}throw new a("\u65E0\u6CD5\u8BC6\u522B\u7684\u914D\u7F6E\u7C7B\u578B",r)}c($,"convertByConfigType");function z(r,e){if(!e.command)throw new a("\u672C\u5730\u914D\u7F6E\u5FC5\u987B\u5305\u542B command \u5B57\u6BB5",r);let t=process.env.XIAOZHI_CONFIG_DIR||process.cwd(),o=(e.args||[]).map(n=>J(n)?F(t,n):n);return{name:r,type:"stdio",command:e.command,args:o,env:e.env,timeout:3e4}}c(z,"convertLocalConfig");function I(r,e){if(e.url===void 0||e.url===null)throw new a("SSE \u914D\u7F6E\u5FC5\u987B\u5305\u542B url \u5B57\u6BB5",r);let t=e.type==="sse"?"sse":b(e.url||""),o=e.url?P(e.url):!1,n={name:r,type:t,url:e.url,timeout:3e4,headers:e.headers};return o&&(n.modelScopeAuth=!0),n}c(I,"convertSSEConfig");function x(r,e){if(e.url===void 0||e.url===null)throw new a("STREAMABLE_HTTP \u914D\u7F6E\u5FC5\u987B\u5305\u542B url \u5B57\u6BB5",r);let t=e.url||"";return{name:r,type:"streamable-http",url:t,timeout:3e4,headers:e.headers}}c(x,"convertStreamableHTTPConfig");function re(r){let e={},t=[];for(let[o,n]of Object.entries(r))try{e[o]=W(o,n)}catch(i){t.push({serviceName:o,error:i instanceof Error?i:new Error(String(i))})}if(t.length>0){let o=t.map(({serviceName:n,error:i})=>`${n}: ${i.message}`).join("; ");throw new a(`\u6279\u91CF\u914D\u7F6E\u8F6C\u6362\u5931\u8D25: ${o}`)}return e}c(re,"convertLegacyConfigBatch");function J(r){return D(r)?!1:!!(r.startsWith("./")||r.startsWith("../")||/\.(js|py|ts|mjs|cjs)$/i.test(r))}c(J,"isRelativePath");function j(r){return"command"in r&&typeof r.command=="string"}c(j,"isLocalConfig");function P(r){try{let t=new URL(r).hostname.toLowerCase();return t.endsWith(".modelscope.net")||t.endsWith(".modelscope.cn")||t==="modelscope.net"||t==="modelscope.cn"}catch{return!1}}c(P,"isModelScopeURL");function B(r){if(!r.name||typeof r.name!="string")throw new a("\u914D\u7F6E\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 name \u5B57\u6BB5");if(r.type&&!Object.values(U).includes(r.type))throw new a(`\u65E0\u6548\u7684\u4F20\u8F93\u7C7B\u578B: ${r.type}`);if(!r.type)throw new a("\u4F20\u8F93\u7C7B\u578B\u672A\u6307\u5B9A\uFF0C\u8BF7\u68C0\u67E5\u914D\u7F6E\u6216\u542F\u7528\u81EA\u52A8\u63A8\u65AD");switch(r.type){case"stdio":if(!r.command)throw new a("STDIO \u914D\u7F6E\u5FC5\u987B\u5305\u542B command \u5B57\u6BB5");break;case"sse":if(r.url===void 0||r.url===null)throw new a("SSE \u914D\u7F6E\u5FC5\u987B\u5305\u542B url \u5B57\u6BB5");break;case"streamable-http":if(r.url===void 0||r.url===null)throw new a("STREAMABLE_HTTP \u914D\u7F6E\u5FC5\u987B\u5305\u542B url \u5B57\u6BB5");break;default:throw new a(`\u4E0D\u652F\u6301\u7684\u4F20\u8F93\u7C7B\u578B: ${r.type}`)}}c(B,"validateNewConfig");function ie(r){if(j(r))return`\u672C\u5730\u8FDB\u7A0B (${r.command})`;if("url"in r){if("type"in r&&r.type==="streamable-http")return`Streamable HTTP (${r.url})`;if("type"in r&&r.type==="sse")return`SSE${P(r.url)?" (ModelScope)":""} (${r.url})`;let e=b(r.url),t=P(r.url);return e==="sse"?`SSE${t?" (ModelScope)":""} (${r.url})`:`Streamable HTTP (${r.url})`}return"\u672A\u77E5\u7C7B\u578B"}c(ie,"getConfigTypeDescription");export{v as ConfigManager,a as ConfigValidationError,U as MCPTransportType,ee as configManager,re as convertLegacyConfigBatch,W as convertLegacyToNew,ie as getConfigTypeDescription,P as isModelScopeURL};
|
|
1784
2
|
//# sourceMappingURL=index.js.map
|