peachsim 0.1.570 → 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 (242) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +108 -108
  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 +28 -20
  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 +3 -2
  12. package/cli/cloud-session.ts +12 -12
  13. package/cli/cloud-test-result-files.ts +97 -0
  14. package/cli/cloud-test-tunnel.ts +4 -4
  15. package/cli/command-registry.ts +7 -7
  16. package/cli/commands/agent-wrapper.ts +7 -7
  17. package/cli/commands/agent.ts +16 -16
  18. package/cli/commands/app-fonts.ts +3 -3
  19. package/cli/commands/assert.ts +10 -10
  20. package/cli/commands/auth.ts +5 -5
  21. package/cli/commands/box/checkout-plane.ts +1 -1
  22. package/cli/commands/box/plane-share.ts +2 -2
  23. package/cli/commands/box/rnx-command.ts +5 -5
  24. package/cli/commands/box.ts +38 -38
  25. package/cli/commands/camera.ts +13 -13
  26. package/cli/commands/cleanup.ts +13 -13
  27. package/cli/commands/compat.ts +7 -7
  28. package/cli/commands/config.ts +5 -5
  29. package/cli/commands/control.ts +63 -63
  30. package/cli/commands/cpu-profile.ts +11 -11
  31. package/cli/commands/daemon-mac-app.ts +10 -10
  32. package/cli/commands/daemon.ts +29 -27
  33. package/cli/commands/debug.ts +35 -35
  34. package/cli/commands/desktop.ts +3 -3
  35. package/cli/commands/detox.ts +33 -33
  36. package/cli/commands/device.ts +9 -9
  37. package/cli/commands/diagnose.ts +17 -17
  38. package/cli/commands/do-chain.ts +4 -4
  39. package/cli/commands/electron.ts +9 -9
  40. package/cli/commands/film.ts +9 -9
  41. package/cli/commands/flow.ts +49 -49
  42. package/cli/commands/inspect/core.ts +12 -12
  43. package/cli/commands/inspect/count.ts +1 -1
  44. package/cli/commands/inspect/describe.ts +1 -1
  45. package/cli/commands/inspect/env.ts +2 -2
  46. package/cli/commands/inspect/find.ts +2 -2
  47. package/cli/commands/inspect/get-layout.ts +2 -2
  48. package/cli/commands/inspect/keyboard.ts +1 -1
  49. package/cli/commands/inspect/list.ts +4 -4
  50. package/cli/commands/inspect/memory.ts +1 -1
  51. package/cli/commands/inspect/resolve-target.ts +1 -1
  52. package/cli/commands/inspect/screens.ts +1 -1
  53. package/cli/commands/inspect/shared.ts +1 -1
  54. package/cli/commands/inspect/tree.ts +1 -1
  55. package/cli/commands/inspect/url.ts +1 -1
  56. package/cli/commands/inspect/wait-event.ts +5 -5
  57. package/cli/commands/inspect.ts +82 -82
  58. package/cli/commands/install-cli.ts +16 -16
  59. package/cli/commands/install-desktop.ts +26 -26
  60. package/cli/commands/login.ts +11 -11
  61. package/cli/commands/maestro-generate.ts +10 -10
  62. package/cli/commands/maestro.ts +173 -71
  63. package/cli/commands/mode.ts +4 -4
  64. package/cli/commands/no-bridge-hint.ts +12 -12
  65. package/cli/commands/perf.ts +11 -11
  66. package/cli/commands/permissions.ts +5 -5
  67. package/cli/commands/platform.ts +21 -21
  68. package/cli/commands/preview.ts +5 -5
  69. package/cli/commands/profile.ts +12 -12
  70. package/cli/commands/react.ts +15 -15
  71. package/cli/commands/record.ts +31 -31
  72. package/cli/commands/report-issue.ts +4 -4
  73. package/cli/commands/reset.ts +2 -2
  74. package/cli/commands/runtime.ts +23 -23
  75. package/cli/commands/screenshot-command.ts +2 -2
  76. package/cli/commands/screenshot-layers.ts +5 -5
  77. package/cli/commands/screenshot-mode.ts +7 -7
  78. package/cli/commands/screenshot.ts +24 -24
  79. package/cli/commands/screenshots-capture.ts +13 -13
  80. package/cli/commands/screenshots.ts +6 -6
  81. package/cli/commands/serve.ts +12 -12
  82. package/cli/commands/setup.ts +25 -25
  83. package/cli/commands/skills.ts +30 -23
  84. package/cli/commands/slides.ts +20 -20
  85. package/cli/commands/state.ts +2 -2
  86. package/cli/commands/storage.ts +3 -3
  87. package/cli/commands/telemetry.ts +2 -2
  88. package/cli/commands/test.ts +176 -18
  89. package/cli/commands/three-mode.ts +17 -17
  90. package/cli/commands/timeline.ts +12 -12
  91. package/cli/commands/upgrade.ts +11 -11
  92. package/cli/commands/upload.ts +26 -26
  93. package/cli/commands/version.ts +7 -7
  94. package/cli/commands/what-happened.ts +20 -20
  95. package/cli/current-sim.ts +3 -3
  96. package/cli/desktop-companion.ts +16 -16
  97. package/cli/drivers/electron.ts +4 -4
  98. package/cli/drivers/playwright-sim-host.ts +7 -7
  99. package/cli/drivers/playwright.ts +4 -4
  100. package/cli/drivers/registry.ts +1 -1
  101. package/cli/drivers/types.ts +5 -5
  102. package/cli/flow-export.ts +1 -1
  103. package/cli/flow-file.ts +5 -5
  104. package/cli/flow-session.ts +1 -1
  105. package/cli/help-core.ts +13 -13
  106. package/cli/hints.ts +14 -14
  107. package/cli/inspect-notice-state.ts +1 -1
  108. package/cli/internal-child.ts +2 -2
  109. package/cli/maestro-js.ts +4 -4
  110. package/cli/maestro-report.ts +4 -2
  111. package/cli/main.ts +14 -14
  112. package/cli/outbound-endpoints.ts +32 -30
  113. package/cli/parse-args.ts +7 -7
  114. package/cli/privacy.ts +1 -1
  115. package/cli/recording-access.ts +10 -10
  116. package/cli/resolve-assets.ts +2 -2
  117. package/cli/rnx-in-process.ts +5 -5
  118. package/cli/run-registry.ts +3 -2
  119. package/cli/run-rnx-core.ts +3 -3
  120. package/cli/run-rnx.ts +1 -1
  121. package/cli/runtime-notes.ts +1 -1
  122. package/cli/self-invocation.ts +1 -1
  123. package/cli/send-to-box.ts +1 -1
  124. package/cli/shell-init.ts +40 -40
  125. package/cli/ui.ts +1 -1
  126. package/cli/ws-bridge.ts +22 -22
  127. package/detox/config-loader.ts +1 -1
  128. package/detox/index.ts +28 -28
  129. package/detox/jest-preset.cjs +2 -2
  130. package/detox/navigation.ts +2 -2
  131. package/dist-lib/agent-daemon-client.cjs +12 -12
  132. package/dist-lib/agent-events.cjs +1 -1
  133. package/dist-lib/agent-identity.cjs +2 -2
  134. package/dist-lib/agent-sessions.cjs +12 -12
  135. package/dist-lib/attached-projects.cjs +4 -4
  136. package/dist-lib/auth/shared-session.cjs +4 -4
  137. package/dist-lib/backend-origin.cjs +1 -1
  138. package/dist-lib/beta.cjs +1 -1
  139. package/dist-lib/beta.mjs +1 -1
  140. package/dist-lib/bridge-constants.cjs +4 -4
  141. package/dist-lib/bridge-contract-input.cjs +1 -1
  142. package/dist-lib/bridge-contract-input.mjs +1 -1
  143. package/dist-lib/bridge-contract.cjs +2 -2
  144. package/dist-lib/bridge-contract.mjs +2 -2
  145. package/dist-lib/capture-contract.cjs +1 -1
  146. package/dist-lib/capture-contract.mjs +1 -1
  147. package/dist-lib/cli-constants.cjs +1 -1
  148. package/dist-lib/cloud-contract.cjs +1 -1
  149. package/dist-lib/cloud-contract.mjs +1 -1
  150. package/dist-lib/cloud-test-args.cjs +116 -0
  151. package/dist-lib/cloud-test-args.mjs +86 -0
  152. package/dist-lib/cloud.cjs +42 -42
  153. package/dist-lib/cloud.mjs +42 -42
  154. package/dist-lib/config.cjs +2 -2
  155. package/dist-lib/detox/index.cjs +29 -29
  156. package/dist-lib/detox/jest-preset.cjs +2 -2
  157. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  158. package/dist-lib/home-paths.cjs +12 -12
  159. package/dist-lib/host/bridge-host.cjs +87 -77
  160. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  161. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  162. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  163. package/dist-lib/host/replacement-module-handler.cjs +6 -6
  164. package/dist-lib/host/websocket-proxy.cjs +1 -1
  165. package/dist-lib/index.cjs +63 -73
  166. package/dist-lib/jump-to-source-babel.cjs +1 -1
  167. package/dist-lib/jump-to-source-native.cjs +1 -1
  168. package/dist-lib/menu.cjs +5 -5
  169. package/dist-lib/menu.mjs +5 -5
  170. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  171. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  172. package/dist-lib/metro-production-bundle.cjs +1 -1
  173. package/dist-lib/metro-production-bundle.mjs +1 -1
  174. package/dist-lib/metro.cjs +15 -15
  175. package/dist-lib/profiles.cjs +4 -4
  176. package/dist-lib/public-brand.cjs +3 -3
  177. package/dist-lib/react-native-host-modules.cjs +1 -1
  178. package/dist-lib/react-native-host-modules.mjs +1 -1
  179. package/dist-lib/render-mode.cjs +1 -1
  180. package/dist-lib/scripts/dev-server-scanner.cjs +2 -1
  181. package/dist-lib/sdk.cjs +9 -9
  182. package/dist-lib/sdk.mjs +9 -9
  183. package/dist-lib/skills.cjs +534 -520
  184. package/dist-lib/swift-compile.cjs +372 -0
  185. package/dist-lib/swift-compile.mjs +339 -0
  186. package/dist-lib/swift-package.cjs +78 -0
  187. package/dist-lib/swift-package.mjs +52 -0
  188. package/dist-lib/swift-submit.cjs +1 -1
  189. package/dist-lib/swift-submit.mjs +1 -1
  190. package/dist-lib/swift.cjs +1 -1
  191. package/dist-lib/vite.cjs +479 -161
  192. package/package.json +22 -1
  193. package/scripts/dev-server-scanner.ts +4 -3
  194. package/skills/contrast/SKILL.md +75 -75
  195. package/skills/{rnx-debug → peach-debug}/SKILL.md +90 -90
  196. package/skills/{rnx-perf → peach-perf}/SKILL.md +13 -13
  197. package/skills/{rnx-setup → peach-setup}/SKILL.md +60 -60
  198. package/skills/{rnx-test → peach-test}/SKILL.md +47 -47
  199. package/skills/{rnx-visual → peach-visual}/SKILL.md +26 -26
  200. package/src/agent-daemon-client.ts +5 -5
  201. package/src/agent-events.ts +1 -1
  202. package/src/agent-identity.ts +1 -1
  203. package/src/agent-sessions.ts +13 -13
  204. package/src/attached-projects.ts +1 -1
  205. package/src/auth/shared-session.ts +1 -1
  206. package/src/bridge-constants.ts +3 -3
  207. package/src/bridge-contract-input.ts +1 -1
  208. package/src/bridge-contract.ts +3 -3
  209. package/src/cli-version.ts +6 -6
  210. package/src/cloud-test-args.ts +100 -0
  211. package/src/cloud.ts +40 -40
  212. package/src/config.ts +1 -1
  213. package/src/connect.ts +6 -6
  214. package/src/dev-server-open.ts +8 -8
  215. package/src/disk-cleanup.ts +3 -3
  216. package/src/home-paths.ts +26 -26
  217. package/src/host/agent-host.ts +3 -3
  218. package/src/host/bridge-host.ts +33 -33
  219. package/src/host/fetch-proxy-handler.ts +1 -1
  220. package/src/host/plane-host.ts +2 -2
  221. package/src/host/replacement-module-handler.ts +2 -2
  222. package/src/menu.ts +2 -2
  223. package/src/metro-plugin.ts +7 -7
  224. package/src/metro-production-bundle.ts +2 -2
  225. package/src/native-dev-bundle-url.ts +2 -2
  226. package/src/playwright-host-log.ts +1 -1
  227. package/src/public-brand.ts +2 -2
  228. package/src/react-native-host-modules.ts +1 -1
  229. package/src/rolldown-plugin-swift.ts +11 -10
  230. package/src/runtime-assets.ts +11 -11
  231. package/src/runtime-delivery.ts +6 -6
  232. package/src/screenshots/orchestrate.ts +1 -1
  233. package/src/screenshots/org-deck.ts +1 -1
  234. package/src/screenshots/schema.ts +2 -2
  235. package/src/sim-client.ts +1 -1
  236. package/src/skills/builtin/compat-check.ts +2 -2
  237. package/src/swift-compile.ts +359 -0
  238. package/src/swift-package.ts +69 -0
  239. package/src/swift-submit.ts +3 -2
  240. package/src/vite-plugin-one.ts +8 -8
  241. package/src/vite-plugin-swift.ts +106 -110
  242. package/src/vite-plugin.ts +6 -19
