peachsim 0.1.940 → 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.
Files changed (396) hide show
  1. package/README.md +28 -22
  2. package/cli/app-catalog.ts +29 -14
  3. package/cli/app-fonts.ts +5 -3
  4. package/cli/bridge-flow-runner.ts +40 -29
  5. package/cli/cloud-client.ts +57 -21
  6. package/cli/cloud-test-tunnel.ts +12 -18
  7. package/cli/commands/agent.ts +1 -1
  8. package/cli/commands/app-fonts.ts +3 -3
  9. package/cli/commands/assert.ts +8 -8
  10. package/cli/commands/auth.ts +8 -3
  11. package/cli/commands/box/bundle-plane.ts +13 -1
  12. package/cli/commands/box.ts +16 -5
  13. package/cli/commands/compat.ts +1 -1
  14. package/cli/commands/config.ts +2 -2
  15. package/cli/commands/control.ts +125 -71
  16. package/cli/commands/cpu-profile.ts +45 -1
  17. package/cli/commands/daemon.ts +131 -33
  18. package/cli/commands/debug.ts +24 -17
  19. package/cli/commands/desktop.ts +7 -1
  20. package/cli/commands/detect.ts +101 -0
  21. package/cli/commands/detox.ts +6 -6
  22. package/cli/commands/device.ts +12 -12
  23. package/cli/commands/electron.ts +32 -14
  24. package/cli/commands/flow.ts +49 -41
  25. package/cli/commands/guest-host.ts +39 -1
  26. package/cli/commands/inspect/core.ts +14 -8
  27. package/cli/commands/inspect/list.ts +1 -1
  28. package/cli/commands/inspect/settle.ts +5 -3
  29. package/cli/commands/inspect/settling.ts +229 -44
  30. package/cli/commands/inspect/wait-idle.ts +3 -2
  31. package/cli/commands/inspect.ts +105 -93
  32. package/cli/commands/install-cli.ts +36 -15
  33. package/cli/commands/install-desktop.ts +68 -195
  34. package/cli/commands/kotlin.ts +6 -5
  35. package/cli/commands/login.ts +5 -5
  36. package/cli/commands/maestro-compare.ts +554 -0
  37. package/cli/commands/maestro-generate.ts +6 -3
  38. package/cli/commands/maestro.ts +22 -0
  39. package/cli/commands/mode.ts +2 -2
  40. package/cli/commands/no-bridge-hint.ts +6 -2
  41. package/cli/commands/platform.ts +35 -6
  42. package/cli/commands/preview.ts +6 -0
  43. package/cli/commands/project-report.ts +10 -12
  44. package/cli/commands/record.ts +37 -37
  45. package/cli/commands/runtime.ts +9 -9
  46. package/cli/commands/screenshot-layers.ts +4 -4
  47. package/cli/commands/screenshot-mode.ts +2 -2
  48. package/cli/commands/screenshot.ts +8 -8
  49. package/cli/commands/screenshots-capture.ts +2 -2
  50. package/cli/commands/serve.ts +9 -12
  51. package/cli/commands/setup.ts +29 -14
  52. package/cli/commands/shell-boolean-mode.ts +2 -2
  53. package/cli/commands/show-touches-mode.ts +2 -2
  54. package/cli/commands/skills.ts +11 -11
  55. package/cli/commands/swift.ts +29 -11
  56. package/cli/commands/telemetry.ts +2 -2
  57. package/cli/commands/test.ts +5 -0
  58. package/cli/commands/three-mode.ts +3 -3
  59. package/cli/commands/timeline.ts +4 -4
  60. package/cli/commands/upgrade.ts +2 -2
  61. package/cli/commands/upload.ts +15 -15
  62. package/cli/commands/what-happened.ts +2 -2
  63. package/cli/current-sim.ts +6 -3
  64. package/cli/desktop-companion.ts +55 -149
  65. package/cli/dev-build-freshness.ts +2 -2
  66. package/cli/drivers/desktop.ts +17 -9
  67. package/cli/drivers/electron.ts +1 -2
  68. package/cli/drivers/playwright-sim-host.ts +11 -2
  69. package/cli/drivers/playwright.ts +35 -2
  70. package/cli/drivers/types.ts +8 -0
  71. package/cli/maestro-compare.ts +670 -0
  72. package/cli/outbound-endpoints.ts +46 -8
  73. package/cli/recording-access.ts +6 -6
  74. package/cli/run-registry.ts +1 -1
  75. package/cli/runtime-compat.ts +5 -1
  76. package/cli/send-to-box.ts +3 -3
  77. package/cli/shell-init.ts +2 -1
  78. package/cli/ws-bridge.ts +9 -5
  79. package/detox/element-types.ts +1 -0
  80. package/detox/gestures.ts +3 -3
  81. package/detox/index.ts +67 -38
  82. package/detox/navigation-lifecycle.ts +9 -6
  83. package/detox/proof-frame.cjs +6 -4
  84. package/dist-lib/agent-daemon-client.cjs +1 -1
  85. package/dist-lib/agent-events.cjs +1 -1
  86. package/dist-lib/attached-projects.cjs +1 -1
  87. package/dist-lib/auth/shared-session.cjs +1 -1
  88. package/dist-lib/backend-origin.cjs +1 -1
  89. package/dist-lib/beta.cjs +1 -1
  90. package/dist-lib/beta.mjs +1 -1
  91. package/dist-lib/bridge-constants.cjs +1 -1
  92. package/dist-lib/bridge-contract-input.cjs +42 -17
  93. package/dist-lib/bridge-contract-input.mjs +41 -16
  94. package/dist-lib/bridge-contract.cjs +1 -4
  95. package/dist-lib/bridge-contract.mjs +1 -3
  96. package/dist-lib/bundle-channel.cjs +2918 -550
  97. package/dist-lib/capture-contract.cjs +65 -24
  98. package/dist-lib/capture-contract.mjs +63 -24
  99. package/dist-lib/cloud-contract.cjs +2 -2
  100. package/dist-lib/cloud-contract.mjs +2 -2
  101. package/dist-lib/cloud-test-args.cjs +1 -1
  102. package/dist-lib/cloud-test-args.mjs +1 -1
  103. package/dist-lib/cloud.cjs +3 -3
  104. package/dist-lib/cloud.mjs +3 -3
  105. package/dist-lib/config.cjs +15 -5
  106. package/dist-lib/config.mjs +154 -0
  107. package/dist-lib/detox/index.cjs +495 -139
  108. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  109. package/dist-lib/expo-swift-bundle.cjs +1168 -238
  110. package/dist-lib/expo-swift-metadata.cjs +34 -23
  111. package/dist-lib/expo-swift-metadata.mjs +32 -23
  112. package/dist-lib/home-paths.cjs +2 -2
  113. package/dist-lib/host/bridge-host.cjs +2938 -654
  114. package/dist-lib/host/fetch-proxy-handler.cjs +14 -1
  115. package/dist-lib/host/fetch-proxy-overrides.cjs +7 -1
  116. package/dist-lib/host/fetch-proxy-overrides.mjs +5 -1
  117. package/dist-lib/host/replacement-module-handler.cjs +2 -2
  118. package/dist-lib/host/websocket-proxy.cjs +1 -1
  119. package/dist-lib/index.cjs +981 -353
  120. package/dist-lib/jump-to-source-babel.cjs +1 -1
  121. package/dist-lib/jump-to-source-native.cjs +1 -1
  122. package/dist-lib/kotlin-host.cjs +332 -19
  123. package/dist-lib/menu.cjs +1 -1
  124. package/dist-lib/menu.mjs +1 -1
  125. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  126. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  127. package/dist-lib/metro-production-bundle.cjs +7 -7
  128. package/dist-lib/metro-production-bundle.mjs +5 -5
  129. package/dist-lib/metro.cjs +670 -250
  130. package/dist-lib/native-app-identity-project.cjs +2647 -0
  131. package/dist-lib/native-app-identity-project.mjs +1257 -0
  132. package/dist-lib/native-app-identity.cjs +304 -25
  133. package/dist-lib/native-app-identity.mjs +302 -25
  134. package/dist-lib/native-firebase-metadata.cjs +1 -1
  135. package/dist-lib/native-firebase-metadata.mjs +1 -1
  136. package/dist-lib/plist.cjs +1 -1
  137. package/dist-lib/plist.mjs +1 -1
  138. package/dist-lib/profiles.cjs +1 -1
  139. package/dist-lib/public-brand.cjs +1 -1
  140. package/dist-lib/react-native-host-modules.cjs +5 -1
  141. package/dist-lib/react-native-host-modules.mjs +5 -1
  142. package/dist-lib/scripts/dev-server-scanner.cjs +57 -32
  143. package/dist-lib/sdk.cjs +231 -51
  144. package/dist-lib/sdk.mjs +231 -51
  145. package/dist-lib/skills.cjs +4107 -1374
  146. package/dist-lib/swift-compile.cjs +290 -30
  147. package/dist-lib/swift-compile.mjs +290 -30
  148. package/dist-lib/swift-package.cjs +143 -12
  149. package/dist-lib/swift-package.mjs +137 -11
  150. package/dist-lib/swift-submit.cjs +47 -5
  151. package/dist-lib/swift-submit.mjs +42 -5
  152. package/dist-lib/swift-toolchain.cjs +7 -2
  153. package/dist-lib/swift.cjs +1 -1
  154. package/dist-lib/vite-swift.cjs +1392 -458
  155. package/dist-lib/vite.cjs +13 -17
  156. package/dist-lib/worklet-executable.cjs +248 -33
  157. package/dist-lib/worklet-executable.mjs +248 -33
  158. package/install.sh +10 -1
  159. package/kotlin/{AppSources → AndroidViewSources}/android/content/res/Resources.kt +9 -9
  160. package/kotlin/AndroidViewSources/android/graphics/Canvas.kt +20 -0
  161. package/kotlin/AndroidViewSources/android/view/View.kt +211 -0
  162. package/kotlin/AndroidViewSources/android/widget/FrameLayout.kt +61 -0
  163. package/kotlin/AndroidViewSources/android/widget/LinearLayout.kt +49 -0
  164. package/kotlin/AppSources/android/view/Window.kt +36 -0
  165. package/kotlin/AppSources/android/view/WindowInsets.kt +9 -0
  166. package/kotlin/AppSources/android/view/WindowInsetsController.kt +5 -0
  167. package/kotlin/AppSources/android/window/OnBackInvokedDispatcher.kt +48 -0
  168. package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcher.kt +20 -5
  169. package/kotlin/AppSources/androidx/compose/foundation/layout/WindowInsets.kt +15 -0
  170. package/kotlin/AppSources/androidx/compose/ui/platform/ComposeView.kt +2 -1
  171. package/kotlin/AppSources/dev/peach/app/PeachApp.kt +42 -50
  172. package/kotlin/ComposeSources/PeachComposeHost.kt +200 -84
  173. package/kotlin/ExpoComposeSources/PeachAndroidView.kt +87 -0
  174. package/kotlin/ExpoComposeSources/PeachExpoViewMount.kt +24 -0
  175. package/kotlin/ExpoComposeSources/dev/peach/expo/PeachExpoCompose.kt +28 -0
  176. package/kotlin/ExpoComposeSources/expo/modules/kotlin/views/ExpoComposeView.kt +70 -0
  177. package/kotlin/ExpoSources/dev/peach/expo/PeachExpoRuntime.kt +513 -0
  178. package/kotlin/ExpoSources/dev/peach/expo/PeachExpoTypes.kt +173 -0
  179. package/kotlin/ExpoSources/expo/modules/kotlin/AppContext.kt +20 -0
  180. package/kotlin/ExpoSources/expo/modules/kotlin/Promise.kt +19 -0
  181. package/kotlin/ExpoSources/expo/modules/kotlin/classcomponent/ClassComponentBuilder.kt +198 -0
  182. package/kotlin/ExpoSources/expo/modules/kotlin/exception/CodedException.kt +126 -0
  183. package/kotlin/ExpoSources/expo/modules/kotlin/functions/Functions.kt +132 -0
  184. package/kotlin/ExpoSources/expo/modules/kotlin/jni/JavaScriptTypedArray.kt +81 -0
  185. package/kotlin/ExpoSources/expo/modules/kotlin/modules/Module.kt +98 -0
  186. package/kotlin/ExpoSources/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +236 -0
  187. package/kotlin/ExpoSources/expo/modules/kotlin/records/Record.kt +12 -0
  188. package/kotlin/ExpoSources/expo/modules/kotlin/runtime/Runtime.kt +9 -0
  189. package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedObject.kt +50 -0
  190. package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedObjectRegistry.kt +57 -0
  191. package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedRef.kt +19 -0
  192. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/ConcreteTypedArrays.kt +158 -0
  193. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/GenericTypedArray.kt +8 -0
  194. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/TypedArray.kt +29 -0
  195. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/TypedArrayIterator.kt +9 -0
  196. package/kotlin/ExpoSources/expo/modules/kotlin/types/AnyType.kt +155 -0
  197. package/kotlin/ExpoSources/expo/modules/kotlin/types/Either.kt +91 -0
  198. package/kotlin/ExpoSources/expo/modules/kotlin/types/Enumerable.kt +6 -0
  199. package/kotlin/ExpoSources/expo/modules/kotlin/viewevent/ViewEvent.kt +54 -0
  200. package/kotlin/ExpoSources/expo/modules/kotlin/views/ExpoView.kt +23 -0
  201. package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +71 -0
  202. package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewProps.kt +35 -0
  203. package/kotlin/README.md +13 -3
  204. package/kotlin/Sources/android/os/Build.kt +17 -0
  205. package/kotlin/Sources/android/os/Handler.kt +35 -0
  206. package/kotlin/Sources/android/os/Looper.kt +17 -0
  207. package/kotlin/Sources/android/os/MessageQueue.kt +56 -0
  208. package/kotlin/Sources/android/os/SystemClock.kt +14 -0
  209. package/kotlin/Sources/android/util/Log.kt +64 -0
  210. package/kotlin/Sources/java/security/MessageDigest.kt +114 -0
  211. package/kotlin/Sources/kotlin/text/StringFormat.kt +9 -0
  212. package/kotlin/Sources/org/json/JSON.kt +44 -0
  213. package/kotlin/fixtures/counter/Counter.d.kt.ts +1 -0
  214. package/kotlin/fixtures/counter/Counter.kt +10 -3
  215. package/kotlin/fixtures/counter/CounterApp.native.tsx +1 -1
  216. package/kotlin/fixtures/counter/vite.config.ts +2 -2
  217. package/kotlin/fixtures/headless-counter/Headless.kt +7 -6
  218. package/kotlin/fixtures/import-animation/Animation.d.kt.ts +7 -0
  219. package/kotlin/fixtures/import-animation/Animation.kt +80 -0
  220. package/kotlin/fixtures/import-animation/ImportAnimation.native.tsx +13 -0
  221. package/kotlin/fixtures/import-animation/README.md +24 -0
  222. package/kotlin/fixtures/import-animation/app/_layout.tsx +4 -0
  223. package/kotlin/fixtures/import-animation/app/index+spa.tsx +1 -0
  224. package/kotlin/fixtures/import-animation/app/routes.d.ts +18 -0
  225. package/kotlin/fixtures/import-animation/package.json +23 -0
  226. package/kotlin/fixtures/import-animation/tsconfig.json +20 -0
  227. package/kotlin/fixtures/import-animation/vite.config.ts +20 -0
  228. package/kotlin/fixtures/import-editor/Editor.d.kt.ts +7 -0
  229. package/kotlin/fixtures/import-editor/Editor.kt +59 -0
  230. package/kotlin/fixtures/import-editor/ImportEditor.native.tsx +23 -0
  231. package/kotlin/fixtures/import-editor/README.md +27 -0
  232. package/kotlin/fixtures/import-editor/app/_layout.tsx +4 -0
  233. package/kotlin/fixtures/import-editor/app/index+spa.tsx +1 -0
  234. package/kotlin/fixtures/import-editor/package.json +23 -0
  235. package/kotlin/fixtures/import-editor/tsconfig.json +20 -0
  236. package/kotlin/fixtures/import-editor/vite.config.ts +20 -0
  237. package/kotlin/fixtures/import-effects/Effects.d.kt.ts +7 -0
  238. package/kotlin/fixtures/import-effects/Effects.kt +87 -0
  239. package/kotlin/fixtures/import-effects/ImportEffects.native.tsx +13 -0
  240. package/kotlin/fixtures/import-effects/README.md +19 -0
  241. package/kotlin/fixtures/import-effects/app/_layout.tsx +4 -0
  242. package/kotlin/fixtures/import-effects/app/index+spa.tsx +1 -0
  243. package/kotlin/fixtures/import-effects/package.json +23 -0
  244. package/kotlin/fixtures/import-effects/tsconfig.json +20 -0
  245. package/kotlin/fixtures/import-effects/vite.config.ts +20 -0
  246. package/kotlin/fixtures/import-layout/ImportLayout.native.tsx +13 -0
  247. package/kotlin/fixtures/import-layout/Layout.d.kt.ts +7 -0
  248. package/kotlin/fixtures/import-layout/Layout.kt +72 -0
  249. package/kotlin/fixtures/import-layout/README.md +24 -0
  250. package/kotlin/fixtures/import-layout/app/_layout.tsx +4 -0
  251. package/kotlin/fixtures/import-layout/app/index+spa.tsx +1 -0
  252. package/kotlin/fixtures/import-layout/package.json +23 -0
  253. package/kotlin/fixtures/import-layout/tsconfig.json +20 -0
  254. package/kotlin/fixtures/import-layout/vite.config.ts +20 -0
  255. package/kotlin/fixtures/import-navigation/ImportNavigation.native.tsx +18 -0
  256. package/kotlin/fixtures/import-navigation/Navigation.d.kt.ts +7 -0
  257. package/kotlin/fixtures/import-navigation/Navigation.kt +104 -0
  258. package/kotlin/fixtures/import-navigation/README.md +31 -0
  259. package/kotlin/fixtures/import-navigation/app/_layout.tsx +4 -0
  260. package/kotlin/fixtures/import-navigation/app/index+spa.tsx +1 -0
  261. package/kotlin/fixtures/import-navigation/package.json +23 -0
  262. package/kotlin/fixtures/import-navigation/tsconfig.json +20 -0
  263. package/kotlin/fixtures/import-navigation/vite.config.ts +20 -0
  264. package/kotlin/fixtures/import-overlays/ImportOverlays.native.tsx +29 -0
  265. package/kotlin/fixtures/import-overlays/Overlays.d.kt.ts +7 -0
  266. package/kotlin/fixtures/import-overlays/Overlays.kt +93 -0
  267. package/kotlin/fixtures/import-overlays/README.md +34 -0
  268. package/kotlin/fixtures/import-overlays/app/_layout.tsx +4 -0
  269. package/kotlin/fixtures/import-overlays/app/index+spa.tsx +1 -0
  270. package/kotlin/fixtures/import-overlays/package.json +23 -0
  271. package/kotlin/fixtures/import-overlays/tsconfig.json +20 -0
  272. package/kotlin/fixtures/import-overlays/vite.config.ts +20 -0
  273. package/kotlin/fixtures/import-scroll/ImportScroll.native.tsx +13 -0
  274. package/kotlin/fixtures/import-scroll/README.md +23 -0
  275. package/kotlin/fixtures/import-scroll/Scroll.d.kt.ts +7 -0
  276. package/kotlin/fixtures/import-scroll/Scroll.kt +91 -0
  277. package/kotlin/fixtures/import-scroll/app/_layout.tsx +4 -0
  278. package/kotlin/fixtures/import-scroll/app/index+spa.tsx +1 -0
  279. package/kotlin/fixtures/import-scroll/package.json +23 -0
  280. package/kotlin/fixtures/import-scroll/tsconfig.json +20 -0
  281. package/kotlin/fixtures/import-scroll/vite.config.ts +20 -0
  282. package/kotlin/fixtures/import-selection/ImportSelection.native.tsx +44 -0
  283. package/kotlin/fixtures/import-selection/README.md +32 -0
  284. package/kotlin/fixtures/import-selection/Selection.d.kt.ts +8 -0
  285. package/kotlin/fixtures/import-selection/Selection.kt +78 -0
  286. package/kotlin/fixtures/import-selection/app/_layout.tsx +4 -0
  287. package/kotlin/fixtures/import-selection/app/index+spa.tsx +1 -0
  288. package/kotlin/fixtures/import-selection/package.json +23 -0
  289. package/kotlin/fixtures/import-selection/tsconfig.json +20 -0
  290. package/kotlin/fixtures/import-selection/vite.config.ts +20 -0
  291. package/kotlin/fixtures/import-surfaces/ImportSurfaces.native.tsx +13 -0
  292. package/kotlin/fixtures/import-surfaces/README.md +22 -0
  293. package/kotlin/fixtures/import-surfaces/Surfaces.d.kt.ts +7 -0
  294. package/kotlin/fixtures/import-surfaces/Surfaces.kt +64 -0
  295. package/kotlin/fixtures/import-surfaces/app/_layout.tsx +4 -0
  296. package/kotlin/fixtures/import-surfaces/app/index+spa.tsx +1 -0
  297. package/kotlin/fixtures/import-surfaces/package.json +23 -0
  298. package/kotlin/fixtures/import-surfaces/tsconfig.json +20 -0
  299. package/kotlin/fixtures/import-surfaces/vite.config.ts +20 -0
  300. package/kotlin/fixtures/import-values/ImportValues.native.tsx +35 -0
  301. package/kotlin/fixtures/import-values/README.md +23 -0
  302. package/kotlin/fixtures/import-values/Values.d.kt.ts +11 -0
  303. package/kotlin/fixtures/import-values/Values.kt +66 -0
  304. package/kotlin/fixtures/import-values/app/_layout.tsx +4 -0
  305. package/kotlin/fixtures/import-values/app/index+spa.tsx +1 -0
  306. package/kotlin/fixtures/import-values/package.json +23 -0
  307. package/kotlin/fixtures/import-values/tsconfig.json +20 -0
  308. package/kotlin/fixtures/import-values/vite.config.ts +20 -0
  309. package/kotlin/fixtures/kotlin-app-variants/BarBehavior.kt +38 -0
  310. package/kotlin/fixtures/kotlin-app-variants/BarVisibility.kt +36 -0
  311. package/kotlin/fixtures/kotlin-app-variants/Ime.kt +3 -1
  312. package/kotlin/fixtures/kotlin-app-variants/TransientInsets.kt +50 -0
  313. package/kotlin/fork/NOTICE +6 -1
  314. package/kotlin/fork/README.md +15 -7
  315. package/kotlin/fork/build.sh +2 -0
  316. package/kotlin/fork/compose-worker-globals.patch +186 -4
  317. package/kotlin/fork/fork.json +2 -2
  318. package/kotlin/fork/skiko-worker-clock.patch +12 -0
  319. package/kotlin/toolchain.json +12 -12
  320. package/kotlin/tools/expo-types.main.kts +115 -0
  321. package/package.json +17 -23
  322. package/scripts/dev-server-scanner.ts +92 -66
  323. package/skills/contrast/SKILL.md +2 -2
  324. package/skills/peach-debug/SKILL.md +1 -1
  325. package/skills/peach-perf/SKILL.md +1 -1
  326. package/skills/peach-setup/SKILL.md +2 -1
  327. package/skills/peach-test/SKILL.md +8 -0
  328. package/src/assetCatalogContract.ts +43 -1
  329. package/src/bridge-contract-input.ts +46 -19
  330. package/src/bridge-contract.ts +0 -1
  331. package/src/bundle-channel.mjs +197 -84
  332. package/src/bundleGlobalFooter.ts +38 -0
  333. package/src/capture-contract.ts +107 -33
  334. package/src/cloud-contract.ts +5 -1
  335. package/src/cloud.ts +5 -2
  336. package/src/config.ts +22 -3
  337. package/src/configured-bundle-producer.ts +1 -1
  338. package/src/desktop-host-delivery.ts +95 -1
  339. package/src/expo-kotlin-bundle.ts +29 -0
  340. package/src/expo-kotlin-host.ts +201 -0
  341. package/src/expo-swift-host.ts +96 -76
  342. package/src/expoKotlinContract.ts +70 -0
  343. package/src/expoSwiftContract.ts +4 -25
  344. package/src/home-paths.ts +7 -4
  345. package/src/host/bridge-host.ts +23 -3
  346. package/src/host/fetch-proxy-handler.ts +13 -0
  347. package/src/host/fetch-proxy-overrides.ts +11 -0
  348. package/src/host/replacement-module-handler.ts +5 -2
  349. package/src/index.ts +3 -1
  350. package/src/kotlin-host.ts +463 -19
  351. package/src/metro-plugin.ts +56 -28
  352. package/src/metro-production-bundle.ts +2 -2
  353. package/src/native-app-identity-project.ts +190 -0
  354. package/src/native-app-identity.ts +367 -34
  355. package/src/native-firebase-config.ts +69 -15
  356. package/src/native-seam-manifest.ts +6 -0
  357. package/src/one-widget-discovery.ts +2 -1
  358. package/src/openstep-plist.ts +107 -0
  359. package/src/react-native-host-modules.ts +4 -0
  360. package/src/runtime-assets.ts +2 -2
  361. package/src/sim-client.ts +2 -2
  362. package/src/swift-binary-modules.ts +3 -1
  363. package/src/swift-compile.ts +286 -38
  364. package/src/swift-distribution-contract.ts +4 -0
  365. package/src/swift-distribution.lock.json +123 -12
  366. package/src/swift-distribution.ts +7 -3
  367. package/src/swift-host.ts +298 -83
  368. package/src/swift-package-manifest.ts +29 -2
  369. package/src/swift-package.ts +180 -10
  370. package/src/swift-packages.ts +4 -0
  371. package/src/swift-standin-emit.ts +4 -1
  372. package/src/swift-standins.ts +274 -15
  373. package/src/swift-submit.ts +62 -7
  374. package/src/swift-toolchain.ts +6 -1
  375. package/src/swiftContract.ts +126 -3
  376. package/src/vite-plugin-one.ts +13 -16
  377. package/src/vite-plugin-swift.ts +4 -5
  378. package/src/vite-plugin.ts +360 -144
  379. package/src/vite.d.ts +1 -1
  380. package/src/worker-system-chunks.ts +221 -0
  381. package/src/worklet-executable.ts +348 -38
  382. package/src/worklets-babel.ts +1 -1
  383. package/src/xcode-project.ts +243 -127
  384. package/dist-lib/agent-identity.cjs +0 -39
  385. package/dist-lib/agent-sessions.cjs +0 -760
  386. package/dist-lib/cli-constants.cjs +0 -32
  387. package/dist-lib/render-mode.cjs +0 -44
  388. package/kotlin/AppSources/android/view/View.kt +0 -74
  389. package/kotlin/AppSources/android/widget/FrameLayout.kt +0 -6
  390. package/src/render-mode.ts +0 -15
  391. /package/kotlin/{AppSources → AndroidViewSources}/android/content/Context.kt +0 -0
  392. /package/kotlin/{AppSources → AndroidViewSources}/android/content/res/Configuration.kt +0 -0
  393. /package/kotlin/{AppSources → AndroidViewSources}/android/graphics/Color.kt +0 -0
  394. /package/kotlin/{AppSources → AndroidViewSources}/android/util/DisplayMetrics.kt +0 -0
  395. /package/kotlin/{AppSources → AndroidViewSources}/dev/peach/app/JavaFormat.kt +0 -0
  396. /package/kotlin/{AppSources → Sources}/java/lang/Runnable.kt +0 -0
