@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
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ staged/unstaged diff, сравнение веток и коммитов, бра
|
|
|
20
20
|
файлов в репозитории — удобно смотреть, как редактирует код AI-агент.
|
|
21
21
|
После перезапуска `serve` уже открытые вкладки перезагружаются сами.
|
|
22
22
|
- Чаты с ACP-агентами (Claude Code, Cursor, Codex, pi): до 5 чатов во
|
|
23
|
-
вкладках, контекст — diff, файл целиком, ревью, вложения и `@`-файлы из
|
|
23
|
+
вкладках, контекст — diff, файл целиком, ревью, вложения и `@`-файлы и папки из
|
|
24
24
|
репозитория. Подробнее — в разделе «Агенты» ниже.
|
|
25
25
|
- Ручной/авто выбор тёмной-светлой темы.
|
|
26
26
|
- CLI: `serve` (поднять сервер), `list`/`export` (быстрый доступ к
|
|
@@ -77,7 +77,13 @@ node dist/cli/index.js export last --format markdown -o review.md
|
|
|
77
77
|
репозитория; нужные адаптеры подтягиваются через `npx` при первом запуске.
|
|
78
78
|
|
|
79
79
|
- **Контекст.** «Add to chat» в меню файла и diff, кнопка у активного
|
|
80
|
-
ревью; в поле ввода `@` + часть имени + Tab вставляет файл из репозитория.
|
|
80
|
+
ревью; в поле ввода `@` + часть имени + Tab вставляет файл или папку из репозитория.
|
|
81
|
+
Кнопка микрофона в поле ввода — голосовой ввод: держите её и говорите,
|
|
82
|
+
после отпускания текст попадает в поле (отправка — как обычно). Работает
|
|
83
|
+
через Web Speech API браузера: в Chrome и Edge звук обрабатывает облачный
|
|
84
|
+
сервис браузера. В Firefox кнопки нет, как и на странице по обычному
|
|
85
|
+
`http` с другой машины (`--host 0.0.0.0`): микрофону нужен защищённый
|
|
86
|
+
контекст (`localhost` или `https`). Язык — в строке настроек чата.
|
|
81
87
|
Shift+Tab переключает режим агента, модель и режим выбираются под полем.
|
|
82
88
|
- **Подтверждения.** Запросы разрешений приходят карточкой в чат. Для
|
|
83
89
|
отдельного чата можно включить автоподтверждение (кнопка-щит) — агент
|
|
@@ -171,3 +177,13 @@ pnpm run build # server (tsc) + web (quasar build) → dist/
|
|
|
171
177
|
## Лицензия
|
|
172
178
|
|
|
173
179
|
MIT, см. [LICENSE](LICENSE).
|
|
180
|
+
|
|
181
|
+
## Встроенный терминал
|
|
182
|
+
|
|
183
|
+
При запуске на loopback кнопка **Terminal** в шапке и меню открывает терминал в корне репозитория. ``Ctrl+Shift+` `` открывает или сворачивает окно. Окно можно перетаскивать и сворачивать; «+» добавляет вкладку, до восьми терминалов. Закрытие вкладки требует подтверждения и останавливает shell с его командами. Команда `exit` закрывает вкладку сразу.
|
|
184
|
+
|
|
185
|
+
Сессии переживают обновление страницы и доступны из нескольких страниц браузера. История ограничена 5000 строками scrollback; после остановки сервера она теряется. Без подписчиков простаивающая сессия завершается через 15 минут, работающая или с неизвестным состоянием — не позднее часа.
|
|
186
|
+
|
|
187
|
+
Если платформенный бинарник PTY недоступен, терминал работает в ограниченном режиме с баннером: команды вводятся в поле под выводом, Ctrl+C прерывает команду, Ctrl+D закрывает shell. `vim`, `less` и другие интерактивные программы требуют PTY. В LAN терминал отключён. Переключатель исходных цветов отключает минимальный контраст для программ со своей палитрой.
|
|
188
|
+
|
|
189
|
+
Для разработки на другом порту backend можно передать `GITHUMAN_DEV_API=http://localhost:3848` при запуске Quasar dev-сервера.
|
package/dist/cli/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { errorMessage } from "../shared/utils/error-message.js";
|
|
2
3
|
import { writeFileSync } from 'node:fs';
|
|
3
4
|
import { parseServeArgs } from "./config.js";
|
|
4
5
|
import { startServer, resolveRepositoryPath, resolveReviewsDbPath } from "./server-runtime.js";
|
|
@@ -7,6 +8,8 @@ import { parseListArgs, runList } from "./commands/list.js";
|
|
|
7
8
|
import { parseExportArgs, runExport } from "./commands/export.js";
|
|
8
9
|
import { createFileDatabase } from "../server/db/index.js";
|
|
9
10
|
import { getAppVersion } from "../server/app-version.js";
|
|
11
|
+
// Force exit when a graceful close hangs. It must exceed the terminal kill time: ps timeout plus grace.
|
|
12
|
+
const SHUTDOWN_TIMEOUT_MS = 5000;
|
|
10
13
|
/** Opens the reviews DB for the repo at cwd, shared by `list` and `export`. */
|
|
11
14
|
async function openReviewsDb(dbPrefix) {
|
|
12
15
|
const repositoryPath = await resolveRepositoryPath(process.cwd());
|
|
@@ -21,7 +24,29 @@ async function main(argv) {
|
|
|
21
24
|
switch (command) {
|
|
22
25
|
case 'serve': {
|
|
23
26
|
const options = parseServeArgs(rest);
|
|
24
|
-
const { url } = await startServer(options);
|
|
27
|
+
const { app, url } = await startServer(options);
|
|
28
|
+
let stopping = false;
|
|
29
|
+
const shutdown = () => {
|
|
30
|
+
if (stopping)
|
|
31
|
+
return;
|
|
32
|
+
stopping = true;
|
|
33
|
+
// Do not wait for a stuck connection forever. Unref lets a clean close end the process by itself.
|
|
34
|
+
const deadline = setTimeout(() => process.exit(1), SHUTDOWN_TIMEOUT_MS);
|
|
35
|
+
deadline.unref();
|
|
36
|
+
void app
|
|
37
|
+
.close()
|
|
38
|
+
.then(() => {
|
|
39
|
+
clearTimeout(deadline);
|
|
40
|
+
process.exitCode = 0;
|
|
41
|
+
return undefined;
|
|
42
|
+
})
|
|
43
|
+
.catch(error => {
|
|
44
|
+
console.error(errorMessage(error));
|
|
45
|
+
process.exitCode = 1;
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
process.once('SIGINT', shutdown);
|
|
49
|
+
process.once('SIGTERM', shutdown);
|
|
25
50
|
console.log(formatStartupMessage(url, options.host));
|
|
26
51
|
return;
|
|
27
52
|
}
|
|
@@ -50,6 +75,6 @@ async function main(argv) {
|
|
|
50
75
|
}
|
|
51
76
|
}
|
|
52
77
|
main(process.argv.slice(2)).catch((error) => {
|
|
53
|
-
console.error(error
|
|
78
|
+
console.error(errorMessage(error));
|
|
54
79
|
process.exitCode = 1;
|
|
55
80
|
});
|
|
@@ -64,6 +64,7 @@ export async function startServer(options) {
|
|
|
64
64
|
db,
|
|
65
65
|
watchFiles: true,
|
|
66
66
|
apiDocs: options.apiDocs === true,
|
|
67
|
+
terminalEnabled: isLoopbackHost(options.host),
|
|
67
68
|
// Agents can run commands, so they are only offered on a loopback bind.
|
|
68
69
|
...(isLoopbackHost(options.host)
|
|
69
70
|
? {
|
package/dist/server/app.js
CHANGED
|
@@ -4,6 +4,10 @@ import { readFileSync } from 'node:fs';
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import Fastify from 'fastify';
|
|
6
6
|
import fastifyStatic from '@fastify/static';
|
|
7
|
+
import fastifyWebsocket from '@fastify/websocket';
|
|
8
|
+
import { terminalRoutes, TERMINAL_WEBSOCKET_OPTIONS } from "./routes/terminal.js";
|
|
9
|
+
import { TerminalRegistry } from "./services/terminal/registry.js";
|
|
10
|
+
import { loadPty, terminalSupported } from "./services/terminal/backend.js";
|
|
7
11
|
import fastifySwagger from '@fastify/swagger';
|
|
8
12
|
import fastifySwaggerUi from '@fastify/swagger-ui';
|
|
9
13
|
// @fastify/sse's published types declare an ESM default export that doesn't match
|
|
@@ -32,6 +36,10 @@ export function buildApp(options = {}) {
|
|
|
32
36
|
const repositoryPath = options.repositoryPath ?? process.cwd();
|
|
33
37
|
const db = options.db ?? createTestDatabase();
|
|
34
38
|
const eventBus = options.eventBus ?? createEventBus();
|
|
39
|
+
// Windows includes a PTY binary, but the shell and process list are Unix-only.
|
|
40
|
+
const terminalEnabled = options.terminalEnabled === true && terminalSupported();
|
|
41
|
+
if (terminalEnabled)
|
|
42
|
+
app.register(fastifyWebsocket, TERMINAL_WEBSOCKET_OPTIONS);
|
|
35
43
|
app.register(fastifySse);
|
|
36
44
|
// The spec collects routes as they are added, so it comes before them.
|
|
37
45
|
app.register(fastifySwagger, openApiOptions());
|
|
@@ -40,7 +48,12 @@ export function buildApp(options = {}) {
|
|
|
40
48
|
}
|
|
41
49
|
app.register(openApiRoutes);
|
|
42
50
|
app.register(healthRoutes);
|
|
43
|
-
app.register(appInfoRoutes
|
|
51
|
+
app.register(appInfoRoutes, {
|
|
52
|
+
terminalCapability: async () => ({
|
|
53
|
+
available: terminalEnabled,
|
|
54
|
+
mode: terminalEnabled ? ((await loadPty()) ? 'pty' : 'pipe') : null
|
|
55
|
+
})
|
|
56
|
+
});
|
|
44
57
|
app.register(diffRoutes, { repositoryPath });
|
|
45
58
|
app.register(gitRoutes, { repositoryPath });
|
|
46
59
|
app.register(reviewRoutes, { repositoryPath, db, eventBus });
|
|
@@ -50,6 +63,14 @@ export function buildApp(options = {}) {
|
|
|
50
63
|
eventBus,
|
|
51
64
|
instanceId: options.instanceId ?? randomUUID()
|
|
52
65
|
});
|
|
66
|
+
if (terminalEnabled)
|
|
67
|
+
app.register(terminalRoutes, {
|
|
68
|
+
registry: options.terminalRegistry ??
|
|
69
|
+
new TerminalRegistry({
|
|
70
|
+
repositoryPath,
|
|
71
|
+
onLifecycle: event => app.log.info({ terminal: event }, 'Terminal lifecycle')
|
|
72
|
+
})
|
|
73
|
+
});
|
|
53
74
|
if (options.agentPresets) {
|
|
54
75
|
app.register(agentRoutes, {
|
|
55
76
|
registry: createSessionRegistry({
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import createError from '@fastify/error';
|
|
2
|
+
export const TerminalError = createError('GHT_TERMINAL', '%s', 409);
|
|
3
|
+
export const TerminalNotFoundError = createError('GHT_TERMINAL_NOT_FOUND', '%s', 404);
|
|
4
|
+
export const TerminalLimitError = createError('GHT_TERMINAL_LIMIT', '%s', 409);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { isLoopbackHost } from "../../../shared/network/loopback.js";
|
|
2
|
+
import { ForbiddenError } from "../../errors/http.js";
|
|
3
|
+
// Returns false on any malformed value: an invalid URL means an invalid origin.
|
|
4
|
+
function isTerminalOrigin(host, origin) {
|
|
5
|
+
if (!host || !origin)
|
|
6
|
+
return false;
|
|
7
|
+
try {
|
|
8
|
+
const source = new URL(origin);
|
|
9
|
+
const target = new URL(`http://${host}`);
|
|
10
|
+
return (isLoopbackHost(target.hostname) &&
|
|
11
|
+
target.host === host &&
|
|
12
|
+
source.host === target.host &&
|
|
13
|
+
source.protocol === 'http:' &&
|
|
14
|
+
source.origin === origin &&
|
|
15
|
+
!target.username &&
|
|
16
|
+
!target.password);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export async function requireTerminalOrigin(request) {
|
|
23
|
+
if (!isTerminalOrigin(request.headers.host, request.headers.origin))
|
|
24
|
+
throw new ForbiddenError('Terminal endpoints only accept requests from this local server. Open the app via localhost.');
|
|
25
|
+
}
|
|
@@ -198,7 +198,7 @@ export async function agentRoutes(app, opts) {
|
|
|
198
198
|
typedApp.get('/api/agent/files', {
|
|
199
199
|
schema: {
|
|
200
200
|
tags: TAGS,
|
|
201
|
-
summary: 'Find repository files for the context picker',
|
|
201
|
+
summary: 'Find repository files and directories for the context picker',
|
|
202
202
|
querystring: FileSearchQuery,
|
|
203
203
|
response: {
|
|
204
204
|
200: AgentFileSearchResponseSchema,
|
|
@@ -206,7 +206,7 @@ export async function agentRoutes(app, opts) {
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
}, async (request) => ({
|
|
209
|
-
|
|
209
|
+
paths: await fileIndex.search(request.query.q ?? '', request.query.limit ?? DEFAULT_FILE_SEARCH_LIMIT)
|
|
210
210
|
}));
|
|
211
211
|
// One stream for every chat (ADR 0024): browsers allow ~6 connections per
|
|
212
212
|
// origin over HTTP/1.1, so a stream per session would starve the page's own
|
|
@@ -257,6 +257,12 @@ export async function agentRoutes(app, opts) {
|
|
|
257
257
|
safeSend(reply.sse, { event: 'sessions', data: {} });
|
|
258
258
|
}
|
|
259
259
|
}, Number.isInteger(lastEventId) ? lastEventId : 0);
|
|
260
|
+
// The replay can close a stream whose queue is full. `onClose` callbacks
|
|
261
|
+
// have run then, so the listener is removed here.
|
|
262
|
+
if (!reply.sse.isConnected) {
|
|
263
|
+
unsubscribe();
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
260
266
|
reply.sse.onClose(unsubscribe);
|
|
261
267
|
});
|
|
262
268
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getAppVersion } from "../app-version.js";
|
|
2
2
|
import { AppInfoSchema } from "../../shared/app/schemas.js";
|
|
3
|
-
export async function appInfoRoutes(app) {
|
|
3
|
+
export async function appInfoRoutes(app, options) {
|
|
4
4
|
app.withTypeProvider().get('/api/app-info', {
|
|
5
5
|
schema: {
|
|
6
6
|
tags: ['app'],
|
|
@@ -8,6 +8,9 @@ export async function appInfoRoutes(app) {
|
|
|
8
8
|
response: { 200: AppInfoSchema }
|
|
9
9
|
}
|
|
10
10
|
}, async () => {
|
|
11
|
-
return {
|
|
11
|
+
return {
|
|
12
|
+
version: getAppVersion(),
|
|
13
|
+
terminal: await options.terminalCapability()
|
|
14
|
+
};
|
|
12
15
|
});
|
|
13
16
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BadRequestError } from "../errors/http.js";
|
|
2
|
+
import { GitFileNotFoundError } from "../errors/git.js";
|
|
2
3
|
import { getRepositoryInfo, stagePaths, unstagePaths, discardPaths, getFilesAtRef, getFileAtRef } from "../services/git.service.js";
|
|
3
4
|
import { DiscardBody, FileContentQuery, FileContentResponseSchema, FileParams, FileTreeResponseSchema, OkSchema, OptionalPathsBody, RepositoryInfoSchema, TreeParams } from "../../shared/git/schemas.js";
|
|
4
5
|
import { ERROR_RESPONSES } from "../../shared/http/schemas.js";
|
|
@@ -95,7 +96,7 @@ export async function gitRoutes(app, opts) {
|
|
|
95
96
|
}
|
|
96
97
|
}, async (request) => {
|
|
97
98
|
const filePath = request.params['*'];
|
|
98
|
-
const { ref } = request.query;
|
|
99
|
+
const { ref, strict } = request.query;
|
|
99
100
|
if (!filePath) {
|
|
100
101
|
throw new BadRequestError('File path is required');
|
|
101
102
|
}
|
|
@@ -103,9 +104,11 @@ export async function gitRoutes(app, opts) {
|
|
|
103
104
|
let isBinary;
|
|
104
105
|
try {
|
|
105
106
|
;
|
|
106
|
-
({ content, isBinary } = await getFileAtRef(repositoryPath, ref, filePath));
|
|
107
|
+
({ content, isBinary } = await getFileAtRef(repositoryPath, ref, filePath, strict));
|
|
107
108
|
}
|
|
108
109
|
catch (err) {
|
|
110
|
+
if (err instanceof GitFileNotFoundError)
|
|
111
|
+
throw err;
|
|
109
112
|
const message = err instanceof Error ? err.message : 'Failed to read file';
|
|
110
113
|
throw new BadRequestError(message, { cause: err });
|
|
111
114
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Type } from '@sinclair/typebox';
|
|
2
|
+
import { TerminalTokenSchema } from "../../shared/terminal/schemas.js";
|
|
3
|
+
import { TERMINAL_FRAME_BYTES } from "../../shared/terminal/constants.js";
|
|
4
|
+
import { ForbiddenError } from "../errors/http.js";
|
|
5
|
+
import { requireTerminalOrigin } from "../hooks/terminal/local-origin.js";
|
|
6
|
+
import { TerminalTokens } from "../services/terminal/tokens.js";
|
|
7
|
+
import { attachTerminalSocket } from "../services/terminal/socket.js";
|
|
8
|
+
// Reject oversized values early. A real token has 43 characters.
|
|
9
|
+
const MAX_TOKEN_LENGTH = 64;
|
|
10
|
+
// Leave room for the JSON envelope around an input frame.
|
|
11
|
+
const MAX_PAYLOAD_BYTES = TERMINAL_FRAME_BYTES * 2;
|
|
12
|
+
// Apply the same frame limit to every terminal upgrade.
|
|
13
|
+
export const TERMINAL_WEBSOCKET_OPTIONS = {
|
|
14
|
+
options: { maxPayload: MAX_PAYLOAD_BYTES }
|
|
15
|
+
};
|
|
16
|
+
export async function terminalRoutes(app, options) {
|
|
17
|
+
const tokens = new TerminalTokens();
|
|
18
|
+
const connections = new Set();
|
|
19
|
+
app.addHook('onError', async (_request, _reply, error) => {
|
|
20
|
+
// Request URLs contain credentials, so log only the rejection code.
|
|
21
|
+
app.log.warn({ code: error.code }, 'Terminal request rejected');
|
|
22
|
+
});
|
|
23
|
+
const api = app.withTypeProvider();
|
|
24
|
+
api.post('/api/terminal/token', {
|
|
25
|
+
onRequest: requireTerminalOrigin,
|
|
26
|
+
schema: {
|
|
27
|
+
tags: ['terminal'],
|
|
28
|
+
summary: 'Issue a terminal connection token',
|
|
29
|
+
response: { 200: TerminalTokenSchema }
|
|
30
|
+
}
|
|
31
|
+
}, async (request) => ({ token: tokens.issue(request.headers.origin ?? '') }));
|
|
32
|
+
api.get('/api/terminal/ws', {
|
|
33
|
+
websocket: true,
|
|
34
|
+
schema: {
|
|
35
|
+
hide: true,
|
|
36
|
+
querystring: Type.Object({
|
|
37
|
+
token: Type.String({
|
|
38
|
+
maxLength: MAX_TOKEN_LENGTH,
|
|
39
|
+
description: 'One-use connection credential.'
|
|
40
|
+
})
|
|
41
|
+
}, {
|
|
42
|
+
additionalProperties: false,
|
|
43
|
+
description: 'Terminal upgrade credential.'
|
|
44
|
+
})
|
|
45
|
+
},
|
|
46
|
+
onRequest: requireTerminalOrigin,
|
|
47
|
+
preValidation: async (request) => {
|
|
48
|
+
if (!tokens.consume(request.query.token, request.headers.origin ?? ''))
|
|
49
|
+
throw new ForbiddenError('Terminal connection token is invalid or expired.');
|
|
50
|
+
}
|
|
51
|
+
}, socket => {
|
|
52
|
+
const connection = attachTerminalSocket(socket, options.registry, {
|
|
53
|
+
onDisconnect: () => connections.delete(connection)
|
|
54
|
+
});
|
|
55
|
+
connections.add(connection);
|
|
56
|
+
});
|
|
57
|
+
app.addHook('onClose', async () => {
|
|
58
|
+
for (const connection of connections)
|
|
59
|
+
connection.close();
|
|
60
|
+
tokens.clear();
|
|
61
|
+
await options.registry.dispose();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
@@ -11,7 +11,8 @@ import * as acp from '@agentclientprotocol/sdk';
|
|
|
11
11
|
import { mapConfigOptions, mapPermissionRequest, mapSessionUpdate } from "../../adapters/agents/event-mapper.js";
|
|
12
12
|
/** `npx` may have to download the adapter first. */
|
|
13
13
|
const START_TIMEOUT_MS = 90_000;
|
|
14
|
-
|
|
14
|
+
/** Events that one session keeps for a client that joins late or reconnects. */
|
|
15
|
+
export const MAX_BUFFERED_EVENTS = 5_000;
|
|
15
16
|
const STDERR_TAIL_CHARS = 2_000;
|
|
16
17
|
/** How long a closed connection waits for the `exit` event that carries the reason. */
|
|
17
18
|
const EXIT_GRACE_MS = 500;
|
|
@@ -2,10 +2,11 @@ import { MAX_ATTACHMENT_BYTES } from "../../../shared/agents/constants.js";
|
|
|
2
2
|
import { AgentContextError } from "../../errors/agents.js";
|
|
3
3
|
import { errorMessage } from "../../../shared/utils/error-message.js";
|
|
4
4
|
import { formatDiffFile } from "../../../shared/agents/context-format.js";
|
|
5
|
+
import { withoutTrailingSlash } from "../../../shared/agents/mention-paths.js";
|
|
5
6
|
import { randomUUID } from 'node:crypto';
|
|
6
7
|
import { pathToFileURL } from 'node:url';
|
|
7
|
-
import { mkdir, stat, writeFile } from 'node:fs/promises';
|
|
8
|
-
import { join } from 'node:path';
|
|
8
|
+
import { mkdir, realpath, stat, writeFile } from 'node:fs/promises';
|
|
9
|
+
import { join, relative, resolve, sep } from 'node:path';
|
|
9
10
|
import { getStagedDiff, getUnstagedDiff } from "../diff.service.js";
|
|
10
11
|
import { exportAsMarkdown } from "../export.service.js";
|
|
11
12
|
import { resolveWithinRepo } from "../git.service.js";
|
|
@@ -57,6 +58,19 @@ async function buildDiffItem(item, deps) {
|
|
|
57
58
|
}
|
|
58
59
|
return embed(deps, `githuman://diff/${item.source}/${item.path ?? ''}`, `Diff (${item.source}${item.path === undefined ? '' : `, ${item.path}`}):`, 'text/x-diff', selected.map(formatDiffFile).join('\n'));
|
|
59
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* `resolveWithinRepo` checks the path as text, but `stat` follows a symbolic
|
|
63
|
+
* link. A link that points out of the repository must not reach the agent.
|
|
64
|
+
*/
|
|
65
|
+
async function assertRealPathInRepo(repositoryPath, absolute, path) {
|
|
66
|
+
const [root, target] = await Promise.all([
|
|
67
|
+
realpath(repositoryPath),
|
|
68
|
+
realpath(absolute)
|
|
69
|
+
]);
|
|
70
|
+
if (target !== root && !target.startsWith(root + sep)) {
|
|
71
|
+
throw new AgentContextError(`"${path}" points out of the repository`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
60
74
|
async function buildFileItem(item, deps) {
|
|
61
75
|
let absolute;
|
|
62
76
|
try {
|
|
@@ -69,12 +83,42 @@ async function buildFileItem(item, deps) {
|
|
|
69
83
|
if (!info?.isFile()) {
|
|
70
84
|
throw new AgentContextError(`"${item.path}" is not a file in the repository`);
|
|
71
85
|
}
|
|
86
|
+
await assertRealPathInRepo(deps.repositoryPath, absolute, item.path);
|
|
72
87
|
return {
|
|
73
88
|
type: 'resource_link',
|
|
74
89
|
uri: pathToFileURL(absolute).href,
|
|
75
90
|
name: item.path
|
|
76
91
|
};
|
|
77
92
|
}
|
|
93
|
+
async function buildDirectoryItem(item, deps) {
|
|
94
|
+
const path = withoutTrailingSlash(item.path);
|
|
95
|
+
let absolute;
|
|
96
|
+
try {
|
|
97
|
+
absolute = resolveWithinRepo(deps.repositoryPath, path);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
throw asContextError(error);
|
|
101
|
+
}
|
|
102
|
+
// The agent runs in the repository root already, so a link to it says nothing.
|
|
103
|
+
if (absolute === resolve(deps.repositoryPath)) {
|
|
104
|
+
throw new AgentContextError('The repository root cannot be a context item');
|
|
105
|
+
}
|
|
106
|
+
const info = await stat(absolute).catch(() => null);
|
|
107
|
+
if (!info?.isDirectory()) {
|
|
108
|
+
throw new AgentContextError(`"${path}" is not a directory in the repository`);
|
|
109
|
+
}
|
|
110
|
+
await assertRealPathInRepo(deps.repositoryPath, absolute, path);
|
|
111
|
+
// ACP has no type for a directory link. The trailing `/` in the URI and the
|
|
112
|
+
// name is the only sign for the agent that the link names a directory.
|
|
113
|
+
const name = relative(resolve(deps.repositoryPath), absolute)
|
|
114
|
+
.split(sep)
|
|
115
|
+
.join('/');
|
|
116
|
+
return {
|
|
117
|
+
type: 'resource_link',
|
|
118
|
+
uri: pathToFileURL(absolute + sep).href,
|
|
119
|
+
name: `${name}/`
|
|
120
|
+
};
|
|
121
|
+
}
|
|
78
122
|
async function buildReviewItem(item, deps) {
|
|
79
123
|
let markdown;
|
|
80
124
|
try {
|
|
@@ -125,6 +169,9 @@ function buildItem(item, deps) {
|
|
|
125
169
|
if (item.kind === 'file') {
|
|
126
170
|
return buildFileItem(item, deps);
|
|
127
171
|
}
|
|
172
|
+
if (item.kind === 'directory') {
|
|
173
|
+
return buildDirectoryItem(item, deps);
|
|
174
|
+
}
|
|
128
175
|
if (item.kind === 'attachment') {
|
|
129
176
|
return buildAttachmentItem(item, deps);
|
|
130
177
|
}
|
|
@@ -2,9 +2,10 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { getFilesAtRef } from "../git.service.js";
|
|
4
4
|
import { DEFAULT_FILE_SEARCH_LIMIT } from "../../../shared/agents/constants.js";
|
|
5
|
-
import {
|
|
5
|
+
import { rankPaths } from "../../../shared/agents/file-search.js";
|
|
6
|
+
import { withDirectories } from "../../../shared/agents/mention-paths.js";
|
|
6
7
|
/**
|
|
7
|
-
* The repository's file list for `@` mentions. Read once from `git ls-files`
|
|
8
|
+
* The repository's file and directory list for `@` mentions. Read once from `git ls-files`
|
|
8
9
|
* and filtered in memory — the query never reaches a git argument — and
|
|
9
10
|
* dropped whenever the working tree changes.
|
|
10
11
|
*/
|
|
@@ -26,6 +27,7 @@ export function createFileIndex(repositoryPath, eventBus) {
|
|
|
26
27
|
if (!loading) {
|
|
27
28
|
const started = generation;
|
|
28
29
|
loading = getFilesAtRef(repositoryPath, 'WORKTREE')
|
|
30
|
+
.then(withDirectories)
|
|
29
31
|
.then(list => {
|
|
30
32
|
if (started === generation) {
|
|
31
33
|
cached = list;
|
|
@@ -40,10 +42,11 @@ export function createFileIndex(repositoryPath, eventBus) {
|
|
|
40
42
|
}
|
|
41
43
|
return {
|
|
42
44
|
async search(query, limit = DEFAULT_FILE_SEARCH_LIMIT) {
|
|
43
|
-
const ranked =
|
|
45
|
+
const ranked = rankPaths(await files(), query);
|
|
44
46
|
const found = [];
|
|
45
47
|
for (const path of ranked) {
|
|
46
|
-
// `ls-files --cached` still lists files deleted from the working tree
|
|
48
|
+
// `ls-files --cached` still lists files deleted from the working tree,
|
|
49
|
+
// so a directory can also be gone.
|
|
47
50
|
if (existsSync(join(repositoryPath, path))) {
|
|
48
51
|
found.push(path);
|
|
49
52
|
if (found.length >= limit) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { execFile, spawn } from 'node:child_process';
|
|
2
2
|
import { promisify } from 'node:util';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
|
-
import { basename, resolve, sep } from 'node:path';
|
|
4
|
+
import { basename, posix, resolve, sep } from 'node:path';
|
|
5
5
|
import { simpleGit } from 'simple-git';
|
|
6
|
+
import { GitFileNotFoundError } from "../errors/git.js";
|
|
6
7
|
import { isBinaryBuffer } from "../utils/text.js";
|
|
7
8
|
const execFileAsync = promisify(execFile);
|
|
8
9
|
/**
|
|
@@ -48,6 +49,31 @@ function objectSpecFor(ref, path) {
|
|
|
48
49
|
// the injection this would otherwise guard against.
|
|
49
50
|
return ref === 'INDEX' ? `:${path}` : `${ref}:${path}`;
|
|
50
51
|
}
|
|
52
|
+
/** `ENOENT` and `ENOTDIR` both mean that the file does not exist. */
|
|
53
|
+
function isMissingPathError(error) {
|
|
54
|
+
return (!!error &&
|
|
55
|
+
typeof error === 'object' &&
|
|
56
|
+
'code' in error &&
|
|
57
|
+
(error.code === 'ENOENT' || error.code === 'ENOTDIR'));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Checks one path with `ls-tree` instead of listing the whole tree. A failing
|
|
61
|
+
* `ls-tree` means the ref is invalid, so the path is reported as present and
|
|
62
|
+
* the caller keeps the original error.
|
|
63
|
+
*/
|
|
64
|
+
async function pathExistsAtRef(repoPath, ref, path) {
|
|
65
|
+
const normalized = posix.normalize(path);
|
|
66
|
+
try {
|
|
67
|
+
const args = ref === 'INDEX'
|
|
68
|
+
? ['ls-files', '-z', '--', `:(literal)${normalized}`]
|
|
69
|
+
: ['ls-tree', '-z', ref, '--', normalized];
|
|
70
|
+
const { stdout } = await execFileAsync('git', args, { cwd: repoPath });
|
|
71
|
+
return stdout.length > 0;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
51
77
|
/**
|
|
52
78
|
* Reads a file's content at a given git ref.
|
|
53
79
|
*
|
|
@@ -58,7 +84,7 @@ function objectSpecFor(ref, path) {
|
|
|
58
84
|
* HEAD version), returns empty content rather than throwing — the caller
|
|
59
85
|
* treats this as "the file did not exist on this side of the diff".
|
|
60
86
|
*/
|
|
61
|
-
export async function getFileAtRef(repoPath, ref, path) {
|
|
87
|
+
export async function getFileAtRef(repoPath, ref, path, strict = false) {
|
|
62
88
|
if (ref === 'WORKTREE') {
|
|
63
89
|
try {
|
|
64
90
|
const absolutePath = resolveWithinRepo(repoPath, path);
|
|
@@ -66,7 +92,14 @@ export async function getFileAtRef(repoPath, ref, path) {
|
|
|
66
92
|
const isBinary = isBinaryBuffer(buffer);
|
|
67
93
|
return { content: isBinary ? '' : buffer.toString('utf-8'), isBinary };
|
|
68
94
|
}
|
|
69
|
-
catch {
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (strict) {
|
|
97
|
+
if (isMissingPathError(error))
|
|
98
|
+
throw new GitFileNotFoundError('File was deleted or moved', {
|
|
99
|
+
cause: error
|
|
100
|
+
});
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
70
103
|
return { content: '', isBinary: false };
|
|
71
104
|
}
|
|
72
105
|
}
|
|
@@ -83,7 +116,14 @@ export async function getFileAtRef(repoPath, ref, path) {
|
|
|
83
116
|
isBinary
|
|
84
117
|
};
|
|
85
118
|
}
|
|
86
|
-
catch {
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (strict) {
|
|
121
|
+
if (!(await pathExistsAtRef(repoPath, ref, path)))
|
|
122
|
+
throw new GitFileNotFoundError('File was deleted or moved', {
|
|
123
|
+
cause: error
|
|
124
|
+
});
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
87
127
|
return { content: '', isBinary: false };
|
|
88
128
|
}
|
|
89
129
|
}
|