react-native-ai-debugger 1.6.15 → 1.8.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/README.md +24 -14
- package/build/__tests__/unit/exec.test.d.ts +2 -0
- package/build/__tests__/unit/exec.test.d.ts.map +1 -0
- package/build/__tests__/unit/exec.test.js +54 -0
- package/build/__tests__/unit/exec.test.js.map +1 -0
- package/build/__tests__/unit/focusedInput.test.d.ts +2 -0
- package/build/__tests__/unit/focusedInput.test.d.ts.map +1 -0
- package/build/__tests__/unit/focusedInput.test.js +58 -0
- package/build/__tests__/unit/focusedInput.test.js.map +1 -0
- package/build/__tests__/unit/focusedInputTools.test.d.ts +2 -0
- package/build/__tests__/unit/focusedInputTools.test.d.ts.map +1 -0
- package/build/__tests__/unit/focusedInputTools.test.js +79 -0
- package/build/__tests__/unit/focusedInputTools.test.js.map +1 -0
- package/build/__tests__/unit/tap.test.js +46 -0
- package/build/__tests__/unit/tap.test.js.map +1 -1
- package/build/core/android.d.ts +3 -3
- package/build/core/android.d.ts.map +1 -1
- package/build/core/android.js +21 -14
- package/build/core/android.js.map +1 -1
- package/build/core/connection.d.ts.map +1 -1
- package/build/core/connection.js +3 -2
- package/build/core/connection.js.map +1 -1
- package/build/core/errors.d.ts +14 -0
- package/build/core/errors.d.ts.map +1 -0
- package/build/core/errors.js +17 -0
- package/build/core/errors.js.map +1 -0
- package/build/core/exec.d.ts +22 -0
- package/build/core/exec.d.ts.map +1 -0
- package/build/core/exec.js +54 -0
- package/build/core/exec.js.map +1 -0
- package/build/core/executor.d.ts +2 -0
- package/build/core/executor.d.ts.map +1 -1
- package/build/core/executor.js +232 -2
- package/build/core/executor.js.map +1 -1
- package/build/core/focusedInput.d.ts +27 -0
- package/build/core/focusedInput.d.ts.map +1 -0
- package/build/core/focusedInput.js +110 -0
- package/build/core/focusedInput.js.map +1 -0
- package/build/core/focusedInputTools.d.ts +21 -0
- package/build/core/focusedInputTools.d.ts.map +1 -0
- package/build/core/focusedInputTools.js +51 -0
- package/build/core/focusedInputTools.js.map +1 -0
- package/build/core/guides.d.ts.map +1 -1
- package/build/core/guides.js +15 -2
- package/build/core/guides.js.map +1 -1
- package/build/core/index.d.ts +1 -1
- package/build/core/index.d.ts.map +1 -1
- package/build/core/index.js +1 -1
- package/build/core/index.js.map +1 -1
- package/build/core/ios.d.ts +8 -0
- package/build/core/ios.d.ts.map +1 -1
- package/build/core/ios.js +142 -2
- package/build/core/ios.js.map +1 -1
- package/build/core/ocr.d.ts +13 -0
- package/build/core/ocr.d.ts.map +1 -1
- package/build/core/ocr.js +37 -15
- package/build/core/ocr.js.map +1 -1
- package/build/core/posthog.d.ts.map +1 -1
- package/build/core/posthog.js +7 -1
- package/build/core/posthog.js.map +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +158 -31
- package/build/index.js.map +1 -1
- package/build/pro/tap.d.ts +51 -5
- package/build/pro/tap.d.ts.map +1 -1
- package/build/pro/tap.js +457 -54
- package/build/pro/tap.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marker class for errors caused by invalid agent input (wrong device name,
|
|
3
|
+
* missing required predicate, ambiguous match, etc). H2 (Step 9 in the
|
|
4
|
+
* 2026-05-15 plan): the top-level catch in index.ts skips PostHog
|
|
5
|
+
* captureException for these so error tracking surfaces real product bugs,
|
|
6
|
+
* not validation noise (~13% of recent dashboard volume).
|
|
7
|
+
*
|
|
8
|
+
* The trade-off vs regex-matching the message: this is self-documenting at
|
|
9
|
+
* the throw site and survives message reformatting / translation.
|
|
10
|
+
*/
|
|
11
|
+
export declare class UserInputError extends Error {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,qBAAa,cAAe,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI9B"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marker class for errors caused by invalid agent input (wrong device name,
|
|
3
|
+
* missing required predicate, ambiguous match, etc). H2 (Step 9 in the
|
|
4
|
+
* 2026-05-15 plan): the top-level catch in index.ts skips PostHog
|
|
5
|
+
* captureException for these so error tracking surfaces real product bugs,
|
|
6
|
+
* not validation noise (~13% of recent dashboard volume).
|
|
7
|
+
*
|
|
8
|
+
* The trade-off vs regex-matching the message: this is self-documenting at
|
|
9
|
+
* the throw site and survives message reformatting / translation.
|
|
10
|
+
*/
|
|
11
|
+
export class UserInputError extends Error {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "UserInputError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IACrC,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IACjC,CAAC;CACJ"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ExecOptions } from "node:child_process";
|
|
2
|
+
export interface ExecAsyncOptions extends ExecOptions {
|
|
3
|
+
signal?: AbortSignal;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Drop-in replacement for `promisify(exec)` that accepts an AbortSignal.
|
|
7
|
+
* On abort, the child gets SIGTERM and then SIGKILL after 500ms — the
|
|
8
|
+
* escalation matters for ADB calls like `uiautomator dump` whose device-side
|
|
9
|
+
* process is independent of the host ADB process and may not exit on SIGTERM.
|
|
10
|
+
*/
|
|
11
|
+
export declare function execAsync(cmd: string, opts?: ExecAsyncOptions): Promise<{
|
|
12
|
+
stdout: string;
|
|
13
|
+
stderr: string;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Race an async operation against a timeout, with cancellation. The inner
|
|
17
|
+
* factory receives an AbortSignal it can pass into execAsync, fetch, etc.
|
|
18
|
+
* On timeout, the signal aborts so in-flight subprocesses get killed instead
|
|
19
|
+
* of running on past the strategy cap.
|
|
20
|
+
*/
|
|
21
|
+
export declare function withCancelableTimeout<T>(make: (signal: AbortSignal) => Promise<T>, ms: number, label: string): Promise<T>;
|
|
22
|
+
//# sourceMappingURL=exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/core/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACjD,MAAM,CAAC,EAAE,WAAW,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CACrB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,gBAAqB,GAC5B,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAqB7C;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACnC,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACzC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,CAAC,CAWZ"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Drop-in replacement for `promisify(exec)` that accepts an AbortSignal.
|
|
4
|
+
* On abort, the child gets SIGTERM and then SIGKILL after 500ms — the
|
|
5
|
+
* escalation matters for ADB calls like `uiautomator dump` whose device-side
|
|
6
|
+
* process is independent of the host ADB process and may not exit on SIGTERM.
|
|
7
|
+
*/
|
|
8
|
+
export function execAsync(cmd, opts = {}) {
|
|
9
|
+
const { signal, ...execOpts } = opts;
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const child = exec(cmd, execOpts, (err, stdout, stderr) => {
|
|
12
|
+
if (err)
|
|
13
|
+
reject(err);
|
|
14
|
+
else
|
|
15
|
+
resolve({ stdout: stdout.toString(), stderr: stderr.toString() });
|
|
16
|
+
});
|
|
17
|
+
const kill = () => {
|
|
18
|
+
try {
|
|
19
|
+
child.kill("SIGTERM");
|
|
20
|
+
}
|
|
21
|
+
catch { /* already dead */ }
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
try {
|
|
24
|
+
child.kill("SIGKILL");
|
|
25
|
+
}
|
|
26
|
+
catch { /* already dead */ }
|
|
27
|
+
}, 500).unref();
|
|
28
|
+
};
|
|
29
|
+
if (signal) {
|
|
30
|
+
if (signal.aborted) {
|
|
31
|
+
kill();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
signal.addEventListener("abort", kill, { once: true });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Race an async operation against a timeout, with cancellation. The inner
|
|
40
|
+
* factory receives an AbortSignal it can pass into execAsync, fetch, etc.
|
|
41
|
+
* On timeout, the signal aborts so in-flight subprocesses get killed instead
|
|
42
|
+
* of running on past the strategy cap.
|
|
43
|
+
*/
|
|
44
|
+
export function withCancelableTimeout(make, ms, label) {
|
|
45
|
+
const ctrl = new AbortController();
|
|
46
|
+
const timer = setTimeout(() => ctrl.abort(), ms);
|
|
47
|
+
return make(ctrl.signal).then((val) => { clearTimeout(timer); return val; }, (err) => {
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
if (ctrl.signal.aborted)
|
|
50
|
+
throw new Error(`${label} timed out after ${ms}ms`);
|
|
51
|
+
throw err;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/core/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAe,MAAM,oBAAoB,CAAC;AAMvD;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CACrB,GAAW,EACX,OAAyB,EAAE;IAE3B,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAC;IACrC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACtD,IAAI,GAAG;gBAAE,MAAM,CAAC,GAAG,CAAC,CAAC;;gBAChB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC3E,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,GAAG,EAAE;YACd,IAAI,CAAC;gBAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;YAC3D,UAAU,CAAC,GAAG,EAAE;gBACZ,IAAI,CAAC;oBAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;YAC/D,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC,CAAC;QACF,IAAI,MAAM,EAAE,CAAC;YACT,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,EAAE,CAAC;gBACP,OAAO;YACX,CAAC;YACD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACjC,IAAyC,EACzC,EAAU,EACV,KAAa;IAEb,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;IACjD,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CACzB,CAAC,GAAG,EAAE,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EAC7C,CAAC,GAAG,EAAE,EAAE;QACJ,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,oBAAoB,EAAE,IAAI,CAAC,CAAC;QAC7E,MAAM,GAAG,CAAC;IACd,CAAC,CACJ,CAAC;AACN,CAAC"}
|
package/build/core/executor.d.ts
CHANGED
|
@@ -104,6 +104,8 @@ interface PressableElement {
|
|
|
104
104
|
}
|
|
105
105
|
export declare function getPressableElements(options?: {
|
|
106
106
|
device?: string;
|
|
107
|
+
platform?: "ios" | "android";
|
|
108
|
+
udid?: string;
|
|
107
109
|
}): Promise<ExecutionResult & {
|
|
108
110
|
parsedElements?: PressableElement[];
|
|
109
111
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/core/executor.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/core/executor.ts"],"names":[],"mappings":"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"}
|
package/build/core/executor.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import WebSocket from "ws";
|
|
2
2
|
import { pendingExecutions, getNextMessageId, connectedApps } from "./state.js";
|
|
3
|
-
import { getFirstConnectedApp, getConnectedAppByDevice, connectToDevice, clearReconnectionSuppression, purgeStaleConnectionsForPorts } from "./connection.js";
|
|
3
|
+
import { getFirstConnectedApp, getConnectedAppByDevice, getConnectedAppBySimulatorUdid, getConnectedAppByAndroidDeviceId, connectToDevice, clearReconnectionSuppression, purgeStaleConnectionsForPorts } from "./connection.js";
|
|
4
4
|
import { fetchDevices, selectMainDevice, filterDebuggableDevices, scanMetroPorts } from "./metro.js";
|
|
5
5
|
import { DEFAULT_RECONNECTION_CONFIG, cancelReconnectionTimer } from "./connectionState.js";
|
|
6
6
|
// Hermes runtime compatibility: polyfill for 'global' which doesn't exist in Hermes
|
|
@@ -1786,8 +1786,118 @@ export async function getScreenLayout(options = {}) {
|
|
|
1786
1786
|
}
|
|
1787
1787
|
return result;
|
|
1788
1788
|
}
|
|
1789
|
+
/**
|
|
1790
|
+
* E1 / Step 5 (2026-05-15 plan): build a pressables list from the platform
|
|
1791
|
+
* accessibility tree when fiber isn't reachable. Used when metro is offline
|
|
1792
|
+
* (connectedApps.size === 0) so the screenshot tools can still hand the agent
|
|
1793
|
+
* labelled coordinates instead of forcing it to guess pixels — the dominant
|
|
1794
|
+
* cause of unmeaningful coordinate-strategy taps in the 7-day failure data.
|
|
1795
|
+
*/
|
|
1796
|
+
async function getAccessibilityPressables(platform, udid) {
|
|
1797
|
+
try {
|
|
1798
|
+
if (platform === "ios") {
|
|
1799
|
+
const { iosGetUITree } = await import("./ios.js");
|
|
1800
|
+
const tree = await iosGetUITree(udid);
|
|
1801
|
+
if (!tree.success || !tree.elements)
|
|
1802
|
+
return [];
|
|
1803
|
+
const interactiveTraits = new Set(["button", "link", "searchfield", "tab", "image"]);
|
|
1804
|
+
return tree.elements
|
|
1805
|
+
.filter(el => {
|
|
1806
|
+
// iOS: rely on a11y traits + element type. Buttons, links,
|
|
1807
|
+
// tabs, and any element with a non-empty label/identifier
|
|
1808
|
+
// that isn't a static text container.
|
|
1809
|
+
const traitsLower = (el.traits || []).map(t => t.toLowerCase());
|
|
1810
|
+
if (traitsLower.some(t => interactiveTraits.has(t)))
|
|
1811
|
+
return true;
|
|
1812
|
+
const type = (el.type || "").toLowerCase();
|
|
1813
|
+
if (type.includes("button") || type.includes("textfield") || type.includes("searchfield"))
|
|
1814
|
+
return true;
|
|
1815
|
+
return false;
|
|
1816
|
+
})
|
|
1817
|
+
.map(el => {
|
|
1818
|
+
const type = (el.type || "").toLowerCase();
|
|
1819
|
+
return {
|
|
1820
|
+
component: el.type || "Element",
|
|
1821
|
+
path: "",
|
|
1822
|
+
center: el.center,
|
|
1823
|
+
frame: el.frame,
|
|
1824
|
+
text: el.label || "",
|
|
1825
|
+
testID: el.identifier || null,
|
|
1826
|
+
accessibilityLabel: el.label || null,
|
|
1827
|
+
hasLabel: !!el.label,
|
|
1828
|
+
isInput: type.includes("textfield") || type.includes("searchfield")
|
|
1829
|
+
};
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
else {
|
|
1833
|
+
const { androidGetUITree } = await import("./android.js");
|
|
1834
|
+
const tree = await androidGetUITree();
|
|
1835
|
+
if (!tree.success || !tree.elements)
|
|
1836
|
+
return [];
|
|
1837
|
+
return tree.elements
|
|
1838
|
+
.filter(el => el.clickable || el.focused || (el.className && el.className.includes("EditText")))
|
|
1839
|
+
.map(el => ({
|
|
1840
|
+
component: (el.className || "View").split(".").pop() || "View",
|
|
1841
|
+
path: "",
|
|
1842
|
+
center: el.center,
|
|
1843
|
+
frame: { x: el.bounds.left, y: el.bounds.top, width: el.bounds.width, height: el.bounds.height },
|
|
1844
|
+
text: el.text || "",
|
|
1845
|
+
testID: el.resourceId || null,
|
|
1846
|
+
accessibilityLabel: el.contentDesc || null,
|
|
1847
|
+
hasLabel: !!(el.text || el.contentDesc),
|
|
1848
|
+
isInput: !!(el.className && el.className.includes("EditText"))
|
|
1849
|
+
}));
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
catch {
|
|
1853
|
+
return [];
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1789
1856
|
export async function getPressableElements(options = {}) {
|
|
1790
|
-
const { device } = options;
|
|
1857
|
+
const { device, platform, udid } = options;
|
|
1858
|
+
// Resolve which connected RN app corresponds to the requested device/udid.
|
|
1859
|
+
// The E1 fallback below must fire when no app matches THIS device, even if
|
|
1860
|
+
// unrelated apps remain connected to Metro. Without this filter the fiber
|
|
1861
|
+
// path would silently fall back to the first-connected app and return data
|
|
1862
|
+
// for the wrong device (Bug 1, 2026-05-16).
|
|
1863
|
+
let targetApp = null;
|
|
1864
|
+
if (udid && platform === "ios") {
|
|
1865
|
+
targetApp = getConnectedAppBySimulatorUdid(udid);
|
|
1866
|
+
}
|
|
1867
|
+
else if (device) {
|
|
1868
|
+
const matched = getConnectedAppByDevice(device);
|
|
1869
|
+
if (matched && (!platform || matched.platform === platform))
|
|
1870
|
+
targetApp = matched;
|
|
1871
|
+
}
|
|
1872
|
+
else if (platform === "android") {
|
|
1873
|
+
targetApp = getConnectedAppByAndroidDeviceId(undefined);
|
|
1874
|
+
}
|
|
1875
|
+
else if (platform === "ios") {
|
|
1876
|
+
const first = getFirstConnectedApp();
|
|
1877
|
+
targetApp = first && first.platform === "ios" ? first : null;
|
|
1878
|
+
}
|
|
1879
|
+
else {
|
|
1880
|
+
targetApp = getFirstConnectedApp();
|
|
1881
|
+
}
|
|
1882
|
+
// E1 fallback: no app resolved for THIS device → use platform accessibility tree.
|
|
1883
|
+
// Only kicks in when caller passed `platform` so we don't break older
|
|
1884
|
+
// metro-required call paths that didn't opt into the fallback.
|
|
1885
|
+
if (!targetApp && platform) {
|
|
1886
|
+
const elements = await getAccessibilityPressables(platform, udid);
|
|
1887
|
+
return {
|
|
1888
|
+
success: true,
|
|
1889
|
+
result: elements.length > 0
|
|
1890
|
+
? `Pressable elements (from accessibility tree, ${elements.length} found):\n` +
|
|
1891
|
+
elements.map((el, i) => {
|
|
1892
|
+
const label = el.accessibilityLabel || el.text || el.testID || el.component;
|
|
1893
|
+
const idStr = el.testID ? ` testID="${el.testID}"` : "";
|
|
1894
|
+
const inputStr = el.isInput ? " [input]" : "";
|
|
1895
|
+
return `${i + 1}. ${el.component} "${label}"${idStr}${inputStr} — center:(${el.center.x},${el.center.y}) frame:(${el.frame.x},${el.frame.y} ${el.frame.width}x${el.frame.height})`;
|
|
1896
|
+
}).join("\n")
|
|
1897
|
+
: "No interactive elements found via accessibility.",
|
|
1898
|
+
parsedElements: elements
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1791
1901
|
// --- Step 1: walk fiber tree, find pressable/input elements, dispatch measureInWindow ---
|
|
1792
1902
|
const dispatchExpression = `
|
|
1793
1903
|
(function() {
|
|
@@ -2376,6 +2486,126 @@ export async function getPressableElements(options = {}) {
|
|
|
2376
2486
|
if (parsed.error)
|
|
2377
2487
|
return { success: false, error: parsed.error };
|
|
2378
2488
|
const pressableElements = parsed.pressableElements || [];
|
|
2489
|
+
// Android coordinate reconciliation (2026-05-17).
|
|
2490
|
+
//
|
|
2491
|
+
// The fiber walker produces rich metadata (component names, testID, intent,
|
|
2492
|
+
// wrapper flags, nearbyText) but its measureInWindow output on Bridgeless/Fabric
|
|
2493
|
+
// is in app-window DP space — off from the actual screen-pixel hit area by the
|
|
2494
|
+
// status bar height (varies per device) AND an additional unaccounted offset
|
|
2495
|
+
// we couldn't characterize across devices.
|
|
2496
|
+
//
|
|
2497
|
+
// uiautomator's bounds, on the other hand, are in absolute display pixels and
|
|
2498
|
+
// match what tap(x, y) → adb input dispatches — they are touch-accurate.
|
|
2499
|
+
//
|
|
2500
|
+
// Strategy: keep fiber's metadata, but replace coords with uiautomator's bounds
|
|
2501
|
+
// when we can match a fiber element to a uiautomator node by text / contentDesc /
|
|
2502
|
+
// resourceId. For icon-only pressables that have no text-match candidate,
|
|
2503
|
+
// fall back to fiber's DP coords scaled by density — they're imperfect but
|
|
2504
|
+
// they keep the same gross layout that an agent could use as a "near this
|
|
2505
|
+
// region" hint, and they're better than nothing.
|
|
2506
|
+
//
|
|
2507
|
+
// iOS is unaffected — fiber returns points and AXe returns points; uniform.
|
|
2508
|
+
if (platform === "android" && pressableElements.length > 0) {
|
|
2509
|
+
let densityScale = 2.625;
|
|
2510
|
+
let statusBarPixels = 0;
|
|
2511
|
+
try {
|
|
2512
|
+
const { androidGetDensity, androidGetStatusBarHeight } = await import("./android.js");
|
|
2513
|
+
const [densityResult, statusBarResult] = await Promise.all([
|
|
2514
|
+
androidGetDensity(),
|
|
2515
|
+
androidGetStatusBarHeight().catch(() => ({ success: false, heightPixels: 0 })),
|
|
2516
|
+
]);
|
|
2517
|
+
densityScale = (densityResult.density || 420) / 160;
|
|
2518
|
+
if (statusBarResult.success && statusBarResult.heightPixels) {
|
|
2519
|
+
statusBarPixels = statusBarResult.heightPixels;
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
catch { /* default */ }
|
|
2523
|
+
let uiNodes;
|
|
2524
|
+
try {
|
|
2525
|
+
const { androidGetUITree } = await import("./android.js");
|
|
2526
|
+
const tree = await androidGetUITree();
|
|
2527
|
+
if (tree.success && tree.elements)
|
|
2528
|
+
uiNodes = tree.elements;
|
|
2529
|
+
}
|
|
2530
|
+
catch { /* uiautomator unavailable */ }
|
|
2531
|
+
const norm = (s) => (s ?? "").toLowerCase().replace(/\s+/g, " ").trim();
|
|
2532
|
+
const findUiMatch = (el) => {
|
|
2533
|
+
if (!uiNodes || uiNodes.length === 0)
|
|
2534
|
+
return null;
|
|
2535
|
+
const targetTestID = norm(el.testID);
|
|
2536
|
+
const targetText = norm(el.text);
|
|
2537
|
+
const targetA11y = norm(el.accessibilityLabel);
|
|
2538
|
+
if (!targetTestID && !targetText && !targetA11y)
|
|
2539
|
+
return null;
|
|
2540
|
+
// Prefer testID → resourceId exact match (most stable).
|
|
2541
|
+
if (targetTestID) {
|
|
2542
|
+
const m = uiNodes.find(u => norm(u.resourceId).endsWith("/" + targetTestID) || norm(u.resourceId) === targetTestID);
|
|
2543
|
+
if (m)
|
|
2544
|
+
return m;
|
|
2545
|
+
}
|
|
2546
|
+
// Then exact text/contentDesc match.
|
|
2547
|
+
for (const query of [targetText, targetA11y]) {
|
|
2548
|
+
if (!query)
|
|
2549
|
+
continue;
|
|
2550
|
+
const m = uiNodes.find(u => norm(u.text) === query || norm(u.contentDesc) === query);
|
|
2551
|
+
if (m)
|
|
2552
|
+
return m;
|
|
2553
|
+
}
|
|
2554
|
+
// Then substring — handles fiber's collected text being a superset of one node's label.
|
|
2555
|
+
for (const query of [targetText, targetA11y]) {
|
|
2556
|
+
if (!query)
|
|
2557
|
+
continue;
|
|
2558
|
+
const m = uiNodes.find(u => {
|
|
2559
|
+
const ut = norm(u.text);
|
|
2560
|
+
const ud = norm(u.contentDesc);
|
|
2561
|
+
return (ut && (ut.includes(query) || query.includes(ut))) ||
|
|
2562
|
+
(ud && (ud.includes(query) || query.includes(ud)));
|
|
2563
|
+
});
|
|
2564
|
+
if (m)
|
|
2565
|
+
return m;
|
|
2566
|
+
}
|
|
2567
|
+
return null;
|
|
2568
|
+
};
|
|
2569
|
+
for (const el of pressableElements) {
|
|
2570
|
+
const match = findUiMatch(el);
|
|
2571
|
+
if (match) {
|
|
2572
|
+
el.center = { x: match.center.x, y: match.center.y };
|
|
2573
|
+
el.frame = {
|
|
2574
|
+
x: match.bounds.left,
|
|
2575
|
+
y: match.bounds.top,
|
|
2576
|
+
width: match.bounds.width,
|
|
2577
|
+
height: match.bounds.height,
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
else {
|
|
2581
|
+
// Fall back to fiber's DP → device-pixel scaling, plus the dynamic
|
|
2582
|
+
// status-bar pixel offset so the y origin lines up with display
|
|
2583
|
+
// coordinates (uiautomator's space). measureInWindow on Bridgeless
|
|
2584
|
+
// returns coordinates relative to the React window which sits
|
|
2585
|
+
// below the system status bar on non-edge-to-edge apps; without
|
|
2586
|
+
// this shift, fallback elements report y-values that are
|
|
2587
|
+
// ~status-bar-height too small.
|
|
2588
|
+
//
|
|
2589
|
+
// Note: this is a best-effort correction. Edge-to-edge apps where
|
|
2590
|
+
// the React window starts at the display origin will see a
|
|
2591
|
+
// small over-correction here. The proper fix would query
|
|
2592
|
+
// WindowInsets, but the data isn't exposed via adb in a way
|
|
2593
|
+
// we can reach today, and the icon-only-no-text fallback is
|
|
2594
|
+
// already a "region hint" path — agents should prefer
|
|
2595
|
+
// tap(component=...) / tap(testID=...) for precision.
|
|
2596
|
+
el.center = {
|
|
2597
|
+
x: el.center.x * densityScale,
|
|
2598
|
+
y: el.center.y * densityScale + statusBarPixels,
|
|
2599
|
+
};
|
|
2600
|
+
el.frame = {
|
|
2601
|
+
x: el.frame.x * densityScale,
|
|
2602
|
+
y: el.frame.y * densityScale + statusBarPixels,
|
|
2603
|
+
width: el.frame.width * densityScale,
|
|
2604
|
+
height: el.frame.height * densityScale,
|
|
2605
|
+
};
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2379
2609
|
// Format as readable text
|
|
2380
2610
|
const lines = [parsed.summary, ""];
|
|
2381
2611
|
for (let i = 0; i < pressableElements.length; i++) {
|