rnxsim 0.1.313 → 0.1.314

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 (297) hide show
  1. package/LICENSE +11 -10
  2. package/README.md +5 -0
  3. package/cli/app-config.ts +65 -0
  4. package/cli/app-fonts.ts +408 -0
  5. package/cli/app-project.ts +231 -0
  6. package/cli/app-splash.ts +185 -0
  7. package/cli/app-state-reset.ts +24 -0
  8. package/cli/auth.ts +155 -0
  9. package/cli/bin.ts +594 -0
  10. package/cli/bridge-diagnostics.ts +226 -0
  11. package/cli/bridge-flow-runner.ts +2830 -0
  12. package/cli/browser-evals.ts +96 -0
  13. package/cli/commands/agent-wrapper.ts +986 -0
  14. package/cli/commands/agent.ts +423 -0
  15. package/cli/commands/app-fonts.ts +98 -0
  16. package/cli/commands/assert.ts +541 -0
  17. package/cli/commands/auth.ts +59 -0
  18. package/cli/commands/camera.ts +266 -0
  19. package/cli/commands/cleanup.ts +169 -0
  20. package/cli/commands/compat.ts +87 -0
  21. package/cli/commands/config.ts +32 -0
  22. package/cli/commands/control.ts +2142 -0
  23. package/cli/commands/cpu-profile.ts +269 -0
  24. package/cli/commands/daemon-mac-app.ts +169 -0
  25. package/cli/commands/daemon.ts +874 -0
  26. package/cli/commands/debug.ts +719 -0
  27. package/cli/commands/desktop.ts +39 -0
  28. package/cli/commands/detect.ts +197 -0
  29. package/cli/commands/detox.ts +385 -0
  30. package/cli/commands/device.ts +133 -0
  31. package/cli/commands/diagnose.ts +589 -0
  32. package/cli/commands/electron.ts +95 -0
  33. package/cli/commands/film.ts +379 -0
  34. package/cli/commands/flow.ts +1124 -0
  35. package/cli/commands/inspect/actions.ts +622 -0
  36. package/cli/commands/inspect/core.ts +2405 -0
  37. package/cli/commands/inspect/count.ts +17 -0
  38. package/cli/commands/inspect/describe.ts +192 -0
  39. package/cli/commands/inspect/env.ts +23 -0
  40. package/cli/commands/inspect/find.ts +171 -0
  41. package/cli/commands/inspect/get-layout.ts +39 -0
  42. package/cli/commands/inspect/keyboard.ts +52 -0
  43. package/cli/commands/inspect/list.ts +58 -0
  44. package/cli/commands/inspect/memory.ts +215 -0
  45. package/cli/commands/inspect/redaction.ts +39 -0
  46. package/cli/commands/inspect/resolve-target.ts +82 -0
  47. package/cli/commands/inspect/screens.ts +78 -0
  48. package/cli/commands/inspect/settle.ts +22 -0
  49. package/cli/commands/inspect/settling.ts +158 -0
  50. package/cli/commands/inspect/shared.ts +353 -0
  51. package/cli/commands/inspect/sleep.ts +14 -0
  52. package/cli/commands/inspect/tree.ts +32 -0
  53. package/cli/commands/inspect/url.ts +17 -0
  54. package/cli/commands/inspect/wait-event.ts +210 -0
  55. package/cli/commands/inspect/wait-idle.ts +24 -0
  56. package/cli/commands/inspect/wait-ready.ts +74 -0
  57. package/cli/commands/inspect/wait-selector.ts +54 -0
  58. package/cli/commands/inspect/wait.ts +31 -0
  59. package/cli/commands/inspect.ts +4519 -0
  60. package/cli/commands/install-desktop.ts +351 -0
  61. package/cli/commands/login.ts +331 -0
  62. package/cli/commands/logout.ts +31 -0
  63. package/cli/commands/maestro-generate.ts +361 -0
  64. package/cli/commands/maestro.ts +453 -0
  65. package/cli/commands/mode.ts +57 -0
  66. package/cli/commands/no-bridge-hint.ts +80 -0
  67. package/cli/commands/perf.ts +66 -0
  68. package/cli/commands/permissions.ts +203 -0
  69. package/cli/commands/profile.ts +108 -0
  70. package/cli/commands/react.ts +353 -0
  71. package/cli/commands/record.ts +1434 -0
  72. package/cli/commands/report-issue.ts +305 -0
  73. package/cli/commands/reset.ts +85 -0
  74. package/cli/commands/runtime.ts +351 -0
  75. package/cli/commands/screenshot-command.ts +106 -0
  76. package/cli/commands/screenshot-layers.ts +143 -0
  77. package/cli/commands/screenshot-mode.ts +37 -0
  78. package/cli/commands/screenshot.ts +488 -0
  79. package/cli/commands/screenshots-capture.ts +607 -0
  80. package/cli/commands/screenshots.ts +127 -0
  81. package/cli/commands/serve.ts +168 -0
  82. package/cli/commands/setup.ts +545 -0
  83. package/cli/commands/shell-boolean-mode.ts +81 -0
  84. package/cli/commands/skills.ts +467 -0
  85. package/cli/commands/slides.ts +361 -0
  86. package/cli/commands/state.ts +87 -0
  87. package/cli/commands/storage.ts +58 -0
  88. package/cli/commands/telemetry.ts +54 -0
  89. package/cli/commands/three-mode.ts +763 -0
  90. package/cli/commands/timeline.ts +122 -0
  91. package/cli/commands/upgrade.ts +208 -0
  92. package/cli/commands/upload.ts +1225 -0
  93. package/cli/commands/version.ts +54 -0
  94. package/cli/commands/what-happened.ts +327 -0
  95. package/cli/current-sim.ts +204 -0
  96. package/cli/desktop-companion.ts +300 -0
  97. package/cli/drivers/electron.ts +70 -0
  98. package/cli/drivers/index.ts +20 -0
  99. package/cli/drivers/playwright-provisioning.ts +180 -0
  100. package/cli/drivers/playwright.ts +698 -0
  101. package/cli/drivers/registry.ts +65 -0
  102. package/cli/drivers/types.ts +102 -0
  103. package/cli/flow-file.ts +142 -0
  104. package/cli/flow-live-status.ts +120 -0
  105. package/cli/flow-session.ts +187 -0
  106. package/cli/help.ts +80 -0
  107. package/cli/hidden-runtime-alias.ts +19 -0
  108. package/cli/hints.ts +216 -0
  109. package/cli/inspect-notice-state.ts +114 -0
  110. package/cli/maestro-js.ts +334 -0
  111. package/cli/open-url.ts +8 -0
  112. package/cli/parent-pid.ts +204 -0
  113. package/cli/parse-args.ts +211 -0
  114. package/cli/prompt.ts +51 -0
  115. package/cli/recording-access.ts +107 -0
  116. package/cli/registry.ts +1 -0
  117. package/cli/resolve-assets.ts +63 -0
  118. package/cli/run-registry.ts +226 -0
  119. package/cli/runtime-notes.ts +66 -0
  120. package/cli/runtime-summary.ts +25 -0
  121. package/cli/setup-repository.ts +187 -0
  122. package/cli/telemetry.ts +187 -0
  123. package/cli/ws-bridge.ts +798 -0
  124. package/dist-cli/bin.js +5 -5
  125. package/dist-cli/chunks/{agent-XZ2KTPCU.js → agent-7YBDCYMA.js} +2 -2
  126. package/dist-cli/chunks/{agent-wrapper-JJYYW2WH.js → agent-wrapper-2GHFBHCR.js} +2 -2
  127. package/dist-cli/chunks/{app-fonts-IXRNQG6B.js → app-fonts-RSNVPQSU.js} +2 -2
  128. package/dist-cli/chunks/{assert-54T5SK5F.js → assert-XCMX3XJX.js} +2 -2
  129. package/dist-cli/chunks/{auth-FI5UDI45.js → auth-B442HRAX.js} +2 -2
  130. package/dist-cli/chunks/{beta-JV6UKADW.js → beta-XJ55JK3M.js} +2 -2
  131. package/dist-cli/chunks/camera-UGYSSLIK.js +33 -0
  132. package/dist-cli/chunks/{chunk-3NV2NCNX.js → chunk-277AEQZX.js} +2 -2
  133. package/dist-cli/chunks/{chunk-2YR5BGA5.js → chunk-2JNSK774.js} +2 -2
  134. package/dist-cli/chunks/{chunk-RSZWCKNT.js → chunk-32WOTSTR.js} +3 -3
  135. package/dist-cli/chunks/{chunk-WEXDAC74.js → chunk-3S753SNQ.js} +2 -2
  136. package/dist-cli/chunks/{chunk-IJO63TDP.js → chunk-46ZOLOYA.js} +2 -2
  137. package/dist-cli/chunks/{chunk-WUSWBCWA.js → chunk-5L7ELDQL.js} +8 -9
  138. package/dist-cli/chunks/{chunk-WF3T4SVI.js → chunk-7OOPFSQS.js} +2 -2
  139. package/dist-cli/chunks/{chunk-TZFFR3SD.js → chunk-7SV3RPRW.js} +2 -2
  140. package/dist-cli/chunks/{chunk-WWZIXIRD.js → chunk-7ZC35MOU.js} +1 -1
  141. package/dist-cli/chunks/chunk-AMG5E6CC.js +9 -0
  142. package/dist-cli/chunks/{chunk-WINYQ44O.js → chunk-APWNH3A4.js} +1 -1
  143. package/dist-cli/chunks/chunk-B57XUKY3.js +4 -0
  144. package/dist-cli/chunks/{chunk-YDGQTMQL.js → chunk-C2NL26TD.js} +1 -1
  145. package/dist-cli/chunks/{chunk-NMF2ZMZQ.js → chunk-D2FNUWAB.js} +4 -4
  146. package/dist-cli/chunks/{chunk-YIFT42WN.js → chunk-E5T4XSJ3.js} +2 -2
  147. package/dist-cli/chunks/{chunk-46EUUFJ5.js → chunk-EAC34EQS.js} +1 -1
  148. package/dist-cli/chunks/{chunk-OVFJFXUD.js → chunk-EG32ML36.js} +2 -2
  149. package/dist-cli/chunks/{chunk-5YJCOWCH.js → chunk-FXUAC6D5.js} +1 -1
  150. package/dist-cli/chunks/chunk-GQSL4USA.js +6 -0
  151. package/dist-cli/chunks/{chunk-VFCMSYZK.js → chunk-HAXW27SS.js} +2 -2
  152. package/dist-cli/chunks/{chunk-2D2UPBBR.js → chunk-IZAHPAN6.js} +1 -1
  153. package/dist-cli/chunks/{chunk-7GN3LVWB.js → chunk-J62KM5TB.js} +2 -2
  154. package/dist-cli/chunks/{chunk-BTWORNNG.js → chunk-JEMCD5E4.js} +1 -1
  155. package/dist-cli/chunks/{chunk-DCEMHR2Y.js → chunk-JZS3Q37N.js} +2 -2
  156. package/dist-cli/chunks/{chunk-D4FFVGI5.js → chunk-KR4JON7D.js} +1 -1
  157. package/dist-cli/chunks/chunk-KWCYKQIQ.js +4 -0
  158. package/dist-cli/chunks/{chunk-VNQEB4L7.js → chunk-MCWPL644.js} +2 -2
  159. package/dist-cli/chunks/chunk-MJYK3N2I.js +4 -0
  160. package/dist-cli/chunks/{chunk-5TEF3ET3.js → chunk-O6TRIZNS.js} +2 -2
  161. package/dist-cli/chunks/{chunk-QKDWYITG.js → chunk-P7XL2E73.js} +3 -3
  162. package/dist-cli/chunks/{chunk-BBULZ7CG.js → chunk-PFQTUKQ4.js} +62 -87
  163. package/dist-cli/chunks/{chunk-GGRX24GF.js → chunk-PG5RZCTN.js} +2 -2
  164. package/dist-cli/chunks/{chunk-W6K4EFPH.js → chunk-QGRI2Z4M.js} +2 -2
  165. package/dist-cli/chunks/{chunk-ZMJD5GEC.js → chunk-QLXXE7GE.js} +1 -1
  166. package/dist-cli/chunks/{chunk-VZXWHRUZ.js → chunk-QOJJJWJE.js} +89 -133
  167. package/dist-cli/chunks/{chunk-OZSSI4WN.js → chunk-RWZY5427.js} +2 -2
  168. package/dist-cli/chunks/{chunk-UC6U3MML.js → chunk-RZKU2K3J.js} +2 -2
  169. package/dist-cli/chunks/{chunk-IJ5CAZZC.js → chunk-SBV4IK4H.js} +1 -1
  170. package/dist-cli/chunks/{chunk-DZS6WPUI.js → chunk-SGMVFFMK.js} +1 -1
  171. package/dist-cli/chunks/{chunk-OHAZNXLK.js → chunk-TAX4UT2N.js} +1 -1
  172. package/dist-cli/chunks/{chunk-5DHC6KHQ.js → chunk-TUOFAWXT.js} +1 -1
  173. package/dist-cli/chunks/{chunk-GASE6UBA.js → chunk-UHZLOHGP.js} +1 -1
  174. package/dist-cli/chunks/{chunk-F5ZRSS3C.js → chunk-VC7V76U3.js} +1 -1
  175. package/dist-cli/chunks/{chunk-RTN5C5RL.js → chunk-VQVMLW4U.js} +1 -1
  176. package/dist-cli/chunks/{chunk-WMIIKMGK.js → chunk-VUKKYPZN.js} +2 -2
  177. package/dist-cli/chunks/{chunk-5TPRP5QT.js → chunk-WGGRJDRE.js} +1 -1
  178. package/dist-cli/chunks/chunk-X6H76EKP.js +15 -0
  179. package/dist-cli/chunks/chunk-XLZ5FNRT.js +27 -0
  180. package/dist-cli/chunks/{chunk-HI5TFJWN.js → chunk-XULEACM4.js} +2 -2
  181. package/dist-cli/chunks/{chunk-MJRLLB4R.js → chunk-YFSDM7AX.js} +4 -4
  182. package/dist-cli/chunks/chunk-YWI3UEVX.js +5 -0
  183. package/dist-cli/chunks/{chunk-XEVZYVIW.js → chunk-ZBSJO4NB.js} +10 -9
  184. package/dist-cli/chunks/{cleanup-P27PA6JI.js → cleanup-EYLGCA6Z.js} +2 -2
  185. package/dist-cli/chunks/cli-version-LL2UGIHE.js +4 -0
  186. package/dist-cli/chunks/{compat-ZD65FED3.js → compat-TLJYHB4E.js} +2 -2
  187. package/dist-cli/chunks/{config-XMJRNM2A.js → config-4JWUOEXK.js} +2 -2
  188. package/dist-cli/chunks/{control-KMIQT3QP.js → control-HPAOYF4N.js} +2 -2
  189. package/dist-cli/chunks/daemon-OYLASXLE.js +4 -0
  190. package/dist-cli/chunks/{debug-PT4HOP7N.js → debug-4BKXF6KI.js} +5 -5
  191. package/dist-cli/chunks/{desktop-S3FG72AK.js → desktop-ZQ6ZD2S6.js} +3 -3
  192. package/dist-cli/chunks/{detox-B3D4IFCN.js → detox-WNPASSS3.js} +2 -2
  193. package/dist-cli/chunks/{device-XBNDSB2R.js → device-BXLXVG3V.js} +2 -2
  194. package/dist-cli/chunks/{diagnose-HMQXJE5N.js → diagnose-3QX7W5RW.js} +2 -2
  195. package/dist-cli/chunks/{disk-cleanup-BLCZ5BSZ.js → disk-cleanup-KSWKI7WB.js} +2 -2
  196. package/dist-cli/chunks/drivers-EPIEFF7P.js +4 -0
  197. package/dist-cli/chunks/{film-BJGTBYZB.js → film-GDMR33VO.js} +3 -3
  198. package/dist-cli/chunks/flow-NKMPBYCJ.js +4 -0
  199. package/dist-cli/chunks/help-OGCBHOKA.js +4 -0
  200. package/dist-cli/chunks/{hidden-runtime-alias-ANOYADHM.js → hidden-runtime-alias-S2GTDX3T.js} +2 -2
  201. package/dist-cli/chunks/home-paths-QRCDLTTV.js +4 -0
  202. package/dist-cli/chunks/inspect-FUYMOZPY.js +4 -0
  203. package/dist-cli/chunks/install-desktop-FR6YKW7Y.js +4 -0
  204. package/dist-cli/chunks/{login-FJ737MWG.js → login-IJAPUZHI.js} +4 -4
  205. package/dist-cli/chunks/{logout-ZCNMMHMY.js → logout-QMXDFU2X.js} +2 -2
  206. package/dist-cli/chunks/{maestro-SZTNKLDF.js → maestro-ZXU3YCVX.js} +3 -3
  207. package/dist-cli/chunks/{maestro-generate-DCFAIZ4H.js → maestro-generate-PYB5QY7K.js} +3 -3
  208. package/dist-cli/chunks/{mode-GRMQCRXR.js → mode-WGUCL5FZ.js} +2 -2
  209. package/dist-cli/chunks/{optional-demo-registry-W36EWFFB.js → optional-demo-registry-WH2O6H36.js} +2 -2
  210. package/dist-cli/chunks/{perf-QYBAAUZG.js → perf-TOD3UFAH.js} +2 -2
  211. package/dist-cli/chunks/{permissions-3QCQ6VF4.js → permissions-I5BRJGTB.js} +2 -2
  212. package/dist-cli/chunks/{record-QPPC2S4E.js → record-ZYL2FYSK.js} +3 -3
  213. package/dist-cli/chunks/{report-issue-7NMFP4HK.js → report-issue-TAI6DYZO.js} +2 -2
  214. package/dist-cli/chunks/reset-7YGYKQPQ.js +4 -0
  215. package/dist-cli/chunks/runtime-B4JO6QRI.js +4 -0
  216. package/dist-cli/chunks/{screenshot-command-67AECJFB.js → screenshot-command-7ANLODZY.js} +7 -7
  217. package/dist-cli/chunks/{screenshot-layers-ASWBYPJL.js → screenshot-layers-A7FYXSVU.js} +3 -3
  218. package/dist-cli/chunks/{screenshots-capture-PXA3HFQK.js → screenshots-capture-WT2ZY6CB.js} +2 -2
  219. package/dist-cli/chunks/serve-TG5WKA4V.js +44 -0
  220. package/dist-cli/chunks/{setup-7DWPMRSB.js → setup-PCWLD22V.js} +2 -2
  221. package/dist-cli/chunks/{skills-S3Y22TUA.js → skills-27ZHWCQS.js} +2 -2
  222. package/dist-cli/chunks/state-ZQVY46ZO.js +14 -0
  223. package/dist-cli/chunks/{storage-XUIMJWAJ.js → storage-OYC57C4X.js} +6 -6
  224. package/dist-cli/chunks/store-32HSPZHI.js +4 -0
  225. package/dist-cli/chunks/telemetry-PBJR7XHR.js +4 -0
  226. package/dist-cli/chunks/{timeline-TMPLQPSP.js → timeline-RAJJFQT6.js} +2 -2
  227. package/dist-cli/chunks/{upgrade-7HDSIM7K.js → upgrade-OEFNZRIP.js} +2 -2
  228. package/dist-cli/chunks/upload-LQJITLKK.js +4 -0
  229. package/dist-cli/chunks/version-NIXTY6PL.js +6 -0
  230. package/dist-cli/chunks/{web-DG3WBYD3.js → web-XBUTBVGR.js} +2 -2
  231. package/dist-cli/chunks/{what-happened-XFVUTZR7.js → what-happened-YNHRFUDX.js} +3 -3
  232. package/dist-lib/agent-daemon-client.cjs +1 -1
  233. package/dist-lib/agent-events.cjs +1 -1
  234. package/dist-lib/agent-identity.cjs +1 -1
  235. package/dist-lib/agent-sessions.cjs +1 -1
  236. package/dist-lib/attached-projects.cjs +1 -1
  237. package/dist-lib/auth/shared-session.cjs +1 -1
  238. package/dist-lib/backend-origin.cjs +1 -1
  239. package/dist-lib/beta.cjs +1 -1
  240. package/dist-lib/beta.mjs +1 -1
  241. package/dist-lib/bridge-constants.cjs +1 -1
  242. package/dist-lib/bridge-contract.cjs +20 -0
  243. package/dist-lib/cli-constants.cjs +1 -1
  244. package/dist-lib/config.cjs +1 -1
  245. package/dist-lib/detox/index.cjs +1 -1
  246. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  247. package/dist-lib/home-paths.cjs +67 -28
  248. package/dist-lib/host/bridge-host.cjs +140 -12
  249. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  250. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  251. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  252. package/dist-lib/host/websocket-proxy.cjs +1 -1
  253. package/dist-lib/index.cjs +2815 -40
  254. package/dist-lib/jump-to-source-babel.cjs +1 -1
  255. package/dist-lib/menu.cjs +1 -1
  256. package/dist-lib/menu.mjs +1 -1
  257. package/dist-lib/metro.cjs +1 -1
  258. package/dist-lib/profiles.cjs +1 -1
  259. package/dist-lib/public-brand.cjs +1 -1
  260. package/dist-lib/render-mode.cjs +1 -1
  261. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  262. package/dist-lib/sdk.cjs +2549 -2061
  263. package/dist-lib/sdk.mjs +2543 -2061
  264. package/dist-lib/skills.cjs +480 -280
  265. package/dist-lib/vite.cjs +1 -1
  266. package/package.json +8 -2
  267. package/src/bridge-constants.ts +3 -4
  268. package/src/bridge-contract.ts +251 -0
  269. package/src/connect.ts +83 -0
  270. package/src/disk-cleanup.ts +30 -0
  271. package/src/home-paths.ts +81 -38
  272. package/src/host/bridge-host.ts +134 -6
  273. package/src/index.ts +27 -1
  274. package/src/sdk.ts +8 -0
  275. package/src/sim-client.ts +660 -0
  276. package/dist-cli/chunks/camera-VL73YIKP.js +0 -22
  277. package/dist-cli/chunks/chunk-4NPPOV2N.js +0 -5
  278. package/dist-cli/chunks/chunk-FSUYIVJ6.js +0 -9
  279. package/dist-cli/chunks/chunk-G2WW6L2C.js +0 -23
  280. package/dist-cli/chunks/chunk-KTHV3RUS.js +0 -26
  281. package/dist-cli/chunks/chunk-LF2ZVT7O.js +0 -6
  282. package/dist-cli/chunks/chunk-NFK7T35W.js +0 -4
  283. package/dist-cli/chunks/chunk-TIVZIMMW.js +0 -4
  284. package/dist-cli/chunks/cli-version-WWLPBDQ7.js +0 -4
  285. package/dist-cli/chunks/daemon-G2ME7NLB.js +0 -4
  286. package/dist-cli/chunks/drivers-LDECZGP2.js +0 -4
  287. package/dist-cli/chunks/flow-UEQNVTU7.js +0 -4
  288. package/dist-cli/chunks/help-T5FYSVGB.js +0 -4
  289. package/dist-cli/chunks/home-paths-GT3LFNOR.js +0 -4
  290. package/dist-cli/chunks/inspect-ZA6XF5LD.js +0 -4
  291. package/dist-cli/chunks/install-desktop-TIMUDHPL.js +0 -4
  292. package/dist-cli/chunks/runtime-XOAXMSTU.js +0 -4
  293. package/dist-cli/chunks/serve-BI2NBAXG.js +0 -44
  294. package/dist-cli/chunks/store-JTHEJLAZ.js +0 -4
  295. package/dist-cli/chunks/telemetry-ZYJGD2DB.js +0 -4
  296. package/dist-cli/chunks/upload-GMSZPWM6.js +0 -4
  297. package/dist-cli/chunks/version-HOCHZ37L.js +0 -6
