import type { AnyAgentTool, OpenClawPluginApi, OpenClawPluginNodeHostCommand, OpenClawPluginSecurityAuditCollector, OpenClawPluginService, OpenClawPluginToolContext, OpenClawPluginToolFactory, } from "openclaw/plugin-sdk/plugin-entry"; import { BrowserToolSchema } from "./src/browser-tool.schema.js"; const BROWSER_CLI_DESCRIPTOR = { name: "browser", description: "Manage OpenClaw's dedicated browser (Chrome/Chromium)", hasSubcommands: true, }; function createLazyBrowserTool(opts?: { sandboxBridgeUrl?: string; allowHostControl?: boolean; agentSessionKey?: string; }): AnyAgentTool { const targetDefault = opts?.sandboxBridgeUrl ? "sandbox" : "host"; const hostHint = opts?.allowHostControl === false ? "Host target blocked by policy." : "Host target allowed."; return { label: "Browser", name: "browser", description: [ "Control the browser via OpenClaw's browser control server (status/start/stop/profiles/tabs/open/snapshot/screenshot/actions).", "Browser choice: omit profile by default for the isolated OpenClaw-managed browser (`openclaw`).", 'For the logged-in user browser, use profile="user". A supported Chromium-based browser (v144+) must be running on the selected host or browser node. Use only when existing logins/cookies matter and the user is present.', 'For profile="user" or other existing-session profiles, omit timeoutMs on act:type, evaluate, hover, scrollIntoView, drag, select, and fill; that driver rejects per-call timeout overrides for those actions.', 'When a node-hosted browser proxy is available, the tool may auto-route to it. Pin a node with node= or target="node".', "When using refs from snapshot (e.g. e12), keep the same tab: prefer passing targetId from the snapshot response into subsequent actions (act/click/type/etc). For tab operations, targetId also accepts tabId handles (t1) and labels from action=tabs.", "For multi-step browser work, login checks, stale refs, duplicate tabs, or Google Meet flows, use the bundled browser-automation skill when it is available.", 'For stable, self-resolving refs across calls, use snapshot with refs="aria" (Playwright aria-ref ids). Default refs="role" are role+name-based.', "Use snapshot+act for UI automation. Avoid act:wait by default; use only in exceptional cases when no reliable UI state exists.", `target selects browser location (sandbox|host|node). Default: ${targetDefault}.`, hostHint, ].join(" "), parameters: BrowserToolSchema, execute: async (toolCallId, args, signal, onUpdate) => { const { createBrowserTool } = await import("./register.runtime.js"); const tool = createBrowserTool(opts); return await tool.execute(toolCallId, args, signal, onUpdate); }, }; } export const browserPluginReload = { restartPrefixes: ["browser"] }; export const browserPluginNodeHostCommands: OpenClawPluginNodeHostCommand[] = [ { command: "browser.proxy", cap: "browser", handle: async (paramsJSON) => { const { runBrowserProxyCommand } = await import("./register.runtime.js"); return await runBrowserProxyCommand(paramsJSON); }, }, ]; export const browserSecurityAuditCollectors: OpenClawPluginSecurityAuditCollector[] = [ async (ctx) => { const { collectBrowserSecurityAuditFindings } = await import("./register.runtime.js"); return collectBrowserSecurityAuditFindings(ctx); }, ]; function createLazyBrowserPluginService(): OpenClawPluginService { let service: OpenClawPluginService | null = null; const loadService = async () => { if (!service) { const { createBrowserPluginService } = await import("./register.runtime.js"); service = createBrowserPluginService(); } return service; }; return { id: "browser-control", start: async (ctx) => { const loaded = await loadService(); await loaded.start(ctx); }, stop: async (ctx) => { if (!service?.stop) { return; } await service.stop(ctx); }, }; } export function registerBrowserPlugin(api: OpenClawPluginApi) { api.registerTool(((ctx: OpenClawPluginToolContext) => createLazyBrowserTool({ sandboxBridgeUrl: ctx.browser?.sandboxBridgeUrl, allowHostControl: ctx.browser?.allowHostControl, agentSessionKey: ctx.sessionKey, })) as OpenClawPluginToolFactory); api.registerCli( async ({ program }) => { const { registerBrowserCli } = await import("./src/cli/browser-cli.js"); registerBrowserCli(program); }, { commands: ["browser"], descriptors: [BROWSER_CLI_DESCRIPTOR] }, ); api.registerGatewayMethod( "browser.request", async (opts) => { const { handleBrowserGatewayRequest } = await import("./register.runtime.js"); return await handleBrowserGatewayRequest(opts); }, { scope: "operator.admin", }, ); api.registerService(createLazyBrowserPluginService()); }