@tequila99/githuman 0.4.2 → 0.6.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/README.md +18 -2
- package/dist/cli/index.js +27 -2
- package/dist/cli/server-runtime.js +1 -0
- package/dist/server/app.js +22 -1
- package/dist/server/errors/git.js +2 -0
- package/dist/server/errors/terminal.js +4 -0
- package/dist/server/hooks/terminal/local-origin.js +25 -0
- package/dist/server/routes/agent.js +8 -2
- package/dist/server/routes/app-info.js +5 -2
- package/dist/server/routes/git.js +5 -2
- package/dist/server/routes/terminal.js +63 -0
- package/dist/server/services/agent/agent-session.js +2 -1
- package/dist/server/services/agent/context-builder.js +49 -2
- package/dist/server/services/agent/file-index.js +7 -4
- package/dist/server/services/git.service.js +44 -4
- package/dist/server/services/terminal/backend.js +216 -0
- package/dist/server/services/terminal/channel.js +200 -0
- package/dist/server/services/terminal/escape-tail.js +93 -0
- package/dist/server/services/terminal/processes.js +115 -0
- package/dist/server/services/terminal/prompt-tracker.js +15 -0
- package/dist/server/services/terminal/registry.js +167 -0
- package/dist/server/services/terminal/session.js +238 -0
- package/dist/server/services/terminal/socket.js +74 -0
- package/dist/server/services/terminal/tokens.js +32 -0
- package/dist/server/utils/sse.js +76 -5
- package/dist/shared/agents/file-search.js +19 -7
- package/dist/shared/agents/mention-paths.js +40 -0
- package/dist/shared/agents/schemas.js +13 -4
- package/dist/shared/app/schemas.js +5 -1
- package/dist/shared/git/schemas.js +4 -1
- package/dist/shared/terminal/constants.js +27 -0
- package/dist/shared/terminal/schemas.js +114 -0
- package/dist/shared/terminal/types.js +1 -0
- package/dist/shared/utils/guards.js +3 -0
- package/dist/web/assets/{AppPage-BYrCfRnC.js → AppPage-CiRB13sb.js} +2 -2
- package/dist/web/assets/CreateReviewFabButton-DTvX9Psc.js +1 -0
- package/dist/web/assets/ErrorNotFound-CYOZg8yN.js +1 -0
- package/dist/web/assets/IndexPage-CYFIGWy9.css +1 -0
- package/dist/web/assets/IndexPage-hI7bcJ6L.js +1 -0
- package/dist/web/assets/MainLayout-DCexR9_Y.css +1 -0
- package/dist/web/assets/MainLayout-baVxjyeL.js +11 -0
- package/dist/web/assets/PdfPreview-Cb0xawi0.css +1 -0
- package/dist/web/assets/PdfPreview-D3HwZ7pH.js +54 -0
- package/dist/web/assets/QBtn-CzrOO1zA.js +1 -0
- package/dist/web/assets/QScrollArea-Cu8zdp0y.js +1 -0
- package/dist/web/assets/QSelect-DQ_RRYIL.js +1 -0
- package/dist/web/assets/QSpace-CEuZSBS2.js +13 -0
- package/dist/web/assets/ReviewDetailPage-CayB2dtb.js +1 -0
- package/dist/web/assets/ReviewDetailPage-CkCilMNW.css +1 -0
- package/dist/web/assets/ReviewsPage-DWfEVjWO.js +1 -0
- package/dist/web/assets/StatusSwitcher--zNcE2-Y.js +1 -0
- package/dist/web/assets/StatusSwitcher-DTD5inU1.css +1 -0
- package/dist/web/assets/TerminalView-B11UHZv3.css +1 -0
- package/dist/web/assets/TerminalView-DvVbWk3f.js +19 -0
- package/dist/web/assets/_plugin-vue_export-helper-BDNMzG2s.js +1 -0
- package/dist/web/assets/{abnfDiagram-O67JEVCF-dyvffE6R.js → abnfDiagram-O67JEVCF-C5SYdl8k.js} +1 -1
- package/dist/web/assets/agent-store-DX4gIEHa.js +1 -0
- package/dist/web/assets/app-info-store-Vy_Pq2yR.js +1 -0
- package/dist/web/assets/architecture-7GRP2DOG-Dpxe25D8.js +1 -0
- package/dist/web/assets/{architectureDiagram-NJMV4G6O-Dk-QthCI.js → architectureDiagram-NJMV4G6O-Bx8KGjS_.js} +1 -1
- package/dist/web/assets/{blockDiagram-BEXU5L5S-sF48dzNS.js → blockDiagram-BEXU5L5S-B4ybhPir.js} +1 -1
- package/dist/web/assets/{c4Diagram-YGBWAQC7-BWrux-0F.js → c4Diagram-YGBWAQC7-D2Fzm9Zx.js} +1 -1
- package/dist/web/assets/channel--0tdF_Sb.js +1 -0
- package/dist/web/assets/chunk-3FUC2YCW-DLRX-HPs.js +2 -0
- package/dist/web/assets/{chunk-5DYCD2WN-CpgXf7LJ.js → chunk-5DYCD2WN-6HcgVbfR.js} +1 -1
- package/dist/web/assets/{chunk-742MDFTN-BGzG-dZE.js → chunk-742MDFTN-CfTGkhlJ.js} +1 -1
- package/dist/web/assets/{chunk-7INBJB4K-DJGc-gqS.js → chunk-7INBJB4K-COfv2qWf.js} +1 -1
- package/dist/web/assets/{chunk-7M6MHVWA-CYIOaZel.js → chunk-7M6MHVWA-D0FxecF4.js} +5 -5
- package/dist/web/assets/{chunk-7PRAP22T-CJZNYL51.js → chunk-7PRAP22T-DFjSoEy0.js} +1 -1
- package/dist/web/assets/{chunk-GTNCS2PH-DOYGHmMk.js → chunk-GTNCS2PH-CGWGXDNO.js} +1 -1
- package/dist/web/assets/{chunk-GWA4HPMP-RIikrSw1.js → chunk-GWA4HPMP-J0EaOPjD.js} +1 -1
- package/dist/web/assets/{chunk-MBY4JIJT-ZSssyEJi.js → chunk-MBY4JIJT-PzEUkWiy.js} +1 -1
- package/dist/web/assets/{chunk-NETBCI7D-3tnHEq90.js → chunk-NETBCI7D-DHHOcLF0.js} +1 -1
- package/dist/web/assets/{chunk-O7XYJQB3-CDFTy-aQ.js → chunk-O7XYJQB3-BHUId2wW.js} +1 -1
- package/dist/web/assets/{chunk-UA2S7LBM-B8rY8Yj-.js → chunk-UA2S7LBM-DXQ1DAj6.js} +1 -1
- package/dist/web/assets/{chunk-WEXAMYUT-BdPY0e7q.js → chunk-WEXAMYUT-BMiibMha.js} +1 -1
- package/dist/web/assets/{chunk-Z7XXMR3K-Br_E149D.js → chunk-Z7XXMR3K-BcgKYeJS.js} +1 -1
- package/dist/web/assets/{chunk-ZIGJFQKS-DDjPFexL.js → chunk-ZIGJFQKS-D6psU74o.js} +1 -1
- package/dist/web/assets/{classDiagram-v2-NBCMYWYE-D9kneDxy.js → classDiagram-v2-NBCMYWYE-6IwOSgSE.js} +4 -4
- package/dist/web/assets/create-BSmXD9Zr.js +1 -0
- package/dist/web/assets/{cynefin-OW5HDTMX-Dot1PBqs.js → cynefin-OW5HDTMX-C77WtKve.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-VND7K2PF-6uYi0u-j.js → cynefinDiagram-VND7K2PF-BvIp3Y9w.js} +1 -1
- package/dist/web/assets/{dagre-6A5THRUB-BDM5YkfJ.js → dagre-6A5THRUB-DN7jrLfY.js} +1 -1
- package/dist/web/assets/debounce-rZaFFwsz.js +1 -0
- package/dist/web/assets/{diagram-22UHCM2B-DxE0D8WB.js → diagram-22UHCM2B-DQUkKx9q.js} +8 -8
- package/dist/web/assets/{diagram-3UASUU5V-CuD1fcFQ.js → diagram-3UASUU5V-CTzVKsCf.js} +1 -1
- package/dist/web/assets/{diagram-ATOU4E4O-DX6enGgq.js → diagram-ATOU4E4O--Z3AVUjn.js} +1 -1
- package/dist/web/assets/{diagram-CDSNMT55-eLqOiVoC.js → diagram-CDSNMT55-BA-cRZ39.js} +1 -1
- package/dist/web/assets/{diagram-MLGK6HIB-DtIMj5y1.js → diagram-MLGK6HIB-Bx_zLrYg.js} +1 -1
- package/dist/web/assets/{diagram-MPIPVDR6-lYcwsjHR.js → diagram-MPIPVDR6-CWKuh-e-.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-ZINNZB2B-CGmBWaKx.js → ebnfDiagram-ZINNZB2B-MyQLfQVI.js} +1 -1
- package/dist/web/assets/{elk-276RUBZZ-B1ntUD9z.js → elk-276RUBZZ-DBbqEAnd.js} +1 -1
- package/dist/web/assets/{erDiagram-OPXOYQCR-Dqzoldin.js → erDiagram-OPXOYQCR-CbRo_Rnk.js} +1 -1
- package/dist/web/assets/eventmodeling-NTZA5JFV-DMla2cPF.js +1 -0
- package/dist/web/assets/flowDiagram-KWPJA3E3-Bas61_mX.js +1 -0
- package/dist/web/assets/{ganttDiagram-FUAMR5RP-CF6kJAoY.js → ganttDiagram-FUAMR5RP-D1gJBc1W.js} +1 -1
- package/dist/web/assets/{gitGraph-4MIJSDKK-CUzcGxMa.js → gitGraph-4MIJSDKK-CIzz8UCs.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-X574FWY7-D-XNa6f2.js → gitGraphDiagram-X574FWY7-B5-RB5JZ.js} +1 -1
- package/dist/web/assets/i18n-B1_G449i.js +1 -0
- package/dist/web/assets/index-DEwYKvIG.js +2 -0
- package/dist/web/assets/index-Dma-RXBR.css +1 -0
- package/dist/web/assets/{info-A6RAGUB7-CH4SNmU8.js → info-A6RAGUB7-DF-JCGlS.js} +1 -1
- package/dist/web/assets/{infoDiagram-VRGFBTTK-35AKnD7M.js → infoDiagram-VRGFBTTK-z0Xepdo0.js} +1 -1
- package/dist/web/assets/init-app-DAsY3Joz.js +1 -0
- package/dist/web/assets/{is-BB7rJzdU.js → is-DssfAlMe.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-D41YBWSb.js → ishikawaDiagram-OU5B5YK6-UqXiqAoe.js} +1 -1
- package/dist/web/assets/{journeyDiagram-ZHPQQLJL-CZF6Wwk8.js → journeyDiagram-ZHPQQLJL-CPGeWRg-.js} +1 -1
- package/dist/web/assets/{kanban-definition-PNTS6WVX-CVGaUle0.js → kanban-definition-PNTS6WVX-CMq9WuD5.js} +1 -1
- package/dist/web/assets/{line-CeH4KBRO.js → line-cZB9xcQk.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-YrU9LTTJ.js → mermaid-parser.core-D8h1T47v.js} +3 -3
- package/dist/web/assets/{mermaid.core-BkLEn5Vh.js → mermaid.core-CdpJws-B.js} +7 -7
- package/dist/web/assets/{mindmap-definition-NLK3R4M7-D22lxuXn.js → mindmap-definition-NLK3R4M7-B01KHb5Z.js} +1 -1
- package/dist/web/assets/{packet-AYTQ26CC-DMK0BdeB.js → packet-AYTQ26CC-CagxQ791.js} +1 -1
- package/dist/web/assets/pdf.worker-C2cQdohj.js +48 -0
- package/dist/web/assets/{pegDiagram-GJSIUBJH-B-e5e5cW.js → pegDiagram-GJSIUBJH-amhZRgGf.js} +1 -1
- package/dist/web/assets/{pie-WAS4IAKB-BYP5ND4b.js → pie-WAS4IAKB-zv3udP2i.js} +1 -1
- package/dist/web/assets/{pieDiagram-5QR66LMP-DrEY0oA_.js → pieDiagram-5QR66LMP-CjPzT-fa.js} +1 -1
- package/dist/web/assets/pinia-DCsnCHsH.js +1 -0
- package/dist/web/assets/pinia-DWj71cM9.js +1 -0
- package/dist/web/assets/{quadrantDiagram-O4NWA36T-D12UM_7Y.js → quadrantDiagram-O4NWA36T-7YpPBn0t.js} +1 -1
- package/dist/web/assets/{radar-RG4KPBEZ-50JpTuMu.js → radar-RG4KPBEZ-DZ-nSYpA.js} +1 -1
- package/dist/web/assets/{railroad-74A4TZTK-C-7jDcYE.js → railroad-74A4TZTK-CoylUO-u.js} +1 -1
- package/dist/web/assets/railroad-abnf-HS5TGJTU-C20Z1OCq.js +1 -0
- package/dist/web/assets/railroad-ebnf-LZEXJU2U-BltGHYDI.js +1 -0
- package/dist/web/assets/railroad-peg-WCYAUIDC-BT3QI-zw.js +1 -0
- package/dist/web/assets/{railroadDiagram-XR7U4H2S-q0-o99ts.js → railroadDiagram-XR7U4H2S-DlHZky0v.js} +1 -1
- package/dist/web/assets/{requirementDiagram-PLB6GJNP-DsO-Ejin.js → requirementDiagram-PLB6GJNP-Dn9whLhB.js} +1 -1
- package/dist/web/assets/runtime-core.esm-bundler-BKFI1w28.js +1 -0
- package/dist/web/assets/{safe-storage-DRouskD4.js → safe-storage-B3bJArYP.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-IPEJSGJF-Ct9goD-6.js → sankeyDiagram-IPEJSGJF-jIf4UAnN.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-PO4LG4MO-BMO_A2FU.js → sequenceDiagram-PO4LG4MO-CJahxeyg.js} +1 -1
- package/dist/web/assets/{stateDiagram-v2-GCMORJYK-BSzg0zQp.js → stateDiagram-v2-GCMORJYK-DArbI5q3.js} +2 -2
- package/dist/web/assets/{swimlanes-2SLR337P-CcmfFMci.js → swimlanes-2SLR337P-wyJPqm8c.js} +1 -1
- package/dist/web/assets/swimlanesDiagram-TC7HE7FX-DgRybibH.js +8 -0
- package/dist/web/assets/theme-CFC7Mb_O.js +1 -0
- package/dist/web/assets/{timeline-definition-EJHVYXUP-0dcHdY2G.js → timeline-definition-EJHVYXUP-Bdh9ma4o.js} +1 -1
- package/dist/web/assets/{treeView-Q6P3EWNA-KynQIJ_Z.js → treeView-Q6P3EWNA-CEiXTwqS.js} +1 -1
- package/dist/web/assets/{treemap-WGGIJYW6-DPpYRzcH.js → treemap-WGGIJYW6-am0aIfU-.js} +1 -1
- package/dist/web/assets/use-app-theme-FYzHIigd.js +1 -0
- package/dist/web/assets/use-panel-width-e-yXva8i.js +1 -0
- package/dist/web/assets/use-server-events-znzHKZZx.js +1 -0
- package/dist/web/assets/use-virtual-scroll-CIwaJCUj.js +1 -0
- package/dist/web/assets/{usecaseDiagram-POWQR4AR-DJBtB_eN.js → usecaseDiagram-POWQR4AR-r3Qf5Bj-.js} +1 -1
- package/dist/web/assets/{vennDiagram-UO4OBE2U-BgllcrO_.js → vennDiagram-UO4OBE2U-eOWNPvLI.js} +1 -1
- package/dist/web/assets/{vue-i18n.runtime-D0pkhc7g.js → vue-i18n.runtime-BpRrzZrT.js} +2 -2
- package/dist/web/assets/{wardley-WFR3VGLG-D2Je-cZt.js → wardley-WFR3VGLG-BdipY77V.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-VNRHLVJA-B7ktegci.js → wardleyDiagram-VNRHLVJA-sFpl0u9C.js} +1 -1
- package/dist/web/assets/{xychartDiagram-PMCCYNJV-DL7WkDZI.js → xychartDiagram-PMCCYNJV-CtLqW--F.js} +1 -1
- package/dist/web/index.html +7 -7
- package/dist/web/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/78-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/78-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Add-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Add-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/B5-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/B5-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/B5pc-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/B5pc-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/CNS1-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/CNS1-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/CNS2-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/CNS2-V.bcmap +3 -0
- package/dist/web/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
- package/dist/web/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/EUC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/EUC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Ext-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Ext-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GB-H.bcmap +4 -0
- package/dist/web/pdfjs/cmaps/GB-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBT-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBT-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Hankaku.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Hiragana.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/KSC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/KSC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Katakana.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/LICENSE +36 -0
- package/dist/web/pdfjs/cmaps/NWP-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/NWP-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/Roman.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/V.bcmap +0 -0
- package/dist/web/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
- package/dist/web/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
- package/dist/web/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
- package/dist/web/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
- package/dist/web/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
- package/dist/web/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
- package/dist/web/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
- package/dist/web/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
- package/dist/web/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
- package/dist/web/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
- package/dist/web/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
- package/dist/web/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
- package/dist/web/pdfjs/standard_fonts/LICENSE_LIBERATION +363 -0
- package/dist/web/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
- package/dist/web/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
- package/dist/web/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
- package/dist/web/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
- package/dist/web/pdfjs/wasm/LICENSE_JBIG2 +196 -0
- package/dist/web/pdfjs/wasm/LICENSE_OPENJPEG +39 -0
- package/dist/web/pdfjs/wasm/LICENSE_PDFJS_JBIG2 +13 -0
- package/dist/web/pdfjs/wasm/LICENSE_PDFJS_OPENJPEG +22 -0
- package/dist/web/pdfjs/wasm/LICENSE_PDFJS_QCMS +19 -0
- package/dist/web/pdfjs/wasm/LICENSE_QCMS +21 -0
- package/dist/web/pdfjs/wasm/jbig2.wasm +0 -0
- package/dist/web/pdfjs/wasm/jbig2_nowasm_fallback.js +15 -0
- package/dist/web/pdfjs/wasm/openjpeg.wasm +0 -0
- package/dist/web/pdfjs/wasm/openjpeg_nowasm_fallback.js +17 -0
- package/dist/web/pdfjs/wasm/qcms_bg.wasm +0 -0
- package/dist/web/pdfjs/wasm/quickjs-eval.js +16 -0
- package/dist/web/pdfjs/wasm/quickjs-eval.wasm +0 -0
- package/package.json +13 -3
- package/dist/web/assets/CreateReviewFabButton-UdzUa_ra.js +0 -1
- package/dist/web/assets/ErrorNotFound-BAP3n0aC.js +0 -1
- package/dist/web/assets/IndexPage-BtoeYBg2.js +0 -1
- package/dist/web/assets/IndexPage-D894ONbY.css +0 -1
- package/dist/web/assets/MainLayout-ChxXEt_Q.css +0 -1
- package/dist/web/assets/MainLayout-DjoItVv0.js +0 -7
- package/dist/web/assets/QBtn-BDbJU6_B.js +0 -1
- package/dist/web/assets/QMenu-Bh-SjGqz.js +0 -1
- package/dist/web/assets/QSelect-CZqAOLiG.js +0 -1
- package/dist/web/assets/ReviewDetailPage-CP6YqXPT.css +0 -1
- package/dist/web/assets/ReviewDetailPage-DvDoJ37c.js +0 -1
- package/dist/web/assets/ReviewsPage-CHUXLKKw.js +0 -1
- package/dist/web/assets/StatusSwitcher-Br_8-_CT.js +0 -2
- package/dist/web/assets/StatusSwitcher-DLU1h0rK.css +0 -1
- package/dist/web/assets/agent-icon-BUd-vCRS.css +0 -1
- package/dist/web/assets/agent-icon-Bc3evt8o.js +0 -17
- package/dist/web/assets/agent-store-FOng_RxK.js +0 -1
- package/dist/web/assets/app-info-store-xZiQfdgO.js +0 -1
- package/dist/web/assets/architecture-7GRP2DOG-JRGyT2Fi.js +0 -1
- package/dist/web/assets/channel-C9BIu6HY.js +0 -1
- package/dist/web/assets/chunk-3FUC2YCW-DM9oDQ0E.js +0 -2
- package/dist/web/assets/create-BkZH2Q0U.js +0 -1
- package/dist/web/assets/debounce-CDIE7CV6.js +0 -1
- package/dist/web/assets/eventmodeling-NTZA5JFV-DblBuyVi.js +0 -1
- package/dist/web/assets/export-file-Di_2jvjP.js +0 -1
- package/dist/web/assets/flowDiagram-KWPJA3E3-D5iLm5ip.js +0 -1
- package/dist/web/assets/i18n-DvBemXXT.js +0 -1
- package/dist/web/assets/index-Bn7ByP7x.js +0 -2
- package/dist/web/assets/index-CgeU6Wuu.css +0 -1
- package/dist/web/assets/init-app-C4PeiqQX.js +0 -1
- package/dist/web/assets/pinia-DB8WAJFa.js +0 -1
- package/dist/web/assets/pinia-DmTi2T44.js +0 -1
- package/dist/web/assets/railroad-abnf-HS5TGJTU-Bf3pkLrz.js +0 -1
- package/dist/web/assets/railroad-ebnf-LZEXJU2U-f10AkgiO.js +0 -1
- package/dist/web/assets/railroad-peg-WCYAUIDC-C4FjCCVf.js +0 -1
- package/dist/web/assets/runtime-core.esm-bundler-CUpq88z7.js +0 -1
- package/dist/web/assets/single-flight-r8B7stt1.js +0 -1
- package/dist/web/assets/swimlanesDiagram-TC7HE7FX-DLq4WqTk.js +0 -8
- package/dist/web/assets/theme-DQpF6lSZ.js +0 -1
- package/dist/web/assets/use-app-theme-B1Vh5WKT.js +0 -1
- package/dist/web/assets/use-panel-width-Cs_K2M_v.js +0 -1
- package/dist/web/assets/use-virtual-scroll-BVHTKnoC.js +0 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { PromptTracker, TERMINAL_PROMPT_MARKER } from "./prompt-tracker.js";
|
|
3
|
+
import { basename } from 'node:path';
|
|
4
|
+
import { TerminalError } from "../../errors/terminal.js";
|
|
5
|
+
import { classifyProcessActivity, processActivity, signalProcess, terminateProcesses } from "./processes.js";
|
|
6
|
+
// Stop waiting for the first prompt marker after this time.
|
|
7
|
+
const STARTUP_TIMEOUT_MS = 2000;
|
|
8
|
+
// Stop waiting for the first prompt marker after this many output characters.
|
|
9
|
+
const STARTUP_BUFFER_CHARS = 65536;
|
|
10
|
+
// Shells that accept `-l -i` and set the prompt through `PS1`. Other shells get no prompt marker.
|
|
11
|
+
const POSIX_SHELLS = ['bash', 'zsh', 'sh', 'dash', 'ksh'];
|
|
12
|
+
// Windows includes a PTY binary, but the shell and process list are Unix-only.
|
|
13
|
+
export function terminalSupported(platform = process.platform) {
|
|
14
|
+
return platform !== 'win32';
|
|
15
|
+
}
|
|
16
|
+
function promptPrefix(shellName) {
|
|
17
|
+
if (shellName === 'bash')
|
|
18
|
+
return "'\\[\\e]133;A\\a\\]'";
|
|
19
|
+
if (shellName === 'zsh')
|
|
20
|
+
return '"%{$(printf \'\\033]133;A\\007\')%}"';
|
|
21
|
+
return '"$(printf \'\\033]133;A\\007\')"';
|
|
22
|
+
}
|
|
23
|
+
// Bash stores this typed line, so delete that entry before the marker.
|
|
24
|
+
function promptSetup(shellName) {
|
|
25
|
+
const assign = `PS1=${promptPrefix(shellName)}"$PS1"`;
|
|
26
|
+
const marker = "printf '\\033]133;A\\007'";
|
|
27
|
+
if (shellName === 'bash')
|
|
28
|
+
return `${assign}; history -d $HISTCMD || true; ${marker}`;
|
|
29
|
+
return `${assign}; ${marker}`;
|
|
30
|
+
}
|
|
31
|
+
async function shellActivity(pid, prompt, rows) {
|
|
32
|
+
const activity = rows === undefined
|
|
33
|
+
? await processActivity(pid)
|
|
34
|
+
: classifyProcessActivity(rows, pid);
|
|
35
|
+
if (activity !== 'idle')
|
|
36
|
+
return activity;
|
|
37
|
+
return prompt.idle ? 'idle' : 'unknown';
|
|
38
|
+
}
|
|
39
|
+
let modulePromise;
|
|
40
|
+
export function loadPty() {
|
|
41
|
+
modulePromise ??= import('@lydell/node-pty').catch(() => null);
|
|
42
|
+
return modulePromise;
|
|
43
|
+
}
|
|
44
|
+
export class NodePtyBackend {
|
|
45
|
+
mode = 'pty';
|
|
46
|
+
pty;
|
|
47
|
+
prompt = new PromptTracker();
|
|
48
|
+
listeners = new Set();
|
|
49
|
+
disposeData;
|
|
50
|
+
startupTimer;
|
|
51
|
+
constructor(pty, shellName) {
|
|
52
|
+
this.pty = pty;
|
|
53
|
+
const integrated = shellName !== undefined && POSIX_SHELLS.includes(shellName);
|
|
54
|
+
let ready = !integrated;
|
|
55
|
+
let startup = '';
|
|
56
|
+
const emit = (data) => {
|
|
57
|
+
for (const listener of this.listeners)
|
|
58
|
+
listener(data);
|
|
59
|
+
};
|
|
60
|
+
this.disposeData = pty.onData(data => {
|
|
61
|
+
this.prompt.output(data);
|
|
62
|
+
if (ready) {
|
|
63
|
+
emit(data);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
startup += data;
|
|
67
|
+
const boundary = startup.indexOf(TERMINAL_PROMPT_MARKER);
|
|
68
|
+
if (boundary >= 0) {
|
|
69
|
+
ready = true;
|
|
70
|
+
emit(startup.slice(boundary));
|
|
71
|
+
startup = '';
|
|
72
|
+
clearTimeout(this.startupTimer);
|
|
73
|
+
}
|
|
74
|
+
else if (startup.length > STARTUP_BUFFER_CHARS) {
|
|
75
|
+
ready = true;
|
|
76
|
+
emit(startup);
|
|
77
|
+
startup = '';
|
|
78
|
+
clearTimeout(this.startupTimer);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
if (integrated) {
|
|
82
|
+
this.startupTimer = setTimeout(() => {
|
|
83
|
+
ready = true;
|
|
84
|
+
emit(startup);
|
|
85
|
+
startup = '';
|
|
86
|
+
}, STARTUP_TIMEOUT_MS);
|
|
87
|
+
this.startupTimer.unref();
|
|
88
|
+
// Preserve the user's prompt and add an invisible activity marker.
|
|
89
|
+
if (shellName !== undefined)
|
|
90
|
+
pty.write(`${promptSetup(shellName)}\r`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
get pid() {
|
|
94
|
+
return this.pty.pid;
|
|
95
|
+
}
|
|
96
|
+
write(data) {
|
|
97
|
+
this.prompt.input();
|
|
98
|
+
this.pty.write(data);
|
|
99
|
+
}
|
|
100
|
+
resize(cols, rows) {
|
|
101
|
+
this.pty.resize(cols, rows);
|
|
102
|
+
}
|
|
103
|
+
pause() {
|
|
104
|
+
this.pty.pause();
|
|
105
|
+
}
|
|
106
|
+
resume() {
|
|
107
|
+
this.pty.resume();
|
|
108
|
+
}
|
|
109
|
+
interrupt() {
|
|
110
|
+
this.write('\x03');
|
|
111
|
+
}
|
|
112
|
+
activity(rows) {
|
|
113
|
+
return shellActivity(this.pid, this.prompt, rows);
|
|
114
|
+
}
|
|
115
|
+
async kill() {
|
|
116
|
+
clearTimeout(this.startupTimer);
|
|
117
|
+
await terminateProcesses(this.pid);
|
|
118
|
+
this.disposeData.dispose();
|
|
119
|
+
this.listeners.clear();
|
|
120
|
+
}
|
|
121
|
+
onData(listener) {
|
|
122
|
+
this.listeners.add(listener);
|
|
123
|
+
return () => this.listeners.delete(listener);
|
|
124
|
+
}
|
|
125
|
+
onExit(listener) {
|
|
126
|
+
const disposable = this.pty.onExit(listener);
|
|
127
|
+
return () => disposable.dispose();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export class PipeBackend {
|
|
131
|
+
mode = 'pipe';
|
|
132
|
+
prompt = new PromptTracker();
|
|
133
|
+
child;
|
|
134
|
+
dataListeners = new Set();
|
|
135
|
+
exitListeners = new Set();
|
|
136
|
+
constructor(cwd) {
|
|
137
|
+
// Merge both output descriptors so prompts cannot overtake stdout.
|
|
138
|
+
this.child = spawn('/bin/sh', ['-c', 'exec /bin/sh -i 2>&1'], {
|
|
139
|
+
cwd,
|
|
140
|
+
detached: true,
|
|
141
|
+
env: {
|
|
142
|
+
...process.env,
|
|
143
|
+
TERM: 'dumb',
|
|
144
|
+
FORCE_COLOR: '1',
|
|
145
|
+
CLICOLOR_FORCE: '1',
|
|
146
|
+
PS1: TERMINAL_PROMPT_MARKER + '$ '
|
|
147
|
+
},
|
|
148
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
149
|
+
});
|
|
150
|
+
this.child.stdout.setEncoding('utf8');
|
|
151
|
+
this.child.stdout.on('data', (data) => {
|
|
152
|
+
this.prompt.output(data);
|
|
153
|
+
for (const listener of this.dataListeners)
|
|
154
|
+
listener(data.replace(/\n/g, '\r\n'));
|
|
155
|
+
});
|
|
156
|
+
this.child.on('error', () => {
|
|
157
|
+
for (const listener of this.exitListeners)
|
|
158
|
+
listener();
|
|
159
|
+
});
|
|
160
|
+
this.child.on('exit', () => {
|
|
161
|
+
for (const listener of this.exitListeners)
|
|
162
|
+
listener();
|
|
163
|
+
});
|
|
164
|
+
this.child.stdin.on('error', () => { });
|
|
165
|
+
}
|
|
166
|
+
get pid() {
|
|
167
|
+
return this.child.pid ?? 0;
|
|
168
|
+
}
|
|
169
|
+
write(data) {
|
|
170
|
+
this.prompt.input();
|
|
171
|
+
this.child.stdin.write(data);
|
|
172
|
+
}
|
|
173
|
+
resize() { }
|
|
174
|
+
pause() {
|
|
175
|
+
this.child.stdout.pause();
|
|
176
|
+
}
|
|
177
|
+
resume() {
|
|
178
|
+
this.child.stdout.resume();
|
|
179
|
+
}
|
|
180
|
+
interrupt() {
|
|
181
|
+
if (this.pid > 1)
|
|
182
|
+
signalProcess(-this.pid, 'SIGINT');
|
|
183
|
+
}
|
|
184
|
+
activity(rows) {
|
|
185
|
+
return shellActivity(this.pid, this.prompt, rows);
|
|
186
|
+
}
|
|
187
|
+
async kill() {
|
|
188
|
+
if (this.pid > 1)
|
|
189
|
+
await terminateProcesses(this.pid);
|
|
190
|
+
}
|
|
191
|
+
onData(listener) {
|
|
192
|
+
this.dataListeners.add(listener);
|
|
193
|
+
return () => this.dataListeners.delete(listener);
|
|
194
|
+
}
|
|
195
|
+
onExit(listener) {
|
|
196
|
+
this.exitListeners.add(listener);
|
|
197
|
+
return () => this.exitListeners.delete(listener);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
export async function createBackend(cwd, cols, rows) {
|
|
201
|
+
if (!terminalSupported())
|
|
202
|
+
throw new TerminalError('The terminal is not available on Windows.');
|
|
203
|
+
const module = await loadPty();
|
|
204
|
+
if (!module)
|
|
205
|
+
return new PipeBackend(cwd);
|
|
206
|
+
const shell = process.env.SHELL || '/bin/sh';
|
|
207
|
+
const args = POSIX_SHELLS.includes(basename(shell)) ? ['-l', '-i'] : ['-i'];
|
|
208
|
+
const pty = module.spawn(shell, args, {
|
|
209
|
+
cwd,
|
|
210
|
+
cols,
|
|
211
|
+
rows,
|
|
212
|
+
name: 'xterm-256color',
|
|
213
|
+
env: { ...process.env, TERM: 'xterm-256color', COLORTERM: 'truecolor' }
|
|
214
|
+
});
|
|
215
|
+
return new NodePtyBackend(pty, basename(shell));
|
|
216
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { Value } from '@sinclair/typebox/value';
|
|
2
|
+
import { TerminalClientMessageSchema } from "../../../shared/terminal/schemas.js";
|
|
3
|
+
import { TERMINAL_CLIENT_BYTES } from "../../../shared/terminal/constants.js";
|
|
4
|
+
import { errorMessage } from "../../../shared/utils/error-message.js";
|
|
5
|
+
// Close the connection when a client queues more commands than it can need.
|
|
6
|
+
const MAX_PENDING_COMMANDS = 128;
|
|
7
|
+
// Disconnect a client that stays blocked for this long.
|
|
8
|
+
const SLOW_CLIENT_MS = 5000;
|
|
9
|
+
// Block the output of a session when a client holds this much unacknowledged data.
|
|
10
|
+
const CREDIT_PAUSE_BYTES = TERMINAL_CLIENT_BYTES / 16;
|
|
11
|
+
export class TerminalChannel {
|
|
12
|
+
subscribed = new Set();
|
|
13
|
+
subscribing = new Set();
|
|
14
|
+
credit = new Map();
|
|
15
|
+
unsubscribe;
|
|
16
|
+
stopped = false;
|
|
17
|
+
commands = Promise.resolve();
|
|
18
|
+
pendingCommands = 0;
|
|
19
|
+
registry;
|
|
20
|
+
send;
|
|
21
|
+
disconnect;
|
|
22
|
+
slowClientMs;
|
|
23
|
+
constructor(registry, send, disconnect, slowClientMs = SLOW_CLIENT_MS) {
|
|
24
|
+
this.registry = registry;
|
|
25
|
+
this.send = send;
|
|
26
|
+
this.disconnect = disconnect;
|
|
27
|
+
this.slowClientMs = slowClientMs;
|
|
28
|
+
this.unsubscribe = registry.subscribe(message => this.event(message));
|
|
29
|
+
send({ type: 'list', sessions: registry.list });
|
|
30
|
+
}
|
|
31
|
+
receive(value) {
|
|
32
|
+
if (!Value.Check(TerminalClientMessageSchema, value)) {
|
|
33
|
+
this.disconnect();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (++this.pendingCommands > MAX_PENDING_COMMANDS) {
|
|
37
|
+
this.disconnect();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this.commands = this.commands
|
|
41
|
+
.then(async () => {
|
|
42
|
+
if (this.stopped)
|
|
43
|
+
return undefined;
|
|
44
|
+
try {
|
|
45
|
+
switch (value.type) {
|
|
46
|
+
case 'create': {
|
|
47
|
+
const session = await this.registry.create(value.cols, value.rows);
|
|
48
|
+
if (!this.stopped)
|
|
49
|
+
this.send({
|
|
50
|
+
type: 'created',
|
|
51
|
+
requestId: value.requestId,
|
|
52
|
+
terminalId: session.id
|
|
53
|
+
});
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case 'subscribe':
|
|
57
|
+
await this.attach(value.terminalId);
|
|
58
|
+
break;
|
|
59
|
+
case 'snapshot':
|
|
60
|
+
await this.snapshot(value.terminalId);
|
|
61
|
+
break;
|
|
62
|
+
case 'input':
|
|
63
|
+
this.registry.get(value.terminalId).write(value.data);
|
|
64
|
+
break;
|
|
65
|
+
case 'line':
|
|
66
|
+
this.registry.get(value.terminalId).line(value.data);
|
|
67
|
+
break;
|
|
68
|
+
case 'signal':
|
|
69
|
+
this.registry.get(value.terminalId).signal(value.signal);
|
|
70
|
+
break;
|
|
71
|
+
case 'resize':
|
|
72
|
+
await this.registry.resize(value.terminalId, value.cols, value.rows);
|
|
73
|
+
break;
|
|
74
|
+
case 'close':
|
|
75
|
+
await this.registry.close(value.terminalId);
|
|
76
|
+
break;
|
|
77
|
+
case 'ack':
|
|
78
|
+
this.ack(value.terminalId, value.sequence);
|
|
79
|
+
break;
|
|
80
|
+
case 'ping':
|
|
81
|
+
this.send({ type: 'pong' });
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (!this.stopped)
|
|
87
|
+
this.send({
|
|
88
|
+
type: 'error',
|
|
89
|
+
...('requestId' in value ? { requestId: value.requestId } : {}),
|
|
90
|
+
code: 'GHT_TERMINAL',
|
|
91
|
+
message: errorMessage(error)
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
})
|
|
96
|
+
.finally(() => {
|
|
97
|
+
this.pendingCommands--;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async attach(id) {
|
|
101
|
+
if (this.subscribed.has(id) || this.subscribing.has(id))
|
|
102
|
+
return;
|
|
103
|
+
this.subscribing.add(id);
|
|
104
|
+
try {
|
|
105
|
+
this.registry.attach(id, this);
|
|
106
|
+
await this.snapshot(id);
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
this.subscribing.delete(id);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async snapshot(id) {
|
|
113
|
+
const session = this.registry.get(id);
|
|
114
|
+
await session.snapshot(message => {
|
|
115
|
+
if (this.stopped)
|
|
116
|
+
return undefined;
|
|
117
|
+
clearTimeout(this.credit.get(id)?.timeout);
|
|
118
|
+
session.block(this, false);
|
|
119
|
+
this.credit.set(id, {
|
|
120
|
+
bytes: 0,
|
|
121
|
+
frames: new Map(),
|
|
122
|
+
lastSequence: message.type === 'snapshot' ? message.sequence : 0
|
|
123
|
+
});
|
|
124
|
+
this.subscribed.add(id);
|
|
125
|
+
this.send(message);
|
|
126
|
+
return undefined;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
ack(id, sequence) {
|
|
130
|
+
const credit = this.credit.get(id);
|
|
131
|
+
if (!credit)
|
|
132
|
+
return;
|
|
133
|
+
if (sequence > credit.lastSequence) {
|
|
134
|
+
this.disconnect();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
for (const [key, bytes] of credit.frames)
|
|
138
|
+
if (key <= sequence) {
|
|
139
|
+
credit.bytes -= bytes;
|
|
140
|
+
credit.frames.delete(key);
|
|
141
|
+
}
|
|
142
|
+
this.flow(id, credit);
|
|
143
|
+
}
|
|
144
|
+
flow(id, credit) {
|
|
145
|
+
if (credit.bytes > CREDIT_PAUSE_BYTES) {
|
|
146
|
+
this.registry.get(id).block(this, true);
|
|
147
|
+
credit.timeout ??= setTimeout(() => this.disconnect(), this.slowClientMs);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
clearTimeout(credit.timeout);
|
|
151
|
+
credit.timeout = undefined;
|
|
152
|
+
this.registry.get(id).block(this, false);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
event(message) {
|
|
156
|
+
if (this.stopped)
|
|
157
|
+
return undefined;
|
|
158
|
+
if (message.type === 'output') {
|
|
159
|
+
if (!this.subscribed.has(message.terminalId))
|
|
160
|
+
return;
|
|
161
|
+
const credit = this.credit.get(message.terminalId);
|
|
162
|
+
if (!credit)
|
|
163
|
+
return;
|
|
164
|
+
const bytes = Buffer.byteLength(message.data);
|
|
165
|
+
credit.bytes += bytes;
|
|
166
|
+
credit.frames.set(message.sequence, bytes);
|
|
167
|
+
credit.lastSequence = message.sequence;
|
|
168
|
+
this.flow(message.terminalId, credit);
|
|
169
|
+
if (credit.bytes > TERMINAL_CLIENT_BYTES) {
|
|
170
|
+
this.disconnect();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else if (message.type === 'exit') {
|
|
175
|
+
clearTimeout(this.credit.get(message.terminalId)?.timeout);
|
|
176
|
+
this.subscribed.delete(message.terminalId);
|
|
177
|
+
this.credit.delete(message.terminalId);
|
|
178
|
+
}
|
|
179
|
+
this.send(message);
|
|
180
|
+
}
|
|
181
|
+
dispose() {
|
|
182
|
+
if (this.stopped)
|
|
183
|
+
return undefined;
|
|
184
|
+
this.stopped = true;
|
|
185
|
+
for (const [id, credit] of this.credit) {
|
|
186
|
+
clearTimeout(credit.timeout);
|
|
187
|
+
try {
|
|
188
|
+
this.registry.get(id).block(this, false);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
/* The session already exited. */
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
this.unsubscribe();
|
|
195
|
+
this.registry.detach(this);
|
|
196
|
+
this.subscribed.clear();
|
|
197
|
+
this.subscribing.clear();
|
|
198
|
+
this.credit.clear();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Drop an unfinished control string that is too long to keep for a snapshot.
|
|
2
|
+
const MAX_ESCAPE_TAIL = 8192;
|
|
3
|
+
export class EscapeTail {
|
|
4
|
+
// text: no sequence is open. escape: after ESC. csi: control sequence, ends with a final byte.
|
|
5
|
+
// string: OSC, DCS, PM or APC data, ends with BEL or ST. string-escape: after ESC inside a string.
|
|
6
|
+
state = 'text';
|
|
7
|
+
value = '';
|
|
8
|
+
get tail() {
|
|
9
|
+
return this.value;
|
|
10
|
+
}
|
|
11
|
+
push(data) {
|
|
12
|
+
for (const char of data) {
|
|
13
|
+
// 8-bit CSI (U+009B) starts a control sequence in any state.
|
|
14
|
+
if (char === '\u009b') {
|
|
15
|
+
this.state = 'csi';
|
|
16
|
+
this.value = char;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
// 8-bit OSC, DCS, SOS, PM and APC start a control string in any state.
|
|
20
|
+
if (['\u009d', '\u0090', '\u0098', '\u009e', '\u009f'].includes(char)) {
|
|
21
|
+
this.state = 'string';
|
|
22
|
+
this.value = char;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (this.state === 'text') {
|
|
26
|
+
// ESC opens a new sequence. Other text is not part of the tail.
|
|
27
|
+
if (char === '\x1b') {
|
|
28
|
+
this.state = 'escape';
|
|
29
|
+
this.value = char;
|
|
30
|
+
}
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
this.value += char;
|
|
34
|
+
// A long OSC, DCS or APC string must not close the shell.
|
|
35
|
+
if (this.value.length > MAX_ESCAPE_TAIL) {
|
|
36
|
+
this.reset();
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
// CAN and SUB cancel the open sequence.
|
|
40
|
+
if (char === '\x18' || char === '\x1a') {
|
|
41
|
+
this.reset();
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (this.state === 'escape') {
|
|
45
|
+
if (char === '[') {
|
|
46
|
+
// ESC [ starts a control sequence.
|
|
47
|
+
this.state = 'csi';
|
|
48
|
+
}
|
|
49
|
+
else if (']PX^_'.includes(char)) {
|
|
50
|
+
// ESC ] P X ^ _ start OSC, DCS, SOS, PM and APC strings.
|
|
51
|
+
this.state = 'string';
|
|
52
|
+
}
|
|
53
|
+
else if (char >= '0' && char <= '~') {
|
|
54
|
+
// A final byte ends a short escape sequence, for example ESC 7.
|
|
55
|
+
this.reset();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (this.state === 'csi') {
|
|
59
|
+
if (char >= '@' && char <= '~') {
|
|
60
|
+
// A final byte ends the control sequence.
|
|
61
|
+
this.reset();
|
|
62
|
+
}
|
|
63
|
+
else if (char === '\x1b') {
|
|
64
|
+
// ESC aborts the control sequence and opens a new one.
|
|
65
|
+
this.state = 'escape';
|
|
66
|
+
this.value = char;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if (this.state === 'string') {
|
|
70
|
+
if (char === '\x07' || char === '\u009c') {
|
|
71
|
+
// BEL or 8-bit ST ends the string.
|
|
72
|
+
this.reset();
|
|
73
|
+
}
|
|
74
|
+
else if (char === '\x1b') {
|
|
75
|
+
// ESC can start the two-character ST (ESC \).
|
|
76
|
+
this.state = 'string-escape';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else if (char === '\\') {
|
|
80
|
+
// ESC \ is ST: it ends the string.
|
|
81
|
+
this.reset();
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// ESC with any other character is part of the string data.
|
|
85
|
+
this.state = 'string';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
reset() {
|
|
90
|
+
this.state = 'text';
|
|
91
|
+
this.value = '';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
// Give processes time to react to SIGHUP before SIGKILL.
|
|
4
|
+
const TERMINATE_GRACE_MS = 200;
|
|
5
|
+
// A slow `ps` must not block the caller. The caller treats a timeout as unknown.
|
|
6
|
+
const PS_TIMEOUT_MS = 2000;
|
|
7
|
+
// Bound the memory for the `ps` output on a host with many processes.
|
|
8
|
+
const PS_MAX_BUFFER_BYTES = 4 * 1024 * 1024;
|
|
9
|
+
// PID 1 is init. PID 0 and -1 make kill() reach many processes. Never signal them.
|
|
10
|
+
const INIT_PID = 1;
|
|
11
|
+
// Share the Node callback adapter without starting a shell.
|
|
12
|
+
const execute = promisify(execFile);
|
|
13
|
+
export async function processes() {
|
|
14
|
+
const { stdout } = await execute('ps', ['-axo', 'pid=,ppid=,pgid='], {
|
|
15
|
+
timeout: PS_TIMEOUT_MS,
|
|
16
|
+
maxBuffer: PS_MAX_BUFFER_BYTES
|
|
17
|
+
});
|
|
18
|
+
return stdout
|
|
19
|
+
.trim()
|
|
20
|
+
.split('\n')
|
|
21
|
+
.map(line => {
|
|
22
|
+
const [pid, parent, group] = line.trim().split(/\s+/).map(Number);
|
|
23
|
+
return { pid, parent, group };
|
|
24
|
+
})
|
|
25
|
+
.filter(row => Number.isInteger(row.pid) &&
|
|
26
|
+
Number.isInteger(row.parent) &&
|
|
27
|
+
Number.isInteger(row.group) &&
|
|
28
|
+
row.pid > 0);
|
|
29
|
+
}
|
|
30
|
+
// Returns the process and its descendants, each parent before its children.
|
|
31
|
+
// One pass over a parent-to-children map keeps the cost linear.
|
|
32
|
+
export function descendants(rows, pid) {
|
|
33
|
+
const byPid = new Map();
|
|
34
|
+
const children = new Map();
|
|
35
|
+
for (const row of rows) {
|
|
36
|
+
byPid.set(row.pid, row);
|
|
37
|
+
const siblings = children.get(row.parent);
|
|
38
|
+
if (siblings) {
|
|
39
|
+
siblings.push(row.pid);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
children.set(row.parent, [row.pid]);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// The queue grows while the loop reads it: this is a breadth-first walk.
|
|
46
|
+
const order = [pid];
|
|
47
|
+
const seen = new Set(order);
|
|
48
|
+
for (const id of order) {
|
|
49
|
+
for (const child of children.get(id) ?? []) {
|
|
50
|
+
if (seen.has(child))
|
|
51
|
+
continue;
|
|
52
|
+
seen.add(child);
|
|
53
|
+
order.push(child);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return order.flatMap(id => byPid.get(id) ?? []);
|
|
57
|
+
}
|
|
58
|
+
export function classifyProcessActivity(rows, pid) {
|
|
59
|
+
if (!rows.some(row => row.pid === pid))
|
|
60
|
+
return 'unknown';
|
|
61
|
+
return descendants(rows, pid).some(row => row.pid !== pid)
|
|
62
|
+
? 'running'
|
|
63
|
+
: 'idle';
|
|
64
|
+
}
|
|
65
|
+
export async function processActivity(pid) {
|
|
66
|
+
try {
|
|
67
|
+
return classifyProcessActivity(await processes(), pid);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return 'unknown';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function signalProcess(pid, signal) {
|
|
74
|
+
// PID 0 and -1 would reach the server's own group or every process of the user.
|
|
75
|
+
if (!Number.isInteger(pid) || Math.abs(pid) <= INIT_PID)
|
|
76
|
+
return;
|
|
77
|
+
try {
|
|
78
|
+
process.kill(pid, signal);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
/* The process may have exited before the signal. */
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export async function terminateProcesses(pid) {
|
|
85
|
+
if (!Number.isInteger(pid) || pid <= INIT_PID)
|
|
86
|
+
return;
|
|
87
|
+
// One snapshot keeps the targets and the group owners consistent.
|
|
88
|
+
let all = [];
|
|
89
|
+
try {
|
|
90
|
+
all = await processes();
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
/* The backend still terminates its shell. */
|
|
94
|
+
}
|
|
95
|
+
const targets = descendants(all, pid);
|
|
96
|
+
const ids = new Set(targets.map(row => row.pid));
|
|
97
|
+
// Only signal groups that are wholly owned by this shell tree.
|
|
98
|
+
const groups = [
|
|
99
|
+
...new Set(targets.map(row => row.group).filter(group => group > INIT_PID))
|
|
100
|
+
].filter(group => all.filter(row => row.group === group).every(row => ids.has(row.pid)));
|
|
101
|
+
// oxlint-disable-next-line unicorn/no-array-reverse -- the type-aware checker lacks toReversed in this module
|
|
102
|
+
const childrenFirst = targets.slice().reverse();
|
|
103
|
+
const signalAll = (signal) => {
|
|
104
|
+
// A group also reaches a child that started after the snapshot.
|
|
105
|
+
for (const group of groups)
|
|
106
|
+
signalProcess(-group, signal);
|
|
107
|
+
for (const row of childrenFirst)
|
|
108
|
+
signalProcess(row.pid, signal);
|
|
109
|
+
signalProcess(pid, signal);
|
|
110
|
+
};
|
|
111
|
+
signalAll('SIGHUP');
|
|
112
|
+
await new Promise(resolve => setTimeout(resolve, TERMINATE_GRACE_MS));
|
|
113
|
+
// A PID can be reused in the grace time. The risk is accepted: PIDs wrap slowly.
|
|
114
|
+
signalAll('SIGKILL');
|
|
115
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// OSC 133 marks a prompt without adding visible characters.
|
|
2
|
+
export const TERMINAL_PROMPT_MARKER = '\x1b]133;A\x07';
|
|
3
|
+
export class PromptTracker {
|
|
4
|
+
idle = false;
|
|
5
|
+
tail = '';
|
|
6
|
+
input() {
|
|
7
|
+
this.idle = false;
|
|
8
|
+
}
|
|
9
|
+
output(data) {
|
|
10
|
+
const combined = this.tail + data;
|
|
11
|
+
if (combined.includes(TERMINAL_PROMPT_MARKER))
|
|
12
|
+
this.idle = true;
|
|
13
|
+
this.tail = combined.slice(-(TERMINAL_PROMPT_MARKER.length - 1));
|
|
14
|
+
}
|
|
15
|
+
}
|