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,316 @@
1
+ import { z } from "@zod/mini";
2
+
3
+ // ── Request schemas (one per command) ──────────────────────────────
4
+
5
+ const PingRequest = z.object({ cmd: z.literal("ping") });
6
+
7
+ const LaunchRequest = z.object({
8
+ cmd: z.literal("launch"),
9
+ args: z.object({
10
+ command: z.array(z.string()),
11
+ brk: z.optional(z.boolean()),
12
+ port: z.optional(z.number()),
13
+ }),
14
+ });
15
+
16
+ const AttachRequest = z.object({
17
+ cmd: z.literal("attach"),
18
+ args: z.object({
19
+ target: z.string(),
20
+ }),
21
+ });
22
+
23
+ const StatusRequest = z.object({ cmd: z.literal("status") });
24
+
25
+ const StateRequest = z.object({
26
+ cmd: z.literal("state"),
27
+ args: z.object({
28
+ vars: z.optional(z.boolean()),
29
+ stack: z.optional(z.boolean()),
30
+ breakpoints: z.optional(z.boolean()),
31
+ code: z.optional(z.boolean()),
32
+ compact: z.optional(z.boolean()),
33
+ depth: z.optional(z.number()),
34
+ lines: z.optional(z.number()),
35
+ frame: z.optional(z.string()),
36
+ allScopes: z.optional(z.boolean()),
37
+ generated: z.optional(z.boolean()),
38
+ }),
39
+ });
40
+
41
+ const ContinueRequest = z.object({ cmd: z.literal("continue") });
42
+
43
+ const StepRequest = z.object({
44
+ cmd: z.literal("step"),
45
+ args: z.object({
46
+ mode: z.optional(z.union([z.literal("over"), z.literal("into"), z.literal("out")])),
47
+ }),
48
+ });
49
+
50
+ const PauseRequest = z.object({ cmd: z.literal("pause") });
51
+
52
+ const RunToRequest = z.object({
53
+ cmd: z.literal("run-to"),
54
+ args: z.object({
55
+ file: z.string(),
56
+ line: z.number(),
57
+ }),
58
+ });
59
+
60
+ const BreakRequest = z.object({
61
+ cmd: z.literal("break"),
62
+ args: z.object({
63
+ file: z.string(),
64
+ line: z.number(),
65
+ condition: z.optional(z.string()),
66
+ hitCount: z.optional(z.number()),
67
+ urlRegex: z.optional(z.string()),
68
+ }),
69
+ });
70
+
71
+ const BreakRmRequest = z.object({
72
+ cmd: z.literal("break-rm"),
73
+ args: z.object({
74
+ ref: z.string(),
75
+ }),
76
+ });
77
+
78
+ const BreakLsRequest = z.object({ cmd: z.literal("break-ls") });
79
+
80
+ const LogpointRequest = z.object({
81
+ cmd: z.literal("logpoint"),
82
+ args: z.object({
83
+ file: z.string(),
84
+ line: z.number(),
85
+ template: z.string(),
86
+ condition: z.optional(z.string()),
87
+ maxEmissions: z.optional(z.number()),
88
+ }),
89
+ });
90
+
91
+ const CatchRequest = z.object({
92
+ cmd: z.literal("catch"),
93
+ args: z.object({
94
+ mode: z.union([
95
+ z.literal("all"),
96
+ z.literal("uncaught"),
97
+ z.literal("caught"),
98
+ z.literal("none"),
99
+ ]),
100
+ }),
101
+ });
102
+
103
+ const SourceRequest = z.object({
104
+ cmd: z.literal("source"),
105
+ args: z.object({
106
+ file: z.optional(z.string()),
107
+ lines: z.optional(z.number()),
108
+ all: z.optional(z.boolean()),
109
+ generated: z.optional(z.boolean()),
110
+ }),
111
+ });
112
+
113
+ const ScriptsRequest = z.object({
114
+ cmd: z.literal("scripts"),
115
+ args: z.object({
116
+ filter: z.optional(z.string()),
117
+ }),
118
+ });
119
+
120
+ const StackRequest = z.object({
121
+ cmd: z.literal("stack"),
122
+ args: z.object({
123
+ asyncDepth: z.optional(z.number()),
124
+ generated: z.optional(z.boolean()),
125
+ }),
126
+ });
127
+
128
+ const SearchRequest = z.object({
129
+ cmd: z.literal("search"),
130
+ args: z.object({
131
+ query: z.string(),
132
+ scriptId: z.optional(z.string()),
133
+ isRegex: z.optional(z.boolean()),
134
+ caseSensitive: z.optional(z.boolean()),
135
+ }),
136
+ });
137
+
138
+ const ConsoleRequest = z.object({
139
+ cmd: z.literal("console"),
140
+ args: z.object({
141
+ level: z.optional(z.string()),
142
+ since: z.optional(z.number()),
143
+ clear: z.optional(z.boolean()),
144
+ }),
145
+ });
146
+
147
+ const ExceptionsRequest = z.object({
148
+ cmd: z.literal("exceptions"),
149
+ args: z.object({
150
+ since: z.optional(z.number()),
151
+ }),
152
+ });
153
+
154
+ const EvalRequest = z.object({
155
+ cmd: z.literal("eval"),
156
+ args: z.object({
157
+ expression: z.string(),
158
+ frame: z.optional(z.string()),
159
+ awaitPromise: z.optional(z.boolean()),
160
+ throwOnSideEffect: z.optional(z.boolean()),
161
+ timeout: z.optional(z.number()),
162
+ }),
163
+ });
164
+
165
+ const VarsRequest = z.object({
166
+ cmd: z.literal("vars"),
167
+ args: z.object({
168
+ frame: z.optional(z.string()),
169
+ names: z.optional(z.array(z.string())),
170
+ allScopes: z.optional(z.boolean()),
171
+ }),
172
+ });
173
+
174
+ const PropsRequest = z.object({
175
+ cmd: z.literal("props"),
176
+ args: z.object({
177
+ ref: z.string(),
178
+ own: z.optional(z.boolean()),
179
+ internal: z.optional(z.boolean()),
180
+ depth: z.optional(z.number()),
181
+ }),
182
+ });
183
+
184
+ const BlackboxRequest = z.object({
185
+ cmd: z.literal("blackbox"),
186
+ args: z.object({
187
+ patterns: z.array(z.string()),
188
+ }),
189
+ });
190
+
191
+ const BlackboxLsRequest = z.object({ cmd: z.literal("blackbox-ls") });
192
+
193
+ const BlackboxRmRequest = z.object({
194
+ cmd: z.literal("blackbox-rm"),
195
+ args: z.object({
196
+ patterns: z.array(z.string()),
197
+ }),
198
+ });
199
+
200
+ const SetRequest = z.object({
201
+ cmd: z.literal("set"),
202
+ args: z.object({
203
+ name: z.string(),
204
+ value: z.string(),
205
+ frame: z.optional(z.string()),
206
+ }),
207
+ });
208
+
209
+ const SetReturnRequest = z.object({
210
+ cmd: z.literal("set-return"),
211
+ args: z.object({
212
+ value: z.string(),
213
+ }),
214
+ });
215
+
216
+ const HotpatchRequest = z.object({
217
+ cmd: z.literal("hotpatch"),
218
+ args: z.object({
219
+ file: z.string(),
220
+ source: z.string(),
221
+ dryRun: z.optional(z.boolean()),
222
+ }),
223
+ });
224
+
225
+ const BreakToggleRequest = z.object({
226
+ cmd: z.literal("break-toggle"),
227
+ args: z.object({
228
+ ref: z.string(),
229
+ }),
230
+ });
231
+
232
+ const BreakableRequest = z.object({
233
+ cmd: z.literal("breakable"),
234
+ args: z.object({
235
+ file: z.string(),
236
+ startLine: z.number(),
237
+ endLine: z.number(),
238
+ }),
239
+ });
240
+
241
+ const RestartFrameRequest = z.object({
242
+ cmd: z.literal("restart-frame"),
243
+ args: z.object({
244
+ frameRef: z.optional(z.string()),
245
+ }),
246
+ });
247
+
248
+ const SourcemapRequest = z.object({
249
+ cmd: z.literal("sourcemap"),
250
+ args: z.object({
251
+ file: z.optional(z.string()),
252
+ }),
253
+ });
254
+
255
+ const SourcemapDisableRequest = z.object({ cmd: z.literal("sourcemap-disable") });
256
+
257
+ const StopRequest = z.object({ cmd: z.literal("stop") });
258
+
259
+ // ── Union of all requests (discriminated on cmd) ───────────────────
260
+
261
+ export const DaemonRequestSchema = z.union([
262
+ PingRequest,
263
+ LaunchRequest,
264
+ AttachRequest,
265
+ StatusRequest,
266
+ StateRequest,
267
+ ContinueRequest,
268
+ StepRequest,
269
+ PauseRequest,
270
+ RunToRequest,
271
+ BreakRequest,
272
+ BreakRmRequest,
273
+ BreakLsRequest,
274
+ LogpointRequest,
275
+ CatchRequest,
276
+ SourceRequest,
277
+ ScriptsRequest,
278
+ StackRequest,
279
+ SearchRequest,
280
+ ConsoleRequest,
281
+ ExceptionsRequest,
282
+ EvalRequest,
283
+ VarsRequest,
284
+ PropsRequest,
285
+ BlackboxRequest,
286
+ BlackboxLsRequest,
287
+ BlackboxRmRequest,
288
+ SetRequest,
289
+ SetReturnRequest,
290
+ HotpatchRequest,
291
+ BreakToggleRequest,
292
+ BreakableRequest,
293
+ RestartFrameRequest,
294
+ SourcemapRequest,
295
+ SourcemapDisableRequest,
296
+ StopRequest,
297
+ ]);
298
+
299
+ export type DaemonRequest = z.infer<typeof DaemonRequestSchema>;
300
+
301
+ // ── Response schema ────────────────────────────────────────────────
302
+
303
+ const SuccessResponse = z.object({
304
+ ok: z.literal(true),
305
+ data: z.optional(z.unknown()),
306
+ });
307
+
308
+ const ErrorResponse = z.object({
309
+ ok: z.literal(false),
310
+ error: z.optional(z.string()),
311
+ suggestion: z.optional(z.string()),
312
+ });
313
+
314
+ export const DaemonResponseSchema = z.union([SuccessResponse, ErrorResponse]);
315
+
316
+ export type DaemonResponse = z.infer<typeof DaemonResponseSchema>;
@@ -0,0 +1,120 @@
1
+ export type RefType = "v" | "f" | "o" | "BP" | "LP" | "HS";
2
+
3
+ export interface RefEntry {
4
+ ref: string;
5
+ type: RefType;
6
+ remoteId: string;
7
+ name?: string;
8
+ meta?: Record<string, unknown>;
9
+ }
10
+
11
+ const PREFIXES: Record<RefType, string> = {
12
+ v: "@v",
13
+ f: "@f",
14
+ o: "@o",
15
+ BP: "BP#",
16
+ LP: "LP#",
17
+ HS: "HS#",
18
+ };
19
+
20
+ export class RefTable {
21
+ private entries = new Map<string, RefEntry>();
22
+ private counters: Record<RefType, number> = {
23
+ v: 1,
24
+ f: 0,
25
+ o: 1,
26
+ BP: 1,
27
+ LP: 1,
28
+ HS: 1,
29
+ };
30
+
31
+ addVar(remoteId: string, name?: string, meta?: Record<string, unknown>): string {
32
+ return this.add("v", remoteId, name, meta);
33
+ }
34
+
35
+ addFrame(remoteId: string, name?: string, meta?: Record<string, unknown>): string {
36
+ return this.add("f", remoteId, name, meta);
37
+ }
38
+
39
+ addObject(remoteId: string, name?: string, meta?: Record<string, unknown>): string {
40
+ return this.add("o", remoteId, name, meta);
41
+ }
42
+
43
+ addBreakpoint(remoteId: string, meta?: Record<string, unknown>): string {
44
+ return this.add("BP", remoteId, undefined, meta);
45
+ }
46
+
47
+ addLogpoint(remoteId: string, meta?: Record<string, unknown>): string {
48
+ return this.add("LP", remoteId, undefined, meta);
49
+ }
50
+
51
+ addHeapSnapshot(remoteId: string, meta?: Record<string, unknown>): string {
52
+ return this.add("HS", remoteId, undefined, meta);
53
+ }
54
+
55
+ resolve(ref: string): RefEntry | undefined {
56
+ return this.entries.get(ref);
57
+ }
58
+
59
+ resolveId(ref: string): string | undefined {
60
+ return this.entries.get(ref)?.remoteId;
61
+ }
62
+
63
+ clearVolatile(): void {
64
+ for (const [key, entry] of this.entries) {
65
+ if (entry.type === "v" || entry.type === "f") {
66
+ this.entries.delete(key);
67
+ }
68
+ }
69
+ this.counters.v = 1;
70
+ this.counters.f = 0;
71
+ }
72
+
73
+ clearObjects(): void {
74
+ for (const [key, entry] of this.entries) {
75
+ if (entry.type === "o") {
76
+ this.entries.delete(key);
77
+ }
78
+ }
79
+ this.counters.o = 1;
80
+ }
81
+
82
+ clearAll(): void {
83
+ this.entries.clear();
84
+ this.counters = { v: 1, f: 0, o: 1, BP: 1, LP: 1, HS: 1 };
85
+ }
86
+
87
+ list(type: RefType): RefEntry[] {
88
+ const result: RefEntry[] = [];
89
+ for (const entry of this.entries.values()) {
90
+ if (entry.type === type) {
91
+ result.push(entry);
92
+ }
93
+ }
94
+ return result;
95
+ }
96
+
97
+ remove(ref: string): boolean {
98
+ return this.entries.delete(ref);
99
+ }
100
+
101
+ private add(
102
+ type: RefType,
103
+ remoteId: string,
104
+ name?: string,
105
+ meta?: Record<string, unknown>,
106
+ ): string {
107
+ const num = this.counters[type];
108
+ this.counters[type] = num + 1;
109
+ const ref = `${PREFIXES[type]}${num}`;
110
+ const entry: RefEntry = { ref, type, remoteId };
111
+ if (name !== undefined) {
112
+ entry.name = name;
113
+ }
114
+ if (meta !== undefined) {
115
+ entry.meta = meta;
116
+ }
117
+ this.entries.set(ref, entry);
118
+ return ref;
119
+ }
120
+ }
@@ -0,0 +1,24 @@
1
+ import type { RefTable, RefType } from "./ref-table.ts";
2
+
3
+ const REF_PATTERN = /^@([vfo])(\d+)$/;
4
+ const HASH_PATTERN = /^(BP|LP|HS)#(\d+)$/;
5
+ const INTERPOLATION_PATTERN = /@[vfo]\d+|(?:BP|LP|HS)#\d+/g;
6
+
7
+ export function parseRef(ref: string): { type: RefType; num: number } | null {
8
+ const atMatch = REF_PATTERN.exec(ref);
9
+ if (atMatch?.[1] && atMatch[2]) {
10
+ return { type: atMatch[1] as RefType, num: Number.parseInt(atMatch[2], 10) };
11
+ }
12
+ const hashMatch = HASH_PATTERN.exec(ref);
13
+ if (hashMatch?.[1] && hashMatch[2]) {
14
+ return { type: hashMatch[1] as RefType, num: Number.parseInt(hashMatch[2], 10) };
15
+ }
16
+ return null;
17
+ }
18
+
19
+ export function interpolateRefs(expr: string, table: RefTable): string {
20
+ return expr.replace(INTERPOLATION_PATTERN, (match) => {
21
+ const remoteId = table.resolveId(match);
22
+ return remoteId ?? match;
23
+ });
24
+ }