UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

116 lines 4.69 kB
import { execSync } from 'child_process'; import chalk from 'chalk'; export class ProcessCleanup { static DEBUG = process.env.MIRA_DEBUG === 'true'; /** * Clean up orphaned MIRA Python processes * This runs automatically on startup to prevent resource leaks * @returns The number of processes cleaned up */ static async cleanupOrphanedProcesses() { try { // Find all MIRA Python processes (excluding current command) const psOutput = execSync('ps aux | grep -E "(python3.*mira|mira.*python)" | grep -v grep | grep -v "ps aux"', { encoding: 'utf8', stdio: 'pipe' }).trim(); if (!psOutput) { if (this.DEBUG) { console.log(chalk.gray('No orphaned MIRA processes found')); } return 0; } const processes = psOutput.split('\n').map(line => { const parts = line.split(/\s+/); return { pid: parseInt(parts[1]), command: parts.slice(10).join(' '), elapsed: parts[9] }; }); // Filter out legitimate daemon processes and very recent processes const orphaned = processes.filter(proc => { // Keep the main daemon process if (proc.command.includes('daemon.py')) { return false; } // Keep the retrospection scheduler (it's supposed to run long-term) if (proc.command.includes('retrospection_scheduler')) { return false; } // Kill stuck direct_interface processes (these should be quick) if (proc.command.includes('direct_interface.py')) { return true; } // Kill any process that's been running for more than 5 minutes const elapsedMatch = proc.elapsed.match(/(\d+):(\d+)/); if (elapsedMatch) { const minutes = parseInt(elapsedMatch[1]); if (minutes >= 5) { return true; } } return false; }); if (orphaned.length === 0) { if (this.DEBUG) { console.log(chalk.gray('No orphaned processes need cleanup')); } return 0; } console.log(chalk.yellow(`🧹 Cleaning up ${orphaned.length} orphaned MIRA processes...`)); // Kill orphaned processes const pids = orphaned.map(p => p.pid).join(' '); // Try graceful termination first try { execSync(`kill -TERM ${pids} 2>/dev/null`, { stdio: 'pipe' }); // Wait a moment for graceful shutdown await new Promise(resolve => setTimeout(resolve, 500)); } catch { // Ignore errors from kill command } // Force kill any remaining try { execSync(`kill -KILL ${pids} 2>/dev/null`, { stdio: 'pipe' }); } catch { // Ignore errors from kill command } console.log(chalk.green(`✅ Cleaned up ${orphaned.length} orphaned processes`)); if (this.DEBUG) { orphaned.forEach(proc => { console.log(chalk.gray(` - PID ${proc.pid}: ${proc.command.substring(0, 80)}...`)); }); } return orphaned.length; } catch (error) { // Silently handle errors (ps/grep might return non-zero if no matches) if (this.DEBUG && error instanceof Error) { console.log(chalk.gray('Process cleanup check completed')); } return 0; } } /** * Register cleanup on process exit */ static registerExitHandlers() { const cleanup = () => { try { // Quick cleanup of any processes we spawned execSync('pkill -TERM -f "python3.*direct_interface.py" 2>/dev/null', { stdio: 'pipe' }); } catch { // Ignore errors } }; process.on('exit', cleanup); process.on('SIGINT', cleanup); process.on('SIGTERM', cleanup); process.on('uncaughtException', (error) => { console.error(chalk.red('Uncaught exception:'), error); cleanup(); process.exit(1); }); } } //# sourceMappingURL=process-cleanup.js.map