Side panel: `sidepanel.html`
The default chat UI: streamed replies with their tool steps, a past-chats overlay that loads transcripts from the host, Markdown export and a settings button that opens the options page. Most users live here.
The Browy extension is a Chromium MV3 extension that hosts three UI surfaces (side panel, DevTools panel, options page) and talks to the native host over a single native-messaging port.
extension/├── manifest.json ← MV3 manifest, permissions, background SW├── background.js ← service worker, port owner, CDP bridge├── sidepanel.html / .js ← chat UI (default surface)├── marked.min.js ← Markdown renderer for the side panel├── devtools.html / .js ← registers the DevTools panel├── panel.html / .js / .css ← the actual DevTools CLI surface├── options.html / .js ← Settings (model, tools, install help)└── icons/ ← toolbar + store assetsA single chrome.runtime.connectNative('com.browy.host') port lives
on the service worker. It’s the one and only channel to the host.
Each UI surface opens its own chrome.runtime.connect port to the
service worker (sidepanel-<random>, devtools-panel-<random> or
options). The service worker forwards their messages to the host
and routes each reply to the surface that owns its sessionId. Host
messages without a session id, such as models.current and
auth.status, go to every surface.
Side panel: `sidepanel.html`
The default chat UI: streamed replies with their tool steps, a past-chats overlay that loads transcripts from the host, Markdown export and a settings button that opens the options page. Most users live here.
DevTools panel: `panel.html`
The terminal-style REPL. Registered by devtools.js via
chrome.devtools.panels.create('Browy', …). Shares the agent
backend with the side panel and is bound to the inspected target.
See the DevTools CLI page.
Options: `options.html`
Model picker, tool toggles, sign-in status, and install help when the host isn’t reachable. Opens via Chrome’s standard extension options entry or Browy’s toolbar menu.
Tools run in the host. When one needs the page, the host sends a
cdp.send message, and the SW:
tabId in the message if the host pinned one,
else the tab bound to the session (a DevTools panel’s inspected
tab), else the active tab in the side panel’s own window (sent as
windowId with session.start), and the active tab in the
last-focused window only when the panel didn’t say.chrome.debugger.attach({tabId}, '1.3') if it isn’t
already attached.chrome.debugger.sendCommand
(Runtime.evaluate for an inspect_page snapshot,
Input.dispatchMouseEvent for click_index, Network.enable,
etc.) and leaves shaping the result to the host.{type: 'tool.result', sessionId, callId, ok, result} back
over the port.tab.new, tab.activate, tab.close, tab.list and tab.getActive go through
chrome.tabs the same way, and events from attached tabs flow back
to the host as cdp.event.
The Yellow Banner of Doom (Chrome’s “Browy started debugging…” notice) is Chrome’s mandatory disclosure for any extension using the DevTools Protocol. We don’t try to suppress it. See Permissions → debugger.
The extension keeps its own state in chrome.storage.local:
browy.sessionId for the side panel and
browy.dt.sessionId.<tabId> for each inspected tab’s DevTools
panel. The id is also the chat’s Copilot session id.browy.chat.<id> (rendered HTML),
browy.chatMeta.<id> → {id, title, updated, preview} and
browy.chatIndex (ids, newest first)settings.tools (per-tool toggles)browy.firstSendDone (hides the starter prompts
after your first message) and browy.hostUpdateSnoozeUntil
(snoozes the backend-update banner)The transcripts themselves live in the Copilot CLI’s session store
under ~/.copilot/, and the host saves your model choice in
~/.browy/data/prefs.json. chrome.storage.local is stored in this
browser profile and is cleared when the extension is removed.
The manifest declares no content scripts. Code that has to run in the
page, such as the inspect_page snapshot, evaluate_js and
query_dom, reaches the tab from the host as CDP Runtime.evaluate
calls. The snapshot script walks open shadow roots and same-origin
iframes, and tags each interactive element with data-browy-id so
the *_index tools can find it again.
The extension ships as-is, with no bundler step. extension/ is a
plain Chromium extension folder. The installer copies it to
Browy-Extension in your Downloads folder, so you can sideload it
with chrome://extensions → Load unpacked. npm run pack:extension
zips the same folder for the Chrome Web Store, whose listing covers
Chrome, Edge and Brave.
I'll write when there's a new version worth trying. Unsubscribe any time.
Rather get them on GitHub? Watch BrowyHQ/browy and choose Custom, then Releases.