@hienlh/ppm 0.18.13 → 0.18.15

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 (542) hide show
  1. package/.rdtest2-cf.out +0 -0
  2. package/CHANGELOG.md +23 -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 +18 -0
  6. package/assets/skills/ppm/references/http-api.md +8 -1
  7. package/dist/web/assets/{adaptive-context-menu-CbgPYQmj.js → adaptive-context-menu-ChBZWLLq.js} +1 -1
  8. package/dist/web/assets/{ai-resource-editor-unM2HNmg.js → ai-resource-editor-C8aAY8Po.js} +1 -1
  9. package/dist/web/assets/{ai-settings-section-CdktdrwF.js → ai-settings-section-ERu1WiZc.js} +1 -1
  10. package/dist/web/assets/{angular-html-DcpbdcNG.js → angular-html-CXgHIQ2U.js} +1 -1
  11. package/dist/web/assets/{angular-ts-Bv_WIk34.js → angular-ts-C8renMV9.js} +1 -1
  12. package/dist/web/assets/api-client-WpZ2FDHg.js +1 -0
  13. package/dist/web/assets/api-settings-BgSEE6et.js +1 -0
  14. package/dist/web/assets/{api-settings-BdCtex9i.js → api-settings-R59c66DE.js} +1 -1
  15. package/dist/web/assets/{api-themes-pRRoLvgD.js → api-themes-DKxrwJda.js} +1 -1
  16. package/dist/web/assets/{apl-D5_lLszu.js → apl-DkGH8laI.js} +1 -1
  17. package/dist/web/assets/architecture-PBZL5I3N-CCalSKAt.js +1 -0
  18. package/dist/web/assets/{astro-CBHwddYL.js → astro-tHUd8Zlg.js} +1 -1
  19. package/dist/web/assets/{audio-preview-BD1iDovf.js → audio-preview-B_C6Dpvz.js} +1 -1
  20. package/dist/web/assets/{blade-DGe1d2Pq.js → blade-BbKpjIYK.js} +1 -1
  21. package/dist/web/assets/{bsl-2lyAzRJA.js → bsl-DA0UYto0.js} +1 -1
  22. package/dist/web/assets/{button-2WCAS8dI.js → button-DBSpDPS0.js} +1 -1
  23. package/dist/web/assets/{cairo-DjzZeJrq.js → cairo-79ayucbB.js} +1 -1
  24. package/dist/web/assets/chat-tab-BraiJsYG.js +15 -0
  25. package/dist/web/assets/{cobol-cC3XIuAJ.js → cobol-DSS2EOYo.js} +1 -1
  26. package/dist/web/assets/code-editor-lhmLNhXu.js +10 -0
  27. package/dist/web/assets/{coffee-4eGboVoU.js → coffee-DvSxq2iD.js} +1 -1
  28. package/dist/web/assets/{conflict-editor-DdJag4QK.js → conflict-editor-C4VCv51R.js} +1 -1
  29. package/dist/web/assets/{context-menu-BO1c9jKy.js → context-menu-BpwUny-o.js} +1 -1
  30. package/dist/web/assets/{cpp-DF9_G-OU.js → cpp-BhWZ-FSn.js} +1 -1
  31. package/dist/web/assets/{crystal-OavZlzcm.js → crystal-yP2OeF-f.js} +1 -1
  32. package/dist/web/assets/{csv-preview-DnXwouF3.js → csv-preview-D46W_icb.js} +1 -1
  33. package/dist/web/assets/{data-grid-overlay-editor-B9Gpjm3l.js → data-grid-overlay-editor-DeGkPYtA.js} +1 -1
  34. package/dist/web/assets/{database-viewer-Dk8oGyZx.js → database-viewer-DFI4YtrH.js} +1 -1
  35. package/dist/web/assets/{dialog-B-l2ZxVm.js → dialog-RIpeb87v.js} +1 -1
  36. package/dist/web/assets/diff-viewer-eaNOLcBL.js +4 -0
  37. package/dist/web/assets/{docx-preview-DAdHPlhZ.js → docx-preview-Cbj-pOK3.js} +1 -1
  38. package/dist/web/assets/{edge-TzYh8OlN.js → edge-D3K9cd-p.js} +1 -1
  39. package/dist/web/assets/{edit-diff-preview-DlwP3UIU.js → edit-diff-preview-DDqy3KLA.js} +1 -1
  40. package/dist/web/assets/{elixir-Cy3RhKKH.js → elixir-D4PBmsLp.js} +1 -1
  41. package/dist/web/assets/{elm-BTBZdweK.js → elm-DSpXjuaH.js} +1 -1
  42. package/dist/web/assets/{erb-dv1mI0y4.js → erb-CaYZMTJW.js} +1 -1
  43. package/dist/web/assets/{erlang-DjeiDrkl.js → erlang-DX5BFqJj.js} +1 -1
  44. package/dist/web/assets/{esm-FRwvcesu.js → esm-Cv7HsFnk.js} +1 -1
  45. package/dist/web/assets/explorer-body-BxbSttfl.js +2 -0
  46. package/dist/web/assets/explorer-window-content-DUqQCC7A.js +1 -0
  47. package/dist/web/assets/{extension-store-DUaZINyC.js → extension-store-B1EClUAr.js} +1 -1
  48. package/dist/web/assets/{extension-webview-W0qRfaAU.js → extension-webview-BaGvDk8v.js} +1 -1
  49. package/dist/web/assets/{file-store-yReVbR8q.js → file-store-m9crh6uq.js} +1 -1
  50. package/dist/web/assets/{fortran-fixed-form-BCH9DAIJ.js → fortran-fixed-form-B0bAufUe.js} +1 -1
  51. package/dist/web/assets/{fsharp-C93NYKYD.js → fsharp-C2AL2b1M.js} +1 -1
  52. package/dist/web/assets/{gdresource-BcIEE2eX.js → gdresource-DRQsDMNI.js} +1 -1
  53. package/dist/web/assets/{git-commit-BVjlXEy8.js → git-commit-GtBdnrzk.js} +1 -1
  54. package/dist/web/assets/{git-log-panel-2_4HuP47.js → git-log-panel-hwV1flSO.js} +1 -1
  55. package/dist/web/assets/{git-rebase-Bj_21B1V.js → git-rebase-h_0CGA6F.js} +1 -1
  56. package/dist/web/assets/gitGraph-HDMCJU4V-DAUrJCqi.js +1 -0
  57. package/dist/web/assets/glide-data-grid-YsN-xm6x.js +138 -0
  58. package/dist/web/assets/{glimmer-js-C20yj4Zv.js → glimmer-js-Bxwu93bS.js} +1 -1
  59. package/dist/web/assets/{glimmer-ts-ipbYC7kh.js → glimmer-ts-CIUYdR4E.js} +1 -1
  60. package/dist/web/assets/{glsl-Dovazu3t.js → glsl-CfxLI5jn.js} +1 -1
  61. package/dist/web/assets/{graphql-DpMlM7hC.js → graphql-CstqDPde.js} +1 -1
  62. package/dist/web/assets/group-chat-tab-B7mJO1gG.js +1 -0
  63. package/dist/web/assets/{hack-gc7LCBuV.js → hack-Bjqnssb5.js} +1 -1
  64. package/dist/web/assets/{haml-vtlFm9gm.js → haml-lVc-DfbM.js} +1 -1
  65. package/dist/web/assets/hand-x0ejq3fS.js +1 -0
  66. package/dist/web/assets/{handlebars-DGKhpCji.js → handlebars-Yy6fPQI_.js} +1 -1
  67. package/dist/web/assets/{html-derivative-C3oTSSCN.js → html-derivative-CchzCY1z.js} +1 -1
  68. package/dist/web/assets/{html-DvfhQbXb.js → html-lqtcy6-2.js} +1 -1
  69. package/dist/web/assets/{http-SG6zLTo7.js → http-ChclRrko.js} +1 -1
  70. package/dist/web/assets/{hurl-BWXsYlqW.js → hurl-CoOK7sXa.js} +1 -1
  71. package/dist/web/assets/{hxml-DUdMzaoh.js → hxml-fZSZ7Iyt.js} +1 -1
  72. package/dist/web/assets/{image-preview-DxgFOV1T.js → image-preview-ClAcqDlL.js} +1 -1
  73. package/dist/web/assets/index-CtJbMNNI.css +2 -0
  74. package/dist/web/assets/index-H_gScEOk.js +52 -0
  75. package/dist/web/assets/info-3K5VOQVL-Cx1ZfJbp.js +1 -0
  76. package/dist/web/assets/{input-DuAF-Stj.js → input-DXPgUtxi.js} +1 -1
  77. package/dist/web/assets/{jinja-955w9RKM.js → jinja-Y0w_KBdE.js} +1 -1
  78. package/dist/web/assets/{jison-WWfqPo50.js → jison-BILj3F2e.js} +1 -1
  79. package/dist/web/assets/{julia-BFrfnaNl.js → julia-DRfOPDts.js} +1 -1
  80. package/dist/web/assets/{just-BrZdyR_I.js → just-n3U_oODO.js} +1 -1
  81. package/dist/web/assets/keybindings-store-BqctreaI.js +1 -0
  82. package/dist/web/assets/{keybindings-store-0Wqfa7Qp.js → keybindings-store-D-CHdq-G.js} +1 -1
  83. package/dist/web/assets/keyboard-DBBaoQSu.js +1 -0
  84. package/dist/web/assets/{latex-CwtPPASD.js → latex-D6gztEOL.js} +1 -1
  85. package/dist/web/assets/{liquid-CYZuvDDd.js → liquid-DPqCEVb9.js} +1 -1
  86. package/dist/web/assets/{lua-BV095ZyY.js → lua-I40KPUm-.js} +1 -1
  87. package/dist/web/assets/markdown-renderer-BwOkiZC1.js +1 -0
  88. package/dist/web/assets/{markdown-renderer-CnRcKK3d.js → markdown-renderer-oJ9jPdQ2.js} +3 -3
  89. package/dist/web/assets/{marko-CNeljZ1C.js → marko-DMjsThSW.js} +1 -1
  90. package/dist/web/assets/{mdc-JfGapShy.js → mdc-CN2alsbo.js} +1 -1
  91. package/dist/web/assets/{media-url-Bm4PsduE.js → media-url-COUvasjn.js} +1 -1
  92. package/dist/web/assets/mobile-bottom-sheet-Cj1YBq-c.js +1 -0
  93. package/dist/web/assets/mobile-explorer-sheet-D1nM0Ik1.js +1 -0
  94. package/dist/web/assets/{nextflow-DD5C8nqX.js → nextflow-CGa4JeZ6.js} +1 -1
  95. package/dist/web/assets/{nginx-Ca7B__x_.js → nginx-CmqrIj84.js} +1 -1
  96. package/dist/web/assets/{nim-B8FogsDw.js → nim-CqhmTeMa.js} +1 -1
  97. package/dist/web/assets/notification-store-n-XEQ5pH.js +1 -0
  98. package/dist/web/assets/{number-overlay-editor-DG9xJwEG.js → number-overlay-editor-BEzJ1YeD.js} +1 -1
  99. package/dist/web/assets/{overview-panel-s53bnl0q.js → overview-panel-Bf9Gn8qV.js} +1 -1
  100. package/dist/web/assets/packet-RMMSAZCW-Bqwiy7dz.js +1 -0
  101. package/dist/web/assets/{panel-store-B4rijFKN.js → panel-store-UWCH6B-_.js} +1 -1
  102. package/dist/web/assets/{pdf-preview-DYt3aBzM.js → pdf-preview-BMHM5cR2.js} +1 -1
  103. package/dist/web/assets/{perl-BR78hS8R.js → perl-BOwpb6Tb.js} +1 -1
  104. package/dist/web/assets/{php-DXBOMC9z.js → php-wT9fVxxu.js} +1 -1
  105. package/dist/web/assets/pie-UPGHQEXC-BXSJ2God.js +1 -0
  106. package/dist/web/assets/{postgres-viewer-BvoWSuoZ.js → postgres-viewer-D8rfEY5b.js} +3 -3
  107. package/dist/web/assets/process-table-CsQ-hDJr.js +1 -0
  108. package/dist/web/assets/{project-store-BuY7rxn_.js → project-store-CKAOr23O.js} +1 -1
  109. package/dist/web/assets/{pug-CvTwVVKV.js → pug-DdlNnj-p.js} +1 -1
  110. package/dist/web/assets/{qml-CPSoZ1-B.js → qml-C5zBdydf.js} +1 -1
  111. package/dist/web/assets/radar-KQ55EAFF-D6XrM7HH.js +1 -0
  112. package/dist/web/assets/{razor-BUA1YKtF.js → razor-DqcJX4iq.js} +1 -1
  113. package/dist/web/assets/remote-desktop-mobile-sheet-CTiipn4k.js +2 -0
  114. package/dist/web/assets/remote-desktop-mobile-view-Cf5B0dKo.js +1 -0
  115. package/dist/web/assets/remote-desktop-stats-overlay-vhJBIW7s.js +1 -0
  116. package/dist/web/assets/remote-desktop-window-content-wCJUfI6G.js +1 -0
  117. package/dist/web/assets/{rst-BJsq7ISz.js → rst-DiMiMYUO.js} +1 -1
  118. package/dist/web/assets/{ruby-DSDMeYf5.js → ruby-DK62iflA.js} +1 -1
  119. package/dist/web/assets/{run-in-terminal-C9xpskFn.js → run-in-terminal-DJSL6nwZ.js} +1 -1
  120. package/dist/web/assets/{sas-B1nJOjbN.js → sas-6QPGJRa5.js} +1 -1
  121. package/dist/web/assets/{scroll-area-DBcG1fQo.js → scroll-area-DXGXeiRz.js} +1 -1
  122. package/dist/web/assets/{scss-BZFyRiQn.js → scss-DmUqh48_.js} +1 -1
  123. package/dist/web/assets/{send-to-chat-BIg70MUZ.js → send-to-chat-BOony0sO.js} +1 -1
  124. package/dist/web/assets/settings-store-BpcY5aYE.js +2 -0
  125. package/dist/web/assets/settings-tab-BVqRO01j.js +1 -0
  126. package/dist/web/assets/settings-tab-FH581A_i.js +3 -0
  127. package/dist/web/assets/{shaderlab-laINRXBo.js → shaderlab-DopFR1mi.js} +1 -1
  128. package/dist/web/assets/{shellsession-BpOOvWVR.js → shellsession-Bp_TGuXb.js} +1 -1
  129. package/dist/web/assets/{soy-BW4d91Sa.js → soy-DEB9KEFb.js} +1 -1
  130. package/dist/web/assets/{sparql-BsvvyoHn.js → sparql-C8lEwO1p.js} +1 -1
  131. package/dist/web/assets/{sql-query-editor-qQce5QJ6.js → sql-query-editor-MMfk0Kbz.js} +1 -1
  132. package/dist/web/assets/{sqlite-viewer-yhc0YNvu.js → sqlite-viewer-vglzrgnZ.js} +1 -1
  133. package/dist/web/assets/{stata-K-dZInUt.js → stata-DVep4apN.js} +1 -1
  134. package/dist/web/assets/{surrealql-CpNp2sgf.js → surrealql-fSlJApOA.js} +1 -1
  135. package/dist/web/assets/{svelte-BG6iBA8R.js → svelte-DezWBEDi.js} +1 -1
  136. package/dist/web/assets/{switch-_NcrktLe.js → switch-DqL_ZjB_.js} +1 -1
  137. package/dist/web/assets/system-monitor-body-BYUfyF8f.js +2 -0
  138. package/dist/web/assets/system-monitor-tab-CKGXB8lH.js +1 -0
  139. package/dist/web/assets/{system-monitor-window-content-CaOhD0AU.js → system-monitor-window-content-CiJ2yIsr.js} +1 -1
  140. package/dist/web/assets/tab-host-window-content-DYlXR0cg.js +1 -0
  141. package/dist/web/assets/{tab-store-tFnyPmm4.js → tab-store-D2thn4Vv.js} +1 -1
  142. package/dist/web/assets/team-member-sheet-Bnygu7Xw.js +2 -0
  143. package/dist/web/assets/{team-member-window-content-B1jkKkDi.js → team-member-window-content-qrgFTC96.js} +1 -1
  144. package/dist/web/assets/{templ-gxNKXmt3.js → templ-D0zB42a9.js} +1 -1
  145. package/dist/web/assets/{terminal-tab-UvwM0sty.js → terminal-tab-D_-R5rGm.js} +3 -3
  146. package/dist/web/assets/{tex-C0ON27g5.js → tex-BJiPijcH.js} +1 -1
  147. package/dist/web/assets/{tool-cards-jB7GT-wj.js → tool-cards-BW7Xc20J.js} +4 -4
  148. package/dist/web/assets/treemap-KZPCXAKY-BobWhcHN.js +1 -0
  149. package/dist/web/assets/{ts-tags-BX_CTVgQ.js → ts-tags-Dcr4u6y5.js} +1 -1
  150. package/dist/web/assets/{twig-CwL7XnU0.js → twig-B-SQU0bg.js} +1 -1
  151. package/dist/web/assets/{use-blob-url-DcRcjNTE.js → use-blob-url-BtPlqwLr.js} +1 -1
  152. package/dist/web/assets/use-monaco-theme-DQY1ZSaf.js +1 -0
  153. package/dist/web/assets/{use-open-team-member-1hRsHLKg.js → use-open-team-member-C5LQ_TCM.js} +1 -1
  154. package/dist/web/assets/use-visual-viewport-pVxLfE5B.js +1 -0
  155. package/dist/web/assets/{use-websocket-rxJLbFqy.js → use-websocket-l39Odhlp.js} +1 -1
  156. package/dist/web/assets/{vendor-mermaid-CHTa6D69.js → vendor-mermaid-CtDsSxbM.js} +1 -1
  157. package/dist/web/assets/{video-preview-DMhnf2Gi.js → video-preview-C5AhkIDl.js} +1 -1
  158. package/dist/web/assets/{vue-Bykm6wSS.js → vue-Cw10Sfbh.js} +1 -1
  159. package/dist/web/assets/{vue-html-CETdK_iV.js → vue-html-Cc5C_Z_w.js} +1 -1
  160. package/dist/web/assets/{vue-vine-DiJb6fO5.js → vue-vine-BuNMlnvu.js} +1 -1
  161. package/dist/web/assets/window-pip-registry-C7oz3Dfy.js +1 -0
  162. package/dist/web/assets/window-store-B5ZswdYg.js +1 -0
  163. package/dist/web/assets/{xml-BNw8J5YW.js → xml-DqT1wkky.js} +1 -1
  164. package/dist/web/assets/{xsl-CsG9L31b.js → xsl-DLkge3A2.js} +1 -1
  165. package/dist/web/index.html +45 -42
  166. package/dist/web/sw.js +1 -1
  167. package/docs/architecture/ai-chat-and-providers.md +3 -0
  168. package/docs/architecture/workspace-and-ui.md +8 -0
  169. package/docs/design-guidelines.md +3 -2
  170. package/package.json +1 -1
  171. package/src/cli/commands/backup-cmd.ts +119 -0
  172. package/src/index.ts +3 -0
  173. package/src/providers/claude-agent-sdk.ts +29 -1
  174. package/src/server/index.ts +42 -15
  175. package/src/server/routes/accounts.ts +30 -2
  176. package/src/server/routes/chat.ts +8 -1
  177. package/src/server/routes/named-tunnel.ts +5 -2
  178. package/src/server/routes/remote-desktop.ts +64 -0
  179. package/src/server/routes/settings.ts +1 -0
  180. package/src/server/ws/chat.ts +126 -7
  181. package/src/server/ws/remote-desktop.ts +86 -0
  182. package/src/services/account.service.ts +106 -23
  183. package/src/services/db-backup/db-backup-paths.ts +66 -0
  184. package/src/services/db-backup/db-backup-restore.ts +105 -0
  185. package/src/services/db-backup/db-backup-retention.ts +96 -0
  186. package/src/services/db-backup/db-backup-sync.ts +113 -0
  187. package/src/services/db-backup/db-backup.service.ts +61 -0
  188. package/src/services/db.service.ts +29 -1
  189. package/src/services/fs-credential-path-guard.ts +19 -1
  190. package/src/services/named-tunnel/cloudflared-login.service.ts +2 -2
  191. package/src/services/nested-subagent-spy.ts +206 -0
  192. package/src/services/remote-desktop/access-unit-assembler.ts +90 -0
  193. package/src/services/remote-desktop/avc1-codec-string.ts +18 -0
  194. package/src/services/remote-desktop/nal-splitter.ts +100 -0
  195. package/src/services/remote-desktop/remote-desktop-capture.ts +120 -0
  196. package/src/services/remote-desktop/remote-desktop-encoder-args.ts +42 -0
  197. package/src/services/remote-desktop/remote-desktop-flag.ts +10 -0
  198. package/src/services/remote-desktop/remote-desktop-input.ts +217 -0
  199. package/src/services/remote-desktop/remote-desktop-nonce.ts +38 -0
  200. package/src/services/remote-desktop/remote-desktop-session.ts +189 -0
  201. package/src/services/remote-desktop/remote-desktop-vk-map.ts +41 -0
  202. package/src/services/subagent-transcript-merger.ts +92 -75
  203. package/src/services/subprocess-retention.ts +29 -6
  204. package/src/services/supervisor.ts +58 -0
  205. package/src/services/team-member-activity/subagent-transcript-index.ts +56 -0
  206. package/src/types/chat.ts +2 -0
  207. package/src/web/app.tsx +8 -0
  208. package/src/web/components/chat/account-dialogs.tsx +11 -4
  209. package/src/web/components/chat/question-card.tsx +9 -2
  210. package/src/web/components/chat/usage-badge.tsx +17 -3
  211. package/src/web/components/floating-window/floating-window.tsx +8 -1
  212. package/src/web/components/floating-window/open-tab-in-pip.ts +78 -0
  213. package/src/web/components/floating-window/window-content-registry.ts +2 -0
  214. package/src/web/components/floating-window/window-pip-registry.ts +46 -0
  215. package/src/web/components/floating-window/window-store-types.ts +1 -1
  216. package/src/web/components/layout/mobile-drawer.tsx +38 -27
  217. package/src/web/components/layout/nav-section-rail.tsx +10 -2
  218. package/src/web/components/layout/tab-pop-out-menu-item.tsx +25 -2
  219. package/src/web/components/remote-desktop/open-remote-desktop.ts +26 -0
  220. package/src/web/components/remote-desktop/remote-desktop-coords.ts +98 -0
  221. package/src/web/components/remote-desktop/remote-desktop-frame-timestamp.ts +21 -0
  222. package/src/web/components/remote-desktop/remote-desktop-gesture-classifiers.ts +56 -0
  223. package/src/web/components/remote-desktop/remote-desktop-mobile-key-bar.tsx +131 -0
  224. package/src/web/components/remote-desktop/remote-desktop-mobile-sheet.tsx +43 -0
  225. package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +79 -0
  226. package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +190 -0
  227. package/src/web/components/remote-desktop/remote-desktop-stats-overlay.tsx +54 -0
  228. package/src/web/components/remote-desktop/remote-desktop-stats.ts +37 -0
  229. package/src/web/components/remote-desktop/remote-desktop-touch-gesture-tracker.ts +195 -0
  230. package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +72 -0
  231. package/src/web/components/remote-desktop/remote-desktop-ws-url.ts +22 -0
  232. package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +175 -0
  233. package/src/web/components/remote-desktop/use-remote-desktop-available.ts +29 -0
  234. package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +147 -0
  235. package/src/web/components/remote-desktop/use-remote-desktop-mobile-open-state.ts +18 -0
  236. package/src/web/components/remote-desktop/use-remote-desktop-touch.ts +224 -0
  237. package/src/web/components/remote-desktop/use-remote-desktop-virtual-keyboard.ts +50 -0
  238. package/src/web/components/remote-desktop/use-remote-input-capture.ts +92 -0
  239. package/src/web/components/tunnels/named-tunnel/named-tunnel-copy.ts +59 -59
  240. package/src/web/components/tunnels/named-tunnel/named-tunnel-section.tsx +2 -2
  241. package/src/web/hooks/use-notification-badge.ts +19 -11
  242. package/src/web/lib/feature-badges.ts +2 -0
  243. package/src/web/stores/settings-store.ts +13 -0
  244. package/src/web/stores/streaming-store.ts +21 -2
  245. package/dist/web/assets/api-client-BogqsFna.js +0 -1
  246. package/dist/web/assets/api-settings-DfXViPKz.js +0 -1
  247. package/dist/web/assets/architecture-PBZL5I3N-BzFm9ELu.js +0 -1
  248. package/dist/web/assets/chat-tab-DzK8LGti.js +0 -15
  249. package/dist/web/assets/code-editor-CuMfPN9X.js +0 -10
  250. package/dist/web/assets/diff-viewer-eaUBjLCH.js +0 -4
  251. package/dist/web/assets/explorer-body-DkCkSVrN.js +0 -2
  252. package/dist/web/assets/explorer-window-content-MsnTVngu.js +0 -1
  253. package/dist/web/assets/gitGraph-HDMCJU4V-B72vcb2r.js +0 -1
  254. package/dist/web/assets/glide-data-grid-B0IY6n_Q.js +0 -138
  255. package/dist/web/assets/group-chat-tab-DRLDHOp6.js +0 -1
  256. package/dist/web/assets/index-CBn4SOoT.js +0 -52
  257. package/dist/web/assets/index-DWpNR3nB.css +0 -2
  258. package/dist/web/assets/info-3K5VOQVL-3irPxRK_.js +0 -1
  259. package/dist/web/assets/keybindings-store-d54pv2NK.js +0 -1
  260. package/dist/web/assets/markdown-renderer-BTS8ifdu.js +0 -1
  261. package/dist/web/assets/mobile-bottom-sheet-M5iZDX7y.js +0 -1
  262. package/dist/web/assets/mobile-explorer-sheet-BqK2-WuN.js +0 -1
  263. package/dist/web/assets/notification-store-Yvy1I8gd.js +0 -1
  264. package/dist/web/assets/packet-RMMSAZCW-Z5jWjFCV.js +0 -1
  265. package/dist/web/assets/pie-UPGHQEXC-Bq7UMi8P.js +0 -1
  266. package/dist/web/assets/process-table-BYwKigiD.js +0 -1
  267. package/dist/web/assets/radar-KQ55EAFF-BLo4BOTD.js +0 -1
  268. package/dist/web/assets/settings-store-GV5pkkke.js +0 -2
  269. package/dist/web/assets/settings-tab-BePvIcjd.js +0 -3
  270. package/dist/web/assets/settings-tab-Br-lJSPa.js +0 -1
  271. package/dist/web/assets/system-monitor-body-D8Z5QXBB.js +0 -2
  272. package/dist/web/assets/system-monitor-tab-BclYXFTe.js +0 -1
  273. package/dist/web/assets/tab-host-window-content-f_Rc5rJK.js +0 -1
  274. package/dist/web/assets/team-member-sheet-QDHoYCs0.js +0 -2
  275. package/dist/web/assets/treemap-KZPCXAKY-dyrE6SW2.js +0 -1
  276. package/dist/web/assets/use-monaco-theme--9tOOkDA.js +0 -1
  277. package/dist/web/assets/window-store-CQ1lghvv.js +0 -1
  278. /package/dist/web/assets/{abap-DGlrX_8G.js → abap-CKgBNk5K.js} +0 -0
  279. /package/dist/web/assets/{actionscript-3-CUqRtUJE.js → actionscript-3-oARhAY6c.js} +0 -0
  280. /package/dist/web/assets/{ada-CY3Spa33.js → ada-2vi8ASy3.js} +0 -0
  281. /package/dist/web/assets/{andromeeda-Lxs2TJn7.js → andromeeda-B3vMjPKd.js} +0 -0
  282. /package/dist/web/assets/{apache-DfxgGdgM.js → apache-DnIEr338.js} +0 -0
  283. /package/dist/web/assets/{apex-QVDF1dnI.js → apex-B7Qs5AY5.js} +0 -0
  284. /package/dist/web/assets/{api-client-oY1tC3tM.js → api-client-BkGKTjsq.js} +0 -0
  285. /package/dist/web/assets/{applescript-pwJ8QXh-.js → applescript-EbQNUCWb.js} +0 -0
  286. /package/dist/web/assets/{apply-theme-B_bMVL6a.js → apply-theme-Dd_cqGa-.js} +0 -0
  287. /package/dist/web/assets/{ara-mPjeX5YX.js → ara-CtHdhFRw.js} +0 -0
  288. /package/dist/web/assets/{asciidoc-Bf7LsDkI.js → asciidoc-BH6qin_O.js} +0 -0
  289. /package/dist/web/assets/{asm-rRKC1OUx.js → asm-D1XU1ACk.js} +0 -0
  290. /package/dist/web/assets/{aurora-x-DwQr0DkG.js → aurora-x-BkhcxkuS.js} +0 -0
  291. /package/dist/web/assets/{awk-Bpt768fN.js → awk-DijzB6o7.js} +0 -0
  292. /package/dist/web/assets/{ayu-dark-D9lwQHMs.js → ayu-dark-tysG77WC.js} +0 -0
  293. /package/dist/web/assets/{ayu-light-B4-ptrU5.js → ayu-light-Dg0WMETC.js} +0 -0
  294. /package/dist/web/assets/{ayu-mirage-DAyRzH5Q.js → ayu-mirage--RsJCjC4.js} +0 -0
  295. /package/dist/web/assets/{ballerina-Cgdpj1HC.js → ballerina-DT_5-l82.js} +0 -0
  296. /package/dist/web/assets/{bat-4FrN36S2.js → bat-BRpDlkv0.js} +0 -0
  297. /package/dist/web/assets/{beancount-DkcxQER3.js → beancount--8Df-WGG.js} +0 -0
  298. /package/dist/web/assets/{berry-Dnuut-Ml.js → berry-Bc9fo35J.js} +0 -0
  299. /package/dist/web/assets/{bibtex-CCDwmbuA.js → bibtex-CwyBpp9X.js} +0 -0
  300. /package/dist/web/assets/{bicep-BC0ryoGM.js → bicep-DiyH2Bs7.js} +0 -0
  301. /package/dist/web/assets/{bird2-C50V_A-0.js → bird2-CHgK4a2A.js} +0 -0
  302. /package/dist/web/assets/{c-Ddorhq0U.js → c-BM1qL6-f.js} +0 -0
  303. /package/dist/web/assets/{c3-DdIf5r9E.js → c3-D2oOjrP7.js} +0 -0
  304. /package/dist/web/assets/{cadence-s9pd1Wng.js → cadence-nS45wqF6.js} +0 -0
  305. /package/dist/web/assets/{can-open-in-ppm-C6hZV1OP.js → can-open-in-ppm-DwP9KU8o.js} +0 -0
  306. /package/dist/web/assets/{catppuccin-frappe-CzJNTyu-.js → catppuccin-frappe-BpnJHiCj.js} +0 -0
  307. /package/dist/web/assets/{catppuccin-latte-BNpt-m7s.js → catppuccin-latte-Bb95Wj3h.js} +0 -0
  308. /package/dist/web/assets/{catppuccin-macchiato-FYGkfsJ5.js → catppuccin-macchiato-Btx8c17W.js} +0 -0
  309. /package/dist/web/assets/{catppuccin-mocha-it2g5D1q.js → catppuccin-mocha-B4DZOUGI.js} +0 -0
  310. /package/dist/web/assets/{clarity-BVFBTjMe.js → clarity-1ps69jU6.js} +0 -0
  311. /package/dist/web/assets/{clipboard-BWznymz7.js → clipboard-CrHX8AYw.js} +0 -0
  312. /package/dist/web/assets/{clojure-BSYvXMI8.js → clojure-BklC0hKV.js} +0 -0
  313. /package/dist/web/assets/{cmake-DGEqH-Np.js → cmake-B573LYAQ.js} +0 -0
  314. /package/dist/web/assets/{codeowners-D8FCOxWg.js → codeowners-BoYeX89M.js} +0 -0
  315. /package/dist/web/assets/{codeql-COV0eluG.js → codeql-7Dhf5Udk.js} +0 -0
  316. /package/dist/web/assets/{common-lisp-xHnZUlgt.js → common-lisp-m1nxlthv.js} +0 -0
  317. /package/dist/web/assets/{coq-Cs_2i3Xg.js → coq-DNhGmd4a.js} +0 -0
  318. /package/dist/web/assets/{csharp-B1ONB4PT.js → csharp-P-nAurtS.js} +0 -0
  319. /package/dist/web/assets/{css-BgGQbvrZ.js → css-LYbSeh3-.js} +0 -0
  320. /package/dist/web/assets/{csv-CNHmQBzs.js → csv-DHZZZm7X.js} +0 -0
  321. /package/dist/web/assets/{csv-parser-DWR24J17.js → csv-parser-CY9Ggtg5.js} +0 -0
  322. /package/dist/web/assets/{cue-ZXAKzwIy.js → cue-D8yLUOle.js} +0 -0
  323. /package/dist/web/assets/{cypher-4PL5bmT5.js → cypher-9cbW8RYA.js} +0 -0
  324. /package/dist/web/assets/{d-CEsV3RT2.js → d-ZihzhxqI.js} +0 -0
  325. /package/dist/web/assets/{dark-plus-NtFnYSDz.js → dark-plus-C8xIA1u3.js} +0 -0
  326. /package/dist/web/assets/{dart-CKHn4mDf.js → dart-CBm6jKc7.js} +0 -0
  327. /package/dist/web/assets/{data-grid-types-CxU8oo-j.js → data-grid-types-DkEQ-Na9.js} +0 -0
  328. /package/dist/web/assets/{dax-Cmkj1I_M.js → dax-B3cx6YeV.js} +0 -0
  329. /package/dist/web/assets/{desktop-DJLalvPQ.js → desktop-C03SpXth.js} +0 -0
  330. /package/dist/web/assets/{diff-BnS2wpj6.js → diff-D8LnnvFY.js} +0 -0
  331. /package/dist/web/assets/{dist-uYjs3Wv6.js → dist-C4XoS_zo.js} +0 -0
  332. /package/dist/web/assets/{dist-CuIdRyUB.js → dist-Do3fljss.js} +0 -0
  333. /package/dist/web/assets/{docker-CovNy7ml.js → docker-DwFiOQWW.js} +0 -0
  334. /package/dist/web/assets/{dotenv-Gh4w3I6B.js → dotenv-CVJ_vFSQ.js} +0 -0
  335. /package/dist/web/assets/{dracula-NF0tAHXl.js → dracula-DMw8UACY.js} +0 -0
  336. /package/dist/web/assets/{dracula-soft-Cj_qBurK.js → dracula-soft-BnB3nDrA.js} +0 -0
  337. /package/dist/web/assets/{dream-maker-CRuZPcl7.js → dream-maker-2riXUQXo.js} +0 -0
  338. /package/dist/web/assets/{emacs-lisp-BACmC9qj.js → emacs-lisp-BQdYaiB5.js} +0 -0
  339. /package/dist/web/assets/{everforest-dark-Dd9av4QG.js → everforest-dark-C2FPIWdj.js} +0 -0
  340. /package/dist/web/assets/{everforest-light-C-jsss-B.js → everforest-light-DMOWf9sD.js} +0 -0
  341. /package/dist/web/assets/{fennel-BJA9XnL8.js → fennel-JtSHp0vW.js} +0 -0
  342. /package/dist/web/assets/{fish-CKlhNSc2.js → fish-CflDcRfe.js} +0 -0
  343. /package/dist/web/assets/{fluent-wfglRk5D.js → fluent-D4eu6Vac.js} +0 -0
  344. /package/dist/web/assets/{format-bytes-utwHMTry.js → format-bytes-DR3228bu.js} +0 -0
  345. /package/dist/web/assets/{fortran-free-form-BlXCdid3.js → fortran-free-form-hO4RuC9Z.js} +0 -0
  346. /package/dist/web/assets/{gdscript-BwAVG6Et.js → gdscript-D7n3a8VM.js} +0 -0
  347. /package/dist/web/assets/{gdshader-DPc_T7ib.js → gdshader-BhUmrlzG.js} +0 -0
  348. /package/dist/web/assets/{genie-B-zZSLCX.js → genie-VbyftqBp.js} +0 -0
  349. /package/dist/web/assets/{gherkin-CJ0ajpGs.js → gherkin-BZzhvUUb.js} +0 -0
  350. /package/dist/web/assets/{github-dark-Dz8ZkzSP.js → github-dark-C4q41woW.js} +0 -0
  351. /package/dist/web/assets/{github-dark-default-CNUYo_kp.js → github-dark-default-CMNi7dQX.js} +0 -0
  352. /package/dist/web/assets/{github-dark-dimmed-241z_Ycx.js → github-dark-dimmed-BRf_-LlY.js} +0 -0
  353. /package/dist/web/assets/{github-dark-high-contrast-7r6yZZYH.js → github-dark-high-contrast-xanKBsRI.js} +0 -0
  354. /package/dist/web/assets/{github-light-C8gmZn-D.js → github-light-BEQeNQCs.js} +0 -0
  355. /package/dist/web/assets/{github-light-default-GAEgq1eA.js → github-light-default-ceQrtdXg.js} +0 -0
  356. /package/dist/web/assets/{github-light-high-contrast-3z91352E.js → github-light-high-contrast-doKbXzgW.js} +0 -0
  357. /package/dist/web/assets/{gleam-C7vxW5zd.js → gleam-Chzqf4fT.js} +0 -0
  358. /package/dist/web/assets/{gn-CyJIXwHP.js → gn-CCN_-6fp.js} +0 -0
  359. /package/dist/web/assets/{gnuplot-CqLbGgPd.js → gnuplot-CIplgwwP.js} +0 -0
  360. /package/dist/web/assets/{go-DdDcK9aX.js → go-Cjj-9dRs.js} +0 -0
  361. /package/dist/web/assets/{groovy-UYHggEqH.js → groovy-PqD4K7X1.js} +0 -0
  362. /package/dist/web/assets/{gruvbox-dark-hard-B4ierkJn.js → gruvbox-dark-hard-BAbpUSF-.js} +0 -0
  363. /package/dist/web/assets/{gruvbox-dark-medium-DYwkDSZ6.js → gruvbox-dark-medium-BWcWXDBB.js} +0 -0
  364. /package/dist/web/assets/{gruvbox-dark-soft-CVL4P-Lz.js → gruvbox-dark-soft-Dkx1okkk.js} +0 -0
  365. /package/dist/web/assets/{gruvbox-light-hard-BJ0LewUZ.js → gruvbox-light-hard-WVsDK0MM.js} +0 -0
  366. /package/dist/web/assets/{gruvbox-light-medium-CaqqjeGJ.js → gruvbox-light-medium-tiR9t7gr.js} +0 -0
  367. /package/dist/web/assets/{gruvbox-light-soft-ByKMoVNr.js → gruvbox-light-soft-HzHohaX6.js} +0 -0
  368. /package/dist/web/assets/{haskell-BilYRVPp.js → haskell-B1eIZfr6.js} +0 -0
  369. /package/dist/web/assets/{haxe-Brs9Bj1s.js → haxe-BcDdi5xr.js} +0 -0
  370. /package/dist/web/assets/{hcl-ClrZpol-.js → hcl-Dq-HKVni.js} +0 -0
  371. /package/dist/web/assets/{hjson-9PvHnYMX.js → hjson-Dr9D9WVU.js} +0 -0
  372. /package/dist/web/assets/{hlsl-BbBk4vqV.js → hlsl-Bo8t2FuZ.js} +0 -0
  373. /package/dist/web/assets/{horizon-E5Py0cny.js → horizon-DWG5IXc-.js} +0 -0
  374. /package/dist/web/assets/{horizon-bright-DVCsNGEk.js → horizon-bright-DFr9wTTx.js} +0 -0
  375. /package/dist/web/assets/{houston-JdFEax9Y.js → houston-NAVNhkup.js} +0 -0
  376. /package/dist/web/assets/{hy-W_W3tLKT.js → hy-DnyvUk3B.js} +0 -0
  377. /package/dist/web/assets/{image-gallery-5IURd6db.js → image-gallery-DoFdNF9i.js} +0 -0
  378. /package/dist/web/assets/{imba-CfAqJz7T.js → imba-CPbR5W_A.js} +0 -0
  379. /package/dist/web/assets/{ini-C_3gvKWs.js → ini-lc8_Lt0y.js} +0 -0
  380. /package/dist/web/assets/{java-CKqtXZ-j.js → java-B0KCo2vK.js} +0 -0
  381. /package/dist/web/assets/{javascript-C7gSEfae.js → javascript-YqEbNRpd.js} +0 -0
  382. /package/dist/web/assets/{json-BoN6e11T.js → json-Co0WtOlh.js} +0 -0
  383. /package/dist/web/assets/{json5-CgE7C8HW.js → json5-2Jhb0QUN.js} +0 -0
  384. /package/dist/web/assets/{jsonc-DjyoattY.js → jsonc-e72F11nU.js} +0 -0
  385. /package/dist/web/assets/{jsonl-lI82wGSw.js → jsonl-Bff8IPye.js} +0 -0
  386. /package/dist/web/assets/{jsonnet-CszEI8va.js → jsonnet-DRCkb-_H.js} +0 -0
  387. /package/dist/web/assets/{jssm-CZXDlszb.js → jssm-C6UiwVEm.js} +0 -0
  388. /package/dist/web/assets/{jsx-BeRz0eEz.js → jsx-u1aZlnfA.js} +0 -0
  389. /package/dist/web/assets/{kanagawa-dragon-BOmnUiyd.js → kanagawa-dragon-De-RQFV3.js} +0 -0
  390. /package/dist/web/assets/{kanagawa-lotus-ffU_P5w-.js → kanagawa-lotus-K8elE4QU.js} +0 -0
  391. /package/dist/web/assets/{kanagawa-wave-DKTJZrMu.js → kanagawa-wave-BrStl_zx.js} +0 -0
  392. /package/dist/web/assets/{kdl-BWECJveH.js → kdl-CLCfMeZy.js} +0 -0
  393. /package/dist/web/assets/{kotlin-DpVwBCSt.js → kotlin-DEEC3pNh.js} +0 -0
  394. /package/dist/web/assets/{kusto-BgVqjUZA.js → kusto-DTqRhCup.js} +0 -0
  395. /package/dist/web/assets/{laserwave-CEVCi8em.js → laserwave-DsgpUSbA.js} +0 -0
  396. /package/dist/web/assets/{lean-CGyfRhLJ.js → lean-Dl9MI0Y3.js} +0 -0
  397. /package/dist/web/assets/{less-B0_H10Ku.js → less-Bg21rao6.js} +0 -0
  398. /package/dist/web/assets/{lib-hrC3bruJ.js → lib-CXUwxw1M.js} +0 -0
  399. /package/dist/web/assets/{libesm-DVpFmwJu.js → libesm-CVkncAQz.js} +0 -0
  400. /package/dist/web/assets/{light-plus-DqzMqPID.js → light-plus-DGNbbK1V.js} +0 -0
  401. /package/dist/web/assets/{llvm-9ucnfNiF.js → llvm-91_ZX3p-.js} +0 -0
  402. /package/dist/web/assets/{loader-circle-Dpqcv0eS.js → loader-circle-DouKgIXy.js} +0 -0
  403. /package/dist/web/assets/{lock-DldbT31x.js → lock-CNxUGpTk.js} +0 -0
  404. /package/dist/web/assets/{log-m6A2dOSj.js → log-DSXzbq8u.js} +0 -0
  405. /package/dist/web/assets/{logo-ByPcsC9P.js → logo-BQRNd4tc.js} +0 -0
  406. /package/dist/web/assets/{luau-DFCo8nBn.js → luau-BE-TMpiZ.js} +0 -0
  407. /package/dist/web/assets/{make-C90w3MeA.js → make-ePYGKTpc.js} +0 -0
  408. /package/dist/web/assets/{markdown-DT0uywZG.js → markdown-Cst3wgMW.js} +0 -0
  409. /package/dist/web/assets/{material-theme-BVUO93tJ.js → material-theme-9XyvnNLb.js} +0 -0
  410. /package/dist/web/assets/{material-theme-darker-xXgGDwnF.js → material-theme-darker-H1VN4MTS.js} +0 -0
  411. /package/dist/web/assets/{material-theme-lighter-BjStPzWB.js → material-theme-lighter-DAreBbuF.js} +0 -0
  412. /package/dist/web/assets/{material-theme-ocean-wFmSiKjy.js → material-theme-ocean-BpTR-3II.js} +0 -0
  413. /package/dist/web/assets/{material-theme-palenight-e-0CtRoe.js → material-theme-palenight-DUNzX711.js} +0 -0
  414. /package/dist/web/assets/{matlab-BNaNQAUH.js → matlab-BpYNufBU.js} +0 -0
  415. /package/dist/web/assets/{mdx-DfpVWAqO.js → mdx-BYjfe1DK.js} +0 -0
  416. /package/dist/web/assets/{mermaid-Bpq7wFCP.js → mermaid-DPCcIW9B.js} +0 -0
  417. /package/dist/web/assets/{min-dark-CL2Y0WH6.js → min-dark-DnGOAGCo.js} +0 -0
  418. /package/dist/web/assets/{min-light-DOgr_IdP.js → min-light-CwV-o31y.js} +0 -0
  419. /package/dist/web/assets/{mipsasm-C9ie2zlB.js → mipsasm-DdeCPC97.js} +0 -0
  420. /package/dist/web/assets/{mojo-Md2A2sJy.js → mojo-BmxxZ3iM.js} +0 -0
  421. /package/dist/web/assets/{monitor-4vWfqzuN.js → monitor-C835ucWH.js} +0 -0
  422. /package/dist/web/assets/{monokai-nbXIEySB.js → monokai-Cx6cQ-MA.js} +0 -0
  423. /package/dist/web/assets/{moonbit-CJ-T-s4A.js → moonbit-D2-Gj5vj.js} +0 -0
  424. /package/dist/web/assets/{move-DwEehLIK.js → move-DSWGoQa8.js} +0 -0
  425. /package/dist/web/assets/{narrat-CuF4sIKr.js → narrat-D9TnZSSu.js} +0 -0
  426. /package/dist/web/assets/{nextflow-groovy-1dYhfPfq.js → nextflow-groovy-BbL-8PwD.js} +0 -0
  427. /package/dist/web/assets/{night-owl-gZS5mfzY.js → night-owl-CZitKm5d.js} +0 -0
  428. /package/dist/web/assets/{night-owl-light-DDAster5.js → night-owl-light-DJVX1Pno.js} +0 -0
  429. /package/dist/web/assets/{nix-BlbEYs2c.js → nix-R6hr3oln.js} +0 -0
  430. /package/dist/web/assets/{nord-BRiPb8Vy.js → nord-ChGHya3l.js} +0 -0
  431. /package/dist/web/assets/{nushell-DgWicB1i.js → nushell-QQWB-GWx.js} +0 -0
  432. /package/dist/web/assets/{objective-c-D8mPZ42Y.js → objective-c-C0pZ6y73.js} +0 -0
  433. /package/dist/web/assets/{objective-cpp-B0pNXzuj.js → objective-cpp-BdzVM31a.js} +0 -0
  434. /package/dist/web/assets/{ocaml-lQd7aK5d.js → ocaml-D4KktNlS.js} +0 -0
  435. /package/dist/web/assets/{odin-CxtIOBjm.js → odin-Y-fypmGT.js} +0 -0
  436. /package/dist/web/assets/{one-dark-pro-T_UFnWfe.js → one-dark-pro-BwecIxQL.js} +0 -0
  437. /package/dist/web/assets/{one-light-LBVqwyV-.js → one-light-Cz7UVzLU.js} +0 -0
  438. /package/dist/web/assets/{openscad-C5dnS6J7.js → openscad-DtRe3qYD.js} +0 -0
  439. /package/dist/web/assets/{pascal-CmWCWQML.js → pascal-Ck6I1fGG.js} +0 -0
  440. /package/dist/web/assets/{pkl-BWWOtDQk.js → pkl-DdFK94VW.js} +0 -0
  441. /package/dist/web/assets/{plastic-ekhupckF.js → plastic-BQSAzZwR.js} +0 -0
  442. /package/dist/web/assets/{play-CpCAoQRZ.js → play-CJlWpVMw.js} +0 -0
  443. /package/dist/web/assets/{plsql-BPsDHXx4.js → plsql-DjXg_CP0.js} +0 -0
  444. /package/dist/web/assets/{po-BX2FiuW2.js → po-Bw2SuM4r.js} +0 -0
  445. /package/dist/web/assets/{poimandres-DzXOdZcN.js → poimandres-YMK6c5VN.js} +0 -0
  446. /package/dist/web/assets/{polar-DDRXgJzG.js → polar-BMu17-O0.js} +0 -0
  447. /package/dist/web/assets/{portal-container-context-a399Hd9F.js → portal-container-context-DSGdlXrJ.js} +0 -0
  448. /package/dist/web/assets/{postcss-BHW6XBhx.js → postcss-DWib10RZ.js} +0 -0
  449. /package/dist/web/assets/{powerquery-B89c2NBb.js → powerquery-Dht4CK0b.js} +0 -0
  450. /package/dist/web/assets/{powershell-Dvxrd3Tu.js → powershell-DvrCg8ji.js} +0 -0
  451. /package/dist/web/assets/{prisma-lMK8EFLU.js → prisma-DbodYvCM.js} +0 -0
  452. /package/dist/web/assets/{prolog-D0YrtMG4.js → prolog-DZ-m6ysB.js} +0 -0
  453. /package/dist/web/assets/{proto-J0mM25aw.js → proto-HHpG3tGO.js} +0 -0
  454. /package/dist/web/assets/{puppet-CUT0xkhh.js → puppet-BljHe_75.js} +0 -0
  455. /package/dist/web/assets/{purescript-CoJWSGRp.js → purescript-DzCiFR0B.js} +0 -0
  456. /package/dist/web/assets/{python-BBrmuz6P.js → python-B5BQ27cC.js} +0 -0
  457. /package/dist/web/assets/{qmldir-CUtzs4Q7.js → qmldir-CYlcXrnn.js} +0 -0
  458. /package/dist/web/assets/{qss-Dm1RZ8aL.js → qss-Ce7T_TEg.js} +0 -0
  459. /package/dist/web/assets/{r-BGP7qZTe.js → r-Bku-2Jp_.js} +0 -0
  460. /package/dist/web/assets/{racket-K_6U1lqJ.js → racket-DtfSL3WR.js} +0 -0
  461. /package/dist/web/assets/{raku-bNSiZeW_.js → raku-CnDLsBJr.js} +0 -0
  462. /package/dist/web/assets/{react-X4jUntEf.js → react-Dfio3PfK.js} +0 -0
  463. /package/dist/web/assets/{red-uVeIEF0f.js → red-vbqcE9b-.js} +0 -0
  464. /package/dist/web/assets/{refresh-cw-B9iv7csb.js → refresh-cw-84mz8aOE.js} +0 -0
  465. /package/dist/web/assets/{reg-DSm9GWjh.js → reg-DIDLT8-A.js} +0 -0
  466. /package/dist/web/assets/{regexp-CljRm_j_.js → regexp-CB4h-VPu.js} +0 -0
  467. /package/dist/web/assets/{rel-d3JcopZg.js → rel-CiQEnWXa.js} +0 -0
  468. /package/dist/web/assets/{resolve-theme-DLKmm-lE.js → resolve-theme-DPTQHAy1.js} +0 -0
  469. /package/dist/web/assets/{riscv-CJmaELjT.js → riscv-R78iq5Rf.js} +0 -0
  470. /package/dist/web/assets/{ron-D0g9B1rW.js → ron-DhYENxc4.js} +0 -0
  471. /package/dist/web/assets/{rose-pine-DOrD2BSH.js → rose-pine-5wBeyjfJ.js} +0 -0
  472. /package/dist/web/assets/{rose-pine-dawn-CzZQ-LCF.js → rose-pine-dawn-FpKtPCuH.js} +0 -0
  473. /package/dist/web/assets/{rose-pine-moon-CdmsYp7C.js → rose-pine-moon-BwcIYc7C.js} +0 -0
  474. /package/dist/web/assets/{rosmsg-CmDth6pO.js → rosmsg-DWIGAYMl.js} +0 -0
  475. /package/dist/web/assets/{rotate-ccw-DnuPzJRE.js → rotate-ccw-CNTk2J7l.js} +0 -0
  476. /package/dist/web/assets/{rust-DGdP6jiR.js → rust-DU_ofKgt.js} +0 -0
  477. /package/dist/web/assets/{sass-Dkgt2FKZ.js → sass-U9SUuYEy.js} +0 -0
  478. /package/dist/web/assets/{save-DxonD4y1.js → save-CtW9ojKk.js} +0 -0
  479. /package/dist/web/assets/{scala-BQLTON6C.js → scala-op4gjA6j.js} +0 -0
  480. /package/dist/web/assets/{scheme-DKlxUGCi.js → scheme-tEjkR8rL.js} +0 -0
  481. /package/dist/web/assets/{sdbl-C3iZJQeZ.js → sdbl-C6SSRmZg.js} +0 -0
  482. /package/dist/web/assets/{send-B-t5Mz26.js → send-C_sLFt56.js} +0 -0
  483. /package/dist/web/assets/{shallow-jwPzEunk.js → shallow-C1YoJQA-.js} +0 -0
  484. /package/dist/web/assets/{shellscript-BRu-ZQAx.js → shellscript-Bf2KiwQj.js} +0 -0
  485. /package/dist/web/assets/{shield-check-CdTiK1j2.js → shield-check-Du6HKYn9.js} +0 -0
  486. /package/dist/web/assets/{shield-off-DuetkXKM.js → shield-off-CWKb1MQL.js} +0 -0
  487. /package/dist/web/assets/{slack-dark-qkm_u2zz.js → slack-dark-oGgCey8r.js} +0 -0
  488. /package/dist/web/assets/{slack-ochin-BP8_8e4m.js → slack-ochin-CemlB8cE.js} +0 -0
  489. /package/dist/web/assets/{smalltalk-Bnz4gt07.js → smalltalk-CTE-6jrZ.js} +0 -0
  490. /package/dist/web/assets/{snazzy-light-fbPZg5io.js → snazzy-light-DoEIGnmz.js} +0 -0
  491. /package/dist/web/assets/{solarized-dark-CM0vTRoi.js → solarized-dark-CStiJyBg.js} +0 -0
  492. /package/dist/web/assets/{solarized-light-C4_W9mvs.js → solarized-light-CfuNycS9.js} +0 -0
  493. /package/dist/web/assets/{solidity-Bz6v1PoD.js → solidity-DL6sO5rd.js} +0 -0
  494. /package/dist/web/assets/{splunk-DyNxu-0i.js → splunk-yK4ZUFBB.js} +0 -0
  495. /package/dist/web/assets/{sql-DXRL1gw1.js → sql-Lw03eoN8.js} +0 -0
  496. /package/dist/web/assets/{ssh-config-2z1QHuOG.js → ssh-config-DeRp25TR.js} +0 -0
  497. /package/dist/web/assets/{stylus-D4I-VaeY.js → stylus-D8OMsPh-.js} +0 -0
  498. /package/dist/web/assets/{swift-C_2G-tIc.js → swift-nsl9stPV.js} +0 -0
  499. /package/dist/web/assets/{synthwave-84-osBjChRP.js → synthwave-84-exAgsgpf.js} +0 -0
  500. /package/dist/web/assets/{system-verilog-zQP2qPNU.js → system-verilog-DBWf00T6.js} +0 -0
  501. /package/dist/web/assets/{systemd-UGfjRSBM.js → systemd-nhu27Dw5.js} +0 -0
  502. /package/dist/web/assets/{table-FLU8ei8b.js → table-GAR4upb6.js} +0 -0
  503. /package/dist/web/assets/{talonscript-C1XrFPwo.js → talonscript-CNOEcGDc.js} +0 -0
  504. /package/dist/web/assets/{tasl-B5raVn43.js → tasl-CjYZenSH.js} +0 -0
  505. /package/dist/web/assets/{tcl-3-vbeMIc.js → tcl-BVA09RDz.js} +0 -0
  506. /package/dist/web/assets/{terraform-a8VXZDm9.js → terraform-C5EloH1q.js} +0 -0
  507. /package/dist/web/assets/{text-wrap-B05VGRa-.js → text-wrap-D71Jqz5U.js} +0 -0
  508. /package/dist/web/assets/{tokyo-night-4SRhf-Hz.js → tokyo-night-D764DdhO.js} +0 -0
  509. /package/dist/web/assets/{toml-B4N0i8wT.js → toml-CCDYblSB.js} +0 -0
  510. /package/dist/web/assets/{trash-2-CTXlTflF.js → trash-2-C08fhIg1.js} +0 -0
  511. /package/dist/web/assets/{triangle-alert-BRq0VAfd.js → triangle-alert-85BQgs3T.js} +0 -0
  512. /package/dist/web/assets/{tsv-DA5k9Lvw.js → tsv-BZ4sA05e.js} +0 -0
  513. /package/dist/web/assets/{tsx-D9SZ8owC.js → tsx-DPLBzAt5.js} +0 -0
  514. /package/dist/web/assets/{turtle-BTHTvyY_.js → turtle-CwGHCq40.js} +0 -0
  515. /package/dist/web/assets/{typescript-DYzV9p5L.js → typescript-CUH0vBAl.js} +0 -0
  516. /package/dist/web/assets/{typespec-BC15eh7F.js → typespec-Bq31Vq4u.js} +0 -0
  517. /package/dist/web/assets/{typst-BJUqzA6D.js → typst-MD9BR9W5.js} +0 -0
  518. /package/dist/web/assets/{use-is-mobile-DFWViA4S.js → use-is-mobile-DUuki97q.js} +0 -0
  519. /package/dist/web/assets/{utils-C9OjCrou.js → utils-u2fbYPrY.js} +0 -0
  520. /package/dist/web/assets/{v-50Lps6oA.js → v-DZV7IxIT.js} +0 -0
  521. /package/dist/web/assets/{vala-8wiUvvrD.js → vala-urd1zi9S.js} +0 -0
  522. /package/dist/web/assets/{vb-BlgPLt6B.js → vb-DIZU3zbJ.js} +0 -0
  523. /package/dist/web/assets/{vendor-xterm-BZOrwF_F.js → vendor-xterm-DCj0zgSC.js} +0 -0
  524. /package/dist/web/assets/{verilog-CD9pVW5p.js → verilog-Ndz_ciTA.js} +0 -0
  525. /package/dist/web/assets/{vesper-BrivW4PT.js → vesper-C86M7LJp.js} +0 -0
  526. /package/dist/web/assets/{vhdl-Bunxfa7N.js → vhdl-_klwDUjd.js} +0 -0
  527. /package/dist/web/assets/{viml-DndUb2qM.js → viml-DzRXWoQh.js} +0 -0
  528. /package/dist/web/assets/{vitesse-black-BurZSkxW.js → vitesse-black-TWZ20_3U.js} +0 -0
  529. /package/dist/web/assets/{vitesse-dark-8UShspzC.js → vitesse-dark-Dl7R6fy9.js} +0 -0
  530. /package/dist/web/assets/{vitesse-light-DghCAZ90.js → vitesse-light-Cs0Dt2_n.js} +0 -0
  531. /package/dist/web/assets/{vyper-b-Clr-8u.js → vyper-qnCVj8ia.js} +0 -0
  532. /package/dist/web/assets/{wasm-CpcKStkw.js → wasm-Ckx9jBPe.js} +0 -0
  533. /package/dist/web/assets/{wasm-C5v2T06Y.js → wasm-Dy-SJ3W9.js} +0 -0
  534. /package/dist/web/assets/{wenyan-Dz2srLZR.js → wenyan-DTEy2Lx-.js} +0 -0
  535. /package/dist/web/assets/{wgsl-CeD1-dRb.js → wgsl-DC7n63Sy.js} +0 -0
  536. /package/dist/web/assets/{wikitext-CyeS-9FH.js → wikitext-1zFoZiW6.js} +0 -0
  537. /package/dist/web/assets/{wit-Kr-q-qBQ.js → wit-DtTfCRph.js} +0 -0
  538. /package/dist/web/assets/{wolfram-BKm8bmln.js → wolfram-B2Bgu1KL.js} +0 -0
  539. /package/dist/web/assets/{x-YDu4m2Iq.js → x-CeN0Qybc.js} +0 -0
  540. /package/dist/web/assets/{yaml-2XSxomjM.js → yaml-B-Jxk0IQ.js} +0 -0
  541. /package/dist/web/assets/{zenscript-CKIS34vf.js → zenscript-BWRfvNER.js} +0 -0
  542. /package/dist/web/assets/{zig-DdHGNllX.js → zig-CkwuvETm.js} +0 -0
