@browserbridge/bbx 1.0.1 → 1.2.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 (70) hide show
  1. package/README.md +4 -4
  2. package/package.json +11 -13
  3. package/packages/agent-client/src/cli-helpers.js +33 -0
  4. package/packages/agent-client/src/cli.js +122 -45
  5. package/packages/agent-client/src/client.js +134 -8
  6. package/packages/agent-client/src/command-registry.js +4 -1
  7. package/packages/agent-client/src/detect.js +159 -48
  8. package/packages/agent-client/src/install.js +24 -1
  9. package/packages/agent-client/src/mcp-config.js +29 -10
  10. package/packages/agent-client/src/setup-status.js +12 -4
  11. package/packages/mcp-server/src/bin.js +57 -5
  12. package/packages/mcp-server/src/handlers-capture.js +279 -0
  13. package/packages/mcp-server/src/handlers-dom.js +196 -0
  14. package/packages/mcp-server/src/handlers-navigation.js +79 -0
  15. package/packages/mcp-server/src/handlers-page.js +365 -0
  16. package/packages/mcp-server/src/handlers-utils.js +296 -0
  17. package/packages/mcp-server/src/handlers.js +63 -1159
  18. package/packages/mcp-server/src/server.js +13 -3
  19. package/packages/native-host/bin/bridge-daemon.js +34 -4
  20. package/packages/native-host/bin/install-manifest.js +32 -2
  21. package/packages/native-host/bin/postinstall.js +16 -0
  22. package/packages/native-host/src/config.js +131 -6
  23. package/packages/native-host/src/daemon-logger.js +157 -0
  24. package/packages/native-host/src/daemon-process.js +422 -0
  25. package/packages/native-host/src/daemon.js +322 -77
  26. package/packages/native-host/src/framing.js +131 -11
  27. package/packages/native-host/src/install-manifest.js +121 -7
  28. package/packages/native-host/src/native-host.js +110 -73
  29. package/packages/protocol/src/capabilities.js +4 -0
  30. package/packages/protocol/src/defaults.js +1 -0
  31. package/packages/protocol/src/errors.js +4 -0
  32. package/packages/protocol/src/payload-cost.js +19 -6
  33. package/packages/protocol/src/protocol.js +143 -7
  34. package/packages/protocol/src/registry.js +13 -0
  35. package/packages/protocol/src/summary.js +18 -10
  36. package/packages/protocol/src/types.js +28 -3
  37. package/skills/browser-bridge/SKILL.md +2 -1
  38. package/skills/browser-bridge/references/interaction.md +1 -0
  39. package/skills/browser-bridge/references/protocol.md +2 -1
  40. package/CHANGELOG.md +0 -55
  41. package/assets/banner.jpg +0 -0
  42. package/assets/logo.png +0 -0
  43. package/assets/logo.svg +0 -65
  44. package/docs/api-reference.md +0 -157
  45. package/docs/cli-guide.md +0 -128
  46. package/docs/index.md +0 -25
  47. package/docs/manual-setup.md +0 -140
  48. package/docs/mcp-vs-cli.md +0 -258
  49. package/docs/publishing.md +0 -112
  50. package/docs/quickstart.md +0 -104
  51. package/docs/troubleshooting.md +0 -59
  52. package/docs/unpacked-extension.md +0 -72
  53. package/docs/usage-scenarios.md +0 -136
  54. package/manifest.json +0 -38
  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 -474
  60. package/packages/extension/src/background-routing.js +0 -89
  61. package/packages/extension/src/background.js +0 -3490
  62. package/packages/extension/src/content-script-helpers.js +0 -282
  63. package/packages/extension/src/content-script.js +0 -2043
  64. package/packages/extension/src/debugger-coordinator.js +0 -188
  65. package/packages/extension/src/sidepanel-helpers.js +0 -104
  66. package/packages/extension/ui/popup.html +0 -35
  67. package/packages/extension/ui/popup.js +0 -298
  68. package/packages/extension/ui/sidepanel.html +0 -102
  69. package/packages/extension/ui/sidepanel.js +0 -1771
  70. package/packages/extension/ui/ui.css +0 -1160
