agent-dbg 0.1.2 → 0.1.4
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 +29 -1
- package/.claude/skills/agent-dbg/SKILL.md +2 -0
- package/.claude/skills/agent-dbg/references/commands.md +2 -1
- package/TODO.md +299 -0
- package/demo/DEMO.md +71 -0
- package/demo/order-processor.js +35 -0
- package/dist/main.js +1480 -256
- package/package.json +3 -1
- package/src/commands/attach.ts +6 -5
- package/src/commands/break-fn.ts +41 -0
- package/src/commands/launch.ts +5 -6
- package/src/commands/logs.ts +58 -16
- package/src/daemon/client.ts +27 -5
- package/src/daemon/entry.ts +94 -46
- package/src/daemon/logger.ts +51 -0
- package/src/daemon/paths.ts +4 -0
- package/src/daemon/server.ts +76 -35
- package/src/daemon/session-breakpoints.ts +2 -1
- package/src/daemon/session-mutation.ts +1 -0
- package/src/daemon/session.ts +50 -10
- package/src/daemon/spawn.ts +47 -8
- package/src/dap/client.ts +252 -0
- package/src/dap/session.ts +1151 -0
- package/src/formatter/logs.ts +15 -0
- package/src/main.ts +1 -0
- package/src/protocol/messages.ts +12 -0
- package/tests/fixtures/dap/hello +0 -0
- package/tests/fixtures/dap/hello.c +8 -0
- package/tests/fixtures/dap/hello.dSYM/Contents/Info.plist +20 -0
- package/tests/fixtures/dap/hello.dSYM/Contents/Resources/DWARF/hello +0 -0
- package/tests/fixtures/dap/hello.dSYM/Contents/Resources/Relocations/aarch64/hello.yml +5 -0
- package/tests/fixtures/hotpatch-active-fn.js +7 -0
- package/tests/integration/daemon-logging.test.ts +155 -0
- package/tests/integration/mutation.test.ts +33 -0
- package/tests/unit/daemon-logger.test.ts +117 -0
- package/tests/unit/daemon.test.ts +60 -0
package/dist/main.js
CHANGED
|
@@ -12,6 +12,1115 @@ var __export = (target, all) => {
|
|
|
12
12
|
};
|
|
13
13
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
14
14
|
|
|
15
|
+
// src/refs/ref-table.ts
|
|
16
|
+
class RefTable {
|
|
17
|
+
entries = new Map;
|
|
18
|
+
counters = {
|
|
19
|
+
v: 1,
|
|
20
|
+
f: 0,
|
|
21
|
+
o: 1,
|
|
22
|
+
BP: 1,
|
|
23
|
+
LP: 1,
|
|
24
|
+
HS: 1
|
|
25
|
+
};
|
|
26
|
+
addVar(remoteId, name, meta) {
|
|
27
|
+
return this.add("v", remoteId, name, meta);
|
|
28
|
+
}
|
|
29
|
+
addFrame(remoteId, name, meta) {
|
|
30
|
+
return this.add("f", remoteId, name, meta);
|
|
31
|
+
}
|
|
32
|
+
addObject(remoteId, name, meta) {
|
|
33
|
+
return this.add("o", remoteId, name, meta);
|
|
34
|
+
}
|
|
35
|
+
addBreakpoint(remoteId, meta) {
|
|
36
|
+
return this.add("BP", remoteId, undefined, meta);
|
|
37
|
+
}
|
|
38
|
+
addLogpoint(remoteId, meta) {
|
|
39
|
+
return this.add("LP", remoteId, undefined, meta);
|
|
40
|
+
}
|
|
41
|
+
addHeapSnapshot(remoteId, meta) {
|
|
42
|
+
return this.add("HS", remoteId, undefined, meta);
|
|
43
|
+
}
|
|
44
|
+
resolve(ref) {
|
|
45
|
+
return this.entries.get(ref);
|
|
46
|
+
}
|
|
47
|
+
resolveId(ref) {
|
|
48
|
+
return this.entries.get(ref)?.remoteId;
|
|
49
|
+
}
|
|
50
|
+
clearVolatile() {
|
|
51
|
+
for (const [key, entry] of this.entries) {
|
|
52
|
+
if (entry.type === "v" || entry.type === "f") {
|
|
53
|
+
this.entries.delete(key);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
this.counters.v = 1;
|
|
57
|
+
this.counters.f = 0;
|
|
58
|
+
}
|
|
59
|
+
clearObjects() {
|
|
60
|
+
for (const [key, entry] of this.entries) {
|
|
61
|
+
if (entry.type === "o") {
|
|
62
|
+
this.entries.delete(key);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
this.counters.o = 1;
|
|
66
|
+
}
|
|
67
|
+
clearAll() {
|
|
68
|
+
this.entries.clear();
|
|
69
|
+
this.counters = { v: 1, f: 0, o: 1, BP: 1, LP: 1, HS: 1 };
|
|
70
|
+
}
|
|
71
|
+
list(type) {
|
|
72
|
+
const result = [];
|
|
73
|
+
for (const entry of this.entries.values()) {
|
|
74
|
+
if (entry.type === type) {
|
|
75
|
+
result.push(entry);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
remove(ref) {
|
|
81
|
+
return this.entries.delete(ref);
|
|
82
|
+
}
|
|
83
|
+
add(type, remoteId, name, meta) {
|
|
84
|
+
const num = this.counters[type];
|
|
85
|
+
this.counters[type] = num + 1;
|
|
86
|
+
const ref = `${PREFIXES[type]}${num}`;
|
|
87
|
+
const entry = { ref, type, remoteId };
|
|
88
|
+
if (name !== undefined) {
|
|
89
|
+
entry.name = name;
|
|
90
|
+
}
|
|
91
|
+
if (meta !== undefined) {
|
|
92
|
+
entry.meta = meta;
|
|
93
|
+
}
|
|
94
|
+
this.entries.set(ref, entry);
|
|
95
|
+
return ref;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
var PREFIXES;
|
|
99
|
+
var init_ref_table = __esm(() => {
|
|
100
|
+
PREFIXES = {
|
|
101
|
+
v: "@v",
|
|
102
|
+
f: "@f",
|
|
103
|
+
o: "@o",
|
|
104
|
+
BP: "BP#",
|
|
105
|
+
LP: "LP#",
|
|
106
|
+
HS: "HS#"
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// src/dap/client.ts
|
|
111
|
+
class DapClient {
|
|
112
|
+
proc;
|
|
113
|
+
nextSeq = 1;
|
|
114
|
+
pending = new Map;
|
|
115
|
+
listeners = new Map;
|
|
116
|
+
isConnected = false;
|
|
117
|
+
buffer = "";
|
|
118
|
+
constructor(proc) {
|
|
119
|
+
this.proc = proc;
|
|
120
|
+
this.isConnected = true;
|
|
121
|
+
this.readLoop();
|
|
122
|
+
this.drainStderr();
|
|
123
|
+
}
|
|
124
|
+
static spawn(command) {
|
|
125
|
+
const [cmd, ...args] = command;
|
|
126
|
+
if (!cmd) {
|
|
127
|
+
throw new Error("DapClient.spawn: command array must not be empty");
|
|
128
|
+
}
|
|
129
|
+
const proc = Bun.spawn([cmd, ...args], {
|
|
130
|
+
stdin: "pipe",
|
|
131
|
+
stdout: "pipe",
|
|
132
|
+
stderr: "pipe"
|
|
133
|
+
});
|
|
134
|
+
return new DapClient(proc);
|
|
135
|
+
}
|
|
136
|
+
async send(command, args) {
|
|
137
|
+
if (!this.isConnected) {
|
|
138
|
+
throw new Error("DAP client is not connected");
|
|
139
|
+
}
|
|
140
|
+
const seq = this.nextSeq++;
|
|
141
|
+
const request = {
|
|
142
|
+
seq,
|
|
143
|
+
type: "request",
|
|
144
|
+
command
|
|
145
|
+
};
|
|
146
|
+
if (args !== undefined) {
|
|
147
|
+
request.arguments = args;
|
|
148
|
+
}
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
const timer = setTimeout(() => {
|
|
151
|
+
this.pending.delete(seq);
|
|
152
|
+
reject(new Error(`DAP request timed out: ${command} (seq=${seq})`));
|
|
153
|
+
}, DEFAULT_TIMEOUT_MS);
|
|
154
|
+
this.pending.set(seq, { resolve, reject, timer });
|
|
155
|
+
this.writeMessage(request);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
on(event, handler) {
|
|
159
|
+
let handlers = this.listeners.get(event);
|
|
160
|
+
if (!handlers) {
|
|
161
|
+
handlers = new Set;
|
|
162
|
+
this.listeners.set(event, handlers);
|
|
163
|
+
}
|
|
164
|
+
handlers.add(handler);
|
|
165
|
+
}
|
|
166
|
+
off(event, handler) {
|
|
167
|
+
const handlers = this.listeners.get(event);
|
|
168
|
+
if (handlers) {
|
|
169
|
+
handlers.delete(handler);
|
|
170
|
+
if (handlers.size === 0) {
|
|
171
|
+
this.listeners.delete(event);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
disconnect() {
|
|
176
|
+
if (!this.isConnected) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
this.isConnected = false;
|
|
180
|
+
const error = new Error("DAP client disconnected");
|
|
181
|
+
for (const [id, pending] of this.pending) {
|
|
182
|
+
clearTimeout(pending.timer);
|
|
183
|
+
pending.reject(error);
|
|
184
|
+
this.pending.delete(id);
|
|
185
|
+
}
|
|
186
|
+
this.listeners.clear();
|
|
187
|
+
try {
|
|
188
|
+
this.proc.stdin.end();
|
|
189
|
+
} catch {}
|
|
190
|
+
try {
|
|
191
|
+
this.proc.kill();
|
|
192
|
+
} catch {}
|
|
193
|
+
}
|
|
194
|
+
get connected() {
|
|
195
|
+
return this.isConnected;
|
|
196
|
+
}
|
|
197
|
+
get pid() {
|
|
198
|
+
return this.proc.pid;
|
|
199
|
+
}
|
|
200
|
+
writeMessage(msg) {
|
|
201
|
+
const json = JSON.stringify(msg);
|
|
202
|
+
const header = `Content-Length: ${Buffer.byteLength(json, "utf-8")}\r
|
|
203
|
+
\r
|
|
204
|
+
`;
|
|
205
|
+
try {
|
|
206
|
+
this.proc.stdin.write(header + json);
|
|
207
|
+
} catch {
|
|
208
|
+
this.isConnected = false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async readLoop() {
|
|
212
|
+
const reader = this.proc.stdout.getReader();
|
|
213
|
+
const decoder = new TextDecoder;
|
|
214
|
+
try {
|
|
215
|
+
while (this.isConnected) {
|
|
216
|
+
const { done, value } = await reader.read();
|
|
217
|
+
if (done)
|
|
218
|
+
break;
|
|
219
|
+
this.buffer += decoder.decode(value, { stream: true });
|
|
220
|
+
this.processBuffer();
|
|
221
|
+
}
|
|
222
|
+
} catch {} finally {
|
|
223
|
+
this.isConnected = false;
|
|
224
|
+
const error = new Error("DAP adapter process terminated");
|
|
225
|
+
for (const [id, pending] of this.pending) {
|
|
226
|
+
clearTimeout(pending.timer);
|
|
227
|
+
pending.reject(error);
|
|
228
|
+
this.pending.delete(id);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
processBuffer() {
|
|
233
|
+
while (true) {
|
|
234
|
+
const headerEnd = this.buffer.indexOf(`\r
|
|
235
|
+
\r
|
|
236
|
+
`);
|
|
237
|
+
if (headerEnd === -1)
|
|
238
|
+
return;
|
|
239
|
+
const header = this.buffer.slice(0, headerEnd);
|
|
240
|
+
const match = /Content-Length:\s*(\d+)/i.exec(header);
|
|
241
|
+
if (!match?.[1]) {
|
|
242
|
+
this.buffer = this.buffer.slice(headerEnd + 4);
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const contentLength = parseInt(match[1], 10);
|
|
246
|
+
const bodyStart = headerEnd + 4;
|
|
247
|
+
const bodyEnd = bodyStart + contentLength;
|
|
248
|
+
if (this.buffer.length < bodyEnd)
|
|
249
|
+
return;
|
|
250
|
+
const body = this.buffer.slice(bodyStart, bodyEnd);
|
|
251
|
+
this.buffer = this.buffer.slice(bodyEnd);
|
|
252
|
+
this.handleMessage(body);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
handleMessage(data) {
|
|
256
|
+
let parsed;
|
|
257
|
+
try {
|
|
258
|
+
parsed = JSON.parse(data);
|
|
259
|
+
} catch {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (parsed.type === "response") {
|
|
263
|
+
const response = parsed;
|
|
264
|
+
const pending = this.pending.get(response.request_seq);
|
|
265
|
+
if (!pending)
|
|
266
|
+
return;
|
|
267
|
+
this.pending.delete(response.request_seq);
|
|
268
|
+
clearTimeout(pending.timer);
|
|
269
|
+
if (!response.success) {
|
|
270
|
+
pending.reject(new Error(`DAP error (${response.command}): ${response.message ?? "unknown error"}`));
|
|
271
|
+
} else {
|
|
272
|
+
pending.resolve(response);
|
|
273
|
+
}
|
|
274
|
+
} else if (parsed.type === "event") {
|
|
275
|
+
const event = parsed;
|
|
276
|
+
const handlers = this.listeners.get(event.event);
|
|
277
|
+
if (handlers) {
|
|
278
|
+
for (const handler of handlers) {
|
|
279
|
+
handler(event.body);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
async drainStderr() {
|
|
285
|
+
const reader = this.proc.stderr.getReader();
|
|
286
|
+
try {
|
|
287
|
+
while (true) {
|
|
288
|
+
const { done } = await reader.read();
|
|
289
|
+
if (done)
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
} catch {}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
var DEFAULT_TIMEOUT_MS = 30000;
|
|
296
|
+
|
|
297
|
+
// src/dap/session.ts
|
|
298
|
+
function resolveAdapterCommand(runtime) {
|
|
299
|
+
switch (runtime) {
|
|
300
|
+
case "lldb":
|
|
301
|
+
case "lldb-dap": {
|
|
302
|
+
const brewPath = "/opt/homebrew/opt/llvm/bin/lldb-dap";
|
|
303
|
+
return [brewPath];
|
|
304
|
+
}
|
|
305
|
+
case "codelldb":
|
|
306
|
+
return ["codelldb", "--port", "0"];
|
|
307
|
+
default:
|
|
308
|
+
return [runtime];
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
class DapSession {
|
|
313
|
+
dap = null;
|
|
314
|
+
refs = new RefTable;
|
|
315
|
+
_state = "idle";
|
|
316
|
+
_pauseInfo = null;
|
|
317
|
+
_session;
|
|
318
|
+
_runtime;
|
|
319
|
+
_startTime = Date.now();
|
|
320
|
+
_threadId = 1;
|
|
321
|
+
_stackFrames = [];
|
|
322
|
+
_consoleMessages = [];
|
|
323
|
+
_exceptionEntries = [];
|
|
324
|
+
capabilities = {};
|
|
325
|
+
breakpoints = new Map;
|
|
326
|
+
allBreakpoints = [];
|
|
327
|
+
functionBreakpoints = [];
|
|
328
|
+
stoppedWaiter = null;
|
|
329
|
+
_stackFetchPromise = null;
|
|
330
|
+
constructor(session, runtime) {
|
|
331
|
+
this._session = session;
|
|
332
|
+
this._runtime = runtime;
|
|
333
|
+
}
|
|
334
|
+
async launch(command, options = {}) {
|
|
335
|
+
if (this._state !== "idle") {
|
|
336
|
+
throw new Error("Session already has an active debug target");
|
|
337
|
+
}
|
|
338
|
+
const adapterCmd = resolveAdapterCommand(this._runtime);
|
|
339
|
+
this.dap = DapClient.spawn(adapterCmd);
|
|
340
|
+
this.setupEventHandlers();
|
|
341
|
+
await this.initializeAdapter();
|
|
342
|
+
const program = options.program ?? command[0];
|
|
343
|
+
const programArgs = options.args ?? command.slice(1);
|
|
344
|
+
const launchArgs = {
|
|
345
|
+
program,
|
|
346
|
+
args: programArgs,
|
|
347
|
+
stopOnEntry: options.brk ?? true,
|
|
348
|
+
cwd: process.cwd()
|
|
349
|
+
};
|
|
350
|
+
await this.dap.send("launch", launchArgs);
|
|
351
|
+
await this.dap.send("configurationDone");
|
|
352
|
+
if (options.brk !== false) {
|
|
353
|
+
await this.waitForStop(5000);
|
|
354
|
+
}
|
|
355
|
+
const result = {
|
|
356
|
+
pid: this.dap.pid,
|
|
357
|
+
wsUrl: `dap://${this._runtime}`,
|
|
358
|
+
paused: this.isPaused()
|
|
359
|
+
};
|
|
360
|
+
if (this._pauseInfo) {
|
|
361
|
+
result.pauseInfo = this._pauseInfo;
|
|
362
|
+
}
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
async attach(target) {
|
|
366
|
+
if (this._state !== "idle") {
|
|
367
|
+
throw new Error("Session already has an active debug target");
|
|
368
|
+
}
|
|
369
|
+
const adapterCmd = resolveAdapterCommand(this._runtime);
|
|
370
|
+
this.dap = DapClient.spawn(adapterCmd);
|
|
371
|
+
this.setupEventHandlers();
|
|
372
|
+
await this.initializeAdapter();
|
|
373
|
+
const pid = parseInt(target, 10);
|
|
374
|
+
const attachArgs = Number.isNaN(pid) ? { program: target, waitFor: true } : { pid };
|
|
375
|
+
await this.dap.send("attach", attachArgs);
|
|
376
|
+
await this.dap.send("configurationDone");
|
|
377
|
+
await this.waitForStop(5000).catch(() => {});
|
|
378
|
+
if (this._state === "idle") {
|
|
379
|
+
this._state = "running";
|
|
380
|
+
}
|
|
381
|
+
return { wsUrl: `dap://${this._runtime}/${target}` };
|
|
382
|
+
}
|
|
383
|
+
getStatus() {
|
|
384
|
+
return {
|
|
385
|
+
session: this._session,
|
|
386
|
+
state: this._state,
|
|
387
|
+
pid: this.dap?.pid,
|
|
388
|
+
wsUrl: this.dap ? `dap://${this._runtime}` : undefined,
|
|
389
|
+
pauseInfo: this._pauseInfo ?? undefined,
|
|
390
|
+
uptime: Math.floor((Date.now() - this._startTime) / 1000),
|
|
391
|
+
scriptCount: 0
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
async stop() {
|
|
395
|
+
if (this.dap) {
|
|
396
|
+
try {
|
|
397
|
+
await this.dap.send("disconnect", { terminateDebuggee: true });
|
|
398
|
+
} catch {}
|
|
399
|
+
this.dap.disconnect();
|
|
400
|
+
this.dap = null;
|
|
401
|
+
}
|
|
402
|
+
this._state = "idle";
|
|
403
|
+
this._pauseInfo = null;
|
|
404
|
+
this._stackFrames = [];
|
|
405
|
+
this.refs.clearAll();
|
|
406
|
+
this.breakpoints.clear();
|
|
407
|
+
this.allBreakpoints = [];
|
|
408
|
+
this.functionBreakpoints = [];
|
|
409
|
+
this._consoleMessages = [];
|
|
410
|
+
this._exceptionEntries = [];
|
|
411
|
+
}
|
|
412
|
+
async continue() {
|
|
413
|
+
this.requireConnected();
|
|
414
|
+
this.requirePaused();
|
|
415
|
+
const waiter = this.createStoppedWaiter(30000);
|
|
416
|
+
await this.getDap().send("continue", { threadId: this._threadId });
|
|
417
|
+
this._state = "running";
|
|
418
|
+
this._pauseInfo = null;
|
|
419
|
+
this._stackFrames = [];
|
|
420
|
+
this.refs.clearVolatile();
|
|
421
|
+
await waiter;
|
|
422
|
+
if (this.isPaused())
|
|
423
|
+
await this.fetchStackTrace();
|
|
424
|
+
}
|
|
425
|
+
async step(mode) {
|
|
426
|
+
this.requireConnected();
|
|
427
|
+
this.requirePaused();
|
|
428
|
+
const waiter = this.createStoppedWaiter(30000);
|
|
429
|
+
const command = mode === "into" ? "stepIn" : mode === "out" ? "stepOut" : "next";
|
|
430
|
+
await this.getDap().send(command, { threadId: this._threadId });
|
|
431
|
+
this._state = "running";
|
|
432
|
+
this._pauseInfo = null;
|
|
433
|
+
this.refs.clearVolatile();
|
|
434
|
+
await waiter;
|
|
435
|
+
if (this.isPaused())
|
|
436
|
+
await this.fetchStackTrace();
|
|
437
|
+
}
|
|
438
|
+
async pause() {
|
|
439
|
+
this.requireConnected();
|
|
440
|
+
if (this._state !== "running") {
|
|
441
|
+
throw new Error("Cannot pause: target is not running");
|
|
442
|
+
}
|
|
443
|
+
const waiter = this.createStoppedWaiter(5000);
|
|
444
|
+
await this.getDap().send("pause", { threadId: this._threadId });
|
|
445
|
+
await waiter;
|
|
446
|
+
if (this.isPaused())
|
|
447
|
+
await this.fetchStackTrace();
|
|
448
|
+
}
|
|
449
|
+
async setBreakpoint(file, line, options) {
|
|
450
|
+
this.requireConnected();
|
|
451
|
+
const entry = {
|
|
452
|
+
ref: "",
|
|
453
|
+
file,
|
|
454
|
+
line,
|
|
455
|
+
condition: options?.condition,
|
|
456
|
+
hitCondition: options?.hitCount ? String(options.hitCount) : undefined,
|
|
457
|
+
verified: false
|
|
458
|
+
};
|
|
459
|
+
let fileBreakpoints = this.breakpoints.get(file);
|
|
460
|
+
if (!fileBreakpoints) {
|
|
461
|
+
fileBreakpoints = [];
|
|
462
|
+
this.breakpoints.set(file, fileBreakpoints);
|
|
463
|
+
}
|
|
464
|
+
fileBreakpoints.push(entry);
|
|
465
|
+
this.allBreakpoints.push(entry);
|
|
466
|
+
const ref = this.refs.addBreakpoint(`dap-bp:${file}:${line}`, {
|
|
467
|
+
file,
|
|
468
|
+
line
|
|
469
|
+
});
|
|
470
|
+
entry.ref = ref;
|
|
471
|
+
await this.syncFileBreakpoints(file);
|
|
472
|
+
return {
|
|
473
|
+
ref,
|
|
474
|
+
location: { url: file, line: entry.actualLine ?? line }
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
async removeBreakpoint(ref) {
|
|
478
|
+
this.requireConnected();
|
|
479
|
+
const entry = this.allBreakpoints.find((bp) => bp.ref === ref);
|
|
480
|
+
if (!entry) {
|
|
481
|
+
throw new Error(`Unknown breakpoint ref: ${ref}`);
|
|
482
|
+
}
|
|
483
|
+
const fileBreakpoints = this.breakpoints.get(entry.file);
|
|
484
|
+
if (fileBreakpoints) {
|
|
485
|
+
const idx = fileBreakpoints.indexOf(entry);
|
|
486
|
+
if (idx !== -1)
|
|
487
|
+
fileBreakpoints.splice(idx, 1);
|
|
488
|
+
if (fileBreakpoints.length === 0) {
|
|
489
|
+
this.breakpoints.delete(entry.file);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const allIdx = this.allBreakpoints.indexOf(entry);
|
|
493
|
+
if (allIdx !== -1)
|
|
494
|
+
this.allBreakpoints.splice(allIdx, 1);
|
|
495
|
+
this.refs.remove(ref);
|
|
496
|
+
await this.syncFileBreakpoints(entry.file);
|
|
497
|
+
}
|
|
498
|
+
async removeAllBreakpoints() {
|
|
499
|
+
this.requireConnected();
|
|
500
|
+
const files = [...this.breakpoints.keys()];
|
|
501
|
+
this.breakpoints.clear();
|
|
502
|
+
this.allBreakpoints = [];
|
|
503
|
+
this.functionBreakpoints = [];
|
|
504
|
+
for (const entry of this.refs.list("BP")) {
|
|
505
|
+
this.refs.remove(entry.ref);
|
|
506
|
+
}
|
|
507
|
+
for (const file of files) {
|
|
508
|
+
await this.getDap().send("setBreakpoints", {
|
|
509
|
+
source: { path: file },
|
|
510
|
+
breakpoints: []
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
await this.getDap().send("setFunctionBreakpoints", { breakpoints: [] });
|
|
514
|
+
}
|
|
515
|
+
listBreakpoints() {
|
|
516
|
+
const fileBps = this.allBreakpoints.map((bp) => ({
|
|
517
|
+
ref: bp.ref,
|
|
518
|
+
type: "BP",
|
|
519
|
+
url: bp.file,
|
|
520
|
+
line: bp.actualLine ?? bp.line,
|
|
521
|
+
condition: bp.condition
|
|
522
|
+
}));
|
|
523
|
+
const fnBps = this.functionBreakpoints.map((bp) => ({
|
|
524
|
+
ref: bp.ref,
|
|
525
|
+
type: "BP",
|
|
526
|
+
url: bp.name,
|
|
527
|
+
line: 0,
|
|
528
|
+
condition: bp.condition
|
|
529
|
+
}));
|
|
530
|
+
return [...fileBps, ...fnBps];
|
|
531
|
+
}
|
|
532
|
+
async setFunctionBreakpoint(name, options) {
|
|
533
|
+
this.requireConnected();
|
|
534
|
+
const entry = {
|
|
535
|
+
ref: "",
|
|
536
|
+
name,
|
|
537
|
+
condition: options?.condition,
|
|
538
|
+
hitCondition: options?.hitCount ? String(options.hitCount) : undefined,
|
|
539
|
+
verified: false
|
|
540
|
+
};
|
|
541
|
+
this.functionBreakpoints.push(entry);
|
|
542
|
+
const ref = this.refs.addBreakpoint(`dap-fn:${name}`, {
|
|
543
|
+
file: name,
|
|
544
|
+
line: 0
|
|
545
|
+
});
|
|
546
|
+
entry.ref = ref;
|
|
547
|
+
await this.syncFunctionBreakpoints();
|
|
548
|
+
return { ref };
|
|
549
|
+
}
|
|
550
|
+
async removeFunctionBreakpoint(ref) {
|
|
551
|
+
this.requireConnected();
|
|
552
|
+
const idx = this.functionBreakpoints.findIndex((bp) => bp.ref === ref);
|
|
553
|
+
if (idx === -1) {
|
|
554
|
+
throw new Error(`Unknown function breakpoint ref: ${ref}`);
|
|
555
|
+
}
|
|
556
|
+
this.functionBreakpoints.splice(idx, 1);
|
|
557
|
+
this.refs.remove(ref);
|
|
558
|
+
await this.syncFunctionBreakpoints();
|
|
559
|
+
}
|
|
560
|
+
async syncFunctionBreakpoints() {
|
|
561
|
+
const dapBps = this.functionBreakpoints.map((bp) => ({
|
|
562
|
+
name: bp.name,
|
|
563
|
+
condition: bp.condition,
|
|
564
|
+
hitCondition: bp.hitCondition
|
|
565
|
+
}));
|
|
566
|
+
const response = await this.getDap().send("setFunctionBreakpoints", {
|
|
567
|
+
breakpoints: dapBps
|
|
568
|
+
});
|
|
569
|
+
const body = response.body;
|
|
570
|
+
const resultBps = body?.breakpoints ?? [];
|
|
571
|
+
for (let i = 0;i < this.functionBreakpoints.length; i++) {
|
|
572
|
+
const entry = this.functionBreakpoints[i];
|
|
573
|
+
const result = resultBps[i];
|
|
574
|
+
if (entry && result) {
|
|
575
|
+
entry.verified = result.verified ?? false;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
async eval(expression, options = {}) {
|
|
580
|
+
this.requireConnected();
|
|
581
|
+
this.requirePaused();
|
|
582
|
+
await this.ensureStack();
|
|
583
|
+
const frameId = this.resolveFrameId(options.frame);
|
|
584
|
+
const response = await this.getDap().send("evaluate", {
|
|
585
|
+
expression,
|
|
586
|
+
frameId,
|
|
587
|
+
context: "repl"
|
|
588
|
+
});
|
|
589
|
+
const body = response.body;
|
|
590
|
+
const remoteId = body.variablesReference > 0 ? String(body.variablesReference) : `eval:${Date.now()}`;
|
|
591
|
+
const ref = this.refs.addVar(remoteId, expression);
|
|
592
|
+
return {
|
|
593
|
+
ref,
|
|
594
|
+
type: body.type ?? "unknown",
|
|
595
|
+
value: body.result,
|
|
596
|
+
objectId: body.variablesReference > 0 ? String(body.variablesReference) : undefined
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
async getVars(options = {}) {
|
|
600
|
+
this.requireConnected();
|
|
601
|
+
this.requirePaused();
|
|
602
|
+
await this.ensureStack();
|
|
603
|
+
const frameId = this.resolveFrameId(options.frame);
|
|
604
|
+
const scopesResponse = await this.getDap().send("scopes", { frameId });
|
|
605
|
+
const scopes = scopesResponse.body.scopes;
|
|
606
|
+
const result = [];
|
|
607
|
+
const scopesToFetch = options.allScopes ? scopes : scopes.filter((s) => !s.expensive).slice(0, 2);
|
|
608
|
+
for (const scope of scopesToFetch) {
|
|
609
|
+
const varsResponse = await this.getDap().send("variables", {
|
|
610
|
+
variablesReference: scope.variablesReference
|
|
611
|
+
});
|
|
612
|
+
const variables = varsResponse.body.variables;
|
|
613
|
+
for (const v of variables) {
|
|
614
|
+
if (options.names && !options.names.includes(v.name))
|
|
615
|
+
continue;
|
|
616
|
+
const remoteId = v.variablesReference > 0 ? String(v.variablesReference) : `var:${v.name}:${Date.now()}`;
|
|
617
|
+
const ref = this.refs.addVar(remoteId, v.name);
|
|
618
|
+
result.push({
|
|
619
|
+
ref,
|
|
620
|
+
name: v.name,
|
|
621
|
+
type: v.type ?? "unknown",
|
|
622
|
+
value: v.value
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
return result;
|
|
627
|
+
}
|
|
628
|
+
async getProps(ref, _options = {}) {
|
|
629
|
+
this.requireConnected();
|
|
630
|
+
const remoteId = this.refs.resolveId(ref);
|
|
631
|
+
if (!remoteId) {
|
|
632
|
+
throw new Error(`Unknown ref: ${ref}`);
|
|
633
|
+
}
|
|
634
|
+
const variablesReference = parseInt(remoteId, 10);
|
|
635
|
+
if (Number.isNaN(variablesReference) || variablesReference <= 0) {
|
|
636
|
+
return [];
|
|
637
|
+
}
|
|
638
|
+
const response = await this.getDap().send("variables", { variablesReference });
|
|
639
|
+
const variables = response.body.variables;
|
|
640
|
+
return variables.map((v) => {
|
|
641
|
+
const childRemoteId = v.variablesReference > 0 ? String(v.variablesReference) : `prop:${v.name}:${Date.now()}`;
|
|
642
|
+
const childRef = v.variablesReference > 0 ? this.refs.addVar(childRemoteId, v.name) : undefined;
|
|
643
|
+
return {
|
|
644
|
+
ref: childRef,
|
|
645
|
+
name: v.name,
|
|
646
|
+
type: v.type ?? "unknown",
|
|
647
|
+
value: v.value,
|
|
648
|
+
isOwn: true
|
|
649
|
+
};
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
getStack(_options = {}) {
|
|
653
|
+
return this._stackFrames.map((frame) => {
|
|
654
|
+
const ref = this.refs.addFrame(String(frame.id), frame.name);
|
|
655
|
+
return {
|
|
656
|
+
ref,
|
|
657
|
+
functionName: frame.name,
|
|
658
|
+
file: frame.file ?? "<unknown>",
|
|
659
|
+
line: frame.line,
|
|
660
|
+
column: frame.column > 0 ? frame.column : undefined
|
|
661
|
+
};
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
async getSource(options = {}) {
|
|
665
|
+
const file = options.file ?? this._pauseInfo?.url;
|
|
666
|
+
if (!file) {
|
|
667
|
+
throw new Error("No source file available. Specify a file path.");
|
|
668
|
+
}
|
|
669
|
+
let content;
|
|
670
|
+
try {
|
|
671
|
+
content = await Bun.file(file).text();
|
|
672
|
+
} catch {
|
|
673
|
+
throw new Error(`Cannot read source file: ${file}`);
|
|
674
|
+
}
|
|
675
|
+
const allLines = content.split(`
|
|
676
|
+
`);
|
|
677
|
+
const currentLine = this._pauseInfo?.line;
|
|
678
|
+
const windowSize = options.lines ?? 10;
|
|
679
|
+
let startLine;
|
|
680
|
+
let endLine;
|
|
681
|
+
if (options.all) {
|
|
682
|
+
startLine = 1;
|
|
683
|
+
endLine = allLines.length;
|
|
684
|
+
} else if (currentLine !== undefined) {
|
|
685
|
+
startLine = Math.max(1, currentLine - windowSize);
|
|
686
|
+
endLine = Math.min(allLines.length, currentLine + windowSize);
|
|
687
|
+
} else {
|
|
688
|
+
startLine = 1;
|
|
689
|
+
endLine = Math.min(allLines.length, windowSize * 2);
|
|
690
|
+
}
|
|
691
|
+
const lines = [];
|
|
692
|
+
for (let i = startLine;i <= endLine; i++) {
|
|
693
|
+
const lineObj = {
|
|
694
|
+
line: i,
|
|
695
|
+
text: allLines[i - 1] ?? ""
|
|
696
|
+
};
|
|
697
|
+
if (currentLine !== undefined && i === currentLine) {
|
|
698
|
+
lineObj.current = true;
|
|
699
|
+
}
|
|
700
|
+
lines.push(lineObj);
|
|
701
|
+
}
|
|
702
|
+
return { url: file, lines };
|
|
703
|
+
}
|
|
704
|
+
async buildState(options = {}) {
|
|
705
|
+
if (this.isPaused())
|
|
706
|
+
await this.ensureStack();
|
|
707
|
+
const snapshot = {
|
|
708
|
+
status: this._state
|
|
709
|
+
};
|
|
710
|
+
if (this._state === "paused" && this._pauseInfo) {
|
|
711
|
+
snapshot.reason = this._pauseInfo.reason;
|
|
712
|
+
if (this._pauseInfo.url && this._pauseInfo.line !== undefined) {
|
|
713
|
+
snapshot.location = {
|
|
714
|
+
url: this._pauseInfo.url,
|
|
715
|
+
line: this._pauseInfo.line,
|
|
716
|
+
column: this._pauseInfo.column
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (this._state === "paused" && options.code !== false) {
|
|
721
|
+
try {
|
|
722
|
+
const source = await this.getSource({ lines: options.lines });
|
|
723
|
+
snapshot.source = source;
|
|
724
|
+
} catch {}
|
|
725
|
+
}
|
|
726
|
+
if (this._state === "paused" && (options.vars !== false || !options.compact)) {
|
|
727
|
+
try {
|
|
728
|
+
const vars = await this.getVars({ frame: options.frame, allScopes: options.allScopes });
|
|
729
|
+
snapshot.vars = vars.map((v) => ({
|
|
730
|
+
ref: v.ref,
|
|
731
|
+
name: v.name,
|
|
732
|
+
value: v.value,
|
|
733
|
+
scope: "local"
|
|
734
|
+
}));
|
|
735
|
+
} catch {}
|
|
736
|
+
}
|
|
737
|
+
if (this._state === "paused" && options.stack !== false) {
|
|
738
|
+
try {
|
|
739
|
+
snapshot.stack = this.getStack();
|
|
740
|
+
} catch {}
|
|
741
|
+
}
|
|
742
|
+
if (options.breakpoints !== false) {
|
|
743
|
+
snapshot.breakpointCount = this.allBreakpoints.length;
|
|
744
|
+
}
|
|
745
|
+
return snapshot;
|
|
746
|
+
}
|
|
747
|
+
getConsoleMessages(options = {}) {
|
|
748
|
+
let msgs = this._consoleMessages;
|
|
749
|
+
if (options.level) {
|
|
750
|
+
msgs = msgs.filter((m) => m.level === options.level);
|
|
751
|
+
}
|
|
752
|
+
if (options.since !== undefined) {
|
|
753
|
+
const since = options.since;
|
|
754
|
+
msgs = msgs.filter((m) => m.timestamp >= since);
|
|
755
|
+
}
|
|
756
|
+
if (options.clear) {
|
|
757
|
+
this._consoleMessages = [];
|
|
758
|
+
}
|
|
759
|
+
return msgs;
|
|
760
|
+
}
|
|
761
|
+
getExceptions(options = {}) {
|
|
762
|
+
let entries = this._exceptionEntries;
|
|
763
|
+
if (options.since !== undefined) {
|
|
764
|
+
const since = options.since;
|
|
765
|
+
entries = entries.filter((e) => e.timestamp >= since);
|
|
766
|
+
}
|
|
767
|
+
return entries;
|
|
768
|
+
}
|
|
769
|
+
async setLogpoint(_file, _line, _template, _options) {
|
|
770
|
+
throw new Error("Logpoints are not supported in DAP mode. Use breakpoints with conditions instead.");
|
|
771
|
+
}
|
|
772
|
+
async setExceptionPause(mode) {
|
|
773
|
+
this.requireConnected();
|
|
774
|
+
const available = this.capabilities.exceptionBreakpointFilters ?? [];
|
|
775
|
+
const filterIds = available.map((f) => f.filter);
|
|
776
|
+
let filters;
|
|
777
|
+
if (mode === "none") {
|
|
778
|
+
filters = [];
|
|
779
|
+
} else if (mode === "all") {
|
|
780
|
+
filters = filterIds;
|
|
781
|
+
} else {
|
|
782
|
+
filters = filterIds.filter((id) => id.includes(mode));
|
|
783
|
+
if (filters.length === 0)
|
|
784
|
+
filters = filterIds;
|
|
785
|
+
}
|
|
786
|
+
await this.getDap().send("setExceptionBreakpoints", { filters });
|
|
787
|
+
}
|
|
788
|
+
async toggleBreakpoint(_ref) {
|
|
789
|
+
throw new Error("Breakpoint toggling is not yet supported in DAP mode. Use break-rm and break.");
|
|
790
|
+
}
|
|
791
|
+
async getBreakableLocations(_file, _startLine, _endLine) {
|
|
792
|
+
throw new Error("Breakable locations are not supported in DAP mode.");
|
|
793
|
+
}
|
|
794
|
+
async hotpatch(_file, _source, _options) {
|
|
795
|
+
throw new Error("Hot-patching is not supported in DAP mode.");
|
|
796
|
+
}
|
|
797
|
+
async searchInScripts(_query, _options) {
|
|
798
|
+
throw new Error("Script search is not supported in DAP mode. Use your shell to search source files.");
|
|
799
|
+
}
|
|
800
|
+
async setVariable(varName, value, options = {}) {
|
|
801
|
+
this.requireConnected();
|
|
802
|
+
this.requirePaused();
|
|
803
|
+
await this.ensureStack();
|
|
804
|
+
const frameId = this.resolveFrameId(options.frame);
|
|
805
|
+
const scopesResponse = await this.getDap().send("scopes", { frameId });
|
|
806
|
+
const scopes = scopesResponse.body.scopes;
|
|
807
|
+
for (const scope of scopes) {
|
|
808
|
+
try {
|
|
809
|
+
const response = await this.getDap().send("setVariable", {
|
|
810
|
+
variablesReference: scope.variablesReference,
|
|
811
|
+
name: varName,
|
|
812
|
+
value
|
|
813
|
+
});
|
|
814
|
+
const body = response.body;
|
|
815
|
+
return { name: varName, newValue: body.value, type: body.type ?? "unknown" };
|
|
816
|
+
} catch {}
|
|
817
|
+
}
|
|
818
|
+
throw new Error(`Variable "${varName}" not found in any scope`);
|
|
819
|
+
}
|
|
820
|
+
async setReturnValue(_value) {
|
|
821
|
+
throw new Error("Setting return values is not supported in DAP mode.");
|
|
822
|
+
}
|
|
823
|
+
async restartFrame(_frameRef) {
|
|
824
|
+
throw new Error("Frame restart is not supported in DAP mode.");
|
|
825
|
+
}
|
|
826
|
+
async runTo(_file, _line) {
|
|
827
|
+
throw new Error("Run-to-location is not yet supported in DAP mode. Set a breakpoint and continue.");
|
|
828
|
+
}
|
|
829
|
+
getScripts(_filter) {
|
|
830
|
+
return [];
|
|
831
|
+
}
|
|
832
|
+
async addBlackbox(_patterns) {
|
|
833
|
+
throw new Error("Blackboxing is not supported in DAP mode.");
|
|
834
|
+
}
|
|
835
|
+
listBlackbox() {
|
|
836
|
+
return [];
|
|
837
|
+
}
|
|
838
|
+
async removeBlackbox(_patterns) {
|
|
839
|
+
throw new Error("Blackboxing is not supported in DAP mode.");
|
|
840
|
+
}
|
|
841
|
+
async restart() {
|
|
842
|
+
throw new Error("Restart is not yet supported in DAP mode. Use stop + launch.");
|
|
843
|
+
}
|
|
844
|
+
get sourceMapResolver() {
|
|
845
|
+
return {
|
|
846
|
+
findScriptForSource: () => null,
|
|
847
|
+
getInfo: () => null,
|
|
848
|
+
getAllInfos: () => [],
|
|
849
|
+
setDisabled: () => {}
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
isPaused() {
|
|
853
|
+
return this._state === "paused";
|
|
854
|
+
}
|
|
855
|
+
async ensureStack() {
|
|
856
|
+
if (this.isPaused() && this._stackFrames.length === 0) {
|
|
857
|
+
await this.fetchStackTrace();
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
getDap() {
|
|
861
|
+
if (!this.dap || !this.dap.connected) {
|
|
862
|
+
throw new Error("Not connected to a debug adapter. Use launch or attach first.");
|
|
863
|
+
}
|
|
864
|
+
return this.dap;
|
|
865
|
+
}
|
|
866
|
+
requireConnected() {
|
|
867
|
+
this.getDap();
|
|
868
|
+
}
|
|
869
|
+
requirePaused() {
|
|
870
|
+
if (!this.isPaused()) {
|
|
871
|
+
throw new Error("Target is not paused. Use pause or wait for a breakpoint.");
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
async initializeAdapter() {
|
|
875
|
+
const response = await this.getDap().send("initialize", {
|
|
876
|
+
adapterID: this._runtime,
|
|
877
|
+
clientID: "agent-dbg",
|
|
878
|
+
clientName: "agent-dbg",
|
|
879
|
+
linesStartAt1: true,
|
|
880
|
+
columnsStartAt1: true,
|
|
881
|
+
pathFormat: "path",
|
|
882
|
+
supportsVariableType: true
|
|
883
|
+
});
|
|
884
|
+
this.capabilities = response.body ?? {};
|
|
885
|
+
}
|
|
886
|
+
setupEventHandlers() {
|
|
887
|
+
const dap = this.getDap();
|
|
888
|
+
dap.on("stopped", (body) => {
|
|
889
|
+
const event = body;
|
|
890
|
+
this._state = "paused";
|
|
891
|
+
if (event.threadId !== undefined) {
|
|
892
|
+
this._threadId = event.threadId;
|
|
893
|
+
}
|
|
894
|
+
this._pauseInfo = {
|
|
895
|
+
reason: event.reason
|
|
896
|
+
};
|
|
897
|
+
if (this.stoppedWaiter) {
|
|
898
|
+
this.stoppedWaiter.resolve();
|
|
899
|
+
this.stoppedWaiter = null;
|
|
900
|
+
} else {
|
|
901
|
+
this.fetchStackTrace().catch(() => {});
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
dap.on("continued", (_body) => {
|
|
905
|
+
this._state = "running";
|
|
906
|
+
this._pauseInfo = null;
|
|
907
|
+
this._stackFrames = [];
|
|
908
|
+
this.refs.clearVolatile();
|
|
909
|
+
});
|
|
910
|
+
dap.on("terminated", (_body) => {
|
|
911
|
+
this._state = "idle";
|
|
912
|
+
this._pauseInfo = null;
|
|
913
|
+
this._stackFrames = [];
|
|
914
|
+
this.stoppedWaiter?.resolve();
|
|
915
|
+
this.stoppedWaiter = null;
|
|
916
|
+
});
|
|
917
|
+
dap.on("exited", (_body) => {
|
|
918
|
+
this._state = "idle";
|
|
919
|
+
this._pauseInfo = null;
|
|
920
|
+
this.stoppedWaiter?.resolve();
|
|
921
|
+
this.stoppedWaiter = null;
|
|
922
|
+
});
|
|
923
|
+
dap.on("output", (body) => {
|
|
924
|
+
const event = body;
|
|
925
|
+
const category = event.category ?? "console";
|
|
926
|
+
if (category === "stdout" || category === "console") {
|
|
927
|
+
this._consoleMessages.push({
|
|
928
|
+
timestamp: Date.now(),
|
|
929
|
+
level: "log",
|
|
930
|
+
text: event.output.trimEnd(),
|
|
931
|
+
url: event.source?.path,
|
|
932
|
+
line: event.line
|
|
933
|
+
});
|
|
934
|
+
} else if (category === "stderr") {
|
|
935
|
+
this._consoleMessages.push({
|
|
936
|
+
timestamp: Date.now(),
|
|
937
|
+
level: "error",
|
|
938
|
+
text: event.output.trimEnd(),
|
|
939
|
+
url: event.source?.path,
|
|
940
|
+
line: event.line
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
if (this._consoleMessages.length > 1000) {
|
|
944
|
+
this._consoleMessages.shift();
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
async fetchStackTrace() {
|
|
949
|
+
if (this._stackFetchPromise) {
|
|
950
|
+
await this._stackFetchPromise;
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
this._stackFetchPromise = this._fetchStackTraceImpl();
|
|
954
|
+
try {
|
|
955
|
+
await this._stackFetchPromise;
|
|
956
|
+
} finally {
|
|
957
|
+
this._stackFetchPromise = null;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
async _fetchStackTraceImpl() {
|
|
961
|
+
if (!this.dap || this._state !== "paused")
|
|
962
|
+
return;
|
|
963
|
+
try {
|
|
964
|
+
const response = await this.dap.send("stackTrace", {
|
|
965
|
+
threadId: this._threadId,
|
|
966
|
+
startFrame: 0,
|
|
967
|
+
levels: 50
|
|
968
|
+
});
|
|
969
|
+
const body = response.body;
|
|
970
|
+
this._stackFrames = body.stackFrames.map((f) => ({
|
|
971
|
+
id: f.id,
|
|
972
|
+
name: f.name,
|
|
973
|
+
file: f.source?.path ?? f.source?.name,
|
|
974
|
+
line: f.line,
|
|
975
|
+
column: f.column
|
|
976
|
+
}));
|
|
977
|
+
const topFrame = this._stackFrames[0];
|
|
978
|
+
if (topFrame && this._pauseInfo) {
|
|
979
|
+
this._pauseInfo.url = topFrame.file;
|
|
980
|
+
this._pauseInfo.line = topFrame.line;
|
|
981
|
+
this._pauseInfo.column = topFrame.column > 0 ? topFrame.column : undefined;
|
|
982
|
+
this._pauseInfo.callFrameCount = this._stackFrames.length;
|
|
983
|
+
}
|
|
984
|
+
} catch {}
|
|
985
|
+
}
|
|
986
|
+
resolveFrameId(frameRef) {
|
|
987
|
+
if (!frameRef) {
|
|
988
|
+
const topFrame = this._stackFrames[0];
|
|
989
|
+
if (!topFrame) {
|
|
990
|
+
throw new Error("No stack frames available");
|
|
991
|
+
}
|
|
992
|
+
return topFrame.id;
|
|
993
|
+
}
|
|
994
|
+
const remoteId = this.refs.resolveId(frameRef);
|
|
995
|
+
if (!remoteId) {
|
|
996
|
+
throw new Error(`Unknown frame ref: ${frameRef}`);
|
|
997
|
+
}
|
|
998
|
+
return parseInt(remoteId, 10);
|
|
999
|
+
}
|
|
1000
|
+
async syncFileBreakpoints(file) {
|
|
1001
|
+
const entries = this.breakpoints.get(file) ?? [];
|
|
1002
|
+
const dapBreakpoints = entries.map((bp) => {
|
|
1003
|
+
const sbp = { line: bp.line };
|
|
1004
|
+
if (bp.condition)
|
|
1005
|
+
sbp.condition = bp.condition;
|
|
1006
|
+
if (bp.hitCondition)
|
|
1007
|
+
sbp.hitCondition = bp.hitCondition;
|
|
1008
|
+
return sbp;
|
|
1009
|
+
});
|
|
1010
|
+
const response = await this.getDap().send("setBreakpoints", {
|
|
1011
|
+
source: { path: file },
|
|
1012
|
+
breakpoints: dapBreakpoints
|
|
1013
|
+
});
|
|
1014
|
+
const body = response.body;
|
|
1015
|
+
for (let i = 0;i < entries.length && i < body.breakpoints.length; i++) {
|
|
1016
|
+
const bp = body.breakpoints[i];
|
|
1017
|
+
const entry = entries[i];
|
|
1018
|
+
if (bp && entry) {
|
|
1019
|
+
entry.dapId = bp.id;
|
|
1020
|
+
entry.verified = bp.verified;
|
|
1021
|
+
entry.actualLine = bp.line ?? entry.line;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
createStoppedWaiter(timeoutMs) {
|
|
1026
|
+
return new Promise((resolve, reject) => {
|
|
1027
|
+
const timer = setTimeout(() => {
|
|
1028
|
+
this.stoppedWaiter = null;
|
|
1029
|
+
resolve();
|
|
1030
|
+
}, timeoutMs);
|
|
1031
|
+
this.stoppedWaiter = {
|
|
1032
|
+
resolve: () => {
|
|
1033
|
+
clearTimeout(timer);
|
|
1034
|
+
this.stoppedWaiter = null;
|
|
1035
|
+
resolve();
|
|
1036
|
+
},
|
|
1037
|
+
reject: (e) => {
|
|
1038
|
+
clearTimeout(timer);
|
|
1039
|
+
this.stoppedWaiter = null;
|
|
1040
|
+
reject(e);
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
async waitForStop(timeoutMs) {
|
|
1046
|
+
if (!this.isPaused()) {
|
|
1047
|
+
await this.createStoppedWaiter(timeoutMs);
|
|
1048
|
+
}
|
|
1049
|
+
if (this.isPaused() && this._stackFrames.length === 0) {
|
|
1050
|
+
await this.fetchStackTrace();
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
var init_session = __esm(() => {
|
|
1055
|
+
init_ref_table();
|
|
1056
|
+
});
|
|
1057
|
+
|
|
1058
|
+
// src/daemon/logger.ts
|
|
1059
|
+
import { appendFileSync, writeFileSync } from "fs";
|
|
1060
|
+
|
|
1061
|
+
class DaemonLogger {
|
|
1062
|
+
logPath;
|
|
1063
|
+
constructor(logPath) {
|
|
1064
|
+
this.logPath = logPath;
|
|
1065
|
+
writeFileSync(logPath, "");
|
|
1066
|
+
}
|
|
1067
|
+
info(event, message, data) {
|
|
1068
|
+
this.write("info", event, message, data);
|
|
1069
|
+
}
|
|
1070
|
+
warn(event, message, data) {
|
|
1071
|
+
this.write("warn", event, message, data);
|
|
1072
|
+
}
|
|
1073
|
+
error(event, message, data) {
|
|
1074
|
+
this.write("error", event, message, data);
|
|
1075
|
+
}
|
|
1076
|
+
debug(event, message, data) {
|
|
1077
|
+
this.write("debug", event, message, data);
|
|
1078
|
+
}
|
|
1079
|
+
clear() {
|
|
1080
|
+
writeFileSync(this.logPath, "");
|
|
1081
|
+
}
|
|
1082
|
+
write(level, event, message, data) {
|
|
1083
|
+
const entry = { ts: Date.now(), level, event, message };
|
|
1084
|
+
if (data !== undefined) {
|
|
1085
|
+
entry.data = data;
|
|
1086
|
+
}
|
|
1087
|
+
appendFileSync(this.logPath, `${JSON.stringify(entry)}
|
|
1088
|
+
`);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
var init_logger = () => {};
|
|
1092
|
+
|
|
1093
|
+
// src/daemon/paths.ts
|
|
1094
|
+
import { existsSync, mkdirSync } from "fs";
|
|
1095
|
+
import { join } from "path";
|
|
1096
|
+
function getSocketDir() {
|
|
1097
|
+
const xdgRuntime = process.env.XDG_RUNTIME_DIR;
|
|
1098
|
+
if (xdgRuntime) {
|
|
1099
|
+
return join(xdgRuntime, "agent-dbg");
|
|
1100
|
+
}
|
|
1101
|
+
const tmpdir = process.env.TMPDIR || "/tmp";
|
|
1102
|
+
return join(tmpdir, `agent-dbg-${process.getuid?.() ?? 0}`);
|
|
1103
|
+
}
|
|
1104
|
+
function getSocketPath(session) {
|
|
1105
|
+
return join(getSocketDir(), `${session}.sock`);
|
|
1106
|
+
}
|
|
1107
|
+
function getLockPath(session) {
|
|
1108
|
+
return join(getSocketDir(), `${session}.lock`);
|
|
1109
|
+
}
|
|
1110
|
+
function getLogPath(session) {
|
|
1111
|
+
return join(getSocketDir(), `${session}.cdp.log`);
|
|
1112
|
+
}
|
|
1113
|
+
function getDaemonLogPath(session) {
|
|
1114
|
+
return join(getSocketDir(), `${session}.daemon.log`);
|
|
1115
|
+
}
|
|
1116
|
+
function ensureSocketDir() {
|
|
1117
|
+
const dir = getSocketDir();
|
|
1118
|
+
if (!existsSync(dir)) {
|
|
1119
|
+
mkdirSync(dir, { recursive: true });
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
var init_paths = () => {};
|
|
1123
|
+
|
|
15
1124
|
// node_modules/@zod/core/dist/esm/core.js
|
|
16
1125
|
function $constructor(name, initializer) {
|
|
17
1126
|
class _ {
|
|
@@ -6035,7 +7144,7 @@ var init_esm2 = __esm(() => {
|
|
|
6035
7144
|
});
|
|
6036
7145
|
|
|
6037
7146
|
// src/protocol/messages.ts
|
|
6038
|
-
var PingRequest, LaunchRequest, AttachRequest, StatusRequest, StateRequest, ContinueRequest, StepRequest, PauseRequest, RunToRequest, BreakRequest, BreakRmRequest, BreakLsRequest, LogpointRequest, CatchRequest, SourceRequest, ScriptsRequest, StackRequest, SearchRequest, ConsoleRequest, ExceptionsRequest, EvalRequest, VarsRequest, PropsRequest, BlackboxRequest, BlackboxLsRequest, BlackboxRmRequest, SetRequest, SetReturnRequest, HotpatchRequest, BreakToggleRequest, BreakableRequest, RestartFrameRequest, SourcemapRequest, SourcemapDisableRequest, RestartRequest, StopRequest, DaemonRequestSchema, SuccessResponse, ErrorResponse, DaemonResponseSchema;
|
|
7147
|
+
var PingRequest, LaunchRequest, AttachRequest, StatusRequest, StateRequest, ContinueRequest, StepRequest, PauseRequest, RunToRequest, BreakRequest, BreakFnRequest, BreakRmRequest, BreakLsRequest, LogpointRequest, CatchRequest, SourceRequest, ScriptsRequest, StackRequest, SearchRequest, ConsoleRequest, ExceptionsRequest, EvalRequest, VarsRequest, PropsRequest, BlackboxRequest, BlackboxLsRequest, BlackboxRmRequest, SetRequest, SetReturnRequest, HotpatchRequest, BreakToggleRequest, BreakableRequest, RestartFrameRequest, SourcemapRequest, SourcemapDisableRequest, RestartRequest, StopRequest, DaemonRequestSchema, SuccessResponse, ErrorResponse, DaemonResponseSchema;
|
|
6039
7148
|
var init_messages = __esm(() => {
|
|
6040
7149
|
init_esm2();
|
|
6041
7150
|
PingRequest = exports_external.object({ cmd: exports_external.literal("ping") });
|
|
@@ -6044,13 +7153,15 @@ var init_messages = __esm(() => {
|
|
|
6044
7153
|
args: exports_external.object({
|
|
6045
7154
|
command: exports_external.array(exports_external.string()),
|
|
6046
7155
|
brk: exports_external.optional(exports_external.boolean()),
|
|
6047
|
-
port: exports_external.optional(exports_external.number())
|
|
7156
|
+
port: exports_external.optional(exports_external.number()),
|
|
7157
|
+
runtime: exports_external.optional(exports_external.string())
|
|
6048
7158
|
})
|
|
6049
7159
|
});
|
|
6050
7160
|
AttachRequest = exports_external.object({
|
|
6051
7161
|
cmd: exports_external.literal("attach"),
|
|
6052
7162
|
args: exports_external.object({
|
|
6053
|
-
target: exports_external.string()
|
|
7163
|
+
target: exports_external.string(),
|
|
7164
|
+
runtime: exports_external.optional(exports_external.string())
|
|
6054
7165
|
})
|
|
6055
7166
|
});
|
|
6056
7167
|
StatusRequest = exports_external.object({ cmd: exports_external.literal("status") });
|
|
@@ -6091,7 +7202,15 @@ var init_messages = __esm(() => {
|
|
|
6091
7202
|
line: exports_external.number(),
|
|
6092
7203
|
condition: exports_external.optional(exports_external.string()),
|
|
6093
7204
|
hitCount: exports_external.optional(exports_external.number()),
|
|
6094
|
-
urlRegex: exports_external.optional(exports_external.string())
|
|
7205
|
+
urlRegex: exports_external.optional(exports_external.string()),
|
|
7206
|
+
column: exports_external.optional(exports_external.number())
|
|
7207
|
+
})
|
|
7208
|
+
});
|
|
7209
|
+
BreakFnRequest = exports_external.object({
|
|
7210
|
+
cmd: exports_external.literal("break-fn"),
|
|
7211
|
+
args: exports_external.object({
|
|
7212
|
+
name: exports_external.string(),
|
|
7213
|
+
condition: exports_external.optional(exports_external.string())
|
|
6095
7214
|
})
|
|
6096
7215
|
});
|
|
6097
7216
|
BreakRmRequest = exports_external.object({
|
|
@@ -6269,6 +7388,7 @@ var init_messages = __esm(() => {
|
|
|
6269
7388
|
PauseRequest,
|
|
6270
7389
|
RunToRequest,
|
|
6271
7390
|
BreakRequest,
|
|
7391
|
+
BreakFnRequest,
|
|
6272
7392
|
BreakRmRequest,
|
|
6273
7393
|
BreakLsRequest,
|
|
6274
7394
|
LogpointRequest,
|
|
@@ -6308,36 +7428,8 @@ var init_messages = __esm(() => {
|
|
|
6308
7428
|
DaemonResponseSchema = exports_external.union([SuccessResponse, ErrorResponse]);
|
|
6309
7429
|
});
|
|
6310
7430
|
|
|
6311
|
-
// src/daemon/paths.ts
|
|
6312
|
-
import { existsSync, mkdirSync } from "fs";
|
|
6313
|
-
import { join } from "path";
|
|
6314
|
-
function getSocketDir() {
|
|
6315
|
-
const xdgRuntime = process.env.XDG_RUNTIME_DIR;
|
|
6316
|
-
if (xdgRuntime) {
|
|
6317
|
-
return join(xdgRuntime, "agent-dbg");
|
|
6318
|
-
}
|
|
6319
|
-
const tmpdir = process.env.TMPDIR || "/tmp";
|
|
6320
|
-
return join(tmpdir, `agent-dbg-${process.getuid?.() ?? 0}`);
|
|
6321
|
-
}
|
|
6322
|
-
function getSocketPath(session) {
|
|
6323
|
-
return join(getSocketDir(), `${session}.sock`);
|
|
6324
|
-
}
|
|
6325
|
-
function getLockPath(session) {
|
|
6326
|
-
return join(getSocketDir(), `${session}.lock`);
|
|
6327
|
-
}
|
|
6328
|
-
function getLogPath(session) {
|
|
6329
|
-
return join(getSocketDir(), `${session}.cdp.log`);
|
|
6330
|
-
}
|
|
6331
|
-
function ensureSocketDir() {
|
|
6332
|
-
const dir = getSocketDir();
|
|
6333
|
-
if (!existsSync(dir)) {
|
|
6334
|
-
mkdirSync(dir, { recursive: true });
|
|
6335
|
-
}
|
|
6336
|
-
}
|
|
6337
|
-
var init_paths = () => {};
|
|
6338
|
-
|
|
6339
7431
|
// src/daemon/server.ts
|
|
6340
|
-
import { existsSync as existsSync2, unlinkSync, writeFileSync } from "fs";
|
|
7432
|
+
import { existsSync as existsSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
6341
7433
|
|
|
6342
7434
|
class DaemonServer {
|
|
6343
7435
|
session;
|
|
@@ -6347,11 +7439,13 @@ class DaemonServer {
|
|
|
6347
7439
|
listener = null;
|
|
6348
7440
|
socketPath;
|
|
6349
7441
|
lockPath;
|
|
7442
|
+
logger;
|
|
6350
7443
|
constructor(session, options) {
|
|
6351
7444
|
this.session = session;
|
|
6352
7445
|
this.idleTimeout = options.idleTimeout;
|
|
6353
7446
|
this.socketPath = getSocketPath(session);
|
|
6354
7447
|
this.lockPath = getLockPath(session);
|
|
7448
|
+
this.logger = options.logger ?? null;
|
|
6355
7449
|
}
|
|
6356
7450
|
onRequest(handler) {
|
|
6357
7451
|
this.handler = handler;
|
|
@@ -6368,13 +7462,13 @@ class DaemonServer {
|
|
|
6368
7462
|
if (existsSync2(this.socketPath)) {
|
|
6369
7463
|
unlinkSync(this.socketPath);
|
|
6370
7464
|
}
|
|
6371
|
-
|
|
7465
|
+
writeFileSync2(this.lockPath, String(process.pid));
|
|
6372
7466
|
const server = this;
|
|
6373
7467
|
this.listener = Bun.listen({
|
|
6374
7468
|
unix: this.socketPath,
|
|
6375
7469
|
socket: {
|
|
6376
7470
|
open(socket) {
|
|
6377
|
-
socket.data = { buffer: "" };
|
|
7471
|
+
socket.data = { buffer: "", pendingWrite: null, pendingOffset: 0 };
|
|
6378
7472
|
server.resetIdleTimer();
|
|
6379
7473
|
},
|
|
6380
7474
|
data(socket, data) {
|
|
@@ -6387,68 +7481,81 @@ class DaemonServer {
|
|
|
6387
7481
|
socket.data.buffer = socket.data.buffer.slice(newlineIdx + 1);
|
|
6388
7482
|
server.handleMessage(socket, line);
|
|
6389
7483
|
},
|
|
7484
|
+
drain(socket) {
|
|
7485
|
+
server.flushPending(socket);
|
|
7486
|
+
},
|
|
6390
7487
|
close() {},
|
|
6391
7488
|
error(_socket, error3) {
|
|
7489
|
+
server.logger?.error("socket.error", error3.message);
|
|
6392
7490
|
console.error(`[daemon] socket error: ${error3.message}`);
|
|
6393
7491
|
}
|
|
6394
7492
|
}
|
|
6395
7493
|
});
|
|
6396
7494
|
this.resetIdleTimer();
|
|
6397
7495
|
}
|
|
7496
|
+
flushPending(socket) {
|
|
7497
|
+
const data = socket.data;
|
|
7498
|
+
if (!data.pendingWrite)
|
|
7499
|
+
return;
|
|
7500
|
+
while (data.pendingOffset < data.pendingWrite.length) {
|
|
7501
|
+
const written = socket.write(data.pendingWrite.subarray(data.pendingOffset));
|
|
7502
|
+
if (written === 0) {
|
|
7503
|
+
return;
|
|
7504
|
+
}
|
|
7505
|
+
data.pendingOffset += written;
|
|
7506
|
+
}
|
|
7507
|
+
data.pendingWrite = null;
|
|
7508
|
+
data.pendingOffset = 0;
|
|
7509
|
+
socket.end();
|
|
7510
|
+
}
|
|
7511
|
+
sendResponse(socket, response) {
|
|
7512
|
+
const payload = Buffer.from(`${JSON.stringify(response)}
|
|
7513
|
+
`);
|
|
7514
|
+
const written = socket.write(payload);
|
|
7515
|
+
if (written < payload.length) {
|
|
7516
|
+
socket.data.pendingWrite = payload;
|
|
7517
|
+
socket.data.pendingOffset = written;
|
|
7518
|
+
} else {
|
|
7519
|
+
socket.end();
|
|
7520
|
+
}
|
|
7521
|
+
}
|
|
6398
7522
|
handleMessage(socket, line) {
|
|
6399
7523
|
let json2;
|
|
6400
7524
|
try {
|
|
6401
7525
|
json2 = JSON.parse(line);
|
|
6402
7526
|
} catch {
|
|
6403
|
-
|
|
6404
|
-
ok: false,
|
|
6405
|
-
error: "Invalid JSON"
|
|
6406
|
-
};
|
|
6407
|
-
socket.write(`${JSON.stringify(errResponse)}
|
|
6408
|
-
`);
|
|
6409
|
-
socket.end();
|
|
7527
|
+
this.sendResponse(socket, { ok: false, error: "Invalid JSON" });
|
|
6410
7528
|
return;
|
|
6411
7529
|
}
|
|
6412
7530
|
const parsed = DaemonRequestSchema.safeParse(json2);
|
|
6413
7531
|
if (!parsed.success) {
|
|
6414
7532
|
const obj = json2;
|
|
6415
7533
|
const cmd = obj && typeof obj === "object" && typeof obj.cmd === "string" ? obj.cmd : undefined;
|
|
6416
|
-
|
|
7534
|
+
this.sendResponse(socket, cmd ? {
|
|
6417
7535
|
ok: false,
|
|
6418
7536
|
error: `Unknown command: ${cmd}`,
|
|
6419
7537
|
suggestion: "-> Try: agent-dbg --help"
|
|
6420
7538
|
} : {
|
|
6421
7539
|
ok: false,
|
|
6422
7540
|
error: "Invalid request: must have { cmd: string, args: object }"
|
|
6423
|
-
};
|
|
6424
|
-
socket.write(`${JSON.stringify(errResponse)}
|
|
6425
|
-
`);
|
|
6426
|
-
socket.end();
|
|
7541
|
+
});
|
|
6427
7542
|
return;
|
|
6428
7543
|
}
|
|
6429
7544
|
const request = parsed.data;
|
|
6430
7545
|
if (!this.handler) {
|
|
6431
|
-
|
|
7546
|
+
this.sendResponse(socket, {
|
|
6432
7547
|
ok: false,
|
|
6433
7548
|
error: "No request handler registered"
|
|
6434
|
-
};
|
|
6435
|
-
socket.write(`${JSON.stringify(errResponse)}
|
|
6436
|
-
`);
|
|
6437
|
-
socket.end();
|
|
7549
|
+
});
|
|
6438
7550
|
return;
|
|
6439
7551
|
}
|
|
6440
7552
|
this.handler(request).then((response) => {
|
|
6441
|
-
|
|
6442
|
-
`);
|
|
6443
|
-
socket.end();
|
|
7553
|
+
this.sendResponse(socket, response);
|
|
6444
7554
|
}).catch((err) => {
|
|
6445
|
-
|
|
7555
|
+
this.sendResponse(socket, {
|
|
6446
7556
|
ok: false,
|
|
6447
7557
|
error: err instanceof Error ? err.message : String(err)
|
|
6448
|
-
};
|
|
6449
|
-
socket.write(`${JSON.stringify(errResponse)}
|
|
6450
|
-
`);
|
|
6451
|
-
socket.end();
|
|
7558
|
+
});
|
|
6452
7559
|
});
|
|
6453
7560
|
}
|
|
6454
7561
|
resetIdleTimer() {
|
|
@@ -6457,6 +7564,7 @@ class DaemonServer {
|
|
|
6457
7564
|
}
|
|
6458
7565
|
if (this.idleTimeout > 0) {
|
|
6459
7566
|
this.idleTimer = setTimeout(() => {
|
|
7567
|
+
this.logger?.info("daemon.idle", `Idle timeout reached (${this.idleTimeout}s), shutting down`);
|
|
6460
7568
|
this.stop();
|
|
6461
7569
|
}, this.idleTimeout * 1000);
|
|
6462
7570
|
}
|
|
@@ -6540,7 +7648,7 @@ class CdpClient {
|
|
|
6540
7648
|
this.pending.delete(id);
|
|
6541
7649
|
this.sentMethods.delete(id);
|
|
6542
7650
|
reject(new Error(`CDP request timed out: ${method} (id=${id})`));
|
|
6543
|
-
},
|
|
7651
|
+
}, DEFAULT_TIMEOUT_MS2);
|
|
6544
7652
|
this.pending.set(id, { resolve, reject, timer });
|
|
6545
7653
|
this.ws.send(JSON.stringify(request));
|
|
6546
7654
|
});
|
|
@@ -6644,17 +7752,17 @@ class CdpClient {
|
|
|
6644
7752
|
}
|
|
6645
7753
|
}
|
|
6646
7754
|
}
|
|
6647
|
-
var
|
|
7755
|
+
var DEFAULT_TIMEOUT_MS2 = 30000;
|
|
6648
7756
|
|
|
6649
7757
|
// src/cdp/logger.ts
|
|
6650
|
-
import { appendFileSync, writeFileSync as
|
|
7758
|
+
import { appendFileSync as appendFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
6651
7759
|
|
|
6652
7760
|
class CdpLogger {
|
|
6653
7761
|
logPath;
|
|
6654
7762
|
pending = new Map;
|
|
6655
7763
|
constructor(logPath) {
|
|
6656
7764
|
this.logPath = logPath;
|
|
6657
|
-
|
|
7765
|
+
writeFileSync3(logPath, "");
|
|
6658
7766
|
}
|
|
6659
7767
|
logSend(id, method, params) {
|
|
6660
7768
|
this.pending.set(id, { method, sentAt: Date.now() });
|
|
@@ -6687,14 +7795,14 @@ class CdpLogger {
|
|
|
6687
7795
|
this.append(entry);
|
|
6688
7796
|
}
|
|
6689
7797
|
clear() {
|
|
6690
|
-
|
|
7798
|
+
writeFileSync3(this.logPath, "");
|
|
6691
7799
|
}
|
|
6692
7800
|
append(entry) {
|
|
6693
|
-
|
|
7801
|
+
appendFileSync2(this.logPath, `${JSON.stringify(entry)}
|
|
6694
7802
|
`);
|
|
6695
7803
|
}
|
|
6696
7804
|
}
|
|
6697
|
-
var
|
|
7805
|
+
var init_logger2 = () => {};
|
|
6698
7806
|
|
|
6699
7807
|
// src/formatter/values.ts
|
|
6700
7808
|
function truncate(str, max) {
|
|
@@ -6901,101 +8009,6 @@ function formatValue(obj, maxLen = 80) {
|
|
|
6901
8009
|
return truncate(obj.description ?? String(obj.value ?? obj.type), maxLen);
|
|
6902
8010
|
}
|
|
6903
8011
|
|
|
6904
|
-
// src/refs/ref-table.ts
|
|
6905
|
-
class RefTable {
|
|
6906
|
-
entries = new Map;
|
|
6907
|
-
counters = {
|
|
6908
|
-
v: 1,
|
|
6909
|
-
f: 0,
|
|
6910
|
-
o: 1,
|
|
6911
|
-
BP: 1,
|
|
6912
|
-
LP: 1,
|
|
6913
|
-
HS: 1
|
|
6914
|
-
};
|
|
6915
|
-
addVar(remoteId, name, meta) {
|
|
6916
|
-
return this.add("v", remoteId, name, meta);
|
|
6917
|
-
}
|
|
6918
|
-
addFrame(remoteId, name, meta) {
|
|
6919
|
-
return this.add("f", remoteId, name, meta);
|
|
6920
|
-
}
|
|
6921
|
-
addObject(remoteId, name, meta) {
|
|
6922
|
-
return this.add("o", remoteId, name, meta);
|
|
6923
|
-
}
|
|
6924
|
-
addBreakpoint(remoteId, meta) {
|
|
6925
|
-
return this.add("BP", remoteId, undefined, meta);
|
|
6926
|
-
}
|
|
6927
|
-
addLogpoint(remoteId, meta) {
|
|
6928
|
-
return this.add("LP", remoteId, undefined, meta);
|
|
6929
|
-
}
|
|
6930
|
-
addHeapSnapshot(remoteId, meta) {
|
|
6931
|
-
return this.add("HS", remoteId, undefined, meta);
|
|
6932
|
-
}
|
|
6933
|
-
resolve(ref) {
|
|
6934
|
-
return this.entries.get(ref);
|
|
6935
|
-
}
|
|
6936
|
-
resolveId(ref) {
|
|
6937
|
-
return this.entries.get(ref)?.remoteId;
|
|
6938
|
-
}
|
|
6939
|
-
clearVolatile() {
|
|
6940
|
-
for (const [key, entry] of this.entries) {
|
|
6941
|
-
if (entry.type === "v" || entry.type === "f") {
|
|
6942
|
-
this.entries.delete(key);
|
|
6943
|
-
}
|
|
6944
|
-
}
|
|
6945
|
-
this.counters.v = 1;
|
|
6946
|
-
this.counters.f = 0;
|
|
6947
|
-
}
|
|
6948
|
-
clearObjects() {
|
|
6949
|
-
for (const [key, entry] of this.entries) {
|
|
6950
|
-
if (entry.type === "o") {
|
|
6951
|
-
this.entries.delete(key);
|
|
6952
|
-
}
|
|
6953
|
-
}
|
|
6954
|
-
this.counters.o = 1;
|
|
6955
|
-
}
|
|
6956
|
-
clearAll() {
|
|
6957
|
-
this.entries.clear();
|
|
6958
|
-
this.counters = { v: 1, f: 0, o: 1, BP: 1, LP: 1, HS: 1 };
|
|
6959
|
-
}
|
|
6960
|
-
list(type) {
|
|
6961
|
-
const result = [];
|
|
6962
|
-
for (const entry of this.entries.values()) {
|
|
6963
|
-
if (entry.type === type) {
|
|
6964
|
-
result.push(entry);
|
|
6965
|
-
}
|
|
6966
|
-
}
|
|
6967
|
-
return result;
|
|
6968
|
-
}
|
|
6969
|
-
remove(ref) {
|
|
6970
|
-
return this.entries.delete(ref);
|
|
6971
|
-
}
|
|
6972
|
-
add(type, remoteId, name, meta) {
|
|
6973
|
-
const num = this.counters[type];
|
|
6974
|
-
this.counters[type] = num + 1;
|
|
6975
|
-
const ref = `${PREFIXES[type]}${num}`;
|
|
6976
|
-
const entry = { ref, type, remoteId };
|
|
6977
|
-
if (name !== undefined) {
|
|
6978
|
-
entry.name = name;
|
|
6979
|
-
}
|
|
6980
|
-
if (meta !== undefined) {
|
|
6981
|
-
entry.meta = meta;
|
|
6982
|
-
}
|
|
6983
|
-
this.entries.set(ref, entry);
|
|
6984
|
-
return ref;
|
|
6985
|
-
}
|
|
6986
|
-
}
|
|
6987
|
-
var PREFIXES;
|
|
6988
|
-
var init_ref_table = __esm(() => {
|
|
6989
|
-
PREFIXES = {
|
|
6990
|
-
v: "@v",
|
|
6991
|
-
f: "@f",
|
|
6992
|
-
o: "@o",
|
|
6993
|
-
BP: "BP#",
|
|
6994
|
-
LP: "LP#",
|
|
6995
|
-
HS: "HS#"
|
|
6996
|
-
};
|
|
6997
|
-
});
|
|
6998
|
-
|
|
6999
8012
|
// node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs
|
|
7000
8013
|
function decodeInteger(reader, relative) {
|
|
7001
8014
|
let value = 0;
|
|
@@ -8819,7 +9832,8 @@ async function hotpatch(session, file2, newSource, options = {}) {
|
|
|
8819
9832
|
}
|
|
8820
9833
|
const setSourceParams = {
|
|
8821
9834
|
scriptId,
|
|
8822
|
-
scriptSource: newSource
|
|
9835
|
+
scriptSource: newSource,
|
|
9836
|
+
allowTopFrameEditing: true
|
|
8823
9837
|
};
|
|
8824
9838
|
if (options.dryRun) {
|
|
8825
9839
|
setSourceParams.dryRun = true;
|
|
@@ -9040,10 +10054,12 @@ class DebugSession {
|
|
|
9040
10054
|
launchCommand = null;
|
|
9041
10055
|
launchOptions = null;
|
|
9042
10056
|
cdpLogger;
|
|
9043
|
-
|
|
10057
|
+
daemonLogger;
|
|
10058
|
+
constructor(session, options) {
|
|
9044
10059
|
this.session = session;
|
|
9045
10060
|
ensureSocketDir();
|
|
9046
10061
|
this.cdpLogger = new CdpLogger(getLogPath(session));
|
|
10062
|
+
this.daemonLogger = options?.daemonLogger ?? new DaemonLogger(getDaemonLogPath(session));
|
|
9047
10063
|
}
|
|
9048
10064
|
async launch(command, options = {}) {
|
|
9049
10065
|
if (this.state !== "idle") {
|
|
@@ -9062,13 +10078,20 @@ class DebugSession {
|
|
|
9062
10078
|
const spawnArgs = [runtime, inspectFlag, ...rest];
|
|
9063
10079
|
const proc = Bun.spawn(spawnArgs, {
|
|
9064
10080
|
stdin: "ignore",
|
|
9065
|
-
stdout: "
|
|
10081
|
+
stdout: "ignore",
|
|
9066
10082
|
stderr: "pipe"
|
|
9067
10083
|
});
|
|
9068
10084
|
this.childProcess = proc;
|
|
10085
|
+
this.daemonLogger.info("child.spawn", `Spawned process pid=${proc.pid}`, {
|
|
10086
|
+
pid: proc.pid,
|
|
10087
|
+
command: spawnArgs
|
|
10088
|
+
});
|
|
9069
10089
|
this.monitorProcessExit(proc);
|
|
9070
10090
|
const wsUrl = await this.readInspectorUrl(proc.stderr);
|
|
9071
10091
|
this.wsUrl = wsUrl;
|
|
10092
|
+
this.daemonLogger.info("inspector.detected", `Inspector URL: ${wsUrl}`, {
|
|
10093
|
+
wsUrl
|
|
10094
|
+
});
|
|
9072
10095
|
await this.connectCdp(wsUrl);
|
|
9073
10096
|
if (brk) {
|
|
9074
10097
|
await this.waitForBrkPause();
|
|
@@ -9382,8 +10405,10 @@ class DebugSession {
|
|
|
9382
10405
|
}
|
|
9383
10406
|
}
|
|
9384
10407
|
async connectCdp(wsUrl) {
|
|
10408
|
+
this.daemonLogger.debug("cdp.connecting", `Connecting to ${wsUrl}`);
|
|
9385
10409
|
const cdp = await CdpClient.connect(wsUrl, this.cdpLogger);
|
|
9386
10410
|
this.cdp = cdp;
|
|
10411
|
+
this.daemonLogger.info("cdp.connected", `CDP connected to ${wsUrl}`);
|
|
9387
10412
|
this.setupCdpEventHandlers(cdp);
|
|
9388
10413
|
await cdp.enableDomains();
|
|
9389
10414
|
if (this.blackboxPatterns.length > 0) {
|
|
@@ -9490,7 +10515,11 @@ class DebugSession {
|
|
|
9490
10515
|
});
|
|
9491
10516
|
}
|
|
9492
10517
|
monitorProcessExit(proc) {
|
|
9493
|
-
proc.exited.then(() => {
|
|
10518
|
+
proc.exited.then((exitCode) => {
|
|
10519
|
+
this.daemonLogger.info("child.exit", `Process exited with code ${exitCode ?? "unknown"}`, {
|
|
10520
|
+
pid: proc.pid,
|
|
10521
|
+
exitCode: exitCode ?? null
|
|
10522
|
+
});
|
|
9494
10523
|
this.childProcess = null;
|
|
9495
10524
|
if (this.cdp) {
|
|
9496
10525
|
this.cdp.disconnect();
|
|
@@ -9499,7 +10528,10 @@ class DebugSession {
|
|
|
9499
10528
|
this.state = "idle";
|
|
9500
10529
|
this.pauseInfo = null;
|
|
9501
10530
|
this.onProcessExit?.();
|
|
9502
|
-
}).catch(() => {
|
|
10531
|
+
}).catch((err) => {
|
|
10532
|
+
this.daemonLogger.error("child.exit.error", `Error waiting for process exit: ${err}`, {
|
|
10533
|
+
pid: proc.pid
|
|
10534
|
+
});
|
|
9503
10535
|
this.childProcess = null;
|
|
9504
10536
|
this.state = "idle";
|
|
9505
10537
|
this.pauseInfo = null;
|
|
@@ -9518,16 +10550,22 @@ class DebugSession {
|
|
|
9518
10550
|
if (done) {
|
|
9519
10551
|
break;
|
|
9520
10552
|
}
|
|
9521
|
-
|
|
10553
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
10554
|
+
accumulated += chunk;
|
|
10555
|
+
this.daemonLogger.debug("child.stderr", chunk.trimEnd());
|
|
9522
10556
|
const match = INSPECTOR_URL_REGEX.exec(accumulated);
|
|
9523
10557
|
if (match?.[1]) {
|
|
9524
10558
|
clearTimeout(timeout);
|
|
9525
|
-
|
|
10559
|
+
this.drainReader(reader);
|
|
9526
10560
|
return match[1];
|
|
9527
10561
|
}
|
|
9528
10562
|
}
|
|
9529
10563
|
} catch {}
|
|
9530
10564
|
clearTimeout(timeout);
|
|
10565
|
+
this.daemonLogger.error("inspector.failed", "Failed to detect inspector URL", {
|
|
10566
|
+
stderr: accumulated.slice(0, 2000),
|
|
10567
|
+
timeoutMs: INSPECTOR_TIMEOUT_MS
|
|
10568
|
+
});
|
|
9531
10569
|
throw new Error(`Failed to detect inspector URL within ${INSPECTOR_TIMEOUT_MS}ms. Stderr: ${accumulated.slice(0, 500)}`);
|
|
9532
10570
|
}
|
|
9533
10571
|
async discoverWsUrl(port) {
|
|
@@ -9552,12 +10590,22 @@ class DebugSession {
|
|
|
9552
10590
|
}
|
|
9553
10591
|
return wsUrl;
|
|
9554
10592
|
}
|
|
10593
|
+
drainReader(reader) {
|
|
10594
|
+
const pump = () => {
|
|
10595
|
+
reader.read().then(({ done }) => {
|
|
10596
|
+
if (!done)
|
|
10597
|
+
pump();
|
|
10598
|
+
}).catch(() => {});
|
|
10599
|
+
};
|
|
10600
|
+
pump();
|
|
10601
|
+
}
|
|
9555
10602
|
}
|
|
9556
10603
|
var INSPECTOR_URL_REGEX, INSPECTOR_TIMEOUT_MS = 5000;
|
|
9557
|
-
var
|
|
9558
|
-
|
|
10604
|
+
var init_session2 = __esm(() => {
|
|
10605
|
+
init_logger2();
|
|
9559
10606
|
init_ref_table();
|
|
9560
10607
|
init_resolver();
|
|
10608
|
+
init_logger();
|
|
9561
10609
|
init_paths();
|
|
9562
10610
|
init_session_inspection();
|
|
9563
10611
|
init_session_mutation();
|
|
@@ -9567,10 +10615,19 @@ var init_session = __esm(() => {
|
|
|
9567
10615
|
|
|
9568
10616
|
// src/daemon/entry.ts
|
|
9569
10617
|
var exports_entry = {};
|
|
9570
|
-
|
|
10618
|
+
function isDapRuntime(runtime) {
|
|
10619
|
+
return runtime !== undefined && runtime !== "node";
|
|
10620
|
+
}
|
|
10621
|
+
function activeSession() {
|
|
10622
|
+
return dapSession ?? cdpSession;
|
|
10623
|
+
}
|
|
10624
|
+
var daemonIdx, session, timeout = 300, timeoutIdx, daemonLogger, server, cdpSession, dapSession = null;
|
|
9571
10625
|
var init_entry = __esm(async () => {
|
|
9572
|
-
init_server();
|
|
9573
10626
|
init_session();
|
|
10627
|
+
init_logger();
|
|
10628
|
+
init_paths();
|
|
10629
|
+
init_server();
|
|
10630
|
+
init_session2();
|
|
9574
10631
|
daemonIdx = process.argv.indexOf("--daemon");
|
|
9575
10632
|
session = daemonIdx !== -1 ? process.argv[daemonIdx + 1] : process.argv[2];
|
|
9576
10633
|
if (!session) {
|
|
@@ -9587,53 +10644,70 @@ var init_entry = __esm(async () => {
|
|
|
9587
10644
|
}
|
|
9588
10645
|
}
|
|
9589
10646
|
}
|
|
9590
|
-
|
|
9591
|
-
|
|
10647
|
+
ensureSocketDir();
|
|
10648
|
+
daemonLogger = new DaemonLogger(getDaemonLogPath(session));
|
|
10649
|
+
daemonLogger.info("daemon.start", `Daemon starting for session "${session}"`, {
|
|
10650
|
+
pid: process.pid,
|
|
10651
|
+
session,
|
|
10652
|
+
timeout
|
|
10653
|
+
});
|
|
10654
|
+
server = new DaemonServer(session, { idleTimeout: timeout, logger: daemonLogger });
|
|
10655
|
+
cdpSession = new DebugSession(session, { daemonLogger });
|
|
9592
10656
|
server.onRequest(async (req) => {
|
|
9593
10657
|
switch (req.cmd) {
|
|
9594
10658
|
case "ping":
|
|
9595
10659
|
return { ok: true, data: "pong" };
|
|
9596
10660
|
case "launch": {
|
|
9597
|
-
const { command, brk = true, port } = req.args;
|
|
9598
|
-
|
|
10661
|
+
const { command, brk = true, port, runtime } = req.args;
|
|
10662
|
+
if (isDapRuntime(runtime)) {
|
|
10663
|
+
dapSession = new DapSession(session, runtime);
|
|
10664
|
+
const result2 = await dapSession.launch(command, { brk });
|
|
10665
|
+
return { ok: true, data: result2 };
|
|
10666
|
+
}
|
|
10667
|
+
const result = await cdpSession.launch(command, { brk, port });
|
|
9599
10668
|
return { ok: true, data: result };
|
|
9600
10669
|
}
|
|
9601
10670
|
case "attach": {
|
|
9602
|
-
const { target } = req.args;
|
|
9603
|
-
|
|
10671
|
+
const { target, runtime } = req.args;
|
|
10672
|
+
if (isDapRuntime(runtime)) {
|
|
10673
|
+
dapSession = new DapSession(session, runtime);
|
|
10674
|
+
const result2 = await dapSession.attach(target);
|
|
10675
|
+
return { ok: true, data: result2 };
|
|
10676
|
+
}
|
|
10677
|
+
const result = await cdpSession.attach(target);
|
|
9604
10678
|
return { ok: true, data: result };
|
|
9605
10679
|
}
|
|
9606
10680
|
case "status":
|
|
9607
|
-
return { ok: true, data:
|
|
10681
|
+
return { ok: true, data: activeSession().getStatus() };
|
|
9608
10682
|
case "state": {
|
|
9609
|
-
const stateResult = await
|
|
10683
|
+
const stateResult = await activeSession().buildState(req.args);
|
|
9610
10684
|
return { ok: true, data: stateResult };
|
|
9611
10685
|
}
|
|
9612
10686
|
case "continue": {
|
|
9613
|
-
await
|
|
9614
|
-
const stateAfter = await
|
|
10687
|
+
await activeSession().continue();
|
|
10688
|
+
const stateAfter = await activeSession().buildState();
|
|
9615
10689
|
return { ok: true, data: stateAfter };
|
|
9616
10690
|
}
|
|
9617
10691
|
case "step": {
|
|
9618
10692
|
const { mode = "over" } = req.args;
|
|
9619
|
-
await
|
|
9620
|
-
const stateAfter = await
|
|
10693
|
+
await activeSession().step(mode);
|
|
10694
|
+
const stateAfter = await activeSession().buildState();
|
|
9621
10695
|
return { ok: true, data: stateAfter };
|
|
9622
10696
|
}
|
|
9623
10697
|
case "pause": {
|
|
9624
|
-
await
|
|
9625
|
-
const stateAfter = await
|
|
10698
|
+
await activeSession().pause();
|
|
10699
|
+
const stateAfter = await activeSession().buildState();
|
|
9626
10700
|
return { ok: true, data: stateAfter };
|
|
9627
10701
|
}
|
|
9628
10702
|
case "run-to": {
|
|
9629
10703
|
const { file: file2, line } = req.args;
|
|
9630
|
-
await
|
|
9631
|
-
const stateAfter = await
|
|
10704
|
+
await activeSession().runTo(file2, line);
|
|
10705
|
+
const stateAfter = await activeSession().buildState();
|
|
9632
10706
|
return { ok: true, data: stateAfter };
|
|
9633
10707
|
}
|
|
9634
10708
|
case "break": {
|
|
9635
10709
|
const { file: file2, line, condition, hitCount, urlRegex: urlRegex2, column } = req.args;
|
|
9636
|
-
const bpResult = await
|
|
10710
|
+
const bpResult = await activeSession().setBreakpoint(file2, line, {
|
|
9637
10711
|
condition,
|
|
9638
10712
|
hitCount,
|
|
9639
10713
|
urlRegex: urlRegex2,
|
|
@@ -9641,20 +10715,35 @@ var init_entry = __esm(async () => {
|
|
|
9641
10715
|
});
|
|
9642
10716
|
return { ok: true, data: bpResult };
|
|
9643
10717
|
}
|
|
10718
|
+
case "break-fn": {
|
|
10719
|
+
const session2 = activeSession();
|
|
10720
|
+
if (!("setFunctionBreakpoint" in session2)) {
|
|
10721
|
+
return {
|
|
10722
|
+
ok: false,
|
|
10723
|
+
error: "Function breakpoints are only supported with DAP runtimes (e.g. --runtime lldb)",
|
|
10724
|
+
suggestion: "Use 'break <file>:<line>' for CDP sessions"
|
|
10725
|
+
};
|
|
10726
|
+
}
|
|
10727
|
+
const { name, condition } = req.args;
|
|
10728
|
+
const bpResult = await session2.setFunctionBreakpoint(name, {
|
|
10729
|
+
condition
|
|
10730
|
+
});
|
|
10731
|
+
return { ok: true, data: bpResult };
|
|
10732
|
+
}
|
|
9644
10733
|
case "break-rm": {
|
|
9645
10734
|
const { ref } = req.args;
|
|
9646
10735
|
if (ref === "all") {
|
|
9647
|
-
await
|
|
10736
|
+
await activeSession().removeAllBreakpoints();
|
|
9648
10737
|
return { ok: true, data: "all removed" };
|
|
9649
10738
|
}
|
|
9650
|
-
await
|
|
10739
|
+
await activeSession().removeBreakpoint(ref);
|
|
9651
10740
|
return { ok: true, data: "removed" };
|
|
9652
10741
|
}
|
|
9653
10742
|
case "break-ls":
|
|
9654
|
-
return { ok: true, data:
|
|
10743
|
+
return { ok: true, data: activeSession().listBreakpoints() };
|
|
9655
10744
|
case "logpoint": {
|
|
9656
10745
|
const { file: file2, line, template, condition, maxEmissions } = req.args;
|
|
9657
|
-
const lpResult = await
|
|
10746
|
+
const lpResult = await activeSession().setLogpoint(file2, line, template, {
|
|
9658
10747
|
condition,
|
|
9659
10748
|
maxEmissions
|
|
9660
10749
|
});
|
|
@@ -9662,114 +10751,115 @@ var init_entry = __esm(async () => {
|
|
|
9662
10751
|
}
|
|
9663
10752
|
case "catch": {
|
|
9664
10753
|
const { mode } = req.args;
|
|
9665
|
-
await
|
|
10754
|
+
await activeSession().setExceptionPause(mode);
|
|
9666
10755
|
return { ok: true, data: mode };
|
|
9667
10756
|
}
|
|
9668
10757
|
case "source": {
|
|
9669
|
-
const sourceResult = await
|
|
10758
|
+
const sourceResult = await activeSession().getSource(req.args);
|
|
9670
10759
|
return { ok: true, data: sourceResult };
|
|
9671
10760
|
}
|
|
9672
10761
|
case "scripts": {
|
|
9673
10762
|
const { filter } = req.args;
|
|
9674
|
-
const scriptsResult =
|
|
10763
|
+
const scriptsResult = activeSession().getScripts(filter);
|
|
9675
10764
|
return { ok: true, data: scriptsResult };
|
|
9676
10765
|
}
|
|
9677
10766
|
case "stack": {
|
|
9678
|
-
const stackResult =
|
|
10767
|
+
const stackResult = activeSession().getStack(req.args);
|
|
9679
10768
|
return { ok: true, data: stackResult };
|
|
9680
10769
|
}
|
|
9681
10770
|
case "search": {
|
|
9682
10771
|
const { query, ...searchOptions } = req.args;
|
|
9683
|
-
const searchResult = await
|
|
10772
|
+
const searchResult = await activeSession().searchInScripts(query, searchOptions);
|
|
9684
10773
|
return { ok: true, data: searchResult };
|
|
9685
10774
|
}
|
|
9686
10775
|
case "console": {
|
|
9687
|
-
const consoleResult =
|
|
10776
|
+
const consoleResult = activeSession().getConsoleMessages(req.args);
|
|
9688
10777
|
return { ok: true, data: consoleResult };
|
|
9689
10778
|
}
|
|
9690
10779
|
case "exceptions": {
|
|
9691
|
-
const exceptionsResult =
|
|
10780
|
+
const exceptionsResult = activeSession().getExceptions(req.args);
|
|
9692
10781
|
return { ok: true, data: exceptionsResult };
|
|
9693
10782
|
}
|
|
9694
10783
|
case "eval": {
|
|
9695
10784
|
const { expression, ...evalOptions } = req.args;
|
|
9696
|
-
const evalResult = await
|
|
10785
|
+
const evalResult = await activeSession().eval(expression, evalOptions);
|
|
9697
10786
|
return { ok: true, data: evalResult };
|
|
9698
10787
|
}
|
|
9699
10788
|
case "vars": {
|
|
9700
|
-
const varsResult = await
|
|
10789
|
+
const varsResult = await activeSession().getVars(req.args);
|
|
9701
10790
|
return { ok: true, data: varsResult };
|
|
9702
10791
|
}
|
|
9703
10792
|
case "props": {
|
|
9704
10793
|
const { ref, ...propsOptions } = req.args;
|
|
9705
|
-
const propsResult = await
|
|
10794
|
+
const propsResult = await activeSession().getProps(ref, propsOptions);
|
|
9706
10795
|
return { ok: true, data: propsResult };
|
|
9707
10796
|
}
|
|
9708
10797
|
case "blackbox": {
|
|
9709
10798
|
const { patterns } = req.args;
|
|
9710
|
-
const result = await
|
|
10799
|
+
const result = await activeSession().addBlackbox(patterns);
|
|
9711
10800
|
return { ok: true, data: result };
|
|
9712
10801
|
}
|
|
9713
10802
|
case "blackbox-ls": {
|
|
9714
|
-
return { ok: true, data:
|
|
10803
|
+
return { ok: true, data: activeSession().listBlackbox() };
|
|
9715
10804
|
}
|
|
9716
10805
|
case "blackbox-rm": {
|
|
9717
10806
|
const { patterns } = req.args;
|
|
9718
|
-
const result = await
|
|
10807
|
+
const result = await activeSession().removeBlackbox(patterns);
|
|
9719
10808
|
return { ok: true, data: result };
|
|
9720
10809
|
}
|
|
9721
10810
|
case "set": {
|
|
9722
10811
|
const { name, value, frame } = req.args;
|
|
9723
|
-
const result = await
|
|
10812
|
+
const result = await activeSession().setVariable(name, value, { frame });
|
|
9724
10813
|
return { ok: true, data: result };
|
|
9725
10814
|
}
|
|
9726
10815
|
case "set-return": {
|
|
9727
10816
|
const { value } = req.args;
|
|
9728
|
-
const result = await
|
|
10817
|
+
const result = await activeSession().setReturnValue(value);
|
|
9729
10818
|
return { ok: true, data: result };
|
|
9730
10819
|
}
|
|
9731
10820
|
case "hotpatch": {
|
|
9732
10821
|
const { file: file2, source, dryRun } = req.args;
|
|
9733
|
-
const result = await
|
|
10822
|
+
const result = await activeSession().hotpatch(file2, source, { dryRun });
|
|
9734
10823
|
return { ok: true, data: result };
|
|
9735
10824
|
}
|
|
9736
10825
|
case "break-toggle": {
|
|
9737
10826
|
const { ref } = req.args;
|
|
9738
|
-
const toggleResult = await
|
|
10827
|
+
const toggleResult = await activeSession().toggleBreakpoint(ref);
|
|
9739
10828
|
return { ok: true, data: toggleResult };
|
|
9740
10829
|
}
|
|
9741
10830
|
case "breakable": {
|
|
9742
10831
|
const { file: file2, startLine, endLine } = req.args;
|
|
9743
|
-
const breakableResult = await
|
|
10832
|
+
const breakableResult = await activeSession().getBreakableLocations(file2, startLine, endLine);
|
|
9744
10833
|
return { ok: true, data: breakableResult };
|
|
9745
10834
|
}
|
|
9746
10835
|
case "restart-frame": {
|
|
9747
10836
|
const { frameRef } = req.args;
|
|
9748
|
-
const restartResult = await
|
|
10837
|
+
const restartResult = await activeSession().restartFrame(frameRef);
|
|
9749
10838
|
return { ok: true, data: restartResult };
|
|
9750
10839
|
}
|
|
9751
10840
|
case "sourcemap": {
|
|
9752
10841
|
const { file: smFile } = req.args;
|
|
9753
10842
|
if (smFile) {
|
|
9754
|
-
const match =
|
|
10843
|
+
const match = activeSession().sourceMapResolver.findScriptForSource(smFile);
|
|
9755
10844
|
if (match) {
|
|
9756
|
-
const info =
|
|
10845
|
+
const info = activeSession().sourceMapResolver.getInfo(match.scriptId);
|
|
9757
10846
|
return { ok: true, data: info ? [info] : [] };
|
|
9758
10847
|
}
|
|
9759
10848
|
return { ok: true, data: [] };
|
|
9760
10849
|
}
|
|
9761
|
-
return { ok: true, data:
|
|
10850
|
+
return { ok: true, data: activeSession().sourceMapResolver.getAllInfos() };
|
|
9762
10851
|
}
|
|
9763
10852
|
case "sourcemap-disable": {
|
|
9764
|
-
|
|
10853
|
+
activeSession().sourceMapResolver.setDisabled(true);
|
|
9765
10854
|
return { ok: true, data: "disabled" };
|
|
9766
10855
|
}
|
|
9767
10856
|
case "restart": {
|
|
9768
|
-
const result = await
|
|
10857
|
+
const result = await activeSession().restart();
|
|
9769
10858
|
return { ok: true, data: result };
|
|
9770
10859
|
}
|
|
9771
10860
|
case "stop":
|
|
9772
|
-
await
|
|
10861
|
+
await activeSession().stop();
|
|
10862
|
+
dapSession = null;
|
|
9773
10863
|
setTimeout(() => {
|
|
9774
10864
|
server.stop();
|
|
9775
10865
|
process.exit(0);
|
|
@@ -9790,7 +10880,7 @@ var init_registry = __esm(() => {
|
|
|
9790
10880
|
});
|
|
9791
10881
|
|
|
9792
10882
|
// src/daemon/client.ts
|
|
9793
|
-
import { existsSync as existsSync3, readdirSync } from "fs";
|
|
10883
|
+
import { existsSync as existsSync3, readdirSync, readFileSync, unlinkSync as unlinkSync2 } from "fs";
|
|
9794
10884
|
|
|
9795
10885
|
class DaemonClient {
|
|
9796
10886
|
session;
|
|
@@ -9810,9 +10900,9 @@ class DaemonClient {
|
|
|
9810
10900
|
const timer = setTimeout(() => {
|
|
9811
10901
|
if (!settled) {
|
|
9812
10902
|
settled = true;
|
|
9813
|
-
reject(new Error(`Request timed out after ${
|
|
10903
|
+
reject(new Error(`Request timed out after ${DEFAULT_TIMEOUT_MS3}ms`));
|
|
9814
10904
|
}
|
|
9815
|
-
},
|
|
10905
|
+
}, DEFAULT_TIMEOUT_MS3);
|
|
9816
10906
|
Bun.connect({
|
|
9817
10907
|
unix: socketPath,
|
|
9818
10908
|
socket: {
|
|
@@ -9890,12 +10980,28 @@ class DaemonClient {
|
|
|
9890
10980
|
if (!existsSync3(socketPath)) {
|
|
9891
10981
|
return false;
|
|
9892
10982
|
}
|
|
10983
|
+
const lockPath = getLockPath(session2);
|
|
10984
|
+
if (!existsSync3(lockPath)) {
|
|
10985
|
+
return false;
|
|
10986
|
+
}
|
|
9893
10987
|
try {
|
|
10988
|
+
const pid = parseInt(readFileSync(lockPath, "utf-8"), 10);
|
|
10989
|
+
if (Number.isNaN(pid))
|
|
10990
|
+
return false;
|
|
10991
|
+
process.kill(pid, 0);
|
|
9894
10992
|
return true;
|
|
9895
10993
|
} catch {
|
|
9896
10994
|
return false;
|
|
9897
10995
|
}
|
|
9898
10996
|
}
|
|
10997
|
+
static cleanStaleFiles(session2) {
|
|
10998
|
+
const socketPath = getSocketPath(session2);
|
|
10999
|
+
const lockPath = getLockPath(session2);
|
|
11000
|
+
if (existsSync3(socketPath))
|
|
11001
|
+
unlinkSync2(socketPath);
|
|
11002
|
+
if (existsSync3(lockPath))
|
|
11003
|
+
unlinkSync2(lockPath);
|
|
11004
|
+
}
|
|
9899
11005
|
static async isAlive(session2) {
|
|
9900
11006
|
const socketPath = getSocketPath(session2);
|
|
9901
11007
|
if (!existsSync3(socketPath)) {
|
|
@@ -9918,20 +11024,20 @@ class DaemonClient {
|
|
|
9918
11024
|
return files.filter((f) => f.endsWith(".sock")).map((f) => f.slice(0, -5));
|
|
9919
11025
|
}
|
|
9920
11026
|
}
|
|
9921
|
-
var
|
|
11027
|
+
var DEFAULT_TIMEOUT_MS3 = 30000;
|
|
9922
11028
|
var init_client = __esm(() => {
|
|
9923
11029
|
init_messages();
|
|
9924
11030
|
init_paths();
|
|
9925
11031
|
});
|
|
9926
11032
|
|
|
9927
11033
|
// src/daemon/spawn.ts
|
|
9928
|
-
import { existsSync as existsSync4 } from "fs";
|
|
11034
|
+
import { existsSync as existsSync4, openSync, readFileSync as readFileSync2 } from "fs";
|
|
9929
11035
|
async function spawnDaemon(session2, options = {}) {
|
|
9930
11036
|
const socketPath = getSocketPath(session2);
|
|
9931
11037
|
const spawnArgs = [];
|
|
9932
11038
|
const execPath = process.execPath;
|
|
9933
11039
|
const scriptPath = process.argv[1];
|
|
9934
|
-
if (scriptPath && scriptPath.endsWith(".ts")) {
|
|
11040
|
+
if (scriptPath && (scriptPath.endsWith(".ts") || scriptPath.endsWith(".js"))) {
|
|
9935
11041
|
spawnArgs.push(execPath, "run", scriptPath);
|
|
9936
11042
|
} else {
|
|
9937
11043
|
spawnArgs.push(execPath);
|
|
@@ -9940,11 +11046,13 @@ async function spawnDaemon(session2, options = {}) {
|
|
|
9940
11046
|
if (options.timeout !== undefined) {
|
|
9941
11047
|
spawnArgs.push("--timeout", String(options.timeout));
|
|
9942
11048
|
}
|
|
11049
|
+
ensureSocketDir();
|
|
11050
|
+
const logFd = openSync(getDaemonLogPath(session2), "a");
|
|
9943
11051
|
const proc = Bun.spawn(spawnArgs, {
|
|
9944
11052
|
detached: true,
|
|
9945
11053
|
stdin: "ignore",
|
|
9946
|
-
stdout:
|
|
9947
|
-
stderr:
|
|
11054
|
+
stdout: logFd,
|
|
11055
|
+
stderr: logFd
|
|
9948
11056
|
});
|
|
9949
11057
|
proc.unref();
|
|
9950
11058
|
const deadline = Date.now() + SPAWN_TIMEOUT_MS;
|
|
@@ -9954,10 +11062,34 @@ async function spawnDaemon(session2, options = {}) {
|
|
|
9954
11062
|
}
|
|
9955
11063
|
await Bun.sleep(POLL_INTERVAL_MS);
|
|
9956
11064
|
}
|
|
9957
|
-
|
|
11065
|
+
const logPath = getDaemonLogPath(session2);
|
|
11066
|
+
let logTail = "";
|
|
11067
|
+
try {
|
|
11068
|
+
const log = readFileSync2(logPath, "utf-8");
|
|
11069
|
+
const lines = log.trimEnd().split(`
|
|
11070
|
+
`);
|
|
11071
|
+
logTail = lines.slice(-20).join(`
|
|
11072
|
+
`);
|
|
11073
|
+
} catch {}
|
|
11074
|
+
const details = [
|
|
11075
|
+
`Daemon for session "${session2}" failed to start within ${SPAWN_TIMEOUT_MS}ms`,
|
|
11076
|
+
`Spawn command: ${spawnArgs.join(" ")}`,
|
|
11077
|
+
`Socket path: ${socketPath}`,
|
|
11078
|
+
logTail ? `Daemon log (last 20 lines):
|
|
11079
|
+
${logTail}` : `No daemon log at ${logPath}`
|
|
11080
|
+
].join(`
|
|
11081
|
+
`);
|
|
11082
|
+
throw new Error(details);
|
|
11083
|
+
}
|
|
11084
|
+
async function ensureDaemon(session2, options) {
|
|
11085
|
+
if (DaemonClient.isRunning(session2))
|
|
11086
|
+
return;
|
|
11087
|
+
DaemonClient.cleanStaleFiles(session2);
|
|
11088
|
+
await spawnDaemon(session2, options);
|
|
9958
11089
|
}
|
|
9959
11090
|
var POLL_INTERVAL_MS = 50, SPAWN_TIMEOUT_MS = 5000;
|
|
9960
11091
|
var init_spawn = __esm(() => {
|
|
11092
|
+
init_client();
|
|
9961
11093
|
init_paths();
|
|
9962
11094
|
});
|
|
9963
11095
|
|
|
@@ -9995,17 +11127,16 @@ var init_launch = __esm(() => {
|
|
|
9995
11127
|
const brk = args.flags.brk === true;
|
|
9996
11128
|
const port = typeof args.flags.port === "string" ? parseInt(args.flags.port, 10) : undefined;
|
|
9997
11129
|
const timeout2 = typeof args.flags.timeout === "string" ? parseInt(args.flags.timeout, 10) : undefined;
|
|
11130
|
+
const runtime = typeof args.flags.runtime === "string" ? args.flags.runtime : undefined;
|
|
9998
11131
|
const command = args.subcommand ? [args.subcommand, ...args.positionals] : [...args.positionals];
|
|
9999
11132
|
if (command.length === 0) {
|
|
10000
11133
|
console.error("No command specified");
|
|
10001
11134
|
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
10002
11135
|
return 1;
|
|
10003
11136
|
}
|
|
10004
|
-
|
|
10005
|
-
await spawnDaemon(session2, { timeout: timeout2 });
|
|
10006
|
-
}
|
|
11137
|
+
await ensureDaemon(session2, { timeout: timeout2 });
|
|
10007
11138
|
const client = new DaemonClient(session2);
|
|
10008
|
-
const response = await client.request("launch", { command, brk, port });
|
|
11139
|
+
const response = await client.request("launch", { command, brk, port, runtime });
|
|
10009
11140
|
if (!response.ok) {
|
|
10010
11141
|
console.error(`${response.error}`);
|
|
10011
11142
|
if (response.suggestion)
|
|
@@ -10038,6 +11169,7 @@ var init_attach = __esm(() => {
|
|
|
10038
11169
|
registerCommand("attach", async (args) => {
|
|
10039
11170
|
const session2 = args.global.session;
|
|
10040
11171
|
const target = args.subcommand ?? args.positionals[0];
|
|
11172
|
+
const runtime = typeof args.flags.runtime === "string" ? args.flags.runtime : undefined;
|
|
10041
11173
|
if (!target) {
|
|
10042
11174
|
console.error("No target specified");
|
|
10043
11175
|
console.error(" -> Try: agent-dbg attach <ws-url | port>");
|
|
@@ -10049,9 +11181,9 @@ var init_attach = __esm(() => {
|
|
|
10049
11181
|
return 1;
|
|
10050
11182
|
}
|
|
10051
11183
|
const timeout2 = typeof args.flags.timeout === "string" ? parseInt(args.flags.timeout, 10) : undefined;
|
|
10052
|
-
await
|
|
11184
|
+
await ensureDaemon(session2, { timeout: timeout2 });
|
|
10053
11185
|
const client = new DaemonClient(session2);
|
|
10054
|
-
const response = await client.request("attach", { target });
|
|
11186
|
+
const response = await client.request("attach", { target, runtime });
|
|
10055
11187
|
if (!response.ok) {
|
|
10056
11188
|
console.error(`${response.error}`);
|
|
10057
11189
|
if (response.suggestion)
|
|
@@ -10811,6 +11943,44 @@ var init_break = __esm(() => {
|
|
|
10811
11943
|
});
|
|
10812
11944
|
});
|
|
10813
11945
|
|
|
11946
|
+
// src/commands/break-fn.ts
|
|
11947
|
+
var exports_break_fn = {};
|
|
11948
|
+
var init_break_fn = __esm(() => {
|
|
11949
|
+
init_registry();
|
|
11950
|
+
init_client();
|
|
11951
|
+
registerCommand("break-fn", async (args) => {
|
|
11952
|
+
const session2 = args.global.session;
|
|
11953
|
+
if (!DaemonClient.isRunning(session2)) {
|
|
11954
|
+
console.error(`No active session "${session2}"`);
|
|
11955
|
+
console.error(" -> Try: agent-dbg launch --brk --runtime lldb ./program");
|
|
11956
|
+
return 1;
|
|
11957
|
+
}
|
|
11958
|
+
const name = args.subcommand;
|
|
11959
|
+
if (!name) {
|
|
11960
|
+
console.error("Usage: agent-dbg break-fn <function-name>");
|
|
11961
|
+
console.error(" Example: agent-dbg break-fn __assert_rtn");
|
|
11962
|
+
console.error(" Example: agent-dbg break-fn 'yoga::Style::operator=='");
|
|
11963
|
+
return 1;
|
|
11964
|
+
}
|
|
11965
|
+
const condition = typeof args.flags.condition === "string" ? args.flags.condition : undefined;
|
|
11966
|
+
const client = new DaemonClient(session2);
|
|
11967
|
+
const response = await client.request("break-fn", { name, condition });
|
|
11968
|
+
if (!response.ok) {
|
|
11969
|
+
console.error(`${response.error}`);
|
|
11970
|
+
if (response.suggestion)
|
|
11971
|
+
console.error(` ${response.suggestion}`);
|
|
11972
|
+
return 1;
|
|
11973
|
+
}
|
|
11974
|
+
const data = response.data;
|
|
11975
|
+
if (args.global.json) {
|
|
11976
|
+
console.log(JSON.stringify(data, null, 2));
|
|
11977
|
+
} else {
|
|
11978
|
+
console.log(`${data.ref} fn:${name}`);
|
|
11979
|
+
}
|
|
11980
|
+
return 0;
|
|
11981
|
+
});
|
|
11982
|
+
});
|
|
11983
|
+
|
|
10814
11984
|
// src/commands/break-rm.ts
|
|
10815
11985
|
var exports_break_rm = {};
|
|
10816
11986
|
var init_break_rm = __esm(() => {
|
|
@@ -12006,7 +13176,13 @@ function formatLogEntry(entry) {
|
|
|
12006
13176
|
const summary = summarizer ? summarizer(entry) : summarizeParams(entry);
|
|
12007
13177
|
return `${time3} <- ${entry.method}${summary ? ` ${summary}` : ""}`;
|
|
12008
13178
|
}
|
|
12009
|
-
|
|
13179
|
+
function formatDaemonLogEntry(entry) {
|
|
13180
|
+
const time3 = `[${formatTime(entry.ts)}]`;
|
|
13181
|
+
const level = levelColors[entry.level] ?? entry.level.toUpperCase();
|
|
13182
|
+
const data = entry.data ? ` ${truncate2(JSON.stringify(entry.data), 120)}` : "";
|
|
13183
|
+
return `${time3} ${level} ${entry.event}: ${entry.message}${data}`;
|
|
13184
|
+
}
|
|
13185
|
+
var eventSummarizers, responseSummarizers, levelColors;
|
|
12010
13186
|
var init_logs = __esm(() => {
|
|
12011
13187
|
eventSummarizers = {
|
|
12012
13188
|
"Debugger.scriptParsed": (e) => {
|
|
@@ -12063,6 +13239,12 @@ var init_logs = __esm(() => {
|
|
|
12063
13239
|
return `breakpointId=${r.breakpointId}`;
|
|
12064
13240
|
}
|
|
12065
13241
|
};
|
|
13242
|
+
levelColors = {
|
|
13243
|
+
info: "INFO ",
|
|
13244
|
+
warn: "WARN ",
|
|
13245
|
+
error: "ERROR",
|
|
13246
|
+
debug: "DEBUG"
|
|
13247
|
+
};
|
|
12066
13248
|
});
|
|
12067
13249
|
|
|
12068
13250
|
// src/commands/logs.ts
|
|
@@ -12070,13 +13252,25 @@ var exports_logs = {};
|
|
|
12070
13252
|
import {
|
|
12071
13253
|
closeSync,
|
|
12072
13254
|
existsSync as existsSync5,
|
|
12073
|
-
openSync,
|
|
12074
|
-
readFileSync,
|
|
13255
|
+
openSync as openSync2,
|
|
13256
|
+
readFileSync as readFileSync3,
|
|
12075
13257
|
readSync,
|
|
12076
13258
|
watch,
|
|
12077
|
-
writeFileSync as
|
|
13259
|
+
writeFileSync as writeFileSync4
|
|
12078
13260
|
} from "fs";
|
|
12079
|
-
function
|
|
13261
|
+
function parseCdpEntries(text) {
|
|
13262
|
+
const entries = [];
|
|
13263
|
+
for (const line of text.split(`
|
|
13264
|
+
`)) {
|
|
13265
|
+
if (!line.trim())
|
|
13266
|
+
continue;
|
|
13267
|
+
try {
|
|
13268
|
+
entries.push(JSON.parse(line));
|
|
13269
|
+
} catch {}
|
|
13270
|
+
}
|
|
13271
|
+
return entries;
|
|
13272
|
+
}
|
|
13273
|
+
function parseDaemonEntries(text) {
|
|
12080
13274
|
const entries = [];
|
|
12081
13275
|
for (const line of text.split(`
|
|
12082
13276
|
`)) {
|
|
@@ -12091,7 +13285,10 @@ function parseEntries(text) {
|
|
|
12091
13285
|
function filterByDomain(entries, domain) {
|
|
12092
13286
|
return entries.filter((e) => e.method.startsWith(`${domain}.`));
|
|
12093
13287
|
}
|
|
12094
|
-
function
|
|
13288
|
+
function filterByLevel(entries, level) {
|
|
13289
|
+
return entries.filter((e) => e.level === level);
|
|
13290
|
+
}
|
|
13291
|
+
function printCdpEntries(entries, json2) {
|
|
12095
13292
|
for (const entry of entries) {
|
|
12096
13293
|
if (json2) {
|
|
12097
13294
|
console.log(JSON.stringify(entry));
|
|
@@ -12100,37 +13297,56 @@ function printEntries(entries, json2) {
|
|
|
12100
13297
|
}
|
|
12101
13298
|
}
|
|
12102
13299
|
}
|
|
13300
|
+
function printDaemonEntries(entries, json2) {
|
|
13301
|
+
for (const entry of entries) {
|
|
13302
|
+
if (json2) {
|
|
13303
|
+
console.log(JSON.stringify(entry));
|
|
13304
|
+
} else {
|
|
13305
|
+
console.log(formatDaemonLogEntry(entry));
|
|
13306
|
+
}
|
|
13307
|
+
}
|
|
13308
|
+
}
|
|
12103
13309
|
var init_logs2 = __esm(() => {
|
|
12104
13310
|
init_registry();
|
|
12105
13311
|
init_paths();
|
|
12106
13312
|
init_logs();
|
|
12107
13313
|
registerCommand("logs", async (args) => {
|
|
12108
13314
|
const session2 = args.global.session;
|
|
12109
|
-
const
|
|
13315
|
+
const isDaemon = args.flags.daemon === true;
|
|
13316
|
+
const logPath = isDaemon ? getDaemonLogPath(session2) : getLogPath(session2);
|
|
12110
13317
|
if (args.flags.clear === true) {
|
|
12111
13318
|
if (existsSync5(logPath)) {
|
|
12112
|
-
|
|
12113
|
-
console.log("Log cleared
|
|
13319
|
+
writeFileSync4(logPath, "");
|
|
13320
|
+
console.log(`${isDaemon ? "Daemon log" : "Log"} cleared`);
|
|
12114
13321
|
} else {
|
|
12115
|
-
console.log(
|
|
13322
|
+
console.log(`No ${isDaemon ? "daemon " : ""}log file to clear`);
|
|
12116
13323
|
}
|
|
12117
13324
|
return 0;
|
|
12118
13325
|
}
|
|
12119
13326
|
if (!existsSync5(logPath)) {
|
|
12120
|
-
console.error(`No log file for session "${session2}"`);
|
|
13327
|
+
console.error(`No ${isDaemon ? "daemon " : ""}log file for session "${session2}"`);
|
|
12121
13328
|
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
12122
13329
|
return 1;
|
|
12123
13330
|
}
|
|
12124
13331
|
const isJson = args.global.json;
|
|
12125
13332
|
const domain = typeof args.flags.domain === "string" ? args.flags.domain : undefined;
|
|
13333
|
+
const level = typeof args.flags.level === "string" ? args.flags.level : undefined;
|
|
12126
13334
|
const limit = typeof args.flags.limit === "string" ? parseInt(args.flags.limit, 10) : 50;
|
|
12127
13335
|
const follow = args.flags.follow === true;
|
|
12128
|
-
const content =
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
13336
|
+
const content = readFileSync3(logPath, "utf-8");
|
|
13337
|
+
if (isDaemon) {
|
|
13338
|
+
let entries = parseDaemonEntries(content);
|
|
13339
|
+
if (level)
|
|
13340
|
+
entries = filterByLevel(entries, level);
|
|
13341
|
+
const sliced = follow ? entries : entries.slice(-limit);
|
|
13342
|
+
printDaemonEntries(sliced, isJson);
|
|
13343
|
+
} else {
|
|
13344
|
+
let entries = parseCdpEntries(content);
|
|
13345
|
+
if (domain)
|
|
13346
|
+
entries = filterByDomain(entries, domain);
|
|
13347
|
+
const sliced = follow ? entries : entries.slice(-limit);
|
|
13348
|
+
printCdpEntries(sliced, isJson);
|
|
13349
|
+
}
|
|
12134
13350
|
if (!follow)
|
|
12135
13351
|
return 0;
|
|
12136
13352
|
let offset = Buffer.byteLength(content, "utf-8");
|
|
@@ -12140,15 +13356,22 @@ var init_logs2 = __esm(() => {
|
|
|
12140
13356
|
const size = Bun.file(logPath).size;
|
|
12141
13357
|
if (size <= offset)
|
|
12142
13358
|
return;
|
|
12143
|
-
const fd =
|
|
13359
|
+
const fd = openSync2(logPath, "r");
|
|
12144
13360
|
const buf = Buffer.alloc(size - offset);
|
|
12145
13361
|
readSync(fd, buf, 0, buf.length, offset);
|
|
12146
13362
|
closeSync(fd);
|
|
12147
13363
|
offset = size;
|
|
12148
|
-
|
|
12149
|
-
|
|
12150
|
-
|
|
12151
|
-
|
|
13364
|
+
if (isDaemon) {
|
|
13365
|
+
let newEntries = parseDaemonEntries(buf.toString("utf-8"));
|
|
13366
|
+
if (level)
|
|
13367
|
+
newEntries = filterByLevel(newEntries, level);
|
|
13368
|
+
printDaemonEntries(newEntries, isJson);
|
|
13369
|
+
} else {
|
|
13370
|
+
let newEntries = parseCdpEntries(buf.toString("utf-8"));
|
|
13371
|
+
if (domain)
|
|
13372
|
+
newEntries = filterByDomain(newEntries, domain);
|
|
13373
|
+
printCdpEntries(newEntries, isJson);
|
|
13374
|
+
}
|
|
12152
13375
|
} catch {}
|
|
12153
13376
|
};
|
|
12154
13377
|
watcher = watch(logPath, () => {
|
|
@@ -12469,6 +13692,7 @@ if (process.argv.includes("--daemon")) {
|
|
|
12469
13692
|
await Promise.resolve().then(() => (init_pause(), exports_pause));
|
|
12470
13693
|
await Promise.resolve().then(() => (init_run_to(), exports_run_to));
|
|
12471
13694
|
await Promise.resolve().then(() => (init_break(), exports_break));
|
|
13695
|
+
await Promise.resolve().then(() => (init_break_fn(), exports_break_fn));
|
|
12472
13696
|
await Promise.resolve().then(() => (init_break_rm(), exports_break_rm));
|
|
12473
13697
|
await Promise.resolve().then(() => (init_break_ls(), exports_break_ls));
|
|
12474
13698
|
await Promise.resolve().then(() => (init_logpoint(), exports_logpoint));
|