@saptools/cf-inspector 0.4.10 → 0.4.12
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/README.md +6 -0
- package/dist/cli.js +131 -49
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +44 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,6 +40,8 @@ Built so an AI agent (or a CI job) can drive a debugger from a single shell comm
|
|
|
40
40
|
npm install -g @saptools/cf-inspector
|
|
41
41
|
# or
|
|
42
42
|
pnpm add @saptools/cf-inspector
|
|
43
|
+
|
|
44
|
+
cf-inspector --version
|
|
43
45
|
```
|
|
44
46
|
|
|
45
47
|
> [!NOTE]
|
|
@@ -94,6 +96,7 @@ cf-inspector snapshot --port 9229 \
|
|
|
94
96
|
| `--condition <expr>` | Only pause when this JS expression evaluates truthy in the paused frame. Errors in the condition are silently treated as `false` by V8 |
|
|
95
97
|
| `--hit-count <n>` | Skip the first N − 1 hits and only pause on the Nth (combines with `--condition` via logical AND) |
|
|
96
98
|
| `--capture <expr,…>` | Top-level comma-separated expressions to evaluate in the paused frame; nested commas inside objects, arrays, calls, or strings are preserved. Object results are materialized to JSON strings when serializable, with fallback to CDP descriptions for non-serializable values |
|
|
99
|
+
| `--setup-eval <expr>` | Repeatable, order-preserving global expression evaluated inside the inspected process before breakpoint setup. It can mutate runtime state, so use it only in controlled debug sessions |
|
|
97
100
|
| `--stack-depth <n>` | Walk this many call frames per hit (default: `1`, top frame only). When `> 1`, the result includes a `stack` array |
|
|
98
101
|
| `--stack-captures <expr,…>` | Expressions to evaluate on each call frame in the captured stack |
|
|
99
102
|
| `--timeout <seconds>` | How long to wait for the breakpoint to hit (default: `30`) |
|
|
@@ -203,6 +206,7 @@ Each event is a `WatchEvent`:
|
|
|
203
206
|
| `--port <number>` | Local port the inspector or tunnel listens on |
|
|
204
207
|
| `--bp <file:line>` | **Required.** Source location to capture on (repeatable) |
|
|
205
208
|
| `--capture <expr,…>` | Top-level comma-separated expressions to evaluate per hit |
|
|
209
|
+
| `--setup-eval <expr>` | Repeatable, order-preserving global expression evaluated inside the inspected process before breakpoint setup. It can mutate runtime state, so use it only in controlled debug sessions |
|
|
206
210
|
| `--condition <expr>` | Only emit hits where this expression evaluates truthy |
|
|
207
211
|
| `--hit-count <n>` | Start emitting once the line has been hit N or more times |
|
|
208
212
|
| `--remote-root <value>` | Path-mapping anchor (same DSL as `snapshot`) |
|
|
@@ -282,6 +286,8 @@ cf-inspector list-targets --port 9229
|
|
|
282
286
|
cf-inspector snapshot --port 9229 --target 1 --bp dist/worker.js:42
|
|
283
287
|
```
|
|
284
288
|
|
|
289
|
+
If `list-targets`, `attach`, or another command reports `ECONNREFUSED`, the local inspector or tunnel on that port is usually stale/closed. Restart the local Node inspector or tunnel and retry; for Cloud Foundry targets, prefer `--app/--region/--org/--space` so `cf-inspector` can open a fresh tunnel.
|
|
290
|
+
|
|
285
291
|
### 🔗 `cf-inspector attach`
|
|
286
292
|
|
|
287
293
|
Connect, fetch the runtime version, print it, disconnect. Useful as a smoke-test that the tunnel is healthy.
|
package/dist/cli.js
CHANGED
|
@@ -155,6 +155,9 @@ var init_wsTransport = __esm({
|
|
|
155
155
|
import process12 from "process";
|
|
156
156
|
|
|
157
157
|
// src/cli/program.ts
|
|
158
|
+
import { readFileSync } from "fs";
|
|
159
|
+
import { dirname, join } from "path";
|
|
160
|
+
import { fileURLToPath } from "url";
|
|
158
161
|
import { Command } from "commander";
|
|
159
162
|
|
|
160
163
|
// src/cli/commands/attach.ts
|
|
@@ -190,16 +193,39 @@ async function fetchJson(url, timeoutMs) {
|
|
|
190
193
|
);
|
|
191
194
|
});
|
|
192
195
|
req.on("error", (err) => {
|
|
193
|
-
reject(
|
|
194
|
-
err instanceof CfInspectorError ? err : new CfInspectorError(
|
|
195
|
-
"INSPECTOR_DISCOVERY_FAILED",
|
|
196
|
-
`Inspector discovery at ${url} failed: ${err.message}`
|
|
197
|
-
)
|
|
198
|
-
);
|
|
196
|
+
reject(err instanceof CfInspectorError ? err : formatDiscoveryRequestError(url, err));
|
|
199
197
|
});
|
|
200
198
|
req.end();
|
|
201
199
|
});
|
|
202
200
|
}
|
|
201
|
+
function isNodeSystemError(err) {
|
|
202
|
+
return err instanceof Error;
|
|
203
|
+
}
|
|
204
|
+
function isConnectionRefusedOrUnreachable(code) {
|
|
205
|
+
return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "ETIMEDOUT" || code === "EHOSTUNREACH" || code === "ENETUNREACH";
|
|
206
|
+
}
|
|
207
|
+
function formatEndpoint(url, err) {
|
|
208
|
+
if (typeof err.address === "string" && typeof err.port === "number") {
|
|
209
|
+
return `${err.address}:${err.port.toString()}`;
|
|
210
|
+
}
|
|
211
|
+
const parsed = new URL(url);
|
|
212
|
+
return parsed.host;
|
|
213
|
+
}
|
|
214
|
+
function formatDiscoveryRequestError(url, err) {
|
|
215
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
216
|
+
if (!isNodeSystemError(err) || !isConnectionRefusedOrUnreachable(err.code)) {
|
|
217
|
+
return new CfInspectorError(
|
|
218
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
219
|
+
`Inspector discovery at ${url} failed: ${detail}`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
const endpoint = formatEndpoint(url, err);
|
|
223
|
+
return new CfInspectorError(
|
|
224
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
225
|
+
`Cannot reach Node inspector discovery at ${url}. Nothing is listening on ${endpoint}, or the inspector tunnel is stale/closed. Restart the local inspector or tunnel and retry. If this port came from cf-debugger, stop the stale session and start a fresh tunnel, or run cf-inspector with --app/--region/--org/--space so it can open a tunnel.`,
|
|
226
|
+
detail
|
|
227
|
+
);
|
|
228
|
+
}
|
|
203
229
|
function parseJsonResponse(chunks) {
|
|
204
230
|
const text = Buffer.concat(chunks).toString("utf8");
|
|
205
231
|
return JSON.parse(text);
|
|
@@ -1124,6 +1150,20 @@ async function evaluateGlobal(session, expression) {
|
|
|
1124
1150
|
silent: true
|
|
1125
1151
|
});
|
|
1126
1152
|
}
|
|
1153
|
+
async function runSetupEvals(session, expressions) {
|
|
1154
|
+
for (const expression of expressions) {
|
|
1155
|
+
const result = await evaluateGlobal(session, expression);
|
|
1156
|
+
if (result.exceptionDetails !== void 0) {
|
|
1157
|
+
throw new CfInspectorError(
|
|
1158
|
+
"SETUP_EVAL_FAILED",
|
|
1159
|
+
exceptionDetailsMessage(result, "setup evaluation failed")
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
function exceptionDetailsMessage(result, fallback) {
|
|
1165
|
+
return typeof result.exceptionDetails?.exception?.description === "string" ? result.exceptionDetails.exception.description : typeof result.exceptionDetails?.text === "string" ? result.exceptionDetails.text : fallback;
|
|
1166
|
+
}
|
|
1127
1167
|
function listScripts(session) {
|
|
1128
1168
|
return [...session.scripts.values()];
|
|
1129
1169
|
}
|
|
@@ -2711,8 +2751,10 @@ function prepareSnapshotCommand(opts, target) {
|
|
|
2711
2751
|
const condition = opts.condition !== void 0 && opts.condition.trim().length > 0 ? opts.condition.trim() : void 0;
|
|
2712
2752
|
const hitCount = parsePositiveInt(opts.hitCount, "--hit-count");
|
|
2713
2753
|
const stackDepth = parsePositiveInt(opts.stackDepth, "--stack-depth");
|
|
2754
|
+
const setupEvals = parseSetupEvals(opts.setupEval);
|
|
2714
2755
|
return {
|
|
2715
2756
|
target,
|
|
2757
|
+
setupEvals,
|
|
2716
2758
|
breakpoints: opts.bp.map((spec) => parseBreakpointSpec(spec)),
|
|
2717
2759
|
captures: parseCaptureList(opts.capture),
|
|
2718
2760
|
remoteRoot: parseRemoteRoot(opts.remoteRoot),
|
|
@@ -2726,48 +2768,57 @@ function prepareSnapshotCommand(opts, target) {
|
|
|
2726
2768
|
}
|
|
2727
2769
|
async function runSnapshotCommand(command, opts, reportProgress) {
|
|
2728
2770
|
return await withSession(command.target, async (session) => {
|
|
2729
|
-
|
|
2730
|
-
reportProgress?.("Validating the breakpoint condition...");
|
|
2731
|
-
await validateExpression(session, command.condition);
|
|
2732
|
-
reportProgress?.("Breakpoint condition is valid.");
|
|
2733
|
-
}
|
|
2734
|
-
const breakpointCount = command.breakpoints.length;
|
|
2735
|
-
reportProgress?.(
|
|
2736
|
-
`Setting ${breakpointCount.toString()} ${breakpointCount === 1 ? "breakpoint" : "breakpoints"}...`
|
|
2737
|
-
);
|
|
2738
|
-
const handles = await setCommandBreakpoints(session, command);
|
|
2739
|
-
const resolvedCount = handles.reduce(
|
|
2740
|
-
(total, handle) => total + handle.resolvedLocations.length,
|
|
2741
|
-
0
|
|
2742
|
-
);
|
|
2743
|
-
reportProgress?.(
|
|
2744
|
-
`Breakpoint setup complete: ${resolvedCount.toString()} resolved ${resolvedCount === 1 ? "location" : "locations"}.`
|
|
2745
|
-
);
|
|
2746
|
-
warnOnUnboundBreakpoints(handles);
|
|
2747
|
-
reportProgress?.(
|
|
2748
|
-
`Waiting up to ${(command.timeoutMs / 1e3).toString()}s for a breakpoint hit...`
|
|
2749
|
-
);
|
|
2750
|
-
const pause = await waitForCommandPause(session, opts, handles, command.timeoutMs);
|
|
2751
|
-
const captureCount = command.captures.length;
|
|
2752
|
-
reportProgress?.(
|
|
2753
|
-
`Breakpoint hit; capturing ${captureCount.toString()} ${captureCount === 1 ? "expression" : "expressions"}...`
|
|
2754
|
-
);
|
|
2755
|
-
const pausedStartedAt = pause.receivedAtMs ?? performance4.now();
|
|
2756
|
-
const snapshot = await captureSnapshot(session, pause, {
|
|
2757
|
-
captures: command.captures,
|
|
2758
|
-
includeScopes: opts.includeScopes === true,
|
|
2759
|
-
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength },
|
|
2760
|
-
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
2761
|
-
stackCaptures: command.stackCaptures
|
|
2762
|
-
});
|
|
2763
|
-
if (opts.keepPaused === true) {
|
|
2764
|
-
reportProgress?.("Snapshot captured; leaving the target paused as requested.");
|
|
2765
|
-
return withPausedDuration(snapshot, null);
|
|
2766
|
-
}
|
|
2767
|
-
reportProgress?.("Snapshot captured; resuming the target...");
|
|
2768
|
-
return await resumeAfterSnapshot(session, snapshot, pausedStartedAt, reportProgress);
|
|
2771
|
+
return await runSnapshotOnSession(session, command, opts, reportProgress);
|
|
2769
2772
|
}, reportProgress);
|
|
2770
2773
|
}
|
|
2774
|
+
async function runSnapshotOnSession(session, command, opts, reportProgress) {
|
|
2775
|
+
if (command.setupEvals.length > 0) {
|
|
2776
|
+
const setupCount = command.setupEvals.length;
|
|
2777
|
+
reportProgress?.(`Running ${setupCount.toString()} setup ${setupCount === 1 ? "evaluation" : "evaluations"}...`);
|
|
2778
|
+
await runSetupEvals(session, command.setupEvals);
|
|
2779
|
+
reportProgress?.("Setup evaluation complete.");
|
|
2780
|
+
}
|
|
2781
|
+
if (command.condition !== void 0) {
|
|
2782
|
+
reportProgress?.("Validating the breakpoint condition...");
|
|
2783
|
+
await validateExpression(session, command.condition);
|
|
2784
|
+
reportProgress?.("Breakpoint condition is valid.");
|
|
2785
|
+
}
|
|
2786
|
+
const breakpointCount = command.breakpoints.length;
|
|
2787
|
+
reportProgress?.(
|
|
2788
|
+
`Setting ${breakpointCount.toString()} ${breakpointCount === 1 ? "breakpoint" : "breakpoints"}...`
|
|
2789
|
+
);
|
|
2790
|
+
const handles = await setCommandBreakpoints(session, command);
|
|
2791
|
+
const resolvedCount = handles.reduce(
|
|
2792
|
+
(total, handle) => total + handle.resolvedLocations.length,
|
|
2793
|
+
0
|
|
2794
|
+
);
|
|
2795
|
+
reportProgress?.(
|
|
2796
|
+
`Breakpoint setup complete: ${resolvedCount.toString()} resolved ${resolvedCount === 1 ? "location" : "locations"}.`
|
|
2797
|
+
);
|
|
2798
|
+
warnOnUnboundBreakpoints(handles);
|
|
2799
|
+
reportProgress?.(
|
|
2800
|
+
`Waiting up to ${(command.timeoutMs / 1e3).toString()}s for a breakpoint hit...`
|
|
2801
|
+
);
|
|
2802
|
+
const pause = await waitForCommandPause(session, opts, handles, command.timeoutMs);
|
|
2803
|
+
const captureCount = command.captures.length;
|
|
2804
|
+
reportProgress?.(
|
|
2805
|
+
`Breakpoint hit; capturing ${captureCount.toString()} ${captureCount === 1 ? "expression" : "expressions"}...`
|
|
2806
|
+
);
|
|
2807
|
+
const pausedStartedAt = pause.receivedAtMs ?? performance4.now();
|
|
2808
|
+
const snapshot = await captureSnapshot(session, pause, {
|
|
2809
|
+
captures: command.captures,
|
|
2810
|
+
includeScopes: opts.includeScopes === true,
|
|
2811
|
+
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength },
|
|
2812
|
+
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
2813
|
+
stackCaptures: command.stackCaptures
|
|
2814
|
+
});
|
|
2815
|
+
if (opts.keepPaused === true) {
|
|
2816
|
+
reportProgress?.("Snapshot captured; leaving the target paused as requested.");
|
|
2817
|
+
return withPausedDuration(snapshot, null);
|
|
2818
|
+
}
|
|
2819
|
+
reportProgress?.("Snapshot captured; resuming the target...");
|
|
2820
|
+
return await resumeAfterSnapshot(session, snapshot, pausedStartedAt, reportProgress);
|
|
2821
|
+
}
|
|
2771
2822
|
async function setCommandBreakpoints(session, command) {
|
|
2772
2823
|
return await Promise.all(
|
|
2773
2824
|
command.breakpoints.map(
|
|
@@ -2808,6 +2859,10 @@ async function resumeAfterSnapshot(session, snapshot, pausedStartedAt, reportPro
|
|
|
2808
2859
|
return withPausedDuration(snapshot, null);
|
|
2809
2860
|
}
|
|
2810
2861
|
}
|
|
2862
|
+
function parseSetupEvals(raw) {
|
|
2863
|
+
const values = Array.isArray(raw) ? raw : [];
|
|
2864
|
+
return values.filter((expr) => typeof expr === "string" && expr.trim().length > 0).map((expr) => expr.trim());
|
|
2865
|
+
}
|
|
2811
2866
|
|
|
2812
2867
|
// src/cli/commands/watch.ts
|
|
2813
2868
|
import { performance as performance5 } from "perf_hooks";
|
|
@@ -2841,8 +2896,10 @@ function prepareWatchCommand(opts, target) {
|
|
|
2841
2896
|
const hitCount = parsePositiveInt(opts.hitCount, "--hit-count");
|
|
2842
2897
|
const stackDepth = parsePositiveInt(opts.stackDepth, "--stack-depth");
|
|
2843
2898
|
const condition = opts.condition !== void 0 && opts.condition.trim().length > 0 ? opts.condition.trim() : void 0;
|
|
2899
|
+
const setupEvals = parseSetupEvals2(opts.setupEval);
|
|
2844
2900
|
return {
|
|
2845
2901
|
target,
|
|
2902
|
+
setupEvals,
|
|
2846
2903
|
breakpoints: opts.bp.map((spec) => parseBreakpointSpec(spec)),
|
|
2847
2904
|
captures: parseCaptureList(opts.capture),
|
|
2848
2905
|
remoteRoot: parseRemoteRoot(opts.remoteRoot),
|
|
@@ -2857,6 +2914,9 @@ function prepareWatchCommand(opts, target) {
|
|
|
2857
2914
|
};
|
|
2858
2915
|
}
|
|
2859
2916
|
async function runWatchLoop(session, command, opts, signal) {
|
|
2917
|
+
if (command.setupEvals.length > 0) {
|
|
2918
|
+
await runSetupEvals(session, command.setupEvals);
|
|
2919
|
+
}
|
|
2860
2920
|
if (command.condition !== void 0) {
|
|
2861
2921
|
await validateExpression(session, command.condition);
|
|
2862
2922
|
}
|
|
@@ -3030,6 +3090,10 @@ function writeWatchSummary(reason, emitted, json) {
|
|
|
3030
3090
|
`
|
|
3031
3091
|
);
|
|
3032
3092
|
}
|
|
3093
|
+
function parseSetupEvals2(raw) {
|
|
3094
|
+
const values = Array.isArray(raw) ? raw : [];
|
|
3095
|
+
return values.filter((expr) => typeof expr === "string" && expr.trim().length > 0).map((expr) => expr.trim());
|
|
3096
|
+
}
|
|
3033
3097
|
|
|
3034
3098
|
// src/cli/program.ts
|
|
3035
3099
|
function applyTargetOptions(cmd, options = {}) {
|
|
@@ -3040,9 +3104,27 @@ var collectStrings = (value, prev = []) => [
|
|
|
3040
3104
|
...prev,
|
|
3041
3105
|
value
|
|
3042
3106
|
];
|
|
3107
|
+
function readPackageVersion() {
|
|
3108
|
+
let current = dirname(fileURLToPath(import.meta.url));
|
|
3109
|
+
for (let depth = 0; depth < 4; depth += 1) {
|
|
3110
|
+
const candidate = join(current, "package.json");
|
|
3111
|
+
try {
|
|
3112
|
+
const parsed = JSON.parse(readFileSync(candidate, "utf8"));
|
|
3113
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
3114
|
+
const record = parsed;
|
|
3115
|
+
if (record["name"] === "@saptools/cf-inspector" && typeof record["version"] === "string") {
|
|
3116
|
+
return record["version"];
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
} catch {
|
|
3120
|
+
}
|
|
3121
|
+
current = dirname(current);
|
|
3122
|
+
}
|
|
3123
|
+
throw new Error("Unable to read @saptools/cf-inspector package version");
|
|
3124
|
+
}
|
|
3043
3125
|
async function main(argv) {
|
|
3044
3126
|
const program = new Command();
|
|
3045
|
-
program.name("cf-inspector").description("Drive a Node.js inspector from the command line \u2014 set breakpoints, capture snapshots, evaluate expressions");
|
|
3127
|
+
program.name("cf-inspector").version(readPackageVersion()).description("Drive a Node.js inspector from the command line \u2014 set breakpoints, capture snapshots, evaluate expressions");
|
|
3046
3128
|
registerSnapshot(program);
|
|
3047
3129
|
registerLog(program);
|
|
3048
3130
|
registerWatch(program);
|
|
@@ -3057,7 +3139,7 @@ function registerSnapshot(program) {
|
|
|
3057
3139
|
applyTargetOptions(
|
|
3058
3140
|
program.command("snapshot").description("Set a breakpoint, wait for it to hit, capture expressions, and resume"),
|
|
3059
3141
|
{ includeTimeout: false }
|
|
3060
|
-
).option("--bp <file:line>", "Breakpoint location (repeatable; first hit wins), e.g. src/handler.ts:42", collectStrings, []).option("--capture <expr,\u2026>", "Top-level comma-separated expressions to evaluate in the paused frame").option("--timeout <seconds>", "How long to wait for the breakpoint to hit (default: 30)").option("--max-value-length <chars>", "Maximum characters per captured value before truncation (default: 4096)").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--condition <expr>", "Only pause when this JS expression evaluates truthy in the paused frame").option("--hit-count <n>", "Only pause after the breakpoint has been hit N or more times").option("--stack-depth <n>", "Walk this many call frames when capturing (default: 1, only top frame)").option("--stack-captures <expr,\u2026>", "Expressions to evaluate on each call frame in the stack").option("--include-scopes", "Include expanded paused-frame scopes in the snapshot").option("--no-json", "Print a human-readable summary instead of JSON").option("--quiet", "Suppress progress messages on stderr").option("--keep-paused", "Skip Debugger.resume after capture; Node may resume when this CLI disconnects").option("--fail-on-unmatched-pause", "Fail immediately if the target pauses somewhere else").action(async (opts) => {
|
|
3142
|
+
).option("--bp <file:line>", "Breakpoint location (repeatable; first hit wins), e.g. src/handler.ts:42", collectStrings, []).option("--capture <expr,\u2026>", "Top-level comma-separated expressions to evaluate in the paused frame").option("--setup-eval <expr>", "Evaluate a global setup expression before breakpoint setup (repeatable)", collectStrings, []).option("--timeout <seconds>", "How long to wait for the breakpoint to hit (default: 30)").option("--max-value-length <chars>", "Maximum characters per captured value before truncation (default: 4096)").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--condition <expr>", "Only pause when this JS expression evaluates truthy in the paused frame").option("--hit-count <n>", "Only pause after the breakpoint has been hit N or more times").option("--stack-depth <n>", "Walk this many call frames when capturing (default: 1, only top frame)").option("--stack-captures <expr,\u2026>", "Expressions to evaluate on each call frame in the stack").option("--include-scopes", "Include expanded paused-frame scopes in the snapshot").option("--no-json", "Print a human-readable summary instead of JSON").option("--quiet", "Suppress progress messages on stderr").option("--keep-paused", "Skip Debugger.resume after capture; Node may resume when this CLI disconnects").option("--fail-on-unmatched-pause", "Fail immediately if the target pauses somewhere else").action(async (opts) => {
|
|
3061
3143
|
await handleSnapshot(opts);
|
|
3062
3144
|
});
|
|
3063
3145
|
}
|
|
@@ -3072,7 +3154,7 @@ function registerWatch(program) {
|
|
|
3072
3154
|
applyTargetOptions(
|
|
3073
3155
|
program.command("watch").description("Stream a snapshot per breakpoint hit (multi-shot watch); resume between hits"),
|
|
3074
3156
|
{ includeTimeout: false }
|
|
3075
|
-
).option("--bp <file:line>", "Breakpoint location (repeatable), e.g. src/handler.ts:42", collectStrings, []).option("--capture <expr,\u2026>", "Top-level comma-separated expressions to evaluate per hit").option("--condition <expr>", "Only emit hits where this JS expression evaluates truthy").option("--hit-count <n>", "Start emitting after the line has been hit N or more times").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--duration <seconds>", "Stop streaming after N seconds (default: run until SIGINT)").option("--max-events <n>", "Stop streaming after emitting N watch events").option("--timeout <seconds>", "How long to wait for the next hit before giving up (default: 30)").option("--max-value-length <chars>", "Maximum characters per captured value before truncation (default: 4096)").option("--stack-depth <n>", "Walk this many call frames per hit (default: 1)").option("--stack-captures <expr,\u2026>", "Expressions to evaluate on each call frame").option("--include-scopes", "Include expanded paused-frame scopes per hit").option("--no-json", "Print human-readable lines instead of JSON Lines").action(async (opts) => {
|
|
3157
|
+
).option("--bp <file:line>", "Breakpoint location (repeatable), e.g. src/handler.ts:42", collectStrings, []).option("--capture <expr,\u2026>", "Top-level comma-separated expressions to evaluate per hit").option("--setup-eval <expr>", "Evaluate a global setup expression before breakpoint setup (repeatable)", collectStrings, []).option("--condition <expr>", "Only emit hits where this JS expression evaluates truthy").option("--hit-count <n>", "Start emitting after the line has been hit N or more times").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--duration <seconds>", "Stop streaming after N seconds (default: run until SIGINT)").option("--max-events <n>", "Stop streaming after emitting N watch events").option("--timeout <seconds>", "How long to wait for the next hit before giving up (default: 30)").option("--max-value-length <chars>", "Maximum characters per captured value before truncation (default: 4096)").option("--stack-depth <n>", "Walk this many call frames per hit (default: 1)").option("--stack-captures <expr,\u2026>", "Expressions to evaluate on each call frame").option("--include-scopes", "Include expanded paused-frame scopes per hit").option("--no-json", "Print human-readable lines instead of JSON Lines").action(async (opts) => {
|
|
3076
3158
|
await handleWatch(opts);
|
|
3077
3159
|
});
|
|
3078
3160
|
}
|