rnxsim 0.1.312 → 0.1.314

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/LICENSE +11 -10
  2. package/README.md +5 -0
  3. package/cli/app-config.ts +65 -0
  4. package/cli/app-fonts.ts +408 -0
  5. package/cli/app-project.ts +231 -0
  6. package/cli/app-splash.ts +185 -0
  7. package/cli/app-state-reset.ts +24 -0
  8. package/cli/auth.ts +155 -0
  9. package/cli/bin.ts +594 -0
  10. package/cli/bridge-diagnostics.ts +226 -0
  11. package/cli/bridge-flow-runner.ts +2830 -0
  12. package/cli/browser-evals.ts +96 -0
  13. package/cli/commands/agent-wrapper.ts +986 -0
  14. package/cli/commands/agent.ts +423 -0
  15. package/cli/commands/app-fonts.ts +98 -0
  16. package/cli/commands/assert.ts +541 -0
  17. package/cli/commands/auth.ts +59 -0
  18. package/cli/commands/camera.ts +266 -0
  19. package/cli/commands/cleanup.ts +169 -0
  20. package/cli/commands/compat.ts +87 -0
  21. package/cli/commands/config.ts +32 -0
  22. package/cli/commands/control.ts +2142 -0
  23. package/cli/commands/cpu-profile.ts +269 -0
  24. package/cli/commands/daemon-mac-app.ts +169 -0
  25. package/cli/commands/daemon.ts +874 -0
  26. package/cli/commands/debug.ts +719 -0
  27. package/cli/commands/desktop.ts +39 -0
  28. package/cli/commands/detect.ts +197 -0
  29. package/cli/commands/detox.ts +385 -0
  30. package/cli/commands/device.ts +133 -0
  31. package/cli/commands/diagnose.ts +589 -0
  32. package/cli/commands/electron.ts +95 -0
  33. package/cli/commands/film.ts +379 -0
  34. package/cli/commands/flow.ts +1124 -0
  35. package/cli/commands/inspect/actions.ts +622 -0
  36. package/cli/commands/inspect/core.ts +2405 -0
  37. package/cli/commands/inspect/count.ts +17 -0
  38. package/cli/commands/inspect/describe.ts +192 -0
  39. package/cli/commands/inspect/env.ts +23 -0
  40. package/cli/commands/inspect/find.ts +171 -0
  41. package/cli/commands/inspect/get-layout.ts +39 -0
  42. package/cli/commands/inspect/keyboard.ts +52 -0
  43. package/cli/commands/inspect/list.ts +58 -0
  44. package/cli/commands/inspect/memory.ts +215 -0
  45. package/cli/commands/inspect/redaction.ts +39 -0
  46. package/cli/commands/inspect/resolve-target.ts +82 -0
  47. package/cli/commands/inspect/screens.ts +78 -0
  48. package/cli/commands/inspect/settle.ts +22 -0
  49. package/cli/commands/inspect/settling.ts +158 -0
  50. package/cli/commands/inspect/shared.ts +353 -0
  51. package/cli/commands/inspect/sleep.ts +14 -0
  52. package/cli/commands/inspect/tree.ts +32 -0
  53. package/cli/commands/inspect/url.ts +17 -0
  54. package/cli/commands/inspect/wait-event.ts +210 -0
  55. package/cli/commands/inspect/wait-idle.ts +24 -0
  56. package/cli/commands/inspect/wait-ready.ts +74 -0
  57. package/cli/commands/inspect/wait-selector.ts +54 -0
  58. package/cli/commands/inspect/wait.ts +31 -0
  59. package/cli/commands/inspect.ts +4519 -0
  60. package/cli/commands/install-desktop.ts +351 -0
  61. package/cli/commands/login.ts +331 -0
  62. package/cli/commands/logout.ts +31 -0
  63. package/cli/commands/maestro-generate.ts +361 -0
  64. package/cli/commands/maestro.ts +453 -0
  65. package/cli/commands/mode.ts +57 -0
  66. package/cli/commands/no-bridge-hint.ts +80 -0
  67. package/cli/commands/perf.ts +66 -0
  68. package/cli/commands/permissions.ts +203 -0
  69. package/cli/commands/profile.ts +108 -0
  70. package/cli/commands/react.ts +353 -0
  71. package/cli/commands/record.ts +1434 -0
  72. package/cli/commands/report-issue.ts +305 -0
  73. package/cli/commands/reset.ts +85 -0
  74. package/cli/commands/runtime.ts +351 -0
  75. package/cli/commands/screenshot-command.ts +106 -0
  76. package/cli/commands/screenshot-layers.ts +143 -0
  77. package/cli/commands/screenshot-mode.ts +37 -0
  78. package/cli/commands/screenshot.ts +488 -0
  79. package/cli/commands/screenshots-capture.ts +607 -0
  80. package/cli/commands/screenshots.ts +127 -0
  81. package/cli/commands/serve.ts +168 -0
  82. package/cli/commands/setup.ts +545 -0
  83. package/cli/commands/shell-boolean-mode.ts +81 -0
  84. package/cli/commands/skills.ts +467 -0
  85. package/cli/commands/slides.ts +361 -0
  86. package/cli/commands/state.ts +87 -0
  87. package/cli/commands/storage.ts +58 -0
  88. package/cli/commands/telemetry.ts +54 -0
  89. package/cli/commands/three-mode.ts +763 -0
  90. package/cli/commands/timeline.ts +122 -0
  91. package/cli/commands/upgrade.ts +208 -0
  92. package/cli/commands/upload.ts +1225 -0
  93. package/cli/commands/version.ts +54 -0
  94. package/cli/commands/what-happened.ts +327 -0
  95. package/cli/current-sim.ts +204 -0
  96. package/cli/desktop-companion.ts +300 -0
  97. package/cli/drivers/electron.ts +70 -0
  98. package/cli/drivers/index.ts +20 -0
  99. package/cli/drivers/playwright-provisioning.ts +180 -0
  100. package/cli/drivers/playwright.ts +698 -0
  101. package/cli/drivers/registry.ts +65 -0
  102. package/cli/drivers/types.ts +102 -0
  103. package/cli/flow-file.ts +142 -0
  104. package/cli/flow-live-status.ts +120 -0
  105. package/cli/flow-session.ts +187 -0
  106. package/cli/help.ts +80 -0
  107. package/cli/hidden-runtime-alias.ts +19 -0
  108. package/cli/hints.ts +216 -0
  109. package/cli/inspect-notice-state.ts +114 -0
  110. package/cli/maestro-js.ts +334 -0
  111. package/cli/open-url.ts +8 -0
  112. package/cli/parent-pid.ts +204 -0
  113. package/cli/parse-args.ts +211 -0
  114. package/cli/prompt.ts +51 -0
  115. package/cli/recording-access.ts +107 -0
  116. package/cli/registry.ts +1 -0
  117. package/cli/resolve-assets.ts +63 -0
  118. package/cli/run-registry.ts +226 -0
  119. package/cli/runtime-notes.ts +66 -0
  120. package/cli/runtime-summary.ts +25 -0
  121. package/cli/setup-repository.ts +187 -0
  122. package/cli/telemetry.ts +187 -0
  123. package/cli/ws-bridge.ts +798 -0
  124. package/dist-cli/bin.js +5 -5
  125. package/dist-cli/chunks/{agent-NXVDDCFG.js → agent-7YBDCYMA.js} +2 -2
  126. package/dist-cli/chunks/{agent-wrapper-DXJCFKL3.js → agent-wrapper-2GHFBHCR.js} +2 -2
  127. package/dist-cli/chunks/{app-fonts-W5AJZQJF.js → app-fonts-RSNVPQSU.js} +2 -2
  128. package/dist-cli/chunks/{assert-3LOJEUDT.js → assert-XCMX3XJX.js} +2 -2
  129. package/dist-cli/chunks/{auth-MFTYYPNX.js → auth-B442HRAX.js} +2 -2
  130. package/dist-cli/chunks/{beta-GT3NN67B.js → beta-XJ55JK3M.js} +2 -2
  131. package/dist-cli/chunks/camera-UGYSSLIK.js +33 -0
  132. package/dist-cli/chunks/{chunk-EUAVBTKU.js → chunk-277AEQZX.js} +2 -2
  133. package/dist-cli/chunks/{chunk-5OX7ANRA.js → chunk-2JNSK774.js} +2 -2
  134. package/dist-cli/chunks/{chunk-L24Z3CRS.js → chunk-32WOTSTR.js} +3 -3
  135. package/dist-cli/chunks/{chunk-BKNOLP3J.js → chunk-3S753SNQ.js} +2 -2
  136. package/dist-cli/chunks/{chunk-N6A3SXMR.js → chunk-46ZOLOYA.js} +2 -2
  137. package/dist-cli/chunks/{chunk-GZDHEONW.js → chunk-5L7ELDQL.js} +8 -9
  138. package/dist-cli/chunks/{chunk-YZRFF4YA.js → chunk-7OOPFSQS.js} +2 -2
  139. package/dist-cli/chunks/{chunk-TBMVBI26.js → chunk-7SV3RPRW.js} +2 -2
  140. package/dist-cli/chunks/{chunk-JG6S4RSH.js → chunk-7ZC35MOU.js} +1 -1
  141. package/dist-cli/chunks/chunk-AMG5E6CC.js +9 -0
  142. package/dist-cli/chunks/{chunk-MENV7VLD.js → chunk-APWNH3A4.js} +1 -1
  143. package/dist-cli/chunks/chunk-B57XUKY3.js +4 -0
  144. package/dist-cli/chunks/{chunk-3D57IBPC.js → chunk-C2NL26TD.js} +1 -1
  145. package/dist-cli/chunks/chunk-D2FNUWAB.js +15 -0
  146. package/dist-cli/chunks/{chunk-4BXCZHAU.js → chunk-E5T4XSJ3.js} +2 -2
  147. package/dist-cli/chunks/{chunk-FTC4M26Q.js → chunk-EAC34EQS.js} +1 -1
  148. package/dist-cli/chunks/{chunk-NPVGMH2N.js → chunk-EG32ML36.js} +2 -2
  149. package/dist-cli/chunks/{chunk-DM6KUAOZ.js → chunk-FXUAC6D5.js} +1 -1
  150. package/dist-cli/chunks/chunk-GQSL4USA.js +6 -0
  151. package/dist-cli/chunks/{chunk-U4PN6JOT.js → chunk-HAXW27SS.js} +2 -2
  152. package/dist-cli/chunks/{chunk-ZMZTHU7V.js → chunk-IZAHPAN6.js} +1 -1
  153. package/dist-cli/chunks/{chunk-SI3BUTQR.js → chunk-J62KM5TB.js} +2 -2
  154. package/dist-cli/chunks/{chunk-Y2FSZSO7.js → chunk-JEMCD5E4.js} +1 -1
  155. package/dist-cli/chunks/{chunk-T4FT6CM7.js → chunk-JZS3Q37N.js} +2 -2
  156. package/dist-cli/chunks/{chunk-2SA2VTGP.js → chunk-KR4JON7D.js} +1 -1
  157. package/dist-cli/chunks/chunk-KWCYKQIQ.js +4 -0
  158. package/dist-cli/chunks/{chunk-HG7ORJMT.js → chunk-MCWPL644.js} +2 -2
  159. package/dist-cli/chunks/chunk-MJYK3N2I.js +4 -0
  160. package/dist-cli/chunks/{chunk-YRV7HR3P.js → chunk-O6TRIZNS.js} +2 -2
  161. package/dist-cli/chunks/{chunk-WBT32R6N.js → chunk-P7XL2E73.js} +3 -3
  162. package/dist-cli/chunks/{chunk-MNGQ42QE.js → chunk-PFQTUKQ4.js} +62 -87
  163. package/dist-cli/chunks/{chunk-Q3RGLERJ.js → chunk-PG5RZCTN.js} +2 -2
  164. package/dist-cli/chunks/{chunk-OR3XTXEB.js → chunk-QGRI2Z4M.js} +2 -2
  165. package/dist-cli/chunks/chunk-QLXXE7GE.js +125 -0
  166. package/dist-cli/chunks/{chunk-KZW5WBGU.js → chunk-QOJJJWJE.js} +89 -133
  167. package/dist-cli/chunks/{chunk-BRIZOR4K.js → chunk-RWZY5427.js} +2 -2
  168. package/dist-cli/chunks/{chunk-TYE3WBSR.js → chunk-RZKU2K3J.js} +2 -2
  169. package/dist-cli/chunks/{chunk-7VWOSTD5.js → chunk-SBV4IK4H.js} +1 -1
  170. package/dist-cli/chunks/{chunk-FWYYCUAZ.js → chunk-SGMVFFMK.js} +1 -1
  171. package/dist-cli/chunks/{chunk-LDQ7HVCW.js → chunk-TAX4UT2N.js} +1 -1
  172. package/dist-cli/chunks/{chunk-G74YIEVS.js → chunk-TUOFAWXT.js} +1 -1
  173. package/dist-cli/chunks/{chunk-274UD5FD.js → chunk-UHZLOHGP.js} +1 -1
  174. package/dist-cli/chunks/{chunk-37I5AIS7.js → chunk-VC7V76U3.js} +1 -1
  175. package/dist-cli/chunks/{chunk-B7KMNHX7.js → chunk-VQVMLW4U.js} +1 -1
  176. package/dist-cli/chunks/{chunk-VZBLFMYC.js → chunk-VUKKYPZN.js} +2 -2
  177. package/dist-cli/chunks/{chunk-VUFRB347.js → chunk-WGGRJDRE.js} +1 -1
  178. package/dist-cli/chunks/chunk-X6H76EKP.js +15 -0
  179. package/dist-cli/chunks/chunk-XLZ5FNRT.js +27 -0
  180. package/dist-cli/chunks/{chunk-TCWQJILS.js → chunk-XULEACM4.js} +2 -2
  181. package/dist-cli/chunks/{chunk-XRGEEPJ5.js → chunk-YFSDM7AX.js} +4 -4
  182. package/dist-cli/chunks/chunk-YWI3UEVX.js +5 -0
  183. package/dist-cli/chunks/{chunk-TGQNQ6XT.js → chunk-ZBSJO4NB.js} +10 -9
  184. package/dist-cli/chunks/{cleanup-H3GWQB63.js → cleanup-EYLGCA6Z.js} +2 -2
  185. package/dist-cli/chunks/cli-version-LL2UGIHE.js +4 -0
  186. package/dist-cli/chunks/{compat-5WG2XJV7.js → compat-TLJYHB4E.js} +2 -2
  187. package/dist-cli/chunks/{config-ANMBVVLL.js → config-4JWUOEXK.js} +2 -2
  188. package/dist-cli/chunks/{control-NSGNQFJG.js → control-HPAOYF4N.js} +2 -2
  189. package/dist-cli/chunks/daemon-OYLASXLE.js +4 -0
  190. package/dist-cli/chunks/{debug-CHV2O2KF.js → debug-4BKXF6KI.js} +5 -5
  191. package/dist-cli/chunks/{desktop-FIPIK4CH.js → desktop-ZQ6ZD2S6.js} +3 -3
  192. package/dist-cli/chunks/{detox-CVUJP7K6.js → detox-WNPASSS3.js} +2 -2
  193. package/dist-cli/chunks/{device-6WCUM4W6.js → device-BXLXVG3V.js} +2 -2
  194. package/dist-cli/chunks/{diagnose-I6EMWEII.js → diagnose-3QX7W5RW.js} +2 -2
  195. package/dist-cli/chunks/{disk-cleanup-P27CYFTN.js → disk-cleanup-KSWKI7WB.js} +2 -2
  196. package/dist-cli/chunks/drivers-EPIEFF7P.js +4 -0
  197. package/dist-cli/chunks/{film-UOQFFYNF.js → film-GDMR33VO.js} +3 -3
  198. package/dist-cli/chunks/flow-NKMPBYCJ.js +4 -0
  199. package/dist-cli/chunks/help-OGCBHOKA.js +4 -0
  200. package/dist-cli/chunks/{hidden-runtime-alias-NJYFSJKZ.js → hidden-runtime-alias-S2GTDX3T.js} +2 -2
  201. package/dist-cli/chunks/home-paths-QRCDLTTV.js +4 -0
  202. package/dist-cli/chunks/inspect-FUYMOZPY.js +4 -0
  203. package/dist-cli/chunks/install-desktop-FR6YKW7Y.js +4 -0
  204. package/dist-cli/chunks/{login-BPFQENCX.js → login-IJAPUZHI.js} +4 -4
  205. package/dist-cli/chunks/{logout-5A45ZGY7.js → logout-QMXDFU2X.js} +2 -2
  206. package/dist-cli/chunks/{maestro-E4CSDMLM.js → maestro-ZXU3YCVX.js} +3 -3
  207. package/dist-cli/chunks/{maestro-generate-CKMOJQX2.js → maestro-generate-PYB5QY7K.js} +3 -3
  208. package/dist-cli/chunks/{mode-SZYNMIQF.js → mode-WGUCL5FZ.js} +2 -2
  209. package/dist-cli/chunks/{optional-demo-registry-FRH4SFVG.js → optional-demo-registry-WH2O6H36.js} +2 -2
  210. package/dist-cli/chunks/{perf-VXU2XJAX.js → perf-TOD3UFAH.js} +2 -2
  211. package/dist-cli/chunks/{permissions-IRIAOVND.js → permissions-I5BRJGTB.js} +2 -2
  212. package/dist-cli/chunks/{record-RY7FPLLH.js → record-ZYL2FYSK.js} +3 -3
  213. package/dist-cli/chunks/{report-issue-KNWXOE53.js → report-issue-TAI6DYZO.js} +2 -2
  214. package/dist-cli/chunks/reset-7YGYKQPQ.js +4 -0
  215. package/dist-cli/chunks/runtime-B4JO6QRI.js +4 -0
  216. package/dist-cli/chunks/{screenshot-command-QLALR6GY.js → screenshot-command-7ANLODZY.js} +7 -7
  217. package/dist-cli/chunks/{screenshot-layers-OQFMFVRL.js → screenshot-layers-A7FYXSVU.js} +3 -3
  218. package/dist-cli/chunks/{screenshots-capture-6TSRJFGB.js → screenshots-capture-WT2ZY6CB.js} +2 -2
  219. package/dist-cli/chunks/serve-TG5WKA4V.js +44 -0
  220. package/dist-cli/chunks/{setup-7V6UKX4U.js → setup-PCWLD22V.js} +2 -2
  221. package/dist-cli/chunks/{skills-QQBOSETH.js → skills-27ZHWCQS.js} +2 -2
  222. package/dist-cli/chunks/state-ZQVY46ZO.js +14 -0
  223. package/dist-cli/chunks/{storage-TAPMKK5O.js → storage-OYC57C4X.js} +6 -6
  224. package/dist-cli/chunks/store-32HSPZHI.js +4 -0
  225. package/dist-cli/chunks/telemetry-PBJR7XHR.js +4 -0
  226. package/dist-cli/chunks/{timeline-IAM564NA.js → timeline-RAJJFQT6.js} +2 -2
  227. package/dist-cli/chunks/{upgrade-CD55QXIX.js → upgrade-OEFNZRIP.js} +2 -2
  228. package/dist-cli/chunks/upload-LQJITLKK.js +4 -0
  229. package/dist-cli/chunks/version-NIXTY6PL.js +6 -0
  230. package/dist-cli/chunks/{web-6NW6YLPI.js → web-XBUTBVGR.js} +2 -2
  231. package/dist-cli/chunks/{what-happened-CZZDOOVG.js → what-happened-YNHRFUDX.js} +3 -3
  232. package/dist-lib/agent-daemon-client.cjs +1 -1
  233. package/dist-lib/agent-events.cjs +1 -1
  234. package/dist-lib/agent-identity.cjs +1 -1
  235. package/dist-lib/agent-sessions.cjs +1 -1
  236. package/dist-lib/attached-projects.cjs +1 -1
  237. package/dist-lib/auth/shared-session.cjs +1 -1
  238. package/dist-lib/backend-origin.cjs +1 -1
  239. package/dist-lib/beta.cjs +1 -1
  240. package/dist-lib/beta.mjs +1 -1
  241. package/dist-lib/bridge-constants.cjs +1 -1
  242. package/dist-lib/bridge-contract.cjs +20 -0
  243. package/dist-lib/cli-constants.cjs +1 -1
  244. package/dist-lib/config.cjs +1 -1
  245. package/dist-lib/detox/index.cjs +1 -1
  246. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  247. package/dist-lib/home-paths.cjs +67 -28
  248. package/dist-lib/host/bridge-host.cjs +140 -12
  249. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  250. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  251. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  252. package/dist-lib/host/websocket-proxy.cjs +1 -1
  253. package/dist-lib/index.cjs +2815 -40
  254. package/dist-lib/jump-to-source-babel.cjs +1 -1
  255. package/dist-lib/menu.cjs +1 -1
  256. package/dist-lib/menu.mjs +1 -1
  257. package/dist-lib/metro.cjs +1 -1
  258. package/dist-lib/profiles.cjs +1 -1
  259. package/dist-lib/public-brand.cjs +1 -1
  260. package/dist-lib/render-mode.cjs +1 -1
  261. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  262. package/dist-lib/sdk.cjs +2549 -2061
  263. package/dist-lib/sdk.mjs +2543 -2061
  264. package/dist-lib/skills.cjs +493 -282
  265. package/dist-lib/vite.cjs +1 -1
  266. package/package.json +8 -2
  267. package/src/bridge-constants.ts +3 -4
  268. package/src/bridge-contract.ts +251 -0
  269. package/src/connect.ts +83 -0
  270. package/src/disk-cleanup.ts +30 -0
  271. package/src/home-paths.ts +81 -38
  272. package/src/host/bridge-host.ts +134 -6
  273. package/src/index.ts +27 -1
  274. package/src/sdk.ts +8 -0
  275. package/src/sim-client.ts +660 -0
  276. package/dist-cli/chunks/camera-6EAK7CUL.js +0 -22
  277. package/dist-cli/chunks/chunk-33FKQH54.js +0 -5
  278. package/dist-cli/chunks/chunk-66NF2KNG.js +0 -9
  279. package/dist-cli/chunks/chunk-6UKJB2VC.js +0 -125
  280. package/dist-cli/chunks/chunk-7INBNA3U.js +0 -26
  281. package/dist-cli/chunks/chunk-AY47WKS5.js +0 -23
  282. package/dist-cli/chunks/chunk-HMDOLCKG.js +0 -15
  283. package/dist-cli/chunks/chunk-KR5BGMBZ.js +0 -4
  284. package/dist-cli/chunks/chunk-NLEMQ4PY.js +0 -6
  285. package/dist-cli/chunks/chunk-PEPUGJVI.js +0 -4
  286. package/dist-cli/chunks/cli-version-3QPAJ3LC.js +0 -4
  287. package/dist-cli/chunks/daemon-JHF4BJ3U.js +0 -4
  288. package/dist-cli/chunks/drivers-HQ6AKZT6.js +0 -4
  289. package/dist-cli/chunks/flow-N27MRRNL.js +0 -4
  290. package/dist-cli/chunks/help-SF6FEGEE.js +0 -4
  291. package/dist-cli/chunks/home-paths-XOV44JEF.js +0 -4
  292. package/dist-cli/chunks/inspect-CWFRFQTY.js +0 -4
  293. package/dist-cli/chunks/install-desktop-KLQDNXZQ.js +0 -4
  294. package/dist-cli/chunks/runtime-AR3GJTGK.js +0 -4
  295. package/dist-cli/chunks/serve-F54J2NQO.js +0 -44
  296. package/dist-cli/chunks/store-7NX3S43H.js +0 -4
  297. package/dist-cli/chunks/telemetry-E7BUK6RA.js +0 -4
  298. package/dist-cli/chunks/upload-GZ6LIMYM.js +0 -4
  299. package/dist-cli/chunks/version-3DYJ5AS3.js +0 -6
