@sirmrmarty/n8n-nodes-tmux-orchestrator
Version:
n8n nodes for orchestrating Claude AI agents through tmux sessions
266 lines • 8.8 kB
JavaScript
;
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