@browserbridge/bbx 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +6 -4
  2. package/package.json +53 -53
  3. package/packages/agent-client/src/cli-helpers.js +43 -5
  4. package/packages/agent-client/src/cli.js +176 -171
  5. package/packages/agent-client/src/client.js +66 -21
  6. package/packages/agent-client/src/command-registry.js +104 -69
  7. package/packages/agent-client/src/detect.js +162 -54
  8. package/packages/agent-client/src/install.js +34 -28
  9. package/packages/agent-client/src/mcp-config.js +40 -40
  10. package/packages/agent-client/src/runtime.js +41 -20
  11. package/packages/agent-client/src/setup-status.js +23 -30
  12. package/packages/mcp-server/src/bin.js +57 -5
  13. package/packages/mcp-server/src/handlers.js +573 -256
  14. package/packages/mcp-server/src/server.js +568 -257
  15. package/packages/native-host/bin/bridge-daemon.js +39 -6
  16. package/packages/native-host/bin/install-manifest.js +26 -4
  17. package/packages/native-host/bin/postinstall.js +4 -2
  18. package/packages/native-host/src/config.js +142 -13
  19. package/packages/native-host/src/daemon-process.js +396 -0
  20. package/packages/native-host/src/daemon.js +350 -150
  21. package/packages/native-host/src/framing.js +131 -11
  22. package/packages/native-host/src/install-manifest.js +194 -29
  23. package/packages/native-host/src/native-host.js +154 -102
  24. package/packages/protocol/src/budget.js +3 -7
  25. package/packages/protocol/src/capabilities.js +6 -3
  26. package/packages/protocol/src/defaults.js +1 -0
  27. package/packages/protocol/src/errors.js +15 -11
  28. package/packages/protocol/src/payload-cost.js +19 -6
  29. package/packages/protocol/src/protocol.js +242 -73
  30. package/packages/protocol/src/registry.js +311 -45
  31. package/packages/protocol/src/summary.js +260 -109
  32. package/packages/protocol/src/types.js +29 -4
  33. package/skills/browser-bridge/SKILL.md +3 -2
  34. package/skills/browser-bridge/agents/openai.yaml +3 -3
  35. package/skills/browser-bridge/references/interaction.md +34 -11
  36. package/skills/browser-bridge/references/patch-workflow.md +3 -0
  37. package/skills/browser-bridge/references/protocol.md +127 -71
  38. package/skills/browser-bridge/references/tailwind.md +12 -11
  39. package/skills/browser-bridge/references/token-efficiency.md +23 -22
  40. package/skills/browser-bridge/references/ui-workflows.md +8 -0
  41. package/CHANGELOG.md +0 -55
  42. package/assets/banner.jpg +0 -0
  43. package/assets/logo.png +0 -0
  44. package/assets/logo.svg +0 -65
  45. package/docs/api-reference.md +0 -157
  46. package/docs/cli-guide.md +0 -128
  47. package/docs/index.md +0 -25
  48. package/docs/manual-setup.md +0 -140
  49. package/docs/mcp-vs-cli.md +0 -258
  50. package/docs/publishing.md +0 -114
  51. package/docs/quickstart.md +0 -104
  52. package/docs/troubleshooting.md +0 -59
  53. package/docs/usage-scenarios.md +0 -136
  54. package/manifest.json +0 -52
  55. package/packages/extension/assets/icon-128.png +0 -0
  56. package/packages/extension/assets/icon-16.png +0 -0
  57. package/packages/extension/assets/icon-32.png +0 -0
  58. package/packages/extension/assets/icon-48.png +0 -0
  59. package/packages/extension/src/background-helpers.js +0 -459
  60. package/packages/extension/src/background-routing.js +0 -91
  61. package/packages/extension/src/background.js +0 -3227
  62. package/packages/extension/src/content-script-helpers.js +0 -281
  63. package/packages/extension/src/content-script.js +0 -1977
  64. package/packages/extension/src/debugger-coordinator.js +0 -188
  65. package/packages/extension/src/sidepanel-helpers.js +0 -102
  66. package/packages/extension/ui/offscreen.html +0 -6
  67. package/packages/extension/ui/offscreen.js +0 -61
  68. package/packages/extension/ui/popup.html +0 -35
  69. package/packages/extension/ui/popup.js +0 -279
  70. package/packages/extension/ui/sidepanel.html +0 -102
  71. package/packages/extension/ui/sidepanel.js +0 -1854
  72. package/packages/extension/ui/ui.css +0 -1159
@@ -29,65 +29,66 @@ The table below includes the legacy capability bucket for each method so agents
29
29
 
30
30
  ## All Methods (57)
31
31
 
