rnxsim 0.1.352 → 0.1.354

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 (198) hide show
  1. package/README.md +17 -16
  2. package/cli/bin.ts +5 -4
  3. package/cli/commands/control.ts +20 -5
  4. package/cli/commands/daemon-mac-app.ts +1 -1
  5. package/cli/commands/daemon.ts +6 -8
  6. package/cli/commands/install-desktop.ts +42 -32
  7. package/cli/commands/no-bridge-hint.ts +3 -3
  8. package/cli/commands/serve.ts +1 -1
  9. package/cli/commands/setup.ts +215 -176
  10. package/cli/commands/welcome.ts +9 -58
  11. package/cli/desktop-companion.ts +31 -16
  12. package/cli/prompt.ts +12 -7
  13. package/cli/setup-repository.ts +6 -2
  14. package/cli/ui.ts +68 -0
  15. package/dist-cli/bin.js +5 -5
  16. package/dist-cli/chunks/{agent-27WDUXK5.js → agent-FB7RPXO6.js} +2 -2
  17. package/dist-cli/chunks/{agent-wrapper-XAUWPZ6O.js → agent-wrapper-LFU3XEIG.js} +2 -2
  18. package/dist-cli/chunks/{app-fonts-COAZFLDG.js → app-fonts-FK6SETRG.js} +2 -2
  19. package/dist-cli/chunks/{assert-ZR2UE3EH.js → assert-3KQYZDDK.js} +2 -2
  20. package/dist-cli/chunks/{auth-LISHWKCC.js → auth-RFE3J27C.js} +2 -2
  21. package/dist-cli/chunks/{beta-AQQUYVA5.js → beta-XVY4WOYH.js} +2 -2
  22. package/dist-cli/chunks/{camera-KE44ZN3U.js → camera-JROBX5GM.js} +2 -2
  23. package/dist-cli/chunks/chunk-24DA3Y2Z.js +9 -0
  24. package/dist-cli/chunks/{chunk-E42ZJLPN.js → chunk-37S36DFW.js} +1 -1
  25. package/dist-cli/chunks/{chunk-LRKODRWI.js → chunk-3QLERZYJ.js} +2 -2
  26. package/dist-cli/chunks/{chunk-VQDY7NEJ.js → chunk-3RW6W3ZO.js} +1 -1
  27. package/dist-cli/chunks/{chunk-R4P7WO4L.js → chunk-4AQCDL24.js} +2 -2
  28. package/dist-cli/chunks/{chunk-M6DC6JZL.js → chunk-4OS3SXPZ.js} +2 -2
  29. package/dist-cli/chunks/{chunk-TF6VBUZ7.js → chunk-766UIGCS.js} +2 -2
  30. package/dist-cli/chunks/{chunk-PEBQKBEP.js → chunk-77CBLMD4.js} +1 -1
  31. package/dist-cli/chunks/{chunk-7T7DR34H.js → chunk-7WANPS3W.js} +2 -2
  32. package/dist-cli/chunks/{chunk-MJB6N7WS.js → chunk-ATQPDEED.js} +2 -2
  33. package/dist-cli/chunks/chunk-B7OMSJXC.js +15 -0
  34. package/dist-cli/chunks/{chunk-MJJP2BEI.js → chunk-BAWV2UEF.js} +3 -3
  35. package/dist-cli/chunks/{chunk-DSF4WC4X.js → chunk-BNXXNT3I.js} +2 -2
  36. package/dist-cli/chunks/{chunk-BZ32JGFC.js → chunk-CKKDXTKV.js} +1 -1
  37. package/dist-cli/chunks/{chunk-P5YC5VGA.js → chunk-CUK6Y2RR.js} +2 -2
  38. package/dist-cli/chunks/{chunk-XI4W6LRQ.js → chunk-ENPWD62S.js} +2 -2
  39. package/dist-cli/chunks/{chunk-ZK5HYQ4I.js → chunk-EWPX6LEN.js} +2 -2
  40. package/dist-cli/chunks/{chunk-AIGZ3TSW.js → chunk-FEHHM2LV.js} +3 -3
  41. package/dist-cli/chunks/{chunk-QP7KGSU4.js → chunk-FPH4TYZO.js} +3 -3
  42. package/dist-cli/chunks/{chunk-XHZEKHB2.js → chunk-GNKXRXND.js} +6 -6
  43. package/dist-cli/chunks/{chunk-3ZV4WSCR.js → chunk-GYGDIT5V.js} +2 -2
  44. package/dist-cli/chunks/{chunk-YAMKV3UB.js → chunk-HIYCRDUP.js} +2 -2
  45. package/dist-cli/chunks/{chunk-5JINX2IB.js → chunk-HUU7ZUQK.js} +2 -2
  46. package/dist-cli/chunks/{chunk-4AOAJXE2.js → chunk-IGV4KCCM.js} +2 -2
  47. package/dist-cli/chunks/{chunk-5JE5PNIV.js → chunk-IICFLREN.js} +1 -1
  48. package/dist-cli/chunks/chunk-IUL2WUWW.js +32 -0
  49. package/dist-cli/chunks/{chunk-EHQHK7KN.js → chunk-JJOHJNUQ.js} +2 -2
  50. package/dist-cli/chunks/{chunk-ESVF72YH.js → chunk-JT435OOM.js} +2 -2
  51. package/dist-cli/chunks/{chunk-R2BFC3AC.js → chunk-JT4IMNCO.js} +2 -2
  52. package/dist-cli/chunks/{chunk-S2PJOB2C.js → chunk-JWXAXCC6.js} +2 -2
  53. package/dist-cli/chunks/{chunk-CY6HBAOB.js → chunk-JXNR2KAB.js} +2 -2
  54. package/dist-cli/chunks/{chunk-45D2GGIA.js → chunk-LC77IVGY.js} +2 -2
  55. package/dist-cli/chunks/{chunk-H6MTPXZS.js → chunk-LCPZNPIB.js} +1 -1
  56. package/dist-cli/chunks/chunk-LETVUC6M.js +11 -0
  57. package/dist-cli/chunks/chunk-LRVL5MAX.js +4 -0
  58. package/dist-cli/chunks/{chunk-24CIPQT5.js → chunk-MIK6LMG4.js} +1 -1
  59. package/dist-cli/chunks/{chunk-JEPQ5YX3.js → chunk-N3CE6AZP.js} +2 -2
  60. package/dist-cli/chunks/{chunk-AR3CPCW3.js → chunk-OKFSCCFC.js} +1 -1
  61. package/dist-cli/chunks/{chunk-3Y7NWE22.js → chunk-P4GCLNFM.js} +2 -2
  62. package/dist-cli/chunks/{chunk-NQMZOJPX.js → chunk-PHIYWV44.js} +2 -2
  63. package/dist-cli/chunks/{chunk-WNJDE2JT.js → chunk-QQNNY43R.js} +1 -1
  64. package/dist-cli/chunks/{chunk-FHLEA4S5.js → chunk-QRYTCPNO.js} +2 -2
  65. package/dist-cli/chunks/{chunk-Q4ROOH3W.js → chunk-SLD2EMLZ.js} +2 -2
  66. package/dist-cli/chunks/{chunk-ELNSQ352.js → chunk-THFM2CFT.js} +1 -1
  67. package/dist-cli/chunks/{chunk-EGKGCDG5.js → chunk-TJRKLRNP.js} +1 -1
  68. package/dist-cli/chunks/{chunk-H4IF54WS.js → chunk-UAYXEP74.js} +1 -1
  69. package/dist-cli/chunks/{chunk-BQE4E24T.js → chunk-UI2IFVHB.js} +1 -1
  70. package/dist-cli/chunks/chunk-UVIWE3LD.js +124 -0
  71. package/dist-cli/chunks/chunk-UX6D4X6B.js +24 -0
  72. package/dist-cli/chunks/{chunk-RH6FJI5O.js → chunk-X2DJK3TB.js} +2 -2
  73. package/dist-cli/chunks/chunk-Y4GUWO3X.js +7 -0
  74. package/dist-cli/chunks/{chunk-DJAYEBF3.js → chunk-YATZ7U4S.js} +1 -1
  75. package/dist-cli/chunks/{chunk-RASWCAJJ.js → chunk-YIUIFZAE.js} +2 -2
  76. package/dist-cli/chunks/{chunk-QJTW4QQW.js → chunk-YXBNGSG6.js} +1 -1
  77. package/dist-cli/chunks/{chunk-2OME2KGY.js → chunk-ZJUFI3R7.js} +1 -1
  78. package/dist-cli/chunks/{chunk-6NSUY346.js → chunk-ZNABCRWX.js} +2 -2
  79. package/dist-cli/chunks/{cleanup-JUY2WU24.js → cleanup-F74ISXJC.js} +2 -2
  80. package/dist-cli/chunks/cli-version-VSDCFXXN.js +4 -0
  81. package/dist-cli/chunks/{compat-S2TBISY3.js → compat-C3RGYGXL.js} +2 -2
  82. package/dist-cli/chunks/{config-3PHD4J5N.js → config-CDY2RB7Q.js} +2 -2
  83. package/dist-cli/chunks/control-J35HAFZA.js +4 -0
  84. package/dist-cli/chunks/daemon-DLLEODJY.js +4 -0
  85. package/dist-cli/chunks/{debug-PFAJONVV.js → debug-3P2DUTUQ.js} +3 -3
  86. package/dist-cli/chunks/{desktop-Z7EOBMNV.js → desktop-XIXW34VW.js} +4 -4
  87. package/dist-cli/chunks/{detox-KRSP5SLY.js → detox-KAKTJ4P4.js} +2 -2
  88. package/dist-cli/chunks/{device-ZXJ7GRBO.js → device-4MAWLZMR.js} +2 -2
  89. package/dist-cli/chunks/{diagnose-JIMLEOEJ.js → diagnose-A3R7HUYA.js} +2 -2
  90. package/dist-cli/chunks/{disk-cleanup-YY7FFGVA.js → disk-cleanup-5R7JYCO6.js} +2 -2
  91. package/dist-cli/chunks/drivers-BB3BRKDS.js +4 -0
  92. package/dist-cli/chunks/{film-LKSJPU2A.js → film-GY5U752Q.js} +3 -3
  93. package/dist-cli/chunks/flow-ALMSRBVL.js +4 -0
  94. package/dist-cli/chunks/help-3ZL7MRUX.js +4 -0
  95. package/dist-cli/chunks/{hidden-runtime-alias-ZN3WZ3ND.js → hidden-runtime-alias-HSGN3GN4.js} +2 -2
  96. package/dist-cli/chunks/home-paths-BIUN4CBO.js +4 -0
  97. package/dist-cli/chunks/inspect-RCU7XVAO.js +4 -0
  98. package/dist-cli/chunks/install-desktop-LYVKLTCU.js +4 -0
  99. package/dist-cli/chunks/{login-XKLIFTAU.js → login-WKNMS4GG.js} +2 -2
  100. package/dist-cli/chunks/{logout-DR22RB5H.js → logout-6UFUOESK.js} +2 -2
  101. package/dist-cli/chunks/{maestro-SLCX6HBV.js → maestro-SJGV7B3W.js} +3 -3
  102. package/dist-cli/chunks/{maestro-generate-GBFZ5INV.js → maestro-generate-PMF2LLKX.js} +3 -3
  103. package/dist-cli/chunks/{mode-F4R2KNXX.js → mode-MN7LZCLB.js} +2 -2
  104. package/dist-cli/chunks/{optional-demo-registry-HM55KXGB.js → optional-demo-registry-HFA27YSP.js} +2 -2
  105. package/dist-cli/chunks/{perf-VBS5GEPG.js → perf-XGANSJT3.js} +2 -2
  106. package/dist-cli/chunks/{permissions-F46PQIUE.js → permissions-25UWEJKW.js} +2 -2
  107. package/dist-cli/chunks/privacy-SIDMP5EF.js +4 -0
  108. package/dist-cli/chunks/{record-NLZ6DFAA.js → record-MA3AI752.js} +3 -3
  109. package/dist-cli/chunks/{report-issue-XIE5BVR4.js → report-issue-4DTESWE2.js} +2 -2
  110. package/dist-cli/chunks/reset-GPHG5XN3.js +4 -0
  111. package/dist-cli/chunks/runtime-VOEIOODA.js +4 -0
  112. package/dist-cli/chunks/{screenshot-command-SD4JPNKW.js → screenshot-command-JUXMWXS5.js} +3 -3
  113. package/dist-cli/chunks/{screenshot-layers-X65RC3GT.js → screenshot-layers-DJF2E4PE.js} +2 -2
  114. package/dist-cli/chunks/{screenshots-capture-BBXN7FEZ.js → screenshots-capture-UGZU5GOW.js} +2 -2
  115. package/dist-cli/chunks/{serve-M6ZKMSAG.js → serve-H7GNKJSM.js} +2 -2
  116. package/dist-cli/chunks/setup-G2WYLPM6.js +4 -0
  117. package/dist-cli/chunks/{skills-KDWGQBOP.js → skills-4JGPOSFE.js} +2 -2
  118. package/dist-cli/chunks/{state-NWABXG2P.js → state-DEAQFKVP.js} +2 -2
  119. package/dist-cli/chunks/{storage-QQE3G65Q.js → storage-Y6F5NTGH.js} +2 -2
  120. package/dist-cli/chunks/store-3EQT7VU6.js +4 -0
  121. package/dist-cli/chunks/telemetry-FANCQHKG.js +4 -0
  122. package/dist-cli/chunks/{timeline-2VZ45MMB.js → timeline-HOHYYDOC.js} +2 -2
  123. package/dist-cli/chunks/{upgrade-T7PDR6T5.js → upgrade-QF5YHLSG.js} +2 -2
  124. package/dist-cli/chunks/upload-QKJHFAX7.js +4 -0
  125. package/dist-cli/chunks/{version-BX6QX5XA.js → version-PDCKYJLB.js} +2 -2
  126. package/dist-cli/chunks/{web-4J6JRZN2.js → web-C5EUAFFW.js} +2 -2
  127. package/dist-cli/chunks/welcome-3CQ2GE4F.js +5 -0
  128. package/dist-cli/chunks/{what-happened-CHQKFNCM.js → what-happened-L56ZFIMJ.js} +2 -2
  129. package/dist-lib/agent-daemon-client.cjs +2 -2
  130. package/dist-lib/agent-events.cjs +1 -1
  131. package/dist-lib/agent-identity.cjs +1 -1
  132. package/dist-lib/agent-sessions.cjs +1 -1
  133. package/dist-lib/attached-projects.cjs +1 -1
  134. package/dist-lib/auth/shared-session.cjs +1 -1
  135. package/dist-lib/backend-origin.cjs +1 -1
  136. package/dist-lib/beta.cjs +1 -1
  137. package/dist-lib/beta.mjs +1 -1
  138. package/dist-lib/bridge-constants.cjs +1 -1
  139. package/dist-lib/bridge-contract.cjs +1 -1
  140. package/dist-lib/cli-constants.cjs +1 -1
  141. package/dist-lib/config.cjs +1 -1
  142. package/dist-lib/detox/index.cjs +1 -1
  143. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  144. package/dist-lib/home-paths.cjs +21 -1
  145. package/dist-lib/host/bridge-host.cjs +1 -1
  146. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  147. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  148. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  149. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  150. package/dist-lib/host/websocket-proxy.cjs +1 -1
  151. package/dist-lib/index.cjs +2 -2
  152. package/dist-lib/jump-to-source-babel.cjs +1 -1
  153. package/dist-lib/menu.cjs +1 -1
  154. package/dist-lib/menu.mjs +1 -1
  155. package/dist-lib/metro-production-bundle.cjs +1 -1
  156. package/dist-lib/metro-production-bundle.mjs +1 -1
  157. package/dist-lib/metro.cjs +2 -2
  158. package/dist-lib/profiles.cjs +1 -1
  159. package/dist-lib/public-brand.cjs +1 -1
  160. package/dist-lib/react-native-host-modules.cjs +1 -1
  161. package/dist-lib/react-native-host-modules.mjs +1 -1
  162. package/dist-lib/render-mode.cjs +1 -1
  163. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  164. package/dist-lib/sdk.cjs +1 -1
  165. package/dist-lib/sdk.mjs +1 -1
  166. package/dist-lib/skills.cjs +87 -22
  167. package/dist-lib/vite.cjs +2 -2
  168. package/package.json +1 -1
  169. package/skills/contrast/SKILL.md +3 -3
  170. package/skills/rnx-setup/SKILL.md +17 -5
  171. package/src/agent-daemon-client.ts +1 -1
  172. package/src/home-paths.ts +22 -0
  173. package/src/metro-plugin.ts +1 -1
  174. package/src/runtime-assets.ts +1 -1
  175. package/dist-cli/chunks/chunk-2JZXOHH6.js +0 -4
  176. package/dist-cli/chunks/chunk-2WJABJ6C.js +0 -15
  177. package/dist-cli/chunks/chunk-5OVXQKUP.js +0 -25
  178. package/dist-cli/chunks/chunk-DFTG53TX.js +0 -11
  179. package/dist-cli/chunks/chunk-HF2FZ2WN.js +0 -7
  180. package/dist-cli/chunks/chunk-S63ENXGH.js +0 -6
  181. package/dist-cli/chunks/chunk-UHP33XGC.js +0 -124
  182. package/dist-cli/chunks/cli-version-6SQSIFNQ.js +0 -4
  183. package/dist-cli/chunks/control-GLBCTKAA.js +0 -4
  184. package/dist-cli/chunks/daemon-H4OVIEMH.js +0 -4
  185. package/dist-cli/chunks/drivers-63LY2NCK.js +0 -4
  186. package/dist-cli/chunks/flow-JHKPF7BN.js +0 -4
  187. package/dist-cli/chunks/help-Q6OY6VVA.js +0 -4
  188. package/dist-cli/chunks/home-paths-UOATIRKF.js +0 -4
  189. package/dist-cli/chunks/inspect-DW3V2FD4.js +0 -4
  190. package/dist-cli/chunks/install-desktop-GWY5H22W.js +0 -4
  191. package/dist-cli/chunks/privacy-34FNXPJP.js +0 -4
  192. package/dist-cli/chunks/reset-BAGCFYTS.js +0 -4
  193. package/dist-cli/chunks/runtime-JTYB4CM7.js +0 -4
  194. package/dist-cli/chunks/setup-KTWKZOJB.js +0 -30
  195. package/dist-cli/chunks/store-6XW4YZM6.js +0 -4
  196. package/dist-cli/chunks/telemetry-MVSV7JHI.js +0 -4
  197. package/dist-cli/chunks/upload-RF2MNO2X.js +0 -4
  198. package/dist-cli/chunks/welcome-3MJHGERV.js +0 -5
