UNPKG

il2cpp-dump-analyzer-mcp

Version:

Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games

498 lines 16.8 kB
"use strict"; /** * Container lifecycle management for IL2CPP dump analyzer MCP system * Handles startup, shutdown, health monitoring, and restart policies */ Object.defineProperty(exports, "__esModule", { value: true }); exports.LifecycleManager = void 0; const events_1 = require("events"); /** * Container lifecycle management service */ class LifecycleManager extends events_1.EventEmitter { constructor(config) { super(); this.shutdownHandlers = []; this.startupHandlers = []; this.config = { startupTimeout: 60000, // 1 minute shutdownTimeout: 30000, // 30 seconds healthCheckInterval: 30000, // 30 seconds restartPolicy: 'on-failure', maxRestarts: 5, restartDelay: 5000, // 5 seconds dependencyChecks: true, gracefulShutdown: true, ...config }; this.state = { phase: 'stopped', restartCount: 0, isHealthy: false, dependencies: this.initializeDependencies() }; this.setupSignalHandlers(); } /** * Initialize dependency definitions */ initializeDependencies() { return [ { name: 'supabase-db', status: 'unknown', lastCheck: new Date(), required: true, timeout: 5000 }, { name: 'supabase-rest', status: 'unknown', lastCheck: new Date(), required: true, checkUrl: process.env.SUPABASE_URL, timeout: 5000 }, { name: 'xenova-models', status: 'unknown', lastCheck: new Date(), required: false, timeout: 10000 } ]; } /** * Initialize lifecycle manager with services */ initialize(services) { this.healthService = services.healthService; this.metricsService = services.metricsService; // Listen to health status changes this.healthService.on('healthCheck', (status) => { this.handleHealthStatusChange(status); }); // Register shutdown handlers for services this.healthService.onShutdown(async () => { console.log('Shutting down health service...'); this.healthService?.stop(); }); this.metricsService.on('started', () => { console.log('Metrics service started'); }); console.log('Lifecycle manager initialized'); } /** * Start the application lifecycle */ async start() { if (this.state.phase === 'running' || this.state.phase === 'starting') { console.warn('Application is already starting or running'); return; } console.log('Starting application lifecycle...'); this.setState({ phase: 'starting', startTime: new Date() }); try { // Set startup timeout const startupPromise = this.executeStartup(); const timeoutPromise = new Promise((_, reject) => { this.startupTimer = setTimeout(() => { reject(new Error(`Startup timeout after ${this.config.startupTimeout}ms`)); }, this.config.startupTimeout); }); await Promise.race([startupPromise, timeoutPromise]); // Clear startup timer if (this.startupTimer) { clearTimeout(this.startupTimer); this.startupTimer = undefined; } this.setState({ phase: 'running', isHealthy: true }); console.log('Application started successfully'); this.emit('started'); } catch (error) { console.error('Application startup failed:', error); this.setState({ phase: 'failed' }); this.emit('startupFailed', error); // Handle restart policy await this.handleRestartPolicy(error); } } /** * Execute startup sequence */ async executeStartup() { console.log('Executing startup sequence...'); // Check dependencies first if (this.config.dependencyChecks) { await this.checkDependencies(); const requiredDepsHealthy = this.state.dependencies .filter(dep => dep.required) .every(dep => dep.status === 'healthy'); if (!requiredDepsHealthy) { throw new Error('Required dependencies are not healthy'); } } // Execute startup handlers for (const [index, handler] of this.startupHandlers.entries()) { try { console.log(`Executing startup handler ${index + 1}/${this.startupHandlers.length}`); await handler(); } catch (error) { throw new Error(`Startup handler ${index + 1} failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } // Start services if (this.healthService) { this.healthService.start(); } if (this.metricsService) { this.metricsService.start(); } // Start dependency monitoring if (this.config.dependencyChecks) { this.startDependencyMonitoring(); } } /** * Stop the application lifecycle */ async stop() { if (this.state.phase === 'stopped' || this.state.phase === 'stopping') { console.warn('Application is already stopping or stopped'); return; } console.log('Stopping application lifecycle...'); this.setState({ phase: 'stopping' }); try { if (this.config.gracefulShutdown) { await this.executeGracefulShutdown(); } else { await this.executeImmediateShutdown(); } this.setState({ phase: 'stopped', isHealthy: false }); console.log('Application stopped successfully'); this.emit('stopped'); } catch (error) { console.error('Application shutdown failed:', error); this.setState({ phase: 'failed' }); this.emit('shutdownFailed', error); } } /** * Execute graceful shutdown */ async executeGracefulShutdown() { console.log('Executing graceful shutdown...'); const shutdownPromise = this.executeShutdownSequence(); const timeoutPromise = new Promise((_, reject) => { this.shutdownTimer = setTimeout(() => { reject(new Error(`Shutdown timeout after ${this.config.shutdownTimeout}ms`)); }, this.config.shutdownTimeout); }); try { await Promise.race([shutdownPromise, timeoutPromise]); } finally { if (this.shutdownTimer) { clearTimeout(this.shutdownTimer); this.shutdownTimer = undefined; } } } /** * Execute shutdown sequence */ async executeShutdownSequence() { // Stop dependency monitoring this.stopDependencyMonitoring(); // Stop services if (this.metricsService) { this.metricsService.stop(); } if (this.healthService) { this.healthService.stop(); } // Execute shutdown handlers for (const [index, handler] of this.shutdownHandlers.entries()) { try { console.log(`Executing shutdown handler ${index + 1}/${this.shutdownHandlers.length}`); await Promise.race([ handler(), new Promise((_, reject) => setTimeout(() => reject(new Error('Handler timeout')), 5000)) ]); } catch (error) { console.error(`Shutdown handler ${index + 1} failed:`, error); } } } /** * Execute immediate shutdown */ async executeImmediateShutdown() { console.log('Executing immediate shutdown...'); // Stop all timers this.stopDependencyMonitoring(); if (this.shutdownTimer) { clearTimeout(this.shutdownTimer); } if (this.startupTimer) { clearTimeout(this.startupTimer); } // Force stop services this.metricsService?.stop(); this.healthService?.stop(); } /** * Handle health status changes */ handleHealthStatusChange(status) { const wasHealthy = this.state.isHealthy; const isHealthy = status.status === 'healthy'; this.setState({ isHealthy }); // Handle health status transitions if (wasHealthy && !isHealthy) { console.warn('Application became unhealthy'); this.emit('unhealthy', status); // Consider restart based on policy if (this.config.restartPolicy === 'on-failure' || this.config.restartPolicy === 'always') { this.scheduleRestart('health check failure'); } } else if (!wasHealthy && isHealthy) { console.log('Application became healthy'); this.emit('healthy', status); } } /** * Handle restart policy */ async handleRestartPolicy(error) { if (this.config.restartPolicy === 'never') { console.log('Restart policy is "never", not restarting'); return; } if (this.state.restartCount >= this.config.maxRestarts) { console.error(`Maximum restart attempts (${this.config.maxRestarts}) reached`); this.emit('maxRestartsReached'); return; } if (this.config.restartPolicy === 'on-failure' || this.config.restartPolicy === 'always') { this.scheduleRestart(error); } } /** * Schedule a restart */ scheduleRestart(reason) { if (this.restartTimer) { return; // Restart already scheduled } console.log(`Scheduling restart in ${this.config.restartDelay}ms due to: ${reason}`); this.restartTimer = setTimeout(async () => { this.restartTimer = undefined; await this.restart(reason); }, this.config.restartDelay); } /** * Restart the application */ async restart(reason) { console.log(`Restarting application (attempt ${this.state.restartCount + 1}/${this.config.maxRestarts})`); this.setState({ restartCount: this.state.restartCount + 1, lastRestart: new Date() }); this.emit('restarting', { reason, attempt: this.state.restartCount }); try { await this.stop(); await new Promise(resolve => setTimeout(resolve, 1000)); // Brief pause await this.start(); } catch (error) { console.error('Restart failed:', error); this.emit('restartFailed', error); } } /** * Check dependencies health */ async checkDependencies() { console.log('Checking dependencies...'); const checkPromises = this.state.dependencies.map(async (dep) => { try { const isHealthy = await this.checkDependency(dep); dep.status = isHealthy ? 'healthy' : 'unhealthy'; dep.lastCheck = new Date(); } catch (error) { console.warn(`Dependency check failed for ${dep.name}:`, error); dep.status = 'unhealthy'; dep.lastCheck = new Date(); } }); await Promise.all(checkPromises); const healthyCount = this.state.dependencies.filter(d => d.status === 'healthy').length; console.log(`Dependencies: ${healthyCount}/${this.state.dependencies.length} healthy`); } /** * Check individual dependency */ async checkDependency(dep) { // Implement specific dependency checks based on type switch (dep.name) { case 'supabase-db': return this.checkDatabaseDependency(dep); case 'supabase-rest': return this.checkRestApiDependency(dep); case 'xenova-models': return this.checkModelsDependency(dep); default: return true; // Unknown dependencies are considered healthy } } /** * Check database dependency */ async checkDatabaseDependency(dep) { try { // Use health service to check database if available if (this.healthService) { const status = await this.healthService.getHealthStatus(); const dbComponent = status.components.find(c => c.name === 'database'); return dbComponent?.status === 'healthy'; } return true; } catch (error) { console.warn(`Database dependency check failed:`, error); return false; } } /** * Check REST API dependency */ async checkRestApiDependency(dep) { if (!dep.checkUrl) { return true; } try { // Simple HTTP health check const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), dep.timeout); const response = await fetch(`${dep.checkUrl}/health`, { method: 'GET', signal: controller.signal, headers: { 'User-Agent': 'IL2CPP-MCP-HealthCheck/1.0' } }); clearTimeout(timeoutId); return response.ok; } catch (error) { console.warn(`REST API dependency check failed:`, error); return false; } } /** * Check models dependency (Xenova models availability) */ async checkModelsDependency(dep) { try { // Check if model cache directory exists and is accessible const fs = await import('fs/promises'); const modelCachePath = process.env.MODEL_CACHE_PATH || '/app/models'; await fs.access(modelCachePath); return true; } catch (error) { console.warn(`Models dependency check failed:`, error); return false; } } /** * Start dependency monitoring */ startDependencyMonitoring() { if (this.dependencyCheckInterval) { return; } console.log('Starting dependency monitoring...'); this.dependencyCheckInterval = setInterval(async () => { await this.checkDependencies(); }, this.config.healthCheckInterval); } /** * Stop dependency monitoring */ stopDependencyMonitoring() { if (this.dependencyCheckInterval) { clearInterval(this.dependencyCheckInterval); this.dependencyCheckInterval = undefined; console.log('Dependency monitoring stopped'); } } /** * Setup signal handlers */ setupSignalHandlers() { const signals = ['SIGTERM', 'SIGINT', 'SIGUSR2']; signals.forEach(signal => { process.on(signal, async () => { console.log(`Received ${signal}, initiating graceful shutdown...`); await this.stop(); process.exit(0); }); }); } /** * Update lifecycle state */ setState(updates) { this.state = { ...this.state, ...updates }; this.emit('stateChanged', this.state); } /** * Register startup handler */ onStartup(handler) { this.startupHandlers.push(handler); } /** * Register shutdown handler */ onShutdown(handler) { this.shutdownHandlers.push(handler); } /** * Get current lifecycle state */ getState() { return { ...this.state }; } /** * Get lifecycle configuration */ getConfig() { return { ...this.config }; } /** * Force restart (manual trigger) */ async forceRestart(reason = 'manual trigger') { console.log(`Force restart requested: ${reason}`); await this.restart(reason); } /** * Reset restart counter */ resetRestartCounter() { this.setState({ restartCount: 0 }); console.log('Restart counter reset'); } } exports.LifecycleManager = LifecycleManager; //# sourceMappingURL=lifecycle-manager.js.map