Keep omitted discovery attachment state as null through Node, browser, Electron, and reconciliation evidence. Report the uncertainty while retaining Node observation unless discovery explicitly marks the target attached. Reject worker TargetInfo records missing the required boolean with partial coverage. Validate omitted and explicit states through provider, CLI, and MCP boundaries and real Node 24 Inspector workflows. Consolidate Electron regression coverage into the existing list-and-inspect workflow. BREAKING CHANGE: attached fields in JavaScript runtime, browser, and Electron target discovery and inspection results may be null when discovery does not report attachment state.
153 lines
7.6 KiB
Markdown
153 lines
7.6 KiB
Markdown
# Passive Node and Electron runtime observation
|
|
|
|
REA can attach to an already-running Node.js or Electron V8 Inspector target
|
|
and retain script-load and execution-context metadata. The
|
|
`list_javascript_runtime_targets` and `observe_javascript_runtime` MCP tools
|
|
have equivalent `rea list-javascript-runtime-targets` and
|
|
`rea observe-javascript-runtime` commands. Successful calls return
|
|
deterministic Evidence.
|
|
|
|
Each request supplies the loopback Inspector endpoint and, for observation, the
|
|
target ID. This surface is separate from browser CDP, Electron file-page
|
|
inspection, and Process Capture:
|
|
|
|
```bash
|
|
rea list-javascript-runtime-targets http://127.0.0.1:9229 \
|
|
--json
|
|
rea observe-javascript-runtime http://127.0.0.1:9229 TARGET_ID \
|
|
--runtime-kind node --json
|
|
```
|
|
|
|
## Start a caller-owned Node target
|
|
|
|
Run your selected application with Inspector bound to loopback:
|
|
|
|
```bash
|
|
node --inspect=127.0.0.1:9229 ./app.mjs
|
|
```
|
|
|
|
Keep it running while listing targets and observing the selected ID. Use a free
|
|
port and the same literal endpoint in both REA calls. Stop the application
|
|
through its normal lifecycle when finished; REA disconnects but does not own or
|
|
terminate it. Node.js must already be installed.
|
|
|
|
Use `--inspect` for this passive workflow. `--inspect-brk` pauses before the
|
|
application starts, and REA never resumes it or sends
|
|
`Runtime.runIfWaitingForDebugger`. Resume a paused target with a separate
|
|
caller-owned debugger before requesting application runtime observations.
|
|
|
|
The caller supplies the literal-loopback HTTP endpoint directly. Selecting an endpoint exposes all targets it serves and their local script locations or renderer origins. A target must match the exact requested target ID and expose a same-port WebSocket reported by that endpoint. Targets explicitly marked attached are rejected rather than displacing another debugger. Node's `/json/list` omits attachment state, so REA preserves it as `null` (unknown), reports that limitation, and permits observation unless the endpoint explicitly reports `attached: true`. Local paths are canonicalized after symlink resolution.
|
|
|
|
The `attached` field in JavaScript runtime and browser target results is nullable: `true` and `false` are reported only when discovery supplied a boolean; `null` means the endpoint omitted or could not report the state. CDP `TargetInfo` worker records require an `attached` boolean; REA excludes a worker record with a missing or malformed value as an invalid protocol value.
|
|
|
|
`list_javascript_runtime_targets` returns every available target in one inline
|
|
array.
|
|
|
|
For the MCP follow-up call, pass the same literal-loopback endpoint and the selected target ID to `observe_javascript_runtime`. REA rediscovers targets and validates the ID against the live endpoint before observation.
|
|
|
|
### Discovery file locations
|
|
|
|
Node's `/json/list` reports a best-effort pathname prefixed with `file://`,
|
|
without URI encoding. On POSIX, REA interprets unambiguous paths literally,
|
|
including `#` and `%` in filenames. Node also replaces backslashes and double
|
|
quotes with underscores. That transformation cannot be reversed reliably:
|
|
an underscore may already be part of the original filename.
|
|
|
|
Windows paths, ambiguous underscore paths, and local discovery paths that
|
|
cannot be verified remain eligible by their endpoint and exact target ID.
|
|
Their target location preserves the reported value:
|
|
|
|
```json
|
|
{
|
|
"kind": "unresolved",
|
|
"reported_url": "file://C:_tools_entry.js",
|
|
"reason": "unverifiable-file-location"
|
|
}
|
|
```
|
|
|
|
This location does not claim that a file exists or authorize reading its
|
|
contents. Listing remains HTTP-only. Observation resolves loaded script
|
|
locations independently from `Debugger.scriptParsed` file URLs, preserving
|
|
the existing checks for local files and encoded separators. Unsupported
|
|
schemes, remote file hosts, and a bare `file://` remain excluded. These Node
|
|
discovery rules do not apply to page targets or other runtime products.
|
|
|
|
Reconciliation retains an unresolved `target_location` object and reports
|
|
`runtime-location-unresolved` with unknown confidence for that target. It
|
|
does not derive a static match from the reported pathname; verified loaded
|
|
script locations remain available for independent matching.
|
|
|
|
## Passive protocol boundary
|
|
|
|
The provider sends exactly two protocol commands:
|
|
|
|
- `Runtime.enable`
|
|
- `Debugger.enable`
|
|
|
|
It never sends `Runtime.evaluate`, reads script source, installs breakpoints,
|
|
pauses or resumes execution, calls `Runtime.runIfWaitingForDebugger`, launches
|
|
the target, instruments JavaScript, or invokes Electron APIs. Closing a call
|
|
closes only REA's WebSocket.
|
|
|
|
The direct observations are:
|
|
|
|
- `Debugger.scriptParsed`, establishing that a bounded script location was
|
|
parsed in the observed execution context;
|
|
- `Debugger.scriptFailedToParse`, retained only as a bounded invalid-script
|
|
count;
|
|
- `Runtime.executionContextCreated`,
|
|
`Runtime.executionContextDestroyed`, and
|
|
`Runtime.executionContextsCleared`, establishing context lifecycle within
|
|
the capture window.
|
|
|
|
The Inspector protocol does not directly expose require/import caller edges,
|
|
EventEmitter emissions or listener invocation, Electron IPC messages or
|
|
handlers, or script-unload events. Those facts remain explicit unknowns.
|
|
`scriptParsed` proves script presence, not the importing module, feature
|
|
execution, initialization order, or causality.
|
|
|
|
## Node and Electron roles
|
|
|
|
The caller declares one role: `node`, `electron-main`, `electron-preload`, or
|
|
`electron-renderer`. Node and Electron-main declarations require a protocol
|
|
target of type `node`; preload and renderer declarations require `page`.
|
|
Inspector does not authenticate the operating-system PID or distinguish an
|
|
Electron role, so Evidence records the role authority as
|
|
`caller-declared-unverified`.
|
|
|
|
Electron main, preload, and renderer behavior can therefore be observed only
|
|
as separate Inspector targets. The provider does not infer that two
|
|
targets belong to the same Electron application.
|
|
|
|
## Capture and determinism
|
|
|
|
Capture retains valid script and execution-context metadata within an 8 MiB
|
|
aggregate budget, including conservative estimates for UTF-16 strings, record
|
|
and collection overhead, and projected URL fields. Each CDP message also has a
|
|
16 MiB transport limit. A message over that limit fails with `payload_limit`;
|
|
when the aggregate budget is reached, later metadata events are dropped and
|
|
the partial result reports `events_dropped`, `truncated: true`, and
|
|
`retained_metadata_budget_exceeded`. Repeated context transitions update the
|
|
current context record instead of accumulating duplicate objects. Scripts are
|
|
validated after capture, deduplicated by stable metadata, and canonically
|
|
sorted. Wall-clock timestamps and protocol script IDs are excluded from the
|
|
durable result, so identical inputs and captured metadata produce the same
|
|
Evidence ID.
|
|
|
|
Inspector attach is inherently incomplete. Enabling `Debugger` reports known
|
|
and uncollected scripts, but scripts collected before attachment may be
|
|
missing. Absence from the observation window never proves that a script or
|
|
behavior does not occur.
|
|
|
|
## Static correlation
|
|
|
|
Pass the resulting observation Evidence and an
|
|
`analyze_javascript_application` Evidence record to
|
|
`reconcile_javascript_runtime`. The reconciliation accepts this provider
|
|
alongside passive web and Electron page captures. Exact file or URL mappings
|
|
supplied with the request can correlate script presence with JavaScript Application Graph
|
|
assets. Because this provider never reads source bytes, matches normally use a
|
|
unique authorized location and remain weaker than captured-byte identity.
|
|
Builtin `node:` scripts remain runtime-only nodes unless a future explicit
|
|
static authority models them.
|