@@ -6,6 +6,8 @@ import path from 'node:path'
6
6
  import { encodeAppCatalogWire } from 'peach-engine/engine/app-catalog-wire'
7
7
  import {
8
8
  devices,
9
+ getRuntimePlatform,
10
+ isDeviceModel,
9
11
  listSelectableDeviceModels,
10
12
  resolveDeviceFitBox,
11
13
  type DeviceModel,
@@ -13,12 +15,13 @@ import {
13
15
  import { WebSocket } from 'ws'
14
16
  import { probePort } from '../../scripts/dev-server-scanner'
15
17
  import { isAppDeepLinkTarget, openAppUrl } from '../../src/app-url'
16
- import { isLoopbackHost } from '../../src/backend-origin'
18
+ import { isLoopbackHost, loopbackOriginOf } from '../../src/backend-origin'
17
19
  import { resolveSootsimShellBaseUrlForBridgePort } from '../../src/bridge-constants'
18
20
  import {
19
21
  applyPeachConfigToUrl,
20
22
  mergePeachConfig,
21
23
  PEACH_CONFIG_QUERY_PARAM,
24
+ readPeachConfigFromSearchParams,
22
25
  type PeachConfig,
23
26
  } from '../../src/config'
24
27
  import {
@@ -80,7 +83,7 @@ import {
80
83
  import { getDriver } from '../drivers/registry'
81
84
  import { isTeamMachineSession, openUrl as openUrlInBrowser } from '../open-url'
82
85
  import { diagnosticsAllowedForCurrentProcess } from '../privacy'
83
- import { rethrowIfExit, rnxExit } from '../run-rnx'
86
+ import { rethrowIfExit, peachExit } from '../run-rnx'
84
87
  import { collectRuntimeDiagnostics } from '../runtime-diagnostics'
85
88
  import { rnxSelfInvocation } from '../self-invocation'
86
89
  import {
@@ -95,6 +98,7 @@ import {
95
98
  worldForBridgePort,
96
99
  type BridgeSimInfo,
97
100
  } from '../ws-bridge'
101
+ import { readNativeProjectRenderer } from './detect'
98
102
  import { inspectDescribe, inspectReadySnapshot, waitReadyReason } from './inspect/core'
99
103
  import { isAgentEnv } from './inspect/env'
100
104
  import { hasInstalledPeachSkills } from './skills'
@@ -425,14 +429,14 @@ function looksLikeSootsimUrl(target: string): boolean {
425
429
  url.pathname.includes(PEACH_SIMULATOR_SHELL_FILE) ||
426
430
  isPeachSimulatorDocumentPath(pathname) ||
427
431
  pathname === '/sootsim' ||
428
- url.pathname === '/__soot' ||
429
- url.pathname === '/__soot/' ||
432
+ url.pathname === '/__peachsim' ||
433
+ url.pathname === '/__peachsim/' ||
430
434
  pathname === '/rn' ||
431
435
  /^\/rn\/[^/]+$/i.test(pathname) ||
432
436
  /^\/app\/[^/]+$/i.test(pathname) ||
433
- pathname === '/__soot/rn' ||
434
- /^\/__soot\/rn\/[^/]+$/i.test(pathname) ||
435
- /^\/__soot\/app\/[^/]+$/i.test(pathname)
437
+ pathname === '/__peachsim/rn' ||
438
+ /^\/__peachsim\/rn\/[^/]+$/i.test(pathname) ||
439
+ /^\/__peachsim\/app\/[^/]+$/i.test(pathname)
436
440
  )
437
441
  } catch {
438
442
  return false
@@ -448,9 +452,9 @@ function looksLikeShellPathTarget(target: string): boolean {
448
452
  pathname === '/rn' ||
449
453
  /^\/rn\/[^/]+$/i.test(pathname) ||
450
454
  /^\/app\/[^/]+$/i.test(pathname) ||
451
- pathname === '/__soot/rn' ||
452
- /^\/__soot\/rn\/[^/]+$/i.test(pathname) ||
453
- /^\/__soot\/app\/[^/]+$/i.test(pathname)
455
+ pathname === '/__peachsim/rn' ||
456
+ /^\/__peachsim\/rn\/[^/]+$/i.test(pathname) ||
457
+ /^\/__peachsim\/app\/[^/]+$/i.test(pathname)
454
458
  )
455
459
  } catch {
456
460
  return false
@@ -461,8 +465,8 @@ function buildShellPathOpenUrl(target: string, baseUrl: string): string {
461
465
  const targetUrl = new URL(target, 'http://sootsim.local')
462
466
  const url = new URL(baseUrl)
463
467
  const prefix = getShellRoutePrefix(url.pathname)
464
- const routePath = targetUrl.pathname.startsWith('/__soot/')
465
- ? targetUrl.pathname.slice('/__soot'.length)
468
+ const routePath = targetUrl.pathname.startsWith('/__peachsim/')
469
+ ? targetUrl.pathname.slice('/__peachsim'.length)
466
470
  : targetUrl.pathname
467
471
 
468
472
  url.pathname = `${prefix}${routePath}`
@@ -558,8 +562,8 @@ async function resolveConnectionInputForCli(target: string): Promise<ResolvedDev
558
562
 
559
563
  function getShellRoutePrefix(pathname: string): string {
560
564
  const normalized = pathname.replace(/\/+$/, '') || '/'
561
- if (normalized === '/__soot' || normalized.startsWith('/__soot/')) {
562
- return '/__soot'
565
+ if (normalized === '/__peachsim' || normalized.startsWith('/__peachsim/')) {
566
+ return '/__peachsim'
563
567
  }
564
568
  return ''
565
569
  }
@@ -636,18 +640,18 @@ function parseOpenRuntimeConfig(args: string[]): PeachConfig | undefined {
636
640
  const raw = args[i + 1]
637
641
  if (!raw) {
638
642
  console.error(' peach open: --replace expects <module>=<file>')
639
- rnxExit(1)
643
+ peachExit(1)
640
644
  }
641
645
  const eqIndex = raw.indexOf('=')
642
646
  if (eqIndex <= 0 || eqIndex === raw.length - 1) {
643
647
  console.error(' peach open: --replace expects <module>=<file>')
644
- rnxExit(1)
648
+ peachExit(1)
645
649
  }
646
650
  const moduleName = raw.slice(0, eqIndex).trim()
647
651
  const filePath = normalizeReplacementPath(raw.slice(eqIndex + 1).trim())
648
652
  if (!fs.existsSync(filePath)) {
649
653
  console.error(` peach open: replacement file not found: ${filePath}`)
650
- rnxExit(1)
654
+ peachExit(1)
651
655
  }
652
656
  modules[moduleName] = { file: filePath }
653
657
  i++
@@ -661,7 +665,7 @@ function parseOpenRuntimeConfig(args: string[]): PeachConfig | undefined {
661
665
  const eqIndex = raw ? raw.indexOf('=') : -1
662
666
  if (!raw || eqIndex <= 0 || eqIndex === raw.length - 1) {
663
667
  console.error(' peach open: --remap expects <host:port>=<host:port>')
664
- rnxExit(1)
668
+ peachExit(1)
665
669
  }
666
670
  remap[raw.slice(0, eqIndex).trim()] = raw.slice(eqIndex + 1).trim()
667
671
  i++
@@ -876,14 +880,15 @@ async function stampAppSplash(
876
880
  }
877
881
 
878
882
  // stamp the engine page url with `?appCatalog=` so an app's asset-catalog
879
- // colors and its declared accent reach the tenant, which applies them to the
880
- // bundle this url opens. the catalog lives in the native binary, never in
881
- // the js bundle, so only the app's ios project on disk has it.
883
+ // colors and images and its declared accent reach the tenant, which applies
884
+ // them to the bundle this url opens. the catalog lives in the native binary,
885
+ // never in the js bundle, so only the app's ios project on disk has it.
882
886
  function stampAppCatalog(urlStr: string, project?: ResolvedAppProject | null): string {
883
887
  try {
884
888
  const url = new URL(urlStr)
885
- if (!url.searchParams.get('bundle') || !project) return urlStr
886
- const catalog = discoverAppCatalog(project.appDir)
889
+ const metroOrigin = loopbackOriginOf(url.searchParams.get('bundle') ?? '')
890
+ if (!metroOrigin || !project) return urlStr
891
+ const catalog = discoverAppCatalog(project.appDir, metroOrigin)
887
892
  if (!catalog) return urlStr
888
893
  url.searchParams.set('appCatalog', encodeAppCatalogWire(catalog))
889
894
  return url.toString()
@@ -953,12 +958,20 @@ export interface BuildShellUrlOptions {
953
958
  // screen holding a focused text field never reaches three identical idle
954
959
  // frames, so the screenshot guard burns all 12 attempts and then fails.
955
960
  proof?: boolean
961
+ // a temporary storage profile, stamped as ?ephemeral=1. the shell then
962
+ // neither reads nor writes the machine-wide settings in
963
+ // ~/.peach/config.json, so a preference another sim saved (bold text,
964
+ // reduce motion, appearance) cannot steer this boot.
965
+ ephemeral?: boolean
956
966
  // launch-time environment variables, stamped as repeated ?env=KEY%3DVALUE.
957
967
  // see the --env flag above.
958
968
  env?: string[]
959
969
  // the project's env file as react-native-config's iOS build compiles it,
960
970
  // stamped as ?dotEnv=<json>. set by `peach open --load-env` only.
961
971
  dotEnv?: Record<string, string>
972
+ // the bridge an explicit --port names; the expo swift compile endpoint
973
+ // stamped onto the URL uses it instead of the discovered world's bridge.
974
+ bridgePort?: number
962
975
  }
963
976
 
964
977
  function readProjectNativeFirebaseConfig(
@@ -1005,6 +1018,29 @@ export async function buildShellUrl(
1005
1018
  project,
1006
1019
  )
1007
1020
  const expoStamped = new URL(stamped)
1021
+ if (project) {
1022
+ const device = opts.device ?? expoStamped.searchParams.get('device')
1023
+ const bundle = bundleUrlOf(resolved)
1024
+ const platform =
1025
+ device && isDeviceModel(device)
1026
+ ? getRuntimePlatform(device)
1027
+ : bundle && new URL(bundle).searchParams.get('platform') === 'android'
1028
+ ? 'android'
1029
+ : 'ios'
1030
+ const nativeRenderer = readNativeProjectRenderer(project.appDir, platform)
1031
+ if (nativeRenderer !== undefined) {
1032
+ const config = readPeachConfigFromSearchParams(expoStamped.searchParams)
1033
+ if (
1034
+ config?.nativeRenderer !== undefined &&
1035
+ config.nativeRenderer !== nativeRenderer
1036
+ )
1037
+ throw new Error('peach config contradicts native project renderer')
1038
+ expoStamped.searchParams.set(
1039
+ PEACH_CONFIG_QUERY_PARAM,
1040
+ JSON.stringify(mergePeachConfig(config, { nativeRenderer })),
1041
+ )
1042
+ }
1043
+ }
1008
1044
  // a plain Metro has no native Firebase config endpoint, so the CLI reads it
1009
1045
  // from the project whose Expo config it can evaluate. a project without its
1010
1046
  // own `@expo/config` (a dependency-free recorder) leaves it to the Peach
@@ -1020,18 +1056,24 @@ export async function buildShellUrl(
1020
1056
  const world = resolveBridgeWorld()
1021
1057
  const port = resolveBridgePortForPin({
1022
1058
  baseUrl,
1023
- wsPort: world.bridgePort,
1024
- explicitPort: false,
1059
+ wsPort: opts.bridgePort ?? world.bridgePort,
1060
+ explicitPort: opts.bridgePort !== undefined,
1025
1061
  })
1026
1062
  if (port.error) throw new Error(port.error)
1027
1063
  expoStamped.searchParams.set(
1028
1064
  'expoSwift',
1029
1065
  `http://localhost:${port.port}/__peach/expo-swift?root=${encodeURIComponent(expoRoot)}`,
1030
1066
  )
1067
+ // the engine fetches only its device platform's build
1068
+ expoStamped.searchParams.set(
1069
+ 'expoKotlin',
1070
+ `http://localhost:${port.port}/__peach/expo-kotlin?root=${encodeURIComponent(expoRoot)}`,
1071
+ )
1031
1072
  }
1032
1073
  if (
1033
1074
  !opts.device &&
1034
1075
  !opts.proof &&
1076
+ !opts.ephemeral &&
1035
1077
  !opts.posture &&
1036
1078
  !opts.orientation &&
1037
1079
  !opts.osVersion &&
@@ -1048,6 +1090,7 @@ export async function buildShellUrl(
1048
1090
  if (opts.osVersion) url.searchParams.set('osVersion', opts.osVersion)
1049
1091
  if (opts.theme) url.searchParams.set('theme', opts.theme)
1050
1092
  if (opts.proof) url.searchParams.set('proof', '1')
1093
+ if (opts.ephemeral) url.searchParams.set('ephemeral', '1')
1051
1094
  for (const entry of opts.env ?? []) url.searchParams.append('env', entry)
1052
1095
  return url.toString()
1053
1096
  }
@@ -1079,6 +1122,7 @@ function deriveCurrentSimBaseUrl(
1079
1122
  url.searchParams.delete('appCatalog')
1080
1123
  url.searchParams.delete('nativeFirebase')
1081
1124
  url.searchParams.delete('expoSwift')
1125
+ url.searchParams.delete('expoKotlin')
1082
1126
  return url.toString()
1083
1127
  } catch {
1084
1128
  return fallback
@@ -1289,7 +1333,7 @@ function readUnservedUrl(result: DriverLaunchResult): string | null {
1289
1333
  }
1290
1334
  }
1291
1335
 
1292
- function printDriverDiagnosticLogTail(result: DriverLaunchResult) {
1336
+ export function printDriverDiagnosticLogTail(result: DriverLaunchResult) {
1293
1337
  if (!result.diagnosticLogPath) return
1294
1338
  try {
1295
1339
  const detail = fs.readFileSync(result.diagnosticLogPath, 'utf8').trim()
@@ -1563,7 +1607,7 @@ export async function runLinkCommand(args: string[], opts: ControlCommandOptions
1563
1607
  if (parsed.positional.length !== 1) {
1564
1608
  console.error(' peach open: expected exactly one <url-or-route>')
1565
1609
  console.error(' examples: peach open /settings | peach open myapp://settings')
1566
- rnxExit(1)
1610
+ peachExit(1)
1567
1611
  }
1568
1612
 
1569
1613
  const bridge = createBridgeFromParsed(parsed)
@@ -1578,7 +1622,7 @@ export async function runLinkCommand(args: string[], opts: ControlCommandOptions
1578
1622
  warningsCommand: `peach get warnings 5${simHint}`,
1579
1623
  requestsCommand: `peach get requests 5${simHint}`,
1580
1624
  })
1581
- rnxExit(1)
1625
+ peachExit(1)
1582
1626
  } finally {
1583
1627
  bridge.close()
1584
1628
  }
@@ -1636,11 +1680,11 @@ export async function runOpenCommand(
1636
1680
  console.error(
1637
1681
  ` peach open: --cdp-port must be a positive port number, got "${cdpPortArg}"`,
1638
1682
  )
1639
- rnxExit(1)
1683
+ peachExit(1)
1640
1684
  }
1641
1685
  if (requestedProfileId && ephemeralProfile) {
1642
1686
  console.error(' peach open: --profile cannot be combined with --ephemeral')
1643
- rnxExit(1)
1687
+ peachExit(1)
1644
1688
  }
1645
1689
  const profileId = requestedProfileId ? ensureProfile(requestedProfileId).id : undefined
1646
1690
  const openInNewSim = args.includes('--new') || !!profileId || ephemeralProfile
@@ -1649,7 +1693,7 @@ export async function runOpenCommand(
1649
1693
  console.error(
1650
1694
  ' peach open: --new, --profile, and --ephemeral cannot be combined with --sim',
1651
1695
  )
1652
- rnxExit(1)
1696
+ peachExit(1)
1653
1697
  }
1654
1698
  const target = parsed.positional[0] || ''
1655
1699
 
@@ -1663,7 +1707,7 @@ export async function runOpenCommand(
1663
1707
  console.error(
1664
1708
  ` peach open: --port is the WS bridge port, not the app port. to open the app on ${portValue}, pass it positionally: \`peach open ${portValue}\``,
1665
1709
  )
1666
- rnxExit(1)
1710
+ peachExit(1)
1667
1711
  }
1668
1712
 
1669
1713
  // -t / --theme <light|dark|auto> seeds the appearance for the opened sim,
@@ -1676,7 +1720,7 @@ export async function runOpenCommand(
1676
1720
  const themeArg = themeFlagIndex === -1 ? undefined : args[themeFlagIndex + 1]
1677
1721
  if (themeFlagIndex !== -1 && themeArg === undefined) {
1678
1722
  console.error(' peach open: --theme expects light, dark, or auto')
1679
- rnxExit(1)
1723
+ peachExit(1)
1680
1724
  }
1681
1725
  if (
1682
1726
  themeArg !== undefined &&
@@ -1685,7 +1729,7 @@ export async function runOpenCommand(
1685
1729
  themeArg !== 'auto'
1686
1730
  ) {
1687
1731
  console.error(` peach open: --theme must be light, dark, or auto, got "${themeArg}"`)
1688
- rnxExit(1)
1732
+ peachExit(1)
1689
1733
  }
1690
1734
 
1691
1735
  if (isAppDeepLinkTarget(target)) {
@@ -1693,7 +1737,7 @@ export async function runOpenCommand(
1693
1737
  console.error(
1694
1738
  ' peach open: deep links target the current app; remove --new, --profile, or --ephemeral',
1695
1739
  )
1696
- rnxExit(1)
1740
+ peachExit(1)
1697
1741
  }
1698
1742
  await runLinkCommand(args, opts)
1699
1743
  return
@@ -1721,7 +1765,7 @@ export async function runOpenCommand(
1721
1765
  ` could not launch a browser: ${err instanceof Error ? err.message : String(err)}`,
1722
1766
  )
1723
1767
  console.error(` open it yourself: ${target}`)
1724
- rnxExit(1)
1768
+ peachExit(1)
1725
1769
  }
1726
1770
  console.log(
1727
1771
  isLocal
@@ -1738,7 +1782,7 @@ export async function runOpenCommand(
1738
1782
  const projectRoot = projectRootIndex < 0 ? undefined : args[projectRootIndex + 1]
1739
1783
  if (projectRootIndex >= 0 && (!projectRoot || projectRoot.startsWith('--'))) {
1740
1784
  console.error(' peach open: --project-root expects a local app directory')
1741
- rnxExit(1)
1785
+ peachExit(1)
1742
1786
  }
1743
1787
  const project = await resolveAppProjectForBundleUrl(
1744
1788
  resolvedBundle?.bundleUrl,
@@ -1772,7 +1816,7 @@ export async function runOpenCommand(
1772
1816
  })
1773
1817
  if (pinned.error !== undefined) {
1774
1818
  console.error(` error: ${pinned.error}`)
1775
- rnxExit(1)
1819
+ peachExit(1)
1776
1820
  } else {
1777
1821
  parsed.wsPort = pinned.port
1778
1822
  }
@@ -1796,10 +1840,12 @@ export async function runOpenCommand(
1796
1840
  console.error(
1797
1841
  ` unknown driver "${requestedDriverId}" — run \`peach list --drivers\``,
1798
1842
  )
1799
- rnxExit(1)
1843
+ peachExit(1)
1800
1844
  }
1801
1845
  const driverId: DriverId = isDriverId(requestedDriverId)
1802
- ? requestedDriverId
1846
+ ? requestedDriverId === 'electron' && process.platform !== 'win32'
1847
+ ? 'desktop'
1848
+ : requestedDriverId
1803
1849
  : process.env.CI === '1' ||
1804
1850
  process.env.CI === 'true' ||
1805
1851
  process.env.GITHUB_ACTIONS === 'true' ||
@@ -1807,11 +1853,17 @@ export async function runOpenCommand(
1807
1853
  isAgentEnv() ||
1808
1854
  !process.stdin.isTTY
1809
1855
  ? 'playwright'
1810
- : readSimulatorDriverPreference() || 'playwright'
1856
+ : readSimulatorDriverPreference() === 'electron' && process.platform !== 'win32'
1857
+ ? 'desktop'
1858
+ : readSimulatorDriverPreference() || 'playwright'
1859
+ if (requestedDriverId === 'electron' && driverId === 'desktop')
1860
+ console.error(
1861
+ ' Electron is Windows-only; using the native desktop app (peach install-desktop).',
1862
+ )
1811
1863
  const driverHeadless = resolveDriverHeadless(args, driverId)
1812
1864
  if (cdpPort && driverId !== 'playwright') {
1813
1865
  console.error(' peach open: --cdp-port is only honored by the playwright driver')
1814
- rnxExit(1)
1866
+ peachExit(1)
1815
1867
  }
1816
1868
 
1817
1869
  // metro HMR mode. interactive opens default to hot=true so edits hot-apply
@@ -1825,7 +1877,7 @@ export async function runOpenCommand(
1825
1877
  const noHmrFlag = args.includes('--no-hmr')
1826
1878
  if (hotFlag && noHmrFlag) {
1827
1879
  console.error(' peach open: --hot and --no-hmr cannot be combined')
1828
- rnxExit(1)
1880
+ peachExit(1)
1829
1881
  }
1830
1882
  const hotMode = hotFlag ? true : noHmrFlag ? false : driverId !== 'playwright'
1831
1883
 
@@ -1837,7 +1889,7 @@ export async function runOpenCommand(
1837
1889
  if (deviceArg && !(deviceArg in devices)) {
1838
1890
  console.error(` peach open: unknown device "${deviceArg}"`)
1839
1891
  console.error(` known devices: ${listSelectableDeviceModels().join(', ')}`)
1840
- rnxExit(1)
1892
+ peachExit(1)
1841
1893
  }
1842
1894
 
1843
1895
  // --posture <open|closed> seeds the fold posture for the opened sim,
@@ -1847,7 +1899,7 @@ export async function runOpenCommand(
1847
1899
  const postureArg = args.find((_, i) => args[i - 1] === '--posture')
1848
1900
  if (postureArg && postureArg !== 'open' && postureArg !== 'closed') {
1849
1901
  console.error(` peach open: --posture must be open or closed, got "${postureArg}"`)
1850
- rnxExit(1)
1902
+ peachExit(1)
1851
1903
  }
1852
1904
  const orientationFlag = args.find((_, i) => args[i - 1] === '--orientation')
1853
1905
  const orientationArg =
@@ -1858,7 +1910,7 @@ export async function runOpenCommand(
1858
1910
  console.error(
1859
1911
  ` peach open: --orientation must be portrait or landscape, got "${orientationFlag}"`,
1860
1912
  )
1861
- rnxExit(1)
1913
+ peachExit(1)
1862
1914
  }
1863
1915
  // the window fits the box the browser stage fits the device into, which for
1864
1916
  // a foldable spans both postures, so the device draws at its natural size in
@@ -1878,7 +1930,7 @@ export async function runOpenCommand(
1878
1930
  console.error(
1879
1931
  ` peach open: --os-version must be <major>.<minor>, got "${osVersionArg}"`,
1880
1932
  )
1881
- rnxExit(1)
1933
+ peachExit(1)
1882
1934
  }
1883
1935
 
1884
1936
  // --env <KEY=VALUE> (repeatable) sets a launch-time environment variable,
@@ -1894,7 +1946,7 @@ export async function runOpenCommand(
1894
1946
  const eqIndex = raw ? raw.indexOf('=') : -1
1895
1947
  if (!raw || eqIndex <= 0) {
1896
1948
  console.error(` peach open: --env expects KEY=VALUE, got "${raw ?? ''}"`)
1897
- rnxExit(1)
1949
+ peachExit(1)
1898
1950
  }
1899
1951
  envArgs.push(raw)
1900
1952
  }
@@ -1909,7 +1961,7 @@ export async function runOpenCommand(
1909
1961
  console.error(
1910
1962
  ` peach open: --viewport must be <width>x<height>, got "${viewportArg}"`,
1911
1963
  )
1912
- rnxExit(1)
1964
+ peachExit(1)
1913
1965
  }
1914
1966
  const explicitViewport = viewportMatch
1915
1967
  ? { width: Number(viewportMatch[1]), height: Number(viewportMatch[2]) }
@@ -1925,7 +1977,7 @@ export async function runOpenCommand(
1925
1977
  console.error(
1926
1978
  ' peach open: --load-env needs a local app project to read the env file from',
1927
1979
  )
1928
- rnxExit(1)
1980
+ peachExit(1)
1929
1981
  }
1930
1982
  try {
1931
1983
  const result = readProjectDotEnv(project.appDir)
@@ -1935,7 +1987,7 @@ export async function runOpenCommand(
1935
1987
  console.error(
1936
1988
  ` peach open: ${error instanceof Error ? error.message : String(error)}`,
1937
1989
  )
1938
- rnxExit(1)
1990
+ peachExit(1)
1939
1991
  }
1940
1992
  }
1941
1993
 
@@ -1951,8 +2003,10 @@ export async function runOpenCommand(
1951
2003
  resolvedBundle,
1952
2004
  project,
1953
2005
  proof: args.includes('--proof'),
2006
+ ephemeral: ephemeralProfile,
1954
2007
  env: envArgs.length ? envArgs : undefined,
1955
2008
  dotEnv,
2009
+ bridgePort: parsed.explicitPort ? parsed.wsPort : undefined,
1956
2010
  }
1957
2011
 
1958
2012
  // resolve the shell base URL from the bridge port we settled on, so the page
@@ -2069,7 +2123,7 @@ export async function runOpenCommand(
2069
2123
  warningsCommand: `peach get warnings 5${simHint}`,
2070
2124
  requestsCommand: `peach get requests 5${simHint}`,
2071
2125
  })
2072
- rnxExit(1)
2126
+ peachExit(1)
2073
2127
  }
2074
2128
 
2075
2129
  if (!fallBackToNewSim && targetSim && navigatedShellUrl) {
@@ -2093,7 +2147,7 @@ export async function runOpenCommand(
2093
2147
  warningsCommand: `peach get warnings 5${simHint}`,
2094
2148
  requestsCommand: `peach get requests 5${simHint}`,
2095
2149
  })
2096
- rnxExit(1)
2150
+ peachExit(1)
2097
2151
  }
2098
2152
  targetSim = navigatedSim
2099
2153
  const ready = await waitForSimReady(
@@ -2110,7 +2164,7 @@ export async function runOpenCommand(
2110
2164
  warningsCommand: `peach get warnings 5${simHint}`,
2111
2165
  requestsCommand: `peach get requests 5${simHint}`,
2112
2166
  })
2113
- rnxExit(1)
2167
+ peachExit(1)
2114
2168
  }
2115
2169
  ready.bridge.close()
2116
2170
  saveCurrentSimId(targetSim.id)
@@ -2136,12 +2190,12 @@ export async function runOpenCommand(
2136
2190
  console.error(
2137
2191
  ' could not resolve a stable owning process; refusing unsupervised sim host',
2138
2192
  )
2139
- rnxExit(1)
2193
+ peachExit(1)
2140
2194
  }
2141
2195
  const driver = getDriver(driverId)
2142
2196
  if (!driver) {
2143
2197
  console.error(` unknown driver "${driverId}" — run \`peach list --drivers\``)
2144
- rnxExit(1)
2198
+ peachExit(1)
2145
2199
  }
2146
2200
  const connectWait = resolveDriverConnectWaitOptions()
2147
2201
  opts.signal?.throwIfAborted()
@@ -2171,7 +2225,7 @@ export async function runOpenCommand(
2171
2225
  opts.signal?.throwIfAborted()
2172
2226
  if (!result.launched) {
2173
2227
  console.error(` ${driver.name} driver: ${result.message}`)
2174
- rnxExit(1)
2228
+ peachExit(1)
2175
2229
  }
2176
2230
  const outcome = await waitForSimMatchOrHostExit(
2177
2231
  parsed.wsPort,
@@ -2194,7 +2248,7 @@ export async function runOpenCommand(
2194
2248
  } else {
2195
2249
  console.error(` ${driver.name} host crashed — sim is no longer attached`)
2196
2250
  }
2197
- rnxExit(1)
2251
+ peachExit(1)
2198
2252
  }
2199
2253
  if (outcome.kind === 'timeout') {
2200
2254
  if (result.pid) {
@@ -2207,7 +2261,7 @@ export async function runOpenCommand(
2207
2261
  console.error(
2208
2262
  ` timed out after ${connectWait.timeoutMs}ms waiting for opened sim to connect`,
2209
2263
  )
2210
- rnxExit(1)
2264
+ peachExit(1)
2211
2265
  }
2212
2266
  const match = outcome.sim
2213
2267
  if (opts.resources) opts.resources.simId = match.id
@@ -2237,7 +2291,7 @@ export async function runOpenCommand(
2237
2291
  requestsCommand: `peach get requests 5${hint}`,
2238
2292
  })
2239
2293
  bridge.close()
2240
- rnxExit(1)
2294
+ peachExit(1)
2241
2295
  }
2242
2296
  ready.bridge.close()
2243
2297
  printOpenedSim(openUrl, match, `${driver.name} driver`, quiet)
@@ -2280,7 +2334,7 @@ async function runUseLikeCommand(args: string[], opts: ControlCommandOptions = {
2280
2334
  ` refused: ${target.id} is locked by the active user (${secs}s) — that's a live human tab`,
2281
2335
  )
2282
2336
  console.error(` run \`peach open --new\` for a fresh investigation sim`)
2283
- rnxExit(1)
2337
+ peachExit(1)
2284
2338
  }
2285
2339
  if (!force) {
2286
2340
  // another CLI agent leases it. don't hijack its target silently;
@@ -2292,7 +2346,7 @@ async function runUseLikeCommand(args: string[], opts: ControlCommandOptions = {
2292
2346
  console.error(
2293
2347
  ` \`peach use ${target.id} --force\` to take it, or \`peach open --new\` for a fresh sim`,
2294
2348
  )
2295
- rnxExit(1)
2349
+ peachExit(1)
2296
2350
  }
2297
2351
  }
2298
2352
  await bridge.focusSim(target.id)
@@ -2306,7 +2360,7 @@ async function runUseLikeCommand(args: string[], opts: ControlCommandOptions = {
2306
2360
  warningsCommand: `peach get warnings 5${simHint}`,
2307
2361
  requestsCommand: `peach get requests 5${simHint}`,
2308
2362
  })
2309
- rnxExit(1)
2363
+ peachExit(1)
2310
2364
  }
2311
2365
  } finally {
2312
2366
  bridge.close()
@@ -2350,10 +2404,10 @@ export async function runClaimCommand(args: string[], opts: ControlCommandOption
2350
2404
  const secs = Math.max(0, Math.round(err.lock.expiresInMs / 1000))
2351
2405
  console.error(` claim failed: locked by ${err.lock.by} for ${secs}s more`)
2352
2406
  console.error(` use --force to take it, or \`peach open --new\` for a fresh sim`)
2353
- rnxExit(1)
2407
+ peachExit(1)
2354
2408
  }
2355
2409
  console.error(` claim failed: ${err instanceof Error ? err.message : String(err)}`)
2356
- rnxExit(1)
2410
+ peachExit(1)
2357
2411
  }
2358
2412
  } finally {
2359
2413
  bridge.close()
@@ -2545,7 +2599,7 @@ export async function runCloseCommand(args: string[], opts: ControlCommandOption
2545
2599
  console.error(
2546
2600
  ` close failed: keep sim ${plan.missingExplicitKeepId} is not connected; not closing other sims`,
2547
2601
  )
2548
- rnxExit(1)
2602
+ peachExit(1)
2549
2603
  }
2550
2604
  if (plan.targets.length === 0) {
2551
2605
  console.log(
@@ -2578,12 +2632,12 @@ export async function runCloseCommand(args: string[], opts: ControlCommandOption
2578
2632
  console.log(summary)
2579
2633
  if (result.remaining.length > 0) {
2580
2634
  console.error(` close failed: still connected: ${result.remaining.join(', ')}`)
2581
- rnxExit(1)
2635
+ peachExit(1)
2582
2636
  }
2583
2637
  } catch (err) {
2584
2638
  rethrowIfExit(err)
2585
2639
  console.error(` close failed: ${err instanceof Error ? err.message : String(err)}`)
2586
- rnxExit(1)
2640
+ peachExit(1)
2587
2641
  } finally {
2588
2642
  bridge.close()
2589
2643
  }
@@ -2606,7 +2660,7 @@ export async function runCloseCommand(args: string[], opts: ControlCommandOption
2606
2660
  if (!closed) {
2607
2661
  await terminatePlaywrightHostsForSims(sims, [target.id])
2608
2662
  console.error(` close failed: ${target.id} is still connected`)
2609
- rnxExit(1)
2663
+ peachExit(1)
2610
2664
  }
2611
2665
  await terminatePlaywrightHostsForSims(sims, [target.id])
2612
2666
  if (readCurrentSimId() === target.id) {
@@ -2623,14 +2677,14 @@ export async function runCloseCommand(args: string[], opts: ControlCommandOption
2623
2677
  console.error(
2624
2678
  ` to close ${word === 'all' ? 'every' : 'every other'} sim: peach close --${word}`,
2625
2679
  )
2626
- rnxExit(1)
2680
+ peachExit(1)
2627
2681
  }
2628
2682
  await printBridgeFailureDiagnostics(bridge, {
2629
2683
  errorsCommand: `peach get errors 5${simHint}`,
2630
2684
  warningsCommand: `peach get warnings 5${simHint}`,
2631
2685
  requestsCommand: `peach get requests 5${simHint}`,
2632
2686
  })
2633
- rnxExit(1)
2687
+ peachExit(1)
2634
2688
  }
2635
2689
  } finally {
2636
2690
  bridge.close()