npm.io
0.9.3 • Published 1 week agoCLI

@saptools/cf-inspector

Licence
MIT
Version
0.9.3
Deps
3
Size
1.1 MB
Vulns
0
Weekly
0
Stars
3

@saptools/cf-inspector

Set breakpoints, capture variable snapshots, and evaluate expressions on a remote Node.js process — over the Chrome DevTools Protocol, no IDE required.

Built so an AI agent (or a CI job) can drive a debugger from a single shell command. Pairs with @saptools/cf-debugger when the target lives behind a Cloud Foundry SSH tunnel.

npm version license node

InstallQuick StartCLIHow it works


Debug-session safety: only one local cf-inspector process may actively debug a given target at a time. Concurrent CDP debugger clients can receive the same pause and race Debugger.resume, disrupting the target and real application traffic. A second local invocation now fails before connecting with TARGET_ALREADY_DEBUGGED; wait for the named owner process to finish. Locks left by dead processes are reclaimed automatically. Debuggers running on another machine or outside cf-inspector cannot be detected by this local guard and must still be coordinated operationally.

Features

  • One-shot snapshotcf-inspector snapshot --bp src/handler.ts:42 sets the breakpoint, waits for it to hit, captures requested expressions, auto-resumes, prints JSON, exits
  • Conditional breakpoints--condition 'req.userId === "abc"' only pauses when the predicate is truthy
  • Hit-count breakpoints--hit-count 5 skips the first N − 1 hits and pauses on the Nth, on every command (snapshot, log, watch)
  • Multi-breakpoint — repeat --bp to race several locations; first hit wins
  • Stack capture--stack-depth N --stack-captures 'this, args' walks call frames and evaluates expressions per frame
  • Watch streamingcf-inspector watch --bp file:line --capture user.id --duration 30 re-captures on every hit and emits JSON Lines (the streaming counterpart of snapshot)
  • Exception breakpointscf-inspector exception --type uncaught --capture err.message pauses on the next thrown error and materializes the exception value
  • Non-pausing logpointscf-inspector log --at file:line --expr 'JSON.stringify({…})' streams JSON Lines as the line executes without pausing the inspectee, with optional --condition, --hit-count, and --max-events
  • Read-only capture guard — snapshot, watch, and exception captures use V8's side-effect analysis by default; --allow-mutation is an explicit escape hatch
  • Automatic worker fan-out — snapshot, watch, exception, and log attach to the main isolate plus every current or newly-spawned NodeWorker; explicit selectors remain available for pinning
  • Machine-readable readiness — opt into a versioned breakpoint-armed stderr event before triggering external traffic; no prose matching or guessed delay required
  • Isolate-aware script listinglist-scripts aggregates the main isolate and every current worker, tagging every script with its isolate
  • Agent-friendly — JSON-by-default I/O, deterministic shapes, and explicit truncated/originalLength/omittedCount metadata for bounded values
  • 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)
  • Composes with cf-debugger — pass --app/--region/--org/--space and the tunnel is opened automatically; pass --port to attach to anything CDP-speaking
  • Tiny dependency footprint@saptools/cf-debugger + commander + ws, with no heavy CDP framework
  • Typed API — every CLI command has a programmatic equivalent with full TypeScript definitions

Install

npm install -g @saptools/cf-inspector
# or
pnpm add @saptools/cf-inspector

cf-inspector --version

Requires Node.js ≥ 20. Cloud Foundry support uses @saptools/cf-debugger, which is installed automatically as a normal runtime dependency.


Updates

Every command first checks npm for a newer @saptools/cf-inspector (at most once an hour, one small request with a 2-second timeout) and, when one exists, installs that exact version with the package manager that owns the running binary and re-runs the command you typed on the new version. Both steps are announced on stderr; nothing is printed when the install is already current:

cf-inspector: updating 0.9.0 -> 0.10.0 ...
cf-inspector: updated to 0.10.0; re-running the command

If the install cannot complete, one stderr line gives the manual command and the command runs on the installed version; that version is not retried for a day. cf-inspector self-update forces the check and install now; cf-inspector self-update --check only reports.

