@hienlh/ppm 0.23.6 → 0.23.7

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 (701) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/CLAUDE.md +21 -1
  3. package/Dockerfile.test +8 -1
  4. package/assets/skills/ppm/SKILL.md +1 -1
  5. package/assets/skills/ppm/references/cli-reference.md +4 -4
  6. package/assets/skills/ppm/references/http-api.md +29 -1
  7. package/dist/web/assets/accounts-settings-section-BYWnY9hH.js +1 -0
  8. package/dist/web/assets/accounts-settings-section-BYWnY9hH.js.br +0 -0
  9. package/dist/web/assets/accounts-settings-section-BYWnY9hH.js.gz +0 -0
  10. package/dist/web/assets/{ai-resource-editor-Dn_wB-o5.js → ai-resource-editor-CY4n_Ccv.js} +1 -1
  11. package/dist/web/assets/ai-resource-editor-CY4n_Ccv.js.br +0 -0
  12. package/dist/web/assets/ai-resource-editor-CY4n_Ccv.js.gz +0 -0
  13. package/dist/web/assets/ai-settings-section-Bs3iUW4j.js +1 -0
  14. package/dist/web/assets/ai-settings-section-Bs3iUW4j.js.br +0 -0
  15. package/dist/web/assets/ai-settings-section-Bs3iUW4j.js.gz +0 -0
  16. package/dist/web/assets/ai-settings-section-DDs11XV4.js +1 -0
  17. package/dist/web/assets/api-client-CJPnCOG-.js +1 -0
  18. package/dist/web/assets/{api-client-c8YOpTlT.js → api-client-D-SMbdiH.js} +1 -1
  19. package/dist/web/assets/api-client-D-SMbdiH.js.br +0 -0
  20. package/dist/web/assets/api-client-D-SMbdiH.js.gz +0 -0
  21. package/dist/web/assets/api-settings-BDkkhFMq.js +1 -0
  22. package/dist/web/assets/{api-settings-BmvNoCbn.js → api-settings-Bv9mLCty.js} +1 -1
  23. package/dist/web/assets/api-settings-Bv9mLCty.js.br +0 -0
  24. package/dist/web/assets/api-settings-Bv9mLCty.js.gz +0 -0
  25. package/dist/web/assets/{api-themes-6fOQJgM9.js → api-themes-3wW-zLzU.js} +1 -1
  26. package/dist/web/assets/appearance-settings-section-C0BoIBmz.js +1 -0
  27. package/dist/web/assets/appearance-settings-section-C0BoIBmz.js.br +0 -0
  28. package/dist/web/assets/appearance-settings-section-C0BoIBmz.js.gz +0 -0
  29. package/dist/web/assets/apps-panel-DXAFsveZ.js +1 -0
  30. package/dist/web/assets/apps-panel-DXAFsveZ.js.br +0 -0
  31. package/dist/web/assets/apps-panel-DXAFsveZ.js.gz +0 -0
  32. package/dist/web/assets/{audio-preview-vaG2cB-k.js → audio-preview-BfES4Cx5.js} +1 -1
  33. package/dist/web/assets/audio-preview-BfES4Cx5.js.br +0 -0
  34. package/dist/web/assets/audio-preview-BfES4Cx5.js.gz +0 -0
  35. package/dist/web/assets/branch-review-tab-CA8hPSX9.js +1 -0
  36. package/dist/web/assets/branch-review-tab-CA8hPSX9.js.br +0 -0
  37. package/dist/web/assets/branch-review-tab-CA8hPSX9.js.gz +0 -0
  38. package/dist/web/assets/{button-Ci9EUgqd.js → button-DeAyLH9U.js} +1 -1
  39. package/dist/web/assets/button-DeAyLH9U.js.br +0 -0
  40. package/dist/web/assets/button-DeAyLH9U.js.gz +0 -0
  41. package/dist/web/assets/{chat-preparation-cache-BSIQGiWx.js → chat-preparation-cache-CcCd2g7A.js} +1 -1
  42. package/dist/web/assets/chat-preparation-cache-CcCd2g7A.js.br +0 -0
  43. package/dist/web/assets/chat-preparation-cache-CcCd2g7A.js.gz +0 -0
  44. package/dist/web/assets/chat-tab-BWi_bUzA.js +1 -0
  45. package/dist/web/assets/chat-tab-BWi_bUzA.js.br +0 -0
  46. package/dist/web/assets/chat-tab-BWi_bUzA.js.gz +0 -0
  47. package/dist/web/assets/chat-tab-BvJhIaVE.js +15 -0
  48. package/dist/web/assets/chat-tab-BvJhIaVE.js.br +0 -0
  49. package/dist/web/assets/chat-tab-BvJhIaVE.js.gz +0 -0
  50. package/dist/web/assets/code-editor-OJUdLVnn.js +7 -0
  51. package/dist/web/assets/code-editor-OJUdLVnn.js.br +0 -0
  52. package/dist/web/assets/code-editor-OJUdLVnn.js.gz +0 -0
  53. package/dist/web/assets/codex-reset-credit-button-Dgxy_VNp.js +1 -0
  54. package/dist/web/assets/codex-reset-credit-button-Dgxy_VNp.js.br +0 -0
  55. package/dist/web/assets/codex-reset-credit-button-Dgxy_VNp.js.gz +0 -0
  56. package/dist/web/assets/{conflict-editor-BA3y-qSH.js → conflict-editor-BpKW_vbL.js} +1 -1
  57. package/dist/web/assets/conflict-editor-BpKW_vbL.js.br +0 -0
  58. package/dist/web/assets/conflict-editor-BpKW_vbL.js.gz +0 -0
  59. package/dist/web/assets/context-menu-7OX80HDB.js +1 -0
  60. package/dist/web/assets/context-menu-7OX80HDB.js.br +0 -0
  61. package/dist/web/assets/context-menu-7OX80HDB.js.gz +0 -0
  62. package/dist/web/assets/{csv-preview-BioxkrQF.js → csv-preview-COYdTABa.js} +1 -1
  63. package/dist/web/assets/csv-preview-COYdTABa.js.br +0 -0
  64. package/dist/web/assets/csv-preview-COYdTABa.js.gz +0 -0
  65. package/dist/web/assets/{data-grid-overlay-editor-CQRVKr7y.js → data-grid-overlay-editor-CeZoduke.js} +1 -1
  66. package/dist/web/assets/data-grid-overlay-editor-CeZoduke.js.br +0 -0
  67. package/dist/web/assets/data-grid-overlay-editor-CeZoduke.js.gz +0 -0
  68. package/dist/web/assets/{database-viewer-BSoJ0E6p.js → database-viewer-BQcBqCKm.js} +1 -1
  69. package/dist/web/assets/database-viewer-BQcBqCKm.js.br +0 -0
  70. package/dist/web/assets/database-viewer-BQcBqCKm.js.gz +0 -0
  71. package/dist/web/assets/{design-export-client-ChMcMoqW.js → design-export-client-OrnGCSwu.js} +1 -1
  72. package/dist/web/assets/design-export-client-OrnGCSwu.js.br +0 -0
  73. package/dist/web/assets/design-export-client-OrnGCSwu.js.gz +0 -0
  74. package/dist/web/assets/{design-responsive-dialog-DNBhJJLf.js → design-responsive-dialog-vJjQrC4n.js} +1 -1
  75. package/dist/web/assets/design-responsive-dialog-vJjQrC4n.js.br +0 -0
  76. package/dist/web/assets/design-responsive-dialog-vJjQrC4n.js.gz +0 -0
  77. package/dist/web/assets/design-tab-BbmFk38H.js +33 -0
  78. package/dist/web/assets/design-tab-BbmFk38H.js.br +0 -0
  79. package/dist/web/assets/design-tab-BbmFk38H.js.gz +0 -0
  80. package/dist/web/assets/{designs-sidebar-panel-DHh5QsH9.js → designs-sidebar-panel-D2kXwB8B.js} +1 -1
  81. package/dist/web/assets/designs-sidebar-panel-D2kXwB8B.js.br +0 -0
  82. package/dist/web/assets/designs-sidebar-panel-D2kXwB8B.js.gz +0 -0
  83. package/dist/web/assets/dialog-Bocc0Fgb.js +1 -0
  84. package/dist/web/assets/dialog-Bocc0Fgb.js.br +0 -0
  85. package/dist/web/assets/dialog-Bocc0Fgb.js.gz +0 -0
  86. package/dist/web/assets/{diff-viewer-lctWoSmG.js → diff-viewer-Bj4DT6Mj.js} +1 -1
  87. package/dist/web/assets/diff-viewer-Bj4DT6Mj.js.br +0 -0
  88. package/dist/web/assets/diff-viewer-Bj4DT6Mj.js.gz +0 -0
  89. package/dist/web/assets/{docx-preview-C9XdKEj1.js → docx-preview-C2QqNGRE.js} +1 -1
  90. package/dist/web/assets/docx-preview-C2QqNGRE.js.br +0 -0
  91. package/dist/web/assets/docx-preview-C2QqNGRE.js.gz +0 -0
  92. package/dist/web/assets/editor-language-service-COqB2tuv.js +1 -0
  93. package/dist/web/assets/editor-language-service-COqB2tuv.js.br +3 -0
  94. package/dist/web/assets/editor-language-service-COqB2tuv.js.gz +0 -0
  95. package/dist/web/assets/explorer-body-DGrYajDN.js +2 -0
  96. package/dist/web/assets/explorer-body-DGrYajDN.js.br +0 -0
  97. package/dist/web/assets/explorer-body-DGrYajDN.js.gz +0 -0
  98. package/dist/web/assets/explorer-window-content-DLzkODTP.js +1 -0
  99. package/dist/web/assets/explorer-window-content-DLzkODTP.js.br +0 -0
  100. package/dist/web/assets/explorer-window-content-DLzkODTP.js.gz +0 -0
  101. package/dist/web/assets/{extension-manager-section-rbkXsZVR.js → extension-manager-section-Bu0qcB2i.js} +1 -1
  102. package/dist/web/assets/extension-manager-section-Bu0qcB2i.js.br +0 -0
  103. package/dist/web/assets/extension-manager-section-Bu0qcB2i.js.gz +0 -0
  104. package/dist/web/assets/{extension-webview-uPO0WgCq.js → extension-webview-G_WJ0N52.js} +1 -1
  105. package/dist/web/assets/extension-webview-G_WJ0N52.js.br +0 -0
  106. package/dist/web/assets/extension-webview-G_WJ0N52.js.gz +0 -0
  107. package/dist/web/assets/{file-icons-gx_FBOXY.js → file-icons-l7fNOmdS.js} +1 -1
  108. package/dist/web/assets/file-icons-l7fNOmdS.js.br +0 -0
  109. package/dist/web/assets/file-icons-l7fNOmdS.js.gz +0 -0
  110. package/dist/web/assets/{file-store-D9KE0kQC.js → file-store-B0e7sLag.js} +1 -1
  111. package/dist/web/assets/file-store-B0e7sLag.js.br +0 -0
  112. package/dist/web/assets/file-store-B0e7sLag.js.gz +0 -0
  113. package/dist/web/assets/{files-settings-section-CT7mDb0L.js → files-settings-section-ChZ_CynX.js} +1 -1
  114. package/dist/web/assets/files-settings-section-ChZ_CynX.js.br +0 -0
  115. package/dist/web/assets/files-settings-section-ChZ_CynX.js.gz +0 -0
  116. package/dist/web/assets/format-bytes-__396eCN.js +1 -0
  117. package/dist/web/assets/{format-file-meta-mQwA6SuY.js → format-file-meta-o1XBuoKS.js} +1 -1
  118. package/dist/web/assets/format-file-meta-o1XBuoKS.js.br +0 -0
  119. package/dist/web/assets/format-file-meta-o1XBuoKS.js.gz +0 -0
  120. package/dist/web/assets/{fs-api-DL36slbd.js → fs-api-CeHKwx95.js} +1 -1
  121. package/dist/web/assets/fs-api-CeHKwx95.js.br +0 -0
  122. package/dist/web/assets/fs-api-CeHKwx95.js.gz +0 -0
  123. package/dist/web/assets/{general-settings-section-DANPfMRN.js → general-settings-section-MSvDEbFa.js} +1 -1
  124. package/dist/web/assets/general-settings-section-MSvDEbFa.js.br +0 -0
  125. package/dist/web/assets/general-settings-section-MSvDEbFa.js.gz +0 -0
  126. package/dist/web/assets/{git-log-panel-BJPkLiEw.js → git-log-panel-gw5_a6j3.js} +1 -1
  127. package/dist/web/assets/git-log-panel-gw5_a6j3.js.br +0 -0
  128. package/dist/web/assets/git-log-panel-gw5_a6j3.js.gz +0 -0
  129. package/dist/web/assets/{git-repo-store-DSypNj19.js → git-repo-store-BpPnah5l.js} +1 -1
  130. package/dist/web/assets/git-repo-store-BpPnah5l.js.br +0 -0
  131. package/dist/web/assets/git-repo-store-BpPnah5l.js.gz +0 -0
  132. package/dist/web/assets/{glide-data-grid-R4PcAntf.js → glide-data-grid-Db2Cl26B.js} +5 -5
  133. package/dist/web/assets/glide-data-grid-Db2Cl26B.js.br +0 -0
  134. package/dist/web/assets/glide-data-grid-Db2Cl26B.js.gz +0 -0
  135. package/dist/web/assets/group-chat-tab-B4etmYTR.js +1 -0
  136. package/dist/web/assets/group-chat-tab-B4etmYTR.js.br +0 -0
  137. package/dist/web/assets/group-chat-tab-B4etmYTR.js.gz +0 -0
  138. package/dist/web/assets/{image-preview-CGdZrmLo.js → image-preview-CjNhpfWw.js} +1 -1
  139. package/dist/web/assets/index-CtAvmybi.js +52 -0
  140. package/dist/web/assets/index-CtAvmybi.js.br +0 -0
  141. package/dist/web/assets/index-CtAvmybi.js.gz +0 -0
  142. package/dist/web/assets/index-DeCJasyy.css +1 -0
  143. package/dist/web/assets/index-DeCJasyy.css.br +0 -0
  144. package/dist/web/assets/index-DeCJasyy.css.gz +0 -0
  145. package/dist/web/assets/{jira-watcher-section-DHRoyWcG.js → jira-watcher-section-CKSI1Alo.js} +1 -1
  146. package/dist/web/assets/keybindings-store-C4_yyU4u.js +1 -0
  147. package/dist/web/assets/{keyboard-shortcuts-section-BKej4mUf.js → keyboard-shortcuts-section-CEaVUc9a.js} +1 -1
  148. package/dist/web/assets/keyboard-shortcuts-section-CEaVUc9a.js.br +0 -0
  149. package/dist/web/assets/keyboard-shortcuts-section-CEaVUc9a.js.gz +0 -0
  150. package/dist/web/assets/language-servers-section-ePtEpnDc.js +2 -0
  151. package/dist/web/assets/language-servers-section-ePtEpnDc.js.br +0 -0
  152. package/dist/web/assets/language-servers-section-ePtEpnDc.js.gz +0 -0
  153. package/dist/web/assets/lsp-client-XzmR3Brr.js +1 -0
  154. package/dist/web/assets/lsp-client-XzmR3Brr.js.br +0 -0
  155. package/dist/web/assets/lsp-client-XzmR3Brr.js.gz +0 -0
  156. package/dist/web/assets/markdown-renderer-CTtDap-l.js +1 -0
  157. package/dist/web/assets/{markdown-renderer-BHtYqkYZ.js → markdown-renderer-D05fAr57.js} +2 -2
  158. package/dist/web/assets/markdown-renderer-D05fAr57.js.br +0 -0
  159. package/dist/web/assets/markdown-renderer-D05fAr57.js.gz +0 -0
  160. package/dist/web/assets/{media-url-CzUJrREv.js → media-url-C6NumuN5.js} +1 -1
  161. package/dist/web/assets/metric-chart-canvas-DHJe0VER.js +1 -0
  162. package/dist/web/assets/metric-chart-canvas-DHJe0VER.js.br +0 -0
  163. package/dist/web/assets/metric-chart-canvas-DHJe0VER.js.gz +0 -0
  164. package/dist/web/assets/{mobile-bottom-sheet-CuNi2FRv.js → mobile-bottom-sheet-D4UyOatB.js} +1 -1
  165. package/dist/web/assets/mobile-bottom-sheet-D4UyOatB.js.br +0 -0
  166. package/dist/web/assets/mobile-bottom-sheet-D4UyOatB.js.gz +0 -0
  167. package/dist/web/assets/mobile-explorer-sheet-CcfGdGL8.js +1 -0
  168. package/dist/web/assets/mobile-explorer-sheet-CcfGdGL8.js.br +0 -0
  169. package/dist/web/assets/mobile-explorer-sheet-CcfGdGL8.js.gz +0 -0
  170. package/dist/web/assets/{new-design-dialog-DS_XrpTG.js → new-design-dialog-BgmQopJM.js} +1 -1
  171. package/dist/web/assets/new-design-dialog-BgmQopJM.js.br +0 -0
  172. package/dist/web/assets/new-design-dialog-BgmQopJM.js.gz +0 -0
  173. package/dist/web/assets/notification-store-q5QBklKA.js +1 -0
  174. package/dist/web/assets/{notifications-settings-section-C05WYqcE.js → notifications-settings-section-BBJ0r9OS.js} +1 -1
  175. package/dist/web/assets/notifications-settings-section-BBJ0r9OS.js.br +0 -0
  176. package/dist/web/assets/notifications-settings-section-BBJ0r9OS.js.gz +0 -0
  177. package/dist/web/assets/{onboarding-entry-CmUqv1kl.js → onboarding-entry-BYOYswsi.js} +1 -1
  178. package/dist/web/assets/onboarding-entry-BYOYswsi.js.br +0 -0
  179. package/dist/web/assets/onboarding-entry-BYOYswsi.js.gz +0 -0
  180. package/dist/web/assets/{onboarding-quick-orientation-DIhWv1CN.js → onboarding-quick-orientation-DPHYFMbA.js} +1 -1
  181. package/dist/web/assets/onboarding-quick-orientation-DPHYFMbA.js.br +0 -0
  182. package/dist/web/assets/onboarding-quick-orientation-DPHYFMbA.js.gz +0 -0
  183. package/dist/web/assets/overview-panel-pkPs44Tv.js +1 -0
  184. package/dist/web/assets/overview-panel-pkPs44Tv.js.br +0 -0
  185. package/dist/web/assets/overview-panel-pkPs44Tv.js.gz +0 -0
  186. package/dist/web/assets/{pdf-preview-qdXmMkOR.js → pdf-preview-c-L_kDAs.js} +1 -1
  187. package/dist/web/assets/pdf-preview-c-L_kDAs.js.br +0 -0
  188. package/dist/web/assets/pdf-preview-c-L_kDAs.js.gz +0 -0
  189. package/dist/web/assets/performance-panel-yPrq0DJV.js +3 -0
  190. package/dist/web/assets/performance-panel-yPrq0DJV.js.br +0 -0
  191. package/dist/web/assets/performance-panel-yPrq0DJV.js.gz +0 -0
  192. package/dist/web/assets/{postgres-viewer-Xvlun_n5.js → postgres-viewer-CnwDCMNd.js} +1 -1
  193. package/dist/web/assets/postgres-viewer-CnwDCMNd.js.br +0 -0
  194. package/dist/web/assets/postgres-viewer-CnwDCMNd.js.gz +0 -0
  195. package/dist/web/assets/{ppmbot-settings-section-D8AES1BA.js → ppmbot-settings-section-BDXoSIDp.js} +1 -1
  196. package/dist/web/assets/ppmbot-settings-section-BDXoSIDp.js.br +0 -0
  197. package/dist/web/assets/ppmbot-settings-section-BDXoSIDp.js.gz +0 -0
  198. package/dist/web/assets/{pptx-export-CG6MRNok.js → pptx-export-D0RDY48h.js} +4 -4
  199. package/dist/web/assets/pptx-export-D0RDY48h.js.br +0 -0
  200. package/dist/web/assets/pptx-export-D0RDY48h.js.gz +0 -0
  201. package/dist/web/assets/{problems-panel-DTBG4beI.js → problems-panel-BgY8oyo9.js} +1 -1
  202. package/dist/web/assets/problems-panel-BgY8oyo9.js.br +0 -0
  203. package/dist/web/assets/problems-panel-BgY8oyo9.js.gz +0 -0
  204. package/dist/web/assets/process-row-format-CaaMcHzn.js +1 -0
  205. package/dist/web/assets/process-table-BOwDtXuG.js +1 -0
  206. package/dist/web/assets/process-table-BOwDtXuG.js.br +0 -0
  207. package/dist/web/assets/process-table-BOwDtXuG.js.gz +0 -0
  208. package/dist/web/assets/{project-framework-store-XxFXcUpb.js → project-framework-store-UDEMu28U.js} +1 -1
  209. package/dist/web/assets/{project-store-C5gJXkvb.js → project-store-DGa31y5B.js} +1 -1
  210. package/dist/web/assets/project-store-DGa31y5B.js.br +0 -0
  211. package/dist/web/assets/project-store-DGa31y5B.js.gz +0 -0
  212. package/dist/web/assets/{proxy-settings-section-CqtmbeRj.js → proxy-settings-section-Db6Kc6xG.js} +1 -1
  213. package/dist/web/assets/proxy-settings-section-Db6Kc6xG.js.br +0 -0
  214. package/dist/web/assets/proxy-settings-section-Db6Kc6xG.js.gz +0 -0
  215. package/dist/web/assets/{query-audit-section-UKG_smyt.js → query-audit-section-C0PAXhfT.js} +1 -1
  216. package/dist/web/assets/query-audit-section-C0PAXhfT.js.br +0 -0
  217. package/dist/web/assets/query-audit-section-C0PAXhfT.js.gz +0 -0
  218. package/dist/web/assets/{remote-desktop-clipboard-notice-Djt6cpNL.js → remote-desktop-clipboard-notice-DOVtobj_.js} +1 -1
  219. package/dist/web/assets/remote-desktop-clipboard-notice-DOVtobj_.js.br +0 -0
  220. package/dist/web/assets/remote-desktop-clipboard-notice-DOVtobj_.js.gz +0 -0
  221. package/dist/web/assets/remote-desktop-mobile-sheet-DXlU3msM.js +2 -0
  222. package/dist/web/assets/remote-desktop-mobile-sheet-DXlU3msM.js.br +0 -0
  223. package/dist/web/assets/remote-desktop-mobile-sheet-DXlU3msM.js.gz +0 -0
  224. package/dist/web/assets/remote-desktop-mobile-view-ClV4YTLU.js +1 -0
  225. package/dist/web/assets/remote-desktop-mobile-view-ClV4YTLU.js.br +0 -0
  226. package/dist/web/assets/remote-desktop-mobile-view-ClV4YTLU.js.gz +0 -0
  227. package/dist/web/assets/{remote-desktop-readiness-gate-DNJF8TcF.js → remote-desktop-readiness-gate-BsfoJzdc.js} +1 -1
  228. package/dist/web/assets/remote-desktop-readiness-gate-BsfoJzdc.js.br +0 -0
  229. package/dist/web/assets/remote-desktop-readiness-gate-BsfoJzdc.js.gz +0 -0
  230. package/dist/web/assets/remote-desktop-window-content-kDaXl1_Q.js +1 -0
  231. package/dist/web/assets/remote-desktop-window-content-kDaXl1_Q.js.br +0 -0
  232. package/dist/web/assets/remote-desktop-window-content-kDaXl1_Q.js.gz +0 -0
  233. package/dist/web/assets/{schedules-settings-section-BKoPovy2.js → schedules-settings-section-CvbjQM8z.js} +1 -1
  234. package/dist/web/assets/schedules-settings-section-CvbjQM8z.js.br +0 -0
  235. package/dist/web/assets/schedules-settings-section-CvbjQM8z.js.gz +0 -0
  236. package/dist/web/assets/{scroll-area-XonvWYn_.js → scroll-area-sjOeAfoD.js} +1 -1
  237. package/dist/web/assets/scroll-area-sjOeAfoD.js.br +0 -0
  238. package/dist/web/assets/scroll-area-sjOeAfoD.js.gz +0 -0
  239. package/dist/web/assets/{separator-sQj04Ari.js → separator-9A3PCG1t.js} +1 -1
  240. package/dist/web/assets/services-panel-CimW47P8.js +1 -0
  241. package/dist/web/assets/services-panel-CimW47P8.js.br +0 -0
  242. package/dist/web/assets/services-panel-CimW47P8.js.gz +0 -0
  243. package/dist/web/assets/settings-body-BTXNtpWL.js +2 -0
  244. package/dist/web/assets/settings-body-BTXNtpWL.js.br +0 -0
  245. package/dist/web/assets/settings-body-BTXNtpWL.js.gz +0 -0
  246. package/dist/web/assets/settings-store-DvftLqzy.js +2 -0
  247. package/dist/web/assets/settings-store-DvftLqzy.js.br +0 -0
  248. package/dist/web/assets/settings-store-DvftLqzy.js.gz +0 -0
  249. package/dist/web/assets/settings-tab-CGhaRXjs.js +1 -0
  250. package/dist/web/assets/settings-window-content--Uyg_Psr.js +1 -0
  251. package/dist/web/assets/sortable-header-DUW9T1Hz.js +1 -0
  252. package/dist/web/assets/sortable-header-DUW9T1Hz.js.br +0 -0
  253. package/dist/web/assets/sortable-header-DUW9T1Hz.js.gz +0 -0
  254. package/dist/web/assets/{sql-query-editor-BjIMi9Wk.js → sql-query-editor-Dn-cgDrW.js} +1 -1
  255. package/dist/web/assets/sql-query-editor-Dn-cgDrW.js.br +0 -0
  256. package/dist/web/assets/sql-query-editor-Dn-cgDrW.js.gz +0 -0
  257. package/dist/web/assets/{sqlite-viewer-BNcA6Irq.js → sqlite-viewer-B0NfaMjq.js} +1 -1
  258. package/dist/web/assets/sqlite-viewer-B0NfaMjq.js.br +0 -0
  259. package/dist/web/assets/sqlite-viewer-B0NfaMjq.js.gz +0 -0
  260. package/dist/web/assets/system-monitor-body-CxCfRQFb.js +2 -0
  261. package/dist/web/assets/system-monitor-body-CxCfRQFb.js.br +0 -0
  262. package/dist/web/assets/system-monitor-body-CxCfRQFb.js.gz +0 -0
  263. package/dist/web/assets/system-monitor-tab-Cb3EZUdp.js +1 -0
  264. package/dist/web/assets/system-monitor-window-content-51CA-2PH.js +1 -0
  265. package/dist/web/assets/{tab-host-window-content-CIRGNCvi.js → tab-host-window-content-CSrcmKNG.js} +1 -1
  266. package/dist/web/assets/tab-host-window-content-CSrcmKNG.js.br +0 -0
  267. package/dist/web/assets/tab-host-window-content-CSrcmKNG.js.gz +0 -0
  268. package/dist/web/assets/{team-member-sheet-CwXpLi3X.js → team-member-sheet-D4X6nh-x.js} +2 -2
  269. package/dist/web/assets/team-member-sheet-D4X6nh-x.js.br +0 -0
  270. package/dist/web/assets/team-member-sheet-D4X6nh-x.js.gz +0 -0
  271. package/dist/web/assets/{team-member-window-content-4swiiDz7.js → team-member-window-content-BxkbGVIL.js} +1 -1
  272. package/dist/web/assets/team-member-window-content-BxkbGVIL.js.br +0 -0
  273. package/dist/web/assets/team-member-window-content-BxkbGVIL.js.gz +0 -0
  274. package/dist/web/assets/{terminal-tab-BGcdtMMT.js → terminal-tab-BYo34IqU.js} +3 -3
  275. package/dist/web/assets/terminal-tab-BYo34IqU.js.br +0 -0
  276. package/dist/web/assets/terminal-tab-BYo34IqU.js.gz +0 -0
  277. package/dist/web/assets/{tool-cards-D0-krtA2.js → tool-cards-DvXjOped.js} +4 -4
  278. package/dist/web/assets/tool-cards-DvXjOped.js.br +0 -0
  279. package/dist/web/assets/tool-cards-DvXjOped.js.gz +0 -0
  280. package/dist/web/assets/{use-blob-url-CmavyEP8.js → use-blob-url-CISBWKG5.js} +1 -1
  281. package/dist/web/assets/use-inline-blame-Br6mheS4.js +16 -0
  282. package/dist/web/assets/use-inline-blame-Br6mheS4.js.br +0 -0
  283. package/dist/web/assets/use-inline-blame-Br6mheS4.js.gz +0 -0
  284. package/dist/web/assets/use-lsp-BpJmvQ0n.js +6 -0
  285. package/dist/web/assets/use-lsp-BpJmvQ0n.js.br +0 -0
  286. package/dist/web/assets/use-lsp-BpJmvQ0n.js.gz +0 -0
  287. package/dist/web/assets/{use-monaco-theme-C88KphGx.js → use-monaco-theme-UGOzyIhv.js} +1 -1
  288. package/dist/web/assets/use-process-kill-CyiiUD1E.js +1 -0
  289. package/dist/web/assets/use-process-kill-CyiiUD1E.js.br +0 -0
  290. package/dist/web/assets/use-process-kill-CyiiUD1E.js.gz +0 -0
  291. package/dist/web/assets/{use-websocket-Chk9guIU.js → use-websocket-Dpl21cHG.js} +1 -1
  292. package/dist/web/assets/{vendor-ui-BHh12_0v.js → vendor-ui-P9Ym7hCv.js} +1 -1
  293. package/dist/web/assets/vendor-ui-P9Ym7hCv.js.br +0 -0
  294. package/dist/web/assets/vendor-ui-P9Ym7hCv.js.gz +0 -0
  295. package/dist/web/assets/video-preview-D6Jw-IZK.js +1 -0
  296. package/dist/web/assets/video-preview-D6Jw-IZK.js.br +0 -0
  297. package/dist/web/assets/video-preview-D6Jw-IZK.js.gz +0 -0
  298. package/dist/web/assets/voice-settings-section-CcHQDYzN.js +1 -0
  299. package/dist/web/assets/voice-settings-section-CcHQDYzN.js.br +0 -0
  300. package/dist/web/assets/voice-settings-section-CcHQDYzN.js.gz +0 -0
  301. package/dist/web/assets/ws-auth-CqI8-aoA.js +1 -0
  302. package/dist/web/index.html +20 -20
  303. package/dist/web/index.html.br +0 -0
  304. package/dist/web/index.html.gz +0 -0
  305. package/dist/web/sw.js +1 -1
  306. package/dist/web/sw.js.br +0 -0
  307. package/dist/web/sw.js.gz +0 -0
  308. package/docs/deployment-guide.md +23 -0
  309. package/package.json +1 -1
  310. package/release-from-worktree.sh +224 -0
  311. package/src/cli/commands/config-cmd.ts +12 -0
  312. package/src/providers/claude-agent-sdk.ts +65 -2
  313. package/src/providers/codex-app-server/codex-jsonrpc-client.ts +16 -1
  314. package/src/providers/codex-app-server/codex-provider.ts +39 -7
  315. package/src/providers/provider-probe.ts +63 -0
  316. package/src/providers/registry.ts +109 -29
  317. package/src/server/index.ts +9 -0
  318. package/src/server/middleware/auth.ts +5 -1
  319. package/src/server/routes/cross-origin-guard.ts +20 -0
  320. package/src/server/routes/git.ts +40 -4
  321. package/src/server/routes/lsp.ts +97 -4
  322. package/src/server/routes/resources.ts +72 -9
  323. package/src/server/routes/settings.ts +19 -1
  324. package/src/server/routes/speech.ts +66 -0
  325. package/src/server/routes/system-services.ts +76 -0
  326. package/src/server/routes/tunnel.ts +33 -1
  327. package/src/server/ws/chat.ts +74 -3
  328. package/src/services/codex-account-login.ts +26 -2
  329. package/src/services/codex-reset-credit.service.ts +16 -3
  330. package/src/services/compaction-savings.ts +127 -0
  331. package/src/services/db.service.ts +123 -8
  332. package/src/services/fs-ops/fs-ops-trash.service.ts +22 -9
  333. package/src/services/git-refs/for-each-ref.ts +118 -0
  334. package/src/services/git.service.ts +25 -2
  335. package/src/services/host-info/spawn-runner.ts +48 -4
  336. package/src/services/jsonl-transcript-parser.ts +7 -42
  337. package/src/services/lsp/lsp-install.ts +289 -0
  338. package/src/services/lsp/lsp-manager.ts +160 -29
  339. package/src/services/lsp/server-registry.ts +120 -4
  340. package/src/services/named-tunnel/named-tunnel-config.ts +8 -2
  341. package/src/services/named-tunnel/named-tunnel-setup.service.ts +4 -0
  342. package/src/services/read-lines.ts +54 -0
  343. package/src/services/speech-to-text/whisper-catalog.ts +142 -0
  344. package/src/services/speech-to-text/whisper-download.ts +113 -0
  345. package/src/services/speech-to-text/whisper-hallucinations.ts +51 -0
  346. package/src/services/speech-to-text/whisper-install.service.ts +247 -0
  347. package/src/services/speech-to-text/whisper-paths.ts +62 -0
  348. package/src/services/speech-to-text/whisper-transcribe.service.ts +117 -0
  349. package/src/services/supervisor.ts +94 -11
  350. package/src/services/system-metrics/cpu-details-linux.ts +252 -0
  351. package/src/services/system-metrics/cpu-memory-collector.ts +140 -18
  352. package/src/services/system-metrics/device-collector-linux.ts +31 -0
  353. package/src/services/system-metrics/device-collector-types.ts +40 -0
  354. package/src/services/system-metrics/disk-devices-linux.ts +156 -0
  355. package/src/services/system-metrics/disk-inventory-linux.ts +150 -0
  356. package/src/services/system-metrics/fans-linux.ts +51 -0
  357. package/src/services/system-metrics/gpu-api-versions.ts +120 -0
  358. package/src/services/system-metrics/gpu-devices-linux.ts +370 -0
  359. package/src/services/system-metrics/gpu-fdinfo-linux.ts +253 -0
  360. package/src/services/system-metrics/hardware-inventory.ts +50 -0
  361. package/src/services/system-metrics/linux-fs.ts +43 -0
  362. package/src/services/system-metrics/memory-darwin.ts +91 -0
  363. package/src/services/system-metrics/memory-linux.ts +156 -0
  364. package/src/services/system-metrics/net-devices-linux.ts +114 -0
  365. package/src/services/system-metrics/net-inventory-linux.ts +100 -0
  366. package/src/services/system-metrics/partitions-linux.ts +158 -0
  367. package/src/services/system-metrics/process-collector-linux.ts +58 -6
  368. package/src/services/system-metrics/process-collector-types.ts +8 -1
  369. package/src/services/system-metrics/process-details-darwin.ts +166 -0
  370. package/src/services/system-metrics/process-details-linux.ts +150 -0
  371. package/src/services/system-metrics/process-grouping.ts +2 -1
  372. package/src/services/system-metrics/process-rows-builder.ts +2 -0
  373. package/src/services/system-metrics/process-swap-linux.ts +37 -0
  374. package/src/services/system-metrics/signal-executor.ts +84 -0
  375. package/src/services/system-metrics/signal-request-handler.ts +92 -0
  376. package/src/services/system-metrics/system-metrics-platform.ts +30 -4
  377. package/src/services/system-metrics/system-metrics-tick.ts +73 -4
  378. package/src/services/system-metrics/system-metrics.service.ts +87 -2
  379. package/src/services/system-metrics/udev-db.ts +40 -0
  380. package/src/services/system-services/app-cgroup-linux.ts +115 -0
  381. package/src/services/system-services/app-icon-service.ts +31 -0
  382. package/src/services/system-services/app-icons-linux.ts +124 -0
  383. package/src/services/system-services/apps-linux.ts +174 -0
  384. package/src/services/system-services/desktop-entry.ts +103 -0
  385. package/src/services/system-services/service-guard.ts +124 -0
  386. package/src/services/system-services/systemd-collector.ts +203 -0
  387. package/src/services/system-services/systemd-parse.ts +162 -0
  388. package/src/shared/prompt-cache-idle.ts +197 -0
  389. package/src/shared/turn-usage.ts +85 -1
  390. package/src/types/chat.ts +27 -0
  391. package/src/types/config.ts +14 -0
  392. package/src/types/git.ts +41 -0
  393. package/src/types/system-hardware.ts +137 -0
  394. package/src/types/system-metrics.ts +231 -2
  395. package/src/types/system-services.ts +73 -0
  396. package/src/web/components/branch-review/branch-review-tab.tsx +20 -20
  397. package/src/web/components/branch-review/branch-review-tree-row.tsx +1 -13
  398. package/src/web/components/chat/chat-tab.tsx +3 -0
  399. package/src/web/components/chat/compaction-divider.tsx +48 -0
  400. package/src/web/components/chat/idle-cache-notice.tsx +67 -0
  401. package/src/web/components/chat/message-input.tsx +48 -13
  402. package/src/web/components/chat/message-list.tsx +15 -0
  403. package/src/web/components/chat/prompt-cache-chip.tsx +74 -0
  404. package/src/web/components/editor/code-editor.tsx +1 -0
  405. package/src/web/components/editor/editor-language-service.tsx +11 -3
  406. package/src/web/components/editor/lsp-status.tsx +115 -13
  407. package/src/web/components/git/branch-picker.tsx +419 -0
  408. package/src/web/components/git/branch-select.tsx +34 -0
  409. package/src/web/components/git/create-worktree-dialog.tsx +17 -16
  410. package/src/web/components/git/git-repo-picker.tsx +28 -40
  411. package/src/web/components/layout/status-bar.tsx +42 -19
  412. package/src/web/components/settings/accounts/account-card.tsx +5 -1
  413. package/src/web/components/settings/ai-settings-section.tsx +67 -3
  414. package/src/web/components/settings/appearance-settings-section.tsx +2 -28
  415. package/src/web/components/settings/language-servers-section.tsx +362 -0
  416. package/src/web/components/settings/settings-categories.ts +8 -3
  417. package/src/web/components/settings/settings-section-content.tsx +2 -0
  418. package/src/web/components/settings/voice-settings-section.tsx +283 -0
  419. package/src/web/components/system/apps/app-row.tsx +110 -0
  420. package/src/web/components/system/apps/app-rows.ts +140 -0
  421. package/src/web/components/system/apps/apps-panel.tsx +129 -0
  422. package/src/web/components/system/metric-chart-canvas.tsx +79 -3
  423. package/src/web/components/system/overview-cards/card-shell.tsx +54 -0
  424. package/src/web/components/system/overview-cards/cpu-card.tsx +11 -6
  425. package/src/web/components/system/overview-cards/disk-card.tsx +11 -6
  426. package/src/web/components/system/overview-cards/gpu-card.tsx +16 -6
  427. package/src/web/components/system/overview-cards/mem-card.tsx +11 -6
  428. package/src/web/components/system/overview-cards/net-card.tsx +11 -6
  429. package/src/web/components/system/overview-device-target.ts +69 -0
  430. package/src/web/components/system/overview-panel.tsx +15 -1
  431. package/src/web/components/system/performance/cpu-core-grid.tsx +74 -0
  432. package/src/web/components/system/performance/cpu-detail.tsx +220 -0
  433. package/src/web/components/system/performance/detail-parts.tsx +149 -0
  434. package/src/web/components/system/performance/device-list.ts +139 -0
  435. package/src/web/components/system/performance/disk-detail.tsx +64 -0
  436. package/src/web/components/system/performance/fans-detail.tsx +35 -0
  437. package/src/web/components/system/performance/gpu-detail.tsx +116 -0
  438. package/src/web/components/system/performance/memory-detail.tsx +109 -0
  439. package/src/web/components/system/performance/nic-detail.tsx +55 -0
  440. package/src/web/components/system/performance/partition-list.tsx +79 -0
  441. package/src/web/components/system/performance/performance-panel.tsx +206 -0
  442. package/src/web/components/system/performance/use-hardware-inventory.ts +48 -0
  443. package/src/web/components/system/process-columns-grid.ts +5 -3
  444. package/src/web/components/system/process-details-dialog.tsx +116 -0
  445. package/src/web/components/system/process-group-row.tsx +6 -1
  446. package/src/web/components/system/process-row-format.ts +6 -0
  447. package/src/web/components/system/process-row-menu.tsx +72 -0
  448. package/src/web/components/system/process-row.tsx +7 -1
  449. package/src/web/components/system/process-table-model.ts +1 -1
  450. package/src/web/components/system/process-table-sort.ts +8 -4
  451. package/src/web/components/system/process-table-toolbar.tsx +10 -2
  452. package/src/web/components/system/process-table-totals.ts +3 -0
  453. package/src/web/components/system/process-table.tsx +25 -2
  454. package/src/web/components/system/services/service-action-confirm.tsx +100 -0
  455. package/src/web/components/system/services/service-columns.ts +103 -0
  456. package/src/web/components/system/services/service-details-sheet.tsx +97 -0
  457. package/src/web/components/system/services/service-resources.ts +105 -0
  458. package/src/web/components/system/services/service-row.tsx +146 -0
  459. package/src/web/components/system/services/service-rows.ts +92 -0
  460. package/src/web/components/system/services/service-sort.ts +90 -0
  461. package/src/web/components/system/services/services-header.tsx +51 -0
  462. package/src/web/components/system/services/services-panel.tsx +207 -0
  463. package/src/web/components/system/services/use-services.ts +92 -0
  464. package/src/web/components/system/sortable-header.tsx +13 -8
  465. package/src/web/components/system/sysmon-preferences.tsx +85 -0
  466. package/src/web/components/system/system-monitor-body.tsx +101 -43
  467. package/src/web/components/system/system-monitor-window-content.tsx +2 -3
  468. package/src/web/components/system/use-process-signal.ts +87 -0
  469. package/src/web/components/tunnels/public-tunnel-switch.tsx +82 -0
  470. package/src/web/components/tunnels/tunnel-manager-tab.tsx +4 -0
  471. package/src/web/components/ui/dialog.tsx +14 -1
  472. package/src/web/components/ui/search-select.tsx +279 -0
  473. package/src/web/components/ui/start-ellipsis.tsx +18 -0
  474. package/src/web/hooks/use-chat.ts +28 -0
  475. package/src/web/hooks/use-global-keybindings.ts +7 -5
  476. package/src/web/hooks/use-lsp.ts +23 -2
  477. package/src/web/hooks/use-minute-clock.ts +20 -0
  478. package/src/web/hooks/use-whisper-voice-input.ts +113 -0
  479. package/src/web/lib/api-tunnels.ts +19 -0
  480. package/src/web/lib/branch-select-items.ts +29 -0
  481. package/src/web/lib/cpu-graph-mode.ts +90 -0
  482. package/src/web/lib/format-bytes.ts +14 -0
  483. package/src/web/lib/git-ref-picker.ts +120 -0
  484. package/src/web/lib/lsp/lsp-client.ts +32 -0
  485. package/src/web/lib/search-select-rows.ts +55 -0
  486. package/src/web/lib/temperature.ts +27 -0
  487. package/src/web/lib/voice-wav.ts +69 -0
  488. package/src/web/stores/git-status-store.ts +31 -17
  489. package/src/web/stores/keybindings-store.ts +14 -1
  490. package/src/web/stores/settings-store.ts +65 -0
  491. package/src/web/styles/globals.css +11 -0
  492. package/dist/web/assets/accounts-settings-section-DUVO32SQ.js +0 -1
  493. package/dist/web/assets/accounts-settings-section-DUVO32SQ.js.br +0 -0
  494. package/dist/web/assets/accounts-settings-section-DUVO32SQ.js.gz +0 -0
  495. package/dist/web/assets/ai-resource-editor-Dn_wB-o5.js.br +0 -0
  496. package/dist/web/assets/ai-resource-editor-Dn_wB-o5.js.gz +0 -0
  497. package/dist/web/assets/ai-settings-section-CEbW8jRy.js +0 -1
  498. package/dist/web/assets/ai-settings-section-CEbW8jRy.js.br +0 -0
  499. package/dist/web/assets/ai-settings-section-CEbW8jRy.js.gz +0 -0
  500. package/dist/web/assets/ai-settings-section-D-mwEYFZ.js +0 -1
  501. package/dist/web/assets/api-client-BgedDTvo.js +0 -1
  502. package/dist/web/assets/api-client-c8YOpTlT.js.br +0 -0
  503. package/dist/web/assets/api-client-c8YOpTlT.js.gz +0 -0
  504. package/dist/web/assets/api-settings-BmvNoCbn.js.br +0 -0
  505. package/dist/web/assets/api-settings-BmvNoCbn.js.gz +0 -0
  506. package/dist/web/assets/api-settings-Bti1-Zn3.js +0 -1
  507. package/dist/web/assets/appearance-settings-section-tw6TpFb6.js +0 -1
  508. package/dist/web/assets/appearance-settings-section-tw6TpFb6.js.br +0 -0
  509. package/dist/web/assets/appearance-settings-section-tw6TpFb6.js.gz +0 -0
  510. package/dist/web/assets/audio-preview-vaG2cB-k.js.br +0 -0
  511. package/dist/web/assets/audio-preview-vaG2cB-k.js.gz +0 -0
  512. package/dist/web/assets/branch-review-tab-MBuMWp_i.js +0 -1
  513. package/dist/web/assets/branch-review-tab-MBuMWp_i.js.br +0 -0
  514. package/dist/web/assets/branch-review-tab-MBuMWp_i.js.gz +0 -0
  515. package/dist/web/assets/button-Ci9EUgqd.js.br +0 -0
  516. package/dist/web/assets/button-Ci9EUgqd.js.gz +0 -0
  517. package/dist/web/assets/chat-preparation-cache-BSIQGiWx.js.br +0 -0
  518. package/dist/web/assets/chat-preparation-cache-BSIQGiWx.js.gz +0 -0
  519. package/dist/web/assets/chat-tab-DVvnqhw6.js +0 -1
  520. package/dist/web/assets/chat-tab-DVvnqhw6.js.br +0 -0
  521. package/dist/web/assets/chat-tab-DVvnqhw6.js.gz +0 -0
  522. package/dist/web/assets/chat-tab-DaEHva22.js +0 -15
  523. package/dist/web/assets/chat-tab-DaEHva22.js.br +0 -0
  524. package/dist/web/assets/chat-tab-DaEHva22.js.gz +0 -0
  525. package/dist/web/assets/code-editor-Bu9lhTVp.js +0 -7
  526. package/dist/web/assets/code-editor-Bu9lhTVp.js.br +0 -0
  527. package/dist/web/assets/code-editor-Bu9lhTVp.js.gz +0 -0
  528. package/dist/web/assets/codex-reset-credit-button-CbmE5mRX.js +0 -1
  529. package/dist/web/assets/codex-reset-credit-button-CbmE5mRX.js.br +0 -0
  530. package/dist/web/assets/codex-reset-credit-button-CbmE5mRX.js.gz +0 -0
  531. package/dist/web/assets/conflict-editor-BA3y-qSH.js.br +0 -0
  532. package/dist/web/assets/conflict-editor-BA3y-qSH.js.gz +0 -0
  533. package/dist/web/assets/context-menu-CjYTq8DI.js +0 -1
  534. package/dist/web/assets/context-menu-CjYTq8DI.js.br +0 -0
  535. package/dist/web/assets/context-menu-CjYTq8DI.js.gz +0 -0
  536. package/dist/web/assets/csv-preview-BioxkrQF.js.br +0 -0
  537. package/dist/web/assets/csv-preview-BioxkrQF.js.gz +0 -0
  538. package/dist/web/assets/data-grid-overlay-editor-CQRVKr7y.js.br +0 -0
  539. package/dist/web/assets/data-grid-overlay-editor-CQRVKr7y.js.gz +0 -0
  540. package/dist/web/assets/database-viewer-BSoJ0E6p.js.br +0 -0
  541. package/dist/web/assets/database-viewer-BSoJ0E6p.js.gz +0 -0
  542. package/dist/web/assets/design-export-client-ChMcMoqW.js.br +0 -0
  543. package/dist/web/assets/design-export-client-ChMcMoqW.js.gz +0 -0
  544. package/dist/web/assets/design-responsive-dialog-DNBhJJLf.js.br +0 -0
  545. package/dist/web/assets/design-responsive-dialog-DNBhJJLf.js.gz +0 -0
  546. package/dist/web/assets/design-tab-iU2CviE3.js +0 -33
  547. package/dist/web/assets/design-tab-iU2CviE3.js.br +0 -0
  548. package/dist/web/assets/design-tab-iU2CviE3.js.gz +0 -0
  549. package/dist/web/assets/designs-sidebar-panel-DHh5QsH9.js.br +0 -0
  550. package/dist/web/assets/designs-sidebar-panel-DHh5QsH9.js.gz +0 -0
  551. package/dist/web/assets/dialog-B8clVkyi.js +0 -1
  552. package/dist/web/assets/dialog-B8clVkyi.js.br +0 -0
  553. package/dist/web/assets/dialog-B8clVkyi.js.gz +0 -0
  554. package/dist/web/assets/diff-viewer-lctWoSmG.js.br +0 -0
  555. package/dist/web/assets/diff-viewer-lctWoSmG.js.gz +0 -0
  556. package/dist/web/assets/docx-preview-C9XdKEj1.js.br +0 -0
  557. package/dist/web/assets/docx-preview-C9XdKEj1.js.gz +0 -0
  558. package/dist/web/assets/editor-language-service-fPG56hQO.js +0 -1
  559. package/dist/web/assets/editor-language-service-fPG56hQO.js.br +0 -0
  560. package/dist/web/assets/editor-language-service-fPG56hQO.js.gz +0 -0
  561. package/dist/web/assets/explorer-body-BgF7FsxI.js +0 -2
  562. package/dist/web/assets/explorer-body-BgF7FsxI.js.br +0 -0
  563. package/dist/web/assets/explorer-body-BgF7FsxI.js.gz +0 -0
  564. package/dist/web/assets/explorer-window-content-C21w_7lf.js +0 -1
  565. package/dist/web/assets/explorer-window-content-C21w_7lf.js.br +0 -0
  566. package/dist/web/assets/explorer-window-content-C21w_7lf.js.gz +0 -0
  567. package/dist/web/assets/extension-manager-section-rbkXsZVR.js.br +0 -0
  568. package/dist/web/assets/extension-manager-section-rbkXsZVR.js.gz +0 -0
  569. package/dist/web/assets/extension-webview-uPO0WgCq.js.br +0 -0
  570. package/dist/web/assets/extension-webview-uPO0WgCq.js.gz +0 -0
  571. package/dist/web/assets/file-icons-gx_FBOXY.js.br +0 -0
  572. package/dist/web/assets/file-icons-gx_FBOXY.js.gz +0 -0
  573. package/dist/web/assets/file-store-D9KE0kQC.js.br +0 -0
  574. package/dist/web/assets/file-store-D9KE0kQC.js.gz +0 -0
  575. package/dist/web/assets/files-settings-section-CT7mDb0L.js.br +0 -0
  576. package/dist/web/assets/files-settings-section-CT7mDb0L.js.gz +0 -0
  577. package/dist/web/assets/format-bytes-DmSHAKFe.js +0 -1
  578. package/dist/web/assets/format-file-meta-mQwA6SuY.js.br +0 -0
  579. package/dist/web/assets/format-file-meta-mQwA6SuY.js.gz +0 -0
  580. package/dist/web/assets/fs-api-DL36slbd.js.br +0 -0
  581. package/dist/web/assets/fs-api-DL36slbd.js.gz +0 -0
  582. package/dist/web/assets/general-settings-section-DANPfMRN.js.br +0 -0
  583. package/dist/web/assets/general-settings-section-DANPfMRN.js.gz +0 -0
  584. package/dist/web/assets/git-log-panel-BJPkLiEw.js.br +0 -0
  585. package/dist/web/assets/git-log-panel-BJPkLiEw.js.gz +0 -0
  586. package/dist/web/assets/git-repo-store-DSypNj19.js.br +0 -0
  587. package/dist/web/assets/git-repo-store-DSypNj19.js.gz +0 -0
  588. package/dist/web/assets/glide-data-grid-R4PcAntf.js.br +0 -0
  589. package/dist/web/assets/glide-data-grid-R4PcAntf.js.gz +0 -0
  590. package/dist/web/assets/group-chat-tab-BXMdo8w9.js +0 -1
  591. package/dist/web/assets/group-chat-tab-BXMdo8w9.js.br +0 -0
  592. package/dist/web/assets/group-chat-tab-BXMdo8w9.js.gz +0 -0
  593. package/dist/web/assets/index-C8nGqkKN.js +0 -52
  594. package/dist/web/assets/index-C8nGqkKN.js.br +0 -0
  595. package/dist/web/assets/index-C8nGqkKN.js.gz +0 -0
  596. package/dist/web/assets/index-DvClbTQx.css +0 -1
  597. package/dist/web/assets/index-DvClbTQx.css.br +0 -0
  598. package/dist/web/assets/index-DvClbTQx.css.gz +0 -0
  599. package/dist/web/assets/keybindings-store-BGKlQQAv.js +0 -1
  600. package/dist/web/assets/keyboard-shortcuts-section-BKej4mUf.js.br +0 -0
  601. package/dist/web/assets/keyboard-shortcuts-section-BKej4mUf.js.gz +0 -0
  602. package/dist/web/assets/markdown-renderer-BHtYqkYZ.js.br +0 -0
  603. package/dist/web/assets/markdown-renderer-BHtYqkYZ.js.gz +0 -0
  604. package/dist/web/assets/markdown-renderer-vDNuKjel.js +0 -1
  605. package/dist/web/assets/mobile-bottom-sheet-CuNi2FRv.js.br +0 -0
  606. package/dist/web/assets/mobile-bottom-sheet-CuNi2FRv.js.gz +0 -0
  607. package/dist/web/assets/mobile-explorer-sheet-DtvxS7-r.js +0 -1
  608. package/dist/web/assets/mobile-explorer-sheet-DtvxS7-r.js.br +0 -0
  609. package/dist/web/assets/mobile-explorer-sheet-DtvxS7-r.js.gz +0 -0
  610. package/dist/web/assets/new-design-dialog-DS_XrpTG.js.br +0 -0
  611. package/dist/web/assets/new-design-dialog-DS_XrpTG.js.gz +0 -0
  612. package/dist/web/assets/notification-store-Dnnj20Ky.js +0 -1
  613. package/dist/web/assets/notifications-settings-section-C05WYqcE.js.br +0 -0
  614. package/dist/web/assets/notifications-settings-section-C05WYqcE.js.gz +0 -0
  615. package/dist/web/assets/onboarding-entry-CmUqv1kl.js.br +0 -0
  616. package/dist/web/assets/onboarding-entry-CmUqv1kl.js.gz +0 -0
  617. package/dist/web/assets/onboarding-quick-orientation-DIhWv1CN.js.br +0 -0
  618. package/dist/web/assets/onboarding-quick-orientation-DIhWv1CN.js.gz +0 -0
  619. package/dist/web/assets/overview-panel-BZAW_nag.js +0 -1
  620. package/dist/web/assets/overview-panel-BZAW_nag.js.br +0 -0
  621. package/dist/web/assets/overview-panel-BZAW_nag.js.gz +0 -0
  622. package/dist/web/assets/pdf-preview-qdXmMkOR.js.br +0 -0
  623. package/dist/web/assets/pdf-preview-qdXmMkOR.js.gz +0 -0
  624. package/dist/web/assets/postgres-viewer-Xvlun_n5.js.br +0 -0
  625. package/dist/web/assets/postgres-viewer-Xvlun_n5.js.gz +0 -0
  626. package/dist/web/assets/ppmbot-settings-section-D8AES1BA.js.br +0 -0
  627. package/dist/web/assets/ppmbot-settings-section-D8AES1BA.js.gz +0 -0
  628. package/dist/web/assets/pptx-export-CG6MRNok.js.br +0 -0
  629. package/dist/web/assets/pptx-export-CG6MRNok.js.gz +0 -0
  630. package/dist/web/assets/problems-panel-DTBG4beI.js.br +0 -0
  631. package/dist/web/assets/problems-panel-DTBG4beI.js.gz +0 -0
  632. package/dist/web/assets/process-table-D_gR2XA6.js +0 -1
  633. package/dist/web/assets/process-table-D_gR2XA6.js.br +0 -0
  634. package/dist/web/assets/process-table-D_gR2XA6.js.gz +0 -0
  635. package/dist/web/assets/project-store-C5gJXkvb.js.br +0 -0
  636. package/dist/web/assets/project-store-C5gJXkvb.js.gz +0 -0
  637. package/dist/web/assets/proxy-settings-section-CqtmbeRj.js.br +0 -0
  638. package/dist/web/assets/proxy-settings-section-CqtmbeRj.js.gz +0 -0
  639. package/dist/web/assets/query-audit-section-UKG_smyt.js.br +0 -0
  640. package/dist/web/assets/query-audit-section-UKG_smyt.js.gz +0 -0
  641. package/dist/web/assets/remote-desktop-clipboard-notice-Djt6cpNL.js.br +0 -0
  642. package/dist/web/assets/remote-desktop-clipboard-notice-Djt6cpNL.js.gz +0 -0
  643. package/dist/web/assets/remote-desktop-mobile-sheet-BQxhJUkm.js +0 -2
  644. package/dist/web/assets/remote-desktop-mobile-sheet-BQxhJUkm.js.br +0 -0
  645. package/dist/web/assets/remote-desktop-mobile-sheet-BQxhJUkm.js.gz +0 -0
  646. package/dist/web/assets/remote-desktop-mobile-view-DGnT8RY7.js +0 -1
  647. package/dist/web/assets/remote-desktop-mobile-view-DGnT8RY7.js.br +0 -0
  648. package/dist/web/assets/remote-desktop-mobile-view-DGnT8RY7.js.gz +0 -0
  649. package/dist/web/assets/remote-desktop-readiness-gate-DNJF8TcF.js.br +0 -0
  650. package/dist/web/assets/remote-desktop-readiness-gate-DNJF8TcF.js.gz +0 -0
  651. package/dist/web/assets/remote-desktop-window-content-Ds0DKmM0.js +0 -1
  652. package/dist/web/assets/remote-desktop-window-content-Ds0DKmM0.js.br +0 -0
  653. package/dist/web/assets/remote-desktop-window-content-Ds0DKmM0.js.gz +0 -0
  654. package/dist/web/assets/schedules-settings-section-BKoPovy2.js.br +0 -0
  655. package/dist/web/assets/schedules-settings-section-BKoPovy2.js.gz +0 -0
  656. package/dist/web/assets/scroll-area-XonvWYn_.js.br +0 -0
  657. package/dist/web/assets/scroll-area-XonvWYn_.js.gz +0 -0
  658. package/dist/web/assets/settings-body-k2qPgRFe.js +0 -2
  659. package/dist/web/assets/settings-body-k2qPgRFe.js.br +0 -0
  660. package/dist/web/assets/settings-body-k2qPgRFe.js.gz +0 -0
  661. package/dist/web/assets/settings-store-Br5c1efC.js +0 -2
  662. package/dist/web/assets/settings-store-Br5c1efC.js.br +0 -0
  663. package/dist/web/assets/settings-store-Br5c1efC.js.gz +0 -0
  664. package/dist/web/assets/settings-tab-Bb23VdBu.js +0 -1
  665. package/dist/web/assets/settings-window-content-ClarWusz.js +0 -1
  666. package/dist/web/assets/sql-query-editor-BjIMi9Wk.js.br +0 -0
  667. package/dist/web/assets/sql-query-editor-BjIMi9Wk.js.gz +0 -0
  668. package/dist/web/assets/sqlite-viewer-BNcA6Irq.js.br +0 -0
  669. package/dist/web/assets/sqlite-viewer-BNcA6Irq.js.gz +0 -0
  670. package/dist/web/assets/system-monitor-body-D0T3ggt2.js +0 -2
  671. package/dist/web/assets/system-monitor-body-D0T3ggt2.js.br +0 -0
  672. package/dist/web/assets/system-monitor-body-D0T3ggt2.js.gz +0 -0
  673. package/dist/web/assets/system-monitor-tab-I1Igmoey.js +0 -1
  674. package/dist/web/assets/system-monitor-window-content-C5SeUv1H.js +0 -1
  675. package/dist/web/assets/tab-host-window-content-CIRGNCvi.js.br +0 -0
  676. package/dist/web/assets/tab-host-window-content-CIRGNCvi.js.gz +0 -0
  677. package/dist/web/assets/team-member-sheet-CwXpLi3X.js.br +0 -0
  678. package/dist/web/assets/team-member-sheet-CwXpLi3X.js.gz +0 -0
  679. package/dist/web/assets/team-member-window-content-4swiiDz7.js.br +0 -0
  680. package/dist/web/assets/team-member-window-content-4swiiDz7.js.gz +0 -0
  681. package/dist/web/assets/terminal-tab-BGcdtMMT.js.br +0 -0
  682. package/dist/web/assets/terminal-tab-BGcdtMMT.js.gz +0 -0
  683. package/dist/web/assets/tool-cards-D0-krtA2.js.br +0 -0
  684. package/dist/web/assets/tool-cards-D0-krtA2.js.gz +0 -0
  685. package/dist/web/assets/use-inline-blame-pKaXyGU-.js +0 -16
  686. package/dist/web/assets/use-inline-blame-pKaXyGU-.js.br +0 -0
  687. package/dist/web/assets/use-inline-blame-pKaXyGU-.js.gz +0 -0
  688. package/dist/web/assets/use-lsp-71azTXQz.js +0 -6
  689. package/dist/web/assets/use-lsp-71azTXQz.js.br +0 -0
  690. package/dist/web/assets/use-lsp-71azTXQz.js.gz +0 -0
  691. package/dist/web/assets/vendor-ui-BHh12_0v.js.br +0 -0
  692. package/dist/web/assets/vendor-ui-BHh12_0v.js.gz +0 -0
  693. package/dist/web/assets/video-preview-DxEX-uGo.js +0 -1
  694. package/dist/web/assets/video-preview-DxEX-uGo.js.br +0 -0
  695. package/dist/web/assets/video-preview-DxEX-uGo.js.gz +0 -0
  696. package/dist/web/assets/ws-auth-kXgsX2Js.js +0 -1
  697. /package/dist/web/assets/{__vite-browser-external-BpSQWxcq.js → __vite-browser-external-BILfYaAs.js} +0 -0
  698. /package/dist/web/assets/{run-in-terminal-HIdNgYim.js → run-in-terminal-2pSiup8e.js} +0 -0
  699. /package/dist/web/assets/{vendor-xterm-DLDtzLib.js → vendor-xterm-DF1JTfsy.js} +0 -0
  700. /package/dist/web/assets/{vendor-xterm-DLDtzLib.js.br → vendor-xterm-DF1JTfsy.js.br} +0 -0
  701. /package/dist/web/assets/{vendor-xterm-DLDtzLib.js.gz → vendor-xterm-DF1JTfsy.js.gz} +0 -0
