xiaozhi-client 1.6.2 → 1.6.3-beta.0
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.js +2 -2
- package/dist/ProxyMCPServer.js.map +1 -1
- package/dist/WebServer.js +8 -8
- package/dist/WebServer.js.map +1 -1
- package/dist/WebServerStandalone.js +8 -8
- package/dist/WebServerStandalone.js.map +1 -1
- package/dist/cli.js +8 -8
- 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 +3 -3
- 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.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 S=Object.defineProperty;var a=(r,t)=>S(r,"name",{value:t,configurable:!0});import g from"ws";import*as o from"fs";import*as s from"path";import p from"chalk";import h from"pino";function y(r){let t=r.getFullYear(),e=String(r.getMonth()+1).padStart(2,"0"),n=String(r.getDate()).padStart(2,"0"),i=String(r.getHours()).padStart(2,"0"),c=String(r.getMinutes()).padStart(2,"0"),l=String(r.getSeconds()).padStart(2,"0");return`${t}-${e}-${n} ${i}:${c}:${l}`}a(y,"formatDateTime");var u=class{static{a(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 t=[];if(!this.isDaemonMode){let e=this.createOptimizedConsoleStream();t.push({level:"debug",stream:e})}return this.logFilePath&&t.push({level:"debug",stream:h.destination({dest:this.logFilePath,sync:!1,append:!0,mkdir:!0})}),t.length===0&&t.push({level:"debug",stream:h.destination({dest:"/dev/null"})}),h({level:"debug",timestamp:h.stdTimeFunctions?.isoTime||(()=>`,"time":${Date.now()}`),formatters:{level:a((e,n)=>({level:n}),"level")},base:null,serializers:{err:h.stdSerializers?.err||(e=>e)}},h.multistream(t,{dedupe:!0}))}createOptimizedConsoleStream(){let t=new Map([[20,{name:"DEBUG",color:p.gray}],[30,{name:"INFO",color:p.blue}],[40,{name:"WARN",color:p.yellow}],[50,{name:"ERROR",color:p.red}],[60,{name:"FATAL",color:p.red}]]);return{write:a(e=>{try{let n=JSON.parse(e),i=this.formatConsoleMessageOptimized(n,t);this.safeWrite(`${i}
|
|
2
|
+
`)}catch{this.safeWrite(e)}},"write")}}safeWrite(t){try{process.stderr&&typeof process.stderr.write=="function"?process.stderr.write(t):console&&typeof console.error=="function"&&console.error(t.trim())}catch{}}formatConsoleMessageOptimized(t,e){let n=y(new Date),i=e.get(t.level)||{name:"UNKNOWN",color:a(m=>m,"color")},c=i.color(`[${i.name}]`),l=t.msg;if(t.args&&Array.isArray(t.args)){let m=t.args.map(d=>typeof d=="object"?JSON.stringify(d):String(d)).join(" ");l=`${l} ${m}`}return`[${n}] ${c} ${l}`}initLogFile(t){this.logFilePath=s.join(t,"xiaozhi.log"),this.rotateLogFileIfNeeded(),o.existsSync(this.logFilePath)||o.writeFileSync(this.logFilePath,""),this.pinoInstance=this.createPinoInstance()}enableFileLogging(t){t&&this.logFilePath&&(this.pinoInstance=this.createPinoInstance())}info(t,...e){typeof t=="string"?e.length===0?this.pinoInstance.info(t):this.pinoInstance.info({args:e},t):this.pinoInstance.info(t,e[0]||"")}success(t,...e){typeof t=="string"?e.length===0?this.pinoInstance.info(t):this.pinoInstance.info({args:e},t):this.pinoInstance.info(t,e[0]||"")}warn(t,...e){typeof t=="string"?e.length===0?this.pinoInstance.warn(t):this.pinoInstance.warn({args:e},t):this.pinoInstance.warn(t,e[0]||"")}error(t,...e){if(typeof t=="string")if(e.length===0)this.pinoInstance.error(t);else{let n=e.map(i=>i instanceof Error?{message:i.message,stack:i.stack,name:i.name,cause:i.cause}:i);this.pinoInstance.error({args:n},t)}else{let n=this.enhanceErrorObject(t);this.pinoInstance.error(n,e[0]||"")}}debug(t,...e){typeof t=="string"?e.length===0?this.pinoInstance.debug(t):this.pinoInstance.debug({args:e},t):this.pinoInstance.debug(t,e[0]||"")}log(t,...e){typeof t=="string"?e.length===0?this.pinoInstance.info(t):this.pinoInstance.info({args:e},t):this.pinoInstance.info(t,e[0]||"")}enhanceErrorObject(t){let e={...t};for(let[n,i]of Object.entries(e))i instanceof Error&&(e[n]={message:i.message,stack:i.stack,name:i.name,cause:i.cause});return e}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 t=s.dirname(this.logFilePath),e=s.basename(this.logFilePath,".log");for(let i=this.maxLogFiles-1;i>=1;i--){let c=s.join(t,`${e}.${i}.log`),l=s.join(t,`${e}.${i+1}.log`);o.existsSync(c)&&(i===this.maxLogFiles-1?o.unlinkSync(c):o.renameSync(c,l))}let n=s.join(t,`${e}.1.log`);o.renameSync(this.logFilePath,n)}catch{}}cleanupOldLogs(){if(this.logFilePath)try{let t=s.dirname(this.logFilePath),e=s.basename(this.logFilePath,".log");for(let n=this.maxLogFiles+1;n<=this.maxLogFiles+10;n++){let i=s.join(t,`${e}.${n}.log`);o.existsSync(i)&&o.unlinkSync(i)}}catch{}}setLogFileOptions(t,e){this.maxLogFileSize=t,this.maxLogFiles=e}withTag(t){return this}close(){}},f=new u;var v=class{static{a(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=f,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 g(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===g.OPEN?this.ws.close(1e3,"Cleaning up connection"):this.ws.readyState===g.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===g.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{v as ProxyMCPServer};
|
|
3
3
|
//# sourceMappingURL=ProxyMCPServer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ProxyMCPServer.ts","../src/logger.ts"],"sourcesContent":["import type { Tool } from \"@modelcontextprotocol/sdk/types.js\";\nimport WebSocket from \"ws\";\nimport { Logger } from \"./logger.js\";\n\nexport type { Tool };\n\n// MCP 消息接口\ninterface MCPMessage {\n jsonrpc: string;\n id?: number | string;\n method?: string;\n params?: any;\n result?: any;\n}\n\n// 连接状态枚举\nenum ConnectionState {\n DISCONNECTED = \"disconnected\",\n CONNECTING = \"connecting\",\n CONNECTED = \"connected\",\n RECONNECTING = \"reconnecting\",\n FAILED = \"failed\",\n}\n\n// 重连配置接口\ninterface ReconnectOptions {\n enabled: boolean; // 是否启用自动重连\n maxAttempts: number; // 最大重连次数\n initialInterval: number; // 初始重连间隔(ms)\n maxInterval: number; // 最大重连间隔(ms)\n backoffStrategy: \"linear\" | \"exponential\" | \"fixed\"; // 退避策略\n backoffMultiplier: number; // 退避倍数\n timeout: number; // 单次连接超时时间(ms)\n jitter: boolean; // 是否添加随机抖动\n}\n\n// 重连状态接口\ninterface ReconnectState {\n attempts: number; // 当前重连次数\n nextInterval: number; // 下次重连间隔\n timer: NodeJS.Timeout | null; // 重连定时器\n lastError: Error | null; // 最后一次错误\n isManualDisconnect: boolean; // 是否为主动断开\n}\n\n// 服务器选项接口\ninterface ProxyMCPServerOptions {\n reconnect?: Partial<ReconnectOptions>;\n}\n\n// 服务器状态接口\ninterface ProxyMCPServerStatus {\n connected: boolean;\n initialized: boolean;\n url: string;\n availableTools: number;\n connectionState: ConnectionState;\n reconnectAttempts: number;\n lastError: string | null;\n}\n\nexport class ProxyMCPServer {\n private endpointUrl: string;\n private ws: WebSocket | null = null;\n private logger: Logger;\n private isConnected = false;\n private serverInitialized = false;\n\n // 工具管理\n private tools: Map<string, Tool> = new Map();\n\n // 连接状态管理\n private connectionState: ConnectionState = ConnectionState.DISCONNECTED;\n\n // 重连配置\n private reconnectOptions: ReconnectOptions;\n\n // 重连状态\n private reconnectState: ReconnectState = {\n attempts: 0,\n nextInterval: 0,\n timer: null,\n lastError: null,\n isManualDisconnect: false,\n };\n\n // 连接超时定时器\n private connectionTimeout: NodeJS.Timeout | null = null;\n\n constructor(endpointUrl: string, options?: ProxyMCPServerOptions) {\n this.endpointUrl = endpointUrl;\n this.logger = new Logger();\n\n // 初始化重连配置\n this.reconnectOptions = {\n enabled: true,\n maxAttempts: 10,\n initialInterval: 3000,\n maxInterval: 30000,\n backoffStrategy: \"exponential\",\n backoffMultiplier: 1.5,\n timeout: 10000,\n jitter: true,\n ...options?.reconnect,\n };\n\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n }\n\n /**\n * 设置 MCPServiceManager 实例\n * @param serviceManager MCPServiceManager 实例\n */\n setServiceManager(serviceManager: any): void {\n // 临时存储在一个变量中,避免类型检查问题\n (this as any).serviceManager = serviceManager;\n this.logger.info(\"已设置 MCPServiceManager\");\n\n // 立即同步工具\n this.syncToolsFromServiceManager();\n }\n\n /**\n * 从 MCPServiceManager 同步工具\n * 优化版本:支持增量同步和错误恢复\n */\n syncToolsFromServiceManager(): void {\n const serviceManager = (this as any).serviceManager;\n if (!serviceManager) {\n this.logger.debug(\"MCPServiceManager 未设置,跳过工具同步\");\n return;\n }\n\n try {\n // 从 MCPServiceManager 获取所有工具\n const allTools = serviceManager.getAllTools();\n\n // 原子性更新:先构建新的工具映射,再替换\n const newTools = new Map<string, Tool>();\n\n for (const toolInfo of allTools) {\n newTools.set(toolInfo.name, {\n name: toolInfo.name,\n description: toolInfo.description,\n inputSchema: toolInfo.inputSchema,\n });\n }\n\n // 原子性替换\n this.tools = newTools;\n\n this.logger.info(`已从 MCPServiceManager 同步 ${this.tools.size} 个工具`);\n } catch (error) {\n this.logger.error(\n `同步工具失败: ${error instanceof Error ? error.message : String(error)}`\n );\n // 同步失败时保持现有工具不变,确保服务可用性\n }\n }\n\n /**\n * 添加单个工具\n * @param name 工具名称\n * @param tool 工具定义\n * @param options 工具选项(可选)\n * @returns 返回 this 支持链式调用\n */\n addTool(name: string, tool: Tool): this {\n this.validateTool(name, tool);\n this.tools.set(name, tool);\n this.logger.debug(`工具 '${name}' 已添加`);\n // TODO: 未来可以使用 options 参数来设置工具的启用状态、元数据等\n return this;\n }\n\n /**\n * 批量添加工具\n * @param tools 工具对象,键为工具名称,值为工具定义\n * @returns 返回 this 支持链式调用\n */\n addTools(tools: Record<string, Tool>): this {\n for (const [name, tool] of Object.entries(tools)) {\n this.addTool(name, tool);\n }\n return this;\n }\n\n /**\n * 移除单个工具\n * @param name 工具名称\n * @returns 返回 this 支持链式调用\n */\n removeTool(name: string): this {\n if (this.tools.delete(name)) {\n this.logger.debug(`工具 '${name}' 已移除`);\n } else {\n this.logger.warn(`尝试移除不存在的工具: '${name}'`);\n }\n return this;\n }\n\n /**\n * 获取当前所有工具列表\n * @returns 工具数组\n */\n getTools(): Tool[] {\n // 每次获取工具时都尝试从 MCPServiceManager 同步\n try {\n this.syncToolsFromServiceManager();\n } catch (error) {\n // 静默处理同步错误,不影响现有工具的返回\n }\n\n return Array.from(this.tools.values());\n }\n\n /**\n * 检查工具是否存在\n * @param name 工具名称\n * @returns 是否存在\n */\n hasTool(name: string): boolean {\n return this.tools.has(name);\n }\n\n /**\n * 验证工具的有效性\n * @param name 工具名称\n * @param tool 工具定义\n */\n private validateTool(name: string, tool: Tool): void {\n if (!name || typeof name !== \"string\" || name.trim() === \"\") {\n throw new Error(\"工具名称必须是非空字符串\");\n }\n\n if (this.tools.has(name)) {\n throw new Error(`工具 '${name}' 已存在`);\n }\n\n if (!tool || typeof tool !== \"object\") {\n throw new Error(\"工具必须是有效的对象\");\n }\n\n // 验证工具的必需字段\n if (!tool.name || typeof tool.name !== \"string\") {\n throw new Error(\"工具必须包含有效的 'name' 字段\");\n }\n\n if (!tool.description || typeof tool.description !== \"string\") {\n throw new Error(\"工具必须包含有效的 'description' 字段\");\n }\n\n if (!tool.inputSchema || typeof tool.inputSchema !== \"object\") {\n throw new Error(\"工具必须包含有效的 'inputSchema' 字段\");\n }\n\n // 验证 inputSchema 的基本结构\n if (!tool.inputSchema.type || !tool.inputSchema.properties) {\n throw new Error(\n \"工具的 inputSchema 必须包含 'type' 和 'properties' 字段\"\n );\n }\n }\n\n /**\n * 连接 MCP 接入点\n * @returns 连接成功后的 Promise\n */\n public async connect(): Promise<void> {\n // 连接前验证\n if (this.tools.size === 0) {\n throw new Error(\"未配置任何工具。请在连接前至少添加一个工具。\");\n }\n\n // 如果正在连接中,等待当前连接完成\n if (this.connectionState === ConnectionState.CONNECTING) {\n throw new Error(\"连接正在进行中,请等待连接完成\");\n }\n\n // 清理之前的连接\n this.cleanupConnection();\n\n // 重置手动断开标志\n this.reconnectState.isManualDisconnect = false;\n\n return this.attemptConnection();\n }\n\n /**\n * 尝试建立连接\n * @returns 连接成功后的 Promise\n */\n private async attemptConnection(): Promise<void> {\n this.connectionState = ConnectionState.CONNECTING;\n this.logger.info(\n `正在连接 MCP 接入点: ${this.endpointUrl} (尝试 ${\n this.reconnectState.attempts + 1\n }/${this.reconnectOptions.maxAttempts})`\n );\n\n return new Promise((resolve, reject) => {\n // 设置连接超时\n this.connectionTimeout = setTimeout(() => {\n const error = new Error(\n `连接超时 (${this.reconnectOptions.timeout}ms)`\n );\n this.handleConnectionError(error);\n reject(error);\n }, this.reconnectOptions.timeout);\n\n this.ws = new WebSocket(this.endpointUrl);\n\n this.ws.on(\"open\", () => {\n this.handleConnectionSuccess();\n resolve();\n });\n\n this.ws.on(\"message\", (data) => {\n try {\n const message: MCPMessage = JSON.parse(data.toString());\n this.handleMessage(message);\n } catch (error) {\n this.logger.error(\"MCP 消息解析错误:\", error);\n }\n });\n\n this.ws.on(\"close\", (code, reason) => {\n this.handleConnectionClose(code, reason.toString());\n });\n\n this.ws.on(\"error\", (error) => {\n this.handleConnectionError(error);\n reject(error);\n });\n });\n }\n\n /**\n * 处理连接成功\n */\n private handleConnectionSuccess(): void {\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n this.isConnected = true;\n this.connectionState = ConnectionState.CONNECTED;\n\n // 重置重连状态\n this.reconnectState.attempts = 0;\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n this.reconnectState.lastError = null;\n\n this.logger.info(\"MCP WebSocket 连接已建立\");\n }\n\n /**\n * 处理连接错误\n */\n private handleConnectionError(error: Error): void {\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n this.reconnectState.lastError = error;\n this.logger.error(\"MCP WebSocket 错误:\", error.message);\n\n // 清理当前连接\n this.cleanupConnection();\n }\n\n /**\n * 处理连接关闭\n */\n private handleConnectionClose(code: number, reason: string): void {\n this.isConnected = false;\n this.serverInitialized = false;\n this.logger.info(`MCP 连接已关闭 (代码: ${code}, 原因: ${reason})`);\n\n // 如果是手动断开,不进行重连\n if (this.reconnectState.isManualDisconnect) {\n this.connectionState = ConnectionState.DISCONNECTED;\n return;\n }\n\n // 检查是否需要重连\n if (this.shouldReconnect()) {\n this.scheduleReconnect();\n } else {\n this.connectionState = ConnectionState.FAILED;\n this.logger.warn(\n `已达到最大重连次数 (${this.reconnectOptions.maxAttempts}),停止重连`\n );\n }\n }\n\n /**\n * 检查是否应该重连\n */\n private shouldReconnect(): boolean {\n return (\n this.reconnectOptions.enabled &&\n this.reconnectState.attempts < this.reconnectOptions.maxAttempts &&\n !this.reconnectState.isManualDisconnect\n );\n }\n\n /**\n * 安排重连\n */\n private scheduleReconnect(): void {\n this.connectionState = ConnectionState.RECONNECTING;\n this.reconnectState.attempts++;\n\n // 计算下次重连间隔\n this.calculateNextInterval();\n\n this.logger.info(\n `将在 ${this.reconnectState.nextInterval}ms 后进行第 ${this.reconnectState.attempts} 次重连`\n );\n\n // 清理之前的重连定时器\n if (this.reconnectState.timer) {\n clearTimeout(this.reconnectState.timer);\n }\n\n // 设置重连定时器\n this.reconnectState.timer = setTimeout(async () => {\n try {\n await this.attemptConnection();\n } catch (error) {\n // 连接失败会触发 handleConnectionError,无需额外处理\n }\n }, this.reconnectState.nextInterval);\n }\n\n /**\n * 计算下次重连间隔\n */\n private calculateNextInterval(): void {\n let interval: number;\n\n switch (this.reconnectOptions.backoffStrategy) {\n case \"fixed\":\n interval = this.reconnectOptions.initialInterval;\n break;\n\n case \"linear\":\n interval =\n this.reconnectOptions.initialInterval +\n this.reconnectState.attempts *\n this.reconnectOptions.backoffMultiplier *\n 1000;\n break;\n\n case \"exponential\":\n interval =\n this.reconnectOptions.initialInterval *\n this.reconnectOptions.backoffMultiplier **\n (this.reconnectState.attempts - 1);\n break;\n\n default:\n interval = this.reconnectOptions.initialInterval;\n }\n\n // 限制最大间隔\n interval = Math.min(interval, this.reconnectOptions.maxInterval);\n\n // 添加随机抖动\n if (this.reconnectOptions.jitter) {\n const jitterRange = interval * 0.1; // 10% 抖动\n const jitter = (Math.random() - 0.5) * 2 * jitterRange;\n interval += jitter;\n }\n\n this.reconnectState.nextInterval = Math.max(interval, 1000); // 最小1秒\n }\n\n /**\n * 清理连接资源\n */\n private cleanupConnection(): void {\n // 清理 WebSocket\n if (this.ws) {\n // 移除所有事件监听器,防止在关闭时触发错误事件\n this.ws.removeAllListeners();\n\n // 安全关闭 WebSocket\n try {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, \"Cleaning up connection\");\n } else if (this.ws.readyState === WebSocket.CONNECTING) {\n this.ws.terminate(); // 强制终止正在连接的 WebSocket\n }\n } catch (error) {\n // 忽略关闭时的错误\n this.logger.debug(\"WebSocket 关闭时出现错误(已忽略):\", error);\n }\n\n this.ws = null;\n }\n\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n // 重置连接状态\n this.isConnected = false;\n this.serverInitialized = false;\n }\n\n /**\n * 停止重连\n */\n private stopReconnect(): void {\n if (this.reconnectState.timer) {\n clearTimeout(this.reconnectState.timer);\n this.reconnectState.timer = null;\n }\n }\n\n private handleMessage(message: MCPMessage): void {\n this.logger.debug(\"收到 MCP 消息:\", JSON.stringify(message, null, 2));\n\n if (message.method) {\n this.handleServerRequest(message);\n }\n }\n\n private handleServerRequest(request: MCPMessage): void {\n switch (request.method) {\n case \"initialize\":\n this.sendResponse(request.id, {\n protocolVersion: \"2024-11-05\",\n capabilities: {\n tools: { listChanged: true },\n logging: {},\n },\n serverInfo: {\n name: \"xiaozhi-mcp-server\",\n version: \"1.0.0\",\n },\n });\n this.serverInitialized = true;\n this.logger.info(\"MCP 服务器初始化完成\");\n break;\n\n case \"tools/list\": {\n const toolsList = this.getTools();\n this.sendResponse(request.id, { tools: toolsList });\n this.logger.info(`MCP 工具列表已发送 (${toolsList.length}个工具)`);\n break;\n }\n\n case \"ping\":\n this.sendResponse(request.id, {});\n this.logger.debug(\"回应 MCP ping 消息\");\n break;\n\n default:\n this.logger.warn(`未知的 MCP 请求: ${request.method}`);\n }\n }\n\n private sendResponse(id: number | string | undefined, result: any): void {\n if (this.isConnected && this.ws?.readyState === WebSocket.OPEN) {\n const response: MCPMessage = {\n jsonrpc: \"2.0\",\n id,\n result,\n };\n this.ws.send(JSON.stringify(response));\n }\n }\n\n /**\n * 获取 MCP 服务器状态\n * @returns 服务器状态\n */\n public getStatus(): ProxyMCPServerStatus {\n return {\n connected: this.isConnected,\n initialized: this.serverInitialized,\n url: this.endpointUrl,\n availableTools: this.tools.size,\n connectionState: this.connectionState,\n reconnectAttempts: this.reconnectState.attempts,\n lastError: this.reconnectState.lastError?.message || null,\n };\n }\n\n /**\n * 主动断开 MCP 连接\n */\n public disconnect(): void {\n this.logger.info(\"主动断开 MCP 连接\");\n\n // 标记为手动断开,阻止自动重连\n this.reconnectState.isManualDisconnect = true;\n\n // 停止重连定时器\n this.stopReconnect();\n\n // 清理连接资源\n this.cleanupConnection();\n\n // 设置状态为已断开\n this.connectionState = ConnectionState.DISCONNECTED;\n }\n\n /**\n * 手动重连 MCP 接入点\n */\n public async reconnect(): Promise<void> {\n this.logger.info(\"手动重连 MCP 接入点\");\n\n // 停止自动重连\n this.stopReconnect();\n\n // 重置重连状态\n this.reconnectState.attempts = 0;\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n this.reconnectState.isManualDisconnect = false;\n\n // 清理现有连接\n this.cleanupConnection();\n\n // 尝试连接\n await this.connect();\n }\n\n /**\n * 启用自动重连\n */\n public enableReconnect(): void {\n this.reconnectOptions.enabled = true;\n this.logger.info(\"自动重连已启用\");\n }\n\n /**\n * 禁用自动重连\n */\n public disableReconnect(): void {\n this.reconnectOptions.enabled = false;\n this.stopReconnect();\n this.logger.info(\"自动重连已禁用\");\n }\n\n /**\n * 更新重连配置\n */\n public updateReconnectOptions(options: Partial<ReconnectOptions>): void {\n this.reconnectOptions = { ...this.reconnectOptions, ...options };\n this.logger.info(\"重连配置已更新\", options);\n }\n\n /**\n * 获取重连配置\n */\n public getReconnectOptions(): ReconnectOptions {\n return { ...this.reconnectOptions };\n }\n\n /**\n * 重置重连状态\n */\n public resetReconnectState(): void {\n this.stopReconnect();\n this.reconnectState.attempts = 0;\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n this.reconnectState.lastError = null;\n this.logger.info(\"重连状态已重置\");\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\nimport { type consola, createConsola } from \"consola\";\n\nfunction formatDateTime(date: Date) {\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\nexport class Logger {\n private logFilePath: string | null = null;\n private writeStream: fs.WriteStream | null = null;\n private consolaInstance: typeof consola;\n private isDaemonMode: boolean;\n\n constructor() {\n // 检查是否为守护进程模式\n this.isDaemonMode = process.env.XIAOZHI_DAEMON === \"true\";\n // 创建自定义的 consola 实例,禁用图标并自定义格式\n this.consolaInstance = createConsola({\n formatOptions: {\n date: false,\n colors: true,\n compact: true,\n },\n fancy: false,\n });\n\n // 保存对当前实例的引用,以便在闭包中访问\n const isDaemonMode = this.isDaemonMode;\n\n // 自定义格式化器\n this.consolaInstance.setReporters([\n {\n log: (logObj) => {\n const levelMap: Record<string, string> = {\n info: \"INFO\",\n success: \"SUCCESS\",\n warn: \"WARN\",\n error: \"ERROR\",\n debug: \"DEBUG\",\n log: \"LOG\",\n };\n\n const colorMap: Record<string, (text: string) => string> = {\n info: chalk.blue,\n success: chalk.green,\n warn: chalk.yellow,\n error: chalk.red,\n debug: chalk.gray,\n log: (text: string) => text,\n };\n\n const level = levelMap[logObj.type] || logObj.type.toUpperCase();\n const colorFn = colorMap[logObj.type] || ((text: string) => text);\n const timestamp = formatDateTime(new Date());\n\n // 为级别添加颜色\n const coloredLevel = colorFn(`[${level}]`);\n const message = `[${timestamp}] ${coloredLevel} ${logObj.args.join(\n \" \"\n )}`;\n\n // 守护进程模式下不输出到控制台,只写入文件\n if (!isDaemonMode) {\n // 输出到 stderr(与原来保持一致)\n try {\n console.error(message);\n } catch (error) {\n // 忽略 EPIPE 错误\n if (error instanceof Error && error.message?.includes(\"EPIPE\")) {\n return;\n }\n throw error;\n }\n }\n },\n },\n ]);\n }\n\n /**\n * 初始化日志文件\n * @param projectDir 项目目录\n */\n initLogFile(projectDir: string): void {\n this.logFilePath = path.join(projectDir, \"xiaozhi.log\");\n\n // 确保日志文件存在\n if (!fs.existsSync(this.logFilePath)) {\n fs.writeFileSync(this.logFilePath, \"\");\n }\n\n // 创建写入流,追加模式\n this.writeStream = fs.createWriteStream(this.logFilePath, {\n flags: \"a\",\n encoding: \"utf8\",\n });\n }\n\n /**\n * 记录日志到文件\n * @param level 日志级别\n * @param message 日志消息\n * @param args 额外参数\n */\n private logToFile(level: string, message: string, ...args: any[]): void {\n if (this.writeStream) {\n const timestamp = new Date().toISOString();\n const formattedMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`;\n const fullMessage =\n args.length > 0\n ? `${formattedMessage} ${args\n .map((arg) =>\n typeof arg === \"object\" ? JSON.stringify(arg) : String(arg)\n )\n .join(\" \")}`\n : formattedMessage;\n\n this.writeStream.write(`${fullMessage}\\n`);\n }\n }\n\n /**\n * 设置是否启用文件日志\n * @param enable 是否启用\n */\n enableFileLogging(enable: boolean): void {\n if (enable && !this.writeStream && this.logFilePath) {\n this.writeStream = fs.createWriteStream(this.logFilePath, {\n flags: \"a\",\n encoding: \"utf8\",\n });\n } else if (!enable && this.writeStream) {\n this.writeStream.end();\n this.writeStream = null;\n }\n }\n\n /**\n * 日志方法\n */\n info(message: string, ...args: any[]): void {\n this.consolaInstance.info(message, ...args);\n this.logToFile(\"info\", message, ...args);\n }\n\n success(message: string, ...args: any[]): void {\n this.consolaInstance.success(message, ...args);\n this.logToFile(\"success\", message, ...args);\n }\n\n warn(message: string, ...args: any[]): void {\n this.consolaInstance.warn(message, ...args);\n this.logToFile(\"warn\", message, ...args);\n }\n\n error(message: string, ...args: any[]): void {\n this.consolaInstance.error(message, ...args);\n this.logToFile(\"error\", message, ...args);\n }\n\n debug(message: string, ...args: any[]): void {\n this.consolaInstance.debug(message, ...args);\n this.logToFile(\"debug\", message, ...args);\n }\n\n log(message: string, ...args: any[]): void {\n this.consolaInstance.log(message, ...args);\n this.logToFile(\"log\", message, ...args);\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 if (this.writeStream) {\n this.writeStream.end();\n this.writeStream = null;\n }\n }\n}\n\n// 导出单例实例\nexport const logger = new Logger();\n"],"mappings":"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","names":["WebSocket","fs","path","chalk","createConsola","formatDateTime","date","year","month","day","hours","minutes","seconds","__name","Logger","createConsola","isDaemonMode","logObj","levelMap","colorMap","chalk","text","level","colorFn","timestamp","coloredLevel","message","error","projectDir","path","fs","args","formattedMessage","fullMessage","arg","enable","tag","logger","ProxyMCPServer","__name","endpointUrl","options","Logger","serviceManager","allTools","newTools","toolInfo","error","name","tool","tools","resolve","reject","WebSocket","data","message","code","reason","interval","jitterRange","jitter","request","toolsList","id","result","response"]}
|
|
1
|
+
{"version":3,"sources":["../src/ProxyMCPServer.ts","../src/Logger.ts"],"sourcesContent":["import type { Tool } from \"@modelcontextprotocol/sdk/types.js\";\nimport WebSocket from \"ws\";\nimport { type Logger, logger } from \"./Logger.js\";\n\nexport type { Tool };\n\n// MCP 消息接口\ninterface MCPMessage {\n jsonrpc: string;\n id?: number | string;\n method?: string;\n params?: any;\n result?: any;\n}\n\n// 连接状态枚举\nenum ConnectionState {\n DISCONNECTED = \"disconnected\",\n CONNECTING = \"connecting\",\n CONNECTED = \"connected\",\n RECONNECTING = \"reconnecting\",\n FAILED = \"failed\",\n}\n\n// 重连配置接口\ninterface ReconnectOptions {\n enabled: boolean; // 是否启用自动重连\n maxAttempts: number; // 最大重连次数\n initialInterval: number; // 初始重连间隔(ms)\n maxInterval: number; // 最大重连间隔(ms)\n backoffStrategy: \"linear\" | \"exponential\" | \"fixed\"; // 退避策略\n backoffMultiplier: number; // 退避倍数\n timeout: number; // 单次连接超时时间(ms)\n jitter: boolean; // 是否添加随机抖动\n}\n\n// 重连状态接口\ninterface ReconnectState {\n attempts: number; // 当前重连次数\n nextInterval: number; // 下次重连间隔\n timer: NodeJS.Timeout | null; // 重连定时器\n lastError: Error | null; // 最后一次错误\n isManualDisconnect: boolean; // 是否为主动断开\n}\n\n// 服务器选项接口\ninterface ProxyMCPServerOptions {\n reconnect?: Partial<ReconnectOptions>;\n}\n\n// 服务器状态接口\ninterface ProxyMCPServerStatus {\n connected: boolean;\n initialized: boolean;\n url: string;\n availableTools: number;\n connectionState: ConnectionState;\n reconnectAttempts: number;\n lastError: string | null;\n}\n\nexport class ProxyMCPServer {\n private endpointUrl: string;\n private ws: WebSocket | null = null;\n private logger: Logger;\n private isConnected = false;\n private serverInitialized = false;\n\n // 工具管理\n private tools: Map<string, Tool> = new Map();\n\n // 连接状态管理\n private connectionState: ConnectionState = ConnectionState.DISCONNECTED;\n\n // 重连配置\n private reconnectOptions: ReconnectOptions;\n\n // 重连状态\n private reconnectState: ReconnectState = {\n attempts: 0,\n nextInterval: 0,\n timer: null,\n lastError: null,\n isManualDisconnect: false,\n };\n\n // 连接超时定时器\n private connectionTimeout: NodeJS.Timeout | null = null;\n\n constructor(endpointUrl: string, options?: ProxyMCPServerOptions) {\n this.endpointUrl = endpointUrl;\n this.logger = logger;\n\n // 初始化重连配置\n this.reconnectOptions = {\n enabled: true,\n maxAttempts: 10,\n initialInterval: 3000,\n maxInterval: 30000,\n backoffStrategy: \"exponential\",\n backoffMultiplier: 1.5,\n timeout: 10000,\n jitter: true,\n ...options?.reconnect,\n };\n\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n }\n\n /**\n * 设置 MCPServiceManager 实例\n * @param serviceManager MCPServiceManager 实例\n */\n setServiceManager(serviceManager: any): void {\n // 临时存储在一个变量中,避免类型检查问题\n (this as any).serviceManager = serviceManager;\n this.logger.info(\"已设置 MCPServiceManager\");\n\n // 立即同步工具\n this.syncToolsFromServiceManager();\n }\n\n /**\n * 从 MCPServiceManager 同步工具\n * 优化版本:支持增量同步和错误恢复\n */\n syncToolsFromServiceManager(): void {\n const serviceManager = (this as any).serviceManager;\n if (!serviceManager) {\n this.logger.debug(\"MCPServiceManager 未设置,跳过工具同步\");\n return;\n }\n\n try {\n // 从 MCPServiceManager 获取所有工具\n const allTools = serviceManager.getAllTools();\n\n // 原子性更新:先构建新的工具映射,再替换\n const newTools = new Map<string, Tool>();\n\n for (const toolInfo of allTools) {\n newTools.set(toolInfo.name, {\n name: toolInfo.name,\n description: toolInfo.description,\n inputSchema: toolInfo.inputSchema,\n });\n }\n\n // 原子性替换\n this.tools = newTools;\n\n this.logger.info(`已从 MCPServiceManager 同步 ${this.tools.size} 个工具`);\n } catch (error) {\n this.logger.error(\n `同步工具失败: ${error instanceof Error ? error.message : String(error)}`\n );\n // 同步失败时保持现有工具不变,确保服务可用性\n }\n }\n\n /**\n * 添加单个工具\n * @param name 工具名称\n * @param tool 工具定义\n * @param options 工具选项(可选)\n * @returns 返回 this 支持链式调用\n */\n addTool(name: string, tool: Tool): this {\n this.validateTool(name, tool);\n this.tools.set(name, tool);\n this.logger.debug(`工具 '${name}' 已添加`);\n // TODO: 未来可以使用 options 参数来设置工具的启用状态、元数据等\n return this;\n }\n\n /**\n * 批量添加工具\n * @param tools 工具对象,键为工具名称,值为工具定义\n * @returns 返回 this 支持链式调用\n */\n addTools(tools: Record<string, Tool>): this {\n for (const [name, tool] of Object.entries(tools)) {\n this.addTool(name, tool);\n }\n return this;\n }\n\n /**\n * 移除单个工具\n * @param name 工具名称\n * @returns 返回 this 支持链式调用\n */\n removeTool(name: string): this {\n if (this.tools.delete(name)) {\n this.logger.debug(`工具 '${name}' 已移除`);\n } else {\n this.logger.warn(`尝试移除不存在的工具: '${name}'`);\n }\n return this;\n }\n\n /**\n * 获取当前所有工具列表\n * @returns 工具数组\n */\n getTools(): Tool[] {\n // 每次获取工具时都尝试从 MCPServiceManager 同步\n try {\n this.syncToolsFromServiceManager();\n } catch (error) {\n // 静默处理同步错误,不影响现有工具的返回\n }\n\n return Array.from(this.tools.values());\n }\n\n /**\n * 检查工具是否存在\n * @param name 工具名称\n * @returns 是否存在\n */\n hasTool(name: string): boolean {\n return this.tools.has(name);\n }\n\n /**\n * 验证工具的有效性\n * @param name 工具名称\n * @param tool 工具定义\n */\n private validateTool(name: string, tool: Tool): void {\n if (!name || typeof name !== \"string\" || name.trim() === \"\") {\n throw new Error(\"工具名称必须是非空字符串\");\n }\n\n if (this.tools.has(name)) {\n throw new Error(`工具 '${name}' 已存在`);\n }\n\n if (!tool || typeof tool !== \"object\") {\n throw new Error(\"工具必须是有效的对象\");\n }\n\n // 验证工具的必需字段\n if (!tool.name || typeof tool.name !== \"string\") {\n throw new Error(\"工具必须包含有效的 'name' 字段\");\n }\n\n if (!tool.description || typeof tool.description !== \"string\") {\n throw new Error(\"工具必须包含有效的 'description' 字段\");\n }\n\n if (!tool.inputSchema || typeof tool.inputSchema !== \"object\") {\n throw new Error(\"工具必须包含有效的 'inputSchema' 字段\");\n }\n\n // 验证 inputSchema 的基本结构\n if (!tool.inputSchema.type || !tool.inputSchema.properties) {\n throw new Error(\n \"工具的 inputSchema 必须包含 'type' 和 'properties' 字段\"\n );\n }\n }\n\n /**\n * 连接 MCP 接入点\n * @returns 连接成功后的 Promise\n */\n public async connect(): Promise<void> {\n // 连接前验证\n if (this.tools.size === 0) {\n throw new Error(\"未配置任何工具。请在连接前至少添加一个工具。\");\n }\n\n // 如果正在连接中,等待当前连接完成\n if (this.connectionState === ConnectionState.CONNECTING) {\n throw new Error(\"连接正在进行中,请等待连接完成\");\n }\n\n // 清理之前的连接\n this.cleanupConnection();\n\n // 重置手动断开标志\n this.reconnectState.isManualDisconnect = false;\n\n return this.attemptConnection();\n }\n\n /**\n * 尝试建立连接\n * @returns 连接成功后的 Promise\n */\n private async attemptConnection(): Promise<void> {\n this.connectionState = ConnectionState.CONNECTING;\n this.logger.info(\n `正在连接 MCP 接入点: ${this.endpointUrl} (尝试 ${\n this.reconnectState.attempts + 1\n }/${this.reconnectOptions.maxAttempts})`\n );\n\n return new Promise((resolve, reject) => {\n // 设置连接超时\n this.connectionTimeout = setTimeout(() => {\n const error = new Error(\n `连接超时 (${this.reconnectOptions.timeout}ms)`\n );\n this.handleConnectionError(error);\n reject(error);\n }, this.reconnectOptions.timeout);\n\n this.ws = new WebSocket(this.endpointUrl);\n\n this.ws.on(\"open\", () => {\n this.handleConnectionSuccess();\n resolve();\n });\n\n this.ws.on(\"message\", (data) => {\n try {\n const message: MCPMessage = JSON.parse(data.toString());\n this.handleMessage(message);\n } catch (error) {\n this.logger.error(\"MCP 消息解析错误:\", error);\n }\n });\n\n this.ws.on(\"close\", (code, reason) => {\n this.handleConnectionClose(code, reason.toString());\n });\n\n this.ws.on(\"error\", (error) => {\n this.handleConnectionError(error);\n reject(error);\n });\n });\n }\n\n /**\n * 处理连接成功\n */\n private handleConnectionSuccess(): void {\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n this.isConnected = true;\n this.connectionState = ConnectionState.CONNECTED;\n\n // 重置重连状态\n this.reconnectState.attempts = 0;\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n this.reconnectState.lastError = null;\n\n this.logger.info(\"MCP WebSocket 连接已建立\");\n }\n\n /**\n * 处理连接错误\n */\n private handleConnectionError(error: Error): void {\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n this.reconnectState.lastError = error;\n this.logger.error(\"MCP WebSocket 错误:\", error.message);\n\n // 清理当前连接\n this.cleanupConnection();\n }\n\n /**\n * 处理连接关闭\n */\n private handleConnectionClose(code: number, reason: string): void {\n this.isConnected = false;\n this.serverInitialized = false;\n this.logger.info(`MCP 连接已关闭 (代码: ${code}, 原因: ${reason})`);\n\n // 如果是手动断开,不进行重连\n if (this.reconnectState.isManualDisconnect) {\n this.connectionState = ConnectionState.DISCONNECTED;\n return;\n }\n\n // 检查是否需要重连\n if (this.shouldReconnect()) {\n this.scheduleReconnect();\n } else {\n this.connectionState = ConnectionState.FAILED;\n this.logger.warn(\n `已达到最大重连次数 (${this.reconnectOptions.maxAttempts}),停止重连`\n );\n }\n }\n\n /**\n * 检查是否应该重连\n */\n private shouldReconnect(): boolean {\n return (\n this.reconnectOptions.enabled &&\n this.reconnectState.attempts < this.reconnectOptions.maxAttempts &&\n !this.reconnectState.isManualDisconnect\n );\n }\n\n /**\n * 安排重连\n */\n private scheduleReconnect(): void {\n this.connectionState = ConnectionState.RECONNECTING;\n this.reconnectState.attempts++;\n\n // 计算下次重连间隔\n this.calculateNextInterval();\n\n this.logger.info(\n `将在 ${this.reconnectState.nextInterval}ms 后进行第 ${this.reconnectState.attempts} 次重连`\n );\n\n // 清理之前的重连定时器\n if (this.reconnectState.timer) {\n clearTimeout(this.reconnectState.timer);\n }\n\n // 设置重连定时器\n this.reconnectState.timer = setTimeout(async () => {\n try {\n await this.attemptConnection();\n } catch (error) {\n // 连接失败会触发 handleConnectionError,无需额外处理\n }\n }, this.reconnectState.nextInterval);\n }\n\n /**\n * 计算下次重连间隔\n */\n private calculateNextInterval(): void {\n let interval: number;\n\n switch (this.reconnectOptions.backoffStrategy) {\n case \"fixed\":\n interval = this.reconnectOptions.initialInterval;\n break;\n\n case \"linear\":\n interval =\n this.reconnectOptions.initialInterval +\n this.reconnectState.attempts *\n this.reconnectOptions.backoffMultiplier *\n 1000;\n break;\n\n case \"exponential\":\n interval =\n this.reconnectOptions.initialInterval *\n this.reconnectOptions.backoffMultiplier **\n (this.reconnectState.attempts - 1);\n break;\n\n default:\n interval = this.reconnectOptions.initialInterval;\n }\n\n // 限制最大间隔\n interval = Math.min(interval, this.reconnectOptions.maxInterval);\n\n // 添加随机抖动\n if (this.reconnectOptions.jitter) {\n const jitterRange = interval * 0.1; // 10% 抖动\n const jitter = (Math.random() - 0.5) * 2 * jitterRange;\n interval += jitter;\n }\n\n this.reconnectState.nextInterval = Math.max(interval, 1000); // 最小1秒\n }\n\n /**\n * 清理连接资源\n */\n private cleanupConnection(): void {\n // 清理 WebSocket\n if (this.ws) {\n // 移除所有事件监听器,防止在关闭时触发错误事件\n this.ws.removeAllListeners();\n\n // 安全关闭 WebSocket\n try {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, \"Cleaning up connection\");\n } else if (this.ws.readyState === WebSocket.CONNECTING) {\n this.ws.terminate(); // 强制终止正在连接的 WebSocket\n }\n } catch (error) {\n // 忽略关闭时的错误\n this.logger.debug(\"WebSocket 关闭时出现错误(已忽略):\", error);\n }\n\n this.ws = null;\n }\n\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n // 重置连接状态\n this.isConnected = false;\n this.serverInitialized = false;\n }\n\n /**\n * 停止重连\n */\n private stopReconnect(): void {\n if (this.reconnectState.timer) {\n clearTimeout(this.reconnectState.timer);\n this.reconnectState.timer = null;\n }\n }\n\n private handleMessage(message: MCPMessage): void {\n this.logger.debug(\"收到 MCP 消息:\", JSON.stringify(message, null, 2));\n\n if (message.method) {\n this.handleServerRequest(message);\n }\n }\n\n private handleServerRequest(request: MCPMessage): void {\n switch (request.method) {\n case \"initialize\":\n this.sendResponse(request.id, {\n protocolVersion: \"2024-11-05\",\n capabilities: {\n tools: { listChanged: true },\n logging: {},\n },\n serverInfo: {\n name: \"xiaozhi-mcp-server\",\n version: \"1.0.0\",\n },\n });\n this.serverInitialized = true;\n this.logger.info(\"MCP 服务器初始化完成\");\n break;\n\n case \"tools/list\": {\n const toolsList = this.getTools();\n this.sendResponse(request.id, { tools: toolsList });\n this.logger.info(`MCP 工具列表已发送 (${toolsList.length}个工具)`);\n break;\n }\n\n case \"ping\":\n this.sendResponse(request.id, {});\n this.logger.debug(\"回应 MCP ping 消息\");\n break;\n\n default:\n this.logger.warn(`未知的 MCP 请求: ${request.method}`);\n }\n }\n\n private sendResponse(id: number | string | undefined, result: any): void {\n if (this.isConnected && this.ws?.readyState === WebSocket.OPEN) {\n const response: MCPMessage = {\n jsonrpc: \"2.0\",\n id,\n result,\n };\n this.ws.send(JSON.stringify(response));\n }\n }\n\n /**\n * 获取 MCP 服务器状态\n * @returns 服务器状态\n */\n public getStatus(): ProxyMCPServerStatus {\n return {\n connected: this.isConnected,\n initialized: this.serverInitialized,\n url: this.endpointUrl,\n availableTools: this.tools.size,\n connectionState: this.connectionState,\n reconnectAttempts: this.reconnectState.attempts,\n lastError: this.reconnectState.lastError?.message || null,\n };\n }\n\n /**\n * 主动断开 MCP 连接\n */\n public disconnect(): void {\n this.logger.info(\"主动断开 MCP 连接\");\n\n // 标记为手动断开,阻止自动重连\n this.reconnectState.isManualDisconnect = true;\n\n // 停止重连定时器\n this.stopReconnect();\n\n // 清理连接资源\n this.cleanupConnection();\n\n // 设置状态为已断开\n this.connectionState = ConnectionState.DISCONNECTED;\n }\n\n /**\n * 手动重连 MCP 接入点\n */\n public async reconnect(): Promise<void> {\n this.logger.info(\"手动重连 MCP 接入点\");\n\n // 停止自动重连\n this.stopReconnect();\n\n // 重置重连状态\n this.reconnectState.attempts = 0;\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n this.reconnectState.isManualDisconnect = false;\n\n // 清理现有连接\n this.cleanupConnection();\n\n // 尝试连接\n await this.connect();\n }\n\n /**\n * 启用自动重连\n */\n public enableReconnect(): void {\n this.reconnectOptions.enabled = true;\n this.logger.info(\"自动重连已启用\");\n }\n\n /**\n * 禁用自动重连\n */\n public disableReconnect(): void {\n this.reconnectOptions.enabled = false;\n this.stopReconnect();\n this.logger.info(\"自动重连已禁用\");\n }\n\n /**\n * 更新重连配置\n */\n public updateReconnectOptions(options: Partial<ReconnectOptions>): void {\n this.reconnectOptions = { ...this.reconnectOptions, ...options };\n this.logger.info(\"重连配置已更新\", options);\n }\n\n /**\n * 获取重连配置\n */\n public getReconnectOptions(): ReconnectOptions {\n return { ...this.reconnectOptions };\n }\n\n /**\n * 重置重连状态\n */\n public resetReconnectState(): void {\n this.stopReconnect();\n this.reconnectState.attempts = 0;\n this.reconnectState.nextInterval = this.reconnectOptions.initialInterval;\n this.reconnectState.lastError = null;\n this.logger.info(\"重连状态已重置\");\n }\n}\n","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":["WebSocket","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","ProxyMCPServer","__name","endpointUrl","options","logger","serviceManager","allTools","newTools","toolInfo","error","name","tool","tools","resolve","reject","WebSocket","data","message","code","reason","interval","jitterRange","jitter","request","toolsList","id","result","response"]}
|