rnxsim 0.1.375 → 0.1.376

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 (193) hide show
  1. package/cli/commands/control.ts +18 -4
  2. package/dist-cli/bin.js +5 -5
  3. package/dist-cli/chunks/{agent-JJN7U6FR.js → agent-H42FCKFF.js} +2 -2
  4. package/dist-cli/chunks/{agent-wrapper-AVVUJ3WF.js → agent-wrapper-BBZKTZS6.js} +2 -2
  5. package/dist-cli/chunks/{app-fonts-QJ22CNAZ.js → app-fonts-43VXIKF4.js} +2 -2
  6. package/dist-cli/chunks/{assert-PZZKIY3K.js → assert-IXOLVO5Z.js} +2 -2
  7. package/dist-cli/chunks/{auth-5AVDJO33.js → auth-KFMFB6FU.js} +2 -2
  8. package/dist-cli/chunks/{beta-PFRGP266.js → beta-5AO3SJB7.js} +2 -2
  9. package/dist-cli/chunks/{box-ILC2O7ZO.js → box-ZHM66L6K.js} +44 -44
  10. package/dist-cli/chunks/{camera-J3V62PP7.js → camera-FNJVFCDQ.js} +2 -2
  11. package/dist-cli/chunks/{chunk-CCMZSHJB.js → chunk-37MCSPTE.js} +1 -1
  12. package/dist-cli/chunks/{chunk-FUTYK55H.js → chunk-3PEMSUAQ.js} +3 -3
  13. package/dist-cli/chunks/chunk-4TQ5HOK3.js +7 -0
  14. package/dist-cli/chunks/{chunk-25KRGDZT.js → chunk-5YZYDEMI.js} +1 -1
  15. package/dist-cli/chunks/{chunk-OJNKRIZF.js → chunk-6CV3R3XD.js} +2 -2
  16. package/dist-cli/chunks/{chunk-5HHWECGT.js → chunk-7LGK5XU6.js} +2 -2
  17. package/dist-cli/chunks/{chunk-K7QQWKGE.js → chunk-7R2TK2NY.js} +1 -1
  18. package/dist-cli/chunks/{chunk-EZXV6U2N.js → chunk-7VZZQQEE.js} +3 -3
  19. package/dist-cli/chunks/{chunk-XUBV77WH.js → chunk-AFOVILIL.js} +1 -1
  20. package/dist-cli/chunks/{chunk-S65CK3RI.js → chunk-BEU7CD7E.js} +1 -1
  21. package/dist-cli/chunks/{chunk-F5F7FIPM.js → chunk-CE4JQJ6Y.js} +2 -2
  22. package/dist-cli/chunks/{chunk-RLCQLMFF.js → chunk-CIE6CZXL.js} +2 -2
  23. package/dist-cli/chunks/{chunk-5PZVYF42.js → chunk-CSJ7L7ZL.js} +1 -1
  24. package/dist-cli/chunks/{chunk-7NCPUZ52.js → chunk-CWCQP53A.js} +2 -2
  25. package/dist-cli/chunks/{chunk-Z6LOIW46.js → chunk-DCS5FK5N.js} +1 -1
  26. package/dist-cli/chunks/{chunk-MXJ2UWRV.js → chunk-DEV3E3MV.js} +1 -1
  27. package/dist-cli/chunks/{chunk-H4MYYOYJ.js → chunk-DGJMIVZM.js} +2 -2
  28. package/dist-cli/chunks/{chunk-GEX7RVHY.js → chunk-DGX3MN65.js} +1 -1
  29. package/dist-cli/chunks/{chunk-FZDRA7Z4.js → chunk-DNPQMTW5.js} +2 -2
  30. package/dist-cli/chunks/{chunk-TPPVAA7Z.js → chunk-DOVBJBVD.js} +2 -2
  31. package/dist-cli/chunks/{chunk-FW6AN7I2.js → chunk-DPBJ3S3J.js} +1 -1
  32. package/dist-cli/chunks/{chunk-PBOWUNXO.js → chunk-FVZBGPCU.js} +2 -2
  33. package/dist-cli/chunks/{chunk-PNRJFHX4.js → chunk-GDKMOIUT.js} +1 -1
  34. package/dist-cli/chunks/{chunk-4IKE4YUS.js → chunk-GIF2NJCS.js} +2 -2
  35. package/dist-cli/chunks/{chunk-MP2GPO4T.js → chunk-GZE57ORL.js} +1 -1
  36. package/dist-cli/chunks/{chunk-LD7I266X.js → chunk-H3U56DKI.js} +2 -2
  37. package/dist-cli/chunks/{chunk-JOHJZUGG.js → chunk-HZLOYAPH.js} +2 -2
  38. package/dist-cli/chunks/{chunk-DSD2UBZI.js → chunk-J2IKAEU2.js} +2 -2
  39. package/dist-cli/chunks/{chunk-HEV7BAPE.js → chunk-JXAXYK7W.js} +2 -2
  40. package/dist-cli/chunks/{chunk-XYSDYGKC.js → chunk-KJE2O2GK.js} +2 -2
  41. package/dist-cli/chunks/{chunk-V7RGRZOL.js → chunk-KMNVDZZI.js} +1 -1
  42. package/dist-cli/chunks/{chunk-4ALPWIMP.js → chunk-LC3QRIRB.js} +2 -2
  43. package/dist-cli/chunks/{chunk-SXBFGGAM.js → chunk-LGFTAQYC.js} +1 -1
  44. package/dist-cli/chunks/{chunk-4CEZZKRY.js → chunk-LQMUA5IY.js} +1 -1
  45. package/dist-cli/chunks/{chunk-LAOKTKT4.js → chunk-MFIQTWC5.js} +2 -2
  46. package/dist-cli/chunks/{chunk-2IUMRRYV.js → chunk-MGALNF2F.js} +2 -2
  47. package/dist-cli/chunks/{chunk-HNPHODBD.js → chunk-MLBZZHOY.js} +2 -2
  48. package/dist-cli/chunks/{chunk-7OSIJCBR.js → chunk-NNRF7GIB.js} +2 -2
  49. package/dist-cli/chunks/{chunk-5CGJFU27.js → chunk-NORLYTTO.js} +2 -2
  50. package/dist-cli/chunks/{chunk-CX7VCKAT.js → chunk-P6KVGV3H.js} +11 -11
  51. package/dist-cli/chunks/chunk-PAJTUK33.js +4 -0
  52. package/dist-cli/chunks/{chunk-C6B5FOP3.js → chunk-QELIC5XP.js} +1 -1
  53. package/dist-cli/chunks/{chunk-HWDVC6YA.js → chunk-QRDF33WD.js} +1 -1
  54. package/dist-cli/chunks/{chunk-LSOGXSDV.js → chunk-R76BA7C7.js} +3 -3
  55. package/dist-cli/chunks/{chunk-SWBUNMQS.js → chunk-RVEB4CZ2.js} +2 -2
  56. package/dist-cli/chunks/{chunk-BQCEI37V.js → chunk-SK4M7NXT.js} +1 -1
  57. package/dist-cli/chunks/{chunk-JWUDT4QK.js → chunk-SKMG5DD7.js} +2 -2
  58. package/dist-cli/chunks/{chunk-AQ5TMOAI.js → chunk-TEESPOYR.js} +1 -1
  59. package/dist-cli/chunks/{chunk-T4ISWS5H.js → chunk-TH2UFGVP.js} +1 -1
  60. package/dist-cli/chunks/{chunk-4ZXR2M3D.js → chunk-TLP3T3VO.js} +1 -1
  61. package/dist-cli/chunks/{chunk-2BRBFLWA.js → chunk-TZUABDIJ.js} +2 -2
  62. package/dist-cli/chunks/{chunk-Z5O5H4O7.js → chunk-UB2W6OQM.js} +2 -2
  63. package/dist-cli/chunks/{chunk-HNDWYCND.js → chunk-UYVXALPB.js} +2 -2
  64. package/dist-cli/chunks/{chunk-ZXFVBYJY.js → chunk-VQDYFFIG.js} +2 -2
  65. package/dist-cli/chunks/{chunk-YNHRSDVG.js → chunk-VWCNJPCJ.js} +2 -2
  66. package/dist-cli/chunks/{chunk-2GXNAZHB.js → chunk-VZIK4WIS.js} +2 -2
  67. package/dist-cli/chunks/{chunk-LMHBU5LZ.js → chunk-W3FNXAQP.js} +1 -1
  68. package/dist-cli/chunks/{chunk-GDLE4NIV.js → chunk-XCDNVNP3.js} +1 -1
  69. package/dist-cli/chunks/{chunk-4EUKOIAV.js → chunk-XSVILA4Z.js} +2 -2
  70. package/dist-cli/chunks/{chunk-USM3P74H.js → chunk-XUTJ7ZWD.js} +2 -2
  71. package/dist-cli/chunks/{chunk-YX52RJKO.js → chunk-YTXY3MHW.js} +2 -2
  72. package/dist-cli/chunks/{chunk-QJQOK34O.js → chunk-ZF7PVX44.js} +2 -2
  73. package/dist-cli/chunks/{chunk-DQUPSAHE.js → chunk-ZSSG7OR7.js} +1 -1
  74. package/dist-cli/chunks/{cleanup-KPUW4SFK.js → cleanup-MLOK2OZA.js} +2 -2
  75. package/dist-cli/chunks/cli-version-OBNR366V.js +4 -0
  76. package/dist-cli/chunks/{compat-XXRFD2TW.js → compat-SHMFSKU7.js} +2 -2
  77. package/dist-cli/chunks/{config-DDMFDQ7F.js → config-TPEAEUJ2.js} +2 -2
  78. package/dist-cli/chunks/{connect-IPG47YXM.js → connect-3LEXWTE5.js} +2 -2
  79. package/dist-cli/chunks/control-Z3FWA6BO.js +4 -0
  80. package/dist-cli/chunks/daemon-RSVYSC65.js +4 -0
  81. package/dist-cli/chunks/{debug-MYIP46TM.js → debug-GUVAK2KR.js} +3 -3
  82. package/dist-cli/chunks/{desktop-ENFFBQAA.js → desktop-OOP2PTKP.js} +3 -3
  83. package/dist-cli/chunks/{detox-J5WVK6OH.js → detox-C2ILVBEC.js} +2 -2
  84. package/dist-cli/chunks/{device-RAVQVL75.js → device-NXIQQJWR.js} +2 -2
  85. package/dist-cli/chunks/{diagnose-PJMPXKJY.js → diagnose-2JBGYE5L.js} +2 -2
  86. package/dist-cli/chunks/{disk-cleanup-YLAY445J.js → disk-cleanup-VURWJYQV.js} +2 -2
  87. package/dist-cli/chunks/{do-chain-UBE7GIOU.js → do-chain-DJ4UQC7A.js} +2 -2
  88. package/dist-cli/chunks/drivers-JMA2OIKU.js +4 -0
  89. package/dist-cli/chunks/{env-Y5XJSNRD.js → env-KTNPKEZR.js} +2 -2
  90. package/dist-cli/chunks/{film-W3V6VURB.js → film-DCPS7EID.js} +3 -3
  91. package/dist-cli/chunks/flow-4DZ7B2OA.js +4 -0
  92. package/dist-cli/chunks/help-34CJMQY2.js +4 -0
  93. package/dist-cli/chunks/{hidden-runtime-alias-S5DJ7EDF.js → hidden-runtime-alias-HHT2C7WE.js} +2 -2
  94. package/dist-cli/chunks/{home-paths-5O4IZPYV.js → home-paths-KTPGCPMS.js} +2 -2
  95. package/dist-cli/chunks/inspect-JF5WYJRD.js +4 -0
  96. package/dist-cli/chunks/install-desktop-ZIC4CDXI.js +4 -0
  97. package/dist-cli/chunks/{login-V6TZ54ZH.js → login-TTJAOI2Q.js} +2 -2
  98. package/dist-cli/chunks/{logout-SPMLFWYA.js → logout-ERAQ6SKW.js} +2 -2
  99. package/dist-cli/chunks/{maestro-5ISTBAZE.js → maestro-W46QCF77.js} +3 -3
  100. package/dist-cli/chunks/{maestro-generate-WUI64NHR.js → maestro-generate-VBLM3MTU.js} +3 -3
  101. package/dist-cli/chunks/{mode-QPNBYXUF.js → mode-ZEDZK7R7.js} +2 -2
  102. package/dist-cli/chunks/{optional-demo-registry-CBSYF4CG.js → optional-demo-registry-ZUTPSVYQ.js} +2 -2
  103. package/dist-cli/chunks/{perf-T6N5PVYV.js → perf-YVT2WXT4.js} +2 -2
  104. package/dist-cli/chunks/{permissions-YZ7RRSZJ.js → permissions-L7VVDHFP.js} +2 -2
  105. package/dist-cli/chunks/{platform-XDQAA72X.js → platform-7TJOU2FB.js} +3 -3
  106. package/dist-cli/chunks/privacy-57UOTB5J.js +4 -0
  107. package/dist-cli/chunks/{record-QGYXRUKP.js → record-VXG37P7U.js} +3 -3
  108. package/dist-cli/chunks/{report-issue-VUUTGX7V.js → report-issue-WNNOBZHC.js} +2 -2
  109. package/dist-cli/chunks/reset-H5NBFE5U.js +4 -0
  110. package/dist-cli/chunks/resolve-target-57TK5WLE.js +4 -0
  111. package/dist-cli/chunks/runtime-QU2NIF2K.js +4 -0
  112. package/dist-cli/chunks/{screenshot-command-NZCMSJ4P.js → screenshot-command-AN56TTG7.js} +3 -3
  113. package/dist-cli/chunks/{screenshot-layers-VMTQ6XIU.js → screenshot-layers-3W5R7DB2.js} +2 -2
  114. package/dist-cli/chunks/{screenshots-capture-K3MWIDBK.js → screenshots-capture-6AWJY7XZ.js} +2 -2
  115. package/dist-cli/chunks/{serve-QJRIB2BV.js → serve-GLYO5Q7J.js} +2 -2
  116. package/dist-cli/chunks/setup-UA2HLD7S.js +4 -0
  117. package/dist-cli/chunks/{sim-client-5YUVL45C.js → sim-client-5C2RDQQO.js} +2 -2
  118. package/dist-cli/chunks/{skills-T3CS7CXK.js → skills-EYXWBRNQ.js} +2 -2
  119. package/dist-cli/chunks/{state-AJC3VK3G.js → state-REJ7W6K3.js} +2 -2
  120. package/dist-cli/chunks/{storage-KVGZKC3Y.js → storage-CFMDHI3V.js} +2 -2
  121. package/dist-cli/chunks/store-XMCUA45G.js +4 -0
  122. package/dist-cli/chunks/telemetry-FBL3NAKB.js +4 -0
  123. package/dist-cli/chunks/{timeline-DXDCAZUA.js → timeline-HMCHN5FO.js} +2 -2
  124. package/dist-cli/chunks/{upgrade-SRPTJACU.js → upgrade-2QSPAA3P.js} +2 -2
  125. package/dist-cli/chunks/upload-DUQBKZYV.js +4 -0
  126. package/dist-cli/chunks/{version-SOLPSBMG.js → version-IOA6QFGB.js} +2 -2
  127. package/dist-cli/chunks/{web-RT75LD7A.js → web-NCGUYVCJ.js} +2 -2
  128. package/dist-cli/chunks/welcome-QYGSQCWQ.js +5 -0
  129. package/dist-cli/chunks/{what-happened-CEIWJIWK.js → what-happened-YTOUK4LO.js} +2 -2
  130. package/dist-cli/chunks/{ws-bridge-YK5R3HOI.js → ws-bridge-OF4OZARG.js} +2 -2
  131. package/dist-lib/agent-daemon-client.cjs +1 -1
  132. package/dist-lib/agent-events.cjs +1 -1
  133. package/dist-lib/agent-identity.cjs +1 -1
  134. package/dist-lib/agent-sessions.cjs +1 -1
  135. package/dist-lib/attached-projects.cjs +1 -1
  136. package/dist-lib/auth/shared-session.cjs +1 -1
  137. package/dist-lib/backend-origin.cjs +1 -1
  138. package/dist-lib/beta.cjs +1 -1
  139. package/dist-lib/beta.mjs +1 -1
  140. package/dist-lib/bridge-constants.cjs +1 -1
  141. package/dist-lib/bridge-contract-input.cjs +1 -1
  142. package/dist-lib/bridge-contract-input.mjs +1 -1
  143. package/dist-lib/bridge-contract.cjs +1 -1
  144. package/dist-lib/bridge-contract.mjs +1 -1
  145. package/dist-lib/cli-constants.cjs +1 -1
  146. package/dist-lib/config.cjs +1 -1
  147. package/dist-lib/detox/index.cjs +1 -1
  148. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  149. package/dist-lib/home-paths.cjs +1 -1
  150. package/dist-lib/host/bridge-host.cjs +148 -134
  151. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  152. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  153. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  154. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  155. package/dist-lib/host/websocket-proxy.cjs +1 -1
  156. package/dist-lib/index.cjs +1 -1
  157. package/dist-lib/jump-to-source-babel.cjs +1 -1
  158. package/dist-lib/menu.cjs +1 -1
  159. package/dist-lib/menu.mjs +1 -1
  160. package/dist-lib/metro-production-bundle.cjs +1 -1
  161. package/dist-lib/metro-production-bundle.mjs +1 -1
  162. package/dist-lib/metro.cjs +1 -1
  163. package/dist-lib/profiles.cjs +1 -1
  164. package/dist-lib/public-brand.cjs +1 -1
  165. package/dist-lib/react-native-host-modules.cjs +1 -1
  166. package/dist-lib/react-native-host-modules.mjs +1 -1
  167. package/dist-lib/render-mode.cjs +1 -1
  168. package/dist-lib/scripts/dev-server-scanner.cjs +18 -4
  169. package/dist-lib/sdk.cjs +1 -1
  170. package/dist-lib/sdk.mjs +1 -1
  171. package/dist-lib/skills.cjs +217 -196
  172. package/dist-lib/vite.cjs +1 -1
  173. package/package.json +1 -1
  174. package/scripts/dev-server-scanner.ts +23 -5
  175. package/dist-cli/chunks/chunk-EWPAI4MO.js +0 -7
  176. package/dist-cli/chunks/chunk-V4EJJHF5.js +0 -4
  177. package/dist-cli/chunks/cli-version-APGT6GAT.js +0 -4
  178. package/dist-cli/chunks/control-O22Z3UJ5.js +0 -4
  179. package/dist-cli/chunks/daemon-PADUIMJV.js +0 -4
  180. package/dist-cli/chunks/drivers-ZFNX3T5Z.js +0 -4
  181. package/dist-cli/chunks/flow-VMLI7BYM.js +0 -4
  182. package/dist-cli/chunks/help-PKZCWHJJ.js +0 -4
  183. package/dist-cli/chunks/inspect-LT4MXK37.js +0 -4
  184. package/dist-cli/chunks/install-desktop-EXCFMXFS.js +0 -4
  185. package/dist-cli/chunks/privacy-54FIGXLT.js +0 -4
  186. package/dist-cli/chunks/reset-CGO52KBW.js +0 -4
  187. package/dist-cli/chunks/resolve-target-NNQCALDM.js +0 -4
  188. package/dist-cli/chunks/runtime-4EP6YJVL.js +0 -4
  189. package/dist-cli/chunks/setup-6XXEJQVF.js +0 -4
  190. package/dist-cli/chunks/store-AWUMOQUH.js +0 -4
  191. package/dist-cli/chunks/telemetry-W5ZDGWK6.js +0 -4
  192. package/dist-cli/chunks/upload-ZDV4BFED.js +0 -4
  193. package/dist-cli/chunks/welcome-IJ55EC7U.js +0 -5
@@ -1,7 +1,7 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.376 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import { createRequire as __sootsimCreateRequire } from 'node:module'
3
3
  var require = __sootsimCreateRequire(import.meta.url)
