@jobshimo/browser-link 0.22.0 → 0.23.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 (63) hide show
  1. package/README.md +5 -0
  2. package/dist/agent-instructions/content.js +11 -0
  3. package/dist/agent-instructions/content.js.map +1 -1
  4. package/dist/cdp/client.d.ts +35 -0
  5. package/dist/cdp/client.js +149 -0
  6. package/dist/cdp/client.js.map +1 -0
  7. package/dist/cdp/flow.d.ts +178 -0
  8. package/dist/cdp/flow.js +217 -0
  9. package/dist/cdp/flow.js.map +1 -0
  10. package/dist/cdp/gate.d.ts +31 -0
  11. package/dist/cdp/gate.js +35 -0
  12. package/dist/cdp/gate.js.map +1 -0
  13. package/dist/cdp/grant.d.ts +45 -0
  14. package/dist/cdp/grant.js +81 -0
  15. package/dist/cdp/grant.js.map +1 -0
  16. package/dist/cdp/inpage/builders.d.ts +165 -0
  17. package/dist/cdp/inpage/builders.js +501 -0
  18. package/dist/cdp/inpage/builders.js.map +1 -0
  19. package/dist/cdp/inpage/deep-query.d.ts +57 -0
  20. package/dist/cdp/inpage/deep-query.js +325 -0
  21. package/dist/cdp/inpage/deep-query.js.map +1 -0
  22. package/dist/cdp/inpage/dom-helpers.d.ts +27 -0
  23. package/dist/cdp/inpage/dom-helpers.js +147 -0
  24. package/dist/cdp/inpage/dom-helpers.js.map +1 -0
  25. package/dist/cdp/keymap.d.ts +83 -0
  26. package/dist/cdp/keymap.js +205 -0
  27. package/dist/cdp/keymap.js.map +1 -0
  28. package/dist/cdp/settle.d.ts +38 -0
  29. package/dist/cdp/settle.js +76 -0
  30. package/dist/cdp/settle.js.map +1 -0
  31. package/dist/cdp/support.d.ts +26 -0
  32. package/dist/cdp/support.js +58 -0
  33. package/dist/cdp/support.js.map +1 -0
  34. package/dist/cdp/targets.d.ts +54 -0
  35. package/dist/cdp/targets.js +176 -0
  36. package/dist/cdp/targets.js.map +1 -0
  37. package/dist/cdp/transport.d.ts +31 -0
  38. package/dist/cdp/transport.js +531 -0
  39. package/dist/cdp/transport.js.map +1 -0
  40. package/dist/cli.js +50 -0
  41. package/dist/cli.js.map +1 -1
  42. package/dist/commands/about.js +13 -0
  43. package/dist/commands/about.js.map +1 -1
  44. package/dist/commands/cdp.d.ts +28 -0
  45. package/dist/commands/cdp.js +172 -0
  46. package/dist/commands/cdp.js.map +1 -0
  47. package/dist/commands/config.d.ts +92 -0
  48. package/dist/commands/config.js +173 -12
  49. package/dist/commands/config.js.map +1 -1
  50. package/dist/config.d.ts +70 -0
  51. package/dist/config.js +94 -1
  52. package/dist/config.js.map +1 -1
  53. package/dist/extension/manifest.json +1 -1
  54. package/dist/server.js +9 -0
  55. package/dist/server.js.map +1 -1
  56. package/dist/tools/browser-definitions.js +4 -3
  57. package/dist/tools/browser-definitions.js.map +1 -1
  58. package/dist/tools/browser-dispatch.d.ts +31 -0
  59. package/dist/tools/browser-dispatch.js +88 -40
  60. package/dist/tools/browser-dispatch.js.map +1 -1
  61. package/dist/tools/server-instructions.js +19 -0
  62. package/dist/tools/server-instructions.js.map +1 -1
  63. package/package.json +1 -1