32
- | # | Method | Tab? | CDP? | Group | Capability | Notes |
33
- |---|--------|------|------|-------|------------|-------|
34
- | 1 | `access.request` | No | - | system | `-` | Request window access; surfaces Enable prompt in extension UI |
35
- | 2 | `tabs.list` | No | - | tabs | `-` | Discover available tabs |
36
- | 3 | `tabs.create` | No | - | tabs | `tabs.manage` | Open a new tab; optional `url` and `active` |
37
- | 4 | `tabs.close` | No | - | tabs | `tabs.manage` | Close a tab by `tabId` |
38
- | 5 | `skill.get_runtime_context` | No | - | system | `-` | Live budget presets + method groups |
39
- | 6 | `setup.get_status` | No | - | system | `-` | Global MCP config + CLI skill install status |
40
- | 7 | `setup.install` | No | - | system | `-` | Install or uninstall MCP/skill integration targets |
41
- | 8 | `health.ping` | No | - | system | `-` | Connectivity check + access routing state |
42
- | 9 | `log.tail` | No | - | system | `-` | Recent bridge logs |
43
- | 10 | `page.get_state` | Yes | - | page | `page.read` | URL, readiness, focus, scroll, viewport |
44
- | 11 | `page.evaluate` | Yes | CDP | page | `page.evaluate` | JS expression in page context; last resort |
45
- | 12 | `page.get_console` | Yes | - | page | `page.read` | Buffered console messages; filter by `level`, `limit` |
46
- | 13 | `page.wait_for_load_state` | Yes | - | wait | `page.read` | Block until tab `complete`; `timeoutMs` capped 30 s |
47
- | 14 | `page.get_storage` | Yes | - | page | `page.read` | `localStorage`/`sessionStorage`; optional `keys` |
48
- | 15 | `page.get_text` | Yes | - | page | `page.read` | Full page text; `textBudget` limits size |
49
- | 16 | `page.get_network` | Yes | - | page | `network.read` | Intercepted fetch/XHR; `limit` entries |
50
- | 17 | `navigation.navigate` | Yes | - | navigate | `navigation.control` | Go to URL; `waitForLoad` default true |
51
- | 18 | `navigation.reload` | Yes | - | navigate | `navigation.control` | Reload; `waitForLoad` default true |
52
- | 19 | `navigation.go_back` | Yes | - | navigate | `navigation.control` | History back |
53
- | 20 | `navigation.go_forward` | Yes | - | navigate | `navigation.control` | History forward |
54
- | 21 | `dom.query` | Yes | - | inspect | `dom.read` | Query subtree with budget constraints |
55
- | 22 | `dom.describe` | Yes | - | inspect | `dom.read` | Single element details via `elementRef` |
56
- | 23 | `dom.get_text` | Yes | - | inspect | `dom.read` | Text content with `textBudget` |
57
- | 24 | `dom.get_attributes` | Yes | - | inspect | `dom.read` | Targeted attribute read |
58
- | 25 | `dom.wait_for` | Yes | - | wait | `dom.read` | Wait for DOM condition; MutationObserver + polling |
59
- | 26 | `dom.find_by_text` | Yes | - | inspect | `dom.read` | Find by visible text; returns `{nodes, count}` |
60
- | 27 | `dom.find_by_role` | Yes | - | inspect | `dom.read` | Find by ARIA role; optional `name` filter |
61
- | 28 | `dom.get_html` | Yes | - | inspect | `dom.read` | `innerHTML`/`outerHTML`; `maxLength` truncation |
62
- | 29 | `dom.get_accessibility_tree` | Yes | CDP | inspect | `dom.read` | Full a11y tree; `maxNodes`/`maxDepth` limits |
63
- | 30 | `layout.get_box_model` | Yes | - | inspect | `layout.read` | Element geometry (no budget needed) |
64
- | 31 | `layout.hit_test` | Yes | - | inspect | `layout.read` | Element at viewport point |
65
- | 32 | `styles.get_computed` | Yes | - | inspect | `styles.read` | Computed CSS; always set `properties` |
66
- | 33 | `styles.get_matched_rules` | Yes | - | inspect | `styles.read` | Matching CSS rules |
67
- | 34 | `viewport.scroll` | Yes | - | navigate | `viewport.control` | Window or element scroll |
68
- | 35 | `viewport.resize` | Yes | CDP | navigate | `viewport.control` | Set viewport via device emulation; `reset: true` |
69
- | 36 | `input.click` | Yes | - | interact | `automation.input` | DOM-level click |
70
- | 37 | `input.focus` | Yes | - | interact | `automation.input` | Focus element |
71
- | 38 | `input.type` | Yes | - | interact | `automation.input` | Type into input/textarea/contenteditable |
72
- | 39 | `input.press_key` | Yes | - | interact | `automation.input` | Single key event |
73
- | 40 | `input.set_checked` | Yes | - | interact | `automation.input` | Checkbox/radio toggle |
74
- | 41 | `input.select_option` | Yes | - | interact | `automation.input` | Native select by value/label/index |
75
- | 42 | `input.hover` | Yes | - | interact | `automation.input` | mouseenter/mouseover/mousemove; optional `duration` |
76
- | 43 | `input.drag` | Yes | - | interact | `automation.input` | Full drag-and-drop event sequence |
77
- | 44 | `input.scroll_into_view` | Yes | - | interact | `automation.input` | Explicitly scroll target into view before inspect/capture |
78
- | 45 | `screenshot.capture_element` | Yes | CDP | capture | `screenshot.partial` | Cropped element screenshot |
79
- | 46 | `screenshot.capture_region` | Yes | CDP | capture | `screenshot.partial` | Cropped viewport region |
80
- | 47 | `screenshot.capture_full_page` | Yes | CDP | capture | `screenshot.partial` | Full document screenshot; use only when page-level context is necessary |
81
- | 48 | `patch.apply_styles` | Yes | - | patch | `patch.styles` | Reversible CSS patch; `verify` returns computed result |
82
- | 49 | `patch.apply_dom` | Yes | - | patch | `patch.dom` | Reversible DOM mutation; `verify` returns result |
83
- | 50 | `patch.list` | Yes | - | patch | `patch.dom` | Active patches |
84
- | 51 | `patch.rollback` | Yes | - | patch | `patch.dom` | Revert one patch |
85
- | 52 | `patch.commit_session_baseline` | Yes | - | patch | `patch.dom` | Accept current state as baseline |
86
- | 53 | `performance.get_metrics` | Yes | CDP | performance | `performance.read` | Chrome performance counters |
87
- | 54 | `cdp.get_document` | Yes | CDP | cdp | `cdp.dom_snapshot` | DevTools document tree |
88
- | 55 | `cdp.get_dom_snapshot` | Yes | CDP | cdp | `cdp.dom_snapshot` | DevTools DOM snapshot |
89
- | 56 | `cdp.get_box_model` | Yes | CDP | cdp | `cdp.box_model` | DevTools-backed element geometry |
90
- | 57 | `cdp.get_computed_styles_for_node` | Yes | CDP | cdp | `cdp.styles` | DevTools-backed computed styles |
32
+ | # | Method | Tab? | CDP? | Group | Capability | Notes |
33
+ | --- | ---------------------------------- | ---- | ---- | ----------- | -------------------- | ----------------------------------------------------------------------- |
34
+ | 1 | `access.request` | No | - | system | `-` | Request window access; surfaces Enable prompt in extension UI |
35
+ | 2 | `tabs.list` | No | - | tabs | `-` | Discover available tabs |
36
+ | 3 | `tabs.create` | No | - | tabs | `tabs.manage` | Open a new tab; optional `url` and `active` |
37
+ | 4 | `tabs.close` | No | - | tabs | `tabs.manage` | Close a tab by `tabId` |
38
+ | 5 | `skill.get_runtime_context` | No | - | system | `-` | Live budget presets + method groups |
39
+ | 6 | `setup.get_status` | No | - | system | `-` | Global MCP config + CLI skill install status |
40
+ | 7 | `setup.install` | No | - | system | `-` | Install or uninstall MCP/skill integration targets |
41
+ | 8 | `health.ping` | No | - | system | `-` | Connectivity check + access routing state |
42
+ | 9 | `log.tail` | No | - | system | `-` | Recent bridge logs |
43
+ | 10 | `page.get_state` | Yes | - | page | `page.read` | URL, readiness, focus, scroll, viewport |
44
+ | 11 | `page.evaluate` | Yes | CDP | page | `page.evaluate` | JS expression in page context; last resort |
45
+ | 12 | `page.get_console` | Yes | - | page | `page.read` | Buffered console messages; filter by `level`, `limit` |
46
+ | 13 | `page.wait_for_load_state` | Yes | - | wait | `page.read` | Block until tab `complete`; `timeoutMs` capped 30 s |
47
+ | 14 | `page.get_storage` | Yes | - | page | `page.read` | `localStorage`/`sessionStorage`; optional `keys` |
48
+ | 15 | `page.get_text` | Yes | - | page | `page.read` | Full page text; `textBudget` limits size |
49
+ | 16 | `page.get_network` | Yes | - | page | `network.read` | Intercepted fetch/XHR; `limit` entries |
50
+ | 17 | `navigation.navigate` | Yes | - | navigate | `navigation.control` | Go to URL; `waitForLoad` default true |
51
+ | 18 | `navigation.reload` | Yes | - | navigate | `navigation.control` | Reload; `waitForLoad` default true |
52
+ | 19 | `navigation.go_back` | Yes | - | navigate | `navigation.control` | History back |
53
+ | 20 | `navigation.go_forward` | Yes | - | navigate | `navigation.control` | History forward |
54
+ | 21 | `dom.query` | Yes | - | inspect | `dom.read` | Query subtree with budget constraints |
55
+ | 22 | `dom.describe` | Yes | - | inspect | `dom.read` | Single element details via `elementRef` |
56
+ | 23 | `dom.get_text` | Yes | - | inspect | `dom.read` | Text content with `textBudget` |
57
+ | 24 | `dom.get_attributes` | Yes | - | inspect | `dom.read` | Targeted attribute read |
58
+ | 25 | `dom.wait_for` | Yes | - | wait | `dom.read` | Wait for DOM condition; MutationObserver + polling |
59
+ | 26 | `dom.find_by_text` | Yes | - | inspect | `dom.read` | Find by visible text; returns `{nodes, count}` |
60
+ | 27 | `dom.find_by_role` | Yes | - | inspect | `dom.read` | Find by ARIA role; optional `name` filter |
61
+ | 28 | `dom.get_html` | Yes | - | inspect | `dom.read` | `innerHTML`/`outerHTML`; `maxLength` truncation |
62
+ | 29 | `dom.get_accessibility_tree` | Yes | CDP | inspect | `dom.read` | Full a11y tree; `maxNodes`/`maxDepth` limits |
63
+ | 30 | `layout.get_box_model` | Yes | - | inspect | `layout.read` | Element geometry (no budget needed) |
64
+ | 31 | `layout.hit_test` | Yes | - | inspect | `layout.read` | Element at viewport point |
65
+ | 32 | `styles.get_computed` | Yes | - | inspect | `styles.read` | Computed CSS; always set `properties` |
66
+ | 33 | `styles.get_matched_rules` | Yes | - | inspect | `styles.read` | Matching CSS rules |
67
+ | 34 | `viewport.scroll` | Yes | - | navigate | `viewport.control` | Window or element scroll |
68
+ | 35 | `viewport.resize` | Yes | CDP | navigate | `viewport.control` | Set viewport via device emulation; `reset: true` |
69
+ | 36 | `input.click` | Yes | - | interact | `automation.input` | DOM-level click |
70
+ | 37 | `input.focus` | Yes | - | interact | `automation.input` | Focus element |
71
+ | 38 | `input.type` | Yes | - | interact | `automation.input` | Type into input/textarea/contenteditable |
72
+ | 39 | `input.press_key` | Yes | - | interact | `automation.input` | Single key event |
73
+ | 40 | `input.set_checked` | Yes | - | interact | `automation.input` | Checkbox/radio toggle |
74
+ | 41 | `input.select_option` | Yes | - | interact | `automation.input` | Native select by value/label/index |
75
+ | 42 | `input.hover` | Yes | - | interact | `automation.input` | mouseenter/mouseover/mousemove; optional `duration` |
76
+ | 43 | `input.drag` | Yes | - | interact | `automation.input` | Full drag-and-drop event sequence |
77
+ | 44 | `input.scroll_into_view` | Yes | - | interact | `automation.input` | Explicitly scroll target into view before inspect/capture |
78
+ | 45 | `screenshot.capture_element` | Yes | CDP | capture | `screenshot.partial` | Cropped element screenshot |
79
+ | 46 | `screenshot.capture_region` | Yes | CDP | capture | `screenshot.partial` | Cropped viewport region |
80
+ | 47 | `screenshot.capture_full_page` | Yes | CDP | capture | `screenshot.partial` | Full document screenshot; use only when page-level context is necessary |
81
+ | 48 | `patch.apply_styles` | Yes | - | patch | `patch.styles` | Reversible CSS patch; `verify` returns computed result |
82
+ | 49 | `patch.apply_dom` | Yes | - | patch | `patch.dom` | Reversible DOM mutation; `verify` returns result |
83
+ | 50 | `patch.list` | Yes | - | patch | `patch.dom` | Active patches |
84
+ | 51 | `patch.rollback` | Yes | - | patch | `patch.dom` | Revert one patch |
85
+ | 52 | `patch.commit_session_baseline` | Yes | - | patch | `patch.dom` | Accept current state as baseline |
86
+ | 53 | `performance.get_metrics` | Yes | CDP | performance | `performance.read` | Chrome performance counters |
87
+ | 54 | `cdp.get_document` | Yes | CDP | cdp | `cdp.dom_snapshot` | DevTools document tree |
88
+ | 55 | `cdp.get_dom_snapshot` | Yes | CDP | cdp | `cdp.dom_snapshot` | DevTools DOM snapshot |
89
+ | 56 | `cdp.get_box_model` | Yes | CDP | cdp | `cdp.box_model` | DevTools-backed element geometry |
90
+ | 57 | `cdp.get_computed_styles_for_node` | Yes | CDP | cdp | `cdp.styles` | DevTools-backed computed styles |
91
+ | 58 | `cdp.dispatch_key_event` | Yes | CDP | cdp | `cdp.input` | DevTools keyDown/keyUp without foreground focus |
91
92
 
