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.

355 lines 10.6 kB
"use strict"; /** * Multi-target debugging support for debugging multiple processes simultaneously * Coordinates breakpoints, aggregates logs, and supports parent-child process debugging */ Object.defineProperty(exports, "__esModule", { value: true }); exports.MultiTargetDebugger = void 0; const events_1 = require("events"); /** * Manages debugging of multiple processes simultaneously */ class MultiTargetDebugger extends events_1.EventEmitter { constructor() { super(...arguments); this.targets = new Map(); this.globalBreakpoints = new Map(); this.logs = []; this.maxLogSize = 10000; } /** * Add a debug target */ addTarget(id, name, session, parentId) { if (this.targets.has(id)) { throw new Error(`Target already exists: ${id}`); } const target = { id, name, session, parentId, children: [], }; this.targets.set(id, target); // Update parent's children list if (parentId) { const parent = this.targets.get(parentId); if (parent) { parent.children.push(id); } } // Set up log aggregation this.setupLogAggregation(target); this.emit("target-added", target); } /** * Remove a debug target */ removeTarget(id) { const target = this.targets.get(id); if (!target) { return false; } // Remove from parent's children list if (target.parentId) { const parent = this.targets.get(target.parentId); if (parent) { parent.children = parent.children.filter((childId) => childId !== id); } } // Remove all children for (const childId of target.children) { this.removeTarget(childId); } this.targets.delete(id); this.emit("target-removed", id); return true; } /** * Get a debug target by ID */ getTarget(id) { return this.targets.get(id); } /** * Get all debug targets */ getAllTargets() { return Array.from(this.targets.values()); } /** * Get root targets (targets without parents) */ getRootTargets() { return Array.from(this.targets.values()).filter((t) => !t.parentId); } /** * Get children of a target */ getChildren(targetId) { const target = this.targets.get(targetId); if (!target) { return []; } return target.children .map((childId) => this.targets.get(childId)) .filter((child) => child !== undefined); } /** * Set a breakpoint across multiple targets */ async setGlobalBreakpoint(file, line, targetIds, condition) { const breakpointId = `bp-${Date.now()}-${Math.random() .toString(36) .substring(2, 11)}`; const breakpoint = { file, line, condition, targets: [], }; // Set breakpoint on each target for (const targetId of targetIds) { const target = this.targets.get(targetId); if (!target) { continue; } try { await target.session.setBreakpoint(file, line, condition); breakpoint.targets.push(targetId); } catch (error) { this.emit("breakpoint-error", { targetId, file, line, error, }); } } this.globalBreakpoints.set(breakpointId, breakpoint); this.emit("global-breakpoint-set", breakpointId, breakpoint); return breakpointId; } /** * Remove a global breakpoint */ async removeGlobalBreakpoint(breakpointId) { const breakpoint = this.globalBreakpoints.get(breakpointId); if (!breakpoint) { return false; } // Remove breakpoint from each target for (const targetId of breakpoint.targets) { const target = this.targets.get(targetId); if (!target) { continue; } try { // Find and remove the breakpoint const breakpoints = target.session.getAllBreakpoints(); const matchingBp = breakpoints.find((bp) => bp.file === breakpoint.file && bp.line === breakpoint.line); if (matchingBp) { await target.session.removeBreakpoint(matchingBp.id); } } catch (error) { this.emit("breakpoint-error", { targetId, error, }); } } this.globalBreakpoints.delete(breakpointId); this.emit("global-breakpoint-removed", breakpointId); return true; } /** * Get all global breakpoints */ getGlobalBreakpoints() { return new Map(this.globalBreakpoints); } /** * Continue execution on all targets */ async continueAll() { const promises = []; for (const target of this.targets.values()) { promises.push(target.session.resume().catch((error) => { this.emit("target-error", { targetId: target.id, operation: "resume", error, }); })); } await Promise.all(promises); } /** * Continue execution on specific targets */ async continueTargets(targetIds) { const promises = []; for (const targetId of targetIds) { const target = this.targets.get(targetId); if (!target) { continue; } promises.push(target.session.resume().catch((error) => { this.emit("target-error", { targetId, operation: "resume", error, }); })); } await Promise.all(promises); } /** * Pause execution on all targets */ async pauseAll() { const promises = []; for (const target of this.targets.values()) { promises.push(target.session.pause().catch((error) => { this.emit("target-error", { targetId: target.id, operation: "pause", error, }); })); } await Promise.all(promises); } /** * Get aggregated logs from all targets */ getAggregatedLogs(options) { let logs = this.logs; // Filter by target IDs if (options?.targetIds) { const targetIdSet = new Set(options.targetIds); logs = logs.filter((log) => targetIdSet.has(log.targetId)); } // Filter by level if (options?.level) { logs = logs.filter((log) => log.level === options.level); } // Filter by timestamp if (options?.since !== undefined) { logs = logs.filter((log) => log.timestamp >= options.since); } // Apply limit if (options?.limit) { logs = logs.slice(-options.limit); } return logs; } /** * Clear aggregated logs */ clearLogs() { this.logs = []; this.emit("logs-cleared"); } /** * Set up log aggregation for a target */ setupLogAggregation(target) { // Get the process from the session const process = target.session.getProcess(); if (!process) { return; } // Listen for stdout if (process.stdout) { process.stdout.on("data", (data) => { this.addLog({ timestamp: Date.now(), targetId: target.id, targetName: target.name, level: "stdout", message: data.toString(), }); }); } // Listen for stderr if (process.stderr) { process.stderr.on("data", (data) => { this.addLog({ timestamp: Date.now(), targetId: target.id, targetName: target.name, level: "stderr", message: data.toString(), }); }); } // Note: Debug-level logs would require inspector integration // which is not exposed through the DebugSession API } /** * Add a log entry */ addLog(log) { this.logs.push(log); // Trim logs if exceeding max size if (this.logs.length > this.maxLogSize) { this.logs = this.logs.slice(-this.maxLogSize); } this.emit("log", log); } /** * Set maximum log size */ setMaxLogSize(size) { if (size <= 0) { throw new Error("Max log size must be positive"); } this.maxLogSize = size; // Trim existing logs if needed if (this.logs.length > this.maxLogSize) { this.logs = this.logs.slice(-this.maxLogSize); } } /** * Get target hierarchy as a tree structure */ getTargetTree() { return this.getRootTargets().map((root) => this.buildTargetTree(root)); } /** * Build target tree recursively */ buildTargetTree(target) { const children = this.getChildren(target.id); return { ...target, children: children.map((child) => child.id), }; } /** * Stop all debug targets */ async stopAll() { const promises = []; for (const target of this.targets.values()) { promises.push(target.session.cleanup().catch((error) => { this.emit("target-error", { targetId: target.id, operation: "cleanup", error, }); })); } await Promise.all(promises); // Clear all targets this.targets.clear(); this.globalBreakpoints.clear(); this.emit("all-stopped"); } } exports.MultiTargetDebugger = MultiTargetDebugger; //# sourceMappingURL=multi-target-debugger.js.map