agent-dbg 0.1.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.
- package/.bin/ndbg +0 -0
- package/.claude/settings.local.json +21 -0
- package/.claude/skills/ndbg-debugger/ndbg-debugger/SKILL.md +116 -0
- package/.claude/skills/ndbg-debugger/ndbg-debugger/references/commands.md +173 -0
- package/CLAUDE.md +43 -0
- package/PROGRESS.md +261 -0
- package/README.md +67 -0
- package/biome.json +41 -0
- package/ndbg-spec.md +958 -0
- package/package.json +30 -0
- package/src/cdp/client.ts +198 -0
- package/src/cdp/types.ts +16 -0
- package/src/cli/parser.ts +287 -0
- package/src/cli/registry.ts +7 -0
- package/src/cli/types.ts +24 -0
- package/src/commands/attach.ts +47 -0
- package/src/commands/blackbox-ls.ts +38 -0
- package/src/commands/blackbox-rm.ts +57 -0
- package/src/commands/blackbox.ts +48 -0
- package/src/commands/break-ls.ts +57 -0
- package/src/commands/break-rm.ts +40 -0
- package/src/commands/break-toggle.ts +42 -0
- package/src/commands/break.ts +145 -0
- package/src/commands/breakable.ts +69 -0
- package/src/commands/catch.ts +38 -0
- package/src/commands/console.ts +61 -0
- package/src/commands/continue.ts +46 -0
- package/src/commands/eval.ts +70 -0
- package/src/commands/exceptions.ts +61 -0
- package/src/commands/hotpatch.ts +67 -0
- package/src/commands/launch.ts +69 -0
- package/src/commands/logpoint.ts +78 -0
- package/src/commands/pause.ts +46 -0
- package/src/commands/props.ts +77 -0
- package/src/commands/restart-frame.ts +36 -0
- package/src/commands/run-to.ts +70 -0
- package/src/commands/scripts.ts +57 -0
- package/src/commands/search.ts +73 -0
- package/src/commands/sessions.ts +71 -0
- package/src/commands/set-return.ts +49 -0
- package/src/commands/set.ts +61 -0
- package/src/commands/source.ts +59 -0
- package/src/commands/sourcemap.ts +66 -0
- package/src/commands/stack.ts +64 -0
- package/src/commands/state.ts +124 -0
- package/src/commands/status.ts +57 -0
- package/src/commands/step.ts +50 -0
- package/src/commands/stop.ts +27 -0
- package/src/commands/vars.ts +71 -0
- package/src/daemon/client.ts +147 -0
- package/src/daemon/entry.ts +242 -0
- package/src/daemon/paths.ts +26 -0
- package/src/daemon/server.ts +185 -0
- package/src/daemon/session-blackbox.ts +41 -0
- package/src/daemon/session-breakpoints.ts +492 -0
- package/src/daemon/session-execution.ts +121 -0
- package/src/daemon/session-inspection.ts +701 -0
- package/src/daemon/session-mutation.ts +197 -0
- package/src/daemon/session-state.ts +258 -0
- package/src/daemon/session.ts +938 -0
- package/src/daemon/spawn.ts +53 -0
- package/src/formatter/errors.ts +15 -0
- package/src/formatter/source.ts +74 -0
- package/src/formatter/stack.ts +70 -0
- package/src/formatter/values.ts +269 -0
- package/src/formatter/variables.ts +20 -0
- package/src/main.ts +45 -0
- package/src/protocol/messages.ts +316 -0
- package/src/refs/ref-table.ts +120 -0
- package/src/refs/resolver.ts +24 -0
- package/src/sourcemap/resolver.ts +318 -0
- package/tests/fixtures/async-app.js +34 -0
- package/tests/fixtures/console-app.js +12 -0
- package/tests/fixtures/error-app.js +28 -0
- package/tests/fixtures/exception-app.js +6 -0
- package/tests/fixtures/inspect-app.js +10 -0
- package/tests/fixtures/mutation-app.js +9 -0
- package/tests/fixtures/simple-app.js +50 -0
- package/tests/fixtures/step-app.js +13 -0
- package/tests/fixtures/ts-app/src/app.ts +21 -0
- package/tests/fixtures/ts-app/tsconfig.json +14 -0
- package/tests/integration/blackbox.test.ts +135 -0
- package/tests/integration/break-extras.test.ts +241 -0
- package/tests/integration/breakpoint.test.ts +217 -0
- package/tests/integration/console.test.ts +275 -0
- package/tests/integration/execution.test.ts +247 -0
- package/tests/integration/inspection.test.ts +311 -0
- package/tests/integration/mutation.test.ts +178 -0
- package/tests/integration/session.test.ts +223 -0
- package/tests/integration/source.test.ts +209 -0
- package/tests/integration/sourcemap.test.ts +214 -0
- package/tests/integration/state.test.ts +208 -0
- package/tests/unit/cdp-client.test.ts +422 -0
- package/tests/unit/daemon.test.ts +286 -0
- package/tests/unit/formatter.test.ts +716 -0
- package/tests/unit/parser.test.ts +105 -0
- package/tests/unit/refs.test.ts +383 -0
- package/tests/unit/sourcemap.test.ts +236 -0
- package/tsconfig.json +32 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { registerCommand } from "../cli/registry.ts";
|
|
2
|
+
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
|
|
4
|
+
registerCommand("blackbox-rm", async (args) => {
|
|
5
|
+
const session = args.global.session;
|
|
6
|
+
|
|
7
|
+
if (!DaemonClient.isRunning(session)) {
|
|
8
|
+
console.error(`No active session "${session}"`);
|
|
9
|
+
console.error(" -> Try: ndbg launch --brk node app.js");
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const patterns: string[] = [];
|
|
14
|
+
if (args.subcommand) {
|
|
15
|
+
if (args.subcommand === "all") {
|
|
16
|
+
patterns.push("all");
|
|
17
|
+
} else {
|
|
18
|
+
patterns.push(args.subcommand);
|
|
19
|
+
for (const p of args.positionals) {
|
|
20
|
+
patterns.push(p);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (patterns.length === 0) {
|
|
26
|
+
console.error("No patterns specified");
|
|
27
|
+
console.error(" -> Try: ndbg blackbox-rm node_modules");
|
|
28
|
+
console.error(" -> Try: ndbg blackbox-rm all");
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const client = new DaemonClient(session);
|
|
33
|
+
const response = await client.request("blackbox-rm", { patterns });
|
|
34
|
+
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
console.error(`${response.error}`);
|
|
37
|
+
if (response.suggestion) console.error(` ${response.suggestion}`);
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const data = response.data as string[];
|
|
42
|
+
|
|
43
|
+
if (args.global.json) {
|
|
44
|
+
console.log(JSON.stringify(data, null, 2));
|
|
45
|
+
} else {
|
|
46
|
+
if (data.length === 0) {
|
|
47
|
+
console.log("All blackbox patterns removed");
|
|
48
|
+
} else {
|
|
49
|
+
console.log("Blackbox patterns:");
|
|
50
|
+
for (const p of data) {
|
|
51
|
+
console.log(` ${p}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return 0;
|
|
57
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { registerCommand } from "../cli/registry.ts";
|
|
2
|
+
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
|
|
4
|
+
registerCommand("blackbox", async (args) => {
|
|
5
|
+
const session = args.global.session;
|
|
6
|
+
|
|
7
|
+
if (!DaemonClient.isRunning(session)) {
|
|
8
|
+
console.error(`No active session "${session}"`);
|
|
9
|
+
console.error(" -> Try: ndbg launch --brk node app.js");
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const patterns: string[] = [];
|
|
14
|
+
if (args.subcommand) {
|
|
15
|
+
patterns.push(args.subcommand);
|
|
16
|
+
}
|
|
17
|
+
for (const p of args.positionals) {
|
|
18
|
+
patterns.push(p);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (patterns.length === 0) {
|
|
22
|
+
console.error("No patterns specified");
|
|
23
|
+
console.error(" -> Try: ndbg blackbox node_modules");
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const client = new DaemonClient(session);
|
|
28
|
+
const response = await client.request("blackbox", { patterns });
|
|
29
|
+
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
console.error(`${response.error}`);
|
|
32
|
+
if (response.suggestion) console.error(` ${response.suggestion}`);
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const data = response.data as string[];
|
|
37
|
+
|
|
38
|
+
if (args.global.json) {
|
|
39
|
+
console.log(JSON.stringify(data, null, 2));
|
|
40
|
+
} else {
|
|
41
|
+
console.log("Blackbox patterns:");
|
|
42
|
+
for (const p of data) {
|
|
43
|
+
console.log(` ${p}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return 0;
|
|
48
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { registerCommand } from "../cli/registry.ts";
|
|
2
|
+
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
|
|
4
|
+
registerCommand("break-ls", async (args) => {
|
|
5
|
+
const session = args.global.session;
|
|
6
|
+
|
|
7
|
+
if (!DaemonClient.isRunning(session)) {
|
|
8
|
+
console.error(`No active session "${session}"`);
|
|
9
|
+
console.error(" -> Try: ndbg launch --brk node app.js");
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const client = new DaemonClient(session);
|
|
14
|
+
const response = await client.request("break-ls");
|
|
15
|
+
|
|
16
|
+
if (!response.ok) {
|
|
17
|
+
console.error(`${response.error}`);
|
|
18
|
+
if (response.suggestion) console.error(` ${response.suggestion}`);
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const data = response.data as Array<{
|
|
23
|
+
ref: string;
|
|
24
|
+
type: "BP" | "LP";
|
|
25
|
+
url: string;
|
|
26
|
+
line: number;
|
|
27
|
+
column?: number;
|
|
28
|
+
condition?: string;
|
|
29
|
+
hitCount?: number;
|
|
30
|
+
template?: string;
|
|
31
|
+
}>;
|
|
32
|
+
|
|
33
|
+
if (args.global.json) {
|
|
34
|
+
console.log(JSON.stringify(data, null, 2));
|
|
35
|
+
} else {
|
|
36
|
+
if (data.length === 0) {
|
|
37
|
+
console.log("No breakpoints or logpoints set");
|
|
38
|
+
} else {
|
|
39
|
+
for (const bp of data) {
|
|
40
|
+
const loc = `${bp.url}:${bp.line}`;
|
|
41
|
+
let line = `${bp.ref} ${loc}`;
|
|
42
|
+
if (bp.type === "LP" && bp.template) {
|
|
43
|
+
line += ` (log: ${bp.template})`;
|
|
44
|
+
}
|
|
45
|
+
if (bp.condition) {
|
|
46
|
+
line += ` [condition: ${bp.condition}]`;
|
|
47
|
+
}
|
|
48
|
+
if (bp.hitCount) {
|
|
49
|
+
line += ` [hit-count: ${bp.hitCount}]`;
|
|
50
|
+
}
|
|
51
|
+
console.log(line);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return 0;
|
|
57
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { registerCommand } from "../cli/registry.ts";
|
|
2
|
+
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
|
|
4
|
+
registerCommand("break-rm", async (args) => {
|
|
5
|
+
const session = args.global.session;
|
|
6
|
+
|
|
7
|
+
if (!DaemonClient.isRunning(session)) {
|
|
8
|
+
console.error(`No active session "${session}"`);
|
|
9
|
+
console.error(" -> Try: ndbg launch --brk node app.js");
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const ref = args.subcommand;
|
|
14
|
+
if (!ref) {
|
|
15
|
+
console.error("No breakpoint ref specified");
|
|
16
|
+
console.error(" -> Try: ndbg break-rm BP#1");
|
|
17
|
+
return 1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const client = new DaemonClient(session);
|
|
21
|
+
const response = await client.request("break-rm", { ref });
|
|
22
|
+
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
console.error(`${response.error}`);
|
|
25
|
+
if (response.suggestion) console.error(` ${response.suggestion}`);
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (args.global.json) {
|
|
30
|
+
console.log(JSON.stringify({ ok: true, ref }, null, 2));
|
|
31
|
+
} else {
|
|
32
|
+
if (ref === "all") {
|
|
33
|
+
console.log("All breakpoints and logpoints removed");
|
|
34
|
+
} else {
|
|
35
|
+
console.log(`${ref} removed`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return 0;
|
|
40
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { registerCommand } from "../cli/registry.ts";
|
|
2
|
+
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
|
|
4
|
+
registerCommand("break-toggle", async (args) => {
|
|
5
|
+
const session = args.global.session;
|
|
6
|
+
|
|
7
|
+
if (!DaemonClient.isRunning(session)) {
|
|
8
|
+
console.error(`No active session "${session}"`);
|
|
9
|
+
console.error(" -> Try: ndbg launch --brk node app.js");
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const ref = args.subcommand;
|
|
14
|
+
if (!ref) {
|
|
15
|
+
console.error("No breakpoint ref specified");
|
|
16
|
+
console.error(" -> Try: ndbg break-toggle BP#1");
|
|
17
|
+
return 1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const client = new DaemonClient(session);
|
|
21
|
+
const response = await client.request("break-toggle", { ref });
|
|
22
|
+
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
console.error(`${response.error}`);
|
|
25
|
+
if (response.suggestion) console.error(` ${response.suggestion}`);
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const data = response.data as { ref: string; state: "enabled" | "disabled" };
|
|
30
|
+
|
|
31
|
+
if (args.global.json) {
|
|
32
|
+
console.log(JSON.stringify(data, null, 2));
|
|
33
|
+
} else {
|
|
34
|
+
if (data.ref === "all") {
|
|
35
|
+
console.log(`All breakpoints ${data.state}`);
|
|
36
|
+
} else {
|
|
37
|
+
console.log(`${data.ref} ${data.state}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return 0;
|
|
42
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { registerCommand } from "../cli/registry.ts";
|
|
2
|
+
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
|
|
4
|
+
registerCommand("break", async (args) => {
|
|
5
|
+
const session = args.global.session;
|
|
6
|
+
|
|
7
|
+
if (!DaemonClient.isRunning(session)) {
|
|
8
|
+
console.error(`No active session "${session}"`);
|
|
9
|
+
console.error(" -> Try: ndbg launch --brk node app.js");
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const patternFlag = typeof args.flags.pattern === "string" ? args.flags.pattern : undefined;
|
|
14
|
+
const shouldContinue = args.flags.continue === true;
|
|
15
|
+
|
|
16
|
+
let file: string;
|
|
17
|
+
let line: number;
|
|
18
|
+
|
|
19
|
+
if (patternFlag) {
|
|
20
|
+
// --pattern urlRegex:line
|
|
21
|
+
const lastColon = patternFlag.lastIndexOf(":");
|
|
22
|
+
if (lastColon === -1 || lastColon === 0) {
|
|
23
|
+
console.error(`Invalid --pattern target: "${patternFlag}"`);
|
|
24
|
+
console.error(" -> Try: ndbg break --pattern 'app\\.js':42");
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
file = patternFlag.slice(0, lastColon);
|
|
28
|
+
line = parseInt(patternFlag.slice(lastColon + 1), 10);
|
|
29
|
+
if (Number.isNaN(line) || line <= 0) {
|
|
30
|
+
console.error(`Invalid line number in --pattern "${patternFlag}"`);
|
|
31
|
+
return 1;
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
const target = args.subcommand;
|
|
35
|
+
if (!target) {
|
|
36
|
+
console.error("No target specified");
|
|
37
|
+
console.error(" -> Try: ndbg break src/app.ts:42");
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Parse file:line from the target
|
|
42
|
+
const lastColon = target.lastIndexOf(":");
|
|
43
|
+
if (lastColon === -1 || lastColon === 0) {
|
|
44
|
+
console.error(`Invalid breakpoint target: "${target}"`);
|
|
45
|
+
console.error(" -> Try: ndbg break src/app.ts:42");
|
|
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");
|
|
54
|
+
return 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const condition = typeof args.flags.condition === "string" ? args.flags.condition : undefined;
|
|
59
|
+
const hitCount =
|
|
60
|
+
typeof args.flags["hit-count"] === "string" ? parseInt(args.flags["hit-count"], 10) : undefined;
|
|
61
|
+
const logTemplate = typeof args.flags.log === "string" ? args.flags.log : undefined;
|
|
62
|
+
|
|
63
|
+
const client = new DaemonClient(session);
|
|
64
|
+
|
|
65
|
+
// If --log is provided, create a logpoint instead
|
|
66
|
+
if (logTemplate) {
|
|
67
|
+
const response = await client.request("logpoint", {
|
|
68
|
+
file,
|
|
69
|
+
line,
|
|
70
|
+
template: logTemplate,
|
|
71
|
+
condition,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
console.error(`${response.error}`);
|
|
76
|
+
if (response.suggestion) console.error(` ${response.suggestion}`);
|
|
77
|
+
return 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const data = response.data as {
|
|
81
|
+
ref: string;
|
|
82
|
+
location: { url: string; line: number; column?: number };
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
if (args.global.json) {
|
|
86
|
+
console.log(JSON.stringify(data, null, 2));
|
|
87
|
+
} else {
|
|
88
|
+
const loc = `${data.location.url}:${data.location.line}`;
|
|
89
|
+
console.log(`${data.ref} set at ${loc} (log: ${logTemplate})`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const breakArgs: Record<string, unknown> = {
|
|
96
|
+
file,
|
|
97
|
+
line,
|
|
98
|
+
condition,
|
|
99
|
+
hitCount,
|
|
100
|
+
};
|
|
101
|
+
if (patternFlag) {
|
|
102
|
+
breakArgs.urlRegex = file;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const response = await client.request("break", breakArgs);
|
|
106
|
+
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
console.error(`${response.error}`);
|
|
109
|
+
if (response.suggestion) console.error(` ${response.suggestion}`);
|
|
110
|
+
return 1;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const data = response.data as {
|
|
114
|
+
ref: string;
|
|
115
|
+
location: { url: string; line: number; column?: number };
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
if (args.global.json) {
|
|
119
|
+
console.log(JSON.stringify(data, null, 2));
|
|
120
|
+
} else {
|
|
121
|
+
const loc = `${data.location.url}:${data.location.line}`;
|
|
122
|
+
let msg = `${data.ref} set at ${loc}`;
|
|
123
|
+
if (condition) {
|
|
124
|
+
msg += ` (condition: ${condition})`;
|
|
125
|
+
}
|
|
126
|
+
if (hitCount) {
|
|
127
|
+
msg += ` (hit-count: ${hitCount})`;
|
|
128
|
+
}
|
|
129
|
+
console.log(msg);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// If --continue flag is set, also send a continue request
|
|
133
|
+
if (shouldContinue) {
|
|
134
|
+
const contResponse = await client.request("continue");
|
|
135
|
+
if (!contResponse.ok) {
|
|
136
|
+
console.error(`${contResponse.error}`);
|
|
137
|
+
return 1;
|
|
138
|
+
}
|
|
139
|
+
if (!args.global.json) {
|
|
140
|
+
console.log("Continued");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return 0;
|
|
145
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { registerCommand } from "../cli/registry.ts";
|
|
2
|
+
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
|
|
4
|
+
registerCommand("breakable", async (args) => {
|
|
5
|
+
const session = args.global.session;
|
|
6
|
+
|
|
7
|
+
if (!DaemonClient.isRunning(session)) {
|
|
8
|
+
console.error(`No active session "${session}"`);
|
|
9
|
+
console.error(" -> Try: ndbg launch --brk node app.js");
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const target = args.subcommand;
|
|
14
|
+
if (!target) {
|
|
15
|
+
console.error("No target specified");
|
|
16
|
+
console.error(" -> Try: ndbg breakable src/app.ts:10-20");
|
|
17
|
+
return 1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Parse file:start-end from the target
|
|
21
|
+
const lastColon = target.lastIndexOf(":");
|
|
22
|
+
if (lastColon === -1 || lastColon === 0) {
|
|
23
|
+
console.error(`Invalid target format: "${target}"`);
|
|
24
|
+
console.error(" -> Expected: <file>:<start>-<end>");
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const file = target.slice(0, lastColon);
|
|
29
|
+
const range = target.slice(lastColon + 1);
|
|
30
|
+
const dashIdx = range.indexOf("-");
|
|
31
|
+
if (dashIdx === -1) {
|
|
32
|
+
console.error(`Invalid range format: "${range}"`);
|
|
33
|
+
console.error(" -> Expected: <start>-<end>");
|
|
34
|
+
return 1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const startLine = parseInt(range.slice(0, dashIdx), 10);
|
|
38
|
+
const endLine = parseInt(range.slice(dashIdx + 1), 10);
|
|
39
|
+
if (Number.isNaN(startLine) || Number.isNaN(endLine) || startLine <= 0 || endLine <= 0) {
|
|
40
|
+
console.error(`Invalid line numbers in "${range}"`);
|
|
41
|
+
console.error(" -> Expected: <start>-<end>");
|
|
42
|
+
return 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const client = new DaemonClient(session);
|
|
46
|
+
const response = await client.request("breakable", { file, startLine, endLine });
|
|
47
|
+
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
console.error(`${response.error}`);
|
|
50
|
+
if (response.suggestion) console.error(` ${response.suggestion}`);
|
|
51
|
+
return 1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const data = response.data as Array<{ line: number; column: number }>;
|
|
55
|
+
|
|
56
|
+
if (args.global.json) {
|
|
57
|
+
console.log(JSON.stringify(data, null, 2));
|
|
58
|
+
} else {
|
|
59
|
+
if (data.length === 0) {
|
|
60
|
+
console.log("No breakable locations in range");
|
|
61
|
+
} else {
|
|
62
|
+
for (const loc of data) {
|
|
63
|
+
console.log(` ${file}:${loc.line}:${loc.column}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return 0;
|
|
69
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { registerCommand } from "../cli/registry.ts";
|
|
2
|
+
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
|
|
4
|
+
const VALID_MODES = new Set(["all", "uncaught", "caught", "none"]);
|
|
5
|
+
|
|
6
|
+
registerCommand("catch", async (args) => {
|
|
7
|
+
const session = args.global.session;
|
|
8
|
+
|
|
9
|
+
if (!DaemonClient.isRunning(session)) {
|
|
10
|
+
console.error(`No active session "${session}"`);
|
|
11
|
+
console.error(" -> Try: ndbg launch --brk node app.js");
|
|
12
|
+
return 1;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const mode = args.subcommand ?? "all";
|
|
16
|
+
if (!VALID_MODES.has(mode)) {
|
|
17
|
+
console.error(`Invalid catch mode: "${mode}"`);
|
|
18
|
+
console.error(" -> Try: ndbg catch [all | uncaught | caught | none]");
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const client = new DaemonClient(session);
|
|
23
|
+
const response = await client.request("catch", { mode });
|
|
24
|
+
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
console.error(`${response.error}`);
|
|
27
|
+
if (response.suggestion) console.error(` ${response.suggestion}`);
|
|
28
|
+
return 1;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (args.global.json) {
|
|
32
|
+
console.log(JSON.stringify({ mode }, null, 2));
|
|
33
|
+
} else {
|
|
34
|
+
console.log(`Exception pause mode: ${mode}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return 0;
|
|
38
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { registerCommand } from "../cli/registry.ts";
|
|
2
|
+
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
import type { ConsoleMessage } from "../daemon/session.ts";
|
|
4
|
+
|
|
5
|
+
function formatTimestamp(ts: number): string {
|
|
6
|
+
const d = new Date(ts);
|
|
7
|
+
const hh = String(d.getHours()).padStart(2, "0");
|
|
8
|
+
const mm = String(d.getMinutes()).padStart(2, "0");
|
|
9
|
+
const ss = String(d.getSeconds()).padStart(2, "0");
|
|
10
|
+
return `${hh}:${mm}:${ss}`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
registerCommand("console", async (args) => {
|
|
14
|
+
const session = args.global.session;
|
|
15
|
+
|
|
16
|
+
if (!DaemonClient.isRunning(session)) {
|
|
17
|
+
console.error(`No active session "${session}"`);
|
|
18
|
+
console.error(" -> Try: ndbg launch --brk node app.js");
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const client = new DaemonClient(session);
|
|
23
|
+
|
|
24
|
+
const consoleArgs: Record<string, unknown> = {};
|
|
25
|
+
if (typeof args.flags.level === "string") {
|
|
26
|
+
consoleArgs.level = args.flags.level;
|
|
27
|
+
}
|
|
28
|
+
if (typeof args.flags.since === "string") {
|
|
29
|
+
consoleArgs.since = parseInt(args.flags.since, 10);
|
|
30
|
+
}
|
|
31
|
+
if (args.flags.clear === true) {
|
|
32
|
+
consoleArgs.clear = true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const response = await client.request("console", consoleArgs);
|
|
36
|
+
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
console.error(`${response.error}`);
|
|
39
|
+
if (response.suggestion) console.error(` ${response.suggestion}`);
|
|
40
|
+
return 1;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const messages = response.data as ConsoleMessage[];
|
|
44
|
+
|
|
45
|
+
if (args.global.json) {
|
|
46
|
+
console.log(JSON.stringify(messages, null, 2));
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (messages.length === 0) {
|
|
51
|
+
console.log("(no console messages)");
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (const msg of messages) {
|
|
56
|
+
const ts = formatTimestamp(msg.timestamp);
|
|
57
|
+
console.log(`[${ts}] [${msg.level}] ${msg.text}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return 0;
|
|
61
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { registerCommand } from "../cli/registry.ts";
|
|
2
|
+
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
import type { SessionStatus } from "../daemon/session.ts";
|
|
4
|
+
|
|
5
|
+
registerCommand("continue", async (args) => {
|
|
6
|
+
const session = args.global.session;
|
|
7
|
+
|
|
8
|
+
if (!DaemonClient.isRunning(session)) {
|
|
9
|
+
console.error(`No active session "${session}"`);
|
|
10
|
+
console.error(" -> Try: ndbg launch --brk node app.js");
|
|
11
|
+
return 1;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const client = new DaemonClient(session);
|
|
15
|
+
const response = await client.request("continue");
|
|
16
|
+
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
console.error(`${response.error}`);
|
|
19
|
+
if (response.suggestion) console.error(` ${response.suggestion}`);
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const data = response.data as SessionStatus;
|
|
24
|
+
|
|
25
|
+
if (args.global.json) {
|
|
26
|
+
console.log(JSON.stringify(data, null, 2));
|
|
27
|
+
} else {
|
|
28
|
+
printStatus(data);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return 0;
|
|
32
|
+
});
|
|
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
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { registerCommand } from "../cli/registry.ts";
|
|
2
|
+
import { DaemonClient } from "../daemon/client.ts";
|
|
3
|
+
|
|
4
|
+
registerCommand("eval", async (args) => {
|
|
5
|
+
const session = args.global.session;
|
|
6
|
+
|
|
7
|
+
if (!DaemonClient.isRunning(session)) {
|
|
8
|
+
console.error(`No active session "${session}"`);
|
|
9
|
+
console.error(" -> Try: ndbg launch --brk node app.js");
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Build expression from subcommand + positionals
|
|
14
|
+
const parts: string[] = [];
|
|
15
|
+
if (args.subcommand) {
|
|
16
|
+
parts.push(args.subcommand);
|
|
17
|
+
}
|
|
18
|
+
parts.push(...args.positionals);
|
|
19
|
+
const expression = parts.join(" ");
|
|
20
|
+
|
|
21
|
+
if (!expression) {
|
|
22
|
+
console.error("No expression specified");
|
|
23
|
+
console.error(" -> Try: ndbg eval 1 + 2");
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const evalArgs: Record<string, unknown> = {
|
|
28
|
+
expression,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
if (typeof args.flags.frame === "string") {
|
|
32
|
+
evalArgs.frame = args.flags.frame;
|
|
33
|
+
}
|
|
34
|
+
if (args.flags["side-effect-free"] === true) {
|
|
35
|
+
evalArgs.throwOnSideEffect = true;
|
|
36
|
+
}
|
|
37
|
+
if (typeof args.flags.timeout === "string") {
|
|
38
|
+
evalArgs.timeout = parseInt(args.flags.timeout, 10);
|
|
39
|
+
}
|
|
40
|
+
if (args.flags.await === true) {
|
|
41
|
+
evalArgs.awaitPromise = true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const client = new DaemonClient(session);
|
|
45
|
+
const response = await client.request("eval", evalArgs);
|
|
46
|
+
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
console.error(`${response.error}`);
|
|
49
|
+
if (response.suggestion) console.error(` ${response.suggestion}`);
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const data = response.data as {
|
|
54
|
+
ref: string;
|
|
55
|
+
type: string;
|
|
56
|
+
value: string;
|
|
57
|
+
objectId?: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (args.global.json) {
|
|
61
|
+
console.log(JSON.stringify(data, null, 2));
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (args.flags.silent !== true) {
|
|
66
|
+
console.log(`${data.ref} ${data.value}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return 0;
|
|
70
|
+
});
|