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