92
93
  ## CLI
93
94
 
@@ -98,26 +99,31 @@ bbx call --tab 123 <method> '{...}' # explicit tab target inside enabled
98
99
  bbx batch '[{"method":"...","params":{}}]' # parallel calls
99
100
  ```
100
101
 
101
- **Convenience shortcuts:** `access-request`, `dom-query`, `describe`, `text`, `styles`, `box`, `click`, `focus`, `type`, `press-key`, `patch-style`, `patch-text`, `patches`, `rollback`, `screenshot`, `eval`, `console`, `wait`, `find`, `find-role`, `html`, `hover`, `navigate`, `storage`, `tab-create`, `tab-close`, `page-text`, `network`, `a11y-tree`, `perf`, `resize`, `reload`, `back`, `forward`, `attrs`, `matched-rules`
102
+ **Convenience shortcuts:** `access-request`, `dom-query`, `describe`, `text`, `styles`, `box`, `click`, `focus`, `type`, `press-key`, `cdp-press-key`, `patch-style`, `patch-text`, `patches`, `rollback`, `screenshot`, `eval`, `console`, `wait`, `find`, `find-role`, `html`, `hover`, `navigate`, `storage`, `tab-create`, `tab-close`, `page-text`, `network`, `a11y-tree`, `perf`, `resize`, `reload`, `back`, `forward`, `attrs`, `matched-rules`
102
103
 
103
104
  Newer bridge methods such as `input.scroll_into_view` and `screenshot.capture_full_page` currently use the raw path: `bbx call <method> '{...}'`.
104
105
 
105
106
  ## Method Details
106
107
 
107
108
  ### access.request
109
+
108
110
  Request Browser Bridge access for the focused browser window. Surfaces an Enable prompt in the extension popup or side panel so the user can grant access. Does not require an existing session.
111
+
109
112
  ```bash
