agent-dbg 0.1.0 → 0.1.2
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.
- package/.claude/settings.local.json +7 -5
- package/.claude/skills/agent-dbg/SKILL.md +116 -0
- package/.claude/skills/agent-dbg/references/commands.md +173 -0
- package/.vscode/launch.json +19 -0
- package/CLAUDE.md +2 -2
- package/PROGRESS.md +91 -91
- package/README.md +45 -17
- package/{ndbg-spec.md → SPEC.md} +152 -152
- package/dist/main.js +12500 -0
- package/package.json +3 -3
- package/src/cdp/client.ts +18 -4
- package/src/cdp/logger.ts +69 -0
- package/src/cli/parser.ts +54 -43
- package/src/commands/attach.ts +2 -2
- package/src/commands/blackbox-ls.ts +1 -1
- package/src/commands/blackbox-rm.ts +3 -3
- package/src/commands/blackbox.ts +2 -2
- package/src/commands/break-ls.ts +3 -2
- package/src/commands/break-rm.ts +2 -2
- package/src/commands/break-toggle.ts +2 -2
- package/src/commands/break.ts +46 -17
- package/src/commands/breakable.ts +2 -2
- package/src/commands/catch.ts +2 -2
- package/src/commands/console.ts +1 -1
- package/src/commands/continue.ts +5 -18
- package/src/commands/eval.ts +2 -2
- package/src/commands/exceptions.ts +1 -1
- package/src/commands/hotpatch.ts +2 -2
- package/src/commands/launch.ts +7 -11
- package/src/commands/logpoint.ts +7 -6
- package/src/commands/logs.ts +116 -0
- package/src/commands/pause.ts +5 -18
- package/src/commands/print-state.ts +85 -0
- package/src/commands/props.ts +2 -2
- package/src/commands/restart-frame.ts +1 -1
- package/src/commands/restart.ts +42 -0
- package/src/commands/run-to.ts +7 -20
- package/src/commands/scripts.ts +1 -1
- package/src/commands/search.ts +4 -3
- package/src/commands/set-return.ts +2 -2
- package/src/commands/set.ts +3 -3
- package/src/commands/source.ts +3 -2
- package/src/commands/sourcemap.ts +1 -1
- package/src/commands/stack.ts +1 -1
- package/src/commands/state.ts +3 -73
- package/src/commands/status.ts +3 -2
- package/src/commands/step.ts +5 -18
- package/src/commands/vars.ts +3 -1
- package/src/daemon/entry.ts +16 -6
- package/src/daemon/paths.ts +6 -2
- package/src/daemon/server.ts +1 -1
- package/src/daemon/session-breakpoints.ts +7 -2
- package/src/daemon/session-inspection.ts +6 -10
- package/src/daemon/session-state.ts +6 -5
- package/src/daemon/session.ts +23 -3
- package/src/formatter/logs.ts +110 -0
- package/src/formatter/path.ts +27 -0
- package/src/formatter/stack.ts +5 -2
- package/src/formatter/variables.ts +48 -1
- package/src/main.ts +2 -0
- package/src/protocol/messages.ts +3 -0
- package/tests/fixtures/async-app.js +1 -1
- package/tests/fixtures/error-app.js +1 -1
- package/tests/fixtures/simple-app.js +1 -1
- package/tests/fixtures/ts-app/src/app.ts +4 -0
- package/tests/integration/state.test.ts +7 -7
- package/tests/unit/daemon.test.ts +1 -1
- package/tests/unit/formatter.test.ts +8 -4
- package/.bin/ndbg +0 -0
- package/.claude/skills/ndbg-debugger/ndbg-debugger/SKILL.md +0 -116
- package/.claude/skills/ndbg-debugger/ndbg-debugger/references/commands.md +0 -173
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-dbg",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Node.js Debugger CLI for AI Agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"
|
|
7
|
+
"agent-dbg": "./dist/main.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"dev": "bun run src/main.ts",
|
|
11
|
-
"build": "bun build src/main.ts --
|
|
11
|
+
"build": "bun build src/main.ts --outdir dist --target=bun",
|
|
12
12
|
"test": "bun test",
|
|
13
13
|
"lint": "biome check .",
|
|
14
14
|
"format": "biome check --write .",
|
package/src/cdp/client.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ProtocolMapping } from "devtools-protocol/types/protocol-mapping.js";
|
|
2
|
+
import type { CdpLogger } from "./logger.ts";
|
|
2
3
|
import type { CdpEvent, CdpRequest, CdpResponse } from "./types.ts";
|
|
3
4
|
|
|
4
5
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
@@ -21,20 +22,24 @@ export class CdpClient {
|
|
|
21
22
|
private pending = new Map<number, PendingRequest>();
|
|
22
23
|
private listeners = new Map<string, Set<AnyHandler>>();
|
|
23
24
|
private isConnected = false;
|
|
25
|
+
private logger: CdpLogger | null;
|
|
26
|
+
/** Map request id → method name for response logging */
|
|
27
|
+
private sentMethods = new Map<number, string>();
|
|
24
28
|
|
|
25
|
-
private constructor(ws: WebSocket) {
|
|
29
|
+
private constructor(ws: WebSocket, logger?: CdpLogger) {
|
|
26
30
|
this.ws = ws;
|
|
31
|
+
this.logger = logger ?? null;
|
|
27
32
|
this.isConnected = true;
|
|
28
33
|
this.setupHandlers();
|
|
29
34
|
}
|
|
30
35
|
|
|
31
|
-
static async connect(wsUrl: string): Promise<CdpClient> {
|
|
36
|
+
static async connect(wsUrl: string, logger?: CdpLogger): Promise<CdpClient> {
|
|
32
37
|
return new Promise<CdpClient>((resolve, reject) => {
|
|
33
38
|
const ws = new WebSocket(wsUrl);
|
|
34
39
|
|
|
35
40
|
const onOpen = () => {
|
|
36
41
|
ws.removeEventListener("error", onError);
|
|
37
|
-
const client = new CdpClient(ws);
|
|
42
|
+
const client = new CdpClient(ws, logger);
|
|
38
43
|
resolve(client);
|
|
39
44
|
};
|
|
40
45
|
|
|
@@ -53,7 +58,6 @@ export class CdpClient {
|
|
|
53
58
|
method: T,
|
|
54
59
|
...params: ProtocolMapping.Commands[T]["paramsType"]
|
|
55
60
|
): Promise<ProtocolMapping.Commands[T]["returnType"]>;
|
|
56
|
-
async send(method: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
57
61
|
async send(method: string, ...args: unknown[]): Promise<unknown> {
|
|
58
62
|
if (!this.isConnected) {
|
|
59
63
|
throw new Error("CDP client is not connected");
|
|
@@ -66,9 +70,13 @@ export class CdpClient {
|
|
|
66
70
|
request.params = params;
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
this.sentMethods.set(id, method);
|
|
74
|
+
this.logger?.logSend(id, method, params as Record<string, unknown> | undefined);
|
|
75
|
+
|
|
69
76
|
return new Promise<unknown>((resolve, reject) => {
|
|
70
77
|
const timer = setTimeout(() => {
|
|
71
78
|
this.pending.delete(id);
|
|
79
|
+
this.sentMethods.delete(id);
|
|
72
80
|
reject(new Error(`CDP request timed out: ${method} (id=${id})`));
|
|
73
81
|
}, DEFAULT_TIMEOUT_MS);
|
|
74
82
|
|
|
@@ -173,6 +181,10 @@ export class CdpClient {
|
|
|
173
181
|
|
|
174
182
|
if ("id" in parsed && typeof parsed.id === "number") {
|
|
175
183
|
const response = parsed as CdpResponse;
|
|
184
|
+
const method = this.sentMethods.get(response.id) ?? "unknown";
|
|
185
|
+
this.sentMethods.delete(response.id);
|
|
186
|
+
this.logger?.logResponse(response.id, method, response.result, response.error);
|
|
187
|
+
|
|
176
188
|
const pending = this.pending.get(response.id);
|
|
177
189
|
if (!pending) {
|
|
178
190
|
return;
|
|
@@ -187,6 +199,8 @@ export class CdpClient {
|
|
|
187
199
|
}
|
|
188
200
|
} else if ("method" in parsed) {
|
|
189
201
|
const event = parsed as CdpEvent;
|
|
202
|
+
this.logger?.logEvent(event.method, event.params);
|
|
203
|
+
|
|
190
204
|
const handlers = this.listeners.get(event.method);
|
|
191
205
|
if (handlers) {
|
|
192
206
|
for (const handler of handlers) {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { appendFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
export interface CdpLogEntry {
|
|
4
|
+
ts: number;
|
|
5
|
+
dir: "send" | "recv" | "event";
|
|
6
|
+
method: string;
|
|
7
|
+
id?: number;
|
|
8
|
+
params?: Record<string, unknown>;
|
|
9
|
+
result?: unknown;
|
|
10
|
+
error?: { code: number; message: string };
|
|
11
|
+
ms?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class CdpLogger {
|
|
15
|
+
private logPath: string;
|
|
16
|
+
/** Map request id → { method, sentAt } for latency tracking */
|
|
17
|
+
private pending = new Map<number, { method: string; sentAt: number }>();
|
|
18
|
+
|
|
19
|
+
constructor(logPath: string) {
|
|
20
|
+
this.logPath = logPath;
|
|
21
|
+
// Truncate log file on creation (new session = fresh log)
|
|
22
|
+
writeFileSync(logPath, "");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
logSend(id: number, method: string, params?: Record<string, unknown>): void {
|
|
26
|
+
this.pending.set(id, { method, sentAt: Date.now() });
|
|
27
|
+
const entry: CdpLogEntry = { ts: Date.now(), dir: "send", method, id };
|
|
28
|
+
if (params !== undefined) {
|
|
29
|
+
entry.params = params;
|
|
30
|
+
}
|
|
31
|
+
this.append(entry);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
logResponse(
|
|
35
|
+
id: number,
|
|
36
|
+
method: string,
|
|
37
|
+
result?: unknown,
|
|
38
|
+
error?: { code: number; message: string },
|
|
39
|
+
): void {
|
|
40
|
+
const pending = this.pending.get(id);
|
|
41
|
+
const ms = pending ? Date.now() - pending.sentAt : undefined;
|
|
42
|
+
if (pending) this.pending.delete(id);
|
|
43
|
+
|
|
44
|
+
const entry: CdpLogEntry = { ts: Date.now(), dir: "recv", method, id };
|
|
45
|
+
if (ms !== undefined) entry.ms = ms;
|
|
46
|
+
if (error) {
|
|
47
|
+
entry.error = error;
|
|
48
|
+
} else if (result !== undefined) {
|
|
49
|
+
entry.result = result;
|
|
50
|
+
}
|
|
51
|
+
this.append(entry);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
logEvent(method: string, params?: unknown): void {
|
|
55
|
+
const entry: CdpLogEntry = { ts: Date.now(), dir: "event", method };
|
|
56
|
+
if (params !== undefined) {
|
|
57
|
+
entry.params = params as Record<string, unknown>;
|
|
58
|
+
}
|
|
59
|
+
this.append(entry);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
clear(): void {
|
|
63
|
+
writeFileSync(this.logPath, "");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private append(entry: CdpLogEntry): void {
|
|
67
|
+
appendFileSync(this.logPath, `${JSON.stringify(entry)}\n`);
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/cli/parser.ts
CHANGED
|
@@ -98,6 +98,7 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
98
98
|
s: "stack",
|
|
99
99
|
b: "breakpoints",
|
|
100
100
|
c: "code",
|
|
101
|
+
f: "follow",
|
|
101
102
|
};
|
|
102
103
|
const mapped = shortMap[key];
|
|
103
104
|
if (mapped) {
|
|
@@ -141,7 +142,7 @@ export async function run(args: ParsedArgs): Promise<number> {
|
|
|
141
142
|
const handler = registry.get(args.command);
|
|
142
143
|
if (!handler) {
|
|
143
144
|
console.error(`✗ Unknown command: ${args.command}`);
|
|
144
|
-
console.error(" → Try:
|
|
145
|
+
console.error(" → Try: agent-dbg --help");
|
|
145
146
|
return 1;
|
|
146
147
|
}
|
|
147
148
|
|
|
@@ -155,9 +156,9 @@ export async function run(args: ParsedArgs): Promise<number> {
|
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
function printHelp(): void {
|
|
158
|
-
console.log(`
|
|
159
|
+
console.log(`agent-dbg — Node.js debugger CLI for AI agents
|
|
159
160
|
|
|
160
|
-
Usage:
|
|
161
|
+
Usage: agent-dbg <command> [options]
|
|
161
162
|
|
|
162
163
|
Session:
|
|
163
164
|
launch [--brk] <command...> Start + attach debugger
|
|
@@ -218,6 +219,10 @@ Source Maps:
|
|
|
218
219
|
sourcemap [file] Show source map info
|
|
219
220
|
sourcemap --disable Disable resolution globally
|
|
220
221
|
|
|
222
|
+
Diagnostics:
|
|
223
|
+
logs [-f|--follow] Show CDP protocol log
|
|
224
|
+
[--limit N] [--domain <name>] [--clear]
|
|
225
|
+
|
|
221
226
|
Global flags:
|
|
222
227
|
--session NAME Target session (default: "default")
|
|
223
228
|
--json JSON output
|
|
@@ -227,61 +232,67 @@ Global flags:
|
|
|
227
232
|
}
|
|
228
233
|
|
|
229
234
|
function printHelpAgent(): void {
|
|
230
|
-
console.log(`
|
|
235
|
+
console.log(`agent-dbg — Node.js debugger CLI for AI agents
|
|
231
236
|
|
|
232
237
|
CORE LOOP:
|
|
233
|
-
1.
|
|
234
|
-
2.
|
|
235
|
-
3.
|
|
236
|
-
4. Inspect:
|
|
237
|
-
5. Mutate/fix:
|
|
238
|
+
1. agent-dbg launch --brk "node app.js" → pauses at first line, returns state
|
|
239
|
+
2. agent-dbg break src/file.ts:42 → set breakpoint
|
|
240
|
+
3. agent-dbg continue → run to breakpoint, returns state
|
|
241
|
+
4. Inspect: agent-dbg vars, agent-dbg eval, agent-dbg props @v1
|
|
242
|
+
5. Mutate/fix: agent-dbg set @v1 value, agent-dbg hotpatch src/file.ts
|
|
238
243
|
6. Repeat from 3
|
|
239
244
|
|
|
240
245
|
REFS: Every output assigns @refs. Use them everywhere:
|
|
241
|
-
@v1..@vN variables |
|
|
242
|
-
@f0..@fN stack frames |
|
|
243
|
-
BP#1..N breakpoints |
|
|
246
|
+
@v1..@vN variables | agent-dbg props @v1, agent-dbg set @v2 true
|
|
247
|
+
@f0..@fN stack frames | agent-dbg eval --frame @f1
|
|
248
|
+
BP#1..N breakpoints | agent-dbg break-rm BP#1, agent-dbg break-toggle BP#1
|
|
244
249
|
|
|
245
250
|
EXECUTION (all return state automatically):
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
+
agent-dbg continue Resume to next breakpoint
|
|
252
|
+
agent-dbg step [over|into|out] Step one statement
|
|
253
|
+
agent-dbg run-to file:line Continue to location
|
|
254
|
+
agent-dbg pause Interrupt running process
|
|
255
|
+
agent-dbg restart-frame [@fN] Re-run frame from beginning
|
|
251
256
|
|
|
252
257
|
BREAKPOINTS:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
258
|
+
agent-dbg break file:line [--condition expr] [--hit-count N] [--continue]
|
|
259
|
+
agent-dbg break --pattern "regex":line
|
|
260
|
+
agent-dbg break-rm <BP#|all> Remove breakpoints
|
|
261
|
+
agent-dbg break-ls List breakpoints
|
|
262
|
+
agent-dbg break-toggle <BP#|all> Enable/disable breakpoints
|
|
263
|
+
agent-dbg breakable file:start-end Valid breakpoint locations
|
|
264
|
+
agent-dbg logpoint file:line "template \${var}" [--condition expr]
|
|
265
|
+
agent-dbg catch [all|uncaught|caught|none]
|
|
261
266
|
|
|
262
267
|
INSPECTION:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
268
|
+
agent-dbg state [-v|-s|-b|-c] [--depth N] [--lines N] [--frame @fN] [--all-scopes] [--compact] [--generated]
|
|
269
|
+
agent-dbg vars [name...] [--frame @fN] [--all-scopes]
|
|
270
|
+
agent-dbg stack [--async-depth N] [--generated]
|
|
271
|
+
agent-dbg eval <expr> [--frame @fN] [--silent] [--timeout MS] [--side-effect-free]
|
|
272
|
+
agent-dbg props @ref [--own] [--depth N] [--private] [--internal]
|
|
273
|
+
agent-dbg source [--lines N] [--file path] [--all] [--generated]
|
|
274
|
+
agent-dbg search "query" [--regex] [--case-sensitive] [--file id]
|
|
275
|
+
agent-dbg scripts [--filter pattern]
|
|
276
|
+
agent-dbg console [--since N] [--level type] [--clear]
|
|
277
|
+
agent-dbg exceptions [--since N]
|
|
273
278
|
|
|
274
279
|
MUTATION:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
280
|
+
agent-dbg set <@ref|name> <value> Change variable
|
|
281
|
+
agent-dbg set-return <value> Change return value (at return point)
|
|
282
|
+
agent-dbg hotpatch <file> [--dry-run] Live-edit code (no restart!)
|
|
278
283
|
|
|
279
284
|
BLACKBOXING:
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
285
|
+
agent-dbg blackbox <pattern...> Skip stepping into matching scripts
|
|
286
|
+
agent-dbg blackbox-ls List current patterns
|
|
287
|
+
agent-dbg blackbox-rm <pattern|all> Remove patterns
|
|
283
288
|
|
|
284
289
|
SOURCE MAPS:
|
|
285
|
-
|
|
286
|
-
|
|
290
|
+
agent-dbg sourcemap [file] Show source map info
|
|
291
|
+
agent-dbg sourcemap --disable Disable resolution globally
|
|
292
|
+
|
|
293
|
+
DIAGNOSTICS:
|
|
294
|
+
agent-dbg logs [-f|--follow] Show CDP protocol log
|
|
295
|
+
agent-dbg logs --limit 100 Show last N entries (default: 50)
|
|
296
|
+
agent-dbg logs --domain Debugger Filter by CDP domain
|
|
297
|
+
agent-dbg logs --clear Clear the log file`);
|
|
287
298
|
}
|
package/src/commands/attach.ts
CHANGED
|
@@ -8,14 +8,14 @@ registerCommand("attach", async (args) => {
|
|
|
8
8
|
|
|
9
9
|
if (!target) {
|
|
10
10
|
console.error("No target specified");
|
|
11
|
-
console.error(" -> Try:
|
|
11
|
+
console.error(" -> Try: agent-dbg attach <ws-url | port>");
|
|
12
12
|
return 1;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
// Check if daemon already running
|
|
16
16
|
if (DaemonClient.isRunning(session)) {
|
|
17
17
|
console.error(`Session "${session}" is already active`);
|
|
18
|
-
console.error(` -> Try:
|
|
18
|
+
console.error(` -> Try: agent-dbg stop --session ${session}`);
|
|
19
19
|
return 1;
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -6,7 +6,7 @@ registerCommand("blackbox-ls", async (args) => {
|
|
|
6
6
|
|
|
7
7
|
if (!DaemonClient.isRunning(session)) {
|
|
8
8
|
console.error(`No active session "${session}"`);
|
|
9
|
-
console.error(" -> Try:
|
|
9
|
+
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
10
10
|
return 1;
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -6,7 +6,7 @@ registerCommand("blackbox-rm", async (args) => {
|
|
|
6
6
|
|
|
7
7
|
if (!DaemonClient.isRunning(session)) {
|
|
8
8
|
console.error(`No active session "${session}"`);
|
|
9
|
-
console.error(" -> Try:
|
|
9
|
+
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
10
10
|
return 1;
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -24,8 +24,8 @@ registerCommand("blackbox-rm", async (args) => {
|
|
|
24
24
|
|
|
25
25
|
if (patterns.length === 0) {
|
|
26
26
|
console.error("No patterns specified");
|
|
27
|
-
console.error(" -> Try:
|
|
28
|
-
console.error(" -> Try:
|
|
27
|
+
console.error(" -> Try: agent-dbg blackbox-rm node_modules");
|
|
28
|
+
console.error(" -> Try: agent-dbg blackbox-rm all");
|
|
29
29
|
return 1;
|
|
30
30
|
}
|
|
31
31
|
|
package/src/commands/blackbox.ts
CHANGED
|
@@ -6,7 +6,7 @@ registerCommand("blackbox", async (args) => {
|
|
|
6
6
|
|
|
7
7
|
if (!DaemonClient.isRunning(session)) {
|
|
8
8
|
console.error(`No active session "${session}"`);
|
|
9
|
-
console.error(" -> Try:
|
|
9
|
+
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
10
10
|
return 1;
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -20,7 +20,7 @@ registerCommand("blackbox", async (args) => {
|
|
|
20
20
|
|
|
21
21
|
if (patterns.length === 0) {
|
|
22
22
|
console.error("No patterns specified");
|
|
23
|
-
console.error(" -> Try:
|
|
23
|
+
console.error(" -> Try: agent-dbg blackbox node_modules");
|
|
24
24
|
return 1;
|
|
25
25
|
}
|
|
26
26
|
|
package/src/commands/break-ls.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { registerCommand } from "../cli/registry.ts";
|
|
2
2
|
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
import { shortPath } from "../formatter/path.ts";
|
|
3
4
|
|
|
4
5
|
registerCommand("break-ls", async (args) => {
|
|
5
6
|
const session = args.global.session;
|
|
6
7
|
|
|
7
8
|
if (!DaemonClient.isRunning(session)) {
|
|
8
9
|
console.error(`No active session "${session}"`);
|
|
9
|
-
console.error(" -> Try:
|
|
10
|
+
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
10
11
|
return 1;
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -37,7 +38,7 @@ registerCommand("break-ls", async (args) => {
|
|
|
37
38
|
console.log("No breakpoints or logpoints set");
|
|
38
39
|
} else {
|
|
39
40
|
for (const bp of data) {
|
|
40
|
-
const loc = `${bp.url}:${bp.line}`;
|
|
41
|
+
const loc = `${shortPath(bp.url)}:${bp.line}`;
|
|
41
42
|
let line = `${bp.ref} ${loc}`;
|
|
42
43
|
if (bp.type === "LP" && bp.template) {
|
|
43
44
|
line += ` (log: ${bp.template})`;
|
package/src/commands/break-rm.ts
CHANGED
|
@@ -6,14 +6,14 @@ registerCommand("break-rm", async (args) => {
|
|
|
6
6
|
|
|
7
7
|
if (!DaemonClient.isRunning(session)) {
|
|
8
8
|
console.error(`No active session "${session}"`);
|
|
9
|
-
console.error(" -> Try:
|
|
9
|
+
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
10
10
|
return 1;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const ref = args.subcommand;
|
|
14
14
|
if (!ref) {
|
|
15
15
|
console.error("No breakpoint ref specified");
|
|
16
|
-
console.error(" -> Try:
|
|
16
|
+
console.error(" -> Try: agent-dbg break-rm BP#1");
|
|
17
17
|
return 1;
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -6,14 +6,14 @@ registerCommand("break-toggle", async (args) => {
|
|
|
6
6
|
|
|
7
7
|
if (!DaemonClient.isRunning(session)) {
|
|
8
8
|
console.error(`No active session "${session}"`);
|
|
9
|
-
console.error(" -> Try:
|
|
9
|
+
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
10
10
|
return 1;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const ref = args.subcommand;
|
|
14
14
|
if (!ref) {
|
|
15
15
|
console.error("No breakpoint ref specified");
|
|
16
|
-
console.error(" -> Try:
|
|
16
|
+
console.error(" -> Try: agent-dbg break-toggle BP#1");
|
|
17
17
|
return 1;
|
|
18
18
|
}
|
|
19
19
|
|
package/src/commands/break.ts
CHANGED
|
@@ -1,12 +1,44 @@
|
|
|
1
1
|
import { registerCommand } from "../cli/registry.ts";
|
|
2
2
|
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
import { shortPath } from "../formatter/path.ts";
|
|
4
|
+
|
|
5
|
+
function parseFileLineColumn(
|
|
6
|
+
target: string,
|
|
7
|
+
): { file: string; line: number; column?: number } | null {
|
|
8
|
+
// Match file:line:column or file:line
|
|
9
|
+
// Walk backwards to find the last two colon-separated numbers
|
|
10
|
+
const lastColon = target.lastIndexOf(":");
|
|
11
|
+
if (lastColon === -1 || lastColon === 0) return null;
|
|
12
|
+
|
|
13
|
+
const afterLast = target.slice(lastColon + 1);
|
|
14
|
+
const maybeNum = parseInt(afterLast, 10);
|
|
15
|
+
if (Number.isNaN(maybeNum) || maybeNum <= 0) return null;
|
|
16
|
+
|
|
17
|
+
const beforeLast = target.slice(0, lastColon);
|
|
18
|
+
const secondColon = beforeLast.lastIndexOf(":");
|
|
19
|
+
if (secondColon > 0) {
|
|
20
|
+
const afterSecond = beforeLast.slice(secondColon + 1);
|
|
21
|
+
const maybeLine = parseInt(afterSecond, 10);
|
|
22
|
+
if (!Number.isNaN(maybeLine) && maybeLine > 0) {
|
|
23
|
+
// file:line:column
|
|
24
|
+
return {
|
|
25
|
+
file: beforeLast.slice(0, secondColon),
|
|
26
|
+
line: maybeLine,
|
|
27
|
+
column: maybeNum,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// file:line
|
|
33
|
+
return { file: beforeLast, line: maybeNum };
|
|
34
|
+
}
|
|
3
35
|
|
|
4
36
|
registerCommand("break", async (args) => {
|
|
5
37
|
const session = args.global.session;
|
|
6
38
|
|
|
7
39
|
if (!DaemonClient.isRunning(session)) {
|
|
8
40
|
console.error(`No active session "${session}"`);
|
|
9
|
-
console.error(" -> Try:
|
|
41
|
+
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
10
42
|
return 1;
|
|
11
43
|
}
|
|
12
44
|
|
|
@@ -15,13 +47,14 @@ registerCommand("break", async (args) => {
|
|
|
15
47
|
|
|
16
48
|
let file: string;
|
|
17
49
|
let line: number;
|
|
50
|
+
let column: number | undefined;
|
|
18
51
|
|
|
19
52
|
if (patternFlag) {
|
|
20
53
|
// --pattern urlRegex:line
|
|
21
54
|
const lastColon = patternFlag.lastIndexOf(":");
|
|
22
55
|
if (lastColon === -1 || lastColon === 0) {
|
|
23
56
|
console.error(`Invalid --pattern target: "${patternFlag}"`);
|
|
24
|
-
console.error(" -> Try:
|
|
57
|
+
console.error(" -> Try: agent-dbg break --pattern 'app\\.js':42");
|
|
25
58
|
return 1;
|
|
26
59
|
}
|
|
27
60
|
file = patternFlag.slice(0, lastColon);
|
|
@@ -34,25 +67,20 @@ registerCommand("break", async (args) => {
|
|
|
34
67
|
const target = args.subcommand;
|
|
35
68
|
if (!target) {
|
|
36
69
|
console.error("No target specified");
|
|
37
|
-
console.error(" -> Try:
|
|
70
|
+
console.error(" -> Try: agent-dbg break src/app.ts:42");
|
|
38
71
|
return 1;
|
|
39
72
|
}
|
|
40
73
|
|
|
41
|
-
// Parse file:line from the target
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
74
|
+
// Parse file:line[:column] from the target
|
|
75
|
+
const parsed = parseFileLineColumn(target);
|
|
76
|
+
if (!parsed) {
|
|
44
77
|
console.error(`Invalid breakpoint target: "${target}"`);
|
|
45
|
-
console.error(" -> Try:
|
|
46
|
-
return 1;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
file = target.slice(0, lastColon);
|
|
50
|
-
line = parseInt(target.slice(lastColon + 1), 10);
|
|
51
|
-
if (Number.isNaN(line) || line <= 0) {
|
|
52
|
-
console.error(`Invalid line number in "${target}"`);
|
|
53
|
-
console.error(" -> Try: ndbg break src/app.ts:42");
|
|
78
|
+
console.error(" -> Try: agent-dbg break src/app.ts:42 or src/app.ts:42:5");
|
|
54
79
|
return 1;
|
|
55
80
|
}
|
|
81
|
+
file = parsed.file;
|
|
82
|
+
line = parsed.line;
|
|
83
|
+
column = parsed.column;
|
|
56
84
|
}
|
|
57
85
|
|
|
58
86
|
const condition = typeof args.flags.condition === "string" ? args.flags.condition : undefined;
|
|
@@ -85,7 +113,7 @@ registerCommand("break", async (args) => {
|
|
|
85
113
|
if (args.global.json) {
|
|
86
114
|
console.log(JSON.stringify(data, null, 2));
|
|
87
115
|
} else {
|
|
88
|
-
const loc = `${data.location.url}:${data.location.line}`;
|
|
116
|
+
const loc = `${shortPath(data.location.url)}:${data.location.line}`;
|
|
89
117
|
console.log(`${data.ref} set at ${loc} (log: ${logTemplate})`);
|
|
90
118
|
}
|
|
91
119
|
|
|
@@ -97,6 +125,7 @@ registerCommand("break", async (args) => {
|
|
|
97
125
|
line,
|
|
98
126
|
condition,
|
|
99
127
|
hitCount,
|
|
128
|
+
column,
|
|
100
129
|
};
|
|
101
130
|
if (patternFlag) {
|
|
102
131
|
breakArgs.urlRegex = file;
|
|
@@ -118,7 +147,7 @@ registerCommand("break", async (args) => {
|
|
|
118
147
|
if (args.global.json) {
|
|
119
148
|
console.log(JSON.stringify(data, null, 2));
|
|
120
149
|
} else {
|
|
121
|
-
const loc = `${data.location.url}:${data.location.line}`;
|
|
150
|
+
const loc = `${shortPath(data.location.url)}:${data.location.line}`;
|
|
122
151
|
let msg = `${data.ref} set at ${loc}`;
|
|
123
152
|
if (condition) {
|
|
124
153
|
msg += ` (condition: ${condition})`;
|
|
@@ -6,14 +6,14 @@ registerCommand("breakable", async (args) => {
|
|
|
6
6
|
|
|
7
7
|
if (!DaemonClient.isRunning(session)) {
|
|
8
8
|
console.error(`No active session "${session}"`);
|
|
9
|
-
console.error(" -> Try:
|
|
9
|
+
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
10
10
|
return 1;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const target = args.subcommand;
|
|
14
14
|
if (!target) {
|
|
15
15
|
console.error("No target specified");
|
|
16
|
-
console.error(" -> Try:
|
|
16
|
+
console.error(" -> Try: agent-dbg breakable src/app.ts:10-20");
|
|
17
17
|
return 1;
|
|
18
18
|
}
|
|
19
19
|
|
package/src/commands/catch.ts
CHANGED
|
@@ -8,14 +8,14 @@ registerCommand("catch", async (args) => {
|
|
|
8
8
|
|
|
9
9
|
if (!DaemonClient.isRunning(session)) {
|
|
10
10
|
console.error(`No active session "${session}"`);
|
|
11
|
-
console.error(" -> Try:
|
|
11
|
+
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
12
12
|
return 1;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const mode = args.subcommand ?? "all";
|
|
16
16
|
if (!VALID_MODES.has(mode)) {
|
|
17
17
|
console.error(`Invalid catch mode: "${mode}"`);
|
|
18
|
-
console.error(" -> Try:
|
|
18
|
+
console.error(" -> Try: agent-dbg catch [all | uncaught | caught | none]");
|
|
19
19
|
return 1;
|
|
20
20
|
}
|
|
21
21
|
|
package/src/commands/console.ts
CHANGED
|
@@ -15,7 +15,7 @@ registerCommand("console", async (args) => {
|
|
|
15
15
|
|
|
16
16
|
if (!DaemonClient.isRunning(session)) {
|
|
17
17
|
console.error(`No active session "${session}"`);
|
|
18
|
-
console.error(" -> Try:
|
|
18
|
+
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
19
19
|
return 1;
|
|
20
20
|
}
|
|
21
21
|
|
package/src/commands/continue.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { registerCommand } from "../cli/registry.ts";
|
|
2
2
|
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
-
import type {
|
|
3
|
+
import type { StateSnapshot } from "../daemon/session.ts";
|
|
4
|
+
import { printState } from "./print-state.ts";
|
|
4
5
|
|
|
5
6
|
registerCommand("continue", async (args) => {
|
|
6
7
|
const session = args.global.session;
|
|
7
8
|
|
|
8
9
|
if (!DaemonClient.isRunning(session)) {
|
|
9
10
|
console.error(`No active session "${session}"`);
|
|
10
|
-
console.error(" -> Try:
|
|
11
|
+
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
11
12
|
return 1;
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -20,27 +21,13 @@ registerCommand("continue", async (args) => {
|
|
|
20
21
|
return 1;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
const data = response.data as
|
|
24
|
+
const data = response.data as StateSnapshot;
|
|
24
25
|
|
|
25
26
|
if (args.global.json) {
|
|
26
27
|
console.log(JSON.stringify(data, null, 2));
|
|
27
28
|
} else {
|
|
28
|
-
|
|
29
|
+
printState(data);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
return 0;
|
|
32
33
|
});
|
|
33
|
-
|
|
34
|
-
function printStatus(data: SessionStatus): void {
|
|
35
|
-
if (data.state === "paused" && data.pauseInfo) {
|
|
36
|
-
const col = data.pauseInfo.column !== undefined ? `:${data.pauseInfo.column + 1}` : "";
|
|
37
|
-
const loc = data.pauseInfo.url
|
|
38
|
-
? `${data.pauseInfo.url}:${(data.pauseInfo.line ?? 0) + 1}${col}`
|
|
39
|
-
: "unknown";
|
|
40
|
-
console.log(`Paused at ${loc} (${data.pauseInfo.reason})`);
|
|
41
|
-
} else if (data.state === "running") {
|
|
42
|
-
console.log("Running");
|
|
43
|
-
} else {
|
|
44
|
-
console.log(`${data.state}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
package/src/commands/eval.ts
CHANGED
|
@@ -6,7 +6,7 @@ registerCommand("eval", async (args) => {
|
|
|
6
6
|
|
|
7
7
|
if (!DaemonClient.isRunning(session)) {
|
|
8
8
|
console.error(`No active session "${session}"`);
|
|
9
|
-
console.error(" -> Try:
|
|
9
|
+
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
10
10
|
return 1;
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -20,7 +20,7 @@ registerCommand("eval", async (args) => {
|
|
|
20
20
|
|
|
21
21
|
if (!expression) {
|
|
22
22
|
console.error("No expression specified");
|
|
23
|
-
console.error(" -> Try:
|
|
23
|
+
console.error(" -> Try: agent-dbg eval 1 + 2");
|
|
24
24
|
return 1;
|
|
25
25
|
}
|
|
26
26
|
|