UNPKG

@yeepay/coderocket-mcp

Version:

CodeRocket MCP - Independent AI-powered code review server for Model Context Protocol

227 lines 6.87 kB
import { appendFile } from 'fs/promises'; import { join } from 'path'; import { tmpdir } from 'os'; // 自定义错误类型 export class AIServiceError extends Error { service; constructor(message, service) { super(message); this.name = 'AIServiceError'; this.service = service; } } export class GitError extends Error { constructor(message) { super(message); this.name = 'GitError'; } } export class FileError extends Error { constructor(message) { super(message); this.name = 'FileError'; } } /** * 日志级别 */ export var LogLevel; (function (LogLevel) { LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG"; LogLevel[LogLevel["INFO"] = 1] = "INFO"; LogLevel[LogLevel["WARN"] = 2] = "WARN"; LogLevel[LogLevel["ERROR"] = 3] = "ERROR"; })(LogLevel || (LogLevel = {})); /** * 简单的日志记录器 */ export class Logger { logLevel; logFile; constructor(level = LogLevel.INFO, logFile) { this.logLevel = level; this.logFile = logFile; // 如果没有指定日志文件,使用临时目录 if (!this.logFile) { this.logFile = join(tmpdir(), 'coderocket-mcp.log'); } } /** * 记录调试信息 */ debug(message, context) { this.log(LogLevel.DEBUG, message, context); } /** * 记录信息 */ info(message, context) { this.log(LogLevel.INFO, message, context); } /** * 记录警告 */ warn(message, context) { this.log(LogLevel.WARN, message, context); } /** * 记录错误 */ error(message, error, context) { this.log(LogLevel.ERROR, message, context, error); } /** * 核心日志记录方法 */ async log(level, message, context, error) { if (level < this.logLevel) { return; } const entry = { timestamp: new Date().toISOString(), level, message, context, error, }; // 在 MCP 服务器模式下,只有在 DEBUG 模式或 WARN/ERROR 级别时才输出到控制台 // 这避免了 IDE 误认为 INFO 级别的日志是错误信息 const shouldOutputToConsole = process.env.DEBUG === 'true' || level >= LogLevel.WARN; if (shouldOutputToConsole) { const levelName = LogLevel[level]; const contextStr = context ? ` ${JSON.stringify(context)}` : ''; const errorStr = error ? ` Error: ${error.message}` : ''; // 所有控制台输出都使用 stderr 以避免污染 MCP 协议的 stdout console.error(`[${entry.timestamp}] ${levelName}: ${message}${contextStr}${errorStr}`); } // 写入日志文件 if (this.logFile) { try { // 处理 Error 对象,避免循环结构 let logEntry = entry; if (entry.error instanceof Error) { logEntry = { ...entry, error: { name: entry.error.name, message: entry.error.message, stack: entry.error.stack, }, }; } const logLine = JSON.stringify(logEntry) + '\n'; await appendFile(this.logFile, logLine, 'utf-8'); } catch (fileError) { console.error('Failed to write to log file:', fileError); } } } /** * 获取日志文件路径 */ getLogFile() { return this.logFile; } /** * 设置日志级别 */ setLevel(level) { this.logLevel = level; } } // 全局日志实例 export const logger = new Logger(process.env.NODE_ENV === 'development' ? LogLevel.DEBUG : LogLevel.INFO); /** * 错误处理工具类 */ export class ErrorHandler { logger; constructor(logger) { this.logger = logger; } /** * 处理并格式化错误 */ handleError(error, context) { let formattedError; if (error instanceof Error) { formattedError = error; } else if (typeof error === 'string') { formattedError = new Error(error); } else { formattedError = new Error(`Unknown error: ${JSON.stringify(error)}`); } // 记录错误 this.logger.error(`${context ? `[${context}] ` : ''}${formattedError.message}`, formattedError, { context }); return formattedError; } /** * 包装异步函数,自动处理错误 */ wrapAsync(fn, context) { return async (...args) => { try { return await fn(...args); } catch (error) { throw this.handleError(error, context); } }; } /** * 创建用户友好的错误消息 */ createUserFriendlyError(error, suggestions) { // 根据错误类型提供不同的错误代码和建议 let errorCode = 'UNKNOWN_ERROR'; let userSuggestions = suggestions || []; if (error instanceof GitError) { errorCode = 'GIT_ERROR'; userSuggestions = [ '确保在Git仓库中执行', '检查Git仓库状态', '验证提交哈希是否存在', ...userSuggestions, ]; } else if (error instanceof AIServiceError) { errorCode = 'AI_SERVICE_ERROR'; userSuggestions = [ '检查AI服务配置', '验证API密钥是否正确', '确保网络连接正常', '尝试切换到其他AI服务', ...userSuggestions, ]; } else if (error instanceof FileError) { errorCode = 'FILE_ERROR'; userSuggestions = [ '检查文件路径是否正确', '确保文件存在且可读', '验证文件权限', ...userSuggestions, ]; } else if (error.message.includes('Shell command failed')) { errorCode = 'SHELL_COMMAND_ERROR'; userSuggestions = [ '验证AI服务是否已配置', '检查API密钥是否正确', '确保有足够的权限执行命令', ...userSuggestions, ]; } return { error: error.message, error_code: errorCode, suggestions: userSuggestions, }; } } // 全局错误处理器实例 export const errorHandler = new ErrorHandler(logger); //# sourceMappingURL=logger.js.map