peachsim 0.1.527 → 0.1.570
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.
- package/README.md +26 -26
- package/cli/app-fonts.ts +1 -1
- package/cli/app-splash.ts +1 -1
- package/cli/bin.ts +1 -1
- package/cli/bridge-diagnostics.ts +1 -1
- package/cli/bridge-flow-runner.ts +57 -5
- package/cli/cloud-client.ts +273 -6
- package/cli/cloud-dispatch.ts +22 -2
- package/cli/cloud-session.ts +60 -4
- package/cli/cloud-test-tunnel.ts +354 -0
- package/cli/commands/box/bundle-plane.ts +2 -2
- package/cli/commands/box/checkout-plane.ts +1 -1
- package/cli/commands/box/rnx-command.ts +2 -2
- package/cli/commands/box/search-command.ts +2 -2
- package/cli/commands/box.ts +8 -8
- package/cli/commands/compat.ts +1 -1
- package/cli/commands/control.ts +22 -3
- package/cli/commands/detox.ts +1 -1
- package/cli/commands/device.ts +1 -1
- package/cli/commands/diagnose.ts +1 -1
- package/cli/commands/film.ts +1 -1
- package/cli/commands/flow.ts +60 -3
- package/cli/commands/inspect/actions.ts +1 -1
- package/cli/commands/inspect/core.ts +6 -7
- package/cli/commands/inspect/find.ts +2 -1
- package/cli/commands/inspect/resolve-target.ts +5 -3
- package/cli/commands/inspect/shared.ts +1 -1
- package/cli/commands/inspect/wait-event.ts +1 -1
- package/cli/commands/inspect/wait-selector.ts +15 -4
- package/cli/commands/inspect.ts +19 -14
- package/cli/commands/maestro.ts +464 -43
- package/cli/commands/permissions.ts +1 -1
- package/cli/commands/platform.ts +205 -8
- package/cli/commands/preview.ts +2 -2
- package/cli/commands/record.ts +2 -2
- package/cli/commands/report-issue.ts +2 -2
- package/cli/commands/screenshot-layers.ts +1 -1
- package/cli/commands/screenshot.ts +1 -1
- package/cli/commands/setup.ts +2 -2
- package/cli/commands/test.ts +440 -115
- package/cli/commands/three-mode.ts +1 -1
- package/cli/commands/upload.ts +5 -5
- package/cli/commands/what-happened.ts +1 -1
- package/cli/desktop-companion.ts +3 -3
- package/cli/drivers/playwright-sim-host.ts +2 -2
- package/cli/flow-file.ts +7 -0
- package/cli/flow-live-status.ts +65 -8
- package/cli/help-core.ts +3 -3
- package/cli/hidden-runtime-alias.ts +1 -1
- package/cli/maestro-report.ts +178 -0
- package/cli/main.ts +1 -1
- package/cli/outbound-endpoints.ts +185 -136
- package/cli/parse-args.ts +1 -1
- package/cli/registry.ts +1 -1
- package/cli/resolve-assets.ts +1 -1
- package/cli/run-registry.ts +8 -0
- package/cli/send-to-box.ts +4 -4
- package/cli/ws-bridge.ts +3 -5
- package/detox/expectations.ts +1 -1
- package/detox/gestures.ts +2 -2
- package/detox/index.ts +71 -29
- package/detox/jest-preset.cjs +3 -3
- package/detox/jest-setup-after-env.cjs +1 -1
- package/dist-lib/agent-daemon-client.cjs +2 -2
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +2 -2
- package/dist-lib/attached-projects.cjs +2 -2
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +94 -3
- package/dist-lib/capture-contract.mjs +93 -3
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud.cjs +2 -2
- package/dist-lib/cloud.mjs +2 -2
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +106 -25
- package/dist-lib/detox/jest-preset.cjs +3 -3
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +137 -61
- package/dist-lib/host/fetch-proxy-handler.cjs +28 -2
- package/dist-lib/host/fetch-proxy-overrides.cjs +33 -3
- package/dist-lib/host/fetch-proxy-overrides.mjs +30 -2
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +209 -16
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/menu.cjs +2 -2
- package/dist-lib/menu.mjs +2 -2
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +3 -3
- package/dist-lib/metro-production-bundle.mjs +3 -3
- package/dist-lib/metro.cjs +8 -8
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +2 -2
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +31 -15
- package/dist-lib/sdk.cjs +3 -2
- package/dist-lib/sdk.mjs +3 -2
- package/dist-lib/skills.cjs +362 -163
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift.cjs +583 -16
- package/dist-lib/vite.cjs +29 -14
- package/package.json +9 -9
- package/scripts/dev-server-scanner.ts +41 -14
- package/scripts/optional-demo-registry.ts +3 -3
- package/skills/contrast/SKILL.md +9 -9
- package/skills/rnx-debug/SKILL.md +14 -4
- package/skills/rnx-perf/SKILL.md +5 -5
- package/skills/rnx-setup/SKILL.md +11 -11
- package/skills/rnx-test/SKILL.md +11 -11
- package/skills/rnx-visual/SKILL.md +6 -6
- package/src/agent-sessions.ts +3 -3
- package/src/attached-projects.ts +1 -1
- package/src/bridge-contract-input.ts +1 -1
- package/src/bridge-contract.ts +23 -5
- package/src/capture-contract.ts +145 -3
- package/src/config.ts +2 -2
- package/src/connect.ts +2 -2
- package/src/home-paths.ts +3 -3
- package/src/host/bridge-host.ts +2 -2
- package/src/host/fetch-proxy-handler.ts +4 -3
- package/src/host/fetch-proxy-overrides.ts +41 -0
- package/src/host/plane-host.ts +2 -2
- package/src/index.ts +3 -0
- package/src/metro-plugin.ts +4 -4
- package/src/metro-production-bundle.ts +2 -2
- package/src/native-seam-manifest.ts +6 -6
- package/src/public-brand.ts +1 -1
- package/src/rolldown-plugin-swift.ts +8 -5
- package/src/runtime-assets.ts +8 -6
- package/src/screenshots/frame-compose.ts +24 -56
- package/src/screenshots/org-deck.ts +1 -1
- package/src/screenshots/registry.ts +4 -4
- package/src/screenshots/schema.ts +1 -1
- package/src/sdk.ts +2 -2
- package/src/settings-bundle.ts +1 -1
- package/src/sim-client.ts +2 -4
- package/src/skills/builtin/compat-check.ts +1 -1
- package/src/vite-plugin-one.ts +1 -1
- package/src/vite-plugin-swift.ts +11 -6
- package/src/vite-plugin.ts +30 -17
- package/src/worklets-babel.ts +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
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.
|
|
@@ -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
|
-
|
|
|
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 +
|
|
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 |
|
|
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,
|
|
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
|
|
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
|
-
|
|
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.
|
|
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 '
|
|
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
|
|
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
|
|
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 '
|
|
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('
|
|
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
|
-
|
|
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
|
|
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
|
|
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('
|
|
430
|
+
} = require('peachsim/metro')
|
|
431
431
|
|
|
432
432
|
const productionUrl = toRNXProductionBundleUrl(
|
|
433
433
|
'http://localhost:8081/index.bundle?platform=ios',
|
|
@@ -439,8 +439,8 @@ const developmentUrl = toRNXDevelopmentBundleUrl(
|
|
|
439
439
|
|
|
440
440
|
## Coming from Detox
|
|
441
441
|
|
|
442
|
-
|
|
443
|
-
(`import { by, element, expect, device } from 'detox'`) run against
|
|
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
|
|
@@ -459,7 +459,7 @@ See `../../docs/migrating-from-detox.md` for details.
|
|
|
459
459
|
|
|
460
460
|
## Coming from Maestro
|
|
461
461
|
|
|
462
|
-
Maestro is
|
|
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
|
|
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
|
|
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/
|
|
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
|
|
571
|
+
## Runtime delivery (the Peach home and CDN)
|
|
572
572
|
|
|
573
573
|
The engine never ships inside this package. `runtime-delivery.ts` names
|
|
574
|
-
|
|
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 '
|
|
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 '
|
|
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/
|
|
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 '@
|
|
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 '
|
|
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 '@
|
|
17
|
+
import type { SootSimReplayTarget, SootSimTapResult } from '@peach/globals'
|
|
18
18
|
import type {
|
|
19
19
|
SootSimExternalAppLifecycleOptions,
|
|
20
20
|
SootSimLaunchArguments,
|
|
21
|
-
} from '@
|
|
21
|
+
} from '@peach/globals'
|
|
22
22
|
|
|
23
23
|
const DEFAULT_TIMEOUT = 10000
|
|
24
24
|
const FLOW_TIMEOUT_SCALE = flowTimeoutScale()
|
|
@@ -2027,7 +2027,33 @@ export class SootSimBridgeFlowRunner {
|
|
|
2027
2027
|
// begin its pixels only after launch has mounted and settled, leaving the
|
|
2028
2028
|
// loading prelude outside the playable video while its trace stays visible.
|
|
2029
2029
|
await this.waitForVisualSettle()
|
|
2030
|
+
const videoAlreadyStarted = this.recordingStartedAtMs != null
|
|
2030
2031
|
await this.ensureRecordingStarted()
|
|
2032
|
+
if (!videoAlreadyStarted && this.recordingStartedAtMs != null) {
|
|
2033
|
+
// the preview event recorder has been running since before launch,
|
|
2034
|
+
// so its clock includes the boot prelude the video just skipped.
|
|
2035
|
+
// rebase it to the video's first frame — without this every event
|
|
2036
|
+
// timestamp overshoots the playable video and preview playback can
|
|
2037
|
+
// never align (play stuck disabled). no-op unless the preview
|
|
2038
|
+
// event recorder is running.
|
|
2039
|
+
await this.rebaseEventRecorderToVideoStart(this.recordingStartedAtMs)
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
private async rebaseEventRecorderToVideoStart(videoStartedAtMs: number) {
|
|
2045
|
+
try {
|
|
2046
|
+
await this.evaluate(
|
|
2047
|
+
`(() => {
|
|
2048
|
+
const recorder = window.SootSim?.bridges?.eventRecorder ?? window.__sootsimEventRecorder
|
|
2049
|
+
if (!recorder?.isRecording?.()) return 'not-recording'
|
|
2050
|
+
return recorder.markVisibleStart?.(${JSON.stringify(videoStartedAtMs)}) ?? 'no-mark'
|
|
2051
|
+
})()`,
|
|
2052
|
+
)
|
|
2053
|
+
} catch (err) {
|
|
2054
|
+
console.warn(
|
|
2055
|
+
` warn: event-recorder rebase to video start failed: ${err instanceof Error ? err.message : err}`,
|
|
2056
|
+
)
|
|
2031
2057
|
}
|
|
2032
2058
|
}
|
|
2033
2059
|
|
|
@@ -2258,6 +2284,7 @@ export class SootSimBridgeFlowRunner {
|
|
|
2258
2284
|
}
|
|
2259
2285
|
|
|
2260
2286
|
async runStep(rawStepInput: MaestroStep | string, stepIndex: number) {
|
|
2287
|
+
await this.liveStatus?.waitWhilePaused()
|
|
2261
2288
|
// maestro allows several commands in BARE-STRING form (`- back`,
|
|
2262
2289
|
// `- hideKeyboard`, `- waitForAnimationToEnd`, `- scrollUp`, …). yaml parses
|
|
2263
2290
|
// those to a plain string, so normalize to the `{ verb: true }` object form
|
|
@@ -2384,7 +2411,14 @@ export class SootSimBridgeFlowRunner {
|
|
|
2384
2411
|
step.waitFor
|
|
2385
2412
|
)
|
|
2386
2413
|
if (this.stepDelay > 0 && !isAssertion) {
|
|
2387
|
-
|
|
2414
|
+
const chunkMs = 50
|
|
2415
|
+
let elapsed = 0
|
|
2416
|
+
while (elapsed < this.stepDelay) {
|
|
2417
|
+
await this.liveStatus?.waitWhilePaused()
|
|
2418
|
+
const toSleep = Math.min(chunkMs, this.stepDelay - elapsed)
|
|
2419
|
+
await sleep(toSleep)
|
|
2420
|
+
elapsed += toSleep
|
|
2421
|
+
}
|
|
2388
2422
|
}
|
|
2389
2423
|
this.recordFlowTraceStep({
|
|
2390
2424
|
stepIndex,
|
|
@@ -2621,7 +2655,12 @@ export class SootSimBridgeFlowRunner {
|
|
|
2621
2655
|
// runFlowPlayback; absent for programmatic callers.
|
|
2622
2656
|
liveStatus: FlowLiveStatusReporter | null = null
|
|
2623
2657
|
|
|
2624
|
-
|
|
2658
|
+
resetFlowState() {
|
|
2659
|
+
this.flowTraceSteps = []
|
|
2660
|
+
this.lastFailedStep = null
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
extractFlowBody(steps: MaestroStep[]) {
|
|
2625
2664
|
// maestro `onFlowStart` / `onFlowComplete` lifecycle hooks: if any step
|
|
2626
2665
|
// in the array is a sole-key entry whose key is one of these names and
|
|
2627
2666
|
// whose value is a list of sub-steps, pull them out and run them as
|
|
@@ -2649,7 +2688,11 @@ export class SootSimBridgeFlowRunner {
|
|
|
2649
2688
|
}
|
|
2650
2689
|
body.push(step)
|
|
2651
2690
|
}
|
|
2691
|
+
return { onStart, onComplete, body }
|
|
2692
|
+
}
|
|
2652
2693
|
|
|
2694
|
+
async planFlow(steps: MaestroStep[], options: { dryRun?: boolean } = {}) {
|
|
2695
|
+
const { body } = this.extractFlowBody(steps)
|
|
2653
2696
|
if (this.liveStatus) {
|
|
2654
2697
|
await this.liveStatus.plan(
|
|
2655
2698
|
body.map((rawStep, index) => {
|
|
@@ -2660,8 +2703,17 @@ export class SootSimBridgeFlowRunner {
|
|
|
2660
2703
|
const name = Object.keys(step).find((k) => k !== 'when') ?? Object.keys(step)[0]
|
|
2661
2704
|
return { index, name, target: this.flowStepTargetLabel(step, name) }
|
|
2662
2705
|
}),
|
|
2706
|
+
options,
|
|
2663
2707
|
)
|
|
2664
2708
|
}
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
async runFlow(steps: MaestroStep[], options: { skipPlan?: boolean } = {}) {
|
|
2712
|
+
const { onStart, onComplete, body } = this.extractFlowBody(steps)
|
|
2713
|
+
|
|
2714
|
+
if (!options.skipPlan && this.liveStatus) {
|
|
2715
|
+
await this.planFlow(steps)
|
|
2716
|
+
}
|
|
2665
2717
|
|
|
2666
2718
|
if (onStart.length > 0) {
|
|
2667
2719
|
console.log(`[flow] onFlowStart (${onStart.length} steps)`)
|