@hienlh/ppm 0.18.14 → 0.18.16

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 (546) hide show
  1. package/.rdtest2-cf.out +0 -0
  2. package/CHANGELOG.md +26 -0
  3. package/CLAUDE.md +3 -0
  4. package/assets/skills/ppm/SKILL.md +1 -1
  5. package/assets/skills/ppm/references/cli-reference.md +22 -4
  6. package/assets/skills/ppm/references/http-api.md +8 -1
  7. package/dist/web/assets/{adaptive-context-menu-DCZtgOL7.js → adaptive-context-menu-BbuRVTLn.js} +1 -1
  8. package/dist/web/assets/{ai-resource-editor-D5CPBg06.js → ai-resource-editor-Cm4n3VXt.js} +1 -1
  9. package/dist/web/assets/ai-settings-section-BEM1z7kp.js +1 -0
  10. package/dist/web/assets/{angular-html-CTvM4ulF.js → angular-html-BlcrLjUx.js} +1 -1
  11. package/dist/web/assets/{angular-ts-B_vSkjco.js → angular-ts-BUjtceAJ.js} +1 -1
  12. package/dist/web/assets/api-client-D1CePgQ4.js +1 -0
  13. package/dist/web/assets/api-settings-CNtKkHqW.js +1 -0
  14. package/dist/web/assets/{api-settings-B2iNXQ0c.js → api-settings-Cubuqjsu.js} +1 -1
  15. package/dist/web/assets/{api-themes-DrWnJbhs.js → api-themes-YsZgf6Xj.js} +1 -1
  16. package/dist/web/assets/{apl-DywzTry0.js → apl-B3g0v1pk.js} +1 -1
  17. package/dist/web/assets/architecture-PBZL5I3N-6konx-J1.js +1 -0
  18. package/dist/web/assets/{astro-1JaRgaSb.js → astro-FRnyn7cH.js} +1 -1
  19. package/dist/web/assets/{audio-preview-DAuGvOiK.js → audio-preview-DWTRm4HC.js} +1 -1
  20. package/dist/web/assets/{blade-CDeeJWBb.js → blade-NLdOqUKw.js} +1 -1
  21. package/dist/web/assets/{bsl-BfgoPVYR.js → bsl-CenHZfaU.js} +1 -1
  22. package/dist/web/assets/{button-tNy0ba3y.js → button-Cq8yixjf.js} +1 -1
  23. package/dist/web/assets/{cairo-C26gNwWW.js → cairo-B_WR0aiQ.js} +1 -1
  24. package/dist/web/assets/chat-tab-C2e6Pwc2.js +15 -0
  25. package/dist/web/assets/{cobol-D92nBnON.js → cobol-DOynhWYj.js} +1 -1
  26. package/dist/web/assets/code-editor-DWtv_VDo.js +10 -0
  27. package/dist/web/assets/{coffee-CIWH40R1.js → coffee-Bn6dbw4P.js} +1 -1
  28. package/dist/web/assets/{conflict-editor-CDp0I3mD.js → conflict-editor-Bi2RWfKQ.js} +1 -1
  29. package/dist/web/assets/{context-menu-BO1c9jKy.js → context-menu-DfWZAxQy.js} +1 -1
  30. package/dist/web/assets/{cpp-DOrBqXCn.js → cpp-CoZR9t8r.js} +1 -1
  31. package/dist/web/assets/{crystal-BNKqmHux.js → crystal-BSheONSg.js} +1 -1
  32. package/dist/web/assets/{csv-preview-D4svNDAq.js → csv-preview-DxGZ1qg5.js} +1 -1
  33. package/dist/web/assets/{data-grid-overlay-editor-BqV0AxZg.js → data-grid-overlay-editor-gwPoVSCU.js} +1 -1
  34. package/dist/web/assets/{database-viewer-BCvv_Bwf.js → database-viewer-DSPRzZny.js} +1 -1
  35. package/dist/web/assets/{dialog-DcLGDwaX.js → dialog-BSfuiTkJ.js} +1 -1
  36. package/dist/web/assets/{diff-viewer-D41fCFAi.js → diff-viewer-YbpnNv5Y.js} +1 -1
  37. package/dist/web/assets/{docx-preview-DfaQn9v7.js → docx-preview-DqVDOMmv.js} +1 -1
  38. package/dist/web/assets/{edge-Dbjp42Pt.js → edge-DTtoslQu.js} +1 -1
  39. package/dist/web/assets/{edit-diff-preview-BqcUuFCc.js → edit-diff-preview-CXSbSUBX.js} +1 -1
  40. package/dist/web/assets/{elixir-na3b1pYy.js → elixir-CIBCZ3Zi.js} +1 -1
  41. package/dist/web/assets/{elm-_Nalil1R.js → elm-BLmb7K1H.js} +1 -1
  42. package/dist/web/assets/{erb-CW_7dubf.js → erb-BBY8ApRd.js} +1 -1
  43. package/dist/web/assets/{erlang-DmZ7Zzb0.js → erlang-D5Zb5_P0.js} +1 -1
  44. package/dist/web/assets/{esm-CwyBetO1.js → esm-6F8v1jLL.js} +1 -1
  45. package/dist/web/assets/explorer-body-DpQndBVw.js +2 -0
  46. package/dist/web/assets/explorer-window-content-m0K6Mtcg.js +1 -0
  47. package/dist/web/assets/{extension-store-DUaZINyC.js → extension-store-Bhu--fPX.js} +1 -1
  48. package/dist/web/assets/{extension-webview-BW_mccb3.js → extension-webview-BSh6KGd7.js} +1 -1
  49. package/dist/web/assets/{file-store-yReVbR8q.js → file-store-6HW2Lfl4.js} +1 -1
  50. package/dist/web/assets/{fortran-fixed-form-DB2A8L4-.js → fortran-fixed-form-CBdwkEUJ.js} +1 -1
  51. package/dist/web/assets/{fsharp-BYHrti7z.js → fsharp-AT4XdVYH.js} +1 -1
  52. package/dist/web/assets/{gdresource-D5GBin40.js → gdresource-D852fWBR.js} +1 -1
  53. package/dist/web/assets/{git-commit-CUiOBoIL.js → git-commit-DWfWAyxy.js} +1 -1
  54. package/dist/web/assets/{git-log-panel-C1QDkmgl.js → git-log-panel-CkTiR70w.js} +1 -1
  55. package/dist/web/assets/{git-rebase-DOxGnpdw.js → git-rebase-C6opk8gr.js} +1 -1
  56. package/dist/web/assets/gitGraph-HDMCJU4V-ikUwsekd.js +1 -0
  57. package/dist/web/assets/glide-data-grid-BB5mCaCD.js +138 -0
  58. package/dist/web/assets/{glimmer-js-Begy4UT7.js → glimmer-js-DLi5mWYO.js} +1 -1
  59. package/dist/web/assets/{glimmer-ts-rPApRY8N.js → glimmer-ts-D-scFX30.js} +1 -1
  60. package/dist/web/assets/{glsl-wrbPvrBL.js → glsl-dbQzF4iA.js} +1 -1
  61. package/dist/web/assets/{graphql-Cu4-NR__.js → graphql-D1m5Wen2.js} +1 -1
  62. package/dist/web/assets/group-chat-tab-BPIOdFJw.js +1 -0
  63. package/dist/web/assets/{hack-Ddo93XKD.js → hack-CqYmpO7u.js} +1 -1
  64. package/dist/web/assets/{haml-BFX_1i8j.js → haml-BXzKWEag.js} +1 -1
  65. package/dist/web/assets/hand-x0ejq3fS.js +1 -0
  66. package/dist/web/assets/{handlebars-5gFKA4H5.js → handlebars-DXnrtHtq.js} +1 -1
  67. package/dist/web/assets/{html-C06I-zeO.js → html-DhgQJ-xL.js} +1 -1
  68. package/dist/web/assets/{html-derivative-e5JLAFZF.js → html-derivative-xZRft-YE.js} +1 -1
  69. package/dist/web/assets/{http-CrAAhSU7.js → http-B53mSj14.js} +1 -1
  70. package/dist/web/assets/{hurl-T1V3SX9T.js → hurl-CsomVy04.js} +1 -1
  71. package/dist/web/assets/{hxml-B056LLKN.js → hxml-LffjWwKA.js} +1 -1
  72. package/dist/web/assets/{image-preview-DWyuCYT3.js → image-preview-CNA6XMNS.js} +1 -1
  73. package/dist/web/assets/index-BHdtSKzb.js +52 -0
  74. package/dist/web/assets/index-DN9XUQ8D.css +2 -0
  75. package/dist/web/assets/info-3K5VOQVL-BAgmboBp.js +1 -0
  76. package/dist/web/assets/{input-DouJ9Zvt.js → input-D0ol5jW0.js} +1 -1
  77. package/dist/web/assets/{jinja-DGdtRh43.js → jinja-CCHURl7V.js} +1 -1
  78. package/dist/web/assets/{jison-Ah6QobPS.js → jison-7qBd8dnR.js} +1 -1
  79. package/dist/web/assets/{julia-BJeGOK0K.js → julia-BY9pnTcy.js} +1 -1
  80. package/dist/web/assets/{just-iw9oRs7j.js → just-D7wRSNRr.js} +1 -1
  81. package/dist/web/assets/{keybindings-store-0Wqfa7Qp.js → keybindings-store-BGkB72gM.js} +1 -1
  82. package/dist/web/assets/keybindings-store-Bq6VWl5X.js +1 -0
  83. package/dist/web/assets/keyboard-DBBaoQSu.js +1 -0
  84. package/dist/web/assets/{latex-DHjj5hyN.js → latex-RgsCae55.js} +1 -1
  85. package/dist/web/assets/{liquid-B-FFecDc.js → liquid-CnDFzvOZ.js} +1 -1
  86. package/dist/web/assets/{lua-DDianJp_.js → lua-0lj5W8sm.js} +1 -1
  87. package/dist/web/assets/{markdown-renderer-BHiUhQN8.js → markdown-renderer-B9wzBWm9.js} +3 -3
  88. package/dist/web/assets/markdown-renderer-DeSV1CCQ.js +1 -0
  89. package/dist/web/assets/{marko-BW5SBVng.js → marko-CQgqAhbV.js} +1 -1
  90. package/dist/web/assets/{mdc-B9gL9bjl.js → mdc-BkIcTiIy.js} +1 -1
  91. package/dist/web/assets/{media-url-DUK-k0bB.js → media-url-DDciwvLU.js} +1 -1
  92. package/dist/web/assets/mobile-bottom-sheet-pjcHKorA.js +1 -0
  93. package/dist/web/assets/mobile-explorer-sheet-DPdxpnKh.js +1 -0
  94. package/dist/web/assets/monitor-smartphone-DLZ6Q05s.js +1 -0
  95. package/dist/web/assets/{nextflow-dTluY6qy.js → nextflow-DvIW68DS.js} +1 -1
  96. package/dist/web/assets/{nginx-DlwghGI-.js → nginx-O-hft0gG.js} +1 -1
  97. package/dist/web/assets/{nim-N9jddb5D.js → nim-BxrR4HwQ.js} +1 -1
  98. package/dist/web/assets/notification-store-BtijuGez.js +1 -0
  99. package/dist/web/assets/{number-overlay-editor-BVXM7aAs.js → number-overlay-editor-BoLRYuZT.js} +1 -1
  100. package/dist/web/assets/{overview-panel-DCCDmtCQ.js → overview-panel-l3g1gT5e.js} +1 -1
  101. package/dist/web/assets/packet-RMMSAZCW-RVlbbDRS.js +1 -0
  102. package/dist/web/assets/{panel-store-B4rijFKN.js → panel-store-UOV8wrfR.js} +1 -1
  103. package/dist/web/assets/{pdf-preview-D7yLmAZW.js → pdf-preview-B2CJ_EKv.js} +1 -1
  104. package/dist/web/assets/{perl-CkmpYKa_.js → perl-Da-uNqmL.js} +1 -1
  105. package/dist/web/assets/{php-BwZ3KDPo.js → php-JMu45mEx.js} +1 -1
  106. package/dist/web/assets/pie-UPGHQEXC-ZWb4izpk.js +1 -0
  107. package/dist/web/assets/{postgres-viewer--CRN1fvO.js → postgres-viewer-23xdZYxp.js} +3 -3
  108. package/dist/web/assets/process-table-CUsPf7wv.js +1 -0
  109. package/dist/web/assets/{project-store-BuY7rxn_.js → project-store-DTUCS65m.js} +1 -1
  110. package/dist/web/assets/{pug-B3rBPqQo.js → pug-CLzyQhia.js} +1 -1
  111. package/dist/web/assets/{qml-BfDbWFmg.js → qml-BHXfGB1l.js} +1 -1
  112. package/dist/web/assets/radar-KQ55EAFF-bsP5x1Bx.js +1 -0
  113. package/dist/web/assets/{razor-B1ww1r38.js → razor-BqetJLN3.js} +1 -1
  114. package/dist/web/assets/remote-desktop-mobile-sheet-FTF6IBhF.js +2 -0
  115. package/dist/web/assets/remote-desktop-mobile-view-DSwG4OSk.js +1 -0
  116. package/dist/web/assets/remote-desktop-stats-overlay-Bz6xZ5y9.js +1 -0
  117. package/dist/web/assets/remote-desktop-warning-gate-DrYySLZ5.js +1 -0
  118. package/dist/web/assets/remote-desktop-window-content-Ndvezorb.js +1 -0
  119. package/dist/web/assets/{rst-C0MSq6m-.js → rst-iQdJZ0BP.js} +1 -1
  120. package/dist/web/assets/{ruby-ClIZ1wb3.js → ruby-CNwzZGdH.js} +1 -1
  121. package/dist/web/assets/{run-in-terminal-4efbW2Xv.js → run-in-terminal-CBXmVGWa.js} +1 -1
  122. package/dist/web/assets/{sas-DR0j3o4_.js → sas-BpBIT2eR.js} +1 -1
  123. package/dist/web/assets/{scroll-area-ZP8wcwTI.js → scroll-area-lc0ZHORr.js} +1 -1
  124. package/dist/web/assets/{scss-Vh3JLayR.js → scss-D1MotuEp.js} +1 -1
  125. package/dist/web/assets/{send-to-chat-BNrEpAyC.js → send-to-chat-DJAdlvJ8.js} +1 -1
  126. package/dist/web/assets/settings-store-zbPkG7o-.js +2 -0
  127. package/dist/web/assets/settings-tab-Cn-AqUP_.js +1 -0
  128. package/dist/web/assets/settings-tab-xyRpt2gq.js +3 -0
  129. package/dist/web/assets/{shaderlab-0rHRnQ18.js → shaderlab-BNqmcPQi.js} +1 -1
  130. package/dist/web/assets/{shellsession-Dxdw6D8c.js → shellsession-BBLOVOO-.js} +1 -1
  131. package/dist/web/assets/{soy-BHlSNj1y.js → soy-BpTGo9uy.js} +1 -1
  132. package/dist/web/assets/{sparql-B2QaPpni.js → sparql-D7A3yKob.js} +1 -1
  133. package/dist/web/assets/{sql-query-editor-R_iKjPP9.js → sql-query-editor-BE3mLYyz.js} +1 -1
  134. package/dist/web/assets/{sqlite-viewer-b2onA0SP.js → sqlite-viewer-wN5RP8sd.js} +1 -1
  135. package/dist/web/assets/{stata-CwIxjsEJ.js → stata-B7N56ptS.js} +1 -1
  136. package/dist/web/assets/{surrealql-C6vo-rp-.js → surrealql-CTmnD-lv.js} +1 -1
  137. package/dist/web/assets/{svelte-LLdJ6rlH.js → svelte-BppwY9TR.js} +1 -1
  138. package/dist/web/assets/{switch-DV-BDjyk.js → switch-BmjKoIpR.js} +1 -1
  139. package/dist/web/assets/system-monitor-body-B2M0MC6y.js +2 -0
  140. package/dist/web/assets/system-monitor-tab-OYca4qaf.js +1 -0
  141. package/dist/web/assets/{system-monitor-window-content-BPif0azo.js → system-monitor-window-content-CHz9WdTm.js} +1 -1
  142. package/dist/web/assets/{tab-host-window-content-BnAN6m4K.js → tab-host-window-content-DKev3GJz.js} +1 -1
  143. package/dist/web/assets/{tab-store-tFnyPmm4.js → tab-store-DzYSsw5a.js} +1 -1
  144. package/dist/web/assets/team-member-sheet-BQ738Lli.js +2 -0
  145. package/dist/web/assets/{team-member-window-content-BSyU-5Uc.js → team-member-window-content-CK08f5cK.js} +1 -1
  146. package/dist/web/assets/{templ-CJGvhEP5.js → templ-CvTMKKra.js} +1 -1
  147. package/dist/web/assets/{terminal-tab-Ncor0Rfu.js → terminal-tab-DZ3QiYcs.js} +3 -3
  148. package/dist/web/assets/{tex-psXWhc4t.js → tex--c4POEUE.js} +1 -1
  149. package/dist/web/assets/{tool-cards-DdVP1wrD.js → tool-cards-Bw1P0MKU.js} +4 -4
  150. package/dist/web/assets/treemap-KZPCXAKY-Dh1oxNdQ.js +1 -0
  151. package/dist/web/assets/{ts-tags-D5Ar_ZCO.js → ts-tags--kx7D-Hw.js} +1 -1
  152. package/dist/web/assets/{twig-vJgGC-qY.js → twig-UG9uMHGd.js} +1 -1
  153. package/dist/web/assets/{use-blob-url-BOVGWg9F.js → use-blob-url-Bdxopwwc.js} +1 -1
  154. package/dist/web/assets/{use-monaco-theme-BshnLXoY.js → use-monaco-theme-BOmd2wpe.js} +1 -1
  155. package/dist/web/assets/{use-open-team-member-CY3wOdWL.js → use-open-team-member-D9EOB8-i.js} +1 -1
  156. package/dist/web/assets/use-visual-viewport-Sfb6CZ80.js +1 -0
  157. package/dist/web/assets/{use-websocket-B7xjNsyD.js → use-websocket-B7Xj-Q4W.js} +1 -1
  158. package/dist/web/assets/{vendor-mermaid-999ksD45.js → vendor-mermaid-0Nt-ew76.js} +1 -1
  159. package/dist/web/assets/{video-preview-7F72sRwf.js → video-preview-B0nbTGjQ.js} +1 -1
  160. package/dist/web/assets/{vue-html-BN5ZLu-H.js → vue-html-8KEW6-I9.js} +1 -1
  161. package/dist/web/assets/{vue-BWngFyav.js → vue-jjTomlbt.js} +1 -1
  162. package/dist/web/assets/{vue-vine-BZHnO8yV.js → vue-vine-C5tohCkU.js} +1 -1
  163. package/dist/web/assets/{window-pip-registry-BnYU83LE.js → window-pip-registry-FdZdGF7h.js} +1 -1
  164. package/dist/web/assets/window-store-D0x_fxr0.js +1 -0
  165. package/dist/web/assets/{xml-DS2beZHp.js → xml-ByFFosqL.js} +1 -1
  166. package/dist/web/assets/{xsl-Ciiku1Bq.js → xsl-CZUwiLkj.js} +1 -1
  167. package/dist/web/index.html +46 -43
  168. package/dist/web/sw.js +1 -1
  169. package/docs/architecture/ai-chat-and-providers.md +3 -0
  170. package/package.json +1 -1
  171. package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
  172. package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
  173. package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
  174. package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
  175. package/src/cli/commands/backup-cmd.ts +119 -0
  176. package/src/index.ts +3 -0
  177. package/src/providers/claude-agent-sdk.ts +29 -1
  178. package/src/server/index.ts +42 -15
  179. package/src/server/routes/accounts.ts +30 -2
  180. package/src/server/routes/chat.ts +8 -1
  181. package/src/server/routes/named-tunnel.ts +5 -2
  182. package/src/server/routes/remote-desktop.ts +64 -0
  183. package/src/server/routes/settings.ts +2 -0
  184. package/src/server/ws/chat.ts +126 -7
  185. package/src/server/ws/remote-desktop.ts +86 -0
  186. package/src/services/account.service.ts +106 -23
  187. package/src/services/db-backup/db-backup-paths.ts +66 -0
  188. package/src/services/db-backup/db-backup-restore.ts +105 -0
  189. package/src/services/db-backup/db-backup-retention.ts +96 -0
  190. package/src/services/db-backup/db-backup-sync.ts +113 -0
  191. package/src/services/db-backup/db-backup.service.ts +61 -0
  192. package/src/services/db.service.ts +29 -1
  193. package/src/services/edge-forwarder.ts +191 -191
  194. package/src/services/fs-credential-path-guard.ts +19 -1
  195. package/src/services/named-tunnel/cloudflare-dns-api.ts +20 -4
  196. package/src/services/named-tunnel/cloudflared-login.service.ts +2 -2
  197. package/src/services/named-tunnel/named-tunnel-args.ts +17 -3
  198. package/src/services/named-tunnel/named-tunnel-setup.service.ts +20 -2
  199. package/src/services/nested-subagent-spy.ts +206 -0
  200. package/src/services/remote-desktop/access-unit-assembler.ts +90 -0
  201. package/src/services/remote-desktop/avc1-codec-string.ts +18 -0
  202. package/src/services/remote-desktop/nal-splitter.ts +100 -0
  203. package/src/services/remote-desktop/remote-desktop-capture.ts +120 -0
  204. package/src/services/remote-desktop/remote-desktop-encoder-args.ts +42 -0
  205. package/src/services/remote-desktop/remote-desktop-flag.ts +12 -0
  206. package/src/services/remote-desktop/remote-desktop-input.ts +217 -0
  207. package/src/services/remote-desktop/remote-desktop-nonce.ts +38 -0
  208. package/src/services/remote-desktop/remote-desktop-session.ts +189 -0
  209. package/src/services/remote-desktop/remote-desktop-vk-map.ts +41 -0
  210. package/src/services/subagent-transcript-merger.ts +92 -75
  211. package/src/services/subprocess-retention.ts +29 -6
  212. package/src/services/supervisor.ts +58 -0
  213. package/src/services/team-member-activity/subagent-transcript-index.ts +56 -0
  214. package/src/services/windows-process-tree.ts +351 -351
  215. package/src/types/chat.ts +2 -0
  216. package/src/web/app.tsx +8 -0
  217. package/src/web/components/chat/account-dialogs.tsx +11 -4
  218. package/src/web/components/chat/question-card.tsx +9 -2
  219. package/src/web/components/chat/usage-badge.tsx +17 -3
  220. package/src/web/components/floating-window/window-content-registry.ts +2 -0
  221. package/src/web/components/floating-window/window-store-types.ts +1 -1
  222. package/src/web/components/layout/mobile-drawer.tsx +38 -27
  223. package/src/web/components/layout/nav-section-rail.tsx +10 -2
  224. package/src/web/components/remote-desktop/open-remote-desktop.ts +26 -0
  225. package/src/web/components/remote-desktop/remote-desktop-coords.ts +98 -0
  226. package/src/web/components/remote-desktop/remote-desktop-frame-timestamp.ts +21 -0
  227. package/src/web/components/remote-desktop/remote-desktop-gesture-classifiers.ts +56 -0
  228. package/src/web/components/remote-desktop/remote-desktop-mobile-key-bar.tsx +131 -0
  229. package/src/web/components/remote-desktop/remote-desktop-mobile-sheet.tsx +48 -0
  230. package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +79 -0
  231. package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +190 -0
  232. package/src/web/components/remote-desktop/remote-desktop-stats-overlay.tsx +54 -0
  233. package/src/web/components/remote-desktop/remote-desktop-stats.ts +37 -0
  234. package/src/web/components/remote-desktop/remote-desktop-touch-gesture-tracker.ts +195 -0
  235. package/src/web/components/remote-desktop/remote-desktop-warning-gate.tsx +106 -0
  236. package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +86 -0
  237. package/src/web/components/remote-desktop/remote-desktop-ws-url.ts +22 -0
  238. package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +175 -0
  239. package/src/web/components/remote-desktop/use-remote-desktop-available.ts +29 -0
  240. package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +147 -0
  241. package/src/web/components/remote-desktop/use-remote-desktop-mobile-open-state.ts +18 -0
  242. package/src/web/components/remote-desktop/use-remote-desktop-touch.ts +224 -0
  243. package/src/web/components/remote-desktop/use-remote-desktop-virtual-keyboard.ts +50 -0
  244. package/src/web/components/remote-desktop/use-remote-input-capture.ts +92 -0
  245. package/src/web/components/tunnels/named-tunnel/named-tunnel-copy.ts +59 -59
  246. package/src/web/components/tunnels/named-tunnel/named-tunnel-section.tsx +2 -2
  247. package/src/web/hooks/use-notification-badge.ts +19 -11
  248. package/src/web/lib/feature-badges.ts +2 -0
  249. package/src/web/stores/settings-store.ts +25 -0
  250. package/src/web/stores/streaming-store.ts +21 -2
  251. package/dist/web/assets/ai-settings-section-DY-t1bK8.js +0 -1
  252. package/dist/web/assets/api-client-BogqsFna.js +0 -1
  253. package/dist/web/assets/api-settings-DRF9oYkn.js +0 -1
  254. package/dist/web/assets/architecture-PBZL5I3N-nw5-j_I_.js +0 -1
  255. package/dist/web/assets/chat-tab-IcnU3nqQ.js +0 -15
  256. package/dist/web/assets/code-editor-Dno5Srkm.js +0 -10
  257. package/dist/web/assets/explorer-body-DB1L30pa.js +0 -2
  258. package/dist/web/assets/explorer-window-content-Cwlt3C3I.js +0 -1
  259. package/dist/web/assets/gitGraph-HDMCJU4V-C1xf87gN.js +0 -1
  260. package/dist/web/assets/glide-data-grid-Dz8lx6n2.js +0 -138
  261. package/dist/web/assets/group-chat-tab-DFwr-pig.js +0 -1
  262. package/dist/web/assets/index-DWpNR3nB.css +0 -2
  263. package/dist/web/assets/index-oUKV5nIA.js +0 -52
  264. package/dist/web/assets/info-3K5VOQVL-C2z7FTDe.js +0 -1
  265. package/dist/web/assets/keybindings-store-d54pv2NK.js +0 -1
  266. package/dist/web/assets/markdown-renderer-CnE36_gd.js +0 -1
  267. package/dist/web/assets/mobile-bottom-sheet-CJ7ApOkI.js +0 -1
  268. package/dist/web/assets/mobile-explorer-sheet-BzjlS1Xn.js +0 -1
  269. package/dist/web/assets/notification-store-C7UBqxYP.js +0 -1
  270. package/dist/web/assets/packet-RMMSAZCW-DQUxN0wU.js +0 -1
  271. package/dist/web/assets/pie-UPGHQEXC-BOr9ek4Q.js +0 -1
  272. package/dist/web/assets/process-table-CXC10GSy.js +0 -1
  273. package/dist/web/assets/radar-KQ55EAFF-DSErxZFb.js +0 -1
  274. package/dist/web/assets/settings-store-OLJE7dPH.js +0 -2
  275. package/dist/web/assets/settings-tab-B1rvHFc6.js +0 -3
  276. package/dist/web/assets/settings-tab-BUlmPJ2w.js +0 -1
  277. package/dist/web/assets/system-monitor-body-DPYQlBEt.js +0 -2
  278. package/dist/web/assets/system-monitor-tab-CPQdWv27.js +0 -1
  279. package/dist/web/assets/team-member-sheet-Bu2wG1sm.js +0 -2
  280. package/dist/web/assets/treemap-KZPCXAKY-C17p0LQw.js +0 -1
  281. package/dist/web/assets/window-store-CQ1lghvv.js +0 -1
  282. /package/dist/web/assets/{abap-CjzrhAS2.js → abap-C5DFF0s3.js} +0 -0
  283. /package/dist/web/assets/{actionscript-3-B59Kbl-9.js → actionscript-3-B0qyRNbc.js} +0 -0
  284. /package/dist/web/assets/{ada-B7gItKAE.js → ada-bAJzplug.js} +0 -0
  285. /package/dist/web/assets/{andromeeda-Dy5zcy0Q.js → andromeeda-DUCpISy6.js} +0 -0
  286. /package/dist/web/assets/{apache-B6T9jrc0.js → apache-BhAr84Z_.js} +0 -0
  287. /package/dist/web/assets/{apex-CTsIxDQw.js → apex-CRv8YnNq.js} +0 -0
  288. /package/dist/web/assets/{api-client-oY1tC3tM.js → api-client-BzWM7XPR.js} +0 -0
  289. /package/dist/web/assets/{applescript-DuycKmmb.js → applescript-CD-Cw8bt.js} +0 -0
  290. /package/dist/web/assets/{apply-theme-r8tKDrCR.js → apply-theme-DJ794zSQ.js} +0 -0
  291. /package/dist/web/assets/{ara-CISL3599.js → ara-BUzeQYoG.js} +0 -0
  292. /package/dist/web/assets/{asciidoc-BXyE2Lpr.js → asciidoc-CT2hTg-v.js} +0 -0
  293. /package/dist/web/assets/{asm-y258phtP.js → asm-CAu35JeY.js} +0 -0
  294. /package/dist/web/assets/{aurora-x-Dq3Jc78M.js → aurora-x-BeFJoIA1.js} +0 -0
  295. /package/dist/web/assets/{awk-DxL6XSJv.js → awk-Vau-DrGN.js} +0 -0
  296. /package/dist/web/assets/{ayu-dark-DjoK6Evo.js → ayu-dark-CLe-OTsQ.js} +0 -0
  297. /package/dist/web/assets/{ayu-light-CKKXBLMI.js → ayu-light-CJbQfu4M.js} +0 -0
  298. /package/dist/web/assets/{ayu-mirage-DGXSrrXB.js → ayu-mirage-GWaKwdrx.js} +0 -0
  299. /package/dist/web/assets/{ballerina-DZSXFmUf.js → ballerina-DWu0USI5.js} +0 -0
  300. /package/dist/web/assets/{bat-BNsZk6nY.js → bat-_07Huw7E.js} +0 -0
  301. /package/dist/web/assets/{beancount-cPl4gJct.js → beancount-EcvogrcF.js} +0 -0
  302. /package/dist/web/assets/{berry-DGAodERk.js → berry-BBB87SY7.js} +0 -0
  303. /package/dist/web/assets/{bibtex-DftVoRif.js → bibtex-DbueS87A.js} +0 -0
  304. /package/dist/web/assets/{bicep-Suioyyzh.js → bicep-tcmXnUo4.js} +0 -0
  305. /package/dist/web/assets/{bird2-DjU24I2P.js → bird2-B2qwilIR.js} +0 -0
  306. /package/dist/web/assets/{c-DFi8Y_nN.js → c-Z58oivSt.js} +0 -0
  307. /package/dist/web/assets/{c3-1eSHnH5V.js → c3-U40cGT9p.js} +0 -0
  308. /package/dist/web/assets/{cadence-DLLsFwQP.js → cadence-BFcD-LyM.js} +0 -0
  309. /package/dist/web/assets/{can-open-in-ppm-in727eFQ.js → can-open-in-ppm-CdCK2c2a.js} +0 -0
  310. /package/dist/web/assets/{catppuccin-frappe-DDj0JEiY.js → catppuccin-frappe-RkC9AX6J.js} +0 -0
  311. /package/dist/web/assets/{catppuccin-latte-DPyANHyR.js → catppuccin-latte-Bsi4l-nO.js} +0 -0
  312. /package/dist/web/assets/{catppuccin-macchiato-CO-h8VTT.js → catppuccin-macchiato-B8JIdTW-.js} +0 -0
  313. /package/dist/web/assets/{catppuccin-mocha-BCmbN2IZ.js → catppuccin-mocha-oH8wwrDj.js} +0 -0
  314. /package/dist/web/assets/{clarity-C0pYsZjs.js → clarity-C3WNeRCR.js} +0 -0
  315. /package/dist/web/assets/{clipboard-DBLGKMrH.js → clipboard-B-gwJ8DF.js} +0 -0
  316. /package/dist/web/assets/{clojure-JFr6s8iZ.js → clojure-CFoAz7u9.js} +0 -0
  317. /package/dist/web/assets/{cmake-EEcLEgVy.js → cmake-Bcsqp9yR.js} +0 -0
  318. /package/dist/web/assets/{codeowners-Deh3Lx0u.js → codeowners-DC2Z7heP.js} +0 -0
  319. /package/dist/web/assets/{codeql-C317igT5.js → codeql-BfG68Al5.js} +0 -0
  320. /package/dist/web/assets/{common-lisp-DepbATZs.js → common-lisp-Z5P-lKQe.js} +0 -0
  321. /package/dist/web/assets/{coq-MxgxU5ya.js → coq-BJslqrnv.js} +0 -0
  322. /package/dist/web/assets/{csharp-eEFXdsAq.js → csharp-DhhZTOTs.js} +0 -0
  323. /package/dist/web/assets/{css-Db-syWW9.js → css-DiFIpMfE.js} +0 -0
  324. /package/dist/web/assets/{csv-DHeYcbaq.js → csv-DViLbHxI.js} +0 -0
  325. /package/dist/web/assets/{csv-parser-_JWMLGl3.js → csv-parser-B2v8MoUy.js} +0 -0
  326. /package/dist/web/assets/{cue-BmUNowrt.js → cue-CoG3mg2I.js} +0 -0
  327. /package/dist/web/assets/{cypher-XjXHgCNS.js → cypher-jTxC8NAB.js} +0 -0
  328. /package/dist/web/assets/{d-BWJnZtig.js → d-6hKIUAbU.js} +0 -0
  329. /package/dist/web/assets/{dark-plus-Db7umas1.js → dark-plus-D_-np2Sr.js} +0 -0
  330. /package/dist/web/assets/{dart-DSI4HYh1.js → dart-CVpxLpVn.js} +0 -0
  331. /package/dist/web/assets/{data-grid-types-CfVHh9gv.js → data-grid-types-Dq_H1IPa.js} +0 -0
  332. /package/dist/web/assets/{dax-yw1JsAni.js → dax-BPqJ8qIY.js} +0 -0
  333. /package/dist/web/assets/{desktop-C2QO4RvD.js → desktop-u_y5_Hr5.js} +0 -0
  334. /package/dist/web/assets/{diff-Drt8aEyp.js → diff-Duj1XMrd.js} +0 -0
  335. /package/dist/web/assets/{dist-DNRvhVdz.js → dist-CEZ4plgH.js} +0 -0
  336. /package/dist/web/assets/{dist-VBxOHLmN.js → dist-aDSxTR_v.js} +0 -0
  337. /package/dist/web/assets/{docker-ByDKbmLl.js → docker-8UGSCc7N.js} +0 -0
  338. /package/dist/web/assets/{dotenv-BkfOLytR.js → dotenv-DVnj8kyR.js} +0 -0
  339. /package/dist/web/assets/{dracula-DAmrm-Un.js → dracula-D0cEmSg7.js} +0 -0
  340. /package/dist/web/assets/{dracula-soft-BUOEnKqR.js → dracula-soft-DfGS5Yfr.js} +0 -0
  341. /package/dist/web/assets/{dream-maker-DQBIfSiO.js → dream-maker-qG8kMGTF.js} +0 -0
  342. /package/dist/web/assets/{emacs-lisp-Blh5kBD3.js → emacs-lisp-iVqyMLyT.js} +0 -0
  343. /package/dist/web/assets/{everforest-dark-CEzhDxHq.js → everforest-dark-Cruygd5U.js} +0 -0
  344. /package/dist/web/assets/{everforest-light-CPk_5Khm.js → everforest-light-BiJa8RHL.js} +0 -0
  345. /package/dist/web/assets/{fennel-B1MT_EBZ.js → fennel-H6XSEIoz.js} +0 -0
  346. /package/dist/web/assets/{fish-C_HGcewt.js → fish-ho_7CxX6.js} +0 -0
  347. /package/dist/web/assets/{fluent-Dp1Iclmx.js → fluent-1l5ssJTF.js} +0 -0
  348. /package/dist/web/assets/{format-bytes-53WBtyi2.js → format-bytes-gQw5wzqa.js} +0 -0
  349. /package/dist/web/assets/{fortran-free-form-B_-UA4XC.js → fortran-free-form-Bo1x233A.js} +0 -0
  350. /package/dist/web/assets/{gdscript-Bm_JpjS_.js → gdscript-DffFMp_k.js} +0 -0
  351. /package/dist/web/assets/{gdshader-C_PNmfYU.js → gdshader-CvnpAiAF.js} +0 -0
  352. /package/dist/web/assets/{genie-B5rmR2TF.js → genie-2eOvAEzW.js} +0 -0
  353. /package/dist/web/assets/{gherkin-BTJ34TJ9.js → gherkin-DL-5OUlV.js} +0 -0
  354. /package/dist/web/assets/{github-dark-DOkNVj9H.js → github-dark-DrTZprgG.js} +0 -0
  355. /package/dist/web/assets/{github-dark-default-DTsmiyVo.js → github-dark-default-_mDH8IMP.js} +0 -0
  356. /package/dist/web/assets/{github-dark-dimmed-CjbxAe9s.js → github-dark-dimmed-C48hJuLn.js} +0 -0
  357. /package/dist/web/assets/{github-dark-high-contrast-r_M8F_k7.js → github-dark-high-contrast-BQ-zGGNe.js} +0 -0
  358. /package/dist/web/assets/{github-light-BzfxyMyb.js → github-light-BYc90Yx8.js} +0 -0
  359. /package/dist/web/assets/{github-light-default-BOqiBIy5.js → github-light-default-Daecj4J5.js} +0 -0
  360. /package/dist/web/assets/{github-light-high-contrast-DQzBrDM4.js → github-light-high-contrast-DoafCbsU.js} +0 -0
  361. /package/dist/web/assets/{gleam-BWmuAtD8.js → gleam-hUWWjmnp.js} +0 -0
  362. /package/dist/web/assets/{gn-CNQPjiZW.js → gn-7PJ3R70o.js} +0 -0
  363. /package/dist/web/assets/{gnuplot-BlHjcPMe.js → gnuplot-V-Pxck7b.js} +0 -0
  364. /package/dist/web/assets/{go-Bn8Ba9uz.js → go-ARF6DprD.js} +0 -0
  365. /package/dist/web/assets/{groovy-C1Y1_F4V.js → groovy-CKGBIPOz.js} +0 -0
  366. /package/dist/web/assets/{gruvbox-dark-hard-DueOsHav.js → gruvbox-dark-hard-Cm9wqdI3.js} +0 -0
  367. /package/dist/web/assets/{gruvbox-dark-medium-DaQjD538.js → gruvbox-dark-medium-Cmsd6dG8.js} +0 -0
  368. /package/dist/web/assets/{gruvbox-dark-soft-CbbX2WOm.js → gruvbox-dark-soft-BBGlGGCz.js} +0 -0
  369. /package/dist/web/assets/{gruvbox-light-hard-E2M70ljZ.js → gruvbox-light-hard-BCR7KmqG.js} +0 -0
  370. /package/dist/web/assets/{gruvbox-light-medium-B_DDLY6h.js → gruvbox-light-medium-CP32bga8.js} +0 -0
  371. /package/dist/web/assets/{gruvbox-light-soft-2SZmyC1F.js → gruvbox-light-soft-qjxk8ps6.js} +0 -0
  372. /package/dist/web/assets/{haskell-D8DeoXtG.js → haskell-C5DzwGRa.js} +0 -0
  373. /package/dist/web/assets/{haxe-BmZm7B0W.js → haxe-C8f_jCVv.js} +0 -0
  374. /package/dist/web/assets/{hcl-CKnBM3ng.js → hcl-DetJkf6E.js} +0 -0
  375. /package/dist/web/assets/{hjson-Ct9t4-ci.js → hjson-DeZhwIdo.js} +0 -0
  376. /package/dist/web/assets/{hlsl-BZSASBqS.js → hlsl-SMIDLRyX.js} +0 -0
  377. /package/dist/web/assets/{horizon-CzX1AZ08.js → horizon-BMAVDxMx.js} +0 -0
  378. /package/dist/web/assets/{horizon-bright-CCVNeRIP.js → horizon-bright-3BaV_VGD.js} +0 -0
  379. /package/dist/web/assets/{houston-DBlJN7xc.js → houston-BWBbEPvb.js} +0 -0
  380. /package/dist/web/assets/{hy-Dyc6h6Xb.js → hy-DcsmE-Nm.js} +0 -0
  381. /package/dist/web/assets/{image-gallery-DujlrjOC.js → image-gallery-Bqb-wjNw.js} +0 -0
  382. /package/dist/web/assets/{imba-Bauru7s2.js → imba-CnkgnYMf.js} +0 -0
  383. /package/dist/web/assets/{ini-SjdwtaTp.js → ini-BnvTdau7.js} +0 -0
  384. /package/dist/web/assets/{java-D2r0ocpf.js → java-BLFACXat.js} +0 -0
  385. /package/dist/web/assets/{javascript-y-lof7iP.js → javascript-BKcFWENk.js} +0 -0
  386. /package/dist/web/assets/{json-DO4LSx5A.js → json-3AJtJADf.js} +0 -0
  387. /package/dist/web/assets/{json5-BEwWf-W3.js → json5-DLyDVlwe.js} +0 -0
  388. /package/dist/web/assets/{jsonc-OBEFv7gP.js → jsonc-B5DgF1jW.js} +0 -0
  389. /package/dist/web/assets/{jsonl-DOyW4Mg3.js → jsonl-CV_O71a2.js} +0 -0
  390. /package/dist/web/assets/{jsonnet-BL5R2CpP.js → jsonnet-DZfPLgYz.js} +0 -0
  391. /package/dist/web/assets/{jssm-ksPusAyg.js → jssm-Cr1zYOsY.js} +0 -0
  392. /package/dist/web/assets/{jsx-B6XEu6O-.js → jsx-VN-JS2_q.js} +0 -0
  393. /package/dist/web/assets/{kanagawa-dragon-JBVe_ji_.js → kanagawa-dragon-D9CcT3xg.js} +0 -0
  394. /package/dist/web/assets/{kanagawa-lotus-ChprO68_.js → kanagawa-lotus-DLXsB8fr.js} +0 -0
  395. /package/dist/web/assets/{kanagawa-wave-D_uB_R0Q.js → kanagawa-wave-weDs3Wjz.js} +0 -0
  396. /package/dist/web/assets/{kdl-pf5B8N_m.js → kdl-0XF56jnT.js} +0 -0
  397. /package/dist/web/assets/{kotlin-BdF0METC.js → kotlin-BPkH9vzk.js} +0 -0
  398. /package/dist/web/assets/{kusto-BneVP-OU.js → kusto-DMVpADb9.js} +0 -0
  399. /package/dist/web/assets/{laserwave-C4-UPIyG.js → laserwave-Cl6T2cGP.js} +0 -0
  400. /package/dist/web/assets/{lean-BN6Jdymq.js → lean-BvwzQGl1.js} +0 -0
  401. /package/dist/web/assets/{less-Cx-fdlDC.js → less-DI_03xkC.js} +0 -0
  402. /package/dist/web/assets/{lib-CMuBa85c.js → lib-v9x_qXgI.js} +0 -0
  403. /package/dist/web/assets/{libesm-BB74PRsH.js → libesm-dcbw4_18.js} +0 -0
  404. /package/dist/web/assets/{light-plus-DFnnn_iL.js → light-plus-hIiIsQN_.js} +0 -0
  405. /package/dist/web/assets/{llvm-BGxtW0Nw.js → llvm-B5hjjSWc.js} +0 -0
  406. /package/dist/web/assets/{loader-circle-Dpqcv0eS.js → loader-circle-DouKgIXy.js} +0 -0
  407. /package/dist/web/assets/{lock-DldbT31x.js → lock-CNxUGpTk.js} +0 -0
  408. /package/dist/web/assets/{log-kQb4eJnj.js → log-D28raQiE.js} +0 -0
  409. /package/dist/web/assets/{logo-DqAzaq7s.js → logo-6Cdd4ASb.js} +0 -0
  410. /package/dist/web/assets/{luau-Dc2cp-zS.js → luau-CK9nKpOX.js} +0 -0
  411. /package/dist/web/assets/{make-DYg9dwaJ.js → make-CVnxKCB6.js} +0 -0
  412. /package/dist/web/assets/{markdown-O4EPpS3K.js → markdown-B9mMQzD5.js} +0 -0
  413. /package/dist/web/assets/{material-theme-ClP8yfQ2.js → material-theme-BZSs2o0j.js} +0 -0
  414. /package/dist/web/assets/{material-theme-darker-LOK55hGu.js → material-theme-darker-BeAQBzQ_.js} +0 -0
  415. /package/dist/web/assets/{material-theme-lighter-j72tzZqi.js → material-theme-lighter-BAe0IXcs.js} +0 -0
  416. /package/dist/web/assets/{material-theme-ocean-C19LKClA.js → material-theme-ocean-CiPIf0Np.js} +0 -0
  417. /package/dist/web/assets/{material-theme-palenight-DJ5tN2ax.js → material-theme-palenight-DJBb4_XN.js} +0 -0
  418. /package/dist/web/assets/{matlab-DQNZ_NU8.js → matlab-CY0dbp_s.js} +0 -0
  419. /package/dist/web/assets/{mdx-CJayB7XK.js → mdx-DU9zbF7h.js} +0 -0
  420. /package/dist/web/assets/{mermaid-Cb0TRX92.js → mermaid-UmmjGwdh.js} +0 -0
  421. /package/dist/web/assets/{min-dark-CWM8Bo-m.js → min-dark-C59GkzOm.js} +0 -0
  422. /package/dist/web/assets/{min-light-BBR1sjS2.js → min-light-B9QglrdQ.js} +0 -0
  423. /package/dist/web/assets/{mipsasm-Jc7u7YSS.js → mipsasm-Dc6Jsr_D.js} +0 -0
  424. /package/dist/web/assets/{mojo-CTB0cutd.js → mojo-D5ij-nGW.js} +0 -0
  425. /package/dist/web/assets/{monitor-4vWfqzuN.js → monitor-CxPV_fQI.js} +0 -0
  426. /package/dist/web/assets/{monokai-CvM_YNKx.js → monokai-QAtE_yCP.js} +0 -0
  427. /package/dist/web/assets/{moonbit-CbFEfYYM.js → moonbit-BMtIdiTL.js} +0 -0
  428. /package/dist/web/assets/{move-BT38p0kT.js → move-BB_jMHYZ.js} +0 -0
  429. /package/dist/web/assets/{narrat-55QZ_kOG.js → narrat-BvvoKGVn.js} +0 -0
  430. /package/dist/web/assets/{nextflow-groovy-Bg8peyqg.js → nextflow-groovy-CqxtAsnF.js} +0 -0
  431. /package/dist/web/assets/{night-owl-DzS77iPK.js → night-owl-CLoE9kjt.js} +0 -0
  432. /package/dist/web/assets/{night-owl-light-DER7fGnx.js → night-owl-light-BwIFT5Xd.js} +0 -0
  433. /package/dist/web/assets/{nix-BSI8YesN.js → nix-BKlnVT9r.js} +0 -0
  434. /package/dist/web/assets/{nord-9bn7iNWo.js → nord-B7R1KRAK.js} +0 -0
  435. /package/dist/web/assets/{nushell-qDVZn_lw.js → nushell-CzUme-NJ.js} +0 -0
  436. /package/dist/web/assets/{objective-c-Blf-ATos.js → objective-c-D-UKKiOc.js} +0 -0
  437. /package/dist/web/assets/{objective-cpp-Bz8bAq47.js → objective-cpp-BucWn4Fx.js} +0 -0
  438. /package/dist/web/assets/{ocaml-B9heAsc3.js → ocaml-EJCNXMGo.js} +0 -0
  439. /package/dist/web/assets/{odin-BaFYnOwO.js → odin-CEjnRU_3.js} +0 -0
  440. /package/dist/web/assets/{one-dark-pro-D6B_hSe-.js → one-dark-pro-Bv469NX5.js} +0 -0
  441. /package/dist/web/assets/{one-light-Ba-f0wyk.js → one-light-BlzO5RWI.js} +0 -0
  442. /package/dist/web/assets/{openscad-UvYyL2MK.js → openscad-Czl6alLw.js} +0 -0
  443. /package/dist/web/assets/{pascal-B4-qvEoG.js → pascal-BI9fLvG4.js} +0 -0
  444. /package/dist/web/assets/{pkl-B146nqGx.js → pkl-ChQLsyjU.js} +0 -0
  445. /package/dist/web/assets/{plastic-L38t-Id3.js → plastic-DGL3mtdL.js} +0 -0
  446. /package/dist/web/assets/{play-CpCAoQRZ.js → play-DpEEBJTT.js} +0 -0
  447. /package/dist/web/assets/{plsql-ItOG5Cca.js → plsql-BJDjqcWp.js} +0 -0
  448. /package/dist/web/assets/{po-imiCTaNX.js → po-DK5O3OpF.js} +0 -0
  449. /package/dist/web/assets/{poimandres-Bu6sIba9.js → poimandres-CnuqZT4T.js} +0 -0
  450. /package/dist/web/assets/{polar-CTeGoFxV.js → polar-DCsUxSsQ.js} +0 -0
  451. /package/dist/web/assets/{portal-container-context-a399Hd9F.js → portal-container-context-D-495m5C.js} +0 -0
  452. /package/dist/web/assets/{postcss-jvFe6GNE.js → postcss-8UjdFmis.js} +0 -0
  453. /package/dist/web/assets/{powerquery-D9Dw1hJp.js → powerquery-CAp0CGQj.js} +0 -0
  454. /package/dist/web/assets/{powershell-CZqvYQtg.js → powershell-CbBsKZJP.js} +0 -0
  455. /package/dist/web/assets/{prisma-B4D2nO_7.js → prisma-BnNpXCJN.js} +0 -0
  456. /package/dist/web/assets/{prolog-CQbyUcCF.js → prolog-CC-l9E8Y.js} +0 -0
  457. /package/dist/web/assets/{proto-Cwpo0cW1.js → proto-CV_1k2rL.js} +0 -0
  458. /package/dist/web/assets/{puppet-CKxCwBod.js → puppet-2D8HU2bv.js} +0 -0
  459. /package/dist/web/assets/{purescript-B-MyXMMp.js → purescript-BEJAORgU.js} +0 -0
  460. /package/dist/web/assets/{python-D6QbSR2S.js → python-BBdbb7nn.js} +0 -0
  461. /package/dist/web/assets/{qmldir-BpTSVcw_.js → qmldir-D5ZYI4Ie.js} +0 -0
  462. /package/dist/web/assets/{qss-DBc4gbpe.js → qss-BOJVGyXE.js} +0 -0
  463. /package/dist/web/assets/{r-i9jMMi0s.js → r-B5dlEtkj.js} +0 -0
  464. /package/dist/web/assets/{racket-BWNaxU7C.js → racket-BUAEygJ6.js} +0 -0
  465. /package/dist/web/assets/{raku-fqTZLn7L.js → raku-BG0Ss0ID.js} +0 -0
  466. /package/dist/web/assets/{react-X4jUntEf.js → react-B549sK_T.js} +0 -0
  467. /package/dist/web/assets/{red-D-JEcuzA.js → red-C5yyQbtG.js} +0 -0
  468. /package/dist/web/assets/{refresh-cw-B9iv7csb.js → refresh-cw-BQU5g2CY.js} +0 -0
  469. /package/dist/web/assets/{reg-BbirlU1S.js → reg-DHoBWtLf.js} +0 -0
  470. /package/dist/web/assets/{regexp-D8VTFrtg.js → regexp-DDtJrlKv.js} +0 -0
  471. /package/dist/web/assets/{rel-ByQGKiNM.js → rel-CR6WJDRD.js} +0 -0
  472. /package/dist/web/assets/{resolve-theme-Cqb9zWYc.js → resolve-theme-4fexHWl6.js} +0 -0
  473. /package/dist/web/assets/{riscv-B5nqCfLE.js → riscv-2V9tC-nx.js} +0 -0
  474. /package/dist/web/assets/{ron-DOVh_iZR.js → ron-QLTmqGUw.js} +0 -0
  475. /package/dist/web/assets/{rose-pine-jp51ZUzi.js → rose-pine-be0__X3A.js} +0 -0
  476. /package/dist/web/assets/{rose-pine-dawn-B7hkmmWy.js → rose-pine-dawn-n_WQJwsW.js} +0 -0
  477. /package/dist/web/assets/{rose-pine-moon-Ff063yLj.js → rose-pine-moon-t2aLtzgB.js} +0 -0
  478. /package/dist/web/assets/{rosmsg-Cyk7Il8z.js → rosmsg-Ce1JD8vt.js} +0 -0
  479. /package/dist/web/assets/{rotate-ccw-DnuPzJRE.js → rotate-ccw-CpjRo90J.js} +0 -0
  480. /package/dist/web/assets/{rust-DRNM5epF.js → rust-5fHH-ZoW.js} +0 -0
  481. /package/dist/web/assets/{sass-D_cB0-M1.js → sass-DPmGFiOJ.js} +0 -0
  482. /package/dist/web/assets/{save-DxonD4y1.js → save-DYLiVzU8.js} +0 -0
  483. /package/dist/web/assets/{scala-Crl-1kKS.js → scala-BRMVFbZF.js} +0 -0
  484. /package/dist/web/assets/{scheme-DCaWyEJ0.js → scheme-BMSJWevF.js} +0 -0
  485. /package/dist/web/assets/{sdbl-BbXNqwtX.js → sdbl-DTZ-pEHI.js} +0 -0
  486. /package/dist/web/assets/{send-B-t5Mz26.js → send-C_sLFt56.js} +0 -0
  487. /package/dist/web/assets/{shallow-4zAfs2KW.js → shallow-CmPMAPte.js} +0 -0
  488. /package/dist/web/assets/{shellscript-BAgksBhW.js → shellscript-6-9yg5Cz.js} +0 -0
  489. /package/dist/web/assets/{shield-check-CdTiK1j2.js → shield-check-CUI36aSc.js} +0 -0
  490. /package/dist/web/assets/{shield-off-DuetkXKM.js → shield-off-GdkkVhb3.js} +0 -0
  491. /package/dist/web/assets/{slack-dark-GB-ElUW_.js → slack-dark-DKJgiR7e.js} +0 -0
  492. /package/dist/web/assets/{slack-ochin-tU6CFD3S.js → slack-ochin-WjehVgyK.js} +0 -0
  493. /package/dist/web/assets/{smalltalk-DSVGUbtL.js → smalltalk-BChf4SN1.js} +0 -0
  494. /package/dist/web/assets/{snazzy-light-DZhi3PoS.js → snazzy-light-C1WAwOx6.js} +0 -0
  495. /package/dist/web/assets/{solarized-dark-D6AMCco7.js → solarized-dark-BxREZrGx.js} +0 -0
  496. /package/dist/web/assets/{solarized-light-DepuTWSE.js → solarized-light-DG29B1pR.js} +0 -0
  497. /package/dist/web/assets/{solidity-CsxN618l.js → solidity-bDbtGwdR.js} +0 -0
  498. /package/dist/web/assets/{splunk-CF1m0Kkv.js → splunk-Ceqx5njI.js} +0 -0
  499. /package/dist/web/assets/{sql-DvyMDgrB.js → sql-OwMEKbz8.js} +0 -0
  500. /package/dist/web/assets/{ssh-config-a8sDo-Q8.js → ssh-config-WAR3lu2k.js} +0 -0
  501. /package/dist/web/assets/{stylus-DCJ-6AjT.js → stylus-Cjmoz3d8.js} +0 -0
  502. /package/dist/web/assets/{swift-c-ZFxyPP.js → swift-S_ZSBG1q.js} +0 -0
  503. /package/dist/web/assets/{synthwave-84-9NPgBcuv.js → synthwave-84-DiUeWMSh.js} +0 -0
  504. /package/dist/web/assets/{system-verilog-DPD02IOo.js → system-verilog-RRjqPjUV.js} +0 -0
  505. /package/dist/web/assets/{systemd-BUr5HRtU.js → systemd-BD3SkXoU.js} +0 -0
  506. /package/dist/web/assets/{table-FLU8ei8b.js → table-D5Q4ap4W.js} +0 -0
  507. /package/dist/web/assets/{talonscript-BNBQV5YJ.js → talonscript-DWefISOm.js} +0 -0
  508. /package/dist/web/assets/{tasl-BETjpSyu.js → tasl-DCjSS-09.js} +0 -0
  509. /package/dist/web/assets/{tcl-Clp_AZiC.js → tcl-DTYqT_66.js} +0 -0
  510. /package/dist/web/assets/{terraform-B0kZ0lim.js → terraform-CkbP5QnR.js} +0 -0
  511. /package/dist/web/assets/{text-wrap-B05VGRa-.js → text-wrap-DBvQD3kc.js} +0 -0
  512. /package/dist/web/assets/{tokyo-night-oOJ0a7Dk.js → tokyo-night-qVSqO9xy.js} +0 -0
  513. /package/dist/web/assets/{toml-DOPCSumy.js → toml-D9F5Osqs.js} +0 -0
  514. /package/dist/web/assets/{trash-2-CTXlTflF.js → trash-2-By4VMbC4.js} +0 -0
  515. /package/dist/web/assets/{triangle-alert-BRq0VAfd.js → triangle-alert-CUTURFbG.js} +0 -0
  516. /package/dist/web/assets/{tsv-D6qUGf84.js → tsv-C5tpHoxm.js} +0 -0
  517. /package/dist/web/assets/{tsx-B6qYhT6O.js → tsx-CMWkGtGm.js} +0 -0
  518. /package/dist/web/assets/{turtle-C0Rjzw73.js → turtle-Cn4z3xOi.js} +0 -0
  519. /package/dist/web/assets/{typescript-BiydShEu.js → typescript-DSyAEWsn.js} +0 -0
  520. /package/dist/web/assets/{typespec-DjmF6Aww.js → typespec-B93Qap7y.js} +0 -0
  521. /package/dist/web/assets/{typst-CPuscfcL.js → typst-DtlOmFlX.js} +0 -0
  522. /package/dist/web/assets/{use-is-mobile-b94q8RvF.js → use-is-mobile-B9UZNwSm.js} +0 -0
  523. /package/dist/web/assets/{utils-C9OjCrou.js → utils-QHe_Q6UF.js} +0 -0
  524. /package/dist/web/assets/{v-DE4vpLLQ.js → v-Dfzf-szS.js} +0 -0
  525. /package/dist/web/assets/{vala-CmUF2pS5.js → vala-CyiQZQ9f.js} +0 -0
  526. /package/dist/web/assets/{vb-BvcRoX4b.js → vb-C7DGSldd.js} +0 -0
  527. /package/dist/web/assets/{vendor-xterm-K8kpLxdb.js → vendor-xterm-BHKF_Nsf.js} +0 -0
  528. /package/dist/web/assets/{verilog-BGIzyH3R.js → verilog-kF-hClY2.js} +0 -0
  529. /package/dist/web/assets/{vesper-CG7IrHQQ.js → vesper-BmKcADuz.js} +0 -0
  530. /package/dist/web/assets/{vhdl-DMSumomw.js → vhdl-CDnb16KJ.js} +0 -0
  531. /package/dist/web/assets/{viml-Drt7AEmU.js → viml-B8cs6Ady.js} +0 -0
  532. /package/dist/web/assets/{vitesse-black-DmsjfN0b.js → vitesse-black-BR6o9Gto.js} +0 -0
  533. /package/dist/web/assets/{vitesse-dark-Dl55_WeJ.js → vitesse-dark-Dye14-KB.js} +0 -0
  534. /package/dist/web/assets/{vitesse-light-Crbv9aGg.js → vitesse-light-BLpswIxf.js} +0 -0
  535. /package/dist/web/assets/{vyper-CB7psEJj.js → vyper-DU51Z97W.js} +0 -0
  536. /package/dist/web/assets/{wasm-BY1tuM5m.js → wasm-CVqlJAR2.js} +0 -0
  537. /package/dist/web/assets/{wasm-Dck8KPgP.js → wasm-ChtccNfv.js} +0 -0
  538. /package/dist/web/assets/{wenyan-DhewQ68b.js → wenyan-DhCa3hCn.js} +0 -0
  539. /package/dist/web/assets/{wgsl-DlmiP456.js → wgsl-Bj1Ec4vv.js} +0 -0
  540. /package/dist/web/assets/{wikitext-cQCnqNgE.js → wikitext-C90DHNa0.js} +0 -0
  541. /package/dist/web/assets/{wit-S5MaCngB.js → wit-DUgbOD7Z.js} +0 -0
  542. /package/dist/web/assets/{wolfram-Bh8n_9zr.js → wolfram-BgwUqD1I.js} +0 -0
  543. /package/dist/web/assets/{x-YDu4m2Iq.js → x-DiOUR_mp.js} +0 -0
  544. /package/dist/web/assets/{yaml-BoK_17l9.js → yaml-DEQulj-8.js} +0 -0
  545. /package/dist/web/assets/{zenscript-DB3ZFwNJ.js → zenscript-C7Jp7TgT.js} +0 -0
  546. /package/dist/web/assets/{zig-Bv1BWmfT.js → zig-9x4qXYLg.js} +0 -0
