rnxsim 0.1.312 → 0.1.314

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 (299) hide show
  1. package/LICENSE +11 -10
  2. package/README.md +5 -0
  3. package/cli/app-config.ts +65 -0
  4. package/cli/app-fonts.ts +408 -0
  5. package/cli/app-project.ts +231 -0
  6. package/cli/app-splash.ts +185 -0
  7. package/cli/app-state-reset.ts +24 -0
  8. package/cli/auth.ts +155 -0
  9. package/cli/bin.ts +594 -0
  10. package/cli/bridge-diagnostics.ts +226 -0
  11. package/cli/bridge-flow-runner.ts +2830 -0
  12. package/cli/browser-evals.ts +96 -0
  13. package/cli/commands/agent-wrapper.ts +986 -0
  14. package/cli/commands/agent.ts +423 -0
  15. package/cli/commands/app-fonts.ts +98 -0
  16. package/cli/commands/assert.ts +541 -0
  17. package/cli/commands/auth.ts +59 -0
  18. package/cli/commands/camera.ts +266 -0
  19. package/cli/commands/cleanup.ts +169 -0
  20. package/cli/commands/compat.ts +87 -0
  21. package/cli/commands/config.ts +32 -0
  22. package/cli/commands/control.ts +2142 -0
  23. package/cli/commands/cpu-profile.ts +269 -0
  24. package/cli/commands/daemon-mac-app.ts +169 -0
  25. package/cli/commands/daemon.ts +874 -0
  26. package/cli/commands/debug.ts +719 -0
  27. package/cli/commands/desktop.ts +39 -0
  28. package/cli/commands/detect.ts +197 -0
  29. package/cli/commands/detox.ts +385 -0
  30. package/cli/commands/device.ts +133 -0
  31. package/cli/commands/diagnose.ts +589 -0
  32. package/cli/commands/electron.ts +95 -0
  33. package/cli/commands/film.ts +379 -0
  34. package/cli/commands/flow.ts +1124 -0
  35. package/cli/commands/inspect/actions.ts +622 -0
  36. package/cli/commands/inspect/core.ts +2405 -0
  37. package/cli/commands/inspect/count.ts +17 -0
  38. package/cli/commands/inspect/describe.ts +192 -0
  39. package/cli/commands/inspect/env.ts +23 -0
  40. package/cli/commands/inspect/find.ts +171 -0
  41. package/cli/commands/inspect/get-layout.ts +39 -0
  42. package/cli/commands/inspect/keyboard.ts +52 -0
  43. package/cli/commands/inspect/list.ts +58 -0
  44. package/cli/commands/inspect/memory.ts +215 -0
  45. package/cli/commands/inspect/redaction.ts +39 -0
  46. package/cli/commands/inspect/resolve-target.ts +82 -0
  47. package/cli/commands/inspect/screens.ts +78 -0
  48. package/cli/commands/inspect/settle.ts +22 -0
  49. package/cli/commands/inspect/settling.ts +158 -0
  50. package/cli/commands/inspect/shared.ts +353 -0
  51. package/cli/commands/inspect/sleep.ts +14 -0
  52. package/cli/commands/inspect/tree.ts +32 -0
  53. package/cli/commands/inspect/url.ts +17 -0
  54. package/cli/commands/inspect/wait-event.ts +210 -0
  55. package/cli/commands/inspect/wait-idle.ts +24 -0
  56. package/cli/commands/inspect/wait-ready.ts +74 -0
  57. package/cli/commands/inspect/wait-selector.ts +54 -0
  58. package/cli/commands/inspect/wait.ts +31 -0
  59. package/cli/commands/inspect.ts +4519 -0
  60. package/cli/commands/install-desktop.ts +351 -0
  61. package/cli/commands/login.ts +331 -0
  62. package/cli/commands/logout.ts +31 -0
  63. package/cli/commands/maestro-generate.ts +361 -0
  64. package/cli/commands/maestro.ts +453 -0
  65. package/cli/commands/mode.ts +57 -0
  66. package/cli/commands/no-bridge-hint.ts +80 -0
  67. package/cli/commands/perf.ts +66 -0
  68. package/cli/commands/permissions.ts +203 -0
  69. package/cli/commands/profile.ts +108 -0
  70. package/cli/commands/react.ts +353 -0
  71. package/cli/commands/record.ts +1434 -0
  72. package/cli/commands/report-issue.ts +305 -0
  73. package/cli/commands/reset.ts +85 -0
  74. package/cli/commands/runtime.ts +351 -0
  75. package/cli/commands/screenshot-command.ts +106 -0
  76. package/cli/commands/screenshot-layers.ts +143 -0
  77. package/cli/commands/screenshot-mode.ts +37 -0
  78. package/cli/commands/screenshot.ts +488 -0
  79. package/cli/commands/screenshots-capture.ts +607 -0
  80. package/cli/commands/screenshots.ts +127 -0
  81. package/cli/commands/serve.ts +168 -0
  82. package/cli/commands/setup.ts +545 -0
  83. package/cli/commands/shell-boolean-mode.ts +81 -0
  84. package/cli/commands/skills.ts +467 -0
  85. package/cli/commands/slides.ts +361 -0
  86. package/cli/commands/state.ts +87 -0
  87. package/cli/commands/storage.ts +58 -0
  88. package/cli/commands/telemetry.ts +54 -0
  89. package/cli/commands/three-mode.ts +763 -0
  90. package/cli/commands/timeline.ts +122 -0
  91. package/cli/commands/upgrade.ts +208 -0
  92. package/cli/commands/upload.ts +1225 -0
  93. package/cli/commands/version.ts +54 -0
  94. package/cli/commands/what-happened.ts +327 -0
  95. package/cli/current-sim.ts +204 -0
  96. package/cli/desktop-companion.ts +300 -0
  97. package/cli/drivers/electron.ts +70 -0
  98. package/cli/drivers/index.ts +20 -0
  99. package/cli/drivers/playwright-provisioning.ts +180 -0
  100. package/cli/drivers/playwright.ts +698 -0
  101. package/cli/drivers/registry.ts +65 -0
  102. package/cli/drivers/types.ts +102 -0
  103. package/cli/flow-file.ts +142 -0
  104. package/cli/flow-live-status.ts +120 -0
  105. package/cli/flow-session.ts +187 -0
  106. package/cli/help.ts +80 -0
  107. package/cli/hidden-runtime-alias.ts +19 -0
  108. package/cli/hints.ts +216 -0
  109. package/cli/inspect-notice-state.ts +114 -0
  110. package/cli/maestro-js.ts +334 -0
  111. package/cli/open-url.ts +8 -0
  112. package/cli/parent-pid.ts +204 -0
  113. package/cli/parse-args.ts +211 -0
  114. package/cli/prompt.ts +51 -0
  115. package/cli/recording-access.ts +107 -0
  116. package/cli/registry.ts +1 -0
  117. package/cli/resolve-assets.ts +63 -0
  118. package/cli/run-registry.ts +226 -0
  119. package/cli/runtime-notes.ts +66 -0
  120. package/cli/runtime-summary.ts +25 -0
  121. package/cli/setup-repository.ts +187 -0
  122. package/cli/telemetry.ts +187 -0
  123. package/cli/ws-bridge.ts +798 -0
  124. package/dist-cli/bin.js +5 -5
  125. package/dist-cli/chunks/{agent-NXVDDCFG.js → agent-7YBDCYMA.js} +2 -2
  126. package/dist-cli/chunks/{agent-wrapper-DXJCFKL3.js → agent-wrapper-2GHFBHCR.js} +2 -2
  127. package/dist-cli/chunks/{app-fonts-W5AJZQJF.js → app-fonts-RSNVPQSU.js} +2 -2
  128. package/dist-cli/chunks/{assert-3LOJEUDT.js → assert-XCMX3XJX.js} +2 -2
  129. package/dist-cli/chunks/{auth-MFTYYPNX.js → auth-B442HRAX.js} +2 -2
  130. package/dist-cli/chunks/{beta-GT3NN67B.js → beta-XJ55JK3M.js} +2 -2
  131. package/dist-cli/chunks/camera-UGYSSLIK.js +33 -0
  132. package/dist-cli/chunks/{chunk-EUAVBTKU.js → chunk-277AEQZX.js} +2 -2
  133. package/dist-cli/chunks/{chunk-5OX7ANRA.js → chunk-2JNSK774.js} +2 -2
  134. package/dist-cli/chunks/{chunk-L24Z3CRS.js → chunk-32WOTSTR.js} +3 -3
  135. package/dist-cli/chunks/{chunk-BKNOLP3J.js → chunk-3S753SNQ.js} +2 -2
  136. package/dist-cli/chunks/{chunk-N6A3SXMR.js → chunk-46ZOLOYA.js} +2 -2
  137. package/dist-cli/chunks/{chunk-GZDHEONW.js → chunk-5L7ELDQL.js} +8 -9
  138. package/dist-cli/chunks/{chunk-YZRFF4YA.js → chunk-7OOPFSQS.js} +2 -2
  139. package/dist-cli/chunks/{chunk-TBMVBI26.js → chunk-7SV3RPRW.js} +2 -2
  140. package/dist-cli/chunks/{chunk-JG6S4RSH.js → chunk-7ZC35MOU.js} +1 -1
  141. package/dist-cli/chunks/chunk-AMG5E6CC.js +9 -0
  142. package/dist-cli/chunks/{chunk-MENV7VLD.js → chunk-APWNH3A4.js} +1 -1
  143. package/dist-cli/chunks/chunk-B57XUKY3.js +4 -0
  144. package/dist-cli/chunks/{chunk-3D57IBPC.js → chunk-C2NL26TD.js} +1 -1
  145. package/dist-cli/chunks/chunk-D2FNUWAB.js +15 -0
  146. package/dist-cli/chunks/{chunk-4BXCZHAU.js → chunk-E5T4XSJ3.js} +2 -2
  147. package/dist-cli/chunks/{chunk-FTC4M26Q.js → chunk-EAC34EQS.js} +1 -1
  148. package/dist-cli/chunks/{chunk-NPVGMH2N.js → chunk-EG32ML36.js} +2 -2
  149. package/dist-cli/chunks/{chunk-DM6KUAOZ.js → chunk-FXUAC6D5.js} +1 -1
  150. package/dist-cli/chunks/chunk-GQSL4USA.js +6 -0
  151. package/dist-cli/chunks/{chunk-U4PN6JOT.js → chunk-HAXW27SS.js} +2 -2
  152. package/dist-cli/chunks/{chunk-ZMZTHU7V.js → chunk-IZAHPAN6.js} +1 -1
  153. package/dist-cli/chunks/{chunk-SI3BUTQR.js → chunk-J62KM5TB.js} +2 -2
  154. package/dist-cli/chunks/{chunk-Y2FSZSO7.js → chunk-JEMCD5E4.js} +1 -1
  155. package/dist-cli/chunks/{chunk-T4FT6CM7.js → chunk-JZS3Q37N.js} +2 -2
  156. package/dist-cli/chunks/{chunk-2SA2VTGP.js → chunk-KR4JON7D.js} +1 -1
  157. package/dist-cli/chunks/chunk-KWCYKQIQ.js +4 -0
  158. package/dist-cli/chunks/{chunk-HG7ORJMT.js → chunk-MCWPL644.js} +2 -2
  159. package/dist-cli/chunks/chunk-MJYK3N2I.js +4 -0
  160. package/dist-cli/chunks/{chunk-YRV7HR3P.js → chunk-O6TRIZNS.js} +2 -2
  161. package/dist-cli/chunks/{chunk-WBT32R6N.js → chunk-P7XL2E73.js} +3 -3
  162. package/dist-cli/chunks/{chunk-MNGQ42QE.js → chunk-PFQTUKQ4.js} +62 -87
  163. package/dist-cli/chunks/{chunk-Q3RGLERJ.js → chunk-PG5RZCTN.js} +2 -2
  164. package/dist-cli/chunks/{chunk-OR3XTXEB.js → chunk-QGRI2Z4M.js} +2 -2
  165. package/dist-cli/chunks/chunk-QLXXE7GE.js +125 -0
  166. package/dist-cli/chunks/{chunk-KZW5WBGU.js → chunk-QOJJJWJE.js} +89 -133
  167. package/dist-cli/chunks/{chunk-BRIZOR4K.js → chunk-RWZY5427.js} +2 -2
  168. package/dist-cli/chunks/{chunk-TYE3WBSR.js → chunk-RZKU2K3J.js} +2 -2
  169. package/dist-cli/chunks/{chunk-7VWOSTD5.js → chunk-SBV4IK4H.js} +1 -1
  170. package/dist-cli/chunks/{chunk-FWYYCUAZ.js → chunk-SGMVFFMK.js} +1 -1
  171. package/dist-cli/chunks/{chunk-LDQ7HVCW.js → chunk-TAX4UT2N.js} +1 -1
  172. package/dist-cli/chunks/{chunk-G74YIEVS.js → chunk-TUOFAWXT.js} +1 -1
  173. package/dist-cli/chunks/{chunk-274UD5FD.js → chunk-UHZLOHGP.js} +1 -1
  174. package/dist-cli/chunks/{chunk-37I5AIS7.js → chunk-VC7V76U3.js} +1 -1
  175. package/dist-cli/chunks/{chunk-B7KMNHX7.js → chunk-VQVMLW4U.js} +1 -1
  176. package/dist-cli/chunks/{chunk-VZBLFMYC.js → chunk-VUKKYPZN.js} +2 -2
  177. package/dist-cli/chunks/{chunk-VUFRB347.js → chunk-WGGRJDRE.js} +1 -1
  178. package/dist-cli/chunks/chunk-X6H76EKP.js +15 -0
  179. package/dist-cli/chunks/chunk-XLZ5FNRT.js +27 -0
  180. package/dist-cli/chunks/{chunk-TCWQJILS.js → chunk-XULEACM4.js} +2 -2
  181. package/dist-cli/chunks/{chunk-XRGEEPJ5.js → chunk-YFSDM7AX.js} +4 -4
  182. package/dist-cli/chunks/chunk-YWI3UEVX.js +5 -0
  183. package/dist-cli/chunks/{chunk-TGQNQ6XT.js → chunk-ZBSJO4NB.js} +10 -9
  184. package/dist-cli/chunks/{cleanup-H3GWQB63.js → cleanup-EYLGCA6Z.js} +2 -2
  185. package/dist-cli/chunks/cli-version-LL2UGIHE.js +4 -0
  186. package/dist-cli/chunks/{compat-5WG2XJV7.js → compat-TLJYHB4E.js} +2 -2
  187. package/dist-cli/chunks/{config-ANMBVVLL.js → config-4JWUOEXK.js} +2 -2
  188. package/dist-cli/chunks/{control-NSGNQFJG.js → control-HPAOYF4N.js} +2 -2
  189. package/dist-cli/chunks/daemon-OYLASXLE.js +4 -0
  190. package/dist-cli/chunks/{debug-CHV2O2KF.js → debug-4BKXF6KI.js} +5 -5
  191. package/dist-cli/chunks/{desktop-FIPIK4CH.js → desktop-ZQ6ZD2S6.js} +3 -3
  192. package/dist-cli/chunks/{detox-CVUJP7K6.js → detox-WNPASSS3.js} +2 -2
  193. package/dist-cli/chunks/{device-6WCUM4W6.js → device-BXLXVG3V.js} +2 -2
  194. package/dist-cli/chunks/{diagnose-I6EMWEII.js → diagnose-3QX7W5RW.js} +2 -2
  195. package/dist-cli/chunks/{disk-cleanup-P27CYFTN.js → disk-cleanup-KSWKI7WB.js} +2 -2
  196. package/dist-cli/chunks/drivers-EPIEFF7P.js +4 -0
  197. package/dist-cli/chunks/{film-UOQFFYNF.js → film-GDMR33VO.js} +3 -3
  198. package/dist-cli/chunks/flow-NKMPBYCJ.js +4 -0
  199. package/dist-cli/chunks/help-OGCBHOKA.js +4 -0
  200. package/dist-cli/chunks/{hidden-runtime-alias-NJYFSJKZ.js → hidden-runtime-alias-S2GTDX3T.js} +2 -2
  201. package/dist-cli/chunks/home-paths-QRCDLTTV.js +4 -0
  202. package/dist-cli/chunks/inspect-FUYMOZPY.js +4 -0
  203. package/dist-cli/chunks/install-desktop-FR6YKW7Y.js +4 -0
  204. package/dist-cli/chunks/{login-BPFQENCX.js → login-IJAPUZHI.js} +4 -4
  205. package/dist-cli/chunks/{logout-5A45ZGY7.js → logout-QMXDFU2X.js} +2 -2
  206. package/dist-cli/chunks/{maestro-E4CSDMLM.js → maestro-ZXU3YCVX.js} +3 -3
  207. package/dist-cli/chunks/{maestro-generate-CKMOJQX2.js → maestro-generate-PYB5QY7K.js} +3 -3
  208. package/dist-cli/chunks/{mode-SZYNMIQF.js → mode-WGUCL5FZ.js} +2 -2
  209. package/dist-cli/chunks/{optional-demo-registry-FRH4SFVG.js → optional-demo-registry-WH2O6H36.js} +2 -2
  210. package/dist-cli/chunks/{perf-VXU2XJAX.js → perf-TOD3UFAH.js} +2 -2
  211. package/dist-cli/chunks/{permissions-IRIAOVND.js → permissions-I5BRJGTB.js} +2 -2
  212. package/dist-cli/chunks/{record-RY7FPLLH.js → record-ZYL2FYSK.js} +3 -3
  213. package/dist-cli/chunks/{report-issue-KNWXOE53.js → report-issue-TAI6DYZO.js} +2 -2
  214. package/dist-cli/chunks/reset-7YGYKQPQ.js +4 -0
  215. package/dist-cli/chunks/runtime-B4JO6QRI.js +4 -0
  216. package/dist-cli/chunks/{screenshot-command-QLALR6GY.js → screenshot-command-7ANLODZY.js} +7 -7
  217. package/dist-cli/chunks/{screenshot-layers-OQFMFVRL.js → screenshot-layers-A7FYXSVU.js} +3 -3
  218. package/dist-cli/chunks/{screenshots-capture-6TSRJFGB.js → screenshots-capture-WT2ZY6CB.js} +2 -2
  219. package/dist-cli/chunks/serve-TG5WKA4V.js +44 -0
  220. package/dist-cli/chunks/{setup-7V6UKX4U.js → setup-PCWLD22V.js} +2 -2
  221. package/dist-cli/chunks/{skills-QQBOSETH.js → skills-27ZHWCQS.js} +2 -2
  222. package/dist-cli/chunks/state-ZQVY46ZO.js +14 -0
  223. package/dist-cli/chunks/{storage-TAPMKK5O.js → storage-OYC57C4X.js} +6 -6
  224. package/dist-cli/chunks/store-32HSPZHI.js +4 -0
  225. package/dist-cli/chunks/telemetry-PBJR7XHR.js +4 -0
  226. package/dist-cli/chunks/{timeline-IAM564NA.js → timeline-RAJJFQT6.js} +2 -2
  227. package/dist-cli/chunks/{upgrade-CD55QXIX.js → upgrade-OEFNZRIP.js} +2 -2
  228. package/dist-cli/chunks/upload-LQJITLKK.js +4 -0
  229. package/dist-cli/chunks/version-NIXTY6PL.js +6 -0
  230. package/dist-cli/chunks/{web-6NW6YLPI.js → web-XBUTBVGR.js} +2 -2
  231. package/dist-cli/chunks/{what-happened-CZZDOOVG.js → what-happened-YNHRFUDX.js} +3 -3
  232. package/dist-lib/agent-daemon-client.cjs +1 -1
  233. package/dist-lib/agent-events.cjs +1 -1
  234. package/dist-lib/agent-identity.cjs +1 -1
  235. package/dist-lib/agent-sessions.cjs +1 -1
  236. package/dist-lib/attached-projects.cjs +1 -1
  237. package/dist-lib/auth/shared-session.cjs +1 -1
  238. package/dist-lib/backend-origin.cjs +1 -1
  239. package/dist-lib/beta.cjs +1 -1
  240. package/dist-lib/beta.mjs +1 -1
  241. package/dist-lib/bridge-constants.cjs +1 -1
  242. package/dist-lib/bridge-contract.cjs +20 -0
  243. package/dist-lib/cli-constants.cjs +1 -1
  244. package/dist-lib/config.cjs +1 -1
  245. package/dist-lib/detox/index.cjs +1 -1
  246. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  247. package/dist-lib/home-paths.cjs +67 -28
  248. package/dist-lib/host/bridge-host.cjs +140 -12
  249. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  250. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  251. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  252. package/dist-lib/host/websocket-proxy.cjs +1 -1
  253. package/dist-lib/index.cjs +2815 -40
  254. package/dist-lib/jump-to-source-babel.cjs +1 -1
  255. package/dist-lib/menu.cjs +1 -1
  256. package/dist-lib/menu.mjs +1 -1
  257. package/dist-lib/metro.cjs +1 -1
  258. package/dist-lib/profiles.cjs +1 -1
  259. package/dist-lib/public-brand.cjs +1 -1
  260. package/dist-lib/render-mode.cjs +1 -1
  261. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  262. package/dist-lib/sdk.cjs +2549 -2061
  263. package/dist-lib/sdk.mjs +2543 -2061
  264. package/dist-lib/skills.cjs +493 -282
  265. package/dist-lib/vite.cjs +1 -1
  266. package/package.json +8 -2
  267. package/src/bridge-constants.ts +3 -4
  268. package/src/bridge-contract.ts +251 -0
  269. package/src/connect.ts +83 -0
  270. package/src/disk-cleanup.ts +30 -0
  271. package/src/home-paths.ts +81 -38
  272. package/src/host/bridge-host.ts +134 -6
  273. package/src/index.ts +27 -1
  274. package/src/sdk.ts +8 -0
  275. package/src/sim-client.ts +660 -0
  276. package/dist-cli/chunks/camera-6EAK7CUL.js +0 -22
  277. package/dist-cli/chunks/chunk-33FKQH54.js +0 -5
  278. package/dist-cli/chunks/chunk-66NF2KNG.js +0 -9
  279. package/dist-cli/chunks/chunk-6UKJB2VC.js +0 -125
  280. package/dist-cli/chunks/chunk-7INBNA3U.js +0 -26
  281. package/dist-cli/chunks/chunk-AY47WKS5.js +0 -23
  282. package/dist-cli/chunks/chunk-HMDOLCKG.js +0 -15
  283. package/dist-cli/chunks/chunk-KR5BGMBZ.js +0 -4
  284. package/dist-cli/chunks/chunk-NLEMQ4PY.js +0 -6
  285. package/dist-cli/chunks/chunk-PEPUGJVI.js +0 -4
  286. package/dist-cli/chunks/cli-version-3QPAJ3LC.js +0 -4
  287. package/dist-cli/chunks/daemon-JHF4BJ3U.js +0 -4
  288. package/dist-cli/chunks/drivers-HQ6AKZT6.js +0 -4
  289. package/dist-cli/chunks/flow-N27MRRNL.js +0 -4
  290. package/dist-cli/chunks/help-SF6FEGEE.js +0 -4
  291. package/dist-cli/chunks/home-paths-XOV44JEF.js +0 -4
  292. package/dist-cli/chunks/inspect-CWFRFQTY.js +0 -4
  293. package/dist-cli/chunks/install-desktop-KLQDNXZQ.js +0 -4
  294. package/dist-cli/chunks/runtime-AR3GJTGK.js +0 -4
  295. package/dist-cli/chunks/serve-F54J2NQO.js +0 -44
  296. package/dist-cli/chunks/store-7NX3S43H.js +0 -4
  297. package/dist-cli/chunks/telemetry-E7BUK6RA.js +0 -4
  298. package/dist-cli/chunks/upload-GZ6LIMYM.js +0 -4
  299. package/dist-cli/chunks/version-3DYJ5AS3.js +0 -6
