peachsim 0.1.527

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 (324) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +640 -0
  3. package/cli/app-config.ts +82 -0
  4. package/cli/app-fonts.ts +388 -0
  5. package/cli/app-project.ts +59 -0
  6. package/cli/app-splash.ts +171 -0
  7. package/cli/app-state-reset.ts +26 -0
  8. package/cli/auth.ts +184 -0
  9. package/cli/bin.ts +59 -0
  10. package/cli/bridge-diagnostics.ts +226 -0
  11. package/cli/bridge-flow-runner.ts +2806 -0
  12. package/cli/browser-backend.ts +44 -0
  13. package/cli/browser-evals.ts +96 -0
  14. package/cli/cli-update.ts +261 -0
  15. package/cli/cloud-client.ts +1530 -0
  16. package/cli/cloud-dispatch.ts +206 -0
  17. package/cli/cloud-session.ts +227 -0
  18. package/cli/command-contract.ts +221 -0
  19. package/cli/command-registry.ts +304 -0
  20. package/cli/commands/agent-wrapper.ts +1048 -0
  21. package/cli/commands/agent.ts +439 -0
  22. package/cli/commands/app-fonts.ts +99 -0
  23. package/cli/commands/assert.ts +542 -0
  24. package/cli/commands/auth.ts +71 -0
  25. package/cli/commands/box/bundle-plane.ts +71 -0
  26. package/cli/commands/box/checkout-plane.ts +256 -0
  27. package/cli/commands/box/plane-share.ts +208 -0
  28. package/cli/commands/box/rnx-command.ts +60 -0
  29. package/cli/commands/box/search-command.ts +157 -0
  30. package/cli/commands/box.ts +925 -0
  31. package/cli/commands/camera.ts +266 -0
  32. package/cli/commands/cleanup.ts +169 -0
  33. package/cli/commands/compat.ts +176 -0
  34. package/cli/commands/config.ts +34 -0
  35. package/cli/commands/control.ts +2154 -0
  36. package/cli/commands/cpu-profile.ts +941 -0
  37. package/cli/commands/daemon-mac-app.ts +165 -0
  38. package/cli/commands/daemon.ts +800 -0
  39. package/cli/commands/debug.ts +751 -0
  40. package/cli/commands/desktop.ts +39 -0
  41. package/cli/commands/detect.ts +201 -0
  42. package/cli/commands/detox.ts +481 -0
  43. package/cli/commands/device.ts +134 -0
  44. package/cli/commands/diagnose.ts +589 -0
  45. package/cli/commands/do-chain.ts +408 -0
  46. package/cli/commands/electron.ts +94 -0
  47. package/cli/commands/film.ts +379 -0
  48. package/cli/commands/flow.ts +1240 -0
  49. package/cli/commands/inspect/actions.ts +516 -0
  50. package/cli/commands/inspect/core.ts +2086 -0
  51. package/cli/commands/inspect/count.ts +17 -0
  52. package/cli/commands/inspect/describe.ts +119 -0
  53. package/cli/commands/inspect/env.ts +25 -0
  54. package/cli/commands/inspect/find.ts +169 -0
  55. package/cli/commands/inspect/get-layout.ts +53 -0
  56. package/cli/commands/inspect/keyboard.ts +54 -0
  57. package/cli/commands/inspect/list.ts +175 -0
  58. package/cli/commands/inspect/memory.ts +215 -0
  59. package/cli/commands/inspect/redaction.ts +39 -0
  60. package/cli/commands/inspect/resolve-target.ts +77 -0
  61. package/cli/commands/inspect/screens.ts +78 -0
  62. package/cli/commands/inspect/settle.ts +32 -0
  63. package/cli/commands/inspect/settling.ts +197 -0
  64. package/cli/commands/inspect/shared.ts +259 -0
  65. package/cli/commands/inspect/sleep.ts +15 -0
  66. package/cli/commands/inspect/tree.ts +23 -0
  67. package/cli/commands/inspect/url.ts +17 -0
  68. package/cli/commands/inspect/wait-event.ts +211 -0
  69. package/cli/commands/inspect/wait-idle.ts +25 -0
  70. package/cli/commands/inspect/wait-ready.ts +87 -0
  71. package/cli/commands/inspect/wait-selector.ts +59 -0
  72. package/cli/commands/inspect/wait.ts +32 -0
  73. package/cli/commands/inspect.ts +4738 -0
  74. package/cli/commands/install-cli.ts +275 -0
  75. package/cli/commands/install-desktop.ts +385 -0
  76. package/cli/commands/login.ts +357 -0
  77. package/cli/commands/logout.ts +24 -0
  78. package/cli/commands/maestro-generate.ts +375 -0
  79. package/cli/commands/maestro.ts +536 -0
  80. package/cli/commands/mode.ts +58 -0
  81. package/cli/commands/no-bridge-hint.ts +80 -0
  82. package/cli/commands/perf.ts +67 -0
  83. package/cli/commands/permissions.ts +203 -0
  84. package/cli/commands/platform.ts +410 -0
  85. package/cli/commands/preview.ts +103 -0
  86. package/cli/commands/profile.ts +106 -0
  87. package/cli/commands/react.ts +353 -0
  88. package/cli/commands/record.ts +1435 -0
  89. package/cli/commands/report-issue.ts +305 -0
  90. package/cli/commands/reset.ts +89 -0
  91. package/cli/commands/runtime.ts +348 -0
  92. package/cli/commands/screenshot-command.ts +106 -0
  93. package/cli/commands/screenshot-layers.ts +144 -0
  94. package/cli/commands/screenshot-mode.ts +38 -0
  95. package/cli/commands/screenshot.ts +492 -0
  96. package/cli/commands/screenshots-capture.ts +608 -0
  97. package/cli/commands/screenshots.ts +127 -0
  98. package/cli/commands/serve.ts +139 -0
  99. package/cli/commands/setup.ts +416 -0
  100. package/cli/commands/shell-boolean-mode.ts +82 -0
  101. package/cli/commands/skills.ts +468 -0
  102. package/cli/commands/slides.ts +357 -0
  103. package/cli/commands/state.ts +94 -0
  104. package/cli/commands/storage.ts +58 -0
  105. package/cli/commands/telemetry.ts +55 -0
  106. package/cli/commands/test.ts +535 -0
  107. package/cli/commands/three-mode.ts +771 -0
  108. package/cli/commands/timeline.ts +121 -0
  109. package/cli/commands/upgrade.ts +167 -0
  110. package/cli/commands/upload.ts +1203 -0
  111. package/cli/commands/version.ts +63 -0
  112. package/cli/commands/welcome.ts +17 -0
  113. package/cli/commands/what-happened.ts +328 -0
  114. package/cli/current-sim.ts +217 -0
  115. package/cli/desktop-companion.ts +315 -0
  116. package/cli/drivers/electron.ts +70 -0
  117. package/cli/drivers/index.ts +20 -0
  118. package/cli/drivers/playwright-provisioning.ts +184 -0
  119. package/cli/drivers/playwright-sim-host.ts +537 -0
  120. package/cli/drivers/playwright.ts +209 -0
  121. package/cli/drivers/registry.ts +65 -0
  122. package/cli/drivers/types.ts +102 -0
  123. package/cli/flow-export.ts +55 -0
  124. package/cli/flow-file.ts +142 -0
  125. package/cli/flow-live-status.ts +126 -0
  126. package/cli/flow-session.ts +140 -0
  127. package/cli/help-core.ts +143 -0
  128. package/cli/help.ts +44 -0
  129. package/cli/hidden-runtime-alias.ts +19 -0
  130. package/cli/hints.ts +214 -0
  131. package/cli/inspect-notice-state.ts +114 -0
  132. package/cli/internal-child.ts +176 -0
  133. package/cli/maestro-js.ts +323 -0
  134. package/cli/main.ts +704 -0
  135. package/cli/open-url.ts +8 -0
  136. package/cli/outbound-endpoints.ts +830 -0
  137. package/cli/parent-pid.ts +204 -0
  138. package/cli/parse-args.ts +189 -0
  139. package/cli/privacy.ts +53 -0
  140. package/cli/prompt.ts +56 -0
  141. package/cli/recording-access.ts +108 -0
  142. package/cli/registry.ts +2 -0
  143. package/cli/resolve-assets.ts +63 -0
  144. package/cli/rnx-in-process.ts +98 -0
  145. package/cli/run-registry.ts +226 -0
  146. package/cli/run-rnx-core.ts +148 -0
  147. package/cli/run-rnx.ts +137 -0
  148. package/cli/runtime-notes.ts +66 -0
  149. package/cli/runtime-summary.ts +25 -0
  150. package/cli/secure-file.ts +31 -0
  151. package/cli/self-invocation.ts +58 -0
  152. package/cli/send-to-box.ts +109 -0
  153. package/cli/setup-repository.ts +191 -0
  154. package/cli/shell-init.ts +98 -0
  155. package/cli/standalone.ts +4 -0
  156. package/cli/telemetry.ts +84 -0
  157. package/cli/ui.ts +68 -0
  158. package/cli/ws-bridge.ts +1020 -0
  159. package/detox/attempt-events.cjs +23 -0
  160. package/detox/colors.ts +54 -0
  161. package/detox/config-loader.ts +135 -0
  162. package/detox/element-types.ts +48 -0
  163. package/detox/expectations.ts +534 -0
  164. package/detox/gestures.ts +501 -0
  165. package/detox/index.ts +2694 -0
  166. package/detox/jest-preset.cjs +29 -0
  167. package/detox/jest-setup-after-env.cjs +22 -0
  168. package/detox/matchers.ts +30 -0
  169. package/detox/motion-change.cjs +109 -0
  170. package/detox/motion-change.d.cts +35 -0
  171. package/detox/navigation-lifecycle.ts +290 -0
  172. package/detox/navigation.ts +43 -0
  173. package/detox/proof-frame.cjs +114 -0
  174. package/detox/record-block-execution.cjs +78 -0
  175. package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
  176. package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
  177. package/detox/screenshots/color-test-rest-norngh.png +0 -0
  178. package/detox/screenshots/theme-blue-toggle.png +0 -0
  179. package/detox/screenshots/theme-blue.png +0 -0
  180. package/detox/screenshots/theme-red-toggle.png +0 -0
  181. package/detox/screenshots/theme-red.png +0 -0
  182. package/dist-lib/agent-daemon-client.cjs +502 -0
  183. package/dist-lib/agent-events.cjs +48 -0
  184. package/dist-lib/agent-identity.cjs +53 -0
  185. package/dist-lib/agent-sessions.cjs +759 -0
  186. package/dist-lib/attached-projects.cjs +526 -0
  187. package/dist-lib/auth/shared-session.cjs +208 -0
  188. package/dist-lib/backend-origin.cjs +88 -0
  189. package/dist-lib/beta.cjs +41 -0
  190. package/dist-lib/beta.mjs +13 -0
  191. package/dist-lib/bridge-constants.cjs +161 -0
  192. package/dist-lib/bridge-contract-input.cjs +451 -0
  193. package/dist-lib/bridge-contract-input.mjs +414 -0
  194. package/dist-lib/bridge-contract.cjs +130 -0
  195. package/dist-lib/bridge-contract.mjs +94 -0
  196. package/dist-lib/capture-contract.cjs +220 -0
  197. package/dist-lib/capture-contract.mjs +190 -0
  198. package/dist-lib/cli-constants.cjs +32 -0
  199. package/dist-lib/cloud-contract.cjs +194 -0
  200. package/dist-lib/cloud-contract.mjs +145 -0
  201. package/dist-lib/cloud.cjs +670 -0
  202. package/dist-lib/cloud.mjs +639 -0
  203. package/dist-lib/config.cjs +96 -0
  204. package/dist-lib/detox/index.cjs +3789 -0
  205. package/dist-lib/detox/jest-preset.cjs +29 -0
  206. package/dist-lib/dev-bundle-resolution.cjs +290 -0
  207. package/dist-lib/home-paths.cjs +638 -0
  208. package/dist-lib/host/bridge-host.cjs +10903 -0
  209. package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
  210. package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
  211. package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
  212. package/dist-lib/host/replacement-module-handler.cjs +469 -0
  213. package/dist-lib/host/websocket-proxy.cjs +298 -0
  214. package/dist-lib/index.cjs +4268 -0
  215. package/dist-lib/jump-to-source-babel.cjs +93 -0
  216. package/dist-lib/jump-to-source-native.cjs +112 -0
  217. package/dist-lib/menu.cjs +646 -0
  218. package/dist-lib/menu.mjs +604 -0
  219. package/dist-lib/metro-fingerprint-registry.cjs +186 -0
  220. package/dist-lib/metro-fingerprint-registry.mjs +156 -0
  221. package/dist-lib/metro-production-bundle.cjs +329 -0
  222. package/dist-lib/metro-production-bundle.mjs +295 -0
  223. package/dist-lib/metro.cjs +904 -0
  224. package/dist-lib/profiles.cjs +246 -0
  225. package/dist-lib/public-brand.cjs +39 -0
  226. package/dist-lib/react-native-host-modules.cjs +51 -0
  227. package/dist-lib/react-native-host-modules.mjs +25 -0
  228. package/dist-lib/render-mode.cjs +54 -0
  229. package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
  230. package/dist-lib/sdk.cjs +2433 -0
  231. package/dist-lib/sdk.mjs +2336 -0
  232. package/dist-lib/skills.cjs +39132 -0
  233. package/dist-lib/swift-submit.cjs +92 -0
  234. package/dist-lib/swift-submit.mjs +65 -0
  235. package/dist-lib/swift.cjs +1646 -0
  236. package/dist-lib/vite.cjs +1591 -0
  237. package/package.json +290 -0
  238. package/scripts/dev-server-scanner.ts +1291 -0
  239. package/scripts/optional-demo-registry.ts +113 -0
  240. package/skills/contrast/SKILL.md +165 -0
  241. package/skills/rnx-debug/SKILL.md +433 -0
  242. package/skills/rnx-perf/SKILL.md +399 -0
  243. package/skills/rnx-setup/SKILL.md +172 -0
  244. package/skills/rnx-test/SKILL.md +321 -0
  245. package/skills/rnx-visual/SKILL.md +126 -0
  246. package/src/agent-daemon-client.ts +410 -0
  247. package/src/agent-events.ts +71 -0
  248. package/src/agent-identity.ts +23 -0
  249. package/src/agent-prompt.ts +71 -0
  250. package/src/agent-sessions.ts +580 -0
  251. package/src/app-url.ts +39 -0
  252. package/src/attached-projects.ts +539 -0
  253. package/src/auth/shared-session.ts +235 -0
  254. package/src/backend-origin.ts +70 -0
  255. package/src/beta.ts +19 -0
  256. package/src/bridge-constants.ts +175 -0
  257. package/src/bridge-contract-input.ts +502 -0
  258. package/src/bridge-contract.ts +558 -0
  259. package/src/browser-cache.ts +16 -0
  260. package/src/capture-contract.ts +587 -0
  261. package/src/cli-constants.ts +1 -0
  262. package/src/cli-version.ts +40 -0
  263. package/src/cloud-contract.ts +274 -0
  264. package/src/cloud.ts +762 -0
  265. package/src/codex-client.ts +234 -0
  266. package/src/config.ts +132 -0
  267. package/src/connect.ts +101 -0
  268. package/src/dev-bundle-resolution.ts +346 -0
  269. package/src/dev-server-open.ts +198 -0
  270. package/src/disk-cleanup.ts +540 -0
  271. package/src/flow-timeout-scale.ts +15 -0
  272. package/src/home-paths.ts +806 -0
  273. package/src/host/agent-host.ts +666 -0
  274. package/src/host/bridge-host.ts +2788 -0
  275. package/src/host/fetch-proxy-handler.ts +449 -0
  276. package/src/host/fetch-proxy-overrides.ts +51 -0
  277. package/src/host/open-url.ts +89 -0
  278. package/src/host/plane-host.ts +203 -0
  279. package/src/host/replacement-module-handler.ts +158 -0
  280. package/src/host/websocket-proxy.ts +315 -0
  281. package/src/index.ts +71 -0
  282. package/src/jump-to-source-babel.ts +135 -0
  283. package/src/jump-to-source-native.ts +102 -0
  284. package/src/menu.ts +777 -0
  285. package/src/metro-fingerprint-registry.ts +302 -0
  286. package/src/metro-plugin.ts +341 -0
  287. package/src/metro-production-bundle.ts +471 -0
  288. package/src/native-dev-bundle-url.ts +118 -0
  289. package/src/native-seam-manifest.ts +341 -0
  290. package/src/playwright-host-log.ts +16 -0
  291. package/src/plist.ts +27 -0
  292. package/src/poll-until.ts +123 -0
  293. package/src/profiles.ts +179 -0
  294. package/src/public-brand.ts +9 -0
  295. package/src/react-native-host-modules.ts +26 -0
  296. package/src/render-mode.ts +27 -0
  297. package/src/rolldown-plugin-swift.ts +240 -0
  298. package/src/runtime-assets.ts +233 -0
  299. package/src/runtime-delivery.ts +54 -0
  300. package/src/screenshots/compose.ts +422 -0
  301. package/src/screenshots/frame-compose.ts +514 -0
  302. package/src/screenshots/orchestrate.ts +256 -0
  303. package/src/screenshots/org-deck.ts +91 -0
  304. package/src/screenshots/registry.ts +58 -0
  305. package/src/screenshots/schema.ts +364 -0
  306. package/src/sdk.ts +15 -0
  307. package/src/settings-bundle.ts +418 -0
  308. package/src/shell-bridge-identity.ts +15 -0
  309. package/src/sim-client.ts +697 -0
  310. package/src/skills/builtin/a11y-review.ts +132 -0
  311. package/src/skills/builtin/compat-check.ts +71 -0
  312. package/src/skills/builtin/maestro-test.ts +121 -0
  313. package/src/skills/builtin/perf-profile.ts +90 -0
  314. package/src/skills/builtin/screenshot-all.ts +52 -0
  315. package/src/skills/builtin/visual-diff.ts +100 -0
  316. package/src/skills/registry.ts +107 -0
  317. package/src/skills/types.ts +41 -0
  318. package/src/swift-submit.ts +185 -0
  319. package/src/swift.ts +7 -0
  320. package/src/team-machine-prompt.ts +73 -0
  321. package/src/vite-plugin-one.ts +264 -0
  322. package/src/vite-plugin-swift.ts +620 -0
  323. package/src/vite-plugin.ts +1946 -0
  324. package/src/worklets-babel.ts +250 -0
