@debugg-ai/cli
Version:
CLI tool for running DebuggAI tests in CI/CD environments
218 lines • 9.14 kB
JavaScript
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
;