| import crypto from "node:crypto"; |
| import path from "node:path"; |
| import type { BrowserRouteContext } from "../server-context.js"; |
| import { |
| readBody, |
| resolveTargetIdFromBody, |
| resolveTargetIdFromQuery, |
| withPlaywrightRouteContext, |
| } from "./agent.shared.js"; |
| import { resolveWritableOutputPathOrRespond } from "./output-paths.js"; |
| import { DEFAULT_TRACE_DIR } from "./path-output.js"; |
| import type { BrowserRouteRegistrar } from "./types.js"; |
| import { toBoolean, toStringOrEmpty } from "./utils.js"; |
|
|
| export function registerBrowserAgentDebugRoutes( |
| app: BrowserRouteRegistrar, |
| ctx: BrowserRouteContext, |
| ) { |
| app.get("/console", async (req, res) => { |
| const targetId = resolveTargetIdFromQuery(req.query); |
| const level = typeof req.query.level === "string" ? req.query.level : ""; |
|
|
| await withPlaywrightRouteContext({ |
| req, |
| res, |
| ctx, |
| targetId, |
| feature: "console messages", |
| run: async ({ cdpUrl, tab, pw }) => { |
| const messages = await pw.getConsoleMessagesViaPlaywright({ |
| cdpUrl, |
| targetId: tab.targetId, |
| level: level.trim() || undefined, |
| }); |
| res.json({ ok: true, messages, targetId: tab.targetId }); |
| }, |
| }); |
| }); |
|
|
| app.get("/errors", async (req, res) => { |
| const targetId = resolveTargetIdFromQuery(req.query); |
| const clear = toBoolean(req.query.clear) ?? false; |
|
|
| await withPlaywrightRouteContext({ |
| req, |
| res, |
| ctx, |
| targetId, |
| feature: "page errors", |
| run: async ({ cdpUrl, tab, pw }) => { |
| const result = await pw.getPageErrorsViaPlaywright({ |
| cdpUrl, |
| targetId: tab.targetId, |
| clear, |
| }); |
| res.json({ ok: true, targetId: tab.targetId, ...result }); |
| }, |
| }); |
| }); |
|
|
| app.get("/requests", async (req, res) => { |
| const targetId = resolveTargetIdFromQuery(req.query); |
| const filter = typeof req.query.filter === "string" ? req.query.filter : ""; |
| const clear = toBoolean(req.query.clear) ?? false; |
|
|
| await withPlaywrightRouteContext({ |
| req, |
| res, |
| ctx, |
| targetId, |
| feature: "network requests", |
| run: async ({ cdpUrl, tab, pw }) => { |
| const result = await pw.getNetworkRequestsViaPlaywright({ |
| cdpUrl, |
| targetId: tab.targetId, |
| filter: filter.trim() || undefined, |
| clear, |
| }); |
| res.json({ ok: true, targetId: tab.targetId, ...result }); |
| }, |
| }); |
| }); |
|
|
| app.post("/trace/start", async (req, res) => { |
| const body = readBody(req); |
| const targetId = resolveTargetIdFromBody(body); |
| const screenshots = toBoolean(body.screenshots) ?? undefined; |
| const snapshots = toBoolean(body.snapshots) ?? undefined; |
| const sources = toBoolean(body.sources) ?? undefined; |
|
|
| await withPlaywrightRouteContext({ |
| req, |
| res, |
| ctx, |
| targetId, |
| feature: "trace start", |
| run: async ({ cdpUrl, tab, pw }) => { |
| await pw.traceStartViaPlaywright({ |
| cdpUrl, |
| targetId: tab.targetId, |
| screenshots, |
| snapshots, |
| sources, |
| }); |
| res.json({ ok: true, targetId: tab.targetId }); |
| }, |
| }); |
| }); |
|
|
| app.post("/trace/stop", async (req, res) => { |
| const body = readBody(req); |
| const targetId = resolveTargetIdFromBody(body); |
| const out = toStringOrEmpty(body.path) || ""; |
|
|
| await withPlaywrightRouteContext({ |
| req, |
| res, |
| ctx, |
| targetId, |
| feature: "trace stop", |
| run: async ({ cdpUrl, tab, pw }) => { |
| const id = crypto.randomUUID(); |
| const tracePath = await resolveWritableOutputPathOrRespond({ |
| res, |
| rootDir: DEFAULT_TRACE_DIR, |
| requestedPath: out, |
| scopeLabel: "trace directory", |
| defaultFileName: `browser-trace-${id}.zip`, |
| ensureRootDir: true, |
| }); |
| if (!tracePath) { |
| return; |
| } |
| await pw.traceStopViaPlaywright({ |
| cdpUrl, |
| targetId: tab.targetId, |
| path: tracePath, |
| }); |
| res.json({ |
| ok: true, |
| targetId: tab.targetId, |
| path: path.resolve(tracePath), |
| }); |
| }, |
| }); |
| }); |
| } |
|
|