peachsim 0.1.571 → 0.1.572

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 (235) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +106 -106
  3. package/cli/app-config.ts +1 -1
  4. package/cli/app-fonts.ts +4 -4
  5. package/cli/auth.ts +11 -11
  6. package/cli/bin.ts +4 -4
  7. package/cli/bridge-flow-runner.ts +13 -13
  8. package/cli/browser-evals.ts +2 -2
  9. package/cli/cli-update.ts +6 -6
  10. package/cli/cloud-client.ts +16 -16
  11. package/cli/cloud-dispatch.ts +2 -2
  12. package/cli/cloud-session.ts +12 -12
  13. package/cli/cloud-test-tunnel.ts +4 -4
  14. package/cli/command-registry.ts +7 -7
  15. package/cli/commands/agent-wrapper.ts +7 -7
  16. package/cli/commands/agent.ts +16 -16
  17. package/cli/commands/app-fonts.ts +3 -3
  18. package/cli/commands/assert.ts +10 -10
  19. package/cli/commands/auth.ts +5 -5
  20. package/cli/commands/box/checkout-plane.ts +1 -1
  21. package/cli/commands/box/plane-share.ts +2 -2
  22. package/cli/commands/box/rnx-command.ts +5 -5
  23. package/cli/commands/box.ts +38 -38
  24. package/cli/commands/camera.ts +13 -13
  25. package/cli/commands/cleanup.ts +13 -13
  26. package/cli/commands/compat.ts +7 -7
  27. package/cli/commands/config.ts +5 -5
  28. package/cli/commands/control.ts +63 -63
  29. package/cli/commands/cpu-profile.ts +11 -11
  30. package/cli/commands/daemon-mac-app.ts +10 -10
  31. package/cli/commands/daemon.ts +29 -27
  32. package/cli/commands/debug.ts +35 -35
  33. package/cli/commands/desktop.ts +3 -3
  34. package/cli/commands/detox.ts +33 -33
  35. package/cli/commands/device.ts +9 -9
  36. package/cli/commands/diagnose.ts +17 -17
  37. package/cli/commands/do-chain.ts +4 -4
  38. package/cli/commands/electron.ts +9 -9
  39. package/cli/commands/film.ts +9 -9
  40. package/cli/commands/flow.ts +49 -49
  41. package/cli/commands/inspect/core.ts +12 -12
  42. package/cli/commands/inspect/count.ts +1 -1
  43. package/cli/commands/inspect/describe.ts +1 -1
  44. package/cli/commands/inspect/env.ts +2 -2
  45. package/cli/commands/inspect/find.ts +2 -2
  46. package/cli/commands/inspect/get-layout.ts +2 -2
  47. package/cli/commands/inspect/keyboard.ts +1 -1
  48. package/cli/commands/inspect/list.ts +4 -4
  49. package/cli/commands/inspect/memory.ts +1 -1
  50. package/cli/commands/inspect/resolve-target.ts +1 -1
  51. package/cli/commands/inspect/screens.ts +1 -1
  52. package/cli/commands/inspect/shared.ts +1 -1
  53. package/cli/commands/inspect/tree.ts +1 -1
  54. package/cli/commands/inspect/url.ts +1 -1
  55. package/cli/commands/inspect/wait-event.ts +5 -5
  56. package/cli/commands/inspect.ts +82 -82
  57. package/cli/commands/install-cli.ts +16 -16
  58. package/cli/commands/install-desktop.ts +26 -26
  59. package/cli/commands/login.ts +11 -11
  60. package/cli/commands/maestro-generate.ts +10 -10
  61. package/cli/commands/maestro.ts +77 -67
  62. package/cli/commands/mode.ts +4 -4
  63. package/cli/commands/no-bridge-hint.ts +12 -12
  64. package/cli/commands/perf.ts +11 -11
  65. package/cli/commands/permissions.ts +5 -5
  66. package/cli/commands/platform.ts +21 -21
  67. package/cli/commands/preview.ts +5 -5
  68. package/cli/commands/profile.ts +12 -12
  69. package/cli/commands/react.ts +15 -15
  70. package/cli/commands/record.ts +31 -31
  71. package/cli/commands/report-issue.ts +4 -4
  72. package/cli/commands/reset.ts +2 -2
  73. package/cli/commands/runtime.ts +23 -23
  74. package/cli/commands/screenshot-command.ts +2 -2
  75. package/cli/commands/screenshot-layers.ts +5 -5
  76. package/cli/commands/screenshot-mode.ts +7 -7
  77. package/cli/commands/screenshot.ts +24 -24
  78. package/cli/commands/screenshots-capture.ts +13 -13
  79. package/cli/commands/screenshots.ts +6 -6
  80. package/cli/commands/serve.ts +12 -12
  81. package/cli/commands/setup.ts +25 -25
  82. package/cli/commands/skills.ts +30 -23
  83. package/cli/commands/slides.ts +20 -20
  84. package/cli/commands/state.ts +2 -2
  85. package/cli/commands/storage.ts +3 -3
  86. package/cli/commands/telemetry.ts +2 -2
  87. package/cli/commands/test.ts +15 -15
  88. package/cli/commands/three-mode.ts +17 -17
  89. package/cli/commands/timeline.ts +12 -12
  90. package/cli/commands/upgrade.ts +11 -11
  91. package/cli/commands/upload.ts +26 -26
  92. package/cli/commands/version.ts +7 -7
  93. package/cli/commands/what-happened.ts +20 -20
  94. package/cli/current-sim.ts +3 -3
  95. package/cli/desktop-companion.ts +16 -16
  96. package/cli/drivers/electron.ts +4 -4
  97. package/cli/drivers/playwright-sim-host.ts +7 -7
  98. package/cli/drivers/playwright.ts +4 -4
  99. package/cli/drivers/registry.ts +1 -1
  100. package/cli/drivers/types.ts +5 -5
  101. package/cli/flow-export.ts +1 -1
  102. package/cli/flow-file.ts +5 -5
  103. package/cli/flow-session.ts +1 -1
  104. package/cli/help-core.ts +13 -13
  105. package/cli/hints.ts +14 -14
  106. package/cli/inspect-notice-state.ts +1 -1
  107. package/cli/internal-child.ts +2 -2
  108. package/cli/maestro-js.ts +4 -4
  109. package/cli/maestro-report.ts +1 -1
  110. package/cli/main.ts +14 -14
  111. package/cli/outbound-endpoints.ts +30 -30
  112. package/cli/parse-args.ts +7 -7
  113. package/cli/privacy.ts +1 -1
  114. package/cli/recording-access.ts +10 -10
  115. package/cli/resolve-assets.ts +2 -2
  116. package/cli/rnx-in-process.ts +5 -5
  117. package/cli/run-registry.ts +1 -1
  118. package/cli/run-rnx-core.ts +3 -3
  119. package/cli/run-rnx.ts +1 -1
  120. package/cli/runtime-notes.ts +1 -1
  121. package/cli/self-invocation.ts +1 -1
  122. package/cli/send-to-box.ts +1 -1
  123. package/cli/shell-init.ts +40 -40
  124. package/cli/ui.ts +1 -1
  125. package/cli/ws-bridge.ts +22 -22
  126. package/detox/config-loader.ts +1 -1
  127. package/detox/index.ts +28 -28
  128. package/detox/navigation.ts +2 -2
  129. package/dist-lib/agent-daemon-client.cjs +12 -12
  130. package/dist-lib/agent-events.cjs +1 -1
  131. package/dist-lib/agent-identity.cjs +2 -2
  132. package/dist-lib/agent-sessions.cjs +12 -12
  133. package/dist-lib/attached-projects.cjs +4 -4
  134. package/dist-lib/auth/shared-session.cjs +4 -4
  135. package/dist-lib/backend-origin.cjs +1 -1
  136. package/dist-lib/beta.cjs +1 -1
  137. package/dist-lib/beta.mjs +1 -1
  138. package/dist-lib/bridge-constants.cjs +4 -4
  139. package/dist-lib/bridge-contract-input.cjs +1 -1
  140. package/dist-lib/bridge-contract-input.mjs +1 -1
  141. package/dist-lib/bridge-contract.cjs +2 -2
  142. package/dist-lib/bridge-contract.mjs +2 -2
  143. package/dist-lib/capture-contract.cjs +1 -1
  144. package/dist-lib/capture-contract.mjs +1 -1
  145. package/dist-lib/cli-constants.cjs +1 -1
  146. package/dist-lib/cloud-contract.cjs +1 -1
  147. package/dist-lib/cloud-contract.mjs +1 -1
  148. package/dist-lib/cloud-test-args.cjs +1 -1
  149. package/dist-lib/cloud-test-args.mjs +1 -1
  150. package/dist-lib/cloud.cjs +42 -42
  151. package/dist-lib/cloud.mjs +42 -42
  152. package/dist-lib/config.cjs +2 -2
  153. package/dist-lib/detox/index.cjs +29 -29
  154. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  155. package/dist-lib/home-paths.cjs +12 -12
  156. package/dist-lib/host/bridge-host.cjs +72 -71
  157. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  158. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  159. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  160. package/dist-lib/host/replacement-module-handler.cjs +6 -6
  161. package/dist-lib/host/websocket-proxy.cjs +1 -1
  162. package/dist-lib/index.cjs +48 -48
  163. package/dist-lib/jump-to-source-babel.cjs +1 -1
  164. package/dist-lib/jump-to-source-native.cjs +1 -1
  165. package/dist-lib/menu.cjs +5 -5
  166. package/dist-lib/menu.mjs +5 -5
  167. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  168. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  169. package/dist-lib/metro-production-bundle.cjs +1 -1
  170. package/dist-lib/metro-production-bundle.mjs +1 -1
  171. package/dist-lib/metro.cjs +15 -15
  172. package/dist-lib/profiles.cjs +4 -4
  173. package/dist-lib/public-brand.cjs +3 -3
  174. package/dist-lib/react-native-host-modules.cjs +1 -1
  175. package/dist-lib/react-native-host-modules.mjs +1 -1
  176. package/dist-lib/render-mode.cjs +1 -1
  177. package/dist-lib/scripts/dev-server-scanner.cjs +2 -1
  178. package/dist-lib/sdk.cjs +9 -9
  179. package/dist-lib/sdk.mjs +9 -9
  180. package/dist-lib/skills.cjs +416 -415
  181. package/dist-lib/swift-compile.cjs +1 -1
  182. package/dist-lib/swift-compile.mjs +1 -1
  183. package/dist-lib/swift-package.cjs +1 -1
  184. package/dist-lib/swift-package.mjs +1 -1
  185. package/dist-lib/swift-submit.cjs +1 -1
  186. package/dist-lib/swift-submit.mjs +1 -1
  187. package/dist-lib/swift.cjs +1 -1
  188. package/dist-lib/vite.cjs +24 -24
  189. package/package.json +1 -1
  190. package/scripts/dev-server-scanner.ts +4 -3
  191. package/skills/contrast/SKILL.md +75 -75
  192. package/skills/{rnx-debug → peach-debug}/SKILL.md +90 -90
  193. package/skills/{rnx-perf → peach-perf}/SKILL.md +13 -13
  194. package/skills/{rnx-setup → peach-setup}/SKILL.md +60 -60
  195. package/skills/{rnx-test → peach-test}/SKILL.md +44 -44
  196. package/skills/{rnx-visual → peach-visual}/SKILL.md +26 -26
  197. package/src/agent-daemon-client.ts +5 -5
  198. package/src/agent-events.ts +1 -1
  199. package/src/agent-identity.ts +1 -1
  200. package/src/agent-sessions.ts +13 -13
  201. package/src/attached-projects.ts +1 -1
  202. package/src/auth/shared-session.ts +1 -1
  203. package/src/bridge-constants.ts +3 -3
  204. package/src/bridge-contract-input.ts +1 -1
  205. package/src/bridge-contract.ts +3 -3
  206. package/src/cli-version.ts +6 -6
  207. package/src/cloud.ts +40 -40
  208. package/src/config.ts +1 -1
  209. package/src/connect.ts +6 -6
  210. package/src/dev-server-open.ts +8 -8
  211. package/src/disk-cleanup.ts +3 -3
  212. package/src/home-paths.ts +26 -26
  213. package/src/host/agent-host.ts +3 -3
  214. package/src/host/bridge-host.ts +33 -33
  215. package/src/host/fetch-proxy-handler.ts +1 -1
  216. package/src/host/plane-host.ts +2 -2
  217. package/src/host/replacement-module-handler.ts +2 -2
  218. package/src/menu.ts +2 -2
  219. package/src/metro-plugin.ts +7 -7
  220. package/src/metro-production-bundle.ts +2 -2
  221. package/src/native-dev-bundle-url.ts +2 -2
  222. package/src/playwright-host-log.ts +1 -1
  223. package/src/public-brand.ts +2 -2
  224. package/src/react-native-host-modules.ts +1 -1
  225. package/src/rolldown-plugin-swift.ts +3 -3
  226. package/src/runtime-assets.ts +11 -11
  227. package/src/runtime-delivery.ts +6 -6
  228. package/src/screenshots/orchestrate.ts +1 -1
  229. package/src/screenshots/org-deck.ts +1 -1
  230. package/src/screenshots/schema.ts +2 -2
  231. package/src/sim-client.ts +1 -1
  232. package/src/skills/builtin/compat-check.ts +2 -2
  233. package/src/vite-plugin-one.ts +8 -8
  234. package/src/vite-plugin-swift.ts +3 -3
  235. package/src/vite-plugin.ts +6 -19
