@xiaozhi-client/endpoint 1.9.7-beta.3

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 ADDED
@@ -0,0 +1,846 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/endpoint.ts
5
+ import WebSocket from "ws";
6
+
7
+ // src/types.ts
8
+ var ToolCallErrorCode = /* @__PURE__ */ ((ToolCallErrorCode2) => {
9
+ ToolCallErrorCode2[ToolCallErrorCode2["INVALID_PARAMS"] = -32602] = "INVALID_PARAMS";
10
+ ToolCallErrorCode2[ToolCallErrorCode2["TOOL_NOT_FOUND"] = -32601] = "TOOL_NOT_FOUND";
11
+ ToolCallErrorCode2[ToolCallErrorCode2["SERVICE_UNAVAILABLE"] = -32001] = "SERVICE_UNAVAILABLE";
12
+ ToolCallErrorCode2[ToolCallErrorCode2["TIMEOUT"] = -32002] = "TIMEOUT";
13
+ ToolCallErrorCode2[ToolCallErrorCode2["TOOL_EXECUTION_ERROR"] = -32e3] = "TOOL_EXECUTION_ERROR";
14
+ return ToolCallErrorCode2;
15
+ })(ToolCallErrorCode || {});
16
+ var ToolCallError = class extends Error {
17
+ constructor(code, message, data) {
18
+ super(message);
19
+ this.code = code;
20
+ this.data = data;
21
+ this.name = "ToolCallError";
22
+ }
23
+ static {
24
+ __name(this, "ToolCallError");
25
+ }
26
+ };
27
+ function ensureToolJSONSchema(schema) {
28
+ if (typeof schema === "object" && schema !== null && "type" in schema && schema.type === "object") {
29
+ return schema;
30
+ }
31
+ return {
32
+ type: "object",
33
+ properties: {},
34
+ required: [],
35
+ additionalProperties: true
36
+ };
37
+ }
38
+ __name(ensureToolJSONSchema, "ensureToolJSONSchema");
39
+ var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
40
+ ConnectionState2["DISCONNECTED"] = "disconnected";
41
+ ConnectionState2["CONNECTING"] = "connecting";
42
+ ConnectionState2["CONNECTED"] = "connected";
43
+ ConnectionState2["FAILED"] = "failed";
44
+ return ConnectionState2;
45
+ })(ConnectionState || {});
46
+
47
+ // src/utils.ts
48
+ function sliceEndpoint(endpoint) {
49
+ return `${endpoint.slice(0, 30)}...${endpoint.slice(-10)}`;
50
+ }
51
+ __name(sliceEndpoint, "sliceEndpoint");
52
+ function validateToolCallParams(params) {
53
+ if (!params || typeof params !== "object") {
54
+ throw new Error("\u5DE5\u5177\u8C03\u7528\u53C2\u6570\u5FC5\u987B\u662F\u5BF9\u8C61");
55
+ }
56
+ const p = params;
57
+ if (!p.name || typeof p.name !== "string") {
58
+ throw new Error("\u5DE5\u5177\u540D\u79F0\u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
59
+ }
60
+ const validated = {
61
+ name: p.name
62
+ };
63
+ if (p.arguments !== void 0) {
64
+ if (typeof p.arguments !== "object" || p.arguments === null) {
65
+ throw new Error("\u5DE5\u5177\u53C2\u6570\u5FC5\u987B\u662F\u5BF9\u8C61");
66
+ }
67
+ validated.arguments = p.arguments;
68
+ }
69
+ return validated;
70
+ }
71
+ __name(validateToolCallParams, "validateToolCallParams");
72
+ function isValidEndpointUrl(endpoint) {
73
+ if (!endpoint || typeof endpoint !== "string") {
74
+ return false;
75
+ }
76
+ if (!endpoint.startsWith("ws://") && !endpoint.startsWith("wss://")) {
77
+ return false;
78
+ }
79
+ try {
80
+ new URL(endpoint);
81
+ return true;
82
+ } catch {
83
+ return false;
84
+ }
85
+ }
86
+ __name(isValidEndpointUrl, "isValidEndpointUrl");
87
+ function deepMerge(target, ...sources) {
88
+ if (sources.length === 0) {
89
+ return target;
90
+ }
91
+ const source = sources.shift();
92
+ if (source === void 0) {
93
+ return target;
94
+ }
95
+ for (const key in source) {
96
+ if (typeof source[key] === "object" && source[key] !== null && !Array.isArray(source[key]) && typeof target[key] === "object" && target[key] !== null && !Array.isArray(target[key])) {
97
+ target[key] = deepMerge(
98
+ target[key],
99
+ source[key]
100
+ );
101
+ } else {
102
+ target[key] = source[key];
103
+ }
104
+ }
105
+ return deepMerge(target, ...sources);
106
+ }
107
+ __name(deepMerge, "deepMerge");
108
+ function sleep(ms) {
109
+ return new Promise((resolve) => setTimeout(resolve, ms));
110
+ }
111
+ __name(sleep, "sleep");
112
+ function formatErrorMessage(error) {
113
+ if (error instanceof Error) {
114
+ return error.message;
115
+ }
116
+ if (typeof error === "string") {
117
+ return error;
118
+ }
119
+ return String(error);
120
+ }
121
+ __name(formatErrorMessage, "formatErrorMessage");
122
+
123
+ // src/internal-mcp-manager.ts
124
+ var InternalMCPManagerAdapter = class {
125
+ constructor(config) {
126
+ this.config = config;
127
+ }
128
+ static {
129
+ __name(this, "InternalMCPManagerAdapter");
130
+ }
131
+ tools = /* @__PURE__ */ new Map();
132
+ isInitialized = false;
133
+ /**
134
+ * 初始化并启动所有 MCP 服务
135
+ */
136
+ async initialize() {
137
+ if (this.isInitialized) {
138
+ return;
139
+ }
140
+ for (const [serviceName, serverConfig] of Object.entries(
141
+ this.config.mcpServers
142
+ )) {
143
+ await this._loadServiceTools(serviceName, serverConfig);
144
+ }
145
+ this.isInitialized = true;
146
+ }
147
+ /**
148
+ * 获取所有工具列表
149
+ */
150
+ getAllTools() {
151
+ return Array.from(this.tools.values());
152
+ }
153
+ /**
154
+ * 调用工具
155
+ */
156
+ async callTool(toolName, arguments_) {
157
+ const tool = this.tools.get(toolName);
158
+ if (!tool) {
159
+ return {
160
+ content: [
161
+ {
162
+ type: "text",
163
+ text: `\u5DE5\u5177\u4E0D\u5B58\u5728: ${toolName}`
164
+ }
165
+ ],
166
+ isError: true
167
+ };
168
+ }
169
+ return {
170
+ content: [
171
+ {
172
+ type: "text",
173
+ text: `\u5DE5\u5177 ${toolName} \u8C03\u7528\u6210\u529F\uFF08\u5360\u4F4D\u5B9E\u73B0\uFF09`
174
+ }
175
+ ],
176
+ isError: false
177
+ };
178
+ }
179
+ /**
180
+ * 清理资源
181
+ */
182
+ async cleanup() {
183
+ this.tools.clear();
184
+ this.isInitialized = false;
185
+ }
186
+ /**
187
+ * 加载服务的工具列表
188
+ * 这是一个简化实现,实际应该连接到 MCP 服务获取工具列表
189
+ */
190
+ async _loadServiceTools(serviceName, serverConfig) {
191
+ const toolName = `${serviceName}__tool`;
192
+ this.tools.set(toolName, {
193
+ name: toolName,
194
+ description: `\u6765\u81EA ${serviceName} \u7684\u5DE5\u5177`,
195
+ inputSchema: {
196
+ type: "object",
197
+ properties: {}
198
+ },
199
+ serviceName,
200
+ originalName: "tool",
201
+ enabled: true,
202
+ usageCount: 0,
203
+ lastUsedTime: (/* @__PURE__ */ new Date()).toISOString()
204
+ });
205
+ }
206
+ };
207
+
208
+ // src/endpoint.ts
209
+ var Endpoint = class {
210
+ static {
211
+ __name(this, "Endpoint");
212
+ }
213
+ endpointUrl;
214
+ ws = null;
215
+ connectionStatus = false;
216
+ serverInitialized = false;
217
+ mcpAdapter;
218
+ // 连接状态管理
219
+ connectionState = "disconnected" /* DISCONNECTED */;
220
+ // 最后一次错误信息
221
+ lastError = null;
222
+ // 连接超时定时器
223
+ connectionTimeout = null;
224
+ // 工具调用超时配置
225
+ toolCallTimeout = 3e4;
226
+ reconnectDelay;
227
+ // 重连延迟(毫秒)
228
+ /**
229
+ * 构造函数
230
+ *
231
+ * @param endpointUrl - 小智接入点 URL
232
+ * @param config - Endpoint 配置(包含 mcpServers)
233
+ */
234
+ constructor(endpointUrl, config) {
235
+ this.endpointUrl = endpointUrl;
236
+ this.reconnectDelay = config.reconnectDelay ?? 2e3;
237
+ this.mcpAdapter = new InternalMCPManagerAdapter(config);
238
+ }
239
+ /**
240
+ * 获取 Endpoint URL
241
+ */
242
+ getUrl() {
243
+ return this.endpointUrl;
244
+ }
245
+ /**
246
+ * 获取当前所有工具列表
247
+ */
248
+ getTools() {
249
+ try {
250
+ const allTools = this.mcpAdapter.getAllTools();
251
+ return allTools.map((toolInfo) => ({
252
+ name: toolInfo.name,
253
+ description: toolInfo.description,
254
+ inputSchema: ensureToolJSONSchema(toolInfo.inputSchema)
255
+ }));
256
+ } catch (error) {
257
+ console.error(
258
+ `\u83B7\u53D6\u5DE5\u5177\u5217\u8868\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
259
+ );
260
+ return [];
261
+ }
262
+ }
263
+ /**
264
+ * 连接小智接入点
265
+ */
266
+ async connect() {
267
+ await this.mcpAdapter.initialize();
268
+ if (this.connectionState === "connecting" /* CONNECTING */) {
269
+ throw new Error("\u8FDE\u63A5\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u8FDE\u63A5\u5B8C\u6210");
270
+ }
271
+ this.cleanupConnection();
272
+ return this.attemptConnection();
273
+ }
274
+ /**
275
+ * 尝试建立连接
276
+ */
277
+ async attemptConnection() {
278
+ this.connectionState = "connecting" /* CONNECTING */;
279
+ console.debug(`\u6B63\u5728\u8FDE\u63A5\u5C0F\u667A\u63A5\u5165\u70B9: ${sliceEndpoint(this.endpointUrl)}`);
280
+ return new Promise((resolve, reject) => {
281
+ this.connectionTimeout = setTimeout(() => {
282
+ const error = new Error("\u8FDE\u63A5\u8D85\u65F6 (10000ms)");
283
+ this.handleConnectionError(error);
284
+ reject(error);
285
+ }, 1e4);
286
+ this.ws = new WebSocket(this.endpointUrl);
287
+ this.ws.on("open", () => {
288
+ this.handleConnectionSuccess();
289
+ resolve();
290
+ });
291
+ this.ws.on("message", (data) => {
292
+ try {
293
+ const message = JSON.parse(data.toString());
294
+ this.handleMessage(message);
295
+ } catch (error) {
296
+ console.error("MCP \u6D88\u606F\u89E3\u6790\u9519\u8BEF:", error);
297
+ }
298
+ });
299
+ this.ws.on("close", (code, reason) => {
300
+ this.handleConnectionClose(code, reason.toString());
301
+ });
302
+ this.ws.on("error", (error) => {
303
+ this.handleConnectionError(error);
304
+ reject(error);
305
+ });
306
+ });
307
+ }
308
+ /**
309
+ * 处理连接成功
310
+ */
311
+ handleConnectionSuccess() {
312
+ if (this.connectionTimeout) {
313
+ clearTimeout(this.connectionTimeout);
314
+ this.connectionTimeout = null;
315
+ }
316
+ this.connectionStatus = true;
317
+ this.connectionState = "connected" /* CONNECTED */;
318
+ console.debug("MCP WebSocket \u8FDE\u63A5\u5DF2\u5EFA\u7ACB");
319
+ }
320
+ /**
321
+ * 处理连接错误
322
+ */
323
+ handleConnectionError(error) {
324
+ this.lastError = error.message;
325
+ if (this.connectionTimeout) {
326
+ clearTimeout(this.connectionTimeout);
327
+ this.connectionTimeout = null;
328
+ }
329
+ console.error("MCP WebSocket \u9519\u8BEF:", error.message);
330
+ this.cleanupConnection();
331
+ }
332
+ /**
333
+ * 处理连接关闭
334
+ */
335
+ handleConnectionClose(code, reason) {
336
+ this.connectionStatus = false;
337
+ this.serverInitialized = false;
338
+ this.connectionState = "disconnected" /* DISCONNECTED */;
339
+ console.info(`\u5C0F\u667A\u8FDE\u63A5\u5DF2\u5173\u95ED (\u4EE3\u7801: ${code}, \u539F\u56E0: ${reason})`);
340
+ }
341
+ /**
342
+ * 清理连接资源
343
+ */
344
+ cleanupConnection() {
345
+ if (this.ws) {
346
+ this.ws.removeAllListeners();
347
+ try {
348
+ if (this.ws.readyState === WebSocket.OPEN) {
349
+ this.ws.close(1e3, "Cleaning up connection");
350
+ } else if (this.ws.readyState === WebSocket.CONNECTING) {
351
+ this.ws.terminate();
352
+ }
353
+ } catch (error) {
354
+ console.debug("WebSocket \u5173\u95ED\u65F6\u51FA\u73B0\u9519\u8BEF\uFF08\u5DF2\u5FFD\u7565\uFF09:", error);
355
+ }
356
+ this.ws = null;
357
+ }
358
+ if (this.connectionTimeout) {
359
+ clearTimeout(this.connectionTimeout);
360
+ this.connectionTimeout = null;
361
+ }
362
+ this.connectionStatus = false;
363
+ this.serverInitialized = false;
364
+ this.connectionState = "disconnected" /* DISCONNECTED */;
365
+ }
366
+ /**
367
+ * 处理 MCP 消息
368
+ */
369
+ handleMessage(message) {
370
+ console.debug("\u6536\u5230 MCP \u6D88\u606F:", JSON.stringify(message, null, 2));
371
+ if (!message.method) {
372
+ console.debug("\u6536\u5230\u6CA1\u6709 method \u5B57\u6BB5\u7684\u6D88\u606F\uFF0C\u5FFD\u7565");
373
+ return;
374
+ }
375
+ switch (message.method) {
376
+ case "initialize":
377
+ case "notifications/initialized":
378
+ this.sendResponse(message.id, {
379
+ protocolVersion: "2024-11-05",
380
+ capabilities: {
381
+ tools: { listChanged: true },
382
+ logging: {}
383
+ },
384
+ serverInfo: {
385
+ name: "xiaozhi-mcp-server",
386
+ version: "1.0.0"
387
+ }
388
+ });
389
+ this.serverInitialized = true;
390
+ console.debug("MCP \u670D\u52A1\u5668\u521D\u59CB\u5316\u5B8C\u6210");
391
+ break;
392
+ case "tools/list": {
393
+ const toolsList = this.getTools();
394
+ this.sendResponse(message.id, { tools: toolsList });
395
+ console.debug(`MCP \u5DE5\u5177\u5217\u8868\u5DF2\u53D1\u9001 (${toolsList.length}\u4E2A\u5DE5\u5177)`);
396
+ break;
397
+ }
398
+ case "tools/call": {
399
+ this.handleToolCall(message).catch((error) => {
400
+ console.error("\u5904\u7406\u5DE5\u5177\u8C03\u7528\u65F6\u53D1\u751F\u672A\u6355\u83B7\u9519\u8BEF:", error);
401
+ });
402
+ break;
403
+ }
404
+ case "ping":
405
+ this.sendResponse(message.id, {});
406
+ console.debug("\u56DE\u5E94 MCP ping \u6D88\u606F");
407
+ break;
408
+ default:
409
+ console.warn(`\u672A\u77E5\u7684 MCP \u8BF7\u6C42: ${message.method}`);
410
+ }
411
+ }
412
+ /**
413
+ * 发送响应消息
414
+ */
415
+ sendResponse(id, result) {
416
+ console.debug(
417
+ `\u5C1D\u8BD5\u53D1\u9001\u54CD\u5E94: id=${id}, isConnected=${this.connectionStatus}, wsReadyState=${this.ws?.readyState}`
418
+ );
419
+ if (this.connectionStatus && this.ws?.readyState === WebSocket.OPEN) {
420
+ const response = {
421
+ jsonrpc: "2.0",
422
+ id,
423
+ result
424
+ };
425
+ try {
426
+ this.ws.send(JSON.stringify(response));
427
+ console.debug("\u54CD\u5E94\u5DF2\u53D1\u9001", {
428
+ id,
429
+ responseSize: JSON.stringify(response).length
430
+ });
431
+ } catch (error) {
432
+ console.error("\u53D1\u9001\u54CD\u5E94\u5931\u8D25", {
433
+ id,
434
+ error
435
+ });
436
+ }
437
+ } else {
438
+ console.error("\u65E0\u6CD5\u53D1\u9001\u54CD\u5E94", {
439
+ id,
440
+ isConnected: this.connectionStatus,
441
+ wsReadyState: this.ws?.readyState
442
+ });
443
+ }
444
+ }
445
+ /**
446
+ * 获取服务器状态
447
+ */
448
+ getStatus() {
449
+ const availableTools = this.mcpAdapter.getAllTools().length;
450
+ return {
451
+ connected: this.connectionStatus,
452
+ initialized: this.serverInitialized,
453
+ url: this.endpointUrl,
454
+ availableTools,
455
+ connectionState: this.connectionState,
456
+ lastError: this.lastError
457
+ };
458
+ }
459
+ /**
460
+ * 检查连接状态
461
+ */
462
+ isConnected() {
463
+ return this.connectionStatus;
464
+ }
465
+ /**
466
+ * 主动断开小智连接
467
+ */
468
+ disconnect() {
469
+ console.info("\u4E3B\u52A8\u65AD\u5F00\u5C0F\u667A\u8FDE\u63A5");
470
+ this.cleanupConnection();
471
+ }
472
+ /**
473
+ * 重连小智接入点
474
+ */
475
+ async reconnect() {
476
+ console.info(`\u91CD\u8FDE\u5C0F\u667A\u63A5\u5165\u70B9: ${sliceEndpoint(this.endpointUrl)}`);
477
+ this.disconnect();
478
+ await new Promise((resolve) => setTimeout(resolve, this.reconnectDelay));
479
+ await this.connect();
480
+ }
481
+ /**
482
+ * 处理工具调用请求
483
+ */
484
+ async handleToolCall(request) {
485
+ if (request.id === void 0 || request.id === null) {
486
+ throw new ToolCallError(
487
+ -32602 /* INVALID_PARAMS */,
488
+ "\u8BF7\u6C42 ID \u4E0D\u80FD\u4E3A\u7A7A"
489
+ );
490
+ }
491
+ const requestId = request.id;
492
+ const startTime = Date.now();
493
+ try {
494
+ const params = validateToolCallParams(request.params);
495
+ console.info("\u5F00\u59CB\u5904\u7406\u5DE5\u5177\u8C03\u7528", {
496
+ requestId,
497
+ toolName: params.name,
498
+ hasArguments: !!params.arguments
499
+ });
500
+ const result = await this.executeToolWithTimeout(
501
+ params.name,
502
+ params.arguments || {},
503
+ this.toolCallTimeout
504
+ );
505
+ this.sendResponse(requestId, {
506
+ content: result.content || [
507
+ { type: "text", text: JSON.stringify(result) }
508
+ ],
509
+ isError: result.isError || false
510
+ });
511
+ console.info("\u5DE5\u5177\u8C03\u7528\u6210\u529F", {
512
+ requestId,
513
+ toolName: params.name,
514
+ duration: `${Date.now() - startTime}ms`
515
+ });
516
+ } catch (error) {
517
+ this.handleToolCallError(error, requestId, Date.now() - startTime);
518
+ }
519
+ }
520
+ /**
521
+ * 带超时控制的工具执行
522
+ */
523
+ async executeToolWithTimeout(toolName, arguments_, timeoutMs = 3e4) {
524
+ return new Promise((resolve, reject) => {
525
+ const timeoutId = setTimeout(() => {
526
+ reject(
527
+ new ToolCallError(
528
+ -32002 /* TIMEOUT */,
529
+ `\u5DE5\u5177\u8C03\u7528\u8D85\u65F6 (${timeoutMs}ms): ${toolName}`
530
+ )
531
+ );
532
+ }, timeoutMs);
533
+ this.mcpAdapter.callTool(toolName, arguments_).then((result) => {
534
+ clearTimeout(timeoutId);
535
+ resolve(result);
536
+ }).catch((error) => {
537
+ clearTimeout(timeoutId);
538
+ const errorMessage = error instanceof Error ? error.message : String(error);
539
+ if (errorMessage.includes("\u672A\u627E\u5230\u5DE5\u5177")) {
540
+ reject(
541
+ new ToolCallError(
542
+ -32601 /* TOOL_NOT_FOUND */,
543
+ `\u5DE5\u5177\u4E0D\u5B58\u5728: ${toolName}`
544
+ )
545
+ );
546
+ } else {
547
+ reject(
548
+ new ToolCallError(
549
+ -32e3 /* TOOL_EXECUTION_ERROR */,
550
+ `\u5DE5\u5177\u6267\u884C\u5931\u8D25: ${errorMessage}`
551
+ )
552
+ );
553
+ }
554
+ });
555
+ });
556
+ }
557
+ /**
558
+ * 处理工具调用错误
559
+ */
560
+ handleToolCallError(error, requestId, duration) {
561
+ let errorResponse;
562
+ if (error instanceof ToolCallError) {
563
+ errorResponse = {
564
+ code: error.code,
565
+ message: error.message,
566
+ data: error.data
567
+ };
568
+ } else {
569
+ const errorMessage = error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF";
570
+ errorResponse = {
571
+ code: -32e3 /* TOOL_EXECUTION_ERROR */,
572
+ message: errorMessage,
573
+ data: { originalError: String(error) || "null" }
574
+ };
575
+ }
576
+ this.sendErrorResponse(requestId, errorResponse);
577
+ console.error("\u5DE5\u5177\u8C03\u7528\u5931\u8D25", {
578
+ requestId,
579
+ duration: `${duration}ms`,
580
+ error: errorResponse
581
+ });
582
+ }
583
+ /**
584
+ * 发送错误响应
585
+ */
586
+ sendErrorResponse(id, error) {
587
+ if (this.connectionStatus && this.ws?.readyState === WebSocket.OPEN) {
588
+ const response = {
589
+ jsonrpc: "2.0",
590
+ id,
591
+ error
592
+ };
593
+ this.ws.send(JSON.stringify(response));
594
+ console.debug("\u5DF2\u53D1\u9001\u9519\u8BEF\u54CD\u5E94:", response);
595
+ }
596
+ }
597
+ };
598
+
599
+ // src/manager.ts
600
+ import { EventEmitter } from "events";
601
+ var EndpointManager = class extends EventEmitter {
602
+ /**
603
+ * 构造函数
604
+ *
605
+ * @param config - 可选的配置
606
+ */
607
+ constructor(config) {
608
+ super();
609
+ this.config = config;
610
+ console.debug("[EndpointManager] \u5B9E\u4F8B\u5DF2\u521B\u5EFA\uFF08\u65B0 API\uFF09");
611
+ }
612
+ static {
613
+ __name(this, "EndpointManager");
614
+ }
615
+ endpoints = /* @__PURE__ */ new Map();
616
+ connectionStates = /* @__PURE__ */ new Map();
617
+ /**
618
+ * 添加 Endpoint 实例
619
+ *
620
+ * @param endpoint - Endpoint 实例
621
+ */
622
+ addEndpoint(endpoint) {
623
+ const url = endpoint.getUrl();
624
+ if (this.endpoints.has(url)) {
625
+ console.debug(
626
+ `[EndpointManager] \u63A5\u5165\u70B9 ${sliceEndpoint(url)} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u6DFB\u52A0`
627
+ );
628
+ return;
629
+ }
630
+ console.debug(`[EndpointManager] \u6DFB\u52A0\u63A5\u5165\u70B9: ${sliceEndpoint(url)}`);
631
+ this.endpoints.set(url, endpoint);
632
+ this.connectionStates.set(url, {
633
+ endpoint: url,
634
+ connected: false,
635
+ initialized: false
636
+ });
637
+ this.emit("endpointAdded", { endpoint: url });
638
+ }
639
+ /**
640
+ * 移除 Endpoint 实例
641
+ *
642
+ * @param endpoint - Endpoint 实例
643
+ */
644
+ removeEndpoint(endpoint) {
645
+ const url = endpoint.getUrl();
646
+ if (!this.endpoints.has(url)) {
647
+ console.debug(
648
+ `[EndpointManager] \u63A5\u5165\u70B9 ${sliceEndpoint(url)} \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u79FB\u9664`
649
+ );
650
+ return;
651
+ }
652
+ console.debug(`[EndpointManager] \u79FB\u9664\u63A5\u5165\u70B9: ${sliceEndpoint(url)}`);
653
+ endpoint.disconnect();
654
+ this.endpoints.delete(url);
655
+ this.connectionStates.delete(url);
656
+ this.emit("endpointRemoved", { endpoint: url });
657
+ }
658
+ /**
659
+ * 连接所有 Endpoint
660
+ */
661
+ async connect() {
662
+ console.debug(`[EndpointManager] \u5F00\u59CB\u8FDE\u63A5\u6240\u6709\u63A5\u5165\u70B9\uFF0C\u603B\u6570: ${this.endpoints.size}`);
663
+ const promises = [];
664
+ for (const [url, endpoint] of this.endpoints) {
665
+ promises.push(
666
+ this.connectSingleEndpoint(url, endpoint).catch((error) => {
667
+ console.error(`[EndpointManager] \u8FDE\u63A5\u5931\u8D25: ${sliceEndpoint(url)}`, error);
668
+ const status = this.connectionStates.get(url);
669
+ if (status) {
670
+ status.connected = false;
671
+ status.initialized = false;
672
+ status.lastError = error instanceof Error ? error.message : String(error);
673
+ }
674
+ })
675
+ );
676
+ }
677
+ await Promise.allSettled(promises);
678
+ const connectedCount = Array.from(this.connectionStates.values()).filter(
679
+ (s) => s.connected
680
+ ).length;
681
+ console.info(`[EndpointManager] \u8FDE\u63A5\u5B8C\u6210: \u6210\u529F ${connectedCount}/${this.endpoints.size}`);
682
+ }
683
+ /**
684
+ * 断开所有连接
685
+ */
686
+ async disconnect() {
687
+ console.debug("[EndpointManager] \u5F00\u59CB\u65AD\u5F00\u6240\u6709\u8FDE\u63A5");
688
+ const promises = [];
689
+ for (const endpoint of this.endpoints.values()) {
690
+ promises.push(
691
+ Promise.resolve().then(() => {
692
+ endpoint.disconnect();
693
+ })
694
+ );
695
+ }
696
+ await Promise.allSettled(promises);
697
+ for (const status of this.connectionStates.values()) {
698
+ status.connected = false;
699
+ status.initialized = false;
700
+ }
701
+ console.debug("[EndpointManager] \u6240\u6709\u63A5\u5165\u70B9\u5DF2\u65AD\u5F00\u8FDE\u63A5");
702
+ }
703
+ /**
704
+ * 获取所有 Endpoint URL
705
+ */
706
+ getEndpoints() {
707
+ return Array.from(this.endpoints.keys());
708
+ }
709
+ /**
710
+ * 获取指定 Endpoint 实例
711
+ *
712
+ * @param url - Endpoint URL
713
+ */
714
+ getEndpoint(url) {
715
+ return this.endpoints.get(url);
716
+ }
717
+ /**
718
+ * 获取所有连接状态
719
+ */
720
+ getConnectionStatus() {
721
+ return Array.from(this.connectionStates.values());
722
+ }
723
+ /**
724
+ * 检查是否有任何连接处于连接状态
725
+ */
726
+ isAnyConnected() {
727
+ for (const status of this.connectionStates.values()) {
728
+ if (status.connected) {
729
+ return true;
730
+ }
731
+ }
732
+ return false;
733
+ }
734
+ /**
735
+ * 检查指定端点是否已连接
736
+ *
737
+ * @param url - 端点 URL
738
+ */
739
+ isEndpointConnected(url) {
740
+ const status = this.connectionStates.get(url);
741
+ return status?.connected ?? false;
742
+ }
743
+ /**
744
+ * 获取指定端点的状态
745
+ *
746
+ * @param url - 端点 URL
747
+ */
748
+ getEndpointStatus(url) {
749
+ return this.connectionStates.get(url);
750
+ }
751
+ /**
752
+ * 重连所有端点
753
+ */
754
+ async reconnectAll() {
755
+ console.info("[EndpointManager] \u5F00\u59CB\u91CD\u8FDE\u6240\u6709\u63A5\u5165\u70B9");
756
+ const promises = [];
757
+ for (const [url, endpoint] of this.endpoints) {
758
+ promises.push(
759
+ this.reconnectSingleEndpoint(url, endpoint).catch((error) => {
760
+ console.error(`[EndpointManager] \u91CD\u8FDE\u5931\u8D25: ${sliceEndpoint(url)}`, error);
761
+ })
762
+ );
763
+ }
764
+ await Promise.allSettled(promises);
765
+ }
766
+ /**
767
+ * 重连指定的端点
768
+ *
769
+ * @param url - 要重连的端点 URL
770
+ */
771
+ async reconnectEndpoint(url) {
772
+ const endpoint = this.endpoints.get(url);
773
+ if (!endpoint) {
774
+ throw new Error(`\u63A5\u5165\u70B9\u4E0D\u5B58\u5728: ${sliceEndpoint(url)}`);
775
+ }
776
+ await this.reconnectSingleEndpoint(url, endpoint);
777
+ }
778
+ /**
779
+ * 清除所有端点
780
+ */
781
+ async clearEndpoints() {
782
+ console.debug("[EndpointManager] \u6E05\u9664\u6240\u6709\u63A5\u5165\u70B9");
783
+ await this.disconnect();
784
+ this.endpoints.clear();
785
+ this.connectionStates.clear();
786
+ console.info("[EndpointManager] \u6240\u6709\u63A5\u5165\u70B9\u5DF2\u6E05\u9664");
787
+ }
788
+ /**
789
+ * 清理资源
790
+ */
791
+ async cleanup() {
792
+ console.debug("[EndpointManager] \u5F00\u59CB\u6E05\u7406\u8D44\u6E90");
793
+ await this.clearEndpoints();
794
+ console.debug("[EndpointManager] \u8D44\u6E90\u6E05\u7406\u5B8C\u6210");
795
+ }
796
+ // ==================== 私有方法 ====================
797
+ /**
798
+ * 连接单个端点
799
+ */
800
+ async connectSingleEndpoint(url, endpoint) {
801
+ const status = this.connectionStates.get(url);
802
+ if (!status) {
803
+ throw new Error(`\u7AEF\u70B9\u72B6\u6001\u4E0D\u5B58\u5728: ${sliceEndpoint(url)}`);
804
+ }
805
+ console.debug(`[EndpointManager] \u8FDE\u63A5\u7AEF\u70B9: ${sliceEndpoint(url)}`);
806
+ status.connected = false;
807
+ status.initialized = false;
808
+ await endpoint.connect();
809
+ status.connected = true;
810
+ status.initialized = true;
811
+ status.lastConnected = /* @__PURE__ */ new Date();
812
+ status.lastError = void 0;
813
+ console.info(`[EndpointManager] \u7AEF\u70B9\u8FDE\u63A5\u6210\u529F: ${sliceEndpoint(url)}`);
814
+ }
815
+ /**
816
+ * 重连单个端点
817
+ */
818
+ async reconnectSingleEndpoint(url, endpoint) {
819
+ const status = this.connectionStates.get(url);
820
+ if (!status) {
821
+ throw new Error(`\u7AEF\u70B9\u72B6\u6001\u4E0D\u5B58\u5728: ${sliceEndpoint(url)}`);
822
+ }
823
+ console.debug(`[EndpointManager] \u91CD\u8FDE\u7AEF\u70B9: ${sliceEndpoint(url)}`);
824
+ await endpoint.reconnect();
825
+ status.connected = true;
826
+ status.initialized = true;
827
+ status.lastConnected = /* @__PURE__ */ new Date();
828
+ status.lastError = void 0;
829
+ console.info(`[EndpointManager] \u7AEF\u70B9\u91CD\u8FDE\u6210\u529F: ${sliceEndpoint(url)}`);
830
+ }
831
+ };
832
+ export {
833
+ ConnectionState,
834
+ Endpoint,
835
+ EndpointManager,
836
+ ToolCallError,
837
+ ToolCallErrorCode,
838
+ deepMerge,
839
+ ensureToolJSONSchema,
840
+ formatErrorMessage,
841
+ isValidEndpointUrl,
842
+ sleep,
843
+ sliceEndpoint,
844
+ validateToolCallParams
845
+ };
846
+ //# sourceMappingURL=index.js.map