peachsim 0.1.1481 → 0.1.1619

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 (292) hide show
  1. package/README.md +56 -13
  2. package/cli/app-catalog.ts +7 -5
  3. package/cli/app-config.ts +47 -8
  4. package/cli/app-fonts.ts +84 -49
  5. package/cli/app-stubs.ts +433 -0
  6. package/cli/bin.ts +15 -2
  7. package/cli/bridge-flow-runner.ts +383 -123
  8. package/cli/cloud-client.ts +26 -25
  9. package/cli/cloud-test-tunnel.ts +2 -0
  10. package/cli/command-registry.ts +2 -1
  11. package/cli/commands/agent-indicator-mode.ts +39 -0
  12. package/cli/commands/box/bundle-plane.ts +4 -0
  13. package/cli/commands/box.ts +16 -23
  14. package/cli/commands/control.ts +221 -126
  15. package/cli/commands/daemon.ts +99 -24
  16. package/cli/commands/debug.ts +31 -3
  17. package/cli/commands/detect.ts +1 -1
  18. package/cli/commands/device.ts +6 -8
  19. package/cli/commands/diagnose.ts +2 -0
  20. package/cli/commands/do-chain.ts +2 -2
  21. package/cli/commands/doctor.ts +1030 -0
  22. package/cli/commands/flow.ts +447 -406
  23. package/cli/commands/inspect/actions.ts +2 -0
  24. package/cli/commands/inspect/core.ts +138 -46
  25. package/cli/commands/inspect/env.ts +10 -0
  26. package/cli/commands/inspect/list.ts +25 -8
  27. package/cli/commands/inspect/memory.ts +9 -0
  28. package/cli/commands/inspect/settling.ts +219 -66
  29. package/cli/commands/inspect/shared.ts +53 -12
  30. package/cli/commands/inspect/wait-event.ts +0 -1
  31. package/cli/commands/inspect/wait.ts +2 -2
  32. package/cli/commands/inspect.ts +51 -16
  33. package/cli/commands/install-desktop.ts +3 -2
  34. package/cli/commands/kotlin.ts +81 -22
  35. package/cli/commands/login.ts +1 -1
  36. package/cli/commands/maestro-compare.ts +41 -5
  37. package/cli/commands/maestro-generate.ts +11 -7
  38. package/cli/commands/maestro.ts +43 -15
  39. package/cli/commands/mode.ts +13 -1
  40. package/cli/commands/no-bridge-hint.ts +26 -3
  41. package/cli/commands/perf-record.ts +296 -0
  42. package/cli/commands/perf.ts +6 -1
  43. package/cli/commands/platform.ts +15 -4
  44. package/cli/commands/preview.ts +1 -1
  45. package/cli/commands/record.ts +37 -6
  46. package/cli/commands/report-issue.ts +2 -1
  47. package/cli/commands/runtime.ts +9 -11
  48. package/cli/commands/screenshots.ts +4 -3
  49. package/cli/commands/serve.ts +30 -2
  50. package/cli/commands/setup.ts +32 -7
  51. package/cli/commands/skills.ts +16 -43
  52. package/cli/commands/swift.ts +22 -3
  53. package/cli/commands/upgrade.ts +7 -6
  54. package/cli/commands/upload.ts +118 -69
  55. package/cli/commands/window.ts +449 -0
  56. package/cli/current-sim.ts +1 -2
  57. package/cli/desktop-companion.ts +84 -10
  58. package/cli/device-option.ts +8 -0
  59. package/cli/drivers/desktop.ts +3 -1
  60. package/cli/drivers/playwright-sim-host.ts +30 -11
  61. package/cli/drivers/playwright.ts +1 -4
  62. package/cli/flow-export.ts +2 -2
  63. package/cli/flow-file.ts +19 -18
  64. package/cli/internal-child.ts +7 -6
  65. package/cli/maestro-compare.ts +28 -6
  66. package/cli/maestro-js.ts +2 -1
  67. package/cli/main.ts +25 -4
  68. package/cli/material-symbol-fonts.ts +171 -0
  69. package/cli/outbound-endpoints.ts +132 -54
  70. package/cli/parse-args.ts +7 -0
  71. package/cli/preview-capture.ts +12 -0
  72. package/cli/privacy.ts +1 -1
  73. package/cli/run-registry.ts +5 -2
  74. package/cli/runtime-compat.ts +16 -0
  75. package/cli/telemetry.ts +6 -3
  76. package/cli/ui.ts +5 -0
  77. package/cli/ws-bridge.ts +121 -62
  78. package/detox/attempt-events.cjs +762 -0
  79. package/detox/attempt-events.d.cts +106 -0
  80. package/detox/gestures.ts +115 -2
  81. package/detox/index.ts +762 -426
  82. package/detox/jest-environment.cjs +86 -0
  83. package/detox/jest-preset.cjs +3 -1
  84. package/detox/jest-setup-after-env.cjs +1 -0
  85. package/detox/matchers.ts +3 -6
  86. package/detox/motion-change.cjs +259 -0
  87. package/detox/motion-change.d.cts +73 -0
  88. package/detox/proof-frame.cjs +83 -9
  89. package/dist-lib/agent-daemon-client.cjs +1 -1
  90. package/dist-lib/agent-events.cjs +1 -1
  91. package/dist-lib/attached-projects.cjs +1 -1
  92. package/dist-lib/auth/shared-session.cjs +15 -5
  93. package/dist-lib/backend-origin.cjs +1 -1
  94. package/dist-lib/beta.cjs +1 -1
  95. package/dist-lib/beta.mjs +1 -1
  96. package/dist-lib/bridge-constants.cjs +3 -1
  97. package/dist-lib/bridge-contract-input.cjs +1 -1
  98. package/dist-lib/bridge-contract-input.mjs +1 -1
  99. package/dist-lib/bridge-contract.cjs +1 -1
  100. package/dist-lib/bridge-contract.mjs +1 -1
  101. package/dist-lib/bundle-channel.cjs +5950 -1775
  102. package/dist-lib/capture-contract.cjs +1 -1
  103. package/dist-lib/capture-contract.mjs +1 -1
  104. package/dist-lib/cloud-contract.cjs +6 -1
  105. package/dist-lib/cloud-contract.mjs +5 -1
  106. package/dist-lib/cloud-test-args.cjs +1 -1
  107. package/dist-lib/cloud-test-args.mjs +1 -1
  108. package/dist-lib/cloud.cjs +1 -1
  109. package/dist-lib/cloud.mjs +1 -1
  110. package/dist-lib/config.cjs +34 -3
  111. package/dist-lib/config.mjs +33 -3
  112. package/dist-lib/detox/index.cjs +3443 -564
  113. package/dist-lib/detox/jest-environment.cjs +86 -0
  114. package/dist-lib/detox/jest-preset.cjs +3 -1
  115. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  116. package/dist-lib/expo-swift-bundle.cjs +5518 -1446
  117. package/dist-lib/expo-swift-metadata.cjs +21 -13
  118. package/dist-lib/expo-swift-metadata.mjs +21 -13
  119. package/dist-lib/home-paths.cjs +1 -1
  120. package/dist-lib/host/bridge-host.cjs +21728 -16808
  121. package/dist-lib/host/fetch-proxy-handler.cjs +177 -102
  122. package/dist-lib/host/fetch-proxy-overrides.cjs +84 -4
  123. package/dist-lib/host/fetch-proxy-overrides.mjs +74 -3
  124. package/dist-lib/host/replacement-module-handler.cjs +16 -15
  125. package/dist-lib/host/websocket-proxy.cjs +243 -6
  126. package/dist-lib/index.cjs +6063 -4113
  127. package/dist-lib/jump-to-source-babel.cjs +73 -35
  128. package/dist-lib/jump-to-source-native.cjs +118 -10
  129. package/dist-lib/kotlin-host.cjs +264 -121
  130. package/dist-lib/menu.cjs +1 -1
  131. package/dist-lib/menu.mjs +1 -1
  132. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  133. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  134. package/dist-lib/metro-production-bundle.cjs +147 -99
  135. package/dist-lib/metro-production-bundle.mjs +142 -99
  136. package/dist-lib/metro.cjs +4249 -360
  137. package/dist-lib/native-app-identity-project.cjs +318 -131
  138. package/dist-lib/native-app-identity-project.mjs +317 -131
  139. package/dist-lib/native-app-identity.cjs +322 -33
  140. package/dist-lib/native-app-identity.mjs +319 -32
  141. package/dist-lib/native-firebase-metadata.cjs +1 -1
  142. package/dist-lib/native-firebase-metadata.mjs +1 -1
  143. package/dist-lib/plist.cjs +1 -1
  144. package/dist-lib/plist.mjs +1 -1
  145. package/dist-lib/profiles.cjs +1 -1
  146. package/dist-lib/public-brand.cjs +1 -1
  147. package/dist-lib/react-native-host-modules.cjs +11 -1
  148. package/dist-lib/react-native-host-modules.mjs +11 -1
  149. package/dist-lib/scripts/dev-server-scanner.cjs +127 -42
  150. package/dist-lib/sdk.cjs +311 -103
  151. package/dist-lib/sdk.mjs +311 -103
  152. package/dist-lib/skills.cjs +18344 -11121
  153. package/dist-lib/swift-compile.cjs +3291 -544
  154. package/dist-lib/swift-compile.mjs +3227 -478
  155. package/dist-lib/swift-package.cjs +25 -3
  156. package/dist-lib/swift-package.mjs +22 -2
  157. package/dist-lib/swift-submit.cjs +2 -2
  158. package/dist-lib/swift-submit.mjs +2 -2
  159. package/dist-lib/swift-toolchain.cjs +1 -1
  160. package/dist-lib/swift.cjs +1 -1
  161. package/dist-lib/vite-swift.cjs +5523 -1428
  162. package/dist-lib/vite.cjs +251 -60
  163. package/dist-lib/worklet-executable.cjs +18 -4
  164. package/dist-lib/worklet-executable.mjs +18 -4
  165. package/install.sh +8 -3
  166. package/kotlin/AndroidViewSources/android/graphics/Canvas.kt +2 -0
  167. package/kotlin/AndroidViewSources/android/graphics/Paint.kt +18 -0
  168. package/kotlin/AndroidViewSources/dev/peach/app/JavaFormat.kt +16 -8
  169. package/kotlin/AppSources/android/view/Window.kt +4 -1
  170. package/kotlin/AppSources/android/window/BackEvent.kt +24 -0
  171. package/kotlin/AppSources/android/window/OnBackAnimationCallback.kt +7 -0
  172. package/kotlin/AppSources/android/window/OnBackInvokedDispatcher.kt +37 -1
  173. package/kotlin/AppSources/androidx/activity/BackEventCompat.kt +5 -0
  174. package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcher.kt +8 -1
  175. package/kotlin/AppSources/dev/peach/app/PeachApp.kt +14 -0
  176. package/kotlin/CompilerSources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar +1 -0
  177. package/kotlin/CompilerSources/PeachJniIncoming.kt +243 -0
  178. package/kotlin/CompilerSources/PeachJniPlugin.kt +116 -0
  179. package/kotlin/ComposeSources/PeachComposeHost.kt +58 -32
  180. package/kotlin/ComposeSources/PeachMagnifier.kt +185 -0
  181. package/kotlin/ExpoComposeSources/PeachAndroidView.kt +16 -0
  182. package/kotlin/JniPlatformSources/androidx/annotation/CallSuper.kt +5 -0
  183. package/kotlin/JniPlatformSources/com/facebook/proguard/annotations/DoNotStrip.kt +5 -0
  184. package/kotlin/JniPlatformSources/dalvik/annotation/optimization/FastNative.kt +5 -0
  185. package/kotlin/JniPlatformSources/kotlin/Synchronized.kt +6 -0
  186. package/kotlin/JniPlatformSources/kotlin/jvm/JvmField.kt +5 -0
  187. package/kotlin/JniPlatformSources/kotlin/jvm/JvmStatic.kt +5 -0
  188. package/kotlin/JniSources/com/facebook/jni/HybridData.kt +68 -0
  189. package/kotlin/JniSources/dev/peach/jni/IncomingBindings.kt +157 -0
  190. package/kotlin/JniSources/dev/peach/jni/NativeBindings.kt +79 -0
  191. package/kotlin/JniTransportSources/dev/peach/jni/integration/JniTransport.kt +91 -0
  192. package/kotlin/README.md +18 -0
  193. package/kotlin/Sources/java/util/IllegalFormatCodePointException.kt +10 -0
  194. package/kotlin/Sources/java/util/IllegalFormatException.kt +5 -0
  195. package/kotlin/fixtures/counter/CounterApp.native.tsx +30 -3
  196. package/kotlin/fixtures/counter/CounterScreen.d.kt.ts +1 -0
  197. package/kotlin/fixtures/counter/CounterScreen.kt +2 -1
  198. package/kotlin/fixtures/counter/package.json +15 -2
  199. package/kotlin/fixtures/counter/vite.config.ts +11 -1
  200. package/kotlin/fork/NOTICE +1 -0
  201. package/kotlin/fork/README.md +5 -4
  202. package/kotlin/fork/compose-worker-globals.patch +629 -0
  203. package/kotlin/fork/fork.json +1 -1
  204. package/kotlin/toolchain.json +3 -3
  205. package/package.json +1 -1
  206. package/scripts/dev-server-scanner.ts +166 -81
  207. package/skills/contrast/SKILL.md +4 -4
  208. package/skills/peach-debug/SKILL.md +1 -1
  209. package/skills/peach-perf/SKILL.md +25 -0
  210. package/skills/peach-setup/SKILL.md +47 -13
  211. package/skills/peach-test/SKILL.md +2 -3
  212. package/src/agent-identity.ts +18 -0
  213. package/src/assetCatalogContract.ts +31 -3
  214. package/src/auth/shared-session.ts +3 -2
  215. package/src/bridge-constants.ts +1 -1
  216. package/src/bridge-contract.ts +5 -0
  217. package/src/bundle-channel.mjs +38 -7
  218. package/src/bundleGlobalFooter.ts +70 -20
  219. package/src/cloud-contract.ts +11 -0
  220. package/src/config.ts +47 -1
  221. package/src/configured-bundle-producer.ts +4 -1
  222. package/src/desktop-host-delivery.ts +4 -3
  223. package/src/expo-kotlin-host.ts +7 -2
  224. package/src/guest-bundle.ts +15 -8
  225. package/src/host/agent-host.ts +2 -4
  226. package/src/host/bridge-host.ts +260 -119
  227. package/src/host/fetch-proxy-handler.ts +237 -146
  228. package/src/host/fetch-proxy-overrides.ts +118 -0
  229. package/src/host/fetch-proxy-transport.ts +77 -63
  230. package/src/host/lease-owner.test.ts +18 -1
  231. package/src/host/lease-owner.ts +4 -5
  232. package/src/host/loopback-ports.ts +34 -0
  233. package/src/host/replacement-module-handler.ts +24 -15
  234. package/src/host/server-scan-handler.ts +87 -0
  235. package/src/host/staged-host-files.ts +92 -0
  236. package/src/host/websocket-proxy.ts +271 -5
  237. package/src/installed-package-versions.ts +66 -0
  238. package/src/jump-to-source-babel.ts +97 -89
  239. package/src/jump-to-source-native.ts +109 -11
  240. package/src/kotlin-host.ts +144 -27
  241. package/src/kotlin-module.ts +52 -0
  242. package/src/kotlinDiscoveryContract.ts +27 -0
  243. package/src/metro-layout-parser.ts +78 -0
  244. package/src/metro-plugin.ts +129 -39
  245. package/src/metro-production-bundle.ts +140 -139
  246. package/src/native-app-identity-project.ts +103 -5
  247. package/src/native-app-identity.ts +173 -11
  248. package/src/native-firebase-config.ts +83 -80
  249. package/src/native-seam-manifest.ts +0 -44
  250. package/src/nativeAppIconContract.ts +137 -0
  251. package/src/nativeViewSourceContract.ts +49 -0
  252. package/src/nativeViewWebContract.ts +41 -0
  253. package/src/nitro-bundle.ts +43 -0
  254. package/src/nitro-compile.ts +107 -0
  255. package/src/nitro-source.ts +310 -0
  256. package/src/nitroContract.ts +51 -0
  257. package/src/one-widget-metadata.ts +23 -3
  258. package/src/oneWidgetContract.ts +36 -3
  259. package/src/packageVersionsContract.ts +52 -0
  260. package/src/process-group.ts +306 -0
  261. package/src/process-liveness.ts +24 -0
  262. package/src/process-output.ts +21 -2
  263. package/src/react-devtools-frontend-react.ts +45 -0
  264. package/src/react-native-host-modules.ts +10 -0
  265. package/src/serverDiscoveryContract.ts +41 -0
  266. package/src/skills/builtin/maestro-test.ts +2 -4
  267. package/src/swift-compile.ts +3195 -650
  268. package/src/swift-design-time.ts +552 -0
  269. package/src/swift-distribution-contract.ts +11 -0
  270. package/src/swift-distribution.lock.json +46 -11
  271. package/src/swift-host.ts +144 -30
  272. package/src/swift-icu.ts +10 -2
  273. package/src/swift-objc-sdk.ts +726 -0
  274. package/src/swift-objc-standin.ts +441 -0
  275. package/src/swift-package.ts +25 -0
  276. package/src/swift-packages.ts +25 -10
  277. package/src/swift-standin-config.ts +94 -0
  278. package/src/swift-standin-emit.ts +5 -3
  279. package/src/swift-standins.ts +225 -81
  280. package/src/swift-submit.ts +2 -1
  281. package/src/swiftContract.ts +4 -0
  282. package/src/target-matcher.ts +67 -0
  283. package/src/visit-parsed-program.ts +21 -0
  284. package/src/vite-plugin-one.ts +65 -17
  285. package/src/vite-plugin-swift.ts +1 -0
  286. package/src/vite-plugin.ts +168 -70
  287. package/src/worker-system-chunks.ts +89 -10
  288. package/src/worklet-executable.ts +21 -3
  289. package/src/worklets-babel.ts +85 -13
  290. package/src/xcode-project.ts +81 -134
  291. package/src/xcode-target-members.ts +107 -0
  292. package/cli/commands/compat.ts +0 -395
