rnxsim 0.1.312 → 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 (299) 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-NXVDDCFG.js → agent-7YBDCYMA.js} +2 -2
  126. package/dist-cli/chunks/{agent-wrapper-DXJCFKL3.js → agent-wrapper-2GHFBHCR.js} +2 -2
  127. package/dist-cli/chunks/{app-fonts-W5AJZQJF.js → app-fonts-RSNVPQSU.js} +2 -2
  128. package/dist-cli/chunks/{assert-3LOJEUDT.js → assert-XCMX3XJX.js} +2 -2
  129. package/dist-cli/chunks/{auth-MFTYYPNX.js → auth-B442HRAX.js} +2 -2
  130. package/dist-cli/chunks/{beta-GT3NN67B.js → beta-XJ55JK3M.js} +2 -2
  131. package/dist-cli/chunks/camera-UGYSSLIK.js +33 -0
  132. package/dist-cli/chunks/{chunk-EUAVBTKU.js → chunk-277AEQZX.js} +2 -2
  133. package/dist-cli/chunks/{chunk-5OX7ANRA.js → chunk-2JNSK774.js} +2 -2
  134. package/dist-cli/chunks/{chunk-L24Z3CRS.js → chunk-32WOTSTR.js} +3 -3
  135. package/dist-cli/chunks/{chunk-BKNOLP3J.js → chunk-3S753SNQ.js} +2 -2
  136. package/dist-cli/chunks/{chunk-N6A3SXMR.js → chunk-46ZOLOYA.js} +2 -2
  137. package/dist-cli/chunks/{chunk-GZDHEONW.js → chunk-5L7ELDQL.js} +8 -9
  138. package/dist-cli/chunks/{chunk-YZRFF4YA.js → chunk-7OOPFSQS.js} +2 -2
  139. package/dist-cli/chunks/{chunk-TBMVBI26.js → chunk-7SV3RPRW.js} +2 -2
  140. package/dist-cli/chunks/{chunk-JG6S4RSH.js → chunk-7ZC35MOU.js} +1 -1
  141. package/dist-cli/chunks/chunk-AMG5E6CC.js +9 -0
  142. package/dist-cli/chunks/{chunk-MENV7VLD.js → chunk-APWNH3A4.js} +1 -1
  143. package/dist-cli/chunks/chunk-B57XUKY3.js +4 -0
  144. package/dist-cli/chunks/{chunk-3D57IBPC.js → chunk-C2NL26TD.js} +1 -1
  145. package/dist-cli/chunks/chunk-D2FNUWAB.js +15 -0
  146. package/dist-cli/chunks/{chunk-4BXCZHAU.js → chunk-E5T4XSJ3.js} +2 -2
  147. package/dist-cli/chunks/{chunk-FTC4M26Q.js → chunk-EAC34EQS.js} +1 -1
  148. package/dist-cli/chunks/{chunk-NPVGMH2N.js → chunk-EG32ML36.js} +2 -2
  149. package/dist-cli/chunks/{chunk-DM6KUAOZ.js → chunk-FXUAC6D5.js} +1 -1
  150. package/dist-cli/chunks/chunk-GQSL4USA.js +6 -0
  151. package/dist-cli/chunks/{chunk-U4PN6JOT.js → chunk-HAXW27SS.js} +2 -2
  152. package/dist-cli/chunks/{chunk-ZMZTHU7V.js → chunk-IZAHPAN6.js} +1 -1
  153. package/dist-cli/chunks/{chunk-SI3BUTQR.js → chunk-J62KM5TB.js} +2 -2
  154. package/dist-cli/chunks/{chunk-Y2FSZSO7.js → chunk-JEMCD5E4.js} +1 -1
  155. package/dist-cli/chunks/{chunk-T4FT6CM7.js → chunk-JZS3Q37N.js} +2 -2
  156. package/dist-cli/chunks/{chunk-2SA2VTGP.js → chunk-KR4JON7D.js} +1 -1
  157. package/dist-cli/chunks/chunk-KWCYKQIQ.js +4 -0
  158. package/dist-cli/chunks/{chunk-HG7ORJMT.js → chunk-MCWPL644.js} +2 -2
  159. package/dist-cli/chunks/chunk-MJYK3N2I.js +4 -0
  160. package/dist-cli/chunks/{chunk-YRV7HR3P.js → chunk-O6TRIZNS.js} +2 -2
  161. package/dist-cli/chunks/{chunk-WBT32R6N.js → chunk-P7XL2E73.js} +3 -3
  162. package/dist-cli/chunks/{chunk-MNGQ42QE.js → chunk-PFQTUKQ4.js} +62 -87
  163. package/dist-cli/chunks/{chunk-Q3RGLERJ.js → chunk-PG5RZCTN.js} +2 -2
  164. package/dist-cli/chunks/{chunk-OR3XTXEB.js → chunk-QGRI2Z4M.js} +2 -2
  165. package/dist-cli/chunks/chunk-QLXXE7GE.js +125 -0
  166. package/dist-cli/chunks/{chunk-KZW5WBGU.js → chunk-QOJJJWJE.js} +89 -133
  167. package/dist-cli/chunks/{chunk-BRIZOR4K.js → chunk-RWZY5427.js} +2 -2
  168. package/dist-cli/chunks/{chunk-TYE3WBSR.js → chunk-RZKU2K3J.js} +2 -2
  169. package/dist-cli/chunks/{chunk-7VWOSTD5.js → chunk-SBV4IK4H.js} +1 -1
  170. package/dist-cli/chunks/{chunk-FWYYCUAZ.js → chunk-SGMVFFMK.js} +1 -1
  171. package/dist-cli/chunks/{chunk-LDQ7HVCW.js → chunk-TAX4UT2N.js} +1 -1
  172. package/dist-cli/chunks/{chunk-G74YIEVS.js → chunk-TUOFAWXT.js} +1 -1
  173. package/dist-cli/chunks/{chunk-274UD5FD.js → chunk-UHZLOHGP.js} +1 -1
  174. package/dist-cli/chunks/{chunk-37I5AIS7.js → chunk-VC7V76U3.js} +1 -1
  175. package/dist-cli/chunks/{chunk-B7KMNHX7.js → chunk-VQVMLW4U.js} +1 -1
  176. package/dist-cli/chunks/{chunk-VZBLFMYC.js → chunk-VUKKYPZN.js} +2 -2
  177. package/dist-cli/chunks/{chunk-VUFRB347.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-TCWQJILS.js → chunk-XULEACM4.js} +2 -2
  181. package/dist-cli/chunks/{chunk-XRGEEPJ5.js → chunk-YFSDM7AX.js} +4 -4
  182. package/dist-cli/chunks/chunk-YWI3UEVX.js +5 -0
  183. package/dist-cli/chunks/{chunk-TGQNQ6XT.js → chunk-ZBSJO4NB.js} +10 -9
  184. package/dist-cli/chunks/{cleanup-H3GWQB63.js → cleanup-EYLGCA6Z.js} +2 -2
  185. package/dist-cli/chunks/cli-version-LL2UGIHE.js +4 -0
  186. package/dist-cli/chunks/{compat-5WG2XJV7.js → compat-TLJYHB4E.js} +2 -2
  187. package/dist-cli/chunks/{config-ANMBVVLL.js → config-4JWUOEXK.js} +2 -2
  188. package/dist-cli/chunks/{control-NSGNQFJG.js → control-HPAOYF4N.js} +2 -2
  189. package/dist-cli/chunks/daemon-OYLASXLE.js +4 -0
  190. package/dist-cli/chunks/{debug-CHV2O2KF.js → debug-4BKXF6KI.js} +5 -5
  191. package/dist-cli/chunks/{desktop-FIPIK4CH.js → desktop-ZQ6ZD2S6.js} +3 -3
  192. package/dist-cli/chunks/{detox-CVUJP7K6.js → detox-WNPASSS3.js} +2 -2
  193. package/dist-cli/chunks/{device-6WCUM4W6.js → device-BXLXVG3V.js} +2 -2
  194. package/dist-cli/chunks/{diagnose-I6EMWEII.js → diagnose-3QX7W5RW.js} +2 -2
  195. package/dist-cli/chunks/{disk-cleanup-P27CYFTN.js → disk-cleanup-KSWKI7WB.js} +2 -2
  196. package/dist-cli/chunks/drivers-EPIEFF7P.js +4 -0
  197. package/dist-cli/chunks/{film-UOQFFYNF.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-NJYFSJKZ.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-BPFQENCX.js → login-IJAPUZHI.js} +4 -4
  205. package/dist-cli/chunks/{logout-5A45ZGY7.js → logout-QMXDFU2X.js} +2 -2
  206. package/dist-cli/chunks/{maestro-E4CSDMLM.js → maestro-ZXU3YCVX.js} +3 -3
  207. package/dist-cli/chunks/{maestro-generate-CKMOJQX2.js → maestro-generate-PYB5QY7K.js} +3 -3
  208. package/dist-cli/chunks/{mode-SZYNMIQF.js → mode-WGUCL5FZ.js} +2 -2
  209. package/dist-cli/chunks/{optional-demo-registry-FRH4SFVG.js → optional-demo-registry-WH2O6H36.js} +2 -2
  210. package/dist-cli/chunks/{perf-VXU2XJAX.js → perf-TOD3UFAH.js} +2 -2
  211. package/dist-cli/chunks/{permissions-IRIAOVND.js → permissions-I5BRJGTB.js} +2 -2
  212. package/dist-cli/chunks/{record-RY7FPLLH.js → record-ZYL2FYSK.js} +3 -3
  213. package/dist-cli/chunks/{report-issue-KNWXOE53.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-QLALR6GY.js → screenshot-command-7ANLODZY.js} +7 -7
  217. package/dist-cli/chunks/{screenshot-layers-OQFMFVRL.js → screenshot-layers-A7FYXSVU.js} +3 -3
  218. package/dist-cli/chunks/{screenshots-capture-6TSRJFGB.js → screenshots-capture-WT2ZY6CB.js} +2 -2
  219. package/dist-cli/chunks/serve-TG5WKA4V.js +44 -0
  220. package/dist-cli/chunks/{setup-7V6UKX4U.js → setup-PCWLD22V.js} +2 -2
  221. package/dist-cli/chunks/{skills-QQBOSETH.js → skills-27ZHWCQS.js} +2 -2
  222. package/dist-cli/chunks/state-ZQVY46ZO.js +14 -0
  223. package/dist-cli/chunks/{storage-TAPMKK5O.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-IAM564NA.js → timeline-RAJJFQT6.js} +2 -2
  227. package/dist-cli/chunks/{upgrade-CD55QXIX.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-6NW6YLPI.js → web-XBUTBVGR.js} +2 -2
  231. package/dist-cli/chunks/{what-happened-CZZDOOVG.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 +493 -282
  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-6EAK7CUL.js +0 -22
  277. package/dist-cli/chunks/chunk-33FKQH54.js +0 -5
  278. package/dist-cli/chunks/chunk-66NF2KNG.js +0 -9
  279. package/dist-cli/chunks/chunk-6UKJB2VC.js +0 -125
  280. package/dist-cli/chunks/chunk-7INBNA3U.js +0 -26
  281. package/dist-cli/chunks/chunk-AY47WKS5.js +0 -23
  282. package/dist-cli/chunks/chunk-HMDOLCKG.js +0 -15
  283. package/dist-cli/chunks/chunk-KR5BGMBZ.js +0 -4
  284. package/dist-cli/chunks/chunk-NLEMQ4PY.js +0 -6
  285. package/dist-cli/chunks/chunk-PEPUGJVI.js +0 -4
  286. package/dist-cli/chunks/cli-version-3QPAJ3LC.js +0 -4
  287. package/dist-cli/chunks/daemon-JHF4BJ3U.js +0 -4
  288. package/dist-cli/chunks/drivers-HQ6AKZT6.js +0 -4
  289. package/dist-cli/chunks/flow-N27MRRNL.js +0 -4
  290. package/dist-cli/chunks/help-SF6FEGEE.js +0 -4
  291. package/dist-cli/chunks/home-paths-XOV44JEF.js +0 -4
  292. package/dist-cli/chunks/inspect-CWFRFQTY.js +0 -4
  293. package/dist-cli/chunks/install-desktop-KLQDNXZQ.js +0 -4
  294. package/dist-cli/chunks/runtime-AR3GJTGK.js +0 -4
  295. package/dist-cli/chunks/serve-F54J2NQO.js +0 -44
  296. package/dist-cli/chunks/store-7NX3S43H.js +0 -4
  297. package/dist-cli/chunks/telemetry-E7BUK6RA.js +0 -4
  298. package/dist-cli/chunks/upload-GZ6LIMYM.js +0 -4
  299. package/dist-cli/chunks/version-3DYJ5AS3.js +0 -6
package/cli/bin.ts ADDED
@@ -0,0 +1,594 @@
1
+ #!/usr/bin/env node
2
+
3
+ // sootsim CLI entry point. the full command and verb surface lives in the
4
+ // registry at `packages/sootsim-skills/src/cli/meta.ts` — help output,
5
+ // website docs, and agent skills all read from there. do not duplicate
6
+ // command listings in this file; run `sootsim --help` for the canonical
7
+ // surface.
8
+
9
+ import { rnxPublicBrand } from '../src/public-brand'
10
+ import { parseArgs, TOP_LEVEL_RUNTIME_COMMANDS } from './parse-args'
11
+
12
+ // `./help` is loaded lazily (only on help paths) — it statically pulls the full
13
+ // CLI registry renderers and engine settings, which would otherwise add ~400ms
14
+ // of eager chunk load to every command, including hot-path reads like
15
+ // `describe` / `do tap` that never render help. parse-args has no imports, so
16
+ // the startup floor stays at the runtime's own cost (~15ms).
17
+
18
+ // keep top-level dispatch crashes readable. without this, a 10s WS bridge
19
+ // timeout (the most common failure when a sim is dead) prints a 1KB minified
20
+ // chunk of the bundled CLI source straight to stderr, which buries the actual
21
+ // "command timed out after 10s" message and is uncopyable for a bug report.
22
+ // rejections that don't match this shape still get the verbose stack so we
23
+ // don't paper over real bugs.
24
+ function handleTopLevelError(err: unknown): never {
25
+ const msg = err instanceof Error ? err.message : String(err)
26
+ // sootsim's known top-level errors are all single-line, narrow strings.
27
+ // everything else gets the full stack so unexpected crashes still surface.
28
+ const isKnownCliFailure =
29
+ /^command timed out after \d+s$/.test(msg) ||
30
+ msg.startsWith('sim disconnected:') ||
31
+ msg.startsWith('bridge never reconnected') ||
32
+ msg.startsWith('multiple sims are connected:') ||
33
+ msg.startsWith('saved sim ') ||
34
+ msg.startsWith('no sim connected with id ') ||
35
+ msg.startsWith('could not connect to ws://') ||
36
+ msg.startsWith('rnx bridge daemon is not running') ||
37
+ msg.startsWith('rnx bridge lockfile is fresh') ||
38
+ msg.startsWith('rnx bridge exited before becoming ready') ||
39
+ msg.startsWith(`${rnxPublicBrand.commandName} open: requested bridge port`) ||
40
+ msg.startsWith('background daemon setup is unavailable') ||
41
+ msg.startsWith('rnx bridge did not start within')
42
+ if (isKnownCliFailure && !process.env.SOOTSIM_VERBOSE) {
43
+ process.stderr.write(` ${msg}\n`)
44
+ process.exit(1)
45
+ }
46
+ if (err instanceof Error && err.stack) {
47
+ process.stderr.write(`${err.stack}\n`)
48
+ } else {
49
+ process.stderr.write(`${msg}\n`)
50
+ }
51
+ process.exit(1)
52
+ }
53
+ process.on('unhandledRejection', handleTopLevelError)
54
+ process.on('uncaughtException', handleTopLevelError)
55
+
56
+ // set to `true` by build-cli-binary.ts via esbuild define. in the standalone
57
+ // binary, commands that rely on vite / electron / playwright from the user's
58
+ // node_modules can't run — gate them with a friendly message rather than
59
+ // letting the dynamic import fail with a confusing module-not-found error.
60
+ declare const __SOOTSIM_STANDALONE__: boolean | undefined
61
+ const IS_STANDALONE =
62
+ typeof __SOOTSIM_STANDALONE__ !== 'undefined' && __SOOTSIM_STANDALONE__
63
+
64
+ const STANDALONE_BLOCKED_COMMANDS = new Set(['desktop'])
65
+
66
+ function exitIfStandaloneBlocked(cmd: string): void {
67
+ if (!IS_STANDALONE || !STANDALONE_BLOCKED_COMMANDS.has(cmd)) return
68
+ process.stderr.write(
69
+ ` ${rnxPublicBrand.commandName} ${cmd} isn't available in the standalone binary —\n` +
70
+ ` it needs vite / electron / playwright from a project's node_modules.\n` +
71
+ ` run the npm package instead:\n` +
72
+ ` ${rnxPublicBrand.commandName} ${cmd}\n`,
73
+ )
74
+ process.exit(1)
75
+ }
76
+
77
+ let parsed = parseArgs(process.argv)
78
+
79
+ // runtime actions and reads also work as hidden root aliases. resolve only
80
+ // after the normal parser declined the token, so a real top-level command
81
+ // always wins. keep this lazy so ordinary commands don't load the docs registry.
82
+ if (!parsed.command && parsed.commandArgs.length > 0) {
83
+ const { resolveHiddenRuntimeAlias } = await import('./hidden-runtime-alias')
84
+ parsed = resolveHiddenRuntimeAlias(parsed)
85
+ }
86
+
87
+ const isAutomaticCleanupWorker =
88
+ parsed.command === 'cleanup' &&
89
+ parsed.commandArgs.length === 1 &&
90
+ parsed.commandArgs[0] === '--automatic-worker'
91
+
92
+ // invocation telemetry. enqueued here, flushed by command-specific paths
93
+ // (most commands run long enough that the beforeExit drain catches the
94
+ // event; fast-exit paths like --version/--help explicitly flush below).
95
+ if (!isAutomaticCleanupWorker) {
96
+ const { trackCliEvent } = await import('./telemetry')
97
+ trackCliEvent({
98
+ event: 'cli_command_invoked',
99
+ properties: {
100
+ command:
101
+ parsed.command || (parsed.version ? 'version' : parsed.help ? 'help' : 'none'),
102
+ arg_count: parsed.commandArgs.length,
103
+ platform: process.platform,
104
+ arch: process.arch,
105
+ node_version: process.versions.node,
106
+ },
107
+ })
108
+ }
109
+
110
+ // one-shot "engine upgraded in the background" banner. the daemon's hourly
111
+ // auto-updater writes a notice file when it swaps the runtime; whichever
112
+ // interactive command runs next prints it once (stderr, so --json stdout
113
+ // stays parseable) and deletes it. the daemon/serve processes skip it so a
114
+ // background process can't eat the notice before a human sees it.
115
+ if (parsed.command !== 'serve' && parsed.command !== 'daemon') {
116
+ const { consumeRuntimeUpgradeNotice } = await import('../src/home-paths')
117
+ const notice = consumeRuntimeUpgradeNotice()
118
+ if (notice) {
119
+ process.stderr.write(
120
+ ` ${rnxPublicBrand.name} engine upgraded to v${notice.to}` +
121
+ (notice.from ? ` (from v${notice.from})` : '') +
122
+ ` · what's new: ${rnxPublicBrand.origin}/changelog\n`,
123
+ )
124
+ }
125
+ }
126
+
127
+ let automaticCleanupFinished = false
128
+ async function scheduleAutomaticCleanup(): Promise<void> {
129
+ if (automaticCleanupFinished) return
130
+ automaticCleanupFinished = true
131
+ if (
132
+ parsed.command === 'cleanup' ||
133
+ parsed.command === 'serve' ||
134
+ parsed.command === 'agent-wrapper' ||
135
+ (parsed.command === 'daemon' && parsed.commandArgs[0] === 'uninstall')
136
+ ) {
137
+ return
138
+ }
139
+
140
+ try {
141
+ const { isAutomaticSootsimCleanupComplete } = await import('../src/disk-cleanup')
142
+ if (isAutomaticSootsimCleanupComplete()) return
143
+
144
+ const { spawn } = await import('node:child_process')
145
+ const workerArgs = ['cleanup', '--automatic-worker']
146
+ const entry = process.argv[1]
147
+ const child = IS_STANDALONE
148
+ ? spawn(process.execPath, workerArgs, {
149
+ detached: true,
150
+ stdio: ['ignore', 'ignore', 'inherit'],
151
+ env: process.env,
152
+ })
153
+ : entry
154
+ ? spawn(process.execPath, [entry, ...workerArgs], {
155
+ detached: true,
156
+ stdio: ['ignore', 'ignore', 'inherit'],
157
+ env: process.env,
158
+ })
159
+ : spawn(rnxPublicBrand.commandName, workerArgs, {
160
+ detached: true,
161
+ stdio: ['ignore', 'ignore', 'inherit'],
162
+ env: process.env,
163
+ })
164
+ child.once('error', (error) => {
165
+ process.stderr.write(
166
+ ` ${rnxPublicBrand.name} automatic cleanup will retry on the next run: ${error.message}\n`,
167
+ )
168
+ })
169
+ child.unref()
170
+ } catch (error) {
171
+ process.stderr.write(
172
+ ` ${rnxPublicBrand.name} automatic cleanup will retry on the next run: ${
173
+ error instanceof Error ? error.message : String(error)
174
+ }\n`,
175
+ )
176
+ }
177
+ }
178
+
179
+ // fast-exit helper: every path that calls process.exit before reaching a
180
+ // command function would lose the cli_command_invoked event because node
181
+ // doesn't await pending fetches on exit. flush first, then exit.
182
+ async function exitWithFlush(code: number): Promise<never> {
183
+ const { flushCliTelemetry } = await import('./telemetry')
184
+ await flushCliTelemetry()
185
+ await scheduleAutomaticCleanup()
186
+ process.exit(code)
187
+ }
188
+
189
+ // --version
190
+ if (parsed.version) {
191
+ // use the single canonical, cached version source — the same one
192
+ // `--help`, `upgrade`, the startup flow, and the bridge-host use. it
193
+ // previously re-read package.json independently here, which could
194
+ // resolve a different copy (stale global install vs repo) and drift
195
+ // from `--help` within one session (QA F20-4).
196
+ const { getCliVersion } = await import('../src/cli-version')
197
+ const { IS_BETA, BETA_LABEL } = await import('../src/beta')
198
+ const suffix = IS_BETA ? ` · ${BETA_LABEL}` : ''
199
+ console.log(`rnxsim v${getCliVersion()}${suffix}`)
200
+ // runtime version on its own line — keeps the first line parseable while
201
+ // surfacing the (independently-versioned) engine runtime users actually
202
+ // render with. see `sootsim upgrade`.
203
+ const { readActiveRuntime } = await import('../src/home-paths')
204
+ const runtime = readActiveRuntime()
205
+ console.log(runtime ? `runtime v${runtime}` : 'runtime not installed')
206
+ await exitWithFlush(0)
207
+ }
208
+
209
+ // --help with no command
210
+ if (parsed.help && !parsed.command) {
211
+ const { printHelp } = await import('./help')
212
+ printHelp()
213
+ await exitWithFlush(0)
214
+ }
215
+
216
+ // apply settings from global flags
217
+ const port = parsed.globalFlags['port'] as number | undefined
218
+ const verbose = parsed.verbose
219
+ const device = parsed.globalFlags['device'] as string | undefined
220
+ const theme = parsed.globalFlags['theme'] as string | undefined
221
+ const driver = parsed.globalFlags['driver'] as string | undefined
222
+ const headless = (parsed.globalFlags['headless'] as boolean | undefined) === true
223
+ const globalSimTarget = (parsed.globalFlags['sim'] ??
224
+ parsed.globalFlags['session'] ??
225
+ parsed.globalFlags['tab']) as string | undefined
226
+ const commandArgsWithSim = globalSimTarget
227
+ ? ['--sim', globalSimTarget, ...parsed.commandArgs]
228
+ : parsed.commandArgs
229
+
230
+ // apply device/theme to settings store if provided
231
+ if (device || theme) {
232
+ const { settingsStore } = await import('sootsim-engine/settings/store')
233
+ const overrides: Record<string, any> = {}
234
+ if (device) overrides.deviceModel = device
235
+ if (theme) overrides.colorScheme = theme
236
+ settingsStore.apply(overrides)
237
+ }
238
+
239
+ // no command, no args: show the clean surface. setup/open own actual work.
240
+ if (!parsed.command && parsed.commandArgs.length === 0) {
241
+ const { runSetup, shouldRunSetupForBareCommand } = await import('./commands/setup')
242
+ if (shouldRunSetupForBareCommand()) {
243
+ await runSetup(['--from-welcome'])
244
+ await exitWithFlush(0)
245
+ }
246
+ const { printHelp } = await import('./help')
247
+ printHelp()
248
+ await exitWithFlush(0)
249
+ }
250
+
251
+ // route to command. an empty command at this point only happens if the
252
+ // caller passed bare positional args without a verb (e.g. `sootsim -- foo`),
253
+ // which used to fall through to the bundler-wrap behavior in `dev`. now that
254
+ // `dev` is gone, treat it as "show help".
255
+ const command = parsed.command ?? ''
256
+ if (!command) {
257
+ // a bare positional that isn't a known command lands here (parse-args
258
+ // leaves `command` null and pushes the token into commandArgs). don't
259
+ // silently dump the full help banner — name the unrecognized token so a
260
+ // typo'd subcommand is obvious. matches the switch `default:` wording.
261
+ if (parsed.commandArgs.length > 0) {
262
+ const attempted =
263
+ parsed.commandArgs.find((a) => !a.startsWith('-')) ?? parsed.commandArgs[0]
264
+ console.error(` unknown command: ${attempted}`)
265
+ console.error(` run \`rnxsim --help\` to see the full surface.`)
266
+ await exitWithFlush(1)
267
+ }
268
+ const { printHelp } = await import('./help')
269
+ printHelp()
270
+ await exitWithFlush(0)
271
+ }
272
+
273
+ // --help with a command (either global flag or in commandArgs)
274
+ if (
275
+ parsed.help ||
276
+ parsed.commandArgs.includes('--help') ||
277
+ parsed.commandArgs.includes('-h')
278
+ ) {
279
+ if (command === 'skill') {
280
+ const { runSkill } = await import('./commands/skills')
281
+ await runSkill(parsed.commandArgs)
282
+ await exitWithFlush(0)
283
+ }
284
+ if (command === 'state') {
285
+ const { runState } = await import('./commands/state')
286
+ await exitWithFlush(await runState(commandArgsWithSim, { port }))
287
+ }
288
+ if (command === 'reset') {
289
+ const { runReset } = await import('./commands/reset')
290
+ await exitWithFlush(await runReset(commandArgsWithSim, { port }))
291
+ }
292
+ // for grouping verbs, try to resolve per-verb help from the first
293
+ // positional arg. e.g. `sootsim do tap --help` shows tap's help page,
294
+ // not the generic `do` grouping page. bare `sootsim do --help` falls
295
+ // through to runInspect which has the full verb group listing.
296
+ const isGroupingVerb =
297
+ command === 'do' ||
298
+ command === 'get' ||
299
+ command === 'debug' ||
300
+ command === 'shell' ||
301
+ command === 'perf' ||
302
+ command === 'wait'
303
+ const subVerb = parsed.commandArgs.find((a) => !a.startsWith('-'))
304
+ if (command === 'shell' || command === 'perf') {
305
+ // shell and perf subcommands are documented by their runtime dispatchers.
306
+ // don't try to route through generated per-verb docs.
307
+ } else if (isGroupingVerb && !subVerb) {
308
+ // bare `sootsim <group> --help` — render from the registry.
309
+ const { printGroupHelp } = await import('./help')
310
+ if (printGroupHelp(command)) await exitWithFlush(0)
311
+ // fall through — runInspect shows the full help for anything the
312
+ // registry doesn't yet cover (e.g. shell).
313
+ } else {
314
+ const { printCommandHelp } = await import('./help')
315
+ const helpName = isGroupingVerb && subVerb ? subVerb : command
316
+ printCommandHelp(helpName, {
317
+ prefer: isGroupingVerb && subVerb ? 'verb' : 'command',
318
+ group: isGroupingVerb && subVerb ? command : undefined,
319
+ })
320
+ await exitWithFlush(0)
321
+ }
322
+ }
323
+
324
+ // gate heavy commands in the standalone binary before any dynamic imports
325
+ // fire. the imports themselves would also fail (vite / electron etc. are
326
+ // kept external from the bundle), but the resulting module-not-found error
327
+ // is much harder to act on than this targeted message.
328
+ exitIfStandaloneBlocked(command)
329
+
330
+ if (TOP_LEVEL_RUNTIME_COMMANDS.has(command)) {
331
+ const { runInspect } = await import('./commands/inspect')
332
+ await runInspect([command, ...commandArgsWithSim], { port, verbose })
333
+ } else {
334
+ switch (command) {
335
+ case 'assert': {
336
+ // stays outside the usual dispatcher — it spawns self to get the
337
+ // inner verb's --json payload, so it never needs the shared bridge
338
+ // machinery that lives inside runInspect.
339
+ const { runAssert } = await import('./commands/assert')
340
+ await runAssert(parsed.commandArgs)
341
+ break
342
+ }
343
+
344
+ case 'detox': {
345
+ const { runDetox } = await import('./commands/detox')
346
+ await runDetox(parsed.commandArgs, { port, verbose })
347
+ break
348
+ }
349
+
350
+ case 'maestro': {
351
+ const { runMaestro } = await import('./commands/maestro')
352
+ // Maestro forwards playback argv to the shared runner, so it must
353
+ // receive the global `--sim`. passing the bare
354
+ // parsed.commandArgs dropped an explicit `sootsim --sim <id> maestro
355
+ // …`, silently falling back to the current/saved sim (QA F21-4).
356
+ const code = await runMaestro(commandArgsWithSim, { port, verbose })
357
+ // bridge + dynamic imports leave handles open that keep node alive.
358
+ await exitWithFlush(typeof code === 'number' ? code : 0)
359
+ }
360
+
361
+ case 'record': {
362
+ const { runRecord } = await import('./commands/record')
363
+ await runRecord(commandArgsWithSim, { port, verbose })
364
+ break
365
+ }
366
+
367
+ case 'film': {
368
+ const { runFilm } = await import('./commands/film')
369
+ // film chains the flow runner, which opens ws bridges + dynamic
370
+ // imports that keep node alive — exit explicitly like `flow` does.
371
+ const code = await runFilm(commandArgsWithSim, { port, verbose })
372
+ await exitWithFlush(typeof code === 'number' ? code : 0)
373
+ }
374
+
375
+ case 'storage': {
376
+ const { runStorage } = await import('./commands/storage')
377
+ const code = await runStorage(commandArgsWithSim, { port, verbose })
378
+ await exitWithFlush(typeof code === 'number' ? code : 0)
379
+ }
380
+
381
+ case 'state': {
382
+ const { runState } = await import('./commands/state')
383
+ const code = await runState(commandArgsWithSim, { port })
384
+ await exitWithFlush(code)
385
+ }
386
+
387
+ case 'reset': {
388
+ const { runReset } = await import('./commands/reset')
389
+ const code = await runReset(commandArgsWithSim, { port })
390
+ await exitWithFlush(code)
391
+ }
392
+
393
+ case 'perf': {
394
+ const { runPerf } = await import('./commands/perf')
395
+ const code = await runPerf(commandArgsWithSim, { port, verbose })
396
+ await exitWithFlush(typeof code === 'number' ? code : 0)
397
+ }
398
+
399
+ case 'screenshot': {
400
+ const { runScreenshotCommand } = await import('./commands/screenshot-command')
401
+ const code = await runScreenshotCommand(commandArgsWithSim, { port, verbose })
402
+ await exitWithFlush(typeof code === 'number' ? code : 0)
403
+ }
404
+
405
+ case 'camera': {
406
+ const { runCamera } = await import('./commands/camera')
407
+ const code = await runCamera(commandArgsWithSim, { port })
408
+ await exitWithFlush(typeof code === 'number' ? code : 0)
409
+ }
410
+
411
+ case 'mode': {
412
+ const { runMode } = await import('./commands/mode')
413
+ await runMode(commandArgsWithSim, { port, verbose })
414
+ break
415
+ }
416
+
417
+ case 'permissions': {
418
+ const { runPermissions } = await import('./commands/permissions')
419
+ const code = await runPermissions(commandArgsWithSim, { port, verbose })
420
+ await exitWithFlush(code)
421
+ }
422
+
423
+ case 'inspect': {
424
+ const { runInspect } = await import('./commands/inspect')
425
+ await runInspect(commandArgsWithSim, { port, verbose })
426
+ break
427
+ }
428
+
429
+ case 'debug': {
430
+ const { runDebug } = await import('./commands/debug')
431
+ await runDebug(commandArgsWithSim, { port, verbose })
432
+ break
433
+ }
434
+
435
+ case 'timeline': {
436
+ const { runTimeline } = await import('./commands/timeline')
437
+ await runTimeline(commandArgsWithSim, { port, verbose })
438
+ break
439
+ }
440
+
441
+ case 'what-happened': {
442
+ const { runWhatHappened } = await import('./commands/what-happened')
443
+ await runWhatHappened(commandArgsWithSim, { port, verbose })
444
+ break
445
+ }
446
+
447
+ case 'open': {
448
+ const { runOpenCommand } = await import('./commands/control')
449
+ await runOpenCommand(commandArgsWithSim, { port })
450
+ break
451
+ }
452
+
453
+ case 'use': {
454
+ const { runUseCommand } = await import('./commands/control')
455
+ await runUseCommand(commandArgsWithSim, { port })
456
+ break
457
+ }
458
+
459
+ case 'claim': {
460
+ const { runClaimCommand } = await import('./commands/control')
461
+ await runClaimCommand(commandArgsWithSim, { port })
462
+ break
463
+ }
464
+
465
+ case 'close': {
466
+ const { runCloseCommand } = await import('./commands/control')
467
+ await runCloseCommand(commandArgsWithSim, { port })
468
+ break
469
+ }
470
+
471
+ case 'device': {
472
+ const { runDeviceCommand } = await import('./commands/device')
473
+ await runDeviceCommand(commandArgsWithSim, { port })
474
+ break
475
+ }
476
+
477
+ case 'compat': {
478
+ const { runCompat } = await import('./commands/compat')
479
+ await runCompat(parsed.commandArgs)
480
+ break
481
+ }
482
+
483
+ case 'report-issue': {
484
+ const { runReportIssue } = await import('./commands/report-issue')
485
+ const code = await runReportIssue(parsed.commandArgs)
486
+ await exitWithFlush(code)
487
+ }
488
+
489
+ case 'desktop': {
490
+ const { runDesktop } = await import('./commands/desktop')
491
+ await runDesktop(parsed.commandArgs, { port, device })
492
+ break
493
+ }
494
+
495
+ case 'login': {
496
+ const { runLogin } = await import('./commands/login')
497
+ await runLogin(parsed.commandArgs)
498
+ break
499
+ }
500
+
501
+ case 'logout': {
502
+ const { runLogout } = await import('./commands/logout')
503
+ await runLogout()
504
+ break
505
+ }
506
+
507
+ case 'auth': {
508
+ const { runAuth } = await import('./commands/auth')
509
+ await runAuth(parsed.commandArgs)
510
+ break
511
+ }
512
+
513
+ case 'setup': {
514
+ const { runSetup } = await import('./commands/setup')
515
+ await runSetup(parsed.commandArgs)
516
+ break
517
+ }
518
+
519
+ case 'serve': {
520
+ const { runServe } = await import('./commands/serve')
521
+ await runServe(parsed.commandArgs, { port })
522
+ break
523
+ }
524
+
525
+ case 'daemon': {
526
+ const { runDaemon } = await import('./commands/daemon')
527
+ await runDaemon(parsed.commandArgs, { port })
528
+ break
529
+ }
530
+
531
+ case 'runtime': {
532
+ const { runRuntime } = await import('./commands/runtime')
533
+ await runRuntime(parsed.commandArgs)
534
+ break
535
+ }
536
+
537
+ case 'upgrade':
538
+ case 'update': {
539
+ const { runUpgrade } = await import('./commands/upgrade')
540
+ await runUpgrade(parsed.commandArgs)
541
+ break
542
+ }
543
+
544
+ case 'version': {
545
+ const { runVersion } = await import('./commands/version')
546
+ await runVersion(parsed.commandArgs)
547
+ break
548
+ }
549
+
550
+ case 'agent': {
551
+ const { runAgentCommand } = await import('./commands/agent')
552
+ const code = await runAgentCommand(parsed.commandArgs)
553
+ await exitWithFlush(code)
554
+ }
555
+
556
+ case 'agent-wrapper': {
557
+ const { runAgentWrapper } = await import('./commands/agent-wrapper')
558
+ const code = await runAgentWrapper(parsed.commandArgs)
559
+ await exitWithFlush(code)
560
+ }
561
+
562
+ case 'skill': {
563
+ const { runSkill } = await import('./commands/skills')
564
+ await runSkill(parsed.commandArgs)
565
+ break
566
+ }
567
+
568
+ case 'app-fonts': {
569
+ const { runAppFonts } = await import('./commands/app-fonts')
570
+ await runAppFonts(parsed.commandArgs)
571
+ break
572
+ }
573
+
574
+ case 'config': {
575
+ const { runConfig } = await import('./commands/config')
576
+ await runConfig(parsed.commandArgs)
577
+ break
578
+ }
579
+
580
+ case 'cleanup': {
581
+ const { runCleanup } = await import('./commands/cleanup')
582
+ const code = await runCleanup(parsed.commandArgs)
583
+ await exitWithFlush(code)
584
+ }
585
+
586
+ default: {
587
+ console.error(` unknown command: ${command}`)
588
+ console.error(` run \`rnxsim --help\` to see the full surface.`)
589
+ await exitWithFlush(1)
590
+ }
591
+ }
592
+ }
593
+
594
+ await scheduleAutomaticCleanup()