@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,203 @@
1
+ # Phase 3 report — Cài APK, chụp màn hình, logcat
2
+
3
+ Ngày: 2026-09-21. Tiếp sau `android-emulator-phase2-report-260921.md`.
4
+ Bản này ghi **những gì đo được**, trong đó có một RPC mà proto mô tả đầy đủ nhưng emulator
5
+ **không cài đặt** và hỏng theo kiểu im lặng, cùng một chỗ chính tôi viết comment ngược với code.
6
+
7
+ ## 1. Kết quả
8
+
9
+ | | |
10
+ |---|---|
11
+ | E2E mức dịch vụ (`tests/e2e/android-phase3-e2e.ts`) | **35/35** |
12
+ | E2E qua server thật (`tests/e2e/android-ws-e2e.ts`) | **35/35** (17 check mới của Phase 3) |
13
+ | E2E hai máy (`tests/e2e/android-two-device-install-e2e.ts`) | **9/9** |
14
+ | Unit mới | **48** (logcat parse 8, apk 16, log filter 18, screenshot download 6) — cộng dồn **116** test Android trên 13 file |
15
+ | `tsc --noEmit` | 0 lỗi |
16
+ | `bun run build:web` | chunk `android-tab-*.js` 37.17 kB (gzip 11.16) — vẫn lazy, không ai trả giá nếu không mở tab |
17
+
18
+ Số đo có ý nghĩa:
19
+
20
+ | | |
21
+ |---|---|
22
+ | Upload + install thật qua HTTP | **78.75 MB**, upload → operation → `succeeded`, staging sạch sau đó |
23
+ | Screenshot PNG | 1.87–1.94 MB, 1080×2400, emulator tự encode |
24
+ | Logcat qua WS | 14 batch / 82 entry trong ~4 s, cửa sổ gộp 120 ms |
25
+ | `getScreenshot` khi stream video đang chạy | 90 → 164 source frame, **không làm gián đoạn** |
26
+
27
+ ## 2. `sort: Parsed` không tồn tại, và nó hỏng bằng cách im lặng
28
+
29
+ `emulator_controller.proto` có `LogMessage.LogType.Parsed`, trả về `LogcatEntry` đã tách sẵn
30
+ level, tag, pid, tid — đúng thứ panel cần. Đo trên **emulator 36.5.10.0**:
31
+
32
+ | Yêu cầu | Kết quả trong 8 giây |
33
+ |---|---|
34
+ | `streamLogcat({sort: "Parsed"})` | **334 message**, `entries` rỗng **và** `contents` rỗng, mỗi reply echo `sort: Text` |
35
+ | `streamLogcat({sort: 1})` (enum số) | 57 message, cũng rỗng hoàn toàn |
36
+ | `streamLogcat({})` (mặc định `Text`) | 41 message, **18 083 ký tự log thật** |
37
+ | `getLogcat` (unary, đã deprecated) | **`12 UNIMPLEMENTED`** |
38
+
39
+ Điểm đáng ghi không phải "Parsed hỏng" mà là **cách nó hỏng**: không lỗi, không `UNIMPLEMENTED`,
40
+ chỉ là một stream bận rộn chở không có gì. Nhìn từ UI nó giống hệt một máy không log. Nếu tin
41
+ theo proto thì panel logcat sẽ trống vĩnh viễn trên mọi máy và không có chỗ nào để bắt đầu tìm.
42
+
43
+ Hệ quả thiết kế: `android-logcat.ts` xin `Text` và **tự parse**. Hai chi tiết nữa đo được, và
44
+ chúng quyết định hình dạng parser:
45
+
46
+ * Mỗi message gRPC mang **đúng một dòng**, **không có `\n` cuối** — nên không cần buffer dòng dở.
47
+ (Vẫn `split("\n")` phòng bản emulator sau gộp dòng, vì nếu gộp mà không split thì nhiều dòng
48
+ sẽ dính thành một entry, lại là một lỗi im lặng nữa.)
49
+ * Định dạng là `threadtime` của logcat: `MM-DD HH:MM:SS.mmm PID TID L TAG: message` — **không có
50
+ năm**. Parser lấy năm hiện tại, và lùi một năm nếu như thế đẩy dòng log ra tương lai quá một
51
+ ngày (đọc log tháng 12 vào ngày 1 tháng 1).
52
+
53
+ Dòng không parse được thì **giữ nguyên**, không bỏ: đó là dòng nối của stack trace hoặc banner
54
+ `--------- beginning of main`, mất dòng nào cũng là cắt đôi một crash.
55
+
56
+ ### 2.1 Lệch so với plan, có chủ ý
57
+
58
+ Plan §4 viết "ADB dùng discovery/boot readiness, cài APK và logcat". Logcat ở đây đi qua **gRPC
59
+ `streamLogcat`**, không phải `adb logcat`. Lý do:
60
+
61
+ * gRPC dùng lại đúng channel đã xác thực của session — không thêm một tiến trình con mỗi máy.
62
+ * `adb logcat` cần adb serial, mà serial là `null` trong lúc máy còn boot; channel gRPC thì đã có.
63
+ * Theo chính comment trong proto, `streamLogcat` **chạy `logcat` trong guest qua `AdbShellStream`**
64
+ — tức dữ liệu là một, chỉ khác đường vận chuyển.
65
+
66
+ Cái mất: không đẩy được filter xuống thiết bị (`adb logcat -s TAG:E`), nên mọi dòng đều qua dây
67
+ rồi mới lọc ở client. Đo thực tế ~40 message/8 s nên chưa đáng đổi. Cài APK thì **vẫn đi adb**
68
+ đúng như plan, vì `EmulatorController` không có RPC install nào cả.
69
+
70
+ ## 3. `adb install` không chọn bừa — nó từ chối
71
+
72
+ Comment đầu tiên tôi viết trong `android-apk.ts` nói `adb install` không `-s` sẽ "chọn máy nào
73
+ tuỳ nó thích". Đo với hai emulator đang chạy:
74
+
75
+ ```
76
+ $ adb install <file>
77
+ adb: more than one device/emulator
78
+ ```
79
+
80
+ Nó **từ chối, không cài gì cả**. Sửa lại comment. Điều này làm `-s` còn quan trọng hơn: trên máy
81
+ một emulator thì thiếu `-s` vẫn chạy **nhờ may**, và hỏng đúng vào ngày người dùng mở máy thứ hai.
82
+
83
+ Gate "install đúng target khi có hai máy" được chứng minh bằng
84
+ `tests/e2e/android-two-device-install-e2e.ts`, 9/9. Chỗ khó là **làm cho test có thể fail**: hai
85
+ AVD spike dùng chung một system image nên danh sách package giống hệt nhau, và cài một gói cả
86
+ hai đều có sẵn thì pass dù `-s` có được tôn trọng hay không. Test tự tạo chênh lệch bằng
87
+ `pm uninstall --user 0 com.google.android.deskclock` trên máy đích (`--user 0` vì đây là app hệ
88
+ thống, uninstall thường bị từ chối), rồi kiểm tra cả hai máy trước và sau, và trả lại nguyên trạng.
89
+
90
+ ## 4. Huỷ upload không để lại temp
91
+
92
+ Gate của plan. Không tự nhiên mà có: request bị abort làm phép đọc **fail giữa chừng file**, nên
93
+ phần đã ghi phải bị xoá trên mọi nhánh lỗi.
94
+
95
+ `stageApkUpload` xoá file dở khi abort, khi quá hạn mức, và khi header không phải zip — kiểm tra
96
+ 4 byte đầu **trong lúc** stream, nên một body 5 GB bị cắt tại ngưỡng chứ không phải sau khi đã
97
+ ghi xong. PPM bị kill giữa chừng upload thì `sweepApkStaging()` dọn ở lần sau (quá 1 giờ).
98
+
99
+ Đo qua server thật: abort giữa upload → `AbortError`, thư mục staging **0 file**; upload 78.75 MB
100
+ thành công → install `succeeded` → staging lại **0 file**.
101
+
102
+ ## 5. Giết tiến trình **không** làm đóng pipe của nó
103
+
104
+ Deadline cho `adb install` (plan §7 yêu cầu) tưởng là một dòng `setTimeout(() => proc.kill())`.
105
+ Test bằng một `adb` giả là script `#!/bin/sh\nsleep 30`: kill xong, `reader.read()` **vẫn không
106
+ bao giờ resolve** — `sh` chết nhưng `sleep` con thừa kế pipe và giữ nó mở. Test timeout 5 s.
107
+
108
+ Tức là bản đầu tiên của tôi có deadline nhưng deadline không thoát được: hàm vẫn treo đúng chỗ nó
109
+ định cứu. Sửa bằng cùng hình dạng `Encoder.stop()` trong `android-video.ts` đã phải dùng cho
110
+ ffmpeg — **race**, không chờ:
111
+
112
+ ```ts
113
+ const outcome = await Promise.race([collected, expired, aborted]);
114
+ ```
115
+
116
+ `cancelled` cũng phải nằm trong race vì cùng một lý do: một cancel chờ read kết thúc sẽ treo tới
117
+ tận deadline 10 phút thay vì trả lời ngay. Hai unit test giữ cả hai nhánh, và cả hai đều **đo thời
118
+ gian** chứ không chỉ đo kết quả — một bản sửa sai vẫn trả đúng message, chỉ là sau 10 phút.
119
+
120
+ ## 6. `DeviceEntry.state` không bao giờ báo `booting`
121
+
122
+ Plan §6 nói "install APK chỉ khi guest ready". Cách hiển nhiên là `if (device.state !== "ready")`.
123
+ Nhưng `listDevices` là hàm **đồng bộ** và đặt `state: e ? "ready" : "stopped"` — tức mọi máy có
124
+ tiến trình đều là `ready`, và giá trị `"booting"` khai báo ở dòng 27 của `device-registry.ts`
125
+ chưa từng được sinh ra. Check đó là một tautology.
126
+
127
+ Câu trả lời thật là `getStatus().booted` của chính emulator, tốn một RPC trước mỗi lần install.
128
+ Đây là lỗ hổng của Phase 1, không sửa ở Phase 3 vì làm `listDevices` thành async sẽ chạm mọi
129
+ caller; ghi lại ở đây để Phase 4 xử lý khi làm device manager.
130
+
131
+ ## 7. Chỗ tôi viết comment ngược với code, và e2e bắt được
132
+
133
+ `case "logcat"` trong `android-session.ts` nằm **dưới** cổng kiểm tra lease, trong khi comment
134
+ ngay trên nó viết "Not gated on the controller lease". Tức là viewer không giữ lease — đúng
135
+ người cần đọc log khi người khác đang điều khiển — bị từ chối im lặng.
136
+
137
+ Không unit test nào bắt được: nó chỉ sai khi có **hai** viewer và lease đã chuyển. `android-ws-e2e.ts`
138
+ gửi `logcat` từ viewer A **sau khi** B đã cướp lease, và trả về `0 batches, 0 entries`. Đây là lý
139
+ do e2e hai-viewer đáng viết chứ không phải là thừa.
140
+
141
+ ## 8. Quyết định thiết kế
142
+
143
+ * **Ring buffer sống qua lần đóng panel.** Gate nói "logs hidden ngừng subscription" — stream gRPC
144
+ dừng thật. Nhưng vứt luôn backlog là chuyện khác và tệ hơn: đóng panel để đọc stack trace ở chỗ
145
+ khác rồi mở lại sẽ thấy hộp rỗng. Ring (2000 entry) chỉ mất khi session của máy đóng. Giá phải
146
+ trả: khoảng trống trong log ở quãng không ai xem — timestamp nói rõ điều đó.
147
+ * **Batch log 120 ms.** Một WS frame mỗi message gRPC sẽ vừa ngập socket vừa tranh chỗ với video
148
+ trên cùng một dây. Viewer nào đang tắc socket thì bỏ qua batch — ring ở server giữ lại rồi.
149
+ * **Screenshot là PNG, không phải RGBA thô.** Emulator tự encode, nên không chỗ nào ở đây phải
150
+ biết layout buffer — mà đó đúng là chỗ proto nói sai (Phase 2 §2.1: proto ghi "bottom up",
151
+ đo ra top-down). File người dùng tải về không phải chỗ để phát hiện lại chuyện đó.
152
+ * **Panel công cụ nằm **dưới** màn hình ở mọi kích thước.** Không phải nhượng bộ cho mobile: log
153
+ phải đọc được *đồng thời* với việc chạm vào guest, mà modal thì không — và dưới cùng cũng là
154
+ thumb zone mà design guidelines yêu cầu, nên layout điện thoại ra từ cùng một quyết định.
155
+ * **Không panel nào được mount khi tab của nó đóng**, nên subscription logcat bám theo panel:
156
+ chuyển sang tab Install là dừng stream log thật sự.
157
+ * **Cài từ project nhận `{project, path}`, không nhận đường dẫn tuyệt đối.** Nhận tuyệt đối thì
158
+ route này thành trình đọc file tuỳ ý. Path được resolve *bên trong* project rồi từ chối nếu
159
+ thoát ra (400) — cùng luật `extension-rpc-handlers.ts` đang dùng.
160
+ * **Quét APK trong project bị giới hạn**: sâu tối đa 8 cấp, prune `node_modules`/`.git`/`.gradle`…
161
+ Cùng lý do với lệnh cấm `fs.watch` đệ quy trong CLAUDE.md.
162
+ * **Hai hạn mức, không phải một.** `MAX_APK_BYTES` (2 GB) chặn một file vô lý; `MAX_STAGING_BYTES`
163
+ (4 GB) chặn nhiều file hợp lệ cùng lúc. Thiếu cái thứ hai thì hai upload 2 GB song song đều hợp
164
+ lệ theo hạn mức riêng và cùng nhau làm đầy đĩa mà database đang nằm trên đó.
165
+
166
+ ## 9. Không log nội dung input/clipboard
167
+
168
+ Gate cuối. Kiểm tra bằng hai cách: `grep` mọi `console.*` trong `src/services/android/`,
169
+ `src/server/ws/android.ts`, `src/server/routes/android.ts` — ba chỗ duy nhất đều là thông báo lỗi
170
+ (`could not persist ownership`, `logcat stream … failed`, `failed to start a session`), không chỗ
171
+ nào chạm vào `text`/`entries`. Và grep chuỗi bí mật của e2e clipboard trong log server sau khi
172
+ chạy: **0 lần xuất hiện**.
173
+
174
+ ## 10. Chưa đo — không được coi là đã đạt
175
+
176
+ * Split APK (`install-multiple`). Plan cho phép bỏ qua nếu chưa có workflow rõ ràng; hiện **không
177
+ hỗ trợ**, và một file `.apks`/`.aab` sẽ bị adb từ chối với `INSTALL_FAILED_INVALID_APK` — thông
178
+ báo có giải thích, nhưng vẫn là từ chối.
179
+ * Cài APK **lỗi ABI / minSdk / chữ ký thật**. Bảng `FAILURES` được unit test bằng output adb dựng
180
+ sẵn, chưa chạy với APK thật gây ra từng lỗi đó (cần APK arm-only và APK ký khác — chưa có).
181
+ * Upload qua tunnel Cloudflare (timeout idle của proxy với body 78 MB).
182
+ * Logcat trên máy **nói nhiều** (app đang crash-loop): mới đo ~40 msg/8 s trên máy nhàn rỗi.
183
+ * Panel trên trình duyệt mobile thật.
184
+
185
+ ## 11. Tái lập
186
+
187
+ ```bash
188
+ # 1. AVD thử nghiệm riêng, không đụng Pixel_9 / Pixel_Tablet của người dùng
189
+ bash spikes/android/boot-test-avd.sh &
190
+ AVD=ppm_spike_test2 LOG=/tmp/ppm-android-spike/emulator2.log bash spikes/android/boot-test-avd.sh &
191
+
192
+ # 2. E2E mức dịch vụ
193
+ ANDROID_EMULATOR_ENABLED=1 PPM_HOME=$(mktemp -d) bun tests/e2e/android-phase3-e2e.ts
194
+
195
+ # 3. Gate hai máy
196
+ ANDROID_EMULATOR_ENABLED=1 PPM_HOME=$(mktemp -d) bun tests/e2e/android-two-device-install-e2e.ts
197
+
198
+ # 4. E2E qua server thật (PPM_SERVER_HOME để check staging đúng PPM_HOME của server)
199
+ SRV=$(mktemp -d)
200
+ ANDROID_EMULATOR_ENABLED=1 PPM_HOME=$SRV bun src/server/index.ts __serve__ 8099 127.0.0.1 &
201
+ PPM_SERVER_HOME=$SRV PPM_BASE=http://127.0.0.1:8099 PPM_TOKEN=<token> \
202
+ PPM_HOME=$(mktemp -d) bun tests/e2e/android-ws-e2e.ts
203
+ ```
@@ -0,0 +1,191 @@
1
+ # Phase 4 report — Tạo, wipe, xoá AVD ngay trong PPM
2
+
3
+ Ngày: 2026-09-21. Tiếp sau `android-emulator-phase3-report-260921.md`.
4
+
5
+ Bản này ghi **những gì đo được**. Trong Phase 4 có ba lỗi đáng kể, và cả ba đều **im lặng**:
6
+ một lỗi ghi AVD vào thư mục AVD **thật của người dùng**, một lỗi khiến mọi emulator đang chạy
7
+ đều bị báo là "không ai khoá", và một lỗi của chính bài test chứ không phải của sản phẩm.
8
+
9
+ ## 1. Kết quả
10
+
11
+ | | |
12
+ |---|---|
13
+ | E2E gate Phase 4 (`tests/e2e/android-avd-crud-e2e.ts`) | **34/34** — tạo → boot → ghi dữ liệu → stop → boot lại → wipe → boot lại → xoá |
14
+ | E2E qua server thật (`tests/e2e/android-ws-e2e.ts`) | **48/48** — 34 check cũ + **14 check mới** cho route Phase 4 |
15
+ | Unit mới | **34** (avd-manager 20, system-images 13, lock NUL 1) — cộng dồn **143** test Android trên 15 file |
16
+ | `tsc --noEmit` | 0 lỗi |
17
+ | `bun run build:web` | chunk `android-tab-*.js` **48.35 kB** (gzip 14.08) — tăng ~11 kB so với Phase 3, vẫn lazy |
18
+ | Toàn bộ `bun test` | 7146 pass / 33 fail — **không có test Android nào đỏ**; 33 lỗi nằm trong các nhóm đã biết từ trước (codex 18, tunnel 7, cloud-ws 2, 6 lỗi lẻ), số lượng dao động giữa các lần chạy ở nhóm tunnel |
19
+
20
+ Số đo có ý nghĩa:
21
+
22
+ - `avdmanager list device` mất **~730 ms** lần đầu và **0 ms** sau đó (cache trong tiến trình);
23
+ 88 profile, trong đó 39 phone và 13 tablet.
24
+ - Đọc system image từ **đĩa** thay vì `sdkmanager --list_installed`: tức thì, không có thanh
25
+ tiến trình phải bóc tách, và vẫn trả lời đúng trên máy có image nhưng **không có** cmdline-tools.
26
+ - AVD tạo bởi PPM: boot lần đầu **25 s**, boot lại từ snapshot **3 s**, boot sau wipe **24 s**.
27
+ - Một wipe giải phóng **3.2 GB** trên AVD đã boot hai lần.
28
+
29
+ ## 2. `ANDROID_AVD_HOME` trỏ vào thư mục **không tồn tại** thì avdmanager ghi vào `~/.android/avd`
30
+
31
+ Đây là biến thể thứ hai của cùng một cái bẫy, và nó nguy hiểm hơn cái đầu.
32
+
33
+ - Lần đầu (đã ghi ở Phase 4 khi đang code): **không** set `ANDROID_AVD_HOME` cho tiến trình con
34
+ thì `avdmanager` ghi AVD vào `~/.android/avd` — đúng thư mục AVD thật của người dùng. Sửa bằng
35
+ `avdHomeEnv()` trong `sdk-discovery.ts`, dùng chung với `emulator-launcher.ts`.
36
+ - Lần hai: **có** set `ANDROID_AVD_HOME`, nhưng trỏ vào thư mục vừa bị xoá. `avdmanager` **không
37
+ báo lỗi** — nó lặng lẽ quay về `~/.android/avd` và ghi AVD ở đó. Phát hiện bằng
38
+ `find ~ -maxdepth 4 -name 'ppm_wipe_probe*'`.
39
+
40
+ Cả hai lần đều không đụng `Pixel_9` / `Pixel_Tablet`; AVD lạc đã được xoá tay ngay.
41
+
42
+ Sửa: một dòng `mkdirSync(opts.avdHome, { recursive: true })` trước khi chạy tool. Kiểm tra
43
+ "config.ini có xuất hiện đúng chỗ không" ở cuối `createAvd` vẫn giữ nguyên — nó chính là thứ đã
44
+ **bắt được** cả hai lần, nên nó không phải kiểm tra thừa.
45
+
46
+ Bài học chung: một biến môi trường kiểu "thay thế đường dẫn" cần thư mục **có thật**; nếu không,
47
+ tool sẽ dùng mặc định và trông như đang hoạt động bình thường.
48
+
49
+ ## 3. File lock của emulator kết thúc bằng NUL, nên `trim()` không cứu được
50
+
51
+ `hardware-qemu.ini.lock` của một emulator đang chạy, đọc bằng `xxd`:
52
+
53
+ ```
54
+ 00000000: 3531 3937 3732 00 519772.
55
+ ```
56
+
57
+ 7 byte: `519772` rồi **một byte NUL**. `String.prototype.trim()` chỉ bỏ khoảng trắng, mà NUL
58
+ không phải khoảng trắng — nên `Number("519772\0")` là `NaN`, `Number.isInteger(NaN)` là `false`,
59
+ và `isLocked()` trả `false` cho **mọi** emulator đang chạy.
60
+
61
+ Hậu quả, tất cả đều im lặng:
62
+
63
+ - Dòng cảnh báo "Locked by another process — it is probably open in Android Studio" trong danh
64
+ sách thiết bị **chưa từng hiện**.
65
+ - `assertDestructible()` của Phase 4 có một lớp bảo vệ không hoạt động (lớp "đang chạy" vẫn
66
+ hoạt động, vì nó quét tiến trình chứ không đọc lock).
67
+
68
+ Lý do nó sống sót qua Phase 1: unit test tự viết fixture `` `${process.pid} ` `` — pid kèm một
69
+ **dấu cách**. `trim()` xử lý được dấu cách, nên bài test xanh với đúng giả định sai của người
70
+ viết nó. Bài test mới ghi đúng thứ emulator ghi (`${pid}\0`) và đỏ trước khi sửa.
71
+
72
+ Sửa: lấy cụm số ở đầu file (`/^\s*(\d+)/`) thay vì tin vào `trim()`.
73
+
74
+ Kiểm tra lại trên máy thật, có một emulator đang chạy:
75
+
76
+ ```
77
+ ppm_spike_test locked=true ← đang chạy
78
+ ppm_spike_test2 locked=false
79
+ Pixel_9 locked=false
80
+ Pixel_Tablet locked=false
81
+ ```
82
+
83
+ ## 4. `userdata.img` **không** được xoá khi wipe — tài liệu của chính emulator nói vậy
84
+
85
+ Danh sách wipe ban đầu có `userdata.img`. `emulator -help-disk-images` xếp nó vào nhóm ảnh
86
+ **khởi tạo**, cùng chỗ với `system.img`, và định nghĩa wipe là:
87
+
88
+ > `-wipe-data` Copy the content of the *initial* user data image (userdata.img) into the
89
+ > writable one (userdata-qemu.img)
90
+
91
+ Tức là xoá `userdata.img` chính là xoá bản gốc mà một lần wipe cần để khôi phục.
92
+
93
+ Đo thêm cho đủ: AVD tạo bằng tooling hiện tại trên máy này **không có** `userdata.img` trong thư
94
+ mục AVD (nó nằm trong thư mục system image), nên lỗi này không biểu hiện ở đây — boot sau wipe
95
+ vẫn 24 s. Nó chỉ cắn một AVD do tooling cũ tạo ra, im lặng, ở lần boot kế tiếp. Đúng loại việc
96
+ mà danh sách cho phép (allowlist) sinh ra để tránh.
97
+
98
+ ## 5. Ba lỗi của **bài test**, không phải của sản phẩm
99
+
100
+ 1. **`adb shell sh -c "echo X > file"`** — `adb` nối các tham số bằng dấu cách rồi đưa cả chuỗi
101
+ cho shell **của máy ảo**, nên dấu `>` bị shell đó nuốt: `sh -c echo` chạy với `echo` là lệnh và
102
+ ghi một dòng trống. File có thật, nội dung rỗng, và hai check "ghi được file" + "dữ liệu còn
103
+ sau khi stop" cùng đỏ. Sửa: truyền **một** tham số duy nhất.
104
+ 2. **`boot()` nuốt output của emulator** — một lần boot không xong chỉ báo đúng chữ `no`, tức là
105
+ thứ duy nhất đáng quan tâm thì không có. Giờ mỗi lần boot ghi ra một file log và phần tổng kết
106
+ in đường dẫn của những lần boot hỏng.
107
+ 3. **Một lần "boot sau wipe" hỏng không tái lập được** — chạy lại đúng kịch bản đó bằng một
108
+ probe riêng (tạo → boot → ghi → stop → wipe → boot) thì boot lần hai mất 24 s và thành công;
109
+ lần chạy e2e kế tiếp cũng qua. Ghi lại ở đây thay vì tuyên bố là đã hiểu: nó **chưa được giải
110
+ thích**, chỉ là không lặp lại.
111
+
112
+ ## 6. Quyết định thiết kế
113
+
114
+ - **PPM không tải system image.** `/system-images` đọc cây `system-images/<api>/<tag>/<abi>/
115
+ source.properties` trên đĩa. Không có nút tải, không tự chấp nhận licence — đúng plan Phase 4.
116
+ Image sai CPU vẫn được **liệt kê** nhưng không chọn được, kèm lý do ngay trên dòng đó: ẩn đi thì
117
+ người dùng biết mình đã cài mà không thấy, và sẽ nghĩ là lỗi.
118
+ - **Bắt buộc có device profile.** `avdmanager create avd` không `--device` cho ra AVD **320x640**
119
+ (đã đo ở Phase 4 khi code), trông như emulator hỏng chứ không như thiếu tham số.
120
+ - **`hw.keyboard=yes` luôn được ghi** sau khi tạo. `avdmanager` ghi `no`; với `no` thì `sendKey`
121
+ trả OK và không một ký tự nào tới máy ảo (đo ở Phase 0).
122
+ - **Không `--force`.** Trùng tên bị từ chối, vì ghi đè một AVD là hành vi phá huỷ đội lốt tạo mới.
123
+ - **Tên được kiểm tra, không được escape.** Regex nằm ở `src/shared/android-avd.ts` và **dùng
124
+ chung** cho form lẫn host — hai bản sao của một regex kiểm tra là hai bản sẽ lệch nhau, và cách
125
+ chúng lệch là form nhận một cái tên rồi host từ chối sau khi hộp thoại đã đóng.
126
+ - **Wipe là allowlist chứ không phải glob.** File nào code này chưa biết thì **giữ lại**.
127
+ - **Wipe/Delete: gõ lại đúng tên máy.** Không phải checkbox — các dòng trong danh sách nhìn giống
128
+ hệt nhau, và checkbox chỉ xác nhận rằng có một hộp thoại đang mở, không xác nhận nó mở trên máy nào.
129
+ - **Nút ba chấm nhìn thấy được**, không phải right-click hay long-press: wipe và delete không có
130
+ đường nào khác, mà một cử chỉ ẩn thì không phải là một đường đi. Nó là control riêng nên không
131
+ cướp mất cú chạm của cả dòng (bẫy đã ghi trong CLAUDE.md).
132
+
133
+ ## 7. UI đo trên trình duyệt thật, không chỉ đọc code
134
+
135
+ Chạy PPM đã build (`bun run build:web` rồi serve `dist/web`) và lái bằng Playwright, ở **390x844**:
136
+
137
+ | | |
138
+ |---|---|
139
+ | Hộp thoại tạo máy dưới `md` | là **bottom sheet** thật (đáy dính đáy màn hình) |
140
+ | Tràn ngang | **không** (`scrollWidth === innerWidth`) |
141
+ | Vùng chạm | 7/7 control **44 px** — nhưng chỉ sau khi sửa: trigger của `SearchSelect` mặc định là `h-10 md:h-7`, tức **40 px**, dưới ngưỡng. Đo mới thấy, đọc code thì không. |
142
+ | Nút ba chấm mỗi dòng | 44x44 |
143
+ | Mục menu | "Wipe data" 44 px, "Delete device" 44 px và `data-variant="destructive"` |
144
+ | Sheet xác nhận | nút vẫn **disabled** khi gõ tên sai (`Pixel_9_wrong`) và khi thiếu **một ký tự** (`ppm_ui_prob`) |
145
+
146
+ Và chạy trọn vòng **qua chính UI** với server thật: tạo `ppm_ui_probe` → nó hiện trong danh sách
147
+ (API 37, 1080x2424, **không** có cảnh báo thiếu hardware keyboard, tức `hw.keyboard=yes` đã vào)
148
+ → xoá bằng cách gõ đúng tên → biến mất. `Pixel_9` (26 file) và `Pixel_Tablet` (22 file) nguyên vẹn.
149
+
150
+ Một quan sát phụ, tình cờ: `vite build` xoá `dist/web` nên tab đang mở mất chunk — và màn hình
151
+ khôi phục chunk (`root-error-boundary` + watchdog) đã bắt đúng, hiện nút Reload thay vì trắng trang.
152
+
153
+ ## 8. Cách viết test phá huỷ mà không phá gì
154
+
155
+ Trong `android-ws-e2e.ts` (chạy với AVD **thật** của người dùng) có check "không wipe được máy
156
+ đang chạy". Nó gửi kèm `confirmName` **sai**:
157
+
158
+ ```ts
159
+ { confirmName: "definitely-not-its-name" }
160
+ ```
161
+
162
+ rồi mới khẳng định thông báo lỗi nói về *đang chạy*. Nghĩa là phải **hai** lớp bảo vệ cùng hỏng
163
+ mới mất dữ liệu. Một bài test có thể phá dữ liệu người dùng khi code hỏng thì không đáng viết.
164
+
165
+ Còn gate thật của Phase 4 (tạo/wipe/xoá thật) nằm ở `android-avd-crud-e2e.ts`, chạy trong AVD
166
+ home riêng `~/.android-phase4-avd` và tự dọn — không đụng `Pixel_9` / `Pixel_Tablet` (plan §9).
167
+
168
+ ## 9. Chưa đo — không được coi là đã đạt
169
+
170
+ - Chưa thử trên Windows / macOS: `avdmanager` là `.bat` trên Windows và cách nó nhận stdin `no\n`
171
+ chưa được kiểm tra ở đó.
172
+ - Chưa thử với AVD do **Android Studio** đang mở (lock thật của Studio, chứ không phải của
173
+ emulator do PPM khởi động) — giờ mới có `isLocked` đúng để thử.
174
+ - Chưa thử tạo AVD khi ổ đĩa đầy, và chưa thử `sdCardMb: 0` trên máy thật.
175
+ - Chưa đo p50/p95 click-to-visible trên LAN và qua tunnel (vẫn là mục còn nợ từ §10 của plan).
176
+
177
+ ## 10. Tái lập
178
+
179
+ ```bash
180
+ # Gate Phase 4 — mất vài phút, 4 lần boot, AVD home riêng
181
+ ANDROID_EMULATOR_ENABLED=1 PPM_E2E_LOG_DIR=/tmp/p4logs PPM_HOME=$(mktemp -d) \
182
+ bun tests/e2e/android-avd-crud-e2e.ts
183
+
184
+ # Route Phase 4 qua server thật
185
+ ANDROID_EMULATOR_ENABLED=1 bun src/server/index.ts __serve__ 8099 127.0.0.1 dev
186
+ PPM_BASE=http://127.0.0.1:8099 PPM_TOKEN=<token> PPM_HOME=$(mktemp -d) \
187
+ bun tests/e2e/android-ws-e2e.ts
188
+
189
+ # Unit
190
+ bun test tests/unit/android tests/unit/web/android-*.test.ts
191
+ ```