UNPKG

@sirmrmarty/n8n-nodes-tmux-orchestrator

Version:

n8n nodes for orchestrating Claude AI agents through tmux sessions

266 lines 8.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.resourceManager = exports.ResourceScope = exports.ResourceManager = void 0; class ResourceManager { constructor(options = {}) { this.resources = new Map(); this.options = { autoCleanupOnExit: true, maxAge: 5 * 60 * 1000, maxResources: 1000, ...options }; if (this.options.autoCleanupOnExit) { this.setupExitHandlers(); } this.cleanupInterval = setInterval(() => { this.performPeriodicCleanup(); }, 30000); } static getInstance(options) { if (!ResourceManager.instance) { ResourceManager.instance = new ResourceManager(options); } return ResourceManager.instance; } createInterval(callback, delay, description) { const id = this.generateId('interval'); const intervalId = setInterval(async () => { try { await callback(); } catch (error) { console.warn(`Interval ${id} callback error:`, error.message); } }, delay); const resource = { id, type: 'interval', resource: intervalId, cleanup: () => clearInterval(intervalId), createdAt: Date.now(), description }; this.resources.set(id, resource); this.enforceResourceLimits(); return { id, intervalId }; } createTimeout(callback, delay, description) { const id = this.generateId('timeout'); const timeoutId = setTimeout(async () => { try { await callback(); } catch (error) { console.warn(`Timeout ${id} callback error:`, error.message); } finally { this.cleanup(id); } }, delay); const resource = { id, type: 'timeout', resource: timeoutId, cleanup: () => clearTimeout(timeoutId), createdAt: Date.now(), description }; this.resources.set(id, resource); this.enforceResourceLimits(); return { id, timeoutId }; } registerResource(type, resource, cleanup, description) { const id = this.generateId(type); const managedResource = { id, type, resource, cleanup, createdAt: Date.now(), description }; this.resources.set(id, managedResource); this.enforceResourceLimits(); return id; } async cleanup(id) { const resource = this.resources.get(id); if (!resource) { return false; } try { await resource.cleanup(); this.resources.delete(id); return true; } catch (error) { console.warn(`Failed to cleanup resource ${id}:`, error.message); this.resources.delete(id); return false; } } async cleanupByType(type) { const resourcesOfType = Array.from(this.resources.values()) .filter(resource => resource.type === type); let cleanedUp = 0; for (const resource of resourcesOfType) { const success = await this.cleanup(resource.id); if (success) cleanedUp++; } return cleanedUp; } async cleanupAll() { const resourceIds = Array.from(this.resources.keys()); let cleanedUp = 0; for (const id of resourceIds) { const success = await this.cleanup(id); if (success) cleanedUp++; } if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = undefined; } return cleanedUp; } getStats() { const now = Date.now(); const resources = Array.from(this.resources.values()); const byType = {}; let totalAge = 0; let oldestAge = 0; for (const resource of resources) { byType[resource.type] = (byType[resource.type] || 0) + 1; const age = now - resource.createdAt; totalAge += age; oldestAge = Math.max(oldestAge, age); } return { total: resources.length, byType, oldestAge, averageAge: resources.length > 0 ? totalAge / resources.length : 0 }; } listResources() { return Array.from(this.resources.values()).map(resource => ({ ...resource, resource: `[${typeof resource.resource}]` })); } createScope(description) { return new ResourceScope(this, description); } generateId(type) { const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 8); return `${type}_${timestamp}_${random}`; } setupExitHandlers() { const cleanup = async () => { console.log('Cleaning up resources on exit...'); const cleaned = await this.cleanupAll(); console.log(`Cleaned up ${cleaned} resources`); }; process.on('exit', () => { for (const resource of this.resources.values()) { try { if (typeof resource.cleanup === 'function') { resource.cleanup(); } } catch (error) { } } }); process.on('SIGTERM', async () => { await cleanup(); process.exit(0); }); process.on('SIGINT', async () => { await cleanup(); process.exit(0); }); process.on('uncaughtException', async (error) => { console.error('Uncaught exception:', error); await cleanup(); process.exit(1); }); process.on('unhandledRejection', async (reason) => { console.error('Unhandled rejection:', reason); await cleanup(); process.exit(1); }); } performPeriodicCleanup() { const now = Date.now(); const toCleanup = []; for (const [id, resource] of this.resources.entries()) { const age = now - resource.createdAt; if (this.options.maxAge && age > this.options.maxAge) { toCleanup.push(id); } } for (const id of toCleanup) { this.cleanup(id).catch(error => { console.warn(`Failed to cleanup old resource ${id}:`, error.message); }); } } enforceResourceLimits() { if (!this.options.maxResources) return; const resourceCount = this.resources.size; if (resourceCount <= this.options.maxResources) return; const resources = Array.from(this.resources.entries()) .sort((a, b) => a[1].createdAt - b[1].createdAt); const toRemove = resources.slice(0, resourceCount - this.options.maxResources); for (const [id] of toRemove) { this.cleanup(id).catch(error => { console.warn(`Failed to cleanup resource during limit enforcement ${id}:`, error.message); }); } } } exports.ResourceManager = ResourceManager; class ResourceScope { constructor(manager, description) { this.resourceIds = []; this.manager = manager; this.description = description; } createInterval(callback, delay, description) { const result = this.manager.createInterval(callback, delay, description); this.resourceIds.push(result.id); return result; } createTimeout(callback, delay, description) { const result = this.manager.createTimeout(callback, delay, description); this.resourceIds.push(result.id); return result; } registerResource(type, resource, cleanup, description) { const id = this.manager.registerResource(type, resource, cleanup, description); this.resourceIds.push(id); return id; } async cleanup() { let cleanedUp = 0; for (const id of this.resourceIds) { const success = await this.manager.cleanup(id); if (success) cleanedUp++; } this.resourceIds = []; return cleanedUp; } getResourceCount() { return this.resourceIds.length; } } exports.ResourceScope = ResourceScope; exports.resourceManager = ResourceManager.getInstance(); //# sourceMappingURL=resourceManager.js.map