@hienlh/ppm 0.18.11 → 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 (571) hide show
  1. package/AGENTS.md +17 -5
  2. package/CHANGELOG.md +27 -0
  3. package/CLAUDE.md +3 -0
  4. package/README.md +87 -29
  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/.spike/cdp-client.ts +0 -116
  240. package/.spike/cleanup-ppm-workspace.mjs +0 -44
  241. package/.spike/group-protected-probe.ts +0 -21
  242. package/.spike/pip-eval.ts +0 -17
  243. package/.spike/pip-page-helpers.js +0 -119
  244. package/.spike/pip-results-editor-glue.json +0 -105
  245. package/.spike/pip-results-editor.json +0 -158
  246. package/.spike/pip-results-input-isolation.json +0 -28
  247. package/.spike/pip-results-key-probe.json +0 -58
  248. package/.spike/pip-results-listener-survival.json +0 -98
  249. package/.spike/pip-results-listeners.json +0 -36
  250. package/.spike/pip-results-pagehide.json +0 -92
  251. package/.spike/pip-results-resize.json +0 -104
  252. package/.spike/pip-results-terminal-debug.json +0 -99
  253. package/.spike/pip-results-terminal-v2.json +0 -191
  254. package/.spike/pip-results-terminal.json +0 -220
  255. package/.spike/pip-results-xterm-trace.json +0 -73
  256. package/.spike/pip-spike-common.ts +0 -83
  257. package/.spike/pip-spike-editor-glue.ts +0 -82
  258. package/.spike/pip-spike-editor.ts +0 -129
  259. package/.spike/pip-spike-input-isolation.ts +0 -38
  260. package/.spike/pip-spike-key-probe.ts +0 -34
  261. package/.spike/pip-spike-listener-survival.ts +0 -54
  262. package/.spike/pip-spike-listeners.ts +0 -19
  263. package/.spike/pip-spike-pagehide.ts +0 -59
  264. package/.spike/pip-spike-resize.ts +0 -42
  265. package/.spike/pip-spike-setup.ts +0 -63
  266. package/.spike/pip-spike-terminal-debug.ts +0 -81
  267. package/.spike/pip-spike-terminal-v2.ts +0 -139
  268. package/.spike/pip-spike-terminal.ts +0 -169
  269. package/.spike/pip-spike-xterm-trace.ts +0 -36
  270. package/.spike/probe-drag-memo-error.mjs +0 -67
  271. package/.spike/read-dev-db.ts +0 -15
  272. package/.spike/s.err +0 -63
  273. package/.spike/scratch-editor-target.ts +0 -7
  274. package/.spike/smoke-unified-chrome-pip.mjs +0 -353
  275. package/.spike/tunnel.err +0 -63
  276. package/.spike/vite-restart.err +0 -663
  277. package/.spike/w.err +0 -571
  278. package/dist/web/assets/api-client-DTZEgjBQ.js +0 -1
  279. package/dist/web/assets/api-settings-kd1j4iR8.js +0 -1
  280. package/dist/web/assets/architecture-PBZL5I3N-8dCMT9r7.js +0 -1
  281. package/dist/web/assets/chat-tab-E3bf_V8X.js +0 -15
  282. package/dist/web/assets/code-editor-BUlM2gXk.js +0 -10
  283. package/dist/web/assets/explorer-body-9aBmyVpZ.js +0 -2
  284. package/dist/web/assets/explorer-window-content-DRtqoSPA.js +0 -1
  285. package/dist/web/assets/gitGraph-HDMCJU4V-2e_8dfbs.js +0 -1
  286. package/dist/web/assets/glide-data-grid-C4_zIneN.js +0 -138
  287. package/dist/web/assets/index-BImUaigO.js +0 -52
  288. package/dist/web/assets/index-CSfi6DSU.css +0 -2
  289. package/dist/web/assets/info-3K5VOQVL-BZi1Gha1.js +0 -1
  290. package/dist/web/assets/keybindings-store-CzV2sPls.js +0 -1
  291. package/dist/web/assets/markdown-renderer-CcbR33VG.js +0 -1
  292. package/dist/web/assets/mobile-explorer-sheet-SIwQXlwA.js +0 -1
  293. package/dist/web/assets/notification-store-DV5QtUTg.js +0 -1
  294. package/dist/web/assets/packet-RMMSAZCW-o5htEcin.js +0 -1
  295. package/dist/web/assets/pie-UPGHQEXC-BXAIFSAD.js +0 -1
  296. package/dist/web/assets/radar-KQ55EAFF-Dbt2Aebl.js +0 -1
  297. package/dist/web/assets/settings-tab-Dic6TKkU.js +0 -1
  298. package/dist/web/assets/system-monitor-body-iwGiCns9.js +0 -2
  299. package/dist/web/assets/system-monitor-tab-DdXQhEel.js +0 -1
  300. package/dist/web/assets/team-member-sheet-LkAFSCvb.js +0 -2
  301. package/dist/web/assets/treemap-KZPCXAKY-DUbnl9dz.js +0 -1
  302. package/dist/web/assets/video-preview-m0-3p7QZ.js +0 -1
  303. /package/dist/web/assets/{abap-C6nB04tn.js → abap-DGlrX_8G.js} +0 -0
  304. /package/dist/web/assets/{actionscript-3-Vx4rw2sC.js → actionscript-3-CUqRtUJE.js} +0 -0
  305. /package/dist/web/assets/{ada-Ccd8vM20.js → ada-CY3Spa33.js} +0 -0
  306. /package/dist/web/assets/{andromeeda-DnBX5xMg.js → andromeeda-Lxs2TJn7.js} +0 -0
  307. /package/dist/web/assets/{apache-B-9_sx0g.js → apache-DfxgGdgM.js} +0 -0
  308. /package/dist/web/assets/{apex-_wPVBYoL.js → apex-QVDF1dnI.js} +0 -0
  309. /package/dist/web/assets/{api-client-aQb5hHE5.js → api-client-oY1tC3tM.js} +0 -0
  310. /package/dist/web/assets/{applescript-CcvD-qr0.js → applescript-pwJ8QXh-.js} +0 -0
  311. /package/dist/web/assets/{apply-theme-BeLRfsSu.js → apply-theme-B_bMVL6a.js} +0 -0
  312. /package/dist/web/assets/{ara-C40Ztoms.js → ara-mPjeX5YX.js} +0 -0
  313. /package/dist/web/assets/{asciidoc-CybYkJV4.js → asciidoc-Bf7LsDkI.js} +0 -0
  314. /package/dist/web/assets/{asm-9eOKwzR0.js → asm-rRKC1OUx.js} +0 -0
  315. /package/dist/web/assets/{aurora-x-B5ZH4t7t.js → aurora-x-DwQr0DkG.js} +0 -0
  316. /package/dist/web/assets/{awk-Dqilob-2.js → awk-Bpt768fN.js} +0 -0
  317. /package/dist/web/assets/{ayu-dark-CPgRhf5r.js → ayu-dark-D9lwQHMs.js} +0 -0
  318. /package/dist/web/assets/{ayu-light-Do06Q52f.js → ayu-light-B4-ptrU5.js} +0 -0
  319. /package/dist/web/assets/{ayu-mirage-_BhxcrxY.js → ayu-mirage-DAyRzH5Q.js} +0 -0
  320. /package/dist/web/assets/{ballerina-DDwK54QW.js → ballerina-Cgdpj1HC.js} +0 -0
  321. /package/dist/web/assets/{bat-BGyGlzGk.js → bat-4FrN36S2.js} +0 -0
  322. /package/dist/web/assets/{beancount-CRWBdapI.js → beancount-DkcxQER3.js} +0 -0
  323. /package/dist/web/assets/{berry-DbSFn2gO.js → berry-Dnuut-Ml.js} +0 -0
  324. /package/dist/web/assets/{bibtex-QpsV9gm9.js → bibtex-CCDwmbuA.js} +0 -0
  325. /package/dist/web/assets/{bicep-CxS8CUve.js → bicep-BC0ryoGM.js} +0 -0
  326. /package/dist/web/assets/{bird2-CuZhQzYR.js → bird2-C50V_A-0.js} +0 -0
  327. /package/dist/web/assets/{c-9bnDqE3g.js → c-Ddorhq0U.js} +0 -0
  328. /package/dist/web/assets/{c3-JMZTrOhA.js → c3-DdIf5r9E.js} +0 -0
  329. /package/dist/web/assets/{cadence-DsDNayJS.js → cadence-s9pd1Wng.js} +0 -0
  330. /package/dist/web/assets/{can-open-in-ppm-BJfpynya.js → can-open-in-ppm-C6hZV1OP.js} +0 -0
  331. /package/dist/web/assets/{catppuccin-frappe-DYIKXXBe.js → catppuccin-frappe-CzJNTyu-.js} +0 -0
  332. /package/dist/web/assets/{catppuccin-latte-IYYOnUJ8.js → catppuccin-latte-BNpt-m7s.js} +0 -0
  333. /package/dist/web/assets/{catppuccin-macchiato-BrJ_7mfS.js → catppuccin-macchiato-FYGkfsJ5.js} +0 -0
  334. /package/dist/web/assets/{catppuccin-mocha-gzSu7v7-.js → catppuccin-mocha-it2g5D1q.js} +0 -0
  335. /package/dist/web/assets/{clarity-DLzsC0Hb.js → clarity-BVFBTjMe.js} +0 -0
  336. /package/dist/web/assets/{clipboard-7fw0Shic.js → clipboard-BWznymz7.js} +0 -0
  337. /package/dist/web/assets/{clojure-DS1BvoTt.js → clojure-BSYvXMI8.js} +0 -0
  338. /package/dist/web/assets/{cmake-CDvsJwYp.js → cmake-DGEqH-Np.js} +0 -0
  339. /package/dist/web/assets/{codeowners-BjsYiflJ.js → codeowners-D8FCOxWg.js} +0 -0
  340. /package/dist/web/assets/{codeql-CG-7ARat.js → codeql-COV0eluG.js} +0 -0
  341. /package/dist/web/assets/{common-lisp-DSO1Lbh9.js → common-lisp-xHnZUlgt.js} +0 -0
  342. /package/dist/web/assets/{coq-DYdh3oP_.js → coq-Cs_2i3Xg.js} +0 -0
  343. /package/dist/web/assets/{csharp-DxEBz5s0.js → csharp-B1ONB4PT.js} +0 -0
  344. /package/dist/web/assets/{css-dal3Q-8C.js → css-BgGQbvrZ.js} +0 -0
  345. /package/dist/web/assets/{csv-CpqjMGiA.js → csv-CNHmQBzs.js} +0 -0
  346. /package/dist/web/assets/{csv-parser-3umBA4NY.js → csv-parser-DWR24J17.js} +0 -0
  347. /package/dist/web/assets/{cue-DinoJ_5k.js → cue-ZXAKzwIy.js} +0 -0
  348. /package/dist/web/assets/{cypher-DzxqmkUW.js → cypher-4PL5bmT5.js} +0 -0
  349. /package/dist/web/assets/{d-BxNwJuEC.js → d-CEsV3RT2.js} +0 -0
  350. /package/dist/web/assets/{dark-plus-Clmv3V-0.js → dark-plus-NtFnYSDz.js} +0 -0
  351. /package/dist/web/assets/{dart-BWVtXD35.js → dart-CKHn4mDf.js} +0 -0
  352. /package/dist/web/assets/{data-grid-types-B4n51Fck.js → data-grid-types-CxU8oo-j.js} +0 -0
  353. /package/dist/web/assets/{dax-DsImuWgJ.js → dax-Cmkj1I_M.js} +0 -0
  354. /package/dist/web/assets/{desktop-B0jJuG2r.js → desktop-DJLalvPQ.js} +0 -0
  355. /package/dist/web/assets/{diff-CWqAAxoE.js → diff-BnS2wpj6.js} +0 -0
  356. /package/dist/web/assets/{dist-C0eNNfbt.js → dist-CuIdRyUB.js} +0 -0
  357. /package/dist/web/assets/{dist-B-EXdSYE.js → dist-uYjs3Wv6.js} +0 -0
  358. /package/dist/web/assets/{docker-CYFCJdIL.js → docker-CovNy7ml.js} +0 -0
  359. /package/dist/web/assets/{dotenv-CKDUjURg.js → dotenv-Gh4w3I6B.js} +0 -0
  360. /package/dist/web/assets/{dracula-CSoPspHE.js → dracula-NF0tAHXl.js} +0 -0
  361. /package/dist/web/assets/{dracula-soft-BuFjw5L1.js → dracula-soft-Cj_qBurK.js} +0 -0
  362. /package/dist/web/assets/{dream-maker-B6PdDRZu.js → dream-maker-CRuZPcl7.js} +0 -0
  363. /package/dist/web/assets/{emacs-lisp-CMcXvD0G.js → emacs-lisp-BACmC9qj.js} +0 -0
  364. /package/dist/web/assets/{everforest-dark-cU8-wYzi.js → everforest-dark-Dd9av4QG.js} +0 -0
  365. /package/dist/web/assets/{everforest-light-KhorllqK.js → everforest-light-C-jsss-B.js} +0 -0
  366. /package/dist/web/assets/{external-link-B8BynhvY.js → external-link-B6Hl0U4X.js} +0 -0
  367. /package/dist/web/assets/{fennel-BCELZK87.js → fennel-BJA9XnL8.js} +0 -0
  368. /package/dist/web/assets/{file-exclamation-point-CZ4DiR75.js → file-exclamation-point-JzhTU2TX.js} +0 -0
  369. /package/dist/web/assets/{fish-BQc0y_jy.js → fish-CKlhNSc2.js} +0 -0
  370. /package/dist/web/assets/{fluent-BTINnwF6.js → fluent-wfglRk5D.js} +0 -0
  371. /package/dist/web/assets/{folder-symlink-B8XrtvQK.js → folder-symlink-Dx1hOk8X.js} +0 -0
  372. /package/dist/web/assets/{format-bytes-WVcFxJuT.js → format-bytes-utwHMTry.js} +0 -0
  373. /package/dist/web/assets/{fortran-free-form-BQim9wDz.js → fortran-free-form-BlXCdid3.js} +0 -0
  374. /package/dist/web/assets/{gdscript-03w5u8un.js → gdscript-BwAVG6Et.js} +0 -0
  375. /package/dist/web/assets/{gdshader-Dp2daNzU.js → gdshader-DPc_T7ib.js} +0 -0
  376. /package/dist/web/assets/{genie-CoNcjPiD.js → genie-B-zZSLCX.js} +0 -0
  377. /package/dist/web/assets/{gherkin-NrPOSPQL.js → gherkin-CJ0ajpGs.js} +0 -0
  378. /package/dist/web/assets/{github-dark-z4RE9ZqA.js → github-dark-Dz8ZkzSP.js} +0 -0
  379. /package/dist/web/assets/{github-dark-default-DOKw-1cI.js → github-dark-default-CNUYo_kp.js} +0 -0
  380. /package/dist/web/assets/{github-dark-dimmed-DJRNMkEA.js → github-dark-dimmed-241z_Ycx.js} +0 -0
  381. /package/dist/web/assets/{github-dark-high-contrast-Bj7jJ_Gs.js → github-dark-high-contrast-7r6yZZYH.js} +0 -0
  382. /package/dist/web/assets/{github-light-B4k_yMFu.js → github-light-C8gmZn-D.js} +0 -0
  383. /package/dist/web/assets/{github-light-default-BHc-Uzhs.js → github-light-default-GAEgq1eA.js} +0 -0
  384. /package/dist/web/assets/{github-light-high-contrast-CHisIywL.js → github-light-high-contrast-3z91352E.js} +0 -0
  385. /package/dist/web/assets/{gleam-CfbypZ4q.js → gleam-C7vxW5zd.js} +0 -0
  386. /package/dist/web/assets/{gn-YoAjyhJa.js → gn-CyJIXwHP.js} +0 -0
  387. /package/dist/web/assets/{gnuplot-xVkc1f1c.js → gnuplot-CqLbGgPd.js} +0 -0
  388. /package/dist/web/assets/{go-GPCwP3Gu.js → go-DdDcK9aX.js} +0 -0
  389. /package/dist/web/assets/{groovy-Crh4Yww9.js → groovy-UYHggEqH.js} +0 -0
  390. /package/dist/web/assets/{gruvbox-dark-hard-BluUFuDS.js → gruvbox-dark-hard-B4ierkJn.js} +0 -0
  391. /package/dist/web/assets/{gruvbox-dark-medium-Cxk1FWQp.js → gruvbox-dark-medium-DYwkDSZ6.js} +0 -0
  392. /package/dist/web/assets/{gruvbox-dark-soft-CHXMiFmj.js → gruvbox-dark-soft-CVL4P-Lz.js} +0 -0
  393. /package/dist/web/assets/{gruvbox-light-hard-B6eKPjAD.js → gruvbox-light-hard-BJ0LewUZ.js} +0 -0
  394. /package/dist/web/assets/{gruvbox-light-medium-pDwMm1ut.js → gruvbox-light-medium-CaqqjeGJ.js} +0 -0
  395. /package/dist/web/assets/{gruvbox-light-soft-Cxu6kHqK.js → gruvbox-light-soft-ByKMoVNr.js} +0 -0
  396. /package/dist/web/assets/{haskell-C6JKHjbK.js → haskell-BilYRVPp.js} +0 -0
  397. /package/dist/web/assets/{haxe-BuBNdF0a.js → haxe-Brs9Bj1s.js} +0 -0
  398. /package/dist/web/assets/{hcl-DZpDdGj2.js → hcl-ClrZpol-.js} +0 -0
  399. /package/dist/web/assets/{hjson-CBkhYRuo.js → hjson-9PvHnYMX.js} +0 -0
  400. /package/dist/web/assets/{hlsl-C5nMxnp9.js → hlsl-BbBk4vqV.js} +0 -0
  401. /package/dist/web/assets/{horizon-VAFvA6R6.js → horizon-E5Py0cny.js} +0 -0
  402. /package/dist/web/assets/{horizon-bright-BC0xn7T3.js → horizon-bright-DVCsNGEk.js} +0 -0
  403. /package/dist/web/assets/{houston-BxrUivmz.js → houston-JdFEax9Y.js} +0 -0
  404. /package/dist/web/assets/{hy-DLtOXoaH.js → hy-W_W3tLKT.js} +0 -0
  405. /package/dist/web/assets/{image-gallery-Gnf7TdCR.js → image-gallery-5IURd6db.js} +0 -0
  406. /package/dist/web/assets/{imba-BdpFXM2W.js → imba-CfAqJz7T.js} +0 -0
  407. /package/dist/web/assets/{ini-BInJO_T0.js → ini-C_3gvKWs.js} +0 -0
  408. /package/dist/web/assets/{java-Dxuddnkb.js → java-CKqtXZ-j.js} +0 -0
  409. /package/dist/web/assets/{javascript-CVamJfLY.js → javascript-C7gSEfae.js} +0 -0
  410. /package/dist/web/assets/{json-BLZ0ozYk.js → json-BoN6e11T.js} +0 -0
  411. /package/dist/web/assets/{json5-BnXzmMKs.js → json5-CgE7C8HW.js} +0 -0
  412. /package/dist/web/assets/{jsonc-CSgkSga7.js → jsonc-DjyoattY.js} +0 -0
  413. /package/dist/web/assets/{jsonl-CY5W2nF2.js → jsonl-lI82wGSw.js} +0 -0
  414. /package/dist/web/assets/{jsonnet-CEJYH3sF.js → jsonnet-CszEI8va.js} +0 -0
  415. /package/dist/web/assets/{jssm-CsJc7Uhk.js → jssm-CZXDlszb.js} +0 -0
  416. /package/dist/web/assets/{jsx-jCiRaHU7.js → jsx-BeRz0eEz.js} +0 -0
  417. /package/dist/web/assets/{kanagawa-dragon-DfoA_GxX.js → kanagawa-dragon-BOmnUiyd.js} +0 -0
  418. /package/dist/web/assets/{kanagawa-lotus-BOLYJpWd.js → kanagawa-lotus-ffU_P5w-.js} +0 -0
  419. /package/dist/web/assets/{kanagawa-wave-DfKlbT1G.js → kanagawa-wave-DKTJZrMu.js} +0 -0
  420. /package/dist/web/assets/{kdl-CmssIFTi.js → kdl-BWECJveH.js} +0 -0
  421. /package/dist/web/assets/{kotlin-BRsNgp6x.js → kotlin-DpVwBCSt.js} +0 -0
  422. /package/dist/web/assets/{kusto-T8X9jUab.js → kusto-BgVqjUZA.js} +0 -0
  423. /package/dist/web/assets/{laserwave-DkJMVJ8Z.js → laserwave-CEVCi8em.js} +0 -0
  424. /package/dist/web/assets/{lean-C65KGVPq.js → lean-CGyfRhLJ.js} +0 -0
  425. /package/dist/web/assets/{less-exIPdDlf.js → less-B0_H10Ku.js} +0 -0
  426. /package/dist/web/assets/{lib-mC1NZfuw.js → lib-hrC3bruJ.js} +0 -0
  427. /package/dist/web/assets/{libesm-DSutUtl1.js → libesm-DVpFmwJu.js} +0 -0
  428. /package/dist/web/assets/{light-plus-DiHUXAZt.js → light-plus-DqzMqPID.js} +0 -0
  429. /package/dist/web/assets/{llvm-CJBnEzFM.js → llvm-9ucnfNiF.js} +0 -0
  430. /package/dist/web/assets/{loader-circle-gSPVxfvH.js → loader-circle-Dpqcv0eS.js} +0 -0
  431. /package/dist/web/assets/{lock-Did-87UV.js → lock-DldbT31x.js} +0 -0
  432. /package/dist/web/assets/{log-DxMyUMuP.js → log-m6A2dOSj.js} +0 -0
  433. /package/dist/web/assets/{logo-CC9NKzca.js → logo-ByPcsC9P.js} +0 -0
  434. /package/dist/web/assets/{luau-Bw10I6zf.js → luau-DFCo8nBn.js} +0 -0
  435. /package/dist/web/assets/{make-C_6M90m6.js → make-C90w3MeA.js} +0 -0
  436. /package/dist/web/assets/{markdown-bR9_R-LF.js → markdown-DT0uywZG.js} +0 -0
  437. /package/dist/web/assets/{material-theme-BUx2Qujn.js → material-theme-BVUO93tJ.js} +0 -0
  438. /package/dist/web/assets/{material-theme-darker-CRJrPgKm.js → material-theme-darker-xXgGDwnF.js} +0 -0
  439. /package/dist/web/assets/{material-theme-lighter-CUAYppPa.js → material-theme-lighter-BjStPzWB.js} +0 -0
  440. /package/dist/web/assets/{material-theme-ocean-DuINF1fg.js → material-theme-ocean-wFmSiKjy.js} +0 -0
  441. /package/dist/web/assets/{material-theme-palenight-W6_icGCj.js → material-theme-palenight-e-0CtRoe.js} +0 -0
  442. /package/dist/web/assets/{matlab-rfqn_b3z.js → matlab-BNaNQAUH.js} +0 -0
  443. /package/dist/web/assets/{mdx-Bh1B_YOJ.js → mdx-DfpVWAqO.js} +0 -0
  444. /package/dist/web/assets/{mermaid-Dxf6Ya2k.js → mermaid-Bpq7wFCP.js} +0 -0
  445. /package/dist/web/assets/{min-dark-CivZ2L0h.js → min-dark-CL2Y0WH6.js} +0 -0
  446. /package/dist/web/assets/{min-light-B1Ykqtma.js → min-light-DOgr_IdP.js} +0 -0
  447. /package/dist/web/assets/{mipsasm-DynHaM0d.js → mipsasm-C9ie2zlB.js} +0 -0
  448. /package/dist/web/assets/{mojo-Clw5PbGH.js → mojo-Md2A2sJy.js} +0 -0
  449. /package/dist/web/assets/{monitor-Dv8vn_6O.js → monitor-4vWfqzuN.js} +0 -0
  450. /package/dist/web/assets/{monokai-DFIooFZT.js → monokai-nbXIEySB.js} +0 -0
  451. /package/dist/web/assets/{moonbit-DnBSZWGM.js → moonbit-CJ-T-s4A.js} +0 -0
  452. /package/dist/web/assets/{move-CCcL2PfL.js → move-DwEehLIK.js} +0 -0
  453. /package/dist/web/assets/{narrat-BDarg5Lk.js → narrat-CuF4sIKr.js} +0 -0
  454. /package/dist/web/assets/{nextflow-groovy-DrZOeCNW.js → nextflow-groovy-1dYhfPfq.js} +0 -0
  455. /package/dist/web/assets/{night-owl-BnZZCK7v.js → night-owl-gZS5mfzY.js} +0 -0
  456. /package/dist/web/assets/{night-owl-light-CEwBsVHS.js → night-owl-light-DDAster5.js} +0 -0
  457. /package/dist/web/assets/{nix-amCzOPMR.js → nix-BlbEYs2c.js} +0 -0
  458. /package/dist/web/assets/{nord-BIs2BC6q.js → nord-BRiPb8Vy.js} +0 -0
  459. /package/dist/web/assets/{nushell-Y02fo4yw.js → nushell-DgWicB1i.js} +0 -0
  460. /package/dist/web/assets/{objective-c-7xOZtWW1.js → objective-c-D8mPZ42Y.js} +0 -0
  461. /package/dist/web/assets/{objective-cpp-eVtjUvVB.js → objective-cpp-B0pNXzuj.js} +0 -0
  462. /package/dist/web/assets/{ocaml-Bi6ZwqG4.js → ocaml-lQd7aK5d.js} +0 -0
  463. /package/dist/web/assets/{odin-8xf1Y0S9.js → odin-CxtIOBjm.js} +0 -0
  464. /package/dist/web/assets/{one-dark-pro-hVv-_y_b.js → one-dark-pro-T_UFnWfe.js} +0 -0
  465. /package/dist/web/assets/{one-light-CJJ3MtHt.js → one-light-LBVqwyV-.js} +0 -0
  466. /package/dist/web/assets/{openscad-B2HcywLk.js → openscad-C5dnS6J7.js} +0 -0
  467. /package/dist/web/assets/{pascal-C7fllV_7.js → pascal-CmWCWQML.js} +0 -0
  468. /package/dist/web/assets/{pkl-qvLC1IWP.js → pkl-BWWOtDQk.js} +0 -0
  469. /package/dist/web/assets/{plastic-BT1wHZv2.js → plastic-ekhupckF.js} +0 -0
  470. /package/dist/web/assets/{play-4MZi1Axp.js → play-CpCAoQRZ.js} +0 -0
  471. /package/dist/web/assets/{plsql-1MsmdmJD.js → plsql-BPsDHXx4.js} +0 -0
  472. /package/dist/web/assets/{plus-BhMZqFsN.js → plus-BR0qewJI.js} +0 -0
  473. /package/dist/web/assets/{po-BigskytD.js → po-BX2FiuW2.js} +0 -0
  474. /package/dist/web/assets/{poimandres-DuklniF7.js → poimandres-DzXOdZcN.js} +0 -0
  475. /package/dist/web/assets/{polar-e2pjN_sp.js → polar-DDRXgJzG.js} +0 -0
  476. /package/dist/web/assets/{portal-container-context-Cm5HipFr.js → portal-container-context-a399Hd9F.js} +0 -0
  477. /package/dist/web/assets/{postcss-DtAeYjWw.js → postcss-BHW6XBhx.js} +0 -0
  478. /package/dist/web/assets/{powerquery-Cr2PtehI.js → powerquery-B89c2NBb.js} +0 -0
  479. /package/dist/web/assets/{powershell-BxFTGLJ4.js → powershell-Dvxrd3Tu.js} +0 -0
  480. /package/dist/web/assets/{prisma-COWJiwdZ.js → prisma-lMK8EFLU.js} +0 -0
  481. /package/dist/web/assets/{prolog-DFvGKRx6.js → prolog-D0YrtMG4.js} +0 -0
  482. /package/dist/web/assets/{proto-CcK80lFt.js → proto-J0mM25aw.js} +0 -0
  483. /package/dist/web/assets/{puppet-BF60bZ4F.js → puppet-CUT0xkhh.js} +0 -0
  484. /package/dist/web/assets/{purescript-zL_69MzU.js → purescript-CoJWSGRp.js} +0 -0
  485. /package/dist/web/assets/{python-CIwKhfb_.js → python-BBrmuz6P.js} +0 -0
  486. /package/dist/web/assets/{qmldir-CXNe04Lo.js → qmldir-CUtzs4Q7.js} +0 -0
  487. /package/dist/web/assets/{qss-C8lAWe6G.js → qss-Dm1RZ8aL.js} +0 -0
  488. /package/dist/web/assets/{r-CL5ZVL3_.js → r-BGP7qZTe.js} +0 -0
  489. /package/dist/web/assets/{racket-BBfh0uh0.js → racket-K_6U1lqJ.js} +0 -0
  490. /package/dist/web/assets/{raku-o6E8R5Q3.js → raku-bNSiZeW_.js} +0 -0
  491. /package/dist/web/assets/{react-CBTnT4sY.js → react-X4jUntEf.js} +0 -0
  492. /package/dist/web/assets/{red-GomRF2Xp.js → red-uVeIEF0f.js} +0 -0
  493. /package/dist/web/assets/{refresh-cw-B06yaBrW.js → refresh-cw-B9iv7csb.js} +0 -0
  494. /package/dist/web/assets/{reg-_ffDIvx9.js → reg-DSm9GWjh.js} +0 -0
  495. /package/dist/web/assets/{regexp-Bl5sF65W.js → regexp-CljRm_j_.js} +0 -0
  496. /package/dist/web/assets/{rel-DgMdqDuj.js → rel-d3JcopZg.js} +0 -0
  497. /package/dist/web/assets/{resolve-theme-CRt_7wpd.js → resolve-theme-DLKmm-lE.js} +0 -0
  498. /package/dist/web/assets/{riscv-8b3-Ye4X.js → riscv-CJmaELjT.js} +0 -0
  499. /package/dist/web/assets/{ron-DrmU1KHP.js → ron-D0g9B1rW.js} +0 -0
  500. /package/dist/web/assets/{rose-pine-C_-E2tF1.js → rose-pine-DOrD2BSH.js} +0 -0
  501. /package/dist/web/assets/{rose-pine-dawn-DEfhNlFH.js → rose-pine-dawn-CzZQ-LCF.js} +0 -0
  502. /package/dist/web/assets/{rose-pine-moon-BdvX3gdn.js → rose-pine-moon-CdmsYp7C.js} +0 -0
  503. /package/dist/web/assets/{rosmsg-B6GhGpd5.js → rosmsg-CmDth6pO.js} +0 -0
  504. /package/dist/web/assets/{rotate-ccw-COXu_9oj.js → rotate-ccw-DnuPzJRE.js} +0 -0
  505. /package/dist/web/assets/{rust-DmC97M1-.js → rust-DGdP6jiR.js} +0 -0
  506. /package/dist/web/assets/{sass-B0tJOHLM.js → sass-Dkgt2FKZ.js} +0 -0
  507. /package/dist/web/assets/{save-CtIGOjmx.js → save-DxonD4y1.js} +0 -0
  508. /package/dist/web/assets/{scala-n8zaFOXv.js → scala-BQLTON6C.js} +0 -0
  509. /package/dist/web/assets/{scheme-BKD9ztBU.js → scheme-DKlxUGCi.js} +0 -0
  510. /package/dist/web/assets/{sdbl-D8gB5-rS.js → sdbl-C3iZJQeZ.js} +0 -0
  511. /package/dist/web/assets/{send-BlyNJAzy.js → send-B-t5Mz26.js} +0 -0
  512. /package/dist/web/assets/{shallow-DYo6goJ4.js → shallow-jwPzEunk.js} +0 -0
  513. /package/dist/web/assets/{shellscript-BfXlf_Ao.js → shellscript-BRu-ZQAx.js} +0 -0
  514. /package/dist/web/assets/{shield-check-B0iGUiOK.js → shield-check-CdTiK1j2.js} +0 -0
  515. /package/dist/web/assets/{shield-off-BNLWBhcq.js → shield-off-DuetkXKM.js} +0 -0
  516. /package/dist/web/assets/{slack-dark-CylEddse.js → slack-dark-qkm_u2zz.js} +0 -0
  517. /package/dist/web/assets/{slack-ochin-w8wg8vuC.js → slack-ochin-BP8_8e4m.js} +0 -0
  518. /package/dist/web/assets/{smalltalk-hiAXA64u.js → smalltalk-Bnz4gt07.js} +0 -0
  519. /package/dist/web/assets/{snazzy-light-BwlV3Xty.js → snazzy-light-fbPZg5io.js} +0 -0
  520. /package/dist/web/assets/{solarized-dark-vmNGdykz.js → solarized-dark-CM0vTRoi.js} +0 -0
  521. /package/dist/web/assets/{solarized-light-DGv_0JXq.js → solarized-light-C4_W9mvs.js} +0 -0
  522. /package/dist/web/assets/{solidity-Y7QSH0B1.js → solidity-Bz6v1PoD.js} +0 -0
  523. /package/dist/web/assets/{splunk-BtgpLjHi.js → splunk-DyNxu-0i.js} +0 -0
  524. /package/dist/web/assets/{sql-CRHYhOia.js → sql-DXRL1gw1.js} +0 -0
  525. /package/dist/web/assets/{ssh-config-DOqSs3au.js → ssh-config-2z1QHuOG.js} +0 -0
  526. /package/dist/web/assets/{stylus-crCAEaBT.js → stylus-D4I-VaeY.js} +0 -0
  527. /package/dist/web/assets/{swift-eE3rT7TC.js → swift-C_2G-tIc.js} +0 -0
  528. /package/dist/web/assets/{synthwave-84-BWy14LBX.js → synthwave-84-osBjChRP.js} +0 -0
  529. /package/dist/web/assets/{system-verilog-DC0-DZM4.js → system-verilog-zQP2qPNU.js} +0 -0
  530. /package/dist/web/assets/{systemd-to5p6N8I.js → systemd-UGfjRSBM.js} +0 -0
  531. /package/dist/web/assets/{table-Bbc783ko.js → table-FLU8ei8b.js} +0 -0
  532. /package/dist/web/assets/{talonscript-DoTcU8wm.js → talonscript-C1XrFPwo.js} +0 -0
  533. /package/dist/web/assets/{tasl-cNZvUs95.js → tasl-B5raVn43.js} +0 -0
  534. /package/dist/web/assets/{tcl-mZE8sZH8.js → tcl-3-vbeMIc.js} +0 -0
  535. /package/dist/web/assets/{terraform-CqHPpIfu.js → terraform-a8VXZDm9.js} +0 -0
  536. /package/dist/web/assets/{text-wrap-CBT4Gxtg.js → text-wrap-B05VGRa-.js} +0 -0
  537. /package/dist/web/assets/{tokyo-night-Tp2rTIZd.js → tokyo-night-4SRhf-Hz.js} +0 -0
  538. /package/dist/web/assets/{toml-CU_vscgn.js → toml-B4N0i8wT.js} +0 -0
  539. /package/dist/web/assets/{trash-2-Bk4yYlck.js → trash-2-CTXlTflF.js} +0 -0
  540. /package/dist/web/assets/{triangle-alert-B8EYecpe.js → triangle-alert-BRq0VAfd.js} +0 -0
  541. /package/dist/web/assets/{tsv-ik7iFB9D.js → tsv-DA5k9Lvw.js} +0 -0
  542. /package/dist/web/assets/{tsx-BMvvMKiv.js → tsx-D9SZ8owC.js} +0 -0
  543. /package/dist/web/assets/{turtle-DBqKCq9o.js → turtle-BTHTvyY_.js} +0 -0
  544. /package/dist/web/assets/{typescript-DXJDmMZZ.js → typescript-DYzV9p5L.js} +0 -0
  545. /package/dist/web/assets/{typespec-CBseCMtZ.js → typespec-BC15eh7F.js} +0 -0
  546. /package/dist/web/assets/{typst-4oY25U9y.js → typst-BJUqzA6D.js} +0 -0
  547. /package/dist/web/assets/{use-is-mobile-D1weanbj.js → use-is-mobile-DFWViA4S.js} +0 -0
  548. /package/dist/web/assets/{utils-BNRH507E.js → utils-C9OjCrou.js} +0 -0
  549. /package/dist/web/assets/{v-BXaZ_1oy.js → v-50Lps6oA.js} +0 -0
  550. /package/dist/web/assets/{vala-BImWJE7z.js → vala-8wiUvvrD.js} +0 -0
  551. /package/dist/web/assets/{vb-CLfj2_sy.js → vb-BlgPLt6B.js} +0 -0
  552. /package/dist/web/assets/{vendor-xterm-_yyu5n32.js → vendor-xterm-BZOrwF_F.js} +0 -0
  553. /package/dist/web/assets/{verilog-BWa8Ed2b.js → verilog-CD9pVW5p.js} +0 -0
  554. /package/dist/web/assets/{vesper-BGj1cg6m.js → vesper-BrivW4PT.js} +0 -0
  555. /package/dist/web/assets/{vhdl-mByK7mlx.js → vhdl-Bunxfa7N.js} +0 -0
  556. /package/dist/web/assets/{viml-CugmlW5g.js → viml-DndUb2qM.js} +0 -0
  557. /package/dist/web/assets/{vitesse-black-vPdpy9TL.js → vitesse-black-BurZSkxW.js} +0 -0
  558. /package/dist/web/assets/{vitesse-dark-BuQK3dUh.js → vitesse-dark-8UShspzC.js} +0 -0
  559. /package/dist/web/assets/{vitesse-light-B3nz1XL9.js → vitesse-light-DghCAZ90.js} +0 -0
  560. /package/dist/web/assets/{vyper-DH_GTIHG.js → vyper-b-Clr-8u.js} +0 -0
  561. /package/dist/web/assets/{wasm-DfVbVJlW.js → wasm-C5v2T06Y.js} +0 -0
  562. /package/dist/web/assets/{wasm-C-LF9GjX.js → wasm-CpcKStkw.js} +0 -0
  563. /package/dist/web/assets/{wenyan-CzQkHyHx.js → wenyan-Dz2srLZR.js} +0 -0
  564. /package/dist/web/assets/{wgsl-Du_M1DW9.js → wgsl-CeD1-dRb.js} +0 -0
  565. /package/dist/web/assets/{wikitext-BWN_w5S7.js → wikitext-CyeS-9FH.js} +0 -0
  566. /package/dist/web/assets/{wit-BR13v5HD.js → wit-Kr-q-qBQ.js} +0 -0
  567. /package/dist/web/assets/{wolfram-BoXxp_Xl.js → wolfram-BKm8bmln.js} +0 -0
  568. /package/dist/web/assets/{x-nhriOX9o.js → x-YDu4m2Iq.js} +0 -0
  569. /package/dist/web/assets/{yaml-DpR_EF-d.js → yaml-2XSxomjM.js} +0 -0
  570. /package/dist/web/assets/{zenscript-C-EdUtUd.js → zenscript-CKIS34vf.js} +0 -0
  571. /package/dist/web/assets/{zig-BD4eA5nU.js → zig-DdHGNllX.js} +0 -0
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Runtime resolver: turns the persisted `tunnel` config row into the shape the
3
+ * supervisor and UI actually consume. This is where "incomplete" data degrades
4
+ * to quick — storage (`sanitizeConfig`) never makes that judgment itself, so
5
+ * `/disable`'s documented "keep the config so Retry works" contract survives a
6
+ * config round-trip.
7
+ */
8
+
9
+ export type TunnelMode = "quick" | "named";
10
+
11
+ export interface ResolvedTunnelConfig {
12
+ mode: TunnelMode;
13
+ hostname: string | null;
14
+ tunnelName: string | null;
15
+ token: string | null;
16
+ zoneID: string | null;
17
+ accountID: string | null;
18
+ dismissed: boolean;
19
+ }
20
+
21
+ const QUICK: Omit<ResolvedTunnelConfig, "dismissed"> = {
22
+ mode: "quick", hostname: null, tunnelName: null, token: null, zoneID: null, accountID: null,
23
+ };
24
+
25
+ function nonEmptyString(v: unknown): v is string {
26
+ return typeof v === "string" && v.length > 0;
27
+ }
28
+
29
+ export function resolveTunnelConfig(raw: unknown): ResolvedTunnelConfig {
30
+ let obj: Record<string, unknown> | null = null;
31
+ if (typeof raw === "string") {
32
+ try { obj = JSON.parse(raw); } catch { obj = null; }
33
+ } else if (raw && typeof raw === "object") {
34
+ obj = raw as Record<string, unknown>;
35
+ }
36
+
37
+ const dismissed = obj != null && typeof obj.dismissed === "boolean" ? obj.dismissed : false;
38
+
39
+ // Named mode requires BOTH the stored mode flag AND all four identity fields —
40
+ // checking fields alone would resurrect a tunnel the user explicitly disabled,
41
+ // since /disable intentionally keeps namedTunnel* fields around for Retry.
42
+ const named = obj != null && obj.mode === "named" &&
43
+ nonEmptyString(obj.namedTunnelToken) &&
44
+ nonEmptyString(obj.namedTunnelHostname) &&
45
+ nonEmptyString(obj.zoneID) &&
46
+ nonEmptyString(obj.accountID);
47
+
48
+ if (!named || obj == null) {
49
+ return { ...QUICK, dismissed };
50
+ }
51
+
52
+ return {
53
+ mode: "named",
54
+ hostname: obj.namedTunnelHostname as string,
55
+ tunnelName: nonEmptyString(obj.namedTunnelName) ? obj.namedTunnelName : null,
56
+ token: obj.namedTunnelToken as string,
57
+ zoneID: obj.zoneID as string,
58
+ accountID: obj.accountID as string,
59
+ dismissed,
60
+ };
61
+ }
62
+
63
+ /** `abcdef...` — first 6 chars + ellipsis; never the full secret. */
64
+ export function maskToken(token: string | null): string | null {
65
+ if (token == null) return null;
66
+ return `${token.slice(0, 6)}...`;
67
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Pure decision function for the named-tunnel probe's "restart once, then warn
3
+ * and stop" state machine. Kept separate from `supervisor.ts` (which owns the
4
+ * actual kill/respawn/status-write side effects) so the transition logic is
5
+ * unit-testable without spawning a real cloudflared process or timers.
6
+ *
7
+ * The bug this exists to prevent: resetting `restartAttempted` anywhere other
8
+ * than a confirmed-healthy observation lets a dark hostname (e.g. the CNAME
9
+ * deleted in the dashboard) restart the connector every threshold window
10
+ * forever, because the RESTART itself always "succeeds" (cloudflared happily
11
+ * reconnects to Cloudflare's edge) even though the DNS route it needs is gone —
12
+ * a spawn success is not proof the hostname is reachable, only the probe's own
13
+ * fetch against the public URL is.
14
+ */
15
+
16
+ export interface NamedProbeState {
17
+ /** Consecutive unhealthy probe cycles observed since the last reset. */
18
+ failCount: number;
19
+ /** Whether the one allowed restart-and-hope has already been used since the
20
+ * last confirmed-healthy observation. */
21
+ restartAttempted: boolean;
22
+ }
23
+
24
+ export type NamedProbeAction =
25
+ | { type: "healthy" }
26
+ /** Counting a failure. `warnEarly` marks the tick that should surface a
27
+ * soft "checking the connection" notice without touching the connector. */
28
+ | { type: "watch"; warnEarly: boolean }
29
+ | { type: "restart-once" }
30
+ | { type: "warn-and-stop" };
31
+
32
+ export interface NamedProbeDecision {
33
+ action: NamedProbeAction;
34
+ nextState: NamedProbeState;
35
+ }
36
+
37
+ /**
38
+ * Is the thing answering the public hostname *our* server? `publicId` is the
39
+ * `instanceId` the hostname returned, `localId` the one our loopback server
40
+ * returned; either may be missing.
41
+ *
42
+ * A deleted CNAME does not make the hostname go dark — it falls back to the
43
+ * zone's wildcard record and some unrelated host answers 200 with a body that
44
+ * has no `instanceId`. If we know our own identity and the public answer lacks
45
+ * one (or differs), the hostname is not ours. Only when our own server has no
46
+ * identity (a build that predates `instanceId`) do we accept bare reachability.
47
+ */
48
+ export function publicHostnameIsOurs(publicId: unknown, localId: unknown): boolean {
49
+ if (typeof localId !== "string") return true;
50
+ return typeof publicId === "string" && publicId === localId;
51
+ }
52
+
53
+ /**
54
+ * Decide the next action for one probe tick.
55
+ *
56
+ * - `healthy` → both counters reset to a clean slate (the only place
57
+ * `restartAttempted` ever goes back to `false`).
58
+ * - unhealthy below `threshold` → `watch`. Acting on a dark hostname is
59
+ * deliberately slow (a flaky minute must not kill a working connector), but
60
+ * *saying nothing* for that long is a different decision: `watch` carries
61
+ * `warnEarly` on the tick that crosses `earlyWarnAt`, so the UI can show
62
+ * "checking the connection" while the restart budget is still untouched.
63
+ * - unhealthy at/above `threshold`, first time → `restart-once` (kill +
64
+ * respawn the connector; arms `restartAttempted` so this never repeats
65
+ * until a healthy observation clears it).
66
+ * - unhealthy at/above `threshold`, restart already attempted → `warn-and-stop`
67
+ * (surface the warning; never kill again — the pinned URL stays put).
68
+ */
69
+ export function decideNamedProbeAction(
70
+ healthy: boolean,
71
+ state: NamedProbeState,
72
+ threshold: number,
73
+ earlyWarnAt = 2,
74
+ ): NamedProbeDecision {
75
+ if (healthy) {
76
+ return { action: { type: "healthy" }, nextState: { failCount: 0, restartAttempted: false } };
77
+ }
78
+
79
+ const failCount = state.failCount + 1;
80
+ if (failCount < threshold) {
81
+ // Only on the crossing tick: re-writing the same warning every 30s would
82
+ // rewrite status.json forever for no new information.
83
+ const warnEarly = failCount === earlyWarnAt;
84
+ return {
85
+ action: { type: "watch", warnEarly },
86
+ nextState: { failCount, restartAttempted: state.restartAttempted },
87
+ };
88
+ }
89
+
90
+ if (!state.restartAttempted) {
91
+ return { action: { type: "restart-once" }, nextState: { failCount: 0, restartAttempted: true } };
92
+ }
93
+
94
+ return { action: { type: "warn-and-stop" }, nextState: { failCount: 0, restartAttempted: true } };
95
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Offset-anchored readiness reader for cloudflared's stderr log file.
3
+ *
4
+ * A quick or named tunnel is only "ready" once cloudflared writes a specific
5
+ * line to its log. The caller truncates that log before every spawn, but the
6
+ * truncation is best-effort and silently fails on Windows while a previous
7
+ * detached cloudflared process still holds the file open — so a scan of the
8
+ * WHOLE file can match a line left over from a dead connector and declare it
9
+ * "ready" in well under 200ms. Anchoring every read to a byte offset captured
10
+ * right after the truncate attempt makes that impossible: only bytes appended
11
+ * by THIS process generation are ever considered.
12
+ */
13
+ import { existsSync, readFileSync } from "node:fs";
14
+
15
+ export interface WaitForLogLineOpts {
16
+ /** Only bytes at or after this offset are scanned — excludes any content
17
+ * left behind by a previous process generation. */
18
+ fromByteOffset: number;
19
+ timeoutMs: number;
20
+ /** Poll-time check for whether the watched process has already exited. */
21
+ getExitCode: () => number | null;
22
+ pollIntervalMs?: number;
23
+ }
24
+
25
+ /**
26
+ * Poll `path` until `regex` matches within the bytes appended after
27
+ * `fromByteOffset`, the watched process exits, or `timeoutMs` elapses.
28
+ *
29
+ * Reads the file as a Buffer and slices by byte offset (not string index) so
30
+ * a multi-byte UTF-8 character straddling the offset is never corrupted —
31
+ * `toString("utf8")` re-decodes the whole remaining tail on every poll, so a
32
+ * boundary can only delay a match by one poll interval, never corrupt it.
33
+ */
34
+ export async function waitForLogLine(
35
+ path: string,
36
+ regex: RegExp,
37
+ opts: WaitForLogLineOpts,
38
+ ): Promise<string> {
39
+ const deadline = Date.now() + opts.timeoutMs;
40
+ const pollMs = opts.pollIntervalMs ?? 200;
41
+
42
+ while (Date.now() < deadline) {
43
+ if (existsSync(path)) {
44
+ try {
45
+ const buf = readFileSync(path);
46
+ const tail = buf.subarray(opts.fromByteOffset).toString("utf8");
47
+ const match = tail.match(regex);
48
+ if (match) return match[0];
49
+ } catch {
50
+ // Transient read race (file replaced mid-poll) — retry next tick.
51
+ }
52
+ }
53
+ if (opts.getExitCode() !== null) {
54
+ throw new Error(`cloudflared exited without matching ${regex}`);
55
+ }
56
+ await Bun.sleep(pollMs);
57
+ }
58
+ throw new Error(`readiness timeout (${opts.timeoutMs}ms)`);
59
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Runtime seam between "what's persisted" (named-tunnel-config.ts) and "what
3
+ * to actually spawn" (cloudflared.service.ts / named-tunnel-args.ts). Kept out
4
+ * of supervisor.ts (already large) and out of phase 1b's config/argv modules
5
+ * so neither grows to own the other's concern.
6
+ */
7
+ import { getConfigValue } from "../db.service.ts";
8
+ import { resolveTunnelConfig, type ResolvedTunnelConfig, type TunnelMode } from "./named-tunnel-config.ts";
9
+ import { getQuickTunnelArgs } from "../cloudflared.service.ts";
10
+ import { namedRunArgs } from "./named-tunnel-args.ts";
11
+
12
+ /**
13
+ * Fresh (non-cached) read of the persisted tunnel config.
14
+ *
15
+ * The supervisor runs in its own process with its own `configService`
16
+ * singleton, which never observes writes the server process makes to
17
+ * SQLite — so every caller here must go through `getConfigValue` (a raw DB
18
+ * read), never `configService.get`, or the supervisor would spawn/adopt
19
+ * against a stale mode forever.
20
+ */
21
+ export async function readTunnelConfigFresh(): Promise<ResolvedTunnelConfig> {
22
+ return resolveTunnelConfig(getConfigValue("tunnel"));
23
+ }
24
+
25
+ export interface TunnelSpawnPlan {
26
+ mode: TunnelMode;
27
+ args: string[];
28
+ }
29
+
30
+ /**
31
+ * Pick the argv for the next tunnel spawn attempt from a resolved config.
32
+ * When `config.mode` is not `"named"` (or the token is somehow absent —
33
+ * `resolveTunnelConfig` already guarantees this can't happen for a config row
34
+ * with `mode: "named"`, this is belt-and-suspenders), the output is exactly
35
+ * `getQuickTunnelArgs(port)` — byte-identical to today's quick-only argv.
36
+ */
37
+ export function chooseTunnelSpawn(config: ResolvedTunnelConfig, port: number): TunnelSpawnPlan {
38
+ if (config.mode === "named" && config.token) {
39
+ return { mode: "named", args: namedRunArgs(config.token, port) };
40
+ }
41
+ return { mode: "quick", args: getQuickTunnelArgs(port) };
42
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Detached confirmation for a named-tunnel setup.
3
+ *
4
+ * Once the supervisor reload has been sent, `runSetup` returns to the caller
5
+ * immediately (a 45s status.json poll must never hold the HTTP response —
6
+ * see the setup service's own comment) and this module polls status.json in
7
+ * the background, broadcasting the final outcome over `/ws/global`.
8
+ *
9
+ * WHY a generation stamp: `setupInFlight`'s synchronous 409 lock is already
10
+ * released by the time this runs, and `isConfirmationRunning` only extends
11
+ * the 409 to a *same-hostname* retry (see the setup service) — a request for
12
+ * a *different* hostname is allowed straight through and immediately
13
+ * supersedes whatever confirmation was still polling. Without a generation
14
+ * check, that superseded confirmer would still broadcast its own result
15
+ * (`setup_done` or `setup_pending`) 1s-to-45s later, carrying the *old*
16
+ * hostname and overwriting the correct, newer result in every connected
17
+ * client. Each confirmation run is stamped with a monotonic generation, and
18
+ * a confirmer checks it is still the current one immediately before
19
+ * broadcasting — a superseded confirmer exits silently instead.
20
+ */
21
+ import { readStatus } from "../supervisor-state.ts";
22
+ import { broadcastGlobalEvent } from "../../server/ws/global.ts";
23
+
24
+ const CONFIRM_POLL_BUDGET_MS = 45_000;
25
+ const CONFIRM_POLL_INTERVAL_MS = 1_000;
26
+
27
+ let generation = 0;
28
+ /** The hostname + deadline of the confirmation currently allowed to broadcast. */
29
+ let current: { hostname: string; deadline: number } | null = null;
30
+
31
+ /**
32
+ * True while a background confirmation is still running for `hostname`.
33
+ * Bounded by the same poll budget the confirmer itself uses, so a wedged
34
+ * confirmation can never lock a hostname out of retry forever. A different
35
+ * hostname's confirmation immediately closes this window (superseding is
36
+ * unconditional, not per-hostname), matching "a different hostname proceeds
37
+ * and takes over" below.
38
+ */
39
+ export function isConfirmationRunning(hostname: string): boolean {
40
+ return current !== null && current.hostname === hostname && Date.now() < current.deadline;
41
+ }
42
+
43
+ async function pollForConfirmation(hostname: string, budgetMs: number, intervalMs: number): Promise<boolean> {
44
+ const deadline = Date.now() + budgetMs;
45
+ while (Date.now() < deadline) {
46
+ const status = readStatus();
47
+ if (status.tunnelMode === "named" && status.shareUrl === `https://${hostname}`) return true;
48
+ await Bun.sleep(intervalMs);
49
+ }
50
+ return false;
51
+ }
52
+
53
+ /**
54
+ * Fire-and-forget: polls status.json and broadcasts the final outcome.
55
+ * Never throws into the caller. Silently drops its own broadcast if a newer
56
+ * setup (any hostname) has started since — see the module docstring.
57
+ * `opts` exists only so tests can shrink the poll budget/interval; production
58
+ * always uses the real 45s/1s constants.
59
+ */
60
+ export function confirmReloadInBackground(
61
+ hostname: string,
62
+ opts: { pollBudgetMs?: number; pollIntervalMs?: number } = {},
63
+ ): void {
64
+ const budgetMs = opts.pollBudgetMs ?? CONFIRM_POLL_BUDGET_MS;
65
+ const intervalMs = opts.pollIntervalMs ?? CONFIRM_POLL_INTERVAL_MS;
66
+ const myGeneration = ++generation;
67
+ current = { hostname, deadline: Date.now() + budgetMs };
68
+ const isSuperseded = () => generation !== myGeneration;
69
+
70
+ pollForConfirmation(hostname, budgetMs, intervalMs)
71
+ .then((confirmed) => {
72
+ if (isSuperseded()) return;
73
+ if (confirmed) {
74
+ broadcastGlobalEvent({ type: "tunnel:setup_done", hostname });
75
+ return;
76
+ }
77
+ const latest = readStatus();
78
+ const warning = typeof latest.tunnelWarning === "string" ? latest.tunnelWarning : null;
79
+ broadcastGlobalEvent({
80
+ type: "tunnel:setup_pending",
81
+ hostname,
82
+ message: warning ?? "setup saved but the supervisor has not confirmed it yet — check again shortly",
83
+ });
84
+ })
85
+ .catch(() => {
86
+ if (isSuperseded()) return;
87
+ broadcastGlobalEvent({
88
+ type: "tunnel:setup_pending",
89
+ hostname,
90
+ message: "confirmation check failed — check status again shortly",
91
+ });
92
+ })
93
+ .finally(() => {
94
+ // Release the 409 window once this generation settles — but only if
95
+ // nothing newer has already taken `current` over.
96
+ if (!isSuperseded() && current) current.deadline = 0;
97
+ });
98
+ }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Zone → precheck → create → route → token → persist → confirm.
3
+ *
4
+ * The DNS collision precheck runs *before* `tunnel create`, so "is this
5
+ * record already ours" is answered via the Cloudflare API's own tunnel
6
+ * lookup-by-name (an idempotent retry has an existing tunnel; a first-time
7
+ * setup does not) rather than by depending on `create` having already run.
8
+ */
9
+ import { readOriginCertState } from "./cloudflared-cert.ts";
10
+ import { fetchZoneName } from "./cloudflare-zone-api.ts";
11
+ import { fetchDnsRecords, fetchTunnelIdByName } from "./cloudflare-dns-api.ts";
12
+ import { proposeHostname, validateHostname } from "./hostname-rules.ts";
13
+ import { createTunnelArgs, routeDnsArgs, tunnelTokenArgs, tunnelNameForHost } from "./named-tunnel-args.ts";
14
+ import { runCloudflared } from "./cloudflared-exec.ts";
15
+ import { configService } from "../config.service.ts";
16
+ import { requestTunnelReload, readStatus } from "../supervisor-state.ts";
17
+ import { broadcastGlobalEvent } from "../../server/ws/global.ts";
18
+ import { pinsMatch } from "./cloudflared-login-helpers.ts";
19
+ import { confirmReloadInBackground, isConfirmationRunning } from "./named-tunnel-setup-confirm.ts";
20
+
21
+ export type CertState = "none" | "invalid" | "ok" | "mismatch";
22
+
23
+ /** Thrown by the flow below; routes map `.status` straight to the HTTP response. */
24
+ export class SetupError extends Error {
25
+ status: number;
26
+ constructor(status: number, message: string) {
27
+ super(message);
28
+ this.status = status;
29
+ }
30
+ }
31
+
32
+ // `cloudflared tunnel token` prints one line of standard base64 (with `+`, `/`
33
+ // and `=` padding — a real token is ~180 chars ending in `=`), not URL-safe
34
+ // base64, so the alphabet must include those three or every real token is
35
+ // rejected as "unexpected output".
36
+ const TOKEN_SHAPE = /^[A-Za-z0-9+/=._-]{100,}$/;
37
+ const RELOAD_RETRY_DELAY_MS = 2_000;
38
+
39
+ /** Module-level in-flight guard — two concurrent setups would race `route dns`. */
40
+ let setupInFlight = false;
41
+
42
+ /**
43
+ * Read-mostly cert classification for `/status` — never leaks the token.
44
+ * `"mismatch"` is a pure pin comparison (no network call): the parsed cert's
45
+ * zoneID/accountID differ from whatever `tunnel` config already pinned, i.e.
46
+ * cloudflared logged into a *different* Cloudflare account than the one this
47
+ * machine's named tunnel was set up under.
48
+ */
49
+ export function currentCertState(): CertState {
50
+ const state = readOriginCertState();
51
+ if (state.kind === "absent") return "none";
52
+ if (state.kind === "unparseable") return "invalid";
53
+ return pinsMatch(state.cert) ? "ok" : "mismatch";
54
+ }
55
+
56
+ export async function readZoneInfo(): Promise<{ zone: string; zoneID: string; accountID: string; proposedHostname: string }> {
57
+ const certState = readOriginCertState();
58
+ if (certState.kind !== "parsed") throw new SetupError(400, "Not logged in to Cloudflare");
59
+ if (!pinsMatch(certState.cert)) {
60
+ throw new SetupError(400, "cert belongs to a different Cloudflare account — log in again");
61
+ }
62
+ const zone = await fetchZoneName(certState.cert.zoneID, certState.cert.apiToken);
63
+ return { zone, zoneID: certState.cert.zoneID, accountID: certState.cert.accountID, proposedHostname: proposeHostname(zone) };
64
+ }
65
+
66
+ export async function disableNamedTunnel(): Promise<void> {
67
+ const current = configService.get("tunnel");
68
+ configService.set("tunnel", { ...current, mode: "quick" });
69
+ requestTunnelReload();
70
+ }
71
+
72
+ export type SetupOutcome =
73
+ | { ok: true; hostname: string; tunnelName: string }
74
+ | { ok: "pending"; hostname: string; tunnelName: string; message: string };
75
+
76
+ /**
77
+ * The synchronous `setupInFlight` lock only covers the create/route/token
78
+ * cycle — once the supervisor reload is sent, confirmation continues in the
79
+ * background (`named-tunnel-setup-confirm.ts`) and this function has already
80
+ * returned. `isConfirmationRunning` extends the 409 specifically to a retry
81
+ * for the *same* hostname during that bounded (<=45s) window, so mashing
82
+ * "Set up" doesn't kick off a second, redundant create/route/token cycle for
83
+ * a request that is already in flight. A *different* hostname is still let
84
+ * through immediately and supersedes the earlier confirmer — see the confirm
85
+ * module for how the stale one is silenced instead of broadcasting late.
86
+ */
87
+ export async function runSetup(hostname: string): Promise<SetupOutcome> {
88
+ if (setupInFlight) throw new SetupError(409, "a setup is already running");
89
+ if (isConfirmationRunning(hostname)) {
90
+ throw new SetupError(409, "a setup for this hostname is already confirming — check again shortly");
91
+ }
92
+ setupInFlight = true;
93
+ try {
94
+ return await runSetupInner(hostname);
95
+ } finally {
96
+ setupInFlight = false;
97
+ }
98
+ }
99
+
100
+ async function runSetupInner(hostname: string): Promise<SetupOutcome> {
101
+ broadcastGlobalEvent({ type: "tunnel:setup_step", step: "zone", message: "reading Cloudflare zone" });
102
+ const certState = readOriginCertState();
103
+ if (certState.kind !== "parsed") throw new SetupError(400, "Not logged in to Cloudflare");
104
+ const { zoneID, accountID, apiToken } = certState.cert;
105
+
106
+ const zone = await fetchZoneName(zoneID, apiToken);
107
+ const check = validateHostname(hostname, zone);
108
+ if (!check.ok) throw new SetupError(400, check.reason);
109
+
110
+ const tunnelName = tunnelNameForHost();
111
+
112
+ broadcastGlobalEvent({ type: "tunnel:setup_step", step: "precheck", message: "checking for a DNS collision" });
113
+ const existingId = await fetchTunnelIdByName(accountID, apiToken, tunnelName);
114
+ const records = await fetchDnsRecords(zoneID, apiToken, hostname);
115
+ let overwrite = false;
116
+ if (records.length > 0) {
117
+ const ownTarget = existingId ? `${existingId}.cfargotunnel.com` : null;
118
+ const isOwn = ownTarget != null && records.some((r) => r.content === ownTarget);
119
+ if (!isOwn) throw new SetupError(400, "that name already points somewhere else — pick another prefix");
120
+ overwrite = true;
121
+ }
122
+
123
+ broadcastGlobalEvent({ type: "tunnel:setup_step", step: "create", message: "creating tunnel" });
124
+ const create = await runCloudflared(createTunnelArgs(tunnelName));
125
+ if (create.code !== 0 && !/already exists/i.test(create.stderr)) {
126
+ throw new SetupError(500, `cloudflared tunnel create failed: ${(create.stderr || create.stdout).trim()}`);
127
+ }
128
+
129
+ broadcastGlobalEvent({ type: "tunnel:setup_step", step: "route", message: "routing DNS" });
130
+ const route = await runCloudflared(routeDnsArgs(tunnelName, hostname, overwrite));
131
+ if (route.code !== 0) {
132
+ throw new SetupError(500, `cloudflared tunnel route dns failed: ${(route.stderr || route.stdout).trim()}`);
133
+ }
134
+
135
+ broadcastGlobalEvent({ type: "tunnel:setup_step", step: "token", message: "fetching run token" });
136
+ const tokenResult = await runCloudflared(tunnelTokenArgs(tunnelName));
137
+ const token = tokenResult.stdout.trim();
138
+ if (tokenResult.code !== 0 || !TOKEN_SHAPE.test(token)) {
139
+ throw new SetupError(500, "unexpected cloudflared output while fetching the run token");
140
+ }
141
+
142
+ broadcastGlobalEvent({ type: "tunnel:setup_step", step: "apply", message: "applying configuration" });
143
+ configService.set("tunnel", {
144
+ mode: "named",
145
+ namedTunnelName: tunnelName,
146
+ namedTunnelHostname: hostname,
147
+ namedTunnelToken: token,
148
+ zoneID,
149
+ accountID,
150
+ });
151
+
152
+ const pending = (message: string): SetupOutcome => {
153
+ broadcastGlobalEvent({ type: "tunnel:setup_pending", hostname, message });
154
+ return { ok: "pending", hostname, tunnelName, message };
155
+ };
156
+
157
+ const status = readStatus();
158
+ const capabilities = Array.isArray(status.capabilities) ? (status.capabilities as unknown[]) : [];
159
+ if (!capabilities.includes("retunnel")) {
160
+ return pending("run `ppm restart` to apply — this PPM version needs a restart to pick up named tunnels");
161
+ }
162
+
163
+ let reload = requestTunnelReload();
164
+ if (reload === "busy") {
165
+ await Bun.sleep(RELOAD_RETRY_DELAY_MS);
166
+ reload = requestTunnelReload();
167
+ }
168
+ if (reload === "busy") {
169
+ return pending("supervisor busy — it will pick up the new setting shortly, or run `ppm restart`");
170
+ }
171
+ if (reload === "no-supervisor") {
172
+ return pending("no supervisor detected — run `ppm restart`");
173
+ }
174
+
175
+ // reload === "sent" — don't block the HTTP response on up to 45s of
176
+ // polling: a proxy/tunnel in front of PPM with a shorter idle timeout would
177
+ // time the browser out while setup was actually succeeding. Confirm
178
+ // detached and let /ws/global carry the final result (setup_done, or a
179
+ // follow-up setup_pending if confirmation itself times out) — the UI
180
+ // already listens for both events.
181
+ confirmReloadInBackground(hostname);
182
+ return pending("reload sent — waiting for the supervisor to confirm");
183
+ }
184
+
185
+ /**
186
+ * Aggregate for callers that need a single patchable seam (the HTTP routes) —
187
+ * route tests monkey-patch these properties directly rather than reaching for
188
+ * `mock.module`, which replaces the module for every importer process-wide and
189
+ * would poison this file's own direct-function unit tests.
190
+ */
191
+ export const namedTunnelSetupService = { readZoneInfo, runSetup, disableNamedTunnel, currentCertState };
@@ -71,7 +71,10 @@ export function writeStatus(data: Record<string, unknown>) {
71
71
  }
72
72
 
73
73
  // ─── Command file protocol ─────────────────────────────────────────────
74
- export type CmdAction = "soft_stop" | "resume" | "restart";
74
+ // "upgrade" is written directly by upgrade.service.ts (bypasses writeCmd, same
75
+ // as restart.ts/stop.ts) and read by supervisor.ts's Windows poll loop — listed
76
+ // here so the type matches what's actually read/written on disk.
77
+ export type CmdAction = "soft_stop" | "resume" | "restart" | "retunnel" | "upgrade";
75
78
 
76
79
  /** Atomically claim + read command file (rename to .claimed, read, delete) */
77
80
  export function readAndDeleteCmd(): { action: CmdAction } | null {
@@ -88,8 +91,70 @@ export function readAndDeleteCmd(): { action: CmdAction } | null {
88
91
  }
89
92
  }
90
93
 
91
- export function writeCmd(action: CmdAction) {
94
+ /**
95
+ * `retunnel` is the only low-priority action — it always yields to whatever
96
+ * is already pending, since a named-tunnel reload can wait behind a genuine
97
+ * lifecycle transition. The other three outrank it: a `resume` stuck behind
98
+ * an unclaimed `retunnel` would otherwise leave the server looking stopped
99
+ * (win32's poll loop is 1s; POSIX has a write/SIGUSR2 gap) for no reason.
100
+ */
101
+ const LIFECYCLE_ACTIONS: readonly CmdAction[] = ["resume", "soft_stop", "restart", "upgrade"];
102
+
103
+ /**
104
+ * Write `.supervisor-cmd`, but never clobber a *different* action that's still
105
+ * unclaimed — with one exception: a lifecycle action (`resume`/`soft_stop`/
106
+ * `restart`/`upgrade`) always overwrites a pending `retunnel`. Any other
107
+ * collision (two different lifecycle actions, or a `retunnel` behind a
108
+ * pending lifecycle action) returns `false` without writing so the caller can
109
+ * report "busy" instead of silently discarding someone else's request; a
110
+ * warning is logged either way so a dropped command is never silent. Note:
111
+ * `restart.ts`/`stop.ts` write this file directly and bypass this peek
112
+ * entirely, so the supervisor's dispatcher must still treat a stale/racing
113
+ * action defensively regardless of who wrote it — this is a best-effort
114
+ * single-slot guard, not a full queue.
115
+ */
116
+ export function writeCmd(action: CmdAction): boolean {
117
+ try {
118
+ const existing = JSON.parse(readFileSync(CMD_FILE(), "utf-8")) as { action?: string };
119
+ if (existing?.action && existing.action !== action) {
120
+ const lifecycleOverridesRetunnel = LIFECYCLE_ACTIONS.includes(action) && existing.action === "retunnel";
121
+ if (!lifecycleOverridesRetunnel) {
122
+ try { process.stderr.write(`[writeCmd] dropped "${action}" — "${existing.action}" is already pending\n`); } catch {}
123
+ return false;
124
+ }
125
+ }
126
+ } catch {
127
+ // Missing file, unreadable, or invalid JSON — nothing unclaimed to protect.
128
+ }
92
129
  writeFileSync(CMD_FILE(), JSON.stringify({ action }));
130
+ return true;
131
+ }
132
+
133
+ /**
134
+ * Ask the running supervisor to reload the tunnel (e.g. after a named-tunnel
135
+ * setup/disable). POSIX confirms the supervisor is alive via a zero-signal
136
+ * probe before writing; win32 has no equivalent probe here, so it relies on
137
+ * the supervisor's existing 1s command-file poll loop to pick up the request.
138
+ */
139
+ export function requestTunnelReload(): "sent" | "busy" | "no-supervisor" {
140
+ const status = readStatus();
141
+ const pid = status.supervisorPid as number | undefined;
142
+ if (!pid) return "no-supervisor";
143
+
144
+ if (process.platform !== "win32") {
145
+ try {
146
+ process.kill(pid, 0);
147
+ } catch {
148
+ return "no-supervisor";
149
+ }
150
+ }
151
+
152
+ if (!writeCmd("retunnel")) return "busy";
153
+
154
+ if (process.platform !== "win32") {
155
+ try { process.kill(pid, "SIGUSR2"); } catch { /* best-effort; poll loop is the fallback path */ }
156
+ }
157
+ return "sent";
93
158
  }
94
159
 
95
160
  // ─── Lockfile ──────────────────────────────────────────────────────────