@@ -0,0 +1,660 @@
1
+ // SimClient — the typed, promise-based client every rnxsim frontend drives a
2
+ // sim through. it wraps a bridge transport (the CLI's ws bridge, or any
3
+ // InspectBridge-shaped channel) and reuses the existing inspect verbs as its
4
+ // implementation; it never re-implements tap resolution, find, settling, or
5
+ // readiness.
6
+ //
7
+ // MUST stay browser-safe: no node builtins, no `process`, no `ws`. the ws
8
+ // transport and `connect()` live in ./connect (node-only) so this class can
9
+ // also back in-page transports later. runtime imports here may only reach the
10
+ // browser-safe inspect kernels.
11
+
12
+ import {
13
+ isTapSuccess,
14
+ tapBest,
15
+ tapById,
16
+ tapByText,
17
+ type TapOutcome,
18
+ type TapTextOptions,
19
+ } from '../cli/commands/inspect/actions'
20
+ import {
21
+ filterLogEntries,
22
+ getShellState,
23
+ inspectAccessibilityTree,
24
+ inspectDescribe,
25
+ inspectErrors,
26
+ inspectFind,
27
+ inspectKeyboard,
28
+ inspectLogs,
29
+ inspectTree,
30
+ inspectUrl,
31
+ inspectWaitReady,
32
+ inspectWarnings,
33
+ waitReadyReason,
34
+ type AccessibilityTreeNode,
35
+ type ConsoleEntry,
36
+ type DescribeResult,
37
+ type FindQuery,
38
+ type InspectBridge,
39
+ type InteractiveNode,
40
+ type KeyboardState,
41
+ type LogEntry,
42
+ type LogFilterOptions,
43
+ type WaitReadyStatus,
44
+ } from '../cli/commands/inspect/core'
45
+ import { resolveTargetCoords } from '../cli/commands/inspect/resolve-target'
46
+ import {
47
+ waitForSootsimIdle,
48
+ type WaitForSootsimIdleResult,
49
+ } from '../cli/commands/inspect/settling'
50
+ import type { BridgeClaimResult, BridgeSimInfo, WsBridge } from '../cli/ws-bridge'
51
+ import type {
52
+ PerformOptions,
53
+ PerformResult,
54
+ PerformStep,
55
+ ResetOptions,
56
+ ResetResult,
57
+ SimStateOptions,
58
+ SimStateResult,
59
+ } from './bridge-contract'
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // typed errors — extend the BridgeSimLockedError precedent so scripts can
63
+ // branch with instanceof instead of parsing message strings.
64
+ // ---------------------------------------------------------------------------
65
+
66
+ export class SimClientError extends Error {
67
+ constructor(message: string) {
68
+ super(message)
69
+ this.name = new.target.name
70
+ }
71
+ }
72
+
73
+ /** no daemon/bridge reachable, no sim connected, or the target sim is gone. */
74
+ export class SimConnectError extends SimClientError {}
75
+
76
+ /** `waitReady` ran out of budget. carries the last probe status. */
77
+ export class SimReadyTimeoutError extends SimClientError {
78
+ status: WaitReadyStatus
79
+ constructor(status: WaitReadyStatus) {
80
+ super(`sim not ready: ${waitReadyReason(status)}`)
81
+ this.status = status
82
+ }
83
+ }
84
+
85
+ /** a tap could not resolve its target or the dispatched press missed. */
86
+ export class SimTapError extends SimClientError {
87
+ outcome: TapOutcome | null
88
+ constructor(message: string, outcome: TapOutcome | null = null) {
89
+ super(message)
90
+ this.outcome = outcome
91
+ }
92
+ }
93
+
94
+ /** `type`/`press` with no visible keyboard (nothing focused to receive it). */
95
+ export class SimKeyboardError extends SimClientError {}
96
+
97
+ /** a `perform` batch reported failure. carries the full step-by-step result. */
98
+ export class SimPerformError extends SimClientError {
99
+ result: PerformResult
100
+ constructor(result: PerformResult) {
101
+ const failed = result.steps.find((step) => !step.ok)
102
+ const at = failed ? `${failed.index} (${failed.type})` : String(result.completed)
103
+ const cause = failed?.error ?? result.error
104
+ super(`perform failed at step ${at}${cause ? `: ${cause}` : ''}`)
105
+ this.result = result
106
+ }
107
+ }
108
+
109
+ /** a `reset` reported failure. carries the engine's result. */
110
+ export class SimResetError extends SimClientError {
111
+ result: ResetResult
112
+ constructor(result: ResetResult) {
113
+ super(`reset (${result.strategy}) failed${result.error ? `: ${result.error}` : ''}`)
114
+ this.result = result
115
+ }
116
+ }
117
+
118
+ // ---------------------------------------------------------------------------
119
+ // public option/target shapes
120
+ // ---------------------------------------------------------------------------
121
+
122
+ export interface SimFindOptions {
123
+ testId?: string
124
+ text?: string
125
+ role?: string
126
+ type?: string
127
+ pressable?: boolean
128
+ }
129
+
130
+ export type SimTapTarget =
131
+ | string
132
+ | { x: number; y: number }
133
+ | { testId: string }
134
+ | ({ text: string } & TapTextOptions)
135
+ | InteractiveNode
136
+
137
+ // the contract deliberately has no text-targeted step (text matching is fuzzy
138
+ // and lives once in the CLI's tap resolution). the SDK accepts one as sugar and
139
+ // resolves it to a coordinate `tap` step client-side before sending ONE batch.
140
+ export type SimPerformStep = PerformStep | { type: 'tapText'; text: string }
141
+
142
+ export interface SimScreenshot {
143
+ dataUrl: string
144
+ bytes: Uint8Array
145
+ }
146
+
147
+ export interface SimClientOptions {
148
+ /** default per-command bridge timeout. */
149
+ commandTimeoutMs?: number
150
+ }
151
+
152
+ function sleep(ms: number): Promise<void> {
153
+ return new Promise((resolve) => setTimeout(resolve, ms))
154
+ }
155
+
156
+ function decodePngDataUrl(dataUrl: unknown): SimScreenshot {
157
+ if (typeof dataUrl !== 'string' || !dataUrl.startsWith('data:image/png;base64,')) {
158
+ throw new SimClientError(
159
+ `screenshot bridge returned a non-png payload: ${JSON.stringify(
160
+ typeof dataUrl === 'string' ? dataUrl.slice(0, 80) : dataUrl,
161
+ )}`,
162
+ )
163
+ }
164
+ const base64 = dataUrl.slice('data:image/png;base64,'.length)
165
+ const binary = atob(base64)
166
+ const bytes = new Uint8Array(binary.length)
167
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)
168
+ return { dataUrl, bytes }
169
+ }
170
+
171
+ // derive the shell base for navigating this sim to a new target, from the
172
+ // sim's current URL: keep the origin and every non-target param (device,
173
+ // renderMode, debug…), drop the params and `/rn` path segment that name the
174
+ // current target. mirrors the CLI open command's base derivation; the shared
175
+ // module extraction is tracked with the driver.
176
+ const TARGET_SEARCH_PARAMS = [
177
+ 'bundle',
178
+ 'demo',
179
+ 'app',
180
+ 'open',
181
+ 'port',
182
+ 'inspectOpen',
183
+ 'appFonts',
184
+ 'appSplash',
185
+ ]
186
+
187
+ export function buildSimOpenUrl(currentUrl: string, target: number | string): string {
188
+ const url = new URL(currentUrl)
189
+ for (const param of TARGET_SEARCH_PARAMS) url.searchParams.delete(param)
190
+ // shell route grammar: an optional /__soot prefix, then /rn, /rn/<port>, or
191
+ // /app/<name>. the current route is replaced wholesale, keeping the prefix.
192
+ const pathname = url.pathname.replace(/\/+$/, '') || '/'
193
+ const prefix =
194
+ pathname === '/__soot' || pathname.startsWith('/__soot/') ? '/__soot' : ''
195
+ const normalized = String(target).trim()
196
+ if (/^\d+$/.test(normalized)) {
197
+ // `/rn/<port>` — the shell's own connect route resolves the metro bundle
198
+ // in-page, so the SDK never grows a second bundle-resolution path.
199
+ url.pathname = `${prefix}/rn/${normalized}`
200
+ } else {
201
+ url.pathname = `${prefix}/rn`
202
+ url.searchParams.set('open', normalized)
203
+ }
204
+ return url.toString()
205
+ }
206
+
207
+ // a `{ text, ...TapTextOptions }` target, as distinct from a node returned by
208
+ // find() (which carries layout/absolutePosition alongside its text).
209
+ function isTextTapTarget(
210
+ target: SimTapTarget,
211
+ ): target is { text: string } & TapTextOptions {
212
+ return (
213
+ typeof target === 'object' &&
214
+ 'text' in target &&
215
+ typeof target.text === 'string' &&
216
+ !('layout' in target) &&
217
+ !('absolutePosition' in target)
218
+ )
219
+ }
220
+
221
+ // ---------------------------------------------------------------------------
222
+ // SimClient
223
+ // ---------------------------------------------------------------------------
224
+
225
+ // implements the full WsBridge surface (not just InspectBridge) so it can be
226
+ // handed to every existing verb and CLI helper unchanged, with each command
227
+ // pinned to the sim this client was connected to.
228
+ export class SimClient implements WsBridge, InspectBridge {
229
+ readonly simId: string
230
+ private readonly bridge: WsBridge
231
+ private closed = false
232
+
233
+ constructor(bridge: WsBridge, simId: string, _options: SimClientOptions = {}) {
234
+ this.bridge = bridge
235
+ this.simId = simId
236
+ }
237
+
238
+ // --- transport (WsBridge/InspectBridge) ---
239
+
240
+ send(
241
+ cmd: { type: string; [k: string]: any },
242
+ opts?: { timeoutMs?: number },
243
+ ): Promise<any> {
244
+ if (this.closed) {
245
+ return Promise.reject(new SimConnectError('sim client is closed'))
246
+ }
247
+ const payload = cmd.simId === undefined ? { ...cmd, simId: this.simId } : cmd
248
+ return this.bridge.send(payload, opts)
249
+ }
250
+
251
+ listSims(): Promise<BridgeSimInfo[]> {
252
+ return this.bridge.listSims()
253
+ }
254
+
255
+ focusSim(simId: string = this.simId): Promise<any> {
256
+ return this.bridge.focusSim(simId)
257
+ }
258
+
259
+ closeSim(simId: string = this.simId): Promise<any> {
260
+ return this.bridge.closeSim(simId)
261
+ }
262
+
263
+ claim(
264
+ simId: string = this.simId,
265
+ opts?: { force?: boolean },
266
+ ): Promise<BridgeClaimResult> {
267
+ return this.bridge.claim(simId, opts)
268
+ }
269
+
270
+ /** release the socket. a script that connected must close to exit cleanly. */
271
+ close(): void {
272
+ if (this.closed) return
273
+ this.closed = true
274
+ this.bridge.close()
275
+ }
276
+
277
+ // --- navigation + readiness ---
278
+
279
+ /**
280
+ * point this sim at a dev target: a metro port (`8081`), or anything the
281
+ * shell's connect flow accepts (`localhost:8081`, an app slug…). resolves
282
+ * once the reloaded page answers on the new target route.
283
+ */
284
+ async open(target: number | string, opts: { timeoutMs?: number } = {}): Promise<void> {
285
+ const sims = await this.listSims()
286
+ const sim = sims.find((entry) => entry.id === this.simId)
287
+ if (!sim) {
288
+ throw new SimConnectError(`sim ${this.simId} is no longer connected`)
289
+ }
290
+ const currentUrl = sim.url ?? sim.origin
291
+ if (!currentUrl) {
292
+ throw new SimConnectError(
293
+ `sim ${this.simId} reported no URL to derive the shell from`,
294
+ )
295
+ }
296
+ const nextUrl = buildSimOpenUrl(currentUrl, target)
297
+ const expected = new URL(nextUrl)
298
+ await this.send({
299
+ type: 'evaluate',
300
+ code: `window.location.href = ${JSON.stringify(nextUrl)}`,
301
+ })
302
+ const deadline = Date.now() + (opts.timeoutMs ?? 30_000)
303
+ while (Date.now() < deadline) {
304
+ await sleep(250)
305
+ try {
306
+ const { url } = await inspectUrl(this)
307
+ const current = new URL(url)
308
+ if (
309
+ current.pathname === expected.pathname &&
310
+ current.searchParams.get('open') === expected.searchParams.get('open')
311
+ ) {
312
+ return
313
+ }
314
+ } catch {
315
+ // page is mid-reload; keep polling until the deadline.
316
+ }
317
+ }
318
+ throw new SimConnectError(
319
+ `timed out waiting for sim ${this.simId} to load ${nextUrl}`,
320
+ )
321
+ }
322
+
323
+ /**
324
+ * block until the guest app has mounted and painted real content.
325
+ * throws SimReadyTimeoutError (with the last probe status) on timeout.
326
+ */
327
+ async waitReady(
328
+ opts: {
329
+ timeoutMs?: number
330
+ onProgress?: (status: WaitReadyStatus) => void
331
+ } = {},
332
+ ): Promise<WaitReadyStatus> {
333
+ const status = await inspectWaitReady(this, opts.timeoutMs ?? 20_000, {
334
+ onProgress: opts.onProgress,
335
+ })
336
+ if (!status.ready) throw new SimReadyTimeoutError(status)
337
+ return status
338
+ }
339
+
340
+ /** wait for layout/animation/transition settle after an interaction. */
341
+ settle(
342
+ opts: { maxMs?: number; strict?: boolean } = {},
343
+ ): Promise<WaitForSootsimIdleResult> {
344
+ return waitForSootsimIdle({
345
+ bridge: this,
346
+ maxMs: opts.maxMs ?? 3_000,
347
+ strict: opts.strict,
348
+ })
349
+ }
350
+
351
+ // --- read verbs ---
352
+
353
+ /** first matching node, or null. query precedence matches the CLI's find. */
354
+ async find(query: SimFindOptions): Promise<InteractiveNode | null> {
355
+ const nodes = await this.findAll(query)
356
+ return nodes[0] ?? null
357
+ }
358
+
359
+ async findAll(query: SimFindOptions): Promise<InteractiveNode[]> {
360
+ const mapped: FindQuery = {
361
+ testId: query.testId ?? null,
362
+ role: query.role ?? null,
363
+ type: query.type ?? null,
364
+ text: query.text ?? null,
365
+ pressable: query.pressable,
366
+ }
367
+ const found = await inspectFind(this, mapped)
368
+ if (!found) {
369
+ throw new SimClientError(
370
+ 'find() needs at least one of testId, text, role, type, or pressable',
371
+ )
372
+ }
373
+ const { result } = found
374
+ if (Array.isArray(result)) return result
375
+ return result ? [result as InteractiveNode] : []
376
+ }
377
+
378
+ async tree(depth = 5): Promise<unknown> {
379
+ const { tree } = await inspectTree(this, depth)
380
+ return tree
381
+ }
382
+
383
+ /** compact visible-UI dump with shell + keyboard state, as the CLI prints. */
384
+ describe(opts: { filter?: string; verbose?: boolean } = {}): Promise<DescribeResult> {
385
+ return inspectDescribe(this, {
386
+ describe: true,
387
+ verbose: opts.verbose ?? false,
388
+ filter: opts.filter ?? '',
389
+ compact: !opts.verbose,
390
+ hideXy: false,
391
+ fullText: true,
392
+ })
393
+ }
394
+
395
+ accessibilityTree(): Promise<AccessibilityTreeNode[]> {
396
+ return inspectAccessibilityTree(this)
397
+ }
398
+
399
+ async logs(opts: LogFilterOptions = {}): Promise<LogEntry[]> {
400
+ return filterLogEntries(await inspectLogs(this), opts)
401
+ }
402
+
403
+ errors(limit = 20): Promise<ConsoleEntry[]> {
404
+ return inspectErrors(this, limit)
405
+ }
406
+
407
+ warnings(limit = 20): Promise<ConsoleEntry[]> {
408
+ return inspectWarnings(this, limit)
409
+ }
410
+
411
+ keyboard(): Promise<KeyboardState | { error: string }> {
412
+ return inspectKeyboard(this)
413
+ }
414
+
415
+ shellState(readyTimeoutMs = 0): Promise<Record<string, any> | null> {
416
+ return getShellState(this, readyTimeoutMs)
417
+ }
418
+
419
+ async url(): Promise<string> {
420
+ const { url } = await inspectUrl(this)
421
+ return url
422
+ }
423
+
424
+ async screenshot(
425
+ opts: {
426
+ layers?: 'full' | 'tenant' | 'shell'
427
+ crop?: { x: number; y: number; w: number; h: number }
428
+ } = {},
429
+ ): Promise<SimScreenshot> {
430
+ const result = await this.send({
431
+ type: 'screenshot',
432
+ ...(opts.layers ? { layers: opts.layers } : {}),
433
+ ...(opts.crop ? { crop: opts.crop } : {}),
434
+ })
435
+ return decodePngDataUrl(result)
436
+ }
437
+
438
+ // --- input verbs ---
439
+
440
+ /**
441
+ * tap by testID, visible text, node, or coordinates. resolution, retry, and
442
+ * hit verification are the CLI's shared tap kernels. throws SimTapError when
443
+ * the target is missing, covered, offscreen, or the press missed.
444
+ */
445
+ async tap(target: SimTapTarget): Promise<void> {
446
+ if (typeof target === 'string') {
447
+ return this.throwUnlessTapped(await tapBest(this, target), target)
448
+ }
449
+ if ('testId' in target && typeof target.testId === 'string') {
450
+ return this.throwUnlessTapped(await tapById(this, target.testId), target.testId)
451
+ }
452
+ if (isTextTapTarget(target)) {
453
+ const { text, ...textOpts } = target
454
+ return this.throwUnlessTapped(await tapByText(this, text, textOpts), text)
455
+ }
456
+ if ('testID' in target && typeof target.testID === 'string') {
457
+ return this.throwUnlessTapped(await tapById(this, target.testID), target.testID)
458
+ }
459
+ const coords = this.coordsForTapTarget(target)
460
+ const result = await this.send({ type: 'tap', x: coords.x, y: coords.y })
461
+ if (!isTapSuccess(result)) {
462
+ throw new SimTapError(
463
+ `tap at ${coords.x},${coords.y} missed: ${JSON.stringify(result)}`,
464
+ )
465
+ }
466
+ }
467
+
468
+ async longPress(
469
+ target: SimTapTarget,
470
+ opts: { durationMs?: number } = {},
471
+ ): Promise<void> {
472
+ const coords = await this.resolveCoords(target)
473
+ const result = await this.send(
474
+ {
475
+ type: 'longPress',
476
+ x: coords.x,
477
+ y: coords.y,
478
+ durationMs: opts.durationMs ?? 600,
479
+ },
480
+ { timeoutMs: (opts.durationMs ?? 600) + 15_000 },
481
+ )
482
+ if (!result?.ok) {
483
+ throw new SimTapError(`longPress at ${coords.x},${coords.y} missed`)
484
+ }
485
+ }
486
+
487
+ /** type through the visual iOS keyboard. requires a focused input. */
488
+ async type(text: string): Promise<void> {
489
+ await this.requireVisibleKeyboard('type')
490
+ await this.send({ type: 'keyboard', action: 'type', text })
491
+ }
492
+
493
+ /** press a keyboard key ('return', 'delete', 'space', …). */
494
+ async press(key: string): Promise<void> {
495
+ await this.requireVisibleKeyboard('press')
496
+ await this.send({ type: 'keyboard', action: 'press', text: key })
497
+ }
498
+
499
+ async dismissKeyboard(): Promise<void> {
500
+ await this.send({ type: 'keyboard', action: 'dismiss' })
501
+ }
502
+
503
+ // --- batched input, compound state, reset ---
504
+
505
+ /**
506
+ * execute an ordered step batch inside the page in ONE round-trip, so waits
507
+ * and gesture physics run on real wall-clock timing. `tapText` steps are
508
+ * resolved to coordinates client-side before the batch is sent. throws
509
+ * SimPerformError (carrying the per-step result) when the batch fails.
510
+ */
511
+ async perform(
512
+ steps: SimPerformStep[],
513
+ options?: PerformOptions,
514
+ ): Promise<PerformResult> {
515
+ const resolved: PerformStep[] = []
516
+ for (const step of steps) {
517
+ if (step.type !== 'tapText') {
518
+ resolved.push(step)
519
+ continue
520
+ }
521
+ const target = await resolveTargetCoords(this, {
522
+ mode: 'text',
523
+ value: step.text,
524
+ })
525
+ if (!target) {
526
+ throw new SimTapError(
527
+ `perform tapText: no node with text ${JSON.stringify(step.text)}`,
528
+ )
529
+ }
530
+ resolved.push({
531
+ type: 'tap',
532
+ x: target.x,
533
+ y: target.y,
534
+ target: {
535
+ testID: target.testID ?? undefined,
536
+ text: target.text ?? undefined,
537
+ type: target.type ?? undefined,
538
+ },
539
+ })
540
+ }
541
+ const batchTimeoutMs = options?.timeoutMs ?? 30_000
542
+ const result = (await this.send(
543
+ {
544
+ type: 'perform',
545
+ steps: resolved,
546
+ ...(options ? { performOptions: options } : {}),
547
+ },
548
+ { timeoutMs: batchTimeoutMs + 5_000 },
549
+ )) as PerformResult
550
+ if (!result?.ok) throw new SimPerformError(result)
551
+ return result
552
+ }
553
+
554
+ /** screenshot + tree + route + recent errors in one round-trip. */
555
+ state(options?: SimStateOptions): Promise<SimStateResult> {
556
+ return this.send({
557
+ type: 'state',
558
+ ...(options ? { stateOptions: options } : {}),
559
+ }) as Promise<SimStateResult>
560
+ }
561
+
562
+ /**
563
+ * two-tier app state wipe ('data' clears storage/caches, 'full' also clears
564
+ * keychain/permissions/native-module state). throws SimResetError on failure.
565
+ */
566
+ async reset(options?: ResetOptions): Promise<ResetResult> {
567
+ const result = (await this.send(
568
+ { type: 'reset', ...(options ? { resetOptions: options } : {}) },
569
+ { timeoutMs: 60_000 },
570
+ )) as ResetResult
571
+ if (!result?.ok) throw new SimResetError(result)
572
+ return result
573
+ }
574
+
575
+ // --- internals ---
576
+
577
+ private throwUnlessTapped(outcome: TapOutcome, label: string): void {
578
+ if (!outcome.failure && isTapSuccess(outcome.result)) return
579
+ const reason =
580
+ outcome.failure === 'not-found'
581
+ ? 'no matching node'
582
+ : outcome.failure === 'missed'
583
+ ? (outcome.result?.reason ?? 'press missed')
584
+ : outcome.payload?.ambiguous
585
+ ? `ambiguous: ${outcome.payload.total} matches`
586
+ : (outcome.result?.reason ?? outcome.payload?.error ?? 'tap failed')
587
+ throw new SimTapError(`tap ${JSON.stringify(label)} failed: ${reason}`, outcome)
588
+ }
589
+
590
+ private coordsForTapTarget(target: SimTapTarget): { x: number; y: number } {
591
+ if (
592
+ typeof target === 'object' &&
593
+ 'x' in target &&
594
+ 'y' in target &&
595
+ typeof target.x === 'number' &&
596
+ typeof target.y === 'number'
597
+ ) {
598
+ return { x: target.x, y: target.y }
599
+ }
600
+ if (
601
+ typeof target === 'object' &&
602
+ 'absolutePosition' in target &&
603
+ target.absolutePosition &&
604
+ target.layout
605
+ ) {
606
+ return {
607
+ x: target.absolutePosition.x + (target.layout.width ?? 0) / 2,
608
+ y: target.absolutePosition.y + (target.layout.height ?? 0) / 2,
609
+ }
610
+ }
611
+ throw new SimClientError(
612
+ 'tap target needs coordinates, a testId, text, or a node from find()',
613
+ )
614
+ }
615
+
616
+ private async resolveCoords(target: SimTapTarget): Promise<{ x: number; y: number }> {
617
+ if (typeof target === 'string') {
618
+ const resolved =
619
+ (await resolveTargetCoords(this, { mode: 'testid', value: target })) ??
620
+ (await resolveTargetCoords(this, { mode: 'text', value: target }))
621
+ if (!resolved) throw new SimTapError(`no node matching ${JSON.stringify(target)}`)
622
+ return resolved
623
+ }
624
+ if ('testId' in target && typeof target.testId === 'string') {
625
+ const resolved = await resolveTargetCoords(this, {
626
+ mode: 'testid',
627
+ value: target.testId,
628
+ })
629
+ if (!resolved) {
630
+ throw new SimTapError(`no node with testID ${JSON.stringify(target.testId)}`)
631
+ }
632
+ return resolved
633
+ }
634
+ if ('text' in target && typeof target.text === 'string' && !('layout' in target)) {
635
+ const resolved = await resolveTargetCoords(this, {
636
+ mode: 'text',
637
+ value: target.text,
638
+ })
639
+ if (!resolved) {
640
+ throw new SimTapError(`no node with text ${JSON.stringify(target.text)}`)
641
+ }
642
+ return resolved
643
+ }
644
+ return this.coordsForTapTarget(target)
645
+ }
646
+
647
+ private async requireVisibleKeyboard(action: string): Promise<void> {
648
+ const deadline = Date.now() + 1_500
649
+ for (;;) {
650
+ const state = await inspectKeyboard(this)
651
+ if (!('error' in state) && state.visible) return
652
+ if (Date.now() >= deadline) {
653
+ throw new SimKeyboardError(
654
+ `${action} requires the iOS keyboard to be visible — focus an input first (tap a TextInput)`,
655
+ )
656
+ }
657
+ await sleep(150)
658
+ }
659
+ }
660
+ }
@@ -1,22 +0,0 @@
1
- /*! rnx v0.1.312 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{m as d}from"./chunk-XRGEEPJ5.js";import"./chunk-TGQNQ6XT.js";import{h as l,j as m,l as p}from"./chunk-7INBNA3U.js";import"./chunk-33FKQH54.js";import"./chunk-7VWOSTD5.js";import"./chunk-66NF2KNG.js";import"./chunk-MENV7VLD.js";function f(){console.log(`
5
- rnxsim do scan \u2014 inject a barcode scan
6
-
7
- usage:
8
- rnxsim do scan <text> [--format <web-format>]
9
-
10
- subcommands:
11
- scan <text> inject a synthetic barcode result into the active scan
12
- session. fails when no guest CameraView is scanning.
13
-
14
- options:
15
- --format <f> BarcodeDetector format string (default: qr_code)
16
- --sim <id> target a specific sim
17
- --port <p> WS bridge port (default: 7668)
18
-
19
- examples:
20
- rnxsim do scan "tm://connect?url=http://10.0.0.5:7777&token=abc"
21
- rnxsim do scan "https://example.com" --format qr_code
22
- `)}async function b(e,g={}){let r=l(e,{port:g.port,stripBooleanFlags:["--help","-h"],stripValueFlags:["--format"]}),a=r.positional,o=a[0];(!o||o==="help"||e.includes("--help")||e.includes("-h"))&&(f(),process.exit(o?0:1)),o!=="scan"&&(console.error(` unknown scan subcommand: ${o}`),f(),process.exit(1));let n=a[1];n||(console.error(" usage: rnxsim do scan <text>"),process.exit(1));let i=e.indexOf("--format"),c=i>=0?e[i+1]:"qr_code",t=m(r);try{await p(t,`globalThis.__sootsimCameraInjectScan?.(${JSON.stringify(n)}, ${JSON.stringify(c)}) ?? false`)?console.log(` injected ${c} scan: ${n}`):(console.error(" no active barcode scan session \u2014 open a screen with a scanning <CameraView> first"),process.exit(1))}catch(s){console.error(` scan failed: ${s instanceof Error?s.message:s}`),await d(t),process.exit(1)}finally{t.close()}}export{b as runCamera};
@@ -1,5 +0,0 @@
1
- /*! rnx v0.1.312 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{a as u,b as c}from"./chunk-7VWOSTD5.js";var w="http://localhost:7668/";var _=5173;var I=2e3;function y(t){let r=t-7668;return r<0||r>I?null:`http://localhost:${_+r}/`}var A=4001,C="sootsim close";import{existsSync as T,mkdirSync as O,readFileSync as d,rmSync as l,writeFileSync as P}from"fs";import{tmpdir as E}from"os";import{dirname as g,join as R,resolve as b}from"path";var p=1,S="SOOTSIM_CLI_CURRENT_SIM_PATH",h=["SOOTSIM_CLI_IDENTITY","TM_SESSION","CLAUDE_CODE_SESSION_ID","CODEX_THREAD_ID","TERM_SESSION_ID","ITERM_SESSION_ID","TMUX_PANE","STY","KITTY_WINDOW_ID","WEZTERM_PANE","ALACRITTY_WINDOW_ID","WINDOWID","VSCODE_INJECTION"],m=/^(?:tmux|screen)|^tm$/i;function a(t,r=20){let e=t,n=u(t);if(!n||n<=1){let o=c(t);return t>1&&o&&!m.test(o)?t:null}for(let o=0;o<r;o++){let s=c(n);if(s&&m.test(s))return e;let i=u(n);if(!i||i<=1)return s?n:e;e=n,n=i}return n}function x(){for(let r of h){let e=process.env[r];if(e&&e.trim())return{key:`${r}:${e.trim()}`,source:r,stable:!0}}let t=a(process.ppid);return t&&t>1?{key:`gppid-${t}`,source:"grand-ppid",stable:!0}:{key:`pid-${process.ppid}`,source:"ppid",stable:!1}}function D(){return x().key}function H(){return a(process.ppid)}function f(){if(process.env[S])return b(process.env[S]);let t=D();return R(E(),`sootsim-current-sim-${t}.json`)}function U(){let t=f();if(!T(t))return null;try{let r=JSON.parse(d(t,"utf8"));return r.version!==p||typeof r.simId!="string"||!r.simId.trim()||typeof r.updatedAt!="string"?(l(t,{force:!0}),null):{version:p,simId:r.simId.trim(),updatedAt:r.updatedAt}}catch{return l(t,{force:!0}),null}}function v(){return U()?.simId||null}function F(t){let r=t.trim();if(!r)return;let e=f();O(g(e),{recursive:!0}),P(e,JSON.stringify({version:p,simId:r,updatedAt:new Date().toISOString()},null,2)+`
5
- `)}function V(){l(f(),{force:!0})}export{w as a,y as b,A as c,C as d,x as e,D as f,H as g,v as h,F as i,V as j};