UNPKG

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

257 lines โ€ข 8.45 kB
/** * Project Session Manager - Persistent Session Storage * * Provides file-based session persistence for multi-project debugging * ensuring sessions survive MCP server restarts. */ import * as path from 'path'; import * as fs from 'fs/promises'; import * as crypto from 'crypto'; import { existsSync } from 'fs'; import * as os from 'os'; export class ProjectSessionManager { baseDir; projectPath; projectHash; sessionFile; browserFile; constructor(projectPath) { this.baseDir = path.join(os.homedir(), '.ai-debug'); this.projectPath = projectPath || process.cwd(); this.projectHash = this.hashProject(this.projectPath); const projectDir = path.join(this.baseDir, 'projects', this.projectHash); this.sessionFile = path.join(projectDir, 'sessions.json'); this.browserFile = path.join(projectDir, 'browser-endpoints.json'); } /** * Initialize storage directories */ async initialize() { const projectDir = path.dirname(this.sessionFile); await fs.mkdir(projectDir, { recursive: true }); // Clean up stale browser connections on startup await this.cleanupStaleBrowsers(); } /** * Get or create project session */ async getProjectSession() { await this.initialize(); try { if (existsSync(this.sessionFile)) { const data = await fs.readFile(this.sessionFile, 'utf-8'); const session = JSON.parse(data); // Convert sessions back to Map session.sessions = new Map(Object.entries(session.sessions || {})); session.createdAt = new Date(session.createdAt); session.lastActivity = new Date(session.lastActivity); return session; } } catch (error) { console.error('๐Ÿšจ Error loading session file:', error); } // Create new session return { id: `proj_${this.projectHash}`, projectPath: this.projectPath, projectHash: this.projectHash, sessions: new Map(), createdAt: new Date(), lastActivity: new Date() }; } /** * Save session to disk */ async saveProjectSession(session) { const data = { ...session, sessions: Object.fromEntries(session.sessions), lastActivity: new Date() }; await fs.writeFile(this.sessionFile, JSON.stringify(data, null, 2)); } /** * Add a debug session */ async addSession(sessionId, sessionData) { const project = await this.getProjectSession(); project.sessions.set(sessionId, sessionData); await this.saveProjectSession(project); } /** * Get a debug session */ async getSession(sessionId) { const project = await this.getProjectSession(); return project.sessions.get(sessionId) || null; } /** * Remove a debug session */ async removeSession(sessionId) { const project = await this.getProjectSession(); project.sessions.delete(sessionId); await this.saveProjectSession(project); } /** * Save browser connection info */ async saveBrowserConnection(endpoint, pid) { const connection = { endpoint, pid, projectHash: this.projectHash }; await fs.writeFile(this.browserFile, JSON.stringify(connection, null, 2)); // Update project session const project = await this.getProjectSession(); project.browserEndpoint = endpoint; project.browserPid = pid; await this.saveProjectSession(project); } /** * Get browser connection info */ async getBrowserConnection() { try { if (existsSync(this.browserFile)) { const data = await fs.readFile(this.browserFile, 'utf-8'); const connection = JSON.parse(data); // Verify browser process is still alive if (connection.pid && this.isProcessAlive(connection.pid)) { return connection; } else { // Clean up dead connection await this.clearBrowserConnection(); } } } catch (error) { console.error('๐Ÿšจ Error loading browser connection:', error); } return null; } /** * Clear browser connection */ async clearBrowserConnection() { try { if (existsSync(this.browserFile)) { await fs.unlink(this.browserFile); } const project = await this.getProjectSession(); delete project.browserEndpoint; delete project.browserPid; await this.saveProjectSession(project); } catch (error) { console.error('๐Ÿšจ Error clearing browser connection:', error); } } /** * Clean up all sessions for this project */ async cleanupProject() { const projectDir = path.dirname(this.sessionFile); // Kill any running browsers const browserConnection = await this.getBrowserConnection(); if (browserConnection?.pid) { try { process.kill(browserConnection.pid, 'SIGTERM'); } catch (error) { // Process might already be dead } } // Remove project directory await fs.rm(projectDir, { recursive: true, force: true }); } /** * Clean up stale browser connections */ async cleanupStaleBrowsers() { const browserConnection = await this.getBrowserConnection(); if (browserConnection && !this.isProcessAlive(browserConnection.pid)) { console.log('๐Ÿงน Cleaning up stale browser connection'); await this.clearBrowserConnection(); } } /** * Check if process is alive */ isProcessAlive(pid) { try { process.kill(pid, 0); return true; } catch { return false; } } /** * Hash project path for consistent ID */ hashProject(projectPath) { const normalized = path.resolve(projectPath); const projectName = path.basename(normalized); const hash = crypto .createHash('sha256') .update(normalized) .digest('hex') .substring(0, 8); return `${projectName}-${hash}`; } /** * Get all projects with sessions */ static async listAllProjects() { const baseDir = path.join(os.homedir(), '.ai-debug', 'projects'); const projects = []; try { if (!existsSync(baseDir)) { return projects; } const dirs = await fs.readdir(baseDir); for (const dir of dirs) { const sessionFile = path.join(baseDir, dir, 'sessions.json'); if (existsSync(sessionFile)) { try { const data = await fs.readFile(sessionFile, 'utf-8'); const session = JSON.parse(data); projects.push({ projectHash: dir, projectPath: session.projectPath, sessionCount: Object.keys(session.sessions || {}).length }); } catch (error) { // Skip invalid session files } } } } catch (error) { console.error('๐Ÿšจ Error listing projects:', error); } return projects; } /** * Clean up all projects */ static async cleanupAllProjects() { const baseDir = path.join(os.homedir(), '.ai-debug', 'projects'); try { if (existsSync(baseDir)) { await fs.rm(baseDir, { recursive: true, force: true }); console.log('โœ… Cleaned up all project sessions'); } } catch (error) { console.error('๐Ÿšจ Error cleaning up projects:', error); } } } //# sourceMappingURL=project-session-manager.js.map