@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
@@ -0,0 +1,529 @@
1
+ /**
2
+ * An `org.freedesktop.portal.ScreenCast` session, spoken over libdbus through `bun:ffi`.
3
+ *
4
+ * This is the only way to capture a Wayland screen: there is no `x11grab` equivalent, and a
5
+ * compositor will not hand pixels to anything that has not been through the portal. The portal
6
+ * is D-Bus, and Bun has no D-Bus — hence FFI against `libdbus-1.so.3`, which is present on any
7
+ * host running a desktop session (it is what the session bus itself is built on).
8
+ *
9
+ * Three things here are load-bearing and each was measured rather than reasoned about:
10
+ *
11
+ * 1. **`OpenPipeWireRemote` is deliberately never called.** The documented flow ends by asking
12
+ * the portal for a PipeWire file descriptor, and an fd cannot be passed to a *separate*
13
+ * process by any shell-out (`busctl`, `gdbus`) — which is what made this look impossible.
14
+ * It turns out not to be needed: the node the portal creates lives in the user's own
15
+ * PipeWire daemon, so `pipewiresrc path=<node>` reaches it through the ordinary
16
+ * `$XDG_RUNTIME_DIR/pipewire-0` socket. Measured: a plain `pipewiresrc path=N` negotiated
17
+ * `BGRA 1920x1080` with no fd anywhere in the pipeline.
18
+ *
19
+ * 2. **The connection must stay open for the node's whole life.** A portal session is owned by
20
+ * the D-Bus connection that created it; when that connection drops, the compositor tears the
21
+ * session down and the node disappears. So this returns a handle that owns the connection,
22
+ * and it must outlive the capture process — not be created per call and closed.
23
+ *
24
+ * 3. **`persist_mode: 2` + `restore_token`** is what stops the consent dialog appearing on the
25
+ * host's screen for every session. That matters more here than it would for a screen
26
+ * recorder: this is a *remote* desktop, so a prompt that only the person sitting at the
27
+ * machine can answer is a prompt nobody can answer. The first session still asks — there is
28
+ * no way around that, and there should not be — and every session after it restores
29
+ * silently. Verified: a second session went straight to a live node with no prompt.
30
+ */
31
+ import { CString, dlopen, FFIType, ptr, suffix, type Pointer } from "bun:ffi";
32
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
33
+ import { dirname, resolve } from "node:path";
34
+ import { getPpmDir } from "../ppm-dir.ts";
35
+
36
+ const BUS = "org.freedesktop.portal.Desktop";
37
+ const OBJ = "/org/freedesktop/portal/desktop";
38
+ const SCREENCAST = "org.freedesktop.portal.ScreenCast";
39
+ const REQUEST = "org.freedesktop.portal.Request";
40
+
41
+ // D-Bus type codes, as the ASCII of their signature characters.
42
+ const T_STRING = 115; // 's'
43
+ const T_ARRAY = 97; // 'a'
44
+ const T_VARIANT = 118; // 'v'
45
+ const T_DICT_ENTRY = 101; // 'e'
46
+ const T_UINT32 = 117; // 'u'
47
+ const T_BOOLEAN = 98; // 'b'
48
+ const T_OBJECT_PATH = 111; // 'o'
49
+ const T_INVALID = 0;
50
+ const T_STRUCT = 114; // 'r'
51
+
52
+ const DBUS_BUS_SESSION = 0;
53
+ /** `DBusMessageIter` is 72 bytes on x86-64; `DBusError` 32. Both are over-allocated rather than
54
+ * computed, because getting either too small corrupts the stack of the C library silently. */
55
+ const ITER_BYTES = 128;
56
+ const ERR_BYTES = 64;
57
+
58
+ /** Source types: 1 = MONITOR, 2 = WINDOW, 4 = VIRTUAL. Only whole monitors are asked for. */
59
+ const SOURCE_MONITOR = 1;
60
+ /** Cursor modes: 1 = HIDDEN, 2 = EMBEDDED (drawn into the frames), 4 = METADATA (sent apart).
61
+ * EMBEDDED is what `-draw_mouse` gives on the other platforms, so the two agree. */
62
+ const CURSOR_EMBEDDED = 2;
63
+ const CURSOR_HIDDEN = 1;
64
+ /** 2 = the permission persists until the user revokes it, which is what yields a restore token. */
65
+ const PERSIST_UNTIL_REVOKED = 2;
66
+
67
+ type Ptr = Pointer;
68
+
69
+ let lib: ReturnType<typeof openLibdbus> | null = null;
70
+ /** Anything whose address has been handed to C stays referenced here for the call's duration —
71
+ * a `Buffer` collected while libdbus still holds its pointer is a use-after-free, and Bun's GC
72
+ * has no idea the pointer escaped. */
73
+ let pinned: unknown[] = [];
74
+
75
+ function openLibdbus() {
76
+ return dlopen(`libdbus-1.${suffix}.3`, {
77
+ dbus_error_init: { args: [FFIType.ptr], returns: FFIType.void },
78
+ dbus_error_is_set: { args: [FFIType.ptr], returns: FFIType.i32 },
79
+ dbus_error_free: { args: [FFIType.ptr], returns: FFIType.void },
80
+ dbus_bus_get_private: { args: [FFIType.i32, FFIType.ptr], returns: FFIType.ptr },
81
+ dbus_bus_get_unique_name: { args: [FFIType.ptr], returns: FFIType.cstring },
82
+ dbus_bus_add_match: { args: [FFIType.ptr, FFIType.ptr, FFIType.ptr], returns: FFIType.void },
83
+ dbus_connection_close: { args: [FFIType.ptr], returns: FFIType.void },
84
+ dbus_connection_unref: { args: [FFIType.ptr], returns: FFIType.void },
85
+ dbus_connection_read_write: { args: [FFIType.ptr, FFIType.i32], returns: FFIType.i32 },
86
+ dbus_connection_pop_message: { args: [FFIType.ptr], returns: FFIType.ptr },
87
+ dbus_connection_send_with_reply_and_block: {
88
+ args: [FFIType.ptr, FFIType.ptr, FFIType.i32, FFIType.ptr], returns: FFIType.ptr,
89
+ },
90
+ dbus_message_new_method_call: {
91
+ args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr], returns: FFIType.ptr,
92
+ },
93
+ dbus_message_unref: { args: [FFIType.ptr], returns: FFIType.void },
94
+ dbus_message_is_signal: { args: [FFIType.ptr, FFIType.ptr, FFIType.ptr], returns: FFIType.i32 },
95
+ dbus_message_get_path: { args: [FFIType.ptr], returns: FFIType.cstring },
96
+ dbus_message_iter_init: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.i32 },
97
+ dbus_message_iter_init_append: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.void },
98
+ dbus_message_iter_append_basic: { args: [FFIType.ptr, FFIType.i32, FFIType.ptr], returns: FFIType.i32 },
99
+ dbus_message_iter_open_container: {
100
+ args: [FFIType.ptr, FFIType.i32, FFIType.ptr, FFIType.ptr], returns: FFIType.i32,
101
+ },
102
+ dbus_message_iter_close_container: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.i32 },
103
+ dbus_message_iter_get_arg_type: { args: [FFIType.ptr], returns: FFIType.i32 },
104
+ dbus_message_iter_recurse: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.void },
105
+ dbus_message_iter_get_basic: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.void },
106
+ dbus_message_iter_next: { args: [FFIType.ptr], returns: FFIType.i32 },
107
+ });
108
+ }
109
+
110
+ /** libdbus, or null on a host without it (no desktop session — there is nothing to capture
111
+ * there anyway, so this is reported as "unavailable", never thrown past the caller). */
112
+ function dbus() {
113
+ if (lib) return lib.symbols;
114
+ try {
115
+ lib = openLibdbus();
116
+ } catch {
117
+ return null;
118
+ }
119
+ return lib.symbols;
120
+ }
121
+
122
+ const cstr = (s: string) => {
123
+ const b = Buffer.from(s + "\0", "utf8");
124
+ pinned.push(b);
125
+ return ptr(b);
126
+ };
127
+
128
+ /** A pointer-sized cell holding the address of `value`, for the `void*` out/in params libdbus
129
+ * takes for every basic type. */
130
+ const cell = (value: Ptr) => {
131
+ const b = new BigUint64Array([BigInt(value)]);
132
+ pinned.push(b);
133
+ return ptr(b);
134
+ };
135
+
136
+ const scratch = (bytes: number) => {
137
+ const b = new Uint8Array(bytes);
138
+ pinned.push(b);
139
+ return b;
140
+ };
141
+
142
+ /** The variant types this client ever sends. */
143
+ type VariantValue =
144
+ | { t: "s"; v: string }
145
+ | { t: "u"; v: number }
146
+ | { t: "b"; v: boolean };
147
+
148
+ /** Append `entries` to `iter` as an `a{sv}` — the options dictionary every portal method takes. */
149
+ function appendOptions(d: NonNullable<ReturnType<typeof dbus>>, iter: Uint8Array, entries: Record<string, VariantValue>) {
150
+ const arr = scratch(ITER_BYTES);
151
+ d.dbus_message_iter_open_container(ptr(iter), T_ARRAY, cstr("{sv}"), ptr(arr));
152
+ for (const [key, val] of Object.entries(entries)) {
153
+ const ent = scratch(ITER_BYTES);
154
+ d.dbus_message_iter_open_container(ptr(arr), T_DICT_ENTRY, null as unknown as number, ptr(ent));
155
+ d.dbus_message_iter_append_basic(ptr(ent), T_STRING, cell(cstr(key)));
156
+ const varIter = scratch(ITER_BYTES);
157
+ d.dbus_message_iter_open_container(ptr(ent), T_VARIANT, cstr(val.t), ptr(varIter));
158
+ if (val.t === "s") {
159
+ d.dbus_message_iter_append_basic(ptr(varIter), T_STRING, cell(cstr(val.v)));
160
+ } else if (val.t === "u") {
161
+ const n = new Uint32Array([val.v]);
162
+ pinned.push(n);
163
+ d.dbus_message_iter_append_basic(ptr(varIter), T_UINT32, ptr(n));
164
+ } else {
165
+ // D-Bus booleans marshal as uint32 0/1, not as a byte.
166
+ const n = new Uint32Array([val.v ? 1 : 0]);
167
+ pinned.push(n);
168
+ d.dbus_message_iter_append_basic(ptr(varIter), T_BOOLEAN, ptr(n));
169
+ }
170
+ d.dbus_message_iter_close_container(ptr(ent), ptr(varIter));
171
+ d.dbus_message_iter_close_container(ptr(arr), ptr(ent));
172
+ }
173
+ d.dbus_message_iter_close_container(ptr(iter), ptr(arr));
174
+ }
175
+
176
+ /** Read a basic value out of `iter` according to its own arg type. Returns null for anything
177
+ * this client does not need (nested containers other than the ones handled by the callers). */
178
+ function readBasic(d: NonNullable<ReturnType<typeof dbus>>, iter: Uint8Array): string | number | boolean | null {
179
+ const t = d.dbus_message_iter_get_arg_type(ptr(iter));
180
+ if (t === T_STRING || t === T_OBJECT_PATH) {
181
+ const out = new BigUint64Array(1);
182
+ d.dbus_message_iter_get_basic(ptr(iter), ptr(out));
183
+ const addr = out[0];
184
+ if (!addr) return null;
185
+ return new CString(Number(addr) as Pointer).toString();
186
+ }
187
+ if (t === T_UINT32) {
188
+ const out = new Uint32Array(1);
189
+ d.dbus_message_iter_get_basic(ptr(iter), ptr(out));
190
+ return out[0] ?? null;
191
+ }
192
+ if (t === T_BOOLEAN) {
193
+ const out = new Uint32Array(1);
194
+ d.dbus_message_iter_get_basic(ptr(iter), ptr(out));
195
+ return out[0] === 1;
196
+ }
197
+ return null;
198
+ }
199
+
200
+ /** Walk an `a{sv}` at `iter` and return the keys `wanted` asks for, as raw iterators resolved
201
+ * by `read`. Keys not in `wanted` are skipped without being decoded. */
202
+ function readOptions<T>(
203
+ d: NonNullable<ReturnType<typeof dbus>>,
204
+ iter: Uint8Array,
205
+ read: (key: string, value: Uint8Array) => void,
206
+ ): void {
207
+ if (d.dbus_message_iter_get_arg_type(ptr(iter)) !== T_ARRAY) return;
208
+ const arr = scratch(ITER_BYTES);
209
+ d.dbus_message_iter_recurse(ptr(iter), ptr(arr));
210
+ while (d.dbus_message_iter_get_arg_type(ptr(arr)) === T_DICT_ENTRY) {
211
+ const ent = scratch(ITER_BYTES);
212
+ d.dbus_message_iter_recurse(ptr(arr), ptr(ent));
213
+ const key = readBasic(d, ent);
214
+ d.dbus_message_iter_next(ptr(ent));
215
+ const varIter = scratch(ITER_BYTES);
216
+ if (d.dbus_message_iter_get_arg_type(ptr(ent)) === T_VARIANT) {
217
+ d.dbus_message_iter_recurse(ptr(ent), ptr(varIter));
218
+ if (typeof key === "string") read(key, varIter);
219
+ }
220
+ d.dbus_message_iter_next(ptr(arr));
221
+ }
222
+ }
223
+
224
+ /** Where the restore token is kept. `getPpmDir()` rather than `homedir()`: under a test
225
+ * `PPM_HOME` this must not read or write the user's real grant. */
226
+ function restoreTokenPath(): string {
227
+ return resolve(getPpmDir(), "remote-desktop", "portal-restore-token");
228
+ }
229
+
230
+ function readRestoreToken(): string | null {
231
+ try {
232
+ const t = readFileSync(restoreTokenPath(), "utf8").trim();
233
+ return t || null;
234
+ } catch {
235
+ return null;
236
+ }
237
+ }
238
+
239
+ function saveRestoreToken(token: string): void {
240
+ try {
241
+ const p = restoreTokenPath();
242
+ mkdirSync(dirname(p), { recursive: true });
243
+ // 0600: the token is a standing grant to capture this user's screen.
244
+ writeFileSync(p, token, { mode: 0o600 });
245
+ } catch (e) {
246
+ console.warn(`[remote-desktop] could not save the portal restore token: ${(e as Error).message}`);
247
+ }
248
+ }
249
+
250
+ /** Forget the stored grant, so the next session asks the user again. */
251
+ export function clearPortalRestoreToken(): void {
252
+ try {
253
+ writeFileSync(restoreTokenPath(), "", { mode: 0o600 });
254
+ } catch {
255
+ /* nothing stored yet */
256
+ }
257
+ }
258
+
259
+ export interface PortalScreenCast {
260
+ /** The PipeWire node to hand to `pipewiresrc path=`. */
261
+ nodeId: number;
262
+ width: number | null;
263
+ height: number | null;
264
+ /** Close the session and drop the connection. The node dies with it — call only once the
265
+ * capture process has exited, or GStreamer loses its source mid-stream. */
266
+ stop(): void;
267
+ isStopped(): boolean;
268
+ }
269
+
270
+ export class PortalUnavailableError extends Error {
271
+ constructor(msg: string) {
272
+ super(msg);
273
+ this.name = "PortalUnavailableError";
274
+ }
275
+ }
276
+
277
+ export interface PortalOptions {
278
+ /** Draw the host pointer into the frames, matching `-draw_mouse` elsewhere. */
279
+ drawMouse?: boolean;
280
+ /** How long to wait for the user to answer the consent dialog, first time round. */
281
+ timeoutMs?: number;
282
+ }
283
+
284
+ /**
285
+ * Open a ScreenCast session and return its PipeWire node.
286
+ *
287
+ * Rejects with `PortalUnavailableError` when there is no libdbus, no portal on the bus, or the
288
+ * user dismissed the dialog — all three are ordinary states on a host that simply has not been
289
+ * set up, so the caller reports them as an unmet requirement rather than a crash.
290
+ */
291
+ export async function startPortalScreenCast(opts: PortalOptions = {}): Promise<PortalScreenCast> {
292
+ const d = dbus();
293
+ if (!d) throw new PortalUnavailableError("libdbus-1 is not installed on this host");
294
+
295
+ const err = scratch(ERR_BYTES);
296
+ d.dbus_error_init(ptr(err));
297
+ // A *private* connection: a shared one is reference-counted process-wide, so closing it
298
+ // could tear down a session belonging to another caller, and not closing it would leak the
299
+ // portal session for the life of PPM.
300
+ const conn = d.dbus_bus_get_private(DBUS_BUS_SESSION, ptr(err));
301
+ if (!conn) throw new PortalUnavailableError("no D-Bus session bus (is a desktop session running?)");
302
+
303
+ let closed = false;
304
+ const close = () => {
305
+ if (closed) return;
306
+ closed = true;
307
+ try {
308
+ d.dbus_connection_close(conn);
309
+ d.dbus_connection_unref(conn);
310
+ } catch { /* already gone */ }
311
+ };
312
+
313
+ try {
314
+ const unique = String(d.dbus_bus_get_unique_name(conn) ?? "");
315
+ if (!unique) throw new PortalUnavailableError("the D-Bus connection has no unique name");
316
+ // ":1.234" -> "1_234", which is how the portal builds the Request object path.
317
+ const sender = unique.replace(/^:/, "").replace(/\./g, "_");
318
+
319
+ d.dbus_bus_add_match(conn, cstr(`type='signal',interface='${REQUEST}',member='Response'`), ptr(err));
320
+
321
+ const deadline = Date.now() + (opts.timeoutMs ?? 120_000);
322
+ const call = (method: string, build: (iter: Uint8Array, handleToken: string) => void) =>
323
+ portalCall(d, conn, sender, method, build, deadline);
324
+
325
+ const created = call("CreateSession", (iter, handleToken) => {
326
+ appendOptions(d, iter, {
327
+ handle_token: { t: "s", v: handleToken },
328
+ session_handle_token: { t: "s", v: `ppm${randomToken()}` },
329
+ });
330
+ });
331
+ const session = stringField(await created, "session_handle");
332
+ if (!session) throw new PortalUnavailableError("the portal returned no session handle");
333
+
334
+ const restore = readRestoreToken();
335
+ await call("SelectSources", (iter, handleToken) => {
336
+ appendPath(d, iter, session);
337
+ appendOptions(d, iter, {
338
+ handle_token: { t: "s", v: handleToken },
339
+ types: { t: "u", v: SOURCE_MONITOR },
340
+ multiple: { t: "b", v: false },
341
+ cursor_mode: { t: "u", v: opts.drawMouse === false ? CURSOR_HIDDEN : CURSOR_EMBEDDED },
342
+ persist_mode: { t: "u", v: PERSIST_UNTIL_REVOKED },
343
+ ...(restore ? { restore_token: { t: "s" as const, v: restore } } : {}),
344
+ });
345
+ });
346
+
347
+ const started = await call("Start", (iter, handleToken) => {
348
+ appendPath(d, iter, session);
349
+ // parent_window: an X11/Wayland handle the portal would parent its dialog to. PPM has no
350
+ // window on the host, so this is empty — the dialog appears unparented, which is correct.
351
+ d.dbus_message_iter_append_basic(ptr(iter), T_STRING, cell(cstr("")));
352
+ appendOptions(d, iter, { handle_token: { t: "s", v: handleToken } });
353
+ });
354
+
355
+ const token = stringField(started, "restore_token");
356
+ if (token) saveRestoreToken(token);
357
+
358
+ const stream = firstStream(started);
359
+ if (!stream) {
360
+ throw new PortalUnavailableError(
361
+ "the portal granted no stream — the screen-share dialog was dismissed",
362
+ );
363
+ }
364
+
365
+ return {
366
+ nodeId: stream.nodeId,
367
+ width: stream.width,
368
+ height: stream.height,
369
+ stop: close,
370
+ isStopped: () => closed,
371
+ };
372
+ } catch (e) {
373
+ close();
374
+ throw e;
375
+ } finally {
376
+ pinned = [];
377
+ }
378
+ }
379
+
380
+ /** A handle token unique to this process and call. Only has to be unique within our own
381
+ * sender name, which the portal already scopes the Request path by. */
382
+ function randomToken(): string {
383
+ return crypto.randomUUID().replace(/-/g, "").slice(0, 16);
384
+ }
385
+
386
+ function appendPath(d: NonNullable<ReturnType<typeof dbus>>, iter: Uint8Array, path: string): void {
387
+ d.dbus_message_iter_append_basic(ptr(iter), T_OBJECT_PATH, cell(cstr(path)));
388
+ }
389
+
390
+ /**
391
+ * Invoke one portal method and wait for its `Response` signal.
392
+ *
393
+ * Every ScreenCast method answers immediately with a Request object path and delivers the real
394
+ * answer later as a signal on it — so the match rule has to be in place *before* the call, and
395
+ * the path is derived from the `handle_token` we choose rather than read from the reply (the
396
+ * portal spec documents that derivation precisely so a client can subscribe first; racing the
397
+ * reply is how a fast portal's signal gets missed).
398
+ */
399
+ async function portalCall(
400
+ d: NonNullable<ReturnType<typeof dbus>>,
401
+ conn: Ptr,
402
+ sender: string,
403
+ method: string,
404
+ build: (iter: Uint8Array, handleToken: string) => void,
405
+ deadline: number,
406
+ ): Promise<Record<string, unknown>> {
407
+ const token = `ppm${randomToken()}`;
408
+ const wantPath = `/org/freedesktop/portal/desktop/request/${sender}/${token}`;
409
+
410
+ const msg = d.dbus_message_new_method_call(cstr(BUS), cstr(OBJ), cstr(SCREENCAST), cstr(method));
411
+ if (!msg) throw new PortalUnavailableError(`could not build the ${method} call`);
412
+ const iter = scratch(ITER_BYTES);
413
+ d.dbus_message_iter_init_append(msg, ptr(iter));
414
+ build(iter, token);
415
+ // `build` receives the token and must put it in the options dict as `handle_token`: it has
416
+ // to match the path subscribed above, or the Response is delivered somewhere nothing is
417
+ // listening and the call hangs until the deadline. `session_handle_token` on CreateSession
418
+ // is a *different* token naming the session, not the request.
419
+ const err = scratch(ERR_BYTES);
420
+ d.dbus_error_init(ptr(err));
421
+ const reply = d.dbus_connection_send_with_reply_and_block(conn, msg, 25_000, ptr(err));
422
+ d.dbus_message_unref(msg);
423
+ if (!reply) throw new PortalUnavailableError(`${method} was refused by the portal`);
424
+ d.dbus_message_unref(reply);
425
+
426
+ for (;;) {
427
+ if (Date.now() > deadline) {
428
+ throw new PortalUnavailableError(
429
+ `${method} timed out — the screen-share dialog on the host was not answered`,
430
+ );
431
+ }
432
+ // 200 ms so a dismissed dialog still unblocks the loop promptly; this runs on the event
433
+ // loop, and a long block here would stall the whole server.
434
+ d.dbus_connection_read_write(conn, 200);
435
+ for (;;) {
436
+ const sig = d.dbus_connection_pop_message(conn);
437
+ if (!sig) break;
438
+ const isResponse = d.dbus_message_is_signal(sig, cstr(REQUEST), cstr("Response")) === 1;
439
+ const path = isResponse ? String(d.dbus_message_get_path(sig) ?? "") : "";
440
+ if (isResponse && path === wantPath) {
441
+ const out = decodeResponse(d, sig);
442
+ d.dbus_message_unref(sig);
443
+ if (out.code !== 0) {
444
+ throw new PortalUnavailableError(
445
+ out.code === 1
446
+ ? `${method} was cancelled on the host (the dialog was dismissed)`
447
+ : `${method} failed with portal code ${out.code}`,
448
+ );
449
+ }
450
+ return out.results;
451
+ }
452
+ d.dbus_message_unref(sig);
453
+ }
454
+ await new Promise((r) => setTimeout(r, 0));
455
+ }
456
+ }
457
+
458
+ /** A Response signal is `(u a{sv})`: a result code then the method's own fields. */
459
+ function decodeResponse(
460
+ d: NonNullable<ReturnType<typeof dbus>>,
461
+ msg: Ptr,
462
+ ): { code: number; results: Record<string, unknown> } {
463
+ const iter = scratch(ITER_BYTES);
464
+ if (d.dbus_message_iter_init(msg, ptr(iter)) !== 1) return { code: -1, results: {} };
465
+ const code = readBasic(d, iter);
466
+ d.dbus_message_iter_next(ptr(iter));
467
+ const results: Record<string, unknown> = {};
468
+ readOptions(d, iter, (key, value) => {
469
+ results[key] = key === "streams" ? decodeStreams(d, value) : readBasic(d, value);
470
+ });
471
+ return { code: typeof code === "number" ? code : -1, results };
472
+ }
473
+
474
+ /** `streams` is `a(ua{sv})` — one entry per shared monitor, each a node id plus properties.
475
+ * Only `size` is read out of the properties; `position` matters for a multi-monitor crop,
476
+ * which the portal does not offer (it shares whole outputs, already cropped). */
477
+ function decodeStreams(
478
+ d: NonNullable<ReturnType<typeof dbus>>,
479
+ iter: Uint8Array,
480
+ ): Array<{ nodeId: number; width: number | null; height: number | null }> {
481
+ const out: Array<{ nodeId: number; width: number | null; height: number | null }> = [];
482
+ if (d.dbus_message_iter_get_arg_type(ptr(iter)) !== T_ARRAY) return out;
483
+ const arr = scratch(ITER_BYTES);
484
+ d.dbus_message_iter_recurse(ptr(iter), ptr(arr));
485
+ while (d.dbus_message_iter_get_arg_type(ptr(arr)) === T_STRUCT) {
486
+ const st = scratch(ITER_BYTES);
487
+ d.dbus_message_iter_recurse(ptr(arr), ptr(st));
488
+ const nodeId = readBasic(d, st);
489
+ d.dbus_message_iter_next(ptr(st));
490
+ let width: number | null = null;
491
+ let height: number | null = null;
492
+ readOptions(d, st, (key, value) => {
493
+ if (key !== "size") return;
494
+ // `size` is `(ii)`, a struct of two int32 — read through the same recurse.
495
+ if (d.dbus_message_iter_get_arg_type(ptr(value)) !== T_STRUCT) return;
496
+ const sz = scratch(ITER_BYTES);
497
+ d.dbus_message_iter_recurse(ptr(value), ptr(sz));
498
+ const w = new Int32Array(1);
499
+ d.dbus_message_iter_get_basic(ptr(sz), ptr(w));
500
+ d.dbus_message_iter_next(ptr(sz));
501
+ const h = new Int32Array(1);
502
+ d.dbus_message_iter_get_basic(ptr(sz), ptr(h));
503
+ width = w[0] || null;
504
+ height = h[0] || null;
505
+ });
506
+ if (typeof nodeId === "number") out.push({ nodeId, width, height });
507
+ d.dbus_message_iter_next(ptr(arr));
508
+ }
509
+ return out;
510
+ }
511
+
512
+ function stringField(results: Record<string, unknown>, key: string): string | null {
513
+ const v = results[key];
514
+ return typeof v === "string" && v ? v : null;
515
+ }
516
+
517
+ function firstStream(
518
+ results: Record<string, unknown>,
519
+ ): { nodeId: number; width: number | null; height: number | null } | null {
520
+ const streams = results.streams;
521
+ if (!Array.isArray(streams) || streams.length === 0) return null;
522
+ return streams[0] as { nodeId: number; width: number | null; height: number | null };
523
+ }
524
+
525
+ /** Is there a portal on this host at all? Used by the requirements checklist, which must be
526
+ * able to say "install xdg-desktop-portal" rather than failing at capture time. */
527
+ export function portalLibraryPresent(): boolean {
528
+ return dbus() !== null;
529
+ }
@@ -15,6 +15,7 @@ import { audioSupport, type AudioSupport } from "./remote-desktop-audio.ts";
15
15
  import { privacySupport, type PrivacySupport } from "./remote-desktop-privacy.ts";
