dev, start, and preview stream one schema-1 frame per lifecycle transition. Each frame is a complete result envelope on its own line.
Turning the stream on
The stream keys off theEXTENSION_OUTPUT environment variable. Set it to json or ndjson and frames own stdout:
Frame statuses
Every frame’s
value carries the session identity: command, browser, distPath, pid, port, plus readyPath, eventsPath, runId, instanceId, and toolchainVersion when known.
A run refused before the session begins sends one failed frame and no starting frame. A missing manifest, a config file that fails to load, and a remote URL with no usable archive all end that way.
Compile failures
Acompile-failed frame carries the compiler output inside value.output, so you never scrape stdout. The output is ANSI-stripped and capped at 2000 characters. When the cap cuts it, the frame sets truncated: true.
The first failure of a session uses code E_FIRST_COMPILE, every later one uses E_COMPILE.
The frame also carries error.details, one entry per diagnostic with its own code, file, and position, errors first, in the shape the result envelope describes under Compile diagnostics. The list is capped at 20 entries, and truncated: true is set when either the output or the list was cut.
Ready follows the contract
Theready frame reads ready.json before it fires. A compile can succeed while the browser refuses the extension, and the contract stays in error when that happens.
In that case the stream emits a failed frame with code E_READY_ERROR_STATUS instead, and value.readyCode names the contract’s own error code.
The same frame arrives when the browser never started or exited before the extension loaded. value.readyCode is then browser_launch_failed or browser_exited. A Firefox whose debugger never answers counts as a launch failure.
A bad --chromium-binary or --gecko-binary pin also reports readyCode: "browser_launch_failed", but its frame has status: "usage" and error.code: "E_BROWSER_BINARY_INVALID", the code every command gives that pin.
A Chromium dev session whose CDP wire failed after the browser came up still sends ready, because the browser is up. That frame carries the fault on warnings as E_CDP_...: message, the same pair the contract stamps as cdpFaultCode and cdpFaultMessage, see ready.json. Reload and HMR cannot attach until the fault is fixed.
Browser exits
A background watcher pollsready.json once per second for the launcher’s exit stamp. When browserExitedAt appears, the stream emits one browser-exited frame.
The frame’s code depends on the evidence in the contract:
E_PROFILE_LOCKEDwhen the contract shows a locked profile. The browser never started, another session holds the profile.E_BROWSER_LAUNCHfor every other unexpected exit.
value carries exitCode, exitSignal, and browserExitedAt when the contract has them. A crash has no exit code, only a signal such as SIGTRAP.
Next steps
- Read the contract behind the frames in ready.json.
- Drive the whole loop from an agent with Driving the CLI.