110
113
  bbx access-request
111
114
  bbx call access.request
112
115
  ```
116
+
113
117
  If a tab-bound call returns `ACCESS_DENIED`, it also surfaces the Enable prompt automatically — so explicit `access.request` is optional but useful for proactive setup.
114
118
 
115
119
  If access is already pending for a window, do not call `access.request` again. Ask the user to click `Enable` for the requested window and wait for confirmation before continuing.
116
120
 
117
121
  ### page.evaluate
122
+
118
123
  Run a JS expression in the page context via CDP `Runtime.evaluate`. Expression is evaluated as a statement and the return value is serialized. Supports `awaitPromise` for async expressions.
119
124
 
120
125
  Use only when non-debugger reads are insufficient. Prefer `page.get_storage`, `page.get_text`, `page.get_console`, `page.get_network`, or DOM methods first.
126
+
121
127
  ```bash
122
128
  bbx eval 'document.title'
123
129
  bbx eval 'window.__NEXT_DATA__.props'
@@ -125,22 +131,29 @@ bbx call page.evaluate '{"expression":"await fetch(\"/api/health\").then(r=>r.js
125
131
  ```
126
132
 
127
133
  ### page.get_console
134
+
128
135
  Read buffered console output. The console interceptor is auto-installed on first call. Captures `log`, `warn`, `error`, `info`, `debug` plus uncaught exceptions and unhandled rejections.
136
+
129
137
  ```bash
130
138
  bbx console # all levels
131
139
  bbx console error # errors only
132
140
  bbx call page.get_console '{"level":"error","limit":20,"clear":true}'
