UNPKG

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

171 lines • 6 kB
/** * Port Allocator - Dynamic Port Management for Multi-Project Sessions * * Automatically assigns unique port ranges to each debugging session * to prevent conflicts between concurrent projects. */ import * as net from 'net'; export class PortAllocator { basePort; portRangeSize; allocatedRanges; portUsageMap; // AI-Debug reserved port range: 8200-8299 (safe from common dev ports like 3000, 4000, 8000, 8080) constructor(basePort = 8200, portRangeSize = 10) { this.basePort = basePort; this.portRangeSize = portRangeSize; this.allocatedRanges = new Map(); this.portUsageMap = new Set(); } /** * Allocate a unique port range for a project session */ async allocatePortRange(projectId) { // Check if already allocated const existing = this.allocatedRanges.get(projectId); if (existing) { return existing; } // Find available base port const basePort = await this.findAvailablePortRange(); const portRange = { debugPort: basePort, inspectorPort: basePort + 1, websocketPort: basePort + 2 }; // Mark ports as used this.portUsageMap.add(portRange.debugPort); this.portUsageMap.add(portRange.inspectorPort); this.portUsageMap.add(portRange.websocketPort); // Store allocation this.allocatedRanges.set(projectId, portRange); return portRange; } /** * Release allocated port range for a project */ async releasePortRange(projectId) { const portRange = this.allocatedRanges.get(projectId); if (!portRange) { return; } // Remove from usage tracking this.portUsageMap.delete(portRange.debugPort); this.portUsageMap.delete(portRange.inspectorPort); this.portUsageMap.delete(portRange.websocketPort); // Remove allocation this.allocatedRanges.delete(projectId); } /** * Get allocated port range for a project */ getPortRange(projectId) { return this.allocatedRanges.get(projectId) || null; } /** * Get all allocated port ranges */ getAllAllocatedRanges() { return new Map(this.allocatedRanges); } /** * Check if a specific port is available */ async isPortAvailable(port) { return new Promise((resolve) => { const server = net.createServer(); server.listen(port, '127.0.0.1', () => { server.close(() => resolve(true)); }); server.on('error', () => resolve(false)); }); } /** * Find the next available port starting from a given port */ async findAvailablePort(startPort) { let port = startPort; while (port < startPort + 1000) { // Reasonable upper limit if (!this.portUsageMap.has(port) && await this.isPortAvailable(port)) { return port; } port++; } throw new Error(`No available ports found starting from ${startPort}`); } /** * Find an available port range (3 consecutive ports) */ async findAvailablePortRange() { let basePort = this.basePort; while (basePort < this.basePort + 1000) { // Check if 3 consecutive ports are available const portsNeeded = [basePort, basePort + 1, basePort + 2]; const allAvailable = await Promise.all(portsNeeded.map(port => !this.portUsageMap.has(port) && this.isPortAvailable(port))); if (allAvailable.every(available => available)) { return basePort; } // Move to next potential range basePort += this.portRangeSize; } throw new Error(`No available port range found starting from ${this.basePort}`); } /** * Get usage statistics */ getUsageStats() { const maxRanges = Math.floor(1000 / this.portRangeSize); // Theoretical max in 1000 port window return { totalAllocated: this.allocatedRanges.size, totalPortsUsed: this.portUsageMap.size, availableRanges: maxRanges - this.allocatedRanges.size, basePort: this.basePort, portRangeSize: this.portRangeSize }; } /** * Force cleanup of all allocations (emergency use) */ emergencyCleanup() { this.allocatedRanges.clear(); this.portUsageMap.clear(); } /** * Validate that allocated ports are still in use * and clean up abandoned allocations */ async validateAndCleanup() { const abandonedProjects = []; for (const [projectId, portRange] of this.allocatedRanges) { // Check if any of the allocated ports are actually in use const portsInUse = await Promise.all([ this.isPortInUse(portRange.debugPort), this.isPortInUse(portRange.inspectorPort), this.isPortInUse(portRange.websocketPort) ]); // If none of the ports are in use, mark as abandoned if (!portsInUse.some(inUse => inUse)) { abandonedProjects.push(projectId); await this.releasePortRange(projectId); } } return abandonedProjects; } /** * Check if a port is actually in use by a process */ async isPortInUse(port) { return new Promise((resolve) => { const server = net.createServer(); server.listen(port, '127.0.0.1', () => { // If we can bind to it, it's not in use server.close(() => resolve(false)); }); server.on('error', (err) => { // If we can't bind (EADDRINUSE), it's in use resolve(err.code === 'EADDRINUSE'); }); }); } } //# sourceMappingURL=port-allocator.js.map