@browserbridge/bbx 1.0.0 → 1.0.1

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 (52) hide show
  1. package/README.md +3 -1
  2. package/docs/api-reference.md +33 -33
  3. package/docs/mcp-vs-cli.md +104 -104
  4. package/docs/publishing.md +1 -3
  5. package/docs/quickstart.md +6 -6
  6. package/docs/unpacked-extension.md +72 -0
  7. package/manifest.json +3 -17
  8. package/package.json +44 -42
  9. package/packages/agent-client/src/cli-helpers.js +10 -5
  10. package/packages/agent-client/src/cli.js +65 -135
  11. package/packages/agent-client/src/client.js +37 -17
  12. package/packages/agent-client/src/command-registry.js +101 -69
  13. package/packages/agent-client/src/detect.js +3 -6
  14. package/packages/agent-client/src/install.js +10 -27
  15. package/packages/agent-client/src/mcp-config.js +11 -30
  16. package/packages/agent-client/src/runtime.js +41 -20
  17. package/packages/agent-client/src/setup-status.js +13 -28
  18. package/packages/extension/src/background-helpers.js +51 -36
  19. package/packages/extension/src/background-routing.js +11 -13
  20. package/packages/extension/src/background.js +562 -299
  21. package/packages/extension/src/content-script-helpers.js +17 -16
  22. package/packages/extension/src/content-script.js +175 -109
  23. package/packages/extension/src/sidepanel-helpers.js +3 -1
  24. package/packages/extension/ui/popup.js +39 -20
  25. package/packages/extension/ui/sidepanel.js +108 -191
  26. package/packages/extension/ui/ui.css +2 -1
  27. package/packages/mcp-server/src/handlers.js +546 -250
  28. package/packages/mcp-server/src/server.js +558 -257
  29. package/packages/native-host/bin/bridge-daemon.js +6 -2
  30. package/packages/native-host/bin/install-manifest.js +2 -2
  31. package/packages/native-host/bin/postinstall.js +4 -2
  32. package/packages/native-host/src/config.js +11 -7
  33. package/packages/native-host/src/daemon.js +143 -92
  34. package/packages/native-host/src/install-manifest.js +73 -22
  35. package/packages/native-host/src/native-host.js +55 -40
  36. package/packages/protocol/src/budget.js +3 -7
  37. package/packages/protocol/src/capabilities.js +3 -3
  38. package/packages/protocol/src/errors.js +11 -11
  39. package/packages/protocol/src/protocol.js +104 -71
  40. package/packages/protocol/src/registry.js +300 -45
  41. package/packages/protocol/src/summary.js +249 -106
  42. package/packages/protocol/src/types.js +1 -1
  43. package/skills/browser-bridge/SKILL.md +1 -1
  44. package/skills/browser-bridge/agents/openai.yaml +3 -3
  45. package/skills/browser-bridge/references/interaction.md +33 -11
  46. package/skills/browser-bridge/references/patch-workflow.md +3 -0
  47. package/skills/browser-bridge/references/protocol.md +125 -70
  48. package/skills/browser-bridge/references/tailwind.md +12 -11
  49. package/skills/browser-bridge/references/token-efficiency.md +23 -22
  50. package/skills/browser-bridge/references/ui-workflows.md +8 -0
  51. package/packages/extension/ui/offscreen.html +0 -6
  52. package/packages/extension/ui/offscreen.js +0 -61
@@ -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
@@ -1,6 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <body>
4
- <script type="module" src="./offscreen.js"></script>
5
- </body>
6
- </html>
@@ -1,61 +0,0 @@
1
- // @ts-check
2
-
3
- /**
4
- * @typedef {{
5
- * type?: string,
6
- * image?: string,
7
- * rect?: { x: number, y: number, width: number, height: number }
8
- * }} CropMessage
9
- */
10
-
11
- chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
12
- if (message?.type !== 'bridge.crop-image') {
13
- return false;
14
- }
15
-
16
- const typedMessage = /** @type {CropMessage} */ (message);
17
- crop(typedMessage.image || '', typedMessage.rect || { x: 0, y: 0, width: 1, height: 1 }).then(sendResponse);
18
- return true;
19
- });
20
-
21
- /**
22
- * @param {string} imageUrl
23
- * @param {{ x: number, y: number, width: number, height: number }} rect
24
- * @returns {Promise<string>}
25
- */
26
- async function crop(imageUrl, rect) {
27
- const response = await fetch(imageUrl);
28
- const blob = await response.blob();
29
- const bitmap = await createImageBitmap(blob);
30
-
31
- // Clamp crop rect to bitmap bounds to prevent out-of-bounds draws
32
- const x = Math.max(0, Math.min(rect.x, bitmap.width - 1));
33
- const y = Math.max(0, Math.min(rect.y, bitmap.height - 1));
34
- const w = Math.max(1, Math.min(rect.width, bitmap.width - x));
35
- const h = Math.max(1, Math.min(rect.height, bitmap.height - y));
36
-
37
- const canvas = new OffscreenCanvas(w, h);
38
- const context = canvas.getContext('2d');
39
- if (!context) {
40
- throw new Error('Failed to create 2D offscreen canvas context.');
41
- }
42
- context.drawImage(bitmap, x, y, w, h, 0, 0, w, h);
43
- bitmap.close();
44
- const croppedBlob = await canvas.convertToBlob({ type: 'image/png' });
45
- return blobToDataUrl(croppedBlob);
46
- }
47
-
48
- /**
49
- * @param {Blob} blob
50
- * @returns {Promise<string>}
51
- */
52
- async function blobToDataUrl(blob) {
53
- const arrayBuffer = await blob.arrayBuffer();
54
- const bytes = new Uint8Array(arrayBuffer);
55
- const chunks = [];
56
- for (let i = 0; i < bytes.length; i += 8192) {
57
- chunks.push(String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + 8192))));
58
- }
59
- const base64 = btoa(chunks.join(''));
60
- return `data:${blob.type};base64,${base64}`;
61
- }