rnxsim 0.1.375 → 0.1.377

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 (295) hide show
  1. package/README.md +28 -25
  2. package/cli/app-config.ts +9 -12
  3. package/cli/app-fonts.ts +3 -3
  4. package/cli/app-project.ts +8 -8
  5. package/cli/auth.ts +7 -9
  6. package/cli/bin.ts +16 -31
  7. package/cli/bridge-flow-runner.ts +3 -3
  8. package/cli/browser-backend.ts +44 -0
  9. package/cli/command-contract.ts +221 -0
  10. package/cli/command-registry.ts +291 -0
  11. package/cli/commands/agent-wrapper.ts +2 -2
  12. package/cli/commands/app-fonts.ts +2 -2
  13. package/cli/commands/assert.ts +3 -3
  14. package/cli/commands/auth.ts +1 -1
  15. package/cli/commands/box/cloud-box.ts +1 -1
  16. package/cli/commands/box/plane-share.ts +2 -2
  17. package/cli/commands/box.ts +4 -6
  18. package/cli/commands/compat.ts +1 -1
  19. package/cli/commands/config.ts +2 -2
  20. package/cli/commands/control.ts +42 -28
  21. package/cli/commands/cpu-profile.ts +7 -7
  22. package/cli/commands/daemon-mac-app.ts +7 -7
  23. package/cli/commands/daemon.ts +10 -10
  24. package/cli/commands/debug.ts +11 -11
  25. package/cli/commands/detox.ts +13 -13
  26. package/cli/commands/diagnose.ts +6 -6
  27. package/cli/commands/do-chain.ts +1 -2
  28. package/cli/commands/electron.ts +1 -3
  29. package/cli/commands/film.ts +3 -3
  30. package/cli/commands/flow.ts +43 -43
  31. package/cli/commands/inspect/count.ts +1 -1
  32. package/cli/commands/inspect/describe.ts +1 -1
  33. package/cli/commands/inspect/env.ts +2 -2
  34. package/cli/commands/inspect/get-layout.ts +1 -1
  35. package/cli/commands/inspect/keyboard.ts +1 -1
  36. package/cli/commands/inspect/list.ts +1 -1
  37. package/cli/commands/inspect/memory.ts +1 -1
  38. package/cli/commands/inspect/resolve-target.ts +1 -1
  39. package/cli/commands/inspect/screens.ts +1 -1
  40. package/cli/commands/inspect/tree.ts +1 -1
  41. package/cli/commands/inspect/url.ts +1 -1
  42. package/cli/commands/inspect/wait-event.ts +5 -5
  43. package/cli/commands/inspect.ts +34 -34
  44. package/cli/commands/install-desktop.ts +3 -3
  45. package/cli/commands/login.ts +6 -6
  46. package/cli/commands/maestro-generate.ts +5 -5
  47. package/cli/commands/maestro.ts +5 -5
  48. package/cli/commands/profile.ts +1 -1
  49. package/cli/commands/record.ts +9 -9
  50. package/cli/commands/runtime.ts +18 -18
  51. package/cli/commands/screenshot-command.ts +1 -1
  52. package/cli/commands/screenshot-layers.ts +5 -5
  53. package/cli/commands/screenshot-mode.ts +1 -1
  54. package/cli/commands/screenshot.ts +8 -8
  55. package/cli/commands/screenshots-capture.ts +5 -5
  56. package/cli/commands/serve.ts +3 -3
  57. package/cli/commands/setup.ts +20 -12
  58. package/cli/commands/skills.ts +5 -5
  59. package/cli/commands/slides.ts +1 -1
  60. package/cli/commands/telemetry.ts +1 -1
  61. package/cli/commands/three-mode.ts +1 -1
  62. package/cli/commands/timeline.ts +8 -8
  63. package/cli/commands/upgrade.ts +116 -131
  64. package/cli/commands/upload.ts +12 -12
  65. package/cli/commands/version.ts +7 -7
  66. package/cli/commands/what-happened.ts +9 -9
  67. package/cli/current-sim.ts +3 -3
  68. package/cli/desktop-companion.ts +1 -1
  69. package/cli/drivers/playwright.ts +9 -9
  70. package/cli/drivers/registry.ts +1 -1
  71. package/cli/drivers/types.ts +1 -1
  72. package/cli/flow-file.ts +2 -2
  73. package/cli/flow-session.ts +1 -1
  74. package/cli/help-core.ts +136 -0
  75. package/cli/help.ts +17 -53
  76. package/cli/hints.ts +5 -5
  77. package/cli/inspect-notice-state.ts +1 -1
  78. package/cli/maestro-js.ts +1 -1
  79. package/cli/outbound-endpoints.ts +23 -5
  80. package/cli/parse-args.ts +19 -66
  81. package/cli/recording-access.ts +1 -1
  82. package/cli/registry.ts +1 -0
  83. package/cli/run-registry.ts +1 -1
  84. package/cli/run-rnx-core.ts +148 -0
  85. package/cli/run-rnx.ts +6 -9
  86. package/cli/runtime-notes.ts +3 -3
  87. package/cli/standalone.ts +4 -0
  88. package/cli/ws-bridge.ts +8 -8
  89. package/detox/config-loader.ts +2 -2
  90. package/detox/index.ts +8 -8
  91. package/detox/jest-preset.cjs +1 -1
  92. package/detox/navigation.ts +2 -2
  93. package/dist-lib/agent-daemon-client.cjs +5 -5
  94. package/dist-lib/agent-events.cjs +1 -1
  95. package/dist-lib/agent-identity.cjs +2 -2
  96. package/dist-lib/agent-sessions.cjs +10 -10
  97. package/dist-lib/attached-projects.cjs +6 -6
  98. package/dist-lib/auth/shared-session.cjs +6 -6
  99. package/dist-lib/backend-origin.cjs +1 -1
  100. package/dist-lib/beta.cjs +1 -1
  101. package/dist-lib/beta.mjs +1 -1
  102. package/dist-lib/bridge-constants.cjs +1 -1
  103. package/dist-lib/bridge-contract-input.cjs +1 -1
  104. package/dist-lib/bridge-contract-input.mjs +1 -1
  105. package/dist-lib/bridge-contract.cjs +1 -1
  106. package/dist-lib/bridge-contract.mjs +1 -1
  107. package/dist-lib/cli-constants.cjs +1 -1
  108. package/dist-lib/config.cjs +25 -25
  109. package/dist-lib/detox/index.cjs +9 -9
  110. package/dist-lib/detox/jest-preset.cjs +1 -1
  111. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  112. package/dist-lib/home-paths.cjs +29 -29
  113. package/dist-lib/host/bridge-host.cjs +274 -181
  114. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  115. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  116. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  117. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  118. package/dist-lib/host/websocket-proxy.cjs +1 -1
  119. package/dist-lib/index.cjs +14 -14
  120. package/dist-lib/jump-to-source-babel.cjs +1 -1
  121. package/dist-lib/menu.cjs +1 -1
  122. package/dist-lib/menu.mjs +1 -1
  123. package/dist-lib/metro-production-bundle.cjs +1 -1
  124. package/dist-lib/metro-production-bundle.mjs +1 -1
  125. package/dist-lib/metro.cjs +8 -8
  126. package/dist-lib/profiles.cjs +12 -12
  127. package/dist-lib/public-brand.cjs +1 -1
  128. package/dist-lib/react-native-host-modules.cjs +1 -1
  129. package/dist-lib/react-native-host-modules.mjs +1 -1
  130. package/dist-lib/render-mode.cjs +1 -1
  131. package/dist-lib/scripts/dev-server-scanner.cjs +25 -11
  132. package/dist-lib/sdk.cjs +1 -1
  133. package/dist-lib/sdk.mjs +1 -1
  134. package/dist-lib/skills.cjs +470 -322
  135. package/dist-lib/vite.cjs +9 -9
  136. package/package.json +4 -11
  137. package/scripts/dev-server-scanner.ts +25 -7
  138. package/scripts/optional-demo-registry.ts +1 -1
  139. package/skills/contrast/SKILL.md +3 -3
  140. package/skills/rnx-debug/SKILL.md +1 -1
  141. package/skills/rnx-test/SKILL.md +2 -2
  142. package/src/agent-identity.ts +1 -1
  143. package/src/agent-sessions.ts +7 -5
  144. package/src/cli-version.ts +12 -4
  145. package/src/config.ts +26 -28
  146. package/src/disk-cleanup.ts +11 -11
  147. package/src/home-paths.ts +28 -28
  148. package/src/host/bridge-host.ts +16 -16
  149. package/src/metro-plugin.ts +2 -2
  150. package/src/profiles.ts +2 -2
  151. package/src/runtime-assets.ts +7 -7
  152. package/src/runtime-delivery.ts +13 -13
  153. package/src/screenshots/orchestrate.ts +1 -1
  154. package/src/screenshots/schema.ts +2 -2
  155. package/src/skills/builtin/a11y-review.ts +2 -2
  156. package/src/skills/builtin/compat-check.ts +2 -2
  157. package/src/skills/builtin/maestro-test.ts +2 -2
  158. package/src/skills/builtin/perf-profile.ts +2 -2
  159. package/src/skills/builtin/screenshot-all.ts +2 -2
  160. package/src/skills/builtin/visual-diff.ts +2 -2
  161. package/src/skills/registry.ts +8 -8
  162. package/src/skills/types.ts +1 -1
  163. package/src/vite-plugin-one.ts +3 -3
  164. package/src/vite-plugin.ts +15 -2
  165. package/dist-cli/bin.js +0 -15
  166. package/dist-cli/chunks/agent-JJN7U6FR.js +0 -48
  167. package/dist-cli/chunks/agent-wrapper-AVVUJ3WF.js +0 -17
  168. package/dist-cli/chunks/app-fonts-QJ22CNAZ.js +0 -4
  169. package/dist-cli/chunks/assert-PZZKIY3K.js +0 -49
  170. package/dist-cli/chunks/auth-5AVDJO33.js +0 -12
  171. package/dist-cli/chunks/beta-PFRGP266.js +0 -4
  172. package/dist-cli/chunks/box-ILC2O7ZO.js +0 -1730
  173. package/dist-cli/chunks/camera-J3V62PP7.js +0 -33
  174. package/dist-cli/chunks/chunk-25KRGDZT.js +0 -4
  175. package/dist-cli/chunks/chunk-2BRBFLWA.js +0 -38
  176. package/dist-cli/chunks/chunk-2GXNAZHB.js +0 -74
  177. package/dist-cli/chunks/chunk-2IUMRRYV.js +0 -7
  178. package/dist-cli/chunks/chunk-4ALPWIMP.js +0 -10
  179. package/dist-cli/chunks/chunk-4CEZZKRY.js +0 -4
  180. package/dist-cli/chunks/chunk-4EUKOIAV.js +0 -4
  181. package/dist-cli/chunks/chunk-4IKE4YUS.js +0 -32
  182. package/dist-cli/chunks/chunk-4ZXR2M3D.js +0 -4
  183. package/dist-cli/chunks/chunk-5CGJFU27.js +0 -7
  184. package/dist-cli/chunks/chunk-5HHWECGT.js +0 -4
  185. package/dist-cli/chunks/chunk-5PZVYF42.js +0 -312
  186. package/dist-cli/chunks/chunk-7NCPUZ52.js +0 -27
  187. package/dist-cli/chunks/chunk-7OSIJCBR.js +0 -107
  188. package/dist-cli/chunks/chunk-AQ5TMOAI.js +0 -4
  189. package/dist-cli/chunks/chunk-BQCEI37V.js +0 -5
  190. package/dist-cli/chunks/chunk-C6B5FOP3.js +0 -4
  191. package/dist-cli/chunks/chunk-CCMZSHJB.js +0 -71
  192. package/dist-cli/chunks/chunk-CX7VCKAT.js +0 -14
  193. package/dist-cli/chunks/chunk-DQUPSAHE.js +0 -4
  194. package/dist-cli/chunks/chunk-DSD2UBZI.js +0 -4
  195. package/dist-cli/chunks/chunk-EWPAI4MO.js +0 -7
  196. package/dist-cli/chunks/chunk-EZXV6U2N.js +0 -497
  197. package/dist-cli/chunks/chunk-F5F7FIPM.js +0 -6
  198. package/dist-cli/chunks/chunk-FUTYK55H.js +0 -30
  199. package/dist-cli/chunks/chunk-FW6AN7I2.js +0 -3
  200. package/dist-cli/chunks/chunk-FZDRA7Z4.js +0 -4
  201. package/dist-cli/chunks/chunk-GDLE4NIV.js +0 -4
  202. package/dist-cli/chunks/chunk-GEX7RVHY.js +0 -5
  203. package/dist-cli/chunks/chunk-H4MYYOYJ.js +0 -5
  204. package/dist-cli/chunks/chunk-HEV7BAPE.js +0 -11
  205. package/dist-cli/chunks/chunk-HNDWYCND.js +0 -506
  206. package/dist-cli/chunks/chunk-HNPHODBD.js +0 -4
  207. package/dist-cli/chunks/chunk-HWDVC6YA.js +0 -11
  208. package/dist-cli/chunks/chunk-JOHJZUGG.js +0 -4
  209. package/dist-cli/chunks/chunk-JWUDT4QK.js +0 -5
  210. package/dist-cli/chunks/chunk-K7QQWKGE.js +0 -4
  211. package/dist-cli/chunks/chunk-LAOKTKT4.js +0 -4
  212. package/dist-cli/chunks/chunk-LD7I266X.js +0 -24
  213. package/dist-cli/chunks/chunk-LMHBU5LZ.js +0 -4
  214. package/dist-cli/chunks/chunk-LSOGXSDV.js +0 -535
  215. package/dist-cli/chunks/chunk-MP2GPO4T.js +0 -4
  216. package/dist-cli/chunks/chunk-MXJ2UWRV.js +0 -4
  217. package/dist-cli/chunks/chunk-OJNKRIZF.js +0 -15
  218. package/dist-cli/chunks/chunk-PBOWUNXO.js +0 -4
  219. package/dist-cli/chunks/chunk-PNRJFHX4.js +0 -4
  220. package/dist-cli/chunks/chunk-QJQOK34O.js +0 -28
  221. package/dist-cli/chunks/chunk-RLCQLMFF.js +0 -142
  222. package/dist-cli/chunks/chunk-S65CK3RI.js +0 -9
  223. package/dist-cli/chunks/chunk-SWBUNMQS.js +0 -4
  224. package/dist-cli/chunks/chunk-SXBFGGAM.js +0 -4
  225. package/dist-cli/chunks/chunk-T4ISWS5H.js +0 -4
  226. package/dist-cli/chunks/chunk-TPPVAA7Z.js +0 -5
  227. package/dist-cli/chunks/chunk-USM3P74H.js +0 -4
  228. package/dist-cli/chunks/chunk-V4EJJHF5.js +0 -4
  229. package/dist-cli/chunks/chunk-V7RGRZOL.js +0 -423
  230. package/dist-cli/chunks/chunk-XUBV77WH.js +0 -130
  231. package/dist-cli/chunks/chunk-XYSDYGKC.js +0 -4
  232. package/dist-cli/chunks/chunk-YNHRSDVG.js +0 -5
  233. package/dist-cli/chunks/chunk-YX52RJKO.js +0 -4
  234. package/dist-cli/chunks/chunk-Z5O5H4O7.js +0 -99
  235. package/dist-cli/chunks/chunk-Z6LOIW46.js +0 -7
  236. package/dist-cli/chunks/chunk-ZXFVBYJY.js +0 -5
  237. package/dist-cli/chunks/cleanup-KPUW4SFK.js +0 -25
  238. package/dist-cli/chunks/cli-version-APGT6GAT.js +0 -4
  239. package/dist-cli/chunks/compat-XXRFD2TW.js +0 -23
  240. package/dist-cli/chunks/config-DDMFDQ7F.js +0 -13
  241. package/dist-cli/chunks/connect-IPG47YXM.js +0 -4
  242. package/dist-cli/chunks/control-O22Z3UJ5.js +0 -4
  243. package/dist-cli/chunks/daemon-PADUIMJV.js +0 -4
  244. package/dist-cli/chunks/debug-MYIP46TM.js +0 -200
  245. package/dist-cli/chunks/desktop-ENFFBQAA.js +0 -31
  246. package/dist-cli/chunks/detox-J5WVK6OH.js +0 -52
  247. package/dist-cli/chunks/device-RAVQVL75.js +0 -18
  248. package/dist-cli/chunks/diagnose-PJMPXKJY.js +0 -43
  249. package/dist-cli/chunks/disk-cleanup-YLAY445J.js +0 -4
  250. package/dist-cli/chunks/do-chain-UBE7GIOU.js +0 -5
  251. package/dist-cli/chunks/drivers-ZFNX3T5Z.js +0 -4
  252. package/dist-cli/chunks/env-Y5XJSNRD.js +0 -4
  253. package/dist-cli/chunks/film-W3V6VURB.js +0 -74
  254. package/dist-cli/chunks/flow-VMLI7BYM.js +0 -4
  255. package/dist-cli/chunks/help-PKZCWHJJ.js +0 -4
  256. package/dist-cli/chunks/hidden-runtime-alias-S5DJ7EDF.js +0 -4
  257. package/dist-cli/chunks/home-paths-5O4IZPYV.js +0 -4
  258. package/dist-cli/chunks/inspect-LT4MXK37.js +0 -4
  259. package/dist-cli/chunks/install-desktop-EXCFMXFS.js +0 -4
  260. package/dist-cli/chunks/login-V6TZ54ZH.js +0 -26
  261. package/dist-cli/chunks/logout-SPMLFWYA.js +0 -4
  262. package/dist-cli/chunks/maestro-5ISTBAZE.js +0 -108
  263. package/dist-cli/chunks/maestro-generate-WUI64NHR.js +0 -36
  264. package/dist-cli/chunks/mode-QPNBYXUF.js +0 -26
  265. package/dist-cli/chunks/optional-demo-registry-CBSYF4CG.js +0 -4
  266. package/dist-cli/chunks/perf-T6N5PVYV.js +0 -48
  267. package/dist-cli/chunks/permissions-YZ7RRSZJ.js +0 -13
  268. package/dist-cli/chunks/platform-XDQAA72X.js +0 -6
  269. package/dist-cli/chunks/privacy-54FIGXLT.js +0 -4
  270. package/dist-cli/chunks/record-QGYXRUKP.js +0 -72
  271. package/dist-cli/chunks/report-issue-VUUTGX7V.js +0 -29
  272. package/dist-cli/chunks/reset-CGO52KBW.js +0 -4
  273. package/dist-cli/chunks/resolve-target-NNQCALDM.js +0 -4
  274. package/dist-cli/chunks/runtime-4EP6YJVL.js +0 -4
  275. package/dist-cli/chunks/screenshot-command-NZCMSJ4P.js +0 -159
  276. package/dist-cli/chunks/screenshot-layers-VMTQ6XIU.js +0 -20
  277. package/dist-cli/chunks/screenshots-capture-K3MWIDBK.js +0 -42
  278. package/dist-cli/chunks/serve-QJRIB2BV.js +0 -56
  279. package/dist-cli/chunks/setup-6XXEJQVF.js +0 -4
  280. package/dist-cli/chunks/sim-client-5YUVL45C.js +0 -4
  281. package/dist-cli/chunks/skills-T3CS7CXK.js +0 -50
  282. package/dist-cli/chunks/state-AJC3VK3G.js +0 -14
  283. package/dist-cli/chunks/storage-KVGZKC3Y.js +0 -37
  284. package/dist-cli/chunks/store-AWUMOQUH.js +0 -4
  285. package/dist-cli/chunks/telemetry-W5ZDGWK6.js +0 -4
  286. package/dist-cli/chunks/timeline-DXDCAZUA.js +0 -24
  287. package/dist-cli/chunks/upgrade-SRPTJACU.js +0 -7
  288. package/dist-cli/chunks/upload-ZDV4BFED.js +0 -4
  289. package/dist-cli/chunks/version-SOLPSBMG.js +0 -6
  290. package/dist-cli/chunks/web-RT75LD7A.js +0 -4
  291. package/dist-cli/chunks/welcome-IJ55EC7U.js +0 -5
  292. package/dist-cli/chunks/what-happened-CEIWJIWK.js +0 -25
  293. package/dist-cli/chunks/ws-bridge-YK5R3HOI.js +0 -4
  294. package/scripts/postinstall.cjs +0 -3
  295. package/scripts/preuninstall.cjs +0 -118
