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
JavaScript
/**
* 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