package/src/cloud.ts CHANGED
@@ -78,7 +78,7 @@ export interface ConnectRemoteSimulatorInput {
78
78
  }
79
79
 
80
80
  // the simulator-driving verbs. the same vocabulary the CLI drives, one for
81
- // one: `rnx describe`, `rnx do tap`, `rnx wait selector`.
81
+ // one: `peach describe`, `peach do tap`, `peach wait selector`.
82
82
  export interface RnxCommands {
83
83
  command(
84
84
  command: { type: string; [key: string]: unknown },
@@ -130,7 +130,7 @@ function normalizeCloudOrigin(source: string): string {
130
130
  try {
131
131
  parsed = new URL(source)
132
132
  } catch {
133
- throw new Error(`invalid RNX Cloud origin: ${source}`)
133
+ throw new Error(`invalid Peach Cloud origin: ${source}`)
134
134
  }
135
135
  if (
136
136
  (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') ||
@@ -140,16 +140,16 @@ function normalizeCloudOrigin(source: string): string {
140
140
  parsed.search ||
141
141
  parsed.hash
142
142
  ) {
143
- throw new Error(`RNX Cloud origin must be an HTTP(S) origin: ${source}`)
143
+ throw new Error(`Peach Cloud origin must be an HTTP(S) origin: ${source}`)
144
144
  }
145
145
  if (parsed.protocol === 'http:' && !isLoopbackHost(parsed.hostname)) {
146
- throw new Error(`RNX Cloud origin requires HTTPS outside loopback: ${source}`)
146
+ throw new Error(`Peach Cloud origin requires HTTPS outside loopback: ${source}`)
147
147
  }
148
148
  return parsed.origin
149
149
  }
150
150
 
151
151
  function resolveOrigin(value?: string): string {
152
- const configured = value?.trim() || process.env.RNX_CLOUD_ORIGIN?.trim()
152
+ const configured = value?.trim() || process.env.PEACH_CLOUD_ORIGIN?.trim()
153
153
  return normalizeCloudOrigin(configured || rnxPublicBrand.origin)
154
154
  }
155
155
 
@@ -159,14 +159,14 @@ function responseError(status: number, text: string): Error {
159
159
  const parsed: unknown = JSON.parse(text)
160
160
  const error = isRecord(parsed) ? parsed.error : null
161
161
  if (isRecord(error) && typeof error.message === 'string') {
162
- return new Error(`RNX Cloud request failed (${status}): ${error.message}`)
162
+ return new Error(`Peach Cloud request failed (${status}): ${error.message}`)
163
163
  }
164
164
  } catch {
165
165
  // body was not json; fall through to the raw text
166
166
  }
167
167
  }
168
168
  return new Error(
169
- `RNX Cloud request failed (${status})${text.trim() ? `: ${text.trim()}` : ''}`,
169
+ `Peach Cloud request failed (${status})${text.trim() ? `: ${text.trim()}` : ''}`,
170
170
  )
171
171
  }
172
172
 
@@ -269,10 +269,10 @@ function readJsArtifact(bytes: Uint8Array): string {
269
269
  try {
270
270
  source = new TextDecoder('utf-8', { fatal: true }).decode(bytes)
271
271
  } catch {
272
- throw new Error('rnx Cloud artifact must be valid UTF-8 JavaScript, not bytecode')
272
+ throw new Error('peach Cloud artifact must be valid UTF-8 JavaScript, not bytecode')
273
273
  }
274
274
  if (source.includes('\0') || !source.includes('__d(') || !source.includes('__r(')) {
275
- throw new Error('rnx Cloud artifact must be a Metro JavaScript bundle')
275
+ throw new Error('peach Cloud artifact must be a Metro JavaScript bundle')
276
276
  }
277
277
  return source
278
278
  }
@@ -284,7 +284,7 @@ function readClaim(value: unknown): RnxCloudClaim {
284
284
  !value.id ||
285
285
  typeof value.expiresAt !== 'number'
286
286
  ) {
287
- throw new Error('RNX Cloud returned an invalid claim')
287
+ throw new Error('Peach Cloud returned an invalid claim')
288
288
  }
289
289
  return { id: value.id, expiresAt: value.expiresAt }
290
290
  }
@@ -305,7 +305,7 @@ function readArtifact(
305
305
  !Number.isInteger(value.bytes) ||
306
306
  value.bytes !== expectedBytes
307
307
  ) {
308
- throw new Error('RNX Cloud returned an invalid artifact receipt')
308
+ throw new Error('Peach Cloud returned an invalid artifact receipt')
309
309
  }
310
310
  return { id: expectedId, bytes: expectedBytes }
311
311
  }
