Development
Setup, WXT dev mode, load unpacked, four debugging entry points (popup / options / background / content), common commands, and distribution workflow
Prerequisites
- pnpm ≥ 10.32 (root
packageManageris locked) - Node.js ≥ 20
- Chrome / Edge / Brave — any Chromium-based browser
- Running backend: either
pnpm dev:server(web flow,ws://localhost:8001/ws/browser) orpnpm dev:desktop(desktop flow,ws://127.0.0.1:48123)
Two ways to run the extension
A — WXT dev mode (recommended for iteration)
pnpm dev:ext
WXT launches a dedicated Chrome profile, preinstalls the extension, and reloads on file changes:
- Popup and options HTML pages get hot module replacement
- Background service worker and content scripts auto-reload
- The terminal logs each rebuild
First run may prompt for the Chrome binary path; persist it via a web-ext.config.ts if needed.
B — Load unpacked (real profile)
Use this path when you need real logins (Gmail, etc.) or to verify store-upload behavior.
pnpm build:ext
- Open
chrome://extensions - Enable Developer mode (top-right)
- Click Load unpacked and select
apps/bua/.output/chrome-mv3/ - Pin the extension to the toolbar
After code changes: pnpm build:ext → 🔄 Reload on the extension card.
Pairing the extension
Run one backend and copy credentials into the extension’s Options.
| Backend | Where to get serverUrl + pairingToken |
|---|---|
Web (pnpm dev:server + pnpm dev:web) | Log in, then visit /settings/browser-extension in the app |
Desktop (pnpm dev:desktop) | Same route, fetched via IPC from <userData>/browser-bridge-token |
In the extension: Options → paste Server URL + Pairing token → Save. Popup should switch to Connected within a second.
Four debugging entry points
| Entry | How to open | What you see |
|---|---|---|
| Popup | Click toolbar icon → right-click the popup → Inspect | React render of session panel; kill switch events |
| Options | chrome://extensions → Extension options → F12 | Allowlist CRUD, config read / save / clear |
| Background SW | chrome://extensions → find Zapvol → click the service worker link | WS connection state, CDP attach / detach, session state machine, agent logs |
| Content script | Open any page → F12 → Console → filter [zapvol] | DOM helper logs, injected element lookups |
The service worker may show inactive — this is normal MV3 behavior. Click the link to wake it and surface latest logs. Any inbound message to the extension also wakes it.
Common commands
| Command | Purpose |
|---|---|
pnpm dev:ext | WXT dev mode with HMR |
pnpm build:ext | Build .output/chrome-mv3/ (production, minified) |
pnpm --filter @zapvol/bua build:firefox | Build Firefox variant |
pnpm --filter @zapvol/bua zip | Package .output/*.zip for distribution |
pnpm --filter @zapvol/bua compile | Type check only (tsc --noEmit) |
pnpm --filter @zapvol/bua lint | ESLint |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Toolbar shows puzzle icon instead of Zapvol bolt | auto-icons didn’t run / stale build | Re-run pnpm build:ext; confirm .output/chrome-mv3/icons/ has 16/32/48/128 |
| Popup shows “Connecting…” forever | Server URL or pairing token wrong | Double-check /settings/browser-extension; re-copy both values |
chrome.debugger.attach fails | DevTools open on same tab, or another debugger attached | Close DevTools; or target a different tab |
| Yellow debugger bar cannot be dismissed | Chrome enforced — feature, not bug | Wait for session to end or click Cancel in the bar |
Agent tool returns domain_blocked unexpectedly | Target domain is on the user’s blocklist | Remove the domain from Options → Blocklist, or pick a different target (terminal — agent must not retry) |
| Chrome yellow-bar Cancel leaves actions stuck | Pre-v4 bug: attached set not cleared on onDetach | Fixed in v4: handleDebuggerDetached now calls debuggerController.detach(tabId) before ending the session |
| Service worker stays inactive | MV3 idle termination | Click the link in chrome://extensions to wake; verify via console logs |
| Tailwind classes not applying | Missing import "../../globals.css" in an entrypoint | Check each main.tsx imports the global stylesheet |
| Extension cannot reach Electron desktop backend | Firewall / loopback disabled | Confirm ws://127.0.0.1:48123 reachable via a WS client; verify Electron logs for browser-bridge.server.listening |
Distribution (three stages)
Do not skip stages. BUA extensions with debugger permission receive extra Chrome Web Store scrutiny; land the
first two stages solidly before submitting.
| Stage | Produced by | Distributed via | Auto-update |
|---|---|---|---|
| 1. Development | pnpm dev:ext → unpacked | chrome://extensions → Load unpacked | Manual reload |
| 2. Internal | pnpm --filter @zapvol/bua zip | GitHub Release / internal channel | Users reinstall |
| 3. Public | Same zip | Chrome Web Store ($5 registration + privacy policy + review 1–7d) | Store handles |
Privacy policy must make clear: the extension only issues debugger commands under user-explicit per-domain time-scoped sessions, no background telemetry of page content.
File-level rules
For “how to add a new action / entrypoint / capability” checklists, see .claude/rules/bua.md in the repo. That file is
the enforceable source of truth for contributors; this page is orientation.