peachsim 0.1.628 → 0.1.630

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 (111) hide show
  1. package/README.md +8 -4
  2. package/cli/bridge-flow-runner.ts +10 -3
  3. package/cli/cli-update.ts +2 -7
  4. package/cli/cloud-test-tunnel.ts +16 -3
  5. package/cli/commands/config.ts +1 -1
  6. package/cli/commands/control.ts +28 -3
  7. package/cli/commands/daemon-mac-app.ts +4 -2
  8. package/cli/commands/daemon.ts +4 -4
  9. package/cli/commands/device.ts +2 -3
  10. package/cli/commands/inspect/actions.ts +125 -90
  11. package/cli/commands/inspect/core.ts +5 -0
  12. package/cli/commands/inspect.ts +27 -13
  13. package/cli/commands/platform.ts +1 -1
  14. package/cli/commands/screenshot-layers.ts +1 -1
  15. package/cli/commands/screenshot.ts +1 -1
  16. package/cli/commands/skills.ts +0 -31
  17. package/cli/drivers/playwright-provisioning.ts +3 -2
  18. package/cli/drivers/playwright-sim-host.ts +25 -9
  19. package/cli/main.ts +8 -2
  20. package/cli/outbound-endpoints.ts +0 -4
  21. package/detox/index.ts +74 -43
  22. package/dist-lib/agent-daemon-client.cjs +1 -1
  23. package/dist-lib/agent-events.cjs +1 -1
  24. package/dist-lib/agent-identity.cjs +1 -1
  25. package/dist-lib/agent-sessions.cjs +1 -1
  26. package/dist-lib/attached-projects.cjs +1 -1
  27. package/dist-lib/auth/shared-session.cjs +1 -1
  28. package/dist-lib/backend-origin.cjs +1 -1
  29. package/dist-lib/beta.cjs +1 -1
  30. package/dist-lib/beta.mjs +1 -1
  31. package/dist-lib/bridge-constants.cjs +1 -1
  32. package/dist-lib/bridge-contract-input.cjs +1 -1
  33. package/dist-lib/bridge-contract-input.mjs +1 -1
  34. package/dist-lib/bridge-contract.cjs +1 -1
  35. package/dist-lib/bridge-contract.mjs +1 -1
  36. package/dist-lib/capture-contract.cjs +1 -1
  37. package/dist-lib/capture-contract.mjs +1 -1
  38. package/dist-lib/cli-constants.cjs +1 -1
  39. package/dist-lib/cloud-contract.cjs +1 -1
  40. package/dist-lib/cloud-contract.mjs +1 -1
  41. package/dist-lib/cloud-test-args.cjs +1 -1
  42. package/dist-lib/cloud-test-args.mjs +1 -1
  43. package/dist-lib/cloud.cjs +1 -1
  44. package/dist-lib/cloud.mjs +1 -1
  45. package/dist-lib/config.cjs +1 -1
  46. package/dist-lib/detox/index.cjs +129 -27
  47. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  48. package/dist-lib/home-paths.cjs +1 -1
  49. package/dist-lib/host/bridge-host.cjs +290 -315
  50. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  51. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  52. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  53. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  54. package/dist-lib/host/websocket-proxy.cjs +1 -1
  55. package/dist-lib/index.cjs +253 -121
  56. package/dist-lib/jump-to-source-babel.cjs +1 -1
  57. package/dist-lib/jump-to-source-native.cjs +1 -1
  58. package/dist-lib/{vite-kotlin.cjs → kotlin-host.cjs} +130 -19
  59. package/dist-lib/menu.cjs +1 -1
  60. package/dist-lib/menu.mjs +1 -1
  61. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  62. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  63. package/dist-lib/metro-production-bundle.cjs +1 -1
  64. package/dist-lib/metro-production-bundle.mjs +1 -1
  65. package/dist-lib/metro.cjs +132 -9
  66. package/dist-lib/native-app-identity.cjs +278 -0
  67. package/dist-lib/native-app-identity.mjs +245 -0
  68. package/dist-lib/profiles.cjs +1 -1
  69. package/dist-lib/public-brand.cjs +1 -1
  70. package/dist-lib/react-native-host-modules.cjs +1 -1
  71. package/dist-lib/react-native-host-modules.mjs +1 -1
  72. package/dist-lib/render-mode.cjs +1 -1
  73. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  74. package/dist-lib/sdk.cjs +110 -87
  75. package/dist-lib/sdk.mjs +110 -87
  76. package/dist-lib/skills.cjs +841 -643
  77. package/dist-lib/swift-compile.cjs +1 -1
  78. package/dist-lib/swift-compile.mjs +1 -1
  79. package/dist-lib/swift-package.cjs +1 -1
  80. package/dist-lib/swift-package.mjs +1 -1
  81. package/dist-lib/swift-submit.cjs +1 -1
  82. package/dist-lib/swift-submit.mjs +1 -1
  83. package/dist-lib/swift.cjs +282 -177
  84. package/dist-lib/vite-swift.cjs +1 -1
  85. package/dist-lib/vite.cjs +11 -28
  86. package/kotlin/README.md +32 -4
  87. package/kotlin/fixtures/counter/Counter.kt +26 -0
  88. package/kotlin/fixtures/counter/CounterScreen.kt +40 -0
  89. package/kotlin/fixtures/headless-counter/Headless.kt +228 -0
  90. package/kotlin/fixtures/runtime-state/RuntimeState.kt +200 -0
  91. package/kotlin/fork/LICENSE +178 -0
  92. package/kotlin/fork/NOTICE +25 -0
  93. package/kotlin/fork/README.md +59 -0
  94. package/kotlin/fork/build.sh +128 -0
  95. package/kotlin/fork/compose-worker-globals.patch +96 -0
  96. package/kotlin/fork/fork.json +33 -0
  97. package/kotlin/fork/publish.ts +77 -0
  98. package/kotlin/fork/skiko-worker-clock.patch +33 -0
  99. package/kotlin/toolchain.json +288 -0
  100. package/package.json +12 -5
  101. package/src/automatic-update-policy.ts +17 -0
  102. package/src/host/bridge-host.ts +3 -1
  103. package/src/{vite-plugin-kotlin.ts → kotlin-host.ts} +155 -12
  104. package/src/metro-plugin.ts +18 -0
  105. package/src/native-app-identity.ts +376 -0
  106. package/src/native-firebase-config.ts +90 -0
  107. package/src/screenshots/frame-compose.ts +5 -2
  108. package/src/screenshots/registry.ts +1 -1
  109. package/src/screenshots/schema.ts +2 -2
  110. package/src/vite-plugin-one.ts +11 -28
  111. package/src/vite-plugin.ts +6 -1
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.630 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -2079,8 +2079,8 @@ var init_capability_inventory_coverage = __esm({
2079
2079
  combined: {
2080
2080
  raw: {
2081
2081
  covered: 7,
2082
- ratio: 0.00432632880099,
2083
- total: 1618
2082
+ ratio: 0.00432365657813,
2083
+ total: 1619
2084
2084
  },
2085
2085
  weighted: {
2086
2086
  covered: 118953211737e-15,
@@ -2091,8 +2091,8 @@ var init_capability_inventory_coverage = __esm({
2091
2091
  semantic: {
2092
2092
  raw: {
2093
2093
  covered: 7,
2094
- ratio: 0.0061295971979,
2095
- total: 1142
2094
+ ratio: 0.00612423447069,
2095
+ total: 1143
2096
2096
  },
2097
2097
  weighted: {
2098
2098
  covered: 118953211737e-15,
@@ -2118,7 +2118,7 @@ var init_capability_inventory_coverage = __esm({
2118
2118
  raw: {
2119
2119
  covered: 0,
2120
2120
  ratio: 0,
2121
- total: 1419
2121
+ total: 1420
2122
2122
  },
2123
2123
  weighted: {
2124
2124
  covered: 0,
@@ -2130,7 +2130,7 @@ var init_capability_inventory_coverage = __esm({
2130
2130
  raw: {
2131
2131
  covered: 0,
2132
2132
  ratio: 0,
2133
- total: 990
2133
+ total: 991
2134
2134
  },
2135
2135
  weighted: {
2136
2136
  covered: 0,
@@ -2154,25 +2154,25 @@ var init_capability_inventory_coverage = __esm({
2154
2154
  ios: {
2155
2155
  combined: {
2156
2156
  raw: {
2157
- covered: 229,
2158
- ratio: 0.159248956885,
2159
- total: 1438
2157
+ covered: 242,
2158
+ ratio: 0.168172341904,
2159
+ total: 1439
2160
2160
  },
2161
2161
  weighted: {
2162
- covered: 0.5733148295,
2163
- ratio: 0.125605921087,
2162
+ covered: 0.587946074544,
2163
+ ratio: 0.128811439095,
2164
2164
  total: 4.56439333862
2165
2165
  }
2166
2166
  },
2167
2167
  semantic: {
2168
2168
  raw: {
2169
- covered: 229,
2170
- ratio: 0.224730127576,
2171
- total: 1019
2169
+ covered: 242,
2170
+ ratio: 0.237254901961,
2171
+ total: 1020
2172
2172
  },
2173
2173
  weighted: {
2174
- covered: 0.5733148295,
2175
- ratio: 0.526183631135,
2174
+ covered: 0.587946074544,
2175
+ ratio: 0.539612067396,
2176
2176
  total: 1.08957176844
2177
2177
  }
2178
2178
  },
@@ -3594,20 +3594,20 @@ var init_capability_outcomes2 = __esm({
3594
3594
  android: {
3595
3595
  passedWeight: 0,
3596
3596
  failedWeight: 0,
3597
- unassessedWeight: 1240,
3598
- totalWeight: 1240,
3597
+ unassessedWeight: 1241,
3598
+ totalWeight: 1241,
3599
3599
  assessedWeight: 0,
3600
3600
  compatibility: null,
3601
3601
  assessed: 0
3602
3602
  },
3603
3603
  ios: {
3604
- passedWeight: 176,
3604
+ passedWeight: 175,
3605
3605
  failedWeight: 0,
3606
- unassessedWeight: 1075,
3607
- totalWeight: 1251,
3608
- assessedWeight: 176,
3606
+ unassessedWeight: 1077,
3607
+ totalWeight: 1252,
3608
+ assessedWeight: 175,
3609
3609
  compatibility: 1,
3610
- assessed: 0.14068745003996802
3610
+ assessed: 0.13977635782747605
3611
3611
  }
3612
3612
  },
3613
3613
  usage: {
@@ -3621,22 +3621,19 @@ var init_capability_outcomes2 = __esm({
3621
3621
  assessed: 0
3622
3622
  },
3623
3623
  ios: {
3624
- passedWeight: 8041,
3624
+ passedWeight: 7981,
3625
3625
  failedWeight: 0,
3626
- unassessedWeight: 24473,
3626
+ unassessedWeight: 24533,
3627
3627
  totalWeight: 32514,
3628
- assessedWeight: 8041,
3628
+ assessedWeight: 7981,
3629
3629
  compatibility: 1,
3630
- assessed: 0.24730885157163068
3630
+ assessed: 0.2454634926493203
3631
3631
  }
3632
3632
  }
3633
3633
  },
3634
- capturedAt: "2026-09-25T14:01:15.160Z",
3635
- receipts: [
3636
- "core-paired-capture.2026-09-25T14:01:15.160Z.json",
3637
- "core-paired-capture.2026-09-26T12:47:39.597Z.json"
3638
- ],
3639
- sourceCommit: "dbfe444161bb1990b6f3264732e27e103798f5dc"
3634
+ capturedAt: "2026-10-01T07:50:49.727Z",
3635
+ receipts: ["core-paired-capture.2026-10-01T07:50:49.727Z.json"],
3636
+ sourceCommit: "7b3f26ea6413895ef474cf7adcbb130c5eb298c7"
3640
3637
  },
3641
3638
  unmatchedReferenceCount: 7368,
3642
3639
  byPlatform: {
@@ -3664,8 +3661,8 @@ var init_capability_outcomes2 = __esm({
3664
3661
  android: {
3665
3662
  passedWeight: 0,
3666
3663
  failedWeight: 0,
3667
- unassessedWeight: 1240,
3668
- totalWeight: 1240,
3664
+ unassessedWeight: 1241,
3665
+ totalWeight: 1241,
3669
3666
  assessedWeight: 0,
3670
3667
  compatibility: null,
3671
3668
  assessed: 0
@@ -3673,8 +3670,8 @@ var init_capability_outcomes2 = __esm({
3673
3670
  ios: {
3674
3671
  passedWeight: 0,
3675
3672
  failedWeight: 0,
3676
- unassessedWeight: 1251,
3677
- totalWeight: 1251,
3673
+ unassessedWeight: 1252,
3674
+ totalWeight: 1252,
3678
3675
  assessedWeight: 0,
3679
3676
  compatibility: null,
3680
3677
  assessed: 0
@@ -4404,6 +4401,8 @@ var init_supported_native_packages = __esm({
4404
4401
  // coverage
4405
4402
  "@expo/ui/community/menu",
4406
4403
  // coverage
4404
+ "@fingerprintjs/fingerprintjs-pro-react-native",
4405
+ // seam
4407
4406
  "@fugood/react-native-audio-pcm-stream",
4408
4407
  // seam
4409
4408
  "@fullstory/react-native",
@@ -4512,6 +4511,8 @@ var init_supported_native_packages = __esm({
4512
4511
  // seam
4513
4512
  "@react-native-firebase/functions",
4514
4513
  // seam
4514
+ "@react-native-firebase/messaging",
4515
+ // seam
4515
4516
  "@react-native-firebase/perf",
4516
4517
  // coverage
4517
4518
  "@react-native-firebase/storage",
@@ -4524,6 +4525,8 @@ var init_supported_native_packages = __esm({
4524
4525
  // seam
4525
4526
  "@react-native-picker/picker",
4526
4527
  // seam
4528
+ "@rive-app/react-native",
4529
+ // seam
4527
4530
  "@rn-org/react-native-shortcuts",
4528
4531
  // seam+coverage
4529
4532
  "@rocket.chat/mobile-crypto",
@@ -5196,6 +5199,8 @@ var init_supported_native_packages = __esm({
5196
5199
  // seam
5197
5200
  "react-native-view-pdf",
5198
5201
  // seam
5202
+ "react-native-view-shot",
5203
+ // seam
5199
5204
  "react-native-vision-camera",
5200
5205
  // coverage
5201
5206
  "react-native-vlc-media-player",
@@ -7041,9 +7046,9 @@ var init_registry = __esm({
7041
7046
  {
7042
7047
  range: ">=3.0.0",
7043
7048
  coverage: 0.82,
7044
- note: "captureRef/captureScreen return REAL captured pixels: the tenant resolves the target node's absolute rect (unstable_getBoundingClientRect), shell routes the native request, and the compositor pixel owner crops + re-encodes that region of the active app surface (createImageBitmap(offscreen, sx,sy,sw,sh) + OffscreenCanvas convertToBlob). The data-uri is coerced into the requested result shape (tmpfile blob URL / base64 / data-uri). validateOptions mirrors upstream src/index.tsx warning text; ViewShot exposes capture() with mount/continuous/update captureMode; releaseCapture revokes tracked blob URLs. When compositor cannot sample the surface (no painted pixels or fully out of bounds), it falls back to a validly-encoded placeholder of the right size so uploads/share/<Image> still get a working result \u2014 one fallback path, no toggle.",
7045
- working: "captureRef (real pixels of the target subtree region via shell snapshot), captureScreen (full app-surface snapshot), tmpfile/base64/data-uri results, png/jpg/webp formats with quality for lossy, explicit width/height as output dims, validateOptions parity with upstream warning text, ViewShot component with capture() ref method, captureMode mount/continuous/update, releaseCapture revoking tracked blob URLs",
7046
- missing: "true subtree isolation (the compositor crops a screen rectangle, so an overlapping sibling/overlay inside the bbox is included \u2014 real captureRef renders only the target's hierarchy), presentation-layer transforms (getBoundingClientRect reports the laid-out rect, not an animated/transformed on-screen position), zip-base64 falls back to plain base64, raw format falls back to png, snapshotContentContainer (full scroll content beyond the viewport \u2014 only the painted surface region is available)"
7049
+ note: "captureRef renders only the target view's own subtree, as RNViewShot.mm draws the view alone: the tenant sends the target's view tag, shell routes it to the compositor pixel owner, and the compositor draws that mirror node into an offscreen target at the device scale (drawIsolatedNode), clipped to the view's bounds, so an overlapping sibling or overlay is not in the image. snapshotContentContainer resolves the scroll view (the target or its first scroll view child, else rejects with the native message) and renders its whole content at offset zero. captureScreen crops the whole composited app surface, the keyWindow capture. the real package's JS runs from the bundle (per-platform option validation, ViewShot component, captureMode); the RNViewShot TurboModule seam answers captureRef/captureScreen/releaseCapture. a capture the compositor cannot produce rejects with the native error text; there is no placeholder image.",
7050
+ working: "captureRef (target subtree alone, sibling overlays excluded, clipped to its bounds), snapshotContentContainer (scroll view at full content size, non-scroll target rejects), captureScreen (full app-surface snapshot), tmpfile/base64/data-uri results, png/jpg with quality for jpg (webp on Android), explicit width/height as the drawn output size, upstream option validation, ViewShot component and captureMode (real package JS), releaseCapture revoking tracked blob URLs",
7051
+ missing: "tmpfile results are blob: URLs, not files under NSTemporaryDirectory/ReactNative, useRenderInContext is ignored (the view always scales to the requested size), presentation-layer transforms (getBoundingClientRect reports the laid-out rect, not an animated on-screen position), a content-container capture whose scroll view has no laid-out content rejects with the generic capture error rather than the zero-size message, a content-container capture draws the scroll view background and content but not the scroll view border, opacity, transform or filters (native resizes and draws the scroll view itself), Android zip-base64 falls back to plain base64 and raw to png"
7047
7052
  }
7048
7053
  ]
7049
7054
  },
@@ -7144,10 +7149,60 @@ var init_registry = __esm({
7144
7149
  versions: [
7145
7150
  {
7146
7151
  range: ">=18.0.0",
7147
- coverage: 0,
7148
- note: "the reference iOS simulator performs this package's behavior. Peach currently provides only this unsupported shape: development noop : push API surface resolves without real FCM delivery. imports and API shape receive no behavior credit",
7149
- working: "default messaging(), getToken/deleteToken, requestPermission/hasPermission AUTHORIZED, onMessage/onNotificationOpenedApp/onTokenRefresh subscriptions, getInitialNotification null, setBackgroundMessageHandler, topic subscribe/unsubscribe, auto-init/device registration helpers, AuthorizationStatus constants",
7150
- missing: "real FCM token, actual message receipt, background handler execution, notification-open events; intentionally absent for browser simulator"
7152
+ note: "native-seam only \u2014 the published @react-native-firebase/messaging JS runs unchanged against a NativeRNFBTurboMessaging seam read from the installed 26.4.0 iOS sources, modeled on the arm64 iOS simulator (never registered for remote notifications). Detox pushes with a gcm.message_id route through the notification bus to onMessage, onNotificationOpenedApp and getInitialNotification like RNFBMessaging+UNUserNotificationCenter. The opened event and one-shot initial notification are read from that native source but have no paired tap proof: on the iOS 27 scene life cycle Detox cannot deliver a tap response to RNFB (a launch tap is dispatched before RNFB installs its center delegate, and a background tap waits for an app delegate applicationDidBecomeActive that never comes). Estimates were reviewed against the installed native source by a second model.",
7153
+ features: [
7154
+ {
7155
+ id: "permission",
7156
+ usage: 3,
7157
+ importance: 3,
7158
+ estimate: 0.9,
7159
+ rationale: "hasPermission and requestPermission map every iOS authorization status like RNFBMessagingModule.mm. Absent: the provisional, announcement, carPlay and providesAppNotificationSettings request options, and the settings-open flag with its messaging_settings_for_notification_opened event."
7160
+ },
7161
+ {
7162
+ id: "foreground-messages",
7163
+ usage: 3,
7164
+ importance: 3,
7165
+ estimate: 0.9,
7166
+ rationale: "onMessage receives a foreground push through the RemoteMessage serializer. Differs: boolean fields such as aps content-available are not converted with RCTConvert BOOL semantics and integer fields use Number rather than intValue, and RNFB's own messaging_ios_foreground_presentation_options and interoperability with another center delegate are not modeled."
7167
+ },
7168
+ {
7169
+ id: "notification-opens",
7170
+ usage: 3,
7171
+ importance: 3,
7172
+ estimate: 1,
7173
+ rationale: "a default-action tap on a push with a gcm.message_id sends messaging_notification_opened and stores a one-shot initial notification, queued until the listener and JS are ready, as RNFBMessaging+UNUserNotificationCenter and RNFBRCTEventEmitter do."
7174
+ },
7175
+ {
7176
+ id: "tokens-and-registration",
7177
+ usage: 3,
7178
+ importance: 3,
7179
+ estimate: 0.9,
7180
+ rationale: "getToken and getAPNSToken reject messaging/unregistered, setAPNSToken reads back upper-case hex, and registerDeviceForRemoteMessages times out after ten seconds, as on the simulator. Absent: the FCM token fetch and onTokenRefresh that native runs after an app supplies an APNs token with setAPNSToken."
7181
+ },
7182
+ {
7183
+ id: "topics-and-token-deletion",
7184
+ usage: 2,
7185
+ importance: 2,
7186
+ estimate: 0,
7187
+ rationale: "deleteToken, subscribeToTopic and unsubscribeFromTopic reject as not available in Peach instead of settling through the FCM backend."
7188
+ },
7189
+ {
7190
+ id: "background-messages",
7191
+ usage: 2,
7192
+ importance: 3,
7193
+ estimate: 0,
7194
+ rationale: "setBackgroundMessageHandler and messaging_message_received_background, the app delegate path for data-only and background pushes, are not implemented."
7195
+ },
7196
+ {
7197
+ id: "auto-init",
7198
+ usage: 1,
7199
+ importance: 1,
7200
+ estimate: 0.5,
7201
+ rationale: "setAutoInitEnabled persists like FIRMessaging. Absent: the FirebaseMessagingAutoInitEnabled Info.plist and data collection defaults native falls back to, and the token request enabling it triggers."
7202
+ }
7203
+ ],
7204
+ working: "hasPermission and requestPermission for ordinary alert, badge, sound and critical alert requests with every iOS authorization status value, onMessage for a foreground push with the RemoteMessage fields (messageId, data, notification title/body/ios fields, category, threadId, contentAvailable), onNotificationOpenedApp and one-shot getInitialNotification from the native delegate source, getToken and getAPNSToken rejecting messaging/unregistered, setAPNSToken with upper-case hex readback, registerDeviceForRemoteMessages rejecting messaging/registration-timeout after ten seconds, constants (isDeviceRegisteredForRemoteMessages false, BigQuery flag), setAutoInitEnabled persistence, sendMessage rejecting as Android-only like iOS",
7205
+ missing: "FCM backend calls: deleteToken, subscribeToTopic and unsubscribeFromTopic reject as not available in Peach instead of settling from the backend; the background handler and messaging_message_received_background; the provisional, announcement, carPlay and providesAppNotificationSettings permission options; setOpenSettingsForNotificationsHandler and the settings-open flag; RNFB's own foreground presentation options and interoperability with another center delegate; RCTConvert BOOL and intValue semantics in the serializer; the FCM token fetch and onTokenRefresh after an app supplies an APNs token; auto-init defaults from Info.plist and data collection; app options: the seeded [DEFAULT] app carries placeholder options rather than the app's GoogleService-Info.plist values, and exists even for an app that never calls FirebaseApp.configure(); onDeletedMessages, onMessageSent and onSendError (Android only)"
7151
7206
  }
7152
7207
  ]
7153
7208
  },
@@ -7809,10 +7864,39 @@ var init_registry = __esm({
7809
7864
  versions: [
7810
7865
  {
7811
7866
  range: ">=50.0.0",
7812
- coverage: 0.2,
7813
- note: `unavailable-on-web shape per docs/compat-scoring.md ("keep low unless we add a real browser OAuth flow"): enums and button render, isAvailableAsync returns false, signInAsync/refreshAsync/signOutAsync reject with ERR_NOT_AVAILABLE matching how an unavailable native module behaves. Apps with graceful-degradation paths take the right branch. Returning a fake credential here would lie to consumer code that may try to verify the identityToken against Apple's keys. The 0.2 score is intentional: a real flow requires app-owned Apple ID JS SDK configuration, a Service ID, and an HTTPS redirect, so it cannot be supplied as a generic simulator seam.`,
7814
- working: "isAvailableAsync (false), AppleAuthenticationButton render (sign in / continue / sign up, WHITE/WHITE_OUTLINE/BLACK styles), all enum exports (Scope, Operation, CredentialState, UserDetectionStatus, ButtonType, ButtonStyle), formatFullName, getCredentialStateAsync (NOT_FOUND), addRevokeListener",
7815
- missing: "real Sign-in-with-Apple OAuth flow (would need AppleID JS SDK + Service ID + HTTPS)"
7867
+ note: "native-seam only \u2014 the published expo-apple-authentication JS runs unchanged over an ExpoAppleAuthentication module read from the installed iOS sources (AppleAuthenticationModule.swift), modeled on the iOS simulator with no Apple Account signed in, which is what the paired native iPhone 16 run uses.",
7868
+ features: [
7869
+ {
7870
+ id: "availability-and-button",
7871
+ usage: 3,
7872
+ importance: 3,
7873
+ estimate: 1,
7874
+ rationale: "isAvailableAsync resolves true as on iOS, and AppleAuthenticationButton renders every button type and style."
7875
+ },
7876
+ {
7877
+ id: "sign-in",
7878
+ usage: 3,
7879
+ importance: 3,
7880
+ estimate: 0.3,
7881
+ rationale: "signInAsync, refreshAsync and signOutAsync present the Sign in to your Apple Account alert and reject ERR_REQUEST_UNKNOWN when its Close or Settings button is tapped, as a simulator without an Apple Account does, and reject invalid scopes and operations with the native codes; after Settings rejects, Settings comes to the front on its Apple Account sign-in sheet and the app goes inactive then background. Absent: the credential a simulator or device signed in to an Apple Account issues, the sheet hero and device art (private system symbols) and the back-to-app breadcrumb, and the inactive app state while the out-of-process alert is up."
7882
+ },
7883
+ {
7884
+ id: "credential-state",
7885
+ usage: 2,
7886
+ importance: 2,
7887
+ estimate: 1,
7888
+ rationale: "getCredentialStateAsync resolves NOT_FOUND for any user, as a simulator without an Apple Account does; the revoke listener registers and never fires."
7889
+ },
7890
+ {
7891
+ id: "name-formatting",
7892
+ usage: 1,
7893
+ importance: 2,
7894
+ estimate: 0.8,
7895
+ rationale: "formatFullName formats the default, short, medium, long and abbreviated styles as PersonNameComponentsFormatter does on an en_US device for Latin, Cyrillic and Han script names: nickname fallbacks, one-letter given names, grapheme initials, family-first Han order and Han abbreviation by length. An unknown style throws ERR_ARGUMENT_CAST from the Expo bridge. Absent: its rules for kana, Hangul, Thai and mixed-script names and for components that start with a digit or symbol."
7896
+ }
7897
+ ],
7898
+ working: "isAvailableAsync (true), AppleAuthenticationButton render (sign in / continue / sign up, WHITE/WHITE_OUTLINE/BLACK styles), all enum exports, the Sign in to your Apple Account alert for signInAsync, refreshAsync and signOutAsync with ERR_REQUEST_UNKNOWN from Close or Settings, the Settings Apple Account sign-in sheet after Settings, ERR_INVALID_SCOPE and ERR_INVALID_OPERATION, getCredentialStateAsync (NOT_FOUND), formatFullName for Latin, Cyrillic and Han script names on an en_US device, addRevokeListener",
7899
+ missing: "a signed-in Apple Account: no credential, identity token or authorization code is ever issued; the Settings Apple Account sheet has no hero or device art (private system symbols) and no back-to-app breadcrumb; the app stays active while the alert is up where iOS makes it inactive; formatFullName rules for kana, Hangul, Thai and mixed-script names and for components that start with a digit or symbol"
7816
7900
  }
7817
7901
  ]
7818
7902
  },
@@ -9937,10 +10021,23 @@ var init_registry = __esm({
9937
10021
  stubType: "native",
9938
10022
  versions: [
9939
10023
  {
9940
- range: ">=8.0.0",
9941
- coverage: 0.6,
9942
- note: "identical surface to rive-react-native \u2014 the new official package is just a rename. shares the same noop stub",
9943
- missing: "same as rive-react-native \u2014 no .riv runtime"
10024
+ range: ">=0.4.19 <0.5.0",
10025
+ coverage: 0,
10026
+ note: "native seam: the published Nitro package JavaScript and hooks run unchanged. Rive canvas-advanced uses its C++ WASM core on the host for file parsing, artboard and view model metadata, instance creation, and async scalar data binding. The native view lays out and reports ready; rendering is not built. No visual or playback behavior credit is claimed.",
10027
+ working: "useRiveFile URL, file URL, and byte loading; artboard names/counts; async view model lookup and instance creation; async scalar reads/writes; RiveRuntime initialization; HybridView ref and readiness",
10028
+ missing: "Rive rendering and playback; RiveImage decoding; view binding and state machine inputs/events; referenced asset overrides; nested, trigger, list, image, and artboard binding; synchronous view model operations and scalar reads; instance names; UIKit fallback fonts"
10029
+ }
10030
+ ]
10031
+ },
10032
+ "@fingerprintjs/fingerprintjs-pro-react-native": {
10033
+ category: "Device & Platform",
10034
+ stubType: "native",
10035
+ versions: [
10036
+ {
10037
+ range: ">=3.17.1 <4.0.0",
10038
+ coverage: 0,
10039
+ note: "native seam only: upstream agent, provider, hooks, and error conversion run unchanged. RNFingerprintjsPro is present and identification calls reject with an explicit UnknownError because the native SDK is unavailable. No visitor identity or identification behavior is claimed.",
10040
+ missing: "FingerprintPro native device signal collection, API transport, visitor identities, extended and sealed responses, and location collection"
9944
10041
  }
9945
10042
  ]
9946
10043
  },
@@ -14375,9 +14472,9 @@ var init_registry = __esm({
14375
14472
  {
14376
14473
  range: ">=0.1.0",
14377
14474
  coverage: 0,
14378
- note: "getVideoDuration() resolves a hardcoded 0 instead of probing the asset",
14379
- working: "nothing \u2014 getVideoDuration() resolves a hardcoded 0",
14380
- missing: "no real AVFoundation duration probe in the simulator"
14475
+ note: "native-seam only: upstream JavaScript reads iOS duration in seconds from real MP4 container metadata; shared parser with expo-video-metadata",
14476
+ working: "iOS getVideoDuration for readable local VFS and HTTP MP4 assets; missing, invalid and unreachable sources resolve zero; published 0.1.2 API exercised",
14477
+ missing: "Android VideoDurationModule, non-MP4 media formats, streaming duration, and native iOS paired evidence"
14381
14478
  }
14382
14479
  ]
14383
14480
  },
@@ -19324,9 +19421,9 @@ function safeChildPlistName(value) {
19324
19421
  name: file.toLowerCase().endsWith(".plist") ? file : `${file}.plist`
19325
19422
  };
19326
19423
  }
19327
- async function readableSettingsBundle(path21) {
19424
+ async function readableSettingsBundle(path22) {
19328
19425
  try {
19329
- const root = await (0, import_promises.stat)((0, import_node_path.join)(path21, "Root.plist"));
19426
+ const root = await (0, import_promises.stat)((0, import_node_path.join)(path22, "Root.plist"));
19330
19427
  return root.isFile() && root.size <= MAX_PLIST_BYTES;
19331
19428
  } catch {
19332
19429
  return false;
@@ -19371,11 +19468,11 @@ async function settingsBundleFingerprint(bundlePath) {
19371
19468
  return null;
19372
19469
  }
19373
19470
  }
19374
- async function parsePlistRecord(path21) {
19471
+ async function parsePlistRecord(path22) {
19375
19472
  try {
19376
- const metadata = await (0, import_promises.stat)(path21);
19473
+ const metadata = await (0, import_promises.stat)(path22);
19377
19474
  if (!metadata.isFile() || metadata.size > MAX_PLIST_BYTES) return null;
19378
- const source = await (0, import_promises.readFile)(path21, "utf8");
19475
+ const source = await (0, import_promises.readFile)(path22, "utf8");
19379
19476
  const parsed = parsePlistSource(source);
19380
19477
  return isRecord2(parsed) ? parsed : null;
19381
19478
  } catch {
@@ -19699,7 +19796,7 @@ async function tcpPing(port, timeout = TCP_GATE_MS) {
19699
19796
  return null;
19700
19797
  }
19701
19798
  }
19702
- function httpGet(hostname, port, path21, method = "GET", timeout = TIMEOUT_MS, headers = {}) {
19799
+ function httpGet(hostname, port, path22, method = "GET", timeout = TIMEOUT_MS, headers = {}) {
19703
19800
  return new Promise((resolve9) => {
19704
19801
  let settled = false;
19705
19802
  let killing = false;
@@ -19708,7 +19805,7 @@ function httpGet(hostname, port, path21, method = "GET", timeout = TIMEOUT_MS, h
19708
19805
  settled = true;
19709
19806
  resolve9(value);
19710
19807
  };
19711
- const req = import_http.default.request({ hostname, port, path: path21, method, headers }, (res) => {
19808
+ const req = import_http.default.request({ hostname, port, path: path22, method, headers }, (res) => {
19712
19809
  let body = "";
19713
19810
  res.on("data", (c) => body += c.toString());
19714
19811
  const contentType = (() => {
@@ -20357,59 +20454,6 @@ var init_dev_server_scanner = __esm({
20357
20454
  }
20358
20455
  });
20359
20456
 
20360
- // src/public-brand.ts
20361
- var name, peachPublicBrand, rnxPublicBrand;
20362
- var init_public_brand = __esm({
20363
- "src/public-brand.ts"() {
20364
- "use strict";
20365
- name = "Peach";
20366
- peachPublicBrand = Object.freeze({
20367
- name,
20368
- packageName: "peachsim",
20369
- commandName: "peach",
20370
- domain: "peachsim.com",
20371
- origin: "https://peachsim.com"
20372
- });
20373
- rnxPublicBrand = peachPublicBrand;
20374
- }
20375
- });
20376
-
20377
- // src/cli-version.ts
20378
- function getCliVersion() {
20379
- if (cached2 != null) return cached2;
20380
- if (typeof __PEACH_CLI_VERSION__ !== "undefined" && typeof __PEACH_CLI_VERSION__ === "string" && __PEACH_CLI_VERSION__) {
20381
- cached2 = __PEACH_CLI_VERSION__;
20382
- return cached2;
20383
- }
20384
- const candidates = [
20385
- () => (0, import_node_url2.fileURLToPath)(import_meta.resolve(`${rnxPublicBrand.packageName}/package.json`)),
20386
- () => (0, import_node_url2.fileURLToPath)(new URL("../package.json", __sootsim_import_meta_url))
20387
- ];
20388
- for (const resolve9 of candidates) {
20389
- try {
20390
- const version = JSON.parse((0, import_node_fs3.readFileSync)(resolve9(), "utf8")).version;
20391
- if (typeof version === "string" && version) {
20392
- cached2 = version;
20393
- return cached2;
20394
- }
20395
- } catch {
20396
- }
20397
- }
20398
- cached2 = "0.0.0";
20399
- return cached2;
20400
- }
20401
- var import_node_fs3, import_node_url2, import_meta, cached2;
20402
- var init_cli_version = __esm({
20403
- "src/cli-version.ts"() {
20404
- "use strict";
20405
- import_node_fs3 = require("node:fs");
20406
- import_node_url2 = require("node:url");
20407
- init_public_brand();
20408
- import_meta = {};
20409
- cached2 = null;
20410
- }
20411
- });
20412
-
20413
20457
  // src/home-paths.ts
20414
20458
  var home_paths_exports = {};
20415
20459
  __export(home_paths_exports, {
@@ -20482,7 +20526,7 @@ function isSootsimDevCheckout() {
20482
20526
  const argv1 = process.argv[1];
20483
20527
  if (!argv1) return false;
20484
20528
  try {
20485
- const real = import_node_fs4.default.realpathSync(argv1);
20529
+ const real = import_node_fs3.default.realpathSync(argv1);
20486
20530
  return real.includes(`${import_node_path4.default.sep}packages${import_node_path4.default.sep}peachsim${import_node_path4.default.sep}`);
20487
20531
  } catch {
20488
20532
  return false;
@@ -20551,7 +20595,7 @@ function cliUpdateFilePath() {
20551
20595
  }
20552
20596
  function readSharedConfig() {
20553
20597
  try {
20554
- const raw = import_node_fs4.default.readFileSync(configFilePath(), "utf8");
20598
+ const raw = import_node_fs3.default.readFileSync(configFilePath(), "utf8");
20555
20599
  const parsed = JSON.parse(raw);
20556
20600
  return parsed && typeof parsed === "object" ? parsed : {};
20557
20601
  } catch {
@@ -20569,9 +20613,9 @@ function writeSharedConfig(patch) {
20569
20613
  };
20570
20614
  }
20571
20615
  const tmp = `${configFilePath()}.tmp`;
20572
- import_node_fs4.default.writeFileSync(tmp, `${JSON.stringify(next, null, 2)}
20616
+ import_node_fs3.default.writeFileSync(tmp, `${JSON.stringify(next, null, 2)}
20573
20617
  `, "utf8");
20574
- import_node_fs4.default.renameSync(tmp, configFilePath());
20618
+ import_node_fs3.default.renameSync(tmp, configFilePath());
20575
20619
  return next;
20576
20620
  }
20577
20621
  function isOnboardingComplete() {
@@ -20630,14 +20674,14 @@ function runtimeUpgradeNoticePath() {
20630
20674
  function writeRuntimeUpgradeNotice(notice) {
20631
20675
  ensureRnxHome();
20632
20676
  const tmp = `${runtimeUpgradeNoticePath()}.tmp`;
20633
- import_node_fs4.default.writeFileSync(tmp, `${JSON.stringify(notice)}
20677
+ import_node_fs3.default.writeFileSync(tmp, `${JSON.stringify(notice)}
20634
20678
  `, "utf8");
20635
- import_node_fs4.default.renameSync(tmp, runtimeUpgradeNoticePath());
20679
+ import_node_fs3.default.renameSync(tmp, runtimeUpgradeNoticePath());
20636
20680
  }
20637
20681
  function consumeRuntimeUpgradeNotice() {
20638
20682
  try {
20639
- const raw = import_node_fs4.default.readFileSync(runtimeUpgradeNoticePath(), "utf8");
20640
- import_node_fs4.default.unlinkSync(runtimeUpgradeNoticePath());
20683
+ const raw = import_node_fs3.default.readFileSync(runtimeUpgradeNoticePath(), "utf8");
20684
+ import_node_fs3.default.unlinkSync(runtimeUpgradeNoticePath());
20641
20685
  const parsed = JSON.parse(raw);
20642
20686
  if (typeof parsed?.to !== "string" || !parsed.to) return null;
20643
20687
  return {
@@ -20650,23 +20694,23 @@ function consumeRuntimeUpgradeNotice() {
20650
20694
  }
20651
20695
  }
20652
20696
  function ensureRnxHome() {
20653
- import_node_fs4.default.mkdirSync(rnxHomeDir(), { recursive: true });
20654
- import_node_fs4.default.mkdirSync(runtimesDir(), { recursive: true });
20655
- import_node_fs4.default.mkdirSync(electronDir(), { recursive: true });
20656
- import_node_fs4.default.mkdirSync(profilesDir(), { recursive: true });
20657
- import_node_fs4.default.mkdirSync(cacheDir(), { recursive: true });
20697
+ import_node_fs3.default.mkdirSync(rnxHomeDir(), { recursive: true });
20698
+ import_node_fs3.default.mkdirSync(runtimesDir(), { recursive: true });
20699
+ import_node_fs3.default.mkdirSync(electronDir(), { recursive: true });
20700
+ import_node_fs3.default.mkdirSync(profilesDir(), { recursive: true });
20701
+ import_node_fs3.default.mkdirSync(cacheDir(), { recursive: true });
20658
20702
  }
20659
20703
  function readActiveRuntime() {
20660
20704
  try {
20661
- const value = import_node_fs4.default.readFileSync(activeRuntimeFile(), "utf8").trim();
20705
+ const value = import_node_fs3.default.readFileSync(activeRuntimeFile(), "utf8").trim();
20662
20706
  return value.length > 0 ? value : null;
20663
20707
  } catch {
20664
20708
  return null;
20665
20709
  }
20666
20710
  }
20667
20711
  function writeActiveRuntime(version) {
20668
- import_node_fs4.default.mkdirSync(runtimesDir(), { recursive: true });
20669
- import_node_fs4.default.writeFileSync(activeRuntimeFile(), `${version}
20712
+ import_node_fs3.default.mkdirSync(runtimesDir(), { recursive: true });
20713
+ import_node_fs3.default.writeFileSync(activeRuntimeFile(), `${version}
20670
20714
  `, "utf8");
20671
20715
  }
20672
20716
  function activeRuntimeDir() {
@@ -20674,17 +20718,17 @@ function activeRuntimeDir() {
20674
20718
  if (!version) return null;
20675
20719
  const dir = runtimeDir(version);
20676
20720
  try {
20677
- if (import_node_fs4.default.statSync(dir).isDirectory()) return dir;
20721
+ if (import_node_fs3.default.statSync(dir).isDirectory()) return dir;
20678
20722
  } catch {
20679
20723
  }
20680
20724
  return null;
20681
20725
  }
20682
20726
  function readDaemonLockfile() {
20683
20727
  try {
20684
- const fd = import_node_fs4.default.openSync(daemonLockfilePath(), "r");
20728
+ const fd = import_node_fs3.default.openSync(daemonLockfilePath(), "r");
20685
20729
  try {
20686
20730
  const buf = Buffer.alloc(DAEMON_LOCKFILE_MAX_BYTES);
20687
- const bytesRead = import_node_fs4.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
20731
+ const bytesRead = import_node_fs3.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
20688
20732
  const raw = buf.subarray(0, bytesRead).toString("utf8");
20689
20733
  const parsed = JSON.parse(raw);
20690
20734
  if (parsed && parsed.schema === 1 && typeof parsed.pid === "number" && typeof parsed.bridgePort === "number" && typeof parsed.runtimePort === "number" && typeof parsed.startedAt === "number" && typeof parsed.heartbeatAt === "number" && (parsed.servedRuntimes === void 0 || Array.isArray(parsed.servedRuntimes) && parsed.servedRuntimes.every((version) => typeof version === "string"))) {
@@ -20692,7 +20736,7 @@ function readDaemonLockfile() {
20692
20736
  }
20693
20737
  return null;
20694
20738
  } finally {
20695
- import_node_fs4.default.closeSync(fd);
20739
+ import_node_fs3.default.closeSync(fd);
20696
20740
  }
20697
20741
  } catch {
20698
20742
  return null;
@@ -20712,7 +20756,7 @@ function parseDevBridgeLockfile(raw) {
20712
20756
  function readDevBridgeLockfiles() {
20713
20757
  let entries;
20714
20758
  try {
20715
- entries = import_node_fs4.default.readdirSync(devBridgesDir(), { withFileTypes: true });
20759
+ entries = import_node_fs3.default.readdirSync(devBridgesDir(), { withFileTypes: true });
20716
20760
  } catch {
20717
20761
  return [];
20718
20762
  }
@@ -20721,10 +20765,10 @@ function readDevBridgeLockfiles() {
20721
20765
  if (!entry.isFile() || !/^\d+\.json$/.test(entry.name)) continue;
20722
20766
  try {
20723
20767
  const filePath = import_node_path4.default.join(devBridgesDir(), entry.name);
20724
- const fd = import_node_fs4.default.openSync(filePath, "r");
20768
+ const fd = import_node_fs3.default.openSync(filePath, "r");
20725
20769
  try {
20726
20770
  const buf = Buffer.alloc(DAEMON_LOCKFILE_MAX_BYTES);
20727
- const bytesRead = import_node_fs4.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
20771
+ const bytesRead = import_node_fs3.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
20728
20772
  const lockfile = parseDevBridgeLockfile(
20729
20773
  buf.subarray(0, bytesRead).toString("utf8")
20730
20774
  );
@@ -20732,7 +20776,7 @@ function readDevBridgeLockfiles() {
20732
20776
  lockfiles.push(lockfile);
20733
20777
  }
20734
20778
  } finally {
20735
- import_node_fs4.default.closeSync(fd);
20779
+ import_node_fs3.default.closeSync(fd);
20736
20780
  }
20737
20781
  } catch {
20738
20782
  }
@@ -20784,28 +20828,28 @@ function writeDaemonLockfile(data) {
20784
20828
  throw new Error("refusing to overwrite another peach daemon lockfile");
20785
20829
  }
20786
20830
  const tmp = `${lockfilePath}.${data.pid}.${(0, import_node_crypto.randomUUID)()}.tmp`;
20787
- import_node_fs4.default.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
20831
+ import_node_fs3.default.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
20788
20832
  `, "utf8");
20789
20833
  try {
20790
20834
  const current = readDaemonLockfile();
20791
20835
  if (!current || !sameDaemonOwner(current, data)) {
20792
20836
  throw new Error("peach daemon lock ownership changed during heartbeat");
20793
20837
  }
20794
- import_node_fs4.default.renameSync(tmp, lockfilePath);
20838
+ import_node_fs3.default.renameSync(tmp, lockfilePath);
20795
20839
  } finally {
20796
20840
  try {
20797
- import_node_fs4.default.unlinkSync(tmp);
20841
+ import_node_fs3.default.unlinkSync(tmp);
20798
20842
  } catch {
20799
20843
  }
20800
20844
  }
20801
20845
  }
20802
20846
  function writeDevBridgeLockfile(data) {
20803
- import_node_fs4.default.mkdirSync(devBridgesDir(), { recursive: true });
20847
+ import_node_fs3.default.mkdirSync(devBridgesDir(), { recursive: true });
20804
20848
  const lockfilePath = devBridgeLockfilePath(data.bridgePort);
20805
20849
  const tmp = `${lockfilePath}.${data.pid}.tmp`;
20806
- import_node_fs4.default.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
20850
+ import_node_fs3.default.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
20807
20851
  `, "utf8");
20808
- import_node_fs4.default.renameSync(tmp, lockfilePath);
20852
+ import_node_fs3.default.renameSync(tmp, lockfilePath);
20809
20853
  }
20810
20854
  function sameDaemonOwner(left, right) {
20811
20855
  return left.pid === right.pid && left.startedAt === right.startedAt;
@@ -20816,17 +20860,17 @@ function isAlreadyExists(error) {
20816
20860
  function createDaemonLockfileExclusive(data) {
20817
20861
  const lockfilePath = daemonLockfilePath();
20818
20862
  const tmp = `${lockfilePath}.${data.pid}.${(0, import_node_crypto.randomUUID)()}.claim`;
20819
- import_node_fs4.default.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
20863
+ import_node_fs3.default.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
20820
20864
  `, "utf8");
20821
20865
  try {
20822
- import_node_fs4.default.linkSync(tmp, lockfilePath);
20866
+ import_node_fs3.default.linkSync(tmp, lockfilePath);
20823
20867
  return true;
20824
20868
  } catch (error) {
20825
20869
  if (isAlreadyExists(error)) return false;
20826
20870
  throw error;
20827
20871
  } finally {
20828
20872
  try {
20829
- import_node_fs4.default.unlinkSync(tmp);
20873
+ import_node_fs3.default.unlinkSync(tmp);
20830
20874
  } catch {
20831
20875
  }
20832
20876
  }
@@ -20850,7 +20894,7 @@ function parseDaemonClaim(raw) {
20850
20894
  }
20851
20895
  function readDaemonClaim() {
20852
20896
  try {
20853
- return parseDaemonClaim(import_node_fs4.default.readFileSync(daemonClaimPath(), "utf8"));
20897
+ return parseDaemonClaim(import_node_fs3.default.readFileSync(daemonClaimPath(), "utf8"));
20854
20898
  } catch {
20855
20899
  return null;
20856
20900
  }
@@ -20874,7 +20918,7 @@ function acquireDaemonClaim(data) {
20874
20918
  `;
20875
20919
  for (let attempt = 0; attempt < 8; attempt++) {
20876
20920
  try {
20877
- import_node_fs4.default.writeFileSync(daemonClaimPath(), serialized, { encoding: "utf8", flag: "wx" });
20921
+ import_node_fs3.default.writeFileSync(daemonClaimPath(), serialized, { encoding: "utf8", flag: "wx" });
20878
20922
  return true;
20879
20923
  } catch (error) {
20880
20924
  if (!isAlreadyExists(error)) throw error;
@@ -20884,11 +20928,11 @@ function acquireDaemonClaim(data) {
20884
20928
  if (existing && isDaemonClaimFresh(existing)) return false;
20885
20929
  let before;
20886
20930
  try {
20887
- before = import_node_fs4.default.readFileSync(daemonClaimPath(), "utf8");
20931
+ before = import_node_fs3.default.readFileSync(daemonClaimPath(), "utf8");
20888
20932
  const latest = parseDaemonClaim(before);
20889
20933
  if (latest && isDaemonClaimFresh(latest)) return false;
20890
- if (import_node_fs4.default.readFileSync(daemonClaimPath(), "utf8") !== before) continue;
20891
- import_node_fs4.default.unlinkSync(daemonClaimPath());
20934
+ if (import_node_fs3.default.readFileSync(daemonClaimPath(), "utf8") !== before) continue;
20935
+ import_node_fs3.default.unlinkSync(daemonClaimPath());
20892
20936
  } catch {
20893
20937
  }
20894
20938
  }
@@ -20898,7 +20942,7 @@ function releaseDaemonClaim(owner) {
20898
20942
  try {
20899
20943
  const claim = readDaemonClaim();
20900
20944
  if (!claim || !sameDaemonOwner(claim, owner)) return;
20901
- import_node_fs4.default.unlinkSync(daemonClaimPath());
20945
+ import_node_fs3.default.unlinkSync(daemonClaimPath());
20902
20946
  } catch {
20903
20947
  }
20904
20948
  }
@@ -20917,7 +20961,7 @@ function claimDaemonLockfile(data) {
20917
20961
  if (!latest || !sameDaemonOwner(latest, existing)) return false;
20918
20962
  if (isDaemonLockfileFresh(latest)) return false;
20919
20963
  try {
20920
- import_node_fs4.default.unlinkSync(daemonLockfilePath());
20964
+ import_node_fs3.default.unlinkSync(daemonLockfilePath());
20921
20965
  } catch {
20922
20966
  return false;
20923
20967
  }
@@ -20931,25 +20975,25 @@ function removeDaemonLockfile(owner) {
20931
20975
  try {
20932
20976
  const lockfile = readDaemonLockfile();
20933
20977
  if (!lockfile || !sameDaemonOwner(lockfile, owner)) return;
20934
- import_node_fs4.default.unlinkSync(daemonLockfilePath());
20978
+ import_node_fs3.default.unlinkSync(daemonLockfilePath());
20935
20979
  } catch {
20936
20980
  }
20937
20981
  }
20938
20982
  function removeDevBridgeLockfile(owner) {
20939
20983
  try {
20940
20984
  const lockfilePath = devBridgeLockfilePath(owner.bridgePort);
20941
- const lockfile = parseDevBridgeLockfile(import_node_fs4.default.readFileSync(lockfilePath, "utf8"));
20985
+ const lockfile = parseDevBridgeLockfile(import_node_fs3.default.readFileSync(lockfilePath, "utf8"));
20942
20986
  if (lockfile?.pid !== owner.pid || lockfile.startedAt !== owner.startedAt) return;
20943
- import_node_fs4.default.unlinkSync(lockfilePath);
20987
+ import_node_fs3.default.unlinkSync(lockfilePath);
20944
20988
  } catch {
20945
20989
  }
20946
20990
  }
20947
- var import_node_crypto, import_node_fs4, import_node_os, import_node_path4, PEACH_HOME_ENV, ACTIVE_RUNTIME_FILE, DAEMON_LOCKFILE, DEV_BRIDGES_DIR, CONFIG_FILE, CLI_UPDATE_FILE, DAEMON_HEARTBEAT_STALE_MS, CURRENT_ONBOARDING_VERSION, DAEMON_LOCKFILE_MAX_BYTES, DAEMON_CLAIM_STALE_MS;
20991
+ var import_node_crypto, import_node_fs3, import_node_os, import_node_path4, PEACH_HOME_ENV, ACTIVE_RUNTIME_FILE, DAEMON_LOCKFILE, DEV_BRIDGES_DIR, CONFIG_FILE, CLI_UPDATE_FILE, DAEMON_HEARTBEAT_STALE_MS, CURRENT_ONBOARDING_VERSION, DAEMON_LOCKFILE_MAX_BYTES, DAEMON_CLAIM_STALE_MS;
20948
20992
  var init_home_paths = __esm({
20949
20993
  "src/home-paths.ts"() {
20950
20994
  "use strict";
20951
20995
  import_node_crypto = require("node:crypto");
20952
- import_node_fs4 = __toESM(require("node:fs"), 1);
20996
+ import_node_fs3 = __toESM(require("node:fs"), 1);
20953
20997
  import_node_os = require("node:os");
20954
20998
  import_node_path4 = __toESM(require("node:path"), 1);
20955
20999
  PEACH_HOME_ENV = "PEACH_HOME";
@@ -20965,19 +21009,87 @@ var init_home_paths = __esm({
20965
21009
  }
20966
21010
  });
20967
21011
 
21012
+ // src/automatic-update-policy.ts
21013
+ function automaticPeachUpdatesEnabled(env = process.env) {
21014
+ const value = env.PEACH_UPDATE_CHECK?.toLowerCase();
21015
+ return value !== "0" && value !== "false" && value !== "off" && !(0, import_node_fs4.existsSync)((0, import_node_path5.join)(rnxHomeDir(), "runtime-update-disabled"));
21016
+ }
21017
+ var import_node_fs4, import_node_path5;
21018
+ var init_automatic_update_policy = __esm({
21019
+ "src/automatic-update-policy.ts"() {
21020
+ "use strict";
21021
+ import_node_fs4 = require("node:fs");
21022
+ import_node_path5 = require("node:path");
21023
+ init_home_paths();
21024
+ }
21025
+ });
21026
+
21027
+ // src/public-brand.ts
21028
+ var name, peachPublicBrand, rnxPublicBrand;
21029
+ var init_public_brand = __esm({
21030
+ "src/public-brand.ts"() {
21031
+ "use strict";
21032
+ name = "Peach";
21033
+ peachPublicBrand = Object.freeze({
21034
+ name,
21035
+ packageName: "peachsim",
21036
+ commandName: "peach",
21037
+ domain: "peachsim.com",
21038
+ origin: "https://peachsim.com"
21039
+ });
21040
+ rnxPublicBrand = peachPublicBrand;
21041
+ }
21042
+ });
21043
+
21044
+ // src/cli-version.ts
21045
+ function getCliVersion() {
21046
+ if (cached2 != null) return cached2;
21047
+ if (typeof __PEACH_CLI_VERSION__ !== "undefined" && typeof __PEACH_CLI_VERSION__ === "string" && __PEACH_CLI_VERSION__) {
21048
+ cached2 = __PEACH_CLI_VERSION__;
21049
+ return cached2;
21050
+ }
21051
+ const candidates = [
21052
+ () => (0, import_node_url2.fileURLToPath)(import_meta.resolve(`${rnxPublicBrand.packageName}/package.json`)),
21053
+ () => (0, import_node_url2.fileURLToPath)(new URL("../package.json", __sootsim_import_meta_url))
21054
+ ];
21055
+ for (const resolve9 of candidates) {
21056
+ try {
21057
+ const version = JSON.parse((0, import_node_fs5.readFileSync)(resolve9(), "utf8")).version;
21058
+ if (typeof version === "string" && version) {
21059
+ cached2 = version;
21060
+ return cached2;
21061
+ }
21062
+ } catch {
21063
+ }
21064
+ }
21065
+ cached2 = "0.0.0";
21066
+ return cached2;
21067
+ }
21068
+ var import_node_fs5, import_node_url2, import_meta, cached2;
21069
+ var init_cli_version = __esm({
21070
+ "src/cli-version.ts"() {
21071
+ "use strict";
21072
+ import_node_fs5 = require("node:fs");
21073
+ import_node_url2 = require("node:url");
21074
+ init_public_brand();
21075
+ import_meta = {};
21076
+ cached2 = null;
21077
+ }
21078
+ });
21079
+
20968
21080
  // ../contrast-runtime-delivery/src/product.ts
20969
21081
  function isValidRuntimeVersion(value) {
20970
21082
  return value.length <= 100 && /^[0-9A-Za-z][0-9A-Za-z._+-]*$/.test(value);
20971
21083
  }
20972
21084
  function listInstalledRuntimes(product) {
20973
21085
  try {
20974
- return import_node_fs5.default.readdirSync(product.runtimesDir(), { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort(compareSemver);
21086
+ return import_node_fs6.default.readdirSync(product.runtimesDir(), { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort(compareSemver);
20975
21087
  } catch {
20976
21088
  return [];
20977
21089
  }
20978
21090
  }
20979
21091
  function runtimeTarballCacheName(product, version) {
20980
- return import_node_path5.default.posix.basename(product.tarballPath(version));
21092
+ return import_node_path6.default.posix.basename(product.tarballPath(version));
20981
21093
  }
20982
21094
  function compareSemver(a, b) {
20983
21095
  const parse2 = (v) => {
@@ -21000,12 +21112,12 @@ function compareSemver(a, b) {
21000
21112
  if (!bp) return -1;
21001
21113
  return ap < bp ? -1 : 1;
21002
21114
  }
21003
- var import_node_fs5, import_node_path5;
21115
+ var import_node_fs6, import_node_path6;
21004
21116
  var init_product = __esm({
21005
21117
  "../contrast-runtime-delivery/src/product.ts"() {
21006
21118
  "use strict";
21007
- import_node_fs5 = __toESM(require("node:fs"), 1);
21008
- import_node_path5 = __toESM(require("node:path"), 1);
21119
+ import_node_fs6 = __toESM(require("node:fs"), 1);
21120
+ import_node_path6 = __toESM(require("node:path"), 1);
21009
21121
  }
21010
21122
  });
21011
21123
 
@@ -21014,7 +21126,7 @@ function createRuntimeDelivery(product) {
21014
21126
  function readConfig() {
21015
21127
  try {
21016
21128
  const parsed = JSON.parse(
21017
- import_node_fs6.default.readFileSync(product.configFile(), "utf8")
21129
+ import_node_fs7.default.readFileSync(product.configFile(), "utf8")
21018
21130
  );
21019
21131
  return parsed && typeof parsed === "object" ? parsed : {};
21020
21132
  } catch {
@@ -21087,7 +21199,7 @@ function createRuntimeDelivery(product) {
21087
21199
  const setActive = opts.setActive !== false;
21088
21200
  const protectVersions = [version, ...opts.protectVersions ?? []];
21089
21201
  pruneLocalRetention({ protectVersions });
21090
- if (!opts.force && import_node_fs6.default.existsSync(import_node_path6.default.join(destDir, product.installedMarker))) {
21202
+ if (!opts.force && import_node_fs7.default.existsSync(import_node_path7.default.join(destDir, product.installedMarker))) {
21091
21203
  if (setActive) product.writeActiveRuntime(version);
21092
21204
  pruneLocalRetention({ protectVersions });
21093
21205
  return {
@@ -21101,18 +21213,18 @@ function createRuntimeDelivery(product) {
21101
21213
  };
21102
21214
  }
21103
21215
  const tarUrl = entry.tarball || tarballUrl(version, cdnOrigin);
21104
- const downloadDir = import_node_fs6.default.mkdtempSync(
21105
- import_node_path6.default.join(import_node_os2.default.tmpdir(), `${product.id}-runtime-download-`)
21216
+ const downloadDir = import_node_fs7.default.mkdtempSync(
21217
+ import_node_path7.default.join(import_node_os2.default.tmpdir(), `${product.id}-runtime-download-`)
21106
21218
  );
21107
- const runtimeArchivePath = import_node_path6.default.join(
21219
+ const runtimeArchivePath = import_node_path7.default.join(
21108
21220
  downloadDir,
21109
21221
  runtimeTarballCacheName(product, version)
21110
21222
  );
21111
- const tmpDir = import_node_path6.default.join(
21112
- import_node_path6.default.dirname(destDir),
21223
+ const tmpDir = import_node_path7.default.join(
21224
+ import_node_path7.default.dirname(destDir),
21113
21225
  `.installing-${version}-${process.pid}`
21114
21226
  );
21115
- import_node_fs6.default.rmSync(tmpDir, { recursive: true, force: true });
21227
+ import_node_fs7.default.rmSync(tmpDir, { recursive: true, force: true });
21116
21228
  try {
21117
21229
  if (!opts.quiet)
21118
21230
  process.stderr.write(`${product.id}: downloading runtime ${version}\u2026
@@ -21127,20 +21239,20 @@ function createRuntimeDelivery(product) {
21127
21239
  `sha256 mismatch for runtime ${version}: expected ${entry.sha256}, actual ${actualSha}`
21128
21240
  );
21129
21241
  }
21130
- import_node_fs6.default.mkdirSync(tmpDir, { recursive: true });
21242
+ import_node_fs7.default.mkdirSync(tmpDir, { recursive: true });
21131
21243
  await extractTarball(runtimeArchivePath, tmpDir);
21132
- if (!import_node_fs6.default.existsSync(import_node_path6.default.join(tmpDir, product.installedMarker))) {
21244
+ if (!import_node_fs7.default.existsSync(import_node_path7.default.join(tmpDir, product.installedMarker))) {
21133
21245
  throw new Error(
21134
21246
  `extracted tarball for runtime ${version} is missing ${product.installedMarker}`
21135
21247
  );
21136
21248
  }
21137
- import_node_fs6.default.rmSync(destDir, { recursive: true, force: true });
21138
- import_node_fs6.default.renameSync(tmpDir, destDir);
21249
+ import_node_fs7.default.rmSync(destDir, { recursive: true, force: true });
21250
+ import_node_fs7.default.renameSync(tmpDir, destDir);
21139
21251
  } catch (err) {
21140
- import_node_fs6.default.rmSync(tmpDir, { recursive: true, force: true });
21252
+ import_node_fs7.default.rmSync(tmpDir, { recursive: true, force: true });
21141
21253
  throw err;
21142
21254
  } finally {
21143
- import_node_fs6.default.rmSync(downloadDir, { recursive: true, force: true });
21255
+ import_node_fs7.default.rmSync(downloadDir, { recursive: true, force: true });
21144
21256
  }
21145
21257
  if (setActive) product.writeActiveRuntime(version);
21146
21258
  pruneLocalRetention({ protectVersions });
@@ -21162,8 +21274,8 @@ function createRuntimeDelivery(product) {
21162
21274
  const pin = resolvePin();
21163
21275
  if (pin) {
21164
21276
  const activeVersion2 = product.readActiveRuntime();
21165
- const pinInstalled = import_node_fs6.default.existsSync(
21166
- import_node_path6.default.join(product.runtimeDir(pin), product.installedMarker)
21277
+ const pinInstalled = import_node_fs7.default.existsSync(
21278
+ import_node_path7.default.join(product.runtimeDir(pin), product.installedMarker)
21167
21279
  );
21168
21280
  if (activeVersion2 === pin && pinInstalled) {
21169
21281
  return {
@@ -21212,7 +21324,7 @@ function createRuntimeDelivery(product) {
21212
21324
  }
21213
21325
  const activeVersion = product.readActiveRuntime();
21214
21326
  const activeDir = activeVersion ? product.runtimeDir(activeVersion) : null;
21215
- const activeInstalled = activeDir ? import_node_fs6.default.existsSync(import_node_path6.default.join(activeDir, product.installedMarker)) : false;
21327
+ const activeInstalled = activeDir ? import_node_fs7.default.existsSync(import_node_path7.default.join(activeDir, product.installedMarker)) : false;
21216
21328
  const shouldInstall = !activeVersion || !activeInstalled || compareSemver(latestVersion, activeVersion) > 0;
21217
21329
  if (!shouldInstall) {
21218
21330
  return {
@@ -21287,12 +21399,12 @@ function createRuntimeDelivery(product) {
21287
21399
  let removeCacheFiles = [];
21288
21400
  if (markerOffset >= 0) {
21289
21401
  try {
21290
- removeCacheFiles = import_node_fs6.default.readdirSync(product.cacheDir(), { withFileTypes: true }).filter((entry) => {
21402
+ removeCacheFiles = import_node_fs7.default.readdirSync(product.cacheDir(), { withFileTypes: true }).filter((entry) => {
21291
21403
  if (!entry.isFile()) return false;
21292
21404
  const complete = entry.name.startsWith(cachePrefix) && entry.name.endsWith(cacheSuffix);
21293
21405
  const partial = entry.name.startsWith(cachePrefix) && entry.name.endsWith(`${cacheSuffix}.partial`);
21294
21406
  return complete || partial;
21295
- }).map((entry) => import_node_path6.default.join(product.cacheDir(), entry.name)).sort();
21407
+ }).map((entry) => import_node_path7.default.join(product.cacheDir(), entry.name)).sort();
21296
21408
  } catch {
21297
21409
  }
21298
21410
  }
@@ -21307,10 +21419,10 @@ function createRuntimeDelivery(product) {
21307
21419
  function pruneLocalRetention(opts = {}) {
21308
21420
  const plan = planLocalRetention(opts);
21309
21421
  for (const version of plan.removeVersions) {
21310
- import_node_fs6.default.rmSync(product.runtimeDir(version), { recursive: true, force: true });
21422
+ import_node_fs7.default.rmSync(product.runtimeDir(version), { recursive: true, force: true });
21311
21423
  }
21312
21424
  for (const cacheFile of plan.removeCacheFiles) {
21313
- import_node_fs6.default.rmSync(cacheFile, { force: true });
21425
+ import_node_fs7.default.rmSync(cacheFile, { force: true });
21314
21426
  }
21315
21427
  return plan;
21316
21428
  }
@@ -21336,17 +21448,17 @@ async function downloadToFile(url, destPath) {
21336
21448
  if (!res.ok || !res.body) {
21337
21449
  throw new Error(`download failed: ${res.status} ${res.statusText} (${url})`);
21338
21450
  }
21339
- import_node_fs6.default.mkdirSync(import_node_path6.default.dirname(destPath), { recursive: true });
21451
+ import_node_fs7.default.mkdirSync(import_node_path7.default.dirname(destPath), { recursive: true });
21340
21452
  const tmp = `${destPath}.partial`;
21341
21453
  try {
21342
21454
  await (0, import_promises3.pipeline)(
21343
21455
  import_node_stream.Readable.fromWeb(res.body),
21344
- import_node_fs6.default.createWriteStream(tmp)
21456
+ import_node_fs7.default.createWriteStream(tmp)
21345
21457
  );
21346
- import_node_fs6.default.renameSync(tmp, destPath);
21458
+ import_node_fs7.default.renameSync(tmp, destPath);
21347
21459
  } catch (err) {
21348
21460
  try {
21349
- import_node_fs6.default.unlinkSync(tmp);
21461
+ import_node_fs7.default.unlinkSync(tmp);
21350
21462
  } catch {
21351
21463
  }
21352
21464
  throw err;
@@ -21355,7 +21467,7 @@ async function downloadToFile(url, destPath) {
21355
21467
  function sha256File(filePath) {
21356
21468
  return new Promise((resolve9, reject) => {
21357
21469
  const hash = import_node_crypto2.default.createHash("sha256");
21358
- const stream = import_node_fs6.default.createReadStream(filePath);
21470
+ const stream = import_node_fs7.default.createReadStream(filePath);
21359
21471
  stream.on("data", (chunk) => hash.update(chunk));
21360
21472
  stream.on("error", reject);
21361
21473
  stream.on("end", () => resolve9(hash.digest("hex")));
@@ -21373,15 +21485,15 @@ function extractTarball(tarPath, destDir) {
21373
21485
  });
21374
21486
  });
21375
21487
  }
21376
- var import_node_child_process3, import_node_crypto2, import_node_fs6, import_node_os2, import_node_path6, import_node_stream, import_promises3, DEFAULT_LOCAL_RUNTIME_RETENTION_COUNT;
21488
+ var import_node_child_process3, import_node_crypto2, import_node_fs7, import_node_os2, import_node_path7, import_node_stream, import_promises3, DEFAULT_LOCAL_RUNTIME_RETENTION_COUNT;
21377
21489
  var init_src = __esm({
21378
21490
  "../contrast-runtime-delivery/src/index.ts"() {
21379
21491
  "use strict";
21380
21492
  import_node_child_process3 = require("node:child_process");
21381
21493
  import_node_crypto2 = __toESM(require("node:crypto"), 1);
21382
- import_node_fs6 = __toESM(require("node:fs"), 1);
21494
+ import_node_fs7 = __toESM(require("node:fs"), 1);
21383
21495
  import_node_os2 = __toESM(require("node:os"), 1);
21384
- import_node_path6 = __toESM(require("node:path"), 1);
21496
+ import_node_path7 = __toESM(require("node:path"), 1);
21385
21497
  import_node_stream = require("node:stream");
21386
21498
  import_promises3 = require("node:stream/promises");
21387
21499
  init_product();
@@ -21557,14 +21669,14 @@ var init_standalone = __esm({
21557
21669
  // cli/self-invocation.ts
21558
21670
  function sourceEntryPath() {
21559
21671
  try {
21560
- const candidate = (0, import_node_path7.join)((0, import_node_path7.dirname)((0, import_node_url3.fileURLToPath)(__sootsim_import_meta_url)), "bin.ts");
21561
- return (0, import_node_fs7.existsSync)(candidate) ? candidate : null;
21672
+ const candidate = (0, import_node_path8.join)((0, import_node_path8.dirname)((0, import_node_url3.fileURLToPath)(__sootsim_import_meta_url)), "bin.ts");
21673
+ return (0, import_node_fs8.existsSync)(candidate) ? candidate : null;
21562
21674
  } catch {
21563
21675
  return null;
21564
21676
  }
21565
21677
  }
21566
- function isCliEntry(path21) {
21567
- const name2 = (0, import_node_path7.basename)(path21);
21678
+ function isCliEntry(path22) {
21679
+ const name2 = (0, import_node_path8.basename)(path22);
21568
21680
  return name2 === "bin.ts" || name2 === "bin.js";
21569
21681
  }
21570
21682
  function rnxSelfInvocation() {
@@ -21582,12 +21694,12 @@ function rnxSelfInvocation() {
21582
21694
  }
21583
21695
  return { executable: process.execPath, prefixArgs: [entry] };
21584
21696
  }
21585
- var import_node_fs7, import_node_path7, import_node_url3;
21697
+ var import_node_fs8, import_node_path8, import_node_url3;
21586
21698
  var init_self_invocation = __esm({
21587
21699
  "cli/self-invocation.ts"() {
21588
21700
  "use strict";
21589
- import_node_fs7 = require("node:fs");
21590
- import_node_path7 = require("node:path");
21701
+ import_node_fs8 = require("node:fs");
21702
+ import_node_path8 = require("node:path");
21591
21703
  import_node_url3 = require("node:url");
21592
21704
  init_public_brand();
21593
21705
  init_standalone();
@@ -21655,7 +21767,7 @@ function getUserDataDir() {
21655
21767
  return userDataDir();
21656
21768
  }
21657
21769
  function storeFile() {
21658
- return import_node_path8.default.join(userDataDir(), "attached-projects.json");
21770
+ return import_node_path9.default.join(userDataDir(), "attached-projects.json");
21659
21771
  }
21660
21772
  function cloneEmpty() {
21661
21773
  return {
@@ -21669,7 +21781,7 @@ function loadStore() {
21669
21781
  const file = storeFile();
21670
21782
  let raw;
21671
21783
  try {
21672
- raw = import_node_fs8.default.readFileSync(file, "utf8");
21784
+ raw = import_node_fs9.default.readFileSync(file, "utf8");
21673
21785
  } catch (err) {
21674
21786
  if (err.code === "ENOENT") return cloneEmpty();
21675
21787
  throw err;
@@ -21686,7 +21798,7 @@ function loadStore() {
21686
21798
  } catch (err) {
21687
21799
  const quarantine = `${file}.corrupt-${Date.now()}`;
21688
21800
  try {
21689
- import_node_fs8.default.renameSync(file, quarantine);
21801
+ import_node_fs9.default.renameSync(file, quarantine);
21690
21802
  console.warn(
21691
21803
  `[sootsim] attached-projects.json was unparseable; quarantined to ${quarantine}. original error: ${err.message}`
21692
21804
  );
@@ -21697,16 +21809,16 @@ function loadStore() {
21697
21809
  }
21698
21810
  function writeStore(store) {
21699
21811
  const file = storeFile();
21700
- import_node_fs8.default.mkdirSync(import_node_path8.default.dirname(file), { recursive: true });
21812
+ import_node_fs9.default.mkdirSync(import_node_path9.default.dirname(file), { recursive: true });
21701
21813
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
21702
- const fd = import_node_fs8.default.openSync(tmp, "w", 384);
21814
+ const fd = import_node_fs9.default.openSync(tmp, "w", 384);
21703
21815
  try {
21704
- import_node_fs8.default.writeFileSync(fd, JSON.stringify(store, null, 2));
21705
- import_node_fs8.default.fsyncSync(fd);
21816
+ import_node_fs9.default.writeFileSync(fd, JSON.stringify(store, null, 2));
21817
+ import_node_fs9.default.fsyncSync(fd);
21706
21818
  } finally {
21707
- import_node_fs8.default.closeSync(fd);
21819
+ import_node_fs9.default.closeSync(fd);
21708
21820
  }
21709
- import_node_fs8.default.renameSync(tmp, file);
21821
+ import_node_fs9.default.renameSync(tmp, file);
21710
21822
  }
21711
21823
  function mutateStore(fn) {
21712
21824
  const store = loadStore();
@@ -21715,13 +21827,13 @@ function mutateStore(fn) {
21715
21827
  return store;
21716
21828
  }
21717
21829
  function projectIdForCwd(cwd) {
21718
- return (0, import_node_crypto3.createHash)("sha256").update(import_node_path8.default.resolve(cwd)).digest("hex").slice(0, 16);
21830
+ return (0, import_node_crypto3.createHash)("sha256").update(import_node_path9.default.resolve(cwd)).digest("hex").slice(0, 16);
21719
21831
  }
21720
21832
  function newSessionId() {
21721
21833
  return `s_${(0, import_node_crypto3.randomBytes)(10).toString("hex")}`;
21722
21834
  }
21723
21835
  function upsertProject(input) {
21724
- const cwd = import_node_path8.default.resolve(input.cwd);
21836
+ const cwd = import_node_path9.default.resolve(input.cwd);
21725
21837
  const id = projectIdForCwd(cwd);
21726
21838
  let result;
21727
21839
  mutateStore((store) => {
@@ -21747,7 +21859,7 @@ function upsertProject(input) {
21747
21859
  const now = Date.now();
21748
21860
  const created = {
21749
21861
  id,
21750
- name: input.name ?? import_node_path8.default.basename(cwd),
21862
+ name: input.name ?? import_node_path9.default.basename(cwd),
21751
21863
  cwd,
21752
21864
  repoRoot: input.repoRoot,
21753
21865
  sourceRoots: input.sourceRoots ?? [cwd],
@@ -21892,8 +22004,8 @@ async function seedFromDemoAppRegistry() {
21892
22004
  if (apps.length === 0) return;
21893
22005
  mutateStore((store) => {
21894
22006
  for (const app of apps) {
21895
- if (!import_node_fs8.default.existsSync(app.dir)) continue;
21896
- const cwd = import_node_path8.default.resolve(app.dir);
22007
+ if (!import_node_fs9.default.existsSync(app.dir)) continue;
22008
+ const cwd = import_node_path9.default.resolve(app.dir);
21897
22009
  const id = projectIdForCwd(cwd);
21898
22010
  if (store.attachedProjects.some((p) => p.id === id)) continue;
21899
22011
  const now = Date.now();
@@ -21914,13 +22026,13 @@ async function seedFromDemoAppRegistry() {
21914
22026
  }
21915
22027
  });
21916
22028
  }
21917
- var import_node_crypto3, import_node_fs8, import_node_path8, overrideDir, COST_HISTORY_MAX_AGE_MS;
22029
+ var import_node_crypto3, import_node_fs9, import_node_path9, overrideDir, COST_HISTORY_MAX_AGE_MS;
21918
22030
  var init_attached_projects = __esm({
21919
22031
  "src/attached-projects.ts"() {
21920
22032
  "use strict";
21921
22033
  import_node_crypto3 = require("node:crypto");
21922
- import_node_fs8 = __toESM(require("node:fs"), 1);
21923
- import_node_path8 = __toESM(require("node:path"), 1);
22034
+ import_node_fs9 = __toESM(require("node:fs"), 1);
22035
+ import_node_path9 = __toESM(require("node:path"), 1);
21924
22036
  init_home_paths();
21925
22037
  overrideDir = null;
21926
22038
  COST_HISTORY_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
@@ -21929,16 +22041,16 @@ var init_attached_projects = __esm({
21929
22041
 
21930
22042
  // src/agent-sessions.ts
21931
22043
  function sessionDir(sessionId) {
21932
- return import_node_path9.default.join(getUserDataDir(), "sessions", sessionId);
22044
+ return import_node_path10.default.join(getUserDataDir(), "sessions", sessionId);
21933
22045
  }
21934
22046
  function promptFifoPath(sessionId) {
21935
- return import_node_path9.default.join(sessionDir(sessionId), "prompt.in");
22047
+ return import_node_path10.default.join(sessionDir(sessionId), "prompt.in");
21936
22048
  }
21937
22049
  function eventsFifoPath(sessionId) {
21938
- return import_node_path9.default.join(sessionDir(sessionId), "events.out");
22050
+ return import_node_path10.default.join(sessionDir(sessionId), "events.out");
21939
22051
  }
21940
22052
  function transcriptPath(sessionId) {
21941
- return import_node_path9.default.join(getUserDataDir(), "transcripts", `${sessionId}.log`);
22053
+ return import_node_path10.default.join(getUserDataDir(), "transcripts", `${sessionId}.log`);
21942
22054
  }
21943
22055
  function pidIsAlive(pid, sessionId) {
21944
22056
  if (!pid) return false;
@@ -21948,7 +22060,7 @@ function pidIsAlive(pid, sessionId) {
21948
22060
  return false;
21949
22061
  }
21950
22062
  if (sessionId) {
21951
- if (!import_node_fs9.default.existsSync(sessionDir(sessionId))) return false;
22063
+ if (!import_node_fs10.default.existsSync(sessionDir(sessionId))) return false;
21952
22064
  }
21953
22065
  return true;
21954
22066
  }
@@ -21961,11 +22073,11 @@ function resolveSootsimInvocation() {
21961
22073
  const resourcesPath = process.resourcesPath;
21962
22074
  if (resourcesPath) {
21963
22075
  const candidates = [
21964
- import_node_path9.default.join(resourcesPath, "bin", "sootsim"),
21965
- import_node_path9.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
22076
+ import_node_path10.default.join(resourcesPath, "bin", "sootsim"),
22077
+ import_node_path10.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
21966
22078
  ];
21967
22079
  for (const c of candidates) {
21968
- if (import_node_fs9.default.existsSync(c)) return { executable: c, prefixArgs: [] };
22080
+ if (import_node_fs10.default.existsSync(c)) return { executable: c, prefixArgs: [] };
21969
22081
  }
21970
22082
  }
21971
22083
  }
@@ -21984,15 +22096,15 @@ function tryWorkspaceSootsim() {
21984
22096
  const sootsimDir = resolveSootsimPackageDir();
21985
22097
  if (!sootsimDir) return null;
21986
22098
  const binaryName = `peach-${process.platform === "win32" ? "windows" : process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`;
21987
- const distBinary = import_node_path9.default.join(sootsimDir, "dist-bin", binaryName);
21988
- if (import_node_fs9.default.existsSync(distBinary)) return { executable: distBinary, prefixArgs: [] };
21989
- const distBin = import_node_path9.default.join(sootsimDir, "dist-cli", "bin.js");
21990
- if (import_node_fs9.default.existsSync(distBin)) {
22099
+ const distBinary = import_node_path10.default.join(sootsimDir, "dist-bin", binaryName);
22100
+ if (import_node_fs10.default.existsSync(distBinary)) return { executable: distBinary, prefixArgs: [] };
22101
+ const distBin = import_node_path10.default.join(sootsimDir, "dist-cli", "bin.js");
22102
+ if (import_node_fs10.default.existsSync(distBin)) {
21991
22103
  try {
21992
- const src = import_node_path9.default.join(sootsimDir, "cli", "commands", "agent-wrapper.ts");
21993
- if (import_node_fs9.default.existsSync(src)) {
21994
- const srcMtime = import_node_fs9.default.statSync(src).mtimeMs;
21995
- const buildMtime = import_node_fs9.default.statSync(distBin).mtimeMs;
22104
+ const src = import_node_path10.default.join(sootsimDir, "cli", "commands", "agent-wrapper.ts");
22105
+ if (import_node_fs10.default.existsSync(src)) {
22106
+ const srcMtime = import_node_fs10.default.statSync(src).mtimeMs;
22107
+ const buildMtime = import_node_fs10.default.statSync(distBin).mtimeMs;
21996
22108
  if (buildMtime < srcMtime) {
21997
22109
  console.warn(
21998
22110
  `[peach] dist-cli/bin.js is older than agent-wrapper.ts \u2014 rebuild with \`bun run --cwd packages/peachsim build:cli\`.`
@@ -22011,22 +22123,22 @@ function tryWorkspaceSootsim() {
22011
22123
  function resolveSootsimPackageDir() {
22012
22124
  try {
22013
22125
  const resolved = require.resolve(`${rnxPublicBrand.packageName}/package.json`);
22014
- return import_node_path9.default.dirname(resolved);
22126
+ return import_node_path10.default.dirname(resolved);
22015
22127
  } catch {
22016
22128
  }
22017
22129
  const here = fileFromImportMeta();
22018
22130
  if (!here) return null;
22019
- let cur = import_node_path9.default.dirname(here);
22131
+ let cur = import_node_path10.default.dirname(here);
22020
22132
  for (let i = 0; i < 8; i++) {
22021
- const pkg = import_node_path9.default.join(cur, "package.json");
22133
+ const pkg = import_node_path10.default.join(cur, "package.json");
22022
22134
  try {
22023
- if (import_node_fs9.default.existsSync(pkg)) {
22024
- const parsed = JSON.parse(import_node_fs9.default.readFileSync(pkg, "utf8"));
22135
+ if (import_node_fs10.default.existsSync(pkg)) {
22136
+ const parsed = JSON.parse(import_node_fs10.default.readFileSync(pkg, "utf8"));
22025
22137
  if (parsed.name === rnxPublicBrand.packageName) return cur;
22026
22138
  }
22027
22139
  } catch {
22028
22140
  }
22029
- const parent = import_node_path9.default.dirname(cur);
22141
+ const parent = import_node_path10.default.dirname(cur);
22030
22142
  if (parent === cur) break;
22031
22143
  cur = parent;
22032
22144
  }
@@ -22042,28 +22154,28 @@ function fileFromImportMeta() {
22042
22154
  }
22043
22155
  }
22044
22156
  async function withStartLock(projectId, provider, fn) {
22045
- const lockDir = import_node_path9.default.join(getUserDataDir(), "locks");
22046
- import_node_fs9.default.mkdirSync(lockDir, { recursive: true });
22157
+ const lockDir = import_node_path10.default.join(getUserDataDir(), "locks");
22158
+ import_node_fs10.default.mkdirSync(lockDir, { recursive: true });
22047
22159
  try {
22048
- import_node_fs9.default.chmodSync(lockDir, 448);
22160
+ import_node_fs10.default.chmodSync(lockDir, 448);
22049
22161
  } catch {
22050
22162
  }
22051
- const lockPath = import_node_path9.default.join(lockDir, `start-${projectId}-${provider}.lock`);
22163
+ const lockPath = import_node_path10.default.join(lockDir, `start-${projectId}-${provider}.lock`);
22052
22164
  const deadline = Date.now() + 4e3;
22053
22165
  let fd = null;
22054
22166
  while (fd === null) {
22055
22167
  try {
22056
- fd = import_node_fs9.default.openSync(
22168
+ fd = import_node_fs10.default.openSync(
22057
22169
  lockPath,
22058
- import_node_fs9.constants.O_WRONLY | import_node_fs9.constants.O_CREAT | import_node_fs9.constants.O_EXCL,
22170
+ import_node_fs10.constants.O_WRONLY | import_node_fs10.constants.O_CREAT | import_node_fs10.constants.O_EXCL,
22059
22171
  384
22060
22172
  );
22061
22173
  } catch (err) {
22062
22174
  if (err.code !== "EEXIST") throw err;
22063
22175
  try {
22064
- const stale = Number(import_node_fs9.default.readFileSync(lockPath, "utf8").trim());
22176
+ const stale = Number(import_node_fs10.default.readFileSync(lockPath, "utf8").trim());
22065
22177
  if (stale && !isProcessAlive(stale)) {
22066
- import_node_fs9.default.unlinkSync(lockPath);
22178
+ import_node_fs10.default.unlinkSync(lockPath);
22067
22179
  continue;
22068
22180
  }
22069
22181
  } catch {
@@ -22077,15 +22189,15 @@ async function withStartLock(projectId, provider, fn) {
22077
22189
  }
22078
22190
  }
22079
22191
  try {
22080
- import_node_fs9.default.writeFileSync(fd, String(process.pid));
22192
+ import_node_fs10.default.writeFileSync(fd, String(process.pid));
22081
22193
  return await fn();
22082
22194
  } finally {
22083
22195
  try {
22084
- import_node_fs9.default.closeSync(fd);
22196
+ import_node_fs10.default.closeSync(fd);
22085
22197
  } catch {
22086
22198
  }
22087
22199
  try {
22088
- import_node_fs9.default.unlinkSync(lockPath);
22200
+ import_node_fs10.default.unlinkSync(lockPath);
22089
22201
  } catch {
22090
22202
  }
22091
22203
  }
@@ -22099,25 +22211,25 @@ function isProcessAlive(pid) {
22099
22211
  }
22100
22212
  }
22101
22213
  function mkfifoSync(p) {
22102
- const parent = import_node_path9.default.dirname(p);
22103
- import_node_fs9.default.mkdirSync(parent, { recursive: true });
22214
+ const parent = import_node_path10.default.dirname(p);
22215
+ import_node_fs10.default.mkdirSync(parent, { recursive: true });
22104
22216
  try {
22105
- import_node_fs9.default.chmodSync(parent, 448);
22217
+ import_node_fs10.default.chmodSync(parent, 448);
22106
22218
  } catch {
22107
22219
  }
22108
- if (import_node_fs9.default.existsSync(p)) {
22220
+ if (import_node_fs10.default.existsSync(p)) {
22109
22221
  try {
22110
- const stat2 = import_node_fs9.default.statSync(p);
22222
+ const stat2 = import_node_fs10.default.statSync(p);
22111
22223
  if (stat2.isFIFO()) {
22112
22224
  try {
22113
- import_node_fs9.default.chmodSync(p, 384);
22225
+ import_node_fs10.default.chmodSync(p, 384);
22114
22226
  } catch {
22115
22227
  }
22116
22228
  return;
22117
22229
  }
22118
- import_node_fs9.default.unlinkSync(p);
22230
+ import_node_fs10.default.unlinkSync(p);
22119
22231
  } catch {
22120
- import_node_fs9.default.unlinkSync(p);
22232
+ import_node_fs10.default.unlinkSync(p);
22121
22233
  }
22122
22234
  }
22123
22235
  const result = (0, import_node_child_process4.spawnSync)("mkfifo", ["-m", "600", p]);
@@ -22157,10 +22269,10 @@ async function startSession(opts) {
22157
22269
  const transcript = transcriptPath(session.id);
22158
22270
  mkfifoSync(promptIn);
22159
22271
  mkfifoSync(eventsOut);
22160
- const transcriptDir = import_node_path9.default.dirname(transcript);
22161
- import_node_fs9.default.mkdirSync(transcriptDir, { recursive: true });
22272
+ const transcriptDir = import_node_path10.default.dirname(transcript);
22273
+ import_node_fs10.default.mkdirSync(transcriptDir, { recursive: true });
22162
22274
  try {
22163
- import_node_fs9.default.chmodSync(transcriptDir, 448);
22275
+ import_node_fs10.default.chmodSync(transcriptDir, 448);
22164
22276
  } catch {
22165
22277
  }
22166
22278
  const { executable, prefixArgs } = resolveSootsimInvocation();
@@ -22211,7 +22323,7 @@ async function startSession(opts) {
22211
22323
  }
22212
22324
  }
22213
22325
  try {
22214
- import_node_fs9.default.rmSync(sessionDir(session.id), { recursive: true, force: true });
22326
+ import_node_fs10.default.rmSync(sessionDir(session.id), { recursive: true, force: true });
22215
22327
  } catch {
22216
22328
  }
22217
22329
  updateSessionStatus(session.id, { status: "ended" });
@@ -22239,18 +22351,18 @@ async function sendPrompt(sessionId, prompt) {
22239
22351
  );
22240
22352
  }
22241
22353
  const fifo = promptFifoPath(sessionId);
22242
- if (!import_node_fs9.default.existsSync(fifo)) {
22354
+ if (!import_node_fs10.default.existsSync(fifo)) {
22243
22355
  throw new AgentSessionError("NO_FIFO", `prompt FIFO missing: ${fifo}`);
22244
22356
  }
22245
- const fd = import_node_fs9.default.openSync(fifo, import_node_fs9.constants.O_WRONLY);
22357
+ const fd = import_node_fs10.default.openSync(fifo, import_node_fs10.constants.O_WRONLY);
22246
22358
  try {
22247
22359
  const wireText = encodeAgentPromptEnvelope(prompt);
22248
22360
  if (!wireText) {
22249
22361
  throw new AgentSessionError("EMPTY_PROMPT", "prompt text is empty");
22250
22362
  }
22251
- import_node_fs9.default.writeSync(fd, wireText + "\n");
22363
+ import_node_fs10.default.writeSync(fd, wireText + "\n");
22252
22364
  } finally {
22253
- import_node_fs9.default.closeSync(fd);
22365
+ import_node_fs10.default.closeSync(fd);
22254
22366
  }
22255
22367
  updateSessionStatus(sessionId, {
22256
22368
  lastPrompt: prompt.displayText ?? prompt.text,
@@ -22272,7 +22384,7 @@ async function endSession(sessionId) {
22272
22384
  const base = getUserDataDir();
22273
22385
  if (dir.startsWith(base)) {
22274
22386
  try {
22275
- import_node_fs9.default.rmSync(dir, { recursive: true, force: true });
22387
+ import_node_fs10.default.rmSync(dir, { recursive: true, force: true });
22276
22388
  } catch {
22277
22389
  }
22278
22390
  }
@@ -22280,11 +22392,11 @@ async function endSession(sessionId) {
22280
22392
  }
22281
22393
  function subscribeEvents(sessionId, onEvent) {
22282
22394
  const fifo = eventsFifoPath(sessionId);
22283
- if (!import_node_fs9.default.existsSync(fifo)) {
22395
+ if (!import_node_fs10.default.existsSync(fifo)) {
22284
22396
  throw new AgentSessionError("NO_FIFO", `events FIFO missing: ${fifo}`);
22285
22397
  }
22286
- const fd = import_node_fs9.default.openSync(fifo, import_node_fs9.constants.O_RDWR);
22287
- const stream = import_node_fs9.default.createReadStream("", { fd, autoClose: true });
22398
+ const fd = import_node_fs10.default.openSync(fifo, import_node_fs10.constants.O_RDWR);
22399
+ const stream = import_node_fs10.default.createReadStream("", { fd, autoClose: true });
22288
22400
  const rl = import_node_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
22289
22401
  rl.on("line", (line) => {
22290
22402
  const event = parseAgentEventLine(line);
@@ -22295,7 +22407,7 @@ function subscribeEvents(sessionId, onEvent) {
22295
22407
  if (closed) return;
22296
22408
  closed = true;
22297
22409
  try {
22298
- import_node_fs9.default.writeSync(fd, "\n");
22410
+ import_node_fs10.default.writeSync(fd, "\n");
22299
22411
  } catch {
22300
22412
  }
22301
22413
  try {
@@ -22309,7 +22421,7 @@ function subscribeEvents(sessionId, onEvent) {
22309
22421
  };
22310
22422
  }
22311
22423
  async function waitForFirstEvent(fifo, predicate, timeoutMs) {
22312
- const fd = import_node_fs9.default.openSync(fifo, import_node_fs9.constants.O_RDWR | import_node_fs9.constants.O_NONBLOCK);
22424
+ const fd = import_node_fs10.default.openSync(fifo, import_node_fs10.constants.O_RDWR | import_node_fs10.constants.O_NONBLOCK);
22313
22425
  const buf = Buffer.alloc(8192);
22314
22426
  let leftover = "";
22315
22427
  const deadline = Date.now() + timeoutMs;
@@ -22317,7 +22429,7 @@ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
22317
22429
  while (Date.now() < deadline) {
22318
22430
  let n = 0;
22319
22431
  try {
22320
- n = import_node_fs9.default.readSync(fd, buf, 0, buf.length, null);
22432
+ n = import_node_fs10.default.readSync(fd, buf, 0, buf.length, null);
22321
22433
  } catch (err) {
22322
22434
  if (err.code !== "EAGAIN") throw err;
22323
22435
  n = 0;
@@ -22337,17 +22449,17 @@ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
22337
22449
  }
22338
22450
  return null;
22339
22451
  } finally {
22340
- import_node_fs9.default.closeSync(fd);
22452
+ import_node_fs10.default.closeSync(fd);
22341
22453
  }
22342
22454
  }
22343
- var import_node_child_process4, import_node_crypto4, import_node_fs9, import_node_path9, import_node_readline, AgentSessionError;
22455
+ var import_node_child_process4, import_node_crypto4, import_node_fs10, import_node_path10, import_node_readline, AgentSessionError;
22344
22456
  var init_agent_sessions = __esm({
22345
22457
  "src/agent-sessions.ts"() {
22346
22458
  "use strict";
22347
22459
  import_node_child_process4 = require("node:child_process");
22348
22460
  import_node_crypto4 = require("node:crypto");
22349
- import_node_fs9 = __toESM(require("node:fs"), 1);
22350
- import_node_path9 = __toESM(require("node:path"), 1);
22461
+ import_node_fs10 = __toESM(require("node:fs"), 1);
22462
+ import_node_path10 = __toESM(require("node:path"), 1);
22351
22463
  import_node_readline = __toESM(require("node:readline"), 1);
22352
22464
  init_self_invocation();
22353
22465
  init_standalone();
@@ -22378,13 +22490,13 @@ function mapFrameworkToProjectFramework(fw) {
22378
22490
  if (fw === "one" || fw === "vxrn") return "one";
22379
22491
  return "unknown";
22380
22492
  }
22381
- var import_node_crypto5, import_node_fs10, import_node_path10, WS_OPEN, SESSION_STATUS_PERSIST_DELAY_MS, AgentHost;
22493
+ var import_node_crypto5, import_node_fs11, import_node_path11, WS_OPEN, SESSION_STATUS_PERSIST_DELAY_MS, AgentHost;
22382
22494
  var init_agent_host = __esm({
22383
22495
  "src/host/agent-host.ts"() {
22384
22496
  "use strict";
22385
22497
  import_node_crypto5 = require("node:crypto");
22386
- import_node_fs10 = __toESM(require("node:fs"), 1);
22387
- import_node_path10 = __toESM(require("node:path"), 1);
22498
+ import_node_fs11 = __toESM(require("node:fs"), 1);
22499
+ import_node_path11 = __toESM(require("node:path"), 1);
22388
22500
  init_dev_server_scanner();
22389
22501
  init_agent_sessions();
22390
22502
  init_attached_projects();
@@ -22630,7 +22742,7 @@ var init_agent_host = __esm({
22630
22742
  );
22631
22743
  const project = upsertProject({
22632
22744
  cwd: match.cwd,
22633
- name: match.projectName ?? import_node_path10.default.basename(match.cwd),
22745
+ name: match.projectName ?? import_node_path11.default.basename(match.cwd),
22634
22746
  preferredProvider: input.provider ?? existing?.preferredProvider,
22635
22747
  sourceRoots: existing?.sourceRoots ?? [match.cwd],
22636
22748
  knownBundleUrls,
@@ -22641,18 +22753,18 @@ var init_agent_host = __esm({
22641
22753
  }
22642
22754
  getTranscript(sessionId) {
22643
22755
  const p = transcriptPath(sessionId);
22644
- if (!import_node_fs10.default.existsSync(p)) {
22756
+ if (!import_node_fs11.default.existsSync(p)) {
22645
22757
  return { error: "transcript not found", code: "NO_TRANSCRIPT" };
22646
22758
  }
22647
- return import_node_fs10.default.readFileSync(p, "utf8");
22759
+ return import_node_fs11.default.readFileSync(p, "utf8");
22648
22760
  }
22649
22761
  getPaths() {
22650
22762
  const dir = getUserDataDir();
22651
22763
  return {
22652
22764
  userDataDir: dir,
22653
- storeFile: import_node_path10.default.join(dir, "attached-projects.json"),
22654
- sessionsDir: import_node_path10.default.join(dir, "sessions"),
22655
- transcriptsDir: import_node_path10.default.join(dir, "transcripts")
22765
+ storeFile: import_node_path11.default.join(dir, "attached-projects.json"),
22766
+ sessionsDir: import_node_path11.default.join(dir, "sessions"),
22767
+ transcriptsDir: import_node_path11.default.join(dir, "transcripts")
22656
22768
  };
22657
22769
  }
22658
22770
  // --- subscription management ---
@@ -23502,23 +23614,23 @@ function looksBinary(filePath, size) {
23502
23614
  if (size === 0) return false;
23503
23615
  let fd;
23504
23616
  try {
23505
- fd = import_node_fs11.default.openSync(filePath, "r");
23617
+ fd = import_node_fs12.default.openSync(filePath, "r");
23506
23618
  const buffer = Buffer.alloc(Math.min(BINARY_SNIFF_BYTES, size));
23507
- const read = import_node_fs11.default.readSync(fd, buffer, 0, buffer.length, 0);
23619
+ const read = import_node_fs12.default.readSync(fd, buffer, 0, buffer.length, 0);
23508
23620
  return buffer.subarray(0, read).includes(0);
23509
23621
  } catch {
23510
23622
  return false;
23511
23623
  } finally {
23512
- if (fd !== void 0) import_node_fs11.default.closeSync(fd);
23624
+ if (fd !== void 0) import_node_fs12.default.closeSync(fd);
23513
23625
  }
23514
23626
  }
23515
- var import_node_child_process5, import_node_fs11, import_node_path11, CheckoutRequiresGitError, BINARY_SNIFF_BYTES, LIST_FILES_TIMEOUT_MS, LIST_FILES_ATTEMPTS, LIST_FILES_RETRY_MS, CheckoutFilePlane;
23627
+ var import_node_child_process5, import_node_fs12, import_node_path12, CheckoutRequiresGitError, BINARY_SNIFF_BYTES, LIST_FILES_TIMEOUT_MS, LIST_FILES_ATTEMPTS, LIST_FILES_RETRY_MS, CheckoutFilePlane;
23516
23628
  var init_checkout_plane = __esm({
23517
23629
  "cli/commands/box/checkout-plane.ts"() {
23518
23630
  "use strict";
23519
23631
  import_node_child_process5 = require("node:child_process");
23520
- import_node_fs11 = __toESM(require("node:fs"), 1);
23521
- import_node_path11 = __toESM(require("node:path"), 1);
23632
+ import_node_fs12 = __toESM(require("node:fs"), 1);
23633
+ import_node_path12 = __toESM(require("node:path"), 1);
23522
23634
  CheckoutRequiresGitError = class extends Error {
23523
23635
  };
23524
23636
  BINARY_SNIFF_BYTES = 8192;
@@ -23535,7 +23647,7 @@ var init_checkout_plane = __esm({
23535
23647
  listTimeoutMs;
23536
23648
  trackedOnly;
23537
23649
  constructor(root, options = {}) {
23538
- this.root = import_node_path11.default.resolve(root);
23650
+ this.root = import_node_path12.default.resolve(root);
23539
23651
  this.listTimeoutMs = options.listTimeoutMs ?? LIST_FILES_TIMEOUT_MS;
23540
23652
  this.trackedOnly = options.trackedOnly ?? false;
23541
23653
  }
@@ -23597,13 +23709,13 @@ var init_checkout_plane = __esm({
23597
23709
  if (!relative2) continue;
23598
23710
  let stat2;
23599
23711
  try {
23600
- stat2 = import_node_fs11.default.statSync(import_node_path11.default.join(this.root, relative2));
23712
+ stat2 = import_node_fs12.default.statSync(import_node_path12.default.join(this.root, relative2));
23601
23713
  } catch {
23602
23714
  continue;
23603
23715
  }
23604
23716
  if (!stat2.isFile()) continue;
23605
23717
  const seen = this.sniffed.get(relative2);
23606
- const binary = seen && seen.size === stat2.size && seen.mtimeMs === stat2.mtimeMs ? seen.binary : looksBinary(import_node_path11.default.join(this.root, relative2), stat2.size);
23718
+ const binary = seen && seen.size === stat2.size && seen.mtimeMs === stat2.mtimeMs ? seen.binary : looksBinary(import_node_path12.default.join(this.root, relative2), stat2.size);
23607
23719
  nextSniffed.set(relative2, { size: stat2.size, mtimeMs: stat2.mtimeMs, binary });
23608
23720
  next.set(relative2, {
23609
23721
  size: stat2.size,
@@ -23617,22 +23729,22 @@ var init_checkout_plane = __esm({
23617
23729
  this.sniffed = nextSniffed;
23618
23730
  }
23619
23731
  absolute(planePath) {
23620
- const resolved = import_node_path11.default.resolve(this.root, planePath);
23621
- if (resolved !== this.root && !resolved.startsWith(`${this.root}${import_node_path11.default.sep}`)) {
23732
+ const resolved = import_node_path12.default.resolve(this.root, planePath);
23733
+ if (resolved !== this.root && !resolved.startsWith(`${this.root}${import_node_path12.default.sep}`)) {
23622
23734
  throw new Error(`path escapes the project: ${planePath}`);
23623
23735
  }
23624
23736
  return resolved;
23625
23737
  }
23626
23738
  async read(planePath) {
23627
- return import_node_fs11.default.promises.readFile(this.absolute(planePath), "utf8");
23739
+ return import_node_fs12.default.promises.readFile(this.absolute(planePath), "utf8");
23628
23740
  }
23629
23741
  async readBytes(planePath) {
23630
- return import_node_fs11.default.promises.readFile(this.absolute(planePath));
23742
+ return import_node_fs12.default.promises.readFile(this.absolute(planePath));
23631
23743
  }
23632
23744
  async write(planePath, content) {
23633
23745
  const absolute = this.absolute(planePath);
23634
- await import_node_fs11.default.promises.mkdir(import_node_path11.default.dirname(absolute), { recursive: true });
23635
- await import_node_fs11.default.promises.writeFile(absolute, content);
23746
+ await import_node_fs12.default.promises.mkdir(import_node_path12.default.dirname(absolute), { recursive: true });
23747
+ await import_node_fs12.default.promises.writeFile(absolute, content);
23636
23748
  this.entries.set(planePath, {
23637
23749
  size: content.byteLength,
23638
23750
  readOnly: false,
@@ -23641,7 +23753,7 @@ var init_checkout_plane = __esm({
23641
23753
  });
23642
23754
  }
23643
23755
  async delete(planePath) {
23644
- await import_node_fs11.default.promises.rm(this.absolute(planePath), { force: true });
23756
+ await import_node_fs12.default.promises.rm(this.absolute(planePath), { force: true });
23645
23757
  this.entries.delete(planePath);
23646
23758
  }
23647
23759
  };
@@ -23649,12 +23761,12 @@ var init_checkout_plane = __esm({
23649
23761
  });
23650
23762
 
23651
23763
  // src/host/plane-host.ts
23652
- var import_node_fs12, import_node_path12, WS_OPEN2, PlaneNotRegisteredError, PlaneHost;
23764
+ var import_node_fs13, import_node_path13, WS_OPEN2, PlaneNotRegisteredError, PlaneHost;
23653
23765
  var init_plane_host = __esm({
23654
23766
  "src/host/plane-host.ts"() {
23655
23767
  "use strict";
23656
- import_node_fs12 = __toESM(require("node:fs"), 1);
23657
- import_node_path12 = __toESM(require("node:path"), 1);
23768
+ import_node_fs13 = __toESM(require("node:fs"), 1);
23769
+ import_node_path13 = __toESM(require("node:path"), 1);
23658
23770
  init_plane();
23659
23771
  init_checkout_plane();
23660
23772
  WS_OPEN2 = 1;
@@ -23777,14 +23889,14 @@ var init_plane_host = __esm({
23777
23889
  // root and an opened one cannot disagree about the same directory.
23778
23890
  realRoot(root) {
23779
23891
  if (!root) throw new Error("root is required");
23780
- const resolved = import_node_path12.default.resolve(root);
23892
+ const resolved = import_node_path13.default.resolve(root);
23781
23893
  let real;
23782
23894
  try {
23783
- real = import_node_fs12.default.realpathSync(resolved);
23895
+ real = import_node_fs13.default.realpathSync(resolved);
23784
23896
  } catch {
23785
23897
  throw new Error(`no such directory: ${resolved}`);
23786
23898
  }
23787
- if (!import_node_fs12.default.statSync(real).isDirectory()) throw new Error(`not a directory: ${real}`);
23899
+ if (!import_node_fs13.default.statSync(real).isDirectory()) throw new Error(`not a directory: ${real}`);
23788
23900
  return real;
23789
23901
  }
23790
23902
  respond(ws, id, result) {
@@ -23835,7 +23947,7 @@ function normalize(p) {
23835
23947
  if (trailingSlash) joined += "/";
23836
23948
  return absolute ? "/" + joined : joined;
23837
23949
  }
23838
- function join4(...parts) {
23950
+ function join5(...parts) {
23839
23951
  const present = parts.filter((part) => part.length > 0);
23840
23952
  if (present.length === 0) return ".";
23841
23953
  return normalize(present.join("/"));
@@ -23898,7 +24010,7 @@ var init_posixPath = __esm({
23898
24010
  delimiter,
23899
24011
  isAbsolute,
23900
24012
  normalize,
23901
- join: join4,
24013
+ join: join5,
23902
24014
  resolve: resolve2,
23903
24015
  dirname: dirname3,
23904
24016
  basename: basename3,
@@ -24166,13 +24278,13 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
24166
24278
  res.end("missing path param");
24167
24279
  return true;
24168
24280
  }
24169
- const extension = import_node_path13.default.extname(targetPath).toLowerCase();
24281
+ const extension = import_node_path14.default.extname(targetPath).toLowerCase();
24170
24282
  if (!REPLACEMENT_MODULE_EXTENSIONS.some((allowed) => allowed === extension)) {
24171
24283
  res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
24172
24284
  res.end(`replacement files must be ${REPLACEMENT_MODULE_EXTENSIONS.join(", ")}`);
24173
24285
  return true;
24174
24286
  }
24175
- if (!import_node_path13.default.isAbsolute(targetPath)) {
24287
+ if (!import_node_path14.default.isAbsolute(targetPath)) {
24176
24288
  res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
24177
24289
  res.end("replacement file path must be absolute");
24178
24290
  return true;
@@ -24180,10 +24292,10 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
24180
24292
  const runtimePackages = new Set(url.searchParams.getAll("runtimePackage"));
24181
24293
  let resolvedProjectRoot;
24182
24294
  if (options.projectRoot) {
24183
- const lexicalProjectRoot = import_node_path13.default.resolve(options.projectRoot);
24295
+ const lexicalProjectRoot = import_node_path14.default.resolve(options.projectRoot);
24184
24296
  resolvedProjectRoot = await import_promises5.default.realpath(options.projectRoot);
24185
- const lexicalTarget = import_node_path13.default.relative(lexicalProjectRoot, targetPath);
24186
- if (lexicalTarget.startsWith("..") || import_node_path13.default.isAbsolute(lexicalTarget)) {
24297
+ const lexicalTarget = import_node_path14.default.relative(lexicalProjectRoot, targetPath);
24298
+ if (lexicalTarget.startsWith("..") || import_node_path14.default.isAbsolute(lexicalTarget)) {
24187
24299
  res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
24188
24300
  res.end("replacement file must be inside the project root");
24189
24301
  return true;
@@ -24199,8 +24311,8 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
24199
24311
  return true;
24200
24312
  }
24201
24313
  if (resolvedProjectRoot) {
24202
- const relativeTarget = import_node_path13.default.relative(resolvedProjectRoot, resolvedTargetPath);
24203
- if (relativeTarget.startsWith("..") || import_node_path13.default.isAbsolute(relativeTarget)) {
24314
+ const relativeTarget = import_node_path14.default.relative(resolvedProjectRoot, resolvedTargetPath);
24315
+ if (relativeTarget.startsWith("..") || import_node_path14.default.isAbsolute(relativeTarget)) {
24204
24316
  res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
24205
24317
  res.end("replacement file must be inside the project root");
24206
24318
  return true;
@@ -24227,12 +24339,12 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
24227
24339
  }
24228
24340
  return true;
24229
24341
  }
24230
- var import_promises5, import_node_path13, REPLACEMENT_MODULE_ROUTE;
24342
+ var import_promises5, import_node_path14, REPLACEMENT_MODULE_ROUTE;
24231
24343
  var init_replacement_module_handler = __esm({
24232
24344
  "src/host/replacement-module-handler.ts"() {
24233
24345
  "use strict";
24234
24346
  import_promises5 = __toESM(require("node:fs/promises"), 1);
24235
- import_node_path13 = __toESM(require("node:path"), 1);
24347
+ import_node_path14 = __toESM(require("node:path"), 1);
24236
24348
  init_replacementModule();
24237
24349
  init_backend_origin();
24238
24350
  REPLACEMENT_MODULE_ROUTE = "/__sootsim-replacement-module";
@@ -24518,14 +24630,14 @@ function sharedDesktopAuthAccounts(user) {
24518
24630
  function getBaseDir() {
24519
24631
  const explicit = process.env[SESSION_FILE_ENV];
24520
24632
  if (explicit?.trim()) {
24521
- return (0, import_node_path14.dirname)((0, import_node_path14.resolve)(explicit));
24633
+ return (0, import_node_path15.dirname)((0, import_node_path15.resolve)(explicit));
24522
24634
  }
24523
24635
  return electronUserDataDir();
24524
24636
  }
24525
24637
  function getSharedDesktopAuthFilePath() {
24526
24638
  const explicit = process.env[SESSION_FILE_ENV];
24527
- if (explicit?.trim()) return (0, import_node_path14.resolve)(explicit);
24528
- return (0, import_node_path14.join)(getBaseDir(), "desktop-auth.json");
24639
+ if (explicit?.trim()) return (0, import_node_path15.resolve)(explicit);
24640
+ return (0, import_node_path15.join)(getBaseDir(), "desktop-auth.json");
24529
24641
  }
24530
24642
  function normalizeUser(input) {
24531
24643
  if (!input || typeof input !== "object") return null;
@@ -24575,26 +24687,26 @@ function normalizeSession(input) {
24575
24687
  }
24576
24688
  function readSharedDesktopAuthSession() {
24577
24689
  const filepath = getSharedDesktopAuthFilePath();
24578
- if (!(0, import_node_fs13.existsSync)(filepath)) return null;
24690
+ if (!(0, import_node_fs14.existsSync)(filepath)) return null;
24579
24691
  try {
24580
- const parsed = JSON.parse((0, import_node_fs13.readFileSync)(filepath, "utf8"));
24692
+ const parsed = JSON.parse((0, import_node_fs14.readFileSync)(filepath, "utf8"));
24581
24693
  const normalized = normalizeSession(parsed);
24582
24694
  if (!normalized) {
24583
- (0, import_node_fs13.rmSync)(filepath, { force: true });
24695
+ (0, import_node_fs14.rmSync)(filepath, { force: true });
24584
24696
  return null;
24585
24697
  }
24586
24698
  return normalized;
24587
24699
  } catch {
24588
- (0, import_node_fs13.rmSync)(filepath, { force: true });
24700
+ (0, import_node_fs14.rmSync)(filepath, { force: true });
24589
24701
  return null;
24590
24702
  }
24591
24703
  }
24592
- var import_node_fs13, import_node_path14, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
24704
+ var import_node_fs14, import_node_path15, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
24593
24705
  var init_shared_session = __esm({
24594
24706
  "src/auth/shared-session.ts"() {
24595
24707
  "use strict";
24596
- import_node_fs13 = require("node:fs");
24597
- import_node_path14 = require("node:path");
24708
+ import_node_fs14 = require("node:fs");
24709
+ import_node_path15 = require("node:path");
24598
24710
  init_home_paths();
24599
24711
  SESSION_VERSION = 1;
24600
24712
  SESSION_FILE_ENV = "SOOTSIM_SHARED_AUTH_FILE";
@@ -24958,7 +25070,7 @@ function rotateDevice(device, side) {
24958
25070
  const barrelProtrusion = device.housing?.hingeBarrel?.protrusion ?? 0;
24959
25071
  if (side === "left") insets.top += barrelProtrusion;
24960
25072
  else insets.bottom += barrelProtrusion;
24961
- const safeArea = device.model === "ipad-pro-11" ? { top: 10, bottom: 20, left: 0, right: 0 } : device.model === "iphone-duo" ? { top: 0, bottom: 20, left: 0, right: 84 } : device.platform === "ios" && device.homeIndicatorHeight > 0 ? {
25073
+ const safeArea = device.model === "ipad-pro-11" ? { top: 10, bottom: 20, left: 0, right: 0 } : device.model === "iphone-duo" ? { top: 0, bottom: 34, left: 0, right: 84 } : device.platform === "ios" && device.homeIndicatorHeight > 0 ? {
24962
25074
  top: 0,
24963
25075
  bottom: 20,
24964
25076
  left: device.safeAreaInsets.top,
@@ -24993,71 +25105,24 @@ function rotateDevice(device, side) {
24993
25105
  // the bars stay on the interface's top and bottom; a cutout's inset moves
24994
25106
  // to whichever side the cutout turned to.
24995
25107
  safeAreaInsets: side === "left" ? safeArea : { ...safeArea, left: safeArea.right, right: safeArea.left },
24996
- statusBarHeight: device.platform === "ios" && device.model !== "ipad-pro-11" && device.model !== "iphone-duo" ? 0 : device.statusBarHeight,
24997
- homeIndicatorHeight: device.platform === "ios" && device.homeIndicatorHeight > 0 ? 20 : device.homeIndicatorHeight
25108
+ statusBarHeight: device.platform === "ios" && device.model !== "ipad-pro-11" ? 0 : device.statusBarHeight,
25109
+ // the indicator's band is the landscape bottom inset
25110
+ homeIndicatorHeight: device.platform === "ios" && device.homeIndicatorHeight > 0 ? safeArea.bottom : device.homeIndicatorHeight
24998
25111
  };
24999
25112
  }
25000
- function getDevice(model, posture, orientation, landscapeSide = "left") {
25113
+ function defaultLandscapeSide(model, posture) {
25114
+ return model === "iphone-duo" && resolveDevicePosture(model, posture) === "closed" ? "right" : "left";
25115
+ }
25116
+ function getDevice(model, posture, orientation, landscapeSide) {
25001
25117
  const device = devices[model];
25118
+ const side = landscapeSide ?? defaultLandscapeSide(model, posture);
25002
25119
  if (!device.supportedPostures) {
25003
- return orientation === "landscape" && model !== "canvas-workspace" ? rotateDevice(device, landscapeSide) : device;
25120
+ return orientation === "landscape" && model !== "canvas-workspace" ? rotateDevice(device, side) : device;
25004
25121
  }
25005
25122
  const resolvedPosture = resolveDevicePosture(model, posture);
25006
25123
  const resolvedOrientation = orientation ?? deviceDefaultOrientation(model, resolvedPosture);
25007
25124
  if (resolvedPosture === "closed") {
25008
- if (resolvedOrientation === "portrait") return device;
25009
- const insets = device.frameInsets;
25010
- const radii = device.cornerRadius;
25011
- const body = device.frameCornerRadius ?? resolveDeviceFrameCorners(device).frameRadius;
25012
- const housing = device.housing;
25013
- const protrusion = housing?.hingeBarrel?.protrusion ?? 0;
25014
- return {
25015
- ...device,
25016
- width: device.height,
25017
- height: device.width,
25018
- orientation: "landscape",
25019
- cornerRadius: {
25020
- topLeft: radii.topRight,
25021
- topRight: radii.bottomRight,
25022
- bottomRight: radii.bottomLeft,
25023
- bottomLeft: radii.topLeft
25024
- },
25025
- continuousCornerRadius: {
25026
- topLeft: device.continuousCornerRadius.topRight,
25027
- topRight: device.continuousCornerRadius.bottomRight,
25028
- bottomRight: device.continuousCornerRadius.bottomLeft,
25029
- bottomLeft: device.continuousCornerRadius.topLeft
25030
- },
25031
- frameInsets: {
25032
- top: insets.right,
25033
- right: insets.bottom,
25034
- bottom: insets.left + protrusion,
25035
- left: insets.top
25036
- },
25037
- frameCornerRadius: {
25038
- topLeft: body.topRight,
25039
- topRight: body.bottomRight,
25040
- bottomRight: body.bottomLeft,
25041
- bottomLeft: body.topLeft
25042
- },
25043
- housing: housing ? {
25044
- rail: {
25045
- top: housing.rail.right,
25046
- right: housing.rail.bottom,
25047
- bottom: housing.rail.left,
25048
- left: housing.rail.top
25049
- },
25050
- hingeBarrel: null
25051
- } : void 0,
25052
- cameraCutout: device.cameraCutout ? {
25053
- diameter: device.cameraCutout.diameter,
25054
- top: device.cameraCutout.top,
25055
- leading: device.cameraCutout.trailing
25056
- } : null,
25057
- safeAreaInsets: { top: 0, bottom: 34, left: 84, right: 0 },
25058
- // vertically compact, so the status bar is hidden.
25059
- statusBarHeight: 0
25060
- };
25125
+ return resolvedOrientation === "portrait" ? device : rotateDevice(device, side);
25061
25126
  }
25062
25127
  const isRotatedPortrait = resolvedOrientation === "portrait";
25063
25128
  return {
@@ -25584,10 +25649,12 @@ var init_devices = __esm({
25584
25649
  posture: "closed",
25585
25650
  supportedPostures: ["closed", "open"]
25586
25651
  }),
25652
+ // the ios 27.1 simulator's camera occlusion region: 37pt, 29 1/3 in from
25653
+ // the top and trailing edges
25587
25654
  cameraCutout: {
25588
25655
  diameter: 37,
25589
- top: 29,
25590
- trailing: 29
25656
+ top: 88 / 3,
25657
+ trailing: 88 / 3
25591
25658
  }
25592
25659
  },
25593
25660
  "ipad-pro-11": {
@@ -26447,8 +26514,8 @@ function metroIosAssetDestRelatives(httpServerLocation, filename) {
26447
26514
  }
26448
26515
  const out = [];
26449
26516
  for (const dir of dirs) {
26450
- const path21 = `${dir}/${filename}`;
26451
- if (!out.includes(path21)) out.push(path21);
26517
+ const path22 = `${dir}/${filename}`;
26518
+ if (!out.includes(path22)) out.push(path22);
26452
26519
  }
26453
26520
  if (!out.includes(filename)) out.push(filename);
26454
26521
  return out;
@@ -26724,7 +26791,9 @@ async function minify(code, sourceLine, options, withMap) {
26724
26791
  }
26725
26792
  const map = withMap && typeof result.map === "string" ? result.map : null;
26726
26793
  if (withMap && map === null) {
26727
- throw new Error(`peach Cloud got no source map for the statement at line ${sourceLine}`);
26794
+ throw new Error(
26795
+ `peach Cloud got no source map for the statement at line ${sourceLine}`
26796
+ );
26728
26797
  }
26729
26798
  return { code: result.code.trimEnd(), map, sourceLine };
26730
26799
  }
@@ -26788,8 +26857,9 @@ async function readOrInferModuleIdentity(bundle, mode, inferModuleIdentity) {
26788
26857
  }
26789
26858
  async function buildRnxCloudArtifact(bundle, options = {}) {
26790
26859
  const mode = options.mode ?? "production";
26860
+ const nativeIdentity = (0, import_native_app_identity.detachNativeAppIdentityFooter)(bundle);
26791
26861
  const identity = await readOrInferModuleIdentity(
26792
- bundle,
26862
+ nativeIdentity.source,
26793
26863
  mode,
26794
26864
  options.inferModuleIdentity
26795
26865
  );
@@ -26815,7 +26885,7 @@ async function buildRnxCloudArtifact(bundle, options = {}) {
26815
26885
  })
26816
26886
  ]);
26817
26887
  const body = pieces.map((piece) => piece.code).join("\n");
26818
- const code = body + (0, import_metro_production_bundle.createRNXMetroModuleIdentityFooter)({
26888
+ const code = body + (nativeIdentity.identity ? (0, import_native_app_identity.createNativeAppIdentityFooter)(nativeIdentity.identity) : "") + (0, import_metro_production_bundle.createRNXMetroModuleIdentityFooter)({
26819
26889
  version: identity.version,
26820
26890
  identitySource: identity.identitySource,
26821
26891
  modulePaths: identity.modulePaths,
@@ -26878,11 +26948,12 @@ async function buildRnxCloudArtifact(bundle, options = {}) {
26878
26948
  }
26879
26949
  };
26880
26950
  }
26881
- var import_metro_production_bundle, import_terser, MODULE_PATHS_REQUIRED, SOURCE_MAP_REQUIRED, MINIFY_OPTIONS;
26951
+ var import_metro_production_bundle, import_native_app_identity, import_terser, MODULE_PATHS_REQUIRED, SOURCE_MAP_REQUIRED, MINIFY_OPTIONS;
26882
26952
  var init_metroCloudArtifact = __esm({
26883
26953
  "../contrast-bundler/src/metroCloudArtifact.ts"() {
26884
26954
  "use strict";
26885
26955
  import_metro_production_bundle = require("peachsim/metro-production-bundle");
26956
+ import_native_app_identity = require("peachsim/native-app-identity");
26886
26957
  import_terser = require("terser");
26887
26958
  init_metroAssetArtifact();
26888
26959
  init_metroDebugSourceMap();
@@ -27111,6 +27182,12 @@ var init_stub_manifest = __esm({
27111
27182
  runtimeTier: "core",
27112
27183
  buildResolvers: ["vite", "dep-prebundle"]
27113
27184
  },
27185
+ vendorEventEmitter: {
27186
+ specifier: "react-native/Libraries/vendor/emitter/EventEmitter",
27187
+ stubFile: "rn-libraries/EventEmitter.ts",
27188
+ runtimeTier: "core",
27189
+ buildResolvers: ["vite", "dep-prebundle"]
27190
+ },
27114
27191
  rctDeviceEventEmitter: {
27115
27192
  specifier: "react-native/Libraries/EventEmitter/RCTDeviceEventEmitter",
27116
27193
  stubFile: "rn-libraries/RCTDeviceEventEmitter.ts",
@@ -27388,13 +27465,13 @@ function readFactoryStringLiterals(factory) {
27388
27465
  return literals;
27389
27466
  }
27390
27467
  function normalizeFingerprintLiteral(literal) {
27391
- const path21 = literal.replaceAll("\\\\", "/").replaceAll("\\/", "/");
27468
+ const path22 = literal.replaceAll("\\\\", "/").replaceAll("\\/", "/");
27392
27469
  const nodeModulesMarker = "/node_modules/";
27393
- const nodeModulesIndex = path21.lastIndexOf(nodeModulesMarker);
27470
+ const nodeModulesIndex = path22.lastIndexOf(nodeModulesMarker);
27394
27471
  if (nodeModulesIndex >= 0) {
27395
- return `node_modules/${path21.slice(nodeModulesIndex + nodeModulesMarker.length)}`;
27472
+ return `node_modules/${path22.slice(nodeModulesIndex + nodeModulesMarker.length)}`;
27396
27473
  }
27397
- if ((path21.startsWith("/") || /^[A-Za-z]:\//.test(path21) || path21.startsWith("file:/")) && /\.[cm]?[jt]sx?(?::\d+(?::\d+)?)?$/.test(path21)) {
27474
+ if ((path22.startsWith("/") || /^[A-Za-z]:\//.test(path22) || path22.startsWith("file:/")) && /\.[cm]?[jt]sx?(?::\d+(?::\d+)?)?$/.test(path22)) {
27398
27475
  return null;
27399
27476
  }
27400
27477
  return literal;
@@ -27632,10 +27709,10 @@ function templateAssignment(template, embeddings) {
27632
27709
  if (existingNode !== void 0 && existingNode !== nodeIndex) return null;
27633
27710
  nodesByModuleId.set(moduleId, nodeIndex);
27634
27711
  const node = template.nodes[nodeIndex];
27635
- const path21 = node.landmarkPath ?? `node_modules/${template.packageName}/__rnx_inferred__/${moduleId}.js`;
27712
+ const path22 = node.landmarkPath ?? `node_modules/${template.packageName}/__rnx_inferred__/${moduleId}.js`;
27636
27713
  const existingPath = pathsByModuleId.get(moduleId);
27637
- if (existingPath !== void 0 && existingPath !== path21) return null;
27638
- pathsByModuleId.set(moduleId, path21);
27714
+ if (existingPath !== void 0 && existingPath !== path22) return null;
27715
+ pathsByModuleId.set(moduleId, path22);
27639
27716
  }
27640
27717
  }
27641
27718
  return {
@@ -27797,14 +27874,14 @@ async function inferMetroModuleIdentity(args) {
27797
27874
  const packageClaims = claimsByPackage.values().next().value;
27798
27875
  if (!packageClaims) continue;
27799
27876
  const exactClaims = packageClaims.filter((assignment2) => {
27800
- const path21 = assignment2.pathsByModuleId.get(moduleId);
27801
- return path21 !== void 0 && !path21.includes("/__rnx_inferred__/");
27877
+ const path22 = assignment2.pathsByModuleId.get(moduleId);
27878
+ return path22 !== void 0 && !path22.includes("/__rnx_inferred__/");
27802
27879
  });
27803
27880
  const preferredClaims = exactClaims.length > 0 ? exactClaims : packageClaims;
27804
27881
  const routes = new Set(
27805
27882
  preferredClaims.flatMap((assignment2) => {
27806
- const path21 = assignment2.pathsByModuleId.get(moduleId);
27807
- return path21 ? [metroFingerprintModuleClaimRoute(assignment2.packageName, path21)] : [];
27883
+ const path22 = assignment2.pathsByModuleId.get(moduleId);
27884
+ return path22 ? [metroFingerprintModuleClaimRoute(assignment2.packageName, path22)] : [];
27808
27885
  })
27809
27886
  );
27810
27887
  if (routes.size !== 1) continue;
@@ -28165,21 +28242,21 @@ function resolveCloudOrigin(value) {
28165
28242
  return normalizeCloudOrigin(source);
28166
28243
  }
28167
28244
  function readImmutableBundleSource(bundlePath) {
28168
- const filepath = (0, import_node_path15.resolve)(bundlePath);
28169
- if ((0, import_node_path15.extname)(filepath) !== ".js") {
28245
+ const filepath = (0, import_node_path16.resolve)(bundlePath);
28246
+ if ((0, import_node_path16.extname)(filepath) !== ".js") {
28170
28247
  throw new Error(`${REMOTE_COMMAND} requires one local Metro .js bundle`);
28171
28248
  }
28172
- const descriptor = (0, import_node_fs14.openSync)(filepath, "r");
28249
+ const descriptor = (0, import_node_fs15.openSync)(filepath, "r");
28173
28250
  try {
28174
- const before = (0, import_node_fs14.fstatSync)(descriptor, { bigint: true });
28251
+ const before = (0, import_node_fs15.fstatSync)(descriptor, { bigint: true });
28175
28252
  if (!before.isFile()) {
28176
28253
  throw new Error(`${REMOTE_COMMAND} bundle must be a regular local file`);
28177
28254
  }
28178
28255
  if (before.size <= 0n) {
28179
28256
  throw new Error(`${REMOTE_COMMAND} bundle is empty`);
28180
28257
  }
28181
- const bytes = (0, import_node_fs14.readFileSync)(descriptor);
28182
- const after = (0, import_node_fs14.fstatSync)(descriptor, { bigint: true });
28258
+ const bytes = (0, import_node_fs15.readFileSync)(descriptor);
28259
+ const after = (0, import_node_fs15.fstatSync)(descriptor, { bigint: true });
28183
28260
  if (before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeNs !== after.mtimeNs || bytes.length !== Number(before.size)) {
28184
28261
  throw new Error(`${REMOTE_COMMAND} bundle changed while it was being read`);
28185
28262
  }
@@ -28194,7 +28271,7 @@ function readImmutableBundleSource(bundlePath) {
28194
28271
  }
28195
28272
  return source;
28196
28273
  } finally {
28197
- (0, import_node_fs14.closeSync)(descriptor);
28274
+ (0, import_node_fs15.closeSync)(descriptor);
28198
28275
  }
28199
28276
  }
28200
28277
  async function fetchBundleSource(bundleUrl) {
@@ -28241,15 +28318,15 @@ async function loadBundleSource(bundleInput) {
28241
28318
  return readImmutableBundleSource(bundleInput);
28242
28319
  }
28243
28320
  function listMetroAssetDest(assetsPath) {
28244
- const directory = (0, import_node_path15.resolve)(assetsPath);
28245
- if (!(0, import_node_fs14.existsSync)(directory)) return { directory, files: [], truncated: false };
28246
- const skipNested = (0, import_node_fs14.existsSync)((0, import_node_path15.join)(directory, "package.json"));
28321
+ const directory = (0, import_node_path16.resolve)(assetsPath);
28322
+ if (!(0, import_node_fs15.existsSync)(directory)) return { directory, files: [], truncated: false };
28323
+ const skipNested = (0, import_node_fs15.existsSync)((0, import_node_path16.join)(directory, "package.json"));
28247
28324
  const files = [];
28248
28325
  let truncated = false;
28249
28326
  const walk = (dir, prefix) => {
28250
28327
  let entries;
28251
28328
  try {
28252
- entries = (0, import_node_fs14.readdirSync)(dir, { withFileTypes: true });
28329
+ entries = (0, import_node_fs15.readdirSync)(dir, { withFileTypes: true });
28253
28330
  } catch {
28254
28331
  return;
28255
28332
  }
@@ -28261,7 +28338,7 @@ function listMetroAssetDest(assetsPath) {
28261
28338
  if (entry.isDirectory()) {
28262
28339
  if (entry.name === ".git") continue;
28263
28340
  if (skipNested && PROJECT_ONLY_DIRECTORIES.has(entry.name)) continue;
28264
- walk((0, import_node_path15.join)(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
28341
+ walk((0, import_node_path16.join)(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
28265
28342
  } else if (entry.isFile()) {
28266
28343
  files.push(prefix ? `${prefix}/${entry.name}` : entry.name);
28267
28344
  }
@@ -28296,7 +28373,7 @@ function distinctByContent(directory, options) {
28296
28373
  for (const relative2 of options) {
28297
28374
  let bytes;
28298
28375
  try {
28299
- bytes = (0, import_node_fs14.readFileSync)((0, import_node_path15.join)(directory, relative2));
28376
+ bytes = (0, import_node_fs15.readFileSync)((0, import_node_path16.join)(directory, relative2));
28300
28377
  } catch {
28301
28378
  continue;
28302
28379
  }
@@ -28328,7 +28405,7 @@ function placeMetroDescriptor(listing, descriptor) {
28328
28405
  let bytes;
28329
28406
  try {
28330
28407
  bytes = combination.map(
28331
- (relative2) => (0, import_node_fs14.readFileSync)((0, import_node_path15.join)(listing.directory, relative2))
28408
+ (relative2) => (0, import_node_fs15.readFileSync)((0, import_node_path16.join)(listing.directory, relative2))
28332
28409
  );
28333
28410
  } catch {
28334
28411
  continue;
@@ -28354,26 +28431,26 @@ function placeMetroDescriptor(listing, descriptor) {
28354
28431
  }
28355
28432
  function unverifiedFileKey(dest) {
28356
28433
  return dest.placements.filter((placement) => !placement.verified).flatMap(
28357
- (placement) => [...placement.files.values()].map((relative2) => (0, import_node_path15.join)(dest.directory, relative2))
28434
+ (placement) => [...placement.files.values()].map((relative2) => (0, import_node_path16.join)(dest.directory, relative2))
28358
28435
  ).sort().join("\n");
28359
28436
  }
28360
28437
  function searchMetroAssetDest(bundlePath, descriptors) {
28361
- const bundleDirectory = (0, import_node_path15.dirname)((0, import_node_path15.resolve)(bundlePath));
28438
+ const bundleDirectory = (0, import_node_path16.dirname)((0, import_node_path16.resolve)(bundlePath));
28362
28439
  const candidates = [
28363
- (0, import_node_path15.join)(bundleDirectory, "assets"),
28440
+ (0, import_node_path16.join)(bundleDirectory, "assets"),
28364
28441
  bundleDirectory,
28365
- (0, import_node_path15.resolve)(bundleDirectory, "..", "assets")
28442
+ (0, import_node_path16.resolve)(bundleDirectory, "..", "assets")
28366
28443
  ];
28367
28444
  const cwd = process.cwd();
28368
28445
  let projectDirectory = null;
28369
28446
  for (const start of [bundleDirectory, cwd]) {
28370
- let directory = (0, import_node_path15.resolve)(start);
28447
+ let directory = (0, import_node_path16.resolve)(start);
28371
28448
  while (true) {
28372
28449
  if (detectProject(directory)?.hasMetroConfig) {
28373
28450
  projectDirectory = directory;
28374
28451
  break;
28375
28452
  }
28376
- const parent = (0, import_node_path15.dirname)(directory);
28453
+ const parent = (0, import_node_path16.dirname)(directory);
28377
28454
  if (parent === directory) break;
28378
28455
  directory = parent;
28379
28456
  }
@@ -28381,11 +28458,11 @@ function searchMetroAssetDest(bundlePath, descriptors) {
28381
28458
  }
28382
28459
  if (projectDirectory) {
28383
28460
  candidates.push(
28384
- (0, import_node_path15.join)(projectDirectory, "assets"),
28385
- (0, import_node_path15.join)(projectDirectory, "dist"),
28386
- (0, import_node_path15.join)(projectDirectory, "dist", "assets"),
28387
- (0, import_node_path15.join)(projectDirectory, "build"),
28388
- (0, import_node_path15.join)(projectDirectory, "build", "assets")
28461
+ (0, import_node_path16.join)(projectDirectory, "assets"),
28462
+ (0, import_node_path16.join)(projectDirectory, "dist"),
28463
+ (0, import_node_path16.join)(projectDirectory, "dist", "assets"),
28464
+ (0, import_node_path16.join)(projectDirectory, "build"),
28465
+ (0, import_node_path16.join)(projectDirectory, "build", "assets")
28389
28466
  );
28390
28467
  }
28391
28468
  const searched = [];
@@ -28393,7 +28470,7 @@ function searchMetroAssetDest(bundlePath, descriptors) {
28393
28470
  const stale = [];
28394
28471
  let truncated = false;
28395
28472
  for (const candidate of candidates) {
28396
- const directory = (0, import_node_path15.resolve)(candidate);
28473
+ const directory = (0, import_node_path16.resolve)(candidate);
28397
28474
  if (searched.includes(directory)) continue;
28398
28475
  searched.push(directory);
28399
28476
  const listing = listMetroAssetDest(directory);
@@ -28499,7 +28576,7 @@ async function embedBundleAssets(bundlePath, command, assetPolicy) {
28499
28576
  );
28500
28577
  }
28501
28578
  const relative2 = placement?.files.get(variant.scale);
28502
- if (relative2 && dest) return (0, import_node_fs14.readFileSync)((0, import_node_path15.join)(dest.directory, relative2));
28579
+ if (relative2 && dest) return (0, import_node_fs15.readFileSync)((0, import_node_path16.join)(dest.directory, relative2));
28503
28580
  if (placement && placement.files.size > 0) return null;
28504
28581
  if (placement && placement.ambiguous.length > 0 && dest) {
28505
28582
  throw new Error(
@@ -28528,7 +28605,7 @@ async function embedBundleAssets(bundlePath, command, assetPolicy) {
28528
28605
  );
28529
28606
  const classic = metroIosAssetDestRelatives(variant.descriptor.httpServerLocation, filename)[0] ?? filename;
28530
28607
  throw new Error(
28531
- `${command} could not read ${(0, import_node_path15.join)(dest?.directory ?? ".", classic)} for ${variant.descriptor.name}.${variant.descriptor.type} at scale ${variant.scale}, and has no stand-in for a .${variant.descriptor.type} asset. Rebuild with \`react-native bundle --assets-dest\`.`
28608
+ `${command} could not read ${(0, import_node_path16.join)(dest?.directory ?? ".", classic)} for ${variant.descriptor.name}.${variant.descriptor.type} at scale ${variant.scale}, and has no stand-in for a .${variant.descriptor.type} asset. Rebuild with \`react-native bundle --assets-dest\`.`
28532
28609
  );
28533
28610
  });
28534
28611
  return {
@@ -28714,13 +28791,13 @@ function createCloudBridgeForParsed(parsed) {
28714
28791
  if (!session) return null;
28715
28792
  return session.service === "box" ? new CloudBoxBridge(session) : new CloudBridge(session);
28716
28793
  }
28717
- var import_node_crypto6, import_node_fs14, import_node_path15, DEFAULT_RNX_CLOUD_ORIGIN, MAX_RNX_CLOUD_BUNDLE_BYTES, REMOTE_COMMAND, MAX_ASSET_DEST_FILES, ACCOUNT_CHOICE_REFUSALS, PROJECT_ONLY_DIRECTORIES, MAX_PLACEMENT_COMBINATIONS, CloudBridge, CloudBoxBridge;
28794
+ var import_node_crypto6, import_node_fs15, import_node_path16, DEFAULT_RNX_CLOUD_ORIGIN, MAX_RNX_CLOUD_BUNDLE_BYTES, REMOTE_COMMAND, MAX_ASSET_DEST_FILES, ACCOUNT_CHOICE_REFUSALS, PROJECT_ONLY_DIRECTORIES, MAX_PLACEMENT_COMBINATIONS, CloudBridge, CloudBoxBridge;
28718
28795
  var init_cloud_client = __esm({
28719
28796
  "cli/cloud-client.ts"() {
28720
28797
  "use strict";
28721
28798
  import_node_crypto6 = require("node:crypto");
28722
- import_node_fs14 = require("node:fs");
28723
- import_node_path15 = require("node:path");
28799
+ import_node_fs15 = require("node:fs");
28800
+ import_node_path16 = require("node:path");
28724
28801
  init_metroAssetArtifact();
28725
28802
  init_metroCloudArtifact();
28726
28803
  init_metro_fingerprint_registry_client();
@@ -29002,6 +29079,7 @@ var init_bridge_host = __esm({
29002
29079
  import_ws = require("ws");
29003
29080
  init_dev_server_scanner();
29004
29081
  init_backend_origin();
29082
+ init_automatic_update_policy();
29005
29083
  init_bridge_constants();
29006
29084
  init_cli_version();
29007
29085
  init_home_paths();
@@ -29875,7 +29953,7 @@ var init_bridge_host = __esm({
29875
29953
  }
29876
29954
  startRuntimeUpdater() {
29877
29955
  if (!this.shouldWriteLockfile || this.runtimeUpdateTimer) return;
29878
- if (import_fs3.default.existsSync(import_path3.default.join(rnxHomeDir(), "runtime-update-disabled"))) {
29956
+ if (!automaticPeachUpdatesEnabled()) {
29879
29957
  this.bootstrapping = false;
29880
29958
  if (this.httpServer) this.writeLockfileSnapshot();
29881
29959
  return;
@@ -29891,6 +29969,7 @@ var init_bridge_host = __esm({
29891
29969
  this.runtimeUpdateTimer.unref();
29892
29970
  }
29893
29971
  runRuntimeUpdate(reason) {
29972
+ if (!automaticPeachUpdatesEnabled()) return Promise.resolve();
29894
29973
  if (this.runtimeUpdateInFlight) return this.runtimeUpdateInFlight;
29895
29974
  this.runtimeUpdateInFlight = (async () => {
29896
29975
  try {
@@ -31128,31 +31207,31 @@ var init_parent_pid = __esm({
31128
31207
 
31129
31208
  // cli/secure-file.ts
31130
31209
  function writeSecureJsonAtomic(filepath, value) {
31131
- (0, import_node_fs15.mkdirSync)((0, import_node_path16.dirname)(filepath), { recursive: true });
31210
+ (0, import_node_fs16.mkdirSync)((0, import_node_path17.dirname)(filepath), { recursive: true });
31132
31211
  const temporary = `${filepath}.tmp-${process.pid}-${(0, import_node_crypto7.randomUUID)()}`;
31133
31212
  let descriptor = null;
31134
31213
  try {
31135
- descriptor = (0, import_node_fs15.openSync)(temporary, "wx", 384);
31136
- (0, import_node_fs15.fchmodSync)(descriptor, 384);
31137
- (0, import_node_fs15.writeFileSync)(descriptor, `${JSON.stringify(value, null, 2)}
31214
+ descriptor = (0, import_node_fs16.openSync)(temporary, "wx", 384);
31215
+ (0, import_node_fs16.fchmodSync)(descriptor, 384);
31216
+ (0, import_node_fs16.writeFileSync)(descriptor, `${JSON.stringify(value, null, 2)}
31138
31217
  `);
31139
- (0, import_node_fs15.fsyncSync)(descriptor);
31140
- (0, import_node_fs15.closeSync)(descriptor);
31218
+ (0, import_node_fs16.fsyncSync)(descriptor);
31219
+ (0, import_node_fs16.closeSync)(descriptor);
31141
31220
  descriptor = null;
31142
- (0, import_node_fs15.renameSync)(temporary, filepath);
31221
+ (0, import_node_fs16.renameSync)(temporary, filepath);
31143
31222
  } catch (error) {
31144
- if (descriptor !== null) (0, import_node_fs15.closeSync)(descriptor);
31145
- (0, import_node_fs15.rmSync)(temporary, { force: true });
31223
+ if (descriptor !== null) (0, import_node_fs16.closeSync)(descriptor);
31224
+ (0, import_node_fs16.rmSync)(temporary, { force: true });
31146
31225
  throw error;
31147
31226
  }
31148
31227
  }
31149
- var import_node_crypto7, import_node_fs15, import_node_path16;
31228
+ var import_node_crypto7, import_node_fs16, import_node_path17;
31150
31229
  var init_secure_file = __esm({
31151
31230
  "cli/secure-file.ts"() {
31152
31231
  "use strict";
31153
31232
  import_node_crypto7 = require("node:crypto");
31154
- import_node_fs15 = require("node:fs");
31155
- import_node_path16 = require("node:path");
31233
+ import_node_fs16 = require("node:fs");
31234
+ import_node_path17 = require("node:path");
31156
31235
  }
31157
31236
  });
31158
31237
 
@@ -31327,17 +31406,17 @@ function liveDevBridgeLockfiles() {
31327
31406
  return readDevBridgeLockfiles().filter((lockfile) => isDevBridgeLockfileFresh(lockfile));
31328
31407
  }
31329
31408
  function checkoutRoot(cwd) {
31330
- let current = import_node_path17.default.resolve(cwd);
31409
+ let current = import_node_path18.default.resolve(cwd);
31331
31410
  while (true) {
31332
- if (import_node_fs16.default.existsSync(import_node_path17.default.join(current, ".git"))) return current;
31333
- const parent = import_node_path17.default.dirname(current);
31411
+ if (import_node_fs17.default.existsSync(import_node_path18.default.join(current, ".git"))) return current;
31412
+ const parent = import_node_path18.default.dirname(current);
31334
31413
  if (parent === current) return null;
31335
31414
  current = parent;
31336
31415
  }
31337
31416
  }
31338
31417
  function pathContains(parent, child) {
31339
- const relative2 = import_node_path17.default.relative(parent, child);
31340
- return relative2 === "" || relative2 !== ".." && !relative2.startsWith(`..${import_node_path17.default.sep}`) && !import_node_path17.default.isAbsolute(relative2);
31418
+ const relative2 = import_node_path18.default.relative(parent, child);
31419
+ return relative2 === "" || relative2 !== ".." && !relative2.startsWith(`..${import_node_path18.default.sep}`) && !import_node_path18.default.isAbsolute(relative2);
31341
31420
  }
31342
31421
  function resolveDefaultDevBridgeLockfile() {
31343
31422
  const lockfiles = liveDevBridgeLockfiles();
@@ -31829,18 +31908,18 @@ async function evalInBridge(bridge, code, opts = {}) {
31829
31908
  if (opts.acquireLock) payload.acquireLock = true;
31830
31909
  return bridge.send(payload);
31831
31910
  }
31832
- async function callInBridge(bridge, path21, ...args) {
31833
- return bridge.send({ type: "call", path: path21, args });
31911
+ async function callInBridge(bridge, path22, ...args) {
31912
+ return bridge.send({ type: "call", path: path22, args });
31834
31913
  }
31835
- async function callInBridgeWrite(bridge, path21, ...args) {
31836
- return bridge.send({ type: "call", path: path21, args, acquireLock: true });
31914
+ async function callInBridgeWrite(bridge, path22, ...args) {
31915
+ return bridge.send({ type: "call", path: path22, args, acquireLock: true });
31837
31916
  }
31838
- var import_node_fs16, import_node_path17, import_ws2, PEACH_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError, BRIDGE_VALUE_FLAGS_TABLE, BRIDGE_VALUE_FLAGS;
31917
+ var import_node_fs17, import_node_path18, import_ws2, PEACH_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError, BRIDGE_VALUE_FLAGS_TABLE, BRIDGE_VALUE_FLAGS;
31839
31918
  var init_ws_bridge = __esm({
31840
31919
  "cli/ws-bridge.ts"() {
31841
31920
  "use strict";
31842
- import_node_fs16 = __toESM(require("node:fs"), 1);
31843
- import_node_path17 = __toESM(require("node:path"), 1);
31921
+ import_node_fs17 = __toESM(require("node:fs"), 1);
31922
+ import_node_path18 = __toESM(require("node:path"), 1);
31844
31923
  import_ws2 = require("ws");
31845
31924
  init_bridge_constants();
31846
31925
  init_cli_constants();
@@ -31968,8 +32047,8 @@ function getDefaultPortForProtocol(protocol) {
31968
32047
  return protocol === "https:" ? 443 : 80;
31969
32048
  }
31970
32049
  function isBaseServerUrl(url) {
31971
- const path21 = url.pathname || "/";
31972
- return (path21 === "/" || path21 === "") && !url.search && !url.hash;
32050
+ const path22 = url.pathname || "/";
32051
+ return (path22 === "/" || path22 === "") && !url.search && !url.hash;
31973
32052
  }
31974
32053
  async function resolvePortViaServerScan(port) {
31975
32054
  try {
@@ -32143,6 +32222,96 @@ var init_flow_timeout_scale = __esm({
32143
32222
  }
32144
32223
  });
32145
32224
 
32225
+ // src/native-firebase-config.ts
32226
+ function readNativeFirebaseConfig(projectRoot) {
32227
+ const result = {};
32228
+ if (![
32229
+ "app.json",
32230
+ "app.config.js",
32231
+ "app.config.ts",
32232
+ "app.config.cjs",
32233
+ "app.config.mjs"
32234
+ ].some((name2) => import_node_fs18.default.existsSync(import_node_path19.default.join(projectRoot, name2))))
32235
+ return result;
32236
+ const require5 = (0, import_node_module.createRequire)(import_node_path19.default.join(projectRoot, "package.json"));
32237
+ const configModule = require5("@expo/config");
32238
+ if (!isRecord(configModule) || typeof configModule.getConfig !== "function") {
32239
+ throw new Error("Expo native configuration is unavailable.");
32240
+ }
32241
+ const resolved = Reflect.apply(configModule.getConfig, configModule, [
32242
+ projectRoot,
32243
+ { skipSDKVersionRequirement: true }
32244
+ ]);
32245
+ const config = isRecord(resolved) && isRecord(resolved.exp) ? resolved.exp : {};
32246
+ for (const platform of ["ios", "android"]) {
32247
+ const native = config[platform];
32248
+ if (!isRecord(native) || typeof native.googleServicesFile !== "string") continue;
32249
+ try {
32250
+ const source = import_node_fs18.default.readFileSync(
32251
+ import_node_path19.default.resolve(projectRoot, native.googleServicesFile),
32252
+ "utf8"
32253
+ );
32254
+ const parsed = platform === "ios" ? parsePlistSource(source) : JSON.parse(source);
32255
+ if (!isRecord(parsed)) throw new Error("Invalid Google services file.");
32256
+ if (platform === "ios") {
32257
+ result[platform] = {
32258
+ apiKey: parsed.API_KEY,
32259
+ appId: parsed.GOOGLE_APP_ID,
32260
+ projectId: parsed.PROJECT_ID,
32261
+ databaseURL: parsed.DATABASE_URL,
32262
+ gaTrackingId: parsed.TRACKING_ID,
32263
+ storageBucket: parsed.STORAGE_BUCKET,
32264
+ messagingSenderId: parsed.GCM_SENDER_ID,
32265
+ clientId: parsed.CLIENT_ID,
32266
+ androidClientID: parsed.ANDROID_CLIENT_ID,
32267
+ deepLinkUrlScheme: parsed.REVERSED_CLIENT_ID
32268
+ };
32269
+ } else {
32270
+ const project = isRecord(parsed.project_info) ? parsed.project_info : {};
32271
+ const clients = Array.isArray(parsed.client) ? parsed.client : [];
32272
+ const client = clients.find((value) => {
32273
+ if (!isRecord(value) || !isRecord(value.client_info)) return false;
32274
+ const android = value.client_info.android_client_info;
32275
+ return isRecord(android) && android.package_name === native.package;
32276
+ });
32277
+ if (!isRecord(client) || !isRecord(client.client_info))
32278
+ throw new Error("No Google services client matches the Android package.");
32279
+ const apiKeys = Array.isArray(client.api_key) ? client.api_key : [];
32280
+ const apiKey = apiKeys.find(
32281
+ (value) => isRecord(value) && typeof value.current_key === "string"
32282
+ );
32283
+ const oauthClients = Array.isArray(client.oauth_client) ? client.oauth_client : [];
32284
+ const oauth = oauthClients.find(
32285
+ (value) => isRecord(value) && value.client_type === 3
32286
+ );
32287
+ result[platform] = {
32288
+ apiKey: isRecord(apiKey) ? apiKey.current_key : void 0,
32289
+ appId: client.client_info.mobilesdk_app_id,
32290
+ projectId: project.project_id,
32291
+ databaseURL: project.firebase_url,
32292
+ storageBucket: project.storage_bucket,
32293
+ messagingSenderId: project.project_number,
32294
+ clientId: isRecord(oauth) ? oauth.client_id : void 0
32295
+ };
32296
+ }
32297
+ } catch (error) {
32298
+ result[platform] = { error: error instanceof Error ? error.message : String(error) };
32299
+ }
32300
+ }
32301
+ return result;
32302
+ }
32303
+ var import_node_fs18, import_node_module, import_node_path19;
32304
+ var init_native_firebase_config = __esm({
32305
+ "src/native-firebase-config.ts"() {
32306
+ "use strict";
32307
+ import_node_fs18 = __toESM(require("node:fs"), 1);
32308
+ import_node_module = require("node:module");
32309
+ import_node_path19 = __toESM(require("node:path"), 1);
32310
+ init_is_record();
32311
+ init_plist();
32312
+ }
32313
+ });
32314
+
32146
32315
  // src/profiles.ts
32147
32316
  function normalizeProfileId(input) {
32148
32317
  const id = input.trim();
@@ -32301,7 +32470,7 @@ function colorsetValue(color) {
32301
32470
  }
32302
32471
  function enumerateBundleColors(paths, readText2) {
32303
32472
  const table = {};
32304
- for (const contentsPath of paths.filter((path21) => baseOf(path21) === "Contents.json")) {
32473
+ for (const contentsPath of paths.filter((path22) => baseOf(path22) === "Contents.json")) {
32305
32474
  const name2 = catalogEntryName(contentsPath, ".colorset", readText2);
32306
32475
  if (name2 === null || name2 in table) continue;
32307
32476
  let colors;
@@ -32349,59 +32518,59 @@ var init_bundleAssets = __esm({
32349
32518
  // cli/app-catalog.ts
32350
32519
  function listDir(dir) {
32351
32520
  try {
32352
- return import_node_fs17.default.readdirSync(dir, { withFileTypes: true });
32521
+ return import_node_fs19.default.readdirSync(dir, { withFileTypes: true });
32353
32522
  } catch {
32354
32523
  return [];
32355
32524
  }
32356
32525
  }
32357
32526
  function readText(file) {
32358
32527
  try {
32359
- return import_node_fs17.default.readFileSync(file, "utf8");
32528
+ return import_node_fs19.default.readFileSync(file, "utf8");
32360
32529
  } catch {
32361
32530
  return null;
32362
32531
  }
32363
32532
  }
32364
32533
  function catalogFiles(dir, found) {
32365
32534
  for (const entry of listDir(dir)) {
32366
- const full = import_node_path18.default.join(dir, entry.name);
32535
+ const full = import_node_path20.default.join(dir, entry.name);
32367
32536
  if (entry.isDirectory()) catalogFiles(full, found);
32368
32537
  else if (entry.name === "Contents.json") found.push(full);
32369
32538
  }
32370
32539
  }
32371
32540
  function discoverAppCatalog(appDir) {
32372
- const iosDir = import_node_path18.default.join(appDir, "ios");
32541
+ const iosDir = import_node_path20.default.join(appDir, "ios");
32373
32542
  const target = listDir(iosDir).find(
32374
- (entry) => entry.isDirectory() && entry.name !== "Pods" && listDir(import_node_path18.default.join(iosDir, entry.name)).some(
32543
+ (entry) => entry.isDirectory() && entry.name !== "Pods" && listDir(import_node_path20.default.join(iosDir, entry.name)).some(
32375
32544
  (file) => /^AppDelegate\.(swift|mm|m)$/.test(file.name)
32376
32545
  )
32377
32546
  );
32378
32547
  if (!target) return null;
32379
- const targetDir = import_node_path18.default.join(iosDir, target.name);
32548
+ const targetDir = import_node_path20.default.join(iosDir, target.name);
32380
32549
  const files = [];
32381
32550
  for (const entry of listDir(targetDir)) {
32382
32551
  if (entry.isDirectory() && entry.name.endsWith(".xcassets"))
32383
- catalogFiles(import_node_path18.default.join(targetDir, entry.name), files);
32552
+ catalogFiles(import_node_path20.default.join(targetDir, entry.name), files);
32384
32553
  }
32385
32554
  const relative2 = files.map(
32386
- (file) => import_node_path18.default.relative(targetDir, file).split(import_node_path18.default.sep).join(import_node_path18.default.posix.sep)
32555
+ (file) => import_node_path20.default.relative(targetDir, file).split(import_node_path20.default.sep).join(import_node_path20.default.posix.sep)
32387
32556
  );
32388
32557
  const colors = enumerateBundleColors(
32389
32558
  relative2,
32390
- (candidate) => candidate.endsWith("Contents.json") ? readText(import_node_path18.default.join(targetDir, candidate)) : null
32559
+ (candidate) => candidate.endsWith("Contents.json") ? readText(import_node_path20.default.join(targetDir, candidate)) : null
32391
32560
  );
32392
32561
  const pbxproj = listDir(iosDir).find((entry) => entry.name.endsWith(".xcodeproj"));
32393
- const accentName = readText(import_node_path18.default.join(targetDir, "Info.plist"))?.match(PLIST_ACCENT)?.[1] ?? (pbxproj ? readText(import_node_path18.default.join(iosDir, pbxproj.name, "project.pbxproj"))?.match(
32562
+ const accentName = readText(import_node_path20.default.join(targetDir, "Info.plist"))?.match(PLIST_ACCENT)?.[1] ?? (pbxproj ? readText(import_node_path20.default.join(iosDir, pbxproj.name, "project.pbxproj"))?.match(
32394
32563
  ACCENT_BUILD_SETTING
32395
32564
  )?.[1] : void 0) ?? null;
32396
32565
  if (accentName === null && Object.keys(colors).length === 0) return null;
32397
32566
  return { colors, accentName };
32398
32567
  }
32399
- var import_node_fs17, import_node_path18, ACCENT_BUILD_SETTING, PLIST_ACCENT;
32568
+ var import_node_fs19, import_node_path20, ACCENT_BUILD_SETTING, PLIST_ACCENT;
32400
32569
  var init_app_catalog = __esm({
32401
32570
  "cli/app-catalog.ts"() {
32402
32571
  "use strict";
32403
- import_node_fs17 = __toESM(require("node:fs"), 1);
32404
- import_node_path18 = __toESM(require("node:path"), 1);
32572
+ import_node_fs19 = __toESM(require("node:fs"), 1);
32573
+ import_node_path20 = __toESM(require("node:path"), 1);
32405
32574
  init_bundleAssets();
32406
32575
  ACCENT_BUILD_SETTING = /ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = "?([^";]+)"?;/;
32407
32576
  PLIST_ACCENT = /<key>NSAccentColorName<\/key>\s*<string>([^<]+)<\/string>/;
@@ -32411,9 +32580,9 @@ var init_app_catalog = __esm({
32411
32580
  // cli/app-project.ts
32412
32581
  function firstNamedFile(dir, names) {
32413
32582
  for (const name2 of names) {
32414
- const candidate = import_node_path19.default.join(dir, name2);
32583
+ const candidate = import_node_path21.default.join(dir, name2);
32415
32584
  try {
32416
- if (import_node_fs18.default.statSync(candidate).isFile()) return candidate;
32585
+ if (import_node_fs20.default.statSync(candidate).isFile()) return candidate;
32417
32586
  } catch {
32418
32587
  }
32419
32588
  }
@@ -32426,24 +32595,24 @@ function findRNXAppConfig(dir) {
32426
32595
  return firstNamedFile(dir, RNX_APP_CONFIG_NAMES);
32427
32596
  }
32428
32597
  function resolveAppProject(projectRoot) {
32429
- const appDir = import_node_path19.default.resolve(projectRoot);
32598
+ const appDir = import_node_path21.default.resolve(projectRoot);
32430
32599
  try {
32431
- if (!import_node_fs18.default.statSync(appDir).isDirectory()) return null;
32600
+ if (!import_node_fs20.default.statSync(appDir).isDirectory()) return null;
32432
32601
  } catch {
32433
32602
  return null;
32434
32603
  }
32435
32604
  const configPath = findRNXAppConfig(appDir);
32436
- if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs18.default.existsSync(import_node_path19.default.join(appDir, "package.json"))) {
32605
+ if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs20.default.existsSync(import_node_path21.default.join(appDir, "package.json"))) {
32437
32606
  return null;
32438
32607
  }
32439
32608
  return { appDir, configPath };
32440
32609
  }
32441
- var import_node_fs18, import_node_path19, EXPO_APP_CONFIG_NAMES, RNX_APP_CONFIG_NAMES;
32610
+ var import_node_fs20, import_node_path21, EXPO_APP_CONFIG_NAMES, RNX_APP_CONFIG_NAMES;
32442
32611
  var init_app_project = __esm({
32443
32612
  "cli/app-project.ts"() {
32444
32613
  "use strict";
32445
- import_node_fs18 = __toESM(require("node:fs"), 1);
32446
- import_node_path19 = __toESM(require("node:path"), 1);
32614
+ import_node_fs20 = __toESM(require("node:fs"), 1);
32615
+ import_node_path21 = __toESM(require("node:path"), 1);
32447
32616
  EXPO_APP_CONFIG_NAMES = [
32448
32617
  "app.config.js",
32449
32618
  "app.config.ts",
@@ -32472,13 +32641,13 @@ function isRNXConfig(value) {
32472
32641
  }
32473
32642
  function resolveConfigReplacementFiles(config, configPath) {
32474
32643
  if (!config.modules) return config;
32475
- const configDir = import_node_path20.default.dirname(configPath);
32644
+ const configDir = import_node_path22.default.dirname(configPath);
32476
32645
  return {
32477
32646
  ...config,
32478
32647
  modules: Object.fromEntries(
32479
32648
  Object.entries(config.modules).map(([name2, resolution]) => [
32480
32649
  name2,
32481
- resolution && typeof resolution === "object" && "file" in resolution ? { ...resolution, file: import_node_path20.default.resolve(configDir, resolution.file) } : resolution
32650
+ resolution && typeof resolution === "object" && "file" in resolution ? { ...resolution, file: import_node_path22.default.resolve(configDir, resolution.file) } : resolution
32482
32651
  ])
32483
32652
  )
32484
32653
  };
@@ -32508,17 +32677,17 @@ async function resolveRNXAppConfig(startDir = process.cwd()) {
32508
32677
  const project = resolveAppProject(startDir);
32509
32678
  return { project, config: await loadRNXAppConfig(project) };
32510
32679
  }
32511
- var import_node_module, import_node_path20, import_jiti, require2;
32680
+ var import_node_module2, import_node_path22, import_jiti, require2;
32512
32681
  var init_app_config = __esm({
32513
32682
  "cli/app-config.ts"() {
32514
32683
  "use strict";
32515
- import_node_module = require("node:module");
32516
- import_node_path20 = __toESM(require("node:path"), 1);
32684
+ import_node_module2 = require("node:module");
32685
+ import_node_path22 = __toESM(require("node:path"), 1);
32517
32686
  init_src();
32518
32687
  import_jiti = require("jiti");
32519
32688
  init_public_brand();
32520
32689
  init_app_project();
32521
- require2 = (0, import_node_module.createRequire)(__sootsim_import_meta_url);
32690
+ require2 = (0, import_node_module2.createRequire)(__sootsim_import_meta_url);
32522
32691
  }
32523
32692
  });
32524
32693
 
@@ -32570,13 +32739,13 @@ function discoverNativeLinkedAppFonts(opts) {
32570
32739
  scannedFontDirectories++;
32571
32740
  let entries;
32572
32741
  try {
32573
- entries = import_node_fs19.default.readdirSync(current.dir, { withFileTypes: true });
32742
+ entries = import_node_fs21.default.readdirSync(current.dir, { withFileTypes: true });
32574
32743
  } catch {
32575
32744
  continue;
32576
32745
  }
32577
32746
  for (const entry of entries) {
32578
32747
  if (entry.isSymbolicLink()) continue;
32579
- const fullPath = import_node_path21.default.join(current.dir, entry.name);
32748
+ const fullPath = import_node_path23.default.join(current.dir, entry.name);
32580
32749
  if (entry.isDirectory()) {
32581
32750
  if (current.depth < 4 && pending.length < 64 && !entry.name.startsWith(".")) {
32582
32751
  pending.push({ dir: fullPath, depth: current.depth + 1 });
@@ -32584,15 +32753,15 @@ function discoverNativeLinkedAppFonts(opts) {
32584
32753
  continue;
32585
32754
  }
32586
32755
  if (!entry.isFile() || !/\.(ttf|otf|woff|woff2)$/i.test(entry.name)) continue;
32587
- const relative2 = import_node_path21.default.relative(root, fullPath).split(import_node_path21.default.sep).join("/");
32588
- fontPaths.push(import_node_path21.default.posix.join(metroPathPrefix, relative2));
32756
+ const relative2 = import_node_path23.default.relative(root, fullPath).split(import_node_path23.default.sep).join("/");
32757
+ fontPaths.push(import_node_path23.default.posix.join(metroPathPrefix, relative2));
32589
32758
  if (fontPaths.length >= 256) break;
32590
32759
  }
32591
32760
  }
32592
32761
  };
32593
32762
  try {
32594
32763
  const packageJson = JSON.parse(
32595
- import_node_fs19.default.readFileSync(import_node_path21.default.join(selectedAppDir, "package.json"), "utf8")
32764
+ import_node_fs21.default.readFileSync(import_node_path23.default.join(selectedAppDir, "package.json"), "utf8")
32596
32765
  );
32597
32766
  const dependencyNames = /* @__PURE__ */ new Set();
32598
32767
  for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
@@ -32607,24 +32776,24 @@ function discoverNativeLinkedAppFonts(opts) {
32607
32776
  const config = Reflect.get(packageJson, "reactNativeVectorIcons");
32608
32777
  const configuredFontDir = config && typeof config === "object" ? Reflect.get(config, "fontDir") : null;
32609
32778
  const configuredDir = typeof configuredFontDir === "string" ? configuredFontDir : "rnvi-fonts";
32610
- const root = import_node_path21.default.resolve(selectedAppDir, configuredDir);
32611
- const relativeRoot = import_node_path21.default.relative(selectedAppDir, root);
32612
- if (!relativeRoot.startsWith("..") && !import_node_path21.default.isAbsolute(relativeRoot)) {
32613
- collectFontFiles(root, relativeRoot.split(import_node_path21.default.sep).join("/"));
32779
+ const root = import_node_path23.default.resolve(selectedAppDir, configuredDir);
32780
+ const relativeRoot = import_node_path23.default.relative(selectedAppDir, root);
32781
+ if (!relativeRoot.startsWith("..") && !import_node_path23.default.isAbsolute(relativeRoot)) {
32782
+ collectFontFiles(root, relativeRoot.split(import_node_path23.default.sep).join("/"));
32614
32783
  }
32615
- const vectorIconRequire = (0, import_node_module2.createRequire)(import_node_path21.default.join(selectedAppDir, "package.json"));
32784
+ const vectorIconRequire = (0, import_node_module3.createRequire)(import_node_path23.default.join(selectedAppDir, "package.json"));
32616
32785
  for (const dependencyName of vectorIconDependencies) {
32617
32786
  try {
32618
32787
  const entryPath = vectorIconRequire.resolve(dependencyName);
32619
- const dependencyPath = dependencyName.split("/").join(import_node_path21.default.sep);
32620
- const marker = `${import_node_path21.default.sep}node_modules${import_node_path21.default.sep}${dependencyPath}${import_node_path21.default.sep}`;
32788
+ const dependencyPath = dependencyName.split("/").join(import_node_path23.default.sep);
32789
+ const marker = `${import_node_path23.default.sep}node_modules${import_node_path23.default.sep}${dependencyPath}${import_node_path23.default.sep}`;
32621
32790
  const markerIndex = entryPath.lastIndexOf(marker);
32622
32791
  if (markerIndex === -1) continue;
32623
32792
  const packageRoot = entryPath.slice(0, markerIndex + marker.length - 1);
32624
32793
  const fontsDir = dependencyName === "react-native-vector-icons" ? "Fonts" : "fonts";
32625
32794
  collectFontFiles(
32626
- import_node_path21.default.join(packageRoot, fontsDir),
32627
- import_node_path21.default.posix.join("node_modules", dependencyName, fontsDir)
32795
+ import_node_path23.default.join(packageRoot, fontsDir),
32796
+ import_node_path23.default.posix.join("node_modules", dependencyName, fontsDir)
32628
32797
  );
32629
32798
  } catch {
32630
32799
  }
@@ -32633,37 +32802,37 @@ function discoverNativeLinkedAppFonts(opts) {
32633
32802
  } catch {
32634
32803
  }
32635
32804
  try {
32636
- const configPath = import_node_path21.default.join(selectedAppDir, "react-native.config.js");
32637
- if (import_node_fs19.default.statSync(configPath).isFile()) {
32638
- const appConfigRequire = (0, import_node_module2.createRequire)(import_node_path21.default.join(selectedAppDir, "package.json"));
32805
+ const configPath = import_node_path23.default.join(selectedAppDir, "react-native.config.js");
32806
+ if (import_node_fs21.default.statSync(configPath).isFile()) {
32807
+ const appConfigRequire = (0, import_node_module3.createRequire)(import_node_path23.default.join(selectedAppDir, "package.json"));
32639
32808
  const rnConfig = appConfigRequire(configPath);
32640
32809
  const assets = rnConfig && typeof rnConfig === "object" ? Reflect.get(rnConfig, "assets") : null;
32641
32810
  if (Array.isArray(assets)) {
32642
32811
  for (const assetDir of assets) {
32643
32812
  if (typeof assetDir !== "string") continue;
32644
- const root = import_node_path21.default.resolve(selectedAppDir, assetDir);
32645
- const relativeRoot = import_node_path21.default.relative(selectedAppDir, root);
32646
- if (relativeRoot.startsWith("..") || import_node_path21.default.isAbsolute(relativeRoot)) continue;
32647
- collectFontFiles(root, relativeRoot.split(import_node_path21.default.sep).join("/"));
32813
+ const root = import_node_path23.default.resolve(selectedAppDir, assetDir);
32814
+ const relativeRoot = import_node_path23.default.relative(selectedAppDir, root);
32815
+ if (relativeRoot.startsWith("..") || import_node_path23.default.isAbsolute(relativeRoot)) continue;
32816
+ collectFontFiles(root, relativeRoot.split(import_node_path23.default.sep).join("/"));
32648
32817
  }
32649
32818
  }
32650
32819
  }
32651
32820
  } catch {
32652
32821
  }
32653
32822
  if (platform === "ios") {
32654
- const iosDir = import_node_path21.default.join(selectedAppDir, "ios");
32823
+ const iosDir = import_node_path23.default.join(selectedAppDir, "ios");
32655
32824
  try {
32656
- const targetDirs = import_node_fs19.default.readdirSync(iosDir, { withFileTypes: true }).filter(
32825
+ const targetDirs = import_node_fs21.default.readdirSync(iosDir, { withFileTypes: true }).filter(
32657
32826
  (entry) => entry.isDirectory() && !entry.isSymbolicLink() && entry.name !== "Pods" && entry.name !== "build" && !entry.name.startsWith(".")
32658
32827
  ).slice(0, 64);
32659
- const plistPaths = [import_node_path21.default.join(iosDir, "Info.plist")];
32828
+ const plistPaths = [import_node_path23.default.join(iosDir, "Info.plist")];
32660
32829
  for (const entry of targetDirs) {
32661
- plistPaths.push(import_node_path21.default.join(iosDir, entry.name, "Info.plist"));
32830
+ plistPaths.push(import_node_path23.default.join(iosDir, entry.name, "Info.plist"));
32662
32831
  }
32663
32832
  for (const plistPath of plistPaths) {
32664
32833
  let parsed;
32665
32834
  try {
32666
- parsed = parsePlistSource(import_node_fs19.default.readFileSync(plistPath, "utf8"));
32835
+ parsed = parsePlistSource(import_node_fs21.default.readFileSync(plistPath, "utf8"));
32667
32836
  } catch {
32668
32837
  continue;
32669
32838
  }
@@ -32673,21 +32842,21 @@ function discoverNativeLinkedAppFonts(opts) {
32673
32842
  for (const declaredFont of declaredFonts) {
32674
32843
  if (typeof declaredFont !== "string") continue;
32675
32844
  const candidates = [
32676
- import_node_path21.default.resolve(import_node_path21.default.dirname(plistPath), declaredFont),
32677
- import_node_path21.default.resolve(iosDir, declaredFont)
32845
+ import_node_path23.default.resolve(import_node_path23.default.dirname(plistPath), declaredFont),
32846
+ import_node_path23.default.resolve(iosDir, declaredFont)
32678
32847
  ];
32679
32848
  const fontPath = candidates.find((candidate) => {
32680
- const relative2 = import_node_path21.default.relative(selectedAppDir, candidate);
32681
- if (relative2.startsWith("..") || import_node_path21.default.isAbsolute(relative2)) return false;
32849
+ const relative2 = import_node_path23.default.relative(selectedAppDir, candidate);
32850
+ if (relative2.startsWith("..") || import_node_path23.default.isAbsolute(relative2)) return false;
32682
32851
  try {
32683
- return import_node_fs19.default.statSync(candidate).isFile();
32852
+ return import_node_fs21.default.statSync(candidate).isFile();
32684
32853
  } catch {
32685
32854
  return false;
32686
32855
  }
32687
32856
  });
32688
32857
  if (!fontPath) continue;
32689
32858
  fontPaths.push(
32690
- import_node_path21.default.relative(selectedAppDir, fontPath).split(import_node_path21.default.sep).join("/")
32859
+ import_node_path23.default.relative(selectedAppDir, fontPath).split(import_node_path23.default.sep).join("/")
32691
32860
  );
32692
32861
  }
32693
32862
  }
@@ -32726,19 +32895,19 @@ async function resolveMetroProjectRoot(bundleUrl, opts) {
32726
32895
  function encodeAppFontsWire(specs) {
32727
32896
  return specs.map((spec) => `${encodeURIComponent(spec.url)}::${encodeURIComponent(spec.family)}`).join(",");
32728
32897
  }
32729
- var import_node_fs19, import_node_module2, import_node_path21, import_config_plugin_fonts, require3;
32898
+ var import_node_fs21, import_node_module3, import_node_path23, import_config_plugin_fonts, require3;
32730
32899
  var init_app_fonts = __esm({
32731
32900
  "cli/app-fonts.ts"() {
32732
32901
  "use strict";
32733
- import_node_fs19 = __toESM(require("node:fs"), 1);
32734
- import_node_module2 = require("node:module");
32735
- import_node_path21 = __toESM(require("node:path"), 1);
32902
+ import_node_fs21 = __toESM(require("node:fs"), 1);
32903
+ import_node_module3 = require("node:module");
32904
+ import_node_path23 = __toESM(require("node:path"), 1);
32736
32905
  import_config_plugin_fonts = require("peach-engine/engine/config-plugin-fonts");
32737
32906
  init_dev_server_scanner();
32738
32907
  init_backend_origin();
32739
32908
  init_plist();
32740
32909
  init_app_project();
32741
- require3 = (0, import_node_module2.createRequire)(__sootsim_import_meta_url);
32910
+ require3 = (0, import_node_module3.createRequire)(__sootsim_import_meta_url);
32742
32911
  }
32743
32912
  });
32744
32913
 
@@ -32807,15 +32976,15 @@ function splashImageUrls(spec) {
32807
32976
  if (spec.dark?.backgroundImageUrl) urls.add(spec.dark.backgroundImageUrl);
32808
32977
  return [...urls];
32809
32978
  }
32810
- var import_node_module3, import_config_plugin_splash, require4;
32979
+ var import_node_module4, import_config_plugin_splash, require4;
32811
32980
  var init_app_splash = __esm({
32812
32981
  "cli/app-splash.ts"() {
32813
32982
  "use strict";
32814
- import_node_module3 = require("node:module");
32983
+ import_node_module4 = require("node:module");
32815
32984
  import_config_plugin_splash = require("peach-engine/engine/config-plugin-splash");
32816
32985
  init_backend_origin();
32817
32986
  init_app_project();
32818
- require4 = (0, import_node_module3.createRequire)(__sootsim_import_meta_url);
32987
+ require4 = (0, import_node_module4.createRequire)(__sootsim_import_meta_url);
32819
32988
  }
32820
32989
  });
32821
32990
 
@@ -33911,15 +34080,16 @@ var init_internal_child = __esm({
33911
34080
  // cli/drivers/playwright-provisioning.ts
33912
34081
  function resolvePlaywright() {
33913
34082
  const roots = [`${process.cwd()}/`];
33914
- if (process.argv[1]) roots.push(process.argv[1]);
34083
+ const self = rnxSelfInvocation();
34084
+ roots.push(self.prefixArgs[0] ?? self.executable);
33915
34085
  for (const root of roots) {
33916
34086
  try {
33917
- const requireFromRoot = (0, import_node_module4.createRequire)(root);
34087
+ const requireFromRoot = (0, import_node_module5.createRequire)(root);
33918
34088
  for (const spec of ["playwright", "playwright-chromium", "playwright-core"]) {
33919
34089
  try {
33920
34090
  const modulePath = requireFromRoot.resolve(spec);
33921
34091
  const packageJsonPath = requireFromRoot.resolve(`${spec}/package.json`);
33922
- const parsed = JSON.parse((0, import_node_fs20.readFileSync)(packageJsonPath, "utf8"));
34092
+ const parsed = JSON.parse((0, import_node_fs22.readFileSync)(packageJsonPath, "utf8"));
33923
34093
  if (!isRecord(parsed) || typeof parsed.version !== "string") continue;
33924
34094
  const bin = parsed.bin;
33925
34095
  let binPath = null;
@@ -33931,8 +34101,8 @@ function resolvePlaywright() {
33931
34101
  binPath = typeof named === "string" ? named : typeof first === "string" ? first : null;
33932
34102
  }
33933
34103
  if (!binPath) continue;
33934
- const cliPath = (0, import_node_path22.join)((0, import_node_path22.dirname)(packageJsonPath), binPath);
33935
- if (!(0, import_node_fs20.existsSync)(cliPath)) continue;
34104
+ const cliPath = (0, import_node_path24.join)((0, import_node_path24.dirname)(packageJsonPath), binPath);
34105
+ if (!(0, import_node_fs22.existsSync)(cliPath)) continue;
33936
34106
  return {
33937
34107
  spec,
33938
34108
  modulePath,
@@ -33976,7 +34146,7 @@ function probeBrowser(resolved, env) {
33976
34146
  return {
33977
34147
  ok: true,
33978
34148
  executablePath: parsed.executablePath,
33979
- exists: parsed.exists && (0, import_node_fs20.existsSync)(parsed.executablePath)
34149
+ exists: parsed.exists && (0, import_node_fs22.existsSync)(parsed.executablePath)
33980
34150
  };
33981
34151
  } catch (error) {
33982
34152
  return {
@@ -34027,17 +34197,17 @@ ${detail}` : ""}`
34027
34197
  }
34028
34198
  return { ok: true, executablePath: after.executablePath, installed: true };
34029
34199
  }
34030
- var import_node_child_process6, import_node_fs20, import_node_module4, import_node_path22;
34200
+ var import_node_child_process6, import_node_fs22, import_node_module5, import_node_path24;
34031
34201
  var init_playwright_provisioning = __esm({
34032
34202
  "cli/drivers/playwright-provisioning.ts"() {
34033
34203
  "use strict";
34034
34204
  import_node_child_process6 = require("node:child_process");
34035
- import_node_fs20 = require("node:fs");
34036
- import_node_module4 = require("node:module");
34037
- import_node_path22 = require("node:path");
34205
+ import_node_fs22 = require("node:fs");
34206
+ import_node_module5 = require("node:module");
34207
+ import_node_path24 = require("node:path");
34208
+ init_is_record();
34038
34209
  init_internal_child();
34039
34210
  init_self_invocation();
34040
- init_is_record();
34041
34211
  }
34042
34212
  });
34043
34213
 
@@ -34703,7 +34873,7 @@ function assertExplicitBridgeLock(preferredPort, explicitPort, lock) {
34703
34873
  async function ensureRuntimeForOpen() {
34704
34874
  if (resolveDevCheckoutRuntimeRoot()) return;
34705
34875
  const active = readActiveRuntime();
34706
- if (active && import_node_fs21.default.existsSync(runtimeDir(active))) return;
34876
+ if (active && import_node_fs23.default.existsSync(runtimeDir(active))) return;
34707
34877
  console.error(" installing peach engine runtime...");
34708
34878
  const { runRuntime: runRuntime2 } = await Promise.resolve().then(() => (init_runtime(), runtime_exports));
34709
34879
  await runRuntime2(["install"], {});
@@ -34857,10 +35027,10 @@ async function buildResolvedBundleOpenUrl(target, baseUrl, opts = {}) {
34857
35027
  }
34858
35028
  function normalizeReplacementPath(filePath) {
34859
35029
  if (filePath.startsWith("~/")) {
34860
- return import_node_path23.default.join(import_node_os3.default.homedir(), filePath.slice(2));
35030
+ return import_node_path25.default.join(import_node_os3.default.homedir(), filePath.slice(2));
34861
35031
  }
34862
- if (import_node_path23.default.isAbsolute(filePath)) return filePath;
34863
- return import_node_path23.default.resolve(process.cwd(), filePath);
35032
+ if (import_node_path25.default.isAbsolute(filePath)) return filePath;
35033
+ return import_node_path25.default.resolve(process.cwd(), filePath);
34864
35034
  }
34865
35035
  function parseOpenRuntimeConfig(args) {
34866
35036
  const modules = {};
@@ -34879,7 +35049,7 @@ function parseOpenRuntimeConfig(args) {
34879
35049
  }
34880
35050
  const moduleName = raw.slice(0, eqIndex).trim();
34881
35051
  const filePath = normalizeReplacementPath(raw.slice(eqIndex + 1).trim());
34882
- if (!import_node_fs21.default.existsSync(filePath)) {
35052
+ if (!import_node_fs23.default.existsSync(filePath)) {
34883
35053
  console.error(` peach open: replacement file not found: ${filePath}`);
34884
35054
  rnxExit(1);
34885
35055
  }
@@ -34910,7 +35080,7 @@ async function resolveAppProjectForBundleUrl(bundleUrl) {
34910
35080
  return advertisedRoot ? resolveAppProject(advertisedRoot) : null;
34911
35081
  }
34912
35082
  async function ensureConfiguredRuntime(version) {
34913
- if (import_node_fs21.default.existsSync(import_node_path23.default.join(runtimeDir(version), "index.html"))) return;
35083
+ if (import_node_fs23.default.existsSync(import_node_path25.default.join(runtimeDir(version), "index.html"))) return;
34914
35084
  console.error(` installing configured engine runtime ${version}...`);
34915
35085
  const result = await rnxRuntime.install({
34916
35086
  version,
@@ -35028,6 +35198,22 @@ async function stampAppSplash(urlStr, project) {
35028
35198
  return urlStr;
35029
35199
  }
35030
35200
  }
35201
+ function stampNativeFirebaseConfig(urlStr, project) {
35202
+ const url = new URL(urlStr);
35203
+ if (!url.searchParams.get("bundle") || !project) return urlStr;
35204
+ try {
35205
+ url.searchParams.set(
35206
+ "nativeFirebase",
35207
+ JSON.stringify(readNativeFirebaseConfig(project.appDir))
35208
+ );
35209
+ } catch (error) {
35210
+ url.searchParams.set(
35211
+ "nativeFirebase",
35212
+ JSON.stringify({ error: error instanceof Error ? error.message : String(error) })
35213
+ );
35214
+ }
35215
+ return url.toString();
35216
+ }
35031
35217
  function stampAppCatalog(urlStr, project) {
35032
35218
  try {
35033
35219
  const url = new URL(urlStr);
@@ -35062,8 +35248,11 @@ function bundleUrlOf(urlStr) {
35062
35248
  async function buildShellUrl(target, baseUrl = resolveDefaultShellBaseUrl(), opts = {}) {
35063
35249
  const resolved = await resolveShellUrl(target, baseUrl, opts);
35064
35250
  const project = opts.project ?? await resolveAppProjectForBundleUrl(bundleUrlOf(resolved));
35065
- const stamped = stampAppCatalog(
35066
- await stampAppSplash(await stampAppFonts(resolved, project), project),
35251
+ const stamped = stampNativeFirebaseConfig(
35252
+ stampAppCatalog(
35253
+ await stampAppSplash(await stampAppFonts(resolved, project), project),
35254
+ project
35255
+ ),
35067
35256
  project
35068
35257
  );
35069
35258
  if (!opts.device && !opts.proof && !opts.posture && !opts.osVersion && !opts.env?.length)
@@ -35089,6 +35278,7 @@ function deriveCurrentSimBaseUrl(sim, fallback) {
35089
35278
  url.searchParams.delete("appFonts");
35090
35279
  url.searchParams.delete("appSplash");
35091
35280
  url.searchParams.delete("appCatalog");
35281
+ url.searchParams.delete("nativeFirebase");
35092
35282
  return url.toString();
35093
35283
  } catch {
35094
35284
  return fallback;
@@ -35204,7 +35394,7 @@ function playwrightHostPidForSim(sim) {
35204
35394
  function signalDriverLaunchConnected(result) {
35205
35395
  if (!result.connectAckFile) return false;
35206
35396
  try {
35207
- import_node_fs21.default.writeFileSync(
35397
+ import_node_fs23.default.writeFileSync(
35208
35398
  result.connectAckFile,
35209
35399
  `${JSON.stringify({ connectedAt: Date.now(), pid: result.pid ?? null })}
35210
35400
  `,
@@ -35218,19 +35408,19 @@ function signalDriverLaunchConnected(result) {
35218
35408
  function printDriverDiagnosticLogTail(result) {
35219
35409
  if (!result.diagnosticLogPath) return;
35220
35410
  try {
35221
- const detail = import_node_fs21.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
35411
+ const detail = import_node_fs23.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
35222
35412
  if (!detail) {
35223
- console.error(` ${import_node_path23.default.basename(result.diagnosticLogPath)} was empty`);
35413
+ console.error(` ${import_node_path25.default.basename(result.diagnosticLogPath)} was empty`);
35224
35414
  return;
35225
35415
  }
35226
35416
  const shown = detail.length > 4e3 ? `\u2026
35227
35417
  ${detail.slice(-4e3)}` : detail;
35228
- console.error(` ${import_node_path23.default.basename(result.diagnosticLogPath)}:`);
35418
+ console.error(` ${import_node_path25.default.basename(result.diagnosticLogPath)}:`);
35229
35419
  console.error(shown);
35230
35420
  } catch (err) {
35231
35421
  const message = err instanceof Error ? err.message : String(err);
35232
35422
  console.error(
35233
- ` failed to read ${import_node_path23.default.basename(result.diagnosticLogPath)}: ${message}`
35423
+ ` failed to read ${import_node_path25.default.basename(result.diagnosticLogPath)}: ${message}`
35234
35424
  );
35235
35425
  }
35236
35426
  }
@@ -35603,9 +35793,9 @@ async function runOpenCommand(args, opts = {}) {
35603
35793
  }
35604
35794
  const hotMode = hotFlag ? true : noHmrFlag ? false : driverId !== "playwright";
35605
35795
  const deviceArg = args.find((_, i) => args[i - 1] === "--device");
35606
- if (deviceArg && !(deviceArg in import_settings.devices)) {
35796
+ if (deviceArg && !(deviceArg in import_devices2.devices)) {
35607
35797
  console.error(` peach open: unknown device "${deviceArg}"`);
35608
- console.error(` known devices: ${(0, import_settings.listSelectableDeviceModels)().join(", ")}`);
35798
+ console.error(` known devices: ${(0, import_devices2.listSelectableDeviceModels)().join(", ")}`);
35609
35799
  rnxExit(1);
35610
35800
  }
35611
35801
  const postureArg = args.find((_, i) => args[i - 1] === "--posture");
@@ -35613,7 +35803,7 @@ async function runOpenCommand(args, opts = {}) {
35613
35803
  console.error(` peach open: --posture must be open or closed, got "${postureArg}"`);
35614
35804
  rnxExit(1);
35615
35805
  }
35616
- const deviceFitBox = deviceArg ? (0, import_settings.resolveDeviceFitBox)(
35806
+ const deviceFitBox = deviceArg ? (0, import_devices2.resolveDeviceFitBox)(
35617
35807
  deviceArg,
35618
35808
  false,
35619
35809
  void 0,
@@ -36160,16 +36350,16 @@ async function runCloseCommand(args, opts = {}) {
36160
36350
  bridge.close();
36161
36351
  }
36162
36352
  }
36163
- var import_node_child_process7, import_node_fs21, import_node_os3, import_node_path23, import_app_catalog_wire, import_settings, import_ws4, DEFAULT_DRIVER_CONNECT_TIMEOUT_MS, DRIVER_CONNECT_INTERVAL_MS, DRIVER_CONNECT_HOST_GRACE_MS, OPEN_BRIDGE_SPAWN_TIMEOUT_MS, SHELL_APPS, DEV_BUNDLE_RESOLVE_ATTEMPTS, DEV_BUNDLE_RESOLVE_INTERVAL_MS;
36353
+ var import_node_child_process7, import_node_fs23, import_node_os3, import_node_path25, import_app_catalog_wire, import_devices2, import_ws4, DEFAULT_DRIVER_CONNECT_TIMEOUT_MS, DRIVER_CONNECT_INTERVAL_MS, DRIVER_CONNECT_HOST_GRACE_MS, OPEN_BRIDGE_SPAWN_TIMEOUT_MS, SHELL_APPS, DEV_BUNDLE_RESOLVE_ATTEMPTS, DEV_BUNDLE_RESOLVE_INTERVAL_MS;
36164
36354
  var init_control = __esm({
36165
36355
  "cli/commands/control.ts"() {
36166
36356
  "use strict";
36167
36357
  import_node_child_process7 = require("node:child_process");
36168
- import_node_fs21 = __toESM(require("node:fs"), 1);
36358
+ import_node_fs23 = __toESM(require("node:fs"), 1);
36169
36359
  import_node_os3 = __toESM(require("node:os"), 1);
36170
- import_node_path23 = __toESM(require("node:path"), 1);
36360
+ import_node_path25 = __toESM(require("node:path"), 1);
36171
36361
  import_app_catalog_wire = require("peach-engine/engine/app-catalog-wire");
36172
- import_settings = require("peach-engine/settings");
36362
+ import_devices2 = require("peach-engine/settings/devices");
36173
36363
  import_ws4 = require("ws");
36174
36364
  init_dev_server_scanner();
36175
36365
  init_app_url();
@@ -36180,6 +36370,7 @@ var init_control = __esm({
36180
36370
  init_flow_timeout_scale();
36181
36371
  init_home_paths();
36182
36372
  init_native_dev_bundle_url();
36373
+ init_native_firebase_config();
36183
36374
  init_profiles();
36184
36375
  init_public_brand();
36185
36376
  init_runtime_assets();
@@ -36309,14 +36500,17 @@ var require_proof_frame = __commonJS({
36309
36500
 
36310
36501
  // src/screenshots/frame-compose.ts
36311
36502
  function getFramedScreenshotLayout(model) {
36312
- const spec = (0, import_settings2.getDevice)(model);
36503
+ const spec = (0, import_devices3.getDevice)(model);
36313
36504
  const renderScale = spec.scale;
36314
36505
  const isLegacyHardware = spec.statusBarHeight > 0 && !spec.dynamicIsland && spec.homeIndicatorHeight === 0 && // home-button iphones have a square display, so any corner reads 0
36315
36506
  spec.cornerRadius.topLeft === 0;
36316
36507
  const logicalScreenWidth = spec.width;
36317
36508
  const logicalScreenHeight = spec.height;
36318
- const modernGeometry = (0, import_settings2.resolveDeviceFrameGeometry)(spec);
36319
- const housingLayers = isLegacyHardware ? null : (0, import_duo_housing.resolveHousingArt)(spec.posture ?? "closed", modernGeometry);
36509
+ const modernGeometry = (0, import_devices3.resolveDeviceFrameGeometry)(spec);
36510
+ const housingLayers = isLegacyHardware ? null : (0, import_duo_housing.resolveHousingArt)(spec.posture ?? "closed", modernGeometry, {
36511
+ orientation: spec.orientation,
36512
+ landscapeSide: spec.landscapeSide
36513
+ });
36320
36514
  const logicalFrameWidth = isLegacyHardware ? spec.width + import_chrome_metrics.LEGACY_SIDE_BEZEL * 2 : modernGeometry.frameWidth;
36321
36515
  const logicalFrameHeight = isLegacyHardware ? spec.height + import_chrome_metrics.LEGACY_TOP_BEZEL + import_chrome_metrics.LEGACY_BOTTOM_BEZEL : modernGeometry.frameHeight;
36322
36516
  const logicalFrameLeft = import_chrome_metrics.SIDE_BUTTON_RESERVE;
@@ -36325,7 +36519,7 @@ function getFramedScreenshotLayout(model) {
36325
36519
  const logicalOuterHeight = logicalFrameHeight + FRAME_OUTLINE_BLEED * 2;
36326
36520
  const logicalScreenLeft = logicalFrameLeft + (isLegacyHardware ? import_chrome_metrics.LEGACY_SIDE_BEZEL : modernGeometry.screenLeft);
36327
36521
  const logicalScreenTop = isLegacyHardware ? import_chrome_metrics.LEGACY_TOP_BEZEL : modernGeometry.screenTop;
36328
- const logicalFrameRadius = isLegacyHardware ? (0, import_settings2.cornerRadii)(import_chrome_metrics.LEGACY_FRAME_RADIUS) : modernGeometry.frameRadius;
36522
+ const logicalFrameRadius = isLegacyHardware ? (0, import_devices3.cornerRadii)(import_chrome_metrics.LEGACY_FRAME_RADIUS) : modernGeometry.frameRadius;
36329
36523
  const buttons = [
36330
36524
  {
36331
36525
  side: spec.hardwareButtons.lock.side,
@@ -36367,7 +36561,7 @@ function getFramedScreenshotLayout(model) {
36367
36561
  logicalScreenHeight,
36368
36562
  logicalScreenLeft,
36369
36563
  logicalScreenTop,
36370
- logicalScreenRadius: isLegacyHardware ? (0, import_settings2.cornerRadii)(0) : modernGeometry.screenRadius,
36564
+ logicalScreenRadius: isLegacyHardware ? (0, import_devices3.cornerRadii)(0) : modernGeometry.screenRadius,
36371
36565
  logicalFrameRadius,
36372
36566
  housingLayers,
36373
36567
  // the housing paints its own body layer, shadow included: its frame box is
@@ -36425,7 +36619,7 @@ function renderFrameHtml(layout, rawImageDataUrl) {
36425
36619
  style="
36426
36620
  position:absolute;
36427
36621
  inset:0;
36428
- border-radius:${(0, import_settings2.formatCornerRadii)(layout.logicalFrameRadius)};
36622
+ border-radius:${(0, import_devices3.formatCornerRadii)(layout.logicalFrameRadius)};
36429
36623
  box-shadow:${layout.metallicRingShadow};
36430
36624
  pointer-events:none;
36431
36625
  z-index:10;
@@ -36546,7 +36740,7 @@ function renderFrameHtml(layout, rawImageDataUrl) {
36546
36740
  left:${layout.logicalFrameLeft}px;
36547
36741
  width:${layout.logicalFrameWidth}px;
36548
36742
  height:${layout.logicalFrameHeight}px;
36549
- border-radius:${layout.housingLayers ? "0" : (0, import_settings2.formatCornerRadii)(layout.logicalFrameRadius)};
36743
+ border-radius:${layout.housingLayers ? "0" : (0, import_devices3.formatCornerRadii)(layout.logicalFrameRadius)};
36550
36744
  box-sizing:border-box;
36551
36745
  overflow:visible;
36552
36746
  background:${layout.frameBackground};
@@ -36565,7 +36759,7 @@ function renderFrameHtml(layout, rawImageDataUrl) {
36565
36759
  left:${layout.logicalScreenLeft - layout.logicalFrameLeft}px;
36566
36760
  width:${layout.logicalScreenWidth}px;
36567
36761
  height:${layout.logicalScreenHeight}px;
36568
- border-radius:${(0, import_settings2.formatCornerRadii)(layout.logicalScreenRadius)};
36762
+ border-radius:${(0, import_devices3.formatCornerRadii)(layout.logicalScreenRadius)};
36569
36763
  overflow:hidden;
36570
36764
  background:#000;
36571
36765
  "
@@ -36637,13 +36831,13 @@ async function createFramedScreenshotComposer(browser) {
36637
36831
  }
36638
36832
  };
36639
36833
  }
36640
- var import_chrome_metrics, import_duo_housing, import_settings2, METALLIC_RING_SHADOW, FRAME_OUTLINE_BLEED;
36834
+ var import_chrome_metrics, import_duo_housing, import_devices3, METALLIC_RING_SHADOW, FRAME_OUTLINE_BLEED;
36641
36835
  var init_frame_compose = __esm({
36642
36836
  "src/screenshots/frame-compose.ts"() {
36643
36837
  "use strict";
36644
36838
  import_chrome_metrics = require("peach-engine/ios/chrome-metrics");
36645
36839
  import_duo_housing = require("peach-engine/ios/duo-housing");
36646
- import_settings2 = require("peach-engine/settings");
36840
+ import_devices3 = require("peach-engine/settings/devices");
36647
36841
  init_reap_browser();
36648
36842
  METALLIC_RING_SHADOW = "inset 0 0 0 0.5px #000, inset 0 0 0 2px #757575, inset 0 0 0 5px #212121";
36649
36843
  FRAME_OUTLINE_BLEED = 1;
@@ -37287,16 +37481,16 @@ function readRecentShellAppId(state) {
37287
37481
  }
37288
37482
  function resolveProjectRelativeFlowBase(flowDir) {
37289
37483
  if (!flowDir) return process.cwd();
37290
- return path19.basename(flowDir) === ".maestro" ? path19.dirname(flowDir) : flowDir;
37484
+ return path20.basename(flowDir) === ".maestro" ? path20.dirname(flowDir) : flowDir;
37291
37485
  }
37292
37486
  function resolveFlowScreenshotPathWithMode(screenshotDir, name2, opts) {
37293
37487
  const normalizedName = name2.endsWith(".png") ? name2 : `${name2}.png`;
37294
- if (path19.isAbsolute(normalizedName)) return normalizedName;
37488
+ if (path20.isAbsolute(normalizedName)) return normalizedName;
37295
37489
  if (opts.mode === "flow" && /[\\/]/.test(normalizedName)) {
37296
37490
  const baseDir = normalizedName.startsWith("./") || normalizedName.startsWith("../") ? opts.flowDir ?? process.cwd() : resolveProjectRelativeFlowBase(opts.flowDir);
37297
- return path19.resolve(baseDir, normalizedName);
37491
+ return path20.resolve(baseDir, normalizedName);
37298
37492
  }
37299
- return path19.join(screenshotDir, normalizedName);
37493
+ return path20.join(screenshotDir, normalizedName);
37300
37494
  }
37301
37495
  function normalizeFlowScreenshotSpec(step) {
37302
37496
  if (typeof step === "string") {
@@ -37353,13 +37547,13 @@ function parseFlowSteps(content) {
37353
37547
  const parsed = import_yaml.default.parse(candidate);
37354
37548
  return Array.isArray(parsed) ? parsed : [];
37355
37549
  }
37356
- var fs19, path19, import_settings3, import_yaml, import_proof_frame, DEFAULT_TIMEOUT, ASSERTION_LOOKUP_MS, FLOW_TIMEOUT_SCALE, scaledTimeout, MAESTRO_SELECTOR_KEYS, SELECTOR_FILTER_KEYS, UNSUPPORTED_SELECTOR_KEYS, MAESTRO_PERMISSION_KINDS, MAESTRO_PERMISSION_STATUS, SootSimBridgeFlowRunner;
37550
+ var fs20, path20, import_devices4, import_yaml, import_proof_frame, DEFAULT_TIMEOUT, ASSERTION_LOOKUP_MS, FLOW_TIMEOUT_SCALE, scaledTimeout, MAESTRO_SELECTOR_KEYS, SELECTOR_FILTER_KEYS, UNSUPPORTED_SELECTOR_KEYS, MAESTRO_PERMISSION_KINDS, MAESTRO_PERMISSION_STATUS, SootSimBridgeFlowRunner;
37357
37551
  var init_bridge_flow_runner = __esm({
37358
37552
  "cli/bridge-flow-runner.ts"() {
37359
37553
  "use strict";
37360
- fs19 = __toESM(require("fs"), 1);
37361
- path19 = __toESM(require("path"), 1);
37362
- import_settings3 = require("peach-engine/settings");
37554
+ fs20 = __toESM(require("fs"), 1);
37555
+ path20 = __toESM(require("path"), 1);
37556
+ import_devices4 = require("peach-engine/settings/devices");
37363
37557
  import_yaml = __toESM(require("yaml"), 1);
37364
37558
  import_proof_frame = __toESM(require_proof_frame(), 1);
37365
37559
  init_flow_timeout_scale();
@@ -37675,14 +37869,14 @@ var init_bridge_flow_runner = __esm({
37675
37869
  if (chunks.length === 0) {
37676
37870
  throw new Error("recording requested but no video buffer was produced");
37677
37871
  }
37678
- fs19.mkdirSync(this.opts.recordingOutputDir, { recursive: true });
37872
+ fs20.mkdirSync(this.opts.recordingOutputDir, { recursive: true });
37679
37873
  const videoMime = stopped.mime || this.opts.recordingFormat || "video/webm";
37680
37874
  const extension = videoMime.includes("mp4") ? "mp4" : "webm";
37681
- const outputPath = path19.join(
37875
+ const outputPath = path20.join(
37682
37876
  this.opts.recordingOutputDir,
37683
37877
  `peach-${Date.now()}.${extension}`
37684
37878
  );
37685
- fs19.writeFileSync(outputPath, Buffer.concat(chunks));
37879
+ fs20.writeFileSync(outputPath, Buffer.concat(chunks));
37686
37880
  return outputPath;
37687
37881
  }
37688
37882
  async waitForRecordingTail(opts) {
@@ -38042,6 +38236,7 @@ var init_bridge_flow_runner = __esm({
38042
38236
  }
38043
38237
  async tapOn(target) {
38044
38238
  const opts = typeof target === "string" ? { text: target } : target;
38239
+ await this.waitForAppToSettle();
38045
38240
  const repeat = Math.max(1, Math.trunc(opts.repeat ?? 1));
38046
38241
  const repeatDelay = opts.delay !== void 0 && opts.delay >= 0 ? opts.delay : 100;
38047
38242
  const repeatTaps = async (tappedAtMs, tapAgain) => {
@@ -38314,7 +38509,7 @@ var init_bridge_flow_runner = __esm({
38314
38509
  flowDir: this.opts.flowDir
38315
38510
  }
38316
38511
  );
38317
- fs19.mkdirSync(path19.dirname(outputPath), { recursive: true });
38512
+ fs20.mkdirSync(path20.dirname(outputPath), { recursive: true });
38318
38513
  const layers = spec.layers ?? this.opts.screenshotLayers;
38319
38514
  const screenshotRequest = {
38320
38515
  type: "screenshot",
@@ -38349,11 +38544,11 @@ var init_bridge_flow_runner = __esm({
38349
38544
  throw new Error("could not read current device model for framed screenshot");
38350
38545
  }
38351
38546
  const framed = await composeFramedScreenshot(rawBuffer, model);
38352
- fs19.writeFileSync(outputPath, framed);
38547
+ fs20.writeFileSync(outputPath, framed);
38353
38548
  console.log(`[flow] screenshot: ${outputPath} (frame: ${model})`);
38354
38549
  return outputPath;
38355
38550
  }
38356
- fs19.writeFileSync(outputPath, rawBuffer);
38551
+ fs20.writeFileSync(outputPath, rawBuffer);
38357
38552
  console.log(`[flow] screenshot: ${outputPath}`);
38358
38553
  return outputPath;
38359
38554
  }
@@ -38365,7 +38560,7 @@ var init_bridge_flow_runner = __esm({
38365
38560
  args: []
38366
38561
  });
38367
38562
  const model = settings && typeof settings.deviceModel === "string" ? settings.deviceModel : null;
38368
- if (!model || !(model in import_settings3.devices)) return null;
38563
+ if (!model || !(model in import_devices4.devices)) return null;
38369
38564
  return model;
38370
38565
  }
38371
38566
  async captureScreenshot(outputPath) {
@@ -38375,10 +38570,10 @@ var init_bridge_flow_runner = __esm({
38375
38570
  // + a11y tree + console errors/warnings + failed network requests +
38376
38571
  // shell state, all under one directory so post-mortem is one grep away.
38377
38572
  async captureFailureBundle(outputDir, context) {
38378
- fs19.mkdirSync(outputDir, { recursive: true });
38573
+ fs20.mkdirSync(outputDir, { recursive: true });
38379
38574
  const write = (name2, content) => {
38380
38575
  try {
38381
- fs19.writeFileSync(path19.join(outputDir, name2), content);
38576
+ fs20.writeFileSync(path20.join(outputDir, name2), content);
38382
38577
  } catch {
38383
38578
  }
38384
38579
  };
@@ -38825,14 +39020,14 @@ var init_bridge_flow_runner = __esm({
38825
39020
  );
38826
39021
  }
38827
39022
  }
38828
- // maestro waits after every input for the screen to stop changing, up to
39023
+ // maestro waits around taps for the screen to stop changing, up to
38829
39024
  // its ios driver's 3 s settle budget, comparing screenshots, so an action
38830
39025
  // still in flight (a submit spinner, a sheet closing) finishes before the
38831
39026
  // next command resolves its target. an app can take a few hundred ms to
38832
39027
  // react to a press, so stillness must hold about half a second, and it
38833
39028
  // counts painted frames because a spinner repaints without moving layout.
38834
39029
  // a screen that never stops proceeds when the budget runs out, as there.
38835
- async settleAfterInput() {
39030
+ async waitForAppToSettle() {
38836
39031
  await waitForSootsimIdle({
38837
39032
  bridge: this.bridge,
38838
39033
  simId: this.opts.simId,
@@ -38842,6 +39037,9 @@ var init_bridge_flow_runner = __esm({
38842
39037
  strict: true,
38843
39038
  paintStill: true
38844
39039
  });
39040
+ }
39041
+ async settleAfterInput() {
39042
+ await this.waitForAppToSettle();
38845
39043
  this.lastInteractionAt = Date.now();
38846
39044
  }
38847
39045
  async waitForVisualSettle() {
@@ -38973,8 +39171,8 @@ var init_bridge_flow_runner = __esm({
38973
39171
  runScript(target) {
38974
39172
  const spec = typeof target === "string" ? { file: target } : target;
38975
39173
  if (!spec.file) throw new Error("runScript requires a file");
38976
- const scriptPath = path19.resolve(this.opts.flowDir, spec.file);
38977
- const source = fs19.readFileSync(scriptPath, "utf8");
39174
+ const scriptPath = path20.resolve(this.opts.flowDir, spec.file);
39175
+ const source = fs20.readFileSync(scriptPath, "utf8");
38978
39176
  this.js.evaluate(source, { env: spec.env });
38979
39177
  console.log(`[flow] runScript: ${spec.file} done`);
38980
39178
  }
@@ -39331,10 +39529,10 @@ var init_bridge_flow_runner = __esm({
39331
39529
  for (const [k, v] of Object.entries(env)) this.js.putEnv(k, v);
39332
39530
  }
39333
39531
  if (file) {
39334
- const flowPath = path19.resolve(this.opts.flowDir, file);
39335
- const flowContent = fs19.readFileSync(flowPath, "utf8");
39532
+ const flowPath = path20.resolve(this.opts.flowDir, file);
39533
+ const flowContent = fs20.readFileSync(flowPath, "utf8");
39336
39534
  const previousDir = this.opts.flowDir;
39337
- this.opts.flowDir = path19.dirname(flowPath);
39535
+ this.opts.flowDir = path20.dirname(flowPath);
39338
39536
  try {
39339
39537
  await this.runFlow(parseFlowSteps(flowContent));
39340
39538
  } finally {
@@ -39505,8 +39703,8 @@ __export(registry_exports, {
39505
39703
  skillRegistry: () => skillRegistry
39506
39704
  });
39507
39705
  module.exports = __toCommonJS(registry_exports);
39508
- var fs20 = __toESM(require("fs"), 1);
39509
- var path20 = __toESM(require("path"), 1);
39706
+ var fs21 = __toESM(require("fs"), 1);
39707
+ var path21 = __toESM(require("path"), 1);
39510
39708
 
39511
39709
  // src/skills/builtin/a11y-review.ts
39512
39710
  init_reap_browser();
@@ -39525,8 +39723,8 @@ var skill = {
39525
39723
  },
39526
39724
  async execute(params, context) {
39527
39725
  const { chromium } = await import("playwright");
39528
- const path21 = await import("path");
39529
- const fs21 = await import("fs");
39726
+ const path22 = await import("path");
39727
+ const fs22 = await import("fs");
39530
39728
  const browser = await launchReapedChrome((options) => chromium.launch(options), {
39531
39729
  headless: true
39532
39730
  });
@@ -39581,9 +39779,9 @@ var skill = {
39581
39779
  warnings: issues.filter((i) => i.severity === "warning").length,
39582
39780
  tree: typeof tree === "string" ? tree : JSON.stringify(tree)
39583
39781
  };
39584
- const reportPath = path21.join(context.outputDir, "a11y-report.json");
39585
- fs21.mkdirSync(path21.dirname(reportPath), { recursive: true });
39586
- fs21.writeFileSync(reportPath, JSON.stringify(report, null, 2));
39782
+ const reportPath = path22.join(context.outputDir, "a11y-report.json");
39783
+ fs22.mkdirSync(path22.dirname(reportPath), { recursive: true });
39784
+ fs22.writeFileSync(reportPath, JSON.stringify(report, null, 2));
39587
39785
  return {
39588
39786
  success: issues.filter((i) => i.severity === "error").length === 0,
39589
39787
  message: `${report.errors} errors, ${report.warnings} warnings across ${report.nodeCount} nodes`,
@@ -39616,14 +39814,14 @@ var skill2 = {
39616
39814
  }
39617
39815
  },
39618
39816
  async execute(params, context) {
39619
- const fs21 = await import("node:fs");
39620
- const path21 = await import("node:path");
39817
+ const fs22 = await import("node:fs");
39818
+ const path22 = await import("node:path");
39621
39819
  const { COMPAT_RESULT_CAVEAT: COMPAT_RESULT_CAVEAT2, formatPackageStatus: formatPackageStatus2, formatScanCounts: formatScanCounts2, scanDeps: scanDeps2 } = await Promise.resolve().then(() => (init_web(), web_exports));
39622
- const packagePath = path21.join(context.projectDir, "package.json");
39623
- if (!fs21.existsSync(packagePath)) {
39820
+ const packagePath = path22.join(context.projectDir, "package.json");
39821
+ if (!fs22.existsSync(packagePath)) {
39624
39822
  return { success: false, message: "No package.json found" };
39625
39823
  }
39626
- const parsed = JSON.parse(fs21.readFileSync(packagePath, "utf8"));
39824
+ const parsed = JSON.parse(fs22.readFileSync(packagePath, "utf8"));
39627
39825
  if (!parsed || typeof parsed !== "object") {
39628
39826
  return { success: false, message: "package.json is not an object" };
39629
39827
  }
@@ -39635,9 +39833,9 @@ var skill2 = {
39635
39833
  }) : scanDeps2(parsed);
39636
39834
  const packageResult = params.packageName ? scan.packages[0] : void 0;
39637
39835
  const message = packageResult ? `${params.packageName}: ${formatPackageStatus2(packageResult)}. ${COMPAT_RESULT_CAVEAT2}` : `${formatScanCounts2(scan)}. ${COMPAT_RESULT_CAVEAT2}`;
39638
- const reportPath = path21.join(context.outputDir, "compat-report.json");
39639
- fs21.mkdirSync(path21.dirname(reportPath), { recursive: true });
39640
- fs21.writeFileSync(reportPath, `${JSON.stringify(scan, null, 2)}
39836
+ const reportPath = path22.join(context.outputDir, "compat-report.json");
39837
+ fs22.mkdirSync(path22.dirname(reportPath), { recursive: true });
39838
+ fs22.writeFileSync(reportPath, `${JSON.stringify(scan, null, 2)}
39641
39839
  `);
39642
39840
  return {
39643
39841
  success: true,
@@ -39679,8 +39877,8 @@ var skill3 = {
39679
39877
  record: { type: "boolean", description: "Record video of the flow" }
39680
39878
  },
39681
39879
  async execute(params, context) {
39682
- const fs21 = await import("fs");
39683
- const path21 = await import("path");
39880
+ const fs22 = await import("fs");
39881
+ const path22 = await import("path");
39684
39882
  const yaml2 = await import("yaml");
39685
39883
  const { DEFAULT_SOOTSIM_BRIDGE_PORT: DEFAULT_SOOTSIM_BRIDGE_PORT3 } = await Promise.resolve().then(() => (init_bridge_constants(), bridge_constants_exports));
39686
39884
  const { SootSimBridgeHost: SootSimBridgeHost2 } = await Promise.resolve().then(() => (init_bridge_host(), bridge_host_exports));
@@ -39690,7 +39888,7 @@ var skill3 = {
39690
39888
  const host = new SootSimBridgeHost2({ port: DEFAULT_SOOTSIM_BRIDGE_PORT3 });
39691
39889
  host.start({ silent: true });
39692
39890
  try {
39693
- const flowContent = fs21.readFileSync(params.flowPath, "utf8");
39891
+ const flowContent = fs22.readFileSync(params.flowPath, "utf8");
39694
39892
  const fmMatch = flowContent.match(/^---\n([\s\S]*?)\n---\n?/);
39695
39893
  const stepsBody = fmMatch ? flowContent.slice(fmMatch[0].length) : flowContent;
39696
39894
  const steps = yaml2.parse(stepsBody);
@@ -39717,7 +39915,7 @@ var skill3 = {
39717
39915
  });
39718
39916
  const driver = new SootSimBridgeFlowRunner2(bridge, {
39719
39917
  screenshotDir: context.outputDir,
39720
- flowDir: path21.dirname(path21.resolve(params.flowPath)),
39918
+ flowDir: path22.dirname(path22.resolve(params.flowPath)),
39721
39919
  simId,
39722
39920
  recordingOutputDir: params.record ? context.outputDir : void 0
39723
39921
  });
@@ -39739,7 +39937,7 @@ var skill3 = {
39739
39937
  simId,
39740
39938
  videoPath: videoPath || void 0
39741
39939
  },
39742
- artifacts: videoPath ? [{ type: "video", name: path21.basename(videoPath), path: videoPath }] : void 0
39940
+ artifacts: videoPath ? [{ type: "video", name: path22.basename(videoPath), path: videoPath }] : void 0
39743
39941
  };
39744
39942
  } finally {
39745
39943
  try {
@@ -39786,8 +39984,8 @@ var skill4 = {
39786
39984
  },
39787
39985
  async execute(params, context) {
39788
39986
  const { chromium } = await import("playwright");
39789
- const path21 = await import("path");
39790
- const fs21 = await import("fs");
39987
+ const path22 = await import("path");
39988
+ const fs22 = await import("fs");
39791
39989
  const browser = await launchReapedChrome((options) => chromium.launch(options), {
39792
39990
  headless: true
39793
39991
  });
@@ -39813,9 +40011,9 @@ var skill4 = {
39813
40011
  ...profile,
39814
40012
  fps: syncCount > 0 ? (syncCount / (duration / 1e3)).toFixed(1) : "0"
39815
40013
  };
39816
- const reportPath = path21.join(context.outputDir, "perf-report.json");
39817
- fs21.mkdirSync(path21.dirname(reportPath), { recursive: true });
39818
- fs21.writeFileSync(reportPath, JSON.stringify(report, null, 2));
40014
+ const reportPath = path22.join(context.outputDir, "perf-report.json");
40015
+ fs22.mkdirSync(path22.dirname(reportPath), { recursive: true });
40016
+ fs22.writeFileSync(reportPath, JSON.stringify(report, null, 2));
39819
40017
  return {
39820
40018
  success: true,
39821
40019
  message: `${report.fps} fps, ${profile.avgSyncMs?.toFixed(1)}ms avg sync, ${nodeCount} nodes`,
@@ -39848,8 +40046,8 @@ var skill5 = {
39848
40046
  },
39849
40047
  async execute(params, context) {
39850
40048
  const { chromium } = await import("playwright");
39851
- const path21 = await import("path");
39852
- const fs21 = await import("fs");
40049
+ const path22 = await import("path");
40050
+ const fs22 = await import("fs");
39853
40051
  const browser = await launchReapedChrome((options) => chromium.launch(options), {
39854
40052
  headless: true
39855
40053
  });
@@ -39857,8 +40055,8 @@ var skill5 = {
39857
40055
  try {
39858
40056
  await page.goto(context.url, { waitUntil: "networkidle" });
39859
40057
  await page.waitForTimeout(2e3);
39860
- const outputPath = path21.join(context.outputDir, `${params.name}.png`);
39861
- fs21.mkdirSync(path21.dirname(outputPath), { recursive: true });
40058
+ const outputPath = path22.join(context.outputDir, `${params.name}.png`);
40059
+ fs22.mkdirSync(path22.dirname(outputPath), { recursive: true });
39862
40060
  await page.screenshot({ path: outputPath });
39863
40061
  return {
39864
40062
  success: true,
@@ -39899,8 +40097,8 @@ var skill6 = {
39899
40097
  },
39900
40098
  async execute(params, context) {
39901
40099
  const { chromium } = await import("playwright");
39902
- const path21 = await import("path");
39903
- const fs21 = await import("fs");
40100
+ const path22 = await import("path");
40101
+ const fs22 = await import("fs");
39904
40102
  const browser = await launchReapedChrome((options) => chromium.launch(options), {
39905
40103
  headless: true
39906
40104
  });
@@ -39908,14 +40106,14 @@ var skill6 = {
39908
40106
  try {
39909
40107
  await page.goto(context.url, { waitUntil: "networkidle" });
39910
40108
  await page.waitForTimeout(2e3);
39911
- const currentPath = path21.join(context.outputDir, `${params.name}-current.png`);
39912
- fs21.mkdirSync(path21.dirname(currentPath), { recursive: true });
40109
+ const currentPath = path22.join(context.outputDir, `${params.name}-current.png`);
40110
+ fs22.mkdirSync(path22.dirname(currentPath), { recursive: true });
39913
40111
  await page.screenshot({ path: currentPath });
39914
40112
  try {
39915
40113
  const { PNG } = await import("pngjs");
39916
40114
  const pixelmatch = (await import("pixelmatch")).default;
39917
- const baseline = PNG.sync.read(fs21.readFileSync(params.baseline));
39918
- const current = PNG.sync.read(fs21.readFileSync(currentPath));
40115
+ const baseline = PNG.sync.read(fs22.readFileSync(params.baseline));
40116
+ const current = PNG.sync.read(fs22.readFileSync(currentPath));
39919
40117
  const { width, height } = baseline;
39920
40118
  const diff = new PNG({ width, height });
39921
40119
  const numDiffPixels = pixelmatch(
@@ -39926,8 +40124,8 @@ var skill6 = {
39926
40124
  height,
39927
40125
  { threshold: params.threshold || 0.1 }
39928
40126
  );
39929
- const diffPath = path21.join(context.outputDir, `${params.name}-diff.png`);
39930
- fs21.writeFileSync(diffPath, PNG.sync.write(diff));
40127
+ const diffPath = path22.join(context.outputDir, `${params.name}-diff.png`);
40128
+ fs22.writeFileSync(diffPath, PNG.sync.write(diff));
39931
40129
  const totalPixels = width * height;
39932
40130
  const diffPercent = (numDiffPixels / totalPixels * 100).toFixed(2);
39933
40131
  return {
@@ -39991,10 +40189,10 @@ var skillRegistry = {
39991
40189
  },
39992
40190
  // load skills from a project manifest (rnx-skills.json)
39993
40191
  async loadFromManifest(projectDir) {
39994
- const manifestPath = path20.join(projectDir, "rnx-skills.json");
39995
- if (!fs20.existsSync(manifestPath)) return;
40192
+ const manifestPath = path21.join(projectDir, "rnx-skills.json");
40193
+ if (!fs21.existsSync(manifestPath)) return;
39996
40194
  try {
39997
- const manifest = JSON.parse(fs20.readFileSync(manifestPath, "utf8"));
40195
+ const manifest = JSON.parse(fs21.readFileSync(manifestPath, "utf8"));
39998
40196
  if (Array.isArray(manifest.skills)) {
39999
40197
  for (const entry of manifest.skills) {
40000
40198
  if (typeof entry === "string") {
@@ -40005,7 +40203,7 @@ var skillRegistry = {
40005
40203
  }
40006
40204
  } else if (entry.file) {
40007
40205
  try {
40008
- const mod = await import(path20.resolve(projectDir, entry.file));
40206
+ const mod = await import(path21.resolve(projectDir, entry.file));
40009
40207
  if (mod.skill) this.register(mod.skill);
40010
40208
  } catch {
40011
40209
  }
@@ -40018,10 +40216,10 @@ var skillRegistry = {
40018
40216
  // auto-discover skills based on project dependencies
40019
40217
  suggestSkills(projectDir) {
40020
40218
  const suggestions = [];
40021
- const pkgPath = path20.join(projectDir, "package.json");
40022
- if (!fs20.existsSync(pkgPath)) return suggestions;
40219
+ const pkgPath = path21.join(projectDir, "package.json");
40220
+ if (!fs21.existsSync(pkgPath)) return suggestions;
40023
40221
  try {
40024
- const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf8"));
40222
+ const pkg = JSON.parse(fs21.readFileSync(pkgPath, "utf8"));
40025
40223
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
40026
40224
  if (deps["detox"] || deps["maestro"]) {
40027
40225
  suggestions.push("maestro-test");