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.
Files changed (99) hide show
  1. package/.bin/ndbg +0 -0
  2. package/.claude/settings.local.json +21 -0
  3. package/.claude/skills/ndbg-debugger/ndbg-debugger/SKILL.md +116 -0
  4. package/.claude/skills/ndbg-debugger/ndbg-debugger/references/commands.md +173 -0
  5. package/CLAUDE.md +43 -0
  6. package/PROGRESS.md +261 -0
  7. package/README.md +67 -0
  8. package/biome.json +41 -0
  9. package/ndbg-spec.md +958 -0
  10. package/package.json +30 -0
  11. package/src/cdp/client.ts +198 -0
  12. package/src/cdp/types.ts +16 -0
  13. package/src/cli/parser.ts +287 -0
  14. package/src/cli/registry.ts +7 -0
  15. package/src/cli/types.ts +24 -0
  16. package/src/commands/attach.ts +47 -0
  17. package/src/commands/blackbox-ls.ts +38 -0
  18. package/src/commands/blackbox-rm.ts +57 -0
  19. package/src/commands/blackbox.ts +48 -0
  20. package/src/commands/break-ls.ts +57 -0
  21. package/src/commands/break-rm.ts +40 -0
  22. package/src/commands/break-toggle.ts +42 -0
  23. package/src/commands/break.ts +145 -0
  24. package/src/commands/breakable.ts +69 -0
  25. package/src/commands/catch.ts +38 -0
  26. package/src/commands/console.ts +61 -0
  27. package/src/commands/continue.ts +46 -0
  28. package/src/commands/eval.ts +70 -0
  29. package/src/commands/exceptions.ts +61 -0
  30. package/src/commands/hotpatch.ts +67 -0
  31. package/src/commands/launch.ts +69 -0
  32. package/src/commands/logpoint.ts +78 -0
  33. package/src/commands/pause.ts +46 -0
  34. package/src/commands/props.ts +77 -0
  35. package/src/commands/restart-frame.ts +36 -0
  36. package/src/commands/run-to.ts +70 -0
  37. package/src/commands/scripts.ts +57 -0
  38. package/src/commands/search.ts +73 -0
  39. package/src/commands/sessions.ts +71 -0
  40. package/src/commands/set-return.ts +49 -0
  41. package/src/commands/set.ts +61 -0
  42. package/src/commands/source.ts +59 -0
  43. package/src/commands/sourcemap.ts +66 -0
  44. package/src/commands/stack.ts +64 -0
  45. package/src/commands/state.ts +124 -0
  46. package/src/commands/status.ts +57 -0
  47. package/src/commands/step.ts +50 -0
  48. package/src/commands/stop.ts +27 -0
  49. package/src/commands/vars.ts +71 -0
  50. package/src/daemon/client.ts +147 -0
  51. package/src/daemon/entry.ts +242 -0
  52. package/src/daemon/paths.ts +26 -0
  53. package/src/daemon/server.ts +185 -0
  54. package/src/daemon/session-blackbox.ts +41 -0
  55. package/src/daemon/session-breakpoints.ts +492 -0
  56. package/src/daemon/session-execution.ts +121 -0
  57. package/src/daemon/session-inspection.ts +701 -0
  58. package/src/daemon/session-mutation.ts +197 -0
  59. package/src/daemon/session-state.ts +258 -0
  60. package/src/daemon/session.ts +938 -0
  61. package/src/daemon/spawn.ts +53 -0
  62. package/src/formatter/errors.ts +15 -0
  63. package/src/formatter/source.ts +74 -0
  64. package/src/formatter/stack.ts +70 -0
  65. package/src/formatter/values.ts +269 -0
  66. package/src/formatter/variables.ts +20 -0
  67. package/src/main.ts +45 -0
  68. package/src/protocol/messages.ts +316 -0
  69. package/src/refs/ref-table.ts +120 -0
  70. package/src/refs/resolver.ts +24 -0
  71. package/src/sourcemap/resolver.ts +318 -0
  72. package/tests/fixtures/async-app.js +34 -0
  73. package/tests/fixtures/console-app.js +12 -0
  74. package/tests/fixtures/error-app.js +28 -0
  75. package/tests/fixtures/exception-app.js +6 -0
  76. package/tests/fixtures/inspect-app.js +10 -0
  77. package/tests/fixtures/mutation-app.js +9 -0
  78. package/tests/fixtures/simple-app.js +50 -0
  79. package/tests/fixtures/step-app.js +13 -0
  80. package/tests/fixtures/ts-app/src/app.ts +21 -0
  81. package/tests/fixtures/ts-app/tsconfig.json +14 -0
  82. package/tests/integration/blackbox.test.ts +135 -0
  83. package/tests/integration/break-extras.test.ts +241 -0
  84. package/tests/integration/breakpoint.test.ts +217 -0
  85. package/tests/integration/console.test.ts +275 -0
  86. package/tests/integration/execution.test.ts +247 -0
  87. package/tests/integration/inspection.test.ts +311 -0
  88. package/tests/integration/mutation.test.ts +178 -0
  89. package/tests/integration/session.test.ts +223 -0
  90. package/tests/integration/source.test.ts +209 -0
  91. package/tests/integration/sourcemap.test.ts +214 -0
  92. package/tests/integration/state.test.ts +208 -0
  93. package/tests/unit/cdp-client.test.ts +422 -0
  94. package/tests/unit/daemon.test.ts +286 -0
  95. package/tests/unit/formatter.test.ts +716 -0
  96. package/tests/unit/parser.test.ts +105 -0
  97. package/tests/unit/refs.test.ts +383 -0
  98. package/tests/unit/sourcemap.test.ts +236 -0
  99. package/tsconfig.json +32 -0
