@john523100/vela-mcp 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +91 -0
  2. package/dist/adapter.d.ts +31 -0
  3. package/dist/adapter.js +32 -0
  4. package/dist/adapter.js.map +1 -0
  5. package/dist/capture.d.ts +41 -0
  6. package/dist/capture.js +96 -0
  7. package/dist/capture.js.map +1 -0
  8. package/dist/cdp.d.ts +18 -0
  9. package/dist/cdp.js +60 -0
  10. package/dist/cdp.js.map +1 -0
  11. package/dist/debug.d.ts +40 -0
  12. package/dist/debug.js +133 -0
  13. package/dist/debug.js.map +1 -0
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.js +146 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/launcher.d.ts +17 -0
  18. package/dist/launcher.js +128 -0
  19. package/dist/launcher.js.map +1 -0
  20. package/dist/ports.d.ts +2 -0
  21. package/dist/ports.js +22 -0
  22. package/dist/ports.js.map +1 -0
  23. package/dist/probe.d.ts +15 -0
  24. package/dist/probe.js +40 -0
  25. package/dist/probe.js.map +1 -0
  26. package/dist/session.d.ts +26 -0
  27. package/dist/session.js +31 -0
  28. package/dist/session.js.map +1 -0
  29. package/dist/spec.d.ts +39 -0
  30. package/dist/spec.js +116 -0
  31. package/dist/spec.js.map +1 -0
  32. package/dist/targets.d.ts +24 -0
  33. package/dist/targets.js +29 -0
  34. package/dist/targets.js.map +1 -0
  35. package/dist/tools/capture.d.ts +21 -0
  36. package/dist/tools/capture.js +30 -0
  37. package/dist/tools/capture.js.map +1 -0
  38. package/dist/tools/debug.d.ts +50 -0
  39. package/dist/tools/debug.js +65 -0
  40. package/dist/tools/debug.js.map +1 -0
  41. package/dist/tools/eval.d.ts +9 -0
  42. package/dist/tools/eval.js +7 -0
  43. package/dist/tools/eval.js.map +1 -0
  44. package/dist/tools/ipc.d.ts +14 -0
  45. package/dist/tools/ipc.js +19 -0
  46. package/dist/tools/ipc.js.map +1 -0
  47. package/dist/tools/launch.d.ts +16 -0
  48. package/dist/tools/launch.js +20 -0
  49. package/dist/tools/launch.js.map +1 -0
  50. package/dist/tools/main.d.ts +7 -0
  51. package/dist/tools/main.js +12 -0
  52. package/dist/tools/main.js.map +1 -0
  53. package/dist/tools/screenshot.d.ts +7 -0
  54. package/dist/tools/screenshot.js +16 -0
  55. package/dist/tools/screenshot.js.map +1 -0
  56. package/dist/tools/spec.d.ts +18 -0
  57. package/dist/tools/spec.js +14 -0
  58. package/dist/tools/spec.js.map +1 -0
  59. package/dist/tools/targets.d.ts +13 -0
  60. package/dist/tools/targets.js +15 -0
  61. package/dist/tools/targets.js.map +1 -0
  62. package/package.json +51 -0
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # vela-mcp
2
+
3
+ **Vela MCP** is the MCP server engine for AI-driven dynamic testing of Electron apps.
4
+
5
+ It exposes 20 tools over the [Model Context Protocol](https://modelcontextprotocol.io), letting an AI agent (Claude Code, Codex, etc.) launch and drive an Electron application, inspect IPC channels, evaluate code in any context, capture screenshots, set breakpoints, record console/network traffic, and save/run re-runnable specs — all without modifying the app's production code.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install -g vela-mcp
11
+ # or use npx / a local path
12
+ ```
13
+
14
+ ## Registering with Claude Code
15
+
16
+ Add to `.mcp.json` in your project root (or `~/.claude/mcp.json` globally):
17
+
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "vela": {
22
+ "command": "node",
23
+ "args": ["<absolute-path-to>/node_modules/vela-mcp/dist/index.js"]
24
+ }
25
+ }
26
+ }
27
+ ```
28
+
29
+ Or if installed globally / via `npx`:
30
+
31
+ ```json
32
+ {
33
+ "mcpServers": {
34
+ "vela": {
35
+ "command": "npx",
36
+ "args": ["vela-mcp"]
37
+ }
38
+ }
39
+ }
40
+ ```
41
+
42
+ ## Registering with Codex
43
+
44
+ Add the same `command`/`args` block under the `tools.mcp` key in your Codex config.
45
+
46
+ ## Tools (20 total)
47
+
48
+ ### Launch & Targets
49
+ | Tool | Description |
50
+ |------|-------------|
51
+ | `vela_launch` | Launch an Electron app and attach CDP |
52
+ | `vela_list_targets` | List open renderer/webview/BrowserView targets |
53
+ | `vela_attach_target` | Attach to a specific target |
54
+ | `vela_screenshot` | Capture a screenshot of the current target |
55
+
56
+ ### Eval & IPC
57
+ | Tool | Description |
58
+ |------|-------------|
59
+ | `vela_eval` | Evaluate JS in the currently attached renderer |
60
+ | `vela_ipc_list` | List registered IPC channels (requires vela-agent) |
61
+ | `vela_ipc_invoke` | Invoke an IPC channel and return the result |
62
+ | `vela_main_eval` | Evaluate JS in the main process (requires vela-agent) |
63
+
64
+ ### Debugger
65
+ | Tool | Description |
66
+ |------|-------------|
67
+ | `vela_debug_attach` | Attach the debugger to a target |
68
+ | `vela_set_breakpoint` | Set a breakpoint by URL + line number |
69
+ | `vela_wait_paused` | Wait until execution hits a breakpoint |
70
+ | `vela_eval_frame` | Evaluate an expression in the paused call frame |
71
+ | `vela_step` | Step over / in / out |
72
+ | `vela_resume` | Resume execution |
73
+ | `vela_debug_detach` | Detach the debugger |
74
+
75
+ ### Specs
76
+ | Tool | Description |
77
+ |------|-------------|
78
+ | `vela_spec_save` | Save a recorded interaction as a re-runnable spec |
79
+ | `vela_spec_run` | Run a saved spec and return results |
80
+ | `vela_spec_list` | List all saved specs |
81
+
82
+ ### Console & Network Capture
83
+ | Tool | Description |
84
+ |------|-------------|
85
+ | `vela_capture_start` | Start capturing console logs and/or network requests |
86
+ | `vela_capture_read` | Read captured events so far |
87
+ | `vela_capture_stop` | Stop capturing and return final buffer |
88
+
89
+ ## More
90
+
91
+ See the [repo](https://github.com/OWNER/vela) for the full playbook, Claude Code skill, and sample app.
@@ -0,0 +1,31 @@
1
+ /** A connection to a launched-or-attached app that the tool layer drives. */
2
+ export interface LaunchHandle {
3
+ cdpPort: number;
4
+ inspectPort?: number;
5
+ probe?: {
6
+ port: number;
7
+ token: string;
8
+ };
9
+ /** Tear down whatever this adapter started (no-op for attach mode). */
10
+ dispose: () => Promise<void>;
11
+ }
12
+ export interface AdapterLaunchOptions {
13
+ projectPath?: string;
14
+ port?: number;
15
+ }
16
+ /** Abstracts HOW an app is started/connected, so the tool layer is platform-agnostic. */
17
+ export interface TargetAdapter {
18
+ readonly kind: string;
19
+ launch(opts: AdapterLaunchOptions): Promise<LaunchHandle>;
20
+ }
21
+ /** Launches an Electron app in dev with CDP + inspector + probe detection. */
22
+ export declare class ElectronAdapter implements TargetAdapter {
23
+ readonly kind = "electron";
24
+ launch(opts: AdapterLaunchOptions): Promise<LaunchHandle>;
25
+ }
26
+ /** Attaches to an already-running CDP endpoint (any Chromium/Electron started with
27
+ * --remote-debugging-port). Vela did not start it, so dispose does NOT kill it. */
28
+ export declare class AttachAdapter implements TargetAdapter {
29
+ readonly kind = "attach";
30
+ launch(opts: AdapterLaunchOptions): Promise<LaunchHandle>;
31
+ }
@@ -0,0 +1,32 @@
1
+ import { launchElectron, killAndWait } from "./launcher.js";
2
+ /** Launches an Electron app in dev with CDP + inspector + probe detection. */
3
+ export class ElectronAdapter {
4
+ kind = "electron";
5
+ async launch(opts) {
6
+ if (!opts.projectPath)
7
+ throw new Error("ElectronAdapter requires a projectPath");
8
+ const app = await launchElectron(opts.projectPath);
9
+ return {
10
+ cdpPort: app.port,
11
+ inspectPort: app.inspectPort,
12
+ probe: app.probe,
13
+ dispose: () => killAndWait(app.child)
14
+ };
15
+ }
16
+ }
17
+ /** Attaches to an already-running CDP endpoint (any Chromium/Electron started with
18
+ * --remote-debugging-port). Vela did not start it, so dispose does NOT kill it. */
19
+ export class AttachAdapter {
20
+ kind = "attach";
21
+ async launch(opts) {
22
+ if (!opts.port)
23
+ throw new Error("attach mode requires a port (the target's --remote-debugging-port)");
24
+ return {
25
+ cdpPort: opts.port,
26
+ dispose: async () => {
27
+ // intentionally a no-op: we don't own this process
28
+ }
29
+ };
30
+ }
31
+ }
32
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAsB5D,8EAA8E;AAC9E,MAAM,OAAO,eAAe;IACjB,IAAI,GAAG,UAAU,CAAC;IAC3B,KAAK,CAAC,MAAM,CAAC,IAA0B;QACrC,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QACjF,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACnD,OAAO;YACL,OAAO,EAAE,GAAG,CAAC,IAAI;YACjB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;SACtC,CAAC;IACJ,CAAC;CACF;AAED;oFACoF;AACpF,MAAM,OAAO,aAAa;IACf,IAAI,GAAG,QAAQ,CAAC;IACzB,KAAK,CAAC,MAAM,CAAC,IAA0B;QACrC,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;QACtG,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,IAAI;YAClB,OAAO,EAAE,KAAK,IAAI,EAAE;gBAClB,mDAAmD;YACrD,CAAC;SACF,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,41 @@
1
+ export interface ConsoleLine {
2
+ level: string;
3
+ text: string;
4
+ }
5
+ export interface NetEntry {
6
+ requestId: string;
7
+ method: string;
8
+ url: string;
9
+ status?: number;
10
+ mimeType?: string;
11
+ }
12
+ export interface CaptureSnapshot {
13
+ console: ConsoleLine[];
14
+ network: NetEntry[];
15
+ }
16
+ /**
17
+ * A long-lived CDP connection buffering console + network events for one target.
18
+ * Buffers are for time-windowed capture; they are capped to avoid unbounded growth.
19
+ */
20
+ export declare class CaptureSession {
21
+ private readonly client;
22
+ private consoleLines;
23
+ private net;
24
+ private constructor();
25
+ private pushConsole;
26
+ static start(wsUrl: string, opts: {
27
+ console?: boolean;
28
+ network?: boolean;
29
+ }): Promise<CaptureSession>;
30
+ snapshot(): CaptureSnapshot;
31
+ stop(): Promise<void>;
32
+ }
33
+ /** Active capture sessions keyed by `${sessionId}:${target}`. */
34
+ export declare class CaptureRegistry {
35
+ private sessions;
36
+ has(key: string): boolean;
37
+ set(key: string, s: CaptureSession): void;
38
+ get(key: string): CaptureSession;
39
+ remove(key: string): Promise<void>;
40
+ removeAll(): Promise<void>;
41
+ }
@@ -0,0 +1,96 @@
1
+ import CDP from "chrome-remote-interface";
2
+ /**
3
+ * A long-lived CDP connection buffering console + network events for one target.
4
+ * Buffers are for time-windowed capture; they are capped to avoid unbounded growth.
5
+ */
6
+ export class CaptureSession {
7
+ client;
8
+ consoleLines = [];
9
+ net = new Map();
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ constructor(client) {
12
+ this.client = client;
13
+ }
14
+ pushConsole(line) {
15
+ this.consoleLines.push(line);
16
+ // Time-windowed capture; cap to avoid unbounded growth on very chatty pages.
17
+ if (this.consoleLines.length > 5000)
18
+ this.consoleLines.shift();
19
+ }
20
+ static async start(wsUrl, opts) {
21
+ const client = await CDP({ target: wsUrl });
22
+ try {
23
+ const s = new CaptureSession(client);
24
+ if (opts.console !== false) {
25
+ await client.Runtime.enable();
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
+ client.Runtime.consoleAPICalled((e) => {
28
+ const text = (e.args ?? [])
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ .map((a) => (a.value !== undefined ? String(a.value) : a.description ?? ""))
31
+ .join(" ");
32
+ s.pushConsole({ level: e.type ?? "log", text });
33
+ });
34
+ await client.Log.enable();
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ client.Log.entryAdded((e) => {
37
+ s.pushConsole({ level: e.entry?.level ?? "log", text: e.entry?.text ?? "" });
38
+ });
39
+ }
40
+ if (opts.network) {
41
+ await client.Network.enable();
42
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
+ client.Network.requestWillBeSent((e) => {
44
+ s.net.set(e.requestId, { requestId: e.requestId, method: e.request?.method ?? "GET", url: e.request?.url ?? "" });
45
+ });
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
+ client.Network.responseReceived((e) => {
48
+ const r = s.net.get(e.requestId);
49
+ if (r) {
50
+ r.status = e.response?.status;
51
+ r.mimeType = e.response?.mimeType;
52
+ }
53
+ });
54
+ }
55
+ return s;
56
+ }
57
+ catch (e) {
58
+ await client.close();
59
+ throw e;
60
+ }
61
+ }
62
+ snapshot() {
63
+ return { console: [...this.consoleLines], network: [...this.net.values()] };
64
+ }
65
+ async stop() {
66
+ await this.client.close();
67
+ }
68
+ }
69
+ /** Active capture sessions keyed by `${sessionId}:${target}`. */
70
+ export class CaptureRegistry {
71
+ sessions = new Map();
72
+ has(key) {
73
+ return this.sessions.has(key);
74
+ }
75
+ set(key, s) {
76
+ this.sessions.set(key, s);
77
+ }
78
+ get(key) {
79
+ const s = this.sessions.get(key);
80
+ if (!s)
81
+ throw new Error(`No capture for ${key}. Call vela_capture_start first.`);
82
+ return s;
83
+ }
84
+ async remove(key) {
85
+ const s = this.sessions.get(key);
86
+ if (s) {
87
+ await s.stop();
88
+ this.sessions.delete(key);
89
+ }
90
+ }
91
+ async removeAll() {
92
+ for (const k of [...this.sessions.keys()])
93
+ await this.remove(k);
94
+ }
95
+ }
96
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"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"}
package/dist/cdp.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { type CdpTarget, type NamedTarget } from "./targets.js";
2
+ export interface EvalResult {
3
+ value: unknown;
4
+ }
5
+ export declare class CdpClient {
6
+ private readonly port;
7
+ constructor(port: number);
8
+ /** List all CDP targets exposed on the debug port. */
9
+ listTargets(): Promise<CdpTarget[]>;
10
+ /** List targets with stable logical names. */
11
+ listNamed(): Promise<NamedTarget[]>;
12
+ /** Resolve a logical target name to its descriptor. Defaults to "renderer". */
13
+ private resolveTarget;
14
+ /** Evaluate an expression in the chosen target's main world (default: the renderer). */
15
+ evaluate(expression: string, awaitPromise?: boolean, target?: string): Promise<EvalResult>;
16
+ /** Capture a PNG screenshot of the chosen target (default: the renderer), as base64. */
17
+ screenshotBase64(target?: string): Promise<string>;
18
+ }
package/dist/cdp.js ADDED
@@ -0,0 +1,60 @@
1
+ import CDP from "chrome-remote-interface";
2
+ import { nameTargets } from "./targets.js";
3
+ export class CdpClient {
4
+ port;
5
+ constructor(port) {
6
+ this.port = port;
7
+ }
8
+ /** List all CDP targets exposed on the debug port. */
9
+ async listTargets() {
10
+ const raw = await CDP.List({ host: "127.0.0.1", port: this.port });
11
+ return raw;
12
+ }
13
+ /** List targets with stable logical names. */
14
+ async listNamed() {
15
+ return nameTargets(await this.listTargets());
16
+ }
17
+ /** Resolve a logical target name to its descriptor. Defaults to "renderer". */
18
+ async resolveTarget(name = "renderer") {
19
+ const named = await this.listNamed();
20
+ const found = named.find((t) => t.name === name);
21
+ if (!found) {
22
+ const available = named.map((t) => t.name).join(", ") || "(none)";
23
+ throw new Error(`Unknown target "${name}". Available: ${available}`);
24
+ }
25
+ return found;
26
+ }
27
+ /** Evaluate an expression in the chosen target's main world (default: the renderer). */
28
+ async evaluate(expression, awaitPromise = true, target = "renderer") {
29
+ const t = await this.resolveTarget(target);
30
+ const client = await CDP({ host: "127.0.0.1", port: this.port, target: t.webSocketDebuggerUrl });
31
+ try {
32
+ await client.Runtime.enable();
33
+ const res = await client.Runtime.evaluate({ expression, awaitPromise, returnByValue: true });
34
+ if (res.exceptionDetails) {
35
+ const d = res.exceptionDetails;
36
+ // exception.description holds the real message; text is usually just "Uncaught".
37
+ const message = d.exception?.description ?? d.text ?? "Unknown evaluation error";
38
+ throw new Error(`Eval exception: ${message}`);
39
+ }
40
+ return { value: res.result.value };
41
+ }
42
+ finally {
43
+ await client.close();
44
+ }
45
+ }
46
+ /** Capture a PNG screenshot of the chosen target (default: the renderer), as base64. */
47
+ async screenshotBase64(target = "renderer") {
48
+ const t = await this.resolveTarget(target);
49
+ const client = await CDP({ host: "127.0.0.1", port: this.port, target: t.webSocketDebuggerUrl });
50
+ try {
51
+ await client.Page.enable();
52
+ const { data } = await client.Page.captureScreenshot({ format: "png" });
53
+ return data;
54
+ }
55
+ finally {
56
+ await client.close();
57
+ }
58
+ }
59
+ }
60
+ //# sourceMappingURL=cdp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp.js","sourceRoot":"","sources":["../src/cdp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ export interface PausedFrame {
2
+ index: number;
3
+ functionName: string;
4
+ url: string;
5
+ line: number;
6
+ }
7
+ export interface PausedInfo {
8
+ reason: string;
9
+ frames: PausedFrame[];
10
+ }
11
+ /** A long-lived debugger attached to one target, tracking pause state. */
12
+ export declare class DebugSession {
13
+ private readonly client;
14
+ private paused;
15
+ private waiters;
16
+ private constructor();
17
+ static attachToWs(wsUrl: string): Promise<DebugSession>;
18
+ /** Set a breakpoint by a URL regex and a 1-based line number. */
19
+ setBreakpoint(urlRegex: string, line: number): Promise<{
20
+ breakpointId: string;
21
+ locations: number;
22
+ }>;
23
+ isPaused(): boolean;
24
+ /** Resolve when paused (now or on the next pause), or reject on timeout. */
25
+ waitPaused(timeoutMs?: number): Promise<PausedInfo>;
26
+ /** Evaluate an expression in a paused call frame (0-based index). */
27
+ evalOnFrame(frameIndex: number, expression: string): Promise<unknown>;
28
+ resume(): Promise<void>;
29
+ step(kind: "over" | "into" | "out"): Promise<void>;
30
+ detach(): Promise<void>;
31
+ }
32
+ /** Active debug sessions keyed by `${sessionId}:${target}`. */
33
+ export declare class DebugRegistry {
34
+ private sessions;
35
+ has(key: string): boolean;
36
+ set(key: string, s: DebugSession): void;
37
+ get(key: string): DebugSession;
38
+ remove(key: string): Promise<void>;
39
+ removeAll(): Promise<void>;
40
+ }
package/dist/debug.js ADDED
@@ -0,0 +1,133 @@
1
+ import CDP from "chrome-remote-interface";
2
+ /** A long-lived debugger attached to one target, tracking pause state. */
3
+ export class DebugSession {
4
+ client;
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ paused = null;
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ waiters = [];
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ constructor(client) {
11
+ this.client = client;
12
+ }
13
+ static async attachToWs(wsUrl) {
14
+ const client = await CDP({ target: wsUrl });
15
+ const s = new DebugSession(client);
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ client.Debugger.paused((params) => {
18
+ s.paused = params;
19
+ const ws = s.waiters;
20
+ s.waiters = [];
21
+ for (const w of ws)
22
+ w(params);
23
+ });
24
+ client.Debugger.resumed(() => {
25
+ s.paused = null;
26
+ });
27
+ await client.Debugger.enable();
28
+ await client.Runtime.enable();
29
+ return s;
30
+ }
31
+ /** Set a breakpoint by a URL regex and a 1-based line number. */
32
+ async setBreakpoint(urlRegex, line) {
33
+ const res = await this.client.Debugger.setBreakpointByUrl({ urlRegex, lineNumber: line - 1 });
34
+ return { breakpointId: res.breakpointId, locations: res.locations.length };
35
+ }
36
+ isPaused() {
37
+ return this.paused !== null;
38
+ }
39
+ /** Resolve when paused (now or on the next pause), or reject on timeout. */
40
+ async waitPaused(timeoutMs = 10_000) {
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ const params = this.paused ??
43
+ (await new Promise((resolve, reject) => {
44
+ const t = setTimeout(() => reject(new Error("Timed out waiting for a pause (breakpoint not hit)")), timeoutMs);
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+ this.waiters.push((p) => {
47
+ clearTimeout(t);
48
+ resolve(p);
49
+ });
50
+ }));
51
+ return {
52
+ reason: params.reason,
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ frames: params.callFrames.map((f, i) => ({
55
+ index: i,
56
+ functionName: f.functionName || "(anonymous)",
57
+ url: f.url,
58
+ line: (f.location?.lineNumber ?? 0) + 1
59
+ }))
60
+ };
61
+ }
62
+ /** Evaluate an expression in a paused call frame (0-based index). */
63
+ async evalOnFrame(frameIndex, expression) {
64
+ if (!this.paused)
65
+ throw new Error("Not paused — call vela_debug_wait_paused first");
66
+ const frame = this.paused.callFrames[frameIndex];
67
+ if (!frame)
68
+ throw new Error(`No call frame #${frameIndex}`);
69
+ const res = await this.client.Debugger.evaluateOnCallFrame({
70
+ callFrameId: frame.callFrameId,
71
+ expression,
72
+ returnByValue: true
73
+ });
74
+ if (res.exceptionDetails) {
75
+ throw new Error(`Frame eval exception: ${res.exceptionDetails.exception?.description ?? res.exceptionDetails.text}`);
76
+ }
77
+ return res.result.value;
78
+ }
79
+ async resume() {
80
+ await this.client.Debugger.resume();
81
+ }
82
+ async step(kind) {
83
+ if (kind === "over")
84
+ await this.client.Debugger.stepOver();
85
+ else if (kind === "into")
86
+ await this.client.Debugger.stepInto();
87
+ else
88
+ await this.client.Debugger.stepOut();
89
+ }
90
+ async detach() {
91
+ try {
92
+ await this.client.Debugger.resume();
93
+ }
94
+ catch {
95
+ // already running / not paused
96
+ }
97
+ try {
98
+ await this.client.Debugger.disable();
99
+ }
100
+ catch {
101
+ // ignore
102
+ }
103
+ await this.client.close();
104
+ }
105
+ }
106
+ /** Active debug sessions keyed by `${sessionId}:${target}`. */
107
+ export class DebugRegistry {
108
+ sessions = new Map();
109
+ has(key) {
110
+ return this.sessions.has(key);
111
+ }
112
+ set(key, s) {
113
+ this.sessions.set(key, s);
114
+ }
115
+ get(key) {
116
+ const s = this.sessions.get(key);
117
+ if (!s)
118
+ throw new Error(`No debug session for ${key}. Call vela_debug_attach first.`);
119
+ return s;
120
+ }
121
+ async remove(key) {
122
+ const s = this.sessions.get(key);
123
+ if (s) {
124
+ await s.detach();
125
+ this.sessions.delete(key);
126
+ }
127
+ }
128
+ async removeAll() {
129
+ for (const k of [...this.sessions.keys()])
130
+ await this.remove(k);
131
+ }
132
+ }
133
+ //# sourceMappingURL=debug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,yBAAyB,CAAC;AAa1C,0EAA0E;AAC1E,MAAM,OAAO,YAAY;IAOc;IANrC,8DAA8D;IACtD,MAAM,GAAQ,IAAI,CAAC;IAC3B,8DAA8D;IACtD,OAAO,GAA4B,EAAE,CAAC;IAE9C,8DAA8D;IAC9D,YAAqC,MAAW;QAAX,WAAM,GAAN,MAAM,CAAK;IAAG,CAAC;IAEpD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,KAAa;QACnC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;QACnC,8DAA8D;QAC9D,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAW,EAAE,EAAE;YACrC,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC;YAClB,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC;YACrB,CAAC,CAAC,OAAO,GAAG,EAAE,CAAC;YACf,KAAK,MAAM,CAAC,IAAI,EAAE;gBAAE,CAAC,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE;YAC3B,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC;QAClB,CAAC,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QAC9B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,iEAAiE;IACjE,KAAK,CAAC,aAAa,CAAC,QAAgB,EAAE,IAAY;QAChD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9F,OAAO,EAAE,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;IAC7E,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC;IAC9B,CAAC;IAED,4EAA4E;IAC5E,KAAK,CAAC,UAAU,CAAC,SAAS,GAAG,MAAM;QACjC,8DAA8D;QAC9D,MAAM,MAAM,GACV,IAAI,CAAC,MAAM;YACX,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACrC,MAAM,CAAC,GAAG,UAAU,CAClB,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC,EAC7E,SAAS,CACV,CAAC;gBACF,8DAA8D;gBAC9D,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE;oBAC3B,YAAY,CAAC,CAAC,CAAC,CAAC;oBAChB,OAAO,CAAC,CAAC,CAAC,CAAC;gBACb,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC,CAAC;QACN,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,8DAA8D;YAC9D,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC;gBACpD,KAAK,EAAE,CAAC;gBACR,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,aAAa;gBAC7C,GAAG,EAAE,CAAC,CAAC,GAAG;gBACV,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,UAAU,IAAI,CAAC,CAAC,GAAG,CAAC;aACxC,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC;IAED,qEAAqE;IACrE,KAAK,CAAC,WAAW,CAAC,UAAkB,EAAE,UAAkB;QACtD,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpF,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,UAAU,EAAE,CAAC,CAAC;QAC5D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YACzD,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,UAAU;YACV,aAAa,EAAE,IAAI;SACpB,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,gBAAgB,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,WAAW,IAAI,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC;QACvH,CAAC;QACD,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;IACtC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,IAA6B;QACtC,IAAI,IAAI,KAAK,MAAM;YAAE,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;aACtD,IAAI,IAAI,KAAK,MAAM;YAAE,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;;YAC3D,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,MAAM;QACV,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,+BAA+B;QACjC,CAAC;QACD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;CACF;AAED,+DAA+D;AAC/D,MAAM,OAAO,aAAa;IAChB,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAC;IACnD,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IACD,GAAG,CAAC,GAAW,EAAE,CAAe;QAC9B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAC5B,CAAC;IACD,GAAG,CAAC,GAAW;QACb,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,iCAAiC,CAAC,CAAC;QACtF,OAAO,CAAC,CAAC;IACX,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,EAAE,CAAC;YACN,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,KAAK,CAAC,SAAS;QACb,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YAAE,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};