@@ -0,0 +1,269 @@
1
+ // sootsim perf cpu — capture a sampled CPU trace from the tenant worker.
2
+ //
3
+ // the JS Self-Profiler API (`new Profiler()`) cannot be constructed in a
4
+ // dedicated-worker scope, and sootsim runs the guest app in the tenant worker,
5
+ // so the old in-worker approach was unsupported (F28). this implementation
6
+ // attaches Chromium's `Profiler` CDP domain to the worker target instead —
7
+ // which works for workers and returns the .cpuprofile shape directly.
8
+ //
9
+ // dedicated workers are not top-level CDP targets, so we connect to the page
10
+ // target, `Target.setAutoAttach` to discover the worker child sessions, and
11
+ // route `Profiler.*` to each worker via its sessionId (flatten mode).
12
+ //
13
+ // the user's normal sootsim tab runs in plain Chrome without a debug port.
14
+ // the easiest way to get a profilable sim is to open one with a CDP port —
15
+ // it stays driveable over the WS bridge, so you can scroll/navigate while
16
+ // profiling (the playwright driver passes --remote-debugging-port through):
17
+ //
18
+ // sootsim open <metro-port> --new --driver playwright --cdp-port 9222
19
+ // sootsim perf cpu --cdp-port 9222 --match <metro-port> --duration 5
20
+ //
21
+ // (a standalone CDP Chrome also works for profiling a static page, but it
22
+ // does NOT register on the bridge, so it can't be driven — see printNoCdp.)
23
+ //
24
+ // interact with the app during the capture window. the reference probe that
25
+ // also drives the interaction itself lives at
26
+ // scripts/debug/cdp-worker-cpu-profile.ts.
27
+
28
+ import { mkdirSync, writeFileSync } from 'fs'
29
+ import { dirname, resolve } from 'path'
30
+ import { WebSocket } from 'ws'
31
+
32
+ interface ProfileOptions {
33
+ port?: number
34
+ verbose?: boolean
35
+ }
36
+
37
+ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
38
+
39
+ // compact CDP client with flatten-mode sessionId routing — one ws to the page
40
+ // target carries the page session plus every auto-attached worker session.
41
+ class Cdp {
42
+ private ws: WebSocket
43
+ private nextId = 1
44
+ private pending = new Map<number, { res: (v: any) => void; rej: (e: any) => void }>()
45
+ private listeners = new Set<(method: string, params: any) => void>()
46
+ private ready: Promise<void>
47
+
48
+ constructor(wsUrl: string) {
49
+ this.ws = new WebSocket(wsUrl, { maxPayload: 1 << 28 })
50
+ this.ready = new Promise((res, rej) => {
51
+ this.ws.once('open', () => res())
52
+ this.ws.once('error', rej)
53
+ })
54
+ this.ws.on('message', (data) => {
55
+ const msg = JSON.parse(data.toString())
56
+ if (msg.id && this.pending.has(msg.id)) {
57
+ const { res, rej } = this.pending.get(msg.id)!
58
+ this.pending.delete(msg.id)
59
+ msg.error ? rej(new Error(JSON.stringify(msg.error))) : res(msg.result)
60
+ } else if (msg.method) {
61
+ for (const l of this.listeners) l(msg.method, msg.params)
62
+ }
63
+ })
64
+ }
65
+
66
+ waitOpen() {
67
+ return this.ready
68
+ }
69
+
70
+ on(l: (method: string, params: any) => void) {
71
+ this.listeners.add(l)
72
+ }
73
+
74
+ send(
75
+ method: string,
76
+ params: Record<string, unknown> = {},
77
+ sessionId?: string,
78
+ ): Promise<any> {
79
+ const id = this.nextId++
80
+ return new Promise((res, rej) => {
81
+ this.pending.set(id, { res, rej })
82
+ this.ws.send(
83
+ JSON.stringify({ id, method, params, ...(sessionId ? { sessionId } : {}) }),
84
+ )
85
+ })
86
+ }
87
+
88
+ close() {
89
+ try {
90
+ this.ws.close()
91
+ } catch {}
92
+ }
93
+ }
94
+
95
+ interface CdpTarget {
96
+ type: string
97
+ url: string
98
+ webSocketDebuggerUrl: string
99
+ }
100
+
101
+ export async function runCpuProfile(
102
+ args: string[],
103
+ opts: ProfileOptions,
104
+ ): Promise<number> {
105
+ const duration = Number(valueOf(args, '--duration') ?? '5')
106
+ if (!Number.isFinite(duration) || duration <= 0) {
107
+ console.error(' --duration must be a positive number (seconds)')
108
+ return 1
109
+ }
110
+ const sampleInterval = Number(valueOf(args, '--sample-interval') ?? '0.1') // ms
111
+ const cdpPort = Number(
112
+ valueOf(args, '--cdp-port') ?? process.env.SOOTSIM_CDP_PORT ?? '9222',
113
+ )
114
+ // optional substring filter to pick the right page when several sims share
115
+ // the CDP browser (e.g. `--match 8089` or `--match /rn/8089`).
116
+ const match = valueOf(args, '--match') ?? '/rn/'
117
+ const outputArg = valueOf(args, '--output') ?? valueOf(args, '-o')
118
+ const outputPath = resolve(process.cwd(), outputArg ?? '/tmp/sootsim.cpuprofile')
119
+
120
+ // locate the page target on the CDP endpoint. NOTE: Chrome's DevTools HTTP
121
+ // `/json` endpoints reject any Host header that isn't `localhost` (a
122
+ // DNS-rebinding guard) — `http://127.0.0.1:<port>/json/list` returns
123
+ // "Not found" / 404, so we must use the `localhost` hostname here. (this is
124
+ // what made the command silently fall into the no-CDP path even with a live
125
+ // CDP browser.)
126
+ let targets: CdpTarget[] = []
127
+ try {
128
+ targets = (await (
129
+ await fetch(`http://localhost:${cdpPort}/json/list`)
130
+ ).json()) as CdpTarget[]
131
+ } catch {
132
+ printNoCdp(cdpPort)
133
+ return 1
134
+ }
135
+ const page = targets.find((t) => t.type === 'page' && t.url.includes(match))
136
+ if (!page) {
137
+ console.error(` no page target matching "${match}" on CDP :${cdpPort}`)
138
+ console.error(` open targets: ${targets.map((t) => t.url).join(', ') || '(none)'}`)
139
+ return 1
140
+ }
141
+
142
+ const cdp = new Cdp(page.webSocketDebuggerUrl)
143
+ await cdp.waitOpen()
144
+ try {
145
+ // discover worker child sessions via auto-attach.
146
+ const workers = new Map<string, string>() // sessionId -> url
147
+ cdp.on((method, params) => {
148
+ if (method === 'Target.attachedToTarget' && params.targetInfo?.type === 'worker') {
149
+ workers.set(params.sessionId, params.targetInfo.url)
150
+ }
151
+ if (method === 'Target.detachedFromTarget') workers.delete(params.sessionId)
152
+ })
153
+ await cdp.send('Target.setAutoAttach', {
154
+ autoAttach: true,
155
+ waitForDebuggerOnStart: false,
156
+ flatten: true,
157
+ })
158
+ // auto-attach events arrive asynchronously after the call resolves.
159
+ await sleep(500)
160
+ if (workers.size === 0) {
161
+ console.error(' no worker targets attached — is the app loaded in this tab?')
162
+ return 1
163
+ }
164
+
165
+ for (const sid of workers.keys()) {
166
+ await cdp.send('Profiler.enable', {}, sid)
167
+ await cdp.send(
168
+ 'Profiler.setSamplingInterval',
169
+ { interval: sampleInterval * 1000 },
170
+ sid,
171
+ ) // µs
172
+ await cdp.send('Profiler.start', {}, sid)
173
+ }
174
+ console.log(
175
+ ` recording ${duration}s across ${workers.size} worker(s) — interact now…`,
176
+ )
177
+ await sleep(duration * 1000)
178
+
179
+ const results: Array<{ url: string; profile: any }> = []
180
+ for (const [sid, url] of workers) {
181
+ const { profile } = await cdp.send('Profiler.stop', {}, sid)
182
+ results.push({ url, profile })
183
+ }
184
+ results.sort((a, b) => b.profile.samples.length - a.profile.samples.length)
185
+
186
+ mkdirSync(dirname(outputPath), { recursive: true })
187
+ // save the busiest worker (the tenant during interaction) to the main path;
188
+ // others get a suffix so a multi-worker capture isn't lost.
189
+ results.forEach((r, i) => {
190
+ const path =
191
+ i === 0 ? outputPath : outputPath.replace(/(\.[^.]+)?$/, `.worker${i}$1`)
192
+ writeFileSync(path, JSON.stringify(r.profile))
193
+ console.log(` ${shortUrl(r.url)}: ${r.profile.samples.length} samples → ${path}`)
194
+ if (opts.verbose && r.profile.samples.length > 0) {
195
+ for (const fn of topSelfTime(r.profile, 12)) {
196
+ console.log(` ${fn.pct.toFixed(1).padStart(5)}% ${fn.name} ${fn.url}`)
197
+ }
198
+ }
199
+ })
200
+ console.log(' open in chrome devtools → Performance → Load profile to inspect.')
201
+ return 0
202
+ } finally {
203
+ cdp.close()
204
+ }
205
+ }
206
+
207
+ function printNoCdp(cdpPort: number) {
208
+ console.error(` perf cpu needs a CDP-enabled browser on :${cdpPort}.`)
209
+ console.error(
210
+ " the tenant worker can't be profiled via the JS Self-Profiler API (F28),",
211
+ )
212
+ console.error(' so the sim must expose Chrome remote debugging. easiest path —')
213
+ console.error(' open the sim with a CDP port (it stays driveable over the bridge,')
214
+ console.error(' so you can scroll/navigate while profiling):')
215
+ console.error('')
216
+ console.error(
217
+ ` rnxsim open <metro-port> --new --driver playwright --cdp-port ${cdpPort}`,
218
+ )
219
+ console.error(` rnxsim perf cpu --cdp-port ${cdpPort} --match <metro-port>`)
220
+ console.error('')
221
+ console.error(' or launch a standalone CDP Chrome (not bridge-driveable):')
222
+ console.error('')
223
+ console.error(' "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \\')
224
+ console.error(
225
+ ` --remote-debugging-port=${cdpPort} --user-data-dir=/tmp/sootsim-cdp \\`,
226
+ )
227
+ console.error(' --no-sandbox --disable-gpu-sandbox \\')
228
+ // never suggest swiftshader — software rendering pegs every core for the
229
+ // browser's lifetime (documented machine-freeze cause). metal is the real
230
+ // rendering path anyway, so the profile is representative.
231
+ console.error(' --use-gl=angle --use-angle=metal \\')
232
+ console.error(' "http://localhost:5173/rn/<metro-port>" &')
233
+ console.error('')
234
+ console.error(' override the port with --cdp-port or $SOOTSIM_CDP_PORT.')
235
+ }
236
+
237
+ function shortUrl(url: string): string {
238
+ if (!url) return '(native)'
239
+ return url
240
+ .replace(/^https?:\/\/[^/]+/, '')
241
+ .replace(/\?.*$/, '')
242
+ .slice(-50)
243
+ }
244
+
245
+ function topSelfTime(profile: any, topN: number) {
246
+ const total = profile.samples.length || 1
247
+ const byFn = new Map<string, { self: number; name: string; url: string }>()
248
+ for (const node of profile.nodes) {
249
+ const f = node.callFrame
250
+ const key = `${f.functionName || '(anonymous)'}@${shortUrl(f.url)}:${f.lineNumber}`
251
+ const cur = byFn.get(key) ?? {
252
+ self: 0,
253
+ name: f.functionName || '(anonymous)',
254
+ url: `${shortUrl(f.url)}:${f.lineNumber}`,
255
+ }
256
+ cur.self += node.hitCount ?? 0
257
+ byFn.set(key, cur)
258
+ }
259
+ return [...byFn.values()]
260
+ .map((v) => ({ ...v, pct: (v.self / total) * 100 }))
261
+ .sort((a, b) => b.self - a.self)
262
+ .slice(0, topN)
263
+ }
264
+
265
+ function valueOf(args: string[], flag: string): string | undefined {
266
+ const i = args.indexOf(flag)
267
+ if (i < 0 || i === args.length - 1) return undefined
268
+ return args[i + 1]
269
+ }
@@ -0,0 +1,169 @@
1
+ // generate + ad-hoc sign the SootSim Daemon.app wrapper that the launchd
2
+ // agent points its ProgramArguments at.
3
+ //
4
+ // why this exists: macOS Background Task Management (the "X wants to run in
5
+ // the background" prompt + the Login Items entry) reads the *signer* of
6
+ // ProgramArguments[0] to decide what name to display. when launchd is
7
+ // pointed at bun directly the entry shows "software from Jarred Sumner"
8
+ // because that's bun's code-signing identity. wrapping the real invocation
9
+ // in an ad-hoc-signed `.app` lets BTM read CFBundleDisplayName from the
10
+ // bundle Info.plist instead, so the entry reads "SootSim Daemon".
11
+ //
12
+ // the bundle is regenerated every time `daemonInstall` runs — it's tiny
13
+ // (a plist + a shell launcher) and the launcher embeds a snapshot of the
14
+ // invocation, so an `npm update -g sootsim` followed by `sootsim daemon
15
+ // install --force` is the documented upgrade path. there's no notarization
16
+ // or Developer ID signing here yet; ad-hoc + hardened runtime is enough to
17
+ // fix the attribution, and a real signed bundle (shipped via the runtime
18
+ // CDN) is a follow-up.
19
+
20
+ import { execFileSync } from 'child_process'
21
+ import { chmodSync, mkdirSync, rmSync, writeFileSync } from 'fs'
22
+ import { dirname } from 'path'
23
+ import {
24
+ daemonAppBundlePath,
25
+ daemonAppDir,
26
+ daemonAppLauncherPath,
27
+ } from '../../src/home-paths'
28
+
29
+ const BUNDLE_IDENTIFIER = 'dev.sootsim.daemon'
30
+ const BUNDLE_DISPLAY_NAME = 'SootSim Daemon'
31
+ // kept in sync with package.json on release. exact value doesn't matter for
32
+ // BTM display — macOS only reads it for "About this app" surfaces — so we
33
+ // hardcode it rather than reach back into the package.json from a runtime
34
+ // path that varies between npm-global, bun-compiled standalone, and dev
35
+ // checkouts.
36
+ const BUNDLE_VERSION = '0.1.36'
37
+
38
+ export interface DaemonInvocation {
39
+ /** absolute path to the executable launchd should spawn. */
40
+ executable: string
41
+ /** arguments passed to the executable (before server/--port flags). */
42
+ prefixArgs: string[]
43
+ }
44
+
45
+ export interface DaemonAppPaths {
46
+ /** the rebuilt .app bundle. */
47
+ bundlePath: string
48
+ /** the Mach-O / shell launcher inside the bundle. launchd points here. */
49
+ launcherPath: string
50
+ }
51
+
52
+ /** rebuild ~/.sootsim/daemon-app/SootSim Daemon.app/ for the given invocation
53
+ * and re-sign it ad-hoc. safe to call repeatedly; the directory is wiped
54
+ * before write so stale state from earlier installs can't leak through. */
55
+ export function ensureDaemonApp(
56
+ invocation: DaemonInvocation,
57
+ port: number,
58
+ logDir: { stdout: string; stderr: string },
59
+ ): DaemonAppPaths {
60
+ const bundlePath = daemonAppBundlePath()
61
+ const launcherPath = daemonAppLauncherPath()
62
+ const contentsDir = dirname(dirname(launcherPath))
63
+ const macOSDir = dirname(launcherPath)
64
+ const resourcesDir = `${contentsDir}/Resources`
65
+
66
+ rmSync(bundlePath, { recursive: true, force: true })
67
+ mkdirSync(daemonAppDir(), { recursive: true })
68
+ mkdirSync(macOSDir, { recursive: true })
69
+ mkdirSync(resourcesDir, { recursive: true })
70
+
71
+ writeFileSync(`${contentsDir}/Info.plist`, renderInfoPlist())
72
+ writeFileSync(`${contentsDir}/PkgInfo`, 'APPL????')
73
+ writeFileSync(launcherPath, renderLauncherScript(invocation, port, logDir))
74
+ chmodSync(launcherPath, 0o755)
75
+
76
+ // ad-hoc sign the bundle so macOS treats it as a real .app and BTM reads
77
+ // its Info.plist. `--deep` covers the inner launcher script + future
78
+ // resources without us having to enumerate them. signing is best-effort —
79
+ // an unsigned bundle still works for launchd, it just falls back to the
80
+ // worse BTM display, which is no worse than the status quo.
81
+ try {
82
+ execFileSync('codesign', ['--force', '--sign', '-', '--deep', bundlePath], {
83
+ stdio: 'pipe',
84
+ })
85
+ } catch (err) {
86
+ const msg = err instanceof Error ? err.message : String(err)
87
+ console.warn(
88
+ ` warning: ad-hoc codesign of ${bundlePath} failed: ${msg}\n` +
89
+ ` the daemon will still run, but Login Items may show a generic name.`,
90
+ )
91
+ }
92
+
93
+ return { bundlePath, launcherPath }
94
+ }
95
+
96
+ function renderInfoPlist(): string {
97
+ // LSUIElement + LSBackgroundOnly together hide the app from the Dock and
98
+ // the Cmd-Tab switcher. CFBundleDisplayName is what Login Items shows.
99
+ // NSHumanReadableCopyright is what "About" surfaces show.
100
+ return `<?xml version="1.0" encoding="UTF-8"?>
101
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
102
+ <plist version="1.0">
103
+ <dict>
104
+ <key>CFBundleDevelopmentRegion</key><string>en</string>
105
+ <key>CFBundleDisplayName</key><string>${BUNDLE_DISPLAY_NAME}</string>
106
+ <key>CFBundleExecutable</key><string>sootsim-daemon</string>
107
+ <key>CFBundleIdentifier</key><string>${BUNDLE_IDENTIFIER}</string>
108
+ <key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
109
+ <key>CFBundleName</key><string>${BUNDLE_DISPLAY_NAME}</string>
110
+ <key>CFBundlePackageType</key><string>APPL</string>
111
+ <key>CFBundleShortVersionString</key><string>${BUNDLE_VERSION}</string>
112
+ <key>CFBundleSignature</key><string>????</string>
113
+ <key>CFBundleVersion</key><string>${BUNDLE_VERSION}</string>
114
+ <key>LSBackgroundOnly</key><true/>
115
+ <key>LSUIElement</key><true/>
116
+ <key>LSMinimumSystemVersion</key><string>13.0</string>
117
+ <key>NSHumanReadableCopyright</key><string>Tamagui LLC</string>
118
+ </dict>
119
+ </plist>
120
+ `
121
+ }
122
+
123
+ function renderLauncherScript(
124
+ invocation: DaemonInvocation,
125
+ port: number,
126
+ logDir: { stdout: string; stderr: string },
127
+ ): string {
128
+ // `exec` replaces the shell process with bun/node, so launchd's PID
129
+ // tracking + KeepAlive both follow the real daemon process. stdout/stderr
130
+ // are already redirected by the launchd plist's StandardOutPath /
131
+ // StandardErrorPath, so we don't redirect again here — doing so would
132
+ // hide log output from `launchctl print`.
133
+ const parts = [
134
+ invocation.executable,
135
+ ...invocation.prefixArgs,
136
+ 'serve',
137
+ '--quiet',
138
+ '--port',
139
+ String(port),
140
+ ]
141
+ const quoted = parts.map(shellSingleQuote).join(' ')
142
+ const stdout = shellSingleQuote(logDir.stdout)
143
+ const stderr = shellSingleQuote(logDir.stderr)
144
+ return `#!/bin/sh
145
+ # SootSim Daemon launcher — auto-generated by 'rnxsim setup'.
146
+ # launchd spawns this; we exec the real rnxsim invocation in place so
147
+ # launchd tracks the daemon process, not the shell wrapper.
148
+ #
149
+ # stdout: ${logDir.stdout}
150
+ # stderr: ${logDir.stderr}
151
+ # a missing or broken executable can make launchd retry forever. truncate a
152
+ # retry log before it passes 10 MiB so that failure stays bounded on disk.
153
+ for log_path in ${stdout} ${stderr}; do
154
+ if [ -f "$log_path" ]; then
155
+ log_bytes=$(wc -c < "$log_path" 2>/dev/null || echo 0)
156
+ if [ "$log_bytes" -gt 10485760 ]; then
157
+ : > "$log_path"
158
+ fi
159
+ fi
160
+ done
161
+ exec ${quoted}
162
+ `
163
+ }
164
+
165
+ function shellSingleQuote(value: string): string {
166
+ // single-quote everything and escape embedded single quotes the POSIX way.
167
+ // safe for paths containing spaces, $, backticks, etc.
168
+ return `'${value.replace(/'/g, `'\\''`)}'`
169
+ }