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,8 +1,8 @@
1
- // rnx maestro — drop-in replacement for the real `maestro` cli.
1
+ // peach maestro — drop-in replacement for the real `maestro` cli.
2
2
  //
3
3
  // users with an existing `.maestro/` directory can swap `maestro test`
4
- // for `rnx maestro test` and have their flows execute against
5
- // rnx's in-browser engine. the heavy lifting lives in `flow.ts` +
4
+ // for `peach maestro test` and have their flows execute against
5
+ // peach's in-browser engine. the heavy lifting lives in `flow.ts` +
6
6
  // `bridge-flow-runner.ts`; this file just re-maps the maestro argv shape
7
7
  // onto the existing flow runner and handles directory iteration + init.
8
8
 
@@ -10,6 +10,7 @@ import * as fs from 'fs'
10
10
  import * as path from 'path'
11
11
  import yaml from 'yaml'
12
12
  import { resolveCliAuth } from '../auth'
13
+ import { cloudTestResultFileNames } from '../cloud-test-result-files'
13
14
  import { parseFlowFile, type FlowFrontmatter } from '../flow-file'
14
15
  import {
15
16
  buildFlowCommandsJson,
@@ -42,36 +43,45 @@ const MAESTRO_DIRS = ['.maestro', 'maestro']
42
43
 
43
44
  function printHelp() {
44
45
  console.log(`
45
- rnx maestro — author and run Maestro YAML flows against rnx
46
+ peach maestro — author and run Maestro YAML flows against peach
46
47
 
47
48
  usage:
48
- rnx maestro # discover .maestro/ or maestro/ in cwd, run all
49
- rnx maestro test <flow-or-dir> # mirrors "maestro test"
50
- rnx maestro load <flow> # load flow into runner without running (--dry-run)
51
- rnx maestro test .maestro/ # every *.yaml / *.yml in a directory
52
- rnx maestro generate "<goal>" # generate, run, and upload a Maestro flow
53
- rnx maestro start|keep|end # author a flow from live CLI actions
54
- rnx maestro validate <flow> # validate a Maestro YAML file
55
- rnx maestro init # scaffold .maestro/login.yaml
56
- rnx maestro --list-compat # print supported/unsupported verbs
49
+ peach maestro # discover .maestro/ or maestro/ in cwd, run all
50
+ peach maestro test <flow-or-dir> # mirrors "maestro test"
51
+ peach maestro load <flow> # load flow into runner without running (--dry-run)
52
+ peach maestro test .maestro/ # every *.yaml / *.yml in a directory
53
+ peach maestro generate "<goal>" # generate, run, and upload a Maestro flow
54
+ peach maestro start|keep|end # author a flow from live CLI actions
55
+ peach maestro validate <flow> # validate a Maestro YAML file
56
+ peach maestro init # scaffold .maestro/login.yaml
57
+ peach maestro --list-compat # print supported/unsupported verbs
57
58
 
58
59
  options:
59
60
  --dry-run, --no-run load flow into runner idle without auto-running
60
61
  --env KEY=VALUE set env vars for \${KEY} interpolation (repeatable)
61
- --continuous accepted for maestro cli compat; rnx runs the flow once
62
+ --continuous accepted for maestro cli compat; peach runs the flow once
62
63
  --include-tags <a,b> run only flows carrying one of these tags (comma-
63
64
  separated; CLI wins over config.yaml includeTags)
64
65
  --exclude-tags <a,b> skip flows carrying any of these tags
65
66
  --config <path> read the workspace config (flows/tags) from this file
66
67
  instead of <dir>/config.yaml
67
68
  --format junit write a junit xml report (default ./report.xml)
69
+ JUNIT, NOOP also accepted (case-insensitive)
68
70
  --output <file> junit report path (needs --format junit)
69
71
  --test-output-dir <dir>
70
72
  per-flow screenshots, commands.json, and logs/maestro.log
71
73
  (config key testOutputDir:; CLI wins over config)
72
74
  --debug-output <dir> write a session maestro.log there
73
- --platform ios accepted; any other platform is refused (rnx is iOS-only)
74
- --no-ansi accepted; rnx output is already plain text
75
+ --flatten-debug-output accepted; peach output is already flat
76
+ --test-suite-name <n> junit testsuite name (default "Test Suite")
77
+ --platform ios accepted; any other platform is refused (peach is iOS-only)
78
+ (-p stays the peach bridge-port short)
79
+ --no-ansi accepted; peach output is already plain text
80
+ --udid <id> warned; peach manages its own sim (pin one with --sim)
81
+ -s, --shards <n> warned; peach runs the full selection on one sim
82
+ --shard-all, --shard-split <spec>
83
+ warned; peach runs the full selection on one sim
84
+ -c warned; --continuous runs once under peach
75
85
  --device <model> override the flow frontmatter device for this run
76
86
  --new force a fresh browser window for this run
77
87
  --headed force a fresh, VISIBLE window (implies --new,
@@ -85,19 +95,19 @@ options:
85
95
  --slow <ms> delay between steps for natural pacing
86
96
 
87
97
  examples:
88
- rnx maestro test .maestro/login.yaml
89
- rnx maestro test .maestro/
90
- rnx maestro generate "log in and verify the welcome screen"
91
- rnx maestro start
92
- rnx maestro end --output .maestro/login.yaml --validate
93
- rnx maestro --env USERNAME=alice test .maestro/login.yaml
94
- rnx maestro init
98
+ peach maestro test .maestro/login.yaml
99
+ peach maestro test .maestro/
100
+ peach maestro generate "log in and verify the welcome screen"
101
+ peach maestro start
102
+ peach maestro end --output .maestro/login.yaml --validate
103
+ peach maestro --env USERNAME=alice test .maestro/login.yaml
104
+ peach maestro init
95
105
  `)
96
106
  }
97
107
 
98
108
  function printCompatMatrix() {
99
109
  console.log(`
100
- rnx maestro — compatibility matrix
110
+ peach maestro — compatibility matrix
101
111
 
102
112
  supported verbs:
103
113
  launchApp (including arguments), stopApp, clearState, clearKeychain (warn-only),
@@ -125,7 +135,7 @@ cli flags (maestro test parity):
125
135
  CLI wins over config.yaml), --config <path>, --format junit with
126
136
  --output (default report.xml; flow name:/properties: shape the
127
137
  testcase), --test-output-dir (config key testOutputDir:, CLI wins),
128
- --debug-output, --platform ios (others refused), --no-ansi (rnx
138
+ --debug-output, --platform ios (others refused), --no-ansi (peach
129
139
  output is already plain), -d for --device, -e for --env.
130
140
  --continuous warns and runs once; --format html warns and runs
131
141
  without a report.
@@ -137,14 +147,14 @@ launch behavior:
137
147
  openLink: replaces the guest, then delivers the URL to the new tenant.
138
148
 
139
149
  partial:
140
- clearKeychain — warn-only, rnx has no keychain surface.
141
- when.platform — rnx reports iOS for this runner.
150
+ clearKeychain — warn-only, peach has no keychain surface.
151
+ when.platform — peach reports iOS for this runner.
142
152
  openLink: app routing works; no OS-level Safari fallback.
143
- launchApp.resetRuntime — rnx-only extension for recorded benchmarks:
153
+ launchApp.resetRuntime — peach-only extension for recorded benchmarks:
144
154
  keep tenant storage/auth, but soft-reload the guest app inside launchApp.
145
- takeScreenshot — maestro string form works unchanged; rnx also accepts
155
+ takeScreenshot — maestro string form works unchanged; peach also accepts
146
156
  { path, withFrame } for framed export.
147
- env — rnx seeds the JS env from the full shell environment, not just
157
+ env — peach seeds the JS env from the full shell environment, not just
148
158
  MAESTRO_* (so \${SOOTSIM_*} credentials resolve); a bare \${NAME} that is
149
159
  undefined fails the step loudly instead of typing "undefined".
150
160
 
@@ -162,11 +172,13 @@ function looksLikeFlag(s: string): boolean {
162
172
  // extract --env KEY=VALUE pairs from argv and apply to process.env for the
163
173
  // duration of the run. returns argv with the consumed flags removed so the
164
174
  // rest can be forwarded to runFlowPlayback verbatim.
165
- function applyEnvFlags(args: string[]): string[] {
175
+ export function applyEnvFlags(args: string[]): string[] {
166
176
  const out: string[] = []
167
177
  for (let i = 0; i < args.length; i++) {
168
- if (args[i] === '--env' || args[i] === '-e') {
169
- const kv = args[i + 1]
178
+ const arg = args[i] as string
179
+ const inline = arg.startsWith('--env=') ? arg.slice('--env='.length) : null
180
+ if (arg === '--env' || arg === '-e' || inline !== null) {
181
+ const kv = inline ?? args[i + 1]
170
182
  if (!kv || !kv.includes('=')) {
171
183
  throw new Error(`--env expects KEY=VALUE (got ${JSON.stringify(kv ?? '')})`)
172
184
  }
@@ -174,16 +186,16 @@ function applyEnvFlags(args: string[]): string[] {
174
186
  const key = kv.slice(0, eq)
175
187
  const value = kv.slice(eq + 1)
176
188
  process.env[key] = value
177
- i += 1
189
+ if (inline === null) i += 1
178
190
  continue
179
191
  }
180
- out.push(args[i])
192
+ out.push(arg)
181
193
  }
182
194
  return out
183
195
  }
184
196
 
185
197
  // maestro-cli flags this command consumes itself, accepted so an existing
186
- // script can swap `maestro test` for `rnx maestro test` unedited. each one
198
+ // script can swap `maestro test` for `peach maestro test` unedited. each one
187
199
  // either takes effect below or says out loud that it does not: an option
188
200
  // that is quietly dropped is how a run reports success while doing something
189
201
  // other than what was asked. `--origin` is consumed here because only this
@@ -204,6 +216,7 @@ export interface MaestroCompatOptions {
204
216
  testOutputDir: string | null
205
217
  debugOutput: string | null
206
218
  platform: string | null
219
+ testSuiteName: string | null
207
220
  }
208
221
 
209
222
  const MAESTRO_COMPAT_VALUE_FLAGS = new Set([
@@ -215,6 +228,14 @@ const MAESTRO_COMPAT_VALUE_FLAGS = new Set([
215
228
  '--test-output-dir',
216
229
  '--debug-output',
217
230
  '--platform',
231
+ '--udid',
232
+ '--test-suite-name',
233
+ '--shards',
234
+ '--shard-all',
235
+ '--shard-split',
236
+ '--api-key',
237
+ '--api-url',
238
+ '--screen-size',
218
239
  ])
219
240
 
220
241
  function splitTagList(value: string): string[] {
@@ -237,6 +258,7 @@ export function extractMaestroCompatFlags(args: string[]): {
237
258
  testOutputDir: null,
238
259
  debugOutput: null,
239
260
  platform: null,
261
+ testSuiteName: null,
240
262
  }
241
263
  const rest: string[] = []
242
264
  for (let i = 0; i < args.length; i++) {
@@ -267,7 +289,8 @@ export function extractMaestroCompatFlags(args: string[]): {
267
289
  compat.configPath = value
268
290
  break
269
291
  case '--format':
270
- compat.format = value
292
+ // maestro spells it JUNIT; normalize so either case works.
293
+ compat.format = value.toLowerCase()
271
294
  break
272
295
  case '--output':
273
296
  compat.output = value
@@ -280,10 +303,32 @@ export function extractMaestroCompatFlags(args: string[]): {
280
303
  break
281
304
  case '--platform':
282
305
  if (value.toLowerCase() !== 'ios') {
283
- throw new Error(`rnx emulates iOS only; --platform ${value} is not supported`)
306
+ throw new Error(`peach emulates iOS only; --platform ${value} is not supported`)
284
307
  }
285
308
  compat.platform = value
286
309
  break
310
+ case '--udid':
311
+ console.warn(
312
+ ' warn: --udid selects a Maestro simulator; peach manages its own sim — pin one with --sim instead',
313
+ )
314
+ break
315
+ case '--test-suite-name':
316
+ compat.testSuiteName = value
317
+ break
318
+ case '--shards':
319
+ case '--shard-all':
320
+ case '--shard-split':
321
+ console.warn(
322
+ ` warn: ${head} shards across Maestro devices; peach runs the full selection on one sim`,
323
+ )
324
+ break
325
+ case '--api-key':
326
+ case '--api-url':
327
+ console.warn(` warn: ${head} is Maestro beta AI analysis; peach ignores it`)
328
+ break
329
+ case '--screen-size':
330
+ console.warn(' warn: --screen-size is web-only; peach ignores it')
331
+ break
287
332
  }
288
333
  continue
289
334
  }
@@ -294,26 +339,66 @@ export function extractMaestroCompatFlags(args: string[]): {
294
339
  continue
295
340
  }
296
341
  if (a === '-d') {
297
- // maestro's short form for --device. rnx's device flag names a device
342
+ // maestro's short form for --device. peach's device flag names a device
298
343
  // model, and the flow runner reads it under the long spelling only.
299
344
  rest.push('--device')
300
345
  continue
301
346
  }
302
- if (a === '--no-ansi') {
303
- // trivially satisfied: rnx prints no ANSI escapes anywhere on this path.
347
+ if (a === '--no-ansi' || a === '--no-color') {
348
+ // trivially satisfied: peach prints no ANSI escapes anywhere on this path.
349
+ continue
350
+ }
351
+ if (a === '--ansi' || a === '--color') {
352
+ console.warn(' warn: peach prints no ANSI escapes; colored output is unavailable')
353
+ continue
354
+ }
355
+ if (a === '--continuous' || a === '-c') {
356
+ console.warn(' warn: --continuous is not implemented in peach — running once')
357
+ continue
358
+ }
359
+ if (a === '--flatten-debug-output') {
360
+ // peach writes one <dir>/maestro.log with no per-run subfolders or
361
+ // timestamps, so the maestro layout flag is already satisfied.
362
+ continue
363
+ }
364
+ if (a === '--analyze') {
365
+ console.warn(' warn: --analyze is Maestro beta AI analysis; peach ignores it')
366
+ continue
367
+ }
368
+ if (a === '-s') {
369
+ const count = args[i + 1]
370
+ if (!count || count.startsWith('-')) throw new Error('-s expects a value')
371
+ i += 1
372
+ console.warn(
373
+ ' warn: -s shards across Maestro devices; peach runs the full selection on one sim',
374
+ )
304
375
  continue
305
376
  }
306
- if (a === '--continuous') {
307
- console.warn(' warn: --continuous is not implemented in rnx — running once')
377
+ if (head === '-p') {
378
+ // -p is the peach bridge-port short, not maestro's --platform short. a
379
+ // non-numeric value is a pasted maestro command line; fail loud with
380
+ // the mapping instead of pinning the bridge to a NaN port.
381
+ const value = eq > 0 ? a.slice(eq + 1) : args[i + 1]
382
+ if (value !== undefined && !value.startsWith('-') && !/^\d+$/.test(value)) {
383
+ throw new Error(
384
+ `-p is the peach bridge-port short; use --platform for the Maestro platform (got ${JSON.stringify(value)})`,
385
+ )
386
+ }
387
+ rest.push(a)
308
388
  continue
309
389
  }
310
390
  rest.push(a)
311
391
  }
312
392
  if (
313
393
  compat.format !== null &&
314
- !['junit', 'html', 'html-detailed'].includes(compat.format)
394
+ !['junit', 'html', 'html-detailed', 'noop'].includes(compat.format)
315
395
  ) {
316
- throw new Error(`unsupported --format: ${compat.format} (rnx writes junit reports)`)
396
+ throw new Error(`unsupported --format: ${compat.format} (peach writes junit reports)`)
397
+ }
398
+ if (compat.format === 'html' || compat.format === 'html-detailed') {
399
+ console.warn(
400
+ ' warn: --format html is not implemented in peach — running without a report',
401
+ )
317
402
  }
318
403
  return { rest, compat }
319
404
  }
@@ -475,8 +560,8 @@ async function runInit(cwd: string): Promise<number> {
475
560
  console.error(` error: ${outPath} already exists`)
476
561
  return 1
477
562
  }
478
- const starter = `# rnx maestro starter flow — drop-in compatible with the maestro cli.
479
- # run: rnx maestro test .maestro/login.yaml
563
+ const starter = `# peach maestro starter flow — drop-in compatible with the maestro cli.
564
+ # run: peach maestro test .maestro/login.yaml
480
565
  appId: com.example.app
481
566
  ---
482
567
  - launchApp
@@ -491,7 +576,7 @@ appId: com.example.app
491
576
  `
492
577
  fs.writeFileSync(outPath, starter, 'utf8')
493
578
  console.log(` + wrote ${outPath}`)
494
- console.log(` next: rnx maestro test .maestro/login.yaml`)
579
+ console.log(` next: peach maestro test .maestro/login.yaml`)
495
580
  return 0
496
581
  }
497
582
 
@@ -566,7 +651,7 @@ export async function runMaestro(
566
651
  }
567
652
  if (compat.format === 'html' || compat.format === 'html-detailed') {
568
653
  console.warn(
569
- ` warn: --format ${compat.format} reports are not implemented in rnx — running without writing a report`,
654
+ ` warn: --format ${compat.format} reports are not implemented in peach — running without writing a report`,
570
655
  )
571
656
  }
572
657
  if (compat.output && compat.format !== 'junit') {
@@ -578,7 +663,7 @@ export async function runMaestro(
578
663
  let forwardArgs: string[]
579
664
 
580
665
  if (sub === 'test') {
581
- // `rnx maestro test <flow-or-dir> [flags...]`. the flow path is the first
666
+ // `peach maestro test <flow-or-dir> [flags...]`. the flow path is the first
582
667
  // token that is neither a flag nor the value of one — without the second
583
668
  // half, `test --sim a2 login.yaml` takes `a2` as the flow file. remove it
584
669
  // by index, since a path can equal a flag value elsewhere in the argv.
@@ -592,7 +677,7 @@ export async function runMaestro(
592
677
  forwardArgs = rest.filter((_, i) => i !== positionalIndex)
593
678
  } else if (compat.configPath) {
594
679
  // no positional with an explicit workspace config: run the directory
595
- // the config lives in, like `rnx test --config` does.
680
+ // the config lives in, like `peach test --config` does.
596
681
  target = path.resolve(cwd, path.dirname(compat.configPath))
597
682
  forwardArgs = rest
598
683
  } else {
@@ -600,7 +685,7 @@ export async function runMaestro(
600
685
  forwardArgs = rest
601
686
  }
602
687
  } else if (sub && !looksLikeFlag(sub)) {
603
- // `rnx maestro <flow-or-dir>` — tolerate the shorthand.
688
+ // `peach maestro <flow-or-dir>` — tolerate the shorthand.
604
689
  target = path.resolve(cwd, sub)
605
690
  forwardArgs = remaining.slice(1)
606
691
  } else {
@@ -618,7 +703,7 @@ export async function runMaestro(
618
703
  for (const problem of unexpected) {
619
704
  console.error(` error: ${problem.message}`)
620
705
  }
621
- console.error('\n run `rnx maestro --help` for the flags this command accepts')
706
+ console.error('\n run `peach maestro --help` for the flags this command accepts')
622
707
  return 1
623
708
  }
624
709
 
@@ -626,8 +711,8 @@ export async function runMaestro(
626
711
  console.error(`
627
712
  error: no maestro flows found.
628
713
  expected one of: ${MAESTRO_DIRS.map((d) => `./${d}/`).join(', ')}
629
- or pass a flow explicitly: rnx maestro test path/to/flow.yaml
630
- or scaffold a starter flow: rnx maestro init
714
+ or pass a flow explicitly: peach maestro test path/to/flow.yaml
715
+ or scaffold a starter flow: peach maestro init
631
716
  `)
632
717
  return 1
633
718
  }
@@ -666,7 +751,7 @@ export async function runMaestro(
666
751
  }
667
752
 
668
753
  // maestro's priority rule: CLI flag, then config.yaml, then the default
669
- // (which for rnx is no test-output routing at all). a config-relative dir
754
+ // (which for peach is no test-output routing at all). a config-relative dir
670
755
  // resolves against the config file, a CLI dir against the invocation cwd.
671
756
  const testOutputRoot = compat.testOutputDir
672
757
  ? path.resolve(cwd, compat.testOutputDir)
@@ -675,7 +760,7 @@ export async function runMaestro(
675
760
  : null
676
761
 
677
762
  console.log(`
678
- rnx maestro — ${files.length} flow${files.length === 1 ? '' : 's'}
763
+ peach maestro — ${files.length} flow${files.length === 1 ? '' : 's'}
679
764
  root: ${path.relative(cwd, target) || '.'}
680
765
  `)
681
766
 
@@ -705,7 +790,7 @@ export async function runMaestro(
705
790
  steps: ReturnType<typeof getLastFlowTraceSteps>
706
791
  frontmatter: FlowFrontmatter
707
792
  }> = []
708
- for (const file of files) {
793
+ for (const [flowIndex, file] of files.entries()) {
709
794
  console.log(`\n ▶ ${path.relative(cwd, file)}`)
710
795
  const playbackArgs = [file, ...forwardArgs]
711
796
  let flowTestDir: string | null = null
@@ -730,7 +815,7 @@ export async function runMaestro(
730
815
  // one. sending it on as `--url` made it an app target instead, which left
731
816
  // the playback bridge on whichever world resolveBridgeWorld picks by
732
817
  // default (a live Vite dev shell selected from the development registry).
733
- // so `rnx --port <installed daemon> maestro …` silently ran the flow
818
+ // so `peach --port <installed daemon> maestro …` silently ran the flow
734
819
  // against the dev engine, then failed trying to resolve a bundle at the
735
820
  // bridge port. an explicit command-level `--port` still wins, since
736
821
  // parseBridgeCliArgs takes the last occurrence.
@@ -789,17 +874,20 @@ export async function runMaestro(
789
874
  }
790
875
 
791
876
  const upload = getLastFlowPreviewUploadResult()
792
- if (upload?.shareId || upload?.previewUrl) {
793
- try {
794
- fs.writeFileSync(
795
- path.join(cwd, 'preview-result.json'),
796
- JSON.stringify({
797
- previewId: upload.shareId,
798
- previewUrl: upload.previewUrl,
799
- }),
800
- )
801
- } catch {}
802
- } else if (args.includes('--preview')) {
877
+ // the receipt files are the per-flow manifest: always written, with
878
+ // null ids when the upload failed, so the flow never disappears from
879
+ // the collected entries.
880
+ const previewPayload = {
881
+ flow: path.relative(cwd, file) || file,
882
+ previewId: upload?.shareId ?? null,
883
+ previewUrl: upload?.previewUrl ?? null,
884
+ }
885
+ try {
886
+ for (const name of cloudTestResultFileNames('preview', flowIndex, files.length)) {
887
+ fs.writeFileSync(path.join(cwd, name), JSON.stringify(previewPayload))
888
+ }
889
+ } catch {}
890
+ if (!upload?.shareId && !upload?.previewUrl && args.includes('--preview')) {
803
891
  console.error(' error: required hosted recording upload failed')
804
892
  if (exit === 0) exit = 1
805
893
  if (worstExit === 0) worstExit = 1
@@ -832,11 +920,24 @@ export async function runMaestro(
832
920
  }),
833
921
  )
834
922
  } catch {}
835
- } else if (process.env.RNX_REQUIRE_REGISTRATION === 'true') {
923
+ } else if (process.env.PEACH_REQUIRE_REGISTRATION === 'true') {
836
924
  console.error(' error: required hosted run registration failed')
837
925
  if (exit === 0) exit = 1
838
926
  if (worstExit === 0) worstExit = 1
839
927
  }
928
+ // like the preview receipt above: always written, null ids when the
929
+ // registration failed, so the flow keeps its collected entry.
930
+ const testPayload = {
931
+ flow: path.relative(cwd, file) || file,
932
+ runId: run?.id ?? null,
933
+ previewUrl: run?.previewUrl ?? null,
934
+ traceUrl: run?.traceUrl ?? null,
935
+ }
936
+ try {
937
+ for (const name of cloudTestResultFileNames('test', flowIndex, files.length)) {
938
+ fs.writeFileSync(path.join(cwd, name), JSON.stringify(testPayload))
939
+ }
940
+ } catch {}
840
941
  }
841
942
  // artifact, upload, and registration failures above flip the local exit
842
943
  // after the result was pushed; the report below must see the final one.
@@ -865,6 +966,7 @@ export async function runMaestro(
865
966
  buildJUnitXml({
866
967
  device: deviceLabel,
867
968
  cases: results.map((r) => junitCaseForResult(cwd, r)),
969
+ ...(compat.testSuiteName ? { name: compat.testSuiteName } : {}),
868
970
  }),
869
971
  )
870
972
  console.log(` junit: ${path.relative(cwd, junitPath) || junitPath}`)
@@ -28,12 +28,12 @@ function withoutSubcommand(args: string[], index: number): string[] {
28
28
 
29
29
  function printHelp(): void {
30
30
  console.log(`
31
- rnx mode — toggle live display modes
31
+ peach mode — toggle live display modes
32
32
 
33
33
  usage:
34
- rnx mode screenshot [on|off|toggle]
35
- rnx mode three [on|off|toggle]
36
- rnx mode three configure [options]
34
+ peach mode screenshot [on|off|toggle]
35
+ peach mode three [on|off|toggle]
36
+ peach mode three configure [options]
37
37
  `)
38
38
  }
39
39
 
@@ -4,39 +4,39 @@ import { findDesktopCompanion } from '../desktop-companion'
4
4
  import type { BridgeSimInfo } from '../ws-bridge'
5
5
 
6
6
  export function printMissingCompanionHint() {
7
- console.error(' no rnx desktop companion found.')
7
+ console.error(' no peach desktop companion found.')
8
8
  console.error('')
9
9
  console.error(' install the desktop app:')
10
- console.error(' rnx desktop install')
10
+ console.error(' peach desktop install')
11
11
  console.error('')
12
12
  console.error(' or install the recommended background service:')
13
- console.error(' rnx daemon install')
13
+ console.error(' peach daemon install')
14
14
  console.error('')
15
15
  }
16
16
 
17
17
  export function printMissingBridgeHint(port: number) {
18
18
  const companion = findDesktopCompanion()
19
- console.log(` note: no rnx bridge detected on port ${port}`)
19
+ console.log(` note: no peach bridge detected on port ${port}`)
20
20
  if (companion) {
21
21
  console.log(` launch the installed companion with:`)
22
- console.log(` rnx desktop`)
22
+ console.log(` peach desktop`)
23
23
  return
24
24
  }
25
25
  console.log('')
26
- console.log(' run `rnx daemon install` to enable the faster background service,')
27
- console.log(' or run `rnx serve` in another shell for a foreground bridge.')
26
+ console.log(' run `peach daemon install` to enable the faster background service,')
27
+ console.log(' or run `peach serve` in another shell for a foreground bridge.')
28
28
  }
29
29
 
30
30
  export function printMissingSimHint(port: number) {
31
31
  console.error('')
32
- console.error(` no sim is connected to the rnx bridge on port ${port}.`)
32
+ console.error(` no sim is connected to the peach bridge on port ${port}.`)
33
33
  console.error('')
34
- console.error(' start your app dev server, then open it in rnx:')
34
+ console.error(' start your app dev server, then open it in peach:')
35
35
  console.error(' npx expo start --localhost --port 8081')
36
- console.error(' rnx open 8081')
36
+ console.error(' peach open 8081')
37
37
  console.error('')
38
38
  console.error(' then inspect the live app:')
39
- console.error(' rnx describe')
39
+ console.error(' peach describe')
40
40
  }
41
41
 
42
42
  // printed when a command targeted a specific `--sim <id>` that the bridge
@@ -76,5 +76,5 @@ export async function printUnknownSimHint(
76
76
  console.error(` ${sim.id} (${flags})`)
77
77
  }
78
78
  console.error('')
79
- console.error(' pass a valid --sim id, or run `rnx list` to see all sessions.')
79
+ console.error(' pass a valid --sim id, or run `peach list` to see all sessions.')
80
80
  }
@@ -28,22 +28,22 @@ function withoutSubcommand(args: string[], index: number): string[] {
28
28
 
29
29
  function printHelp(): void {
30
30
  console.log(`
31
- rnx perf — performance profiling tools
31
+ peach perf — performance profiling tools
32
32
 
33
33
  usage:
34
- rnx perf cpu [options]
35
- rnx perf react <subcommand> [options]
36
- rnx perf shell <start|stop|transition> [options]
37
- rnx perf scroll <start|stop> [options]
34
+ peach perf cpu [options]
35
+ peach perf react <subcommand> [options]
36
+ peach perf shell <start|stop|transition> [options]
37
+ peach perf scroll <start|stop> [options]
38
38
 
39
39
  examples:
40
- rnx perf cpu --duration 5 --output /tmp/trace.cpuprofile
40
+ peach perf cpu --duration 5 --output /tmp/trace.cpuprofile
41
41
  # writes page + every worker profile and a role-labelled manifest
42
- rnx perf react summary --limit 10
43
- rnx perf shell start
44
- rnx perf shell stop --json
45
- rnx perf scroll start
46
- rnx perf scroll stop --json
42
+ peach perf react summary --limit 10
43
+ peach perf shell start
44
+ peach perf shell stop --json
45
+ peach perf scroll start
46
+ peach perf scroll stop --json
47
47
  `)
48
48
  }
49
49
 
@@ -51,13 +51,13 @@ const KINDS: readonly SystemPermissionKind[] = [
51
51
 
52
52
  function printHelp(): void {
53
53
  console.log(`
54
- rnx permissions — inspect and change simulated operating-system permissions
54
+ peach permissions — inspect and change simulated operating-system permissions
55
55
 
56
56
  usage:
57
- rnx permissions list [--json]
58
- rnx permissions get <app-id> [--json]
59
- rnx permissions set <app-id> <kind> <status> [--once] [--accuracy full|reduced] [--json]
60
- rnx permissions reset <app-id> [kind] [--json]
57
+ peach permissions list [--json]
58
+ peach permissions get <app-id> [--json]
59
+ peach permissions set <app-id> <kind> <status> [--once] [--accuracy full|reduced] [--json]
60
+ peach permissions reset <app-id> [kind] [--json]
61
61
  `)
62
62
  }
63
63