peachsim 0.1.527

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 (324) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +640 -0
  3. package/cli/app-config.ts +82 -0
  4. package/cli/app-fonts.ts +388 -0
  5. package/cli/app-project.ts +59 -0
  6. package/cli/app-splash.ts +171 -0
  7. package/cli/app-state-reset.ts +26 -0
  8. package/cli/auth.ts +184 -0
  9. package/cli/bin.ts +59 -0
  10. package/cli/bridge-diagnostics.ts +226 -0
  11. package/cli/bridge-flow-runner.ts +2806 -0
  12. package/cli/browser-backend.ts +44 -0
  13. package/cli/browser-evals.ts +96 -0
  14. package/cli/cli-update.ts +261 -0
  15. package/cli/cloud-client.ts +1530 -0
  16. package/cli/cloud-dispatch.ts +206 -0
  17. package/cli/cloud-session.ts +227 -0
  18. package/cli/command-contract.ts +221 -0
  19. package/cli/command-registry.ts +304 -0
  20. package/cli/commands/agent-wrapper.ts +1048 -0
  21. package/cli/commands/agent.ts +439 -0
  22. package/cli/commands/app-fonts.ts +99 -0
  23. package/cli/commands/assert.ts +542 -0
  24. package/cli/commands/auth.ts +71 -0
  25. package/cli/commands/box/bundle-plane.ts +71 -0
  26. package/cli/commands/box/checkout-plane.ts +256 -0
  27. package/cli/commands/box/plane-share.ts +208 -0
  28. package/cli/commands/box/rnx-command.ts +60 -0
  29. package/cli/commands/box/search-command.ts +157 -0
  30. package/cli/commands/box.ts +925 -0
  31. package/cli/commands/camera.ts +266 -0
  32. package/cli/commands/cleanup.ts +169 -0
  33. package/cli/commands/compat.ts +176 -0
  34. package/cli/commands/config.ts +34 -0
  35. package/cli/commands/control.ts +2154 -0
  36. package/cli/commands/cpu-profile.ts +941 -0
  37. package/cli/commands/daemon-mac-app.ts +165 -0
  38. package/cli/commands/daemon.ts +800 -0
  39. package/cli/commands/debug.ts +751 -0
  40. package/cli/commands/desktop.ts +39 -0
  41. package/cli/commands/detect.ts +201 -0
  42. package/cli/commands/detox.ts +481 -0
  43. package/cli/commands/device.ts +134 -0
  44. package/cli/commands/diagnose.ts +589 -0
  45. package/cli/commands/do-chain.ts +408 -0
  46. package/cli/commands/electron.ts +94 -0
  47. package/cli/commands/film.ts +379 -0
  48. package/cli/commands/flow.ts +1240 -0
  49. package/cli/commands/inspect/actions.ts +516 -0
  50. package/cli/commands/inspect/core.ts +2086 -0
  51. package/cli/commands/inspect/count.ts +17 -0
  52. package/cli/commands/inspect/describe.ts +119 -0
  53. package/cli/commands/inspect/env.ts +25 -0
  54. package/cli/commands/inspect/find.ts +169 -0
  55. package/cli/commands/inspect/get-layout.ts +53 -0
  56. package/cli/commands/inspect/keyboard.ts +54 -0
  57. package/cli/commands/inspect/list.ts +175 -0
  58. package/cli/commands/inspect/memory.ts +215 -0
  59. package/cli/commands/inspect/redaction.ts +39 -0
  60. package/cli/commands/inspect/resolve-target.ts +77 -0
  61. package/cli/commands/inspect/screens.ts +78 -0
  62. package/cli/commands/inspect/settle.ts +32 -0
  63. package/cli/commands/inspect/settling.ts +197 -0
  64. package/cli/commands/inspect/shared.ts +259 -0
  65. package/cli/commands/inspect/sleep.ts +15 -0
  66. package/cli/commands/inspect/tree.ts +23 -0
  67. package/cli/commands/inspect/url.ts +17 -0
  68. package/cli/commands/inspect/wait-event.ts +211 -0
  69. package/cli/commands/inspect/wait-idle.ts +25 -0
  70. package/cli/commands/inspect/wait-ready.ts +87 -0
  71. package/cli/commands/inspect/wait-selector.ts +59 -0
  72. package/cli/commands/inspect/wait.ts +32 -0
  73. package/cli/commands/inspect.ts +4738 -0
  74. package/cli/commands/install-cli.ts +275 -0
  75. package/cli/commands/install-desktop.ts +385 -0
  76. package/cli/commands/login.ts +357 -0
  77. package/cli/commands/logout.ts +24 -0
  78. package/cli/commands/maestro-generate.ts +375 -0
  79. package/cli/commands/maestro.ts +536 -0
  80. package/cli/commands/mode.ts +58 -0
  81. package/cli/commands/no-bridge-hint.ts +80 -0
  82. package/cli/commands/perf.ts +67 -0
  83. package/cli/commands/permissions.ts +203 -0
  84. package/cli/commands/platform.ts +410 -0
  85. package/cli/commands/preview.ts +103 -0
  86. package/cli/commands/profile.ts +106 -0
  87. package/cli/commands/react.ts +353 -0
  88. package/cli/commands/record.ts +1435 -0
  89. package/cli/commands/report-issue.ts +305 -0
  90. package/cli/commands/reset.ts +89 -0
  91. package/cli/commands/runtime.ts +348 -0
  92. package/cli/commands/screenshot-command.ts +106 -0
  93. package/cli/commands/screenshot-layers.ts +144 -0
  94. package/cli/commands/screenshot-mode.ts +38 -0
  95. package/cli/commands/screenshot.ts +492 -0
  96. package/cli/commands/screenshots-capture.ts +608 -0
  97. package/cli/commands/screenshots.ts +127 -0
  98. package/cli/commands/serve.ts +139 -0
  99. package/cli/commands/setup.ts +416 -0
  100. package/cli/commands/shell-boolean-mode.ts +82 -0
  101. package/cli/commands/skills.ts +468 -0
  102. package/cli/commands/slides.ts +357 -0
  103. package/cli/commands/state.ts +94 -0
  104. package/cli/commands/storage.ts +58 -0
  105. package/cli/commands/telemetry.ts +55 -0
  106. package/cli/commands/test.ts +535 -0
  107. package/cli/commands/three-mode.ts +771 -0
  108. package/cli/commands/timeline.ts +121 -0
  109. package/cli/commands/upgrade.ts +167 -0
  110. package/cli/commands/upload.ts +1203 -0
  111. package/cli/commands/version.ts +63 -0
  112. package/cli/commands/welcome.ts +17 -0
  113. package/cli/commands/what-happened.ts +328 -0
  114. package/cli/current-sim.ts +217 -0
  115. package/cli/desktop-companion.ts +315 -0
  116. package/cli/drivers/electron.ts +70 -0
  117. package/cli/drivers/index.ts +20 -0
  118. package/cli/drivers/playwright-provisioning.ts +184 -0
  119. package/cli/drivers/playwright-sim-host.ts +537 -0
  120. package/cli/drivers/playwright.ts +209 -0
  121. package/cli/drivers/registry.ts +65 -0
  122. package/cli/drivers/types.ts +102 -0
  123. package/cli/flow-export.ts +55 -0
  124. package/cli/flow-file.ts +142 -0
  125. package/cli/flow-live-status.ts +126 -0
  126. package/cli/flow-session.ts +140 -0
  127. package/cli/help-core.ts +143 -0
  128. package/cli/help.ts +44 -0
  129. package/cli/hidden-runtime-alias.ts +19 -0
  130. package/cli/hints.ts +214 -0
  131. package/cli/inspect-notice-state.ts +114 -0
  132. package/cli/internal-child.ts +176 -0
  133. package/cli/maestro-js.ts +323 -0
  134. package/cli/main.ts +704 -0
  135. package/cli/open-url.ts +8 -0
  136. package/cli/outbound-endpoints.ts +830 -0
  137. package/cli/parent-pid.ts +204 -0
  138. package/cli/parse-args.ts +189 -0
  139. package/cli/privacy.ts +53 -0
  140. package/cli/prompt.ts +56 -0
  141. package/cli/recording-access.ts +108 -0
  142. package/cli/registry.ts +2 -0
  143. package/cli/resolve-assets.ts +63 -0
  144. package/cli/rnx-in-process.ts +98 -0
  145. package/cli/run-registry.ts +226 -0
  146. package/cli/run-rnx-core.ts +148 -0
  147. package/cli/run-rnx.ts +137 -0
  148. package/cli/runtime-notes.ts +66 -0
  149. package/cli/runtime-summary.ts +25 -0
  150. package/cli/secure-file.ts +31 -0
  151. package/cli/self-invocation.ts +58 -0
  152. package/cli/send-to-box.ts +109 -0
  153. package/cli/setup-repository.ts +191 -0
  154. package/cli/shell-init.ts +98 -0
  155. package/cli/standalone.ts +4 -0
  156. package/cli/telemetry.ts +84 -0
  157. package/cli/ui.ts +68 -0
  158. package/cli/ws-bridge.ts +1020 -0
  159. package/detox/attempt-events.cjs +23 -0
  160. package/detox/colors.ts +54 -0
  161. package/detox/config-loader.ts +135 -0
  162. package/detox/element-types.ts +48 -0
  163. package/detox/expectations.ts +534 -0
  164. package/detox/gestures.ts +501 -0
  165. package/detox/index.ts +2694 -0
  166. package/detox/jest-preset.cjs +29 -0
  167. package/detox/jest-setup-after-env.cjs +22 -0
  168. package/detox/matchers.ts +30 -0
  169. package/detox/motion-change.cjs +109 -0
  170. package/detox/motion-change.d.cts +35 -0
  171. package/detox/navigation-lifecycle.ts +290 -0
  172. package/detox/navigation.ts +43 -0
  173. package/detox/proof-frame.cjs +114 -0
  174. package/detox/record-block-execution.cjs +78 -0
  175. package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
  176. package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
  177. package/detox/screenshots/color-test-rest-norngh.png +0 -0
  178. package/detox/screenshots/theme-blue-toggle.png +0 -0
  179. package/detox/screenshots/theme-blue.png +0 -0
  180. package/detox/screenshots/theme-red-toggle.png +0 -0
  181. package/detox/screenshots/theme-red.png +0 -0
  182. package/dist-lib/agent-daemon-client.cjs +502 -0
  183. package/dist-lib/agent-events.cjs +48 -0
  184. package/dist-lib/agent-identity.cjs +53 -0
  185. package/dist-lib/agent-sessions.cjs +759 -0
  186. package/dist-lib/attached-projects.cjs +526 -0
  187. package/dist-lib/auth/shared-session.cjs +208 -0
  188. package/dist-lib/backend-origin.cjs +88 -0
  189. package/dist-lib/beta.cjs +41 -0
  190. package/dist-lib/beta.mjs +13 -0
  191. package/dist-lib/bridge-constants.cjs +161 -0
  192. package/dist-lib/bridge-contract-input.cjs +451 -0
  193. package/dist-lib/bridge-contract-input.mjs +414 -0
  194. package/dist-lib/bridge-contract.cjs +130 -0
  195. package/dist-lib/bridge-contract.mjs +94 -0
  196. package/dist-lib/capture-contract.cjs +220 -0
  197. package/dist-lib/capture-contract.mjs +190 -0
  198. package/dist-lib/cli-constants.cjs +32 -0
  199. package/dist-lib/cloud-contract.cjs +194 -0
  200. package/dist-lib/cloud-contract.mjs +145 -0
  201. package/dist-lib/cloud.cjs +670 -0
  202. package/dist-lib/cloud.mjs +639 -0
  203. package/dist-lib/config.cjs +96 -0
  204. package/dist-lib/detox/index.cjs +3789 -0
  205. package/dist-lib/detox/jest-preset.cjs +29 -0
  206. package/dist-lib/dev-bundle-resolution.cjs +290 -0
  207. package/dist-lib/home-paths.cjs +638 -0
  208. package/dist-lib/host/bridge-host.cjs +10903 -0
  209. package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
  210. package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
  211. package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
  212. package/dist-lib/host/replacement-module-handler.cjs +469 -0
  213. package/dist-lib/host/websocket-proxy.cjs +298 -0
  214. package/dist-lib/index.cjs +4268 -0
  215. package/dist-lib/jump-to-source-babel.cjs +93 -0
  216. package/dist-lib/jump-to-source-native.cjs +112 -0
  217. package/dist-lib/menu.cjs +646 -0
  218. package/dist-lib/menu.mjs +604 -0
  219. package/dist-lib/metro-fingerprint-registry.cjs +186 -0
  220. package/dist-lib/metro-fingerprint-registry.mjs +156 -0
  221. package/dist-lib/metro-production-bundle.cjs +329 -0
  222. package/dist-lib/metro-production-bundle.mjs +295 -0
  223. package/dist-lib/metro.cjs +904 -0
  224. package/dist-lib/profiles.cjs +246 -0
  225. package/dist-lib/public-brand.cjs +39 -0
  226. package/dist-lib/react-native-host-modules.cjs +51 -0
  227. package/dist-lib/react-native-host-modules.mjs +25 -0
  228. package/dist-lib/render-mode.cjs +54 -0
  229. package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
  230. package/dist-lib/sdk.cjs +2433 -0
  231. package/dist-lib/sdk.mjs +2336 -0
  232. package/dist-lib/skills.cjs +39132 -0
  233. package/dist-lib/swift-submit.cjs +92 -0
  234. package/dist-lib/swift-submit.mjs +65 -0
  235. package/dist-lib/swift.cjs +1646 -0
  236. package/dist-lib/vite.cjs +1591 -0
  237. package/package.json +290 -0
  238. package/scripts/dev-server-scanner.ts +1291 -0
  239. package/scripts/optional-demo-registry.ts +113 -0
  240. package/skills/contrast/SKILL.md +165 -0
  241. package/skills/rnx-debug/SKILL.md +433 -0
  242. package/skills/rnx-perf/SKILL.md +399 -0
  243. package/skills/rnx-setup/SKILL.md +172 -0
  244. package/skills/rnx-test/SKILL.md +321 -0
  245. package/skills/rnx-visual/SKILL.md +126 -0
  246. package/src/agent-daemon-client.ts +410 -0
  247. package/src/agent-events.ts +71 -0
  248. package/src/agent-identity.ts +23 -0
  249. package/src/agent-prompt.ts +71 -0
  250. package/src/agent-sessions.ts +580 -0
  251. package/src/app-url.ts +39 -0
  252. package/src/attached-projects.ts +539 -0
  253. package/src/auth/shared-session.ts +235 -0
  254. package/src/backend-origin.ts +70 -0
  255. package/src/beta.ts +19 -0
  256. package/src/bridge-constants.ts +175 -0
  257. package/src/bridge-contract-input.ts +502 -0
  258. package/src/bridge-contract.ts +558 -0
  259. package/src/browser-cache.ts +16 -0
  260. package/src/capture-contract.ts +587 -0
  261. package/src/cli-constants.ts +1 -0
  262. package/src/cli-version.ts +40 -0
  263. package/src/cloud-contract.ts +274 -0
  264. package/src/cloud.ts +762 -0
  265. package/src/codex-client.ts +234 -0
  266. package/src/config.ts +132 -0
  267. package/src/connect.ts +101 -0
  268. package/src/dev-bundle-resolution.ts +346 -0
  269. package/src/dev-server-open.ts +198 -0
  270. package/src/disk-cleanup.ts +540 -0
  271. package/src/flow-timeout-scale.ts +15 -0
  272. package/src/home-paths.ts +806 -0
  273. package/src/host/agent-host.ts +666 -0
  274. package/src/host/bridge-host.ts +2788 -0
  275. package/src/host/fetch-proxy-handler.ts +449 -0
  276. package/src/host/fetch-proxy-overrides.ts +51 -0
  277. package/src/host/open-url.ts +89 -0
  278. package/src/host/plane-host.ts +203 -0
  279. package/src/host/replacement-module-handler.ts +158 -0
  280. package/src/host/websocket-proxy.ts +315 -0
  281. package/src/index.ts +71 -0
  282. package/src/jump-to-source-babel.ts +135 -0
  283. package/src/jump-to-source-native.ts +102 -0
  284. package/src/menu.ts +777 -0
  285. package/src/metro-fingerprint-registry.ts +302 -0
  286. package/src/metro-plugin.ts +341 -0
  287. package/src/metro-production-bundle.ts +471 -0
  288. package/src/native-dev-bundle-url.ts +118 -0
  289. package/src/native-seam-manifest.ts +341 -0
  290. package/src/playwright-host-log.ts +16 -0
  291. package/src/plist.ts +27 -0
  292. package/src/poll-until.ts +123 -0
  293. package/src/profiles.ts +179 -0
  294. package/src/public-brand.ts +9 -0
  295. package/src/react-native-host-modules.ts +26 -0
  296. package/src/render-mode.ts +27 -0
  297. package/src/rolldown-plugin-swift.ts +240 -0
  298. package/src/runtime-assets.ts +233 -0
  299. package/src/runtime-delivery.ts +54 -0
  300. package/src/screenshots/compose.ts +422 -0
  301. package/src/screenshots/frame-compose.ts +514 -0
  302. package/src/screenshots/orchestrate.ts +256 -0
  303. package/src/screenshots/org-deck.ts +91 -0
  304. package/src/screenshots/registry.ts +58 -0
  305. package/src/screenshots/schema.ts +364 -0
  306. package/src/sdk.ts +15 -0
  307. package/src/settings-bundle.ts +418 -0
  308. package/src/shell-bridge-identity.ts +15 -0
  309. package/src/sim-client.ts +697 -0
  310. package/src/skills/builtin/a11y-review.ts +132 -0
  311. package/src/skills/builtin/compat-check.ts +71 -0
  312. package/src/skills/builtin/maestro-test.ts +121 -0
  313. package/src/skills/builtin/perf-profile.ts +90 -0
  314. package/src/skills/builtin/screenshot-all.ts +52 -0
  315. package/src/skills/builtin/visual-diff.ts +100 -0
  316. package/src/skills/registry.ts +107 -0
  317. package/src/skills/types.ts +41 -0
  318. package/src/swift-submit.ts +185 -0
  319. package/src/swift.ts +7 -0
  320. package/src/team-machine-prompt.ts +73 -0
  321. package/src/vite-plugin-one.ts +264 -0
  322. package/src/vite-plugin-swift.ts +620 -0
  323. package/src/vite-plugin.ts +1946 -0
  324. package/src/worklets-babel.ts +250 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Proprietary Software License
