UNPKG

@debugg-ai/cli

Version:
218 lines 9.14 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ServerManager = void 0; const child_process_1 = require("child_process"); const axios_1 = __importDefault(require("axios")); const system_logger_1 = require("../util/system-logger"); class ServerManager { constructor(options = {}) { this.servers = new Map(); this.serverConfigs = new Map(); this.defaultStartupTimeout = options.defaultStartupTimeout || 60000; this.defaultHealthPath = options.defaultHealthPath || '/'; } async startServer(id, config) { if (this.servers.has(id)) { system_logger_1.systemLogger.debug(`Server ${id} is already running`); return true; } const host = config.host || 'localhost'; const healthPath = config.healthPath || this.defaultHealthPath; const startupTimeout = config.startupTimeout || this.defaultStartupTimeout; const url = `http://${host}:${config.port}`; system_logger_1.systemLogger.debug(`Starting server ${id}: ${config.command} ${config.args?.join(' ') || ''}`); system_logger_1.systemLogger.debug(`Expected URL: ${url}`); return new Promise((resolve, reject) => { const serverProcess = (0, child_process_1.spawn)(config.command, config.args || [], { cwd: config.cwd || process.cwd(), env: { ...process.env, ...config.env }, stdio: ['ignore', 'pipe', 'pipe'], detached: false }); let resolved = false; const timeout = setTimeout(() => { if (!resolved) { resolved = true; this.stopServer(id); reject(new Error(`Server ${id} failed to start within ${startupTimeout}ms`)); } }, startupTimeout); serverProcess.on('error', (error) => { if (!resolved) { resolved = true; clearTimeout(timeout); reject(new Error(`Failed to start server ${id}: ${error.message}`)); } }); serverProcess.on('exit', (code, signal) => { system_logger_1.systemLogger.debug(`Server ${id} exited with code ${code}, signal ${signal}`); this.servers.delete(id); this.serverConfigs.delete(id); if (!resolved) { resolved = true; clearTimeout(timeout); reject(new Error(`Server ${id} exited unexpectedly with code ${code}`)); } }); if (serverProcess.stdout) { serverProcess.stdout.on('data', (data) => { const output = data.toString(); system_logger_1.systemLogger.debug(`[${id}] ${output.trim()}`); if (config.readyRegex && config.readyRegex.test(output)) { if (!resolved) { resolved = true; clearTimeout(timeout); this.servers.set(id, serverProcess); this.serverConfigs.set(id, config); system_logger_1.systemLogger.debug(`Server ${id} is ready (detected from output)`); resolve(true); } } }); } if (serverProcess.stderr) { serverProcess.stderr.on('data', (data) => { system_logger_1.systemLogger.error(`[${id}] ERROR: ${data.toString().trim()}`); }); } this.servers.set(id, serverProcess); this.serverConfigs.set(id, config); if (!config.readyRegex) { this.waitForServerHealth(url + healthPath, startupTimeout) .then((ready) => { if (!resolved) { resolved = true; clearTimeout(timeout); if (ready) { system_logger_1.systemLogger.debug(`Server ${id} is ready (health check passed)`); resolve(true); } else { this.stopServer(id); reject(new Error(`Server ${id} health check failed`)); } } }) .catch((error) => { if (!resolved) { resolved = true; clearTimeout(timeout); this.stopServer(id); reject(new Error(`Server ${id} health check error: ${error.message}`)); } }); } }); } async stopServer(id) { const serverProcess = this.servers.get(id); if (!serverProcess) { system_logger_1.systemLogger.debug(`Server ${id} is not running`); return; } system_logger_1.systemLogger.debug(`Stopping server ${id}...`); return new Promise((resolve) => { const timeout = setTimeout(() => { system_logger_1.systemLogger.debug(`Force killing server ${id}...`); serverProcess.kill('SIGKILL'); resolve(); }, 5000); serverProcess.on('exit', () => { clearTimeout(timeout); resolve(); }); serverProcess.kill('SIGTERM'); }); } async stopAllServers() { const serverIds = Array.from(this.servers.keys()); system_logger_1.systemLogger.debug(`Stopping ${serverIds.length} servers...`); const stopPromises = serverIds.map(id => this.stopServer(id)); await Promise.all(stopPromises); this.servers.clear(); this.serverConfigs.clear(); system_logger_1.systemLogger.debug('All servers stopped'); } getServerStatus(id) { const serverProcess = this.servers.get(id); const config = this.serverConfigs.get(id); if (!serverProcess || !config) { return { running: false }; } const host = config.host || 'localhost'; const url = `http://${host}:${config.port}`; return { running: !serverProcess.killed, pid: serverProcess.pid || undefined, port: config.port, url }; } async checkServerHealth(id) { const config = this.serverConfigs.get(id); if (!config) { return false; } const host = config.host || 'localhost'; const healthPath = config.healthPath || this.defaultHealthPath; const url = `http://${host}:${config.port}${healthPath}`; return this.waitForServerHealth(url, 5000); } async waitForServerHealth(url, timeout) { const startTime = Date.now(); const pollInterval = 1000; while (Date.now() - startTime < timeout) { try { const response = await axios_1.default.get(url, { timeout: 3000, validateStatus: (status) => status < 500 }); if (response.status >= 200 && response.status < 400) { return true; } } catch (error) { const axiosError = error; if (axiosError.response && axiosError.response.status < 500) { return true; } } await new Promise(resolve => setTimeout(resolve, pollInterval)); } return false; } getAllServerStatus() { const status = {}; for (const id of this.servers.keys()) { status[id] = this.getServerStatus(id); } return status; } isServerRunning(id) { const serverProcess = this.servers.get(id); return serverProcess ? !serverProcess.killed : false; } getServerUrl(id) { const config = this.serverConfigs.get(id); if (!config) return null; const host = config.host || 'localhost'; return `http://${host}:${config.port}`; } async waitForServer(id, timeout = 60000) { const config = this.serverConfigs.get(id); if (!config) { throw new Error(`Server configuration for ${id} not found`); } const host = config.host || 'localhost'; const healthPath = config.healthPath || this.defaultHealthPath; const url = `http://${host}:${config.port}${healthPath}`; system_logger_1.systemLogger.debug(`Waiting for server ${id} to be ready at ${url}...`); return this.waitForServerHealth(url, timeout); } } exports.ServerManager = ServerManager; //# sourceMappingURL=server-manager.js.map