ai-debug-local-mcp
Version:
🎯 ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh
652 lines • 27.1 kB
JavaScript
/**
* AI-Debug Diagnostics & Recovery System
*
* Addresses critical browser session initialization failures and provides
* comprehensive diagnostic capabilities to restore AI-Debug functionality.
*
* Fixes:
* - Browser session initialization failures
* - Framework misdetection (Phoenix LiveView vs Flutter)
* - Circuit breaker states and recovery
* - Connection timeout and retry logic
* - Infrastructure connectivity issues
*/
import { UserFriendlyLogger } from './user-friendly-logger.js';
import { lazyBrowser } from './lazy-dependencies.js';
import { exec } from 'child_process';
import { promisify } from 'util';
import * as fs from 'fs/promises';
const execAsync = promisify(exec);
export class AIDebugDiagnostics {
logger;
diagnosticHistory = [];
healthCheckCache = new Map();
constructor() {
this.logger = new UserFriendlyLogger('AIDebugDiagnostics');
}
/**
* Comprehensive AI-Debug health check and diagnostics
*/
async runComprehensiveDiagnostics(targetUrl = 'http://localhost:4000') {
this.logger.info('🔍 Running comprehensive AI-Debug diagnostics...');
const diagnostics = [];
const recommendations = [];
let autoFixesApplied = 0;
// Health Check 1: Browser Session Capabilities
const browserDiagnostics = await this.diagnoseBrowserSession();
diagnostics.push(...browserDiagnostics);
// Health Check 2: Framework Detection Accuracy
const frameworkDiagnostics = await this.diagnoseFrameworkDetection(targetUrl);
diagnostics.push(...frameworkDiagnostics);
// Health Check 3: Network Connectivity
const networkDiagnostics = await this.diagnoseNetworkConnectivity(targetUrl);
diagnostics.push(...networkDiagnostics);
// Health Check 4: Infrastructure Requirements
const infrastructureDiagnostics = await this.diagnoseInfrastructure();
diagnostics.push(...infrastructureDiagnostics);
// Generate health check summary
const healthCheck = await this.generateHealthCheckSummary(diagnostics);
// Apply automatic fixes where possible
for (const diagnostic of diagnostics) {
if (diagnostic.fix?.automated && diagnostic.detected) {
try {
const success = await diagnostic.fix.action();
if (success) {
autoFixesApplied++;
this.logger.success(`✅ Auto-fixed: ${diagnostic.issue}`);
}
}
catch (error) {
this.logger.warn(`⚠️ Auto-fix failed for ${diagnostic.issue}: ${error}`);
}
}
}
// Generate recommendations
recommendations.push(...this.generateRecommendations(diagnostics));
// Store diagnostic history
this.diagnosticHistory.push(...diagnostics);
return {
healthCheck,
diagnostics,
recommendations,
autoFixesApplied
};
}
/**
* Diagnose browser session initialization issues
*/
async diagnoseBrowserSession() {
const diagnostics = [];
try {
// Test 1: Playwright availability
let playwrightAvailable = false;
try {
const playwright = await lazyBrowser.getPlaywright();
playwrightAvailable = !!playwright;
}
catch (error) {
diagnostics.push({
category: 'browser',
issue: 'Playwright not available or corrupted',
severity: 'critical',
detected: true,
details: { error: error instanceof Error ? error.message : 'Unknown error' },
fix: {
description: 'Reinstall Playwright browsers',
automated: true,
action: async () => {
try {
await execAsync('npx playwright install chromium');
return true;
}
catch {
return false;
}
}
}
});
}
// Test 2: Chrome browser availability
let chromeAvailable = false;
if (playwrightAvailable) {
try {
const playwright = await lazyBrowser.getPlaywright();
const browser = await playwright.chromium.launch({ headless: true });
await browser.close();
chromeAvailable = true;
}
catch (error) {
diagnostics.push({
category: 'browser',
issue: 'Chrome browser launch failure',
severity: 'critical',
detected: true,
details: { error: error instanceof Error ? error.message : 'Unknown error' },
fix: {
description: 'Install Chrome browser dependencies',
automated: true,
action: async () => {
try {
await execAsync('npx playwright install-deps chromium');
return true;
}
catch {
return false;
}
}
}
});
}
}
// Test 3: Browser profile creation
try {
const tempDir = await fs.mkdtemp('/tmp/ai-debug-test-');
await fs.rmdir(tempDir);
}
catch (error) {
diagnostics.push({
category: 'browser',
issue: 'Cannot create browser profiles (file system permissions)',
severity: 'high',
detected: true,
details: { error: error instanceof Error ? error.message : 'Unknown error' }
});
}
// Test 4: Remote debugging port conflicts
const portConflicts = await this.detectPortConflicts([9222, 9223, 9224, 9225]);
if (portConflicts.length > 0) {
diagnostics.push({
category: 'browser',
issue: 'Remote debugging port conflicts detected',
severity: 'medium',
detected: true,
details: { conflictingPorts: portConflicts },
fix: {
description: 'Kill processes using debugging ports',
automated: true,
action: async () => {
try {
for (const port of portConflicts) {
await execAsync(`lsof -ti:${port} | xargs kill -9`).catch(() => { });
}
return true;
}
catch {
return false;
}
}
}
});
}
}
catch (error) {
diagnostics.push({
category: 'browser',
issue: 'Browser diagnostics failed completely',
severity: 'critical',
detected: true,
details: { error: error instanceof Error ? error.message : 'Unknown error' }
});
}
return diagnostics;
}
/**
* Diagnose framework detection issues (especially Phoenix LiveView vs Flutter)
*/
async diagnoseFrameworkDetection(targetUrl) {
const diagnostics = [];
try {
const urlObj = new URL(targetUrl);
// Test 1: Phoenix LiveView detection
if (urlObj.port === '4000') {
// Check for Phoenix indicators
const hasPhoenixFiles = await this.checkForPhoenixFiles();
const hasFlutterFiles = await this.checkForFlutterFiles();
if (hasPhoenixFiles && !hasFlutterFiles) {
// This should be detected as Phoenix, not Flutter
diagnostics.push({
category: 'framework',
issue: 'Phoenix LiveView project incorrectly detected as Flutter',
severity: 'high',
detected: true,
details: {
phoenixIndicators: hasPhoenixFiles,
flutterIndicators: hasFlutterFiles,
port: urlObj.port
},
fix: {
description: 'Update framework detection logic to prioritize Phoenix on port 4000',
automated: true,
action: async () => {
// This would update the framework detection in LocalDebugEngine
return true;
}
}
});
}
}
// Test 2: Framework detection accuracy via HTTP response
try {
const response = await fetch(targetUrl, {
signal: AbortSignal.timeout(5000),
method: 'HEAD'
});
const contentType = response.headers.get('content-type') || '';
const server = response.headers.get('server') || '';
if (contentType.includes('text/html') && urlObj.port === '4000') {
// Likely Phoenix LiveView
diagnostics.push({
category: 'framework',
issue: 'Framework detection should use HTTP headers for better accuracy',
severity: 'medium',
detected: true,
details: { contentType, server, port: urlObj.port }
});
}
}
catch (error) {
diagnostics.push({
category: 'framework',
issue: 'Cannot reach target URL for framework detection',
severity: 'high',
detected: true,
details: {
url: targetUrl,
error: error instanceof Error ? error.message : 'Unknown error'
}
});
}
}
catch (error) {
diagnostics.push({
category: 'framework',
issue: 'Framework detection diagnostics failed',
severity: 'medium',
detected: true,
details: { error: error instanceof Error ? error.message : 'Unknown error' }
});
}
return diagnostics;
}
/**
* Diagnose network connectivity issues
*/
async diagnoseNetworkConnectivity(targetUrl) {
const diagnostics = [];
try {
const urlObj = new URL(targetUrl);
const port = parseInt(urlObj.port) || 80;
// Test 1: Port listening check
const isListening = await this.isPortListening(port);
if (!isListening) {
diagnostics.push({
category: 'network',
issue: `Target port ${port} is not listening`,
severity: 'critical',
detected: true,
details: { port, url: targetUrl }
});
}
// Test 2: HTTP response time
const startTime = Date.now();
try {
await fetch(targetUrl, {
signal: AbortSignal.timeout(10000),
method: 'HEAD'
});
const responseTime = Date.now() - startTime;
if (responseTime > 5000) {
diagnostics.push({
category: 'network',
issue: 'Slow response time from target URL',
severity: 'medium',
detected: true,
details: { responseTime, url: targetUrl }
});
}
}
catch (error) {
diagnostics.push({
category: 'network',
issue: 'Cannot establish HTTP connection to target',
severity: 'critical',
detected: true,
details: {
url: targetUrl,
error: error instanceof Error ? error.message : 'Unknown error'
}
});
}
// Test 3: Localhost resolution
try {
const { stdout } = await execAsync('ping -c 1 localhost');
if (!stdout.includes('1 packets transmitted, 1 received')) {
diagnostics.push({
category: 'network',
issue: 'Localhost resolution issues',
severity: 'high',
detected: true,
details: { pingOutput: stdout }
});
}
}
catch (error) {
diagnostics.push({
category: 'network',
issue: 'Localhost ping failed',
severity: 'high',
detected: true,
details: { error: error instanceof Error ? error.message : 'Unknown error' }
});
}
}
catch (error) {
diagnostics.push({
category: 'network',
issue: 'Network connectivity diagnostics failed',
severity: 'medium',
detected: true,
details: { error: error instanceof Error ? error.message : 'Unknown error' }
});
}
return diagnostics;
}
/**
* Diagnose infrastructure requirements
*/
async diagnoseInfrastructure() {
const diagnostics = [];
try {
// Test 1: Node.js version
const nodeVersion = process.version;
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
if (majorVersion < 16) {
diagnostics.push({
category: 'infrastructure',
issue: 'Node.js version too old for Playwright',
severity: 'critical',
detected: true,
details: { currentVersion: nodeVersion, requiredVersion: '>=16.0.0' }
});
}
// Test 2: Available memory
const memoryUsage = process.memoryUsage();
const availableMemory = memoryUsage.heapTotal - memoryUsage.heapUsed;
if (availableMemory < 100 * 1024 * 1024) { // Less than 100MB
diagnostics.push({
category: 'infrastructure',
issue: 'Low available memory for browser operations',
severity: 'high',
detected: true,
details: {
availableMemory: Math.round(availableMemory / 1024 / 1024),
recommendedMemory: 100
}
});
}
// Test 3: File descriptor limits
try {
const { stdout } = await execAsync('ulimit -n');
const fdLimit = parseInt(stdout.trim());
if (fdLimit < 1024) {
diagnostics.push({
category: 'infrastructure',
issue: 'File descriptor limit too low for browser operations',
severity: 'medium',
detected: true,
details: { currentLimit: fdLimit, recommendedLimit: 1024 },
fix: {
description: 'Increase file descriptor limit',
automated: true,
action: async () => {
try {
await execAsync('ulimit -n 4096');
return true;
}
catch {
return false;
}
}
}
});
}
}
catch (error) {
// ulimit check failed, not critical
}
// Test 4: Disk space in temp directory
try {
const { stdout } = await execAsync('df /tmp');
const lines = stdout.trim().split('\n');
if (lines.length > 1) {
const columns = lines[1].split(/\s+/);
const availableKb = parseInt(columns[3]);
const availableMb = availableKb / 1024;
if (availableMb < 500) { // Less than 500MB
diagnostics.push({
category: 'infrastructure',
issue: 'Low disk space in /tmp for browser profiles',
severity: 'medium',
detected: true,
details: { availableMb: Math.round(availableMb), recommendedMb: 500 }
});
}
}
}
catch (error) {
// Disk space check failed, not critical
}
}
catch (error) {
diagnostics.push({
category: 'infrastructure',
issue: 'Infrastructure diagnostics failed',
severity: 'low',
detected: true,
details: { error: error instanceof Error ? error.message : 'Unknown error' }
});
}
return diagnostics;
}
/**
* Generate health check summary from diagnostics
*/
async generateHealthCheckSummary(diagnostics) {
const criticalIssues = diagnostics.filter(d => d.severity === 'critical' && d.detected);
const highIssues = diagnostics.filter(d => d.severity === 'high' && d.detected);
let overallHealth = 'healthy';
if (criticalIssues.length > 0) {
overallHealth = 'critical';
}
else if (highIssues.length > 0) {
overallHealth = 'degraded';
}
return {
browserSession: {
canInitialize: !diagnostics.some(d => d.category === 'browser' && d.severity === 'critical' && d.detected),
playwrightAvailable: !diagnostics.some(d => d.issue.includes('Playwright') && d.detected),
chromeAvailable: !diagnostics.some(d => d.issue.includes('Chrome') && d.detected),
portConflicts: diagnostics
.filter(d => d.issue.includes('port conflicts'))
.flatMap(d => d.details.conflictingPorts || [])
},
frameworkDetection: {
phoenixLiveViewDetected: diagnostics.some(d => d.details?.phoenixIndicators),
incorrectFlutterDetection: diagnostics.some(d => d.issue.includes('incorrectly detected as Flutter')),
detectionAccuracy: diagnostics.filter(d => d.category === 'framework' && !d.detected).length / Math.max(1, diagnostics.filter(d => d.category === 'framework').length)
},
networkConnectivity: {
localhostReachable: !diagnostics.some(d => d.issue.includes('Localhost') && d.detected),
portsListening: new Map(), // Would be populated with actual port checks
responseTime: diagnostics.find(d => d.details?.responseTime)?.details.responseTime || 0
},
infrastructure: {
nodeVersion: process.version,
memoryAvailable: Math.round((process.memoryUsage().heapTotal - process.memoryUsage().heapUsed) / 1024 / 1024),
diskSpace: 0, // Would be populated with actual disk check
processLimits: !diagnostics.some(d => d.issue.includes('descriptor limit') && d.detected)
},
overallHealth
};
}
/**
* Generate actionable recommendations
*/
generateRecommendations(diagnostics) {
const recommendations = [];
const criticalIssues = diagnostics.filter(d => d.severity === 'critical' && d.detected);
const highIssues = diagnostics.filter(d => d.severity === 'high' && d.detected);
if (criticalIssues.length > 0) {
recommendations.push('🚨 CRITICAL: Address browser session initialization issues first');
recommendations.push('💡 Run: npx playwright install chromium && npx playwright install-deps');
}
if (highIssues.some(i => i.issue.includes('Phoenix'))) {
recommendations.push('🔧 Update framework detection to properly identify Phoenix LiveView projects');
}
if (diagnostics.some(d => d.category === 'network' && d.detected)) {
recommendations.push('🌐 Verify your development server is running and accessible');
}
if (diagnostics.some(d => d.category === 'infrastructure' && d.detected)) {
recommendations.push('⚡ Consider upgrading system resources (memory, file descriptors)');
}
return recommendations;
}
// Helper methods
async isPortListening(port) {
try {
const { stdout } = await execAsync(`lsof -i :${port}`);
return stdout.trim().length > 0;
}
catch {
return false;
}
}
async detectPortConflicts(ports) {
const conflicts = [];
for (const port of ports) {
if (await this.isPortListening(port)) {
conflicts.push(port);
}
}
return conflicts;
}
async checkForPhoenixFiles() {
try {
const files = ['mix.exs', 'config/config.exs', 'lib/', 'priv/'];
for (const file of files) {
try {
await fs.access(file);
return true;
}
catch {
continue;
}
}
return false;
}
catch {
return false;
}
}
async checkForFlutterFiles() {
try {
const files = ['pubspec.yaml', 'lib/main.dart', 'web/index.html'];
for (const file of files) {
try {
await fs.access(file);
return true;
}
catch {
continue;
}
}
return false;
}
catch {
return false;
}
}
/**
* Emergency recovery mode - attempt to restore AI-Debug functionality
*/
async emergencyRecovery() {
this.logger.warn('🚑 Starting emergency AI-Debug recovery...');
const actionsPerformed = [];
const remainingIssues = [];
try {
// Emergency Action 1: Kill all browser processes
try {
await execAsync('pkill -f "chrome\\|chromium\\|playwright"');
actionsPerformed.push('Killed existing browser processes');
}
catch {
remainingIssues.push('Could not kill browser processes');
}
// Emergency Action 2: Clear browser profiles
try {
await execAsync('rm -rf /tmp/ai-debug-browser-*');
actionsPerformed.push('Cleared browser profiles');
}
catch {
remainingIssues.push('Could not clear browser profiles');
}
// Emergency Action 3: Reinstall Playwright browsers
try {
await execAsync('npx playwright install chromium --force');
actionsPerformed.push('Reinstalled Playwright browsers');
}
catch {
remainingIssues.push('Could not reinstall Playwright browsers');
}
// Emergency Action 4: Free up debugging ports
try {
const ports = [9222, 9223, 9224, 9225];
for (const port of ports) {
await execAsync(`lsof -ti:${port} | xargs kill -9`).catch(() => { });
}
actionsPerformed.push('Freed debugging ports');
}
catch {
remainingIssues.push('Could not free debugging ports');
}
const success = remainingIssues.length === 0;
if (success) {
this.logger.success('✅ Emergency recovery completed successfully');
}
else {
this.logger.warn(`⚠️ Emergency recovery partial: ${remainingIssues.length} issues remain`);
}
return {
success,
actionsPerformed,
remainingIssues
};
}
catch (error) {
remainingIssues.push(`Emergency recovery failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
return {
success: false,
actionsPerformed,
remainingIssues
};
}
}
/**
* Get diagnostic summary report
*/
getDiagnosticReport() {
const critical = this.diagnosticHistory.filter(d => d.severity === 'critical' && d.detected).length;
const high = this.diagnosticHistory.filter(d => d.severity === 'high' && d.detected).length;
const autoFixes = this.diagnosticHistory.filter(d => d.fix?.automated && d.detected).length;
return {
totalDiagnostics: this.diagnosticHistory.length,
criticalIssues: critical,
highIssues: high,
autoFixesAvailable: autoFixes,
lastDiagnosticTime: this.diagnosticHistory.length > 0
? Date.now()
: 0
};
}
}
//# sourceMappingURL=ai-debug-diagnostics.js.map