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>
This commit is contained in:
uroma
2026-01-22 14:43:05 +00:00
Unverified
parent b82837aa5f
commit 55aafbae9a
6463 changed files with 1115462 additions and 4486 deletions

508
scripts/realtime-monitor.js Executable file
View File

@@ -0,0 +1,508 @@
#!/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 };