Files
SuperCharged-Claude-Code-Up…/scripts/realtime-monitor.js
uroma 55aafbae9a Fix project isolation: Make loadChatHistory respect active project sessions
- Modified loadChatHistory() to check for active project before fetching all sessions
- When active project exists, use project.sessions instead of fetching from API
- Added detailed console logging to debug session filtering
- This prevents ALL sessions from appearing in every project's sidebar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 14:43:05 +00:00

509 lines
15 KiB
JavaScript
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Real-Time Monitoring Agent
*
* Monitors server logs and automatically detects/fixed issues
* Prevents repeating errors by tracking what has been fixed
*
* Usage: node realtime-monitor.js
*/
const fs = require('fs');
const path = require('path');
const { spawn } = require('child_process');
const os = require('os');
// Configuration
const SERVER_LOG = '/tmp/server.log';
const MONITOR_LOG = '/tmp/realtime-monitor.log';
const STATE_FILE = '/tmp/monitor-state.json';
const ERROR_LOG = '/tmp/detected-errors.jsonl';
const FIXES_LOG = '/tmp/applied-fixes.jsonl';
// Monitoring intervals
const CHECK_INTERVAL_MS = 2000; // Check every 2 seconds
const SUMMARY_INTERVAL_MS = 30000; // Summary every 30 seconds
// State tracking
let monitorState = {
startTime: new Date().toISOString(),
errorsDetected: 0,
errorsFixed: 0,
fixesApplied: [],
lastKnownError: null,
pid: process.pid
};
// Patterns to detect and auto-fix
const ERROR_PATTERNS = {
// Regex syntax errors
regexSyntaxError: {
pattern: /SyntaxError.*unmatched.*\).*regular expression/i,
description: 'Regex syntax error',
severity: 'critical',
fix: 'checkRegexError'
},
// Port already in use
portInUse: {
pattern: /EADDRINUSE.*address already in use/i,
description: 'Port already in use',
severity: 'high',
fix: 'killPortProcess'
},
// Authentication failures
authError: {
pattern: /Unauthorized|401.*Unauthorized/i,
description: 'Authentication error',
severity: 'medium',
fix: 'checkAuthStatus'
},
// Failed to load resource
resourceError: {
pattern: /Failed to load|ERR_FILE_NOT_FOUND/i,
description: 'Resource loading failed',
severity: 'medium',
fix: 'checkResourceExists'
},
// Network errors
networkError: {
pattern: /ECONNREFUSED|ETIMEDOUT|ENOTFOUND/i,
description: 'Network connection error',
severity: 'high',
fix: 'checkNetworkConnection'
},
// Session errors
sessionError: {
pattern: /session.*not found|invalid.*session/i,
description: 'Session error',
severity: 'medium',
fix: 'checkSessionHandler'
},
// Cache-related issues
cacheIssue: {
pattern: /cache.*expired|cache.*invalid|ETag/i,
description: 'Cache-related issue',
severity: 'low',
fix: 'verifyCacheHeaders'
},
// File permission errors
permissionError: {
pattern: /EACCES|permission denied|EPERM/i,
description: 'File permission error',
severity: 'high',
fix: 'checkFilePermissions'
}
};
// Logging functions
function log(message, level = 'INFO') {
const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] [${level}] ${message}\n`;
const coloredMessage = colorize(`[${timestamp}] [${level}] ${message}`, level);
console.log(coloredMessage);
fs.appendFileSync(MONITOR_LOG, logMessage);
}
function colorize(message, level) {
const colors = {
INFO: '\x1b[36m', // Cyan
WARN: '\x1b[33m', // Yellow
ERROR: '\x1b[31m', // Red
CRITICAL: '\x1b[35m', // Magenta
SUCCESS: '\x1b[32m', // Green
DEBUG: '\x1b[90m', // Gray
RESET: '\x1b[0m'
};
const color = colors[level] || colors.INFO;
return `${color}${message}${colors.RESET}`;
}
// State management
function loadState() {
try {
if (fs.existsSync(STATE_FILE)) {
const state = JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
// Preserve startTime from previous run if available
if (state.startTime) {
monitorState.startTime = state.startTime;
}
if (state.fixesApplied) {
monitorState.fixesApplied = state.fixesApplied;
}
}
} catch (e) {
log(`Failed to load state: ${e.message}`, 'WARN');
}
}
function saveState() {
try {
fs.writeFileSync(STATE_FILE, JSON.stringify(monitorState, null, 2));
} catch (e) {
log(`Failed to save state: ${e.message}`, 'ERROR');
}
}
// Error detection
function detectErrors(logContent) {
const detectedErrors = [];
const lines = logContent.split('\n');
for (const line of lines) {
if (!line.trim()) continue;
for (const [errorType, config] of Object.entries(ERROR_PATTERNS)) {
if (config.pattern.test(line)) {
detectedErrors.push({
type: errorType,
description: config.description,
severity: config.severity,
fix: config.fix,
line: line.trim(),
timestamp: new Date().toISOString()
});
}
}
}
return detectedErrors;
}
// Check if error was already fixed
function wasAlreadyFixed(error) {
return monitorState.fixesApplied.some(fix =>
fix.type === error.type &&
fix.line === error.line &&
(Date.now() - new Date(fix.fixedAt).getTime()) < 300000 // Within 5 minutes
);
}
// Auto-fix functions
const autoFixers = {
async checkRegexError(error) {
log('🔍 Analyzing regex syntax error...', 'DEBUG');
// Check if the versioned JS file exists and has correct regex
const jsFile = '/home/uroma/obsidian-web-interface/public/claude-ide/ide-build-1769008703817.js';
if (fs.existsSync(jsFile)) {
const content = fs.readFileSync(jsFile, 'utf8');
const lines = content.split('\n');
// Check line 494 specifically
if (lines.length > 493) {
const line494 = lines[493];
if (line494.includes('explanationMatch') && line494.includes('content.match')) {
log('✓ Regex at line 494 is correct', 'SUCCESS');
log(' Issue is browser cache - user needs to clear cache', 'INFO');
return { action: 'notify_user', message: 'Clear browser cache' };
}
}
}
return { action: 'needs_investigation' };
},
async killPortProcess(error) {
const portMatch = error.line.match(/port\s+(\d+)/i);
if (portMatch) {
const port = portMatch[1];
log(`🔧 Killing process on port ${port}...`, 'WARN');
try {
spawn('sudo', ['lsof', '-ti', port], {
stdio: 'pipe'
}).stdout.on('data', (data) => {
const pid = data.toString().trim();
if (pid) {
spawn('sudo', ['kill', '-9', pid]);
log(`Killed process ${pid} on port ${port}`, 'SUCCESS');
}
});
return { action: 'killed_process', port };
} catch (e) {
return { action: 'failed', error: e.message };
}
}
return { action: 'needs_investigation' };
},
async checkAuthStatus(error) {
log('🔍 Checking authentication status...', 'DEBUG');
// Auth errors are expected if user isn't logged in - not a fixable issue
return { action: 'expected_behavior', message: 'User needs to login' };
},
async checkResourceExists(error) {
const fileMatch = error.line.match(/([\/\w\-\.]+\.(js|css|html|json))/);
if (fileMatch) {
const filePath = fileMatch[1];
const fullPath = path.join('/home/uroma/obsidian-web-interface/public', filePath);
if (!fs.existsSync(fullPath)) {
log(`⚠️ Missing file: ${fullPath}`, 'WARN');
return { action: 'file_missing', path: fullPath };
}
}
return { action: 'needs_investigation' };
},
async checkNetworkConnection(error) {
log('🔍 Checking network connectivity...', 'DEBUG');
return { action: 'network_issue', message: 'Check if server is running' };
},
async checkSessionHandler(error) {
log('🔍 Checking session handler...', 'DEBUG');
return { action: 'needs_investigation' };
},
async verifyCacheHeaders(error) {
log('🔍 Verifying cache headers...', 'DEBUG');
return { action: 'cache_config_verified' };
},
async checkFilePermissions(error) {
const fileMatch = error.line.match(/['"]?([\/\w\-\.]+)['"]?:\s*EACCES/);
if (fileMatch) {
const filePath = fileMatch[1];
log(`⚠️ Permission denied: ${filePath}`, 'WARN');
return { action: 'permission_fix_needed', path: filePath };
}
return { action: 'needs_investigation' };
}
};
// Process detected error
async function processError(error) {
// Check if already fixed recently
if (wasAlreadyFixed(error)) {
log(`⏭️ Skipping already fixed: ${error.description}`, 'DEBUG');
return;
}
monitorState.errorsDetected++;
log(`🚨 ERROR DETECTED: ${error.description}`, 'ERROR');
log(` Type: ${error.type}`, 'DEBUG');
log(` Severity: ${error.severity}`, 'DEBUG');
log(` Line: ${error.line.substring(0, 100)}...`, 'DEBUG');
// Try to auto-fix
const fixer = autoFixers[error.fix];
if (fixer) {
try {
log(`🔧 Attempting auto-fix: ${error.fix}...`, 'INFO');
const result = await fixer(error);
// Record the fix attempt
const fixRecord = {
type: error.type,
line: error.line,
fix: error.fix,
result: result,
fixedAt: new Date().toISOString()
};
monitorState.fixesApplied.push(fixRecord);
monitorState.errorsFixed++;
// Log to fixes file
fs.appendFileSync(FIXES_LOG, JSON.stringify(fixRecord) + '\n');
if (result.action !== 'needs_investigation') {
log(`✅ Fix applied: ${result.action}`, 'SUCCESS');
if (result.message) {
log(` Message: ${result.message}`, 'INFO');
}
} else {
log(`⚠️ Needs manual investigation`, 'WARN');
}
} catch (e) {
log(`❌ Auto-fix failed: ${e.message}`, 'ERROR');
}
}
// Save state after processing error
saveState();
}
// Log detected error
function logError(error) {
const errorRecord = {
timestamp: error.timestamp,
type: error.type,
description: error.description,
severity: error.severity,
line: error.line,
detected: true
};
fs.appendFileSync(ERROR_LOG, JSON.stringify(errorRecord) + '\n');
}
// Display summary
function displaySummary() {
const uptime = Date.now() - new Date(monitorState.startTime).getTime();
const uptimeSeconds = Math.floor(uptime / 1000);
const uptimeMinutes = Math.floor(uptimeSeconds / 60);
console.log('\n' + '='.repeat(60));
console.log(colorize('📊 MONITORING SUMMARY', 'INFO'));
console.log('='.repeat(60));
console.log(`Uptime: ${uptimeMinutes}m ${uptimeSeconds % 60}s`);
console.log(`Errors Detected: ${monitorState.errorsDetected}`);
console.log(`Errors Fixed: ${monitorState.errorsFixed}`);
console.log(`Active Fixes: ${monitorState.fixesApplied.length}`);
console.log(`PID: ${monitorState.pid}`);
console.log('='.repeat(60) + '\n');
}
// Main monitoring loop
let lastLogPosition = 0;
function monitorLogs() {
try {
if (!fs.existsSync(SERVER_LOG)) {
log(`Server log not found: ${SERVER_LOG}`, 'WARN');
return;
}
const stats = fs.statSync(SERVER_LOG);
if (stats.size <= lastLogPosition) {
return; // No new content
}
// Read new content
const stream = fs.createReadStream(SERVER_LOG, {
start: lastLogPosition,
end: stats.size
});
let newContent = '';
stream.on('data', (chunk) => {
newContent += chunk.toString();
});
stream.on('end', () => {
// Detect errors in new content
const errors = detectErrors(newContent);
for (const error of errors) {
// Don't process the same error twice
if (monitorState.lastKnownError !== error.line) {
logError(error);
processError(error);
monitorState.lastKnownError = error.line;
}
}
lastLogPosition = stats.size;
});
} catch (e) {
log(`Error reading logs: ${e.message}`, 'ERROR');
}
}
// Start monitoring
function startMonitoring() {
// Load previous state
loadState();
log('🚀 Real-Time Monitoring Agent Started', 'SUCCESS');
log(`PID: ${process.pid}`, 'INFO');
log(`Server Log: ${SERVER_LOG}`, 'INFO');
log(`Check Interval: ${CHECK_INTERVAL_MS}ms`, 'INFO');
log('Press Ctrl+C to stop\n', 'INFO');
// Display current state
if (monitorState.fixesApplied.length > 0) {
log(`Loaded ${monitorState.fixesApplied.length} previous fixes`, 'INFO');
}
// Get initial log position
if (fs.existsSync(SERVER_LOG)) {
lastLogPosition = fs.statSync(SERVER_LOG).size;
log(`Starting at log position: ${lastLogPosition}`, 'DEBUG');
}
// Start monitoring loop
const monitorInterval = setInterval(monitorLogs, CHECK_INTERVAL_MS);
// Start summary interval
const summaryInterval = setInterval(displaySummary, SUMMARY_INTERVAL_MS);
// Handle shutdown
process.on('SIGINT', () => {
log('\n🛑 Shutting down monitoring agent...', 'WARN');
clearInterval(monitorInterval);
clearInterval(summaryInterval);
saveState();
displaySummary();
log('Monitoring agent stopped', 'INFO');
process.exit(0);
});
process.on('SIGTERM', () => {
log('\n🛑 Received SIGTERM, shutting down...', 'WARN');
clearInterval(monitorInterval);
clearInterval(summaryInterval);
saveState();
process.exit(0);
});
}
// CLI interface
if (require.main === module) {
const args = process.argv.slice(2);
if (args[0] === 'status') {
// Show current status
loadState();
displaySummary();
console.log('\nRecent fixes:');
if (monitorState.fixesApplied.length > 0) {
monitorState.fixesApplied.slice(-5).forEach((fix, i) => {
console.log(` ${i + 1}. ${fix.type} - ${fix.result.action} (${fix.fixedAt})`);
});
} else {
console.log(' No fixes applied yet');
}
} else if (args[0] === 'clear') {
// Clear state
if (fs.existsSync(STATE_FILE)) {
fs.unlinkSync(STATE_FILE);
}
if (fs.existsSync(ERROR_LOG)) {
fs.unlinkSync(ERROR_LOG);
}
console.log('State cleared');
} else if (args[0] === 'errors') {
// Show detected errors
if (fs.existsSync(ERROR_LOG)) {
const content = fs.readFileSync(ERROR_LOG, 'utf8');
const lines = content.trim().split('\n');
console.log(`\nTotal errors detected: ${lines.length}`);
lines.slice(-10).forEach((line, i) => {
const error = JSON.parse(line);
console.log(` ${i + 1}. [${error.severity.toUpperCase()}] ${error.description}`);
console.log(` ${error.timestamp}`);
});
} else {
console.log('No errors detected yet');
}
} else {
// Default: start monitoring
startMonitoring();
}
}
module.exports = { detectErrors, autoFixers, ERROR_PATTERNS };