133
141
  ```
142
+
134
143
  Responses include `dropped` when older buffered entries were discarded on noisy pages.
135
144
 
136
145
  ### page.wait_for_load_state
146
+
137
147
  Block until the tab reaches `complete` status. Useful after `input.click` on a navigation link.
148
+
138
149
  ```bash
139
150
  bbx call page.wait_for_load_state '{"timeoutMs":10000}'
140
151
  ```
141
152
 
142
153
  ### page.get_storage
154
+
143
155
  Read `localStorage` or `sessionStorage` entries. Values truncated at 500 chars each.
156
+
144
157
  ```bash
145
158
  bbx storage # all localStorage
146
159
  bbx storage session token,user # specific sessionStorage keys
@@ -148,67 +161,87 @@ bbx call page.get_storage '{"type":"session","keys":["token"]}'
148
161
  ```
149
162
 
150
163
  ### dom.query
164
+
151
165
  Run a bounded breadth-first DOM summary rooted at a selector or existing ref. Returns `{nodes, revision, truncated, registrySize}` and may also include `_registryPruned: true` when the element registry evicted older refs.
166
+
152
167
  ```bash
153
168
  bbx dom-query main
154
169
  bbx call dom.query '{"selector":"main","maxNodes":10,"attributeAllowlist":["class","data-testid"]}'
155
170
  ```
171
+
156
172
  If `_registryPruned` is true, refresh previously cached refs before reusing them.
157
173
 
158
174
  ### dom.wait_for
175
+
159
176
  Wait for a DOM condition using MutationObserver + 250 ms polling fallback. Returns `{found, elementRef, duration}`.
177
+
160
178
  - `state`: `attached` (default), `detached`, `visible`, `hidden`
161
179
  - `text`: optional text content filter
162
180
  - `timeoutMs`: 100–30000 (default 5000)
181
+
163
182
  ```bash
164
183
  bbx wait '.toast-success' 5000
165
184
  bbx call dom.wait_for '{"selector":".modal","state":"visible","timeoutMs":10000}'
166
185
  ```
167
186
 
168
187
  ### dom.find_by_text
188
+
169
189
  Find elements matching visible text content. Like Playwright's `getByText`.
190
+
170
191
  ```bash
171
192
  bbx find 'Submit Order'
172
193
  bbx call dom.find_by_text '{"text":"Submit","scope":"button","exact":false}'
173
194
  ```
174
195
 
175
196
  ### dom.find_by_role
197
+
176
198
  Find elements by ARIA role (explicit `role` attribute or implicit from HTML tag). Covers 25+ implicit role mappings.
199
+
177
200
  ```bash
178
201
  bbx find-role button 'Save'
179
202
  bbx call dom.find_by_role '{"role":"navigation"}'
180
203
  ```
181
204
 
182
205
  ### dom.get_html
206
+
183
207
  Get raw HTML of an element. Defaults to `innerHTML`; set `outer: true` for `outerHTML`.
208
+
184
209
  ```bash
185
210
  bbx html el_abc123
186
211
  bbx call dom.get_html '{"elementRef":"el_abc123","outer":true,"maxLength":2000}'
187
212
  ```
188
213
 
189
214
  ### input.hover
215
+
190
216
  Trigger CSS `:hover` state by dispatching `mouseenter`, `mouseover`, `mousemove`. Optional `duration` to hold hover before auto-releasing.
217
+
191
218
  ```bash
192
219
  bbx hover el_abc123
193
220
  bbx call input.hover '{"target":{"elementRef":"el_abc123"},"duration":1000}'
194
221
  ```
195
222
 
196
223
  ### input.drag
224
+
197
225
  Full drag-and-drop sequence: `mousedown → dragstart → drag → dragenter → dragover → drop → dragend → mouseup`. Accepts source target, destination target, and optional pixel offsets.
226
+
198
227
  ```bash
199
228
  bbx call input.drag '{"source":{"elementRef":"el_src"},"destination":{"elementRef":"el_dst"}}'
200
229
  bbx call input.drag '{"source":{"elementRef":"el_src"},"destination":{"elementRef":"el_dst"},"sourceOffset":{"x":10,"y":10}}'
201
230
  ```
202
231
 
203
232
  ### input.scroll_into_view
233
+
204
234
  Explicitly scroll an element into the visible viewport before inspecting, hovering, or capturing it.
235
+
205
236
  ```bash
206
237
  bbx call input.scroll_into_view '{"target":{"elementRef":"el_abc123"}}'
207
238
  bbx call input.scroll_into_view '{"target":{"selector":"[data-testid=\\"checkout-summary\\"]"}}'
208
239
  ```
209
240
 
210
241
  ### tabs.create
242
+
211
243
  Open a new browser tab. Optional `url` (defaults to `about:blank`) and `active` flag (defaults to `true`). Does not require a session.
244
+
212
245
  ```bash
213
246
  bbx tab-create https://example.com
214
247
  bbx call tabs.create '{"url":"https://example.com","active":false}'
@@ -217,20 +250,26 @@ bbx call tabs.create '{"url":"https://example.com","active":false}'
217
250
  The `bbx tab-create` shortcut intentionally covers the common case. Use `bbx call tabs.create ...` when you need advanced fields such as `active:false`.
218
251
 
219
252
  ### setup.get_status
253
+
220
254
  Inspect the host-side Browser Bridge setup. Returns global MCP config status for supported clients and global CLI skill install status for supported targets.
255
+
221
256
  ```bash
222
257
  bbx call setup.get_status
223
258
  ```
224
259
 
225
260
  ### tabs.close
261
+
226
262
  Close a tab by its `tabId`. Does not require a session.
263
+
227
264
  ```bash
228
265
  bbx tab-close 12345
229
266
  bbx call tabs.close '{"tabId":12345}'
230
267
  ```
231
268
 
232
269
  ### page.get_text