@@ -320,14 +320,14 @@ function readSimulatorCreate(
320
320
  claim: RnxCloudClaim
321
321
  artifact: RnxCloudBoxCreateReceipt['artifact']
322
322
  } {
323
- if (!isRecord(value)) throw new Error('RNX Cloud create returned an invalid simulator')
323
+ if (!isRecord(value)) throw new Error('Peach Cloud create returned an invalid simulator')
324
324
  const claim = readClaim(value.claim)
325
325
  if (
326
326
  typeof value.simId !== 'string' ||
327
327
  !value.simId ||
328
328
  typeof value.token !== 'string'
329
329
  ) {
330
- throw new Error('RNX Cloud create returned an invalid simulator')
330
+ throw new Error('Peach Cloud create returned an invalid simulator')
331
331
  }
332
332
  return {
333
333
  simId: value.simId,
@@ -347,7 +347,7 @@ function readBoxCreate(
347
347
  simulatorToken: string
348
348
  } {
349
349
  const artifactId = artifactDigestId(expectedSha256)
350
- if (!isRecord(value)) throw new Error('RNX Cloud create returned invalid JSON')
350
+ if (!isRecord(value)) throw new Error('Peach Cloud create returned invalid JSON')
351
351
  const simulator = readSimulatorCreate(value.simulator, artifactId, expectedBytes)
352
352
  if (
353
353
  typeof value.boxId !== 'string' ||
@@ -356,7 +356,7 @@ function readBoxCreate(
356
356
  typeof value.token !== 'string' ||
357
357
  !value.token
358
358
  ) {
359
- throw new Error('RNX Cloud create returned an invalid box receipt')
359
+ throw new Error('Peach Cloud create returned an invalid box receipt')
360
360
  }
361
361
  return {
362
362
  receipt: {
@@ -383,14 +383,14 @@ function cloudCommand(
383
383
  if (key !== 'id' && key !== 'simId') clean[key] = value
384
384
  }
385
385
  if (!isRnxCloudCommandType(command.type)) {
386
- throw new Error(`RNX Cloud does not support bridge command type ${command.type}`)
386
+ throw new Error(`Peach Cloud does not support bridge command type ${command.type}`)
387
387
  }
388
388
  // a verb outside this token's scopes never leaves the sdk: the refusal
389
389
  // names the scope before a request. the full token carries every scope
390
390
  // except debug, so evaluate/call/close stop here for it.
391
391
  if (!rnxCloudScopesAllowCommand(scopes, command.type)) {
392
392
  throw new Error(
393
- `RNX Cloud command ${command.type} requires the ${rnxCloudCommandScope(command.type)} scope`,
393
+ `Peach Cloud command ${command.type} requires the ${rnxCloudCommandScope(command.type)} scope`,
394
394
  )
395
395
  }
396
396
  return { ...clean, type: command.type }
@@ -427,14 +427,14 @@ export class RemoteSimulator {
427
427
  layers: options?.layers ?? 'tenant',
428
428
  })
429
429
  if (typeof result !== 'string' || !result.startsWith('data:image/')) {
430
- throw new Error('RNX Cloud screenshot did not return an image data url')
430
+ throw new Error('Peach Cloud screenshot did not return an image data url')
431
431
  }
432
432
  return result
433
433
  },
434
434
  capture: async () => {
435
435
  const result = await this.sendCommand({ type: 'capture' })
436
436
  if (!isRnxScreenCapture(result)) {
437
- throw new Error('RNX Cloud capture returned an invalid screen capture')
437
+ throw new Error('Peach Cloud capture returned an invalid screen capture')
438
438
  }
439
439
  return result
440
440
  },
@@ -444,13 +444,13 @@ export class RemoteSimulator {
444
444
  options.strategy !== 'data' &&
445
445
  options.strategy !== 'full'
446
446
  ) {
447
- throw new Error('RNX Cloud reset strategy must be data or full')
447
+ throw new Error('Peach Cloud reset strategy must be data or full')
448
448
  }
449
449
  if (
450
450
  options?.from !== undefined &&
451
451
  (typeof options.from !== 'string' || options.from.length === 0)
452
452
  ) {
453
- throw new Error('RNX Cloud reset from must name a simulator')
453
+ throw new Error('Peach Cloud reset from must name a simulator')
454
454
  }
455
455
  return this.sendCommand(
456
456
  options === undefined
@@ -525,7 +525,7 @@ export class RemoteSimulator {
525
525
  )
526
526
  } catch (error) {
527
527
  throw new Error(
528
- `could not reach RNX Cloud at ${this.session.origin}: ${
528
+ `could not reach Peach Cloud at ${this.session.origin}: ${
529
529
  error instanceof Error ? error.message : String(error)
530
530
  }`,
531
531
  )
@@ -536,14 +536,14 @@ export class RemoteSimulator {
536
536
  try {
537
537
  parsed = JSON.parse(text)
538
538
  } catch {
539
- throw new Error('RNX Cloud command returned unreadable JSON')
539
+ throw new Error('Peach Cloud command returned unreadable JSON')
540
540
  }
541
541
  if (!isRecord(parsed) || parsed.id !== body.id) {
542
- throw new Error('RNX Cloud command returned an invalid command receipt')
542
+ throw new Error('Peach Cloud command returned an invalid command receipt')
543
543
  }
544
544
  if (typeof parsed.error === 'string' && parsed.error) throw new Error(parsed.error)
545
545
  if (!Object.hasOwn(parsed, 'result')) {
546
- throw new Error('RNX Cloud command receipt has no result')
546
+ throw new Error('Peach Cloud command receipt has no result')
547
547
  }
548
548
  return parsed.result
549
549
  }
@@ -570,11 +570,11 @@ export class RemoteSimulator {
570
570
  try {
571
571
  parsed = JSON.parse(text)
572
572
  } catch {
573
- throw new Error('RNX Cloud claim returned unreadable JSON')
573
+ throw new Error('Peach Cloud claim returned unreadable JSON')
574
574
  }
575
575
  const claim = readClaim(isRecord(parsed) ? parsed.claim : null)
576
576
  if (claim.id !== claimId) {
577
- throw new Error('RNX Cloud confirmation replaced the initial claim')
577
+ throw new Error('Peach Cloud confirmation replaced the initial claim')
578
578
  }
579
579
  this.session = { ...this.session, claim }
580
580
  return claim
@@ -611,17 +611,17 @@ export async function createRemoteSimulator(
611
611
  ): Promise<RemoteSimulator> {
612
612
  if (input.artifact.byteLength > RNX_CLOUD_MAX_ARTIFACT_BYTES) {
613
613
  throw new Error(
614
- `rnx Cloud artifact is ${input.artifact.byteLength} bytes and exceeds the ${RNX_CLOUD_MAX_ARTIFACT_BYTES}-byte limit`,
614
+ `peach Cloud artifact is ${input.artifact.byteLength} bytes and exceeds the ${RNX_CLOUD_MAX_ARTIFACT_BYTES}-byte limit`,
615
615
  )
616
616
  }
617
617
  if (input.artifact.byteLength <= 0) {
618
- throw new Error('rnx Cloud artifact is empty')
618
+ throw new Error('peach Cloud artifact is empty')
619
619
  }
620
620
  readJsArtifact(input.artifact)
621
621
  const origin = resolveOrigin(input.origin)
622
622
  const sha256 = await sha256Hex(input.artifact)
623
623
  if (input.sha256 && input.sha256 !== sha256) {
624
- throw new Error('rnx Cloud artifact sha256 does not match the provided digest')
624
+ throw new Error('peach Cloud artifact sha256 does not match the provided digest')
625
625
  }
626
626
  const device = input.device?.trim() || DEFAULT_DEVICE
627
627
  let response: Response
@@ -640,7 +640,7 @@ export async function createRemoteSimulator(
640
640
  })
641
641
  } catch (error) {
642
642
  throw new Error(
643
- `could not reach RNX Cloud at ${origin}: ${
643
+ `could not reach Peach Cloud at ${origin}: ${
644
644
  error instanceof Error ? error.message : String(error)
645
645
  }`,
646
646
  )
@@ -651,7 +651,7 @@ export async function createRemoteSimulator(
651
651
  try {
652
652
  parsed = JSON.parse(responseText)
653
653
  } catch {
654
- throw new Error('RNX Cloud create returned unreadable JSON')
654
+ throw new Error('Peach Cloud create returned unreadable JSON')
655
655
  }
656
656
  const created = readBoxCreate(parsed, input.artifact.byteLength, sha256)
657
657
  const simulator = new RemoteSimulator({
@@ -677,7 +677,7 @@ function readSimArtifact(value: unknown): RnxCloudCreateReceipt['artifact'] {
677
677
  !Number.isInteger(value.bytes) ||
678
678
  value.bytes <= 0
679
679
  ) {
680
- throw new Error('RNX Cloud returned an invalid simulator artifact')
680
+ throw new Error('Peach Cloud returned an invalid simulator artifact')
681
681
  }
682
682
  return { id: `sha256:${value.sha256}`, bytes: value.bytes }
683
683
  }
@@ -707,7 +707,7 @@ function readConnectScopes(
707
707
  ) ||
708
708
  typeof value.expiresAt !== 'number'
709
709
  ) {
710
- throw new Error('RNX Cloud returned an invalid scoped token')
710
+ throw new Error('Peach Cloud returned an invalid scoped token')
711
711
  }
712
712
  return Object.freeze([...value.scopes])
713
713
  }
@@ -718,8 +718,8 @@ function readConnectScopes(
718
718
  export async function connect(
719
719
  input: ConnectRemoteSimulatorInput,
720
720
  ): Promise<RemoteSimulator> {
721
- if (!input.id) throw new Error('RNX Cloud connect needs a simulator id')
722
- if (!input.token) throw new Error('RNX Cloud connect needs a token')
721
+ if (!input.id) throw new Error('Peach Cloud connect needs a simulator id')
722
+ if (!input.token) throw new Error('Peach Cloud connect needs a token')
723
723
  const origin = resolveOrigin(input.origin)
724
724
  let response: Response
725
725
  try {
@@ -729,7 +729,7 @@ export async function connect(
729
729
  )
730
730
  } catch (error) {
731
731
  throw new Error(
732
- `could not reach RNX Cloud at ${origin}: ${
732
+ `could not reach Peach Cloud at ${origin}: ${
733
733
  error instanceof Error ? error.message : String(error)
734
734
  }`,
735
735
  )
@@ -740,14 +740,14 @@ export async function connect(
740
740
  try {
741
741
  parsed = JSON.parse(text)
742
742
  } catch {
743
- throw new Error('RNX Cloud connect returned unreadable JSON')
743
+ throw new Error('Peach Cloud connect returned unreadable JSON')
744
744
  }
745
745
  if (!isRecord(parsed) || !isRecord(parsed.sim)) {
746
- throw new Error('RNX Cloud connect returned an invalid simulator')
746
+ throw new Error('Peach Cloud connect returned an invalid simulator')
747
747
  }
748
748
  const sim = parsed.sim
749
749
  if (typeof sim.simId !== 'string' || !sim.simId) {
750
- throw new Error('RNX Cloud connect returned an invalid simulator')
750
+ throw new Error('Peach Cloud connect returned an invalid simulator')
751
751
  }
752
752
  return new RemoteSimulator({
753
753
  origin,
package/src/config.ts CHANGED
@@ -114,7 +114,7 @@ export function readRNXConfigFromSearchParams<
114
114
  return hasRNXConfig(parsed) ? parsed : undefined
115
115
  } catch (error) {
116
116
  const message = error instanceof Error ? error.message : String(error)
117
- console.warn(`[rnx] invalid ${RNX_CONFIG_QUERY_PARAM}: ${message}`)
117
+ console.warn(`[peach] invalid ${RNX_CONFIG_QUERY_PARAM}: ${message}`)
118
118
  return undefined
119
119
  }
120
120
  }
package/src/connect.ts CHANGED
@@ -15,7 +15,7 @@ import { SimClient, SimConnectError } from './sim-client'
15
15
  export interface ConnectOptions {
16
16
  /** ws bridge port. default: the resolved bridge world (lockfiles). */
17
17
  port?: number
18
- /** sim id to attach to (as shown by `rnx list`). default: the primary sim. */
18
+ /** sim id to attach to (as shown by `peach list`). default: the primary sim. */
19
19
  sim?: string
20
20
  /** per-command bridge timeout in ms. default 15000. */
21
21
  timeoutMs?: number
@@ -34,13 +34,13 @@ function pickSim(
34
34
  `no sim connected with id ${wanted} on bridge :${port}` +
35
35
  (open.length
36
36
  ? ` — connected: ${open.map((sim) => sim.id).join(', ')}`
37
- : ' — no sims connected; open one with `rnx open <port>`'),
37
+ : ' — no sims connected; open one with `peach open <port>`'),
38
38
  )
39
39
  }
40
40
  return match
41
41
  }
42
- // same session affinity as the CLI: a sim pinned by `rnx open` /
43
- // `rnx use` in this terminal wins over the primary election, so a script
42
+ // same session affinity as the CLI: a sim pinned by `peach open` /
43
+ // `peach use` in this terminal wins over the primary election, so a script
44
44
  // run next to CLI commands drives the sim the terminal is working with. a
45
45
  // stale pin (sim gone) falls through to the primary.
46
46
  const pinned = readCurrentSimId()
@@ -51,7 +51,7 @@ function pickSim(
51
51
  const primary = open.find((sim) => sim.isPrimary) ?? open[0]
52
52
  if (!primary) {
53
53
  throw new SimConnectError(
54
- `no sims connected on bridge :${port} — open one with \`rnx open <port>\``,
54
+ `no sims connected on bridge :${port} — open one with \`peach open <port>\``,
55
55
  )
56
56
  }
57
57
  return primary
@@ -85,7 +85,7 @@ export async function connect(options: ConnectOptions = {}): Promise<SimClient>
85
85
  discovery.close()
86
86
  if (error instanceof SimConnectError) throw error
87
87
  const message = error instanceof Error ? error.message : String(error)
88
- throw new SimConnectError(`${message} — is the rnx daemon or dev shell running?`)
88
+ throw new SimConnectError(`${message} — is the peach daemon or dev shell running?`)
89
89
  }
90
90
  discovery.close()
91
91
 
@@ -16,7 +16,7 @@ export function resolveRnxDesktopAppName(
16
16
  if (!open) return null
17
17
  if (typeof open === 'object') {
18
18
  const appName = open.appName.trim()
19
- if (!appName) throw new Error('rnx: open.appName must not be empty')
19
+ if (!appName) throw new Error('peach: open.appName must not be empty')
20
20
  return appName
21
21
  }
22
22
 
@@ -27,7 +27,7 @@ export function resolveRnxDesktopAppName(
27
27
  ? Reflect.get(packageJson, 'name')
28
28
  : undefined
29
29
  if (typeof packageName !== 'string' || !packageName.trim()) {
30
- throw new Error(`rnx: ${packageJsonPath} must declare a package name`)
30
+ throw new Error(`peach: ${packageJsonPath} must declare a package name`)
31
31
  }
32
32
  return packageName.trim()
33
33
  }
@@ -40,7 +40,7 @@ export function tagRnxBundleForPlugin(
40
40
  appName?: string | null,
41
41
  ): string {
42
42
  const absolute = /^[a-z][a-z\d+.-]*:\/\//i.test(bundleUrl)
43
- const url = new URL(bundleUrl, absolute ? undefined : 'http://rnx.local')
43
+ const url = new URL(bundleUrl, absolute ? undefined : 'http://peach.local')
44
44
  if (appName) {
45
45
  url.searchParams.set('rnxAppName', appName)
46
46
  url.searchParams.set(
@@ -73,23 +73,23 @@ export function observeRnxDesktopLaunch(child: ChildProcess, appName: string): v
73
73
  })
74
74
  child.once('error', (error) => {
75
75
  spawnFailed = true
76
- console.error(`[rnx] could not open ${appName}: ${error.message}`)
76
+ console.error(`[peach] could not open ${appName}: ${error.message}`)
77
77
  })
78
78
  child.once('close', (code, signal) => {
79
79
  if (spawnFailed) return
80
80
  if (code === 0) {
81
- console.log(`[rnx] opened ${appName} in the desktop simulator`)
81
+ console.log(`[peach] opened ${appName} in the desktop simulator`)
82
82
  return
83
83
  }
84
84
  const detail = stderr.trim() || (signal ? `signal ${signal}` : `exit ${code}`)
85
- console.error(`[rnx] could not open ${appName}: ${detail}`)
85
+ console.error(`[peach] could not open ${appName}: ${detail}`)
86
86
  })
87
87
  }
88
88
 
89
89
  export function launchRnxDesktop(bundleUrl: string, appName: string): void {
90
90
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
91
91
  const binPath = path.join(packageRoot, 'dist-cli', 'bin.js')
92
- console.log(`[rnx] opening ${appName} in the desktop simulator`)
92
+ console.log(`[peach] opening ${appName} in the desktop simulator`)
93
93
  const child = spawn(
94
94
  process.execPath,
95
95
  [binPath, ...buildRnxDesktopOpenArgs(bundleUrl)],
@@ -141,7 +141,7 @@ export function openRnxDesktopWhenMetroIsReady({
141
141
  if (finished) return
142
142
  if (Date.now() >= deadline) {
143
143
  console.error(
144
- `[rnx] could not open ${appName}: Metro did not become ready on port ${port}`,
144
+ `[peach] could not open ${appName}: Metro did not become ready on port ${port}`,
145
145
  )
146
146
  finish(false)
147
147
  return
@@ -11,7 +11,7 @@ const AUTOMATIC_CLEANUP_LOCK_STALE_MS = 6 * 60 * 60 * 1000
11
11
  // a deferral is recorded so the retry does not happen on literally every
12
12
  // command. cleanup defers whenever anything is in use, and on a machine that
13
13
  // always has a sim or daemon running that is the permanent state: the complete
14
- // marker is never written, so every rnx invocation spawned a detached worker
14
+ // marker is never written, so every peach invocation spawned a detached worker
15
15
  // that rescanned the home directory, reclaimed nothing, and printed the same
16
16
  // "N is in use and will be retried on a later run" line into the caller's
17
17
  // stderr. measured on this machine: an identical 662.2 MiB across every
@@ -209,7 +209,7 @@ function collectRecordingEntries(
209
209
  })
210
210
  }
211
211
 
212
- // videos staged by `rnx camera play`. a fixture is only a copy of a file
212
+ // videos staged by `peach camera play`. a fixture is only a copy of a file
213
213
  // the user still has, so it is cheap to reclaim, but the automatic sweep must
214
214
  // not pull one out from under a sim that is playing it right now. aggressive
215
215
  // only, matching recordings.
@@ -336,7 +336,7 @@ export function planSootsimCleanup(
336
336
  function assertInsideHome(home: string, target: string): void {
337
337
  const relative = path.relative(home, target)
338
338
  if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) {
339
- throw new Error(`refusing cleanup target outside the rnx data home: ${target}`)
339
+ throw new Error(`refusing cleanup target outside the peach data home: ${target}`)
340
340
  }
341
341
  }
342
342
 
package/src/home-paths.ts CHANGED
@@ -1,10 +1,10 @@
1
- // canonical filesystem layout under ~/.rnx/. shared by the CLI and the
1
+ // canonical filesystem layout under ~/.peach/. shared by the CLI and the
2
2
  // electron main process so every surface agrees where runtimes, the daemon
3
3
  // lockfile, and caches live.
4
4
  //
5
- // ~/.rnx/
5
+ // ~/.peach/
6
6
  // ├── runtimes/
7
- // │ ├── <version>/ unpacked dist/ of sootsim-engine
7
+ // │ ├── <version>/ unpacked dist/ of peach-engine
8
8
  // │ └── active text file with the active version string (win-safe
9
9
  // │ alternative to a symlink)
10
10
  // ├── electron/
@@ -12,7 +12,7 @@
12
12
  // ├── profiles/
13
13
  // │ └── profiles.json storage profile metadata
14
14
  // ├── cache/
15
- // │ └── rnx-runtime-<version>.tar.gz
15
+ // │ └── peach-runtime-<version>.tar.gz
16
16
  // ├── daemon.json lockfile: pid, ports, active runtime, heartbeat
17
17
  // ├── dev-bridges/ live Vite dev bridge records, keyed by bound port
18
18
  // ├── runtime-upgraded.json one-shot notice written by the daemon's
@@ -29,7 +29,7 @@ import fs from 'node:fs'
29
29
  import { homedir } from 'node:os'
30
30
  import path from 'node:path'
31
31
 
32
- export const RNX_HOME_ENV = 'RNX_HOME'
32
+ export const PEACH_HOME_ENV = 'PEACH_HOME'
33
33
  export const ACTIVE_RUNTIME_FILE = 'active'
34
34
  export const DAEMON_LOCKFILE = 'daemon.json'
35
35
  export const DEV_BRIDGES_DIR = 'dev-bridges'
@@ -38,12 +38,12 @@ export const CLI_UPDATE_FILE = 'cli-update.json'
38
38
  export const DAEMON_HEARTBEAT_STALE_MS = 30_000
39
39
 
40
40
  export function rnxHomeDir(): string {
41
- const override = process.env[RNX_HOME_ENV]
41
+ const override = process.env[PEACH_HOME_ENV]
42
42
  if (override && override.length > 0) return path.resolve(override)
43
- return path.join(homedir(), '.rnx')
43
+ return path.join(homedir(), '.peach')
44
44
  }
45
45
 
46
- // detect when sootsim is running from a source checkout (the Contrast monorepo)
46
+ // detect when peach is running from a source checkout (the Contrast monorepo)
47
47
  // rather than a published npm install. used to skip auto-install of the
48
48
  // persistent launchd / systemd agent: dev shells shouldn't register an agent
49
49
  // whose Program path points at workspace artifacts that change between
@@ -57,10 +57,10 @@ export function rnxHomeDir(): string {
57
57
  // install path from this repo
58
58
  //
59
59
  // signal: realpath of process.argv[1] lands inside a `packages/peachsim/`
60
- // directory. workspace bin symlinks (`node_modules/.bin/sootsim` →
60
+ // directory. workspace bin symlinks (`node_modules/.bin/peachsim` →
61
61
  // `packages/peachsim/dist-cli/bin.js`) and bun-direct invocations
62
62
  // (`bun packages/peachsim/cli/bin.ts ...`) both match; published installs
63
- // resolve under `node_modules/sootsim/` instead.
63
+ // resolve under `node_modules/peachsim/` instead.
64
64
  export function isSootsimDevCheckout(): boolean {
65
65
  if (process.env.SOOTSIM_FORCE_DAEMON_INSTALL === '1') return false
66
66
  const env = process.env.SOOTSIM_DEV
@@ -70,7 +70,7 @@ export function isSootsimDevCheckout(): boolean {
70
70
  if (!argv1) return false
71
71
  try {
72
72
  const real = fs.realpathSync(argv1)
73
- return real.includes(`${path.sep}packages${path.sep}sootsim${path.sep}`)
73
+ return real.includes(`${path.sep}packages${path.sep}peachsim${path.sep}`)
74
74
  } catch {
75
75
  return false
76
76
  }
@@ -95,8 +95,8 @@ export function isDevWorkstation(): boolean {
95
95
  // the persistent launchd / systemd daemon must never auto-install or run on a
96
96
  // dev checkout or a dev workstation. either makes the background agent point at
97
97
  // moving workspace artifacts / dev env and serve stale assets that shadow the
98
- // live dev stack. setup and the launchd-spawned `rnx serve` boot consult
99
- // this; a foreground `rnx serve` the dev starts
98
+ // live dev stack. setup and the launchd-spawned `peach serve` boot consult
99
+ // this; a foreground `peach serve` the dev starts
100
100
  // by hand is intentional and stays allowed.
101
101
  export function shouldSkipPersistentDaemon(): boolean {
102
102
  return isSootsimDevCheckout() || isDevWorkstation()
@@ -130,7 +130,7 @@ export function profilesDir(): string {
130
130
  return path.join(rnxHomeDir(), 'profiles')
131
131
  }
132
132
 
133
- // video fixtures staged for `rnx camera play`. the CLI copies the user's
133
+ // video fixtures staged for `peach camera play`. the CLI copies the user's
134
134
  // file in here and the daemon serves it by basename from this one directory,
135
135
  // so the route can never be talked into reading anywhere else on disk.
136
136
  export function cameraFixturesDir(): string {
@@ -148,11 +148,11 @@ export function daemonAppDir(): string {
148
148
  }
149
149
 
150
150
  export function daemonAppBundlePath(): string {
151
- return path.join(daemonAppDir(), 'RNX Daemon.app')
151
+ return path.join(daemonAppDir(), 'Peach Daemon.app')
152
152
  }
153
153
 
154
154
  export function daemonAppLauncherPath(): string {
155
- return path.join(daemonAppBundlePath(), 'Contents', 'MacOS', 'rnx-daemon')
155
+ return path.join(daemonAppBundlePath(), 'Contents', 'MacOS', 'peach-daemon')
156
156
  }
157
157
 
158
158
  export function cacheDir(): string {
@@ -182,9 +182,9 @@ export function cliUpdateFilePath(): string {
182
182
  return path.join(rnxHomeDir(), CLI_UPDATE_FILE)
183
183
  }
184
184
 
185
- // --- shared rnx config ---------------------------------------------------
185
+ // --- shared peach config ---------------------------------------------------
186
186
  //
187
- // ~/.rnx/config.json is the single user-level config file every rnx
187
+ // ~/.peach/config.json is the single user-level config file every peach
188
188
  // surface (cli, electron renderer, dev browser via electron ipc) agrees on.
189
189
  //
190
190
  // privacy preferences live in the nested settings object so the cli, desktop
@@ -293,8 +293,8 @@ export function readOrCreateAnonymousId(): string {
293
293
 
294
294
  // --- default box ----------------------------------------------------------
295
295
  //
296
- // the box every box verb acts on when the command line names none. `rnx box
297
- // use` writes it and `rnx box stop` clears it when it stops that box.
296
+ // the box every box verb acts on when the command line names none. `peach box
297
+ // use` writes it and `peach box stop` clears it when it stops that box.
298
298
  //
299
299
  // the id and the display name, and nothing else. a Box is reached with a
300
300
  // bearer token the account service hands out per attach, so writing one here
@@ -327,7 +327,7 @@ export function writeDefaultBox(box: DefaultBox | null): void {
327
327
  // the only place its "updated to vX" line lands is the daemon's own stderr,
328
328
  // which nobody reads. it also drops a one-shot notice file here; the next
329
329
  // interactive CLI invocation prints it once and deletes it, so users learn
330
- // their engine moved without having to run `rnx version`.
330
+ // their engine moved without having to run `peach version`.
331
331
 
332
332
  export interface RuntimeUpgradeNotice {
333
333
  from: string | null
@@ -421,7 +421,7 @@ export interface DaemonLockfile {
421
421
  /** http runtime server port (where electron loads the renderer from). */
422
422
  runtimePort: number
423
423
  /** active runtime version at boot, or null if the daemon booted with no
424
- * runtime installed. updated live when the user runs `sootsim runtime use`. */
424
+ * runtime installed. updated live when the user runs `peach runtime use`. */
425
425
  activeRuntime: string | null
426
426
  /** absolute path to the active runtime's dist directory, or null. */
427
427
  activeRuntimeDir: string | null
@@ -470,7 +470,7 @@ interface DaemonClaim {
470
470
  export function readDaemonLockfile(): DaemonLockfile | null {
471
471
  try {
472
472
  // cap the read so a junk/large file on the lockfile path can't OOM
473
- // the CLI when someone has been messing with ~/.rnx/.
473
+ // the CLI when someone has been messing with ~/.peach/.
474
474
  const fd = fs.openSync(daemonLockfilePath(), 'r')
475
475
  try {
476
476
  const buf = Buffer.alloc(DAEMON_LOCKFILE_MAX_BYTES)
@@ -618,17 +618,17 @@ export function writeDaemonLockfile(data: DaemonLockfile): void {
618
618
  const existing = readDaemonLockfile()
619
619
  if (!existing) {
620
620
  if (createDaemonLockfileExclusive(data)) return
621
- throw new Error('another rnx daemon claimed the lockfile before this write')
621
+ throw new Error('another peach daemon claimed the lockfile before this write')
622
622
  }
623
623
  if (!sameDaemonOwner(existing, data)) {
624
- throw new Error('refusing to overwrite another rnx daemon lockfile')
624
+ throw new Error('refusing to overwrite another peach daemon lockfile')
625
625
  }
626
626
  const tmp = `${lockfilePath}.${data.pid}.${randomUUID()}.tmp`
627
627
  fs.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}\n`, 'utf8')
628
628
  try {
629
629
  const current = readDaemonLockfile()
630
630
  if (!current || !sameDaemonOwner(current, data)) {
631
- throw new Error('rnx daemon lock ownership changed during heartbeat')
631
+ throw new Error('peach daemon lock ownership changed during heartbeat')
632
632
  }
633
633
  fs.renameSync(tmp, lockfilePath)
634
634
  } finally {
@@ -67,7 +67,7 @@ interface PendingPromptEcho {
67
67
  export interface AgentHostOptions {
68
68
  /** callback so hosts with a Contrast-env context (electron, dev middleware)
69
69
  * can inject their own exclude list. defaults to the env-derived list
70
- * the rest of the repo uses, so `rnx serve` standalone does the
70
+ * the rest of the repo uses, so `peach serve` standalone does the
71
71
  * right thing. */
72
72
  getExcludePorts?: () => number[]
73
73
  /** returns the active CLI lease for a sim. used only for prompt routing to
@@ -84,7 +84,7 @@ export interface AgentCliLeaseSnapshot {
84
84
  /** default excludes keep the dev-server scanner from hitting Contrast's own
85
85
  * web / zero / r2 ports. mirrored from
86
86
  * sootsim-engine/src/dev-scan-excludes.ts — duplicated rather than
87
- * imported because sootsim is a dependency of sootsim-engine, not the
87
+ * imported because peach is a dependency of sootsim-engine, not the
88
88
  * other way around. */
89
89
  function defaultExcludePorts(): number[] {
90
90
  return [
@@ -102,7 +102,7 @@ export class AgentHost {
102
102
  // every session.
103
103
  private sessionsBySocket = new Map<WebSocket, Set<string>>()
104
104
  // every connected socket (regardless of role) — gets session-status
105
- // pushes so a CLI `sootsim agent sessions --watch` can react to state
105
+ // pushes so a CLI `peach agent sessions --watch` can react to state
106
106
  // changes driven by another client.
107
107
  private allSockets = new Set<WebSocket>()
108
108
  // agent wrappers also emit prompt-received from the FIFO reader, but the