@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.
- package/README.md +3 -1
- package/docs/api-reference.md +33 -33
- package/docs/mcp-vs-cli.md +104 -104
- package/docs/publishing.md +1 -3
- package/docs/quickstart.md +6 -6
- package/docs/unpacked-extension.md +72 -0
- package/manifest.json +3 -17
- package/package.json +44 -42
- package/packages/agent-client/src/cli-helpers.js +10 -5
- package/packages/agent-client/src/cli.js +65 -135
- package/packages/agent-client/src/client.js +37 -17
- package/packages/agent-client/src/command-registry.js +101 -69
- package/packages/agent-client/src/detect.js +3 -6
- package/packages/agent-client/src/install.js +10 -27
- package/packages/agent-client/src/mcp-config.js +11 -30
- package/packages/agent-client/src/runtime.js +41 -20
- package/packages/agent-client/src/setup-status.js +13 -28
- package/packages/extension/src/background-helpers.js +51 -36
- package/packages/extension/src/background-routing.js +11 -13
- package/packages/extension/src/background.js +562 -299
- package/packages/extension/src/content-script-helpers.js +17 -16
- package/packages/extension/src/content-script.js +175 -109
- package/packages/extension/src/sidepanel-helpers.js +3 -1
- package/packages/extension/ui/popup.js +39 -20
- package/packages/extension/ui/sidepanel.js +108 -191
- package/packages/extension/ui/ui.css +2 -1
- package/packages/mcp-server/src/handlers.js +546 -250
- package/packages/mcp-server/src/server.js +558 -257
- package/packages/native-host/bin/bridge-daemon.js +6 -2
- package/packages/native-host/bin/install-manifest.js +2 -2
- package/packages/native-host/bin/postinstall.js +4 -2
- package/packages/native-host/src/config.js +11 -7
- package/packages/native-host/src/daemon.js +143 -92
- package/packages/native-host/src/install-manifest.js +73 -22
- package/packages/native-host/src/native-host.js +55 -40
- package/packages/protocol/src/budget.js +3 -7
- package/packages/protocol/src/capabilities.js +3 -3
- package/packages/protocol/src/errors.js +11 -11
- package/packages/protocol/src/protocol.js +104 -71
- package/packages/protocol/src/registry.js +300 -45
- package/packages/protocol/src/summary.js +249 -106
- package/packages/protocol/src/types.js +1 -1
- package/skills/browser-bridge/SKILL.md +1 -1
- package/skills/browser-bridge/agents/openai.yaml +3 -3
- package/skills/browser-bridge/references/interaction.md +33 -11
- package/skills/browser-bridge/references/patch-workflow.md +3 -0
- package/skills/browser-bridge/references/protocol.md +125 -70
- package/skills/browser-bridge/references/tailwind.md +12 -11
- package/skills/browser-bridge/references/token-efficiency.md +23 -22
- package/skills/browser-bridge/references/ui-workflows.md +8 -0
- package/packages/extension/ui/offscreen.html +0 -6
- 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
|
|
10
|
-
|
|
11
|
-
| `top-[30px]`
|
|
12
|
-
| `bg-[#f00]`
|
|
13
|
-
| `w-[calc(100%-2rem)]` | crashes
|
|
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
|
|
79
|
-
|
|
80
|
-
| Selecting by Tailwind class `.flex.items-center` | Fragile, breaks on refactor
|
|
81
|
-
| Parsing class string to infer styles
|
|
82
|
-
| Patching by adding Tailwind classes
|
|
83
|
-
| Using `!important` in patches
|
|
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
|
|
8
|
-
| normal | 25
|
|
9
|
-
| deep
|
|
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
|
|
55
|
-
|
|
56
|
-
| `dom.query` on `body` with no budget
|
|
57
|
-
| Screenshot before structured read
|
|
58
|
-
| Re-querying DOM for same element
|
|
59
|
-
| Full-page screenshot
|
|
60
|
-
| Requesting all computed styles
|
|
61
|
-
| Multiple CLI calls for independent reads | overhead/call
|
|
62
|
-
| Guessing selectors for known labels
|
|
63
|
-
| Polling page state with repeated queries | ~500 tok/poll
|
|
64
|
-
| Inspecting DOM to read app state
|
|
65
|
-
| Re-querying after HMR without waiting
|
|
66
|
-
| Separate call to verify a patch
|
|
67
|
-
| `dom.query` on body for page text
|
|
68
|
-
| Guessing interactive elements from DOM
|
|
69
|
-
| Fetching network via evaluate hacks
|
|
70
|
-
| Full a11y tree with no limits
|
|
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,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
|
-
}
|