@@ -0,0 +1,357 @@
1
+ // rnx screenshot slides — drive the live screenshot-mode composition from the
2
+ // terminal. same pipeline as the in-shell UI (rail/strip): every mutation
3
+ // goes through the shell's screenshotControl bridge so the browser UI
4
+ // reflects each step live, captures auto-upload, and exports are the
5
+ // composed App-Store canvases (bg + copy + posed device at export size).
6
+
7
+ import { mkdirSync, writeFileSync } from 'node:fs'
8
+ import { dirname, isAbsolute, join, resolve } from 'node:path'
9
+ import {
10
+ callInBridge,
11
+ callInBridgeWrite,
12
+ createBridgeFromParsed,
13
+ evalInBridge,
14
+ parseBridgeCliArgs,
15
+ type WsBridge,
16
+ } from '../ws-bridge'
17
+
18
+ const HELP = `
19
+ rnx screenshot slides — compose App Store screenshots in the live sim
20
+
21
+ usage:
22
+ rnx screenshot slides list slides (alias: slides list)
23
+ rnx screenshot slides set <n> [fields] patch a slide's copy/colors
24
+ rnx screenshot slides add append a blank slide (focuses it)
25
+ rnx screenshot slides remove <n> delete a slide
26
+ rnx screenshot slides move <n> <position> reorder a slide
27
+ rnx screenshot slides focus <n> open a slide (device flips into it)
28
+ rnx screenshot slides overview back to the gallery
29
+ rnx screenshot slides capture [<n>] capture the live frame into a slide
30
+ rnx screenshot slides export [<n>] [--out <dir>]
31
+ write composed App-Store PNG(s)
32
+
33
+ set fields:
34
+ --title <text> headline
35
+ --subtitle <text> subheadline
36
+ --bg-top <color> gradient top (white gray slate black cyan blue
37
+ --bg-bottom <color> gradient bottom teal green lime gold orange coral
38
+ --title-color <color> copy color override, or "auto" red berry magenta
39
+ --title-size <s> sm | md | lg | none gray-light
40
+ --underlay <s> black | white | none gray-dark …)
41
+ --shadow <s> light | medium | strong | none
42
+
43
+ notes:
44
+ slides are addressed by 1-based index. screenshot mode is auto-enabled.
45
+ capture shoots whatever the sim currently shows — navigate the app first
46
+ (rnx do tap-id …), then capture.
47
+
48
+ examples:
49
+ rnx screenshot slides set 1 --title "The ultimate MMA community." --bg-top cyan --bg-bottom black
50
+ rnx screenshot slides capture 1
51
+ rnx screenshot slides export --out ./exports
52
+ `
53
+
54
+ interface SlideRow {
55
+ index: number
56
+ id: string
57
+ title: string
58
+ subtitle: string
59
+ bgTop: string
60
+ bgBottom: string
61
+ titleColor: string | null
62
+ titleSize: string | null
63
+ titleUnderlay: string | null
64
+ titleShadow: string | null
65
+ captured: boolean
66
+ captureMode: string | null
67
+ focused: boolean
68
+ active: boolean
69
+ }
70
+
71
+ interface ControlState {
72
+ projectName: string
73
+ canvas: string
74
+ exportSize: { width: number; height: number }
75
+ deviceModel: string
76
+ slides: SlideRow[]
77
+ }
78
+
79
+ // my value flags, extracted before bridge-arg parsing.
80
+ const VALUE_FLAGS: Record<string, string> = {
81
+ '--title': 'title',
82
+ '--subtitle': 'subtitle',
83
+ '--bg-top': 'bgTop',
84
+ '--bg-bottom': 'bgBottom',
85
+ '--title-color': 'titleColor',
86
+ '--title-size': 'titleSize',
87
+ '--underlay': 'titleUnderlay',
88
+ '--shadow': 'titleShadow',
89
+ '--out': 'out',
90
+ }
91
+
92
+ function extractFlags(args: string[]): {
93
+ rest: string[]
94
+ values: Record<string, string>
95
+ json: boolean
96
+ } {
97
+ const rest: string[] = []
98
+ const values: Record<string, string> = {}
99
+ let json = false
100
+ for (let i = 0; i < args.length; i += 1) {
101
+ const arg = args[i]
102
+ if (arg === '--json') {
103
+ json = true
104
+ continue
105
+ }
106
+ const eq = arg.indexOf('=')
107
+ const flagName = eq > 0 ? arg.slice(0, eq) : arg
108
+ const key = VALUE_FLAGS[flagName]
109
+ if (key) {
110
+ if (eq > 0) {
111
+ values[key] = arg.slice(eq + 1)
112
+ } else {
113
+ values[key] = args[i + 1] ?? ''
114
+ i += 1
115
+ }
116
+ continue
117
+ }
118
+ rest.push(arg)
119
+ }
120
+ return { rest, values, json }
121
+ }
122
+
123
+ function parseSlideRef(raw: string | undefined): number | string | undefined {
124
+ if (!raw) return undefined
125
+ const n = Number(raw)
126
+ return Number.isInteger(n) && n > 0 ? n : raw
127
+ }
128
+
129
+ async function ensureScreenshotMode(bridge: WsBridge): Promise<void> {
130
+ const on = await evalInBridge<boolean>(
131
+ bridge,
132
+ `(async () => {
133
+ if (window.SootSim?.bridges?.settings?.get?.()?.screenshotMode) return true
134
+ window.dispatchEvent(new CustomEvent('sootsim:shell-command', {
135
+ detail: { type: 'fire-action', id: 'toggle-screenshot-mode' },
136
+ }))
137
+ const deadline = Date.now() + 2000
138
+ while (Date.now() < deadline) {
139
+ if (window.SootSim?.bridges?.settings?.get?.()?.screenshotMode) return true
140
+ await new Promise((r) => setTimeout(r, 40))
141
+ }
142
+ return false
143
+ })()`,
144
+ { acquireLock: true },
145
+ )
146
+ if (!on) {
147
+ throw new Error(
148
+ 'could not enable screenshot mode — is this a browser sim with the screenshot plugin? (electron has no screenshot mode)',
149
+ )
150
+ }
151
+ }
152
+
153
+ function printState(state: ControlState) {
154
+ console.log(
155
+ ` ${state.projectName} — canvas ${state.canvas} (${state.exportSize.width}×${state.exportSize.height}), device ${state.deviceModel}`,
156
+ )
157
+ for (const s of state.slides) {
158
+ const marks = [
159
+ s.focused ? 'focused' : s.active ? 'active' : null,
160
+ s.captured ? `captured${s.captureMode === '3d' ? ' 3d' : ''}` : 'no capture',
161
+ ]
162
+ .filter(Boolean)
163
+ .join(', ')
164
+ const copy = s.title ? `"${s.title}"` : '(no title)'
165
+ const style = [
166
+ `bg ${s.bgTop}→${s.bgBottom}`,
167
+ s.titleColor ? `color ${s.titleColor}` : null,
168
+ s.titleSize ? `size ${s.titleSize}` : null,
169
+ s.titleUnderlay ? `underlay ${s.titleUnderlay}` : null,
170
+ s.titleShadow ? `shadow ${s.titleShadow}` : null,
171
+ ]
172
+ .filter(Boolean)
173
+ .join(', ')
174
+ console.log(` ${s.index}. ${copy} — ${style} [${marks}]`)
175
+ if (s.subtitle) console.log(` ${s.subtitle}`)
176
+ }
177
+ }
178
+
179
+ function dataUrlToBytes(dataUrl: string): Buffer {
180
+ const m = dataUrl.match(/^data:[^;]+;base64,(.+)$/)
181
+ if (!m) throw new Error('export returned an unexpected payload')
182
+ return Buffer.from(m[1], 'base64')
183
+ }
184
+
185
+ export async function runSlides(
186
+ args: string[],
187
+ opts: { port?: number; verbose?: boolean },
188
+ ): Promise<void> {
189
+ if (args.includes('--help') || args.includes('-h')) {
190
+ console.log(HELP)
191
+ return
192
+ }
193
+ const { rest, values, json } = extractFlags(args)
194
+ const parsed = parseBridgeCliArgs(rest, {
195
+ port: opts.port,
196
+ // capture waits on focus animation + frame bake; export composes at
197
+ // full App-Store resolution — both can exceed the default 15s.
198
+ commandTimeoutMs: 45_000,
199
+ })
200
+ const sub = parsed.positional[0] ?? 'list'
201
+ const refArg = parseSlideRef(parsed.positional[1])
202
+ const positionArg = Number(parsed.positional[2])
203
+ const bridge = createBridgeFromParsed(parsed)
204
+ const emit = (state: ControlState) => {
205
+ if (json) console.log(JSON.stringify(state, null, 2))
206
+ else printState(state)
207
+ }
208
+
209
+ try {
210
+ await ensureScreenshotMode(bridge)
211
+ switch (sub) {
212
+ case 'list': {
213
+ emit(
214
+ await callInBridge<ControlState>(
215
+ bridge,
216
+ 'SootSim.bridges.screenshotControl.getState',
217
+ ),
218
+ )
219
+ break
220
+ }
221
+ case 'set': {
222
+ if (refArg == null)
223
+ throw new Error('usage: rnx screenshot slides set <n> --title … (see --help)')
224
+ const patch: Record<string, string> = {}
225
+ for (const key of [
226
+ 'title',
227
+ 'subtitle',
228
+ 'bgTop',
229
+ 'bgBottom',
230
+ 'titleColor',
231
+ 'titleSize',
232
+ 'titleUnderlay',
233
+ 'titleShadow',
234
+ ]) {
235
+ if (values[key] != null) patch[key] = values[key]
236
+ }
237
+ if (Object.keys(patch).length === 0) {
238
+ throw new Error('nothing to set — pass at least one field (see --help)')
239
+ }
240
+ emit(
241
+ await callInBridgeWrite<ControlState>(
242
+ bridge,
243
+ 'SootSim.bridges.screenshotControl.setSlide',
244
+ refArg,
245
+ patch,
246
+ ),
247
+ )
248
+ break
249
+ }
250
+ case 'add': {
251
+ emit(
252
+ await callInBridgeWrite<ControlState>(
253
+ bridge,
254
+ 'SootSim.bridges.screenshotControl.addSlide',
255
+ ),
256
+ )
257
+ break
258
+ }
259
+ case 'remove': {
260
+ if (refArg == null) throw new Error('usage: rnx screenshot slides remove <n>')
261
+ emit(
262
+ await callInBridgeWrite<ControlState>(
263
+ bridge,
264
+ 'SootSim.bridges.screenshotControl.removeSlide',
265
+ refArg,
266
+ ),
267
+ )
268
+ break
269
+ }
270
+ case 'move': {
271
+ if (refArg == null || !Number.isInteger(positionArg) || positionArg < 1) {
272
+ throw new Error('usage: rnx screenshot slides move <n> <position>')
273
+ }
274
+ emit(
275
+ await callInBridgeWrite<ControlState>(
276
+ bridge,
277
+ 'SootSim.bridges.screenshotControl.moveSlide',
278
+ refArg,
279
+ positionArg,
280
+ ),
281
+ )
282
+ break
283
+ }
284
+ case 'focus': {
285
+ if (refArg == null) throw new Error('usage: rnx screenshot slides focus <n>')
286
+ emit(
287
+ await callInBridgeWrite<ControlState>(
288
+ bridge,
289
+ 'SootSim.bridges.screenshotControl.focusSlide',
290
+ refArg,
291
+ ),
292
+ )
293
+ break
294
+ }
295
+ case 'overview': {
296
+ emit(
297
+ await callInBridgeWrite<ControlState>(
298
+ bridge,
299
+ 'SootSim.bridges.screenshotControl.overview',
300
+ ),
301
+ )
302
+ break
303
+ }
304
+ case 'capture': {
305
+ const state = await callInBridgeWrite<ControlState>(
306
+ bridge,
307
+ 'SootSim.bridges.screenshotControl.capture',
308
+ ...(refArg != null ? [refArg] : []),
309
+ )
310
+ emit(state)
311
+ break
312
+ }
313
+ case 'export': {
314
+ const outDir = values.out
315
+ ? isAbsolute(values.out)
316
+ ? values.out
317
+ : resolve(process.cwd(), values.out)
318
+ : join(process.cwd(), 'screenshots')
319
+ const state = await callInBridge<ControlState>(
320
+ bridge,
321
+ 'SootSim.bridges.screenshotControl.getState',
322
+ )
323
+ const targets =
324
+ refArg != null
325
+ ? state.slides.filter((s) => s.index === refArg || s.id === refArg)
326
+ : state.slides.filter((s) => s.captured)
327
+ if (targets.length === 0) {
328
+ throw new Error(
329
+ refArg != null
330
+ ? `no slide ${refArg}`
331
+ : 'no captured slides to export — capture first',
332
+ )
333
+ }
334
+ const written: string[] = []
335
+ for (const slide of targets) {
336
+ const result = await callInBridge<{
337
+ name: string
338
+ width: number
339
+ height: number
340
+ dataUrl: string
341
+ }>(bridge, 'SootSim.bridges.screenshotControl.exportSlide', slide.index)
342
+ const path = join(outDir, result.name)
343
+ mkdirSync(dirname(path), { recursive: true })
344
+ writeFileSync(path, dataUrlToBytes(result.dataUrl))
345
+ written.push(path)
346
+ console.log(` saved: ${path} (${result.width}×${result.height})`)
347
+ }
348
+ if (json) console.log(JSON.stringify({ written }, null, 2))
349
+ break
350
+ }
351
+ default:
352
+ throw new Error(`unknown subcommand "${sub}" — see rnx screenshot slides --help`)
353
+ }
354
+ } finally {
355
+ bridge.close()
356
+ }
357
+ }
@@ -0,0 +1,94 @@
1
+ import { printBridgeFailureDiagnostics } from '../bridge-diagnostics'
2
+ import {
3
+ type BridgeTransportOption,
4
+ createBridgeFromParsed,
5
+ parseBridgeCliArgs,
6
+ } from '../ws-bridge'
7
+ import { formatSemanticTree, isSimSemanticTree } from './inspect/core'
8
+ import type { SimStateResult } from '../../src/bridge-contract'
9
+
10
+ function isSimStateResult(value: unknown): value is SimStateResult {
11
+ return (
12
+ !!value &&
13
+ typeof value === 'object' &&
14
+ typeof Reflect.get(value, 'capturedAt') === 'number' &&
15
+ (Reflect.get(value, 'tree') === undefined ||
16
+ isSimSemanticTree(Reflect.get(value, 'tree')))
17
+ )
18
+ }
19
+
20
+ function printHelp(): void {
21
+ console.log(`
22
+ rnx state - capture the current app state in one call
23
+
24
+ usage:
25
+ rnx state [--json] [--sim <id>]
26
+
27
+ options:
28
+ --json print the complete structured result, including the screenshot
29
+ --sim <id> target a specific sim
30
+ --port <p> WS bridge port
31
+ `)
32
+ }
33
+
34
+ export async function runState(
35
+ args: string[],
36
+ opts: { port?: number } & BridgeTransportOption = {},
37
+ ): Promise<number> {
38
+ if (args.includes('--help') || args.includes('-h')) {
39
+ printHelp()
40
+ return 0
41
+ }
42
+
43
+ const parsed = parseBridgeCliArgs(args, {
44
+ port: opts.port,
45
+ stripBooleanFlags: ['--json'],
46
+ })
47
+ if (parsed.positional.length > 0) {
48
+ console.error(` unexpected state argument: ${parsed.positional[0]}`)
49
+ return 1
50
+ }
51
+
52
+ const bridge = (opts.createBridge ?? createBridgeFromParsed)(parsed)
53
+ try {
54
+ const value: unknown = await bridge.send({ type: 'state' })
55
+ if (!isSimStateResult(value)) {
56
+ throw new Error('state bridge returned an invalid result')
57
+ }
58
+
59
+ if (args.includes('--json')) {
60
+ console.log(JSON.stringify(value))
61
+ return 0
62
+ }
63
+
64
+ const route = value.route
65
+ console.log(` route: ${route?.screen ?? route?.url ?? '(unknown)'}`)
66
+ if (route?.stack?.length) console.log(` stack: ${route.stack.join(' > ')}`)
67
+ console.log(` keyboard: ${value.keyboard?.visible ? 'visible' : 'hidden'}`)
68
+ console.log('')
69
+ console.log(value.tree ? formatSemanticTree(value.tree) : '')
70
+ console.log('')
71
+ if (value.recentErrors?.length) {
72
+ console.log(` recent errors (${value.recentErrors.length}):`)
73
+ for (const error of value.recentErrors) {
74
+ console.log(` [${error.level}] ${error.text}`)
75
+ }
76
+ } else {
77
+ console.log(' recent errors: none')
78
+ }
79
+ const screenshotLength =
80
+ typeof value.screenshot === 'string' ? value.screenshot.length : 0
81
+ console.log(
82
+ screenshotLength > 0
83
+ ? ` screenshot: captured (${screenshotLength} characters; use --json to read it)`
84
+ : ' screenshot: unavailable',
85
+ )
86
+ return 0
87
+ } catch (error) {
88
+ console.error(` state failed: ${error instanceof Error ? error.message : error}`)
89
+ await printBridgeFailureDiagnostics(bridge)
90
+ return 1
91
+ } finally {
92
+ bridge.close()
93
+ }
94
+ }
@@ -0,0 +1,58 @@
1
+ import { runProfile } from './profile'
2
+ import { runReset } from './reset'
3
+
4
+ interface StorageOptions {
5
+ port?: number
6
+ verbose?: boolean
7
+ }
8
+
9
+ const VALUE_FLAGS = new Set(['--sim', '--port', '-p'])
10
+ const SUBCOMMANDS = new Set(['profile', 'clear'])
11
+
12
+ function findSubcommand(args: string[]): { name: string; index: number } | null {
13
+ for (let i = 0; i < args.length; i++) {
14
+ const arg = args[i]
15
+ if (arg.startsWith('-')) {
16
+ if (VALUE_FLAGS.has(arg)) i++
17
+ continue
18
+ }
19
+ return SUBCOMMANDS.has(arg) ? { name: arg, index: i } : null
20
+ }
21
+ return null
22
+ }
23
+
24
+ function printHelp(): void {
25
+ console.log(`
26
+ rnx storage — manage sim and app storage
27
+
28
+ usage:
29
+ rnx storage profile <subcommand>
30
+ rnx storage clear [--full] [--sim <id>] [--json]
31
+
32
+ subcommands:
33
+ profile list
34
+ profile create <id>
35
+ profile clear <id>
36
+ profile delete <id>
37
+ clear clear the current guest app data and relaunch it
38
+ `)
39
+ }
40
+
41
+ export async function runStorage(args: string[], opts: StorageOptions): Promise<number> {
42
+ if (args.includes('--help') || args.includes('-h')) {
43
+ printHelp()
44
+ return 0
45
+ }
46
+
47
+ const sub = findSubcommand(args)
48
+ if (sub?.name === 'profile') {
49
+ return runProfile([...args.slice(0, sub.index), ...args.slice(sub.index + 1)], opts)
50
+ }
51
+ if (sub?.name === 'clear') {
52
+ return runReset([...args.slice(0, sub.index), ...args.slice(sub.index + 1)], opts)
53
+ }
54
+
55
+ console.error(` unknown storage command: ${sub?.name ?? '(missing)'}`)
56
+ printHelp()
57
+ return 1
58
+ }
@@ -0,0 +1,55 @@
1
+ // `rnx config telemetry on|off` — retained as the command spelling for the
2
+ // product analytics preference shown by guided setup and Settings.
3
+ //
4
+ // this is the process-wide preference; `DO_NOT_TRACK=1` still forces opt-out
5
+ // for a single run regardless of what's stored (honored by isOptedOut in
6
+ // telemetry.ts, the same cross-tool signal npm/turbo/supabase use).
7
+
8
+ import {
9
+ configFilePath,
10
+ readPrivacyPreferences,
11
+ writePrivacyPreferences,
12
+ } from '../../src/home-paths'
13
+ import { rnxExit } from '../run-rnx'
14
+
15
+ function printStatus(): void {
16
+ const preferences = readPrivacyPreferences()
17
+ const enabled = preferences.configured && preferences.productAnalytics
18
+ console.log(` bounded diagnostics are ${enabled ? 'ON' : 'OFF'}`)
19
+ console.log(' permitted fields: package name/version/unsupported API or error class')
20
+ console.log(` config: ${configFilePath()}`)
21
+ const doNotTrack = process.env.DO_NOT_TRACK
22
+ if (doNotTrack === '1' || doNotTrack === 'true') {
23
+ console.log(
24
+ ` note: DO_NOT_TRACK=${doNotTrack} is set — telemetry is off for this run`,
25
+ )
26
+ }
27
+ }
28
+
29
+ export async function runTelemetry(args: string[]): Promise<void> {
30
+ const sub = args.find((a) => !a.startsWith('-'))?.toLowerCase()
31
+
32
+ switch (sub) {
33
+ case undefined:
34
+ case 'status':
35
+ printStatus()
36
+ return
37
+
38
+ case 'on':
39
+ writePrivacyPreferences({ productAnalytics: true, crashReports: false })
40
+ console.log(' bounded diagnostics ON')
41
+ console.log(` config: ${configFilePath()}`)
42
+ return
43
+
44
+ case 'off':
45
+ writePrivacyPreferences({ productAnalytics: false, crashReports: false })
46
+ console.log(' bounded diagnostics OFF')
47
+ console.log(` config: ${configFilePath()}`)
48
+ return
49
+
50
+ default:
51
+ console.error(` unknown telemetry action: ${sub}`)
52
+ console.error(' usage: rnx config telemetry [on|off|status]')
53
+ rnxExit(1)
54
+ }
55
+ }