@@ -0,0 +1,203 @@
1
+ /**
2
+ * The Services page's backend: TWO `systemctl` spawns for a whole scope's list,
3
+ * one more for a unit's details, one `journalctl` for its log.
4
+ *
5
+ * Two traps, both found against a real host and both load-bearing:
6
+ *
7
+ * - A unit name may begin with a dash. The root mount is literally `-.mount`,
8
+ * so every call that takes unit names passes them after `--`, or systemctl
9
+ * reads the name as an option bundle and the whole call fails.
10
+ * - An action without `--no-ask-password` can BLOCK on a polkit prompt nobody
11
+ * can answer, turning "stop this unit" into a wedged request until the
12
+ * timeout. PPM is not root, so a system-scope action is expected to be
13
+ * refused by systemd; it must be refused quickly and reported plainly.
14
+ */
15
+ import type {
16
+ ServiceAction, ServiceActionResult, ServiceDetails, ServiceInfo, ServiceLogLine,
17
+ ServiceScope, ServicesSnapshot,
18
+ } from "../../types/system-services.ts";
19
+ import type { RunResult, Runner } from "../host-info/spawn-runner.ts";
20
+ import { defaultRunner } from "../host-info/spawn-runner.ts";
21
+ import { realLinuxFs } from "../system-metrics/linux-fs.ts";
22
+ import {
23
+ keepUnit, parseJournalJson, parseListUnits, parseShowBlock, parseShowRecords,
24
+ SHOW_DETAIL_PROPERTIES, SHOW_PROPERTIES, toServiceInfo, type ListUnitsRow,
25
+ } from "./systemd-parse.ts";
26
+ import { checkServiceActionAllowed, serviceRefusals, type ServiceGuardContext } from "./service-guard.ts";
27
+
28
+ export const SYSTEMCTL_TIMEOUT_MS = 10_000;
29
+ /** An action may legitimately take a while; a stop waits for the unit to settle. */
30
+ export const ACTION_TIMEOUT_MS = 20_000;
31
+ export const MAX_LOG_LINES = 200;
32
+
33
+ export const SCOPES: readonly ServiceScope[] = ["system", "user"];
34
+
35
+ export function systemctlArgv(scope: ServiceScope, args: readonly string[]): string[] {
36
+ return scope === "user" ? ["systemctl", "--user", ...args] : ["systemctl", ...args];
37
+ }
38
+
39
+ /** `--unit=<name>` rather than `--unit <name>`: see the dash trap above. */
40
+ export function journalctlArgv(scope: ServiceScope, unit: string, lines: number): string[] {
41
+ const argv = ["journalctl"];
42
+ if (scope === "user") argv.push("--user");
43
+ argv.push(
44
+ `--unit=${unit}`, "--boot", `--lines=${lines}`,
45
+ "--no-pager", "--output=json", "--output-fields=MESSAGE",
46
+ );
47
+ return argv;
48
+ }
49
+
50
+ export interface SystemdDeps {
51
+ run: Runner;
52
+ guard: ServiceGuardContext;
53
+ }
54
+
55
+ /** Attaches the refusals this unit carries, from the same function the action
56
+ * route enforces — so a greyed-out button and a 403 cannot disagree. */
57
+ export function withRefusals(info: ServiceInfo, guard: ServiceGuardContext): ServiceInfo {
58
+ const refused = serviceRefusals(info.unit, info.scope, guard);
59
+ return Object.keys(refused).length > 0 ? { ...info, refused } : info;
60
+ }
61
+
62
+ export function failureText(result: RunResult): string {
63
+ if (result.timedOut) return "timed out";
64
+ return result.stderr.trim() || result.stdout.trim() || `exited ${result.code}`;
65
+ }
66
+
67
+ export interface ScopeListing {
68
+ services: ServiceInfo[];
69
+ warnings: string[];
70
+ /** False when this manager did not answer at all (no systemd, no user bus). */
71
+ ok: boolean;
72
+ }
73
+
74
+ export async function listScope(scope: ServiceScope, deps: SystemdDeps): Promise<ScopeListing> {
75
+ const list = await deps.run(systemctlArgv(scope, [
76
+ "list-units", "--type=service,socket,mount", "--all", "--no-legend", "--plain", "--no-pager",
77
+ ]), SYSTEMCTL_TIMEOUT_MS);
78
+ if (list.timedOut || list.code !== 0) {
79
+ return { services: [], warnings: [`${scope} services unavailable: ${failureText(list)}`], ok: false };
80
+ }
81
+
82
+ const rows = parseListUnits(list.stdout).filter(keepUnit);
83
+ if (rows.length === 0) return { services: [], warnings: [], ok: true };
84
+
85
+ const show = await deps.run(systemctlArgv(scope, [
86
+ "show", `--property=${SHOW_PROPERTIES.join(",")}`, "--no-pager", "--", ...rows.map((r) => r.unit),
87
+ ]), SYSTEMCTL_TIMEOUT_MS);
88
+
89
+ const warnings: string[] = [];
90
+ let byId = new Map<string, Record<string, string>>();
91
+ if (show.timedOut || show.code !== 0) {
92
+ // The list still stands: LOAD/ACTIVE/SUB came from `list-units`. Only the
93
+ // unit-file state and the main pid are lost, so the page degrades rather
94
+ // than emptying.
95
+ warnings.push(`${scope} service details unavailable: ${failureText(show)}`);
96
+ } else {
97
+ byId = parseShowRecords(show.stdout);
98
+ }
99
+
100
+ const services = rows.map((row) => withRefusals(toServiceInfo(row, scope, byId.get(row.unit)), deps.guard));
101
+ return { services, warnings, ok: true };
102
+ }
103
+
104
+ export async function collectServices(deps: SystemdDeps): Promise<ServicesSnapshot> {
105
+ const listings = await Promise.all(SCOPES.map((scope) => listScope(scope, deps)));
106
+ return {
107
+ // Either manager answering is enough: a container commonly has the system
108
+ // one and no user session, and a headless server the other way round.
109
+ supported: listings.some((l) => l.ok),
110
+ services: listings.flatMap((l) => l.services),
111
+ warnings: listings.flatMap((l) => l.warnings),
112
+ };
113
+ }
114
+
115
+ export async function serviceLogs(
116
+ unit: string,
117
+ scope: ServiceScope,
118
+ deps: SystemdDeps,
119
+ lines: number = MAX_LOG_LINES,
120
+ ): Promise<ServiceLogLine[]> {
121
+ const result = await deps.run(journalctlArgv(scope, unit, lines), SYSTEMCTL_TIMEOUT_MS);
122
+ // Not gated on the exit code: journalctl exits non-zero for a unit with no
123
+ // entries this boot while still being perfectly healthy.
124
+ if (result.timedOut || !result.stdout.trim()) return [];
125
+ return parseJournalJson(result.stdout);
126
+ }
127
+
128
+ /** Null when the unit does not exist — the route turns that into a 404. */
129
+ export async function serviceDetails(
130
+ unit: string,
131
+ scope: ServiceScope,
132
+ deps: SystemdDeps,
133
+ ): Promise<ServiceDetails | null> {
134
+ const show = await deps.run(systemctlArgv(scope, [
135
+ "show", `--property=${SHOW_DETAIL_PROPERTIES.join(",")}`, "--no-pager", "--", unit,
136
+ ]), SYSTEMCTL_TIMEOUT_MS);
137
+ if (show.timedOut || show.code !== 0) return null;
138
+
139
+ const record = parseShowBlock(show.stdout);
140
+ // `show` answers for a unit it has never heard of too, with LoadState=not-found
141
+ // and every other field blank. That is a 404, not a unit with no description.
142
+ if (!record.Id || record.LoadState === "not-found") return null;
143
+
144
+ const row: ListUnitsRow = {
145
+ unit: record.Id,
146
+ loadState: record.LoadState ?? "",
147
+ activeState: record.ActiveState ?? "",
148
+ subState: record.SubState ?? "",
149
+ description: record.Description ?? "",
150
+ };
151
+ const info = withRefusals(toServiceInfo(row, scope, record), deps.guard);
152
+ return {
153
+ ...info,
154
+ user: record.User || null,
155
+ group: record.Group || null,
156
+ fragmentPath: record.FragmentPath || null,
157
+ logs: await serviceLogs(record.Id, scope, deps),
158
+ };
159
+ }
160
+
161
+ /** Thrown when PPM's own guard refuses; the route maps it to 403, not 500. */
162
+ export class ServiceActionRefused extends Error {
163
+ constructor(message: string) {
164
+ super(message);
165
+ this.name = "ServiceActionRefused";
166
+ }
167
+ }
168
+
169
+ export function actionFailureText(result: RunResult, scope: ServiceScope): string {
170
+ const text = failureText(result);
171
+ if (/interactive authentication required/i.test(text)) {
172
+ return scope === "system"
173
+ ? "systemd refused: PPM does not run as root, so system units cannot be changed from here"
174
+ : "systemd refused: this action needs an authentication PPM cannot provide";
175
+ }
176
+ return text;
177
+ }
178
+
179
+ export async function runServiceAction(
180
+ unit: string,
181
+ scope: ServiceScope,
182
+ action: ServiceAction,
183
+ deps: SystemdDeps,
184
+ ): Promise<ServiceActionResult> {
185
+ const verdict = checkServiceActionAllowed(unit, scope, action, deps.guard);
186
+ if (!verdict.allowed) throw new ServiceActionRefused(verdict.reason ?? "Refused");
187
+
188
+ const result = await deps.run(
189
+ systemctlArgv(scope, ["--no-ask-password", action, "--no-pager", "--", unit]),
190
+ ACTION_TIMEOUT_MS,
191
+ );
192
+ if (result.timedOut || result.code !== 0) throw new Error(actionFailureText(result, scope));
193
+ return { unit, scope, action };
194
+ }
195
+
196
+ /** PPM's own cgroup, which is what the guard derives its refusals from. */
197
+ export function readSelfCgroup(): string | null {
198
+ return realLinuxFs.read("/proc/self/cgroup");
199
+ }
200
+
201
+ export function createSystemdServices(run: Runner = defaultRunner): SystemdDeps {
202
+ return { run, guard: { selfCgroup: readSelfCgroup() } };
203
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Pure parsers for systemd's text output - Mission Center's Services page, read
3
+ * through `systemctl` instead of D-Bus.
4
+ *
5
+ * MC talks to org.freedesktop.systemd1 over zbus. PPM has no D-Bus client in Bun,
6
+ * and shelling out is not the compromise it looks like: `list-units` plus ONE
7
+ * multi-unit `show` covers all 239 units on this host in 6 ms total, against MC's
8
+ * property read per unit. What is lost is change signalling, so the page polls
9
+ * (SERVICES_POLL_INTERVAL_MS) rather than subscribing.
10
+ *
11
+ * Mission Center also reads `list-unit-files` for the enabled flag. That call
12
+ * alone measured 322 ms here and `show` already answers UnitFileState for every
13
+ * unit that has one - the 26 units it leaves empty have no unit file at all, so
14
+ * the file list would not know them either. It is not made.
15
+ *
16
+ * Nothing here spawns anything; every function takes the captured stdout.
17
+ */
18
+ import type { ServiceInfo, ServiceLogLine, ServiceScope } from "../../types/system-services.ts";
19
+
20
+ /** Mission Center lists exactly these three unit types and ignores the rest. */
21
+ export const LISTED_SUFFIXES: readonly string[] = [".service", ".socket", ".mount"];
22
+
23
+ export function isListedUnit(name: string): boolean {
24
+ return LISTED_SUFFIXES.some((s) => name.endsWith(s));
25
+ }
26
+
27
+ export interface ListUnitsRow {
28
+ unit: string;
29
+ loadState: string;
30
+ activeState: string;
31
+ subState: string;
32
+ description: string;
33
+ }
34
+
35
+ /**
36
+ * `systemctl list-units --plain --no-legend` gives UNIT LOAD ACTIVE SUB DESCRIPTION.
37
+ *
38
+ * The `--plain` flag is supposed to drop the leading status bullet, but a failed
39
+ * unit is still marked in some versions, so it is stripped defensively - an
40
+ * unstripped bullet would shift every column by one and rename every unit.
41
+ */
42
+ export function parseListUnits(stdout: string): ListUnitsRow[] {
43
+ const rows: ListUnitsRow[] = [];
44
+ for (const line of stdout.split("\n")) {
45
+ const cleaned = line.trim().replace(/^[●○×→*]\s+/, "");
46
+ if (!cleaned) continue;
47
+ const parts = cleaned.split(/\s+/);
48
+ if (parts.length < 4) continue;
49
+ const [unit, loadState, activeState, subState] = parts as [string, string, string, string];
50
+ rows.push({ unit, loadState, activeState, subState, description: parts.slice(4).join(" ") });
51
+ }
52
+ return rows;
53
+ }
54
+
55
+ /** One Key=Value block from `systemctl show`. A value may itself contain "=". */
56
+ export function parseShowBlock(block: string): Record<string, string> {
57
+ const record: Record<string, string> = {};
58
+ for (const line of block.split("\n")) {
59
+ const eq = line.indexOf("=");
60
+ if (eq <= 0) continue;
61
+ record[line.slice(0, eq)] = line.slice(eq + 1);
62
+ }
63
+ return record;
64
+ }
65
+
66
+ /**
67
+ * A multi-unit `show` writes one block per unit separated by a blank line. Keyed
68
+ * by Id rather than by position: `show` answers under the unit's CANONICAL name,
69
+ * so asking about an alias comes back under a different one, and a positional
70
+ * read would attach every later unit's figures to the wrong row.
71
+ */
72
+ export function parseShowRecords(stdout: string): Map<string, Record<string, string>> {
73
+ const byId = new Map<string, Record<string, string>>();
74
+ for (const block of stdout.split(/\n[ \t]*\n/)) {
75
+ const record = parseShowBlock(block);
76
+ if (record.Id) byId.set(record.Id, record);
77
+ }
78
+ return byId;
79
+ }
80
+
81
+ /** Properties one `show` call must ask for to fill a ServiceInfo. */
82
+ export const SHOW_PROPERTIES: readonly string[] = [
83
+ "Id", "Description", "LoadState", "ActiveState", "SubState", "UnitFileState", "MainPID",
84
+ ];
85
+
86
+ /** Everything a details pane adds on top of a list row. */
87
+ export const SHOW_DETAIL_PROPERTIES: readonly string[] = [
88
+ ...SHOW_PROPERTIES, "User", "Group", "FragmentPath",
89
+ ];
90
+
91
+ /**
92
+ * Mission Center's three booleans, derived exactly as it does: ActiveState
93
+ * "active" is running, "failed" is failed, and enabled is the unit-file state
94
+ * being the literal string "enabled" - static, indirect, alias, generated and
95
+ * masked all count as not enabled.
96
+ */
97
+ export function toServiceInfo(
98
+ row: ListUnitsRow,
99
+ scope: ServiceScope,
100
+ show: Record<string, string> | undefined,
101
+ ): ServiceInfo {
102
+ const activeState = show?.ActiveState || row.activeState;
103
+ const subState = show?.SubState || row.subState;
104
+ // An empty UnitFileState means the unit has no unit file, which is null rather
105
+ // than a state of "".
106
+ const fileState = show?.UnitFileState ?? "";
107
+ const mainPid = Number(show?.MainPID ?? "0");
108
+ return {
109
+ unit: row.unit,
110
+ scope,
111
+ description: row.description || show?.Description || "",
112
+ activeState,
113
+ subState,
114
+ unitFileState: fileState || null,
115
+ running: activeState === "active",
116
+ failed: activeState === "failed",
117
+ enabled: fileState === "enabled",
118
+ mainPid: Number.isFinite(mainPid) && mainPid > 0 ? mainPid : null,
119
+ };
120
+ }
121
+
122
+ /** Drops what Mission Center drops: the wrong unit type, and a name systemd knows
123
+ * with no file behind it. Masked units deliberately stay in the list. */
124
+ export function keepUnit(row: ListUnitsRow): boolean {
125
+ return isListedUnit(row.unit) && row.loadState !== "not-found";
126
+ }
127
+
128
+ /**
129
+ * `journalctl -o json`, one JSON object per line.
130
+ *
131
+ * The short-iso form would be shorter to parse and is wrong: a multi-line message
132
+ * - a stack trace, a core dump report - prints as continuation lines with no
133
+ * timestamp, so each would become its own bogus entry. The JSON form keeps one
134
+ * record per entry whatever is in it.
135
+ */
136
+ export function parseJournalJson(stdout: string): ServiceLogLine[] {
137
+ const lines: ServiceLogLine[] = [];
138
+ for (const raw of stdout.split("\n")) {
139
+ if (!raw.trim()) continue;
140
+ let record: Record<string, unknown>;
141
+ try {
142
+ record = JSON.parse(raw) as Record<string, unknown>;
143
+ } catch {
144
+ continue;
145
+ }
146
+ const message = journalMessage(record.MESSAGE);
147
+ if (message === null) continue;
148
+ const micros = Number(record.__REALTIME_TIMESTAMP);
149
+ lines.push({ ts: Number.isFinite(micros) ? Math.round(micros / 1000) : 0, message });
150
+ }
151
+ return lines;
152
+ }
153
+
154
+ /** journald keeps a message that is not valid UTF-8 as an array of byte values. */
155
+ export function journalMessage(value: unknown): string | null {
156
+ if (typeof value === "string") return value;
157
+ if (Array.isArray(value)) {
158
+ const bytes = value.filter((n): n is number => typeof n === "number" && n >= 0 && n <= 255);
159
+ return new TextDecoder().decode(Uint8Array.from(bytes));
160
+ }
161
+ return null;
162
+ }
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Whether a session has sat idle long enough that its prompt cache is gone.
3
+ *
4
+ * The cost of a turn is dominated by the replayed transcript, and that replay is cheap only
5
+ * while the prefix behind it is still cached (see `turn-usage.ts`). The cache is the one
6
+ * part of that with a clock on it: it lapses on its own, silently, some time after the last
7
+ * turn — so the first message of the next morning costs several times what the last message
8
+ * of the night before did, for no reason visible in the conversation.
9
+ *
10
+ * `TurnCostWarning` reports that after the fact, on the turn that paid. This says it
11
+ * *before*, while the user can still decide whether to carry on in this chat or start a
12
+ * cheaper one. Pure and shared so the threshold is the same one the warning already uses.
13
+ */
14
+
15
+ import { PREFIX_WARN_TOKENS } from "./turn-usage.ts";
16
+
17
+ /**
18
+ * What the server knows about a session's cache.
19
+ *
20
+ * `ttlMs` alone is the state of a session that has not completed a turn yet — it is a
21
+ * property of the install, not of the conversation, and sending it early is what lets a
22
+ * long-lived tab arm this notice from its own turns without waiting for a reconnect.
23
+ */
24
+ export interface PromptCacheState {
25
+ /** This install's cache lifetime: an hour on a subscription, five minutes on an API key. */
26
+ ttlMs: number;
27
+ /** When the last turn completed — the moment the cache was last written. */
28
+ lastTurnEndedAt?: number;
29
+ /**
30
+ * Cached prefix *billed* on the last turn — a running session total, not a transcript size.
31
+ *
32
+ * The SDK sums `modelUsage` across a streaming session, so a long chat reports tens of
33
+ * millions against a context window of one. It is a usable floor for "has this session
34
+ * cached anything worth losing" and nothing more; never show it as a number.
35
+ */
36
+ billedPrefixTokens?: number;
37
+ /**
38
+ * Context the last turn actually held — the figure that may be displayed.
39
+ *
40
+ * Measured from one API call's own `usage` rather than summed (see
41
+ * `TurnUsage.contextTokens`). Absent for a turn recorded before PPM measured this, so the
42
+ * notice has to read correctly without it.
43
+ */
44
+ contextTokens?: number;
45
+ /**
46
+ * When this session was compacted, if it has been since its last cache write.
47
+ *
48
+ * A compaction replaces the conversation with a summary, so the cached prefix no longer
49
+ * matches anything that will be sent — the cache is gone regardless of how recently it was
50
+ * written. Cleared again by the first API call after the boundary, which caches the new
51
+ * prefix; it therefore only survives a compaction that nothing followed, which is exactly
52
+ * the `/compact` the user is about to type into.
53
+ */
54
+ compactedAt?: number;
55
+ }
56
+
57
+ /** Why a session's cache is gone. */
58
+ export type ColdReason = "expired" | "compacted";
59
+
60
+ export interface IdleCacheNotice {
61
+ /** How long since the last turn, or since the compaction, for the wording. */
62
+ idleMs: number;
63
+ reason: ColdReason;
64
+ /**
65
+ * Transcript that the next message re-sends, when PPM measured it.
66
+ *
67
+ * Never set for a compaction: what will be re-cached then is the summary the compaction
68
+ * produced, which no API call has reported a size for yet. Claude Code hardcodes the same
69
+ * `undefined` for that branch.
70
+ */
71
+ contextTokens?: number;
72
+ }
73
+
74
+ /**
75
+ * The notice to show, or null for nothing worth saying.
76
+ *
77
+ * Silent in three cases, all of them deliberate. Before the TTL, because the cache really is
78
+ * still warm and a countdown to a cost that has not happened is just noise. Below
79
+ * `PREFIX_WARN_TOKENS`, because a session that has never billed even that much has nothing
80
+ * worth warning about — the same floor the after-the-fact warning uses, so the two cannot
81
+ * disagree about what is worth mentioning. And with no state at all, because "PPM has not
82
+ * measured this" and "this session has nothing cached" must not be reported as the same thing.
83
+ */
84
+ export function idleCacheNotice(
85
+ state: PromptCacheState | null | undefined,
86
+ now: number,
87
+ ): IdleCacheNotice | null {
88
+ const status = promptCacheStatus(state, now);
89
+ if (status.kind !== "cold") return null;
90
+ return {
91
+ idleMs: status.idleMs,
92
+ reason: status.reason,
93
+ ...(status.contextTokens != null && { contextTokens: status.contextTokens }),
94
+ };
95
+ }
96
+
97
+ /**
98
+ * The cache's clock, for a countdown that is on screen the whole time.
99
+ *
100
+ * Same three gates as the notice, deliberately: a chip that turns red at the hour while no
101
+ * banner appears — or the reverse — asks the user to reconcile two readings of one fact.
102
+ * `unknown` means PPM has not measured this session, which must render as nothing rather
103
+ * than as a full or empty timer.
104
+ */
105
+ export type PromptCacheStatus =
106
+ | { kind: "unknown" }
107
+ | { kind: "warm"; remainingMs: number }
108
+ | { kind: "cold"; reason: ColdReason; idleMs: number; contextTokens?: number };
109
+
110
+ export function promptCacheStatus(
111
+ state: PromptCacheState | null | undefined,
112
+ now: number,
113
+ ): PromptCacheStatus {
114
+ if (!state) return { kind: "unknown" };
115
+
116
+ // Before every other question, including whether anything has been measured at all. A
117
+ // compaction does not make the cache *stale*, it makes it inapplicable — the prefix it
118
+ // holds is not the conversation any more — so a turn that finished ten seconds ago is
119
+ // still cold. Ordered exactly as Claude Code orders it, and it also settles the floor
120
+ // below: a session large enough to have been compacted is never a trivial one.
121
+ if (state.compactedAt != null) {
122
+ return { kind: "cold", reason: "compacted", idleMs: Math.max(0, now - state.compactedAt) };
123
+ }
124
+
125
+ // No completed turn means nothing has been cached, so there is nothing to lose yet.
126
+ if (state.lastTurnEndedAt == null || state.billedPrefixTokens == null) return { kind: "unknown" };
127
+ if (state.billedPrefixTokens < PREFIX_WARN_TOKENS) return { kind: "unknown" };
128
+
129
+ const idleMs = now - state.lastTurnEndedAt;
130
+ // A clock that disagrees between server and browser can make this negative; a turn that
131
+ // just finished is the warmest case there is, so it reads as "not idle" either way.
132
+ if (idleMs < state.ttlMs) {
133
+ // Clamped, so a skewed clock shows a full window rather than one longer than the TTL.
134
+ return { kind: "warm", remainingMs: Math.min(state.ttlMs, state.ttlMs - idleMs) };
135
+ }
136
+
137
+ return {
138
+ kind: "cold",
139
+ reason: "expired",
140
+ idleMs,
141
+ ...(state.contextTokens != null && { contextTokens: state.contextTokens }),
142
+ };
143
+ }
144
+
145
+ const MINUTE_MS = 60_000;
146
+ const HOUR_MS = 60 * MINUTE_MS;
147
+ const DAY_MS = 24 * HOUR_MS;
148
+
149
+ /**
150
+ * How long the session has been idle, at the coarsest unit that still says something.
151
+ *
152
+ * Minutes are dropped past a day: "2d 7h 13m" is three figures to answer "is this stale",
153
+ * which is the only question being asked.
154
+ */
155
+ export function formatIdleDuration(ms: number): string {
156
+ if (ms >= DAY_MS) {
157
+ const days = Math.floor(ms / DAY_MS);
158
+ const hours = Math.floor((ms % DAY_MS) / HOUR_MS);
159
+ return hours > 0 ? `${days}d ${hours}h` : `${days}d`;
160
+ }
161
+ if (ms >= HOUR_MS) {
162
+ const hours = Math.floor(ms / HOUR_MS);
163
+ const minutes = Math.floor((ms % HOUR_MS) / MINUTE_MS);
164
+ return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
165
+ }
166
+ return `${Math.max(1, Math.floor(ms / MINUTE_MS))}m`;
167
+ }
168
+
169
+ /**
170
+ * Time left on the cache, for the composer's countdown chip.
171
+ *
172
+ * Rounded **up**, so a fresh turn reads the install's whole window (`60m`, not `59m`) and the
173
+ * last partial minute reads `1m` rather than `0m` — a zero here would say expired while the
174
+ * cache is still warm, which is the one reading the chip must never give.
175
+ *
176
+ * Minutes throughout, with no hours branch: the longest window PPM has is the subscription's
177
+ * hour, so the only thing an hours branch could ever render is the full window as `1h` — one
178
+ * more unit for the eye to parse, in the single case the chip is least interesting.
179
+ */
180
+ export function formatCacheCountdown(ms: number): string {
181
+ return `${Math.max(1, Math.ceil(ms / MINUTE_MS))}m`;
182
+ }
183
+
184
+ /**
185
+ * A measured context size, at the precision the decision needs.
186
+ *
187
+ * Whole thousands: the user is deciding whether to carry on in this chat or start a fresh
188
+ * one, and no part of that turns on the difference between 58.7k and 59k.
189
+ */
190
+ export function formatContextTokens(tokens: number): string {
191
+ if (tokens >= 1_000_000) {
192
+ const millions = tokens / 1_000_000;
193
+ return `${millions >= 10 ? Math.round(millions) : millions.toFixed(1)}M`;
194
+ }
195
+ if (tokens >= 1_000) return `${Math.round(tokens / 1_000)}k`;
196
+ return `${tokens}`;
197
+ }
@@ -46,6 +46,36 @@ export interface TurnUsage {
46
46
  */
47
47
  accountId?: string;
48
48
  accountLabel?: string;
49
+ /**
50
+ * Live context held at the end of the turn — what the next message actually replays.
51
+ *
52
+ * Not derivable from anything else on this type. Every other field here is summed out of
53
+ * the result's `modelUsage`, which the SDK accumulates across a streaming session and
54
+ * across subagents: one measured session reported 55.9M prefix tokens against a 200k
55
+ * window, and per-turn deltas fare no better (one turn's delta was 4.2M — twenty-one
56
+ * windows — because twenty subagents each replayed their own prefix into the same sum).
57
+ *
58
+ * This comes instead from the `usage` on the turn's last top-level assistant message,
59
+ * which is one API call's own input side and so is a real context size. Absent when no
60
+ * such message carried usage; never substitute a summed figure for it.
61
+ */
62
+ contextTokens?: number;
63
+ /**
64
+ * Cache window the API reported for this turn, when it reported one.
65
+ *
66
+ * Outranks the provider's credential-shaped guess wherever both exist — see
67
+ * `messageCacheTtl`. Absent on a turn that only read the cache, and on every turn recorded
68
+ * before PPM read this field.
69
+ */
70
+ cacheTtlMs?: number;
71
+ /**
72
+ * When a compaction invalidated this session's cache, if one did and nothing re-cached after.
73
+ *
74
+ * Not a timestamp of "the last compaction" — the provider clears it on the next API call,
75
+ * so an auto-compact mid-turn never reaches here. What survives is a compaction the turn
76
+ * ended on, which is the case where the user's next message pays to rebuild the prefix.
77
+ */
78
+ compactedAt?: number;
49
79
  }
50
80
 
51
81
  /** Total prefix replayed to the API this turn, cached or not. */
@@ -66,7 +96,7 @@ export function uncachedPrefixTokens(u: TurnUsage): number {
66
96
  */
67
97
  export function buildTurnUsage(
68
98
  modelUsage: Record<string, ModelUsageLike> | undefined,
69
- opts: { coldReason?: string; accountId?: string; accountLabel?: string } = {},
99
+ opts: { coldReason?: string; accountId?: string; accountLabel?: string; contextTokens?: number; cacheTtlMs?: number; compactedAt?: number } = {},
70
100
  ): TurnUsage | undefined {
71
101
  if (!modelUsage) return undefined;
72
102
  const entries = Object.entries(modelUsage);
@@ -113,9 +143,63 @@ export function buildTurnUsage(
113
143
  ...(opts.coldReason && { coldReason: opts.coldReason }),
114
144
  ...(opts.accountId && { accountId: opts.accountId }),
115
145
  ...(opts.accountLabel && { accountLabel: opts.accountLabel }),
146
+ ...(opts.contextTokens != null && { contextTokens: opts.contextTokens }),
147
+ ...(opts.cacheTtlMs != null && { cacheTtlMs: opts.cacheTtlMs }),
148
+ ...(opts.compactedAt != null && { compactedAt: opts.compactedAt }),
116
149
  };
117
150
  }
118
151
 
152
+ /**
153
+ * Context size carried by one assistant message's `usage`, or undefined if it carried none.
154
+ *
155
+ * The input side of a single API call: what the model was actually given, whether it arrived
156
+ * fresh or out of the cache. Output tokens are excluded — they are not in the context the
157
+ * *next* call replays until they have been written back as a message, and counting them here
158
+ * would inflate every turn by its own answer.
159
+ */
160
+ export function messageContextTokens(usage: unknown): number | undefined {
161
+ if (!usage || typeof usage !== "object") return undefined;
162
+ const u = usage as Record<string, unknown>;
163
+ const num = (v: unknown): number => (typeof v === "number" && Number.isFinite(v) ? v : 0);
164
+ const total = num(u.input_tokens) + num(u.cache_read_input_tokens) + num(u.cache_creation_input_tokens);
165
+ // Zero is not a context, it is a frame that carried no input side — a usage object with
166
+ // only output tokens, say. Returning it would render as "re-sends about 0 tokens" on a
167
+ // full session. Matches Claude Code's own reader, which likewise sums all three fields
168
+ // without requiring any one of them and rejects a zero total.
169
+ return total > 0 ? total : undefined;
170
+ }
171
+
172
+ /** Which prompt-cache window the API wrote this turn's prefix into. */
173
+ export type PromptCacheTtl = "1h" | "5m";
174
+
175
+ /**
176
+ * The cache lifetime the API itself reports, or undefined when it reported none.
177
+ *
178
+ * PPM otherwise *infers* this from the credential — an `sk-ant-oat` token means a
179
+ * subscription and therefore an hour, anything else five minutes — which is a guess about
180
+ * billing made from the shape of a string. It is wrong for a proxy, for a custom `base_url`,
181
+ * and for a subscription past its usage limits, and being wrong is not cosmetic: the
182
+ * countdown and the idle banner both measure against this, so a session whose cache really
183
+ * lives an hour is declared cold at minute five.
184
+ *
185
+ * `usage.cache_creation` answers it outright. Mirrors Claude Code's own reader, including
186
+ * the order: a prefix written into both windows is reported as the longer one, because that
187
+ * is the one still standing. Returns the discriminant rather than milliseconds so this stays
188
+ * free of the server's constants — `shared/` is imported by the browser bundle.
189
+ */
190
+ export function messageCacheTtl(usage: unknown): PromptCacheTtl | undefined {
191
+ if (!usage || typeof usage !== "object") return undefined;
192
+ const cc = (usage as { cache_creation?: unknown }).cache_creation;
193
+ if (!cc || typeof cc !== "object") return undefined;
194
+ const c = cc as Record<string, unknown>;
195
+ const num = (v: unknown): number => (typeof v === "number" && Number.isFinite(v) ? v : 0);
196
+ if (num(c.ephemeral_1h_input_tokens) > 0) return "1h";
197
+ if (num(c.ephemeral_5m_input_tokens) > 0) return "5m";
198
+ // A turn that only *read* the cache writes nothing, so it names no window. That is silence,
199
+ // not five minutes: answering the short one here would expire a warm hour-long cache.
200
+ return undefined;
201
+ }
202
+
119
203
  /**
120
204
  * A prefix smaller than this is cheap however it is billed, so a poor hit rate on it is not
121
205
  * worth interrupting the user over. Short sessions legitimately start cold.