@@ -0,0 +1,351 @@
1
+ // sootsim runtime — manage engine runtimes under ~/.sootsim/runtimes/.
2
+ //
3
+ // runtimes are versioned tarballs hosted behind the configured CDN origin
4
+ // (default https://contrast.dev/runtimes/). each install:
5
+ // 1. fetches manifest.json to resolve a version (or confirm the literal)
6
+ // 2. downloads sootsim-runtime-<version>.tar.gz to an operation temp directory
7
+ // 3. verifies sha256
8
+ // 4. extracts to ~/.sootsim/runtimes/<version>/
9
+ // 5. (optionally) sets it active + tells the daemon to hot-swap
10
+ //
11
+ // the daemon-side state (active runtime) is mirrored in ~/.sootsim/runtimes/active
12
+ // so the CLI can change it even when the daemon isn't running.
13
+
14
+ import fs from 'fs'
15
+ import { compareSemver } from '@contrast/runtime-delivery'
16
+ import { WebSocket } from 'ws'
17
+ import {
18
+ ensureSootsimHome,
19
+ readActiveRuntime,
20
+ readLiveRuntimeVersions,
21
+ runtimeDir,
22
+ writeActiveRuntime,
23
+ } from '../../src/home-paths'
24
+ import {
25
+ DEFAULT_RUNTIME_CDN_ORIGIN,
26
+ SOOTSIM_CHANGELOG_URL,
27
+ sootsimRuntime,
28
+ } from '../../src/runtime-delivery'
29
+ import { resolveSootSimAppConfig } from '../app-config'
30
+ import { fetchRuntimeReleaseNotes, formatRuntimeReleaseNotes } from '../runtime-notes'
31
+ import { resolveDefaultBridgePort } from '../ws-bridge'
32
+
33
+ interface RuntimeOptions {
34
+ channel?: string
35
+ }
36
+
37
+ export async function runRuntime(args: string[], opts: RuntimeOptions = {}) {
38
+ const [sub, ...rest] = args
39
+ if (!sub || sub === '--help' || sub === '-h') {
40
+ printHelp()
41
+ return
42
+ }
43
+ switch (sub) {
44
+ case 'install':
45
+ return runtimeInstall(rest, opts)
46
+ case 'list':
47
+ case 'ls':
48
+ return runtimeList(rest)
49
+ case 'use':
50
+ return runtimeUse(rest)
51
+ case 'remove':
52
+ case 'rm':
53
+ return runtimeRemove(rest)
54
+ case 'which':
55
+ case 'active':
56
+ return runtimeWhich()
57
+ case 'notes':
58
+ case 'changelog':
59
+ return runtimeNotes(rest)
60
+ default:
61
+ console.error(` unknown runtime subcommand: ${sub}`)
62
+ printHelp()
63
+ process.exit(1)
64
+ }
65
+ }
66
+
67
+ function printHelp() {
68
+ console.log(`
69
+ rnxsim runtime — manage engine runtimes under ~/.sootsim/runtimes/
70
+
71
+ usage:
72
+ rnxsim runtime install [version] install a version (default: channel latest)
73
+ rnxsim runtime list show installed + available versions
74
+ rnxsim runtime use <version> switch active runtime
75
+ rnxsim runtime remove <version> delete an installed runtime
76
+ rnxsim runtime which print active runtime version
77
+ rnxsim runtime notes print repo or default engine release notes
78
+
79
+ flags:
80
+ --channel <name> channel to resolve 'latest' from (default: stable)
81
+ --force reinstall even if the version is already on disk
82
+ --set-active=false do not switch active runtime after install
83
+
84
+ environment:
85
+ SOOTSIM_CDN_ORIGIN override the CDN base URL (default: ${DEFAULT_RUNTIME_CDN_ORIGIN})
86
+
87
+ examples:
88
+ rnxsim runtime install
89
+ rnxsim runtime install 1.2.3
90
+ rnxsim runtime install --channel beta
91
+ rnxsim runtime use 1.2.3
92
+ rnxsim runtime notes
93
+ `)
94
+ }
95
+
96
+ // --- install --------------------------------------------------------------
97
+
98
+ async function runtimeInstall(args: string[], opts: RuntimeOptions) {
99
+ const { version: versionArg, flags } = parseVersionAndFlags(args)
100
+ const channel = flags.channel ?? opts.channel ?? 'stable'
101
+ const force = flags.force === true
102
+ const setActive = flags.setActive !== false
103
+
104
+ ensureSootsimHome()
105
+
106
+ console.log(`rnxsim runtime install`)
107
+ console.log(` cdn: ${sootsimRuntime.resolveCdnOrigin()}`)
108
+
109
+ try {
110
+ const result = await sootsimRuntime.install({
111
+ version: versionArg,
112
+ channel,
113
+ force,
114
+ setActive,
115
+ protectVersions: readLiveRuntimeVersions(),
116
+ })
117
+ console.log(` version: ${result.version} (channel: ${result.channel})`)
118
+ if (result.installed) {
119
+ console.log(` installed ${result.version}`)
120
+ } else {
121
+ console.log(` already installed at ${result.runtimeDir}`)
122
+ }
123
+ if (setActive) await activateVersion(result.version)
124
+ return result
125
+ } catch (err) {
126
+ console.error(` ${describeError(err)}`)
127
+ process.exit(1)
128
+ }
129
+ }
130
+
131
+ async function activateVersion(version: string) {
132
+ writeActiveRuntime(version)
133
+ console.log(` active: ${version}`)
134
+ // if a daemon is running, tell it to hot-swap its http root. the
135
+ // daemon re-reads the active runtime per-request anyway, so this is
136
+ // mostly a nudge — but it also broadcasts runtime:changed so any
137
+ // connected electron webContents can reload. silent failure is fine;
138
+ // the next daemon start picks up the active file from disk.
139
+ const notified = await tellDaemonRuntimeChanged(version)
140
+ if (!notified) {
141
+ console.log(
142
+ ` (no daemon running — next sootsim/electron launch will pick up ${version})`,
143
+ )
144
+ }
145
+ }
146
+
147
+ /** notify a running daemon that the active runtime changed so it hot-swaps
148
+ * its http root + pushes a runtime:changed message to every connected
149
+ * browser (electron). uses a raw WebSocket to skip the bridge's
150
+ * primary-browser resolution — this message is daemon-only, not routed
151
+ * to a browser. returns true when the daemon acknowledged; false when
152
+ * no fresh daemon was reachable (caller logs accordingly).
153
+ *
154
+ * skips the ws probe entirely when the lockfile isn't fresh — the
155
+ * default-port fallback in resolveDefaultBridgePort() would otherwise
156
+ * eat a full handshakeTimeout on every `runtime use` when no daemon
157
+ * is running. */
158
+ async function tellDaemonRuntimeChanged(version: string): Promise<boolean> {
159
+ const { isDaemonLockfileFresh, readDaemonLockfile } =
160
+ await import('../../src/home-paths')
161
+ const lock = readDaemonLockfile()
162
+ if (!isDaemonLockfileFresh(lock)) return false
163
+ const port = resolveDefaultBridgePort()
164
+ return new Promise<boolean>((resolve) => {
165
+ let settled = false
166
+ let success = false
167
+ const finish = (ok: boolean) => {
168
+ if (settled) return
169
+ settled = true
170
+ resolve(ok)
171
+ }
172
+ const ws = new WebSocket(`ws://127.0.0.1:${port}`, {
173
+ handshakeTimeout: 800,
174
+ })
175
+ const timer = setTimeout(() => {
176
+ try {
177
+ ws.close()
178
+ } catch {}
179
+ finish(success)
180
+ }, 1500)
181
+ ws.on('open', () => {
182
+ try {
183
+ ws.send(JSON.stringify({ type: 'runtime:use', version, id: 0 }))
184
+ success = true
185
+ } catch {}
186
+ // give the daemon a beat to process, then close
187
+ setTimeout(() => {
188
+ try {
189
+ ws.close()
190
+ } catch {}
191
+ }, 100)
192
+ })
193
+ ws.on('close', () => {
194
+ clearTimeout(timer)
195
+ finish(success)
196
+ })
197
+ ws.on('error', () => {
198
+ clearTimeout(timer)
199
+ finish(false)
200
+ })
201
+ })
202
+ }
203
+
204
+ // --- list -----------------------------------------------------------------
205
+
206
+ async function runtimeList(_args: string[]) {
207
+ ensureSootsimHome()
208
+ const installed = sootsimRuntime.listInstalled()
209
+ const active = readActiveRuntime()
210
+ console.log(`installed:`)
211
+ if (installed.length === 0) {
212
+ console.log(` (none)`)
213
+ } else {
214
+ for (const v of installed) {
215
+ const marker = v === active ? '*' : ' '
216
+ console.log(` ${marker} ${v}`)
217
+ }
218
+ }
219
+ try {
220
+ const manifest = await sootsimRuntime.fetchManifest()
221
+ console.log(`available (latest per channel):`)
222
+ for (const [name, ch] of Object.entries(manifest.channels)) {
223
+ console.log(` ${name.padEnd(8)} ${ch.latest}`)
224
+ }
225
+ const hosted = Object.keys(manifest.versions).sort(compareSemver).reverse()
226
+ if (hosted.length > 1) {
227
+ console.log(`hosted versions:`)
228
+ for (const v of hosted) console.log(` ${v}`)
229
+ }
230
+ } catch (err) {
231
+ console.log(`available: (could not fetch manifest: ${describeError(err)})`)
232
+ }
233
+ }
234
+
235
+ // --- use ------------------------------------------------------------------
236
+
237
+ async function runtimeUse(args: string[]) {
238
+ const version = args[0]
239
+ if (!version) {
240
+ console.error(` usage: rnxsim runtime use <version>`)
241
+ process.exit(1)
242
+ }
243
+ const installed = sootsimRuntime.listInstalled()
244
+ if (!installed.includes(version)) {
245
+ console.error(` version ${version} is not installed`)
246
+ console.error(` installed: ${installed.join(', ') || '(none)'}`)
247
+ console.error(` run \`rnxsim runtime install ${version}\` first`)
248
+ process.exit(1)
249
+ }
250
+ await activateVersion(version)
251
+ }
252
+
253
+ // --- remove ---------------------------------------------------------------
254
+
255
+ async function runtimeRemove(args: string[]) {
256
+ const version = args[0]
257
+ if (!version) {
258
+ console.error(` usage: rnxsim runtime remove <version>`)
259
+ process.exit(1)
260
+ }
261
+ const active = readActiveRuntime()
262
+ if (active === version) {
263
+ console.error(` cannot remove active runtime ${version}`)
264
+ console.error(
265
+ ` switch with \`rnxsim runtime use <other>\` first, or install another version`,
266
+ )
267
+ process.exit(1)
268
+ }
269
+ if (readLiveRuntimeVersions().includes(version)) {
270
+ console.error(` cannot remove runtime ${version} while a project is using it`)
271
+ process.exit(1)
272
+ }
273
+ const dir = runtimeDir(version)
274
+ if (!fs.existsSync(dir)) {
275
+ console.error(` ${version} is not installed`)
276
+ return
277
+ }
278
+ fs.rmSync(dir, { recursive: true, force: true })
279
+ console.log(` removed ${version}`)
280
+ }
281
+
282
+ // --- which ----------------------------------------------------------------
283
+
284
+ async function runtimeWhich() {
285
+ const active = readActiveRuntime()
286
+ if (!active) {
287
+ console.log(` no active runtime`)
288
+ return
289
+ }
290
+ console.log(active)
291
+ }
292
+
293
+ // --- notes ----------------------------------------------------------------
294
+
295
+ async function runtimeNotes(args: string[]) {
296
+ if (args.length > 0) {
297
+ console.error(` usage: rnxsim runtime notes`)
298
+ process.exit(1)
299
+ }
300
+ const { config } = await resolveSootSimAppConfig()
301
+ const version = config?.runtimeVersion ?? readActiveRuntime()
302
+ if (!version) {
303
+ console.log(` no active runtime`)
304
+ console.log(` release notes: ${SOOTSIM_CHANGELOG_URL}`)
305
+ return
306
+ }
307
+ console.log(formatRuntimeReleaseNotes(version, await fetchRuntimeReleaseNotes(version)))
308
+ }
309
+
310
+ // --- helpers --------------------------------------------------------------
311
+
312
+ interface InstallFlags {
313
+ channel?: string
314
+ force?: boolean
315
+ setActive?: boolean
316
+ }
317
+
318
+ function parseVersionAndFlags(args: string[]): {
319
+ version: string | null
320
+ flags: InstallFlags
321
+ } {
322
+ const flags: InstallFlags = {}
323
+ const positional: string[] = []
324
+ for (let i = 0; i < args.length; i++) {
325
+ const arg = args[i]
326
+ if (arg === '--channel' && i + 1 < args.length) {
327
+ flags.channel = args[i + 1]
328
+ i++
329
+ continue
330
+ }
331
+ if (arg.startsWith('--channel=')) {
332
+ flags.channel = arg.slice('--channel='.length)
333
+ continue
334
+ }
335
+ if (arg === '--force') {
336
+ flags.force = true
337
+ continue
338
+ }
339
+ if (arg === '--set-active=false' || arg === '--no-set-active') {
340
+ flags.setActive = false
341
+ continue
342
+ }
343
+ positional.push(arg)
344
+ }
345
+ return { version: positional[0] ?? null, flags }
346
+ }
347
+
348
+ function describeError(err: unknown): string {
349
+ if (err instanceof Error) return err.message
350
+ return String(err)
351
+ }
@@ -0,0 +1,106 @@
1
+ import { runScreenshot } from './screenshot'
2
+ import { runScreenshots } from './screenshots'
3
+ import { runSlides } from './slides'
4
+
5
+ interface ScreenshotCommandOptions {
6
+ port?: number
7
+ verbose?: boolean
8
+ }
9
+
10
+ const VALUE_FLAGS = new Set([
11
+ '--sim',
12
+ '--port',
13
+ '-p',
14
+ '--output',
15
+ '-o',
16
+ '--out',
17
+ '--plan',
18
+ '--app',
19
+ '--device',
20
+ '--org',
21
+ '--repo',
22
+ '--api-origin',
23
+ '--branch',
24
+ '--platform',
25
+ '--slides',
26
+ '--shell-origin',
27
+ '--timeout',
28
+ '--ids',
29
+ '--out-dir',
30
+ ])
31
+ const SUBCOMMANDS = new Set(['capture', 'slides', 'appstore', 'layers'])
32
+
33
+ function findSubcommand(args: string[]): { name: string; index: number } | null {
34
+ for (let i = 0; i < args.length; i++) {
35
+ const arg = args[i]
36
+ if (arg.startsWith('-')) {
37
+ if (VALUE_FLAGS.has(arg)) i++
38
+ continue
39
+ }
40
+ return SUBCOMMANDS.has(arg) ? { name: arg, index: i } : null
41
+ }
42
+ return null
43
+ }
44
+
45
+ function removeAt(args: string[], index: number): string[] {
46
+ return [...args.slice(0, index), ...args.slice(index + 1)]
47
+ }
48
+
49
+ function stripSimFlag(args: string[]): string[] {
50
+ const out: string[] = []
51
+ for (let i = 0; i < args.length; i++) {
52
+ if (args[i] === '--sim') {
53
+ i++
54
+ continue
55
+ }
56
+ out.push(args[i])
57
+ }
58
+ return out
59
+ }
60
+
61
+ export async function runScreenshotCommand(
62
+ args: string[],
63
+ opts: ScreenshotCommandOptions,
64
+ ): Promise<number | void> {
65
+ const sub = findSubcommand(args)
66
+ if (!sub || sub.name === 'capture') {
67
+ await runScreenshot(sub ? removeAt(args, sub.index) : args, opts)
68
+ return
69
+ }
70
+
71
+ if (sub.name === 'slides') {
72
+ await runSlides(removeAt(args, sub.index), opts)
73
+ return
74
+ }
75
+
76
+ if (sub.name === 'layers') {
77
+ const { runScreenshotLayers } = await import('./screenshot-layers')
78
+ await runScreenshotLayers(removeAt(args, sub.index), opts)
79
+ return
80
+ }
81
+
82
+ const appstoreArgs = removeAt(args, sub.index)
83
+ const nested = findSubcommand(appstoreArgs)
84
+ if (nested?.name === 'capture') {
85
+ // repo-only tooling: screenshots-capture statically imports playwright-core,
86
+ // which the published CLI never ships (it's external in build-cli.ts). a
87
+ // static import here would put that bare import at the top of THIS chunk,
88
+ // so every published `sootsim screenshot` printed ERR_MODULE_NOT_FOUND —
89
+ // keep it a runtime dynamic import that only evaluates when capture runs.
90
+ try {
91
+ const { runScreenshotsCapture } = await import('./screenshots-capture')
92
+ return await runScreenshotsCapture(
93
+ stripSimFlag(removeAt(appstoreArgs, nested.index)),
94
+ )
95
+ } catch (err) {
96
+ if (err instanceof Error && 'code' in err && err.code === 'ERR_MODULE_NOT_FOUND') {
97
+ console.error(
98
+ 'screenshot appstore capture is repo-only tooling — it needs the rnx workspace (playwright is not shipped with the published CLI).',
99
+ )
100
+ return 1
101
+ }
102
+ throw err
103
+ }
104
+ }
105
+ return runScreenshots(appstoreArgs, opts)
106
+ }
@@ -0,0 +1,143 @@
1
+ // sootsim screenshot layers — engine-rendered layer isolation export.
2
+ //
3
+ // captures one screen as a stack the 3d stage can pull apart: `full.png` is
4
+ // the normal frame, `base.png` re-renders the frame with the target subtrees
5
+ // hidden (content and glass blur behind them are real, not inpainted), and
6
+ // each `layer-<testID>.png` draws exactly that subtree alone on transparency
7
+ // at its on-screen position. `manifest.json` records device metrics and the
8
+ // logical rect of every layer.
9
+
10
+ import { mkdirSync, writeFileSync } from 'fs'
11
+ import { resolve } from 'path'
12
+ import { devices } from 'sootsim-engine/settings'
13
+ import { buildResolveRectEval, type SampleRect } from '../browser-evals'
14
+ import { callInBridge, createBridgeFromParsed, parseBridgeCliArgs } from '../ws-bridge'
15
+ import { inspectWaitReady, waitReadyReason } from './inspect/core'
16
+
17
+ interface ScreenshotLayersOptions {
18
+ port?: number
19
+ verbose?: boolean
20
+ }
21
+
22
+ export async function runScreenshotLayers(
23
+ args: string[],
24
+ opts: ScreenshotLayersOptions,
25
+ ): Promise<void> {
26
+ if (args.includes('--help') || args.includes('-h')) {
27
+ console.log(`
28
+ sootsim screenshot layers — export a screen as engine-isolated layers
29
+
30
+ usage:
31
+ sootsim screenshot layers --ids <testID,testID,...> [--out-dir <dir>]
32
+
33
+ options:
34
+ --ids <list> comma-separated testIDs of the subtrees to isolate
35
+ --out-dir <dir> output directory (default: /tmp/sootsim-layers)
36
+
37
+ output:
38
+ full.png the normal frame
39
+ base.png the frame re-rendered with every listed subtree hidden
40
+ layer-<id>.png each subtree rendered alone on a transparent frame
41
+ manifest.json device metrics + logical rect per layer
42
+ `)
43
+ process.exit(0)
44
+ }
45
+
46
+ const idsArg = args.find((_, i) => args[i - 1] === '--ids')
47
+ if (!idsArg) {
48
+ console.error(' --ids is required (comma-separated testIDs)')
49
+ process.exit(1)
50
+ }
51
+ const testIds = idsArg
52
+ .split(',')
53
+ .map((entry) => entry.trim())
54
+ .filter(Boolean)
55
+ if (testIds.length === 0) {
56
+ console.error(' --ids resolved to an empty list')
57
+ process.exit(1)
58
+ }
59
+ const outDirArg = args.find((_, i) => args[i - 1] === '--out-dir')
60
+ const outDir = resolve(process.cwd(), outDirArg ?? '/tmp/sootsim-layers')
61
+
62
+ const parsed = parseBridgeCliArgs(args, {
63
+ port: opts.port,
64
+ stripValueFlags: ['--ids', '--out-dir'],
65
+ })
66
+ const bridge = createBridgeFromParsed({ ...parsed, commandTimeoutMs: 45000 })
67
+ try {
68
+ const readiness = await inspectWaitReady(bridge, 90_000)
69
+ if (!readiness.ready) {
70
+ throw new Error(
71
+ `app not ready to capture after ${Math.round(readiness.elapsedMs / 1000)}s ` +
72
+ `(${waitReadyReason(readiness)})`,
73
+ )
74
+ }
75
+
76
+ const rects: Record<string, SampleRect> = {}
77
+ for (const testId of testIds) {
78
+ const node = await bridge.send({
79
+ type: 'evaluate',
80
+ code: buildResolveRectEval({ id: testId }),
81
+ })
82
+ if (!node) throw new Error(`no node with testID "${testId}"`)
83
+ rects[testId] = node as SampleRect
84
+ }
85
+
86
+ mkdirSync(outDir, { recursive: true })
87
+ const savePng = (name: string, dataUrl: unknown) => {
88
+ if (typeof dataUrl !== 'string' || !dataUrl.startsWith('data:image/png;base64,')) {
89
+ throw new Error(`capture for ${name} returned no image data`)
90
+ }
91
+ const path = resolve(outDir, name)
92
+ writeFileSync(
93
+ path,
94
+ Buffer.from(dataUrl.slice('data:image/png;base64,'.length), 'base64'),
95
+ )
96
+ console.log(` saved: ${path}`)
97
+ }
98
+
99
+ savePng('full.png', await bridge.send({ type: 'screenshot' }))
100
+ savePng(
101
+ 'base.png',
102
+ await bridge.send({
103
+ type: 'screenshot',
104
+ captureFilter: { hideTestIds: testIds },
105
+ }),
106
+ )
107
+ for (const testId of testIds) {
108
+ // every other listed target hides inside this solo, so nested targets
109
+ // (a button inside a lifted card) land in exactly one layer.
110
+ savePng(
111
+ `layer-${testId}.png`,
112
+ await bridge.send({
113
+ type: 'screenshot',
114
+ captureFilter: {
115
+ soloTestId: testId,
116
+ hideTestIds: testIds.filter((other) => other !== testId),
117
+ },
118
+ }),
119
+ )
120
+ }
121
+
122
+ const settings = (await callInBridge<Record<string, unknown> | null>(
123
+ bridge,
124
+ 'SootSim.bridges.settings.get',
125
+ )) ?? { deviceModel: null }
126
+ const model =
127
+ typeof settings.deviceModel === 'string' && settings.deviceModel in devices
128
+ ? settings.deviceModel
129
+ : null
130
+ const device = model ? devices[model as keyof typeof devices] : null
131
+ const manifest = {
132
+ device: device
133
+ ? { model, width: device.width, height: device.height, scale: device.scale }
134
+ : { model },
135
+ layers: testIds.map((testId) => ({ testId, rect: rects[testId] })),
136
+ }
137
+ const manifestPath = resolve(outDir, 'manifest.json')
138
+ writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`)
139
+ console.log(` saved: ${manifestPath}`)
140
+ } finally {
141
+ bridge.close()
142
+ }
143
+ }
@@ -0,0 +1,37 @@
1
+ // sootsim mode screenshot — toggle the "screenshot mode" chrome overlay
2
+ // in the running sootsim sim. orthogonal to `--no-shell` on the
3
+ // `screenshot` command: this hides the DOM browser chrome (rail, menubar,
4
+ // window controls) while still rendering the iOS shell inside the canvas.
5
+ //
6
+ // wraps the existing `toggle-screenshot-mode` shell action so the CLI
7
+ // stays behavior-compatible with the menu item / keyboard shortcut.
8
+
9
+ import { runShellBooleanMode, type ShellBooleanModeOptions } from './shell-boolean-mode'
10
+
11
+ export async function runScreenshotMode(args: string[], opts: ShellBooleanModeOptions) {
12
+ if (args.includes('--help') || args.includes('-h')) {
13
+ console.log(`
14
+ rnxsim mode screenshot — toggle the screenshot-mode chrome overlay
15
+
16
+ usage:
17
+ rnxsim mode screenshot [on|off|toggle]
18
+
19
+ arguments:
20
+ on enable screenshot mode (enables showFrame if needed)
21
+ off disable screenshot mode
22
+ toggle flip the current state (default)
23
+
24
+ examples:
25
+ rnxsim mode screenshot
26
+ rnxsim mode screenshot on
27
+ rnxsim mode screenshot off
28
+ `)
29
+ process.exit(0)
30
+ }
31
+
32
+ await runShellBooleanMode(args, opts, {
33
+ modeKey: 'screenshotMode',
34
+ displayName: 'screenshot-mode',
35
+ actionId: 'toggle-screenshot-mode',
36
+ })
37
+ }