package/README.md CHANGED
@@ -132,9 +132,10 @@ graph TD
132
132
  ```
133
133
 
134
134
  **The CLI process** (`cli/bin.ts`) parses argv and lazy-imports exactly one
135
- command module per run. Bare `rnx` discovers a running React Native development
136
- server and opens it directly. `rnx setup` remains the explicit path for
137
- repository preparation and the optional background daemon. `rnx open`
135
+ command module per run. Bare `rnx` opens ConnectRN, whose simulator UI discovers
136
+ running React Native development servers. The first human run offers the
137
+ recommended background service and optional Desktop app, then opens the simulator
138
+ before asking about private diagnostics or an optional repository install. `rnx open`
138
139
  ensures the engine runtime exists and starts a local foreground bridge when no
139
140
  daemon is reachable, then opens a `WsBridge` (`cli/ws-bridge.ts`) on **port
140
141
  7668** and sends JSON commands. Inspection/interaction verbs live in
@@ -200,15 +201,15 @@ bridge always addresses the **shell worker**, never an undifferentiated sim.
200
201
  ## Getting started
201
202
 
202
203
  ```sh
203
- npx rnxsim # discover your running app and open it in the simulator
204
+ npx rnxsim # open ConnectRN and choose a running app in the simulator
204
205
  rnx open /settings # dispatch a React Native deep link into that app