@@ -1,29 +0,0 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{a as m,b as f}from"./chunk-HEV7BAPE.js";import"./chunk-JWUDT4QK.js";import"./chunk-T4ISWS5H.js";import{i as l}from"./chunk-S65CK3RI.js";import{M as p}from"./chunk-HWDVC6YA.js";import{a as d}from"./chunk-4EUKOIAV.js";import"./chunk-25KRGDZT.js";import"./chunk-DQUPSAHE.js";import{existsSync as h,readFileSync as w}from"node:fs";import{resolve as b}from"node:path";var R="https://contrast.dev",g=8e3;function v(){return{request:(t,e)=>fetch(t,e),confirmSubmission:l,isInteractive:process.stdin.isTTY===!0,now:()=>new Date,stdout:t=>process.stdout.write(t),stderr:t=>process.stderr.write(t)}}function I(t,e){let n=t.indexOf(e);if(n===-1)return null;let r=t[n+1];if(!r||r.startsWith("-"))throw new Error(`${e} requires a value`);return r}function D(t,e=process.cwd()){let n=I(t,"--app"),r=new Set,s=t.indexOf("--app");s!==-1&&(r.add(s),r.add(s+1));let a=[];for(let c=0;c<t.length;c+=1){if(r.has(c))continue;let u=t[c];if(!(u==="--yes"||u==="-y"||u==="--dry-run")){if(u.startsWith("-"))throw new Error(`unknown flag: ${u}`);a.push(u)}}let i=a.join(" ").trim();if(!i)throw new Error("describe the missing or broken compatibility behavior");if(i.length>g)throw new Error(`description is too long (maximum ${g} characters)`);return{appDir:b(e,n??"."),description:i,dryRun:t.includes("--dry-run"),yes:t.includes("--yes")||t.includes("-y")}}function y(t){if(!t||typeof t!="object")return;let e={};for(let n of Object.keys(t)){let r=Object.getOwnPropertyDescriptor(t,n)?.value;typeof r=="string"&&(e[n]=r)}return e}function O(t){let e=b(t,"package.json");if(!h(e))return null;let n;try{n=JSON.parse(w(e,"utf8"))}catch{throw new Error(`could not parse ${e}`)}if(!n||typeof n!="object")throw new Error(`${e} must contain a JSON object`);let r=Object.getOwnPropertyDescriptor(n,"name")?.value,s=Object.getOwnPropertyDescriptor(n,"dependencies")?.value,a=Object.getOwnPropertyDescriptor(n,"devDependencies")?.value,i=y(s),c=y(a);return{...typeof r=="string"?{name:r}:{},...i?{dependencies:i}:{},...c?{devDependencies:c}:{}}}async function S(t,e=new Date){let n=O(t.appDir),r=n?(await import("./web-RT75LD7A.js")).scanDeps(n):null,s=d(),a=p();return{note:t.description,timestamp:e.toISOString(),sootsim_version:a,cli_version:s,bundle:{activeApp:r&&r.projectName!=="unknown"?{appName:r.projectName}:null,reportSource:"cli",runtimeVersion:a,cliVersion:s,platform:process.platform,arch:process.arch,compatibility:r}}}function $(){let t=m(),e=t?.kind==="session"?t.origin.replace(/\/+$/,""):R,n=t&&t.kind!=="github"?f(t):null;return{url:`${e}/api/sootsim/report-issue`,authorization:n}}function k(t){if(!t)return"no package.json found; no compatibility scan attached";let e=t.packages.filter(n=>n.status!=="full"&&n.status!=="not-relevant").length;return`${t.packages.length} React Native ecosystem packages, ${e} needing attention`}function P(t){if(!t||typeof t!="object")return null;let e=Object.getOwnPropertyDescriptor(t,"id")?.value;return typeof e=="string"&&e?e:null}async function V(t,e=v()){let n;try{n=D(t)}catch(o){return e.stderr(` ${o instanceof Error?o.message:String(o)}
5
- usage: rnx report-issue [--app <dir>] [--dry-run] [--yes] "<description>"
6
- `),1}if(!h(n.appDir))return e.stderr(` directory not found: ${n.appDir}
7
- `),1;let r;try{r=await S(n,e.now())}catch(o){return e.stderr(` could not prepare the issue report: ${o instanceof Error?o.message:String(o)}
8
- `),1}if(e.stdout(`
9
- rnx issue report preview
10
-
11
- description: ${r.note}
12
- compatibility: ${k(r.bundle.compatibility)}
13
- environment: CLI ${r.bundle.cliVersion}, runtime ${r.bundle.runtimeVersion??"not installed"}, ${r.bundle.platform}/${r.bundle.arch}
14
-
15
- Includes only the description, compatibility scan, and the version/platform details above.
16
- It does not include source files, environment variables, terminal output, logs, screenshots, or app data.
17
- `),n.dryRun)return e.stdout(`
18
- dry run: nothing was sent.
19
- `),0;if(!n.yes){if(!e.isInteractive)return e.stderr(`
20
- not sent: ask the user for explicit approval, then rerun with --yes.
21
- `),2;if(!await e.confirmSubmission("send this compatibility report to the rnx team?",!1))return e.stdout(`
22
- not sent.
23
- `),2}let s=$(),a={"content-type":"application/json"};s.authorization&&(a.authorization=s.authorization);let i;try{i=await e.request(s.url,{method:"POST",headers:a,body:JSON.stringify(r),signal:AbortSignal.timeout(15e3)})}catch(o){return e.stderr(` report failed: ${o instanceof Error?o.message:String(o)}
24
- `),1}if(!i.ok){let o=(await i.text().catch(()=>"")).slice(0,240);return e.stderr(` report failed: ${i.status} ${i.statusText}${o?` \u2014 ${o}`:""}
25
- `),1}let c=await i.json().catch(()=>null),u=P(c);return e.stdout(u?`
26
- sent. Report id: ${u}
27
- `:`
28
- sent. Thank you.
29
- `),0}export{S as createReportIssuePayload,D as parseReportIssueOptions,V as runReportIssue};
@@ -1,4 +0,0 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{a}from"./chunk-OJNKRIZF.js";import"./chunk-2BRBFLWA.js";import"./chunk-5PZVYF42.js";import"./chunk-7NCPUZ52.js";import"./chunk-GEX7RVHY.js";import"./chunk-HWDVC6YA.js";import"./chunk-DQUPSAHE.js";export{a as runReset};
@@ -1,4 +0,0 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{a,b}from"./chunk-YX52RJKO.js";import"./chunk-5PZVYF42.js";import"./chunk-DQUPSAHE.js";export{a as readTargetFlag,b as resolveTargetCoords};
@@ -1,4 +0,0 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{c as a}from"./chunk-FUTYK55H.js";import"./chunk-PBOWUNXO.js";import"./chunk-MP2GPO4T.js";import"./chunk-7NCPUZ52.js";import"./chunk-GEX7RVHY.js";import"./chunk-T4ISWS5H.js";import"./chunk-F5F7FIPM.js";import"./chunk-HWDVC6YA.js";import"./chunk-25KRGDZT.js";import"./chunk-DQUPSAHE.js";export{a as runRuntime};
@@ -1,159 +0,0 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{a as se,b as ie,g as ae}from"./chunk-LSOGXSDV.js";import{a as C}from"./chunk-BQCEI37V.js";import"./chunk-QJQOK34O.js";import"./chunk-EZXV6U2N.js";import"./chunk-MXJ2UWRV.js";import{a as te}from"./chunk-CCMZSHJB.js";import{a as re}from"./chunk-RLCQLMFF.js";import"./chunk-Z5O5H4O7.js";import{a as oe,b as ne}from"./chunk-HEV7BAPE.js";import"./chunk-JWUDT4QK.js";import"./chunk-V7RGRZOL.js";import"./chunk-YX52RJKO.js";import"./chunk-EWPAI4MO.js";import"./chunk-XYSDYGKC.js";import"./chunk-PNRJFHX4.js";import"./chunk-HNDWYCND.js";import"./chunk-YNHRSDVG.js";import"./chunk-USM3P74H.js";import"./chunk-CX7VCKAT.js";import"./chunk-Z6LOIW46.js";import"./chunk-2BRBFLWA.js";import"./chunk-C6B5FOP3.js";import{o as Q,p as ee}from"./chunk-5PZVYF42.js";import"./chunk-FZDRA7Z4.js";import"./chunk-4ZXR2M3D.js";import"./chunk-PBOWUNXO.js";import"./chunk-MP2GPO4T.js";import"./chunk-ZXFVBYJY.js";import"./chunk-TPPVAA7Z.js";import{b as R,g as $,h as I,i as L,j as U,k as H,l as A}from"./chunk-H4MYYOYJ.js";import{t as B,u as X,v as O,w as J,x as Z,y as N,z as k}from"./chunk-7NCPUZ52.js";import"./chunk-GEX7RVHY.js";import"./chunk-4CEZZKRY.js";import{c as P}from"./chunk-T4ISWS5H.js";import"./chunk-JOHJZUGG.js";import"./chunk-XUBV77WH.js";import"./chunk-K7QQWKGE.js";import"./chunk-F5F7FIPM.js";import"./chunk-LMHBU5LZ.js";import"./chunk-HWDVC6YA.js";import"./chunk-25KRGDZT.js";import"./chunk-DQUPSAHE.js";import{mkdirSync as ue,writeFileSync as le}from"fs";import{dirname as de,resolve as pe}from"path";async function me(e,t){let r=Ue(e);(r.includes("--help")||r.includes("-h"))&&(console.log(`
5
- rnx screenshot \u2014 capture the canvas as a PNG
6
-
7
- usage:
8
- rnx screenshot [output] [options]
9
- rnx screenshot [options] --output <path>
10
-
11
- options:
12
- --output <path> output file path (default: /tmp/sootsim-inspect.png)
13
- first positional arg works too: \`screenshot /tmp/x.png\`
14
- --with-frame wrap the captured screen in a tight device frame
15
- --no-frame capture the raw screen bitmap (default)
16
- --no-shell tenant-only capture (no status bar, keyboard, toasts,
17
- notification center, or other shell overlays). use for
18
- clean app screenshots when a shell surface is in the way.
19
- --shell-only capture only the shell chrome, no tenant content
20
- --allow-loading capture after caller-owned readiness checks even when
21
- visible app copy matches a generic loading phrase
22
- --area x,y,w,h crop to a logical rnx rect
23
- --id <testID> crop to a node's bounding box
24
- --text <text> crop to a node found by text content
25
- --gallery crawl and capture multiple screens as HTML gallery
26
- --themes capture light and dark variants
27
-
28
- examples:
29
- rnx screenshot
30
- rnx screenshot --no-shell --output app.png
31
- rnx screenshot --with-frame --output framed.png
32
- rnx screenshot --area 0,200,393,400 --output hero.png
33
- rnx screenshot --id loginButton --output button.png
34
- `),P(0));let i=He(r);if(i.length>0){for(let{flag:h,suggestion:w}of i)console.error(w?` unknown flag: ${h} (did you mean ${w}?)`:` unknown flag: ${h}`);console.error(" run `rnx screenshot --help` for the list of supported flags"),P(1)}let o=r.includes("--gallery"),s=r.includes("--themes"),n=r.includes("--allow-loading"),a=_e(r),l=je(r);if(o||s)return a&&(console.error(" --with-frame is not supported with --gallery / --themes"),P(1)),ce(r,t);a&&r.some(h=>h==="--area"||h==="--id"||h==="--text")&&(console.error(" --with-frame only supports full-screen capture for now"),console.error(" remove --area / --id / --text, or capture raw and compose later"),P(1));let c=B(r,{port:t.port,stripBooleanFlags:["--with-frame","--no-frame","--no-shell","--shell-only","--allow-loading"],stripValueFlags:["--output","--area","--id","--text"]}),u=fe(r),d=await Oe(r);if(await Be(c.wsPort)){try{await Te(c,u,d,a,l,n)}catch(h){console.error(` screenshot failed: ${h instanceof Error?h.message:String(h)}`),P(1)}return}a&&(console.error(" --with-frame requires a running rnx bridge"),console.error(" open the app in a live sim first, then rerun the command"),P(1)),d&&(console.error(" --area / --id / --text require a running rnx bridge"),console.error(" start one with `rnx serve`, `rnx desktop`, or your project dev server"),P(1)),await ce(r,t)}async function Be(e){let t=X(e,{commandTimeoutMs:1e3});try{return await t.listSims(),!0}catch{return!1}finally{t.close()}}async function Oe(e){let t=e.find((o,s)=>e[s-1]==="--area");if(t){let o=t.split(",").map(c=>Number(c.trim()));if(o.length!==4||o.some(c=>!Number.isFinite(c)))throw new Error(`--area expects x,y,w,h (got "${t}")`);let[s,n,a,l]=o;return{x:s,y:n,w:a,h:l}}let r=e.find((o,s)=>e[s-1]==="--id"),i=e.find((o,s)=>e[s-1]==="--text");return r||i?{__matcher:{id:r,text:i}}:null}async function Te(e,t,r,i,o,s){let n=O({...e,commandTimeoutMs:45e3});try{if((await J(n)).hidden&&process.stderr.write(` \u26A0 screenshot will capture the LAST PAINTED canvas \u2014 likely stale.
35
- navigation/\`shell\` commands sent before this screenshot may not
36
- have repainted yet. focus the tab manually or \`rnx --sim <id>\`
37
- to target a visible sim before capturing.
38
-
39
- `),!s){let f=Number(process.env.CONTRAST_SCREENSHOT_READY_MAX_MS)||9e4,x=await ee(n,f);if(!x.ready&&x.externalError)process.stderr.write(` \u26A0 capturing an ERROR screen. the app failed to load: ${x.externalError}
40
- this is failure evidence, not app content. never use it as a baseline.
41
-
42
- `);else if(!x.ready)throw new Error(`app not ready to capture after ${Math.round(x.elapsedMs/1e3)}s (${Q(x)}); refusing to capture the boot/loading card. retry once the app paints.`)}let l=r,c=r?.__matcher;if(c){let f=await n.send({type:"evaluate",code:te(c)});if(!f)throw new Error(c.id?`no node with id "${c.id}"`:`no node matching text "${c.text}"`);l=f}let u=l?{x:l.x,y:l.y,w:l.w,h:l.h}:void 0,d={type:"screenshot"};o&&(d.layers=o),u&&(d.crop=u);let m=await n.send(d);if(typeof m!="string"||m.length<="data:image/png;base64,".length)throw new Error("screenshot capture returned no image data \u2014 the sim is connected but not painting; check `rnx get errors`");let w=m.replace(/^data:image\/png;base64,/,"");u&&console.log(` area: x=${u.x} y=${u.y} w=${u.w} h=${u.h}`),o&&console.log(` layers: ${o}`);let p=pe(process.cwd(),t||"/tmp/sootsim-inspect.png");ue(de(p),{recursive:!0});let y=Buffer.from(w,"base64");if(i){let f=await ze(n);if(!f)throw new Error("could not read current device model from the target sim");let x=await se(y,f);le(p,x),console.log(` frame: ${f}`)}else le(p,y);console.log(` saved: ${p}`)}finally{n.close()}}async function ze(e){let t=await N(e,"SootSim.bridges.settings.get")??{deviceModel:null},r=typeof t.deviceModel=="string"?t.deviceModel:null;return!r||!(r in R)?null:r}function _e(e){let t=!1;for(let r of e)r==="--with-frame"&&(t=!0),r==="--no-frame"&&(t=!1);return t}function je(e){if(e.includes("--shell-only"))return"shell";if(e.includes("--no-shell"))return"tenant"}var Ie=new Set(["--help","-h","--verbose","-v","--with-frame","--no-frame","--no-shell","--shell-only","--allow-loading","--gallery","--themes"]),he=new Set(["--output","--area","--id","--text","--url","--port","--timeout","--sim"]),Le={"--rect":"--area","--crop":"--area","--region":"--area","--bounds":"--area","--box":"--area","--xywh":"--area","--out":"--output","-o":"--output","--file":"--output","--path":"--output","--testid":"--id","--test-id":"--id","--test_id":"--id"};function Ue(e){let t=[],r={"-o":"--output","--out":"--output","--file":"--output","--path":"--output","--testid":"--id","--test-id":"--id","--test_id":"--id","--rect":"--area","--crop":"--area","--region":"--area","--bounds":"--area","--box":"--area","--xywh":"--area"};for(let i of e){let o=r[i];t.push(o??i)}return t}function He(e){let t=[];for(let r=0;r<e.length;r++){let i=e[r];if(i.startsWith("-")){if(he.has(i)){r++;continue}Ie.has(i)||t.push({flag:i,suggestion:Le[i]})}}return t}function fe(e){let t=e.find((r,i)=>e[i-1]==="--output");if(t)return t;for(let r=0;r<e.length;r++){let i=e[r];if(i.startsWith("-")){he.has(i)&&r++;continue}return i}}async function ce(e,t){let r=fe(e)||"sootsim-screenshot.png",i=e.includes("--gallery"),o=e.find((a,l)=>e[l-1]==="--url")||`http://localhost:${t.port||5173}`,{chromium:s}=await import("playwright"),n=await C(a=>s.launch(a),{headless:!0});try{if(i)console.log(" capturing gallery..."),console.log(" (gallery mode not yet implemented)");else{let a=await n.newPage({viewport:{width:500,height:900}});await a.goto(o,{waitUntil:"networkidle"}),await a.waitForTimeout(2e3);let l=pe(process.cwd(),r);ue(de(l),{recursive:!0}),await a.screenshot({path:l}),console.log(` saved: ${l}`),await a.close()}}catch(a){console.error(` screenshot failed: ${a.message}`),process.exitCode=1}finally{await n.close()}}import{mkdirSync as K,readFileSync as tt,writeFileSync as Ne}from"fs";import{tmpdir as or}from"os";import _ from"path";import{mkdirSync as we,readFileSync as Ye,writeFileSync as ye}from"fs";import Y from"path";function ge(e){return e.replace(/\\/g,"/").replace(/\.png$/i,"").trim().split("/").filter(Boolean).join("--").replace(/[^A-Za-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"slide"}function G(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}function Ge(e){let t=e.trim();return/^#[0-9a-f]{3}$/i.test(t)?`#${t[1]}${t[1]}${t[2]}${t[2]}${t[3]}${t[3]}`:t}function T(e,t){let r=Ge(e),i=Math.max(0,Math.min(1,t)),o=r.match(/^#([0-9a-f]{6})$/i);if(o){let s=o[1],n=Number.parseInt(s.slice(0,2),16),a=Number.parseInt(s.slice(2,4),16),l=Number.parseInt(s.slice(4,6),16);return`rgba(${n}, ${a}, ${l}, ${i})`}return r.startsWith("rgb(")?r.replace(/^rgb\((.+)\)$/i,`rgba($1, ${i})`):r}function We(e){if(e.type==="solid")return e.color||"#000000";let t=e.direction??180,r=e.stops?.map(i=>`${i.color} ${i.offset}%`).join(", ")||"#000000 0%, #111111 100%";return`linear-gradient(${t}deg, ${r})`}function qe(e){let t=e.glow;return t?`radial-gradient(circle at 50% 24%, ${T(t,.46)} 0%, ${T(t,.2)} 24%, rgba(0,0,0,0) 62%)`:"none"}function W(e){switch(e){case"tilted-left":return"perspective(2400px) rotateY(10deg) rotateZ(-2deg)";case"tilted-right":return"perspective(2400px) rotateY(-10deg) rotateZ(2deg)";case"cut-bottom":return"translateY(10%) scale(1.08)";case"cut-top":return"translateY(-16%) scale(1.08)";case"straight":default:return"none"}}function be(...e){let t=e.map(r=>r.trim()).filter(r=>r.length>0&&r!=="none");return t.length>0?t.join(" "):"none"}function q(e){let t=T(e.color,e.opacity),r=T(e.color,e.opacity*.52);return`drop-shadow(0 34px ${e.spread}px ${r}) drop-shadow(0 0 ${e.blur}px ${t})`}function V(e,t){return!t.glow||e.color&&e.color!==$.color?e:{...e,color:t.glow}}function Ve(e,t){if(e==="editorial-left"){let o=Math.round(t.height*.056),s=Math.round(t.height*.022);return{copyStyle:["position:absolute",`top:${Math.round(t.height*.12)}px`,`left:${Math.round(t.width*.085)}px`,`width:${Math.round(t.width*.42)}px`,"text-align:left","z-index:3"].join(";"),titleStyle:`font-size:${o}px;line-height:0.92;`,subStyle:`font-size:${s}px;line-height:1.36;max-width:${Math.round(t.width*.36)}px;`,eyebrowStyle:"align-items:flex-start;",deviceStyle:(n,a,l)=>{let c=Math.round(t.width*.68*(a.scale??n.scale)),u=Math.round(t.height*(.03+n.offsetY+(a.offsetY??0))),d=a.pose||n.pose||"tilted-right",m=V(n.shadow,l);return["position:absolute",`top:${Math.round(t.height*.18)+u}px`,`left:${Math.round(t.width*.56)}px`,`width:${c}px`,"transform-origin:center center",`transform:${W(d)}`,`filter:${q(m)}`,"z-index:2"].join(";")}}}if(e==="minimal-bottom"){let o=Math.round(t.height*.041),s=Math.round(t.height*.019);return{copyStyle:["position:absolute",`left:${Math.round(t.width*.12)}px`,`right:${Math.round(t.width*.12)}px`,`bottom:${Math.round(t.height*.085)}px`,"text-align:center","z-index:3"].join(";"),titleStyle:`font-size:${o}px;line-height:0.96;`,subStyle:`font-size:${s}px;line-height:1.34;max-width:${Math.round(t.width*.56)}px;margin:0 auto;`,eyebrowStyle:"align-items:center;",deviceStyle:(n,a,l)=>{let c=Math.round(t.width*.84*(a.scale??n.scale)),u=Math.round(t.height*(n.offsetY+(a.offsetY??0))),d=a.pose||n.pose,m=V(n.shadow,l);return["position:absolute",`top:${Math.round(t.height*.075)+u}px`,"left:50%",`width:${c}px`,"transform-origin:center center",`transform:${be("translateX(-50%)",W(d))}`,`filter:${q(m)}`,"z-index:2"].join(";")}}}let r=Math.round(t.height*.04),i=Math.round(t.height*.019);return{copyStyle:["position:absolute",`top:${Math.round(t.height*.084)}px`,"left:50%",`width:${Math.round(t.width*.88)}px`,"transform:translateX(-50%)","text-align:center","z-index:3"].join(";"),titleStyle:`font-size:${r}px;line-height:0.92;`,subStyle:`font-size:${i}px;line-height:1.34;max-width:${Math.round(t.width*.6)}px;margin:0 auto;`,eyebrowStyle:"align-items:center;",deviceStyle:(o,s,n)=>{let a=Math.round(t.width*.64*(s.scale??o.scale)),l=Math.round(t.height*(o.offsetY+(s.offsetY??0))),c=s.pose||o.pose,u=V(o.shadow,n);return["position:absolute",`top:${Math.round(t.height*.28)+l}px`,"left:50%",`width:${a}px`,"transform-origin:center top",`transform:${be("translateX(-50%)",W(c))}`,`filter:${q(u)}`,"z-index:2"].join(";")}}}function Ke({canvas:e,compose:t,slide:r,imageDataUrl:i}){let o=r.background??t.background,s=Ve(t.text.preset,e),n=t.text.preset!=="none"&&!!(r.eyebrow||r.headline||r.subheadline),a=s.deviceStyle(t.frame,r,o),l=r.headline?`<div style="font-weight:780;white-space:pre-line;text-wrap:balance;${s.titleStyle}">${G(r.headline)}</div>`:"",c=r.subheadline?`<div style="margin-top:${Math.round(e.height*.018)}px;color:${t.text.subColor};white-space:pre-line;text-wrap:balance;${s.subStyle}">${G(r.subheadline)}</div>`:"",u=r.eyebrow?`<div style="display:flex;${s.eyebrowStyle}margin-bottom:${Math.round(e.height*.016)}px;"><div style="font-size:${Math.round(e.height*.014)}px;font-weight:700;letter-spacing:0.18em;text-transform:uppercase;color:${t.text.eyebrowColor};">${G(r.eyebrow)}</div></div>`:"";return`<!doctype html>
43
- <html>
44
- <head>
45
- <meta charset="utf-8" />
46
- <style>
47
- html, body {
48
- margin: 0;
49
- width: ${e.width}px;
50
- height: ${e.height}px;
51
- overflow: hidden;
52
- background: transparent;
53
- }
54
- body {
55
- font-family: "SF Pro Display", "Helvetica Neue", system-ui, sans-serif;
56
- }
57
- #canvas {
58
- position: relative;
59
- width: ${e.width}px;
60
- height: ${e.height}px;
61
- overflow: hidden;
62
- background: ${We(o)};
63
- color: ${t.text.color};
64
- isolation: isolate;
65
- }
66
- #glow {
67
- position: absolute;
68
- inset: 0;
69
- background: ${qe(o)};
70
- pointer-events: none;
71
- }
72
- #copy {
73
- ${s.copyStyle};
74
- }
75
- #device {
76
- ${a};
77
- }
78
- #device img {
79
- display: block;
80
- width: 100%;
81
- height: auto;
82
- }
83
- </style>
84
- </head>
85
- <body>
86
- <div id="canvas">
87
- <div id="glow"></div>
88
- ${n?`<div id="copy">${u}${l}${c}</div>`:""}
89
- <div id="device"><img src="${i}" alt="" /></div>
90
- </div>
91
- </body>
92
- </html>`}async function Se(e,t,r){let i=[],o=r??await(async()=>{let{chromium:n}=await import("playwright");return C(a=>n.launch(a),{headless:!0})})();try{let n=await o.newContext({viewport:{width:1280,height:720}});try{for(let a of t){let c=`data:image/png;base64,${Ye(a.imagePath).toString("base64")}`;for(let u of e.canvases){let d=await n.newPage();try{await d.setViewportSize({width:u.width,height:u.height}),await d.setContent(Ke({canvas:u,compose:e,slide:a.slide,imageDataUrl:c}),{waitUntil:"load"}),await d.waitForTimeout(20);let m=Y.join(e.outDir,u.name,e.locale);we(m,{recursive:!0});let h=Y.join(m,`${a.slide.assetKey}.png`),w=await d.screenshot({type:"png",clip:{x:0,y:0,width:u.width,height:u.height}});ye(h,w),i.push({slideId:a.slide.id,canvas:u.name,locale:e.locale,outputPath:h,sourcePath:a.imagePath})}finally{await d.close()}}}}finally{await n.close()}}finally{!r&&typeof o.close=="function"&&await o.close()}we(e.outDir,{recursive:!0});let s=Y.join(e.outDir,"manifest.json");return ye(s,JSON.stringify({generatedAt:new Date().toISOString(),locale:e.locale,outputs:i},null,2)),{manifestPath:s,outputs:i.map(n=>n.outputPath),entries:i}}function D(e){return typeof e=="string"&&e.length>0?e:void 0}async function xe(e){let r=`${e.origin.replace(/\/$/,"")}/api/sootsim/screenshot-decks?org=${encodeURIComponent(e.org)}&repo=${encodeURIComponent(e.repo)}`,i=await fetch(r,{headers:{Authorization:e.authHeader}});if(!i.ok){let a=await i.text().catch(()=>"");throw new Error(`org deck fetch failed (${i.status} ${i.statusText}) for ${e.org}/${e.repo}${a?` \u2014 ${a.slice(0,200)}`:""}`)}let s=(await i.json()).decks?.[0];if(!s||!s.config||typeof s.config!="object")return null;let n=s.config.frames;return Array.isArray(n)?n.map(a=>{let l=a??{};return{title:D(l.title),subtitle:D(l.subtitle),bgTop:D(l.bgTop),bgBottom:D(l.bgBottom),titleColor:D(l.titleColor)}}):null}function Pe(e,t){e.compose.slides.forEach((r,i)=>{let o=t[i];if(o&&(o.title!=null&&(r.headline=o.title),o.subtitle!=null&&(r.subheadline=o.subtitle),o.bgTop&&o.bgBottom)){let s={type:"gradient",direction:180,stops:[{offset:0,color:L(o.bgTop)},{offset:1,color:L(o.bgBottom)}]};r.background=s}})}import{readFileSync as Xe}from"fs";import v from"path";function g(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function z(e,t){if(!g(e))throw new Error(`${t} must be an object`);return e}function b(e,t=""){return typeof e=="string"?e:t}function S(e){return typeof e=="string"&&e.trim().length>0?e.trim():null}function $e(e,t){return typeof e=="boolean"?e:t}function M(e,t){return typeof e=="number"&&Number.isFinite(e)?e:t}function ke(e,t){return typeof e!="string"||e.trim().length===0?t:Object.prototype.hasOwnProperty.call(R,e)?e:null}function ve(e,t){if(typeof e=="string"){let o=A(e);if(!o)throw new Error(`unknown background preset: ${e}`);return o}if(!g(e))return H(t);let r=e.type==="solid"||e.type==="gradient"?e.type:t.type;if(r==="solid")return{type:r,color:b(e.color,t.color??"#000000"),glow:S(e.glow)??t.glow};let i=Array.isArray(e.stops)?e.stops.map(o=>{if(!g(o))return null;let s=M(o.offset,Number.NaN),n=S(o.color);return!Number.isFinite(s)||!n?null:{offset:s,color:n}}).filter(o=>!!o):t.stops?.map(o=>({...o}))??[];return{type:r,direction:M(e.direction,t.direction??180),glow:S(e.glow)??t.glow,stops:i.length>0?i:t.stops?.map(o=>({...o}))??[]}}function Je(e){let r=(Array.isArray(e)?e:["iphone-6-9"]).map(i=>{if(typeof i!="string")throw new Error("compose.canvases entries must be strings");let o=U(i);if(!o)throw new Error(`unknown canvas preset: ${i}`);return o});if(r.length===0)throw new Error("compose.canvases must include at least one preset");return r}function Ze(e){switch(e){case"editorial-left":case"minimal-bottom":case"none":case"bold-top":return e;default:return"bold-top"}}function Qe(e){switch(e){case"plan":case"flow":case"auto":return e;default:return"auto"}}function Ce(e){switch(e){case"straight":case"tilted-left":case"tilted-right":case"cut-bottom":case"cut-top":return e;default:return}}function E(e,t){return v.isAbsolute(t)?t:v.resolve(e,t)}function et(e,t){if(!Array.isArray(e)||e.length===0)throw new Error("compose.slides must be a non-empty array");return e.map((r,i)=>{let o=z(r,`compose.slides[${i}]`),s=S(o.screenshot);if(!s)throw new Error(`compose.slides[${i}].screenshot is required`);let n=S(o.id),a=ge(n||s);return{id:n||a,assetKey:a,screenshot:s,headline:b(o.headline),subheadline:b(o.subheadline),eyebrow:b(o.eyebrow),pose:Ce(o.pose),scale:typeof o.scale=="number"&&Number.isFinite(o.scale)?o.scale:void 0,offsetY:typeof o.offsetY=="number"&&Number.isFinite(o.offsetY)?o.offsetY:void 0,background:o.theme||o.background?ve(o.theme??o.background,t):void 0}})}function Me(e){let t=v.resolve(e),r=v.dirname(t),i=re.parse(Xe(t,"utf8")),o=z(i,"screenshots plan"),s=z(o.capture??{},"capture"),n=z(o.compose??{},"compose"),a=typeof o.app=="number"?String(o.app):S(o.app),l=ke(o.device,null),c=E(r,b(s.out,v.join(".sootsim","screenshots","capture"))),u=S(s.from),d=S(s.flow);if(!u&&!d)throw new Error("capture.flow or capture.from is required");let m=s.mode==="raw"||s.mode==="framed"||s.mode==="raw+framed"?s.mode:"raw+framed",h=Qe(s.pathMode),w=ve(n.background??"cyan",A("cyan")),p={show:$e(n.frame&&g(n.frame)?n.frame.show:void 0,!0),style:ke(n.frame&&g(n.frame)?n.frame.style:null,null)||l||I,pose:Ce(n.frame&&g(n.frame)?n.frame.pose:void 0)||"straight",scale:M(n.frame&&g(n.frame)?n.frame.scale:void 0,1),offsetY:M(n.frame&&g(n.frame)?n.frame.offsetY:void 0,0),shadow:{color:b(n.frame&&g(n.frame)&&g(n.frame.shadow)?n.frame.shadow.color:void 0,$.color),blur:M(n.frame&&g(n.frame)&&g(n.frame.shadow)?n.frame.shadow.blur:void 0,$.blur),spread:M(n.frame&&g(n.frame)&&g(n.frame.shadow)?n.frame.shadow.spread:void 0,$.spread),opacity:M(n.frame&&g(n.frame)&&g(n.frame.shadow)?n.frame.shadow.opacity:void 0,$.opacity)}};return{planPath:t,planDir:r,appTarget:a,deviceModel:l,capture:{flowPath:d?E(r,d):null,fromDir:u?E(r,u):null,outDir:c,rawDir:u?E(r,u):v.join(c,"raw"),framedDir:v.join(c,"framed"),mode:m,pathMode:h,simId:S(s.sim),openInNewSim:$e(s.new,!1)},compose:{outDir:E(r,b(n.out,v.join(".sootsim","screenshots","exports"))),locale:b(n.locale,"en"),canvases:Je(n.canvases),frame:p,background:w,text:{preset:Ze(n.text&&g(n.text)?n.text.preset:void 0),color:b(n.text&&g(n.text)?n.text.color:void 0,"#ffffff"),subColor:b(n.text&&g(n.text)?n.text.subColor:void 0,"rgba(232,240,247,0.86)"),eyebrowColor:b(n.text&&g(n.text)?n.text.eyebrowColor:void 0,"rgba(229,242,255,0.72)")},slides:et(n.slides,w)}}}function rt(e){return e.capture.pathMode==="plan"||e.capture.pathMode==="flow"?e.capture.pathMode:e.capture.fromDir?"flow":"plan"}function ot(e,t){if(!e.capture.flowPath)throw new Error("capture flow path is required to build flow args");let r=[e.capture.flowPath],i=rt(e);r.push("--screenshots",e.capture.rawDir),i==="flow"&&r.push("--screenshot-paths","flow");let o=t.appTarget??e.appTarget,s=t.deviceModel??e.deviceModel,n=t.simId??e.capture.simId;return o&&r.push("--url",o),s&&r.push("--device",s),n&&r.push("--sim",n),e.capture.openInNewSim&&r.push("--new"),r}function j(e,t){return _.isAbsolute(t)?t:_.join(e.capture.rawDir,t)}function nt(e){return _.join(e.capture.outDir,"manifest.json")}async function st(e,t){if(!e.capture.flowPath)return;K(e.capture.rawDir,{recursive:!0});let r=ot(e,t),i=await ae(r);if(i!==0)throw new Error(`flow capture failed with exit code ${i}`)}async function it(e,t){let r=new Map;K(e.capture.framedDir,{recursive:!0});let i=t??await(async()=>{let{chromium:o}=await import("playwright");return C(s=>o.launch(s),{headless:!0})})();try{let o=await ie(i);try{for(let s of e.compose.slides){let n=j(e,s.screenshot),a=tt(n),l=await o.compose(a,e.compose.frame.style),c=_.join(e.capture.framedDir,`${s.assetKey}.png`);Ne(c,l),r.set(s.id,c)}}finally{await o.close()}}finally{!t&&typeof i.close=="function"&&await i.close()}return r}function at(e,t,r){let i=[],o=[];K(e.capture.outDir,{recursive:!0});let s=nt(e),n=e.compose.slides.map(a=>{let l=j(e,a.screenshot),c=t.get(a.id)??null;return i.push(l),c&&o.push(c),{id:a.id,screenshot:a.screenshot,rawPath:l,framedPath:c}});return Ne(s,JSON.stringify({generatedAt:new Date().toISOString(),deviceModel:r,mode:e.capture.mode,rawDir:e.capture.rawDir,framedDir:e.capture.framedDir,slides:n},null,2)),{manifestPath:s,rawDir:e.capture.rawDir,framedDir:e.capture.framedDir,rawFiles:i,framedFiles:o}}function lt(e,t){return e.compose.slides.map(r=>({slide:r,imagePath:e.compose.frame.show===!0?t.get(r.id)??j(e,r.screenshot):j(e,r.screenshot)}))}async function De(e,t={}){let r=Me(e);t.orgDeckFrames&&t.orgDeckFrames.length>0&&Pe(r,t.orgDeckFrames);let i=t.deviceModel??r.deviceModel;t.composeOnly||await st(r,t);let o=r.capture.mode!=="raw"||!t.captureOnly&&r.compose.frame.show===!0,n=o||!t.captureOnly?await(async()=>{let{chromium:a}=await import("playwright");return C(l=>a.launch(l),{headless:!0})})():null;try{let a=o?await it(r,n??void 0):new Map,l=at(r,a,i);if(t.captureOnly)return{plan:r,capture:l,compose:null};let c=lt(r,a),u=await Se(r.compose,c,n??void 0);return{plan:r,capture:l,compose:u}}finally{n&&typeof n.close=="function"&&await n.close()}}async function Ee(e,t={}){if(e.includes("--help")||e.includes("-h"))return console.log(`
93
- rnx screenshot appstore \u2014 capture and compose app-store screenshot sets
94
-
95
- usage:
96
- rnx screenshot appstore --plan <plan.yaml> [options]
97
-
98
- options:
99
- --plan <path> screenshot plan yaml
100
- --sim <id> reuse a live sim for capture
101
- --app <target> override plan app target (port, url, or shell url)
102
- --device <model> override plan capture device for this run
103
- --org <slug> overlay copy/colors from a team's org screenshot deck
104
- --repo <name> repo for --org (both required to pull the org deck)
105
- --api-origin <url> contrast origin to fetch the deck from (default: the
106
- signed-in session origin, else https://contrast.dev)
107
- --capture-only stop after raw/framed intermediates
108
- --compose-only skip flow capture, reuse existing raw screenshots
109
-
110
- examples:
111
- rnx screenshot appstore --plan app-store.yaml
112
- rnx screenshot appstore --plan app-store.yaml --sim a9
113
- rnx screenshot appstore --plan app-store.yaml --org acme --repo mobile
114
- `),0;let r=p=>e.find((y,f)=>e[f-1]===p),i=new Set(["--plan","--sim","--app","--device","--org","--repo","--api-origin"]),o=e.filter((p,y)=>{if(p.startsWith("-"))return!1;let f=e[y-1];return!f||!i.has(f)}),s=r("--plan")||o[0];if(!s)return console.error(" error: screenshots plan path is required (`--plan <path>`)."),1;let n=r("--sim")?.trim()||null,a=r("--app")?.trim()||null,l=r("--device")?.trim()||null,c=r("--org")?.trim()||null,u=r("--repo")?.trim()||null,d=r("--api-origin")?.trim()||null,m=e.includes("--capture-only"),h=e.includes("--compose-only"),w=null;if(c||u){if(!c||!u)return console.error(" error: --org and --repo must be passed together."),1;let p=oe();if(!p)return console.error(" error: --org/--repo needs auth \u2014 run `rnx login` or set RNX_API_KEY."),1;let y=d||(p.kind==="session"?p.origin:"https://contrast.dev");try{w=await xe({org:c,repo:u,origin:y,authHeader:ne(p)})}catch(f){return console.error(` ${f.message}`),1}!w||w.length===0?console.log(` note: no org deck for ${c}/${u} \u2014 composing with the plan's copy/colors.`):console.log(` org deck: ${w.length} slide(s) from ${c}/${u}`)}try{let p=await De(s,{simId:n,appTarget:a,deviceModel:l,captureOnly:m,composeOnly:h,orgDeckFrames:w});return console.log(` capture manifest: ${p.capture.manifestPath}`),p.compose?(console.log(` compose manifest: ${p.compose.manifestPath}`),console.log(` exports: ${p.compose.outputs.length}`)):console.log(" compose skipped"),0}catch(p){return console.error(` screenshots failed: ${p.message}`),1}}import{mkdirSync as ct,writeFileSync as ut}from"node:fs";import{dirname as dt,isAbsolute as pt,join as Fe,resolve as mt}from"node:path";var ht=`
115
- rnx screenshot slides \u2014 compose App Store screenshots in the live sim
116
-
117
- usage:
118
- rnx screenshot slides list slides (alias: slides list)
119
- rnx screenshot slides set <n> [fields] patch a slide's copy/colors
120
- rnx screenshot slides add append a blank slide (focuses it)
121
- rnx screenshot slides remove <n> delete a slide
122
- rnx screenshot slides move <n> <position> reorder a slide
123
- rnx screenshot slides focus <n> open a slide (device flips into it)
124
- rnx screenshot slides overview back to the gallery
125
- rnx screenshot slides capture [<n>] capture the live frame into a slide
126
- rnx screenshot slides export [<n>] [--out <dir>]
127
- write composed App-Store PNG(s)
128
-
129
- set fields:
130
- --title <text> headline
131
- --subtitle <text> subheadline
132
- --bg-top <color> gradient top (white gray slate black cyan blue
133
- --bg-bottom <color> gradient bottom teal green lime gold orange coral
134
- --title-color <color> copy color override, or "auto" red berry magenta
135
- --title-size <s> sm | md | lg | none gray-light
136
- --underlay <s> black | white | none gray-dark \u2026)
137
- --shadow <s> light | medium | strong | none
138
-
139
- notes:
140
- slides are addressed by 1-based index. screenshot mode is auto-enabled.
141
- capture shoots whatever the sim currently shows \u2014 navigate the app first
142
- (rnx do tap-id \u2026), then capture.
143
-
144
- examples:
145
- rnx screenshot slides set 1 --title "The ultimate MMA community." --bg-top cyan --bg-bottom black
146
- rnx screenshot slides capture 1
147
- rnx screenshot slides export --out ./exports
148
- `,ft={"--title":"title","--subtitle":"subtitle","--bg-top":"bgTop","--bg-bottom":"bgBottom","--title-color":"titleColor","--title-size":"titleSize","--underlay":"titleUnderlay","--shadow":"titleShadow","--out":"out"};function gt(e){let t=[],r={},i=!1;for(let o=0;o<e.length;o+=1){let s=e[o];if(s==="--json"){i=!0;continue}let n=s.indexOf("="),a=n>0?s.slice(0,n):s,l=ft[a];if(l){n>0?r[l]=s.slice(n+1):(r[l]=e[o+1]??"",o+=1);continue}t.push(s)}return{rest:t,values:r,json:i}}function wt(e){if(!e)return;let t=Number(e);return Number.isInteger(t)&&t>0?t:e}async function yt(e){if(!await Z(e,`(async () => {
149
- if (window.SootSim?.bridges?.settings?.get?.()?.screenshotMode) return true
150
- window.dispatchEvent(new CustomEvent('sootsim:shell-command', {
151
- detail: { type: 'fire-action', id: 'toggle-screenshot-mode' },
152
- }))
153
- const deadline = Date.now() + 2000
154
- while (Date.now() < deadline) {
155
- if (window.SootSim?.bridges?.settings?.get?.()?.screenshotMode) return true
156
- await new Promise((r) => setTimeout(r, 40))
157
- }
158
- return false
159
- })()`,{acquireLock:!0}))throw new Error("could not enable screenshot mode \u2014 is this a browser sim with the screenshot plugin? (electron has no screenshot mode)")}function bt(e){console.log(` ${e.projectName} \u2014 canvas ${e.canvas} (${e.exportSize.width}\xD7${e.exportSize.height}), device ${e.deviceModel}`);for(let t of e.slides){let r=[t.focused?"focused":t.active?"active":null,t.captured?`captured${t.captureMode==="3d"?" 3d":""}`:"no capture"].filter(Boolean).join(", "),i=t.title?`"${t.title}"`:"(no title)",o=[`bg ${t.bgTop}\u2192${t.bgBottom}`,t.titleColor?`color ${t.titleColor}`:null,t.titleSize?`size ${t.titleSize}`:null,t.titleUnderlay?`underlay ${t.titleUnderlay}`:null,t.titleShadow?`shadow ${t.titleShadow}`:null].filter(Boolean).join(", ");console.log(` ${t.index}. ${i} \u2014 ${o} [${r}]`),t.subtitle&&console.log(` ${t.subtitle}`)}}function St(e){let t=e.match(/^data:[^;]+;base64,(.+)$/);if(!t)throw new Error("export returned an unexpected payload");return Buffer.from(t[1],"base64")}async function Re(e,t){if(e.includes("--help")||e.includes("-h")){console.log(ht);return}let{rest:r,values:i,json:o}=gt(e),s=B(r,{port:t.port,commandTimeoutMs:45e3}),n=s.positional[0]??"list",a=wt(s.positional[1]),l=Number(s.positional[2]),c=O(s),u=d=>{o?console.log(JSON.stringify(d,null,2)):bt(d)};try{switch(await yt(c),n){case"list":{u(await N(c,"SootSim.bridges.screenshotControl.getState"));break}case"set":{if(a==null)throw new Error("usage: rnx screenshot slides set <n> --title \u2026 (see --help)");let d={};for(let m of["title","subtitle","bgTop","bgBottom","titleColor","titleSize","titleUnderlay","titleShadow"])i[m]!=null&&(d[m]=i[m]);if(Object.keys(d).length===0)throw new Error("nothing to set \u2014 pass at least one field (see --help)");u(await k(c,"SootSim.bridges.screenshotControl.setSlide",a,d));break}case"add":{u(await k(c,"SootSim.bridges.screenshotControl.addSlide"));break}case"remove":{if(a==null)throw new Error("usage: rnx screenshot slides remove <n>");u(await k(c,"SootSim.bridges.screenshotControl.removeSlide",a));break}case"move":{if(a==null||!Number.isInteger(l)||l<1)throw new Error("usage: rnx screenshot slides move <n> <position>");u(await k(c,"SootSim.bridges.screenshotControl.moveSlide",a,l));break}case"focus":{if(a==null)throw new Error("usage: rnx screenshot slides focus <n>");u(await k(c,"SootSim.bridges.screenshotControl.focusSlide",a));break}case"overview":{u(await k(c,"SootSim.bridges.screenshotControl.overview"));break}case"capture":{let d=await k(c,"SootSim.bridges.screenshotControl.capture",...a!=null?[a]:[]);u(d);break}case"export":{let d=i.out?pt(i.out)?i.out:mt(process.cwd(),i.out):Fe(process.cwd(),"screenshots"),m=await N(c,"SootSim.bridges.screenshotControl.getState"),h=a!=null?m.slides.filter(p=>p.index===a||p.id===a):m.slides.filter(p=>p.captured);if(h.length===0)throw new Error(a!=null?`no slide ${a}`:"no captured slides to export \u2014 capture first");let w=[];for(let p of h){let y=await N(c,"SootSim.bridges.screenshotControl.exportSlide",p.index),f=Fe(d,y.name);ct(dt(f),{recursive:!0}),ut(f,St(y.dataUrl)),w.push(f),console.log(` saved: ${f} (${y.width}\xD7${y.height})`)}o&&console.log(JSON.stringify({written:w},null,2));break}default:throw new Error(`unknown subcommand "${n}" \u2014 see rnx screenshot slides --help`)}}finally{c.close()}}var xt=new Set(["--sim","--port","-p","--output","-o","--out","--plan","--app","--device","--org","--repo","--api-origin","--branch","--platform","--slides","--shell-origin","--timeout","--ids","--out-dir"]),Pt=new Set(["capture","slides","appstore","layers"]);function Ae(e){for(let t=0;t<e.length;t++){let r=e[t];if(r.startsWith("-")){xt.has(r)&&t++;continue}return Pt.has(r)?{name:r,index:t}:null}return null}function F(e,t){return[...e.slice(0,t),...e.slice(t+1)]}function $t(e){let t=[];for(let r=0;r<e.length;r++){if(e[r]==="--sim"){r++;continue}t.push(e[r])}return t}async function $r(e,t){let r=Ae(e);if(!r||r.name==="capture"){await me(r?F(e,r.index):e,t);return}if(r.name==="slides"){await Re(F(e,r.index),t);return}if(r.name==="layers"){let{runScreenshotLayers:s}=await import("./screenshot-layers-VMTQ6XIU.js");await s(F(e,r.index),t);return}let i=F(e,r.index),o=Ae(i);if(o?.name==="capture")try{let{runScreenshotsCapture:s}=await import("./screenshots-capture-K3MWIDBK.js");return await s($t(F(i,o.index)))}catch(s){if(s instanceof Error&&"code"in s&&s.code==="ERR_MODULE_NOT_FOUND")return console.error("screenshot appstore capture is repo-only tooling \u2014 it needs the rnx workspace (playwright is not shipped with the published CLI)."),1;throw s}return Ee(i,t)}export{$r as runScreenshotCommand};
@@ -1,20 +0,0 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{a as R}from"./chunk-CCMZSHJB.js";import"./chunk-PNRJFHX4.js";import{o as I,p as S}from"./chunk-5PZVYF42.js";import"./chunk-FZDRA7Z4.js";import"./chunk-TPPVAA7Z.js";import{b as f}from"./chunk-H4MYYOYJ.js";import{t as v,v as w,y as b}from"./chunk-7NCPUZ52.js";import"./chunk-GEX7RVHY.js";import{c}from"./chunk-T4ISWS5H.js";import"./chunk-HWDVC6YA.js";import"./chunk-DQUPSAHE.js";import{mkdirSync as F,writeFileSync as $}from"fs";import{resolve as u}from"path";async function _(s,D){(s.includes("--help")||s.includes("-h"))&&(console.log(`
5
- sootsim screenshot layers \u2014 export a screen as engine-isolated layers
6
-
7
- usage:
8
- sootsim screenshot layers --ids <testID,testID,...> [--out-dir <dir>]
9
-
10
- options:
11
- --ids <list> comma-separated testIDs of the subtrees to isolate
12
- --out-dir <dir> output directory (default: /tmp/sootsim-layers)
13
-
14
- output:
15
- full.png the normal frame
16
- base.png the frame re-rendered with every listed subtree hidden
17
- layer-<id>.png each subtree rendered alone on a transparent frame
18
- manifest.json device metrics + logical rect per layer
19
- `),c(0));let g=s.find((r,n)=>s[n-1]==="--ids");g||(console.error(" --ids is required (comma-separated testIDs)"),c(1));let i=g.split(",").map(r=>r.trim()).filter(Boolean);i.length===0&&(console.error(" --ids resolved to an empty list"),c(1));let M=s.find((r,n)=>s[n-1]==="--out-dir"),l=u(process.cwd(),M??"/tmp/sootsim-layers"),B=v(s,{port:D.port,stripValueFlags:["--ids","--out-dir"]}),o=w({...B,commandTimeoutMs:45e3});try{let r=await S(o,9e4);if(!r.ready)throw new Error(`app not ready to capture after ${Math.round(r.elapsedMs/1e3)}s (${I(r)})`);let n={};for(let e of i){let t=await o.send({type:"evaluate",code:R({id:e})});if(!t)throw new Error(`no node with testID "${e}"`);n[e]=t}F(l,{recursive:!0});let p=(e,t)=>{if(typeof t!="string"||!t.startsWith("data:image/png;base64,"))throw new Error(`capture for ${e} returned no image data`);let y=u(l,e);$(y,Buffer.from(t.slice(22),"base64")),console.log(` saved: ${y}`)};p("full.png",await o.send({type:"screenshot"})),p("base.png",await o.send({type:"screenshot",captureFilter:{hideTestIds:i}}));for(let e of i)p(`layer-${e}.png`,await o.send({type:"screenshot",captureFilter:{soloTestId:e,hideTestIds:i.filter(t=>t!==e)}}));let m=await b(o,"SootSim.bridges.settings.get")??{deviceModel:null},a=typeof m.deviceModel=="string"&&m.deviceModel in f?m.deviceModel:null,d=a?f[a]:null,E={device:d?{model:a,width:d.width,height:d.height,scale:d.scale}:{model:a},layers:i.map(e=>({testId:e,rect:n[e]}))},h=u(l,"manifest.json");$(h,`${JSON.stringify(E,null,2)}
20
- `),console.log(` saved: ${h}`)}finally{o.close()}}export{_ as runScreenshotLayers};
@@ -1,42 +0,0 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{b as I}from"./chunk-BQCEI37V.js";import{a as U}from"./chunk-HEV7BAPE.js";import"./chunk-JWUDT4QK.js";import{c as M}from"./chunk-T4ISWS5H.js";import"./chunk-HWDVC6YA.js";import{a as F}from"./chunk-25KRGDZT.js";import"./chunk-DQUPSAHE.js";import{execSync as A}from"node:child_process";import{mkdirSync as O,mkdtempSync as j,rmSync as G,writeFileSync as q}from"node:fs";import{tmpdir as B}from"node:os";import{dirname as L,isAbsolute as z,join as T,resolve as W}from"node:path";import{chromium as H}from"playwright-core";var J=`
5
- rnx screenshot appstore capture \u2014 headlessly capture + upload App Store slides
6
-
7
- usage:
8
- rnx screenshot appstore capture --org <org> --repo <repo> [options]
9
-
10
- what it does:
11
- boots the org-mode build shell in headless GPU Chrome (authed via your
12
- \`rnx login\` session), captures each slide through the live screenshot
13
- composer so every slide gets editable layers (captureRef + cleanRef +
14
- screenRef), and lets the shell auto-upload them to your org deck.
15
-
16
- options:
17
- --org <org> github org slug (required)
18
- --repo <repo> repo name (required)
19
- --branch <branch> build branch to boot (default: main)
20
- --platform <p> ios | android (default: ios)
21
- --slides <list> 1-based indices to capture, e.g. 1,3,5 (default: all)
22
- --dry-run capture + report refs, do NOT upload (PUTs aborted)
23
- --force allow overwriting a NON-EMPTY existing deck
24
- --out <dir> also write composed App-Store PNGs locally
25
- --shell-origin <url> origin serving /build (default: your session origin)
26
- --api-origin <url> origin for deck/capture APIs (default: session origin)
27
- --timeout <ms> per-step bridge timeout (default: 45000)
28
- --json machine-readable summary
29
-
30
- examples:
31
- rnx screenshot appstore capture --org acme --repo my-app --dry-run
32
- rnx screenshot appstore capture --org acme --repo my-app --branch main --force
33
- `;function Y(t){let e=a=>{let d=t.indexOf(a);return d>=0?t[d+1]:void 0},n=a=>t.includes(a),s=e("--slides"),i=s?s.split(",").map(a=>Number(a.trim())).filter(a=>Number.isInteger(a)&&a>0):null;return{org:e("--org")??"",repo:e("--repo")??"",branch:e("--branch")??"main",platform:e("--platform")??"ios",slides:i&&i.length?i:null,dryRun:n("--dry-run"),force:n("--force"),out:e("--out")??null,shellOrigin:e("--shell-origin")??null,apiOrigin:e("--api-origin")??null,timeoutMs:Number(e("--timeout")??"45000")||45e3,json:n("--json")}}function K(){let t=U();if(t&&t.kind==="session")return{token:t.token,origin:t.origin};process.stderr.write(`
34
- rnx screenshot appstore capture needs a logged-in session.
35
- run \`rnx login\` first (the captured slides upload to that account).
36
-
37
- `),M(1)}function Q(t){let e=t.replace(/^\[|\]$/g,"").toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="::1"||/^127(?:\.\d{1,3}){3}$/.test(e)}function V(t){let e=new URL(t);if(Q(e.hostname)){let s=(Number(e.port)||3e3)-3e3;if(s>=0&&s<=1e3)return e.port=String(5173+s),e.origin}return e.hostname==="contrast.dev"||e.hostname.endsWith(".contrast.dev")?F.origin:t}function X(){try{return Number(A("sysctl -n vm.loadavg").toString().trim().split(/\s+/)[1])||0}catch{return 0}}async function Z(t,e,n,s){let i=await fetch(`${t}/api/sootsim/screenshot-decks?org=${encodeURIComponent(n)}&repo=${encodeURIComponent(s)}`,{headers:{authorization:`Bearer ${e}`}});if(!i.ok)return[];let a=await i.json().catch(()=>null),d=[];for(let g of a?.decks??[])Array.isArray(g.config?.frames)&&d.push(...g.config.frames);return d}async function _(t,e,n,s){let i=await fetch(`${t}/api/sootsim/screenshot-capture?ref=${encodeURIComponent(s)}&org=${encodeURIComponent(n)}`,{headers:{authorization:`Bearer ${e}`}});return i.ok?`data:image/png;base64,${Buffer.from(await i.arrayBuffer()).toString("base64")}`:null}async function ee(t,e){let n=Date.now()+e;for(;Date.now()<n;){for(let s of t.frames())if(await s.evaluate(()=>!!globalThis.SootSim?.bridges?.screenshotControl).catch(()=>!1))return s;await t.waitForTimeout(250)}throw new Error("screenshot bridge never appeared \u2014 the build shell did not boot screenshot mode (check --branch/--platform has a ready build, and you are logged in)")}async function N(t){return t.evaluate(async()=>{let n=globalThis.SootSim?.bridges?.screenshotControl;if(!n)throw new Error("screenshotControl missing");return n.getState()})}async function te(t){await t.evaluate(async()=>{let e=globalThis.SootSim?.bridges?.settings,n=Date.now()+3e3;for(;Date.now()<n;){if(e?.get?.()?.screenshotMode)return;await new Promise(s=>setTimeout(s,50))}e&&!e.get().screenshotMode&&(e.set("screenshotMode",!0),e.get().showFrame||e.set("showFrame",!0))})}async function re(t,e,n,s){await t.evaluate(async([i,a,d])=>{let g=globalThis.SootSim,h=g?.bridges?.screenshotControl;if(!h)throw new Error("screenshotControl missing");let w=b=>new Promise(u=>setTimeout(u,b));if(a){h.focusSlide(i);let b=Date.now()+3e4,u=g?.bridges?.threeMode;for(;Date.now()<b&&(u=g?.bridges?.threeMode,!(u?.setScreenImage&&u?.captureStage&&u?.getState?.()?.ready));)await w(150);await w(1e3);let c=a;u?.configure?.({colorway:c.colorway,background:c.background,...c.customGradient?{customGradient:c.customGradient}:{},setting:c.setting,focus:c.focus,environment:c.environmentActive?{pins:c.environmentPins??{},effects:c.environmentEffects??{}}:!1,pose:{phonePosition:c.phonePosition,phoneRotation:c.phoneRotation,cameraPosition:c.cameraPosition,target:c.target}}),await w(900),d&&u?.setScreenImage&&(u.setScreenImage(d),await w(1200))}await h.capture(i)},[e,n,s])}async function me(t){if(t.includes("--help")||t.includes("-h"))return console.log(J),0;let e=Y(t);if(!e.org||!e.repo)return process.stderr.write(` --org and --repo are required (see --help)
38
- `),1;let{token:n,origin:s}=K(),i=(e.apiOrigin??s).replace(/\/$/,""),a=(e.shellOrigin??V(i)).replace(/\/$/,""),d=await Z(i,n,e.org,e.repo);if(!e.dryRun&&!e.force&&d.length>0)return process.stderr.write(` refusing to overwrite the existing ${e.org}/${e.repo} deck (${d.length} slide(s)). re-run with --force to replace it, or --dry-run to preview.
39
- `),1;let g=X(),h=Number(A("sysctl -n hw.ncpu").toString().trim())||8;if(g>h)return process.stderr.write(` load average ${g.toFixed(1)} exceeds ${h} cores \u2014 tear down other work first.
40
- `),1;let w=`${a}/build/${encodeURIComponent(e.org)}/${encodeURIComponent(e.repo)}/${encodeURIComponent(e.branch)}?platform=${encodeURIComponent(e.platform)}&ssMode=1`,b=j(T(B(),"sootsim-capture-")),{browser:u,reapNow:c}=await I(o=>H.launchPersistentContext(b,o),{headless:!0,viewport:{width:393,height:852},deviceScaleFactor:3,args:["--hide-scrollbars"]});await u.addInitScript(o=>{try{localStorage.setItem("sootsim.session.token",o)}catch{}},n);let y=[],m=u.pages()[0]??await u.newPage();e.dryRun?await m.route(/\/api\/sootsim\/screenshot-(decks|capture)/,o=>o.request().method()==="PUT"?o.abort():o.continue()):m.on("response",o=>{let p=o.url();o.request().method()==="PUT"&&(p.includes("/api/sootsim/screenshot-decks")?y.push({kind:"deck",status:o.status()}):p.includes("/api/sootsim/screenshot-capture")&&y.push({kind:"capture",status:o.status()}))});let R=[];m.on("pageerror",o=>R.push(o.message));let D=m.waitForResponse(o=>o.url().includes("/api/sootsim/screenshot-decks")&&o.request().method()==="GET",{timeout:e.timeoutMs}).catch(()=>null);try{await m.goto(w,{waitUntil:"domcontentloaded",timeout:e.timeoutMs});let o=await ee(m,e.timeoutMs);await te(o),await D,await m.waitForTimeout(800);let p=await N(o);if(p.slides.length===0)return process.stderr.write(` the deck has no slides to capture \u2014 add slides in the composer first.
41
- `),1;let $=e.slides?p.slides.filter(r=>e.slides.includes(r.index)):p.slides;console.log(` ${p.projectName} on ${p.deviceModel} \u2014 capturing ${$.length}/${p.slides.length} slide(s)${e.dryRun?" (dry-run, no upload)":""}`);for(let r of $)try{let l=d[r.index-1],f=l?.threeScene??null,C=f&&l?.screenRef?await _(i,n,e.org,l.screenRef):null;await re(o,r.index,f,C),console.log(` \u2713 slide ${r.index} "${r.title.slice(0,40)}"`)}catch(l){console.log(` \u2717 slide ${r.index} "${r.title.slice(0,40)}" \u2014 ${l.message}`)}e.dryRun||await m.waitForTimeout(2500);let S=(await N(o)).slides.filter(r=>$.some(l=>l.id===r.id)),v=[];if(e.out){let r=z(e.out)?e.out:W(process.cwd(),e.out);for(let l of S){if(!l.captureRef)continue;let f=await o.evaluate(async k=>{let x=globalThis.SootSim?.bridges?.screenshotControl;if(!x)throw new Error("screenshotControl missing");return x.exportSlide(k)},l.index),C=f.dataUrl.match(/^data:[^;]+;base64,(.+)$/);if(C){let k=T(r,f.name);O(L(k),{recursive:!0}),q(k,Buffer.from(C[1],"base64")),v.push(k)}}}let P=S.filter(r=>r.captureRef&&r.cleanRef),E={org:e.org,repo:e.repo,branch:e.branch,dryRun:e.dryRun,slides:S.map(r=>({index:r.index,title:r.title,captureRef:!!r.captureRef,cleanRef:!!r.cleanRef,screenRef:!!r.screenRef,captureMode:r.captureMode})),editableCount:P.length,uploads:e.dryRun?"skipped (dry-run)":y,written:v,pageErrors:R.slice(0,5)};if(e.json)console.log(JSON.stringify(E,null,2));else{console.log(`
42
- ${P.length}/${S.length} slide(s) now have editable layers (captureRef + cleanRef)`);let r=S.filter(l=>l.captureRef&&!l.cleanRef);if(r.length&&console.log(` \u26A0 ${r.length} slide(s) baked a flat capture only (no cleanRef) \u2014 typically 3d slides whose scene didn't restore for a re-bake.`),!e.dryRun){let l=y.filter(f=>f.status>=200&&f.status<300).length;console.log(` uploads: ${l}/${y.length} PUT(s) succeeded`)}v.length&&console.log(` wrote ${v.length} local PNG(s) to ${e.out}`),R.length&&console.log(` \u26A0 ${R.length} page error(s) during capture`)}return 0}finally{await u.close().catch(()=>{}),c(),G(b,{recursive:!0,force:!0})}}export{me as runScreenshotsCapture};
@@ -1,56 +0,0 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{b as Te,c as q,d as Ce,e as X,f as Ee,h as Ie,i as _,j as xe,k as Re,l as Pe,m as Oe,q as _e,t as T,u as Le,v as Me,w as Be,x as De}from"./chunk-5CGJFU27.js";import"./chunk-GDLE4NIV.js";import{a as we,b as Ae,d as ke}from"./chunk-SXBFGGAM.js";import{e as be}from"./chunk-Z5O5H4O7.js";import"./chunk-HEV7BAPE.js";import"./chunk-JWUDT4QK.js";import{a as ge,c as Se,d as D,e as ye}from"./chunk-XYSDYGKC.js";import{d as ve}from"./chunk-USM3P74H.js";import{b as z,d as E,l as B}from"./chunk-CX7VCKAT.js";import"./chunk-4ZXR2M3D.js";import"./chunk-MP2GPO4T.js";import{c as me,d as fe}from"./chunk-7NCPUZ52.js";import"./chunk-GEX7RVHY.js";import{c as O}from"./chunk-T4ISWS5H.js";import{f as M}from"./chunk-F5F7FIPM.js";import{A as V,B as re,J as ne,L,M as K,N as se,O as oe,P as ae,R as ce,U as de,V as le,W as ue,X as G,Y as pe,g as ee,h as te,r as ie}from"./chunk-HWDVC6YA.js";import{a as he}from"./chunk-4EUKOIAV.js";import"./chunk-25KRGDZT.js";import"./chunk-DQUPSAHE.js";import{spawn as Ut}from"child_process";import w from"fs";import{createServer as jt}from"http";import v from"path";import{WebSocket as g,WebSocketServer as Ft}from"ws";import je from"node:fs";import H from"node:path";import{spawn as dt}from"node:child_process";var He=/^[A-Za-z0-9._:-]{1,160}$/;function Y(o){if(!o?.startsWith("TM_SESSION:"))return null;let e=o.slice(11).trim();return He.test(e)?e:null}function Ne(o){return He.test(o)}function lt(o){return typeof o.text=="string"?o.text.trim():""}function ut(o){let e=o?.trim();return e||"tm"}async function Ue(o){let e=o.sessionId.trim();if(!Ne(e))throw new Error(`invalid Team Machine session id: ${e||"<empty>"}`);let t=lt(o.prompt);if(!t)throw new Error("prompt text is empty");let i=ut(o.command),r=o.timeoutMs??15e3;await new Promise((n,c)=>{let s=dt(i,["send",e,t],{stdio:["ignore","ignore","pipe"],env:process.env}),a="",l=setTimeout(()=>{try{s.kill("SIGTERM")}catch{}c(new Error(`tm send timed out after ${Math.round(r/1e3)}s`))},r);s.stderr?.setEncoding("utf8"),s.stderr?.on("data",d=>{a.length<4e3&&(a+=String(d))}),s.on("error",d=>{clearTimeout(l),c(d)}),s.on("exit",(d,u)=>{if(clearTimeout(l),d===0){n();return}let p=a.trim();c(new Error(p||`tm send exited with ${u?`signal ${u}`:`code ${d??"unknown"}`}`))})})}var N=1,pt=25;function ht(){return[Number(process.env.VITE_PORT_WEB||process.env.PORT||3e3),Number(process.env.VITE_PORT_ZERO||7849),Number(process.env.VITE_PORT_R2||9500)].filter(o=>Number.isFinite(o)&&o>0)}var U=class{subscriptions=new Map;sessionsBySocket=new Map;allSockets=new Set;pendingPromptEchoes=new Map;pendingTurns=new Map;pendingSessionStates=new Map;pendingSessionPatches=new Map;sessionStatusPersistTimer=null;opts;constructor(e={}){this.opts=e}registerSocket(e){this.allSockets.add(e)}unregisterSocket(e){let t=this.sessionsBySocket.get(e);if(t){for(let i of t)this.decrementSubscription(i);this.sessionsBySocket.delete(e)}this.allSockets.delete(e)}async handleMessage(e,t){let i=t?.type;if(typeof i!="string"||!i.startsWith("agent:"))return!1;let r=t.id;try{let n=await this.dispatch(e,i,t);this.respond(e,r,n)}catch(n){n instanceof T?this.respondError(e,r,n.message,n.code):this.respondError(e,r,n instanceof Error?n.message:String(n))}return!0}async seedOnBoot(){try{await Oe()}catch(e){process.stderr.write(`[sootsim-agent] seedFromDemoAppRegistry failed: ${e instanceof Error?e.message:String(e)}
5
- `)}}close(){this.sessionStatusPersistTimer&&(clearTimeout(this.sessionStatusPersistTimer),this.sessionStatusPersistTimer=null),this.flushSessionStatuses();for(let e of this.subscriptions.values())try{e.unsubscribe()}catch{}this.subscriptions.clear(),this.sessionsBySocket.clear(),this.allSockets.clear()}async dispatch(e,t,i){switch(t){case"agent:list-projects":return X();case"agent:upsert-project":return q(i.input??{});case"agent:delete-project":return Ie(String(i.projectId)),{ok:!0};case"agent:auto-attach-for-url":return this.autoAttachForUrl(i.input??{});case"agent:list-sessions":return xe(i.projectId?String(i.projectId):void 0);case"agent:start-session":return this.doStartSession(i.input??{});case"agent:send-claimed-prompt":return this.sendClaimedPrompt(i);case"agent:send-prompt":{let n=String(i.sessionId),c=_(n);if(!c)throw new T("NO_SESSION",`no session: ${n}`);let s=this.normalizePromptEnvelope(i);return await Me(n,s),this.notePromptAccepted(n,s,c.status==="working")}case"agent:end-session":this.dropSessionFanout(String(i.sessionId)),await Be(String(i.sessionId));let r=_(String(i.sessionId));return r&&this.broadcastSessionStatus(r),{ok:!0};case"agent:get-transcript":return this.getTranscript(String(i.sessionId));case"agent:get-paths":return this.getPaths();case"agent:subscribe-events":return this.subscribeSocket(e,String(i.sessionId));case"agent:unsubscribe-events":return this.unsubscribeSocket(e,String(i.sessionId));default:throw new T("UNKNOWN_AGENT_MSG",`unknown agent message: ${t}`)}}async sendClaimedPrompt(e){let t=typeof e.simId=="string"?e.simId.trim():"";if(!t)throw new T("NO_SIM","agent:send-claimed-prompt requires simId");let i=this.opts.resolveCliLease?.(t)??null;if(!i||i.kind!=="cli"||i.expiresAt<=Date.now())throw new T("NO_CLAIM",`sim ${t} has no active CLI claim`);let r=Y(i.cliIdentityKey);if(!r)throw new T("UNSUPPORTED_CLAIM",`sim ${t} is claimed by a CLI identity that is not promptable`);return await Ue({sessionId:r,prompt:this.normalizePromptEnvelope(e)}),{ok:!0,routed:"team-machine",sessionId:r}}async doStartSession(e){if(!Ce(e.projectId))throw new T("NO_PROJECT",`no project: ${e.projectId}`);let i=await Le(e);return this.broadcastSessionStatus(i.session),i}async autoAttachForUrl(e){let t=e.bundleUrl??"",i=(()=>{try{return new URL(t).port||null}catch{return null}})();if(!i)return{project:null};let r=this.opts.getExcludePorts?.()??ht(),c=(await B({excludePorts:r})).find(d=>String(d.port)===i);if(!c||!c.cwd)return{project:null};let s=X().find(d=>d.cwd===c.cwd)??null,a=Array.from(new Set([...s?.knownBundleUrls??[],c.bundleUrl,t]));return{project:q({cwd:c.cwd,name:c.projectName??H.basename(c.cwd),preferredProvider:e.provider??s?.preferredProvider,sourceRoots:s?.sourceRoots??[c.cwd],knownBundleUrls:a,framework:s?.framework??mt(c.framework),bundleId:c.bundleId??s?.bundleId})}}getTranscript(e){let t=_e(e);return je.existsSync(t)?je.readFileSync(t,"utf8"):{error:"transcript not found",code:"NO_TRANSCRIPT"}}getPaths(){let e=Te();return{userDataDir:e,storeFile:H.join(e,"attached-projects.json"),sessionsDir:H.join(e,"sessions"),transcriptsDir:H.join(e,"transcripts")}}subscribeSocket(e,t){let i=this.sessionsBySocket.get(e);if(i||(i=new Set,this.sessionsBySocket.set(e,i)),i.has(t))return{ok:!0,refCount:this.subscriptions.get(t)?.refCount??1};i.add(t);let r=this.subscriptions.get(t);if(r)return r.refCount++,{ok:!0,refCount:r.refCount};let n=De(t,c=>{let s=this.coalescePromptEcho(t,c);if(s&&(this.applySessionEvent(t,s),this.fanOutEvent(t,s)),c.type==="turn-completed"){let a=_(t);if(a)try{Ee(a.projectId,{usd:c.costUsd,ts:c.ts})}catch(l){process.stderr.write(`[sootsim-agent] recordTurnTelemetry failed: ${l instanceof Error?l.message:String(l)}
6
- `)}}});return this.subscriptions.set(t,{unsubscribe:n,refCount:1}),{ok:!0,refCount:1}}unsubscribeSocket(e,t){let i=this.sessionsBySocket.get(e);return!i||!i.has(t)?{ok:!0,refCount:0}:(i.delete(t),this.decrementSubscription(t))}decrementSubscription(e){let t=this.subscriptions.get(e);if(!t)return{ok:!0,refCount:0};if(t.refCount--,t.refCount<=0){try{t.unsubscribe()}catch{}return this.subscriptions.delete(e),{ok:!0,refCount:0}}return{ok:!0,refCount:t.refCount}}dropSessionFanout(e){let t=this.subscriptions.get(e);if(t){try{t.unsubscribe()}catch{}this.subscriptions.delete(e)}for(let i of this.sessionsBySocket.values())i.delete(e);this.clearPromptTracking(e)}normalizePromptEnvelope(e){if(e?.prompt&&typeof e.prompt=="object"){let t=e.prompt;return{text:String(t.text??""),...typeof t.displayText=="string"?{displayText:t.displayText}:{},...typeof t.inspectSummary=="string"?{inspectSummary:t.inspectSummary}:{},...typeof t.inspectTrace=="string"?{inspectTrace:t.inspectTrace}:{}}}return{text:String(e?.text??""),...typeof e?.displayText=="string"?{displayText:e.displayText}:{},...typeof e?.inspectSummary=="string"?{inspectSummary:e.inspectSummary}:{},...typeof e?.inspectTrace=="string"?{inspectTrace:e.inspectTrace}:{}}}notePromptAccepted(e,t,i){let r=Date.now(),n=this.pendingPromptEchoes.get(e)??[];n.push({sentAt:r}),this.pendingPromptEchoes.set(e,n);let c=Math.max(this.pendingTurns.get(e)??0,i?1:0)+1;this.pendingTurns.set(e,c);let s=t.displayText??t.text;return this.patchSession(e,{lastPrompt:s,status:"working",needsAttention:!1}),this.fanOutEvent(e,{type:"prompt-received",text:s,...t.inspectSummary?{inspectSummary:t.inspectSummary}:{},...t.inspectTrace?{inspectTrace:t.inspectTrace}:{},ts:r}),{ok:!0,queued:c>1,pendingTurns:c,queueDepth:Math.max(0,c-1)}}applySessionEvent(e,t){switch(t.type){case"prompt-received":case"turn-started":this.patchSession(e,{status:"working",needsAttention:!1});return;case"turn-completed":{let i=this.consumeSettledTurn(e);this.patchSession(e,{status:i>0?"working":"idle",needsAttention:!1,lastTurnFiles:t.filesTouched,currentlyEditing:void 0});return}case"approval-needed":this.patchSession(e,{status:"needs-attention",needsAttention:!0});return;case"error":{let i=this.consumeSettledTurn(e);this.patchSession(e,{status:i>0?"working":"needs-attention",needsAttention:i<=0,currentlyEditing:void 0});return}case"exited":this.clearPromptTracking(e),this.patchSession(e,{status:"ended",needsAttention:!1,wrapperPid:void 0,currentlyEditing:void 0});return;case"ready":case"turn-reasoning":case"turn-message":case"turn-plan":case"tool-call":case"file-edited":case"file-diff-delta":return}}patchSession(e,t){let i=this.pendingSessionStates.get(e)??_(e);if(!i)return;let r=Re(i,t);this.pendingSessionStates.set(e,r),this.pendingSessionPatches.set(e,{...this.pendingSessionPatches.get(e),...t}),this.broadcastSessionStatus(r),!this.sessionStatusPersistTimer&&(this.sessionStatusPersistTimer=setTimeout(()=>{this.sessionStatusPersistTimer=null,this.flushSessionStatuses()},pt))}flushSessionStatuses(){if(this.pendingSessionPatches.size===0)return;let e=this.pendingSessionStates,t=this.pendingSessionPatches;this.pendingSessionStates=new Map,this.pendingSessionPatches=new Map;try{Pe([...t].map(([i,r])=>({id:i,patch:r})))}catch(i){for(let[r,n]of e)this.pendingSessionStates.has(r)||this.pendingSessionStates.set(r,n);for(let[r,n]of t)this.pendingSessionPatches.set(r,{...n,...this.pendingSessionPatches.get(r)});process.stderr.write(`[sootsim-agent] session status persistence failed: ${i instanceof Error?i.message:String(i)}
7
- `)}}coalescePromptEcho(e,t){if(t.type!=="prompt-received")return t;let i=this.pendingPromptEchoes.get(e);if(!i||i.length===0)return t;for(;i.length>0&&Date.now()-i[0].sentAt>15e3;)i.shift();return i.length===0?(this.pendingPromptEchoes.delete(e),t):(i.shift(),i.length===0?this.pendingPromptEchoes.delete(e):this.pendingPromptEchoes.set(e,i),null)}consumeSettledTurn(e){let t=Math.max(0,(this.pendingTurns.get(e)??1)-1);return t>0?this.pendingTurns.set(e,t):this.pendingTurns.delete(e),t}clearPromptTracking(e){this.pendingPromptEchoes.delete(e),this.pendingTurns.delete(e)}fanOutEvent(e,t){let i=JSON.stringify({type:"agent:event",sessionId:e,event:t});for(let[r,n]of this.sessionsBySocket)if(n.has(e)&&r.readyState===N)try{r.send(i)}catch{}}broadcastSessionStatus(e){let t=JSON.stringify({type:"agent:session-status",session:e});for(let i of this.allSockets)if(i.readyState===N)try{i.send(t)}catch{}}respond(e,t,i){if(e.readyState===N)try{e.send(JSON.stringify({id:t,result:i}))}catch{}}respondError(e,t,i,r){if(e.readyState===N)try{e.send(JSON.stringify({id:t,error:i,...r?{code:r}:{}}))}catch{}}};function mt(o){return o==="expo"?"expo":o==="one"||o==="vxrn"?"one":"unknown"}import We from"http";import $e from"https";import{finished as St,pipeline as yt}from"stream/promises";var Z="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",ft=[{hostSuffix:"uniswap.org",headers:{origin:"https://app.uniswap.org",referer:"https://app.uniswap.org/"},suppressBrowserUserAgent:!0}];function gt(o){let e=o.toLowerCase();for(let t of ft)if(e===t.hostSuffix||e.endsWith(`.${t.hostSuffix}`))return t}function Fe(o){let e=gt(o.hostname);return{"accept-encoding":"identity",...e?.suppressBrowserUserAgent?{}:{"user-agent":Z},...e?.headers}}var vt=new Set(["host","origin","referer","user-agent","accept-encoding","cookie","connection","keep-alive","transfer-encoding","upgrade","content-length","sec-fetch-site","sec-fetch-mode","sec-fetch-dest","sec-ch-ua","sec-ch-ua-mobile","sec-ch-ua-platform"]),bt={"access-control-allow-origin":"*","access-control-allow-methods":"GET,POST,PUT,DELETE,PATCH,OPTIONS","access-control-allow-headers":"*","access-control-expose-headers":"*","access-control-max-age":"3600"},wt=new Set(["host","origin","referer","sec-fetch-site","sec-fetch-mode","sec-fetch-dest"]);function Je(o){return o.protocol!=="https:"?{}:E(o.hostname)?{rejectUnauthorized:!1}:{}}function C(o){for(let[e,t]of Object.entries(bt))o.setHeader(e,t)}function At(o,e){let t=[],i=e;i?.code&&t.push(i.code),i?.message&&t.push(i.message),i?.cause?.code&&t.push(i.cause.code),i?.cause?.message&&t.push(i.cause.message);let n=[...new Set(t.filter(Boolean))].join(" | ")||String(e);return o.includes("stored-in-.env.local")?`${n} | upstream url still contains placeholder env values`:n}function kt(o,e){let t={};for(let[i,r]of Object.entries(o))r&&(vt.has(i.toLowerCase())||(t[i]=Array.isArray(r)?r.join(", "):r));return Object.assign(t,e?Fe(e):{"user-agent":Z}),t}function Tt(o,e){let t={};for(let[i,r]of Object.entries(o))r&&(wt.has(i.toLowerCase())||(t[i]=r));return t.host=e.host,t.origin=e.origin,t.referer=`${e.origin}/`,t}function Ve(o){return o?.startsWith("/__fetch-proxy?")||o?.startsWith("/__proxy?")||!1}function Ke(o){return o?!!(o.startsWith("/__app-api?")||o.startsWith("/__app-api/")):!1}async function Q(o,e){if(o.method==="OPTIONS"){C(e),e.writeHead(204),e.end();return}let i=new URLSearchParams((o.url||"").split("?")[1]||"").get("url");if(!i){C(e),e.writeHead(400,{"Content-Type":"text/plain"}),e.end("missing url param");return}let r;try{r=new URL(i)}catch{C(e),e.writeHead(400,{"Content-Type":"text/plain"}),e.end("invalid url param");return}let n;if(o.method!=="GET"&&o.method!=="HEAD"){let a=[];for await(let l of o)a.push(Buffer.isBuffer(l)?l:Buffer.from(l));a.length>0&&(n=Buffer.concat(a))}let c=o.method||"GET",s=async(a,l,d,u)=>{let p=a.protocol==="https:"?$e:We,h=kt(o.headers,a);a.origin!==r.origin&&(delete h.authorization,delete h["proxy-authorization"]),l==="GET"||l==="HEAD"?(delete h["content-length"],delete h["content-type"]):d&&(h["content-length"]=String(d.byteLength));let m=await new Promise((A,k)=>{let y=p.request({hostname:z(a.hostname),port:a.port||(a.protocol==="https:"?443:80),path:a.pathname+a.search,method:l,headers:h,...Je(a)},A),R=()=>y.destroy(new Error("fetch proxy client disconnected"));o.once("aborted",R),y.once("close",()=>o.off("aborted",R)),y.once("error",k),d===void 0?y.end():y.end(d)}),S=m.statusCode??502,f=m.headers.location;if(f&&(S===301||S===302||S===303||S===307||S===308)){if(u>=10)throw m.destroy(),new Error("too many redirects");let A=(S===301||S===302)&&l==="POST"||S===303&&l!=="GET"&&l!=="HEAD",k=new URL(f,a);m.resume(),await St(m),await s(k,A?"GET":l,A?void 0:d,u+1);return}for(let[A,k]of Object.entries(m.headers)){let y=A.toLowerCase();k===void 0||y==="set-cookie"||y==="connection"||y==="keep-alive"||y==="proxy-authenticate"||y==="proxy-authorization"||y==="te"||y==="trailer"||y==="transfer-encoding"||y==="upgrade"||y.startsWith("access-control-")||e.setHeader(A,k)}C(e);let b=m.headers["set-cookie"]??[];b.length>0&&e.setHeader("x-sootsim-set-cookie",(Array.isArray(b)?b:[b]).join(", ")),e.statusCode=S,await yt(m,e)};try{await s(r,c,n,0)}catch(a){if(e.headersSent){e.destroy(a instanceof Error?a:new Error(String(a)));return}C(e),e.writeHead(502,{"Content-Type":"text/plain"}),e.end(`fetch proxy error: ${At(r.href,a)}`)}}async function Ge(o,e){let i=new URLSearchParams((o.url||"").split("?")[1]||"").get("url");if(!i){C(e),e.writeHead(400,{"Content-Type":"text/plain"}),e.end("bundle-proxy: missing url query param");return}let r;try{r=new URL(i)}catch{C(e),e.writeHead(400,{"Content-Type":"text/plain"}),e.end("bundle-proxy: invalid url");return}if(!E(r.hostname)){C(e),e.writeHead(403,{"Content-Type":"text/plain"}),e.end("bundle-proxy: only loopback targets allowed");return}await Q(o,e)}function ze(o,e){let t=o.url||"",i="",r="";if(t.startsWith("/__app-api?")){let l=new URL(t,"http://sootsim.local");i=l.searchParams.get("path")||"",r=l.searchParams.get("origin")?.trim()||""}else if(t.startsWith("/__app-api/"))i=t.slice(10);else return!1;if(!r)return e.writeHead(400,{"Content-Type":"text/plain"}),e.end("app-api: missing origin query param"),!0;if(o.method==="OPTIONS")return e.writeHead(204,{"Access-Control-Allow-Origin":o.headers.origin||"*","Access-Control-Allow-Methods":"GET,POST,PUT,PATCH,DELETE,OPTIONS","Access-Control-Allow-Headers":o.headers["access-control-request-headers"]||"*","Access-Control-Allow-Credentials":"true","Access-Control-Max-Age":"86400"}),e.end(),!0;let n;try{n=new URL(i,r)}catch{return e.writeHead(400,{"Content-Type":"text/plain"}),e.end("app-api: invalid origin or path"),!0}let c=n.protocol==="https:"?$e:We,s=Tt(o.headers,n),a=c.request({hostname:z(n.hostname),port:n.port||(n.protocol==="https:"?443:80),path:n.pathname+n.search,method:o.method,headers:s,...Je(n)},l=>{let d=Object.keys(l.headers).filter(u=>{let p=u.toLowerCase();return!p.startsWith("access-control-")&&p!=="set-cookie"}).join(", ");e.writeHead(l.statusCode??502,{...l.headers,"access-control-allow-origin":o.headers.origin||"*","access-control-allow-credentials":"true","access-control-expose-headers":d}),l.pipe(e)});return a.on("error",l=>{e.statusCode=502,e.end(`app proxy error: ${l.message}`)}),o.pipe(a),!0}import qe from"node:fs";import Ct from"node:path";var Xe=1,j=class extends Error{},F=class{registered=new Map;open=new Map;byRoot=new Map;nextPlaneId=1;async handleMessage(e,t){if(typeof t!="object"||t===null)return!1;let i={...t},r=i.type;if(typeof r!="string"||!r.startsWith("plane:"))return!1;let n=i.id;try{this.respond(e,n,await this.dispatch(e,r,i))}catch(c){let s=c instanceof j?"plane-not-registered":void 0;this.respondError(e,n,c instanceof Error?c.message:String(c),s)}return!0}unregisterSocket(e){for(let[t,i]of[...this.registered]){if(!i.delete(e)||i.size>0)continue;this.registered.delete(t);let r=this.byRoot.get(t);r&&(this.open.delete(r),this.byRoot.delete(t))}for(let[t,i]of[...this.open])i.sockets.delete(e),i.sockets.size===0&&!this.registered.has(i.root)&&(this.open.delete(t),this.byRoot.delete(i.root))}registeredRoots(){return[...this.registered.keys()]}async dispatch(e,t,i){switch(t){case"plane:register":{let r=this.realRoot(String(i.root??"")),n=this.registered.get(r)??new Set;return n.add(e),this.registered.set(r,n),{root:r,holders:n.size}}case"plane:status":{let r=this.realRoot(String(i.root??"")),n=this.registered.get(r);return{root:r,holders:n?.size??0,registered:!!n}}case"plane:open":{let r=this.realRoot(String(i.root??""));if(!this.registered.has(r))throw new j(`no box has registered ${r}; start one with \`rnx box\` there`);let n=this.byRoot.get(r),c=n?this.open.get(n):void 0;if(n&&c)return c.sockets.add(e),{planeId:n,version:c.version};let s=new ke(r);await s.refresh();let a=`plane-${this.nextPlaneId++}`,l={root:r,plane:s,version:we(s.index()),sockets:new Set([e])};return this.open.set(a,l),this.byRoot.set(r,a),{planeId:a,version:l.version}}case"plane:close":{let r=this.open.get(String(i.planeId??""));return r&&r.sockets.delete(e),{ok:!0}}default:{let r=this.entry(i),n=await Ae(r.plane,t,i);if(!n)throw new Error(`unknown plane command: ${t}`);return n.version&&(r.version=n.version),n.result}}}entry(e){let t=this.open.get(String(e.planeId??""));if(!t)throw new Error(`unknown planeId: ${String(e.planeId)}`);return t}realRoot(e){if(!e)throw new Error("root is required");let t=Ct.resolve(e),i;try{i=qe.realpathSync(t)}catch{throw new Error(`no such directory: ${t}`)}if(!qe.statSync(i).isDirectory())throw new Error(`not a directory: ${i}`);return i}respond(e,t,i){if(e.readyState===Xe)try{e.send(JSON.stringify({id:t,result:i}))}catch{}}respondError(e,t,i,r){if(e.readyState===Xe)try{e.send(JSON.stringify({id:t,error:i,...r?{code:r}:{}}))}catch{}}};import Ye from"node:fs/promises";import I from"node:path";import{build as Et}from"esbuild";var Ze="/__sootsim-replacement-module";function It(o){switch(I.extname(o).toLowerCase()){case".js":return"js";case".jsx":return"jsx";case".ts":return"ts";case".tsx":return"tsx";default:return null}}function xt(o,e,t){let i=o.headers.origin,r;if(i)try{E(new URL(i).hostname)&&(r=i)}catch{}e.writeHead(200,{"Content-Type":"application/javascript; charset=utf-8","Cache-Control":"no-cache",...r?{"Access-Control-Allow-Origin":r,Vary:"Origin"}:{}}),e.end(o.method==="HEAD"?void 0:t)}function Qe(o){if(!o)return!1;try{return new URL(o,"http://rnx.local").pathname===Ze}catch{return!1}}async function et(o,e,t={}){let i=o.url||"",r;try{r=new URL(i,"http://rnx.local")}catch{return!1}if(t.requireLoopbackOrigin){let d=o.headers.origin,u=o.socket.remoteAddress?.replace(/^::ffff:/,""),p=!!(u&&E(u));if(p&&d)try{p=E(new URL(d).hostname)}catch{p=!1}if(!p)return e.writeHead(403,{"Content-Type":"text/plain; charset=utf-8"}),e.end("replacement modules are available only to loopback origins"),!0}if(r.pathname!==Ze)return!1;if(t.requestToken&&r.searchParams.get("token")!==t.requestToken)return e.writeHead(403,{"Content-Type":"text/plain; charset=utf-8"}),e.end("replacement module request token is invalid"),!0;let n=r.searchParams.get("path");if(!n)return e.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}),e.end("missing path param"),!0;if(!It(n))return e.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}),e.end("replacement files must be .js, .jsx, .ts, or .tsx"),!0;if(!I.isAbsolute(n))return e.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}),e.end("replacement file path must be absolute"),!0;let s=new Set(r.searchParams.getAll("runtimePackage")),a;if(t.projectRoot){let d=I.resolve(t.projectRoot);a=await Ye.realpath(t.projectRoot);let u=I.relative(d,n);if(u.startsWith("..")||I.isAbsolute(u))return e.writeHead(403,{"Content-Type":"text/plain; charset=utf-8"}),e.end("replacement file must be inside the project root"),!0}let l;try{l=await Ye.realpath(n)}catch(d){let u=d instanceof Error?d.message:String(d);return e.writeHead(404,{"Content-Type":"text/plain; charset=utf-8"}),e.end(`replacement file not found: ${u}`),!0}if(a){let d=I.relative(a,l);if(d.startsWith("..")||I.isAbsolute(d))return e.writeHead(403,{"Content-Type":"text/plain; charset=utf-8"}),e.end("replacement file must be inside the project root"),!0}try{let d=await Et({entryPoints:[l],bundle:!0,write:!1,format:"esm",platform:"browser",target:"es2022",jsx:"transform",jsxFactory:"React.createElement",jsxFragment:"React.Fragment",tsconfigRaw:{compilerOptions:{jsx:"react"}},sourcemap:"inline",logLevel:"silent",plugins:[{name:"sootsim-replacement-runtime",setup(u){u.onResolve({filter:/^(react|react-native|react\/jsx-runtime|react\/jsx-dev-runtime)$/},p=>({path:p.path,namespace:"sootsim-replacement-runtime"})),u.onResolve({filter:/.*/},p=>s.has(p.path)?{path:p.path,namespace:"sootsim-replacement-runtime"}:void 0),u.onLoad({filter:/.*/,namespace:"sootsim-replacement-runtime"},p=>p.path==="react-native"?{contents:`const bridge = globalThis.__sootsim_components__
8
- if (!bridge) throw new Error('[rnx] replacement React Native bridge is not ready')
9
- module.exports = bridge`,loader:"js"}:p.path==="react"?{contents:`const bridge = globalThis.__sootsim_react_bridge
10
- if (!bridge) throw new Error('[rnx] replacement React bridge is not ready')
11
- module.exports = bridge`,loader:"js"}:p.path!=="react/jsx-runtime"&&p.path!=="react/jsx-dev-runtime"?{contents:`const packages = globalThis.__sootsim_config_package_modules
12
- const bridge = packages?.[${JSON.stringify(p.path)}]
13
- if (!bridge) throw new Error(${JSON.stringify(`[rnx] replacement package bridge ${p.path} is not ready`)})
14
- module.exports = bridge`,loader:"js"}:{contents:`const React = globalThis.__sootsim_react_bridge
15
- if (!React) throw new Error('[rnx] replacement React bridge is not ready')
16
- exports.Fragment = React.Fragment
17
- exports.jsx = (type, props, key) => React.createElement(type, key === undefined ? props : { ...props, key })
18
- exports.jsxs = exports.jsx
19
- exports.jsxDEV = exports.jsx`,loader:"js"})}}]});xt(o,e,d.outputFiles[0]?.text??"")}catch(d){let u=d instanceof Error?d.message:String(d);e.writeHead(500,{"Content-Type":"text/plain; charset=utf-8"}),e.end(`replacement bundle failed: ${u}`)}return!0}import{WebSocket as x,WebSocketServer as Rt}from"ws";var Pt="/__websocket-proxy",Ot=new Set(["host","connection","upgrade","transfer-encoding","content-length","sec-websocket-accept","sec-websocket-extensions","sec-websocket-key","sec-websocket-protocol","sec-websocket-version"]);function P(o,e,t){try{o.write(`HTTP/1.1 ${e} ${t}\r
20
- Connection: close\r
21
- Content-Type: text/plain\r
22
- Content-Length: ${t.length}\r
23
- \r
24
- ${t}`)}catch{}o.destroy()}function _t(o){let e=o.headers.origin,t=o.headers.host;if(!e||!t)return!1;try{return new URL(e).host===t}catch{return!1}}function Lt(o){if(!o)return{};let e=o.replace(/-/g,"+").replace(/_/g,"/"),t=e+"=".repeat((4-e.length%4)%4),i=JSON.parse(Buffer.from(t,"base64").toString("utf8"));if(!i||typeof i!="object"||Array.isArray(i))return{};let r={};for(let[n,c]of Object.entries(i))c!=null&&(Ot.has(n.toLowerCase())||(r[n]=Array.isArray(c)?c.join(", "):String(c)));return r}function Mt(o){let e=new URL(o.href);return e.protocol=o.protocol==="wss:"?"https:":"http:",e.origin}function Bt(o){let e=o.headers["sec-websocket-protocol"];return(Array.isArray(e)?e.join(","):e||"").split(",").map(i=>i.trim()).filter(Boolean)}function W(o,e,t){if(!(o.readyState===x.CLOSED||o.readyState===x.CLOSING))try{o.close(e,t)}catch{o.terminate()}}function Dt(o,e){let t=!1,i=(r,n,c,s)=>{t||(t=!0,W(n,c,s.toString()),r.readyState===x.OPEN&&W(r,c,s.toString()))};o.on("message",(r,n)=>{e.readyState===x.OPEN&&e.send(r,{binary:n})}),e.on("message",(r,n)=>{o.readyState===x.OPEN&&o.send(r,{binary:n})}),o.on("close",(r,n)=>i(o,e,r,n)),e.on("close",(r,n)=>i(e,o,r,n)),o.on("error",()=>W(e,1011,"proxy client error")),e.on("error",()=>W(o,1011,"upstream websocket error"))}function Ht(o,e,t,i){let r={...t},n=Object.keys(r).filter(c=>c.toLowerCase()==="origin");if(i&&n.length===0)r.origin=Mt(o);else if(!i)for(let c of n)delete r[c];return Object.keys(r).length===0?new x(o.href,e):new x(o.href,e,{headers:r})}function Nt(o){if(!o)return!1;try{return new URL(o,"http://localhost").pathname===Pt}catch{return!1}}function tt(o,e,t,i=!0){if(!Nt(o.url))return!1;if(!_t(o))return P(e,403,"forbidden websocket proxy origin"),!0;let r,n;try{let l=new URL(o.url||"/","http://localhost"),d=l.searchParams.get("url");if(!d)return P(e,400,"missing websocket proxy url"),!0;if(r=new URL(d),r.protocol!=="ws:"&&r.protocol!=="wss:")return P(e,400,"invalid websocket proxy protocol"),!0;n=Lt(l.searchParams.get("headers"))}catch{return P(e,400,"invalid websocket proxy request"),!0}let c=Bt(o),s=Ht(r,c,n,i),a=!1;return e.once("close",()=>{a||s.terminate()}),s.once("open",()=>{if(a)return;a=!0;let l=s.protocol;new Rt({noServer:!0,clientTracking:!1,handleProtocols(u){return l||u.values().next().value||!1}}).handleUpgrade(o,e,t,u=>{Dt(u,s)})}),s.once("error",()=>{a||(a=!0,P(e,502,"upstream websocket error"))}),s.once("close",()=>{a||(a=!0,P(e,502,"upstream websocket closed"))}),!0}var Wt="require-corp",$t=new Set(["tap","keyboard","longPress","perform","reset","camera"]),Jt=2e3,Vt=1e3;function Kt(o){return!o||typeof o.type!="string"?!1:o.acquireLock===!0?!0:o.readOnly===!0?!1:$t.has(o.type)}var it=5e3,Gt=3600*1e3,zt="SOOTSIM_RUNTIME_UPDATE_INTERVAL_MS",rt={".html":"text/html; charset=utf-8",".js":"application/javascript",".cjs":"application/javascript",".mjs":"application/javascript",".css":"text/css; charset=utf-8",".json":"application/json; charset=utf-8",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".svg":"image/svg+xml",".webp":"image/webp",".avif":"image/avif",".ico":"image/x-icon",".wasm":"application/wasm",".ttf":"font/ttf",".otf":"font/otf",".woff":"font/woff",".woff2":"font/woff2",".map":"application/json",".txt":"text/plain; charset=utf-8",".mp4":"video/mp4",".m4v":"video/mp4",".webm":"video/webm",".mov":"video/quicktime"},nt="/__camera-fixtures/",qt=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function st(o,e,t){let i;try{let a=V();i=JSON.stringify(a)}catch{i="{}"}let r=e>0?`window.__sootsimBridgePort=${e};`:"",n=t?`window.__sootsimContrastOrigin=${JSON.stringify(t)};`:"",c=`<script>window.__sootsimSharedConfig=${i};`+r+n+`window.__sootsimCliVersion=${JSON.stringify(he())};</script>`,s=o.toString("utf8");return s.includes("<head>")?s.replace("<head>",`<head>${c}`):s.includes("</head>")?s.replace("</head>",c+"</head>"):s.includes("</body>")?s.replace("</body>",c+"</body>"):c+s}function ot(o){typeof o=="object"&&o!==null&&"unref"in o&&o.unref()}var $=class o{port;openUrlHandler;httpServer=null;wss=null;nextCommandId=1;nextSimNumber=161;sims=new Map;primarySimId=null;pendingCommands=new Map;cliBySentId=new Map;cliSimBySocket=new Map;cliLastCommandAt=new Map;cliIdentityKeyBySocket=new Map;cliLabelBySocket=new Map;restorableSims=new Map;nextCliFallbackId=1;cliIdleTimer=null;agentHost;planeHost=new F;static CLI_IDLE_TIMEOUT_MS=6e4;static CLI_LEASE_TTL_MS=6e5;static USER_ACTIVE_LEASE_TTL_MS=8e3;static USER_BOOT_LEASE_TTL_MS=6e4;static SIM_RECONNECT_TTL_MS=3e4;static SIM_IDLE_REAP_TTL_MS=30*6e4;static AUTOMATION_SIM_IDLE_REAP_TTL_MS=10*6e4;static MAX_CONCURRENT_AUTOMATION_SIMS=6;static AUTOMATION_SIM_ACTIVE_GRACE_MS=6e4;preferredPort;portFallbackCount;simIdleReapTtlMs;automationSimIdleReapTtlMs;maxConcurrentAutomationSims;automationSimActiveGraceMs;shouldWriteLockfile;shouldWriteDevLockfile;getShellPort;contrastOrigin=null;effectivePort=0;startedAt=0;heartbeatTimer=null;devHeartbeatTimer=null;wsHeartbeatTimer=null;wsIsAlive=new WeakMap;static WS_HEARTBEAT_INTERVAL_MS=3e4;runtimeUpdateTimer=null;runtimeUpdateInFlight=null;activeRuntimeVersion=null;activeRuntimeDirPath=null;scanCache=null;scanCacheAt=0;inflightScan=null;static SCAN_FRESH_MS=2e3;constructor(e={}){this.preferredPort=e.port||7668,this.port=this.preferredPort,this.shouldWriteLockfile=e.writeLockfile===!0,this.shouldWriteDevLockfile=e.writeDevLockfile===!0,this.getShellPort=e.getShellPort??null,this.portFallbackCount=Math.max(1,e.portFallbackCount??10),this.openUrlHandler=e.openUrl,this.agentHost=new U({getExcludePorts:e.agentScanExcludes,resolveCliLease:t=>{let i=this.sims.get(t),r=i?this.getActiveLease(i):null;return r?{kind:r.kind,cliIdentityKey:r.cliIdentityKey,expiresAt:r.expiresAt}:null}}),this.contrastOrigin=e.contrastOrigin?.replace(/\/$/,"")||null,this.simIdleReapTtlMs=e.simIdleReapTtlMs??o.SIM_IDLE_REAP_TTL_MS,this.automationSimIdleReapTtlMs=e.automationSimIdleReapTtlMs??o.AUTOMATION_SIM_IDLE_REAP_TTL_MS,this.maxConcurrentAutomationSims=e.maxConcurrentAutomationSims??o.MAX_CONCURRENT_AUTOMATION_SIMS,this.automationSimActiveGraceMs=e.automationSimActiveGraceMs??o.AUTOMATION_SIM_ACTIVE_GRACE_MS}getAgentHost(){return this.agentHost}reapIdleSimsForTest(e=Date.now()){this.reapIdleSims(e)}start(e){this.startAsync(e)}async startAsync(e){if(this.httpServer||this.wss)return this.effectivePort;this.refreshActiveRuntime();for(let t=0;t<this.portFallbackCount;t++){let i=this.preferredPort+t;try{return await this.bindOnce(i,e?.silent===!0),this.effectivePort=i,this.port=i,this.startedAt=Date.now(),t>0&&!e?.silent&&process.stderr.write(`ws bridge bound to port ${i} (preferred ${this.preferredPort} was taken)
25
- `),this.afterBind(),i}catch(r){if(r?.code!=="EADDRINUSE")throw r;e?.silent||process.stderr.write(`ws bridge port ${i} already in use, trying ${i+1}
26
- `)}}throw new Error(`could not bind ws bridge after ${this.portFallbackCount} attempts starting at ${this.preferredPort}`)}bindOnce(e,t){return new Promise((i,r)=>{let n=jt((a,l)=>this.handleHttpRequest(a,l)),c=!1,s=a=>{if(!c){c=!0;try{n.close()}catch{}this.httpServer=null,this.wss=null,r(a)}};n.once("error",s),n.listen(e,"127.0.0.1",()=>{c||(c=!0,n.removeListener("error",s),n.on("error",a=>{process.stderr.write(`ws bridge http error: ${String(a)}
27
- `)}),this.httpServer=n,this.wss=new Ft({noServer:!0}),this.wireWebSocketServer(),n.on("upgrade",(a,l,d)=>{tt(a,l,d)||this.wss?.handleUpgrade(a,l,d,u=>{this.wss?.emit("connection",u,a)})}),i())})})}wireWebSocketServer(){this.wss&&this.wss.on("connection",(e,t)=>{let i=t.headers.origin,r=i?"sim":"cli",n=null;if(e.on("error",()=>{}),this.wsIsAlive.set(e,!0),e.on("pong",()=>{this.wsIsAlive.set(e,!0)}),this.agentHost.registerSocket(e),r==="sim")n={id:this.allocateSimId(),ws:e,origin:i,connectedAt:Date.now(),lastSeenAt:Date.now(),lastActiveAt:0,recentActions:[]},this.sims.set(n.id,n),this.writeConnectedRuntimeSnapshot(),this.shouldPromoteSim(n)&&(this.primarySimId=n.id),this.broadcastSimAssignments(),this.broadcastSimClientStates();else{let c=`ws-${this.nextCliFallbackId++}`;this.cliIdentityKeyBySocket.set(e,c)}e.on("message",c=>{let s;try{s=JSON.parse(c.toString())}catch{return}if(!(!s||typeof s!="object")){if(typeof s.type=="string"&&s.type.startsWith("agent:")){this.agentHost.handleMessage(e,s);return}if(typeof s.type=="string"&&s.type.startsWith("plane:")){this.planeHost.handleMessage(e,s);return}if(s.type==="runtime:list"){let a=M.listInstalled(),l=this.getActiveRuntime(),d={type:"runtime:list:ok",id:s.id,installed:a,active:l.version,activeRuntimeDir:l.runtimeDir};try{e.send(JSON.stringify(d))}catch{}return}if(s.type==="runtime:use"){let a=typeof s.version=="string"?s.version:"";if(!M.listInstalled().includes(a)){try{e.send(JSON.stringify({type:"runtime:use:error",id:s.id,error:`runtime ${a||"(missing)"} is not installed`}))}catch{}return}let d=this.setActiveRuntime(a);try{e.send(JSON.stringify({type:"runtime:use:ok",id:s.id,version:d.version,runtimeDir:d.runtimeDir}))}catch{}return}if(s.type==="runtime:get"){let a=this.getActiveRuntime();try{e.send(JSON.stringify({type:"runtime:get:ok",id:s.id,active:a.version,activeRuntimeDir:a.runtimeDir}))}catch{}return}if(r==="sim"&&s.type==="bridge:hello"&&n&&!n.url){let a=n.id;this.sims.delete(a),this.primarySimId===a&&(this.primarySimId=this.getOpenSim()?.id??null),n=null,r="cli",this.cliIdentityKeyBySocket.set(e,`ws-${this.nextCliFallbackId++}`),this.writeConnectedRuntimeSnapshot(),this.broadcastSimAssignments(),this.broadcastSimClientStates()}if(r==="sim"){if(n&&(n.lastSeenAt=Date.now()),s.type==="bridge:register"&&n){let d=s,u=this.tryRestoreSimId(n,d.simId);n.url=d.url,n.title=d.title,n.userAgent=d.userAgent,this.writeConnectedRuntimeSnapshot(),typeof d.kind=="string"&&d.kind.trim()&&(n.kind=d.kind.trim()),d.meta&&typeof d.meta=="object"&&(n.meta=d.meta);let p=this.primarySimId!==n.id&&this.shouldPromoteSim(n);p&&(this.primarySimId=n.id),(u||p)&&(this.broadcastSimAssignments(),this.broadcastSimClientStates());return}if(s.type==="bridge:user-focus-state"&&n){let d=s;this.updateUserFocusLease(n,d);return}if(s.type==="bridge:user-interact"&&n){this.updateUserActivity(n);return}if(s.type==="bridge:write-shared-config"){if(!s.patch||typeof s.patch!="object"||Array.isArray(s.patch))return;let d=Object.fromEntries(Object.entries(s.patch));try{this.writeAndBroadcastSharedConfig(d)}catch(u){process.stderr.write(`sootsim: bridge:write-shared-config failed: ${u instanceof Error?u.message:String(u)}
28
- `);return}return}if(s.type==="bridge:open-path"){let d=typeof s.path=="string"?s.path:"",u=typeof s.line=="number"&&Number.isFinite(s.line)?s.line:void 0,p=typeof s.column=="number"&&Number.isFinite(s.column)?s.column:void 0;d&&this.openPathInEditor(d,u,p);return}if(s.type==="bridge:boot-clients"&&n){let d=[];for(let[p,h]of this.cliSimBySocket)h===n.id&&d.push(p);for(let p of d){this.cliSimBySocket.delete(p);try{p.close(1e3,"booted by sim")}catch{}}let u=!!n.cliLease;n.cliLease={kind:"user-active",cliIdentityKey:"__user-active__",cliLabel:"active user",expiresAt:Date.now()+o.USER_BOOT_LEASE_TTL_MS},process.stderr.write(`rnx booted ${d.length} cli client(s)${u?" (overrode prior lease)":""}; held sim for user [${n.id}]
29
- `),this.recordSimAction(n.id,"sim booted cli clients"),this.broadcastSimClientStates();return}let a=this.pendingCommands.get(s.id);if(a){this.pendingCommands.delete(s.id),s.error?a.reject(new Error(s.error)):a.resolve(s.result);return}let l=this.cliBySentId.get(s.id);if(l&&(this.cliBySentId.delete(s.id),l.ws.readyState===g.OPEN)){let d=this.getOtherCliIdentityCount(l.ws,l.simId),u=d>0?{...s,id:l.originalId,_otherCliCount:d}:{...s,id:l.originalId};l.ws.send(JSON.stringify(u))}return}(async()=>{this.cliLastCommandAt.set(e,Date.now());try{if(s.type==="bridge:bye"){let p=this.cliSimBySocket.delete(e);this.cliLastCommandAt.delete(e),this.cliIdentityKeyBySocket.delete(e),this.cliLabelBySocket.delete(e);for(let[h,m]of this.cliBySentId)m.ws===e&&this.cliBySentId.delete(h);p&&this.broadcastSimClientStates();return}if(s.type==="bridge:hello"){let p=typeof s.cliIdentityKey=="string"&&s.cliIdentityKey.trim()?s.cliIdentityKey.trim():this.cliIdentityKeyBySocket.get(e)||`ws-${this.nextCliFallbackId++}`;this.cliIdentityKeyBySocket.set(e,p),typeof s.cliLabel=="string"&&s.cliLabel.trim()&&this.cliLabelBySocket.set(e,s.cliLabel.trim()),e.readyState===g.OPEN&&e.send(JSON.stringify({id:s.id,result:{cliIdentityKey:p,leaseTtlMs:o.CLI_LEASE_TTL_MS,leasing:!0}}));return}if(s.type==="bridge:list-sims"){e.readyState===g.OPEN&&e.send(JSON.stringify({id:s.id,result:this.listSims()}));return}if(s.type==="bridge:claim"){let p=await this.waitForSim(s.simId),h=this.tryAcquireLease(e,p,{force:s.force===!0});if(!h.granted){e.readyState===g.OPEN&&e.send(JSON.stringify({id:s.id,error:`sim ${p.id} is locked by another cli`,_locked:h.lock}));return}this.setCliSimTarget(e,p.id),this.recordSimAction(p.id,h.bootedCount>0?`cli force-claimed sim (booted ${h.bootedCount})`:"cli claimed sim"),e.readyState===g.OPEN&&e.send(JSON.stringify({id:s.id,result:{simId:p.id,lockedBy:h.lease.cliIdentityKey,lockExpiresAt:h.lease.expiresAt,bootedCount:h.bootedCount}}));return}let a=await this.waitForSim(s.simId);if(Kt(s)){let p=this.tryAcquireLease(e,a);if(!p.granted){e.readyState===g.OPEN&&e.send(JSON.stringify({id:s.id,error:`sim ${a.id} is locked by another cli \u2014 use \`rnx claim ${a.id} --force\` or \`rnx open --new\``,_locked:p.lock}));return}}else this.ensureCliIdentityKey(e);this.setCliSimTarget(e,a.id),this.recordSimAction(a.id,this.describeForwardedCommand(s));let l=this.nextCommandId++;this.cliBySentId.set(l,{simId:a.id,ws:e,originalId:s.id});let{simId:d,...u}=s;if(a.ws.send(JSON.stringify({...u,id:l})),u.type==="close"){this.cliBySentId.delete(l),e.readyState===g.OPEN&&e.send(JSON.stringify({id:s.id,result:{requested:!0,simId:a.id}}));let p=a.ws,h=setTimeout(()=>{this.closeSimSocketFromHost(p)},Jt);ot(h)}}catch(a){e.readyState===g.OPEN&&e.send(JSON.stringify({id:s.id,error:a instanceof Error?a.message:String(a)}))}})()}}),e.on("close",()=>{if(this.agentHost.unregisterSocket(e),this.planeHost.unregisterSocket(e),r==="sim"&&n){this.rememberDisconnectedSim(n),this.primarySimId===n.id&&(this.primarySimId=this.getOpenSim()?.id??null);for(let[c,s]of this.pendingCommands)s.simId===n.id&&(s.reject(new Error("sim disconnected")),this.pendingCommands.delete(c));for(let[c,s]of this.cliBySentId)s.simId===n.id&&(s.ws.readyState===g.OPEN&&s.ws.send(JSON.stringify({id:s.originalId,error:"sim disconnected before responding"})),this.cliBySentId.delete(c));this.broadcastSimAssignments(),this.broadcastSimClientStates()}else if(r==="cli"){let c=this.cliSimBySocket.delete(e);this.cliLastCommandAt.delete(e),this.cliIdentityKeyBySocket.delete(e),this.cliLabelBySocket.delete(e);for(let[s,a]of this.cliBySentId)a.ws===e&&this.cliBySentId.delete(s);c&&this.broadcastSimClientStates()}})})}afterBind(){if(process.stderr.write(`ws bridge listening on port ${this.port}
30
- `),this.cliIdleTimer=setInterval(()=>this.sweepIdleCliClients(),3e4),this.cliIdleTimer.unref(),this.wsHeartbeatTimer=setInterval(()=>this.sweepDeadWebSockets(),o.WS_HEARTBEAT_INTERVAL_MS),this.wsHeartbeatTimer.unref(),this.shouldWriteLockfile){try{if(L(),!ue(this.buildLockfileSnapshot()))throw new Error("another rnx daemon wrote the lockfile during startup \u2014 aborting")}catch(e){throw process.stderr.write(`ws bridge failed to claim daemon lockfile: ${String(e)}
31
- `),e}this.heartbeatTimer=setInterval(()=>{try{this.writeLockfileSnapshot()}catch{}},it),this.heartbeatTimer.unref(),this.startRuntimeUpdater()}if(this.shouldWriteDevLockfile){try{this.writeDevLockfileSnapshot()}catch(e){process.stderr.write(`ws bridge failed to write dev bridge lockfile: ${String(e)}
32
- `)}this.devHeartbeatTimer=setInterval(()=>{try{this.writeDevLockfileSnapshot()}catch{}},it),this.devHeartbeatTimer.unref()}this.agentHost.seedOnBoot()}bootstrapping=!0;connectedRuntimeVersions(){let e=new Set;for(let t of this.sims.values())try{let i=new URL(t.url||t.origin||"http://localhost"),r=D(i.hostname);r&&e.add(r)}catch{}return[...e].sort()}buildLockfileSnapshot(){return{schema:1,pid:process.pid,platform:process.platform,bridgePort:this.effectivePort,runtimePort:this.effectivePort,activeRuntime:this.activeRuntimeVersion,activeRuntimeDir:this.activeRuntimeDirPath,servedRuntimes:this.connectedRuntimeVersions(),startedAt:this.startedAt,heartbeatAt:Date.now(),bootstrapping:this.bootstrapping}}buildDevLockfileSnapshot(){let e=this.getShellPort?.()??null;return{schema:1,pid:process.pid,platform:process.platform,bridgePort:this.effectivePort,runtimePort:this.effectivePort,...e&&e>0?{shellPort:e}:{},cwd:process.cwd(),startedAt:this.startedAt,heartbeatAt:Date.now(),source:"vite-dev",servedRuntimes:this.connectedRuntimeVersions()}}writeLockfileSnapshot(){de(this.buildLockfileSnapshot())}writeDevLockfileSnapshot(){le(this.buildDevLockfileSnapshot())}writeConnectedRuntimeSnapshot(){if(this.shouldWriteLockfile&&this.httpServer)try{this.writeLockfileSnapshot()}catch{}if(this.shouldWriteDevLockfile&&this.httpServer)try{this.writeDevLockfileSnapshot()}catch{}}refreshActiveRuntime(){this.activeRuntimeVersion=K(),this.activeRuntimeDirPath=ge()??oe()}runServerScan(){if(this.inflightScan)return this.inflightScan;let e=this.effectivePort>0?[this.effectivePort]:[];return this.inflightScan=B({excludePorts:e,buildIconProxyUrl:t=>`/__bundle-proxy?url=${encodeURIComponent(t)}`}).then(t=>(this.scanCache=t,this.scanCacheAt=Date.now(),t)).catch(t=>{let i=t instanceof Error?t.message:String(t);return console.error("[rnx] /__server-scan failed:",i),this.scanCache??[]}).finally(()=>{this.inflightScan=null}),this.inflightScan}handleCameraFixture(e,t,i){let r=(t.method||"GET").toUpperCase(),n={"Access-Control-Allow-Origin":"*","Cache-Control":"no-store"};if(r==="OPTIONS"){i.writeHead(204,{...n,"Access-Control-Allow-Methods":"GET, HEAD, OPTIONS","Access-Control-Allow-Headers":"range"}),i.end();return}if(r!=="GET"&&r!=="HEAD"){i.writeHead(405,n),i.end();return}if(!qt.test(e)){i.writeHead(400,{...n,"Content-Type":"text/plain; charset=utf-8"}),i.end("bad fixture name");return}let c=ie(),s=v.join(c,e),a;try{a=w.lstatSync(s)}catch{i.writeHead(404,{...n,"Content-Type":"text/plain; charset=utf-8"}),i.end("not found");return}if(!a.isFile()){i.writeHead(403,{...n,"Content-Type":"text/plain; charset=utf-8"}),i.end("forbidden");return}let l=rt[v.extname(s).toLowerCase()]||"application/octet-stream",d=a.size,u=/^bytes=(\d*)-(\d*)$/.exec(t.headers.range||"");if(u){let[,p,h]=u,m=p?Number(p):0,S=h?Math.min(Number(h),d-1):d-1;if(!Number.isFinite(m)||m>S||m>=d){i.writeHead(416,{...n,"Content-Range":`bytes */${d}`}),i.end();return}if(i.writeHead(206,{...n,"Content-Type":l,"Content-Range":`bytes ${m}-${S}/${d}`,"Accept-Ranges":"bytes","Content-Length":String(S-m+1)}),r==="HEAD"){i.end();return}w.createReadStream(s,{start:m,end:S}).pipe(i);return}if(i.writeHead(200,{...n,"Content-Type":l,"Accept-Ranges":"bytes","Content-Length":String(d)}),r==="HEAD"){i.end();return}w.createReadStream(s).pipe(i)}handleServerScan(e){let t=r=>{e.writeHead(200,{"Content-Type":"application/json; charset=utf-8","Cache-Control":"no-store"}),e.end(JSON.stringify(r))},i=Date.now()-this.scanCacheAt;if(this.scanCache&&i<o.SCAN_FRESH_MS){t(this.scanCache);return}if(this.scanCache){t(this.scanCache),this.runServerScan().catch(()=>{});return}this.runServerScan().then(r=>t(r))}resolveRuntimeUpdateIntervalMs(){let e=Number(process.env[zt]);return Number.isFinite(e)&&e>0?Math.max(100,Math.round(e)):Gt}startRuntimeUpdater(){if(!this.shouldWriteLockfile||this.runtimeUpdateTimer||w.existsSync(v.join(ee(),"runtime-update-disabled"))||process.env.SOOTSIM_HOME&&te()&&!K())return;this.runRuntimeUpdate("startup");let e=this.resolveRuntimeUpdateIntervalMs();this.runtimeUpdateTimer=setInterval(()=>{this.runRuntimeUpdate("periodic")},e),this.runtimeUpdateTimer.unref()}runRuntimeUpdate(e){return this.runtimeUpdateInFlight?this.runtimeUpdateInFlight:(this.runtimeUpdateInFlight=(async()=>{try{e==="startup"&&process.stderr.write(`rnx: checking for runtime updates\u2026
33
- `);let t=await M.updateToLatest({protectVersions:this.connectedRuntimeVersions()});if(!t.updated||!t.latestVersion){e==="startup"&&process.stderr.write(`rnx: runtime ${this.activeRuntimeVersion??"(none)"} is current
34
- `);return}let i=this.activeRuntimeVersion,r=this.setActiveRuntime(t.latestVersion);process.stderr.write(`rnx runtime updated to ${r.version} (${e})
35
- `);try{ne({from:i,to:r.version,at:Date.now()})}catch{}}catch(t){process.stderr.write(`rnx runtime update failed (${e}): ${t instanceof Error?t.message:String(t)}
36
- `)}finally{if(this.runtimeUpdateInFlight=null,e==="startup"&&this.bootstrapping){if(this.bootstrapping=!1,this.shouldWriteLockfile&&this.httpServer)try{this.writeLockfileSnapshot()}catch{}process.stderr.write(`rnx: ready
37
- `)}}})(),this.runtimeUpdateInFlight)}setActiveRuntime(e){if(se(e),this.refreshActiveRuntime(),this.shouldWriteLockfile&&this.httpServer)try{this.writeLockfileSnapshot()}catch{}let t=JSON.stringify({type:"runtime:changed",version:e,runtimeDir:this.activeRuntimeDirPath});for(let i of this.sims.values()){try{let r=new URL(i.url||i.origin||"http://localhost");if(D(r.hostname))continue}catch{}if(i.ws.readyState===g.OPEN)try{i.ws.send(t)}catch{}}return{version:e,runtimeDir:this.activeRuntimeDirPath}}getActiveRuntime(){return{version:this.activeRuntimeVersion,runtimeDir:this.activeRuntimeDirPath}}removeLockfile(){if(this.shouldWriteLockfile)try{G({pid:process.pid,startedAt:this.startedAt})}catch{}}handleHttpRequest(e,t){if(t.setHeader("Cross-Origin-Opener-Policy","same-origin"),t.setHeader("Cross-Origin-Embedder-Policy",Wt),t.setHeader("Cross-Origin-Resource-Policy","cross-origin"),t.setHeader("Document-Policy","js-profiling"),Ve(e.url)){Q(e,t);return}if(Ke(e.url)&&ze(e,t))return;if(Qe(e.url)){et(e,t);return}let i=(e.method||"GET").toUpperCase(),r;try{r=new URL(e.url||"/",`http://${e.headers.host||"localhost"}`)}catch{t.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}),t.end("invalid request URL or host");return}if(r.pathname==="/__sootsim/shared-config"){if(t.setHeader("Access-Control-Allow-Origin","*"),t.setHeader("Access-Control-Allow-Headers","Content-Type"),t.setHeader("Cache-Control","no-store"),i==="OPTIONS"){t.writeHead(204,{Allow:"GET, HEAD, POST, OPTIONS"}),t.end();return}if(i==="GET"||i==="HEAD"){let u="{}";try{u=JSON.stringify(V())}catch{}t.writeHead(200,{"Content-Type":"application/json"}),i==="HEAD"?t.end():t.end(u);return}if(i==="POST"){(async()=>{try{let u=[],p=0;for await(let f of e){let b=Buffer.isBuffer(f)?f:Buffer.from(f);if(p+=b.byteLength,p>8*1024*1024){t.writeHead(413,{"Content-Type":"text/plain; charset=utf-8"}),t.end("shared config patch is too large");return}u.push(b)}let h=JSON.parse(Buffer.concat(u).toString("utf8"));if(!h||typeof h!="object"||Array.isArray(h))throw new Error("shared config patch must be an object");let m=Object.fromEntries(Object.entries(h)),S=this.writeAndBroadcastSharedConfig(m);t.writeHead(200,{"Content-Type":"application/json"}),t.end(JSON.stringify(S))}catch(u){t.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}),t.end(`invalid shared config patch: ${u instanceof Error?u.message:String(u)}`)}})();return}t.writeHead(405,{Allow:"GET, HEAD, POST, OPTIONS"}),t.end("method not allowed");return}if(r.pathname.startsWith(nt)){this.handleCameraFixture(r.pathname.slice(nt.length),e,t);return}if(i!=="GET"&&i!=="HEAD"){t.writeHead(405,{Allow:"GET, HEAD"}),t.end("method not allowed");return}if(r.pathname==="/__bundle-proxy"){Ge(e,t);return}if(r.pathname==="/__server-scan"){this.handleServerScan(t);return}if(r.pathname==="/healthz"){t.writeHead(200,{"Content-Type":"application/json","Cache-Control":"no-store"}),t.end(JSON.stringify({ok:!0,pid:process.pid,platform:process.platform,bridgePort:this.effectivePort,runtimePort:this.effectivePort,activeRuntime:this.activeRuntimeVersion,bridgeSource:this.shouldWriteDevLockfile?"vite-dev":this.shouldWriteLockfile?"daemon":"embedded",startedAt:this.startedAt,uptimeMs:this.startedAt>0?Date.now()-this.startedAt:0}));return}let n=Se(r.hostname),c=D(r.hostname);n||this.refreshActiveRuntime();let s=n?ye(r.hostname):this.activeRuntimeDirPath;if(!s){t.writeHead(503,{"Content-Type":"text/plain; charset=utf-8"}),t.end(n?`rnx: runtime ${c??"(invalid version origin)"} is not installed. run \`rnx open\` from that app again.`:"rnx: no active runtime installed. run `rnx runtime install` to fetch one.");return}let a=r.pathname;if(a==="/runtime"||a==="/runtime/"?a="/":a.startsWith("/runtime/")?a=a.slice(8):a==="/sootsim"||a==="/sootsim/"?a="/":a.startsWith("/sootsim/")&&(a=a.slice(8)),(a===""||a==="/")&&(a="/index.html"),a.includes("\0")){t.writeHead(400),t.end("bad request");return}if(process.platform!=="win32"&&a.includes("\\")){t.writeHead(400),t.end("bad request");return}for(let u of a.split("/"))if(u===".."){t.writeHead(403),t.end("forbidden");return}let l=v.resolve(s,"."+a),d=s.endsWith(v.sep)?s:s+v.sep;if(!l.startsWith(d)&&l!==s){t.writeHead(403),t.end("forbidden");return}w.realpath(l,(u,p)=>{let h=u?l:p,m=h.endsWith(v.sep)?h:h+v.sep;if(!u){let S=(()=>{try{let f=w.realpathSync(s);return f.endsWith(v.sep)?f:f+v.sep}catch{return d}})();if(!m.startsWith(S)&&h+v.sep!==S){t.writeHead(403),t.end("forbidden");return}}w.stat(h,(S,f)=>{if(S||!f?.isFile()){let y=v.extname(a).toLowerCase();if(y&&y!==".html"){t.writeHead(404),t.end("not found");return}if(a.startsWith("/__")||a.startsWith("/api/")||a==="/api"){t.writeHead(404,{"Content-Type":"text/plain; charset=utf-8"}),t.end("not found");return}let R=v.join(s,"index.html");w.readFile(R,(at,ct)=>{if(at){t.writeHead(404),t.end("not found");return}if(t.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),i==="HEAD"){t.end();return}t.end(st(ct,this.effectivePort,this.contrastOrigin))});return}let b=v.extname(h).toLowerCase(),A=rt[b]||"application/octet-stream";if(t.writeHead(200,{"Content-Type":A,"Cache-Control":"no-store"}),i==="HEAD"){t.end();return}if(b===".html"){w.readFile(h,(y,R)=>{if(y){try{t.end()}catch{}return}t.end(st(R,this.effectivePort,this.contrastOrigin))});return}let k=w.createReadStream(h);k.pipe(t),k.on("error",()=>{try{t.end()}catch{}})})})}sweepIdleCliClients(){let e=Date.now(),t=!1;for(let[i,r]of this.cliSimBySocket){let n=this.cliLastCommandAt.get(i)??0;if(e-n<o.CLI_IDLE_TIMEOUT_MS)continue;let c=!1;for(let s of this.cliBySentId.values())if(s.ws===i){c=!0;break}if(!c){this.cliSimBySocket.delete(i),this.cliLastCommandAt.delete(i);for(let[s,a]of this.cliBySentId)a.ws===i&&this.cliBySentId.delete(s);try{i.close(1e3,"idle timeout")}catch{}t=!0}}t&&this.broadcastSimClientStates(),this.sweepRestorableSims(e),this.reapIdleSims(e)}isAutomationSim(e){return e.meta?.sootsimHostDriver==="playwright"}automationSimOwnerAlive(e){let t=Number(e.meta?.sootsimHostPid);if(!Number.isInteger(t)||t<=1||t===process.pid)return!1;try{return process.kill(t,0),!0}catch(i){return i.code==="EPERM"}}reapIdleSims(e=Date.now()){let t=new Set(this.cliSimBySocket.values()),i=[];for(let n of this.sims.values()){if(n.id===this.primarySimId||t.has(n.id)||this.getActiveLease(n))continue;let c=Math.max(n.lastActiveAt,n.connectedAt),s=e-c,a=this.isAutomationSim(n);if(a&&this.automationSimOwnerAlive(n))continue;let l=a?this.automationSimIdleReapTtlMs:this.simIdleReapTtlMs;if(s>=l){this.closeSimSocketFromHost(n.ws);continue}a&&i.push({sim:n,idleMs:s})}let r=this.maxConcurrentAutomationSims;if(i.length>r){i.sort((n,c)=>c.idleMs-n.idleMs);for(let{sim:n,idleMs:c}of i.slice(0,i.length-r))c<this.automationSimActiveGraceMs||this.closeSimSocketFromHost(n.ws)}}sweepDeadWebSockets(){if(this.wss)for(let e of this.wss.clients){if(e.readyState!==g.OPEN)continue;if(this.wsIsAlive.get(e)===!1){try{e.terminate()}catch{}continue}this.wsIsAlive.set(e,!1);try{e.ping()}catch{try{e.terminate()}catch{}}}}closeSimSocketFromHost(e){if(e.readyState!==g.OPEN)return;try{e.close(me,fe)}catch{try{e.terminate()}catch{}return}let t=setTimeout(()=>{if(e.readyState!==g.CLOSED)try{e.terminate()}catch{}},Vt);ot(t)}listSims(){return Array.from(this.sims.values()).sort((e,t)=>e.id===this.primarySimId?-1:t.id===this.primarySimId?1:e.connectedAt-t.connectedAt).map(e=>this.describeSim(e))}async sendCommand(e){let t=await this.waitForSim(e.simId),i=this.nextCommandId++;return new Promise((r,n)=>{let c=setTimeout(()=>{this.pendingCommands.delete(i),this.broadcastSimClientStates(),n(new Error("command timed out after 30s"))},3e4);this.pendingCommands.set(i,{simId:t.id,resolve:l=>{clearTimeout(c),this.pendingCommands.delete(i),this.broadcastSimClientStates(),r(l)},reject:l=>{clearTimeout(c),this.pendingCommands.delete(i),this.broadcastSimClientStates(),n(l)}}),this.broadcastSimClientStates();let{simId:s,...a}=e;t.ws.send(JSON.stringify({...a,id:i}))})}async evaluate(e,t){return this.sendCommand({type:"evaluate",code:e,simId:t})}async focusSim(e){return this.sendCommand({type:"focus",simId:e})}async closeSim(e){return this.sendCommand({type:"close",simId:e})}async openPathInEditor(e,t,i){let r=t!=null?`:${t}${i!=null?`:${i}`:""}`:"",n=`${e}${r}`,c=(a,l)=>new Promise(d=>{try{let u=Ut(a,l,{detached:!0,stdio:"ignore"}),p=!1;u.on("error",()=>{p||(p=!0,d(!1))}),u.on("spawn",()=>{p||(p=!0,u.unref(),d(!0))})}catch{d(!1)}}),s=process.env.REACT_EDITOR||process.env.EDITOR;if(s){let a=s.split(" ").filter(Boolean);if(a.length&&await c(a[0],[...a.slice(1),"-g",n]))return}await c("cursor",["-g",n])||await c("code",["-g",n])||await this.openUrl(e)}async openUrl(e,t={}){if(this.openUrlHandler){await this.openUrlHandler(e,t);return}await ve(e,t)}async close(){if(this.cliIdleTimer&&(clearInterval(this.cliIdleTimer),this.cliIdleTimer=null),this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),this.devHeartbeatTimer&&(clearInterval(this.devHeartbeatTimer),this.devHeartbeatTimer=null),this.wsHeartbeatTimer&&(clearInterval(this.wsHeartbeatTimer),this.wsHeartbeatTimer=null),this.runtimeUpdateTimer&&(clearInterval(this.runtimeUpdateTimer),this.runtimeUpdateTimer=null),this.shouldWriteLockfile)try{G({pid:process.pid,startedAt:this.startedAt})}catch{}if(this.shouldWriteDevLockfile)try{pe({bridgePort:this.effectivePort,pid:process.pid,startedAt:this.startedAt})}catch{}this.effectivePort=0,this.startedAt=0,this.agentHost.close();for(let[i,r]of this.pendingCommands)r.reject(new Error("server closing")),this.pendingCommands.delete(i);for(let i of this.sims.values())i.ws.close();this.sims.clear(),this.primarySimId=null;let e=this.wss,t=this.httpServer;if(this.wss=null,this.httpServer=null,e)try{e.close()}catch{}if(t)try{t.close()}catch{}}describeSim(e){let t;try{t=e.ws.readyState}catch{t=g.CLOSED}let i=this.getActiveLease(e);return{id:e.id,origin:e.origin,url:e.url,title:e.title,userAgent:e.userAgent,connectedAt:e.connectedAt,lastSeenAt:e.lastSeenAt,lastActiveAt:e.lastActiveAt||void 0,isPrimary:e.id===this.primarySimId,readyState:t===g.OPEN?"open":t===g.CLOSING?"closing":"closed",attachedCliCount:this.getAttachedCliCount(e.id),lockedBy:i?i.cliLabel||i.cliIdentityKey:void 0,lockedByKind:i?i.kind:void 0,lockExpiresAt:i?i.expiresAt:void 0,userFocused:e.userFocused||void 0,userVisible:e.userVisible,visibilityState:e.visibilityState,documentFocused:e.documentFocused,kind:e.kind,meta:e.meta}}getActiveLease(e){let t=e.cliLease;return t?Date.now()>=t.expiresAt?(e.cliLease=void 0,null):t:null}tryAcquireLease(e,t,i={}){let r=this.cliIdentityKeyBySocket.get(e)??(()=>{let u=`ws-${this.nextCliFallbackId++}`;return this.cliIdentityKeyBySocket.set(e,u),u})(),n=this.cliLabelBySocket.get(e),c=Date.now(),s=this.getActiveLease(t),a=s&&s.cliIdentityKey===r,l=0;if(s&&!a&&!i.force)return{granted:!1,lease:s,lock:{by:s.cliLabel||s.cliIdentityKey,expiresInMs:Math.max(0,s.expiresAt-c)},bootedCount:0};if(s&&!a&&i.force)for(let[u,p]of this.cliSimBySocket){if(p!==t.id)continue;let h=this.cliIdentityKeyBySocket.get(u);if(h&&h!==r){this.cliSimBySocket.delete(u);try{u.close(1e3,"lease claimed by another cli")}catch{}l++}}let d={kind:"cli",cliIdentityKey:r,cliLabel:n,expiresAt:c+o.CLI_LEASE_TTL_MS};return t.cliLease=d,{granted:!0,lease:d,bootedCount:l}}updateUserFocusLease(e,t){let i=t.focused===!0,r=typeof t.visible=="boolean"?t.visible:void 0,n=typeof t.visibilityState=="string"?t.visibilityState:void 0,c=typeof t.documentFocused=="boolean"?t.documentFocused:void 0;e.userFocused===i&&e.userVisible===r&&e.visibilityState===n&&e.documentFocused===c||(e.userFocused=i,e.userVisible=r,e.visibilityState=n,e.documentFocused=c,this.broadcastSimClientStates())}updateUserActivity(e){let t=this.getActiveLease(e);if(t&&t.kind==="cli")return;let r=Date.now()+o.USER_ACTIVE_LEASE_TTL_MS,n=t&&t.kind==="user-active"?Math.max(t.expiresAt,r):r;e.cliLease={kind:"user-active",cliIdentityKey:"__user-active__",cliLabel:"active user",expiresAt:n},this.broadcastSimClientStates()}ensureCliIdentityKey(e){let t=this.cliIdentityKeyBySocket.get(e);if(t)return t;let i=`ws-${this.nextCliFallbackId++}`;return this.cliIdentityKeyBySocket.set(e,i),i}getOpenSim(e){if(e){let i=this.sims.get(e);return i?.ws.readyState===g.OPEN?i:null}let t=this.getDefaultSimCandidates();return t.find(i=>i.id===this.primarySimId)??t[0]??null}getDefaultSimCandidates(){let e=Array.from(this.sims.values()).filter(i=>i.ws.readyState===g.OPEN),t=e.filter(i=>i.url);return t.length>0?t:e}async waitForSim(e,t={}){let i=t.attempts??10,r=t.intervalMs??200;for(let c=0;c<i;c++){if(e){let s=this.getOpenSim(e);if(s)return s}else{let s=this.getDefaultSimCandidates();if(s.length>1)throw new Error(`multiple sims are connected: ${s.map(l=>l.id).join(", ")}; run \`rnx use <sim>\` or pass \`--sim <sim>\``);let a=s[0];if(a)return a}await new Promise(s=>setTimeout(s,r))}if(!e)throw new Error("no sim connected");let n=this.getDefaultSimCandidates().map(c=>c.id);throw new Error(`no sim connected with id ${e}`+(n.length>0?`; connected sims: ${n.join(", ")}`:""))}shouldPromoteSim(e){let t=this.primarySimId?this.sims.get(this.primarySimId):null;if(!e.url)return!t;let i=t?.ws.readyState===g.OPEN;if(!t||!i||!t.url)return!0;let r=e.origin?.includes(":5173"),n=t.origin?.includes(":5173");return!!r||!n}broadcastSimAssignments(){for(let e of this.sims.values())e.ws.readyState===g.OPEN&&e.ws.send(JSON.stringify({type:"bridge:welcome",simId:e.id,isPrimary:e.id===this.primarySimId}))}writeAndBroadcastSharedConfig(e){let t=re(e),i=JSON.stringify({type:"bridge:shared-config-changed",config:t});for(let r of this.sims.values())if(r.ws.readyState===g.OPEN)try{r.ws.send(i)}catch{}return t}broadcastSimClientStates(){for(let e of this.sims.values()){if(e.ws.readyState!==g.OPEN)continue;let t=this.getActiveLease(e),i={type:"bridge:client-state",attachedCliCount:this.getAttachedCliCount(e.id),activeAgentCommandCount:this.getActiveAgentCommandCount(e.id),recentActions:e.recentActions,lockedBy:t?t.cliLabel||t.cliIdentityKey:void 0,lockedByKind:t?t.kind:void 0,lockExpiresAt:t?t.expiresAt:void 0,userFocused:e.userFocused||void 0,userVisible:e.userVisible,visibilityState:e.visibilityState,documentFocused:e.documentFocused};e.ws.send(JSON.stringify(i))}}setCliSimTarget(e,t){let i=this.cliSimBySocket.get(e);i!==t&&(this.cliSimBySocket.set(e,t),this.recordSimAction(t,i?"cli switched sims":"cli connected",!1),this.broadcastSimClientStates())}recordSimAction(e,t,i=!0){let r=t?.trim();if(!r)return;let n=this.sims.get(e);if(!n)return;let c=Date.now();n.lastActiveAt=c,n.recentActions=[{label:r,at:c},...n.recentActions.filter(s=>s.label!==r)].slice(0,4),i&&this.broadcastSimClientStates()}describeForwardedCommand(e){switch(e?.type){case"evaluate":return"evaluated page state";case"screenshot":return"captured screenshot";case"tap":return"sent tap event";case"keyboard":return e?.action==="type"?"typed text":"used keyboard";case"tree":return"dumped tree";case"query":return"queried semantic tree";case"resolve":return"resolved semantic selector";case"focus":return"focused sim";case"close":return"requested close";default:return typeof e?.type=="string"?e.type:null}}getAttachedCliCount(e){let t=new Set;for(let[i,r]of this.cliSimBySocket){if(r!==e||i.readyState!==g.OPEN)continue;let n=this.cliIdentityKeyBySocket.get(i);t.add(n??`ws-unknown-${t.size}`)}return t.size}getOtherCliIdentityCount(e,t){let i=this.cliIdentityKeyBySocket.get(e),r=new Set;for(let[n,c]of this.cliSimBySocket){if(c!==t||n.readyState!==g.OPEN)continue;let s=this.cliIdentityKeyBySocket.get(n);s&&s===i||r.add(s??`ws-unknown-${r.size}`)}return r.size}getActiveAgentCommandCount(e){let t=0;for(let i of this.pendingCommands.values())i.simId===e&&t++;return t}allocateSimId(){for(;;){let e=this.nextSimNumber.toString(16);if(this.nextSimNumber++,!this.sims.has(e)&&!this.restorableSims.has(e))return e}}tryRestoreSimId(e,t){let i=t?.trim();if(!i||i===e.id)return!1;let r=this.sims.get(i);if(r&&r!==e&&r.ws.readyState===g.OPEN)return!1;let n=this.getRestorableSimState(i),c=e.id;this.sims.delete(c),e.id=i,n&&(e.recentActions=n.recentActions.map(s=>({...s})),e.lastActiveAt=n.lastActiveAt,e.cliLease=n.cliLease?{...n.cliLease}:void 0,this.restorableSims.delete(i)),this.sims.set(e.id,e),this.primarySimId===c&&(this.primarySimId=e.id);for(let[s,a]of this.cliSimBySocket)a===c&&this.cliSimBySocket.set(s,e.id);return!0}rememberDisconnectedSim(e){let t=this.getActiveLease(e);this.restorableSims.set(e.id,{recentActions:e.recentActions.map(i=>({...i})),lastActiveAt:e.lastActiveAt,cliLease:t&&t.kind==="cli"?{...t}:void 0,expiresAt:Date.now()+o.SIM_RECONNECT_TTL_MS}),this.sims.delete(e.id),this.writeConnectedRuntimeSnapshot()}getRestorableSimState(e){let t=this.restorableSims.get(e);return t?t.expiresAt<=Date.now()?(this.restorableSims.delete(e),null):(t.cliLease&&t.cliLease.expiresAt<=Date.now()&&(t.cliLease=void 0),t):null}sweepRestorableSims(e=Date.now()){for(let[t,i]of this.restorableSims)if(!(i.expiresAt>e)){this.restorableSims.delete(t);for(let[r,n]of this.cliSimBySocket)n===t&&this.cliSimBySocket.delete(r)}}resetServerState(){this.cliIdleTimer&&(clearInterval(this.cliIdleTimer),this.cliIdleTimer=null),this.wsHeartbeatTimer&&(clearInterval(this.wsHeartbeatTimer),this.wsHeartbeatTimer=null),this.runtimeUpdateTimer&&(clearInterval(this.runtimeUpdateTimer),this.runtimeUpdateTimer=null);let e=this.wss,t=this.httpServer;if(this.wss=null,this.httpServer=null,e)try{e.close()}catch{}if(t)try{t.close()}catch{}}};async function Yi(o,e={}){(o.includes("--help")||o.includes("-h"))&&(console.log(`
38
- rnx serve \u2014 run the rnx bridge in the foreground
39
-
40
- hosts the WS bridge that CLI commands talk to. once running, any rnx
41
- renderer (browser, electron, headless playwright) that connects to port 7668
42
- becomes drivable from 'rnx describe', 'rnx do tap', etc.
43
-
44
- usage:
45
- rnx serve [options]
46
-
47
- options:
48
- --port <n> bridge port (defaults to ${7668})
49
- --quiet suppress per-connection logging
50
-
51
- examples:
52
- rnx serve
53
- rnx serve --port 7668 --quiet
54
- `),O(0));let t=o.indexOf("--port"),i=t>=0&&o[t+1]?Number(o[t+1]):e.port??7668;Number.isNaN(i)&&(console.error(` invalid --port value: ${o[t+1]}`),O(1));let r=o.includes("--quiet")||o.includes("-q"),n=ae();n&&ce(n)&&(console.error(` an rnx bridge is already running (pid ${n.pid}, port ${n.bridgePort})`),console.error(" stop it with 'rnx daemon stop' first"),O(1)),L();let c=await be(),s=new $({port:i,writeLockfile:!0,contrastOrigin:c}),a=await s.startAsync({silent:r}),l=Date.now(),d=m=>{r||process.stdout.write(`${m}
55
- `)},u=new Set,p=setInterval(()=>{let m=s.listSims(),S=new Set(m.map(f=>f.id));for(let f of m)if(!u.has(f.id)){let b=f.title||f.url||f.origin||"(unknown)";d(` + ${f.id} ${b}`)}for(let f of u)S.has(f)||d(` - ${f}`);u.clear();for(let f of S)u.add(f)},500);d(`rnx bridge listening on ws://localhost:${a} (runtime http on same port)`),a!==i&&d(` (preferred port ${i} was taken \u2014 fell back to ${a})`),d(" ready for browser, electron, or headless playwright sims to connect"),d(" (ctrl-c to stop)");let h=async m=>{clearInterval(p),d(`
56
- ${m} received \u2014 shutting down after ${Math.round((Date.now()-l)/1e3)}s`);try{await s.close()}catch{}O(0)};process.on("SIGINT",()=>h("SIGINT")),process.on("SIGTERM",()=>h("SIGTERM")),process.on("SIGHUP",()=>h("SIGHUP")),process.on("exit",()=>{try{s.removeLockfile()}catch{}}),await new Promise(()=>{})}export{Yi as runServe};
@@ -1,4 +0,0 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{a}from"./chunk-4IKE4YUS.js";import"./chunk-SWBUNMQS.js";import"./chunk-LD7I266X.js";import"./chunk-EWPAI4MO.js";import"./chunk-XYSDYGKC.js";import"./chunk-PNRJFHX4.js";import"./chunk-HNDWYCND.js";import"./chunk-YNHRSDVG.js";import"./chunk-USM3P74H.js";import"./chunk-CX7VCKAT.js";import"./chunk-Z6LOIW46.js";import"./chunk-2BRBFLWA.js";import"./chunk-C6B5FOP3.js";import"./chunk-5PZVYF42.js";import"./chunk-FZDRA7Z4.js";import"./chunk-4ZXR2M3D.js";import"./chunk-PBOWUNXO.js";import"./chunk-MP2GPO4T.js";import"./chunk-2GXNAZHB.js";import"./chunk-4ALPWIMP.js";import"./chunk-HNPHODBD.js";import"./chunk-ZXFVBYJY.js";import"./chunk-AQ5TMOAI.js";import"./chunk-TPPVAA7Z.js";import"./chunk-H4MYYOYJ.js";import"./chunk-7NCPUZ52.js";import"./chunk-GEX7RVHY.js";import"./chunk-T4ISWS5H.js";import"./chunk-XUBV77WH.js";import"./chunk-K7QQWKGE.js";import"./chunk-F5F7FIPM.js";import"./chunk-DSD2UBZI.js";import"./chunk-5HHWECGT.js";import"./chunk-S65CK3RI.js";import"./chunk-LMHBU5LZ.js";import"./chunk-HWDVC6YA.js";import"./chunk-4EUKOIAV.js";import"./chunk-25KRGDZT.js";import"./chunk-DQUPSAHE.js";export{a as runSetup};
@@ -1,4 +0,0 @@
1
- /*! rnx v0.1.375 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{a,b,c,d,e,f,g,h,i}from"./chunk-LAOKTKT4.js";import"./chunk-V7RGRZOL.js";import"./chunk-YX52RJKO.js";import"./chunk-C6B5FOP3.js";import"./chunk-5PZVYF42.js";import"./chunk-DQUPSAHE.js";export{i as SimClient,a as SimClientError,b as SimConnectError,e as SimKeyboardError,f as SimPerformError,c as SimReadyTimeoutError,g as SimResetError,d as SimTapError,h as buildSimOpenUrl};