@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 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