205
206
  ```
206
207
 
207
208
  Start Metro, Expo, One, or another supported React Native development server
208
- the way you normally do, then run `npx rnxsim` from any directory. If one app is
209
- running, rnx opens it immediately. If several are running, a human terminal gets
210
- one short choice. CI and agents refuse to guess and print one explicit
211
- `rnx open <port>` command for each discovered app.
209
+ the way you normally do, then run `npx rnxsim` from any directory. The simulator
210
+ opens ConnectRN, which shows the available local apps and lets you connect one.
211
+ Pass a target to `rnx open <port-or-url>` when a script or agent needs an explicit
212
+ selection.
212
213
 
213
214
  The first `rnx open` launches an isolated Chrome for Testing sim. The CLI
214
215
  resolves Playwright from the app or CLI install, asks that exact package for
@@ -221,17 +222,17 @@ consume memory and CPU on shared machines.
221
222
 
222
223
  There is no required global or per-app install. Optional global installs can
223
224
  use `npm install --global rnxsim`, `bun add --global rnxsim`, or
224
- `pnpm add --global rnxsim`. `rnx setup` is optional machine/repository tuning:
225
- start your Metro, Expo, or One dev server the way you already do, then open it
226
- from the CLI. There is no iOS native build in the inner loop; Metro alone is
225
+ `pnpm add --global rnxsim`. Start your Metro, Expo, or One dev server the way you
226
+ already do, then open it from the CLI. There is no iOS native build in the inner loop; Metro alone is
227
227
  enough. The manifest bundle path and query remain authoritative. When both the
228
228
  discovery request and manifest target are loopback addresses, RNX keeps the
229
229
  loopback host that answered discovery so an IPv6-only listener is not reopened
230
230
  through an unreachable IPv4 address. `rnx open` starts a local bridge when no
231
- daemon is reachable; the guided setup installs the faster background daemon so
232
- later CLI commands reuse one server. A desktop GUI (Electron) is optional on
233
- top, via `rnx desktop
234
- install`. Native Electron windows created by `rnx open` belong to that CLI
231
+ daemon is reachable. The background service is strongly recommended for local
232
+ agent work because inspect, interaction, and test commands reuse one ready bridge
233
+ and runtime. Enable it at any time with `rnx daemon install`; it is never required
234
+ in CI. The packaged Desktop app is optional and available through first-run
235
+ onboarding or `rnx desktop install`. Native Electron windows created by `rnx open` belong to that CLI
235
236
  session and close when the session exits; `rnx desktop` keeps the persistent
236
237
  desktop-app lifecycle. In Electron, `File > New Window`
237
238
  duplicates the focused simulator, while `File > New Simulator >` opens a new
@@ -257,7 +258,7 @@ To uninstall the machine daemon and cached runtimes, run
257
258
  `rnx daemon uninstall`. It stops current and legacy launchd / systemd
258
259
  services, removes daemon logs, and removes `~/.sootsim/` (runtimes, profiles,
259
260
  recordings, and lockfiles). It does not remove the optional
260
- `/Applications/sootsim.app` desktop bundle or macOS preference plist. To inspect
261
+ `/Applications/rnx.app` desktop bundle or macOS preference plist. To inspect
261
262
  existing disk usage before uninstalling, run `rnx cleanup`; add `--apply`
262
263
  only after reviewing the preview.
263
264
 
package/cli/bin.ts CHANGED
@@ -88,12 +88,15 @@ const isAutomaticCleanupWorker =
88
88
  parsed.command === 'cleanup' &&
89
89
  parsed.commandArgs.length === 1 &&
90
90
  parsed.commandArgs[0] === '--automatic-worker'
91
+ const isBareWelcome = !parsed.command && parsed.commandArgs.length === 0
91
92
 
92
93
  if (
93
94
  !isAutomaticCleanupWorker &&
95
+ !isBareWelcome &&
94
96
  !parsed.help &&
95
97
  !parsed.version &&
96
98
  parsed.command !== 'config' &&
99
+ parsed.command !== 'setup' &&
97
100
  parsed.command !== 'telemetry'
98
101
  ) {
99
102
  const { ensurePrivacyChoice } = await import('./privacy')
@@ -169,8 +172,6 @@ async function scheduleAutomaticCleanup(): Promise<void> {
169
172
  }
170
173
  }
171
174
 
172
- // fast-exit helper: diagnostic error shapes are best-effort, but explicit exit
173
- // paths still give a permitted queued diagnostic a chance to drain.
174
175
  async function exitWithFlush(code: number): Promise<never> {
175
176
  const { flushCliTelemetry } = await import('./telemetry')
176
177
  await flushCliTelemetry()
@@ -228,8 +229,8 @@ if (device || theme) {
228
229
  settingsStore.apply(overrides)
229
230
  }
230
231
 
231
- // no command, no args: discover the running app and open it directly. explicit
232
- // `rnx setup` remains available for repository and background-service choices.
232
+ // no command, no args: guide first-time machine setup, then open ConnectRN.
233
+ // returning runs open ConnectRN directly; app discovery belongs inside it.
233
234
  if (!parsed.command && parsed.commandArgs.length === 0) {
234
235
  const { runWelcome } = await import('./commands/welcome')
235
236
  await exitWithFlush(await runWelcome())
@@ -25,6 +25,7 @@ import {
25
25
  readActiveRuntime,
26
26
  readDaemonLockfile,
27
27
  readLiveRuntimeVersions,
28
+ readSimulatorDriverPreference,
28
29
  runtimeDir,
29
30
  } from '../../src/home-paths'
30
31
  import { applyHotMode } from '../../src/native-dev-bundle-url'
@@ -65,6 +66,7 @@ import {
65
66
  type BridgeSimInfo,
66
67
  } from '../ws-bridge'
67
68
  import { WAIT_READY_PROBE, readyProbeHasContent, type ReadyProbe } from './inspect/core'
69
+ import { isAgentEnv } from './inspect/env'
68
70
  import type { DriverId, DriverLaunchResult } from '../drivers/types'
69
71
 
70
72
  interface ControlCommandOptions {
@@ -228,7 +230,7 @@ export async function ensureBridgeForOpen(
228
230
  (await canConnectToBridge(nextLock.bridgePort, 250))
229
231
  ) {
230
232
  console.error(
231
- ` started local bridge on port ${nextLock.bridgePort}; run \`sootsim setup\` to install the background daemon`,
233
+ ` Started a local bridge on port ${nextLock.bridgePort}. Run \`rnx daemon install\` to keep it ready between commands.`,
232
234
  )
