Files
Bob.qwencodeandgithub-actions[bot] 208beb2b3b chore(release): v0.26.9 [skip ci]
* chore(release): v0.26.9

* docs(changelog): sync for v0.26.9

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-06 18:03:10 +00:00
..
2026-11-06 18:03:10 +00:00

@qwen-code/chrome-bridge

A Chrome extension that brings Qwen Code into the browser as a thin client of a local qwen serve daemon and provides the Chrome side of Browser Use.

It provides:

  • Side panel — frames the daemon's Web Shell (chat + tools), the same UI the daemon serves to the browser. The panel has no UI of its own.
  • Service worker — a CDP-tunnel pipe. It connects to the daemon's /acp WebSocket and bridges cdp_* frames into chrome.debugger, so the agent can drive the real browser when an external CDP MCP adapter is configured.
  • Browser Use bridge — connects to the local com.qwen.browser_use Native Messaging host, lists open HTTP(S) tabs, and forwards Playwright CDP traffic for tabs claimed by Browser Use.
  • Readiness warning — the framed Web Shell stays usable for chat while a small status message distinguishes a disabled CDP tunnel from a missing browser automation adapter.

Build

To use the extension, install it from the Chrome Web Store. The steps below build it from source, for development or where the listing is not available.

npm run build        # -> dist/extension (static assets + bundled service worker)

Then load it: chrome://extensions → enable Developer mode → Load unpacked → pick dist/extension.

Run

The extension is a client; the daemon does the work and must be started separately (an extension cannot spawn a local process). Open the side panel and it will tell you exactly what to run — it generates the command with this extension's own id:

qwen serve --allow-origin chrome-extension://<this-extension-id>

--allow-origin chrome-extension://<id> is required: it lets the daemon's Web Shell be framed by the extension (the frame-ancestors CSP) and accepts the extension's requests. The side panel reads the id at runtime via chrome.runtime.id, so you never have to look it up.

Do not replace this command with --open-with-auth. That mode delivers its generated bearer only to the tab it opens; the extension cannot discover it. To protect a daemon used by the extension, set QWEN_SERVER_TOKEN explicitly and configure the same stable token in every authorized client.

Once the daemon is reachable and permits framing, the side panel swaps the welcome screen for the chat UI automatically.

Browser Use

Browser Use ships with Qwen Code as a built-in skill and SDK runtime, using Qwen's standard Node REPL. No separate Qwen extension installation is needed. On macOS and Linux, the first browser task automatically registers the shared Native Messaging host in the user's installation directory. The SDK can finish this local setup before the extension connects; it verifies the live connection and protocol instead of reading Chrome's extension preferences. If it cannot connect, open Chrome and install or enable the extension in the intended profile, then retry. Later sessions reuse an installed Host of the same protocol, and a Host installed by a newer Qwen Code is never downgraded. Run node <skill-base>/runtime/scripts/native-host-setup.js status to inspect the installation, install to switch it to this Qwen Code's Host from the next Host start, or uninstall to remove files owned by Browser Use. <skill-base> is the base directory shown when loading the Browser Use skill. A later Browser Use initialization can register the Host again.

The Chrome-launched Host serves multiple independent Qwen sessions per profile. Each session controls its own tabs and groups; claiming another session's tab returns TAB_OWNERSHIP_CONFLICT. A CLI exit leaves other sessions and the Host running. Host files persist in the user's installation directory independently of individual CLI checkouts. Updating the launcher takes effect on the next Host start. Browser Use does not require qwen serve or a Chrome debugging port.

Installing the Chrome extension authorizes Browser Use to list and claim open top-level HTTP(S) tabs. Its declared history permission supports explicit, bounded history queries. The existing toolbar action and side panel continue to open the Qwen UI.

Web Shell CDP tunnel

The qwen serve command above only makes the side panel and Web Shell available. Browser automation through the Web Shell's older CDP tunnel still requires an explicit external MCP adapter command:

QWEN_CDP_MCP_COMMAND=/path/to/cdp-mcp-adapter \
qwen serve --allow-origin chrome-extension://<this-extension-id>

