@metamask/device-mcp 0.1.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/CHANGELOG.md +34 -0
- package/README.md +266 -0
- package/dist/backends/adb-backend.cjs +442 -0
- package/dist/backends/adb-backend.cjs.map +1 -0
- package/dist/backends/adb-backend.d.cts +35 -0
- package/dist/backends/adb-backend.d.cts.map +1 -0
- package/dist/backends/adb-backend.d.mts +35 -0
- package/dist/backends/adb-backend.d.mts.map +1 -0
- package/dist/backends/adb-backend.mjs +436 -0
- package/dist/backends/adb-backend.mjs.map +1 -0
- package/dist/backends/appium-backend.cjs +518 -0
- package/dist/backends/appium-backend.cjs.map +1 -0
- package/dist/backends/appium-backend.d.cts +36 -0
- package/dist/backends/appium-backend.d.cts.map +1 -0
- package/dist/backends/appium-backend.d.mts +36 -0
- package/dist/backends/appium-backend.d.mts.map +1 -0
- package/dist/backends/appium-backend.mjs +512 -0
- package/dist/backends/appium-backend.mjs.map +1 -0
- package/dist/backends/idb-backend.cjs +442 -0
- package/dist/backends/idb-backend.cjs.map +1 -0
- package/dist/backends/idb-backend.d.cts +35 -0
- package/dist/backends/idb-backend.d.cts.map +1 -0
- package/dist/backends/idb-backend.d.mts +35 -0
- package/dist/backends/idb-backend.d.mts.map +1 -0
- package/dist/backends/idb-backend.mjs +436 -0
- package/dist/backends/idb-backend.mjs.map +1 -0
- package/dist/backends/index.cjs +134 -0
- package/dist/backends/index.cjs.map +1 -0
- package/dist/backends/index.d.cts +16 -0
- package/dist/backends/index.d.cts.map +1 -0
- package/dist/backends/index.d.mts +16 -0
- package/dist/backends/index.d.mts.map +1 -0
- package/dist/backends/index.mjs +126 -0
- package/dist/backends/index.mjs.map +1 -0
- package/dist/backends/session-file.cjs +62 -0
- package/dist/backends/session-file.cjs.map +1 -0
- package/dist/backends/session-file.d.cts +24 -0
- package/dist/backends/session-file.d.cts.map +1 -0
- package/dist/backends/session-file.d.mts +24 -0
- package/dist/backends/session-file.d.mts.map +1 -0
- package/dist/backends/session-file.mjs +59 -0
- package/dist/backends/session-file.mjs.map +1 -0
- package/dist/backends/types.cjs +3 -0
- package/dist/backends/types.cjs.map +1 -0
- package/dist/backends/types.d.cts +93 -0
- package/dist/backends/types.d.cts.map +1 -0
- package/dist/backends/types.d.mts +93 -0
- package/dist/backends/types.d.mts.map +1 -0
- package/dist/backends/types.mjs +2 -0
- package/dist/backends/types.mjs.map +1 -0
- package/dist/backends/webdriver-client.cjs +167 -0
- package/dist/backends/webdriver-client.cjs.map +1 -0
- package/dist/backends/webdriver-client.d.cts +55 -0
- package/dist/backends/webdriver-client.d.cts.map +1 -0
- package/dist/backends/webdriver-client.d.mts +55 -0
- package/dist/backends/webdriver-client.d.mts.map +1 -0
- package/dist/backends/webdriver-client.mjs +162 -0
- package/dist/backends/webdriver-client.mjs.map +1 -0
- package/dist/cli/device-mcp.cjs +20 -0
- package/dist/cli/device-mcp.cjs.map +1 -0
- package/dist/cli/device-mcp.d.cts +3 -0
- package/dist/cli/device-mcp.d.cts.map +1 -0
- package/dist/cli/device-mcp.d.mts +3 -0
- package/dist/cli/device-mcp.d.mts.map +1 -0
- package/dist/cli/device-mcp.mjs +18 -0
- package/dist/cli/device-mcp.mjs.map +1 -0
- package/dist/index.cjs +9 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server.cjs +43 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +4 -0
- package/dist/server.d.cts.map +1 -0
- package/dist/server.d.mts +4 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +40 -0
- package/dist/server.mjs.map +1 -0
- package/dist/tools/alert-text.cjs +25 -0
- package/dist/tools/alert-text.cjs.map +1 -0
- package/dist/tools/alert-text.d.cts +4 -0
- package/dist/tools/alert-text.d.cts.map +1 -0
- package/dist/tools/alert-text.d.mts +4 -0
- package/dist/tools/alert-text.d.mts.map +1 -0
- package/dist/tools/alert-text.mjs +22 -0
- package/dist/tools/alert-text.mjs.map +1 -0
- package/dist/tools/app-state.cjs +40 -0
- package/dist/tools/app-state.cjs.map +1 -0
- package/dist/tools/app-state.d.cts +4 -0
- package/dist/tools/app-state.d.cts.map +1 -0
- package/dist/tools/app-state.d.mts +4 -0
- package/dist/tools/app-state.d.mts.map +1 -0
- package/dist/tools/app-state.mjs +37 -0
- package/dist/tools/app-state.mjs.map +1 -0
- package/dist/tools/clipboard.cjs +42 -0
- package/dist/tools/clipboard.cjs.map +1 -0
- package/dist/tools/clipboard.d.cts +4 -0
- package/dist/tools/clipboard.d.cts.map +1 -0
- package/dist/tools/clipboard.d.mts +4 -0
- package/dist/tools/clipboard.d.mts.map +1 -0
- package/dist/tools/clipboard.mjs +39 -0
- package/dist/tools/clipboard.mjs.map +1 -0
- package/dist/tools/close-app.cjs +25 -0
- package/dist/tools/close-app.cjs.map +1 -0
- package/dist/tools/close-app.d.cts +4 -0
- package/dist/tools/close-app.d.cts.map +1 -0
- package/dist/tools/close-app.d.mts +4 -0
- package/dist/tools/close-app.d.mts.map +1 -0
- package/dist/tools/close-app.mjs +22 -0
- package/dist/tools/close-app.mjs.map +1 -0
- package/dist/tools/context.cjs +52 -0
- package/dist/tools/context.cjs.map +1 -0
- package/dist/tools/context.d.cts +4 -0
- package/dist/tools/context.d.cts.map +1 -0
- package/dist/tools/context.d.mts +4 -0
- package/dist/tools/context.d.mts.map +1 -0
- package/dist/tools/context.mjs +49 -0
- package/dist/tools/context.mjs.map +1 -0
- package/dist/tools/device-info.cjs +34 -0
- package/dist/tools/device-info.cjs.map +1 -0
- package/dist/tools/device-info.d.cts +4 -0
- package/dist/tools/device-info.d.cts.map +1 -0
- package/dist/tools/device-info.d.mts +4 -0
- package/dist/tools/device-info.d.mts.map +1 -0
- package/dist/tools/device-info.mjs +31 -0
- package/dist/tools/device-info.mjs.map +1 -0
- package/dist/tools/dismiss-alert.cjs +34 -0
- package/dist/tools/dismiss-alert.cjs.map +1 -0
- package/dist/tools/dismiss-alert.d.cts +4 -0
- package/dist/tools/dismiss-alert.d.cts.map +1 -0
- package/dist/tools/dismiss-alert.d.mts +4 -0
- package/dist/tools/dismiss-alert.d.mts.map +1 -0
- package/dist/tools/dismiss-alert.mjs +31 -0
- package/dist/tools/dismiss-alert.mjs.map +1 -0
- package/dist/tools/dismiss-keyboard.cjs +23 -0
- package/dist/tools/dismiss-keyboard.cjs.map +1 -0
- package/dist/tools/dismiss-keyboard.d.cts +4 -0
- package/dist/tools/dismiss-keyboard.d.cts.map +1 -0
- package/dist/tools/dismiss-keyboard.d.mts +4 -0
- package/dist/tools/dismiss-keyboard.d.mts.map +1 -0
- package/dist/tools/dismiss-keyboard.mjs +20 -0
- package/dist/tools/dismiss-keyboard.mjs.map +1 -0
- package/dist/tools/generate-locators.cjs +33 -0
- package/dist/tools/generate-locators.cjs.map +1 -0
- package/dist/tools/generate-locators.d.cts +4 -0
- package/dist/tools/generate-locators.d.cts.map +1 -0
- package/dist/tools/generate-locators.d.mts +4 -0
- package/dist/tools/generate-locators.d.mts.map +1 -0
- package/dist/tools/generate-locators.mjs +30 -0
- package/dist/tools/generate-locators.mjs.map +1 -0
- package/dist/tools/index.cjs +50 -0
- package/dist/tools/index.cjs.map +1 -0
- package/dist/tools/index.d.cts +24 -0
- package/dist/tools/index.d.cts.map +1 -0
- package/dist/tools/index.d.mts +24 -0
- package/dist/tools/index.d.mts.map +1 -0
- package/dist/tools/index.mjs +24 -0
- package/dist/tools/index.mjs.map +1 -0
- package/dist/tools/logs.cjs +48 -0
- package/dist/tools/logs.cjs.map +1 -0
- package/dist/tools/logs.d.cts +4 -0
- package/dist/tools/logs.d.cts.map +1 -0
- package/dist/tools/logs.d.mts +4 -0
- package/dist/tools/logs.d.mts.map +1 -0
- package/dist/tools/logs.mjs +45 -0
- package/dist/tools/logs.mjs.map +1 -0
- package/dist/tools/long-press.cjs +38 -0
- package/dist/tools/long-press.cjs.map +1 -0
- package/dist/tools/long-press.d.cts +4 -0
- package/dist/tools/long-press.d.cts.map +1 -0
- package/dist/tools/long-press.d.mts +4 -0
- package/dist/tools/long-press.d.mts.map +1 -0
- package/dist/tools/long-press.mjs +35 -0
- package/dist/tools/long-press.mjs.map +1 -0
- package/dist/tools/open-app.cjs +26 -0
- package/dist/tools/open-app.cjs.map +1 -0
- package/dist/tools/open-app.d.cts +4 -0
- package/dist/tools/open-app.d.cts.map +1 -0
- package/dist/tools/open-app.d.mts +4 -0
- package/dist/tools/open-app.d.mts.map +1 -0
- package/dist/tools/open-app.mjs +23 -0
- package/dist/tools/open-app.mjs.map +1 -0
- package/dist/tools/press-button.cjs +30 -0
- package/dist/tools/press-button.cjs.map +1 -0
- package/dist/tools/press-button.d.cts +4 -0
- package/dist/tools/press-button.d.cts.map +1 -0
- package/dist/tools/press-button.d.mts +4 -0
- package/dist/tools/press-button.d.mts.map +1 -0
- package/dist/tools/press-button.mjs +27 -0
- package/dist/tools/press-button.mjs.map +1 -0
- package/dist/tools/screen-recording.cjs +46 -0
- package/dist/tools/screen-recording.cjs.map +1 -0
- package/dist/tools/screen-recording.d.cts +4 -0
- package/dist/tools/screen-recording.d.cts.map +1 -0
- package/dist/tools/screen-recording.d.mts +4 -0
- package/dist/tools/screen-recording.d.mts.map +1 -0
- package/dist/tools/screen-recording.mjs +43 -0
- package/dist/tools/screen-recording.mjs.map +1 -0
- package/dist/tools/screenshot.cjs +42 -0
- package/dist/tools/screenshot.cjs.map +1 -0
- package/dist/tools/screenshot.d.cts +4 -0
- package/dist/tools/screenshot.d.cts.map +1 -0
- package/dist/tools/screenshot.d.mts +4 -0
- package/dist/tools/screenshot.d.mts.map +1 -0
- package/dist/tools/screenshot.mjs +39 -0
- package/dist/tools/screenshot.mjs.map +1 -0
- package/dist/tools/scroll-to-element.cjs +56 -0
- package/dist/tools/scroll-to-element.cjs.map +1 -0
- package/dist/tools/scroll-to-element.d.cts +4 -0
- package/dist/tools/scroll-to-element.d.cts.map +1 -0
- package/dist/tools/scroll-to-element.d.mts +4 -0
- package/dist/tools/scroll-to-element.d.mts.map +1 -0
- package/dist/tools/scroll-to-element.mjs +53 -0
- package/dist/tools/scroll-to-element.mjs.map +1 -0
- package/dist/tools/shared.cjs +11 -0
- package/dist/tools/shared.cjs.map +1 -0
- package/dist/tools/shared.d.cts +8 -0
- package/dist/tools/shared.d.cts.map +1 -0
- package/dist/tools/shared.d.mts +8 -0
- package/dist/tools/shared.d.mts.map +1 -0
- package/dist/tools/shared.mjs +8 -0
- package/dist/tools/shared.mjs.map +1 -0
- package/dist/tools/snapshot.cjs +37 -0
- package/dist/tools/snapshot.cjs.map +1 -0
- package/dist/tools/snapshot.d.cts +4 -0
- package/dist/tools/snapshot.d.cts.map +1 -0
- package/dist/tools/snapshot.d.mts +4 -0
- package/dist/tools/snapshot.d.mts.map +1 -0
- package/dist/tools/snapshot.mjs +34 -0
- package/dist/tools/snapshot.mjs.map +1 -0
- package/dist/tools/swipe.cjs +39 -0
- package/dist/tools/swipe.cjs.map +1 -0
- package/dist/tools/swipe.d.cts +4 -0
- package/dist/tools/swipe.d.cts.map +1 -0
- package/dist/tools/swipe.d.mts +4 -0
- package/dist/tools/swipe.d.mts.map +1 -0
- package/dist/tools/swipe.mjs +36 -0
- package/dist/tools/swipe.mjs.map +1 -0
- package/dist/tools/tap-coordinates.cjs +27 -0
- package/dist/tools/tap-coordinates.cjs.map +1 -0
- package/dist/tools/tap-coordinates.d.cts +4 -0
- package/dist/tools/tap-coordinates.d.cts.map +1 -0
- package/dist/tools/tap-coordinates.d.mts +4 -0
- package/dist/tools/tap-coordinates.d.mts.map +1 -0
- package/dist/tools/tap-coordinates.mjs +24 -0
- package/dist/tools/tap-coordinates.mjs.map +1 -0
- package/dist/tools/tap-element.cjs +46 -0
- package/dist/tools/tap-element.cjs.map +1 -0
- package/dist/tools/tap-element.d.cts +4 -0
- package/dist/tools/tap-element.d.cts.map +1 -0
- package/dist/tools/tap-element.d.mts +4 -0
- package/dist/tools/tap-element.d.mts.map +1 -0
- package/dist/tools/tap-element.mjs +43 -0
- package/dist/tools/tap-element.mjs.map +1 -0
- package/dist/tools/type-text.cjs +32 -0
- package/dist/tools/type-text.cjs.map +1 -0
- package/dist/tools/type-text.d.cts +4 -0
- package/dist/tools/type-text.d.cts.map +1 -0
- package/dist/tools/type-text.d.mts +4 -0
- package/dist/tools/type-text.d.mts.map +1 -0
- package/dist/tools/type-text.mjs +29 -0
- package/dist/tools/type-text.mjs.map +1 -0
- package/dist/tools/wait-for.cjs +51 -0
- package/dist/tools/wait-for.cjs.map +1 -0
- package/dist/tools/wait-for.d.cts +4 -0
- package/dist/tools/wait-for.d.cts.map +1 -0
- package/dist/tools/wait-for.d.mts +4 -0
- package/dist/tools/wait-for.d.mts.map +1 -0
- package/dist/tools/wait-for.mjs +48 -0
- package/dist/tools/wait-for.mjs.map +1 -0
- package/dist/tools/window-size.cjs +29 -0
- package/dist/tools/window-size.cjs.map +1 -0
- package/dist/tools/window-size.d.cts +4 -0
- package/dist/tools/window-size.d.cts.map +1 -0
- package/dist/tools/window-size.d.mts +4 -0
- package/dist/tools/window-size.d.mts.map +1 -0
- package/dist/tools/window-size.mjs +26 -0
- package/dist/tools/window-size.mjs.map +1 -0
- package/dist/utils/alert-labels.cjs +26 -0
- package/dist/utils/alert-labels.cjs.map +1 -0
- package/dist/utils/alert-labels.d.cts +3 -0
- package/dist/utils/alert-labels.d.cts.map +1 -0
- package/dist/utils/alert-labels.d.mts +3 -0
- package/dist/utils/alert-labels.d.mts.map +1 -0
- package/dist/utils/alert-labels.mjs +23 -0
- package/dist/utils/alert-labels.mjs.map +1 -0
- package/dist/utils/element.cjs +212 -0
- package/dist/utils/element.cjs.map +1 -0
- package/dist/utils/element.d.cts +24 -0
- package/dist/utils/element.d.cts.map +1 -0
- package/dist/utils/element.d.mts +24 -0
- package/dist/utils/element.d.mts.map +1 -0
- package/dist/utils/element.mjs +203 -0
- package/dist/utils/element.mjs.map +1 -0
- package/dist/utils/exec.cjs +57 -0
- package/dist/utils/exec.cjs.map +1 -0
- package/dist/utils/exec.d.cts +23 -0
- package/dist/utils/exec.d.cts.map +1 -0
- package/dist/utils/exec.d.mts +23 -0
- package/dist/utils/exec.d.mts.map +1 -0
- package/dist/utils/exec.mjs +52 -0
- package/dist/utils/exec.mjs.map +1 -0
- package/dist/utils/platform.cjs +93 -0
- package/dist/utils/platform.cjs.map +1 -0
- package/dist/utils/platform.d.cts +8 -0
- package/dist/utils/platform.d.cts.map +1 -0
- package/dist/utils/platform.d.mts +8 -0
- package/dist/utils/platform.d.mts.map +1 -0
- package/dist/utils/platform.mjs +90 -0
- package/dist/utils/platform.mjs.map +1 -0
- package/package.json +106 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findElement = findElement;
|
|
4
|
+
exports.matchesQuery = matchesQuery;
|
|
5
|
+
exports.describeElement = describeElement;
|
|
6
|
+
exports.computeSwipeEnd = computeSwipeEnd;
|
|
7
|
+
exports.generateLocators = generateLocators;
|
|
8
|
+
exports.formatLocators = formatLocators;
|
|
9
|
+
exports.formatHierarchy = formatHierarchy;
|
|
10
|
+
function findElement(elements, query) {
|
|
11
|
+
for (const el of elements) {
|
|
12
|
+
if (matchesQuery(el, query)) {
|
|
13
|
+
return el;
|
|
14
|
+
}
|
|
15
|
+
if (el.children) {
|
|
16
|
+
const found = findElement(el.children, query);
|
|
17
|
+
if (found) {
|
|
18
|
+
return found;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
function matchesQuery(element, query) {
|
|
25
|
+
if (query.identifier && !fuzzyMatch(element.identifier, query.identifier)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (query.label && !fuzzyMatch(element.label, query.label)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (query.text &&
|
|
32
|
+
!fuzzyMatch(element.value, query.text) &&
|
|
33
|
+
!fuzzyMatch(element.label, query.text)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (query.type && !fuzzyMatch(element.type, query.type)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const hasAtLeastOneFilter = query.identifier ?? query.label ?? query.text ?? query.type;
|
|
40
|
+
return Boolean(hasAtLeastOneFilter);
|
|
41
|
+
}
|
|
42
|
+
function fuzzyMatch(actual, expected) {
|
|
43
|
+
if (!actual) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
if (actual === expected) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
if (actual.toLowerCase() === expected.toLowerCase()) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return actual.toLowerCase().includes(expected.toLowerCase());
|
|
53
|
+
}
|
|
54
|
+
function describeElement(element) {
|
|
55
|
+
const parts = [element.type];
|
|
56
|
+
if (element.label) {
|
|
57
|
+
parts.push(`label="${element.label}"`);
|
|
58
|
+
}
|
|
59
|
+
if (element.identifier) {
|
|
60
|
+
parts.push(`id="${element.identifier}"`);
|
|
61
|
+
}
|
|
62
|
+
if (element.value) {
|
|
63
|
+
parts.push(`value="${element.value}"`);
|
|
64
|
+
}
|
|
65
|
+
return parts.join(' ');
|
|
66
|
+
}
|
|
67
|
+
function computeSwipeEnd(startX, startY, direction, distance) {
|
|
68
|
+
switch (direction) {
|
|
69
|
+
case 'up':
|
|
70
|
+
return [startX, startY - distance];
|
|
71
|
+
case 'down':
|
|
72
|
+
return [startX, startY + distance];
|
|
73
|
+
case 'left':
|
|
74
|
+
return [startX - distance, startY];
|
|
75
|
+
case 'right':
|
|
76
|
+
return [startX + distance, startY];
|
|
77
|
+
default:
|
|
78
|
+
return [startX, startY];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const INTERACTIVE_TYPES = new Set([
|
|
82
|
+
'Button',
|
|
83
|
+
'TextField',
|
|
84
|
+
'SecureTextField',
|
|
85
|
+
'Switch',
|
|
86
|
+
'Slider',
|
|
87
|
+
'Picker',
|
|
88
|
+
'Link',
|
|
89
|
+
'SearchField',
|
|
90
|
+
'TextArea',
|
|
91
|
+
'Tab',
|
|
92
|
+
'MenuItem',
|
|
93
|
+
'Cell',
|
|
94
|
+
'android.widget.Button',
|
|
95
|
+
'android.widget.EditText',
|
|
96
|
+
'android.widget.CheckBox',
|
|
97
|
+
'android.widget.RadioButton',
|
|
98
|
+
'android.widget.Switch',
|
|
99
|
+
'android.widget.ToggleButton',
|
|
100
|
+
'android.widget.Spinner',
|
|
101
|
+
'android.widget.SeekBar',
|
|
102
|
+
'android.widget.ImageButton',
|
|
103
|
+
]);
|
|
104
|
+
function isInteractive(element) {
|
|
105
|
+
if (INTERACTIVE_TYPES.has(element.type)) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
for (const t of INTERACTIVE_TYPES) {
|
|
109
|
+
if (element.type.includes(t)) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return element.enabled && Boolean(element.identifier);
|
|
114
|
+
}
|
|
115
|
+
function buildSuggestions(element) {
|
|
116
|
+
const suggestions = [];
|
|
117
|
+
if (element.identifier) {
|
|
118
|
+
suggestions.push({
|
|
119
|
+
strategy: 'identifier',
|
|
120
|
+
value: element.identifier,
|
|
121
|
+
confidence: 'high',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (element.label) {
|
|
125
|
+
suggestions.push({
|
|
126
|
+
strategy: 'label',
|
|
127
|
+
value: element.label,
|
|
128
|
+
confidence: element.identifier ? 'medium' : 'high',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (element.value && element.value !== element.label) {
|
|
132
|
+
suggestions.push({
|
|
133
|
+
strategy: 'text',
|
|
134
|
+
value: element.value,
|
|
135
|
+
confidence: 'low',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (suggestions.length === 0) {
|
|
139
|
+
suggestions.push({
|
|
140
|
+
strategy: 'type',
|
|
141
|
+
value: element.type,
|
|
142
|
+
confidence: 'low',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return suggestions;
|
|
146
|
+
}
|
|
147
|
+
function generateLocators(elements) {
|
|
148
|
+
const results = [];
|
|
149
|
+
collectLocators(elements, results);
|
|
150
|
+
return results;
|
|
151
|
+
}
|
|
152
|
+
function collectLocators(elements, results) {
|
|
153
|
+
for (const el of elements) {
|
|
154
|
+
if (isInteractive(el)) {
|
|
155
|
+
const suggestions = buildSuggestions(el);
|
|
156
|
+
results.push({
|
|
157
|
+
description: describeElement(el),
|
|
158
|
+
frame: el.frame,
|
|
159
|
+
suggestions,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if (el.children) {
|
|
163
|
+
collectLocators(el.children, results);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function formatLocators(locators) {
|
|
168
|
+
if (locators.length === 0) {
|
|
169
|
+
return 'No interactive elements found on screen.';
|
|
170
|
+
}
|
|
171
|
+
const confidenceIcon = {
|
|
172
|
+
high: '***',
|
|
173
|
+
medium: '**',
|
|
174
|
+
low: '*',
|
|
175
|
+
};
|
|
176
|
+
return locators
|
|
177
|
+
.map((loc, i) => {
|
|
178
|
+
const header = `${i + 1}. ${loc.description} (${loc.frame.x},${loc.frame.y} ${loc.frame.width}x${loc.frame.height})`;
|
|
179
|
+
const strategies = loc.suggestions
|
|
180
|
+
.map((s) => ` ${confidenceIcon[s.confidence]} { ${s.strategy}: "${s.value}" }`)
|
|
181
|
+
.join('\n');
|
|
182
|
+
return `${header}\n${strategies}`;
|
|
183
|
+
})
|
|
184
|
+
.join('\n\n');
|
|
185
|
+
}
|
|
186
|
+
function formatHierarchy(elements, depth) {
|
|
187
|
+
const indent = ' '.repeat(depth);
|
|
188
|
+
return elements
|
|
189
|
+
.map((el) => {
|
|
190
|
+
const parts = [`${indent}[${el.type}]`];
|
|
191
|
+
if (el.label) {
|
|
192
|
+
parts.push(`label="${el.label}"`);
|
|
193
|
+
}
|
|
194
|
+
if (el.identifier) {
|
|
195
|
+
parts.push(`id="${el.identifier}"`);
|
|
196
|
+
}
|
|
197
|
+
if (el.value) {
|
|
198
|
+
parts.push(`value="${el.value}"`);
|
|
199
|
+
}
|
|
200
|
+
parts.push(`(${el.frame.x},${el.frame.y} ${el.frame.width}x${el.frame.height})`);
|
|
201
|
+
if (!el.enabled) {
|
|
202
|
+
parts.push('DISABLED');
|
|
203
|
+
}
|
|
204
|
+
let result = parts.join(' ');
|
|
205
|
+
if (el.children?.length) {
|
|
206
|
+
result += `\n${formatHierarchy(el.children, depth + 1)}`;
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
})
|
|
210
|
+
.join('\n');
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=element.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element.cjs","sourceRoot":"","sources":["../../src/utils/element.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UIElement, ElementQuery } from '../backends/types.js';\n\nexport function findElement(\n elements: UIElement[],\n query: ElementQuery,\n): UIElement | null {\n for (const el of elements) {\n if (matchesQuery(el, query)) {\n return el;\n }\n if (el.children) {\n const found = findElement(el.children, query);\n if (found) {\n return found;\n }\n }\n }\n return null;\n}\n\nexport function matchesQuery(element: UIElement, query: ElementQuery): boolean {\n if (query.identifier && !fuzzyMatch(element.identifier, query.identifier)) {\n return false;\n }\n if (query.label && !fuzzyMatch(element.label, query.label)) {\n return false;\n }\n if (\n query.text &&\n !fuzzyMatch(element.value, query.text) &&\n !fuzzyMatch(element.label, query.text)\n ) {\n return false;\n }\n if (query.type && !fuzzyMatch(element.type, query.type)) {\n return false;\n }\n\n const hasAtLeastOneFilter =\n query.identifier ?? query.label ?? query.text ?? query.type;\n return Boolean(hasAtLeastOneFilter);\n}\n\nfunction fuzzyMatch(actual: string | undefined, expected: string): boolean {\n if (!actual) {\n return false;\n }\n if (actual === expected) {\n return true;\n }\n if (actual.toLowerCase() === expected.toLowerCase()) {\n return true;\n }\n return actual.toLowerCase().includes(expected.toLowerCase());\n}\n\nexport function describeElement(element: UIElement): string {\n const parts: string[] = [element.type];\n if (element.label) {\n parts.push(`label=\"${element.label}\"`);\n }\n if (element.identifier) {\n parts.push(`id=\"${element.identifier}\"`);\n }\n if (element.value) {\n parts.push(`value=\"${element.value}\"`);\n }\n return parts.join(' ');\n}\n\nexport function computeSwipeEnd(\n startX: number,\n startY: number,\n direction: 'up' | 'down' | 'left' | 'right',\n distance: number,\n): [number, number] {\n switch (direction) {\n case 'up':\n return [startX, startY - distance];\n case 'down':\n return [startX, startY + distance];\n case 'left':\n return [startX - distance, startY];\n case 'right':\n return [startX + distance, startY];\n default:\n return [startX, startY];\n }\n}\n\nexport type LocatorSuggestion = {\n strategy: 'identifier' | 'label' | 'text' | 'type';\n value: string;\n confidence: 'high' | 'medium' | 'low';\n};\n\nexport type ElementLocator = {\n description: string;\n frame: { x: number; y: number; width: number; height: number };\n suggestions: LocatorSuggestion[];\n};\n\nconst INTERACTIVE_TYPES = new Set([\n 'Button',\n 'TextField',\n 'SecureTextField',\n 'Switch',\n 'Slider',\n 'Picker',\n 'Link',\n 'SearchField',\n 'TextArea',\n 'Tab',\n 'MenuItem',\n 'Cell',\n 'android.widget.Button',\n 'android.widget.EditText',\n 'android.widget.CheckBox',\n 'android.widget.RadioButton',\n 'android.widget.Switch',\n 'android.widget.ToggleButton',\n 'android.widget.Spinner',\n 'android.widget.SeekBar',\n 'android.widget.ImageButton',\n]);\n\nfunction isInteractive(element: UIElement): boolean {\n if (INTERACTIVE_TYPES.has(element.type)) {\n return true;\n }\n for (const t of INTERACTIVE_TYPES) {\n if (element.type.includes(t)) {\n return true;\n }\n }\n return element.enabled && Boolean(element.identifier);\n}\n\nfunction buildSuggestions(element: UIElement): LocatorSuggestion[] {\n const suggestions: LocatorSuggestion[] = [];\n\n if (element.identifier) {\n suggestions.push({\n strategy: 'identifier',\n value: element.identifier,\n confidence: 'high',\n });\n }\n\n if (element.label) {\n suggestions.push({\n strategy: 'label',\n value: element.label,\n confidence: element.identifier ? 'medium' : 'high',\n });\n }\n\n if (element.value && element.value !== element.label) {\n suggestions.push({\n strategy: 'text',\n value: element.value,\n confidence: 'low',\n });\n }\n\n if (suggestions.length === 0) {\n suggestions.push({\n strategy: 'type',\n value: element.type,\n confidence: 'low',\n });\n }\n\n return suggestions;\n}\n\nexport function generateLocators(elements: UIElement[]): ElementLocator[] {\n const results: ElementLocator[] = [];\n collectLocators(elements, results);\n return results;\n}\n\nfunction collectLocators(\n elements: UIElement[],\n results: ElementLocator[],\n): void {\n for (const el of elements) {\n if (isInteractive(el)) {\n const suggestions = buildSuggestions(el);\n results.push({\n description: describeElement(el),\n frame: el.frame,\n suggestions,\n });\n }\n if (el.children) {\n collectLocators(el.children, results);\n }\n }\n}\n\nexport function formatLocators(locators: ElementLocator[]): string {\n if (locators.length === 0) {\n return 'No interactive elements found on screen.';\n }\n\n const confidenceIcon: Record<string, string> = {\n high: '***',\n medium: '**',\n low: '*',\n };\n\n return locators\n .map((loc, i) => {\n const header = `${i + 1}. ${loc.description} (${loc.frame.x},${loc.frame.y} ${loc.frame.width}x${loc.frame.height})`;\n const strategies = loc.suggestions\n .map(\n (s) =>\n ` ${confidenceIcon[s.confidence]} { ${s.strategy}: \"${s.value}\" }`,\n )\n .join('\\n');\n return `${header}\\n${strategies}`;\n })\n .join('\\n\\n');\n}\n\nexport function formatHierarchy(elements: UIElement[], depth: number): string {\n const indent = ' '.repeat(depth);\n return elements\n .map((el) => {\n const parts = [`${indent}[${el.type}]`];\n if (el.label) {\n parts.push(`label=\"${el.label}\"`);\n }\n if (el.identifier) {\n parts.push(`id=\"${el.identifier}\"`);\n }\n if (el.value) {\n parts.push(`value=\"${el.value}\"`);\n }\n parts.push(\n `(${el.frame.x},${el.frame.y} ${el.frame.width}x${el.frame.height})`,\n );\n if (!el.enabled) {\n parts.push('DISABLED');\n }\n\n let result = parts.join(' ');\n if (el.children?.length) {\n result += `\\n${formatHierarchy(el.children, depth + 1)}`;\n }\n return result;\n })\n .join('\\n');\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { UIElement, ElementQuery } from "../backends/types.cjs";
|
|
2
|
+
export declare function findElement(elements: UIElement[], query: ElementQuery): UIElement | null;
|
|
3
|
+
export declare function matchesQuery(element: UIElement, query: ElementQuery): boolean;
|
|
4
|
+
export declare function describeElement(element: UIElement): string;
|
|
5
|
+
export declare function computeSwipeEnd(startX: number, startY: number, direction: 'up' | 'down' | 'left' | 'right', distance: number): [number, number];
|
|
6
|
+
export type LocatorSuggestion = {
|
|
7
|
+
strategy: 'identifier' | 'label' | 'text' | 'type';
|
|
8
|
+
value: string;
|
|
9
|
+
confidence: 'high' | 'medium' | 'low';
|
|
10
|
+
};
|
|
11
|
+
export type ElementLocator = {
|
|
12
|
+
description: string;
|
|
13
|
+
frame: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
};
|
|
19
|
+
suggestions: LocatorSuggestion[];
|
|
20
|
+
};
|
|
21
|
+
export declare function generateLocators(elements: UIElement[]): ElementLocator[];
|
|
22
|
+
export declare function formatLocators(locators: ElementLocator[]): string;
|
|
23
|
+
export declare function formatHierarchy(elements: UIElement[], depth: number): string;
|
|
24
|
+
//# sourceMappingURL=element.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element.d.cts","sourceRoot":"","sources":["../../src/utils/element.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,8BAA6B;AAEpE,wBAAgB,WAAW,CACzB,QAAQ,EAAE,SAAS,EAAE,EACrB,KAAK,EAAE,YAAY,GAClB,SAAS,GAAG,IAAI,CAalB;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAqB7E;AAeD,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAY1D;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAC3C,QAAQ,EAAE,MAAM,GACf,CAAC,MAAM,EAAE,MAAM,CAAC,CAalB;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D,WAAW,EAAE,iBAAiB,EAAE,CAAC;CAClC,CAAC;AA4EF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,cAAc,EAAE,CAIxE;AAqBD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CAuBjE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA4B5E"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { UIElement, ElementQuery } from "../backends/types.mjs";
|
|
2
|
+
export declare function findElement(elements: UIElement[], query: ElementQuery): UIElement | null;
|
|
3
|
+
export declare function matchesQuery(element: UIElement, query: ElementQuery): boolean;
|
|
4
|
+
export declare function describeElement(element: UIElement): string;
|
|
5
|
+
export declare function computeSwipeEnd(startX: number, startY: number, direction: 'up' | 'down' | 'left' | 'right', distance: number): [number, number];
|
|
6
|
+
export type LocatorSuggestion = {
|
|
7
|
+
strategy: 'identifier' | 'label' | 'text' | 'type';
|
|
8
|
+
value: string;
|
|
9
|
+
confidence: 'high' | 'medium' | 'low';
|
|
10
|
+
};
|
|
11
|
+
export type ElementLocator = {
|
|
12
|
+
description: string;
|
|
13
|
+
frame: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
};
|
|
19
|
+
suggestions: LocatorSuggestion[];
|
|
20
|
+
};
|
|
21
|
+
export declare function generateLocators(elements: UIElement[]): ElementLocator[];
|
|
22
|
+
export declare function formatLocators(locators: ElementLocator[]): string;
|
|
23
|
+
export declare function formatHierarchy(elements: UIElement[], depth: number): string;
|
|
24
|
+
//# sourceMappingURL=element.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element.d.mts","sourceRoot":"","sources":["../../src/utils/element.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,8BAA6B;AAEpE,wBAAgB,WAAW,CACzB,QAAQ,EAAE,SAAS,EAAE,EACrB,KAAK,EAAE,YAAY,GAClB,SAAS,GAAG,IAAI,CAalB;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAqB7E;AAeD,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAY1D;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAC3C,QAAQ,EAAE,MAAM,GACf,CAAC,MAAM,EAAE,MAAM,CAAC,CAalB;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D,WAAW,EAAE,iBAAiB,EAAE,CAAC;CAClC,CAAC;AA4EF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,cAAc,EAAE,CAIxE;AAqBD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CAuBjE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA4B5E"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
export function findElement(elements, query) {
|
|
2
|
+
for (const el of elements) {
|
|
3
|
+
if (matchesQuery(el, query)) {
|
|
4
|
+
return el;
|
|
5
|
+
}
|
|
6
|
+
if (el.children) {
|
|
7
|
+
const found = findElement(el.children, query);
|
|
8
|
+
if (found) {
|
|
9
|
+
return found;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
export function matchesQuery(element, query) {
|
|
16
|
+
if (query.identifier && !fuzzyMatch(element.identifier, query.identifier)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (query.label && !fuzzyMatch(element.label, query.label)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (query.text &&
|
|
23
|
+
!fuzzyMatch(element.value, query.text) &&
|
|
24
|
+
!fuzzyMatch(element.label, query.text)) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (query.type && !fuzzyMatch(element.type, query.type)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const hasAtLeastOneFilter = query.identifier ?? query.label ?? query.text ?? query.type;
|
|
31
|
+
return Boolean(hasAtLeastOneFilter);
|
|
32
|
+
}
|
|
33
|
+
function fuzzyMatch(actual, expected) {
|
|
34
|
+
if (!actual) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (actual === expected) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (actual.toLowerCase() === expected.toLowerCase()) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return actual.toLowerCase().includes(expected.toLowerCase());
|
|
44
|
+
}
|
|
45
|
+
export function describeElement(element) {
|
|
46
|
+
const parts = [element.type];
|
|
47
|
+
if (element.label) {
|
|
48
|
+
parts.push(`label="${element.label}"`);
|
|
49
|
+
}
|
|
50
|
+
if (element.identifier) {
|
|
51
|
+
parts.push(`id="${element.identifier}"`);
|
|
52
|
+
}
|
|
53
|
+
if (element.value) {
|
|
54
|
+
parts.push(`value="${element.value}"`);
|
|
55
|
+
}
|
|
56
|
+
return parts.join(' ');
|
|
57
|
+
}
|
|
58
|
+
export function computeSwipeEnd(startX, startY, direction, distance) {
|
|
59
|
+
switch (direction) {
|
|
60
|
+
case 'up':
|
|
61
|
+
return [startX, startY - distance];
|
|
62
|
+
case 'down':
|
|
63
|
+
return [startX, startY + distance];
|
|
64
|
+
case 'left':
|
|
65
|
+
return [startX - distance, startY];
|
|
66
|
+
case 'right':
|
|
67
|
+
return [startX + distance, startY];
|
|
68
|
+
default:
|
|
69
|
+
return [startX, startY];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const INTERACTIVE_TYPES = new Set([
|
|
73
|
+
'Button',
|
|
74
|
+
'TextField',
|
|
75
|
+
'SecureTextField',
|
|
76
|
+
'Switch',
|
|
77
|
+
'Slider',
|
|
78
|
+
'Picker',
|
|
79
|
+
'Link',
|
|
80
|
+
'SearchField',
|
|
81
|
+
'TextArea',
|
|
82
|
+
'Tab',
|
|
83
|
+
'MenuItem',
|
|
84
|
+
'Cell',
|
|
85
|
+
'android.widget.Button',
|
|
86
|
+
'android.widget.EditText',
|
|
87
|
+
'android.widget.CheckBox',
|
|
88
|
+
'android.widget.RadioButton',
|
|
89
|
+
'android.widget.Switch',
|
|
90
|
+
'android.widget.ToggleButton',
|
|
91
|
+
'android.widget.Spinner',
|
|
92
|
+
'android.widget.SeekBar',
|
|
93
|
+
'android.widget.ImageButton',
|
|
94
|
+
]);
|
|
95
|
+
function isInteractive(element) {
|
|
96
|
+
if (INTERACTIVE_TYPES.has(element.type)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
for (const t of INTERACTIVE_TYPES) {
|
|
100
|
+
if (element.type.includes(t)) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return element.enabled && Boolean(element.identifier);
|
|
105
|
+
}
|
|
106
|
+
function buildSuggestions(element) {
|
|
107
|
+
const suggestions = [];
|
|
108
|
+
if (element.identifier) {
|
|
109
|
+
suggestions.push({
|
|
110
|
+
strategy: 'identifier',
|
|
111
|
+
value: element.identifier,
|
|
112
|
+
confidence: 'high',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (element.label) {
|
|
116
|
+
suggestions.push({
|
|
117
|
+
strategy: 'label',
|
|
118
|
+
value: element.label,
|
|
119
|
+
confidence: element.identifier ? 'medium' : 'high',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (element.value && element.value !== element.label) {
|
|
123
|
+
suggestions.push({
|
|
124
|
+
strategy: 'text',
|
|
125
|
+
value: element.value,
|
|
126
|
+
confidence: 'low',
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (suggestions.length === 0) {
|
|
130
|
+
suggestions.push({
|
|
131
|
+
strategy: 'type',
|
|
132
|
+
value: element.type,
|
|
133
|
+
confidence: 'low',
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return suggestions;
|
|
137
|
+
}
|
|
138
|
+
export function generateLocators(elements) {
|
|
139
|
+
const results = [];
|
|
140
|
+
collectLocators(elements, results);
|
|
141
|
+
return results;
|
|
142
|
+
}
|
|
143
|
+
function collectLocators(elements, results) {
|
|
144
|
+
for (const el of elements) {
|
|
145
|
+
if (isInteractive(el)) {
|
|
146
|
+
const suggestions = buildSuggestions(el);
|
|
147
|
+
results.push({
|
|
148
|
+
description: describeElement(el),
|
|
149
|
+
frame: el.frame,
|
|
150
|
+
suggestions,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (el.children) {
|
|
154
|
+
collectLocators(el.children, results);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
export function formatLocators(locators) {
|
|
159
|
+
if (locators.length === 0) {
|
|
160
|
+
return 'No interactive elements found on screen.';
|
|
161
|
+
}
|
|
162
|
+
const confidenceIcon = {
|
|
163
|
+
high: '***',
|
|
164
|
+
medium: '**',
|
|
165
|
+
low: '*',
|
|
166
|
+
};
|
|
167
|
+
return locators
|
|
168
|
+
.map((loc, i) => {
|
|
169
|
+
const header = `${i + 1}. ${loc.description} (${loc.frame.x},${loc.frame.y} ${loc.frame.width}x${loc.frame.height})`;
|
|
170
|
+
const strategies = loc.suggestions
|
|
171
|
+
.map((s) => ` ${confidenceIcon[s.confidence]} { ${s.strategy}: "${s.value}" }`)
|
|
172
|
+
.join('\n');
|
|
173
|
+
return `${header}\n${strategies}`;
|
|
174
|
+
})
|
|
175
|
+
.join('\n\n');
|
|
176
|
+
}
|
|
177
|
+
export function formatHierarchy(elements, depth) {
|
|
178
|
+
const indent = ' '.repeat(depth);
|
|
179
|
+
return elements
|
|
180
|
+
.map((el) => {
|
|
181
|
+
const parts = [`${indent}[${el.type}]`];
|
|
182
|
+
if (el.label) {
|
|
183
|
+
parts.push(`label="${el.label}"`);
|
|
184
|
+
}
|
|
185
|
+
if (el.identifier) {
|
|
186
|
+
parts.push(`id="${el.identifier}"`);
|
|
187
|
+
}
|
|
188
|
+
if (el.value) {
|
|
189
|
+
parts.push(`value="${el.value}"`);
|
|
190
|
+
}
|
|
191
|
+
parts.push(`(${el.frame.x},${el.frame.y} ${el.frame.width}x${el.frame.height})`);
|
|
192
|
+
if (!el.enabled) {
|
|
193
|
+
parts.push('DISABLED');
|
|
194
|
+
}
|
|
195
|
+
let result = parts.join(' ');
|
|
196
|
+
if (el.children?.length) {
|
|
197
|
+
result += `\n${formatHierarchy(el.children, depth + 1)}`;
|
|
198
|
+
}
|
|
199
|
+
return result;
|
|
200
|
+
})
|
|
201
|
+
.join('\n');
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=element.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element.mjs","sourceRoot":"","sources":["../../src/utils/element.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UIElement, ElementQuery } from '../backends/types.js';\n\nexport function findElement(\n elements: UIElement[],\n query: ElementQuery,\n): UIElement | null {\n for (const el of elements) {\n if (matchesQuery(el, query)) {\n return el;\n }\n if (el.children) {\n const found = findElement(el.children, query);\n if (found) {\n return found;\n }\n }\n }\n return null;\n}\n\nexport function matchesQuery(element: UIElement, query: ElementQuery): boolean {\n if (query.identifier && !fuzzyMatch(element.identifier, query.identifier)) {\n return false;\n }\n if (query.label && !fuzzyMatch(element.label, query.label)) {\n return false;\n }\n if (\n query.text &&\n !fuzzyMatch(element.value, query.text) &&\n !fuzzyMatch(element.label, query.text)\n ) {\n return false;\n }\n if (query.type && !fuzzyMatch(element.type, query.type)) {\n return false;\n }\n\n const hasAtLeastOneFilter =\n query.identifier ?? query.label ?? query.text ?? query.type;\n return Boolean(hasAtLeastOneFilter);\n}\n\nfunction fuzzyMatch(actual: string | undefined, expected: string): boolean {\n if (!actual) {\n return false;\n }\n if (actual === expected) {\n return true;\n }\n if (actual.toLowerCase() === expected.toLowerCase()) {\n return true;\n }\n return actual.toLowerCase().includes(expected.toLowerCase());\n}\n\nexport function describeElement(element: UIElement): string {\n const parts: string[] = [element.type];\n if (element.label) {\n parts.push(`label=\"${element.label}\"`);\n }\n if (element.identifier) {\n parts.push(`id=\"${element.identifier}\"`);\n }\n if (element.value) {\n parts.push(`value=\"${element.value}\"`);\n }\n return parts.join(' ');\n}\n\nexport function computeSwipeEnd(\n startX: number,\n startY: number,\n direction: 'up' | 'down' | 'left' | 'right',\n distance: number,\n): [number, number] {\n switch (direction) {\n case 'up':\n return [startX, startY - distance];\n case 'down':\n return [startX, startY + distance];\n case 'left':\n return [startX - distance, startY];\n case 'right':\n return [startX + distance, startY];\n default:\n return [startX, startY];\n }\n}\n\nexport type LocatorSuggestion = {\n strategy: 'identifier' | 'label' | 'text' | 'type';\n value: string;\n confidence: 'high' | 'medium' | 'low';\n};\n\nexport type ElementLocator = {\n description: string;\n frame: { x: number; y: number; width: number; height: number };\n suggestions: LocatorSuggestion[];\n};\n\nconst INTERACTIVE_TYPES = new Set([\n 'Button',\n 'TextField',\n 'SecureTextField',\n 'Switch',\n 'Slider',\n 'Picker',\n 'Link',\n 'SearchField',\n 'TextArea',\n 'Tab',\n 'MenuItem',\n 'Cell',\n 'android.widget.Button',\n 'android.widget.EditText',\n 'android.widget.CheckBox',\n 'android.widget.RadioButton',\n 'android.widget.Switch',\n 'android.widget.ToggleButton',\n 'android.widget.Spinner',\n 'android.widget.SeekBar',\n 'android.widget.ImageButton',\n]);\n\nfunction isInteractive(element: UIElement): boolean {\n if (INTERACTIVE_TYPES.has(element.type)) {\n return true;\n }\n for (const t of INTERACTIVE_TYPES) {\n if (element.type.includes(t)) {\n return true;\n }\n }\n return element.enabled && Boolean(element.identifier);\n}\n\nfunction buildSuggestions(element: UIElement): LocatorSuggestion[] {\n const suggestions: LocatorSuggestion[] = [];\n\n if (element.identifier) {\n suggestions.push({\n strategy: 'identifier',\n value: element.identifier,\n confidence: 'high',\n });\n }\n\n if (element.label) {\n suggestions.push({\n strategy: 'label',\n value: element.label,\n confidence: element.identifier ? 'medium' : 'high',\n });\n }\n\n if (element.value && element.value !== element.label) {\n suggestions.push({\n strategy: 'text',\n value: element.value,\n confidence: 'low',\n });\n }\n\n if (suggestions.length === 0) {\n suggestions.push({\n strategy: 'type',\n value: element.type,\n confidence: 'low',\n });\n }\n\n return suggestions;\n}\n\nexport function generateLocators(elements: UIElement[]): ElementLocator[] {\n const results: ElementLocator[] = [];\n collectLocators(elements, results);\n return results;\n}\n\nfunction collectLocators(\n elements: UIElement[],\n results: ElementLocator[],\n): void {\n for (const el of elements) {\n if (isInteractive(el)) {\n const suggestions = buildSuggestions(el);\n results.push({\n description: describeElement(el),\n frame: el.frame,\n suggestions,\n });\n }\n if (el.children) {\n collectLocators(el.children, results);\n }\n }\n}\n\nexport function formatLocators(locators: ElementLocator[]): string {\n if (locators.length === 0) {\n return 'No interactive elements found on screen.';\n }\n\n const confidenceIcon: Record<string, string> = {\n high: '***',\n medium: '**',\n low: '*',\n };\n\n return locators\n .map((loc, i) => {\n const header = `${i + 1}. ${loc.description} (${loc.frame.x},${loc.frame.y} ${loc.frame.width}x${loc.frame.height})`;\n const strategies = loc.suggestions\n .map(\n (s) =>\n ` ${confidenceIcon[s.confidence]} { ${s.strategy}: \"${s.value}\" }`,\n )\n .join('\\n');\n return `${header}\\n${strategies}`;\n })\n .join('\\n\\n');\n}\n\nexport function formatHierarchy(elements: UIElement[], depth: number): string {\n const indent = ' '.repeat(depth);\n return elements\n .map((el) => {\n const parts = [`${indent}[${el.type}]`];\n if (el.label) {\n parts.push(`label=\"${el.label}\"`);\n }\n if (el.identifier) {\n parts.push(`id=\"${el.identifier}\"`);\n }\n if (el.value) {\n parts.push(`value=\"${el.value}\"`);\n }\n parts.push(\n `(${el.frame.x},${el.frame.y} ${el.frame.width}x${el.frame.height})`,\n );\n if (!el.enabled) {\n parts.push('DISABLED');\n }\n\n let result = parts.join(' ');\n if (el.children?.length) {\n result += `\\n${formatHierarchy(el.children, depth + 1)}`;\n }\n return result;\n })\n .join('\\n');\n}\n"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.exec = exec;
|
|
4
|
+
exports.execStrict = execStrict;
|
|
5
|
+
exports.isCommandAvailable = isCommandAvailable;
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
/**
|
|
8
|
+
* Execute a command and return stdout/stderr/exitCode.
|
|
9
|
+
* Rejects only on spawn failure — non-zero exit codes are returned, not thrown.
|
|
10
|
+
*
|
|
11
|
+
* @param command - The command to execute.
|
|
12
|
+
* @param args - Arguments to pass to the command.
|
|
13
|
+
* @param options - Execution options including timeoutMs.
|
|
14
|
+
* @returns The stdout, stderr, and exit code.
|
|
15
|
+
*/
|
|
16
|
+
async function exec(command, args, options) {
|
|
17
|
+
const { timeoutMs = 30000, ...execOptions } = options ?? {};
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const child = (0, node_child_process_1.execFile)(command, args, {
|
|
20
|
+
...execOptions,
|
|
21
|
+
timeout: timeoutMs,
|
|
22
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB — UI hierarchies can be large
|
|
23
|
+
}, (error, stdout, stderr) => {
|
|
24
|
+
if (error && !('code' in error)) {
|
|
25
|
+
// Spawn failure (command not found, ENOENT, etc.)
|
|
26
|
+
reject(new Error(error.message));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
resolve({
|
|
30
|
+
stdout: stdout?.toString() ?? '',
|
|
31
|
+
stderr: stderr?.toString() ?? '',
|
|
32
|
+
exitCode: error?.code
|
|
33
|
+
? 1
|
|
34
|
+
: (child.exitCode ?? 0),
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async function execStrict(command, args, options) {
|
|
40
|
+
const result = await exec(command, args, options);
|
|
41
|
+
if (result.exitCode !== 0) {
|
|
42
|
+
throw new Error(`Command failed: ${command} ${args.join(' ')}\n` +
|
|
43
|
+
`Exit code: ${result.exitCode}\n` +
|
|
44
|
+
`stderr: ${result.stderr}`);
|
|
45
|
+
}
|
|
46
|
+
return result.stdout;
|
|
47
|
+
}
|
|
48
|
+
async function isCommandAvailable(command) {
|
|
49
|
+
try {
|
|
50
|
+
await exec('which', [command], { timeoutMs: 5000 });
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=exec.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.cjs","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":";;AAiBA,oBAgCC;AAED,gCAcC;AAED,gDAOC;AA1ED,2DAA+D;AAQ/D;;;;;;;;GAQG;AACI,KAAK,UAAU,IAAI,CACxB,OAAe,EACf,IAAc,EACd,OAAkD;IAElD,MAAM,EAAE,SAAS,GAAG,KAAM,EAAE,GAAG,WAAW,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;IAE7D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,IAAA,6BAAQ,EACpB,OAAO,EACP,IAAI,EACJ;YACE,GAAG,WAAW;YACd,OAAO,EAAE,SAAS;YAClB,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,qCAAqC;SACnE,EACD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,IAAI,KAAK,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,EAAE,CAAC;gBAChC,kDAAkD;gBAClD,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;gBACjC,OAAO;YACT,CAAC;YACD,OAAO,CAAC;gBACN,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAChC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAChC,QAAQ,EAAG,KAA+B,EAAE,IAAI;oBAC9C,CAAC,CAAC,CAAC;oBACH,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC;aAC1B,CAAC,CAAC;QACL,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAEM,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,IAAc,EACd,OAAkD;IAElD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,mBAAmB,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI;YAC9C,cAAc,MAAM,CAAC,QAAQ,IAAI;YACjC,WAAW,MAAM,CAAC,MAAM,EAAE,CAC7B,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC;AAEM,KAAK,UAAU,kBAAkB,CAAC,OAAe;IACtD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAK,EAAE,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["import { execFile, ExecFileOptions } from 'node:child_process';\n\nexport type ExecResult = {\n stdout: string;\n stderr: string;\n exitCode: number;\n};\n\n/**\n * Execute a command and return stdout/stderr/exitCode.\n * Rejects only on spawn failure — non-zero exit codes are returned, not thrown.\n *\n * @param command - The command to execute.\n * @param args - Arguments to pass to the command.\n * @param options - Execution options including timeoutMs.\n * @returns The stdout, stderr, and exit code.\n */\nexport async function exec(\n command: string,\n args: string[],\n options?: ExecFileOptions & { timeoutMs?: number },\n): Promise<ExecResult> {\n const { timeoutMs = 30_000, ...execOptions } = options ?? {};\n\n return new Promise((resolve, reject) => {\n const child = execFile(\n command,\n args,\n {\n ...execOptions,\n timeout: timeoutMs,\n maxBuffer: 10 * 1024 * 1024, // 10MB — UI hierarchies can be large\n },\n (error, stdout, stderr) => {\n if (error && !('code' in error)) {\n // Spawn failure (command not found, ENOENT, etc.)\n reject(new Error(error.message));\n return;\n }\n resolve({\n stdout: stdout?.toString() ?? '',\n stderr: stderr?.toString() ?? '',\n exitCode: (error as NodeJS.ErrnoException)?.code\n ? 1\n : (child.exitCode ?? 0),\n });\n },\n );\n });\n}\n\nexport async function execStrict(\n command: string,\n args: string[],\n options?: ExecFileOptions & { timeoutMs?: number },\n): Promise<string> {\n const result = await exec(command, args, options);\n if (result.exitCode !== 0) {\n throw new Error(\n `Command failed: ${command} ${args.join(' ')}\\n` +\n `Exit code: ${result.exitCode}\\n` +\n `stderr: ${result.stderr}`,\n );\n }\n return result.stdout;\n}\n\nexport async function isCommandAvailable(command: string): Promise<boolean> {\n try {\n await exec('which', [command], { timeoutMs: 5_000 });\n return true;\n } catch {\n return false;\n }\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ExecFileOptions } from "node:child_process";
|
|
2
|
+
export type ExecResult = {
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
exitCode: number;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Execute a command and return stdout/stderr/exitCode.
|
|
9
|
+
* Rejects only on spawn failure — non-zero exit codes are returned, not thrown.
|
|
10
|
+
*
|
|
11
|
+
* @param command - The command to execute.
|
|
12
|
+
* @param args - Arguments to pass to the command.
|
|
13
|
+
* @param options - Execution options including timeoutMs.
|
|
14
|
+
* @returns The stdout, stderr, and exit code.
|
|
15
|
+
*/
|
|
16
|
+
export declare function exec(command: string, args: string[], options?: ExecFileOptions & {
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
}): Promise<ExecResult>;
|
|
19
|
+
export declare function execStrict(command: string, args: string[], options?: ExecFileOptions & {
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
}): Promise<string>;
|
|
22
|
+
export declare function isCommandAvailable(command: string): Promise<boolean>;
|
|
23
|
+
//# sourceMappingURL=exec.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.cts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,eAAe,EAAE,2BAA2B;AAE/D,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,UAAU,CAAC,CA4BrB;AAED,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1E"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ExecFileOptions } from "node:child_process";
|
|
2
|
+
export type ExecResult = {
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
exitCode: number;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Execute a command and return stdout/stderr/exitCode.
|
|
9
|
+
* Rejects only on spawn failure — non-zero exit codes are returned, not thrown.
|
|
10
|
+
*
|
|
11
|
+
* @param command - The command to execute.
|
|
12
|
+
* @param args - Arguments to pass to the command.
|
|
13
|
+
* @param options - Execution options including timeoutMs.
|
|
14
|
+
* @returns The stdout, stderr, and exit code.
|
|
15
|
+
*/
|
|
16
|
+
export declare function exec(command: string, args: string[], options?: ExecFileOptions & {
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
}): Promise<ExecResult>;
|
|
19
|
+
export declare function execStrict(command: string, args: string[], options?: ExecFileOptions & {
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
}): Promise<string>;
|
|
22
|
+
export declare function isCommandAvailable(command: string): Promise<boolean>;
|
|
23
|
+
//# sourceMappingURL=exec.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.mts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,eAAe,EAAE,2BAA2B;AAE/D,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,UAAU,CAAC,CA4BrB;AAED,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1E"}
|