agent-dbg 0.1.9 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/agent-dbg/SKILL.md +44 -14
- package/.claude/skills/agent-dbg/references/commands.md +4 -0
- package/README.md +65 -29
- package/dist/main.js +362 -98
- package/package.json +3 -2
- package/src/cdp/client.ts +67 -6
- package/src/cdp/jsc-client.ts +58 -0
- package/src/cdp/jsc-protocol.d.ts +2807 -0
- package/src/daemon/adapters/bun-adapter.ts +190 -0
- package/src/daemon/adapters/index.ts +13 -0
- package/src/daemon/adapters/node-adapter.ts +121 -0
- package/src/daemon/runtime-adapter.ts +50 -0
- package/src/daemon/session-blackbox.ts +2 -6
- package/src/daemon/session-breakpoints.ts +64 -53
- package/src/daemon/session-inspection.ts +2 -2
- package/src/daemon/session-state.ts +1 -1
- package/src/daemon/session.ts +46 -44
- package/src/formatter/source.ts +6 -2
- package/tests/fixtures/bun-simple.js +12 -0
- package/tests/integration/bun-launch.test.ts +135 -0
- package/tests/unit/cdp-client.test.ts +134 -10
- package/tests/unit/jsc-client.test.ts +165 -0
- package/demo/DEMO.md +0 -71
- package/demo/order-processor.js +0 -35
- package/tests/fixtures/dap/hello +0 -0
- package/tests/fixtures/dap/hello.dSYM/Contents/Info.plist +0 -20
- package/tests/fixtures/dap/hello.dSYM/Contents/Resources/DWARF/hello +0 -0
- package/tests/fixtures/dap/hello.dSYM/Contents/Resources/Relocations/aarch64/hello.yml +0 -5
package/dist/main.js
CHANGED
|
@@ -7631,6 +7631,9 @@ class CdpClient {
|
|
|
7631
7631
|
ws.addEventListener("error", onError, { once: true });
|
|
7632
7632
|
});
|
|
7633
7633
|
}
|
|
7634
|
+
async sendRaw(method, params) {
|
|
7635
|
+
return this.send(method, params);
|
|
7636
|
+
}
|
|
7634
7637
|
async send(method, ...args) {
|
|
7635
7638
|
if (!this.isConnected) {
|
|
7636
7639
|
throw new Error("CDP client is not connected");
|
|
@@ -7670,13 +7673,39 @@ class CdpClient {
|
|
|
7670
7673
|
}
|
|
7671
7674
|
}
|
|
7672
7675
|
}
|
|
7676
|
+
waitFor(event, opts) {
|
|
7677
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
7678
|
+
const filter = opts?.filter;
|
|
7679
|
+
return new Promise((resolve, reject) => {
|
|
7680
|
+
const handler = (params) => {
|
|
7681
|
+
if (filter && !filter(params))
|
|
7682
|
+
return;
|
|
7683
|
+
cleanup();
|
|
7684
|
+
resolve(params);
|
|
7685
|
+
};
|
|
7686
|
+
const timer = setTimeout(() => {
|
|
7687
|
+
cleanup();
|
|
7688
|
+
reject(new Error(`waitFor timed out: ${event} (after ${timeoutMs}ms)`));
|
|
7689
|
+
}, timeoutMs);
|
|
7690
|
+
const cleanup = () => {
|
|
7691
|
+
clearTimeout(timer);
|
|
7692
|
+
this.off(event, handler);
|
|
7693
|
+
};
|
|
7694
|
+
this.on(event, handler);
|
|
7695
|
+
});
|
|
7696
|
+
}
|
|
7697
|
+
enabledDomains = new Set;
|
|
7673
7698
|
async enableDomains() {
|
|
7674
|
-
await Promise.all([
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7699
|
+
await Promise.all([this.send("Debugger.enable"), this.send("Runtime.enable")]);
|
|
7700
|
+
this.enabledDomains.add("Debugger");
|
|
7701
|
+
this.enabledDomains.add("Runtime");
|
|
7702
|
+
const optional2 = ["Profiler", "HeapProfiler"];
|
|
7703
|
+
await Promise.allSettled(optional2.map(async (domain) => {
|
|
7704
|
+
try {
|
|
7705
|
+
await this.send(`${domain}.enable`);
|
|
7706
|
+
this.enabledDomains.add(domain);
|
|
7707
|
+
} catch {}
|
|
7708
|
+
}));
|
|
7680
7709
|
}
|
|
7681
7710
|
async runIfWaitingForDebugger() {
|
|
7682
7711
|
await this.send("Runtime.runIfWaitingForDebugger");
|
|
@@ -8765,6 +8794,237 @@ var init_resolver = __esm(() => {
|
|
|
8765
8794
|
init_trace_mapping();
|
|
8766
8795
|
});
|
|
8767
8796
|
|
|
8797
|
+
// src/cdp/jsc-client.ts
|
|
8798
|
+
class JscClient {
|
|
8799
|
+
cdp;
|
|
8800
|
+
constructor(cdp) {
|
|
8801
|
+
this.cdp = cdp;
|
|
8802
|
+
}
|
|
8803
|
+
async send(method, ...args) {
|
|
8804
|
+
const params = args[0];
|
|
8805
|
+
return this.cdp.sendRaw(method, params);
|
|
8806
|
+
}
|
|
8807
|
+
on(event, handler) {
|
|
8808
|
+
this.cdp.on(event, handler);
|
|
8809
|
+
}
|
|
8810
|
+
off(event, handler) {
|
|
8811
|
+
this.cdp.off(event, handler);
|
|
8812
|
+
}
|
|
8813
|
+
waitFor(event, opts) {
|
|
8814
|
+
return this.cdp.waitFor(event, opts);
|
|
8815
|
+
}
|
|
8816
|
+
disconnect() {
|
|
8817
|
+
this.cdp.disconnect();
|
|
8818
|
+
}
|
|
8819
|
+
get connected() {
|
|
8820
|
+
return this.cdp.connected;
|
|
8821
|
+
}
|
|
8822
|
+
}
|
|
8823
|
+
|
|
8824
|
+
// src/daemon/adapters/bun-adapter.ts
|
|
8825
|
+
class BunAdapter {
|
|
8826
|
+
name = "bun";
|
|
8827
|
+
internalUrlPrefix = "bun:";
|
|
8828
|
+
jsc = null;
|
|
8829
|
+
async preEnable(cdp) {
|
|
8830
|
+
this.jsc = new JscClient(cdp);
|
|
8831
|
+
await this.jsc.send("Inspector.enable");
|
|
8832
|
+
}
|
|
8833
|
+
async waitForBrkPause(session) {
|
|
8834
|
+
if (!this.jsc)
|
|
8835
|
+
return;
|
|
8836
|
+
await this.jsc.send("Debugger.setBreakpointsActive", { active: true });
|
|
8837
|
+
await this.jsc.send("Debugger.setPauseForInternalScripts", { shouldPause: false });
|
|
8838
|
+
const entryScript = this.resolveEntryScript(session);
|
|
8839
|
+
const tempBpId = await this.setEntryBreakpoint(entryScript);
|
|
8840
|
+
try {
|
|
8841
|
+
const waiter = session.createPauseWaiter(5000);
|
|
8842
|
+
await this.jsc.send("Inspector.initialized");
|
|
8843
|
+
await waiter;
|
|
8844
|
+
} finally {
|
|
8845
|
+
if (tempBpId && this.jsc.connected) {
|
|
8846
|
+
try {
|
|
8847
|
+
await this.jsc.send("Debugger.removeBreakpoint", { breakpointId: tempBpId });
|
|
8848
|
+
} catch {}
|
|
8849
|
+
}
|
|
8850
|
+
}
|
|
8851
|
+
}
|
|
8852
|
+
async setBreakpointByLocation(_cdp, params) {
|
|
8853
|
+
const jsc = this.ensureJsc();
|
|
8854
|
+
const scriptId = params.scriptId ?? this.findScriptId(params.scripts, params.url);
|
|
8855
|
+
if (!scriptId) {
|
|
8856
|
+
throw new Error(`Cannot find script for "${params.file}" \u2014 ensure the script is loaded`);
|
|
8857
|
+
}
|
|
8858
|
+
const r = await jsc.send("Debugger.setBreakpoint", {
|
|
8859
|
+
location: {
|
|
8860
|
+
scriptId,
|
|
8861
|
+
lineNumber: params.line - 1,
|
|
8862
|
+
columnNumber: params.column
|
|
8863
|
+
},
|
|
8864
|
+
options: params.condition ? { condition: params.condition } : undefined
|
|
8865
|
+
});
|
|
8866
|
+
return {
|
|
8867
|
+
breakpointId: r.breakpointId,
|
|
8868
|
+
location: r.actualLocation,
|
|
8869
|
+
url: params.url
|
|
8870
|
+
};
|
|
8871
|
+
}
|
|
8872
|
+
async getBreakableLocations(_cdp, scriptId, startLine, endLine) {
|
|
8873
|
+
const jsc = this.ensureJsc();
|
|
8874
|
+
const r = await jsc.send("Debugger.getBreakpointLocations", {
|
|
8875
|
+
start: { scriptId, lineNumber: startLine - 1 },
|
|
8876
|
+
end: { scriptId, lineNumber: endLine }
|
|
8877
|
+
});
|
|
8878
|
+
return (r.locations ?? []).map((loc) => ({
|
|
8879
|
+
line: loc.lineNumber + 1,
|
|
8880
|
+
column: (loc.columnNumber ?? 0) + 1
|
|
8881
|
+
}));
|
|
8882
|
+
}
|
|
8883
|
+
async getProperties(cdp, params) {
|
|
8884
|
+
const raw = await cdp.sendRaw("Runtime.getProperties", params);
|
|
8885
|
+
if ("properties" in raw && !("result" in raw)) {
|
|
8886
|
+
raw.result = raw.properties;
|
|
8887
|
+
delete raw.properties;
|
|
8888
|
+
}
|
|
8889
|
+
return raw;
|
|
8890
|
+
}
|
|
8891
|
+
async setBlackboxPatterns(_cdp, patterns) {
|
|
8892
|
+
const jsc = this.ensureJsc();
|
|
8893
|
+
for (const pattern of patterns) {
|
|
8894
|
+
await jsc.send("Debugger.setShouldBlackboxURL", {
|
|
8895
|
+
url: pattern,
|
|
8896
|
+
caseSensitive: false,
|
|
8897
|
+
shouldBlackbox: true
|
|
8898
|
+
});
|
|
8899
|
+
}
|
|
8900
|
+
}
|
|
8901
|
+
ensureJsc() {
|
|
8902
|
+
if (!this.jsc)
|
|
8903
|
+
throw new Error("JscClient not initialized \u2014 call preEnable first");
|
|
8904
|
+
return this.jsc;
|
|
8905
|
+
}
|
|
8906
|
+
async setEntryBreakpoint(entryScript) {
|
|
8907
|
+
if (!entryScript || !this.jsc)
|
|
8908
|
+
return null;
|
|
8909
|
+
const parts = entryScript.split("/");
|
|
8910
|
+
const filename = parts[parts.length - 1] ?? entryScript;
|
|
8911
|
+
const urlRegex2 = `${escapeRegex2(filename)}$`;
|
|
8912
|
+
try {
|
|
8913
|
+
const r = await this.jsc.send("Debugger.setBreakpointByUrl", {
|
|
8914
|
+
urlRegex: urlRegex2,
|
|
8915
|
+
lineNumber: 1
|
|
8916
|
+
});
|
|
8917
|
+
return r.breakpointId;
|
|
8918
|
+
} catch {
|
|
8919
|
+
return null;
|
|
8920
|
+
}
|
|
8921
|
+
}
|
|
8922
|
+
findScriptId(scripts, url2) {
|
|
8923
|
+
if (!url2)
|
|
8924
|
+
return;
|
|
8925
|
+
for (const [sid, info] of scripts) {
|
|
8926
|
+
if (info.url === url2)
|
|
8927
|
+
return sid;
|
|
8928
|
+
}
|
|
8929
|
+
return;
|
|
8930
|
+
}
|
|
8931
|
+
resolveEntryScript(session) {
|
|
8932
|
+
if (!session.launchCommand)
|
|
8933
|
+
return null;
|
|
8934
|
+
for (let i = session.launchCommand.length - 1;i >= 0; i--) {
|
|
8935
|
+
const arg = session.launchCommand[i];
|
|
8936
|
+
if (!arg.startsWith("-"))
|
|
8937
|
+
return arg;
|
|
8938
|
+
}
|
|
8939
|
+
return null;
|
|
8940
|
+
}
|
|
8941
|
+
}
|
|
8942
|
+
function escapeRegex2(s) {
|
|
8943
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8944
|
+
}
|
|
8945
|
+
var init_bun_adapter = () => {};
|
|
8946
|
+
|
|
8947
|
+
// src/daemon/adapters/node-adapter.ts
|
|
8948
|
+
class NodeAdapter {
|
|
8949
|
+
name = "node";
|
|
8950
|
+
internalUrlPrefix = "node:";
|
|
8951
|
+
async preEnable(_cdp) {}
|
|
8952
|
+
async waitForBrkPause(session) {
|
|
8953
|
+
if (!session.isPaused()) {
|
|
8954
|
+
await Bun.sleep(100);
|
|
8955
|
+
}
|
|
8956
|
+
if (!session.isPaused() && session.cdp) {
|
|
8957
|
+
await session.cdp.send("Debugger.pause");
|
|
8958
|
+
await session.cdp.send("Runtime.runIfWaitingForDebugger");
|
|
8959
|
+
const deadline = Date.now() + 2000;
|
|
8960
|
+
while (!session.isPaused() && Date.now() < deadline) {
|
|
8961
|
+
await Bun.sleep(50);
|
|
8962
|
+
}
|
|
8963
|
+
}
|
|
8964
|
+
await this.skipInternalPauses(session);
|
|
8965
|
+
}
|
|
8966
|
+
async setBreakpointByLocation(cdp, params) {
|
|
8967
|
+
const bpParams = {
|
|
8968
|
+
lineNumber: params.line - 1
|
|
8969
|
+
};
|
|
8970
|
+
if (params.column !== undefined) {
|
|
8971
|
+
bpParams.columnNumber = params.column;
|
|
8972
|
+
}
|
|
8973
|
+
if (params.urlRegex) {
|
|
8974
|
+
bpParams.urlRegex = params.urlRegex;
|
|
8975
|
+
} else if (params.url) {
|
|
8976
|
+
bpParams.url = params.url;
|
|
8977
|
+
}
|
|
8978
|
+
if (params.condition) {
|
|
8979
|
+
bpParams.condition = params.condition;
|
|
8980
|
+
}
|
|
8981
|
+
const r = await cdp.send("Debugger.setBreakpointByUrl", bpParams);
|
|
8982
|
+
const loc = r.locations[0];
|
|
8983
|
+
return {
|
|
8984
|
+
breakpointId: r.breakpointId,
|
|
8985
|
+
location: loc ? { scriptId: loc.scriptId, lineNumber: loc.lineNumber, columnNumber: loc.columnNumber } : undefined,
|
|
8986
|
+
url: params.url
|
|
8987
|
+
};
|
|
8988
|
+
}
|
|
8989
|
+
async getBreakableLocations(cdp, scriptId, startLine, endLine) {
|
|
8990
|
+
const r = await cdp.send("Debugger.getPossibleBreakpoints", {
|
|
8991
|
+
start: { scriptId, lineNumber: startLine - 1 },
|
|
8992
|
+
end: { scriptId, lineNumber: endLine }
|
|
8993
|
+
});
|
|
8994
|
+
return r.locations.map((loc) => ({
|
|
8995
|
+
line: loc.lineNumber + 1,
|
|
8996
|
+
column: (loc.columnNumber ?? 0) + 1
|
|
8997
|
+
}));
|
|
8998
|
+
}
|
|
8999
|
+
async getProperties(cdp, params) {
|
|
9000
|
+
return cdp.send("Runtime.getProperties", params);
|
|
9001
|
+
}
|
|
9002
|
+
async setBlackboxPatterns(cdp, patterns) {
|
|
9003
|
+
await cdp.send("Debugger.setBlackboxPatterns", { patterns });
|
|
9004
|
+
}
|
|
9005
|
+
async skipInternalPauses(session) {
|
|
9006
|
+
let skips = 0;
|
|
9007
|
+
while (session.isPaused() && session.cdp && session.pauseInfo?.url?.startsWith(this.internalUrlPrefix) && skips < 5) {
|
|
9008
|
+
skips++;
|
|
9009
|
+
const waiter = session.createPauseWaiter(5000);
|
|
9010
|
+
await session.cdp.send("Debugger.resume");
|
|
9011
|
+
await waiter;
|
|
9012
|
+
}
|
|
9013
|
+
}
|
|
9014
|
+
}
|
|
9015
|
+
|
|
9016
|
+
// src/daemon/adapters/index.ts
|
|
9017
|
+
function createAdapter(command) {
|
|
9018
|
+
const bin = command[0]?.split("/").pop();
|
|
9019
|
+
if (bin === "bun" || bin === "bunx")
|
|
9020
|
+
return new BunAdapter;
|
|
9021
|
+
return new NodeAdapter;
|
|
9022
|
+
}
|
|
9023
|
+
var init_adapters = __esm(() => {
|
|
9024
|
+
init_bun_adapter();
|
|
9025
|
+
init_bun_adapter();
|
|
9026
|
+
});
|
|
9027
|
+
|
|
8768
9028
|
// src/daemon/session-blackbox.ts
|
|
8769
9029
|
async function addBlackbox(session, patterns) {
|
|
8770
9030
|
if (!session.cdp) {
|
|
@@ -8775,9 +9035,7 @@ async function addBlackbox(session, patterns) {
|
|
|
8775
9035
|
session.blackboxPatterns.push(p);
|
|
8776
9036
|
}
|
|
8777
9037
|
}
|
|
8778
|
-
await session.
|
|
8779
|
-
patterns: session.blackboxPatterns
|
|
8780
|
-
});
|
|
9038
|
+
await session.adapter.setBlackboxPatterns(session.cdp, session.blackboxPatterns);
|
|
8781
9039
|
return [...session.blackboxPatterns];
|
|
8782
9040
|
}
|
|
8783
9041
|
function listBlackbox(session) {
|
|
@@ -8792,9 +9050,7 @@ async function removeBlackbox(session, patterns) {
|
|
|
8792
9050
|
} else {
|
|
8793
9051
|
session.blackboxPatterns = session.blackboxPatterns.filter((p) => !patterns.includes(p));
|
|
8794
9052
|
}
|
|
8795
|
-
await session.
|
|
8796
|
-
patterns: session.blackboxPatterns
|
|
8797
|
-
});
|
|
9053
|
+
await session.adapter.setBlackboxPatterns(session.cdp, session.blackboxPatterns);
|
|
8798
9054
|
return [...session.blackboxPatterns];
|
|
8799
9055
|
}
|
|
8800
9056
|
|
|
@@ -8809,6 +9065,7 @@ async function setBreakpoint(session, file2, line, options) {
|
|
|
8809
9065
|
let actualLine = line;
|
|
8810
9066
|
let actualColumn = options?.column !== undefined ? options.column - 1 : undefined;
|
|
8811
9067
|
let actualFile = file2;
|
|
9068
|
+
let generatedScriptId = null;
|
|
8812
9069
|
if (!options?.urlRegex) {
|
|
8813
9070
|
const generated = session.sourceMapResolver.toGenerated(file2, line, actualColumn ?? 0);
|
|
8814
9071
|
if (generated) {
|
|
@@ -8816,34 +9073,37 @@ async function setBreakpoint(session, file2, line, options) {
|
|
|
8816
9073
|
originalLine = line;
|
|
8817
9074
|
actualLine = generated.line;
|
|
8818
9075
|
actualColumn = generated.column;
|
|
9076
|
+
generatedScriptId = generated.scriptId;
|
|
8819
9077
|
const scriptInfo = session.scripts.get(generated.scriptId);
|
|
8820
9078
|
if (scriptInfo) {
|
|
8821
9079
|
actualFile = scriptInfo.url;
|
|
8822
9080
|
}
|
|
8823
9081
|
}
|
|
8824
9082
|
}
|
|
8825
|
-
const params = {
|
|
8826
|
-
lineNumber: actualLine - 1
|
|
8827
|
-
};
|
|
8828
|
-
if (actualColumn !== undefined) {
|
|
8829
|
-
params.columnNumber = actualColumn;
|
|
8830
|
-
}
|
|
8831
9083
|
let url2 = null;
|
|
9084
|
+
let urlRegex2;
|
|
8832
9085
|
if (options?.urlRegex) {
|
|
8833
|
-
|
|
9086
|
+
urlRegex2 = options.urlRegex;
|
|
8834
9087
|
} else {
|
|
8835
9088
|
url2 = session.findScriptUrl(actualFile);
|
|
8836
|
-
if (url2) {
|
|
8837
|
-
|
|
8838
|
-
}
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
9089
|
+
if (!url2 && !generatedScriptId) {
|
|
9090
|
+
urlRegex2 = `${actualFile.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`;
|
|
9091
|
+
}
|
|
9092
|
+
}
|
|
9093
|
+
const r = await session.adapter.setBreakpointByLocation(session.cdp, {
|
|
9094
|
+
file: file2,
|
|
9095
|
+
line: actualLine,
|
|
9096
|
+
column: actualColumn,
|
|
9097
|
+
condition,
|
|
9098
|
+
url: url2 ?? undefined,
|
|
9099
|
+
urlRegex: urlRegex2,
|
|
9100
|
+
scriptId: generatedScriptId ?? undefined,
|
|
9101
|
+
scripts: session.scripts
|
|
9102
|
+
});
|
|
9103
|
+
const breakpointId = r.breakpointId;
|
|
9104
|
+
const loc = r.location;
|
|
9105
|
+
if (!url2)
|
|
9106
|
+
url2 = r.url ?? session.findScriptUrl(actualFile);
|
|
8847
9107
|
const resolvedUrl = originalFile ?? url2 ?? file2;
|
|
8848
9108
|
const resolvedLine = originalLine ?? (loc ? loc.lineNumber + 1 : line);
|
|
8849
9109
|
const resolvedColumn = loc?.columnNumber;
|
|
@@ -8869,7 +9129,7 @@ async function setBreakpoint(session, file2, line, options) {
|
|
|
8869
9129
|
if (options?.urlRegex) {
|
|
8870
9130
|
meta.urlRegex = options.urlRegex;
|
|
8871
9131
|
}
|
|
8872
|
-
const ref = session.refs.addBreakpoint(
|
|
9132
|
+
const ref = session.refs.addBreakpoint(breakpointId, meta);
|
|
8873
9133
|
const location = {
|
|
8874
9134
|
url: resolvedUrl,
|
|
8875
9135
|
line: resolvedLine
|
|
@@ -9025,18 +9285,24 @@ async function reEnableBreakpoint(session, ref, entry) {
|
|
|
9025
9285
|
const hitCount = meta.hitCount;
|
|
9026
9286
|
const urlRegex2 = meta.urlRegex;
|
|
9027
9287
|
const builtCondition = session.buildBreakpointCondition(condition, hitCount);
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
if (builtCondition) {
|
|
9037
|
-
bpParams.condition = builtCondition;
|
|
9288
|
+
let scriptId;
|
|
9289
|
+
if (url2) {
|
|
9290
|
+
for (const [sid, info] of session.scripts) {
|
|
9291
|
+
if (info.url === url2) {
|
|
9292
|
+
scriptId = sid;
|
|
9293
|
+
break;
|
|
9294
|
+
}
|
|
9295
|
+
}
|
|
9038
9296
|
}
|
|
9039
|
-
const r = await session.
|
|
9297
|
+
const r = await session.adapter.setBreakpointByLocation(session.cdp, {
|
|
9298
|
+
file: url2 ?? urlRegex2 ?? "",
|
|
9299
|
+
line,
|
|
9300
|
+
condition: builtCondition,
|
|
9301
|
+
url: url2,
|
|
9302
|
+
urlRegex: urlRegex2,
|
|
9303
|
+
scriptId,
|
|
9304
|
+
scripts: session.scripts
|
|
9305
|
+
});
|
|
9040
9306
|
const type = meta.type === "LP" ? "LP" : "BP";
|
|
9041
9307
|
const newMeta = { ...meta };
|
|
9042
9308
|
delete newMeta.type;
|
|
@@ -9065,14 +9331,7 @@ async function getBreakableLocations(session, file2, startLine, endLine) {
|
|
|
9065
9331
|
if (!scriptId) {
|
|
9066
9332
|
throw new Error(`No scriptId found for "${file2}"`);
|
|
9067
9333
|
}
|
|
9068
|
-
|
|
9069
|
-
start: { scriptId, lineNumber: startLine - 1 },
|
|
9070
|
-
end: { scriptId, lineNumber: endLine }
|
|
9071
|
-
});
|
|
9072
|
-
return r.locations.map((loc) => ({
|
|
9073
|
-
line: loc.lineNumber + 1,
|
|
9074
|
-
column: (loc.columnNumber ?? 0) + 1
|
|
9075
|
-
}));
|
|
9334
|
+
return session.adapter.getBreakableLocations(session.cdp, scriptId, startLine, endLine);
|
|
9076
9335
|
}
|
|
9077
9336
|
async function setLogpoint(session, file2, line, template, options) {
|
|
9078
9337
|
if (!session.cdp) {
|
|
@@ -9085,17 +9344,29 @@ async function setLogpoint(session, file2, line, template, options) {
|
|
|
9085
9344
|
} else {
|
|
9086
9345
|
logExpr = `${logExpr}, false`;
|
|
9087
9346
|
}
|
|
9088
|
-
|
|
9089
|
-
|
|
9090
|
-
|
|
9091
|
-
}
|
|
9347
|
+
let urlRegex2;
|
|
9348
|
+
if (!url2) {
|
|
9349
|
+
urlRegex2 = `${file2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`;
|
|
9350
|
+
}
|
|
9351
|
+
let scriptId;
|
|
9092
9352
|
if (url2) {
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
9353
|
+
for (const [sid, info] of session.scripts) {
|
|
9354
|
+
if (info.url === url2) {
|
|
9355
|
+
scriptId = sid;
|
|
9356
|
+
break;
|
|
9357
|
+
}
|
|
9358
|
+
}
|
|
9096
9359
|
}
|
|
9097
|
-
const r = await session.
|
|
9098
|
-
|
|
9360
|
+
const r = await session.adapter.setBreakpointByLocation(session.cdp, {
|
|
9361
|
+
file: file2,
|
|
9362
|
+
line,
|
|
9363
|
+
condition: logExpr,
|
|
9364
|
+
url: url2 ?? undefined,
|
|
9365
|
+
urlRegex: urlRegex2,
|
|
9366
|
+
scriptId,
|
|
9367
|
+
scripts: session.scripts
|
|
9368
|
+
});
|
|
9369
|
+
const loc = r.location;
|
|
9099
9370
|
const resolvedUrl = url2 ?? file2;
|
|
9100
9371
|
const resolvedLine = loc ? loc.lineNumber + 1 : line;
|
|
9101
9372
|
const resolvedColumn = loc?.columnNumber;
|
|
@@ -9364,7 +9635,7 @@ async function getVars(session, options = {}) {
|
|
|
9364
9635
|
const objectId = scopeObj.objectId;
|
|
9365
9636
|
if (!objectId)
|
|
9366
9637
|
continue;
|
|
9367
|
-
const propsResult = await session.
|
|
9638
|
+
const propsResult = await session.adapter.getProperties(session.cdp, {
|
|
9368
9639
|
objectId,
|
|
9369
9640
|
ownProperties: true,
|
|
9370
9641
|
generatePreview: true
|
|
@@ -9417,7 +9688,7 @@ async function getProps(session, ref, options = {}) {
|
|
|
9417
9688
|
if (options.internal) {
|
|
9418
9689
|
propsParams.accessorPropertiesOnly = false;
|
|
9419
9690
|
}
|
|
9420
|
-
const propsResult = await session.
|
|
9691
|
+
const propsResult = await session.adapter.getProperties(session.cdp, propsParams);
|
|
9421
9692
|
const properties = propsResult.result ?? [];
|
|
9422
9693
|
const internalProps = options.internal ? propsResult.internalProperties ?? [] : [];
|
|
9423
9694
|
const result = [];
|
|
@@ -9995,7 +10266,7 @@ async function buildState(session, options = {}) {
|
|
|
9995
10266
|
const objectId = scopeObj.objectId;
|
|
9996
10267
|
if (!objectId)
|
|
9997
10268
|
continue;
|
|
9998
|
-
const propsResult = await session.
|
|
10269
|
+
const propsResult = await session.adapter.getProperties(session.cdp, {
|
|
9999
10270
|
objectId,
|
|
10000
10271
|
ownProperties: true,
|
|
10001
10272
|
generatePreview: true
|
|
@@ -10053,6 +10324,7 @@ class DebugSession {
|
|
|
10053
10324
|
disabledBreakpoints = new Map;
|
|
10054
10325
|
launchCommand = null;
|
|
10055
10326
|
launchOptions = null;
|
|
10327
|
+
adapter;
|
|
10056
10328
|
cdpLogger;
|
|
10057
10329
|
daemonLogger;
|
|
10058
10330
|
constructor(session, options) {
|
|
@@ -10060,6 +10332,10 @@ class DebugSession {
|
|
|
10060
10332
|
ensureSocketDir();
|
|
10061
10333
|
this.cdpLogger = new CdpLogger(getLogPath(session));
|
|
10062
10334
|
this.daemonLogger = options?.daemonLogger ?? new DaemonLogger(getDaemonLogPath(session));
|
|
10335
|
+
this.adapter = createAdapter(["node"]);
|
|
10336
|
+
}
|
|
10337
|
+
get runtime() {
|
|
10338
|
+
return this.adapter.name;
|
|
10063
10339
|
}
|
|
10064
10340
|
async launch(command, options = {}) {
|
|
10065
10341
|
if (this.state !== "idle") {
|
|
@@ -10070,12 +10346,13 @@ class DebugSession {
|
|
|
10070
10346
|
}
|
|
10071
10347
|
this.launchCommand = command;
|
|
10072
10348
|
this.launchOptions = options;
|
|
10349
|
+
this.adapter = createAdapter(command);
|
|
10073
10350
|
const brk = options.brk ?? true;
|
|
10074
10351
|
const port = options.port ?? 0;
|
|
10075
10352
|
const inspectFlag = brk ? `--inspect-brk=${port}` : `--inspect=${port}`;
|
|
10076
|
-
const
|
|
10353
|
+
const runtimeBin = command[0];
|
|
10077
10354
|
const rest = command.slice(1);
|
|
10078
|
-
const spawnArgs = [
|
|
10355
|
+
const spawnArgs = [runtimeBin, inspectFlag, ...rest];
|
|
10079
10356
|
const proc = Bun.spawn(spawnArgs, {
|
|
10080
10357
|
stdin: "ignore",
|
|
10081
10358
|
stdout: "ignore",
|
|
@@ -10102,7 +10379,18 @@ class DebugSession {
|
|
|
10102
10379
|
paused: this.sessionState === "paused"
|
|
10103
10380
|
};
|
|
10104
10381
|
if (this.pauseInfo) {
|
|
10105
|
-
|
|
10382
|
+
const translated = { ...this.pauseInfo };
|
|
10383
|
+
if (translated.scriptId && translated.line !== undefined) {
|
|
10384
|
+
const resolved = this.resolveOriginalLocation(translated.scriptId, translated.line + 1, translated.column ?? 0);
|
|
10385
|
+
if (resolved) {
|
|
10386
|
+
translated.url = resolved.url;
|
|
10387
|
+
translated.line = resolved.line - 1;
|
|
10388
|
+
if (resolved.column !== undefined) {
|
|
10389
|
+
translated.column = resolved.column - 1;
|
|
10390
|
+
}
|
|
10391
|
+
}
|
|
10392
|
+
}
|
|
10393
|
+
result.pauseInfo = translated;
|
|
10106
10394
|
}
|
|
10107
10395
|
return result;
|
|
10108
10396
|
}
|
|
@@ -10338,24 +10626,16 @@ class DebugSession {
|
|
|
10338
10626
|
if (settled)
|
|
10339
10627
|
return;
|
|
10340
10628
|
settled = true;
|
|
10341
|
-
clearTimeout(timer);
|
|
10342
10629
|
clearInterval(pollTimer);
|
|
10343
|
-
this.cdp?.off("Debugger.paused", handler);
|
|
10344
10630
|
this.onProcessExit = null;
|
|
10345
10631
|
resolve3();
|
|
10346
10632
|
};
|
|
10347
|
-
|
|
10348
|
-
settle();
|
|
10349
|
-
}, timeoutMs);
|
|
10350
|
-
const handler = () => {
|
|
10351
|
-
settle();
|
|
10352
|
-
};
|
|
10633
|
+
this.cdp?.waitFor("Debugger.paused", { timeoutMs }).then(() => settle()).catch(() => settle());
|
|
10353
10634
|
const pollTimer = setInterval(() => {
|
|
10354
10635
|
if (this.isPaused() || this.state === "idle") {
|
|
10355
10636
|
settle();
|
|
10356
10637
|
}
|
|
10357
10638
|
}, 100);
|
|
10358
|
-
this.cdp?.on("Debugger.paused", handler);
|
|
10359
10639
|
this.onProcessExit = settle;
|
|
10360
10640
|
});
|
|
10361
10641
|
}
|
|
@@ -10385,24 +10665,7 @@ class DebugSession {
|
|
|
10385
10665
|
return this.state === "paused";
|
|
10386
10666
|
}
|
|
10387
10667
|
async waitForBrkPause() {
|
|
10388
|
-
|
|
10389
|
-
await Bun.sleep(100);
|
|
10390
|
-
}
|
|
10391
|
-
if (!this.isPaused() && this.cdp) {
|
|
10392
|
-
await this.cdp.send("Debugger.pause");
|
|
10393
|
-
await this.cdp.send("Runtime.runIfWaitingForDebugger");
|
|
10394
|
-
const deadline = Date.now() + 2000;
|
|
10395
|
-
while (!this.isPaused() && Date.now() < deadline) {
|
|
10396
|
-
await Bun.sleep(50);
|
|
10397
|
-
}
|
|
10398
|
-
}
|
|
10399
|
-
let skips = 0;
|
|
10400
|
-
while (this.isPaused() && this.cdp && this.pauseInfo?.url?.startsWith("node:") && skips < 5) {
|
|
10401
|
-
skips++;
|
|
10402
|
-
const waiter = this.createPauseWaiter(5000);
|
|
10403
|
-
await this.cdp.send("Debugger.resume");
|
|
10404
|
-
await waiter;
|
|
10405
|
-
}
|
|
10668
|
+
return this.adapter.waitForBrkPause(this);
|
|
10406
10669
|
}
|
|
10407
10670
|
async connectCdp(wsUrl) {
|
|
10408
10671
|
this.daemonLogger.debug("cdp.connecting", `Connecting to ${wsUrl}`);
|
|
@@ -10410,11 +10673,10 @@ class DebugSession {
|
|
|
10410
10673
|
this.cdp = cdp;
|
|
10411
10674
|
this.daemonLogger.info("cdp.connected", `CDP connected to ${wsUrl}`);
|
|
10412
10675
|
this.setupCdpEventHandlers(cdp);
|
|
10676
|
+
await this.adapter.preEnable(cdp);
|
|
10413
10677
|
await cdp.enableDomains();
|
|
10414
10678
|
if (this.blackboxPatterns.length > 0) {
|
|
10415
|
-
await
|
|
10416
|
-
patterns: this.blackboxPatterns
|
|
10417
|
-
});
|
|
10679
|
+
await this.adapter.setBlackboxPatterns(cdp, this.blackboxPatterns);
|
|
10418
10680
|
}
|
|
10419
10681
|
if (this.state === "idle") {
|
|
10420
10682
|
this.state = "running";
|
|
@@ -10605,12 +10867,13 @@ var init_session2 = __esm(() => {
|
|
|
10605
10867
|
init_logger2();
|
|
10606
10868
|
init_ref_table();
|
|
10607
10869
|
init_resolver();
|
|
10870
|
+
init_adapters();
|
|
10608
10871
|
init_logger();
|
|
10609
10872
|
init_paths();
|
|
10610
10873
|
init_session_inspection();
|
|
10611
10874
|
init_session_mutation();
|
|
10612
10875
|
init_session_state();
|
|
10613
|
-
INSPECTOR_URL_REGEX = /Debugger listening on
|
|
10876
|
+
INSPECTOR_URL_REGEX = /(?:Debugger listening on\s+)?(wss?:\/\/\S+)/;
|
|
10614
10877
|
});
|
|
10615
10878
|
|
|
10616
10879
|
// src/daemon/entry.ts
|
|
@@ -11416,8 +11679,9 @@ function formatSource(lines) {
|
|
|
11416
11679
|
const trimmed = line.isCurrent ? trimLine(line.content, line.currentColumn) : trimLine(line.content);
|
|
11417
11680
|
result.push(`${marker} ${num}\u2502${trimmed.text}`);
|
|
11418
11681
|
if (line.isCurrent && trimmed.caretOffset !== undefined && trimmed.caretOffset >= 0) {
|
|
11419
|
-
const
|
|
11420
|
-
|
|
11682
|
+
const gutter = " ".repeat(numWidth + 4);
|
|
11683
|
+
const indent = trimmed.text.slice(0, trimmed.caretOffset).replace(/[^\t]/g, " ");
|
|
11684
|
+
result.push(`${gutter}${indent}^`);
|
|
11421
11685
|
}
|
|
11422
11686
|
}
|
|
11423
11687
|
return result.join(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-dbg",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Node.js Debugger CLI for AI Agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"test": "bun test",
|
|
13
13
|
"lint": "biome check .",
|
|
14
14
|
"format": "biome check --write .",
|
|
15
|
-
"typecheck": "tsc --noEmit"
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
|
+
"update-jsc-protocol": "curl -sL https://raw.githubusercontent.com/oven-sh/bun/main/packages/bun-inspector-protocol/src/protocol/jsc/index.d.ts -o src/cdp/jsc-protocol.d.ts"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
18
19
|
"@biomejs/biome": "^2.3.14",
|