agent-dbg 0.1.2 → 0.1.3
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 +3 -1
- package/.claude/skills/agent-dbg/SKILL.md +2 -0
- package/.claude/skills/agent-dbg/references/commands.md +2 -1
- package/bun.lock +60 -0
- package/dist/main.js +236 -75
- package/package.json +1 -1
- package/src/commands/attach.ts +4 -4
- package/src/commands/launch.ts +3 -5
- package/src/commands/logs.ts +58 -16
- package/src/daemon/client.ts +27 -5
- package/src/daemon/entry.ts +12 -2
- package/src/daemon/logger.ts +51 -0
- package/src/daemon/paths.ts +4 -0
- package/src/daemon/server.ts +9 -1
- package/src/daemon/session.ts +48 -10
- package/src/daemon/spawn.ts +27 -4
- package/src/formatter/logs.ts +15 -0
- package/tests/integration/daemon-logging.test.ts +156 -0
- package/tests/unit/daemon-logger.test.ts +117 -0
- package/tests/unit/daemon.test.ts +60 -0
|
@@ -20,6 +20,8 @@ description: >
|
|
|
20
20
|
```bash
|
|
21
21
|
# 1. Launch with breakpoint at first line
|
|
22
22
|
agent-dbg launch --brk node app.js
|
|
23
|
+
# Or attach to a running node process with the --inspect flag
|
|
24
|
+
agent-dbg attach 9229
|
|
23
25
|
|
|
24
26
|
# 2. Set breakpoints at suspicious locations
|
|
25
27
|
agent-dbg break src/handler.ts:42
|
|
@@ -111,7 +111,8 @@ agent-dbg exceptions --since 3 # Last 3 exceptions
|
|
|
111
111
|
## Breakpoints
|
|
112
112
|
|
|
113
113
|
```bash
|
|
114
|
-
agent-dbg break <file>:<line>
|
|
114
|
+
agent-dbg break <file>:<line> # Set breakpoint
|
|
115
|
+
agent-dbg break <file>:<line>:<column> # Set breakpoint
|
|
115
116
|
agent-dbg break src/app.ts:42 --condition "x > 10" # Conditional
|
|
116
117
|
agent-dbg break src/app.ts:42 --hit-count 5 # Break on Nth hit
|
|
117
118
|
agent-dbg break src/app.ts:42 --continue # Log but don't pause
|
package/bun.lock
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "ndbg",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@jridgewell/trace-mapping": "^0.3.31",
|
|
9
|
+
"zod": "^4.0.0",
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@biomejs/biome": "^2.3.14",
|
|
13
|
+
"@types/bun": "latest",
|
|
14
|
+
"devtools-protocol": "^0.0.1581282",
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"typescript": "^5",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
"packages": {
|
|
22
|
+
"@biomejs/biome": ["@biomejs/biome@2.3.14", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.14", "@biomejs/cli-darwin-x64": "2.3.14", "@biomejs/cli-linux-arm64": "2.3.14", "@biomejs/cli-linux-arm64-musl": "2.3.14", "@biomejs/cli-linux-x64": "2.3.14", "@biomejs/cli-linux-x64-musl": "2.3.14", "@biomejs/cli-win32-arm64": "2.3.14", "@biomejs/cli-win32-x64": "2.3.14" }, "bin": { "biome": "bin/biome" } }, "sha512-QMT6QviX0WqXJCaiqVMiBUCr5WRQ1iFSjvOLoTk6auKukJMvnMzWucXpwZB0e8F00/1/BsS9DzcKgWH+CLqVuA=="],
|
|
23
|
+
|
|
24
|
+
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UJGPpvWJMkLxSRtpCAKfKh41Q4JJXisvxZL8ChN1eNW3m/WlPFJ6EFDCE7YfUb4XS8ZFi3C1dFpxUJ0Ety5n+A=="],
|
|
25
|
+
|
|
26
|
+
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-PNkLNQG6RLo8lG7QoWe/hhnMxJIt1tEimoXpGQjwS/dkdNiKBLPv4RpeQl8o3s1OKI3ZOR5XPiYtmbGGHAOnLA=="],
|
|
27
|
+
|
|
28
|
+
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-KT67FKfzIw6DNnUNdYlBg+eU24Go3n75GWK6NwU4+yJmDYFe9i/MjiI+U/iEzKvo0g7G7MZqoyrhIYuND2w8QQ=="],
|
|
29
|
+
|
|
30
|
+
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-LInRbXhYujtL3sH2TMCH/UBwJZsoGwfQjBrMfl84CD4hL/41C/EU5mldqf1yoFpsI0iPWuU83U+nB2TUUypWeg=="],
|
|
31
|
+
|
|
32
|
+
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.14", "", { "os": "linux", "cpu": "x64" }, "sha512-ZsZzQsl9U+wxFrGGS4f6UxREUlgHwmEfu1IrXlgNFrNnd5Th6lIJr8KmSzu/+meSa9f4rzFrbEW9LBBA6ScoMA=="],
|
|
33
|
+
|
|
34
|
+
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.14", "", { "os": "linux", "cpu": "x64" }, "sha512-KQU7EkbBBuHPW3/rAcoiVmhlPtDSGOGRPv9js7qJVpYTzjQmVR+C9Rfcz+ti8YCH+zT1J52tuBybtP4IodjxZQ=="],
|
|
35
|
+
|
|
36
|
+
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-+IKYkj/pUBbnRf1G1+RlyA3LWiDgra1xpS7H2g4BuOzzRbRB+hmlw0yFsLprHhbbt7jUzbzAbAjK/Pn0FDnh1A=="],
|
|
37
|
+
|
|
38
|
+
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.14", "", { "os": "win32", "cpu": "x64" }, "sha512-oizCjdyQ3WJEswpb3Chdngeat56rIdSYK12JI3iI11Mt5T5EXcZ7WLuowzEaFPNJ3zmOQFliMN8QY1Pi+qsfdQ=="],
|
|
39
|
+
|
|
40
|
+
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
|
41
|
+
|
|
42
|
+
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
|
43
|
+
|
|
44
|
+
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
|
45
|
+
|
|
46
|
+
"@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="],
|
|
47
|
+
|
|
48
|
+
"@types/node": ["@types/node@25.2.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ=="],
|
|
49
|
+
|
|
50
|
+
"bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="],
|
|
51
|
+
|
|
52
|
+
"devtools-protocol": ["devtools-protocol@0.0.1581282", "", {}, "sha512-nv7iKtNZQshSW2hKzYNr46nM/Cfh5SEvE2oV0/SEGgc9XupIY5ggf84Cz8eJIkBce7S3bmTAauFD6aysMpnqsQ=="],
|
|
53
|
+
|
|
54
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
55
|
+
|
|
56
|
+
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
|
57
|
+
|
|
58
|
+
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
|
59
|
+
}
|
|
60
|
+
}
|
package/dist/main.js
CHANGED
|
@@ -12,6 +12,72 @@ var __export = (target, all) => {
|
|
|
12
12
|
};
|
|
13
13
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
14
14
|
|
|
15
|
+
// src/daemon/logger.ts
|
|
16
|
+
import { appendFileSync, writeFileSync } from "fs";
|
|
17
|
+
|
|
18
|
+
class DaemonLogger {
|
|
19
|
+
logPath;
|
|
20
|
+
constructor(logPath) {
|
|
21
|
+
this.logPath = logPath;
|
|
22
|
+
writeFileSync(logPath, "");
|
|
23
|
+
}
|
|
24
|
+
info(event, message, data) {
|
|
25
|
+
this.write("info", event, message, data);
|
|
26
|
+
}
|
|
27
|
+
warn(event, message, data) {
|
|
28
|
+
this.write("warn", event, message, data);
|
|
29
|
+
}
|
|
30
|
+
error(event, message, data) {
|
|
31
|
+
this.write("error", event, message, data);
|
|
32
|
+
}
|
|
33
|
+
debug(event, message, data) {
|
|
34
|
+
this.write("debug", event, message, data);
|
|
35
|
+
}
|
|
36
|
+
clear() {
|
|
37
|
+
writeFileSync(this.logPath, "");
|
|
38
|
+
}
|
|
39
|
+
write(level, event, message, data) {
|
|
40
|
+
const entry = { ts: Date.now(), level, event, message };
|
|
41
|
+
if (data !== undefined) {
|
|
42
|
+
entry.data = data;
|
|
43
|
+
}
|
|
44
|
+
appendFileSync(this.logPath, `${JSON.stringify(entry)}
|
|
45
|
+
`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
var init_logger = () => {};
|
|
49
|
+
|
|
50
|
+
// src/daemon/paths.ts
|
|
51
|
+
import { existsSync, mkdirSync } from "fs";
|
|
52
|
+
import { join } from "path";
|
|
53
|
+
function getSocketDir() {
|
|
54
|
+
const xdgRuntime = process.env.XDG_RUNTIME_DIR;
|
|
55
|
+
if (xdgRuntime) {
|
|
56
|
+
return join(xdgRuntime, "agent-dbg");
|
|
57
|
+
}
|
|
58
|
+
const tmpdir = process.env.TMPDIR || "/tmp";
|
|
59
|
+
return join(tmpdir, `agent-dbg-${process.getuid?.() ?? 0}`);
|
|
60
|
+
}
|
|
61
|
+
function getSocketPath(session) {
|
|
62
|
+
return join(getSocketDir(), `${session}.sock`);
|
|
63
|
+
}
|
|
64
|
+
function getLockPath(session) {
|
|
65
|
+
return join(getSocketDir(), `${session}.lock`);
|
|
66
|
+
}
|
|
67
|
+
function getLogPath(session) {
|
|
68
|
+
return join(getSocketDir(), `${session}.cdp.log`);
|
|
69
|
+
}
|
|
70
|
+
function getDaemonLogPath(session) {
|
|
71
|
+
return join(getSocketDir(), `${session}.daemon.log`);
|
|
72
|
+
}
|
|
73
|
+
function ensureSocketDir() {
|
|
74
|
+
const dir = getSocketDir();
|
|
75
|
+
if (!existsSync(dir)) {
|
|
76
|
+
mkdirSync(dir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
var init_paths = () => {};
|
|
80
|
+
|
|
15
81
|
// node_modules/@zod/core/dist/esm/core.js
|
|
16
82
|
function $constructor(name, initializer) {
|
|
17
83
|
class _ {
|
|
@@ -6308,36 +6374,8 @@ var init_messages = __esm(() => {
|
|
|
6308
6374
|
DaemonResponseSchema = exports_external.union([SuccessResponse, ErrorResponse]);
|
|
6309
6375
|
});
|
|
6310
6376
|
|
|
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
6377
|
// src/daemon/server.ts
|
|
6340
|
-
import { existsSync as existsSync2, unlinkSync, writeFileSync } from "fs";
|
|
6378
|
+
import { existsSync as existsSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
6341
6379
|
|
|
6342
6380
|
class DaemonServer {
|
|
6343
6381
|
session;
|
|
@@ -6347,11 +6385,13 @@ class DaemonServer {
|
|
|
6347
6385
|
listener = null;
|
|
6348
6386
|
socketPath;
|
|
6349
6387
|
lockPath;
|
|
6388
|
+
logger;
|
|
6350
6389
|
constructor(session, options) {
|
|
6351
6390
|
this.session = session;
|
|
6352
6391
|
this.idleTimeout = options.idleTimeout;
|
|
6353
6392
|
this.socketPath = getSocketPath(session);
|
|
6354
6393
|
this.lockPath = getLockPath(session);
|
|
6394
|
+
this.logger = options.logger ?? null;
|
|
6355
6395
|
}
|
|
6356
6396
|
onRequest(handler) {
|
|
6357
6397
|
this.handler = handler;
|
|
@@ -6368,7 +6408,7 @@ class DaemonServer {
|
|
|
6368
6408
|
if (existsSync2(this.socketPath)) {
|
|
6369
6409
|
unlinkSync(this.socketPath);
|
|
6370
6410
|
}
|
|
6371
|
-
|
|
6411
|
+
writeFileSync2(this.lockPath, String(process.pid));
|
|
6372
6412
|
const server = this;
|
|
6373
6413
|
this.listener = Bun.listen({
|
|
6374
6414
|
unix: this.socketPath,
|
|
@@ -6389,6 +6429,7 @@ class DaemonServer {
|
|
|
6389
6429
|
},
|
|
6390
6430
|
close() {},
|
|
6391
6431
|
error(_socket, error3) {
|
|
6432
|
+
server.logger?.error("socket.error", error3.message);
|
|
6392
6433
|
console.error(`[daemon] socket error: ${error3.message}`);
|
|
6393
6434
|
}
|
|
6394
6435
|
}
|
|
@@ -6457,6 +6498,7 @@ class DaemonServer {
|
|
|
6457
6498
|
}
|
|
6458
6499
|
if (this.idleTimeout > 0) {
|
|
6459
6500
|
this.idleTimer = setTimeout(() => {
|
|
6501
|
+
this.logger?.info("daemon.idle", `Idle timeout reached (${this.idleTimeout}s), shutting down`);
|
|
6460
6502
|
this.stop();
|
|
6461
6503
|
}, this.idleTimeout * 1000);
|
|
6462
6504
|
}
|
|
@@ -6647,14 +6689,14 @@ class CdpClient {
|
|
|
6647
6689
|
var DEFAULT_TIMEOUT_MS = 30000;
|
|
6648
6690
|
|
|
6649
6691
|
// src/cdp/logger.ts
|
|
6650
|
-
import { appendFileSync, writeFileSync as
|
|
6692
|
+
import { appendFileSync as appendFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
6651
6693
|
|
|
6652
6694
|
class CdpLogger {
|
|
6653
6695
|
logPath;
|
|
6654
6696
|
pending = new Map;
|
|
6655
6697
|
constructor(logPath) {
|
|
6656
6698
|
this.logPath = logPath;
|
|
6657
|
-
|
|
6699
|
+
writeFileSync3(logPath, "");
|
|
6658
6700
|
}
|
|
6659
6701
|
logSend(id, method, params) {
|
|
6660
6702
|
this.pending.set(id, { method, sentAt: Date.now() });
|
|
@@ -6687,14 +6729,14 @@ class CdpLogger {
|
|
|
6687
6729
|
this.append(entry);
|
|
6688
6730
|
}
|
|
6689
6731
|
clear() {
|
|
6690
|
-
|
|
6732
|
+
writeFileSync3(this.logPath, "");
|
|
6691
6733
|
}
|
|
6692
6734
|
append(entry) {
|
|
6693
|
-
|
|
6735
|
+
appendFileSync2(this.logPath, `${JSON.stringify(entry)}
|
|
6694
6736
|
`);
|
|
6695
6737
|
}
|
|
6696
6738
|
}
|
|
6697
|
-
var
|
|
6739
|
+
var init_logger2 = () => {};
|
|
6698
6740
|
|
|
6699
6741
|
// src/formatter/values.ts
|
|
6700
6742
|
function truncate(str, max) {
|
|
@@ -9040,10 +9082,12 @@ class DebugSession {
|
|
|
9040
9082
|
launchCommand = null;
|
|
9041
9083
|
launchOptions = null;
|
|
9042
9084
|
cdpLogger;
|
|
9043
|
-
|
|
9085
|
+
daemonLogger;
|
|
9086
|
+
constructor(session, options) {
|
|
9044
9087
|
this.session = session;
|
|
9045
9088
|
ensureSocketDir();
|
|
9046
9089
|
this.cdpLogger = new CdpLogger(getLogPath(session));
|
|
9090
|
+
this.daemonLogger = options?.daemonLogger ?? new DaemonLogger(getDaemonLogPath(session));
|
|
9047
9091
|
}
|
|
9048
9092
|
async launch(command, options = {}) {
|
|
9049
9093
|
if (this.state !== "idle") {
|
|
@@ -9062,13 +9106,20 @@ class DebugSession {
|
|
|
9062
9106
|
const spawnArgs = [runtime, inspectFlag, ...rest];
|
|
9063
9107
|
const proc = Bun.spawn(spawnArgs, {
|
|
9064
9108
|
stdin: "ignore",
|
|
9065
|
-
stdout: "
|
|
9109
|
+
stdout: "ignore",
|
|
9066
9110
|
stderr: "pipe"
|
|
9067
9111
|
});
|
|
9068
9112
|
this.childProcess = proc;
|
|
9113
|
+
this.daemonLogger.info("child.spawn", `Spawned process pid=${proc.pid}`, {
|
|
9114
|
+
pid: proc.pid,
|
|
9115
|
+
command: spawnArgs
|
|
9116
|
+
});
|
|
9069
9117
|
this.monitorProcessExit(proc);
|
|
9070
9118
|
const wsUrl = await this.readInspectorUrl(proc.stderr);
|
|
9071
9119
|
this.wsUrl = wsUrl;
|
|
9120
|
+
this.daemonLogger.info("inspector.detected", `Inspector URL: ${wsUrl}`, {
|
|
9121
|
+
wsUrl
|
|
9122
|
+
});
|
|
9072
9123
|
await this.connectCdp(wsUrl);
|
|
9073
9124
|
if (brk) {
|
|
9074
9125
|
await this.waitForBrkPause();
|
|
@@ -9382,8 +9433,10 @@ class DebugSession {
|
|
|
9382
9433
|
}
|
|
9383
9434
|
}
|
|
9384
9435
|
async connectCdp(wsUrl) {
|
|
9436
|
+
this.daemonLogger.debug("cdp.connecting", `Connecting to ${wsUrl}`);
|
|
9385
9437
|
const cdp = await CdpClient.connect(wsUrl, this.cdpLogger);
|
|
9386
9438
|
this.cdp = cdp;
|
|
9439
|
+
this.daemonLogger.info("cdp.connected", `CDP connected to ${wsUrl}`);
|
|
9387
9440
|
this.setupCdpEventHandlers(cdp);
|
|
9388
9441
|
await cdp.enableDomains();
|
|
9389
9442
|
if (this.blackboxPatterns.length > 0) {
|
|
@@ -9490,7 +9543,11 @@ class DebugSession {
|
|
|
9490
9543
|
});
|
|
9491
9544
|
}
|
|
9492
9545
|
monitorProcessExit(proc) {
|
|
9493
|
-
proc.exited.then(() => {
|
|
9546
|
+
proc.exited.then((exitCode) => {
|
|
9547
|
+
this.daemonLogger.info("child.exit", `Process exited with code ${exitCode ?? "unknown"}`, {
|
|
9548
|
+
pid: proc.pid,
|
|
9549
|
+
exitCode: exitCode ?? null
|
|
9550
|
+
});
|
|
9494
9551
|
this.childProcess = null;
|
|
9495
9552
|
if (this.cdp) {
|
|
9496
9553
|
this.cdp.disconnect();
|
|
@@ -9499,7 +9556,10 @@ class DebugSession {
|
|
|
9499
9556
|
this.state = "idle";
|
|
9500
9557
|
this.pauseInfo = null;
|
|
9501
9558
|
this.onProcessExit?.();
|
|
9502
|
-
}).catch(() => {
|
|
9559
|
+
}).catch((err) => {
|
|
9560
|
+
this.daemonLogger.error("child.exit.error", `Error waiting for process exit: ${err}`, {
|
|
9561
|
+
pid: proc.pid
|
|
9562
|
+
});
|
|
9503
9563
|
this.childProcess = null;
|
|
9504
9564
|
this.state = "idle";
|
|
9505
9565
|
this.pauseInfo = null;
|
|
@@ -9518,16 +9578,22 @@ class DebugSession {
|
|
|
9518
9578
|
if (done) {
|
|
9519
9579
|
break;
|
|
9520
9580
|
}
|
|
9521
|
-
|
|
9581
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
9582
|
+
accumulated += chunk;
|
|
9583
|
+
this.daemonLogger.debug("child.stderr", chunk.trimEnd());
|
|
9522
9584
|
const match = INSPECTOR_URL_REGEX.exec(accumulated);
|
|
9523
9585
|
if (match?.[1]) {
|
|
9524
9586
|
clearTimeout(timeout);
|
|
9525
|
-
|
|
9587
|
+
this.drainReader(reader);
|
|
9526
9588
|
return match[1];
|
|
9527
9589
|
}
|
|
9528
9590
|
}
|
|
9529
9591
|
} catch {}
|
|
9530
9592
|
clearTimeout(timeout);
|
|
9593
|
+
this.daemonLogger.error("inspector.failed", "Failed to detect inspector URL", {
|
|
9594
|
+
stderr: accumulated.slice(0, 2000),
|
|
9595
|
+
timeoutMs: INSPECTOR_TIMEOUT_MS
|
|
9596
|
+
});
|
|
9531
9597
|
throw new Error(`Failed to detect inspector URL within ${INSPECTOR_TIMEOUT_MS}ms. Stderr: ${accumulated.slice(0, 500)}`);
|
|
9532
9598
|
}
|
|
9533
9599
|
async discoverWsUrl(port) {
|
|
@@ -9552,12 +9618,22 @@ class DebugSession {
|
|
|
9552
9618
|
}
|
|
9553
9619
|
return wsUrl;
|
|
9554
9620
|
}
|
|
9621
|
+
drainReader(reader) {
|
|
9622
|
+
const pump = () => {
|
|
9623
|
+
reader.read().then(({ done }) => {
|
|
9624
|
+
if (!done)
|
|
9625
|
+
pump();
|
|
9626
|
+
}).catch(() => {});
|
|
9627
|
+
};
|
|
9628
|
+
pump();
|
|
9629
|
+
}
|
|
9555
9630
|
}
|
|
9556
9631
|
var INSPECTOR_URL_REGEX, INSPECTOR_TIMEOUT_MS = 5000;
|
|
9557
9632
|
var init_session = __esm(() => {
|
|
9558
|
-
|
|
9633
|
+
init_logger2();
|
|
9559
9634
|
init_ref_table();
|
|
9560
9635
|
init_resolver();
|
|
9636
|
+
init_logger();
|
|
9561
9637
|
init_paths();
|
|
9562
9638
|
init_session_inspection();
|
|
9563
9639
|
init_session_mutation();
|
|
@@ -9567,8 +9643,10 @@ var init_session = __esm(() => {
|
|
|
9567
9643
|
|
|
9568
9644
|
// src/daemon/entry.ts
|
|
9569
9645
|
var exports_entry = {};
|
|
9570
|
-
var daemonIdx, session, timeout = 300, timeoutIdx, server, debugSession;
|
|
9646
|
+
var daemonIdx, session, timeout = 300, timeoutIdx, daemonLogger, server, debugSession;
|
|
9571
9647
|
var init_entry = __esm(async () => {
|
|
9648
|
+
init_logger();
|
|
9649
|
+
init_paths();
|
|
9572
9650
|
init_server();
|
|
9573
9651
|
init_session();
|
|
9574
9652
|
daemonIdx = process.argv.indexOf("--daemon");
|
|
@@ -9587,8 +9665,15 @@ var init_entry = __esm(async () => {
|
|
|
9587
9665
|
}
|
|
9588
9666
|
}
|
|
9589
9667
|
}
|
|
9590
|
-
|
|
9591
|
-
|
|
9668
|
+
ensureSocketDir();
|
|
9669
|
+
daemonLogger = new DaemonLogger(getDaemonLogPath(session));
|
|
9670
|
+
daemonLogger.info("daemon.start", `Daemon starting for session "${session}"`, {
|
|
9671
|
+
pid: process.pid,
|
|
9672
|
+
session,
|
|
9673
|
+
timeout
|
|
9674
|
+
});
|
|
9675
|
+
server = new DaemonServer(session, { idleTimeout: timeout, logger: daemonLogger });
|
|
9676
|
+
debugSession = new DebugSession(session, { daemonLogger });
|
|
9592
9677
|
server.onRequest(async (req) => {
|
|
9593
9678
|
switch (req.cmd) {
|
|
9594
9679
|
case "ping":
|
|
@@ -9790,7 +9875,7 @@ var init_registry = __esm(() => {
|
|
|
9790
9875
|
});
|
|
9791
9876
|
|
|
9792
9877
|
// src/daemon/client.ts
|
|
9793
|
-
import { existsSync as existsSync3, readdirSync } from "fs";
|
|
9878
|
+
import { existsSync as existsSync3, readFileSync, readdirSync, unlinkSync as unlinkSync2 } from "fs";
|
|
9794
9879
|
|
|
9795
9880
|
class DaemonClient {
|
|
9796
9881
|
session;
|
|
@@ -9890,12 +9975,28 @@ class DaemonClient {
|
|
|
9890
9975
|
if (!existsSync3(socketPath)) {
|
|
9891
9976
|
return false;
|
|
9892
9977
|
}
|
|
9978
|
+
const lockPath = getLockPath(session2);
|
|
9979
|
+
if (!existsSync3(lockPath)) {
|
|
9980
|
+
return false;
|
|
9981
|
+
}
|
|
9893
9982
|
try {
|
|
9983
|
+
const pid = parseInt(readFileSync(lockPath, "utf-8"), 10);
|
|
9984
|
+
if (Number.isNaN(pid))
|
|
9985
|
+
return false;
|
|
9986
|
+
process.kill(pid, 0);
|
|
9894
9987
|
return true;
|
|
9895
9988
|
} catch {
|
|
9896
9989
|
return false;
|
|
9897
9990
|
}
|
|
9898
9991
|
}
|
|
9992
|
+
static cleanStaleFiles(session2) {
|
|
9993
|
+
const socketPath = getSocketPath(session2);
|
|
9994
|
+
const lockPath = getLockPath(session2);
|
|
9995
|
+
if (existsSync3(socketPath))
|
|
9996
|
+
unlinkSync2(socketPath);
|
|
9997
|
+
if (existsSync3(lockPath))
|
|
9998
|
+
unlinkSync2(lockPath);
|
|
9999
|
+
}
|
|
9899
10000
|
static async isAlive(session2) {
|
|
9900
10001
|
const socketPath = getSocketPath(session2);
|
|
9901
10002
|
if (!existsSync3(socketPath)) {
|
|
@@ -9925,7 +10026,7 @@ var init_client = __esm(() => {
|
|
|
9925
10026
|
});
|
|
9926
10027
|
|
|
9927
10028
|
// src/daemon/spawn.ts
|
|
9928
|
-
import { existsSync as existsSync4 } from "fs";
|
|
10029
|
+
import { existsSync as existsSync4, openSync } from "fs";
|
|
9929
10030
|
async function spawnDaemon(session2, options = {}) {
|
|
9930
10031
|
const socketPath = getSocketPath(session2);
|
|
9931
10032
|
const spawnArgs = [];
|
|
@@ -9940,11 +10041,13 @@ async function spawnDaemon(session2, options = {}) {
|
|
|
9940
10041
|
if (options.timeout !== undefined) {
|
|
9941
10042
|
spawnArgs.push("--timeout", String(options.timeout));
|
|
9942
10043
|
}
|
|
10044
|
+
ensureSocketDir();
|
|
10045
|
+
const logFd = openSync(getDaemonLogPath(session2), "a");
|
|
9943
10046
|
const proc = Bun.spawn(spawnArgs, {
|
|
9944
10047
|
detached: true,
|
|
9945
10048
|
stdin: "ignore",
|
|
9946
|
-
stdout:
|
|
9947
|
-
stderr:
|
|
10049
|
+
stdout: logFd,
|
|
10050
|
+
stderr: logFd
|
|
9948
10051
|
});
|
|
9949
10052
|
proc.unref();
|
|
9950
10053
|
const deadline = Date.now() + SPAWN_TIMEOUT_MS;
|
|
@@ -9956,8 +10059,15 @@ async function spawnDaemon(session2, options = {}) {
|
|
|
9956
10059
|
}
|
|
9957
10060
|
throw new Error(`Daemon for session "${session2}" failed to start within ${SPAWN_TIMEOUT_MS}ms`);
|
|
9958
10061
|
}
|
|
10062
|
+
async function ensureDaemon(session2, options) {
|
|
10063
|
+
if (DaemonClient.isRunning(session2))
|
|
10064
|
+
return;
|
|
10065
|
+
DaemonClient.cleanStaleFiles(session2);
|
|
10066
|
+
await spawnDaemon(session2, options);
|
|
10067
|
+
}
|
|
9959
10068
|
var POLL_INTERVAL_MS = 50, SPAWN_TIMEOUT_MS = 5000;
|
|
9960
10069
|
var init_spawn = __esm(() => {
|
|
10070
|
+
init_client();
|
|
9961
10071
|
init_paths();
|
|
9962
10072
|
});
|
|
9963
10073
|
|
|
@@ -10001,9 +10111,7 @@ var init_launch = __esm(() => {
|
|
|
10001
10111
|
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
10002
10112
|
return 1;
|
|
10003
10113
|
}
|
|
10004
|
-
|
|
10005
|
-
await spawnDaemon(session2, { timeout: timeout2 });
|
|
10006
|
-
}
|
|
10114
|
+
await ensureDaemon(session2, { timeout: timeout2 });
|
|
10007
10115
|
const client = new DaemonClient(session2);
|
|
10008
10116
|
const response = await client.request("launch", { command, brk, port });
|
|
10009
10117
|
if (!response.ok) {
|
|
@@ -10049,7 +10157,7 @@ var init_attach = __esm(() => {
|
|
|
10049
10157
|
return 1;
|
|
10050
10158
|
}
|
|
10051
10159
|
const timeout2 = typeof args.flags.timeout === "string" ? parseInt(args.flags.timeout, 10) : undefined;
|
|
10052
|
-
await
|
|
10160
|
+
await ensureDaemon(session2, { timeout: timeout2 });
|
|
10053
10161
|
const client = new DaemonClient(session2);
|
|
10054
10162
|
const response = await client.request("attach", { target });
|
|
10055
10163
|
if (!response.ok) {
|
|
@@ -12006,7 +12114,13 @@ function formatLogEntry(entry) {
|
|
|
12006
12114
|
const summary = summarizer ? summarizer(entry) : summarizeParams(entry);
|
|
12007
12115
|
return `${time3} <- ${entry.method}${summary ? ` ${summary}` : ""}`;
|
|
12008
12116
|
}
|
|
12009
|
-
|
|
12117
|
+
function formatDaemonLogEntry(entry) {
|
|
12118
|
+
const time3 = `[${formatTime(entry.ts)}]`;
|
|
12119
|
+
const level = levelColors[entry.level] ?? entry.level.toUpperCase();
|
|
12120
|
+
const data = entry.data ? ` ${truncate2(JSON.stringify(entry.data), 120)}` : "";
|
|
12121
|
+
return `${time3} ${level} ${entry.event}: ${entry.message}${data}`;
|
|
12122
|
+
}
|
|
12123
|
+
var eventSummarizers, responseSummarizers, levelColors;
|
|
12010
12124
|
var init_logs = __esm(() => {
|
|
12011
12125
|
eventSummarizers = {
|
|
12012
12126
|
"Debugger.scriptParsed": (e) => {
|
|
@@ -12063,6 +12177,12 @@ var init_logs = __esm(() => {
|
|
|
12063
12177
|
return `breakpointId=${r.breakpointId}`;
|
|
12064
12178
|
}
|
|
12065
12179
|
};
|
|
12180
|
+
levelColors = {
|
|
12181
|
+
info: "INFO ",
|
|
12182
|
+
warn: "WARN ",
|
|
12183
|
+
error: "ERROR",
|
|
12184
|
+
debug: "DEBUG"
|
|
12185
|
+
};
|
|
12066
12186
|
});
|
|
12067
12187
|
|
|
12068
12188
|
// src/commands/logs.ts
|
|
@@ -12070,13 +12190,25 @@ var exports_logs = {};
|
|
|
12070
12190
|
import {
|
|
12071
12191
|
closeSync,
|
|
12072
12192
|
existsSync as existsSync5,
|
|
12073
|
-
openSync,
|
|
12074
|
-
readFileSync,
|
|
12193
|
+
openSync as openSync2,
|
|
12194
|
+
readFileSync as readFileSync2,
|
|
12075
12195
|
readSync,
|
|
12076
12196
|
watch,
|
|
12077
|
-
writeFileSync as
|
|
12197
|
+
writeFileSync as writeFileSync4
|
|
12078
12198
|
} from "fs";
|
|
12079
|
-
function
|
|
12199
|
+
function parseCdpEntries(text) {
|
|
12200
|
+
const entries = [];
|
|
12201
|
+
for (const line of text.split(`
|
|
12202
|
+
`)) {
|
|
12203
|
+
if (!line.trim())
|
|
12204
|
+
continue;
|
|
12205
|
+
try {
|
|
12206
|
+
entries.push(JSON.parse(line));
|
|
12207
|
+
} catch {}
|
|
12208
|
+
}
|
|
12209
|
+
return entries;
|
|
12210
|
+
}
|
|
12211
|
+
function parseDaemonEntries(text) {
|
|
12080
12212
|
const entries = [];
|
|
12081
12213
|
for (const line of text.split(`
|
|
12082
12214
|
`)) {
|
|
@@ -12091,7 +12223,10 @@ function parseEntries(text) {
|
|
|
12091
12223
|
function filterByDomain(entries, domain) {
|
|
12092
12224
|
return entries.filter((e) => e.method.startsWith(`${domain}.`));
|
|
12093
12225
|
}
|
|
12094
|
-
function
|
|
12226
|
+
function filterByLevel(entries, level) {
|
|
12227
|
+
return entries.filter((e) => e.level === level);
|
|
12228
|
+
}
|
|
12229
|
+
function printCdpEntries(entries, json2) {
|
|
12095
12230
|
for (const entry of entries) {
|
|
12096
12231
|
if (json2) {
|
|
12097
12232
|
console.log(JSON.stringify(entry));
|
|
@@ -12100,37 +12235,56 @@ function printEntries(entries, json2) {
|
|
|
12100
12235
|
}
|
|
12101
12236
|
}
|
|
12102
12237
|
}
|
|
12238
|
+
function printDaemonEntries(entries, json2) {
|
|
12239
|
+
for (const entry of entries) {
|
|
12240
|
+
if (json2) {
|
|
12241
|
+
console.log(JSON.stringify(entry));
|
|
12242
|
+
} else {
|
|
12243
|
+
console.log(formatDaemonLogEntry(entry));
|
|
12244
|
+
}
|
|
12245
|
+
}
|
|
12246
|
+
}
|
|
12103
12247
|
var init_logs2 = __esm(() => {
|
|
12104
12248
|
init_registry();
|
|
12105
12249
|
init_paths();
|
|
12106
12250
|
init_logs();
|
|
12107
12251
|
registerCommand("logs", async (args) => {
|
|
12108
12252
|
const session2 = args.global.session;
|
|
12109
|
-
const
|
|
12253
|
+
const isDaemon = args.flags.daemon === true;
|
|
12254
|
+
const logPath = isDaemon ? getDaemonLogPath(session2) : getLogPath(session2);
|
|
12110
12255
|
if (args.flags.clear === true) {
|
|
12111
12256
|
if (existsSync5(logPath)) {
|
|
12112
|
-
|
|
12113
|
-
console.log("Log cleared
|
|
12257
|
+
writeFileSync4(logPath, "");
|
|
12258
|
+
console.log(`${isDaemon ? "Daemon log" : "Log"} cleared`);
|
|
12114
12259
|
} else {
|
|
12115
|
-
console.log(
|
|
12260
|
+
console.log(`No ${isDaemon ? "daemon " : ""}log file to clear`);
|
|
12116
12261
|
}
|
|
12117
12262
|
return 0;
|
|
12118
12263
|
}
|
|
12119
12264
|
if (!existsSync5(logPath)) {
|
|
12120
|
-
console.error(`No log file for session "${session2}"`);
|
|
12265
|
+
console.error(`No ${isDaemon ? "daemon " : ""}log file for session "${session2}"`);
|
|
12121
12266
|
console.error(" -> Try: agent-dbg launch --brk node app.js");
|
|
12122
12267
|
return 1;
|
|
12123
12268
|
}
|
|
12124
12269
|
const isJson = args.global.json;
|
|
12125
12270
|
const domain = typeof args.flags.domain === "string" ? args.flags.domain : undefined;
|
|
12271
|
+
const level = typeof args.flags.level === "string" ? args.flags.level : undefined;
|
|
12126
12272
|
const limit = typeof args.flags.limit === "string" ? parseInt(args.flags.limit, 10) : 50;
|
|
12127
12273
|
const follow = args.flags.follow === true;
|
|
12128
|
-
const content =
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
12274
|
+
const content = readFileSync2(logPath, "utf-8");
|
|
12275
|
+
if (isDaemon) {
|
|
12276
|
+
let entries = parseDaemonEntries(content);
|
|
12277
|
+
if (level)
|
|
12278
|
+
entries = filterByLevel(entries, level);
|
|
12279
|
+
const sliced = follow ? entries : entries.slice(-limit);
|
|
12280
|
+
printDaemonEntries(sliced, isJson);
|
|
12281
|
+
} else {
|
|
12282
|
+
let entries = parseCdpEntries(content);
|
|
12283
|
+
if (domain)
|
|
12284
|
+
entries = filterByDomain(entries, domain);
|
|
12285
|
+
const sliced = follow ? entries : entries.slice(-limit);
|
|
12286
|
+
printCdpEntries(sliced, isJson);
|
|
12287
|
+
}
|
|
12134
12288
|
if (!follow)
|
|
12135
12289
|
return 0;
|
|
12136
12290
|
let offset = Buffer.byteLength(content, "utf-8");
|
|
@@ -12140,15 +12294,22 @@ var init_logs2 = __esm(() => {
|
|
|
12140
12294
|
const size = Bun.file(logPath).size;
|
|
12141
12295
|
if (size <= offset)
|
|
12142
12296
|
return;
|
|
12143
|
-
const fd =
|
|
12297
|
+
const fd = openSync2(logPath, "r");
|
|
12144
12298
|
const buf = Buffer.alloc(size - offset);
|
|
12145
12299
|
readSync(fd, buf, 0, buf.length, offset);
|
|
12146
12300
|
closeSync(fd);
|
|
12147
12301
|
offset = size;
|
|
12148
|
-
|
|
12149
|
-
|
|
12150
|
-
|
|
12151
|
-
|
|
12302
|
+
if (isDaemon) {
|
|
12303
|
+
let newEntries = parseDaemonEntries(buf.toString("utf-8"));
|
|
12304
|
+
if (level)
|
|
12305
|
+
newEntries = filterByLevel(newEntries, level);
|
|
12306
|
+
printDaemonEntries(newEntries, isJson);
|
|
12307
|
+
} else {
|
|
12308
|
+
let newEntries = parseCdpEntries(buf.toString("utf-8"));
|
|
12309
|
+
if (domain)
|
|
12310
|
+
newEntries = filterByDomain(newEntries, domain);
|
|
12311
|
+
printCdpEntries(newEntries, isJson);
|
|
12312
|
+
}
|
|
12152
12313
|
} catch {}
|
|
12153
12314
|
};
|
|
12154
12315
|
watcher = watch(logPath, () => {
|