233
235
  return nextLock.bridgePort
234
236
  }
@@ -1219,6 +1221,7 @@ function printOpenedSim(
1219
1221
  | 'direct shell open'
1220
1222
  | 'current sim'
1221
1223
  | `${string} driver`,
1224
+ quiet = false,
1222
1225
  ) {
1223
1226
  const [printableOpenUrl, printableSimUrl] = [openUrl, sim.url].map((value) => {
1224
1227
  if (!value) return value
@@ -1231,6 +1234,10 @@ function printOpenedSim(
1231
1234
  } catch {}
1232
1235
  return value
1233
1236
  })
1237
+ if (quiet) {
1238
+ console.log(` Opened ConnectRN [${mode}]`)
1239
+ return
1240
+ }
1234
1241
  console.log(
1235
1242
  ` ${mode === 'current sim' ? 'loaded' : 'opened'}: ${printableOpenUrl} [${mode}]`,
1236
1243
  )
@@ -1428,6 +1435,7 @@ export async function runOpenCommand(
1428
1435
  '--hot',
1429
1436
  '--no-hmr',
1430
1437
  '--no-describe',
1438
+ '--quiet',
1431
1439
  ],
1432
1440
  stripValueFlags: [
1433
1441
  '--base-url',
@@ -1440,6 +1448,7 @@ export async function runOpenCommand(
1440
1448
  ],
1441
1449
  })
