UNPKG

@ai-capabilities-suite/mcp-debugger-core

Version:

Core debugging engine for Node.js and TypeScript applications. Provides Inspector Protocol integration, breakpoint management, variable inspection, execution control, profiling, hang detection, and source map support.

183 lines 6.09 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InspectorClient = void 0; const ws_1 = __importDefault(require("ws")); const events_1 = require("events"); /** * Inspector client for connecting to Node.js Inspector Protocol via WebSocket * Implements CDP (Chrome DevTools Protocol) message handling */ class InspectorClient extends events_1.EventEmitter { constructor(wsUrl) { super(); this.wsUrl = wsUrl; this.ws = null; this.messageId = 0; this.pendingRequests = new Map(); this.connected = false; // Increase max listeners to handle multiple pause/resume cycles during sampling this.setMaxListeners(100); } /** * Connect to the Inspector Protocol WebSocket endpoint */ async connect() { const maxRetries = 3; const retryDelay = 200; for (let attempt = 0; attempt < maxRetries; attempt++) { try { await this.attemptConnect(); return; } catch (error) { if (attempt === maxRetries - 1) { throw error; } // Wait before retrying await new Promise((resolve) => setTimeout(resolve, retryDelay)); } } } attemptConnect() { return new Promise((resolve, reject) => { this.ws = new ws_1.default(this.wsUrl); this.ws.on("open", () => { this.connected = true; this.emit("connected"); resolve(); }); this.ws.on("message", (data) => { this.handleMessage(data.toString()); }); this.ws.on("error", (error) => { this.emit("error", error); reject(error); }); this.ws.on("close", () => { this.connected = false; this.emit("disconnected"); this.cleanup(); }); }); } /** * Handle incoming CDP messages */ handleMessage(data) { try { const message = JSON.parse(data); // Check if it's a response to a request if ("id" in message) { const pending = this.pendingRequests.get(message.id); if (pending) { this.pendingRequests.delete(message.id); if (message.error) { const error = new Error(message.error.message); error.code = message.error.code; error.data = message.error.data; pending.reject(error); } else { pending.resolve(message.result); } } } else if ("method" in message) { // It's an event this.emit("event", message); this.emit(message.method, message.params); } } catch (error) { this.emit("error", error); } } /** * Send a CDP command and wait for response * @param method CDP method name * @param params Optional parameters * @param timeout Timeout in milliseconds (default: 5000) */ async send(method, params, timeout = 5000) { if (!this.connected || !this.ws) { throw new Error("Inspector client is not connected"); } const id = ++this.messageId; const request = { id, method, params }; return new Promise((resolve, reject) => { // Set up timeout const timeoutId = setTimeout(() => { this.pendingRequests.delete(id); reject(new Error(`CDP command '${method}' timed out after ${timeout}ms`)); }, timeout); this.pendingRequests.set(id, { resolve: (value) => { clearTimeout(timeoutId); resolve(value); }, reject: (error) => { clearTimeout(timeoutId); reject(error); }, }); this.ws.send(JSON.stringify(request), (error) => { if (error) { clearTimeout(timeoutId); this.pendingRequests.delete(id); reject(error); } }); }); } /** * Check if the client is connected */ isConnected() { return this.connected; } /** * Disconnect from the Inspector Protocol */ async disconnect() { if (this.ws && this.ws.readyState === ws_1.default.OPEN) { return new Promise((resolve) => { const timeout = setTimeout(() => { // Force close if not closed within timeout if (this.ws) { this.ws.terminate(); this.ws = null; } this.cleanup(); resolve(); }, 1000); this.ws.once('close', () => { clearTimeout(timeout); this.ws = null; this.cleanup(); resolve(); }); this.ws.close(); }); } else { this.ws = null; this.cleanup(); } } /** * Clean up resources */ cleanup() { // Reject all pending requests for (const [id, pending] of this.pendingRequests.entries()) { pending.reject(new Error("Inspector client disconnected")); } this.pendingRequests.clear(); this.connected = false; } } exports.InspectorClient = InspectorClient; //# sourceMappingURL=inspector-client.js.map