@@ -0,0 +1,61 @@
1
+ import { registerCommand } from "../cli/registry.ts";
2
+ import { DaemonClient } from "../daemon/client.ts";
3
+ import type { ExceptionEntry } 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("exceptions", 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 exceptionsArgs: Record<string, unknown> = {};
25
+ if (typeof args.flags.since === "string") {
26
+ exceptionsArgs.since = parseInt(args.flags.since, 10);
27
+ }
28
+
29
+ const response = await client.request("exceptions", exceptionsArgs);
30
+
31
+ if (!response.ok) {
32
+ console.error(`${response.error}`);
33
+ if (response.suggestion) console.error(` ${response.suggestion}`);
34
+ return 1;
35
+ }
36
+
37
+ const entries = response.data as ExceptionEntry[];
38
+
39
+ if (args.global.json) {
40
+ console.log(JSON.stringify(entries, null, 2));
41
+ return 0;
42
+ }
43
+
44
+ if (entries.length === 0) {
45
+ console.log("(no exceptions)");
46
+ return 0;
47
+ }
48
+
49
+ for (const entry of entries) {
50
+ const ts = formatTimestamp(entry.timestamp);
51
+ console.log(`[${ts}] ${entry.text}`);
52
+ if (entry.description) {
53
+ console.log(` ${entry.description}`);
54
+ }
55
+ if (entry.stackTrace) {
56
+ console.log(entry.stackTrace);
57
+ }
58
+ }
59
+
60
+ return 0;
61
+ });
@@ -0,0 +1,67 @@
1
+ import { registerCommand } from "../cli/registry.ts";
2
+ import { DaemonClient } from "../daemon/client.ts";
3
+
4
+ registerCommand("hotpatch", 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 file = args.subcommand;
14
+ if (!file) {
15
+ console.error("No file specified");
16
+ console.error(" -> Try: ndbg hotpatch app.js");
17
+ return 1;
18
+ }
19
+
20
+ // Read the file contents
21
+ let source: string;
22
+ try {
23
+ source = await Bun.file(file).text();
24
+ } catch {
25
+ console.error(`Cannot read file: ${file}`);
26
+ return 1;
27
+ }
28
+
29
+ const hotpatchArgs: Record<string, unknown> = {
30
+ file,
31
+ source,
32
+ };
33
+
34
+ if (args.flags["dry-run"] === true) {
35
+ hotpatchArgs.dryRun = true;
36
+ }
37
+
38
+ const client = new DaemonClient(session);
39
+ const response = await client.request("hotpatch", hotpatchArgs);
40
+
41
+ if (!response.ok) {
42
+ console.error(`${response.error}`);
43
+ if (response.suggestion) console.error(` ${response.suggestion}`);
44
+ return 1;
45
+ }
46
+
47
+ const data = response.data as {
48
+ status: string;
49
+ callFrames?: unknown[];
50
+ exceptionDetails?: unknown;
51
+ };
52
+
53
+ if (args.global.json) {
54
+ console.log(JSON.stringify(data, null, 2));
55
+ return 0;
56
+ }
57
+
58
+ if (data.exceptionDetails) {
59
+ console.error(`hotpatch failed: ${JSON.stringify(data.exceptionDetails)}`);
60
+ return 1;
61
+ }
62
+
63
+ const dryRunLabel = args.flags["dry-run"] === true ? " (dry-run)" : "";
64
+ console.log(`hotpatch ${data.status}${dryRunLabel}: ${file}`);
65
+
66
+ return 0;
67
+ });
@@ -0,0 +1,69 @@
1
+ import { registerCommand } from "../cli/registry.ts";
2
+ import { DaemonClient } from "../daemon/client.ts";
3
+ import { spawnDaemon } from "../daemon/spawn.ts";
4
+
5
+ registerCommand("launch", async (args) => {
6
+ const session = args.global.session;
7
+ const brk = args.flags.brk === true;
8
+ const port = typeof args.flags.port === "string" ? parseInt(args.flags.port, 10) : undefined;
9
+ const timeout =
10
+ typeof args.flags.timeout === "string" ? parseInt(args.flags.timeout, 10) : undefined;
11
+
12
+ // Reconstruct the full command from subcommand + positionals.
13
+ // The parser treats the second non-flag word as subcommand, but for launch
14
+ // it should be part of the command to execute.
15
+ // e.g., "ndbg launch node app.js" -> subcommand="node", positionals=["app.js"]
16
+ // We need command = ["node", "app.js"]
17
+ const command = args.subcommand ? [args.subcommand, ...args.positionals] : [...args.positionals];
18
+
19
+ if (command.length === 0) {
20
+ console.error("No command specified");
21
+ console.error(" -> Try: ndbg launch --brk node app.js");
22
+ return 1;
23
+ }
24
+
25
+ // Check if daemon already running for this session
26
+ if (DaemonClient.isRunning(session)) {
27
+ console.error(`Session "${session}" is already active`);
28
+ console.error(` -> Try: ndbg stop --session ${session}`);
29
+ return 1;
30
+ }
31
+
32
+ // Spawn daemon
33
+ await spawnDaemon(session, { timeout });
34
+
35
+ // Send launch command to daemon
36
+ const client = new DaemonClient(session);
37
+ const response = await client.request("launch", { command, brk, port });
38
+
39
+ if (!response.ok) {
40
+ console.error(`${response.error}`);
41
+ if (response.suggestion) console.error(` ${response.suggestion}`);
42
+ return 1;
43
+ }
44
+
45
+ // Format output
46
+ const data = response.data as {
47
+ pid: number;
48
+ wsUrl: string;
49
+ paused: boolean;
50
+ pauseInfo?: { reason: string; url?: string; line?: number };
51
+ };
52
+
53
+ if (args.global.json) {
54
+ console.log(JSON.stringify(data, null, 2));
55
+ } else {
56
+ console.log(`Session "${session}" started (pid ${data.pid})`);
57
+ if (data.paused && data.pauseInfo) {
58
+ const col = data.pauseInfo.column !== undefined ? `:${data.pauseInfo.column + 1}` : "";
59
+ const loc = data.pauseInfo.url
60
+ ? `${data.pauseInfo.url}:${data.pauseInfo.line}${col}`
61
+ : "unknown";
62
+ console.log(`Paused at ${loc}`);
63
+ } else {
64
+ console.log("Running");
65
+ }
66
+ }
67
+
68
+ return 0;
69
+ });
@@ -0,0 +1,78 @@
1
+ import { registerCommand } from "../cli/registry.ts";
2
+ import { DaemonClient } from "../daemon/client.ts";
3
+
4
+ registerCommand("logpoint", 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 logpoint src/app.ts:42 "x =", x');
17
+ return 1;
18
+ }
19
+
20
+ // Parse file:line from the target
21
+ const lastColon = target.lastIndexOf(":");
22
+ if (lastColon === -1 || lastColon === 0) {
23
+ console.error(`Invalid logpoint target: "${target}"`);
24
+ console.error(' -> Try: ndbg logpoint src/app.ts:42 "x =", x');
25
+ return 1;
26
+ }
27
+
28
+ const file = target.slice(0, lastColon);
29
+ const line = parseInt(target.slice(lastColon + 1), 10);
30
+ if (Number.isNaN(line) || line <= 0) {
31
+ console.error(`Invalid line number in "${target}"`);
32
+ console.error(' -> Try: ndbg logpoint src/app.ts:42 "x =", x');
33
+ return 1;
34
+ }
35
+
36
+ // Template is the first positional argument (after the subcommand)
37
+ const template = args.positionals[0];
38
+ if (!template) {
39
+ console.error("No log template specified");
40
+ console.error(' -> Try: ndbg logpoint src/app.ts:42 "x =", x');
41
+ return 1;
42
+ }
43
+
44
+ const condition = typeof args.flags.condition === "string" ? args.flags.condition : undefined;
45
+ const maxEmissions =
46
+ typeof args.flags["max-emissions"] === "string"
47
+ ? parseInt(args.flags["max-emissions"], 10)
48
+ : undefined;
49
+
50
+ const client = new DaemonClient(session);
51
+ const response = await client.request("logpoint", {
52
+ file,
53
+ line,
54
+ template,
55
+ condition,
56
+ maxEmissions,
57
+ });
58
+
59
+ if (!response.ok) {
60
+ console.error(`${response.error}`);
61
+ if (response.suggestion) console.error(` ${response.suggestion}`);
62
+ return 1;
63
+ }
64
+
65
+ const data = response.data as {
66
+ ref: string;
67
+ location: { url: string; line: number; column?: number };
68
+ };
69
+
70
+ if (args.global.json) {
71
+ console.log(JSON.stringify(data, null, 2));
72
+ } else {
73
+ const loc = `${data.location.url}:${data.location.line}`;
74
+ console.log(`${data.ref} set at ${loc} (log: ${template})`);
75
+ }
76
+
77
+ return 0;
78
+ });
@@ -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("pause", 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("pause");
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,77 @@
1
+ import { registerCommand } from "../cli/registry.ts";
2
+ import { DaemonClient } from "../daemon/client.ts";
3
+
4
+ registerCommand("props", 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 ref specified");
16
+ console.error(" -> Try: ndbg props @v1");
17
+ return 1;
18
+ }
19
+
20
+ const propsArgs: Record<string, unknown> = {
21
+ ref,
22
+ };
23
+
24
+ if (args.flags.own === true || args.flags.own === false) {
25
+ propsArgs.own = args.flags.own;
26
+ }
27
+ if (args.flags.internal === true) {
28
+ propsArgs.internal = true;
29
+ }
30
+ if (args.flags.private === true) {
31
+ propsArgs.internal = true;
32
+ }
33
+ if (typeof args.flags.depth === "string") {
34
+ propsArgs.depth = parseInt(args.flags.depth, 10);
35
+ }
36
+
37
+ const client = new DaemonClient(session);
38
+ const response = await client.request("props", propsArgs);
39
+
40
+ if (!response.ok) {
41
+ console.error(`${response.error}`);
42
+ if (response.suggestion) console.error(` ${response.suggestion}`);
43
+ return 1;
44
+ }
45
+
46
+ const data = response.data as Array<{
47
+ ref?: string;
48
+ name: string;
49
+ type: string;
50
+ value: string;
51
+ isOwn?: boolean;
52
+ isAccessor?: boolean;
53
+ }>;
54
+
55
+ if (args.global.json) {
56
+ console.log(JSON.stringify(data, null, 2));
57
+ return 0;
58
+ }
59
+
60
+ if (data.length === 0) {
61
+ console.log("(no properties)");
62
+ return 0;
63
+ }
64
+
65
+ // Format output with aligned columns
66
+ const maxRefLen = Math.max(...data.map((p) => (p.ref ? p.ref.length : 0)));
67
+ const maxNameLen = Math.max(...data.map((p) => p.name.length));
68
+
69
+ for (const prop of data) {
70
+ const refCol = prop.ref ? prop.ref.padEnd(maxRefLen) : " ".repeat(maxRefLen);
71
+ const nameCol = prop.name.padEnd(maxNameLen);
72
+ const accessor = prop.isAccessor ? " [accessor]" : "";
73
+ console.log(`${refCol} ${nameCol} ${prop.value}${accessor}`);
74
+ }
75
+
76
+ return 0;
77
+ });
@@ -0,0 +1,36 @@
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("restart-frame", 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 frameRef = args.subcommand ?? undefined;
15
+
16
+ const client = new DaemonClient(session);
17
+ const response = await client.request("restart-frame", {
18
+ frameRef,
19
+ });
20
+
21
+ if (!response.ok) {
22
+ console.error(`${response.error}`);
23
+ if (response.suggestion) console.error(` ${response.suggestion}`);
24
+ return 1;
25
+ }
26
+
27
+ const data = response.data as SessionStatus | { status: string };
28
+
29
+ if (args.global.json) {
30
+ console.log(JSON.stringify(data, null, 2));
31
+ } else {
32
+ console.log("Frame restarted");
33
+ }
34
+
35
+ return 0;
36
+ });
@@ -0,0 +1,70 @@
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("run-to", 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
+ // Parse file:line from subcommand
15
+ // e.g., "ndbg run-to src/file.ts:42" -> subcommand = "src/file.ts:42"
16
+ const target = args.subcommand ?? args.positionals[0];
17
+ if (!target) {
18
+ console.error("No target specified");
19
+ console.error(" -> Try: ndbg run-to src/file.ts:42");
20
+ return 1;
21
+ }
22
+
23
+ const lastColon = target.lastIndexOf(":");
24
+ if (lastColon === -1 || lastColon === 0) {
25
+ console.error(`Invalid target format: "${target}"`);
26
+ console.error(" -> Expected: <file>:<line>");
27
+ return 1;
28
+ }
29
+
30
+ const file = target.slice(0, lastColon);
31
+ const line = parseInt(target.slice(lastColon + 1), 10);
32
+ if (Number.isNaN(line) || line <= 0) {
33
+ console.error(`Invalid line number in "${target}"`);
34
+ console.error(" -> Expected: <file>:<line>");
35
+ return 1;
36
+ }
37
+
38
+ const client = new DaemonClient(session);
39
+ const response = await client.request("run-to", { file, line });
40
+
41
+ if (!response.ok) {
42
+ console.error(`${response.error}`);
43
+ if (response.suggestion) console.error(` ${response.suggestion}`);
44
+ return 1;
45
+ }
46
+
47
+ const data = response.data as SessionStatus;
48
+
49
+ if (args.global.json) {
50
+ console.log(JSON.stringify(data, null, 2));
51
+ } else {
52
+ printStatus(data);
53
+ }
54
+
55
+ return 0;
56
+ });
57
+
58
+ function printStatus(data: SessionStatus): void {
59
+ if (data.state === "paused" && data.pauseInfo) {
60
+ const col = data.pauseInfo.column !== undefined ? `:${data.pauseInfo.column + 1}` : "";
61
+ const loc = data.pauseInfo.url
62
+ ? `${data.pauseInfo.url}:${(data.pauseInfo.line ?? 0) + 1}${col}`
63
+ : "unknown";
64
+ console.log(`Paused at ${loc} (${data.pauseInfo.reason})`);
65
+ } else if (data.state === "running") {
66
+ console.log("Running");
67
+ } else {
68
+ console.log(`${data.state}`);
69
+ }
70
+ }
@@ -0,0 +1,57 @@
1
+ import { registerCommand } from "../cli/registry.ts";
2
+ import { DaemonClient } from "../daemon/client.ts";
3
+
4
+ registerCommand("scripts", 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
+
15
+ const scriptsArgs: Record<string, unknown> = {};
16
+
17
+ // Accept filter from --filter flag or from subcommand
18
+ if (typeof args.flags.filter === "string") {
19
+ scriptsArgs.filter = args.flags.filter;
20
+ } else if (args.subcommand) {
21
+ scriptsArgs.filter = args.subcommand;
22
+ }
23
+
24
+ const response = await client.request("scripts", scriptsArgs);
25
+
26
+ if (!response.ok) {
27
+ console.error(`${response.error}`);
28
+ if (response.suggestion) console.error(` ${response.suggestion}`);
29
+ return 1;
30
+ }
31
+
32
+ const data = response.data as Array<{
33
+ scriptId: string;
34
+ url: string;
35
+ sourceMapURL?: string;
36
+ }>;
37
+
38
+ if (args.global.json) {
39
+ console.log(JSON.stringify(data, null, 2));
40
+ return 0;
41
+ }
42
+
43
+ if (data.length === 0) {
44
+ console.log("No scripts loaded");
45
+ return 0;
46
+ }
47
+
48
+ for (const script of data) {
49
+ let line = `${script.scriptId} ${script.url}`;
50
+ if (script.sourceMapURL) {
51
+ line += ` (sourcemap: ${script.sourceMapURL})`;
52
+ }
53
+ console.log(line);
54
+ }
55
+
56
+ return 0;
57
+ });
@@ -0,0 +1,73 @@
1
+ import { registerCommand } from "../cli/registry.ts";
2
+ import { DaemonClient } from "../daemon/client.ts";
3
+
4
+ registerCommand("search", 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
+ // Query from subcommand + positionals
14
+ const parts: string[] = [];
15
+ if (args.subcommand) {
16
+ parts.push(args.subcommand);
17
+ }
18
+ for (const p of args.positionals) {
19
+ parts.push(p);
20
+ }
21
+ const query = parts.join(" ");
22
+
23
+ if (!query) {
24
+ console.error("No search query specified");
25
+ console.error(" -> Try: ndbg search <query> [--regex] [--case-sensitive]");
26
+ return 1;
27
+ }
28
+
29
+ const client = new DaemonClient(session);
30
+
31
+ const searchArgs: Record<string, unknown> = { query };
32
+
33
+ if (args.flags.regex === true) {
34
+ searchArgs.isRegex = true;
35
+ }
36
+ if (args.flags["case-sensitive"] === true) {
37
+ searchArgs.caseSensitive = true;
38
+ }
39
+ if (typeof args.flags.file === "string") {
40
+ searchArgs.scriptId = args.flags.file;
41
+ }
42
+
43
+ const response = await client.request("search", searchArgs);
44
+
45
+ if (!response.ok) {
46
+ console.error(`${response.error}`);
47
+ if (response.suggestion) console.error(` ${response.suggestion}`);
48
+ return 1;
49
+ }
50
+
51
+ const data = response.data as Array<{
52
+ url: string;
53
+ line: number;
54
+ column: number;
55
+ content: string;
56
+ }>;
57
+
58
+ if (args.global.json) {
59
+ console.log(JSON.stringify(data, null, 2));
60
+ return 0;
61
+ }
62
+
63
+ if (data.length === 0) {
64
+ console.log("No matches found");
65
+ return 0;
66
+ }
67
+
68
+ for (const match of data) {
69
+ console.log(`${match.url}:${match.line}: ${match.content}`);
70
+ }
71
+
72
+ return 0;
73
+ });