@@ -70,7 +70,6 @@ import type {
70
70
  export const DEFAULT_RNX_CLOUD_ORIGIN = rnxPublicBrand.origin
71
71
  export const MAX_RNX_CLOUD_BUNDLE_BYTES = RNX_CLOUD_MAX_ARTIFACT_BYTES
72
72
 
73
- // every message this module writes names the command through this constant.
74
73
  const REMOTE_COMMAND = 'peach remote'
75
74
  const PREVIEW_COMMAND = 'peach preview'
76
75
 
@@ -170,19 +169,19 @@ function resolveCloudOrigin(value?: string): string {
170
169
  return normalizeCloudOrigin(source)
171
170
  }
172
171
 
173
- function readImmutableBundleSource(bundlePath: string): string {
172
+ function readImmutableBundleSource(bundlePath: string, command: string): string {
174
173
  const filepath = resolve(bundlePath)
175
174
  if (extname(filepath) !== '.js') {
176
- throw new Error(`${REMOTE_COMMAND} requires one local Metro .js bundle`)
175
+ throw new Error(`${command} requires one local Metro .js bundle`)
177
176
  }
178
177
  const descriptor = openSync(filepath, 'r')
179
178
  try {
180
179
  const before = fstatSync(descriptor, { bigint: true })
181
180
  if (!before.isFile()) {
182
- throw new Error(`${REMOTE_COMMAND} bundle must be a regular local file`)
181
+ throw new Error(`${command} bundle must be a regular local file`)
183
182
  }
184
183
  if (before.size <= 0n) {
185
- throw new Error(`${REMOTE_COMMAND} bundle is empty`)
184
+ throw new Error(`${command} bundle is empty`)
186
185
  }
187
186
  const bytes = readFileSync(descriptor)
188
187
  const after = fstatSync(descriptor, { bigint: true })
@@ -193,16 +192,16 @@ function readImmutableBundleSource(bundlePath: string): string {
193
192
  before.mtimeNs !== after.mtimeNs ||
194
193
  bytes.length !== Number(before.size)
195
194
  ) {
196
- throw new Error(`${REMOTE_COMMAND} bundle changed while it was being read`)
195
+ throw new Error(`${command} bundle changed while it was being read`)
197
196
  }
198
197
  let source: string
199
198
  try {
200
199
  source = new TextDecoder('utf-8', { fatal: true }).decode(bytes)
201
200
  } catch {
202
- throw new Error(`${REMOTE_COMMAND} bundle must be valid UTF-8 JavaScript`)
201
+ throw new Error(`${command} bundle must be valid UTF-8 JavaScript`)
203
202
  }
204
203
  if (source.includes('\0') || !source.includes('__d(') || !source.includes('__r(')) {
205
- throw new Error(`${REMOTE_COMMAND} requires a Metro JavaScript bundle`)
204
+ throw new Error(`${command} requires a Metro JavaScript bundle`)
206
205
  }
207
206
  return source
208
207
  } finally {
@@ -210,30 +209,30 @@ function readImmutableBundleSource(bundlePath: string): string {
210
209
  }
211
210
  }
212
211
 
213
- async function fetchBundleSource(bundleUrl: string): Promise<string> {
212
+ async function fetchBundleSource(bundleUrl: string, command: string): Promise<string> {
214
213
  let response: Response
215
214
  try {
216
215
  response = await fetch(bundleUrl)
217
216
  } catch (error) {
218
217
  throw new Error(
219
- `${REMOTE_COMMAND} could not fetch bundle from ${bundleUrl}: ${
218
+ `${command} could not fetch bundle from ${bundleUrl}: ${
220
219
  error instanceof Error ? error.message : String(error)
221
220
  }`,
222
221
  )
223
222
  }
224
223
  if (!response.ok) {
225
224
  throw new Error(
226
- `${REMOTE_COMMAND} could not fetch bundle from ${bundleUrl}: HTTP ${response.status}${
225
+ `${command} could not fetch bundle from ${bundleUrl}: HTTP ${response.status}${
227
226
  response.statusText ? ` ${response.statusText}` : ''
228
227
  }`,
229
228
  )
230
229
  }
231
230
  const text = await response.text()
232
231
  if (text.length === 0) {
233
- throw new Error(`${REMOTE_COMMAND} bundle is empty`)
232
+ throw new Error(`${command} bundle is empty`)
234
233
  }
235
234
  if (text.includes('\0') || !text.includes('__d(') || !text.includes('__r(')) {
236
- throw new Error(`${REMOTE_COMMAND} requires a Metro JavaScript bundle`)
235
+ throw new Error(`${command} requires a Metro JavaScript bundle`)
237
236
  }
238
237
  return text
239
238
  }
@@ -254,20 +253,20 @@ function isRemoteBundleInput(bundleInput: string): boolean {
254
253
  return bundleInput.startsWith('https://') || bundleInput.startsWith('http://')
255
254
  }
256
255
 
257
- async function loadBundleSource(bundleInput: string): Promise<string> {
256
+ async function loadBundleSource(bundleInput: string, command: string): Promise<string> {
258
257
  if (bundleInput.startsWith('http://') && !isLoopbackBundleUrl(bundleInput)) {
259
- throw new Error(`${REMOTE_COMMAND} bundle URL must use HTTPS`)
258
+ throw new Error(`${command} bundle URL must use HTTPS`)
260
259
  }
261
260
  const source = isRemoteBundleInput(bundleInput)
262
- ? await fetchBundleSource(bundleInput)
263
- : readImmutableBundleSource(bundleInput)
261
+ ? await fetchBundleSource(bundleInput, command)
262
+ : readImmutableBundleSource(bundleInput, command)
264
263
  // validate the actual runtime script form without executing guest code or
265
264
  // allocating a host.
266
265
  try {
267
266
  new Script(rewriteGuestImportMeta(source), { filename: bundleInput })
268
267
  } catch (error) {
269
268
  throw new Error(
270
- `${REMOTE_COMMAND} bundle contains invalid JavaScript: ${
269
+ `${command} bundle contains invalid JavaScript: ${
271
270
  error instanceof Error ? error.message : String(error)
272
271
  }`,
273
272
  )
@@ -549,6 +548,7 @@ function unverifiedContentKey(
549
548
  function searchMetroAssetDest(
550
549
  bundlePath: string,
551
550
  descriptors: readonly MetroAssetDescriptor[],
551
+ command: string,
552
552
  assetsDirectory?: string,
553
553
  ): MetroAssetSearch {
554
554
  const bundleDirectory = dirname(resolve(bundlePath))
@@ -616,7 +616,7 @@ function searchMetroAssetDest(
616
616
  // the wrong repair for a dest that only needs rebuilding.
617
617
  if (stale.length > 0) {
618
618
  throw new Error(
619
- `${REMOTE_COMMAND} found this bundle's assets in ${stale.join(', ')}, but their bytes are not the ones it was built against. Rebuild with \`react-native bundle --assets-dest ${stale[0]}\` so the bundle and the images match.`,
619
+ `${command} found this bundle's assets in ${stale.join(', ')}, but their bytes are not the ones it was built against. Rebuild with \`react-native bundle --assets-dest ${stale[0]}\` so the bundle and the images match.`,
620
620
  )
621
621
  }
622
622
  return { dest: null, searched, truncated, unconfirmed: false }
@@ -633,7 +633,7 @@ function searchMetroAssetDest(
633
633
  if (!winner) return { dest: null, searched, truncated, unconfirmed: false }
634
634
  if (distinct.size > 1) {
635
635
  throw new Error(
636
- `${REMOTE_COMMAND} found this bundle's assets in more than one directory and cannot tell which build wrote them: ${rivals
636
+ `${command} found this bundle's assets in more than one directory and cannot tell which build wrote them: ${rivals
637
637
  .map((rival) => rival.dest.directory)
638
638
  .join(
639
639
  ', ',
@@ -649,13 +649,14 @@ function searchMetroAssetDest(
649
649
 
650
650
  async function inferCloudModuleIdentity(
651
651
  source: string,
652
+ command: string,
652
653
  ): Promise<RNXMetroModuleIdentityMetadata> {
653
654
  let receipt
654
655
  try {
655
656
  receipt = await loadMetroFingerprintRegistry()
656
657
  } catch (error) {
657
658
  throw new Error(
658
- `${REMOTE_COMMAND} could not load the Metro fingerprint registry needed to identify this bundle: ${
659
+ `${command} could not load the Metro fingerprint registry needed to identify this bundle: ${
659
660
  error instanceof Error ? error.message : String(error)
660
661
  }. Build it through the published Metro plugin: withPeach(config).`,
661
662
  )
@@ -674,7 +675,7 @@ async function inferCloudModuleIdentity(
674
675
  }
675
676
  } catch (error) {
676
677
  throw new Error(
677
- `${REMOTE_COMMAND} could not infer module identity for this Metro bundle: ${
678
+ `${command} could not infer module identity for this Metro bundle: ${
678
679
  error instanceof Error ? error.message : String(error)
679
680
  }. Build it through the published Metro plugin: withPeach(config).`,
680
681
  )
@@ -775,7 +776,7 @@ async function embedBundleAssets(
775
776
  dest: MetroAssetDest | null
776
777
  substituted: string[]
777
778
  }> {
778
- const source = await loadBundleSource(bundlePath)
779
+ const source = await loadBundleSource(bundlePath, command)
779
780
  const descriptors = extractAssetDescriptors(source).filter(
780
781
  (descriptor) => !isEmbeddedAssetDescriptor(descriptor),
781
782
  )
@@ -791,7 +792,7 @@ async function embedBundleAssets(
791
792
  // an https bundle uses only an explicit local asset destination.
792
793
  const search: MetroAssetSearch =
793
794
  assetsDirectory || !isRemoteBundleInput(bundlePath)
794
- ? searchMetroAssetDest(bundlePath, descriptors, assetsDirectory)
795
+ ? searchMetroAssetDest(bundlePath, descriptors, command, assetsDirectory)
795
796
  : { dest: null, searched: [], truncated: false, unconfirmed: false }
796
797
  const dest = search.dest
797
798
  // `embedMetroAssetFiles` re-parses the bundle, so its descriptors are equal
@@ -877,7 +878,7 @@ async function produceArtifact(
877
878
  assetsDirectory,
878
879
  )
879
880
  const artifact = await buildRnxCloudArtifact(prepared.code, {
880
- inferModuleIdentity: inferCloudModuleIdentity,
881
+ inferModuleIdentity: (source) => inferCloudModuleIdentity(source, command),
881
882
  })
882
883
  const bytes = Buffer.from(artifact.code, 'utf8')
883
884
  const assets = artifact.receipt.assets
@@ -137,6 +137,8 @@ export function startCloudTestTokenProxy(
137
137
  return
138
138
  }
139
139
  if (selectedPort !== upstreamPort) headers.host = `127.0.0.1:${selectedPort}`
140
+ // the recorder's browser origin belongs to its host, not this dev server.
141
+ if (headers.origin) headers.origin = `http://127.0.0.1:${selectedPort}`
140
142
  for (const key of Object.keys(headers)) {
141
143
  const lower = key.toLowerCase()
142
144
  if (lower.startsWith('x-forwarded-') || lower.startsWith('cf-')) {
@@ -50,7 +50,7 @@ const NODE_COMMAND_NAMES = [
50
50
  'use',
51
51
  'claim',
52
52
  'close',
53
- 'compat',
53
+ 'doctor',
54
54
  'project-report',
55
55
  'support',
56
56
  'desktop',
@@ -129,6 +129,7 @@ const VALUE_FLAGS = new Set([
129
129
  '--device',
130
130
  '--posture',
131
131
  '--os-version',
132
+ '--ios-target',
132
133
  '--output',
133
134
  '-o',
134
135
  '--out',
@@ -0,0 +1,39 @@
1
+ // peach mode agent-indicator — show or hide the connected agent indicator and
2
+ // its resting agent cursor over the device in the running peach sim, so
3
+ // scripted recordings can film clean devices while the CLI drives them.
4
+
5
+ import { peachExit } from '../run-rnx'
6
+ import { runShellBooleanMode, type ShellBooleanModeOptions } from './shell-boolean-mode'
7
+
8
+ export async function runAgentIndicatorMode(
9
+ args: string[],
10
+ opts: ShellBooleanModeOptions,
11
+ ) {
12
+ if (args.includes('--help') || args.includes('-h')) {
13
+ console.log(`
14
+ peach mode agent-indicator — toggle the connected agent indicator
15
+
16
+ usage:
17
+ peach mode agent-indicator [on|off|toggle]
18
+
19
+ arguments:
20
+ on show the indicator while a CLI or agent drives the sim (default)
21
+ off hide it and the agent cursor that rests under it, for clean recordings
22
+ of scripted sims
23
+ toggle flip the current state (default action)
24
+
25
+ the setting persists. a shell url can also seed it with agentIndicator=0.
26
+
27
+ examples:
28
+ peach mode agent-indicator off
29
+ peach --sim a1 mode agent-indicator on
30
+ `)
31
+ peachExit(0)
32
+ }
33
+
34
+ await runShellBooleanMode(args, opts, {
35
+ modeKey: 'agentIndicator',
36
+ displayName: 'agent-indicator',
37
+ actionId: 'toggle-agent-indicator',
38
+ })
39
+ }
@@ -10,6 +10,7 @@
10
10
 
11
11
  import {
12
12
  PREBUILT_BUNDLE_DEVICE_PATH,
13
+ PREBUILT_BUNDLE_INITIAL_URL_PATH,
13
14
  PREBUILT_BUNDLE_NATIVE_RENDERER_PATH,
14
15
  prebuiltAppFontsPartPath,
15
16
  PREBUILT_BUNDLE_PART_BYTES,
@@ -25,6 +26,7 @@ export class BundleFilePlane implements ProjectFilePlane {
25
26
  constructor(
26
27
  bundle: Uint8Array,
27
28
  device: string | null,
29
+ initialUrl: string | null,
28
30
  appFonts = '',
29
31
  nativeRenderer?: NativeRenderer,
30
32
  ) {
@@ -32,6 +34,8 @@ export class BundleFilePlane implements ProjectFilePlane {
32
34
  if (appFonts)
33
35
  this.recordParts(new TextEncoder().encode(appFonts), prebuiltAppFontsPartPath)
34
36
  if (device) this.record(PREBUILT_BUNDLE_DEVICE_PATH, new TextEncoder().encode(device))
37
+ if (initialUrl)
38
+ this.record(PREBUILT_BUNDLE_INITIAL_URL_PATH, new TextEncoder().encode(initialUrl))
35
39
  if (nativeRenderer !== undefined)
36
40
  this.record(
37
41
  PREBUILT_BUNDLE_NATIVE_RENDERER_PATH,
@@ -27,7 +27,6 @@ import {
27
27
  cloudBoxConnectUrl,
28
28
  createCloudBoxProvider,
29
29
  ensureCloudBoxBrowser,
30
- heartbeatSession,
31
30
  resolveCloudBoxReference,
32
31
  } from 'peach-cloud-box/client'
33
32
  import { getRuntimePlatform, isDeviceModel } from 'peach-engine/settings/devices'
@@ -125,7 +124,7 @@ function apiOrigin(args: string[], auth: CliAuth): string {
125
124
  return (
126
125
  flag(args, 'api-origin') ??
127
126
  process.env[API_ORIGIN_ENV]?.trim() ??
128
- (auth.kind === 'session' ? auth.origin : 'https://contrast.dev')
127
+ (auth.kind === 'session' ? auth.origin : rnxPublicBrand.origin)
129
128
  ).replace(/\/+$/, '')
130
129
  }
131
130
 
@@ -187,6 +186,7 @@ async function runCloudBoxUse(args: string[]): Promise<number> {
187
186
  apiOrigin: apiOrigin(args, auth),
188
187
  apiKey,
189
188
  boxId: named,
189
+ accountId: cloudAccountIdOrExit(auth),
190
190
  })
191
191
  // attach for the Box's own name: the caller may have typed either an id or
192
192
  // a name, and the default has to be able to print the one a person reads.
@@ -361,9 +361,13 @@ async function runCloudBoxConnect(args: string[]): Promise<number> {
361
361
  const origin = apiOrigin(args, auth)
362
362
 
363
363
  let session: BoxShellSession | null = null
364
- let heartbeat: { stop: () => void } | null = null
365
364
  try {
366
- const reference = await resolveCloudBoxReference({ apiOrigin: origin, apiKey, boxId })
365
+ const reference = await resolveCloudBoxReference({
366
+ apiOrigin: origin,
367
+ apiKey,
368
+ boxId,
369
+ accountId: cloudAccountIdOrExit(auth),
370
+ })
367
371
  const box = await createBoxClient(
368
372
  createCloudBoxProvider({
369
373
  endpoint: reference.endpoint,
@@ -388,11 +392,6 @@ async function runCloudBoxConnect(args: string[]): Promise<number> {
388
392
  }
389
393
  console.log(` ...accessing shell\n`)
390
394
  session = await box.connect()
391
- // the same reason `create` heartbeats: the box holds no account credential,
392
- // so the client using it is what keeps its session alive.
393
- const sessionId = box.description.sessionId
394
- if (!sessionId) throw new BoxClientError('the box service returned no session id')
395
- heartbeat = heartbeatSession({ apiOrigin: origin, apiKey, sessionId })
396
395
  return await repl(session, '')
397
396
  } catch (error) {
398
397
  if (error instanceof BoxClientError) {
@@ -401,7 +400,6 @@ async function runCloudBoxConnect(args: string[]): Promise<number> {
401
400
  }
402
401
  throw error
403
402
  } finally {
404
- heartbeat?.stop()
405
403
  session?.close()
406
404
  }
407
405
  }
@@ -414,7 +412,7 @@ async function runCloudBoxConnect(args: string[]): Promise<number> {
414
412
  * person sees is what the agent is working on, and two people opening the same
415
413
  * box see the same screen.
416
414
  *
417
- * the url carries no credential: it is the box's page on the Contrast origin,
415
+ * the url carries no credential: it is the box's page on its account origin,
418
416
  * and opening it signed in resolves the box through the account service. that
419
417
  * makes it a link somebody can keep, paste, or reload.
420
418
  */
@@ -436,6 +434,7 @@ async function runCloudBoxOpen(args: string[]): Promise<number> {
436
434
  apiOrigin: apiOrigin(args, auth),
437
435
  apiKey,
438
436
  boxId,
437
+ accountId: cloudAccountIdOrExit(auth),
439
438
  })
440
439
  const connectUrl = cloudBoxConnectUrl(reference.endpoint, reference.boxId)
441
440
  // ensure first: it is what navigates the generation's tab to the Box page,
@@ -499,6 +498,7 @@ export async function openInBrowser(url: string): Promise<number> {
499
498
 
500
499
  function cloudBoxClient(options: {
501
500
  endpoint: string
501
+ fresh?: boolean
502
502
  apiOrigin: string
503
503
  apiKey: string
504
504
  accountId: string | null
@@ -506,6 +506,7 @@ function cloudBoxClient(options: {
506
506
  return createBoxClient(
507
507
  createCloudBoxProvider({
508
508
  endpoint: options.endpoint,
509
+ fresh: options.fresh,
509
510
  authority: {
510
511
  kind: 'account',
511
512
  apiOrigin: options.apiOrigin,
@@ -525,10 +526,13 @@ function cloudBoxClient(options: {
525
526
  * that bundle instead of building anything.
526
527
  */
527
528
  export async function createPrebuiltBundleBox(options: {
529
+ fresh?: boolean
528
530
  bundle: Uint8Array
529
531
  nativeProjectDir?: string
530
532
  // the simulator's device model, or null for the Box page's default
531
533
  device: string | null
534
+ // the link the app launches with, as Linking.getInitialURL reports it
535
+ initialUrl?: string | null
532
536
  name: string
533
537
  endpoint: string
534
538
  apiOrigin: string
@@ -550,6 +554,7 @@ export async function createPrebuiltBundleBox(options: {
550
554
  source: new BundleFilePlane(
551
555
  options.bundle,
552
556
  options.device,
557
+ options.initialUrl ?? null,
553
558
  options.nativeProjectDir
554
559
  ? readNativeLinkedAppFontsWire({
555
560
  projectDir: options.nativeProjectDir,
@@ -711,7 +716,6 @@ async function runCloudBoxCreate(
711
716
  accountId,
712
717
  }
713
718
  let session: BoxShellSession | null = null
714
- let heartbeat: { stop: () => void } | null = null
715
719
  try {
716
720
  const box =
717
721
  'bundle' in source
@@ -760,16 +764,6 @@ async function runCloudBoxCreate(
760
764
  }
761
765
  console.log(` ...accessing shell\n`)
762
766
  session = await box.connect()
763
- // bank the wall clock while the shell is open. the box holds no account
764
- // credential, so the client that is using it is what keeps the session
765
- // alive; when this process dies the sweep closes it at the last heartbeat.
766
- const sessionId = box.description.sessionId
767
- if (!sessionId) throw new BoxClientError('the box service returned no session id')
768
- heartbeat = heartbeatSession({
769
- apiOrigin: apiOrigin(args, auth),
770
- apiKey,
771
- sessionId,
772
- })
773
767
  return await repl(session, '')
774
768
  } catch (error) {
775
769
  if (error instanceof BoxClientError) {
@@ -778,7 +772,6 @@ async function runCloudBoxCreate(
778
772
  }
779
773
  throw error
780
774
  } finally {
781
- heartbeat?.stop()
782
775
  session?.close()
783
776
  }
784
777
  }