@hienlh/ppm 0.18.15 → 0.19.0
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 +4 -0
- 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 +2 -1
- package/dist/web/assets/{adaptive-context-menu-ChBZWLLq.js → adaptive-context-menu-JfuUSqbe.js} +1 -1
- package/dist/web/assets/{ai-resource-editor-C8aAY8Po.js → ai-resource-editor-MfdJKVM5.js} +1 -1
- package/dist/web/assets/ai-settings-section-CJE7jFsj.js +1 -0
- package/dist/web/assets/{angular-html-CXgHIQ2U.js → angular-html-VzxSzTCy.js} +1 -1
- package/dist/web/assets/{angular-ts-C8renMV9.js → angular-ts-CmJ4bmzM.js} +1 -1
- package/dist/web/assets/api-client-DLWwuUkJ.js +1 -0
- package/dist/web/assets/{api-client-BkGKTjsq.js → api-client-qRWu6_s5.js} +1 -1
- package/dist/web/assets/api-settings-BFuaZrNC.js +1 -0
- package/dist/web/assets/{api-settings-R59c66DE.js → api-settings-CPg4yGvS.js} +1 -1
- package/dist/web/assets/{api-themes-DKxrwJda.js → api-themes-DsAp8m7m.js} +1 -1
- package/dist/web/assets/{apl-DkGH8laI.js → apl-do17bnVx.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-6konx-J1.js +1 -0
- package/dist/web/assets/{astro-tHUd8Zlg.js → astro-ClY0e0OA.js} +1 -1
- package/dist/web/assets/{audio-preview-B_C6Dpvz.js → audio-preview-Fw935Mok.js} +1 -1
- package/dist/web/assets/{blade-BbKpjIYK.js → blade-BXSdXDGq.js} +1 -1
- package/dist/web/assets/{bot-D6sx11y0.js → bot-CWZAuloY.js} +1 -1
- package/dist/web/assets/{bsl-DA0UYto0.js → bsl-MxsMKDUZ.js} +1 -1
- package/dist/web/assets/{button-DBSpDPS0.js → button-CrwgGd1y.js} +1 -1
- package/dist/web/assets/{cairo-79ayucbB.js → cairo-4CIrzX_p.js} +1 -1
- package/dist/web/assets/chat-tab-bSOVoC5N.js +15 -0
- package/dist/web/assets/{chevron-down-Dp0rpHb8.js → chevron-down-D7CbB5f6.js} +1 -1
- package/dist/web/assets/chevron-right-D7Pn5agw.js +1 -0
- package/dist/web/assets/circle-4g2coECH.js +1 -0
- package/dist/web/assets/{circle-question-mark-CzR7YRc-.js → circle-question-mark-BBzFZrNh.js} +1 -1
- package/dist/web/assets/clock-DbiNiwCG.js +1 -0
- package/dist/web/assets/{cobol-DSS2EOYo.js → cobol-phPOE89d.js} +1 -1
- package/dist/web/assets/code-DdKXqtf_.js +1 -0
- package/dist/web/assets/code-editor-COTa37oe.js +10 -0
- package/dist/web/assets/{coffee-DvSxq2iD.js → coffee-Cjuukfz3.js} +1 -1
- package/dist/web/assets/{conflict-editor-C4VCv51R.js → conflict-editor-Ni0PPsKu.js} +1 -1
- package/dist/web/assets/{context-menu-BpwUny-o.js → context-menu-BEAa1qhd.js} +1 -1
- package/dist/web/assets/{copy-BewHj66E.js → copy-CbPdNp7E.js} +1 -1
- package/dist/web/assets/{cpp-BhWZ-FSn.js → cpp-CT7tGBC3.js} +1 -1
- package/dist/web/assets/{createLucideIcon-D8pyde1A.js → createLucideIcon-ConhwJnb.js} +1 -1
- package/dist/web/assets/{crystal-yP2OeF-f.js → crystal-D8U1Qnqh.js} +1 -1
- package/dist/web/assets/{csv-preview-D46W_icb.js → csv-preview-Coc2oed1.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-DeGkPYtA.js → data-grid-overlay-editor-B8WQkZDB.js} +1 -1
- package/dist/web/assets/{data-grid-types-DkEQ-Na9.js → data-grid-types-KpjaKAci.js} +1 -1
- package/dist/web/assets/{database-Y3ESDK4-.js → database-BqyeGogo.js} +1 -1
- package/dist/web/assets/{database-viewer-DFI4YtrH.js → database-viewer-Dta7lSSk.js} +1 -1
- package/dist/web/assets/{dialog-RIpeb87v.js → dialog-DrL3WEXd.js} +1 -1
- package/dist/web/assets/{diff-viewer-eaNOLcBL.js → diff-viewer-CT0lmDc9.js} +1 -1
- package/dist/web/assets/{dist-Do3fljss.js → dist-C2_dKAa_.js} +1 -1
- package/dist/web/assets/{docx-preview-Cbj-pOK3.js → docx-preview-76KDw1eE.js} +1 -1
- package/dist/web/assets/{download-DG3_9xXy.js → download-DAF7SXcf.js} +1 -1
- package/dist/web/assets/{edge-D3K9cd-p.js → edge-Db4ze6bw.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-DDqy3KLA.js → edit-diff-preview-DyC_-4My.js} +1 -1
- package/dist/web/assets/{elixir-D4PBmsLp.js → elixir-RZPZ0472.js} +1 -1
- package/dist/web/assets/{elm-DSpXjuaH.js → elm-DNZcQt_7.js} +1 -1
- package/dist/web/assets/{erb-CaYZMTJW.js → erb-DRONsZXs.js} +1 -1
- package/dist/web/assets/{erlang-DX5BFqJj.js → erlang-DeG0-QwJ.js} +1 -1
- package/dist/web/assets/{esm-Cv7HsFnk.js → esm-CZy-sSoK.js} +1 -1
- package/dist/web/assets/explorer-body-BGjR2Gbt.js +2 -0
- package/dist/web/assets/explorer-window-content-DAhHh5EQ.js +1 -0
- package/dist/web/assets/{extension-store-B1EClUAr.js → extension-store-ClQpnxQr.js} +1 -1
- package/dist/web/assets/{extension-webview-BaGvDk8v.js → extension-webview-BW5GuBnH.js} +1 -1
- package/dist/web/assets/{external-link-B6Hl0U4X.js → external-link-ojDHcQxQ.js} +1 -1
- package/dist/web/assets/{eye-off-CUdzgmX8.js → eye-off-Rq1n2-oe.js} +1 -1
- package/dist/web/assets/{file-exclamation-point-JzhTU2TX.js → file-exclamation-point-Bpg-zDPP.js} +1 -1
- package/dist/web/assets/{file-store-m9crh6uq.js → file-store-28e44KP3.js} +1 -1
- package/dist/web/assets/{folder-symlink-Dx1hOk8X.js → folder-symlink-LD-mr54V.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-B0bAufUe.js → fortran-fixed-form-CK5VRJNd.js} +1 -1
- package/dist/web/assets/{fsharp-C2AL2b1M.js → fsharp-TVVgzHWp.js} +1 -1
- package/dist/web/assets/{gdresource-DRQsDMNI.js → gdresource-DiUcyewI.js} +1 -1
- package/dist/web/assets/{git-commit-GtBdnrzk.js → git-commit-CfOruFrg.js} +1 -1
- package/dist/web/assets/{git-log-panel-hwV1flSO.js → git-log-panel-D1-YXZ0O.js} +1 -1
- package/dist/web/assets/{git-rebase-h_0CGA6F.js → git-rebase-Bxyr24oc.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-ikUwsekd.js +1 -0
- package/dist/web/assets/{glide-data-grid-YsN-xm6x.js → glide-data-grid-BSsqxe2v.js} +6 -6
- package/dist/web/assets/{glimmer-js-Bxwu93bS.js → glimmer-js-CT5aJxz_.js} +1 -1
- package/dist/web/assets/{glimmer-ts-CIUYdR4E.js → glimmer-ts-CRjP9Yey.js} +1 -1
- package/dist/web/assets/{glsl-CfxLI5jn.js → glsl-BX2--oI_.js} +1 -1
- package/dist/web/assets/{graphql-CstqDPde.js → graphql-BIQsvBxQ.js} +1 -1
- package/dist/web/assets/{group-chat-tab-B7mJO1gG.js → group-chat-tab-CMtzoaFc.js} +1 -1
- package/dist/web/assets/{hack-Bjqnssb5.js → hack-CTWk0k1m.js} +1 -1
- package/dist/web/assets/{haml-lVc-DfbM.js → haml-DGh27hXc.js} +1 -1
- package/dist/web/assets/{hand-x0ejq3fS.js → hand-DKSxDa5c.js} +1 -1
- package/dist/web/assets/{handlebars-Yy6fPQI_.js → handlebars-BJL1jNrG.js} +1 -1
- package/dist/web/assets/{html-lqtcy6-2.js → html-DALyDNqU.js} +1 -1
- package/dist/web/assets/{html-derivative-CchzCY1z.js → html-derivative-B4XcU57e.js} +1 -1
- package/dist/web/assets/{http-ChclRrko.js → http-DfMo7nNu.js} +1 -1
- package/dist/web/assets/{hurl-CoOK7sXa.js → hurl-CGc_n2Qf.js} +1 -1
- package/dist/web/assets/{hxml-fZSZ7Iyt.js → hxml-40zZ1t4x.js} +1 -1
- package/dist/web/assets/{image-preview-ClAcqDlL.js → image-preview-Cp3XB8UO.js} +1 -1
- package/dist/web/assets/index-BkHlMfPD.js +52 -0
- package/dist/web/assets/index-SFFv2-VM.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-BAgmboBp.js +1 -0
- package/dist/web/assets/{input-DXPgUtxi.js → input-DTmI80l2.js} +1 -1
- package/dist/web/assets/{jinja-Y0w_KBdE.js → jinja-D5zuIn9u.js} +1 -1
- package/dist/web/assets/{jison-BILj3F2e.js → jison-Dxut9QwA.js} +1 -1
- package/dist/web/assets/{julia-DRfOPDts.js → julia-Cu85tTPo.js} +1 -1
- package/dist/web/assets/{just-n3U_oODO.js → just-CctM4uLa.js} +1 -1
- package/dist/web/assets/{keybindings-store-D-CHdq-G.js → keybindings-store-Bi0q9x4Q.js} +1 -1
- package/dist/web/assets/keybindings-store-_KfovSz7.js +1 -0
- package/dist/web/assets/{keyboard-DBBaoQSu.js → keyboard-AdwmYDSu.js} +1 -1
- package/dist/web/assets/{latex-D6gztEOL.js → latex-kpwS_CC7.js} +1 -1
- package/dist/web/assets/{lib-CXUwxw1M.js → lib-Ca7ai8xa.js} +1 -1
- package/dist/web/assets/{liquid-DPqCEVb9.js → liquid-B8Rr0xue.js} +1 -1
- package/dist/web/assets/loader-circle-DD65e9-7.js +1 -0
- package/dist/web/assets/{lock-CNxUGpTk.js → lock-BEtt_eTO.js} +1 -1
- package/dist/web/assets/{lua-I40KPUm-.js → lua-C_3gixwM.js} +1 -1
- package/dist/web/assets/markdown-renderer-DroTmKbJ.js +1 -0
- package/dist/web/assets/markdown-renderer-ntDi3Tx-.js +3 -0
- package/dist/web/assets/{marko-DMjsThSW.js → marko-B28rvNL-.js} +1 -1
- package/dist/web/assets/{mdc-CN2alsbo.js → mdc-DQ6-STFO.js} +1 -1
- package/dist/web/assets/{media-url-COUvasjn.js → media-url-BjvbfTTl.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-Cj1YBq-c.js → mobile-bottom-sheet-DXn5g2LB.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-BP6iU4ma.js +1 -0
- package/dist/web/assets/{monitor-C835ucWH.js → monitor-CXI_XpWJ.js} +1 -1
- package/dist/web/assets/monitor-smartphone-C1wUPOSg.js +1 -0
- package/dist/web/assets/{nextflow-CGa4JeZ6.js → nextflow-B1xq2O8L.js} +1 -1
- package/dist/web/assets/{nginx-CmqrIj84.js → nginx-xsLxRGxO.js} +1 -1
- package/dist/web/assets/{nim-CqhmTeMa.js → nim-wqcek3WO.js} +1 -1
- package/dist/web/assets/notification-store-II5Kw9rD.js +1 -0
- package/dist/web/assets/{number-overlay-editor-BEzJ1YeD.js → number-overlay-editor-1c4sgZJt.js} +1 -1
- package/dist/web/assets/{overview-panel-Bf9Gn8qV.js → overview-panel-Cr2xsHc6.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-RVlbbDRS.js +1 -0
- package/dist/web/assets/{panel-store-UWCH6B-_.js → panel-store-Dv28vz3L.js} +1 -1
- package/dist/web/assets/{pdf-preview-BMHM5cR2.js → pdf-preview-DKeIIJTv.js} +1 -1
- package/dist/web/assets/{pencil-BlIIg6RH.js → pencil-CHKLOgLs.js} +1 -1
- package/dist/web/assets/{perl-BOwpb6Tb.js → perl-BH54gN9q.js} +1 -1
- package/dist/web/assets/{php-wT9fVxxu.js → php-Br5ZrEZm.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-ZWb4izpk.js +1 -0
- package/dist/web/assets/play-CPbVzGgq.js +1 -0
- package/dist/web/assets/{plus-BR0qewJI.js → plus-BxqD2FIZ.js} +1 -1
- package/dist/web/assets/portal-container-context-BmG4--I-.js +1 -0
- package/dist/web/assets/{postgres-viewer-D8rfEY5b.js → postgres-viewer-BJPr_FYL.js} +3 -3
- package/dist/web/assets/{process-table-CsQ-hDJr.js → process-table-BqTcAJ-h.js} +1 -1
- package/dist/web/assets/{project-store-CKAOr23O.js → project-store-KHjFTCxf.js} +1 -1
- package/dist/web/assets/{pug-DdlNnj-p.js → pug-8VCubZ3H.js} +1 -1
- package/dist/web/assets/{qml-C5zBdydf.js → qml-D8AF1RZ6.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-bsP5x1Bx.js +1 -0
- package/dist/web/assets/{razor-DqcJX4iq.js → razor-6v042IJW.js} +1 -1
- package/dist/web/assets/{react-Dfio3PfK.js → react-DHs6efqI.js} +1 -1
- package/dist/web/assets/{refresh-cw-84mz8aOE.js → refresh-cw-BALzw7L3.js} +1 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-CYZUjYTO.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-view-DaoB49k4.js +1 -0
- package/dist/web/assets/remote-desktop-readiness-gate--LJgsuoj.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-Cihx4PNZ.js +1 -0
- package/dist/web/assets/{rotate-ccw-CNTk2J7l.js → rotate-ccw-CuvNBM80.js} +1 -1
- package/dist/web/assets/{rst-DiMiMYUO.js → rst-mTGFo4AQ.js} +1 -1
- package/dist/web/assets/{ruby-DK62iflA.js → ruby-BBaOP636.js} +1 -1
- package/dist/web/assets/{run-in-terminal-DJSL6nwZ.js → run-in-terminal-Zl5iIx-B.js} +1 -1
- package/dist/web/assets/{sas-6QPGJRa5.js → sas-D5QEkIH2.js} +1 -1
- package/dist/web/assets/{save-CtW9ojKk.js → save-Dxq-Zepc.js} +1 -1
- package/dist/web/assets/{scroll-area-DXGXeiRz.js → scroll-area-B5yhPpZo.js} +1 -1
- package/dist/web/assets/{scss-DmUqh48_.js → scss-C5Mu6y2u.js} +1 -1
- package/dist/web/assets/{send-C_sLFt56.js → send-Cits8q0l.js} +1 -1
- package/dist/web/assets/{send-to-chat-BOony0sO.js → send-to-chat-Cv7ikFUC.js} +1 -1
- package/dist/web/assets/settings-store-Dp4i8zeA.js +2 -0
- package/dist/web/assets/settings-tab-BtMvvKAF.js +3 -0
- package/dist/web/assets/settings-tab-UPPycGRF.js +1 -0
- package/dist/web/assets/{shaderlab-DopFR1mi.js → shaderlab-mLnpMpN3.js} +1 -1
- package/dist/web/assets/{shallow-C1YoJQA-.js → shallow-BBJ_qoEF.js} +1 -1
- package/dist/web/assets/{shellsession-Bp_TGuXb.js → shellsession-qdcDfUuk.js} +1 -1
- package/dist/web/assets/{shield-check-Du6HKYn9.js → shield-check-CH7F1P7m.js} +1 -1
- package/dist/web/assets/{shield-off-CWKb1MQL.js → shield-off-C9HiF0-6.js} +1 -1
- package/dist/web/assets/{soy-DEB9KEFb.js → soy-CGGf1G43.js} +1 -1
- package/dist/web/assets/{sparkles-Dh-_J2Rd.js → sparkles-BnuLH6Ck.js} +1 -1
- package/dist/web/assets/{sparql-C8lEwO1p.js → sparql-tQHB50VD.js} +1 -1
- package/dist/web/assets/{sql-query-editor-MMfk0Kbz.js → sql-query-editor-DZqnYIB7.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-vglzrgnZ.js → sqlite-viewer-DXutos0e.js} +1 -1
- package/dist/web/assets/{stata-DVep4apN.js → stata-DVQNLMcR.js} +1 -1
- package/dist/web/assets/{surrealql-fSlJApOA.js → surrealql-lu576tBi.js} +1 -1
- package/dist/web/assets/{svelte-DezWBEDi.js → svelte-CSCrOBjT.js} +1 -1
- package/dist/web/assets/{switch-DqL_ZjB_.js → switch-BPol3BFC.js} +1 -1
- package/dist/web/assets/system-monitor-body-Ct8_GX_f.js +2 -0
- package/dist/web/assets/system-monitor-tab-DC_EerdI.js +1 -0
- package/dist/web/assets/system-monitor-window-content-D25DMNpw.js +1 -0
- package/dist/web/assets/{tab-host-window-content-DYlXR0cg.js → tab-host-window-content-C5kYP0-z.js} +1 -1
- package/dist/web/assets/{tab-store-D2thn4Vv.js → tab-store-DS3YlUwR.js} +1 -1
- package/dist/web/assets/{table-GAR4upb6.js → table-BXDyImqk.js} +1 -1
- package/dist/web/assets/team-member-sheet-DvmioeQL.js +2 -0
- package/dist/web/assets/{team-member-window-content-qrgFTC96.js → team-member-window-content-BJ_XFw8-.js} +1 -1
- package/dist/web/assets/{templ-D0zB42a9.js → templ-CPt8AS4Q.js} +1 -1
- package/dist/web/assets/{terminal-tab-D_-R5rGm.js → terminal-tab-8bKQ3CWR.js} +3 -3
- package/dist/web/assets/{tex-BJiPijcH.js → tex-BrTByT7i.js} +1 -1
- package/dist/web/assets/{text-wrap-D71Jqz5U.js → text-wrap-CzMjB6GD.js} +1 -1
- package/dist/web/assets/{tool-cards-BW7Xc20J.js → tool-cards-pIu_cmEL.js} +4 -4
- package/dist/web/assets/{trash-2-C08fhIg1.js → trash-2-BjShZ7if.js} +1 -1
- package/dist/web/assets/treemap-KZPCXAKY-Dh1oxNdQ.js +1 -0
- package/dist/web/assets/{triangle-alert-85BQgs3T.js → triangle-alert-qQ6Plydp.js} +1 -1
- package/dist/web/assets/{ts-tags-Dcr4u6y5.js → ts-tags-DSqEZ7A8.js} +1 -1
- package/dist/web/assets/{twig-B-SQU0bg.js → twig-DW9qc1bf.js} +1 -1
- package/dist/web/assets/{upload-BNlX8D07.js → upload-7Zs6oaK5.js} +1 -1
- package/dist/web/assets/{use-blob-url-BtPlqwLr.js → use-blob-url-DKbV7PM_.js} +1 -1
- package/dist/web/assets/{use-coarse-long-press-BmI_GVW5.js → use-coarse-long-press-DGyC48NF.js} +1 -1
- package/dist/web/assets/{use-is-mobile-DUuki97q.js → use-is-mobile-ClE-UcPF.js} +1 -1
- package/dist/web/assets/use-monaco-theme-DaxAk3gB.js +1 -0
- package/dist/web/assets/use-open-team-member-CopUuDHG.js +1 -0
- package/dist/web/assets/use-remote-desktop-display-choice-BUA_1eaH.js +1 -0
- package/dist/web/assets/{use-visual-viewport-pVxLfE5B.js → use-visual-viewport-CCX21c9w.js} +1 -1
- package/dist/web/assets/{use-websocket-l39Odhlp.js → use-websocket-CWpG7PWN.js} +1 -1
- package/dist/web/assets/vendor-markdown-CKTlmXbz.js +291 -0
- package/dist/web/assets/{vendor-mermaid-CtDsSxbM.js → vendor-mermaid-0Nt-ew76.js} +1 -1
- package/dist/web/assets/{vendor-ui-CaeeFO0B.js → vendor-ui-CXJtBULr.js} +1 -1
- package/dist/web/assets/{video-preview-C5AhkIDl.js → video-preview-C2_fdunE.js} +1 -1
- package/dist/web/assets/{vue-Cw10Sfbh.js → vue-CJNDL1g-.js} +1 -1
- package/dist/web/assets/{vue-html-Cc5C_Z_w.js → vue-html-CG5BKtq1.js} +1 -1
- package/dist/web/assets/{vue-vine-BuNMlnvu.js → vue-vine-Dd5HZ7T5.js} +1 -1
- package/dist/web/assets/{window-pip-registry-C7oz3Dfy.js → window-pip-registry-9H6Y-HqR.js} +1 -1
- package/dist/web/assets/{window-store-B5ZswdYg.js → window-store-CNzkeUyO.js} +1 -1
- package/dist/web/assets/x-Dzu9ecti.js +1 -0
- package/dist/web/assets/{xml-DqT1wkky.js → xml-DTOm5niz.js} +1 -1
- package/dist/web/assets/{xsl-DLkge3A2.js → xsl-BD_kBC04.js} +1 -1
- package/dist/web/index.html +65 -64
- package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
- package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
- package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
- package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
- package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
- package/dist/web/sw.js +1 -1
- package/docs/system-architecture.md +19 -0
- package/package.json +1 -1
- package/scripts/check-ppm-dir-usage.sh +0 -0
- package/scripts/install.sh +0 -0
- package/scripts/release.sh +0 -0
- package/src/server/index.ts +1 -1
- package/src/server/routes/remote-desktop.ts +24 -7
- package/src/server/routes/settings.ts +1 -0
- package/src/server/ws/remote-desktop.ts +4 -2
- package/src/services/edge-forwarder.ts +191 -191
- package/src/services/named-tunnel/cloudflare-dns-api.ts +20 -4
- package/src/services/named-tunnel/named-tunnel-args.ts +17 -3
- package/src/services/named-tunnel/named-tunnel-setup.service.ts +20 -2
- package/src/services/remote-desktop/remote-desktop-capture-input.ts +62 -0
- package/src/services/remote-desktop/remote-desktop-capture.ts +29 -12
- package/src/services/remote-desktop/remote-desktop-cg-key-map.ts +45 -0
- package/src/services/remote-desktop/remote-desktop-displays.ts +90 -0
- package/src/services/remote-desktop/remote-desktop-encoder-args.ts +7 -0
- package/src/services/remote-desktop/remote-desktop-flag.ts +8 -6
- package/src/services/remote-desktop/remote-desktop-input-backend.ts +36 -0
- package/src/services/remote-desktop/remote-desktop-input-darwin.ts +200 -0
- package/src/services/remote-desktop/remote-desktop-input-win32.ts +243 -0
- package/src/services/remote-desktop/remote-desktop-input.ts +43 -193
- package/src/services/remote-desktop/remote-desktop-macos-permissions.ts +62 -0
- package/src/services/remote-desktop/remote-desktop-requirements.ts +124 -0
- package/src/services/remote-desktop/remote-desktop-session.ts +16 -5
- package/src/services/windows-process-tree.ts +351 -351
- package/src/web/components/remote-desktop/remote-desktop-mobile-sheet.tsx +12 -4
- package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +12 -1
- package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +5 -1
- package/src/web/components/remote-desktop/remote-desktop-readiness-gate.tsx +128 -0
- package/src/web/components/remote-desktop/remote-desktop-warning-gate.tsx +106 -0
- package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +38 -4
- package/src/web/components/remote-desktop/use-remote-desktop-available.ts +7 -24
- package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +10 -3
- package/src/web/components/remote-desktop/use-remote-desktop-display-choice.ts +30 -0
- package/src/web/components/remote-desktop/use-remote-desktop-readiness.ts +82 -0
- package/src/web/components/remote-desktop/use-remote-desktop-virtual-keyboard.ts +61 -10
- package/src/web/stores/settings-store.ts +12 -0
- package/.rdtest2-cf.out +0 -0
- package/dist/web/assets/ai-settings-section-ERu1WiZc.js +0 -1
- package/dist/web/assets/api-client-WpZ2FDHg.js +0 -1
- package/dist/web/assets/api-settings-BgSEE6et.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-CCalSKAt.js +0 -1
- package/dist/web/assets/chat-tab-BraiJsYG.js +0 -15
- package/dist/web/assets/chevron-right-De-Wz5Lz.js +0 -1
- package/dist/web/assets/circle-BCy0AUCo.js +0 -1
- package/dist/web/assets/clock-C1CQUQPZ.js +0 -1
- package/dist/web/assets/code-DJgYrFzm.js +0 -1
- package/dist/web/assets/code-editor-lhmLNhXu.js +0 -10
- package/dist/web/assets/explorer-body-BxbSttfl.js +0 -2
- package/dist/web/assets/explorer-window-content-DUqQCC7A.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-DAUrJCqi.js +0 -1
- package/dist/web/assets/index-CtJbMNNI.css +0 -2
- package/dist/web/assets/index-H_gScEOk.js +0 -52
- package/dist/web/assets/info-3K5VOQVL-Cx1ZfJbp.js +0 -1
- package/dist/web/assets/keybindings-store-BqctreaI.js +0 -1
- package/dist/web/assets/loader-circle-DouKgIXy.js +0 -1
- package/dist/web/assets/markdown-renderer-BwOkiZC1.js +0 -1
- package/dist/web/assets/markdown-renderer-oJ9jPdQ2.js +0 -3
- package/dist/web/assets/mobile-explorer-sheet-D1nM0Ik1.js +0 -1
- package/dist/web/assets/notification-store-n-XEQ5pH.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-Bqwiy7dz.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-BXSJ2God.js +0 -1
- package/dist/web/assets/play-CJlWpVMw.js +0 -1
- package/dist/web/assets/portal-container-context-DSGdlXrJ.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-D6XrM7HH.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-CTiipn4k.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-view-Cf5B0dKo.js +0 -1
- package/dist/web/assets/remote-desktop-stats-overlay-vhJBIW7s.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-wCJUfI6G.js +0 -1
- package/dist/web/assets/settings-store-BpcY5aYE.js +0 -2
- package/dist/web/assets/settings-tab-BVqRO01j.js +0 -1
- package/dist/web/assets/settings-tab-FH581A_i.js +0 -3
- package/dist/web/assets/system-monitor-body-BYUfyF8f.js +0 -2
- package/dist/web/assets/system-monitor-tab-CKGXB8lH.js +0 -1
- package/dist/web/assets/system-monitor-window-content-CiJ2yIsr.js +0 -1
- package/dist/web/assets/team-member-sheet-Bnygu7Xw.js +0 -2
- package/dist/web/assets/treemap-KZPCXAKY-BobWhcHN.js +0 -1
- package/dist/web/assets/use-monaco-theme-DQY1ZSaf.js +0 -1
- package/dist/web/assets/use-open-team-member-C5LQ_TCM.js +0 -1
- package/dist/web/assets/vendor-markdown-Ls5AcU91.js +0 -291
- package/dist/web/assets/x-CeN0Qybc.js +0 -1
- package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
- /package/dist/web/assets/{abap-CKgBNk5K.js → abap-Cjj2olMr.js} +0 -0
- /package/dist/web/assets/{actionscript-3-oARhAY6c.js → actionscript-3-DCkW7kWC.js} +0 -0
- /package/dist/web/assets/{ada-2vi8ASy3.js → ada-AEWvZXuX.js} +0 -0
- /package/dist/web/assets/{andromeeda-B3vMjPKd.js → andromeeda-D7cSpME6.js} +0 -0
- /package/dist/web/assets/{apache-DnIEr338.js → apache-DjvX0RSe.js} +0 -0
- /package/dist/web/assets/{apex-B7Qs5AY5.js → apex-70xIZ0pH.js} +0 -0
- /package/dist/web/assets/{applescript-EbQNUCWb.js → applescript-C3qKqxmA.js} +0 -0
- /package/dist/web/assets/{apply-theme-Dd_cqGa-.js → apply-theme-DJ794zSQ.js} +0 -0
- /package/dist/web/assets/{ara-CtHdhFRw.js → ara-iGt2zixk.js} +0 -0
- /package/dist/web/assets/{asciidoc-BH6qin_O.js → asciidoc-Bs2t-06W.js} +0 -0
- /package/dist/web/assets/{asm-D1XU1ACk.js → asm-B-T0eqhE.js} +0 -0
- /package/dist/web/assets/{aurora-x-BkhcxkuS.js → aurora-x-D3h_9M3E.js} +0 -0
- /package/dist/web/assets/{awk-DijzB6o7.js → awk-CZwfwpPi.js} +0 -0
- /package/dist/web/assets/{ayu-dark-tysG77WC.js → ayu-dark-DulkglEA.js} +0 -0
- /package/dist/web/assets/{ayu-light-Dg0WMETC.js → ayu-light-DyNQiRpn.js} +0 -0
- /package/dist/web/assets/{ayu-mirage--RsJCjC4.js → ayu-mirage-CcEt4xkI.js} +0 -0
- /package/dist/web/assets/{ballerina-DT_5-l82.js → ballerina-OhQPjyHS.js} +0 -0
- /package/dist/web/assets/{bat-BRpDlkv0.js → bat-DqKcJTUn.js} +0 -0
- /package/dist/web/assets/{beancount--8Df-WGG.js → beancount-BdXlMi8T.js} +0 -0
- /package/dist/web/assets/{berry-Bc9fo35J.js → berry-DvgY6BkY.js} +0 -0
- /package/dist/web/assets/{bibtex-CwyBpp9X.js → bibtex-BSRJq0yM.js} +0 -0
- /package/dist/web/assets/{bicep-DiyH2Bs7.js → bicep-DpAdumKt.js} +0 -0
- /package/dist/web/assets/{bird2-CHgK4a2A.js → bird2-rlbtp8di.js} +0 -0
- /package/dist/web/assets/{c-BM1qL6-f.js → c-AjOLBXIs.js} +0 -0
- /package/dist/web/assets/{c3-D2oOjrP7.js → c3-DqIXz4Ia.js} +0 -0
- /package/dist/web/assets/{cadence-nS45wqF6.js → cadence-DAJtUpOE.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-DwP9KU8o.js → can-open-in-ppm-CdCK2c2a.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-BpnJHiCj.js → catppuccin-frappe-DUPoeZfb.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-Bb95Wj3h.js → catppuccin-latte-CK3U68Ql.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-Btx8c17W.js → catppuccin-macchiato-BzteLnIX.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-B4DZOUGI.js → catppuccin-mocha-ozLd0aBR.js} +0 -0
- /package/dist/web/assets/{clarity-1ps69jU6.js → clarity-6KqEoBfz.js} +0 -0
- /package/dist/web/assets/{clipboard-CrHX8AYw.js → clipboard-B-gwJ8DF.js} +0 -0
- /package/dist/web/assets/{clojure-BklC0hKV.js → clojure-B_uBL5C7.js} +0 -0
- /package/dist/web/assets/{cmake-B573LYAQ.js → cmake-B3nxBNmj.js} +0 -0
- /package/dist/web/assets/{codeowners-BoYeX89M.js → codeowners-DkS-51at.js} +0 -0
- /package/dist/web/assets/{codeql-7Dhf5Udk.js → codeql-rpr1z_mR.js} +0 -0
- /package/dist/web/assets/{common-lisp-m1nxlthv.js → common-lisp-BfNbv_W6.js} +0 -0
- /package/dist/web/assets/{coq-DNhGmd4a.js → coq-EAW-1nw0.js} +0 -0
- /package/dist/web/assets/{csharp-P-nAurtS.js → csharp-BE3Gn5bq.js} +0 -0
- /package/dist/web/assets/{css-LYbSeh3-.js → css-Cr27CfPY.js} +0 -0
- /package/dist/web/assets/{csv-DHZZZm7X.js → csv-B5VtaPtH.js} +0 -0
- /package/dist/web/assets/{csv-parser-CY9Ggtg5.js → csv-parser-B2v8MoUy.js} +0 -0
- /package/dist/web/assets/{cue-D8yLUOle.js → cue-jm6lYNrx.js} +0 -0
- /package/dist/web/assets/{cypher-9cbW8RYA.js → cypher-DXkvK-pn.js} +0 -0
- /package/dist/web/assets/{d-ZihzhxqI.js → d-CgG1_yzd.js} +0 -0
- /package/dist/web/assets/{dark-plus-C8xIA1u3.js → dark-plus-CALzcIVB.js} +0 -0
- /package/dist/web/assets/{dart-CBm6jKc7.js → dart-BEVoLlWg.js} +0 -0
- /package/dist/web/assets/{dax-B3cx6YeV.js → dax-H0BW3Q_P.js} +0 -0
- /package/dist/web/assets/{desktop-C03SpXth.js → desktop-BtYq1l_0.js} +0 -0
- /package/dist/web/assets/{diff-D8LnnvFY.js → diff-CnBYdfTg.js} +0 -0
- /package/dist/web/assets/{dist-C4XoS_zo.js → dist-CJJKpgR7.js} +0 -0
- /package/dist/web/assets/{docker-DwFiOQWW.js → docker-COBitpld.js} +0 -0
- /package/dist/web/assets/{dotenv-CVJ_vFSQ.js → dotenv-CG0x1m7c.js} +0 -0
- /package/dist/web/assets/{dracula-DMw8UACY.js → dracula-CLCi47X-.js} +0 -0
- /package/dist/web/assets/{dracula-soft-BnB3nDrA.js → dracula-soft-BMv5cCAV.js} +0 -0
- /package/dist/web/assets/{dream-maker-2riXUQXo.js → dream-maker-1Bd4OSww.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-BQdYaiB5.js → emacs-lisp-B-2zKLn-.js} +0 -0
- /package/dist/web/assets/{everforest-dark-C2FPIWdj.js → everforest-dark-BGVynCbV.js} +0 -0
- /package/dist/web/assets/{everforest-light-DMOWf9sD.js → everforest-light--rn3R7Cm.js} +0 -0
- /package/dist/web/assets/{fennel-JtSHp0vW.js → fennel-SWg9Us_u.js} +0 -0
- /package/dist/web/assets/{fish-CflDcRfe.js → fish-B04oqgKk.js} +0 -0
- /package/dist/web/assets/{fluent-D4eu6Vac.js → fluent-BefzAGMN.js} +0 -0
- /package/dist/web/assets/{format-bytes-DR3228bu.js → format-bytes-gQw5wzqa.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-hO4RuC9Z.js → fortran-free-form-alw7jMxb.js} +0 -0
- /package/dist/web/assets/{gdscript-D7n3a8VM.js → gdscript-BAyhvjCx.js} +0 -0
- /package/dist/web/assets/{gdshader-BhUmrlzG.js → gdshader-Crl09X-B.js} +0 -0
- /package/dist/web/assets/{genie-VbyftqBp.js → genie-BSIGRHu7.js} +0 -0
- /package/dist/web/assets/{gherkin-BZzhvUUb.js → gherkin-DJhSnSWH.js} +0 -0
- /package/dist/web/assets/{github-dark-default-CMNi7dQX.js → github-dark-default-FVtXjcm8.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-BRf_-LlY.js → github-dark-dimmed-CzrUPgSb.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-xanKBsRI.js → github-dark-high-contrast-1s9rF5EB.js} +0 -0
- /package/dist/web/assets/{github-dark-C4q41woW.js → github-dark-r8C6qwug.js} +0 -0
- /package/dist/web/assets/{github-light-BEQeNQCs.js → github-light-BvIWCkl7.js} +0 -0
- /package/dist/web/assets/{github-light-default-ceQrtdXg.js → github-light-default-CVdhzrxV.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-doKbXzgW.js → github-light-high-contrast-CBAf6hjF.js} +0 -0
- /package/dist/web/assets/{gleam-Chzqf4fT.js → gleam-DvL1q7aa.js} +0 -0
- /package/dist/web/assets/{gn-CCN_-6fp.js → gn-BG5NvwAf.js} +0 -0
- /package/dist/web/assets/{gnuplot-CIplgwwP.js → gnuplot-BARnM-zG.js} +0 -0
- /package/dist/web/assets/{go-Cjj-9dRs.js → go-DXvjjq4q.js} +0 -0
- /package/dist/web/assets/{groovy-PqD4K7X1.js → groovy-CRylSKhr.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-BAbpUSF-.js → gruvbox-dark-hard-BVPjnqm7.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-BWcWXDBB.js → gruvbox-dark-medium-U_PDgq0a.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-Dkx1okkk.js → gruvbox-dark-soft-CkzNtpbr.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-WVsDK0MM.js → gruvbox-light-hard-DfMmR2G8.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-tiR9t7gr.js → gruvbox-light-medium-BGCak-dd.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-HzHohaX6.js → gruvbox-light-soft-DnpM3fkN.js} +0 -0
- /package/dist/web/assets/{haskell-B1eIZfr6.js → haskell-DXgGIDD_.js} +0 -0
- /package/dist/web/assets/{haxe-BcDdi5xr.js → haxe-B7qlTpRz.js} +0 -0
- /package/dist/web/assets/{hcl-Dq-HKVni.js → hcl-ADWfVe_Q.js} +0 -0
- /package/dist/web/assets/{hjson-Dr9D9WVU.js → hjson-BaM9gLbq.js} +0 -0
- /package/dist/web/assets/{hlsl-Bo8t2FuZ.js → hlsl-iqaRA4MG.js} +0 -0
- /package/dist/web/assets/{horizon-DWG5IXc-.js → horizon-BPpTnhB9.js} +0 -0
- /package/dist/web/assets/{horizon-bright-DFr9wTTx.js → horizon-bright-yBLg3QbE.js} +0 -0
- /package/dist/web/assets/{houston-NAVNhkup.js → houston-eFOp0ySL.js} +0 -0
- /package/dist/web/assets/{hy-DnyvUk3B.js → hy-BALeMn3i.js} +0 -0
- /package/dist/web/assets/{image-gallery-DoFdNF9i.js → image-gallery-Bqb-wjNw.js} +0 -0
- /package/dist/web/assets/{imba-CPbR5W_A.js → imba-CdGUm_GD.js} +0 -0
- /package/dist/web/assets/{ini-lc8_Lt0y.js → ini-DG7imsqe.js} +0 -0
- /package/dist/web/assets/{java-B0KCo2vK.js → java-D5GTT474.js} +0 -0
- /package/dist/web/assets/{javascript-YqEbNRpd.js → javascript-V7WwJvRm.js} +0 -0
- /package/dist/web/assets/{json-Co0WtOlh.js → json-B9FhwAgR.js} +0 -0
- /package/dist/web/assets/{json5-2Jhb0QUN.js → json5--Abk7MUy.js} +0 -0
- /package/dist/web/assets/{jsonc-e72F11nU.js → jsonc-C8jb-Z6x.js} +0 -0
- /package/dist/web/assets/{jsonl-Bff8IPye.js → jsonl-CaJNxfkm.js} +0 -0
- /package/dist/web/assets/{jsonnet-DRCkb-_H.js → jsonnet-CPrKMSCU.js} +0 -0
- /package/dist/web/assets/{jssm-C6UiwVEm.js → jssm-DEu-9_uo.js} +0 -0
- /package/dist/web/assets/{jsx-u1aZlnfA.js → jsx-MfAsCWDW.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-De-RQFV3.js → kanagawa-dragon-DnbcAduF.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-K8elE4QU.js → kanagawa-lotus-yxsY3ew8.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-BrStl_zx.js → kanagawa-wave-C2RyvZaM.js} +0 -0
- /package/dist/web/assets/{kdl-CLCfMeZy.js → kdl-CvssmvcU.js} +0 -0
- /package/dist/web/assets/{kotlin-DEEC3pNh.js → kotlin-jufO49mU.js} +0 -0
- /package/dist/web/assets/{kusto-DTqRhCup.js → kusto-BAFnylR7.js} +0 -0
- /package/dist/web/assets/{laserwave-DsgpUSbA.js → laserwave-BgxFC4Ge.js} +0 -0
- /package/dist/web/assets/{lean-Dl9MI0Y3.js → lean-Bgt24V1D.js} +0 -0
- /package/dist/web/assets/{less-Bg21rao6.js → less-Bk_UaqsM.js} +0 -0
- /package/dist/web/assets/{libesm-CVkncAQz.js → libesm-dcbw4_18.js} +0 -0
- /package/dist/web/assets/{light-plus-DGNbbK1V.js → light-plus-nMxFfInN.js} +0 -0
- /package/dist/web/assets/{llvm-91_ZX3p-.js → llvm-3_PASDOO.js} +0 -0
- /package/dist/web/assets/{log-DSXzbq8u.js → log-CKm0CxB1.js} +0 -0
- /package/dist/web/assets/{logo-BQRNd4tc.js → logo-DFP1_5U6.js} +0 -0
- /package/dist/web/assets/{luau-BE-TMpiZ.js → luau-BcZnoyh3.js} +0 -0
- /package/dist/web/assets/{make-ePYGKTpc.js → make-CpNSwgwT.js} +0 -0
- /package/dist/web/assets/{markdown-Cst3wgMW.js → markdown-DhU3Djpv.js} +0 -0
- /package/dist/web/assets/{material-theme-9XyvnNLb.js → material-theme-DD5L3OqF.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-H1VN4MTS.js → material-theme-darker-CG2_tD3R.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-DAreBbuF.js → material-theme-lighter-CzNkUc9Y.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-BpTR-3II.js → material-theme-ocean-BtLiAaF_.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-DUNzX711.js → material-theme-palenight-Brvjz035.js} +0 -0
- /package/dist/web/assets/{matlab-BpYNufBU.js → matlab-CCY0KtMD.js} +0 -0
- /package/dist/web/assets/{mdx-BYjfe1DK.js → mdx-CH6YY0aS.js} +0 -0
- /package/dist/web/assets/{mermaid-DPCcIW9B.js → mermaid-Bqh3gFI0.js} +0 -0
- /package/dist/web/assets/{min-dark-DnGOAGCo.js → min-dark-BUwfTzI6.js} +0 -0
- /package/dist/web/assets/{min-light-CwV-o31y.js → min-light-oLanXCOk.js} +0 -0
- /package/dist/web/assets/{mipsasm-DdeCPC97.js → mipsasm-CTh3hPWK.js} +0 -0
- /package/dist/web/assets/{mojo-BmxxZ3iM.js → mojo-DXdszZKa.js} +0 -0
- /package/dist/web/assets/{monokai-Cx6cQ-MA.js → monokai-TNE_tRxc.js} +0 -0
- /package/dist/web/assets/{moonbit-D2-Gj5vj.js → moonbit-jrk-Gx1X.js} +0 -0
- /package/dist/web/assets/{move-DSWGoQa8.js → move-DItn61bH.js} +0 -0
- /package/dist/web/assets/{narrat-D9TnZSSu.js → narrat-3kB4dkh0.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-BbL-8PwD.js → nextflow-groovy-CSKtwETr.js} +0 -0
- /package/dist/web/assets/{night-owl-CZitKm5d.js → night-owl-Dm91nSIq.js} +0 -0
- /package/dist/web/assets/{night-owl-light-DJVX1Pno.js → night-owl-light-riwrcPRg.js} +0 -0
- /package/dist/web/assets/{nix-R6hr3oln.js → nix-sLgl9zGF.js} +0 -0
- /package/dist/web/assets/{nord-ChGHya3l.js → nord-TWkJPI6M.js} +0 -0
- /package/dist/web/assets/{nushell-QQWB-GWx.js → nushell-CDZi4-Gf.js} +0 -0
- /package/dist/web/assets/{objective-c-C0pZ6y73.js → objective-c-DW9PboNp.js} +0 -0
- /package/dist/web/assets/{objective-cpp-BdzVM31a.js → objective-cpp-2XCCX6q1.js} +0 -0
- /package/dist/web/assets/{ocaml-D4KktNlS.js → ocaml-CxPdnjrh.js} +0 -0
- /package/dist/web/assets/{odin-Y-fypmGT.js → odin-BJjlKpkM.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-BwecIxQL.js → one-dark-pro-K1KOmBZ7.js} +0 -0
- /package/dist/web/assets/{one-light-Cz7UVzLU.js → one-light-DQB-vUyZ.js} +0 -0
- /package/dist/web/assets/{openscad-DtRe3qYD.js → openscad-DCoLwn4j.js} +0 -0
- /package/dist/web/assets/{pascal-Ck6I1fGG.js → pascal-z2ktbbWp.js} +0 -0
- /package/dist/web/assets/{pkl-DdFK94VW.js → pkl-Dyq4-v6z.js} +0 -0
- /package/dist/web/assets/{plastic-BQSAzZwR.js → plastic-C4Rq5PnD.js} +0 -0
- /package/dist/web/assets/{plsql-DjXg_CP0.js → plsql-BCIjO63y.js} +0 -0
- /package/dist/web/assets/{po-Bw2SuM4r.js → po-_kXpd_0n.js} +0 -0
- /package/dist/web/assets/{poimandres-YMK6c5VN.js → poimandres-C9XggjB3.js} +0 -0
- /package/dist/web/assets/{polar-BMu17-O0.js → polar-DV_1L04z.js} +0 -0
- /package/dist/web/assets/{postcss-DWib10RZ.js → postcss-BuqS91mQ.js} +0 -0
- /package/dist/web/assets/{powerquery-Dht4CK0b.js → powerquery-DK13B2Dc.js} +0 -0
- /package/dist/web/assets/{powershell-DvrCg8ji.js → powershell-tkuhhtuP.js} +0 -0
- /package/dist/web/assets/{prisma-DbodYvCM.js → prisma-WNXfBtpr.js} +0 -0
- /package/dist/web/assets/{prolog-DZ-m6ysB.js → prolog-BzEdNKfO.js} +0 -0
- /package/dist/web/assets/{proto-HHpG3tGO.js → proto-B5meVsrA.js} +0 -0
- /package/dist/web/assets/{puppet-BljHe_75.js → puppet-C7h_p4r2.js} +0 -0
- /package/dist/web/assets/{purescript-DzCiFR0B.js → purescript-qH1KxAs6.js} +0 -0
- /package/dist/web/assets/{python-B5BQ27cC.js → python-DeTIYd5V.js} +0 -0
- /package/dist/web/assets/{qmldir-CYlcXrnn.js → qmldir-4Rm5AvD3.js} +0 -0
- /package/dist/web/assets/{qss-Ce7T_TEg.js → qss-YRbeNtr0.js} +0 -0
- /package/dist/web/assets/{r-Bku-2Jp_.js → r-DHwHK0Jh.js} +0 -0
- /package/dist/web/assets/{racket-DtfSL3WR.js → racket-gKn4SFLQ.js} +0 -0
- /package/dist/web/assets/{raku-CnDLsBJr.js → raku-DWTmAj3A.js} +0 -0
- /package/dist/web/assets/{red-vbqcE9b-.js → red-BFuHRZFS.js} +0 -0
- /package/dist/web/assets/{reg-DIDLT8-A.js → reg-DYoOF5tH.js} +0 -0
- /package/dist/web/assets/{regexp-CB4h-VPu.js → regexp-6rXQHD2-.js} +0 -0
- /package/dist/web/assets/{rel-CiQEnWXa.js → rel-Z_tTruCU.js} +0 -0
- /package/dist/web/assets/{resolve-theme-DPTQHAy1.js → resolve-theme-4fexHWl6.js} +0 -0
- /package/dist/web/assets/{riscv-R78iq5Rf.js → riscv-DfTfKuvU.js} +0 -0
- /package/dist/web/assets/{ron-DhYENxc4.js → ron-CsUtW8gQ.js} +0 -0
- /package/dist/web/assets/{rose-pine-5wBeyjfJ.js → rose-pine-Dng_vCo6.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-FpKtPCuH.js → rose-pine-dawn-Ds88Nezf.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-BwcIYc7C.js → rose-pine-moon-DWpzrqNz.js} +0 -0
- /package/dist/web/assets/{rosmsg-DWIGAYMl.js → rosmsg-h2sO84oD.js} +0 -0
- /package/dist/web/assets/{rust-DU_ofKgt.js → rust-Dx86yfCi.js} +0 -0
- /package/dist/web/assets/{sass-U9SUuYEy.js → sass-BwwVhdT9.js} +0 -0
- /package/dist/web/assets/{scala-op4gjA6j.js → scala-WJqNEN-w.js} +0 -0
- /package/dist/web/assets/{scheme-tEjkR8rL.js → scheme-uwj-t1Rp.js} +0 -0
- /package/dist/web/assets/{sdbl-C6SSRmZg.js → sdbl-BYy5hgEz.js} +0 -0
- /package/dist/web/assets/{shellscript-Bf2KiwQj.js → shellscript-DWTW_w2K.js} +0 -0
- /package/dist/web/assets/{slack-dark-oGgCey8r.js → slack-dark-BvhGq5vQ.js} +0 -0
- /package/dist/web/assets/{slack-ochin-CemlB8cE.js → slack-ochin-C9g0ZRFr.js} +0 -0
- /package/dist/web/assets/{smalltalk-CTE-6jrZ.js → smalltalk-ZEiZ6i7U.js} +0 -0
- /package/dist/web/assets/{snazzy-light-DoEIGnmz.js → snazzy-light-29OA24q5.js} +0 -0
- /package/dist/web/assets/{solarized-dark-CStiJyBg.js → solarized-dark-Bnq9IOn_.js} +0 -0
- /package/dist/web/assets/{solarized-light-CfuNycS9.js → solarized-light-CYdI7T5g.js} +0 -0
- /package/dist/web/assets/{solidity-DL6sO5rd.js → solidity-DcuIDB-_.js} +0 -0
- /package/dist/web/assets/{splunk-yK4ZUFBB.js → splunk-D5ONE91g.js} +0 -0
- /package/dist/web/assets/{sql-Lw03eoN8.js → sql-DIwJJDU-.js} +0 -0
- /package/dist/web/assets/{ssh-config-DeRp25TR.js → ssh-config-D8717tLI.js} +0 -0
- /package/dist/web/assets/{stylus-D8OMsPh-.js → stylus-Cm1lO4Y6.js} +0 -0
- /package/dist/web/assets/{swift-nsl9stPV.js → swift-X8oWpSeU.js} +0 -0
- /package/dist/web/assets/{synthwave-84-exAgsgpf.js → synthwave-84-DF-t1UKY.js} +0 -0
- /package/dist/web/assets/{system-verilog-DBWf00T6.js → system-verilog-JYyHFpPf.js} +0 -0
- /package/dist/web/assets/{systemd-nhu27Dw5.js → systemd-Da-5Ma_c.js} +0 -0
- /package/dist/web/assets/{talonscript-CNOEcGDc.js → talonscript-CXraVPho.js} +0 -0
- /package/dist/web/assets/{tasl-CjYZenSH.js → tasl-BCmWezH9.js} +0 -0
- /package/dist/web/assets/{tcl-BVA09RDz.js → tcl-BTufzfbf.js} +0 -0
- /package/dist/web/assets/{terraform-C5EloH1q.js → terraform-CtUA9c8W.js} +0 -0
- /package/dist/web/assets/{tokyo-night-D764DdhO.js → tokyo-night-BCThYXmz.js} +0 -0
- /package/dist/web/assets/{toml-CCDYblSB.js → toml-D5YlDr6Y.js} +0 -0
- /package/dist/web/assets/{tsv-BZ4sA05e.js → tsv-CBB-P_Sl.js} +0 -0
- /package/dist/web/assets/{tsx-DPLBzAt5.js → tsx-D5B4juDp.js} +0 -0
- /package/dist/web/assets/{turtle-CwGHCq40.js → turtle-D8PCeJ-v.js} +0 -0
- /package/dist/web/assets/{typescript-CUH0vBAl.js → typescript-CMPnTawp.js} +0 -0
- /package/dist/web/assets/{typespec-Bq31Vq4u.js → typespec-CHxldxIW.js} +0 -0
- /package/dist/web/assets/{typst-MD9BR9W5.js → typst-Q8CqX5OR.js} +0 -0
- /package/dist/web/assets/{utils-u2fbYPrY.js → utils-QHe_Q6UF.js} +0 -0
- /package/dist/web/assets/{v-DZV7IxIT.js → v-Bf2hKAMu.js} +0 -0
- /package/dist/web/assets/{vala-urd1zi9S.js → vala-Cbete-UE.js} +0 -0
- /package/dist/web/assets/{vb-DIZU3zbJ.js → vb-CA-xWAcX.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-DCj0zgSC.js → vendor-xterm-BHKF_Nsf.js} +0 -0
- /package/dist/web/assets/{verilog-Ndz_ciTA.js → verilog-B3wb8CfT.js} +0 -0
- /package/dist/web/assets/{vesper-C86M7LJp.js → vesper-D6r3VJq_.js} +0 -0
- /package/dist/web/assets/{vhdl-_klwDUjd.js → vhdl-C42S_797.js} +0 -0
- /package/dist/web/assets/{viml-DzRXWoQh.js → viml-BePrIAs8.js} +0 -0
- /package/dist/web/assets/{vitesse-black-TWZ20_3U.js → vitesse-black-DRqn61Ck.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-Dl7R6fy9.js → vitesse-dark-39_8UE-w.js} +0 -0
- /package/dist/web/assets/{vitesse-light-Cs0Dt2_n.js → vitesse-light-B1jwrxDm.js} +0 -0
- /package/dist/web/assets/{vyper-qnCVj8ia.js → vyper-tDJv3imF.js} +0 -0
- /package/dist/web/assets/{wasm-Ckx9jBPe.js → wasm-ChtccNfv.js} +0 -0
- /package/dist/web/assets/{wasm-Dy-SJ3W9.js → wasm-DR7eskZH.js} +0 -0
- /package/dist/web/assets/{wenyan-DTEy2Lx-.js → wenyan-CezVfqN3.js} +0 -0
- /package/dist/web/assets/{wgsl-DC7n63Sy.js → wgsl-CwOcIiIG.js} +0 -0
- /package/dist/web/assets/{wikitext-1zFoZiW6.js → wikitext-C5coomYc.js} +0 -0
- /package/dist/web/assets/{wit-DtTfCRph.js → wit-C9yyX6-U.js} +0 -0
- /package/dist/web/assets/{wolfram-B2Bgu1KL.js → wolfram-BWi0rMAa.js} +0 -0
- /package/dist/web/assets/{yaml-B-Jxk0IQ.js → yaml-CGXhKUtT.js} +0 -0
- /package/dist/web/assets/{zenscript-BWRfvNER.js → zenscript-Do7WLoJh.js} +0 -0
- /package/dist/web/assets/{zig-CkwuvETm.js → zig-Cr80Qrru.js} +0 -0
|
@@ -1,217 +1,67 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* plan flagged for a bundled injector; phase 02's SYSTEM-session helper is a different
|
|
5
|
-
* process/token anyway, so nothing here is meant to be reused as-is).
|
|
2
|
+
* Platform-neutral entry point for input injection. Picks the backend for `process.platform`
|
|
3
|
+
* from the registry below; the session and routes never import a platform module directly.
|
|
6
4
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `
|
|
10
|
-
* merely importing this module (routing, types) on Linux/macOS test runners never touches
|
|
11
|
-
* `user32.dll`.
|
|
5
|
+
* Every backend module is safe to *import* anywhere (FFI is loaded lazily inside each), so the
|
|
6
|
+
* registry can hold them all statically — only the selected one ever `dlopen`s. Adding Linux =
|
|
7
|
+
* `remote-desktop-input-linux.ts` + one line in `BACKENDS`.
|
|
12
8
|
*/
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
constructor(msg = "Remote input injection is only available on Windows") {
|
|
17
|
-
super(msg);
|
|
18
|
-
this.name = "RemoteInputUnavailableError";
|
|
19
|
-
}
|
|
20
|
-
}
|
|
9
|
+
import { win32InputBackend } from "./remote-desktop-input-win32.ts";
|
|
10
|
+
import { darwinInputBackend } from "./remote-desktop-input-darwin.ts";
|
|
11
|
+
import { RemoteInputUnavailableError, type InputTargetRect, type RemoteInputBackend } from "./remote-desktop-input-backend.ts";
|
|
21
12
|
|
|
22
|
-
|
|
23
|
-
const INPUT_KEYBOARD = 1;
|
|
24
|
-
const MOUSEEVENTF_MOVE = 0x0001;
|
|
25
|
-
const MOUSEEVENTF_LEFTDOWN = 0x0002;
|
|
26
|
-
const MOUSEEVENTF_LEFTUP = 0x0004;
|
|
27
|
-
const MOUSEEVENTF_RIGHTDOWN = 0x0008;
|
|
28
|
-
const MOUSEEVENTF_RIGHTUP = 0x0010;
|
|
29
|
-
const MOUSEEVENTF_ABSOLUTE = 0x8000;
|
|
30
|
-
const MOUSEEVENTF_VIRTUALDESK = 0x4000;
|
|
31
|
-
const MOUSEEVENTF_WHEEL = 0x0800;
|
|
32
|
-
/** One notch of a real mouse wheel — `mouseData` for `MOUSEEVENTF_WHEEL` is a signed multiple
|
|
33
|
-
* of this constant, not a raw pixel delta. */
|
|
34
|
-
const WHEEL_DELTA = 120;
|
|
35
|
-
const KEYEVENTF_KEYUP = 0x0002;
|
|
36
|
-
/** `((DPI_AWARENESS_CONTEXT)-4)` — PER_MONITOR_AWARE_V2. Not a real pointer; the x64 ABI
|
|
37
|
-
* passes it in the same register/slot as a pointer arg would, bit pattern only. */
|
|
38
|
-
const DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4n;
|
|
39
|
-
/** Each `INPUT` struct is 40 bytes on x64 (8-byte `type` slot + 32-byte union). */
|
|
40
|
-
const INPUT_STRUCT_SIZE = 40;
|
|
41
|
-
/** Desktop access rights for input injection. `SendInput` silently no-ops when the calling
|
|
42
|
-
* thread is not attached to the *input* desktop; attaching needs the READ/WRITE/SWITCH rights
|
|
43
|
-
* plus JOURNALPLAYBACK so the desktop accepts injected input. These MUST be the real DESKTOP_*
|
|
44
|
-
* bit values — an invalid mask (e.g. an out-of-range bit) makes `OpenInputDesktop` return NULL
|
|
45
|
-
* and the attach silently fails, leaving SendInput pointed at the wrong desktop. */
|
|
46
|
-
const DESKTOP_READOBJECTS = 0x0001;
|
|
47
|
-
const DESKTOP_JOURNALPLAYBACK = 0x0020;
|
|
48
|
-
const DESKTOP_WRITEOBJECTS = 0x0080;
|
|
49
|
-
const DESKTOP_SWITCHDESKTOP = 0x0100;
|
|
50
|
-
const INPUT_DESKTOP_ACCESS =
|
|
51
|
-
DESKTOP_READOBJECTS | DESKTOP_JOURNALPLAYBACK | DESKTOP_WRITEOBJECTS | DESKTOP_SWITCHDESKTOP; // 0x1A1
|
|
13
|
+
export { RemoteInputUnavailableError, type InputTargetRect, type RemoteInputBackend };
|
|
52
14
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
type FfiModule = typeof import("bun:ffi");
|
|
58
|
-
type User32Symbols = {
|
|
59
|
-
SendInput: (count: number, ptr: number | bigint, size: number) => number;
|
|
60
|
-
SetProcessDpiAwarenessContext: (ctx: bigint) => boolean;
|
|
61
|
-
OpenInputDesktop: (flags: number, inherit: boolean, access: number) => number | bigint;
|
|
62
|
-
SetThreadDesktop: (hdesk: number | bigint) => boolean;
|
|
63
|
-
CloseDesktop: (hdesk: number | bigint) => boolean;
|
|
64
|
-
OpenWindowStationW: (name: number | bigint, inherit: boolean, access: number) => number | bigint;
|
|
65
|
-
SetProcessWindowStation: (hwinsta: number | bigint) => boolean;
|
|
15
|
+
const BACKENDS: Partial<Record<NodeJS.Platform, RemoteInputBackend>> = {
|
|
16
|
+
win32: win32InputBackend,
|
|
17
|
+
darwin: darwinInputBackend,
|
|
66
18
|
};
|
|
67
19
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
async function loadUser32(): Promise<{ ffi: FfiModule; lib: User32Symbols }> {
|
|
73
|
-
if (process.platform !== "win32") throw new RemoteInputUnavailableError();
|
|
74
|
-
if (!ffiModule) ffiModule = await import("bun:ffi");
|
|
75
|
-
const { dlopen, FFIType } = ffiModule;
|
|
76
|
-
if (!user32) {
|
|
77
|
-
user32 = dlopen("user32.dll", {
|
|
78
|
-
SendInput: { args: [FFIType.u32, FFIType.ptr, FFIType.i32], returns: FFIType.u32 },
|
|
79
|
-
SetProcessDpiAwarenessContext: { args: [FFIType.i64], returns: FFIType.bool },
|
|
80
|
-
OpenInputDesktop: { args: [FFIType.u32, FFIType.bool, FFIType.u32], returns: FFIType.ptr },
|
|
81
|
-
SetThreadDesktop: { args: [FFIType.ptr], returns: FFIType.bool },
|
|
82
|
-
CloseDesktop: { args: [FFIType.ptr], returns: FFIType.bool },
|
|
83
|
-
OpenWindowStationW: { args: [FFIType.ptr, FFIType.bool, FFIType.u32], returns: FFIType.ptr },
|
|
84
|
-
SetProcessWindowStation: { args: [FFIType.ptr], returns: FFIType.bool },
|
|
85
|
-
}).symbols as unknown as User32Symbols;
|
|
86
|
-
}
|
|
87
|
-
if (!dpiAwarenessAttempted) {
|
|
88
|
-
dpiAwarenessAttempted = true;
|
|
89
|
-
// A second call to this in the same process returns E_ACCESSDENIED — swallow it, it just
|
|
90
|
-
// means something else (or a previous session) already set it.
|
|
91
|
-
try { user32.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } catch { /* already set */ }
|
|
92
|
-
}
|
|
93
|
-
return { ffi: ffiModule, lib: user32 };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function writeMouseInput(dv: DataView, offset: number, xAbs: number, yAbs: number, flags: number, mouseData = 0): void {
|
|
97
|
-
dv.setUint32(offset, INPUT_MOUSE, true);
|
|
98
|
-
dv.setInt32(offset + 8, xAbs, true);
|
|
99
|
-
dv.setInt32(offset + 12, yAbs, true);
|
|
100
|
-
dv.setInt32(offset + 16, mouseData, true); // wheel delta for MOUSEEVENTF_WHEEL, unused otherwise
|
|
101
|
-
dv.setUint32(offset + 20, flags >>> 0, true);
|
|
102
|
-
dv.setUint32(offset + 24, 0, true); // time: 0 lets the system supply it
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function writeKeyInput(dv: DataView, offset: number, vk: number, keyUp: boolean): void {
|
|
106
|
-
dv.setUint32(offset, INPUT_KEYBOARD, true);
|
|
107
|
-
dv.setUint16(offset + 8, vk, true);
|
|
108
|
-
dv.setUint16(offset + 10, 0, true); // wScan — unused, we inject by VK not scan code
|
|
109
|
-
dv.setUint32(offset + 12, keyUp ? KEYEVENTF_KEYUP : 0, true);
|
|
110
|
-
dv.setUint32(offset + 16, 0, true); // time
|
|
20
|
+
/** The backend for this host, or null when the platform has none. */
|
|
21
|
+
export function getInputBackend(platform: NodeJS.Platform = process.platform): RemoteInputBackend | null {
|
|
22
|
+
return BACKENDS[platform] ?? null;
|
|
111
23
|
}
|
|
112
24
|
|
|
113
|
-
/**
|
|
114
|
-
*
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
* because the input desktop changes on lock/unlock; `CreateProcessAsUser`/process start only
|
|
119
|
-
* fixes the initial desktop, not later switches. Best-effort: on failure we still try SendInput
|
|
120
|
-
* (it just no-ops), and log once rather than per-event spam. */
|
|
121
|
-
/** Attach the PROCESS to the interactive window station ("winsta0") once, so its input desktop
|
|
122
|
-
* is reachable. Without this, a PPM process launched from a non-interactive station (a service,
|
|
123
|
-
* or some spawners) can't OpenInputDesktop at all — SendInput then goes nowhere. No-op when the
|
|
124
|
-
* process is already on winsta0 (the normal case for PPM started inside the user's session). */
|
|
125
|
-
let stationEnsured = false;
|
|
126
|
-
function ensureInteractiveStation(ffi: FfiModule, lib: User32Symbols): void {
|
|
127
|
-
if (stationEnsured) return;
|
|
128
|
-
stationEnsured = true;
|
|
129
|
-
const name = "winsta0";
|
|
130
|
-
const wide = new Uint16Array(name.length + 1);
|
|
131
|
-
for (let i = 0; i < name.length; i++) wide[i] = name.charCodeAt(i);
|
|
132
|
-
const hwinsta = lib.OpenWindowStationW(ffi.ptr(wide), false, WINSTA_ALL_ACCESS);
|
|
133
|
-
if (!hwinsta) return; // already on it, or no access — OpenInputDesktop below will tell us
|
|
134
|
-
lib.SetProcessWindowStation(hwinsta);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function attachToInputDesktop(ffi: FfiModule, lib: User32Symbols): void {
|
|
138
|
-
ensureInteractiveStation(ffi, lib);
|
|
139
|
-
const hdesk = lib.OpenInputDesktop(0, false, INPUT_DESKTOP_ACCESS);
|
|
140
|
-
if (!hdesk) return; // couldn't open the input desktop (e.g. secure/lock desktop); leave thread as-is
|
|
141
|
-
const ok = lib.SetThreadDesktop(hdesk);
|
|
142
|
-
if (ok) {
|
|
143
|
-
if (attachedDesktop && attachedDesktop !== hdesk) {
|
|
144
|
-
try { lib.CloseDesktop(attachedDesktop); } catch { /* ignore */ }
|
|
145
|
-
}
|
|
146
|
-
attachedDesktop = hdesk;
|
|
147
|
-
} else {
|
|
148
|
-
// SetThreadDesktop fails if this thread owns windows/hooks — shouldn't for the server thread.
|
|
149
|
-
try { lib.CloseDesktop(hdesk); } catch { /* ignore */ }
|
|
150
|
-
}
|
|
25
|
+
/** Sync, cheap: does this platform have an injector at all? Used on the per-event hot path to
|
|
26
|
+
* drop input messages early. "Would it actually work" (OS permissions) is answered by
|
|
27
|
+
* `remote-desktop-requirements.ts`. */
|
|
28
|
+
export function isInputAvailable(): boolean {
|
|
29
|
+
return getInputBackend() !== null;
|
|
151
30
|
}
|
|
152
31
|
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (sent !== count) console.warn(`[remote-desktop] SendInput accepted ${sent}/${count} events`);
|
|
32
|
+
function required(): RemoteInputBackend {
|
|
33
|
+
const backend = getInputBackend();
|
|
34
|
+
if (!backend) throw new RemoteInputUnavailableError();
|
|
35
|
+
return backend;
|
|
158
36
|
}
|
|
159
37
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
* are 0..1 fractions of the capture (client already dropped devicePixelRatio; only the
|
|
163
|
-
* canvas-relative fraction is meaningful for host coordinates). */
|
|
164
|
-
export async function injectPointer(
|
|
165
|
-
xFrac: number,
|
|
166
|
-
yFrac: number,
|
|
167
|
-
button: "left" | "right" | null,
|
|
168
|
-
down: boolean | null,
|
|
38
|
+
export function injectPointer(
|
|
39
|
+
xFrac: number, yFrac: number, button: "left" | "right" | null, down: boolean | null, target: InputTargetRect | null = null,
|
|
169
40
|
): Promise<void> {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
let flags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK;
|
|
173
|
-
if (button === "left") flags |= down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
|
|
174
|
-
else if (button === "right") flags |= down ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
|
|
41
|
+
return required().pointer(xFrac, yFrac, button, down, target);
|
|
42
|
+
}
|
|
175
43
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
44
|
+
export function injectWheel(deltaY: number): Promise<void> {
|
|
45
|
+
// A zero delta is a no-op on every OS — settle it here so no backend is even consulted.
|
|
46
|
+
if (Math.round(deltaY) === 0) return Promise.resolve();
|
|
47
|
+
return required().wheel(deltaY);
|
|
179
48
|
}
|
|
180
49
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
* client accumulates raw two-finger-drag pixels into this unit before sending, so this layer
|
|
184
|
-
* stays a dumb relay: round + clamp to a safe i16 range so a runaway client value can't wrap
|
|
185
|
-
* into a huge or negative-looking mouseData field. */
|
|
186
|
-
export async function injectWheel(deltaY: number): Promise<void> {
|
|
187
|
-
const clamped = Math.max(-32768, Math.min(32767, Math.round(deltaY)));
|
|
188
|
-
if (clamped === 0) return;
|
|
189
|
-
const buf = new Uint8Array(INPUT_STRUCT_SIZE);
|
|
190
|
-
writeMouseInput(new DataView(buf.buffer), 0, 0, 0, MOUSEEVENTF_WHEEL, clamped);
|
|
191
|
-
await sendRaw(buf, 1);
|
|
50
|
+
export function injectKey(code: string, down: boolean): Promise<boolean> {
|
|
51
|
+
return required().key(code, down);
|
|
192
52
|
}
|
|
193
53
|
|
|
194
|
-
/**
|
|
195
|
-
*
|
|
196
|
-
export async function
|
|
197
|
-
const
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
writeKeyInput(new DataView(buf.buffer), 0, vk, !down);
|
|
201
|
-
await sendRaw(buf, 1);
|
|
54
|
+
/** Type text as-is. Resolves false when this platform's backend has no text path (the caller
|
|
55
|
+
* may fall back to per-key events or tell the user). */
|
|
56
|
+
export async function injectText(text: string): Promise<boolean> {
|
|
57
|
+
const backend = required();
|
|
58
|
+
if (!backend.text) return false;
|
|
59
|
+
await backend.text(text);
|
|
202
60
|
return true;
|
|
203
61
|
}
|
|
204
62
|
|
|
205
|
-
/** Force keyup for every modifier VK, regardless of tracked state — the backstop for a lost
|
|
206
|
-
* keyup (blur, WS drop) leaving Shift/Ctrl/Alt/Win logically held on the host. */
|
|
207
63
|
export async function releaseAllModifiers(): Promise<void> {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
MODIFIER_VK_CODES.forEach((vk, i) => writeKeyInput(dv, i * INPUT_STRUCT_SIZE, vk, true));
|
|
212
|
-
try { await sendRaw(buf, MODIFIER_VK_CODES.length); } catch { /* best-effort */ }
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export function isInputAvailable(): boolean {
|
|
216
|
-
return process.platform === "win32";
|
|
64
|
+
const backend = getInputBackend();
|
|
65
|
+
if (!backend) return;
|
|
66
|
+
await backend.releaseAllModifiers();
|
|
217
67
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* macOS TCC pre-flight for the two grants remote desktop needs, via Bun FFI. Both checks are
|
|
3
|
+
* cheap and side-effect free; the `request*` calls make macOS show its own prompt (once per
|
|
4
|
+
* grant — a denied prompt never re-appears, the user then has to use System Settings).
|
|
5
|
+
*
|
|
6
|
+
* Why pre-flight instead of reacting to failures: without Screen Recording, avfoundation still
|
|
7
|
+
* runs and hands ffmpeg **black frames**; without Accessibility, `CGEventPost` is a silent
|
|
8
|
+
* no-op. Neither path produces an error the session could surface.
|
|
9
|
+
*
|
|
10
|
+
* TCC keys command-line tools by executable — the grant lands on `bun` (the PPM daemon's
|
|
11
|
+
* binary), not on a terminal or on PPM. Frameworks are `dlopen`-ed lazily and only on darwin.
|
|
12
|
+
*/
|
|
13
|
+
const CG = "/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics";
|
|
14
|
+
const APP_SERVICES = "/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices";
|
|
15
|
+
|
|
16
|
+
type Symbols = {
|
|
17
|
+
CGPreflightScreenCaptureAccess: () => boolean;
|
|
18
|
+
CGRequestScreenCaptureAccess: () => boolean;
|
|
19
|
+
AXIsProcessTrusted: () => boolean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
let loaded: { ffi: typeof import("bun:ffi"); lib: Symbols } | null = null;
|
|
23
|
+
|
|
24
|
+
async function load() {
|
|
25
|
+
if (loaded) return loaded;
|
|
26
|
+
const ffi = await import("bun:ffi");
|
|
27
|
+
const { dlopen, FFIType: T } = ffi;
|
|
28
|
+
const cg = dlopen(CG, {
|
|
29
|
+
CGPreflightScreenCaptureAccess: { args: [], returns: T.bool },
|
|
30
|
+
CGRequestScreenCaptureAccess: { args: [], returns: T.bool },
|
|
31
|
+
}).symbols;
|
|
32
|
+
const ax = dlopen(APP_SERVICES, { AXIsProcessTrusted: { args: [], returns: T.bool } }).symbols;
|
|
33
|
+
loaded = { ffi, lib: { ...cg, ...ax } as unknown as Symbols };
|
|
34
|
+
return loaded;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type MacPermissionId = "screen-recording" | "accessibility";
|
|
38
|
+
|
|
39
|
+
/** System Settings deep links (macOS 13+ pane ids). */
|
|
40
|
+
export const MAC_PERMISSION_SETTINGS_URL: Record<MacPermissionId, string> = {
|
|
41
|
+
"screen-recording": "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture",
|
|
42
|
+
accessibility: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** Current grant state. Off darwin both are reported as granted so callers need no OS branch. */
|
|
46
|
+
export async function macPermissionStatus(): Promise<Record<MacPermissionId, boolean>> {
|
|
47
|
+
if (process.platform !== "darwin") return { "screen-recording": true, accessibility: true };
|
|
48
|
+
const { lib } = await load();
|
|
49
|
+
return { "screen-recording": lib.CGPreflightScreenCaptureAccess(), accessibility: lib.AXIsProcessTrusted() };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Ask macOS to show the grant prompt. Returns the state *after* the call — for Accessibility
|
|
53
|
+
* the prompt is asynchronous, so the caller should keep polling `macPermissionStatus()`. */
|
|
54
|
+
export async function requestMacPermission(id: MacPermissionId): Promise<boolean> {
|
|
55
|
+
if (process.platform !== "darwin") return true;
|
|
56
|
+
const { lib } = await load();
|
|
57
|
+
if (id === "screen-recording") return lib.CGRequestScreenCaptureAccess();
|
|
58
|
+
// Accessibility has no prompt API without a CFDictionary (`AXIsProcessTrustedWithOptions` +
|
|
59
|
+
// the `kAXTrustedCheckOptionPrompt` data symbol, which bun:ffi cannot dlsym). The panel
|
|
60
|
+
// deep-links to the Settings pane instead, where the user adds the process by hand.
|
|
61
|
+
return lib.AXIsProcessTrusted();
|
|
62
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What this host still needs before remote desktop works, as a flat checklist the UI renders
|
|
3
|
+
* generically. Each platform contributes its own items; the client never branches on OS — it
|
|
4
|
+
* only knows `gates` (which half of the feature an item blocks) and the `actions` it can offer.
|
|
5
|
+
*
|
|
6
|
+
* Adding a requirement = push one more item here (and, for `host` actions, handle its id in
|
|
7
|
+
* `runHostAction`). The UI needs no change.
|
|
8
|
+
*/
|
|
9
|
+
import { getFfmpegCapabilities } from "../media-transcode/ffmpeg-capabilities.ts";
|
|
10
|
+
import { captureInputForPlatform } from "./remote-desktop-capture-input.ts";
|
|
11
|
+
import { getInputBackend } from "./remote-desktop-input.ts";
|
|
12
|
+
import {
|
|
13
|
+
MAC_PERMISSION_SETTINGS_URL,
|
|
14
|
+
macPermissionStatus,
|
|
15
|
+
requestMacPermission,
|
|
16
|
+
type MacPermissionId,
|
|
17
|
+
} from "./remote-desktop-macos-permissions.ts";
|
|
18
|
+
|
|
19
|
+
export type HostAction = "request" | "open-settings";
|
|
20
|
+
|
|
21
|
+
export type RequirementAction =
|
|
22
|
+
/** Type `command` into a PPM terminal — that shell runs on the host, so this works from a phone. */
|
|
23
|
+
| { kind: "terminal"; label: string; command: string }
|
|
24
|
+
/** A web page the *client* opens in its own browser (docs, downloads). */
|
|
25
|
+
| { kind: "link"; label: string; url: string }
|
|
26
|
+
/** Something only the host can do — show an OS permission prompt, open its Settings pane.
|
|
27
|
+
* `POST /api/remote-desktop/requirements/:id/:action`. Never a client-side URL: a
|
|
28
|
+
* `x-apple.systempreferences:` link means nothing on the phone driving the session. */
|
|
29
|
+
| { kind: "host"; label: string; action: HostAction };
|
|
30
|
+
|
|
31
|
+
export interface RemoteDesktopRequirement {
|
|
32
|
+
id: string;
|
|
33
|
+
ok: boolean;
|
|
34
|
+
/** Which part of the feature this blocks: no video = nothing to show; no input = view-only. */
|
|
35
|
+
gates: "video" | "input";
|
|
36
|
+
title: string;
|
|
37
|
+
/** One sentence: what it is for and what to do. Shown only while `ok` is false. */
|
|
38
|
+
detail: string;
|
|
39
|
+
actions: RequirementAction[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface RemoteDesktopReadiness {
|
|
43
|
+
platform: NodeJS.Platform;
|
|
44
|
+
/** There is a capture path for this OS at all. Gates the UI entry; everything else is a
|
|
45
|
+
* requirement the user can satisfy in place. */
|
|
46
|
+
platformSupported: boolean;
|
|
47
|
+
requirements: RemoteDesktopRequirement[];
|
|
48
|
+
/** Every `video` requirement is met. */
|
|
49
|
+
videoReady: boolean;
|
|
50
|
+
/** Every `input` requirement is met (and the platform has an injector). */
|
|
51
|
+
inputReady: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const FFMPEG_INSTALL: Partial<Record<NodeJS.Platform, RequirementAction>> = {
|
|
55
|
+
darwin: { kind: "terminal", label: "Install with Homebrew", command: "brew install ffmpeg" },
|
|
56
|
+
win32: { kind: "terminal", label: "Install with winget", command: "winget install --id Gyan.FFmpeg -e" },
|
|
57
|
+
linux: { kind: "terminal", label: "Install with apt", command: "sudo apt install ffmpeg" },
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function ffmpegRequirement(platform: NodeJS.Platform, present: boolean): RemoteDesktopRequirement {
|
|
61
|
+
const install = FFMPEG_INSTALL[platform];
|
|
62
|
+
return {
|
|
63
|
+
id: "ffmpeg",
|
|
64
|
+
ok: present,
|
|
65
|
+
gates: "video",
|
|
66
|
+
title: "ffmpeg",
|
|
67
|
+
detail: "Captures and encodes the screen. Install it, then come back — PPM re-checks automatically.",
|
|
68
|
+
actions: [
|
|
69
|
+
...(install ? [install] : []),
|
|
70
|
+
{ kind: "link", label: "Download page", url: "https://ffmpeg.org/download.html" },
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function macPermissionRequirement(id: MacPermissionId, granted: boolean): RemoteDesktopRequirement {
|
|
76
|
+
const screen = id === "screen-recording";
|
|
77
|
+
return {
|
|
78
|
+
id,
|
|
79
|
+
ok: granted,
|
|
80
|
+
gates: screen ? "video" : "input",
|
|
81
|
+
title: screen ? "Screen Recording permission" : "Accessibility permission",
|
|
82
|
+
detail: screen
|
|
83
|
+
? "Without it macOS hands PPM a black screen. Allow the PPM process (bun) under Screen & System Audio Recording."
|
|
84
|
+
: "Needed to move the mouse and type. Add the PPM process (bun) under Accessibility; until then the view is read-only.",
|
|
85
|
+
actions: [
|
|
86
|
+
...(screen ? [{ kind: "host", label: "Ask macOS now", action: "request" } as RequirementAction] : []),
|
|
87
|
+
{ kind: "host", label: "Open System Settings on the host", action: "open-settings" },
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function remoteDesktopReadiness(platform: NodeJS.Platform = process.platform): Promise<RemoteDesktopReadiness> {
|
|
93
|
+
const platformSupported = captureInputForPlatform(platform) !== null;
|
|
94
|
+
const requirements: RemoteDesktopRequirement[] = [];
|
|
95
|
+
if (platformSupported) {
|
|
96
|
+
const caps = await getFfmpegCapabilities();
|
|
97
|
+
requirements.push(ffmpegRequirement(platform, !!caps.ffmpeg));
|
|
98
|
+
if (platform === "darwin") {
|
|
99
|
+
const status = await macPermissionStatus();
|
|
100
|
+
requirements.push(macPermissionRequirement("screen-recording", status["screen-recording"]));
|
|
101
|
+
requirements.push(macPermissionRequirement("accessibility", status.accessibility));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const inputSupported = getInputBackend(platform) !== null;
|
|
105
|
+
return {
|
|
106
|
+
platform,
|
|
107
|
+
platformSupported,
|
|
108
|
+
requirements,
|
|
109
|
+
videoReady: platformSupported && requirements.filter((r) => r.gates === "video").every((r) => r.ok),
|
|
110
|
+
inputReady: inputSupported && requirements.filter((r) => r.gates === "input").every((r) => r.ok),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Run a `host` action. Returns the requirement's `ok` afterwards, or null when the pair has
|
|
115
|
+
* no host path (caller answers 404). `open-settings` returns the current state — the grant
|
|
116
|
+
* lands later, the UI keeps polling. */
|
|
117
|
+
export async function runHostAction(id: string, action: HostAction): Promise<boolean | null> {
|
|
118
|
+
if (id !== "screen-recording" && id !== "accessibility") return null;
|
|
119
|
+
if (action === "request") return requestMacPermission(id);
|
|
120
|
+
if (process.platform === "darwin") {
|
|
121
|
+
Bun.spawn(["open", MAC_PERMISSION_SETTINGS_URL[id]], { stdout: "ignore", stderr: "ignore" });
|
|
122
|
+
}
|
|
123
|
+
return (await macPermissionStatus())[id];
|
|
124
|
+
}
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
import { startCapture, type CaptureHandle } from "./remote-desktop-capture.ts";
|
|
8
8
|
import { avc1CodecString } from "./avc1-codec-string.ts";
|
|
9
9
|
import type { AccessUnit } from "./access-unit-assembler.ts";
|
|
10
|
-
import { injectPointer, injectKey, injectWheel, releaseAllModifiers, isInputAvailable } from "./remote-desktop-input.ts";
|
|
10
|
+
import { injectPointer, injectKey, injectWheel, injectText, releaseAllModifiers, isInputAvailable } from "./remote-desktop-input.ts";
|
|
11
|
+
import { resolveDisplay, type RemoteDisplay } from "./remote-desktop-displays.ts";
|
|
11
12
|
|
|
12
13
|
/** Minimal socket surface this module needs — matches Bun's `ServerWebSocket` shape closely
|
|
13
14
|
* enough to be faked in a unit test without a real connection. */
|
|
@@ -28,6 +29,8 @@ const HEARTBEAT_TIMEOUT_MS = 30_000;
|
|
|
28
29
|
* queueing forever — a slow WAN/tunnel link must degrade to "waits ~2s for a keyframe",
|
|
29
30
|
* never to unbounded memory growth or an ever-growing latency queue. */
|
|
30
31
|
const BACKPRESSURE_THRESHOLD_BYTES = 512 * 1024;
|
|
32
|
+
/** Longest `text` message injected in one go — a paste, not a file; anything bigger is dropped. */
|
|
33
|
+
const MAX_TEXT_CHARS = 1024;
|
|
31
34
|
|
|
32
35
|
export class RemoteDesktopSession {
|
|
33
36
|
private capture: CaptureHandle | null = null;
|
|
@@ -41,12 +44,13 @@ export class RemoteDesktopSession {
|
|
|
41
44
|
/** Resolves once the underlying ffmpeg process has actually exited (not merely asked to). */
|
|
42
45
|
readonly exited: Promise<void>;
|
|
43
46
|
|
|
44
|
-
constructor(private readonly ws: RemoteDesktopSocket) {
|
|
47
|
+
constructor(private readonly ws: RemoteDesktopSocket, private readonly display: RemoteDisplay | null) {
|
|
45
48
|
this.exited = new Promise((resolve) => { this.exitResolve = resolve; });
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
async start(): Promise<void> {
|
|
49
52
|
this.capture = await startCapture({
|
|
53
|
+
display: this.display,
|
|
50
54
|
onAccessUnit: (au) => this.handleAccessUnit(au),
|
|
51
55
|
// `reason` is only set when ffmpeg died on its own (crash, access denied, etc) — tell
|
|
52
56
|
// the client *why* before closing instead of leaving it to guess from a bare
|
|
@@ -80,7 +84,7 @@ export class RemoteDesktopSession {
|
|
|
80
84
|
const { xFrac, yFrac, button, down } = msg as { xFrac?: unknown; yFrac?: unknown; button?: unknown; down?: unknown };
|
|
81
85
|
if (typeof xFrac === "number" && typeof yFrac === "number") {
|
|
82
86
|
const btn = button === "left" || button === "right" ? button : null;
|
|
83
|
-
await injectPointer(xFrac, yFrac, btn, typeof down === "boolean" ? down : null);
|
|
87
|
+
await injectPointer(xFrac, yFrac, btn, typeof down === "boolean" ? down : null, this.display);
|
|
84
88
|
}
|
|
85
89
|
return;
|
|
86
90
|
}
|
|
@@ -97,6 +101,13 @@ export class RemoteDesktopSession {
|
|
|
97
101
|
}
|
|
98
102
|
return;
|
|
99
103
|
}
|
|
104
|
+
if (msg.type === "text") {
|
|
105
|
+
// Layout-independent text entry (mobile keyboards, non-ASCII). Backends without a text
|
|
106
|
+
// path report false and the message is dropped — the client keeps its per-key fallback.
|
|
107
|
+
const { text } = msg as { text?: unknown };
|
|
108
|
+
if (typeof text === "string" && text.length > 0 && text.length <= MAX_TEXT_CHARS) await injectText(text);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
100
111
|
if (msg.type === "releaseAll") {
|
|
101
112
|
await this.releaseHeldKeys();
|
|
102
113
|
}
|
|
@@ -164,12 +175,12 @@ const activeSessions = new Set<RemoteDesktopSession>();
|
|
|
164
175
|
|
|
165
176
|
/** Evict any previous session (awaiting its ffmpeg exit, capped so a wedged process can't
|
|
166
177
|
* hang a reconnect) before starting the new one. */
|
|
167
|
-
export async function createRemoteDesktopSession(ws: RemoteDesktopSocket): Promise<RemoteDesktopSession> {
|
|
178
|
+
export async function createRemoteDesktopSession(ws: RemoteDesktopSocket, displayId?: string): Promise<RemoteDesktopSession> {
|
|
168
179
|
for (const existing of [...activeSessions]) {
|
|
169
180
|
existing.close();
|
|
170
181
|
await Promise.race([existing.exited, Bun.sleep(2000)]);
|
|
171
182
|
}
|
|
172
|
-
const session = new RemoteDesktopSession(ws);
|
|
183
|
+
const session = new RemoteDesktopSession(ws, await resolveDisplay(displayId));
|
|
173
184
|
await session.start();
|
|
174
185
|
activeSessions.add(session);
|
|
175
186
|
return session;
|