vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
229 lines (228 loc) • 8.19 kB
JavaScript
import os from 'os';
import logger from '../../../logger.js';
import { getMemoryStats } from '../parser.js';
import { MemoryLeakDetector } from './memoryLeakDetector.js';
import { ResourceTracker } from './resourceTracker.js';
export class ProcessLifecycleManager {
options;
memoryLeakDetector = null;
resourceTracker = null;
memoryManager = null;
healthCheckTimer = null;
gcTimer = null;
isInitialized = false;
healthStatus = 'healthy';
cpuUsage = { user: 0, system: 0 };
lastCpuUsage = { user: 0, system: 0 };
lastCpuUsageTime = process.hrtime.bigint();
activeJobs = new Set();
healthListeners = [];
static DEFAULT_OPTIONS = {
maxMemoryPercentage: 0.7,
healthCheckInterval: 30 * 1000,
degradationThreshold: 0.8,
emergencyThreshold: 0.9,
autoMonitor: true,
gcInterval: 5 * 60 * 1000
};
constructor(options = {}) {
this.options = {
...ProcessLifecycleManager.DEFAULT_OPTIONS,
...options
};
}
async init(memoryManager, resourceTracker) {
if (this.isInitialized) {
return;
}
this.memoryLeakDetector = new MemoryLeakDetector({
autoDetect: true,
checkInterval: this.options.healthCheckInterval
});
await this.memoryLeakDetector.init();
this.memoryManager = memoryManager || null;
this.resourceTracker = resourceTracker || new ResourceTracker();
if (this.options.autoMonitor) {
this.startHealthMonitoring();
this.startPeriodicGC();
}
this.isInitialized = true;
logger.info('ProcessLifecycleManager initialized');
}
startHealthMonitoring() {
if (this.healthCheckTimer) {
clearInterval(this.healthCheckTimer);
}
this.lastCpuUsage = process.cpuUsage();
this.lastCpuUsageTime = process.hrtime.bigint();
this.healthCheckTimer = setInterval(() => {
this.checkProcessHealth();
}, this.options.healthCheckInterval);
logger.debug(`Process health monitoring started with interval: ${this.options.healthCheckInterval}ms`);
}
stopHealthMonitoring() {
if (this.healthCheckTimer) {
clearInterval(this.healthCheckTimer);
this.healthCheckTimer = null;
}
logger.debug('Process health monitoring stopped');
}
startPeriodicGC() {
if (this.gcTimer) {
clearInterval(this.gcTimer);
}
this.gcTimer = setInterval(() => {
this.runGarbageCollection();
}, this.options.gcInterval);
logger.debug(`Periodic garbage collection started with interval: ${this.options.gcInterval}ms`);
}
stopPeriodicGC() {
if (this.gcTimer) {
clearInterval(this.gcTimer);
this.gcTimer = null;
}
logger.debug('Periodic garbage collection stopped');
}
checkProcessHealth() {
const memoryStats = getMemoryStats();
const memoryUsagePercentage = memoryStats.memoryUsagePercentage;
const currentCpuUsage = process.cpuUsage();
const currentTime = process.hrtime.bigint();
const elapsedTime = Number(currentTime - this.lastCpuUsageTime) / 1e9;
const userUsage = (currentCpuUsage.user - this.lastCpuUsage.user) / 1000 / elapsedTime;
const systemUsage = (currentCpuUsage.system - this.lastCpuUsage.system) / 1000 / elapsedTime;
const cpuUsagePercentage = (userUsage + systemUsage) / (os.cpus().length * 100);
this.lastCpuUsage = currentCpuUsage;
this.lastCpuUsageTime = currentTime;
const memoryLeakResult = this.memoryLeakDetector?.analyzeMemoryTrend();
const memoryLeakDetected = memoryLeakResult?.leakDetected || false;
let newStatus = 'healthy';
if (memoryUsagePercentage > this.options.emergencyThreshold) {
newStatus = 'critical';
this.handleCriticalMemory();
}
else if (memoryUsagePercentage > this.options.degradationThreshold || memoryLeakDetected) {
newStatus = 'degraded';
this.handleDegradedMemory();
}
else if (this.healthStatus === 'critical' || this.healthStatus === 'degraded') {
newStatus = 'recovering';
}
const previousStatus = this.healthStatus;
this.healthStatus = newStatus;
if (previousStatus !== newStatus) {
logger.info(`Process health status changed from ${previousStatus} to ${newStatus}`);
}
const healthInfo = {
status: newStatus,
memoryUsagePercentage,
cpuUsagePercentage,
memoryLeakDetected,
timestamp: Date.now(),
memoryStats,
activeJobs: this.activeJobs.size
};
this.notifyHealthListeners(healthInfo);
return healthInfo;
}
handleDegradedMemory() {
logger.warn('Memory usage is degraded, applying graceful degradation measures');
this.runGarbageCollection();
if (this.memoryManager) {
this.memoryManager.pruneCaches();
}
}
handleCriticalMemory() {
logger.error('Memory usage is critical, applying emergency measures');
this.runGarbageCollection();
if (this.memoryManager) {
this.memoryManager.pruneCaches();
}
if (this.memoryLeakDetector) {
this.memoryLeakDetector.takeHeapSnapshot()
.then(snapshotPath => {
logger.info(`Took emergency heap snapshot: ${snapshotPath}`);
})
.catch(error => {
logger.error(`Failed to take emergency heap snapshot: ${error}`);
});
}
}
runGarbageCollection() {
logger.info('Running garbage collection');
if (this.memoryManager) {
this.memoryManager.runGarbageCollection();
}
else {
if (typeof global !== 'undefined' && global.gc) {
try {
logger.debug('Calling global.gc() to suggest garbage collection');
global.gc();
}
catch (error) {
logger.warn(`Failed to suggest garbage collection: ${error}`);
}
}
else {
logger.debug('global.gc not available. Run Node.js with --expose-gc to enable manual GC suggestions');
}
}
}
registerJob(jobId) {
this.activeJobs.add(jobId);
if (this.resourceTracker) {
this.resourceTracker.trackJob(jobId);
}
logger.debug(`Registered job: ${jobId}`);
}
async unregisterJob(jobId) {
this.activeJobs.delete(jobId);
if (this.resourceTracker) {
await this.resourceTracker.cleanupJob(jobId);
}
logger.debug(`Unregistered job: ${jobId}`);
}
getActiveJobCount() {
return this.activeJobs.size;
}
getActiveJobIds() {
return Array.from(this.activeJobs);
}
addHealthListener(listener) {
this.healthListeners.push(listener);
}
removeHealthListener(listener) {
const index = this.healthListeners.indexOf(listener);
if (index !== -1) {
this.healthListeners.splice(index, 1);
}
}
notifyHealthListeners(health) {
for (const listener of this.healthListeners) {
try {
listener(health);
}
catch (error) {
logger.warn(`Error in health listener: ${error}`);
}
}
}
getHealthStatus() {
return this.healthStatus;
}
getMemoryLeakDetector() {
return this.memoryLeakDetector;
}
getResourceTracker() {
return this.resourceTracker;
}
cleanup() {
this.stopHealthMonitoring();
this.stopPeriodicGC();
if (this.memoryLeakDetector) {
this.memoryLeakDetector.cleanup();
}
this.healthListeners = [];
logger.info('Process lifecycle manager cleaned up');
}
}