Optional Chrome extension that enables relay mode (attach to existing logged-in tabs).
Status: active
Last updated: 2026-06-30
Quick file-level overview: <public-repo-root>/extension/README.md
- Connects to the local relay server (
ws://127.0.0.1:<port>/extension). - Uses the Chrome Debugger API to forward CDP commands across attached tabs/targets (with a primary tab used for handshake/status).
- Allows OpenDevBrowser to control tabs without launching a new browser.
- Lets extension-backed sessions participate in manager-owned browser replay capture through the existing screenshot primitive; there is no separate extension screencast relay family.
- Supports multi-tab CDP routing with flat sessions (Chrome 125+).
- Exposes top-level tabs and auto-attached child targets (workers/OOPIF) through
Target.getTargets. - Hosts the dedicated design-canvas runtime used by
/canvasfor design-tab, overlay, and workspace-scoped multi-child shell operations. - Preserves additive canvas session-summary metadata such as
availableInventoryCount,catalogKitIds,availableStarterCount, and the currently applied starter so the design tab stays in sync with starter and kit availability without introducing a second starter execution path in the extension. - Routes popup/canvas/in-page annotation
Sendactions through/annotationstore_agent_payload; core enqueues screenshot-free payloads intoAgentInbox, delivering to one active chat scope or returning a stored-only receipt. - Launch defaults to extension relay when available; managed/CDPConnect require explicit user choice.
- Extension mode is headed-only; extension-intent headless launch/connect is rejected with
unsupported_mode. - Desktop observation is not an extension feature; the shipped desktop commands and tools stay daemon/core-owned, public, and observe-only.
- Generated help surfaces the exact lookup labels
screencast / browser replay,desktop observation, andcomputer use / browser-scoped computer use; the extension participates in relay-backed browser work, not the public read-only desktop plane. - When hub mode is enabled, the hub daemon is the sole relay owner and enforces FIFO leases (no local relay fallback).
Requires Node.js >=18 and the npm package (npx opendevbrowser recommended; npm install -g opendevbrowser optional).
- Run the CLI once with
--fullso the extension assets are extracted:npx opendevbrowser --full
- Load the extension unpacked from:
~/.config/opencode/opendevbrowser/extension- Fallback:
~/.cache/opencode/node_modules/opendevbrowser/extension - Pre-release local package onboarding:
<WORKDIR>/node_modules/opendevbrowser/extension
- Open the extension popup to configure relay settings.
For full first-run pre-release onboarding flow (local package install, isolated daemon, extension connect, first task), use:
<public-repo-root>/docs/FIRST_RUN_ONBOARDING.md
- Relay port: Port of the local relay server (default
8787). - Auto-connect: Reconnect on browser start (default on).
- Auto-pair: Fetch pairing token automatically from the plugin (default on).
- Native fallback (experimental): Allow native messaging fallback when relay is unavailable (default off).
- Require pairing token: Require token for relay pairing (recommended).
- Pairing token: Manual token entry when auto-pair is off.
| Setting | Default |
|---|---|
| Relay port | 8787 |
| Auto-connect | true |
| Auto-pair | true |
| Native fallback (experimental) | false |
| Require pairing token | true |
| Pairing token | null (fetched on connect) |
Auto-connect is enabled by default. The extension attempts to connect on browser startup, install, and when the toggle is enabled in the UI. Auto-connect respects the current relay port, pairing settings, and auto-pair toggle.
Native fallback is only attempted when the experimental native toggle is enabled.
The toolbar action uses the OpenDevBrowser icon set from extension/icons/ (synced from assets/extension-icons/ during npm run extension:build) and a badge dot indicator (green = connected, red = disconnected).
When auto-pair is enabled:
- The extension calls the local discovery endpoint (
/config,http://127.0.0.1:8787/configby default) to learn the relay port and pairing requirement. - If pairing is required, it fetches the token from
/pair. - The extension connects to the relay with the pairing token.
/config and /pair reject explicit non-extension origins. Chrome extension requests may omit the Origin header, so the relay also accepts missing-Origin requests. CLI/tools may call /config and /pair to auto-fetch relay settings and tokens.
Keep discoveryPort at 8787 for normal extension discovery; set it to the same isolated value as relayPort when running a separate daemon with isolated config/cache roots.
Relay ops endpoint: ws://127.0.0.1:<relayPort>/ops. The CLI/tool connect command accepts base relay WS URLs
(for example ws://127.0.0.1:<relayPort>) and normalizes them to /ops.
Relay canvas endpoint: ws://127.0.0.1:<relayPort>/canvas for live design-canvas preview and overlay commands. Public canvas commands are still the canvas.* commands exposed by the CLI/tool; extension canvas.tab.sync and canvas.overlay.sync are internal runtime capabilities.
Relay annotation endpoint: ws://127.0.0.1:<relayPort>/annotation for interactive annotate capture plus one-off store_agent_payload and fetch_stored requests.
Legacy relay /cdp is still available but must be explicitly opted in (CLI: --extension-legacy).
Legacy /cdp is mutually exclusive with an active /ops lease on the extension target. If the CLI returns cdp_attach_blocked, disconnect the /ops session first, then retry the legacy path.
When pairing is enabled, /ops, /canvas, /annotation, and /cdp require a relay token (?token=<relayToken>). Tools and the CLI auto-fetch /config and /pair
to obtain the token before connecting, so users should not manually pass or share tokenized URLs.
Readiness checks:
npx opendevbrowser status --daemon --output-format json
npx opendevbrowser --help
node scripts/chrome-store-compliance-check.mjsExpected extension-ready daemon fields:
extensionConnected=trueextensionHandshakeComplete=trueopsConnected=falseuntil a default extension session connects through/opscanvasConnected=falseunless a design-canvas session is actively using relay preview/overlay flowsannotationConnected=falseunless annotation capture or stored-payload retrieval is active
Auto-connect behavior:
- If a second extension client replaces the current relay client, reconnect suppression lasts only while that replacement remains active. When the relay no longer reports an active extension client, the background auto-connect loop should retry the stored relay instead of requiring a manual popup click.
Extension relay uses flat CDP sessions and requires Chrome 125+. Older versions will fail fast with a clear error.
- Target discovery (
Target.getTargets) includes top-level tabs and child targets. - Child targets are auto-attached recursively for session-aware routing.
- A single primary tab is used for relay handshake/status; switching tabs updates the handshake without disconnecting others.
- Design-canvas flows can open dedicated extension-hosted design tabs (
canvas.html) and mount overlays on existing tabs through the/canvasruntime. - The extension design tab is a same-origin infinite-canvas editor: it persists full
CanvasPageStatesnapshots in workspace and child scopedIndexedDBkeys, fans out same-origin convergence over workspace and child scopedBroadcastChannelnames, sends editor-originated patch requests back through/canvas, keeps arbitrary page overlays in sync through the same runtime, and exposes page selection, hierarchical layers, a property inspector, lease-aware undo/redo controls, keyboard shortcuts, extension-stage region annotation, and a dedicated token panel for collection or mode creation, token value or alias editing, selected-node binding, and token usage inspection. CanvasPageState,CanvasSessionSummary, page messages, current selection, drafts, cached previews, and preview sync payloads carry optionalworkspaceIdandchildId. When present, reload, relay reconnect, and MV3 restart restore the same workspace child without cross-child draft, selection, or preview leakage.- Workspace design tabs render a 4x2 agent shell with coordinator lane, active child detail, worker panes, activity log, review lane, checkpoint lane, preview budget indicators, and visible delivered/degraded/paused/conflict/lease/revision/sync states.
- Extension-hosted design tabs must register their synthetic target through
targets.registerCanvasbefore/opstargets.usecan activate that surface.
- Popup
Annotateresolves against the opener window's active http(s) tab first. If the focused surface iscanvas.html, another extension page, or a restricted tab, the background falls back to the last annotatable web tab it stored instead of trying to inject into the extension page itself, so popup annotate can recover after an MV3 service-worker restart. - Popup, canvas, and in-page annotation
Sendactions dispatchannotation:sendPayloadto the background, and the background then posts/annotationstore_agent_payload. - The relay handles
store_agent_payloadlocally through core bootstrap'sAgentInboxhandler and returns a shared-inbox receipt. - Successful scoped delivery reports
Delivered to agent. - When delivery cannot be scoped safely, the receipt includes
stored_onlywithno_active_scopeorambiguous_scope. - When the relay path fails, the extension stores the sanitized payload locally and reports
Stored only; fetch with annotate --stored. - Shared inbox persistence strips screenshots;
annotate --stored --include-screenshotsonly affects the extension-local fallback copy when it is still available in memory.
- Relay connections are local-only by default.
- Pairing tokens are stored in
chrome.storage.localand never sent to third parties. - The extension does not log page content or tokens.
- Non-local relay endpoints are not supported unless explicitly configured in the plugin.
- Extension not connecting: Confirm the relay is running (
opendevbrowser serve) and the port matches the popup. - Temp-profile unpacked extension automation does nothing: Google Chrome stable may ignore startup flags like
--disable-extensions-exceptand--load-extension. For isolated automation harnesses, prefer Chromium or Chrome for Testing, or use the already-installed unpacked extension in your real Chrome profile. - Auto-pair failing: Ensure the plugin is running and the relay server is available on the configured port.
- Pairing token required: Enable "Require pairing token" and provide the value from your
opendevbrowser.jsonc. - No active tab / restricted tab: The popup cannot attach to
chrome://,chrome-extension://, or Chrome Web Store pages. Focus a normal http(s) tab before connecting. - Popup annotate says
Annotation UI did not load in the page: The popup could not confirm the page-side annotation bridge after injection. The popup now sends its opener-tab id directly and otherwise restores the last stored annotatable web tab, but you should still focus the intended http(s) tab once, reload that page, and retry. If you just rebuilt the unpacked extension, reload it in Chrome before retesting so the new background and content-script bundles are active. - Canvas design-tab overlay fails with
restricted_urlonchrome-extension://.../canvas.html: Chrome is still running stale unpacked-extension runtime code. Rebuild if needed, then reload the unpacked extension in Chrome before retryingcanvas.overlay.mountor related design-tab commands; reconnect the extension after reload so the fresh background bundle owns the relay again. - Canvas workspace panes, drafts, selection, or previews look mixed after rebuild or reload: Reload the unpacked extension, reconnect the relay, and re-open the workspace child route. The design tab stores workspace and child scoped cache keys, so persistent mixing usually means Chrome is still running stale extension code.
- Debugger attach failed: Close DevTools on the target tab (or any other debugger) and retry.
- Chrome too old: Extension relay requires Chrome 125+ for flat sessions.
- Headless extension launch/connect fails: expected. Extension mode is headed-only; use
launch --no-extension --headlessfor managed headless sessions. - Launch fails due to missing extension: The CLI/tool will print exact commands for Managed or CDPConnect fallbacks when the extension is not connected.
- Popup shows Connected but launch says not connected: Check the popup note for the relay port/instance (it now includes the relay identity) and ensure it matches the daemon relay port.
- Canvas code sync shows
unsupported: Runnpx opendevbrowser canvas --command canvas.code.status --params '{"canvasSessionId":"<canvas-session-id>","bindingId":"<binding-id>"}' --output-format jsonand inspectframeworkAdapterId,declaredCapabilities,grantedCapabilities,capabilityDenials, andreasonCode. Built-in lanes currently cover React TSX v2, static HTML, custom elements, Vue SFC, and Svelte SFC; legacytsx-react-v1bindings migrate tobuiltin:react-tsx-v2on load. - Local adapter plugin will not load: Repo-local BYO plugins only load from local
package.json,.opendevbrowser/canvas/adapters.json, or explicit config declarations. Out-of-worktree package declarations are rejected withtrust_denied, and malformed manifests or broken entrypoints surface deterministic plugin load failures. - Annotation send says stored only: no safe chat scope was available for the current worktree or relay enqueue failed. Use
annotate --storedto fetch the payload explicitly, or keep one target chat active and retry the send.