@jobshimo/browser-link 0.15.0 → 0.16.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.
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Keyboard key definitions for CDP `Input.dispatchKeyEvent`, driving
3
+ * `browser.press` (trusted keyboard input — see `background.ts`'s
4
+ * `case 'press'`).
5
+ *
6
+ * Deliberately NOT an `inpage/*.ts` module: this runs in the extension's
7
+ * own service-worker context and only produces CDP command parameters —
8
+ * no page JS execution involved, nothing to inject via `Runtime.evaluate`.
9
+ *
10
+ * Modeled after Puppeteer's `USKeyboardLayout` (US keyboard — the only
11
+ * layout CDP's `Input.dispatchKeyEvent` needs; Chrome maps
12
+ * `windowsVirtualKeyCode` + `code` to the OS's actual layout on its own),
13
+ * trimmed to what `browser.press` exposes: the named control/navigation
14
+ * keys plus any single printable character.
15
+ */
16
+ export interface KeyDefinition {
17
+ /** DOM `KeyboardEvent.key` value. */
18
+ key: string;
19
+ /** DOM `KeyboardEvent.code` value — physical key identity. */
20
+ code: string;
21
+ /** CDP `windowsVirtualKeyCode` (mirrored into `nativeVirtualKeyCode`).
22
+ * Chrome derives the platform-native key from this plus `code` — the
23
+ * same numeric value works on every OS. */
24
+ keyCode: number;
25
+ /** Text CDP should insert via a `char` event. Only set for keys that
26
+ * produce visible input (printable characters, Space). Control keys
27
+ * (Enter, Escape, Tab, arrows, …) omit it — they dispatch keyDown/keyUp
28
+ * only, no `char` event, matching real hardware. */
29
+ text?: string;
30
+ /** True when producing this exact character requires the Shift modifier
31
+ * on a US layout (e.g. "@" is Shift+2, "_" is Shift+-). The caller ORs
32
+ * this into the dispatched modifiers bitmask so `event.shiftKey`
33
+ * reports correctly even when the caller only asked for the character,
34
+ * not the physical chord. */
35
+ needsShift?: boolean;
36
+ }
37
+ /**
38
+ * Resolve a human-provided `key` name (the `browser.press` `key` param)
39
+ * into CDP dispatch parameters. Accepts:
40
+ * - a named control/navigation key, case-insensitive ("Enter", "arrowup"),
41
+ * - a single printable character, case-SENSITIVE ("a" vs "A", "@").
42
+ *
43
+ * Returns `null` when the name is neither — the caller surfaces that as a
44
+ * validation error instead of silently no-op'ing on an unrecognized key.
45
+ */
46
+ export declare function resolveKey(name: string): KeyDefinition | null;
47
+ /** CDP `Input.dispatchKeyEvent` modifiers bitmask. */
48
+ export declare const MODIFIER_BITS: Readonly<Record<string, number>>;
49
+ /** Fold a `modifiers` array (as accepted by `browser.press`) into the CDP
50
+ * bitmask. Unknown entries are ignored defensively — the server-side
51
+ * dispatcher already validates the enum, this is a second line of
52
+ * defence for direct/test callers. */
53
+ export declare function modifiersToBitmask(modifiers: readonly string[] | undefined): number;
54
+ /** One CDP `Input.dispatchKeyEvent` command payload. A type alias (not an
55
+ * interface) so it carries an implicit index signature and is directly
56
+ * assignable to the `Record<string, unknown>` the cdp() helper takes. */
57
+ export type CdpKeyEvent = {
58
+ type: 'keyDown' | 'rawKeyDown' | 'char' | 'keyUp';
59
+ modifiers: number;
60
+ key: string;
61
+ code: string;
62
+ windowsVirtualKeyCode: number;
63
+ nativeVirtualKeyCode: number;
64
+ text?: string;
65
+ unmodifiedText?: string;
66
+ };
67
+ /**
68
+ * Build the CDP `Input.dispatchKeyEvent` sequence for one key press.
69
+ * Pure function (no chrome.* access) so the exact event shapes are
70
+ * unit-testable; background.ts dispatches each entry in order.
71
+ *
72
+ * Shape: a text-bearing key (printable characters, Space, Enter) gets a
73
+ * `keyDown` (identity only, no `text` field — Chrome would insert text
74
+ * from a text-bearing keyDown AND from a following char event, doubling
75
+ * the input) followed by a separate `char` event carrying the text — the
76
+ * `char` event is what produces the KEYPRESS whose default action drives
77
+ * text insertion and, for Enter ('\r'), the WHATWG implicit form
78
+ * submission. A control key with no text (Escape, Tab, arrows, …) gets
79
+ * `rawKeyDown` with nothing to follow but `keyUp`, matching real hardware
80
+ * (those keys never fire keypress in Blink).
81
+ */
82
+ export declare function buildKeyEventSequence(def: KeyDefinition, modifiers: number): CdpKeyEvent[];
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Keyboard key definitions for CDP `Input.dispatchKeyEvent`, driving
3
+ * `browser.press` (trusted keyboard input — see `background.ts`'s
4
+ * `case 'press'`).
5
+ *
6
+ * Deliberately NOT an `inpage/*.ts` module: this runs in the extension's
7
+ * own service-worker context and only produces CDP command parameters —
8
+ * no page JS execution involved, nothing to inject via `Runtime.evaluate`.
9
+ *
10
+ * Modeled after Puppeteer's `USKeyboardLayout` (US keyboard — the only
11
+ * layout CDP's `Input.dispatchKeyEvent` needs; Chrome maps
12
+ * `windowsVirtualKeyCode` + `code` to the OS's actual layout on its own),
13
+ * trimmed to what `browser.press` exposes: the named control/navigation
14
+ * keys plus any single printable character.
15
+ */
16
+ /** The named control/navigation keys `browser.press` accepts, keyed by
17
+ * their canonical human name. Values are the standard Windows virtual-key
18
+ * codes (VK_RETURN, VK_ESCAPE, VK_TAB, …) — well-known constants, stable
19
+ * across every Chromium build. */
20
+ const NAMED_KEYS = {
21
+ // Enter carries text '\r' (matching Puppeteer's USKeyboardLayout): the
22
+ // WHATWG implicit form submission algorithm runs as the default action
23
+ // of the KEYPRESS event, and the CDP sequence only produces a keypress
24
+ // through the `char` event — which `buildKeyEventSequence` emits only
25
+ // for text-bearing keys. Without this, pressing Enter in a form field
26
+ // would fire keydown/keyup but never submit the form.
27
+ Enter: { key: 'Enter', code: 'Enter', keyCode: 13, text: '\r' },
28
+ Escape: { key: 'Escape', code: 'Escape', keyCode: 27 },
29
+ Tab: { key: 'Tab', code: 'Tab', keyCode: 9 },
30
+ Backspace: { key: 'Backspace', code: 'Backspace', keyCode: 8 },
31
+ Delete: { key: 'Delete', code: 'Delete', keyCode: 46 },
32
+ ArrowUp: { key: 'ArrowUp', code: 'ArrowUp', keyCode: 38 },
33
+ ArrowDown: { key: 'ArrowDown', code: 'ArrowDown', keyCode: 40 },
34
+ ArrowLeft: { key: 'ArrowLeft', code: 'ArrowLeft', keyCode: 37 },
35
+ ArrowRight: { key: 'ArrowRight', code: 'ArrowRight', keyCode: 39 },
36
+ Home: { key: 'Home', code: 'Home', keyCode: 36 },
37
+ End: { key: 'End', code: 'End', keyCode: 35 },
38
+ PageUp: { key: 'PageUp', code: 'PageUp', keyCode: 33 },
39
+ PageDown: { key: 'PageDown', code: 'PageDown', keyCode: 34 },
40
+ Space: { key: ' ', code: 'Space', keyCode: 32, text: ' ' },
41
+ };
42
+ /** Lowercased lookup so NAME matching is case-insensitive ("arrowup",
43
+ * "ENTER", "Escape" all resolve to the same definition). Single printable
44
+ * characters are resolved separately and stay case-SENSITIVE — "a" and
45
+ * "A" are different keystrokes. A `Map` (not a plain `Record`) so a missing
46
+ * key types as `undefined` — a `Record<string, KeyDefinition>` index would
47
+ * type as always-`KeyDefinition` (never falsy, since objects are always
48
+ * truthy) without `noUncheckedIndexedAccess`, which would make the
49
+ * presence check in `resolveKey` dead code from TypeScript's point of view. */
50
+ const NAMED_KEYS_LOWER = new Map(Object.entries(NAMED_KEYS).map(([name, def]) => [name.toLowerCase(), def]));
51
+ /** Physical key (code + Windows virtual-key code) for each punctuation key
52
+ * on a US keyboard, keyed by the character it produces UNSHIFTED. Shared
53
+ * with the shifted variant of the same physical key below. Values are the
54
+ * standard VK_OEM_* constants. */
55
+ const PUNCTUATION_PHYSICAL = [
56
+ ['-', 'Minus', 189],
57
+ ['=', 'Equal', 187],
58
+ ['[', 'BracketLeft', 219],
59
+ [']', 'BracketRight', 221],
60
+ ['\\', 'Backslash', 220],
61
+ [';', 'Semicolon', 186],
62
+ ["'", 'Quote', 222],
63
+ ['`', 'Backquote', 192],
64
+ [',', 'Comma', 188],
65
+ ['.', 'Period', 190],
66
+ ['/', 'Slash', 191],
67
+ ];
68
+ /** Shifted glyph produced by the same physical punctuation key, US layout. */
69
+ const SHIFTED_PUNCTUATION = {
70
+ '-': '_',
71
+ '=': '+',
72
+ '[': '{',
73
+ ']': '}',
74
+ '\\': '|',
75
+ ';': ':',
76
+ "'": '"',
77
+ '`': '~',
78
+ ',': '<',
79
+ '.': '>',
80
+ '/': '?',
81
+ };
82
+ /** Shifted digit-row symbols, US layout — the physical key is the digit
83
+ * itself (Shift+2 -> "@", Shift+1 -> "!", …). */
84
+ const SHIFTED_DIGITS = {
85
+ '1': '!',
86
+ '2': '@',
87
+ '3': '#',
88
+ '4': '$',
89
+ '5': '%',
90
+ '6': '^',
91
+ '7': '&',
92
+ '8': '*',
93
+ '9': '(',
94
+ '0': ')',
95
+ };
96
+ function buildPrintableTable() {
97
+ const table = new Map();
98
+ // A literal ' ' resolves to the same definition as the named 'Space' key —
99
+ // without this alias it would fall through to the generic printable
100
+ // fallback and lose the physical Space code/keyCode.
101
+ table.set(' ', NAMED_KEYS.Space);
102
+ for (let i = 0; i < 26; i++) {
103
+ const lower = String.fromCharCode(97 + i);
104
+ const upper = String.fromCharCode(65 + i);
105
+ const code = `Key${upper}`;
106
+ const keyCode = upper.charCodeAt(0);
107
+ table.set(lower, { key: lower, code, keyCode, text: lower });
108
+ table.set(upper, { key: upper, code, keyCode, text: upper, needsShift: true });
109
+ }
110
+ for (let d = 0; d <= 9; d++) {
111
+ const ch = String(d);
112
+ const code = `Digit${ch}`;
113
+ const keyCode = 48 + d;
114
+ table.set(ch, { key: ch, code, keyCode, text: ch });
115
+ const shifted = SHIFTED_DIGITS[ch];
116
+ table.set(shifted, { key: shifted, code, keyCode, text: shifted, needsShift: true });
117
+ }
118
+ for (const [unshifted, code, keyCode] of PUNCTUATION_PHYSICAL) {
119
+ table.set(unshifted, { key: unshifted, code, keyCode, text: unshifted });
120
+ const shifted = SHIFTED_PUNCTUATION[unshifted];
121
+ table.set(shifted, { key: shifted, code, keyCode, text: shifted, needsShift: true });
122
+ }
123
+ return table;
124
+ }
125
+ const PRINTABLE_TABLE = buildPrintableTable();
126
+ /**
127
+ * Resolve a human-provided `key` name (the `browser.press` `key` param)
128
+ * into CDP dispatch parameters. Accepts:
129
+ * - a named control/navigation key, case-insensitive ("Enter", "arrowup"),
130
+ * - a single printable character, case-SENSITIVE ("a" vs "A", "@").
131
+ *
132
+ * Returns `null` when the name is neither — the caller surfaces that as a
133
+ * validation error instead of silently no-op'ing on an unrecognized key.
134
+ */
135
+ export function resolveKey(name) {
136
+ const named = NAMED_KEYS_LOWER.get(name.toLowerCase());
137
+ if (named)
138
+ return named;
139
+ if (name.length === 1) {
140
+ const printable = PRINTABLE_TABLE.get(name);
141
+ if (printable)
142
+ return printable;
143
+ // Any other single printable character (unicode letters, symbols not
144
+ // in the curated punctuation table): best-effort. `key`/`text` are
145
+ // always correct — they drive what actually gets inserted via the
146
+ // `char` event. `code`/`keyCode` degrade to a generic placeholder
147
+ // since there is no physical-key mapping for it on a US layout.
148
+ return { key: name, code: 'Unidentified', keyCode: name.charCodeAt(0), text: name };
149
+ }
150
+ return null;
151
+ }
152
+ /** CDP `Input.dispatchKeyEvent` modifiers bitmask. */
153
+ export const MODIFIER_BITS = {
154
+ Alt: 1,
155
+ Control: 2,
156
+ Meta: 4,
157
+ Shift: 8,
158
+ };
159
+ /** Fold a `modifiers` array (as accepted by `browser.press`) into the CDP
160
+ * bitmask. Unknown entries are ignored defensively — the server-side
161
+ * dispatcher already validates the enum, this is a second line of
162
+ * defence for direct/test callers. */
163
+ export function modifiersToBitmask(modifiers) {
164
+ if (!modifiers || modifiers.length === 0)
165
+ return 0;
166
+ let mask = 0;
167
+ for (const m of modifiers) {
168
+ const bit = MODIFIER_BITS[m];
169
+ if (bit)
170
+ mask |= bit;
171
+ }
172
+ return mask;
173
+ }
174
+ /**
175
+ * Build the CDP `Input.dispatchKeyEvent` sequence for one key press.
176
+ * Pure function (no chrome.* access) so the exact event shapes are
177
+ * unit-testable; background.ts dispatches each entry in order.
178
+ *
179
+ * Shape: a text-bearing key (printable characters, Space, Enter) gets a
180
+ * `keyDown` (identity only, no `text` field — Chrome would insert text
181
+ * from a text-bearing keyDown AND from a following char event, doubling
182
+ * the input) followed by a separate `char` event carrying the text — the
183
+ * `char` event is what produces the KEYPRESS whose default action drives
184
+ * text insertion and, for Enter ('\r'), the WHATWG implicit form
185
+ * submission. A control key with no text (Escape, Tab, arrows, …) gets
186
+ * `rawKeyDown` with nothing to follow but `keyUp`, matching real hardware
187
+ * (those keys never fire keypress in Blink).
188
+ */
189
+ export function buildKeyEventSequence(def, modifiers) {
190
+ const base = {
191
+ modifiers,
192
+ key: def.key,
193
+ code: def.code,
194
+ windowsVirtualKeyCode: def.keyCode,
195
+ nativeVirtualKeyCode: def.keyCode,
196
+ };
197
+ const hasText = typeof def.text === 'string' && def.text.length > 0;
198
+ const events = [{ ...base, type: hasText ? 'keyDown' : 'rawKeyDown' }];
199
+ if (hasText) {
200
+ events.push({ ...base, type: 'char', text: def.text, unmodifiedText: def.text });
201
+ }
202
+ events.push({ ...base, type: 'keyUp' });
203
+ return events;
204
+ }
205
+ //# sourceMappingURL=keymap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keymap.js","sourceRoot":"","sources":["../src/keymap.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "browser-link",
4
- "version": "0.15.0",
4
+ "version": "0.16.0",
5
5
  "description": "Bridge between Chrome and an MCP client (Claude Code, OpenCode, GitHub Copilot CLI, …). Per-tab manual activation.",
