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.
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 +999 -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 +688 -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 +325 -25
  133. package/dist-lib/native-app-identity.mjs +323 -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 +398 -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
@@ -30,7 +30,7 @@ import { authHeaderValue, githubUploadIdentity, resolveCliAuth } from '../auth'
30
30
  import { openUrl } from '../open-url'
31
31
  import { extractStringFlag } from '../parse-args'
32
32
  import { resolveRunProvenance } from '../run-registry'
33
- import { rnxExit } from '../run-rnx'
33
+ import { peachExit } from '../run-rnx'
34
34
  import {
35
35
  callInBridge,
36
36
  createBridgeFromParsed,
@@ -227,7 +227,7 @@ function parsePositiveMs(raw: string | undefined, name: string): number | undefi
227
227
  const value = Number(raw)
228
228
  if (!Number.isFinite(value) || value <= 0) {
229
229
  console.error(` invalid ${name}: ${raw}`)
230
- rnxExit(1)
230
+ peachExit(1)
231
231
  }
232
232
  return Math.round(value)
233
233
  }
@@ -433,7 +433,7 @@ export async function runUpload(
433
433
  ): Promise<UploadResult> {
434
434
  if (args.includes('--help') || args.includes('-h')) {
435
435
  printHelp()
436
- rnxExit(0)
436
+ peachExit(0)
437
437
  }
438
438
 
439
439
  const mutableArgs = [...args]
@@ -472,17 +472,17 @@ export async function runUpload(
472
472
  const openResult = extractBoolFlag(mutableArgs, '--open')
473
473
  if ((owner && !repo) || (!owner && repo)) {
474
474
  console.error(' --owner and --repo must be provided together')
475
- rnxExit(1)
475
+ peachExit(1)
476
476
  }
477
477
  if (billingKind && billingKind !== 'test_run') {
478
478
  console.error(` invalid --billing-kind: ${billingKind}`)
479
- rnxExit(1)
479
+ peachExit(1)
480
480
  }
481
481
  // the hosted recorder names the job whose minutes cover this recording
482
482
  const runnerJobId = process.env.CONTRAST_RUNNER_JOB_ID
483
483
  if (billingKind === 'test_run' && !runnerJobId) {
484
484
  console.error(' --billing-kind test_run is only for hosted test jobs')
485
- rnxExit(1)
485
+ peachExit(1)
486
486
  }
487
487
  if (!eventsPath && !videoPath) {
488
488
  console.error(
@@ -490,7 +490,7 @@ export async function runUpload(
490
490
  ' for a normal local preview, use `peach record --mode combined --open`\n' +
491
491
  ' or `peach maestro test --preview` instead of uploading a bundle-only snapshot.',
492
492
  )
493
- rnxExit(1)
493
+ peachExit(1)
494
494
  }
495
495
  // assets-only filters captured cross-origin responses to just image/font/
496
496
  // css/js/binary records before upload — API responses (JSON, HTML) hit the
@@ -715,14 +715,14 @@ export async function runUpload(
715
715
  console.error(
716
716
  ' could not read bundle snapshot — is peach running and is the bundle loaded?',
717
717
  )
718
- rnxExit(2)
718
+ peachExit(2)
719
719
  }
720
720
  if (!snapshot.bundleUrl) {
721
721
  console.error(
722
722
  ' no ?bundle= URL on the current peach tab.\n' +
723
723
  ' open the app you want to share first (e.g. peach open 8082), then run upload.',
724
724
  )
725
- rnxExit(2)
725
+ peachExit(2)
726
726
  }
727
727
 
728
728
  // prefer the post-transform bundle captured from the running tab. this
@@ -867,13 +867,13 @@ export async function runUpload(
867
867
  : undefined
868
868
  if (!videoContentType) {
869
869
  console.error(' preview flow videos must be mp4 or gif')
870
- rnxExit(1)
870
+ peachExit(1)
871
871
  }
872
872
  if (videoDurationMsRaw) {
873
873
  const parsed = Number(videoDurationMsRaw)
874
874
  if (!Number.isFinite(parsed) || parsed <= 0) {
875
875
  console.error(` invalid --video-duration-ms: ${videoDurationMsRaw}`)
876
- rnxExit(1)
876
+ peachExit(1)
877
877
  }
878
878
  videoDurationMs = Math.round(parsed)
879
879
  }
@@ -893,7 +893,7 @@ export async function runUpload(
893
893
  console.error(
894
894
  ' set PEACH_API_KEY=sk_rnx_..., run `peach login`, or use the Contrast github runner.',
895
895
  )
896
- rnxExit(1)
896
+ peachExit(1)
897
897
  }
898
898
  const resolvedAuthHeader = authHeaderValue(cliAuth)
899
899
 
@@ -1193,7 +1193,7 @@ export async function runUpload(
1193
1193
  if (e.phase === 'init' && e.status === 401) {
1194
1194
  console.error(' preview upload requires a valid login.')
1195
1195
  console.error(' run `peach login` and retry.')
1196
- rnxExit(1, 'preview upload requires a valid login')
1196
+ peachExit(1, 'preview upload requires a valid login')
1197
1197
  }
1198
1198
  if (e.status === 402) {
1199
1199
  // billing gate — surface the server's own `message`, not raw JSON.
@@ -1201,13 +1201,13 @@ export async function runUpload(
1201
1201
  parseServerMessage(e.responseText) ??
1202
1202
  'preview uploads require Personal, Team, or an active trial — upgrade from the billing dialog.'
1203
1203
  console.error(` ${message}`)
1204
- rnxExit(1, message)
1204
+ peachExit(1, message)
1205
1205
  }
1206
1206
  // PresignedUploadError.message already carries the "<phase> failed:"
1207
1207
  // prefix — don't re-prefix it (that produced "init failed: init failed:").
1208
1208
  const message = e.message ?? `${e.phase ?? 'upload'} failed: ${String(err)}`
1209
1209
  console.error(` ${message}`)
1210
- rnxExit(1, message)
1210
+ peachExit(1, message)
1211
1211
  }
1212
1212
 
1213
1213
  const previewUrl = `${canonicalPublicPreviewOrigin(publicOrigin)}${finalize.url}`
@@ -15,7 +15,7 @@
15
15
  // peach what-happened --limit 50 # cap (default 200)
16
16
 
17
17
  import { getCliIdentityKey } from '../current-sim'
18
- import { rnxExit } from '../run-rnx'
18
+ import { peachExit } from '../run-rnx'
19
19
  import { createBridgeFromParsed, parseBridgeCliArgs } from '../ws-bridge'
20
20
  import {
21
21
  formatTimelineEvent,
@@ -184,7 +184,7 @@ note: react-commit, layout, and scroll are opt-in/noisy events. enable them
184
184
  with "peach timeline start <kind>", then pass --noisy or include them in
185
185
  --kinds to see them.
186
186
  `)
187
- rnxExit(0)
187
+ peachExit(0)
188
188
  }
189
189
 
190
190
  const summaryMode = args.includes('--summary')
@@ -5,7 +5,8 @@ import { getParentPid, getProcessComm, getProcessPgid } from './parent-pid'
5
5
  import { writeSecureJsonAtomic } from './secure-file'
6
6
 
7
7
  const CURRENT_SIM_VERSION = 2 as const
8
- const CURRENT_SIM_PATH_ENV = 'SOOTSIM_CLI_CURRENT_SIM_PATH'
8
+ const CURRENT_SIM_PATH_ENV = 'PEACH_CLI_CURRENT_SIM_PATH'
9
+ const LEGACY_CURRENT_SIM_PATH_ENV = 'SOOTSIM_CLI_CURRENT_SIM_PATH'
9
10
 
10
11
  export interface LocalCurrentTarget {
11
12
  version: 2
@@ -167,8 +168,10 @@ export function getOwnerProcessGroupId(): number | null {
167
168
  }
168
169
 
169
170
  export function getCurrentSimPath() {
170
- if (process.env[CURRENT_SIM_PATH_ENV]) {
171
- return resolve(process.env[CURRENT_SIM_PATH_ENV])
171
+ const envPath =
172
+ process.env[CURRENT_SIM_PATH_ENV] || process.env[LEGACY_CURRENT_SIM_PATH_ENV]
173
+ if (envPath) {
174
+ return resolve(envPath)
172
175
  }
173
176
  const key = getCliIdentityKey()
174
177
  return join(tmpdir(), `peach-current-sim-${key}.json`)
@@ -1,10 +1,11 @@
1
1
  import { execFileSync, spawn } from 'child_process'
2
- import { existsSync, readdirSync } from 'fs'
2
+ import { existsSync, mkdirSync, writeFileSync } from 'fs'
3
3
  import { dirname, join, resolve } from 'path'
4
4
  import { fileURLToPath } from 'url'
5
5
  import { activeDesktopHostApp, isNativeHostBundle } from '../src/desktop-host-delivery'
6
+ import { rnxHomeDir } from '../src/home-paths'
6
7
 
7
- // resolved lazily — sootsim-engine is a workspace package in dev, but absent
8
+ // resolved lazily: peach-engine is a workspace package in dev, but absent
8
9
  // from the published npm tarball. don't crash module load just because the
9
10
  // engine package isn't installed; commands that don't need the dev-electron
10
11
  // path (most of them) shouldn't care.
@@ -20,19 +21,11 @@ function getSootsimRoot(): string | null {
20
21
  }
21
22
  return _sootsimRoot
22
23
  }
23
- const PEACH_BUNDLE_ID = 'dev.peachsim.simulator'
24
24
 
25
25
  export interface DesktopCompanionInstall {
26
26
  path: string
27
27
  platform: NodeJS.Platform
28
- kind:
29
- | 'native-mac-app'
30
- | 'native-linux-app'
31
- | 'mac-app'
32
- | 'linux-appimage'
33
- | 'linux-binary'
34
- | 'win-exe'
35
- | 'dev-electron'
28
+ kind: 'native-mac-app' | 'native-linux-app' | 'win-exe' | 'dev-electron'
36
29
  // present for dev-electron: the sootsim-engine dir that electron treats
37
30
  // as the app root (its package.json "main" points at dist-electron/main.cjs).
38
31
  engineDir?: string
@@ -40,14 +33,7 @@ export interface DesktopCompanionInstall {
40
33
 
41
34
  export interface DesktopCompanionLaunchResult {
42
35
  launched: boolean
43
- via?:
44
- | 'native-mac-app'
45
- | 'native-linux-app'
46
- | 'mac-app'
47
- | 'linux-appimage'
48
- | 'linux-binary'
49
- | 'win-exe'
50
- | 'dev-electron'
36
+ via?: 'native-mac-app' | 'native-linux-app' | 'win-exe' | 'dev-electron'
51
37
  target?: string
52
38
  }
53
39
 
@@ -84,8 +70,7 @@ function findDevElectron(): DesktopCompanionInstall | null {
84
70
 
85
71
  // detect whether the sootsim-engine we resolved is the in-repo workspace
86
72
  // checkout (has src-electron/main.ts) vs something pulled in as a dep.
87
- // this matters because repo development has one supported path: dev electron
88
- // against the live :5173 renderer.
73
+ // Windows development uses Electron against the live renderer.
89
74
  function isInRepoCheckout(): boolean {
90
75
  const root = getSootsimRoot()
91
76
  if (!root) return false
@@ -95,7 +80,7 @@ function isInRepoCheckout(): boolean {
95
80
  function findElectronBin(startDir: string): string | null {
96
81
  let dir = startDir
97
82
  for (let i = 0; i < 6; i++) {
98
- const candidate = join(dir, 'node_modules/.bin/electron')
83
+ const candidate = join(dir, 'node_modules/electron/dist/electron.exe')
99
84
  if (existsSync(candidate)) return candidate
100
85
  const parent = dirname(dir)
101
86
  if (parent === dir) break
@@ -104,120 +89,38 @@ function findElectronBin(startDir: string): string | null {
104
89
  return null
105
90
  }
106
91
 
107
- function findInstalledMacApp(): DesktopCompanionInstall | null {
108
- const candidates = [
109
- '/Applications/peach.app',
110
- resolve(process.env.HOME || '', 'Applications/peach.app'),
111
- ]
112
-
113
- // the native host's /Applications/Peach.app is the same path on a
114
- // case-insensitive disk
115
- const found = candidates.find(
116
- (candidate) => existsSync(candidate) && !isNativeHostBundle(candidate),
117
- )
118
- if (found) {
119
- return {
120
- path: found,
121
- platform: 'darwin',
122
- kind: 'mac-app',
123
- }
124
- }
125
-
126
- return null
127
- }
128
-
129
- function findMacApp(): DesktopCompanionInstall | null {
130
- const installed = findInstalledMacApp()
131
- if (installed) return installed
132
- const root = getSootsimRoot()
133
- const builtApp = root ? resolve(root, 'app/peach.app') : null
134
- if (builtApp && existsSync(builtApp)) {
135
- return { path: builtApp, platform: 'darwin', kind: 'mac-app' }
136
- }
137
-
138
- try {
139
- // runner-toolchain-platform: darwin
140
- const result = execFileSync(
141
- 'mdfind',
142
- [`kMDItemCFBundleIdentifier == "${PEACH_BUNDLE_ID}"`],
143
- { encoding: 'utf8', timeout: 3000 },
144
- ).trim()
145
- if (result) {
146
- return {
147
- path: result.split('\n')[0],
148
- platform: 'darwin',
149
- kind: 'mac-app',
150
- }
151
- }
152
- } catch {}
153
-
154
- return null
155
- }
156
-
157
92
  export function findInstalledDesktopCompanion(): DesktopCompanionInstall | null {
158
- if (process.platform === 'darwin') return findInstalledMacApp()
159
- if (process.platform === 'linux') return findInstalledLinuxApp()
160
- if (process.platform === 'win32') return findWindowsInstall()
161
- return null
93
+ return process.platform === 'win32'
94
+ ? findWindowsInstall()
95
+ : findNativeDesktopCompanion()
162
96
  }
163
97
 
164
- function findFirstAppImage(dir: string): string | null {
165
- if (!existsSync(dir)) return null
166
- const match = readdirSync(dir)
167
- .filter((entry) => entry.startsWith('peach') && entry.endsWith('.AppImage'))
168
- .sort()
169
- .reverse()[0]
170
- return match ? join(dir, match) : null
171
- }
172
-
173
- function findInstalledLinuxApp(): DesktopCompanionInstall | null {
174
- const directCandidates = [
175
- resolve(process.env.HOME || '', 'Applications/peach.AppImage'),
176
- resolve(process.env.HOME || '', '.local/bin/peach.AppImage'),
177
- '/opt/peach/peach.AppImage',
178
- '/opt/peach/peach',
179
- ]
180
-
181
- const directMatch = directCandidates.find((candidate) => existsSync(candidate))
182
- if (directMatch) {
183
- return {
184
- path: directMatch,
185
- platform: 'linux',
186
- kind: directMatch.endsWith('.AppImage') ? 'linux-appimage' : 'linux-binary',
187
- }
188
- }
189
-
190
- return null
191
- }
192
-
193
- function findLinuxInstall(): DesktopCompanionInstall | null {
194
- const installed = findInstalledLinuxApp()
195
- if (installed) return installed
196
- const root = getSootsimRoot()
197
- const directCandidates = root
198
- ? [resolve(root, 'app/peach.AppImage'), resolve(root, 'release/linux-unpacked/peach')]
199
- : []
200
- const directMatch = directCandidates.find((candidate) => existsSync(candidate))
201
- if (directMatch) {
202
- return {
203
- path: directMatch,
204
- platform: 'linux',
205
- kind: directMatch.endsWith('.AppImage') ? 'linux-appimage' : 'linux-binary',
206
- }
207
- }
208
-
209
- const scannedMatch = root
210
- ? findFirstAppImage(resolve(root, 'app')) ||
211
- findFirstAppImage(resolve(root, 'release'))
212
- : null
213
-
214
- if (!scannedMatch) return null
215
-
216
- return {
217
- path: scannedMatch,
218
- platform: 'linux',
219
- kind: 'linux-appimage',
98
+ // existing Electron apps stay on disk; CLI users receive the migration once.
99
+ export function notifyDesktopMigration(): void {
100
+ if (process.platform !== 'darwin' && process.platform !== 'linux') return
101
+ const legacy =
102
+ process.platform === 'darwin'
103
+ ? [
104
+ '/Applications/peach.app',
105
+ resolve(process.env.HOME || '', 'Applications/peach.app'),
106
+ ].some((candidate) => existsSync(candidate) && !isNativeHostBundle(candidate))
107
+ : ['Applications/peach.AppImage', '.local/bin/peach.AppImage'].some((candidate) =>
108
+ existsSync(resolve(process.env.HOME || '', candidate)),
109
+ ) ||
110
+ existsSync('/opt/peach/peach.AppImage') ||
111
+ existsSync('/opt/peach/peach')
112
+ const marker = join(rnxHomeDir(), 'electron-native-migration')
113
+ if (!legacy || existsSync(marker)) return
114
+ mkdirSync(rnxHomeDir(), { recursive: true })
115
+ try {
116
+ writeFileSync(marker, '', { flag: 'wx' })
117
+ } catch (error) {
118
+ if (error instanceof Error && 'code' in error && error.code === 'EEXIST') return
119
+ throw error
220
120
  }
121
+ console.error(
122
+ ' Electron has retired on Mac and Linux. Install the native app: peach install-desktop',
123
+ )
221
124
  }
222
125
 
223
126
  function findWindowsInstall(): DesktopCompanionInstall | null {
@@ -244,14 +147,21 @@ export interface DesktopCompanionOptions {
244
147
  native?: boolean
245
148
  }
246
149
 
247
- // an explicit PEACH_DESKTOP_APP selects the native host for every launch.
150
+ // Electron is supported only on Windows; Mac and Linux always use native.
248
151
  export function wantsNativeDesktop(opts: DesktopCompanionOptions = {}): boolean {
249
- return opts.native === true || !!process.env.PEACH_DESKTOP_APP
152
+ return (
153
+ process.platform === 'darwin' || process.platform === 'linux' || opts.native === true
154
+ )
250
155
  }
251
156
 
252
157
  export function getNativeDesktopAppPath(): string | null {
253
158
  const explicitApp = process.env.PEACH_DESKTOP_APP
254
- if (!explicitApp) return activeDesktopHostApp()
159
+ if (!explicitApp)
160
+ return process.platform === 'linux'
161
+ ? existsSync('/usr/bin/peach-desktop')
162
+ ? '/usr/bin/peach-desktop'
163
+ : null
164
+ : activeDesktopHostApp()
255
165
  if (process.platform === 'darwin' && !explicitApp.endsWith('.app'))
256
166
  throw new Error('PEACH_DESKTOP_APP must name a Mac .app bundle')
257
167
  if (process.platform !== 'darwin' && process.platform !== 'linux')
@@ -279,6 +189,7 @@ export function findDesktopCompanion(
279
189
  opts: DesktopCompanionOptions = {},
280
190
  ): DesktopCompanionInstall | null {
281
191
  if (wantsNativeDesktop(opts)) return findNativeDesktopCompanion()
192
+ if (process.platform !== 'win32') return null
282
193
  const dev = findDevElectron()
283
194
  if (dev) return dev
284
195
  if (isInRepoCheckout()) {
@@ -288,7 +199,7 @@ export function findDesktopCompanion(
288
199
  console.error(
289
200
  ` dist-electron/main.cjs missing in ${engineDir}.\n` +
290
201
  ` run: bun run --cwd packages/peach-engine build:electron-main\n` +
291
- ` (or keep it fresh with: bun run watch:sootsim:electron-main)`,
202
+ ` (or keep it fresh with: bun run watch:peach:electron-main)`,
292
203
  )
293
204
  } else {
294
205
  console.error(
@@ -297,8 +208,6 @@ export function findDesktopCompanion(
297
208
  }
298
209
  return null
299
210
  }
300
- if (process.platform === 'darwin') return findMacApp()
301
- if (process.platform === 'linux') return findLinuxInstall()
302
211
  if (process.platform === 'win32') return findWindowsInstall()
303
212
  return null
304
213
  }
@@ -343,6 +252,7 @@ export async function launchDesktopCompanion(
343
252
  install = findDesktopCompanion(),
344
253
  opts: LaunchCompanionOptions = {},
345
254
  ): Promise<DesktopCompanionLaunchResult> {
255
+ notifyDesktopMigration()
346
256
  if (!install) return { launched: false }
347
257
 
348
258
  // electron argv extra: main.ts reads --device <model> from process.argv
@@ -361,22 +271,18 @@ export async function launchDesktopCompanion(
361
271
  if (opts.ephemeralProfile) extraArgs.push('--ephemeral-profile')
362
272
  if (opts.ownerPid && opts.ownerPid > 1) extraArgs.push(`--owner-pid=${opts.ownerPid}`)
363
273
 
364
- if (install.kind === 'mac-app' || install.kind === 'native-mac-app') {
274
+ if (install.kind === 'native-mac-app') {
365
275
  // `open -a` splits app args before and after `--args`. anything after
366
276
  // --args is forwarded to electron's process.argv.
367
- const nativeApp = install.kind === 'native-mac-app'
368
277
  // the personal home screen belongs to launches from Spotlight or the Dock;
369
278
  // every CLI launch gets the plain one
370
- if (nativeApp) extraArgs.push('--no-personal-home')
279
+ extraArgs.push('--no-personal-home')
371
280
  // a CLI launch builds its windows from its own args: skip appkit's restore of a prior
372
281
  // crashed session, whose reopen alert blocks boot, without clearing the saved state
373
- if (nativeApp) extraArgs.push('-ApplePersistenceIgnoreState', 'YES')
282
+ extraArgs.push('-ApplePersistenceIgnoreState', 'YES')
374
283
  const openArgs: string[] = ['-g', '-a', install.path]
375
- if (url) openArgs.push(buildMacLaunchUrl(url, opts, nativeApp))
376
- else if (
377
- nativeApp &&
378
- (opts.profileId || opts.ephemeralProfile || opts.ownerPid || opts.device)
379
- )
284
+ if (url) openArgs.push(buildMacLaunchUrl(url, opts, true))
285
+ else if (opts.profileId || opts.ephemeralProfile || opts.ownerPid || opts.device)
380
286
  openArgs.push(buildMacLaunchUrl(undefined, opts, true))
381
287
  if (extraArgs.length > 0) openArgs.push('--args', ...extraArgs)
382
288
  await spawnDetached('open', openArgs)
@@ -419,8 +325,8 @@ function buildMacLaunchUrl(
419
325
  (!nativeApp || !opts.device)
420
326
  )
421
327
  return url || ''
422
- const deepLink = new URL('peach://dev')
423
- if (url) deepLink.searchParams.set('url', url)
328
+ const deepLink = new URL(url?.startsWith('peach://') ? url : 'peach://dev')
329
+ if (url && !url.startsWith('peach://')) deepLink.searchParams.set('url', url)
424
330
  if (nativeApp && opts.device) deepLink.searchParams.set('device', opts.device)
425
331
  if (opts.profileId) deepLink.searchParams.set('profile', opts.profileId)
426
332
  if (opts.ephemeralProfile) deepLink.searchParams.set('ephemeral', '1')
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // inside the Contrast monorepo checkout serve() serves public/sootsim — the
4
4
  // dev-stack engine build — directly (see resolveDevCheckoutRuntimeRoot).
5
- // without the dev watch running (bun dev's watch:sootsim) that build goes
5
+ // without the dev watch running (bun dev's watch:peach) that build goes
6
6
  // stale the moment an engine/shell source is edited, and serve() would hand
7
7
  // sims the old bundle with no complaint. this module compares source and
8
8
  // artifact mtimes per build route and reports which routes are stale, so
@@ -20,7 +20,7 @@
20
20
  import { readdirSync, statSync, type Dirent } from 'node:fs'
21
21
  import { join, relative, sep } from 'node:path'
22
22
 
23
- export const DEV_BUILD_REBUILD_COMMAND = 'bun run build:sootsim'
23
+ export const DEV_BUILD_REBUILD_COMMAND = 'bun run build:peach'
24
24
 
25
25
  export type DevBuildRouteId = 'shell' | 'engine' | 'tenant'
26
26
 
@@ -1,8 +1,11 @@
1
- // desktop drivers: `desktop` launches the native host, `electron` the
2
- // Electron companion. both wrap the desktop-companion helpers, which already
3
- // handle mac `open -a`, linux AppImage and the --device seed.
1
+ // desktop driver selection is shared with companion discovery.
4
2
 
5
- import { findDesktopCompanion, launchDesktopCompanion } from '../desktop-companion'
3
+ import {
4
+ findDesktopCompanion,
5
+ launchDesktopCompanion,
6
+ wantsNativeDesktop,
7
+ notifyDesktopMigration,
8
+ } from '../desktop-companion'
6
9
  import type {
7
10
  Driver,
8
11
  DriverAvailability,
@@ -11,8 +14,8 @@ import type {
11
14
  } from './types'
12
15
 
13
16
  export function createDesktopDriver(id: 'desktop' | 'electron'): Driver {
14
- const native = id === 'desktop'
15
- const missing = `peach ${id} app not installed (run \`peach desktop install${native ? ' --native' : ''}\`)`
17
+ const native = wantsNativeDesktop({ native: id === 'desktop' })
18
+ const missing = `peach ${id} app not installed (run \`peach desktop install${native ? '' : ' --electron'}\`)`
16
19
  function probe(): DriverAvailability {
17
20
  const install = findDesktopCompanion({ native })
18
21
  if (!install) return { available: false, reason: missing }
@@ -25,6 +28,11 @@ export function createDesktopDriver(id: 'desktop' | 'electron'): Driver {
25
28
 
26
29
  async function launch(opts: DriverLaunchOptions): Promise<DriverLaunchResult> {
27
30
  try {
31
+ notifyDesktopMigration()
32
+ if (id === 'electron' && native)
33
+ console.error(
34
+ ' Electron is Windows-only; using the native desktop app (peach install-desktop).',
35
+ )
28
36
  const install = findDesktopCompanion({ native })
29
37
  if (!install) return { launched: false, message: missing }
30
38
  const result = await launchDesktopCompanion(opts.url, install, {
@@ -38,7 +46,7 @@ export function createDesktopDriver(id: 'desktop' | 'electron'): Driver {
38
46
  }
39
47
  return {
40
48
  launched: true,
41
- message: `${install.kind === 'native-mac-app' ? 'native desktop' : 'electron'} launched via ${result.via}${opts.url ? ` → ${opts.url}` : ''}`,
49
+ message: `${native ? 'native desktop' : 'electron'} launched via ${result.via}${opts.url ? ` → ${opts.url}` : ''}`,
42
50
  target: result.target,
43
51
  attachUrl: opts.url,
44
52
  }
@@ -54,8 +62,8 @@ export function createDesktopDriver(id: 'desktop' | 'electron'): Driver {
54
62
  id,
55
63
  name: id,
56
64
  description: native
57
- ? 'native desktop host (macOS arm64; native window, menu bar)'
58
- : 'peach desktop companion app (Electron; native window, menu bar)',
65
+ ? 'native desktop host (macOS arm64 and Linux x64)'
66
+ : 'peach desktop companion app (Electron; Windows only)',
59
67
  kind: 'native',
60
68
  availability: probe,
61
69
  launch,
@@ -1,5 +1,4 @@
1
- // electron driver: the Electron desktop companion, until the native host
2
- // (the `desktop` driver) replaces it.
1
+ // Electron is Windows-only; other desktop platforms launch the native host.
3
2
 
4
3
  import { createDesktopDriver } from './desktop'
5
4
 
@@ -104,10 +104,11 @@ const viewport = viewportSpec
104
104
  const deviceScaleFactor = Number(process.env.SOOTSIM_PW_DSF || 2);
105
105
  const closeTimeoutMs = Number(process.env.SOOTSIM_PW_CLOSE_TIMEOUT_MS || 2500);
106
106
  const connectAckFile = process.env.SOOTSIM_PW_CONNECTED_ACK_FILE || '';
107
+ const completionFile = process.env.PEACH_PW_COMPLETION_FILE || '';
107
108
  const disposableBrowserCacheDirs = new Set(${JSON.stringify(
108
109
  DISPOSABLE_BROWSER_CACHE_DIR_NAMES,
109
110
  )});
110
- const { existsSync, mkdtempSync, readdirSync, rmSync, unlinkSync } = require('fs');
111
+ const { existsSync, mkdtempSync, readdirSync, rmSync, unlinkSync, writeFileSync } = require('fs');
111
112
  const { execSync, spawn } = require('child_process');
112
113
  const { tmpdir } = require('os');
113
114
  const { join } = require('path');
@@ -363,10 +364,17 @@ const cleanupProfile = () => {
363
364
  process.once('SIGINT', () => { void shutdown(0); });
364
365
  // the launch-failure path at the bottom of this script exits without going
365
366
  // through shutdown(); the exit hook (sync sweep) still reaps the tree.
366
- process.on('exit', () => {
367
+ process.on('exit', (code) => {
367
368
  if (desktopHost && desktopHost.exitCode === null) desktopHost.kill('SIGKILL');
368
369
  reapChrome();
369
370
  cleanupProfile();
371
+ if (completionFile) {
372
+ try {
373
+ writeFileSync(completionFile, JSON.stringify({ code, pid: process.pid, completedAt: Date.now() }) + '\\n');
374
+ } catch (error) {
375
+ process.stderr.write('[completion-error] ' + String(error) + '\\n');
376
+ }
377
+ }
370
378
  });
371
379
  // owner-liveness watchdog — the load-bearing fix for leaked Chrome hosts.
372
380
  // we are spawned detached + unref'd so \`peach open\` returns immediately,
@@ -651,6 +659,7 @@ const cleanupProfile = () => {
651
659
  page.on('console', (m) => {
652
660
  if (m.type() === 'error') {
653
661
  process.stderr.write('[console.error] ' + m.text() + '\\n');
662
+ if (hostCommand && m.text().includes('[compositor] worker lost:')) void shutdown(1);
654
663
  }
655
664
  });
656
665
  // a crashed renderer never runs the page's bridge close path, so nothing