270
+
233
271
  Extract the full visible text content of the page (`document.body.innerText`). Truncated to `textBudget` (default 8000 chars). Lighter than `dom.query` on `body` when you only need text.
272
+
234
273
  ```bash
235
274
  bbx page-text
236
275
  bbx page-text 8000
@@ -238,18 +277,23 @@ bbx call page.get_text '{"textBudget":2000}'
238
277
  ```
239
278
 
240
279
  ### page.get_network
280
+
241
281
  Read intercepted fetch/XHR requests. The interceptor is auto-installed on first call (via MAIN world script). Returns `{entries, count}` sorted newest-first.
282
+
242
283
  ```bash
243
284
  bbx network
244
285
  bbx network 50
245
286
  bbx call page.get_network '{"limit":20,"clear":true}'
246
287
  ```
288
+
247
289
  Each entry: `{method, url, status, duration, initiator}`. Responses include `dropped` when older buffered entries were discarded.
248
290
 
249
291
  ### dom.get_accessibility_tree
292
+
250
293
  Retrieve the page's accessibility tree via CDP `Accessibility.getFullAXTree`. Each node is simplified to: `role`, `name`, `description`, `value`, `focused`, `required`, `checked`, `disabled`, `interactive`, `childIds`. Use `maxNodes` and `maxDepth` to control size.
251
294
 
252
295
  This is debugger-backed. Prefer `dom.find_by_role`, `dom.find_by_text`, and targeted `dom.query`/`dom.describe` first.
296
+
253
297
  ```bash
254
298
  bbx a11y-tree
255
299
  bbx a11y-tree 50 3
@@ -257,9 +301,11 @@ bbx call dom.get_accessibility_tree '{"maxNodes":100,"maxDepth":5}'
257
301
  ```
258
302
 
259
303
  ### viewport.resize
304
+
260
305
  Set the browser viewport to specific dimensions using CDP device emulation. Pass `reset: true` to clear the override.
261
306
 
262
307
  Debugger-backed. Use only when an exact viewport override is required for responsive verification.
308
+
263
309
  ```bash
264
310
  bbx resize 375 812
265
311
  bbx call viewport.resize '{"width":1024,"height":768}'
@@ -267,16 +313,20 @@ bbx call viewport.resize '{"reset":true}'
267
313
  ```
268
314
 
269
315
  ### performance.get_metrics
316
+
270
317
  Read Chrome performance counters via CDP `Performance.getMetrics`. Returns a flat `{metrics}` object with keys like `JSHeapUsedSize`, `LayoutCount`, `TaskDuration`, etc.
271
318
 
272
319
  Debugger-backed. Use after lighter reads fail to explain a performance symptom.
320
+
273
321
  ```bash
274
322
  bbx perf
275
323
  bbx call performance.get_metrics
276
324
  ```
277
325
 
278
326
  ### screenshot.capture_full_page
327
+
279
328
  Capture a full-document screenshot beyond the current viewport. Use only when element or tight region captures cannot express the issue. Chrome capture limits still apply on very large pages.
329
+
280
330
  ```bash
281
331
  bbx call screenshot.capture_full_page '{}'
282
332
  ```
@@ -284,21 +334,27 @@ bbx call screenshot.capture_full_page '{}'
284
334
  ## Request Envelope
285
335
 
286
336
  ```json
287
- {"id":"req_1","tab_id":123,"method":"dom.query","params":{},"meta":{"protocol_version":"1.0","token_budget":1200}}
337
+ {
338
+ "id": "req_1",
339
+ "tab_id": 123,
340
+ "method": "dom.query",
341
+ "params": {},
342
+ "meta": { "protocol_version": "1.0", "token_budget": 1200 }
343
+ }
288
344
  ```
289
345
 
290
346
  ## Error Codes
291
347
 
