mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 09:41:43 +00:00
refactor: route browser control via gateway/node
This commit is contained in:
@@ -1,25 +1,16 @@
|
||||
import type { Command } from "commander";
|
||||
import type { BrowserTab } from "../browser/client.js";
|
||||
import {
|
||||
browserCloseTab,
|
||||
browserCreateProfile,
|
||||
browserDeleteProfile,
|
||||
browserFocusTab,
|
||||
browserOpenTab,
|
||||
browserProfiles,
|
||||
browserResetProfile,
|
||||
browserStart,
|
||||
browserStatus,
|
||||
browserStop,
|
||||
browserTabAction,
|
||||
browserTabs,
|
||||
resolveBrowserControlUrl,
|
||||
import type {
|
||||
BrowserCreateProfileResult,
|
||||
BrowserDeleteProfileResult,
|
||||
BrowserResetProfileResult,
|
||||
BrowserStatus,
|
||||
BrowserTab,
|
||||
ProfileStatus,
|
||||
} from "../browser/client.js";
|
||||
import { browserAct } from "../browser/client-actions-core.js";
|
||||
import { danger, info } from "../globals.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { shortenHomePath } from "../utils.js";
|
||||
import type { BrowserParentOpts } from "./browser-cli-shared.js";
|
||||
import { callBrowserRequest, type BrowserParentOpts } from "./browser-cli-shared.js";
|
||||
import { runCommandWithRuntime } from "./cli-utils.js";
|
||||
|
||||
function runBrowserCommand(action: () => Promise<void>) {
|
||||
@@ -38,11 +29,18 @@ export function registerBrowserManageCommands(
|
||||
.description("Show browser status")
|
||||
.action(async (_opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
await runBrowserCommand(async () => {
|
||||
const status = await browserStatus(baseUrl, {
|
||||
profile: parent?.browserProfile,
|
||||
});
|
||||
const status = await callBrowserRequest<BrowserStatus>(
|
||||
parent,
|
||||
{
|
||||
method: "GET",
|
||||
path: "/",
|
||||
query: parent?.browserProfile ? { profile: parent.browserProfile } : undefined,
|
||||
},
|
||||
{
|
||||
timeoutMs: 1500,
|
||||
},
|
||||
);
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify(status, null, 2));
|
||||
return;
|
||||
@@ -54,7 +52,6 @@ export function registerBrowserManageCommands(
|
||||
`profile: ${status.profile ?? "clawd"}`,
|
||||
`enabled: ${status.enabled}`,
|
||||
`running: ${status.running}`,
|
||||
`controlUrl: ${status.controlUrl}`,
|
||||
`cdpPort: ${status.cdpPort}`,
|
||||
`cdpUrl: ${status.cdpUrl ?? `http://127.0.0.1:${status.cdpPort}`}`,
|
||||
`browser: ${status.chosenBrowser ?? "unknown"}`,
|
||||
@@ -72,11 +69,26 @@ export function registerBrowserManageCommands(
|
||||
.description("Start the browser (no-op if already running)")
|
||||
.action(async (_opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
const profile = parent?.browserProfile;
|
||||
await runBrowserCommand(async () => {
|
||||
await browserStart(baseUrl, { profile });
|
||||
const status = await browserStatus(baseUrl, { profile });
|
||||
await callBrowserRequest(
|
||||
parent,
|
||||
{
|
||||
method: "POST",
|
||||
path: "/start",
|
||||
query: profile ? { profile } : undefined,
|
||||
},
|
||||
{ timeoutMs: 15000 },
|
||||
);
|
||||
const status = await callBrowserRequest<BrowserStatus>(
|
||||
parent,
|
||||
{
|
||||
method: "GET",
|
||||
path: "/",
|
||||
query: profile ? { profile } : undefined,
|
||||
},
|
||||
{ timeoutMs: 1500 },
|
||||
);
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify(status, null, 2));
|
||||
return;
|
||||
@@ -91,11 +103,26 @@ export function registerBrowserManageCommands(
|
||||
.description("Stop the browser (best-effort)")
|
||||
.action(async (_opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
const profile = parent?.browserProfile;
|
||||
await runBrowserCommand(async () => {
|
||||
await browserStop(baseUrl, { profile });
|
||||
const status = await browserStatus(baseUrl, { profile });
|
||||
await callBrowserRequest(
|
||||
parent,
|
||||
{
|
||||
method: "POST",
|
||||
path: "/stop",
|
||||
query: profile ? { profile } : undefined,
|
||||
},
|
||||
{ timeoutMs: 15000 },
|
||||
);
|
||||
const status = await callBrowserRequest<BrowserStatus>(
|
||||
parent,
|
||||
{
|
||||
method: "GET",
|
||||
path: "/",
|
||||
query: profile ? { profile } : undefined,
|
||||
},
|
||||
{ timeoutMs: 1500 },
|
||||
);
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify(status, null, 2));
|
||||
return;
|
||||
@@ -110,10 +137,17 @@ export function registerBrowserManageCommands(
|
||||
.description("Reset browser profile (moves it to Trash)")
|
||||
.action(async (_opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
const profile = parent?.browserProfile;
|
||||
await runBrowserCommand(async () => {
|
||||
const result = await browserResetProfile(baseUrl, { profile });
|
||||
const result = await callBrowserRequest<BrowserResetProfileResult>(
|
||||
parent,
|
||||
{
|
||||
method: "POST",
|
||||
path: "/reset-profile",
|
||||
query: profile ? { profile } : undefined,
|
||||
},
|
||||
{ timeoutMs: 20000 },
|
||||
);
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
@@ -132,10 +166,18 @@ export function registerBrowserManageCommands(
|
||||
.description("List open tabs")
|
||||
.action(async (_opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
const profile = parent?.browserProfile;
|
||||
await runBrowserCommand(async () => {
|
||||
const tabs = await browserTabs(baseUrl, { profile });
|
||||
const result = await callBrowserRequest<{ running: boolean; tabs: BrowserTab[] }>(
|
||||
parent,
|
||||
{
|
||||
method: "GET",
|
||||
path: "/tabs",
|
||||
query: profile ? { profile } : undefined,
|
||||
},
|
||||
{ timeoutMs: 3000 },
|
||||
);
|
||||
const tabs = result.tabs ?? [];
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify({ tabs }, null, 2));
|
||||
return;
|
||||
@@ -159,13 +201,20 @@ export function registerBrowserManageCommands(
|
||||
.description("Tab shortcuts (index-based)")
|
||||
.action(async (_opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
const profile = parent?.browserProfile;
|
||||
await runBrowserCommand(async () => {
|
||||
const result = (await browserTabAction(baseUrl, {
|
||||
action: "list",
|
||||
profile,
|
||||
})) as { ok: true; tabs: BrowserTab[] };
|
||||
const result = await callBrowserRequest<{ ok: true; tabs: BrowserTab[] }>(
|
||||
parent,
|
||||
{
|
||||
method: "POST",
|
||||
path: "/tabs/action",
|
||||
query: profile ? { profile } : undefined,
|
||||
body: {
|
||||
action: "list",
|
||||
},
|
||||
},
|
||||
{ timeoutMs: 10_000 },
|
||||
);
|
||||
const tabs = result.tabs ?? [];
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify({ tabs }, null, 2));
|
||||
@@ -190,13 +239,18 @@ export function registerBrowserManageCommands(
|
||||
.description("Open a new tab (about:blank)")
|
||||
.action(async (_opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
const profile = parent?.browserProfile;
|
||||
await runBrowserCommand(async () => {
|
||||
const result = await browserTabAction(baseUrl, {
|
||||
action: "new",
|
||||
profile,
|
||||
});
|
||||
const result = await callBrowserRequest(
|
||||
parent,
|
||||
{
|
||||
method: "POST",
|
||||
path: "/tabs/action",
|
||||
query: profile ? { profile } : undefined,
|
||||
body: { action: "new" },
|
||||
},
|
||||
{ timeoutMs: 10_000 },
|
||||
);
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
@@ -211,7 +265,6 @@ export function registerBrowserManageCommands(
|
||||
.argument("<index>", "Tab index (1-based)", (v: string) => Number(v))
|
||||
.action(async (index: number, _opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
const profile = parent?.browserProfile;
|
||||
if (!Number.isFinite(index) || index < 1) {
|
||||
defaultRuntime.error(danger("index must be a positive number"));
|
||||
@@ -219,11 +272,16 @@ export function registerBrowserManageCommands(
|
||||
return;
|
||||
}
|
||||
await runBrowserCommand(async () => {
|
||||
const result = await browserTabAction(baseUrl, {
|
||||
action: "select",
|
||||
index: Math.floor(index) - 1,
|
||||
profile,
|
||||
});
|
||||
const result = await callBrowserRequest(
|
||||
parent,
|
||||
{
|
||||
method: "POST",
|
||||
path: "/tabs/action",
|
||||
query: profile ? { profile } : undefined,
|
||||
body: { action: "select", index: Math.floor(index) - 1 },
|
||||
},
|
||||
{ timeoutMs: 10_000 },
|
||||
);
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
@@ -238,7 +296,6 @@ export function registerBrowserManageCommands(
|
||||
.argument("[index]", "Tab index (1-based)", (v: string) => Number(v))
|
||||
.action(async (index: number | undefined, _opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
const profile = parent?.browserProfile;
|
||||
const idx =
|
||||
typeof index === "number" && Number.isFinite(index) ? Math.floor(index) - 1 : undefined;
|
||||
@@ -248,11 +305,16 @@ export function registerBrowserManageCommands(
|
||||
return;
|
||||
}
|
||||
await runBrowserCommand(async () => {
|
||||
const result = await browserTabAction(baseUrl, {
|
||||
action: "close",
|
||||
index: idx,
|
||||
profile,
|
||||
});
|
||||
const result = await callBrowserRequest(
|
||||
parent,
|
||||
{
|
||||
method: "POST",
|
||||
path: "/tabs/action",
|
||||
query: profile ? { profile } : undefined,
|
||||
body: { action: "close", index: idx },
|
||||
},
|
||||
{ timeoutMs: 10_000 },
|
||||
);
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
@@ -267,10 +329,18 @@ export function registerBrowserManageCommands(
|
||||
.argument("<url>", "URL to open")
|
||||
.action(async (url: string, _opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
const profile = parent?.browserProfile;
|
||||
await runBrowserCommand(async () => {
|
||||
const tab = await browserOpenTab(baseUrl, url, { profile });
|
||||
const tab = await callBrowserRequest<BrowserTab>(
|
||||
parent,
|
||||
{
|
||||
method: "POST",
|
||||
path: "/tabs/open",
|
||||
query: profile ? { profile } : undefined,
|
||||
body: { url },
|
||||
},
|
||||
{ timeoutMs: 15000 },
|
||||
);
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify(tab, null, 2));
|
||||
return;
|
||||
@@ -285,10 +355,18 @@ export function registerBrowserManageCommands(
|
||||
.argument("<targetId>", "Target id or unique prefix")
|
||||
.action(async (targetId: string, _opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
const profile = parent?.browserProfile;
|
||||
await runBrowserCommand(async () => {
|
||||
await browserFocusTab(baseUrl, targetId, { profile });
|
||||
await callBrowserRequest(
|
||||
parent,
|
||||
{
|
||||
method: "POST",
|
||||
path: "/tabs/focus",
|
||||
query: profile ? { profile } : undefined,
|
||||
body: { targetId },
|
||||
},
|
||||
{ timeoutMs: 5000 },
|
||||
);
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify({ ok: true }, null, 2));
|
||||
return;
|
||||
@@ -303,13 +381,29 @@ export function registerBrowserManageCommands(
|
||||
.argument("[targetId]", "Target id or unique prefix (optional)")
|
||||
.action(async (targetId: string | undefined, _opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
const profile = parent?.browserProfile;
|
||||
await runBrowserCommand(async () => {
|
||||
if (targetId?.trim()) {
|
||||
await browserCloseTab(baseUrl, targetId.trim(), { profile });
|
||||
await callBrowserRequest(
|
||||
parent,
|
||||
{
|
||||
method: "DELETE",
|
||||
path: `/tabs/${encodeURIComponent(targetId.trim())}`,
|
||||
query: profile ? { profile } : undefined,
|
||||
},
|
||||
{ timeoutMs: 5000 },
|
||||
);
|
||||
} else {
|
||||
await browserAct(baseUrl, { kind: "close" }, { profile });
|
||||
await callBrowserRequest(
|
||||
parent,
|
||||
{
|
||||
method: "POST",
|
||||
path: "/act",
|
||||
query: profile ? { profile } : undefined,
|
||||
body: { kind: "close" },
|
||||
},
|
||||
{ timeoutMs: 20000 },
|
||||
);
|
||||
}
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify({ ok: true }, null, 2));
|
||||
@@ -325,9 +419,16 @@ export function registerBrowserManageCommands(
|
||||
.description("List all browser profiles")
|
||||
.action(async (_opts, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
await runBrowserCommand(async () => {
|
||||
const profiles = await browserProfiles(baseUrl);
|
||||
const result = await callBrowserRequest<{ profiles: ProfileStatus[] }>(
|
||||
parent,
|
||||
{
|
||||
method: "GET",
|
||||
path: "/profiles",
|
||||
},
|
||||
{ timeoutMs: 3000 },
|
||||
);
|
||||
const profiles = result.profiles ?? [];
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify({ profiles }, null, 2));
|
||||
return;
|
||||
@@ -361,14 +462,21 @@ export function registerBrowserManageCommands(
|
||||
.action(
|
||||
async (opts: { name: string; color?: string; cdpUrl?: string; driver?: string }, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
await runBrowserCommand(async () => {
|
||||
const result = await browserCreateProfile(baseUrl, {
|
||||
name: opts.name,
|
||||
color: opts.color,
|
||||
cdpUrl: opts.cdpUrl,
|
||||
driver: opts.driver === "extension" ? "extension" : undefined,
|
||||
});
|
||||
const result = await callBrowserRequest<BrowserCreateProfileResult>(
|
||||
parent,
|
||||
{
|
||||
method: "POST",
|
||||
path: "/profiles/create",
|
||||
body: {
|
||||
name: opts.name,
|
||||
color: opts.color,
|
||||
cdpUrl: opts.cdpUrl,
|
||||
driver: opts.driver === "extension" ? "extension" : undefined,
|
||||
},
|
||||
},
|
||||
{ timeoutMs: 10_000 },
|
||||
);
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
@@ -391,9 +499,15 @@ export function registerBrowserManageCommands(
|
||||
.requiredOption("--name <name>", "Profile name to delete")
|
||||
.action(async (opts: { name: string }, cmd) => {
|
||||
const parent = parentOpts(cmd);
|
||||
const baseUrl = resolveBrowserControlUrl(parent?.url);
|
||||
await runBrowserCommand(async () => {
|
||||
const result = await browserDeleteProfile(baseUrl, opts.name);
|
||||
const result = await callBrowserRequest<BrowserDeleteProfileResult>(
|
||||
parent,
|
||||
{
|
||||
method: "DELETE",
|
||||
path: `/profiles/${encodeURIComponent(opts.name)}`,
|
||||
},
|
||||
{ timeoutMs: 20_000 },
|
||||
);
|
||||
if (parent?.json) {
|
||||
defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user