@browserbridge/bbx 1.9.0 → 1.10.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.
- package/package.json +1 -1
- package/packages/agent-client/src/cli-args.js +35 -6
- package/packages/agent-client/src/cli-batch.js +10 -5
- package/packages/agent-client/src/cli-helpers.js +8 -0
- package/packages/agent-client/src/cli-setup-commands.js +3 -1
- package/packages/agent-client/src/cli.js +35 -15
- package/packages/agent-client/src/command-registry.js +7 -3
- package/packages/agent-client/src/mcp-config.js +8 -8
- package/packages/agent-client/src/runtime.js +2 -2
- package/packages/mcp-server/src/guidance.js +37 -6
- package/packages/mcp-server/src/handlers-capture.js +1 -1
- package/packages/mcp-server/src/handlers-page.js +162 -9
- package/packages/mcp-server/src/handlers-utils.js +126 -167
- package/packages/mcp-server/src/handlers.js +7 -1
- package/packages/mcp-server/src/server.js +195 -46
- package/packages/mcp-server/src/toolset.js +48 -0
- package/packages/native-host/src/daemon.js +20 -106
- package/packages/native-host/src/dom-baseline-owners.js +210 -0
- package/packages/native-host/src/native-host.js +14 -28
- package/packages/protocol/src/budget-presets.js +182 -0
- package/packages/protocol/src/har.js +34 -0
- package/packages/protocol/src/index.js +2 -0
- package/packages/protocol/src/json-lines.js +3 -2
- package/packages/protocol/src/registry.js +83 -0
- package/packages/protocol/src/types.ts +1 -0
- package/skills/browser-bridge/SKILL.md +4 -0
- package/skills/browser-bridge/references/protocol.md +11 -5
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared budget-preset merge helpers. Presets fill gaps in method params;
|
|
5
|
+
* explicit per-call params always win. Used by both the MCP server and the
|
|
6
|
+
* agent CLI so `budgetPreset`/`--preset` behave identically.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_CONSOLE_LIMIT,
|
|
11
|
+
DEFAULT_HAR_LIMIT,
|
|
12
|
+
DEFAULT_LOG_TAIL_LIMIT,
|
|
13
|
+
DEFAULT_MAX_HTML_LENGTH,
|
|
14
|
+
DEFAULT_NETWORK_LIMIT,
|
|
15
|
+
DEFAULT_PAGE_TEXT_BUDGET,
|
|
16
|
+
getBudgetPreset,
|
|
17
|
+
isBudgetPresetName,
|
|
18
|
+
} from './defaults.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {unknown} value
|
|
22
|
+
* @returns {import('./defaults.js').BudgetPresetName | null}
|
|
23
|
+
*/
|
|
24
|
+
export function getBudgetPresetName(value) {
|
|
25
|
+
return isBudgetPresetName(value) ? value : null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @template {{ budgetPreset?: unknown, maxNodes?: unknown, maxDepth?: unknown, textBudget?: unknown }} T
|
|
30
|
+
* @param {T} args
|
|
31
|
+
* @returns {T}
|
|
32
|
+
*/
|
|
33
|
+
export function applyTreeBudgetPreset(args) {
|
|
34
|
+
const presetName = getBudgetPresetName(args.budgetPreset);
|
|
35
|
+
if (!presetName) {
|
|
36
|
+
return args;
|
|
37
|
+
}
|
|
38
|
+
const preset = getBudgetPreset(presetName);
|
|
39
|
+
return /** @type {T} */ ({
|
|
40
|
+
...args,
|
|
41
|
+
maxNodes: args.maxNodes ?? preset.maxNodes,
|
|
42
|
+
maxDepth: args.maxDepth ?? preset.maxDepth,
|
|
43
|
+
textBudget: args.textBudget ?? preset.textBudget,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @template {{ budgetPreset?: unknown, textBudget?: unknown }} T
|
|
49
|
+
* @param {T} args
|
|
50
|
+
* @returns {T}
|
|
51
|
+
*/
|
|
52
|
+
export function applyTextBudgetPreset(args) {
|
|
53
|
+
const presetName = getBudgetPresetName(args.budgetPreset);
|
|
54
|
+
if (!presetName) {
|
|
55
|
+
return args;
|
|
56
|
+
}
|
|
57
|
+
const preset = getBudgetPreset(presetName);
|
|
58
|
+
return /** @type {T} */ ({
|
|
59
|
+
...args,
|
|
60
|
+
textBudget: args.textBudget ?? preset.textBudget,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @template {{ budgetPreset?: unknown, textBudget?: unknown }} T
|
|
66
|
+
* @param {T} args
|
|
67
|
+
* @returns {T}
|
|
68
|
+
*/
|
|
69
|
+
export function applyPageTextBudgetPreset(args) {
|
|
70
|
+
const presetName = getBudgetPresetName(args.budgetPreset);
|
|
71
|
+
if (!presetName) {
|
|
72
|
+
return args;
|
|
73
|
+
}
|
|
74
|
+
const textBudgetByPreset = {
|
|
75
|
+
quick: 2000,
|
|
76
|
+
normal: DEFAULT_PAGE_TEXT_BUDGET,
|
|
77
|
+
deep: DEFAULT_PAGE_TEXT_BUDGET * 2,
|
|
78
|
+
};
|
|
79
|
+
return /** @type {T} */ ({
|
|
80
|
+
...args,
|
|
81
|
+
textBudget: args.textBudget ?? textBudgetByPreset[presetName],
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @template {{ budgetPreset?: unknown, limit?: unknown }} T
|
|
87
|
+
* @param {T} args
|
|
88
|
+
* @param {{ quick: number, normal: number, deep: number }} defaults
|
|
89
|
+
* @returns {T}
|
|
90
|
+
*/
|
|
91
|
+
export function applyLimitBudgetPreset(args, defaults) {
|
|
92
|
+
const presetName = getBudgetPresetName(args.budgetPreset);
|
|
93
|
+
if (!presetName) {
|
|
94
|
+
return args;
|
|
95
|
+
}
|
|
96
|
+
return /** @type {T} */ ({
|
|
97
|
+
...args,
|
|
98
|
+
limit: args.limit ?? defaults[presetName],
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @template {{ budgetPreset?: unknown, maxLength?: unknown }} T
|
|
104
|
+
* @param {T} args
|
|
105
|
+
* @returns {T}
|
|
106
|
+
*/
|
|
107
|
+
export function applyHtmlBudgetPreset(args) {
|
|
108
|
+
const presetName = getBudgetPresetName(args.budgetPreset);
|
|
109
|
+
if (!presetName) {
|
|
110
|
+
return args;
|
|
111
|
+
}
|
|
112
|
+
const maxLengthByPreset = {
|
|
113
|
+
quick: 600,
|
|
114
|
+
normal: DEFAULT_MAX_HTML_LENGTH,
|
|
115
|
+
deep: 6000,
|
|
116
|
+
};
|
|
117
|
+
return /** @type {T} */ ({
|
|
118
|
+
...args,
|
|
119
|
+
maxLength: args.maxLength ?? maxLengthByPreset[presetName],
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Apply a preset to method parameters before dispatch. Explicit method params
|
|
125
|
+
* always win over preset defaults.
|
|
126
|
+
*
|
|
127
|
+
* @param {import('./types.js').BridgeMethod} method
|
|
128
|
+
* @param {Record<string, unknown>} params
|
|
129
|
+
* @param {unknown} budgetPreset
|
|
130
|
+
* @returns {Record<string, unknown>}
|
|
131
|
+
*/
|
|
132
|
+
export function applyMethodBudgetPreset(method, params, budgetPreset) {
|
|
133
|
+
const args = { ...params, budgetPreset };
|
|
134
|
+
/** @type {Record<string, unknown>} */
|
|
135
|
+
let normalized = args;
|
|
136
|
+
if (
|
|
137
|
+
method === 'dom.query' ||
|
|
138
|
+
method === 'dom.get_accessibility_tree' ||
|
|
139
|
+
method === 'dom.baseline.create'
|
|
140
|
+
) {
|
|
141
|
+
normalized = applyTreeBudgetPreset(args);
|
|
142
|
+
} else if (method === 'dom.baseline.compare') {
|
|
143
|
+
const preset = getBudgetPresetName(budgetPreset);
|
|
144
|
+
const defaults = { quick: 10, normal: 50, deep: 100 };
|
|
145
|
+
normalized = {
|
|
146
|
+
...args,
|
|
147
|
+
maxChanges: params.maxChanges ?? (preset ? defaults[preset] : undefined),
|
|
148
|
+
};
|
|
149
|
+
} else if (method === 'dom.get_text') {
|
|
150
|
+
normalized = applyTextBudgetPreset(args);
|
|
151
|
+
} else if (method === 'dom.get_html') {
|
|
152
|
+
normalized = applyHtmlBudgetPreset(args);
|
|
153
|
+
} else if (method === 'page.get_text' || method === 'page.extract_content') {
|
|
154
|
+
normalized = applyPageTextBudgetPreset(args);
|
|
155
|
+
} else if (method === 'page.get_console') {
|
|
156
|
+
normalized = applyLimitBudgetPreset(args, {
|
|
157
|
+
quick: 10,
|
|
158
|
+
normal: DEFAULT_CONSOLE_LIMIT,
|
|
159
|
+
deep: 100,
|
|
160
|
+
});
|
|
161
|
+
} else if (method === 'page.get_network') {
|
|
162
|
+
normalized = applyLimitBudgetPreset(args, {
|
|
163
|
+
quick: 10,
|
|
164
|
+
normal: DEFAULT_NETWORK_LIMIT,
|
|
165
|
+
deep: 100,
|
|
166
|
+
});
|
|
167
|
+
} else if (method === 'network.export_har') {
|
|
168
|
+
normalized = applyLimitBudgetPreset(args, {
|
|
169
|
+
quick: 20,
|
|
170
|
+
normal: DEFAULT_HAR_LIMIT,
|
|
171
|
+
deep: 100,
|
|
172
|
+
});
|
|
173
|
+
} else if (method === 'log.tail') {
|
|
174
|
+
normalized = applyLimitBudgetPreset(args, {
|
|
175
|
+
quick: 10,
|
|
176
|
+
normal: DEFAULT_LOG_TAIL_LIMIT,
|
|
177
|
+
deep: 100,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
const { budgetPreset: _budgetPreset, ...methodParams } = normalized;
|
|
181
|
+
return methodParams;
|
|
182
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal structural validation for HAR 1.2 documents shared by the CLI and
|
|
5
|
+
* the MCP server. Only the envelope is validated (`log.version` and the
|
|
6
|
+
* `entries` array); deeper field checks are left to HAR consumers.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {unknown} value
|
|
11
|
+
* @returns {value is import('./types.js').HarLog}
|
|
12
|
+
*/
|
|
13
|
+
export function isHarLog(value) {
|
|
14
|
+
return Boolean(
|
|
15
|
+
value &&
|
|
16
|
+
typeof value === 'object' &&
|
|
17
|
+
!Array.isArray(value) &&
|
|
18
|
+
Reflect.get(value, 'version') === '1.2' &&
|
|
19
|
+
Array.isArray(Reflect.get(value, 'entries'))
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {unknown} value
|
|
25
|
+
* @returns {value is import('./types.js').HarDocument}
|
|
26
|
+
*/
|
|
27
|
+
export function isHarDocument(value) {
|
|
28
|
+
return Boolean(
|
|
29
|
+
value &&
|
|
30
|
+
typeof value === 'object' &&
|
|
31
|
+
!Array.isArray(value) &&
|
|
32
|
+
isHarLog(Reflect.get(value, 'log'))
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* Public protocol barrel for shared bridge helpers, errors, capabilities, and
|
|
5
5
|
* request/response normalization utilities.
|
|
6
6
|
*/
|
|
7
|
+
export * from './budget-presets.js';
|
|
7
8
|
export * from './budget.js';
|
|
8
9
|
export * from './capabilities.js';
|
|
9
10
|
export * from './defaults.js';
|
|
10
11
|
export * from './errors.js';
|
|
12
|
+
export * from './har.js';
|
|
11
13
|
export * from './incidental-sanitizer.js';
|
|
12
14
|
export * from './json-lines.js';
|
|
13
15
|
export * from './payload-cost.js';
|
|
@@ -7,7 +7,7 @@ import { MAX_JSON_LINE_BYTES } from './defaults.js';
|
|
|
7
7
|
*
|
|
8
8
|
* @param {import('node:net').Socket} socket
|
|
9
9
|
* @param {(message: unknown) => void} onMessage
|
|
10
|
-
* @param {{ maxLineBytes?: number, onProtocolError?: (error: Error) => void }} [options]
|
|
10
|
+
* @param {{ maxLineBytes?: number, onProtocolError?: (error: Error) => void, onInvalidLine?: (error: Error) => void }} [options]
|
|
11
11
|
* @returns {void}
|
|
12
12
|
*/
|
|
13
13
|
export function parseJsonLines(socket, onMessage, options = {}) {
|
|
@@ -50,8 +50,9 @@ export function parseJsonLines(socket, onMessage, options = {}) {
|
|
|
50
50
|
}
|
|
51
51
|
try {
|
|
52
52
|
onMessage(JSON.parse(line));
|
|
53
|
-
} catch {
|
|
53
|
+
} catch (error) {
|
|
54
54
|
// Malformed JSON line - skip it.
|
|
55
|
+
options.onInvalidLine?.(error instanceof Error ? error : new Error(String(error)));
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import { CAPABILITIES } from './capability-values.js';
|
|
4
|
+
import { BridgeError, ERROR_CODES } from './errors.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @typedef {'trivial' | 'low' | 'moderate' | 'high'} BridgeMethodComplexity
|
|
@@ -97,6 +98,8 @@ const METHOD_POLICIES = Object.freeze({
|
|
|
97
98
|
const BRIDGE_METHOD_DESCRIPTIONS = Object.freeze({
|
|
98
99
|
'access.request':
|
|
99
100
|
'Request Browser Bridge access for the focused window. Do not repeat while access is already pending.',
|
|
101
|
+
'protocol.describe':
|
|
102
|
+
'Describe bridge method parameters and metadata by method or registry group.',
|
|
100
103
|
'tabs.list': 'List tabs in the enabled window.',
|
|
101
104
|
'tabs.create': 'Create a new tab in the enabled window.',
|
|
102
105
|
'tabs.close': 'Close a tab in the enabled window.',
|
|
@@ -222,6 +225,13 @@ function createRegistryEntry(
|
|
|
222
225
|
export const BRIDGE_METHOD_REGISTRY = Object.freeze({
|
|
223
226
|
// system - trivial
|
|
224
227
|
'access.request': createRegistryEntry('access.request', 'system', false, ['intent'], 'trivial'),
|
|
228
|
+
'protocol.describe': createRegistryEntry(
|
|
229
|
+
'protocol.describe',
|
|
230
|
+
'system',
|
|
231
|
+
false,
|
|
232
|
+
['method', 'group'],
|
|
233
|
+
'trivial'
|
|
234
|
+
),
|
|
225
235
|
'skill.get_runtime_context': createRegistryEntry(
|
|
226
236
|
'skill.get_runtime_context',
|
|
227
237
|
'system',
|
|
@@ -888,6 +898,79 @@ export function createBridgeMethodGroups() {
|
|
|
888
898
|
return groups;
|
|
889
899
|
}
|
|
890
900
|
|
|
901
|
+
/**
|
|
902
|
+
* @typedef {{
|
|
903
|
+
* method: import('./types.js').BridgeMethod,
|
|
904
|
+
* group: string,
|
|
905
|
+
* tab: boolean,
|
|
906
|
+
* params: readonly string[],
|
|
907
|
+
* description: string,
|
|
908
|
+
* complexity: BridgeMethodComplexity
|
|
909
|
+
* }} BridgeMethodDescription
|
|
910
|
+
*/
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* @param {import('./types.js').BridgeMethod} method
|
|
914
|
+
* @returns {BridgeMethodDescription}
|
|
915
|
+
*/
|
|
916
|
+
function createBridgeMethodDescription(method) {
|
|
917
|
+
const entry = BRIDGE_METHOD_REGISTRY[method];
|
|
918
|
+
return {
|
|
919
|
+
method,
|
|
920
|
+
group: entry.group,
|
|
921
|
+
tab: entry.tab,
|
|
922
|
+
params: entry.params,
|
|
923
|
+
description: entry.description,
|
|
924
|
+
complexity: entry.complexity,
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Return a compact registry index or detailed descriptions for one method or group.
|
|
930
|
+
*
|
|
931
|
+
* @param {{ method?: unknown, group?: unknown }} [options]
|
|
932
|
+
* @returns {{ groups: Array<{ group: string, count: number }> } | BridgeMethodDescription | { group: string, methods: BridgeMethodDescription[] }}
|
|
933
|
+
*/
|
|
934
|
+
export function describeBridgeMethods(options = {}) {
|
|
935
|
+
const { method, group } = options;
|
|
936
|
+
if (method !== undefined && group !== undefined) {
|
|
937
|
+
throw new BridgeError(
|
|
938
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
939
|
+
'protocol.describe accepts either method or group, not both.'
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
if (method !== undefined) {
|
|
943
|
+
if (typeof method !== 'string' || !isBridgeMethod(method)) {
|
|
944
|
+
throw new BridgeError(
|
|
945
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
946
|
+
`Unknown bridge method "${String(method)}".`
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
return createBridgeMethodDescription(method);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
const groups = createBridgeMethodGroups();
|
|
953
|
+
if (group !== undefined) {
|
|
954
|
+
if (typeof group !== 'string' || !Object.hasOwn(groups, group)) {
|
|
955
|
+
throw new BridgeError(
|
|
956
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
957
|
+
`Unknown bridge method group "${String(group)}".`
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
return {
|
|
961
|
+
group,
|
|
962
|
+
methods: groups[group].map(createBridgeMethodDescription),
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
return {
|
|
967
|
+
groups: Object.entries(groups).map(([groupName, methods]) => ({
|
|
968
|
+
group: groupName,
|
|
969
|
+
count: methods.length,
|
|
970
|
+
})),
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
|
|
891
974
|
/**
|
|
892
975
|
* @param {string} method
|
|
893
976
|
* @returns {method is import('./types.js').BridgeMethod}
|
|
@@ -14,6 +14,8 @@ Permission prompts are controlled by the host agent, not by Browser Bridge. In p
|
|
|
14
14
|
Skill name: `browser-bridge` (also known as `bbx`). In GitHub Copilot, invoke as `/browser-bridge`. `bbx` is the CLI command used throughout this skill.
|
|
15
15
|
When the runtime supports subagents, delegate bridge inspection to a smaller, lower-cost worker and return only concise findings to the parent.
|
|
16
16
|
For open-ended investigation, start with structured reads (`page.get_state`, `dom.query`, `page.get_text`, `page.extract_content`, `styles.get_computed`, and `bbx batch` for CLI or `browser_batch` for MCP) and escalate to screenshots or debugger-backed methods only when structured evidence is insufficient. `browser_call` accepts one protocol method at a time; `batch` is not a valid `browser_call` method.
|
|
17
|
+
When an unfamiliar method is needed, run `bbx protocol describe <method|group>` before guessing parameters. In MCP mode, call `browser_call` with method `protocol.describe` and params `{ "method": "dom.query" }` or `{ "group": "inspect" }`.
|
|
18
|
+
MCP exposes common tools first. Load one specialized typed tool only when needed by calling `browser_toolset` with its exact name, for example `{ "tool": "browser_dom" }`; `browser_call` remains the universal fallback.
|
|
17
19
|
|
|
18
20
|
## CLI
|
|
19
21
|
|
|
@@ -32,6 +34,7 @@ bbx tab-create [url] # open a new tab (avoid unless necessary)
|
|
|
32
34
|
bbx tab-close <tabId> # close a tab
|
|
33
35
|
bbx tab-activate <tabId> # bring a tab to the foreground
|
|
34
36
|
bbx skill # live runtime presets + limits
|
|
37
|
+
bbx protocol describe [method|group] # local method params or compact group index
|
|
35
38
|
```
|
|
36
39
|
|
|
37
40
|
Use the globally installed `bbx` command or `node packages/agent-client/src/cli.js ...` for live readiness checks from this repository. Avoid `rtk npm exec -- bbx status` as a health signal: that path can restart/replace the daemon and report `Extension: disconnected` before Chrome reconnects. If it happens, wait a few seconds and verify with `bbx status`.
|
|
@@ -351,6 +354,7 @@ Every CLI shortcut command produces consistent `{ok, summary, evidence}` JSON. U
|
|
|
351
354
|
- `network.export_har` requires that explicit capture to still be armed. Params are `limit` (1-200), sanitized-URL substring `urlPattern`, and `delivery` (`auto`, `inline`, `artifact`). Inline bounds remove whole oldest entries; export never changes capture state. `bbx har` uses the same client to download, length/SHA-256 verify, delete, validate, and atomically write artifacts locally.
|
|
352
355
|
- `page.handle_dialog` mutations are not atomically bound to `expectedDialogId`; treat `commandDispatched` as dispatch evidence and verify follow-up state.
|
|
353
356
|
- Raw screenshot calls default to inline base64. Use `delivery: "auto"` for size-aware artifact fallback, or explicit `inline` when MCP image content is required; prefer `bbx screenshot <ref> [outPath]` when one element is enough because the CLI downloads and verifies artifacts locally.
|
|
357
|
+
- Artifact delivery returns a descriptor (artifactId, sha256, byteLength), not bytes. From MCP, read chunks with the `browser_artifact` tool (`action: "read"`, paging `offset` until `nextOffset` is null, then verify the reassembled bytes against `sha256`) and free storage with `action: "delete"`. Each standalone CLI invocation has a new artifact owner, so use `bbx screenshot` / `bbx har` to capture, download, verify, and delete within one CLI process; use `delivery: "inline"` for raw CLI screenshot calls that need the bytes in their immediate result.
|
|
354
358
|
|
|
355
359
|
## Response Shapes
|
|
356
360
|
|
|
@@ -27,11 +27,16 @@ The table below includes the legacy capability bucket for each method so agents
|
|
|
27
27
|
- `-` means the method is global/system-scoped and was never gated by a former capability bucket.
|
|
28
28
|
- Capability names are descriptive coverage labels only. Browser Bridge access is window-scoped now; there are no capability-scoped sessions.
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Use `bbx protocol describe <method|group>` for a compact local signature lookup. In MCP mode,
|
|
31
|
+
call `browser_call` with method `protocol.describe` and params containing `method` or `group`;
|
|
32
|
+
omit both for the group index.
|
|
33
|
+
|
|
34
|
+
## All Methods (76)
|
|
31
35
|
|
|
32
36
|
| Method | Tab? | CDP? | Group | Capability | Notes |
|
|
33
37
|
| ---------------------------------- | ---- | ---------- | ----------- | -------------------- | ------------------------------------------------------------------------------------------ |
|
|
34
38
|
| `access.request` | No | - | system | `intent?` | Request window access with bounded operation context in the extension UI |
|
|
39
|
+
| `protocol.describe` | No | - | system | `-` | Local compact method/group parameters and metadata |
|
|
35
40
|
| `tabs.list` | No | - | tabs | `-` | Discover tabs in the enabled window |
|
|
36
41
|
| `tabs.create` | No | - | tabs | `tabs.manage` | Open a new tab; optional `url` and `active` |
|
|
37
42
|
| `tabs.close` | No | - | tabs | `tabs.manage` | Close a tab by `tabId` |
|
|
@@ -115,6 +120,7 @@ stopped.
|
|
|
115
120
|
|
|
116
121
|
```bash
|
|
117
122
|
bbx status | doctor | restart | logs | tabs | skill # no routed tab needed
|
|
123
|
+
bbx protocol describe [method|group] # local signature or compact group index
|
|
118
124
|
bbx call <method> '{"key":"val"}' # generic RPC (routes to active tab in enabled window)
|
|
119
125
|
bbx call --tab 123 <method> '{...}' # explicit tab target inside enabled window
|
|
120
126
|
bbx batch '[{"method":"...","params":{}}]' # parallel calls
|
|
@@ -437,7 +443,7 @@ artifact, and atomically write the output on the CLI host.
|
|
|
437
443
|
|
|
438
444
|
### dom.get_accessibility_tree
|
|
439
445
|
|
|
440
|
-
Retrieve a depth-limited accessibility tree via CDP. Full reads use `Accessibility.getFullAXTree`; an optional unique `selector` resolves through CDP DOM methods
|
|
446
|
+
Retrieve a depth-limited accessibility tree via CDP. Full reads use `Accessibility.getFullAXTree`; an optional unique `selector` resolves through CDP DOM methods, starts with `Accessibility.getPartialAXTree`, and incrementally fetches selected descendant layers with `Accessibility.getChildAXNodes`. Only the selected subtree and required ancestor chain are retained. Missing and ambiguous selectors return typed errors instead of selecting an arbitrary candidate. Nodes include `role`, `name`, `description`, `value`, state fields, `interactive`, `semanticInteractive`, `focusable`, `focusableAndEnabled`, `ignored`, and `childIds`. `interactive` is semantic/focusability metadata, not current pointer actionability.
|
|
441
447
|
|
|
442
448
|
This is debugger-backed. Prefer `dom.find_by_role`, `dom.find_by_text`, and targeted `dom.query`/`dom.describe` first.
|
|
443
449
|
|
|
@@ -449,7 +455,7 @@ bbx call dom.get_accessibility_tree '{"maxNodes":100,"maxDepth":6,"interactiveOn
|
|
|
449
455
|
bbx call dom.get_accessibility_tree '{"selector":"[role=dialog]","maxNodes":50,"compact":true}'
|
|
450
456
|
```
|
|
451
457
|
|
|
452
|
-
Filtering occurs before `maxNodes`. Compact mode drops ignored, decorative, and empty nodes while reconnecting retained descendants; interactive-only keeps non-ignored semantic interactive roles.
|
|
458
|
+
Filtering occurs before `maxNodes`. Compact mode drops ignored, decorative, and empty nodes while reconnecting retained descendants; interactive-only keeps non-ignored semantic interactive roles. Full reads ask CDP for the requested depth, while selector-scoped reads fetch descendant layers up to that same bound. Every result reports `truncated: true`, `partialTopology: true`, depth metadata, missing-child counts, and a continuation hint. No AX node is written into the page DOM or converted directly into an actionable element ref; use role/name with `dom.find_by_role` before input.
|
|
453
459
|
|
|
454
460
|
### viewport.resize
|
|
455
461
|
|
|
@@ -482,7 +488,7 @@ Capture a complete element, a tight viewport region, or a full document. All met
|
|
|
482
488
|
|
|
483
489
|
Element capture uses full page-coordinate bounds with CDP capture beyond the viewport. If Browser Bridge cannot guarantee the entire element, or if an element/page exceeds Chrome capture limits, it returns a typed failure rather than a viewport-clipped image labeled complete. Use full-page capture only when element or tight region captures cannot express the issue.
|
|
484
490
|
|
|
485
|
-
Raw calls default to inline base64 for compatibility. `auto` keeps small captures inline and returns a short-lived, daemon-owned opaque artifact handle for large captures; explicit `inline` preserves MCP image content when model vision is needed. Artifact handles expose no browser-host path and support owner-scoped bounded `artifact.read` and `artifact.delete` calls. Prefer `bbx screenshot [--format png|jpeg|webp] [--quality 0-100] <ref> [outPath]` when one element is enough; the CLI downloads, verifies, deletes, and atomically writes the artifact
|
|
491
|
+
Raw calls default to inline base64 for compatibility. `auto` keeps small captures inline and returns a short-lived, daemon-owned opaque artifact handle for large captures; explicit `inline` preserves MCP image content when model vision is needed. Artifact handles expose no browser-host path and support owner-scoped bounded `artifact.read` and `artifact.delete` calls. Each standalone CLI invocation uses a new artifact owner, so raw CLI screenshot calls should keep inline delivery. Prefer `bbx screenshot [--format png|jpeg|webp] [--quality 0-100] <ref> [outPath]` when one element is enough; the CLI downloads, verifies, deletes, and atomically writes the artifact within one process.
|
|
486
492
|
|
|
487
493
|
Artifact handles expire after five minutes and can be read or deleted only by
|
|
488
494
|
the requesting client. The daemon creates a private artifact directory and
|
|
@@ -490,7 +496,7 @@ owner-only files on POSIX hosts, removes artifacts on expiry/teardown, and never
|
|
|
490
496
|
returns its browser-host filesystem path.
|
|
491
497
|
|
|
492
498
|
```bash
|
|
493
|
-
bbx call screenshot.capture_full_page '{"format":"jpeg","quality":75,"delivery":"
|
|
499
|
+
bbx call screenshot.capture_full_page '{"format":"jpeg","quality":75,"delivery":"inline","scale":0.75}'
|
|
494
500
|
```
|
|
495
501
|
|
|
496
502
|
## Recovery Telemetry
|