@@ -1,7 +1,7 @@
1
1
  // node-side client for the daemon's agent:* ws protocol.
2
2
  //
3
3
  // used by electron main (translates IPC → daemon) and by the CLI
4
- // `sootsim agent …` subcommands. the browser shell has its own shim that
4
+ // `peach agent …` subcommands. the browser shell has its own shim that
5
5
  // uses window.WebSocket; this file is node-only.
6
6
  //
7
7
  // single long-lived connection per client. if the daemon goes away, the
@@ -105,7 +105,7 @@ export class AgentDaemonClient {
105
105
  this.ws.off('open', onOpen)
106
106
  reject(
107
107
  new AgentDaemonError(
108
- `could not connect to rnx daemon on port ${this.port}: ${err.message}`,
108
+ `could not connect to peach daemon on port ${this.port}: ${err.message}`,
109
109
  'NO_DAEMON',
110
110
  ),
111
111
  )
@@ -361,7 +361,7 @@ export async function ensureDaemonRunning(
361
361
  }
362
362
  if (shouldSkipPersistentDaemon()) {
363
363
  throw new AgentDaemonError(
364
- `no rnx bridge on port ${port}. run \`rnx daemon install\` to enable the faster background service, or \`rnx serve\` in another shell.`,
364
+ `no peach bridge on port ${port}. run \`peach daemon install\` to enable the faster background service, or \`peach serve\` in another shell.`,
365
365
  'DEV_HOST_REFUSED',
366
366
  )
367
367
  }
@@ -385,8 +385,8 @@ export async function ensureDaemonRunning(
385
385
  await new Promise((r) => setTimeout(r, 100))
386
386
  }
387
387
  throw new AgentDaemonError(
388
- `spawned rnx daemon on port ${port} but it did not come up in time. ` +
389
- `run \`rnx serve\` manually to diagnose.`,
388
+ `spawned peach daemon on port ${port} but it did not come up in time. ` +
389
+ `run \`peach serve\` manually to diagnose.`,
390
390
  'SPAWN_TIMEOUT',
391
391
  )
392
392
  }
