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,300 @@
1
+ import { execFileSync, spawn } from 'child_process'
2
+ import { existsSync, readdirSync } from 'fs'
3
+ import { dirname, join, resolve } from 'path'
4
+ import { fileURLToPath } from 'url'
5
+
6
+ // resolved lazily — sootsim-engine is a workspace package in dev, but absent
7
+ // from the published npm tarball. don't crash module load just because the
8
+ // engine package isn't installed; commands that don't need the dev-electron
9
+ // path (most of them) shouldn't care.
10
+ let _sootsimRoot: string | null | undefined
11
+ function getSootsimRoot(): string | null {
12
+ if (_sootsimRoot !== undefined) return _sootsimRoot
13
+ try {
14
+ _sootsimRoot = dirname(
15
+ fileURLToPath(import.meta.resolve('sootsim-engine/package.json')),
16
+ )
17
+ } catch {
18
+ _sootsimRoot = null
19
+ }
20
+ return _sootsimRoot
21
+ }
22
+ const SOOTSIM_BUNDLE_ID = 'dev.sootsim.simulator'
23
+
24
+ export interface DesktopCompanionInstall {
25
+ path: string
26
+ platform: NodeJS.Platform
27
+ kind: 'mac-app' | 'linux-appimage' | 'linux-binary' | 'dev-electron'
28
+ // present for dev-electron: the sootsim-engine dir that electron treats
29
+ // as the app root (its package.json "main" points at dist-electron/main.cjs).
30
+ engineDir?: string
31
+ }
32
+
33
+ export interface DesktopCompanionLaunchResult {
34
+ launched: boolean
35
+ via?: 'mac-app' | 'linux-appimage' | 'linux-binary' | 'dev-electron'
36
+ target?: string
37
+ }
38
+
39
+ // dev-electron: when running from inside the Contrast checkout with sootsim-engine
40
+ // built and the electron dev dep present, prefer launching the local unpacked
41
+ // electron instead of the stale packaged /Applications/sootsim.app.
42
+ //
43
+ // rationale: in dev we want electron to hit http://localhost:5173
44
+ // (src-electron/main.ts does this when !app.isPackaged).
45
+ // using the local `node_modules/.bin/electron` binary against the engine dir
46
+ // gives us that: isPackaged=false → loads the live dev server.
47
+ function findDevElectron(): DesktopCompanionInstall | null {
48
+ // engine dir must have a built main.cjs for electron to boot. the dev build
49
+ // chain (`bun run dev:electron` / `bun run build:electron-main`) produces
50
+ // this; bun dev keeps it fresh via a one-shot step added to run-all.
51
+ const engineDir = getSootsimRoot()
52
+ if (!engineDir) return null
53
+ const engineMain = join(engineDir, 'dist-electron/main.cjs')
54
+ if (!existsSync(engineMain)) return null
55
+
56
+ // walk up from engine dir looking for a node_modules/.bin/electron.
57
+ // the repo layout is packages/sootsim-engine next to a root node_modules,
58
+ // but also works when installed as a dependency.
59
+ const electronBin = findElectronBin(engineDir)
60
+ if (!electronBin) return null
61
+
62
+ return {
63
+ path: electronBin,
64
+ platform: process.platform,
65
+ kind: 'dev-electron',
66
+ engineDir,
67
+ }
68
+ }
69
+
70
+ // detect whether the sootsim-engine we resolved is the in-repo workspace
71
+ // checkout (has src-electron/main.ts) vs something pulled in as a dep.
72
+ // this matters because repo development has one supported path: dev electron
73
+ // against the live :5173 renderer.
74
+ function isInRepoCheckout(): boolean {
75
+ const root = getSootsimRoot()
76
+ if (!root) return false
77
+ return existsSync(join(root, 'src-electron/main.ts'))
78
+ }
79
+
80
+ function findElectronBin(startDir: string): string | null {
81
+ let dir = startDir
82
+ for (let i = 0; i < 6; i++) {
83
+ const candidate = join(dir, 'node_modules/.bin/electron')
84
+ if (existsSync(candidate)) return candidate
85
+ const parent = dirname(dir)
86
+ if (parent === dir) break
87
+ dir = parent
88
+ }
89
+ return null
90
+ }
91
+
92
+ function findMacApp(): DesktopCompanionInstall | null {
93
+ const root = getSootsimRoot()
94
+ const candidates = [
95
+ '/Applications/sootsim.app',
96
+ resolve(process.env.HOME || '', 'Applications/sootsim.app'),
97
+ ...(root ? [resolve(root, 'app/sootsim.app')] : []),
98
+ ]
99
+
100
+ const found = candidates.find((candidate) => existsSync(candidate))
101
+ if (found) {
102
+ return {
103
+ path: found,
104
+ platform: 'darwin',
105
+ kind: 'mac-app',
106
+ }
107
+ }
108
+
109
+ try {
110
+ const result = execFileSync(
111
+ 'mdfind',
112
+ [`kMDItemCFBundleIdentifier == "${SOOTSIM_BUNDLE_ID}"`],
113
+ { encoding: 'utf8', timeout: 3000 },
114
+ ).trim()
115
+ if (result) {
116
+ return {
117
+ path: result.split('\n')[0],
118
+ platform: 'darwin',
119
+ kind: 'mac-app',
120
+ }
121
+ }
122
+ } catch {}
123
+
124
+ return null
125
+ }
126
+
127
+ function findFirstAppImage(dir: string): string | null {
128
+ if (!existsSync(dir)) return null
129
+ const match = readdirSync(dir)
130
+ .filter((entry) => entry.startsWith('sootsim') && entry.endsWith('.AppImage'))
131
+ .sort()
132
+ .reverse()[0]
133
+ return match ? join(dir, match) : null
134
+ }
135
+
136
+ function findLinuxInstall(): DesktopCompanionInstall | null {
137
+ const root = getSootsimRoot()
138
+ const directCandidates = [
139
+ resolve(process.env.HOME || '', 'Applications/sootsim.AppImage'),
140
+ resolve(process.env.HOME || '', '.local/bin/sootsim.AppImage'),
141
+ '/opt/sootsim/sootsim.AppImage',
142
+ '/opt/sootsim/sootsim',
143
+ ...(root
144
+ ? [
145
+ resolve(root, 'app/sootsim.AppImage'),
146
+ resolve(root, 'release/linux-unpacked/sootsim'),
147
+ ]
148
+ : []),
149
+ ]
150
+
151
+ const directMatch = directCandidates.find((candidate) => existsSync(candidate))
152
+ if (directMatch) {
153
+ return {
154
+ path: directMatch,
155
+ platform: 'linux',
156
+ kind: directMatch.endsWith('.AppImage') ? 'linux-appimage' : 'linux-binary',
157
+ }
158
+ }
159
+
160
+ const scannedMatch = root
161
+ ? findFirstAppImage(resolve(root, 'app')) ||
162
+ findFirstAppImage(resolve(root, 'release'))
163
+ : null
164
+
165
+ if (!scannedMatch) return null
166
+
167
+ return {
168
+ path: scannedMatch,
169
+ platform: 'linux',
170
+ kind: 'linux-appimage',
171
+ }
172
+ }
173
+
174
+ export function findDesktopCompanion(): DesktopCompanionInstall | null {
175
+ const dev = findDevElectron()
176
+ if (dev) return dev
177
+ if (isInRepoCheckout()) {
178
+ const engineDir = getSootsimRoot()!
179
+ const engineMain = join(engineDir, 'dist-electron/main.cjs')
180
+ if (!existsSync(engineMain)) {
181
+ console.error(
182
+ ` dist-electron/main.cjs missing in ${engineDir}.\n` +
183
+ ` run: bun run --cwd packages/sootsim-engine build:electron-main\n` +
184
+ ` (or keep it fresh with: bun run watch:sootsim:electron-main)`,
185
+ )
186
+ } else {
187
+ console.error(
188
+ ` no node_modules/.bin/electron found near ${engineDir}. run \`bun install\`.`,
189
+ )
190
+ }
191
+ return null
192
+ }
193
+ if (process.platform === 'darwin') return findMacApp()
194
+ if (process.platform === 'linux') return findLinuxInstall()
195
+ return null
196
+ }
197
+
198
+ async function spawnDetached(
199
+ command: string,
200
+ args: string[],
201
+ opts: { inheritStdio?: boolean } = {},
202
+ ) {
203
+ // dev-electron streams main-process console.log + renderer crash dumps to
204
+ // the terminal so the developer sees them. inherit stdout/stderr but
205
+ // ignore stdin so the child doesn't fight the shell for keystrokes.
206
+ // detached + unref means the CLI still returns the prompt immediately;
207
+ // electron keeps writing to the inherited fds until the user quits it
208
+ // or closes the terminal.
209
+ const stdio: 'ignore' | ['ignore', 'inherit', 'inherit'] = opts.inheritStdio
210
+ ? ['ignore', 'inherit', 'inherit']
211
+ : 'ignore'
212
+ await new Promise<void>((resolve, reject) => {
213
+ const child = spawn(command, args, { detached: true, stdio })
214
+ child.once('error', reject)
215
+ child.once('spawn', () => {
216
+ child.unref()
217
+ resolve()
218
+ })
219
+ })
220
+ }
221
+
222
+ export interface LaunchCompanionOptions {
223
+ // seeds the initial device model for the first-created window. wins
224
+ // over any persisted window state; read by main.ts readCliDeviceOverride.
225
+ device?: string
226
+ profileId?: string
227
+ ephemeralProfile?: boolean
228
+ // set by `sootsim open`, whose native window belongs to the launching
229
+ // session. `sootsim desktop` omits it and remains a persistent desktop app.
230
+ ownerPid?: number
231
+ }
232
+
233
+ export async function launchDesktopCompanion(
234
+ url?: string,
235
+ install = findDesktopCompanion(),
236
+ opts: LaunchCompanionOptions = {},
237
+ ): Promise<DesktopCompanionLaunchResult> {
238
+ if (!install) return { launched: false }
239
+
240
+ // electron argv extra: main.ts reads --device <model> from process.argv
241
+ // and uses it as the seed for the first createWindow.
242
+ //
243
+ // ⚠ chromium reorders argv before electron's main handler (and before any
244
+ // `second-instance` event) sees it: `--flag value` becomes the bare switch
245
+ // `--flag` followed by `value` as a stray positional. it also injects its
246
+ // own switches (e.g. `--allow-file-access-from-files`) between the no-value
247
+ // flags, so the original pairing is lost entirely. anything that takes a
248
+ // value MUST use the `--flag=value` single-arg form so chromium preserves
249
+ // it intact. ephemeral-profile has no value so it survives as-is.
250
+ const extraArgs: string[] = []
251
+ if (opts.device) extraArgs.push(`--device=${opts.device}`)
252
+ if (opts.profileId) extraArgs.push(`--profile=${opts.profileId}`)
253
+ if (opts.ephemeralProfile) extraArgs.push('--ephemeral-profile')
254
+ if (opts.ownerPid && opts.ownerPid > 1) extraArgs.push(`--owner-pid=${opts.ownerPid}`)
255
+
256
+ if (install.kind === 'mac-app') {
257
+ // `open -a` splits app args before and after `--args`. anything after
258
+ // --args is forwarded to electron's process.argv.
259
+ const openArgs: string[] = ['-g', '-a', install.path]
260
+ if (url) openArgs.push(buildMacLaunchUrl(url, opts))
261
+ if (extraArgs.length > 0) openArgs.push('--args', ...extraArgs)
262
+ await spawnDetached('open', openArgs)
263
+ return { launched: true, via: 'mac-app', target: install.path }
264
+ }
265
+
266
+ if (install.kind === 'dev-electron') {
267
+ // electron <app-dir> loads the dir's package.json "main" (dist-electron/main.cjs).
268
+ // main.ts checks app.isPackaged and loads http://localhost:5173 in dev.
269
+ // local dev shells commonly run under sandbox-exec/safehouse. chromium's
270
+ // own sandbox cannot initialize inside that outer sandbox, so pass the
271
+ // standard electron switch for the unpacked dev app only.
272
+ const args: string[] = ['--no-sandbox', install.engineDir || getSootsimRoot()!]
273
+ if (url) args.push(url)
274
+ if (extraArgs.length > 0) args.push(...extraArgs)
275
+ await spawnDetached(install.path, args, { inheritStdio: true })
276
+ return { launched: true, via: 'dev-electron', target: install.path }
277
+ }
278
+
279
+ const binaryArgs: string[] = []
280
+ if (url) binaryArgs.push(url)
281
+ if (extraArgs.length > 0) binaryArgs.push(...extraArgs)
282
+ await spawnDetached(install.path, binaryArgs)
283
+ return {
284
+ launched: true,
285
+ via: install.kind,
286
+ target: install.path,
287
+ }
288
+ }
289
+
290
+ function buildMacLaunchUrl(url: string, opts: LaunchCompanionOptions): string {
291
+ if (!opts.profileId && !opts.ephemeralProfile && !opts.ownerPid) return url
292
+ const deepLink = new URL('sootsim://dev')
293
+ deepLink.searchParams.set('url', url)
294
+ if (opts.profileId) deepLink.searchParams.set('profile', opts.profileId)
295
+ if (opts.ephemeralProfile) deepLink.searchParams.set('ephemeral', '1')
296
+ if (opts.ownerPid && opts.ownerPid > 1) {
297
+ deepLink.searchParams.set('ownerPid', String(opts.ownerPid))
298
+ }
299
+ return deepLink.toString()
300
+ }
@@ -0,0 +1,70 @@
1
+ // electron driver — wraps the existing desktop-companion helper. availability
2
+ // is "is the sootsim desktop app installed on this machine". launch delegates
3
+ // to launchDesktopCompanion, which already handles mac `open -a`, linux
4
+ // AppImage, and the --device seed.
5
+
6
+ import { findDesktopCompanion, launchDesktopCompanion } from '../desktop-companion'
7
+ import type {
8
+ Driver,
9
+ DriverAvailability,
10
+ DriverLaunchOptions,
11
+ DriverLaunchResult,
12
+ } from './types'
13
+
14
+ function probe(): DriverAvailability {
15
+ const install = findDesktopCompanion()
16
+ if (!install) {
17
+ return {
18
+ available: false,
19
+ reason: 'rnxsim desktop app not installed (run `rnxsim desktop install`)',
20
+ }
21
+ }
22
+ return {
23
+ available: true,
24
+ reason: null,
25
+ detail: `${install.kind} @ ${install.path}`,
26
+ }
27
+ }
28
+
29
+ async function launch(opts: DriverLaunchOptions): Promise<DriverLaunchResult> {
30
+ const install = findDesktopCompanion()
31
+ if (!install) {
32
+ return {
33
+ launched: false,
34
+ message: 'rnxsim desktop app not installed',
35
+ }
36
+ }
37
+ try {
38
+ const result = await launchDesktopCompanion(opts.url, install, {
39
+ device: opts.device,
40
+ profileId: opts.profileId,
41
+ ephemeralProfile: opts.ephemeralProfile,
42
+ ownerPid: opts.ownerPid,
43
+ })
44
+ if (!result.launched) {
45
+ return { launched: false, message: 'desktop companion failed to start' }
46
+ }
47
+ return {
48
+ launched: true,
49
+ message: opts.url
50
+ ? `electron launched via ${result.via} → ${opts.url}`
51
+ : `electron launched via ${result.via}`,
52
+ target: result.target,
53
+ attachUrl: opts.url,
54
+ }
55
+ } catch (err) {
56
+ return {
57
+ launched: false,
58
+ message: `electron launch failed: ${err instanceof Error ? err.message : String(err)}`,
59
+ }
60
+ }
61
+ }
62
+
63
+ export const electronDriver: Driver = {
64
+ id: 'electron',
65
+ name: 'electron',
66
+ description: 'rnxsim desktop companion app (native window, menu bar)',
67
+ kind: 'native',
68
+ availability: probe,
69
+ launch,
70
+ }
@@ -0,0 +1,20 @@
1
+ // sootsim driver registry — public entry.
2
+
3
+ export type {
4
+ Driver,
5
+ DriverId,
6
+ DriverKind,
7
+ DriverAvailability,
8
+ DriverLaunchOptions,
9
+ DriverLaunchResult,
10
+ } from './types'
11
+ export {
12
+ ALL_DRIVERS,
13
+ buildDriverListRows,
14
+ getAllDrivers,
15
+ getDriver,
16
+ resolveDriver,
17
+ type DriverListRow,
18
+ } from './registry'
19
+ export { electronDriver } from './electron'
20
+ export { playwrightDriver } from './playwright'
@@ -0,0 +1,180 @@
1
+ import { spawnSync } from 'node:child_process'
2
+ import { existsSync, readFileSync } from 'node:fs'
3
+ import { createRequire } from 'node:module'
4
+ import { dirname, join } from 'node:path'
5
+ export interface ResolvedPlaywright {
6
+ // package name that resolved (for human-facing messaging)
7
+ spec: string
8
+ // absolute package entry loaded by the detached browser host
9
+ modulePath: string
10
+ // absolute package-owned CLI entry used to install this package's browser
11
+ cliPath: string
12
+ // package version shown in availability and provisioning failures
13
+ version: string
14
+ }
15
+
16
+ export type PlaywrightBrowserProvisioningResult =
17
+ | {
18
+ ok: true
19
+ executablePath: string
20
+ installed: boolean
21
+ }
22
+ | {
23
+ ok: false
24
+ message: string
25
+ }
26
+
27
+ const PLAYWRIGHT_BROWSER_PROBE = `
28
+ const { existsSync } = require('node:fs');
29
+ const modulePath = process.env.SOOTSIM_PW_MODULE;
30
+ try {
31
+ const playwright = require(modulePath);
32
+ const browser = playwright && playwright.chromium;
33
+ if (!browser || typeof browser.executablePath !== 'function') {
34
+ throw new Error('resolved package does not expose browser "chromium"');
35
+ }
36
+ const executablePath = browser.executablePath();
37
+ process.stdout.write(JSON.stringify({ executablePath, exists: existsSync(executablePath) }));
38
+ } catch (error) {
39
+ process.stderr.write(String((error && error.stack) || error));
40
+ process.exit(1);
41
+ }
42
+ `
43
+
44
+ function isRecord(value: unknown): value is Record<string, unknown> {
45
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
46
+ }
47
+
48
+ export function resolvePlaywright(): ResolvedPlaywright | null {
49
+ // resolve from several roots, not just cwd. a user can invoke a global CLI
50
+ // from an arbitrary directory while Playwright is installed beside the CLI.
51
+ const roots = [`${process.cwd()}/`]
52
+ if (process.argv[1]) roots.push(process.argv[1])
53
+ for (const root of roots) {
54
+ try {
55
+ const requireFromRoot = createRequire(root)
56
+ for (const spec of ['playwright', 'playwright-chromium', 'playwright-core']) {
57
+ try {
58
+ const modulePath = requireFromRoot.resolve(spec)
59
+ const packageJsonPath = requireFromRoot.resolve(`${spec}/package.json`)
60
+ const parsed: unknown = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
61
+ if (!isRecord(parsed) || typeof parsed.version !== 'string') continue
62
+ const bin = parsed.bin
63
+ let binPath: string | null = null
64
+ if (typeof bin === 'string') {
65
+ binPath = bin
66
+ } else if (isRecord(bin)) {
67
+ const named = bin.playwright ?? bin['playwright-core']
68
+ const first = Object.values(bin).find((value) => typeof value === 'string')
69
+ binPath =
70
+ typeof named === 'string' ? named : typeof first === 'string' ? first : null
71
+ }
72
+ if (!binPath) continue
73
+ const cliPath = join(dirname(packageJsonPath), binPath)
74
+ if (!existsSync(cliPath)) continue
75
+ return {
76
+ spec,
77
+ modulePath,
78
+ cliPath,
79
+ version: parsed.version,
80
+ }
81
+ } catch {}
82
+ }
83
+ } catch {}
84
+ }
85
+ return null
86
+ }
87
+
88
+ type BrowserProbeResult =
89
+ | { ok: true; executablePath: string; exists: boolean }
90
+ | { ok: false; message: string }
91
+
92
+ function probeBrowser(
93
+ resolved: ResolvedPlaywright,
94
+ env: NodeJS.ProcessEnv,
95
+ ): BrowserProbeResult {
96
+ const probe = spawnSync(process.execPath, ['-e', PLAYWRIGHT_BROWSER_PROBE], {
97
+ env: {
98
+ ...env,
99
+ SOOTSIM_PW_MODULE: resolved.modulePath,
100
+ },
101
+ encoding: 'utf8',
102
+ timeout: 30_000,
103
+ })
104
+ if (probe.status !== 0) {
105
+ const detail = (probe.stderr || probe.error?.message || 'browser probe failed').trim()
106
+ return {
107
+ ok: false,
108
+ message: `could not inspect ${resolved.spec}@${resolved.version} chromium: ${detail}`,
109
+ }
110
+ }
111
+ try {
112
+ const parsed: unknown = JSON.parse(probe.stdout)
113
+ if (
114
+ !isRecord(parsed) ||
115
+ typeof parsed.executablePath !== 'string' ||
116
+ typeof parsed.exists !== 'boolean'
117
+ ) {
118
+ throw new Error('probe returned an invalid result')
119
+ }
120
+ return {
121
+ ok: true,
122
+ executablePath: parsed.executablePath,
123
+ exists: parsed.exists && existsSync(parsed.executablePath),
124
+ }
125
+ } catch (error) {
126
+ return {
127
+ ok: false,
128
+ message: `could not inspect ${resolved.spec}@${resolved.version} chromium: ${
129
+ error instanceof Error ? error.message : String(error)
130
+ }`,
131
+ }
132
+ }
133
+ }
134
+
135
+ export function ensurePlaywrightBrowser(
136
+ resolved: ResolvedPlaywright,
137
+ env: NodeJS.ProcessEnv = process.env,
138
+ ): PlaywrightBrowserProvisioningResult {
139
+ const before = probeBrowser(resolved, env)
140
+ if (!before.ok) return before
141
+ if (before.exists) {
142
+ return { ok: true, executablePath: before.executablePath, installed: false }
143
+ }
144
+
145
+ // the resolved package is the authority for both the required executable
146
+ // path and the installer. never scan a shared cache or select a neighboring
147
+ // revision: Playwright's own CLI installs the exact revision its API named.
148
+ const install = spawnSync(process.execPath, [resolved.cliPath, 'install', 'chromium'], {
149
+ env,
150
+ encoding: 'utf8',
151
+ timeout: 10 * 60_000,
152
+ })
153
+ if (install.status !== 0) {
154
+ const rawDetail = (
155
+ install.stderr ||
156
+ install.stdout ||
157
+ install.error?.message ||
158
+ 'browser install failed'
159
+ ).trim()
160
+ const detail = rawDetail.length <= 4_000 ? rawDetail : rawDetail.slice(-4_000)
161
+ return {
162
+ ok: false,
163
+ message:
164
+ `${resolved.spec}@${resolved.version} requires chromium at ` +
165
+ `${before.executablePath}, but its browser install failed${detail ? `:\n${detail}` : ''}`,
166
+ }
167
+ }
168
+
169
+ const after = probeBrowser(resolved, env)
170
+ if (!after.ok) return after
171
+ if (!after.exists) {
172
+ return {
173
+ ok: false,
174
+ message:
175
+ `${resolved.spec}@${resolved.version} browser install completed, but its required ` +
176
+ `chromium executable is still missing at ${after.executablePath}`,
177
+ }
178
+ }
179
+ return { ok: true, executablePath: after.executablePath, installed: true }
180
+ }