@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.
- package/CHANGELOG.md +18 -0
- package/CLAUDE.md +13 -2
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/cli-reference.md +4 -4
- package/assets/skills/ppm/references/http-api.md +37 -1
- package/bun.lock +34 -0
- package/dist/web/assets/accounts-settings-section-CGe5ikvs.js +1 -0
- package/dist/web/assets/accounts-settings-section-CGe5ikvs.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-CGe5ikvs.js.gz +0 -0
- package/dist/web/assets/{agent-session-sheet-CBMsnl_p.js → agent-session-sheet-vBhfHYrC.js} +2 -2
- package/dist/web/assets/agent-session-sheet-vBhfHYrC.js.br +0 -0
- package/dist/web/assets/agent-session-sheet-vBhfHYrC.js.gz +0 -0
- package/dist/web/assets/{agent-session-window-content-D9C9FA7E.js → agent-session-window-content-BxIxDN5i.js} +1 -1
- package/dist/web/assets/agent-session-window-content-BxIxDN5i.js.br +0 -0
- package/dist/web/assets/agent-session-window-content-BxIxDN5i.js.gz +0 -0
- package/dist/web/assets/{ai-resource-editor-DhUVgDEG.js → ai-resource-editor-Y8clBPRv.js} +1 -1
- package/dist/web/assets/ai-resource-editor-Y8clBPRv.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-Y8clBPRv.js.gz +0 -0
- package/dist/web/assets/{ai-settings-section-DOckWvp4.js → ai-settings-section-BB8vscte.js} +1 -1
- package/dist/web/assets/{ai-settings-section-CETpSpYd.js → ai-settings-section-Ck8uN_Bw.js} +1 -1
- package/dist/web/assets/ai-settings-section-Ck8uN_Bw.js.br +0 -0
- package/dist/web/assets/ai-settings-section-Ck8uN_Bw.js.gz +0 -0
- package/dist/web/assets/android-tab-BIOUVVoW.js +2 -0
- package/dist/web/assets/android-tab-BIOUVVoW.js.br +0 -0
- package/dist/web/assets/android-tab-BIOUVVoW.js.gz +0 -0
- package/dist/web/assets/{api-settings-JIgKfcCx.js → api-settings-D1axn9vm.js} +1 -1
- package/dist/web/assets/{appearance-settings-section-BJUiLUb7.js → appearance-settings-section-Bv5_n9lH.js} +1 -1
- package/dist/web/assets/appearance-settings-section-Bv5_n9lH.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-Bv5_n9lH.js.gz +0 -0
- package/dist/web/assets/{apps-panel-1VElRgV3.js → apps-panel-DXHF0QEv.js} +1 -1
- package/dist/web/assets/apps-panel-DXHF0QEv.js.br +0 -0
- package/dist/web/assets/apps-panel-DXHF0QEv.js.gz +0 -0
- package/dist/web/assets/{audio-preview-DYkyHu5X.js → audio-preview-DpjOKEm4.js} +1 -1
- package/dist/web/assets/audio-preview-DpjOKEm4.js.br +0 -0
- package/dist/web/assets/audio-preview-DpjOKEm4.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-4dgMNadQ.js +1 -0
- package/dist/web/assets/branch-review-tab-4dgMNadQ.js.br +0 -0
- package/dist/web/assets/branch-review-tab-4dgMNadQ.js.gz +0 -0
- package/dist/web/assets/chat-tab-Bbe_77oJ.js +15 -0
- package/dist/web/assets/chat-tab-Bbe_77oJ.js.br +0 -0
- package/dist/web/assets/chat-tab-Bbe_77oJ.js.gz +0 -0
- package/dist/web/assets/chat-tab-D3i5O3dO.js +1 -0
- package/dist/web/assets/chat-tab-D3i5O3dO.js.br +0 -0
- package/dist/web/assets/chat-tab-D3i5O3dO.js.gz +0 -0
- package/dist/web/assets/code-editor-DXsIWoQ1.js +7 -0
- package/dist/web/assets/code-editor-DXsIWoQ1.js.br +0 -0
- package/dist/web/assets/code-editor-DXsIWoQ1.js.gz +0 -0
- package/dist/web/assets/{codex-reset-credit-button-Fr45FUxi.js → codex-reset-credit-button-DRhievFQ.js} +1 -1
- package/dist/web/assets/codex-reset-credit-button-DRhievFQ.js.br +0 -0
- package/dist/web/assets/codex-reset-credit-button-DRhievFQ.js.gz +0 -0
- package/dist/web/assets/{conflict-editor-B-wdjSXI.js → conflict-editor-D_RImo_V.js} +3 -3
- package/dist/web/assets/conflict-editor-D_RImo_V.js.br +0 -0
- package/dist/web/assets/conflict-editor-D_RImo_V.js.gz +0 -0
- package/dist/web/assets/{csv-preview-B6RodObB.js → csv-preview-BuFdfLo3.js} +1 -1
- package/dist/web/assets/csv-preview-BuFdfLo3.js.br +0 -0
- package/dist/web/assets/csv-preview-BuFdfLo3.js.gz +0 -0
- package/dist/web/assets/{database-viewer-CdPBUQPY.js → database-viewer-ChcbYn9E.js} +1 -1
- package/dist/web/assets/database-viewer-ChcbYn9E.js.br +0 -0
- package/dist/web/assets/database-viewer-ChcbYn9E.js.gz +0 -0
- package/dist/web/assets/{design-settings-section-BpCpaBwL.js → design-settings-section-B_xOf51h.js} +1 -1
- package/dist/web/assets/design-settings-section-B_xOf51h.js.br +0 -0
- package/dist/web/assets/design-settings-section-B_xOf51h.js.gz +0 -0
- package/dist/web/assets/{design-skill-suggestion-DkT2E2IV.js → design-skill-suggestion-BbEDg15Z.js} +1 -1
- package/dist/web/assets/design-skill-suggestion-BbEDg15Z.js.br +0 -0
- package/dist/web/assets/design-skill-suggestion-BbEDg15Z.js.gz +0 -0
- package/dist/web/assets/{design-tab-BZb9eemI.js → design-tab-BQSYJ4cS.js} +6 -6
- package/dist/web/assets/design-tab-BQSYJ4cS.js.br +0 -0
- package/dist/web/assets/design-tab-BQSYJ4cS.js.gz +0 -0
- package/dist/web/assets/{designs-sidebar-panel-CF4tp6j-.js → designs-sidebar-panel-AiPaYfSU.js} +1 -1
- package/dist/web/assets/designs-sidebar-panel-AiPaYfSU.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-AiPaYfSU.js.gz +0 -0
- package/dist/web/assets/{diff-viewer-BqEqwSw6.js → diff-viewer-BZ6glGK-.js} +1 -1
- package/dist/web/assets/diff-viewer-BZ6glGK-.js.br +0 -0
- package/dist/web/assets/diff-viewer-BZ6glGK-.js.gz +0 -0
- package/dist/web/assets/{edit-diff-preview-DD4HayXY.js → edit-diff-preview-D-FNLOU2.js} +1 -1
- package/dist/web/assets/edit-diff-preview-D-FNLOU2.js.br +0 -0
- package/dist/web/assets/edit-diff-preview-D-FNLOU2.js.gz +0 -0
- package/dist/web/assets/{editor-language-service-CkA5sEdV.js → editor-language-service-4sbsdqBV.js} +1 -1
- package/dist/web/assets/editor-language-service-4sbsdqBV.js.br +0 -0
- package/dist/web/assets/editor-language-service-4sbsdqBV.js.gz +0 -0
- package/dist/web/assets/explorer-body-DbgrG1fe.js +2 -0
- package/dist/web/assets/explorer-body-DbgrG1fe.js.br +0 -0
- package/dist/web/assets/explorer-body-DbgrG1fe.js.gz +0 -0
- package/dist/web/assets/{explorer-window-content-CRJ2eMZ4.js → explorer-window-content-BCjWvHpp.js} +1 -1
- package/dist/web/assets/explorer-window-content-BCjWvHpp.js.br +0 -0
- package/dist/web/assets/explorer-window-content-BCjWvHpp.js.gz +0 -0
- package/dist/web/assets/{extension-manager-section-DwQTdCII.js → extension-manager-section-DiMF4RnT.js} +1 -1
- package/dist/web/assets/extension-manager-section-DiMF4RnT.js.br +0 -0
- package/dist/web/assets/extension-manager-section-DiMF4RnT.js.gz +0 -0
- package/dist/web/assets/{extension-webview-CMq-VTLu.js → extension-webview-Coti6Vfw.js} +1 -1
- package/dist/web/assets/extension-webview-Coti6Vfw.js.br +0 -0
- package/dist/web/assets/extension-webview-Coti6Vfw.js.gz +0 -0
- package/dist/web/assets/file-store-Cm7-pHux.js +1 -0
- package/dist/web/assets/file-store-Cm7-pHux.js.br +0 -0
- package/dist/web/assets/file-store-Cm7-pHux.js.gz +0 -0
- package/dist/web/assets/{files-settings-section-B2lcyh-6.js → files-settings-section-ZTVwOwx0.js} +1 -1
- package/dist/web/assets/files-settings-section-ZTVwOwx0.js.br +0 -0
- package/dist/web/assets/files-settings-section-ZTVwOwx0.js.gz +0 -0
- package/dist/web/assets/{general-settings-section-Doepm2sH.js → general-settings-section-B7Hjx_fb.js} +1 -1
- package/dist/web/assets/general-settings-section-B7Hjx_fb.js.br +0 -0
- package/dist/web/assets/general-settings-section-B7Hjx_fb.js.gz +0 -0
- package/dist/web/assets/{git-log-panel-CJakNMDc.js → git-log-panel-CbcJ_YEH.js} +1 -1
- package/dist/web/assets/git-log-panel-CbcJ_YEH.js.br +0 -0
- package/dist/web/assets/git-log-panel-CbcJ_YEH.js.gz +0 -0
- package/dist/web/assets/{glide-data-grid-C_SeOhSq.js → glide-data-grid-BfByGSf1.js} +3 -3
- package/dist/web/assets/glide-data-grid-BfByGSf1.js.br +0 -0
- package/dist/web/assets/glide-data-grid-BfByGSf1.js.gz +0 -0
- package/dist/web/assets/group-chat-tab-DCo9OP3D.js +1 -0
- package/dist/web/assets/group-chat-tab-DCo9OP3D.js.br +0 -0
- package/dist/web/assets/group-chat-tab-DCo9OP3D.js.gz +0 -0
- package/dist/web/assets/{image-preview-C1Frlx22.js → image-preview-Cjlps7Pt.js} +1 -1
- package/dist/web/assets/index-CyDLBddf.js +52 -0
- package/dist/web/assets/index-CyDLBddf.js.br +0 -0
- package/dist/web/assets/index-CyDLBddf.js.gz +0 -0
- package/dist/web/assets/index-Dh5apC3I.css +1 -0
- package/dist/web/assets/index-Dh5apC3I.css.br +0 -0
- package/dist/web/assets/index-Dh5apC3I.css.gz +0 -0
- package/dist/web/assets/{jira-watcher-section-BmkIGQFV.js → jira-watcher-section-Dz_-sUC2.js} +1 -1
- package/dist/web/assets/keybindings-store-BTq5Z7XP.js +1 -0
- package/dist/web/assets/{keyboard-shortcuts-section-BgZe3QDr.js → keyboard-shortcuts-section-Ba5NYwlG.js} +1 -1
- package/dist/web/assets/keyboard-shortcuts-section-Ba5NYwlG.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-Ba5NYwlG.js.gz +0 -0
- package/dist/web/assets/{language-servers-section-CCMzgOaE.js → language-servers-section-DJJ7SFXo.js} +2 -2
- package/dist/web/assets/language-servers-section-DJJ7SFXo.js.br +0 -0
- package/dist/web/assets/language-servers-section-DJJ7SFXo.js.gz +0 -0
- package/dist/web/assets/{lsp-client-B10suuaZ.js → lsp-client-Dr3vQ1bb.js} +1 -1
- package/dist/web/assets/lsp-client-Dr3vQ1bb.js.br +0 -0
- package/dist/web/assets/lsp-client-Dr3vQ1bb.js.gz +0 -0
- package/dist/web/assets/{markdown-renderer-AHHFS3jc.js → markdown-renderer-CmnqFfTU.js} +3 -3
- package/dist/web/assets/markdown-renderer-CmnqFfTU.js.br +0 -0
- package/dist/web/assets/markdown-renderer-CmnqFfTU.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-LJdVMbGl.js +1 -0
- package/dist/web/assets/{mobile-explorer-sheet-CqCsLWSr.js → mobile-explorer-sheet-BiIhZtJ2.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-BiIhZtJ2.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-BiIhZtJ2.js.gz +0 -0
- package/dist/web/assets/new-design-dialog-DI_hxvFV.js +1 -0
- package/dist/web/assets/new-design-dialog-DI_hxvFV.js.br +0 -0
- package/dist/web/assets/new-design-dialog-DI_hxvFV.js.gz +0 -0
- package/dist/web/assets/notification-store-9lBSplOV.js +1 -0
- package/dist/web/assets/{notifications-settings-section-CXgzH_bK.js → notifications-settings-section-0s-2OKRv.js} +1 -1
- package/dist/web/assets/notifications-settings-section-0s-2OKRv.js.br +0 -0
- package/dist/web/assets/notifications-settings-section-0s-2OKRv.js.gz +0 -0
- package/dist/web/assets/{overview-panel-BYB8mgRE.js → overview-panel-3htxfZS2.js} +1 -1
- package/dist/web/assets/overview-panel-3htxfZS2.js.br +0 -0
- package/dist/web/assets/overview-panel-3htxfZS2.js.gz +0 -0
- package/dist/web/assets/panel-store-B6PoCwBA.js +1 -0
- package/dist/web/assets/panel-store-B6PoCwBA.js.br +0 -0
- package/dist/web/assets/panel-store-B6PoCwBA.js.gz +0 -0
- package/dist/web/assets/{performance-panel-Dpuq3g0W.js → performance-panel-V8gpnnf7.js} +2 -2
- package/dist/web/assets/performance-panel-V8gpnnf7.js.br +0 -0
- package/dist/web/assets/performance-panel-V8gpnnf7.js.gz +0 -0
- package/dist/web/assets/{postgres-viewer-DW0Gkmow.js → postgres-viewer-sWS-ogTC.js} +1 -1
- package/dist/web/assets/postgres-viewer-sWS-ogTC.js.br +0 -0
- package/dist/web/assets/postgres-viewer-sWS-ogTC.js.gz +0 -0
- package/dist/web/assets/{ppmbot-settings-section-DFtlGoW6.js → ppmbot-settings-section-CT8vvlzk.js} +1 -1
- package/dist/web/assets/ppmbot-settings-section-CT8vvlzk.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-CT8vvlzk.js.gz +0 -0
- package/dist/web/assets/{pptx-export-CNyG32Xr.js → pptx-export-Ugjv0hso.js} +4 -4
- package/dist/web/assets/pptx-export-Ugjv0hso.js.br +0 -0
- package/dist/web/assets/pptx-export-Ugjv0hso.js.gz +0 -0
- package/dist/web/assets/{problems-panel-V5zqpuXq.js → problems-panel-Gcy79pSS.js} +1 -1
- package/dist/web/assets/problems-panel-Gcy79pSS.js.br +0 -0
- package/dist/web/assets/problems-panel-Gcy79pSS.js.gz +0 -0
- package/dist/web/assets/{process-row-format-CiJJSf_V.js → process-row-format-u6y7lOee.js} +1 -1
- package/dist/web/assets/process-table-BUTIp9Oy.js +1 -0
- package/dist/web/assets/process-table-BUTIp9Oy.js.br +0 -0
- package/dist/web/assets/process-table-BUTIp9Oy.js.gz +0 -0
- package/dist/web/assets/{proxy-settings-section-B7AVlTfY.js → proxy-settings-section-Buv_8y54.js} +1 -1
- package/dist/web/assets/proxy-settings-section-Buv_8y54.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-Buv_8y54.js.gz +0 -0
- package/dist/web/assets/{query-audit-section-gHmRfWKr.js → query-audit-section-N2DQtgJx.js} +1 -1
- package/dist/web/assets/query-audit-section-N2DQtgJx.js.br +0 -0
- package/dist/web/assets/query-audit-section-N2DQtgJx.js.gz +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-CRvJqmaz.js +1 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-CRvJqmaz.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-CRvJqmaz.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BoYINLbJ.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BoYINLbJ.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BoYINLbJ.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DPdP0yYu.js +1 -0
- package/dist/web/assets/remote-desktop-mobile-view-DPdP0yYu.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DPdP0yYu.js.gz +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-BeQ_Jrp7.js +1 -0
- package/dist/web/assets/remote-desktop-readiness-gate-BeQ_Jrp7.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-BeQ_Jrp7.js.gz +0 -0
- package/dist/web/assets/remote-desktop-settings-section-Bs4sHS3l.js +1 -0
- package/dist/web/assets/remote-desktop-settings-section-Bs4sHS3l.js.br +0 -0
- package/dist/web/assets/remote-desktop-settings-section-Bs4sHS3l.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-DDr__JxQ.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-DDr__JxQ.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-DDr__JxQ.js.gz +0 -0
- package/dist/web/assets/{run-in-terminal-DKBcEGVu.js → run-in-terminal-DCBNtYHQ.js} +1 -1
- package/dist/web/assets/{schedules-settings-section-sYDAsBMO.js → schedules-settings-section-_cHX2qOa.js} +1 -1
- package/dist/web/assets/schedules-settings-section-_cHX2qOa.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-_cHX2qOa.js.gz +0 -0
- package/dist/web/assets/{send-to-chat-CCdOrKc7.js → send-to-chat-dNqNV7Y4.js} +1 -1
- package/dist/web/assets/send-to-chat-dNqNV7Y4.js.br +0 -0
- package/dist/web/assets/send-to-chat-dNqNV7Y4.js.gz +0 -0
- package/dist/web/assets/{services-panel-CKa2aP1d.js → services-panel-Dm54cnW3.js} +1 -1
- package/dist/web/assets/services-panel-Dm54cnW3.js.br +0 -0
- package/dist/web/assets/services-panel-Dm54cnW3.js.gz +0 -0
- package/dist/web/assets/settings-body-DRRQNaDA.js +1 -0
- package/dist/web/assets/settings-body-DRRQNaDA.js.br +0 -0
- package/dist/web/assets/settings-body-DRRQNaDA.js.gz +0 -0
- package/dist/web/assets/settings-section-content-DRaHfvm8.js +1 -0
- package/dist/web/assets/settings-section-content-U3mIx6hF.js +2 -0
- package/dist/web/assets/settings-section-content-U3mIx6hF.js.br +0 -0
- package/dist/web/assets/settings-section-content-U3mIx6hF.js.gz +0 -0
- package/dist/web/assets/{settings-store-DfuiLq_9.js → settings-store-Cyuo8ceh.js} +2 -2
- package/dist/web/assets/settings-store-Cyuo8ceh.js.br +0 -0
- package/dist/web/assets/settings-store-Cyuo8ceh.js.gz +0 -0
- package/dist/web/assets/settings-tab-CB_BhHTK.js +1 -0
- package/dist/web/assets/settings-window-content-CgZIMNX8.js +1 -0
- package/dist/web/assets/{slash-command-picker-CLevA_Gf.js → slash-command-picker-CQlIqMW0.js} +1 -1
- package/dist/web/assets/slash-command-picker-CQlIqMW0.js.br +0 -0
- package/dist/web/assets/slash-command-picker-CQlIqMW0.js.gz +0 -0
- package/dist/web/assets/{sql-query-editor-Db8b0jEr.js → sql-query-editor--Z4bcqd8.js} +1 -1
- package/dist/web/assets/sql-query-editor--Z4bcqd8.js.br +0 -0
- package/dist/web/assets/sql-query-editor--Z4bcqd8.js.gz +0 -0
- package/dist/web/assets/{sqlite-viewer-VzYLMqkD.js → sqlite-viewer-CSmX3qaj.js} +1 -1
- package/dist/web/assets/sqlite-viewer-CSmX3qaj.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-CSmX3qaj.js.gz +0 -0
- package/dist/web/assets/{system-monitor-body-BR60W_mG.js → system-monitor-body-LannpU6H.js} +2 -2
- package/dist/web/assets/system-monitor-body-LannpU6H.js.br +0 -0
- package/dist/web/assets/system-monitor-body-LannpU6H.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-BfVucwEF.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-BLw1QBXo.js → system-monitor-window-content-kLNW9Ln3.js} +1 -1
- package/dist/web/assets/{tab-host-window-content-BL61SB5m.js → tab-host-window-content-II_FtOYM.js} +1 -1
- package/dist/web/assets/tab-host-window-content-II_FtOYM.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-II_FtOYM.js.gz +0 -0
- package/dist/web/assets/{tab-store-CbPh8CAX.js → tab-store-DCpc0g0Q.js} +1 -1
- package/dist/web/assets/{terminal-tab-B4y6at4h.js → terminal-tab-CTe2polt.js} +1 -1
- package/dist/web/assets/terminal-tab-CTe2polt.js.br +0 -0
- package/dist/web/assets/terminal-tab-CTe2polt.js.gz +0 -0
- package/dist/web/assets/{tool-cards-K0r_yxI0.js → tool-cards-rxLXbBSm.js} +5 -5
- package/dist/web/assets/tool-cards-rxLXbBSm.js.br +0 -0
- package/dist/web/assets/tool-cards-rxLXbBSm.js.gz +0 -0
- package/dist/web/assets/use-h264-canvas-decoder-BfO9QrlH.js +1 -0
- package/dist/web/assets/use-h264-canvas-decoder-BfO9QrlH.js.br +0 -0
- package/dist/web/assets/use-h264-canvas-decoder-BfO9QrlH.js.gz +0 -0
- package/dist/web/assets/{use-inline-blame-BK23icsE.js → use-inline-blame-CxZ_QWzS.js} +3 -3
- package/dist/web/assets/use-inline-blame-CxZ_QWzS.js.br +0 -0
- package/dist/web/assets/use-inline-blame-CxZ_QWzS.js.gz +0 -0
- package/dist/web/assets/{use-lsp-CRdCPCKW.js → use-lsp-gCNE5A9K.js} +1 -1
- package/dist/web/assets/use-lsp-gCNE5A9K.js.br +0 -0
- package/dist/web/assets/use-lsp-gCNE5A9K.js.gz +0 -0
- package/dist/web/assets/{use-monaco-theme-CpXT2uh3.js → use-monaco-theme-DvNGcCvz.js} +1 -1
- package/dist/web/assets/{use-process-kill-BWYX03KF.js → use-process-kill-Cbiu1mbY.js} +1 -1
- package/dist/web/assets/use-process-kill-Cbiu1mbY.js.br +0 -0
- package/dist/web/assets/use-process-kill-Cbiu1mbY.js.gz +0 -0
- package/dist/web/assets/{use-websocket-CiWQKhHr.js → use-websocket-AC7EG1LK.js} +1 -1
- package/dist/web/assets/video-preview-C2Y61WUE.js +1 -0
- package/dist/web/assets/video-preview-C2Y61WUE.js.br +0 -0
- package/dist/web/assets/video-preview-C2Y61WUE.js.gz +0 -0
- package/dist/web/assets/{voice-settings-section-Ceihggqf.js → voice-settings-section-DCXZeLYI.js} +1 -1
- package/dist/web/assets/voice-settings-section-DCXZeLYI.js.br +0 -0
- package/dist/web/assets/voice-settings-section-DCXZeLYI.js.gz +0 -0
- package/dist/web/assets/{window-pip-registry-mDgwpAwP.js → window-pip-registry-9Y9LQQYS.js} +1 -1
- package/dist/web/index.html +50 -7
- package/dist/web/index.html.br +0 -0
- package/dist/web/index.html.gz +0 -0
- package/dist/web/sw.js +1 -1
- package/dist/web/sw.js.br +0 -0
- package/dist/web/sw.js.gz +0 -0
- package/docs/architecture/android-emulator-phase0-report-260921.md +310 -0
- package/docs/architecture/android-emulator-phase2-report-260921.md +163 -0
- package/docs/architecture/android-emulator-phase3-report-260921.md +203 -0
- package/docs/architecture/android-emulator-phase4-report-260921.md +191 -0
- package/docs/architecture/android-emulator-plan-260920.md +376 -0
- package/docs/architecture/plugins-and-tracing.md +48 -7
- package/package.json +4 -2
- package/scripts/gen-android-proto.ts +37 -0
- package/scripts/release.sh +3 -1
- package/src/cli/commands/chat-cmd.ts +2 -2
- package/src/providers/claude-agent-sdk-query-options.ts +28 -0
- package/src/providers/claude-agent-sdk.ts +153 -71
- package/src/providers/claude-warm-spare.ts +176 -0
- package/src/providers/provider.interface.ts +1 -0
- package/src/server/index.ts +53 -0
- package/src/server/middleware/gzip-json.ts +13 -1
- package/src/server/routes/android.ts +616 -0
- package/src/server/routes/chat.ts +31 -0
- package/src/server/routes/files.ts +52 -5
- package/src/server/routes/remote-desktop.ts +95 -2
- package/src/server/routes/trace.ts +66 -0
- package/src/server/ws/android.ts +101 -0
- package/src/server/ws/chat.ts +12 -17
- package/src/server/ws/global.ts +13 -1
- package/src/server/ws/remote-desktop.ts +6 -1
- package/src/services/android/android-apk.ts +387 -0
- package/src/services/android/android-capabilities.ts +154 -0
- package/src/services/android/android-flag.ts +12 -0
- package/src/services/android/android-grpc.ts +124 -0
- package/src/services/android/android-input.ts +129 -0
- package/src/services/android/android-logcat.ts +169 -0
- package/src/services/android/android-nonce.ts +50 -0
- package/src/services/android/android-operations.ts +124 -0
- package/src/services/android/android-screenshot.ts +53 -0
- package/src/services/android/android-session.ts +510 -0
- package/src/services/android/android-video.ts +495 -0
- package/src/services/android/avd-list.ts +131 -0
- package/src/services/android/avd-manager.ts +397 -0
- package/src/services/android/device-registry.ts +223 -0
- package/src/services/android/emulator-discovery.ts +160 -0
- package/src/services/android/emulator-launcher.ts +290 -0
- package/src/services/android/proto/emulator-controller-descriptor.ts +10 -0
- package/src/services/android/proto/emulator_controller.proto +1791 -0
- package/src/services/android/sdk-discovery.ts +178 -0
- package/src/services/android/system-images.ts +124 -0
- package/src/services/chat.service.ts +81 -4
- package/src/services/config.service.ts +1 -1
- package/src/services/file-filter.service.ts +2 -61
- package/src/services/file-glob.ts +67 -0
- package/src/services/file-index/file-index-runner.ts +169 -0
- package/src/services/file-index/file-index-worker.ts +73 -0
- package/src/services/file-index/index-search.ts +152 -0
- package/src/services/file-index/index-walk.ts +109 -0
- package/src/services/file-list-index.service.ts +189 -70
- package/src/services/file-watcher/linux-inotify.ts +238 -0
- package/src/services/file-watcher/watch-tree.ts +37 -6
- package/src/services/file-watcher.service.ts +40 -8
- package/src/services/file.service.ts +17 -6
- package/src/services/group-chat/group-chat.service.ts +1 -1
- package/src/services/jira-debug-session.service.ts +1 -1
- package/src/services/ppmbot/ppmbot-delegation.ts +1 -1
- package/src/services/ppmbot/ppmbot-service.ts +1 -1
- package/src/services/project-content-search.service.ts +45 -11
- package/src/services/proxy-agent-turn.ts +7 -3
- package/src/services/remote-desktop/mediamtx-catalog.ts +89 -0
- package/src/services/remote-desktop/mediamtx-config.ts +91 -0
- package/src/services/remote-desktop/mediamtx-install.service.ts +191 -0
- package/src/services/remote-desktop/mediamtx-paths.ts +51 -0
- package/src/services/remote-desktop/mediamtx-process.ts +152 -0
- package/src/services/remote-desktop/remote-desktop-capture-wayland.ts +312 -0
- package/src/services/remote-desktop/remote-desktop-capture.ts +60 -4
- package/src/services/remote-desktop/remote-desktop-portal.ts +529 -0
- package/src/services/remote-desktop/remote-desktop-requirements.ts +56 -20
- package/src/services/remote-desktop/remote-desktop-session.ts +77 -3
- package/src/services/remote-desktop/remote-desktop-whep-registry.ts +36 -0
- package/src/services/scheduler-runner.ts +2 -3
- package/src/services/session-trace/browser-trace-ingest.ts +131 -0
- package/src/services/session-trace/session-trace-cleanup.ts +65 -0
- package/src/services/session-trace/session-trace-db.ts +89 -0
- package/src/services/session-trace/session-trace-store.ts +132 -0
- package/src/services/session-trace/trace-coalescer.ts +86 -0
- package/src/services/session-trace/trace-recorder.ts +304 -0
- package/src/services/session-trace/trace-writer.ts +83 -0
- package/src/shared/android-avd.ts +53 -0
- package/src/shared/android-protocol.ts +184 -0
- package/src/shared/file-index-limits.ts +17 -0
- package/src/shared/session-trace.ts +61 -0
- package/src/types/chat.ts +17 -0
- package/src/types/config.ts +27 -0
- package/src/types/project.ts +9 -0
- package/src/web/app.tsx +5 -0
- package/src/web/components/android/android-apk-upload.ts +75 -0
- package/src/web/components/android/android-avd-create-dialog.tsx +305 -0
- package/src/web/components/android/android-avd-destroy-dialog.tsx +151 -0
- package/src/web/components/android/android-clipboard-panel.tsx +94 -0
- package/src/web/components/android/android-controls.tsx +128 -0
- package/src/web/components/android/android-coords.ts +99 -0
- package/src/web/components/android/android-device-list.tsx +329 -0
- package/src/web/components/android/android-install-panel.tsx +280 -0
- package/src/web/components/android/android-log-filter.ts +75 -0
- package/src/web/components/android/android-logcat-panel.tsx +214 -0
- package/src/web/components/android/android-screenshot-download.ts +56 -0
- package/src/web/components/android/android-tab.tsx +66 -0
- package/src/web/components/android/android-tools-drawer.tsx +76 -0
- package/src/web/components/android/android-viewer.tsx +200 -0
- package/src/web/components/android/android-ws-url.ts +21 -0
- package/src/web/components/android/open-android.ts +21 -0
- package/src/web/components/android/use-android-available.ts +29 -0
- package/src/web/components/android/use-android-keyboard.ts +111 -0
- package/src/web/components/android/use-android-session.ts +277 -0
- package/src/web/components/android/use-android-touch.ts +123 -0
- package/src/web/components/chat/chat-tab.tsx +28 -2
- package/src/web/components/chat/message-input.tsx +3 -5
- package/src/web/components/editor/compare-picker.tsx +12 -2
- package/src/web/components/explorer/file-tree.tsx +6 -7
- package/src/web/components/explorer/use-tree-row-dnd.ts +0 -2
- package/src/web/components/floating-window/window-content-registry.ts +8 -8
- package/src/web/components/layout/command-palette.tsx +31 -9
- package/src/web/components/layout/mobile-drawer.tsx +8 -1
- package/src/web/components/layout/nav-section-rail.tsx +8 -1
- package/src/web/components/layout/reparenting-tab.tsx +2 -2
- package/src/web/components/layout/tab-content.tsx +5 -0
- package/src/web/components/layout/tab-pool.tsx +31 -20
- package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +23 -1
- package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +64 -6
- package/src/web/components/remote-desktop/use-webrtc-canvas-video.ts +255 -0
- package/src/web/components/root-error-boundary.tsx +5 -0
- package/src/web/components/settings/remote-desktop-settings-section.tsx +176 -0
- package/src/web/components/settings/settings-categories.ts +3 -0
- package/src/web/components/settings/settings-section-content.tsx +25 -18
- package/src/web/hooks/use-chat-prewarm.ts +53 -0
- package/src/web/hooks/use-chat.ts +18 -4
- package/src/web/hooks/use-file-index-invalidation.ts +7 -5
- package/src/web/hooks/use-global-events.ts +12 -1
- package/src/web/hooks/use-remote-file-search.ts +39 -0
- package/src/web/hooks/use-ui-preload.ts +38 -0
- package/src/web/index.html +43 -0
- package/src/web/lib/chunk-recovery.ts +5 -1
- package/src/web/lib/device-id.ts +39 -0
- package/src/web/lib/feature-badges.ts +2 -0
- package/src/web/lib/lazy-with-preload.tsx +43 -0
- package/src/web/lib/preload-ui.ts +60 -0
- package/src/web/lib/score-file-search.ts +27 -14
- package/src/web/lib/tab-type-icons.ts +2 -1
- package/src/web/lib/trace-boot.ts +8 -0
- package/src/web/lib/trace-client.ts +516 -0
- package/src/web/main.tsx +12 -0
- package/src/web/stores/file-store.ts +124 -18
- package/src/web/stores/panel-utils.ts +3 -0
- package/src/web/stores/tab-store.ts +2 -1
- package/src/web/stores/window-panel-persistence.ts +3 -0
- package/vite.config.ts +3 -0
- package/dist/web/assets/accounts-settings-section-DEIm-W-D.js +0 -1
- package/dist/web/assets/accounts-settings-section-DEIm-W-D.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-DEIm-W-D.js.gz +0 -0
- package/dist/web/assets/agent-session-sheet-CBMsnl_p.js.br +0 -0
- package/dist/web/assets/agent-session-sheet-CBMsnl_p.js.gz +0 -0
- package/dist/web/assets/agent-session-window-content-D9C9FA7E.js.br +0 -0
- package/dist/web/assets/agent-session-window-content-D9C9FA7E.js.gz +0 -0
- package/dist/web/assets/ai-resource-editor-DhUVgDEG.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-DhUVgDEG.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-CETpSpYd.js.br +0 -0
- package/dist/web/assets/ai-settings-section-CETpSpYd.js.gz +0 -0
- package/dist/web/assets/appearance-settings-section-BJUiLUb7.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-BJUiLUb7.js.gz +0 -0
- package/dist/web/assets/apps-panel-1VElRgV3.js.br +0 -0
- package/dist/web/assets/apps-panel-1VElRgV3.js.gz +0 -0
- package/dist/web/assets/audio-preview-DYkyHu5X.js.br +0 -0
- package/dist/web/assets/audio-preview-DYkyHu5X.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-DhTMSpSU.js +0 -1
- package/dist/web/assets/branch-review-tab-DhTMSpSU.js.br +0 -0
- package/dist/web/assets/branch-review-tab-DhTMSpSU.js.gz +0 -0
- package/dist/web/assets/chat-tab-Cg3BKugo.js +0 -15
- package/dist/web/assets/chat-tab-Cg3BKugo.js.br +0 -0
- package/dist/web/assets/chat-tab-Cg3BKugo.js.gz +0 -0
- package/dist/web/assets/chat-tab-tbUDK__Y.js +0 -1
- package/dist/web/assets/chat-tab-tbUDK__Y.js.br +0 -0
- package/dist/web/assets/chat-tab-tbUDK__Y.js.gz +0 -0
- package/dist/web/assets/code-editor-6ZiQehbr.js +0 -7
- package/dist/web/assets/code-editor-6ZiQehbr.js.br +0 -0
- package/dist/web/assets/code-editor-6ZiQehbr.js.gz +0 -0
- package/dist/web/assets/codex-reset-credit-button-Fr45FUxi.js.br +0 -0
- package/dist/web/assets/codex-reset-credit-button-Fr45FUxi.js.gz +0 -0
- package/dist/web/assets/conflict-editor-B-wdjSXI.js.br +0 -0
- package/dist/web/assets/conflict-editor-B-wdjSXI.js.gz +0 -0
- package/dist/web/assets/csv-preview-B6RodObB.js.br +0 -0
- package/dist/web/assets/csv-preview-B6RodObB.js.gz +0 -0
- package/dist/web/assets/database-viewer-CdPBUQPY.js.br +0 -0
- package/dist/web/assets/database-viewer-CdPBUQPY.js.gz +0 -0
- package/dist/web/assets/design-settings-section-BpCpaBwL.js.br +0 -0
- package/dist/web/assets/design-settings-section-BpCpaBwL.js.gz +0 -0
- package/dist/web/assets/design-skill-suggestion-DkT2E2IV.js.br +0 -0
- package/dist/web/assets/design-skill-suggestion-DkT2E2IV.js.gz +0 -0
- package/dist/web/assets/design-tab-BZb9eemI.js.br +0 -0
- package/dist/web/assets/design-tab-BZb9eemI.js.gz +0 -0
- package/dist/web/assets/designs-sidebar-panel-CF4tp6j-.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-CF4tp6j-.js.gz +0 -0
- package/dist/web/assets/diff-viewer-BqEqwSw6.js.br +0 -0
- package/dist/web/assets/diff-viewer-BqEqwSw6.js.gz +0 -0
- package/dist/web/assets/edit-diff-preview-DD4HayXY.js.br +0 -0
- package/dist/web/assets/edit-diff-preview-DD4HayXY.js.gz +0 -0
- package/dist/web/assets/editor-language-service-CkA5sEdV.js.br +0 -0
- package/dist/web/assets/editor-language-service-CkA5sEdV.js.gz +0 -0
- package/dist/web/assets/explorer-body-Ds7oKuz7.js +0 -2
- package/dist/web/assets/explorer-body-Ds7oKuz7.js.br +0 -0
- package/dist/web/assets/explorer-body-Ds7oKuz7.js.gz +0 -0
- package/dist/web/assets/explorer-window-content-CRJ2eMZ4.js.br +0 -0
- package/dist/web/assets/explorer-window-content-CRJ2eMZ4.js.gz +0 -0
- package/dist/web/assets/extension-manager-section-DwQTdCII.js.br +0 -0
- package/dist/web/assets/extension-manager-section-DwQTdCII.js.gz +0 -0
- package/dist/web/assets/extension-webview-CMq-VTLu.js.br +0 -0
- package/dist/web/assets/extension-webview-CMq-VTLu.js.gz +0 -0
- package/dist/web/assets/file-store-DY1_0nzY.js +0 -1
- package/dist/web/assets/file-store-DY1_0nzY.js.br +0 -0
- package/dist/web/assets/file-store-DY1_0nzY.js.gz +0 -0
- package/dist/web/assets/files-settings-section-B2lcyh-6.js.br +0 -0
- package/dist/web/assets/files-settings-section-B2lcyh-6.js.gz +0 -0
- package/dist/web/assets/general-settings-section-Doepm2sH.js.br +0 -0
- package/dist/web/assets/general-settings-section-Doepm2sH.js.gz +0 -0
- package/dist/web/assets/git-log-panel-CJakNMDc.js.br +0 -0
- package/dist/web/assets/git-log-panel-CJakNMDc.js.gz +0 -0
- package/dist/web/assets/glide-data-grid-C_SeOhSq.js.br +0 -0
- package/dist/web/assets/glide-data-grid-C_SeOhSq.js.gz +0 -0
- package/dist/web/assets/group-chat-tab--by1xCQW.js +0 -1
- package/dist/web/assets/group-chat-tab--by1xCQW.js.br +0 -0
- package/dist/web/assets/group-chat-tab--by1xCQW.js.gz +0 -0
- package/dist/web/assets/index-DhWjx5iR.js +0 -52
- package/dist/web/assets/index-DhWjx5iR.js.br +0 -0
- package/dist/web/assets/index-DhWjx5iR.js.gz +0 -0
- package/dist/web/assets/index-oIvYSKTT.css +0 -1
- package/dist/web/assets/index-oIvYSKTT.css.br +0 -0
- package/dist/web/assets/index-oIvYSKTT.css.gz +0 -0
- package/dist/web/assets/keybindings-store-30LYZU0l.js +0 -1
- package/dist/web/assets/keyboard-shortcuts-section-BgZe3QDr.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-BgZe3QDr.js.gz +0 -0
- package/dist/web/assets/language-servers-section-CCMzgOaE.js.br +0 -0
- package/dist/web/assets/language-servers-section-CCMzgOaE.js.gz +0 -0
- package/dist/web/assets/lsp-client-B10suuaZ.js.br +0 -0
- package/dist/web/assets/lsp-client-B10suuaZ.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-AHHFS3jc.js.br +0 -0
- package/dist/web/assets/markdown-renderer-AHHFS3jc.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-DxnXtBoj.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-CqCsLWSr.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-CqCsLWSr.js.gz +0 -0
- package/dist/web/assets/new-design-dialog-B9tgnQXq.js +0 -1
- package/dist/web/assets/new-design-dialog-B9tgnQXq.js.br +0 -0
- package/dist/web/assets/new-design-dialog-B9tgnQXq.js.gz +0 -0
- package/dist/web/assets/notification-store-HUVzO_bp.js +0 -1
- package/dist/web/assets/notifications-settings-section-CXgzH_bK.js.br +0 -0
- package/dist/web/assets/notifications-settings-section-CXgzH_bK.js.gz +0 -0
- package/dist/web/assets/overview-panel-BYB8mgRE.js.br +0 -0
- package/dist/web/assets/overview-panel-BYB8mgRE.js.gz +0 -0
- package/dist/web/assets/panel-store-CWHPmHwj.js +0 -1
- package/dist/web/assets/panel-store-CWHPmHwj.js.br +0 -0
- package/dist/web/assets/panel-store-CWHPmHwj.js.gz +0 -0
- package/dist/web/assets/performance-panel-Dpuq3g0W.js.br +0 -0
- package/dist/web/assets/performance-panel-Dpuq3g0W.js.gz +0 -0
- package/dist/web/assets/postgres-viewer-DW0Gkmow.js.br +0 -0
- package/dist/web/assets/postgres-viewer-DW0Gkmow.js.gz +0 -0
- package/dist/web/assets/ppmbot-settings-section-DFtlGoW6.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-DFtlGoW6.js.gz +0 -0
- package/dist/web/assets/pptx-export-CNyG32Xr.js.br +0 -0
- package/dist/web/assets/pptx-export-CNyG32Xr.js.gz +0 -0
- package/dist/web/assets/problems-panel-V5zqpuXq.js.br +0 -0
- package/dist/web/assets/problems-panel-V5zqpuXq.js.gz +0 -0
- package/dist/web/assets/process-table-DAj46HIl.js +0 -1
- package/dist/web/assets/process-table-DAj46HIl.js.br +0 -0
- package/dist/web/assets/process-table-DAj46HIl.js.gz +0 -0
- package/dist/web/assets/proxy-settings-section-B7AVlTfY.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-B7AVlTfY.js.gz +0 -0
- package/dist/web/assets/query-audit-section-gHmRfWKr.js.br +0 -0
- package/dist/web/assets/query-audit-section-gHmRfWKr.js.gz +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-Cgoznh8L.js +0 -1
- package/dist/web/assets/remote-desktop-clipboard-notice-Cgoznh8L.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-Cgoznh8L.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-X12QbPhO.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-sheet-X12QbPhO.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-X12QbPhO.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DsSHawSK.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-view-DsSHawSK.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DsSHawSK.js.gz +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-CHiQEd9Q.js +0 -1
- package/dist/web/assets/remote-desktop-readiness-gate-CHiQEd9Q.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-CHiQEd9Q.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-BnznIby3.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-BnznIby3.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-BnznIby3.js.gz +0 -0
- package/dist/web/assets/schedules-settings-section-sYDAsBMO.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-sYDAsBMO.js.gz +0 -0
- package/dist/web/assets/send-to-chat-CCdOrKc7.js.br +0 -0
- package/dist/web/assets/send-to-chat-CCdOrKc7.js.gz +0 -0
- package/dist/web/assets/services-panel-CKa2aP1d.js.br +0 -0
- package/dist/web/assets/services-panel-CKa2aP1d.js.gz +0 -0
- package/dist/web/assets/settings-body-CEIF8lkl.js +0 -2
- package/dist/web/assets/settings-body-CEIF8lkl.js.br +0 -0
- package/dist/web/assets/settings-body-CEIF8lkl.js.gz +0 -0
- package/dist/web/assets/settings-store-DfuiLq_9.js.br +0 -0
- package/dist/web/assets/settings-store-DfuiLq_9.js.gz +0 -0
- package/dist/web/assets/settings-tab-Du5AUPni.js +0 -1
- package/dist/web/assets/settings-window-content-BTflfLez.js +0 -1
- package/dist/web/assets/slash-command-picker-CLevA_Gf.js.br +0 -0
- package/dist/web/assets/slash-command-picker-CLevA_Gf.js.gz +0 -0
- package/dist/web/assets/sql-query-editor-Db8b0jEr.js.br +0 -0
- package/dist/web/assets/sql-query-editor-Db8b0jEr.js.gz +0 -0
- package/dist/web/assets/sqlite-viewer-VzYLMqkD.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-VzYLMqkD.js.gz +0 -0
- package/dist/web/assets/system-monitor-body-BR60W_mG.js.br +0 -0
- package/dist/web/assets/system-monitor-body-BR60W_mG.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-BYpantE2.js +0 -1
- package/dist/web/assets/tab-host-window-content-BL61SB5m.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-BL61SB5m.js.gz +0 -0
- package/dist/web/assets/terminal-tab-B4y6at4h.js.br +0 -0
- package/dist/web/assets/terminal-tab-B4y6at4h.js.gz +0 -0
- package/dist/web/assets/tool-cards-K0r_yxI0.js.br +0 -0
- package/dist/web/assets/tool-cards-K0r_yxI0.js.gz +0 -0
- package/dist/web/assets/use-inline-blame-BK23icsE.js.br +0 -0
- package/dist/web/assets/use-inline-blame-BK23icsE.js.gz +0 -0
- package/dist/web/assets/use-lsp-CRdCPCKW.js.br +0 -0
- package/dist/web/assets/use-lsp-CRdCPCKW.js.gz +0 -0
- package/dist/web/assets/use-process-kill-BWYX03KF.js.br +0 -0
- package/dist/web/assets/use-process-kill-BWYX03KF.js.gz +0 -0
- package/dist/web/assets/video-preview-DugVkMB7.js +0 -1
- package/dist/web/assets/video-preview-DugVkMB7.js.br +0 -0
- package/dist/web/assets/video-preview-DugVkMB7.js.gz +0 -0
- package/dist/web/assets/voice-settings-section-Ceihggqf.js.br +0 -0
- package/dist/web/assets/voice-settings-section-Ceihggqf.js.gz +0 -0
- /package/dist/web/assets/{__vite-browser-external-B_XMEguz.js → __vite-browser-external-C5mXParC.js} +0 -0
- /package/dist/web/assets/{agent-session-sheet-store-DO682WSg.js → agent-session-sheet-store-_OeezCXB.js} +0 -0
- /package/dist/web/assets/{api-settings-CpO8SPk9.js → api-settings-Cc_CpfmC.js} +0 -0
- /package/dist/web/assets/{api-settings-CpO8SPk9.js.br → api-settings-Cc_CpfmC.js.br} +0 -0
- /package/dist/web/assets/{api-settings-CpO8SPk9.js.gz → api-settings-Cc_CpfmC.js.gz} +0 -0
- /package/dist/web/assets/{api-themes-CJipDH_l.js → api-themes-BtMBSjuM.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-Bw_xiOXX.js → can-open-in-ppm-DeE2-kd5.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-Bw_xiOXX.js.br → can-open-in-ppm-DeE2-kd5.js.br} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-Bw_xiOXX.js.gz → can-open-in-ppm-DeE2-kd5.js.gz} +0 -0
- /package/dist/web/assets/{csv-parser-DcRWxcNV.js → csv-parser-CoxPdyLw.js} +0 -0
- /package/dist/web/assets/{csv-parser-DcRWxcNV.js.br → csv-parser-CoxPdyLw.js.br} +0 -0
- /package/dist/web/assets/{csv-parser-DcRWxcNV.js.gz → csv-parser-CoxPdyLw.js.gz} +0 -0
- /package/dist/web/assets/{design-export-client-CHffN3DM.js → design-export-client-BfdZB7xK.js} +0 -0
- /package/dist/web/assets/{design-export-client-CHffN3DM.js.br → design-export-client-BfdZB7xK.js.br} +0 -0
- /package/dist/web/assets/{design-export-client-CHffN3DM.js.gz → design-export-client-BfdZB7xK.js.gz} +0 -0
- /package/dist/web/assets/{design-responsive-dialog-DSCpv6O1.js → design-responsive-dialog-BiYy3ibS.js} +0 -0
- /package/dist/web/assets/{design-responsive-dialog-DSCpv6O1.js.br → design-responsive-dialog-BiYy3ibS.js.br} +0 -0
- /package/dist/web/assets/{design-responsive-dialog-DSCpv6O1.js.gz → design-responsive-dialog-BiYy3ibS.js.gz} +0 -0
- /package/dist/web/assets/{design-system-status-label-B_0I_3a8.js → design-system-status-label-ekiLyjKn.js} +0 -0
- /package/dist/web/assets/{docx-preview-B3eJyY2y.js → docx-preview-Dt6RQpfA.js} +0 -0
- /package/dist/web/assets/{docx-preview-B3eJyY2y.js.br → docx-preview-Dt6RQpfA.js.br} +0 -0
- /package/dist/web/assets/{docx-preview-B3eJyY2y.js.gz → docx-preview-Dt6RQpfA.js.gz} +0 -0
- /package/dist/web/assets/{editor-font-BNwo7zo5.js → editor-font-CBszn6vt.js} +0 -0
- /package/dist/web/assets/{format-bytes-CRZfxO2J.js → format-bytes-FQmpZ25A.js} +0 -0
- /package/dist/web/assets/{image-gallery-DC3gRAqk.js → image-gallery-B16hO6Ab.js} +0 -0
- /package/dist/web/assets/{image-preview-geometry-DN3DZQp0.js → image-preview-geometry-D4Ihz4UV.js} +0 -0
- /package/dist/web/assets/{lib-B9k5tn9s.js → lib-6APiJkU3.js} +0 -0
- /package/dist/web/assets/{lib-B9k5tn9s.js.br → lib-6APiJkU3.js.br} +0 -0
- /package/dist/web/assets/{lib-B9k5tn9s.js.gz → lib-6APiJkU3.js.gz} +0 -0
- /package/dist/web/assets/{libesm-5f7T4AHA.js → libesm-5wREk7JN.js} +0 -0
- /package/dist/web/assets/{libesm-5f7T4AHA.js.br → libesm-5wREk7JN.js.br} +0 -0
- /package/dist/web/assets/{libesm-5f7T4AHA.js.gz → libesm-5wREk7JN.js.gz} +0 -0
- /package/dist/web/assets/{media-url-CCGOIxzS.js → media-url-CrT-qulZ.js} +0 -0
- /package/dist/web/assets/{metric-chart-canvas-CjFbZuhk.js → metric-chart-canvas-D7f-LQ8x.js} +0 -0
- /package/dist/web/assets/{metric-chart-canvas-CjFbZuhk.js.br → metric-chart-canvas-D7f-LQ8x.js.br} +0 -0
- /package/dist/web/assets/{metric-chart-canvas-CjFbZuhk.js.gz → metric-chart-canvas-D7f-LQ8x.js.gz} +0 -0
- /package/dist/web/assets/{monaco-dotenv-language-Bwq3gQsi.js → monaco-dotenv-language-DbSss7YR.js} +0 -0
- /package/dist/web/assets/{onboarding-entry-CuH1Kqwu.js → onboarding-entry-CGFdGLLP.js} +0 -0
- /package/dist/web/assets/{onboarding-entry-CuH1Kqwu.js.br → onboarding-entry-CGFdGLLP.js.br} +0 -0
- /package/dist/web/assets/{onboarding-entry-CuH1Kqwu.js.gz → onboarding-entry-CGFdGLLP.js.gz} +0 -0
- /package/dist/web/assets/{pdf-preview-X8bdpfE9.js → pdf-preview-B8A4T-ES.js} +0 -0
- /package/dist/web/assets/{pdf-preview-X8bdpfE9.js.br → pdf-preview-B8A4T-ES.js.br} +0 -0
- /package/dist/web/assets/{pdf-preview-X8bdpfE9.js.gz → pdf-preview-B8A4T-ES.js.gz} +0 -0
- /package/dist/web/assets/{separator-DMu7VFQa.js → separator-dHj3Km--.js} +0 -0
- /package/dist/web/assets/{sortable-header-CipIP7aH.js → sortable-header-BOa8o7NJ.js} +0 -0
- /package/dist/web/assets/{sortable-header-CipIP7aH.js.br → sortable-header-BOa8o7NJ.js.br} +0 -0
- /package/dist/web/assets/{sortable-header-CipIP7aH.js.gz → sortable-header-BOa8o7NJ.js.gz} +0 -0
- /package/dist/web/assets/{use-blob-url-DjuRQWnR.js → use-blob-url-BC69n15x.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-Be4L1NHd.js → vendor-xterm-CfasyE0m.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-Be4L1NHd.js.br → vendor-xterm-CfasyE0m.js.br} +0 -0
- /package/dist/web/assets/{vendor-xterm-Be4L1NHd.js.gz → vendor-xterm-CfasyE0m.js.gz} +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which emulators are running right now, and how to reach each one's gRPC endpoint.
|
|
3
|
+
*
|
|
4
|
+
* The emulator advertises itself in a per-process `.ini` under the *current user's* runtime
|
|
5
|
+
* directory. Plan §5 requires this to be read defensively: bounded in size, with PID liveness
|
|
6
|
+
* and process identity verified, and only a loopback endpoint accepted — a stale `.ini` from a
|
|
7
|
+
* crashed emulator otherwise names a port some unrelated process may now hold.
|
|
8
|
+
*/
|
|
9
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
10
|
+
import { homedir, platform } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
|
|
13
|
+
/** A discovery .ini is a few hundred bytes; anything larger is not one, and we refuse to read it. */
|
|
14
|
+
const MAX_DISCOVERY_BYTES = 64 * 1024;
|
|
15
|
+
|
|
16
|
+
export interface RunningEmulator {
|
|
17
|
+
/** Absolute path of the discovery file this came from. */
|
|
18
|
+
file: string;
|
|
19
|
+
pid: number;
|
|
20
|
+
/**
|
|
21
|
+
* The AVD **id** (`avd.id`) — what `-avd` takes, what an AVD's `config.ini` calls `AvdId`, and
|
|
22
|
+
* what `listAvds()` reports as `name`. Deliberately not `avd.name`, which is the display name.
|
|
23
|
+
*/
|
|
24
|
+
avdName: string;
|
|
25
|
+
avdDir: string | null;
|
|
26
|
+
emulatorVersion: string | null;
|
|
27
|
+
grpcPort: number;
|
|
28
|
+
/** Bearer token for the gRPC channel. Never leaves the backend. */
|
|
29
|
+
grpcToken: string | null;
|
|
30
|
+
/** Directory of JSON Web Key Sets, when the emulator offers JWT auth. */
|
|
31
|
+
grpcJwks: string | null;
|
|
32
|
+
adbPort: number | null;
|
|
33
|
+
adbSerial: string | null;
|
|
34
|
+
/** The emulator's own argv, used to verify this pid is still the process the file describes. */
|
|
35
|
+
cmdline: string | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Where the emulator writes discovery files, current user only. */
|
|
39
|
+
export function discoveryDirs(): string[] {
|
|
40
|
+
const dirs: string[] = [];
|
|
41
|
+
const runtime = process.env.XDG_RUNTIME_DIR;
|
|
42
|
+
if (runtime) dirs.push(join(runtime, "avd", "running"));
|
|
43
|
+
dirs.push(join(homedir(), ".android", "avd", "running"));
|
|
44
|
+
if (platform() === "win32") {
|
|
45
|
+
const local = process.env.LOCALAPPDATA;
|
|
46
|
+
if (local) dirs.push(join(local, "Temp", "avd", "running"));
|
|
47
|
+
} else {
|
|
48
|
+
const tmp = process.env.TMPDIR ?? "/tmp";
|
|
49
|
+
dirs.push(join(tmp, `android-${process.env.USER ?? ""}`, "avd", "running"));
|
|
50
|
+
}
|
|
51
|
+
return dirs;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function parseIni(text: string): Record<string, string> {
|
|
55
|
+
const out: Record<string, string> = {};
|
|
56
|
+
for (const line of text.split("\n")) {
|
|
57
|
+
const t = line.trim();
|
|
58
|
+
if (!t || t.startsWith("#") || t.startsWith("[")) continue;
|
|
59
|
+
const eq = t.indexOf("=");
|
|
60
|
+
if (eq <= 0) continue;
|
|
61
|
+
out[t.slice(0, eq).trim()] = t.slice(eq + 1).trim();
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Is this pid alive? Signal 0 tests existence without delivering anything. */
|
|
67
|
+
function pidAlive(pid: number): boolean {
|
|
68
|
+
try { process.kill(pid, 0); return true; } catch { return false; }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Does the live process still look like the emulator the discovery file describes?
|
|
73
|
+
*
|
|
74
|
+
* PIDs are reused. Without this, a stale `.ini` whose pid now belongs to something else would
|
|
75
|
+
* be treated as a running emulator — and a later "stop" would signal an innocent process.
|
|
76
|
+
*/
|
|
77
|
+
export function verifyProcessIdentity(pid: number, avdName: string | undefined): boolean {
|
|
78
|
+
const avd = avdName;
|
|
79
|
+
if (!avd) return false;
|
|
80
|
+
if (platform() !== "linux") {
|
|
81
|
+
// Only /proc gives a cheap, dependency-free argv. Elsewhere the pid check plus a live gRPC
|
|
82
|
+
// handshake is what establishes identity, so accept a live pid here and let the connect
|
|
83
|
+
// prove it.
|
|
84
|
+
return pidAlive(pid);
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const argv = readFileSync(`/proc/${pid}/cmdline`, "utf8").split("\0").filter(Boolean);
|
|
88
|
+
return argv.some((a) => a === avd) || argv.some((a) => a.includes("qemu-system"));
|
|
89
|
+
} catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function parsePort(v: string | undefined): number | null {
|
|
95
|
+
const n = Number(v);
|
|
96
|
+
return Number.isInteger(n) && n > 0 && n <= 65535 ? n : null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Every emulator this user is running that advertises a usable loopback gRPC endpoint. */
|
|
100
|
+
export function findRunningEmulators(): RunningEmulator[] {
|
|
101
|
+
const found: RunningEmulator[] = [];
|
|
102
|
+
const seenPids = new Set<number>();
|
|
103
|
+
|
|
104
|
+
for (const dir of discoveryDirs()) {
|
|
105
|
+
let entries: string[];
|
|
106
|
+
try { entries = readdirSync(dir); } catch { continue; }
|
|
107
|
+
|
|
108
|
+
for (const name of entries) {
|
|
109
|
+
const m = /^pid_(\d+)\.ini$/.exec(name);
|
|
110
|
+
if (!m) continue;
|
|
111
|
+
const pid = Number(m[1]);
|
|
112
|
+
if (seenPids.has(pid)) continue;
|
|
113
|
+
|
|
114
|
+
const file = join(dir, name);
|
|
115
|
+
let fields: Record<string, string>;
|
|
116
|
+
try {
|
|
117
|
+
if (statSync(file).size > MAX_DISCOVERY_BYTES) continue;
|
|
118
|
+
fields = parseIni(readFileSync(file, "utf8"));
|
|
119
|
+
} catch {
|
|
120
|
+
continue; // racing with emulator shutdown is normal
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const grpcPort = parsePort(fields["grpc.port"]);
|
|
124
|
+
if (!grpcPort) continue; // no control channel, nothing we can drive
|
|
125
|
+
if (!pidAlive(pid)) continue; // stale file from a crashed emulator
|
|
126
|
+
// pid was reused by something else
|
|
127
|
+
if (!verifyProcessIdentity(pid, fields["avd.id"] ?? fields["avd.name"])) continue;
|
|
128
|
+
|
|
129
|
+
seenPids.add(pid);
|
|
130
|
+
const adbPort = parsePort(fields["port.adb"]);
|
|
131
|
+
const serialPort = parsePort(fields["port.serial"]);
|
|
132
|
+
found.push({
|
|
133
|
+
file,
|
|
134
|
+
pid,
|
|
135
|
+
// `avd.id`, **never** `avd.name`. The discovery file carries both, and for any AVD made
|
|
136
|
+
// in Android Studio they differ: the id is `Pixel_9` while `avd.name` is `Pixel 9`, the
|
|
137
|
+
// same string with the underscores turned back into spaces. Read the display name here
|
|
138
|
+
// and a running emulator stops matching its own AVD row — the device list then shows the
|
|
139
|
+
// AVD as stopped *and* a second `external:` row beside it with no geometry, and Start is
|
|
140
|
+
// offered for a device that is already up. Every AVD used while this was built had a
|
|
141
|
+
// one-word name, where the two fields agree, which is why it was never seen.
|
|
142
|
+
avdName: fields["avd.id"] ?? fields["avd.name"] ?? "(unknown)",
|
|
143
|
+
avdDir: fields["avd.dir"] ?? null,
|
|
144
|
+
emulatorVersion: fields["emulator.version"] ?? null,
|
|
145
|
+
grpcPort,
|
|
146
|
+
grpcToken: fields["grpc.token"] ?? null,
|
|
147
|
+
grpcJwks: fields["grpc.jwks"] ?? null,
|
|
148
|
+
adbPort,
|
|
149
|
+
// adb names an emulator by its *console* port, not its adb port.
|
|
150
|
+
adbSerial: serialPort ? `emulator-${serialPort}` : null,
|
|
151
|
+
cmdline: fields["cmdline"] ?? null,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return found;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function findRunningEmulatorByAvd(avdName: string): RunningEmulator | null {
|
|
159
|
+
return findRunningEmulators().find((e) => e.avdName === avdName) ?? null;
|
|
160
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starting and stopping emulators PPM owns.
|
|
3
|
+
*
|
|
4
|
+
* Four things here are load-bearing and each one is in the plan for a reason:
|
|
5
|
+
*
|
|
6
|
+
* - **One start per AVD at a time.** A refresh, a double-tap on Start, or two browser tabs must
|
|
7
|
+
* not spawn two emulators against one AVD — that corrupts the userdata image. A per-AVD mutex
|
|
8
|
+
* plus start idempotency (an in-flight start is *returned*, not duplicated) is what prevents it.
|
|
9
|
+
* - **Never `-grpc <port>`.** `emulator -help` says `-grpc-use-jwt ... (default, disable with
|
|
10
|
+
* -grpc flag)`, so pinning a port silently drops the control channel to unauthenticated.
|
|
11
|
+
* The port is discovered from the emulator's own advertisement instead.
|
|
12
|
+
* - **`ANDROID_AVD_HOME` goes in the child's env only.** Setting it on this process would hide
|
|
13
|
+
* every AVD outside it from our own listing — it replaces the default rather than adding to it.
|
|
14
|
+
* - **A lock file is reported, never removed.** An AVD Android Studio is running stays untouched.
|
|
15
|
+
*/
|
|
16
|
+
import { spawn } from "node:child_process";
|
|
17
|
+
import { existsSync } from "node:fs";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { connectToEmulator, getEmulatorStatus, requestShutdown } from "./android-grpc.ts";
|
|
21
|
+
import { findRunningEmulatorByAvd, findRunningEmulators, type RunningEmulator } from "./emulator-discovery.ts";
|
|
22
|
+
import { claimOwnership, listDevices, releaseOwnership } from "./device-registry.ts";
|
|
23
|
+
import { createOperation, failOperation, finishOperation, updateOperation, type Operation } from "./android-operations.ts";
|
|
24
|
+
import { avdHomeEnv } from "./sdk-discovery.ts";
|
|
25
|
+
|
|
26
|
+
/** How long to wait for the guest to report boot complete before calling it failed. */
|
|
27
|
+
const DEFAULT_BOOT_DEADLINE_MS = 5 * 60_000;
|
|
28
|
+
/** How often to ask. Cheap: a getStatus is sub-millisecond once the channel is up. */
|
|
29
|
+
const BOOT_POLL_MS = 1_000;
|
|
30
|
+
/** Emulator stderr/stdout kept for diagnostics. Bounded: a boot loop can log without end. */
|
|
31
|
+
const LOG_RING_LINES = 200;
|
|
32
|
+
|
|
33
|
+
export interface StartOptions {
|
|
34
|
+
avdName: string;
|
|
35
|
+
avdHome: string;
|
|
36
|
+
emulatorPath: string;
|
|
37
|
+
/** Extra flags the caller has already validated. Passed as argv, never through a shell. */
|
|
38
|
+
extraArgs?: string[];
|
|
39
|
+
bootDeadlineMs?: number;
|
|
40
|
+
/** Run without a visible window. `qt-hide-window` still creates one (and needs a display). */
|
|
41
|
+
windowMode?: "no-window" | "qt-hide-window" | "window";
|
|
42
|
+
gpuMode?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Quick boot resumes the saved snapshot and saves one on exit; cold boot ignores any snapshot
|
|
45
|
+
* and boots the guest from scratch.
|
|
46
|
+
*
|
|
47
|
+
* Neither touches user data — that is what **wipe** is for, and conflating the two is how a
|
|
48
|
+
* "boot fresh" button ends up deleting somebody's app state. A cold boot is minutes; a quick
|
|
49
|
+
* boot is seconds, which is why it is the default.
|
|
50
|
+
*/
|
|
51
|
+
bootMode?: "quick" | "cold";
|
|
52
|
+
/**
|
|
53
|
+
* How many emulators this host may run at once, starts still booting included. Counted here
|
|
54
|
+
* because only here is the count in the same synchronous step as the reservation below: a
|
|
55
|
+
* caller that counts, awaits, then starts lets two requests for two different AVDs both see
|
|
56
|
+
* the same free slot.
|
|
57
|
+
*/
|
|
58
|
+
maxConcurrent?: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A start refused because the host is already at `maxConcurrent`. */
|
|
62
|
+
export class EmulatorLimitError extends Error {
|
|
63
|
+
constructor(readonly busy: number, readonly limit: number) {
|
|
64
|
+
super(`already running or starting ${busy} emulator(s); the limit for this host is ${limit}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface StartedDevice {
|
|
69
|
+
avdName: string;
|
|
70
|
+
pid: number;
|
|
71
|
+
grpcPort: number;
|
|
72
|
+
adbSerial: string | null;
|
|
73
|
+
generation: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface InFlight {
|
|
77
|
+
operation: Operation<StartedDevice>;
|
|
78
|
+
promise: Promise<StartedDevice>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** One entry per AVD name while a start is running — this *is* the mutex. */
|
|
82
|
+
const starting = new Map<string, InFlight>();
|
|
83
|
+
/** Bounded log of what each emulator printed, for the error surface. */
|
|
84
|
+
const logs = new Map<string, string[]>();
|
|
85
|
+
|
|
86
|
+
export function emulatorLog(avdName: string): string[] {
|
|
87
|
+
return logs.get(avdName) ?? [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function appendLog(avdName: string, chunk: string): void {
|
|
91
|
+
const ring = logs.get(avdName) ?? [];
|
|
92
|
+
for (const line of chunk.split("\n")) {
|
|
93
|
+
if (!line.trim()) continue;
|
|
94
|
+
ring.push(line);
|
|
95
|
+
if (ring.length > LOG_RING_LINES) ring.shift();
|
|
96
|
+
}
|
|
97
|
+
logs.set(avdName, ring);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** The env the *child* gets. Shared with the SDK tools, which need the same treatment. */
|
|
101
|
+
const childEnv = avdHomeEnv;
|
|
102
|
+
|
|
103
|
+
/** Exported for tests: the flag set is a decision, and `-grpc`'s absence is load-bearing. */
|
|
104
|
+
export function buildArgs(opts: StartOptions): string[] {
|
|
105
|
+
const args = ["-avd", opts.avdName];
|
|
106
|
+
switch (opts.windowMode ?? "no-window") {
|
|
107
|
+
case "no-window": args.push("-no-window"); break;
|
|
108
|
+
case "qt-hide-window": args.push("-qt-hide-window"); break;
|
|
109
|
+
case "window": break;
|
|
110
|
+
}
|
|
111
|
+
args.push("-no-boot-anim");
|
|
112
|
+
// `-no-snapshot-load` only refuses to *resume* one; the guest's disk is untouched either way.
|
|
113
|
+
if ((opts.bootMode ?? "quick") === "cold") args.push("-no-snapshot-load");
|
|
114
|
+
if (opts.gpuMode) args.push("-gpu", opts.gpuMode);
|
|
115
|
+
// Deliberately absent: `-grpc <port>`. See the file header.
|
|
116
|
+
for (const extra of opts.extraArgs ?? []) args.push(extra);
|
|
117
|
+
return args;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
121
|
+
|
|
122
|
+
/** Poll discovery until this AVD advertises a gRPC endpoint, or the deadline passes. */
|
|
123
|
+
async function waitForAdvertisement(avdName: string, deadline: number): Promise<RunningEmulator> {
|
|
124
|
+
while (Date.now() < deadline) {
|
|
125
|
+
const found = findRunningEmulatorByAvd(avdName);
|
|
126
|
+
if (found) return found;
|
|
127
|
+
await sleep(250);
|
|
128
|
+
}
|
|
129
|
+
throw new Error(`emulator for ${avdName} never advertised a gRPC endpoint`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Poll the guest until it reports boot complete. */
|
|
133
|
+
async function waitForBoot(
|
|
134
|
+
emulator: RunningEmulator,
|
|
135
|
+
deadline: number,
|
|
136
|
+
onProgress: (detail: string) => void,
|
|
137
|
+
): Promise<void> {
|
|
138
|
+
const channel = connectToEmulator(emulator);
|
|
139
|
+
const startedAt = Date.now();
|
|
140
|
+
try {
|
|
141
|
+
while (Date.now() < deadline) {
|
|
142
|
+
try {
|
|
143
|
+
const status = await getEmulatorStatus(channel);
|
|
144
|
+
if (status.booted) return;
|
|
145
|
+
onProgress(`booting (${Math.round((Date.now() - startedAt) / 1000)}s)`);
|
|
146
|
+
} catch {
|
|
147
|
+
onProgress(`waiting for control channel (${Math.round((Date.now() - startedAt) / 1000)}s)`);
|
|
148
|
+
}
|
|
149
|
+
await sleep(BOOT_POLL_MS);
|
|
150
|
+
}
|
|
151
|
+
throw new Error("guest did not report boot complete before the deadline");
|
|
152
|
+
} finally {
|
|
153
|
+
channel.close();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Start an emulator, or return the start already running for this AVD.
|
|
159
|
+
*
|
|
160
|
+
* Returns immediately with an operation the caller polls; a cold boot is minutes and no HTTP
|
|
161
|
+
* request should be held that long.
|
|
162
|
+
*/
|
|
163
|
+
export function startEmulator(opts: StartOptions): Operation<StartedDevice> {
|
|
164
|
+
const inFlight = starting.get(opts.avdName);
|
|
165
|
+
if (inFlight) return inFlight.operation; // idempotent: same operation, no second process
|
|
166
|
+
|
|
167
|
+
const already = findRunningEmulatorByAvd(opts.avdName);
|
|
168
|
+
if (already) {
|
|
169
|
+
const op = createOperation<StartedDevice>("android.start", "already running");
|
|
170
|
+
finishOperation(op.id, {
|
|
171
|
+
avdName: opts.avdName,
|
|
172
|
+
pid: already.pid,
|
|
173
|
+
grpcPort: already.grpcPort,
|
|
174
|
+
adbSerial: already.adbSerial,
|
|
175
|
+
generation: 0,
|
|
176
|
+
});
|
|
177
|
+
return op;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (opts.maxConcurrent !== undefined) {
|
|
181
|
+
// A start in flight counts as much as a running emulator: it is minutes to boot, and until
|
|
182
|
+
// it advertises itself discovery cannot see it.
|
|
183
|
+
const busy = new Set([...findRunningEmulators().map((e) => e.avdName), ...starting.keys()]);
|
|
184
|
+
if (busy.size >= opts.maxConcurrent) throw new EmulatorLimitError(busy.size, opts.maxConcurrent);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const operation = createOperation<StartedDevice>("android.start", "spawning emulator");
|
|
188
|
+
const promise = run(opts, operation).finally(() => starting.delete(opts.avdName));
|
|
189
|
+
starting.set(opts.avdName, { operation, promise });
|
|
190
|
+
// The operation carries the outcome; an unobserved rejection here must not crash the server.
|
|
191
|
+
promise.catch(() => {});
|
|
192
|
+
return operation;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function run(opts: StartOptions, operation: Operation<StartedDevice>): Promise<StartedDevice> {
|
|
196
|
+
const deadline = Date.now() + (opts.bootDeadlineMs ?? DEFAULT_BOOT_DEADLINE_MS);
|
|
197
|
+
updateOperation(operation.id, { state: "running", detail: "spawning emulator" });
|
|
198
|
+
logs.delete(opts.avdName);
|
|
199
|
+
|
|
200
|
+
if (!existsSync(opts.emulatorPath)) {
|
|
201
|
+
const message = `emulator binary not found at ${opts.emulatorPath}`;
|
|
202
|
+
failOperation(operation.id, message);
|
|
203
|
+
throw new Error(message);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// argv, never a shell string: an AVD name or SDK path with a space or a quote in it would
|
|
207
|
+
// otherwise be re-split by the shell.
|
|
208
|
+
const child = spawn(opts.emulatorPath, buildArgs(opts), {
|
|
209
|
+
env: childEnv(opts.avdHome),
|
|
210
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
211
|
+
detached: false,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
child.stdout?.on("data", (d: Buffer) => appendLog(opts.avdName, d.toString()));
|
|
215
|
+
child.stderr?.on("data", (d: Buffer) => appendLog(opts.avdName, d.toString()));
|
|
216
|
+
|
|
217
|
+
let spawnError: Error | null = null;
|
|
218
|
+
child.on("error", (e) => { spawnError = e; });
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
updateOperation(operation.id, { detail: "waiting for the emulator to advertise itself" });
|
|
222
|
+
const emulator = await waitForAdvertisement(opts.avdName, deadline);
|
|
223
|
+
|
|
224
|
+
updateOperation(operation.id, { detail: "booting" });
|
|
225
|
+
await waitForBoot(emulator, deadline, (detail) => updateOperation(operation.id, { detail }));
|
|
226
|
+
|
|
227
|
+
const generation = claimOwnership(opts.avdName, emulator.pid);
|
|
228
|
+
const started: StartedDevice = {
|
|
229
|
+
avdName: opts.avdName,
|
|
230
|
+
pid: emulator.pid,
|
|
231
|
+
grpcPort: emulator.grpcPort,
|
|
232
|
+
adbSerial: emulator.adbSerial,
|
|
233
|
+
generation,
|
|
234
|
+
};
|
|
235
|
+
finishOperation(operation.id, started);
|
|
236
|
+
return started;
|
|
237
|
+
} catch (e) {
|
|
238
|
+
const tail = emulatorLog(opts.avdName).slice(-6).join("\n");
|
|
239
|
+
const base = spawnError ? `${(spawnError as Error).message}` : (e as Error).message;
|
|
240
|
+
const message = tail ? `${base}\n${tail}` : base;
|
|
241
|
+
failOperation(operation.id, message);
|
|
242
|
+
throw new Error(message);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export type StopOutcome = "stopped" | "not-running" | "not-owned";
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Stop an emulator PPM started.
|
|
250
|
+
*
|
|
251
|
+
* Graceful only: `setVmState SHUTDOWN` over gRPC, which measured at 1-3 seconds. Nothing here
|
|
252
|
+
* signals a pid, and certainly not by process *name* — on this very host `pgrep qemu-system`
|
|
253
|
+
* matches Docker Desktop.
|
|
254
|
+
*/
|
|
255
|
+
export async function stopEmulator(
|
|
256
|
+
avdName: string,
|
|
257
|
+
opts: { avdHome: string; requireOwnership?: boolean },
|
|
258
|
+
): Promise<StopOutcome> {
|
|
259
|
+
const emulator = findRunningEmulatorByAvd(avdName);
|
|
260
|
+
if (!emulator) {
|
|
261
|
+
releaseOwnership(avdName);
|
|
262
|
+
return "not-running";
|
|
263
|
+
}
|
|
264
|
+
if (opts.requireOwnership !== false) {
|
|
265
|
+
const entry = listDevices(opts.avdHome).find((d) => d.name === avdName);
|
|
266
|
+
if (entry?.runtime && !entry.runtime.ownedByPpm) return "not-owned";
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const channel = connectToEmulator(emulator);
|
|
270
|
+
try {
|
|
271
|
+
await requestShutdown(channel);
|
|
272
|
+
} finally {
|
|
273
|
+
channel.close();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Wait for the process to actually go, so the caller can report truthfully.
|
|
277
|
+
const deadline = Date.now() + 30_000;
|
|
278
|
+
while (Date.now() < deadline) {
|
|
279
|
+
try { process.kill(emulator.pid, 0); } catch { break; }
|
|
280
|
+
await sleep(250);
|
|
281
|
+
}
|
|
282
|
+
releaseOwnership(avdName);
|
|
283
|
+
return "stopped";
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Test seam. */
|
|
287
|
+
export function _resetLauncher(): void {
|
|
288
|
+
starting.clear();
|
|
289
|
+
logs.clear();
|
|
290
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// GENERATED by scripts/gen-android-proto.ts — do not edit.
|
|
2
|
+
//
|
|
3
|
+
// Source: emulator_controller.proto (Apache 2.0, The Android Open Source Project)
|
|
4
|
+
// Taken from: $ANDROID_SDK_ROOT/emulator/lib/emulator_controller.proto
|
|
5
|
+
// Emulator: 36.5.10.0 (build 15081367)
|
|
6
|
+
// SHA-256: e872af01d8ff0a46d2d19c644709e4d524be609e9cff69f75b06ab0bb67653dd
|
|
7
|
+
//
|
|
8
|
+
// Embedded rather than loaded from disk: a compiled binary cannot read the .proto file.
|
|
9
|
+
export const EMULATOR_CONTROLLER_PROTO_SHA256 = "e872af01d8ff0a46d2d19c644709e4d524be609e9cff69f75b06ab0bb67653dd";
|
|
10
|
+
export const emulatorControllerDescriptor = {"options":{"java_multiple_files":true,"java_package":"com.android.emulator.control","objc_class_prefix":"AEC"},"nested":{"android":{"nested":{"emulation":{"nested":{"control":{"nested":{"EmulatorController":{"methods":{"streamSensor":{"requestType":"SensorValue","responseType":"SensorValue","responseStream":true},"getSensor":{"requestType":"SensorValue","responseType":"SensorValue"},"setSensor":{"requestType":"SensorValue","responseType":"google.protobuf.Empty"},"setPhysicalModel":{"requestType":"PhysicalModelValue","responseType":"google.protobuf.Empty"},"getPhysicalModel":{"requestType":"PhysicalModelValue","responseType":"PhysicalModelValue"},"streamPhysicalModel":{"requestType":"PhysicalModelValue","responseType":"PhysicalModelValue","responseStream":true},"setClipboard":{"requestType":"ClipData","responseType":"google.protobuf.Empty"},"getClipboard":{"requestType":"google.protobuf.Empty","responseType":"ClipData"},"streamClipboard":{"requestType":"google.protobuf.Empty","responseType":"ClipData","responseStream":true},"setBattery":{"requestType":"BatteryState","responseType":"google.protobuf.Empty"},"getBattery":{"requestType":"google.protobuf.Empty","responseType":"BatteryState"},"setGps":{"requestType":"GpsState","responseType":"google.protobuf.Empty"},"getGps":{"requestType":"google.protobuf.Empty","responseType":"GpsState"},"sendFingerprint":{"requestType":"Fingerprint","responseType":"google.protobuf.Empty"},"sendKey":{"requestType":"KeyboardEvent","responseType":"google.protobuf.Empty"},"sendTouch":{"requestType":"TouchEvent","responseType":"google.protobuf.Empty"},"sendMouse":{"requestType":"MouseEvent","responseType":"google.protobuf.Empty"},"injectWheel":{"requestType":"WheelEvent","requestStream":true,"responseType":"google.protobuf.Empty"},"streamInputEvent":{"requestType":"InputEvent","requestStream":true,"responseType":"google.protobuf.Empty"},"sendPhone":{"requestType":"PhoneCall","responseType":"PhoneResponse"},"sendSms":{"requestType":"SmsMessage","responseType":"PhoneResponse"},"setPhoneNumber":{"requestType":"PhoneNumber","responseType":"PhoneResponse"},"getStatus":{"requestType":"google.protobuf.Empty","responseType":"EmulatorStatus"},"getScreenshot":{"requestType":"ImageFormat","responseType":"Image"},"streamScreenshot":{"requestType":"ImageFormat","responseType":"Image","responseStream":true},"streamAudio":{"requestType":"AudioFormat","responseType":"AudioPacket","responseStream":true},"injectAudio":{"requestType":"AudioPacket","requestStream":true,"responseType":"google.protobuf.Empty"},"getMicrophoneState":{"requestType":"google.protobuf.Empty","responseType":"MicrophoneState"},"setMicrophoneState":{"requestType":"MicrophoneState","responseType":"google.protobuf.Empty"},"getLogcat":{"requestType":"LogMessage","responseType":"LogMessage","options":{"deprecated":true},"parsedOptions":[{"deprecated":true}]},"streamLogcat":{"requestType":"LogMessage","responseType":"LogMessage","responseStream":true},"setVmState":{"requestType":"VmRunState","responseType":"google.protobuf.Empty"},"getVmState":{"requestType":"google.protobuf.Empty","responseType":"VmRunState"},"setDisplayConfigurations":{"requestType":"DisplayConfigurations","responseType":"DisplayConfigurations"},"getDisplayConfigurations":{"requestType":"google.protobuf.Empty","responseType":"DisplayConfigurations"},"streamNotification":{"requestType":"google.protobuf.Empty","responseType":"Notification","responseStream":true},"rotateVirtualSceneCamera":{"requestType":"RotationRadian","responseType":"google.protobuf.Empty"},"setVirtualSceneCameraVelocity":{"requestType":"Velocity","responseType":"google.protobuf.Empty"},"setPosture":{"requestType":"Posture","responseType":"google.protobuf.Empty"},"getBrightness":{"requestType":"BrightnessValue","responseType":"BrightnessValue"},"setBrightness":{"requestType":"BrightnessValue","responseType":"google.protobuf.Empty"},"getDisplayMode":{"requestType":"google.protobuf.Empty","responseType":"DisplayMode"},"setDisplayMode":{"requestType":"DisplayMode","responseType":"google.protobuf.Empty"},"setXrOptions":{"requestType":"XrOptions","responseType":"google.protobuf.Empty"},"getXrOptions":{"requestType":"google.protobuf.Empty","responseType":"XrOptions"}}},"VmRunState":{"fields":{"state":{"type":"RunState","id":1}},"nested":{"RunState":{"values":{"UNKNOWN":0,"RUNNING":1,"RESTORE_VM":2,"PAUSED":3,"SAVE_VM":4,"SHUTDOWN":5,"TERMINATE":7,"RESET":9,"INTERNAL_ERROR":10,"RESTART":11,"START":12,"STOP":13}}}},"ParameterValue":{"fields":{"data":{"rule":"repeated","type":"float","id":1,"options":{"packed":true}}}},"PhysicalModelValue":{"fields":{"target":{"type":"PhysicalType","id":1},"status":{"type":"State","id":2},"value":{"type":"ParameterValue","id":3}},"nested":{"State":{"values":{"OK":0,"NO_SERVICE":-3,"DISABLED":-2,"UNKNOWN":-1}},"PhysicalType":{"values":{"POSITION":0,"ROTATION":1,"MAGNETIC_FIELD":2,"TEMPERATURE":3,"PROXIMITY":4,"LIGHT":5,"PRESSURE":6,"HUMIDITY":7,"VELOCITY":8,"AMBIENT_MOTION":9,"HINGE_ANGLE0":10,"HINGE_ANGLE1":11,"HINGE_ANGLE2":12,"ROLLABLE0":13,"ROLLABLE1":14,"ROLLABLE2":15,"POSTURE":16,"HEART_RATE":17,"RGBC_LIGHT":18,"WRIST_TILT":19}}}},"SensorValue":{"fields":{"target":{"type":"SensorType","id":1},"status":{"type":"State","id":2},"value":{"type":"ParameterValue","id":3}},"nested":{"State":{"values":{"OK":0,"NO_SERVICE":-3,"DISABLED":-2,"UNKNOWN":-1}},"SensorType":{"values":{"ACCELERATION":0,"GYROSCOPE":1,"MAGNETIC_FIELD":2,"ORIENTATION":3,"TEMPERATURE":4,"PROXIMITY":5,"LIGHT":6,"PRESSURE":7,"HUMIDITY":8,"MAGNETIC_FIELD_UNCALIBRATED":9,"GYROSCOPE_UNCALIBRATED":10,"HEART_RATE":14,"RGBC_LIGHT":15,"ACCELERATION_UNCALIBRATED":17,"HEADING":18}}}},"BrightnessValue":{"fields":{"target":{"type":"LightType","id":1},"value":{"type":"uint32","id":2}},"nested":{"LightType":{"values":{"LCD":0,"KEYBOARD":1,"BUTTON":2}}}},"DisplayModeValue":{"values":{"PHONE":0,"FOLDABLE":1,"TABLET":2,"DESKTOP":3}},"DisplayMode":{"fields":{"value":{"type":"DisplayModeValue","id":1}}},"XrOptions":{"fields":{"environment":{"type":"Environment","id":1},"passthroughCoefficient":{"type":"float","id":2}},"nested":{"Environment":{"values":{"LIVING_ROOM_DAY":0,"LIVING_ROOM_NIGHT":1}}}},"LogMessage":{"fields":{"contents":{"type":"string","id":1},"start":{"type":"int64","id":2,"options":{"deprecated":true}},"next":{"type":"int64","id":3,"options":{"deprecated":true}},"sort":{"type":"LogType","id":4},"entries":{"rule":"repeated","type":"LogcatEntry","id":5}},"nested":{"LogType":{"values":{"Text":0,"Parsed":1}}}},"LogcatEntry":{"fields":{"timestamp":{"type":"uint64","id":1},"pid":{"type":"uint32","id":2},"tid":{"type":"uint32","id":3},"level":{"type":"LogLevel","id":4},"tag":{"type":"string","id":5},"msg":{"type":"string","id":6}},"nested":{"LogLevel":{"values":{"UNKNOWN":0,"DEFAULT":1,"VERBOSE":2,"DEBUG":3,"INFO":4,"WARN":5,"ERR":6,"FATAL":7,"SILENT":8}}}},"VmConfiguration":{"fields":{"hypervisorType":{"type":"VmHypervisorType","id":1},"numberOfCpuCores":{"type":"int32","id":2},"ramSizeBytes":{"type":"int64","id":3}},"nested":{"VmHypervisorType":{"valuesOptions":{"HAXM":{"deprecated":true}},"values":{"UNKNOWN":0,"NONE":1,"KVM":2,"HAXM":3,"HVF":4,"WHPX":5,"AEHD":6}}}},"ClipData":{"fields":{"text":{"type":"string","id":1}}},"Touch":{"fields":{"x":{"type":"int32","id":1},"y":{"type":"int32","id":2},"identifier":{"type":"int32","id":3},"pressure":{"type":"int32","id":4},"touchMajor":{"type":"int32","id":5},"touchMinor":{"type":"int32","id":6},"expiration":{"type":"EventExpiration","id":7},"orientation":{"type":"int32","id":8}},"nested":{"EventExpiration":{"values":{"EVENT_EXPIRATION_UNSPECIFIED":0,"NEVER_EXPIRE":1}}}},"Pen":{"fields":{"location":{"type":"Touch","id":1},"buttonPressed":{"type":"bool","id":2},"rubberPointer":{"type":"bool","id":3}}},"TouchEvent":{"fields":{"touches":{"rule":"repeated","type":"Touch","id":1},"display":{"type":"int32","id":2}}},"TouchpadEvent":{"fields":{"touches":{"rule":"repeated","type":"Touch","id":1},"touchpad":{"type":"int32","id":2}}},"PenEvent":{"fields":{"events":{"rule":"repeated","type":"Pen","id":1},"display":{"type":"int32","id":2}}},"MouseEvent":{"fields":{"x":{"type":"int32","id":1},"y":{"type":"int32","id":2},"buttons":{"type":"int32","id":3},"display":{"type":"int32","id":4}}},"WheelEvent":{"fields":{"dx":{"type":"int32","id":1},"dy":{"type":"int32","id":2},"display":{"type":"int32","id":3}}},"KeyboardEvent":{"fields":{"codeType":{"type":"KeyCodeType","id":1},"eventType":{"type":"KeyEventType","id":2},"keyCode":{"type":"int32","id":3},"key":{"type":"string","id":4},"text":{"type":"string","id":5}},"nested":{"KeyCodeType":{"values":{"Usb":0,"Evdev":1,"XKB":2,"Win":3,"Mac":4}},"KeyEventType":{"values":{"keydown":0,"keyup":1,"keypress":2}}}},"XrCommand":{"fields":{"action":{"type":"Action","id":1}},"nested":{"Action":{"values":{"RECENTER":0}}}},"InputEvent":{"oneofs":{"type":{"oneof":["keyEvent","touchEvent","mouseEvent","androidEvent","penEvent","wheelEvent","xrHandEvent","xrEyeEvent","xrCommand","xrHeadRotationEvent","xrHeadMovementEvent","xrHeadAngularVelocityEvent","xrHeadVelocityEvent","touchpadEvent"]}},"fields":{"keyEvent":{"type":"KeyboardEvent","id":1},"touchEvent":{"type":"TouchEvent","id":2},"mouseEvent":{"type":"MouseEvent","id":3},"androidEvent":{"type":"AndroidEvent","id":4},"penEvent":{"type":"PenEvent","id":5},"wheelEvent":{"type":"WheelEvent","id":6},"xrHandEvent":{"type":"MouseEvent","id":7},"xrEyeEvent":{"type":"MouseEvent","id":8},"xrCommand":{"type":"XrCommand","id":9},"xrHeadRotationEvent":{"type":"RotationRadian","id":10},"xrHeadMovementEvent":{"type":"Translation","id":11},"xrHeadAngularVelocityEvent":{"type":"AngularVelocity","id":12},"xrHeadVelocityEvent":{"type":"Velocity","id":13},"touchpadEvent":{"type":"TouchpadEvent","id":14}}},"AndroidEvent":{"fields":{"type":{"type":"int32","id":1},"code":{"type":"int32","id":2},"value":{"type":"int32","id":3},"display":{"type":"int32","id":4}}},"Fingerprint":{"fields":{"isTouching":{"type":"bool","id":1},"touchId":{"type":"int32","id":2}}},"GpsState":{"fields":{"passiveUpdate":{"type":"bool","id":1},"latitude":{"type":"double","id":2},"longitude":{"type":"double","id":3},"speed":{"type":"double","id":4},"bearing":{"type":"double","id":5},"altitude":{"type":"double","id":6},"satellites":{"type":"int32","id":7}}},"BatteryState":{"fields":{"hasBattery":{"type":"bool","id":1},"isPresent":{"type":"bool","id":2},"charger":{"type":"BatteryCharger","id":3},"chargeLevel":{"type":"int32","id":4},"health":{"type":"BatteryHealth","id":5},"status":{"type":"BatteryStatus","id":6}},"nested":{"BatteryStatus":{"values":{"UNKNOWN":0,"CHARGING":1,"DISCHARGING":2,"NOT_CHARGING":3,"FULL":4}},"BatteryCharger":{"values":{"NONE":0,"AC":1,"USB":2,"WIRELESS":3}},"BatteryHealth":{"values":{"GOOD":0,"FAILED":1,"DEAD":2,"OVERVOLTAGE":3,"OVERHEATED":4}}}},"ImageTransport":{"fields":{"channel":{"type":"TransportChannel","id":1},"handle":{"type":"string","id":2}},"nested":{"TransportChannel":{"values":{"TRANSPORT_CHANNEL_UNSPECIFIED":0,"MMAP":1}}}},"FoldedDisplay":{"fields":{"width":{"type":"uint32","id":1},"height":{"type":"uint32","id":2},"xOffset":{"type":"uint32","id":3},"yOffset":{"type":"uint32","id":4}}},"ImageFormat":{"fields":{"format":{"type":"ImgFormat","id":1},"rotation":{"type":"Rotation","id":2},"width":{"type":"uint32","id":3},"height":{"type":"uint32","id":4},"display":{"type":"uint32","id":5},"transport":{"type":"ImageTransport","id":6},"foldedDisplay":{"type":"FoldedDisplay","id":7},"displayMode":{"type":"DisplayModeValue","id":8}},"nested":{"ImgFormat":{"values":{"PNG":0,"RGBA8888":1,"RGB888":2}}}},"Image":{"fields":{"format":{"type":"ImageFormat","id":1},"width":{"type":"uint32","id":2,"options":{"deprecated":true}},"height":{"type":"uint32","id":3,"options":{"deprecated":true}},"image":{"type":"bytes","id":4},"seq":{"type":"uint32","id":5},"timestampUs":{"type":"uint64","id":6}}},"Rotation":{"fields":{"rotation":{"type":"SkinRotation","id":1},"xAxis":{"type":"double","id":2},"yAxis":{"type":"double","id":3},"zAxis":{"type":"double","id":4}},"nested":{"SkinRotation":{"values":{"PORTRAIT":0,"LANDSCAPE":1,"REVERSE_PORTRAIT":2,"REVERSE_LANDSCAPE":3}}}},"PhoneCall":{"fields":{"operation":{"type":"Operation","id":1},"number":{"type":"string","id":2}},"nested":{"Operation":{"values":{"InitCall":0,"AcceptCall":1,"RejectCallExplicit":2,"RejectCallBusy":3,"DisconnectCall":4,"PlaceCallOnHold":5,"TakeCallOffHold":6}}}},"PhoneResponse":{"fields":{"response":{"type":"Response","id":1}},"nested":{"Response":{"values":{"OK":0,"BadOperation":1,"BadNumber":2,"InvalidAction":3,"ActionFailed":4,"RadioOff":5}}}},"Entry":{"fields":{"key":{"type":"string","id":1},"value":{"type":"string","id":2}}},"EntryList":{"fields":{"entry":{"rule":"repeated","type":"Entry","id":1}}},"EmulatorStatus":{"fields":{"version":{"type":"string","id":1},"uptime":{"type":"uint64","id":2},"booted":{"type":"bool","id":3},"vmConfig":{"type":"VmConfiguration","id":4},"hardwareConfig":{"type":"EntryList","id":5},"heartbeat":{"type":"uint64","id":6},"guestConfig":{"keyType":"string","type":"string","id":7}}},"AudioFormat":{"fields":{"samplingRate":{"type":"uint64","id":1},"channels":{"type":"Channels","id":2},"format":{"type":"SampleFormat","id":3},"mode":{"type":"DeliveryMode","id":4}},"nested":{"SampleFormat":{"values":{"AUD_FMT_U8":0,"AUD_FMT_S16":1}},"Channels":{"values":{"Mono":0,"Stereo":1}},"DeliveryMode":{"values":{"MODE_UNSPECIFIED":0,"MODE_REAL_TIME":1}}}},"AudioPacket":{"fields":{"format":{"type":"AudioFormat","id":1},"timestamp":{"type":"uint64","id":2},"audio":{"type":"bytes","id":3}}},"MicrophoneState":{"fields":{"realAudioEnabled":{"type":"bool","id":1}}},"SmsMessage":{"fields":{"srcAddress":{"type":"string","id":1},"text":{"type":"string","id":2}}},"DisplayConfiguration":{"fields":{"width":{"type":"uint32","id":1},"height":{"type":"uint32","id":2},"dpi":{"type":"uint32","id":3},"flags":{"type":"uint32","id":4},"display":{"type":"uint32","id":5}},"nested":{"DisplayFlags":{"values":{"DISPLAYFLAGS_UNSPECIFIED":0,"VIRTUAL_DISPLAY_FLAG_PUBLIC":1,"VIRTUAL_DISPLAY_FLAG_PRESENTATION":2,"VIRTUAL_DISPLAY_FLAG_SECURE":4,"VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY":8,"VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR":16}}}},"DisplayConfigurations":{"fields":{"displays":{"rule":"repeated","type":"DisplayConfiguration","id":1},"userConfigurable":{"type":"uint32","id":2},"maxDisplays":{"type":"uint32","id":3}}},"Notification":{"oneofs":{"type":{"oneof":["cameraNotification","displayConfigurationsChangedNotification","posture","booted","brightness","textViewFocus","xrOptions","microphoneState"]}},"fields":{"cameraNotification":{"type":"CameraNotification","id":2},"displayConfigurationsChangedNotification":{"type":"DisplayConfigurationsChangedNotification","id":3},"posture":{"type":"Posture","id":4},"booted":{"type":"BootCompletedNotification","id":5},"brightness":{"type":"BrightnessValue","id":6},"textViewFocus":{"type":"TextViewFocus","id":7},"xrOptions":{"type":"XrOptions","id":8},"microphoneState":{"type":"MicrophoneState","id":9}}},"BootCompletedNotification":{"fields":{"time":{"type":"int32","id":1}}},"CameraNotification":{"fields":{"active":{"type":"bool","id":1},"display":{"type":"int32","id":2}}},"TextViewFocus":{"fields":{"textViewHasFocus":{"type":"bool","id":1},"display":{"type":"int32","id":2}}},"DisplayConfigurationsChangedNotification":{"fields":{"displayConfigurations":{"type":"DisplayConfigurations","id":1}}},"RotationRadian":{"fields":{"x":{"type":"float","id":1},"y":{"type":"float","id":2},"z":{"type":"float","id":3}}},"Translation":{"fields":{"deltaX":{"type":"float","id":1},"deltaY":{"type":"float","id":2},"deltaZ":{"type":"float","id":3}}},"AngularVelocity":{"fields":{"omegaX":{"type":"float","id":1},"omegaY":{"type":"float","id":2},"omegaZ":{"type":"float","id":3}}},"Velocity":{"fields":{"x":{"type":"float","id":1},"y":{"type":"float","id":2},"z":{"type":"float","id":3}}},"Posture":{"fields":{"value":{"type":"PostureValue","id":3}},"nested":{"PostureValue":{"values":{"POSTURE_UNKNOWN":0,"POSTURE_CLOSED":1,"POSTURE_HALF_OPENED":2,"POSTURE_OPENED":3,"POSTURE_FLIPPED":4,"POSTURE_TENT":5,"POSTURE_MAX":6}}}},"PhoneNumber":{"fields":{"number":{"type":"string","id":1}}}}}}}}},"google":{"nested":{"protobuf":{"nested":{"Empty":{"fields":{}}}}}}}} as const;
|