Trace de navigateur
Attachez un deuxième client CDP en lecture seule à une session de navigateur déjà contrôlée par votre automation principale. La trace enregistre le flux complet DevTools en NDJSON, récupère les screenshots et les dumps DOM en parallèle, et découpe tout en une arborescence de répertoires que les outils bash peuvent fouiller.
Cette skill ne contrôle pas les pages — elle n'écoute que. Associez-la à la skill browser, bb, Stagehand, Playwright ou toute autre plateforme qui parle CDP.
Quand l'utiliser
- L'utilisateur veut déboguer une exécution d'automation de navigateur (formulaire défaillant, élément manquant, navigation bloquée, exception JS).
- L'utilisateur a une automation en cours et veut attacher une trace en vol sans la redémarrer.
- L'utilisateur veut découper un flux CDP en seaux network / console / DOM / page.
- L'utilisateur veut des screenshots + snapshots DOM dans le temps, joints aux événements CDP par timestamp.
Si l'utilisateur veut juste contrôler le navigateur, utilisez plutôt la skill browser.
Vérification de la configuration
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 querying
Vérifiez que browse cdp existe (il est inclus dans 0.5.0-alpha-a4ca430+) :
browse --help | grep -q "^\s*cdp " || echo "browse cdp not in this version — install @alpha"
Fonctionnement
Chaque cible Chrome DevTools accepte plusieurs clients CDP concurrents. Votre automation principale en est un ; cette skill en ajoute un deuxième qui ne permet que les domaines d'observation (Network, Console, Runtime, Log, Page) et n'envoie jamais de commandes d'action.
Le traceur a trois composants :
- Firehose :
browse cdp <target>diffuse chaque événement CDP sous forme d'un objet JSON par ligne verscdp/raw.ndjson. - Sampler : une boucle de polling appelle
browse --ws <target> screenshotetbrowse --ws <target> get html bodyà intervalle régulier (par défaut 2s).--wsest ponctuel et contourne le daemon, ne rentre donc pas en conflit avec l'automation principale. - Bisector : après l'exécution,
bisect-cdp.mjsparcourtraw.ndjsonune fois, le découpe en fichiers JSONL par seau avec clés CDP method, et découpe aussi par page en utilisant les événements top-levelPage.frameNavigatedcomme délimiteurs.
Démarrage rapide
Chrome local
# 1. Lancez Chrome avec un port debugger (tout user-data-dir le garde isolé).
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/chrome-o11y \
about:blank &
# 2. Démarrez le traceur.
node scripts/start-capture.mjs 9222 my-run
# 3. Exécutez votre automation principale contre le port 9222.
browse env local 9222
browse open https://example.com
# ...whatever the run does...
# 4. Arrêtez et découpez.
node scripts/stop-capture.mjs my-run
node scripts/bisect-cdp.mjs my-run
Browserbase distant
Deux helpers enveloppent la gestion de plateforme : bb-capture.mjs crée ou s'attache à une session et démarre le traceur ; bb-finalize.mjs récupère les artefacts de plateforme (métadonnées finales de session, logs serveur, téléchargements) dans le répertoire d'exécution.
Browserbase termine une session dès que son dernier client CDP se déconnecte. Créez toujours avec
--keep-aliveet attachez un client automation avant (ou en même temps que) le traceur.bb-capture.mjs --newle fait pour vous.
export BROWSERBASE_API_KEY=...
# 1. Créez une session keep-alive ET démarrez le traceur en une étape.
# Affiche l'id de session, le préfixe connectUrl, et une URL de debugger en direct que vous
# pouvez ouvrir dans un navigateur pour regarder l'exécution interactivement.
node scripts/bb-capture.mjs --new my-run
# 2. Lancez l'automation. bb-capture a tamponné l'id de session dans le 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. Arrêtez le traceur, découpez, puis récupérez les artefacts de plateforme et libérez.
node scripts/stop-capture.mjs my-run
node scripts/bisect-cdp.mjs my-run
node scripts/bb-finalize.mjs my-run --release
S'attacher à une session déjà en cours (par ex. une créée par votre worker de production) — bb-capture.mjs accepte un id de session à la place de --new :
# Sélectionnez une session en cours (filtrez côté client ; bb sessions list n'a pas de flag --status)
bb sessions list | jq -r '.[] | select(.status == "RUNNING") | .id'
node scripts/bb-capture.mjs <session-id> mid-flight-debug
# ...traceur s'exécute aux côtés du client automation existant ; pas de 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 running
Ce que vous obtenez de la plateforme Browserbase
bb-capture.mjs ajoute un bloc browserbase à manifest.json (id de session, projet, région, started_at, expires_at, URL de debugger). bb-finalize.mjs écrit :
<run>/browserbase/session.json— snapshot final debb sessions get(proxyBytes, status, ended_at, viewport, …)<run>/browserbase/logs.json— sortiebb sessions logs. Souvent vide. Le flux CDP danscdp/raw.ndjsonest la source de vérité ; ceci est un canal secondaire.<run>/browserbase/downloads.zip— fichiers téléchargés par la session, le cas échéant (le script descarte le zip vide de 22 octets que vous obtenez quand il n'y en a pas)
bb sessions recording (rejeu de session rrweb) est déprécié et n'est pas récupéré. Utilisez les screenshots + dumps DOM dans screenshots/ et dom/ comme source de vérité visuelle.
L'debugger_url en direct dans le manifest ouvre une vue Chrome DevTools interactive servie par Browserbase — pratique pour regarder une automation longue durée pendant que le traceur capture le flux vers le disque.
Arborescence du système de fichiers
.o11y/<run-id>/
manifest.json métadonnées d'exécution : target, domains, started_at, stopped_at
index.jsonl une ligne par sample : {ts, screenshot, dom, url}
cdp/
raw.ndjson flux CDP complet (un objet JSON par ligne)
summary.json {sessionId, duration, totalEvents, pages[]} — voir forme ci-dessous
network/{requests,responses,finished,failed,websocket}.jsonl seaux au niveau session (toujours écrits)
console/{logs,exceptions}.jsonl
runtime/all.jsonl
log/entries.jsonl
page/{navigations,lifecycle,frames,dialogs,all}.jsonl
dom/all.jsonl (seulement si O11Y_DOMAINS inclut DOM)
target/{attached,detached}.jsonl
pages/ tranches par page, indexées par délimiteurs frameNavigated top-level
000/ première page concrète
url.txt l'URL de cette page
summary.json bloc domains/network/timing de cette page (même forme qu'une entry pages[])
raw.jsonl flux scoped à cette page
network/, console/, page/, runtime/, log/, target/, dom/ mêmes seaux, seulement fichiers non-vides
screenshots/<iso-ts>.png un PNG par intervalle de sample
dom/<iso-ts>.html un dump HTML par intervalle de sample
browserbase/ ajouté par bb-finalize.mjs (exécutions Browserbase seulement)
session.json snapshot final de `bb sessions get` (proxyBytes, status, ended_at, …)
logs.json sortie `bb sessions logs` (souvent [])
downloads.zip sortie `bb sessions downloads get` (seulement si la session a téléchargé des fichiers)
Quand une exécution a été démarrée via bb-capture.mjs, manifest.json porte aussi un bloc browserbase top-level : session_id, project_id, region, started_at, expires_at, keep_alive, debugger_url.
Forme du summary
cdp/summary.json est le point d'entrée pour toute analyse : il contient les totaux au niveau session et un tableau pages[] indexé par Page.frameNavigated top-level. Les entrées par page sont émises dans l'ordre de navigation (page 0 = première URL concrète).
{
"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 } }
}
]
}
startMs / endMs / durationMs sont des ms wall-clock, dérivés de manifest.started_at plus l'offset du timestamp CDP monotone de chaque événement. domains[*] inclut seulement les clés errors/warnings quand elles sont non-zéro.
Exploration interactive avec query.mjs
Pour l'exploration interactive, utilisez scripts/query.mjs <run-id> <command> au lieu de mémoriser les chemins :
node scripts/query.mjs my-run list # table one-line de pages
node scripts/query.mjs my-run page 1 # summary complet pour page 1
node scripts/query.mjs my-run page 1 network/failed # cat failed.jsonl pour page 1
node scripts/query.mjs my-run errors # toutes les erreurs entre pages, attribuées par pid
node scripts/query.mjs my-run errors 2 # erreurs de page 2 seulement
node scripts/query.mjs my-run hosts # top hosts par nombre de requêtes
node scripts/query.mjs my-run host api.example.com # toutes les requests/responses pour un host
node scripts/query.mjs my-run summary # summary.json complet
Derrière les coulisses, il lit juste cdp/summary.json et l'arbre cdp/pages/<pid>/ — n'hésitez pas à le contourner avec du jq/rg brut une fois que vous connaissez la forme.
Recettes de traversal top
# Toutes les requêtes réseau échouées (utilisez jq -c pour garder le délimiteur de ligne)
jq -c '.params' .o11y/<run>/cdp/network/failed.jsonl
# Trouvez les requêtes vers un host spécifique
jq -c 'select(.params.request.url | test("api\\.example\\.com"))' \
.o11y/<run>/cdp/network/requests.jsonl
# Réponses 4xx/5xx
jq -c 'select(.params.response.status >= 400)
| {status: .params.response.status, url: .params.response.url}' \
.o11y/<run>/cdp/network/responses.jsonl
# Erreurs console seulement
jq -c 'select(.params.type == "error")' .o11y/<run>/cdp/console/logs.jsonl
# Séquence d'URLs visitées
jq -r '.params.frame.url' .o11y/<run>/cdp/page/navigations.jsonl
# Trouvez le screenshot pris le plus proche d'un timestamp (par ex., quand une exception s'est déclenchée)
ls .o11y/<run>/screenshots/ | sort | awk -v t=20260427T1714123NZ '
$0 >= t { print; exit }'
Voir REFERENCE.md pour la librairie de recettes jq complète et une carte bisect méthode par méthode. Voir EXAMPLES.md pour des scénarios de debug de bout en bout.
Bonnes pratiques
- Utilisez
bb-capture.mjssur Browserbase : il impose--keep-alive, récupère la connectUrl, capture l'URL de debugger, et tamponne le manifest. Le faire manuellement invite aux erreurs. - Ne
--releasepas une session que vous ne possédez pas :bb-finalize.mjs --releaseest pour les sessions que vous avez créées avec--new. Quand vous vous attachez à une session de production viabb-capture.mjs <session-id>, lancezbb-finalize.mjssans--releasepour que l'automation originale continue. - L'ordre compte pour remote : sur Browserbase, attachez le client automation principal avant (ou en même temps que) le traceur, et créez la session avec
--keep-alive. Sinon la session se termine dès que le WS du traceur ferme. - Ne pollez pas plus vite qu'~1s : chaque sample ouvre une connexion CDP ponctuelle et prend un screenshot de Chrome. 2s est un bon défaut.
- Choisissez les domaines délibérément : les défauts (
Network Console Runtime Log Page) couvrent la plupart des debogage. AjoutezDOMpour les mutations d'arbre DOM (très bruyant) viaO11Y_DOMAINS="$O11Y_DOMAINS DOM". - Utilisez
--connect <session-id>pour le client automation sur remote, pas un nouveaubrowse env remote(ce qui créerait une nouvelle session à chaque fois). - Lancez toujours
stop-capture.mjs, même après un crash, pour que les processus de fond ne persistent pas et que le manifest reçoivestopped_at. - Découpez une fois par exécution :
bisect-cdp.mjsest idempotent — il réécrit les fichiers par-seau depuisraw.ndjsonà chaque fois.
Dépannage
browse cdp exited immediately: signifie généralement que la cible est inaccessible (mauvais port) ou que la session Browserbase a déjà terminé. Pour remote, vérifiez avecbb sessions get <id>— sistatusestCOMPLETED, recréez avec--keep-aliveet attachez d'abord l'automation.raw.ndjsonvide bien que les processus s'exécutent : confirmez qu'un client CDP conduit réellement la page. Le traceur n'émet que les événements que le navigateur génère, donc un navigateur inactif produit ~5 lignes de messages attach/discover et rien d'autre.- Les screenshots se ressemblent tous : vérifiez
index.jsonl— siurlne change pas, la page ne s'est pas encore navigée. La boucle de polling s'exécute indépendamment du rythme de l'automation principale. - Browserbase session se termine mid-run : elle a probablement atteint
--timeout. Recréez avec un timeout plus élevé (BB_SESSION_TIMEOUT=1800 node scripts/bb-capture.mjs --new ...) ou supprimez le flag de timeout. bb-capture.mjs <id>dit "not RUNNING" : la session à laquelle vous avez essayé de vous attacher a terminé. Listez les candidats avecbb sessions list | jq '.[] | select(.status == "RUNNING")'et réessayez.browserbase/logs.jsonest un[]vide : attendu —bb sessions logsest sparse en pratique. Le flux CDP danscdp/raw.ndjsonest la source de vérité.- Où est l'enregistrement de session (rrweb)? :
bb sessions recordingest déprécié ; cette skill ne le récupère pas. Utilisez le flux de screenshots dansscreenshots/et les dumps DOM dansdom/.
Pour une référence complète, voir REFERENCE.md. Pour des exécutions de debug example, voir EXAMPLES.md.