Control Effect
SAPTOOLS_AUTO_UPDATE=on|notify|off on (default) installs and re-runs; notify prints the manual command once per version; off never checks. Applies to every @saptools CLI.
CF_INSPECTOR_AUTO_UPDATE same values, this CLI only; wins over the global variable
SAPTOOLS_UPDATE_INTERVAL_MINUTES minutes between checks (default 60; 0 checks on every run)
SAPTOOLS_NPM_REGISTRY registry to check and install from (default: npm's configured registry, then npmjs)
SAPTOOLS_UPDATE_DEBUG=1 explain on stderr why nothing happened

The updater switches itself off in CI (CI set), under NODE_ENV=test or NO_UPDATE_NOTIFIER, when the binary runs from a source checkout, an npm link or an npx cache, and inside the re-run itself. It never writes to stdout, never asks for input, never uses sudo, and never moves onto a prerelease. Its state lives in ~/.saptools/updates/.

Quick Start

Cloud Foundry app (auto-tunnel)
export SAP_EMAIL=...
export SAP_PASSWORD=...

cf-inspector snapshot \
  --region eu10 --org my-org --space dev --app my-srv \
  --bp src/handler.ts:42 \
  --remote-root 'regex:^/(home/vcap/app|example-root-.*)

This command internally calls @saptools/cf-debugger to open the SSH tunnel, runs the snapshot through it, and tears the tunnel down on exit.

Cloud Foundry targeting is deliberately deterministic: --app requires --region, --org, and --space. The CLI never inherits missing selectors from ambient cf target state. Use --api-endpoint only when the selected region needs an explicit endpoint override.

Worker behavior

When no isolate selector is passed, snapshot, watch, exception, log, and check-breakpoint attach to the main isolate and every NodeWorker under the selected raw target. Breakpoints are mirrored to workers that attach later, the first matching pause wins, and JSON/human output identifies the winning isolate as either {"kind":"main"} or {"kind":"worker","workerId":"…"}. Losing isolates that also paused are resumed before the command continues or exits.

Use --worker-id <id> to pin a stable live worker ID from list-targets, --worker <index> for the legacy positional selector, --target <index> to pin a raw inspector target, or --main-only to deliberately ignore workers. Explicit selectors preserve single-isolate behavior.

Automation readiness contract

Commands that arm debugger behavior before waiting (snapshot, watch, exception, and log) accept --ready-event. The flag writes exactly one compact JSON object to stderr after every session present during initial arming has completed its CDP setup and immediately before the command waits or streams:

{"event":"breakpoint-armed","schemaVersion":1,"command":"snapshot","sessions":3,"resolvedLocations":2,"timeoutMs":30000}

This is the supported synchronization contract for callers that must start the CLI before firing an HTTP request, queue job, or other external trigger. Parse the JSON line and require event === "breakpoint-armed" plus schemaVersion === 1, then confirm command matches the invocation; do not poll prose such as Waiting up to.

Field Contract
event Always "breakpoint-armed"
schemaVersion Event schema version; currently 1
command snapshot, watch, exception, or log
sessions Number of main/worker sessions whose initial arming completed before emission
resolvedLocations Total locations V8 had already resolved, or null for exception; 0 can still represent an accepted URL breakpoint that may resolve when a script loads
timeoutMs Snapshot/exception wait or watch per-hit timeout; null for log

Workers attaching after this one-time event are still armed dynamically, but are not included in its sessions or resolvedLocations totals. If initial arming fails or the command is cancelled first, no readiness event is emitted. With log --ready-event, matching events received while the remaining initial sessions are still arming are neither emitted nor counted, so the log stream cannot precede its readiness marker. Without --ready-event, stderr/stdout behavior is unchanged. An explicitly requested event is still emitted with snapshot --quiet.


CLI

cf-inspector snapshot

Set one or more breakpoints, wait for any of them to hit, capture frame metadata and requested expressions, auto-resume, exit.

# Conditional snapshot — only pauses for the user we care about
cf-inspector snapshot --port 9229 \
  --bp src/handler.ts:42 \
  --condition 'req.userId === "abc"' \
  --capture 'req.body'

# Multi-breakpoint — first hit wins (useful when you don't know which path is taken)
cf-inspector snapshot --port 9229 \
  --bp src/auth.ts:120 \
  --bp src/auth.ts:155 \
  --bp src/auth.ts:180 \
  --capture 'req.url, this.user'
Flag Description
--port <number> Local port the inspector or tunnel listens on. Required unless --app/--region/--org/--space are all set
--region/--org/--space/--app Explicit Cloud Foundry target. All four are required when --port is omitted; ambient cf target is never consulted
--api-endpoint <url> Override the API endpoint resolved from --region
--target <index> Raw /json/list target index (default: 0)
--worker <index> Nested NodeWorker index reported under the selected raw target by list-targets
--worker-id <id> Stable live NodeWorker ID reported by list-targets
--main-only Attach only to the main isolate and ignore NodeWorkers
--bp <file:line> Required. Source location to break at. Pass multiple times to race several locations — the first one to hit wins
--condition <expr> Native breakpoint condition. It is compile-checked before arming; mutation-shaped conditions require --allow-mutation because CDP provides no side-effect guard for native conditions
--hit-count <n> Skip the first N − 1 hits and only pause on the Nth (combines with --condition via logical AND)
--capture <expr,…> Top-level comma-separated expressions evaluated in the paused frame under V8's side-effect guard. Nested commas inside objects, arrays, calls, or strings are preserved. Objects are materialized to JSON strings when serializable
--setup-eval <expr> Repeatable, order-preserving global expression evaluated before breakpoint setup. It is mutation-capable and only receives an advisory warning
--stack-depth <n> Walk this many call frames per hit (default: 1, top frame only). When > 1, the result includes a stack array
--stack-captures <expr,…> Expressions evaluated on each captured call frame under the same side-effect guard as --capture
--allow-mutation Disable V8's capture side-effect guard and explicitly allow mutation-shaped native conditions. Heuristic matches are annotated with mutationRisk: true
--timeout <seconds> How long to wait for the breakpoint to hit (default: 30)
--max-value-length <chars> Maximum characters per captured value before truncation (one-shot default: 131072). Explicit values are honored exactly
--remote-root <value> Optional path-mapping anchor: literal path or regex:<pattern> / /pattern/flags
--include-scopes Include expanded paused-frame scopes under topFrame.scopes. Omitted by default to keep targeted captures concise
--no-json Print a human-readable summary instead of JSON
--quiet Suppress snapshot progress messages on stderr
--ready-event Emit the versioned breakpoint-armed JSON event on stderr after every current isolate is armed
--keep-paused Skip Debugger.resume after capture
--fail-on-unmatched-pause Fail immediately if the target pauses somewhere else instead of waiting cooperatively

Snapshot progress is printed to stderr by default, including Cloud Foundry login/tunnel setup, inspector connection, breakpoint binding, the breakpoint wait, capture, resume, and cleanup phases. The final JSON document remains the only content written to stdout, so piping it to jq or another parser stays safe. Pass --quiet to suppress these progress lines; warnings and errors still use stderr.

Snapshot JSON includes frame metadata and captures by default. topFrame.scopes is only present with --include-scopes because scope objects can be large and drown out targeted captures. Values are raw debugger values, so be careful when sharing logs.

Capture expressions are read-only by default. cf-inspector sends throwOnSideEffect: true to V8 for both --capture and --stack-captures. Assignments, mutating methods such as push, and calls V8 cannot prove pure are returned as a blocked capture with blocked: true, mutationRisk: true, and a MUTATION_NOT_ALLOWED error. Pass --allow-mutation only when changing the live inspectee is intentional. The opt-in disables the V8 guard and adds mutationRisk: true when the advisory syntax scan recognizes a likely mutation; arbitrary function calls can still mutate without being recognized.

--condition is different: V8 executes it internally as a native breakpoint condition, where CDP has no throwOnSideEffect option. Mutation-shaped native conditions are rejected unless --allow-mutation is present. --setup-eval and the standalone eval command remain mutation-capable by design and emit advisory warnings for recognizable mutation syntax.

A blocked capture remains a normal additive CapturedExpression, so one unsafe expression does not corrupt the rest of the snapshot:

{"expression":"items.push(1)","error":"MUTATION_NOT_ALLOWED: V8 blocked the capture expression ...","mutationRisk":true,"blocked":true}

With --allow-mutation, recognized mutation syntax runs and the result carries "mutationRisk": true; ordinary reads do not gain the field.

Truncation contract

JSON truncation is always out-of-band. A text value longer than the effective limit is cut to exactly that many JavaScript characters and gains "truncated": true plus its full "originalLength"; no ellipsis is appended to the JSON value. These fields are absent when no cut occurs. Human output may add a visual ellipsis.

Expanded objects and scopes also report bounded structural capture. The VariableSnapshot, ScopeSnapshot, or FrameSnapshot whose properties, variables, or scopes were cut gains truncated: true and the exact direct omittedCount. A serialized object capture propagates the aggregate known omission count to its CapturedExpression. ExceptionSnapshot additionally uses valueOriginalLength and descriptionOriginalLength so consumers can identify which field was cut; its compatibility originalLength is the larger reported field length.

One-shot snapshot and exception commands default to 131072 characters. Repeated watch and log events default to 4096. All four accept --max-value-length, and an explicit limit is applied exactly.

{"expression":"largeText","value":"exactly-N-characters","type":"string","truncated":true,"originalLength":250000}

pausedDurationMs measures the client-observed time from receiving the matching pause event until Debugger.resume completes. With --keep-paused, it is null because resume is intentionally skipped.

If the target pauses somewhere else first, for example another debugger's breakpoint or a debugger; statement, snapshot does not resume it by default. It warns once, waits for Debugger.resumed, then continues waiting for its own breakpoint within the remaining timeout. Use --fail-on-unmatched-pause when a strict immediate error is preferred.

For Cloud Foundry targets, replace --port with --region/--org/--space/--app. Cloud Foundry commands and tunnel readiness allow up to 180 seconds by default. For commands without their own wait semantics, --timeout <seconds> controls CF tunnel readiness. For breakpoint and exception commands, --timeout is reserved for the command wait and tunnel readiness keeps the 180-second default.

cf-inspector log

Set a non-pausing logpoint and stream the evaluated expression each time the line executes. The inspectee does not pause, but the expression and condition still execute against live state and can mutate it.

# Stream user IDs hitting handler.ts:42 for 30 seconds
cf-inspector log \
  --port 9229 \
  --at src/handler.ts:42 \
  --expr 'JSON.stringify({ user: req.user, body: req.body })' \
  --duration 30

Output is JSON Lines on stdout (one event per line) plus a summary trailer on stderr:

{"ts":"2026-04-29T...","at":"src/handler.ts:42","value":"{\"user\":\"alice\",\"body\":{}}"}
{"ts":"2026-04-29T...","at":"src/handler.ts:42","value":"{\"user\":\"bob\",\"body\":{}}"}
// stderr:
{"stopped":"duration","emitted":2}

When the user expression throws, the event is emitted with error instead of value so the stream never silently gaps:

{"ts":"…","at":"src/handler.ts:42","error":"Cannot read properties of undefined (reading 'user')"}
Flag Description
--port <number> Local port the inspector or tunnel listens on. Required unless --app/--region/--org/--space are all set
--target <index> / --worker <index> / --worker-id <id> Pin one raw target or worker instead of automatic fan-out
--main-only Ignore workers and attach only to the main isolate
--at <file:line> Required. Source location to log at
--expr <expression> Required. JavaScript expression evaluated at each hit, wrapped in try/catch on the inspectee side. It is mutation-capable; recognizable risks produce a warning
--duration <seconds> Stop streaming after N seconds (default: run until SIGINT)
--max-events <n> Stop streaming after emitting N log events. The trailer reports stopped: "max-events"
--hit-count <n> Start emitting once the line has been hit N or more times
--condition <expr> Mutation-capable native condition evaluated on the inspectee. Recognizable risks produce a warning. Composes with --hit-count via logical AND
--max-value-length <chars> Maximum characters per log value (streaming default: 4096). Truncated events include truncated and originalLength
--remote-root <value> Optional path-mapping anchor (same DSL as snapshot)
--no-json Print human-readable lines instead of JSON Lines
--ready-event Emit the versioned breakpoint-armed JSON event on stderr after every current isolate is armed

Native logpoint expressions and conditions have no V8 side-effect gate. The CLI warns when its best-effort syntax scan recognizes assignments or common mutating calls, but that scan cannot prove an arbitrary function is pure. Treat logpoint expressions as executable live code.

cf-inspector watch

Stream a snapshot per breakpoint hit. The inspectee is paused briefly while captures are evaluated, then resumed automatically; output is JSON Lines on stdout with a trailer on stderr (same shape as log).

cf-inspector watch --port 9229 \
  --bp src/handler.ts:42 \
  --capture 'user.id, payload' \
  --condition 'user.id !== "system"' \
  --duration 30 \
  --max-events 50

Each event is a WatchEvent:

{"ts":"2026-04-29T...","at":"file:///app/src/handler.ts:42","hit":1,"reason":"other","hitBreakpoints":["..."],"captures":[{"expression":"user.id","value":"\"alice\""}]}
{"ts":"2026-04-29T...","at":"file:///app/src/handler.ts:42","hit":2,"reason":"other","hitBreakpoints":["..."],"captures":[{"expression":"user.id","value":"\"bob\""}]}
// stderr trailer:
{"stopped":"max-events","emitted":50}
Flag Description
--port <number> Local port the inspector or tunnel listens on. Otherwise pass all explicit Cloud Foundry selectors
--target <index> / --worker <index> / --worker-id <id> Pin one raw target or worker instead of automatic fan-out
--main-only Ignore workers and attach only to the main isolate
--bp <file:line> Required. Source location to capture on (repeatable)
--capture <expr,…> Top-level comma-separated expressions evaluated per hit under V8's side-effect guard
--setup-eval <expr> Repeatable mutation-capable global expression evaluated before breakpoint setup; recognizable risks produce a warning
--condition <expr> Native condition; mutation-shaped conditions require --allow-mutation
--hit-count <n> Start emitting once the line has been hit N or more times
--remote-root <value> Path-mapping anchor (same DSL as snapshot)
--duration <seconds> Stop streaming after N seconds (default: until SIGINT)
--max-events <n> Stop streaming after emitting N events
--timeout <seconds> How long to wait for the next hit before giving up (default: 30)
--max-value-length <chars> Maximum characters per captured value (streaming default: 4096)
--stack-depth <n> Walk this many call frames per hit (default: 1)
--stack-captures <expr,…> Expressions evaluated on each call frame under the capture side-effect guard
--allow-mutation Disable the capture side-effect guard and explicitly allow mutation-shaped native conditions
--include-scopes Include expanded paused-frame scopes per hit
--no-json Print human-readable lines instead of JSON Lines
--ready-event Emit the versioned breakpoint-armed JSON event on stderr after every current isolate is armed
cf-inspector exception

Pause on a thrown exception, capture the exception value plus the paused frame, then resume.

cf-inspector exception --port 9229 \
  --type uncaught \
  --capture 'this' \
  --stack-depth 4 \
  --stack-captures 'arguments[0]' \
  --timeout 30

Result is a SnapshotResult with an extra exception field:

{
  "reason": "exception",
  "hitBreakpoints": [],
  "capturedAt": "2026-04-29T...",
  "pausedDurationMs": 0.5,
  "topFrame": {"functionName": "validate", "url": "...", "line": 42, "column": 5},
  "exception": {"value": "{\"message\":\"missing field\",\"name\":\"Error\"}", "type": "object", "description": "missing field"},
  "captures": [],
  "stack": [...]
}
Flag Description
--port or explicit --region/--org/--space/--app Select the local inspector or deterministic Cloud Foundry target
--target <index> / --worker <index> / --worker-id <id> Pin one raw target or worker instead of automatic fan-out
--main-only Ignore workers and attach only to the main isolate
--type <state> Pause on which exceptions: uncaught (default), caught, or all
--capture <expr,…> Top-level expressions evaluated in the paused frame under V8's side-effect guard
--stack-depth <n> Walk this many call frames (default: 1)
--stack-captures <expr,…> Expressions evaluated on each frame under V8's side-effect guard
--allow-mutation Disable the capture side-effect guard; heuristic matches gain mutationRisk: true
--include-scopes Include paused-frame scopes
--remote-root <value> Path-mapping anchor (only used if you also wire snapshot helpers)
--timeout <seconds> How long to wait for an exception (default: 30)
--max-value-length <chars> Maximum characters per captured value (one-shot default: 131072)
--keep-paused Skip Debugger.resume after capture
--no-json Print a human-readable summary instead of JSON
--ready-event Emit the versioned breakpoint-armed JSON event on stderr after pause-on-exception is active in every current isolate
cf-inspector eval

Evaluate one expression with Runtime.evaluate in the selected isolate's global scope and print the result. eval is intentionally mutation-capable and has no side-effect gate; recognizable mutation syntax emits an advisory warning to stderr. For read-only paused-frame values, use snapshot --capture or call the programmatic evaluateOnFrame(..., { throwOnSideEffect: true }) API. Plain evaluateOnFrame(...) remains unrestricted by default for backward compatibility.

cf-inspector eval --port 9229 --expr 'process.uptime()'
cf-inspector list-scripts

Print every script known by the main isolate and each currently attached NodeWorker (useful for debugging path-mapping issues). Each JSON entry carries isolate: {"kind":"main"} or isolate: {"kind":"worker","workerId":"…"}, so duplicate per-isolate scriptId values remain unambiguous. Add --filter <pattern> to narrow noisy script lists with a literal/wildcard pattern; | separates alternatives and .* / .+ match variable text.

cf-inspector list-scripts --port 9229 --filter 'dist/.+\.js'

With --no-json, rows are scriptId<TAB>url<TAB>isolate, where isolate is main or worker:<workerId>. --main-only, --worker-id, --worker, and --target retain their normal narrowing behavior.

cf-inspector list-targets

Print raw /json/list inspector targets with stable index values, likely worker labels, and the total target/worker counts on stderr. For each raw target, the command also probes Node's NodeWorker CDP domain and lists live nested workers with their own indexes.

cf-inspector list-targets --port 9229
cf-inspector snapshot --port 9229 --bp dist/worker.js:42
cf-inspector snapshot --port 9229 --worker-id 1 --bp dist/worker.js:42
# If a runtime publishes a worker as another raw /json/list target instead:
cf-inspector snapshot --port 9229 --target 1 --bp dist/worker.js:42

JSON output nests workers beneath their raw target:

[
  {
    "index": 0,
    "description": "node.js instance",
    "id": "target-id",
    "type": "node",
    "title": "app.mjs",
    "url": "file:///app/app.mjs",
    "webSocketDebuggerUrl": "ws://127.0.0.1:9229/target-id",
    "likelyWorker": false,
    "workerDiscoverySupported": true,
    "workers": [
      {"index": 0, "workerId": "1", "type": "worker", "title": "jobs", "url": "file:///app/worker.mjs"}
    ]
  }
]

--target selects a complete raw inspector endpoint. --worker-id selects a live nested worker by its stable ID, while --worker retains positional-index selection for compatibility. Modern Node.js 20–25 verification found workers on the NodeWorker path, including workers already alive before post-hoc SIGUSR1 inspector activation; the raw-target selector remains supported for runtimes that publish that shape.

When no selector is passed, breakpoint-oriented commands attach to raw target 0 and automatically fan out across its main isolate and nested workers. If only one raw target and no workers are visible, list-targets explains that the worker may have exited, the runtime may not expose NodeWorker discovery, or a separate worker port may be unreachable through the single Cloud Foundry tunnel. Rerun the command while the worker is alive before pinning its workerId.

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, pass the complete --region/--org/--space/--app selector so cf-inspector can open a fresh tunnel.

cf-inspector check-breakpoint

Check whether a file:line can accept a breakpoint before arming one. The command uses the same path mapping as --bp and checks every currently attached isolate by default.

cf-inspector check-breakpoint --port 9229 --bp dist/handler.js:42

status: "script-not-loaded" means no loaded script matched the file/path mapping; use list-scripts, adjust --remote-root, or trigger lazy loading. status: "unbreakable" means the script is loaded but that exact line has no V8 break location; choose a neighboring executable line. status: "breakable" includes the concrete script IDs, isolate identities, lines, and columns.

cf-inspector attach

Connect, fetch the runtime version, print it, disconnect. Useful as a smoke-test that the tunnel is healthy.

cf-inspector attach --port 9229

attach checks the port-level /json/version endpoint, so raw-target and worker selectors do not apply to this smoke test.

Exact CDP tracing APIs

The library also exports lower-level, validated primitives for tools that need to plan and step an exact loaded function instead of using a URL-regex breakpoint:

const scripts = listScripts(session);
const source = await getScriptSource(session, scripts[0].scriptId);
const locations = await getPossibleBreakpoints(session, {
  start: { scriptId: scripts[0].scriptId, lineNumber: 10 },
  restrictToFunction: true,
});
const breakpoint = await setBreakpointAtLocation(session, { location: locations[0] });
const pause = await waitForPause(session, {
  timeoutMs: 30_000,
  breakpointIds: [breakpoint.breakpointId],
  signal: abortController.signal,
});
await stepOver(session);
await releaseObject(session, objectId);

ScriptLocation and BreakLocation use CDP-native zero-based line and column numbers. Exact breakpoint setup returns both the requested and actual location, then fails closed and removes the breakpoint if V8 resolves a different script, line, or column. An omitted column means CDP column zero. RemoteObjectInfo.completeness is truncated for logical values stored in internal slots (including maps, sets, promises, and dates) and unavailable for proxies; it is omitted for ordinary objects. waitForPause accepts an AbortSignal and cleans its event listener and timer on success, timeout, abort, or session close. The same layer exports stepInto, stepOver, stepOut, releaseObject, and releaseObjectGroup for bounded controllers.

Programmatic Cloud Foundry tunnels can select the same process instance and Node PID used for both the remote signal and SSH forwarding:

const tunnel = await openCfTunnel({
  region: "eu10",
  org: "my-org",
  space: "dev",
  app: "orders-srv",
  process: "worker",
  instance: 2,
  nodePid: 4312,
});

openCfTunnel retains backward-compatible reuse when cf-debugger reports a healthy local port for an existing session. Controllers that must prove they own and can dispose the tunnel should use openOwnedCfTunnel; it propagates SESSION_ALREADY_RUNNING instead of parsing or borrowing a pre-existing session.


How it works

┌──────────────────────┐   1. GET http://127.0.0.1:<port>/json/list
│ cf-inspector         │   2. Open the selected raw WebSocket target
│  snapshot --bp X:Y   │ ─►3. Auto-attach to current and future NodeWorker sub-sessions
└──────────────────────┘   4. Debugger.enable + Runtime.enable
            │              5. Debugger.setBreakpointByUrl({ urlRegex, lineNumber: Y - 1 })
            ▼              6. Wait for `Debugger.paused`
   JSON snapshot           7. Debugger.evaluateOnCallFrame({ throwOnSideEffect: true, ... })
                           8. Runtime.getProperties(...) when object/scopes are expanded
                           9. Debugger.resume   (unless --keep-paused)

Path mapping uses CDP's first-class urlRegex:

--remote-root Resulting urlRegex (line 42 of src/handler.ts)
omitted `(?:^
/home/vcap/app (literal) ^file:///home/vcap/app/src/handler\.(?:ts|js|mts|mjs|cts|cjs)$
regex:^/example-root-.*$ ^file:///example-root-.*/src/handler\.(?:ts|js|mts|mjs|cts|cjs)$
regex:^/(home/vcap/app|example-root-.*)$ ^file:///(home/vcap/app|example-root-.*)/src/handler\.(?:ts|js|mts|mjs|cts|cjs)$

.ts ↔ .js is folded into the regex automatically because Node's V8 inspector normally serves both the source-mapped TypeScript URL and the runtime JavaScript URL — matching either is correct.


Composing with cf-debugger

If --port is omitted, all of --region/--org/--space/--app are required. The CLI does not read ambient cf target state. It calls startDebugger(...) from @saptools/cf-debugger, attaches over the SSH tunnel, and disposes the tunnel on exit. You get the same one-shot UX whether the target is local or in CF.

The tunnel forwards one inspector port. Nested NodeWorker sessions carried by that inspector connection are auto-attached by breakpoint-oriented commands and can be pinned with --worker-id; a worker exposing only an unrelated separate port is outside that tunnel's reach.

cf-inspector snapshot \
  --region eu10 --org my-org --space dev --app my-srv \
  --bp src/handler.ts:42 \
  --capture 'req.url, this.user'


Author

dongtran

License

MIT


Made with to make your work life easier!

This command internally calls __INLINE_CODE_56__ to open the SSH tunnel, runs the snapshot through it, and tears the tunnel down on exit.

Cloud Foundry targeting is deliberately deterministic: __INLINE_CODE_57__ requires __INLINE_CODE_58__, __INLINE_CODE_59__, and __INLINE_CODE_60__. The CLI never inherits missing selectors from ambient __INLINE_CODE_61__ state. Use __INLINE_CODE_62__ only when the selected region needs an explicit endpoint override.

Worker behavior

When no isolate selector is passed, __INLINE_CODE_63__, __INLINE_CODE_64__, __INLINE_CODE_65__, __INLINE_CODE_66__, and __INLINE_CODE_67__ attach to the main isolate and every NodeWorker under the selected raw target. Breakpoints are mirrored to workers that attach later, the first matching pause wins, and JSON/human output identifies the winning __INLINE_CODE_68__ as either __INLINE_CODE_69__ or __INLINE_CODE_70__. Losing isolates that also paused are resumed before the command continues or exits.

Use __INLINE_CODE_71__ to pin a stable live worker ID from __INLINE_CODE_72__, __INLINE_CODE_73__ for the legacy positional selector, __INLINE_CODE_74__ to pin a raw inspector target, or __INLINE_CODE_75__ to deliberately ignore workers. Explicit selectors preserve single-isolate behavior.

Automation readiness contract

Commands that arm debugger behavior before waiting (__INLINE_CODE_76__, __INLINE_CODE_77__, __INLINE_CODE_78__, and __INLINE_CODE_79__) accept __INLINE_CODE_80__. The flag writes exactly one compact JSON object to __INLINE_CODE_81__ after every session present during initial arming has completed its CDP setup and immediately before the command waits or streams:

__CODE_BLOCK_3__

This is the supported synchronization contract for callers that must start the CLI before firing an HTTP request, queue job, or other external trigger. Parse the JSON line and require __INLINE_CODE_82__ plus __INLINE_CODE_83__, then confirm __INLINE_CODE_84__ matches the invocation; do not poll prose such as __INLINE_CODE_85__.

Field Contract
__INLINE_CODE_86__ Always __INLINE_CODE_87__
__INLINE_CODE_88__ Event schema version; currently __INLINE_CODE_89__
__INLINE_CODE_90__ __INLINE_CODE_91__, __INLINE_CODE_92__, __INLINE_CODE_93__, or __INLINE_CODE_94__
__INLINE_CODE_95__ Number of main/worker sessions whose initial arming completed before emission
__INLINE_CODE_96__ Total locations V8 had already resolved, or __INLINE_CODE_97__ for __INLINE_CODE_98__; __INLINE_CODE_99__ can still represent an accepted URL breakpoint that may resolve when a script loads
__INLINE_CODE_100__ Snapshot/exception wait or watch per-hit timeout; __INLINE_CODE_101__ for log

Workers attaching after this one-time event are still armed dynamically, but are not included in its __INLINE_CODE_102__ or __INLINE_CODE_103__ totals. If initial arming fails or the command is cancelled first, no readiness event is emitted. With __INLINE_CODE_104__, matching events received while the remaining initial sessions are still arming are neither emitted nor counted, so the log stream cannot precede its readiness marker. Without __INLINE_CODE_105__, stderr/stdout behavior is unchanged. An explicitly requested event is still emitted with __INLINE_CODE_106__.


CLI

__INLINE_CODE_107__

Set one or more breakpoints, wait for any of them to hit, capture frame metadata and requested expressions, auto-resume, exit.

__CODE_BLOCK_4__
Flag Description
__INLINE_CODE_108__ Local port the inspector or tunnel listens on. Required unless __INLINE_CODE_109__ are all set
__INLINE_CODE_110__ Explicit Cloud Foundry target. All four are required when __INLINE_CODE_111__ is omitted; ambient __INLINE_CODE_112__ is never consulted
__INLINE_CODE_113__ Override the API endpoint resolved from __INLINE_CODE_114__
__INLINE_CODE_115__ Raw __INLINE_CODE_116__ target index (default: __INLINE_CODE_117__)
__INLINE_CODE_118__ Nested NodeWorker index reported under the selected raw target by __INLINE_CODE_119__
__INLINE_CODE_120__ Stable live NodeWorker ID reported by __INLINE_CODE_121__
__INLINE_CODE_122__ Attach only to the main isolate and ignore NodeWorkers
__INLINE_CODE_123__ Required. Source location to break at. Pass multiple times to race several locations — the first one to hit wins
__INLINE_CODE_124__ Native breakpoint condition. It is compile-checked before arming; mutation-shaped conditions require __INLINE_CODE_125__ because CDP provides no side-effect guard for native conditions
__INLINE_CODE_126__ Skip the first N − 1 hits and only pause on the Nth (combines with __INLINE_CODE_127__ via logical AND)
__INLINE_CODE_128__ Top-level comma-separated expressions evaluated in the paused frame under V8's side-effect guard. Nested commas inside objects, arrays, calls, or strings are preserved. Objects are materialized to JSON strings when serializable
__INLINE_CODE_129__ Repeatable, order-preserving global expression evaluated before breakpoint setup. It is mutation-capable and only receives an advisory warning
__INLINE_CODE_130__ Walk this many call frames per hit (default: __INLINE_CODE_131__, top frame only). When __INLINE_CODE_132__, the result includes a __INLINE_CODE_133__ array
__INLINE_CODE_134__ Expressions evaluated on each captured call frame under the same side-effect guard as __INLINE_CODE_135__
__INLINE_CODE_136__ Disable V8's capture side-effect guard and explicitly allow mutation-shaped native conditions. Heuristic matches are annotated with __INLINE_CODE_137__
__INLINE_CODE_138__ How long to wait for the breakpoint to hit (default: __INLINE_CODE_139__)
__INLINE_CODE_140__ Maximum characters per captured value before truncation (one-shot default: __INLINE_CODE_141__). Explicit values are honored exactly
__INLINE_CODE_142__ Optional path-mapping anchor: literal path or __INLINE_CODE_143__ / __INLINE_CODE_144__
__INLINE_CODE_145__ Include expanded paused-frame scopes under __INLINE_CODE_146__. Omitted by default to keep targeted captures concise
__INLINE_CODE_147__ Print a human-readable summary instead of JSON
__INLINE_CODE_148__ Suppress snapshot progress messages on stderr
__INLINE_CODE_149__ Emit the versioned __INLINE_CODE_150__ JSON event on stderr after every current isolate is armed
__INLINE_CODE_151__ Skip __INLINE_CODE_152__ after capture
__INLINE_CODE_153__ Fail immediately if the target pauses somewhere else instead of waiting cooperatively

Snapshot progress is printed to __INLINE_CODE_154__ by default, including Cloud Foundry login/tunnel setup, inspector connection, breakpoint binding, the breakpoint wait, capture, resume, and cleanup phases. The final JSON document remains the only content written to __INLINE_CODE_155__, so piping it to __INLINE_CODE_156__ or another parser stays safe. Pass __INLINE_CODE_157__ to suppress these progress lines; warnings and errors still use __INLINE_CODE_158__.

Snapshot JSON includes frame metadata and __INLINE_CODE_159__ by default. __INLINE_CODE_160__ is only present with __INLINE_CODE_161__ because scope objects can be large and drown out targeted captures. Values are raw debugger values, so be careful when sharing logs.

Capture expressions are read-only by default. __INLINE_CODE_162__ sends __INLINE_CODE_163__ to V8 for both __INLINE_CODE_164__ and __INLINE_CODE_165__. Assignments, mutating methods such as __INLINE_CODE_166__, and calls V8 cannot prove pure are returned as a blocked capture with __INLINE_CODE_167__, __INLINE_CODE_168__, and a __INLINE_CODE_169__ error. Pass __INLINE_CODE_170__ only when changing the live inspectee is intentional. The opt-in disables the V8 guard and adds __INLINE_CODE_171__ when the advisory syntax scan recognizes a likely mutation; arbitrary function calls can still mutate without being recognized.

__INLINE_CODE_172__ is different: V8 executes it internally as a native breakpoint condition, where CDP has no __INLINE_CODE_173__ option. Mutation-shaped native conditions are rejected unless __INLINE_CODE_174__ is present. __INLINE_CODE_175__ and the standalone __INLINE_CODE_176__ command remain mutation-capable by design and emit advisory warnings for recognizable mutation syntax.

A blocked capture remains a normal additive __INLINE_CODE_177__, so one unsafe expression does not corrupt the rest of the snapshot:

__CODE_BLOCK_5__

With __INLINE_CODE_178__, recognized mutation syntax runs and the result carries __INLINE_CODE_179__; ordinary reads do not gain the field.

Truncation contract

JSON truncation is always out-of-band. A text value longer than the effective limit is cut to exactly that many JavaScript characters and gains __INLINE_CODE_180__ plus its full __INLINE_CODE_181__; no ellipsis is appended to the JSON value. These fields are absent when no cut occurs. Human output may add a visual ellipsis.

Expanded objects and scopes also report bounded structural capture. The __INLINE_CODE_182__, __INLINE_CODE_183__, or __INLINE_CODE_184__ whose properties, variables, or scopes were cut gains __INLINE_CODE_185__ and the exact direct __INLINE_CODE_186__. A serialized object capture propagates the aggregate known omission count to its __INLINE_CODE_187__. __INLINE_CODE_188__ additionally uses __INLINE_CODE_189__ and __INLINE_CODE_190__ so consumers can identify which field was cut; its compatibility __INLINE_CODE_191__ is the larger reported field length.

One-shot __INLINE_CODE_192__ and __INLINE_CODE_193__ commands default to __INLINE_CODE_194__ characters. Repeated __INLINE_CODE_195__ and __INLINE_CODE_196__ events default to __INLINE_CODE_197__. All four accept __INLINE_CODE_198__, and an explicit limit is applied exactly.

__CODE_BLOCK_6__

__INLINE_CODE_199__ measures the client-observed time from receiving the matching pause event until __INLINE_CODE_200__ completes. With __INLINE_CODE_201__, it is __INLINE_CODE_202__ because resume is intentionally skipped.

If the target pauses somewhere else first, for example another debugger's breakpoint or a __INLINE_CODE_203__ statement, __INLINE_CODE_204__ does not resume it by default. It warns once, waits for __INLINE_CODE_205__, then continues waiting for its own breakpoint within the remaining timeout. Use __INLINE_CODE_206__ when a strict immediate error is preferred.

For Cloud Foundry targets, replace __INLINE_CODE_207__ with __INLINE_CODE_208__. Cloud Foundry commands and tunnel readiness allow up to 180 seconds by default. For commands without their own wait semantics, __INLINE_CODE_209__ controls CF tunnel readiness. For breakpoint and exception commands, __INLINE_CODE_210__ is reserved for the command wait and tunnel readiness keeps the 180-second default.

__INLINE_CODE_211__

Set a non-pausing logpoint and stream the evaluated expression each time the line executes. The inspectee does not pause, but the expression and condition still execute against live state and can mutate it.

__CODE_BLOCK_7__

Output is JSON Lines on stdout (one event per line) plus a summary trailer on stderr:

__CODE_BLOCK_8__

When the user expression throws, the event is emitted with __INLINE_CODE_212__ instead of __INLINE_CODE_213__ so the stream never silently gaps:

__CODE_BLOCK_9__
Flag Description
__INLINE_CODE_214__ Local port the inspector or tunnel listens on. Required unless __INLINE_CODE_215__ are all set
__INLINE_CODE_216__ / __INLINE_CODE_217__ / __INLINE_CODE_218__ Pin one raw target or worker instead of automatic fan-out
__INLINE_CODE_219__ Ignore workers and attach only to the main isolate
__INLINE_CODE_220__ Required. Source location to log at
__INLINE_CODE_221__ Required. JavaScript expression evaluated at each hit, wrapped in try/catch on the inspectee side. It is mutation-capable; recognizable risks produce a warning
__INLINE_CODE_222__ Stop streaming after N seconds (default: run until SIGINT)
__INLINE_CODE_223__ Stop streaming after emitting N log events. The trailer reports __INLINE_CODE_224__
__INLINE_CODE_225__ Start emitting once the line has been hit N or more times
__INLINE_CODE_226__ Mutation-capable native condition evaluated on the inspectee. Recognizable risks produce a warning. Composes with __INLINE_CODE_227__ via logical AND
__INLINE_CODE_228__ Maximum characters per log value (streaming default: __INLINE_CODE_229__). Truncated events include __INLINE_CODE_230__ and __INLINE_CODE_231__
__INLINE_CODE_232__ Optional path-mapping anchor (same DSL as __INLINE_CODE_233__)
__INLINE_CODE_234__ Print human-readable lines instead of JSON Lines
__INLINE_CODE_235__ Emit the versioned __INLINE_CODE_236__ JSON event on stderr after every current isolate is armed

Native logpoint expressions and conditions have no V8 side-effect gate. The CLI warns when its best-effort syntax scan recognizes assignments or common mutating calls, but that scan cannot prove an arbitrary function is pure. Treat logpoint expressions as executable live code.

__INLINE_CODE_237__

Stream a snapshot per breakpoint hit. The inspectee is paused briefly while captures are evaluated, then resumed automatically; output is JSON Lines on stdout with a trailer on stderr (same shape as __INLINE_CODE_238__).

__CODE_BLOCK_10__

Each event is a __INLINE_CODE_239__:

__CODE_BLOCK_11__
Flag Description
__INLINE_CODE_240__ Local port the inspector or tunnel listens on. Otherwise pass all explicit Cloud Foundry selectors
__INLINE_CODE_241__ / __INLINE_CODE_242__ / __INLINE_CODE_243__ Pin one raw target or worker instead of automatic fan-out
__INLINE_CODE_244__ Ignore workers and attach only to the main isolate
__INLINE_CODE_245__ Required. Source location to capture on (repeatable)
__INLINE_CODE_246__ Top-level comma-separated expressions evaluated per hit under V8's side-effect guard
__INLINE_CODE_247__ Repeatable mutation-capable global expression evaluated before breakpoint setup; recognizable risks produce a warning
__INLINE_CODE_248__ Native condition; mutation-shaped conditions require __INLINE_CODE_249__
__INLINE_CODE_250__ Start emitting once the line has been hit N or more times
__INLINE_CODE_251__ Path-mapping anchor (same DSL as __INLINE_CODE_252__)
__INLINE_CODE_253__ Stop streaming after N seconds (default: until SIGINT)
__INLINE_CODE_254__ Stop streaming after emitting N events
__INLINE_CODE_255__ How long to wait for the next hit before giving up (default: __INLINE_CODE_256__)
__INLINE_CODE_257__ Maximum characters per captured value (streaming default: __INLINE_CODE_258__)
__INLINE_CODE_259__ Walk this many call frames per hit (default: __INLINE_CODE_260__)
__INLINE_CODE_261__ Expressions evaluated on each call frame under the capture side-effect guard
__INLINE_CODE_262__ Disable the capture side-effect guard and explicitly allow mutation-shaped native conditions
__INLINE_CODE_263__ Include expanded paused-frame scopes per hit
__INLINE_CODE_264__ Print human-readable lines instead of JSON Lines
__INLINE_CODE_265__ Emit the versioned __INLINE_CODE_266__ JSON event on stderr after every current isolate is armed
__INLINE_CODE_267__

Pause on a thrown exception, capture the exception value plus the paused frame, then resume.

__CODE_BLOCK_12__

Result is a __INLINE_CODE_268__ with an extra __INLINE_CODE_269__ field:

__CODE_BLOCK_13__
Flag Description
__INLINE_CODE_270__ or explicit __INLINE_CODE_271__ Select the local inspector or deterministic Cloud Foundry target
__INLINE_CODE_272__ / __INLINE_CODE_273__ / __INLINE_CODE_274__ Pin one raw target or worker instead of automatic fan-out
__INLINE_CODE_275__ Ignore workers and attach only to the main isolate
__INLINE_CODE_276__ Pause on which exceptions: __INLINE_CODE_277__ (default), __INLINE_CODE_278__, or __INLINE_CODE_279__
__INLINE_CODE_280__ Top-level expressions evaluated in the paused frame under V8's side-effect guard
__INLINE_CODE_281__ Walk this many call frames (default: __INLINE_CODE_282__)
__INLINE_CODE_283__ Expressions evaluated on each frame under V8's side-effect guard
__INLINE_CODE_284__ Disable the capture side-effect guard; heuristic matches gain __INLINE_CODE_285__
__INLINE_CODE_286__ Include paused-frame scopes
__INLINE_CODE_287__ Path-mapping anchor (only used if you also wire snapshot helpers)
__INLINE_CODE_288__ How long to wait for an exception (default: __INLINE_CODE_289__)
__INLINE_CODE_290__ Maximum characters per captured value (one-shot default: __INLINE_CODE_291__)
__INLINE_CODE_292__ Skip __INLINE_CODE_293__ after capture
__INLINE_CODE_294__ Print a human-readable summary instead of JSON
__INLINE_CODE_295__ Emit the versioned __INLINE_CODE_296__ JSON event on stderr after pause-on-exception is active in every current isolate
__INLINE_CODE_297__

Evaluate one expression with __INLINE_CODE_298__ in the selected isolate's global scope and print the result. __INLINE_CODE_299__ is intentionally mutation-capable and has no side-effect gate; recognizable mutation syntax emits an advisory warning to __INLINE_CODE_300__. For read-only paused-frame values, use __INLINE_CODE_301__ or call the programmatic __INLINE_CODE_302__ API. Plain __INLINE_CODE_303__ remains unrestricted by default for backward compatibility.

__CODE_BLOCK_14__
__INLINE_CODE_304__

Print every script known by the main isolate and each currently attached NodeWorker (useful for debugging path-mapping issues). Each JSON entry carries __INLINE_CODE_305__ or __INLINE_CODE_306__, so duplicate per-isolate __INLINE_CODE_307__ values remain unambiguous. Add __INLINE_CODE_308__ to narrow noisy script lists with a literal/wildcard pattern; __INLINE_CODE_309__ separates alternatives and __INLINE_CODE_310__ / __INLINE_CODE_311__ match variable text.

__CODE_BLOCK_15__

With __INLINE_CODE_312__, rows are __INLINE_CODE_313__, where isolate is __INLINE_CODE_314__ or __INLINE_CODE_315__. __INLINE_CODE_316__, __INLINE_CODE_317__, __INLINE_CODE_318__, and __INLINE_CODE_319__ retain their normal narrowing behavior.

__INLINE_CODE_320__

Print raw __INLINE_CODE_321__ inspector targets with stable __INLINE_CODE_322__ values, likely worker labels, and the total target/worker counts on __INLINE_CODE_323__. For each raw target, the command also probes Node's __INLINE_CODE_324__ CDP domain and lists live nested workers with their own indexes.

__CODE_BLOCK_16__

JSON output nests workers beneath their raw target:

__CODE_BLOCK_17__

__INLINE_CODE_325__ selects a complete raw inspector endpoint. __INLINE_CODE_326__ selects a live nested worker by its stable ID, while __INLINE_CODE_327__ retains positional-index selection for compatibility. Modern Node.js 20–25 verification found workers on the __INLINE_CODE_328__ path, including workers already alive before post-hoc __INLINE_CODE_329__ inspector activation; the raw-target selector remains supported for runtimes that publish that shape.

When no selector is passed, breakpoint-oriented commands attach to raw target __INLINE_CODE_330__ and automatically fan out across its main isolate and nested workers. If only one raw target and no workers are visible, __INLINE_CODE_331__ explains that the worker may have exited, the runtime may not expose NodeWorker discovery, or a separate worker port may be unreachable through the single Cloud Foundry tunnel. Rerun the command while the worker is alive before pinning its __INLINE_CODE_332__.

If __INLINE_CODE_333__, __INLINE_CODE_334__, or another command reports __INLINE_CODE_335__, 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, pass the complete __INLINE_CODE_336__ selector so __INLINE_CODE_337__ can open a fresh tunnel.

__INLINE_CODE_338__

Check whether a __INLINE_CODE_339__ can accept a breakpoint before arming one. The command uses the same path mapping as __INLINE_CODE_340__ and checks every currently attached isolate by default.

__CODE_BLOCK_18__

__INLINE_CODE_341__ means no loaded script matched the file/path mapping; use __INLINE_CODE_342__, adjust __INLINE_CODE_343__, or trigger lazy loading. __INLINE_CODE_344__ means the script is loaded but that exact line has no V8 break location; choose a neighboring executable line. __INLINE_CODE_345__ includes the concrete script IDs, isolate identities, lines, and columns.

__INLINE_CODE_346__

Connect, fetch the runtime version, print it, disconnect. Useful as a smoke-test that the tunnel is healthy.

__CODE_BLOCK_19__

__INLINE_CODE_347__ checks the port-level __INLINE_CODE_348__ endpoint, so raw-target and worker selectors do not apply to this smoke test.

Exact CDP tracing APIs

The library also exports lower-level, validated primitives for tools that need to plan and step an exact loaded function instead of using a URL-regex breakpoint:

__CODE_BLOCK_20__

__INLINE_CODE_349__ and __INLINE_CODE_350__ use CDP-native zero-based line and column numbers. Exact breakpoint setup returns both the requested and actual location, then fails closed and removes the breakpoint if V8 resolves a different script, line, or column. An omitted column means CDP column zero. __INLINE_CODE_351__ is __INLINE_CODE_352__ for logical values stored in internal slots (including maps, sets, promises, and dates) and __INLINE_CODE_353__ for proxies; it is omitted for ordinary objects. __INLINE_CODE_354__ accepts an __INLINE_CODE_355__ and cleans its event listener and timer on success, timeout, abort, or session close. The same layer exports __INLINE_CODE_356__, __INLINE_CODE_357__, __INLINE_CODE_358__, __INLINE_CODE_359__, and __INLINE_CODE_360__ for bounded controllers.

Programmatic Cloud Foundry tunnels can select the same process instance and Node PID used for both the remote signal and SSH forwarding:

__CODE_BLOCK_21__

__INLINE_CODE_361__ retains backward-compatible reuse when __INLINE_CODE_362__ reports a healthy local port for an existing session. Controllers that must prove they own and can dispose the tunnel should use __INLINE_CODE_363__; it propagates __INLINE_CODE_364__ instead of parsing or borrowing a pre-existing session.


How it works

__CODE_BLOCK_22__

Path mapping uses CDP's first-class __INLINE_CODE_365__:

__INLINE_CODE_366__ Resulting urlRegex (line __INLINE_CODE_367__ of __INLINE_CODE_368__)
omitted `(?:^
__INLINE_CODE_369__ (literal) __INLINE_CODE_370__
__INLINE_CODE_371__ __INLINE_CODE_372__
__INLINE_CODE_373__ __INLINE_CODE_374__

__INLINE_CODE_375__ is folded into the regex automatically because Node's V8 inspector normally serves both the source-mapped TypeScript URL and the runtime JavaScript URL — matching either is correct.


Composing with __INLINE_CODE_376__

If __INLINE_CODE_377__ is omitted, all of __INLINE_CODE_378__ are required. The CLI does not read ambient __INLINE_CODE_379__ state. It calls __INLINE_CODE_380__ from __INLINE_CODE_381__, attaches over the SSH tunnel, and disposes the tunnel on exit. You get the same one-shot UX whether the target is local or in CF.

The tunnel forwards one inspector port. Nested NodeWorker sessions carried by that inspector connection are auto-attached by breakpoint-oriented commands and can be pinned with __INLINE_CODE_382__; a worker exposing only an unrelated separate port is outside that tunnel's reach.

__CODE_BLOCK_23__

Author

dongtran

License

MIT


Made with to make your work life easier!

Keywords