openai-cli-unofficial
Version:
A powerful OpenAI CLI Coding Agent built with TypeScript
591 lines • 24 kB
JavaScript
;
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