peachsim 0.1.527 → 0.1.571

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 (169) hide show
  1. package/README.md +28 -28
  2. package/cli/app-fonts.ts +1 -1
  3. package/cli/app-splash.ts +1 -1
  4. package/cli/bin.ts +1 -1
  5. package/cli/bridge-diagnostics.ts +1 -1
  6. package/cli/bridge-flow-runner.ts +72 -12
  7. package/cli/cloud-client.ts +273 -6
  8. package/cli/cloud-dispatch.ts +23 -2
  9. package/cli/cloud-session.ts +60 -4
  10. package/cli/cloud-test-result-files.ts +97 -0
  11. package/cli/cloud-test-tunnel.ts +354 -0
  12. package/cli/commands/box/bundle-plane.ts +2 -2
  13. package/cli/commands/box/checkout-plane.ts +1 -1
  14. package/cli/commands/box/rnx-command.ts +2 -2
  15. package/cli/commands/box/search-command.ts +2 -2
  16. package/cli/commands/box.ts +8 -8
  17. package/cli/commands/compat.ts +1 -1
  18. package/cli/commands/control.ts +22 -3
  19. package/cli/commands/detox.ts +1 -1
  20. package/cli/commands/device.ts +1 -1
  21. package/cli/commands/diagnose.ts +1 -1
  22. package/cli/commands/film.ts +1 -1
  23. package/cli/commands/flow.ts +60 -3
  24. package/cli/commands/inspect/actions.ts +1 -1
  25. package/cli/commands/inspect/core.ts +6 -7
  26. package/cli/commands/inspect/find.ts +2 -1
  27. package/cli/commands/inspect/resolve-target.ts +5 -3
  28. package/cli/commands/inspect/shared.ts +1 -1
  29. package/cli/commands/inspect/wait-event.ts +1 -1
  30. package/cli/commands/inspect/wait-selector.ts +15 -4
  31. package/cli/commands/inspect.ts +19 -14
  32. package/cli/commands/maestro.ts +582 -69
  33. package/cli/commands/permissions.ts +1 -1
  34. package/cli/commands/platform.ts +205 -8
  35. package/cli/commands/preview.ts +2 -2
  36. package/cli/commands/record.ts +2 -2
  37. package/cli/commands/report-issue.ts +2 -2
  38. package/cli/commands/screenshot-layers.ts +1 -1
  39. package/cli/commands/screenshot.ts +1 -1
  40. package/cli/commands/setup.ts +2 -2
  41. package/cli/commands/test.ts +600 -117
  42. package/cli/commands/three-mode.ts +1 -1
  43. package/cli/commands/upload.ts +5 -5
  44. package/cli/commands/what-happened.ts +1 -1
  45. package/cli/desktop-companion.ts +3 -3
  46. package/cli/drivers/playwright-sim-host.ts +2 -2
  47. package/cli/flow-file.ts +7 -0
  48. package/cli/flow-live-status.ts +65 -8
  49. package/cli/help-core.ts +3 -3
  50. package/cli/hidden-runtime-alias.ts +1 -1
  51. package/cli/maestro-report.ts +180 -0
  52. package/cli/main.ts +1 -1
  53. package/cli/outbound-endpoints.ts +187 -136
  54. package/cli/parse-args.ts +1 -1
  55. package/cli/registry.ts +1 -1
  56. package/cli/resolve-assets.ts +1 -1
  57. package/cli/run-registry.ts +10 -1
  58. package/cli/send-to-box.ts +4 -4
  59. package/cli/ws-bridge.ts +3 -5
  60. package/detox/expectations.ts +1 -1
  61. package/detox/gestures.ts +2 -2
  62. package/detox/index.ts +71 -29
  63. package/detox/jest-preset.cjs +5 -5
  64. package/detox/jest-setup-after-env.cjs +1 -1
  65. package/dist-lib/agent-daemon-client.cjs +2 -2
  66. package/dist-lib/agent-events.cjs +1 -1
  67. package/dist-lib/agent-identity.cjs +1 -1
  68. package/dist-lib/agent-sessions.cjs +2 -2
  69. package/dist-lib/attached-projects.cjs +2 -2
  70. package/dist-lib/auth/shared-session.cjs +1 -1
  71. package/dist-lib/backend-origin.cjs +1 -1
  72. package/dist-lib/beta.cjs +1 -1
  73. package/dist-lib/beta.mjs +1 -1
  74. package/dist-lib/bridge-constants.cjs +1 -1
  75. package/dist-lib/bridge-contract-input.cjs +1 -1
  76. package/dist-lib/bridge-contract-input.mjs +1 -1
  77. package/dist-lib/bridge-contract.cjs +1 -1
  78. package/dist-lib/bridge-contract.mjs +1 -1
  79. package/dist-lib/capture-contract.cjs +94 -3
  80. package/dist-lib/capture-contract.mjs +93 -3
  81. package/dist-lib/cli-constants.cjs +1 -1
  82. package/dist-lib/cloud-contract.cjs +1 -1
  83. package/dist-lib/cloud-contract.mjs +1 -1
  84. package/dist-lib/cloud-test-args.cjs +116 -0
  85. package/dist-lib/cloud-test-args.mjs +86 -0
  86. package/dist-lib/cloud.cjs +2 -2
  87. package/dist-lib/cloud.mjs +2 -2
  88. package/dist-lib/config.cjs +1 -1
  89. package/dist-lib/detox/index.cjs +106 -25
  90. package/dist-lib/detox/jest-preset.cjs +5 -5
  91. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  92. package/dist-lib/home-paths.cjs +1 -1
  93. package/dist-lib/host/bridge-host.cjs +150 -65
  94. package/dist-lib/host/fetch-proxy-handler.cjs +28 -2
  95. package/dist-lib/host/fetch-proxy-overrides.cjs +33 -3
  96. package/dist-lib/host/fetch-proxy-overrides.mjs +30 -2
  97. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  98. package/dist-lib/host/websocket-proxy.cjs +1 -1
  99. package/dist-lib/index.cjs +224 -41
  100. package/dist-lib/jump-to-source-babel.cjs +1 -1
  101. package/dist-lib/jump-to-source-native.cjs +1 -1
  102. package/dist-lib/menu.cjs +2 -2
  103. package/dist-lib/menu.mjs +2 -2
  104. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  105. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  106. package/dist-lib/metro-production-bundle.cjs +3 -3
  107. package/dist-lib/metro-production-bundle.mjs +3 -3
  108. package/dist-lib/metro.cjs +8 -8
  109. package/dist-lib/profiles.cjs +1 -1
  110. package/dist-lib/public-brand.cjs +2 -2
  111. package/dist-lib/react-native-host-modules.cjs +1 -1
  112. package/dist-lib/react-native-host-modules.mjs +1 -1
  113. package/dist-lib/render-mode.cjs +1 -1
  114. package/dist-lib/scripts/dev-server-scanner.cjs +31 -15
  115. package/dist-lib/sdk.cjs +3 -2
  116. package/dist-lib/sdk.mjs +3 -2
  117. package/dist-lib/skills.cjs +478 -266
  118. package/dist-lib/swift-compile.cjs +372 -0
  119. package/dist-lib/swift-compile.mjs +339 -0
  120. package/dist-lib/swift-package.cjs +78 -0
  121. package/dist-lib/swift-package.mjs +52 -0
  122. package/dist-lib/swift-submit.cjs +1 -1
  123. package/dist-lib/swift-submit.mjs +1 -1
  124. package/dist-lib/swift.cjs +583 -16
  125. package/dist-lib/vite.cjs +483 -150
  126. package/package.json +30 -9
  127. package/scripts/dev-server-scanner.ts +41 -14
  128. package/scripts/optional-demo-registry.ts +3 -3
  129. package/skills/contrast/SKILL.md +9 -9
  130. package/skills/rnx-debug/SKILL.md +14 -4
  131. package/skills/rnx-perf/SKILL.md +5 -5
  132. package/skills/rnx-setup/SKILL.md +11 -11
  133. package/skills/rnx-test/SKILL.md +14 -14
  134. package/skills/rnx-visual/SKILL.md +6 -6
  135. package/src/agent-sessions.ts +3 -3
  136. package/src/attached-projects.ts +1 -1
  137. package/src/bridge-contract-input.ts +1 -1
  138. package/src/bridge-contract.ts +23 -5
  139. package/src/capture-contract.ts +145 -3
  140. package/src/cloud-test-args.ts +100 -0
  141. package/src/config.ts +2 -2
  142. package/src/connect.ts +2 -2
  143. package/src/home-paths.ts +3 -3
  144. package/src/host/bridge-host.ts +2 -2
  145. package/src/host/fetch-proxy-handler.ts +4 -3
  146. package/src/host/fetch-proxy-overrides.ts +41 -0
  147. package/src/host/plane-host.ts +2 -2
  148. package/src/index.ts +3 -0
  149. package/src/metro-plugin.ts +4 -4
  150. package/src/metro-production-bundle.ts +2 -2
  151. package/src/native-seam-manifest.ts +6 -6
  152. package/src/public-brand.ts +1 -1
  153. package/src/rolldown-plugin-swift.ts +16 -12
  154. package/src/runtime-assets.ts +8 -6
  155. package/src/screenshots/frame-compose.ts +24 -56
  156. package/src/screenshots/org-deck.ts +1 -1
  157. package/src/screenshots/registry.ts +4 -4
  158. package/src/screenshots/schema.ts +1 -1
  159. package/src/sdk.ts +2 -2
  160. package/src/settings-bundle.ts +1 -1
  161. package/src/sim-client.ts +2 -4
  162. package/src/skills/builtin/compat-check.ts +1 -1
  163. package/src/swift-compile.ts +359 -0
  164. package/src/swift-package.ts +69 -0
  165. package/src/swift-submit.ts +3 -2
  166. package/src/vite-plugin-one.ts +1 -1
  167. package/src/vite-plugin-swift.ts +114 -113
  168. package/src/vite-plugin.ts +30 -17
  169. package/src/worklets-babel.ts +2 -2
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # rnx
1
+ # Peach
2
2
 
