Skip to main content
Glama
fmangot
by fmangot
session-manager.ts4.2 kB
/** * Session Manager * Manages multiple thinking sessions for concurrent users */ import { SequentialThinkingManager } from '../lib.js'; import { generateSecureSessionId } from '../utils/crypto.js'; interface SessionData { manager: SequentialThinkingManager; lastAccess: number; created: number; } export class SessionManager { private sessions: Map<string, SessionData>; private cleanupInterval: NodeJS.Timeout | null; private readonly SESSION_TTL = 3600000; // 1 hour private readonly CLEANUP_INTERVAL = 600000; // 10 minutes private readonly MAX_SESSIONS = 10000; constructor() { this.sessions = new Map(); this.cleanupInterval = null; this.startCleanup(); } private startCleanup(): void { if (this.cleanupInterval) { return; } this.cleanupInterval = setInterval(() => { this.cleanupExpiredSessions(); }, this.CLEANUP_INTERVAL); if (this.cleanupInterval.unref) { this.cleanupInterval.unref(); } } private cleanupExpiredSessions(): void { const now = Date.now(); let cleaned = 0; for (const [id, session] of this.sessions.entries()) { if (now - session.lastAccess > this.SESSION_TTL) { session.manager.destroy(); this.sessions.delete(id); cleaned++; } } if (cleaned > 0) { console.log(`SessionManager: Cleaned up ${cleaned} expired sessions. Active: ${this.sessions.size}`); } // Emergency cleanup if over limit if (this.sessions.size > this.MAX_SESSIONS) { this.emergencyCleanup(); } } private emergencyCleanup(): void { const sessions = Array.from(this.sessions.entries()) .sort((a, b) => a[1].lastAccess - b[1].lastAccess); const toRemove = sessions.slice(0, Math.floor(this.MAX_SESSIONS * 0.2)); for (const [id, session] of toRemove) { session.manager.destroy(); this.sessions.delete(id); } console.warn( `SessionManager: Emergency cleanup removed ${toRemove.length} oldest sessions. Active: ${this.sessions.size}` ); } /** * Get or create a session */ public getOrCreate(sessionId?: string): { sessionId: string; manager: SequentialThinkingManager } { const sid = sessionId || generateSecureSessionId(); let session = this.sessions.get(sid); if (!session) { session = { manager: new SequentialThinkingManager(), lastAccess: Date.now(), created: Date.now(), }; this.sessions.set(sid, session); } session.lastAccess = Date.now(); return { sessionId: sid, manager: session.manager }; } /** * Get an existing session */ public get(sessionId: string): SequentialThinkingManager | null { const session = this.sessions.get(sessionId); if (session) { session.lastAccess = Date.now(); return session.manager; } return null; } /** * Delete a session */ public delete(sessionId: string): boolean { const session = this.sessions.get(sessionId); if (session) { session.manager.destroy(); return this.sessions.delete(sessionId); } return false; } /** * Get statistics */ public getStats() { return { totalSessions: this.sessions.size, oldestSession: this.getOldestSessionAge(), newestSession: this.getNewestSessionAge(), }; } private getOldestSessionAge(): number | null { let oldest: number | null = null; for (const session of this.sessions.values()) { if (!oldest || session.created < oldest) { oldest = session.created; } } return oldest; } private getNewestSessionAge(): number | null { let newest: number | null = null; for (const session of this.sessions.values()) { if (!newest || session.created > newest) { newest = session.created; } } return newest; } /** * Destroy all sessions and stop cleanup */ public destroy(): void { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = null; } for (const session of this.sessions.values()) { session.manager.destroy(); } this.sessions.clear(); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/fmangot/Mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server