xiaozhi-client 1.6.2 → 1.6.3-beta.1

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.
@@ -0,0 +1,94 @@
1
+ /**
2
+ * 高性能日志记录器,基于 pino 实现
3
+ *
4
+ * 特性:
5
+ * - 支持控制台和文件双重输出
6
+ * - 支持守护进程模式(仅文件输出)
7
+ * - 支持结构化日志记录
8
+ * - 自动日志文件轮转和管理
9
+ * - 高性能异步写入
10
+ * - 完整的错误堆栈跟踪
11
+ */
12
+ declare class Logger {
13
+ private logFilePath;
14
+ private pinoInstance;
15
+ private isDaemonMode;
16
+ private maxLogFileSize;
17
+ private maxLogFiles;
18
+ constructor();
19
+ private createPinoInstance;
20
+ private createOptimizedConsoleStream;
21
+ /**
22
+ * 安全地写入到 stderr,在测试环境中避免错误
23
+ */
24
+ private safeWrite;
25
+ private formatConsoleMessageOptimized;
26
+ /**
27
+ * 初始化日志文件
28
+ * @param projectDir 项目目录
29
+ */
30
+ initLogFile(projectDir: string): void;
31
+ /**
32
+ * 设置是否启用文件日志
33
+ * @param enable 是否启用
34
+ */
35
+ enableFileLogging(enable: boolean): void;
36
+ /**
37
+ * 记录信息级别日志
38
+ * @param message 日志消息
39
+ * @param args 额外参数
40
+ * @example
41
+ * logger.info('用户登录', 'userId', 12345);
42
+ * logger.info({ userId: 12345, action: 'login' }, '用户登录');
43
+ */
44
+ info(message: string, ...args: any[]): void;
45
+ /**
46
+ * 记录结构化信息级别日志
47
+ * @param obj 结构化日志对象
48
+ * @param message 可选的日志消息
49
+ */
50
+ info(obj: object, message?: string): void;
51
+ success(message: string, ...args: any[]): void;
52
+ success(obj: object, message?: string): void;
53
+ warn(message: string, ...args: any[]): void;
54
+ warn(obj: object, message?: string): void;
55
+ error(message: string, ...args: any[]): void;
56
+ error(obj: object, message?: string): void;
57
+ debug(message: string, ...args: any[]): void;
58
+ debug(obj: object, message?: string): void;
59
+ log(message: string, ...args: any[]): void;
60
+ log(obj: object, message?: string): void;
61
+ /**
62
+ * 增强错误对象,提取更多错误信息
63
+ */
64
+ private enhanceErrorObject;
65
+ /**
66
+ * 检查并轮转日志文件(如果需要)
67
+ */
68
+ private rotateLogFileIfNeeded;
69
+ /**
70
+ * 轮转日志文件
71
+ */
72
+ private rotateLogFile;
73
+ /**
74
+ * 清理旧的日志文件
75
+ */
76
+ cleanupOldLogs(): void;
77
+ /**
78
+ * 设置日志文件管理参数
79
+ */
80
+ setLogFileOptions(maxSize: number, maxFiles: number): void;
81
+ /**
82
+ * 创建一个带标签的日志实例(已废弃,直接返回原实例)
83
+ * @param tag 标签(不再使用)
84
+ * @deprecated 标签功能已移除
85
+ */
86
+ withTag(_tag: string): Logger;
87
+ /**
88
+ * 关闭日志文件流
89
+ */
90
+ close(): void;
91
+ }
92
+ declare const logger: Logger;
93
+
94
+ export { Logger, logger };
package/dist/Logger.js ADDED
@@ -0,0 +1,3 @@
1
+ var d=Object.defineProperty;var c=(r,e)=>d(r,"name",{value:e,configurable:!0});import*as o from"fs";import*as s from"path";import h from"chalk";import g from"pino";function u(r){let e=r.getFullYear(),t=String(r.getMonth()+1).padStart(2,"0"),n=String(r.getDate()).padStart(2,"0"),i=String(r.getHours()).padStart(2,"0"),a=String(r.getMinutes()).padStart(2,"0"),l=String(r.getSeconds()).padStart(2,"0");return`${e}-${t}-${n} ${i}:${a}:${l}`}c(u,"formatDateTime");var m=class{static{c(this,"Logger")}logFilePath=null;pinoInstance;isDaemonMode;maxLogFileSize=10*1024*1024;maxLogFiles=5;constructor(){this.isDaemonMode=process.env.XIAOZHI_DAEMON==="true",this.pinoInstance=this.createPinoInstance()}createPinoInstance(){let e=[];if(!this.isDaemonMode){let t=this.createOptimizedConsoleStream();e.push({level:"debug",stream:t})}return this.logFilePath&&e.push({level:"debug",stream:g.destination({dest:this.logFilePath,sync:!1,append:!0,mkdir:!0})}),e.length===0&&e.push({level:"debug",stream:g.destination({dest:"/dev/null"})}),g({level:"debug",timestamp:g.stdTimeFunctions?.isoTime||(()=>`,"time":${Date.now()}`),formatters:{level:c((t,n)=>({level:n}),"level")},base:null,serializers:{err:g.stdSerializers?.err||(t=>t)}},g.multistream(e,{dedupe:!0}))}createOptimizedConsoleStream(){let e=new Map([[20,{name:"DEBUG",color:h.gray}],[30,{name:"INFO",color:h.blue}],[40,{name:"WARN",color:h.yellow}],[50,{name:"ERROR",color:h.red}],[60,{name:"FATAL",color:h.red}]]);return{write:c(t=>{try{let n=JSON.parse(t),i=this.formatConsoleMessageOptimized(n,e);this.safeWrite(`${i}
2
+ `)}catch{this.safeWrite(t)}},"write")}}safeWrite(e){try{process.stderr&&typeof process.stderr.write=="function"?process.stderr.write(e):console&&typeof console.error=="function"&&console.error(e.trim())}catch{}}formatConsoleMessageOptimized(e,t){let n=u(new Date),i=t.get(e.level)||{name:"UNKNOWN",color:c(p=>p,"color")},a=i.color(`[${i.name}]`),l=e.msg;if(e.args&&Array.isArray(e.args)){let p=e.args.map(f=>typeof f=="object"?JSON.stringify(f):String(f)).join(" ");l=`${l} ${p}`}return`[${n}] ${a} ${l}`}initLogFile(e){this.logFilePath=s.join(e,"xiaozhi.log"),this.rotateLogFileIfNeeded(),o.existsSync(this.logFilePath)||o.writeFileSync(this.logFilePath,""),this.pinoInstance=this.createPinoInstance()}enableFileLogging(e){e&&this.logFilePath&&(this.pinoInstance=this.createPinoInstance())}info(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.info(e):this.pinoInstance.info({args:t},e):this.pinoInstance.info(e,t[0]||"")}success(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.info(e):this.pinoInstance.info({args:t},e):this.pinoInstance.info(e,t[0]||"")}warn(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.warn(e):this.pinoInstance.warn({args:t},e):this.pinoInstance.warn(e,t[0]||"")}error(e,...t){if(typeof e=="string")if(t.length===0)this.pinoInstance.error(e);else{let n=t.map(i=>i instanceof Error?{message:i.message,stack:i.stack,name:i.name,cause:i.cause}:i);this.pinoInstance.error({args:n},e)}else{let n=this.enhanceErrorObject(e);this.pinoInstance.error(n,t[0]||"")}}debug(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.debug(e):this.pinoInstance.debug({args:t},e):this.pinoInstance.debug(e,t[0]||"")}log(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.info(e):this.pinoInstance.info({args:t},e):this.pinoInstance.info(e,t[0]||"")}enhanceErrorObject(e){let t={...e};for(let[n,i]of Object.entries(t))i instanceof Error&&(t[n]={message:i.message,stack:i.stack,name:i.name,cause:i.cause});return t}rotateLogFileIfNeeded(){if(!(!this.logFilePath||!o.existsSync(this.logFilePath)))try{o.statSync(this.logFilePath).size>this.maxLogFileSize&&this.rotateLogFile()}catch{}}rotateLogFile(){if(this.logFilePath)try{let e=s.dirname(this.logFilePath),t=s.basename(this.logFilePath,".log");for(let i=this.maxLogFiles-1;i>=1;i--){let a=s.join(e,`${t}.${i}.log`),l=s.join(e,`${t}.${i+1}.log`);o.existsSync(a)&&(i===this.maxLogFiles-1?o.unlinkSync(a):o.renameSync(a,l))}let n=s.join(e,`${t}.1.log`);o.renameSync(this.logFilePath,n)}catch{}}cleanupOldLogs(){if(this.logFilePath)try{let e=s.dirname(this.logFilePath),t=s.basename(this.logFilePath,".log");for(let n=this.maxLogFiles+1;n<=this.maxLogFiles+10;n++){let i=s.join(e,`${t}.${n}.log`);o.existsSync(i)&&o.unlinkSync(i)}}catch{}}setLogFileOptions(e,t){this.maxLogFileSize=e,this.maxLogFiles=t}withTag(e){return this}close(){}},S=new m;export{m as Logger,S as logger};
3
+ //# sourceMappingURL=Logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/Logger.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport chalk from \"chalk\";\nimport pino from \"pino\";\nimport type { Logger as PinoLogger } from \"pino\";\n\n/**\n * 格式化日期时间为 YYYY-MM-DD HH:mm:ss 格式\n * @param date 要格式化的日期对象\n * @returns 格式化后的日期时间字符串\n */\nfunction formatDateTime(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n const hours = String(date.getHours()).padStart(2, \"0\");\n const minutes = String(date.getMinutes()).padStart(2, \"0\");\n const seconds = String(date.getSeconds()).padStart(2, \"0\");\n\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;\n}\n\n/**\n * 高性能日志记录器,基于 pino 实现\n *\n * 特性:\n * - 支持控制台和文件双重输出\n * - 支持守护进程模式(仅文件输出)\n * - 支持结构化日志记录\n * - 自动日志文件轮转和管理\n * - 高性能异步写入\n * - 完整的错误堆栈跟踪\n */\nexport class Logger {\n private logFilePath: string | null = null;\n private pinoInstance: PinoLogger;\n private isDaemonMode: boolean;\n private maxLogFileSize = 10 * 1024 * 1024; // 10MB 默认最大文件大小\n private maxLogFiles = 5; // 最多保留5个日志文件\n\n constructor() {\n // 检查是否为守护进程模式\n this.isDaemonMode = process.env.XIAOZHI_DAEMON === \"true\";\n\n // 创建 pino 实例\n this.pinoInstance = this.createPinoInstance();\n }\n\n private createPinoInstance(): PinoLogger {\n const streams: pino.StreamEntry[] = [];\n\n // 控制台流 - 只在非守护进程模式下添加\n if (!this.isDaemonMode) {\n // 使用高性能的控制台输出流\n const consoleStream = this.createOptimizedConsoleStream();\n streams.push({\n level: \"debug\",\n stream: consoleStream,\n });\n }\n\n // 文件流 - 如果有日志文件路径,使用高性能异步写入\n if (this.logFilePath) {\n streams.push({\n level: \"debug\",\n stream: pino.destination({\n dest: this.logFilePath,\n sync: false, // 异步写入提升性能\n append: true,\n mkdir: true,\n }),\n });\n }\n\n // 如果没有流,创建一个空的流避免错误\n if (streams.length === 0) {\n streams.push({\n level: \"debug\",\n stream: pino.destination({ dest: \"/dev/null\" }),\n });\n }\n\n return pino(\n {\n level: \"debug\",\n // 高性能配置\n timestamp:\n pino.stdTimeFunctions?.isoTime || (() => `,\"time\":${Date.now()}`),\n formatters: {\n // 优化级别格式化\n level: (_label: string, number: number) => ({ level: number }),\n },\n // 禁用不必要的功能以提升性能\n base: null, // 不包含 pid 和 hostname\n serializers: {\n // 优化错误序列化,在测试环境中安全处理\n err: pino.stdSerializers?.err || ((err: any) => err),\n },\n },\n pino.multistream(streams, { dedupe: true })\n );\n }\n\n private createOptimizedConsoleStream() {\n // 预编译级别映射以提升性能\n const levelMap = new Map([\n [20, { name: \"DEBUG\", color: chalk.gray }],\n [30, { name: \"INFO\", color: chalk.blue }],\n [40, { name: \"WARN\", color: chalk.yellow }],\n [50, { name: \"ERROR\", color: chalk.red }],\n [60, { name: \"FATAL\", color: chalk.red }],\n ]);\n\n return {\n write: (chunk: string) => {\n try {\n const logObj = JSON.parse(chunk);\n const message = this.formatConsoleMessageOptimized(logObj, levelMap);\n // 在测试环境中安全地写入\n this.safeWrite(`${message}\\n`);\n } catch (error) {\n // 如果解析失败,直接输出原始内容\n this.safeWrite(chunk);\n }\n },\n };\n }\n\n /**\n * 安全地写入到 stderr,在测试环境中避免错误\n */\n private safeWrite(content: string): void {\n try {\n if (process.stderr && typeof process.stderr.write === \"function\") {\n process.stderr.write(content);\n } else if (console && typeof console.error === \"function\") {\n // 在测试环境中回退到 console.error\n console.error(content.trim());\n }\n } catch (error) {\n // 在极端情况下静默失败,避免测试中断\n }\n }\n\n private formatConsoleMessageOptimized(\n logObj: any,\n levelMap: Map<number, { name: string; color: (text: string) => string }>\n ): string {\n const timestamp = formatDateTime(new Date());\n\n const levelInfo = levelMap.get(logObj.level) || {\n name: \"UNKNOWN\",\n color: (text: string) => text,\n };\n const coloredLevel = levelInfo.color(`[${levelInfo.name}]`);\n\n // 处理结构化日志中的 args,保持兼容性\n let message = logObj.msg;\n if (logObj.args && Array.isArray(logObj.args)) {\n const argsStr = logObj.args\n .map((arg: any) =>\n typeof arg === \"object\" ? JSON.stringify(arg) : String(arg)\n )\n .join(\" \");\n message = `${message} ${argsStr}`;\n }\n\n return `[${timestamp}] ${coloredLevel} ${message}`;\n }\n\n /**\n * 初始化日志文件\n * @param projectDir 项目目录\n */\n initLogFile(projectDir: string): void {\n this.logFilePath = path.join(projectDir, \"xiaozhi.log\");\n\n // 检查并轮转日志文件\n this.rotateLogFileIfNeeded();\n\n // 确保日志文件存在\n if (!fs.existsSync(this.logFilePath)) {\n fs.writeFileSync(this.logFilePath, \"\");\n }\n\n // 重新创建 pino 实例以包含文件流\n this.pinoInstance = this.createPinoInstance();\n }\n\n /**\n * 设置是否启用文件日志\n * @param enable 是否启用\n */\n enableFileLogging(enable: boolean): void {\n // 在 pino 实现中,文件日志的启用/禁用通过重新创建实例来实现\n // 这里保持方法兼容性,但实际上文件日志在 initLogFile 时就已经启用\n if (enable && this.logFilePath) {\n // 重新创建 pino 实例以确保文件流正确配置\n this.pinoInstance = this.createPinoInstance();\n }\n }\n\n /**\n * 记录信息级别日志\n * @param message 日志消息\n * @param args 额外参数\n * @example\n * logger.info('用户登录', 'userId', 12345);\n * logger.info({ userId: 12345, action: 'login' }, '用户登录');\n */\n info(message: string, ...args: any[]): void;\n /**\n * 记录结构化信息级别日志\n * @param obj 结构化日志对象\n * @param message 可选的日志消息\n */\n info(obj: object, message?: string): void;\n info(messageOrObj: string | object, ...args: any[]): void {\n if (typeof messageOrObj === \"string\") {\n if (args.length === 0) {\n this.pinoInstance.info(messageOrObj);\n } else {\n this.pinoInstance.info({ args }, messageOrObj);\n }\n } else {\n // 结构化日志支持\n this.pinoInstance.info(messageOrObj, args[0] || \"\");\n }\n }\n\n success(message: string, ...args: any[]): void;\n success(obj: object, message?: string): void;\n success(messageOrObj: string | object, ...args: any[]): void {\n // success 映射为 info 级别,保持 API 兼容性\n if (typeof messageOrObj === \"string\") {\n if (args.length === 0) {\n this.pinoInstance.info(messageOrObj);\n } else {\n this.pinoInstance.info({ args }, messageOrObj);\n }\n } else {\n this.pinoInstance.info(messageOrObj, args[0] || \"\");\n }\n }\n\n warn(message: string, ...args: any[]): void;\n warn(obj: object, message?: string): void;\n warn(messageOrObj: string | object, ...args: any[]): void {\n if (typeof messageOrObj === \"string\") {\n if (args.length === 0) {\n this.pinoInstance.warn(messageOrObj);\n } else {\n this.pinoInstance.warn({ args }, messageOrObj);\n }\n } else {\n this.pinoInstance.warn(messageOrObj, args[0] || \"\");\n }\n }\n\n error(message: string, ...args: any[]): void;\n error(obj: object, message?: string): void;\n error(messageOrObj: string | object, ...args: any[]): void {\n if (typeof messageOrObj === \"string\") {\n if (args.length === 0) {\n this.pinoInstance.error(messageOrObj);\n } else {\n // 改进错误处理 - 特殊处理 Error 对象\n const errorArgs = args.map((arg) => {\n if (arg instanceof Error) {\n return {\n message: arg.message,\n stack: arg.stack,\n name: arg.name,\n cause: arg.cause,\n };\n }\n return arg;\n });\n this.pinoInstance.error({ args: errorArgs }, messageOrObj);\n }\n } else {\n // 结构化错误日志,自动提取错误信息\n const enhancedObj = this.enhanceErrorObject(messageOrObj);\n this.pinoInstance.error(enhancedObj, args[0] || \"\");\n }\n }\n\n debug(message: string, ...args: any[]): void;\n debug(obj: object, message?: string): void;\n debug(messageOrObj: string | object, ...args: any[]): void {\n if (typeof messageOrObj === \"string\") {\n if (args.length === 0) {\n this.pinoInstance.debug(messageOrObj);\n } else {\n this.pinoInstance.debug({ args }, messageOrObj);\n }\n } else {\n this.pinoInstance.debug(messageOrObj, args[0] || \"\");\n }\n }\n\n log(message: string, ...args: any[]): void;\n log(obj: object, message?: string): void;\n log(messageOrObj: string | object, ...args: any[]): void {\n // log 方法使用 info 级别\n if (typeof messageOrObj === \"string\") {\n if (args.length === 0) {\n this.pinoInstance.info(messageOrObj);\n } else {\n this.pinoInstance.info({ args }, messageOrObj);\n }\n } else {\n this.pinoInstance.info(messageOrObj, args[0] || \"\");\n }\n }\n\n /**\n * 增强错误对象,提取更多错误信息\n */\n private enhanceErrorObject(obj: any): any {\n const enhanced = { ...obj };\n\n // 遍历对象属性,查找 Error 实例\n for (const [key, value] of Object.entries(enhanced)) {\n if (value instanceof Error) {\n enhanced[key] = {\n message: value.message,\n stack: value.stack,\n name: value.name,\n cause: value.cause,\n };\n }\n }\n\n return enhanced;\n }\n\n /**\n * 检查并轮转日志文件(如果需要)\n */\n private rotateLogFileIfNeeded(): void {\n if (!this.logFilePath || !fs.existsSync(this.logFilePath)) {\n return;\n }\n\n try {\n const stats = fs.statSync(this.logFilePath);\n if (stats.size > this.maxLogFileSize) {\n this.rotateLogFile();\n }\n } catch (error) {\n // 忽略文件状态检查错误\n }\n }\n\n /**\n * 轮转日志文件\n */\n private rotateLogFile(): void {\n if (!this.logFilePath) return;\n\n try {\n const logDir = path.dirname(this.logFilePath);\n const logName = path.basename(this.logFilePath, \".log\");\n\n // 移动现有的编号日志文件\n for (let i = this.maxLogFiles - 1; i >= 1; i--) {\n const oldFile = path.join(logDir, `${logName}.${i}.log`);\n const newFile = path.join(logDir, `${logName}.${i + 1}.log`);\n\n if (fs.existsSync(oldFile)) {\n if (i === this.maxLogFiles - 1) {\n // 删除最老的文件\n fs.unlinkSync(oldFile);\n } else {\n fs.renameSync(oldFile, newFile);\n }\n }\n }\n\n // 将当前日志文件重命名为 .1.log\n const firstRotatedFile = path.join(logDir, `${logName}.1.log`);\n fs.renameSync(this.logFilePath, firstRotatedFile);\n } catch (error) {\n // 轮转失败时忽略错误,继续使用当前文件\n }\n }\n\n /**\n * 清理旧的日志文件\n */\n cleanupOldLogs(): void {\n if (!this.logFilePath) return;\n\n try {\n const logDir = path.dirname(this.logFilePath);\n const logName = path.basename(this.logFilePath, \".log\");\n\n // 删除超过最大数量的日志文件\n for (let i = this.maxLogFiles + 1; i <= this.maxLogFiles + 10; i++) {\n const oldFile = path.join(logDir, `${logName}.${i}.log`);\n if (fs.existsSync(oldFile)) {\n fs.unlinkSync(oldFile);\n }\n }\n } catch (error) {\n // 忽略清理错误\n }\n }\n\n /**\n * 设置日志文件管理参数\n */\n setLogFileOptions(maxSize: number, maxFiles: number): void {\n this.maxLogFileSize = maxSize;\n this.maxLogFiles = maxFiles;\n }\n\n /**\n * 创建一个带标签的日志实例(已废弃,直接返回原实例)\n * @param tag 标签(不再使用)\n * @deprecated 标签功能已移除\n */\n withTag(_tag: string): Logger {\n // 不再添加标签,直接返回共享实例\n return this;\n }\n\n /**\n * 关闭日志文件流\n */\n close(): void {\n // pino 实例会自动处理流的关闭\n // 这里保持方法兼容性\n }\n}\n\n// 导出单例实例\nexport const logger = new Logger();\n"],"mappings":"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","names":["fs","path","chalk","pino","formatDateTime","date","year","month","day","hours","minutes","seconds","__name","Logger","streams","consoleStream","pino","_label","number","err","levelMap","chalk","chunk","logObj","message","content","timestamp","levelInfo","text","coloredLevel","argsStr","arg","projectDir","enable","messageOrObj","args","errorArgs","enhancedObj","obj","enhanced","key","value","logDir","logName","oldFile","newFile","firstRotatedFile","i","maxSize","maxFiles","_tag","logger"]}
@@ -8,6 +8,45 @@ declare enum ConnectionState {
8
8
  RECONNECTING = "reconnecting",
9
9
  FAILED = "failed"
10
10
  }
