UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

200 lines (199 loc) 7.29 kB
import logger from '../../../logger.js'; export class ServiceLifecycleManager { static instance = null; services = new Map(); dependencies = []; startupInProgress = false; shutdownInProgress = false; constructor() { } static getInstance() { if (!ServiceLifecycleManager.instance) { ServiceLifecycleManager.instance = new ServiceLifecycleManager(); } return ServiceLifecycleManager.instance; } static resetInstance() { ServiceLifecycleManager.instance = null; } registerService(config) { const service = { ...config, isStarted: false, isDisposed: false }; this.services.set(config.name, service); logger.debug(`Service registered: ${config.name}`); } registerDependency(service, dependsOn) { this.dependencies.push({ service, dependsOn }); logger.debug(`Dependencies registered for ${service}: ${dependsOn.join(', ')}`); } async startAllServices() { if (this.startupInProgress) { logger.warn('Service startup already in progress, skipping'); return; } if (this.shutdownInProgress) { logger.warn('Service shutdown in progress, cannot start services'); return; } this.startupInProgress = true; try { const startOrder = this.calculateStartupOrder(); logger.info(`Starting services in order: ${startOrder.join(' -> ')}`); for (const serviceName of startOrder) { await this.startService(serviceName); } logger.info('All services started successfully'); } catch (error) { logger.error('Failed to start all services', { error }); throw error; } finally { this.startupInProgress = false; } } async stopAllServices() { if (this.shutdownInProgress) { logger.warn('Service shutdown already in progress, skipping'); return; } this.shutdownInProgress = true; try { const stopOrder = this.calculateStartupOrder().reverse(); logger.info(`Stopping services in order: ${stopOrder.join(' -> ')}`); for (const serviceName of stopOrder) { await this.stopService(serviceName); } logger.info('All services stopped successfully'); } catch (error) { logger.error('Failed to stop all services', { error }); throw error; } finally { this.shutdownInProgress = false; } } async disposeAllServices() { await this.stopAllServices(); const disposeOrder = this.calculateStartupOrder().reverse(); logger.info(`Disposing services in order: ${disposeOrder.join(' -> ')}`); for (const serviceName of disposeOrder) { await this.disposeService(serviceName); } this.services.clear(); this.dependencies = []; logger.info('All services disposed and state cleared'); } async startService(serviceName) { const service = this.services.get(serviceName); if (!service) { throw new Error(`Service not registered: ${serviceName}`); } if (service.isStarted || service.isDisposed) { logger.debug(`Service ${serviceName} already started or disposed, skipping`); return; } try { const deps = this.dependencies.find(d => d.service === serviceName); if (deps) { for (const depName of deps.dependsOn) { const depService = this.services.get(depName); if (!depService?.isStarted) { throw new Error(`Dependency ${depName} not started for service ${serviceName}`); } } } if (service.startMethod && typeof service.instance[service.startMethod] === 'function') { await service.instance[service.startMethod](); } service.isStarted = true; logger.info(`Service started: ${serviceName}`); } catch (error) { logger.error(`Failed to start service: ${serviceName}`, { error }); throw error; } } async stopService(serviceName) { const service = this.services.get(serviceName); if (!service || !service.isStarted || service.isDisposed) { return; } try { if (service.stopMethod && typeof service.instance[service.stopMethod] === 'function') { await service.instance[service.stopMethod](); } service.isStarted = false; logger.info(`Service stopped: ${serviceName}`); } catch (error) { logger.error(`Failed to stop service: ${serviceName}`, { error }); } } async disposeService(serviceName) { const service = this.services.get(serviceName); if (!service || service.isDisposed) { return; } try { if (service.isStarted) { await this.stopService(serviceName); } if (service.disposeMethod && typeof service.instance[service.disposeMethod] === 'function') { await service.instance[service.disposeMethod](); } if (service.resetStaticMethod && typeof service.instance.constructor[service.resetStaticMethod] === 'function') { service.instance.constructor[service.resetStaticMethod](); } service.isDisposed = true; logger.info(`Service disposed: ${serviceName}`); } catch (error) { logger.error(`Failed to dispose service: ${serviceName}`, { error }); } } calculateStartupOrder() { const visited = new Set(); const visiting = new Set(); const order = []; const visit = (serviceName) => { if (visited.has(serviceName)) { return; } if (visiting.has(serviceName)) { throw new Error(`Circular dependency detected involving service: ${serviceName}`); } visiting.add(serviceName); const deps = this.dependencies.find(d => d.service === serviceName); if (deps) { for (const depName of deps.dependsOn) { visit(depName); } } visiting.delete(serviceName); visited.add(serviceName); order.push(serviceName); }; for (const serviceName of this.services.keys()) { visit(serviceName); } return order; } getServiceStatus(serviceName) { return this.services.get(serviceName) || null; } getAllServiceStatuses() { return new Map(this.services); } areAllServicesHealthy() { for (const service of this.services.values()) { if (!service.isStarted || service.isDisposed) { return false; } } return true; } }