@@ -0,0 +1,31 @@
1
+ export type CdpDirectGate = {
2
+ ok: true;
3
+ } | {
4
+ ok: false;
5
+ error: string;
6
+ };
7
+ /** Exact wording agents receive when cdp-direct is off entirely. Agents get
8
+ * this string and NOTHING else — there is no bypass, no partial access, no
9
+ * alternate path to a `cdp:` tab while this check fails. */
10
+ export declare const CDP_DIRECT_DISABLED_ERROR = "cdp-direct is disabled. The user can enable it with: browser-link config set cdp-direct.enabled true";
11
+ /** Exact wording agents receive when cdp-direct is enabled but no live
12
+ * grant exists (never granted, revoked, or expired). */
13
+ export declare const CDP_DIRECT_NO_GRANT_ERROR = "cdp-direct requires an active grant. Ask the user to run: browser-link cdp allow";
14
+ /**
15
+ * The ONE gate every cdp-direct code path runs through, in the exact order
16
+ * the feature's permission model requires: (1) is cdp-direct enabled at
17
+ * all, (2) is there a live, unexpired grant. Re-evaluated on every call —
18
+ * never cached — because either condition can flip mid-session (the user
19
+ * disables the setting, or a time-boxed grant simply expires) and the very
20
+ * next tool call must see that.
21
+ *
22
+ * Called from two places by design: `tools/browser-dispatch.ts`'s routing
23
+ * layer (so an agent gets the exact error text above before any cdp-direct
24
+ * code runs at all) AND `cdp/transport.ts` itself (defense in depth, so the
25
+ * transport module is never safe to call without this check even if a
26
+ * future caller reaches it directly). `cdp/targets.ts`'s `browser.list_tabs`
27
+ * discovery also gates on this, but treats a failing gate as "show nothing"
28
+ * rather than an error — an agent that has never heard of cdp-direct should
29
+ * not see it mentioned in a routine list_tabs call.
30
+ */
31
+ export declare function checkCdpDirectGate(now?: number): CdpDirectGate;
@@ -0,0 +1,35 @@
1
+ import { loadConfig } from '../config.js';
2
+ import { isGrantLive, loadGrant } from './grant.js';
3
+ /** Exact wording agents receive when cdp-direct is off entirely. Agents get
4
+ * this string and NOTHING else — there is no bypass, no partial access, no
5
+ * alternate path to a `cdp:` tab while this check fails. */
6
+ export const CDP_DIRECT_DISABLED_ERROR = 'cdp-direct is disabled. The user can enable it with: browser-link config set cdp-direct.enabled true';
7
+ /** Exact wording agents receive when cdp-direct is enabled but no live
8
+ * grant exists (never granted, revoked, or expired). */
9
+ export const CDP_DIRECT_NO_GRANT_ERROR = 'cdp-direct requires an active grant. Ask the user to run: browser-link cdp allow';
10
+ /**
11
+ * The ONE gate every cdp-direct code path runs through, in the exact order
12
+ * the feature's permission model requires: (1) is cdp-direct enabled at
13
+ * all, (2) is there a live, unexpired grant. Re-evaluated on every call —
14
+ * never cached — because either condition can flip mid-session (the user
15
+ * disables the setting, or a time-boxed grant simply expires) and the very
16
+ * next tool call must see that.
17
+ *
18
+ * Called from two places by design: `tools/browser-dispatch.ts`'s routing
19
+ * layer (so an agent gets the exact error text above before any cdp-direct
20
+ * code runs at all) AND `cdp/transport.ts` itself (defense in depth, so the
21
+ * transport module is never safe to call without this check even if a
22
+ * future caller reaches it directly). `cdp/targets.ts`'s `browser.list_tabs`
23
+ * discovery also gates on this, but treats a failing gate as "show nothing"
24
+ * rather than an error — an agent that has never heard of cdp-direct should
25
+ * not see it mentioned in a routine list_tabs call.
26
+ */
27
+ export function checkCdpDirectGate(now = Date.now()) {
28
+ const cfg = loadConfig();
29
+ if (cfg.cdpDirectEnabled !== true)
30
+ return { ok: false, error: CDP_DIRECT_DISABLED_ERROR };
31
+ if (!isGrantLive(loadGrant(), now))
32
+ return { ok: false, error: CDP_DIRECT_NO_GRANT_ERROR };
33
+ return { ok: true };
34
+ }
35
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/cdp/gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIpD;;4DAE4D;AAC5D,MAAM,CAAC,MAAM,yBAAyB,GACpC,sGAAsG,CAAC;AAEzG;wDACwD;AACxD,MAAM,CAAC,MAAM,yBAAyB,GACpC,kFAAkF,CAAC;AAErF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc,IAAI,CAAC,GAAG,EAAE;IACzD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,IAAI,GAAG,CAAC,gBAAgB,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC;IAC1F,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,EAAE,GAAG,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC;IAC3F,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * On-disk record of a `browser-link cdp allow` grant — the time-boxed human
3
+ * consent that, together with `cdp-direct.enabled`, is required before any
4
+ * tool may address a `cdp:` tab (see `gate.ts`). Lives in its own small JSON
5
+ * file in the data dir, never in the repo, never in `config.json` (a grant
6
+ * is a momentary permission, not a persistent preference).
7
+ */
8
+ export interface CdpGrant {
9
+ grantedAt: number;
10
+ /** `null` means the grant never expires ("never" / `--minutes 0`) —
11
+ * documented in the CLI help and README as reducing the security
12
+ * posture, not a default anyone should reach for casually. */
13
+ expiresAt: number | null;
14
+ }
15
+ /** Absolute path of the grant file — exported so the CLI can name it in an
16
+ * honest "could not remove" error (see `commands/cdp.ts`) rather than
17
+ * leaving the user guessing which file to delete by hand. */
18
+ export declare function grantFilePath(): string;
19
+ /** Read the current grant, or `null` when none exists or the file is
20
+ * corrupt/unreadable — corruption degrades to "no grant" (the safer
21
+ * failure mode for a permission file) rather than throwing. */
22
+ export declare function loadGrant(): CdpGrant | null;
23
+ /** Record a fresh grant. `ttlMinutes: 0` means "never expires"
24
+ * (`expiresAt: null`); any other value is the grant's lifetime in minutes
25
+ * from `now`. `now` is a parameter so tests do not need to mock the clock. */
26
+ export declare function saveGrant(ttlMinutes: number, now?: number): CdpGrant;
27
+ /** Revoke the current grant, if any. Idempotent — revoking with no grant
28
+ * present is not an error and is a no-op.
29
+ *
30
+ * Deliberately does NOT swallow an unlink failure: this is a SECURITY
31
+ * operation, and a grant file that survives a "revoke" (a transient
32
+ * Windows lock, an EPERM, a read-only dir) is still honoured by the gate
33
+ * on the very next tool call — so reporting success while the door stays
34
+ * open would be a lie. The error propagates; the CLI (`revokeCdpDirect` in
35
+ * `commands/cdp.ts`) catches it and tells the user the grant may still be
36
+ * active and to remove the file by hand. */
37
+ export declare function clearGrant(): void;
38
+ /** Whether `grant` is currently live: present, and either non-expiring or
39
+ * not yet past its `expiresAt`. */
40
+ export declare function isGrantLive(grant: CdpGrant | null, now?: number): boolean;
41
+ /** Milliseconds remaining on `grant`, or `null` when it never expires or
42
+ * does not exist (callers distinguish "no grant" from "never expires" by
43
+ * checking `grant` itself first). Never negative — an expired grant reads
44
+ * as `0` remaining, not a negative number. */
45
+ export declare function remainingMs(grant: CdpGrant | null, now?: number): number | null;
@@ -0,0 +1,81 @@
1
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { getDataDir } from '../map/paths.js';
4
+ function grantFile() {
5
+ return join(getDataDir(), 'cdp-grant.json');
6
+ }
7
+ /** Absolute path of the grant file — exported so the CLI can name it in an
8
+ * honest "could not remove" error (see `commands/cdp.ts`) rather than
9
+ * leaving the user guessing which file to delete by hand. */
10
+ export function grantFilePath() {
11
+ return grantFile();
12
+ }
13
+ function isCdpGrant(value) {
14
+ if (typeof value !== 'object' || value === null)
15
+ return false;
16
+ const v = value;
17
+ return (typeof v.grantedAt === 'number' && (v.expiresAt === null || typeof v.expiresAt === 'number'));
18
+ }
19
+ /** Read the current grant, or `null` when none exists or the file is
20
+ * corrupt/unreadable — corruption degrades to "no grant" (the safer
21
+ * failure mode for a permission file) rather than throwing. */
22
+ export function loadGrant() {
23
+ const path = grantFile();
24
+ if (!existsSync(path))
25
+ return null;
26
+ try {
27
+ const parsed = JSON.parse(readFileSync(path, 'utf8'));
28
+ return isCdpGrant(parsed) ? parsed : null;
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ }
34
+ /** Record a fresh grant. `ttlMinutes: 0` means "never expires"
35
+ * (`expiresAt: null`); any other value is the grant's lifetime in minutes
36
+ * from `now`. `now` is a parameter so tests do not need to mock the clock. */
37
+ export function saveGrant(ttlMinutes, now = Date.now()) {
38
+ const path = grantFile();
39
+ mkdirSync(dirname(path), { recursive: true });
40
+ const grant = {
41
+ grantedAt: now,
42
+ expiresAt: ttlMinutes === 0 ? null : now + ttlMinutes * 60_000,
43
+ };
44
+ writeFileSync(path, JSON.stringify(grant, null, 2) + '\n', 'utf8');
45
+ return grant;
46
+ }
47
+ /** Revoke the current grant, if any. Idempotent — revoking with no grant
48
+ * present is not an error and is a no-op.
49
+ *
50
+ * Deliberately does NOT swallow an unlink failure: this is a SECURITY
51
+ * operation, and a grant file that survives a "revoke" (a transient
52
+ * Windows lock, an EPERM, a read-only dir) is still honoured by the gate
53
+ * on the very next tool call — so reporting success while the door stays
54
+ * open would be a lie. The error propagates; the CLI (`revokeCdpDirect` in
55
+ * `commands/cdp.ts`) catches it and tells the user the grant may still be
56
+ * active and to remove the file by hand. */
57
+ export function clearGrant() {
58
+ const path = grantFile();
59
+ if (!existsSync(path))
60
+ return;
61
+ unlinkSync(path);
62
+ }
63
+ /** Whether `grant` is currently live: present, and either non-expiring or
64
+ * not yet past its `expiresAt`. */
65
+ export function isGrantLive(grant, now = Date.now()) {
66
+ if (!grant)
67
+ return false;
68
+ if (grant.expiresAt === null)
69
+ return true;
70
+ return grant.expiresAt > now;
71
+ }
72
+ /** Milliseconds remaining on `grant`, or `null` when it never expires or
73
+ * does not exist (callers distinguish "no grant" from "never expires" by
74
+ * checking `grant` itself first). Never negative — an expired grant reads
75
+ * as `0` remaining, not a negative number. */
76
+ export function remainingMs(grant, now = Date.now()) {
77
+ if (!grant || grant.expiresAt === null)
78
+ return null;
79
+ return Math.max(0, grant.expiresAt - now);
80
+ }
81
+ //# sourceMappingURL=grant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant.js","sourceRoot":"","sources":["../../src/cdp/grant.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAiB7C,SAAS,SAAS;IAChB,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,gBAAgB,CAAC,CAAC;AAC9C,CAAC;AAED;;6DAE6D;AAC7D,MAAM,UAAU,aAAa;IAC3B,OAAO,SAAS,EAAE,CAAC;AACrB,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,CACL,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAC7F,CAAC;AACJ,CAAC;AAED;;+DAE+D;AAC/D,MAAM,UAAU,SAAS;IACvB,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;IACzB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAY,CAAC;QACjE,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;8EAE8E;AAC9E,MAAM,UAAU,SAAS,CAAC,UAAkB,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;IACpE,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;IACzB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAa;QACtB,SAAS,EAAE,GAAG;QACd,SAAS,EAAE,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,UAAU,GAAG,MAAM;KAC/D,CAAC;IACF,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IACnE,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;4CAS4C;AAC5C,MAAM,UAAU,UAAU;IACxB,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;IACzB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO;IAC9B,UAAU,CAAC,IAAI,CAAC,CAAC;AACnB,CAAC;AAED;mCACmC;AACnC,MAAM,UAAU,WAAW,CAAC,KAAsB,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;IAC1E,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC1C,OAAO,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC;AAC/B,CAAC;AAED;;;8CAG8C;AAC9C,MAAM,UAAU,WAAW,CAAC,KAAsB,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;IAC1E,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACpD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Build the snapshot expression. Filters are applied INSIDE the page so the
3
+ * server never receives the dropped material at all — that's where the token
4
+ * win for `within_selector` / `only_interactive` / `exclude` actually lives.
5
+ * The serializer omits empty-string fields per entry (token win that applies
6
+ * unconditionally on every snapshot).
7
+ *
8
+ * The scan pierces open Shadow DOM roots and same-origin iframes via
9
+ * `deepQueryAll` (see `deep-query.ts`). `within_selector` itself is resolved
10
+ * with `deepQueryFirst`, so a subtree inside a shadow root or iframe can be
11
+ * targeted too. Matched entries that live inside an iframe carry an
12
+ * optional `frame` field with the innermost hosting iframe's selector, and
13
+ * entries whose generated selector could not be made unique across the deep
14
+ * search scope carry `ambiguous: true` (see `genSelectorInfo`).
15
+ */
16
+ export interface SnapshotOpts {
17
+ within_selector?: string;
18
+ only_interactive?: boolean;
19
+ exclude?: string[];
20
+ max_interactive?: number;
21
+ }
22
+ export declare function buildSnapshotJs(opts?: SnapshotOpts): string;
23
+ /** Build the find-by-text expression. Returns one of:
24
+ * { matched: true, selector, coords:{x,y}, tag, text, frame?, ambiguous? }
25
+ * { matched: false, reason: 'not-found', error?, near_misses?: [{text,selector,role?}] }
26
+ * { matched: false, reason: 'multiple-matches', candidates: [{selector,text,tag}] }
27
+ *
28
+ * Role-aware: when `role` is provided, only elements whose explicit ARIA
29
+ * role or implicit role match are considered. When omitted, the search
30
+ * scans a broad set of interactive + clickable elements.
31
+ *
32
+ * The scan pierces open Shadow DOM roots and same-origin iframes via
33
+ * `deepQueryAll`. `coords` are mapped to TOP-LEVEL viewport coordinates
34
+ * (accounting for ancestor iframe offsets) so they are directly usable by
35
+ * `browser.drag` and other coordinate-based tools.
36
+ *
37
+ * `not-found` carries up to 3 `near_misses` — VISIBLE interactive-ish
38
+ * elements ranked by (1) case-insensitive substring containment of the
39
+ * query in the candidate's accessible text/name, then (2) token overlap —
40
+ * so the agent gets a "did you mean" instead of a bare miss. Omitted
41
+ * entirely when nothing scores (no junk suggestions). When `role` narrowed
42
+ * the scan and the text DOES exist outside that role, `error` names the
43
+ * exclusion explicitly (e.g. `text matched 2 elements but none with role
44
+ * "button" — closest: <div onclick> "GIF picker"`) and `near_misses` is
45
+ * populated from that broader, role-agnostic match set instead.
46
+ *
47
+ * Near-miss entries use `genSelector` (not `genSelectorInfo`), so the
48
+ * `ambiguous` flag is intentionally NOT carried — a near-miss selector
49
+ * that collides with structurally-identical twins in other roots resolves
50
+ * first-match-wins silently. Documented on the tool as: hints for the
51
+ * next `find`, never click targets.
52
+ */
53
+ export interface FindOpts {
54
+ text: string;
55
+ role?: string;
56
+ exact?: boolean;
57
+ }
58
+ export declare function buildFindJs(opts: FindOpts): string;
59
+ /**
60
+ * Build the click-resolution expression: resolves the selector across the
61
+ * deep search scope, scrolls the element (and every ancestor iframe) into
62
+ * view, computes the TOP-LEVEL viewport click point, and — unless `force`
63
+ * is set — hit-tests that point before returning it, so a covering overlay
64
+ * is caught before dispatching CDP mouse events. Returns:
65
+ * { ok: true, x, y, tag }
66
+ * { ok: false, reason: 'invalid-selector', error }
67
+ * { ok: false, reason: 'not-found' }
68
+ * { ok: false, reason: 'occluded', blocker }
69
+ *
70
+ * The `invalid-selector` check runs BEFORE `deepQueryFirst` — a malformed
71
+ * CSS selector throws the identical `SyntaxError` from every root's own
72
+ * `querySelector`, which `deepQueryFirst`'s per-root try/catch otherwise
73
+ * swallows into an indistinguishable "not-found". Checking once against
74
+ * `document` up front separates "the selector text is broken" from "the
75
+ * selector is valid but nothing on the page matches it".
76
+ */
77
+ export interface ClickResolveOpts {
78
+ selector: string;
79
+ force: boolean;
80
+ }
81
+ export declare function buildClickResolveJs(opts: ClickResolveOpts): string;
82
+ /**
83
+ * Build the type-resolution expression: resolves the selector across the
84
+ * deep search scope, focuses it, optionally clears its value, and reports
85
+ * success so the caller can follow up with CDP `Input.insertText` (which
86
+ * types into whatever currently has focus at the browser level — works the
87
+ * same whether that focus target lives in the top document, a shadow root,
88
+ * or a same-origin iframe). Returns:
89
+ * { ok: true }
90
+ * { ok: false, reason: 'invalid-selector', error }
91
+ * { ok: false, reason: 'not-found' }
92
+ *
93
+ * Same invalid-selector pre-check as `buildClickResolveJs` — see its doc
94
+ * comment for why the check has to run before `deepQueryFirst`.
95
+ */
96
+ export interface TypeResolveOpts {
97
+ selector: string;
98
+ clear: boolean;
99
+ }
100
+ export declare function buildTypeResolveJs(opts: TypeResolveOpts): string;
101
+ /**
102
+ * Build the focus-resolution expression for `browser.press`'s optional
103
+ * `selector`: resolves the selector across the deep search scope and
104
+ * focuses it, reporting success so the caller can follow up with the CDP
105
+ * key event sequence (which — like `Input.insertText` — targets whatever
106
+ * currently has focus at the browser level). Intentionally separate from
107
+ * `buildTypeResolveJs`: press never clears a value, and naming it
108
+ * distinctly keeps the press code path readable on its own.
109
+ */
110
+ export interface FocusResolveOpts {
111
+ selector: string;
112
+ }
113
+ export declare function buildFocusJs(opts: FocusResolveOpts): string;
114
+ /**
115
+ * Build the settle-await expression shared by `browser.click`, `.type` and
116
+ * `.press`. Called AFTER the action's CDP input events have been
117
+ * dispatched: installs one `MutationObserver` on `document` (subtree,
118
+ * childList, attributes, characterData) and resolves once no mutation has
119
+ * landed for `settle_ms` consecutive milliseconds, or once `settle_timeout_ms`
120
+ * total has elapsed — whichever comes first. Returned as a Promise so the
121
+ * caller's `evaluateInTab` (CDP `Runtime.evaluate` with `awaitPromise: true`)
122
+ * waits for it naturally.
123
+ *
124
+ * Baseline `url`/`activeElement` are captured at the TOP of this
125
+ * expression — i.e. right after the action's own focus/navigation side
126
+ * effects have already landed (dispatch already happened by the time this
127
+ * runs), so `focus_moved`/`url_changed` report drift that happened DURING
128
+ * the settle window, not the action's own expected effect.
129
+ *
130
+ * The result omits `url_changed` / `focus_moved` when they did not change
131
+ * — token-lean by construction, no post-filtering needed.
132
+ */
133
+ export interface SettleOpts {
134
+ /** Quiet-period length in ms. Caller is expected to have already
135
+ * clamped this to (0, 2000] — 0 means "don't call this builder at all". */
136
+ settle_ms: number;
137
+ /** Overall cap in ms. Caller is expected to have already clamped this
138
+ * to [0, 10000]. */
139
+ settle_timeout_ms: number;
140
+ }
141
+ export declare function buildSettleJs(opts: SettleOpts): string;
142
+ /**
143
+ * Build the `browser.state` expression: a compact orientation snapshot —
144
+ * current url/title, the deep-resolved focused element, visible dialog-role
145
+ * elements, scroll position, and viewport size. Cheaper than a full
146
+ * `browser.snapshot` when the agent only needs "where am I right now".
147
+ *
148
+ * `focused` descends through `document.activeElement` past shadow-root and
149
+ * same-origin-iframe boundaries to the real innermost focused element (a
150
+ * shadow host or an <iframe> being "active" at one level is not the actual
151
+ * focus target the agent cares about). Its `selector` goes through the same
152
+ * `genSelectorInfo` uniqueness check `snapshot`/`find` use, so it carries
153
+ * `ambiguous: true` under the same structurally-identical-twins condition.
154
+ *
155
+ * `dialogs` matches visible `[role=dialog]`, `[role=alertdialog]` and open
156
+ * `<dialog>` elements found via the same deep walk, with a best-effort
157
+ * `label` resolved from aria-label / aria-labelledby / the first heading
158
+ * inside the dialog.
159
+ *
160
+ * Every optional field is omitted when there is nothing to report: no
161
+ * `focused` beyond `<body>`, no `dialogs` when none are open, and no
162
+ * `scroll` when the page is at its default (0,0) position — token-lean by
163
+ * construction, matching the omit-falsy convention the other builders use.
164
+ */
165
+ export declare function buildStateJs(): string;