@@ -1,282 +0,0 @@
1
- // @ts-check
2
-
3
- (() => {
4
- const globalState =
5
- /** @type {typeof globalThis & { __BBX_CONTENT_HELPERS__?: Record<string, unknown> }} */ (
6
- globalThis
7
- );
8
-
9
- if (globalState.__BBX_CONTENT_HELPERS__) {
10
- return;
11
- }
12
-
13
- /**
14
- * @typedef {{
15
- * maxNodes: number,
16
- * maxDepth: number,
17
- * textBudget: number,
18
- * includeBbox: boolean,
19
- * attributeAllowlist: string[]
20
- * }} Budget
21
- */
22
-
23
- const NON_TEXT_INPUT_TYPES = new Set([
24
- 'button',
25
- 'checkbox',
26
- 'color',
27
- 'file',
28
- 'hidden',
29
- 'image',
30
- 'radio',
31
- 'range',
32
- 'reset',
33
- 'submit',
34
- ]);
35
-
36
- /**
37
- * @param {number | string | null | undefined} value
38
- * @param {number} minimum
39
- * @param {number} maximum
40
- * @returns {number}
41
- */
42
- function clamp(value, minimum, maximum) {
43
- return Math.min(Math.max(Number(value) || minimum, minimum), maximum);
44
- }
45
-
46
- /**
47
- * @param {unknown} value
48
- * @returns {string[]}
49
- */
50
- function normalizeList(value) {
51
- if (!Array.isArray(value)) {
52
- return [];
53
- }
54
-
55
- return [...new Set(value.filter((item) => typeof item === 'string' && item.trim()))];
56
- }
57
-
58
- /**
59
- * @param {Record<string, any>} [options={}]
60
- * @returns {Budget}
61
- */
62
- function applyBudget(options = {}) {
63
- return {
64
- maxNodes: clamp(options.maxNodes ?? 25, 1, 250),
65
- maxDepth: clamp(options.maxDepth ?? 4, 1, 20),
66
- textBudget: clamp(options.textBudget ?? 600, 32, 10000),
67
- includeBbox: options.includeBbox !== false,
68
- attributeAllowlist: normalizeList(options.attributeAllowlist),
69
- };
70
- }
71
-
72
- /**
73
- * @param {string} value
74
- * @param {number} budget
75
- * @returns {{ value: string, truncated: boolean, omitted: number }}
76
- */
77
- function truncateText(value, budget) {
78
- if (!value) {
79
- return { value: '', truncated: false, omitted: 0 };
80
- }
81
-
82
- if (value.length <= budget) {
83
- return { value, truncated: false, omitted: 0 };
84
- }
85
-
86
- return {
87
- value: `${value.slice(0, Math.max(0, budget - 1))}\u2026`,
88
- truncated: true,
89
- omitted: value.length - budget,
90
- };
91
- }
92
-
93
- /**
94
- * @param {string} selector
95
- * @returns {string}
96
- */
97
- function escapeTailwindSelector(selector) {
98
- return selector.replace(
99
- /(\.[-\w]+)\[([^\]]+)\]/g,
100
- (_, prefix, value) => `${prefix}\\[${value}\\]`
101
- );
102
- }
103
-
104
- /**
105
- * @param {Element} el
106
- * @returns {string}
107
- */
108
- function getInputImplicitRole(el) {
109
- if (!(el instanceof HTMLInputElement)) return 'textbox';
110
- const type = el.type.toLowerCase();
111
- /** @type {Record<string, string>} */
112
- const map = {
113
- button: 'button',
114
- checkbox: 'checkbox',
115
- radio: 'radio',
116
- range: 'slider',
117
- search: 'searchbox',
118
- submit: 'button',
119
- reset: 'button',
120
- image: 'button',
121
- };
122
- return map[type] || 'textbox';
123
- }
124
-
125
- /**
126
- * @param {Element} el
127
- * @returns {string}
128
- */
129
- function getImplicitRole(el) {
130
- const tag = el.tagName.toLowerCase();
131
- /** @type {Record<string, string>} */
132
- const roleMap = {
133
- a: el.hasAttribute('href') ? 'link' : '',
134
- article: 'article',
135
- aside: 'complementary',
136
- button: 'button',
137
- dialog: 'dialog',
138
- footer: 'contentinfo',
139
- form: 'form',
140
- h1: 'heading',
141
- h2: 'heading',
142
- h3: 'heading',
143
- h4: 'heading',
144
- h5: 'heading',
145
- h6: 'heading',
146
- header: 'banner',
147
- img: 'img',
148
- input: getInputImplicitRole(el),
149
- li: 'listitem',
150
- main: 'main',
151
- nav: 'navigation',
152
- ol: 'list',
153
- option: 'option',
154
- progress: 'progressbar',
155
- section: 'region',
156
- select: 'listbox',
157
- table: 'table',
158
- td: 'cell',
159
- textarea: 'textbox',
160
- th: 'columnheader',
161
- tr: 'row',
162
- ul: 'list',
163
- };
164
- return roleMap[tag] || '';
165
- }
166
-
167
- /**
168
- * @param {string} role
169
- * @returns {string}
170
- */
171
- function getImplicitRoleSelector(role) {
172
- /** @type {Record<string, string>} */
173
- const map = {
174
- link: 'a[href]',
175
- article: 'article',
176
- complementary: 'aside',
177
- button:
178
- 'button, input[type=button], input[type=submit], input[type=reset], input[type=image]',
179
- dialog: 'dialog',
180
- contentinfo: 'footer',
181
- form: 'form',
182
- heading: 'h1, h2, h3, h4, h5, h6',
183
- banner: 'header',
184
- img: 'img',
185
- textbox:
186
- 'input:not([type=button]):not([type=checkbox]):not([type=radio]):not([type=range]):not([type=submit]):not([type=reset]):not([type=image]):not([type=hidden]), textarea',
187
- listitem: 'li',
188
- main: 'main',
189
- navigation: 'nav',
190
- list: 'ol, ul',
191
- option: 'option',
192
- progressbar: 'progress',
193
- region: 'section',
194
- listbox: 'select',
195
- table: 'table',
196
- cell: 'td',
197
- columnheader: 'th',
198
- row: 'tr',
199
- checkbox: 'input[type=checkbox]',
200
- radio: 'input[type=radio]',
201
- slider: 'input[type=range]',
202
- searchbox: 'input[type=search]',
203
- };
204
- return map[role] || '';
205
- }
206
-
207
- /**
208
- * @param {DOMRect | DOMRectReadOnly} rect
209
- * @returns {{ x: number, y: number, width: number, height: number }}
210
- */
211
- function toRect(rect) {
212
- return {
213
- x: rect.x + window.scrollX,
214
- y: rect.y + window.scrollY,
215
- width: rect.width,
216
- height: rect.height,
217
- };
218
- }
219
-
220
- /**
221
- * @param {string[]} values
222
- * @param {string | null | undefined} candidate
223
- * @returns {void}
224
- */
225
- function pushUnique(values, candidate) {
226
- if (!candidate) {
227
- return;
228
- }
229
-
230
- const normalized = candidate.replace(/\s+/g, ' ').trim();
231
- if (normalized && !values.includes(normalized)) {
232
- values.push(normalized);
233
- }
234
- }
235
-
236
- /**
237
- * @param {Element} element
238
- * @returns {string}
239
- */
240
- function extractElementText(element) {
241
- /** @type {string[]} */
242
- const parts = [];
243
-
244
- pushUnique(parts, element.getAttribute('aria-label'));
245
- pushUnique(parts, element.getAttribute('name'));
246
- pushUnique(parts, element.getAttribute('placeholder'));
247
- pushUnique(parts, element.getAttribute('title'));
248
-
249
- if ('value' in element && typeof element.value === 'string' && element.value.trim()) {
250
- pushUnique(parts, element.value);
251
- }
252
-
253
- const ownText = [...element.childNodes]
254
- .filter((node) => node.nodeType === Node.TEXT_NODE)
255
- .map((node) => node.textContent || '')
256
- .join(' ')
257
- .replace(/\s+/g, ' ')
258
- .trim();
259
-
260
- pushUnique(parts, ownText);
261
-
262
- if (!parts.length && element.childElementCount === 0) {
263
- pushUnique(parts, (element.textContent || '').replace(/\s+/g, ' ').trim());
264
- }
265
-
266
- return parts.join(' | ');
267
- }
268
-
269
- globalState.__BBX_CONTENT_HELPERS__ = Object.freeze({
270
- NON_TEXT_INPUT_TYPES,
271
- applyBudget,
272
- clamp,
273
- escapeTailwindSelector,
274
- extractElementText,
275
- getImplicitRole,
276
- getImplicitRoleSelector,
277
- getInputImplicitRole,
278
- normalizeList,
279
- toRect,
280
- truncateText,
281
- });
282
- })();