@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
@@ -3,7 +3,7 @@ import { resolve } from "node:path";
3
3
  import { mkdirSync, existsSync } from "node:fs";
4
4
  import { encrypt, decrypt } from "../lib/account-crypto.ts";
5
5
  import { getPpmDir } from "./ppm-dir.ts";
6
- export const CURRENT_SCHEMA_VERSION = 41;
6
+ export const CURRENT_SCHEMA_VERSION = 42;
7
7
 
8
8
  let db: Database | null = null;
9
9
  let dbProfile: string | null = null;
@@ -899,6 +899,34 @@ function runMigrations(database: Database): void {
899
899
  try { database.exec("ALTER TABLE turn_usage ADD COLUMN account_label TEXT"); } catch { /* column exists */ }
900
900
  database.exec("PRAGMA user_version = 41;");
901
901
  }
902
+
903
+ if (current < 42) {
904
+ // Unread rows created before `incrementSessionUnread` learned the project name have
905
+ // `project_name IS NULL`. They could not heal on their own: the bell refuses to open a
906
+ // session it cannot place, so the connection that would have filled the name never
907
+ // happens, and the entry comes back on every reload. Fill them from the mapping that
908
+ // already knows, same shape as the v11 backfill above.
909
+ database.exec(`
910
+ UPDATE session_metadata SET
911
+ project_name = COALESCE(project_name, (
912
+ SELECT project_name FROM session_map
913
+ WHERE session_map.sdk_id = session_metadata.session_id
914
+ OR session_map.ppm_id = session_metadata.session_id
915
+ )),
916
+ project_path = COALESCE(project_path, (
917
+ SELECT project_path FROM session_map
918
+ WHERE session_map.sdk_id = session_metadata.session_id
919
+ OR session_map.ppm_id = session_metadata.session_id
920
+ ))
921
+ WHERE project_name IS NULL OR project_path IS NULL
922
+ `);
923
+ database.exec(`
924
+ UPDATE session_metadata SET project_path = (
925
+ SELECT path FROM projects WHERE projects.name = session_metadata.project_name
926
+ ) WHERE project_path IS NULL AND project_name IS NOT NULL
927
+ `);
928
+ database.exec("PRAGMA user_version = 42;");
929
+ }
902
930
  }
903
931
 
904
932
  // ---------------------------------------------------------------------------
@@ -1161,17 +1189,26 @@ export interface UnreadEntry {
1161
1189
  sessionTitle: string | null;
1162
1190
  }
1163
1191
 
