@hienlh/ppm 0.23.11 → 0.23.12

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 (641) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/CLAUDE.md +13 -2
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/cli-reference.md +4 -4
  5. package/assets/skills/ppm/references/http-api.md +37 -1
  6. package/bun.lock +34 -0
  7. package/dist/web/assets/accounts-settings-section-CGe5ikvs.js +1 -0
  8. package/dist/web/assets/accounts-settings-section-CGe5ikvs.js.br +0 -0
  9. package/dist/web/assets/accounts-settings-section-CGe5ikvs.js.gz +0 -0
  10. package/dist/web/assets/{agent-session-sheet-CBMsnl_p.js → agent-session-sheet-vBhfHYrC.js} +2 -2
  11. package/dist/web/assets/agent-session-sheet-vBhfHYrC.js.br +0 -0
  12. package/dist/web/assets/agent-session-sheet-vBhfHYrC.js.gz +0 -0
  13. package/dist/web/assets/{agent-session-window-content-D9C9FA7E.js → agent-session-window-content-BxIxDN5i.js} +1 -1
  14. package/dist/web/assets/agent-session-window-content-BxIxDN5i.js.br +0 -0
  15. package/dist/web/assets/agent-session-window-content-BxIxDN5i.js.gz +0 -0
  16. package/dist/web/assets/{ai-resource-editor-DhUVgDEG.js → ai-resource-editor-Y8clBPRv.js} +1 -1
  17. package/dist/web/assets/ai-resource-editor-Y8clBPRv.js.br +0 -0
  18. package/dist/web/assets/ai-resource-editor-Y8clBPRv.js.gz +0 -0
  19. package/dist/web/assets/{ai-settings-section-DOckWvp4.js → ai-settings-section-BB8vscte.js} +1 -1
  20. package/dist/web/assets/{ai-settings-section-CETpSpYd.js → ai-settings-section-Ck8uN_Bw.js} +1 -1
  21. package/dist/web/assets/ai-settings-section-Ck8uN_Bw.js.br +0 -0
  22. package/dist/web/assets/ai-settings-section-Ck8uN_Bw.js.gz +0 -0
  23. package/dist/web/assets/android-tab-BIOUVVoW.js +2 -0
  24. package/dist/web/assets/android-tab-BIOUVVoW.js.br +0 -0
  25. package/dist/web/assets/android-tab-BIOUVVoW.js.gz +0 -0
  26. package/dist/web/assets/{api-settings-JIgKfcCx.js → api-settings-D1axn9vm.js} +1 -1
  27. package/dist/web/assets/{appearance-settings-section-BJUiLUb7.js → appearance-settings-section-Bv5_n9lH.js} +1 -1
  28. package/dist/web/assets/appearance-settings-section-Bv5_n9lH.js.br +0 -0
  29. package/dist/web/assets/appearance-settings-section-Bv5_n9lH.js.gz +0 -0
  30. package/dist/web/assets/{apps-panel-1VElRgV3.js → apps-panel-DXHF0QEv.js} +1 -1
  31. package/dist/web/assets/apps-panel-DXHF0QEv.js.br +0 -0
  32. package/dist/web/assets/apps-panel-DXHF0QEv.js.gz +0 -0
  33. package/dist/web/assets/{audio-preview-DYkyHu5X.js → audio-preview-DpjOKEm4.js} +1 -1
  34. package/dist/web/assets/audio-preview-DpjOKEm4.js.br +0 -0
  35. package/dist/web/assets/audio-preview-DpjOKEm4.js.gz +0 -0
  36. package/dist/web/assets/branch-review-tab-4dgMNadQ.js +1 -0
  37. package/dist/web/assets/branch-review-tab-4dgMNadQ.js.br +0 -0
  38. package/dist/web/assets/branch-review-tab-4dgMNadQ.js.gz +0 -0
  39. package/dist/web/assets/chat-tab-Bbe_77oJ.js +15 -0
  40. package/dist/web/assets/chat-tab-Bbe_77oJ.js.br +0 -0
  41. package/dist/web/assets/chat-tab-Bbe_77oJ.js.gz +0 -0
  42. package/dist/web/assets/chat-tab-D3i5O3dO.js +1 -0
  43. package/dist/web/assets/chat-tab-D3i5O3dO.js.br +0 -0
  44. package/dist/web/assets/chat-tab-D3i5O3dO.js.gz +0 -0
  45. package/dist/web/assets/code-editor-DXsIWoQ1.js +7 -0
  46. package/dist/web/assets/code-editor-DXsIWoQ1.js.br +0 -0
  47. package/dist/web/assets/code-editor-DXsIWoQ1.js.gz +0 -0
  48. package/dist/web/assets/{codex-reset-credit-button-Fr45FUxi.js → codex-reset-credit-button-DRhievFQ.js} +1 -1
  49. package/dist/web/assets/codex-reset-credit-button-DRhievFQ.js.br +0 -0
  50. package/dist/web/assets/codex-reset-credit-button-DRhievFQ.js.gz +0 -0
  51. package/dist/web/assets/{conflict-editor-B-wdjSXI.js → conflict-editor-D_RImo_V.js} +3 -3
  52. package/dist/web/assets/conflict-editor-D_RImo_V.js.br +0 -0
  53. package/dist/web/assets/conflict-editor-D_RImo_V.js.gz +0 -0
  54. package/dist/web/assets/{csv-preview-B6RodObB.js → csv-preview-BuFdfLo3.js} +1 -1
  55. package/dist/web/assets/csv-preview-BuFdfLo3.js.br +0 -0
  56. package/dist/web/assets/csv-preview-BuFdfLo3.js.gz +0 -0
  57. package/dist/web/assets/{database-viewer-CdPBUQPY.js → database-viewer-ChcbYn9E.js} +1 -1
  58. package/dist/web/assets/database-viewer-ChcbYn9E.js.br +0 -0
  59. package/dist/web/assets/database-viewer-ChcbYn9E.js.gz +0 -0
  60. package/dist/web/assets/{design-settings-section-BpCpaBwL.js → design-settings-section-B_xOf51h.js} +1 -1
  61. package/dist/web/assets/design-settings-section-B_xOf51h.js.br +0 -0
  62. package/dist/web/assets/design-settings-section-B_xOf51h.js.gz +0 -0
  63. package/dist/web/assets/{design-skill-suggestion-DkT2E2IV.js → design-skill-suggestion-BbEDg15Z.js} +1 -1
  64. package/dist/web/assets/design-skill-suggestion-BbEDg15Z.js.br +0 -0
  65. package/dist/web/assets/design-skill-suggestion-BbEDg15Z.js.gz +0 -0
  66. package/dist/web/assets/{design-tab-BZb9eemI.js → design-tab-BQSYJ4cS.js} +6 -6
  67. package/dist/web/assets/design-tab-BQSYJ4cS.js.br +0 -0
  68. package/dist/web/assets/design-tab-BQSYJ4cS.js.gz +0 -0
  69. package/dist/web/assets/{designs-sidebar-panel-CF4tp6j-.js → designs-sidebar-panel-AiPaYfSU.js} +1 -1
  70. package/dist/web/assets/designs-sidebar-panel-AiPaYfSU.js.br +0 -0
  71. package/dist/web/assets/designs-sidebar-panel-AiPaYfSU.js.gz +0 -0
  72. package/dist/web/assets/{diff-viewer-BqEqwSw6.js → diff-viewer-BZ6glGK-.js} +1 -1
  73. package/dist/web/assets/diff-viewer-BZ6glGK-.js.br +0 -0
  74. package/dist/web/assets/diff-viewer-BZ6glGK-.js.gz +0 -0
  75. package/dist/web/assets/{edit-diff-preview-DD4HayXY.js → edit-diff-preview-D-FNLOU2.js} +1 -1
  76. package/dist/web/assets/edit-diff-preview-D-FNLOU2.js.br +0 -0
  77. package/dist/web/assets/edit-diff-preview-D-FNLOU2.js.gz +0 -0
  78. package/dist/web/assets/{editor-language-service-CkA5sEdV.js → editor-language-service-4sbsdqBV.js} +1 -1
  79. package/dist/web/assets/editor-language-service-4sbsdqBV.js.br +0 -0
  80. package/dist/web/assets/editor-language-service-4sbsdqBV.js.gz +0 -0
  81. package/dist/web/assets/explorer-body-DbgrG1fe.js +2 -0
  82. package/dist/web/assets/explorer-body-DbgrG1fe.js.br +0 -0
  83. package/dist/web/assets/explorer-body-DbgrG1fe.js.gz +0 -0
  84. package/dist/web/assets/{explorer-window-content-CRJ2eMZ4.js → explorer-window-content-BCjWvHpp.js} +1 -1
  85. package/dist/web/assets/explorer-window-content-BCjWvHpp.js.br +0 -0
  86. package/dist/web/assets/explorer-window-content-BCjWvHpp.js.gz +0 -0
  87. package/dist/web/assets/{extension-manager-section-DwQTdCII.js → extension-manager-section-DiMF4RnT.js} +1 -1
  88. package/dist/web/assets/extension-manager-section-DiMF4RnT.js.br +0 -0
  89. package/dist/web/assets/extension-manager-section-DiMF4RnT.js.gz +0 -0
  90. package/dist/web/assets/{extension-webview-CMq-VTLu.js → extension-webview-Coti6Vfw.js} +1 -1
  91. package/dist/web/assets/extension-webview-Coti6Vfw.js.br +0 -0
  92. package/dist/web/assets/extension-webview-Coti6Vfw.js.gz +0 -0
  93. package/dist/web/assets/file-store-Cm7-pHux.js +1 -0
  94. package/dist/web/assets/file-store-Cm7-pHux.js.br +0 -0
  95. package/dist/web/assets/file-store-Cm7-pHux.js.gz +0 -0
  96. package/dist/web/assets/{files-settings-section-B2lcyh-6.js → files-settings-section-ZTVwOwx0.js} +1 -1
  97. package/dist/web/assets/files-settings-section-ZTVwOwx0.js.br +0 -0
  98. package/dist/web/assets/files-settings-section-ZTVwOwx0.js.gz +0 -0
  99. package/dist/web/assets/{general-settings-section-Doepm2sH.js → general-settings-section-B7Hjx_fb.js} +1 -1
  100. package/dist/web/assets/general-settings-section-B7Hjx_fb.js.br +0 -0
  101. package/dist/web/assets/general-settings-section-B7Hjx_fb.js.gz +0 -0
  102. package/dist/web/assets/{git-log-panel-CJakNMDc.js → git-log-panel-CbcJ_YEH.js} +1 -1
  103. package/dist/web/assets/git-log-panel-CbcJ_YEH.js.br +0 -0
  104. package/dist/web/assets/git-log-panel-CbcJ_YEH.js.gz +0 -0
  105. package/dist/web/assets/{glide-data-grid-C_SeOhSq.js → glide-data-grid-BfByGSf1.js} +3 -3
  106. package/dist/web/assets/glide-data-grid-BfByGSf1.js.br +0 -0
  107. package/dist/web/assets/glide-data-grid-BfByGSf1.js.gz +0 -0
  108. package/dist/web/assets/group-chat-tab-DCo9OP3D.js +1 -0
  109. package/dist/web/assets/group-chat-tab-DCo9OP3D.js.br +0 -0
  110. package/dist/web/assets/group-chat-tab-DCo9OP3D.js.gz +0 -0
  111. package/dist/web/assets/{image-preview-C1Frlx22.js → image-preview-Cjlps7Pt.js} +1 -1
  112. package/dist/web/assets/index-CyDLBddf.js +52 -0
  113. package/dist/web/assets/index-CyDLBddf.js.br +0 -0
  114. package/dist/web/assets/index-CyDLBddf.js.gz +0 -0
  115. package/dist/web/assets/index-Dh5apC3I.css +1 -0
  116. package/dist/web/assets/index-Dh5apC3I.css.br +0 -0
  117. package/dist/web/assets/index-Dh5apC3I.css.gz +0 -0
  118. package/dist/web/assets/{jira-watcher-section-BmkIGQFV.js → jira-watcher-section-Dz_-sUC2.js} +1 -1
  119. package/dist/web/assets/keybindings-store-BTq5Z7XP.js +1 -0
  120. package/dist/web/assets/{keyboard-shortcuts-section-BgZe3QDr.js → keyboard-shortcuts-section-Ba5NYwlG.js} +1 -1
  121. package/dist/web/assets/keyboard-shortcuts-section-Ba5NYwlG.js.br +0 -0
  122. package/dist/web/assets/keyboard-shortcuts-section-Ba5NYwlG.js.gz +0 -0
  123. package/dist/web/assets/{language-servers-section-CCMzgOaE.js → language-servers-section-DJJ7SFXo.js} +2 -2
  124. package/dist/web/assets/language-servers-section-DJJ7SFXo.js.br +0 -0
  125. package/dist/web/assets/language-servers-section-DJJ7SFXo.js.gz +0 -0
  126. package/dist/web/assets/{lsp-client-B10suuaZ.js → lsp-client-Dr3vQ1bb.js} +1 -1
  127. package/dist/web/assets/lsp-client-Dr3vQ1bb.js.br +0 -0
  128. package/dist/web/assets/lsp-client-Dr3vQ1bb.js.gz +0 -0
  129. package/dist/web/assets/{markdown-renderer-AHHFS3jc.js → markdown-renderer-CmnqFfTU.js} +3 -3
  130. package/dist/web/assets/markdown-renderer-CmnqFfTU.js.br +0 -0
  131. package/dist/web/assets/markdown-renderer-CmnqFfTU.js.gz +0 -0
  132. package/dist/web/assets/markdown-renderer-LJdVMbGl.js +1 -0
  133. package/dist/web/assets/{mobile-explorer-sheet-CqCsLWSr.js → mobile-explorer-sheet-BiIhZtJ2.js} +1 -1
  134. package/dist/web/assets/mobile-explorer-sheet-BiIhZtJ2.js.br +0 -0
  135. package/dist/web/assets/mobile-explorer-sheet-BiIhZtJ2.js.gz +0 -0
  136. package/dist/web/assets/new-design-dialog-DI_hxvFV.js +1 -0
  137. package/dist/web/assets/new-design-dialog-DI_hxvFV.js.br +0 -0
  138. package/dist/web/assets/new-design-dialog-DI_hxvFV.js.gz +0 -0
  139. package/dist/web/assets/notification-store-9lBSplOV.js +1 -0
  140. package/dist/web/assets/{notifications-settings-section-CXgzH_bK.js → notifications-settings-section-0s-2OKRv.js} +1 -1
  141. package/dist/web/assets/notifications-settings-section-0s-2OKRv.js.br +0 -0
  142. package/dist/web/assets/notifications-settings-section-0s-2OKRv.js.gz +0 -0
  143. package/dist/web/assets/{overview-panel-BYB8mgRE.js → overview-panel-3htxfZS2.js} +1 -1
  144. package/dist/web/assets/overview-panel-3htxfZS2.js.br +0 -0
  145. package/dist/web/assets/overview-panel-3htxfZS2.js.gz +0 -0
  146. package/dist/web/assets/panel-store-B6PoCwBA.js +1 -0
  147. package/dist/web/assets/panel-store-B6PoCwBA.js.br +0 -0
  148. package/dist/web/assets/panel-store-B6PoCwBA.js.gz +0 -0
  149. package/dist/web/assets/{performance-panel-Dpuq3g0W.js → performance-panel-V8gpnnf7.js} +2 -2
  150. package/dist/web/assets/performance-panel-V8gpnnf7.js.br +0 -0
  151. package/dist/web/assets/performance-panel-V8gpnnf7.js.gz +0 -0
  152. package/dist/web/assets/{postgres-viewer-DW0Gkmow.js → postgres-viewer-sWS-ogTC.js} +1 -1
  153. package/dist/web/assets/postgres-viewer-sWS-ogTC.js.br +0 -0
  154. package/dist/web/assets/postgres-viewer-sWS-ogTC.js.gz +0 -0
  155. package/dist/web/assets/{ppmbot-settings-section-DFtlGoW6.js → ppmbot-settings-section-CT8vvlzk.js} +1 -1
  156. package/dist/web/assets/ppmbot-settings-section-CT8vvlzk.js.br +0 -0
  157. package/dist/web/assets/ppmbot-settings-section-CT8vvlzk.js.gz +0 -0
  158. package/dist/web/assets/{pptx-export-CNyG32Xr.js → pptx-export-Ugjv0hso.js} +4 -4
  159. package/dist/web/assets/pptx-export-Ugjv0hso.js.br +0 -0
  160. package/dist/web/assets/pptx-export-Ugjv0hso.js.gz +0 -0
  161. package/dist/web/assets/{problems-panel-V5zqpuXq.js → problems-panel-Gcy79pSS.js} +1 -1
  162. package/dist/web/assets/problems-panel-Gcy79pSS.js.br +0 -0
  163. package/dist/web/assets/problems-panel-Gcy79pSS.js.gz +0 -0
  164. package/dist/web/assets/{process-row-format-CiJJSf_V.js → process-row-format-u6y7lOee.js} +1 -1
  165. package/dist/web/assets/process-table-BUTIp9Oy.js +1 -0
  166. package/dist/web/assets/process-table-BUTIp9Oy.js.br +0 -0
  167. package/dist/web/assets/process-table-BUTIp9Oy.js.gz +0 -0
  168. package/dist/web/assets/{proxy-settings-section-B7AVlTfY.js → proxy-settings-section-Buv_8y54.js} +1 -1
  169. package/dist/web/assets/proxy-settings-section-Buv_8y54.js.br +0 -0
  170. package/dist/web/assets/proxy-settings-section-Buv_8y54.js.gz +0 -0
  171. package/dist/web/assets/{query-audit-section-gHmRfWKr.js → query-audit-section-N2DQtgJx.js} +1 -1
  172. package/dist/web/assets/query-audit-section-N2DQtgJx.js.br +0 -0
  173. package/dist/web/assets/query-audit-section-N2DQtgJx.js.gz +0 -0
  174. package/dist/web/assets/remote-desktop-clipboard-notice-CRvJqmaz.js +1 -0
  175. package/dist/web/assets/remote-desktop-clipboard-notice-CRvJqmaz.js.br +0 -0
  176. package/dist/web/assets/remote-desktop-clipboard-notice-CRvJqmaz.js.gz +0 -0
  177. package/dist/web/assets/remote-desktop-mobile-sheet-BoYINLbJ.js +2 -0
  178. package/dist/web/assets/remote-desktop-mobile-sheet-BoYINLbJ.js.br +0 -0
  179. package/dist/web/assets/remote-desktop-mobile-sheet-BoYINLbJ.js.gz +0 -0
  180. package/dist/web/assets/remote-desktop-mobile-view-DPdP0yYu.js +1 -0
  181. package/dist/web/assets/remote-desktop-mobile-view-DPdP0yYu.js.br +0 -0
  182. package/dist/web/assets/remote-desktop-mobile-view-DPdP0yYu.js.gz +0 -0
  183. package/dist/web/assets/remote-desktop-readiness-gate-BeQ_Jrp7.js +1 -0
  184. package/dist/web/assets/remote-desktop-readiness-gate-BeQ_Jrp7.js.br +0 -0
  185. package/dist/web/assets/remote-desktop-readiness-gate-BeQ_Jrp7.js.gz +0 -0
  186. package/dist/web/assets/remote-desktop-settings-section-Bs4sHS3l.js +1 -0
  187. package/dist/web/assets/remote-desktop-settings-section-Bs4sHS3l.js.br +0 -0
  188. package/dist/web/assets/remote-desktop-settings-section-Bs4sHS3l.js.gz +0 -0
  189. package/dist/web/assets/remote-desktop-window-content-DDr__JxQ.js +1 -0
  190. package/dist/web/assets/remote-desktop-window-content-DDr__JxQ.js.br +0 -0
  191. package/dist/web/assets/remote-desktop-window-content-DDr__JxQ.js.gz +0 -0
  192. package/dist/web/assets/{run-in-terminal-DKBcEGVu.js → run-in-terminal-DCBNtYHQ.js} +1 -1
  193. package/dist/web/assets/{schedules-settings-section-sYDAsBMO.js → schedules-settings-section-_cHX2qOa.js} +1 -1
  194. package/dist/web/assets/schedules-settings-section-_cHX2qOa.js.br +0 -0
  195. package/dist/web/assets/schedules-settings-section-_cHX2qOa.js.gz +0 -0
  196. package/dist/web/assets/{send-to-chat-CCdOrKc7.js → send-to-chat-dNqNV7Y4.js} +1 -1
  197. package/dist/web/assets/send-to-chat-dNqNV7Y4.js.br +0 -0
  198. package/dist/web/assets/send-to-chat-dNqNV7Y4.js.gz +0 -0
  199. package/dist/web/assets/{services-panel-CKa2aP1d.js → services-panel-Dm54cnW3.js} +1 -1
  200. package/dist/web/assets/services-panel-Dm54cnW3.js.br +0 -0
  201. package/dist/web/assets/services-panel-Dm54cnW3.js.gz +0 -0
  202. package/dist/web/assets/settings-body-DRRQNaDA.js +1 -0
  203. package/dist/web/assets/settings-body-DRRQNaDA.js.br +0 -0
  204. package/dist/web/assets/settings-body-DRRQNaDA.js.gz +0 -0
  205. package/dist/web/assets/settings-section-content-DRaHfvm8.js +1 -0
  206. package/dist/web/assets/settings-section-content-U3mIx6hF.js +2 -0
  207. package/dist/web/assets/settings-section-content-U3mIx6hF.js.br +0 -0
  208. package/dist/web/assets/settings-section-content-U3mIx6hF.js.gz +0 -0
  209. package/dist/web/assets/{settings-store-DfuiLq_9.js → settings-store-Cyuo8ceh.js} +2 -2
  210. package/dist/web/assets/settings-store-Cyuo8ceh.js.br +0 -0
  211. package/dist/web/assets/settings-store-Cyuo8ceh.js.gz +0 -0
  212. package/dist/web/assets/settings-tab-CB_BhHTK.js +1 -0
  213. package/dist/web/assets/settings-window-content-CgZIMNX8.js +1 -0
  214. package/dist/web/assets/{slash-command-picker-CLevA_Gf.js → slash-command-picker-CQlIqMW0.js} +1 -1
  215. package/dist/web/assets/slash-command-picker-CQlIqMW0.js.br +0 -0
  216. package/dist/web/assets/slash-command-picker-CQlIqMW0.js.gz +0 -0
  217. package/dist/web/assets/{sql-query-editor-Db8b0jEr.js → sql-query-editor--Z4bcqd8.js} +1 -1
  218. package/dist/web/assets/sql-query-editor--Z4bcqd8.js.br +0 -0
  219. package/dist/web/assets/sql-query-editor--Z4bcqd8.js.gz +0 -0
  220. package/dist/web/assets/{sqlite-viewer-VzYLMqkD.js → sqlite-viewer-CSmX3qaj.js} +1 -1
  221. package/dist/web/assets/sqlite-viewer-CSmX3qaj.js.br +0 -0
  222. package/dist/web/assets/sqlite-viewer-CSmX3qaj.js.gz +0 -0
  223. package/dist/web/assets/{system-monitor-body-BR60W_mG.js → system-monitor-body-LannpU6H.js} +2 -2
  224. package/dist/web/assets/system-monitor-body-LannpU6H.js.br +0 -0
  225. package/dist/web/assets/system-monitor-body-LannpU6H.js.gz +0 -0
  226. package/dist/web/assets/system-monitor-tab-BfVucwEF.js +1 -0
  227. package/dist/web/assets/{system-monitor-window-content-BLw1QBXo.js → system-monitor-window-content-kLNW9Ln3.js} +1 -1
  228. package/dist/web/assets/{tab-host-window-content-BL61SB5m.js → tab-host-window-content-II_FtOYM.js} +1 -1
  229. package/dist/web/assets/tab-host-window-content-II_FtOYM.js.br +0 -0
  230. package/dist/web/assets/tab-host-window-content-II_FtOYM.js.gz +0 -0
  231. package/dist/web/assets/{tab-store-CbPh8CAX.js → tab-store-DCpc0g0Q.js} +1 -1
  232. package/dist/web/assets/{terminal-tab-B4y6at4h.js → terminal-tab-CTe2polt.js} +1 -1
  233. package/dist/web/assets/terminal-tab-CTe2polt.js.br +0 -0
  234. package/dist/web/assets/terminal-tab-CTe2polt.js.gz +0 -0
  235. package/dist/web/assets/{tool-cards-K0r_yxI0.js → tool-cards-rxLXbBSm.js} +5 -5
  236. package/dist/web/assets/tool-cards-rxLXbBSm.js.br +0 -0
  237. package/dist/web/assets/tool-cards-rxLXbBSm.js.gz +0 -0
  238. package/dist/web/assets/use-h264-canvas-decoder-BfO9QrlH.js +1 -0
  239. package/dist/web/assets/use-h264-canvas-decoder-BfO9QrlH.js.br +0 -0
  240. package/dist/web/assets/use-h264-canvas-decoder-BfO9QrlH.js.gz +0 -0
  241. package/dist/web/assets/{use-inline-blame-BK23icsE.js → use-inline-blame-CxZ_QWzS.js} +3 -3
  242. package/dist/web/assets/use-inline-blame-CxZ_QWzS.js.br +0 -0
  243. package/dist/web/assets/use-inline-blame-CxZ_QWzS.js.gz +0 -0
  244. package/dist/web/assets/{use-lsp-CRdCPCKW.js → use-lsp-gCNE5A9K.js} +1 -1
  245. package/dist/web/assets/use-lsp-gCNE5A9K.js.br +0 -0
  246. package/dist/web/assets/use-lsp-gCNE5A9K.js.gz +0 -0
  247. package/dist/web/assets/{use-monaco-theme-CpXT2uh3.js → use-monaco-theme-DvNGcCvz.js} +1 -1
  248. package/dist/web/assets/{use-process-kill-BWYX03KF.js → use-process-kill-Cbiu1mbY.js} +1 -1
  249. package/dist/web/assets/use-process-kill-Cbiu1mbY.js.br +0 -0
  250. package/dist/web/assets/use-process-kill-Cbiu1mbY.js.gz +0 -0
  251. package/dist/web/assets/{use-websocket-CiWQKhHr.js → use-websocket-AC7EG1LK.js} +1 -1
  252. package/dist/web/assets/video-preview-C2Y61WUE.js +1 -0
  253. package/dist/web/assets/video-preview-C2Y61WUE.js.br +0 -0
  254. package/dist/web/assets/video-preview-C2Y61WUE.js.gz +0 -0
  255. package/dist/web/assets/{voice-settings-section-Ceihggqf.js → voice-settings-section-DCXZeLYI.js} +1 -1
  256. package/dist/web/assets/voice-settings-section-DCXZeLYI.js.br +0 -0
  257. package/dist/web/assets/voice-settings-section-DCXZeLYI.js.gz +0 -0
  258. package/dist/web/assets/{window-pip-registry-mDgwpAwP.js → window-pip-registry-9Y9LQQYS.js} +1 -1
  259. package/dist/web/index.html +50 -7
  260. package/dist/web/index.html.br +0 -0
  261. package/dist/web/index.html.gz +0 -0
  262. package/dist/web/sw.js +1 -1
  263. package/dist/web/sw.js.br +0 -0
  264. package/dist/web/sw.js.gz +0 -0
  265. package/docs/architecture/android-emulator-phase0-report-260921.md +310 -0
  266. package/docs/architecture/android-emulator-phase2-report-260921.md +163 -0
  267. package/docs/architecture/android-emulator-phase3-report-260921.md +203 -0
  268. package/docs/architecture/android-emulator-phase4-report-260921.md +191 -0
  269. package/docs/architecture/android-emulator-plan-260920.md +376 -0
  270. package/docs/architecture/plugins-and-tracing.md +48 -7
  271. package/package.json +4 -2
  272. package/scripts/gen-android-proto.ts +37 -0
  273. package/scripts/release.sh +3 -1
  274. package/src/cli/commands/chat-cmd.ts +2 -2
  275. package/src/providers/claude-agent-sdk-query-options.ts +28 -0
  276. package/src/providers/claude-agent-sdk.ts +153 -71
  277. package/src/providers/claude-warm-spare.ts +176 -0
  278. package/src/providers/provider.interface.ts +1 -0
  279. package/src/server/index.ts +53 -0
  280. package/src/server/middleware/gzip-json.ts +13 -1
  281. package/src/server/routes/android.ts +616 -0
  282. package/src/server/routes/chat.ts +31 -0
  283. package/src/server/routes/files.ts +52 -5
  284. package/src/server/routes/remote-desktop.ts +95 -2
  285. package/src/server/routes/trace.ts +66 -0
  286. package/src/server/ws/android.ts +101 -0
  287. package/src/server/ws/chat.ts +12 -17
  288. package/src/server/ws/global.ts +13 -1
  289. package/src/server/ws/remote-desktop.ts +6 -1
  290. package/src/services/android/android-apk.ts +387 -0
  291. package/src/services/android/android-capabilities.ts +154 -0
  292. package/src/services/android/android-flag.ts +12 -0
  293. package/src/services/android/android-grpc.ts +124 -0
  294. package/src/services/android/android-input.ts +129 -0
  295. package/src/services/android/android-logcat.ts +169 -0
  296. package/src/services/android/android-nonce.ts +50 -0
  297. package/src/services/android/android-operations.ts +124 -0
  298. package/src/services/android/android-screenshot.ts +53 -0
  299. package/src/services/android/android-session.ts +510 -0
  300. package/src/services/android/android-video.ts +495 -0
  301. package/src/services/android/avd-list.ts +131 -0
  302. package/src/services/android/avd-manager.ts +397 -0
  303. package/src/services/android/device-registry.ts +223 -0
  304. package/src/services/android/emulator-discovery.ts +160 -0
  305. package/src/services/android/emulator-launcher.ts +290 -0
  306. package/src/services/android/proto/emulator-controller-descriptor.ts +10 -0
  307. package/src/services/android/proto/emulator_controller.proto +1791 -0
  308. package/src/services/android/sdk-discovery.ts +178 -0
  309. package/src/services/android/system-images.ts +124 -0
  310. package/src/services/chat.service.ts +81 -4
  311. package/src/services/config.service.ts +1 -1
  312. package/src/services/file-filter.service.ts +2 -61
  313. package/src/services/file-glob.ts +67 -0
  314. package/src/services/file-index/file-index-runner.ts +169 -0
  315. package/src/services/file-index/file-index-worker.ts +73 -0
  316. package/src/services/file-index/index-search.ts +152 -0
  317. package/src/services/file-index/index-walk.ts +109 -0
  318. package/src/services/file-list-index.service.ts +189 -70
  319. package/src/services/file-watcher/linux-inotify.ts +238 -0
  320. package/src/services/file-watcher/watch-tree.ts +37 -6
  321. package/src/services/file-watcher.service.ts +40 -8
  322. package/src/services/file.service.ts +17 -6
  323. package/src/services/group-chat/group-chat.service.ts +1 -1
  324. package/src/services/jira-debug-session.service.ts +1 -1
  325. package/src/services/ppmbot/ppmbot-delegation.ts +1 -1
  326. package/src/services/ppmbot/ppmbot-service.ts +1 -1
  327. package/src/services/project-content-search.service.ts +45 -11
  328. package/src/services/proxy-agent-turn.ts +7 -3
  329. package/src/services/remote-desktop/mediamtx-catalog.ts +89 -0
  330. package/src/services/remote-desktop/mediamtx-config.ts +91 -0
  331. package/src/services/remote-desktop/mediamtx-install.service.ts +191 -0
  332. package/src/services/remote-desktop/mediamtx-paths.ts +51 -0
  333. package/src/services/remote-desktop/mediamtx-process.ts +152 -0
  334. package/src/services/remote-desktop/remote-desktop-capture-wayland.ts +312 -0
  335. package/src/services/remote-desktop/remote-desktop-capture.ts +60 -4
  336. package/src/services/remote-desktop/remote-desktop-portal.ts +529 -0
  337. package/src/services/remote-desktop/remote-desktop-requirements.ts +56 -20
  338. package/src/services/remote-desktop/remote-desktop-session.ts +77 -3
  339. package/src/services/remote-desktop/remote-desktop-whep-registry.ts +36 -0
  340. package/src/services/scheduler-runner.ts +2 -3
  341. package/src/services/session-trace/browser-trace-ingest.ts +131 -0
  342. package/src/services/session-trace/session-trace-cleanup.ts +65 -0
  343. package/src/services/session-trace/session-trace-db.ts +89 -0
  344. package/src/services/session-trace/session-trace-store.ts +132 -0
  345. package/src/services/session-trace/trace-coalescer.ts +86 -0
  346. package/src/services/session-trace/trace-recorder.ts +304 -0
  347. package/src/services/session-trace/trace-writer.ts +83 -0
  348. package/src/shared/android-avd.ts +53 -0
  349. package/src/shared/android-protocol.ts +184 -0
  350. package/src/shared/file-index-limits.ts +17 -0
  351. package/src/shared/session-trace.ts +61 -0
  352. package/src/types/chat.ts +17 -0
  353. package/src/types/config.ts +27 -0
  354. package/src/types/project.ts +9 -0
  355. package/src/web/app.tsx +5 -0
  356. package/src/web/components/android/android-apk-upload.ts +75 -0
  357. package/src/web/components/android/android-avd-create-dialog.tsx +305 -0
  358. package/src/web/components/android/android-avd-destroy-dialog.tsx +151 -0
  359. package/src/web/components/android/android-clipboard-panel.tsx +94 -0
  360. package/src/web/components/android/android-controls.tsx +128 -0
  361. package/src/web/components/android/android-coords.ts +99 -0
  362. package/src/web/components/android/android-device-list.tsx +329 -0
  363. package/src/web/components/android/android-install-panel.tsx +280 -0
  364. package/src/web/components/android/android-log-filter.ts +75 -0
  365. package/src/web/components/android/android-logcat-panel.tsx +214 -0
  366. package/src/web/components/android/android-screenshot-download.ts +56 -0
  367. package/src/web/components/android/android-tab.tsx +66 -0
  368. package/src/web/components/android/android-tools-drawer.tsx +76 -0
  369. package/src/web/components/android/android-viewer.tsx +200 -0
  370. package/src/web/components/android/android-ws-url.ts +21 -0
  371. package/src/web/components/android/open-android.ts +21 -0
  372. package/src/web/components/android/use-android-available.ts +29 -0
  373. package/src/web/components/android/use-android-keyboard.ts +111 -0
  374. package/src/web/components/android/use-android-session.ts +277 -0
  375. package/src/web/components/android/use-android-touch.ts +123 -0
  376. package/src/web/components/chat/chat-tab.tsx +28 -2
  377. package/src/web/components/chat/message-input.tsx +3 -5
  378. package/src/web/components/editor/compare-picker.tsx +12 -2
  379. package/src/web/components/explorer/file-tree.tsx +6 -7
  380. package/src/web/components/explorer/use-tree-row-dnd.ts +0 -2
  381. package/src/web/components/floating-window/window-content-registry.ts +8 -8
  382. package/src/web/components/layout/command-palette.tsx +31 -9
  383. package/src/web/components/layout/mobile-drawer.tsx +8 -1
  384. package/src/web/components/layout/nav-section-rail.tsx +8 -1
  385. package/src/web/components/layout/reparenting-tab.tsx +2 -2
  386. package/src/web/components/layout/tab-content.tsx +5 -0
  387. package/src/web/components/layout/tab-pool.tsx +31 -20
  388. package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +23 -1
  389. package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +64 -6
  390. package/src/web/components/remote-desktop/use-webrtc-canvas-video.ts +255 -0
  391. package/src/web/components/root-error-boundary.tsx +5 -0
  392. package/src/web/components/settings/remote-desktop-settings-section.tsx +176 -0
  393. package/src/web/components/settings/settings-categories.ts +3 -0
  394. package/src/web/components/settings/settings-section-content.tsx +25 -18
  395. package/src/web/hooks/use-chat-prewarm.ts +53 -0
  396. package/src/web/hooks/use-chat.ts +18 -4
  397. package/src/web/hooks/use-file-index-invalidation.ts +7 -5
  398. package/src/web/hooks/use-global-events.ts +12 -1
  399. package/src/web/hooks/use-remote-file-search.ts +39 -0
  400. package/src/web/hooks/use-ui-preload.ts +38 -0
  401. package/src/web/index.html +43 -0
  402. package/src/web/lib/chunk-recovery.ts +5 -1
  403. package/src/web/lib/device-id.ts +39 -0
  404. package/src/web/lib/feature-badges.ts +2 -0
  405. package/src/web/lib/lazy-with-preload.tsx +43 -0
  406. package/src/web/lib/preload-ui.ts +60 -0
  407. package/src/web/lib/score-file-search.ts +27 -14
  408. package/src/web/lib/tab-type-icons.ts +2 -1
  409. package/src/web/lib/trace-boot.ts +8 -0
  410. package/src/web/lib/trace-client.ts +516 -0
  411. package/src/web/main.tsx +12 -0
  412. package/src/web/stores/file-store.ts +124 -18
  413. package/src/web/stores/panel-utils.ts +3 -0
  414. package/src/web/stores/tab-store.ts +2 -1
  415. package/src/web/stores/window-panel-persistence.ts +3 -0
  416. package/vite.config.ts +3 -0
  417. package/dist/web/assets/accounts-settings-section-DEIm-W-D.js +0 -1
  418. package/dist/web/assets/accounts-settings-section-DEIm-W-D.js.br +0 -0
  419. package/dist/web/assets/accounts-settings-section-DEIm-W-D.js.gz +0 -0
  420. package/dist/web/assets/agent-session-sheet-CBMsnl_p.js.br +0 -0
  421. package/dist/web/assets/agent-session-sheet-CBMsnl_p.js.gz +0 -0
  422. package/dist/web/assets/agent-session-window-content-D9C9FA7E.js.br +0 -0
  423. package/dist/web/assets/agent-session-window-content-D9C9FA7E.js.gz +0 -0
  424. package/dist/web/assets/ai-resource-editor-DhUVgDEG.js.br +0 -0
  425. package/dist/web/assets/ai-resource-editor-DhUVgDEG.js.gz +0 -0
  426. package/dist/web/assets/ai-settings-section-CETpSpYd.js.br +0 -0
  427. package/dist/web/assets/ai-settings-section-CETpSpYd.js.gz +0 -0
  428. package/dist/web/assets/appearance-settings-section-BJUiLUb7.js.br +0 -0
  429. package/dist/web/assets/appearance-settings-section-BJUiLUb7.js.gz +0 -0
  430. package/dist/web/assets/apps-panel-1VElRgV3.js.br +0 -0
  431. package/dist/web/assets/apps-panel-1VElRgV3.js.gz +0 -0
  432. package/dist/web/assets/audio-preview-DYkyHu5X.js.br +0 -0
  433. package/dist/web/assets/audio-preview-DYkyHu5X.js.gz +0 -0
  434. package/dist/web/assets/branch-review-tab-DhTMSpSU.js +0 -1
  435. package/dist/web/assets/branch-review-tab-DhTMSpSU.js.br +0 -0
  436. package/dist/web/assets/branch-review-tab-DhTMSpSU.js.gz +0 -0
  437. package/dist/web/assets/chat-tab-Cg3BKugo.js +0 -15
  438. package/dist/web/assets/chat-tab-Cg3BKugo.js.br +0 -0
  439. package/dist/web/assets/chat-tab-Cg3BKugo.js.gz +0 -0
  440. package/dist/web/assets/chat-tab-tbUDK__Y.js +0 -1
  441. package/dist/web/assets/chat-tab-tbUDK__Y.js.br +0 -0
  442. package/dist/web/assets/chat-tab-tbUDK__Y.js.gz +0 -0
  443. package/dist/web/assets/code-editor-6ZiQehbr.js +0 -7
  444. package/dist/web/assets/code-editor-6ZiQehbr.js.br +0 -0
  445. package/dist/web/assets/code-editor-6ZiQehbr.js.gz +0 -0
  446. package/dist/web/assets/codex-reset-credit-button-Fr45FUxi.js.br +0 -0
  447. package/dist/web/assets/codex-reset-credit-button-Fr45FUxi.js.gz +0 -0
  448. package/dist/web/assets/conflict-editor-B-wdjSXI.js.br +0 -0
  449. package/dist/web/assets/conflict-editor-B-wdjSXI.js.gz +0 -0
  450. package/dist/web/assets/csv-preview-B6RodObB.js.br +0 -0
  451. package/dist/web/assets/csv-preview-B6RodObB.js.gz +0 -0
  452. package/dist/web/assets/database-viewer-CdPBUQPY.js.br +0 -0
  453. package/dist/web/assets/database-viewer-CdPBUQPY.js.gz +0 -0
  454. package/dist/web/assets/design-settings-section-BpCpaBwL.js.br +0 -0
  455. package/dist/web/assets/design-settings-section-BpCpaBwL.js.gz +0 -0
  456. package/dist/web/assets/design-skill-suggestion-DkT2E2IV.js.br +0 -0
  457. package/dist/web/assets/design-skill-suggestion-DkT2E2IV.js.gz +0 -0
  458. package/dist/web/assets/design-tab-BZb9eemI.js.br +0 -0
  459. package/dist/web/assets/design-tab-BZb9eemI.js.gz +0 -0
  460. package/dist/web/assets/designs-sidebar-panel-CF4tp6j-.js.br +0 -0
  461. package/dist/web/assets/designs-sidebar-panel-CF4tp6j-.js.gz +0 -0
  462. package/dist/web/assets/diff-viewer-BqEqwSw6.js.br +0 -0
  463. package/dist/web/assets/diff-viewer-BqEqwSw6.js.gz +0 -0
  464. package/dist/web/assets/edit-diff-preview-DD4HayXY.js.br +0 -0
  465. package/dist/web/assets/edit-diff-preview-DD4HayXY.js.gz +0 -0
  466. package/dist/web/assets/editor-language-service-CkA5sEdV.js.br +0 -0
  467. package/dist/web/assets/editor-language-service-CkA5sEdV.js.gz +0 -0
  468. package/dist/web/assets/explorer-body-Ds7oKuz7.js +0 -2
  469. package/dist/web/assets/explorer-body-Ds7oKuz7.js.br +0 -0
  470. package/dist/web/assets/explorer-body-Ds7oKuz7.js.gz +0 -0
  471. package/dist/web/assets/explorer-window-content-CRJ2eMZ4.js.br +0 -0
  472. package/dist/web/assets/explorer-window-content-CRJ2eMZ4.js.gz +0 -0
  473. package/dist/web/assets/extension-manager-section-DwQTdCII.js.br +0 -0
  474. package/dist/web/assets/extension-manager-section-DwQTdCII.js.gz +0 -0
  475. package/dist/web/assets/extension-webview-CMq-VTLu.js.br +0 -0
  476. package/dist/web/assets/extension-webview-CMq-VTLu.js.gz +0 -0
  477. package/dist/web/assets/file-store-DY1_0nzY.js +0 -1
  478. package/dist/web/assets/file-store-DY1_0nzY.js.br +0 -0
  479. package/dist/web/assets/file-store-DY1_0nzY.js.gz +0 -0
  480. package/dist/web/assets/files-settings-section-B2lcyh-6.js.br +0 -0
  481. package/dist/web/assets/files-settings-section-B2lcyh-6.js.gz +0 -0
  482. package/dist/web/assets/general-settings-section-Doepm2sH.js.br +0 -0
  483. package/dist/web/assets/general-settings-section-Doepm2sH.js.gz +0 -0
  484. package/dist/web/assets/git-log-panel-CJakNMDc.js.br +0 -0
  485. package/dist/web/assets/git-log-panel-CJakNMDc.js.gz +0 -0
  486. package/dist/web/assets/glide-data-grid-C_SeOhSq.js.br +0 -0
  487. package/dist/web/assets/glide-data-grid-C_SeOhSq.js.gz +0 -0
  488. package/dist/web/assets/group-chat-tab--by1xCQW.js +0 -1
  489. package/dist/web/assets/group-chat-tab--by1xCQW.js.br +0 -0
  490. package/dist/web/assets/group-chat-tab--by1xCQW.js.gz +0 -0
  491. package/dist/web/assets/index-DhWjx5iR.js +0 -52
  492. package/dist/web/assets/index-DhWjx5iR.js.br +0 -0
  493. package/dist/web/assets/index-DhWjx5iR.js.gz +0 -0
  494. package/dist/web/assets/index-oIvYSKTT.css +0 -1
  495. package/dist/web/assets/index-oIvYSKTT.css.br +0 -0
  496. package/dist/web/assets/index-oIvYSKTT.css.gz +0 -0
  497. package/dist/web/assets/keybindings-store-30LYZU0l.js +0 -1
  498. package/dist/web/assets/keyboard-shortcuts-section-BgZe3QDr.js.br +0 -0
  499. package/dist/web/assets/keyboard-shortcuts-section-BgZe3QDr.js.gz +0 -0
  500. package/dist/web/assets/language-servers-section-CCMzgOaE.js.br +0 -0
  501. package/dist/web/assets/language-servers-section-CCMzgOaE.js.gz +0 -0
  502. package/dist/web/assets/lsp-client-B10suuaZ.js.br +0 -0
  503. package/dist/web/assets/lsp-client-B10suuaZ.js.gz +0 -0
  504. package/dist/web/assets/markdown-renderer-AHHFS3jc.js.br +0 -0
  505. package/dist/web/assets/markdown-renderer-AHHFS3jc.js.gz +0 -0
  506. package/dist/web/assets/markdown-renderer-DxnXtBoj.js +0 -1
  507. package/dist/web/assets/mobile-explorer-sheet-CqCsLWSr.js.br +0 -0
  508. package/dist/web/assets/mobile-explorer-sheet-CqCsLWSr.js.gz +0 -0
  509. package/dist/web/assets/new-design-dialog-B9tgnQXq.js +0 -1
  510. package/dist/web/assets/new-design-dialog-B9tgnQXq.js.br +0 -0
  511. package/dist/web/assets/new-design-dialog-B9tgnQXq.js.gz +0 -0
  512. package/dist/web/assets/notification-store-HUVzO_bp.js +0 -1
  513. package/dist/web/assets/notifications-settings-section-CXgzH_bK.js.br +0 -0
  514. package/dist/web/assets/notifications-settings-section-CXgzH_bK.js.gz +0 -0
  515. package/dist/web/assets/overview-panel-BYB8mgRE.js.br +0 -0
  516. package/dist/web/assets/overview-panel-BYB8mgRE.js.gz +0 -0
  517. package/dist/web/assets/panel-store-CWHPmHwj.js +0 -1
  518. package/dist/web/assets/panel-store-CWHPmHwj.js.br +0 -0
  519. package/dist/web/assets/panel-store-CWHPmHwj.js.gz +0 -0
  520. package/dist/web/assets/performance-panel-Dpuq3g0W.js.br +0 -0
  521. package/dist/web/assets/performance-panel-Dpuq3g0W.js.gz +0 -0
  522. package/dist/web/assets/postgres-viewer-DW0Gkmow.js.br +0 -0
  523. package/dist/web/assets/postgres-viewer-DW0Gkmow.js.gz +0 -0
  524. package/dist/web/assets/ppmbot-settings-section-DFtlGoW6.js.br +0 -0
  525. package/dist/web/assets/ppmbot-settings-section-DFtlGoW6.js.gz +0 -0
  526. package/dist/web/assets/pptx-export-CNyG32Xr.js.br +0 -0
  527. package/dist/web/assets/pptx-export-CNyG32Xr.js.gz +0 -0
  528. package/dist/web/assets/problems-panel-V5zqpuXq.js.br +0 -0
  529. package/dist/web/assets/problems-panel-V5zqpuXq.js.gz +0 -0
  530. package/dist/web/assets/process-table-DAj46HIl.js +0 -1
  531. package/dist/web/assets/process-table-DAj46HIl.js.br +0 -0
  532. package/dist/web/assets/process-table-DAj46HIl.js.gz +0 -0
  533. package/dist/web/assets/proxy-settings-section-B7AVlTfY.js.br +0 -0
  534. package/dist/web/assets/proxy-settings-section-B7AVlTfY.js.gz +0 -0
  535. package/dist/web/assets/query-audit-section-gHmRfWKr.js.br +0 -0
  536. package/dist/web/assets/query-audit-section-gHmRfWKr.js.gz +0 -0
  537. package/dist/web/assets/remote-desktop-clipboard-notice-Cgoznh8L.js +0 -1
  538. package/dist/web/assets/remote-desktop-clipboard-notice-Cgoznh8L.js.br +0 -0
  539. package/dist/web/assets/remote-desktop-clipboard-notice-Cgoznh8L.js.gz +0 -0
  540. package/dist/web/assets/remote-desktop-mobile-sheet-X12QbPhO.js +0 -2
  541. package/dist/web/assets/remote-desktop-mobile-sheet-X12QbPhO.js.br +0 -0
  542. package/dist/web/assets/remote-desktop-mobile-sheet-X12QbPhO.js.gz +0 -0
  543. package/dist/web/assets/remote-desktop-mobile-view-DsSHawSK.js +0 -1
  544. package/dist/web/assets/remote-desktop-mobile-view-DsSHawSK.js.br +0 -0
  545. package/dist/web/assets/remote-desktop-mobile-view-DsSHawSK.js.gz +0 -0
  546. package/dist/web/assets/remote-desktop-readiness-gate-CHiQEd9Q.js +0 -1
  547. package/dist/web/assets/remote-desktop-readiness-gate-CHiQEd9Q.js.br +0 -0
  548. package/dist/web/assets/remote-desktop-readiness-gate-CHiQEd9Q.js.gz +0 -0
  549. package/dist/web/assets/remote-desktop-window-content-BnznIby3.js +0 -1
  550. package/dist/web/assets/remote-desktop-window-content-BnznIby3.js.br +0 -0
  551. package/dist/web/assets/remote-desktop-window-content-BnznIby3.js.gz +0 -0
  552. package/dist/web/assets/schedules-settings-section-sYDAsBMO.js.br +0 -0
  553. package/dist/web/assets/schedules-settings-section-sYDAsBMO.js.gz +0 -0
  554. package/dist/web/assets/send-to-chat-CCdOrKc7.js.br +0 -0
  555. package/dist/web/assets/send-to-chat-CCdOrKc7.js.gz +0 -0
  556. package/dist/web/assets/services-panel-CKa2aP1d.js.br +0 -0
  557. package/dist/web/assets/services-panel-CKa2aP1d.js.gz +0 -0
  558. package/dist/web/assets/settings-body-CEIF8lkl.js +0 -2
  559. package/dist/web/assets/settings-body-CEIF8lkl.js.br +0 -0
  560. package/dist/web/assets/settings-body-CEIF8lkl.js.gz +0 -0
  561. package/dist/web/assets/settings-store-DfuiLq_9.js.br +0 -0
  562. package/dist/web/assets/settings-store-DfuiLq_9.js.gz +0 -0
  563. package/dist/web/assets/settings-tab-Du5AUPni.js +0 -1
  564. package/dist/web/assets/settings-window-content-BTflfLez.js +0 -1
  565. package/dist/web/assets/slash-command-picker-CLevA_Gf.js.br +0 -0
  566. package/dist/web/assets/slash-command-picker-CLevA_Gf.js.gz +0 -0
  567. package/dist/web/assets/sql-query-editor-Db8b0jEr.js.br +0 -0
  568. package/dist/web/assets/sql-query-editor-Db8b0jEr.js.gz +0 -0
  569. package/dist/web/assets/sqlite-viewer-VzYLMqkD.js.br +0 -0
  570. package/dist/web/assets/sqlite-viewer-VzYLMqkD.js.gz +0 -0
  571. package/dist/web/assets/system-monitor-body-BR60W_mG.js.br +0 -0
  572. package/dist/web/assets/system-monitor-body-BR60W_mG.js.gz +0 -0
  573. package/dist/web/assets/system-monitor-tab-BYpantE2.js +0 -1
  574. package/dist/web/assets/tab-host-window-content-BL61SB5m.js.br +0 -0
  575. package/dist/web/assets/tab-host-window-content-BL61SB5m.js.gz +0 -0
  576. package/dist/web/assets/terminal-tab-B4y6at4h.js.br +0 -0
  577. package/dist/web/assets/terminal-tab-B4y6at4h.js.gz +0 -0
  578. package/dist/web/assets/tool-cards-K0r_yxI0.js.br +0 -0
  579. package/dist/web/assets/tool-cards-K0r_yxI0.js.gz +0 -0
  580. package/dist/web/assets/use-inline-blame-BK23icsE.js.br +0 -0
  581. package/dist/web/assets/use-inline-blame-BK23icsE.js.gz +0 -0
  582. package/dist/web/assets/use-lsp-CRdCPCKW.js.br +0 -0
  583. package/dist/web/assets/use-lsp-CRdCPCKW.js.gz +0 -0
  584. package/dist/web/assets/use-process-kill-BWYX03KF.js.br +0 -0
  585. package/dist/web/assets/use-process-kill-BWYX03KF.js.gz +0 -0
  586. package/dist/web/assets/video-preview-DugVkMB7.js +0 -1
  587. package/dist/web/assets/video-preview-DugVkMB7.js.br +0 -0
  588. package/dist/web/assets/video-preview-DugVkMB7.js.gz +0 -0
  589. package/dist/web/assets/voice-settings-section-Ceihggqf.js.br +0 -0
  590. package/dist/web/assets/voice-settings-section-Ceihggqf.js.gz +0 -0
  591. /package/dist/web/assets/{__vite-browser-external-B_XMEguz.js → __vite-browser-external-C5mXParC.js} +0 -0
  592. /package/dist/web/assets/{agent-session-sheet-store-DO682WSg.js → agent-session-sheet-store-_OeezCXB.js} +0 -0
  593. /package/dist/web/assets/{api-settings-CpO8SPk9.js → api-settings-Cc_CpfmC.js} +0 -0
  594. /package/dist/web/assets/{api-settings-CpO8SPk9.js.br → api-settings-Cc_CpfmC.js.br} +0 -0
  595. /package/dist/web/assets/{api-settings-CpO8SPk9.js.gz → api-settings-Cc_CpfmC.js.gz} +0 -0
  596. /package/dist/web/assets/{api-themes-CJipDH_l.js → api-themes-BtMBSjuM.js} +0 -0
  597. /package/dist/web/assets/{can-open-in-ppm-Bw_xiOXX.js → can-open-in-ppm-DeE2-kd5.js} +0 -0
  598. /package/dist/web/assets/{can-open-in-ppm-Bw_xiOXX.js.br → can-open-in-ppm-DeE2-kd5.js.br} +0 -0
  599. /package/dist/web/assets/{can-open-in-ppm-Bw_xiOXX.js.gz → can-open-in-ppm-DeE2-kd5.js.gz} +0 -0
  600. /package/dist/web/assets/{csv-parser-DcRWxcNV.js → csv-parser-CoxPdyLw.js} +0 -0
  601. /package/dist/web/assets/{csv-parser-DcRWxcNV.js.br → csv-parser-CoxPdyLw.js.br} +0 -0
  602. /package/dist/web/assets/{csv-parser-DcRWxcNV.js.gz → csv-parser-CoxPdyLw.js.gz} +0 -0
  603. /package/dist/web/assets/{design-export-client-CHffN3DM.js → design-export-client-BfdZB7xK.js} +0 -0
  604. /package/dist/web/assets/{design-export-client-CHffN3DM.js.br → design-export-client-BfdZB7xK.js.br} +0 -0
  605. /package/dist/web/assets/{design-export-client-CHffN3DM.js.gz → design-export-client-BfdZB7xK.js.gz} +0 -0
  606. /package/dist/web/assets/{design-responsive-dialog-DSCpv6O1.js → design-responsive-dialog-BiYy3ibS.js} +0 -0
  607. /package/dist/web/assets/{design-responsive-dialog-DSCpv6O1.js.br → design-responsive-dialog-BiYy3ibS.js.br} +0 -0
  608. /package/dist/web/assets/{design-responsive-dialog-DSCpv6O1.js.gz → design-responsive-dialog-BiYy3ibS.js.gz} +0 -0
  609. /package/dist/web/assets/{design-system-status-label-B_0I_3a8.js → design-system-status-label-ekiLyjKn.js} +0 -0
  610. /package/dist/web/assets/{docx-preview-B3eJyY2y.js → docx-preview-Dt6RQpfA.js} +0 -0
  611. /package/dist/web/assets/{docx-preview-B3eJyY2y.js.br → docx-preview-Dt6RQpfA.js.br} +0 -0
  612. /package/dist/web/assets/{docx-preview-B3eJyY2y.js.gz → docx-preview-Dt6RQpfA.js.gz} +0 -0
  613. /package/dist/web/assets/{editor-font-BNwo7zo5.js → editor-font-CBszn6vt.js} +0 -0
  614. /package/dist/web/assets/{format-bytes-CRZfxO2J.js → format-bytes-FQmpZ25A.js} +0 -0
  615. /package/dist/web/assets/{image-gallery-DC3gRAqk.js → image-gallery-B16hO6Ab.js} +0 -0
  616. /package/dist/web/assets/{image-preview-geometry-DN3DZQp0.js → image-preview-geometry-D4Ihz4UV.js} +0 -0
  617. /package/dist/web/assets/{lib-B9k5tn9s.js → lib-6APiJkU3.js} +0 -0
  618. /package/dist/web/assets/{lib-B9k5tn9s.js.br → lib-6APiJkU3.js.br} +0 -0
  619. /package/dist/web/assets/{lib-B9k5tn9s.js.gz → lib-6APiJkU3.js.gz} +0 -0
  620. /package/dist/web/assets/{libesm-5f7T4AHA.js → libesm-5wREk7JN.js} +0 -0
  621. /package/dist/web/assets/{libesm-5f7T4AHA.js.br → libesm-5wREk7JN.js.br} +0 -0
  622. /package/dist/web/assets/{libesm-5f7T4AHA.js.gz → libesm-5wREk7JN.js.gz} +0 -0
  623. /package/dist/web/assets/{media-url-CCGOIxzS.js → media-url-CrT-qulZ.js} +0 -0
  624. /package/dist/web/assets/{metric-chart-canvas-CjFbZuhk.js → metric-chart-canvas-D7f-LQ8x.js} +0 -0
  625. /package/dist/web/assets/{metric-chart-canvas-CjFbZuhk.js.br → metric-chart-canvas-D7f-LQ8x.js.br} +0 -0
  626. /package/dist/web/assets/{metric-chart-canvas-CjFbZuhk.js.gz → metric-chart-canvas-D7f-LQ8x.js.gz} +0 -0
  627. /package/dist/web/assets/{monaco-dotenv-language-Bwq3gQsi.js → monaco-dotenv-language-DbSss7YR.js} +0 -0
  628. /package/dist/web/assets/{onboarding-entry-CuH1Kqwu.js → onboarding-entry-CGFdGLLP.js} +0 -0
  629. /package/dist/web/assets/{onboarding-entry-CuH1Kqwu.js.br → onboarding-entry-CGFdGLLP.js.br} +0 -0
  630. /package/dist/web/assets/{onboarding-entry-CuH1Kqwu.js.gz → onboarding-entry-CGFdGLLP.js.gz} +0 -0
  631. /package/dist/web/assets/{pdf-preview-X8bdpfE9.js → pdf-preview-B8A4T-ES.js} +0 -0
  632. /package/dist/web/assets/{pdf-preview-X8bdpfE9.js.br → pdf-preview-B8A4T-ES.js.br} +0 -0
  633. /package/dist/web/assets/{pdf-preview-X8bdpfE9.js.gz → pdf-preview-B8A4T-ES.js.gz} +0 -0
  634. /package/dist/web/assets/{separator-DMu7VFQa.js → separator-dHj3Km--.js} +0 -0
  635. /package/dist/web/assets/{sortable-header-CipIP7aH.js → sortable-header-BOa8o7NJ.js} +0 -0
  636. /package/dist/web/assets/{sortable-header-CipIP7aH.js.br → sortable-header-BOa8o7NJ.js.br} +0 -0
  637. /package/dist/web/assets/{sortable-header-CipIP7aH.js.gz → sortable-header-BOa8o7NJ.js.gz} +0 -0
  638. /package/dist/web/assets/{use-blob-url-DjuRQWnR.js → use-blob-url-BC69n15x.js} +0 -0
  639. /package/dist/web/assets/{vendor-xterm-Be4L1NHd.js → vendor-xterm-CfasyE0m.js} +0 -0
  640. /package/dist/web/assets/{vendor-xterm-Be4L1NHd.js.br → vendor-xterm-CfasyE0m.js.br} +0 -0
  641. /package/dist/web/assets/{vendor-xterm-Be4L1NHd.js.gz → vendor-xterm-CfasyE0m.js.gz} +0 -0
