@saptools/cf-inspector 0.3.19 → 0.4.1
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 +105 -23
- package/dist/cli.js +1515 -735
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +56 -5
- package/dist/index.js +446 -53
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/cli.d.ts +0 -2
package/README.md
CHANGED
|
@@ -20,8 +20,12 @@ Built so an AI agent (or a CI job) can drive a debugger from a single shell comm
|
|
|
20
20
|
|
|
21
21
|
- 🎯 **One-shot snapshot** — `cf-inspector snapshot --bp src/handler.ts:42` sets the breakpoint, waits for it to hit, captures requested expressions, auto-resumes, prints JSON, exits
|
|
22
22
|
- ✅ **Conditional breakpoints** — `--condition 'req.userId === "abc"'` only pauses when the predicate is truthy
|
|
23
|
+
- 🔢 **Hit-count breakpoints** — `--hit-count 5` skips the first N − 1 hits and pauses on the Nth, on every command (snapshot, log, watch)
|
|
23
24
|
- 🎭 **Multi-breakpoint** — repeat `--bp` to race several locations; first hit wins
|
|
24
|
-
-
|
|
25
|
+
- 🪜 **Stack capture** — `--stack-depth N --stack-captures 'this, args'` walks call frames and evaluates expressions per frame
|
|
26
|
+
- 🔁 **Watch streaming** — `cf-inspector watch --bp file:line --capture user.id --duration 30` re-captures on every hit and emits JSON Lines (the streaming counterpart of `snapshot`)
|
|
27
|
+
- 💥 **Exception breakpoints** — `cf-inspector exception --type uncaught --capture err.message` pauses on the next thrown error and materializes the exception value
|
|
28
|
+
- 📡 **Non-pausing logpoints** — `cf-inspector log --at file:line --expr 'JSON.stringify({…})'` streams JSON Lines as the line executes, **without ever pausing the inspectee** (safe for production traffic), with optional `--condition`, `--hit-count`, `--max-events`
|
|
25
29
|
- 🧠 **Agent-friendly** — JSON-by-default I/O, deterministic shape, bounded value previews for large debugger payloads
|
|
26
30
|
- 🧭 **Path mapping** — local `src/handler.ts:42` is matched against the remote URL via a `urlRegex`, with optional `--remote-root` literal or regex (same DSL as `cds-debug`)
|
|
27
31
|
- 🔁 **Composes with `cf-debugger`** — pass `--app/--region/--org/--space` and the tunnel is opened automatically; pass `--port` to attach to anything CDP-speaking
|
|
@@ -88,7 +92,10 @@ cf-inspector snapshot --port 9229 \
|
|
|
88
92
|
| `--port <number>` | Local port the inspector or tunnel listens on. **Required** unless `--app/--region/--org/--space` are all set |
|
|
89
93
|
| `--bp <file:line>` | **Required.** Source location to break at. Pass multiple times to race several locations — the first one to hit wins |
|
|
90
94
|
| `--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
|
+
| `--hit-count <n>` | Skip the first N − 1 hits and only pause on the Nth (combines with `--condition` via logical AND) |
|
|
91
96
|
| `--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 |
|
|
97
|
+
| `--stack-depth <n>` | Walk this many call frames per hit (default: `1`, top frame only). When `> 1`, the result includes a `stack` array |
|
|
98
|
+
| `--stack-captures <expr,…>` | Expressions to evaluate on each call frame in the captured stack |
|
|
92
99
|
| `--timeout <seconds>` | How long to wait for the breakpoint to hit (default: `30`) |
|
|
93
100
|
| `--max-value-length <chars>` | Maximum characters per captured value before truncation (default: `4096`) |
|
|
94
101
|
| `--remote-root <value>` | Optional path-mapping anchor: literal path or `regex:<pattern>` / `/pattern/flags` |
|
|
@@ -148,9 +155,95 @@ When the user expression throws, the event is emitted with `error` instead of `v
|
|
|
148
155
|
| `--at <file:line>` | **Required.** Source location to log at |
|
|
149
156
|
| `--expr <expression>` | **Required.** JS expression to evaluate at each hit (wrapped in try/catch on the inspectee side) |
|
|
150
157
|
| `--duration <seconds>` | Stop streaming after N seconds (default: run until SIGINT) |
|
|
158
|
+
| `--max-events <n>` | Stop streaming after emitting N log events. The trailer reports `stopped: "max-events"` |
|
|
159
|
+
| `--hit-count <n>` | Start emitting once the line has been hit N or more times |
|
|
160
|
+
| `--condition <expr>` | Only log when this JS expression evaluates truthy on the inspectee. Composes with `--hit-count` via logical AND |
|
|
151
161
|
| `--remote-root <value>` | Optional path-mapping anchor (same DSL as `snapshot`) |
|
|
152
162
|
| `--no-json` | Print human-readable lines instead of JSON Lines |
|
|
153
163
|
|
|
164
|
+
### 🔁 `cf-inspector watch`
|
|
165
|
+
|
|
166
|
+
Stream a snapshot per breakpoint hit. The inspectee is paused briefly while
|
|
167
|
+
captures are evaluated, then resumed automatically; output is JSON Lines on
|
|
168
|
+
stdout with a trailer on stderr (same shape as `log`).
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
cf-inspector watch --port 9229 \
|
|
172
|
+
--bp src/handler.ts:42 \
|
|
173
|
+
--capture 'user.id, payload' \
|
|
174
|
+
--condition 'user.id !== "system"' \
|
|
175
|
+
--duration 30 \
|
|
176
|
+
--max-events 50
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Each event is a `WatchEvent`:
|
|
180
|
+
|
|
181
|
+
```jsonc
|
|
182
|
+
{"ts":"2026-04-29T...","at":"file:///app/src/handler.ts:42","hit":1,"reason":"other","hitBreakpoints":["..."],"captures":[{"expression":"user.id","value":"\"alice\""}]}
|
|
183
|
+
{"ts":"2026-04-29T...","at":"file:///app/src/handler.ts:42","hit":2,"reason":"other","hitBreakpoints":["..."],"captures":[{"expression":"user.id","value":"\"bob\""}]}
|
|
184
|
+
// stderr trailer:
|
|
185
|
+
{"stopped":"max-events","emitted":50}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
| Flag | Description |
|
|
189
|
+
| --- | --- |
|
|
190
|
+
| `--port <number>` | Local port the inspector or tunnel listens on |
|
|
191
|
+
| `--bp <file:line>` | **Required.** Source location to capture on (repeatable) |
|
|
192
|
+
| `--capture <expr,…>` | Top-level comma-separated expressions to evaluate per hit |
|
|
193
|
+
| `--condition <expr>` | Only emit hits where this expression evaluates truthy |
|
|
194
|
+
| `--hit-count <n>` | Start emitting once the line has been hit N or more times |
|
|
195
|
+
| `--remote-root <value>` | Path-mapping anchor (same DSL as `snapshot`) |
|
|
196
|
+
| `--duration <seconds>` | Stop streaming after N seconds (default: until SIGINT) |
|
|
197
|
+
| `--max-events <n>` | Stop streaming after emitting N events |
|
|
198
|
+
| `--timeout <seconds>` | How long to wait for the next hit before giving up (default: `30`) |
|
|
199
|
+
| `--max-value-length <chars>` | Maximum characters per captured value before truncation |
|
|
200
|
+
| `--stack-depth <n>` | Walk this many call frames per hit (default: `1`) |
|
|
201
|
+
| `--stack-captures <expr,…>` | Expressions to evaluate on each call frame |
|
|
202
|
+
| `--include-scopes` | Include expanded paused-frame scopes per hit |
|
|
203
|
+
| `--no-json` | Print human-readable lines instead of JSON Lines |
|
|
204
|
+
|
|
205
|
+
### 💥 `cf-inspector exception`
|
|
206
|
+
|
|
207
|
+
Pause on a thrown exception, capture the exception value plus the paused
|
|
208
|
+
frame, then resume.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
cf-inspector exception --port 9229 \
|
|
212
|
+
--type uncaught \
|
|
213
|
+
--capture 'this' \
|
|
214
|
+
--stack-depth 4 \
|
|
215
|
+
--stack-captures 'arguments[0]' \
|
|
216
|
+
--timeout 30
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Result is a `SnapshotResult` with an extra `exception` field:
|
|
220
|
+
|
|
221
|
+
```jsonc
|
|
222
|
+
{
|
|
223
|
+
"reason": "exception",
|
|
224
|
+
"hitBreakpoints": [],
|
|
225
|
+
"capturedAt": "2026-04-29T...",
|
|
226
|
+
"pausedDurationMs": 0.5,
|
|
227
|
+
"topFrame": {"functionName": "validate", "url": "...", "line": 42, "column": 5},
|
|
228
|
+
"exception": {"value": "{\"message\":\"missing field\",\"name\":\"Error\"}", "type": "object", "description": "missing field"},
|
|
229
|
+
"captures": [],
|
|
230
|
+
"stack": [...]
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
| Flag | Description |
|
|
235
|
+
| --- | --- |
|
|
236
|
+
| `--type <state>` | Pause on which exceptions: `uncaught` (default), `caught`, or `all` |
|
|
237
|
+
| `--capture <expr,…>` | Top-level expressions to evaluate in the paused frame |
|
|
238
|
+
| `--stack-depth <n>` | Walk this many call frames (default: `1`) |
|
|
239
|
+
| `--stack-captures <expr,…>` | Expressions to evaluate on each frame |
|
|
240
|
+
| `--include-scopes` | Include paused-frame scopes |
|
|
241
|
+
| `--remote-root <value>` | Path-mapping anchor (only used if you also wire snapshot helpers) |
|
|
242
|
+
| `--timeout <seconds>` | How long to wait for an exception (default: `30`) |
|
|
243
|
+
| `--max-value-length <chars>` | Maximum characters per captured value before truncation |
|
|
244
|
+
| `--keep-paused` | Skip `Debugger.resume` after capture |
|
|
245
|
+
| `--no-json` | Print a human-readable summary instead of JSON |
|
|
246
|
+
|
|
154
247
|
### 🧮 `cf-inspector eval`
|
|
155
248
|
|
|
156
249
|
Evaluate one expression with `Runtime.evaluate` in the global scope and print the result. For paused-frame values, use `snapshot --capture` or the programmatic `evaluateOnFrame(...)` API.
|
|
@@ -216,16 +309,20 @@ console.log({ bp, snapshot, customValue });
|
|
|
216
309
|
| Export | Description |
|
|
217
310
|
| --- | --- |
|
|
218
311
|
| `connectInspector(options)` | Open a CDP WebSocket session against a port |
|
|
219
|
-
| `setBreakpoint(session, location)` | Set a breakpoint by file/line + optional remote root |
|
|
312
|
+
| `setBreakpoint(session, location)` | Set a breakpoint by file/line + optional remote root and `hitCount` |
|
|
220
313
|
| `removeBreakpoint(session, id)` | Remove a breakpoint by id |
|
|
221
|
-
| `
|
|
222
|
-
| `
|
|
314
|
+
| `setPauseOnExceptions(session, state)` | Configure exception pause state: `none / uncaught / caught / all` |
|
|
315
|
+
| `waitForPause(session, options)` | Resolve when the next `Debugger.paused` event fires; supports `pauseReasons` allow-list |
|
|
316
|
+
| `captureSnapshot(session, pause, options)` | Build a structured snapshot of the paused frame. Pass `includeScopes: true` to expand scopes, `stackDepth` + `stackCaptures` for multi-frame walks, or `maxValueLength` to override the default captured value limit |
|
|
317
|
+
| `captureException(session, pause, maxValueLength)` | Materialize the exception attached to a `Debugger.paused` event |
|
|
318
|
+
| `walkStack(session, frames, options)` | Walk a call stack and evaluate per-frame expressions |
|
|
223
319
|
| `evaluateOnFrame(session, frameId, expression)` | Evaluate in a paused frame |
|
|
224
320
|
| `evaluateGlobal(session, expression)` | Evaluate against the global Runtime |
|
|
225
321
|
| `listScripts(session)` | Return the scripts the V8 instance knows about |
|
|
226
322
|
| `resume(session)` | Resume execution |
|
|
227
|
-
| `streamLogpoint(session, options)` | Stream a non-pausing logpoint until duration / signal / transport-close |
|
|
228
|
-
| `buildLogpointCondition(sentinel, expression)` | Build the CDP `condition` string for a logpoint
|
|
323
|
+
| `streamLogpoint(session, options)` | Stream a non-pausing logpoint until duration / signal / max-events / transport-close |
|
|
324
|
+
| `buildLogpointCondition(sentinel, expression, options?)` | Build the CDP `condition` string for a logpoint with optional predicate / hit-count gates |
|
|
325
|
+
| `buildHitCountedCondition(hitCount, key, userCondition?)` | Build the hit-count gate used by `setBreakpoint({ hitCount })` |
|
|
229
326
|
| `parseRemoteRoot(value)` | Parse a literal/regex remote-root setting |
|
|
230
327
|
| `buildBreakpointUrlRegex(input)` | Build a CDP `urlRegex` for a file path |
|
|
231
328
|
| `CfInspectorError` | Rich error class with typed `code` |
|
|
@@ -241,6 +338,8 @@ console.log({ bp, snapshot, customValue });
|
|
|
241
338
|
| `INVALID_BREAKPOINT` | `--bp` / `--at` is not in `file:line` form, or line is not a positive integer |
|
|
242
339
|
| `INVALID_REMOTE_ROOT` | `--remote-root` regex did not compile |
|
|
243
340
|
| `INVALID_EXPRESSION` | `--condition` or `--expr` failed to parse on V8 (`Runtime.compileScript` reported a SyntaxError) — fast-fail before the breakpoint is set |
|
|
341
|
+
| `INVALID_HIT_COUNT` | `--hit-count` is not a positive integer |
|
|
342
|
+
| `INVALID_PAUSE_TYPE` | `cf-inspector exception --type` is not one of `uncaught / caught / all` |
|
|
244
343
|
| `BREAKPOINT_DID_NOT_BIND` | Reserved: a breakpoint resolved to no scripts. Currently surfaced as a stderr warning only — see `BreakpointHandle.resolvedLocations` for programmatic detection |
|
|
245
344
|
| `INSPECTOR_DISCOVERY_FAILED` | `/json/list` did not return a usable WebSocket URL |
|
|
246
345
|
| `INSPECTOR_CONNECTION_FAILED` | WebSocket handshake failed, or the connection closed mid-request |
|
|
@@ -295,23 +394,6 @@ cf-inspector snapshot \
|
|
|
295
394
|
|
|
296
395
|
---
|
|
297
396
|
|
|
298
|
-
## 🛠️ Development
|
|
299
|
-
|
|
300
|
-
From the monorepo root:
|
|
301
|
-
|
|
302
|
-
```bash
|
|
303
|
-
pnpm install
|
|
304
|
-
pnpm --filter @saptools/cf-inspector build
|
|
305
|
-
pnpm --filter @saptools/cf-inspector typecheck
|
|
306
|
-
pnpm --filter @saptools/cf-inspector lint
|
|
307
|
-
pnpm --filter @saptools/cf-inspector test:unit
|
|
308
|
-
pnpm --filter @saptools/cf-inspector test:e2e
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
The e2e suite is fully self-contained: it spawns a small Node fixture under `--inspect=0`, drives the CLI against it, and asserts the JSON output. No CF / live network required.
|
|
312
|
-
|
|
313
|
-
---
|
|
314
|
-
|
|
315
397
|
## 🌐 Related
|
|
316
398
|
|
|
317
399
|
- 🐛 [`@saptools/cf-debugger`](https://www.npmjs.com/package/@saptools/cf-debugger) — opens the SSH inspector tunnel
|