No adapter for this older CDP tunnel is bundled with @qwen-code/qwen-code. When QWEN_CDP_MCP_COMMAND is unset, the extension can still open the Web Shell, but the daemon will not register browser automation MCP tools. Install the adapter separately and point the daemon at its executable:

The pinned adapter requires Node.js 22.12 or newer.

npm install -g [email protected]
QWEN_CDP_MCP_COMMAND=chrome-devtools-mcp \
  qwen serve --allow-origin chrome-extension://<this-extension-id>

The separately installed adapter is not included in the Qwen Code npm package or Chrome extension zip. Clients can distinguish the states through /capabilities:

  • allow_origin means the extension may frame and call the daemon.
  • cdp_tunnel_over_ws means the daemon exposes the reverse CDP tunnel.
  • browser_automation_mcp means the external adapter command is configured and browser automation MCP tools can be registered when the CDP bridge connects.

When browser automation is configured, the panel also checks /workspace/mcp. It warns when the adapter has not connected or when an existing user-defined chrome-devtools server takes precedence over the extension tunnel.

Onboarding states

The side panel probes GET /health and GET /capabilities and shows one of:

State Meaning Shown
down no daemon reachable "Start qwen serve" + command
needs-allow-origin daemon up but --allow-origin not set "Allow this extension" + command
chat-only Web Shell ready, CDP tunnel disabled chat + bridge warning
tunnel-only CDP tunnel ready, adapter missing chat + adapter warning
automation-unavailable adapter status could not be read chat + status warning
automation-pending adapter not connected chat + connection warning
automation-shadowed an existing MCP config takes precedence chat + migration warning
automation-configured adapter configured, discovery not started the Web Shell
automation-connected extension-backed MCP connected the Web Shell

Automated real-Chrome acceptance

With Chrome running and the unpacked extension loaded, the acceptance runner starts an isolated daemon and fixture page, exercises DOM snapshots, console messages, network requests, button clicks, link navigation, restores the original page, restarts the daemon, and verifies a cold restart:

QWEN_CDP_MCP_COMMAND=/path/to/cdp-mcp-adapter \
  npm -w packages/chrome-extension run test:e2e:chrome

The command exits successfully only after printing DEGRADED-MODE: PASS, RUNTIME-MCP: PASS, RUNTIME-MCP-COLD-RESTART: PASS, FULL-CDP-SMOKE: PASS, and REAL-CHROME-E2E: PASS. It does not read or modify the user's Qwen settings.

Packaging for the Chrome Web Store

Packaging (npm run package) and the release test require the POSIX zip utility.

npm run package        # -> chrome-extension.zip (manifest at the zip root)
npm run package:store  # -> chrome-extension-store.zip, without the manifest key

Run the complete release check from the repository root. It builds the main npm payload, runs the extension tests and typecheck, packages the zip, and scans both generated payloads for external Chrome DevTools MCP source signatures:

npm run test:chrome-extension:release

The generated manifest version follows this package's version. Release Chrome Extension (.github/workflows/release-chrome-extension.yml) packages and uploads it on a published stable release, and can be dispatched by hand; its dry_run default packages and scans without uploading anything. Automatic publishing pauses while the repository variable RELEASE_CHROME_SYNC_PUBLISH is false. Nightly and preview builds are not published to the store, because every upload takes a version bump and a review; upload the zip to a GitHub prerelease for alpha side-loading instead. The debugger permission will draw manual review and must be justified in the store listing.

Extension id note: the manifest's key fixes the id of an unpacked build. The store rejected that key on the first upload and assigned the listing its own id, so the two builds carry different ids. npm run package zips the built manifest verbatim, for side-loading; npm run package:store stages a copy without the key, which is what the release workflow uploads. Browser Use treats the id as a set (CHROME_EXTENSION_IDS in the Browser Use package): the Native Messaging registration lists every known origin and the handshake accepts any of them, so both builds reach the same Host. Adding an id there also means bumping the Host revision, so installed Hosts are replaced rather than reused.

Version note: the manifest version is derived from this package's semver (e.g. 0.21.2.65535), which is lower than the legacy side-loaded 1.0.0 alpha. Chrome refuses to update an extension to a lower version, so testers upgrading from the 1.0.0 build must remove it in chrome://extensions before loading this package.