| .. | ||
| script | ||
| src | ||
| test | ||
| package.json | ||
| README.md | ||
| tsconfig.build.json | ||
| tsconfig.json | ||
| tsconfig.test.json | ||
Browser plugin
@opencode-ai/plugin-browser exposes the desktop browser through Code Mode.
The server owns tools, invocation scope, and permissions; the desktop owns tabs,
CDP, captured traffic, evaluations, and capture files. Core only registers the
plugin. Neither endpoint imports the other's implementation.
const tab = await tools.browser.tabs.open({ url: "https://example.com" })
return await tools.browser.snapshot({ tabID: tab.id })
All page operations require a tabID returned by browser.tabs.open/list.
Focus selects the visible Review tab, not an implicit command target. Discover
current signatures with search({ namespace: "browser" }).
Screenshots require a focused, visible tab; call browser.tabs.focus first.
Tools
- Tabs:
tabs.list,tabs.open,tabs.focus,tabs.close. - Navigation:
navigate,back,forward,reload,stop,frames. - Observation:
snapshot,find,evaluate,wait,screenshot. - Input:
click,hover,drag,fill,fill_form,select,check,press,scroll,dialog. - Files:
files.upload,files.drop,files.list,files.get. - Diagnostics:
console,network.list,network.get. - Performance:
trace.start,trace.stop,trace.analyze,cpu.start,cpu.stop,cpu.analyze. - Memory:
heap.snapshot,heap.summary,heap.query,heap.object,heap.compare. - Audits:
lighthouse(accessibility, SEO, best practices).
The source of truth for inputs, descriptions, and outputs is
Browser.Operations in @opencode-ai/plugin-browser/rpc.
RPC
The plugin-owned contract is @opencode-ai/plugin-browser/rpc. This entrypoint
contains only schemas and descriptions; it does not load the server plugin or
filesystem code. The desktop subscribes
to control events before starting attach with version: 2. The attachment call
stays pending for its lifetime. A matching attached event is the readiness barrier.
statepublishes the authoritative tab inventory.controlannounces a request ID or cancellation; it never broadcasts arguments, script source, file bytes, or browser results on the server-wide event feed.commandretrieves the pending request through authenticated RPC.resultcompletes it. The plugin validates the selected operation's output.
The connection ID is correlation, not separate client authentication. Requests are bound to their attachment and tab. Disconnect, replacement, session movement, and unload fail outstanding work. Calls are not replayed automatically: a lost response does not prove that a click or evaluation never happened.
Files and remote servers
Upload paths are server-local. File bytes cross RPC and the desktop writes its
own temporary copy. Captures/downloads travel back as bounded bytes and are saved
to server-local temporary files. Returned files[].path values refer to that
server; bytes are not included in the model's structured output. Images are also
attached for the model to inspect. Temporary exports are not deleted on plugin
reload, so a returned path remains usable; they follow the host's temporary-file
lifetime.
Each transfer is limited to 5 MiB total. There is no shared filesystem assumption,
resumable transfer service, new socket, or object store. Browsing uses the
desktop's network: its localhost is not the remote server's localhost.
Remote endpoints can use HTTPS and the existing server credentials. A reverse proxy must allow long-lived event and attachment requests; the attachment RPC stays open rather than sending response-body heartbeats.
Lighthouse audits use snapshot mode without changing device emulation or adding
an embedded report screenshot; use browser.screenshot for images. Trace exports
contain the target renderer process, not the whole desktop application. A tab
process change or trace-buffer loss is reported as an incomplete capture. Heap
summaries report shallow size, not computed retained size, and do not prove leaks.
All page-derived data is untrusted, including structured outputs. Schema validation does not make page text an instruction or grant it authority.
Per-URL and server-file permission checks belong to the final permission layer (#46530). This base plugin layer intentionally does not enforce those rules.
Disable through normal configuration:
{ "plugins": ["-opencode.browser"] }