3
3
  The source owner for the standalone `rnx` executable and the public `rnxsim`
4
4
  integration library used to drive a browser-native React Native simulator.
@@ -33,7 +33,7 @@ What this workspace gives you:
33
33
  - `rnxsim/jump-to-source-babel` — an optional Babel plugin that annotates JSX
34
34
  with source locations so inspect mode can open the selected element in your
35
35
  editor
36
- - `rnxsim/detox` — a drop-in Detox driver + jest preset
36
+ - `peachsim/detox` — a drop-in Detox driver + jest preset
37
37
  - `rnxsim/sdk` — the inspect/interact verbs as a programmatic API
38
38
  - `rnxsim/bridge-contract-input` — parsers turning untrusted JSON into
39
39
  bridge-contract arguments
@@ -202,7 +202,7 @@ bridge always addresses the **shell worker**, never an undifferentiated sim.
202
202
 
203
203
  | component | role | key files |
204
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` |
205
+ | Peach 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
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
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
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` |
@@ -211,11 +211,11 @@ bridge always addresses the **shell worker**, never an undifferentiated sim.
211
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
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
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` |
214
+ | runtime delivery + Peach home | Versioned engine runtime management. The shared machinery owns manifest fetch, sha256 verification, channels, and auto-update. Peach 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
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
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` |
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/peach-compat/src/stub-manifest.ts` |
218
+ | skills registry | Peach 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
219
 
220
220
  ## Getting started
221
221
 
@@ -245,7 +245,7 @@ The `rnxsim` npm package contains bundler plugins and library exports, not a CLI
245
245
  binary. Start your Metro, Expo, or One dev server the way you already do, then
246
246
  open it from the CLI. There is no iOS native build in the inner loop; Metro alone is
247
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
248
+ discovery request and manifest target are loopback addresses, Peach keeps the
249
249
  loopback host that answered discovery so an IPv6-only listener is not reopened
250
250
  through an unreachable IPv4 address. `rnx open` starts a local bridge when no
251
251
  daemon is reachable. The background service is strongly recommended for local
@@ -253,7 +253,7 @@ agent work because inspect, interaction, and test commands reuse one ready bridg
253
253
  and runtime. Enable it at any time with `rnx daemon install`; it is never required
254
254
  in CI. The packaged Desktop app is optional and available through first-run
255
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
256
+ daemon on first launch, then offers **Install the Peach command** over the running
257
257
  simulator. That action installs `~/.local/bin/rnx`, updates shell startup, and
258
258
  hands the daemon to launchd or systemd without reloading the window. Windows
259
259
  keeps the desktop-owned daemon because it has no daemon service yet. Native Electron windows created by `rnx open` belong to that CLI
@@ -264,7 +264,7 @@ window for a selected device (same device list as the `Window >` menu).
264
264
 
265
265
  ### Privacy
266
266
 
267
- rnx sends no analytics, crash report, command event, app data, project data, or
267
+ Peach sends no analytics, crash report, command event, app data, project data, or
268
268
  identity by default. An unsigned-in interactive run may request the runtime it
269
269
  is about to execute and the stable standalone CLI version document. The latter
270
270
  runs after the command in a detached process at most once every 20 hours and can
@@ -275,7 +275,7 @@ consuming the later human choice. Legacy browser and Electron telemetry senders
275
275
  are removed rather than being enabled by this consent.
276
276
 
277
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,
278
+ class. Peach never sends source, file paths, project names, app content, commands,
279
279
  identity, screenshots, or recordings as diagnostics. Cloud commands such as
280
280
  login, upload, billing, issue reporting, generated flows, and screenshot decks
281
281
  contact their documented service only when explicitly invoked. The complete
@@ -317,7 +317,7 @@ preset configures it automatically.
317
317
  For One's Rolldown native bundler, use the native transform in `vite.config.ts`:
318
318
 
319
319
  ```ts