16
16
  import { listHostResolutions, type HostResolutions } from "./remote-desktop-resolution.ts";
17
17
  import { detectLinuxSession, type LinuxSession } from "./remote-desktop-linux-session.ts";
18
+ import { gstElements, type GstElements } from "./remote-desktop-capture-wayland.ts";
18
19
  import { getX11 } from "./remote-desktop-x11.ts";
19
20
  import {
20
21
  MAC_PERMISSION_SETTINGS_URL,
@@ -131,22 +132,60 @@ function macPermissionRequirement(id: MacPermissionId, granted: boolean): Remote
131
132
  };
132
133
  }
133
134
 
134
- /** The session-type row. A Wayland host is *not* reported as an unsupported platform: nothing
135
- * about Linux is missing, one specific capture path is — and the user can satisfy it in place
136
- * by choosing X11 at the login screen, which is exactly what a requirement is for. Hiding the
137
- * entry instead would leave them with no explanation at all. */
138
- function linuxSessionRequirement(session: LinuxSession): RemoteDesktopRequirement {
139
- const x11 = session.kind === "x11";
140
- return {
141
- id: "linux-session",
142
- ok: x11,
135
+ /**
136
+ * What a Wayland host still needs for capture.
137
+ *
138
+ * Wayland used to be reported here as a hard blocker telling the user to log out and pick X11.
139
+ * It is not one any more: the desktop portal hands out a PipeWire node and GStreamer reads it
140
+ * (`remote-desktop-capture-wayland.ts`). What *can* be missing is the GStreamer side, and it is
141
+ * split into two rows because they are two different packages with two different install
142
+ * commands — a single "GStreamer" row would name a package that is already installed on a host
143
+ * missing only the other one.
144
+ *
145
+ * `pipewiresrc` is the one that decides whether capture is possible at all; the encoder row is
146
+ * separate because a host can have the source and no H.264 encoder, which fails later and for a
147
+ * reason the first row's install command cannot fix.
148
+ */
149
+ function waylandCaptureRequirements(els: GstElements): RemoteDesktopRequirement[] {
150
+ const rows: RemoteDesktopRequirement[] = [];
151
+ const haveSource = !!els.launch && els.pipewiresrc;
152
+ rows.push({
153
+ id: "gst-pipewire",
154
+ ok: haveSource,
143
155
  gates: "video",
144
- title: "X11 session",
145
- detail: "Screen capture currently needs an X11 session. This host is on Wayland, which only "
146
- + "shares the screen through the desktop portal — not supported yet. Log out and pick "
147
- + "\"Plasma (X11)\" / \"GNOME on Xorg\" at the login screen; mouse and keyboard already work here.",
148
- actions: [],
149
- };
156
+ title: "GStreamer PipeWire capture",
157
+ detail: "A Wayland compositor only shares the screen through the desktop portal, which hands "
158
+ + "back a PipeWire stream. PPM reads it with GStreamer — the same way RustDesk does. "
159
+ + "Install the tools and the PipeWire plugin, then come back; PPM re-checks automatically.",
160
+ actions: [linuxInstallAction(gstPipewirePackages())].filter((a): a is RequirementAction => a !== undefined),
161
+ });
162
+ if (haveSource && !els.vah264enc && !els.x264enc) {
163
+ rows.push({
164
+ id: "gst-h264",
165
+ ok: false,
166
+ gates: "video",
167
+ title: "A GStreamer H.264 encoder",
168
+ detail: "The screen is captured but nothing can encode it. `vah264enc` uses the GPU and is "
169
+ + "much cheaper; `x264enc` is the software fallback. Installing either is enough.",
170
+ actions: [linuxInstallAction(gstEncoderPackages())].filter((a): a is RequirementAction => a !== undefined),
171
+ });
172
+ }
173
+ return rows;
174
+ }
175
+
176
+ /** `gst-launch-1.0` plus the PipeWire plugin. Arch splits the tools out of the core package;
177
+ * Debian/Ubuntu call the plugin `gstreamer1.0-pipewire`; Fedora ships it as part of pipewire. */
178
+ function gstPipewirePackages(): string {
179
+ if (existsSync("/usr/bin/pacman")) return "gstreamer gst-plugin-pipewire";
180
+ if (existsSync("/usr/bin/apt")) return "gstreamer1.0-tools gstreamer1.0-pipewire";
181
+ return "gstreamer1 pipewire-gstreamer";
182
+ }
183
+
184
+ /** `vah264enc` lives in the bad set, `x264enc` in the ugly one; either satisfies the row. */
185
+ function gstEncoderPackages(): string {
186
+ if (existsSync("/usr/bin/pacman")) return "gst-plugins-bad gst-plugins-ugly";
187
+ if (existsSync("/usr/bin/apt")) return "gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly";
188
+ return "gstreamer1-plugins-bad-free gstreamer1-plugins-ugly-free";
150
189
  }
151
190
 
152
191
  /** uinput is how a Wayland session receives input, and the device is root-owned by default. */
@@ -220,14 +259,11 @@ function clipboardSupport(platform: NodeJS.Platform, session: LinuxSession | nul
220
259
  }
221
260
 
222
261
  async function linuxRequirements(session: LinuxSession): Promise<RemoteDesktopRequirement[]> {
223
- const rows: RemoteDesktopRequirement[] = [linuxSessionRequirement(session)];
224
262
  if (session.kind === "x11") {
225
263
  const conn = await getX11(session);
226
- rows.push(conn ? xtestRequirement(conn.hasXTest) : xlibRequirement(session.display));
227
- } else {
228
- rows.push(uinputRequirement());
264
+ return [conn ? xtestRequirement(conn.hasXTest) : xlibRequirement(session.display)];
229
265
  }
230
- return rows;
266
+ return [...waylandCaptureRequirements(await gstElements()), uinputRequirement()];
231
267
  }
232
268
 
233
269
  export async function remoteDesktopReadiness(