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
222 lines • 7.64 kB
JavaScript
// Session Resource Management System
// Provides session-level resource tracking and cleanup
import { ResourceManager } from './resource-manager.js';
/**
* SessionResourceManager - Manages resources at the session level
*
* Provides a higher-level interface for resource management that
* coordinates with the global ResourceManager
*/
export class SessionResourceManager {
sessions = new Map();
resourceManager;
cleanupCallbacks = [];
constructor(resourceManager) {
this.resourceManager = resourceManager || new ResourceManager();
}
/**
* Create a new session
*/
async createSession(sessionId) {
if (this.sessions.has(sessionId)) {
throw new Error(`Session ${sessionId} already exists`);
}
const session = {
sessionId,
createdAt: new Date(),
lastUsed: new Date(),
resources: [],
status: 'active'
};
this.sessions.set(sessionId, session);
return session;
}
/**
* Get session by ID
*/
getSession(sessionId) {
return this.sessions.get(sessionId);
}
/**
* Attach resource to session
*/
async attachResource(sessionId, resourceType, resource) {
const session = this.sessions.get(sessionId);
if (!session) {
throw new Error(`Session ${sessionId} not found`);
}
if (session.status !== 'active') {
throw new Error(`Session ${sessionId} is not active (status: ${session.status})`);
}
// Track in global resource manager
this.resourceManager.trackResource(resourceType, sessionId, resource);
// Track in session
const trackedResource = {
sessionId,
resourceType,
resource,
addedAt: new Date(),
lastUsed: new Date()
};
session.resources.push(trackedResource);
session.lastUsed = new Date();
}
/**
* Get all resources for a session
*/
getSessionResources(sessionId) {
const session = this.sessions.get(sessionId);
return session ? [...session.resources] : [];
}
/**
* Update session last used time
*/
touchSession(sessionId) {
const session = this.sessions.get(sessionId);
if (session) {
session.lastUsed = new Date();
}
}
/**
* Cleanup session and all its resources
*/
async cleanupSession(sessionId) {
const session = this.sessions.get(sessionId);
if (!session) {
return; // Already cleaned up
}
// Mark as cleaning to prevent new resources
session.status = 'cleaning';
try {
// Cleanup all resources
const cleanupPromises = session.resources.map(async (resource) => {
try {
if (resource.resourceType === 'page' && resource.resource.close) {
await resource.resource.close();
}
else if (resource.resourceType === 'browser' && resource.resource.close) {
await resource.resource.close();
}
else if (resource.resourceType === 'context' && resource.resource.close) {
await resource.resource.close();
}
}
catch (error) {
console.warn(`Failed to cleanup ${resource.resourceType} resource:`, error);
}
});
await Promise.allSettled(cleanupPromises);
// Run registered cleanup callbacks
for (const callback of this.cleanupCallbacks) {
try {
await callback(sessionId);
}
catch (error) {
console.warn(`Cleanup callback failed for session ${sessionId}:`, error);
}
}
// Cleanup in global resource manager
await this.resourceManager.cleanupSession(sessionId);
// Mark as closed and remove from tracking
session.status = 'closed';
this.sessions.delete(sessionId);
}
catch (error) {
console.error(`Error cleaning up session ${sessionId}:`, error);
// Force remove even if cleanup failed
this.sessions.delete(sessionId);
}
}
/**
* Cleanup all sessions
*/
async cleanupAll() {
const sessionIds = Array.from(this.sessions.keys());
const cleanupPromises = sessionIds.map(sessionId => this.cleanupSession(sessionId).catch(error => console.error(`Failed to cleanup session ${sessionId}:`, error)));
await Promise.allSettled(cleanupPromises);
}
/**
* Find and cleanup stale sessions
*/
async cleanupStaleSessions(maxAgeMinutes = 120) {
const now = Date.now();
const maxAge = maxAgeMinutes * 60 * 1000;
let cleanedCount = 0;
const staleSessionIds = [];
for (const [sessionId, session] of this.sessions.entries()) {
if ((now - session.lastUsed.getTime()) > maxAge) {
staleSessionIds.push(sessionId);
}
}
// Cleanup stale sessions
for (const sessionId of staleSessionIds) {
await this.cleanupSession(sessionId);
cleanedCount++;
}
return cleanedCount;
}
/**
* Get session statistics
*/
getSessionStats() {
const activeSessions = Array.from(this.sessions.values()).filter(s => s.status === 'active');
const totalResources = activeSessions.reduce((sum, session) => sum + session.resources.length, 0);
const resourcesByType = {};
// Count resources by type
activeSessions.forEach(session => {
session.resources.forEach(resource => {
resourcesByType[resource.resourceType] = (resourcesByType[resource.resourceType] || 0) + 1;
});
});
// Calculate session ages
const now = Date.now();
const sessionAges = activeSessions.map(session => Math.floor((now - session.createdAt.getTime()) / 1000 / 60) // age in minutes
);
return {
totalSessions: this.sessions.size,
activeSessions: activeSessions.length,
totalResources,
resourcesByType,
averageResourcesPerSession: activeSessions.length > 0 ? totalResources / activeSessions.length : 0,
sessionAges
};
}
/**
* Check if session exists and is active
*/
isSessionActive(sessionId) {
const session = this.sessions.get(sessionId);
return session ? session.status === 'active' : false;
}
/**
* Get all active session IDs
*/
getActiveSessionIds() {
return Array.from(this.sessions.entries())
.filter(([_, session]) => session.status === 'active')
.map(([sessionId]) => sessionId);
}
/**
* Register cleanup callback
*/
registerCleanupCallback(callback) {
this.cleanupCallbacks.push(callback);
}
/**
* Get the underlying ResourceManager
*/
getResourceManager() {
return this.resourceManager;
}
/**
* Clear all sessions (for testing)
*/
clear() {
this.sessions.clear();
}
}
/**
* Global session resource manager instance
*/
export const globalSessionResourceManager = new SessionResourceManager();
//# sourceMappingURL=session-resource-manager.js.map