@@ -0,0 +1,119 @@
1
+ import { Command } from "commander";
2
+
3
+ /**
4
+ * `ppm backup` / `ppm restore` — manual control over config-database
5
+ * snapshots. Snapshots also happen automatically (hourly, at start-up, and
6
+ * before any schema migration); these commands exist for the two moments
7
+ * automation cannot cover: taking one deliberately before a risky change, and
8
+ * getting back on your feet after something ate the database.
9
+ *
10
+ * Kept out of `db-cmd.ts` on purpose — that command group manages *external*
11
+ * database connections, so `ppm db backup` would read as backing one of those
12
+ * up rather than PPM's own store.
13
+ */
14
+
15
+ const C = {
16
+ reset: "\x1b[0m",
17
+ bold: "\x1b[1m",
18
+ green: "\x1b[32m",
19
+ red: "\x1b[31m",
20
+ yellow: "\x1b[33m",
21
+ cyan: "\x1b[36m",
22
+ dim: "\x1b[2m",
23
+ };
24
+
25
+ function mb(bytes: number): string {
26
+ return `${(bytes / 1_048_576).toFixed(1)} MB`;
27
+ }
28
+
29
+ function ago(date: Date, now = new Date()): string {
30
+ const mins = Math.round((now.getTime() - date.getTime()) / 60_000);
31
+ if (mins < 60) return `${mins}m ago`;
32
+ const hours = Math.round(mins / 60);
33
+ if (hours < 48) return `${hours}h ago`;
34
+ return `${Math.round(hours / 24)}d ago`;
35
+ }
36
+
37
+ export function registerBackupCommands(program: Command): void {
38
+ program
39
+ .command("backup")
40
+ .description("Snapshot the PPM config database now")
41
+ .option("--json", "Output as JSON")
42
+ .action(async (options: { json?: boolean }) => {
43
+ try {
44
+ const { backupDb } = await import("../../services/db-backup/db-backup.service.ts");
45
+ const result = await backupDb("manual");
46
+ if (options.json) {
47
+ console.log(JSON.stringify(result, null, 2));
48
+ return;
49
+ }
50
+ console.log(`${C.green}Snapshot written${C.reset} ${result.path}`);
51
+ console.log(` ${mb(result.bytes)} in ${result.ms}ms, integrity verified`);
52
+ if (result.pruned.length) {
53
+ console.log(` ${C.dim}pruned ${result.pruned.length} older snapshot(s) per retention policy${C.reset}`);
54
+ }
55
+ } catch (e: any) {
56
+ console.error(`${C.red}Backup failed:${C.reset} ${e?.message ?? e}`);
57
+ process.exit(1);
58
+ }
59
+ });
60
+
61
+ program
62
+ .command("restore")
63
+ .description("Restore the PPM config database from a snapshot")
64
+ .argument("[backup]", "Snapshot filename or path (default: the newest one)")
65
+ .option("--list", "List available snapshots and exit")
66
+ .option("--force", "Restore even while PPM is running (stop it first when you can)")
67
+ .option("--json", "Output as JSON")
68
+ .action(async (backupArg: string | undefined, options: { list?: boolean; force?: boolean; json?: boolean }) => {
69
+ try {
70
+ const { listBackups, activeDbStem } = await import("../../services/db-backup/db-backup.service.ts");
71
+ const { getBackupsDir } = await import("../../services/db-backup/db-backup-paths.ts");
72
+ const { restoreDb, resolveBackupArg } = await import("../../services/db-backup/db-backup-restore.ts");
73
+ const { getDbPath } = await import("../../services/db.service.ts");
74
+
75
+ const stem = await activeDbStem();
76
+ const entries = listBackups(stem);
77
+
78
+ if (options.list) {
79
+ if (options.json) {
80
+ console.log(JSON.stringify(entries, null, 2));
81
+ return;
82
+ }
83
+ if (entries.length === 0) {
84
+ console.log(`${C.yellow}No snapshots in ${getBackupsDir()}${C.reset}`);
85
+ return;
86
+ }
87
+ console.log(`${C.bold}Snapshots in ${getBackupsDir()}${C.reset}`);
88
+ for (const e of entries) {
89
+ console.log(` ${C.cyan}${e.name}${C.reset} ${mb(e.bytes)} ${C.dim}${e.reason}, ${ago(e.date)}${C.reset}`);
90
+ }
91
+ return;
92
+ }
93
+
94
+ const target = backupArg
95
+ ? resolveBackupArg(backupArg, getBackupsDir())
96
+ : entries[0]?.path;
97
+ if (!target) {
98
+ console.error(`${C.red}No snapshot to restore${C.reset} — nothing in ${getBackupsDir()}`);
99
+ process.exit(1);
100
+ return;
101
+ }
102
+
103
+ const result = restoreDb(target, { dbPath: getDbPath(), force: options.force });
104
+ if (options.json) {
105
+ console.log(JSON.stringify(result, null, 2));
106
+ return;
107
+ }
108
+ console.log(`${C.green}Restored${C.reset} ${result.dbPath} (${mb(result.bytes)})`);
109
+ console.log(` from ${result.restoredFrom}`);
110
+ if (result.archivedTo) {
111
+ console.log(` ${C.dim}previous database kept at ${result.archivedTo}${C.reset}`);
112
+ }
113
+ console.log(` ${C.dim}start PPM with: ppm start${C.reset}`);
114
+ } catch (e: any) {
115
+ console.error(`${C.red}Restore failed:${C.reset} ${e?.message ?? e}`);
116
+ process.exit(1);
117
+ }
118
+ });
119
+ }
package/src/index.ts CHANGED
@@ -154,6 +154,9 @@ export async function buildProgram(): Promise<Command> {
154
154
  const { registerDbCommands } = await import("./cli/commands/db-cmd.ts");
155
155
  registerDbCommands(program);
156
156
 
157
+ const { registerBackupCommands } = await import("./cli/commands/backup-cmd.ts");
158
+ registerBackupCommands(program);
159
+
157
160
  const { registerBotCommands } = await import("./cli/commands/bot-cmd.ts");
158
161
  registerBotCommands(program);
159
162
 
@@ -20,7 +20,8 @@ import { configService } from "../services/config.service.ts";
20
20
  import { mcpConfigService } from "../services/mcp-config.service.ts";
21
21
  import { listInheritedClaudeMcpServers } from "../services/claude-code-mcp.service.ts";
22
22
  import { updateFromSdkEvent } from "../services/claude-usage.service.ts";
23
- import { getSessionProjectPath, setSessionMetadata, getSessionTitles, insertTurnUsage } from "../services/db.service.ts";
23
+ import { getSessionProjectPath, setSessionMetadata, getSessionTitles, insertTurnUsage, getSessionAccount } from "../services/db.service.ts";
24
+ import { SUBSCRIPTION_PROMPT_CACHE_TTL_MS, API_KEY_PROMPT_CACHE_TTL_MS } from "../services/subprocess-retention.ts";
24
25
  import { buildTurnUsage, formatTurnUsageLog } from "../shared/turn-usage.ts";
25
26
  import { accountSelector } from "../services/account-selector.service.ts";
26
27
  import { accountService, type AccountWithTokens } from "../services/account.service.ts";
@@ -761,6 +762,33 @@ export class ClaudeAgentSdkProvider implements AIProvider {
761
762
  return this.streamingSessions.has(sessionId);
762
763
  }
763
764
 
765
+ /**
766
+ * How long this session's prompt cache is worth holding a subprocess for.
767
+ *
768
+ * The hour only applies to a Claude subscription; an API key — settings `api_key`, a shell
769
+ * key, or anything behind a custom `base_url` — gets five minutes. Mirrors
770
+ * `buildQueryEnv`'s precedence, since that is what actually decides how the subprocess
771
+ * authenticates: settings api_key first, then the session's account, then the shell.
772
+ */
773
+ promptCacheTtlMs(sessionId: string): number {
774
+ const cfg = this.getProviderConfig();
775
+ // A custom endpoint is not Anthropic's subscription API whatever the credential is.
776
+ const shellBaseUrl = process.env.ANTHROPIC_BASE_URL ?? "";
777
+ if (cfg.base_url || (shellBaseUrl && !shellBaseUrl.includes("/proxy"))) {
778
+ return API_KEY_PROMPT_CACHE_TTL_MS;
779
+ }
780
+ if (cfg.api_key?.trim()) return API_KEY_PROMPT_CACHE_TTL_MS;
781
+
782
+ const accountId = getSessionAccount(sessionId);
783
+ const token = accountId ? accountService.getWithTokens(accountId)?.accessToken : undefined;
784
+ if (token) return token.startsWith("sk-ant-oat") ? SUBSCRIPTION_PROMPT_CACHE_TTL_MS : API_KEY_PROMPT_CACHE_TTL_MS;
785
+
786
+ // No account recorded yet: the shell's own credentials decide, and an OAuth token there
787
+ // is still a subscription. Nothing at all means the CLI's own auth, which we cannot
788
+ // classify — take the short window rather than hold memory on a guess.
789
+ return process.env.CLAUDE_CODE_OAUTH_TOKEN ? SUBSCRIPTION_PROMPT_CACHE_TTL_MS : API_KEY_PROMPT_CACHE_TTL_MS;
790
+ }
791
+
764
792
  async *sendMessage(
765
793
  _sessionId: string,
766
794
  message: string,
@@ -30,6 +30,8 @@ import { chatWebSocket } from "./ws/chat.ts";
30
30
  import { extensionWebSocket } from "./ws/extensions.ts";
31
31
  import { globalWebSocket } from "./ws/global.ts";
32
32
  import { groupChatWebSocket } from "./ws/group-chat.ts";
33
+ import { remoteDesktopWebSocket } from "./ws/remote-desktop.ts";
34
+ import { isRemoteDesktopEnabled } from "../services/remote-desktop/remote-desktop-flag.ts";
33
35
  import { ok, err } from "../types/api.ts";
34
36
 
35
37
  /** Tee console.log/error to ~/.ppm/ppm.log while preserving terminal output */
@@ -175,6 +177,10 @@ app.route("/api/system", resourceRoutes);
175
177
  import { hostInfoRoutes } from "./routes/host-info.ts";
176
178
  app.route("/api/system", hostInfoRoutes);
177
179
 
180
+ // Remote desktop (video capture + input) — feature-flagged off by default, see remote-desktop-flag.ts
181
+ import { remoteDesktopRoutes } from "./routes/remote-desktop.ts";
182
+ app.route("/api/remote-desktop", remoteDesktopRoutes);
183
+
178
184
  // Finishes an OAuth loopback login started from another device
179
185
  import { loopbackRoutes } from "./routes/oauth-loopback.ts";
180
186
  app.route("/api/loopback", loopbackRoutes);
@@ -860,6 +866,20 @@ if (process.argv.includes("__serve__")) {
860
866
  return new Response("WebSocket upgrade failed", { status: 400 });
861
867
  }
862
868
 
869
+ if (url.pathname === "/ws/remote-desktop") {
870
+ // Explicit branch so this socket can never fall through to the terminal (shell)
871
+ // handler's default case. Feature flag + `auth.enabled` are re-checked here
872
+ // independently of `isWsUpgradeAuthorized` above, which returns true unconditionally
873
+ // when PPM auth is disabled — a live keyboard/mouse channel must not inherit that.
874
+ if (!isRemoteDesktopEnabled()) return new Response("Not Found", { status: 404 });
875
+ if (!configService.get("auth").enabled) {
876
+ return new Response("Forbidden: remote desktop requires PPM authentication to be enabled", { status: 403 });
877
+ }
878
+ const upgraded = server.upgrade(req, { data: { type: "remote-desktop" } });
879
+ if (upgraded) return undefined;
880
+ return new Response("WebSocket upgrade failed", { status: 400 });
881
+ }
882
+
863
883
  if (url.pathname.startsWith("/ws/project/")) {
864
884
  const parts = url.pathname.split("/");
865
885
  const projectName = decodeURIComponent(parts[3] ?? "");
@@ -901,25 +921,32 @@ if (process.argv.includes("__serve__")) {
901
921
  sendPong: true,
902
922
  perMessageDeflate: false,
903
923
  open(ws: any) {
904
- if (ws.data?.type === "chat") chatWebSocket.open(ws);
905
- else if (ws.data?.type === "group") groupChatWebSocket.open(ws);
906
- else if (ws.data?.type === "extensions") extensionWebSocket.open(ws);
907
- else if (ws.data?.type === "global") globalWebSocket.open(ws);
908
- else terminalWebSocket.open(ws);
924
+ const t = ws.data?.type;
925
+ if (t === "chat") chatWebSocket.open(ws);
926
+ else if (t === "group") groupChatWebSocket.open(ws);
927
+ else if (t === "extensions") extensionWebSocket.open(ws);
928
+ else if (t === "global") globalWebSocket.open(ws);
929
+ else if (t === "remote-desktop") remoteDesktopWebSocket.open(ws);
930
+ else if (t === "terminal") terminalWebSocket.open(ws);
931
+ else ws.close(1008, "unknown socket type");
909
932
  },
910
933
  message(ws: any, msg: any) {
911
- if (ws.data?.type === "chat") chatWebSocket.message(ws, msg);
912
- else if (ws.data?.type === "group") groupChatWebSocket.message(ws, msg);
913
- else if (ws.data?.type === "extensions") extensionWebSocket.message(ws, msg);
914
- else if (ws.data?.type === "global") globalWebSocket.message(ws, msg);
915
- else terminalWebSocket.message(ws, msg);
934
+ const t = ws.data?.type;
935
+ if (t === "chat") chatWebSocket.message(ws, msg);
936
+ else if (t === "group") groupChatWebSocket.message(ws, msg);
937
+ else if (t === "extensions") extensionWebSocket.message(ws, msg);
938
+ else if (t === "global") globalWebSocket.message(ws, msg);
939
+ else if (t === "remote-desktop") remoteDesktopWebSocket.message(ws, msg);
940
+ else if (t === "terminal") terminalWebSocket.message(ws, msg);
916
941
  },
917
942
  close(ws: any) {
918
- if (ws.data?.type === "chat") chatWebSocket.close(ws);
919
- else if (ws.data?.type === "group") groupChatWebSocket.close(ws);
920
- else if (ws.data?.type === "extensions") extensionWebSocket.close(ws);
921
- else if (ws.data?.type === "global") globalWebSocket.close(ws);
922
- else terminalWebSocket.close(ws);
943
+ const t = ws.data?.type;
944
+ if (t === "chat") chatWebSocket.close(ws);
945
+ else if (t === "group") groupChatWebSocket.close(ws);
946
+ else if (t === "extensions") extensionWebSocket.close(ws);
947
+ else if (t === "global") globalWebSocket.close(ws);
948
+ else if (t === "remote-desktop") remoteDesktopWebSocket.close(ws);
949
+ else if (t === "terminal") terminalWebSocket.close(ws);
923
950
  },
924
951
  } as Parameters<typeof Bun.serve>[0] extends { websocket?: infer W } ? W : never,
925
952
  });
@@ -376,8 +376,36 @@ accountsRoutes.patch("/:id", async (c) => {
376
376
  const body = await c.req.json<{ status?: string }>();
377
377
  try {
378
378
  if (body.status === "disabled") accountService.setDisabled(id);
379
- else if (body.status === "active") accountService.setEnabled(id);
380
- else return c.json(err("status must be active or disabled"), 400);
379
+ else if (body.status === "active") {
380
+ const wasParked = accountService.list().find((a) => a.id === id)?.status === "disabled";
381
+ // Enabling is where this machine claims the token, so prove it *before* the account
382
+ // becomes selectable rather than after. postRefreshGrant is three 15s attempts with
383
+ // 2s and 6s backoff between them — 53s worst case — so enabling first would leave the
384
+ // account active with an unproven token for most of a minute, long enough for
385
+ // accountSelector.next() or the proxy to pick it, and the re-park would land only
386
+ // after that turn had already failed. Refreshing while still
387
+ // parked needs no rollback at all: on failure setEnabled() simply never runs.
388
+ //
389
+ // Only for an account that has a refresh token: a temporary one has nothing to prove,
390
+ // and setEnabled()'s own guard says something more useful than a refresh error would.
391
+ if (wasParked && accountService.hasRefreshToken(id)) {
392
+ const proof = await accountService.ensureFreshTokenChecked(id);
393
+ if (!proof.account) {
394
+ // Worth splitting: invalid_grant means the token is gone and only a fresh sign-in
395
+ // brings it back, while a network drop or a 429 is worth trying again in a minute.
396
+ // Telling someone to re-authenticate because their wifi blinked is a bad trade.
397
+ return c.json(
398
+ err(
399
+ proof.rejected
400
+ ? "This account's login was rejected — it stays disabled. Sign in again to give it a live token, then enable it."
401
+ : "Could not reach Anthropic to check this account — it stays disabled. Try again in a moment.",
402
+ ),
403
+ 400,
404
+ );
405
+ }
406
+ }
407
+ accountService.setEnabled(id);
408
+ } else return c.json(err("status must be active or disabled"), 400);
381
409
  } catch (e) {
382
410
  return c.json(err((e as Error).message), 400);
383
411
  }
@@ -802,7 +802,7 @@ chatRoutes.post("/sessions/:id/images/strip", async (c) => {
802
802
  const mode: StripMode = (body as { mode?: unknown }).mode === "all" ? "all" : "oversized";
803
803
  const includeAttachments = (body as { includeAttachments?: unknown }).includeAttachments === true;
804
804
 
805
- const { listRunningSessions } = await import("../ws/chat.ts");
805
+ const { listRunningSessions, dropSubprocessForTranscriptRewrite } = await import("../ws/chat.ts");
806
806
  if (listRunningSessions().some((s) => s.sessionId === sessionId)) {
807
807
  return c.json(err("Session is running — wait for the turn to finish"), 409);
808
808
  }
@@ -810,6 +810,13 @@ chatRoutes.post("/sessions/:id/images/strip", async (c) => {
810
810
  const found = locateTranscript(sessionId);
811
811
  if ("error" in found) return c.json(err(found.error), found.status);
812
812
 
813
+ // A subprocess that is merely idle is not "running", but it still holds the conversation
814
+ // in memory — including the image being stripped. Leaving it alive means the next turn
815
+ // re-sends the oversized attachment from memory and fails exactly as before, which is the
816
+ // failure this endpoint exists to clear. Drop it so the turn is rebuilt from the file we
817
+ // are about to rewrite.
818
+ dropSubprocessForTranscriptRewrite(sessionId);
819
+
813
820
  const result = await stripTranscriptImagesFile(found.path, mode, { includeAttachments });
814
821
  return c.json(ok({
815
822
  removed: result.removed,
@@ -28,9 +28,12 @@ function assertMutationAllowed(c: Context): Response | null {
28
28
  }
29
29
  const origin = c.req.header("origin");
30
30
  if (origin) {
31
+ // Compare hostname only, not host:port: a 2-process dev setup (Vite proxy rewrites
32
+ // Host without X-Forwarded-Host) always differs by port, so a full-host compare rejects
33
+ // every dev request. Origin is browser-set, so a real cross-site attacker still can't forge it.
31
34
  let originHost: string | null = null;
32
- try { originHost = new URL(origin).host; } catch { originHost = null; }
33
- const requestHost = new URL(c.req.url).host;
35
+ try { originHost = new URL(origin).hostname; } catch { originHost = null; }
36
+ const requestHost = new URL(c.req.url).hostname;
34
37
  if (!originHost || originHost !== requestHost) {
35
38
  return c.json(err("cross-origin request rejected"), 403);
36
39
  }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * `GET /api/remote-desktop/capabilities` (read-only, always answerable) and
3
+ * `POST /api/remote-desktop/session` (mints the single-use WS nonce). Guard style mirrors
4
+ * `named-tunnel.ts`: `authMiddleware` already passes every request through when PPM auth is
5
+ * disabled, so a feature that hands out host control must enforce `auth.enabled` itself, plus
6
+ * a same-origin check so a foreign page can't drive it via ambient browser credentials.
7
+ */
8
+ import { Hono, type Context } from "hono";
9
+ import { ok, err } from "../../types/api.ts";
10
+ import { configService } from "../../services/config.service.ts";
11
+ import { getFfmpegCapabilities } from "../../services/media-transcode/ffmpeg-capabilities.ts";
12
+ import { isRemoteDesktopEnabled } from "../../services/remote-desktop/remote-desktop-flag.ts";
13
+ import { mintRemoteDesktopNonce } from "../../services/remote-desktop/remote-desktop-nonce.ts";
14
+ import { isInputAvailable } from "../../services/remote-desktop/remote-desktop-input.ts";
15
+
16
+ export const remoteDesktopRoutes = new Hono();
17
+
18
+ function assertSessionAllowed(c: Context): Response | null {
19
+ if (!isRemoteDesktopEnabled()) {
20
+ return c.json(err("remote desktop is disabled (set REMOTE_DESKTOP_ENABLED=1)"), 404);
21
+ }
22
+ if (!configService.get("auth").enabled) {
23
+ return c.json(err("remote desktop requires PPM authentication to be enabled"), 403);
24
+ }
25
+ const origin = c.req.header("origin");
26
+ if (origin) {
27
+ // Compare *hostname* only, not host:port. `named-tunnel.ts`'s copy of this check compares
28
+ // the full `host` (hostname:port), which only ever matches when the browser's page and the
29
+ // API it's calling share one origin verbatim — true for the compiled/production build (one
30
+ // server, one port) but NOT for the standard two-process dev topology (`bun dev:web` on
31
+ // 5173 proxying to `bun dev:server` on 8081, or this feature's own alt-port test setup):
32
+ // Vite's dev proxy rewrites the `Host` header to the proxy target's port without adding
33
+ // `X-Forwarded-Host`, so the backend always sees a different port than the browser's real
34
+ // `Origin` — the full-host check would reject every dev-mode session request. A real
35
+ // cross-origin attacker (e.g. evil.com) cannot spoof `Origin` to say `localhost` in the
36
+ // first place — the browser sets it from the page's actual origin — so relaxing port
37
+ // equality does not weaken the protection this check exists for.
38
+ let originHost: string | null = null;
39
+ try { originHost = new URL(origin).hostname; } catch { originHost = null; }
40
+ let requestHost: string | null = null;
41
+ try { requestHost = new URL(c.req.url).hostname; } catch { requestHost = null; }
42
+ if (!originHost || !requestHost || originHost !== requestHost) {
43
+ return c.json(err("cross-origin request rejected"), 403);
44
+ }
45
+ }
46
+ return null;
47
+ }
48
+
49
+ remoteDesktopRoutes.get("/capabilities", async (c) => {
50
+ if (!isRemoteDesktopEnabled()) return c.json(err("remote desktop is disabled"), 404);
51
+ const caps = await getFfmpegCapabilities();
52
+ return c.json(ok({
53
+ ffmpegAvailable: !!caps.ffmpeg,
54
+ videoAvailable: process.platform === "win32" && !!caps.ffmpeg,
55
+ inputAvailable: isInputAvailable(),
56
+ authRequired: configService.get("auth").enabled,
57
+ }));
58
+ });
59
+
60
+ remoteDesktopRoutes.post("/session", (c) => {
61
+ const rejected = assertSessionAllowed(c);
62
+ if (rejected) return rejected;
63
+ return c.json(ok({ nonce: mintRemoteDesktopNonce(), wsPath: "/ws/remote-desktop" }));
64
+ });
@@ -114,6 +114,7 @@ const UI_PREF_VALIDATORS: Record<string, (v: unknown) => boolean> = {
114
114
  wordWrap: (v) => typeof v === "boolean",
115
115
  tabWrap: (v) => typeof v === "boolean",
116
116
  sidebarCollapsed: (v) => typeof v === "boolean",
117
+ remoteDesktopStatsVisible: (v) => typeof v === "boolean",
117
118
  sidebarWidth: (v) => typeof v === "number" && v >= 200 && v <= 600,
118
119
  gitStatusViewMode: (v) => v === "flat" || v === "tree",
119
120
  editorTabStyle: (v) => v === "default" || v === "boxed" || v === "pill",