1442
1450
  const requestedProfileId = args.find((_, i) => args[i - 1] === '--profile')
1451
+ const quiet = args.includes('--quiet')
1443
1452
  const ephemeralProfile = args.includes('--ephemeral')
1444
1453
  // --cdp-port exposes Chrome's remote-debugging endpoint on the opened sim so
1445
1454
  // it can be cpu-profiled while bridge-driven (playwright driver only).
@@ -1548,7 +1557,13 @@ export async function runOpenCommand(
1548
1557
  }
1549
1558
  const driverId: DriverId = isDriverId(requestedDriverId)
1550
1559
  ? requestedDriverId
1551
- : 'playwright'
1560
+ : process.env.CI === '1' ||
1561
+ process.env.CI === 'true' ||
1562
+ process.env.GITHUB_ACTIONS === 'true' ||
1563
+ isAgentEnv() ||
1564
+ !process.stdin.isTTY
1565
+ ? 'playwright'
1566
+ : readSimulatorDriverPreference() || 'playwright'
1552
1567
  const driverHeadless = resolveDriverHeadless(args, driverId)
1553
1568
  if (cdpPort && driverId !== 'playwright') {
1554
1569
  console.error(' rnx open: --cdp-port is only honored by the playwright driver')
@@ -1568,7 +1583,7 @@ export async function runOpenCommand(
1568
1583
  console.error(' rnx open: --hot and --no-hmr cannot be combined')
1569
1584
  process.exit(1)
1570
1585
  }
1571
- const hotMode = hotFlag ? true : noHmrFlag ? false : requestedDriverId !== 'playwright'
1586
+ const hotMode = hotFlag ? true : noHmrFlag ? false : driverId !== 'playwright'
1572
1587
 
1573
1588
  // --device <model> seeds the device profile for the opened sim (stamped on
1574
1589
  // the engine URL, so both playwright and electron honor it). without it
@@ -1735,7 +1750,7 @@ export async function runOpenCommand(
1735
1750
  applySootSimConfigToUrl(resolvedBaseUrl, runtimeConfig),
1736
1751
  buildShellUrlOpts,
1737
1752
  ))