6
6
  "permissions": [
7
7
  "debugger",
@@ -0,0 +1,39 @@
1
+ /** Defaults and hard ceilings — same clamp-at-the-boundary philosophy as
2
+ * the drag durations in background.ts. Mirrored by the schema bounds in
3
+ * the server's browser-definitions.ts. */
4
+ export declare const DEFAULT_SETTLE_MS = 150;
5
+ export declare const MAX_SETTLE_MS = 2000;
6
+ export declare const DEFAULT_SETTLE_TIMEOUT_MS = 2000;
7
+ export declare const MAX_SETTLE_TIMEOUT_MS = 10000;
8
+ export interface SettleParams {
9
+ settleMs: number;
10
+ settleTimeoutMs: number;
11
+ }
12
+ /**
13
+ * Resolve the settle params for an action call. Returns `null` when settle
14
+ * is disabled (`settle_ms: 0`, explicit) — the caller skips the extra
15
+ * `Runtime.evaluate` round trip entirely in that case rather than running a
16
+ * zero-length wait. `settle_ms` omitted defaults to `DEFAULT_SETTLE_MS`
17
+ * (settle stays ON by default); any other numeric value is clamped into
18
+ * (0, MAX_SETTLE_MS].
19
+ */
20
+ export declare function resolveSettleParams(p: Record<string, unknown>): SettleParams | null;
21
+ /** Evaluate-in-page function shape settleSafely needs — background.ts
22
+ * passes `(expr) => evaluateInTab(tabId, expr)`. */
23
+ export type EvaluateFn = (expression: string) => Promise<unknown>;
24
+ /**
25
+ * Run the settle wait, GUARANTEED not to throw. Returns the in-page settle
26
+ * result, a degraded-but-truthful fallback when the evaluation failed, or
27
+ * `undefined` when settle is disabled for this call.
28
+ *
29
+ * The guarantee matters because the action itself already SUCCEEDED by the
30
+ * time settle runs: if the action triggered a full-document navigation, the
31
+ * execution context the settle expression runs in is destroyed mid-wait and
32
+ * `Runtime.evaluate` rejects. Letting that rejection escape would flip the
33
+ * whole tool response to ok:false for an action that landed — an agent
34
+ * would retry it and risk a duplicate submission. Instead the caller gets
35
+ * `{ settled: false, reason: 'context-destroyed' | 'settle-error' }` spliced
36
+ * onto an ok:true result: 'context-destroyed' is itself a strong signal the
37
+ * action navigated the page.
38
+ */
39
+ export declare function settleSafely(evaluate: EvaluateFn, settle: SettleParams | null): Promise<Record<string, unknown> | undefined>;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Shared `settle_ms` / `settle_timeout_ms` handling for the click / type /
3
+ * press actions in background.ts. Lives outside `inpage/` (this runs in the
4
+ * extension's service-worker context) and takes the evaluate function as a
5
+ * parameter so the failure handling is unit-testable without chrome.* —
6
+ * background.ts passes a closure over `evaluateInTab`.
7
+ */
8
+ import { buildSettleJs } from './inpage/builders.js';
9
+ /** Defaults and hard ceilings — same clamp-at-the-boundary philosophy as
10
+ * the drag durations in background.ts. Mirrored by the schema bounds in
11
+ * the server's browser-definitions.ts. */
12
+ export const DEFAULT_SETTLE_MS = 150;
13
+ export const MAX_SETTLE_MS = 2000;
14
+ export const DEFAULT_SETTLE_TIMEOUT_MS = 2000;
15
+ export const MAX_SETTLE_TIMEOUT_MS = 10_000;
16
+ /**
17
+ * Resolve the settle params for an action call. Returns `null` when settle
18
+ * is disabled (`settle_ms: 0`, explicit) — the caller skips the extra
19
+ * `Runtime.evaluate` round trip entirely in that case rather than running a
20
+ * zero-length wait. `settle_ms` omitted defaults to `DEFAULT_SETTLE_MS`
21
+ * (settle stays ON by default); any other numeric value is clamped into
22
+ * (0, MAX_SETTLE_MS].
23
+ */
24
+ export function resolveSettleParams(p) {
25
+ const rawSettleMs = p.settle_ms;
26
+ const settleMs = typeof rawSettleMs === 'number' && Number.isFinite(rawSettleMs) && rawSettleMs >= 0
27
+ ? Math.min(rawSettleMs, MAX_SETTLE_MS)
28
+ : DEFAULT_SETTLE_MS;
29
+ if (settleMs <= 0)
30
+ return null;
31
+ const rawTimeout = p.settle_timeout_ms;
32
+ const settleTimeoutMs = typeof rawTimeout === 'number' && Number.isFinite(rawTimeout) && rawTimeout >= 0
33
+ ? Math.min(rawTimeout, MAX_SETTLE_TIMEOUT_MS)
34
+ : DEFAULT_SETTLE_TIMEOUT_MS;
35
+ return { settleMs, settleTimeoutMs };
36
+ }
37
+ /**
38
+ * Run the settle wait, GUARANTEED not to throw. Returns the in-page settle
39
+ * result, a degraded-but-truthful fallback when the evaluation failed, or
40
+ * `undefined` when settle is disabled for this call.
41
+ *
42
+ * The guarantee matters because the action itself already SUCCEEDED by the
43
+ * time settle runs: if the action triggered a full-document navigation, the
44
+ * execution context the settle expression runs in is destroyed mid-wait and
45
+ * `Runtime.evaluate` rejects. Letting that rejection escape would flip the
46
+ * whole tool response to ok:false for an action that landed — an agent
47
+ * would retry it and risk a duplicate submission. Instead the caller gets
48
+ * `{ settled: false, reason: 'context-destroyed' | 'settle-error' }` spliced
49
+ * onto an ok:true result: 'context-destroyed' is itself a strong signal the
50
+ * action navigated the page.
51
+ */
52
+ export async function settleSafely(evaluate, settle) {
53
+ if (!settle)
54
+ return undefined;
55
+ try {
56
+ const result = await evaluate(buildSettleJs({ settle_ms: settle.settleMs, settle_timeout_ms: settle.settleTimeoutMs }));
57
+ if (result && typeof result === 'object')
58
+ return result;
59
+ // Defensive: the expression always returns an object; anything else
60
+ // means the evaluation pipeline degraded (e.g. serialization loss).
61
+ return { settled: false, reason: 'settle-error' };
62
+ }
63
+ catch (err) {
64
+ const message = err instanceof Error ? err.message : String(err);
65
+ const contextDestroyed = /context/i.test(message) || /navigat/i.test(message);
66
+ return { settled: false, reason: contextDestroyed ? 'context-destroyed' : 'settle-error' };
67
+ }
68
+ }
69
+ //# sourceMappingURL=settle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settle.js","sourceRoot":"","sources":["../src/settle.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD;;0CAE0C;AAC1C,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AACrC,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC;AAClC,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAC9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAO5C;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,CAA0B;IAC5D,MAAM,WAAW,GAAG,CAAC,CAAC,SAAS,CAAC;IAChC,MAAM,QAAQ,GACZ,OAAO,WAAW,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,IAAI,CAAC;QACjF,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,aAAa,CAAC;QACtC,CAAC,CAAC,iBAAiB,CAAC;IACxB,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,UAAU,GAAG,CAAC,CAAC,iBAAiB,CAAC;IACvC,MAAM,eAAe,GACnB,OAAO,UAAU,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,CAAC;QAC9E,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,qBAAqB,CAAC;QAC7C,CAAC,CAAC,yBAAyB,CAAC;IAChC,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;AACvC,CAAC;AAMD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAoB,EACpB,MAA2B;IAE3B,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,aAAa,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,CAAC,eAAe,EAAE,CAAC,CACzF,CAAC;QACF,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,MAAiC,CAAC;QACnF,oEAAoE;QACpE,oEAAoE;QACpE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IACpD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,gBAAgB,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9E,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC;IAC7F,CAAC;AACH,CAAC"}
@@ -96,6 +96,12 @@ export const TOOL_CATALOGUE = [
96
96
  category: 'action',
97
97
  summary: 'Type text into an input',
98
98
  },
99
+ {
100
+ name: 'browser.press',
101
+ family: 'bridge',
102
+ category: 'action',
103
+ summary: 'Press a key (with modifiers) using trusted CDP input',
104
+ },
99
105
  {
100
106
  name: 'browser.drag',
101
107
  family: 'bridge',
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,25 @@
9
9
  * structured shape keeps the "when to use" copy beside the tool that owns
10
10
  * it instead of drifting in a single monolithic string.
11
11
  */
12
+ /**
13
+ * Shared `settle_ms` / `settle_timeout_ms` schema properties for
14
+ * click / type / press. Identical wording and semantics across all three —
15
+ * defined once so the descriptions cannot drift between tools.
16
+ */
17
+ const SETTLE_SCHEMA_PROPERTIES = {
18
+ settle_ms: {
19
+ type: 'number',
20
+ minimum: 0,
21
+ maximum: 2000,
22
+ description: 'After dispatching the action, wait until the page goes quiet — no DOM mutations for this many consecutive ms — before returning. Folds the wait_for + snapshot round trip most flows need after an action into the action call itself. Default 150, hard ceiling 2000. Pass 0 to disable and return immediately (pre-v0.16.0 behavior). Blind spot to keep in mind: the observer installs right AFTER the action dispatches, so mutations that complete in that gap are invisible — mutation_count:0 with settled:true does NOT prove the action had no effect — and an async reaction that only starts after the quiet window is likewise missed. For a specific expected condition, browser.wait_for remains the right tool.',
23
+ },
24
+ settle_timeout_ms: {
25
+ type: 'number',
26
+ minimum: 0,
27
+ maximum: 10000,
28
+ description: 'Overall cap on the settle wait in ms, in case the page never goes fully quiet (polling animations, live tickers, spinners). Default 2000, hard ceiling 10000. Ignored when settle_ms is 0.',
29
+ },
30
+ };
12
31
  export const BROWSER_TOOL_DEFINITIONS = [
13
32
  {
14
33
  name: 'browser.list_tabs',
@@ -503,7 +522,7 @@ export const BROWSER_TOOL_DEFINITIONS = [
503
522
  },
504
523
  {
505
524
  name: 'browser.click',
506
- description: 'Click an element by CSS selector in the connected tab. The selector usually comes from browser.snapshot or browser.find. The lookup pierces open Shadow DOM roots and same-origin iframes (nested arbitrarily), so a selector for a web-component internal or an in-page iframe works without extra steps. Before dispatching the click, the element is hit-tested at its own click point (starting from its own shadow root / iframe document) — if a different element covers that point, the call returns ok:false with a description of the blocker instead of clicking the wrong thing blindly. Pass `force:true` to skip that guard.',
525
+ description: 'Click an element by CSS selector in the connected tab. The selector usually comes from browser.snapshot or browser.find. The lookup pierces open Shadow DOM roots and same-origin iframes (nested arbitrarily), so a selector for a web-component internal or an in-page iframe works without extra steps. Before dispatching the click, the element is hit-tested at its own click point (starting from its own shadow root / iframe document) — if a different element covers that point, the call returns ok:false with a description of the blocker instead of clicking the wrong thing blindly. Pass `force:true` to skip that guard. Optionally waits for the page to settle after the click — see `settle_ms`.',
507
526
  inputSchema: {
508
527
  type: 'object',
509
528
  properties: {
@@ -514,6 +533,7 @@ export const BROWSER_TOOL_DEFINITIONS = [
514
533
  default: false,
515
534
  description: 'Skip the occlusion guard and dispatch the click even if another element currently covers the click point. Escape hatch for cases where the "covering" element is intentional (e.g. a transparent hit-target layer) or the guard produces a false positive. Default false.',
516
535
  },
536
+ ...SETTLE_SCHEMA_PROPERTIES,
517
537
  },
518
538
  required: ['tab_id', 'selector'],
519
539
  additionalProperties: false,
@@ -528,12 +548,13 @@ export const BROWSER_TOOL_DEFINITIONS = [
528
548
  'Auto-claims the tab on first use in multi-agent mode.',
529
549
  'ok:false with an "Element covered by …" error means something else is on top of the target at click time (a modal backdrop, a loading overlay, a dropdown). Click or dismiss the covering element first, or re-snapshot — do not blindly retry with force:true.',
530
550
  'CLOSED shadow roots (attachShadow({mode:"closed"})) are unreachable — there is no CDP-level workaround. Cross-origin iframes are also unreachable (same-origin policy).',
551
+ 'The result carries a compact `settle` object (`{ settled, duration_ms, mutation_count, url_changed?, focus_moved?, reason? }`) unless `settle_ms:0`. `settled:false` means the page never went quiet within `settle_timeout_ms` — not necessarily an error, some pages never stop mutating (tickers, spinners). When the settle wait itself could not run to completion, `settled:false` comes with `reason`: "context-destroyed" (the action navigated the page, destroying the observer\'s execution context — the action still succeeded, and this is itself a strong navigation signal) or "settle-error" (any other settle-side failure). With settle on, a separate browser.wait_for right after a click is usually unnecessary.',
531
552
  ],
532
553
  },
533
554
  },
534
555
  {
535
556
  name: 'browser.type',
536
- description: 'Focus an input by CSS selector and type text into it. If clear=true, clears the current value first. The lookup pierces open Shadow DOM roots and same-origin iframes, same as browser.click.',
557
+ description: 'Focus an input by CSS selector and type text into it. If clear=true, clears the current value first. The lookup pierces open Shadow DOM roots and same-origin iframes, same as browser.click. Optionally waits for the page to settle after typing — see `settle_ms`.',
537
558
  inputSchema: {
538
559
  type: 'object',
539
560
  properties: {
@@ -541,6 +562,7 @@ export const BROWSER_TOOL_DEFINITIONS = [
541
562
  selector: { type: 'string' },
542
563
  text: { type: 'string' },
543
564
  clear: { type: 'boolean', default: false },
565
+ ...SETTLE_SCHEMA_PROPERTIES,
544
566
  },
545
567
  required: ['tab_id', 'selector', 'text'],
546
568
  additionalProperties: false,
@@ -553,7 +575,51 @@ export const BROWSER_TOOL_DEFINITIONS = [
553
575
  gotchas: [
554
576
  'Pass clear:true when you need to replace the current value instead of appending to it.',
555
577
  'CLOSED shadow roots and cross-origin iframes are unreachable, same as browser.click.',
578
+ 'For Enter/Tab/Escape/arrow keys after typing (submitting a form, confirming an autocomplete suggestion), use browser.press — dispatchEvent-based synthetic KeyboardEvent via browser.evaluate is NOT trusted input and many rich editors and autocompletes ignore it.',
579
+ 'The result carries a compact `settle` object (same shape as browser.click) unless `settle_ms:0`.',
580
+ ],
581
+ },
582
+ },
583
+ {
584
+ name: 'browser.press',
585
+ description: 'Press a single key, optionally with modifiers, using a TRUSTED CDP `Input.dispatchKeyEvent` sequence (`isTrusted: true`) — not a synthetic `KeyboardEvent` dispatched via `browser.evaluate`, which many rich text editors, autocompletes, and non-DOM runtimes (Qt-WASM, WebGL — see browser.canvas_screenshot) silently ignore. `key` accepts a human name (`Enter`, `Escape`, `Tab`, `Backspace`, `Delete`, `ArrowUp`/`ArrowDown`/`ArrowLeft`/`ArrowRight`, `Home`, `End`, `PageUp`, `PageDown`, `Space`), case-insensitive, or a single printable character, case-sensitive (`"a"` vs `"A"`, `"@"`). `modifiers` combines with the key for shortcuts (`Ctrl+A` = `{ key: "a", modifiers: ["Control"] }`). Pass `selector` to focus an element first (same deep Shadow DOM / iframe search as click/type/find); omit it to send the key to whatever currently has focus. Optionally waits for the page to settle after the key press — see `settle_ms`.',
586
+ inputSchema: {
587
+ type: 'object',
588
+ properties: {
589
+ tab_id: { type: 'string' },
590
+ key: {
591
+ type: 'string',
592
+ description: 'Human key name ("Enter", "Escape", "Tab", "Backspace", "Delete", "ArrowUp"/"ArrowDown"/"ArrowLeft"/"ArrowRight", "Home", "End", "PageUp", "PageDown", "Space" — case-insensitive) or a single printable character (case-sensitive, e.g. "a", "A", "@").',
593
+ },
594
+ modifiers: {
595
+ type: 'array',
596
+ items: { type: 'string', enum: ['Alt', 'Control', 'Meta', 'Shift'] },
597
+ description: 'Modifier keys held during the press. Combine for shortcuts, e.g. ["Control"] + key:"a" for Ctrl+A, or ["Meta"] + key:"s" for Cmd+S on macOS.',
598
+ },
599
+ selector: {
600
+ type: 'string',
601
+ description: 'Optional. Focus this element (resolved via the same deep Shadow DOM / iframe search as browser.click) before dispatching the key. Omit to send the key to the currently focused element.',
602
+ },
603
+ ...SETTLE_SCHEMA_PROPERTIES,
604
+ },
605
+ required: ['tab_id', 'key'],
606
+ additionalProperties: false,
607
+ },
608
+ doc: {
609
+ purpose: 'Dispatch one trusted keyboard key press (optionally with modifiers) — Enter/Escape/Tab/arrows/shortcuts/a single character.',
610
+ when_to_use: [
611
+ 'Submitting a form or confirming an autocomplete suggestion with Enter, dismissing a dialog with Escape, moving focus with Tab, navigating a list/menu with arrow keys.',
612
+ 'A keyboard shortcut the page listens for (Ctrl+A select-all, Cmd+S save, Ctrl+Z undo) — pass modifiers plus the base key.',
613
+ 'A rich text editor, autocomplete widget, or non-DOM runtime (Qt-WASM, WebGL) that discards synthetic input dispatched via browser.evaluate and needs a REAL, isTrusted:true keystroke.',
614
+ ],
615
+ gotchas: [
616
+ 'For typing normal text into an input, use browser.type instead — it goes through the native value setter and is far cheaper per character than one browser.press call per keystroke.',
617
+ 'For anything not covered by the named keys or a single character, browser.evaluate is the escape hatch — but note it produces isTrusted:false events, which some pages ignore.',
618
+ 'Named keys are case-insensitive ("arrowup" works); single printable characters are case-SENSITIVE. Shift IS auto-added for characters that require it on a US layout — uppercase letters, shifted punctuation like "@" or "{" — so to type an uppercase letter just pass key:"A". Do NOT pass key:"a" with modifiers:["Shift"] expecting an "A": that inserts a lowercase "a" with shiftKey reported as held, a combination no physical keyboard produces.',
619
+ 'CLOSED shadow roots and cross-origin iframes are unreachable for the optional `selector`, same as browser.click.',
620
+ 'The result carries a compact `settle` object (same shape as browser.click) unless `settle_ms:0`.',
556
621
  ],
622
+ example: 'browser.press({ tab_id: "tab_1", key: "Enter" })',
557
623
  },
558
624
  },
559
625
  {