rnxsim 0.1.366 → 0.1.367

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 (239) hide show
  1. package/README.md +14 -2
  2. package/cli/bin.ts +18 -0
  3. package/cli/bridge-flow-runner.ts +14 -151
  4. package/cli/commands/assert.ts +1 -1
  5. package/cli/commands/box/checkout-plane.ts +126 -0
  6. package/cli/commands/box/rnx-command.ts +68 -0
  7. package/cli/commands/box.ts +200 -0
  8. package/cli/commands/control.ts +12 -32
  9. package/cli/commands/do-chain.ts +412 -0
  10. package/cli/commands/flow.ts +0 -10
  11. package/cli/commands/inspect/core.ts +302 -725
  12. package/cli/commands/inspect/describe.ts +3 -76
  13. package/cli/commands/inspect/find.ts +3 -6
  14. package/cli/commands/inspect/get-layout.ts +14 -1
  15. package/cli/commands/inspect/resolve-target.ts +41 -47
  16. package/cli/commands/inspect/shared.ts +28 -123
  17. package/cli/commands/inspect/tree.ts +6 -15
  18. package/cli/commands/inspect/wait-ready.ts +2 -2
  19. package/cli/commands/inspect/wait-selector.ts +4 -8
  20. package/cli/commands/inspect.ts +23 -17
  21. package/cli/commands/install-desktop.ts +44 -7
  22. package/cli/commands/maestro-generate.ts +0 -2
  23. package/cli/commands/platform.ts +68 -0
  24. package/cli/commands/state.ts +5 -2
  25. package/cli/outbound-endpoints.ts +6 -1
  26. package/cli/parse-args.ts +13 -2
  27. package/cli/ws-bridge.ts +30 -2
  28. package/detox/index.ts +34 -17
  29. package/dist-cli/bin.js +5 -5
  30. package/dist-cli/chunks/{agent-SO6IOPRK.js → agent-EKKIAKP5.js} +2 -2
  31. package/dist-cli/chunks/{agent-wrapper-PMYCB2KO.js → agent-wrapper-MHLQUGA6.js} +2 -2
  32. package/dist-cli/chunks/{app-fonts-LULQGFY7.js → app-fonts-HN6ADRTQ.js} +2 -2
  33. package/dist-cli/chunks/{assert-WGNCD4ZU.js → assert-IMR6QCN6.js} +3 -3
  34. package/dist-cli/chunks/{auth-G7CCHH3M.js → auth-27LPMQNH.js} +2 -2
  35. package/dist-cli/chunks/{beta-N4LXL5TO.js → beta-6RXBB5AI.js} +2 -2
  36. package/dist-cli/chunks/box-DORLF3GP.js +1709 -0
  37. package/dist-cli/chunks/{camera-4JHCYBQS.js → camera-2G7JZ5DL.js} +2 -2
  38. package/dist-cli/chunks/{chunk-2UXU53MG.js → chunk-3QZJLNYY.js} +1 -1
  39. package/dist-cli/chunks/{chunk-YXXKSL6R.js → chunk-3VBVEDDA.js} +1 -1
  40. package/dist-cli/chunks/{chunk-ZJIS3CTW.js → chunk-4BWGLTKU.js} +2 -2
  41. package/dist-cli/chunks/chunk-4XSNO374.js +423 -0
  42. package/dist-cli/chunks/{chunk-RYIMSKDO.js → chunk-5DGMFUZG.js} +2 -2
  43. package/dist-cli/chunks/{chunk-M3BNCF7E.js → chunk-6DBS5EGX.js} +2 -2
  44. package/dist-cli/chunks/{chunk-ONORMKWX.js → chunk-6SO6I3D5.js} +2 -2
  45. package/dist-cli/chunks/chunk-75EFOPXE.js +32 -0
  46. package/dist-cli/chunks/{chunk-5NMCF5VF.js → chunk-A5AJFVOF.js} +1 -1
  47. package/dist-cli/chunks/{chunk-N3UC7HIX.js → chunk-AM2ODYAN.js} +1 -1
  48. package/dist-cli/chunks/{chunk-I6KIM6PN.js → chunk-AVOROGA3.js} +1 -1
  49. package/dist-cli/chunks/chunk-AXMMLO7D.js +130 -0
  50. package/dist-cli/chunks/{chunk-ORWP2E7R.js → chunk-BSEXEYNX.js} +2 -2
  51. package/dist-cli/chunks/{chunk-NQFJOCSF.js → chunk-CM6ZKZW7.js} +2 -2
  52. package/dist-cli/chunks/{chunk-ZMJ75EDG.js → chunk-DNVABCEC.js} +2 -2
  53. package/dist-cli/chunks/{chunk-2S7BKUP2.js → chunk-E33A4AIR.js} +2 -2
  54. package/dist-cli/chunks/{chunk-GXAB52K7.js → chunk-EFMOXYDD.js} +2 -2
  55. package/dist-cli/chunks/{chunk-TIAEYO6I.js → chunk-EV4P5UBP.js} +2 -2
  56. package/dist-cli/chunks/{chunk-JHXETDVC.js → chunk-G5IUIGGC.js} +1 -1
  57. package/dist-cli/chunks/chunk-GMLOCV3E.js +312 -0
  58. package/dist-cli/chunks/{chunk-ZLVRYXZ7.js → chunk-GRN2FHVW.js} +1 -1
  59. package/dist-cli/chunks/{chunk-A5FXX5PJ.js → chunk-HFFXPUEP.js} +1 -1
  60. package/dist-cli/chunks/chunk-HG6T4OOR.js +4 -0
  61. package/dist-cli/chunks/{chunk-OW5XHAA4.js → chunk-HNT2OXPZ.js} +1 -1
  62. package/dist-cli/chunks/chunk-IPEURNLI.js +24 -0
  63. package/dist-cli/chunks/chunk-IR44SFBA.js +5 -0
  64. package/dist-cli/chunks/chunk-JMSLXZFW.js +4 -0
  65. package/dist-cli/chunks/{chunk-EFR5DVNP.js → chunk-JPXQKLNH.js} +1 -1
  66. package/dist-cli/chunks/chunk-K3EC6JDE.js +4 -0
  67. package/dist-cli/chunks/{chunk-YGMEDJKJ.js → chunk-KHX65HAU.js} +2 -2
  68. package/dist-cli/chunks/{chunk-EQK5MJCU.js → chunk-LWOVKHRC.js} +2 -2
  69. package/dist-cli/chunks/{chunk-5OK3LCEJ.js → chunk-MQN5DWTI.js} +2 -2
  70. package/dist-cli/chunks/{chunk-TTASWLKM.js → chunk-N3BXQEUF.js} +1 -1
  71. package/dist-cli/chunks/{chunk-UCHW7ZHR.js → chunk-N5OSV26B.js} +2 -2
  72. package/dist-cli/chunks/chunk-PHNURIH3.js +4 -0
  73. package/dist-cli/chunks/{chunk-AFV32WZT.js → chunk-PP2KNVXP.js} +3 -3
  74. package/dist-cli/chunks/{chunk-XJU2QIRY.js → chunk-PROKNJVY.js} +2 -2
  75. package/dist-cli/chunks/chunk-QXLLY4IM.js +7 -0
  76. package/dist-cli/chunks/{chunk-HT6GFZOZ.js → chunk-RNQTOGM7.js} +1 -1
  77. package/dist-cli/chunks/{chunk-CN6XYYEA.js → chunk-RPNKKVBN.js} +27 -27
  78. package/dist-cli/chunks/{chunk-LN3RNW6X.js → chunk-RSNLI2NH.js} +2 -2
  79. package/dist-cli/chunks/{chunk-YCCCPFWT.js → chunk-RZGHV2WI.js} +2 -2
  80. package/dist-cli/chunks/chunk-SGGL3AFV.js +497 -0
  81. package/dist-cli/chunks/chunk-SH5ETJKG.js +4 -0
  82. package/dist-cli/chunks/{chunk-77ZHQJZR.js → chunk-T6J4YP56.js} +2 -2
  83. package/dist-cli/chunks/{chunk-MGDSJZ37.js → chunk-TWUE7WF5.js} +2 -2
  84. package/dist-cli/chunks/{chunk-SA6XVI55.js → chunk-U44VEQRN.js} +1 -1
  85. package/dist-cli/chunks/{chunk-IZKHOM37.js → chunk-URLVQHC3.js} +1 -1
  86. package/dist-cli/chunks/{chunk-GAJQSITE.js → chunk-VANZFTUE.js} +1 -1
  87. package/dist-cli/chunks/{chunk-EGAGZHO6.js → chunk-VB2YBH46.js} +1 -1
  88. package/dist-cli/chunks/{chunk-H23KWIKA.js → chunk-VFU2SJ7M.js} +2 -2
  89. package/dist-cli/chunks/{chunk-34IQM22N.js → chunk-VUBJ6FXU.js} +15 -15
  90. package/dist-cli/chunks/{chunk-NM2L2QV4.js → chunk-W5PATILQ.js} +2 -2
  91. package/dist-cli/chunks/chunk-WI5V4TYG.js +38 -0
  92. package/dist-cli/chunks/{chunk-6PL2CCGP.js → chunk-XTRHQAOV.js} +2 -2
  93. package/dist-cli/chunks/chunk-XWUTJLBM.js +27 -0
  94. package/dist-cli/chunks/{chunk-5DAIA77C.js → chunk-XYRUQQUR.js} +2 -2
  95. package/dist-cli/chunks/chunk-YBFB3RM7.js +4 -0
  96. package/dist-cli/chunks/chunk-YGQVQ2VG.js +4 -0
  97. package/dist-cli/chunks/{chunk-CTG7HBCA.js → chunk-YINACV32.js} +1 -1
  98. package/dist-cli/chunks/chunk-YV7ERO2V.js +5 -0
  99. package/dist-cli/chunks/{cleanup-KRIATKFH.js → cleanup-GVFHQ3KH.js} +2 -2
  100. package/dist-cli/chunks/cli-version-DPX5MA2M.js +4 -0
  101. package/dist-cli/chunks/{compat-6PTQ3QEA.js → compat-AYRTXYAM.js} +2 -2
  102. package/dist-cli/chunks/{config-ETGDUANO.js → config-K7LOOV62.js} +2 -2
  103. package/dist-cli/chunks/connect-H3CNY4IO.js +4 -0
  104. package/dist-cli/chunks/control-SMHZ2CF5.js +4 -0
  105. package/dist-cli/chunks/daemon-TEZUNI73.js +4 -0
  106. package/dist-cli/chunks/{debug-WIIB6ZKC.js → debug-ELGHG57T.js} +3 -3
  107. package/dist-cli/chunks/{desktop-TD7Q5LNB.js → desktop-4G7FK3MP.js} +3 -3
  108. package/dist-cli/chunks/{detox-7GNR33VM.js → detox-JKXGEJIZ.js} +2 -2
  109. package/dist-cli/chunks/{device-QOR3PGUF.js → device-IV3AR6KG.js} +2 -2
  110. package/dist-cli/chunks/{diagnose-RRCBLP6E.js → diagnose-3ERGF7EW.js} +2 -2
  111. package/dist-cli/chunks/{disk-cleanup-MJUGJPXT.js → disk-cleanup-ALFEVZ4B.js} +2 -2
  112. package/dist-cli/chunks/do-chain-K2B4OHM7.js +5 -0
  113. package/dist-cli/chunks/drivers-KJYXA3M3.js +4 -0
  114. package/dist-cli/chunks/env-2KF6IYZE.js +4 -0
  115. package/dist-cli/chunks/{film-D7Y64LID.js → film-UQXJFAD5.js} +3 -3
  116. package/dist-cli/chunks/flow-ASTUH56E.js +4 -0
  117. package/dist-cli/chunks/help-SNNYFELA.js +4 -0
  118. package/dist-cli/chunks/{hidden-runtime-alias-JGURVR2Z.js → hidden-runtime-alias-VJCWCFVO.js} +2 -2
  119. package/dist-cli/chunks/{home-paths-4ICFIGXO.js → home-paths-EGUZZ6J6.js} +2 -2
  120. package/dist-cli/chunks/inspect-XANAADQT.js +4 -0
  121. package/dist-cli/chunks/install-desktop-JKQA7UY6.js +4 -0
  122. package/dist-cli/chunks/{login-RU33QR2G.js → login-6KQGD7HK.js} +2 -2
  123. package/dist-cli/chunks/{logout-2IUG3Y5J.js → logout-2FCOVLBC.js} +2 -2
  124. package/dist-cli/chunks/{maestro-2MXTGG7I.js → maestro-HPSSEVJD.js} +3 -3
  125. package/dist-cli/chunks/{maestro-generate-2PYIFGLR.js → maestro-generate-KQQ6JHMR.js} +5 -5
  126. package/dist-cli/chunks/{mode-3DAPG3HF.js → mode-SH7KRZET.js} +2 -2
  127. package/dist-cli/chunks/{optional-demo-registry-BWSDJF2Z.js → optional-demo-registry-NAMCAVFX.js} +2 -2
  128. package/dist-cli/chunks/{perf-2TKPWB4N.js → perf-LWKAHXUL.js} +2 -2
  129. package/dist-cli/chunks/{permissions-5CSMHYNA.js → permissions-EZTKWTXV.js} +2 -2
  130. package/dist-cli/chunks/platform-KSOJIVN5.js +6 -0
  131. package/dist-cli/chunks/privacy-EJULWP7L.js +4 -0
  132. package/dist-cli/chunks/{record-I2HJCFP3.js → record-2IC7BAQN.js} +3 -3
  133. package/dist-cli/chunks/{report-issue-A4FZWBOC.js → report-issue-MMX6HOQO.js} +2 -2
  134. package/dist-cli/chunks/reset-PKFZA4NC.js +4 -0
  135. package/dist-cli/chunks/resolve-target-AFKU3BRE.js +4 -0
  136. package/dist-cli/chunks/runtime-XHWWPOIN.js +4 -0
  137. package/dist-cli/chunks/{screenshot-command-UFW5BFHP.js → screenshot-command-JYUXDVSA.js} +3 -3
  138. package/dist-cli/chunks/{screenshot-layers-5BIAL4YN.js → screenshot-layers-OGRY6AA3.js} +2 -2
  139. package/dist-cli/chunks/{screenshots-capture-EHYS3BV2.js → screenshots-capture-QHDN65XS.js} +2 -2
  140. package/dist-cli/chunks/{serve-O4FDM3AC.js → serve-W4BKAG4J.js} +3 -3
  141. package/dist-cli/chunks/setup-WJPMPSAC.js +4 -0
  142. package/dist-cli/chunks/sim-client-UOQGFBPO.js +4 -0
  143. package/dist-cli/chunks/{skills-ZK55SY32.js → skills-OPPXOD63.js} +2 -2
  144. package/dist-cli/chunks/state-TDAN7BOM.js +14 -0
  145. package/dist-cli/chunks/{storage-WP33ISEM.js → storage-CFB2YGLY.js} +2 -2
  146. package/dist-cli/chunks/store-4TEED4ZU.js +4 -0
  147. package/dist-cli/chunks/telemetry-S6N7CV6G.js +4 -0
  148. package/dist-cli/chunks/{timeline-DKGW5S4I.js → timeline-ASOOVP4C.js} +2 -2
  149. package/dist-cli/chunks/{upgrade-LL7G4B7G.js → upgrade-6MVJISHG.js} +2 -2
  150. package/dist-cli/chunks/upload-CBNIRFEW.js +4 -0
  151. package/dist-cli/chunks/{version-YLWR5G4F.js → version-5GS7CWRL.js} +2 -2
  152. package/dist-cli/chunks/{web-QELZ4CP6.js → web-QRRMYMV4.js} +2 -2
  153. package/dist-cli/chunks/welcome-33FXMAMB.js +5 -0
  154. package/dist-cli/chunks/{what-happened-MIOMZWM5.js → what-happened-GAZJGKXX.js} +2 -2
  155. package/dist-cli/chunks/ws-bridge-DIXUAPLQ.js +4 -0
  156. package/dist-lib/agent-daemon-client.cjs +1 -1
  157. package/dist-lib/agent-events.cjs +1 -1
  158. package/dist-lib/agent-identity.cjs +1 -1
  159. package/dist-lib/agent-sessions.cjs +1 -1
  160. package/dist-lib/attached-projects.cjs +1 -1
  161. package/dist-lib/auth/shared-session.cjs +1 -1
  162. package/dist-lib/backend-origin.cjs +1 -1
  163. package/dist-lib/beta.cjs +1 -1
  164. package/dist-lib/beta.mjs +1 -1
  165. package/dist-lib/bridge-constants.cjs +1 -1
  166. package/dist-lib/bridge-contract-input.cjs +336 -0
  167. package/dist-lib/bridge-contract-input.mjs +303 -0
  168. package/dist-lib/bridge-contract.cjs +29 -1
  169. package/dist-lib/bridge-contract.mjs +19 -0
  170. package/dist-lib/cli-constants.cjs +1 -1
  171. package/dist-lib/config.cjs +1 -1
  172. package/dist-lib/detox/index.cjs +32 -18
  173. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  174. package/dist-lib/home-paths.cjs +1 -1
  175. package/dist-lib/host/bridge-host.cjs +5 -1
  176. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  177. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  178. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  179. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  180. package/dist-lib/host/websocket-proxy.cjs +1 -1
  181. package/dist-lib/index.cjs +214 -594
  182. package/dist-lib/jump-to-source-babel.cjs +1 -1
  183. package/dist-lib/menu.cjs +1 -1
  184. package/dist-lib/menu.mjs +1 -1
  185. package/dist-lib/metro-production-bundle.cjs +1 -1
  186. package/dist-lib/metro-production-bundle.mjs +1 -1
  187. package/dist-lib/metro.cjs +1 -1
  188. package/dist-lib/profiles.cjs +1 -1
  189. package/dist-lib/public-brand.cjs +1 -1
  190. package/dist-lib/react-native-host-modules.cjs +1 -1
  191. package/dist-lib/react-native-host-modules.mjs +1 -1
  192. package/dist-lib/render-mode.cjs +1 -1
  193. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  194. package/dist-lib/sdk.cjs +273 -657
  195. package/dist-lib/sdk.mjs +271 -652
  196. package/dist-lib/skills.cjs +190 -287
  197. package/dist-lib/vite.cjs +1 -1
  198. package/package.json +9 -6
  199. package/skills/contrast/SKILL.md +3 -0
  200. package/skills/rnx-debug/SKILL.md +8 -0
  201. package/skills/rnx-test/SKILL.md +21 -0
  202. package/src/bridge-contract-input.ts +359 -0
  203. package/src/bridge-contract.ts +121 -1
  204. package/src/host/bridge-host.ts +7 -12
  205. package/src/index.ts +7 -0
  206. package/src/sim-client.ts +12 -3
  207. package/dist-cli/chunks/chunk-3X6IBUBX.js +0 -38
  208. package/dist-cli/chunks/chunk-7B37IZBN.js +0 -15
  209. package/dist-cli/chunks/chunk-DMPF2D24.js +0 -4
  210. package/dist-cli/chunks/chunk-DW37EVX5.js +0 -767
  211. package/dist-cli/chunks/chunk-DWFIH7CB.js +0 -32
  212. package/dist-cli/chunks/chunk-GUI3PDH2.js +0 -27
  213. package/dist-cli/chunks/chunk-KLF3K6ZZ.js +0 -4
  214. package/dist-cli/chunks/chunk-M5QCGLXR.js +0 -944
  215. package/dist-cli/chunks/chunk-M6SKQGAN.js +0 -5
  216. package/dist-cli/chunks/chunk-NAYLPOC5.js +0 -24
  217. package/dist-cli/chunks/chunk-VIND4SP3.js +0 -124
  218. package/dist-cli/chunks/chunk-XXNLYR64.js +0 -5
  219. package/dist-cli/chunks/chunk-ZFM24DOX.js +0 -4
  220. package/dist-cli/chunks/cli-version-SYGNHGFR.js +0 -4
  221. package/dist-cli/chunks/control-RBTNOLQM.js +0 -4
  222. package/dist-cli/chunks/daemon-N3SC5DK6.js +0 -4
  223. package/dist-cli/chunks/drivers-W6E3BZH4.js +0 -4
  224. package/dist-cli/chunks/flow-7QAE2V5Y.js +0 -4
  225. package/dist-cli/chunks/help-NLD6KWVG.js +0 -4
  226. package/dist-cli/chunks/inspect-C347BG52.js +0 -4
  227. package/dist-cli/chunks/install-desktop-YR6TWZYK.js +0 -4
  228. package/dist-cli/chunks/privacy-PWWC2BXO.js +0 -4
  229. package/dist-cli/chunks/reset-Y3PBPFYA.js +0 -4
  230. package/dist-cli/chunks/runtime-IC6NVVGA.js +0 -4
  231. package/dist-cli/chunks/setup-Y4QGSJBM.js +0 -4
  232. package/dist-cli/chunks/state-KFLAZNEY.js +0 -14
  233. package/dist-cli/chunks/store-VEE22CAY.js +0 -4
  234. package/dist-cli/chunks/telemetry-PZV4BISJ.js +0 -4
  235. package/dist-cli/chunks/upload-Y4C3YZDK.js +0 -4
  236. package/dist-cli/chunks/welcome-ZPKA5PZJ.js +0 -5
  237. package/dist-lib/mcp.cjs +0 -673
  238. package/dist-lib/mcp.mjs +0 -641
  239. package/src/mcp.ts +0 -791
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.366 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.367 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1175,8 +1175,8 @@ var REACT_NATIVE_ROOT_EXPORT_WILL_IT_WORK_COVERAGE, REACT_NATIVE_ROOT_EXPORT_STR
1175
1175
  var init_react_native_root_export_coverage = __esm({
1176
1176
  "../compat/src/generated/react-native-root-export-coverage.ts"() {
1177
1177
  "use strict";
1178
- REACT_NATIVE_ROOT_EXPORT_WILL_IT_WORK_COVERAGE = 0.98;
1179
- REACT_NATIVE_ROOT_EXPORT_STRICT_COVERAGE = 0.653;
1178
+ REACT_NATIVE_ROOT_EXPORT_WILL_IT_WORK_COVERAGE = 0.99;
1179
+ REACT_NATIVE_ROOT_EXPORT_STRICT_COVERAGE = 0.765;
1180
1180
  }
1181
1181
  });
