diff --git a/src/browser/routes/agent.snapshot.ts b/src/browser/routes/agent.snapshot.ts index dd7d28d6315..cb3c94c4417 100644 --- a/src/browser/routes/agent.snapshot.ts +++ b/src/browser/routes/agent.snapshot.ts @@ -18,82 +18,90 @@ import { readBody, requirePwAi, resolveProfileContext, + withPlaywrightRouteContext, + withRouteTabContext, } from "./agent.shared.js"; -import type { BrowserRouteRegistrar } from "./types.js"; +import type { BrowserResponse, BrowserRouteRegistrar } from "./types.js"; import { jsonError, toBoolean, toNumber, toStringOrEmpty } from "./utils.js"; +async function saveBrowserMediaResponse(params: { + res: BrowserResponse; + buffer: Buffer; + contentType: string; + maxBytes: number; + targetId: string; + url: string; +}) { + await ensureMediaDir(); + const saved = await saveMediaBuffer( + params.buffer, + params.contentType, + "browser", + params.maxBytes, + ); + params.res.json({ + ok: true, + path: path.resolve(saved.path), + targetId: params.targetId, + url: params.url, + }); +} + export function registerBrowserAgentSnapshotRoutes( app: BrowserRouteRegistrar, ctx: BrowserRouteContext, ) { app.post("/navigate", async (req, res) => { - const profileCtx = resolveProfileContext(req, res, ctx); - if (!profileCtx) { - return; - } const body = readBody(req); const url = toStringOrEmpty(body.url); const targetId = toStringOrEmpty(body.targetId) || undefined; if (!url) { return jsonError(res, 400, "url is required"); } - try { - const tab = await profileCtx.ensureTabAvailable(targetId); - const pw = await requirePwAi(res, "navigate"); - if (!pw) { - return; - } - const result = await pw.navigateViaPlaywright({ - cdpUrl: profileCtx.profile.cdpUrl, - targetId: tab.targetId, - url, - }); - res.json({ ok: true, targetId: tab.targetId, ...result }); - } catch (err) { - handleRouteError(ctx, res, err); - } + await withPlaywrightRouteContext({ + req, + res, + ctx, + targetId, + feature: "navigate", + run: async ({ cdpUrl, tab, pw }) => { + const result = await pw.navigateViaPlaywright({ + cdpUrl, + targetId: tab.targetId, + url, + }); + res.json({ ok: true, targetId: tab.targetId, ...result }); + }, + }); }); app.post("/pdf", async (req, res) => { - const profileCtx = resolveProfileContext(req, res, ctx); - if (!profileCtx) { - return; - } const body = readBody(req); const targetId = toStringOrEmpty(body.targetId) || undefined; - try { - const tab = await profileCtx.ensureTabAvailable(targetId); - const pw = await requirePwAi(res, "pdf"); - if (!pw) { - return; - } - const pdf = await pw.pdfViaPlaywright({ - cdpUrl: profileCtx.profile.cdpUrl, - targetId: tab.targetId, - }); - await ensureMediaDir(); - const saved = await saveMediaBuffer( - pdf.buffer, - "application/pdf", - "browser", - pdf.buffer.byteLength, - ); - res.json({ - ok: true, - path: path.resolve(saved.path), - targetId: tab.targetId, - url: tab.url, - }); - } catch (err) { - handleRouteError(ctx, res, err); - } + await withPlaywrightRouteContext({ + req, + res, + ctx, + targetId, + feature: "pdf", + run: async ({ cdpUrl, tab, pw }) => { + const pdf = await pw.pdfViaPlaywright({ + cdpUrl, + targetId: tab.targetId, + }); + await saveBrowserMediaResponse({ + res, + buffer: pdf.buffer, + contentType: "application/pdf", + maxBytes: pdf.buffer.byteLength, + targetId: tab.targetId, + url: tab.url, + }); + }, + }); }); app.post("/screenshot", async (req, res) => { - const profileCtx = resolveProfileContext(req, res, ctx); - if (!profileCtx) { - return; - } const body = readBody(req); const targetId = toStringOrEmpty(body.targetId) || undefined; const fullPage = toBoolean(body.fullPage) ?? false; @@ -105,54 +113,55 @@ export function registerBrowserAgentSnapshotRoutes( return jsonError(res, 400, "fullPage is not supported for element screenshots"); } - try { - const tab = await profileCtx.ensureTabAvailable(targetId); - let buffer: Buffer; - const shouldUsePlaywright = - profileCtx.profile.driver === "extension" || !tab.wsUrl || Boolean(ref) || Boolean(element); - if (shouldUsePlaywright) { - const pw = await requirePwAi(res, "screenshot"); - if (!pw) { - return; + await withRouteTabContext({ + req, + res, + ctx, + targetId, + run: async ({ profileCtx, tab, cdpUrl }) => { + let buffer: Buffer; + const shouldUsePlaywright = + profileCtx.profile.driver === "extension" || + !tab.wsUrl || + Boolean(ref) || + Boolean(element); + if (shouldUsePlaywright) { + const pw = await requirePwAi(res, "screenshot"); + if (!pw) { + return; + } + const snap = await pw.takeScreenshotViaPlaywright({ + cdpUrl, + targetId: tab.targetId, + ref, + element, + fullPage, + type, + }); + buffer = snap.buffer; + } else { + buffer = await captureScreenshot({ + wsUrl: tab.wsUrl ?? "", + fullPage, + format: type, + quality: type === "jpeg" ? 85 : undefined, + }); } - const snap = await pw.takeScreenshotViaPlaywright({ - cdpUrl: profileCtx.profile.cdpUrl, - targetId: tab.targetId, - ref, - element, - fullPage, - type, - }); - buffer = snap.buffer; - } else { - buffer = await captureScreenshot({ - wsUrl: tab.wsUrl ?? "", - fullPage, - format: type, - quality: type === "jpeg" ? 85 : undefined, - }); - } - const normalized = await normalizeBrowserScreenshot(buffer, { - maxSide: DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE, - maxBytes: DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES, - }); - await ensureMediaDir(); - const saved = await saveMediaBuffer( - normalized.buffer, - normalized.contentType ?? `image/${type}`, - "browser", - DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES, - ); - res.json({ - ok: true, - path: path.resolve(saved.path), - targetId: tab.targetId, - url: tab.url, - }); - } catch (err) { - handleRouteError(ctx, res, err); - } + const normalized = await normalizeBrowserScreenshot(buffer, { + maxSide: DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE, + maxBytes: DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES, + }); + await saveBrowserMediaResponse({ + res, + buffer: normalized.buffer, + contentType: normalized.contentType ?? `image/${type}`, + maxBytes: DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES, + targetId: tab.targetId, + url: tab.url, + }); + }, + }); }); app.get("/snapshot", async (req, res) => {