execbro 0.0.1-reserve → 1.10.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (475) hide show
  1. package/LICENSE +32 -0
  2. package/README.md +496 -0
  3. package/build/__tests__/helpers/fake-cdp-server.d.ts +56 -0
  4. package/build/__tests__/helpers/fake-cdp-server.d.ts.map +1 -0
  5. package/build/__tests__/helpers/fake-cdp-server.js +120 -0
  6. package/build/__tests__/helpers/fake-cdp-server.js.map +1 -0
  7. package/build/__tests__/helpers/tap-test-helpers.d.ts +36 -0
  8. package/build/__tests__/helpers/tap-test-helpers.d.ts.map +1 -0
  9. package/build/__tests__/helpers/tap-test-helpers.js +130 -0
  10. package/build/__tests__/helpers/tap-test-helpers.js.map +1 -0
  11. package/build/__tests__/integration/auto-reconnect.test.d.ts +2 -0
  12. package/build/__tests__/integration/auto-reconnect.test.d.ts.map +1 -0
  13. package/build/__tests__/integration/auto-reconnect.test.js +118 -0
  14. package/build/__tests__/integration/auto-reconnect.test.js.map +1 -0
  15. package/build/__tests__/integration/connection-health.test.d.ts +2 -0
  16. package/build/__tests__/integration/connection-health.test.d.ts.map +1 -0
  17. package/build/__tests__/integration/connection-health.test.js +157 -0
  18. package/build/__tests__/integration/connection-health.test.js.map +1 -0
  19. package/build/__tests__/integration/execute-in-app.test.d.ts +2 -0
  20. package/build/__tests__/integration/execute-in-app.test.d.ts.map +1 -0
  21. package/build/__tests__/integration/execute-in-app.test.js +124 -0
  22. package/build/__tests__/integration/execute-in-app.test.js.map +1 -0
  23. package/build/__tests__/integration/per-call-timeout.test.d.ts +2 -0
  24. package/build/__tests__/integration/per-call-timeout.test.d.ts.map +1 -0
  25. package/build/__tests__/integration/per-call-timeout.test.js +103 -0
  26. package/build/__tests__/integration/per-call-timeout.test.js.map +1 -0
  27. package/build/__tests__/integration/tap-strategies.test.d.ts +2 -0
  28. package/build/__tests__/integration/tap-strategies.test.d.ts.map +1 -0
  29. package/build/__tests__/integration/tap-strategies.test.js +330 -0
  30. package/build/__tests__/integration/tap-strategies.test.js.map +1 -0
  31. package/build/__tests__/integration/tools.test.d.ts +2 -0
  32. package/build/__tests__/integration/tools.test.d.ts.map +1 -0
  33. package/build/__tests__/integration/tools.test.js +227 -0
  34. package/build/__tests__/integration/tools.test.js.map +1 -0
  35. package/build/__tests__/setup.d.ts +2 -0
  36. package/build/__tests__/setup.d.ts.map +1 -0
  37. package/build/__tests__/setup.js +15 -0
  38. package/build/__tests__/setup.js.map +1 -0
  39. package/build/__tests__/unit/androidEmulatorAvds.test.d.ts +2 -0
  40. package/build/__tests__/unit/androidEmulatorAvds.test.d.ts.map +1 -0
  41. package/build/__tests__/unit/androidEmulatorAvds.test.js +98 -0
  42. package/build/__tests__/unit/androidEmulatorAvds.test.js.map +1 -0
  43. package/build/__tests__/unit/buildToken.test.d.ts +2 -0
  44. package/build/__tests__/unit/buildToken.test.d.ts.map +1 -0
  45. package/build/__tests__/unit/buildToken.test.js +22 -0
  46. package/build/__tests__/unit/buildToken.test.js.map +1 -0
  47. package/build/__tests__/unit/bundle.test.d.ts +2 -0
  48. package/build/__tests__/unit/bundle.test.d.ts.map +1 -0
  49. package/build/__tests__/unit/bundle.test.js +53 -0
  50. package/build/__tests__/unit/bundle.test.js.map +1 -0
  51. package/build/__tests__/unit/burst.test.d.ts +2 -0
  52. package/build/__tests__/unit/burst.test.d.ts.map +1 -0
  53. package/build/__tests__/unit/burst.test.js +54 -0
  54. package/build/__tests__/unit/burst.test.js.map +1 -0
  55. package/build/__tests__/unit/connection-health.test.d.ts +2 -0
  56. package/build/__tests__/unit/connection-health.test.d.ts.map +1 -0
  57. package/build/__tests__/unit/connection-health.test.js +38 -0
  58. package/build/__tests__/unit/connection-health.test.js.map +1 -0
  59. package/build/__tests__/unit/debugGlobals.test.d.ts +2 -0
  60. package/build/__tests__/unit/debugGlobals.test.d.ts.map +1 -0
  61. package/build/__tests__/unit/debugGlobals.test.js +14 -0
  62. package/build/__tests__/unit/debugGlobals.test.js.map +1 -0
  63. package/build/__tests__/unit/deviceDiscovery.test.d.ts +2 -0
  64. package/build/__tests__/unit/deviceDiscovery.test.d.ts.map +1 -0
  65. package/build/__tests__/unit/deviceDiscovery.test.js +119 -0
  66. package/build/__tests__/unit/deviceDiscovery.test.js.map +1 -0
  67. package/build/__tests__/unit/deviceResolver.test.d.ts +2 -0
  68. package/build/__tests__/unit/deviceResolver.test.d.ts.map +1 -0
  69. package/build/__tests__/unit/deviceResolver.test.js +261 -0
  70. package/build/__tests__/unit/deviceResolver.test.js.map +1 -0
  71. package/build/__tests__/unit/escapeNonAscii.test.d.ts +2 -0
  72. package/build/__tests__/unit/escapeNonAscii.test.d.ts.map +1 -0
  73. package/build/__tests__/unit/escapeNonAscii.test.js +76 -0
  74. package/build/__tests__/unit/escapeNonAscii.test.js.map +1 -0
  75. package/build/__tests__/unit/exec.test.d.ts +2 -0
  76. package/build/__tests__/unit/exec.test.d.ts.map +1 -0
  77. package/build/__tests__/unit/exec.test.js +54 -0
  78. package/build/__tests__/unit/exec.test.js.map +1 -0
  79. package/build/__tests__/unit/executor.test.d.ts +2 -0
  80. package/build/__tests__/unit/executor.test.d.ts.map +1 -0
  81. package/build/__tests__/unit/executor.test.js +220 -0
  82. package/build/__tests__/unit/executor.test.js.map +1 -0
  83. package/build/__tests__/unit/failureArtifact.test.d.ts +2 -0
  84. package/build/__tests__/unit/failureArtifact.test.d.ts.map +1 -0
  85. package/build/__tests__/unit/failureArtifact.test.js +207 -0
  86. package/build/__tests__/unit/failureArtifact.test.js.map +1 -0
  87. package/build/__tests__/unit/fastRefreshRecorder.test.d.ts +2 -0
  88. package/build/__tests__/unit/fastRefreshRecorder.test.d.ts.map +1 -0
  89. package/build/__tests__/unit/fastRefreshRecorder.test.js +68 -0
  90. package/build/__tests__/unit/fastRefreshRecorder.test.js.map +1 -0
  91. package/build/__tests__/unit/fastRefreshTools.test.d.ts +2 -0
  92. package/build/__tests__/unit/fastRefreshTools.test.d.ts.map +1 -0
  93. package/build/__tests__/unit/fastRefreshTools.test.js +112 -0
  94. package/build/__tests__/unit/fastRefreshTools.test.js.map +1 -0
  95. package/build/__tests__/unit/fiberMatch.test.d.ts +2 -0
  96. package/build/__tests__/unit/fiberMatch.test.d.ts.map +1 -0
  97. package/build/__tests__/unit/fiberMatch.test.js +114 -0
  98. package/build/__tests__/unit/fiberMatch.test.js.map +1 -0
  99. package/build/__tests__/unit/fingerprint.test.d.ts +2 -0
  100. package/build/__tests__/unit/fingerprint.test.d.ts.map +1 -0
  101. package/build/__tests__/unit/fingerprint.test.js +99 -0
  102. package/build/__tests__/unit/fingerprint.test.js.map +1 -0
  103. package/build/__tests__/unit/focusedInput.test.d.ts +2 -0
  104. package/build/__tests__/unit/focusedInput.test.d.ts.map +1 -0
  105. package/build/__tests__/unit/focusedInput.test.js +58 -0
  106. package/build/__tests__/unit/focusedInput.test.js.map +1 -0
  107. package/build/__tests__/unit/focusedInputTools.test.d.ts +2 -0
  108. package/build/__tests__/unit/focusedInputTools.test.d.ts.map +1 -0
  109. package/build/__tests__/unit/focusedInputTools.test.js +79 -0
  110. package/build/__tests__/unit/focusedInputTools.test.js.map +1 -0
  111. package/build/__tests__/unit/iconSemantics.test.d.ts +2 -0
  112. package/build/__tests__/unit/iconSemantics.test.d.ts.map +1 -0
  113. package/build/__tests__/unit/iconSemantics.test.js +80 -0
  114. package/build/__tests__/unit/iconSemantics.test.js.map +1 -0
  115. package/build/__tests__/unit/imageBuffer.test.d.ts +2 -0
  116. package/build/__tests__/unit/imageBuffer.test.d.ts.map +1 -0
  117. package/build/__tests__/unit/imageBuffer.test.js +128 -0
  118. package/build/__tests__/unit/imageBuffer.test.js.map +1 -0
  119. package/build/__tests__/unit/logs.test.d.ts +2 -0
  120. package/build/__tests__/unit/logs.test.d.ts.map +1 -0
  121. package/build/__tests__/unit/logs.test.js +90 -0
  122. package/build/__tests__/unit/logs.test.js.map +1 -0
  123. package/build/__tests__/unit/measureComponent.test.d.ts +2 -0
  124. package/build/__tests__/unit/measureComponent.test.d.ts.map +1 -0
  125. package/build/__tests__/unit/measureComponent.test.js +66 -0
  126. package/build/__tests__/unit/measureComponent.test.js.map +1 -0
  127. package/build/__tests__/unit/measureComponentTools.test.d.ts +2 -0
  128. package/build/__tests__/unit/measureComponentTools.test.d.ts.map +1 -0
  129. package/build/__tests__/unit/measureComponentTools.test.js +94 -0
  130. package/build/__tests__/unit/measureComponentTools.test.js.map +1 -0
  131. package/build/__tests__/unit/metro.test.d.ts +2 -0
  132. package/build/__tests__/unit/metro.test.d.ts.map +1 -0
  133. package/build/__tests__/unit/metro.test.js +61 -0
  134. package/build/__tests__/unit/metro.test.js.map +1 -0
  135. package/build/__tests__/unit/network.test.d.ts +2 -0
  136. package/build/__tests__/unit/network.test.d.ts.map +1 -0
  137. package/build/__tests__/unit/network.test.js +102 -0
  138. package/build/__tests__/unit/network.test.js.map +1 -0
  139. package/build/__tests__/unit/networkInterceptor.test.d.ts +2 -0
  140. package/build/__tests__/unit/networkInterceptor.test.d.ts.map +1 -0
  141. package/build/__tests__/unit/networkInterceptor.test.js +104 -0
  142. package/build/__tests__/unit/networkInterceptor.test.js.map +1 -0
  143. package/build/__tests__/unit/probe.test.d.ts +2 -0
  144. package/build/__tests__/unit/probe.test.d.ts.map +1 -0
  145. package/build/__tests__/unit/probe.test.js +62 -0
  146. package/build/__tests__/unit/probe.test.js.map +1 -0
  147. package/build/__tests__/unit/rnGlobalsBootstrap.test.d.ts +2 -0
  148. package/build/__tests__/unit/rnGlobalsBootstrap.test.d.ts.map +1 -0
  149. package/build/__tests__/unit/rnGlobalsBootstrap.test.js +29 -0
  150. package/build/__tests__/unit/rnGlobalsBootstrap.test.js.map +1 -0
  151. package/build/__tests__/unit/screenState.test.d.ts +2 -0
  152. package/build/__tests__/unit/screenState.test.d.ts.map +1 -0
  153. package/build/__tests__/unit/screenState.test.js +384 -0
  154. package/build/__tests__/unit/screenState.test.js.map +1 -0
  155. package/build/__tests__/unit/screenshot-diff.test.d.ts +2 -0
  156. package/build/__tests__/unit/screenshot-diff.test.d.ts.map +1 -0
  157. package/build/__tests__/unit/screenshot-diff.test.js +97 -0
  158. package/build/__tests__/unit/screenshot-diff.test.js.map +1 -0
  159. package/build/__tests__/unit/tap-evidence.test.d.ts +2 -0
  160. package/build/__tests__/unit/tap-evidence.test.d.ts.map +1 -0
  161. package/build/__tests__/unit/tap-evidence.test.js +89 -0
  162. package/build/__tests__/unit/tap-evidence.test.js.map +1 -0
  163. package/build/__tests__/unit/tap.test.d.ts +2 -0
  164. package/build/__tests__/unit/tap.test.d.ts.map +1 -0
  165. package/build/__tests__/unit/tap.test.js +767 -0
  166. package/build/__tests__/unit/tap.test.js.map +1 -0
  167. package/build/__tests__/unit/timeoutClamp.test.d.ts +2 -0
  168. package/build/__tests__/unit/timeoutClamp.test.d.ts.map +1 -0
  169. package/build/__tests__/unit/timeoutClamp.test.js +17 -0
  170. package/build/__tests__/unit/timeoutClamp.test.js.map +1 -0
  171. package/build/__tests__/unit/toolDescriptions.test.d.ts +2 -0
  172. package/build/__tests__/unit/toolDescriptions.test.d.ts.map +1 -0
  173. package/build/__tests__/unit/toolDescriptions.test.js +145 -0
  174. package/build/__tests__/unit/toolDescriptions.test.js.map +1 -0
  175. package/build/__tests__/unit/transportClassify.test.d.ts +2 -0
  176. package/build/__tests__/unit/transportClassify.test.d.ts.map +1 -0
  177. package/build/__tests__/unit/transportClassify.test.js +60 -0
  178. package/build/__tests__/unit/transportClassify.test.js.map +1 -0
  179. package/build/__tests__/unit/verifyAction.test.d.ts +2 -0
  180. package/build/__tests__/unit/verifyAction.test.d.ts.map +1 -0
  181. package/build/__tests__/unit/verifyAction.test.js +26 -0
  182. package/build/__tests__/unit/verifyAction.test.js.map +1 -0
  183. package/build/core/android.d.ts +299 -0
  184. package/build/core/android.d.ts.map +1 -0
  185. package/build/core/android.js +1582 -0
  186. package/build/core/android.js.map +1 -0
  187. package/build/core/appDetection.d.ts +20 -0
  188. package/build/core/appDetection.d.ts.map +1 -0
  189. package/build/core/appDetection.js +167 -0
  190. package/build/core/appDetection.js.map +1 -0
  191. package/build/core/bundle.d.ts +49 -0
  192. package/build/core/bundle.d.ts.map +1 -0
  193. package/build/core/bundle.js +356 -0
  194. package/build/core/bundle.js.map +1 -0
  195. package/build/core/componentSearch.d.ts +41 -0
  196. package/build/core/componentSearch.d.ts.map +1 -0
  197. package/build/core/componentSearch.js +588 -0
  198. package/build/core/componentSearch.js.map +1 -0
  199. package/build/core/componentTree.d.ts +13 -0
  200. package/build/core/componentTree.d.ts.map +1 -0
  201. package/build/core/componentTree.js +275 -0
  202. package/build/core/componentTree.js.map +1 -0
  203. package/build/core/config.d.ts +9 -0
  204. package/build/core/config.d.ts.map +1 -0
  205. package/build/core/config.js +28 -0
  206. package/build/core/config.js.map +1 -0
  207. package/build/core/connection.d.ts +102 -0
  208. package/build/core/connection.d.ts.map +1 -0
  209. package/build/core/connection.js +1710 -0
  210. package/build/core/connection.js.map +1 -0
  211. package/build/core/connectionState.d.ts +122 -0
  212. package/build/core/connectionState.d.ts.map +1 -0
  213. package/build/core/connectionState.js +313 -0
  214. package/build/core/connectionState.js.map +1 -0
  215. package/build/core/debugGlobals.d.ts +6 -0
  216. package/build/core/debugGlobals.d.ts.map +1 -0
  217. package/build/core/debugGlobals.js +370 -0
  218. package/build/core/debugGlobals.js.map +1 -0
  219. package/build/core/deviceDiscovery.d.ts +52 -0
  220. package/build/core/deviceDiscovery.d.ts.map +1 -0
  221. package/build/core/deviceDiscovery.js +74 -0
  222. package/build/core/deviceDiscovery.js.map +1 -0
  223. package/build/core/deviceResolver.d.ts +44 -0
  224. package/build/core/deviceResolver.d.ts.map +1 -0
  225. package/build/core/deviceResolver.js +225 -0
  226. package/build/core/deviceResolver.js.map +1 -0
  227. package/build/core/errorScreenParser.d.ts +30 -0
  228. package/build/core/errorScreenParser.d.ts.map +1 -0
  229. package/build/core/errorScreenParser.js +198 -0
  230. package/build/core/errorScreenParser.js.map +1 -0
  231. package/build/core/errors.d.ts +14 -0
  232. package/build/core/errors.d.ts.map +1 -0
  233. package/build/core/errors.js +17 -0
  234. package/build/core/errors.js.map +1 -0
  235. package/build/core/escapeNonAscii.d.ts +18 -0
  236. package/build/core/escapeNonAscii.d.ts.map +1 -0
  237. package/build/core/escapeNonAscii.js +352 -0
  238. package/build/core/escapeNonAscii.js.map +1 -0
  239. package/build/core/exec.d.ts +22 -0
  240. package/build/core/exec.d.ts.map +1 -0
  241. package/build/core/exec.js +54 -0
  242. package/build/core/exec.js.map +1 -0
  243. package/build/core/executor.d.ts +9 -0
  244. package/build/core/executor.d.ts.map +1 -0
  245. package/build/core/executor.js +12 -0
  246. package/build/core/executor.js.map +1 -0
  247. package/build/core/failureArtifact.d.ts +120 -0
  248. package/build/core/failureArtifact.d.ts.map +1 -0
  249. package/build/core/failureArtifact.js +113 -0
  250. package/build/core/failureArtifact.js.map +1 -0
  251. package/build/core/fastRefreshRecorder.d.ts +23 -0
  252. package/build/core/fastRefreshRecorder.d.ts.map +1 -0
  253. package/build/core/fastRefreshRecorder.js +83 -0
  254. package/build/core/fastRefreshRecorder.js.map +1 -0
  255. package/build/core/fastRefreshTools.d.ts +19 -0
  256. package/build/core/fastRefreshTools.d.ts.map +1 -0
  257. package/build/core/fastRefreshTools.js +71 -0
  258. package/build/core/fastRefreshTools.js.map +1 -0
  259. package/build/core/feedback.d.ts +18 -0
  260. package/build/core/feedback.d.ts.map +1 -0
  261. package/build/core/feedback.js +73 -0
  262. package/build/core/feedback.js.map +1 -0
  263. package/build/core/fiberMatch.d.ts +25 -0
  264. package/build/core/fiberMatch.d.ts.map +1 -0
  265. package/build/core/fiberMatch.js +35 -0
  266. package/build/core/fiberMatch.js.map +1 -0
  267. package/build/core/fingerprint.d.ts +4 -0
  268. package/build/core/fingerprint.d.ts.map +1 -0
  269. package/build/core/fingerprint.js +47 -0
  270. package/build/core/fingerprint.js.map +1 -0
  271. package/build/core/focusedInput.d.ts +27 -0
  272. package/build/core/focusedInput.d.ts.map +1 -0
  273. package/build/core/focusedInput.js +110 -0
  274. package/build/core/focusedInput.js.map +1 -0
  275. package/build/core/focusedInputTools.d.ts +21 -0
  276. package/build/core/focusedInputTools.d.ts.map +1 -0
  277. package/build/core/focusedInputTools.js +51 -0
  278. package/build/core/focusedInputTools.js.map +1 -0
  279. package/build/core/format.d.ts +8 -0
  280. package/build/core/format.d.ts.map +1 -0
  281. package/build/core/format.js +34 -0
  282. package/build/core/format.js.map +1 -0
  283. package/build/core/guides.d.ts +21 -0
  284. package/build/core/guides.d.ts.map +1 -0
  285. package/build/core/guides.js +397 -0
  286. package/build/core/guides.js.map +1 -0
  287. package/build/core/iconSemantics.d.ts +18 -0
  288. package/build/core/iconSemantics.d.ts.map +1 -0
  289. package/build/core/iconSemantics.js +114 -0
  290. package/build/core/iconSemantics.js.map +1 -0
  291. package/build/core/imageBuffer.d.ts +37 -0
  292. package/build/core/imageBuffer.d.ts.map +1 -0
  293. package/build/core/imageBuffer.js +58 -0
  294. package/build/core/imageBuffer.js.map +1 -0
  295. package/build/core/index.d.ts +37 -0
  296. package/build/core/index.d.ts.map +1 -0
  297. package/build/core/index.js +66 -0
  298. package/build/core/index.js.map +1 -0
  299. package/build/core/inspector.d.ts +55 -0
  300. package/build/core/inspector.d.ts.map +1 -0
  301. package/build/core/inspector.js +603 -0
  302. package/build/core/inspector.js.map +1 -0
  303. package/build/core/ios.d.ts +263 -0
  304. package/build/core/ios.d.ts.map +1 -0
  305. package/build/core/ios.js +1705 -0
  306. package/build/core/ios.js.map +1 -0
  307. package/build/core/jsExecute.d.ts +62 -0
  308. package/build/core/jsExecute.d.ts.map +1 -0
  309. package/build/core/jsExecute.js +696 -0
  310. package/build/core/jsExecute.js.map +1 -0
  311. package/build/core/license.d.ts +48 -0
  312. package/build/core/license.d.ts.map +1 -0
  313. package/build/core/license.js +267 -0
  314. package/build/core/license.js.map +1 -0
  315. package/build/core/logbox.d.ts +58 -0
  316. package/build/core/logbox.d.ts.map +1 -0
  317. package/build/core/logbox.js +330 -0
  318. package/build/core/logbox.js.map +1 -0
  319. package/build/core/logs.d.ts +44 -0
  320. package/build/core/logs.d.ts.map +1 -0
  321. package/build/core/logs.js +149 -0
  322. package/build/core/logs.js.map +1 -0
  323. package/build/core/measureComponent.d.ts +19 -0
  324. package/build/core/measureComponent.d.ts.map +1 -0
  325. package/build/core/measureComponent.js +94 -0
  326. package/build/core/measureComponent.js.map +1 -0
  327. package/build/core/measureComponentTools.d.ts +6 -0
  328. package/build/core/measureComponentTools.d.ts.map +1 -0
  329. package/build/core/measureComponentTools.js +43 -0
  330. package/build/core/measureComponentTools.js.map +1 -0
  331. package/build/core/metro.d.ts +31 -0
  332. package/build/core/metro.d.ts.map +1 -0
  333. package/build/core/metro.js +124 -0
  334. package/build/core/metro.js.map +1 -0
  335. package/build/core/nativeOnlyHints.d.ts +14 -0
  336. package/build/core/nativeOnlyHints.d.ts.map +1 -0
  337. package/build/core/nativeOnlyHints.js +58 -0
  338. package/build/core/nativeOnlyHints.js.map +1 -0
  339. package/build/core/network.d.ts +43 -0
  340. package/build/core/network.d.ts.map +1 -0
  341. package/build/core/network.js +217 -0
  342. package/build/core/network.js.map +1 -0
  343. package/build/core/networkInterceptor.d.ts +30 -0
  344. package/build/core/networkInterceptor.d.ts.map +1 -0
  345. package/build/core/networkInterceptor.js +211 -0
  346. package/build/core/networkInterceptor.js.map +1 -0
  347. package/build/core/ocr.d.ts +87 -0
  348. package/build/core/ocr.d.ts.map +1 -0
  349. package/build/core/ocr.js +335 -0
  350. package/build/core/ocr.js.map +1 -0
  351. package/build/core/paths.d.ts +2 -0
  352. package/build/core/paths.d.ts.map +1 -0
  353. package/build/core/paths.js +28 -0
  354. package/build/core/paths.js.map +1 -0
  355. package/build/core/posthog.d.ts +5 -0
  356. package/build/core/posthog.d.ts.map +1 -0
  357. package/build/core/posthog.js +52 -0
  358. package/build/core/posthog.js.map +1 -0
  359. package/build/core/pressables.d.ts +46 -0
  360. package/build/core/pressables.d.ts.map +1 -0
  361. package/build/core/pressables.js +1719 -0
  362. package/build/core/pressables.js.map +1 -0
  363. package/build/core/probe.d.ts +12 -0
  364. package/build/core/probe.d.ts.map +1 -0
  365. package/build/core/probe.js +56 -0
  366. package/build/core/probe.js.map +1 -0
  367. package/build/core/redux.d.ts +22 -0
  368. package/build/core/redux.d.ts.map +1 -0
  369. package/build/core/redux.js +116 -0
  370. package/build/core/redux.js.map +1 -0
  371. package/build/core/register.d.ts +8 -0
  372. package/build/core/register.d.ts.map +1 -0
  373. package/build/core/register.js +202 -0
  374. package/build/core/register.js.map +1 -0
  375. package/build/core/rnGlobalsBootstrap.d.ts +16 -0
  376. package/build/core/rnGlobalsBootstrap.d.ts.map +1 -0
  377. package/build/core/rnGlobalsBootstrap.js +116 -0
  378. package/build/core/rnGlobalsBootstrap.js.map +1 -0
  379. package/build/core/screenLayout.d.ts +53 -0
  380. package/build/core/screenLayout.d.ts.map +1 -0
  381. package/build/core/screenLayout.js +878 -0
  382. package/build/core/screenLayout.js.map +1 -0
  383. package/build/core/screenState.d.ts +166 -0
  384. package/build/core/screenState.d.ts.map +1 -0
  385. package/build/core/screenState.js +1352 -0
  386. package/build/core/screenState.js.map +1 -0
  387. package/build/core/sdkBridge.d.ts +67 -0
  388. package/build/core/sdkBridge.d.ts.map +1 -0
  389. package/build/core/sdkBridge.js +148 -0
  390. package/build/core/sdkBridge.js.map +1 -0
  391. package/build/core/state.d.ts +37 -0
  392. package/build/core/state.d.ts.map +1 -0
  393. package/build/core/state.js +124 -0
  394. package/build/core/state.js.map +1 -0
  395. package/build/core/telemetry.d.ts +50 -0
  396. package/build/core/telemetry.d.ts.map +1 -0
  397. package/build/core/telemetry.js +653 -0
  398. package/build/core/telemetry.js.map +1 -0
  399. package/build/core/toolHelpers.d.ts +24 -0
  400. package/build/core/toolHelpers.d.ts.map +1 -0
  401. package/build/core/toolHelpers.js +118 -0
  402. package/build/core/toolHelpers.js.map +1 -0
  403. package/build/core/types.d.ts +162 -0
  404. package/build/core/types.d.ts.map +1 -0
  405. package/build/core/types.js +2 -0
  406. package/build/core/types.js.map +1 -0
  407. package/build/index.d.ts +4 -0
  408. package/build/index.d.ts.map +1 -0
  409. package/build/index.js +132 -0
  410. package/build/index.js.map +1 -0
  411. package/build/pro/screenshot-diff.d.ts +10 -0
  412. package/build/pro/screenshot-diff.d.ts.map +1 -0
  413. package/build/pro/screenshot-diff.js +38 -0
  414. package/build/pro/screenshot-diff.js.map +1 -0
  415. package/build/pro/tap.d.ts +291 -0
  416. package/build/pro/tap.d.ts.map +1 -0
  417. package/build/pro/tap.js +1839 -0
  418. package/build/pro/tap.js.map +1 -0
  419. package/build/pro/verifyAction.d.ts +50 -0
  420. package/build/pro/verifyAction.d.ts.map +1 -0
  421. package/build/pro/verifyAction.js +285 -0
  422. package/build/pro/verifyAction.js.map +1 -0
  423. package/build/tools/_deviceArg.d.ts +45 -0
  424. package/build/tools/_deviceArg.d.ts.map +1 -0
  425. package/build/tools/_deviceArg.js +58 -0
  426. package/build/tools/_deviceArg.js.map +1 -0
  427. package/build/tools/accountTools.d.ts +46 -0
  428. package/build/tools/accountTools.d.ts.map +1 -0
  429. package/build/tools/accountTools.js +223 -0
  430. package/build/tools/accountTools.js.map +1 -0
  431. package/build/tools/bundleTools.d.ts +3 -0
  432. package/build/tools/bundleTools.d.ts.map +1 -0
  433. package/build/tools/bundleTools.js +375 -0
  434. package/build/tools/bundleTools.js.map +1 -0
  435. package/build/tools/componentTools.d.ts +3 -0
  436. package/build/tools/componentTools.d.ts.map +1 -0
  437. package/build/tools/componentTools.js +746 -0
  438. package/build/tools/componentTools.js.map +1 -0
  439. package/build/tools/connectionTools.d.ts +3 -0
  440. package/build/tools/connectionTools.d.ts.map +1 -0
  441. package/build/tools/connectionTools.js +625 -0
  442. package/build/tools/connectionTools.js.map +1 -0
  443. package/build/tools/deviceTools.d.ts +3 -0
  444. package/build/tools/deviceTools.d.ts.map +1 -0
  445. package/build/tools/deviceTools.js +423 -0
  446. package/build/tools/deviceTools.js.map +1 -0
  447. package/build/tools/executionTools.d.ts +3 -0
  448. package/build/tools/executionTools.d.ts.map +1 -0
  449. package/build/tools/executionTools.js +243 -0
  450. package/build/tools/executionTools.js.map +1 -0
  451. package/build/tools/interactionTools.d.ts +3 -0
  452. package/build/tools/interactionTools.d.ts.map +1 -0
  453. package/build/tools/interactionTools.js +539 -0
  454. package/build/tools/interactionTools.js.map +1 -0
  455. package/build/tools/logTools.d.ts +3 -0
  456. package/build/tools/logTools.d.ts.map +1 -0
  457. package/build/tools/logTools.js +371 -0
  458. package/build/tools/logTools.js.map +1 -0
  459. package/build/tools/metaTools.d.ts +7 -0
  460. package/build/tools/metaTools.d.ts.map +1 -0
  461. package/build/tools/metaTools.js +186 -0
  462. package/build/tools/metaTools.js.map +1 -0
  463. package/build/tools/networkTools.d.ts +3 -0
  464. package/build/tools/networkTools.d.ts.map +1 -0
  465. package/build/tools/networkTools.js +478 -0
  466. package/build/tools/networkTools.js.map +1 -0
  467. package/build/tools/reduxTools.d.ts +3 -0
  468. package/build/tools/reduxTools.d.ts.map +1 -0
  469. package/build/tools/reduxTools.js +83 -0
  470. package/build/tools/reduxTools.js.map +1 -0
  471. package/build/tools/screenshotTools.d.ts +3 -0
  472. package/build/tools/screenshotTools.d.ts.map +1 -0
  473. package/build/tools/screenshotTools.js +703 -0
  474. package/build/tools/screenshotTools.js.map +1 -0
  475. package/package.json +63 -2