1738
- printOpenedSim(shellUrl, { ...targetSim, url: shellUrl }, 'current sim')
1753
+ printOpenedSim(shellUrl, { ...targetSim, url: shellUrl }, 'current sim', quiet)
1739
1754
  await maybeDescribeAfterOpen(parsed.wsPort, targetSim.id, args)
1740
1755
  return parsed.wsPort
1741
1756
  }
@@ -1814,7 +1829,7 @@ export async function runOpenCommand(
1814
1829
  const match = outcome.sim
1815
1830
  saveCurrentSimId(match.id)
1816
1831
  signalDriverLaunchConnected(result)
1817
- printOpenedSim(openUrl, match, `${driver.name} driver`)
1832
+ printOpenedSim(openUrl, match, `${driver.name} driver`, quiet)
1818
1833
  await maybeDescribeAfterOpen(parsed.wsPort, match.id, args)
1819
1834
  return parsed.wsPort
1820
1835
  }
@@ -142,7 +142,7 @@ function renderLauncherScript(
142
142
  const stdout = shellSingleQuote(logDir.stdout)
143
143
  const stderr = shellSingleQuote(logDir.stderr)
144
144
  return `#!/bin/sh
145
- # SootSim Daemon launcher — auto-generated by 'rnx setup'.
145
+ # sootsim daemon launcher generated by 'rnx daemon install'.
146
146
  # launchd spawns this; we exec the real rnx invocation in place so
147
147
  # launchd tracks the daemon process, not the shell wrapper.
148
148
  #
@@ -5,7 +5,7 @@
5
5
  // registration time, so `npm update -g sootsim` + `sootsim daemon restart`
6
6
  // picks up the upgrade without touching the plist/unit file.
7
7
  //
8
- // `daemonInstall` is shared by `sootsim setup` and the explicit
8
+ // `daemonInstall` is shared by `rnx setup` and the explicit
9
9
  // `sootsim daemon install` machine-bootstrap path.
10
10
  //
11
11
  // macOS: ~/Library/LaunchAgents/dev.sootsim.daemon.plist (launchd)
@@ -656,7 +656,7 @@ async function daemonRestart() {
656
656
  if (process.platform === 'darwin') {
657
657
  const path = macPlistPath()
658
658
  if (!existsSync(path)) {
659
- console.error(' daemon not registered run `rnx setup`')
659
+ console.error(' daemon not registered. run `rnx daemon install`')
660
660
  process.exit(1)
661
661
  }
662
662
  const res = spawnSync(
@@ -674,7 +674,7 @@ async function daemonRestart() {
674
674
  if (process.platform === 'linux') {
675
675
  const path = linuxUnitPath()
676
676
  if (!existsSync(path)) {
677
- console.error(' daemon not registered run `rnx setup`')
677
+ console.error(' daemon not registered. run `rnx daemon install`')
678
678
  process.exit(1)
679
679
  }
680
680
  const res = spawnSync('systemctl', ['--user', 'restart', SERVICE_NAME_LINUX], {
@@ -696,7 +696,7 @@ async function daemonStart() {
696
696
  if (process.platform === 'darwin') {
697
697
  const path = macPlistPath()
698
698
  if (!existsSync(path)) {
699
- console.error(' daemon not registered run `rnx setup`')
699
+ console.error(' daemon not registered. run `rnx daemon install`')
700
700
  process.exit(1)
701
701
  }
702
702
  const res = spawnSync(
@@ -714,7 +714,7 @@ async function daemonStart() {
714
714
  if (process.platform === 'linux') {
715
715
  const path = linuxUnitPath()
716
716
  if (!existsSync(path)) {
717
- console.error(' daemon not registered run `rnx setup`')
717
+ console.error(' daemon not registered. run `rnx daemon install`')
718
718
  process.exit(1)
719
719
  }
720
720
  const res = spawnSync('systemctl', ['--user', 'start', SERVICE_NAME_LINUX], {
@@ -788,9 +788,7 @@ async function daemonLogs(rest: string[]) {
788
788
  const err = resolve(LOG_DIR, 'bridge.err.log')
789
789
  const present = [out, err].filter((p) => existsSync(p))
790
790
  if (present.length === 0) {
791
- console.error(
792
- ` no daemon logs yet at ${LOG_DIR} — run \`rnx setup\` to install the daemon`,
793
- )
791
+ console.error(` no daemon logs yet at ${LOG_DIR}. run \`rnx daemon install\``)
794
792
  process.exit(1)
