@hienlh/ppm 0.18.12 → 0.18.13

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 (532) hide show
  1. package/AGENTS.md +17 -5
  2. package/CHANGELOG.md +22 -0
  3. package/CLAUDE.md +3 -0
  4. package/README.md +6 -6
  5. package/assets/skills/ppm/SKILL.md +1 -1
  6. package/assets/skills/ppm/references/http-api.md +19 -1
  7. package/dist/web/assets/{adaptive-context-menu-DGCKKeTy.js → adaptive-context-menu-CbgPYQmj.js} +1 -1
  8. package/dist/web/assets/{ai-resource-editor-BOHHTTe7.js → ai-resource-editor-unM2HNmg.js} +1 -1
  9. package/dist/web/assets/{ai-settings-section-BigC_nZR.js → ai-settings-section-CdktdrwF.js} +1 -1
  10. package/dist/web/assets/{angular-html-BYPaydvh.js → angular-html-DcpbdcNG.js} +1 -1
  11. package/dist/web/assets/{angular-ts-Bb4AMKlk.js → angular-ts-Bv_WIk34.js} +1 -1
  12. package/dist/web/assets/api-client-BogqsFna.js +1 -0
  13. package/dist/web/assets/{api-settings-Bd3M4YpI.js → api-settings-BdCtex9i.js} +1 -1
  14. package/dist/web/assets/api-settings-DfXViPKz.js +1 -0
  15. package/dist/web/assets/{api-themes-Cpx419SL.js → api-themes-pRRoLvgD.js} +1 -1
  16. package/dist/web/assets/{apl-DB2VjFCS.js → apl-D5_lLszu.js} +1 -1
  17. package/dist/web/assets/architecture-PBZL5I3N-BzFm9ELu.js +1 -0
  18. package/dist/web/assets/{astro-BeoJdVpk.js → astro-CBHwddYL.js} +1 -1
  19. package/dist/web/assets/{audio-preview-BgFuTn-0.js → audio-preview-BD1iDovf.js} +1 -1
  20. package/dist/web/assets/{blade-Cf2q1sPf.js → blade-DGe1d2Pq.js} +1 -1
  21. package/dist/web/assets/{bsl-W1kOaNpu.js → bsl-2lyAzRJA.js} +1 -1
  22. package/dist/web/assets/{button-5zTrkEoN.js → button-2WCAS8dI.js} +1 -1
  23. package/dist/web/assets/{cairo-BoAZFcje.js → cairo-DjzZeJrq.js} +1 -1
  24. package/dist/web/assets/chat-tab-DzK8LGti.js +15 -0
  25. package/dist/web/assets/{cobol-B0Nm7RXj.js → cobol-cC3XIuAJ.js} +1 -1
  26. package/dist/web/assets/code-editor-CuMfPN9X.js +10 -0
  27. package/dist/web/assets/{coffee-BakMn3on.js → coffee-4eGboVoU.js} +1 -1
  28. package/dist/web/assets/{conflict-editor-BUs5k9q_.js → conflict-editor-DdJag4QK.js} +1 -1
  29. package/dist/web/assets/{context-menu-Bq92km4s.js → context-menu-BO1c9jKy.js} +1 -1
  30. package/dist/web/assets/{cpp-BRAJQHK2.js → cpp-DF9_G-OU.js} +1 -1
  31. package/dist/web/assets/{crystal-kqqO3zkK.js → crystal-OavZlzcm.js} +1 -1
  32. package/dist/web/assets/{csv-preview-ByoDgLAZ.js → csv-preview-DnXwouF3.js} +1 -1
  33. package/dist/web/assets/{data-grid-overlay-editor-Brkf9z41.js → data-grid-overlay-editor-B9Gpjm3l.js} +1 -1
  34. package/dist/web/assets/{database-viewer-Btc3h8Yu.js → database-viewer-Dk8oGyZx.js} +1 -1
  35. package/dist/web/assets/{dialog-DwtGlowE.js → dialog-B-l2ZxVm.js} +1 -1
  36. package/dist/web/assets/{diff-viewer-CZaDnR4w.js → diff-viewer-eaUBjLCH.js} +1 -1
  37. package/dist/web/assets/{docx-preview-DAD_vg59.js → docx-preview-DAdHPlhZ.js} +1 -1
  38. package/dist/web/assets/{edge-Cq1ZKZuE.js → edge-TzYh8OlN.js} +1 -1
  39. package/dist/web/assets/{edit-diff-preview-B_VE7ppQ.js → edit-diff-preview-DlwP3UIU.js} +1 -1
  40. package/dist/web/assets/{elixir-BM_JoJRk.js → elixir-Cy3RhKKH.js} +1 -1
  41. package/dist/web/assets/{elm-Dw7oO08Y.js → elm-BTBZdweK.js} +1 -1
  42. package/dist/web/assets/{erb-CfKOGAfk.js → erb-dv1mI0y4.js} +1 -1
  43. package/dist/web/assets/{erlang-DlDmGVeE.js → erlang-DjeiDrkl.js} +1 -1
  44. package/dist/web/assets/{esm-DGAP0GFh.js → esm-FRwvcesu.js} +1 -1
  45. package/dist/web/assets/explorer-body-DkCkSVrN.js +2 -0
  46. package/dist/web/assets/explorer-window-content-MsnTVngu.js +1 -0
  47. package/dist/web/assets/{extension-store-Cpfh2vMm.js → extension-store-DUaZINyC.js} +1 -1
  48. package/dist/web/assets/{extension-webview-B2OnjuCu.js → extension-webview-W0qRfaAU.js} +1 -1
  49. package/dist/web/assets/{file-store-aOERIEo-.js → file-store-yReVbR8q.js} +1 -1
  50. package/dist/web/assets/{fortran-fixed-form-XnAhQWxS.js → fortran-fixed-form-BCH9DAIJ.js} +1 -1
  51. package/dist/web/assets/{fsharp-CdNvy0PG.js → fsharp-C93NYKYD.js} +1 -1
  52. package/dist/web/assets/{gdresource-DxuG5i8i.js → gdresource-BcIEE2eX.js} +1 -1
  53. package/dist/web/assets/{git-commit-3lMonsur.js → git-commit-BVjlXEy8.js} +1 -1
  54. package/dist/web/assets/{git-log-panel-Co0PBPHC.js → git-log-panel-2_4HuP47.js} +1 -1
  55. package/dist/web/assets/{git-rebase-N8Z9kIMf.js → git-rebase-Bj_21B1V.js} +1 -1
  56. package/dist/web/assets/gitGraph-HDMCJU4V-B72vcb2r.js +1 -0
  57. package/dist/web/assets/glide-data-grid-B0IY6n_Q.js +138 -0
  58. package/dist/web/assets/{glimmer-js-Dp9OXiib.js → glimmer-js-C20yj4Zv.js} +1 -1
  59. package/dist/web/assets/{glimmer-ts-CDVKBcDj.js → glimmer-ts-ipbYC7kh.js} +1 -1
  60. package/dist/web/assets/{glsl-DPJxI4us.js → glsl-Dovazu3t.js} +1 -1
  61. package/dist/web/assets/{graphql-BrFhaQQl.js → graphql-DpMlM7hC.js} +1 -1
  62. package/dist/web/assets/{group-chat-tab-rOd8J8GC.js → group-chat-tab-DRLDHOp6.js} +1 -1
  63. package/dist/web/assets/{hack-BzXbDMlA.js → hack-gc7LCBuV.js} +1 -1
  64. package/dist/web/assets/{haml-CPpkX0s5.js → haml-vtlFm9gm.js} +1 -1
  65. package/dist/web/assets/{handlebars-BqrApy52.js → handlebars-DGKhpCji.js} +1 -1
  66. package/dist/web/assets/{html-DmjUl-OA.js → html-DvfhQbXb.js} +1 -1
  67. package/dist/web/assets/{html-derivative-Cnd6XFR5.js → html-derivative-C3oTSSCN.js} +1 -1
  68. package/dist/web/assets/{http-DMLWYun2.js → http-SG6zLTo7.js} +1 -1
  69. package/dist/web/assets/{hurl-CKkCVxbj.js → hurl-BWXsYlqW.js} +1 -1
  70. package/dist/web/assets/{hxml-CAx19TtQ.js → hxml-DUdMzaoh.js} +1 -1
  71. package/dist/web/assets/{image-preview-DHUoylQ1.js → image-preview-DxgFOV1T.js} +1 -1
  72. package/dist/web/assets/index-CBn4SOoT.js +52 -0
  73. package/dist/web/assets/index-DWpNR3nB.css +2 -0
  74. package/dist/web/assets/info-3K5VOQVL-3irPxRK_.js +1 -0
  75. package/dist/web/assets/{input-DcEk7mA3.js → input-DuAF-Stj.js} +1 -1
  76. package/dist/web/assets/{jinja-BmB9YN9l.js → jinja-955w9RKM.js} +1 -1
  77. package/dist/web/assets/{jison-BaVsKKpw.js → jison-WWfqPo50.js} +1 -1
  78. package/dist/web/assets/{julia-BgVBLveX.js → julia-BFrfnaNl.js} +1 -1
  79. package/dist/web/assets/{just-C2ENIbl9.js → just-BrZdyR_I.js} +1 -1
  80. package/dist/web/assets/{keybindings-store-BGIyqXHI.js → keybindings-store-0Wqfa7Qp.js} +1 -1
  81. package/dist/web/assets/keybindings-store-d54pv2NK.js +1 -0
  82. package/dist/web/assets/{latex-BiDIWxKL.js → latex-CwtPPASD.js} +1 -1
  83. package/dist/web/assets/{liquid-NxpapreE.js → liquid-CYZuvDDd.js} +1 -1
  84. package/dist/web/assets/{lua-DVepQvwH.js → lua-BV095ZyY.js} +1 -1
  85. package/dist/web/assets/markdown-renderer-BTS8ifdu.js +1 -0
  86. package/dist/web/assets/{markdown-renderer-DlcDhgrF.js → markdown-renderer-CnRcKK3d.js} +3 -3
  87. package/dist/web/assets/{marko-CJ2606Xl.js → marko-CNeljZ1C.js} +1 -1
  88. package/dist/web/assets/{mdc-D2bjqclE.js → mdc-JfGapShy.js} +1 -1
  89. package/dist/web/assets/{media-url-H17XJF-I.js → media-url-Bm4PsduE.js} +1 -1
  90. package/dist/web/assets/{mobile-bottom-sheet-g-r2hCFa.js → mobile-bottom-sheet-M5iZDX7y.js} +1 -1
  91. package/dist/web/assets/mobile-explorer-sheet-BqK2-WuN.js +1 -0
  92. package/dist/web/assets/{nextflow-BtJwUbfF.js → nextflow-DD5C8nqX.js} +1 -1
  93. package/dist/web/assets/{nginx-j4gyIhHE.js → nginx-Ca7B__x_.js} +1 -1
  94. package/dist/web/assets/{nim-ClH_gjP5.js → nim-B8FogsDw.js} +1 -1
  95. package/dist/web/assets/notification-store-Yvy1I8gd.js +1 -0
  96. package/dist/web/assets/{number-overlay-editor-C1EqJnWq.js → number-overlay-editor-DG9xJwEG.js} +1 -1
  97. package/dist/web/assets/{overview-panel-vTeeJYVZ.js → overview-panel-s53bnl0q.js} +1 -1
  98. package/dist/web/assets/packet-RMMSAZCW-Z5jWjFCV.js +1 -0
  99. package/dist/web/assets/{panel-store-BSnkjgHt.js → panel-store-B4rijFKN.js} +1 -1
  100. package/dist/web/assets/{pdf-preview-CqFGRY0Z.js → pdf-preview-DYt3aBzM.js} +1 -1
  101. package/dist/web/assets/{perl-C9H7X7CT.js → perl-BR78hS8R.js} +1 -1
  102. package/dist/web/assets/{php-Bx2U-cQ_.js → php-DXBOMC9z.js} +1 -1
  103. package/dist/web/assets/pie-UPGHQEXC-Bq7UMi8P.js +1 -0
  104. package/dist/web/assets/{postgres-viewer-DHwcQucn.js → postgres-viewer-BvoWSuoZ.js} +3 -3
  105. package/dist/web/assets/{process-table-BGRJNlpn.js → process-table-BYwKigiD.js} +1 -1
  106. package/dist/web/assets/{project-store--nvQiy75.js → project-store-BuY7rxn_.js} +1 -1
  107. package/dist/web/assets/{pug-Cw74sKsE.js → pug-CvTwVVKV.js} +1 -1
  108. package/dist/web/assets/{qml-BXD94Eqz.js → qml-CPSoZ1-B.js} +1 -1
  109. package/dist/web/assets/radar-KQ55EAFF-BLo4BOTD.js +1 -0
  110. package/dist/web/assets/{razor-DZ0-Ge0S.js → razor-BUA1YKtF.js} +1 -1
  111. package/dist/web/assets/{rst-Dtdh0QUj.js → rst-BJsq7ISz.js} +1 -1
  112. package/dist/web/assets/{ruby-BC51BW2Q.js → ruby-DSDMeYf5.js} +1 -1
  113. package/dist/web/assets/{run-in-terminal-Sd3KyjN4.js → run-in-terminal-C9xpskFn.js} +1 -1
  114. package/dist/web/assets/{sas-BN8i4qOU.js → sas-B1nJOjbN.js} +1 -1
  115. package/dist/web/assets/{scroll-area-0gljZ6lF.js → scroll-area-DBcG1fQo.js} +1 -1
  116. package/dist/web/assets/{scss-H-QtA7Rz.js → scss-BZFyRiQn.js} +1 -1
  117. package/dist/web/assets/{send-to-chat-u6m8l-rH.js → send-to-chat-BIg70MUZ.js} +1 -1
  118. package/dist/web/assets/{settings-store-CHY6Kzin.js → settings-store-GV5pkkke.js} +2 -2
  119. package/dist/web/assets/{settings-tab-IY6K1MiZ.js → settings-tab-BePvIcjd.js} +1 -1
  120. package/dist/web/assets/settings-tab-Br-lJSPa.js +1 -0
  121. package/dist/web/assets/{shaderlab-CalNsj_x.js → shaderlab-laINRXBo.js} +1 -1
  122. package/dist/web/assets/{shellsession-BFrcoR8x.js → shellsession-BpOOvWVR.js} +1 -1
  123. package/dist/web/assets/{soy-CNbku7vP.js → soy-BW4d91Sa.js} +1 -1
  124. package/dist/web/assets/{sparql-BN_orcVp.js → sparql-BsvvyoHn.js} +1 -1
  125. package/dist/web/assets/{sql-query-editor-C6pC3I8l.js → sql-query-editor-qQce5QJ6.js} +1 -1
  126. package/dist/web/assets/{sqlite-viewer-DTr5PzUb.js → sqlite-viewer-yhc0YNvu.js} +1 -1
  127. package/dist/web/assets/{stata-C6a5Ijzq.js → stata-K-dZInUt.js} +1 -1
  128. package/dist/web/assets/{surrealql-ClplI_HY.js → surrealql-CpNp2sgf.js} +1 -1
  129. package/dist/web/assets/{svelte-DNm-UNXB.js → svelte-BG6iBA8R.js} +1 -1
  130. package/dist/web/assets/{switch-DKvKya9y.js → switch-_NcrktLe.js} +1 -1
  131. package/dist/web/assets/system-monitor-body-D8Z5QXBB.js +2 -0
  132. package/dist/web/assets/system-monitor-tab-BclYXFTe.js +1 -0
  133. package/dist/web/assets/{system-monitor-window-content-BUqRsPK2.js → system-monitor-window-content-CaOhD0AU.js} +1 -1
  134. package/dist/web/assets/{tab-host-window-content-BnqGPu8a.js → tab-host-window-content-f_Rc5rJK.js} +1 -1
  135. package/dist/web/assets/{tab-store-CJuJRapl.js → tab-store-tFnyPmm4.js} +1 -1
  136. package/dist/web/assets/team-member-sheet-QDHoYCs0.js +2 -0
  137. package/dist/web/assets/{team-member-window-content-T4Q5JI-G.js → team-member-window-content-B1jkKkDi.js} +1 -1
  138. package/dist/web/assets/{templ-CSdqVobu.js → templ-gxNKXmt3.js} +1 -1
  139. package/dist/web/assets/{terminal-tab-Bb2rZegQ.js → terminal-tab-UvwM0sty.js} +3 -3
  140. package/dist/web/assets/{tex-DzSJ4kcZ.js → tex-C0ON27g5.js} +1 -1
  141. package/dist/web/assets/{tool-cards-lhqhrDTT.js → tool-cards-jB7GT-wj.js} +4 -4
  142. package/dist/web/assets/treemap-KZPCXAKY-dyrE6SW2.js +1 -0
  143. package/dist/web/assets/{ts-tags-C28w3bTg.js → ts-tags-BX_CTVgQ.js} +1 -1
  144. package/dist/web/assets/{twig-i5jY3QXB.js → twig-CwL7XnU0.js} +1 -1
  145. package/dist/web/assets/{use-blob-url-Dsq9OjTh.js → use-blob-url-DcRcjNTE.js} +1 -1
  146. package/dist/web/assets/use-coarse-long-press-BmI_GVW5.js +1 -0
  147. package/dist/web/assets/{use-monaco-theme-BZvlsoBv.js → use-monaco-theme--9tOOkDA.js} +1 -1
  148. package/dist/web/assets/{use-open-team-member-DkiEjFYE.js → use-open-team-member-1hRsHLKg.js} +1 -1
  149. package/dist/web/assets/{use-websocket-B8R50f7f.js → use-websocket-rxJLbFqy.js} +1 -1
  150. package/dist/web/assets/{vendor-mermaid-By4AVvJY.js → vendor-mermaid-CHTa6D69.js} +1 -1
  151. package/dist/web/assets/video-preview-DMhnf2Gi.js +1 -0
  152. package/dist/web/assets/{vue-Ceyet_lv.js → vue-Bykm6wSS.js} +1 -1
  153. package/dist/web/assets/{vue-html-GuU7g_hg.js → vue-html-CETdK_iV.js} +1 -1
  154. package/dist/web/assets/{vue-vine-CxXoaJr2.js → vue-vine-DiJb6fO5.js} +1 -1
  155. package/dist/web/assets/{window-store-CsEW6jiE.js → window-store-CQ1lghvv.js} +1 -1
  156. package/dist/web/assets/{xml-CbqOVhkP.js → xml-BNw8J5YW.js} +1 -1
  157. package/dist/web/assets/{xsl-KfAmCauy.js → xsl-CsG9L31b.js} +1 -1
  158. package/dist/web/index.html +46 -45
  159. package/dist/web/sw.js +1 -1
  160. package/docs/architecture/ai-chat-and-providers.md +309 -0
  161. package/docs/architecture/data-and-storage.md +356 -0
  162. package/docs/architecture/extensions.md +496 -0
  163. package/docs/architecture/integrations.md +214 -0
  164. package/docs/architecture/workspace-and-ui.md +391 -0
  165. package/docs/claude-agent-sdk-reference.md +1 -1
  166. package/docs/code-patterns.md +361 -0
  167. package/docs/code-standards.md +8 -358
  168. package/docs/codebase-subsystems.md +364 -0
  169. package/docs/codebase-summary.md +34 -379
  170. package/docs/deployment-guide.md +280 -490
  171. package/docs/design-guidelines.md +1 -1
  172. package/docs/lessons-learned.md +27 -10
  173. package/docs/project-changelog.md +18 -806
  174. package/docs/project-overview-pdr.md +117 -111
  175. package/docs/project-roadmap.md +89 -133
  176. package/docs/system-architecture.md +77 -1826
  177. package/package.json +2 -2
  178. package/src/cli/commands/autostart.ts +2 -2
  179. package/src/cli/commands/config-cmd.ts +12 -3
  180. package/src/cli/commands/status.ts +3 -2
  181. package/src/cli/commands/stop.ts +32 -4
  182. package/src/server/index.ts +17 -3
  183. package/src/server/routes/chat-unread.ts +36 -0
  184. package/src/server/routes/named-tunnel.ts +132 -0
  185. package/src/server/routes/tunnel.ts +13 -0
  186. package/src/server/ws/chat.ts +5 -2
  187. package/src/services/autostart-register.ts +47 -29
  188. package/src/services/cloud.service.ts +26 -1
  189. package/src/services/config-secret-keys.ts +48 -0
  190. package/src/services/config.service.ts +1 -1
  191. package/src/services/db.service.ts +62 -12
  192. package/src/services/extension-rpc-handlers.ts +9 -1
  193. package/src/services/file-watcher/watch-tree.ts +67 -33
  194. package/src/services/file-watcher.service.ts +28 -10
  195. package/src/services/fs-credential-path-guard.ts +103 -0
  196. package/src/services/fs-path-guard.service.ts +14 -66
  197. package/src/services/named-tunnel/cloudflare-dns-api.ts +40 -0
  198. package/src/services/named-tunnel/cloudflare-zone-api.ts +28 -0
  199. package/src/services/named-tunnel/cloudflared-cert.ts +95 -0
  200. package/src/services/named-tunnel/cloudflared-exec.ts +50 -0
  201. package/src/services/named-tunnel/cloudflared-login-helpers.ts +98 -0
  202. package/src/services/named-tunnel/cloudflared-login.service.ts +182 -0
  203. package/src/services/named-tunnel/hostname-rules.ts +58 -0
  204. package/src/services/named-tunnel/login-output-parser.ts +36 -0
  205. package/src/services/named-tunnel/named-tunnel-args.ts +110 -0
  206. package/src/services/named-tunnel/named-tunnel-config.ts +67 -0
  207. package/src/services/named-tunnel/named-tunnel-probe-state.ts +95 -0
  208. package/src/services/named-tunnel/named-tunnel-readiness.ts +59 -0
  209. package/src/services/named-tunnel/named-tunnel-runtime.ts +42 -0
  210. package/src/services/named-tunnel/named-tunnel-setup-confirm.ts +98 -0
  211. package/src/services/named-tunnel/named-tunnel-setup.service.ts +191 -0
  212. package/src/services/supervisor-state.ts +67 -2
  213. package/src/services/supervisor.ts +454 -157
  214. package/src/services/tunnel-registry-parse.ts +27 -0
  215. package/src/services/tunnel-registry.service.ts +2 -15
  216. package/src/services/tunnel.service.ts +12 -3
  217. package/src/types/config.ts +31 -0
  218. package/src/web/app.tsx +5 -0
  219. package/src/web/components/chat/chat-history-bar.tsx +95 -98
  220. package/src/web/components/chat/chat-toolbar-overflow-menu.tsx +57 -0
  221. package/src/web/components/chat/session-debug-dialog.tsx +35 -27
  222. package/src/web/components/layout/cloud-share-named-tunnel-row.tsx +111 -0
  223. package/src/web/components/layout/cloud-share-popover.tsx +56 -14
  224. package/src/web/components/shared/connection-lost-overlay.tsx +44 -5
  225. package/src/web/components/tunnels/named-tunnel/hostname-validation.ts +14 -0
  226. package/src/web/components/tunnels/named-tunnel/named-tunnel-copy.ts +96 -0
  227. package/src/web/components/tunnels/named-tunnel/named-tunnel-hostname-field.tsx +51 -0
  228. package/src/web/components/tunnels/named-tunnel/named-tunnel-login-step.tsx +105 -0
  229. package/src/web/components/tunnels/named-tunnel/named-tunnel-section.tsx +159 -0
  230. package/src/web/components/tunnels/named-tunnel/named-tunnel-setup-content.tsx +197 -0
  231. package/src/web/components/tunnels/named-tunnel/named-tunnel-setup-popup.tsx +57 -0
  232. package/src/web/components/tunnels/named-tunnel/named-tunnel-step-reducer.ts +191 -0
  233. package/src/web/components/tunnels/named-tunnel/use-named-tunnel-setup.ts +212 -0
  234. package/src/web/components/tunnels/tunnel-manager-tab.tsx +4 -0
  235. package/src/web/hooks/use-global-events.ts +3 -1
  236. package/src/web/lib/api-named-tunnel.ts +54 -0
  237. package/src/web/lib/last-known-local-url.ts +23 -0
  238. package/src/web/stores/notification-store.ts +20 -1
  239. package/dist/web/assets/api-client-DTZEgjBQ.js +0 -1
  240. package/dist/web/assets/api-settings-kd1j4iR8.js +0 -1
  241. package/dist/web/assets/architecture-PBZL5I3N-8dCMT9r7.js +0 -1
  242. package/dist/web/assets/chat-tab-E3bf_V8X.js +0 -15
  243. package/dist/web/assets/code-editor-BUlM2gXk.js +0 -10
  244. package/dist/web/assets/explorer-body-9aBmyVpZ.js +0 -2
  245. package/dist/web/assets/explorer-window-content-DRtqoSPA.js +0 -1
  246. package/dist/web/assets/gitGraph-HDMCJU4V-2e_8dfbs.js +0 -1
  247. package/dist/web/assets/glide-data-grid-C4_zIneN.js +0 -138
  248. package/dist/web/assets/index-BImUaigO.js +0 -52
  249. package/dist/web/assets/index-CSfi6DSU.css +0 -2
  250. package/dist/web/assets/info-3K5VOQVL-BZi1Gha1.js +0 -1
  251. package/dist/web/assets/keybindings-store-CzV2sPls.js +0 -1
  252. package/dist/web/assets/markdown-renderer-CcbR33VG.js +0 -1
  253. package/dist/web/assets/mobile-explorer-sheet-SIwQXlwA.js +0 -1
  254. package/dist/web/assets/notification-store-DV5QtUTg.js +0 -1
  255. package/dist/web/assets/packet-RMMSAZCW-o5htEcin.js +0 -1
  256. package/dist/web/assets/pie-UPGHQEXC-BXAIFSAD.js +0 -1
  257. package/dist/web/assets/radar-KQ55EAFF-Dbt2Aebl.js +0 -1
  258. package/dist/web/assets/settings-tab-Dic6TKkU.js +0 -1
  259. package/dist/web/assets/system-monitor-body-iwGiCns9.js +0 -2
  260. package/dist/web/assets/system-monitor-tab-DdXQhEel.js +0 -1
  261. package/dist/web/assets/team-member-sheet-LkAFSCvb.js +0 -2
  262. package/dist/web/assets/treemap-KZPCXAKY-DUbnl9dz.js +0 -1
  263. package/dist/web/assets/video-preview-m0-3p7QZ.js +0 -1
  264. /package/dist/web/assets/{abap-C6nB04tn.js → abap-DGlrX_8G.js} +0 -0
  265. /package/dist/web/assets/{actionscript-3-Vx4rw2sC.js → actionscript-3-CUqRtUJE.js} +0 -0
  266. /package/dist/web/assets/{ada-Ccd8vM20.js → ada-CY3Spa33.js} +0 -0
  267. /package/dist/web/assets/{andromeeda-DnBX5xMg.js → andromeeda-Lxs2TJn7.js} +0 -0
  268. /package/dist/web/assets/{apache-B-9_sx0g.js → apache-DfxgGdgM.js} +0 -0
  269. /package/dist/web/assets/{apex-_wPVBYoL.js → apex-QVDF1dnI.js} +0 -0
  270. /package/dist/web/assets/{api-client-aQb5hHE5.js → api-client-oY1tC3tM.js} +0 -0
  271. /package/dist/web/assets/{applescript-CcvD-qr0.js → applescript-pwJ8QXh-.js} +0 -0
  272. /package/dist/web/assets/{apply-theme-BeLRfsSu.js → apply-theme-B_bMVL6a.js} +0 -0
  273. /package/dist/web/assets/{ara-C40Ztoms.js → ara-mPjeX5YX.js} +0 -0
  274. /package/dist/web/assets/{asciidoc-CybYkJV4.js → asciidoc-Bf7LsDkI.js} +0 -0
  275. /package/dist/web/assets/{asm-9eOKwzR0.js → asm-rRKC1OUx.js} +0 -0
  276. /package/dist/web/assets/{aurora-x-B5ZH4t7t.js → aurora-x-DwQr0DkG.js} +0 -0
  277. /package/dist/web/assets/{awk-Dqilob-2.js → awk-Bpt768fN.js} +0 -0
  278. /package/dist/web/assets/{ayu-dark-CPgRhf5r.js → ayu-dark-D9lwQHMs.js} +0 -0
  279. /package/dist/web/assets/{ayu-light-Do06Q52f.js → ayu-light-B4-ptrU5.js} +0 -0
  280. /package/dist/web/assets/{ayu-mirage-_BhxcrxY.js → ayu-mirage-DAyRzH5Q.js} +0 -0
  281. /package/dist/web/assets/{ballerina-DDwK54QW.js → ballerina-Cgdpj1HC.js} +0 -0
  282. /package/dist/web/assets/{bat-BGyGlzGk.js → bat-4FrN36S2.js} +0 -0
  283. /package/dist/web/assets/{beancount-CRWBdapI.js → beancount-DkcxQER3.js} +0 -0
  284. /package/dist/web/assets/{berry-DbSFn2gO.js → berry-Dnuut-Ml.js} +0 -0
  285. /package/dist/web/assets/{bibtex-QpsV9gm9.js → bibtex-CCDwmbuA.js} +0 -0
  286. /package/dist/web/assets/{bicep-CxS8CUve.js → bicep-BC0ryoGM.js} +0 -0
  287. /package/dist/web/assets/{bird2-CuZhQzYR.js → bird2-C50V_A-0.js} +0 -0
  288. /package/dist/web/assets/{c-9bnDqE3g.js → c-Ddorhq0U.js} +0 -0
  289. /package/dist/web/assets/{c3-JMZTrOhA.js → c3-DdIf5r9E.js} +0 -0
  290. /package/dist/web/assets/{cadence-DsDNayJS.js → cadence-s9pd1Wng.js} +0 -0
  291. /package/dist/web/assets/{can-open-in-ppm-BJfpynya.js → can-open-in-ppm-C6hZV1OP.js} +0 -0
  292. /package/dist/web/assets/{catppuccin-frappe-DYIKXXBe.js → catppuccin-frappe-CzJNTyu-.js} +0 -0
  293. /package/dist/web/assets/{catppuccin-latte-IYYOnUJ8.js → catppuccin-latte-BNpt-m7s.js} +0 -0
  294. /package/dist/web/assets/{catppuccin-macchiato-BrJ_7mfS.js → catppuccin-macchiato-FYGkfsJ5.js} +0 -0
  295. /package/dist/web/assets/{catppuccin-mocha-gzSu7v7-.js → catppuccin-mocha-it2g5D1q.js} +0 -0
  296. /package/dist/web/assets/{clarity-DLzsC0Hb.js → clarity-BVFBTjMe.js} +0 -0
  297. /package/dist/web/assets/{clipboard-7fw0Shic.js → clipboard-BWznymz7.js} +0 -0
  298. /package/dist/web/assets/{clojure-DS1BvoTt.js → clojure-BSYvXMI8.js} +0 -0
  299. /package/dist/web/assets/{cmake-CDvsJwYp.js → cmake-DGEqH-Np.js} +0 -0
  300. /package/dist/web/assets/{codeowners-BjsYiflJ.js → codeowners-D8FCOxWg.js} +0 -0
  301. /package/dist/web/assets/{codeql-CG-7ARat.js → codeql-COV0eluG.js} +0 -0
  302. /package/dist/web/assets/{common-lisp-DSO1Lbh9.js → common-lisp-xHnZUlgt.js} +0 -0
  303. /package/dist/web/assets/{coq-DYdh3oP_.js → coq-Cs_2i3Xg.js} +0 -0
  304. /package/dist/web/assets/{csharp-DxEBz5s0.js → csharp-B1ONB4PT.js} +0 -0
  305. /package/dist/web/assets/{css-dal3Q-8C.js → css-BgGQbvrZ.js} +0 -0
  306. /package/dist/web/assets/{csv-CpqjMGiA.js → csv-CNHmQBzs.js} +0 -0
  307. /package/dist/web/assets/{csv-parser-3umBA4NY.js → csv-parser-DWR24J17.js} +0 -0
  308. /package/dist/web/assets/{cue-DinoJ_5k.js → cue-ZXAKzwIy.js} +0 -0
  309. /package/dist/web/assets/{cypher-DzxqmkUW.js → cypher-4PL5bmT5.js} +0 -0
  310. /package/dist/web/assets/{d-BxNwJuEC.js → d-CEsV3RT2.js} +0 -0
  311. /package/dist/web/assets/{dark-plus-Clmv3V-0.js → dark-plus-NtFnYSDz.js} +0 -0
  312. /package/dist/web/assets/{dart-BWVtXD35.js → dart-CKHn4mDf.js} +0 -0
  313. /package/dist/web/assets/{data-grid-types-B4n51Fck.js → data-grid-types-CxU8oo-j.js} +0 -0
  314. /package/dist/web/assets/{dax-DsImuWgJ.js → dax-Cmkj1I_M.js} +0 -0
  315. /package/dist/web/assets/{desktop-B0jJuG2r.js → desktop-DJLalvPQ.js} +0 -0
  316. /package/dist/web/assets/{diff-CWqAAxoE.js → diff-BnS2wpj6.js} +0 -0
  317. /package/dist/web/assets/{dist-C0eNNfbt.js → dist-CuIdRyUB.js} +0 -0
  318. /package/dist/web/assets/{dist-B-EXdSYE.js → dist-uYjs3Wv6.js} +0 -0
  319. /package/dist/web/assets/{docker-CYFCJdIL.js → docker-CovNy7ml.js} +0 -0
  320. /package/dist/web/assets/{dotenv-CKDUjURg.js → dotenv-Gh4w3I6B.js} +0 -0
  321. /package/dist/web/assets/{dracula-CSoPspHE.js → dracula-NF0tAHXl.js} +0 -0
  322. /package/dist/web/assets/{dracula-soft-BuFjw5L1.js → dracula-soft-Cj_qBurK.js} +0 -0
  323. /package/dist/web/assets/{dream-maker-B6PdDRZu.js → dream-maker-CRuZPcl7.js} +0 -0
  324. /package/dist/web/assets/{emacs-lisp-CMcXvD0G.js → emacs-lisp-BACmC9qj.js} +0 -0
  325. /package/dist/web/assets/{everforest-dark-cU8-wYzi.js → everforest-dark-Dd9av4QG.js} +0 -0
  326. /package/dist/web/assets/{everforest-light-KhorllqK.js → everforest-light-C-jsss-B.js} +0 -0
  327. /package/dist/web/assets/{external-link-B8BynhvY.js → external-link-B6Hl0U4X.js} +0 -0
  328. /package/dist/web/assets/{fennel-BCELZK87.js → fennel-BJA9XnL8.js} +0 -0
  329. /package/dist/web/assets/{file-exclamation-point-CZ4DiR75.js → file-exclamation-point-JzhTU2TX.js} +0 -0
  330. /package/dist/web/assets/{fish-BQc0y_jy.js → fish-CKlhNSc2.js} +0 -0
  331. /package/dist/web/assets/{fluent-BTINnwF6.js → fluent-wfglRk5D.js} +0 -0
  332. /package/dist/web/assets/{folder-symlink-B8XrtvQK.js → folder-symlink-Dx1hOk8X.js} +0 -0
  333. /package/dist/web/assets/{format-bytes-WVcFxJuT.js → format-bytes-utwHMTry.js} +0 -0
  334. /package/dist/web/assets/{fortran-free-form-BQim9wDz.js → fortran-free-form-BlXCdid3.js} +0 -0
  335. /package/dist/web/assets/{gdscript-03w5u8un.js → gdscript-BwAVG6Et.js} +0 -0
  336. /package/dist/web/assets/{gdshader-Dp2daNzU.js → gdshader-DPc_T7ib.js} +0 -0
  337. /package/dist/web/assets/{genie-CoNcjPiD.js → genie-B-zZSLCX.js} +0 -0
  338. /package/dist/web/assets/{gherkin-NrPOSPQL.js → gherkin-CJ0ajpGs.js} +0 -0
  339. /package/dist/web/assets/{github-dark-z4RE9ZqA.js → github-dark-Dz8ZkzSP.js} +0 -0
  340. /package/dist/web/assets/{github-dark-default-DOKw-1cI.js → github-dark-default-CNUYo_kp.js} +0 -0
  341. /package/dist/web/assets/{github-dark-dimmed-DJRNMkEA.js → github-dark-dimmed-241z_Ycx.js} +0 -0
  342. /package/dist/web/assets/{github-dark-high-contrast-Bj7jJ_Gs.js → github-dark-high-contrast-7r6yZZYH.js} +0 -0
  343. /package/dist/web/assets/{github-light-B4k_yMFu.js → github-light-C8gmZn-D.js} +0 -0
  344. /package/dist/web/assets/{github-light-default-BHc-Uzhs.js → github-light-default-GAEgq1eA.js} +0 -0
  345. /package/dist/web/assets/{github-light-high-contrast-CHisIywL.js → github-light-high-contrast-3z91352E.js} +0 -0
  346. /package/dist/web/assets/{gleam-CfbypZ4q.js → gleam-C7vxW5zd.js} +0 -0
  347. /package/dist/web/assets/{gn-YoAjyhJa.js → gn-CyJIXwHP.js} +0 -0
  348. /package/dist/web/assets/{gnuplot-xVkc1f1c.js → gnuplot-CqLbGgPd.js} +0 -0
  349. /package/dist/web/assets/{go-GPCwP3Gu.js → go-DdDcK9aX.js} +0 -0
  350. /package/dist/web/assets/{groovy-Crh4Yww9.js → groovy-UYHggEqH.js} +0 -0
  351. /package/dist/web/assets/{gruvbox-dark-hard-BluUFuDS.js → gruvbox-dark-hard-B4ierkJn.js} +0 -0
  352. /package/dist/web/assets/{gruvbox-dark-medium-Cxk1FWQp.js → gruvbox-dark-medium-DYwkDSZ6.js} +0 -0
  353. /package/dist/web/assets/{gruvbox-dark-soft-CHXMiFmj.js → gruvbox-dark-soft-CVL4P-Lz.js} +0 -0
  354. /package/dist/web/assets/{gruvbox-light-hard-B6eKPjAD.js → gruvbox-light-hard-BJ0LewUZ.js} +0 -0
  355. /package/dist/web/assets/{gruvbox-light-medium-pDwMm1ut.js → gruvbox-light-medium-CaqqjeGJ.js} +0 -0
  356. /package/dist/web/assets/{gruvbox-light-soft-Cxu6kHqK.js → gruvbox-light-soft-ByKMoVNr.js} +0 -0
  357. /package/dist/web/assets/{haskell-C6JKHjbK.js → haskell-BilYRVPp.js} +0 -0
  358. /package/dist/web/assets/{haxe-BuBNdF0a.js → haxe-Brs9Bj1s.js} +0 -0
  359. /package/dist/web/assets/{hcl-DZpDdGj2.js → hcl-ClrZpol-.js} +0 -0
  360. /package/dist/web/assets/{hjson-CBkhYRuo.js → hjson-9PvHnYMX.js} +0 -0
  361. /package/dist/web/assets/{hlsl-C5nMxnp9.js → hlsl-BbBk4vqV.js} +0 -0
  362. /package/dist/web/assets/{horizon-VAFvA6R6.js → horizon-E5Py0cny.js} +0 -0
  363. /package/dist/web/assets/{horizon-bright-BC0xn7T3.js → horizon-bright-DVCsNGEk.js} +0 -0
  364. /package/dist/web/assets/{houston-BxrUivmz.js → houston-JdFEax9Y.js} +0 -0
  365. /package/dist/web/assets/{hy-DLtOXoaH.js → hy-W_W3tLKT.js} +0 -0
  366. /package/dist/web/assets/{image-gallery-Gnf7TdCR.js → image-gallery-5IURd6db.js} +0 -0
  367. /package/dist/web/assets/{imba-BdpFXM2W.js → imba-CfAqJz7T.js} +0 -0
  368. /package/dist/web/assets/{ini-BInJO_T0.js → ini-C_3gvKWs.js} +0 -0
  369. /package/dist/web/assets/{java-Dxuddnkb.js → java-CKqtXZ-j.js} +0 -0
  370. /package/dist/web/assets/{javascript-CVamJfLY.js → javascript-C7gSEfae.js} +0 -0
  371. /package/dist/web/assets/{json-BLZ0ozYk.js → json-BoN6e11T.js} +0 -0
  372. /package/dist/web/assets/{json5-BnXzmMKs.js → json5-CgE7C8HW.js} +0 -0
  373. /package/dist/web/assets/{jsonc-CSgkSga7.js → jsonc-DjyoattY.js} +0 -0
  374. /package/dist/web/assets/{jsonl-CY5W2nF2.js → jsonl-lI82wGSw.js} +0 -0
  375. /package/dist/web/assets/{jsonnet-CEJYH3sF.js → jsonnet-CszEI8va.js} +0 -0
  376. /package/dist/web/assets/{jssm-CsJc7Uhk.js → jssm-CZXDlszb.js} +0 -0
  377. /package/dist/web/assets/{jsx-jCiRaHU7.js → jsx-BeRz0eEz.js} +0 -0
  378. /package/dist/web/assets/{kanagawa-dragon-DfoA_GxX.js → kanagawa-dragon-BOmnUiyd.js} +0 -0
  379. /package/dist/web/assets/{kanagawa-lotus-BOLYJpWd.js → kanagawa-lotus-ffU_P5w-.js} +0 -0
  380. /package/dist/web/assets/{kanagawa-wave-DfKlbT1G.js → kanagawa-wave-DKTJZrMu.js} +0 -0
  381. /package/dist/web/assets/{kdl-CmssIFTi.js → kdl-BWECJveH.js} +0 -0
  382. /package/dist/web/assets/{kotlin-BRsNgp6x.js → kotlin-DpVwBCSt.js} +0 -0
  383. /package/dist/web/assets/{kusto-T8X9jUab.js → kusto-BgVqjUZA.js} +0 -0
  384. /package/dist/web/assets/{laserwave-DkJMVJ8Z.js → laserwave-CEVCi8em.js} +0 -0
  385. /package/dist/web/assets/{lean-C65KGVPq.js → lean-CGyfRhLJ.js} +0 -0
  386. /package/dist/web/assets/{less-exIPdDlf.js → less-B0_H10Ku.js} +0 -0
  387. /package/dist/web/assets/{lib-mC1NZfuw.js → lib-hrC3bruJ.js} +0 -0
  388. /package/dist/web/assets/{libesm-DSutUtl1.js → libesm-DVpFmwJu.js} +0 -0
  389. /package/dist/web/assets/{light-plus-DiHUXAZt.js → light-plus-DqzMqPID.js} +0 -0
  390. /package/dist/web/assets/{llvm-CJBnEzFM.js → llvm-9ucnfNiF.js} +0 -0
  391. /package/dist/web/assets/{loader-circle-gSPVxfvH.js → loader-circle-Dpqcv0eS.js} +0 -0
  392. /package/dist/web/assets/{lock-Did-87UV.js → lock-DldbT31x.js} +0 -0
  393. /package/dist/web/assets/{log-DxMyUMuP.js → log-m6A2dOSj.js} +0 -0
  394. /package/dist/web/assets/{logo-CC9NKzca.js → logo-ByPcsC9P.js} +0 -0
  395. /package/dist/web/assets/{luau-Bw10I6zf.js → luau-DFCo8nBn.js} +0 -0
  396. /package/dist/web/assets/{make-C_6M90m6.js → make-C90w3MeA.js} +0 -0
  397. /package/dist/web/assets/{markdown-bR9_R-LF.js → markdown-DT0uywZG.js} +0 -0
  398. /package/dist/web/assets/{material-theme-BUx2Qujn.js → material-theme-BVUO93tJ.js} +0 -0
  399. /package/dist/web/assets/{material-theme-darker-CRJrPgKm.js → material-theme-darker-xXgGDwnF.js} +0 -0
  400. /package/dist/web/assets/{material-theme-lighter-CUAYppPa.js → material-theme-lighter-BjStPzWB.js} +0 -0
  401. /package/dist/web/assets/{material-theme-ocean-DuINF1fg.js → material-theme-ocean-wFmSiKjy.js} +0 -0
  402. /package/dist/web/assets/{material-theme-palenight-W6_icGCj.js → material-theme-palenight-e-0CtRoe.js} +0 -0
  403. /package/dist/web/assets/{matlab-rfqn_b3z.js → matlab-BNaNQAUH.js} +0 -0
  404. /package/dist/web/assets/{mdx-Bh1B_YOJ.js → mdx-DfpVWAqO.js} +0 -0
  405. /package/dist/web/assets/{mermaid-Dxf6Ya2k.js → mermaid-Bpq7wFCP.js} +0 -0
  406. /package/dist/web/assets/{min-dark-CivZ2L0h.js → min-dark-CL2Y0WH6.js} +0 -0
  407. /package/dist/web/assets/{min-light-B1Ykqtma.js → min-light-DOgr_IdP.js} +0 -0
  408. /package/dist/web/assets/{mipsasm-DynHaM0d.js → mipsasm-C9ie2zlB.js} +0 -0
  409. /package/dist/web/assets/{mojo-Clw5PbGH.js → mojo-Md2A2sJy.js} +0 -0
  410. /package/dist/web/assets/{monitor-Dv8vn_6O.js → monitor-4vWfqzuN.js} +0 -0
  411. /package/dist/web/assets/{monokai-DFIooFZT.js → monokai-nbXIEySB.js} +0 -0
  412. /package/dist/web/assets/{moonbit-DnBSZWGM.js → moonbit-CJ-T-s4A.js} +0 -0
  413. /package/dist/web/assets/{move-CCcL2PfL.js → move-DwEehLIK.js} +0 -0
  414. /package/dist/web/assets/{narrat-BDarg5Lk.js → narrat-CuF4sIKr.js} +0 -0
  415. /package/dist/web/assets/{nextflow-groovy-DrZOeCNW.js → nextflow-groovy-1dYhfPfq.js} +0 -0
  416. /package/dist/web/assets/{night-owl-BnZZCK7v.js → night-owl-gZS5mfzY.js} +0 -0
  417. /package/dist/web/assets/{night-owl-light-CEwBsVHS.js → night-owl-light-DDAster5.js} +0 -0
  418. /package/dist/web/assets/{nix-amCzOPMR.js → nix-BlbEYs2c.js} +0 -0
  419. /package/dist/web/assets/{nord-BIs2BC6q.js → nord-BRiPb8Vy.js} +0 -0
  420. /package/dist/web/assets/{nushell-Y02fo4yw.js → nushell-DgWicB1i.js} +0 -0
  421. /package/dist/web/assets/{objective-c-7xOZtWW1.js → objective-c-D8mPZ42Y.js} +0 -0
  422. /package/dist/web/assets/{objective-cpp-eVtjUvVB.js → objective-cpp-B0pNXzuj.js} +0 -0
  423. /package/dist/web/assets/{ocaml-Bi6ZwqG4.js → ocaml-lQd7aK5d.js} +0 -0
  424. /package/dist/web/assets/{odin-8xf1Y0S9.js → odin-CxtIOBjm.js} +0 -0
  425. /package/dist/web/assets/{one-dark-pro-hVv-_y_b.js → one-dark-pro-T_UFnWfe.js} +0 -0
  426. /package/dist/web/assets/{one-light-CJJ3MtHt.js → one-light-LBVqwyV-.js} +0 -0
  427. /package/dist/web/assets/{openscad-B2HcywLk.js → openscad-C5dnS6J7.js} +0 -0
  428. /package/dist/web/assets/{pascal-C7fllV_7.js → pascal-CmWCWQML.js} +0 -0
  429. /package/dist/web/assets/{pkl-qvLC1IWP.js → pkl-BWWOtDQk.js} +0 -0
  430. /package/dist/web/assets/{plastic-BT1wHZv2.js → plastic-ekhupckF.js} +0 -0
  431. /package/dist/web/assets/{play-4MZi1Axp.js → play-CpCAoQRZ.js} +0 -0
  432. /package/dist/web/assets/{plsql-1MsmdmJD.js → plsql-BPsDHXx4.js} +0 -0
  433. /package/dist/web/assets/{plus-BhMZqFsN.js → plus-BR0qewJI.js} +0 -0
  434. /package/dist/web/assets/{po-BigskytD.js → po-BX2FiuW2.js} +0 -0
  435. /package/dist/web/assets/{poimandres-DuklniF7.js → poimandres-DzXOdZcN.js} +0 -0
  436. /package/dist/web/assets/{polar-e2pjN_sp.js → polar-DDRXgJzG.js} +0 -0
  437. /package/dist/web/assets/{portal-container-context-Cm5HipFr.js → portal-container-context-a399Hd9F.js} +0 -0
  438. /package/dist/web/assets/{postcss-DtAeYjWw.js → postcss-BHW6XBhx.js} +0 -0
  439. /package/dist/web/assets/{powerquery-Cr2PtehI.js → powerquery-B89c2NBb.js} +0 -0
  440. /package/dist/web/assets/{powershell-BxFTGLJ4.js → powershell-Dvxrd3Tu.js} +0 -0
  441. /package/dist/web/assets/{prisma-COWJiwdZ.js → prisma-lMK8EFLU.js} +0 -0
  442. /package/dist/web/assets/{prolog-DFvGKRx6.js → prolog-D0YrtMG4.js} +0 -0
  443. /package/dist/web/assets/{proto-CcK80lFt.js → proto-J0mM25aw.js} +0 -0
  444. /package/dist/web/assets/{puppet-BF60bZ4F.js → puppet-CUT0xkhh.js} +0 -0
  445. /package/dist/web/assets/{purescript-zL_69MzU.js → purescript-CoJWSGRp.js} +0 -0
  446. /package/dist/web/assets/{python-CIwKhfb_.js → python-BBrmuz6P.js} +0 -0
  447. /package/dist/web/assets/{qmldir-CXNe04Lo.js → qmldir-CUtzs4Q7.js} +0 -0
  448. /package/dist/web/assets/{qss-C8lAWe6G.js → qss-Dm1RZ8aL.js} +0 -0
  449. /package/dist/web/assets/{r-CL5ZVL3_.js → r-BGP7qZTe.js} +0 -0
  450. /package/dist/web/assets/{racket-BBfh0uh0.js → racket-K_6U1lqJ.js} +0 -0
  451. /package/dist/web/assets/{raku-o6E8R5Q3.js → raku-bNSiZeW_.js} +0 -0
  452. /package/dist/web/assets/{react-CBTnT4sY.js → react-X4jUntEf.js} +0 -0
  453. /package/dist/web/assets/{red-GomRF2Xp.js → red-uVeIEF0f.js} +0 -0
  454. /package/dist/web/assets/{refresh-cw-B06yaBrW.js → refresh-cw-B9iv7csb.js} +0 -0
  455. /package/dist/web/assets/{reg-_ffDIvx9.js → reg-DSm9GWjh.js} +0 -0
  456. /package/dist/web/assets/{regexp-Bl5sF65W.js → regexp-CljRm_j_.js} +0 -0
  457. /package/dist/web/assets/{rel-DgMdqDuj.js → rel-d3JcopZg.js} +0 -0
  458. /package/dist/web/assets/{resolve-theme-CRt_7wpd.js → resolve-theme-DLKmm-lE.js} +0 -0
  459. /package/dist/web/assets/{riscv-8b3-Ye4X.js → riscv-CJmaELjT.js} +0 -0
  460. /package/dist/web/assets/{ron-DrmU1KHP.js → ron-D0g9B1rW.js} +0 -0
  461. /package/dist/web/assets/{rose-pine-C_-E2tF1.js → rose-pine-DOrD2BSH.js} +0 -0
  462. /package/dist/web/assets/{rose-pine-dawn-DEfhNlFH.js → rose-pine-dawn-CzZQ-LCF.js} +0 -0
  463. /package/dist/web/assets/{rose-pine-moon-BdvX3gdn.js → rose-pine-moon-CdmsYp7C.js} +0 -0
  464. /package/dist/web/assets/{rosmsg-B6GhGpd5.js → rosmsg-CmDth6pO.js} +0 -0
  465. /package/dist/web/assets/{rotate-ccw-COXu_9oj.js → rotate-ccw-DnuPzJRE.js} +0 -0
  466. /package/dist/web/assets/{rust-DmC97M1-.js → rust-DGdP6jiR.js} +0 -0
  467. /package/dist/web/assets/{sass-B0tJOHLM.js → sass-Dkgt2FKZ.js} +0 -0
  468. /package/dist/web/assets/{save-CtIGOjmx.js → save-DxonD4y1.js} +0 -0
  469. /package/dist/web/assets/{scala-n8zaFOXv.js → scala-BQLTON6C.js} +0 -0
  470. /package/dist/web/assets/{scheme-BKD9ztBU.js → scheme-DKlxUGCi.js} +0 -0
  471. /package/dist/web/assets/{sdbl-D8gB5-rS.js → sdbl-C3iZJQeZ.js} +0 -0
  472. /package/dist/web/assets/{send-BlyNJAzy.js → send-B-t5Mz26.js} +0 -0
  473. /package/dist/web/assets/{shallow-DYo6goJ4.js → shallow-jwPzEunk.js} +0 -0
  474. /package/dist/web/assets/{shellscript-BfXlf_Ao.js → shellscript-BRu-ZQAx.js} +0 -0
  475. /package/dist/web/assets/{shield-check-B0iGUiOK.js → shield-check-CdTiK1j2.js} +0 -0
  476. /package/dist/web/assets/{shield-off-BNLWBhcq.js → shield-off-DuetkXKM.js} +0 -0
  477. /package/dist/web/assets/{slack-dark-CylEddse.js → slack-dark-qkm_u2zz.js} +0 -0
  478. /package/dist/web/assets/{slack-ochin-w8wg8vuC.js → slack-ochin-BP8_8e4m.js} +0 -0
  479. /package/dist/web/assets/{smalltalk-hiAXA64u.js → smalltalk-Bnz4gt07.js} +0 -0
  480. /package/dist/web/assets/{snazzy-light-BwlV3Xty.js → snazzy-light-fbPZg5io.js} +0 -0
  481. /package/dist/web/assets/{solarized-dark-vmNGdykz.js → solarized-dark-CM0vTRoi.js} +0 -0
  482. /package/dist/web/assets/{solarized-light-DGv_0JXq.js → solarized-light-C4_W9mvs.js} +0 -0
  483. /package/dist/web/assets/{solidity-Y7QSH0B1.js → solidity-Bz6v1PoD.js} +0 -0
  484. /package/dist/web/assets/{splunk-BtgpLjHi.js → splunk-DyNxu-0i.js} +0 -0
  485. /package/dist/web/assets/{sql-CRHYhOia.js → sql-DXRL1gw1.js} +0 -0
  486. /package/dist/web/assets/{ssh-config-DOqSs3au.js → ssh-config-2z1QHuOG.js} +0 -0
  487. /package/dist/web/assets/{stylus-crCAEaBT.js → stylus-D4I-VaeY.js} +0 -0
  488. /package/dist/web/assets/{swift-eE3rT7TC.js → swift-C_2G-tIc.js} +0 -0
  489. /package/dist/web/assets/{synthwave-84-BWy14LBX.js → synthwave-84-osBjChRP.js} +0 -0
  490. /package/dist/web/assets/{system-verilog-DC0-DZM4.js → system-verilog-zQP2qPNU.js} +0 -0
  491. /package/dist/web/assets/{systemd-to5p6N8I.js → systemd-UGfjRSBM.js} +0 -0
  492. /package/dist/web/assets/{table-Bbc783ko.js → table-FLU8ei8b.js} +0 -0
  493. /package/dist/web/assets/{talonscript-DoTcU8wm.js → talonscript-C1XrFPwo.js} +0 -0
  494. /package/dist/web/assets/{tasl-cNZvUs95.js → tasl-B5raVn43.js} +0 -0
  495. /package/dist/web/assets/{tcl-mZE8sZH8.js → tcl-3-vbeMIc.js} +0 -0
  496. /package/dist/web/assets/{terraform-CqHPpIfu.js → terraform-a8VXZDm9.js} +0 -0
  497. /package/dist/web/assets/{text-wrap-CBT4Gxtg.js → text-wrap-B05VGRa-.js} +0 -0
  498. /package/dist/web/assets/{tokyo-night-Tp2rTIZd.js → tokyo-night-4SRhf-Hz.js} +0 -0
  499. /package/dist/web/assets/{toml-CU_vscgn.js → toml-B4N0i8wT.js} +0 -0
  500. /package/dist/web/assets/{trash-2-Bk4yYlck.js → trash-2-CTXlTflF.js} +0 -0
  501. /package/dist/web/assets/{triangle-alert-B8EYecpe.js → triangle-alert-BRq0VAfd.js} +0 -0
  502. /package/dist/web/assets/{tsv-ik7iFB9D.js → tsv-DA5k9Lvw.js} +0 -0
  503. /package/dist/web/assets/{tsx-BMvvMKiv.js → tsx-D9SZ8owC.js} +0 -0
  504. /package/dist/web/assets/{turtle-DBqKCq9o.js → turtle-BTHTvyY_.js} +0 -0
  505. /package/dist/web/assets/{typescript-DXJDmMZZ.js → typescript-DYzV9p5L.js} +0 -0
  506. /package/dist/web/assets/{typespec-CBseCMtZ.js → typespec-BC15eh7F.js} +0 -0
  507. /package/dist/web/assets/{typst-4oY25U9y.js → typst-BJUqzA6D.js} +0 -0
  508. /package/dist/web/assets/{use-is-mobile-D1weanbj.js → use-is-mobile-DFWViA4S.js} +0 -0
  509. /package/dist/web/assets/{utils-BNRH507E.js → utils-C9OjCrou.js} +0 -0
  510. /package/dist/web/assets/{v-BXaZ_1oy.js → v-50Lps6oA.js} +0 -0
  511. /package/dist/web/assets/{vala-BImWJE7z.js → vala-8wiUvvrD.js} +0 -0
  512. /package/dist/web/assets/{vb-CLfj2_sy.js → vb-BlgPLt6B.js} +0 -0
  513. /package/dist/web/assets/{vendor-xterm-_yyu5n32.js → vendor-xterm-BZOrwF_F.js} +0 -0
  514. /package/dist/web/assets/{verilog-BWa8Ed2b.js → verilog-CD9pVW5p.js} +0 -0
  515. /package/dist/web/assets/{vesper-BGj1cg6m.js → vesper-BrivW4PT.js} +0 -0
  516. /package/dist/web/assets/{vhdl-mByK7mlx.js → vhdl-Bunxfa7N.js} +0 -0
  517. /package/dist/web/assets/{viml-CugmlW5g.js → viml-DndUb2qM.js} +0 -0
  518. /package/dist/web/assets/{vitesse-black-vPdpy9TL.js → vitesse-black-BurZSkxW.js} +0 -0
  519. /package/dist/web/assets/{vitesse-dark-BuQK3dUh.js → vitesse-dark-8UShspzC.js} +0 -0
  520. /package/dist/web/assets/{vitesse-light-B3nz1XL9.js → vitesse-light-DghCAZ90.js} +0 -0
  521. /package/dist/web/assets/{vyper-DH_GTIHG.js → vyper-b-Clr-8u.js} +0 -0
  522. /package/dist/web/assets/{wasm-DfVbVJlW.js → wasm-C5v2T06Y.js} +0 -0
  523. /package/dist/web/assets/{wasm-C-LF9GjX.js → wasm-CpcKStkw.js} +0 -0
  524. /package/dist/web/assets/{wenyan-CzQkHyHx.js → wenyan-Dz2srLZR.js} +0 -0
  525. /package/dist/web/assets/{wgsl-Du_M1DW9.js → wgsl-CeD1-dRb.js} +0 -0
  526. /package/dist/web/assets/{wikitext-BWN_w5S7.js → wikitext-CyeS-9FH.js} +0 -0
  527. /package/dist/web/assets/{wit-BR13v5HD.js → wit-Kr-q-qBQ.js} +0 -0
  528. /package/dist/web/assets/{wolfram-BoXxp_Xl.js → wolfram-BKm8bmln.js} +0 -0
  529. /package/dist/web/assets/{x-nhriOX9o.js → x-YDu4m2Iq.js} +0 -0
  530. /package/dist/web/assets/{yaml-DpR_EF-d.js → yaml-2XSxomjM.js} +0 -0
  531. /package/dist/web/assets/{zenscript-C-EdUtUd.js → zenscript-CKIS34vf.js} +0 -0
  532. /package/dist/web/assets/{zig-BD4eA5nU.js → zig-DdHGNllX.js} +0 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Parse `~/.cloudflared/cert.pem` — the credential `cloudflared tunnel login` writes.