@@ -1,191 +1,191 @@
1
- /**
2
- * Edge forwarder — owns the public port and pipes raw TCP to the server's
3
- * current loopback port.
4
- *
5
- * ─── CRITICAL INVARIANT: THIS PROCESS MUST NEVER SPAWN A CHILD ───
6
- *
7
- * On Windows a child spawned with fd stdio inherits every inheritable handle,
8
- * including a listening socket. When such a descendant is orphaned it keeps the
9
- * socket open, so the port stays in LISTEN under a dead PID and can never be
10
- * rebound — a "zombie port". The server hits this constantly because it spawns
11
- * chat/tool/MCP subprocesses; those inherit its listener and wedge its port.
12
- *
13
- * The edge exists precisely because it spawns nothing, so its socket cannot be
14
- * inherited and its port cannot zombie. cloudflared can therefore stay pinned
15
- * to it forever and the public URL stops rotating. Adding any child-process
16
- * call to this file reintroduces the exact bug the edge was built to remove.
17
- *
18
- * Raw TCP, never HTTP: WebSocket upgrades and SSE streams pass through
19
- * untouched, and forwarded requests keep the headers cloudflared set.
20
- */
21
- import net from "node:net";
22
- import { resolveTargetPort, _resetTargetCache } from "./edge-target-resolver.ts";
23
-
24
- /** Delay between attempts while waiting for the server to come back. */
25
- const CONNECT_RETRY_MS = 250;
26
- /**
27
- * How long a client connection waits for a reachable server before giving up.
28
- * During an upgrade the server is briefly absent; refusing instantly would
29
- * surface as a broken page rather than a slow one.
30
- */
31
- const CONNECT_WINDOW_MS = 5000;
32
-
33
- const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
34
-
35
- /** Resolve once the socket is connected; reject on the first connect error. */
36
- function connectUpstream(port: number): Promise<net.Socket> {
37
- return new Promise((resolvePromise, reject) => {
38
- const upstream = net.connect({
39
- port,
40
- host: "127.0.0.1", // never taken from status.json — loopback only
41
- allowHalfOpen: true,
42
- });
43
- const onError = (err: Error) => {
44
- upstream.destroy();
45
- reject(err);
46
- };
47
- upstream.once("error", onError);
48
- upstream.once("connect", () => {
49
- upstream.removeListener("error", onError);
50
- resolvePromise(upstream);
51
- });
52
- });
53
- }
54
-
55
- /**
56
- * Largest amount of client data held while waiting for the server. Bounds the
57
- * memory a slow upload can pin during an upgrade window.
58
- */
59
- const MAX_PENDING_BYTES = 8 * 1024 * 1024;
60
-
61
- /**
62
- * Pipe one client connection to the server.
63
- *
64
- * Resolving the upstream is async, so the first bytes usually arrive before
65
- * there is anywhere to send them. They must be captured by a `data` listener
66
- * attached synchronously on this very tick and replayed once the upstream
67
- * exists.
68
- *
69
- * `socket.pause()` is NOT a substitute — verified against Bun 1.3.13: bytes
70
- * that arrive while paused are lost, and a later `pipe()`/`resume()` does not
71
- * bring them back. A socket with no `data` listener drops them outright too.
72
- * Both variants silently swallowed the first request in testing.
73
- */
74
- async function handleConnection(
75
- client: net.Socket,
76
- connectWindowMs: number,
77
- ): Promise<void> {
78
- const pending: Buffer[] = [];
79
- let pendingBytes = 0;
80
- let clientEnded = false;
81
- const onEarlyData = (chunk: Buffer) => {
82
- pendingBytes += chunk.length;
83
- if (pendingBytes > MAX_PENDING_BYTES) {
84
- client.destroy();
85
- return;
86
- }
87
- pending.push(chunk);
88
- };
89
- client.on("data", onEarlyData);
90
- client.once("end", () => { clientEnded = true; });
91
-
92
- let upstream: net.Socket | null = null;
93
- let clientGone = false;
94
-
95
- // A socket error with no listener is an unhandled 'error' event, which would
96
- // take the whole edge process — and the public port — down with it.
97
- const teardown = () => {
98
- upstream?.destroy();
99
- client.destroy();
100
- };
101
- client.on("error", teardown);
102
- client.once("close", () => { clientGone = true; });
103
-
104
- const deadline = Date.now() + connectWindowMs;
105
- while (!clientGone && Date.now() < deadline) {
106
- const port = resolveTargetPort();
107
- if (port !== null) {
108
- try {
109
- upstream = await connectUpstream(port);
110
- break;
111
- } catch {
112
- // Server not up yet (or moved mid-restart) — re-resolve and retry.
113
- _resetTargetCache();
114
- }
115
- }
116
- await sleep(CONNECT_RETRY_MS);
117
- }
118
-
119
- if (clientGone) return;
120
- if (!upstream) {
121
- client.destroy();
122
- return;
123
- }
124
-
125
- upstream.on("error", teardown);
126
-
127
- // Replay what arrived during the connect, then hand both directions to the
128
- // stream machinery. Removing the listener and piping happen in the same tick,
129
- // so no chunk can slip through the gap.
130
- client.off("data", onEarlyData);
131
- for (const chunk of pending) upstream.write(chunk);
132
- pending.length = 0;
133
-
134
- client.pipe(upstream);
135
- upstream.pipe(client);
136
-
137
- // The client may have half-closed while we were still connecting.
138
- if (clientEnded) upstream.end();
139
- }
140
-
141
- export interface EdgeForwarderOptions {
142
- publicPort: number;
143
- host?: string;
144
- /** How long a client waits for a reachable server. Defaults to 5s. */
145
- connectWindowMs?: number;
146
- }
147
-
148
- /**
149
- * Bind the public port and start forwarding. Resolves once listening so the
150
- * caller can fail fast when the port is unavailable.
151
- */
152
- export function startEdgeForwarder(
153
- opts: EdgeForwarderOptions,
154
- ): Promise<net.Server> {
155
- const host = opts.host ?? "0.0.0.0";
156
- const connectWindowMs = opts.connectWindowMs ?? CONNECT_WINDOW_MS;
157
- return new Promise((resolvePromise, reject) => {
158
- // allowHalfOpen keeps the other direction alive when one side sends FIN —
159
- // long-lived chat WS and SSE streams depend on it.
160
- const server = net.createServer({ allowHalfOpen: true }, (client) => {
161
- void handleConnection(client, connectWindowMs);
162
- });
163
- server.once("error", reject);
164
- server.listen(opts.publicPort, host, () => {
165
- server.removeListener("error", reject);
166
- resolvePromise(server);
167
- });
168
- });
169
- }
170
-
171
- // ─── Process entry ─────────────────────────────────────────────────────
172
- // Mirrors the `__serve__` guard in src/server/index.ts: the supervisor
173
- // re-invokes the binary as `<bin> __edge__ <publicPort> <host>`.
174
- if (process.argv.includes("__edge__")) {
175
- const idx = process.argv.indexOf("__edge__");
176
- const publicPort = parseInt(process.argv[idx + 1] ?? "", 10);
177
- const host = process.argv[idx + 2] ?? "0.0.0.0";
178
-
179
- if (!Number.isInteger(publicPort) || publicPort <= 0 || publicPort > 65535) {
180
- process.stderr.write(`[edge] Invalid public port: ${process.argv[idx + 1]}\n`);
181
- process.exit(2);
182
- }
183
-
184
- try {
185
- await startEdgeForwarder({ publicPort, host });
186
- process.stderr.write(`[edge] Listening on ${host}:${publicPort}\n`);
187
- } catch (err) {
188
- process.stderr.write(`[edge] Failed to bind ${host}:${publicPort}: ${err}\n`);
189
- process.exit(1);
190
- }
191
- }
1
+ /**
2
+ * Edge forwarder — owns the public port and pipes raw TCP to the server's
3
+ * current loopback port.
4
+ *
5
+ * ─── CRITICAL INVARIANT: THIS PROCESS MUST NEVER SPAWN A CHILD ───
6
+ *
7
+ * On Windows a child spawned with fd stdio inherits every inheritable handle,
8
+ * including a listening socket. When such a descendant is orphaned it keeps the
9
+ * socket open, so the port stays in LISTEN under a dead PID and can never be
10
+ * rebound — a "zombie port". The server hits this constantly because it spawns
11
+ * chat/tool/MCP subprocesses; those inherit its listener and wedge its port.
12
+ *
13
+ * The edge exists precisely because it spawns nothing, so its socket cannot be
14
+ * inherited and its port cannot zombie. cloudflared can therefore stay pinned
15
+ * to it forever and the public URL stops rotating. Adding any child-process
16
+ * call to this file reintroduces the exact bug the edge was built to remove.
17
+ *
18
+ * Raw TCP, never HTTP: WebSocket upgrades and SSE streams pass through
19
+ * untouched, and forwarded requests keep the headers cloudflared set.
20
+ */
21
+ import net from "node:net";
22
+ import { resolveTargetPort, _resetTargetCache } from "./edge-target-resolver.ts";
23
+
24
+ /** Delay between attempts while waiting for the server to come back. */
25
+ const CONNECT_RETRY_MS = 250;
26
+ /**
27
+ * How long a client connection waits for a reachable server before giving up.
28
+ * During an upgrade the server is briefly absent; refusing instantly would
29
+ * surface as a broken page rather than a slow one.
30
+ */
31
+ const CONNECT_WINDOW_MS = 5000;
32
+
33
+ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
34
+
35
+ /** Resolve once the socket is connected; reject on the first connect error. */
36
+ function connectUpstream(port: number): Promise<net.Socket> {
37
+ return new Promise((resolvePromise, reject) => {
38
+ const upstream = net.connect({
39
+ port,
40
+ host: "127.0.0.1", // never taken from status.json — loopback only
41
+ allowHalfOpen: true,
42
+ });
43
+ const onError = (err: Error) => {
44
+ upstream.destroy();
45
+ reject(err);
46
+ };
47
+ upstream.once("error", onError);
48
+ upstream.once("connect", () => {
49
+ upstream.removeListener("error", onError);
50
+ resolvePromise(upstream);
51
+ });
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Largest amount of client data held while waiting for the server. Bounds the
57
+ * memory a slow upload can pin during an upgrade window.
58
+ */
59
+ const MAX_PENDING_BYTES = 8 * 1024 * 1024;
60
+
61
+ /**
62
+ * Pipe one client connection to the server.
63
+ *
64
+ * Resolving the upstream is async, so the first bytes usually arrive before
65
+ * there is anywhere to send them. They must be captured by a `data` listener
66
+ * attached synchronously on this very tick and replayed once the upstream
67
+ * exists.
68
+ *
69
+ * `socket.pause()` is NOT a substitute — verified against Bun 1.3.13: bytes
70
+ * that arrive while paused are lost, and a later `pipe()`/`resume()` does not
71
+ * bring them back. A socket with no `data` listener drops them outright too.
72
+ * Both variants silently swallowed the first request in testing.
73
+ */
74
+ async function handleConnection(
75
+ client: net.Socket,
76
+ connectWindowMs: number,
77
+ ): Promise<void> {
78
+ const pending: Buffer[] = [];
79
+ let pendingBytes = 0;
80
+ let clientEnded = false;
81
+ const onEarlyData = (chunk: Buffer) => {
82
+ pendingBytes += chunk.length;
83
+ if (pendingBytes > MAX_PENDING_BYTES) {
84
+ client.destroy();
85
+ return;
86
+ }
87
+ pending.push(chunk);
88
+ };
89
+ client.on("data", onEarlyData);
90
+ client.once("end", () => { clientEnded = true; });
91
+
92
+ let upstream: net.Socket | null = null;
93
+ let clientGone = false;
94
+
95
+ // A socket error with no listener is an unhandled 'error' event, which would
96
+ // take the whole edge process — and the public port — down with it.
97
+ const teardown = () => {
98
+ upstream?.destroy();
99
+ client.destroy();
100
+ };
101
+ client.on("error", teardown);
102
+ client.once("close", () => { clientGone = true; });
103
+
104
+ const deadline = Date.now() + connectWindowMs;
105
+ while (!clientGone && Date.now() < deadline) {
106
+ const port = resolveTargetPort();
107
+ if (port !== null) {
108
+ try {
109
+ upstream = await connectUpstream(port);
110
+ break;
111
+ } catch {
112
+ // Server not up yet (or moved mid-restart) — re-resolve and retry.
113
+ _resetTargetCache();
114
+ }
115
+ }
116
+ await sleep(CONNECT_RETRY_MS);
117
+ }
118
+
119
+ if (clientGone) return;
120
+ if (!upstream) {
121
+ client.destroy();
122
+ return;
123
+ }
124
+
125
+ upstream.on("error", teardown);
126
+
127
+ // Replay what arrived during the connect, then hand both directions to the
128
+ // stream machinery. Removing the listener and piping happen in the same tick,
129
+ // so no chunk can slip through the gap.
130
+ client.off("data", onEarlyData);
131
+ for (const chunk of pending) upstream.write(chunk);
132
+ pending.length = 0;
133
+
134
+ client.pipe(upstream);
135
+ upstream.pipe(client);
136
+
137
+ // The client may have half-closed while we were still connecting.
138
+ if (clientEnded) upstream.end();
139
+ }
140
+
141
+ export interface EdgeForwarderOptions {
142
+ publicPort: number;
143
+ host?: string;
144
+ /** How long a client waits for a reachable server. Defaults to 5s. */
145
+ connectWindowMs?: number;
146
+ }
147
+
148
+ /**
149
+ * Bind the public port and start forwarding. Resolves once listening so the
150
+ * caller can fail fast when the port is unavailable.
151
+ */
152
+ export function startEdgeForwarder(
153
+ opts: EdgeForwarderOptions,
154
+ ): Promise<net.Server> {
155
+ const host = opts.host ?? "0.0.0.0";
156
+ const connectWindowMs = opts.connectWindowMs ?? CONNECT_WINDOW_MS;
157
+ return new Promise((resolvePromise, reject) => {
158
+ // allowHalfOpen keeps the other direction alive when one side sends FIN —
159
+ // long-lived chat WS and SSE streams depend on it.
160
+ const server = net.createServer({ allowHalfOpen: true }, (client) => {
161
+ void handleConnection(client, connectWindowMs);
162
+ });
163
+ server.once("error", reject);
164
+ server.listen(opts.publicPort, host, () => {
165
+ server.removeListener("error", reject);
166
+ resolvePromise(server);
167
+ });
168
+ });
169
+ }
170
+
171
+ // ─── Process entry ─────────────────────────────────────────────────────
172
+ // Mirrors the `__serve__` guard in src/server/index.ts: the supervisor
173
+ // re-invokes the binary as `<bin> __edge__ <publicPort> <host>`.
174
+ if (process.argv.includes("__edge__")) {
175
+ const idx = process.argv.indexOf("__edge__");
176
+ const publicPort = parseInt(process.argv[idx + 1] ?? "", 10);
177
+ const host = process.argv[idx + 2] ?? "0.0.0.0";
178
+
179
+ if (!Number.isInteger(publicPort) || publicPort <= 0 || publicPort > 65535) {
180
+ process.stderr.write(`[edge] Invalid public port: ${process.argv[idx + 1]}\n`);
181
+ process.exit(2);
182
+ }
183
+
184
+ try {
185
+ await startEdgeForwarder({ publicPort, host });
186
+ process.stderr.write(`[edge] Listening on ${host}:${publicPort}\n`);
187
+ } catch (err) {
188
+ process.stderr.write(`[edge] Failed to bind ${host}:${publicPort}: ${err}\n`);
189
+ process.exit(1);
190
+ }
191
+ }
@@ -2,6 +2,7 @@ import { resolve, sep } from "node:path";
2
2
  import { homedir } from "node:os";
3
3
  import { getPpmDir } from "./ppm-dir.ts";
4
4
  import { getUploadsDir } from "./chat-upload-storage.service.ts";
5
+ import { getBackupsDir } from "./db-backup/db-backup-paths.ts";
5
6
  import { realPathOrSelf } from "./fs-ops/fs-real-path.ts";
6
7
 
7
8
  /**
@@ -11,6 +12,9 @@ import { realPathOrSelf } from "./fs-ops/fs-real-path.ts";
11
12
  * cert). Split out of `fs-path-guard.service.ts` so this one seam — "is this
12
13
  * a credential path" — stays a single file instead of growing alongside the
13
14
  * platform-allowlist and protected-root logic that lives there.
15
+ *
16
+ * Three roots, not two: the PPM config DB, `~/.cloudflared`, and the database
17
+ * snapshot directory, which holds full copies of the first one.
14
18
  */
15
19
 
16
20
  /** Case-insensitive prefix test on Windows/macOS-style paths. */
@@ -50,9 +54,23 @@ export function isCloudflaredDirPath(resolved: string): boolean {
50
54
  return isInside(resolved, resolve(homedir(), ".cloudflared"));
51
55
  }
52
56
 
57
+ /**
58
+ * True when the path is the database-snapshot directory or anything inside it.
59
+ *
60
+ * A snapshot is a byte-for-byte copy of the config database, so it carries the
61
+ * same provider keys, encrypted accounts, and auth token. In production that
62
+ * directory deliberately sits OUTSIDE `~/.ppm` (so it survives a wipe of the
63
+ * PPM directory), which means `isPpmDirPath` does not cover it — without this
64
+ * branch, moving snapshots out of the PPM directory would have quietly opened
65
+ * a credential-read hole through the generic file routes.
66
+ */
67
+ export function isDbBackupsDirPath(resolved: string): boolean {
68
+ return isInside(resolved, getBackupsDir());
69
+ }
70
+
53
71
  /** True when the path holds credential material a generic file route must never serve or relocate. */
54
72
  export function isCredentialPath(resolved: string): boolean {
55
- return isPpmDirPath(resolved) || isCloudflaredDirPath(resolved);
73
+ return isPpmDirPath(resolved) || isCloudflaredDirPath(resolved) || isDbBackupsDirPath(resolved);
56
74
  }
57
75
 
58
76
  /**
@@ -31,10 +31,26 @@ export async function fetchDnsRecords(zoneID: string, apiToken: string, name: st
31
31
  return Array.isArray(json.result) ? json.result : [];
32
32
  }
33
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> {
34
+ export interface ExistingTunnel {
35
+ id: string;
36
+ /** Connectors currently registered on it — non-zero means something is serving it right now. */
37
+ activeConnections: number;
38
+ }
39
+
40
+ /** An existing, non-deleted tunnel named `name` — null if none exists yet. */
41
+ export async function fetchTunnelByName(
42
+ accountID: string,
43
+ apiToken: string,
44
+ name: string,
45
+ ): Promise<ExistingTunnel | null> {
36
46
  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);
47
+ const json = await cfGet<{ success: true; result?: Array<{ id?: string; connections?: unknown[] }> }>(url, apiToken);
38
48
  const found = (json.result ?? []).find((t) => typeof t.id === "string" && t.id);
39
- return found?.id ?? null;
49
+ if (!found?.id) return null;
50
+ return { id: found.id, activeConnections: Array.isArray(found.connections) ? found.connections.length : 0 };
51
+ }
52
+
53
+ /** UUID of an existing, non-deleted tunnel named `name` — null if none exists yet. */
54
+ export async function fetchTunnelIdByName(accountID: string, apiToken: string, name: string): Promise<string | null> {
55
+ return (await fetchTunnelByName(accountID, apiToken, name))?.id ?? null;
40
56
  }
@@ -98,7 +98,7 @@ export async function startLogin(opts: { relogin?: boolean } = {}): Promise<Logi
98
98
  // itself failed to run, so cert.pem is left untouched and the user
99
99
  // gets a clear, retryable error instead of an unwanted fresh login.
100
100
  if (liveCheck === "unreachable") {
101
- setTerminal("error", "không kết nối được Cloudflare để kiểm tra đăng nhập thử lại");
101
+ setTerminal("error", "could not reach Cloudflare to check the sign-intry again");
102
102
  return getLoginSnapshot();
103
103
  }
104
104
 
@@ -117,7 +117,7 @@ export async function startLogin(opts: { relogin?: boolean } = {}): Promise<Logi
117
117
  renameCertAside();
118
118
  }
