browser-trace
Original:🇺🇸 English
Translated
8 scripts
Capture a full DevTools-protocol trace of any browser automation — CDP firehose, screenshots, and DOM dumps — then bisect the stream into per-page searchable buckets. Use when the user wants to debug a failed run, audit network/console/DOM activity, attach a trace to an in-progress session, or feed structured per-page summaries back into an agent loop so its next iteration learns from the last one.
7installs
Sourcebrowserbase/skills
Added on
NPX Install
npx skill4agent add browserbase/skills browser-traceTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →Browser Trace
Attach a second, read-only CDP client to a browser session that is already being driven by your main automation. The trace records the full DevTools firehose to NDJSON, polls for screenshots and DOM dumps in parallel, and slices everything into a directory tree that bash tools can search.
This skill does not drive pages — it only listens. Pair it with the skill, , Stagehand, Playwright, or anything else that speaks CDP.
browserbbWhen to use
- The user wants to debug a browser-automation run (failing form, missing element, hung navigation, JS exception).
- The user has a running automation and wants to attach a trace mid-flight without restarting it.
- The user wants to split a CDP firehose into network / console / DOM / page buckets.
- The user wants screenshots + DOM snapshots over time, joined to CDP events by timestamp.
If the user just wants to drive the browser, use the skill instead.
browserSetup check
bash
node --version # require Node 18+
which browse || npm install -g @browserbasehq/browse-cli@alpha
which bb || npm install -g @browserbasehq/cli # only needed for Browserbase remote
which jq || true # optional — used only for ad-hoc queryingVerify exists (it ships in 0.5.0-alpha-a4ca430+):
browse cdpbash
browse --help | grep -q "^\s*cdp " || echo "browse cdp not in this version — install @alpha"How it works
Every Chrome DevTools target accepts multiple concurrent CDP clients. Your main automation is one client; this skill adds a second one that only enables observation domains (Network, Console, Runtime, Log, Page) and never sends action commands.
The tracer has three pieces:
- Firehose: streams every CDP event as one JSON object per line to
browse cdp <target>.cdp/raw.ndjson - Sampler: a polling loop calls and
browse --ws <target> screenshoton an interval (default 2s).browse --ws <target> get html bodyis one-shot and bypasses the daemon, so it doesn't fight the main automation.--ws - Bisector: after the run, walks
bisect-cdp.mjsonce, slices it into per-bucket JSONL files keyed by CDP method, and additionally bisects per page using top-levelraw.ndjsonevents as boundaries.Page.frameNavigated
Quickstart
Local Chrome
bash
# 1. Launch Chrome with a debugger port (any user-data-dir keeps it isolated).
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/chrome-o11y \
about:blank &
# 2. Start the tracer.
node scripts/start-capture.mjs 9222 my-run
# 3. Run your main automation against port 9222.
browse env local 9222
browse open https://example.com
# ...whatever the run does...
# 4. Stop and bisect.
node scripts/stop-capture.mjs my-run
node scripts/bisect-cdp.mjs my-runBrowserbase remote
Two helpers wrap the platform-side bookkeeping: creates or attaches to a session and starts the tracer; pulls platform artifacts (final session metadata, server logs, downloads) into the run dir at the end.
bb-capture.mjsbb-finalize.mjsBrowserbase ends a session as soon as its last CDP client disconnects. Always create withand attach an automation client before (or together with) the tracer.--keep-alivedoes this for you.bb-capture.mjs --new
bash
export BROWSERBASE_API_KEY=...
# 1. Create a keep-alive session AND start the tracer in one step.
# Prints the session id, connectUrl prefix, and a live debugger URL you
# can open in a browser to watch the run interactively.
node scripts/bb-capture.mjs --new my-run
# 2. Drive automation. bb-capture stamped the session id into the manifest.
SID=$(jq -r .browserbase.session_id .o11y/my-run/manifest.json)
browse --connect "$SID" open https://example.com
browse --connect "$SID" open https://news.ycombinator.com
# 3. Stop the tracer, bisect, then pull platform artifacts and release.
node scripts/stop-capture.mjs my-run
node scripts/bisect-cdp.mjs my-run
node scripts/bb-finalize.mjs my-run --releaseAttaching to a session that's already running (e.g. one your production worker created) — accepts a session id instead of :
bb-capture.mjs--newbash
# Pick a running session (filter client-side; bb sessions list has no --status flag)
bb sessions list | jq -r '.[] | select(.status == "RUNNING") | .id'
node scripts/bb-capture.mjs <session-id> mid-flight-debug
# ...tracer runs alongside the existing automation client; no disruption...
node scripts/stop-capture.mjs mid-flight-debug
node scripts/bisect-cdp.mjs mid-flight-debug
node scripts/bb-finalize.mjs mid-flight-debug # without --release: leave the session runningWhat you get from the Browserbase platform
bb-capture.mjsbrowserbasemanifest.jsonbb-finalize.mjs- — final
<run>/browserbase/session.jsonsnapshot (proxyBytes, status, ended_at, viewport, …)bb sessions get - —
<run>/browserbase/logs.jsonoutput. Often empty. The CDP firehose inbb sessions logsis the source of truth; this is a side channel.cdp/raw.ndjson - — files the session downloaded, if any (the script discards the empty 22-byte zip you get when there are none)
<run>/browserbase/downloads.zip
bb sessions recordingscreenshots/dom/The live in the manifest opens an interactive Chrome DevTools view served by Browserbase — handy for watching a long-running automation while the tracer captures the firehose to disk.
debugger_urlFilesystem layout
.o11y/<run-id>/
manifest.json run metadata: target, domains, started_at, stopped_at
index.jsonl one line per sample: {ts, screenshot, dom, url}
cdp/
raw.ndjson full CDP firehose (one JSON object per line)
summary.json {sessionId, duration, totalEvents, pages[]} — see shape below
network/{requests,responses,finished,failed,websocket}.jsonl session-wide buckets (always written)
console/{logs,exceptions}.jsonl
runtime/all.jsonl
log/entries.jsonl
page/{navigations,lifecycle,frames,dialogs,all}.jsonl
dom/all.jsonl (only if O11Y_DOMAINS includes DOM)
target/{attached,detached}.jsonl
pages/ per-page slices, indexed by top-level frameNavigated boundaries
000/ first concrete page
url.txt the URL for this page
summary.json this page's domains/network/timing block (same shape as a pages[] entry)
raw.jsonl firehose scoped to this page
network/, console/, page/, runtime/, log/, target/, dom/ same buckets, only non-empty files
screenshots/<iso-ts>.png one PNG per sample interval
dom/<iso-ts>.html one HTML dump per sample interval
browserbase/ added by bb-finalize.mjs (Browserbase runs only)
session.json final `bb sessions get` snapshot (proxyBytes, status, ended_at, …)
logs.json `bb sessions logs` output (often [])
downloads.zip `bb sessions downloads get` output (only if the session downloaded files)When a run was started via , also carries a top-level block: , , , , , , .
bb-capture.mjsmanifest.jsonbrowserbasesession_idproject_idregionstarted_atexpires_atkeep_alivedebugger_urlSummary shape
cdp/summary.jsonpages[]Page.frameNavigatedjson
{
"sessionId": "45f28023-…",
"duration": { "startMs": 1777312533000, "endMs": 1777312609000, "totalMs": 76000 },
"totalEvents": 420,
"pages": [
{
"pageId": 0,
"url": "https://example.com/",
"startMs": 1777312533000, "endMs": 1777312538886, "durationMs": 5886,
"eventCount": 60,
"domains": {
"Network": { "count": 18, "errors": 1 },
"Console": { "count": 2 },
"Page": { "count": 24 },
"Runtime": { "count": 13 }
},
"network": { "requests": 4, "failed": 1, "byType": { "Document": 2, "Script": 1, "Other": 1 } }
}
]
}startMsendMsdurationMsmanifest.started_atdomains[*]errorswarningsDrilling in with query.mjs
query.mjsFor interactive exploration, use instead of remembering paths:
scripts/query.mjs <run-id> <command>bash
node scripts/query.mjs my-run list # one-line table of pages
node scripts/query.mjs my-run page 1 # full summary for page 1
node scripts/query.mjs my-run page 1 network/failed # cat failed.jsonl for page 1
node scripts/query.mjs my-run errors # all errors across pages, attributed by pid
node scripts/query.mjs my-run errors 2 # errors from page 2 only
node scripts/query.mjs my-run hosts # top hosts by request count
node scripts/query.mjs my-run host api.example.com # all requests/responses for a host
node scripts/query.mjs my-run summary # full summary.jsonBehind the scenes it just reads and the tree — feel free to bypass it with raw / once you know the shape.
cdp/summary.jsoncdp/pages/<pid>/jqrgTop traversal recipes
bash
# All failed network requests (use jq -c to keep it line-delimited)
jq -c '.params' .o11y/<run>/cdp/network/failed.jsonl
# Find requests to a specific host
jq -c 'select(.params.request.url | test("api\\.example\\.com"))' \
.o11y/<run>/cdp/network/requests.jsonl
# 4xx/5xx responses
jq -c 'select(.params.response.status >= 400)
| {status: .params.response.status, url: .params.response.url}' \
.o11y/<run>/cdp/network/responses.jsonl
# Console errors only
jq -c 'select(.params.type == "error")' .o11y/<run>/cdp/console/logs.jsonl
# Sequence of URLs visited
jq -r '.params.frame.url' .o11y/<run>/cdp/page/navigations.jsonl
# Find the screenshot taken closest to a timestamp (e.g., when an exception fired)
ls .o11y/<run>/screenshots/ | sort | awk -v t=20260427T1714123NZ '
$0 >= t { print; exit }'See REFERENCE.md for the full jq recipe library and a method-by-method bisect map. See EXAMPLES.md for end-to-end debug scenarios.
Best practices
- Use on Browserbase: it enforces
bb-capture.mjs, fetches the connectUrl, captures the debugger URL, and stamps the manifest. Doing it manually invites mistakes.--keep-alive - Don't a session you don't own:
--releaseis for sessions you created withbb-finalize.mjs --release. When attaching to a production session via--new, runbb-capture.mjs <session-id>withoutbb-finalize.mjsso the original automation keeps running.--release - Order matters for remote: on Browserbase, attach the main automation client before (or together with) the tracer, and create the session with . Otherwise the session ends as soon as the tracer's WS closes.
--keep-alive - Don't poll faster than ~1s: each sample opens a one-shot CDP connection and screenshots Chrome. 2s is a good default.
- Pick domains deliberately: defaults () cover most debugging. Add
Network Console Runtime Log Pagefor DOM-tree mutations (very noisy) viaDOM.O11Y_DOMAINS="$O11Y_DOMAINS DOM" - Use for the automation client on remote, not a fresh
--connect <session-id>(which would create a new session each time).browse env remote - Always run , even after a crash, so background processes don't linger and the manifest gets
stop-capture.mjs.stopped_at - Bisect once per run: is idempotent — it overwrites the per-bucket files from
bisect-cdp.mjseach time.raw.ndjson
Troubleshooting
- : usually means the target is unreachable (wrong port) or the Browserbase session has already ended. For remote, verify with
browse cdp exited immediately— ifbb sessions get <id>isstatus, recreate withCOMPLETEDand attach automation first.--keep-alive - Empty even though processes are running: confirm a CDP client is actually driving the page. The tracer only emits events that the browser generates, so an idle browser produces ~5 lines of attach/discover messages and nothing else.
raw.ndjson - Screenshots all look identical: check — if
index.jsonldoesn't change, the page hasn't navigated yet. The polling loop runs independently of the main automation's pace.url - Browserbase session ends mid-run: it likely hit . Recreate with a higher timeout (
--timeout) or remove the timeout flag.BB_SESSION_TIMEOUT=1800 node scripts/bb-capture.mjs --new ... - says "not RUNNING": the session you tried to attach to ended. List candidates with
bb-capture.mjs <id>and try again.bb sessions list | jq '.[] | select(.status == "RUNNING")' - is empty
browserbase/logs.json: expected —[]is sparse in practice. The CDP firehose inbb sessions logsis the source of truth.cdp/raw.ndjson - Where's the session recording (rrweb)?: is deprecated; this skill doesn't fetch it. Use the screenshot stream in
bb sessions recordingand DOM dumps inscreenshots/.dom/
For full reference, see REFERENCE.md.
For example debug runs, see EXAMPLES.md.