1164
- /** Increment unread count for a session and set the notification type + title snapshot */
1165
- export function incrementSessionUnread(sessionId: string, type: string, title?: string | null): void {
1166
- if (title) {
1167
- getDb().query(
1168
- "UPDATE session_metadata SET unread_count = unread_count + 1, unread_type = ?, last_known_title = ? WHERE session_id = ?",
1169
- ).run(type, title, sessionId);
1170
- } else {
1171
- getDb().query(
1172
- "UPDATE session_metadata SET unread_count = unread_count + 1, unread_type = ? WHERE session_id = ?",
1173
- ).run(type, sessionId);
1174
- }
1192
+ /** Increment unread count for a session and set the notification type + title snapshot.
1193
+ *
1194
+ * Row-creating, like {@link setSessionUnread}. A session PPM did not create — resumed
1195
+ * here, or started in the CLI — has no `session_metadata` row, and an UPDATE against a
1196
+ * missing row silently dropped the unread entry altogether.
1197
+ *
1198
+ * `project_name` is filled in the same statement so the bell can group and open the
1199
+ * session, and the COALESCE keeps the *stored* name: a name arriving later is not
1200
+ * evidence the session moved, and letting it win would let a stale name in a persisted
1201
+ * tab's URL overwrite a good one. */
1202
+ export function incrementSessionUnread(sessionId: string, type: string, title?: string | null, projectName?: string | null): void {
1203
+ getDb().query(
1204
+ `INSERT INTO session_metadata (session_id, unread_count, unread_type, last_known_title, project_name)
1205
+ VALUES (?, 1, ?, ?, ?)
1206
+ ON CONFLICT(session_id) DO UPDATE SET
1207
+ unread_count = session_metadata.unread_count + 1,
1208
+ unread_type = excluded.unread_type,
1209
+ last_known_title = COALESCE(excluded.last_known_title, session_metadata.last_known_title),
1210
+ project_name = COALESCE(session_metadata.project_name, excluded.project_name)`,
1211
+ ).run(sessionId, type, title ?? null, projectName ?? null);
1175
1212
  }
1176
1213
 
1177
1214
  /** Manually mark a session unread (idempotent: SETS count to 1, not increment; row-creating).
@@ -1188,6 +1225,19 @@ export function setSessionUnread(sessionId: string, type: string, title?: string
1188
1225
  ).run(sessionId, type, title ?? null, projectName ?? null);
1189
1226
  }
1190
1227
 
1228
+ /** Mark several sessions as read in one transaction.
1229
+ * Clearing is project-agnostic — the read route never looked at the project segment —
1230
+ * so this works for rows whose project is unknown or names a project that no longer
1231
+ * exists, which is exactly the set that could not be cleared before. */
1232
+ export function clearSessionUnreadMany(sessionIds: string[]): void {
1233
+ if (sessionIds.length === 0) return;
1234
+ const db = getDb();
1235
+ const stmt = db.query("UPDATE session_metadata SET unread_count = 0, unread_type = NULL WHERE session_id = ?");
1236
+ db.transaction(() => {
1237
+ for (const id of sessionIds) stmt.run(id);
1238
+ })();
1239
+ }
1240
+
1191
1241
  /** Mark a session as read (reset unread count) */
1192
1242
  export function clearSessionUnread(sessionId: string): void {
1193
1243
  getDb().query(
@@ -8,6 +8,7 @@ import type { RpcChannel } from "./extension-rpc.ts";
8
8
  import { contributionRegistry } from "./contribution-registry.ts";
9
9
  import { broadcastExtMsg, requestFromBrowser } from "../server/ws/extensions.ts";
10
10
  import { getProjects } from "./db.service.ts";
11
+ import { isSecretConfigKey, redactSecretConfigValue } from "./config-secret-keys.ts";
11
12
 
12
13
  let requestIdCounter = 0;
13
14
  function nextRequestId(): string {
@@ -153,11 +154,18 @@ export function registerVscodeCompatHandlers(rpc: RpcChannel): void {
153
154
  // --- workspace config ---
154
155
  rpc.onRequest("workspace:config:get", async (params) => {
155
156
  const [key] = params as [string];
157
+ // An extension gets no signal beyond "not present" for a secret key —
158
+ // same not-found shape as a key that genuinely does not exist.
159
+ if (isSecretConfigKey(key)) return null;
156
160
  // Read from PPM config service
157
161
  try {
158
162
  const { configService } = await import("./config.service.ts");
159
163
  const config = configService.getAll() as unknown as Record<string, unknown>;
160
- return getNestedValue(config, key) ?? null;
164
+ const value = getNestedValue(config, key) ?? null;
165
+ // A request for an ANCESTOR of a secret key (e.g. "tunnel", "auth")
166
+ // is not caught by isSecretConfigKey above and still holds the raw
167
+ // object here, so deep-redact before it crosses the RPC boundary.
168
+ return value === null ? null : redactSecretConfigValue(key, value);
161
169
  } catch {
162
170
  return null;
163
171
  }
@@ -8,23 +8,50 @@ import { RecreatedDirPoller } from "./recreated-dir-poller.ts";
8
8
  * near the number of *interesting* ones.
9
9
  *
10
10
  * `fs.watch(root, { recursive: true })` is a single call, but on Linux the runtime
11
- * expands it into one inotify watch per subdirectorydependency trees included.
12
- * Filtering those events on arrival (what this service used to do) still pays the
13
- * full cost, which is how a handful of projects reached ~360k watches and pushed
14
- * the machine against `fs.inotify.max_user_watches`.
11
+ * expands it into one inotify watch per *entry*every file as well as every
12
+ * directory, dependency trees included. Filtering those events on arrival (what this
13
+ * service used to do) still pays the full cost, which is how a handful of projects
14
+ * reached ~360k watches and pushed the machine against `fs.inotify.max_user_watches`.
15
15
  *
16
- * So coverage is chosen up front: a subtree with no ignored directory anywhere
17
- * inside it gets one native recursive watch (cheap, and the runtime tracks new
18
- * directories for us); any subtree that contains an ignored directory gets a
19
- * non-recursive watch plus per-child recursion, so the ignored branch is never
20
- * handed to the runtime. That keeps the watcher count small which also keeps
21
- * `fs.inotify.max_user_instances` out of play while pruning ~92% of the
22
- * directories in a typical repo.
16
+ * So coverage is chosen up front, and the shape depends on what the platform's
17
+ * recursive watch actually is.
18
+ *
19
+ * On Windows and macOS it is a kernel-side subtree watch (`ReadDirectoryChangesW`,
20
+ * FSEvents): one handle covers a whole subtree, it tracks directories created later,
21
+ * and it does not traverse reparse points. Measured on Windows 11 / Bun 1.3.10 over a
22
+ * 2,441-directory tree, one recursive handle attaches in 80ms for +7.0MB where one
23
+ * handle per directory takes 965ms for +25.2MB. So a subtree with no ignored directory
24
+ * inside it gets a single recursive watch there.
25
+ *
26
+ * On Linux there is no such call. Bun emulates it by walking the tree and opening a
27
+ * watch per entry, and that cost does not depend on the shape: measured over 4
28
+ * directories holding 600 files, one recursive handle and one non-recursive handle per
29
+ * directory both come to 604 descriptors on a single inotify instance. So this split
30
+ * buys nothing in inotify terms — the handle count was never what `max_user_instances`
31
+ * counts — and it is not why Linux takes a handle per directory.
32
+ *
33
+ * What it buys is coverage. The emulation differs from this scan in two ways: it does
34
+ * not pick up directories created after it attached, and it walks through symlinks this
35
+ * scan deliberately skips — a pnpm workspace with a symlinked `node_modules` had its
36
+ * whole store watched, one open descriptor per file, which exhausted the process at
37
+ * ~724k descriptors.
38
+ *
39
+ * Either way the walk below is what prunes ~92% of the directories in a typical repo,
40
+ * and an ignored branch is never handed to the runtime.
23
41
  */
24
42
 
25
43
  /** Coalesce a burst of directory churn into a single subtree rebuild. */
26
44
  const REBUILD_DEBOUNCE_MS = 500;
27
45
 
46
+ /**
47
+ * Whether `{ recursive: true }` is a real subtree watch rather than a per-directory walk.
48
+ * Named platforms rather than `!== "linux"`, so a runtime nobody has measured here —
49
+ * freebsd, say — takes the conservative walk instead of inheriting a promise about
50
+ * `ReadDirectoryChangesW`. `RecreatedDirPoller` is derived from this for the same reason:
51
+ * the emulation is a different mechanism with different defects.
52
+ */
53
+ const NATIVE_RECURSIVE = process.platform === "win32" || process.platform === "darwin";
54
+
28
55
  export interface WatchTreeOptions {
29
56
  /** Absolute path of the directory to watch. */
30
57
  root: string;
@@ -81,7 +108,10 @@ export class WatchTree {
81
108
  constructor(private readonly options: WatchTreeOptions) {
82
109
  // Only Bun on Linux has the stale-watch defect; elsewhere re-watching works
83
110
  // and paying for polling would be pure waste.
84
- this.poller = process.platform === "linux"
111
+ // Tied to NATIVE_RECURSIVE rather than re-testing the platform: the poller stands in
112
+ // for a defect of the same emulation, and `attach()` relies on exactly one of the two
113
+ // being in play. Deriving it here keeps that from drifting apart.
114
+ this.poller = !NATIVE_RECURSIVE
85
115
  ? new RecreatedDirPoller({ onChange: (abs) => this.reportAbs(abs) })
86
116
  : null;
87
117
  }
@@ -135,13 +165,23 @@ export class WatchTree {
135
165
  }
136
166
 
137
167
  for (const entry of entries) {
138
- // isDirectory() is false for symlinks, which keeps link cycles and
139
- // duplicate coverage (pnpm stores, workspace links) out of the walk.
140
- if (!entry.isDirectory()) continue;
141
- if (isIgnoredDirName(entry.name)) {
168
+ // A symlink named like an ignored directory still has to mark the subtree, or an
169
+ // ancestor takes a recursive watch over a `node_modules` this walk never entered —
170
+ // which is how a pnpm store got watched through the link. Tested before
171
+ // isDirectory(), which is false for symlinks. Plain files are deliberately left out:
172
+ // IGNORED_DIRS holds names like `build`, `out` and `target`, and a compiled binary
173
+ // called `build` is no reason to cost everything around it its recursive coverage.
174
+ // A symlink to a *file* is caught here too, since telling it from a link to a
175
+ // directory needs a stat() the walk would otherwise never make. It holds no subtree,
176
+ // so marking it only forfeits recursive coverage — the safe direction, and it takes
177
+ // a file named exactly `node_modules` or `target` to happen at all.
178
+ if (isIgnoredDirName(entry.name) && (entry.isDirectory() || entry.isSymbolicLink())) {
142
179
  node.hasIgnored = true;
143
180
  continue;
144
181
  }
182
+ // isDirectory() is false for symlinks, which keeps link cycles and
183
+ // duplicate coverage (pnpm stores, workspace links) out of the walk.
184
+ if (!entry.isDirectory()) continue;
145
185
  if (budget.left <= 0) {
146
186
  // Unscanned directories remain: mark the node so no ancestor covers them
147
187
  // recursively, since we cannot know what they hold.
@@ -161,23 +201,17 @@ export class WatchTree {
161
201
  private attach(node: ScanNode): void {
162
202
  const fitsWholeSubtree = this.covered + node.size <= this.options.maxDirs;
163
203
 
164
- // A path we have watched before is being re-attached, so this directory was
165
- // deleted and recreated. On Bun + Linux its watcher will never fire again:
166
- // poll its own entries, and cover the subtree non-recursively so each child
167
- // gets a watcher on a path the runtime still honours.
168
- if (this.poller && this.everAttached.has(node.path)) {
169
- this.poller.add(node.path);
170
- if (this.covered + 1 > this.options.maxDirs) {
171
- this.truncated = true;
172
- return;
173
- }
174
- this.addWatcher(node.path, false, 1);
175
- for (const child of node.dirs) this.attach(child);
176
- return;
177
- }
178
-
179
- // One recursive watch for a subtree the runtime can safely expand on its own.
180
- if (!node.hasIgnored && fitsWholeSubtree && this.addWatcher(node.path, true, node.size)) return;
204
+ // A path we have watched before is being re-attached, so this directory was deleted
205
+ // and recreated, and on Bun + Linux its watcher will never fire again — poll its own
206
+ // entries instead. It needs no special coverage beyond that: the poller only exists
207
+ // where the recursive branch below is off, so the per-directory walk it falls through
208
+ // to is already the watch-on-a-path-the-runtime-still-honours this case wants.
209
+ if (this.poller && this.everAttached.has(node.path)) this.poller.add(node.path);
210
+
211
+ // One recursive watch for a subtree the runtime can safely expand on its own —
212
+ // only where that is a kernel-side subtree watch rather than a per-directory walk.
213
+ if (NATIVE_RECURSIVE && !node.hasIgnored && fitsWholeSubtree
214
+ && this.addWatcher(node.path, true, node.size)) return;
181
215
 
182
216
  // Otherwise cover this directory alone and recurse — a failed recursive attach
183
217
  // falls through to here too, so one unwatchable directory cannot silently drop
@@ -2,17 +2,32 @@ import { WatchTree } from "./file-watcher/watch-tree.ts";
2
2
 
3
3
  const DEBOUNCE_MS = 500;
4
4
  /**
5
- * Directory budgets. One covered directory costs one inotify watch on Linux, and
6
- * the machine-wide default ceiling is 524288 shared with every other process, so
7
- * PPM stays well inside it even with several projects watched at once.
5
+ * Directory budgets. These bound the directories PPM *covers*, which is not the same as
6
+ * the inotify watches it holds: measured on Linux, watching a directory costs one
7
+ * descriptor for it plus one for every file inside 4 directories holding 600 files
8
+ * come to 604, recursive and non-recursive alike. So a directory cap bounds handles and
9
+ * walk cost, not descriptors; what keeps PPM inside the machine-wide 524288 ceiling is
10
+ * never registering `node_modules` in the first place.
8
11
  *
9
- * The per-project cap is hard. The total is best-effort: a native recursive watch
10
- * picks up directories created later without telling us, so exact accounting is not
11
- * possible — the worst case is a few projects at their own cap, still ~4% of the
12
- * kernel ceiling.
12
+ * The per-project cap is hard. The total is best-effort: a tree keeps growing after
13
+ * it starts, through `syncChildDir` -> `cover`, up to its own `maxDirs` and on
14
+ * Windows/macOS a recursive handle covers directories created later without telling
15
+ * us either. So projects can add up past the total.
16
+ *
17
+ * A pnpm monorepo runs past 8000 directories on its own — the cap then silently stops
18
+ * watching the rest of the tree, which reads as a file change that never arrives.
19
+ * 12000 covers those with room to spare.
20
+ */
21
+ const MAX_DIRS_PER_PROJECT = 12_000;
22
+ const MAX_DIRS_TOTAL = 30_000;
23
+ /**
24
+ * Floor on what a project is handed. Dividing the total down to nothing is worse than
25
+ * overshooting it: at 12000 per project the budgets ran 12000 -> 8000 -> **0**, and a
26
+ * project watching zero directories reports no changes at all — the same "file change
27
+ * that never arrives", total instead of partial, and indistinguishable from a broken
28
+ * watcher. A late project now gets a small budget and the truncation warning instead.
13
29
  */
14
- const MAX_DIRS_PER_PROJECT = 8_000;
15
- const MAX_DIRS_TOTAL = 20_000;
30
+ const MIN_DIRS_PER_PROJECT = 1_000;
16
31
 
17
32
  type ChangeCallback = (projectName: string, path: string) => void;
18
33
 
@@ -64,7 +79,10 @@ export function startWatching(projectName: string, projectPath: string): void {
64
79
  return;
65
80
  }
66
81
 
67
- const maxDirs = Math.max(0, Math.min(MAX_DIRS_PER_PROJECT, MAX_DIRS_TOTAL - totalCoveredDirs()));
82
+ const maxDirs = Math.max(
83
+ MIN_DIRS_PER_PROJECT,
84
+ Math.min(MAX_DIRS_PER_PROJECT, MAX_DIRS_TOTAL - totalCoveredDirs()),
85
+ );
68
86
  const entry: WatchEntry = {
69
87
  tree: new WatchTree({
70
88
  root: projectPath,
@@ -0,0 +1,103 @@
1
+ import { resolve, sep } from "node:path";
2
+ import { homedir } from "node:os";
3
+ import { getPpmDir } from "./ppm-dir.ts";
4
+ import { getUploadsDir } from "./chat-upload-storage.service.ts";
5
+ import { realPathOrSelf } from "./fs-ops/fs-real-path.ts";
6
+
7
+ /**
8
+ * Every path a generic filesystem route (read, write, copy, move, upload,
9
+ * trash…) must refuse because it holds credential material: the PPM config
10
+ * DB (provider keys, auth token) and `~/.cloudflared` (the Cloudflare login
11
+ * cert). Split out of `fs-path-guard.service.ts` so this one seam — "is this
12
+ * a credential path" — stays a single file instead of growing alongside the
13
+ * platform-allowlist and protected-root logic that lives there.
14
+ */
15
+
16
+ /** Case-insensitive prefix test on Windows/macOS-style paths. */
17
+ function isInside(child: string, parent: string): boolean {
18
+ const norm = (p: string) => (process.platform === "win32" ? p.toLowerCase() : p);
19
+ const c = norm(child);
20
+ const p = norm(parent);
21
+ return c === p || c.startsWith(p.endsWith(sep) ? p : p + sep);
22
+ }
23
+
24
+ /** True when the path is the PPM directory or anything inside it. */
25
+ export function isPpmDirPath(resolved: string): boolean {
26
+ return isInside(resolved, getPpmDir());
27
+ }
28
+
29
+ /**
30
+ * Chat attachments the user uploaded through the UI. They sit under the PPM
31
+ * dir so chat history keeps resolving them across reboots, but they are
32
+ * ordinary user content, not credential material: the transcript has to render
33
+ * the very image the assistant just read back from that directory.
34
+ */
35
+ export function isChatUploadPath(resolved: string): boolean {
36
+ return isInside(resolved, getUploadsDir());
37
+ }
38
+
39
+ /**
40
+ * True when the path is `~/.cloudflared` or anything inside it. Real
41
+ * `homedir()` is a deliberate exception to the getPpmDir()-only rule (see
42
+ * CLAUDE.md "PPM Directory"): `cloudflared`, not PPM, decides this location,
43
+ * and it holds `cert.pem` — an account-level Cloudflare login credential that
44
+ * must never be servable through a generic file route. `isInside` is a
45
+ * prefix match against this exact resolved path, not a substring test, so an
46
+ * unrelated folder that merely contains ".cloudflared" as a path segment
47
+ * elsewhere does not match.
48
+ */
49
+ export function isCloudflaredDirPath(resolved: string): boolean {
50
+ return isInside(resolved, resolve(homedir(), ".cloudflared"));
51
+ }
52
+
53
+ /** True when the path holds credential material a generic file route must never serve or relocate. */
54
+ export function isCredentialPath(resolved: string): boolean {
55
+ return isPpmDirPath(resolved) || isCloudflaredDirPath(resolved);
56
+ }
57
+
58
+ /**
59
+ * Refuse the PPM directory subtree and `~/.cloudflared` on read-style doors.
60
+ * The former stores the config database with provider credentials and auth
61
+ * tokens; the latter stores the Cloudflare login cert. Neither may be
62
+ * downloadable through a generic file route.
63
+ *
64
+ * Chat uploads are the one exception, and only for the PPM-dir branch — chat
65
+ * uploads always live under `getPpmDir()`, never under `~/.cloudflared`, so
66
+ * the exception is a no-op for the cloudflared branch. Every caller applies
67
+ * this to the requested path *and* to its real path, so a symlink parked in
68
+ * the uploads directory still fails on the second call and cannot reach the
69
+ * rest of the PPM dir through the exception, and a symlink pointing at
70
+ * `~/.cloudflared/cert.pem` fails the same way.
71
+ */
72
+ export function assertNotPpmDir(resolved: string): void {
73
+ if (isCredentialPath(resolved) && !isChatUploadPath(resolved)) {
74
+ throw Object.assign(new Error("Access denied"), { status: 403, code: "EDENIED" });
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Refuse operations that would relocate a credential directory's contents
80
+ * anywhere else — copy, move, rename, upload-over, trash. Reading a
81
+ * credential path is already blocked by `assertNotPpmDir`, so without this a
82
+ * copy to a public path followed by an ordinary read would still hand out
83
+ * the PPM config DB or the Cloudflare login cert; every write/transfer door
84
+ * in `fs-ops/` calls this on both the source and the destination.
85
+ */
86
+ export function assertNotPpmSubtree(candidate: string): void {
87
+ if (isCredentialPath(candidate)) {
88
+ throw Object.assign(new Error(`Refusing to operate on a credential directory: ${candidate}`), {
89
+ status: 403,
90
+ code: "EPROTECTED",
91
+ });
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Same refusal, applied to the real path as well. A path that does not exist
97
+ * yet is still resolved through its parents, so a symlinked directory cannot
98
+ * be used to reach — or create something inside — a credential directory.
99
+ */
100
+ export async function assertNotPpmSubtreeDeep(candidate: string): Promise<void> {
101
+ assertNotPpmSubtree(candidate);
102
+ assertNotPpmSubtree(await realPathOrSelf(candidate));
103
+ }
@@ -1,14 +1,26 @@
1
- import { resolve, sep } from "node:path";
1
+ import { resolve } from "node:path";
2
2
  import { lstat, realpath } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
4
  import { getPpmDir } from "./ppm-dir.ts";
5
- import { getUploadsDir } from "./chat-upload-storage.service.ts";
6
5
  import { realPathOrSelf, realPathOrSelfSync } from "./fs-ops/fs-real-path.ts";
7
6
 
8
7
  // Re-exported so callers reach both the guards and the resolver they build on
9
8
  // through one import.
10
9
  export { realPathOrSelf, realPathOrSelfSync };
11
10
 
11
+ // The credential-path predicates (PPM dir + ~/.cloudflared) live in their own
12
+ // module so this file stays focused on the platform allowlist and protected
13
+ // roots; re-exported here so every existing caller keeps a single import.
14
+ export {
15
+ isPpmDirPath,
16
+ isChatUploadPath,
17
+ isCloudflaredDirPath,
18
+ isCredentialPath,
19
+ assertNotPpmDir,
20
+ assertNotPpmSubtree,
21
+ assertNotPpmSubtreeDeep,
22
+ } from "./fs-credential-path-guard.ts";
23
+
12
24
  /**
13
25
  * Central guard for every filesystem route that works outside project scope.
14
26
  * The whole disk is browsable behind PPM auth, so the remaining defences are:
@@ -38,14 +50,6 @@ export function resolvePath(input: string): string {
38
50
  return resolve(input);
39
51
  }
40
52
 
41
- /** Case-insensitive prefix test on Windows/macOS-style paths. */
42
- function isInside(child: string, parent: string): boolean {
43
- const norm = (p: string) => (process.platform === "win32" ? p.toLowerCase() : p);
44
- const c = norm(child);
45
- const p = norm(parent);
46
- return c === p || c.startsWith(p.endsWith(sep) ? p : p + sep);
47
- }
48
-
49
53
  /**
50
54
  * Whitelist for system-level access. POSIX exposes the whole tree; Windows
51
55
  * requires a drive letter, which keeps UNC shares (`\\server\share`) out —
@@ -80,62 +84,6 @@ export function assertAllowed(resolved: string): void {
80
84
  }
81
85
  }
82
86
 
83
- /** True when the path is the PPM directory or anything inside it. */
84
- export function isPpmDirPath(resolved: string): boolean {
85
- return isInside(resolved, getPpmDir());
86
- }
87
-
88
- /**
89
- * Chat attachments the user uploaded through the UI. They sit under the PPM
90
- * dir so chat history keeps resolving them across reboots, but they are
91
- * ordinary user content, not credential material: the transcript has to render
92
- * the very image the assistant just read back from that directory.
93
- */
94
- export function isChatUploadPath(resolved: string): boolean {
95
- return isInside(resolved, getUploadsDir());
96
- }
97
-
98
- /**
99
- * Refuse the PPM directory subtree on read-style doors. It stores the config
100
- * database with provider credentials and auth tokens, which must never be
101
- * downloadable through a generic file route.
102
- *
103
- * Chat uploads are the one exception. Every caller applies this to the
104
- * requested path *and* to its real path, so a symlink parked in the uploads
105
- * directory still fails on the second call and cannot reach the rest of the
106
- * PPM dir through the exception.
107
- */
108
- export function assertNotPpmDir(resolved: string): void {
109
- if (isPpmDirPath(resolved) && !isChatUploadPath(resolved)) {
110
- throw Object.assign(new Error("Access denied"), { status: 403, code: "EDENIED" });
111
- }
112
- }
113
-
114
- /**
115
- * Refuse operations that would take the PPM directory's contents anywhere
116
- * else. Reading that subtree is already blocked, so without this a copy to a
117
- * public path followed by an ordinary read would still hand out the
118
- * credentials database.
119
- */
120
- export function assertNotPpmSubtree(candidate: string): void {
121
- if (isPpmDirPath(candidate)) {
122
- throw Object.assign(new Error(`Refusing to operate on the PPM directory: ${candidate}`), {
123
- status: 403,
124
- code: "EPROTECTED",
125
- });
126
- }
127
- }
128
-
129
- /**
130
- * Same refusal, applied to the real path as well. A path that does not exist
131
- * yet is still resolved through its parents, so a symlinked directory cannot
132
- * be used to reach — or create something inside — the PPM directory.
133
- */
134
- export async function assertNotPpmSubtreeDeep(candidate: string): Promise<void> {
135
- assertNotPpmSubtree(candidate);
136
- assertNotPpmSubtree(await realPathOrSelf(candidate));
137
- }
138
-
139
87
  /** Paths whose removal or rename would break the host or PPM itself. */
140
88
  export function isProtectedRoot(candidate: string): boolean {
141
89
  const p = resolve(candidate);
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Cloudflare REST calls the setup flow needs beyond what cloudflared's own
3
+ * CLI exposes: looking up an existing tunnel's UUID by name (so the DNS
4
+ * collision check can tell "already ours" from "someone else's" before
5
+ * `tunnel create` has even run) and reading a zone's DNS records for a
6
+ * candidate hostname.
7
+ */
8
+
9
+ const TIMEOUT_MS = 10_000;
10
+
11
+ export interface DnsRecord {
12
+ content: string;
13
+ }
14
+
15
+ async function cfGet<T extends { success?: boolean }>(url: string, apiToken: string): Promise<T> {
16
+ const res = await fetch(url, {
17
+ headers: { Authorization: `Bearer ${apiToken}` },
18
+ signal: AbortSignal.timeout(TIMEOUT_MS),
19
+ });
20
+ const json = (await res.json().catch(() => null)) as T | null;
21
+ if (!res.ok || !json || json.success !== true) {
22
+ throw new Error(`Cloudflare API call failed (HTTP ${res.status})`);
23
+ }
24
+ return json;
25
+ }
26
+
27
+ /** DNS records at `zoneID` matching `name` exactly (empty when none exist). */
28
+ export async function fetchDnsRecords(zoneID: string, apiToken: string, name: string): Promise<DnsRecord[]> {
29
+ const url = `https://api.cloudflare.com/client/v4/zones/${encodeURIComponent(zoneID)}/dns_records?name=${encodeURIComponent(name)}`;
30
+ const json = await cfGet<{ success: true; result?: DnsRecord[] }>(url, apiToken);
31
+ return Array.isArray(json.result) ? json.result : [];
32
+ }
33
+
34
+ /** UUID of an existing, non-deleted tunnel named `name` — null if none exists yet. */
35
+ export async function fetchTunnelIdByName(accountID: string, apiToken: string, name: string): Promise<string | null> {
36
+ const url = `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(accountID)}/cfd_tunnel?name=${encodeURIComponent(name)}&is_deleted=false`;
37
+ const json = await cfGet<{ success: true; result?: Array<{ id?: string }> }>(url, apiToken);
38
+ const found = (json.result ?? []).find((t) => typeof t.id === "string" && t.id);
39
+ return found?.id ?? null;
40
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Resolve the zone name behind a `zoneID`, using the `apiToken` embedded in the
3
+ * user's origin cert — this avoids asking the user to type a hostname; PPM
4
+ * proposes `ppm.<zone>` from data it already has.
5
+ */
6
+
7
+ /** 10s bound so a hung Cloudflare API call never wedges setup indefinitely. */
8
+ const TIMEOUT_MS = 10_000;
9
+
10
+ export async function fetchZoneName(zoneID: string, apiToken: string): Promise<string> {
11
+ const url = `https://api.cloudflare.com/client/v4/zones/${encodeURIComponent(zoneID)}`;
12
+ const res = await fetch(url, {
13
+ headers: { Authorization: `Bearer ${apiToken}` },
14
+ signal: AbortSignal.timeout(TIMEOUT_MS),
15
+ });
16
+
17
+ let json: { success?: boolean; result?: { name?: string } } | null = null;
18
+ try {
19
+ json = await res.json();
20
+ } catch {
21
+ json = null;
22
+ }
23
+
24
+ if (!res.ok || !json || json.success !== true || typeof json.result?.name !== "string" || !json.result.name) {
25
+ throw new Error(`Cloudflare rejected the certificate token (HTTP ${res.status}) — log in again`);
26
+ }
27
+ return json.result.name;
28
+ }