3
+ * State is always derived by parsing the file, never `existsSync`: a truncated,
4
+ * foreign, or revoked cert must never look "authenticated" just because a file
5
+ * happens to sit at the expected path.
6
+ */
7
+ import { resolve } from "node:path";
8
+ import { homedir } from "node:os";
9
+ import { existsSync, readFileSync } from "node:fs";
10
+
11
+ export interface ArgoCert {
12
+ zoneID: string;
13
+ accountID: string;
14
+ apiToken: string;
15
+ }
16
+
17
+ export type CertState =
18
+ | { kind: "absent" }
19
+ | { kind: "unparseable"; reason: string }
20
+ | { kind: "parsed"; cert: ArgoCert };
21
+
22
+ const HEX32 = /^[0-9a-f]{32}$/;
23
+ // The real-world label cloudflared ships today.
24
+ const STRICT_BLOCK = /-----BEGIN ARGO TUNNEL TOKEN-----([\s\S]*?)-----END ARGO TUNNEL TOKEN-----/;
25
+ // Fallback for a future cloudflared build that renames the label — accept any
26
+ // "...TOKEN..." PEM block rather than hard-failing every login on a cosmetic
27
+ // rename, while still preferring the known-good label as the first match.
28
+ const GENERIC_TOKEN_BLOCK = /-----BEGIN ([A-Z0-9 ]*TOKEN[A-Z0-9 ]*)-----([\s\S]*?)-----END \1-----/;
29
+
30
+ /**
31
+ * `cloudflared` decides this location, not PPM — it is a deliberate real-`homedir()`
32
+ * exception (see CLAUDE.md's "PPM Directory" section): `getPpmDir()` would point
33
+ * PPM's own data elsewhere and never find the user's actual login credential.
34
+ */
35
+ export function getOriginCertPath(): string {
36
+ return process.env.TUNNEL_ORIGIN_CERT || resolve(homedir(), ".cloudflared", "cert.pem");
37
+ }
38
+
39
+ /** Extract the token PEM block's base64 payload, trying the strict label first. */
40
+ function extractPayload(pem: string): string | null {
41
+ const strict = pem.match(STRICT_BLOCK);
42
+ if (strict) return strict[1]!;
43
+ const generic = pem.match(GENERIC_TOKEN_BLOCK);
44
+ if (generic) return generic[2]!;
45
+ return null;
46
+ }
47
+
48
+ /**
49
+ * Read and parse the origin cert. Never throws — every failure mode collapses
50
+ * to `unparseable` with a reason string that is safe to log (it never contains
51
+ * the base64 payload or any field value, so a leaked apiToken can't hide in it).
52
+ */
53
+ export function readOriginCertState(path?: string): CertState {
54
+ const certPath = path ?? getOriginCertPath();
55
+ if (!existsSync(certPath)) return { kind: "absent" };
56
+
57
+ let raw: string;
58
+ try {
59
+ raw = readFileSync(certPath, "utf-8");
60
+ } catch {
61
+ return { kind: "unparseable", reason: "cert.pem could not be read" };
62
+ }
63
+
64
+ const payload = extractPayload(raw);
65
+ if (payload == null) return { kind: "unparseable", reason: "no TOKEN PEM block found" };
66
+
67
+ let decoded: string;
68
+ try {
69
+ decoded = Buffer.from(payload.replace(/\s+/g, ""), "base64").toString("utf-8");
70
+ } catch {
71
+ return { kind: "unparseable", reason: "PEM block is not valid base64" };
72
+ }
73
+
74
+ let json: unknown;
75
+ try {
76
+ json = JSON.parse(decoded);
77
+ } catch {
78
+ return { kind: "unparseable", reason: "PEM payload is not valid JSON" };
79
+ }
80
+
81
+ if (typeof json !== "object" || json === null) {
82
+ return { kind: "unparseable", reason: "PEM payload is not a JSON object" };
83
+ }
84
+ const obj = json as Record<string, unknown>;
85
+ const { zoneID, accountID, apiToken } = obj;
86
+ if (typeof zoneID !== "string" || !zoneID || typeof accountID !== "string" || !accountID ||
87
+ typeof apiToken !== "string" || !apiToken) {
88
+ return { kind: "unparseable", reason: "cert is missing zoneID/accountID/apiToken" };
89
+ }
90
+ if (!HEX32.test(zoneID) || !HEX32.test(accountID)) {
91
+ return { kind: "unparseable", reason: "zoneID/accountID are not 32-char hex ids" };
92
+ }
93
+
94
+ return { kind: "parsed", cert: { zoneID, accountID, apiToken } };
95
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * One-shot cloudflared invocations (`tunnel create`, `route dns`, `tunnel
3
+ * token`) — distinct from the long-running login process in
4
+ * `cloudflared-login.service.ts`, which needs incremental stdout/stderr reads
5
+ * instead of a single awaited result.
6
+ */
7
+ import { ensureCloudflared } from "../cloudflared.service.ts";
8
+ import { killProcessTree } from "../windows-process-tree.ts";
9
+
10
+ export interface RunCloudflaredResult {
11
+ code: number;
12
+ stdout: string;
13
+ stderr: string;
14
+ }
15
+
16
+ /** Short one-shot calls must never hang setup indefinitely on a network stall. */
17
+ const DEFAULT_TIMEOUT_MS = 30_000;
18
+
19
+ /**
20
+ * Spawn cloudflared with `args`, capture both streams, and enforce a timeout.
21
+ * Never logs `args` — phase 1b's argv builders already keep them secret-free
22
+ * (the run token travels via `--token-file`, never argv), so this stays that
23
+ * way rather than re-introducing a leak at the logging layer.
24
+ */
25
+ export async function runCloudflared(
26
+ args: string[],
27
+ opts: { timeoutMs?: number } = {},
28
+ ): Promise<RunCloudflaredResult> {
29
+ const bin = await ensureCloudflared();
30
+ const proc = Bun.spawn([bin, ...args], { stdout: "pipe", stderr: "pipe" });
31
+
32
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
33
+ let timedOut = false;
34
+ const timer = setTimeout(() => {
35
+ timedOut = true;
36
+ killProcessTree(proc.pid);
37
+ }, timeoutMs);
38
+
39
+ try {
40
+ const [stdout, stderr, code] = await Promise.all([
41
+ new Response(proc.stdout).text(),
42
+ new Response(proc.stderr).text(),
43
+ proc.exited,
44
+ ]);
45
+ if (timedOut) throw new Error(`cloudflared timed out after ${timeoutMs}ms`);
46
+ return { code, stdout, stderr };
47
+ } finally {
48
+ clearTimeout(timer);
49
+ }
50
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Pure/self-contained helpers for the login session state machine, split out
3
+ * of `cloudflared-login.service.ts` purely to keep that file under the
4
+ * project's line-count guideline — none of these hold their own module state.
5
+ */
6
+ import { existsSync, renameSync } from "node:fs";
7
+ import { getOriginCertPath } from "./cloudflared-cert.ts";
8
+ import { resolveTunnelConfig } from "./named-tunnel-config.ts";
9
+ import { configService } from "../config.service.ts";
10
+ import { extractLoginUrl, isLoginSuccess } from "./login-output-parser.ts";
11
+
12
+ const VERIFY_TIMEOUT_MS = 8_000;
13
+
14
+ export type CertLiveCheck = "valid" | "invalid" | "unreachable";
15
+
16
+ /**
17
+ * Live-checks an apiToken against Cloudflare, distinguishing an authoritative
18
+ * rejection from simply failing to ask. This distinction is the whole point:
19
+ * the caller renames a stale cert.pem aside on "invalid" (Cloudflare itself
20
+ * said the token is dead) but must NOT do that on "unreachable" (a network
21
+ * blip, timeout, rate limit, or Cloudflare-side outage) — a user on a flaky
22
+ * connection who presses "Set up" must never have a perfectly working
23
+ * credential discarded just because the verify call itself didn't land.
24
+ */
25
+ export async function verifyCertLive(apiToken: string): Promise<CertLiveCheck> {
26
+ let res: Response;
27
+ try {
28
+ res = await fetch("https://api.cloudflare.com/client/v4/user/tokens/verify", {
29
+ headers: { Authorization: `Bearer ${apiToken}` },
30
+ signal: AbortSignal.timeout(VERIFY_TIMEOUT_MS),
31
+ });
32
+ } catch {
33
+ return "unreachable"; // network error, DNS failure, or the bounded timeout firing
34
+ }
35
+
36
+ // 401/403: Cloudflare rejected the token outright. 429/5xx: rate-limited or
37
+ // Cloudflare's own outage — neither says anything about the token itself.
38
+ if (res.status === 401 || res.status === 403) return "invalid";
39
+ if (res.status === 429 || res.status >= 500) return "unreachable";
40
+
41
+ const json = (await res.json().catch(() => null)) as { success?: boolean } | null;
42
+ if (json?.success === true) return "valid";
43
+ if (json?.success === false) return "invalid";
44
+ return "unreachable"; // an unexpected response shape is not an authoritative rejection
45
+ }
46
+
47
+ /** True when no `tunnel` identity is pinned yet, or the cert matches the pinned one. */
48
+ export function pinsMatch(cert: { zoneID: string; accountID: string }): boolean {
49
+ const resolved = resolveTunnelConfig(configService.get("tunnel"));
50
+ if (resolved.mode !== "named") return true;
51
+ return resolved.zoneID === cert.zoneID && resolved.accountID === cert.accountID;
52
+ }
53
+
54
+ /** Rename an existing cert.pem aside so a stale/foreign cert can never block a fresh login. */
55
+ export function renameCertAside(): void {
56
+ const path = getOriginCertPath();
57
+ if (!existsSync(path)) return;
58
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
59
+ renameSync(path, `${path}.bak-${stamp}`);
60
+ }
61
+
62
+ /**
63
+ * Read a login process's stdout/stderr stream incrementally: report the URL
64
+ * as soon as it appears, and signal success the moment the success line
65
+ * shows up (without waiting for the process to exit). `isSuperseded` lets the
66
+ * caller bail out the instant this stream stops being the "current" one
67
+ * (cancel/retry/timeout raced ahead of us).
68
+ */
69
+ export async function pumpLoginStream(
70
+ stream: ReadableStream<Uint8Array>,
71
+ hooks: { isSuperseded: () => boolean; hasUrl: () => boolean; onUrl: (url: string) => void; onSuccess: () => void },
72
+ ): Promise<void> {
73
+ const reader = stream.getReader();
74
+ const decoder = new TextDecoder();
75
+ let buffered = "";
76
+ try {
77
+ while (true) {
78
+ const { done, value } = await reader.read();
79
+ if (done) return;
80
+ if (hooks.isSuperseded()) return;
81
+
82
+ const chunk = decoder.decode(value, { stream: true });
83
+ buffered += chunk;
84
+
85
+ if (!hooks.hasUrl()) {
86
+ const url = extractLoginUrl(chunk) ?? extractLoginUrl(buffered);
87
+ if (url) hooks.onUrl(url);
88
+ }
89
+ if (isLoginSuccess(chunk) || isLoginSuccess(buffered)) {
90
+ hooks.onSuccess();
91
+ return;
92
+ }
93
+ }
94
+ } catch {
95
+ // Stream closed underneath us (process killed) — a terminal state has
96
+ // already been (or is about to be) set by whichever timer/cancel did it.
97
+ }
98
+ }
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Login session singleton behind the "log in to Cloudflare" popup.
3
+ *
4
+ * One process-per-machine at a time: a second `startLogin` call while one is
5
+ * live (`waiting`/`slow`) just returns the current snapshot instead of
6
+ * spawning a competing `cloudflared tunnel login`. Every terminal state
7
+ * (`success`/`timeout`/`cancelled`/`error`) drops the process ref, so the
8
+ * *next* call after a terminal state always starts a genuinely fresh process
9
+ * with a fresh URL — a killed session's URL is gone for good, there is no
10
+ * "cert appears later" path once the process is dead.
11
+ */
12
+ import { readOriginCertState } from "./cloudflared-cert.ts";
13
+ import { loginArgs } from "./named-tunnel-args.ts";
14
+ import { ensureCloudflared } from "../cloudflared.service.ts";
15
+ import { killProcessTree } from "../windows-process-tree.ts";
16
+ import { broadcastGlobalEvent } from "../../server/ws/global.ts";
17
+ import { verifyCertLive, pinsMatch, renameCertAside, pumpLoginStream } from "./cloudflared-login-helpers.ts";
18
+
19
+ export type LoginState = "idle" | "waiting" | "slow" | "success" | "timeout" | "cancelled" | "error";
20
+ export interface LoginSnapshot { state: LoginState; url: string | null; message: string | null }
21
+
22
+ export const LOGIN_SLOW_MS = 60_000;
23
+ export const LOGIN_TIMEOUT_MS = 300_000;
24
+ const CERT_POLL_MS = 2_000;
25
+
26
+ interface Session {
27
+ state: LoginState;
28
+ url: string | null;
29
+ message: string | null;
30
+ proc: ReturnType<typeof Bun.spawn> | null;
31
+ slowTimer: ReturnType<typeof setTimeout> | null;
32
+ killTimer: ReturnType<typeof setTimeout> | null;
33
+ certPollTimer: ReturnType<typeof setInterval> | null;
34
+ }
35
+
36
+ function freshSession(): Session {
37
+ return { state: "idle", url: null, message: null, proc: null, slowTimer: null, killTimer: null, certPollTimer: null };
38
+ }
39
+
40
+ let session: Session = freshSession();
41
+
42
+ export function getLoginSnapshot(): LoginSnapshot {
43
+ return { state: session.state, url: session.url, message: session.message };
44
+ }
45
+
46
+ function clearTimers(): void {
47
+ if (session.slowTimer) clearTimeout(session.slowTimer);
48
+ if (session.killTimer) clearTimeout(session.killTimer);
49
+ if (session.certPollTimer) clearInterval(session.certPollTimer);
50
+ session.slowTimer = null;
51
+ session.killTimer = null;
52
+ session.certPollTimer = null;
53
+ }
54
+
55
+ /** Move to a terminal state: clear timers, drop the process ref, broadcast. */
56
+ function setTerminal(state: LoginState, message: string | null): void {
57
+ clearTimers();
58
+ session.proc = null;
59
+ session.state = state;
60
+ session.message = message;
61
+ broadcastGlobalEvent({ type: "tunnel:login_state", state, message: message ?? undefined });
62
+ }
63
+
64
+ /** Wraps `pumpLoginStream` with this module's session-singleton hooks. */
65
+ function pumpStream(stream: ReadableStream<Uint8Array>, proc: ReturnType<typeof Bun.spawn>): Promise<void> {
66
+ return pumpLoginStream(stream, {
67
+ isSuperseded: () => session.proc !== proc,
68
+ hasUrl: () => session.url != null,
69
+ onUrl: (url) => {
70
+ session.url = url;
71
+ broadcastGlobalEvent({ type: "tunnel:login_url", url });
72
+ },
73
+ onSuccess: () => setTerminal("success", "logged in"),
74
+ });
75
+ }
76
+
77
+ /**
78
+ * Start (or return the live) login session.
79
+ *
80
+ * The cert-present shortcut is intentionally re-verified on every call — not
81
+ * a bare `existsSync` — because a replaced or revoked cert must never
82
+ * silently pass as "already logged in".
83
+ */
84
+ export async function startLogin(opts: { relogin?: boolean } = {}): Promise<LoginSnapshot> {
85
+ if (session.state === "waiting" || session.state === "slow") {
86
+ return getLoginSnapshot();
87
+ }
88
+
89
+ const relogin = !!opts.relogin;
90
+ if (!relogin) {
91
+ const certState = readOriginCertState();
92
+ if (certState.kind === "parsed") {
93
+ const liveCheck = await verifyCertLive(certState.cert.apiToken);
94
+
95
+ // WHY: a network blip must never discard a working credential. Only an
96
+ // authoritative "Cloudflare said no" (invalid) or a confirmed pin
97
+ // mismatch earns a rename-aside below; "unreachable" means the check
98
+ // itself failed to run, so cert.pem is left untouched and the user
99
+ // gets a clear, retryable error instead of an unwanted fresh login.
100
+ if (liveCheck === "unreachable") {
101
+ setTerminal("error", "không kết nối được Cloudflare để kiểm tra đăng nhập — thử lại");
102
+ return getLoginSnapshot();
103
+ }
104
+
105
+ if (liveCheck === "valid" && pinsMatch(certState.cert)) {
106
+ session = freshSession();
107
+ session.state = "success";
108
+ session.message = "already logged in";
109
+ return getLoginSnapshot();
110
+ }
111
+
112
+ // liveCheck === "invalid" (Cloudflare authoritatively rejected the
113
+ // token), or valid but pinned to a different account/zone: this cert
114
+ // can never pass the shortcut again as-is — move it aside now instead
115
+ // of leaving a bad file to keep silently triggering the same
116
+ // fallthrough on every future login attempt.
117
+ renameCertAside();
118
+ }
119
+ } else {
120
+ // Explicit ?relogin=1 — the user deliberately pressed "Đăng nhập lại",
121
+ // so this renames unconditionally regardless of network reachability.
122
+ renameCertAside();
123
+ }
124
+
125
+ const bin = await ensureCloudflared();
126
+ const proc = Bun.spawn([bin, ...loginArgs()], { stdout: "pipe", stderr: "pipe" });
127
+ session = { state: "waiting", url: null, message: null, proc, slowTimer: null, killTimer: null, certPollTimer: null };
128
+
129
+ session.slowTimer = setTimeout(() => {
130
+ if (session.proc !== proc) return;
131
+ session.state = "slow";
132
+ broadcastGlobalEvent({ type: "tunnel:login_state", state: "slow" });
133
+ }, LOGIN_SLOW_MS);
134
+
135
+ session.killTimer = setTimeout(() => {
136
+ if (session.proc !== proc) return;
137
+ killProcessTree(proc.pid);
138
+ setTerminal("timeout", "login timed out after 5 minutes");
139
+ }, LOGIN_TIMEOUT_MS);
140
+
141
+ session.certPollTimer = setInterval(() => {
142
+ if (session.proc !== proc) return;
143
+ if (readOriginCertState().kind === "parsed") setTerminal("success", "logged in");
144
+ }, CERT_POLL_MS);
145
+
146
+ Promise.all([pumpStream(proc.stdout, proc), pumpStream(proc.stderr, proc)])
147
+ .then(async () => {
148
+ if (session.proc !== proc) return; // already terminal
149
+ const code = await proc.exited;
150
+ if (session.proc !== proc) return;
151
+ if (code === 0) {
152
+ setTerminal("success", "logged in");
153
+ } else {
154
+ setTerminal("error", `cloudflared exited with code ${code}`);
155
+ }
156
+ })
157
+ .catch(() => {});
158
+
159
+ return getLoginSnapshot();
160
+ }
161
+
162
+ /** Kill the live process immediately (if any) and mark the session cancelled. */
163
+ export function cancelLogin(): void {
164
+ if (session.proc) killProcessTree(session.proc.pid);
165
+ setTerminal("cancelled", "cancelled");
166
+ }
167
+
168
+ // Orphaned login process on server crash/restart would otherwise wait forever
169
+ // for a browser callback that lands nowhere.
170
+ process.on("exit", () => {
171
+ if (session.proc) {
172
+ try { killProcessTree(session.proc.pid); } catch {}
173
+ }
174
+ });
175
+
176
+ /**
177
+ * Aggregate for callers that need a single patchable seam (the HTTP routes) —
178
+ * route tests monkey-patch these properties directly rather than reaching for
179
+ * `mock.module`, which replaces the module for every importer process-wide and
180
+ * would poison the direct-function unit tests in this same file's test suite.
181
+ */
182
+ export const cloudflaredLoginService = { getLoginSnapshot, startLogin, cancelLogin };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Hostname shape rules for the named tunnel.
3
+ *
4
+ * Exactly one label above the zone, never the apex, never `www`. Two reasons,
5
+ * not just a rule to copy: `cloudflared route dns` never errors on a hostname
6
+ * outside the zone — it silently appends the zone instead (e.g. asking for
7
+ * `ppm-probe.ppm.sh` under zone `hienle.tech` creates `ppm-probe.ppm.sh.hienle.tech`),
8
+ * so PPM must refuse before calling it; and Cloudflare's free Universal SSL
9
+ * certificate only covers one level of subdomain, so a deeper label parses fine
10
+ * here but serves a TLS error to every visitor.
11
+ */
12
+
13
+ export type HostnameCheck = { ok: true } | { ok: false; reason: string };
14
+
15
+ const LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
16
+ const MAX_HOSTNAME_LENGTH = 253;
17
+
18
+ /** Default proposal shown to the user: `ppm.<zone>`. */
19
+ export function proposeHostname(zone: string): string {
20
+ return `ppm.${zone.trim().toLowerCase()}`;
21
+ }
22
+
23
+ /** Combine a user-typed prefix with the pinned zone. */
24
+ export function buildHostname(prefix: string, zone: string): string {
25
+ return `${prefix.trim().toLowerCase()}.${zone.trim().toLowerCase()}`;
26
+ }
27
+
28
+ export function validateHostname(hostname: string, zone: string): HostnameCheck {
29
+ const host = hostname.trim().toLowerCase();
30
+ const z = zone.trim().toLowerCase();
31
+ if (!host || !z) return { ok: false, reason: "hostname and zone are required" };
32
+ if (host.length > MAX_HOSTNAME_LENGTH) return { ok: false, reason: "hostname exceeds 253 characters" };
33
+
34
+ if (host === z) {
35
+ return { ok: false, reason: "hostname cannot be the zone apex — a PPM token must never rewrite the zone's own record" };
36
+ }
37
+ if (host === `www.${z}`) {
38
+ return { ok: false, reason: "www is reserved" };
39
+ }
40
+ if (!host.endsWith(`.${z}`)) {
41
+ return { ok: false, reason: `hostname must be a subdomain of ${z} — cloudflared silently mis-routes anything else` };
42
+ }
43
+
44
+ // Everything above the zone must be exactly one label — Cloudflare's free
45
+ // certificate only covers one level, and a deeper label serves a TLS error.
46
+ const prefix = host.slice(0, host.length - z.length - 1);
47
+ if (prefix.includes(".")) {
48
+ return { ok: false, reason: "hostname must be exactly one label above the zone" };
49
+ }
50
+
51
+ for (const label of host.split(".")) {
52
+ if (!LABEL.test(label)) {
53
+ return { ok: false, reason: `invalid DNS label: "${label}"` };
54
+ }
55
+ }
56
+
57
+ return { ok: true };
58
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Pure parsers for `cloudflared tunnel login` stdout/stderr chunks.
3
+ *
4
+ * cloudflared prints the browser URL and progress lines on stderr, not
5
+ * stdout (verified 2026-09-05, cloudflared 2026.3.0) — callers must feed both
6
+ * streams through these functions, never stdout alone.
7
+ */
8
+
9
+ /** Primary shape: the real dash.cloudflare.com/argotunnel URL cloudflared prints today. */
10
+ const PRIMARY_URL = /https:\/\/dash\.cloudflare\.com\/argotunnel\?[^\s]+/;
11
+
12
+ /** Exact success line cloudflared prints on a successful login, exit code 0. */
13
+ const SUCCESS_LINE = /You have successfully logged in\./;
14
+
15
+ /**
16
+ * Extract the login URL from a stdout/stderr chunk (or accumulated buffer).
17
+ * Falls back to the first `https://` token on any line mentioning
18
+ * "argotunnel" so a cosmetic rename of the URL shape never leaves the flow
19
+ * stuck at `waiting` with no way for the user to open the browser page.
20
+ */
21
+ export function extractLoginUrl(text: string): string | null {
22
+ const primary = text.match(PRIMARY_URL);
23
+ if (primary) return primary[0];
24
+
25
+ for (const line of text.split(/\r?\n/)) {
26
+ if (!/argotunnel/i.test(line)) continue;
27
+ const fallback = line.match(/https:\/\/[^\s]+/);
28
+ if (fallback) return fallback[0];
29
+ }
30
+ return null;
31
+ }
32
+
33
+ /** True once cloudflared has printed its success line. */
34
+ export function isLoginSuccess(text: string): boolean {
35
+ return SUCCESS_LINE.test(text);
36
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * cloudflared argv builders for the named-tunnel flow, plus the two artifact
3
+ * files they depend on. Every builder here guarantees its own artifacts (mirrors
4
+ * `cloudflared.service.ts`'s `getQuickTunnelArgs` → `ensureQuickTunnelConfig`
5
+ * pattern) rather than assuming an earlier phase already created them.
6
+ *
7
+ * The run token never appears in argv, ever — it is written to a 0600 file and
8
+ * passed via `--token-file`, so it can't leak through `ppm status`, a process
9
+ * list, or a crash dump that captures cmdlines.
10
+ */
11
+ import { resolve } from "node:path";
12
+ import { hostname as osHostname } from "node:os";
13
+ import { existsSync, mkdirSync, writeFileSync, chmodSync } from "node:fs";
14
+ import { getPpmDir } from "../ppm-dir.ts";
15
+ import { getOriginCertPath } from "./cloudflared-cert.ts";
16
+
17
+ const namedTunnelConfigPath = () => resolve(getPpmDir(), "cloudflared-named.yml");
18
+ const namedTunnelTokenPath = () => resolve(getPpmDir(), "named-tunnel.token");
19
+ const isWindows = process.platform === "win32";
20
+
21
+ /**
22
+ * Ensure `getPpmDir()` exists without letting a pre-existing directory throw
23
+ * EEXIST (Windows read-only-attribute folders — Desktop/Documents/Downloads —
24
+ * have thrown on a recursive mkdir here before even though the dir was already
25
+ * present and usable).
26
+ */
27
+ function ensurePpmDir(): void {
28
+ if (existsSync(getPpmDir())) return;
29
+ try {
30
+ mkdirSync(getPpmDir(), { recursive: true, mode: 0o700 });
31
+ } catch (e: any) {
32
+ if (e?.code !== "EEXIST") throw e;
33
+ }
34
+ }
35
+
36
+ /** Best-effort chmod — POSIX enforces the mode; Windows has no equivalent bit. */
37
+ function chmodBestEffort(path: string, mode: number): void {
38
+ if (isWindows) return;
39
+ try { chmodSync(path, mode); } catch {}
40
+ }
41
+
42
+ /**
43
+ * Create `~/.ppm/cloudflared-named.yml` if absent; returns its path.
44
+ * A comment-only file still parses but logs `ERR Configuration file ... was
45
+ * empty` on every command (harmless noise, verified live); a *missing* file
46
+ * exits 1. `no-autoupdate: true` is a real, valid key so the file is never
47
+ * comment-only.
48
+ */
49
+ export function ensureNamedTunnelConfig(): string {
50
+ const path = namedTunnelConfigPath();
51
+ if (!existsSync(path)) {
52
+ ensurePpmDir();
53
+ writeFileSync(path, "no-autoupdate: true\n", { mode: 0o600 });
54
+ }
55
+ chmodBestEffort(path, 0o600);
56
+ return path;
57
+ }
58
+
59
+ /** Write the run token to a 0600 file; returns its path. Overwritten on every call. */
60
+ export function writeNamedTunnelToken(token: string): string {
61
+ ensurePpmDir();
62
+ const path = namedTunnelTokenPath();
63
+ writeFileSync(path, token, { mode: 0o600 });
64
+ chmodBestEffort(path, 0o600);
65
+ return path;
66
+ }
67
+
68
+ /** `tunnel login` has no cert yet, so no `--origincert` — that flag would point nowhere. */
69
+ export function loginArgs(): string[] {
70
+ return ["tunnel", "login"];
71
+ }
72
+
73
+ /** Global flags (`--origincert`/`--config`) always precede the subcommand. */
74
+ function managementPrefix(): string[] {
75
+ return ["--origincert", getOriginCertPath(), "--config", ensureNamedTunnelConfig()];
76
+ }
77
+
78
+ export function createTunnelArgs(name: string): string[] {
79
+ return [...managementPrefix(), "tunnel", "create", name];
80
+ }
81
+
82
+ export function tunnelTokenArgs(name: string): string[] {
83
+ return [...managementPrefix(), "tunnel", "token", name];
84
+ }
85
+
86
+ /**
87
+ * `--overwrite-dns` only when the caller has confirmed this is our own tunnel's
88
+ * UUID being re-pointed (e.g. after a reset) — never passed blindly, since it
89
+ * silently steals a CNAME that might belong to something else.
90
+ */
91
+ export function routeDnsArgs(name: string, hostname: string, overwrite: boolean): string[] {
92
+ const flags = overwrite ? ["--overwrite-dns"] : [];
93
+ return [...managementPrefix(), "tunnel", "route", "dns", ...flags, name, hostname];
94
+ }
95
+
96
+ /**
97
+ * Argv for the long-running named-tunnel process. No `--token` literal ever
98
+ * appears here — only a path to a 0600 file cloudflared reads itself.
99
+ */
100
+ export function namedRunArgs(token: string, port: number): string[] {
101
+ const cfg = ensureNamedTunnelConfig();
102
+ const tokenPath = writeNamedTunnelToken(token);
103
+ return ["--config", cfg, "tunnel", "run", "--token-file", tokenPath, "--url", `http://127.0.0.1:${port}`];
104
+ }
105
+
106
+ /** Stable, DNS-safe tunnel name derived from this machine's hostname. */
107
+ export function tunnelNameForHost(): string {
108
+ const sanitized = osHostname().toLowerCase().replace(/[^a-z0-9-]/g, "-");
109
+ return `ppm-${sanitized}`.slice(0, 32);
110
+ }