2
+
3
+ Copyright (c) Tamagui LLC. All rights reserved.
4
+
5
+ This package (rnxsim) is proprietary software of Tamagui LLC, licensed under
6
+ the rnx Software License Agreement: https://rnxsim.com/legal/license
7
+
8
+ In summary (the agreement governs): personal, non-commercial use is free;
9
+ commercial use requires an active Pro subscription after a single 30-day
10
+ evaluation period; embedding or redistributing the software requires a
11
+ separate written agreement with Tamagui LLC.
12
+
13
+ Versions of this package published before this license change remain under
14
+ the license they were published with.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,640 @@
1
+ # rnx
2
+
3
+ The source owner for the standalone `rnx` executable and the public `rnxsim`
4
+ integration library used to drive a browser-native React Native simulator.
5
+
6
+ ## Overview
7
+
8
+ The CLI and library are separate public artifacts. The recommended installer
9
+ downloads one Bun-compiled `rnx` executable that needs no Node.js, npm, or Bun
10
+ installation. The `rnxsim` npm package contains bundler plugins, testing
11
+ drivers, and library exports, with no command-line binary.
12
+
13
+ The executable updates itself with `rnx upgrade`. Successful interactive human
14
+ commands refresh a cached stable CLI version in a detached process at most once
15
+ every 20 hours. A newer version produces one short notice on stderr, once per
16
+ version and no more than weekly. CI, agents, JSON output, pipes, and
17
+ noninteractive commands stay silent. Set `RNX_UPDATE_CHECK=off` to disable the
18
+ check and notice; `rnx version` checks explicitly, while `rnx --version` remains
19
+ offline.
20
+
21
+ Neither artifact contains rendering code. The canvas engine (CanvasKit
22
+ renderer, Yoga layout, iOS/Android shell chrome, Electron shell) lives in the
23
+ private `sootsim-engine` workspace package and is shipped at runtime as a
24
+ versioned tarball, fetched from the contrast.dev CDN and unpacked into `~/.rnx`.
25
+
26
+ What this workspace gives you:
27
+
28
+ - the standalone `rnx` CLI source and binary build, which provides the primary
29
+ debugging and automation surface (inspect the UI tree, tap elements, capture
30
+ flows, screenshot, record, run agents)
31
+ - `rnxsim/vite` and `rnxsim/metro` — bundler plugins that serve the installed
32
+ engine runtime from your own dev server
33
+ - `rnxsim/jump-to-source-babel` — an optional Babel plugin that annotates JSX
34
+ with source locations so inspect mode can open the selected element in your
35
+ editor
36
+ - `rnxsim/detox` — a drop-in Detox driver + jest preset
37
+ - `rnxsim/sdk` — the inspect/interact verbs as a programmatic API
38
+ - `rnxsim/bridge-contract-input` — parsers turning untrusted JSON into
39
+ bridge-contract arguments
40
+ - `rnxsim/skills` — a registry of Contrast-flavored automation skills
41
+
42
+ Platform status: iOS is the parity baseline. Android is bootstrapped and usable
43
+ for focused conformance slices (bundle/runtime identity, device profiles, shell
44
+ chrome, gesture + three-button nav, system UI/window metrics with cutout-safe
45
+ insets) but is not full parity until the Android launch gates in the Contrast
46
+ repo plan pass.
47
+
48
+ ## What's in each distribution
49
+
50
+ | standalone `rnx` executable | `rnxsim` npm package | private engine runtime |
51
+ | --- | --- | --- |
52
+ | CLI commands and bridge client | Vite and Metro plugins | CanvasKit renderer and Yoga layout |
53
+ | bridge daemon host | SDK and bridge contracts | iOS/Android shell chrome, home grid, app switcher |
54
+ | runtime, browser, and desktop launchers | Detox driver and Jest preset | Electron shell and wasm |
55
+ | Maestro, screenshots, recording, skills | native-resolution helpers and skills registry | the actual pixels |
56
+
57
+ The engine is delivered at runtime, never bundled into this package. A
58
+ committed-but-unbuilt engine fix has no effect on a running sim until the
59
+ runtime tarball is rebuilt and re-fetched.
60
+
61
+ ## Architecture
62
+
63
+ The architecture is a **three-tier relay**: a short-lived CLI process, a single
64
+ persistent bridge daemon, and the sim itself — a browser/Electron *page* that
65
+ internally splits into shell, compositor, and tenant workers.
66
+
67
+ ```mermaid
68
+ graph TD
69
+ subgraph cli["rnx CLI process (short-lived, cli/)"]
70
+ BIN["bin.ts (dispatcher + privacy choice)"]
71
+ SETUP["commands/setup.ts (guided daemon setup)"]
72
+ RUNTIMECMD["commands/runtime.ts (runtime install/use)"]
73
+ INSPECT["commands/inspect/* (describe/find/do/get/wait)"]
74
+ FLOW["bridge-flow-runner.ts (Maestro YAML)"]
75
+ DETOXCMD["commands/detox.ts + detox/ driver"]
76
+ SHOTS["commands/screenshot + record"]
77
+ AGENTCMD["commands/agent.ts"]
78
+ WSB["ws-bridge.ts (WsBridge client)"]
79
+ DRIVERS["drivers/* (chromium/electron/playwright/system)"]
80
+ end
81
+ subgraph daemon["bridge daemon process (persistent :7668)"]
82
+ HOST["SootSimBridgeHost (host/bridge-host.ts, HTTP+WS)"]
83
+ AGENTHOST["AgentHost (host/agent-host.ts, FIFO fan-out)"]
84
+ AGENTSESS["agent-sessions.ts + attached-projects.ts"]
85
+ SCAN["/__server-scan (dev-server-scanner.ts)"]
86
+ PROXY["fetch-proxy-handler + websocket-proxy"]
87
+ RTHTTP["runtime HTTP + self-update (runtime-delivery.ts)"]
88
+ end
89
+ subgraph sim["sim page (browser / Electron, launched by a driver)"]
90
+ SHELLW["shell worker (iOS chrome, home grid, app switcher)"]
91
+ COMPOSITORW["compositor worker (app canvases + independent rAF)"]
92
+ TENANTW["tenant worker (guest RN app tree)"]
93
+ end
94
+ subgraph build["build-time plugins (src/)"]
95
+ VITEONE["rnxsim/vite = vite-plugin-one.ts (serve runtime at /__soot/)"]
96
+ VITERN["sootsim() = vite-plugin.ts (RN resolver + native stubs)"]
97
+ METRO["rnxsim/metro = metro-plugin.ts"]
98
+ COMPAT["@sootsim/compat stub-manifest (native seams)"]
99
+ ENGINESHIM["sootsim-engine react-native shim"]
100
+ end
101
+ SKILLS["rnxsim/skills registry.ts (builtin skills)"]
102
+ subgraph fs["~/.rnx (home-paths.ts)"]
103
+ RTDIR["runtimes/<version>/ (engine assets)"]
104
+ LOCK["daemon.json lockfile"]
105
+ CFG["config.json"]
106
+ end
107
+ CDN["contrast.dev CDN (runtimes/manifest.json + tarballs)"]
108
+ BIN --> SETUP
109
+ BIN --> RUNTIMECMD
110
+ BIN --> INSPECT
111
+ BIN --> FLOW
112
+ BIN --> DETOXCMD
113
+ BIN --> SHOTS
114
+ BIN --> AGENTCMD
115
+ SETUP --> LOCK
116
+ SETUP -->|"register launchd/systemd"| HOST
117
+ RUNTIMECMD --> RTDIR
118
+ RUNTIMECMD -->|"fetch runtime"| CDN
119
+ INSPECT --> WSB
120
+ FLOW --> WSB
121
+ SHOTS --> WSB
122
+ AGENTCMD --> WSB
123
+ DETOXCMD --> DRIVERS
124
+ DETOXCMD --> WSB
125
+ WSB -->|"JSON WS cmds"| HOST
126
+ HOST -->|"forward to registered sim"| SHELLW
127
+ SHELLW -->|"replies + pushes"| HOST
128
+ HOST -->|"relayed results"| WSB
129
+ HOST --> AGENTHOST
130
+ AGENTHOST --> AGENTSESS
131
+ AGENTCMD --> AGENTHOST
132
+ HOST --> SCAN
133
+ HOST --> PROXY
134
+ HOST --> RTHTTP
135
+ RTHTTP --> RTDIR
136
+ RTHTTP -->|"manifest + tarball"| CDN
137
+ DRIVERS -->|"open sim page URL"| SHELLW
138
+ SHELLW -->|"NativeUIRequest"| TENANTW
139
+ VITEONE -->|"serve /__soot/ from"| RTDIR
140
+ METRO -->|"serve /__soot/ from"| RTDIR
141
+ VITEONE -.->|"launch electron app"| SHELLW
142
+ VITERN -->|"alias react-native"| ENGINESHIM
143
+ VITERN -->|"alias native pkgs"| COMPAT
144
+ VITERN -->|"dev server hosts"| HOST
145
+ SKILLS --> INSPECT
146
+ ```
147
+
148
+ **The CLI process** (`cli/bin.ts` routes, `cli/main.ts` parses argv) lazy-imports
149
+ exactly one command module per run. Bare `rnx` opens ConnectRN, whose simulator UI discovers
150
+ running React Native development servers. The first human run offers the
151
+ recommended background service and optional Desktop app, then opens the simulator
152
+ before asking about private diagnostics or an optional repository install. `rnx open`
153
+ ensures the engine runtime exists and starts a local foreground bridge when no
154
+ daemon is reachable, then opens a `WsBridge` (`cli/ws-bridge.ts`) on **port
155
+ 7668** and sends JSON commands. Inspection/interaction verbs live in
156
+ `cli/commands/inspect/*` and are re-exported as the programmatic `rnxsim/sdk`;
157
+ the Maestro runner is `cli/bridge-flow-runner.ts`; the Detox compat driver
158
+ is in `detox/`. Test and screenshot paths additionally use `cli/drivers/*`.
159
+ Browser sims launch in an isolated Playwright Chrome for Testing profile;
160
+ Electron is the native desktop surface.
161
+
162
+ **The daemon is one process** — `SootSimBridgeHost` (`src/host/bridge-host.ts`).
163
+ `rnx serve` runs it in the foreground; the background daemon is the *same
164
+ process* wrapped with autostart and the `~/.rnx/daemon.json` lockfile. It is
165
+ an HTTP+WS hub: sims register over WS, CLI clients connect to drive them, and
166
+ the host forwards each command to the targeted sim and relays the reply back. It
167
+ also serves the engine runtime over HTTP (with a self-update route), exposes
168
+ `/__server-scan` to discover local metro/expo/vxrn/one dev servers, and proxies
169
+ guest-app fetch/WebSocket traffic so the cross-origin tenant worker can reach
170
+ localhost. The `AgentHost` extension owns one FIFO reader per agent session and
171
+ fans agent events out to every subscriber.
172
+
173
+ During source development, each Vite bridge owns one
174
+ `~/.rnx/dev-bridges/<port>.json` heartbeat record. CLI defaults select the
175
+ current `PORT_OFFSET`, then the current checkout, then the canonical `:7668`
176
+ bridge, so concurrent worktrees cannot replace or delete one another's address.
177
+ Each Vite shell also publishes its exact bridge at `/__sootsim/bridge`, so a
178
+ launcher that reuses a shell from another isolated `RNX_HOME` stays in that
179
+ shell's bridge world without reading the other process's lockfile directory.
180
+
181
+ **The sim** is a browser/Electron *page* launched by a CLI driver. Inside that
182
+ page run three workers: the **shell worker** owns device and native state, the
183
+ **compositor worker** paints every visible CanvasKit surface, and the **tenant
184
+ worker** runs the guest RN tree and sends native-UI requests to the shell. The
185
+ bridge always addresses the **shell worker**, never an undifferentiated sim.
186
+
187
+ **Two distinct plugin families** round it out, and they must not be conflated:
188
+
189
+ - The published **`rnxsim/vite`** (`src/vite-plugin-one.ts`, `rnxPlugin`)
190
+ and **`rnxsim/metro`** plugins do one thing: serve the installed runtime at
191
+ `/__soot/` so an app's own dev server can host the sim shell. The Vite plugin accepts an
192
+ opt-in `open` option that launches a named Electron window when the dev server
193
+ starts (skipped when `CI` or `RNX_NO_OPEN` is set).
194
+ - The internal **`sootsim()`** plugin (`src/vite-plugin.ts`) is a separate, much
195
+ larger RN-resolution / native-stub plugin: it aliases `react-native` to the
196
+ engine shim and native packages to `@sootsim/compat` stubs, applies worklets
197
+ transforms, and is the one that instantiates `SootSimBridgeHost` inside its
198
+ own dev server. It is used to build the engine/shell and to load external RN
199
+ apps — it is **not** the published `rnxsim/vite` export.
200
+
201
+ ## Key components
202
+
203
+ | component | role | key files |
204
+ | --- | --- | --- |
205
+ | rnx CLI (bin + dispatcher) | Short-lived terminal entry point. Parses argv, owns the private-by-default one-time choice, lazy-imports one command per run, and routes direct open/runtime/inspection flows. | `cli/bin.ts`, `cli/main.ts`, `cli/privacy.ts`, `cli/parse-args.ts`, `cli/help.ts`, `cli/commands/setup.ts`, `cli/commands/control.ts` |
206
+ | WsBridge client | Client side of the CLI→daemon link. Resolves the daemon port from the lockfile, opens a WS to `:7668`, sends JSON commands and awaits `{id,result}`/`{id,error}`. Used by every interactive command. | `cli/ws-bridge.ts`, `src/bridge-constants.ts`, `cli/current-sim.ts` |
207
+ | inspect / SDK command surface | Runtime inspection + interaction verbs (describe, find, do tap, get errors, wait, layout, settle). Re-exported as `rnxsim/sdk` so programmatic callers drive a sim the same way the CLI does. | `cli/commands/inspect/core.ts`, `cli/commands/inspect/actions.ts`, `cli/commands/inspect.ts`, `src/sdk.ts` |
208
+ | bridge-contract input parsers | Turn untrusted JSON into typed bridge-contract arguments, so a transport in front of a sim validates request bodies once instead of writing a second reader. | `src/bridge-contract-input.ts`, `src/bridge-contract.ts` |
209
+ | Maestro / Detox runners | Drop-in test compatibility. `bridge-flow-runner.ts` runs Maestro YAML and recorded flows over the bridge; `detox/` provides a `by`/`element`/`expect`/`device` driver + jest preset so existing Detox suites run unchanged. | `cli/bridge-flow-runner.ts`, `cli/commands/maestro.ts`, `cli/commands/detox.ts`, `detox/index.ts`, `detox/jest-preset.cjs` |
210
+ | sim drivers | Launch or attach the actual sim page. Playwright owns isolated browser profiles; Electron owns the native desktop surface. The current sim is reused unless `--new`, `--profile`, or `--ephemeral` requests a separate one. | `cli/drivers/index.ts`, `cli/drivers/registry.ts`, `cli/drivers/electron.ts`, `cli/drivers/playwright.ts` |
211
+ | SootSimBridgeHost (serve/daemon) | The bridge process (`rnx serve`, or autostarted as the daemon via launchd/systemd). HTTP+WS hub on `:7668`: relays commands between CLI clients and registered sims; also serves runtime HTTP, `/__server-scan`, and fetch/WS proxies. | `src/host/bridge-host.ts`, `cli/commands/serve.ts`, `cli/commands/daemon.ts`, `src/home-paths.ts` |
212
+ | AgentHost + sessions | Agent-routing extension of the host: owns the single FIFO reader per agent session and fans `agent:event`/`session-status` pushes to every WS subscriber. Backed by agent-sessions + attached-projects stores. | `src/host/agent-host.ts`, `src/agent-sessions.ts`, `src/attached-projects.ts`, `src/agent-daemon-client.ts` |
213
+ | host proxies + dev-server scan | Host-side helpers so a cross-origin tenant worker can reach local dev servers and same-origin APIs: `/__server-scan` discovers running metro/expo/vxrn/one servers; fetch + websocket proxies relay guest-app network through the daemon. | `src/host/fetch-proxy-handler.ts`, `src/host/websocket-proxy.ts`, `scripts/dev-server-scanner.ts`, `src/dev-bundle-resolution.ts` |
214
+ | runtime delivery + rnx home | Versioned engine runtime management. The shared machinery owns manifest fetch, sha256 verification, channels, and auto-update. rnx binds it to `https://contrast.dev` and `~/.rnx/runtimes/<version>`. A repo can select `runtimeVersion` without changing the machine default. | `src/runtime-delivery.ts`, `packages/contrast-runtime-delivery/`, `src/home-paths.ts`, `src/runtime-assets.ts` |
215
+ | standalone CLI delivery | Cached stable-version checks, bounded interactive notices, and verified atomic executable replacement. | `cli/cli-update.ts`, `cli/commands/upgrade.ts`, `cli/commands/version.ts`, `src/cli-version.ts` |
216
+ | `rnxsim/vite` + `rnxsim/metro` (runtime serving) | Published bundler plugins. Both serve the installed engine runtime at `/__soot/` from `~/.rnx/runtimes/<version>` so an app's own dev server can host the sim shell; the Vite plugin can opt into a named Electron window. | `src/vite-plugin-one.ts`, `src/metro-plugin.ts`, `src/runtime-assets.ts` |
217
+ | `sootsim()` vite resolver plugin (internal) | The large RN-resolution / native-stub vite plugin: aliases `react-native` to the engine shim, native packages to `@sootsim/compat` stubs, applies worklets/babel transforms, and instantiates `SootSimBridgeHost` in its dev server. Distinct from the published `rnxsim/vite` export. | `src/vite-plugin.ts`, `src/worklets-babel.ts`, `packages/compat/src/stub-manifest.ts` |
218
+ | skills registry | rnx agent skills (`rnx-setup`, `rnx-debug`, `rnx-test`, `rnx-visual`, and the generated `contrast` index) installable with `rnx skill`. | `skills/*.md`, `src/skills/registry.ts`, `src/skills/types.ts` |
219
+
220
+ ## Getting started
221
+
222
+ ```sh
223
+ curl -fsSL https://rnxsim.com/install.sh | sh
224
+ rnx # open ConnectRN and choose a running app in the simulator
225
+ rnx open /settings # dispatch a React Native deep link into that app
226
+ ```
227
+
228
+ Start Metro, Expo, One, or another supported React Native development server
229
+ the way you normally do, then run `rnx` from any directory. The simulator
230
+ opens ConnectRN, which shows the available local apps and lets you connect one.
231
+ Pass a target to `rnx open <port-or-url>` when a script or agent needs an explicit
232
+ selection.
233
+
234
+ The first `rnx open` launches an isolated Chrome for Testing sim. The CLI
235
+ resolves Playwright from the app or CLI install, asks that exact package for
236
+ its required browser executable, and runs that package's installer once when
237
+ the executable is absent. It never substitutes another revision found in the
238
+ shared Playwright cache. Later opens reuse that sim and reload it at the
239
+ requested target. Use `rnx do reload` when the target has not changed.
240
+ Reserve `--new` for a genuinely concurrent sim; repeated new browser trees
241
+ consume memory and CPU on shared machines.
242
+
243
+ The public installer places one standalone `rnx` executable in `~/.local/bin`.
244
+ The `rnxsim` npm package contains bundler plugins and library exports, not a CLI
245
+ binary. Start your Metro, Expo, or One dev server the way you already do, then
246
+ open it from the CLI. There is no iOS native build in the inner loop; Metro alone is
247
+ enough. The manifest bundle path and query remain authoritative. When both the
248
+ discovery request and manifest target are loopback addresses, RNX keeps the
249
+ loopback host that answered discovery so an IPv6-only listener is not reopened
250
+ through an unreachable IPv4 address. `rnx open` starts a local bridge when no
251
+ daemon is reachable. The background service is strongly recommended for local
252
+ agent work because inspect, interaction, and test commands reuse one ready bridge
253
+ and runtime. Enable it at any time with `rnx daemon install`; it is never required
254
+ in CI. The packaged Desktop app is optional and available through first-run
255
+ onboarding or `rnx desktop install`. A downloaded app starts its own bundled
256
+ daemon on first launch, then offers **Install the rnx command** over the running
257
+ simulator. That action installs `~/.local/bin/rnx`, updates shell startup, and
258
+ hands the daemon to launchd or systemd without reloading the window. Windows
259
+ keeps the desktop-owned daemon because it has no daemon service yet. Native Electron windows created by `rnx open` belong to that CLI
260
+ session and close when the session exits; `rnx desktop` keeps the persistent
261
+ desktop-app lifecycle. In Electron, `File > New Window`
262
+ duplicates the focused simulator, while `File > New Simulator >` opens a new
263
+ window for a selected device (same device list as the `Window >` menu).
264
+
265
+ ### Privacy
266
+
267
+ rnx sends no analytics, crash report, command event, app data, project data, or
268
+ identity by default. An unsigned-in interactive run may request the runtime it
269
+ is about to execute and the stable standalone CLI version document. The latter
270
+ runs after the command in a detached process at most once every 20 hours and can
271
+ be disabled with `RNX_UPDATE_CHECK=off`. The first human run asks once about
272
+ bounded diagnostics, defaults to no, and never prompts in a non-TTY shell, CI,
273
+ or an agent. An automated first run is declined for that process without
274
+ consuming the later human choice. Legacy browser and Electron telemetry senders
275
+ are removed rather than being enabled by this consent.
276
+
277
+ Opt-in diagnostics are limited to package name/version/unsupported API or error
278
+ class. rnx never sends source, file paths, project names, app content, commands,
279
+ identity, screenshots, or recordings as diagnostics. Cloud commands such as
280
+ login, upload, billing, issue reporting, generated flows, and screenshot decks
281
+ contact their documented service only when explicitly invoked. The complete
282
+ enforced network contract is in [`specs/rnx-privacy.md`](../../specs/rnx-privacy.md).
283
+
284
+ `rnx daemon uninstall` removes the machine daemon: it stops current and legacy
285
+ launchd / systemd services and removes their logs, launcher, and generated
286
+ `.app` wrapper. `~/.rnx/` survives, because runtimes, device profiles and their
287
+ app storage, and live dev-bridge records belong to simulators the user may
288
+ still be running. `rnx daemon uninstall --purge` deletes `~/.rnx/` as well.
289
+ Neither form removes the optional `/Applications/rnx.app` desktop bundle or the
290
+ macOS preference plist. To inspect existing disk usage first, run `rnx cleanup`;
291
+ add `--apply` only after reviewing the preview.
292
+
293
+ ## Jump to source in inspect mode
294
+
295
+ Inspect mode can open a selected React Native element in your editor when the
296
+ bundle includes source metadata. Add the optional Babel plugin to your app's
297
+ Metro/Babel config:
298
+
299
+ ```js
300
+ plugins: [
301
+ [
302
+ 'rnxsim/jump-to-source-babel',
303
+ {
304
+ include: [__dirname],
305
+ },
306
+ ],
307
+ 'react-native-worklets/plugin',
308
+ ]
309
+ ```
310
+
311
+ The plugin adds an `srcloc="/absolute/path/App.tsx:12:3"` prop to JSX elements
312
+ outside `node_modules`, `dist`, and `build`. Keep it before
313
+ `react-native-worklets/plugin` in a custom Babel stack that lists both plugins.
314
+ Apps using `babel-preset-expo` omit the explicit Worklets plugin because the
315
+ preset configures it automatically.
316
+
317
+ For One's Rolldown native bundler, use the native transform in `vite.config.ts`:
318
+
319
+ ```ts
320
+ import { jumpToSourceNativePlugin } from 'rnxsim/jump-to-source-native'
321
+
322
+ one({
323
+ native: {
324
+ bundler: 'vite',
325
+ bundlerOptions: {
326
+ plugins: [jumpToSourceNativePlugin({ include: [import.meta.dirname] })],
327
+ },
328
+ },
329
+ })
330
+ ```
331
+
332
+ This transform uses the same source props and filters without running Babel.
333
+ For One's `nativeTransformModules`, export
334
+ `createJumpToSourceNativeTransform(options)` from a module and pass its module id.
335
+
336
+ ## CLI
337
+
338
+ The CLI is the primary debugging surface for rnx — use it first for runtime
339
+ inspection, interaction, animation debugging, shell tracing, screenshots, and
340
+ flow capture, not just as a test runner.
341
+
342
+ ```sh
343
+ rnx list # connected tabs
344
+ rnx open 8081 # load a running metro/expo dev server
345
+ rnx open /settings # route via React Native Linking
346
+ rnx describe # dump UI tree
347
+ rnx get errors 5 # recent runtime failures
348
+ rnx compat # scan native package compatibility
349
+ rnx report-issue "…" # preview an opt-in compatibility report
350
+ rnx find --testid cta # inspect one node deeply
351
+ rnx debug enable animated,layout
352
+ rnx debug trace shell on 240
353
+ rnx do tap-text "..." # interact
354
+ rnx do tap email --then type user@example.com --then tap submit
355
+ rnx debug snapshot before
356
+ rnx debug snapshot after
357
+ rnx debug diff before after
358
+ rnx maestro start # begin a Maestro draft
359
+ rnx maestro keep # persist the last action
360
+ rnx maestro end --output .maestro/login.yaml --validate
361
+ rnx detox # run Detox/Jest suites against rnx
362
+ ```
363
+
364
+ See the full command reference via `rnx --help` or at
365
+ `src/features/site/docs/rnx/cli/` in the Contrast repo.
366
+
367
+ Sim-scoped commands use the primary fallback only when one driveable sim is
368
+ connected. With multiple driveable sims, the bridge lists their ids and refuses
369
+ to choose. Run `rnx use <id>` to pin later commands, or pass `--sim <id>` to
370
+ target one command.
371
+
372
+ Use an inline `rnx do` chain for a short action sequence that should execute as
373
+ one engine batch. Repeat `--then` before each additional action. Each phrase
374
+ uses the same positional grammar as its standalone `do` command. Touch timing
375
+ stays inside the sim, the batch stops at the first failure, and `--json` prints
376
+ the complete per-step result. Reusable file-based flows stay in Maestro YAML.
377
+
378
+ ```sh
379
+ rnx do tap email --then type user@example.com --then tap submit
380
+ ```
381
+
382
+ ## Bundler plugins (`rnxsim/vite` and `rnxsim/metro`)
383
+
384
+ The Vite plugin serves the installed RNX runtime from your existing dev server.
385
+
386
+ ```ts
387
+ // vite.config.ts
388
+ import { rnxPlugin } from 'rnxsim/vite'
389
+
390
+ export default {
391
+ plugins: [rnxPlugin({ open: { appName: 'My App' } })],
392
+ }
393
+ ```
394
+
395
+ ```js
396
+ // metro.config.js
397
+ const { getDefaultConfig } = require('expo/metro-config')
398
+ const { withRNX } = require('rnxsim/metro')
399
+
400
+ const config = getDefaultConfig(__dirname)
401
+
402
+ module.exports = withRNX(config)
403
+ ```
404
+
405
+ rnx works directly with regular, unannotated Metro production JavaScript bundles
406
+ out of the box. It identifies modules by matching package graph fingerprints
407
+ against a separately updated, integrity-checked registry. Hermes bytecode is not
408
+ a supported input; pass rnx the JavaScript bundle produced before Hermes
409
+ compilation.
410
+
411
+ If an app has unusual dependencies or fingerprint inference fails, you can
412
+ optionally wrap your Metro configuration with `withRNX(config)`. It takes no
413
+ options and enables two integrations:
414
+
415
+ 1. **Deterministic module identity:** appends Metro's exact module identity map
416
+ as a trailing `globalThis.__sootsimModulePaths = { ... }` footer to every
417
+ bundle. Resolution, transforms, minification, and source maps stay the app's
418
+ own, so the annotated bundle is the exact bundle you ship: the footer assigns
419
+ one unused global on a device, and Hermes drops it during bytecode compilation.
420
+ 2. **In-browser dev shell:** serves the rnx simulator shell at `/__soot/` on
421
+ the Metro dev server.
422
+
423
+ The URL helpers request ordinary Metro graphs: `dev=false&minify=true` for
424
+ production and `dev=true&minify=false` for development.
425
+
426
+ ```js
427
+ const {
428
+ toRNXProductionBundleUrl,
429
+ toRNXDevelopmentBundleUrl,
430
+ } = require('rnxsim/metro')
431
+
432
+ const productionUrl = toRNXProductionBundleUrl(
433
+ 'http://localhost:8081/index.bundle?platform=ios',
434
+ )
435
+ const developmentUrl = toRNXDevelopmentBundleUrl(
436
+ 'http://localhost:8081/index.bundle?platform=ios',
437
+ )
438
+ ```
439
+
440
+ ## Coming from Detox
441
+
442
+ rnx ships a drop-in Detox driver. Existing Detox test files
443
+ (`import { by, element, expect, device } from 'detox'`) run against rnx with
444
+ no code changes — just add one line to your jest config:
445
+
446
+ ```js
447
+ // jest config
448
+ { preset: 'rnxsim/detox' }
449
+ ```
450
+
451
+ Or use the CLI directly:
452
+
453
+ ```sh
454
+ rnx detox # auto-discovers e2e/ tests and launches a shell
455
+ rnx detox init # scaffold a config + sample test
456
+ ```
457
+
458
+ See `../../docs/migrating-from-detox.md` for details.
459
+
460
+ ## Coming from Maestro
461
+
462
+ Maestro is rnx's single YAML test surface. Point it at your existing
463
+ `.maestro/` directory, generate a test, or author one from live actions:
464
+
465
+ ```sh
466
+ rnx maestro # discover .maestro/ and run all flows
467
+ rnx maestro test .maestro/ # explicit
468
+ rnx maestro generate "verify login"
469
+ rnx maestro --list-compat # see the verb support matrix
470
+ ```
471
+
472
+ Most Maestro verbs work out of the box (`tapOn`, `assertVisible`, `inputText`,
473
+ `scrollUntilVisible`, `launchApp`, `when:`, `repeat`, `runFlow`, …). Verbs that
474
+ need real device hardware (GPS, radio, photo library) throw a clear error. See
475
+ `../../docs/migrating-from-maestro.md` for the full compat matrix.
476
+
477
+ ## Screenshots and recordings
478
+
479
+ For animation-heavy debugging, the useful path is usually:
480
+
481
+ ```sh
482
+ rnx debug enable animated
483
+ rnx debug trace shell on 240
484
+ rnx debug snapshot before
485
+ # reproduce the transition
486
+ rnx debug snapshot after
487
+ rnx debug diff before after
488
+ rnx screenshot --with-frame --output /tmp/rnx-framed.png
489
+ rnx record --duration 5 --output /tmp/rnx-anim.mp4
490
+ ```
491
+
492
+ `rnx screenshot --with-frame` composes the real rnx shell device chrome
493
+ around the raw screen bitmap (reusing the shell bezel/button geometry, excluding
494
+ the Electron top bar, rail gutter, and other window chrome). Flows can request
495
+ the framed export inline without changing Maestro syntax:
496
+
497
+ `rnx wait ready` and the screenshot readiness guard accept inspectable
498
+ native content, a populated node tree, or a live-frame channel whose publish
499
+ count advances across probes. Canvas-only GL, WebGPU, and video surfaces can
500
+ therefore prove that they are painting without weakening the boot-card guard.
501
+
502
+ ```yaml
503
+ - takeScreenshot: hero
504
+ - takeScreenshot:
505
+ path: marketing/hero
506
+ withFrame: true
507
+ ```
508
+
509
+ For plan-driven app-store exports, use `rnx screenshot appstore`:
510
+
511
+ ```yaml
512
+ app: 8081
513
+ device: iphone-16
514
+
515
+ capture:
516
+ flow: .maestro/capture-app-store-screenshots.yaml
517
+ mode: raw+framed
518
+
519
+ compose:
520
+ canvases: [iphone-6-9, iphone-6-1]
521
+ background: cyan
522
+ text:
523
+ preset: bold-top
524
+ slides:
525
+ - id: splash-hero
526
+ screenshot: apple/iphone/en/01-splash.png
527
+ headline: First punch.
528
+ subheadline: Every pick in one place.
529
+ ```
530
+
531
+ ```sh
532
+ rnx screenshot appstore --plan .rnx/app-store.yaml
533
+ ```
534
+
535
+ The plan runner can reuse a visible sim for capture (`--sim a9`), stop after
536
+ raw/framed intermediates (`--capture-only`), or rerender final marketing
537
+ canvases from an existing raw directory (`--compose-only`). If your flow already
538
+ writes screenshots to explicit project paths, set `from:` + `pathMode: flow` so
539
+ `rnx screenshot appstore` respects the flow's own `takeScreenshot` paths
540
+ instead of prepending `--screenshots <rawDir>`:
541
+
542
+ ```yaml
543
+ capture:
544
+ flow: .maestro/capture-app-store-screenshots.yaml
545
+ from: ./apps/app-store-screenshots/public/screenshots/apple/iphone/en
546
+ pathMode: flow
547
+ ```
548
+
549
+ In the browser shell, `Screenshot Mode` turns the live shell into a simple DOM
550
+ composition surface: the rail + mac menu bar disappear, the device shifts down
551
+ with a short transition, and editable title/subtitle fields appear above the
552
+ frame for quick art-direction passes.
553
+
554
+ ## Skills registry
555
+
556
+ `rnx skill install` installs rnx agent skills in the standard
557
+ `<skill-name>/SKILL.md` layout used by Codex and Claude Code. The bundled
558
+ skills cover setup, debugging/perf/accessibility, testing, visual review, and
559
+ the generated `contrast` command index. The published CLI registry and generated
560
+ website docs both come from `packages/rnx-skills/`:
561
+
562
+ - `packages/sootsim/skills/contrast/SKILL.md` is generated from that registry
563
+ - `src/features/site/docs/rnx/cli/*` is generated output, not hand-edited
564
+
565
+ When command docs or examples drift, update `packages/rnx-skills/` and run:
566
+
567
+ ```sh
568
+ bun run generate:sootsim-docs
569
+ ```
570
+
571
+ ## Runtime delivery (the rnx home and CDN)
572
+
573
+ The engine never ships inside this package. `runtime-delivery.ts` names
574
+ rnx's CDN origin (default `https://contrast.dev`, overridable in
575
+ `config.json`), its `SOOTSIM_*` env overrides, and its hosted paths, then hands
576
+ them to the shared delivery machinery in `@contrast/runtime-delivery` — an
577
+ internal workspace package the CLI bundles, so a published install has it
578
+ inlined. That machinery fetches `runtimes/manifest.json` + the runtime tarball,
579
+ verifies its sha256, and unpacks it under `~/.rnx`:
580
+
581
+ ```
582
+ ~/.rnx/
583
+ ├── runtimes/
584
+ │ ├── <active>/ unpacked engine assets (served at /__soot/)
585
+ │ └── <rollback>/ previous version for rollback
586
+ ├── cache/ legacy downloads; empty after cleanup
587
+ ├── profiles/ persistent named app storage, without browser caches
588
+ ├── electron/userData/ desktop app storage, with bounded browser caches
589
+ ├── daemon.json lockfile: pid, ports, active runtime, heartbeat
590
+ ├── cli-update.json cached standalone CLI version and notice state
591
+ ├── automatic-cleanup-v1.json completed legacy cleanup generation
592
+ └── config.json user prefs: update channel, cdn origin override
593
+ ```
594
+
595
+ The bridge daemon serves these assets over HTTP and exposes a self-update route;
596
+ `rnx upgrade` / `rnx runtime` drive explicit version changes from the
597
+ CLI side. Installation retains the active runtime plus one rollback and removes
598
+ the downloaded archive after extraction. Disposable Playwright caches live in a
599
+ bounded per-session temporary directory; Electron's persistent HTTP cache is
600
+ disabled for simulator profiles and globally capped for app chrome. Cookies,
601
+ localStorage, IndexedDB, service workers, recordings, and captured frames are
602
+ user-owned data and are not removed by the default cleanup command.
603
+
604
+ Per-repo configuration is optional. Every installed-runtime `rnx open`
605
+ serves its selected version from a version-specific localhost origin, so a
606
+ background activation cannot mix an already loaded shell with chunks from the
607
+ next runtime. Without config, each new open selects the active stable version.
608
+ When `rnx.config.ts` sets `runtimeVersion`, `rnx open` installs that
609
+ version without changing the machine default. App config, native-linked fonts,
610
+ and splash assets all resolve from the project root advertised by the selected
611
+ development server. Bare Metro, which has no Expo manifest project root, uses
612
+ the listening development process's cwd inside that same resolver. The invoking
613
+ CLI's cwd is never an asset/config fallback. Asset wires stay on each opened
614
+ project URL, so one daemon can serve several attached projects without sharing
615
+ font or splash state between them.
616
+
617
+ The first interactive CLI command after this cleanup generation finishes its
618
+ requested work, then launches the same safe default plan in a detached
619
+ maintenance process. The app and terminal command are usable before the legacy
620
+ filesystem scan begins. The worker prints the reclaimed size to stderr, records
621
+ completion, and does not run the scan again. Open browser profiles and a runtime
622
+ served by a live daemon or development bridge are left untouched. The automatic
623
+ pass retries on a later command after they close. The explicit `rnx cleanup --aggressive`
624
+ command remains the only path that removes the rollback runtime or user-created
625
+ recordings.
626
+
627
+ ## Development and building
628
+
629
+ This package is part of the Contrast monorepo. To build the standalone executable
630
+ for the current platform:
631
+
632
+ ```sh
633
+ bun run build:cli-binary
634
+ ```
635
+
636
+ The output is `dist-bin/rnx-<platform>-<arch>`. `dist-cli/bin.js` remains a
637
+ repository build artifact for tests and internal tooling and is not published
638
+ as an npm executable. Library exports build to `dist-lib/` (consumed via the
639
+ `exports` map: `.`, `./vite`, `./metro`, `./sdk`, `./skills`, `./detox`, and the
640
+ host/agent helpers). `bun run pack:smoke` validates the publishable tarball.