peachsim 0.1.939 → 0.1.1399
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 +28 -22
- package/cli/app-catalog.ts +29 -14
- package/cli/app-fonts.ts +5 -3
- package/cli/bridge-flow-runner.ts +40 -29
- package/cli/cloud-client.ts +57 -21
- package/cli/cloud-test-tunnel.ts +12 -18
- package/cli/commands/agent.ts +1 -1
- package/cli/commands/app-fonts.ts +3 -3
- package/cli/commands/assert.ts +8 -8
- package/cli/commands/auth.ts +8 -3
- package/cli/commands/box/bundle-plane.ts +13 -1
- package/cli/commands/box.ts +16 -5
- package/cli/commands/compat.ts +1 -1
- package/cli/commands/config.ts +2 -2
- package/cli/commands/control.ts +125 -71
- package/cli/commands/cpu-profile.ts +45 -1
- package/cli/commands/daemon.ts +131 -33
- package/cli/commands/debug.ts +24 -17
- package/cli/commands/desktop.ts +7 -1
- package/cli/commands/detect.ts +101 -0
- package/cli/commands/detox.ts +6 -6
- package/cli/commands/device.ts +12 -12
- package/cli/commands/electron.ts +32 -14
- package/cli/commands/flow.ts +49 -41
- package/cli/commands/guest-host.ts +39 -1
- package/cli/commands/inspect/core.ts +14 -8
- package/cli/commands/inspect/list.ts +1 -1
- package/cli/commands/inspect/settle.ts +5 -3
- package/cli/commands/inspect/settling.ts +229 -44
- package/cli/commands/inspect/wait-idle.ts +3 -2
- package/cli/commands/inspect.ts +105 -93
- package/cli/commands/install-cli.ts +36 -15
- package/cli/commands/install-desktop.ts +68 -195
- package/cli/commands/kotlin.ts +6 -5
- package/cli/commands/login.ts +5 -5
- package/cli/commands/maestro-compare.ts +554 -0
- package/cli/commands/maestro-generate.ts +6 -3
- package/cli/commands/maestro.ts +22 -0
- package/cli/commands/mode.ts +2 -2
- package/cli/commands/no-bridge-hint.ts +6 -2
- package/cli/commands/platform.ts +35 -6
- package/cli/commands/preview.ts +6 -0
- package/cli/commands/project-report.ts +10 -12
- package/cli/commands/record.ts +37 -37
- package/cli/commands/runtime.ts +9 -9
- package/cli/commands/screenshot-layers.ts +4 -4
- package/cli/commands/screenshot-mode.ts +2 -2
- package/cli/commands/screenshot.ts +8 -8
- package/cli/commands/screenshots-capture.ts +2 -2
- package/cli/commands/serve.ts +9 -12
- package/cli/commands/setup.ts +29 -14
- package/cli/commands/shell-boolean-mode.ts +2 -2
- package/cli/commands/show-touches-mode.ts +2 -2
- package/cli/commands/skills.ts +11 -11
- package/cli/commands/swift.ts +29 -11
- package/cli/commands/telemetry.ts +2 -2
- package/cli/commands/test.ts +5 -0
- package/cli/commands/three-mode.ts +3 -3
- package/cli/commands/timeline.ts +4 -4
- package/cli/commands/upgrade.ts +2 -2
- package/cli/commands/upload.ts +15 -15
- package/cli/commands/what-happened.ts +2 -2
- package/cli/current-sim.ts +6 -3
- package/cli/desktop-companion.ts +55 -149
- package/cli/dev-build-freshness.ts +2 -2
- package/cli/drivers/desktop.ts +17 -9
- package/cli/drivers/electron.ts +1 -2
- package/cli/drivers/playwright-sim-host.ts +11 -2
- package/cli/drivers/playwright.ts +35 -2
- package/cli/drivers/types.ts +8 -0
- package/cli/maestro-compare.ts +670 -0
- package/cli/outbound-endpoints.ts +46 -8
- package/cli/recording-access.ts +6 -6
- package/cli/run-registry.ts +1 -1
- package/cli/runtime-compat.ts +5 -1
- package/cli/send-to-box.ts +3 -3
- package/cli/shell-init.ts +2 -1
- package/cli/ws-bridge.ts +9 -5
- package/detox/element-types.ts +1 -0
- package/detox/gestures.ts +3 -3
- package/detox/index.ts +67 -38
- package/detox/navigation-lifecycle.ts +9 -6
- package/detox/proof-frame.cjs +6 -4
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- 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 +42 -17
- package/dist-lib/bridge-contract-input.mjs +41 -16
- package/dist-lib/bridge-contract.cjs +1 -4
- package/dist-lib/bridge-contract.mjs +1 -3
- package/dist-lib/bundle-channel.cjs +2918 -550
- package/dist-lib/capture-contract.cjs +65 -24
- package/dist-lib/capture-contract.mjs +63 -24
- package/dist-lib/cloud-contract.cjs +2 -2
- package/dist-lib/cloud-contract.mjs +2 -2
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +3 -3
- package/dist-lib/cloud.mjs +3 -3
- package/dist-lib/config.cjs +15 -5
- package/dist-lib/config.mjs +154 -0
- package/dist-lib/detox/index.cjs +495 -139
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +1168 -238
- package/dist-lib/expo-swift-metadata.cjs +34 -23
- package/dist-lib/expo-swift-metadata.mjs +32 -23
- package/dist-lib/home-paths.cjs +2 -2
- package/dist-lib/host/bridge-host.cjs +2938 -654
- package/dist-lib/host/fetch-proxy-handler.cjs +14 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +7 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +5 -1
- package/dist-lib/host/replacement-module-handler.cjs +2 -2
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +999 -353
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +332 -19
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- 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 +7 -7
- package/dist-lib/metro-production-bundle.mjs +5 -5
- package/dist-lib/metro.cjs +688 -250
- package/dist-lib/native-app-identity-project.cjs +2647 -0
- package/dist-lib/native-app-identity-project.mjs +1257 -0
- package/dist-lib/native-app-identity.cjs +325 -25
- package/dist-lib/native-app-identity.mjs +323 -25
- package/dist-lib/native-firebase-metadata.cjs +1 -1
- package/dist-lib/native-firebase-metadata.mjs +1 -1
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +5 -1
- package/dist-lib/react-native-host-modules.mjs +5 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +57 -32
- package/dist-lib/sdk.cjs +231 -51
- package/dist-lib/sdk.mjs +231 -51
- package/dist-lib/skills.cjs +4107 -1374
- package/dist-lib/swift-compile.cjs +290 -30
- package/dist-lib/swift-compile.mjs +290 -30
- package/dist-lib/swift-package.cjs +143 -12
- package/dist-lib/swift-package.mjs +137 -11
- package/dist-lib/swift-submit.cjs +47 -5
- package/dist-lib/swift-submit.mjs +42 -5
- package/dist-lib/swift-toolchain.cjs +7 -2
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +1392 -458
- package/dist-lib/vite.cjs +13 -17
- package/dist-lib/worklet-executable.cjs +248 -33
- package/dist-lib/worklet-executable.mjs +248 -33
- package/install.sh +10 -1
- package/kotlin/{AppSources → AndroidViewSources}/android/content/res/Resources.kt +9 -9
- package/kotlin/AndroidViewSources/android/graphics/Canvas.kt +20 -0
- package/kotlin/AndroidViewSources/android/view/View.kt +211 -0
- package/kotlin/AndroidViewSources/android/widget/FrameLayout.kt +61 -0
- package/kotlin/AndroidViewSources/android/widget/LinearLayout.kt +49 -0
- package/kotlin/AppSources/android/view/Window.kt +36 -0
- package/kotlin/AppSources/android/view/WindowInsets.kt +9 -0
- package/kotlin/AppSources/android/view/WindowInsetsController.kt +5 -0
- package/kotlin/AppSources/android/window/OnBackInvokedDispatcher.kt +48 -0
- package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcher.kt +20 -5
- package/kotlin/AppSources/androidx/compose/foundation/layout/WindowInsets.kt +15 -0
- package/kotlin/AppSources/androidx/compose/ui/platform/ComposeView.kt +2 -1
- package/kotlin/AppSources/dev/peach/app/PeachApp.kt +42 -50
- package/kotlin/ComposeSources/PeachComposeHost.kt +200 -84
- package/kotlin/ExpoComposeSources/PeachAndroidView.kt +87 -0
- package/kotlin/ExpoComposeSources/PeachExpoViewMount.kt +24 -0
- package/kotlin/ExpoComposeSources/dev/peach/expo/PeachExpoCompose.kt +28 -0
- package/kotlin/ExpoComposeSources/expo/modules/kotlin/views/ExpoComposeView.kt +70 -0
- package/kotlin/ExpoSources/dev/peach/expo/PeachExpoRuntime.kt +513 -0
- package/kotlin/ExpoSources/dev/peach/expo/PeachExpoTypes.kt +173 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/AppContext.kt +20 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/Promise.kt +19 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/classcomponent/ClassComponentBuilder.kt +198 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/exception/CodedException.kt +126 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/functions/Functions.kt +132 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/jni/JavaScriptTypedArray.kt +81 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/modules/Module.kt +98 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +236 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/records/Record.kt +12 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/runtime/Runtime.kt +9 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedObject.kt +50 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedObjectRegistry.kt +57 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedRef.kt +19 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/ConcreteTypedArrays.kt +158 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/GenericTypedArray.kt +8 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/TypedArray.kt +29 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/TypedArrayIterator.kt +9 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/types/AnyType.kt +155 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/types/Either.kt +91 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/types/Enumerable.kt +6 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/viewevent/ViewEvent.kt +54 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/views/ExpoView.kt +23 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +71 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewProps.kt +35 -0
- package/kotlin/README.md +13 -3
- package/kotlin/Sources/android/os/Build.kt +17 -0
- package/kotlin/Sources/android/os/Handler.kt +35 -0
- package/kotlin/Sources/android/os/Looper.kt +17 -0
- package/kotlin/Sources/android/os/MessageQueue.kt +56 -0
- package/kotlin/Sources/android/os/SystemClock.kt +14 -0
- package/kotlin/Sources/android/util/Log.kt +64 -0
- package/kotlin/Sources/java/security/MessageDigest.kt +114 -0
- package/kotlin/Sources/kotlin/text/StringFormat.kt +9 -0
- package/kotlin/Sources/org/json/JSON.kt +44 -0
- package/kotlin/fixtures/counter/Counter.d.kt.ts +1 -0
- package/kotlin/fixtures/counter/Counter.kt +10 -3
- package/kotlin/fixtures/counter/CounterApp.native.tsx +1 -1
- package/kotlin/fixtures/counter/vite.config.ts +2 -2
- package/kotlin/fixtures/headless-counter/Headless.kt +7 -6
- package/kotlin/fixtures/import-animation/Animation.d.kt.ts +7 -0
- package/kotlin/fixtures/import-animation/Animation.kt +80 -0
- package/kotlin/fixtures/import-animation/ImportAnimation.native.tsx +13 -0
- package/kotlin/fixtures/import-animation/README.md +24 -0
- package/kotlin/fixtures/import-animation/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-animation/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-animation/app/routes.d.ts +18 -0
- package/kotlin/fixtures/import-animation/package.json +23 -0
- package/kotlin/fixtures/import-animation/tsconfig.json +20 -0
- package/kotlin/fixtures/import-animation/vite.config.ts +20 -0
- package/kotlin/fixtures/import-editor/Editor.d.kt.ts +7 -0
- package/kotlin/fixtures/import-editor/Editor.kt +59 -0
- package/kotlin/fixtures/import-editor/ImportEditor.native.tsx +23 -0
- package/kotlin/fixtures/import-editor/README.md +27 -0
- package/kotlin/fixtures/import-editor/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-editor/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-editor/package.json +23 -0
- package/kotlin/fixtures/import-editor/tsconfig.json +20 -0
- package/kotlin/fixtures/import-editor/vite.config.ts +20 -0
- package/kotlin/fixtures/import-effects/Effects.d.kt.ts +7 -0
- package/kotlin/fixtures/import-effects/Effects.kt +87 -0
- package/kotlin/fixtures/import-effects/ImportEffects.native.tsx +13 -0
- package/kotlin/fixtures/import-effects/README.md +19 -0
- package/kotlin/fixtures/import-effects/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-effects/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-effects/package.json +23 -0
- package/kotlin/fixtures/import-effects/tsconfig.json +20 -0
- package/kotlin/fixtures/import-effects/vite.config.ts +20 -0
- package/kotlin/fixtures/import-layout/ImportLayout.native.tsx +13 -0
- package/kotlin/fixtures/import-layout/Layout.d.kt.ts +7 -0
- package/kotlin/fixtures/import-layout/Layout.kt +72 -0
- package/kotlin/fixtures/import-layout/README.md +24 -0
- package/kotlin/fixtures/import-layout/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-layout/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-layout/package.json +23 -0
- package/kotlin/fixtures/import-layout/tsconfig.json +20 -0
- package/kotlin/fixtures/import-layout/vite.config.ts +20 -0
- package/kotlin/fixtures/import-navigation/ImportNavigation.native.tsx +18 -0
- package/kotlin/fixtures/import-navigation/Navigation.d.kt.ts +7 -0
- package/kotlin/fixtures/import-navigation/Navigation.kt +104 -0
- package/kotlin/fixtures/import-navigation/README.md +31 -0
- package/kotlin/fixtures/import-navigation/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-navigation/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-navigation/package.json +23 -0
- package/kotlin/fixtures/import-navigation/tsconfig.json +20 -0
- package/kotlin/fixtures/import-navigation/vite.config.ts +20 -0
- package/kotlin/fixtures/import-overlays/ImportOverlays.native.tsx +29 -0
- package/kotlin/fixtures/import-overlays/Overlays.d.kt.ts +7 -0
- package/kotlin/fixtures/import-overlays/Overlays.kt +93 -0
- package/kotlin/fixtures/import-overlays/README.md +34 -0
- package/kotlin/fixtures/import-overlays/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-overlays/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-overlays/package.json +23 -0
- package/kotlin/fixtures/import-overlays/tsconfig.json +20 -0
- package/kotlin/fixtures/import-overlays/vite.config.ts +20 -0
- package/kotlin/fixtures/import-scroll/ImportScroll.native.tsx +13 -0
- package/kotlin/fixtures/import-scroll/README.md +23 -0
- package/kotlin/fixtures/import-scroll/Scroll.d.kt.ts +7 -0
- package/kotlin/fixtures/import-scroll/Scroll.kt +91 -0
- package/kotlin/fixtures/import-scroll/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-scroll/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-scroll/package.json +23 -0
- package/kotlin/fixtures/import-scroll/tsconfig.json +20 -0
- package/kotlin/fixtures/import-scroll/vite.config.ts +20 -0
- package/kotlin/fixtures/import-selection/ImportSelection.native.tsx +44 -0
- package/kotlin/fixtures/import-selection/README.md +32 -0
- package/kotlin/fixtures/import-selection/Selection.d.kt.ts +8 -0
- package/kotlin/fixtures/import-selection/Selection.kt +78 -0
- package/kotlin/fixtures/import-selection/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-selection/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-selection/package.json +23 -0
- package/kotlin/fixtures/import-selection/tsconfig.json +20 -0
- package/kotlin/fixtures/import-selection/vite.config.ts +20 -0
- package/kotlin/fixtures/import-surfaces/ImportSurfaces.native.tsx +13 -0
- package/kotlin/fixtures/import-surfaces/README.md +22 -0
- package/kotlin/fixtures/import-surfaces/Surfaces.d.kt.ts +7 -0
- package/kotlin/fixtures/import-surfaces/Surfaces.kt +64 -0
- package/kotlin/fixtures/import-surfaces/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-surfaces/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-surfaces/package.json +23 -0
- package/kotlin/fixtures/import-surfaces/tsconfig.json +20 -0
- package/kotlin/fixtures/import-surfaces/vite.config.ts +20 -0
- package/kotlin/fixtures/import-values/ImportValues.native.tsx +35 -0
- package/kotlin/fixtures/import-values/README.md +23 -0
- package/kotlin/fixtures/import-values/Values.d.kt.ts +11 -0
- package/kotlin/fixtures/import-values/Values.kt +66 -0
- package/kotlin/fixtures/import-values/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-values/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-values/package.json +23 -0
- package/kotlin/fixtures/import-values/tsconfig.json +20 -0
- package/kotlin/fixtures/import-values/vite.config.ts +20 -0
- package/kotlin/fixtures/kotlin-app-variants/BarBehavior.kt +38 -0
- package/kotlin/fixtures/kotlin-app-variants/BarVisibility.kt +36 -0
- package/kotlin/fixtures/kotlin-app-variants/Ime.kt +3 -1
- package/kotlin/fixtures/kotlin-app-variants/TransientInsets.kt +50 -0
- package/kotlin/fork/NOTICE +6 -1
- package/kotlin/fork/README.md +15 -7
- package/kotlin/fork/build.sh +2 -0
- package/kotlin/fork/compose-worker-globals.patch +186 -4
- package/kotlin/fork/fork.json +2 -2
- package/kotlin/fork/skiko-worker-clock.patch +12 -0
- package/kotlin/toolchain.json +12 -12
- package/kotlin/tools/expo-types.main.kts +115 -0
- package/package.json +17 -23
- package/scripts/dev-server-scanner.ts +92 -66
- package/skills/contrast/SKILL.md +2 -2
- package/skills/peach-debug/SKILL.md +1 -1
- package/skills/peach-perf/SKILL.md +1 -1
- package/skills/peach-setup/SKILL.md +2 -1
- package/skills/peach-test/SKILL.md +8 -0
- package/src/assetCatalogContract.ts +43 -1
- package/src/bridge-contract-input.ts +46 -19
- package/src/bridge-contract.ts +0 -1
- package/src/bundle-channel.mjs +197 -84
- package/src/bundleGlobalFooter.ts +38 -0
- package/src/capture-contract.ts +107 -33
- package/src/cloud-contract.ts +5 -1
- package/src/cloud.ts +5 -2
- package/src/config.ts +22 -3
- package/src/configured-bundle-producer.ts +1 -1
- package/src/desktop-host-delivery.ts +95 -1
- package/src/expo-kotlin-bundle.ts +29 -0
- package/src/expo-kotlin-host.ts +201 -0
- package/src/expo-swift-host.ts +96 -76
- package/src/expoKotlinContract.ts +70 -0
- package/src/expoSwiftContract.ts +4 -25
- package/src/home-paths.ts +7 -4
- package/src/host/bridge-host.ts +23 -3
- package/src/host/fetch-proxy-handler.ts +13 -0
- package/src/host/fetch-proxy-overrides.ts +11 -0
- package/src/host/replacement-module-handler.ts +5 -2
- package/src/index.ts +3 -1
- package/src/kotlin-host.ts +463 -19
- package/src/metro-plugin.ts +56 -28
- package/src/metro-production-bundle.ts +2 -2
- package/src/native-app-identity-project.ts +190 -0
- package/src/native-app-identity.ts +398 -34
- package/src/native-firebase-config.ts +69 -15
- package/src/native-seam-manifest.ts +6 -0
- package/src/one-widget-discovery.ts +2 -1
- package/src/openstep-plist.ts +107 -0
- package/src/react-native-host-modules.ts +4 -0
- package/src/runtime-assets.ts +2 -2
- package/src/sim-client.ts +2 -2
- package/src/swift-binary-modules.ts +3 -1
- package/src/swift-compile.ts +286 -38
- package/src/swift-distribution-contract.ts +4 -0
- package/src/swift-distribution.lock.json +123 -12
- package/src/swift-distribution.ts +7 -3
- package/src/swift-host.ts +298 -83
- package/src/swift-package-manifest.ts +29 -2
- package/src/swift-package.ts +180 -10
- package/src/swift-packages.ts +4 -0
- package/src/swift-standin-emit.ts +4 -1
- package/src/swift-standins.ts +274 -15
- package/src/swift-submit.ts +62 -7
- package/src/swift-toolchain.ts +6 -1
- package/src/swiftContract.ts +126 -3
- package/src/vite-plugin-one.ts +13 -16
- package/src/vite-plugin-swift.ts +4 -5
- package/src/vite-plugin.ts +360 -144
- package/src/vite.d.ts +1 -1
- package/src/worker-system-chunks.ts +221 -0
- package/src/worklet-executable.ts +348 -38
- package/src/worklets-babel.ts +1 -1
- package/src/xcode-project.ts +243 -127
- package/dist-lib/agent-identity.cjs +0 -39
- package/dist-lib/agent-sessions.cjs +0 -760
- package/dist-lib/cli-constants.cjs +0 -32
- package/dist-lib/render-mode.cjs +0 -44
- package/kotlin/AppSources/android/view/View.kt +0 -74
- package/kotlin/AppSources/android/widget/FrameLayout.kt +0 -6
- package/src/render-mode.ts +0 -15
- /package/kotlin/{AppSources → AndroidViewSources}/android/content/Context.kt +0 -0
- /package/kotlin/{AppSources → AndroidViewSources}/android/content/res/Configuration.kt +0 -0
- /package/kotlin/{AppSources → AndroidViewSources}/android/graphics/Color.kt +0 -0
- /package/kotlin/{AppSources → AndroidViewSources}/android/util/DisplayMetrics.kt +0 -0
- /package/kotlin/{AppSources → AndroidViewSources}/dev/peach/app/JavaFormat.kt +0 -0
- /package/kotlin/{AppSources → Sources}/java/lang/Runnable.kt +0 -0
package/README.md
CHANGED
|
@@ -95,7 +95,7 @@ graph TD
|
|
|
95
95
|
TENANTW["tenant worker (guest RN app tree)"]
|
|
96
96
|
end
|
|
97
97
|
subgraph build["build-time plugins (src/)"]
|
|
98
|
-
VITEONE["peachsim/vite = vite-plugin-one.ts (serve runtime at /
|
|
98
|
+
VITEONE["peachsim/vite = vite-plugin-one.ts (serve runtime at /__peachsim/)"]
|
|
99
99
|
VITERN["sootsim() = vite-plugin.ts (RN resolver + native stubs)"]
|
|
100
100
|
METRO["peachsim/metro = metro-plugin.ts"]
|
|
101
101
|
COMPAT["@sootsim/compat stub-manifest (native seams)"]
|
|
@@ -139,8 +139,8 @@ graph TD
|
|
|
139
139
|
RTHTTP -->|"manifest + tarball"| CDN
|
|
140
140
|
DRIVERS -->|"open sim page URL"| SHELLW
|
|
141
141
|
SHELLW -->|"NativeUIRequest"| TENANTW
|
|
142
|
-
VITEONE -->|"serve /
|
|
143
|
-
METRO -->|"serve /
|
|
142
|
+
VITEONE -->|"serve /__peachsim/ from"| RTDIR
|
|
143
|
+
METRO -->|"serve /__peachsim/ from"| RTDIR
|
|
144
144
|
VITEONE -.->|"launch electron app"| SHELLW
|
|
145
145
|
VITERN -->|"alias react-native"| ENGINESHIM
|
|
146
146
|
VITERN -->|"alias native pkgs"| COMPAT
|
|
@@ -165,7 +165,7 @@ An explicit `--base-url` keeps that shell's origin and uses its assets without
|
|
|
165
165
|
installing an active runtime. A bridge started for that shell skips automatic
|
|
166
166
|
runtime updates. An app's configured `runtimeVersion` still selects its installed,
|
|
167
167
|
version-owned bridge origin.
|
|
168
|
-
|
|
168
|
+
The native host supplies the desktop surface on Mac and Linux; Electron is Windows-only. Team Machine sessions (`TM_SESSION`)
|
|
169
169
|
default to headless Playwright; `--headed` explicitly opens an isolated headed
|
|
170
170
|
browser. System browser launches, including share links, login, box links and
|
|
171
171
|
repo scripts, go through `src/host/open-url.ts`. That owner refuses a system
|
|
@@ -203,9 +203,9 @@ bridge always addresses the **shell worker**, never an undifferentiated sim.
|
|
|
203
203
|
|
|
204
204
|
**Two distinct plugin families** round it out, and they must not be conflated:
|
|
205
205
|
|
|
206
|
-
- The published **`peachsim/vite`** (`src/vite-plugin-one.ts`, `
|
|
206
|
+
- The published **`peachsim/vite`** (`src/vite-plugin-one.ts`, `peachPlugin`)
|
|
207
207
|
and **`peachsim/metro`** plugins do one thing: serve the installed runtime at
|
|
208
|
-
`/
|
|
208
|
+
`/__peachsim/` so an app's own dev server can host the sim shell. The Vite plugin accepts an
|
|
209
209
|
opt-in `open` option that launches a named Electron window when the dev server
|
|
210
210
|
starts (skipped when `CI` or `PEACH_NO_OPEN` is set).
|
|
211
211
|
- The internal **`sootsim()`** plugin (`src/vite-plugin.ts`) is a separate, much
|
|
@@ -236,13 +236,13 @@ the later trace URL notifies viewers without extending that duration.
|
|
|
236
236
|
Maestro YAML header `onFlowStart` and `onFlowComplete` hooks reach the same
|
|
237
237
|
runner as body hooks, including nested `runFlow` files. A header reset flow
|
|
238
238
|
therefore runs before assertions in each flow.
|
|
239
|
-
| sim drivers | Launch or attach the actual sim page. Playwright owns isolated browser profiles;
|
|
239
|
+
| sim drivers | Launch or attach the actual sim page. Playwright owns isolated browser profiles; the native host owns Mac and Linux desktop windows; Electron owns Windows windows. 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` |
|
|
240
240
|
| SootSimBridgeHost (serve/daemon) | The bridge process (`peach 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` |
|
|
241
241
|
| 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` |
|
|
242
242
|
| 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` |
|
|
243
243
|
| 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 `~/.peach/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` |
|
|
244
244
|
| 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` |
|
|
245
|
-
| `peachsim/vite` + `peachsim/metro` (runtime serving) | Published bundler plugins. Both serve the installed engine runtime at `/
|
|
245
|
+
| `peachsim/vite` + `peachsim/metro` (runtime serving) | Published bundler plugins. Both serve the installed engine runtime at `/__peachsim/` from `~/.peach/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` |
|
|
246
246
|
| `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 `peachsim/vite` export. | `src/vite-plugin.ts`, `src/worklets-babel.ts`, `packages/peach-compat/src/stub-manifest.ts` |
|
|
247
247
|
| skills registry | Peach agent skills (`peach-setup`, `peach-debug`, `peach-test`, `peach-visual`, and the generated `contrast` index) installable with `peach skill`. | `skills/*.md`, `src/skills/registry.ts`, `src/skills/types.ts` |
|
|
248
248
|
|
|
@@ -418,7 +418,7 @@ peach detox # run Detox/Jest suites against Peach
|
|
|
418
418
|
```
|
|
419
419
|
|
|
420
420
|
See the full command reference via `peach --help` or at
|
|
421
|
-
`src/features/site/docs/
|
|
421
|
+
`src/features/site/docs/peach/cli/` in the Contrast repo.
|
|
422
422
|
|
|
423
423
|
Sim-scoped commands use the primary fallback only when one driveable sim is
|
|
424
424
|
connected. With multiple driveable sims, the bridge lists their ids and refuses
|
|
@@ -441,21 +441,21 @@ The Vite plugin serves the installed Peach runtime from your existing dev server
|
|
|
441
441
|
|
|
442
442
|
```ts
|
|
443
443
|
// vite.config.ts
|
|
444
|
-
import {
|
|
444
|
+
import { peachPlugin } from 'peachsim/vite'
|
|
445
445
|
|
|
446
446
|
export default {
|
|
447
|
-
plugins: [
|
|
447
|
+
plugins: [peachPlugin({ open: { appName: 'My App' } })],
|
|
448
448
|
}
|
|
449
449
|
```
|
|
450
450
|
|
|
451
451
|
```js
|
|
452
452
|
// metro.config.js
|
|
453
453
|
const { getDefaultConfig } = require('expo/metro-config')
|
|
454
|
-
const {
|
|
454
|
+
const { withPeach } = require('peachsim/metro')
|
|
455
455
|
|
|
456
456
|
const config = getDefaultConfig(__dirname)
|
|
457
457
|
|
|
458
|
-
module.exports =
|
|
458
|
+
module.exports = withPeach(config)
|
|
459
459
|
```
|
|
460
460
|
|
|
461
461
|
Peach works directly with regular, unannotated Metro production JavaScript bundles
|
|
@@ -465,7 +465,7 @@ a supported input; pass Peach the JavaScript bundle produced before Hermes
|
|
|
465
465
|
compilation.
|
|
466
466
|
|
|
467
467
|
If an app has unusual dependencies or fingerprint inference fails, you can
|
|
468
|
-
optionally wrap your Metro configuration with `
|
|
468
|
+
optionally wrap your Metro configuration with `withPeach(config)`. It takes no
|
|
469
469
|
options and enables two integrations:
|
|
470
470
|
|
|
471
471
|
1. **Deterministic module identity:** appends Metro's exact module identity map
|
|
@@ -473,7 +473,7 @@ options and enables two integrations:
|
|
|
473
473
|
bundle. Resolution, transforms, minification, and source maps stay the app's
|
|
474
474
|
own, so the annotated bundle is the exact bundle you ship: the footer assigns
|
|
475
475
|
one unused global on a device, and Hermes drops it during bytecode compilation.
|
|
476
|
-
2. **In-browser dev shell:** serves the Peach simulator shell at `/
|
|
476
|
+
2. **In-browser dev shell:** serves the Peach simulator shell at `/__peachsim/` on
|
|
477
477
|
the Metro dev server.
|
|
478
478
|
|
|
479
479
|
The URL helpers request ordinary Metro graphs: `dev=false&minify=true` for
|
|
@@ -481,14 +481,14 @@ production and `dev=true&minify=false` for development.
|
|
|
481
481
|
|
|
482
482
|
```js
|
|
483
483
|
const {
|
|
484
|
-
|
|
485
|
-
|
|
484
|
+
toPeachProductionBundleUrl,
|
|
485
|
+
toPeachDevelopmentBundleUrl,
|
|
486
486
|
} = require('peachsim/metro')
|
|
487
487
|
|
|
488
|
-
const productionUrl =
|
|
488
|
+
const productionUrl = toPeachProductionBundleUrl(
|
|
489
489
|
'http://localhost:8081/index.bundle?platform=ios',
|
|
490
490
|
)
|
|
491
|
-
const developmentUrl =
|
|
491
|
+
const developmentUrl = toPeachDevelopmentBundleUrl(
|
|
492
492
|
'http://localhost:8081/index.bundle?platform=ios',
|
|
493
493
|
)
|
|
494
494
|
```
|
|
@@ -629,12 +629,12 @@ the generated `contrast` command index. The published CLI registry and generated
|
|
|
629
629
|
website docs both come from `packages/peach-skills/`:
|
|
630
630
|
|
|
631
631
|
- `packages/peachsim/skills/contrast/SKILL.md` is generated from that registry
|
|
632
|
-
- `src/features/site/docs/
|
|
632
|
+
- `src/features/site/docs/peach/cli/*` is generated output, not hand-edited
|
|
633
633
|
|
|
634
634
|
When command docs or examples drift, update `packages/peach-skills/` and run:
|
|
635
635
|
|
|
636
636
|
```sh
|
|
637
|
-
bun run generate:
|
|
637
|
+
bun run generate:peach-docs
|
|
638
638
|
```
|
|
639
639
|
|
|
640
640
|
## Runtime delivery (the Peach home and CDN)
|
|
@@ -650,7 +650,7 @@ verifies its sha256, and unpacks it under `~/.peach`:
|
|
|
650
650
|
```
|
|
651
651
|
~/.peach/
|
|
652
652
|
├── runtimes/
|
|
653
|
-
│ ├── <active>/ unpacked engine assets (served at /
|
|
653
|
+
│ ├── <active>/ unpacked engine assets (served at /__peachsim/)
|
|
654
654
|
│ └── <rollback>/ previous version for rollback
|
|
655
655
|
├── cache/ legacy downloads; empty after cleanup
|
|
656
656
|
├── profiles/ persistent named app storage, without browser caches
|
|
@@ -691,6 +691,12 @@ CLI's cwd is never an asset/config fallback. Asset wires stay on each opened
|
|
|
691
691
|
project URL, so one daemon can serve several attached projects without sharing
|
|
692
692
|
font or splash state between them.
|
|
693
693
|
|
|
694
|
+
Bare iOS projects carry default Firebase options from their selected app
|
|
695
|
+
target's Google services build resource when the app delegate initializes
|
|
696
|
+
Firebase unconditionally during launch. Completed compile conditional blocks
|
|
697
|
+
before that call are allowed. Initialization inside an open conditional block
|
|
698
|
+
or after a possible early return is rejected.
|
|
699
|
+
|
|
694
700
|
The first interactive CLI command after this cleanup generation finishes its
|
|
695
701
|
requested work, then launches the same safe default plan in a detached
|
|
696
702
|
maintenance process. The app and terminal command are usable before the legacy
|
package/cli/app-catalog.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
enumerateBundleColors,
|
|
5
|
+
enumerateBundleImages,
|
|
6
|
+
} from '../../sootsim-swift/src/bundleAssets'
|
|
4
7
|
import type { AppCatalog } from 'peach-engine/engine/app-catalog-wire'
|
|
5
8
|
|
|
6
9
|
// the xcode build setting actool turns into the built Info.plist's
|
|
@@ -33,12 +36,16 @@ function catalogFiles(dir: string, found: string[]) {
|
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
// the app's asset-catalog colors and its declared accent, read off
|
|
37
|
-
// project a prebuild or a bare app keeps on disk: the app target is
|
|
38
|
-
// folder holding the AppDelegate, the catalogs are its .xcassets, and the
|
|
39
|
-
// accent is what its Info.plist or project build settings name.
|
|
40
|
-
//
|
|
41
|
-
|
|
39
|
+
// the app's asset-catalog colors and images and its declared accent, read off
|
|
40
|
+
// the ios project a prebuild or a bare app keeps on disk: the app target is
|
|
41
|
+
// the folder holding the AppDelegate, the catalogs are its .xcassets, and the
|
|
42
|
+
// accent is what its Info.plist or project build settings name. metro serves
|
|
43
|
+
// each picked raster at `<origin>/assets/<project-relative-path>`. null when
|
|
44
|
+
// the app has no ios project on disk or declares none of them.
|
|
45
|
+
export function discoverAppCatalog(
|
|
46
|
+
appDir: string,
|
|
47
|
+
metroOrigin: string,
|
|
48
|
+
): AppCatalog | null {
|
|
42
49
|
const iosDir = path.join(appDir, 'ios')
|
|
43
50
|
const target = listDir(iosDir).find(
|
|
44
51
|
(entry) =>
|
|
@@ -58,11 +65,14 @@ export function discoverAppCatalog(appDir: string): AppCatalog | null {
|
|
|
58
65
|
const relative = files.map((file) =>
|
|
59
66
|
path.relative(targetDir, file).split(path.sep).join(path.posix.sep),
|
|
60
67
|
)
|
|
61
|
-
const
|
|
62
|
-
candidate.endsWith('Contents.json')
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
const readContents = (candidate: string) =>
|
|
69
|
+
candidate.endsWith('Contents.json') ? readText(path.join(targetDir, candidate)) : null
|
|
70
|
+
const colors = enumerateBundleColors(relative, readContents)
|
|
71
|
+
const served = path.relative(appDir, targetDir).split(path.sep).join(path.posix.sep)
|
|
72
|
+
const images: AppCatalog['images'] = {}
|
|
73
|
+
for (const { name, file, scale } of enumerateBundleImages(relative, readContents)) {
|
|
74
|
+
;(images[name] ??= []).push({ uri: `${metroOrigin}/assets/${served}/${file}`, scale })
|
|
75
|
+
}
|
|
66
76
|
const pbxproj = listDir(iosDir).find((entry) => entry.name.endsWith('.xcodeproj'))
|
|
67
77
|
const accentName =
|
|
68
78
|
readText(path.join(targetDir, 'Info.plist'))?.match(PLIST_ACCENT)?.[1] ??
|
|
@@ -72,6 +82,11 @@ export function discoverAppCatalog(appDir: string): AppCatalog | null {
|
|
|
72
82
|
)?.[1]
|
|
73
83
|
: undefined) ??
|
|
74
84
|
null
|
|
75
|
-
if (
|
|
76
|
-
|
|
85
|
+
if (
|
|
86
|
+
accentName === null &&
|
|
87
|
+
Object.keys(colors).length === 0 &&
|
|
88
|
+
Object.keys(images).length === 0
|
|
89
|
+
)
|
|
90
|
+
return null
|
|
91
|
+
return { colors, images, accentName }
|
|
77
92
|
}
|
package/cli/app-fonts.ts
CHANGED
|
@@ -305,10 +305,12 @@ export async function resolveMetroProjectRoot(
|
|
|
305
305
|
const controller = new AbortController()
|
|
306
306
|
const timer = setTimeout(() => controller.abort(), opts?.timeoutMs ?? 1500)
|
|
307
307
|
let servedManifest = false
|
|
308
|
+
let metroRunning = false
|
|
308
309
|
try {
|
|
309
310
|
const status = await fetch(`${origin}/status`, { signal: controller.signal })
|
|
310
311
|
const advertisedRoot = status.headers.get('X-React-Native-Project-Root')
|
|
311
312
|
if (status.ok && advertisedRoot) return decodeURI(advertisedRoot)
|
|
313
|
+
metroRunning = status.ok && (await status.text()).trim() === 'packager-status:running'
|
|
312
314
|
const res = await fetch(`${origin}/`, {
|
|
313
315
|
headers: { 'expo-platform': 'ios', Accept: 'application/json' },
|
|
314
316
|
signal: controller.signal,
|
|
@@ -330,9 +332,9 @@ export async function resolveMetroProjectRoot(
|
|
|
330
332
|
// knows. if it did not name a project root there is none to learn, and asking
|
|
331
333
|
// the OS about the listening process would answer with whatever directory
|
|
332
334
|
// that process happens to sit in — which is the CLI's own cwd trap this
|
|
333
|
-
// function exists to avoid. only a server
|
|
334
|
-
// the process lookup.
|
|
335
|
-
if (servedManifest) return null
|
|
335
|
+
// function exists to avoid. only a server whose status identifies Metro
|
|
336
|
+
// and which served no manifest gets the process lookup.
|
|
337
|
+
if (servedManifest || !metroRunning) return null
|
|
336
338
|
|
|
337
339
|
const port = Number(new URL(origin).port)
|
|
338
340
|
if (!Number.isFinite(port) || port <= 0) return null
|
|
@@ -548,6 +548,12 @@ export type SootSimFlowTraceStep = {
|
|
|
548
548
|
screenshotPath?: string
|
|
549
549
|
}
|
|
550
550
|
|
|
551
|
+
export type SootSimFlowConsole = {
|
|
552
|
+
errors: unknown[]
|
|
553
|
+
warnings: unknown[]
|
|
554
|
+
requests: unknown[]
|
|
555
|
+
}
|
|
556
|
+
|
|
551
557
|
export interface MaestroStep {
|
|
552
558
|
tapOn?:
|
|
553
559
|
| string
|
|
@@ -2135,6 +2141,24 @@ export class SootSimBridgeFlowRunner {
|
|
|
2135
2141
|
return model as DeviceModel
|
|
2136
2142
|
}
|
|
2137
2143
|
|
|
2144
|
+
// the app's console errors and warnings plus failed requests, captured by
|
|
2145
|
+
// peach's native-globals hook and request recorder (same shape as
|
|
2146
|
+
// `peach get errors`).
|
|
2147
|
+
async readConsole(): Promise<SootSimFlowConsole> {
|
|
2148
|
+
return (await this.bridge.send({
|
|
2149
|
+
type: 'evaluate',
|
|
2150
|
+
simId: this.opts.simId,
|
|
2151
|
+
code: `(() => {
|
|
2152
|
+
const c = window.__sootsimConsole
|
|
2153
|
+
return {
|
|
2154
|
+
errors: c?.getErrors?.() ?? [],
|
|
2155
|
+
warnings: c?.getWarnings?.() ?? [],
|
|
2156
|
+
requests: (window.__sootsimGetFailedRequests?.() ?? []),
|
|
2157
|
+
}
|
|
2158
|
+
})()`,
|
|
2159
|
+
})) as SootSimFlowConsole
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2138
2162
|
async captureScreenshot(outputPath: string) {
|
|
2139
2163
|
await this.takeScreenshot({ path: outputPath })
|
|
2140
2164
|
}
|
|
@@ -2262,20 +2286,8 @@ export class SootSimBridgeFlowRunner {
|
|
|
2262
2286
|
)
|
|
2263
2287
|
if (typeof tree === 'string') write('tree.txt', tree)
|
|
2264
2288
|
|
|
2265
|
-
// 6. console buffer + failed requests
|
|
2266
|
-
|
|
2267
|
-
const consoleBuf = await safeEval<{
|
|
2268
|
-
errors: unknown[]
|
|
2269
|
-
warnings: unknown[]
|
|
2270
|
-
requests: unknown[]
|
|
2271
|
-
} | null>(`(() => {
|
|
2272
|
-
const c = window.__sootsimConsole
|
|
2273
|
-
return {
|
|
2274
|
-
errors: c?.getErrors?.() ?? [],
|
|
2275
|
-
warnings: c?.getWarnings?.() ?? [],
|
|
2276
|
-
requests: (window.__sootsimGetFailedRequests?.() ?? []),
|
|
2277
|
-
}
|
|
2278
|
-
})()`)
|
|
2289
|
+
// 6. console buffer + failed requests
|
|
2290
|
+
const consoleBuf = await this.readConsole().catch(() => null)
|
|
2279
2291
|
if (consoleBuf) {
|
|
2280
2292
|
write('console.json', JSON.stringify(consoleBuf, null, 2))
|
|
2281
2293
|
}
|
|
@@ -2418,20 +2430,21 @@ export class SootSimBridgeFlowRunner {
|
|
|
2418
2430
|
}
|
|
2419
2431
|
}
|
|
2420
2432
|
|
|
2433
|
+
// maestro's waitForAnimationToEnd compares screenshots until the screen holds
|
|
2434
|
+
// still, so motion the screen does not show (an endless loop under a
|
|
2435
|
+
// full-screen sheet) never holds it; visible shell motion changes the pixels.
|
|
2436
|
+
// like maestro, a screen that never holds still (a looping illustration)
|
|
2437
|
+
// ends the wait at the timeout and the flow continues
|
|
2421
2438
|
async waitForAnimationToEnd(timeoutMs: number = 2000) {
|
|
2422
2439
|
const maxMs = Math.max(0, Math.round(timeoutMs))
|
|
2423
2440
|
if (maxMs <= 0) return
|
|
2424
|
-
|
|
2441
|
+
await waitForSootsimIdle({
|
|
2425
2442
|
bridge: this.bridge,
|
|
2426
2443
|
simId: this.opts.simId,
|
|
2427
2444
|
maxMs,
|
|
2428
2445
|
pollMs: 32,
|
|
2429
|
-
|
|
2430
|
-
strict: true,
|
|
2446
|
+
paintStill: true,
|
|
2431
2447
|
})
|
|
2432
|
-
if (!result.settled) {
|
|
2433
|
-
throw new Error(`animation did not settle within ${maxMs}ms`)
|
|
2434
|
-
}
|
|
2435
2448
|
}
|
|
2436
2449
|
|
|
2437
2450
|
async back() {
|
|
@@ -3411,7 +3424,7 @@ export class SootSimBridgeFlowRunner {
|
|
|
3411
3424
|
this.lastFailedStep = null
|
|
3412
3425
|
}
|
|
3413
3426
|
|
|
3414
|
-
extractFlowBody(steps: MaestroStep[]) {
|
|
3427
|
+
extractFlowBody(steps: (MaestroStep | string)[]) {
|
|
3415
3428
|
// maestro `onFlowStart` / `onFlowComplete` lifecycle hooks: if any step
|
|
3416
3429
|
// in the array is a sole-key entry whose key is one of these names and
|
|
3417
3430
|
// whose value is a list of sub-steps, pull them out and run them as
|
|
@@ -3419,7 +3432,9 @@ export class SootSimBridgeFlowRunner {
|
|
|
3419
3432
|
const onStart: MaestroStep[] = []
|
|
3420
3433
|
const onComplete: MaestroStep[] = []
|
|
3421
3434
|
const body: MaestroStep[] = []
|
|
3422
|
-
for (const
|
|
3435
|
+
for (const rawStep of steps) {
|
|
3436
|
+
const step =
|
|
3437
|
+
typeof rawStep === 'string' ? normalizeBareStringStep(rawStep) : rawStep
|
|
3423
3438
|
const keys = Object.keys(step)
|
|
3424
3439
|
if (
|
|
3425
3440
|
keys.length === 1 &&
|
|
@@ -3442,15 +3457,11 @@ export class SootSimBridgeFlowRunner {
|
|
|
3442
3457
|
return { onStart, onComplete, body }
|
|
3443
3458
|
}
|
|
3444
3459
|
|
|
3445
|
-
async planFlow(steps: MaestroStep[], options: { dryRun?: boolean } = {}) {
|
|
3460
|
+
async planFlow(steps: (MaestroStep | string)[], options: { dryRun?: boolean } = {}) {
|
|
3446
3461
|
const { body } = this.extractFlowBody(steps)
|
|
3447
3462
|
if (this.liveStatus) {
|
|
3448
3463
|
await this.liveStatus.plan(
|
|
3449
|
-
body.map((
|
|
3450
|
-
// yaml parses bare-string steps (`- back`) as strings; mirror
|
|
3451
|
-
// runStep's normalization so the rail shows the verb, not chars.
|
|
3452
|
-
const step =
|
|
3453
|
-
typeof rawStep === 'string' ? normalizeBareStringStep(rawStep) : rawStep
|
|
3464
|
+
body.map((step, index) => {
|
|
3454
3465
|
const name = Object.keys(step).find((k) => k !== 'when') ?? Object.keys(step)[0]
|
|
3455
3466
|
return { index, name, target: this.flowStepTargetLabel(step, name) }
|
|
3456
3467
|
}),
|
|
@@ -3459,7 +3470,7 @@ export class SootSimBridgeFlowRunner {
|
|
|
3459
3470
|
}
|
|
3460
3471
|
}
|
|
3461
3472
|
|
|
3462
|
-
async runFlow(steps: MaestroStep[], options: { skipPlan?: boolean } = {}) {
|
|
3473
|
+
async runFlow(steps: (MaestroStep | string)[], options: { skipPlan?: boolean } = {}) {
|
|
3463
3474
|
const { onStart, onComplete, body } = this.extractFlowBody(steps)
|
|
3464
3475
|
this.lastInteractionAt = Date.now()
|
|
3465
3476
|
|
package/cli/cloud-client.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
readFileSync,
|
|
9
9
|
} from 'node:fs'
|
|
10
10
|
import { dirname, extname, join, resolve } from 'node:path'
|
|
11
|
+
import { Script } from 'node:vm'
|
|
11
12
|
import {
|
|
12
13
|
embedMetroAssetFiles,
|
|
13
14
|
extractAssetDescriptors,
|
|
@@ -256,10 +257,21 @@ async function loadBundleSource(bundleInput: string): Promise<string> {
|
|
|
256
257
|
if (bundleInput.startsWith('http://') && !isLoopbackBundleUrl(bundleInput)) {
|
|
257
258
|
throw new Error(`${REMOTE_COMMAND} bundle URL must use HTTPS`)
|
|
258
259
|
}
|
|
259
|
-
|
|
260
|
-
|
|
260
|
+
const source = isRemoteBundleInput(bundleInput)
|
|
261
|
+
? await fetchBundleSource(bundleInput)
|
|
262
|
+
: readImmutableBundleSource(bundleInput)
|
|
263
|
+
// a box evaluates the bundle as a script; refuse invalid syntax before
|
|
264
|
+
// allocating a billable host. compiling does not execute guest code.
|
|
265
|
+
try {
|
|
266
|
+
new Script(source, { filename: bundleInput })
|
|
267
|
+
} catch (error) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`${REMOTE_COMMAND} bundle contains invalid JavaScript: ${
|
|
270
|
+
error instanceof Error ? error.message : String(error)
|
|
271
|
+
}`,
|
|
272
|
+
)
|
|
261
273
|
}
|
|
262
|
-
return
|
|
274
|
+
return source
|
|
263
275
|
}
|
|
264
276
|
|
|
265
277
|
// `react-native bundle --assets-dest` dest paths are not always the JS
|
|
@@ -536,16 +548,19 @@ function unverifiedContentKey(
|
|
|
536
548
|
function searchMetroAssetDest(
|
|
537
549
|
bundlePath: string,
|
|
538
550
|
descriptors: readonly MetroAssetDescriptor[],
|
|
551
|
+
assetsDirectory?: string,
|
|
539
552
|
): MetroAssetSearch {
|
|
540
553
|
const bundleDirectory = dirname(resolve(bundlePath))
|
|
541
|
-
const candidates =
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
554
|
+
const candidates = assetsDirectory
|
|
555
|
+
? [resolve(assetsDirectory)]
|
|
556
|
+
: [
|
|
557
|
+
join(bundleDirectory, 'assets'),
|
|
558
|
+
bundleDirectory,
|
|
559
|
+
resolve(bundleDirectory, '..', 'assets'),
|
|
560
|
+
]
|
|
546
561
|
const cwd = process.cwd()
|
|
547
562
|
let projectDirectory: string | null = null
|
|
548
|
-
for (const start of [bundleDirectory, cwd]) {
|
|
563
|
+
for (const start of assetsDirectory ? [] : [bundleDirectory, cwd]) {
|
|
549
564
|
let directory = resolve(start)
|
|
550
565
|
while (true) {
|
|
551
566
|
if (detectProject(directory)?.hasMetroConfig) {
|
|
@@ -653,7 +668,7 @@ async function inferCloudModuleIdentity(
|
|
|
653
668
|
throw new Error(
|
|
654
669
|
`${REMOTE_COMMAND} could not load the Metro fingerprint registry needed to identify this bundle: ${
|
|
655
670
|
error instanceof Error ? error.message : String(error)
|
|
656
|
-
}. Build it through the published Metro plugin:
|
|
671
|
+
}. Build it through the published Metro plugin: withPeach(config).`,
|
|
657
672
|
)
|
|
658
673
|
}
|
|
659
674
|
try {
|
|
@@ -672,7 +687,7 @@ async function inferCloudModuleIdentity(
|
|
|
672
687
|
throw new Error(
|
|
673
688
|
`${REMOTE_COMMAND} could not infer module identity for this Metro bundle: ${
|
|
674
689
|
error instanceof Error ? error.message : String(error)
|
|
675
|
-
}. Build it through the published Metro plugin:
|
|
690
|
+
}. Build it through the published Metro plugin: withPeach(config).`,
|
|
676
691
|
)
|
|
677
692
|
}
|
|
678
693
|
}
|
|
@@ -682,7 +697,7 @@ async function inferCloudModuleIdentity(
|
|
|
682
697
|
* the runner executes: every asset scale is read from the asset graph and
|
|
683
698
|
* embedded, module identity is normalized, and the JavaScript is minified.
|
|
684
699
|
* an ordinary unannotated Metro production bundle is identified through the
|
|
685
|
-
* published fingerprint registry; a
|
|
700
|
+
* published fingerprint registry; a withPeach footer remains the fast path.
|
|
686
701
|
*/
|
|
687
702
|
export interface CloudArtifactProduction {
|
|
688
703
|
bytes: Buffer
|
|
@@ -701,8 +716,15 @@ export async function produceCloudArtifact(
|
|
|
701
716
|
* the default; a box that accepts a larger artifact passes its own.
|
|
702
717
|
*/
|
|
703
718
|
maxBytes: number = MAX_RNX_CLOUD_BUNDLE_BYTES,
|
|
719
|
+
assetsDirectory?: string,
|
|
704
720
|
): Promise<CloudArtifactProduction> {
|
|
705
|
-
return produceArtifact(
|
|
721
|
+
return produceArtifact(
|
|
722
|
+
bundlePath,
|
|
723
|
+
maxBytes,
|
|
724
|
+
REMOTE_COMMAND,
|
|
725
|
+
assetsDirectory ? 'require-assets' : 'allow-placeholders',
|
|
726
|
+
assetsDirectory,
|
|
727
|
+
)
|
|
706
728
|
}
|
|
707
729
|
|
|
708
730
|
/** prepares a standalone preview whose bytes must represent every Metro asset. */
|
|
@@ -726,11 +748,13 @@ export interface CloudBoxArtifactProduction {
|
|
|
726
748
|
/** prepares a prebuilt bundle for a Box by embedding any beside-the-bundle Metro assets. */
|
|
727
749
|
export async function produceCloudBoxArtifact(
|
|
728
750
|
bundlePath: string,
|
|
751
|
+
assetsDirectory?: string,
|
|
729
752
|
): Promise<CloudBoxArtifactProduction> {
|
|
730
753
|
const prepared = await embedBundleAssets(
|
|
731
754
|
bundlePath,
|
|
732
755
|
'peach box create',
|
|
733
|
-
'allow-placeholders',
|
|
756
|
+
assetsDirectory ? 'require-assets' : 'allow-placeholders',
|
|
757
|
+
assetsDirectory,
|
|
734
758
|
)
|
|
735
759
|
const warnings: string[] = []
|
|
736
760
|
if (prepared.search.unconfirmed && prepared.dest) {
|
|
@@ -754,6 +778,7 @@ async function embedBundleAssets(
|
|
|
754
778
|
bundlePath: string,
|
|
755
779
|
command: string,
|
|
756
780
|
assetPolicy: 'allow-placeholders' | 'require-assets',
|
|
781
|
+
assetsDirectory?: string,
|
|
757
782
|
): Promise<{
|
|
758
783
|
source: string
|
|
759
784
|
code: string
|
|
@@ -774,11 +799,11 @@ async function embedBundleAssets(
|
|
|
774
799
|
substituted: [],
|
|
775
800
|
}
|
|
776
801
|
}
|
|
777
|
-
// an https bundle
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
802
|
+
// an https bundle uses only an explicit local asset destination.
|
|
803
|
+
const search: MetroAssetSearch =
|
|
804
|
+
assetsDirectory || !isRemoteBundleInput(bundlePath)
|
|
805
|
+
? searchMetroAssetDest(bundlePath, descriptors, assetsDirectory)
|
|
806
|
+
: { dest: null, searched: [], truncated: false, unconfirmed: false }
|
|
782
807
|
const dest = search.dest
|
|
783
808
|
// `embedMetroAssetFiles` re-parses the bundle, so its descriptors are equal
|
|
784
809
|
// to the ones the search placed but not the same objects.
|
|
@@ -854,8 +879,14 @@ async function produceArtifact(
|
|
|
854
879
|
maxBytes: number,
|
|
855
880
|
command: string,
|
|
856
881
|
assetPolicy: 'allow-placeholders' | 'require-assets',
|
|
882
|
+
assetsDirectory?: string,
|
|
857
883
|
): Promise<CloudArtifactProduction> {
|
|
858
|
-
const prepared = await embedBundleAssets(
|
|
884
|
+
const prepared = await embedBundleAssets(
|
|
885
|
+
bundlePath,
|
|
886
|
+
command,
|
|
887
|
+
assetPolicy,
|
|
888
|
+
assetsDirectory,
|
|
889
|
+
)
|
|
859
890
|
const artifact = await buildRnxCloudArtifact(prepared.code, {
|
|
860
891
|
inferModuleIdentity: inferCloudModuleIdentity,
|
|
861
892
|
})
|
|
@@ -1040,6 +1071,7 @@ export async function createCloudBox(options: CreateCloudBoxOptions): Promise<{
|
|
|
1040
1071
|
|
|
1041
1072
|
export interface CreateRemoteSimOptions {
|
|
1042
1073
|
bundlePath: string
|
|
1074
|
+
assetsDirectory?: string
|
|
1043
1075
|
device: string
|
|
1044
1076
|
authorization: string
|
|
1045
1077
|
/** the account to bill when the authorization is a session token; an api key names its own */
|
|
@@ -1110,7 +1142,11 @@ export async function createRemoteSim(options: CreateRemoteSimOptions): Promise<
|
|
|
1110
1142
|
reused: boolean
|
|
1111
1143
|
}> {
|
|
1112
1144
|
const apiOrigin = resolveCloudOrigin(options.apiOrigin)
|
|
1113
|
-
const artifact = await produceCloudArtifact(
|
|
1145
|
+
const artifact = await produceCloudArtifact(
|
|
1146
|
+
options.bundlePath,
|
|
1147
|
+
undefined,
|
|
1148
|
+
options.assetsDirectory,
|
|
1149
|
+
)
|
|
1114
1150
|
const query = new URLSearchParams({
|
|
1115
1151
|
labels: `peach.remote=1,peach.platform=${options.platform},peach.bundle=${artifact.sha256.slice(0, 12)}`,
|
|
1116
1152
|
wait: 'true',
|
package/cli/cloud-test-tunnel.ts
CHANGED
|
@@ -110,7 +110,7 @@ export function startCloudTestTokenProxy(
|
|
|
110
110
|
upstreamPort: number,
|
|
111
111
|
sidecarPorts: readonly number[],
|
|
112
112
|
token: string,
|
|
113
|
-
): Promise<{ close: () => void } | null> {
|
|
113
|
+
): Promise<{ port: number; close: () => void } | null> {
|
|
114
114
|
const server = createServer((req, res) => {
|
|
115
115
|
const auth = req.headers.authorization || ''
|
|
116
116
|
if (auth !== `Bearer ${token}`) {
|
|
@@ -177,7 +177,11 @@ export function startCloudTestTokenProxy(
|
|
|
177
177
|
return new Promise((resolve) => {
|
|
178
178
|
server.on('error', () => resolve(null))
|
|
179
179
|
server.listen(listenPort, '127.0.0.1', () => {
|
|
180
|
-
|
|
180
|
+
const address = server.address()
|
|
181
|
+
if (!address || typeof address !== 'object') {
|
|
182
|
+
throw new Error('token proxy has no listening address')
|
|
183
|
+
}
|
|
184
|
+
resolve({ port: address.port, close: () => server.close() })
|
|
181
185
|
})
|
|
182
186
|
})
|
|
183
187
|
}
|
|
@@ -187,7 +191,7 @@ function cloudflaredAvailable(): boolean {
|
|
|
187
191
|
return probe.status === 0
|
|
188
192
|
}
|
|
189
193
|
|
|
190
|
-
async function ensureCloudflared(): Promise<boolean> {
|
|
194
|
+
export async function ensureCloudflared(): Promise<boolean> {
|
|
191
195
|
if (cloudflaredAvailable()) return true
|
|
192
196
|
const archMap: Record<string, string> = { x64: 'amd64', arm64: 'arm64' }
|
|
193
197
|
const arch = archMap[process.arch]
|
|
@@ -232,7 +236,7 @@ function childAlive(child: ChildProcess): boolean {
|
|
|
232
236
|
return child.exitCode === null && child.signalCode === null
|
|
233
237
|
}
|
|
234
238
|
|
|
235
|
-
async function launchQuickTunnel(
|
|
239
|
+
export async function launchQuickTunnel(
|
|
236
240
|
originUrl: string,
|
|
237
241
|
hostHeader: string,
|
|
238
242
|
logFile: string,
|
|
@@ -297,23 +301,13 @@ export async function publishLiveDevStack(args: {
|
|
|
297
301
|
}): Promise<PublishedLiveDevStack> {
|
|
298
302
|
const { devPort, sidecarPorts } = args
|
|
299
303
|
const jobToken = randomBytes(16).toString('hex')
|
|
300
|
-
//
|
|
304
|
+
// let the token proxy own its port; the advertised authority stays
|
|
301
305
|
// the dev server's own port so Metro URLs resolve on the recorder.
|
|
302
|
-
const
|
|
303
|
-
const proxyPort = await new Promise<number>((resolve, reject) => {
|
|
304
|
-
const probe = new Server()
|
|
305
|
-
probe.once('error', reject)
|
|
306
|
-
probe.listen(0, '127.0.0.1', () => {
|
|
307
|
-
const address = probe.address()
|
|
308
|
-
const port = address && typeof address === 'object' ? address.port : 0
|
|
309
|
-
probe.close(() => resolve(port))
|
|
310
|
-
})
|
|
311
|
-
})
|
|
312
|
-
if (!proxyPort) throw new Error('could not allocate a tunnel proxy port')
|
|
313
|
-
const proxy = await startCloudTestTokenProxy(proxyPort, devPort, sidecarPorts, jobToken)
|
|
306
|
+
const proxy = await startCloudTestTokenProxy(0, devPort, sidecarPorts, jobToken)
|
|
314
307
|
if (!proxy) {
|
|
315
|
-
throw new Error(
|
|
308
|
+
throw new Error('could not start the tunnel token proxy on 127.0.0.1')
|
|
316
309
|
}
|
|
310
|
+
const proxyPort = proxy.port
|
|
317
311
|
const closeProxy = proxy.close
|
|
318
312
|
const hostHeader = `127.0.0.1:${devPort}`
|
|
319
313
|
if (!(await ensureCloudflared())) {
|
package/cli/commands/agent.ts
CHANGED
|
@@ -37,7 +37,7 @@ import type { AgentEvent } from '../../src/agent-events'
|
|
|
37
37
|
import type { AgentInspectMessage } from '../../src/agent-prompt'
|
|
38
38
|
|
|
39
39
|
// the bridge this invocation talks to: `-p`, else the same dev-bridge or
|
|
40
|
-
// daemon pick every other command makes (PORT_OFFSET and
|
|
40
|
+
// daemon pick every other command makes (PORT_OFFSET and PEACH_BRIDGE_PORT
|
|
41
41
|
// included).
|
|
42
42
|
let daemonPort = 0
|
|
43
43
|
|