4
- import{m as O}from"./chunk-2BRBFLWA.js";import"./chunk-5PZVYF42.js";import{t as E,v as F,x as N}from"./chunk-7NCPUZ52.js";import"./chunk-GEX7RVHY.js";import{r as S}from"./chunk-HWDVC6YA.js";import"./chunk-DQUPSAHE.js";import{createHash as I,randomUUID as q}from"node:crypto";import{createReadStream as D}from"node:fs";import m from"node:fs/promises";import f from"node:path";var T="/__camera-fixtures/",J=new Set([".m4v",".mov",".mp4",".webm"]);function _(){console.log(`
4
+ import{m as O}from"./chunk-TZUABDIJ.js";import"./chunk-CSJ7L7ZL.js";import{t as E,v as F,x as N}from"./chunk-CWCQP53A.js";import"./chunk-DGX3MN65.js";import{r as S}from"./chunk-QRDF33WD.js";import"./chunk-ZSSG7OR7.js";import{createHash as I,randomUUID as q}from"node:crypto";import{createReadStream as D}from"node:fs";import m from"node:fs/promises";import f from"node:path";var T="/__camera-fixtures/",J=new Set([".m4v",".mov",".mp4",".webm"]);function _(){console.log(`
5
5
  rnx camera \u2014 control the simulator camera source
6
6
 
7
7
  usage:
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.376 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import { createRequire as __sootsimCreateRequire } from 'node:module'
3
3
  var require = __sootsimCreateRequire(import.meta.url)
4
4
  function t(_){return`(async () => {
@@ -1,7 +1,7 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.376 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import { createRequire as __sootsimCreateRequire } from 'node:module'
3
3
  var require = __sootsimCreateRequire(import.meta.url)
4
- import{b}from"./chunk-PBOWUNXO.js";import{q as x}from"./chunk-7NCPUZ52.js";import{c as l}from"./chunk-T4ISWS5H.js";import{b as R,c as w,d as m,e as $,f as u}from"./chunk-F5F7FIPM.js";import{L as h,M as f,N as y,T as v,l as d}from"./chunk-HWDVC6YA.js";import S from"fs";import{WebSocket as L}from"ws";function E(t){if(!t||typeof t!="object")return!1;let e=Reflect.get(t,"sections");return typeof Reflect.get(t,"title")=="string"&&Array.isArray(e)&&e.every(o=>o&&typeof o=="object"&&typeof Reflect.get(o,"title")=="string"&&Array.isArray(Reflect.get(o,"items"))&&Reflect.get(o,"items").every(n=>n&&typeof n=="object"&&typeof Reflect.get(n,"summary")=="string"&&typeof Reflect.get(n,"hash")=="string"))}async function A(t,e=fetch){try{let o=new URL($);o.searchParams.set("version",t);let n=await e(o,{headers:{accept:"application/json"},signal:AbortSignal.timeout(5e3)});if(!n.ok)return null;let r=await n.json();if(!r||typeof r!="object")return null;let i=Reflect.get(r,"entries");return!Array.isArray(i)||!E(i[0])?null:i[0]}catch{return null}}function O(t,e){let o=[`what's new in rnx engine v${t}:`];if(!e||e.sections.length===0)o.push(" release notes are not available yet");else for(let n of e.sections){o.push(` ${n.title}:`);for(let r of n.items)o.push(` \u2022 ${r.summary}`)}return o.push(` full changelog: ${m}`),o.join(`
4
+ import{b}from"./chunk-FVZBGPCU.js";import{q as x}from"./chunk-CWCQP53A.js";import{c as l}from"./chunk-TH2UFGVP.js";import{b as R,c as w,d as m,e as $,f as u}from"./chunk-CE4JQJ6Y.js";import{L as h,M as f,N as y,T as v,l as d}from"./chunk-QRDF33WD.js";import S from"fs";import{WebSocket as L}from"ws";function E(t){if(!t||typeof t!="object")return!1;let e=Reflect.get(t,"sections");return typeof Reflect.get(t,"title")=="string"&&Array.isArray(e)&&e.every(o=>o&&typeof o=="object"&&typeof Reflect.get(o,"title")=="string"&&Array.isArray(Reflect.get(o,"items"))&&Reflect.get(o,"items").every(n=>n&&typeof n=="object"&&typeof Reflect.get(n,"summary")=="string"&&typeof Reflect.get(n,"hash")=="string"))}async function A(t,e=fetch){try{let o=new URL($);o.searchParams.set("version",t);let n=await e(o,{headers:{accept:"application/json"},signal:AbortSignal.timeout(5e3)});if(!n.ok)return null;let r=await n.json();if(!r||typeof r!="object")return null;let i=Reflect.get(r,"entries");return!Array.isArray(i)||!E(i[0])?null:i[0]}catch{return null}}function O(t,e){let o=[`what's new in rnx engine v${t}:`];if(!e||e.sections.length===0)o.push(" release notes are not available yet");else for(let n of e.sections){o.push(` ${n.title}:`);for(let r of n.items)o.push(` \u2022 ${r.summary}`)}return o.push(` full changelog: ${m}`),o.join(`
5
5
  `)}async function Y(t,e={}){let[o,...n]=t;if(!o||o==="--help"||o==="-h"){k();return}switch(o){case"install":return _(n,e);case"list":return T(n);case"use":return D(n);case"remove":return G(n);case"which":return U();case"notes":return F(n);default:console.error(` unknown runtime subcommand: ${o}`),k(),l(1)}}function k(){console.log(`
6
6
  rnx runtime \u2014 manage engine runtimes under ~/.sootsim/runtimes/
7
7
 
@@ -27,4 +27,4 @@ examples:
27
27
  rnx runtime install --channel beta
28
28
  rnx runtime use 1.2.3
29
29
  rnx runtime notes
30
- `)}async function _(t,e){let{version:o,flags:n}=M(t),r=n.channel??e.channel??"stable",i=n.force===!0,a=n.setActive!==!1;h(),console.log("rnx runtime install"),console.log(` cdn: ${u.resolveCdnOrigin()}`);try{let s=await u.install({version:o,channel:r,force:i,setActive:a,protectVersions:v()});return console.log(` version: ${s.version} (channel: ${s.channel})`),s.installed?console.log(` installed ${s.version}`):console.log(` already installed at ${s.runtimeDir}`),a&&await I(s.version),s}catch(s){console.error(` ${N(s)}`),l(1)}}async function I(t){y(t),console.log(` active: ${t}`),await j(t)||console.log(` (no daemon running \u2014 next sootsim/electron launch will pick up ${t})`)}async function j(t){let{isDaemonLockfileFresh:e,readDaemonLockfile:o}=await import("./home-paths-5O4IZPYV.js"),n=o();if(!e(n))return!1;let r=x();return new Promise(i=>{let a=!1,s=!1,g=C=>{a||(a=!0,i(C))},c=new L(`ws://127.0.0.1:${r}`,{handshakeTimeout:800}),p=setTimeout(()=>{try{c.close()}catch{}g(s)},1500);c.on("open",()=>{try{c.send(JSON.stringify({type:"runtime:use",version:t,id:0})),s=!0}catch{}setTimeout(()=>{try{c.close()}catch{}},100)}),c.on("close",()=>{clearTimeout(p),g(s)}),c.on("error",()=>{clearTimeout(p),g(!1)})})}async function T(t){h();let e=u.listInstalled(),o=f();if(console.log("installed:"),e.length===0)console.log(" (none)");else for(let n of e)console.log(` ${n===o?"*":" "} ${n}`);try{let n=await u.fetchManifest();console.log("available (latest per channel):");for(let[i,a]of Object.entries(n.channels))console.log(` ${i.padEnd(8)} ${a.latest}`);let r=Object.keys(n.versions).sort(R).reverse();if(r.length>1){console.log("hosted versions:");for(let i of r)console.log(` ${i}`)}}catch(n){console.log(`available: (could not fetch manifest: ${N(n)})`)}}async function D(t){let e=t[0];e||(console.error(" usage: rnx runtime use <version>"),l(1));let o=u.listInstalled();o.includes(e)||(console.error(` version ${e} is not installed`),console.error(` installed: ${o.join(", ")||"(none)"}`),console.error(` run \`rnx runtime install ${e}\` first`),l(1)),await I(e)}async function G(t){let e=t[0];e||(console.error(" usage: rnx runtime remove <version>"),l(1)),f()===e&&(console.error(` cannot remove active runtime ${e}`),console.error(" switch with `rnx runtime use <other>` first, or install another version"),l(1)),v().includes(e)&&(console.error(` cannot remove runtime ${e} while a project is using it`),l(1));let n=d(e);if(!S.existsSync(n)){console.error(` ${e} is not installed`);return}S.rmSync(n,{recursive:!0,force:!0}),console.log(` removed ${e}`)}async function U(){let t=f();if(!t){console.log(" no active runtime");return}console.log(t)}async function F(t){t.length>0&&(console.error(" usage: rnx runtime notes"),l(1));let{config:e}=await b(),o=e?.runtimeVersion??f();if(!o){console.log(" no active runtime"),console.log(` release notes: ${m}`);return}console.log(O(o,await A(o)))}function M(t){let e={},o=[];for(let n=0;n<t.length;n++){let r=t[n];if(r==="--channel"&&n+1<t.length){e.channel=t[n+1],n++;continue}if(r.startsWith("--channel=")){e.channel=r.slice(10);continue}if(r==="--force"){e.force=!0;continue}if(r==="--set-active=false"||r==="--no-set-active"){e.setActive=!1;continue}o.push(r)}return{version:o[0]??null,flags:e}}function N(t){return t instanceof Error?t.message:String(t)}export{A as a,O as b,Y as c};
30
+ `)}async function _(t,e){let{version:o,flags:n}=M(t),r=n.channel??e.channel??"stable",i=n.force===!0,a=n.setActive!==!1;h(),console.log("rnx runtime install"),console.log(` cdn: ${u.resolveCdnOrigin()}`);try{let s=await u.install({version:o,channel:r,force:i,setActive:a,protectVersions:v()});return console.log(` version: ${s.version} (channel: ${s.channel})`),s.installed?console.log(` installed ${s.version}`):console.log(` already installed at ${s.runtimeDir}`),a&&await I(s.version),s}catch(s){console.error(` ${N(s)}`),l(1)}}async function I(t){y(t),console.log(` active: ${t}`),await j(t)||console.log(` (no daemon running \u2014 next sootsim/electron launch will pick up ${t})`)}async function j(t){let{isDaemonLockfileFresh:e,readDaemonLockfile:o}=await import("./home-paths-KTPGCPMS.js"),n=o();if(!e(n))return!1;let r=x();return new Promise(i=>{let a=!1,s=!1,g=C=>{a||(a=!0,i(C))},c=new L(`ws://127.0.0.1:${r}`,{handshakeTimeout:800}),p=setTimeout(()=>{try{c.close()}catch{}g(s)},1500);c.on("open",()=>{try{c.send(JSON.stringify({type:"runtime:use",version:t,id:0})),s=!0}catch{}setTimeout(()=>{try{c.close()}catch{}},100)}),c.on("close",()=>{clearTimeout(p),g(s)}),c.on("error",()=>{clearTimeout(p),g(!1)})})}async function T(t){h();let e=u.listInstalled(),o=f();if(console.log("installed:"),e.length===0)console.log(" (none)");else for(let n of e)console.log(` ${n===o?"*":" "} ${n}`);try{let n=await u.fetchManifest();console.log("available (latest per channel):");for(let[i,a]of Object.entries(n.channels))console.log(` ${i.padEnd(8)} ${a.latest}`);let r=Object.keys(n.versions).sort(R).reverse();if(r.length>1){console.log("hosted versions:");for(let i of r)console.log(` ${i}`)}}catch(n){console.log(`available: (could not fetch manifest: ${N(n)})`)}}async function D(t){let e=t[0];e||(console.error(" usage: rnx runtime use <version>"),l(1));let o=u.listInstalled();o.includes(e)||(console.error(` version ${e} is not installed`),console.error(` installed: ${o.join(", ")||"(none)"}`),console.error(` run \`rnx runtime install ${e}\` first`),l(1)),await I(e)}async function G(t){let e=t[0];e||(console.error(" usage: rnx runtime remove <version>"),l(1)),f()===e&&(console.error(` cannot remove active runtime ${e}`),console.error(" switch with `rnx runtime use <other>` first, or install another version"),l(1)),v().includes(e)&&(console.error(` cannot remove runtime ${e} while a project is using it`),l(1));let n=d(e);if(!S.existsSync(n)){console.error(` ${e} is not installed`);return}S.rmSync(n,{recursive:!0,force:!0}),console.log(` removed ${e}`)}async function U(){let t=f();if(!t){console.log(" no active runtime");return}console.log(t)}async function F(t){t.length>0&&(console.error(" usage: rnx runtime notes"),l(1));let{config:e}=await b(),o=e?.runtimeVersion??f();if(!o){console.log(" no active runtime"),console.log(` release notes: ${m}`);return}console.log(O(o,await A(o)))}function M(t){let e={},o=[];for(let n=0;n<t.length;n++){let r=t[n];if(r==="--channel"&&n+1<t.length){e.channel=t[n+1],n++;continue}if(r.startsWith("--channel=")){e.channel=r.slice(10);continue}if(r==="--force"){e.force=!0;continue}if(r==="--set-active=false"||r==="--no-set-active"){e.setActive=!1;continue}o.push(r)}return{version:o[0]??null,flags:e}}function N(t){return t instanceof Error?t.message:String(t)}export{A as a,O as b,Y as c};
@@ -0,0 +1,7 @@
1
+ /*! rnx v0.1.376 | (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 ge,b as ze}from"./chunk-KJE2O2GK.js";import{e as ot}from"./chunk-UYVXALPB.js";import{c as Ge}from"./chunk-VWCNJPCJ.js";import{a as et,b as tt,d as it}from"./chunk-XUTJ7ZWD.js";import{a as Fe,c as He,d as k,e as A,f as me,g as L,h as pe,i as je,j as fe,k as We,l as Ke,n as Je,o as Xe,p as Ze}from"./chunk-P6KVGV3H.js";import{m as N}from"./chunk-TZUABDIJ.js";import{a as qe,b as Ve}from"./chunk-QELIC5XP.js";import{h as nt,p as rt}from"./chunk-CSJ7L7ZL.js";import{a as Ye}from"./chunk-FVZBGPCU.js";import{a as Qe}from"./chunk-GZE57ORL.js";import{b as ce,e as Oe}from"./chunk-DGJMIVZM.js";import{b as Be,f as De,g as B,h as x,i as D,n as Ae,o as de,p as ue,r as Le,s as Ne,t as T,u as I,v as _}from"./chunk-CWCQP53A.js";import{b as z,c as m}from"./chunk-TH2UFGVP.js";import{f as Me}from"./chunk-CE4JQJ6Y.js";import{a as Ee}from"./chunk-W3FNXAQP.js";import{D as Te,M as ae,P as le,R as O,T as _e,l as se}from"./chunk-QRDF33WD.js";import{a as ie}from"./chunk-5YZYDEMI.js";import{spawn as pt,spawnSync as ft}from"node:child_process";import E from"node:fs";import qt from"node:os";import U from"node:path";import{WebSocket as he}from"ws";function st(e){return new Error(`could not resolve a native bundle for ${e}. pass an explicit bundle URL or open Connect and choose the app there.`)}function Bt(e,n){return e?.includes("/one/metro-entry.bundle")?"one":typeof n=="string"&&n?"expo":"unknown"}function Dt(e,n,t){return`${e}//${n}:${t}`}function At(e){if(typeof window>"u")return!1;try{let n=new URL(e);return k(n.hostname)&&k(window.location.hostname)&&n.origin!==window.location.origin}catch{return!1}}async function ct(e,n){let t={...n,cache:n?.cache??"no-store"};return At(e)?fetch(`/__fetch-proxy?url=${encodeURIComponent(e)}`,t):fetch(e,t)}function Lt(e){return e==="https:"?443:80}function Nt(e){let n=e.pathname||"/";return(n==="/"||n==="")&&!e.search&&!e.hash}async function dt(e){try{let n=await fetch("/__server-scan",{cache:"no-store"});if(!n.ok||!(n.headers.get("content-type")||"").toLowerCase().includes("json"))return null;let t=await n.json();if(!Array.isArray(t))return null;let r=t.find(o=>!!o&&typeof o=="object"&&o.port===e&&typeof o.bundleUrl=="string");return!r||r.bundleUrlProvisional?null:{bundleUrl:fe(r.bundleUrl),port:e,framework:typeof r.framework=="string"?r.framework:"unknown",projectName:typeof r.projectName=="string"?r.projectName:void 0}}catch{return null}}var Ft=5e3,Ht=3e4,jt=1e3;async function at(e,n){let t,r=ct(`${e}/`,{headers:{"expo-platform":"ios"}}).then(async c=>c.ok?{ok:!0,body:await c.text()}:null,()=>{}),o=await Promise.race([r,new Promise(c=>setTimeout(()=>c("timeout"),Ft).unref?.())]);if(o==="timeout"||o===void 0)return{state:"no-answer"};if(o===null)return{state:"no-manifest"};if(t=o.body,!t.trim())return{state:"no-answer"};let i;try{i=JSON.parse(t)}catch{return{state:"no-manifest"}}let a=i?.extra?.expoClient||i?.extra||{},s=typeof i?.launchAsset?.url=="string"?i.launchAsset.url:void 0,l=s?je(s,e):void 0;return!l&&!a.name?{state:"no-manifest"}:{state:"resolved",bundle:{bundleUrl:fe(l||`${e}${pe}`),port:n,framework:Bt(l,a.sdkVersion),projectName:a.name}}}async function lt(e){try{let n=await ct(`${e}/status`);return n.ok?(await n.text()).includes("packager-status:running"):!1}catch{return!1}}async function ut(e,n){let t=e.replace(/\/+$/,""),r=await at(t,n),o=!1;if(r.state==="no-answer"){if(o=await lt(t),!o)return null;let i=Date.now()+Ht;for(;r.state==="no-answer"&&Date.now()<i;)await new Promise(a=>setTimeout(a,jt)),r=await at(t,n)}return r.state==="resolved"?r.bundle:r.state==="no-answer"||!o&&!await lt(t)?null:{bundleUrl:`${t}${pe}`,port:n,framework:"metro"}}async function Wt(e,n){try{if(k(new URL(e).hostname)){let t=await dt(n);if(t)return t}}catch{}return ut(e,n)}async function Kt(e){let n=await dt(e);return n||((await Promise.all(Fe.map(r=>ut(He("http:",r,e),e)))).find(r=>r!==null)??null)}async function mt(e){let n=e.trim();if(/^\d+$/.test(n)){let s=parseInt(n,10),l=await Kt(s);if(l)return l;throw st(`localhost:${s}`)}let t=n.startsWith("http")?n:`http://${n}`,r;try{r=new URL(t)}catch{throw new Error(`could not parse "${e}". pass a dev-server port, a dev-server base URL, or a full bundle URL.`)}let o=r.protocol||"http:",i=r.port?parseInt(r.port,10):Lt(o),a=Dt(o,r.hostname,i);if(Nt(r)){let s=await Wt(a,i);if(s)return s;throw st(a)}return{bundleUrl:r.toString(),port:i,framework:"unknown"}}var Vt=12e4,gt=100,Gt=5e3,ht=1e4,zt={rn:"/rn",connectrn:"/rn","connect-rn":"/rn",clock:"/app/clock","native-ui":"/app/native-ui",tamagui:"/app/tamagui",settings:"/app/settings",photos:"/app/photos",camera:"/app/camera"};function $(e){return new Promise(n=>setTimeout(n,e))}function wt(e){let n=process.env[e]?.trim();if(!n)return null;let t=Number(n);return Number.isInteger(t)&&t>0?t:null}function Qt(){let e=wt("SOOTSIM_DRIVER_CONNECT_TIMEOUT_MS")??wt("SOOTSIM_PW_CONNECT_TIMEOUT_MS")??Vt;return{timeoutMs:e,hostTimeoutMs:e+Gt,intervalMs:gt,attempts:Math.max(1,Math.ceil(e/gt))}}function bt(e){return e==="electron"||e==="playwright"}function we(e,n){return n==="playwright"?e.meta?.sootsimHostDriver==="playwright":e.userAgent?.includes("Electron/")===!0}function Yt(e,n){return e.includes("--headless")?!0:e.includes("--headed")?!1:n==="playwright"&&process.platform==="linux"&&!process.env.DISPLAY&&!process.env.WAYLAND_DISPLAY?(console.error(" no display detected \u2192 running playwright headless (override with --headed)"),!0):!1}function Y(e){return e.trim()}async function Jt(e,n=!1){let t=le();vt(e,n,t);let r=de(e);if((n||r!==null)&&await be(e,250))return e;if(t&&O(t)&&await be(t.bridgePort,500))return t.bridgePort;if(t&&O(t))throw new Error(`rnx bridge lockfile is fresh (port ${t.bridgePort}) but the bridge is not reachable. run \`rnx daemon restart\` or \`rnx daemon uninstall\`.`);await Xt();let o=Zt(["serve","--quiet","--port",String(e)]),i=null;o.once("exit",(s,l)=>{i=String(s??l??"unknown")}),o.unref();let a=Date.now()+ht;for(;Date.now()<a;){let s=le();if(vt(e,n,s),s&&O(s)&&await be(s.bridgePort,250))return console.error(` Started a local bridge on port ${s.bridgePort}. Run \`rnx daemon install\` to keep it ready between commands.`),s.bridgePort;if(i)throw new Error(`rnx bridge exited before becoming ready (exit ${i})`);await $(150)}throw new Error(`rnx bridge did not start within ${Math.round(ht/1e3)}s`)}function vt(e,n,t){if(!(!n||!t||!O(t)||t.bridgePort===e))throw new Error(`${ie.commandName} open: requested bridge port ${e}, but the active daemon owns port ${t.bridgePort}. explicit --port never selects another bridge; stop that daemon or use an isolated SOOTSIM_HOME.`)}async function Xt(){let e=ae();if(e&&E.existsSync(se(e)))return;console.error(" installing rnx engine runtime...");let{runRuntime:n}=await import("./runtime-QU2NIF2K.js");await n(["install"],{})}function Zt(e){let n=process.argv[1];return n&&E.existsSync(n)?pt(process.execPath,[n,...e],{detached:!0,stdio:"ignore",env:process.env}):pt(ie.commandName,e,{detached:!0,stdio:"ignore",env:process.env})}function be(e,n){return new Promise(t=>{let r=new he(`ws://127.0.0.1:${e}`,{handshakeTimeout:n}),o=!1,i=a=>{o||(o=!0,r.readyState===he.OPEN?r.close():r.readyState===he.CONNECTING&&r.terminate(),t(a))};r.once("open",()=>i(!0)),r.on("error",()=>i(!1)),setTimeout(()=>i(!1),n)})}function ye(e){try{let n=new URL(e),t=n.pathname.replace(/\/+$/,"")||"/";return n.searchParams.has("open")||n.searchParams.has("port")||n.searchParams.has("bundle")||n.searchParams.has("demo")||n.pathname.includes("/sootsim/index.html")||t==="/sootsim"||n.pathname==="/__soot"||n.pathname==="/__soot/"||t==="/rn"||/^\/rn\/[^/]+$/i.test(t)||/^\/app\/[^/]+$/i.test(t)||t==="/__soot/rn"||/^\/__soot\/rn\/[^/]+$/i.test(t)||/^\/__soot\/app\/[^/]+$/i.test(t)}catch{return!1}}function en(e){try{let n=new URL(e,"http://sootsim.local");if(n.origin!=="http://sootsim.local")return!1;let t=n.pathname.replace(/\/+$/,"")||"/";return t==="/rn"||/^\/rn\/[^/]+$/i.test(t)||/^\/app\/[^/]+$/i.test(t)||t==="/__soot/rn"||/^\/__soot\/rn\/[^/]+$/i.test(t)||/^\/__soot\/app\/[^/]+$/i.test(t)}catch{return!1}}function tn(e,n){let t=new URL(e,"http://sootsim.local"),r=new URL(n),o=J(r.pathname),i=t.pathname.startsWith("/__soot/")?t.pathname.slice(7):t.pathname;return r.pathname=`${o}${i}`,r.search=t.search,r.hash=t.hash,r.toString()}function nn(e){try{let n=new URL(e).pathname.replace(/\/+$/,"")||"/";return/^\/preview\/[^/]+$/.test(n)||/^\/build\/.+/.test(n)}catch{return!1}}async function pr(e){let n=Y(e);return(await $e(n)).bundleUrl}function rn(e){try{let n=new URL(e.startsWith("http")?e:`http://${e}`),t=n.pathname||"/";if(t!=="/"&&t!==""||n.search||n.hash||!k(n.hostname))return null;let r=n.port?Number(n.port):n.protocol==="https:"?443:80;return Number.isFinite(r)&&r>0?r:null}catch{return null}}async function $e(e){let n=Y(e),t=/^\d+$/.test(n)?Number(n):rn(n);if(t&&t>0){let r=await Ke(t);if(r&&!r.bundleUrlProvisional)return{bundleUrl:r.bundleUrl,port:r.port,framework:r.framework,projectName:r.projectName}}return mt(n)}function J(e){let n=e.replace(/\/+$/,"")||"/";return n==="/__soot"||n.startsWith("/__soot/")?"/__soot":""}function on(e,n){let t=Y(e),r=new URL(n),o=J(r.pathname);return r.pathname=`${o}/rn`,r.searchParams.delete("bundle"),r.searchParams.delete("demo"),r.searchParams.delete("app"),r.searchParams.delete("open"),r.searchParams.delete("port"),/^\d+$/.test(t)?r.pathname=`${o}/rn/${t}`:(r.pathname=`${o}/rn`,r.searchParams.set("open",t)),r.toString()}function It(e){let n=Y(e);return/^\d+$/.test(n)||/^https?:\/\//i.test(n)?!0:/^(localhost|127\.0\.0\.1|\[::1\]|[^/]+\.localhost):\d+(?:\/.*)?$/i.test(n)}async function sn(e,n,t={}){let r=t.resolvedBundle??await $e(e),o=new URL(n),i=J(o.pathname);o.pathname=`${i}/rn`,o.searchParams.delete("open"),o.searchParams.delete("port"),o.searchParams.delete("demo"),o.searchParams.delete("app");let a=t.hot===void 0?r.bundleUrl:We(r.bundleUrl,t.hot);return o.searchParams.set("bundle",a),o.toString()}function an(e){return e.startsWith("~/")?U.join(qt.homedir(),e.slice(2)):U.isAbsolute(e)?e:U.resolve(process.cwd(),e)}function ln(e){let n={},t={};for(let o=0;o<e.length;o++){if(e[o]==="--replace"){let i=e[o+1];i||(console.error(" rnx open: --replace expects <module>=<file>"),m(1));let a=i.indexOf("=");(a<=0||a===i.length-1)&&(console.error(" rnx open: --replace expects <module>=<file>"),m(1));let s=i.slice(0,a).trim(),l=an(i.slice(a+1).trim());E.existsSync(l)||(console.error(` rnx open: replacement file not found: ${l}`),m(1)),n[s]={file:l},o++;continue}if(e[o]==="--remap"){let i=e[o+1],a=i?i.indexOf("="):-1;(!i||a<=0||a===i.length-1)&&(console.error(" rnx open: --remap expects <host:port>=<host:port>"),m(1)),t[i.slice(0,a).trim()]=i.slice(a+1).trim(),o++;continue}}let r={};return Object.keys(n).length>0&&(r.modules=n),Object.keys(t).length>0&&(r.network={remap:t}),r.modules||r.network?r:void 0}async function cn(e){if(!e)return null;let n=await Xe(e.bundleUrl);return n?Qe(n):null}async function dn(e){if(E.existsSync(U.join(se(e),"index.html")))return;console.error(` installing configured engine runtime ${e}...`);let n=await Me.install({version:e,setActive:!1,protectVersions:_e()});console.error(` installed engine runtime ${n.version}`)}function un(e,n){let t=n??(ge()?void 0:ae()??void 0);if(!t)return e;let r=new URL(e);if(!k(r.hostname)){if(!n)return e;throw new Error(`runtimeVersion ${t} requires a local rnx host; got ${r.origin}`)}return r.hostname=ze(t),r.toString()}function kt(){return Ae().shellBaseUrl}function mn(e){return de(e)?.shellBaseUrl??Be(e)??kt()}async function pn(e,n){try{let t=new URL(e),r=t.searchParams.get("bundle")||"";if(!r||!n)return e;let o=Je({bundleUrl:r,projectDir:n.appDir});if(o.length===0)return e;let i=await fn(o);return i.length===0?e:(t.searchParams.set("appFonts",Ze(i)),t.toString())}catch{return e}}async function fn(e){let n=await Promise.all(e.map(async r=>{let o=new AbortController,i=setTimeout(()=>o.abort(),1500);try{let a=await fetch(r.url,{method:"HEAD",signal:o.signal});return a.status>=200&&a.status<400?r:null}catch{return null}finally{clearTimeout(i)}})),t=[];for(let r of n)r!==null&&t.push(r);return t}async function gn(e,n,t={}){if(!e)return new URL(n).toString();if(ye(e))return new URL(e).toString();if(en(e))return tn(e,n);let r=zt[e.toLowerCase()];if(r){let o=new URL(n),i=J(o.pathname);return o.pathname=`${i}${r}`,o.toString()}return It(e)?sn(e,n,t):on(e,n)}async function hn(e,n){try{let t=new URL(e),r=t.searchParams.get("bundle")||"";if(!r||!n)return e;let o=et({bundleUrl:r,projectDir:n.appDir});return!o||o.imageUrl&&!await wn(o.imageUrl)?e:(t.searchParams.set("appSplash",tt(o)),t.toString())}catch{return e}}async function wn(e){let n=new AbortController,t=setTimeout(()=>n.abort(),1500);try{let r=await fetch(e,{method:"HEAD",signal:n.signal});return r.status>=200&&r.status<400}catch{return!1}finally{clearTimeout(t)}}async function Q(e,n=kt(),t={}){let r=await gn(e,n,t),o=await hn(await pn(r,t.project),t.project);if(!t.device)return o;let i=new URL(o);return i.searchParams.set("device",t.device),i.toString()}function yt(e,n){let t=e?.url||e?.origin||n;try{let r=new URL(t);return r.searchParams.delete("bundle"),r.searchParams.delete("demo"),r.searchParams.delete("app"),r.searchParams.delete("open"),r.searchParams.delete("port"),r.searchParams.delete("inspectOpen"),r.searchParams.delete("appFonts"),r.searchParams.delete("appSplash"),r.toString()}catch{return n}}async function bn(e,n,t,r={}){let o=new URL(await Q(e,n,r));return o.searchParams.set("inspectOpen",t),o.toString()}async function vn(e,n,t,r={}){let o=r.attempts??30,i=r.intervalMs??500,a=r.minNodeCount??10;for(let s=0;s<o;s++){let l=I(e,{commandTimeoutMs:n,simId:t,simIdSource:"flag"});try{let c=await l.send({type:"evaluate",code:"(async () => (await window.__sootsimTest?.getNodeCount()) || 0)()"});if(typeof c=="number"&&c>a)return{bridge:l,count:c}}catch{}l.close(),await $(i)}return null}function yn(e){if(!e)return null;try{let n=new URL(e);if(n.searchParams.has("bundle")){let t=n.searchParams.get("bundle")||"";try{let r=new URL(t),o=r.pathname.length>36?`...${r.pathname.slice(-36)}`:r.pathname;return`bundle ${r.host}${o}`}catch{return"bundle"}}return n.searchParams.has("port")?`connect :${n.searchParams.get("port")||""}`:n.searchParams.has("open")?`connect ${n.searchParams.get("open")||""}`:n.searchParams.has("demo")?`demo ${n.searchParams.get("demo")||"default"}`:n.pathname.includes("/sootsim/index.html")||n.pathname==="/sootsim/"||n.pathname==="/sootsim"?"embedded rnx":null}catch{return null}}function Sn(e){let n=e.indexOf(":");if(n<=0)return e.length>24?`${e.slice(0,21)}\u2026`:e;let t=e.slice(0,n),r=e.slice(n+1),o=r.length>12?`${r.slice(0,8)}\u2026`:r;return t==="TM_SESSION"||t==="CLAUDE_CODE_SESSION_ID"||t==="CODEX_THREAD_ID"?`agent ${o}`:`${t} ${o}`}function fr(e,n){if(e.length===0){console.log(" no sims connected");return}console.log(` connected sims (${e.length}):
5
+ `);for(let t of e){let r=t.lockedBy&&t.lockExpiresAt?`locked by ${Sn(t.lockedBy)} (${Math.max(0,Math.round((t.lockExpiresAt-Date.now())/1e3))}s)`:"",o=[t.isPrimary?"primary":"",t.id===n?"selected":"",t.readyState,t.userVisible===!1?"hidden":t.userVisible===!0?"visible":"",t.attachedCliCount&&t.attachedCliCount>0?"in use":"",t.userFocused?"focused":"",r].filter(Boolean);console.log(` ${t.id}${o.length?` [${o.join(", ")}]`:""}`);let i=yn(t.url);if(i)console.log(` loaded: ${i}`);else if(t.url){let s=t.url.length>96?`${t.url.slice(0,93)}\u2026`:t.url;console.log(` url: ${s}`)}else t.origin&&console.log(` origin: ${t.origin}`);let a=$n(t);a&&console.log(` engine: ${a}`),t.title&&console.log(` title: ${t.title}`),t.visibilityState&&t.visibilityState!=="visible"&&console.log(` visibility: ${t.visibilityState}`),console.log(` connected: ${St(Date.now()-t.connectedAt)}`),t.lastActiveAt&&t.lastActiveAt>0&&console.log(` last active: ${St(Date.now()-t.lastActiveAt)}`)}}function $n(e){let n=e.url||e.origin||"";if(!n)return null;try{let t=new URL(n).port;return t==="5173"?"dev \u2014 local source (:5173)":t==="3000"?"Contrast app embedded (:3000)":ge()?`dev \u2014 local build (:${t||"?"})`:`runtime \u2014 published engine (:${t||"?"})`}catch{return null}}function St(e){let n=Math.max(0,Math.round(e/1e3));return n<60?`${n}s ago`:n<3600?`${Math.round(n/60)}m ago`:n<86400?`${Math.round(n/3600)}h ago`:`${Math.round(n/86400)}d ago`}async function gr(e,n,t,r={}){let o=r.attempts??30,i=r.intervalMs??500;for(let a=0;a<o;a++){let s=I(e,{commandTimeoutMs:n});try{let c=(await s.listSims()).find(t);if(c)return c}catch{}finally{s.close()}await $(i)}return null}async function Pn(e,n,t,r,o={}){let i=o.attempts??30,a=o.intervalMs??500,s=!!r&&Number.isInteger(r)&&r>1,l=()=>s&&r!==void 0&&!Se(r);for(let c=0;c<i;c++){if(l())return{kind:"host-exit"};let u=I(e,{commandTimeoutMs:n});try{let h=(await u.listSims()).find(t);if(h)return l()?{kind:"host-exit"}:{kind:"match",sim:h}}catch{}finally{u.close()}if(l())return{kind:"host-exit"};await $(a)}return{kind:"timeout"}}async function xn(e,n,t,r={}){let o=r.attempts??20,i=r.intervalMs??250;for(let a=0;a<o;a++){let s=I(e,{commandTimeoutMs:n});try{let c=(await s.listSims()).find(u=>u.id===t);if(!c||c.readyState!=="open")return!0}catch{return!0}finally{s.close()}await $(i)}return!1}function In(e){let n=e.meta;if(!n||n.sootsimHostDriver!=="playwright")return null;let t=Number(n.sootsimHostPid);return!Number.isInteger(t)||t<=1||t===process.pid?null:t}function kn(e){if(!e.connectAckFile)return!1;try{return E.writeFileSync(e.connectAckFile,`${JSON.stringify({connectedAt:Date.now(),pid:e.pid??null})}
6
+ `,{flag:"w"}),!0}catch{return!1}}function $t(e){if(e.diagnosticLogPath)try{let n=E.readFileSync(e.diagnosticLogPath,"utf8").trim();if(!n){console.error(` ${U.basename(e.diagnosticLogPath)} was empty`);return}let t=n.length>4e3?`\u2026
7
+ ${n.slice(-4e3)}`:n;console.error(` ${U.basename(e.diagnosticLogPath)}:`),console.error(t)}catch(n){let t=n instanceof Error?n.message:String(n);console.error(` failed to read ${U.basename(e.diagnosticLogPath)}: ${t}`)}}function Se(e){try{return process.kill(e,0),!0}catch(n){return n?.code==="EPERM"}}async function Cn(e,n=2500){let t=Date.now()+n;for(;Date.now()<t;){if(!Se(e))return!0;await $(100)}return!Se(e)}function Un(e){let n=ft("ps",["-o","command=","-p",String(e)],{encoding:"utf8",stdio:["ignore","pipe","pipe"]}),t=ft("ps",["eww","-o","command=","-p",String(e)],{encoding:"utf8",stdio:["ignore","pipe","pipe"]});if(n.status!==0||t.status!==0)return!1;let r=(n.stdout||"").trimEnd(),o=(t.stdout||"").trimEnd();return(o.startsWith(r)?o.slice(r.length):o).includes("SOOTSIM_PW_URL=")}async function ve(e,n){let t=new Set(n),r=new Set;for(let o of e){if(!t.has(o.id))continue;let i=In(o);i&&r.add(i)}for(let o of r){if(!Un(o)){console.log(` skipped pid ${o}: not a rnx playwright host (stale sim record)`);continue}try{process.kill(o,"SIGTERM")}catch{}if(await Cn(o)){console.log(` closed playwright host process ${o}`);continue}try{process.kill(o,"SIGKILL"),console.log(` force-closed playwright host process ${o}`)}catch{}}}function X(e,n){if(n){let o=n.trim(),i=e.find(a=>a.id===o);if(!i)throw new Error(`no sim connected with id ${o}`);return i}let t=e.find(o=>o.isPrimary&&o.readyState==="open");if(t)return t;let r=e.find(o=>o.readyState==="open");if(r)return r;throw new Error("no sim connected")}function Pt(e,n,t,r=!1){let[o,i]=[e,n.url].map(a=>{if(!a)return a;try{let s=new URL(a),l=!1;s.searchParams.has(A)&&(s.searchParams.set(A,"[redacted]"),l=!0);let c=s.searchParams.get("bundle");if(c)try{let u=new URL(c);u.searchParams.has(A)&&(u.searchParams.set(A,"[redacted]"),s.searchParams.set("bundle",u.toString()),l=!0)}catch{}if(l)return s.toString()}catch{}return a});if(r){console.log(` Opened ConnectRN [${t}]`);return}console.log(` ${t==="current sim"?"loaded":"opened"}: ${o} [${t}]`),console.log(` current sim: ${n.id}`),console.log(JSON.stringify({simId:n.id,url:i},null,2))}async function xt(e,n,t){if(t.includes("--no-describe"))return;let r=process.env.SOOTSIM_QUIET_TARGET_NOTICE;process.env.SOOTSIM_QUIET_TARGET_NOTICE="1";try{await Rn(e,n,{stableMs:150,maxMs:400});let o=I(e,{commandTimeoutMs:3e3,simId:n,cliLabel:"open --describe",simIdSource:"flag"});try{let i=null;try{i=await rt(o,1)}catch{}if(i&&!i.ready){let s=i.loadingText?`still showing "${i.loadingText}"`:i.flag!==!0?"guest ready event has not fired":i.targets<=0?"no visible app content is inspectable yet":"app tree is still stabilizing";console.log(` app still loading: ${s} (nodes: ${i.nodes}, targets: ${i.targets})`),console.log(" before interacting, run: rnx wait ready --max-ms 120000")}let a=await nt(o,{describe:!0,verbose:!1,filter:"",compact:!0,hideXy:!1,maxDepth:12});if(!a?.tree)return;console.log(""),console.log(a.tree)}finally{o.close()}}catch{}finally{r===void 0?delete process.env.SOOTSIM_QUIET_TARGET_NOTICE:process.env.SOOTSIM_QUIET_TARGET_NOTICE=r}}async function Rn(e,n,t){let r=Date.now()+t.maxMs,o="(async () => (await window.__sootsimTest?.getNodeCount?.()) || 0)()",i=I(e,{commandTimeoutMs:2e3,simId:n,simIdSource:"flag"});try{let a=-1,s=0;for(;Date.now()<r;){let l=-1;try{let c=await i.send({type:"evaluate",code:o});typeof c=="number"&&(l=c)}catch{}if(l>=0&&l===a){if(Date.now()-s>=t.stableMs)return}else a=l,s=Date.now();await $(50)}}finally{i.close()}}async function Tn(e,n={}){let t=T(e,{port:n.port,commandTimeoutMs:n.timeoutMs});t.positional.length!==1&&(console.error(" rnx open: expected exactly one <url-or-route>"),console.error(" examples: rnx open /settings | rnx open myapp://settings"),m(1));let r=_(t),o=t.simId?` --sim ${t.simId}`:"";try{let i=await Ve(r,t.positional[0]??"");console.log(` opened deep link: ${i}`)}catch(i){console.error(` link failed: ${i instanceof Error?i.message:String(i)}`),await N(r,{errorsCommand:`rnx get errors 5${o}`,warningsCommand:`rnx get warnings 5${o}`,requestsCommand:`rnx get requests 5${o}`}),m(1)}finally{r.close()}}async function hr(e,n={}){let t=T(e,{port:n.port,commandTimeoutMs:n.timeoutMs,stripBooleanFlags:["--new","--headless","--headed","--ephemeral","--hot","--no-hmr","--no-describe","--quiet"],stripValueFlags:["--base-url","--replace","--driver","--profile","--cdp-port","--device","--viewport"]}),r=e.find((f,p)=>e[p-1]==="--profile"),o=e.includes("--quiet"),i=e.includes("--ephemeral"),a=e.find((f,p)=>e[p-1]==="--cdp-port"),s=a?Number(a):void 0;a&&(!Number.isFinite(s)||s<=0)&&(console.error(` rnx open: --cdp-port must be a positive port number, got "${a}"`),m(1)),r&&i&&(console.error(" rnx open: --profile cannot be combined with --ephemeral"),m(1));let l=r?Ge(r).id:void 0,c=e.includes("--new")||!!l||i,u=ln(e);c&&t.simIdSource==="flag"&&(console.error(" rnx open: --new, --profile, and --ephemeral cannot be combined with --sim"),m(1));let d=t.positional[0]||"";if(qe(d)){c&&(console.error(" rnx open: deep links target the current app; remove --new, --profile, or --ephemeral"),m(1)),await Tn(e,n);return}if(nn(d)){let f=(()=>{try{return k(new URL(d).hostname)}catch{return!1}})();console.log(" that\u2019s an rnx preview/build link \u2014 a full player page, not a driveable sim."),console.log(" opening it in your browser for viewing\u2026");try{await it(d,{background:!1})}catch(p){console.error(` could not launch a browser: ${p instanceof Error?p.message:String(p)}`),console.error(` open it yourself: ${d}`),m(1)}console.log(f?" to drive it under the CLI, point `rnx open` at the app\u2019s dev port instead (e.g. `rnx open 8081`).":" the CLI can\u2019t drive a remote preview (the engine only attaches to the local bridge from localhost). to drive the app, run it locally and `rnx open <port>`.");return}let h=It(d)?await $e(d):void 0,F=await cn(h),Z=await Ye(F),H=Z?.runtimeVersion,j=me(me(Z?{...Z,runtimeVersion:void 0}:void 0,n.runtimeConfig),u);H&&await dn(H);let ee=e.find((f,p)=>e[p-1]==="--base-url");if(ee&&!t.explicitPort){let f=ue(ee);f&&(t.wsPort=f)}t.wsPort=await Jt(t.wsPort,t.explicitPort);let M=e.find((f,p)=>e[p-1]==="--driver")||"";M&&!bt(M)&&(console.error(` unknown driver "${M}" \u2014 run \`rnx list --drivers\``),m(1));let v=bt(M)?M:process.env.CI==="1"||process.env.CI==="true"||process.env.GITHUB_ACTIONS==="true"||Ee()||!process.stdin.isTTY?"playwright":Te()||"playwright",Ut=Yt(e,v);s&&v!=="playwright"&&(console.error(" rnx open: --cdp-port is only honored by the playwright driver"),m(1));let Pe=e.includes("--hot"),xe=e.includes("--no-hmr");Pe&&xe&&(console.error(" rnx open: --hot and --no-hmr cannot be combined"),m(1));let Rt=Pe?!0:xe?!1:v!=="playwright",C=e.find((f,p)=>e[p-1]==="--device");C&&!(C in ce)&&(console.error(` rnx open: unknown device "${C}"`),console.error(` known devices: ${Oe().join(", ")}`),m(1));let te=C?ce[C]:void 0,W=e.find((f,p)=>e[p-1]==="--viewport"),K=W?/^(\d+)x(\d+)$/.exec(W):null;W&&!K&&(console.error(` rnx open: --viewport must be <width>x<height>, got "${W}"`),m(1));let Tt=K?{width:Number(K[1]),height:Number(K[2])}:void 0,q={hot:Rt,device:C,resolvedBundle:h,project:F},_t=H?`http://localhost:${t.wsPort}/`:ee||mn(t.wsPort),P=un(_t,H),Ie=e.includes("--base-url");Le(t.wsPort,P);let Et=B();if(!c){let f=_(t),p=t.simId?` --sim ${t.simId}`:"",oe=!1;try{let g=null,G=null;try{let w=await f.listSims();if(t.simIdSource==="saved")g=w.find(b=>b.id===Et&&b.readyState==="open"&&we(b,v))??null,g||D();else if(t.simIdSource==="flag"&&(g=X(w,t.simId),!we(g,v)))throw new Error(`sim ${g.id} is not managed by the ${v} driver`);if(!g&&t.simIdSource!=="flag"){G=await Q(d,L(P,j),q);let b=new URL(G);b.searchParams.delete("inspectOpen"),g=w.find(S=>{if(S.readyState!=="open"||!S.url||!we(S,v))return!1;try{let Re=new URL(S.url);return Re.searchParams.delete("inspectOpen"),Re.toString()===b.toString()}catch{return!1}})??null}if(!g)if(t.simIdSource!=="flag")oe=!0;else throw new Error("no sim connected");if(!oe&&g){let b=Ie||ye(d)?P:yt(g,P),S=G??await Q(d,L(b,j),q);f.send({type:"evaluate",simId:g.id,code:`window.location.href = ${JSON.stringify(S)}`}).catch(()=>{})}}catch(w){console.error(` open failed: ${w instanceof Error?w.message:String(w)}`),await N(f,{errorsCommand:`rnx get errors 5${p}`,warningsCommand:`rnx get warnings 5${p}`,requestsCommand:`rnx get requests 5${p}`}),m(1)}if(!oe&&g){await $(1500);let w=await vn(t.wsPort,t.commandTimeoutMs,g.id);w||(console.error(" timed out waiting for current sim to load target"),m(1)),w.bridge.close(),x(g.id);let b=Ie||ye(d)?P:yt(g,P),S=G??await Q(d,L(b,j),q);return Pt(S,{...g,url:S},"current sim",o),await xt(t.wsPort,g.id,e),t.wsPort}}finally{f.close()}}let Mt=L(P,j),ke=`cli-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`,Ce=await bn(d,Mt,ke,q),Ue=De();Ue||(console.error(" could not resolve a stable owning process; refusing unsupervised sim host"),m(1));let Ot=f=>f.url?.includes(`inspectOpen=${ke}`)??!1,R=ot(v);R||(console.error(` unknown driver "${v}" \u2014 run \`rnx list --drivers\``),m(1));let V=Qt(),y=await R.launch({url:Ce,bridgePort:ue(P)??t.wsPort,headless:Ut,profileId:l,ephemeralProfile:i,cdpPort:s,device:C,ownerPid:Ue,viewport:Tt??(te?{width:te.width+120,height:te.height+60}:void 0),connectTimeoutMs:V.hostTimeoutMs});y.launched||(console.error(` ${R.name} driver: ${y.message}`),m(1));let ne=await Pn(t.wsPort,t.commandTimeoutMs,Ot,y.pid,{attempts:V.attempts,intervalMs:V.intervalMs});if(ne.kind==="host-exit"&&($t(y),console.error(` ${R.name} host crashed \u2014 sim is no longer attached`),m(1)),ne.kind==="timeout"){if(y.pid)try{process.kill(y.pid,"SIGTERM"),console.error(` closed ${R.name} host process ${y.pid}`)}catch{}$t(y),console.error(` timed out after ${V.timeoutMs}ms waiting for opened sim to connect`),m(1)}let re=ne.sim;return x(re.id),kn(y),Pt(Ce,re,`${R.name} driver`,o),await xt(t.wsPort,re.id,e),t.wsPort}async function Ct(e,n={}){let t=T(e,{port:n.port,commandTimeoutMs:n.timeoutMs,stripBooleanFlags:["--force"]}),r=e.includes("--force"),o=_(t),i=t.simId?` --sim ${t.simId}`:"";try{try{let a=await o.listSims(),s=X(a,t.positional[0]||t.simId);if(s.lockedBy&&s.lockExpiresAt&&s.lockExpiresAt>Date.now()){let l=Math.max(0,Math.round((s.lockExpiresAt-Date.now())/1e3));s.lockedByKind==="user-active"&&(console.error(` refused: ${s.id} is locked by the active user (${l}s) \u2014 that's a live human tab`),console.error(" run `rnx open --new` for a fresh investigation sim"),m(1)),r||(console.error(` refused: ${s.id} is leased by ${s.lockedBy} (${l}s)`),console.error(` \`sootsim use ${s.id} --force\` to take it, or \`rnx open --new\` for a fresh sim`),m(1))}await o.focusSim(s.id),x(s.id),console.log(` using: ${s.id}`)}catch(a){z(a),console.error(` use failed: ${a instanceof Error?a.message:String(a)}`),await N(o,{errorsCommand:`rnx get errors 5${i}`,warningsCommand:`rnx get warnings 5${i}`,requestsCommand:`rnx get requests 5${i}`}),m(1)}}finally{o.close()}}async function wr(e,n={}){await Ct(e,n)}async function br(e,n={}){await Ct(e,n)}async function vr(e,n={}){let t=T(e,{port:n.port,commandTimeoutMs:n.timeoutMs,stripBooleanFlags:["--force"]}),r=e.includes("--force"),o=_(t);try{try{let i=await o.listSims(),a=X(i,t.positional[0]||t.simId),s=r&&a.lockedBy&&a.lockedByKind!=="user-active"?a.lockedBy:null,l=await o.claim(a.id,{force:r});x(a.id);let c=Math.max(0,Math.round((l.lockExpiresAt-Date.now())/1e3)),u=l.bootedCount>0?` (booted ${l.bootedCount})`:"";console.log(` claimed: ${l.simId} [${c}s]${u}`),s&&console.log(` took over from: ${s}`)}catch(i){if(i instanceof Ne){let a=Math.max(0,Math.round(i.lock.expiresInMs/1e3));console.error(` claim failed: locked by ${i.lock.by} for ${a}s more`),console.error(" use --force to take it, or `rnx open --new` for a fresh sim"),m(1)}console.error(` claim failed: ${i instanceof Error?i.message:String(i)}`),m(1)}}finally{o.close()}}async function _n(e,n,t,r){if(r.length===0)return{closed:[],remaining:[]};await Promise.all(r.map(i=>e.closeSim(i).catch(()=>{})));let o=new Set(r);for(let i=0;i<40;i++){let a=[];try{let s=I(n,{commandTimeoutMs:t});try{a=(await s.listSims()).filter(c=>o.has(c.id)&&c.readyState==="open").map(c=>c.id)}finally{s.close()}}catch{a=[]}if(a.length===0)return{closed:[...o],remaining:[]};if(i===39)return{closed:[...o].filter(s=>!a.includes(s)),remaining:a};await $(250)}return{closed:[...o],remaining:[]}}function En(e,n){let t=e.filter(d=>d.readyState==="open"),r=t.map(d=>d.id),o=n.explicitKeepId?.trim()||"",i=n.savedKeepId?.trim()||"",a=i?t.some(d=>d.id===i):!1,s=i&&!a?i:null,l=null,c=null,u=null;if(n.closeOthers)if(o){let d=t.find(h=>h.id===o);d?l=d.id:c=o}else if(i&&a)l=i;else{let d=t.find(h=>h.isPrimary)??t[0]??null;d&&(l=d.id,i&&!a&&(u=d.id))}return{openIds:r,keepId:l,targets:c?[]:r.filter(d=>d!==l),staleSavedId:s,missingExplicitKeepId:c,fallbackKeepId:u}}async function yr(e,n={}){let t=T(e,{port:n.port,commandTimeoutMs:n.timeoutMs,stripBooleanFlags:["--all","--others"]}),r=_(t),o=t.simId?` --sim ${t.simId}`:"",i=e.includes("--all"),a=e.includes("--others");if(i||a){try{let s=await r.listSims(),l=B(),c=t.positional[0]||(t.simIdSource==="flag"?t.simId:void 0),u=En(s,{closeOthers:a,explicitKeepId:c,savedKeepId:l});if(u.staleSavedId&&D(),u.fallbackKeepId&&(x(u.fallbackKeepId),console.log(` saved sim ${u.staleSavedId} is gone \u2014 keeping primary ${u.fallbackKeepId}`)),u.missingExplicitKeepId&&(console.error(` close failed: keep sim ${u.missingExplicitKeepId} is not connected; not closing other sims`),m(1)),u.targets.length===0){console.log(u.keepId?` nothing to close \u2014 only the kept sim ${u.keepId} is connected`:u.staleSavedId?` nothing to close \u2014 no sims connected (cleared stale current sim ${u.staleSavedId})`:" nothing to close \u2014 no sims connected");return}let d=await _n(r,t.wsPort,t.commandTimeoutMs,u.targets);await ve(s,u.targets);let h=B();h&&d.closed.includes(h)&&(u.keepId?x(u.keepId):D());let F=` closed ${d.closed.length} sim(s)${u.keepId?` (kept ${u.keepId})`:""}`;console.log(F),d.remaining.length>0&&(console.error(` close failed: still connected: ${d.remaining.join(", ")}`),m(1))}catch(s){z(s),console.error(` close failed: ${s instanceof Error?s.message:String(s)}`),m(1)}finally{r.close()}return}try{try{let s=await r.listSims(),l=X(s,t.positional[0]||t.simId),c=s.find(d=>d.id!==l.id&&d.readyState==="open");await r.closeSim(l.id),await xn(t.wsPort,t.commandTimeoutMs,l.id)||(await ve(s,[l.id]),console.error(` close failed: ${l.id} is still connected`),m(1)),await ve(s,[l.id]),B()===l.id&&(c?x(c.id):D()),console.log(` closed: ${l.id}`)}catch(s){z(s),console.error(` close failed: ${s instanceof Error?s.message:String(s)}`),await N(r,{errorsCommand:`rnx get errors 5${o}`,warningsCommand:`rnx get warnings 5${o}`,requestsCommand:`rnx get requests 5${o}`}),m(1)}}finally{r.close()}}export{Qt as a,Jt as b,pr as c,un as d,kt as e,mn as f,Q as g,bn as h,yn as i,fr as j,gr as k,Pn as l,In as m,kn as n,ve as o,Pt as p,Tn as q,hr as r,wr as s,br as t,vr as u,_n as v,En as w,yr as x};
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.376 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import { createRequire as __sootsimCreateRequire } from 'node:module'
3
3
  var require = __sootsimCreateRequire(import.meta.url)
4
4
  var n=Object.freeze({name:"rnx",packageName:"rnxsim",commandName:"rnx",domain:"rnxsim.com",origin:"https://rnxsim.com"});export{n as a};
@@ -1,7 +1,7 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.376 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import { createRequire as __sootsimCreateRequire } from 'node:module'
3
3
  var require = __sootsimCreateRequire(import.meta.url)
4
- import{m as a}from"./chunk-2BRBFLWA.js";import{t as l,v as i}from"./chunk-7NCPUZ52.js";function c(r){if(!r||typeof r!="object")return!1;let t=Reflect.get(r,"strategy");return typeof Reflect.get(r,"ok")=="boolean"&&(t==="data"||t==="full")&&Array.isArray(Reflect.get(r,"cleared"))&&typeof Reflect.get(r,"relaunched")=="boolean"}function d(){console.log(`
4
+ import{m as a}from"./chunk-TZUABDIJ.js";import{t as l,v as i}from"./chunk-CWCQP53A.js";function c(r){if(!r||typeof r!="object")return!1;let t=Reflect.get(r,"strategy");return typeof Reflect.get(r,"ok")=="boolean"&&(t==="data"||t==="full")&&Array.isArray(Reflect.get(r,"cleared"))&&typeof Reflect.get(r,"relaunched")=="boolean"}function d(){console.log(`
5
5
  rnx reset - clear the current guest app and relaunch it
6
6
 
7
7
  usage:
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.376 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import { createRequire as __sootsimCreateRequire } from 'node:module'
3
3
  var require = __sootsimCreateRequire(import.meta.url)
4
- import{i as s}from"./chunk-S65CK3RI.js";import{a as t}from"./chunk-LMHBU5LZ.js";import{F as o,G as n}from"./chunk-HWDVC6YA.js";function a(){return process.env.CI==="1"||process.env.CI==="true"||process.env.GITHUB_ACTIONS==="true"}function c(){return process.stdin.isTTY===!0&&!a()&&!t()}function l(e=o()){let r=process.env.DO_NOT_TRACK;return r==="1"||r==="true"||!c()?!1:e.configured&&e.productAnalytics}async function P(e){let r=o();if(r.configured)return r;if(!e.prompt||!c())return{productAnalytics:!1,crashReports:!1,configured:!1};let i=await s("Share anonymous package incompatibilities and error classes to improve rnx?",!1);return n({productAnalytics:i,crashReports:!1}),o()}export{c as a,l as b,P as c};
4
+ import{i as s}from"./chunk-BEU7CD7E.js";import{a as t}from"./chunk-W3FNXAQP.js";import{F as o,G as n}from"./chunk-QRDF33WD.js";function a(){return process.env.CI==="1"||process.env.CI==="true"||process.env.GITHUB_ACTIONS==="true"}function c(){return process.stdin.isTTY===!0&&!a()&&!t()}function l(e=o()){let r=process.env.DO_NOT_TRACK;return r==="1"||r==="true"||!c()?!1:e.configured&&e.productAnalytics}async function P(e){let r=o();if(r.configured)return r;if(!e.prompt||!c())return{productAnalytics:!1,crashReports:!1,configured:!1};let i=await s("Share anonymous package incompatibilities and error classes to improve rnx?",!1);return n({productAnalytics:i,crashReports:!1}),o()}export{c as a,l as b,P as c};
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.376 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import { createRequire as __sootsimCreateRequire } from 'node:module'
3
3
  var require = __sootsimCreateRequire(import.meta.url)
4
4
  var a=["Cache","Code Cache","DawnCache","DawnGraphiteCache","DawnWebGPUCache","GPUCache","GrShaderCache","ShaderCache"],r=new Set(a);function s(e){return r.has(e)}export{a,s as b};
@@ -1,7 +1,7 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.376 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import { createRequire as __sootsimCreateRequire } from 'node:module'
3
3
  var require = __sootsimCreateRequire(import.meta.url)
4
- import{b as Ht,c as Kt}from"./chunk-MXJ2UWRV.js";import{a as Jt,b as qt}from"./chunk-CCMZSHJB.js";import{a as ce}from"./chunk-RLCQLMFF.js";import{a as ne,b as ye,c as Bt,d as Ct,e as Dt,f as Lt,g as Wt}from"./chunk-V7RGRZOL.js";import{a as he,b as Fe}from"./chunk-YX52RJKO.js";import{j as jt}from"./chunk-EWPAI4MO.js";import{a as Z,b as D,c as C,d as Ie,e as oe,f as G,g as me,h as ge,i as At,j as Pt,k as Me,l as _t}from"./chunk-2BRBFLWA.js";import{C as Et,E as Ot,G as ue,H as Rt,N as We,a as Se,b as pt,c as ke,d as ft,e as gt,f as yt,g as ht,h as bt,i as wt,j as $t,k as xt,l as ve,m as Be,n as Ce,o as De,p as St,q as kt,r as fe,s as vt,t as Ne,u as Le,v as Nt,w as It,x as Mt,y as Ft,z as Tt}from"./chunk-5PZVYF42.js";import{a as X}from"./chunk-ZXFVBYJY.js";import{e as it,j as at,r as lt,t as ct,v as dt,w as ut,x as mt,y as le}from"./chunk-7NCPUZ52.js";import{a as ot}from"./chunk-4CEZZKRY.js";import{b as st,c as y}from"./chunk-T4ISWS5H.js";import{c as nt,d as je,f as rt}from"./chunk-XUBV77WH.js";import{a as de}from"./chunk-LMHBU5LZ.js";function Je(e){return`(async () => {
4
+ import{b as Ht,c as Kt}from"./chunk-DEV3E3MV.js";import{a as Jt,b as qt}from"./chunk-37MCSPTE.js";import{a as ce}from"./chunk-CIE6CZXL.js";import{a as ne,b as ye,c as Bt,d as Ct,e as Dt,f as Lt,g as Wt}from"./chunk-KMNVDZZI.js";import{a as he,b as Fe}from"./chunk-YTXY3MHW.js";import{j as jt}from"./chunk-4TQ5HOK3.js";import{a as Z,b as D,c as C,d as Ie,e as oe,f as G,g as me,h as ge,i as At,j as Pt,k as Me,l as _t}from"./chunk-TZUABDIJ.js";import{C as Et,E as Ot,G as ue,H as Rt,N as We,a as Se,b as pt,c as ke,d as ft,e as gt,f as yt,g as ht,h as bt,i as wt,j as $t,k as xt,l as ve,m as Be,n as Ce,o as De,p as St,q as kt,r as fe,s as vt,t as Ne,u as Le,v as Nt,w as It,x as Mt,y as Ft,z as Tt}from"./chunk-CSJ7L7ZL.js";import{a as X}from"./chunk-VQDYFFIG.js";import{e as it,j as at,r as lt,t as ct,v as dt,w as ut,x as mt,y as le}from"./chunk-CWCQP53A.js";import{a as ot}from"./chunk-LQMUA5IY.js";import{b as st,c as y}from"./chunk-TH2UFGVP.js";import{c as nt,d as je,f as rt}from"./chunk-AFOVILIL.js";import{a as de}from"./chunk-W3FNXAQP.js";function Je(e){return`(async () => {
5
5
  const bridge = globalThis.SootSim && globalThis.SootSim.bridges && globalThis.SootSim.bridges.hotRemount
6
6
  if (!bridge || typeof bridge.reloadExternalApp !== 'function') return false
7
7
  await bridge.reloadExternalApp({ resetStorage: ${e?"true":"false"} })
@@ -112,7 +112,7 @@ import{b as Ht,c as Kt}from"./chunk-MXJ2UWRV.js";import{a as Jt,b as qt}from"./c
112
112
  `),y(0))}let o=["do","get","debug","wait"].map(s=>je(s,S)).filter(s=>s!=null).join(`
113
113
 
114
114
  `);console.log(`${o}
115
- `),y(0)}let T=u.wsPort,x=u.simId,P=u.simIdSource,O=u.commandTimeoutMs;if(b==="get"&&m==="diagnosis"){let S=l.indexOf("diagnosis"),o=S>=0?[...l.slice(0,S),...l.slice(S+1)]:l,{runDiagnose:s}=await import("./diagnose-PJMPXKJY.js"),n=await s(["recent",...o],t);process.exitCode=n;return}if(b==="do"&&m==="scan"){let{runCamera:S}=await import("./camera-J3V62PP7.js");process.exitCode=await S(l,{port:t.port});return}if(m==="list"&&l.some(S=>S==="--drivers"||S==="-D")){let{buildDriverListRows:S}=await import("./drivers-ZFNX3T5Z.js"),o=S();console.log(` available drivers (${o.length}):
115
+ `),y(0)}let T=u.wsPort,x=u.simId,P=u.simIdSource,O=u.commandTimeoutMs;if(b==="get"&&m==="diagnosis"){let S=l.indexOf("diagnosis"),o=S>=0?[...l.slice(0,S),...l.slice(S+1)]:l,{runDiagnose:s}=await import("./diagnose-2JBGYE5L.js"),n=await s(["recent",...o],t);process.exitCode=n;return}if(b==="do"&&m==="scan"){let{runCamera:S}=await import("./camera-FNJVFCDQ.js");process.exitCode=await S(l,{port:t.port});return}if(m==="list"&&l.some(S=>S==="--drivers"||S==="-D")){let{buildDriverListRows:S}=await import("./drivers-JMA2OIKU.js"),o=S();console.log(` available drivers (${o.length}):
116
116
  `);let s=Math.max(...o.map(i=>i.id.length),6),n=Math.max(...o.map(i=>i.kind.length),4);for(let i of o){let d=i.available?"\u2713":"\u2717",p=i.id.padEnd(s),f=i.kind.padEnd(n);console.log(` ${d} ${p} ${f} ${i.description}`),i.available&&i.detail?console.log(` ${i.detail}`):!i.available&&i.reason&&console.log(` unavailable: ${i.reason}`)}return}let c=dt(u),j=x||"default",A=new Set(["errors","warnings","requests","js","reload","globals","perf","storage-clear","list","wait","sleep"]),B=200;function H(S){let o=S.replace(/\s+/g," ").trim();if(!o)return"";if(/^<(!doctype html|html|\?xml)|<body[\s>]/i.test(o)){let n=/<title[^>]*>([^<]+)<\/title>/i.exec(S)?.[1]?.trim(),i=/<body[^>]*>([\s\S]*?)<\//i.exec(S)?.[1]?.replace(/<[^>]+>/g," ").replace(/\s+/g," ").trim().slice(0,80),d=n||i||"html error page";return`<html ${S.length}B> "${d}" (body elided \u2014 add --json for the full payload)`}return o.length<=B?o:`${o.slice(0,B)}\u2026 (+${o.length-B} more bytes)`}function W(S){let o=S.displayUrl||S.url;return S.status!=null?`${S.method} ${o} -> ${S.status}${S.statusText?` ${S.statusText}`:""}`:S.error?`${S.method} ${o} -> ${S.error}`:`${S.method} ${o}`}async function F(S){let o=de()?400:200;try{let{settled:s,elapsed:n}=await ne({bridge:S,maxMs:o,pollMs:32,stablePolls:2});s||process.stderr.write(` \u26A0 auto-wait timed out after ${n??o}ms \u2014 next command may see mid-animation state. use \`rnx do settle\` for a longer wait.
117
117
  `)}catch{}}function _(S,o){if(o.result?.reason==="offscreen"){let{x:s,y:n,screen:i}=o.result;console.error(` tap failed: ${S} resolved to (${Math.round(s)},${Math.round(n)}), outside the ${i?.width}x${i?.height} screen`),console.error(" the node exists but is scrolled out of view \u2014 scroll it on-screen first (rnx do scroll / swipe), then tap.");return}console.error(` tap failed: ${S} stayed visible but did not receive a hittable press after ${o.attempts} attempt${o.attempts===1?"":"s"}`),o.result&&console.error(` last result: ${JSON.stringify(o.result)}`)}async function V(){try{return await c.send({type:"evaluate",code:`(() => ({
118
118
  console: ${Ne},
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.376 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import { createRequire as __sootsimCreateRequire } from 'node:module'
3
3
  var require = __sootsimCreateRequire(import.meta.url)
4
4
  var v={bridgePort:7668,defaultShellUrl:"http://localhost:5173/"};function t(r,n){return{flag:r,description:n}}function e(r,n){return{command:r,description:n}}var D=[{id:"cleanup",slug:"cleanup",navTitle:"cleanup",title:"rnx cleanup",description:"inspect and reclaim local rnx disk usage",summary:"Preview reclaimable disk usage, then remove downloaded runtime archives, excess inactive runtimes, and disposable browser caches without touching the active runtime or persistent app storage. Aggressive mode also removes the rollback runtime and user-created recordings.",order:58,usage:["rnx cleanup","rnx cleanup --apply","rnx cleanup --aggressive","rnx cleanup --apply --aggressive"],options:[t("--apply","perform the previewed cleanup; without it the command is read-only"),t("--aggressive","also remove the rollback runtime plus user-created recordings and captured frames")],examples:[e("rnx cleanup","show exact reclaimable disk usage without deleting"),e("rnx cleanup --apply","reclaim safe caches and old runtimes"),e("rnx cleanup --apply --aggressive","also remove the rollback runtime and recordings")],related:["runtime","storage","daemon"]},{id:"record",slug:"record",navTitle:"record",title:"rnx record",description:"capture the live sim as webm/mp4/gif, or sample N png frames (--frames)",summary:"Drives the engine's built-in canvas recorder over the WS bridge: no second browser, no ffmpeg. Encoding happens in the running page (MediaRecorder for webm, WebCodecs for mp4, gifenc for gif). Two modes: atomic (duration-bounded) or stateful (start \u2192 interact \u2192 stop). Stateful mode is webm/mp4 only; for gif/png use atomic mode. Use --frames to sample N evenly-spaced png snapshots instead of a video. NOTE on `live`/`combined`: the `/preview/<id>` id is derived from the app's bundle identity, not the recording, so re-recording the same app reuses the same URL and overwrites the previous preview. This is intentional (re-record replaces in place); keep a recording you want to preserve by copying its `/preview/<id>` page elsewhere before recording the same app again.",order:60,usage:["rnx record [options]","rnx record start [options]","rnx record prelude-start [--origin <url>]","rnx record stop [--output <path>]","rnx record status # reports the record-start session; also probes the engine","rnx record cancel # cancels a record-start session","rnx record upload (--events <path> | --video <path>) [--open]"],options:[t("--format <type>","webm | mp4 | gif | png (inferred from --output extension; stateful mode is webm/mp4 only)"),t("--mode <kind>","video | live | combined (default: video). live captures the event stream; combined captures both and uploads automatically. NOTE: live/combined upload to /preview/<id> whose id is bundle-derived, so re-recording the same app overwrites the previous preview"),t("--duration <seconds>","recording duration (default: 10, atomic mode only)"),t("--fps <number>","target frame rate for video/gif (default: 30)"),t("--output <path>","output file, or directory when --frames is set"),t("--frames <n>","sample N evenly-spaced png frames instead of a video"),t("--max-width <px>","downscale gif frames to this width"),t("--lockstep","forced-perfect-frame-rate capture (record start/stop, mp4 only): step the engine clock exactly 1/fps per frame so every stored frame is freshly rendered unique content. virtual time runs slower than wall time, so wall-clock app timers and live gesture pacing compress relative to the output timeline"),t("--no-shell","exclude the simulated iOS chrome (status bar, keyboard, toasts) and record the tenant surfaces only"),t("--shell-only","record only the shell chrome"),t("--open","open the uploaded preview URL in the browser after stop (live/combined)"),t("--sim <sim>","target a specific sim"),t("--origin <url>","billing/upload origin for live/combined (default: auto, which probes a local :3000 stack, else https://contrast.dev; also honors SOOTSIM_UPLOAD_ORIGIN)"),t("--owner <org>","associate live/combined preview uploads with a linked org repo; pass with --repo"),t("--repo <repo>","repo name for --owner; the signed-in user must belong to the owning org team"),t("--events <path>","for upload: gzipped events .jsonl.gz file to attach"),t("--video <path>","for upload: webm/mp4/gif recording to attach"),t("--assets-only","for upload: keep assets only; replay API calls live")],examples:[e("rnx record --duration 5"),e("rnx record --format mp4 --output demo.mp4 --duration 8"),e("rnx record --output demo.gif --duration 3"),e("rnx record --frames 10 --output ./frames/"),e("rnx record --mode combined --duration 8 --open --owner my-org --repo my-app","capture video + events and upload a repo-associated preview link"),e("rnx record start --format mp4","begin; interact freely across any number of CLI calls"),e("rnx record start --mode combined","start a stateful preview-share recording"),e("rnx record prelude-start","capture hidden setup events before starting the visible proof segment"),e("rnx record stop --output flow.mp4","finalize + save"),e("rnx record cancel","discard a record-start session (atomic --duration runs own their own lifecycle)"),e("rnx record upload --events ./my-run.jsonl.gz --open"),e("rnx record upload --video /tmp/sootsim-flow.webm")],related:["screenshot","maestro"]},{id:"film",slug:"film",navTitle:"film",title:"rnx film",description:"record a flow on the flat canvas, replay it on the 3d device stage under a camera cinematic, save a clip",summary:"Two-phase cinematic capture. Running a flow inside live 3d mode is too slow to look good (the guest app, the shell, and the WebGL stage compete for the same cores), so film first runs the flow at full speed on the flat canvas while the engine recording store captures the device screen, then brings up 3d mode + the recording editor, plays that capture back on the phone screen while a camera script animates, and records the WebGL stage canvas. Stage decor (background, colorway, environment, room, focus) accepts the same flags as `rnx mode three configure`.",order:61,usage:["rnx film <flow.yaml> [options]"],options:[t("--out <path>","output file (.webm, or .mp4 when ffmpeg is installed). default: ~/Desktop/sootsim-films/<flow>.webm"),t("--script <id>","camera script to animate during playback (default: hero-arc)"),t("--fps <n>","stage capture framerate (default: 30)"),t("--slow <ms>","per-step delay forwarded to the flow runner"),t("--keep-three","leave 3d mode + the recording editor on afterwards"),t("--background <id>","stage background (see mode three configure)"),t("--gradient <stops>","custom gradient background stops"),t("--colorway <id>","phone colorway"),t("--environment on|off","environment objects, with --env-shape/spread/size/color/glow/plastic"),t("--room on|off","room effect"),t("--focus on|off","depth-of-field, with --focus-amount / --focus-range"),t("--sim <id>","target a specific connected sim")],examples:[e("rnx film flows/feed-scroll.yaml"),e("rnx film flows/swap.yaml --script hero-arc --background gradient-tide --out ~/Desktop/swap-3d.mp4"),e("rnx film flows/login.yaml --colorway deep-blue --environment on --env-shape pane --room on")],related:["maestro","record","mode"]},{id:"storage",slug:"storage",navTitle:"storage",title:"rnx storage",description:"manage guest app storage and launch profiles",summary:"Clear the current guest app's local storage through the live reload bridge, or manage persistent storage profiles for Electron and Playwright launches. Profiles isolate cookies, localStorage, IndexedDB, cache, permissions, and service workers. Selecting a profile creates a fresh managed sim because storage identity is fixed at launch.",order:65,usage:["rnx storage clear [--sim <id>] [--json]","rnx storage profile list","rnx storage profile create <id>","rnx storage profile clear <id>","rnx storage profile delete <id>"],options:[t("--sim <id>","target a specific connected sim for `storage clear`"),t("--json","emit { cleared, ready, reload } for `storage clear`"),t("<id>","profile id: letters, numbers, dot, dash, or underscore")],examples:[e("rnx storage clear"),e("rnx storage clear --sim a9 --json"),e("rnx storage profile list"),e("rnx storage profile create qa"),e("rnx open 8081 --profile qa --driver playwright"),e("rnx storage profile clear qa")],related:["open","desktop"]},{id:"permissions",slug:"permissions",navTitle:"permissions",title:"rnx permissions",description:"inspect and change simulated operating-system permissions",summary:"Read the installed-app permission registry or set and reset one app permission through the typed simulator bridge. Changes are immediately visible to guest permission APIs and the Settings app.",order:65.5,usage:["rnx permissions list [--json]","rnx permissions get <app-id> [--json]","rnx permissions set <app-id> <kind> <status> [options]","rnx permissions reset <app-id> [kind] [--json]"],options:[t("--once","make an authorized grant expire when the app closes"),t("--accuracy <value>","full or reduced location accuracy"),t("--json","emit structured JSON"),t("--sim <id>","target a specific connected sim")],examples:[e("rnx permissions list"),e("rnx permissions get dock-8081 --json"),e("rnx permissions set dock-8081 camera denied"),e("rnx permissions set dock-8081 photo-library limited"),e("rnx permissions reset dock-8081 camera")],related:["storage","open"]},{id:"perf",slug:"perf",navTitle:"perf",title:"rnx perf",description:"CPU, React render, shell frame, and scroll-layer profiling",summary:"One profiling group for sampled JS CPU profiles, React render-cost tables, shell and compositor frame timing, and timestamp-aligned scroll offsets from the tenant, shell, and compositor workers.",order:66,usage:["rnx perf cpu [options]","rnx perf react <profile|summary|slow|rerenders|tree|why> [options]","rnx perf shell <start|stop|transition> [options]","rnx perf scroll <start|stop> [options]"],options:[t("--cdp-port <port>","for `perf cpu`: Chrome DevTools port (default: $SOOTSIM_CDP_PORT or 9222)"),t("--duration <seconds>","for `perf cpu`: recording duration (default: 5)"),t("--match <substring>","for `perf cpu`: pick the page target by URL substring"),t("--output <path>","for `perf cpu`: output .cpuprofile path"),t("--include-props","for `perf react profile start`: record shallow prop previews"),t("--limit <n>","for `perf react`: rows; for `perf scroll`: samples per layer"),t("--json","structured output where supported"),t("--sim <sim>","target a specific connected sim")],examples:[e("rnx open 8089 --new --driver playwright --cdp-port 9222"),e("rnx perf cpu --cdp-port 9222 --duration 5"),e("rnx perf react profile start"),e("rnx perf react summary --limit 10"),e("rnx perf shell start"),e("rnx perf shell stop --json"),e("rnx perf scroll start"),e("rnx perf scroll stop --json")],related:["open","record","maestro","debug"]},{id:"screenshot",slug:"screenshot",navTitle:"screenshot",title:"rnx screenshot",description:"capture PNGs and manage App Store screenshot assets",summary:"One screenshot surface: capture the current canvas as a PNG, drive the live App Store slide composer, or run the plan/headless App Store capture pipeline. Bare `rnx screenshot` is the same as `rnx screenshot capture`.",order:70,usage:["rnx screenshot [capture] [output] [options]","rnx screenshot layers --ids <testID,testID,...> [--out-dir <dir>]","rnx screenshot slides <list|set|add|remove|focus|overview|capture|export> [args]","rnx screenshot appstore --plan <plan.yaml> [options]","rnx screenshot appstore capture --org <org> --repo <repo> [options]"],options:[t("--output <path>","for capture: output PNG path (default: /tmp/sootsim-inspect.png). aliases: -o, --out, --file, --path"),t("--area <x,y,w,h>","for capture: crop to a logical rnx rect. aliases: --rect, --crop"),t("--id <testID>","for capture: crop to the bounding box of a node by testID. alias: --testid"),t("--text <text>","for capture: crop to the bounding box of a node by text content"),t("--no-shell","for capture/flows: exclude simulated iOS chrome"),t("--shell-only","for capture/flows: capture only shell chrome"),t("--plan <path>","for appstore: screenshot plan yaml"),t("--org <org>","for appstore capture: github org slug"),t("--repo <repo>","for appstore capture: repo name"),t("--compose-only","for appstore: skip flow capture and reuse existing raws"),t("--capture-only","for appstore: stop after raw/framed intermediates"),t("--ids <list>","for layers: comma-separated testIDs of the subtrees to isolate"),t("--out-dir <dir>","for layers: output directory (default: /tmp/sootsim-layers)")],examples:[e("rnx screenshot","capture the current sim to a PNG"),e("rnx screenshot --output app.png"),e("rnx screenshot capture --no-shell --output tenant-only.png"),e("rnx screenshot --area 0,200,393,400"),e("rnx screenshot slides","list the current live deck"),e("rnx screenshot slides capture 1"),e("rnx screenshot slides export --out ./exports"),e("rnx screenshot appstore --plan .sootsim/app-store.yaml"),e("rnx screenshot appstore --plan .sootsim/app-store.yaml --compose-only","rerender marketing canvases from existing raw PNGs"),e("rnx screenshot appstore capture --org acme --repo my-app --dry-run","headlessly capture editable org-deck slides without uploading")],related:["mode","record","maestro"]},{id:"mode",slug:"mode",navTitle:"mode",title:"rnx mode",description:"toggle screenshot and 3D display modes",summary:"Toggle live shell display modes using the same plugin-owned action paths as the rail and menus. `mode screenshot` hides browser chrome for clean captures; `mode three` toggles and configures the live 3D device stage.",order:73,usage:["rnx mode screenshot [on|off|toggle]","rnx mode three [on|off|toggle]","rnx mode three configure [flags]","rnx mode three load <timeline.json>","rnx mode three seek <time-ms>","rnx mode three play|pause","rnx mode three render <timeline.json> --output <dir> [flags]"],options:[t("screenshot on|off|toggle","toggle screenshot-mode chrome overlay"),t("three on|off|toggle","toggle the live 3D device stage"),t("--background <id>","set the 3d background"),t("--gradient <stops>","custom gradient bg: comma-separated css hex stops (overrides --background)"),t("--gradient-angle <deg>","custom gradient angle (default 180 = top\u2192bottom)"),t("--colorway <id>","set the phone colorway"),t("--script <id>","apply a camera preset pose"),t("--progress <0..1>","script progress to sample (default: 1)"),t("--animate","run the script instead of sampling one frame"),t("--slow","run the script slowly when --animate is set"),t("--environment <on|off>","enable or disable environment objects"),t("--env-shape <id>","orb | pill | diamond | pane"),t("--env-spread <id>","tight | medium | wide"),t("--env-size <id>","small | medium | large"),t("--env-color <id>","peach | gold | mint | sky | violet"),t("--env-glow <on|off>","enable or disable environment glow"),t("--env-plastic <on|off>","enable or disable environment plastic material"),t("--setting <id>","none | room | white-backdrop"),t("--focus <on|off>","enable or disable Focus depth of field"),t("--focus-amount <id>","subtle | medium | strong | dramatic"),t("--focus-range <id>","tight | balanced | wide"),t("--reset-pose","return to the default hero pose before other changes"),t("--output <dir>","for render: frame-%05d.png sequence output directory"),t("--fps <number>","for render: exact frame rate (default 30)"),t("--width <px>","for render: exact frame width (default 1920)"),t("--height <px>","for render: exact frame height (default 1080)"),t("--from <ms>","for render: first timeline time (default 0)"),t("--to <ms>","for render: exclusive timeline end time"),t("--overwrite","for render: replace existing timeline frames in output")],examples:[e("rnx mode screenshot on","hide browser chrome around the canvas"),e("rnx mode screenshot off"),e("rnx mode three on","force 3d mode on"),e("rnx mode three configure --colorway sky --background gradient-sunset","set the phone and background to specific options"),e('rnx mode three configure --setting room --gradient "#0a1822,#0d2233,#06101a"',"physical room setting + a custom brand-color gradient background"),e("rnx mode three configure --focus on --focus-amount strong --focus-range tight","enable a strong Focus with a tight sharp band"),e("rnx mode three configure --script hero-pan --animate --slow","play a camera script slowly"),e("rnx mode three configure --reset-pose","return to the default pose"),e("rnx mode three render ./cinematic.json --output ./frames --fps 30","render deterministic transparent RGBA PNG frames")],related:["screenshot"]},{id:"describe",slug:"describe",navTitle:"describe",title:"rnx describe",description:"curated human-readable UI summary (default inspection tool)",summary:"The default way to see what is on screen. Lists visible UI elements with type, text, position, size, and style: buttons show [button], tappable views show [tap], inputs show [input]. Structural noise (icon wrappers, layout containers, duplicate text inside buttons) is pruned by default; use --all for the unfiltered tree, --verbose for ids/labels/style, and --json to script against it. Reach for describe before screenshots or DOM poking.",order:68,usage:["rnx describe [filter] [--verbose] [--watch] [--all] [--json] [--no-clipped] [--compact] [--no-xy] [--only <glob>] [--subtree <id>] [--testid-like <pat>]"],options:[t("--verbose","include ids, labels, and extra style info"),t("--all","do not prune structural view nodes"),t("--watch","redraw as the UI changes (ctrl-c to stop)"),t("--json","emit structured JSON. rows keep their full text and labels: the terminal dump clips a long string to keep a row on one line, and a machine reader cannot undo that, so a red-box message or a bundle-eval stack comes back whole here"),t("--no-clipped","strip rows annotated (clipped:left|right|top|bottom) so the on-screen state is not buried"),t("--compact","render the tree in a tighter format"),t("--no-xy","hide x,y coordinates from each row"),t("--only <glob>","glob filter across text/role/testID"),t("--subtree <id>","limit the dump to a subtree rooted at the given testID"),t("--testid-like <pattern>","fuzzy-match nodes whose testID contains the pattern")],examples:[e("rnx describe"),e("rnx describe button"),e("rnx describe --verbose"),e("rnx describe --json"),e("rnx describe --watch"),e("rnx describe --subtree feedListRoot","limit to one subtree"),e('rnx describe --only "*Header*"',"glob-match across text/role/testID")],related:["find","get","do"]},{id:"find",slug:"find",navTitle:"find",title:"rnx find",description:"locate nodes by text / testID / role / type / predicate",summary:"The unified finder. A bare argument matches node text (findByText); flag forms match by testID, role, component type, or predicate. --pressable and --visible list every tappable or visible node, and --interactive-targets (alias --actions) ranks visible+pressable nodes and prints a ready-to-run `rnx do tap-...` command per row, so the next tap is one copy away. Use --verbose for full node JSON and --json to script.",order:68.5,usage:["rnx find <text>","rnx find --testid <id>","rnx find --role <role>","rnx find --type <component-type>","rnx find --pressable","rnx find --visible","rnx find --interactive-targets"],options:[t("--text <text>","match node text content (same as bare positional)"),t("--testid <id>","match node testID or id exactly"),t("--role <role>","match accessibilityRole"),t("--type <type>","match React component type"),t("--pressable","list all tappable nodes"),t("--visible","list all visible (non-zero) nodes"),t("--interactive-targets, --actions","ranked tappable nodes, each with a ready-to-run tap command"),t("--verbose, --dump","emit full node JSON for each result"),t("--json","emit structured JSON")],examples:[e('rnx find "Sign in"'),e("rnx find --testid loginButton"),e("rnx find --role button"),e("rnx find --pressable"),e("rnx find --visible --json"),e("rnx find --interactive-targets","ranked visible+pressable nodes with copy-paste tap commands")],related:["describe","get","do"]},{id:"assert",slug:"assert",navTitle:"assert",title:"rnx assert",description:"run any read verb and convert its output into an exit code",summary:"Thin wrapper that spawns a read verb (describe, find, get errors, \u2026) with --json forced on, parses stdout, and evaluates assertion flags over the payload. Exit codes are bisect-friendly: 0 pass, 1 fail, 125 skip (app not loaded / verb crashed), 2 misuse. Flag-level composition (--count / --contains / --jq / --has-path / --within) means the same predicate surface works across every read verb.",order:69,usage:["rnx assert <verb> <verb-args...> <assertion-flags...>"],options:[t("--count <n>","result array has exactly n items"),t("--count-at-least <n>","length >= n"),t("--count-at-most <n>","length <= n"),t("--exists","non-null, non-empty (default when no flag given)"),t("--empty","null or zero length"),t("--contains <str>","stringified result contains str (repeatable)"),t("--not-contains <str>","stringified result does not contain str"),t("--matches <regex>","stringified result matches regex"),t("--not-matches <regex>","stringified result does not match regex"),t("--equals <literal>","scalar equality"),t("--has-path <dotted.path>","nested path exists"),t("--path-equals <path> <value>","nested path equals value"),t("--jq <expr>","jq expression is truthy (requires jq on PATH)"),t("--within <ms>","retry verb + predicates until pass or deadline"),t("--allow-timeout","treat verb auto-wait timeout as fail, not skip"),t("--negate, !","flip pass/fail (does not flip skip)"),t("--quiet","suppress the one-line pass/fail summary")],examples:[e("rnx assert find --testid set-max-input --count 4","exactly four matching nodes"),e("rnx assert describe --contains swap-form-header","substring anywhere in the tree dump"),e("rnx assert get errors --count 0","fail the commit if any console error"),e("rnx assert find --testid submit --exists --within 3000","poll for 3s before failing"),e(`rnx assert describe --jq '.shell.state == "app"'`,"jq predicate over the payload"),e("git bisect run rnx assert find --testid set-max-input --count 4","git-bisect integration")],related:["find","describe","get","list"]},{id:"get",slug:"get",navTitle:"get",title:"rnx get",description:"read runtime state (tree, a11y, layout, url, count, errors, \u2026)",summary:"Grouping verb for pure reads. `rnx get <noun>` covers the read side of the bridge: tree, a11y, layout (including native rendered text contrast with `--styling`), url, count, console errors/warnings/requests, globals, and single-node lookups.",order:70,usage:["rnx get <noun> [args]"],examples:[e("rnx get tree"),e("rnx get a11y 3"),e("rnx get layout --styling"),e("rnx get url"),e("rnx get count"),e("rnx get errors 5"),e("rnx get requests 5"),e("rnx get node loginButton"),e("rnx get layout loginButton"),e("rnx get globals")],related:["do","find","describe"]},{id:"do",slug:"do",navTitle:"do",title:"rnx do",description:"drive the app (tap, type, scroll, swipe, key, reload, \u2026)",summary:"Grouping verb for user-interaction actions. `rnx do <action>` covers the write side of the bridge: taps, typing, key presses, touch/scroll/drag/swipe/pinch gestures, reload, and local timing helpers. Repeat `--then` before each additional batchable action to run one ordered engine batch.",order:71,usage:["rnx do <action> [args]","rnx do <action> [args] [--then <action> [args] ...]"],examples:[e("rnx do tap email --then type user@example.com --then tap submit","run three existing do actions as one batch"),e("rnx do tap 196 727"),e("rnx do double-tap 196 400"),e('rnx do tap-text "Sign in"'),e("rnx do tap-id loginNextButton"),e('rnx do type "natew.bsky.social"'),e('rnx do type-into email "me@example.com"'),e("rnx do key return"),e("rnx do key-sequence shift h i"),e("rnx do dismiss"),e("rnx do scroll feed 0 400"),e("rnx do drag 200 720 200 160"),e("rnx do swipe 200 720 200 160"),e("rnx do gesture swipe-from-bottom-edge"),e("rnx do reload")],options:[t("--then <action>","append an action to the same ordered engine batch"),t("--json","for an inline chain, print every engine-step result"),t("--no-wait","for an inline chain, skip the final auto-settle")],related:["shell","get","find","describe","wait"]},{id:"wait",slug:"wait",navTitle:"wait",title:"rnx wait",description:"block until the runtime reaches a known state",summary:"Grouping verb for explicit synchronization. Use instead of guessing with `sleep`. Every subverb has a bounded timeout and exits non-zero when it times out, so automations fail loudly rather than silently racing animations or missing nodes.",order:72,usage:["rnx wait <kind> [args]"],examples:[e("rnx wait ready","block until the app bundle is loaded and painted"),e("rnx wait idle","block until animations + rAF queue are settled"),e("rnx wait selector loginButton","block until a node with this testID is present"),e("rnx wait selector feedList --max-ms 3000","cap how long to wait for a node")],related:["do","find","describe","get"]},{id:"shell",slug:"shell",navTitle:"shell",title:"rnx shell",description:"drive shell state and simulator chrome",summary:"Top-level shell controls for app launch, home/switcher transitions, and simulator-owned chrome such as appearance and lock state.",order:72,usage:["rnx shell launch <appId> [waitMs] [--clear-state]","rnx shell home [waitMs]","rnx shell switcher [waitMs]","rnx shell open-card <appId> [waitMs]","rnx shell appearance <light|dark|auto|toggle>","rnx shell lock","rnx shell shake"],examples:[e("rnx shell launch photos"),e("rnx shell home"),e("rnx shell switcher 800"),e("rnx shell appearance dark"),e("rnx shell lock")],related:["do","debug"]},{id:"network",slug:"network",navTitle:"network",title:"rnx network",description:"inspect live network traffic from the running rnx worker",summary:"Read the shared observability store (fetch + XHR captured in the render worker) from the terminal. List recent requests, tail in real time, filter by URL or status, inspect one entry in detail, or clear the buffer. Shares its data source with the in-shell devtools dock so both views stay in lock-step.",order:85,usage:["rnx network [limit]","rnx network --failed","rnx network --slow [--threshold <ms>]","rnx network --filter <substring>","rnx network --tail","rnx network get <id>","rnx network clear"],options:[t("--failed","only show errored or non-2xx responses"),t("--slow","only show requests slower than --threshold (default 1000ms), sorted by duration desc"),t("--threshold <ms>","slow threshold in milliseconds for --slow (default 1000)"),t("--filter <substring>","URL substring filter (case-insensitive)"),t("--limit <n>","max entries to print (default 20)"),t("--tail, -f","follow mode: stream new entries as they land"),t("--json","emit structured JSON instead of formatted rows"),t("--sim <sim>","target a specific connected sim"),t("--port <number>","bridge port (defaults to {{bridgePort}})")],examples:[e("rnx network","list the last 20 requests"),e("rnx network 100","list the last 100"),e("rnx network --failed","only errors and 4xx/5xx"),e("rnx network --slow","requests slower than 1000ms, sorted by duration desc"),e("rnx network --slow --threshold 200","tighten the slow threshold to 200ms"),e("rnx network --filter api.example.com"),e("rnx network --tail","live follow mode, ctrl-c to stop"),e("rnx network --tail --failed"),e("rnx network get req-42-l7k9"),e("rnx network clear")],related:["debug","get","logs"]},{id:"logs",slug:"logs",navTitle:"logs",title:"rnx logs",description:"inspect live console output from the running rnx worker",summary:"Read the shared observability log store (console.log/info/warn/error/debug plus uncaught errors and unhandled rejections, captured in the render worker) from the terminal. List recent logs, tail in real time, filter by level or substring, or clear the buffer. Shares its data source with the devtools dock so both views stay in lock-step.",order:86,usage:["rnx logs [limit]","rnx logs --level <csv>","rnx logs --filter <substring>","rnx logs --tail","rnx logs clear"],options:[t("--level <csv>","comma-separated level filter (log,info,warn,error,debug)"),t("--filter <substring>","message substring filter (case-insensitive)"),t("--limit <n>","max entries to print (default 50)"),t("--tail, -f","follow mode: stream new log entries as they land"),t("--json","emit structured JSON instead of formatted rows"),t("--internal, --all","include engine-internal [sootsim] logs (bundle-loader, HMR traps, etc.), hidden by default so guest-app output stays readable"),t("--sim <sim>","target a specific connected sim"),t("--port <number>","bridge port (defaults to {{bridgePort}})")],examples:[e("rnx logs","last 50 log entries"),e("rnx logs 200","last 200"),e("rnx logs --level error,warn"),e('rnx logs --filter "hydrate"'),e("rnx logs --tail","live follow mode, ctrl-c to stop"),e("rnx logs --tail --level error"),e("rnx logs --internal","include engine-internal logs"),e("rnx logs clear")],related:["network","get"]},{id:"debug",slug:"debug",navTitle:"debug",title:"rnx debug",description:"drive __sootsimDebug: channels, snapshots, and inspectors",summary:"Toggle engine debug channels, capture snapshots, and diff internal state from the terminal while targeting any connected sim.",order:90,usage:["rnx debug <subcommand> [args]"],options:[t("--sim <sim>","target a specific connected sim"),t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--json","emit compact JSON for machine consumption"),t("--pretty","emit formatted JSON for human inspection")],examples:[e("rnx debug enable sheets,portals"),e("rnx debug snapshot before"),e("rnx debug diff before after"),e("rnx debug recent portals 20")],related:["open","what-happened"]},{id:"what-happened",slug:"what-happened",navTitle:"what-happened",title:"rnx what-happened",description:"show recent semantic events: toasts, keyboards, screens, alerts, fetches, errors",summary:"Read the unified semantic timeline across host, shell worker, and tenant worker. Defaults to events since the previous CLI call (per-agent cursor), so 'what-happened' reliably shows what's new instead of replaying state you already saw. Catches ephemeral UI like toasts that disappear before describe/find can find them.",order:91,usage:["rnx what-happened","rnx what-happened --summary","rnx what-happened --since 5s --kinds toast,fetch"],options:[t("--summary","one-line counts by kind, no event list"),t("--flow","section the event stream by screen/route boundaries (demo-friendly recap)"),t("--all","full ring (ignore the per-identity cursor)"),t("--since <duration>","absolute window; accepts e.g. 5s, 500ms, 2m"),t("--kinds <csv>","filter to specific kinds: toast, keyboard, screen, alert, fetch, console, ..."),t("--limit <n>","cap returned events (default 200)"),t("--json","emit structured JSON"),t("--no-advance","do not advance the read cursor after rendering"),t("--noisy","include react-commit, layout, and scroll events (suppressed by default so taps/fetches/screen transitions are not buried)"),t("--sim <sim>","target a specific connected sim"),t("--port <number>","bridge port (defaults to {{bridgePort}})")],examples:[e("rnx what-happened","since the previous CLI call"),e("rnx what-happened --summary","5 errors \xB7 2 warnings \xB7 1 toast"),e("rnx what-happened --kinds toast,fetch --since 10s","trace a toast back to its trigger"),e("rnx what-happened --flow --since 5m","section the last 5 minutes by screen/route, a demo-friendly recap"),e("rnx what-happened --all --json","dump full ring as JSON")],related:["timeline","debug","logs","network"]},{id:"timeline",slug:"timeline",navTitle:"timeline",title:"rnx timeline",description:"control the semantic event timeline (enable opt-in kinds, clear, dump)",summary:"Distinct from `rnx record` (video) and `rnx debug record` (engine channels): manages the semantic agent-facing event stream that backs `rnx what-happened`. Cheap kinds record at all times; opt-in kinds (scroll, gesture, text-input, reanimated, animation) need to be enabled.",order:92,usage:["rnx timeline <subcommand> [args]"],options:[t("--sim <sim>","target a specific connected sim"),t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--json","emit compact JSON for machine consumption")],examples:[e("rnx timeline status"),e("rnx timeline start scroll,gesture","enable opt-in kinds"),e("rnx timeline stop all","silence everything"),e("rnx timeline dump /tmp/events.json")],related:["what-happened","debug","record"]},{id:"ios",slug:"ios",navTitle:"ios",title:"rnx ios",description:"start an iOS simulator",summary:"Start a simulator on iOS. The platform picks the device: your current device is kept when it is already an iOS model, otherwise the default iOS device is used. Where the simulator runs comes from context \u2014 a normal terminal or a local box starts a local simulator, and inside a cloud box the same command starts a cloud simulator attached to that box. There is no separate spelling per place.",order:98,usage:["rnx ios [target] [--device <model>]"],options:[t("--device <model>","pick a specific iOS device model. an Android model here is refused by name rather than silently overriding the platform")],examples:[e("rnx ios","start an iOS simulator on your current or default iOS device"),e("rnx ios --device iphone-16","start on a specific iOS device")],related:["android","open","device","box"]},{id:"android",slug:"android",navTitle:"android",title:"rnx android",description:"start an Android simulator",summary:"Start a simulator on Android. The platform picks the device: your current device is kept when it is already an Android model, otherwise the default Android device is used. Where the simulator runs comes from context \u2014 a normal terminal or a local box starts a local simulator, and inside a cloud box the same command starts a cloud simulator attached to that box.",order:99,usage:["rnx android [target] [--device <model>]"],options:[t("--device <model>","pick a specific Android device model. an iOS model here is refused by name rather than silently overriding the platform")],examples:[e("rnx android","start an Android simulator on your current or default Android device"),e("rnx android --device pixel-10","start on a specific Android device")],related:["ios","open","device","box"]},{id:"box",slug:"box",navTitle:"box",title:"rnx box",description:"start a local box and enter its shell",summary:"A box is an environment holding the project filesystem, a shell, rnx tools, a bundler, an app server, and a simulator. Bare `rnx box` is intentionally local: it mounts the current checkout at /project and enters a shell with ls, cat, find, rg, sed, pipes and redirects, plus rnx itself registered as a shell command. Writes go straight to the checkout \u2014 the local box never creates a second copy of the project. It is not SSH: a shell command is served by rnx, not by a generic Linux machine, so native binaries and installed tools are not available. `rnx box create` provisions a durable cloud box and is not built yet.",order:97,usage:["rnx box","rnx box create","rnx box create --name=<name>"],examples:[e("rnx box","start a local box over the current checkout"),e("rnx box create --name=my-sim","create a named cloud box")],related:["ios","android","open","shell"]},{id:"open",slug:"open",navTitle:"open",title:"rnx open",description:"load a target into the current sim or open a new one",summary:"Resolve a port, dev-server URL, bundle URL, full shell URL, or app deep link. The first browser sim uses an isolated Playwright Chrome for Testing profile. Later opens reuse the saved sim and navigate it in place. Custom-scheme URLs and leading-slash routes dispatch through the current app\u2019s React Native Linking pipeline. Named profiles force a fresh Electron or Playwright sim because storage isolation is fixed at window/context construction time. A rnx preview/build link (e.g. https://rnxsim.com/preview/<id>) is a full player page, not a driveable sim; `open` recognizes it and launches it in your browser for viewing instead of mis-loading the HTML as a bundle.",order:100,usage:["rnx open <port|target|bundle-url|rnx-url|deeplink> [--new]"],options:[t("--new","open a genuinely concurrent sim instead of reusing the current one. each new sim creates another isolated browser tree"),t("--profile <id>","open with an isolated persistent storage profile"),t("--ephemeral","open with a temporary isolated storage profile. combined with `--new --driver playwright` this is the guaranteed-clean boot: a throwaway browser profile with no service worker registration and no preview-share IndexedDB, so nothing a previous sim left behind can steer this one"),t("--driver <id>","launch a fresh sim through playwright (default browser surface) or electron (native desktop surface)"),t("--headless","pass headless=true to the launch driver (useful with `--driver playwright` for CI runs)"),t("--cdp-port <number>","expose Chrome remote debugging on this port so the sim can be cpu-profiled while still driveable over the bridge (playwright driver only)"),t("--device <model>","device profile for the opened sim (e.g. iphone-16, iphone-16-pro-max, pixel-8). stamped on the engine URL so every open path honors it; playwright sims also size their viewport to the device instead of a desktop-shaped default"),t("--viewport <WxH>","explicit page size for a driver-launched sim (e.g. 1920x1080 to frame the 3d stage in landscape for film capture). takes precedence over the device-derived window size (playwright driver only)"),t("--base-url <url>","base shell URL to wrap around bundle targets"),t("--replace <module>=<file>","replace a Metro module with a local file for this run; useful for swapping in a deterministic fixture component during demos. repeatable"),t("--remap <host:port>=<host:port>","rewrite guest-app fetch/websocket traffic from one host:port to another for this run (e.g. a third-party e2e suite that hardcodes a port already taken on the host). repeatable"),t("--no-describe","skip the automatic `describe` snapshot that prints right after open finishes (useful when scripting and you only want open's own output)"),t("--hot","force metro HMR on (sets `hot=true` on the bundle URL). default for interactive opens; edits hot-apply through rnx's HmrClient"),t("--no-hmr","force metro HMR off (sets `hot=false` on the bundle URL). default under `--driver playwright` so agent/CI runs stay deterministic with no in-bundle refresh wiring"),t("--port <number>","bridge port (defaults to {{bridgePort}})")],examples:[e("rnx open 8081"),e("rnx open --new 8085"),e("rnx open 8081 --profile qa"),e("rnx open 8081 --profile qa --driver playwright"),e("rnx open http://localhost:8082"),e("rnx open /settings","dispatch a route deep link to the current app"),e("rnx open myapp://settings/profile"),e("rnx open 8081 --driver playwright --headless","headless CI / scripted runs"),e("rnx open 8081 --new --driver playwright --device iphone-16","pinned device profile (comparisons need the same device)"),e("rnx open 8089 --new --driver playwright --cdp-port 9222","open a sim that is both driveable and cpu-profilable (then `rnx perf cpu --cdp-port 9222`)"),e("rnx open 8081 --new --ephemeral --driver playwright","boot against a guaranteed-clean browser profile: no service worker, no preview-share IndexedDB, no carried-over storage. the first thing to reach for when a sim is wedged or you cannot tell whether a failure belongs to the app or to leftover state"),e("rnx open 8081 --no-describe","silent open for scripts"),e("rnx open 8081 --no-hmr","open with metro HMR off (e.g. when reproducing a non-HMR boot path)"),e("rnx open 8081 --driver playwright --hot","driver playwright but keep HMR on (e.g. recording an edit-driven demo)"),e("rnx open https://rnxsim.com/preview/<id>","opens a preview/build link in the browser for viewing (not a driveable sim)")],related:["list","use","close","desktop"]},{id:"list",slug:"list",navTitle:"list",title:"rnx list",description:"list connected sims or available launch drivers",summary:"Show every connected sim, or (with `--drivers`) enumerate the Electron and Playwright launch drivers along with their availability on the current machine. By default hides idle host shells (open tabs with no app bundle loaded) so the output stays focused on the demos that actually have an app running; pass `--all` to see everything.",order:110,usage:["rnx list [--all] [--bundle <substr>] [--app-port <port>] [--primary] [--sim <sim>] [--json]","rnx list --drivers"],options:[t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--sim <sim>","highlight a specific sim in the output"),t("--all","show every connected sim, including idle host shells with no app loaded"),t("--bundle <substring>","only sims whose loaded url contains this substring"),t("--app-port <port>","shorthand for `--bundle '/rn/<port>'`: filter to sims for the app on a specific metro/vite port. NOT `--port` (that's reserved for the bridge port)"),t("--primary","only the primary sim"),t("--drivers, -D","list available launch drivers instead of connected sims"),t("--json","emit array of sim objects for scripts")],examples:[e("rnx list"),e("rnx list --all","include idle host shells"),e("rnx list --app-port 8082","only sims running the app on port 8082"),e("rnx list --bundle bluesky","sims whose bundle url mentions bluesky"),e("rnx list --primary"),e("rnx list --sim a6"),e("rnx list --drivers","show every installable launch driver"),e('rnx list --json | jq ".[] | select(.active)"')],related:["open","use","close"]},{id:"use",slug:"use",navTitle:"use",title:"rnx use",description:"select a connected sim",summary:"Make one connected sim current by focusing it and saving it as the default target for later bridge-backed commands.",order:120,usage:["rnx use [sim]"],options:[t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--sim <sim>","target a specific connected sim")],examples:[e("rnx use"),e("rnx use a6")],related:["list","open","close"]},{id:"claim",slug:"claim",navTitle:"claim",title:"rnx claim",description:"take an exclusive lease on a sim",summary:"Reserve a connected sim so other agents cannot send commands to it. Leases auto-expire after 60s of inactivity and refresh on every command from the owner. Prefer `rnx open --new` over `claim --force` when another agent is active, since the latter steals the lease mid-flight and breaks the other agent's in-progress flow.",order:125,usage:["rnx claim [sim] [--force]"],options:[t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--sim <sim>","target a specific connected sim"),t("--force","steal the lease from whoever currently holds it. only safe when you know the other holder is dead or abandoned; prefer `rnx open --new` to get a fresh sim instead")],examples:[e("rnx claim"),e("rnx claim a6"),e("rnx claim a6 --force","last-resort lease steal; see `rnx open --new` for the safer alternative")],related:["list","use","open","close"]},{id:"close",slug:"close",navTitle:"close",title:"rnx close",description:"close a connected sim",summary:"Request that a sim close itself over the bridge and wait for it to disappear from the sim registry.",order:130,usage:["rnx close [sim]","rnx close --all","rnx close --others"],options:[t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--sim <sim>","target a specific connected sim"),t("--all","close every connected sim"),t("--others","close every connected sim except the current/target one")],examples:[e("rnx close"),e("rnx close a6"),e("rnx close --all"),e("rnx close --others")],related:["list","use"]},{id:"setup",slug:"setup",navTitle:"setup",title:"rnx setup",description:"configure optional local conveniences",summary:"rnx works without project setup. This guided flow explains and offers the strongly recommended background service, which keeps the bridge and runtime ready so agent inspection, interaction, and test commands start much faster. It also chooses Browser or the packaged Desktop app, keeps diagnostics private unless explicitly enabled, and can optionally add an exact rnxsim dependency plus an rnx script when the current directory is inside a Git repository. Declining the service is reversible with `rnx daemon install`.",order:140,usage:["rnx setup [options]"],options:[t("--app <dir>","project directory to inspect (default: current directory)"),t("-y, --yes","accept recommended machine defaults without prompting"),t("--dry-run","show what setup would offer without changing files"),t("--service | --no-service","enable or decline the background service"),t("--desktop | --browser","choose the simulator window"),t("--analytics on|off","configure bounded diagnostics"),t("--repo | --no-repo","add or skip the optional repository install")],examples:[e("rnx setup"),e("rnx setup --app ./apps/mobile"),e("rnx setup --dry-run"),e("rnx setup --yes")],related:["open","daemon","serve","compat","skill"]},{id:"compat",slug:"compat",navTitle:"compat",title:"rnx compat",description:"check package compatibility",summary:"Scan a project or specific dependency against the compat registry and summarize where native packages are fully supported, partially shimmed, or still risky.",order:150,usage:["rnx compat [package-name]"],options:[t("--json","emit machine-readable JSON"),t("--brief","print a compact one-line summary"),t("--app <dir>","scan a target app directory instead of cwd")],examples:[e("rnx compat"),e("rnx compat react-native-reanimated"),e("rnx compat --brief")],related:["setup","report-issue"]},{id:"report-issue",slug:"report-issue",navTitle:"report-issue",title:"rnx report-issue",description:"send an explicitly approved compatibility report",summary:"Preview and submit a missing or broken rnx compatibility report. The command attaches the local compatibility scan plus CLI, runtime, operating-system, and architecture details. It never attaches source, environment variables, terminal output, logs, screenshots, or app data, and it requires confirmation before sending.",order:155,usage:['rnx report-issue [options] "<description>"'],options:[t("--app <dir>","scan a target app directory instead of cwd"),t("--dry-run","preview the attachment summary without sending"),t("-y, --yes","submit after the user has explicitly approved the preview; required in non-interactive sessions")],examples:[e('rnx report-issue "react-native-example renders its sheet behind the tab bar"',"preview the report and ask for interactive confirmation"),e('rnx report-issue --dry-run "the compat scan does not recognize react-native-example"',"inspect what would be attached without sending"),e('rnx report-issue --yes "react-native-example crashes when opening its picker"',"submit only after the user explicitly asks you to send it")],related:["compat","setup"]},{id:"desktop",slug:"desktop",navTitle:"desktop",title:"rnx desktop",description:"launch or install the desktop companion",summary:"Launch the optional desktop companion from the global CLI, or install it when missing. Profiles map to Electron partitions, so cookies and app storage stay isolated per profile.",order:160,usage:["rnx desktop [--port <number>] [--profile <id>]","rnx desktop install [options]"],options:[t("--port <number>","open a local app server through the rnx shell, for example Metro on 8081"),t("--profile <id>","launch with an isolated persistent storage profile"),t("--ephemeral","launch with a temporary isolated storage profile"),t("-y, --yes","for install: skip confirmation and install immediately"),t("--force","for install: reinstall even if the companion is present")],examples:[e("rnx desktop"),e("rnx desktop --port 8081"),e("rnx desktop --profile qa"),e("rnx desktop install --yes")],related:["open","list","debug"]},{id:"daemon",slug:"daemon",navTitle:"daemon",title:"rnx daemon",description:"manage the recommended background bridge service",summary:"Install or manage the persistent background bridge. It registers a launchd agent on macOS or systemd user service on Linux and keeps the bridge and runtime ready between commands. This is strongly recommended for local agent work because inspect, interaction, and test commands start much faster. It is optional and not needed in CI. If you declined during onboarding, enable it with `rnx daemon install`. `rnx daemon uninstall` stops and removes the agent and deletes the ~/.sootsim cache (runtimes, profiles, lockfile).",order:173,usage:["rnx daemon install [--force]","rnx daemon uninstall","rnx daemon status","rnx daemon restart","rnx daemon start","rnx daemon stop","rnx daemon logs [-n <lines>] [-f]"],options:[t("--force","replace an existing daemon registration"),t("-n, --lines <n>","number of log lines to show (default 200)"),t("-f, --follow","stream new log lines as they arrive")],examples:[e("rnx daemon install","install and start the background bridge"),e("rnx daemon status","is the bridge daemon registered + alive?"),e("rnx daemon restart","kick the daemon (after a CLI upgrade, etc.)"),e("rnx daemon logs -n 100","tail the last 100 daemon log lines"),e("rnx daemon uninstall","clean teardown: remove the launchd/systemd agent + ~/.sootsim cache")],related:["setup","serve","open"]},{id:"serve",slug:"serve",navTitle:"serve",title:"rnx serve",description:"run the rnx bridge in the foreground",summary:"Host the WS bridge in the foreground. This is mainly for development or diagnosis. `rnx open` starts a local bridge when needed; `rnx daemon install` enables the faster persistent service for local work.",order:175,usage:["rnx serve [options]"],options:[t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("-q, --quiet","suppress per-connection logging")],examples:[e("rnx serve"),e("rnx serve --quiet"),e("rnx serve --port 7668")],related:["setup","daemon","open"]},{id:"runtime",slug:"runtime",navTitle:"runtime",title:"rnx runtime",description:"manage engine runtimes under ~/.sootsim/runtimes/",summary:"Install, list, switch, and remove versioned SootSim engine runtimes. The CLI downloads a tarball from the configured CDN, verifies its sha256, and extracts it. The daemon serves the default runtime and updates it hourly in the background. To keep one app on a fixed engine, set `runtimeVersion` in that repo's optional `sootsim.config.ts`; this does not change other apps. `rnx runtime notes` prints release notes for the repo-selected engine, or the machine default when the repo has no selection.",order:176,usage:["rnx runtime install [version]","rnx runtime list","rnx runtime use <version>","rnx runtime remove <version>","rnx runtime which","rnx runtime notes"],options:[t("--channel <name>","channel for 'latest' (default: stable)"),t("--force","reinstall even if already on disk"),t("--no-set-active","don't switch active runtime after install (also accepts --set-active=false)")],examples:[e("rnx runtime install"),e("rnx runtime install 1.2.3"),e("rnx runtime install --channel beta","install the latest from a non-stable channel"),e("rnx runtime use 1.2.3"),e("rnx runtime list","show installed + available versions"),e("rnx runtime which","print the currently active runtime version"),e("rnx runtime notes","print release notes for this repo runtime"),e("rnx runtime remove 1.2.2")],related:["setup","serve","daemon","version"]},{id:"upgrade",slug:"upgrade",navTitle:"upgrade",title:"rnx upgrade",description:"update the rnx CLI and engine runtime to the latest",summary:"Update both independently versioned parts of rnx. The command installs the latest engine runtime, or the repo's configured `runtimeVersion`, then checks npm and updates the CLI only when a newer CLI exists. It prints the previous and installed runtime versions, the CLI version result, and the engine release notes in the terminal. The background daemon also checks for a new default runtime on startup and hourly.",order:174,usage:["rnx upgrade [--channel <name>]"],options:[t("--channel <name>","runtime channel for 'latest' (default: stable)")],examples:[e("rnx upgrade"),e("rnx upgrade --channel beta","install the latest engine runtime from the beta channel")],related:["runtime","setup","version"]},{id:"version",slug:"version",navTitle:"version",title:"rnx version",description:"show the CLI + engine runtime versions you are running",summary:"Show both independently versioned parts: the `rnx` CLI package and the engine runtime rendered by the browser or desktop shell. The command also shows a repo-configured `runtimeVersion`, whether it is installed, the active channel, a newer-runtime hint, and the official changelog URL. The terse `rnx --version` flag keeps the two-line script-friendly output.",order:173,usage:["rnx version"],options:[],examples:[e("rnx version"),e("rnx --version","terse, script-parseable")],related:["upgrade","runtime"]},{id:"maestro",slug:"maestro",navTitle:"maestro",title:"rnx maestro",description:"author, generate, and run Maestro YAML flows against rnx",summary:"The single YAML test surface. Run existing `.maestro/*.yaml` suites unchanged, generate a flow from a plain-language goal, or author one from live CLI actions. Playback, recording, profiling, and hosted results all use the same runner.",order:40,usage:["rnx maestro [test] <flow-or-dir> [options]","rnx maestro # auto-discovers ./.maestro/",'rnx maestro generate "<goal>"',"rnx maestro start|keep|end","rnx maestro validate <flow>","rnx maestro init # scaffold a starter flow","rnx maestro --list-compat"],options:[t("--env KEY=VALUE","set env vars for ${KEY} interpolation (repeatable)"),t("--continuous","re-run the flow on file changes (alias for --watch)"),t("--format <fmt>","accepted for maestro cli compat (not used)"),t("--profile","capture performance stats while the flow runs"),t("--out <dir>","write recordings and screenshots to this directory"),t("--url <url>","load this target before running the flow"),t("--output <path>","write a live-authored flow on `maestro end`"),t("--validate","replay a live-authored flow before clearing its draft"),t("--video","record while validating a live-authored flow"),t("--new","force a fresh sim for this run"),t("--headed","force a fresh, VISIBLE window (implies --new, overrides --headless) to watch the flow run live"),t("--record","record a webm while the flow runs"),t("--preview","record events+video, upload, and print /preview/<id>"),t("--preview-open","open the uploaded preview link after a successful run"),t("--preview-origin <url>","upload target for --preview"),t("--preview-public-origin <url>","public link origin for --preview"),t("--slow <ms>","delay between steps for natural pacing"),t("--list-compat","print supported and unsupported Maestro verbs")],examples:[e("rnx maestro test .maestro/login.yaml"),e('rnx maestro generate "log in and verify the welcome screen"'),e("rnx maestro start"),e("rnx maestro end --output .maestro/login.yaml --validate"),e("rnx maestro --env USERNAME=alice test .maestro/login.yaml"),e("rnx maestro test .maestro/flow.yaml --record"),e("rnx maestro --list-compat")],related:["detox","record"]},{id:"detox",slug:"detox",navTitle:"detox",title:"rnx detox",description:"run existing Detox suites against rnx on headless Chromium",summary:"Drop-in Detox runner. Existing `import from 'detox'` suites run unchanged via a Jest `moduleNameMapper` rewrite: no code edits, no config churn. Auto-launches an rnx shell if none is running.",order:42,usage:["rnx detox [options] [-- <jest args>]"],options:[t("--config <path>","use a specific jest config instead of the default"),t("--watch","jest watch mode"),t("--headed","keep the rnx shell window visible"),t("-t <pattern>","pass a Jest --testNamePattern"),t("--no-launch","skip auto-launching an rnx shell (manage it yourself)")],examples:[e("rnx detox"),e("rnx detox --watch"),e('rnx detox -t "login flow"'),e("rnx detox --headed","keep the shell visible while iterating on a failing suite")],related:["maestro"]},{id:"device",slug:"device",navTitle:"device",title:"rnx device",description:"inspect or change the live device preset for a sim",summary:"Read the current device preset, switch it on the fly, or list the available models. Switching a preset reshapes the running sim (screen size, density, and chrome) without restarting the bundle.",order:78,usage:["rnx device","rnx device get","rnx device set <model> [--sim <id>]","rnx device list"],options:[t("--sim <sim>","target a specific connected sim when setting"),t("--port <number>","bridge port (defaults to {{bridgePort}})")],examples:[e("rnx device","show the active device preset"),e("rnx device list","list every available model id"),e("rnx device set iphone-14"),e("rnx device set iphone-16-pro-max --sim a7")],related:["mode","screenshot","open","list"]},{id:"skill",slug:"skill",navTitle:"skill",title:"rnx skill",description:"install and inspect bundled rnx agent skills",summary:"Install bundled rnx agent skills into agent-compatible skill directories, or print the bundled skill markdown. `install` defaults to all supported local agent homes; pass `codex` or `claude` to target one.",order:122,usage:["rnx skill install [all|codex|claude] [skill-name...]","rnx skill install --target <dir> [skill-name...]","rnx skill show [skill-name]"],options:[t("install","install skills; no target means all supported local agents"),t("show","list bundled skills, or print one skill markdown"),t("--target <dir>","install into an explicit skill directory")],examples:[e("rnx skill install"),e("rnx skill install codex"),e("rnx skill install codex rnx-debug"),e("rnx skill show"),e("rnx skill show rnx-debug")],related:["debug"]},{id:"agent",slug:"agent",navTitle:"agent",title:"rnx agent",description:"attached projects + local agent sessions (codex/claude)",summary:"Manage attached project directories and the agent sessions running inside them. Session-lifecycle commands (start, prompt, watch, end) route through the bridge daemon, which owns the single FIFO reader per session so concurrent cli watches + electron UI subscribers no longer steal bytes from each other. Disk-only inspection commands (projects, sessions, paths, transcript) read attached-project state directly, no daemon needed.",order:125,usage:["rnx agent attach <dir> [--name <label>] [--provider codex|claude]","rnx agent projects","rnx agent project show <id>","rnx agent project remove <id>","rnx agent sessions [--project <id>] [--all]","rnx agent start <projectId> [--provider codex|claude]",'rnx agent prompt <sessionId> "<text>"',"rnx agent watch <sessionId>","rnx agent transcript <sessionId>","rnx agent end <sessionId>","rnx agent paths"],options:[t("--name <label>","human-readable label when attaching a project"),t("--provider <id>","agent backend: codex (default) or claude; used by attach + start"),t("--project <id>","filter sessions to a specific attached project"),t("--all","include ended sessions (sessions defaults to active only)")],examples:[e("rnx agent attach ~/projects/three-punch --name 3pc"),e("rnx agent projects","list every attached project"),e("rnx agent project show 3pc"),e("rnx agent start 3pc --provider claude","spawn a wrapper session (detached) inside the attached project"),e('rnx agent prompt <sessionId> "fix the failing scroll test"',"enqueue a prompt; the daemon delivers it to the wrapper"),e("rnx agent watch <sessionId>","tail events.out pretty-printed"),e("rnx agent sessions --all","include ended sessions in the list"),e("rnx agent transcript <sessionId>"),e("rnx agent end <sessionId>"),e("rnx agent paths","print userData + state file locations")],related:["skill","auth","login"]},{id:"auth",slug:"auth",navTitle:"auth",title:"rnx auth",description:"inspect active CLI auth",summary:"Print the auth source the CLI will use. API keys are not managed by the CLI; pass them via RNX_API_KEY when CI or a headless script needs one. Use `rnx login` and `rnx logout` for the browser-backed desktop session.",order:190,usage:["rnx auth"],options:[t("RNX_API_KEY","env var for API-key auth; no local key file is read")],examples:[e("rnx auth","show the active auth source")],related:["login","logout","record","maestro"]},{id:"login",slug:"login",navTitle:"login",title:"rnx login",description:"sign in for preview uploads",summary:"Sign in with the browser-backed desktop session so uploads and App Store screenshot capture can attach to your account. Local development origins use the dev-login fast path; CI should pass RNX_API_KEY instead.",order:191,usage:["rnx login [--origin <url>]"],options:[t("--origin <url>","auth host (default: auto; prefers contrast.localhost:3000 when available)")],examples:[e("rnx login"),e("rnx login --origin https://contrast.localhost:3000")],related:["auth","logout","record","maestro"]},{id:"logout",slug:"logout",navTitle:"logout",title:"rnx logout",description:"clear the local desktop auth session",summary:"Clear the browser-backed desktop session written by `rnx login`. RNX_API_KEY and GitHub runner auth are environment-provided and are not affected.",order:192,usage:["rnx logout"],examples:[e("rnx logout")],related:["auth","login"]},{id:"config",slug:"config",navTitle:"config",title:"rnx config",description:"manage local CLI preferences",summary:"Manage local preferences that affect the CLI and desktop companion. Telemetry is persisted in ~/.sootsim/config.json. CLI hints are intentionally not a command surface; set SOOTSIM_HINTS=off to suppress them.",order:195,usage:["rnx config telemetry [on|off|status]"],examples:[e("rnx config telemetry","show the current opt-out state and config path"),e("rnx config telemetry off","disable CLI + desktop analytics on this machine"),e("SOOTSIM_HINTS=off rnx describe","suppress hints for a single run")],related:["auth"]}],F=new Map(D.map(r=>[r.id,r])),A=new Map;for(let r of D){A.set(r.id,r);for(let n of r.aliases||[])A.set(n,r)}function j(r){return A.get(r)}function s(r,n,a,l,i,o){return{verb:r,description:n,usage:a,examples:l,notes:i,...o}}var M=[s("describe","show visible UI elements with type, text, position, size, and style. filters structural noise by default; use --all to include everything.",["rnx describe [filter] [--verbose] [--watch] [--all] [--json] [--no-clipped] [--compact] [--no-xy] [--only <glob>] [--subtree <id>] [--testid-like <pat>]"],[e("rnx describe"),e("rnx describe button"),e("rnx describe --verbose"),e("rnx describe --all"),e("rnx describe --json"),e("rnx describe --watch"),e("rnx describe --no-clipped","hide rows annotated (clipped:left|right|top|bottom); useful on screens dominated by edge-pulled chrome or off-screen list items"),e("rnx describe --compact --no-xy","tighter tree without coords"),e("rnx describe --subtree feedListRoot","limit the dump to a subtree rooted at testID feedListRoot"),e('rnx describe --only "*Header*"',"glob-match across text/role/testID"),e("rnx describe --testid-like login",'fuzzy-match nodes whose testID contains "login"')],"buttons show [button], tappable views show [tap], inputs show [input]. structural noise (icon wrappers, layout containers, duplicate text inside buttons) is pruned by default. use --all for the unfiltered view.",{shortDescription:"curated human-readable UI summary (default inspection tool)",options:[t("--verbose","include ids, labels, and extra style info"),t("--all","do not prune structural view nodes"),t("--watch","redraw as the UI changes (ctrl-c to stop)"),t("--json","emit structured JSON"),t("--no-clipped","strip rows annotated (clipped:left|right|top|bottom); keeps the on-screen state from being buried"),t("--compact","render the tree in a tighter format"),t("--no-xy","hide x,y coordinates from each row"),t("--only <glob>","glob filter across text/role/testID; preferred when the bare positional would be ambiguous"),t("--subtree <id>","limit the dump to a subtree rooted at the given testID"),t("--testid-like <pattern>","fuzzy-match nodes whose testID contains the pattern (substring + case-insensitive)")],seeAlso:["tree","a11y","find"]}),s("find","unified finder (text, testid, role, type, pressable, visible, interactive-targets)",["rnx find <text>","rnx find --text <text>","rnx find --testid <id>","rnx find --role <role>","rnx find --type <component-type>","rnx find --pressable","rnx find --visible","rnx find --interactive-targets"],[e('rnx find "Sign in"'),e("rnx find --testid loginButton"),e("rnx find --role button"),e("rnx find --pressable"),e("rnx find --visible --json"),e("rnx find --testid loginButton --verbose"),e("rnx find --interactive-targets","ranked list of visible+pressable nodes with copy-paste tap commands"),e("rnx find --actions","alias for --interactive-targets")],"bare text matches findByText. flag-based forms replace the old `find-id` and `query <predicate>` subcommands. use --verbose to dump the full node JSON for each result instead of the one-liner summary. `--interactive-targets` (alias `--actions`) ranks visible+pressable nodes and emits a `rnx do tap-...` command per row so the agent's next tap is one copy away.",{shortDescription:"locate nodes by text / testID / role / type / predicate",options:[t("--text <text>","match node text content (same as bare positional)"),t("--testid <id>","match node testID or id exactly"),t("--role <role>","match accessibilityRole"),t("--type <type>","match React component type"),t("--pressable","list all tappable nodes"),t("--visible","list all visible (non-zero) nodes"),t("--interactive-targets, --actions",'ranked tappable nodes with per-row tap command (best demo "next tap" picker)'),t("--verbose, --dump","emit full node JSON for each result, not a one-liner"),t("--json","emit structured JSON")],seeAlso:["describe","node","selector"]}),s("count","show total node count",["rnx get count [--json]"],[e("rnx get count"),e("rnx get count --json")],void 0,{group:"get",subgroup:"tree",shortDescription:"total node count",options:[t("--json","emit { nodes: n } for scripts")]}),s("layout","measure layout: bare for every visible element on the screen (box + radius/padding/fontSize), or with an id for one node",["rnx get layout [id] [--json]"],[e("rnx get layout","every visible element: on-screen box (x/y/w/h) + borderRadius, per-side padding, fontSize"),e("rnx get layout --json","machine-readable { count, elements }"),e("rnx get layout loginButton","single node {x, y, width, height} by id/testID")],"bare `get layout` is the screen-wide measurement designers reach for to check spacing/sizing/radius/type-scale parity: more detailed than describe, which gives the element tree but not these numbers. it runs the same shared extractor as the agent app_get_layout tool, so CLI and agent output match. pass an id for the single-node layout lookup.",{group:"get",subgroup:"tree",shortDescription:"screen-wide box + radius/padding/fontSize (or one node by id)",options:[t("--json","emit { count, elements } for scripts")],seeAlso:["describe","find","node"]}),s("tree","dump the node tree",["rnx get tree [depth] [--json] [--boxes]"],[e("rnx get tree"),e("rnx get tree 3"),e("rnx get tree --json"),e("rnx get tree --boxes --json","structured rects for the whole tree")],"raw, depth-limited tree dump. good for machine consumption and debugging when describe has filtered something out. for day-to-day inspection prefer describe. --boxes swaps the text dump for a structured nested tree with absolute device-local rects and colors ({ type, testID?, text?, x, y, w, h, bg?, fg?, br?, children? }) \u2014 the geometry source for layout tooling; its default depth is 50.",{group:"get",subgroup:"tree",shortDescription:"raw node tree dump (default depth 5)",options:[t("--json","emit { depth, tree } for scripts"),t("--boxes","structured nested rects instead of the text dump")],seeAlso:["describe","a11y","node"]}),s("a11y","dump the accessibility tree with roles and labels",["rnx get a11y [depth]"],[e("rnx get a11y")],"only accessibility-relevant fields (role, label, hint). for native rendered text contrast use get layout --styling; for general inspection use describe; for raw tree use get tree.",{group:"get",subgroup:"tree",shortDescription:"accessibility tree (roles, labels, hints)",seeAlso:["describe","tree","layout"]}),s("node","resolve testID/id/text to a full node JSON",["rnx get node <matcher>"],[e("rnx get node loginButton"),e('rnx get node "Sign in"')],void 0,{group:"get",subgroup:"tree",shortDescription:"resolve testID \u2192 id \u2192 text, full node JSON",seeAlso:["find","layout"]}),s("tap","tap a testID, absolute coordinates, or a node resolved from --testid / --text",["rnx do tap <testid>","rnx do tap <x> <y>","rnx do tap --testid <id>","rnx do tap --text <text>"],[e("rnx do tap loginButton"),e("rnx do tap 196 400"),e("rnx do tap --testid loginButton"),e('rnx do tap --text "Sign in"')],"a bare non-numeric argument is a testID, so `do tap loginButton` and `do tap --testid loginButton` are the same command (double-tap and long-press take it too). uses the internal interaction bridge (fireTap), not real PointerEvent dispatch. for gesture-handler debugging, use drag/swipe which dispatch real pointer events.",{group:"do",subgroup:"targeting",shortDescription:"tap a testID, absolute coords, or --testid / --text",seeAlso:["tap-id","tap-text","double-tap","long-press"],order:10}),s("double-tap","tap the same spot twice with a short gap",["rnx do double-tap <testid> [gapMs]","rnx do double-tap <x> <y> [gapMs]","rnx do double-tap --testid <id> [gapMs]"],[e("rnx do double-tap myButton"),e("rnx do double-tap 196 400"),e("rnx do double-tap --testid myButton 80")],"uses the shared interact bridge so text selection and other double-tap gestures run through the same path as live taps.",{group:"do",subgroup:"targeting",shortDescription:"tap the same coordinates twice quickly",seeAlso:["tap"],order:20}),s("tap-text","find a node by text content and tap its center",["rnx do tap-text <text> [--nth <n>] [--within <testID>]"," [--min-y <px>] [--max-y <px>] [--min-x <px>] [--max-x <px>]"," [--near <x> <y>] [--exact] [--role <role>] [--first]"],[e('rnx do tap-text "Sign in"'),e("rnx do tap-text Swap --within sheet-container"),e("rnx do tap-text Swap --min-y 700"),e("rnx do tap-text Cancel --nth 2"),e("rnx do tap-text OK --near 283 784")],"errors with a ranked candidate list (testID + abs position + ancestor chain) when more than one visible match remains after filters, instead of silently picking one. pass --first to keep the old behavior.",{group:"do",subgroup:"targeting",shortDescription:"findByText then tap (rich disambiguation flags)",options:[t("--nth <n>","pick the nth visible match (supports -1)"),t("--within <testID>","descendants of a testID ancestor only"),t("--min-y <px>","absolute y-position floor"),t("--max-y <px>","absolute y-position ceiling"),t("--min-x <px>","absolute x-position floor"),t("--max-x <px>","absolute x-position ceiling"),t("--near <x> <y>","pick the closest match to a coordinate"),t("--exact","exact text match (default: substring)"),t("--role <role>","narrow to accessibilityRole"),t("--first","keep legacy pick-first-silently behavior")],seeAlso:["tap","tap-id","find"],order:30}),s("tap-id","find a node by testID and tap its center",["rnx do tap-id <id>"],[e("rnx do tap-id loginButton")],void 0,{group:"do",subgroup:"targeting",shortDescription:"findByTestId then tap center",seeAlso:["tap","tap-text","tap-best"],order:40}),s("tap-best","tap by query: try testID first, then visible text. one command, two strategies",["rnx do tap-best <query>"],[e('rnx do tap-best "Create expense"',`agent doesn't need to know whether "Create expense" is a testID or text`),e("rnx do tap-best floating-action-button")],"designed for agent ergonomics: when you have a label and don't know whether it's the testID or the visible text, tap-best tries findByTestId first (strongest signal), then findByText. prints which strategy matched. for ambiguity (multiple text matches) use tap-text with --nth / --within instead; tap-best fails fast and points you there.",{group:"do",subgroup:"targeting",shortDescription:"testID-first, text-fallback robust tap",seeAlso:["tap-id","tap-text","find"],order:41}),s("scan","inject a synthetic barcode scan into the active expo-camera scan session",["rnx do scan <text> [--format <web-format>]"],[e('rnx do scan "https://example.com/pair/abc123"'),e('rnx do scan "myapp://connect?token=abc" --format qr_code',"drive a connect-by-QR flow")],"fails when no guest CameraView is actively scanning. the event flows through the same host \u2192 worker \u2192 native seam path as a real BarcodeDetector hit.",{group:"do",subgroup:"native device",shortDescription:"inject a barcode scan into active camera scanning",options:[t("--format <f>","BarcodeDetector format string (default: qr_code)")],seeAlso:["tap","wait"],order:10}),s("type","type text through the iOS keyboard (requires a focused input)",["rnx do type <text>"],[e('rnx do type "hello world"')],"a text input must be focused first via tap-id or tap-text. if no input is focused, this silently does nothing.",{group:"do",subgroup:"text input",shortDescription:"type through the iOS visual keyboard",seeAlso:["type-into","key","dismiss"],order:10}),s("type-into","find an input by testID, tap to focus, then type text",["rnx do type-into <id> <text>"],[e('rnx do type-into kav-input "hello world"')],"combines tap-id + type in one command. warns if the target is not a text input or if the keyboard did not open.",{group:"do",subgroup:"text input",shortDescription:"focus input by testID then type",seeAlso:["type","tap-id"],order:20}),s("key","press a special keyboard key",["rnx do key <name>"],[e("rnx do key return"),e("rnx do key delete")],"valid keys: return, delete, space, shift, 123, ABC, #+=, num-back",{group:"do",subgroup:"text input",shortDescription:"special key (return, delete, space, shift, ...)",seeAlso:["key-sequence","keycode"],order:30}),s("key-sequence","press multiple visual keyboard keys in order",["rnx do key-sequence <key> [<key> ...]"],[e("rnx do key-sequence shift h i")],void 0,{group:"do",subgroup:"text input",shortDescription:"press multiple visual keyboard keys",seeAlso:["key","keycode"],order:40}),s("keycode","press DOM-style key codes through the visual keyboard",["rnx do keycode <code> [<code> ...]"],[e("rnx do keycode ShiftLeft KeyH KeyI")],void 0,{group:"do",subgroup:"text input",shortDescription:"press DOM-style key codes via visual keyboard",seeAlso:["key","key-sequence"],order:50}),s("dismiss","dismiss the keyboard",["rnx do dismiss"],void 0,void 0,{group:"do",subgroup:"text input",shortDescription:"dismiss the iOS keyboard",order:60}),s("scroll","scroll a scrollview by testID or inspect node id",["rnx do scroll <id> <x> <y>","rnx do scroll --testid <id> <x> <y>","rnx do scroll --node-id <nodeId> <x> <y>"],[e("rnx do scroll feed-list 0 500"),e("rnx do scroll --testid feed-list 0 500"),e("rnx do scroll --node-id 3027 0 1200")],void 0,{group:"do",subgroup:"gestures",shortDescription:"scroll a ScrollView by testID or inspect node id",seeAlso:["gesture","swipe"],order:10}),s("drag","drag from one point to another using real PointerEvent dispatch",["rnx do drag <x1> <y1> <x2> <y2> [steps] [stepMs]"],[e("rnx do drag 200 400 200 200 20 16")],"dispatches real PointerEvents to the canvas, unlike tap which uses the internal bridge. use this for gesture-handler and responder chain debugging.",{group:"do",subgroup:"gestures",shortDescription:"drag via real PointerEvents",seeAlso:["swipe","touch","pinch"],order:20}),s("swipe","swipe gesture (drag with fewer steps)",["rnx do swipe <x1> <y1> <x2> <y2> [steps] [stepMs]"],[e("rnx do swipe 350 400 50 400 10 16")],void 0,{group:"do",subgroup:"gestures",shortDescription:"drag with faster defaults",seeAlso:["drag","gesture"],order:30}),s("long-press","hold a press on a testID or at coordinates",["rnx do long-press <testid> [durationMs]","rnx do long-press <x> <y> [durationMs]","rnx do long-press --testid <id> [durationMs]"],[e("rnx do long-press myButton 600"),e("rnx do long-press 196 400 600"),e("rnx do long-press --testid myButton 600")],void 0,{group:"do",subgroup:"gestures",shortDescription:"hold a press on a testID or at coordinates",seeAlso:["tap","gesture"],order:40}),s("touch","low-level touch primitives",["rnx do touch <down|move|up|cancel> <x> <y> [pointerId]"],[e("rnx do touch down 200 720 1"),e("rnx do touch up 200 720 1")],void 0,{group:"do",subgroup:"gestures",shortDescription:"low-level touch primitives",seeAlso:["drag","pinch"],order:50}),s("gesture","preset gestures such as scrolls and edge swipes",["rnx do gesture <preset> [durationMs]"],[e("rnx do gesture swipe-from-bottom-edge"),e("rnx do gesture scroll-up 250")],void 0,{group:"do",subgroup:"gestures",shortDescription:"preset gestures (scroll / edge swipes)",seeAlso:["swipe","scroll"],order:60}),s("pinch","two-pointer pinch helper",["rnx do pinch <x1> <y1> <x2> <y2> <x1'> <y1'> <x2'> <y2'> [steps] [stepMs]"],[e("rnx do pinch 120 320 280 320 90 320 310 320")],void 0,{group:"do",subgroup:"gestures",shortDescription:"two-pointer pinch helper",seeAlso:["touch","gesture"],order:70}),s("state","dump raw runtime state (shell, worker, keyboard, node, scroll, hit, gesture, ownership)",["rnx debug state <kind> [args]","rnx debug state shell","rnx debug state keyboard","rnx debug state node <id>","rnx debug state hit <x> <y>","rnx debug state gesture <x> <y>","rnx debug state ownership","rnx debug state worklets"],[e("rnx debug state shell"),e("rnx debug state keyboard"),e("rnx debug state node photos"),e("rnx debug state hit 200 720"),e("rnx debug state ownership",'authoritative surface-ownership + pointer-delivery snapshot to diagnose "which surface owns this region"'),e("rnx debug state worklets","tenant + shell worklet-runtime counts. shell-sourced events (keyboard, scroll) only reach handlers the SHELL runtime holds, so compare eventHandlerNames across both sides when a reanimated handler never fires")],void 0,{group:"debug",subgroup:"state",shortDescription:"runtime dumps (shell, worker, keyboard, hit, gesture, ownership, ...)",seeAlso:["js"]}),s("js","evaluate javascript in the browser context",["rnx debug js <expression>"],[e("rnx debug js SootSim.bridges.test.getNodeCount()"),e("rnx debug js SootSim.bridges.keyboard.isVisible()"),e("rnx debug js SootSim.state.root.children.length")],"reach into the SootSim global: bridges (test, debug, interact, keyboard, a11y, perf, ...), state (root, simId, ...), chrome (shell, headless, engineBase, ...).",{group:"debug",subgroup:"js",shortDescription:"evaluate JS in the engine realm (reach into SootSim.*)",seeAlso:["state"]}),s("errors","show recent console errors",["rnx get errors [n|clear] [--json]"],[e("rnx get errors 5"),e("rnx get errors clear"),e("rnx get errors --json | jq length")],void 0,{group:"get",subgroup:"diagnostics",shortDescription:"console errors (default 20)",options:[t("--json","emit array of { timestamp, args, stack } for scripts")],seeAlso:["warnings","requests","logs"]}),s("warnings","show recent console warnings",["rnx get warnings [n] [--json]"],[e("rnx get warnings 5"),e("rnx get warnings --json")],void 0,{group:"get",subgroup:"diagnostics",shortDescription:"console warnings (default 20)",options:[t("--json","emit array of { timestamp, args } for scripts")],seeAlso:["errors","logs"]}),s("requests","show recent failed network requests",["rnx get requests [n|all n|clear] [--json]"],[e("rnx get requests 5"),e("rnx get requests all 10"),e("rnx get requests --json")],void 0,{group:"get",subgroup:"diagnostics",shortDescription:"failed / all / clear request buffer",options:[t("--json","emit array of request entries for scripts")],seeAlso:["errors","network"]}),s("diagnosis",'composed "what just went wrong?" report',["rnx get diagnosis [--since <duration>] [--include <csv>] [--json]"],[e("rnx get diagnosis","last 90s, all dimensions"),e("rnx get diagnosis --since 30s --include network,react"),e("rnx get diagnosis --json > /tmp/diagnosis.json","structured snapshot for agent / CI consumption")],"runs timeline + network (failed/slow) + react summary + console errors in parallel against the same sim and returns a grouped report.",{group:"get",subgroup:"diagnostics",shortDescription:"timeline + network + react + console summary",options:[t("--since <duration>","absolute window, e.g. 90s or 2m (default 90s)"),t("--include <csv>","timeline, network, react, console, screen"),t("--slow-threshold <n>","network slow-request threshold in ms"),t("--limit <n>","rows to print per react table"),t("--json","emit one structured JSON document")],seeAlso:["errors","warnings","requests","network"]}),s("animations","list every active animation with id, kind, progress, and current value",["rnx get animations","rnx get animations --json"],[e("rnx get animations","one line per running animation"),e("rnx get animations --json | jq","structured for scripts")],'answers "what is moving right now?" without resorting to screenshots or debug js. covers both realms: `shell` rows are native UI, home, and app transitions, `tenant` rows belong to the app itself. a row tagged `graph` or `remote-driven` is sampled by the compositor on its own clock, and a row with no from/to is a native-driver registration whose curve is not declared. pair with `rnx get animation <id>` for full spec, `rnx do stop-animation <id>` to halt one, or `rnx debug trace anim on <id>` for per-tick values (tracing applies to app Animated values only).',{group:"get",subgroup:"animations",shortDescription:"list active animations",seeAlso:["animation","stop-animation","state"]}),s("animation","full detail for a single active animation by id",["rnx get animation <id>"],[e("rnx get animation 3","JSON spec + current value + progress")],"ids come from `rnx get animations`. poll this to watch a single animation settle without enabling a trace.",{group:"get",subgroup:"animations",shortDescription:"single animation detail",seeAlso:["animations","stop-animation"]}),s("state","runtime dashboard: url, active animation counts, diagnostics, shell scene",["rnx get state"],[e("rnx get state","what is this sim doing right now")],'one-shot JSON snapshot answering "what is happening in this sim right now", intended as the first call of a debugging pass. separate from `rnx debug state <kind>`, which dumps raw runtime state for one subsystem.',{group:"get",subgroup:"diagnostics",shortDescription:"runtime dashboard snapshot",seeAlso:["animations","errors","keyboard"]}),s("memory","per-worker live object counts (nodes, paragraphs, yoga nodes, pictures, raster images) + image cache + JS heaps; --watch samples over time and reports per-counter slopes",["rnx get memory","rnx debug memory --watch [seconds] [--interval seconds]"],[e("rnx get memory","one-shot tenant + shell + host snapshot"),e("rnx debug memory --watch 120","sample every 5s for 2 min, then print which counters are climbing"),e("rnx debug memory --watch 60 --interval 2 --json | jq .perSecond","machine-readable slopes for scripting")],"the memory-leak RCA instrument. counters are maintained at engine create/delete sites, so resources retained by discarded (unmounted) subtrees still show up: a climbing nodesRegistered/paragraphs while the on-screen tree is stable means unmounted nodes are being retained. tenant and shell workers report independently (the shell paints the app:* mirror trees). heaps are chrome-only (webkit omits performance.memory).",{group:"debug",subgroup:"profiling",shortDescription:"live object counts + heap sampler",options:[t("--watch [seconds]","sample repeatedly for N seconds (default 60)"),t("--interval <seconds>","seconds between samples (default 5)"),t("--json","one-shot report, or { samples, perSecond } under --watch")],seeAlso:["state","perf"]}),s("stop-animation","stop a running animation by id, or stop all active animations",["rnx do stop-animation <id|all>"],[e("rnx do stop-animation 3"),e("rnx do stop-animation all","freeze everything for inspection")],"the id comes from `rnx get animations`. stopping fires the animation's finished callback with finished=false, matching react-native Animated.stop() semantics.",{group:"do",subgroup:"animations",shortDescription:"stop a running animation",seeAlso:["animations","animation"]}),s("trace","opt-in trace recorders: shell animation frames or per-tick animation values",["rnx debug trace shell [on [limit]|off|status|clear|<recentLimit>]","rnx debug trace anim <on <id|all> [limit]|off [id|all]|status|clear|<id> [limit]>"],[e("rnx debug trace shell on 240","arm the shell frame recorder"),e("rnx debug trace shell 120","dump the last 120 shell samples"),e("rnx debug trace anim on 3 240","record up to 240 ticks for anim #3"),e("rnx debug trace anim 3 60","read last 60 ticks for anim #3"),e("rnx debug trace anim on all","record every active animation")],"for a focused perf trace: `get animations` to find the id, `trace anim on <id>` to arm, reproduce the issue, then `trace anim <id>` to pull per-tick value + velocity samples. `trace anim off` disables when done.",{group:"debug",subgroup:"profiling",shortDescription:"per-tick animation + shell frame trace",seeAlso:["animations","animation","stop-animation"]}),s("image-audit","for every image the compositor actually drew, how many source pixels it landed on each device pixel",["rnx debug image-audit [--min <ratio>] [--all] [--json]"],[e("rnx debug image-audit","list only the images that came up short"),e("rnx debug image-audit --all","every drawn image, shortfall or not"),e("rnx debug image-audit --min 0.9","accept down to 0.9 source pixels per device pixel"),e("rnx debug image-audit --json | jq .rows","machine-readable rows")],"a row whose shortfall is well under 1.0 is an image the pipeline under-resolved, and 0.5 is half resolution. exits 1 when any image is under the ratio. exits 2 when the audit observed no image draws at all, which is not a pass: the frame it forced painted without re-running an image draw, so either nothing on screen is an image or every image replayed from a cached paint boundary. scroll or navigate the sim and run it again.",{group:"debug",subgroup:"profiling",shortDescription:"source pixels per device pixel, per drawn image",options:[t("--min <ratio>","shortfall below which a row counts as under-resolved (default 0.98)"),t("--all","show every drawn image, not just the ones under the ratio"),t("--json","the full report plus the resolved minimum and under count")],seeAlso:["screenshot","memory"]}),s("snapshot","capture a tree snapshot (layout + transform + opacity) for later diffing",["rnx debug snapshot [label]"],[e("rnx debug snapshot before","capture state before an interaction"),e("rnx debug snapshot after","capture state after an interaction"),e("rnx debug snapshot","unlabeled: auto-named based on current time")],"snapshots live in-memory in the engine for the life of the page. use `snapshots` to list labels and `diff <a> <b>` to compare two. clear with `clear-snapshots [label]`.",{group:"debug",subgroup:"snapshot",shortDescription:"capture tree snapshot for later diff",seeAlso:["snapshots","diff","state"]}),s("snapshots","list the labels of every captured tree snapshot",["rnx debug snapshots"],[e("rnx debug snapshots","list every label, with capture time")],void 0,{group:"debug",subgroup:"snapshot",shortDescription:"list captured snapshot labels",seeAlso:["snapshot","diff"]}),s("diff","compare two captured snapshots: added / removed / changed nodes",["rnx debug diff <labelA> <labelB>"],[e("rnx debug diff before after","diff a paired snapshot: added/removed/changed counts + per-node detail")],"project both snapshots with `snapshot <label>` first, then diff. emits counts plus added / removed / changed arrays; pipe through jq for filtering.",{group:"debug",subgroup:"snapshot",shortDescription:"compare two snapshots",seeAlso:["snapshot","snapshots","clear-snapshots"]}),s("clear-snapshots","drop a labeled snapshot (or all snapshots if no label is given)",["rnx debug clear-snapshots [label]"],[e("rnx debug clear-snapshots before","drop just the `before` snapshot"),e("rnx debug clear-snapshots","drop every captured snapshot")],void 0,{group:"debug",subgroup:"snapshot",shortDescription:"discard captured snapshots",seeAlso:["snapshot","snapshots","diff"]}),s("find","dump engine-internal nodes by category (e.g. all Sheet.Frame portals)",["rnx debug find sheets","rnx debug find portals"],[e("rnx debug find sheets","every Sheet.Frame-shaped node in the tree"),e("rnx debug find portals","every portal-host / portal-view pair the engine is currently mounting"),e("rnx debug find sheets | jq '.[] | select(.translateY != null)'","narrow to sheets mid-animation")],"category-specific inspectors for runtime structures that `describe` collapses. emits a JSON array of node-shaped records; pipe through jq for filtering.",{group:"debug",subgroup:"inspectors",shortDescription:"list engine-internal nodes by category",seeAlso:["state","describe"]}),s("record","toggle the debug event ring buffer (off / on)",["rnx debug record [on|off]"],[e("rnx debug record on","arm the ring buffer"),e("rnx debug record off","stop recording"),e("rnx debug record","query current state without flipping it")],"the ring buffer feeds `debug recent <channel> [n]`. enable a channel via `debug enable <channel>` first so the corresponding events actually fire.",{group:"debug",subgroup:"events",shortDescription:"toggle the debug event ring buffer",seeAlso:["recent","clear-events"]}),s("recent","tail the last N events from the debug ring buffer (optionally filtered by channel)",["rnx debug recent","rnx debug recent <channel>","rnx debug recent <channel> <n>"],[e("rnx debug recent","last 50 events across every channel"),e("rnx debug recent portals 20","last 20 portal events"),e("rnx debug recent layout 200","larger window for layout debugging")],"requires `debug record on` to have armed the buffer at some prior point. default limit is 50.",{group:"debug",subgroup:"events",shortDescription:"last N debug events",seeAlso:["record","clear-events"]}),s("clear-events","drop everything currently in the debug event ring buffer",["rnx debug clear-events"],[e("rnx debug clear-events","reset the buffer before a clean repro")],void 0,{group:"debug",subgroup:"events",shortDescription:"reset the debug event ring buffer",seeAlso:["record","recent"]}),s("sample-color","average the canvas color over a pixel or rect; returns hex + rgba",["rnx debug sample-color <x> <y> [w] [h]","rnx debug sample-color --area x,y,w,h","rnx debug sample-color --id <testID>","rnx debug sample-color --text <text>"],[e("rnx debug sample-color 196 400"),e("rnx debug sample-color 100 200 50 80"),e("rnx debug sample-color --id loginButton"),e('rnx debug sample-color --text "Sign in" --json')],"coords are logical rnx units. pixels are averaged across the rect. --json emits { r, g, b, a, hex, samples, rect } for scripts.",{group:"debug",subgroup:"profiling",shortDescription:"average canvas color over a rect",options:[t("--area x,y,w,h","rect to sample (coords are logical rnx units)"),t("--id <testID>","sample over a node's layout rect"),t("--text <text>","sample over the node matching text"),t("--json","emit { r, g, b, a, hex, samples, rect }")],seeAlso:["screenshot"]}),s("screenshot","capture the canvas (full or a cropped area) as PNG",["rnx screenshot [--output <path>]","rnx screenshot --area x,y,w,h [--output <path>]","rnx screenshot --id <testID> [--output <path>]","rnx screenshot --text <text> [--output <path>]"],[e("rnx screenshot"),e("rnx screenshot --output /tmp/app.png"),e("rnx screenshot --area 0,200,393,400"),e("rnx screenshot --id loginButton --output button.png")],"area args use logical rnx coords; default output is /tmp/sootsim-inspect.png.",{options:[t("--output <path>","write PNG here (default /tmp/sootsim-inspect.png)"),t("--area x,y,w,h","crop to rect (logical rnx coords)"),t("--id <testID>","crop to a node's layout rect"),t("--text <text>","crop to the node matching text")],seeAlso:["sample-color","record"]}),s("url","show the current page URL",["rnx get url [--json]"],[e("rnx get url"),e("rnx get url --json")],void 0,{group:"get",subgroup:"info",shortDescription:"current page URL",options:[t("--json","emit { url } for scripts")]}),s("reload","reload the page and wait for reconnection",["rnx do reload"],void 0,void 0,{group:"do",subgroup:"lifecycle",shortDescription:"reload the page + wait for reconnect",seeAlso:["wait","ready"],order:10}),s("globals","list all __sootsim* globals available in the browser",["rnx get globals"],void 0,void 0,{group:"get",subgroup:"info",shortDescription:"list available __sootsim* globals"}),s("sleep","simple local pause between commands",["rnx do sleep [seconds]"],[e("rnx do sleep 0.5")],void 0,{group:"do",subgroup:"lifecycle",shortDescription:"local pause between commands",seeAlso:["settle","idle"],order:40}),s("wait","wait for the current sim to reconnect",["rnx do wait [seconds]"],[e("rnx do wait 30")],void 0,{group:"do",subgroup:"lifecycle",shortDescription:"wait for sim reconnect",seeAlso:["ready","idle","settle"],order:20}),s("settle","wait until the visible layout is stable (default max 3s)",["rnx do settle [seconds] [--strict]"],[e("rnx do settle 3"),e("rnx do settle 2 --strict")],"default requires layout hash to be stable for 3 consecutive polls; this is the right signal for slide transitions, spring-driven position changes, and scroll momentum. --strict additionally requires the worker animation flags (hasActiveAnims, hasActiveNativeAnimations, hasPendingAnimationFrames) to all be false. use --strict only when the target app has no perpetual background animation, otherwise it will time out.",{group:"do",subgroup:"lifecycle",shortDescription:"wait for animations to settle",options:[t("--strict","also require all worker animation flags to be false (use only when no perpetual bg animation)")],seeAlso:["idle","ready","selector"],order:30}),s("ready","block until the guest app bundle is loaded and painted (20s default)",["rnx wait ready [--max-ms <ms>]"],[e("rnx wait ready"),e("rnx wait ready --max-ms 30000","cold uniswap/expensify reload"),e("rnx wait ready --max-ms 5000")],"authoritative signal: the persistent __sootsimExternalAppReady flag (set by the sootsim:externalAppReady event handler, cleared on reload). polls CLI-side so --max-ms can exceed the bridge per-command timeout. prefer this over `do wait`, which is a node-count heuristic that false-positives on ConnectRN skeletons. idempotent: calling it after the app is already ready returns in ~200ms. exits non-zero on timeout.",{group:"wait",shortDescription:"block until the guest app bundle is loaded and painted",options:[t("--max-ms <ms>","override the 20s default deadline")],seeAlso:["idle","selector","settle"],order:10}),s("idle","block until animations, rAF queue, and layout are all settled",["rnx wait idle [--max-ms <ms>] [--strict]"],[e("rnx wait idle"),e("rnx wait idle --max-ms 2000 --strict")],"same signal as `do settle` but under the discoverable `wait` verb. exits non-zero on timeout.",{group:"wait",shortDescription:"block until animations + rAF + layout are settled",options:[t("--max-ms <ms>","override the idle deadline"),t("--strict","also require worker animation flags to all be false")],seeAlso:["ready","selector","settle"],order:20}),s("selector","block until a node with <testid> is present with non-zero layout (or absent with --gone)",["rnx wait selector <testid> [--max-ms <ms>] [--gone]"],[e("rnx wait selector loginButton"),e("rnx wait selector feedList --max-ms 3000"),e("rnx wait selector loginButton --gone --max-ms 90000","after a gate action, wait for the gate to leave instead of re-tapping")],'playwright-style. when `find --testid <id>` returns no result, the CLI now prints this command as a hint. `--gone` inverts the wait: it resolves when the node is confirmed absent (the positive "gate/screen left" signal), and never resolves on an indeterminate bridge. exits non-zero on timeout.',{group:"wait",shortDescription:"block until a testID appears (or, with --gone, disappears)",options:[t("--max-ms <ms>","override the deadline"),t("--gone","resolve when the node is absent instead of present")],seeAlso:["ready","idle","find"],order:30}),s("event","block until a semantic timeline event of <kind> appears",["rnx wait event <kind> [--max-ms <ms>]","rnx wait event <kind> --filter <substring>","rnx wait event <kind> --equals <exact>"],[e("rnx wait event fetch --filter BeginSignIn","wait for the next BeginSignIn fetch after a tap"),e("rnx wait event screen --equals Inbox","wait until a screen named Inbox appears"),e("rnx wait event alert","wait for the next iOS alert/sheet"),e("rnx wait event react-commit --max-ms 2000","block until the next React commit fires (or 2s)")],"timeline-backed companion to wait selector / wait idle. polls SootSim.bridges.timeline.recent() at 200ms; matches against url, message, name, path, title, and phase fields. defaults: --max-ms 5000, --since now (only counts events that arrive after this command starts; pass --since cursor to include prior events). exits non-zero on timeout.",{group:"wait",shortDescription:"block until the next timeline event of a given kind",options:[t("--max-ms <ms>","wait deadline in milliseconds (default 5000)"),t("--filter <substring>","substring match against url/message/name/path/title (case-insensitive)"),t("--equals <exact>","exact match against any of the same fields"),t("--since <mode>",'"now" (default: only events after this command starts) or "cursor"'),t("--json","emit { found, elapsedMs, event } JSON")],seeAlso:["ready","idle","selector"],order:40}),s("dispatch","dispatch a raw keyboard key (bypasses visual shift state)",["rnx do dispatch <char>"],[e("rnx do dispatch A")],void 0,{group:"do",subgroup:"text input",shortDescription:"raw key (bypasses visual shift state)",seeAlso:["key","type"],order:70}),s("keyboard","on-screen keyboard state (type, returnKey, autoCap, mode, shift, accessory)",["rnx get keyboard [--json]"],[e("rnx get keyboard"),e("rnx get keyboard --json")],void 0,{group:"get",subgroup:"info",shortDescription:"on-screen keyboard state",options:[t("--json","emit structured JSON")],seeAlso:["type","dismiss","state"]}),s("screens","list the current tenant route, navigation stack, active app, modal stack, and keyboard",["rnx get screens [--json]"],[e("rnx get screens"),e("rnx get screens --json")],'pulls the canonical tenant route path from the router, plus shell state (active app, switcher phase), the navigation bridge (route stack for react-navigation / one-router if present), and the top presented modal. JSON exposes the exact path as route.currentPath. use this when a route, screen id, or nav-state is the fastest orientation signal, e.g. "am I on the onboarding landing, or did the login sheet open over it?". Exits quietly when no navigation bridge is available.',{group:"get",subgroup:"info",shortDescription:"current route + screen + modal + route stack",options:[t("--json","emit structured JSON")],seeAlso:["describe","state","keyboard"]})];function $(r,n){return`${r??""}:${n}`}var C=new Map,P=new Map;for(let r of M){let n=C.get(r.verb);n?n.push(r):C.set(r.verb,[r]),P.set($(r.group,r.verb),r)}var z=new Map(Array.from(C,([r,n])=>[r,n.find(a=>!a.group)??n[0]]));function E(r,n){return P.get($(r,n))}function w(r,n){if(n!==void 0)return E(n,r);let a=C.get(r);return a?.find(l=>!l.group)??a?.[0]}function u(r,n=v){return r.replaceAll("{{bridgePort}}",String(n.bridgePort)).replaceAll("{{defaultShellUrl}}",n.defaultShellUrl)}function B(){return D.slice().sort((r,n)=>r.order-n.order)}function k(r,n){let a=Math.max(...n.map(i=>i.name.length),0)+2,l=n.map(i=>` ${i.name.padEnd(a)}${i.desc}`);return` ${r}
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.376 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import { createRequire as __sootsimCreateRequire } from 'node:module'
3
3
  var require = __sootsimCreateRequire(import.meta.url)
4
4
  var f="\x1B[0m",p="\x1B[1m",a="\x1B[2m",m="\x1B[38;2;42;95;255m",x="\x1B[38;5;84m",d="\x1B[38;5;203m";function c(n,t){return!process.stdout.isTTY||process.env.NO_COLOR!==void 0?t:`${n}${t}${f}`}function s(n){return c(m,n)}function u(n){return c(p,n)}function i(n){return c(a,n)}function w(n){return c(x,n)}function h(n){return c(d,n)}function v(n){console.log(),console.log(` ${s("\u25E4\u25E2")} ${u("rnx")}`),n&&console.log(` ${i(n)}`)}function y(n,t){console.log(`
@@ -1,6 +1,6 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.376 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  import { createRequire as __sootsimCreateRequire } from 'node:module'
3
3
  var require = __sootsimCreateRequire(import.meta.url)
4
- import{L as j,M as H,N as W,k as L,l as N,v as E,z as F}from"./chunk-HWDVC6YA.js";import{a as U}from"./chunk-25KRGDZT.js";import Z from"node:fs";import B from"node:path";function _(e){return e.length<=100&&/^[0-9A-Za-z][0-9A-Za-z._+-]*$/.test(e)}function D(e){try{return Z.readdirSync(e.runtimesDir(),{withFileTypes:!0}).filter(c=>c.isDirectory()).map(c=>c.name).sort(k)}catch{return[]}}function $(e,c){return B.posix.basename(e.tarballPath(c))}function k(e,c){let l=h=>{let p=h.indexOf("-"),w=p>=0?h.slice(0,p):h,x=p>=0?h.slice(p+1):"",M=w.split(".").map(I=>Number.parseInt(I,10));return M.some(I=>!Number.isFinite(I))?[[Number.POSITIVE_INFINITY],h]:[M,x]},[m,u]=l(e),[v,b]=l(c);for(let h=0;h<Math.max(m.length,v.length);h++){let p=m[h]??0,w=v[h]??0;if(p!==w)return p-w}return u===b?0:u?b?u<b?-1:1:-1:1}import{spawn as J}from"node:child_process";import K from"node:crypto";import o from"node:fs";import Y from"node:os";import y from"node:path";import{Readable as q}from"node:stream";import{pipeline as Q}from"node:stream/promises";var X=2;function z(e){function c(){try{let n=JSON.parse(o.readFileSync(e.configFile(),"utf8"));return n&&typeof n=="object"?n:{}}catch{return{}}}function l(n){let t=c();return(n||process.env[e.env.cdnOrigin]||t.cdnOrigin||e.defaultCdnOrigin).replace(/\/+$/,"")}function m(n){let t=c();return n||process.env[e.env.channel]||t.runtimeChannel||"stable"}function u(){if(!e.env.pin)return null;let n=process.env[e.env.pin];if(n&&n.trim())return n.trim();let t=c();return typeof t.runtimeVersion=="string"&&t.runtimeVersion.trim()?t.runtimeVersion.trim():null}function v(n){let t=new URL(`${l(n)}${e.manifestPath}`);return t.searchParams.set("t",String(Date.now())),t.toString()}function b(n,t){return`${l(t)}${e.tarballPath(n)}`}let h=2e3;async function p(n,t){let r=v(n),i=await fetch(r,{headers:{Accept:"application/json"},...t?{signal:AbortSignal.timeout(t)}:{}});if(!i.ok)throw new Error(`manifest fetch failed: ${i.status} ${i.statusText} (${r})`);return await i.json()}function w(n,t={}){let r=m(t.channel),i=t.version||u()||n.channels[r]?.latest;if(!i)throw new Error(`no version specified and channel '${r}' has no latest entry in the manifest`);if(!_(i))throw new Error(`invalid runtime version: ${i}`);let g=n.versions[i];if(!g)throw new Error(`version ${i} not found in manifest; available: ${Object.keys(n.versions).slice(-10).join(", ")||"(none)"}`);return{version:i,channel:r,entry:g}}async function x(n={}){e.ensureHome();let t=l(n.cdnOrigin),r=await p(t),{version:i,channel:g,entry:a}=w(r,n),f=e.runtimeDir(i),d=n.setActive!==!1,O=[i,...n.protectVersions??[]];if(T({protectVersions:O}),!n.force&&o.existsSync(y.join(f,e.installedMarker)))return d&&e.writeActiveRuntime(i),T({protectVersions:O}),{version:i,channel:g,cdnOrigin:t,runtimeDir:f,installed:!1,activated:d,manifest:r};let P=a.tarball||b(i,t),S=o.mkdtempSync(y.join(Y.tmpdir(),`${e.id}-runtime-download-`)),V=y.join(S,$(e,i)),R=y.join(y.dirname(f),`.installing-${i}-${process.pid}`);o.rmSync(R,{recursive:!0,force:!0});try{process.stderr.write(`${e.id}: downloading runtime ${i}\u2026
4
+ import{L as j,M as H,N as W,k as L,l as N,v as E,z as F}from"./chunk-QRDF33WD.js";import{a as U}from"./chunk-5YZYDEMI.js";import Z from"node:fs";import B from"node:path";function _(e){return e.length<=100&&/^[0-9A-Za-z][0-9A-Za-z._+-]*$/.test(e)}function D(e){try{return Z.readdirSync(e.runtimesDir(),{withFileTypes:!0}).filter(c=>c.isDirectory()).map(c=>c.name).sort(k)}catch{return[]}}function $(e,c){return B.posix.basename(e.tarballPath(c))}function k(e,c){let l=h=>{let p=h.indexOf("-"),w=p>=0?h.slice(0,p):h,x=p>=0?h.slice(p+1):"",M=w.split(".").map(I=>Number.parseInt(I,10));return M.some(I=>!Number.isFinite(I))?[[Number.POSITIVE_INFINITY],h]:[M,x]},[m,u]=l(e),[v,b]=l(c);for(let h=0;h<Math.max(m.length,v.length);h++){let p=m[h]??0,w=v[h]??0;if(p!==w)return p-w}return u===b?0:u?b?u<b?-1:1:-1:1}import{spawn as J}from"node:child_process";import K from"node:crypto";import o from"node:fs";import Y from"node:os";import y from"node:path";import{Readable as q}from"node:stream";import{pipeline as Q}from"node:stream/promises";var X=2;function z(e){function c(){try{let n=JSON.parse(o.readFileSync(e.configFile(),"utf8"));return n&&typeof n=="object"?n:{}}catch{return{}}}function l(n){let t=c();return(n||process.env[e.env.cdnOrigin]||t.cdnOrigin||e.defaultCdnOrigin).replace(/\/+$/,"")}function m(n){let t=c();return n||process.env[e.env.channel]||t.runtimeChannel||"stable"}function u(){if(!e.env.pin)return null;let n=process.env[e.env.pin];if(n&&n.trim())return n.trim();let t=c();return typeof t.runtimeVersion=="string"&&t.runtimeVersion.trim()?t.runtimeVersion.trim():null}function v(n){let t=new URL(`${l(n)}${e.manifestPath}`);return t.searchParams.set("t",String(Date.now())),t.toString()}function b(n,t){return`${l(t)}${e.tarballPath(n)}`}let h=2e3;async function p(n,t){let r=v(n),i=await fetch(r,{headers:{Accept:"application/json"},...t?{signal:AbortSignal.timeout(t)}:{}});if(!i.ok)throw new Error(`manifest fetch failed: ${i.status} ${i.statusText} (${r})`);return await i.json()}function w(n,t={}){let r=m(t.channel),i=t.version||u()||n.channels[r]?.latest;if(!i)throw new Error(`no version specified and channel '${r}' has no latest entry in the manifest`);if(!_(i))throw new Error(`invalid runtime version: ${i}`);let g=n.versions[i];if(!g)throw new Error(`version ${i} not found in manifest; available: ${Object.keys(n.versions).slice(-10).join(", ")||"(none)"}`);return{version:i,channel:r,entry:g}}async function x(n={}){e.ensureHome();let t=l(n.cdnOrigin),r=await p(t),{version:i,channel:g,entry:a}=w(r,n),f=e.runtimeDir(i),d=n.setActive!==!1,O=[i,...n.protectVersions??[]];if(T({protectVersions:O}),!n.force&&o.existsSync(y.join(f,e.installedMarker)))return d&&e.writeActiveRuntime(i),T({protectVersions:O}),{version:i,channel:g,cdnOrigin:t,runtimeDir:f,installed:!1,activated:d,manifest:r};let P=a.tarball||b(i,t),S=o.mkdtempSync(y.join(Y.tmpdir(),`${e.id}-runtime-download-`)),V=y.join(S,$(e,i)),R=y.join(y.dirname(f),`.installing-${i}-${process.pid}`);o.rmSync(R,{recursive:!0,force:!0});try{process.stderr.write(`${e.id}: downloading runtime ${i}\u2026
5
5
  `),await ee(P,V),process.stderr.write(`${e.id}: extracting runtime ${i}\u2026
6
6
  `);let s=await ne(V);if(s!==a.sha256)throw new Error(`sha256 mismatch for runtime ${i}: expected ${a.sha256}, actual ${s}`);if(o.mkdirSync(R,{recursive:!0}),await te(V,R),!o.existsSync(y.join(R,e.installedMarker)))throw new Error(`extracted tarball for runtime ${i} is missing ${e.installedMarker}`);o.rmSync(f,{recursive:!0,force:!0}),o.renameSync(R,f)}catch(s){throw o.rmSync(R,{recursive:!0,force:!0}),s}finally{o.rmSync(S,{recursive:!0,force:!0})}return d&&e.writeActiveRuntime(i),T({protectVersions:O}),{version:i,channel:g,cdnOrigin:t,runtimeDir:f,installed:!0,activated:d,manifest:r}}async function M(n={}){e.ensureHome(),T({protectVersions:n.protectVersions});let t=l(n.cdnOrigin),r=m(n.channel),i=u();if(i){let R=e.readActiveRuntime(),s=o.existsSync(y.join(e.runtimeDir(i),e.installedMarker));if(R===i&&s)return{checked:!0,updated:!1,reason:`runtime is pinned to ${i}`,activeVersion:R,latestVersion:i};let C=await x({version:i,channel:r,cdnOrigin:t,setActive:!1,protectVersions:n.protectVersions});return{checked:!0,updated:!0,reason:`runtime is pinned to ${i}`,activeVersion:i,latestVersion:i,install:C}}let g=await p(t),a=g.channels[r]?.latest;if(!a)return{checked:!0,updated:!1,reason:`channel '${r}' has no latest runtime`,activeVersion:e.readActiveRuntime()};if(!g.versions[a])return{checked:!0,updated:!1,reason:`manifest is missing version ${a}`,activeVersion:e.readActiveRuntime(),latestVersion:a};let d=e.readActiveRuntime(),O=d?e.runtimeDir(d):null,P=O?o.existsSync(y.join(O,e.installedMarker)):!1;if(!(!d||!P||k(a,d)>0))return{checked:!0,updated:!1,reason:"active runtime is current",activeVersion:d,latestVersion:a};let V=await x({version:a,channel:r,cdnOrigin:t,setActive:!1,protectVersions:n.protectVersions});return{checked:!0,updated:!0,activeVersion:a,latestVersion:a,install:V}}async function I(n={}){let t=e.readActiveRuntime(),r=u();if(r)return{active:t,latest:r,pinned:r,outdated:t!==r};try{let i=await p(l(n.cdnOrigin),h),g=m(n.channel),a=i.channels[g]?.latest??null,f=!!(a&&(!t||k(a,t)>0));return{active:t,latest:a,pinned:null,outdated:f}}catch{return{active:t,latest:null,pinned:null,outdated:!1}}}function A(n={}){e.ensureHome();let t=Math.max(1,Math.floor(n.retainVersions??X)),r=D(e),i=new Set(r),g=e.readActiveRuntime(),a=u(),f=new Set;for(let s of[g,a,...n.protectVersions??[]])s&&i.has(s)&&f.add(s);for(let s of[...r].reverse()){if(f.size>=t)break;f.add(s)}let d="__RUNTIME_VERSION__",O=$(e,d),P=O.indexOf(d),S=O.slice(0,P),V=O.slice(P+d.length),R=[];if(P>=0)try{R=o.readdirSync(e.cacheDir(),{withFileTypes:!0}).filter(s=>{if(!s.isFile())return!1;let C=s.name.startsWith(S)&&s.name.endsWith(V),G=s.name.startsWith(S)&&s.name.endsWith(`${V}.partial`);return C||G}).map(s=>y.join(e.cacheDir(),s.name)).sort()}catch{}return{activeVersion:g,pinnedVersion:a,keepVersions:r.filter(s=>f.has(s)),removeVersions:r.filter(s=>!f.has(s)),removeCacheFiles:R}}function T(n={}){let t=A(n);for(let r of t.removeVersions)o.rmSync(e.runtimeDir(r),{recursive:!0,force:!0});for(let r of t.removeCacheFiles)o.rmSync(r,{force:!0});return t}return{product:e,resolveCdnOrigin:l,resolveChannel:m,resolvePin:u,manifestUrl:v,tarballUrl:b,fetchManifest:p,resolveVersion:w,install:x,updateToLatest:M,checkUpToDate:I,planLocalRetention:A,pruneLocalRetention:T,listInstalled:()=>D(e)}}async function ee(e,c){let l=await fetch(e);if(!l.ok||!l.body)throw new Error(`download failed: ${l.status} ${l.statusText} (${e})`);o.mkdirSync(y.dirname(c),{recursive:!0});let m=`${c}.partial`;try{await Q(q.fromWeb(l.body),o.createWriteStream(m)),o.renameSync(m,c)}catch(u){try{o.unlinkSync(m)}catch{}throw u}}function ne(e){return new Promise((c,l)=>{let m=K.createHash("sha256"),u=o.createReadStream(e);u.on("data",v=>m.update(v)),u.on("error",l),u.on("end",()=>c(m.digest("hex")))})}function te(e,c){return new Promise((l,m)=>{let u=J("tar",["-xzf",e,"-C",c],{stdio:["ignore","inherit","inherit"]});u.on("error",m),u.on("exit",v=>{v===0?l():m(new Error(`tar exited with code ${v}`))})})}var ie="https://contrast.dev",Ve=`${U.origin}/changelog`,we="https://contrast.dev/api/changelog/sootsim",re={id:"sootsim",defaultCdnOrigin:ie,manifestPath:"/runtimes/manifest.json",tarballPath:e=>`/runtimes/sootsim-runtime-${e}.tar.gz`,installedMarker:"index.html",env:{cdnOrigin:"SOOTSIM_CDN_ORIGIN",channel:"SOOTSIM_RUNTIME_CHANNEL"},ensureHome:j,configFile:F,runtimesDir:L,runtimeDir:N,cacheDir:E,readActiveRuntime:H,writeActiveRuntime:W},Pe=z(re);export{_ as a,k as b,ie as c,Ve as d,we as e,Pe as f};