import type { Command } from "commander"; import { resolveBrowserControlUrl } from "../browser/client.js"; import { browserHighlight, browserPageErrors, browserRequests, browserTraceStart, browserTraceStop, } from "../browser/client-actions.js"; import { danger } from "../globals.js"; import { defaultRuntime } from "../runtime.js"; import type { BrowserParentOpts } from "./browser-cli-shared.js"; export function registerBrowserDebugCommands( browser: Command, parentOpts: (cmd: Command) => BrowserParentOpts, ) { browser .command("highlight") .description("Highlight an element by ref") .argument("", "Ref id from snapshot") .option("--target-id ", "CDP target id (or unique prefix)") .action(async (ref: string, opts, cmd) => { const parent = parentOpts(cmd); const baseUrl = resolveBrowserControlUrl(parent?.url); const profile = parent?.browserProfile; try { const result = await browserHighlight(baseUrl, { ref: ref.trim(), targetId: opts.targetId?.trim() || undefined, profile, }); if (parent?.json) { defaultRuntime.log(JSON.stringify(result, null, 2)); return; } defaultRuntime.log(`highlighted ${ref.trim()}`); } catch (err) { defaultRuntime.error(danger(String(err))); defaultRuntime.exit(1); } }); browser .command("errors") .description("Get recent page errors") .option("--clear", "Clear stored errors after reading", false) .option("--target-id ", "CDP target id (or unique prefix)") .action(async (opts, cmd) => { const parent = parentOpts(cmd); const baseUrl = resolveBrowserControlUrl(parent?.url); const profile = parent?.browserProfile; try { const result = await browserPageErrors(baseUrl, { targetId: opts.targetId?.trim() || undefined, clear: Boolean(opts.clear), profile, }); if (parent?.json) { defaultRuntime.log(JSON.stringify(result, null, 2)); return; } if (!result.errors.length) { defaultRuntime.log("No page errors."); return; } defaultRuntime.log( result.errors .map( (e) => `${e.timestamp} ${e.name ? `${e.name}: ` : ""}${e.message}`, ) .join("\n"), ); } catch (err) { defaultRuntime.error(danger(String(err))); defaultRuntime.exit(1); } }); browser .command("requests") .description("Get recent network requests (best-effort)") .option("--filter ", "Only show URLs that contain this substring") .option("--clear", "Clear stored requests after reading", false) .option("--target-id ", "CDP target id (or unique prefix)") .action(async (opts, cmd) => { const parent = parentOpts(cmd); const baseUrl = resolveBrowserControlUrl(parent?.url); const profile = parent?.browserProfile; try { const result = await browserRequests(baseUrl, { targetId: opts.targetId?.trim() || undefined, filter: opts.filter?.trim() || undefined, clear: Boolean(opts.clear), profile, }); if (parent?.json) { defaultRuntime.log(JSON.stringify(result, null, 2)); return; } if (!result.requests.length) { defaultRuntime.log("No requests recorded."); return; } defaultRuntime.log( result.requests .map((r) => { const status = typeof r.status === "number" ? ` ${r.status}` : ""; const ok = r.ok === true ? " ok" : r.ok === false ? " fail" : ""; const fail = r.failureText ? ` (${r.failureText})` : ""; return `${r.timestamp} ${r.method}${status}${ok} ${r.url}${fail}`; }) .join("\n"), ); } catch (err) { defaultRuntime.error(danger(String(err))); defaultRuntime.exit(1); } }); const trace = browser .command("trace") .description("Record a Playwright trace"); trace .command("start") .description("Start trace recording") .option("--target-id ", "CDP target id (or unique prefix)") .option("--no-screenshots", "Disable screenshots") .option("--no-snapshots", "Disable snapshots") .option("--sources", "Include sources (bigger traces)", false) .action(async (opts, cmd) => { const parent = parentOpts(cmd); const baseUrl = resolveBrowserControlUrl(parent?.url); const profile = parent?.browserProfile; try { const result = await browserTraceStart(baseUrl, { targetId: opts.targetId?.trim() || undefined, screenshots: Boolean(opts.screenshots), snapshots: Boolean(opts.snapshots), sources: Boolean(opts.sources), profile, }); if (parent?.json) { defaultRuntime.log(JSON.stringify(result, null, 2)); return; } defaultRuntime.log("trace started"); } catch (err) { defaultRuntime.error(danger(String(err))); defaultRuntime.exit(1); } }); trace .command("stop") .description("Stop trace recording and write a .zip") .option("--out ", "Output path for the trace zip") .option("--target-id ", "CDP target id (or unique prefix)") .action(async (opts, cmd) => { const parent = parentOpts(cmd); const baseUrl = resolveBrowserControlUrl(parent?.url); const profile = parent?.browserProfile; try { const result = await browserTraceStop(baseUrl, { targetId: opts.targetId?.trim() || undefined, path: opts.out?.trim() || undefined, profile, }); if (parent?.json) { defaultRuntime.log(JSON.stringify(result, null, 2)); return; } defaultRuntime.log(`TRACE:${result.path}`); } catch (err) { defaultRuntime.error(danger(String(err))); defaultRuntime.exit(1); } }); }