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
JavaScript
/**
* 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