@geometra/proxy 1.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # @geometra/proxy
2
+
3
+ Headless Chromium proxy that extracts **live DOM layout** (`getBoundingClientRect`) plus a **synthetic Geometra UI tree** and streams **GEOM v1** `frame` / `patch` messages over WebSocket (JSON text). Use it with [`@geometra/mcp`](../../mcp/README.md) to drive arbitrary web apps without screenshots.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @geometra/proxy playwright
9
+ npx playwright install chromium
10
+ ```
11
+
12
+ ## CLI
13
+
14
+ ```bash
15
+ npx geometra-proxy https://example.com --port 3200
16
+ npx geometra-proxy http://localhost:8080 --width 1440 --height 900 --headed
17
+ ```
18
+
19
+ ## Protocol
20
+
21
+ Matches `packages/server` GEOM v1: `frame` with `layout`, `tree`, `protocolVersion`; `patch` with `patches`; client messages `resize`, `event` (`onClick`), `key`, `composition`.
22
+
23
+ Proxy-specific client messages (native Textura servers respond with `error`):
24
+
25
+ - **`file`** — `paths`, optional `x`/`y`, `strategy` (`auto`|`chooser`|`hidden`|`drop`), optional `dropX`/`dropY` for drop targets.
26
+ - **`selectOption`** — `{ type: 'selectOption', x, y, value? | label? | index? }` — native `<select>` only.
27
+ - **`listboxPick`** — `{ type: 'listboxPick', label, exact?, openX?, openY? }` — ARIA `role=option` (custom dropdowns).
28
+ - **`wheel`** — `{ type: 'wheel', deltaY?, deltaX?, x?, y? }` — scroll / wheel at optional coordinates.
29
+
30
+ Extraction merges **all nested iframes** (including cross-origin) into root viewport coordinates, walks **open shadow roots**, and best-effort **enriches names** from Chrome’s accessibility tree (CDP) for closed shadow / opaque widgets.
31
+
32
+ Binary framing is not used (always JSON text), which is what the MCP server expects.
33
+
34
+ ## Limitations
35
+
36
+ - No shadow-DOM piercing; iframes are not flattened.
37
+ - Large DOMs produce large frames; patch coalescing follows the same rules as `@geometra/server`.
@@ -0,0 +1,8 @@
1
+ import type { Page } from 'playwright';
2
+ import type { GeometrySnapshot } from './types.js';
3
+ /**
4
+ * Use Chrome DevTools `Accessibility.getFullAXTree` to back-fill `semantic.ariaLabel` on nodes
5
+ * whose center falls inside an AX bounds box (helps closed shadow + custom controls).
6
+ */
7
+ export declare function enrichSnapshotWithCdpAx(page: Page, snap: GeometrySnapshot): Promise<void>;
8
+ //# sourceMappingURL=a11y-enrich.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a11y-enrich.d.ts","sourceRoot":"","sources":["../src/a11y-enrich.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,IAAI,EAAE,MAAM,YAAY,CAAA;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAgC,MAAM,YAAY,CAAA;AAuChF;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6D/F"}
@@ -0,0 +1,94 @@
1
+ function parseAxBounds(props) {
2
+ if (!Array.isArray(props))
3
+ return null;
4
+ for (const p of props) {
5
+ if (p && typeof p === 'object' && 'name' in p && 'value' in p) {
6
+ const name = p.name;
7
+ const value = p.value;
8
+ if (name === 'bounds' && value && typeof value === 'object' && value !== null) {
9
+ const v = value;
10
+ const left = Number(v.left);
11
+ const top = Number(v.top);
12
+ const width = Number(v.width);
13
+ const height = Number(v.height);
14
+ if ([left, top, width, height].every(Number.isFinite)) {
15
+ return { left, top, width, height };
16
+ }
17
+ }
18
+ }
19
+ }
20
+ return null;
21
+ }
22
+ function boundsIntersectsViewport(b, root) {
23
+ return (b.left + b.width >= root.x &&
24
+ b.left <= root.x + root.width &&
25
+ b.top + b.height >= root.y &&
26
+ b.top <= root.y + root.height);
27
+ }
28
+ /**
29
+ * Use Chrome DevTools `Accessibility.getFullAXTree` to back-fill `semantic.ariaLabel` on nodes
30
+ * whose center falls inside an AX bounds box (helps closed shadow + custom controls).
31
+ */
32
+ export async function enrichSnapshotWithCdpAx(page, snap) {
33
+ let session;
34
+ try {
35
+ session = await page.context().newCDPSession(page);
36
+ await session.send('Accessibility.enable');
37
+ const res = (await session.send('Accessibility.getFullAXTree'));
38
+ const nodes = res.nodes ?? [];
39
+ const root = snap.layout;
40
+ const candidates = [];
41
+ for (const n of nodes) {
42
+ const name = n.name?.value?.trim();
43
+ const role = n.role?.value ?? '';
44
+ if (!name)
45
+ continue;
46
+ const b = parseAxBounds(n.properties);
47
+ if (!b || b.width <= 0 || b.height <= 0)
48
+ continue;
49
+ if (!boundsIntersectsViewport(b, root))
50
+ continue;
51
+ candidates.push({ name, role, bounds: b });
52
+ }
53
+ const visit = (tNode, lNode) => {
54
+ const sem = tNode.semantic;
55
+ if (sem?.ariaLabel || sem?.a11yEnriched) {
56
+ /* skip */
57
+ }
58
+ else {
59
+ const cx = lNode.x + lNode.width / 2;
60
+ const cy = lNode.y + lNode.height / 2;
61
+ for (const c of candidates) {
62
+ const b = c.bounds;
63
+ if (cx >= b.left && cx <= b.left + b.width && cy >= b.top && cy <= b.top + b.height) {
64
+ tNode.semantic = {
65
+ ...(sem ?? {}),
66
+ ariaLabel: c.name,
67
+ a11yRoleHint: c.role,
68
+ a11yEnriched: true,
69
+ };
70
+ break;
71
+ }
72
+ }
73
+ }
74
+ const tch = tNode.children ?? [];
75
+ const lch = lNode.children;
76
+ for (let i = 0; i < tch.length; i++) {
77
+ visit(tch[i], lch[i]);
78
+ }
79
+ };
80
+ visit(snap.tree, snap.layout);
81
+ }
82
+ catch {
83
+ /* optional */
84
+ }
85
+ finally {
86
+ try {
87
+ await session?.detach();
88
+ }
89
+ catch {
90
+ /* ignore */
91
+ }
92
+ }
93
+ }
94
+ //# sourceMappingURL=a11y-enrich.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a11y-enrich.js","sourceRoot":"","sources":["../src/a11y-enrich.ts"],"names":[],"mappings":"AAUA,SAAS,aAAa,CAAC,KAA4B;IACjD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACtC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;YAC9D,MAAM,IAAI,GAAI,CAAuB,CAAC,IAAI,CAAA;YAC1C,MAAM,KAAK,GAAI,CAAwB,CAAC,KAAK,CAAA;YAC7C,IAAI,IAAI,KAAK,QAAQ,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBAC9E,MAAM,CAAC,GAAG,KAAgC,CAAA;gBAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;gBAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;gBACzB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;gBAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;gBAC/B,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACtD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;gBACrC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,wBAAwB,CAAC,CAAW,EAAE,IAAoB;IACjE,OAAO,CACL,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;QAC1B,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK;QAC7B,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC;QAC1B,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAC9B,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,IAAU,EAAE,IAAsB;IAC9E,IAAI,OAA+B,CAAA;IACnC,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAA;QAClD,MAAM,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAA;QAC1C,MAAM,GAAG,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAM7D,CAAA;QACD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,EAAE,CAAA;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAA;QACxB,MAAM,UAAU,GAAuD,EAAE,CAAA;QACzE,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;YAClC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAA;YAChC,IAAI,CAAC,IAAI;gBAAE,SAAQ;YACnB,MAAM,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,UAAmC,CAAC,CAAA;YAC9D,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC;gBAAE,SAAQ;YACjD,IAAI,CAAC,wBAAwB,CAAC,CAAC,EAAE,IAAI,CAAC;gBAAE,SAAQ;YAChD,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;QAC5C,CAAC;QAED,MAAM,KAAK,GAAG,CAAC,KAAmB,EAAE,KAAqB,EAAQ,EAAE;YACjE,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAA;YAC1B,IAAI,GAAG,EAAE,SAAS,IAAI,GAAG,EAAE,YAAY,EAAE,CAAC;gBACxC,UAAU;YACZ,CAAC;iBAAM,CAAC;gBACN,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAA;gBACpC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;gBACrC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;oBAC3B,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAA;oBAClB,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;wBACpF,KAAK,CAAC,QAAQ,GAAG;4BACf,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC;4BACd,SAAS,EAAE,CAAC,CAAC,IAAI;4BACjB,YAAY,EAAE,CAAC,CAAC,IAAI;4BACpB,YAAY,EAAE,IAAI;yBACnB,CAAA;wBACD,MAAK;oBACP,CAAC;gBACH,CAAC;YACH,CAAC;YACD,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAA;YAChC,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAA;YAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACpC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAE,EAAE,GAAG,CAAC,CAAC,CAAE,CAAC,CAAA;YACzB,CAAC;QACH,CAAC,CAAA;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,cAAc;IAChB,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YACH,MAAM,OAAO,EAAE,MAAM,EAAE,CAAA;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,YAAY;QACd,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { LayoutSnapshot } from './types.js';
2
+ /** Geometry patch aligned with GEOM v1 `patch.patches`. */
3
+ export interface LayoutPatch {
4
+ path: number[];
5
+ x?: number;
6
+ y?: number;
7
+ width?: number;
8
+ height?: number;
9
+ }
10
+ export declare function coalescePatches(patches: LayoutPatch[]): LayoutPatch[];
11
+ export declare function diffLayout(prev: LayoutSnapshot, next: LayoutSnapshot, path?: number[]): LayoutPatch[];
12
+ //# sourceMappingURL=diff-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-layout.d.ts","sourceRoot":"","sources":["../src/diff-layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAeD,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAsBrE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,GAAE,MAAM,EAAO,GAAG,WAAW,EAAE,CAoCzG"}
@@ -0,0 +1,77 @@
1
+ function sameLayoutScalar(a, b) {
2
+ if (Number.isNaN(a) && Number.isNaN(b))
3
+ return true;
4
+ return a === b;
5
+ }
6
+ function isFinitePatchNumber(value) {
7
+ return typeof value === 'number' && Number.isFinite(value);
8
+ }
9
+ function isNonNegativePatchDimension(value) {
10
+ return isFinitePatchNumber(value) && value >= 0;
11
+ }
12
+ export function coalescePatches(patches) {
13
+ const byPath = new Map();
14
+ const order = [];
15
+ for (const patch of patches) {
16
+ if (patch == null || !Array.isArray(patch.path))
17
+ continue;
18
+ let key;
19
+ try {
20
+ key = JSON.stringify(patch.path);
21
+ }
22
+ catch {
23
+ continue;
24
+ }
25
+ if (!byPath.has(key)) {
26
+ byPath.set(key, { path: [...patch.path] });
27
+ order.push(key);
28
+ }
29
+ const next = byPath.get(key);
30
+ if (isFinitePatchNumber(patch.x))
31
+ next.x = patch.x;
32
+ if (isFinitePatchNumber(patch.y))
33
+ next.y = patch.y;
34
+ if (isNonNegativePatchDimension(patch.width))
35
+ next.width = patch.width;
36
+ if (isNonNegativePatchDimension(patch.height))
37
+ next.height = patch.height;
38
+ }
39
+ return order.map(k => byPath.get(k));
40
+ }
41
+ export function diffLayout(prev, next, path = []) {
42
+ if (prev === next)
43
+ return [];
44
+ const patches = [];
45
+ const patch = { path };
46
+ let changed = false;
47
+ if (!sameLayoutScalar(prev.x, next.x)) {
48
+ patch.x = next.x;
49
+ changed = true;
50
+ }
51
+ if (!sameLayoutScalar(prev.y, next.y)) {
52
+ patch.y = next.y;
53
+ changed = true;
54
+ }
55
+ if (!sameLayoutScalar(prev.width, next.width)) {
56
+ patch.width = next.width;
57
+ changed = true;
58
+ }
59
+ if (!sameLayoutScalar(prev.height, next.height)) {
60
+ patch.height = next.height;
61
+ changed = true;
62
+ }
63
+ if (changed)
64
+ patches.push(patch);
65
+ const prevChildren = Array.isArray(prev.children) ? prev.children : [];
66
+ const nextChildren = Array.isArray(next.children) ? next.children : [];
67
+ const maxChildren = Math.max(prevChildren.length, nextChildren.length);
68
+ for (let i = 0; i < maxChildren; i++) {
69
+ const prevChild = prevChildren[i];
70
+ const nextChild = nextChildren[i];
71
+ if (prevChild && nextChild) {
72
+ patches.push(...diffLayout(prevChild, nextChild, [...path, i]));
73
+ }
74
+ }
75
+ return patches;
76
+ }
77
+ //# sourceMappingURL=diff-layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-layout.js","sourceRoot":"","sources":["../src/diff-layout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { Page } from 'playwright';
2
+ export type FileAttachStrategy = 'auto' | 'chooser' | 'hidden' | 'drop';
3
+ /**
4
+ * Resolve and validate paths on the machine running the proxy (not the agent host).
5
+ */
6
+ export declare function resolveExistingFiles(rawPaths: unknown[]): string[];
7
+ /**
8
+ * `strategy`:
9
+ * - `chooser` — requires x,y; opens native file chooser.
10
+ * - `hidden` — `setInputFiles({ force: true })` on every `input[type=file]` until one succeeds.
11
+ * - `drop` — requires dropX, dropY; synthetic drop with file payloads at coordinates.
12
+ * - `auto` — chooser if x,y; else hidden; if hidden fails, first visible file input without force.
13
+ */
14
+ export declare function attachFiles(page: Page, paths: string[], opts?: {
15
+ clickX?: number;
16
+ clickY?: number;
17
+ strategy?: FileAttachStrategy;
18
+ dropX?: number;
19
+ dropY?: number;
20
+ }): Promise<void>;
21
+ export interface SelectOptionPayload {
22
+ value?: string;
23
+ label?: string;
24
+ index?: number;
25
+ }
26
+ export declare function selectNativeOption(page: Page, x: number, y: number, opt: SelectOptionPayload): Promise<void>;
27
+ /**
28
+ * Custom listbox / combobox (ARIA): optional click to open, then `getByRole('option')`.
29
+ */
30
+ export declare function pickListboxOption(page: Page, label: string, opts?: {
31
+ exact?: boolean;
32
+ openX?: number;
33
+ openY?: number;
34
+ }): Promise<void>;
35
+ export declare function wheelAt(page: Page, deltaX: number, deltaY: number, x?: number, y?: number): Promise<void>;
36
+ //# sourceMappingURL=dom-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom-actions.d.ts","sourceRoot":"","sources":["../src/dom-actions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAMtC,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEvE;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAclE;AAiDD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,CAAC,EAAE;IACL,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,GACA,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0ClH;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACzD,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK/G"}
@@ -0,0 +1,172 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ function delay(ms) {
4
+ return new Promise(r => setTimeout(r, ms));
5
+ }
6
+ /**
7
+ * Resolve and validate paths on the machine running the proxy (not the agent host).
8
+ */
9
+ export function resolveExistingFiles(rawPaths) {
10
+ if (!Array.isArray(rawPaths) || rawPaths.length === 0) {
11
+ throw new Error('file: paths must be a non-empty array of strings');
12
+ }
13
+ const paths = [];
14
+ for (const p of rawPaths) {
15
+ if (typeof p !== 'string' || p.trim() === '')
16
+ continue;
17
+ paths.push(resolve(p));
18
+ }
19
+ if (paths.length === 0)
20
+ throw new Error('file: paths must contain at least one non-empty string');
21
+ for (const p of paths) {
22
+ if (!existsSync(p))
23
+ throw new Error(`file: path does not exist: ${p}`);
24
+ }
25
+ return paths;
26
+ }
27
+ async function attachHiddenInAllFrames(page, paths) {
28
+ for (const frame of page.frames()) {
29
+ const loc = frame.locator('input[type="file"]');
30
+ const n = await loc.count();
31
+ for (let i = 0; i < n; i++) {
32
+ try {
33
+ await loc.nth(i).setInputFiles(paths);
34
+ return true;
35
+ }
36
+ catch {
37
+ /* try next */
38
+ }
39
+ }
40
+ }
41
+ return false;
42
+ }
43
+ async function attachViaChooser(page, paths, clickX, clickY) {
44
+ const [chooser] = await Promise.all([
45
+ page.waitForEvent('filechooser', { timeout: 12_000 }),
46
+ page.mouse.click(clickX, clickY),
47
+ ]);
48
+ await chooser.setFiles(paths);
49
+ }
50
+ /** Synthetic drop at (x,y) using file bytes from the proxy host (targets elementFromPoint). */
51
+ async function attachViaDropPlaywright(page, paths, dropX, dropY) {
52
+ const fs = await import('node:fs/promises');
53
+ const buffers = await Promise.all(paths.map(p => fs.readFile(p)));
54
+ const names = paths.map(p => p.split(/[/\\\\]/).pop() ?? 'file');
55
+ await page.mouse.move(dropX, dropY);
56
+ await page.mainFrame().evaluate(({ bufs, ns, x, y }) => {
57
+ const dt = new DataTransfer();
58
+ for (let i = 0; i < bufs.length; i++) {
59
+ const u8 = new Uint8Array(bufs[i]);
60
+ const blob = new Blob([u8]);
61
+ dt.items.add(new File([blob], ns[i], { type: 'application/octet-stream' }));
62
+ }
63
+ const target = document.elementFromPoint(x, y) ?? document.body;
64
+ target.dispatchEvent(new DragEvent('drop', { bubbles: true, cancelable: true, clientX: x, clientY: y, dataTransfer: dt }));
65
+ }, { bufs: buffers.map(b => Array.from(b)), ns: names, x: dropX, y: dropY });
66
+ }
67
+ /**
68
+ * `strategy`:
69
+ * - `chooser` — requires x,y; opens native file chooser.
70
+ * - `hidden` — `setInputFiles({ force: true })` on every `input[type=file]` until one succeeds.
71
+ * - `drop` — requires dropX, dropY; synthetic drop with file payloads at coordinates.
72
+ * - `auto` — chooser if x,y; else hidden; if hidden fails, first visible file input without force.
73
+ */
74
+ export async function attachFiles(page, paths, opts) {
75
+ const strategy = opts?.strategy ?? 'auto';
76
+ const clickX = opts?.clickX;
77
+ const clickY = opts?.clickY;
78
+ const dropX = opts?.dropX;
79
+ const dropY = opts?.dropY;
80
+ if (strategy === 'chooser' || (strategy === 'auto' && clickX !== undefined && clickY !== undefined)) {
81
+ if (clickX === undefined || clickY === undefined) {
82
+ throw new Error('file: chooser strategy requires x,y click coordinates');
83
+ }
84
+ await attachViaChooser(page, paths, clickX, clickY);
85
+ return;
86
+ }
87
+ if (strategy === 'hidden' || strategy === 'auto') {
88
+ if (await attachHiddenInAllFrames(page, paths))
89
+ return;
90
+ if (strategy === 'hidden') {
91
+ throw new Error('file: hidden strategy could not set any input[type=file]');
92
+ }
93
+ }
94
+ if (strategy === 'drop' || (strategy === 'auto' && dropX !== undefined && dropY !== undefined)) {
95
+ if (dropX === undefined || dropY === undefined) {
96
+ throw new Error('file: drop strategy requires dropX, dropY');
97
+ }
98
+ await attachViaDropPlaywright(page, paths, dropX, dropY);
99
+ return;
100
+ }
101
+ for (const frame of page.frames()) {
102
+ const loc = frame.locator('input[type="file"]');
103
+ const n = await loc.count();
104
+ if (n > 0) {
105
+ await loc.first().setInputFiles(paths);
106
+ return;
107
+ }
108
+ }
109
+ throw new Error('file: no input[type=file] in any frame; pass x,y (chooser), dropX/dropY (drop), or strategy hidden');
110
+ }
111
+ export async function selectNativeOption(page, x, y, opt) {
112
+ if (opt.value === undefined && opt.label === undefined && opt.index === undefined) {
113
+ throw new Error('selectOption: provide at least one of value, label, or index');
114
+ }
115
+ await page.mouse.click(x, y);
116
+ await delay(40);
117
+ for (const frame of page.frames()) {
118
+ const applied = await frame.evaluate((payload) => {
119
+ const a = document.activeElement;
120
+ if (!a || a.tagName !== 'SELECT')
121
+ return false;
122
+ const sel = a;
123
+ if (typeof payload.index === 'number' && Number.isFinite(payload.index)) {
124
+ const i = Math.trunc(payload.index);
125
+ if (i < 0 || i >= sel.options.length)
126
+ return false;
127
+ sel.selectedIndex = i;
128
+ }
129
+ else if (payload.value !== null && payload.value !== undefined) {
130
+ sel.value = payload.value;
131
+ }
132
+ else if (payload.label !== null && payload.label !== undefined) {
133
+ const optEl = Array.from(sel.options).find(o => o.text.trim() === payload.label || o.text.includes(payload.label));
134
+ if (!optEl)
135
+ return false;
136
+ sel.value = optEl.value;
137
+ }
138
+ else {
139
+ return false;
140
+ }
141
+ sel.dispatchEvent(new Event('input', { bubbles: true }));
142
+ sel.dispatchEvent(new Event('change', { bubbles: true }));
143
+ return true;
144
+ }, {
145
+ value: opt.value ?? null,
146
+ label: opt.label ?? null,
147
+ index: typeof opt.index === 'number' && Number.isFinite(opt.index) ? opt.index : undefined,
148
+ });
149
+ if (applied)
150
+ return;
151
+ }
152
+ throw new Error('selectOption: no focused <select> after click — use geometra_pick_listbox_option for custom dropdowns');
153
+ }
154
+ /**
155
+ * Custom listbox / combobox (ARIA): optional click to open, then `getByRole('option')`.
156
+ */
157
+ export async function pickListboxOption(page, label, opts) {
158
+ if (opts?.openX !== undefined && opts?.openY !== undefined) {
159
+ await page.mouse.click(opts.openX, opts.openY);
160
+ await delay(120);
161
+ }
162
+ const opt = page.getByRole('option', { name: label, exact: opts?.exact ?? false }).first();
163
+ await opt.waitFor({ state: 'visible', timeout: 8000 });
164
+ await opt.click();
165
+ }
166
+ export async function wheelAt(page, deltaX, deltaY, x, y) {
167
+ if (x !== undefined && y !== undefined && Number.isFinite(x) && Number.isFinite(y)) {
168
+ await page.mouse.move(x, y);
169
+ }
170
+ await page.mouse.wheel(deltaX, deltaY);
171
+ }
172
+ //# sourceMappingURL=dom-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom-actions.js","sourceRoot":"","sources":["../src/dom-actions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Frame, Page } from 'playwright';
2
+ import type { GeometrySnapshot, LayoutSnapshot, TreeSnapshot } from './types.js';
3
+ export declare function extractFrameGeometry(frame: Frame): Promise<GeometrySnapshot>;
4
+ export declare function mergeAllIframes(tree: TreeSnapshot, layout: LayoutSnapshot, ownerFrame: Frame): Promise<void>;
5
+ /**
6
+ * Full page: main frame + every nested iframe (any origin) + optional CDP AX name enrichment.
7
+ */
8
+ export declare function extractGeometry(page: Page): Promise<GeometrySnapshot>;
9
+ //# sourceMappingURL=extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../src/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAI7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAyShF,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC,CASlF;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,YAAY,EAClB,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,KAAK,GAChB,OAAO,CAAC,IAAI,CAAC,CA8Bf;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAS3E"}