795
793
  }
796
794
  const args = follow
@@ -5,8 +5,8 @@
5
5
  //
6
6
  // fetches the right platform artifact from https://contrast.dev/api/electron-release/
7
7
  // and installs it into the OS-native location that findDesktopCompanion() looks for:
8
- // macOS: ~/Applications/sootsim.app (or /Applications if writable)
9
- // linux: ~/Applications/sootsim.AppImage
8
+ // macos: ~/Applications/rnx.app (or /Applications if writable)
9
+ // linux: ~/Applications/rnx.AppImage
10
10
  // win: spawns the NSIS installer
11
11
  //
12
12
  // post-install the electron-updater feed (same URL) handles upgrades inside the
@@ -27,6 +27,7 @@ import { tmpdir } from 'os'
27
27
  import { dirname, join, resolve } from 'path'
28
28
  import { findDesktopCompanion } from '../desktop-companion'
29
29
  import { confirm } from '../prompt'
30
+ import { accent, bold, failure, muted, success, withSpinner } from '../ui'
30
31
 
31
32
  const DOWNLOAD_BASE = 'https://contrast.dev/api/electron-release'
32
33
 
@@ -122,7 +123,7 @@ function renderProgress(received: number, total: number | null): string {
122
123
  const totalMb = (total / (1024 * 1024)).toFixed(1)
123
124
  const barWidth = 24
124
125
  const filled = Math.round((pct / 100) * barWidth)
125
- const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled)
126
+ const bar = accent('█'.repeat(filled)) + muted('░'.repeat(barWidth - filled))
126
127
  return ` ${bar} ${pct}% ${mb} / ${totalMb} MB`
127
128
  }
128
129
 
@@ -169,7 +170,7 @@ async function installMacDmg(dmgPath: string): Promise<string> {
169
170
  mkdirSync(destRoot, { recursive: true })
170
171
  }
171
172
 
172
- const appDest = join(destRoot, 'sootsim.app')
173
+ const appDest = join(destRoot, 'rnx.app')
173
174
  if (existsSync(appDest)) rmSync(appDest, { recursive: true, force: true })
174
175
 
175
176
  const copy = spawnSync('cp', ['-R', appSource, appDest])
@@ -193,7 +194,7 @@ async function installMacDmg(dmgPath: string): Promise<string> {
193
194
  async function installLinuxAppImage(appImagePath: string): Promise<string> {
194
195
  const destDir = resolve(process.env.HOME || '', 'Applications')
195
196
  mkdirSync(destDir, { recursive: true })
196
- const dest = join(destDir, 'sootsim.AppImage')
197
+ const dest = join(destDir, 'rnx.AppImage')
197
198
  if (existsSync(dest)) rmSync(dest, { force: true })
198
199
  // copyFile + unlink instead of rename: /tmp and ~/Applications are
199
200
  // routinely on separate mounts in containers / VMs, where renameSync
@@ -217,17 +218,20 @@ async function installWindowsExe(exePath: string): Promise<string> {
217
218
  else rejectPromise(new Error(`installer exited with code ${code}`))
218
219
  })
219
220
  })
220
- return exePath
221
+ const installed = findDesktopCompanion()
222
+ if (!installed) {
223
+ throw new Error('the installer finished but the rnx application was not registered')
224
+ }
225
+ return installed.path
221
226
  }
222
227
 
