UNPKG

giga-code

Version:

A personal AI CLI assistant powered by Grok for local development.

218 lines 7.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpMcpManager = void 0; const child_process_1 = require("child_process"); const added_mcp_servers_1 = require("../utils/added-mcp-servers"); const http_mcp_client_1 = require("./http-mcp-client"); class HttpMcpManager { constructor() { this.runningServers = new Map(); } static getInstance() { if (!HttpMcpManager.instance) { HttpMcpManager.instance = new HttpMcpManager(); } return HttpMcpManager.instance; } async startAllHttpServers() { const servers = (0, added_mcp_servers_1.getEnabledMcpServers)().filter(s => s.type === 'http'); if (servers.length === 0) { return; } const startPromises = servers.map(server => this.startHttpServer(server)); const results = await Promise.allSettled(startPromises); results.forEach((result, index) => { if (result.status === 'rejected') { } }); } async startHttpServer(server) { if (server.type !== 'http') { throw new Error(`Server ${server.name} is not an HTTP server`); } // Check if already running const existing = this.runningServers.get(server.name); if (existing && existing.client.isConnectedToServer()) { return existing.client; } // Assign port if not already assigned if (!server.port) { server.port = (0, added_mcp_servers_1.getNextAvailablePort)(); } try { // Start the server process const process = await this.spawnServerProcess(server); // Wait for server to be ready await this.waitForServerReady(server.port, 10000); // 10 second timeout // Create client and connect const updatedServer = { ...server, httpUrl: `http://localhost:${server.port}/mcp`, }; const client = new http_mcp_client_1.HttpMcpClient(updatedServer); await client.connect(); // Store running server info const runningServer = { server: updatedServer, process, client, port: server.port, startTime: new Date(), restartCount: existing?.restartCount || 0, }; this.runningServers.set(server.name, runningServer); return client; } catch (error) { throw error; } } async spawnServerProcess(server) { if (!server.command) { throw new Error(`No command specified for server ${server.name}`); } return new Promise((resolve, reject) => { const commandParts = server.command.split(' '); const command = commandParts[0]; const args = [...commandParts.slice(1), ...(server.args || [])]; // Add port to environment const env = { ...process.env, ...server.env, PORT: server.port?.toString(), MCP_PORT: server.port?.toString(), }; const childProcess = (0, child_process_1.spawn)(command, args, { stdio: ['pipe', 'pipe', 'pipe'], env, detached: false, }); childProcess.on('error', (error) => { reject(new Error(`Failed to spawn server process: ${error.message}`)); }); childProcess.on('exit', (code, signal) => { this.runningServers.delete(server.name); // Auto-restart if enabled and not intentionally stopped if (server.enabled && code !== 0 && !signal) { this.handleServerRestart(server); } }); childProcess.stdout?.on('data', (data) => { }); childProcess.stderr?.on('data', (data) => { }); // Give the process a moment to start setTimeout(() => { if (childProcess.killed) { reject(new Error('Process was killed during startup')); } else { resolve(childProcess); } }, 1000); }); } async waitForServerReady(port, timeout) { const startTime = Date.now(); while (Date.now() - startTime < timeout) { try { const response = await fetch(`http://localhost:${port}/mcp`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'ping', params: {}, }), }); if (response.ok) { return; // Server is ready } } catch (error) { // Server not ready yet, continue waiting } await new Promise(resolve => setTimeout(resolve, 500)); } throw new Error(`Server did not become ready within ${timeout}ms`); } async handleServerRestart(server) { const existing = this.runningServers.get(server.name); if (!existing) return; existing.restartCount++; // Exponential backoff for restarts const delay = Math.min(1000 * Math.pow(2, existing.restartCount), 30000); setTimeout(async () => { try { await this.startHttpServer(server); } catch (error) { } }, delay); } async stopHttpServer(serverName) { const running = this.runningServers.get(serverName); if (!running) { return; } try { await running.client.disconnect(); running.process.kill('SIGTERM'); // Give it time to shut down gracefully setTimeout(() => { if (!running.process.killed) { running.process.kill('SIGKILL'); } }, 5000); this.runningServers.delete(serverName); } catch (error) { } } async stopAllHttpServers() { const stopPromises = Array.from(this.runningServers.keys()).map(serverName => this.stopHttpServer(serverName)); await Promise.all(stopPromises); } getRunningServers() { return Array.from(this.runningServers.keys()); } getHttpClient(serverName) { const running = this.runningServers.get(serverName); return running?.client || null; } isServerRunning(serverName) { const running = this.runningServers.get(serverName); return running ? running.client.isConnectedToServer() : false; } async healthCheck() { const results = new Map(); for (const [serverName, running] of this.runningServers) { try { const isHealthy = await running.client.healthCheck(); results.set(serverName, isHealthy); } catch (error) { results.set(serverName, false); } } return results; } getServerStats(serverName) { const running = this.runningServers.get(serverName); if (!running) return null; return { name: serverName, port: running.port, startTime: running.startTime, uptime: Date.now() - running.startTime.getTime(), restartCount: running.restartCount, isConnected: running.client.isConnectedToServer(), }; } } exports.HttpMcpManager = HttpMcpManager; HttpMcpManager.instance = null; //# sourceMappingURL=http-mcp-manager.js.map