mira-consciousness
Version:
Memory & Intelligence Retention Archive - Preserving The Spark
116 lines • 4.69 kB
JavaScript
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