fix: auto-terminate stale bot instances to prevent port conflicts

- Added execSync import for child_process
- Modified acquirePidfile() to send SIGTERM to old instances
- Waits up to 2.5s for graceful shutdown with checks every 500ms
- Prevents continuous restart loop when old PID holds port 3001
- Bot now self-heals on restart instead of crashing
This commit is contained in:
admin
2026-05-06 10:32:30 +00:00
Unverified
parent d51ab51032
commit c164446a9c

View File

@@ -6,6 +6,7 @@ import { createServer } from 'http';
import { WebSocketServer } from 'ws'; import { WebSocketServer } from 'ws';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { execSync } from 'child_process';
import { logger } from '../utils/logger.js'; import { logger } from '../utils/logger.js';
import { checkEnv } from '../utils/env.js'; import { checkEnv } from '../utils/env.js';
import { getRTK } from '../utils/rtk.js'; import { getRTK } from '../utils/rtk.js';
@@ -38,15 +39,32 @@ function acquirePidfile() {
const oldPid = parseInt(fs.readFileSync(PIDFILE, 'utf8').trim()); const oldPid = parseInt(fs.readFileSync(PIDFILE, 'utf8').trim());
// Check if old process is still alive // Check if old process is still alive
try { process.kill(oldPid, 0); try { process.kill(oldPid, 0);
// Same PID or different process - just log warning, don't kill // Old process is still running - kill it to prevent port conflicts
if (oldPid !== process.pid) { if (oldPid !== process.pid) {
logger.warn(`⚠ Another zCode instance (PID ${oldPid}) is running — keeping this instance (PID ${process.pid})`); logger.warn(`⚠ Another zCode instance (PID ${oldPid}) detected — terminating to prevent port conflict`);
try {
process.kill(oldPid, 'SIGTERM');
logger.info(` ✓ Sent SIGTERM to PID ${oldPid}`);
// Give it time to shut down gracefully
for (let i = 0; i < 5; i++) {
try { process.kill(oldPid, 0); }
catch { break; } // Process is dead
if (i < 4) {
// Sleep 500ms between checks
execSync('sleep 0.5', { stdio: 'ignore' });
}
}
} catch (e) {
logger.warn(` Failed to kill old PID ${oldPid}: ${e.message}`);
}
} else { } else {
logger.info(`✓ Pidfile already acquired by this instance (PID ${process.pid})`); logger.info(`✓ Pidfile already acquired by this instance (PID ${process.pid})`);
} }
// Don't kill - just continue with current process // Continue - old process should be dead now
return; } catch {
} catch { /* old PID dead, safe to acquire */ } // Old PID dead, safe to acquire
logger.info(` Old PID ${oldPid} is no longer running`);
}
} }
fs.writeFileSync(PIDFILE, process.pid.toString()); fs.writeFileSync(PIDFILE, process.pid.toString());
logger.info(`✓ Pidfile acquired: ${PIDFILE} (PID ${process.pid})`); logger.info(`✓ Pidfile acquired: ${PIDFILE} (PID ${process.pid})`);