1182
1182
 
@@ -20256,6 +20256,10 @@ var init_bridge_host = __esm({
20256
20256
  return msg?.action === "type" ? "typed text" : "used keyboard";
20257
20257
  case "tree":
20258
20258
  return "dumped tree";
20259
+ case "query":
20260
+ return "queried semantic tree";
20261
+ case "resolve":
20262
+ return "resolved semantic selector";
20259
20263
  case "focus":
20260
20264
  return "focused sim";
20261
20265
  case "close":
@@ -20704,7 +20708,9 @@ var init_current_sim = __esm({
20704
20708
  var ws_bridge_exports = {};
20705
20709
  __export(ws_bridge_exports, {
20706
20710
  BridgeSimLockedError: () => BridgeSimLockedError,
20711
+ RNX_PLACEMENT_ENV: () => RNX_PLACEMENT_ENV,
20707
20712
  bridgePortForShellBaseUrl: () => bridgePortForShellBaseUrl,
20713
+ bridgeWsUrl: () => bridgeWsUrl,
20708
20714
  callInBridge: () => callInBridge,
20709
20715
  callInBridgeWrite: () => callInBridgeWrite,
20710
20716
  checkSimHealth: () => checkSimHealth,
@@ -20715,8 +20721,15 @@ __export(ws_bridge_exports, {
20715
20721
  printBridgeWorldNotice: () => printBridgeWorldNotice,
20716
20722
  resolveBridgeWorld: () => resolveBridgeWorld,
20717
20723
  resolveDefaultBridgePort: () => resolveDefaultBridgePort,
20724
+ resolvePlacement: () => resolvePlacement,
20718
20725
  worldForBridgePort: () => worldForBridgePort
20719
20726
  });
20727
+ function resolvePlacement() {
20728
+ return process.env[RNX_PLACEMENT_ENV] === "local-box" ? "local-box" : "host";
20729
+ }
20730
+ function bridgeWsUrl(wsPort) {
20731
+ return `ws://localhost:${wsPort}`;
20732
+ }
20720
20733
  function liveDevBridgeLockfiles() {
20721
20734
  return readDevBridgeLockfiles().filter((lockfile) => isDevBridgeLockfileFresh(lockfile));
20722
20735
  }
@@ -20937,7 +20950,7 @@ function createBridge(wsPort, opts = {}) {
20937
20950
  let nextId = 1;
20938
20951
  const commandTimeoutMs = opts.commandTimeoutMs ?? 15e3;
20939
20952
  const pending = /* @__PURE__ */ new Map();
20940
- const ws = new import_ws3.WebSocket(`ws://localhost:${wsPort}`);
20953
+ const ws = new import_ws3.WebSocket(bridgeWsUrl(wsPort));
20941
20954
  const identity = getCliIdentity();
20942
20955
  const ready = new Promise((resolve7, reject) => {
20943
20956
  ws.on("open", () => {
@@ -20957,7 +20970,7 @@ function createBridge(wsPort, opts = {}) {
20957
20970
  });
20958
20971
  ws.on("error", (err) => {
20959
20972
  const cause = err.message ? `: ${err.message}` : "";
20960
- reject(new Error(`could not connect to ws://localhost:${wsPort}${cause}`));
20973
+ reject(new Error(`could not connect to ${bridgeWsUrl(wsPort)}${cause}`));
20961
20974
  });
20962
20975
  });
20963
20976
  ready.catch(() => {
@@ -21160,7 +21173,7 @@ async function callInBridge(bridge, path18, ...args) {
21160
21173
  async function callInBridgeWrite(bridge, path18, ...args) {
21161
21174
  return bridge.send({ type: "call", path: path18, args, acquireLock: true });
21162
21175
  }
21163
- var import_node_fs9, import_node_path11, import_ws3, resolvedWorld, BridgeSimLockedError;
21176
+ var import_node_fs9, import_node_path11, import_ws3, RNX_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError;
21164
21177
  var init_ws_bridge = __esm({
21165
21178
  "cli/ws-bridge.ts"() {
21166
21179
  "use strict";
@@ -21171,6 +21184,7 @@ var init_ws_bridge = __esm({
21171
21184
  init_cli_constants();
21172
21185
  init_home_paths();
21173
21186
  init_current_sim();
21187
+ RNX_PLACEMENT_ENV = "RNX_PLACEMENT";
21174
21188
  resolvedWorld = null;
21175
21189
  BridgeSimLockedError = class extends Error {
21176
21190
  lock;
@@ -21890,7 +21904,107 @@ var init_app_splash = __esm({
21890
21904
  }
21891
21905
  });
21892
21906
 
21907
+ // src/bridge-contract.ts
21908
+ var init_bridge_contract = __esm({
21909
+ "src/bridge-contract.ts"() {
21910
+ "use strict";
21911
+ }
21912
+ });
21913
+
21893
21914
  // cli/commands/inspect/core.ts
21915
+ function isSemanticGeometry(value) {
21916
+ return value !== null && typeof value === "object" && [
21917
+ Reflect.get(value, "x"),
21918
+ Reflect.get(value, "y"),
21919
+ Reflect.get(value, "width"),
21920
+ Reflect.get(value, "height")
21921
+ ].every(
21922
+ (coordinate) => typeof coordinate === "number" ? Number.isFinite(coordinate) : false
21923
+ );
21924
+ }
21925
+ function isSimSemanticTree(value) {
21926
+ if (!Array.isArray(value)) return false;
21927
+ return value.every((node) => {
21928
+ if (node === null || typeof node !== "object" || !Number.isInteger(Reflect.get(node, "nodeId")) || typeof Reflect.get(node, "type") !== "string" || !isSemanticGeometry(Reflect.get(node, "geometry"))) {
21929
+ return false;
21930
+ }
21931
+ for (const key of ["testID", "text", "role", "label"]) {
21932
+ const value2 = Reflect.get(node, key);
21933
+ if (value2 !== void 0 && typeof value2 !== "string") return false;
21934
+ }
21935
+ const pressable = Reflect.get(node, "pressable");
21936
+ if (pressable !== void 0 && pressable !== true) return false;
21937
+ const children = Reflect.get(node, "children");
21938
+ return children === void 0 || isSimSemanticTree(children);
21939
+ });
21940
+ }
21941
+ function isSemanticQueryResult(value) {
21942
+ const total = value !== null && typeof value === "object" ? Reflect.get(value, "total") : void 0;
21943
+ return value !== null && typeof value === "object" && typeof total === "number" && Number.isInteger(total) && total >= 0 && isSimSemanticTree(Reflect.get(value, "nodes"));
21944
+ }
21945
+ function globRegex(glob) {
21946
+ const escaped = glob.replace(/[.+^$(){}|[\]\\]/g, "\\$&");
21947
+ return new RegExp(`^${escaped.replace(/\*/g, ".*").replace(/\?/g, ".")}$`, "i");
21948
+ }
21949
+ function formatSemanticTree(nodes, opts = {}) {
21950
+ const filter = opts.filter?.toLowerCase() ?? "";
21951
+ const testIDRegex = opts.testIdLike ? globRegex(opts.testIdLike) : null;
21952
+ const onlyRegex = opts.onlyGlob ? globRegex(opts.onlyGlob) : null;
21953
+ const lines = [];
21954
+ const visit = (node, indent) => {
21955
+ const joined = [node.role, node.label, node.text, node.testID].filter((value) => typeof value === "string").join(" ");
21956
+ const matches = (!filter || joined.toLowerCase().includes(filter)) && (!testIDRegex || testIDRegex.test(node.testID ?? "")) && (!onlyRegex || onlyRegex.test(joined));
21957
+ if (matches) {
21958
+ const parts = [
21959
+ node.role ? `[${node.role}]` : node.pressable ? "[tap]" : `<${node.type}>`
21960
+ ];
21961
+ if (node.text) {
21962
+ const text = opts.fullText || node.text.length <= 50 ? node.text : `${node.text.slice(0, 49).trimEnd()}\u2026`;
21963
+ parts.push(JSON.stringify(text));
21964
+ }
21965
+ if (node.testID) parts.push(`#${node.testID}`);
21966
+ if (node.label && node.label !== node.text) {
21967
+ parts.push(`label=${JSON.stringify(node.label)}`);
21968
+ }
21969
+ if (!opts.hideXy) {
21970
+ const geometry = node.geometry;
21971
+ parts.push(
21972
+ `@(${Math.round(geometry.x)},${Math.round(geometry.y)}) ${Math.round(geometry.width)}x${Math.round(geometry.height)}`
21973
+ );
21974
+ }
21975
+ if (node.pressable) parts.push("(tap)");
21976
+ lines.push(`${" ".repeat(indent + 1)}${parts.join(" ")}`);
21977
+ }
21978
+ for (const child of node.children ?? []) visit(child, indent + 1);
21979
+ };
21980
+ let roots = nodes;
21981
+ if (opts.subtreeRoot) {
21982
+ const pending = [...nodes];
21983
+ let found;
21984
+ while (pending.length > 0 && !found) {
21985
+ const candidate = pending.shift();
21986
+ if (!candidate) continue;
21987
+ if (candidate.testID === opts.subtreeRoot) found = candidate;
21988
+ else pending.push(...candidate.children ?? []);
21989
+ }
21990
+ if (!found) return `__SUBTREE_NOT_FOUND__:${opts.subtreeRoot}`;
21991
+ roots = [found];
21992
+ }
21993
+ for (const root of roots) visit(root, 0);
21994
+ return lines.join("\n");
21995
+ }
21996
+ async function inspectDescribe(bridge, dumpOpts) {
21997
+ const dumpDepth = dumpOpts.maxDepth ?? 12;
21998
+ const result = await bridge.send({
21999
+ type: "query",
22000
+ query: { kind: "tree", depth: dumpDepth }
22001
+ });
22002
+ if (!isSemanticQueryResult(result)) return {};
22003
+ return {
22004
+ tree: formatSemanticTree(result.nodes, dumpOpts),
22005
+ nodeCount: result.total
22006
+ };
22007
+ }
21894
22008
  function scoreLayoutTextContrast(input) {
21895
22009
  const required = input.fontSize >= 18 || input.fontSize >= 14 && (input.fontWeight ?? 400) >= 700 ? 3 : 4.5;
21896
22010
  const { normal, background } = input;
@@ -22104,9 +22218,6 @@ function layoutGetNativeEval(opts = {}) {
22104
22218
  type: typeof n.type === 'string' ? n.type : 'node',
22105
22219
  text,
22106
22220
  box: { x: round(abs.x), y: round(abs.y), w: round(layout.width), h: round(layout.height) },
22107
- borderRadius: 0,
22108
- padding: { t: 0, r: 0, b: 0, l: 0 },
22109
- fontSize: 0,
22110
22221
  }
22111
22222
  if (!includeStyle) return base
22112
22223
  // computedStyle has the resolved values; style is the curated subset \u2014
@@ -22175,11 +22286,56 @@ function layoutGetNativeEval(opts = {}) {
22175
22286
  return elements
22176
22287
  })()`;
22177
22288
  }
22178
- function readyProbeHasContent(probe3) {
22179
- return probe3.liveFrameActive || probe3.targets > 0 || probe3.nodes >= READY_CONTENT_NODE_FLOOR;
22289
+ function waitReadyReason(status) {
22290
+ if (status.bridgeError) {
22291
+ return `sim unreachable: ${status.bridgeError} \u2014 the target tab is closed or no sim is connected (check \`rnx list\`, reopen with \`rnx open\`)`;
22292
+ }
22293
+ const base = status.externalError ? `guest app errored: ${status.externalError}` : status.loadingText ? `still showing "${status.loadingText}"` : status.externalReady === false ? "guest app is still loading" : status.flag !== true && status.targets > 0 ? "native content is rendered but the ready signal has not settled" : status.flag !== true ? "guest app has not emitted sootsim:externalAppReady" : status.targets <= 0 ? "ready flag emitted but no visible app content is inspectable yet" : "node tree is still changing";
22294
+ return !status.externalError && status.suppressedEntryError ? `${base} (suppressed entry error: ${status.suppressedEntryError})` : base;
22180
22295
  }
22181
- function readyProbeHasTargetContent(probe3) {
22182
- return probe3.liveFrameActive || probe3.targets > 0;
22296
+ async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
22297
+ const start = Date.now();
22298
+ let result;
22299
+ try {
22300
+ result = await bridge.send(
22301
+ {
22302
+ type: "waitFor",
22303
+ waitForOptions: { condition: { type: "ready" }, timeoutMs }
22304
+ },
22305
+ { timeoutMs: timeoutMs + 1e3 }
22306
+ );
22307
+ } catch (error) {
22308
+ const bridgeError = error instanceof Error ? error.message : String(error);
22309
+ if (bridgeError.startsWith("multiple sims are connected:") || bridgeError.startsWith("saved sim ")) {
22310
+ throw error;
22311
+ }
22312
+ result = {
22313
+ matched: false,
22314
+ elapsedMs: Date.now() - start,
22315
+ polls: 0,
22316
+ error: bridgeError
22317
+ };
22318
+ }
22319
+ const probe3 = result.probe;
22320
+ const status = {
22321
+ ready: result.matched,
22322
+ elapsedMs: result.elapsedMs,
22323
+ nodes: probe3?.nodes ?? 0,
22324
+ targets: probe3?.targets ?? 0,
22325
+ liveFrameActive: probe3?.liveFrameActive ?? false,
22326
+ liveFrameChannels: probe3?.liveFrameChannels ?? 0,
22327
+ liveFramePublishes: probe3?.liveFramePublishes ?? 0,
22328
+ flag: probe3?.flag,
22329
+ loadingText: probe3?.loadingText ?? "",
22330
+ externalReady: probe3?.externalReady ?? null,
22331
+ externalStatus: probe3?.externalStatus ?? "",
22332
+ externalError: probe3?.externalError ?? "",
22333
+ suppressedEntryError: probe3?.suppressedEntryError ?? "",
22334
+ errors: probe3?.errors ?? 0,
22335
+ bridgeError: result.error ?? ""
22336
+ };
22337
+ if (!status.ready && options.onProgress) options.onProgress(status);
22338
+ return status;
22183
22339
  }
22184
22340
  function consoleEntriesEval(level, limit) {
22185
22341
  const getter = level === "error" ? "getErrors" : "getWarnings";
@@ -22233,156 +22389,12 @@ async function getShellState(bridge, readyTimeoutMs = 0) {
22233
22389
  }
22234
22390
  }
22235
22391
  }
22236
- var LAYOUT_GET_NATIVE_EVAL, WAIT_READY_PROBE, READY_CONTENT_NODE_FLOOR, READY_NODE_STABLE_MS, READY_PROBE_POLL_MS, MERGED_CONSOLE_COUNT_EVAL;
22392
+ var LAYOUT_GET_NATIVE_EVAL, MERGED_CONSOLE_COUNT_EVAL;
22237
22393
  var init_core = __esm({
22238
22394
  "cli/commands/inspect/core.ts"() {
22239
22395
  "use strict";
22396
+ init_bridge_contract();
22240
22397
  LAYOUT_GET_NATIVE_EVAL = layoutGetNativeEval();
22241
- WAIT_READY_PROBE = `(async () => {
22242
- const t = window.__sootsimTest
22243
- let nodes = 0
22244
- try { nodes = (await t?.getNodeCount?.()) || 0 } catch {}
22245
- let targets = 0
22246
- let liveFrameActive = false
22247
- let liveFrameChannels = 0
22248
- let liveFramePublishes = 0
22249
- let loadingText = ''
22250
- const cleanText = (value) =>
22251
- typeof value === 'string' ? value.replace(/\\s+/g, ' ').trim() : ''
22252
- const loadingSignal = (value) => {
22253
- const text = cleanText(value)
22254
- if (!text) return ''
22255
- return /^(opening app\\b|looking for your app\\b|building bundle\\b|preparing rnx runtime\\b|loading native bundle\\b|loading rnx stubs\\b|loading bundle loader\\b|loading native stubs\\b|loading compat bridge\\b|loading react-native shim\\b|loading react bridge\\b|loading reconciler\\b|loading replacement modules\\b|injecting rnx modules\\b|warming sqlite engine\\b|capturing root component\\b|reloading app\\b|loading(?:[.!\u2026]*)?$|connecting(?:[.!\u2026]*)?$)/i.test(text)
22256
- ? text
22257
- : ''
22258
- }
22259
- try {
22260
- const loading = window.__sootsimConnectRNLoading
22261
- if (loading && loading.active === true) {
22262
- loadingText = [loading.title, loading.detail].map(cleanText).filter(Boolean).join(' ')
22263
- }
22264
- } catch {}
22265
- try {
22266
- if (typeof t?.listInspectable === 'function') {
22267
- const list = await t.listInspectable({})
22268
- if (Array.isArray(list)) {
22269
- const screenW = window.innerWidth || 0
22270
- const screenH = window.innerHeight || 0
22271
- const isVisible = (n) => {
22272
- const layout = n?.layout
22273
- if (!layout || layout.width <= 0 || layout.height <= 0) return false
22274
- const abs = n?.absolutePosition || n?.absolute
22275
- if (!abs) return true
22276
- const device = n?.device || {}
22277
- const w = Number(device.width) || screenW
22278
- const h = Number(device.height) || screenH
22279
- return (
22280
- abs.x + layout.width > 0 &&
22281
- abs.y + layout.height > 0 &&
22282
- abs.x < w &&
22283
- abs.y < h
22284
- )
22285
- }
22286
- const hasContentSignal = (n) => {
22287
- const role = n?.accessibilityRole || n?.role
22288
- const label = cleanText(n?.accessibilityLabel || n?.label)
22289
- const handlers = Array.isArray(n?.handlers) ? n.handlers : []
22290
- const text = cleanText(n?.text)
22291
- const testID = cleanText(n?.testID || n?.testId)
22292
- const loading = loadingSignal(text) || loadingSignal(label) || loadingSignal(testID)
22293
- if (!loadingText && loading) {
22294
- loadingText = loading
22295
- }
22296
- if (handlers.length > 0) return true
22297
- if (role) return true
22298
- if (n?.isTextInput) return true
22299
- if (text) return true
22300
- if (label) return true
22301
- if (testID && !/^splash|loading/i.test(testID)) return true
22302
- return false
22303
- }
22304
- targets = list.filter(n => isVisible(n) && hasContentSignal(n)).length
22305
- }
22306
- }
22307
- } catch {}
22308
- try {
22309
- const stats = await t?.getImageLoaderStats?.()
22310
- const presentation = stats?.liveFramePresentation
22311
- liveFrameChannels = Number(presentation?.activeChannels) || 0
22312
- liveFramePublishes = Number(presentation?.publishes) || 0
22313
- const previous = window.__sootsimWaitReadyLiveFramePublishes
22314
- liveFrameActive =
22315
- liveFrameChannels > 0 &&
22316
- typeof previous === 'number' &&
22317
- liveFramePublishes > previous
22318
- window.__sootsimWaitReadyLiveFramePublishes = liveFramePublishes
22319
- } catch {}
22320
- let externalReady = null
22321
- let externalStatus = ''
22322
- let externalError = ''
22323
- let suppressedEntryError = ''
22324
- try {
22325
- const getExternalAppState = t?.getExternalAppState
22326
- const external = typeof getExternalAppState === 'function'
22327
- ? await Promise.race([
22328
- getExternalAppState(),
22329
- new Promise((resolve) => setTimeout(
22330
- () => resolve({
22331
- state: {
22332
- ready: false,
22333
- loading: true,
22334
- status: 'waiting for tenant worker...',
22335
- },
22336
- }),
22337
- 1000,
22338
- )),
22339
- ])
22340
- : null
22341
- const state = external?.state
22342
- if (state && typeof state === 'object') {
22343
- externalReady = state.ready === true
22344
- if (typeof state.status === 'string') externalStatus = state.status
22345
- if (typeof state.error === 'string') externalError = state.error
22346
- }
22347
- if (!externalError && Array.isArray(external?.entryErrors)) {
22348
- // suppressed-and-continued entry errors (bundle-loader's patched __r
22349
- // swallows entry throws and moves on) must NOT gate readiness \u2014 the
22350
- // app frequently runs fine after one (e.g. metro's "Unknown named
22351
- // module" probing noise). they surface separately as a diagnostic
22352
- // for the timeout path.
22353
- const isReal = (item) => item && typeof item.message === 'string' && item.message.trim()
22354
- const fatal = external.entryErrors.find((item) => isReal(item) && item.suppressed !== true)
22355
- if (fatal) externalError = fatal.message
22356
- const suppressed = external.entryErrors.find((item) => isReal(item) && item.suppressed === true)
22357
- if (suppressed) suppressedEntryError = suppressed.message
22358
- }
22359
- // once the app has errored its last status ("injecting sootsim
22360
- // modules...") is stale \u2014 reporting it makes a dead app read as one that
22361
- // is still booting.
22362
- if (externalError) loadingText = ''
22363
- else if (externalReady === false && externalStatus) loadingText = externalStatus
22364
- } catch {}
22365
- let errors = 0
22366
- try { errors = window.__sootsimConsole?.count?.()?.errors ?? 0 } catch {}
22367
- return {
22368
- flag: (window).__sootsimExternalAppReady,
22369
- at: (window).__sootsimExternalAppReadyAt || 0,
22370
- nodes,
22371
- targets,
22372
- liveFrameActive,
22373
- liveFrameChannels,
22374
- liveFramePublishes,
22375
- errors,
22376
- loadingText,
22377
- externalReady,
22378
- externalStatus,
22379
- externalError,
22380
- suppressedEntryError,
22381
- }
22382
- })()`;
22383
- READY_CONTENT_NODE_FLOOR = 100;
22384
- READY_NODE_STABLE_MS = 750;
22385
- READY_PROBE_POLL_MS = 150;
22386
22398
  MERGED_CONSOLE_COUNT_EVAL = `(() => {
22387
22399
  const norm = (s) => (typeof s === 'string' ? s : (() => { try { return JSON.stringify(s) } catch { return String(s) } })())
22388
22400
  const key = (lvl, ts, args) => lvl + '|' + Math.round((ts || 0) / 250) + '|' + (Array.isArray(args) ? args.map(norm).join(' ') : norm(args))
@@ -24873,15 +24885,11 @@ async function maybeDescribeAfterOpen(wsPort, simId, args) {
24873
24885
  try {
24874
24886
  let readyProbe = null;
24875
24887
  try {
24876
- readyProbe = await bridge.send({
24877
- type: "evaluate",
24878
- code: WAIT_READY_PROBE
24879
- });
24888
+ readyProbe = await inspectWaitReady(bridge, 1);
24880
24889
  } catch {
24881
24890
  }
24882
24891
  if (readyProbe) {
24883
- const readyEnough = readyProbe.flag === true && !readyProbe.loadingText && readyProbeHasContent(readyProbe);
24884
- if (!readyEnough) {
24892
+ if (!readyProbe.ready) {
24885
24893
  const reason = readyProbe.loadingText ? `still showing "${readyProbe.loadingText}"` : readyProbe.flag !== true ? "guest ready event has not fired" : readyProbe.targets <= 0 ? "no visible app content is inspectable yet" : "app tree is still stabilizing";
24886
24894
  console.log(
24887
24895
  ` app still loading: ${reason} (nodes: ${readyProbe.nodes}, targets: ${readyProbe.targets})`
@@ -24889,26 +24897,16 @@ async function maybeDescribeAfterOpen(wsPort, simId, args) {
24889
24897
  console.log(" before interacting, run: rnx wait ready --max-ms 120000");
24890
24898
  }
24891
24899
  }
24892
- const dumpOpts = { describe: true, verbose: false, filter: "" };
24893
- const code = `(async () => {
24894
- const t = window.__sootsimTest
24895
- const ms = window.SootSim?.bridges?.mainShell
24896
- if (!t) return null
24897
- let shell = null
24898
- try { shell = ms?.getState ? await ms.getState() : null } catch {}
24899
- const tree = await t.dumpTree(12, ${JSON.stringify(dumpOpts)})
24900
- return { tree, shell }
24901
- })()`;
24902
- const result = await bridge.send({ type: "evaluate", code });
24900
+ const result = await inspectDescribe(bridge, {
24901
+ describe: true,
24902
+ verbose: false,
24903
+ filter: "",
24904
+ compact: true,
24905
+ hideXy: false,
24906
+ maxDepth: 12
24907
+ });
24903
24908
  if (!result?.tree) return;
24904
24909
  console.log("");
24905
- if (result.shell?.state) {
24906
- const parts = [
24907
- `state=${result.shell.state}`,
24908
- result.shell.activeApp ? `app=${result.shell.activeApp}` : null
24909
- ].filter(Boolean);
24910
- console.log(` shell: ${parts.join(" ")}`);
24911
- }
24912
24910
  console.log(result.tree);
24913
24911
  } finally {
24914
24912
  bridge.close();
@@ -26860,7 +26858,6 @@ var init_bridge_flow_runner = __esm({
26860
26858
  lastRecordingDurationMs = null;
26861
26859
  lastRecordingFrameStats = null;
26862
26860
  flowTraceSteps = [];
26863
- simRouteHint = null;
26864
26861
  // set by runStep when a step throws — lets the outer flow command
26865
26862
  // dump a rich per-step failure bundle (screenshot + describe.json +
26866
26863
  // console + network) keyed by the failing step.
@@ -26868,50 +26865,6 @@ var init_bridge_flow_runner = __esm({
26868
26865
  get simId() {
26869
26866
  return this.opts.simId;
26870
26867
  }
26871
- setSimRouteHint(urlLike) {
26872
- this.simRouteHint = this.normalizeSimRoute(urlLike);
26873
- }
26874
- normalizeSimRoute(urlLike) {
26875
- if (!urlLike) return null;
26876
- try {
26877
- const url = new URL(urlLike);
26878
- url.searchParams.delete("inspectOpen");
26879
- const search = url.searchParams.toString();
26880
- return `${url.origin}${url.pathname}${search ? `?${search}` : ""}`;
26881
- } catch {
26882
- return urlLike;
26883
- }
26884
- }
26885
- // sim ids rotate on `window.location.reload()` when the shell reconnects.
26886
- // after launchApp we re-resolve the primary sim so the rest of the flow
26887
- // doesn't send commands to a dead id.
26888
- async refreshSimId() {
26889
- if (!this.opts.simId) return;
26890
- try {
26891
- const sims = await this.bridge.listSims();
26892
- const current = sims.find((b) => b.id === this.opts.simId);
26893
- if (current) {
26894
- this.simRouteHint = this.normalizeSimRoute(current.url || current.origin);
26895
- }
26896
- const stillThere = sims.find(
26897
- (b) => b.id === this.opts.simId && b.readyState === "open"
26898
- );
26899
- if (stillThere) return;
26900
- if (!this.simRouteHint) return;
26901
- const hinted = this.simRouteHint && sims.find(
26902
- (b) => b.readyState === "open" && this.normalizeSimRoute(b.url || b.origin) === this.simRouteHint
26903
- );
26904
- if (!hinted) return;
26905
- const primary = sims.find((b) => b.isPrimary && b.readyState === "open");
26906
- const firstOpen = sims.find((b) => b.readyState === "open");
26907
- const next = hinted ?? primary ?? firstOpen;
26908
- if (next) {
26909
- this.opts.simId = next.id;
26910
- console.log(` [flow] sim rotated to ${next.id}`);
26911
- }
26912
- } catch {
26913
- }
26914
- }
26915
26868
  async evaluate(code, timeoutMs) {
26916
26869
  return this.bridge.send(
26917
26870
  {
@@ -26940,63 +26893,17 @@ var init_bridge_flow_runner = __esm({
26940
26893
  });
26941
26894
  }
26942
26895
  async waitForTree(timeout = DEFAULT_TIMEOUT) {
26943
- const deadline = Date.now() + scaledTimeout(timeout);
26944
- const startedAt = Date.now();
26945
- let lastError = null;
26946
- let consecutiveBridgeFailures = 0;
26947
- let lastProgressAt = 0;
26948
- let lastNodes = -1;
26949
- let nodeStableSince = startedAt;
26950
- while (Date.now() < deadline) {
26951
- try {
26952
- const probe3 = await this.evaluate(
26953
- WAIT_READY_PROBE,
26954
- // the readiness probe is cheap — cap it well under the bridge-wide
26955
- // command timeout so a non-responsive sim is detected in seconds,
26956
- // not after a full 15s-per-poll stall.
26957
- 8e3
26958
- );
26959
- consecutiveBridgeFailures = 0;
26960
- if (probe3?.externalError) {
26961
- throw new Error(`app failed to load: ${probe3.externalError}`);
26962
- }
26963
- if (probe3) {
26964
- const now = Date.now();
26965
- if (probe3.nodes !== lastNodes) {
26966
- lastNodes = probe3.nodes;
26967
- nodeStableSince = now;
26968
- }
26969
- const hasReadySignal = probe3.flag === true && readyProbeHasContent(probe3);
26970
- const hasRenderedContentWithoutReadySignal = probe3.flag !== true && readyProbeHasTargetContent(probe3);
26971
- const ready = (hasReadySignal || hasRenderedContentWithoutReadySignal) && probe3.externalReady !== false && !probe3.externalError && !probe3.loadingText && now - nodeStableSince >= READY_NODE_STABLE_MS;
26972
- if (ready) return;
26973
- }
26974
- } catch (err) {
26975
- lastError = err instanceof Error ? err : new Error(String(err));
26976
- if (lastError.message.startsWith("app failed to load:")) {
26977
- throw lastError;
26978
- }
26979
- consecutiveBridgeFailures++;
26980
- if (consecutiveBridgeFailures >= 4) {
26981
- throw new Error(
26982
- `target sim is not responding to the bridge (${lastError.message}). it is likely a stale orphan \u2014 run \`rnx list\`, then close it with \`rnx close <id>\` or start a fresh one with \`rnx open --new <port>\`.`
26983
- );
26984
- }
26985
- await this.refreshSimId();
26986
- }
26987
- const elapsed = Date.now() - startedAt;
26988
- if (elapsed - lastProgressAt >= 5e3) {
26989
- lastProgressAt = elapsed;
26990
- console.log(
26991
- ` [flow] waiting for app to be ready\u2026 (${Math.round(elapsed / 1e3)}s)`
26992
- );
26993
- }
26994
- await sleep3(READY_PROBE_POLL_MS);
26896
+ const status = await inspectWaitReady(this.bridge, scaledTimeout(timeout));
26897
+ if (status.ready) return;
26898
+ if (status.externalError) {
26899
+ throw new Error(`app failed to load: ${status.externalError}`);
26995
26900
  }
26996
- if (lastError?.message.startsWith("app failed to load:")) {
26997
- throw lastError;
26901
+ if (status.bridgeError) {
26902
+ throw new Error(
26903
+ `target sim is not responding to the bridge (${status.bridgeError}). it is likely a stale orphan; run \`rnx list\`, then close it with \`rnx close <id>\` or start a fresh one with \`rnx open --new <port>\`.`
26904
+ );
26998
26905
  }
26999
- throw new Error(`app not ready after ${timeout}ms`);
26906
+ throw new Error(`app not ready after ${timeout}ms: ${waitReadyReason(status)}`);
27000
26907
  }
27001
26908
  async ensureRecordingStarted() {
27002
26909
  if (!this.opts.recordingOutputDir) {
@@ -27974,8 +27881,6 @@ var init_bridge_flow_runner = __esm({
27974
27881
  async reloadGuestApp() {
27975
27882
  await this.evaluate("window.location.reload()").catch(() => {
27976
27883
  });
27977
- await sleep3(500);
27978
- await this.refreshSimId();
27979
27884
  }
27980
27885
  async resetGuestAppData(launchOptions) {
27981
27886
  const reset = await this.bridge.send(
@@ -28012,7 +27917,6 @@ var init_bridge_flow_runner = __esm({
28012
27917
  const deadline = Date.now() + 1e4;
28013
27918
  let lastError = null;
28014
27919
  while (Date.now() < deadline) {
28015
- await this.refreshSimId();
28016
27920
  try {
28017
27921
  if (this.recordingEnabled) await this.ensureRecordingStarted();
28018
27922
  if (this.profilingEnabled) await this.startProfile();
@@ -28027,7 +27931,6 @@ var init_bridge_flow_runner = __esm({
28027
27931
  );
28028
27932
  }
28029
27933
  async launchApp(opts) {
28030
- await this.refreshSimId();
28031
27934
  let hasLaunchArguments = false;
28032
27935
  let launchArguments;
28033
27936
  if (isObjectRecord(opts) && Object.prototype.hasOwnProperty.call(opts, "arguments")) {
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.366 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.367 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rnxsim",
3
- "version": "0.1.366",
3
+ "version": "0.1.367",
4
4
  "description": "rnx CLI, Vite and Metro plugins, and agent skills for driving React Native apps in the browser.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -71,11 +71,11 @@
71
71
  "import": "./dist-lib/sdk.mjs",
72
72
  "default": "./dist-lib/sdk.cjs"
73
73
  },
74
- "./mcp": {
75
- "types": "./src/mcp.ts",
76
- "source": "./src/mcp.ts",
77
- "import": "./dist-lib/mcp.mjs",
78
- "default": "./dist-lib/mcp.cjs"
74
+ "./bridge-contract-input": {
75
+ "types": "./src/bridge-contract-input.ts",
76
+ "source": "./src/bridge-contract-input.ts",
77
+ "import": "./dist-lib/bridge-contract-input.mjs",
78
+ "default": "./dist-lib/bridge-contract-input.cjs"
79
79
  },
80
80
  "./beta": {
81
81
  "source": "./src/beta.ts",
@@ -111,6 +111,8 @@
111
111
  "./bridge-contract": {
112
112
  "types": "./src/bridge-contract.ts",
113
113
  "source": "./src/bridge-contract.ts",
114
+ "bun": "./src/bridge-contract.ts",
115
+ "import": "./dist-lib/bridge-contract.mjs",
114
116
  "default": "./dist-lib/bridge-contract.cjs"
115
117
  },
116
118
  "./bridge-constants": {
@@ -219,6 +221,7 @@
219
221
  }
220
222
  },
221
223
  "devDependencies": {
224
+ "@contrast/harness": "workspace:*",
222
225
  "@contrast/runtime-delivery": "workspace:*",
223
226
  "@sootsim/compat": "workspace:*",
224
227
  "@sootsim/globals": "workspace:*",