320
- import { jumpToSourceNativePlugin } from 'rnxsim/jump-to-source-native'
320
+ import { jumpToSourceNativePlugin } from 'peachsim/jump-to-source-native'
321
321
 
322
322
  one({
323
323
  native: {
@@ -335,7 +335,7 @@ For One's `nativeTransformModules`, export
335
335
 
336
336
  ## CLI
337
337
 
338
- The CLI is the primary debugging surface for rnx — use it first for runtime
338
+ The CLI is the primary debugging surface for Peach — use it first for runtime
339
339
  inspection, interaction, animation debugging, shell tracing, screenshots, and
340
340
  flow capture, not just as a test runner.
341
341
 
@@ -381,11 +381,11 @@ rnx do tap email --then type user@example.com --then tap submit
381
381
 
382
382
  ## Bundler plugins (`rnxsim/vite` and `rnxsim/metro`)
383
383
 
384
- The Vite plugin serves the installed RNX runtime from your existing dev server.
384
+ The Vite plugin serves the installed Peach runtime from your existing dev server.
385
385
 
386
386
  ```ts
387
387
  // vite.config.ts
388
- import { rnxPlugin } from 'rnxsim/vite'
388
+ import { rnxPlugin } from 'peachsim/vite'
389
389
 
390
390
  export default {
391
391
  plugins: [rnxPlugin({ open: { appName: 'My App' } })],
@@ -395,17 +395,17 @@ export default {
395
395
  ```js
396
396
  // metro.config.js
397
397
  const { getDefaultConfig } = require('expo/metro-config')
398
- const { withRNX } = require('rnxsim/metro')
398
+ const { withRNX } = require('peachsim/metro')
399
399
 
400
400
  const config = getDefaultConfig(__dirname)
401
401
 
402
402
  module.exports = withRNX(config)
403
403
  ```
404
404
 
405
- rnx works directly with regular, unannotated Metro production JavaScript bundles
405
+ Peach works directly with regular, unannotated Metro production JavaScript bundles
406
406
  out of the box. It identifies modules by matching package graph fingerprints
407
407
  against a separately updated, integrity-checked registry. Hermes bytecode is not
408
- a supported input; pass rnx the JavaScript bundle produced before Hermes
408
+ a supported input; pass Peach the JavaScript bundle produced before Hermes
409
409
  compilation.
410
410
 
411
411
  If an app has unusual dependencies or fingerprint inference fails, you can
@@ -417,7 +417,7 @@ options and enables two integrations:
417
417
  bundle. Resolution, transforms, minification, and source maps stay the app's
418
418
  own, so the annotated bundle is the exact bundle you ship: the footer assigns
419
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
420
+ 2. **In-browser dev shell:** serves the Peach simulator shell at `/__soot/` on
421
421
  the Metro dev server.
422
422
 
423
423
  The URL helpers request ordinary Metro graphs: `dev=false&minify=true` for
@@ -427,7 +427,7 @@ production and `dev=true&minify=false` for development.
427
427
  const {
428
428
  toRNXProductionBundleUrl,
429
429
  toRNXDevelopmentBundleUrl,
430
- } = require('rnxsim/metro')
430
+ } = require('peachsim/metro')
431
431
 
432
432
  const productionUrl = toRNXProductionBundleUrl(
433
433
  'http://localhost:8081/index.bundle?platform=ios',
@@ -439,13 +439,13 @@ const developmentUrl = toRNXDevelopmentBundleUrl(
439
439
 
440
440
  ## Coming from Detox
441
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
442
+ Peach ships a drop-in Detox driver. Existing Detox test files
443
+ (`import { by, element, expect, device } from 'detox'`) run against Peach with
444
444
  no code changes — just add one line to your jest config:
445
445
 
446
446
  ```js
447
447
  // jest config
448
- { preset: 'rnxsim/detox' }
448
+ { preset: 'peachsim/detox' }
449
449
  ```
450
450
 
451
451
  Or use the CLI directly:
@@ -459,7 +459,7 @@ See `../../docs/migrating-from-detox.md` for details.
459
459
 
460
460
  ## Coming from Maestro
461
461
 
462
- Maestro is rnx's single YAML test surface. Point it at your existing
462
+ Maestro is Peach's single YAML test surface. Point it at your existing
463
463
  `.maestro/` directory, generate a test, or author one from live actions:
464
464
 
465
465
  ```sh
@@ -489,7 +489,7 @@ rnx screenshot --with-frame --output /tmp/rnx-framed.png
489
489
  rnx record --duration 5 --output /tmp/rnx-anim.mp4
490
490
  ```
491
491
 
492
- `rnx screenshot --with-frame` composes the real rnx shell device chrome
492
+ `rnx screenshot --with-frame` composes the real Peach shell device chrome
493
493
  around the raw screen bitmap (reusing the shell bezel/button geometry, excluding
494
494
  the Electron top bar, rail gutter, and other window chrome). Flows can request
495
495
  the framed export inline without changing Maestro syntax:
@@ -553,13 +553,13 @@ frame for quick art-direction passes.
553
553
 
554
554
  ## Skills registry
555
555
 
556
- `rnx skill install` installs rnx agent skills in the standard
556
+ `rnx skill install` installs Peach agent skills in the standard
557
557
  `<skill-name>/SKILL.md` layout used by Codex and Claude Code. The bundled
558
558
  skills cover setup, debugging/perf/accessibility, testing, visual review, and
559
559
  the generated `contrast` command index. The published CLI registry and generated
560
560
  website docs both come from `packages/rnx-skills/`:
561
561
 
562
- - `packages/sootsim/skills/contrast/SKILL.md` is generated from that registry
562
+ - `packages/peachsim/skills/contrast/SKILL.md` is generated from that registry
563
563
  - `src/features/site/docs/rnx/cli/*` is generated output, not hand-edited
564
564
 
565
565
  When command docs or examples drift, update `packages/rnx-skills/` and run:
@@ -568,10 +568,10 @@ When command docs or examples drift, update `packages/rnx-skills/` and run:
568
568
  bun run generate:sootsim-docs
569
569
  ```
570
570
 
571
- ## Runtime delivery (the rnx home and CDN)
571
+ ## Runtime delivery (the Peach home and CDN)
572
572
 
573
573
  The engine never ships inside this package. `runtime-delivery.ts` names
574
- rnx's CDN origin (default `https://contrast.dev`, overridable in
574
+ Peach's CDN origin (default `https://contrast.dev`, overridable in
575
575
  `config.json`), its `SOOTSIM_*` env overrides, and its hosted paths, then hands
576
576
  them to the shared delivery machinery in `@contrast/runtime-delivery` — an
577
577
  internal workspace package the CLI bundles, so a published install has it
package/cli/app-fonts.ts CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  expoFontPathsFromPlugins,
7
7
  metroFontSpecsFromPaths,
8
8
  type ConfigPluginFontSpec,
9
- } from 'sootsim-engine/engine/config-plugin-fonts'
9
+ } from 'peach-engine/engine/config-plugin-fonts'
10
10
  import { resolveListeningPid, resolveProcessCwd } from '../scripts/dev-server-scanner'
11
11
  import { parsePlistSource } from '../src/plist.ts'
12
12
  import { resolveAppProject } from './app-project'
package/cli/app-splash.ts CHANGED
@@ -5,7 +5,7 @@ import path from 'node:path'
5
5
  import {
6
6
  configPluginSplashFromExpoConfig,
7
7
  type ConfigPluginSplashSpec,
8
- } from 'sootsim-engine/engine/config-plugin-splash'
8
+ } from 'peach-engine/engine/config-plugin-splash'
9
9
  import { resolveAppProject } from './app-project'
10
10
 
11
11
  const require = createRequire(import.meta.url)
package/cli/bin.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // rnx CLI entry point. the full command and verb surface lives in the
4
- // registry at `packages/rnx-skills/src/cli/meta.ts` — help output,
4
+ // registry at `packages/peach-skills/src/cli/meta.ts` — help output,
5
5
  // website docs, and agent skills all read from there. do not duplicate
6
6
  // command listings in this file; run `rnx --help` for the canonical
7
7
  // surface.
@@ -1,7 +1,7 @@
1
1
  import { MERGED_CONSOLE_COUNT_EVAL, inspectErrors } from './commands/inspect/core'
2
2
  import { formatLogTimestamp } from './commands/inspect/shared'
3
3
  import type { WsBridge } from './ws-bridge'
4
- import type { SootSimRequestEntry } from '@rnx/globals'
4
+ import type { SootSimRequestEntry } from '@peach/globals'
5
5
 
6
6
  interface ConsoleSummaryOptions {
7
7
  includeTail?: boolean
@@ -1,6 +1,6 @@
1
1
  import * as fs from 'fs'
2
2
  import * as path from 'path'
3
- import { devices, type DeviceModel } from 'sootsim-engine/settings'
3
+ import { devices, type DeviceModel } from 'peach-engine/settings'
4
4
  import yaml from 'yaml'
5
5
  import { REQUIRED_IDENTICAL_FLOW_FRAMES, framesIdentical } from '../detox/proof-frame.cjs'
6
6
  import { flowTimeoutScale } from '../src/flow-timeout-scale'
@@ -14,11 +14,11 @@ import { MaestroJsContext, scriptConditionIsTruthy } from './maestro-js'
14
14
  import { ensureCliRecordingEntitlement } from './recording-access'
15
15
  import type { PerformResult, PerformStep, ResetResult } from '../src/bridge-contract'
16
16
  import type { WsBridge } from './ws-bridge'
17
- import type { SootSimReplayTarget, SootSimTapResult } from '@rnx/globals'
17
+ import type { SootSimReplayTarget, SootSimTapResult } from '@peach/globals'
18
18
  import type {
19
19
  SootSimExternalAppLifecycleOptions,
20
20
  SootSimLaunchArguments,
21
- } from '@rnx/globals'
21
+ } from '@peach/globals'
22
22
 
23
23
  const DEFAULT_TIMEOUT = 10000
24
24
  const FLOW_TIMEOUT_SCALE = flowTimeoutScale()
@@ -1163,7 +1163,9 @@ export class SootSimBridgeFlowRunner {
1163
1163
 
1164
1164
  async assertVisible(target: string | { id?: string; text?: string }) {
1165
1165
  const opts = typeof target === 'string' ? { text: target } : target
1166
- for (let attempt = 0; attempt < 15; attempt++) {
1166
+ // maestro auto-retries assertions for up to 7 seconds before failing; a
1167
+ // shorter window fails flows real maestro passes.
1168
+ for (let attempt = 0; attempt < 35; attempt++) {
1167
1169
  if (await this.isElementVisible(opts)) {
1168
1170
  return
1169
1171
  }
@@ -1184,13 +1186,19 @@ export class SootSimBridgeFlowRunner {
1184
1186
 
1185
1187
  async assertNotVisible(target: string | { id?: string; text?: string }) {
1186
1188
  const opts = typeof target === 'string' ? { text: target } : target
1187
- if (await this.isElementVisible(opts)) {
1188
- const found = await this.findElement(opts)
1189
- const where = found
1190
- ? ` at (${Math.round(found.absolutePosition.x)},${Math.round(found.absolutePosition.y)})`
1191
- : ''
1192
- throw new Error(`assertNotVisible: ${JSON.stringify(opts)} IS visible${where}`)
1189
+ // like assertVisible, maestro waits up to 7 seconds for the element to
1190
+ // disappear rather than failing on the first visible sample.
1191
+ for (let attempt = 0; attempt < 35; attempt++) {
1192
+ if (!(await this.isElementVisible(opts))) {
1193
+ return
1194
+ }
1195
+ await sleep(200)
1193
1196
  }
1197
+ const found = await this.findElement(opts)
1198
+ const where = found
1199
+ ? ` at (${Math.round(found.absolutePosition.x)},${Math.round(found.absolutePosition.y)})`
1200
+ : ''
1201
+ throw new Error(`assertNotVisible: ${JSON.stringify(opts)} IS visible${where}`)
1194
1202
  }
1195
1203
 
1196
1204
  // visibility is the engine's call, not a layout-bounds guess. the old test
@@ -2027,7 +2035,33 @@ export class SootSimBridgeFlowRunner {
2027
2035
  // begin its pixels only after launch has mounted and settled, leaving the
2028
2036
  // loading prelude outside the playable video while its trace stays visible.
2029
2037
  await this.waitForVisualSettle()
2038
+ const videoAlreadyStarted = this.recordingStartedAtMs != null
2030
2039
  await this.ensureRecordingStarted()
2040
+ if (!videoAlreadyStarted && this.recordingStartedAtMs != null) {
2041
+ // the preview event recorder has been running since before launch,
2042
+ // so its clock includes the boot prelude the video just skipped.
2043
+ // rebase it to the video's first frame — without this every event
2044
+ // timestamp overshoots the playable video and preview playback can
2045
+ // never align (play stuck disabled). no-op unless the preview
2046
+ // event recorder is running.
2047
+ await this.rebaseEventRecorderToVideoStart(this.recordingStartedAtMs)
2048
+ }
2049
+ }
2050
+ }
2051
+
2052
+ private async rebaseEventRecorderToVideoStart(videoStartedAtMs: number) {
2053
+ try {
2054
+ await this.evaluate(
2055
+ `(() => {
2056
+ const recorder = window.SootSim?.bridges?.eventRecorder ?? window.__sootsimEventRecorder
2057
+ if (!recorder?.isRecording?.()) return 'not-recording'
2058
+ return recorder.markVisibleStart?.(${JSON.stringify(videoStartedAtMs)}) ?? 'no-mark'
2059
+ })()`,
2060
+ )
2061
+ } catch (err) {
2062
+ console.warn(
2063
+ ` warn: event-recorder rebase to video start failed: ${err instanceof Error ? err.message : err}`,
2064
+ )
2031
2065
  }
2032
2066
  }
2033
2067
 
@@ -2258,6 +2292,7 @@ export class SootSimBridgeFlowRunner {
2258
2292
  }
2259
2293
 
2260
2294
  async runStep(rawStepInput: MaestroStep | string, stepIndex: number) {
2295
+ await this.liveStatus?.waitWhilePaused()
2261
2296
  // maestro allows several commands in BARE-STRING form (`- back`,
2262
2297
  // `- hideKeyboard`, `- waitForAnimationToEnd`, `- scrollUp`, …). yaml parses
2263
2298
  // those to a plain string, so normalize to the `{ verb: true }` object form
@@ -2384,7 +2419,14 @@ export class SootSimBridgeFlowRunner {
2384
2419
  step.waitFor
2385
2420
  )
2386
2421
  if (this.stepDelay > 0 && !isAssertion) {
2387
- await sleep(this.stepDelay)
2422
+ const chunkMs = 50
2423
+ let elapsed = 0
2424
+ while (elapsed < this.stepDelay) {
2425
+ await this.liveStatus?.waitWhilePaused()
2426
+ const toSleep = Math.min(chunkMs, this.stepDelay - elapsed)
2427
+ await sleep(toSleep)
2428
+ elapsed += toSleep
2429
+ }
2388
2430
  }
2389
2431
  this.recordFlowTraceStep({
2390
2432
  stepIndex,
@@ -2621,7 +2663,12 @@ export class SootSimBridgeFlowRunner {
2621
2663
  // runFlowPlayback; absent for programmatic callers.
2622
2664
  liveStatus: FlowLiveStatusReporter | null = null
2623
2665
 
2624
- async runFlow(steps: MaestroStep[]) {
2666
+ resetFlowState() {
2667
+ this.flowTraceSteps = []
2668
+ this.lastFailedStep = null
2669
+ }
2670
+
2671
+ extractFlowBody(steps: MaestroStep[]) {
2625
2672
  // maestro `onFlowStart` / `onFlowComplete` lifecycle hooks: if any step
2626
2673
  // in the array is a sole-key entry whose key is one of these names and
2627
2674
  // whose value is a list of sub-steps, pull them out and run them as
@@ -2649,7 +2696,11 @@ export class SootSimBridgeFlowRunner {
2649
2696
  }
2650
2697
  body.push(step)
2651
2698
  }
2699
+ return { onStart, onComplete, body }
2700
+ }
2652
2701
 
2702
+ async planFlow(steps: MaestroStep[], options: { dryRun?: boolean } = {}) {
2703
+ const { body } = this.extractFlowBody(steps)
2653
2704
  if (this.liveStatus) {
2654
2705
  await this.liveStatus.plan(
2655
2706
  body.map((rawStep, index) => {
@@ -2660,8 +2711,17 @@ export class SootSimBridgeFlowRunner {
2660
2711
  const name = Object.keys(step).find((k) => k !== 'when') ?? Object.keys(step)[0]
2661
2712
  return { index, name, target: this.flowStepTargetLabel(step, name) }
2662
2713
  }),
2714
+ options,
2663
2715
  )
2664
2716
  }
2717
+ }
2718
+
2719
+ async runFlow(steps: MaestroStep[], options: { skipPlan?: boolean } = {}) {
2720
+ const { onStart, onComplete, body } = this.extractFlowBody(steps)
2721
+
2722
+ if (!options.skipPlan && this.liveStatus) {
2723
+ await this.planFlow(steps)
2724
+ }
2665
2725
 
2666
2726
  if (onStart.length > 0) {
2667
2727
  console.log(`[flow] onFlowStart (${onStart.length} steps)`)