From 42f34af776ee7b34962533b3b631cc4df51877b9 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 18 Feb 2026 22:18:29 +0000 Subject: [PATCH] refactor(browser): share basic and tabs route helpers --- src/browser/routes/basic.ts | 90 +++++++++++++++++++++---------------- src/browser/routes/tabs.ts | 82 +++++++++++++++++++++++---------- 2 files changed, 110 insertions(+), 62 deletions(-) diff --git a/src/browser/routes/basic.ts b/src/browser/routes/basic.ts index 26df2d1bb3c..76a4c3f9d6a 100644 --- a/src/browser/routes/basic.ts +++ b/src/browser/routes/basic.ts @@ -1,9 +1,27 @@ import { resolveBrowserExecutableForPlatform } from "../chrome.executables.js"; import { createBrowserProfilesService } from "../profiles-service.js"; -import type { BrowserRouteContext } from "../server-context.js"; -import type { BrowserRouteRegistrar } from "./types.js"; +import type { BrowserRouteContext, ProfileContext } from "../server-context.js"; +import { resolveProfileContext } from "./agent.shared.js"; +import type { BrowserRequest, BrowserResponse, BrowserRouteRegistrar } from "./types.js"; import { getProfileContext, jsonError, toStringOrEmpty } from "./utils.js"; +async function withBasicProfileRoute(params: { + req: BrowserRequest; + res: BrowserResponse; + ctx: BrowserRouteContext; + run: (profileCtx: ProfileContext) => Promise; +}) { + const profileCtx = resolveProfileContext(params.req, params.res, params.ctx); + if (!profileCtx) { + return; + } + try { + await params.run(profileCtx); + } catch (err) { + jsonError(params.res, 500, String(err)); + } +} + export function registerBrowserBasicRoutes(app: BrowserRouteRegistrar, ctx: BrowserRouteContext) { // List all profiles with their status app.get("/profiles", async (_req, res) => { @@ -74,51 +92,45 @@ export function registerBrowserBasicRoutes(app: BrowserRouteRegistrar, ctx: Brow // Start browser (profile-aware) app.post("/start", async (req, res) => { - const profileCtx = getProfileContext(req, ctx); - if ("error" in profileCtx) { - return jsonError(res, profileCtx.status, profileCtx.error); - } - - try { - await profileCtx.ensureBrowserAvailable(); - res.json({ ok: true, profile: profileCtx.profile.name }); - } catch (err) { - jsonError(res, 500, String(err)); - } + await withBasicProfileRoute({ + req, + res, + ctx, + run: async (profileCtx) => { + await profileCtx.ensureBrowserAvailable(); + res.json({ ok: true, profile: profileCtx.profile.name }); + }, + }); }); // Stop browser (profile-aware) app.post("/stop", async (req, res) => { - const profileCtx = getProfileContext(req, ctx); - if ("error" in profileCtx) { - return jsonError(res, profileCtx.status, profileCtx.error); - } - - try { - const result = await profileCtx.stopRunningBrowser(); - res.json({ - ok: true, - stopped: result.stopped, - profile: profileCtx.profile.name, - }); - } catch (err) { - jsonError(res, 500, String(err)); - } + await withBasicProfileRoute({ + req, + res, + ctx, + run: async (profileCtx) => { + const result = await profileCtx.stopRunningBrowser(); + res.json({ + ok: true, + stopped: result.stopped, + profile: profileCtx.profile.name, + }); + }, + }); }); // Reset profile (profile-aware) app.post("/reset-profile", async (req, res) => { - const profileCtx = getProfileContext(req, ctx); - if ("error" in profileCtx) { - return jsonError(res, profileCtx.status, profileCtx.error); - } - - try { - const result = await profileCtx.resetProfile(); - res.json({ ok: true, profile: profileCtx.profile.name, ...result }); - } catch (err) { - jsonError(res, 500, String(err)); - } + await withBasicProfileRoute({ + req, + res, + ctx, + run: async (profileCtx) => { + const result = await profileCtx.resetProfile(); + res.json({ ok: true, profile: profileCtx.profile.name, ...result }); + }, + }); }); // Create a new profile diff --git a/src/browser/routes/tabs.ts b/src/browser/routes/tabs.ts index 7a2de725dfe..c0644a97d1a 100644 --- a/src/browser/routes/tabs.ts +++ b/src/browser/routes/tabs.ts @@ -48,6 +48,52 @@ async function withTabsProfileRoute(params: { } } +async function ensureBrowserRunning(profileCtx: ProfileContext, res: BrowserResponse) { + if (!(await profileCtx.isReachable(300))) { + jsonError(res, 409, "browser not running"); + return false; + } + return true; +} + +function resolveIndexedTab( + tabs: Awaited>, + index: number | undefined, +) { + return typeof index === "number" ? tabs[index] : tabs.at(0); +} + +function parseRequiredTargetId(res: BrowserResponse, rawTargetId: unknown): string | null { + const targetId = toStringOrEmpty(rawTargetId); + if (!targetId) { + jsonError(res, 400, "targetId is required"); + return null; + } + return targetId; +} + +async function runTabTargetMutation(params: { + req: BrowserRequest; + res: BrowserResponse; + ctx: BrowserRouteContext; + targetId: string; + mutate: (profileCtx: ProfileContext, targetId: string) => Promise; +}) { + await withTabsProfileRoute({ + req: params.req, + res: params.res, + ctx: params.ctx, + mapTabError: true, + run: async (profileCtx) => { + if (!(await ensureBrowserRunning(profileCtx, params.res))) { + return; + } + await params.mutate(profileCtx, params.targetId); + params.res.json({ ok: true }); + }, + }); +} + export function registerBrowserTabRoutes(app: BrowserRouteRegistrar, ctx: BrowserRouteContext) { app.get("/tabs", async (req, res) => { await withTabsProfileRoute({ @@ -84,43 +130,33 @@ export function registerBrowserTabRoutes(app: BrowserRouteRegistrar, ctx: Browse }); app.post("/tabs/focus", async (req, res) => { - const targetId = toStringOrEmpty((req.body as { targetId?: unknown })?.targetId); + const targetId = parseRequiredTargetId(res, (req.body as { targetId?: unknown })?.targetId); if (!targetId) { - return jsonError(res, 400, "targetId is required"); + return; } - - await withTabsProfileRoute({ + await runTabTargetMutation({ req, res, ctx, - mapTabError: true, - run: async (profileCtx) => { - if (!(await profileCtx.isReachable(300))) { - return jsonError(res, 409, "browser not running"); - } - await profileCtx.focusTab(targetId); - res.json({ ok: true }); + targetId, + mutate: async (profileCtx, id) => { + await profileCtx.focusTab(id); }, }); }); app.delete("/tabs/:targetId", async (req, res) => { - const targetId = toStringOrEmpty(req.params.targetId); + const targetId = parseRequiredTargetId(res, req.params.targetId); if (!targetId) { - return jsonError(res, 400, "targetId is required"); + return; } - - await withTabsProfileRoute({ + await runTabTargetMutation({ req, res, ctx, - mapTabError: true, - run: async (profileCtx) => { - if (!(await profileCtx.isReachable(300))) { - return jsonError(res, 409, "browser not running"); - } - await profileCtx.closeTab(targetId); - res.json({ ok: true }); + targetId, + mutate: async (profileCtx, id) => { + await profileCtx.closeTab(id); }, }); }); @@ -152,7 +188,7 @@ export function registerBrowserTabRoutes(app: BrowserRouteRegistrar, ctx: Browse if (action === "close") { const tabs = await profileCtx.listTabs(); - const target = typeof index === "number" ? tabs[index] : tabs.at(0); + const target = resolveIndexedTab(tabs, index); if (!target) { return jsonError(res, 404, "tab not found"); }