11
+ declare enum ToolCallErrorCode {
12
+ INVALID_PARAMS = -32602,// 无效参数
13
+ TOOL_NOT_FOUND = -32601,// 工具不存在
14
+ TOOL_EXECUTION_ERROR = -32000,// 工具执行错误
15
+ SERVICE_UNAVAILABLE = -32001,// 服务不可用
16
+ TIMEOUT = -32002
17
+ }
18
+ interface ToolCallOptions {
19
+ timeout?: number;
20
+ retryAttempts?: number;
21
+ retryDelay?: number;
22
+ }
23
+ interface PerformanceMetrics {
24
+ totalCalls: number;
25
+ successfulCalls: number;
26
+ failedCalls: number;
27
+ averageResponseTime: number;
28
+ minResponseTime: number;
29
+ maxResponseTime: number;
30
+ successRate: number;
31
+ lastUpdated: Date;
32
+ }
33
+ interface CallRecord {
34
+ id: string;
35
+ toolName: string;
36
+ startTime: Date;
37
+ endTime?: Date;
38
+ duration?: number;
39
+ success: boolean;
40
+ errorCode?: number;
41
+ errorMessage?: string;
42
+ }
43
+ interface RetryConfig {
44
+ maxAttempts: number;
45
+ initialDelay: number;
46
+ maxDelay: number;
47
+ backoffMultiplier: number;
48
+ retryableErrors: ToolCallErrorCode[];
49
+ }
11
50
  interface ReconnectOptions {
12
51
  enabled: boolean;
13
52
  maxAttempts: number;
@@ -41,6 +80,11 @@ declare class ProxyMCPServer {
41
80
  private reconnectOptions;
42
81
  private reconnectState;
43
82
  private connectionTimeout;
83
+ private performanceMetrics;
84
+ private callRecords;
85
+ private readonly maxCallRecords;
86
+ private retryConfig;
87
+ private toolCallConfig;
44
88
  constructor(endpointUrl: string, options?: ProxyMCPServerOptions);
45
89
  /**
46
90
  * 设置 MCPServiceManager 实例
@@ -167,6 +211,79 @@ declare class ProxyMCPServer {
167
211
  * 重置重连状态
168
212
  */
169
213
  resetReconnectState(): void;
214
+ /**
215
+ * 处理工具调用请求
216
+ */
217
+ private handleToolCall;
218
+ /**
219
+ * 验证工具调用参数
220
+ */
221
+ private validateToolCallParams;
222
+ /**
223
+ * 带重试机制的工具执行
224
+ */
225
+ private executeToolWithRetry;
226
+ /**
227
+ * 带超时控制的工具执行
228
+ */
229
+ private executeToolWithTimeout;
230
+ /**
231
+ * 处理工具调用错误
232
+ */
233
+ private handleToolCallError;
234
+ /**
235
+ * 发送错误响应
236
+ */
237
+ private sendErrorResponse;
238
+ /**
239
+ * 记录工具调用开始
240
+ */
241
+ private recordCallStart;
242
+ /**
243
+ * 记录工具调用结束
244
+ */
245
+ private recordCallEnd;
246
+ /**
247
+ * 更新性能指标
248
+ */
249
+ private updatePerformanceMetrics;
250
+ /**
251
+ * 获取性能指标
252
+ */
253
+ getPerformanceMetrics(): PerformanceMetrics;
254
+ /**
255
+ * 获取调用记录
256
+ */
257
+ getCallRecords(limit?: number): CallRecord[];
258
+ /**
259
+ * 重置性能指标
260
+ */
261
+ resetPerformanceMetrics(): void;
262
+ /**
263
+ * 更新工具调用配置
264
+ */
265
+ updateToolCallConfig(config: Partial<ToolCallOptions>): void;
266
+ /**
267
+ * 更新重试配置
268
+ */
269
+ updateRetryConfig(config: Partial<RetryConfig>): void;
270
+ /**
271
+ * 获取当前配置
272
+ */
273
+ getConfiguration(): {
274
+ toolCall: ToolCallOptions;
275
+ retry: RetryConfig;
276
+ };
277
+ /**
278
+ * 获取服务器状态(增强版)
279
+ */
280
+ getEnhancedStatus(): ProxyMCPServerStatus & {
281
+ performance: PerformanceMetrics;
282
+ configuration: {
283
+ toolCall: ToolCallOptions;
284
+ retry: RetryConfig;
285
+ };
286
+ };
170
287
  }
171
288
 
172
289
  export { ProxyMCPServer };
@@ -1,3 +1,3 @@
1
- var f=Object.defineProperty;var r=(o,t)=>f(o,"name",{value:t,configurable:!0});import u from"ws";import g from"fs";import v from"path";import h from"chalk";import{createConsola as C}from"consola";function b(o){let t=o.getFullYear(),e=String(o.getMonth()+1).padStart(2,"0"),n=String(o.getDate()).padStart(2,"0"),i=String(o.getHours()).padStart(2,"0"),c=String(o.getMinutes()).padStart(2,"0"),a=String(o.getSeconds()).padStart(2,"0");return`${t}-${e}-${n} ${i}:${c}:${a}`}r(b,"formatDateTime");var p=class{static{r(this,"Logger")}logFilePath=null;writeStream=null;consolaInstance;isDaemonMode;constructor(){this.isDaemonMode=process.env.XIAOZHI_DAEMON==="true",this.consolaInstance=C({formatOptions:{date:!1,colors:!0,compact:!0},fancy:!1});let t=this.isDaemonMode;this.consolaInstance.setReporters([{log:r(e=>{let n={info:"INFO",success:"SUCCESS",warn:"WARN",error:"ERROR",debug:"DEBUG",log:"LOG"},i={info:h.blue,success:h.green,warn:h.yellow,error:h.red,debug:h.gray,log:r(s=>s,"log")},c=n[e.type]||e.type.toUpperCase(),a=i[e.type]||(s=>s),l=b(new Date),d=a(`[${c}]`),S=`[${l}] ${d} ${e.args.join(" ")}`;if(!t)try{console.error(S)}catch(s){if(s instanceof Error&&s.message?.includes("EPIPE"))return;throw s}},"log")}])}initLogFile(t){this.logFilePath=v.join(t,"xiaozhi.log"),g.existsSync(this.logFilePath)||g.writeFileSync(this.logFilePath,""),this.writeStream=g.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"})}logToFile(t,e,...n){if(this.writeStream){let c=`[${new Date().toISOString()}] [${t.toUpperCase()}] ${e}`,a=n.length>0?`${c} ${n.map(l=>typeof l=="object"?JSON.stringify(l):String(l)).join(" ")}`:c;this.writeStream.write(`${a}
2
- `)}}enableFileLogging(t){t&&!this.writeStream&&this.logFilePath?this.writeStream=g.createWriteStream(this.logFilePath,{flags:"a",encoding:"utf8"}):!t&&this.writeStream&&(this.writeStream.end(),this.writeStream=null)}info(t,...e){this.consolaInstance.info(t,...e),this.logToFile("info",t,...e)}success(t,...e){this.consolaInstance.success(t,...e),this.logToFile("success",t,...e)}warn(t,...e){this.consolaInstance.warn(t,...e),this.logToFile("warn",t,...e)}error(t,...e){this.consolaInstance.error(t,...e),this.logToFile("error",t,...e)}debug(t,...e){this.consolaInstance.debug(t,...e),this.logToFile("debug",t,...e)}log(t,...e){this.consolaInstance.log(t,...e),this.logToFile("log",t,...e)}withTag(t){return this}close(){this.writeStream&&(this.writeStream.end(),this.writeStream=null)}},I=new p;var m=class{static{r(this,"ProxyMCPServer")}endpointUrl;ws=null;logger;isConnected=!1;serverInitialized=!1;tools=new Map;connectionState="disconnected";reconnectOptions;reconnectState={attempts:0,nextInterval:0,timer:null,lastError:null,isManualDisconnect:!1};connectionTimeout=null;constructor(t,e){this.endpointUrl=t,this.logger=new p,this.reconnectOptions={enabled:!0,maxAttempts:10,initialInterval:3e3,maxInterval:3e4,backoffStrategy:"exponential",backoffMultiplier:1.5,timeout:1e4,jitter:!0,...e?.reconnect},this.reconnectState.nextInterval=this.reconnectOptions.initialInterval}setServiceManager(t){this.serviceManager=t,this.logger.info("\u5DF2\u8BBE\u7F6E MCPServiceManager"),this.syncToolsFromServiceManager()}syncToolsFromServiceManager(){let t=this.serviceManager;if(!t){this.logger.debug("MCPServiceManager \u672A\u8BBE\u7F6E\uFF0C\u8DF3\u8FC7\u5DE5\u5177\u540C\u6B65");return}try{let e=t.getAllTools(),n=new Map;for(let i of e)n.set(i.name,{name:i.name,description:i.description,inputSchema:i.inputSchema});this.tools=n,this.logger.info(`\u5DF2\u4ECE MCPServiceManager \u540C\u6B65 ${this.tools.size} \u4E2A\u5DE5\u5177`)}catch(e){this.logger.error(`\u540C\u6B65\u5DE5\u5177\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`)}}addTool(t,e){return this.validateTool(t,e),this.tools.set(t,e),this.logger.debug(`\u5DE5\u5177 '${t}' \u5DF2\u6DFB\u52A0`),this}addTools(t){for(let[e,n]of Object.entries(t))this.addTool(e,n);return this}removeTool(t){return this.tools.delete(t)?this.logger.debug(`\u5DE5\u5177 '${t}' \u5DF2\u79FB\u9664`):this.logger.warn(`\u5C1D\u8BD5\u79FB\u9664\u4E0D\u5B58\u5728\u7684\u5DE5\u5177: '${t}'`),this}getTools(){try{this.syncToolsFromServiceManager()}catch{}return Array.from(this.tools.values())}hasTool(t){return this.tools.has(t)}validateTool(t,e){if(!t||typeof t!="string"||t.trim()==="")throw new Error("\u5DE5\u5177\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(this.tools.has(t))throw new Error(`\u5DE5\u5177 '${t}' \u5DF2\u5B58\u5728`);if(!e||typeof e!="object")throw new Error("\u5DE5\u5177\u5FC5\u987B\u662F\u6709\u6548\u7684\u5BF9\u8C61");if(!e.name||typeof e.name!="string")throw new Error("\u5DE5\u5177\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 'name' \u5B57\u6BB5");if(!e.description||typeof e.description!="string")throw new Error("\u5DE5\u5177\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 'description' \u5B57\u6BB5");if(!e.inputSchema||typeof e.inputSchema!="object")throw new Error("\u5DE5\u5177\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 'inputSchema' \u5B57\u6BB5");if(!e.inputSchema.type||!e.inputSchema.properties)throw new Error("\u5DE5\u5177\u7684 inputSchema \u5FC5\u987B\u5305\u542B 'type' \u548C 'properties' \u5B57\u6BB5")}async connect(){if(this.tools.size===0)throw new Error("\u672A\u914D\u7F6E\u4EFB\u4F55\u5DE5\u5177\u3002\u8BF7\u5728\u8FDE\u63A5\u524D\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u5DE5\u5177\u3002");if(this.connectionState==="connecting")throw new Error("\u8FDE\u63A5\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u8FDE\u63A5\u5B8C\u6210");return this.cleanupConnection(),this.reconnectState.isManualDisconnect=!1,this.attemptConnection()}async attemptConnection(){return this.connectionState="connecting",this.logger.info(`\u6B63\u5728\u8FDE\u63A5 MCP \u63A5\u5165\u70B9: ${this.endpointUrl} (\u5C1D\u8BD5 ${this.reconnectState.attempts+1}/${this.reconnectOptions.maxAttempts})`),new Promise((t,e)=>{this.connectionTimeout=setTimeout(()=>{let n=new Error(`\u8FDE\u63A5\u8D85\u65F6 (${this.reconnectOptions.timeout}ms)`);this.handleConnectionError(n),e(n)},this.reconnectOptions.timeout),this.ws=new u(this.endpointUrl),this.ws.on("open",()=>{this.handleConnectionSuccess(),t()}),this.ws.on("message",n=>{try{let i=JSON.parse(n.toString());this.handleMessage(i)}catch(i){this.logger.error("MCP \u6D88\u606F\u89E3\u6790\u9519\u8BEF:",i)}}),this.ws.on("close",(n,i)=>{this.handleConnectionClose(n,i.toString())}),this.ws.on("error",n=>{this.handleConnectionError(n),e(n)})})}handleConnectionSuccess(){this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.isConnected=!0,this.connectionState="connected",this.reconnectState.attempts=0,this.reconnectState.nextInterval=this.reconnectOptions.initialInterval,this.reconnectState.lastError=null,this.logger.info("MCP WebSocket \u8FDE\u63A5\u5DF2\u5EFA\u7ACB")}handleConnectionError(t){this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.reconnectState.lastError=t,this.logger.error("MCP WebSocket \u9519\u8BEF:",t.message),this.cleanupConnection()}handleConnectionClose(t,e){if(this.isConnected=!1,this.serverInitialized=!1,this.logger.info(`MCP \u8FDE\u63A5\u5DF2\u5173\u95ED (\u4EE3\u7801: ${t}, \u539F\u56E0: ${e})`),this.reconnectState.isManualDisconnect){this.connectionState="disconnected";return}this.shouldReconnect()?this.scheduleReconnect():(this.connectionState="failed",this.logger.warn(`\u5DF2\u8FBE\u5230\u6700\u5927\u91CD\u8FDE\u6B21\u6570 (${this.reconnectOptions.maxAttempts})\uFF0C\u505C\u6B62\u91CD\u8FDE`))}shouldReconnect(){return this.reconnectOptions.enabled&&this.reconnectState.attempts<this.reconnectOptions.maxAttempts&&!this.reconnectState.isManualDisconnect}scheduleReconnect(){this.connectionState="reconnecting",this.reconnectState.attempts++,this.calculateNextInterval(),this.logger.info(`\u5C06\u5728 ${this.reconnectState.nextInterval}ms \u540E\u8FDB\u884C\u7B2C ${this.reconnectState.attempts} \u6B21\u91CD\u8FDE`),this.reconnectState.timer&&clearTimeout(this.reconnectState.timer),this.reconnectState.timer=setTimeout(async()=>{try{await this.attemptConnection()}catch{}},this.reconnectState.nextInterval)}calculateNextInterval(){let t;switch(this.reconnectOptions.backoffStrategy){case"fixed":t=this.reconnectOptions.initialInterval;break;case"linear":t=this.reconnectOptions.initialInterval+this.reconnectState.attempts*this.reconnectOptions.backoffMultiplier*1e3;break;case"exponential":t=this.reconnectOptions.initialInterval*this.reconnectOptions.backoffMultiplier**(this.reconnectState.attempts-1);break;default:t=this.reconnectOptions.initialInterval}if(t=Math.min(t,this.reconnectOptions.maxInterval),this.reconnectOptions.jitter){let e=t*.1,n=(Math.random()-.5)*2*e;t+=n}this.reconnectState.nextInterval=Math.max(t,1e3)}cleanupConnection(){if(this.ws){this.ws.removeAllListeners();try{this.ws.readyState===u.OPEN?this.ws.close(1e3,"Cleaning up connection"):this.ws.readyState===u.CONNECTING&&this.ws.terminate()}catch(t){this.logger.debug("WebSocket \u5173\u95ED\u65F6\u51FA\u73B0\u9519\u8BEF\uFF08\u5DF2\u5FFD\u7565\uFF09:",t)}this.ws=null}this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.isConnected=!1,this.serverInitialized=!1}stopReconnect(){this.reconnectState.timer&&(clearTimeout(this.reconnectState.timer),this.reconnectState.timer=null)}handleMessage(t){this.logger.debug("\u6536\u5230 MCP \u6D88\u606F:",JSON.stringify(t,null,2)),t.method&&this.handleServerRequest(t)}handleServerRequest(t){switch(t.method){case"initialize":this.sendResponse(t.id,{protocolVersion:"2024-11-05",capabilities:{tools:{listChanged:!0},logging:{}},serverInfo:{name:"xiaozhi-mcp-server",version:"1.0.0"}}),this.serverInitialized=!0,this.logger.info("MCP \u670D\u52A1\u5668\u521D\u59CB\u5316\u5B8C\u6210");break;case"tools/list":{let e=this.getTools();this.sendResponse(t.id,{tools:e}),this.logger.info(`MCP \u5DE5\u5177\u5217\u8868\u5DF2\u53D1\u9001 (${e.length}\u4E2A\u5DE5\u5177)`);break}case"ping":this.sendResponse(t.id,{}),this.logger.debug("\u56DE\u5E94 MCP ping \u6D88\u606F");break;default:this.logger.warn(`\u672A\u77E5\u7684 MCP \u8BF7\u6C42: ${t.method}`)}}sendResponse(t,e){if(this.isConnected&&this.ws?.readyState===u.OPEN){let n={jsonrpc:"2.0",id:t,result:e};this.ws.send(JSON.stringify(n))}}getStatus(){return{connected:this.isConnected,initialized:this.serverInitialized,url:this.endpointUrl,availableTools:this.tools.size,connectionState:this.connectionState,reconnectAttempts:this.reconnectState.attempts,lastError:this.reconnectState.lastError?.message||null}}disconnect(){this.logger.info("\u4E3B\u52A8\u65AD\u5F00 MCP \u8FDE\u63A5"),this.reconnectState.isManualDisconnect=!0,this.stopReconnect(),this.cleanupConnection(),this.connectionState="disconnected"}async reconnect(){this.logger.info("\u624B\u52A8\u91CD\u8FDE MCP \u63A5\u5165\u70B9"),this.stopReconnect(),this.reconnectState.attempts=0,this.reconnectState.nextInterval=this.reconnectOptions.initialInterval,this.reconnectState.isManualDisconnect=!1,this.cleanupConnection(),await this.connect()}enableReconnect(){this.reconnectOptions.enabled=!0,this.logger.info("\u81EA\u52A8\u91CD\u8FDE\u5DF2\u542F\u7528")}disableReconnect(){this.reconnectOptions.enabled=!1,this.stopReconnect(),this.logger.info("\u81EA\u52A8\u91CD\u8FDE\u5DF2\u7981\u7528")}updateReconnectOptions(t){this.reconnectOptions={...this.reconnectOptions,...t},this.logger.info("\u91CD\u8FDE\u914D\u7F6E\u5DF2\u66F4\u65B0",t)}getReconnectOptions(){return{...this.reconnectOptions}}resetReconnectState(){this.stopReconnect(),this.reconnectState.attempts=0,this.reconnectState.nextInterval=this.reconnectOptions.initialInterval,this.reconnectState.lastError=null,this.logger.info("\u91CD\u8FDE\u72B6\u6001\u5DF2\u91CD\u7F6E")}};export{m as ProxyMCPServer};
1
+ var y=Object.defineProperty;var m=(h,e)=>y(h,"name",{value:e,configurable:!0});import d from"ws";import*as c from"fs";import*as l from"path";import u from"chalk";import p from"pino";function S(h){let e=h.getFullYear(),t=String(h.getMonth()+1).padStart(2,"0"),i=String(h.getDate()).padStart(2,"0"),n=String(h.getHours()).padStart(2,"0"),s=String(h.getMinutes()).padStart(2,"0"),o=String(h.getSeconds()).padStart(2,"0");return`${e}-${t}-${i} ${n}:${s}:${o}`}m(S,"formatDateTime");var f=class{static{m(this,"Logger")}logFilePath=null;pinoInstance;isDaemonMode;maxLogFileSize=10*1024*1024;maxLogFiles=5;constructor(){this.isDaemonMode=process.env.XIAOZHI_DAEMON==="true",this.pinoInstance=this.createPinoInstance()}createPinoInstance(){let e=[];if(!this.isDaemonMode){let t=this.createOptimizedConsoleStream();e.push({level:"debug",stream:t})}return this.logFilePath&&e.push({level:"debug",stream:p.destination({dest:this.logFilePath,sync:!1,append:!0,mkdir:!0})}),e.length===0&&e.push({level:"debug",stream:p.destination({dest:"/dev/null"})}),p({level:"debug",timestamp:p.stdTimeFunctions?.isoTime||(()=>`,"time":${Date.now()}`),formatters:{level:m((t,i)=>({level:i}),"level")},base:null,serializers:{err:p.stdSerializers?.err||(t=>t)}},p.multistream(e,{dedupe:!0}))}createOptimizedConsoleStream(){let e=new Map([[20,{name:"DEBUG",color:u.gray}],[30,{name:"INFO",color:u.blue}],[40,{name:"WARN",color:u.yellow}],[50,{name:"ERROR",color:u.red}],[60,{name:"FATAL",color:u.red}]]);return{write:m(t=>{try{let i=JSON.parse(t),n=this.formatConsoleMessageOptimized(i,e);this.safeWrite(`${n}
2
+ `)}catch{this.safeWrite(t)}},"write")}}safeWrite(e){try{process.stderr&&typeof process.stderr.write=="function"?process.stderr.write(e):console&&typeof console.error=="function"&&console.error(e.trim())}catch{}}formatConsoleMessageOptimized(e,t){let i=S(new Date),n=t.get(e.level)||{name:"UNKNOWN",color:m(g=>g,"color")},s=n.color(`[${n.name}]`),o=e.msg;if(e.args&&Array.isArray(e.args)){let g=e.args.map(r=>typeof r=="object"?JSON.stringify(r):String(r)).join(" ");o=`${o} ${g}`}return`[${i}] ${s} ${o}`}initLogFile(e){this.logFilePath=l.join(e,"xiaozhi.log"),this.rotateLogFileIfNeeded(),c.existsSync(this.logFilePath)||c.writeFileSync(this.logFilePath,""),this.pinoInstance=this.createPinoInstance()}enableFileLogging(e){e&&this.logFilePath&&(this.pinoInstance=this.createPinoInstance())}info(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.info(e):this.pinoInstance.info({args:t},e):this.pinoInstance.info(e,t[0]||"")}success(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.info(e):this.pinoInstance.info({args:t},e):this.pinoInstance.info(e,t[0]||"")}warn(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.warn(e):this.pinoInstance.warn({args:t},e):this.pinoInstance.warn(e,t[0]||"")}error(e,...t){if(typeof e=="string")if(t.length===0)this.pinoInstance.error(e);else{let i=t.map(n=>n instanceof Error?{message:n.message,stack:n.stack,name:n.name,cause:n.cause}:n);this.pinoInstance.error({args:i},e)}else{let i=this.enhanceErrorObject(e);this.pinoInstance.error(i,t[0]||"")}}debug(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.debug(e):this.pinoInstance.debug({args:t},e):this.pinoInstance.debug(e,t[0]||"")}log(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.info(e):this.pinoInstance.info({args:t},e):this.pinoInstance.info(e,t[0]||"")}enhanceErrorObject(e){let t={...e};for(let[i,n]of Object.entries(t))n instanceof Error&&(t[i]={message:n.message,stack:n.stack,name:n.name,cause:n.cause});return t}rotateLogFileIfNeeded(){if(!(!this.logFilePath||!c.existsSync(this.logFilePath)))try{c.statSync(this.logFilePath).size>this.maxLogFileSize&&this.rotateLogFile()}catch{}}rotateLogFile(){if(this.logFilePath)try{let e=l.dirname(this.logFilePath),t=l.basename(this.logFilePath,".log");for(let n=this.maxLogFiles-1;n>=1;n--){let s=l.join(e,`${t}.${n}.log`),o=l.join(e,`${t}.${n+1}.log`);c.existsSync(s)&&(n===this.maxLogFiles-1?c.unlinkSync(s):c.renameSync(s,o))}let i=l.join(e,`${t}.1.log`);c.renameSync(this.logFilePath,i)}catch{}}cleanupOldLogs(){if(this.logFilePath)try{let e=l.dirname(this.logFilePath),t=l.basename(this.logFilePath,".log");for(let i=this.maxLogFiles+1;i<=this.maxLogFiles+10;i++){let n=l.join(e,`${t}.${i}.log`);c.existsSync(n)&&c.unlinkSync(n)}}catch{}}setLogFileOptions(e,t){this.maxLogFileSize=e,this.maxLogFiles=t}withTag(e){return this}close(){}},C=new f;var a=class extends Error{constructor(t,i,n){super(i);this.code=t;this.data=n;this.name="ToolCallError"}static{m(this,"ToolCallError")}},v=class{static{m(this,"ProxyMCPServer")}endpointUrl;ws=null;logger;isConnected=!1;serverInitialized=!1;tools=new Map;connectionState="disconnected";reconnectOptions;reconnectState={attempts:0,nextInterval:0,timer:null,lastError:null,isManualDisconnect:!1};connectionTimeout=null;performanceMetrics={totalCalls:0,successfulCalls:0,failedCalls:0,averageResponseTime:0,minResponseTime:Number.MAX_VALUE,maxResponseTime:0,successRate:0,lastUpdated:new Date};callRecords=[];maxCallRecords=100;retryConfig={maxAttempts:3,initialDelay:1e3,maxDelay:1e4,backoffMultiplier:2,retryableErrors:[-32001,-32002]};toolCallConfig={timeout:3e4,retryAttempts:3,retryDelay:1e3};constructor(e,t){this.endpointUrl=e,this.logger=C,this.reconnectOptions={enabled:!0,maxAttempts:10,initialInterval:3e3,maxInterval:3e4,backoffStrategy:"exponential",backoffMultiplier:1.5,timeout:1e4,jitter:!0,...t?.reconnect},this.reconnectState.nextInterval=this.reconnectOptions.initialInterval}setServiceManager(e){this.serviceManager=e,this.logger.info("\u5DF2\u8BBE\u7F6E MCPServiceManager"),this.syncToolsFromServiceManager()}syncToolsFromServiceManager(){let e=this.serviceManager;if(!e){this.logger.debug("MCPServiceManager \u672A\u8BBE\u7F6E\uFF0C\u8DF3\u8FC7\u5DE5\u5177\u540C\u6B65");return}try{let t=e.getAllTools(),i=new Map;for(let n of t)i.set(n.name,{name:n.name,description:n.description,inputSchema:n.inputSchema});this.tools=i,this.logger.info(`\u5DF2\u4ECE MCPServiceManager \u540C\u6B65 ${this.tools.size} \u4E2A\u5DE5\u5177`)}catch(t){this.logger.error(`\u540C\u6B65\u5DE5\u5177\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`)}}addTool(e,t){return this.validateTool(e,t),this.tools.set(e,t),this.logger.debug(`\u5DE5\u5177 '${e}' \u5DF2\u6DFB\u52A0`),this}addTools(e){for(let[t,i]of Object.entries(e))this.addTool(t,i);return this}removeTool(e){return this.tools.delete(e)?this.logger.debug(`\u5DE5\u5177 '${e}' \u5DF2\u79FB\u9664`):this.logger.warn(`\u5C1D\u8BD5\u79FB\u9664\u4E0D\u5B58\u5728\u7684\u5DE5\u5177: '${e}'`),this}getTools(){try{this.syncToolsFromServiceManager()}catch{}return Array.from(this.tools.values())}hasTool(e){return this.tools.has(e)}validateTool(e,t){if(!e||typeof e!="string"||e.trim()==="")throw new Error("\u5DE5\u5177\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(this.tools.has(e))throw new Error(`\u5DE5\u5177 '${e}' \u5DF2\u5B58\u5728`);if(!t||typeof t!="object")throw new Error("\u5DE5\u5177\u5FC5\u987B\u662F\u6709\u6548\u7684\u5BF9\u8C61");if(!t.name||typeof t.name!="string")throw new Error("\u5DE5\u5177\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 'name' \u5B57\u6BB5");if(!t.description||typeof t.description!="string")throw new Error("\u5DE5\u5177\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 'description' \u5B57\u6BB5");if(!t.inputSchema||typeof t.inputSchema!="object")throw new Error("\u5DE5\u5177\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 'inputSchema' \u5B57\u6BB5");if(!t.inputSchema.type||!t.inputSchema.properties)throw new Error("\u5DE5\u5177\u7684 inputSchema \u5FC5\u987B\u5305\u542B 'type' \u548C 'properties' \u5B57\u6BB5")}async connect(){if(this.tools.size===0)throw new Error("\u672A\u914D\u7F6E\u4EFB\u4F55\u5DE5\u5177\u3002\u8BF7\u5728\u8FDE\u63A5\u524D\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u5DE5\u5177\u3002");if(this.connectionState==="connecting")throw new Error("\u8FDE\u63A5\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u8FDE\u63A5\u5B8C\u6210");return this.cleanupConnection(),this.reconnectState.isManualDisconnect=!1,this.attemptConnection()}async attemptConnection(){return this.connectionState="connecting",this.logger.info(`\u6B63\u5728\u8FDE\u63A5 MCP \u63A5\u5165\u70B9: ${this.endpointUrl} (\u5C1D\u8BD5 ${this.reconnectState.attempts+1}/${this.reconnectOptions.maxAttempts})`),new Promise((e,t)=>{this.connectionTimeout=setTimeout(()=>{let i=new Error(`\u8FDE\u63A5\u8D85\u65F6 (${this.reconnectOptions.timeout}ms)`);this.handleConnectionError(i),t(i)},this.reconnectOptions.timeout),this.ws=new d(this.endpointUrl),this.ws.on("open",()=>{this.handleConnectionSuccess(),e()}),this.ws.on("message",i=>{try{let n=JSON.parse(i.toString());this.handleMessage(n)}catch(n){this.logger.error("MCP \u6D88\u606F\u89E3\u6790\u9519\u8BEF:",n)}}),this.ws.on("close",(i,n)=>{this.handleConnectionClose(i,n.toString())}),this.ws.on("error",i=>{this.handleConnectionError(i),t(i)})})}handleConnectionSuccess(){this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.isConnected=!0,this.connectionState="connected",this.reconnectState.attempts=0,this.reconnectState.nextInterval=this.reconnectOptions.initialInterval,this.reconnectState.lastError=null,this.logger.info("MCP WebSocket \u8FDE\u63A5\u5DF2\u5EFA\u7ACB")}handleConnectionError(e){this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.reconnectState.lastError=e,this.logger.error("MCP WebSocket \u9519\u8BEF:",e.message),this.cleanupConnection()}handleConnectionClose(e,t){if(this.isConnected=!1,this.serverInitialized=!1,this.logger.info(`MCP \u8FDE\u63A5\u5DF2\u5173\u95ED (\u4EE3\u7801: ${e}, \u539F\u56E0: ${t})`),this.reconnectState.isManualDisconnect){this.connectionState="disconnected";return}this.shouldReconnect()?this.scheduleReconnect():(this.connectionState="failed",this.logger.warn(`\u5DF2\u8FBE\u5230\u6700\u5927\u91CD\u8FDE\u6B21\u6570 (${this.reconnectOptions.maxAttempts})\uFF0C\u505C\u6B62\u91CD\u8FDE`))}shouldReconnect(){return this.reconnectOptions.enabled&&this.reconnectState.attempts<this.reconnectOptions.maxAttempts&&!this.reconnectState.isManualDisconnect}scheduleReconnect(){this.connectionState="reconnecting",this.reconnectState.attempts++,this.calculateNextInterval(),this.logger.info(`\u5C06\u5728 ${this.reconnectState.nextInterval}ms \u540E\u8FDB\u884C\u7B2C ${this.reconnectState.attempts} \u6B21\u91CD\u8FDE`),this.reconnectState.timer&&clearTimeout(this.reconnectState.timer),this.reconnectState.timer=setTimeout(async()=>{try{await this.attemptConnection()}catch{}},this.reconnectState.nextInterval)}calculateNextInterval(){let e;switch(this.reconnectOptions.backoffStrategy){case"fixed":e=this.reconnectOptions.initialInterval;break;case"linear":e=this.reconnectOptions.initialInterval+this.reconnectState.attempts*this.reconnectOptions.backoffMultiplier*1e3;break;case"exponential":e=this.reconnectOptions.initialInterval*this.reconnectOptions.backoffMultiplier**(this.reconnectState.attempts-1);break;default:e=this.reconnectOptions.initialInterval}if(e=Math.min(e,this.reconnectOptions.maxInterval),this.reconnectOptions.jitter){let t=e*.1,i=(Math.random()-.5)*2*t;e+=i}this.reconnectState.nextInterval=Math.max(e,1e3)}cleanupConnection(){if(this.ws){this.ws.removeAllListeners();try{this.ws.readyState===d.OPEN?this.ws.close(1e3,"Cleaning up connection"):this.ws.readyState===d.CONNECTING&&this.ws.terminate()}catch(e){this.logger.debug("WebSocket \u5173\u95ED\u65F6\u51FA\u73B0\u9519\u8BEF\uFF08\u5DF2\u5FFD\u7565\uFF09:",e)}this.ws=null}this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.isConnected=!1,this.serverInitialized=!1}stopReconnect(){this.reconnectState.timer&&(clearTimeout(this.reconnectState.timer),this.reconnectState.timer=null)}handleMessage(e){this.logger.debug("\u6536\u5230 MCP \u6D88\u606F:",JSON.stringify(e,null,2)),e.method&&this.handleServerRequest(e)}handleServerRequest(e){switch(e.method){case"initialize":case"notifications/initialized":this.sendResponse(e.id,{protocolVersion:"2024-11-05",capabilities:{tools:{listChanged:!0},logging:{}},serverInfo:{name:"xiaozhi-mcp-server",version:"1.0.0"}}),this.serverInitialized=!0,this.logger.info("MCP \u670D\u52A1\u5668\u521D\u59CB\u5316\u5B8C\u6210");break;case"tools/list":{let t=this.getTools();this.sendResponse(e.id,{tools:t}),this.logger.info(`MCP \u5DE5\u5177\u5217\u8868\u5DF2\u53D1\u9001 (${t.length}\u4E2A\u5DE5\u5177)`);break}case"tools/call":{this.handleToolCall(e).catch(t=>{this.logger.error("\u5904\u7406\u5DE5\u5177\u8C03\u7528\u65F6\u53D1\u751F\u672A\u6355\u83B7\u9519\u8BEF:",t)});break}case"ping":this.sendResponse(e.id,{}),this.logger.debug("\u56DE\u5E94 MCP ping \u6D88\u606F");break;default:this.logger.warn(`\u672A\u77E5\u7684 MCP \u8BF7\u6C42: ${e.method}`)}}sendResponse(e,t){if(this.isConnected&&this.ws?.readyState===d.OPEN){let i={jsonrpc:"2.0",id:e,result:t};this.ws.send(JSON.stringify(i))}}getStatus(){return{connected:this.isConnected,initialized:this.serverInitialized,url:this.endpointUrl,availableTools:this.tools.size,connectionState:this.connectionState,reconnectAttempts:this.reconnectState.attempts,lastError:this.reconnectState.lastError?.message||null}}disconnect(){this.logger.info("\u4E3B\u52A8\u65AD\u5F00 MCP \u8FDE\u63A5"),this.reconnectState.isManualDisconnect=!0,this.stopReconnect(),this.cleanupConnection(),this.connectionState="disconnected"}async reconnect(){this.logger.info("\u624B\u52A8\u91CD\u8FDE MCP \u63A5\u5165\u70B9"),this.stopReconnect(),this.reconnectState.attempts=0,this.reconnectState.nextInterval=this.reconnectOptions.initialInterval,this.reconnectState.isManualDisconnect=!1,this.cleanupConnection(),await this.connect()}enableReconnect(){this.reconnectOptions.enabled=!0,this.logger.info("\u81EA\u52A8\u91CD\u8FDE\u5DF2\u542F\u7528")}disableReconnect(){this.reconnectOptions.enabled=!1,this.stopReconnect(),this.logger.info("\u81EA\u52A8\u91CD\u8FDE\u5DF2\u7981\u7528")}updateReconnectOptions(e){this.reconnectOptions={...this.reconnectOptions,...e},this.logger.info("\u91CD\u8FDE\u914D\u7F6E\u5DF2\u66F4\u65B0",e)}getReconnectOptions(){return{...this.reconnectOptions}}resetReconnectState(){this.stopReconnect(),this.reconnectState.attempts=0,this.reconnectState.nextInterval=this.reconnectOptions.initialInterval,this.reconnectState.lastError=null,this.logger.info("\u91CD\u8FDE\u72B6\u6001\u5DF2\u91CD\u7F6E")}async handleToolCall(e){let t=String(e.id||"unknown"),i=null;try{let n=this.validateToolCallParams(e.params);i=this.recordCallStart(n.name,t),this.logger.info(`\u5F00\u59CB\u5904\u7406\u5DE5\u5177\u8C03\u7528: ${n.name}`,{requestId:t,toolName:n.name,hasArguments:!!n.arguments});let s=this.serviceManager;if(!s)throw new a(-32001,"MCPServiceManager \u672A\u8BBE\u7F6E");let o=await this.executeToolWithRetry(s,n.name,n.arguments||{});this.sendResponse(t,{content:o.content||[{type:"text",text:JSON.stringify(o)}],isError:o.isError||!1}),i&&this.recordCallEnd(i,!0),this.logger.info(`\u5DE5\u5177\u8C03\u7528\u6210\u529F: ${n.name}`,{requestId:t,duration:i?.duration?`${i.duration}ms`:"unknown"})}catch(n){if(i){let s=n instanceof a?n.code:-32e3,o=n instanceof Error?n.message:"\u672A\u77E5\u9519\u8BEF";this.recordCallEnd(i,!1,s,o)}this.handleToolCallError(n,t,i?.duration||0)}}validateToolCallParams(e){if(!e||typeof e!="object")throw new a(-32602,"\u8BF7\u6C42\u53C2\u6570\u5FC5\u987B\u662F\u5BF9\u8C61");if(!e.name||typeof e.name!="string")throw new a(-32602,"\u5DE5\u5177\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(e.arguments!==void 0&&(typeof e.arguments!="object"||Array.isArray(e.arguments)))throw new a(-32602,"\u5DE5\u5177\u53C2\u6570\u5FC5\u987B\u662F\u5BF9\u8C61");return{name:e.name,arguments:e.arguments}}async executeToolWithRetry(e,t,i){let n=null;for(let s=1;s<=this.retryConfig.maxAttempts;s++)try{return await this.executeToolWithTimeout(e,t,i,this.toolCallConfig.timeout)}catch(o){if(o instanceof a?n=o:n=new a(-32e3,o instanceof Error?o.message:"\u672A\u77E5\u9519\u8BEF"),this.retryConfig.retryableErrors.includes(n.code)&&s<this.retryConfig.maxAttempts){let g=Math.min(this.retryConfig.initialDelay*this.retryConfig.backoffMultiplier**(s-1),this.retryConfig.maxDelay);this.logger.warn(`\u5DE5\u5177\u8C03\u7528\u5931\u8D25\uFF0C\u5C06\u5728 ${g}ms \u540E\u91CD\u8BD5 (${s}/${this.retryConfig.maxAttempts})`,{toolName:t,error:n.message,attempt:s,delay:g}),await new Promise(r=>setTimeout(r,g));continue}break}throw n}async executeToolWithTimeout(e,t,i,n=3e4){return new Promise((s,o)=>{let g=setTimeout(()=>{o(new a(-32002,`\u5DE5\u5177\u8C03\u7528\u8D85\u65F6 (${n}ms): ${t}`))},n);e.callTool(t,i).then(r=>{clearTimeout(g),s(r)}).catch(r=>{clearTimeout(g),r.message?.includes("\u672A\u627E\u5230\u5DE5\u5177")?o(new a(-32601,`\u5DE5\u5177\u4E0D\u5B58\u5728: ${t}`)):r.message?.includes("\u670D\u52A1")&&r.message?.includes("\u4E0D\u53EF\u7528")?o(new a(-32001,r.message)):r.message?.includes("\u6682\u65F6\u4E0D\u53EF\u7528")?o(new a(-32001,r.message)):r.message?.includes("\u6301\u7EED\u4E0D\u53EF\u7528")?o(new a(-32001,r.message)):o(new a(-32e3,`\u5DE5\u5177\u6267\u884C\u5931\u8D25: ${r.message}`))})})}handleToolCallError(e,t,i){let n;e instanceof a?n={code:e.code,message:e.message,data:e.data}:n={code:-32e3,message:e?.message||"\u672A\u77E5\u9519\u8BEF",data:{originalError:e?.toString()||"null"}},this.sendErrorResponse(t,n),this.logger.error("\u5DE5\u5177\u8C03\u7528\u5931\u8D25",{requestId:t,duration:`${i}ms`,error:n})}sendErrorResponse(e,t){if(this.isConnected&&this.ws?.readyState===d.OPEN){let i={jsonrpc:"2.0",id:e,error:t};this.ws.send(JSON.stringify(i)),this.logger.debug("\u5DF2\u53D1\u9001\u9519\u8BEF\u54CD\u5E94:",i)}}recordCallStart(e,t){let i={id:t,toolName:e,startTime:new Date,success:!1};return this.callRecords.push(i),this.callRecords.length>this.maxCallRecords&&this.callRecords.shift(),i}recordCallEnd(e,t,i,n){e.endTime=new Date,e.duration=e.endTime.getTime()-e.startTime.getTime(),e.success=t,e.errorCode=i,e.errorMessage=n,this.updatePerformanceMetrics(e)}updatePerformanceMetrics(e){if(this.performanceMetrics.totalCalls++,e.success?this.performanceMetrics.successfulCalls++:this.performanceMetrics.failedCalls++,e.duration!==void 0){e.duration<this.performanceMetrics.minResponseTime&&(this.performanceMetrics.minResponseTime=e.duration),e.duration>this.performanceMetrics.maxResponseTime&&(this.performanceMetrics.maxResponseTime=e.duration);let t=this.callRecords.filter(n=>n.duration!==void 0).reduce((n,s)=>n+(s.duration||0),0),i=this.callRecords.filter(n=>n.duration!==void 0).length;this.performanceMetrics.averageResponseTime=i>0?t/i:0}this.performanceMetrics.successRate=this.performanceMetrics.totalCalls>0?this.performanceMetrics.successfulCalls/this.performanceMetrics.totalCalls*100:0,this.performanceMetrics.lastUpdated=new Date}getPerformanceMetrics(){return{...this.performanceMetrics}}getCallRecords(e){let t=[...this.callRecords].reverse();return e?t.slice(0,e):t}resetPerformanceMetrics(){this.performanceMetrics={totalCalls:0,successfulCalls:0,failedCalls:0,averageResponseTime:0,minResponseTime:Number.MAX_VALUE,maxResponseTime:0,successRate:0,lastUpdated:new Date},this.callRecords=[]}updateToolCallConfig(e){this.toolCallConfig={...this.toolCallConfig,...e},this.logger.info("\u5DE5\u5177\u8C03\u7528\u914D\u7F6E\u5DF2\u66F4\u65B0",this.toolCallConfig)}updateRetryConfig(e){this.retryConfig={...this.retryConfig,...e},this.logger.info("\u91CD\u8BD5\u914D\u7F6E\u5DF2\u66F4\u65B0",this.retryConfig)}getConfiguration(){return{toolCall:{...this.toolCallConfig},retry:{...this.retryConfig}}}getEnhancedStatus(){return{connected:this.isConnected,initialized:this.serverInitialized,url:this.endpointUrl,availableTools:this.tools.size,connectionState:this.connectionState,reconnectAttempts:this.reconnectState.attempts,lastError:this.reconnectState.lastError?.message||null,performance:this.getPerformanceMetrics(),configuration:this.getConfiguration()}}};export{v as ProxyMCPServer};
3
3
  //# sourceMappingURL=ProxyMCPServer.js.map