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.
- package/dist/Logger.d.ts +94 -0
- package/dist/Logger.js +3 -0
- package/dist/Logger.js.map +1 -0
- package/dist/ProxyMCPServer.d.ts +117 -0
- package/dist/ProxyMCPServer.js +2 -2
- package/dist/ProxyMCPServer.js.map +1 -1
- package/dist/WebServer.js +11 -11
- package/dist/WebServer.js.map +1 -1
- package/dist/WebServerStandalone.js +11 -11
- package/dist/WebServerStandalone.js.map +1 -1
- package/dist/cli.js +11 -11
- package/dist/cli.js.map +1 -1
- package/dist/configManager.js +2 -2
- package/dist/configManager.js.map +1 -1
- package/dist/mcpCommands.js +2 -2
- package/dist/mcpCommands.js.map +1 -1
- package/dist/mcpServerProxy.js +5 -5
- package/dist/mcpServerProxy.js.map +1 -1
- package/dist/package.json +3 -2
- package/dist/services/MCPServer.d.ts +4 -0
- package/dist/services/MCPServer.js +4 -4
- package/dist/services/MCPServer.js.map +1 -1
- package/package.json +26 -32
- package/dist/logger.d.ts +0 -46
- package/dist/logger.js +0 -3
- package/dist/logger.js.map +0 -1
- package/web/README.md +0 -169
package/dist/Logger.d.ts
ADDED
|
@@ -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"]}
|
package/dist/ProxyMCPServer.d.ts
CHANGED
|
@@ -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 };
|
package/dist/ProxyMCPServer.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
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
|