292
- | Code | Action | Recovery |
293
- |------|--------|----------|
294
- | `ACCESS_DENIED` | Turn on Browser Bridge for the target window | `retry: false` |
295
- | `TAB_MISMATCH` | Explicit `tabId` is missing, closed, or outside enabled window | `retry: false`, use `tabs.list` |
296
- | `ELEMENT_STALE` | Re-query DOM for fresh `elementRef` | `retry: false`, use `dom.query` |
297
- | `CONTENT_SCRIPT_UNAVAILABLE` | Page is restricted (chrome://, extensions, etc.) | `retry: false` |
298
- | `NATIVE_HOST_UNAVAILABLE` | Check daemon: `bbx status` | `retry: false` |
299
- | `EXTENSION_DISCONNECTED` | Extension not connected to daemon | `retry: true` after 3 s, check `health.ping` |
300
- | `TIMEOUT` | Wait/evaluate exceeded `timeoutMs` | `retry: true` after 1 s |
301
- | `RATE_LIMITED` | Too many requests | `retry: true` after 2 s |
348
+ | Code | Action | Recovery |
349
+ | ---------------------------- | -------------------------------------------------------------- | -------------------------------------------- |
350
+ | `ACCESS_DENIED` | Turn on Browser Bridge for the target window | `retry: false` |
351
+ | `TAB_MISMATCH` | Explicit `tabId` is missing, closed, or outside enabled window | `retry: false`, use `tabs.list` |
352
+ | `ELEMENT_STALE` | Re-query DOM for fresh `elementRef` | `retry: false`, use `dom.query` |
353
+ | `CONTENT_SCRIPT_UNAVAILABLE` | Page is restricted (chrome://, extensions, etc.) | `retry: false` |
354
+ | `NATIVE_HOST_UNAVAILABLE` | Check daemon: `bbx status` | `retry: false` |
355
+ | `EXTENSION_DISCONNECTED` | Extension not connected to daemon | `retry: true` after 3 s, check `health.ping` |
356
+ | `TIMEOUT` | Wait/evaluate exceeded `timeoutMs` | `retry: true` after 1 s |
357
+ | `RATE_LIMITED` | Too many requests | `retry: true` after 2 s |
302
358
 
303
359
  Timeout on content-script request → use narrower `dom.query` or CDP fallback.
304
360
  Timeout on navigation → increase `timeoutMs`, set `waitForLoad:false`, or check `page.get_state`.
@@ -6,11 +6,11 @@
6
6
 
7
7
  Tailwind arbitrary-value classes use `[]` which are invalid in CSS selectors:
8
8
 
9
- | Class in HTML | ❌ Raw selector | ✅ Escaped selector |
10
- |---|---|---|
11
- | `top-[30px]` | `.top-[30px]` | `.top-\[30px\]` |
12
- | `bg-[#f00]` | `.bg-[#f00]` | `.bg-\[\#f00\]` |
13
- | `w-[calc(100%-2rem)]` | crashes | `.w-\[calc\(100\%-2rem\)\]` |
9
+ | Class in HTML | ❌ Raw selector | ✅ Escaped selector |
10
+ | --------------------- | --------------- | --------------------------- |
11
+ | `top-[30px]` | `.top-[30px]` | `.top-\[30px\]` |
12
+ | `bg-[#f00]` | `.bg-[#f00]` | `.bg-\[\#f00\]` |
13
+ | `w-[calc(100%-2rem)]` | crashes | `.w-\[calc\(100\%-2rem\)\]` |
14
14
 
15
15
  **Bridge auto-escapes** Tailwind brackets in `dom.query` selectors. But for `page.evaluate` or `dom.wait_for`, escape manually or avoid class-based selectors entirely.
16
16
 
@@ -37,6 +37,7 @@ bbx styles el_abc 'display,align-items,gap,padding,background-color,border-radiu
37
37
  ```
38
38
 
39
39
  Key patterns:
40
+
40
41
  - **Layout**: `styles.get_computed` with `display, flex-direction, gap, grid-template-columns`
41
42
  - **Spacing**: `layout.get_box_model` - gives padding/margin/border as numbers
42
43
  - **Colors**: `styles.get_computed` with `background-color, color, border-color`
@@ -75,9 +76,9 @@ Default Tailwind breakpoints: `sm: 640px`, `md: 768px`, `lg: 1024px`, `xl: 1280p
75
76
 
76
77
  ## Common Tailwind Anti-Patterns
77
78
 
78
- | Anti-pattern | Cost | Fix |
79
- |---|---|---|
80
- | Selecting by Tailwind class `.flex.items-center` | Fragile, breaks on refactor | `dom.find_by_role` or `dom.find_by_text` |
81
- | Parsing class string to infer styles | ~500 tok wasted | `styles.get_computed` with property list |
82
- | Patching by adding Tailwind classes | Won't work (classes need build) | `patch.apply_styles` with CSS properties |
83
- | Using `!important` in patches | Unnecessary | Inline styles already beat utilities |
79
+ | Anti-pattern | Cost | Fix |
80
+ | ------------------------------------------------ | ------------------------------- | ---------------------------------------- |
81
+ | Selecting by Tailwind class `.flex.items-center` | Fragile, breaks on refactor | `dom.find_by_role` or `dom.find_by_text` |
82
+ | Parsing class string to infer styles | ~500 tok wasted | `styles.get_computed` with property list |
83
+ | Patching by adding Tailwind classes | Won't work (classes need build) | `patch.apply_styles` with CSS properties |
84
+ | Using `!important` in patches | Unnecessary | Inline styles already beat utilities |
@@ -2,11 +2,11 @@
2
2
 
3
3
  ## Budget Presets
4
4
 
5
- | Preset | maxNodes | maxDepth | textBudget | Use When |
6
- |--------|----------|----------|------------|----------|
7
- | quick | 5 | 2 | 300 | Checking one element or confirming state |
8
- | normal | 25 | 4 | 600 | General inspection (default) |
9
- | deep | 100 | 8 | 2000 | Complex nested components |
5
+ | Preset | maxNodes | maxDepth | textBudget | Use When |
6
+ | ------ | -------- | -------- | ---------- | ---------------------------------------- |
7
+ | quick | 5 | 2 | 300 | Checking one element or confirming state |
8
+ | normal | 25 | 4 | 600 | General inspection (default) |
9
+ | deep | 100 | 8 | 2000 | Complex nested components |
10
10
 
11
11
  Always start at **quick** or **normal**; widen only if the result indicates truncation.
12
12
 
@@ -51,23 +51,23 @@ Omitting allowlists or leaving the text budget wide open often returns 3–5× t
51
51
 
52
52
  ## Anti-Patterns (Token Waste)
53
53
 
54
- | Pattern | Cost | Fix |
55
- |---------|------|-----|
56
- | `dom.query` on `body` with no budget | ~2000 tok | Use specific selector + quick budget |
57
- | Screenshot before structured read | ~1500 tok wasted | Always `dom.query` or `styles.get_computed` first |
58
- | Re-querying DOM for same element | ~500 tok/call | Reuse `elementRef` from prior result |
59
- | Full-page screenshot | ~3000 tok | Use `screenshot.capture_element`, or `screenshot.capture_region` with a tight rect |
60
- | Requesting all computed styles | ~800 tok | Set `properties` list (usually 3–8 props) |
61
- | Multiple CLI calls for independent reads | overhead/call | Use `batch` command |
62
- | Guessing selectors for known labels | ~300 tok wasted/try | Use `dom.find_by_text` or `dom.find_by_role` |
63
- | Polling page state with repeated queries | ~500 tok/poll | Use `dom.wait_for` (single call, waits async) |
64
- | Inspecting DOM to read app state | ~800 tok | Use `page.evaluate` to read JS directly |
65
- | Re-querying after HMR without waiting | ~500 tok stale | `dom.wait_for` first, then query |
66
- | Separate call to verify a patch | ~500 tok wasted | Set `verify: true` on `patch.apply_styles` / `patch.apply_dom` to get computed result inline |
67
- | `dom.query` on body for page text | ~2000 tok | Use `page.get_text` (extracts innerText directly) |
68
- | Guessing interactive elements from DOM | ~600 tok/try | Use `dom.get_accessibility_tree` for semantic roles |
69
- | Fetching network via evaluate hacks | ~400 tok | Use `page.get_network` (auto-interceptor) |
70
- | Full a11y tree with no limits | ~3000 tok | Set `maxNodes` ≤ 50, `maxDepth` ≤ 4 |
54
+ | Pattern | Cost | Fix |
55
+ | ---------------------------------------- | ------------------- | -------------------------------------------------------------------------------------------- |
56
+ | `dom.query` on `body` with no budget | ~2000 tok | Use specific selector + quick budget |
57
+ | Screenshot before structured read | ~1500 tok wasted | Always `dom.query` or `styles.get_computed` first |
58
+ | Re-querying DOM for same element | ~500 tok/call | Reuse `elementRef` from prior result |
59
+ | Full-page screenshot | ~3000 tok | Use `screenshot.capture_element`, or `screenshot.capture_region` with a tight rect |
60
+ | Requesting all computed styles | ~800 tok | Set `properties` list (usually 3–8 props) |
61
+ | Multiple CLI calls for independent reads | overhead/call | Use `batch` command |
62
+ | Guessing selectors for known labels | ~300 tok wasted/try | Use `dom.find_by_text` or `dom.find_by_role` |
63
+ | Polling page state with repeated queries | ~500 tok/poll | Use `dom.wait_for` (single call, waits async) |
64
+ | Inspecting DOM to read app state | ~800 tok | Use `page.evaluate` to read JS directly |
65
+ | Re-querying after HMR without waiting | ~500 tok stale | `dom.wait_for` first, then query |
66
+ | Separate call to verify a patch | ~500 tok wasted | Set `verify: true` on `patch.apply_styles` / `patch.apply_dom` to get computed result inline |
67
+ | `dom.query` on body for page text | ~2000 tok | Use `page.get_text` (extracts innerText directly) |
68
+ | Guessing interactive elements from DOM | ~600 tok/try | Use `dom.get_accessibility_tree` for semantic roles |
69
+ | Fetching network via evaluate hacks | ~400 tok | Use `page.get_network` (auto-interceptor) |
70
+ | Full a11y tree with no limits | ~3000 tok | Set `maxNodes` ≤ 50, `maxDepth` ≤ 4 |
71
71
 
72
72
  ## Efficient Loop
73
73
 
@@ -162,6 +162,7 @@ bbx eval 'module.hot?.status?.()' # check HMR status (webpack)
162
162
  ## Parent-Agent Response Policy
163
163
 
164
164
  The subagent should return:
165
+
165
166
  - What was inspected (selector or elementRef)
166
167
  - What changed (if patching)
167
168
  - Whether it answers the question
@@ -14,6 +14,7 @@ Use when a dev server is already running and you want to prove a fix rendered.
14
14
  8. `patch.rollback`
15
15
 
16
16
  Acceptance:
17
+
17
18
  - target element renders with expected layout or styles
18
19
  - no new console errors after HMR
19
20
  - temporary patch is rolled back
@@ -30,6 +31,7 @@ Use when a submission flow fails silently or the UI state is inconsistent.
30
31
  6. `dom.query` on the resulting panel or message
31
32
 
32
33
  Acceptance:
34
+
33
35
  - request status and failing endpoint are identified
34
36
  - visible error or success state matches the network result
35
37
  - console exceptions are ruled in or out
@@ -47,6 +49,7 @@ Use when comparing the live UI to an expected layout or visual spec.
47
49
  7. `patch.rollback`
48
50
 
49
51
  Acceptance:
52
+
50
53
  - the live patch proves the intended fix
51
54
  - source implementation matches the validated patch
52
55
  - no patch is left active
@@ -64,6 +67,7 @@ Use when a component only breaks at a specific breakpoint.
64
67
  7. `viewport.resize` with `reset: true`
65
68
 
66
69
  Acceptance:
70
+
67
71
  - the target breakpoint is reproduced
68
72
  - geometry and style regressions are confirmed without a full screenshot
69
73
  - viewport override is reset
@@ -73,18 +77,21 @@ Acceptance:
73
77
  Use when transient states matter.
74
78
 
75
79
  Hover:
80
+
76
81
  1. `dom.find_by_text` or `dom.find_by_role`
77
82
  2. `input.hover`
78
83
  3. `dom.query` for tooltip or menu
79
84
  4. `styles.get_computed`
80
85
 
81
86
  Drag:
87
+
82
88
  1. `dom.query` for source and destination
83
89
  2. `input.drag`
84
90
  3. `dom.wait_for`
85
91
  4. `dom.query` to verify order or placement
86
92
 
87
93
  Acceptance:
94
+
88
95
  - transient state appears while the interaction is active
89
96
  - DOM and layout confirm the intended state change
90
97
 
@@ -97,6 +104,7 @@ Use when semantic navigation matters more than CSS selectors.
97
104
  3. `dom.get_accessibility_tree` only if role discovery is insufficient
98
105
 
99
106
  Acceptance:
107
+
100
108
  - expected interactive roles are present
101
109
  - accessible names match the UI copy
102
110
  - the full accessibility tree is only used when lighter reads fail