@@ -0,0 +1,703 @@
1
+ import { z } from "zod";
2
+ import { registerToolWithTelemetry } from "../core/register.js";
3
+ import { resolveAndroidDeviceId, resolveIosUdid } from "./_deviceArg.js";
4
+ import { iconLabel } from "../core/iconSemantics.js";
5
+ import { iosScreenshot, androidScreenshot, detectLogBox, formatLogBoxWarning, recognizeText, inferIOSDevicePixelRatio, getPressableElements, getScreenState, formatScreenStateSummary, imageBuffer, getActiveOrBootedSimulatorUdid, connectedApps, getConnectedAppBySimulatorUdid, getConnectedAppByAndroidDeviceId, iosDescribeAll, detectIOSSystemOverlay, formatIOSSystemOverlayWarning, androidGetStatusBarHeight, androidGetDensity, } from "../core/index.js";
6
+ export function registerScreenshotTools(server) {
7
+ // Tool: iOS screenshot
8
+ registerToolWithTelemetry(server, "ios_screenshot", {
9
+ description: "Take a screenshot from an iOS simulator. Returns the image plus a screen-state summary: active route (name + navigation stack), overlay-grouped tappable elements (pressables behind an open sheet/modal are excluded), component names as JSX tags, labels, testIDs, and frames — all in ready-to-tap pixel coordinates. Prefer tap(text=\"...\") when text is exact and unique; otherwise use tap(x, y) with coordinates from the list — this is the most reliable way to tap icons or visually-identified elements. Use component names for inspect_component/find_components.\n" +
10
+ "PURPOSE: Snapshot what the user sees on iOS AND get tap-ready pressables + a structured component map in one call.\n" +
11
+ "WHEN TO USE: Any visual verification, before/after comparison, or as the starting point for tapping UI by coordinates.\n" +
12
+ "WORKFLOW: ios_screenshot -> pick element from pressables -> tap(x, y) or tap(testID=...) -> ios_screenshot to verify.\n" +
13
+ "LIMITATIONS: Requires a booted iOS simulator (simctl). For physical devices or system dialogs without RN, combine with tap(..., native=true).\n" +
14
+ "GOOD: ios_screenshot()\n" +
15
+ "BAD: ios_screenshot({ udid: \"guess\" }) with a made-up UDID — run list_ios_simulators first.\n" +
16
+ "SEE ALSO: call get_usage_guide(topic=\"interact\") for the full device-interaction playbook.",
17
+ inputSchema: {
18
+ outputPath: z
19
+ .string()
20
+ .optional()
21
+ .describe("Optional path to save the screenshot. If not provided, saves to temp directory."),
22
+ udid: z
23
+ .string()
24
+ .optional()
25
+ .describe("Optional iOS target. Accepts a simulator UDID, the simulator name (e.g. 'iPhone 17 Pro'), or a substring of the connected RN device name. Uses booted simulator if not specified.")
26
+ }
27
+ }, async ({ outputPath, udid }) => {
28
+ const resolved = await resolveIosUdid(udid);
29
+ if (!resolved.ok)
30
+ return resolved.response;
31
+ const result = await iosScreenshot(outputPath, resolved.udid);
32
+ if (!result.success) {
33
+ return {
34
+ content: [
35
+ {
36
+ type: "text",
37
+ text: `Error: ${result.error}`
38
+ }
39
+ ],
40
+ isError: true
41
+ };
42
+ }
43
+ // Include image data if available
44
+ if (result.data) {
45
+ // Build info text with coordinate guidance for iOS
46
+ const pixelWidth = result.originalWidth || 0;
47
+ const pixelHeight = result.originalHeight || 0;
48
+ // Resolve the RN app running on THIS simulator so enrichment pulls
49
+ // fiber/layout data from the right app (not the first-connected one,
50
+ // which may belong to a different simulator).
51
+ const resolvedUdid = udid || (await getActiveOrBootedSimulatorUdid());
52
+ const targetApp = resolvedUdid ? getConnectedAppBySimulatorUdid(resolvedUdid) : null;
53
+ const targetDeviceName = targetApp?.deviceInfo.deviceName;
54
+ // Store screenshot metadata on the matching app (not an arbitrary one)
55
+ if (targetApp) {
56
+ targetApp.lastScreenshot = {
57
+ originalWidth: pixelWidth,
58
+ originalHeight: pixelHeight,
59
+ scaleFactor: result.scaleFactor || 1,
60
+ };
61
+ }
62
+ // Try to get actual screen dimensions and safe area from accessibility tree
63
+ let pointWidth = 0;
64
+ let pointHeight = 0;
65
+ let scaleFactor = 3; // Default to 3x for modern iPhones
66
+ let safeAreaTop = 59; // Default safe area offset
67
+ try {
68
+ const describeResult = await iosDescribeAll(udid);
69
+ if (describeResult.success && describeResult.elements && describeResult.elements.length > 0) {
70
+ // First element is typically the Application with full screen frame
71
+ const rootElement = describeResult.elements[0];
72
+ // Try parsed frame first, then parse AXFrame string
73
+ if (rootElement.frame) {
74
+ pointWidth = Math.round(rootElement.frame.width);
75
+ pointHeight = Math.round(rootElement.frame.height);
76
+ // The frame.y of the root element indicates where content starts (after status bar)
77
+ if (rootElement.frame.y > 0) {
78
+ safeAreaTop = Math.round(rootElement.frame.y);
79
+ }
80
+ }
81
+ else if (rootElement.AXFrame) {
82
+ // Parse format: "{{x, y}, {width, height}}"
83
+ const match = rootElement.AXFrame.match(/\{\{([\d.]+),\s*([\d.]+)\},\s*\{([\d.]+),\s*([\d.]+)\}\}/);
84
+ if (match) {
85
+ const frameY = parseFloat(match[2]);
86
+ pointWidth = Math.round(parseFloat(match[3]));
87
+ pointHeight = Math.round(parseFloat(match[4]));
88
+ if (frameY > 0) {
89
+ safeAreaTop = Math.round(frameY);
90
+ }
91
+ }
92
+ }
93
+ // Calculate actual scale factor
94
+ if (pointWidth > 0) {
95
+ scaleFactor = Math.round(pixelWidth / pointWidth);
96
+ }
97
+ }
98
+ }
99
+ catch {
100
+ // Fallback: use 3x scale for modern devices
101
+ }
102
+ // Fallback if we couldn't get dimensions
103
+ if (pointWidth === 0) {
104
+ pointWidth = Math.round(pixelWidth / scaleFactor);
105
+ pointHeight = Math.round(pixelHeight / scaleFactor);
106
+ }
107
+ const safeAreaOffsetPixels = safeAreaTop * scaleFactor;
108
+ // The Screen Layout tree was previously appended here but produced huge noisy
109
+ // output (nested Svg/G/Path duplicates). Agents should use get_screen_layout
110
+ // explicitly when they need the tree. The Pressable elements block below is
111
+ // the signal most consumers actually want.
112
+ let pressablesText = null;
113
+ let pressablesIsScreenState = false;
114
+ // Enrich with the screen-state summary (route + overlay-grouped pressables —
115
+ // same engine as get_screen_state, so blocked pressables behind sheets are
116
+ // excluded). Requires a connected RN app; otherwise fall back to the flat
117
+ // pressables list (which degrades further to the iOS accessibility tree).
118
+ if (targetApp) {
119
+ try {
120
+ const ssResult = await getScreenState({ device: targetDeviceName });
121
+ if (ssResult.success && ssResult.screenState) {
122
+ const screenshotScale = result.scaleFactor || 1;
123
+ const toPx = (v) => Math.round((v * scaleFactor) / screenshotScale);
124
+ pressablesText = formatScreenStateSummary(ssResult.screenState, (p) => {
125
+ // See enrichScreenshotWithLayout: shift y when fiber reports the
126
+ // element inside the safe-area band (react-native-screens modals).
127
+ const yShift = safeAreaTop > 0 && p.center.y < safeAreaTop ? safeAreaTop : 0;
128
+ return {
129
+ center: { x: toPx(p.center.x), y: toPx(p.center.y + yShift) },
130
+ frame: {
131
+ x: toPx(p.bounds.x),
132
+ y: toPx(p.bounds.y + yShift),
133
+ width: toPx(p.bounds.width),
134
+ height: toPx(p.bounds.height),
135
+ },
136
+ };
137
+ });
138
+ pressablesIsScreenState = true;
139
+ }
140
+ }
141
+ catch {
142
+ // Non-fatal: fall through to the flat pressables list below
143
+ }
144
+ }
145
+ if (!pressablesText) {
146
+ try {
147
+ const pressables = await getPressableElements({
148
+ device: targetDeviceName,
149
+ platform: "ios",
150
+ udid: resolvedUdid ?? undefined
151
+ });
152
+ if (pressables.success && pressables.parsedElements && pressables.parsedElements.length > 0) {
153
+ const screenshotScale = result.scaleFactor || 1;
154
+ pressablesText = pressables.parsedElements.map((el) => {
155
+ // See enrichScreenshotWithLayout: shift y when fiber reports the element
156
+ // inside the safe-area band (a react-native-screens modal artifact).
157
+ let centerYPoints = el.center.y;
158
+ if (safeAreaTop > 0 && centerYPoints < safeAreaTop) {
159
+ centerYPoints += safeAreaTop;
160
+ }
161
+ const px = Math.round((el.center.x * scaleFactor) / screenshotScale);
162
+ const py = Math.round((centerYPoints * scaleFactor) / screenshotScale);
163
+ const label = el.accessibilityLabel || el.text || el.testID || iconLabel(el.component, el.icon) || (el.intent ? `${el.intent} icon` : el.component);
164
+ const idPart = el.testID ? ` testID="${el.testID}"` : "";
165
+ const kindPart = el.isInput ? " [input]" : "";
166
+ const wrapPart = el.isWrapper ? " [wrapper — skip]" : "";
167
+ const nearPart = !el.text && !el.accessibilityLabel && el.nearbyText ? ` near "${el.nearbyText}"` : "";
168
+ return ` (${px}, ${py}) ${el.component}: "${label}"${nearPart}${idPart}${kindPart}${wrapPart}`;
169
+ }).join("\n");
170
+ }
171
+ }
172
+ catch {
173
+ // Non-fatal: screenshot works without pressables enrichment
174
+ }
175
+ }
176
+ const deliveredWidth = result.scaleFactor && result.scaleFactor > 1
177
+ ? Math.round(pixelWidth / result.scaleFactor)
178
+ : pixelWidth;
179
+ const deliveredHeight = result.scaleFactor && result.scaleFactor > 1
180
+ ? Math.round(pixelHeight / result.scaleFactor)
181
+ : pixelHeight;
182
+ let infoText;
183
+ if (result.scaleFactor && result.scaleFactor > 1) {
184
+ infoText = `Screenshot: raw ${pixelWidth}x${pixelHeight} px → delivered ${deliveredWidth}x${deliveredHeight} px (downscaled ${(1 / result.scaleFactor).toFixed(3)}× to fit API limits). Pressable coordinates below are in delivered-image pixels.`;
185
+ }
186
+ else {
187
+ infoText = `Screenshot captured (${pixelWidth}x${pixelHeight} pixels)`;
188
+ }
189
+ infoText += `\n📱 iOS screen: ${pointWidth}x${pointHeight} points (${scaleFactor}x scale)`;
190
+ infoText += `\n📐 tap() handles pixel-to-point conversion automatically — pass pixel coords from this image directly`;
191
+ infoText += `\n⚠️ Status bar + safe area: ${safeAreaTop} points (${safeAreaOffsetPixels} pixels) from top`;
192
+ if (pressablesText) {
193
+ infoText += pressablesIsScreenState
194
+ ? `\n\n🧭 Screen state (route + tappable elements, coordinates in screenshot pixels):\n`
195
+ : `\n\n🎯 Pressable elements (ready-to-tap, coordinates in screenshot pixels):`;
196
+ infoText += `\n${pressablesText}`;
197
+ infoText += `\n\n💡 Next steps:`;
198
+ infoText += `\n • tap(text="Button Label") — when text is exact and unique`;
199
+ infoText += `\n • tap(testID="id") or tap(component="Name") — when you know the identifier`;
200
+ infoText += `\n • tap(x=<px>, y=<px>) — use coordinates from the pressable elements list above (reliable for icons and ambiguous elements)`;
201
+ infoText += `\n • get_screen_layout — full component tree when you need more than pressables`;
202
+ }
203
+ else {
204
+ if (!targetApp && connectedApps.size > 0) {
205
+ infoText += `\n\nℹ️ Pressable enrichment skipped: no RN app is connected to simulator ${resolvedUdid}.`;
206
+ infoText += ` ${connectedApps.size} other app(s) are connected on different device(s) — their fiber data was intentionally not used to avoid mismatched output.`;
207
+ }
208
+ infoText += `\n\n💡 Next steps:`;
209
+ infoText += `\n • tap(text="Button Label") — tap element by visible text`;
210
+ infoText += `\n • tap(x=<px>, y=<px>) — tap at coordinates from this screenshot`;
211
+ infoText += `\n • get_screen_layout — get full UI tree with real on-screen positions`;
212
+ }
213
+ // Check for LogBox overlay — only on the matching RN app; skip otherwise
214
+ // to avoid surfacing warnings from a different simulator's app.
215
+ if (targetApp) {
216
+ try {
217
+ const logBoxState = await detectLogBox(targetDeviceName);
218
+ if (logBoxState && logBoxState.total > 0) {
219
+ infoText += formatLogBoxWarning(logBoxState);
220
+ }
221
+ }
222
+ catch {
223
+ // Non-fatal: LogBox detection failure should not break screenshot
224
+ }
225
+ }
226
+ // I1 (2026-05-16): detect native iOS system overlays (auth sheets, alerts,
227
+ // permission dialogs) that sit on top of the RN app. The pressables list
228
+ // reflects the RN screen underneath; without this warning the agent will
229
+ // happily tap inert RN buttons and loop. Runs whether or not there's a
230
+ // matching RN app — the overlay belongs to the simulator, not the app.
231
+ try {
232
+ const overlay = await detectIOSSystemOverlay(resolvedUdid ?? undefined);
233
+ if (overlay) {
234
+ infoText += formatIOSSystemOverlayWarning(overlay);
235
+ }
236
+ }
237
+ catch {
238
+ // Non-fatal: overlay detection failure should not break screenshot
239
+ }
240
+ imageBuffer.add({
241
+ id: `ios-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
242
+ image: result.data,
243
+ timestamp: Date.now(),
244
+ source: "ios_screenshot",
245
+ metadata: {
246
+ width: result.originalWidth || 0,
247
+ height: result.originalHeight || 0,
248
+ scaleFactor: result.scaleFactor || 1,
249
+ platform: "ios",
250
+ },
251
+ });
252
+ return {
253
+ content: [
254
+ {
255
+ type: "text",
256
+ text: infoText
257
+ },
258
+ {
259
+ type: "image",
260
+ data: result.data.toString("base64"),
261
+ mimeType: "image/jpeg"
262
+ }
263
+ ]
264
+ };
265
+ }
266
+ return {
267
+ content: [
268
+ {
269
+ type: "text",
270
+ text: `Screenshot saved to: ${result.result}`
271
+ }
272
+ ]
273
+ };
274
+ });
275
+ // Tool: Android screenshot
276
+ registerToolWithTelemetry(server, "android_screenshot", {
277
+ description: "Take a screenshot from an Android device/emulator. Returns the image plus a screen-state summary: active route (name + navigation stack), overlay-grouped tappable elements (pressables behind an open sheet/modal are excluded), component names as JSX tags, labels, testIDs, and frames — all in ready-to-tap pixel coordinates. Prefer tap(text=\"...\") when text is exact and unique; otherwise use tap(x, y) with coordinates from the list — this is the most reliable way to tap icons or visually-identified elements. Use component names for inspect_component/find_components.\n" +
278
+ "PURPOSE: Snapshot what the user sees on Android AND get tap-ready pressables + a structured component map in one call.\n" +
279
+ "WHEN TO USE: Any visual verification, before/after comparison, or as the starting point for tapping UI by coordinates on Android.\n" +
280
+ "WORKFLOW: android_screenshot -> pick element from pressables -> tap(x, y) or tap(testID=...) -> android_screenshot to verify.\n" +
281
+ "LIMITATIONS: Requires adb in PATH and a running device/emulator. For non-RN surfaces (system dialogs, permission prompts), combine with tap(..., native=true).\n" +
282
+ "GOOD: android_screenshot()\n" +
283
+ "BAD: android_screenshot({ deviceId: \"guess\" }) with a made-up serial — run list_android_devices first.\n" +
284
+ "SEE ALSO: call get_usage_guide(topic=\"interact\") for the full device-interaction playbook.",
285
+ inputSchema: {
286
+ outputPath: z
287
+ .string()
288
+ .optional()
289
+ .describe("Optional path to save the screenshot. If not provided, saves to temp directory."),
290
+ deviceId: z
291
+ .string()
292
+ .optional()
293
+ .describe("Optional Android target. Accepts an adb serial (e.g. 'emulator-5554', 'RFCX20CLX3F'), an emulator name, or a substring of the connected RN device name (e.g. 'sdk_gphone'). Uses first available device if not specified.")
294
+ }
295
+ }, async ({ outputPath, deviceId }) => {
296
+ const resolved = await resolveAndroidDeviceId(deviceId);
297
+ if (!resolved.ok)
298
+ return resolved.response;
299
+ const result = await androidScreenshot(outputPath, resolved.serial);
300
+ if (!result.success) {
301
+ return {
302
+ content: [
303
+ {
304
+ type: "text",
305
+ text: `Error: ${result.error}`
306
+ }
307
+ ],
308
+ isError: true
309
+ };
310
+ }
311
+ // Include image data if available
312
+ if (result.data) {
313
+ // Build info text with coordinate conversion guidance
314
+ const pixelWidth = result.originalWidth || 0;
315
+ const pixelHeight = result.originalHeight || 0;
316
+ // Resolve the RN app running on THIS Android device so enrichment
317
+ // pulls data from the right app (not whichever app is "first").
318
+ const targetApp = getConnectedAppByAndroidDeviceId(deviceId);
319
+ const targetDeviceName = targetApp?.deviceInfo.deviceName;
320
+ // Store screenshot metadata on the matching app (not an arbitrary one)
321
+ if (targetApp) {
322
+ targetApp.lastScreenshot = {
323
+ originalWidth: pixelWidth,
324
+ originalHeight: pixelHeight,
325
+ scaleFactor: result.scaleFactor || 1,
326
+ };
327
+ }
328
+ const androidDeliveredW = result.scaleFactor && result.scaleFactor > 1
329
+ ? Math.round(pixelWidth / result.scaleFactor)
330
+ : pixelWidth;
331
+ const androidDeliveredH = result.scaleFactor && result.scaleFactor > 1
332
+ ? Math.round(pixelHeight / result.scaleFactor)
333
+ : pixelHeight;
334
+ let infoText = result.scaleFactor && result.scaleFactor > 1
335
+ ? `Screenshot: raw ${pixelWidth}x${pixelHeight} px → delivered ${androidDeliveredW}x${androidDeliveredH} px (downscaled ${(1 / result.scaleFactor).toFixed(3)}× to fit API limits). Pressable coordinates below are in delivered-image pixels.`
336
+ : `Screenshot captured (${pixelWidth}x${pixelHeight} pixels)`;
337
+ // Get status bar height for coordinate guidance
338
+ let statusBarPixels = 63; // Default fallback
339
+ let statusBarDp = 24;
340
+ let densityDpi = 440; // Common default
341
+ try {
342
+ const statusBarResult = await androidGetStatusBarHeight(deviceId);
343
+ if (statusBarResult.success && statusBarResult.heightPixels) {
344
+ statusBarPixels = statusBarResult.heightPixels;
345
+ statusBarDp = statusBarResult.heightDp || 24;
346
+ }
347
+ const densityResult = await androidGetDensity(deviceId);
348
+ if (densityResult.success && densityResult.density) {
349
+ densityDpi = densityResult.density;
350
+ }
351
+ }
352
+ catch {
353
+ // Use defaults
354
+ }
355
+ // Enrich with screen layout data (component names + tap coordinates).
356
+ // On Bridgeless/Fabric Android (the only target architecture we support;
357
+ // legacy arch is <5% of users and not a priority), both code paths that
358
+ // feed this enrichment return DEVICE PIXELS:
359
+ // - fiber path: React's measureInWindow on Fabric returns native pixels.
360
+ // - a11y fallback: uiautomator's bounds are already device pixels.
361
+ // The earlier formula multiplied by densityDpi/160 on the (incorrect)
362
+ // assumption that fiber returned DP — inflating every coordinate by
363
+ // ~2.6× on a 420dpi emulator and producing numbers like (1170, 4054)
364
+ // for a button visually sitting near (445, 1370) in the JPEG. Drop the
365
+ // density factor; only the scaleFactor downscale is needed.
366
+ let pressablesText = null;
367
+ let pressablesIsScreenState = false;
368
+ // Screen Layout tree previously appended here was dropped — it was noisy
369
+ // (nested Svg/G/Path duplicates). Use get_screen_layout when the tree is needed.
370
+ // Prefer the screen-state summary (route + overlay-grouped pressables).
371
+ // Coordinates are fiber dp scaled by density + status-bar offset — the same
372
+ // best-effort conversion as the pressables fallback path (see pressables.ts);
373
+ // tap(text=)/tap(testID=) remain the precise options.
374
+ if (targetApp) {
375
+ try {
376
+ const ssResult = await getScreenState({ device: targetDeviceName });
377
+ if (ssResult.success && ssResult.screenState) {
378
+ const screenshotScale = result.scaleFactor || 1;
379
+ const densityScale = densityDpi / 160;
380
+ const toPx = (v) => Math.round((v * densityScale) / screenshotScale);
381
+ const toPxY = (v) => Math.round((v * densityScale + statusBarPixels) / screenshotScale);
382
+ pressablesText = formatScreenStateSummary(ssResult.screenState, (p) => ({
383
+ center: { x: toPx(p.center.x), y: toPxY(p.center.y) },
384
+ frame: {
385
+ x: toPx(p.bounds.x),
386
+ y: toPxY(p.bounds.y),
387
+ width: toPx(p.bounds.width),
388
+ height: toPx(p.bounds.height),
389
+ },
390
+ }));
391
+ pressablesIsScreenState = true;
392
+ }
393
+ }
394
+ catch {
395
+ // Non-fatal: fall through to the flat pressables list below
396
+ }
397
+ }
398
+ // Fallback: flat pressable elements. With targetApp present this uses the
399
+ // fiber path (uiautomator-reconciled coords). Without targetApp it degrades
400
+ // to the Android accessibility (uiautomator) tree (E1).
401
+ try {
402
+ const pressables = pressablesText ? null : await getPressableElements({ device: targetDeviceName, platform: "android" });
403
+ if (pressables && pressables.success && pressables.parsedElements && pressables.parsedElements.length > 0) {
404
+ const screenshotScale = result.scaleFactor || 1;
405
+ pressablesText = pressables.parsedElements.map((el) => {
406
+ const px = Math.round(el.center.x / screenshotScale);
407
+ const py = Math.round(el.center.y / screenshotScale);
408
+ const label = el.accessibilityLabel || el.text || el.testID || iconLabel(el.component, el.icon) || (el.intent ? `${el.intent} icon` : el.component);
409
+ const idPart = el.testID ? ` testID="${el.testID}"` : "";
410
+ const kindPart = el.isInput ? " [input]" : "";
411
+ const wrapPart = el.isWrapper ? " [wrapper — skip]" : "";
412
+ const nearPart = !el.text && !el.accessibilityLabel && el.nearbyText ? ` near "${el.nearbyText}"` : "";
413
+ return ` (${px}, ${py}) ${el.component}: "${label}"${nearPart}${idPart}${kindPart}${wrapPart}`;
414
+ }).join("\n");
415
+ }
416
+ }
417
+ catch {
418
+ // Non-fatal: screenshot works without pressables enrichment
419
+ }
420
+ infoText += `\n📱 Android uses PIXELS for all coordinates`;
421
+ if (result.scaleFactor && result.scaleFactor > 1) {
422
+ infoText += `\n📐 tap() handles coordinate conversion automatically — pass pixel coords from this image directly`;
423
+ }
424
+ else {
425
+ infoText += `\n📐 Screenshot coords = tap coords (no conversion needed)`;
426
+ }
427
+ infoText += `\n⚠️ Status bar: ${statusBarPixels}px (${statusBarDp}dp) from top - app content starts below this`;
428
+ infoText += `\n📊 Display density: ${densityDpi}dpi`;
429
+ if (pressablesText) {
430
+ infoText += pressablesIsScreenState
431
+ ? `\n\n🧭 Screen state (route + tappable elements, coordinates in screenshot pixels):\n`
432
+ : `\n\n🎯 Pressable elements (ready-to-tap, coordinates in screenshot pixels):`;
433
+ infoText += `\n${pressablesText}`;
434
+ infoText += `\n\n💡 Next steps:`;
435
+ infoText += `\n • tap(text="Button Label") — when text is exact and unique`;
436
+ infoText += `\n • tap(testID="id") or tap(component="Name") — when you know the identifier`;
437
+ infoText += `\n • tap(x=<px>, y=<px>) — use coordinates from the pressable elements list above (reliable for icons and ambiguous elements)`;
438
+ infoText += `\n • get_screen_layout — full component tree when you need more than pressables`;
439
+ }
440
+ else {
441
+ if (!targetApp && connectedApps.size > 0) {
442
+ infoText += `\n\nℹ️ Pressable enrichment skipped: no RN app is connected to device ${deviceId ?? "(default)"}.`;
443
+ infoText += ` ${connectedApps.size} other app(s) are connected on different device(s) — their fiber data was intentionally not used to avoid mismatched output.`;
444
+ }
445
+ infoText += `\n\n💡 Next steps:`;
446
+ infoText += `\n • tap(text="Button Label") — tap element by visible text`;
447
+ infoText += `\n • tap(x=<px>, y=<px>) — tap at coordinates from this screenshot`;
448
+ infoText += `\n • get_screen_layout — get full UI tree with real on-screen positions`;
449
+ }
450
+ // Check for LogBox overlay — only on the matching RN app; skip otherwise
451
+ // to avoid surfacing warnings from a different device's app.
452
+ if (targetApp) {
453
+ try {
454
+ const logBoxState = await detectLogBox(targetDeviceName);
455
+ if (logBoxState && logBoxState.total > 0) {
456
+ infoText += formatLogBoxWarning(logBoxState);
457
+ }
458
+ }
459
+ catch {
460
+ // Non-fatal: LogBox detection failure should not break screenshot
461
+ }
462
+ }
463
+ imageBuffer.add({
464
+ id: `android-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
465
+ image: result.data,
466
+ timestamp: Date.now(),
467
+ source: "android_screenshot",
468
+ metadata: {
469
+ width: result.originalWidth || 0,
470
+ height: result.originalHeight || 0,
471
+ scaleFactor: result.scaleFactor || 1,
472
+ platform: "android",
473
+ },
474
+ });
475
+ return {
476
+ content: [
477
+ {
478
+ type: "text",
479
+ text: infoText
480
+ },
481
+ {
482
+ type: "image",
483
+ data: result.data.toString("base64"),
484
+ mimeType: "image/jpeg"
485
+ }
486
+ ]
487
+ };
488
+ }
489
+ return {
490
+ content: [
491
+ {
492
+ type: "text",
493
+ text: `Screenshot saved to: ${result.result}`
494
+ }
495
+ ]
496
+ };
497
+ });
498
+ // Tool: Get images from shared image buffer
499
+ registerToolWithTelemetry(server, "get_images", {
500
+ description: "Access the shared image buffer containing screenshots from all tools (ios_screenshot, android_screenshot, ocr_screenshot, tap verification). Returns metadata only by default — use id or groupId+frameIndex to retrieve actual image data. Tap burst verification stores frame groups here when burst=true is used.\n" +
501
+ "PURPOSE: Retrieve prior screenshots — especially tap burst frames — without re-taking them, for visual diffing or reviewing transient UI states.\n" +
502
+ "WHEN TO USE: After tap(burst=true) reports transientChangeDetected, or to compare before/after frames without another screenshot round-trip.\n" +
503
+ "WORKFLOW: tap(burst=true) -> note verification.burstGroupId -> get_images(groupId, frameIndex=N) to inspect individual frames.\n" +
504
+ "LIMITATIONS: Circular buffer (50 entries) — old images are evicted. Metadata is cheap; fetching image data is not — request specific ids, not bulk.\n" +
505
+ "GOOD: get_images({ list: true }); get_images({ groupId: \"burst-abc\", frameIndex: 2 })\n" +
506
+ "BAD: get_images() with no filter when buffer is full — floods context. Use list:true or last:N first.\n" +
507
+ "SEE ALSO: call get_usage_guide(topic=\"interact\") for the full interaction playbook.",
508
+ inputSchema: {
509
+ list: z.boolean().optional().describe("List all entries and groups (metadata only, no image data)"),
510
+ id: z.string().optional().describe("Retrieve a specific image by ID (returns image data)"),
511
+ groupId: z.string().optional().describe("List frames in a group (metadata only), or combine with frameIndex to retrieve a specific frame"),
512
+ frameIndex: z.coerce.number().optional().describe("Retrieve a specific frame from a group (requires groupId)"),
513
+ last: z.coerce.number().optional().describe("Return the N most recent entries (metadata only)"),
514
+ source: z.string().optional().describe("Filter entries by source"),
515
+ clear: z.boolean().optional().describe("Clear the buffer")
516
+ }
517
+ }, async ({ id, groupId, frameIndex, last, source, clear }) => {
518
+ if (clear) {
519
+ const count = imageBuffer.clear();
520
+ return {
521
+ content: [{ type: "text", text: `Cleared ${count} images from buffer.` }]
522
+ };
523
+ }
524
+ if (id) {
525
+ const entry = imageBuffer.getById(id);
526
+ if (!entry) {
527
+ return {
528
+ content: [{ type: "text", text: `No image found with id "${id}".` }],
529
+ isError: true
530
+ };
531
+ }
532
+ const { image, ...meta } = entry;
533
+ return {
534
+ content: [
535
+ { type: "text", text: JSON.stringify(meta, null, 2) },
536
+ { type: "image", data: image.toString("base64"), mimeType: "image/png" }
537
+ ]
538
+ };
539
+ }
540
+ if (groupId !== undefined && frameIndex !== undefined) {
541
+ const entry = imageBuffer.getByGroupFrame(groupId, frameIndex);
542
+ if (!entry) {
543
+ return {
544
+ content: [{ type: "text", text: `No frame ${frameIndex} found in group "${groupId}".` }],
545
+ isError: true
546
+ };
547
+ }
548
+ const { image, ...meta } = entry;
549
+ return {
550
+ content: [
551
+ { type: "text", text: JSON.stringify(meta, null, 2) },
552
+ { type: "image", data: image.toString("base64"), mimeType: "image/png" }
553
+ ]
554
+ };
555
+ }
556
+ const entries = imageBuffer.listEntries({ source, groupId, last });
557
+ const groups = imageBuffer.listGroups();
558
+ return {
559
+ content: [
560
+ {
561
+ type: "text",
562
+ text: JSON.stringify({ entries, groups, total: imageBuffer.size }, null, 2)
563
+ }
564
+ ]
565
+ };
566
+ });
567
+ // Tool: OCR Screenshot - Extract text with coordinates from screenshot
568
+ registerToolWithTelemetry(server, "ocr_screenshot", {
569
+ description: "RECOMMENDED: Use this tool FIRST when you need to find and tap UI elements. Takes a screenshot and extracts all visible text with tap-ready coordinates using OCR. " +
570
+ "ADVANTAGES over accessibility trees: (1) Works on ANY visible text regardless of accessibility labels, (2) Returns ready-to-use tapX/tapY coordinates - no conversion needed, (3) Faster than parsing accessibility hierarchies, (4) Works consistently across iOS and Android. " +
571
+ "USE THIS FOR: Finding buttons, labels, menu items, tab bars, or any text you need to tap. Simply find the text in the results and use its tapX/tapY with the tap command.\n" +
572
+ "PURPOSE: Visually locate text on screen and return coordinates safe to pass straight into tap.\n" +
573
+ "WHEN TO USE: Non-RN surfaces, third-party WebViews, accessibility-poor screens, or when fiber/testID strategies have failed.\n" +
574
+ "WORKFLOW: ocr_screenshot(platform=\"ios\") -> scan results for the label -> tap(x=tapX, y=tapY) -> ios_screenshot to verify.\n" +
575
+ "LIMITATIONS: OCR accuracy degrades on very small or stylized text; icons with no label won't appear — use tap(component=...) instead.\n" +
576
+ "GOOD: ocr_screenshot({ platform: \"ios\" })\n" +
577
+ "BAD: ocr_screenshot used just to view the screen — plain ios_screenshot / android_screenshot is cheaper when you don't need OCR text.\n" +
578
+ "SEE ALSO: call get_usage_guide(topic=\"interact\") for the full device-interaction playbook.",
579
+ inputSchema: {
580
+ platform: z.enum(["ios", "android"]).describe("Platform to capture screenshot from"),
581
+ deviceId: z
582
+ .string()
583
+ .optional()
584
+ .describe("Optional device ID (Android) or UDID (iOS). Uses first available device if not specified.")
585
+ }
586
+ }, async ({ platform, deviceId }) => {
587
+ try {
588
+ let screenshotResult;
589
+ if (platform === "android") {
590
+ const r = await resolveAndroidDeviceId(deviceId);
591
+ if (!r.ok)
592
+ return r.response;
593
+ screenshotResult = await androidScreenshot(undefined, r.serial);
594
+ }
595
+ else {
596
+ const r = await resolveIosUdid(deviceId);
597
+ if (!r.ok)
598
+ return r.response;
599
+ screenshotResult = await iosScreenshot(undefined, r.udid);
600
+ }
601
+ if (!screenshotResult.success || !screenshotResult.data) {
602
+ return {
603
+ content: [
604
+ {
605
+ type: "text",
606
+ text: `Screenshot failed: ${screenshotResult.error || "No image data"}`
607
+ }
608
+ ],
609
+ isError: true
610
+ };
611
+ }
612
+ // Calculate device pixel ratio for iOS
613
+ const devicePixelRatio = platform === "ios" && screenshotResult.originalWidth && screenshotResult.originalHeight
614
+ ? inferIOSDevicePixelRatio(screenshotResult.originalWidth, screenshotResult.originalHeight)
615
+ : 1;
616
+ // Run OCR on the screenshot
617
+ const scaleFactor = screenshotResult.scaleFactor || 1;
618
+ const ocrResult = await recognizeText(screenshotResult.data, {
619
+ scaleFactor,
620
+ platform,
621
+ devicePixelRatio
622
+ });
623
+ if (!ocrResult.success) {
624
+ return {
625
+ content: [
626
+ {
627
+ type: "text",
628
+ text: `OCR failed: no text recognized`
629
+ }
630
+ ],
631
+ isError: true
632
+ };
633
+ }
634
+ // Format results for MCP tool output
635
+ const elements = ocrResult.words
636
+ .filter((w) => w.confidence > 50 && w.text.trim().length > 0)
637
+ .map((w) => ({
638
+ text: w.text,
639
+ confidence: Math.round(w.confidence),
640
+ tapX: w.tapCenter.x,
641
+ tapY: w.tapCenter.y
642
+ }));
643
+ const result = {
644
+ platform,
645
+ engine: ocrResult.engine || "unknown",
646
+ processingTimeMs: ocrResult.processingTimeMs,
647
+ fullText: ocrResult.fullText?.trim() || "",
648
+ confidence: Math.round(ocrResult.confidence || 0),
649
+ elementCount: elements.length,
650
+ elements,
651
+ note: "tapX/tapY are in device pixels — pass directly to tap(x, y) for automatic platform conversion"
652
+ };
653
+ // Check for LogBox overlay (uses default CDP device — native deviceId cannot be mapped to CDP device name)
654
+ try {
655
+ const logBoxState = await detectLogBox();
656
+ if (logBoxState && logBoxState.total > 0) {
657
+ result.logBoxWarning = formatLogBoxWarning(logBoxState).trim();
658
+ }
659
+ }
660
+ catch {
661
+ // Non-fatal: LogBox detection failure should not break OCR
662
+ }
663
+ // Store screenshot in image buffer
664
+ try {
665
+ imageBuffer.add({
666
+ id: `ocr-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
667
+ image: screenshotResult.data,
668
+ timestamp: Date.now(),
669
+ source: "ocr_screenshot",
670
+ metadata: {
671
+ width: screenshotResult.originalWidth || 0,
672
+ height: screenshotResult.originalHeight || 0,
673
+ scaleFactor,
674
+ platform,
675
+ },
676
+ });
677
+ }
678
+ catch {
679
+ // Non-fatal: image buffer write failure should not break OCR response
680
+ }
681
+ return {
682
+ content: [
683
+ {
684
+ type: "text",
685
+ text: JSON.stringify(result, null, 2)
686
+ }
687
+ ]
688
+ };
689
+ }
690
+ catch (error) {
691
+ return {
692
+ content: [
693
+ {
694
+ type: "text",
695
+ text: `OCR failed: ${error instanceof Error ? error.message : String(error)}`
696
+ }
697
+ ],
698
+ isError: true
699
+ };
700
+ }
701
+ });
702
+ }
703
+ //# sourceMappingURL=screenshotTools.js.map