mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 08:02:42 +00:00
refactor(browser): share basic and tabs route helpers
This commit is contained in:
@@ -1,9 +1,27 @@
|
|||||||
import { resolveBrowserExecutableForPlatform } from "../chrome.executables.js";
|
import { resolveBrowserExecutableForPlatform } from "../chrome.executables.js";
|
||||||
import { createBrowserProfilesService } from "../profiles-service.js";
|
import { createBrowserProfilesService } from "../profiles-service.js";
|
||||||
import type { BrowserRouteContext } from "../server-context.js";
|
import type { BrowserRouteContext, ProfileContext } from "../server-context.js";
|
||||||
import type { BrowserRouteRegistrar } from "./types.js";
|
import { resolveProfileContext } from "./agent.shared.js";
|
||||||
|
import type { BrowserRequest, BrowserResponse, BrowserRouteRegistrar } from "./types.js";
|
||||||
import { getProfileContext, jsonError, toStringOrEmpty } from "./utils.js";
|
import { getProfileContext, jsonError, toStringOrEmpty } from "./utils.js";
|
||||||
|
|
||||||
|
async function withBasicProfileRoute(params: {
|
||||||
|
req: BrowserRequest;
|
||||||
|
res: BrowserResponse;
|
||||||
|
ctx: BrowserRouteContext;
|
||||||
|
run: (profileCtx: ProfileContext) => Promise<void>;
|
||||||
|
}) {
|
||||||
|
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) {
|
export function registerBrowserBasicRoutes(app: BrowserRouteRegistrar, ctx: BrowserRouteContext) {
|
||||||
// List all profiles with their status
|
// List all profiles with their status
|
||||||
app.get("/profiles", async (_req, res) => {
|
app.get("/profiles", async (_req, res) => {
|
||||||
@@ -74,51 +92,45 @@ export function registerBrowserBasicRoutes(app: BrowserRouteRegistrar, ctx: Brow
|
|||||||
|
|
||||||
// Start browser (profile-aware)
|
// Start browser (profile-aware)
|
||||||
app.post("/start", async (req, res) => {
|
app.post("/start", async (req, res) => {
|
||||||
const profileCtx = getProfileContext(req, ctx);
|
await withBasicProfileRoute({
|
||||||
if ("error" in profileCtx) {
|
req,
|
||||||
return jsonError(res, profileCtx.status, profileCtx.error);
|
res,
|
||||||
}
|
ctx,
|
||||||
|
run: async (profileCtx) => {
|
||||||
try {
|
await profileCtx.ensureBrowserAvailable();
|
||||||
await profileCtx.ensureBrowserAvailable();
|
res.json({ ok: true, profile: profileCtx.profile.name });
|
||||||
res.json({ ok: true, profile: profileCtx.profile.name });
|
},
|
||||||
} catch (err) {
|
});
|
||||||
jsonError(res, 500, String(err));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Stop browser (profile-aware)
|
// Stop browser (profile-aware)
|
||||||
app.post("/stop", async (req, res) => {
|
app.post("/stop", async (req, res) => {
|
||||||
const profileCtx = getProfileContext(req, ctx);
|
await withBasicProfileRoute({
|
||||||
if ("error" in profileCtx) {
|
req,
|
||||||
return jsonError(res, profileCtx.status, profileCtx.error);
|
res,
|
||||||
}
|
ctx,
|
||||||
|
run: async (profileCtx) => {
|
||||||
try {
|
const result = await profileCtx.stopRunningBrowser();
|
||||||
const result = await profileCtx.stopRunningBrowser();
|
res.json({
|
||||||
res.json({
|
ok: true,
|
||||||
ok: true,
|
stopped: result.stopped,
|
||||||
stopped: result.stopped,
|
profile: profileCtx.profile.name,
|
||||||
profile: profileCtx.profile.name,
|
});
|
||||||
});
|
},
|
||||||
} catch (err) {
|
});
|
||||||
jsonError(res, 500, String(err));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset profile (profile-aware)
|
// Reset profile (profile-aware)
|
||||||
app.post("/reset-profile", async (req, res) => {
|
app.post("/reset-profile", async (req, res) => {
|
||||||
const profileCtx = getProfileContext(req, ctx);
|
await withBasicProfileRoute({
|
||||||
if ("error" in profileCtx) {
|
req,
|
||||||
return jsonError(res, profileCtx.status, profileCtx.error);
|
res,
|
||||||
}
|
ctx,
|
||||||
|
run: async (profileCtx) => {
|
||||||
try {
|
const result = await profileCtx.resetProfile();
|
||||||
const result = await profileCtx.resetProfile();
|
res.json({ ok: true, profile: profileCtx.profile.name, ...result });
|
||||||
res.json({ ok: true, profile: profileCtx.profile.name, ...result });
|
},
|
||||||
} catch (err) {
|
});
|
||||||
jsonError(res, 500, String(err));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a new profile
|
// Create a new profile
|
||||||
|
|||||||
@@ -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<ReturnType<ProfileContext["listTabs"]>>,
|
||||||
|
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<void>;
|
||||||
|
}) {
|
||||||
|
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) {
|
export function registerBrowserTabRoutes(app: BrowserRouteRegistrar, ctx: BrowserRouteContext) {
|
||||||
app.get("/tabs", async (req, res) => {
|
app.get("/tabs", async (req, res) => {
|
||||||
await withTabsProfileRoute({
|
await withTabsProfileRoute({
|
||||||
@@ -84,43 +130,33 @@ export function registerBrowserTabRoutes(app: BrowserRouteRegistrar, ctx: Browse
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.post("/tabs/focus", async (req, res) => {
|
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) {
|
if (!targetId) {
|
||||||
return jsonError(res, 400, "targetId is required");
|
return;
|
||||||
}
|
}
|
||||||
|
await runTabTargetMutation({
|
||||||
await withTabsProfileRoute({
|
|
||||||
req,
|
req,
|
||||||
res,
|
res,
|
||||||
ctx,
|
ctx,
|
||||||
mapTabError: true,
|
targetId,
|
||||||
run: async (profileCtx) => {
|
mutate: async (profileCtx, id) => {
|
||||||
if (!(await profileCtx.isReachable(300))) {
|
await profileCtx.focusTab(id);
|
||||||
return jsonError(res, 409, "browser not running");
|
|
||||||
}
|
|
||||||
await profileCtx.focusTab(targetId);
|
|
||||||
res.json({ ok: true });
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete("/tabs/:targetId", async (req, res) => {
|
app.delete("/tabs/:targetId", async (req, res) => {
|
||||||
const targetId = toStringOrEmpty(req.params.targetId);
|
const targetId = parseRequiredTargetId(res, req.params.targetId);
|
||||||
if (!targetId) {
|
if (!targetId) {
|
||||||
return jsonError(res, 400, "targetId is required");
|
return;
|
||||||
}
|
}
|
||||||
|
await runTabTargetMutation({
|
||||||
await withTabsProfileRoute({
|
|
||||||
req,
|
req,
|
||||||
res,
|
res,
|
||||||
ctx,
|
ctx,
|
||||||
mapTabError: true,
|
targetId,
|
||||||
run: async (profileCtx) => {
|
mutate: async (profileCtx, id) => {
|
||||||
if (!(await profileCtx.isReachable(300))) {
|
await profileCtx.closeTab(id);
|
||||||
return jsonError(res, 409, "browser not running");
|
|
||||||
}
|
|
||||||
await profileCtx.closeTab(targetId);
|
|
||||||
res.json({ ok: true });
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -152,7 +188,7 @@ export function registerBrowserTabRoutes(app: BrowserRouteRegistrar, ctx: Browse
|
|||||||
|
|
||||||
if (action === "close") {
|
if (action === "close") {
|
||||||
const tabs = await profileCtx.listTabs();
|
const tabs = await profileCtx.listTabs();
|
||||||
const target = typeof index === "number" ? tabs[index] : tabs.at(0);
|
const target = resolveIndexedTab(tabs, index);
|
||||||
if (!target) {
|
if (!target) {
|
||||||
return jsonError(res, 404, "tab not found");
|
return jsonError(res, 404, "tab not found");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user