@@ -1,7 +1,7 @@
1
1
  // wire format emitted by the agent-wrapper CLI subcommand on the events.out
2
2
  // FIFO. every line on the FIFO is exactly one JSON object — parse line-by-line.
3
3
  //
4
- // this file is the source of truth for the sootsim ↔ agent channel. it's
4
+ // this file is the source of truth for the peach ↔ agent channel. it's
5
5
  // imported by electron main (cockpit bridge), the CLI watch/transcript tools,
6
6
  // and the wrapper implementation itself, so it must stay runtime-safe (no
7
7
  // node-specific or electron-specific imports).
@@ -6,7 +6,7 @@ export function agentSessionIdFromCliIdentity(identity: string | null): string |
6
6
  if (colon < 0) return identity.trim() || null
7
7
  const source = identity.slice(0, colon)
8
8
  const value = identity.slice(colon + 1).trim()
9
- return source === 'RNX_CLI_IDENTITY' && value ? value : null
9
+ return source === 'PEACH_CLI_IDENTITY' && value ? value : null
10
10
  }
11
11
 
12
12
  export function teamMachineSessionIdFromCliIdentity(
@@ -1,5 +1,5 @@
1
- // session lifecycle shared between the CLI (`sootsim agent ...`) and electron
2
- // main (IPC handlers). both contexts spawn the same `sootsim agent-wrapper`
1
+ // session lifecycle shared between the CLI (`peach agent ...`) and electron
2
+ // main (IPC handlers). both contexts spawn the same `peach agent-wrapper`
3
3
  // subcommand, mutate the same AttachedProjects store, and subscribe to the
4
4
  // same events.out FIFO.
5
5
  //
@@ -67,10 +67,10 @@ export function pidIsAlive(pid: number | undefined, sessionId?: string): boolean
67
67
 
68
68
  // --- invocation resolution ---
69
69
 
70
- /** locate the rnx CLI so this process can run a subcommand of it. callers
70
+ /** locate the peach CLI so this process can run a subcommand of it. callers
71
71
  * range from the CLI itself to electron main and the vite-plugin daemon.
72
72
  * priority:
73
- * 1. RNX_BIN env var (explicit override)
73
+ * 1. PEACH_BIN env var (explicit override)
74
74
  * 2. this process already IS the CLI — `rnxSelfInvocation` owns that answer
75
75
  * 3. electron prod: bundled binary under process.resourcesPath/bin/
76
76
  * 4. workspace build artifacts (dist-bin/<binary> or dist-cli/bin.js)
@@ -80,8 +80,8 @@ export function pidIsAlive(pid: number | undefined, sessionId?: string): boolean
80
80
  * 5. CLI dev (argv[1] points at the entry script): re-use argv[0] + argv[1]
81
81
  */
82
82
  export function resolveSootsimInvocation(): RnxSelfInvocation {
83
- if (process.env.RNX_BIN) {
84
- return { executable: process.env.RNX_BIN, prefixArgs: [] }
83
+ if (process.env.PEACH_BIN) {
84
+ return { executable: process.env.PEACH_BIN, prefixArgs: [] }
85
85
  }
86
86
  // the compiled standalone binary cannot be found through argv: bun reports
87
87
  // argv[0] as the literal string "bun" and argv[1] as a path inside its
@@ -121,11 +121,11 @@ export function resolveSootsimInvocation(): RnxSelfInvocation {
121
121
  return { executable: process.argv[0], prefixArgs: [argv1] }
122
122
  }
123
123
  // anything else — a bin shim such as `…/node_modules/.bin/vite`, or no
124
- // argv[1] at all — names no rnx entry script, so say so instead of
124
+ // argv[1] at all — names no peach entry script, so say so instead of
125
125
  // spawning the host runtime with no arguments.
126
126
  throw new Error(
127
- 'rnx CLI not found. set RNX_BIN to the path of the rnx binary, ' +
128
- 'or build the workspace CLI via `bun run --cwd packages/sootsim build:cli`.',
127
+ 'peach CLI not found. set PEACH_BIN to the path of the peach binary, ' +
128
+ 'or build the workspace CLI via `bun run --cwd packages/peachsim build:cli`.',
129
129
  )
130
130
  }
131
131
 
@@ -133,7 +133,7 @@ function tryWorkspaceSootsim(): RnxSelfInvocation | null {
133
133
  try {
134
134
  const sootsimDir = resolveSootsimPackageDir()
135
135
  if (!sootsimDir) return null
136
- const binaryName = `rnx-${
136
+ const binaryName = `peach-${
137
137
  process.platform === 'win32' ? 'windows' : process.platform
138
138
  }-${process.arch}${process.platform === 'win32' ? '.exe' : ''}`
139
139
  const distBinary = path.join(sootsimDir, 'dist-bin', binaryName)
@@ -147,8 +147,8 @@ function tryWorkspaceSootsim(): RnxSelfInvocation | null {
147
147
  const buildMtime = fs.statSync(distBin).mtimeMs
148
148
  if (buildMtime < srcMtime) {
149
149
  console.warn(
150
- `[rnx] dist-cli/bin.js is older than agent-wrapper.ts — ` +
151
- `rebuild with \`bun run --cwd packages/sootsim build:cli\`.`,
150
+ `[peach] dist-cli/bin.js is older than agent-wrapper.ts — ` +
151
+ `rebuild with \`bun run --cwd packages/peachsim build:cli\`.`,
152
152
  )
153
153
  }
154
154
  }
@@ -342,7 +342,7 @@ export async function startSession(opts: StartSessionOpts): Promise<StartSession
342
342
  'ALREADY_RUNNING',
343
343
  `session already running for project=${project.id} provider=${provider} ` +
344
344
  `(session ${existingLive.id}, pid ${existingLive.wrapperPid}). ` +
345
- 'end it first with `rnx agent end <sessionId>`.',
345
+ 'end it first with `peach agent end <sessionId>`.',
346
346
  )