@@ -5,6 +5,8 @@ import { existsSync, mkdirSync } from "node:fs";
5
5
  import { fileService, SecurityError, NotFoundError, ValidationError } from "../../services/file.service.ts";
6
6
  import { readSystemFileSync } from "../../services/fs-browse.service.ts";
7
7
  import { ok, err } from "../../types/api.ts";
8
+ import type { FileIndexTooLarge } from "../../types/project.ts";
9
+ import { REMOTE_FILE_SEARCH_LIMIT } from "../../shared/file-index-limits.ts";
8
10
  import { errorStatus } from "../helpers/error-status.ts";
9
11
  import { rangeFileResponse } from "../helpers/range-file-response.ts";
10
12
  import { handleMediaProbe, handleMediaTranscode, handleMediaTranscodeStop } from "../helpers/media-route-handlers.ts";
@@ -63,15 +65,60 @@ fileRoutes.post("/list-batch", async (c) => {
63
65
  });
64
66
 
65
67
  /**
66
- * GET /files/index
68
+ * A project's first walk can outlast Bun's 10 s idle timeout — nxsys-workspace took 7.6 s on an
69
+ * idle server and more on a busy one — and Bun then cuts the request off with the walk nearly
70
+ * done. The walk no longer stops the server, so waiting is safe; 30 s is as long as the browser
71
+ * waits for headers. `c.env` is the Bun server that `app.fetch(req, server)` passed.
72
+ */
73
+ function outlastFirstWalk(c: { env: unknown; req: { raw: Request } }): void {
74
+ (c.env as { timeout?: (request: Request, seconds: number) => void } | undefined)?.timeout?.(c.req.raw, 30);
75
+ }
76
+
77
+ /** Most entries a `/files/index/search` answers with. */
78
+ const MAX_INDEX_SEARCH_LIMIT = 500;
79
+
80
+ /**
81
+ * GET /files/index?max=<n>
67
82
  * Returns flat array of all project files {path, name} for palette/search.
68
- * Result is cached; cache is invalidated on file change events.
83
+ * Result is cached; a file change marks it stale, and the stale list is served while it rebuilds.
84
+ * With `max`, a project holding more entries than that answers `FileIndexTooLarge` instead of the
85
+ * list, to be searched with `/files/index/search`.
69
86
  */
