UNPKG

figma-restoration-mcp-vue-tools

Version:

Professional Figma Component Restoration Kit - MCP tools with snapDOM-powered high-quality screenshots, intelligent shadow detection, and advanced diff analysis for Vue component restoration. Features enhanced figma_compare with color-coded region analysi

777 lines (679 loc) 20.5 kB
/** * Health Monitor - System health checking and diagnostic capabilities * * This module provides comprehensive health monitoring including: * - Tool health checks and diagnostics * - System resource monitoring * - Performance metrics collection * - Alert and notification system */ import { EventEmitter } from 'events'; import os from 'os'; import { globalResourceManager } from './resource-manager.js'; import { globalConfig } from './config-manager.js'; import { globalRecoveryManager } from './error-recovery-strategies.js'; /** * Health Monitor for system diagnostics */ export class HealthMonitor extends EventEmitter { constructor(options = {}) { super(); this.options = { checkInterval: options.checkInterval || 60000, // 1 minute alertThresholds: { memory: options.memoryThreshold || 0.85, cpu: options.cpuThreshold || 0.9, disk: options.diskThreshold || 0.9, errorRate: options.errorRateThreshold || 0.1, responseTime: options.responseTimeThreshold || 5000 }, enableAlerts: options.enableAlerts !== false, enableMetrics: options.enableMetrics !== false, retentionPeriod: options.retentionPeriod || 86400000, // 24 hours ...options }; this.healthChecks = new Map(); this.metrics = new Map(); this.alerts = []; this.monitoringInterval = null; this.isRunning = false; this.registerDefaultHealthChecks(); } /** * Register default health checks */ registerDefaultHealthChecks() { // System resource checks this.registerHealthCheck('system_memory', { name: 'System Memory', description: 'Monitor system memory usage', check: this.checkSystemMemory.bind(this), interval: 30000, critical: true }); this.registerHealthCheck('system_cpu', { name: 'System CPU', description: 'Monitor CPU usage', check: this.checkSystemCPU.bind(this), interval: 30000, critical: true }); this.registerHealthCheck('disk_space', { name: 'Disk Space', description: 'Monitor disk space usage', check: this.checkDiskSpace.bind(this), interval: 60000, critical: true }); // Application-specific checks this.registerHealthCheck('resource_manager', { name: 'Resource Manager', description: 'Check resource manager health', check: this.checkResourceManager.bind(this), interval: 30000, critical: true }); this.registerHealthCheck('error_recovery', { name: 'Error Recovery', description: 'Check error recovery system', check: this.checkErrorRecovery.bind(this), interval: 60000, critical: false }); this.registerHealthCheck('configuration', { name: 'Configuration', description: 'Validate system configuration', check: this.checkConfiguration.bind(this), interval: 300000, // 5 minutes critical: false }); // Tool-specific checks this.registerHealthCheck('enhanced_tools', { name: 'Enhanced Tools', description: 'Check enhanced tool availability', check: this.checkEnhancedTools.bind(this), interval: 120000, // 2 minutes critical: false }); } /** * Register a health check * @param {string} id - Health check ID * @param {Object} healthCheck - Health check configuration */ registerHealthCheck(id, healthCheck) { this.healthChecks.set(id, { ...healthCheck, id, lastRun: null, lastResult: null, runCount: 0, errorCount: 0, enabled: healthCheck.enabled !== false }); this.emit('healthCheckRegistered', { id, healthCheck }); } /** * Unregister a health check * @param {string} id - Health check ID */ unregisterHealthCheck(id) { const healthCheck = this.healthChecks.get(id); if (healthCheck) { this.healthChecks.delete(id); this.emit('healthCheckUnregistered', { id, healthCheck }); } } /** * Start health monitoring */ start() { if (this.isRunning) return; this.isRunning = true; this.monitoringInterval = setInterval(() => { this.runHealthChecks(); }, this.options.checkInterval); // Run initial health check this.runHealthChecks(); this.emit('monitoringStarted'); } /** * Stop health monitoring */ stop() { if (!this.isRunning) return; this.isRunning = false; if (this.monitoringInterval) { clearInterval(this.monitoringInterval); this.monitoringInterval = null; } this.emit('monitoringStopped'); } /** * Run all health checks */ async runHealthChecks() { const results = new Map(); const now = Date.now(); for (const [id, healthCheck] of this.healthChecks) { if (!healthCheck.enabled) continue; // Check if it's time to run this health check if (healthCheck.lastRun && (now - healthCheck.lastRun) < healthCheck.interval) { continue; } try { const startTime = Date.now(); const result = await this.runHealthCheck(id); const duration = Date.now() - startTime; results.set(id, { ...result, duration }); // Record metrics if (this.options.enableMetrics) { this.recordMetric(`health_check_${id}`, { status: result.status, duration, timestamp: now }); } // Check for alerts if (this.options.enableAlerts && result.status !== 'healthy') { this.handleAlert(id, result, healthCheck); } } catch (error) { const errorResult = { status: 'error', message: `Health check failed: ${error.message}`, error: error.message, timestamp: now }; results.set(id, errorResult); healthCheck.errorCount++; this.emit('healthCheckError', { id, error, healthCheck }); } } if (results.size > 0) { this.emit('healthCheckResults', results); } // Cleanup old metrics this.cleanupOldMetrics(); } /** * Run a specific health check * @param {string} id - Health check ID * @returns {Promise<Object>} - Health check result */ async runHealthCheck(id) { const healthCheck = this.healthChecks.get(id); if (!healthCheck) { throw new Error(`Health check not found: ${id}`); } const now = Date.now(); healthCheck.lastRun = now; healthCheck.runCount++; const result = await healthCheck.check(); healthCheck.lastResult = result; this.emit('healthCheckCompleted', { id, result, healthCheck }); return { ...result, id, timestamp: now, runCount: healthCheck.runCount }; } /** * Handle health check alerts * @param {string} id - Health check ID * @param {Object} result - Health check result * @param {Object} healthCheck - Health check configuration */ handleAlert(id, result, healthCheck) { const alert = { id: `alert_${id}_${Date.now()}`, healthCheckId: id, healthCheckName: healthCheck.name, status: result.status, message: result.message, details: result.details, severity: this.getAlertSeverity(result.status, healthCheck.critical), timestamp: Date.now(), acknowledged: false }; this.alerts.push(alert); // Keep only recent alerts const cutoff = Date.now() - this.options.retentionPeriod; this.alerts = this.alerts.filter(a => a.timestamp > cutoff); this.emit('alert', alert); } /** * Get alert severity * @param {string} status - Health check status * @param {boolean} critical - Is critical check * @returns {string} - Alert severity */ getAlertSeverity(status, critical) { if (status === 'error' || status === 'critical') { return critical ? 'critical' : 'high'; } if (status === 'warning') { return critical ? 'high' : 'medium'; } return 'low'; } /** * Record a metric * @param {string} name - Metric name * @param {Object} value - Metric value */ recordMetric(name, value) { if (!this.metrics.has(name)) { this.metrics.set(name, []); } const metric = { ...value, timestamp: value.timestamp || Date.now() }; this.metrics.get(name).push(metric); this.emit('metricRecorded', { name, value: metric }); } /** * Get metrics * @param {string} name - Metric name (optional) * @param {number} since - Since timestamp (optional) * @returns {Object|Array} - Metrics */ getMetrics(name = null, since = null) { if (name) { const metrics = this.metrics.get(name) || []; return since ? metrics.filter(m => m.timestamp >= since) : metrics; } const result = {}; for (const [metricName, values] of this.metrics) { result[metricName] = since ? values.filter(m => m.timestamp >= since) : values; } return result; } /** * Cleanup old metrics */ cleanupOldMetrics() { const cutoff = Date.now() - this.options.retentionPeriod; for (const [name, values] of this.metrics) { const filtered = values.filter(v => v.timestamp > cutoff); this.metrics.set(name, filtered); } } /** * Get overall health status * @returns {Promise<Object>} - Overall health status */ async getOverallHealth() { const healthChecks = {}; let overallStatus = 'healthy'; const issues = []; const warnings = []; for (const [id, healthCheck] of this.healthChecks) { if (!healthCheck.enabled || !healthCheck.lastResult) continue; const result = healthCheck.lastResult; healthChecks[id] = { name: healthCheck.name, status: result.status, message: result.message, lastRun: healthCheck.lastRun, runCount: healthCheck.runCount, errorCount: healthCheck.errorCount }; // Determine overall status if (result.status === 'error' || result.status === 'critical') { if (healthCheck.critical) { overallStatus = 'critical'; } else if (overallStatus !== 'critical') { overallStatus = 'warning'; } issues.push(`${healthCheck.name}: ${result.message}`); } else if (result.status === 'warning') { if (overallStatus === 'healthy') { overallStatus = 'warning'; } warnings.push(`${healthCheck.name}: ${result.message}`); } } return { status: overallStatus, issues, warnings, healthChecks, alerts: this.getRecentAlerts(), metrics: this.getSystemMetrics(), timestamp: new Date().toISOString() }; } /** * Get recent alerts * @param {number} limit - Maximum number of alerts * @returns {Array} - Recent alerts */ getRecentAlerts(limit = 10) { return this.alerts .sort((a, b) => b.timestamp - a.timestamp) .slice(0, limit); } /** * Get system metrics summary * @returns {Object} - System metrics */ getSystemMetrics() { const memoryUsage = process.memoryUsage(); const cpuUsage = process.cpuUsage(); return { memory: { process: memoryUsage, system: { total: os.totalmem(), free: os.freemem(), usage: (os.totalmem() - os.freemem()) / os.totalmem() } }, cpu: { usage: cpuUsage, loadAverage: os.loadavg(), cores: os.cpus().length }, uptime: { process: process.uptime(), system: os.uptime() }, platform: { type: os.type(), platform: os.platform(), arch: os.arch(), release: os.release() } }; } // Default Health Check Implementations /** * Check system memory usage * @returns {Promise<Object>} - Memory check result */ async checkSystemMemory() { const totalMemory = os.totalmem(); const freeMemory = os.freemem(); const usedMemory = totalMemory - freeMemory; const usagePercent = usedMemory / totalMemory; const processMemory = process.memoryUsage(); const processUsagePercent = processMemory.heapUsed / processMemory.heapTotal; let status = 'healthy'; let message = `Memory usage: ${Math.round(usagePercent * 100)}%`; if (usagePercent > 0.95) { status = 'critical'; message = `Critical memory usage: ${Math.round(usagePercent * 100)}%`; } else if (usagePercent > this.options.alertThresholds.memory) { status = 'warning'; message = `High memory usage: ${Math.round(usagePercent * 100)}%`; } return { status, message, details: { system: { total: totalMemory, used: usedMemory, free: freeMemory, usagePercent }, process: { ...processMemory, usagePercent: processUsagePercent } } }; } /** * Check system CPU usage * @returns {Promise<Object>} - CPU check result */ async checkSystemCPU() { const loadAvg = os.loadavg(); const cpuCount = os.cpus().length; const load1min = loadAvg[0] / cpuCount; let status = 'healthy'; let message = `CPU load: ${Math.round(load1min * 100)}%`; if (load1min > 0.95) { status = 'critical'; message = `Critical CPU load: ${Math.round(load1min * 100)}%`; } else if (load1min > this.options.alertThresholds.cpu) { status = 'warning'; message = `High CPU load: ${Math.round(load1min * 100)}%`; } return { status, message, details: { loadAverage: loadAvg, cpuCount, load1min, cpus: os.cpus().map(cpu => ({ model: cpu.model, speed: cpu.speed })) } }; } /** * Check disk space usage * @returns {Promise<Object>} - Disk check result */ async checkDiskSpace() { try { const tempDir = os.tmpdir(); const diskUsage = await globalResourceManager.getDiskUsage(tempDir); let status = 'healthy'; let message = `Disk usage: ${Math.round(diskUsage.usagePercent * 100)}%`; if (diskUsage.usagePercent > 0.95) { status = 'critical'; message = `Critical disk usage: ${Math.round(diskUsage.usagePercent * 100)}%`; } else if (diskUsage.usagePercent > this.options.alertThresholds.disk) { status = 'warning'; message = `High disk usage: ${Math.round(diskUsage.usagePercent * 100)}%`; } return { status, message, details: { path: tempDir, ...diskUsage } }; } catch (error) { return { status: 'warning', message: `Could not check disk usage: ${error.message}`, details: { error: error.message } }; } } /** * Check resource manager health * @returns {Promise<Object>} - Resource manager check result */ async checkResourceManager() { try { const health = await globalResourceManager.getHealthStatus(); const stats = globalResourceManager.getStatistics(); let status = 'healthy'; let message = 'Resource manager is healthy'; if (health.status === 'critical') { status = 'critical'; message = `Resource manager critical: ${health.issues.join(', ')}`; } else if (health.status === 'warning') { status = 'warning'; message = `Resource manager warnings: ${health.warnings.join(', ')}`; } return { status, message, details: { health, statistics: stats } }; } catch (error) { return { status: 'error', message: `Resource manager check failed: ${error.message}`, details: { error: error.message } }; } } /** * Check error recovery system * @returns {Promise<Object>} - Error recovery check result */ async checkErrorRecovery() { try { const stats = globalRecoveryManager.getGlobalStatistics(); let status = 'healthy'; let message = 'Error recovery system is healthy'; const errorRate = stats.global.totalRecoveries > 0 ? (stats.global.totalRecoveries - stats.global.successfulRecoveries) / stats.global.totalRecoveries : 0; if (errorRate > this.options.alertThresholds.errorRate) { status = 'warning'; message = `High error recovery rate: ${Math.round(errorRate * 100)}%`; } return { status, message, details: { statistics: stats, errorRate } }; } catch (error) { return { status: 'error', message: `Error recovery check failed: ${error.message}`, details: { error: error.message } }; } } /** * Check system configuration * @returns {Promise<Object>} - Configuration check result */ async checkConfiguration() { try { const config = globalConfig.getAllConfig(); const issues = []; const warnings = []; // Check for missing required configurations if (!config.reliability) { issues.push('Missing reliability configuration'); } // Check timeout values if (config.timeouts) { for (const [tool, timeout] of Object.entries(config.timeouts)) { if (timeout < 1000) { warnings.push(`Very low timeout for ${tool}: ${timeout}ms`); } else if (timeout > 300000) { warnings.push(`Very high timeout for ${tool}: ${timeout}ms`); } } } let status = 'healthy'; let message = 'Configuration is valid'; if (issues.length > 0) { status = 'warning'; message = `Configuration issues: ${issues.join(', ')}`; } else if (warnings.length > 0) { status = 'warning'; message = `Configuration warnings: ${warnings.join(', ')}`; } return { status, message, details: { issues, warnings, configKeys: Object.keys(config) } }; } catch (error) { return { status: 'error', message: `Configuration check failed: ${error.message}`, details: { error: error.message } }; } } /** * Check enhanced tools availability * @returns {Promise<Object>} - Enhanced tools check result */ async checkEnhancedTools() { const tools = ['figma-compare', 'snapdom-screenshot', 'optimize-svg']; const results = {}; let healthyCount = 0; for (const tool of tools) { try { // Basic availability check results[tool] = { available: true, status: 'healthy' }; healthyCount++; } catch (error) { results[tool] = { available: false, status: 'error', error: error.message }; } } const successRate = healthyCount / tools.length; let status = 'healthy'; let message = `${healthyCount}/${tools.length} enhanced tools available`; if (successRate < 0.5) { status = 'critical'; message = `Most enhanced tools unavailable: ${healthyCount}/${tools.length}`; } else if (successRate < 1.0) { status = 'warning'; message = `Some enhanced tools unavailable: ${healthyCount}/${tools.length}`; } return { status, message, details: { tools: results, successRate, healthyCount, totalCount: tools.length } }; } } /** * Global health monitor instance */ export const globalHealthMonitor = new HealthMonitor(); /** * Health check decorator for methods * @param {Object} options - Health check options * @returns {Function} - Decorator function */ export function healthCheck(options = {}) { return function(target, propertyKey, descriptor) { const originalMethod = descriptor.value; const healthCheckId = options.id || `${target.constructor.name}_${propertyKey}`; // Register health check globalHealthMonitor.registerHealthCheck(healthCheckId, { name: options.name || `${target.constructor.name}.${propertyKey}`, description: options.description || `Health check for ${propertyKey}`, check: originalMethod.bind(target), interval: options.interval || 60000, critical: options.critical || false, enabled: options.enabled !== false }); return descriptor; }; }