347
347
  }
348
348
 
@@ -114,7 +114,7 @@ function userDataDir(): string {
114
114
  return electronUserDataDir()
115
115
  }
116
116
 
117
- /** exposed for CLI status printing so `sootsim agent projects` can show the
117
+ /** exposed for CLI status printing so `peach agent projects` can show the
118
118
  * user which file it's reading from. */
119
119
  export function getUserDataDir(): string {
120
120
  return userDataDir()
@@ -38,7 +38,7 @@ export type SharedDesktopAuthSession = {
38
38
  validatedAt?: string
39
39
  /**
40
40
  * the account this sign-in bills cloud work to. a session token names the
41
- * person, not an account, so the CLI chooses one at `rnx login` and names it
41
+ * person, not an account, so the CLI chooses one at `peach login` and names it
42
42
  * on every request that opens a paid session.
43
43
  */
44
44
  accountId?: string
@@ -127,16 +127,16 @@ export function resolveSootsimWebSocketProxyUrl(input: {
127
127
  }): string {
128
128
  const pageUrl = parseHrefUrl(input.href)
129
129
  if (!pageUrl || (pageUrl.protocol !== 'http:' && pageUrl.protocol !== 'https:')) {
130
- throw new Error(`invalid rnx websocket proxy page URL: ${input.href}`)
130
+ throw new Error(`invalid peach websocket proxy page URL: ${input.href}`)
131
131
  }
132
132
  let targetUrl: URL
133
133
  try {
134
134
  targetUrl = new URL(input.targetUrl)
135
135
  } catch {
136
- throw new Error(`invalid rnx websocket proxy target URL: ${input.targetUrl}`)
136
+ throw new Error(`invalid peach websocket proxy target URL: ${input.targetUrl}`)
137
137
  }
138
138
  if (targetUrl.protocol !== 'ws:' && targetUrl.protocol !== 'wss:') {
139
- throw new Error(`invalid rnx websocket proxy target URL: ${input.targetUrl}`)
139
+ throw new Error(`invalid peach websocket proxy target URL: ${input.targetUrl}`)
140
140
  }
141
141
  const proxyUrl = new URL('/__websocket-proxy', pageUrl)
142
142
  proxyUrl.protocol = pageUrl.protocol === 'https:' ? 'wss:' : 'ws:'
@@ -1,6 +1,6 @@
1
1
  import { SIM_LONG_PRESS_MAX_MS } from './bridge-contract'
2
2
  // untrusted JSON -> bridge-contract argument. one owner for that conversion, so
3
- // every transport in front of a sim (the RNX Cloud sim service's HTTP and
3
+ // every transport in front of a sim (the Peach Cloud sim service's HTTP and
4
4
  // WebSocket routes) validates its request bodies here rather than growing a
5
5
  // second reader for the same shapes.
6
6
  import type {
@@ -1,4 +1,4 @@
1
- // the wire contract for the sootsim ws bridge. the engine implements it in
1
+ // the wire contract for the peach ws bridge. the engine implements it in
2
2
  // sootsim-engine/src/mcp/ws-bridge.ts; the peachsim cli client, the SimClient
3
3
  // sdk, and the flow runner speak it. it is the single owner of these shapes,
4
4
  // and neither side redeclares them.
@@ -367,7 +367,7 @@ export const SEMANTIC_READY_CONTENT_NODE_FLOOR = 100
367
367
  export const SEMANTIC_READY_NODE_STABLE_MS = 750
368
368
  export const SEMANTIC_READY_POLL_MS = 150
369
369
 
370
- export const PUBLIC_NO_NETWORK_MARKER = 'public RNX Cloud simulators have no network'
370
+ export const PUBLIC_NO_NETWORK_MARKER = 'public Peach Cloud simulators have no network'
371
371
  export const HEADLESS_TENANT_FETCH_HUNG_MARKER = 'headless tenant fetch hung'
372
372
 
373
373
  export function readyProbeHasContent(
@@ -424,7 +424,7 @@ export interface SimStateOptions {
424
424
  route?: boolean
425
425
  errors?: boolean
426
426
  // include the bounded guest console tail. omitted by the ordinary state
427
- // command so one-shot state remains compact; `rnx logs` requests it.
427
+ // command so one-shot state remains compact; `peach logs` requests it.
428
428
  logs?: boolean
429
429
  // tree depth, matching the standalone `tree` command. default 5.
430
430
  depth?: number
@@ -1,22 +1,22 @@
1
- // rnx cli version, resolved once and cached. standalone builds embed it so
1
+ // peach cli version, resolved once and cached. standalone builds embed it so
2
2
  // lazy command chunks do not depend on an npm package layout at runtime.
3
3
 
4
4
  import { readFileSync } from 'node:fs'
5
5
  import { fileURLToPath } from 'node:url'
6
6
  import { rnxPublicBrand } from './public-brand.ts'
7
7
 
8
- declare const __RNX_CLI_VERSION__: string
8
+ declare const __PEACH_CLI_VERSION__: string
9
9
 
10
10
  let cached: string | null = null
11
11
 
12
12
  export function getCliVersion(): string {
13
13
  if (cached != null) return cached
14
14
  if (
15
- typeof __RNX_CLI_VERSION__ !== 'undefined' &&
16
- typeof __RNX_CLI_VERSION__ === 'string' &&
17
- __RNX_CLI_VERSION__
15
+ typeof __PEACH_CLI_VERSION__ !== 'undefined' &&
16
+ typeof __PEACH_CLI_VERSION__ === 'string' &&
17
+ __PEACH_CLI_VERSION__
18
18
  ) {
19
- cached = __RNX_CLI_VERSION__
19
+ cached = __PEACH_CLI_VERSION__
20
20
  return cached
21
21
  }
22
22
  // prefer package resolution (works for npm/global installs); fall back to
@@ -0,0 +1,100 @@
1
+ // hosted `rnx test` selection args: the argv slice a cloud submitter
2
+ // forwards and the recorder appends verbatim to its executor invocation.
3
+ //
4
+ // selection only. the flags below choose WHICH tests run; everything
5
+ // about HOW they run (workers, timeouts, shards, devices, environment)
6
+ // stays with the recorder that owns the execution environment, and
7
+ // everything about WHERE they run (app, origin, tunnel) is consumed by
8
+ // the submitter. a flag outside this list never reaches the executor:
9
+ // the submitter warns and the server refuses, so a filter is never
10
+ // silently dropped. pure: the gpu recorder daemon imports this too, so
11
+ // no ~/ aliases, no server-only, no node builtins.
12
+ export const CLOUD_TEST_ARGS_MAX_COUNT = 32
13
+ export const CLOUD_TEST_ARG_MAX_LENGTH = 1000
14
+ export const CLOUD_TEST_ARGS_MAX_BYTES = 16 * 1024
15
+
16
+ // every flag a hosted job honors from the submitting command line. each
17
+ // takes exactly one value, as a separate token or --flag=value.
18
+ export const CLOUD_TEST_VALUE_FLAGS: readonly string[] = [
19
+ '-t',
20
+ '--testNamePattern',
21
+ '--testPathPattern',
22
+ '--include-tags',
23
+ '--exclude-tags',
24
+ '--grep',
25
+ ]
26
+
27
+ const CLOUD_TEST_VALUE_FLAG_SET = new Set<string>(CLOUD_TEST_VALUE_FLAGS)
28
+
29
+ export function isCloudTestValueFlag(name: string): boolean {
30
+ return CLOUD_TEST_VALUE_FLAG_SET.has(name)
31
+ }
32
+
33
+ export type CloudTestArgsVerdict =
34
+ | { ok: true; args: string[] }
35
+ | { ok: false; message: string }
36
+
37
+ // the submit payload's testArgs field, or null when the submission
38
+ // carries no selection. every violation names the offending arg.
39
+ export function parseCloudTestArgs(value: unknown): CloudTestArgsVerdict {
40
+ if (value === undefined || value === null) return { ok: true, args: [] }
41
+ if (!Array.isArray(value)) {
42
+ return { ok: false, message: 'testArgs must be an array of strings' }
43
+ }
44
+ if (value.length > CLOUD_TEST_ARGS_MAX_COUNT) {
45
+ return {
46
+ ok: false,
47
+ message: `testArgs may contain at most ${CLOUD_TEST_ARGS_MAX_COUNT} entries`,
48
+ }
49
+ }
50
+ // utf-8 bytes, not utf-16 code units: the bound names bytes, and a
51
+ // multibyte pattern must not slip twice its size past the gate.
52
+ const encoder = new TextEncoder()
53
+ let bytes = 0
54
+ const entries: string[] = []
55
+ for (const entry of value) {
56
+ if (typeof entry !== 'string' || entry.length === 0) {
57
+ return { ok: false, message: 'testArgs entries must be non-empty strings' }
58
+ }
59
+ if (entry.length > CLOUD_TEST_ARG_MAX_LENGTH || entry.includes('\0')) {
60
+ return { ok: false, message: `testArgs entry is too long or carries a NUL byte: ${entry.slice(0, 60)}` }
61
+ }
62
+ bytes += encoder.encode(entry).length
63
+ if (bytes > CLOUD_TEST_ARGS_MAX_BYTES) {
64
+ return {
65
+ ok: false,
66
+ message: `testArgs may contain at most ${CLOUD_TEST_ARGS_MAX_BYTES} bytes`,
67
+ }
68
+ }
69
+ entries.push(entry)
70
+ }
71
+ const args: string[] = []
72
+ for (let i = 0; i < entries.length; i++) {
73
+ const entry = entries[i]
74
+ if (!entry.startsWith('-')) {
75
+ return {
76
+ ok: false,
77
+ message: `testArgs carries a positional argument (selection flags only): ${entry.slice(0, 60)}`,
78
+ }
79
+ }
80
+ const eq = entry.indexOf('=')
81
+ const head = eq > 0 ? entry.slice(0, eq) : entry
82
+ if (!isCloudTestValueFlag(head)) {
83
+ return { ok: false, message: `testArgs carries an unknown flag: ${head.slice(0, 60)}` }
84
+ }
85
+ if (eq > 0) {
86
+ if (!entry.slice(eq + 1)) {
87
+ return { ok: false, message: `${head} expects a value` }
88
+ }
89
+ args.push(entry)
90
+ continue
91
+ }
92
+ const val = entries[i + 1]
93
+ if (val === undefined || val.startsWith('-')) {
94
+ return { ok: false, message: `${head} expects a value` }
95
+ }
96
+ args.push(entry, val)
97
+ i += 1
98
+ }
99
+ return { ok: true, args }
100
+ }
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
  }