UNPKG

openai-cli-unofficial

Version:

A powerful OpenAI CLI Coding Agent built with TypeScript

591 lines 24 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SystemDetector = void 0; const boxen_1 = __importDefault(require("boxen")); const chalk_1 = __importDefault(require("chalk")); const mcp_client_1 = require("mcp-client"); const manager_1 = require("../mcp/manager"); const utils_1 = require("../utils"); const language_1 = require("./language"); const storage_1 = require("./storage"); class SystemDetector { constructor() { this.mcpClients = new Map(); this.globalMCPManager = manager_1.GlobalMCPManager.getInstance(); } async detectSystem() { const messages = language_1.languageService.getMessages().systemDetector; try { // 检测系统角色 let controller = utils_1.AnimationUtils.showLoadingAnimation({ text: messages.progress.detectingRole, interval: 100 }); const config = storage_1.StorageService.getApiConfig(); const hasRole = !!config.role; await this.delay(500); controller.stop(); // 检测MCP服务 controller = utils_1.AnimationUtils.showLoadingAnimation({ text: messages.progress.detectingMcp, interval: 100 }); const mcpConfig = storage_1.StorageService.getMcpConfig(); const hasMcpServices = Object.keys(mcpConfig.mcpServers).length > 0; await this.delay(500); controller.stop(); let mcpServers = []; if (hasMcpServices) { // 连接MCP服务 controller = utils_1.AnimationUtils.showLoadingAnimation({ text: messages.progress.connectingMcp, interval: 100 }); mcpServers = await this.detectMcpServers(mcpConfig.mcpServers); controller.stop(); // 获取工具 controller = utils_1.AnimationUtils.showLoadingAnimation({ text: messages.progress.fetchingTools, interval: 100 }); await this.fetchToolsForServers(mcpServers); controller.stop(); } const result = { hasRole, role: config.role, hasMcpServices, mcpServers }; return result; } catch (error) { throw error; } } async displaySystemInfo(detectionResult) { const messages = language_1.languageService.getMessages().systemDetector; // 如果没有任何服务或角色配置,显示准备就绪信息 if (!detectionResult.hasRole && !detectionResult.hasMcpServices) { console.log(); console.log(' ' + chalk_1.default.green('✓ ' + messages.ready)); console.log(); return; } // 构建系统状态显示内容 let content = ''; let hasContent = false; // 显示角色信息(如果有) if (detectionResult.hasRole && detectionResult.role) { const lines = detectionResult.role.split('\n'); const displayLines = lines.slice(0, 3); // 限制为3行 const hasMore = lines.length > 3; content += chalk_1.default.cyan('● ') + chalk_1.default.white.bold('System Role') + '\n'; displayLines.forEach(line => { content += ' ' + chalk_1.default.white(line.trim()) + '\n'; }); if (hasMore) { content += ' ' + chalk_1.default.gray('...') + '\n'; } hasContent = true; } // 显示MCP服务信息(如果有) if (detectionResult.hasMcpServices && detectionResult.mcpServers.length > 0) { if (hasContent) content += '\n'; content += chalk_1.default.green('● ') + chalk_1.default.white.bold('MCP Services') + '\n'; detectionResult.mcpServers.forEach(server => { const statusIcon = this.getStatusIcon(server.status); const statusColor = this.getStatusColor(server.status); content += ` ${statusIcon} ${chalk_1.default.white(server.name)}`; // 显示类型和状态 if (server.isBuiltIn) { content += ` ${chalk_1.default.gray('(Built-in)')}`; } else { content += ` ${chalk_1.default.gray(`(${server.type.toUpperCase()})`)}`; } content += ` - ${statusColor(this.getStatusText(server.status))}`; // 显示工具数量 if (server.tools && server.tools.length > 0) { content += ` ${chalk_1.default.cyan(`[${server.tools.length} tools]`)}`; } content += '\n'; }); hasContent = true; } // 显示整合的系统状态框 if (hasContent) { const systemBox = (0, boxen_1.default)(content.trim(), { title: chalk_1.default.white.bold('System Status'), titleAlignment: 'center', padding: { top: 1, bottom: 1, left: 2, right: 2 }, margin: { top: 1, bottom: 1, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'cyan' }); console.log(systemBox); } } async detectMcpServers(mcpServers) { const servers = []; for (const [name, config] of Object.entries(mcpServers)) { const serverInfo = { name, type: this.detectServerType(config), status: 'pending' }; // 检查是否为内置服务 if (config.transport === 'builtin') { serverInfo.isBuiltIn = true; serverInfo.actualTransport = 'builtin'; // 直接连接内置服务 try { await this.connectToBuiltInServer(serverInfo); servers.push(serverInfo); } catch (error) { serverInfo.status = 'failed'; serverInfo.error = error instanceof Error ? error.message : 'Built-in service error'; servers.push(serverInfo); } } else { // 外部服务 if (config.url) { serverInfo.url = config.url; } else if (config.command) { serverInfo.command = config.command; serverInfo.args = config.args || []; } // 尝试连接外部MCP服务器,传递完整配置包括env try { await this.connectToMcpServer(serverInfo, config); servers.push(serverInfo); } catch (error) { serverInfo.status = 'failed'; serverInfo.error = error instanceof Error ? error.message : 'Unknown error'; servers.push(serverInfo); } } } return servers; } async connectToBuiltInServer(serverInfo) { try { // 检查全局MCP管理器是否已初始化 if (!this.globalMCPManager.isReady()) { throw new Error('Built-in MCP manager not initialized'); } // 验证内置服务存在 const servicesInfo = this.globalMCPManager.getServicesInfo(); const serviceExists = servicesInfo.some(service => service.serviceName === serverInfo.name); if (!serviceExists) { throw new Error(`Built-in service '${serverInfo.name}' not found`); } serverInfo.status = 'connected'; } catch (error) { serverInfo.status = 'failed'; serverInfo.error = error instanceof Error ? error.message : 'Connection to built-in service failed'; } } async fetchToolsForServers(servers) { const connectedServers = servers.filter(s => s.status === 'connected'); for (const server of connectedServers) { try { if (server.isBuiltIn) { // 内置服务,使用全局MCP管理器获取工具 const tools = this.globalMCPManager.getServiceTools(server.name); server.tools = tools.map(tool => tool.name); } else { // 外部服务,使用MCP客户端获取工具 const client = this.mcpClients.get(server.name); if (client) { const tools = await client.getAllTools(); server.tools = tools.map(tool => tool.name); } } } catch (error) { // 获取工具失败不影响连接状态,但记录错误 server.tools = []; } } } detectServerType(config) { if (config.transport === 'builtin') { return 'builtin'; } else if (config.url) { // 根据URL判断是HTTP还是SSE if (config.type === 'sse' || config.url.includes('/sse')) { return 'sse'; } return 'http'; } else if (config.command) { return 'stdio'; } else if (config.transport) { return 'other'; } return 'unknown'; } async connectToMcpServer(serverInfo, config) { const messages = language_1.languageService.getMessages().systemDetector; try { const client = new mcp_client_1.MCPClient({ name: "OpenAI-CLI-Detector", version: "1.0.0", }); if (serverInfo.type === 'http' && serverInfo.url) { try { // 首先尝试HTTP连接 // 对于HTTP连接,环境变量通常不直接传递,而是通过headers或认证方式 await client.connect({ type: "httpStream", url: serverInfo.url, }); serverInfo.actualTransport = 'http'; } catch (httpError) { // HTTP失败,尝试SSE回退 try { const sseUrl = this.convertToSseUrl(serverInfo.url); await client.connect({ type: "sse", url: sseUrl, }); serverInfo.actualTransport = 'sse'; } catch (sseError) { throw httpError; // 抛出原始错误 } } } else if (serverInfo.type === 'sse' && serverInfo.url) { // 直接使用SSE连接 await client.connect({ type: "sse", url: serverInfo.url, }); serverInfo.actualTransport = 'sse'; } else if (serverInfo.type === 'stdio' && serverInfo.command) { // 准备环境变量 - 只对STDIO连接有效 const processEnv = process.env; let connectionEnv = {}; // 复制现有环境变量,过滤掉undefined值 Object.keys(processEnv).forEach(key => { const value = processEnv[key]; if (value !== undefined) { connectionEnv[key] = value; } }); // 如果配置中有env,合并到环境变量中 if (config?.env && typeof config.env === 'object') { connectionEnv = { ...connectionEnv, ...config.env }; } // STDIO连接 await client.connect({ type: "stdio", command: serverInfo.command, args: serverInfo.args || [], env: connectionEnv // 传递环境变量给子进程 }); serverInfo.actualTransport = 'stdio'; } else { throw new Error('Unsupported server type'); } // 连接成功 serverInfo.status = 'connected'; // 存储客户端以备后用 this.mcpClients.set(serverInfo.name, client); } catch (error) { serverInfo.status = 'failed'; serverInfo.error = error instanceof Error ? error.message : 'Connection failed'; } } convertToSseUrl(httpUrl) { // 简单的URL转换逻辑,将HTTP URL转换为SSE URL // 这个逻辑可能需要根据具体的服务器实现进行调整 if (httpUrl.endsWith('/mcp')) { return httpUrl.replace('/mcp', '/sse'); } else if (httpUrl.endsWith('/')) { return httpUrl + 'sse'; } else { return httpUrl + '/sse'; } } async delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } getStatusIcon(status) { switch (status) { case 'connected': return chalk_1.default.green('●'); case 'failed': return chalk_1.default.red('●'); case 'not_found': return chalk_1.default.yellow('●'); case 'pending': return chalk_1.default.gray('●'); default: return chalk_1.default.gray('●'); } } getStatusColor(status) { switch (status) { case 'connected': return chalk_1.default.green; case 'failed': return chalk_1.default.red; case 'not_found': return chalk_1.default.yellow; case 'pending': return chalk_1.default.gray; default: return chalk_1.default.gray; } } getStatusText(status) { const messages = language_1.languageService.getMessages().systemDetector; switch (status) { case 'connected': return messages.mcpConnected; case 'failed': return messages.mcpFailed; case 'not_found': return messages.mcpNotFound; case 'pending': return messages.mcpConnecting; default: return 'Unknown'; } } async waitForUserToContinue() { const messages = language_1.languageService.getMessages().systemDetector; return new Promise((resolve) => { console.log(' ' + chalk_1.default.gray(messages.pressEnterToContinue)); // 确保stdin处于正确状态 if (process.stdin.isTTY) { process.stdin.setRawMode(true); } process.stdin.resume(); process.stdin.setEncoding('utf8'); const cleanup = () => { process.stdin.removeListener('data', keyHandler); if (process.stdin.isTTY) { process.stdin.setRawMode(false); } process.stdin.pause(); }; const keyHandler = (key) => { const keyCode = key.charCodeAt(0); switch (keyCode) { case 13: // Enter case 10: // Line feed cleanup(); resolve(); break; case 3: // Ctrl+C cleanup(); process.exit(); break; // 忽略其他按键 } }; process.stdin.on('data', keyHandler); }); } // 公开方法:获取已连接的MCP客户端 getMcpClient(serverName) { return this.mcpClients.get(serverName); } // 公开方法:获取所有连接的MCP客户端 getAllMcpClients() { return new Map(this.mcpClients); } // 公开方法:获取所有MCP工具的OpenAI格式定义 async getAllToolDefinitions() { const toolDefinitions = []; // 1. 获取内置服务的工具 try { if (this.globalMCPManager.isReady()) { const servicesInfo = this.globalMCPManager.getServicesInfo(); for (const serviceInfo of servicesInfo) { try { const tools = this.globalMCPManager.getServiceTools(serviceInfo.serviceName); // 将内置服务的工具转换为OpenAI格式 for (const tool of tools) { const openAITool = this.convertMcpToolToOpenAI(tool, serviceInfo.serviceName); if (openAITool) { toolDefinitions.push(openAITool); } } } catch (error) { console.warn(`Failed to get tools from built-in service ${serviceInfo.serviceName}:`, error); } } } else { // Attempt to initialize if not ready try { await this.globalMCPManager.initialize(); // Retry getting services after initialization const servicesInfo = this.globalMCPManager.getServicesInfo(); for (const serviceInfo of servicesInfo) { try { const tools = this.globalMCPManager.getServiceTools(serviceInfo.serviceName); // 将内置服务的工具转换为OpenAI格式 for (const tool of tools) { const openAITool = this.convertMcpToolToOpenAI(tool, serviceInfo.serviceName); if (openAITool) { toolDefinitions.push(openAITool); } } } catch (error) { console.warn(`Failed to get tools from built-in service ${serviceInfo.serviceName}:`, error); } } } catch (initError) { console.warn(`Failed to initialize GlobalMCPManager:`, initError); } } } catch (error) { console.warn('Failed to get built-in MCP tools:', error); } // 2. 获取外部服务的工具 for (const [serverName, client] of this.mcpClients.entries()) { try { // 获取服务器的工具列表 const tools = await client.getAllTools(); // 将MCP工具格式转换为OpenAI工具格式 for (const tool of tools) { const openAITool = this.convertMcpToolToOpenAI(tool, serverName); if (openAITool) { toolDefinitions.push(openAITool); } } } catch (error) { console.warn(`Failed to get tool definitions from server ${serverName}:`, error); } } return toolDefinitions; } // 私有方法:将MCP工具格式转换为OpenAI工具格式 convertMcpToolToOpenAI(mcpTool, serverName) { try { // 基本的工具定义结构 const openAITool = { type: 'function', function: { name: `${serverName}_${mcpTool.name}`, description: mcpTool.description || `Tool from ${serverName}: ${mcpTool.name}`, parameters: { type: 'object', properties: {}, required: [] } } }; // 如果MCP工具有参数定义,转换为OpenAI格式 if (mcpTool.inputSchema) { openAITool.function.parameters = mcpTool.inputSchema; } else if (mcpTool.parameters) { // 处理不同的参数格式 openAITool.function.parameters.properties = mcpTool.parameters; if (mcpTool.required && Array.isArray(mcpTool.required)) { openAITool.function.parameters.required = mcpTool.required; } } return openAITool; } catch (error) { console.warn(`Failed to convert MCP tool ${mcpTool.name} to OpenAI format:`, error); return null; } } // 公开方法:执行MCP工具调用 async executeMcpTool(toolName, parameters) { // 解析服务器名和工具名 const parts = toolName.split('_'); if (parts.length < 2) { throw new Error(`Invalid tool name format: ${toolName}`); } const serverName = parts[0]; const actualToolName = parts.slice(1).join('_'); // 检查是否为内置服务 const servicesInfo = this.globalMCPManager.getServicesInfo(); const isBuiltInService = servicesInfo.some(service => service.serviceName === serverName); if (isBuiltInService) { // 内置服务,使用全局MCP管理器 try { const request = { id: `tool-call-${Date.now()}`, method: actualToolName, params: parameters }; const response = await this.globalMCPManager.handleRequest(serverName, request); if (response.error) { throw new Error(response.error.message); } return response.result; } catch (error) { throw new Error(`Failed to execute built-in MCP tool ${toolName}: ${error instanceof Error ? error.message : 'Unknown error'}`); } } else { // 外部服务,使用MCP客户端 const client = this.mcpClients.get(serverName); if (!client) { throw new Error(`MCP client not found for server: ${serverName}`); } try { // 调用MCP工具 const result = await client.callTool({ name: actualToolName, arguments: parameters }); return result; } catch (error) { throw new Error(`Failed to execute external MCP tool ${toolName}: ${error instanceof Error ? error.message : 'Unknown error'}`); } } } // 清理资源 async cleanup() { // 清理MCP客户端 for (const client of this.mcpClients.values()) { try { // 注意:mcp-client可能没有显式的disconnect方法 // 如果有,可以在这里调用 } catch (error) { // 忽略清理错误 } } this.mcpClients.clear(); } } exports.SystemDetector = SystemDetector; //# sourceMappingURL=system-detector.js.map