223
228
  export async function runInstallDesktop(args: string[]) {
224
229
  if (args.includes('--help') || args.includes('-h')) {
225
230
  console.log(`
226
- rnx desktop install — download and install the optional desktop GUI
231
+ rnx desktop install
227
232
 
228
- the CLI + daemon are the canonical rnx surface; the GUI is optional.
229
- if you just want to drive a running metro/expo dev server from the
230
- terminal, you don't need this.
233
+ Download and install the optional rnx Desktop app.
234
+ The CLI works without it. Choose Desktop when you want a dedicated native window.
231
235
 
232
236
  usage:
233
237
  rnx desktop install [options]
@@ -253,28 +257,31 @@ examples:
253
257
 
254
258
  const existing = findDesktopCompanion()
255
259
  if (existing && !force) {
256
- console.log(` rnx desktop already installed at: ${existing.path}`)
257
- console.log(' pass --force to reinstall.')
260
+ console.log(` ${success('✓')} Desktop app is already installed at ${existing.path}`)
261
+ console.log(` ${muted('Pass --force to reinstall.')}`)
258
262
  return
259
263
  }
260
264
 
261
265
  const target = detectTarget()
262
266
  if (!target) {
263
- console.error(` no desktop build available for ${process.platform}/${process.arch}.`)
264
267
  console.error(
265
- ' supported: darwin-arm64, darwin-x64, linux-x64, linux-arm64, win32-x64',
268
+ ` ${failure('×')} No desktop build is available for ${process.platform}/${process.arch}.`,
269
+ )
270
+ console.error(
271
+ ' Supported: darwin-arm64, darwin-x64, linux-x64, linux-arm64, win32-x64',
266
272
  )
267
273
  process.exit(1)
268
274
  }
269
275
 
270
- console.log(` platform: ${target.platform}`)
271
- console.log(` download: ${target.url}`)
276
+ console.log(` ${bold('Desktop app')}`)
277
+ console.log(` Platform ${target.platform}`)
278
+ console.log(` Download ${target.url}`)
272
279
  console.log()
273
280
 
274
281
  if (!skipConfirm) {
275
- const ok = await confirm('download and install now?', true)
282
+ const ok = await confirm('Download and install now?', true)
276
283
  if (!ok) {
277
- console.log(' cancelled.')
284
+ console.log(` ${muted('Cancelled.')}`)
278
285
  return
279
286
  }
280
287
  console.log()
@@ -284,7 +291,7 @@ examples:
284
291
  const downloadPath = join(workDir, target.filename)
285
292
 
286
293
  let lastDraw = 0
287
- process.stdout.write(` downloading ${target.filename}...\n`)
294
+ process.stdout.write(` Downloading ${target.filename}...\n`)
288
295
 
289
296
  try {
290
297
  await download({
@@ -302,36 +309,39 @@ examples:
302
309
  } catch (err) {
303
310
  rmSync(workDir, { recursive: true, force: true })
304
311
  const message = err instanceof Error ? err.message : String(err)
305
- console.error(`\n download failed: ${message}`)
312
+ console.error(`\n ${failure('×')} Download failed: ${message}`)
306
313
  console.error(
307
- ` the ${target.platform} desktop build may not be published yet, or your`,
314
+ ` The ${target.platform} desktop build may not be published yet, or your`,
308
315
  )
309
316
  console.error(
310
- ' network blocked the request. check your connection and try again later;',
317
+ ' network blocked the request. Check your connection and try again later.',
311
318
  )
312
- console.error(' the CLI + daemon work without the desktop app (run: rnx --help).')
319
+ console.error(' The CLI and daemon work without the desktop app. Run `rnx --help`.')
313
320
  process.exit(1)
314
321
  }
315
322
 
316
323
  if (process.stdout.isTTY) process.stdout.write('\n')
317
324
  const size = statSync(downloadPath).size
318
- console.log(` downloaded ${(size / (1024 * 1024)).toFixed(1)} MB`)
325
+ console.log(` ${success('✓')} Downloaded ${(size / (1024 * 1024)).toFixed(1)} MB`)
319
326
  console.log()
320
- console.log(` installing...`)
321
327
 
322
328
  try {
323
- const installedAt = await target.install(downloadPath)
324
- console.log(` installed: ${installedAt}`)
329
+ const installedAt = await withSpinner('Installing the desktop app', () =>
330
+ target.install(downloadPath),
331
+ )
332
+ console.log(` Installed at ${installedAt}`)
325
333
  } catch (err) {
326
- console.error(` install failed: ${err instanceof Error ? err.message : String(err)}`)
327
- console.error(` keeping download at ${downloadPath} for manual install.`)
334
+ console.error(
335
+ ` ${failure('×')} Install failed: ${err instanceof Error ? err.message : String(err)}`,
336
+ )
337
+ console.error(` Keeping the download at ${downloadPath} for manual installation.`)
328
338
  process.exit(1)
329
339
  } finally {
330
340
  rmSync(workDir, { recursive: true, force: true })
331
341
  }
332
342
 
333
343
  console.log()
334
- console.log(' next steps:')
335
- console.log(' rnx desktop launch the desktop app')
336
- console.log(' rnx open <target> open a demo or bundle in it')
344
+ console.log(` ${bold('Ready')}`)
345
+ console.log(' rnx desktop Launch the desktop app')
346
+ console.log(' rnx open <target> Open a demo or bundle in it')
337
347
  }
@@ -9,8 +9,8 @@ export function printMissingCompanionHint() {
9
9
  console.error(' install the desktop app:')
10
10
  console.error(' rnx desktop install')
11
11
  console.error('')
12
- console.error(' or run setup to install the background daemon:')
13
- console.error(' rnx setup')
12
+ console.error(' or install the recommended background service:')
13
+ console.error(' rnx daemon install')
14
14
  console.error('')
15
15
  }
16
16
 
@@ -23,7 +23,7 @@ export function printMissingBridgeHint(port: number) {
23
23
  return
24
24
  }
25
25
  console.log('')
26
- console.log(' run `rnx setup` to install the background daemon,')
26
+ console.log(' run `rnx daemon install` to enable the faster background service,')
27
27
  console.log(' or run `rnx serve` in another shell for a foreground bridge.')
28
28
  }
29
29
 
@@ -6,7 +6,7 @@
6
6
  // them; electron fetches renderer assets over http from the same port.
7
7
  //
8
8
  // run in the foreground (ctrl-c to stop). the persistent launchd/systemd
9
- // daemon installation is handled by `sootsim setup`.
9
+ // daemon installation is handled by `rnx daemon install`.
10
10
 
11
11
  import { DEFAULT_SOOTSIM_BRIDGE_PORT } from '../../src/bridge-constants'
12
12
  import {