119
119
  } else {
120
- // Explicit ?relogin=1 — the user deliberately pressed "Đăng nhập lại",
120
+ // Explicit ?relogin=1 — the user deliberately pressed "Sign in again",
121
121
  // so this renames unconditionally regardless of network reachability.
122
122
  renameCertAside();
123
123
  }
@@ -8,10 +8,10 @@
8
8
  * passed via `--token-file`, so it can't leak through `ppm status`, a process
9
9
  * list, or a crash dump that captures cmdlines.
10
10
  */
11
- import { resolve } from "node:path";
11
+ import { resolve, basename } from "node:path";
12
12
  import { hostname as osHostname } from "node:os";
13
13
  import { existsSync, mkdirSync, writeFileSync, chmodSync } from "node:fs";
14
- import { getPpmDir } from "../ppm-dir.ts";
14
+ import { getPpmDir, isIsolatedPpmHome } from "../ppm-dir.ts";
15
15
  import { getOriginCertPath } from "./cloudflared-cert.ts";
16
16
 
17
17
  const namedTunnelConfigPath = () => resolve(getPpmDir(), "cloudflared-named.yml");
@@ -106,5 +106,19 @@ export function namedRunArgs(token: string, port: number): string[] {
106
106
  /** Stable, DNS-safe tunnel name derived from this machine's hostname. */
107
107
  export function tunnelNameForHost(): string {
108
108
  const sanitized = osHostname().toLowerCase().replace(/[^a-z0-9-]/g, "-");
109
- return `ppm-${sanitized}`.slice(0, 32);
109
+ // A second PPM on the same machine (a dev instance under its own PPM_HOME)
110
+ // would otherwise derive the identical name, "reuse" the production tunnel,
111
+ // and register a second connector on it — Cloudflare then load-balances the
112
+ // production hostname across both, so the domain intermittently serves the
113
+ // wrong instance. Observed live. The PPM_HOME directory is what actually
114
+ // distinguishes the two, so it belongs in the name.
115
+ const suffix = ppmHomeSuffix();
116
+ return `ppm-${sanitized}${suffix}`.slice(0, 32);
117
+ }
118
+
119
+ /** `-<dir>` for a non-default PPM_HOME, empty for the real `~/.ppm`. */
120
+ function ppmHomeSuffix(): string {
121
+ if (!isIsolatedPpmHome()) return "";
122
+ const dir = basename(getPpmDir()).toLowerCase().replace(/^\.+/, "").replace(/[^a-z0-9-]/g, "-");
123
+ return dir ? `-${dir}` : "-alt";
110
124
  }
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { readOriginCertState } from "./cloudflared-cert.ts";
10
10
  import { fetchZoneName } from "./cloudflare-zone-api.ts";
11
- import { fetchDnsRecords, fetchTunnelIdByName } from "./cloudflare-dns-api.ts";
11
+ import { fetchDnsRecords, fetchTunnelByName } from "./cloudflare-dns-api.ts";
12
12
  import { proposeHostname, validateHostname } from "./hostname-rules.ts";
13
13
  import { createTunnelArgs, routeDnsArgs, tunnelTokenArgs, tunnelNameForHost } from "./named-tunnel-args.ts";
14
14
  import { runCloudflared } from "./cloudflared-exec.ts";
@@ -110,7 +110,25 @@ async function runSetupInner(hostname: string): Promise<SetupOutcome> {
110
110
  const tunnelName = tunnelNameForHost();
111
111
 
112
112
  broadcastGlobalEvent({ type: "tunnel:setup_step", step: "precheck", message: "checking for a DNS collision" });
113
- const existingId = await fetchTunnelIdByName(accountID, apiToken, tunnelName);
113
+ const existing = await fetchTunnelByName(accountID, apiToken, tunnelName);
114
+ const existingId = existing?.id ?? null;
115
+
116
+ // Reusing a same-named tunnel is normal (re-running setup, changing the
117
+ // prefix). Reusing one that ANOTHER PPM is currently serving is not: both
118
+ // connectors register on the same tunnel and Cloudflare spreads requests
119
+ // across them, so each hostname on that tunnel intermittently answers from
120
+ // the wrong instance. Only the installation that already owns this tunnel in
121
+ // its own config may take it over.
122
+ // Read the raw row, not `resolveTunnelConfig`: that degrades to "quick" when
123
+ // named mode is off, which would hide the fact that this installation still
124
+ // owns the tunnel and would wrongly block its own re-setup.
125
+ const ownsTunnelAlready = configService.get("tunnel")?.namedTunnelName === tunnelName;
126
+ if (existing && existing.activeConnections > 0 && !ownsTunnelAlready) {
127
+ throw new SetupError(
128
+ 409,
129
+ `a tunnel named "${tunnelName}" is already running elsewhere — stop that PPM instance first, or run this one with its own PPM_HOME`,
130
+ );
131
+ }
114
132
  const records = await fetchDnsRecords(zoneID, apiToken, hostname);
115
133
  let overwrite = false;
116
134
  if (records.length > 0) {