70
- fileRoutes.get("/index", (c) => {
87
+ fileRoutes.get("/index", async (c) => {
71
88
  try {
72
89
  const projectPath = c.get("projectPath");
73
- const entries = fileService.buildIndex(projectPath);
74
- return c.json(ok(entries));
90
+ outlastFirstWalk(c);
91
+ // The body is serialised and gzipped once per walk, on the index worker: nxsys-workspace's
92
+ // is 22 MB, which cost ~120 ms of the event loop to serialise and compress per download.
93
+ const build = await fileService.buildIndex(projectPath);
94
+ const max = Number(c.req.query("max"));
95
+ if (max > 0 && build.count > max) return c.json(ok<FileIndexTooLarge>({ tooLarge: true, count: build.count }));
96
+ const gzip = (c.req.header("Accept-Encoding") ?? "").includes("gzip");
97
+ return new Response(gzip ? build.gzip : build.json, {
98
+ headers: {
99
+ "Content-Type": "application/json",
100
+ ...(gzip && { "Content-Encoding": "gzip", Vary: "Accept-Encoding" }),
101
+ },
102
+ });
103
+ } catch (e) {
104
+ return c.json(err((e as Error).message), errorStatus(e));
105
+ }
106
+ });
107
+
108
+ /**
109
+ * GET /files/index/search?q=<query>&kind=file|all&limit=<n>
110
+ * The best entries of the file index for `q`, best first — ranked as the palette ranks a list it
111
+ * holds — searched on the server, for a project whose list is too long to send. `kind=all`
112
+ * includes directories. A blank `q` answers the first entries in list order.
113
+ */
114
+ fileRoutes.get("/index/search", async (c) => {
115
+ try {
116
+ const projectPath = c.get("projectPath");
117
+ outlastFirstWalk(c);
118
+ const query = c.req.query("q") ?? "";
119
+ const kind = c.req.query("kind") === "all" ? "all" : "file";
120
+ const limit = Math.min(Math.max(Math.trunc(Number(c.req.query("limit"))) || REMOTE_FILE_SEARCH_LIMIT, 1), MAX_INDEX_SEARCH_LIMIT);
121
+ return c.json(ok(await fileService.searchIndex(projectPath, query, kind, limit)));
75
122
  } catch (e) {
76
123
  return c.json(err((e as Error).message), errorStatus(e));
77
124
  }
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * `GET /api/remote-desktop/capabilities` (read-only, always answerable — includes the host's
3
3
  * requirements checklist), `POST /api/remote-desktop/requirements/:id/:action` (host-side
4
- * fixes: OS permission prompt, open its Settings pane) and `POST /api/remote-desktop/session` (mints the single-use WS
5
- * nonce). Guard style mirrors
4
+ * fixes: OS permission prompt, open its Settings pane), `POST /api/remote-desktop/session` (mints the single-use WS
5
+ * nonce) and `POST /api/remote-desktop/whep/:ticket` (the WebRTC handshake, forwarded to the
6
+ * session's own loopback relay). Guard style mirrors
6
7
  * `named-tunnel.ts`: `authMiddleware` already passes every request through when PPM auth is
7
8
  * disabled, so a feature that hands out host control must enforce `auth.enabled` itself, plus
8
9
  * a same-origin check so a foreign page can't drive it via ambient browser credentials.
@@ -15,6 +16,10 @@ import { isRemoteDesktopEnabled } from "../../services/remote-desktop/remote-des
15
16
  import { mintRemoteDesktopNonce } from "../../services/remote-desktop/remote-desktop-nonce.ts";
16
17
  import { listDisplays } from "../../services/remote-desktop/remote-desktop-displays.ts";
17
18
  import { remoteDesktopReadiness, runHostAction } from "../../services/remote-desktop/remote-desktop-requirements.ts";
19
+ import { resolveWhepTarget } from "../../services/remote-desktop/remote-desktop-whep-registry.ts";
20
+ import {
21
+ relayStatus, startMediamtxInstall, uninstallMediamtx,
22
+ } from "../../services/remote-desktop/mediamtx-install.service.ts";
18
23
 
19
24
  export const remoteDesktopRoutes = new Hono();
20
25
 
@@ -84,3 +89,91 @@ remoteDesktopRoutes.post("/session", (c) => {
84
89
  if (rejected) return rejected;
85
90
  return c.json(ok({ nonce: mintRemoteDesktopNonce(), wsPath: "/ws/remote-desktop" }));
86
91
  });
92
+
93
+ /**
94
+ * WHEP: the browser posts an SDP offer, the relay answers with an SDP answer, and media then
95
+ * flows peer-to-peer over the relay's ICE port.
96
+ *
97
+ * The relay's signalling port is bound to loopback (`mediamtx-config.ts`), so this proxy is
98
+ * the only way to reach it — and it is deliberately **ticket-addressed, never URL-addressed**:
99
+ * the client presents a ticket PPM minted for its own session and handed it over the
100
+ * already-authenticated WebSocket. Forwarding a client-supplied URL here would turn a feature
101
+ * that already carries host control into an SSRF hole.
102
+ *
103
+ * `Location` from the relay is not passed back: it addresses 127.0.0.1 and means nothing to
104
+ * the browser. MediaMTX drops the session on its own when the peer connection goes away.
105
+ */
106
+ const MAX_SDP_BYTES = 64 * 1024;
107
+
108
+ remoteDesktopRoutes.post("/whep/:ticket", async (c) => {
109
+ const rejected = assertSessionAllowed(c);
110
+ if (rejected) return rejected;
111
+
112
+ const target = resolveWhepTarget(c.req.param("ticket"));
113
+ // One answer for unknown and for expired: a distinguishable pair would say whether a ticket
114
+ // ever existed.
115
+ if (!target) return c.json(err("unknown or expired WebRTC ticket"), 404);
116
+
117
+ const contentType = c.req.header("content-type") ?? "";
118
+ if (!contentType.startsWith("application/sdp")) {
119
+ return c.json(err("expected application/sdp"), 415);
120
+ }
121
+ const offer = await c.req.text();
122
+ if (offer.length > MAX_SDP_BYTES) return c.json(err("SDP offer too large"), 413);
123
+
124
+ let answer: Response;
125
+ try {
126
+ answer = await fetch(target, {
127
+ method: "POST",
128
+ headers: { "content-type": "application/sdp" },
129
+ body: offer,
130
+ });
131
+ } catch {
132
+ // The relay died between the ticket being handed out and this call.
133
+ return c.json(err("the WebRTC relay is not reachable"), 502);
134
+ }
135
+
136
+ const body = await answer.text();
137
+ if (!answer.ok) return c.json(err(`relay rejected the offer (${answer.status})`), 502);
138
+ return new Response(body, {
139
+ status: 201,
140
+ headers: { "content-type": "application/sdp", "cache-control": "no-store" },
141
+ });
142
+ });
143
+
144
+ /**
145
+ * The WebRTC relay's own install lifecycle, for the Settings pane.
146
+ *
147
+ * Behind the same guards as `/session`: downloading and unpacking an executable onto the host
148
+ * is host control, and the same-origin check keeps a foreign page from triggering it with
149
+ * ambient credentials.
150
+ */
151
+ remoteDesktopRoutes.get("/relay", (c) => {
152
+ const rejected = assertSessionAllowed(c);
153
+ if (rejected) return rejected;
154
+ return c.json(ok(relayStatus()));
155
+ });
156
+
157
+ remoteDesktopRoutes.post("/relay/install", (c) => {
158
+ const rejected = assertSessionAllowed(c);
159
+ if (rejected) return rejected;
160
+ try {
161
+ return c.json(ok(startMediamtxInstall()));
162
+ } catch (e) {
163
+ return c.json(err((e as Error).message), 409);
164
+ }
165
+ });
166
+
167
+ remoteDesktopRoutes.post("/relay/uninstall", (c) => {
168
+ const rejected = assertSessionAllowed(c);
169
+ if (rejected) return rejected;
170
+ try {
171
+ // False means a copy PPM did not install — the host's own, which is not PPM's to remove.
172
+ const removed = uninstallMediamtx();
173
+ return removed
174
+ ? c.json(ok({ removed }))
175
+ : c.json(err("this copy was not installed by PPM"), 409);
176
+ } catch (e) {
177
+ return c.json(err((e as Error).message), 409);
178
+ }
179
+ });
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Session trace over HTTP: browser logs coming in, and a session's timeline going out.
3
+ * Both sit behind authMiddleware — an error raised while logged out is lost, by decision.
4
+ */
5
+ import { Hono } from "hono";
6
+ import { ok, err } from "../../types/api.ts";
7
+ import {
8
+ DeviceRateLimiter,
9
+ TRACE_INGEST_MAX_BODY_BYTES,
10
+ browserRows,
11
+ parseBrowserBatch,
12
+ } from "../../services/session-trace/browser-trace-ingest.ts";
13
+ import { appendBatch, readSessionTimeline } from "../../services/session-trace/session-trace-store.ts";
14
+
15
+ const MAX_TIMELINE_ROWS = 20_000;
16
+
17
+ export function createTraceRoutes(limiter = new DeviceRateLimiter()): Hono {
18
+ const routes = new Hono();
19
+
20
+ /** POST /api/trace — body: BrowserTraceBatch (`src/shared/session-trace.ts`). */
21
+ routes.post("/", async (c) => {
22
+ const declared = Number(c.req.header("content-length") ?? 0);
23
+ if (declared > TRACE_INGEST_MAX_BODY_BYTES) return c.json(err("batch too large"), 400);
24
+ let text: string;
25
+ try { text = await c.req.text(); } catch { return c.json(err("unreadable body"), 400); }
26
+ if (Buffer.byteLength(text) > TRACE_INGEST_MAX_BODY_BYTES) return c.json(err("batch too large"), 400);
27
+ let raw: unknown;
28
+ try { raw = JSON.parse(text); } catch { return c.json(err("body must be JSON"), 400); }
29
+
30
+ const parsed = parseBrowserBatch(raw);
31
+ if (!parsed.ok) return c.json(err(parsed.error), 400);
32
+
33
+ const { batch, dropped } = parsed;
34
+ const verdict = limiter.take(batch.deviceId, batch.entries.length);
35
+ if (!verdict.allowed) {
36
+ c.header("Retry-After", String(Math.ceil(verdict.retryAfterMs / 1000)));
37
+ return c.json(err("too many trace entries from this device"), 429);
38
+ }
39
+
40
+ try {
41
+ appendBatch(browserRows(batch));
42
+ } catch (e) {
43
+ // 503 rather than 500: the client keeps the batch and tries again later.
44
+ console.warn(`[session-trace] browser batch not stored: ${(e as Error).message}`);
45
+ return c.json(err("trace store unavailable"), 503);
46
+ }
47
+ return c.json(ok({ accepted: batch.entries.length, dropped }));
48
+ });
49
+
50
+ /**
51
+ * GET /api/trace/sessions/:sessionId — the session's own trace plus every browser row filed
52
+ * against it, under any id the session has had, oldest first.
53
+ */
54
+ routes.get("/sessions/:sessionId", (c) => {
55
+ const limit = Math.min(MAX_TIMELINE_ROWS, Math.max(1, Number(c.req.query("limit")) || 5_000));
56
+ try {
57
+ return c.json(ok({ events: readSessionTimeline(c.req.param("sessionId"), limit) }));
58
+ } catch (e) {
59
+ return c.json(err((e as Error).message), 500);
60
+ }
61
+ });
62
+
63
+ return routes;
64
+ }
65
+
66
+ export const traceRoutes = createTraceRoutes();
@@ -0,0 +1,101 @@
1
+ /**
2
+ * WS handler for `/ws/android` — binary out = framed H.264 access units, text in/out = JSON.
3
+ *
4
+ * The upgrade succeeding proves nothing: the coarse PPM token travels as `?token=` on every WS
5
+ * URL (`isWsUpgradeAuthorized`, `src/server/index.ts`) and says only "holds the one reusable app
6
+ * token". A socket that hands over touch and keyboard control of a running VM therefore requires
7
+ * a single-use, short-TTL nonce from `POST /api/android/devices/:deviceId/sessions` as the
8
+ * client's *first* message, and the nonce — not the URL — is what names the device.
9
+ *
10
+ * The flag and `auth.enabled` are re-checked here even though `src/server/index.ts` already gated
11
+ * the upgrade on them: this handler must never depend on one call site staying correct, and must
12
+ * NOT reuse `isWsUpgradeAuthorized()`, which returns true unconditionally when PPM auth is off.
13
+ */
14
+ import { configService } from "../../services/config.service.ts";
15
+ import { isAndroidEmulatorEnabled } from "../../services/android/android-flag.ts";
16
+ import { consumeAndroidNonce } from "../../services/android/android-nonce.ts";
17
+ import { findRunningByDeviceId } from "../../services/android/device-registry.ts";
18
+ import {
19
+ attachAndroidViewer, registerAndroidExitSweep,
20
+ type AndroidSocket, type AndroidViewerSession,
21
+ } from "../../services/android/android-session.ts";
22
+ import { ANDROID_QUALITY_PRESETS, type AndroidQuality } from "../../shared/android-protocol.ts";
23
+
24
+ registerAndroidExitSweep();
25
+
26
+ interface AndroidWs {
27
+ data: { type: "android"; session?: AndroidViewerSession; authenticated?: boolean; closed?: boolean };
28
+ send: (d: string | Uint8Array) => number;
29
+ getBufferedAmount?: () => number;
30
+ close: (code?: number, reason?: string) => void;
31
+ }
32
+
33
+ function guardOrClose(ws: AndroidWs): boolean {
34
+ if (!isAndroidEmulatorEnabled()) { ws.close(1008, "android emulator support is disabled"); return false; }
35
+ if (!configService.get("auth").enabled) {
36
+ ws.close(1008, "android emulator control requires PPM authentication to be enabled");
37
+ return false;
38
+ }
39
+ return true;
40
+ }
41
+
42
+ /** The session layer's view of this socket. `isClosed` reads the flag `close` below sets: a
43
+ * close during `attachAndroidViewer` finds no session to close yet. */
44
+ export function androidSocketFor(ws: AndroidWs): AndroidSocket {
45
+ return {
46
+ send: (d) => ws.send(d),
47
+ getBufferedAmount: ws.getBufferedAmount ? () => ws.getBufferedAmount!() : undefined,
48
+ close: (code, reason) => ws.close(code, reason),
49
+ isClosed: () => ws.data.closed === true,
50
+ };
51
+ }
52
+
53
+ function parseQuality(value: unknown): AndroidQuality {
54
+ return typeof value === "string" && value in ANDROID_QUALITY_PRESETS ? (value as AndroidQuality) : "balanced";
55
+ }
56
+
57
+ async function authenticateFirstMessage(ws: AndroidWs, text: string): Promise<void> {
58
+ let parsed: Record<string, unknown>;
59
+ try { parsed = JSON.parse(text); } catch { ws.close(1008, "expected auth message"); return; }
60
+
61
+ const nonce = parsed.type === "auth" ? parsed.nonce : undefined;
62
+ const deviceId = typeof nonce === "string" ? consumeAndroidNonce(nonce) : null;
63
+ if (!deviceId) { ws.close(1008, "invalid or expired session nonce"); return; }
64
+
65
+ const emulator = findRunningByDeviceId(deviceId);
66
+ if (!emulator) { ws.close(1011, "that emulator is no longer running"); return; }
67
+
68
+ ws.data.authenticated = true;
69
+ try {
70
+ ws.data.session = await attachAndroidViewer(androidSocketFor(ws), {
71
+ deviceId,
72
+ emulator,
73
+ quality: parseQuality(parsed.quality),
74
+ });
75
+ } catch (e) {
76
+ console.error(`[android] failed to start a session: ${(e as Error).message}`);
77
+ ws.send(JSON.stringify({ type: "error", message: (e as Error).message, fatal: true }));
78
+ ws.close(1011, "session failed to start");
79
+ }
80
+ }
81
+
82
+ export const androidWebSocket = {
83
+ open(ws: AndroidWs) {
84
+ guardOrClose(ws); // nothing else until the client's first (auth) message arrives
85
+ },
86
+
87
+ async message(ws: AndroidWs, msg: string | ArrayBuffer | Uint8Array) {
88
+ if (!guardOrClose(ws)) return;
89
+ const text = typeof msg === "string" ? msg : new TextDecoder().decode(msg as ArrayBuffer);
90
+ if (!ws.data.authenticated) {
91
+ await authenticateFirstMessage(ws, text);
92
+ return;
93
+ }
94
+ await ws.data.session?.handleClientMessage(text);
95
+ },
96
+
97
+ close(ws: AndroidWs) {
98
+ ws.data.closed = true;
99
+ ws.data.session?.close();
100
+ },
101
+ };
@@ -180,7 +180,7 @@ function releaseSubprocess(sessionId: string, reason: string, note: string): voi
180
180
  if (entry.clients.size > 0 || entry.isStreamingActive) return;
181
181
  const provider = providerRegistry.get(entry.providerId);
182
182
  if (!provider?.hasStreamingSession?.(sessionId)) return;
183
- provider.abortQuery?.(sessionId, reason);
183
+ chatService.abortQuery(entry.providerId, sessionId, reason, "ws");
184
184
  console.log(`[chat] session=${sessionId} released subprocess (${reason})`);
185
185
  logSessionEvent(sessionId, "INFO", note);
186
186
  }
@@ -204,7 +204,7 @@ export function dropIdleSubprocess(sessionId: string, reason: string, note: stri
204
204
  if (!entry) return;
205
205
  const provider = providerRegistry.get(entry.providerId);
206
206
  if (!provider?.hasStreamingSession?.(sessionId)) return;
207
- provider.abortQuery?.(sessionId, reason);
207
+ chatService.abortQuery(entry.providerId, sessionId, reason, "ws");
208
208
  if (entry.cacheReleaseTimer) {
209
209
  clearTimeout(entry.cacheReleaseTimer);
210
210
  entry.cacheReleaseTimer = undefined;
@@ -612,7 +612,7 @@ function startCleanupTimer(sessionId: string): void {
612
612
  // flight, so the session entry going away is the last chance to free the process.
613
613
  const provider = providerRegistry.get(entry.providerId);
614
614
  if (provider?.hasStreamingSession?.(sessionId)) {
615
- provider.abortQuery?.(sessionId, "idle_timeout");
615
+ chatService.abortQuery(entry.providerId, sessionId, "idle_timeout", "ws");
616
616
  }
617
617
  if (entry.cacheReleaseTimer) {
618
618
  clearTimeout(entry.cacheReleaseTimer);
@@ -696,7 +696,7 @@ async function startSessionConsumer(sessionId: string, providerId: string, conte
696
696
  // provider config: omit so the provider falls back. thinking 0 = explicit OFF (overrides config).
697
697
  const effortOverride = getSessionEffort(sessionId) ?? undefined;
698
698
  const thinkingBudget = getSessionThinking(sessionId);
699
- for await (const event of chatService.sendMessage(providerId, sessionId, content, { permissionMode, images, ...(imagePaths?.length && { imagePaths }), ...(model && { model }), ...(effortOverride && { effort: effortOverride }), ...(thinkingBudget != null && { thinkingBudget }) })) {
699
+ for await (const event of chatService.sendMessage(providerId, sessionId, content, { permissionMode, images, ...(imagePaths?.length && { imagePaths }), ...(model && { model }), ...(effortOverride && { effort: effortOverride }), ...(thinkingBudget != null && { thinkingBudget }), origin: "ws" })) {
700
700
  eventCount++;
701
701
  const ev = event as any;
702
702
  const evType = ev.type ?? "unknown";
@@ -1388,9 +1388,7 @@ export const chatWebSocket = {
1388
1388
  // unblock it, then the follow-up message flows through normally.
1389
1389
  if (entry.pendingApprovalEvent) {
1390
1390
  const pendingReqId = entry.pendingApprovalEvent.requestId;
1391
- if (provider && typeof provider.resolveApproval === "function") {
1392
- provider.resolveApproval(pendingReqId, false);
1393
- }
1391
+ chatService.resolveApproval(providerId, sessionId, pendingReqId, false, undefined, { reason: "superseded_by_message", origin: "ws" });
1394
1392
  entry.pendingApprovalEvent = undefined;
1395
1393
  broadcast(sessionId, {
1396
1394
  type: "approval_resolved",
@@ -1442,6 +1440,7 @@ export const chatWebSocket = {
1442
1440
  const effort = getSessionEffort(sessionId) ?? undefined;
1443
1441
  const thinkingBudget = getSessionThinking(sessionId);
1444
1442
  await chatService.pushMessage(providerId, sessionId, parsed.content, {
1443
+ origin: "ws",
1445
1444
  priority: parsed.priority ?? 'next',
1446
1445
  images: parsed.images,
1447
1446
  imagePaths: parsed.imagePaths,
@@ -1472,7 +1471,7 @@ export const chatWebSocket = {
1472
1471
  // Aborting the idle-but-alive subprocess forces the next message to take
1473
1472
  // the resume path, recreating the query with the new model.
1474
1473
  if (hasLiveStream && entry.phase === "idle") {
1475
- provider?.abortQuery?.(sessionId, "set_model");
1474
+ chatService.abortQuery(providerId, sessionId, "set_model", "ws");
1476
1475
  }
1477
1476
  logSessionEvent(sessionId, "INFO", `Model switched to ${parsed.model}`);
1478
1477
  ws.send(JSON.stringify({
@@ -1499,7 +1498,7 @@ export const chatWebSocket = {
1499
1498
  // Abort only when idle-but-alive so the next turn recreates the query with the new
1500
1499
  // effort (mirror set_model); never interrupt an active turn.
1501
1500
  if ((provider?.hasStreamingSession?.(sessionId) ?? false) && entry.phase === "idle") {
1502
- provider?.abortQuery?.(sessionId, "set_effort");
1501
+ chatService.abortQuery(providerId, sessionId, "set_effort", "ws");
1503
1502
  }
1504
1503
  logSessionEvent(sessionId, "INFO", `Effort switched to ${parsed.effort}`);
1505
1504
  ws.send(JSON.stringify({
@@ -1520,7 +1519,7 @@ export const chatWebSocket = {
1520
1519
  setSessionThinking(sessionId, parsed.enabled ? THINKING_ADAPTIVE : 0);
1521
1520
  const provider = providerRegistry.get(providerId);
1522
1521
  if ((provider?.hasStreamingSession?.(sessionId) ?? false) && entry.phase === "idle") {
1523
- provider?.abortQuery?.(sessionId, "set_thinking");
1522
+ chatService.abortQuery(providerId, sessionId, "set_thinking", "ws");
1524
1523
  }
1525
1524
  logSessionEvent(sessionId, "INFO", `Thinking ${parsed.enabled ? "on" : "off"}`);
1526
1525
  ws.send(JSON.stringify({
@@ -1537,11 +1536,10 @@ export const chatWebSocket = {
1537
1536
  }));
1538
1537
  } else if (parsed.type === "cancel") {
1539
1538
  // Fully teardown streaming session — user must resume to continue
1540
- const provider = providerRegistry.get(providerId);
1541
1539
  const phase = entry?.phase ?? "unknown";
1542
1540
  console.log(`[chat] session=${sessionId} WS cancel received from FE (phase=${phase})`);
1543
1541
  logSessionEvent(sessionId, "CANCEL", `WS cancel from FE (phase=${phase})`);
1544
- provider?.abortQuery?.(sessionId, "ws_cancel");
1542
+ chatService.abortQuery(providerId, sessionId, "ws_cancel", "ws");
1545
1543
  } else if (parsed.type === "kill_background_shell") {
1546
1544
  // Kill via the AI: enqueue an instruction so the model calls KillShell.
1547
1545
  // Cross-platform and safe (no OS-PID guessing). Runs when the AI is idle
@@ -1572,17 +1570,14 @@ export const chatWebSocket = {
1572
1570
  }, 0);
1573
1571
  });
1574
1572
  } else if (provider && "pushMessage" in provider) {
1575
- await chatService.pushMessage(providerId, sessionId, instruction, { priority: "next" });
1573
+ await chatService.pushMessage(providerId, sessionId, instruction, { priority: "next", origin: "ws" });
1576
1574
  entry.turnEvents = [];
1577
1575
  entry.pendingApprovalEvent = undefined;
1578
1576
  setPhase(sessionId, "thinking");
1579
1577
  }
1580
1578
  logSessionEvent(sessionId, "INFO", `kill_background_shell requested shellId=${shellId}`);
1581
1579
  } else if (parsed.type === "approval_response") {
1582
- const provider = providerRegistry.get(providerId);
1583
- if (provider && typeof provider.resolveApproval === "function") {
1584
- provider.resolveApproval(parsed.requestId, parsed.approved, (parsed as any).data);
1585
- }
1580
+ chatService.resolveApproval(providerId, sessionId, parsed.requestId, parsed.approved, (parsed as any).data, { origin: "ws" });
1586
1581
  if (entry) {
1587
1582
  entry.pendingApprovalEvent = undefined;
1588
1583
  // Enrich the buffered approval_request with response data so replayed
@@ -9,7 +9,7 @@
9
9
  * so a workspace whose visible tabs are an editor and a terminal would get no
10
10
  * file watching at all — silently breaking editor live-reload, docx/pdf
11
11
  * preview reload, and file-tree invalidation.
12
- * 2. **Cross-cutting broadcasts** (`file:changed`, `session:unread_changed`,
12
+ * 2. **Cross-cutting broadcasts** (`file:changed`, `files:index-changed`, `session:unread_changed`,
13
13
  * `session:phase_changed`, `jira:*`, `design:*`). These are app-wide, not session-scoped,
14
14
  * so they belong on an app-wide channel.
15
15
  *
@@ -18,6 +18,7 @@
18
18
  * example, make an editor re-fetch its file twice per change).
19
19
  */
20
20
  import { startWatching, stopWatching, onFileChange } from "../../services/file-watcher.service.ts";
21
+ import { onIndexRebuilt, warmIndex } from "../../services/file-list-index.service.ts";
21
22
  import { configService } from "../../services/config.service.ts";
22
23
  import { onDesignEvent } from "../../services/design/design-events.ts";
23
24
  import { resolve } from "node:path";
@@ -71,6 +72,8 @@ function setWatch(ws: GlobalWsSocket, projectName: string): void {
71
72
  if (!project) return;
72
73
 
73
74
  void startWatching(projectName, project.path);
75
+ // A client opened this project: have its file list ready before the palette asks for it.
76
+ warmIndex(project.path);
74
77
  watchedProject.set(ws, projectName);
75
78
  }
76
79
 
@@ -86,6 +89,15 @@ function projectNameForPath(projectPath: string): string | null {
86
89
  return configService.get("projects").find((p) => fold(p.path) === target)?.name ?? null;
87
90
  }
88
91
 
92
+ // A project's file index was rebuilt behind a stale one and now lists other paths. Clients do
93
+ // not refetch the index on every `file:changed` (on a large project that is a 22 MB download
94
+ // per change); this is what tells them the list they hold is out of date.
95
+ onIndexRebuilt((projectPath, changed) => {
96
+ if (!changed) return;
97
+ const projectName = projectNameForPath(projectPath);
98
+ if (projectName) broadcastGlobalEvent({ type: "files:index-changed", projectName });
99
+ });
100
+
89
101
  // Design history/comment changes: `.design/` is not watched, so these are the only signal.
90
102
  // Services know the project path; browsers address projects by name, and a path no
91
103
  // registered project owns is dropped rather than broadcast.
@@ -9,7 +9,9 @@
9
9
  * client's *first* message — `{type:"auth", nonce, displayId?, cursor?, codec?}` — before
10
10
  * capture or input starts. `displayId` picks one of `/capabilities`' `displays`; absent/unknown = primary.
11
11
  * `cursor` and `codec` carry the client's saved cursor and encoder prefs, because ffmpeg takes
12
- * both at startup and applying them afterwards would respawn it on every connect. Kept out of
12
+ * both at startup and applying them afterwards would respawn it on every connect. `webrtc`
13
+ * asks for the relay transport; when it is not installed the session answers on this socket
14
+ * as before rather than failing. Kept out of
13
15
  * the query string (unlike the coarse token) so they never land in proxy/tunnel access logs.
14
16
  *
15
17
  * Both the feature flag and `auth.enabled` are re-checked here even though
@@ -66,6 +68,9 @@ async function authenticateFirstMessage(ws: RemoteDesktopWs, text: string): Prom
66
68
  // getting the pointer it has always had.
67
69
  showCursor: parsed.cursor !== false,
68
70
  encoder: typeof parsed.codec === "string" ? parsed.codec : undefined,
71
+ // Opt-in, and only honoured when the relay is installed: an older client sends no flag
72
+ // and keeps the WebSocket path it has always used.
73
+ webrtc: parsed.webrtc === true,
69
74
  });
70
75
  } catch (e) {
71
76
  console.error(`[remote-desktop] failed to start capture: ${(e as Error).message}`);