rnxsim 0.1.319 → 0.1.320

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 (156) hide show
  1. package/dist-cli/bin.js +5 -5
  2. package/dist-cli/chunks/{agent-Q4VAKGOL.js → agent-E5UUPQXH.js} +2 -2
  3. package/dist-cli/chunks/{agent-wrapper-Q2O4TR7H.js → agent-wrapper-LG33N5K3.js} +2 -2
  4. package/dist-cli/chunks/{app-fonts-B7UP3XTI.js → app-fonts-PCQ4B3U7.js} +2 -2
  5. package/dist-cli/chunks/{assert-NAFO4KMH.js → assert-2ADFEOQF.js} +2 -2
  6. package/dist-cli/chunks/{auth-CNUGYLCX.js → auth-NLB6E4XZ.js} +2 -2
  7. package/dist-cli/chunks/{beta-GN5BSED6.js → beta-AHSJMAOI.js} +2 -2
  8. package/dist-cli/chunks/{camera-EJRFUEXV.js → camera-IYDWHB6P.js} +2 -2
  9. package/dist-cli/chunks/{chunk-IHBAZ6ZJ.js → chunk-2HKDCEHH.js} +2 -2
  10. package/dist-cli/chunks/{chunk-YT6RDBLT.js → chunk-2MJ3M5IF.js} +1 -1
  11. package/dist-cli/chunks/{chunk-ZH5X2H73.js → chunk-3U24SEPY.js} +2 -2
  12. package/dist-cli/chunks/{chunk-VQJMFDUN.js → chunk-4FISS7J4.js} +1 -1
  13. package/dist-cli/chunks/{chunk-JWAF5GXK.js → chunk-4MGSHK3U.js} +2 -2
  14. package/dist-cli/chunks/{chunk-VLT3KCQT.js → chunk-66W32ZAC.js} +3 -3
  15. package/dist-cli/chunks/{chunk-VB6FBQBW.js → chunk-6TYW7J2S.js} +2 -2
  16. package/dist-cli/chunks/{chunk-X2AHIPGY.js → chunk-6W4W2I2H.js} +1 -1
  17. package/dist-cli/chunks/{chunk-YPDFFAE7.js → chunk-7QZCEUQH.js} +2 -2
  18. package/dist-cli/chunks/{chunk-5KXQT465.js → chunk-ACQPOL3N.js} +1 -1
  19. package/dist-cli/chunks/{chunk-TJSU45JD.js → chunk-AGGO4OMP.js} +1 -1
  20. package/dist-cli/chunks/{chunk-3DEQXXSF.js → chunk-AXCNZWS3.js} +3 -3
  21. package/dist-cli/chunks/{chunk-4UY4JSQ4.js → chunk-BB2LGH5B.js} +1 -1
  22. package/dist-cli/chunks/{chunk-BSCOA6LL.js → chunk-BGG34LXF.js} +2 -2
  23. package/dist-cli/chunks/{chunk-WUZNWEBT.js → chunk-BQQ2PVYQ.js} +1 -1
  24. package/dist-cli/chunks/{chunk-YS6EWKGV.js → chunk-BVWMU5CV.js} +2 -2
  25. package/dist-cli/chunks/{chunk-KMOLZN54.js → chunk-C2TJ2PLG.js} +1 -1
  26. package/dist-cli/chunks/{chunk-GHHVBATM.js → chunk-C5VFP3FH.js} +2 -2
  27. package/dist-cli/chunks/{chunk-QNNCKWZF.js → chunk-CQIH7FPK.js} +1 -1
  28. package/dist-cli/chunks/{chunk-AZ6S36CJ.js → chunk-DNWMXDM5.js} +2 -2
  29. package/dist-cli/chunks/{chunk-AX7PASOS.js → chunk-F2BHWSSW.js} +2 -2
  30. package/dist-cli/chunks/{chunk-PFRV3TNY.js → chunk-FECIIPFS.js} +2 -2
  31. package/dist-cli/chunks/{chunk-2TNUPISH.js → chunk-FEWMMQJW.js} +2 -2
  32. package/dist-cli/chunks/{chunk-DD6F2WOA.js → chunk-FL3B7VQN.js} +1 -1
  33. package/dist-cli/chunks/{chunk-R4JD7RQ5.js → chunk-FLBNTPBZ.js} +2 -2
  34. package/dist-cli/chunks/{chunk-Z72QDFQG.js → chunk-GBXRASTZ.js} +2 -2
  35. package/dist-cli/chunks/{chunk-BVVUWEDG.js → chunk-GTNGFY6G.js} +2 -2
  36. package/dist-cli/chunks/{chunk-BQEWRZON.js → chunk-GTSF2TRR.js} +1 -1
  37. package/dist-cli/chunks/{chunk-HPO2HNJX.js → chunk-I76GK5JV.js} +1 -1
  38. package/dist-cli/chunks/{chunk-SPHYBQAZ.js → chunk-IETTTBIZ.js} +2 -2
  39. package/dist-cli/chunks/{chunk-PYDVLJOW.js → chunk-IQKWVOTX.js} +2 -2
  40. package/dist-cli/chunks/{chunk-Z4LSUN4M.js → chunk-JB2XWONS.js} +1 -1
  41. package/dist-cli/chunks/{chunk-7IW3PBRN.js → chunk-JMI7WP73.js} +2 -2
  42. package/dist-cli/chunks/{chunk-7DABL656.js → chunk-KFZQQUKH.js} +2 -2
  43. package/dist-cli/chunks/{chunk-S5XIMHEZ.js → chunk-KX3OJSX7.js} +2 -2
  44. package/dist-cli/chunks/{chunk-UGYANOZR.js → chunk-LEXXWNFD.js} +2 -2
  45. package/dist-cli/chunks/{chunk-RIRZMX6H.js → chunk-LRRJ3ORH.js} +2 -2
  46. package/dist-cli/chunks/{chunk-L5JZXGYY.js → chunk-LYC2SH2Y.js} +1 -1
  47. package/dist-cli/chunks/{chunk-IJYWC6QT.js → chunk-MHWLXGJ3.js} +2 -2
  48. package/dist-cli/chunks/{chunk-72VB52IV.js → chunk-OI3DYYZV.js} +1 -1
  49. package/dist-cli/chunks/{chunk-RVKU4F4H.js → chunk-OTZCVQOW.js} +1 -1
  50. package/dist-cli/chunks/{chunk-M52VGRRN.js → chunk-QB35GBD5.js} +1 -1
  51. package/dist-cli/chunks/{chunk-522E7BRF.js → chunk-QSLD3RW7.js} +3 -3
  52. package/dist-cli/chunks/{chunk-5ABSWU6L.js → chunk-RHJFGX4A.js} +2 -2
  53. package/dist-cli/chunks/{chunk-HCM7K4AU.js → chunk-RY6GQZCH.js} +2 -2
  54. package/dist-cli/chunks/{chunk-6BGXKWGF.js → chunk-VN56TL3M.js} +2 -2
  55. package/dist-cli/chunks/{chunk-76EVUPAL.js → chunk-XMKZNJSL.js} +1 -1
  56. package/dist-cli/chunks/{chunk-3F3R7BM4.js → chunk-YK6FDOG6.js} +2 -2
  57. package/dist-cli/chunks/{chunk-JZIFPU64.js → chunk-YNVC42AB.js} +1 -1
  58. package/dist-cli/chunks/{chunk-B27W3QWS.js → chunk-YUFTRNFC.js} +1 -1
  59. package/dist-cli/chunks/{chunk-LKO7E4VO.js → chunk-Z2PV5BCT.js} +2 -2
  60. package/dist-cli/chunks/{chunk-XJHAGKJD.js → chunk-Z2TCDWQ7.js} +2 -2
  61. package/dist-cli/chunks/{chunk-DIADYUIH.js → chunk-ZWEBQ5MB.js} +3 -3
  62. package/dist-cli/chunks/{cleanup-GBLORG7C.js → cleanup-TDZUWYSO.js} +2 -2
  63. package/dist-cli/chunks/cli-version-6BZFQWCZ.js +4 -0
  64. package/dist-cli/chunks/{compat-OIWTQTTQ.js → compat-A7N5NVPI.js} +2 -2
  65. package/dist-cli/chunks/{config-DZCL6YFH.js → config-LL62ATVZ.js} +2 -2
  66. package/dist-cli/chunks/{control-MY5IZSCI.js → control-ILH3IPOG.js} +2 -2
  67. package/dist-cli/chunks/daemon-ZUGLIV5U.js +4 -0
  68. package/dist-cli/chunks/{debug-6UQUAZKP.js → debug-WJN2YK7L.js} +3 -3
  69. package/dist-cli/chunks/{desktop-DFBFLSQ3.js → desktop-SACOU52P.js} +3 -3
  70. package/dist-cli/chunks/{detox-CIHUJOCI.js → detox-HHA3KUST.js} +2 -2
  71. package/dist-cli/chunks/{device-7SOHSSJO.js → device-K5DKLWPM.js} +2 -2
  72. package/dist-cli/chunks/{diagnose-P2FMX5HU.js → diagnose-XZYAGIEN.js} +2 -2
  73. package/dist-cli/chunks/{disk-cleanup-6PP5CQVO.js → disk-cleanup-BUHPEEFX.js} +2 -2
  74. package/dist-cli/chunks/drivers-4HU5USPO.js +4 -0
  75. package/dist-cli/chunks/{film-2WWHMPGU.js → film-XDVNORAX.js} +3 -3
  76. package/dist-cli/chunks/flow-Y42LVOVP.js +4 -0
  77. package/dist-cli/chunks/help-V6WBUFML.js +4 -0
  78. package/dist-cli/chunks/{hidden-runtime-alias-ZZMC3E6R.js → hidden-runtime-alias-YIA5VZFO.js} +2 -2
  79. package/dist-cli/chunks/{home-paths-Y6X4LFSU.js → home-paths-V276ZMFH.js} +2 -2
  80. package/dist-cli/chunks/inspect-WTEDX3A3.js +4 -0
  81. package/dist-cli/chunks/install-desktop-AFDKV7BH.js +4 -0
  82. package/dist-cli/chunks/{login-BARKKDRL.js → login-KZAVKL6H.js} +4 -4
  83. package/dist-cli/chunks/{logout-YVUMTGMP.js → logout-JFXC6U2P.js} +2 -2
  84. package/dist-cli/chunks/{maestro-UPVELJ7G.js → maestro-BS7T227L.js} +3 -3
  85. package/dist-cli/chunks/{maestro-generate-EP76F4GB.js → maestro-generate-WXDB2WUX.js} +3 -3
  86. package/dist-cli/chunks/{mode-ULOBJKDO.js → mode-DV2L7WE3.js} +2 -2
  87. package/dist-cli/chunks/{optional-demo-registry-O6RANNU3.js → optional-demo-registry-RRSPBGOI.js} +2 -2
  88. package/dist-cli/chunks/{perf-ANPUHMRS.js → perf-O3TENAUU.js} +2 -2
  89. package/dist-cli/chunks/{permissions-NGMRAUGD.js → permissions-DY5DNWIF.js} +2 -2
  90. package/dist-cli/chunks/{record-CBO3QGRB.js → record-XY5YEMJB.js} +3 -3
  91. package/dist-cli/chunks/{report-issue-K7XKNJ2D.js → report-issue-NQA6W4LU.js} +2 -2
  92. package/dist-cli/chunks/reset-6JVRMIUQ.js +4 -0
  93. package/dist-cli/chunks/runtime-IQ5TU5BZ.js +4 -0
  94. package/dist-cli/chunks/{screenshot-command-KZZSYRX5.js → screenshot-command-26DMBR73.js} +3 -3
  95. package/dist-cli/chunks/{screenshot-layers-Z3KSMJ3R.js → screenshot-layers-MVE2F5BO.js} +2 -2
  96. package/dist-cli/chunks/{screenshots-capture-QTB5GQNA.js → screenshots-capture-DTSUAE6Q.js} +2 -2
  97. package/dist-cli/chunks/{serve-JKTMCDG2.js → serve-6PXVFH4O.js} +2 -2
  98. package/dist-cli/chunks/{setup-GCAPEHZH.js → setup-LDAY6QUQ.js} +2 -2
  99. package/dist-cli/chunks/{skills-Y4YDYEKA.js → skills-RQWM2J2Q.js} +2 -2
  100. package/dist-cli/chunks/{state-3HTG2CZU.js → state-IDQ43TIA.js} +2 -2
  101. package/dist-cli/chunks/{storage-XQE2XZJM.js → storage-SNSBN3HY.js} +2 -2
  102. package/dist-cli/chunks/store-7L2QR4QG.js +4 -0
  103. package/dist-cli/chunks/telemetry-BHDOJRSL.js +4 -0
  104. package/dist-cli/chunks/{timeline-XYACO63Y.js → timeline-36EQTWQZ.js} +2 -2
  105. package/dist-cli/chunks/{upgrade-EVQTCUSG.js → upgrade-MTJRBSTJ.js} +2 -2
  106. package/dist-cli/chunks/upload-76KYWLC7.js +4 -0
  107. package/dist-cli/chunks/{version-NLGYSRXS.js → version-CMVXNVHJ.js} +2 -2
  108. package/dist-cli/chunks/{web-25OBFWDS.js → web-AHOIPBU3.js} +2 -2
  109. package/dist-cli/chunks/{what-happened-MRN6CH5W.js → what-happened-W3GZXD7R.js} +2 -2
  110. package/dist-lib/agent-daemon-client.cjs +1 -1
  111. package/dist-lib/agent-events.cjs +1 -1
  112. package/dist-lib/agent-identity.cjs +1 -1
  113. package/dist-lib/agent-sessions.cjs +1 -1
  114. package/dist-lib/attached-projects.cjs +1 -1
  115. package/dist-lib/auth/shared-session.cjs +1 -1
  116. package/dist-lib/backend-origin.cjs +1 -1
  117. package/dist-lib/beta.cjs +1 -1
  118. package/dist-lib/beta.mjs +1 -1
  119. package/dist-lib/bridge-constants.cjs +1 -1
  120. package/dist-lib/bridge-contract.cjs +1 -1
  121. package/dist-lib/cli-constants.cjs +1 -1
  122. package/dist-lib/config.cjs +1 -1
  123. package/dist-lib/detox/index.cjs +1 -1
  124. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  125. package/dist-lib/home-paths.cjs +1 -1
  126. package/dist-lib/host/bridge-host.cjs +1 -1
  127. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  128. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  129. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  130. package/dist-lib/host/websocket-proxy.cjs +1 -1
  131. package/dist-lib/index.cjs +1 -1
  132. package/dist-lib/jump-to-source-babel.cjs +1 -1
  133. package/dist-lib/menu.cjs +1 -1
  134. package/dist-lib/menu.mjs +1 -1
  135. package/dist-lib/metro.cjs +1 -1
  136. package/dist-lib/profiles.cjs +1 -1
  137. package/dist-lib/public-brand.cjs +1 -1
  138. package/dist-lib/render-mode.cjs +1 -1
  139. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  140. package/dist-lib/sdk.cjs +1 -1
  141. package/dist-lib/sdk.mjs +1 -1
  142. package/dist-lib/skills.cjs +1 -1
  143. package/dist-lib/vite.cjs +1 -1
  144. package/package.json +1 -1
  145. package/dist-cli/chunks/cli-version-6C4ZR7I7.js +0 -4
  146. package/dist-cli/chunks/daemon-42K3PILG.js +0 -4
  147. package/dist-cli/chunks/drivers-CFJGYQFX.js +0 -4
  148. package/dist-cli/chunks/flow-KL7ZTVKL.js +0 -4
  149. package/dist-cli/chunks/help-T2OCMQPI.js +0 -4
  150. package/dist-cli/chunks/inspect-PVMJQHMF.js +0 -4
  151. package/dist-cli/chunks/install-desktop-NHQZI4EH.js +0 -4
  152. package/dist-cli/chunks/reset-FXAX2W5A.js +0 -4
  153. package/dist-cli/chunks/runtime-GNCU66RR.js +0 -4
  154. package/dist-cli/chunks/store-22QXHYUS.js +0 -4
  155. package/dist-cli/chunks/telemetry-VYGOHQWW.js +0 -4
  156. package/dist-cli/chunks/upload-XH3RQR4U.js +0 -4
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.319 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.320 | (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 w={bridgePort:7668,defaultShellUrl:"http://localhost:5173/"};function t(s,n){return{flag:s,description:n}}function e(s,n){return{command:s,description:n}}var D=[{id:"cleanup",slug:"cleanup",navTitle:"cleanup",title:"rnxsim 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:["rnxsim cleanup","rnxsim cleanup --apply","rnxsim cleanup --aggressive","rnxsim 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("rnxsim cleanup","show exact reclaimable disk usage without deleting"),e("rnxsim cleanup --apply","reclaim safe caches and old runtimes"),e("rnxsim cleanup --apply --aggressive","also remove the rollback runtime and recordings")],related:["runtime","storage","daemon"]},{id:"record",slug:"record",navTitle:"record",title:"rnxsim 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:["rnxsim record [options]","rnxsim record start [options]","rnxsim record prelude-start [--origin <url>]","rnxsim record stop [--output <path>]","rnxsim record status # reports the record-start session; also probes the engine","rnxsim record cancel # cancels a record-start session","rnxsim 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("rnxsim record --duration 5"),e("rnxsim record --format mp4 --output demo.mp4 --duration 8"),e("rnxsim record --output demo.gif --duration 3"),e("rnxsim record --frames 10 --output ./frames/"),e("rnxsim record --mode combined --duration 8 --open --owner my-org --repo my-app","capture video + events and upload a repo-associated preview link"),e("rnxsim record start --format mp4","begin; interact freely across any number of CLI calls"),e("rnxsim record start --mode combined","start a stateful preview-share recording"),e("rnxsim record prelude-start","capture hidden setup events before starting the visible proof segment"),e("rnxsim record stop --output flow.mp4","finalize + save"),e("rnxsim record cancel","discard a record-start session (atomic --duration runs own their own lifecycle)"),e("rnxsim record upload --events ./my-run.jsonl.gz --open"),e("rnxsim record upload --video /tmp/sootsim-flow.webm")],related:["screenshot","maestro"]},{id:"film",slug:"film",navTitle:"film",title:"rnxsim 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 `rnxsim mode three configure`.",order:61,usage:["rnxsim 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("rnxsim film flows/feed-scroll.yaml"),e("rnxsim film flows/swap.yaml --script hero-arc --background gradient-tide --out ~/Desktop/swap-3d.mp4"),e("rnxsim 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:"rnxsim 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:["rnxsim storage clear [--sim <id>] [--json]","rnxsim storage profile list","rnxsim storage profile create <id>","rnxsim storage profile clear <id>","rnxsim 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("rnxsim storage clear"),e("rnxsim storage clear --sim a9 --json"),e("rnxsim storage profile list"),e("rnxsim storage profile create qa"),e("rnxsim open 8081 --profile qa --driver playwright"),e("rnxsim storage profile clear qa")],related:["open","desktop"]},{id:"permissions",slug:"permissions",navTitle:"permissions",title:"rnxsim 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:["rnxsim permissions list [--json]","rnxsim permissions get <app-id> [--json]","rnxsim permissions set <app-id> <kind> <status> [options]","rnxsim 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("rnxsim permissions list"),e("rnxsim permissions get dock-8081 --json"),e("rnxsim permissions set dock-8081 camera denied"),e("rnxsim permissions set dock-8081 photo-library limited"),e("rnxsim permissions reset dock-8081 camera")],related:["storage","open"]},{id:"perf",slug:"perf",navTitle:"perf",title:"rnxsim 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:["rnxsim perf cpu [options]","rnxsim perf react <profile|summary|slow|rerenders|tree|why> [options]","rnxsim perf shell <start|stop|transition> [options]","rnxsim 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("rnxsim open 8089 --new --driver playwright --cdp-port 9222"),e("rnxsim perf cpu --cdp-port 9222 --duration 5"),e("rnxsim perf react profile start"),e("rnxsim perf react summary --limit 10"),e("rnxsim perf shell start"),e("rnxsim perf shell stop --json"),e("rnxsim perf scroll start"),e("rnxsim perf scroll stop --json")],related:["open","record","maestro","debug"]},{id:"screenshot",slug:"screenshot",navTitle:"screenshot",title:"rnxsim 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 `rnxsim screenshot` is the same as `rnxsim screenshot capture`.",order:70,usage:["rnxsim screenshot [capture] [output] [options]","rnxsim screenshot layers --ids <testID,testID,...> [--out-dir <dir>]","rnxsim screenshot slides <list|set|add|remove|focus|overview|capture|export> [args]","rnxsim screenshot appstore --plan <plan.yaml> [options]","rnxsim 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("rnxsim screenshot","capture the current sim to a PNG"),e("rnxsim screenshot --output app.png"),e("rnxsim screenshot capture --no-shell --output tenant-only.png"),e("rnxsim screenshot --area 0,200,393,400"),e("rnxsim screenshot slides","list the current live deck"),e("rnxsim screenshot slides capture 1"),e("rnxsim screenshot slides export --out ./exports"),e("rnxsim screenshot appstore --plan .sootsim/app-store.yaml"),e("rnxsim screenshot appstore --plan .sootsim/app-store.yaml --compose-only","rerender marketing canvases from existing raw PNGs"),e("rnxsim 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:"rnxsim 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:["rnxsim mode screenshot [on|off|toggle]","rnxsim mode three [on|off|toggle]","rnxsim mode three configure [flags]","rnxsim mode three load <timeline.json>","rnxsim mode three seek <time-ms>","rnxsim mode three play|pause","rnxsim 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("rnxsim mode screenshot on","hide browser chrome around the canvas"),e("rnxsim mode screenshot off"),e("rnxsim mode three on","force 3d mode on"),e("rnxsim mode three configure --colorway sky --background gradient-sunset","set the phone and background to specific options"),e('rnxsim mode three configure --setting room --gradient "#0a1822,#0d2233,#06101a"',"physical room setting + a custom brand-color gradient background"),e("rnxsim mode three configure --focus on --focus-amount strong --focus-range tight","enable a strong Focus with a tight sharp band"),e("rnxsim mode three configure --script hero-pan --animate --slow","play a camera script slowly"),e("rnxsim mode three configure --reset-pose","return to the default pose"),e("rnxsim mode three render ./cinematic.json --output ./frames --fps 30","render deterministic transparent RGBA PNG frames")],related:["screenshot"]},{id:"describe",slug:"describe",navTitle:"describe",title:"rnxsim 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:["rnxsim 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("rnxsim describe"),e("rnxsim describe button"),e("rnxsim describe --verbose"),e("rnxsim describe --json"),e("rnxsim describe --watch"),e("rnxsim describe --subtree feedListRoot","limit to one subtree"),e('rnxsim describe --only "*Header*"',"glob-match across text/role/testID")],related:["find","get","do"]},{id:"find",slug:"find",navTitle:"find",title:"rnxsim 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 `rnxsim 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:["rnxsim find <text>","rnxsim find --testid <id>","rnxsim find --role <role>","rnxsim find --type <component-type>","rnxsim find --pressable","rnxsim find --visible","rnxsim 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('rnxsim find "Sign in"'),e("rnxsim find --testid loginButton"),e("rnxsim find --role button"),e("rnxsim find --pressable"),e("rnxsim find --visible --json"),e("rnxsim find --interactive-targets","ranked visible+pressable nodes with copy-paste tap commands")],related:["describe","get","do"]},{id:"assert",slug:"assert",navTitle:"assert",title:"rnxsim 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:["rnxsim 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("rnxsim assert find --testid set-max-input --count 4","exactly four matching nodes"),e("rnxsim assert describe --contains swap-form-header","substring anywhere in the tree dump"),e("rnxsim assert get errors --count 0","fail the commit if any console error"),e("rnxsim assert find --testid submit --exists --within 3000","poll for 3s before failing"),e(`rnxsim assert describe --jq '.shell.state == "app"'`,"jq predicate over the payload"),e("git bisect run rnxsim assert find --testid set-max-input --count 4","git-bisect integration")],related:["find","describe","get","list"]},{id:"get",slug:"get",navTitle:"get",title:"rnxsim get",description:"read runtime state (tree, a11y, layout, url, count, errors, \u2026)",summary:"Grouping verb for pure reads. `rnxsim 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:["rnxsim get <noun> [args]"],examples:[e("rnxsim get tree"),e("rnxsim get a11y 3"),e("rnxsim get layout --styling"),e("rnxsim get url"),e("rnxsim get count"),e("rnxsim get errors 5"),e("rnxsim get requests 5"),e("rnxsim get node loginButton"),e("rnxsim get layout loginButton"),e("rnxsim get globals")],related:["do","find","describe"]},{id:"do",slug:"do",navTitle:"do",title:"rnxsim do",description:"drive the app (tap, type, scroll, swipe, key, reload, \u2026)",summary:"Grouping verb for user-interaction actions. `rnxsim do <action>` covers the write side of the bridge: taps, typing, key presses, touch/scroll/drag/swipe/pinch gestures, reload, and local timing helpers.",order:71,usage:["rnxsim do <action> [args]"],examples:[e("rnxsim do tap 196 727"),e("rnxsim do double-tap 196 400"),e('rnxsim do tap-text "Sign in"'),e("rnxsim do tap-id loginNextButton"),e('rnxsim do type "natew.bsky.social"'),e('rnxsim do type-into email "me@example.com"'),e("rnxsim do key return"),e("rnxsim do key-sequence shift h i"),e("rnxsim do dismiss"),e("rnxsim do scroll feed 0 400"),e("rnxsim do drag 200 720 200 160"),e("rnxsim do swipe 200 720 200 160"),e("rnxsim do gesture swipe-from-bottom-edge"),e("rnxsim do reload")],related:["shell","get","find","describe","wait"]},{id:"wait",slug:"wait",navTitle:"wait",title:"rnxsim 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:["rnxsim wait <kind> [args]"],examples:[e("rnxsim wait ready","block until the app bundle is loaded and painted"),e("rnxsim wait idle","block until animations + rAF queue are settled"),e("rnxsim wait selector loginButton","block until a node with this testID is present"),e("rnxsim 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:"rnxsim 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:["rnxsim shell launch <appId> [waitMs] [--clear-state]","rnxsim shell home [waitMs]","rnxsim shell switcher [waitMs]","rnxsim shell open-card <appId> [waitMs]","rnxsim shell appearance <light|dark|auto|toggle>","rnxsim shell lock","rnxsim shell shake"],examples:[e("rnxsim shell launch photos"),e("rnxsim shell home"),e("rnxsim shell switcher 800"),e("rnxsim shell appearance dark"),e("rnxsim shell lock")],related:["do","debug"]},{id:"network",slug:"network",navTitle:"network",title:"rnxsim 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:["rnxsim network [limit]","rnxsim network --failed","rnxsim network --slow [--threshold <ms>]","rnxsim network --filter <substring>","rnxsim network --tail","rnxsim network get <id>","rnxsim 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("rnxsim network","list the last 20 requests"),e("rnxsim network 100","list the last 100"),e("rnxsim network --failed","only errors and 4xx/5xx"),e("rnxsim network --slow","requests slower than 1000ms, sorted by duration desc"),e("rnxsim network --slow --threshold 200","tighten the slow threshold to 200ms"),e("rnxsim network --filter api.example.com"),e("rnxsim network --tail","live follow mode, ctrl-c to stop"),e("rnxsim network --tail --failed"),e("rnxsim network get req-42-l7k9"),e("rnxsim network clear")],related:["debug","get","logs"]},{id:"logs",slug:"logs",navTitle:"logs",title:"rnxsim 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:["rnxsim logs [limit]","rnxsim logs --level <csv>","rnxsim logs --filter <substring>","rnxsim logs --tail","rnxsim 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("rnxsim logs","last 50 log entries"),e("rnxsim logs 200","last 200"),e("rnxsim logs --level error,warn"),e('rnxsim logs --filter "hydrate"'),e("rnxsim logs --tail","live follow mode, ctrl-c to stop"),e("rnxsim logs --tail --level error"),e("rnxsim logs --internal","include engine-internal logs"),e("rnxsim logs clear")],related:["network","get"]},{id:"debug",slug:"debug",navTitle:"debug",title:"rnxsim 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:["rnxsim 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("rnxsim debug enable sheets,portals"),e("rnxsim debug snapshot before"),e("rnxsim debug diff before after"),e("rnxsim debug recent portals 20")],related:["open","what-happened"]},{id:"what-happened",slug:"what-happened",navTitle:"what-happened",title:"rnxsim 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:["rnxsim what-happened","rnxsim what-happened --summary","rnxsim 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("rnxsim what-happened","since the previous CLI call"),e("rnxsim what-happened --summary","5 errors \xB7 2 warnings \xB7 1 toast"),e("rnxsim what-happened --kinds toast,fetch --since 10s","trace a toast back to its trigger"),e("rnxsim what-happened --flow --since 5m","section the last 5 minutes by screen/route, a demo-friendly recap"),e("rnxsim what-happened --all --json","dump full ring as JSON")],related:["timeline","debug","logs","network"]},{id:"timeline",slug:"timeline",navTitle:"timeline",title:"rnxsim timeline",description:"control the semantic event timeline (enable opt-in kinds, clear, dump)",summary:"Distinct from `rnxsim record` (video) and `rnxsim debug record` (engine channels): manages the semantic agent-facing event stream that backs `rnxsim what-happened`. Cheap kinds record at all times; opt-in kinds (scroll, gesture, text-input, reanimated, animation) need to be enabled.",order:92,usage:["rnxsim 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("rnxsim timeline status"),e("rnxsim timeline start scroll,gesture","enable opt-in kinds"),e("rnxsim timeline stop all","silence everything"),e("rnxsim timeline dump /tmp/events.json")],related:["what-happened","debug","record"]},{id:"open",slug:"open",navTitle:"open",title:"rnxsim 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 rnxsim 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:["rnxsim 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("rnxsim open 8081"),e("rnxsim open --new 8085"),e("rnxsim open 8081 --profile qa"),e("rnxsim open 8081 --profile qa --driver playwright"),e("rnxsim open http://localhost:8082"),e("rnxsim open /settings","dispatch a route deep link to the current app"),e("rnxsim open myapp://settings/profile"),e("rnxsim open 8081 --driver playwright --headless","headless CI / scripted runs"),e("rnxsim open 8081 --new --driver playwright --device iphone-16","pinned device profile (comparisons need the same device)"),e("rnxsim open 8089 --new --driver playwright --cdp-port 9222","open a sim that is both driveable and cpu-profilable (then `rnxsim perf cpu --cdp-port 9222`)"),e("rnxsim 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("rnxsim open 8081 --no-describe","silent open for scripts"),e("rnxsim open 8081 --no-hmr","open with metro HMR off (e.g. when reproducing a non-HMR boot path)"),e("rnxsim open 8081 --driver playwright --hot","driver playwright but keep HMR on (e.g. recording an edit-driven demo)"),e("rnxsim 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:"rnxsim 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:["rnxsim list [--all] [--bundle <substr>] [--app-port <port>] [--primary] [--sim <sim>] [--json]","rnxsim 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("rnxsim list"),e("rnxsim list --all","include idle host shells"),e("rnxsim list --app-port 8082","only sims running the app on port 8082"),e("rnxsim list --bundle bluesky","sims whose bundle url mentions bluesky"),e("rnxsim list --primary"),e("rnxsim list --sim a6"),e("rnxsim list --drivers","show every installable launch driver"),e('rnxsim list --json | jq ".[] | select(.active)"')],related:["open","use","close"]},{id:"use",slug:"use",navTitle:"use",title:"rnxsim 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:["rnxsim use [sim]"],options:[t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("--sim <sim>","target a specific connected sim")],examples:[e("rnxsim use"),e("rnxsim use a6")],related:["list","open","close"]},{id:"claim",slug:"claim",navTitle:"claim",title:"rnxsim 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 `rnxsim 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:["rnxsim 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 `rnxsim open --new` to get a fresh sim instead")],examples:[e("rnxsim claim"),e("rnxsim claim a6"),e("rnxsim claim a6 --force","last-resort lease steal; see `rnxsim open --new` for the safer alternative")],related:["list","use","open","close"]},{id:"close",slug:"close",navTitle:"close",title:"rnxsim 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:["rnxsim close [sim]","rnxsim close --all","rnxsim 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("rnxsim close"),e("rnxsim close a6"),e("rnxsim close --all"),e("rnxsim close --others")],related:["list","use"]},{id:"setup",slug:"setup",navTitle:"setup",title:"rnxsim setup",description:"check local setup and install the recommended bridge daemon",summary:"Run the guided machine setup. It asks which package manager you use for app commands, explains repo-pinned Node versions, recommends installing `rnxsim` globally, and offers the background daemon so every CLI command can reuse the same bridge instead of spinning up a server. No per-app rnx install or iOS native build is required; run Metro/Expo/One normally, then open it with `rnxsim open`.",order:140,usage:["rnxsim setup [options]"],options:[t("--app <dir>","project directory to inspect (default: current directory)"),t("-y, --yes","accept the recommended setup steps without prompting"),t("--dry-run","show what setup would offer without changing files")],examples:[e("rnxsim setup"),e("rnxsim setup --app ./apps/mobile"),e("rnxsim setup --dry-run"),e("rnxsim setup --yes")],related:["open","daemon","serve","compat","skill"]},{id:"compat",slug:"compat",navTitle:"compat",title:"rnxsim 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:["rnxsim 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("rnxsim compat"),e("rnxsim compat react-native-reanimated"),e("rnxsim compat --brief")],related:["setup","report-issue"]},{id:"report-issue",slug:"report-issue",navTitle:"report-issue",title:"rnxsim 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:['rnxsim 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('rnxsim report-issue "react-native-example renders its sheet behind the tab bar"',"preview the report and ask for interactive confirmation"),e('rnxsim report-issue --dry-run "the compat scan does not recognize react-native-example"',"inspect what would be attached without sending"),e('rnxsim 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:"rnxsim 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:["rnxsim desktop [--port <number>] [--profile <id>]","rnxsim 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("rnxsim desktop"),e("rnxsim desktop --port 8081"),e("rnxsim desktop --profile qa"),e("rnxsim desktop install --yes")],related:["open","list","debug"]},{id:"daemon",slug:"daemon",navTitle:"daemon",title:"rnxsim daemon",description:"manage the optional background bridge service",summary:"Install or manage the persistent background bridge. It registers a launchd agent on macOS or systemd user service on Linux so local CLI commands can reuse one server. The registration is recommended for local development and is not needed in CI. `rnxsim daemon uninstall` is the clean-teardown path: it stops and removes the agent and deletes the ~/.sootsim cache (runtimes, profiles, lockfile).",order:173,usage:["rnxsim daemon install [--force]","rnxsim daemon uninstall","rnxsim daemon status","rnxsim daemon restart","rnxsim daemon start","rnxsim daemon stop","rnxsim 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("rnxsim daemon install","install and start the background bridge"),e("rnxsim daemon status","is the bridge daemon registered + alive?"),e("rnxsim daemon restart","kick the daemon (after a CLI upgrade, etc.)"),e("rnxsim daemon logs -n 100","tail the last 100 daemon log lines"),e("rnxsim daemon uninstall","clean teardown: remove the launchd/systemd agent + ~/.sootsim cache")],related:["setup","serve","open"]},{id:"serve",slug:"serve",navTitle:"serve",title:"rnxsim serve",description:"run the rnx bridge in the foreground",summary:"Host the WS bridge in the foreground. This is mainly for development or diagnosis; the recommended path is `rnxsim open`, which starts a local bridge when needed, and `rnxsim setup`, which installs the faster background daemon.",order:175,usage:["rnxsim serve [options]"],options:[t("--port <number>","bridge port (defaults to {{bridgePort}})"),t("-q, --quiet","suppress per-connection logging")],examples:[e("rnxsim serve"),e("rnxsim serve --quiet"),e("rnxsim serve --port 7668")],related:["setup","daemon","open"]},{id:"runtime",slug:"runtime",navTitle:"runtime",title:"rnxsim 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. `rnxsim runtime notes` prints release notes for the repo-selected engine, or the machine default when the repo has no selection.",order:176,usage:["rnxsim runtime install [version]","rnxsim runtime list","rnxsim runtime use <version>","rnxsim runtime remove <version>","rnxsim runtime which","rnxsim 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("rnxsim runtime install"),e("rnxsim runtime install 1.2.3"),e("rnxsim runtime install --channel beta","install the latest from a non-stable channel"),e("rnxsim runtime use 1.2.3"),e("rnxsim runtime list","show installed + available versions"),e("rnxsim runtime which","print the currently active runtime version"),e("rnxsim runtime notes","print release notes for this repo runtime"),e("rnxsim runtime remove 1.2.2")],related:["setup","serve","daemon","version"]},{id:"upgrade",slug:"upgrade",navTitle:"upgrade",title:"rnxsim upgrade",description:"update the rnxsim 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:["rnxsim upgrade [--channel <name>]"],options:[t("--channel <name>","runtime channel for 'latest' (default: stable)")],examples:[e("rnxsim upgrade"),e("rnxsim upgrade --channel beta","install the latest engine runtime from the beta channel")],related:["runtime","setup","version"]},{id:"version",slug:"version",navTitle:"version",title:"rnxsim version",description:"show the CLI + engine runtime versions you are running",summary:"Show both independently versioned parts: the `rnxsim` 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 `rnxsim --version` flag keeps the two-line script-friendly output.",order:173,usage:["rnxsim version"],options:[],examples:[e("rnxsim version"),e("rnxsim --version","terse, script-parseable")],related:["upgrade","runtime"]},{id:"maestro",slug:"maestro",navTitle:"maestro",title:"rnxsim 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:["rnxsim maestro [test] <flow-or-dir> [options]","rnxsim maestro # auto-discovers ./.maestro/",'rnxsim maestro generate "<goal>"',"rnxsim maestro start|keep|end","rnxsim maestro validate <flow>","rnxsim maestro init # scaffold a starter flow","rnxsim 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("rnxsim maestro test .maestro/login.yaml"),e('rnxsim maestro generate "log in and verify the welcome screen"'),e("rnxsim maestro start"),e("rnxsim maestro end --output .maestro/login.yaml --validate"),e("rnxsim maestro --env USERNAME=alice test .maestro/login.yaml"),e("rnxsim maestro test .maestro/flow.yaml --record"),e("rnxsim maestro --list-compat")],related:["detox","record"]},{id:"detox",slug:"detox",navTitle:"detox",title:"rnxsim 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:["rnxsim 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("rnxsim detox"),e("rnxsim detox --watch"),e('rnxsim detox -t "login flow"'),e("rnxsim detox --headed","keep the shell visible while iterating on a failing suite")],related:["maestro"]},{id:"device",slug:"device",navTitle:"device",title:"rnxsim 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:["rnxsim device","rnxsim device get","rnxsim device set <model> [--sim <id>]","rnxsim device list"],options:[t("--sim <sim>","target a specific connected sim when setting"),t("--port <number>","bridge port (defaults to {{bridgePort}})")],examples:[e("rnxsim device","show the active device preset"),e("rnxsim device list","list every available model id"),e("rnxsim device set iphone-14"),e("rnxsim device set iphone-16-pro-max --sim a7")],related:["mode","screenshot","open","list"]},{id:"skill",slug:"skill",navTitle:"skill",title:"rnxsim 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:["rnxsim skill install [all|codex|claude] [skill-name...]","rnxsim skill install --target <dir> [skill-name...]","rnxsim 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("rnxsim skill install"),e("rnxsim skill install codex"),e("rnxsim skill install codex rnx-debug"),e("rnxsim skill show"),e("rnxsim skill show rnx-debug")],related:["debug"]},{id:"agent",slug:"agent",navTitle:"agent",title:"rnxsim 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:["rnxsim agent attach <dir> [--name <label>] [--provider codex|claude]","rnxsim agent projects","rnxsim agent project show <id>","rnxsim agent project remove <id>","rnxsim agent sessions [--project <id>] [--all]","rnxsim agent start <projectId> [--provider codex|claude]",'rnxsim agent prompt <sessionId> "<text>"',"rnxsim agent watch <sessionId>","rnxsim agent transcript <sessionId>","rnxsim agent end <sessionId>","rnxsim 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("rnxsim agent attach ~/projects/three-punch --name 3pc"),e("rnxsim agent projects","list every attached project"),e("rnxsim agent project show 3pc"),e("rnxsim agent start 3pc --provider claude","spawn a wrapper session (detached) inside the attached project"),e('rnxsim agent prompt <sessionId> "fix the failing scroll test"',"enqueue a prompt; the daemon delivers it to the wrapper"),e("rnxsim agent watch <sessionId>","tail events.out pretty-printed"),e("rnxsim agent sessions --all","include ended sessions in the list"),e("rnxsim agent transcript <sessionId>"),e("rnxsim agent end <sessionId>"),e("rnxsim agent paths","print userData + state file locations")],related:["skill","auth","login"]},{id:"auth",slug:"auth",navTitle:"auth",title:"rnxsim 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 `rnxsim login` and `rnxsim logout` for the browser-backed desktop session.",order:190,usage:["rnxsim auth"],options:[t("RNX_API_KEY","env var for API-key auth; no local key file is read")],examples:[e("rnxsim auth","show the active auth source")],related:["login","logout","record","maestro"]},{id:"login",slug:"login",navTitle:"login",title:"rnxsim 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:["rnxsim login [--origin <url>]"],options:[t("--origin <url>","auth host (default: auto; prefers contrast.localhost:3000 when available)")],examples:[e("rnxsim login"),e("rnxsim login --origin https://contrast.localhost:3000")],related:["auth","logout","record","maestro"]},{id:"logout",slug:"logout",navTitle:"logout",title:"rnxsim logout",description:"clear the local desktop auth session",summary:"Clear the browser-backed desktop session written by `rnxsim login`. RNX_API_KEY and GitHub runner auth are environment-provided and are not affected.",order:192,usage:["rnxsim logout"],examples:[e("rnxsim logout")],related:["auth","login"]},{id:"config",slug:"config",navTitle:"config",title:"rnxsim 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:["rnxsim config telemetry [on|off|status]"],examples:[e("rnxsim config telemetry","show the current opt-out state and config path"),e("rnxsim config telemetry off","disable CLI + desktop analytics on this machine"),e("SOOTSIM_HINTS=off rnxsim describe","suppress hints for a single run")],related:["auth"]}],F=new Map(D.map(s=>[s.id,s])),T=new Map;for(let s of D){T.set(s.id,s);for(let n of s.aliases||[])T.set(n,s)}function A(s){return T.get(s)}function r(s,n,o,l,a,i){return{verb:s,description:n,usage:o,examples:l,notes:a,...i}}var M=[r("describe","show visible UI elements with type, text, position, size, and style. filters structural noise by default; use --all to include everything.",["rnxsim describe [filter] [--verbose] [--watch] [--all] [--json] [--no-clipped] [--compact] [--no-xy] [--only <glob>] [--subtree <id>] [--testid-like <pat>]"],[e("rnxsim describe"),e("rnxsim describe button"),e("rnxsim describe --verbose"),e("rnxsim describe --all"),e("rnxsim describe --json"),e("rnxsim describe --watch"),e("rnxsim 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("rnxsim describe --compact --no-xy","tighter tree without coords"),e("rnxsim describe --subtree feedListRoot","limit the dump to a subtree rooted at testID feedListRoot"),e('rnxsim describe --only "*Header*"',"glob-match across text/role/testID"),e("rnxsim 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"]}),r("find","unified finder (text, testid, role, type, pressable, visible, interactive-targets)",["rnxsim find <text>","rnxsim find --text <text>","rnxsim find --testid <id>","rnxsim find --role <role>","rnxsim find --type <component-type>","rnxsim find --pressable","rnxsim find --visible","rnxsim find --interactive-targets"],[e('rnxsim find "Sign in"'),e("rnxsim find --testid loginButton"),e("rnxsim find --role button"),e("rnxsim find --pressable"),e("rnxsim find --visible --json"),e("rnxsim find --testid loginButton --verbose"),e("rnxsim find --interactive-targets","ranked list of visible+pressable nodes with copy-paste tap commands"),e("rnxsim 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 `rnxsim 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"]}),r("count","show total node count",["rnxsim get count [--json]"],[e("rnxsim get count"),e("rnxsim get count --json")],void 0,{group:"get",subgroup:"tree",shortDescription:"total node count",options:[t("--json","emit { nodes: n } for scripts")]}),r("layout","measure layout: bare for every visible element on the screen (box + radius/padding/fontSize), or with an id for one node",["rnxsim get layout [id] [--json]"],[e("rnxsim get layout","every visible element: on-screen box (x/y/w/h) + borderRadius, per-side padding, fontSize"),e("rnxsim get layout --json","machine-readable { count, elements }"),e("rnxsim 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"]}),r("tree","dump the node tree",["rnxsim get tree [depth] [--json] [--boxes]"],[e("rnxsim get tree"),e("rnxsim get tree 3"),e("rnxsim get tree --json"),e("rnxsim 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"]}),r("a11y","dump the accessibility tree with roles and labels",["rnxsim get a11y [depth]"],[e("rnxsim 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"]}),r("node","resolve testID/id/text to a full node JSON",["rnxsim get node <matcher>"],[e("rnxsim get node loginButton"),e('rnxsim get node "Sign in"')],void 0,{group:"get",subgroup:"tree",shortDescription:"resolve testID \u2192 id \u2192 text, full node JSON",seeAlso:["find","layout"]}),r("tap","tap a testID, absolute coordinates, or a node resolved from --testid / --text",["rnxsim do tap <testid>","rnxsim do tap <x> <y>","rnxsim do tap --testid <id>","rnxsim do tap --text <text>"],[e("rnxsim do tap loginButton"),e("rnxsim do tap 196 400"),e("rnxsim do tap --testid loginButton"),e('rnxsim 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}),r("double-tap","tap the same spot twice with a short gap",["rnxsim do double-tap <testid> [gapMs]","rnxsim do double-tap <x> <y> [gapMs]","rnxsim do double-tap --testid <id> [gapMs]"],[e("rnxsim do double-tap myButton"),e("rnxsim do double-tap 196 400"),e("rnxsim 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}),r("tap-text","find a node by text content and tap its center",["rnxsim 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('rnxsim do tap-text "Sign in"'),e("rnxsim do tap-text Swap --within sheet-container"),e("rnxsim do tap-text Swap --min-y 700"),e("rnxsim do tap-text Cancel --nth 2"),e("rnxsim 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}),r("tap-id","find a node by testID and tap its center",["rnxsim do tap-id <id>"],[e("rnxsim do tap-id loginButton")],void 0,{group:"do",subgroup:"targeting",shortDescription:"findByTestId then tap center",seeAlso:["tap","tap-text","tap-best"],order:40}),r("tap-best","tap by query: try testID first, then visible text. one command, two strategies",["rnxsim do tap-best <query>"],[e('rnxsim do tap-best "Create expense"',`agent doesn't need to know whether "Create expense" is a testID or text`),e("rnxsim 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}),r("scan","inject a synthetic barcode scan into the active expo-camera scan session",["rnxsim do scan <text> [--format <web-format>]"],[e('rnxsim do scan "https://example.com/pair/abc123"'),e('rnxsim 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}),r("type","type text through the iOS keyboard (requires a focused input)",["rnxsim do type <text>"],[e('rnxsim 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}),r("type-into","find an input by testID, tap to focus, then type text",["rnxsim do type-into <id> <text>"],[e('rnxsim 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}),r("key","press a special keyboard key",["rnxsim do key <name>"],[e("rnxsim do key return"),e("rnxsim 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}),r("key-sequence","press multiple visual keyboard keys in order",["rnxsim do key-sequence <key> [<key> ...]"],[e("rnxsim do key-sequence shift h i")],void 0,{group:"do",subgroup:"text input",shortDescription:"press multiple visual keyboard keys",seeAlso:["key","keycode"],order:40}),r("keycode","press DOM-style key codes through the visual keyboard",["rnxsim do keycode <code> [<code> ...]"],[e("rnxsim 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}),r("dismiss","dismiss the keyboard",["rnxsim do dismiss"],void 0,void 0,{group:"do",subgroup:"text input",shortDescription:"dismiss the iOS keyboard",order:60}),r("scroll","scroll a scrollview by testID or inspect node id",["rnxsim do scroll <id> <x> <y>","rnxsim do scroll --testid <id> <x> <y>","rnxsim do scroll --node-id <nodeId> <x> <y>"],[e("rnxsim do scroll feed-list 0 500"),e("rnxsim do scroll --testid feed-list 0 500"),e("rnxsim 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}),r("drag","drag from one point to another using real PointerEvent dispatch",["rnxsim do drag <x1> <y1> <x2> <y2> [steps] [stepMs]"],[e("rnxsim 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}),r("swipe","swipe gesture (drag with fewer steps)",["rnxsim do swipe <x1> <y1> <x2> <y2> [steps] [stepMs]"],[e("rnxsim do swipe 350 400 50 400 10 16")],void 0,{group:"do",subgroup:"gestures",shortDescription:"drag with faster defaults",seeAlso:["drag","gesture"],order:30}),r("long-press","hold a press on a testID or at coordinates",["rnxsim do long-press <testid> [durationMs]","rnxsim do long-press <x> <y> [durationMs]","rnxsim do long-press --testid <id> [durationMs]"],[e("rnxsim do long-press myButton 600"),e("rnxsim do long-press 196 400 600"),e("rnxsim 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}),r("touch","low-level touch primitives",["rnxsim do touch <down|move|up|cancel> <x> <y> [pointerId]"],[e("rnxsim do touch down 200 720 1"),e("rnxsim do touch up 200 720 1")],void 0,{group:"do",subgroup:"gestures",shortDescription:"low-level touch primitives",seeAlso:["drag","pinch"],order:50}),r("gesture","preset gestures such as scrolls and edge swipes",["rnxsim do gesture <preset> [durationMs]"],[e("rnxsim do gesture swipe-from-bottom-edge"),e("rnxsim do gesture scroll-up 250")],void 0,{group:"do",subgroup:"gestures",shortDescription:"preset gestures (scroll / edge swipes)",seeAlso:["swipe","scroll"],order:60}),r("pinch","two-pointer pinch helper",["rnxsim do pinch <x1> <y1> <x2> <y2> <x1'> <y1'> <x2'> <y2'> [steps] [stepMs]"],[e("rnxsim 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}),r("state","dump raw runtime state (shell, worker, keyboard, node, scroll, hit, gesture, ownership)",["rnxsim debug state <kind> [args]","rnxsim debug state shell","rnxsim debug state keyboard","rnxsim debug state node <id>","rnxsim debug state hit <x> <y>","rnxsim debug state gesture <x> <y>","rnxsim debug state ownership","rnxsim debug state worklets"],[e("rnxsim debug state shell"),e("rnxsim debug state keyboard"),e("rnxsim debug state node photos"),e("rnxsim debug state hit 200 720"),e("rnxsim debug state ownership",'authoritative surface-ownership + pointer-delivery snapshot to diagnose "which surface owns this region"'),e("rnxsim 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"]}),r("js","evaluate javascript in the browser context",["rnxsim debug js <expression>"],[e("rnxsim debug js SootSim.bridges.test.getNodeCount()"),e("rnxsim debug js SootSim.bridges.keyboard.isVisible()"),e("rnxsim 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"]}),r("errors","show recent console errors",["rnxsim get errors [n|clear] [--json]"],[e("rnxsim get errors 5"),e("rnxsim get errors clear"),e("rnxsim 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"]}),r("warnings","show recent console warnings",["rnxsim get warnings [n] [--json]"],[e("rnxsim get warnings 5"),e("rnxsim 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"]}),r("requests","show recent failed network requests",["rnxsim get requests [n|all n|clear] [--json]"],[e("rnxsim get requests 5"),e("rnxsim get requests all 10"),e("rnxsim 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"]}),r("diagnosis",'composed "what just went wrong?" report',["rnxsim get diagnosis [--since <duration>] [--include <csv>] [--json]"],[e("rnxsim get diagnosis","last 90s, all dimensions"),e("rnxsim get diagnosis --since 30s --include network,react"),e("rnxsim 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"]}),r("animations","list every active animation with id, kind, progress, and current value",["rnxsim get animations","rnxsim get animations --json"],[e("rnxsim get animations","one line per running animation"),e("rnxsim 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 `rnxsim get animation <id>` for full spec, `rnxsim do stop-animation <id>` to halt one, or `rnxsim 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"]}),r("animation","full detail for a single active animation by id",["rnxsim get animation <id>"],[e("rnxsim get animation 3","JSON spec + current value + progress")],"ids come from `rnxsim 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"]}),r("state","runtime dashboard: url, active animation counts, diagnostics, shell scene",["rnxsim get state"],[e("rnxsim 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 `rnxsim debug state <kind>`, which dumps raw runtime state for one subsystem.',{group:"get",subgroup:"diagnostics",shortDescription:"runtime dashboard snapshot",seeAlso:["animations","errors","keyboard"]}),r("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",["rnxsim get memory","rnxsim debug memory --watch [seconds] [--interval seconds]"],[e("rnxsim get memory","one-shot tenant + shell + host snapshot"),e("rnxsim debug memory --watch 120","sample every 5s for 2 min, then print which counters are climbing"),e("rnxsim 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"]}),r("stop-animation","stop a running animation by id, or stop all active animations",["rnxsim do stop-animation <id|all>"],[e("rnxsim do stop-animation 3"),e("rnxsim do stop-animation all","freeze everything for inspection")],"the id comes from `rnxsim 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"]}),r("trace","opt-in trace recorders: shell animation frames or per-tick animation values",["rnxsim debug trace shell [on [limit]|off|status|clear|<recentLimit>]","rnxsim debug trace anim <on <id|all> [limit]|off [id|all]|status|clear|<id> [limit]>"],[e("rnxsim debug trace shell on 240","arm the shell frame recorder"),e("rnxsim debug trace shell 120","dump the last 120 shell samples"),e("rnxsim debug trace anim on 3 240","record up to 240 ticks for anim #3"),e("rnxsim debug trace anim 3 60","read last 60 ticks for anim #3"),e("rnxsim 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"]}),r("image-audit","for every image the compositor actually drew, how many source pixels it landed on each device pixel",["rnxsim debug image-audit [--min <ratio>] [--all] [--json]"],[e("rnxsim debug image-audit","list only the images that came up short"),e("rnxsim debug image-audit --all","every drawn image, shortfall or not"),e("rnxsim debug image-audit --min 0.9","accept down to 0.9 source pixels per device pixel"),e("rnxsim 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"]}),r("snapshot","capture a tree snapshot (layout + transform + opacity) for later diffing",["rnxsim debug snapshot [label]"],[e("rnxsim debug snapshot before","capture state before an interaction"),e("rnxsim debug snapshot after","capture state after an interaction"),e("rnxsim 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"]}),r("snapshots","list the labels of every captured tree snapshot",["rnxsim debug snapshots"],[e("rnxsim debug snapshots","list every label, with capture time")],void 0,{group:"debug",subgroup:"snapshot",shortDescription:"list captured snapshot labels",seeAlso:["snapshot","diff"]}),r("diff","compare two captured snapshots: added / removed / changed nodes",["rnxsim debug diff <labelA> <labelB>"],[e("rnxsim 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"]}),r("clear-snapshots","drop a labeled snapshot (or all snapshots if no label is given)",["rnxsim debug clear-snapshots [label]"],[e("rnxsim debug clear-snapshots before","drop just the `before` snapshot"),e("rnxsim debug clear-snapshots","drop every captured snapshot")],void 0,{group:"debug",subgroup:"snapshot",shortDescription:"discard captured snapshots",seeAlso:["snapshot","snapshots","diff"]}),r("find","dump engine-internal nodes by category (e.g. all Sheet.Frame portals)",["rnxsim debug find sheets","rnxsim debug find portals"],[e("rnxsim debug find sheets","every Sheet.Frame-shaped node in the tree"),e("rnxsim debug find portals","every portal-host / portal-view pair the engine is currently mounting"),e("rnxsim 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"]}),r("record","toggle the debug event ring buffer (off / on)",["rnxsim debug record [on|off]"],[e("rnxsim debug record on","arm the ring buffer"),e("rnxsim debug record off","stop recording"),e("rnxsim 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"]}),r("recent","tail the last N events from the debug ring buffer (optionally filtered by channel)",["rnxsim debug recent","rnxsim debug recent <channel>","rnxsim debug recent <channel> <n>"],[e("rnxsim debug recent","last 50 events across every channel"),e("rnxsim debug recent portals 20","last 20 portal events"),e("rnxsim 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"]}),r("clear-events","drop everything currently in the debug event ring buffer",["rnxsim debug clear-events"],[e("rnxsim 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"]}),r("sample-color","average the canvas color over a pixel or rect; returns hex + rgba",["rnxsim debug sample-color <x> <y> [w] [h]","rnxsim debug sample-color --area x,y,w,h","rnxsim debug sample-color --id <testID>","rnxsim debug sample-color --text <text>"],[e("rnxsim debug sample-color 196 400"),e("rnxsim debug sample-color 100 200 50 80"),e("rnxsim debug sample-color --id loginButton"),e('rnxsim debug sample-color --text "Sign in" --json')],"coords are logical rnxsim 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 rnxsim 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"]}),r("screenshot","capture the canvas (full or a cropped area) as PNG",["rnxsim screenshot [--output <path>]","rnxsim screenshot --area x,y,w,h [--output <path>]","rnxsim screenshot --id <testID> [--output <path>]","rnxsim screenshot --text <text> [--output <path>]"],[e("rnxsim screenshot"),e("rnxsim screenshot --output /tmp/app.png"),e("rnxsim screenshot --area 0,200,393,400"),e("rnxsim screenshot --id loginButton --output button.png")],"area args use logical rnxsim 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 rnxsim coords)"),t("--id <testID>","crop to a node's layout rect"),t("--text <text>","crop to the node matching text")],seeAlso:["sample-color","record"]}),r("url","show the current page URL",["rnxsim get url [--json]"],[e("rnxsim get url"),e("rnxsim get url --json")],void 0,{group:"get",subgroup:"info",shortDescription:"current page URL",options:[t("--json","emit { url } for scripts")]}),r("reload","reload the page and wait for reconnection",["rnxsim do reload"],void 0,void 0,{group:"do",subgroup:"lifecycle",shortDescription:"reload the page + wait for reconnect",seeAlso:["wait","ready"],order:10}),r("globals","list all __sootsim* globals available in the browser",["rnxsim get globals"],void 0,void 0,{group:"get",subgroup:"info",shortDescription:"list available __sootsim* globals"}),r("sleep","simple local pause between commands",["rnxsim do sleep [seconds]"],[e("rnxsim do sleep 0.5")],void 0,{group:"do",subgroup:"lifecycle",shortDescription:"local pause between commands",seeAlso:["settle","idle"],order:40}),r("wait","wait for the current sim to reconnect",["rnxsim do wait [seconds]"],[e("rnxsim do wait 30")],void 0,{group:"do",subgroup:"lifecycle",shortDescription:"wait for sim reconnect",seeAlso:["ready","idle","settle"],order:20}),r("settle","wait until the visible layout is stable (default max 3s)",["rnxsim do settle [seconds] [--strict]"],[e("rnxsim do settle 3"),e("rnxsim 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}),r("ready","block until the guest app bundle is loaded and painted (20s default)",["rnxsim wait ready [--max-ms <ms>]"],[e("rnxsim wait ready"),e("rnxsim wait ready --max-ms 30000","cold uniswap/expensify reload"),e("rnxsim 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}),r("idle","block until animations, rAF queue, and layout are all settled",["rnxsim wait idle [--max-ms <ms>] [--strict]"],[e("rnxsim wait idle"),e("rnxsim 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}),r("selector","block until a node with <testid> is present with non-zero layout (or absent with --gone)",["rnxsim wait selector <testid> [--max-ms <ms>] [--gone]"],[e("rnxsim wait selector loginButton"),e("rnxsim wait selector feedList --max-ms 3000"),e("rnxsim 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}),r("event","block until a semantic timeline event of <kind> appears",["rnxsim wait event <kind> [--max-ms <ms>]","rnxsim wait event <kind> --filter <substring>","rnxsim wait event <kind> --equals <exact>"],[e("rnxsim wait event fetch --filter BeginSignIn","wait for the next BeginSignIn fetch after a tap"),e("rnxsim wait event screen --equals Inbox","wait until a screen named Inbox appears"),e("rnxsim wait event alert","wait for the next iOS alert/sheet"),e("rnxsim 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}),r("dispatch","dispatch a raw keyboard key (bypasses visual shift state)",["rnxsim do dispatch <char>"],[e("rnxsim do dispatch A")],void 0,{group:"do",subgroup:"text input",shortDescription:"raw key (bypasses visual shift state)",seeAlso:["key","type"],order:70}),r("keyboard","on-screen keyboard state (type, returnKey, autoCap, mode, shift, accessory)",["rnxsim get keyboard [--json]"],[e("rnxsim get keyboard"),e("rnxsim get keyboard --json")],void 0,{group:"get",subgroup:"info",shortDescription:"on-screen keyboard state",options:[t("--json","emit structured JSON")],seeAlso:["type","dismiss","state"]}),r("screens","list the current tenant route, navigation stack, active app, modal stack, and keyboard",["rnxsim get screens [--json]"],[e("rnxsim get screens"),e("rnxsim 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 $(s,n){return`${s??""}:${n}`}var C=new Map,P=new Map;for(let s of M){let n=C.get(s.verb);n?n.push(s):C.set(s.verb,[s]),P.set($(s.group,s.verb),s)}var z=new Map(Array.from(C,([s,n])=>[s,n.find(o=>!o.group)??n[0]]));function E(s,n){return P.get($(s,n))}function x(s,n){if(n!==void 0)return E(n,s);let o=C.get(s);return o?.find(l=>!l.group)??o?.[0]}function g(s,n=w){return s.replaceAll("{{bridgePort}}",String(n.bridgePort)).replaceAll("{{defaultShellUrl}}",n.defaultShellUrl)}function B(){return D.slice().sort((s,n)=>s.order-n.order)}function y(s,n){let o=Math.max(...n.map(a=>a.name.length),0)+2,l=n.map(a=>` ${a.name.padEnd(o)}${a.desc}`);return` ${s}
@@ -1,7 +1,7 @@
1
- /*! rnx v0.1.319 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.320 | (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{a as I,b as me,c as je}from"./chunk-JWAF5GXK.js";import{a as He}from"./chunk-PFRV3TNY.js";import{e as ze}from"./chunk-7IW3PBRN.js";import{c as Fe}from"./chunk-5ABSWU6L.js";import{a as Ve,b as qe,d as Ge}from"./chunk-LKO7E4VO.js";import{a as ce,b as L,c as de,d as ue,e as Le,f as Ne,h as We,i as q,j as Ke}from"./chunk-PYDVLJOW.js";import{a as pe}from"./chunk-RVKU4F4H.js";import{m as A}from"./chunk-YS6EWKGV.js";import{m as Be,p as Ae}from"./chunk-Z4LSUN4M.js";import{f as Re}from"./chunk-3F3R7BM4.js";import{b as ae,d as Ee}from"./chunk-UGYANOZR.js";import{b as Ue,f as _e,g as D,h as $,i as B,k as Te,l as Oe,m as le,o as Me,p as De,q as U,r as P,s as _}from"./chunk-S5XIMHEZ.js";import{J as ie,M as se,O as M,Q as Ce,l as oe}from"./chunk-QNNCKWZF.js";import{a as re}from"./chunk-4UY4JSQ4.js";import{spawn as et,spawnSync as tt}from"node:child_process";import T from"node:fs";import Et from"node:os";import C from"node:path";import{WebSocket as fe}from"ws";function Je(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 $t(e,n){return e?.includes("/one/metro-entry.bundle")?"one":typeof n=="string"&&n?"expo":"unknown"}function Ye(e,n,t){return`${e}//${n}:${t}`}function Pt(e){if(typeof window>"u")return!1;try{let n=new URL(e);return I(n.hostname)&&I(window.location.hostname)&&n.origin!==window.location.origin}catch{return!1}}async function Qe(e,n){let t={...n,cache:n?.cache??"no-store"};return Pt(e)?fetch(`/__fetch-proxy?url=${encodeURIComponent(e)}`,t):fetch(e,t)}function It(e){return e==="https:"?443:80}function kt(e){let n=e.pathname||"/";return(n==="/"||n==="")&&!e.search&&!e.hash}async function Ct(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?{bundleUrl:ue(r.bundleUrl),port:e,framework:typeof r.framework=="string"?r.framework:"unknown",projectName:typeof r.projectName=="string"?r.projectName:void 0}:null}catch{return null}}async function Ze(e,n){let t=e.replace(/\/+$/,"");try{if(I(new URL(t).hostname)){let r=await Ct(n);if(r)return r}}catch{}try{let r=await Qe(`${t}/`,{headers:{"expo-platform":"ios"}});if(r.ok){let o=await r.json(),i=o?.extra?.expoClient||o?.extra||{},s=typeof o?.launchAsset?.url=="string"?o.launchAsset.url:void 0;if(s||i.name)return{bundleUrl:ue(s||`${t}${de}`),port:n,framework:$t(s,i.sdkVersion),projectName:i.name}}}catch{}try{let r=await Qe(`${t}/status`);if(r.ok&&(await r.text()).includes("packager-status:running"))return{bundleUrl:`${t}${de}`,port:n,framework:"metro"}}catch{}return null}async function Rt(e){return Ze(Ye("http:","localhost",e),e)}async function Xe(e){let n=e.trim();if(/^\d+$/.test(n)){let a=parseInt(n,10),l=await Rt(a);if(l)return l;throw Je(`localhost:${a}`)}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):It(o),s=Ye(o,r.hostname,i);if(kt(r)){let a=await Ze(s,i);if(a)return a;throw Je(s)}return{bundleUrl:r.toString(),port:i,framework:"unknown"}}var Ut=12e4,nt=100,_t=5e3,rt=1e4,Tt={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 y(e){return new Promise(n=>setTimeout(n,e))}function ot(e){let n=process.env[e]?.trim();if(!n)return null;let t=Number(n);return Number.isInteger(t)&&t>0?t:null}function Ot(){let e=ot("SOOTSIM_DRIVER_CONNECT_TIMEOUT_MS")??ot("SOOTSIM_PW_CONNECT_TIMEOUT_MS")??Ut;return{timeoutMs:e,hostTimeoutMs:e+_t,intervalMs:nt,attempts:Math.max(1,Math.ceil(e/nt))}}function it(e){return e==="electron"||e==="playwright"}function ge(e,n){return n==="playwright"?e.meta?.sootsimHostDriver==="playwright":e.userAgent?.includes("Electron/")===!0}function Mt(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 J(e){return e.trim()}var mt=/^[A-Za-z][A-Za-z0-9+.-]*:/;function Dt(e){let n=e.trim();if(!n)throw new Error("expected <url-or-route>");return mt.test(n)||n.startsWith("/")?n:`/${n}`}function Bt(e){return e!==null&&typeof e=="object"}function At(e){let n=e.trim();return n?n.startsWith("/")?!0:mt.test(n)?!/^https?:\/\//i.test(n):!1:!1}async function Lt(e,n=!1){if(await z(e,250))return e;let t=se();if(st(e,n,t),t&&M(t)&&await z(t.bridgePort,500))return t.bridgePort;if(t&&M(t))throw new Error(`rnx bridge lockfile is fresh (port ${t.bridgePort}) but the bridge is not reachable. run \`rnxsim daemon restart\` or \`rnxsim daemon uninstall\`.`);await Nt();let r=Ft(["serve","--quiet","--port",String(e)]),o=null;r.once("exit",(s,a)=>{o=String(s??a??"unknown")}),r.unref();let i=Date.now()+rt;for(;Date.now()<i;){let s=se();if(st(e,n,s),s&&M(s)&&await z(s.bridgePort,250))return console.error(` started local bridge on port ${s.bridgePort}; run \`sootsim setup\` to install the background daemon`),s.bridgePort;if(await z(e,250))return console.error(` started local bridge on port ${e}; run \`sootsim setup\` to install the background daemon`),e;if(o)throw new Error(`rnx bridge exited before becoming ready (exit ${o})`);await y(150)}throw new Error(`rnx bridge did not start within ${Math.round(rt/1e3)}s`)}function st(e,n,t){if(!(!n||!t||!M(t)||t.bridgePort===e))throw new Error(`${re.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 Nt(){let e=ie();if(e&&T.existsSync(oe(e)))return;console.error(" installing rnx engine runtime...");let{runRuntime:n}=await import("./runtime-GNCU66RR.js");await n(["install"],{})}function Ft(e){let n=process.argv[1];return n&&T.existsSync(n)?et(process.execPath,[n,...e],{detached:!0,stdio:"ignore",env:process.env}):et(re.commandName,e,{detached:!0,stdio:"ignore",env:process.env})}function z(e,n){return new Promise(t=>{let r=new fe(`ws://127.0.0.1:${e}`,{handshakeTimeout:n}),o=!1,i=s=>{o||(o=!0,r.readyState===fe.OPEN?r.close():r.readyState===fe.CONNECTING&&r.terminate(),t(s))};r.once("open",()=>i(!0)),r.on("error",()=>i(!1)),setTimeout(()=>i(!1),n)})}function we(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 jt(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 Ht(e,n){let t=new URL(e,"http://sootsim.local"),r=new URL(n),o=Q(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 Wt(e){try{let n=new URL(e).pathname.replace(/\/+$/,"")||"/";return/^\/preview\/[^/]+$/.test(n)||/^\/build\/.+/.test(n)}catch{return!1}}async function Yn(e){let n=J(e);return(await ve(n)).bundleUrl}function Kt(e){try{let n=new URL(e.startsWith("http")?e:`http://${e}`),t=n.pathname||"/";if(t!=="/"&&t!==""||n.search||n.hash||!I(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 ve(e){let n=J(e),t=/^\d+$/.test(n)?Number(n):Kt(n);if(t&&t>0){let r=await Ne(t);if(r)return{bundleUrl:r.bundleUrl,port:r.port,framework:r.framework,projectName:r.projectName}}return Xe(n)}function Q(e){let n=e.replace(/\/+$/,"")||"/";return n==="/__soot"||n.startsWith("/__soot/")?"/__soot":""}function Vt(e,n){let t=J(e),r=new URL(n),o=Q(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 pt(e){let n=J(e);return/^\d+$/.test(n)||/^https?:\/\//i.test(n)?!0:/^(localhost|127\.0\.0\.1|\[::1\]|[^/]+\.localhost):\d+(?:\/.*)?$/i.test(n)}async function qt(e,n,t={}){let r=t.resolvedBundle??await ve(e),o=new URL(n),i=Q(o.pathname);o.pathname=`${i}/rn`,o.searchParams.delete("open"),o.searchParams.delete("port"),o.searchParams.delete("demo"),o.searchParams.delete("app");let s=t.hot===void 0?r.bundleUrl:Le(r.bundleUrl,t.hot);return o.searchParams.set("bundle",s),o.toString()}function zt(e){return e.startsWith("~/")?C.join(Et.homedir(),e.slice(2)):C.isAbsolute(e)?e:C.resolve(process.cwd(),e)}function Gt(e){let n={},t={};for(let o=0;o<e.length;o++){if(e[o]==="--replace"){let i=e[o+1];i||(console.error(" rnxsim open: --replace expects <module>=<file>"),process.exit(1));let s=i.indexOf("=");(s<=0||s===i.length-1)&&(console.error(" rnxsim open: --replace expects <module>=<file>"),process.exit(1));let a=i.slice(0,s).trim(),l=zt(i.slice(s+1).trim());T.existsSync(l)||(console.error(` rnxsim open: replacement file not found: ${l}`),process.exit(1)),n[a]={file:l},o++;continue}if(e[o]==="--remap"){let i=e[o+1],s=i?i.indexOf("="):-1;(!i||s<=0||s===i.length-1)&&(console.error(" rnxsim open: --remap expects <host:port>=<host:port>"),process.exit(1)),t[i.slice(0,s).trim()]=i.slice(s+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 Jt(e){if(e){let n=await q(e.bundleUrl);if(n)return pe(n,{trustStart:!0})}return pe(process.env.CONTRAST_REPO_DIR||process.cwd())}async function Qt(e){if(T.existsSync(C.join(oe(e),"index.html")))return;console.error(` installing configured engine runtime ${e}...`);let n=await Re.install({version:e,setActive:!1,protectVersions:Ce()});console.error(` installed engine runtime ${n.version}`)}function Yt(e,n){let t=n??(me()?void 0:ie()??void 0);if(!t)return e;let r=new URL(e);if(!I(r.hostname)){if(!n)return e;throw new Error(`runtimeVersion ${t} requires a local rnx host; got ${r.origin}`)}return r.hostname=je(t),r.toString()}function ft(){return Te().shellBaseUrl}function Zt(e){return Oe(e)?.shellBaseUrl??Ue(e)??ft()}async function Xt(e,n){try{let t=new URL(e),r=t.searchParams.get("bundle")||"";if(!r)return e;let o=n?.appDir??await q(r)??void 0,i=We({bundleUrl:r,repoDir:o,exactProjectDir:!!n||!!o});if(i.length===0)return e;let s=await en(i);return s.length===0?e:(t.searchParams.set("appFonts",Ke(s)),t.toString())}catch{return e}}async function en(e){let n=await Promise.all(e.map(async r=>{let o=new AbortController,i=setTimeout(()=>o.abort(),1500);try{let s=await fetch(r.url,{method:"HEAD",signal:o.signal});return s.status>=200&&s.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 tn(e,n,t={}){if(!e)return new URL(n).toString();if(we(e))return new URL(e).toString();if(jt(e))return Ht(e,n);let r=Tt[e.toLowerCase()];if(r){let o=new URL(n),i=Q(o.pathname);return o.pathname=`${i}${r}`,o.toString()}return pt(e)?qt(e,n,t):Vt(e,n)}async function nn(e,n){try{let t=new URL(e),r=t.searchParams.get("bundle")||"";if(!r)return e;let o=n?.appDir??await q(r)??void 0,i=Ve({bundleUrl:r,repoDir:o,exactProjectDir:!!n||!!o});return!i||i.imageUrl&&!await rn(i.imageUrl)?e:(t.searchParams.set("appSplash",qe(i)),t.toString())}catch{return e}}async function rn(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 G(e,n=ft(),t={}){let r=await tn(e,n,t),o=await nn(await Xt(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 at(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 on(e,n,t,r={}){let o=new URL(await G(e,n,r));return o.searchParams.set("inspectOpen",t),o.toString()}async function sn(e,n,t,r={}){let o=r.attempts??30,i=r.intervalMs??500,s=r.minNodeCount??10;for(let a=0;a<o;a++){let l=P(e,{commandTimeoutMs:n,simId:t,simIdSource:"flag"});try{let d=await l.send({type:"evaluate",code:"(async () => (await window.__sootsimTest?.getNodeCount()) || 0)()"});if(typeof d=="number"&&d>s)return{bridge:l,count:d}}catch{}l.close(),await y(i)}return null}function an(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 ln(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 Zn(e,n){if(e.length===0){console.log(" no sims connected");return}console.log(` connected sims (${e.length}):
4
+ import{a as I,b as me,c as je}from"./chunk-4MGSHK3U.js";import{a as He}from"./chunk-FECIIPFS.js";import{e as ze}from"./chunk-JMI7WP73.js";import{c as Fe}from"./chunk-RHJFGX4A.js";import{a as Ve,b as qe,d as Ge}from"./chunk-Z2PV5BCT.js";import{a as ce,b as L,c as de,d as ue,e as Le,f as Ne,h as We,i as q,j as Ke}from"./chunk-IQKWVOTX.js";import{a as pe}from"./chunk-OTZCVQOW.js";import{m as A}from"./chunk-BVWMU5CV.js";import{m as Be,p as Ae}from"./chunk-JB2XWONS.js";import{f as Re}from"./chunk-YK6FDOG6.js";import{b as ae,d as Ee}from"./chunk-LEXXWNFD.js";import{b as Ue,f as _e,g as D,h as $,i as B,k as Te,l as Oe,m as le,o as Me,p as De,q as U,r as P,s as _}from"./chunk-KX3OJSX7.js";import{J as ie,M as se,O as M,Q as Ce,l as oe}from"./chunk-CQIH7FPK.js";import{a as re}from"./chunk-BB2LGH5B.js";import{spawn as et,spawnSync as tt}from"node:child_process";import T from"node:fs";import Et from"node:os";import C from"node:path";import{WebSocket as fe}from"ws";function Je(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 $t(e,n){return e?.includes("/one/metro-entry.bundle")?"one":typeof n=="string"&&n?"expo":"unknown"}function Ye(e,n,t){return`${e}//${n}:${t}`}function Pt(e){if(typeof window>"u")return!1;try{let n=new URL(e);return I(n.hostname)&&I(window.location.hostname)&&n.origin!==window.location.origin}catch{return!1}}async function Qe(e,n){let t={...n,cache:n?.cache??"no-store"};return Pt(e)?fetch(`/__fetch-proxy?url=${encodeURIComponent(e)}`,t):fetch(e,t)}function It(e){return e==="https:"?443:80}function kt(e){let n=e.pathname||"/";return(n==="/"||n==="")&&!e.search&&!e.hash}async function Ct(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?{bundleUrl:ue(r.bundleUrl),port:e,framework:typeof r.framework=="string"?r.framework:"unknown",projectName:typeof r.projectName=="string"?r.projectName:void 0}:null}catch{return null}}async function Ze(e,n){let t=e.replace(/\/+$/,"");try{if(I(new URL(t).hostname)){let r=await Ct(n);if(r)return r}}catch{}try{let r=await Qe(`${t}/`,{headers:{"expo-platform":"ios"}});if(r.ok){let o=await r.json(),i=o?.extra?.expoClient||o?.extra||{},s=typeof o?.launchAsset?.url=="string"?o.launchAsset.url:void 0;if(s||i.name)return{bundleUrl:ue(s||`${t}${de}`),port:n,framework:$t(s,i.sdkVersion),projectName:i.name}}}catch{}try{let r=await Qe(`${t}/status`);if(r.ok&&(await r.text()).includes("packager-status:running"))return{bundleUrl:`${t}${de}`,port:n,framework:"metro"}}catch{}return null}async function Rt(e){return Ze(Ye("http:","localhost",e),e)}async function Xe(e){let n=e.trim();if(/^\d+$/.test(n)){let a=parseInt(n,10),l=await Rt(a);if(l)return l;throw Je(`localhost:${a}`)}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):It(o),s=Ye(o,r.hostname,i);if(kt(r)){let a=await Ze(s,i);if(a)return a;throw Je(s)}return{bundleUrl:r.toString(),port:i,framework:"unknown"}}var Ut=12e4,nt=100,_t=5e3,rt=1e4,Tt={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 y(e){return new Promise(n=>setTimeout(n,e))}function ot(e){let n=process.env[e]?.trim();if(!n)return null;let t=Number(n);return Number.isInteger(t)&&t>0?t:null}function Ot(){let e=ot("SOOTSIM_DRIVER_CONNECT_TIMEOUT_MS")??ot("SOOTSIM_PW_CONNECT_TIMEOUT_MS")??Ut;return{timeoutMs:e,hostTimeoutMs:e+_t,intervalMs:nt,attempts:Math.max(1,Math.ceil(e/nt))}}function it(e){return e==="electron"||e==="playwright"}function ge(e,n){return n==="playwright"?e.meta?.sootsimHostDriver==="playwright":e.userAgent?.includes("Electron/")===!0}function Mt(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 J(e){return e.trim()}var mt=/^[A-Za-z][A-Za-z0-9+.-]*:/;function Dt(e){let n=e.trim();if(!n)throw new Error("expected <url-or-route>");return mt.test(n)||n.startsWith("/")?n:`/${n}`}function Bt(e){return e!==null&&typeof e=="object"}function At(e){let n=e.trim();return n?n.startsWith("/")?!0:mt.test(n)?!/^https?:\/\//i.test(n):!1:!1}async function Lt(e,n=!1){if(await z(e,250))return e;let t=se();if(st(e,n,t),t&&M(t)&&await z(t.bridgePort,500))return t.bridgePort;if(t&&M(t))throw new Error(`rnx bridge lockfile is fresh (port ${t.bridgePort}) but the bridge is not reachable. run \`rnxsim daemon restart\` or \`rnxsim daemon uninstall\`.`);await Nt();let r=Ft(["serve","--quiet","--port",String(e)]),o=null;r.once("exit",(s,a)=>{o=String(s??a??"unknown")}),r.unref();let i=Date.now()+rt;for(;Date.now()<i;){let s=se();if(st(e,n,s),s&&M(s)&&await z(s.bridgePort,250))return console.error(` started local bridge on port ${s.bridgePort}; run \`sootsim setup\` to install the background daemon`),s.bridgePort;if(await z(e,250))return console.error(` started local bridge on port ${e}; run \`sootsim setup\` to install the background daemon`),e;if(o)throw new Error(`rnx bridge exited before becoming ready (exit ${o})`);await y(150)}throw new Error(`rnx bridge did not start within ${Math.round(rt/1e3)}s`)}function st(e,n,t){if(!(!n||!t||!M(t)||t.bridgePort===e))throw new Error(`${re.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 Nt(){let e=ie();if(e&&T.existsSync(oe(e)))return;console.error(" installing rnx engine runtime...");let{runRuntime:n}=await import("./runtime-IQ5TU5BZ.js");await n(["install"],{})}function Ft(e){let n=process.argv[1];return n&&T.existsSync(n)?et(process.execPath,[n,...e],{detached:!0,stdio:"ignore",env:process.env}):et(re.commandName,e,{detached:!0,stdio:"ignore",env:process.env})}function z(e,n){return new Promise(t=>{let r=new fe(`ws://127.0.0.1:${e}`,{handshakeTimeout:n}),o=!1,i=s=>{o||(o=!0,r.readyState===fe.OPEN?r.close():r.readyState===fe.CONNECTING&&r.terminate(),t(s))};r.once("open",()=>i(!0)),r.on("error",()=>i(!1)),setTimeout(()=>i(!1),n)})}function we(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 jt(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 Ht(e,n){let t=new URL(e,"http://sootsim.local"),r=new URL(n),o=Q(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 Wt(e){try{let n=new URL(e).pathname.replace(/\/+$/,"")||"/";return/^\/preview\/[^/]+$/.test(n)||/^\/build\/.+/.test(n)}catch{return!1}}async function Yn(e){let n=J(e);return(await ve(n)).bundleUrl}function Kt(e){try{let n=new URL(e.startsWith("http")?e:`http://${e}`),t=n.pathname||"/";if(t!=="/"&&t!==""||n.search||n.hash||!I(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 ve(e){let n=J(e),t=/^\d+$/.test(n)?Number(n):Kt(n);if(t&&t>0){let r=await Ne(t);if(r)return{bundleUrl:r.bundleUrl,port:r.port,framework:r.framework,projectName:r.projectName}}return Xe(n)}function Q(e){let n=e.replace(/\/+$/,"")||"/";return n==="/__soot"||n.startsWith("/__soot/")?"/__soot":""}function Vt(e,n){let t=J(e),r=new URL(n),o=Q(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 pt(e){let n=J(e);return/^\d+$/.test(n)||/^https?:\/\//i.test(n)?!0:/^(localhost|127\.0\.0\.1|\[::1\]|[^/]+\.localhost):\d+(?:\/.*)?$/i.test(n)}async function qt(e,n,t={}){let r=t.resolvedBundle??await ve(e),o=new URL(n),i=Q(o.pathname);o.pathname=`${i}/rn`,o.searchParams.delete("open"),o.searchParams.delete("port"),o.searchParams.delete("demo"),o.searchParams.delete("app");let s=t.hot===void 0?r.bundleUrl:Le(r.bundleUrl,t.hot);return o.searchParams.set("bundle",s),o.toString()}function zt(e){return e.startsWith("~/")?C.join(Et.homedir(),e.slice(2)):C.isAbsolute(e)?e:C.resolve(process.cwd(),e)}function Gt(e){let n={},t={};for(let o=0;o<e.length;o++){if(e[o]==="--replace"){let i=e[o+1];i||(console.error(" rnxsim open: --replace expects <module>=<file>"),process.exit(1));let s=i.indexOf("=");(s<=0||s===i.length-1)&&(console.error(" rnxsim open: --replace expects <module>=<file>"),process.exit(1));let a=i.slice(0,s).trim(),l=zt(i.slice(s+1).trim());T.existsSync(l)||(console.error(` rnxsim open: replacement file not found: ${l}`),process.exit(1)),n[a]={file:l},o++;continue}if(e[o]==="--remap"){let i=e[o+1],s=i?i.indexOf("="):-1;(!i||s<=0||s===i.length-1)&&(console.error(" rnxsim open: --remap expects <host:port>=<host:port>"),process.exit(1)),t[i.slice(0,s).trim()]=i.slice(s+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 Jt(e){if(e){let n=await q(e.bundleUrl);if(n)return pe(n,{trustStart:!0})}return pe(process.env.CONTRAST_REPO_DIR||process.cwd())}async function Qt(e){if(T.existsSync(C.join(oe(e),"index.html")))return;console.error(` installing configured engine runtime ${e}...`);let n=await Re.install({version:e,setActive:!1,protectVersions:Ce()});console.error(` installed engine runtime ${n.version}`)}function Yt(e,n){let t=n??(me()?void 0:ie()??void 0);if(!t)return e;let r=new URL(e);if(!I(r.hostname)){if(!n)return e;throw new Error(`runtimeVersion ${t} requires a local rnx host; got ${r.origin}`)}return r.hostname=je(t),r.toString()}function ft(){return Te().shellBaseUrl}function Zt(e){return Oe(e)?.shellBaseUrl??Ue(e)??ft()}async function Xt(e,n){try{let t=new URL(e),r=t.searchParams.get("bundle")||"";if(!r)return e;let o=n?.appDir??await q(r)??void 0,i=We({bundleUrl:r,repoDir:o,exactProjectDir:!!n||!!o});if(i.length===0)return e;let s=await en(i);return s.length===0?e:(t.searchParams.set("appFonts",Ke(s)),t.toString())}catch{return e}}async function en(e){let n=await Promise.all(e.map(async r=>{let o=new AbortController,i=setTimeout(()=>o.abort(),1500);try{let s=await fetch(r.url,{method:"HEAD",signal:o.signal});return s.status>=200&&s.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 tn(e,n,t={}){if(!e)return new URL(n).toString();if(we(e))return new URL(e).toString();if(jt(e))return Ht(e,n);let r=Tt[e.toLowerCase()];if(r){let o=new URL(n),i=Q(o.pathname);return o.pathname=`${i}${r}`,o.toString()}return pt(e)?qt(e,n,t):Vt(e,n)}async function nn(e,n){try{let t=new URL(e),r=t.searchParams.get("bundle")||"";if(!r)return e;let o=n?.appDir??await q(r)??void 0,i=Ve({bundleUrl:r,repoDir:o,exactProjectDir:!!n||!!o});return!i||i.imageUrl&&!await rn(i.imageUrl)?e:(t.searchParams.set("appSplash",qe(i)),t.toString())}catch{return e}}async function rn(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 G(e,n=ft(),t={}){let r=await tn(e,n,t),o=await nn(await Xt(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 at(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 on(e,n,t,r={}){let o=new URL(await G(e,n,r));return o.searchParams.set("inspectOpen",t),o.toString()}async function sn(e,n,t,r={}){let o=r.attempts??30,i=r.intervalMs??500,s=r.minNodeCount??10;for(let a=0;a<o;a++){let l=P(e,{commandTimeoutMs:n,simId:t,simIdSource:"flag"});try{let d=await l.send({type:"evaluate",code:"(async () => (await window.__sootsimTest?.getNodeCount()) || 0)()"});if(typeof d=="number"&&d>s)return{bridge:l,count:d}}catch{}l.close(),await y(i)}return null}function an(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 ln(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 Zn(e,n){if(e.length===0){console.log(" no sims connected");return}console.log(` connected sims (${e.length}):
5
5
  `);for(let t of e){let r=t.lockedBy&&t.lockExpiresAt?`locked by ${ln(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=an(t.url);if(i)console.log(` loaded: ${i}`);else if(t.url){let a=t.url.length>96?`${t.url.slice(0,93)}\u2026`:t.url;console.log(` url: ${a}`)}else t.origin&&console.log(` origin: ${t.origin}`);let s=cn(t);s&&console.log(` engine: ${s}`),t.title&&console.log(` title: ${t.title}`),t.visibilityState&&t.visibilityState!=="visible"&&console.log(` visibility: ${t.visibilityState}`),console.log(` connected: ${lt(Date.now()-t.connectedAt)}`),t.lastActiveAt&&t.lastActiveAt>0&&console.log(` last active: ${lt(Date.now()-t.lastActiveAt)}`)}}function cn(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)":me()?`dev \u2014 local build (:${t||"?"})`:`runtime \u2014 published engine (:${t||"?"})`}catch{return null}}function lt(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 Xn(e,n,t,r={}){let o=r.attempts??30,i=r.intervalMs??500;for(let s=0;s<o;s++){let a=P(e,{commandTimeoutMs:n});try{let d=(await a.listSims()).find(t);if(d)return d}catch{}finally{a.close()}await y(i)}return null}async function dn(e,n,t,r,o={}){let i=o.attempts??30,s=o.intervalMs??500,a=!!r&&Number.isInteger(r)&&r>1,l=()=>a&&r!==void 0&&!be(r);for(let d=0;d<i;d++){if(l())return{kind:"host-exit"};let c=P(e,{commandTimeoutMs:n});try{let g=(await c.listSims()).find(t);if(g)return l()?{kind:"host-exit"}:{kind:"match",sim:g}}catch{}finally{c.close()}if(l())return{kind:"host-exit"};await y(s)}return{kind:"timeout"}}async function un(e,n,t,r={}){let o=r.attempts??20,i=r.intervalMs??250;for(let s=0;s<o;s++){let a=P(e,{commandTimeoutMs:n});try{let d=(await a.listSims()).find(c=>c.id===t);if(!d||d.readyState!=="open")return!0}catch{return!0}finally{a.close()}await y(i)}return!1}function mn(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 pn(e){if(!e.connectAckFile)return!1;try{return T.writeFileSync(e.connectAckFile,`${JSON.stringify({connectedAt:Date.now(),pid:e.pid??null})}
6
6
  `,{flag:"w"}),!0}catch{return!1}}function ct(e){if(e.diagnosticLogPath)try{let n=T.readFileSync(e.diagnosticLogPath,"utf8").trim();if(!n){console.error(` ${C.basename(e.diagnosticLogPath)} was empty`);return}let t=n.length>4e3?`\u2026
7
7
  ${n.slice(-4e3)}`:n;console.error(` ${C.basename(e.diagnosticLogPath)}:`),console.error(t)}catch(n){let t=n instanceof Error?n.message:String(n);console.error(` failed to read ${C.basename(e.diagnosticLogPath)}: ${t}`)}}function be(e){try{return process.kill(e,0),!0}catch(n){return n?.code==="EPERM"}}async function fn(e,n=2500){let t=Date.now()+n;for(;Date.now()<t;){if(!be(e))return!0;await y(100)}return!be(e)}function gn(e){let n=tt("ps",["-o","command=","-p",String(e)],{encoding:"utf8",stdio:["ignore","pipe","pipe"]}),t=tt("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 he(e,n){let t=new Set(n),r=new Set;for(let o of e){if(!t.has(o.id))continue;let i=mn(o);i&&r.add(i)}for(let o of r){if(!gn(o)){console.log(` skipped pid ${o}: not a rnx playwright host (stale sim record)`);continue}try{process.kill(o,"SIGTERM")}catch{}if(await fn(o)){console.log(` closed playwright host process ${o}`);continue}try{process.kill(o,"SIGKILL"),console.log(` force-closed playwright host process ${o}`)}catch{}}}function Y(e,n){if(n){let o=n.trim(),i=e.find(s=>s.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 dt(e,n,t){let[r,o]=[e,n.url].map(i=>{if(!i)return i;try{let s=new URL(i);if(s.searchParams.has("sootsimConfig"))return s.searchParams.set("sootsimConfig","[redacted]"),s.toString()}catch{}return i});console.log(` ${t==="current sim"?"loaded":"opened"}: ${r} [${t}]`),console.log(` current sim: ${n.id}`),console.log(JSON.stringify({simId:n.id,url:o},null,2))}async function ut(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 hn(e,n,{stableMs:150,maxMs:400});let o=P(e,{commandTimeoutMs:3e3,simId:n,cliLabel:"open --describe",simIdSource:"flag"});try{let i=null;try{i=await o.send({type:"evaluate",code:Be})}catch{}if(i&&!(i.flag===!0&&!i.loadingText&&Ae(i))){let c=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: ${c} (nodes: ${i.nodes}, targets: ${i.targets})`),console.log(" before interacting, run: rnxsim wait ready --max-ms 120000")}let a=`(async () => {