UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

843 lines (842 loc) • 32.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.CrashReporter = exports.CrashSeverity = exports.CrashType = void 0; exports.createCrashReporter = createCrashReporter; exports.getGlobalCrashReporter = getGlobalCrashReporter; exports.setGlobalCrashReporter = setGlobalCrashReporter; const events_1 = require("events"); const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const os = __importStar(require("os")); var CrashType; (function (CrashType) { CrashType["UNCAUGHT_EXCEPTION"] = "uncaught_exception"; CrashType["UNHANDLED_REJECTION"] = "unhandled_rejection"; CrashType["ASSERTION_ERROR"] = "assertion_error"; CrashType["MEMORY_ERROR"] = "memory_error"; CrashType["TIMEOUT_ERROR"] = "timeout_error"; CrashType["FILE_SYSTEM_ERROR"] = "file_system_error"; CrashType["NETWORK_ERROR"] = "network_error"; CrashType["DEPENDENCY_ERROR"] = "dependency_error"; CrashType["CONFIGURATION_ERROR"] = "configuration_error"; CrashType["PERMISSION_ERROR"] = "permission_error"; CrashType["OPERATION_FAILURE"] = "operation_failure"; CrashType["PLUGIN_ERROR"] = "plugin_error"; CrashType["BUILD_ERROR"] = "build_error"; CrashType["CUSTOM"] = "custom"; })(CrashType || (exports.CrashType = CrashType = {})); var CrashSeverity; (function (CrashSeverity) { CrashSeverity["LOW"] = "low"; CrashSeverity["MEDIUM"] = "medium"; CrashSeverity["HIGH"] = "high"; CrashSeverity["CRITICAL"] = "critical"; })(CrashSeverity || (exports.CrashSeverity = CrashSeverity = {})); class CrashReporter extends events_1.EventEmitter { constructor(workspaceRoot, options = {}) { super(); this.workspaceRoot = workspaceRoot; this.options = options; this.issuePatterns = new Map(); this.recentLogs = []; this.recentErrors = []; this.hookInstalled = false; this.defaultOptions = { enableRemoteReporting: false, enableLocalStorage: true, maxReportsStored: 100, enableAutoRecovery: true, enableUserNotification: true, anonymizeData: true, reportingLevel: CrashSeverity.MEDIUM, retryAttempts: 3 }; this.sessionId = this.generateSessionId(); this.startTime = new Date(); this.crashReportsPath = path.join(workspaceRoot, '.re-shell', 'crash-reports'); this.options = { ...this.defaultOptions, ...options }; this.ensureReportsDirectory(); this.loadKnownIssuePatterns(); this.installGlobalHooks(); } ensureReportsDirectory() { try { fs.ensureDirSync(this.crashReportsPath); } catch (error) { console.warn('Failed to create crash reports directory:', error); } } loadKnownIssuePatterns() { // Memory issues this.registerIssuePattern({ id: 'memory_heap_out_of_memory', name: 'Memory Heap Exceeded', description: 'JavaScript heap out of memory error', pattern: /JavaScript heap out of memory/i, severity: CrashSeverity.HIGH, category: 'Memory', knownCauses: [ 'Large file processing', 'Memory leaks in plugins', 'Insufficient Node.js heap size', 'Processing too many files simultaneously' ], suggestedFixes: [ 'Increase Node.js heap size with --max-old-space-size', 'Process files in smaller batches', 'Check for memory leaks in custom plugins', 'Use streaming for large file operations' ], documentationLink: 'https://nodejs.org/api/cli.html#cli_max_old_space_size_size_in_megabytes' }); // Permission issues this.registerIssuePattern({ id: 'permission_denied_eacces', name: 'Permission Denied', description: 'File system permission error', pattern: /EACCES|permission denied/i, severity: CrashSeverity.MEDIUM, category: 'Permissions', knownCauses: [ 'Insufficient file system permissions', 'Files owned by different user', 'Read-only file system', 'Protected system directories' ], suggestedFixes: [ 'Run with appropriate user permissions', 'Change file ownership: sudo chown -R $USER:$USER .', 'Check directory permissions', 'Use sudo if accessing system directories' ] }); // Network issues this.registerIssuePattern({ id: 'network_connection_refused', name: 'Network Connection Refused', description: 'Unable to connect to remote service', pattern: /ECONNREFUSED|connection refused/i, severity: CrashSeverity.MEDIUM, category: 'Network', knownCauses: [ 'Service is not running', 'Incorrect port or URL', 'Firewall blocking connection', 'Network connectivity issues' ], suggestedFixes: [ 'Verify service is running and accessible', 'Check network connectivity', 'Verify firewall settings', 'Check proxy configuration' ] }); // Dependency issues this.registerIssuePattern({ id: 'module_not_found', name: 'Module Not Found', description: 'Required module cannot be found', pattern: /Cannot find module|Module not found/i, severity: CrashSeverity.HIGH, category: 'Dependencies', knownCauses: [ 'Missing dependencies', 'Corrupted node_modules', 'Version incompatibility', 'Path resolution issues' ], suggestedFixes: [ 'Run npm install or yarn install', 'Delete node_modules and reinstall', 'Check package.json for correct dependencies', 'Clear package manager cache' ], autoRecovery: async () => { try { const { execSync } = require('child_process'); execSync('npm install', { stdio: 'inherit' }); return true; } catch { return false; } } }); // Configuration issues this.registerIssuePattern({ id: 'invalid_configuration', name: 'Invalid Configuration', description: 'Configuration file is invalid or corrupted', pattern: (error, context) => { return error.message.includes('configuration') && (error.message.includes('invalid') || error.message.includes('parse')); }, severity: CrashSeverity.MEDIUM, category: 'Configuration', knownCauses: [ 'Syntax error in config file', 'Invalid configuration values', 'Missing required configuration', 'Corrupted configuration file' ], suggestedFixes: [ 'Validate configuration syntax', 'Reset to default configuration', 'Check for required fields', 'Restore from backup' ] }); // File system issues this.registerIssuePattern({ id: 'file_system_full', name: 'File System Full', description: 'No space left on device', pattern: /ENOSPC|no space left on device/i, severity: CrashSeverity.CRITICAL, category: 'FileSystem', knownCauses: [ 'Disk space exhausted', 'Inode exhaustion', 'Temporary files accumulation', 'Large log files' ], suggestedFixes: [ 'Free up disk space', 'Clean temporary files', 'Remove old log files', 'Move files to different location' ] }); // Plugin issues this.registerIssuePattern({ id: 'plugin_error', name: 'Plugin Error', description: 'Error in plugin execution', pattern: (error, context) => { return context.pluginsLoaded.length > 0 && (error.stack?.includes('plugin') || error.message.includes('plugin')); }, severity: CrashSeverity.MEDIUM, category: 'Plugins', knownCauses: [ 'Plugin compatibility issues', 'Plugin dependency conflicts', 'Malformed plugin code', 'Plugin API misuse' ], suggestedFixes: [ 'Update plugins to latest versions', 'Disable problematic plugins', 'Check plugin compatibility', 'Report issue to plugin author' ] }); } installGlobalHooks() { if (this.hookInstalled) return; // Uncaught exceptions process.on('uncaughtException', (error) => { this.reportCrash(CrashType.UNCAUGHT_EXCEPTION, error, CrashSeverity.CRITICAL); }); // Unhandled promise rejections process.on('unhandledRejection', (reason, promise) => { const error = reason instanceof Error ? reason : new Error(String(reason)); this.reportCrash(CrashType.UNHANDLED_REJECTION, error, CrashSeverity.HIGH); }); // Exit events process.on('exit', () => { this.handleGracefulShutdown(); }); process.on('SIGINT', () => { this.handleGracefulShutdown(); process.exit(0); }); process.on('SIGTERM', () => { this.handleGracefulShutdown(); process.exit(0); }); this.hookInstalled = true; } registerIssuePattern(pattern) { this.issuePatterns.set(pattern.id, pattern); this.emit('pattern:registered', pattern); } async reportCrash(type, error, severity = CrashSeverity.MEDIUM, context) { const reportId = this.generateReportId(); try { const crashReport = { id: reportId, timestamp: new Date(), type, severity, error: { name: error.name, message: error.message, stack: error.stack, code: error.code, signal: error.signal }, context: await this.gatherContext(context), systemInfo: await this.gatherSystemInfo(), diagnostics: await this.gatherDiagnostics(), fingerprint: this.generateFingerprint(error), recovered: false, reportedToRemote: false }; // Store locally if enabled if (this.options.enableLocalStorage) { await this.storeReportLocally(crashReport); } // Analyze the crash const analysis = await this.analyzeCrash(crashReport); // Attempt auto-recovery if enabled if (this.options.enableAutoRecovery && analysis.recoverable) { const recovered = await this.attemptAutoRecovery(analysis); crashReport.recovered = recovered; if (recovered) { this.emit('crash:recovered', { report: crashReport, analysis }); } } // Send to remote if enabled and severity is high enough if (this.options.enableRemoteReporting && this.shouldReportRemotely(severity)) { await this.sendToRemote(crashReport); } // Notify user if enabled if (this.options.enableUserNotification) { this.notifyUser(crashReport, analysis); } this.emit('crash:reported', { report: crashReport, analysis }); return reportId; } catch (reportingError) { console.error('Failed to report crash:', reportingError); this.emit('crash:reporting_failed', { error: reportingError, originalError: error }); return reportId; } } async gatherContext(partial) { const memoryUsage = process.memoryUsage(); const uptime = Date.now() - this.startTime.getTime(); // Filter environment variables for privacy const env = this.options.anonymizeData ? this.anonymizeEnvironment(process.env) : { ...process.env }; return { command: process.argv[2] || 'unknown', args: process.argv.slice(3), workingDirectory: process.cwd(), sessionId: this.sessionId, uptime, memoryUsage, environmentVariables: env, recentLogs: [...this.recentLogs], activeOperations: [], // TODO: Get from operation manager pluginsLoaded: [], // TODO: Get from plugin manager configurationState: {}, // TODO: Get from config manager ...partial }; } async gatherSystemInfo() { const cpus = os.cpus(); return { platform: os.platform(), arch: os.arch(), nodeVersion: process.version, cliVersion: await this.getCliVersion(), homeDirectory: this.options.anonymizeData ? '<anonymized>' : os.homedir(), totalMemory: os.totalmem(), freeMemory: os.freemem(), cpuModel: cpus[0]?.model || 'Unknown', cpuCores: cpus.length, loadAverage: os.loadavg(), uptime: os.uptime(), diskSpace: await this.getDiskSpace() }; } async gatherDiagnostics() { return { healthChecks: [], // TODO: Get from health diagnostics networkConnectivity: await this.testNetworkConnectivity(), fileSystemPermissions: await this.testFileSystemPermissions(), dependencyVersions: await this.getDependencyVersions(), configurationValid: await this.validateConfiguration(), recentErrors: [...this.recentErrors], performanceMetrics: { averageResponseTime: 0, // TODO: Calculate from metrics memoryTrend: 'stable', // TODO: Analyze memory usage trend cpuUsage: 0 // TODO: Get current CPU usage } }; } async analyzeCrash(report) { const matchedPatterns = []; // Match against known patterns for (const pattern of this.issuePatterns.values()) { let matches = false; let confidence = 0; if (pattern.pattern instanceof RegExp) { if (pattern.pattern.test(report.error.message) || (report.error.stack && pattern.pattern.test(report.error.stack))) { matches = true; confidence = 0.8; } } else if (typeof pattern.pattern === 'function') { matches = pattern.pattern(new Error(report.error.message), report.context); confidence = matches ? 0.7 : 0; } if (matches) { matchedPatterns.push({ pattern, confidence, extractedData: this.extractPatternData(pattern, report) }); } } // Sort by confidence matchedPatterns.sort((a, b) => b.confidence - a.confidence); // Determine root cause and recommendations const bestMatch = matchedPatterns[0]; const rootCause = bestMatch ? bestMatch.pattern.description : 'Unknown error'; const recommendations = []; const quickFixes = []; if (bestMatch) { recommendations.push(...bestMatch.pattern.suggestedFixes); if (bestMatch.pattern.autoRecovery) { quickFixes.push({ description: `Auto-fix for ${bestMatch.pattern.name}`, action: bestMatch.pattern.autoRecovery, safe: true }); } } // Find similar issues const similarIssues = await this.findSimilarIssues(report); return { reportId: report.id, matchedPatterns, rootCause, recommendations, quickFixes, similarIssues, severity: bestMatch ? bestMatch.pattern.severity : report.severity, recoverable: quickFixes.length > 0 || this.isRecoverableError(report) }; } extractPatternData(pattern, report) { // Extract relevant data based on pattern type switch (pattern.category) { case 'Memory': return { memoryUsage: report.context.memoryUsage, totalMemory: report.systemInfo.totalMemory, freeMemory: report.systemInfo.freeMemory }; case 'Dependencies': return { dependencies: report.diagnostics.dependencyVersions, nodeVersion: report.systemInfo.nodeVersion }; default: return null; } } async attemptAutoRecovery(analysis) { for (const quickFix of analysis.quickFixes) { if (quickFix.safe) { try { this.emit('recovery:attempt', quickFix); const success = await quickFix.action(); if (success) { this.emit('recovery:success', quickFix); return true; } } catch (error) { this.emit('recovery:failed', { quickFix, error }); } } } return false; } isRecoverableError(report) { // Check if error type is generally recoverable const recoverableTypes = [ CrashType.NETWORK_ERROR, CrashType.DEPENDENCY_ERROR, CrashType.CONFIGURATION_ERROR, CrashType.TIMEOUT_ERROR ]; return recoverableTypes.includes(report.type); } async findSimilarIssues(report) { try { const reportFiles = await fs.readdir(this.crashReportsPath); const similarReports = []; for (const file of reportFiles.slice(0, 50)) { // Limit search if (file.endsWith('.json') && !file.includes(report.id)) { try { const otherReport = await fs.readJson(path.join(this.crashReportsPath, file)); if (this.isSimilarError(report, otherReport)) { similarReports.push(otherReport.id); } } catch { // Skip corrupted reports } } } return similarReports.slice(0, 5); // Return top 5 similar issues } catch { return []; } } isSimilarError(report1, report2) { // Check if fingerprints match (exact same error) if (report1.fingerprint === report2.fingerprint) { return true; } // Check if error names and types match if (report1.error.name === report2.error.name && report1.type === report2.type) { return true; } // Check if error messages are similar (basic similarity) const similarity = this.calculateStringSimilarity(report1.error.message, report2.error.message); return similarity > 0.7; } calculateStringSimilarity(str1, str2) { const words1 = str1.toLowerCase().split(/\s+/); const words2 = str2.toLowerCase().split(/\s+/); const commonWords = words1.filter(word => words2.includes(word)); const totalWords = new Set([...words1, ...words2]).size; return commonWords.length / totalWords; } async storeReportLocally(report) { try { const reportPath = path.join(this.crashReportsPath, `${report.id}.json`); await fs.writeJson(reportPath, report, { spaces: 2 }); // Cleanup old reports if limit exceeded await this.cleanupOldReports(); } catch (error) { console.warn('Failed to store crash report locally:', error); } } async cleanupOldReports() { try { const reportFiles = await fs.readdir(this.crashReportsPath); const jsonFiles = reportFiles.filter(f => f.endsWith('.json')); if (jsonFiles.length > this.options.maxReportsStored) { // Sort by modification time and remove oldest const fileStats = await Promise.all(jsonFiles.map(async (file) => ({ file, stats: await fs.stat(path.join(this.crashReportsPath, file)) }))); fileStats.sort((a, b) => a.stats.mtime.getTime() - b.stats.mtime.getTime()); const toDelete = fileStats.slice(0, jsonFiles.length - this.options.maxReportsStored); for (const { file } of toDelete) { await fs.unlink(path.join(this.crashReportsPath, file)); } } } catch (error) { console.warn('Failed to cleanup old crash reports:', error); } } shouldReportRemotely(severity) { const severityOrder = [CrashSeverity.LOW, CrashSeverity.MEDIUM, CrashSeverity.HIGH, CrashSeverity.CRITICAL]; const currentIndex = severityOrder.indexOf(severity); const thresholdIndex = severityOrder.indexOf(this.options.reportingLevel); return currentIndex >= thresholdIndex; } async sendToRemote(report) { if (!this.options.remoteEndpoint) return; try { const anonymizedReport = this.options.anonymizeData ? this.anonymizeReport(report) : report; const response = await fetch(this.options.remoteEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(this.options.apiKey && { 'Authorization': `Bearer ${this.options.apiKey}` }) }, body: JSON.stringify(anonymizedReport) }); if (response.ok) { report.reportedToRemote = true; this.emit('crash:reported_remote', report); } else { throw new Error(`Remote reporting failed: ${response.status}`); } } catch (error) { this.emit('crash:remote_failed', { report, error }); } } anonymizeReport(report) { const anonymized = { ...report }; // Anonymize paths anonymized.context.workingDirectory = this.anonymizePath(report.context.workingDirectory); anonymized.systemInfo.homeDirectory = '<anonymized>'; // Remove potentially sensitive environment variables anonymized.context.environmentVariables = this.anonymizeEnvironment(report.context.environmentVariables); // Anonymize error stack traces if (anonymized.error.stack) { anonymized.error.stack = this.anonymizeStackTrace(anonymized.error.stack); } return anonymized; } anonymizePath(filePath) { const homeDir = os.homedir(); return filePath.replace(homeDir, '~').replace(/\/Users\/[^\/]+/, '/Users/<user>'); } anonymizeEnvironment(env) { const sensitiveKeys = ['PATH', 'HOME', 'USER', 'USERNAME', 'USERPROFILE']; const result = {}; for (const [key, value] of Object.entries(env)) { if (sensitiveKeys.some(k => key.includes(k))) { result[key] = '<anonymized>'; } else if (key.startsWith('RE_SHELL_')) { result[key] = value; // Keep Re-Shell specific vars } } return result; } anonymizeStackTrace(stack) { const homeDir = os.homedir(); return stack .replace(new RegExp(homeDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), '~') .replace(/\/Users\/[^\/]+/g, '/Users/<user>'); } notifyUser(report, analysis) { console.error(`\nšŸ”„ Re-Shell CLI Crash Detected (${report.id})`); console.error(`Type: ${report.type}`); console.error(`Severity: ${report.severity}`); console.error(`Error: ${report.error.message}\n`); if (analysis.rootCause) { console.error(`Root Cause: ${analysis.rootCause}\n`); } if (analysis.recommendations.length > 0) { console.error('Suggested Fixes:'); analysis.recommendations.slice(0, 3).forEach((rec, i) => { console.error(` ${i + 1}. ${rec}`); }); console.error(''); } if (report.recovered) { console.error('āœ… Auto-recovery was successful'); } else if (analysis.quickFixes.length > 0) { console.error('šŸ”§ Auto-recovery options are available'); } console.error(`šŸ“ Full report saved: ${report.id}`); console.error(''); } handleGracefulShutdown() { // Perform final cleanup and reporting this.emit('shutdown'); } // Utility methods generateReportId() { return `crash_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`; } generateSessionId() { return `session_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`; } generateFingerprint(error) { const crypto = require('crypto'); const data = `${error.name}:${error.message}:${error.stack?.split('\n')[0] || ''}`; return crypto.createHash('sha256').update(data).digest('hex').substr(0, 16); } async getCliVersion() { try { const packagePath = path.join(__dirname, '..', '..', 'package.json'); const pkg = await fs.readJson(packagePath); return pkg.version || 'unknown'; } catch { return 'unknown'; } } async getDiskSpace() { try { const stats = await fs.stat(this.workspaceRoot); return { total: 0, // TODO: Implement platform-specific disk space check free: 0, used: 0 }; } catch { return undefined; } } async testNetworkConnectivity() { try { const https = require('https'); return new Promise((resolve) => { const req = https.get('https://registry.npmjs.org/', (res) => { resolve(res.statusCode === 200); }); req.on('error', () => resolve(false)); req.setTimeout(5000, () => { req.destroy(); resolve(false); }); }); } catch { return false; } } async testFileSystemPermissions() { try { const testFile = path.join(os.tmpdir(), 're-shell-permission-test'); await fs.writeFile(testFile, 'test'); await fs.readFile(testFile); await fs.unlink(testFile); return true; } catch { return false; } } async getDependencyVersions() { try { const packagePath = path.join(this.workspaceRoot, 'package.json'); if (await fs.pathExists(packagePath)) { const pkg = await fs.readJson(packagePath); return { ...pkg.dependencies, ...pkg.devDependencies }; } } catch { // Ignore errors } return {}; } async validateConfiguration() { try { const configPath = path.join(this.workspaceRoot, '.re-shell', 'config.yaml'); return await fs.pathExists(configPath); } catch { return false; } } // Public methods for logging and error tracking addLogEntry(message) { this.recentLogs.push(`${new Date().toISOString()}: ${message}`); // Keep only recent logs if (this.recentLogs.length > 100) { this.recentLogs = this.recentLogs.slice(-50); } } recordError(error, context) { this.recentErrors.push({ timestamp: new Date(), error, context }); // Keep only recent errors if (this.recentErrors.length > 50) { this.recentErrors = this.recentErrors.slice(-25); } } // Query methods async getReports(limit = 10) { try { const reportFiles = await fs.readdir(this.crashReportsPath); const jsonFiles = reportFiles.filter(f => f.endsWith('.json')); const reports = []; for (const file of jsonFiles.slice(0, limit)) { try { const report = await fs.readJson(path.join(this.crashReportsPath, file)); reports.push(report); } catch { // Skip corrupted reports } } return reports.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); } catch { return []; } } async getReport(id) { try { const reportPath = path.join(this.crashReportsPath, `${id}.json`); if (await fs.pathExists(reportPath)) { return await fs.readJson(reportPath); } } catch { // Ignore errors } return null; } getSessionInfo() { return { id: this.sessionId, startTime: this.startTime, uptime: Date.now() - this.startTime.getTime() }; } } exports.CrashReporter = CrashReporter; // Global crash reporter let globalCrashReporter = null; function createCrashReporter(workspaceRoot, options) { return new CrashReporter(workspaceRoot, options); } function getGlobalCrashReporter() { if (!globalCrashReporter) { globalCrashReporter = new CrashReporter(process.cwd()); } return globalCrashReporter; } function setGlobalCrashReporter(reporter) { globalCrashReporter = reporter; }