termdock 1.4.324 → 1.4.325
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/dist/client/assets/App-D8MWr9xp.js +186 -0
- package/dist/client/assets/App-D8MWr9xp.js.br +0 -0
- package/dist/client/assets/App-D8MWr9xp.js.gz +0 -0
- package/dist/client/assets/{ArchitecturePanel-CUAB0wFe.js → ArchitecturePanel-CJXvqmtu.js} +6 -6
- package/dist/client/assets/ArchitecturePanel-CJXvqmtu.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-CJXvqmtu.js.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-CKIG8diY.js → ChangeWalkthroughPanel-ChPZVfRz.js} +2 -2
- package/dist/client/assets/ChangeWalkthroughPanel-ChPZVfRz.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-ChPZVfRz.js.gz +0 -0
- package/dist/client/assets/{CollaborationMainWorkspace-CIq-JAbF.js → CollaborationMainWorkspace-DkNhqCj4.js} +1 -1
- package/dist/client/assets/CollaborationMainWorkspace-DkNhqCj4.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-DkNhqCj4.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-BA3bVNJo.js +2 -0
- package/dist/client/assets/{ComputerControlView-CPSI-M86.js → ComputerControlView-BTntDI6C.js} +2 -2
- package/dist/client/assets/ComputerControlView-BTntDI6C.js.br +0 -0
- package/dist/client/assets/ComputerControlView-BTntDI6C.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-BxocNRPm.js → DagPlayground-BIL5vjer.js} +1 -1
- package/dist/client/assets/DagPlayground-BIL5vjer.js.br +0 -0
- package/dist/client/assets/DagPlayground-BIL5vjer.js.gz +0 -0
- package/dist/client/assets/{DiffLab-COGOFO0s.js → DiffLab-CX1AwU2v.js} +2 -2
- package/dist/client/assets/DiffLab-CX1AwU2v.js.br +0 -0
- package/dist/client/assets/DiffLab-CX1AwU2v.js.gz +0 -0
- package/dist/client/assets/{DiffReview-5ERdwz1C.js → DiffReview-CLc1hwO7.js} +1 -1
- package/dist/client/assets/DiffReview-CLc1hwO7.js.br +0 -0
- package/dist/client/assets/DiffReview-CLc1hwO7.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-BSz9IND6.js → DiffReviewLab-DE6m1o4p.js} +1 -1
- package/dist/client/assets/DiffReviewLab-DE6m1o4p.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-DE6m1o4p.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-RT-p-ZBo.js → DiffViewer-BDwtwbQM.js} +8 -8
- package/dist/client/assets/DiffViewer-BDwtwbQM.js.br +0 -0
- package/dist/client/assets/DiffViewer-BDwtwbQM.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-DI9anVWA.js → ModelPreview-DEwATnxi.js} +1 -1
- package/dist/client/assets/ModelPreview-DEwATnxi.js.br +0 -0
- package/dist/client/assets/ModelPreview-DEwATnxi.js.gz +0 -0
- package/dist/client/assets/{RightSidebar-DOLn8R7c.js → RightSidebar-jOQJePDV.js} +3 -3
- package/dist/client/assets/RightSidebar-jOQJePDV.js.br +0 -0
- package/dist/client/assets/RightSidebar-jOQJePDV.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-CXd0unH5.js → abnfDiagram-VCTEODGH-GBSf8AEr.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-GBSf8AEr.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-GBSf8AEr.js.gz +0 -0
- package/dist/client/assets/app-VCmiASzh.js +35 -0
- package/dist/client/assets/app-VCmiASzh.js.br +0 -0
- package/dist/client/assets/app-VCmiASzh.js.gz +0 -0
- package/dist/client/assets/{arc-DCqJmX8M.js → arc-BecvFgjf.js} +1 -1
- package/dist/client/assets/{arc-DCqJmX8M.js.br → arc-BecvFgjf.js.br} +0 -0
- package/dist/client/assets/arc-BecvFgjf.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-DGu_m54H.js → architectureDiagram-5GKGNRK7-Cfk47zEF.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-Cfk47zEF.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-Cfk47zEF.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-BI0gsxDN.js → blockDiagram-I7D4REHJ-BHYgsLvx.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-BHYgsLvx.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-BHYgsLvx.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-Ct3xzGtD.js → c4Diagram-7LVT6UL2-ClWWjTyz.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-ClWWjTyz.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-ClWWjTyz.js.gz +0 -0
- package/dist/client/assets/channel-8F8qxLSK.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-BPd2UShA.js → chunk-2Q5K7J3B-Cat1Dsqx.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-CoFZ5Fo3.js → chunk-5VM5RSS4-CT0S2-E8.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-C5gZrGp4.js → chunk-F27PBJKO-7o5_yA1b.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-7o5_yA1b.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-7o5_yA1b.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-GW8b9ya-.js → chunk-IMKFNOWR-iAZxGMup.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-iAZxGMup.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-iAZxGMup.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-GpMoViQf.js → chunk-JWPE2WC7-DLvwOWZm.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-C_vgVPaw.js → chunk-POPQ4Y6H-BpiRuG56.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-bA7RXwWn.js → chunk-SVP7TREG-DpB7xpqI.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-DpB7xpqI.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-DpB7xpqI.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-BG8DAtll.js → chunk-TICWLB2K-Z_Sti6yX.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-Z_Sti6yX.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-Z_Sti6yX.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-Ces56sud.js → chunk-XXDRQBXY-DKngx1rf.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-C0AmKz7U.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-C0AmKz7U.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-Bwch0qL5.js → cose-bilkent-JH36ORCC-CDo8eUXo.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-CDo8eUXo.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-CDo8eUXo.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-CrAXyA2Z.js → cynefin-OW5HDTMX-R6vltrl4.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-R6vltrl4.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-R6vltrl4.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-YlP0Q93l.js → cynefinDiagram-5FMLGOSQ-BTGMG-zO.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BTGMG-zO.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BTGMG-zO.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-CZ4XcPK9.js → dagre-GXQ25YYZ-D67dNSHU.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-D67dNSHU.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-D67dNSHU.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-c_8HfHGA.js → diagram-S7CK7UJ4-CxZmcHs4.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-CxZmcHs4.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-CxZmcHs4.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-BGtRfxMR.js → diagram-UQ7AKVKN-BErivsDz.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-BErivsDz.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BErivsDz.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-_9yZnhuv.js → diagram-VSXAHHWV-BQWEvK9d.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-BQWEvK9d.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-BQWEvK9d.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-C_mnc4_u.js → diagram-VX7I27RA-COR5k4JL.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-COR5k4JL.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-COR5k4JL.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-sZ_AiSpj.js → diagram-Z3DM3KII-Cx6oTlVi.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-Cx6oTlVi.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-Cx6oTlVi.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-Dm-BXeqS.js → ebnfDiagram-PWID7BFC-dKNZki9k.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-dKNZki9k.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-dKNZki9k.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-Dz4cBepd.js → erDiagram-RLTQ6QDP-DD06d9Gl.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-DD06d9Gl.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-DD06d9Gl.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-CbRG8-WW.js → flowDiagram-HODETNUW-DXNAvw1d.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-DXNAvw1d.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-DXNAvw1d.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-CmBXWlr0.js → ganttDiagram-EL5Y4UJY-BSMu3Rsd.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-BSMu3Rsd.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-BSMu3Rsd.js.gz +0 -0
- package/dist/client/assets/{gestureArbiter-B26W6rS7.js → gestureArbiter-D1TteC76.js} +1 -1
- package/dist/client/assets/gestureArbiter-D1TteC76.js.br +0 -0
- package/dist/client/assets/gestureArbiter-D1TteC76.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-JW5QMYEk.js → gitGraphDiagram-WWUBYQGX-DNDKI803.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DNDKI803.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DNDKI803.js.gz +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-CsA6WdPn.js → infoDiagram-27XIBGKW-BRmB1D5B.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-CsKPUwTw.js → ishikawaDiagram-5VMMS53U-DXU4nnZ6.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-DXU4nnZ6.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-DXU4nnZ6.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-DpN98gN0.js → journeyDiagram-3NMN7TZE-DFx7AfiP.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DFx7AfiP.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DFx7AfiP.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-RHO--43O.js → kanban-definition-UXKFOSKX-B1ikfXJe.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-B1ikfXJe.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-B1ikfXJe.js.gz +0 -0
- package/dist/client/assets/{layout-Cm_prknW.js → layout-DkyvZr1Y.js} +1 -1
- package/dist/client/assets/layout-DkyvZr1Y.js.br +0 -0
- package/dist/client/assets/layout-DkyvZr1Y.js.gz +0 -0
- package/dist/client/assets/{linear-BVAsPkvS.js → linear-CECAbRuX.js} +1 -1
- package/dist/client/assets/linear-CECAbRuX.js.br +0 -0
- package/dist/client/assets/linear-CECAbRuX.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-ufe77Xaz.js → mermaid.core-CYY7Wb75.js} +5 -5
- package/dist/client/assets/mermaid.core-CYY7Wb75.js.br +0 -0
- package/dist/client/assets/mermaid.core-CYY7Wb75.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-CRyy1U2d.js → mindmap-definition-YA3MSWOX-CTs-Y3Lq.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-CTs-Y3Lq.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-CTs-Y3Lq.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-CPNGDwv6.js +2 -0
- package/dist/client/assets/passwordBootstrap-CPNGDwv6.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-CPNGDwv6.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-DSHgdU9q.js → pegDiagram-XKGWAZYB-Y1oZrwtK.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-Y1oZrwtK.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-Y1oZrwtK.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-lIUeAuhN.js → pieDiagram-E7YTZNPT-DYSm5OxQ.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-DYSm5OxQ.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-DYSm5OxQ.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-CIgG9vBX.js → quadrantDiagram-AXDQQJYC-DpibIBz0.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-DpibIBz0.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-DpibIBz0.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-DjP8oesA.js → railroadDiagram-O6MQD6OU-b7jp7C1K.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-b7jp7C1K.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-b7jp7C1K.js.gz +0 -0
- package/dist/client/assets/{rdpSession-BshIobdj.js → rdpSession-DU_OJfX5.js} +1 -1
- package/dist/client/assets/rdpSession-DU_OJfX5.js.br +0 -0
- package/dist/client/assets/rdpSession-DU_OJfX5.js.gz +0 -0
- package/dist/client/assets/{referenceLongPress-DBJnfFHa.js → referenceLongPress-BPQ_i68g.js} +1 -1
- package/dist/client/assets/referenceLongPress-BPQ_i68g.js.br +0 -0
- package/dist/client/assets/referenceLongPress-BPQ_i68g.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-Dbvu6IC_.js → requirementDiagram-BXWQKSXE-BMinO1mf.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-BMinO1mf.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-BMinO1mf.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-1mZ4s-lv.js → sankeyDiagram-P5KCCOFB-CmC977VA.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CmC977VA.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CmC977VA.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-Cu3hhzlV.js → sequenceDiagram-WJ2MYXX4-D6eGbgK8.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-D6eGbgK8.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-D6eGbgK8.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-xfm69HlR.js → sizeCapture-INFHLROL-CnRvyBd8.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-CnRvyBd8.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-CnRvyBd8.js.gz +0 -0
- package/dist/client/assets/{sparkles-CDonlwGu.js → sparkles-DzadqrlX.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-u-bh6mDK.js → stateDiagram-D77RDMKH-DRSalH10.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-DRSalH10.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-DRSalH10.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-DKe8v_ic.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-U8WN__SO.js → swimlanes-42K2YHIH-u-NnDSzq.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-u-NnDSzq.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-u-NnDSzq.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-ei5b8ZYx.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-CMsoNo4F.js → timeline-definition-24CTP7MA-DkLijg_J.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-DkLijg_J.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-DkLijg_J.js.gz +0 -0
- package/dist/client/assets/{triangle-alert-BgErMO2V.js → triangle-alert-D_6xTTiX.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-CPE6pVwh.js → vennDiagram-4TSXK5OY-AWGJtiv3.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-CPE6pVwh.js.br → vennDiagram-4TSXK5OY-AWGJtiv3.js.br} +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-AWGJtiv3.js.gz +0 -0
- package/dist/client/assets/{videoCompression-BWkZvEjM.js → videoCompression-D3y7Bq8J.js} +1 -1
- package/dist/client/assets/videoCompression-D3y7Bq8J.js.br +0 -0
- package/dist/client/assets/videoCompression-D3y7Bq8J.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-4lWd-R4N.js → wardleyDiagram-VM6X3IG4-DI0aZr_y.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-DI0aZr_y.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-DI0aZr_y.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-CpKJMCWV.js → xychartDiagram-S5SC5T6Z-CCoIN3uU.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CCoIN3uU.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CCoIN3uU.js.gz +0 -0
- package/dist/client/index.html +1 -1
- package/dist/client/index.html.br +0 -0
- package/dist/client/index.html.gz +0 -0
- package/dist/client/sw.js +1 -1
- package/dist/client/sw.js.br +0 -0
- package/dist/client/sw.js.gz +0 -0
- package/dist/server/agent/integrationServer.js +127 -2
- package/dist/server/routes/terminal.js +225 -65
- package/dist/server/utils/hostResources.js +128 -0
- package/dist/server/utils/tmuxRecoveryOperations.js +348 -0
- package/package.json +1 -1
- package/runtime-manifest.json +3 -3
- package/dist/client/assets/App-7IUcmCLC.js +0 -186
- package/dist/client/assets/App-7IUcmCLC.js.br +0 -0
- package/dist/client/assets/App-7IUcmCLC.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-CUAB0wFe.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-CUAB0wFe.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-CKIG8diY.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-CKIG8diY.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-CIq-JAbF.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-CIq-JAbF.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-7vWDzH-d.js +0 -2
- package/dist/client/assets/ComputerControlView-CPSI-M86.js.br +0 -0
- package/dist/client/assets/ComputerControlView-CPSI-M86.js.gz +0 -0
- package/dist/client/assets/DagPlayground-BxocNRPm.js.br +0 -0
- package/dist/client/assets/DagPlayground-BxocNRPm.js.gz +0 -0
- package/dist/client/assets/DiffLab-COGOFO0s.js.br +0 -0
- package/dist/client/assets/DiffLab-COGOFO0s.js.gz +0 -0
- package/dist/client/assets/DiffReview-5ERdwz1C.js.br +0 -0
- package/dist/client/assets/DiffReview-5ERdwz1C.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-BSz9IND6.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-BSz9IND6.js.gz +0 -0
- package/dist/client/assets/DiffViewer-RT-p-ZBo.js.br +0 -0
- package/dist/client/assets/DiffViewer-RT-p-ZBo.js.gz +0 -0
- package/dist/client/assets/ModelPreview-DI9anVWA.js.br +0 -0
- package/dist/client/assets/ModelPreview-DI9anVWA.js.gz +0 -0
- package/dist/client/assets/RightSidebar-DOLn8R7c.js.br +0 -0
- package/dist/client/assets/RightSidebar-DOLn8R7c.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-CXd0unH5.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-CXd0unH5.js.gz +0 -0
- package/dist/client/assets/app-DCFOZi_L.js +0 -35
- package/dist/client/assets/app-DCFOZi_L.js.br +0 -0
- package/dist/client/assets/app-DCFOZi_L.js.gz +0 -0
- package/dist/client/assets/arc-DCqJmX8M.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DGu_m54H.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DGu_m54H.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-BI0gsxDN.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-BI0gsxDN.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-Ct3xzGtD.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-Ct3xzGtD.js.gz +0 -0
- package/dist/client/assets/channel-BLSCUksA.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-C5gZrGp4.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-C5gZrGp4.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-GW8b9ya-.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-GW8b9ya-.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-bA7RXwWn.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-bA7RXwWn.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-BG8DAtll.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-BG8DAtll.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-DkVIqOvw.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-DkVIqOvw.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-Bwch0qL5.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-Bwch0qL5.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CrAXyA2Z.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CrAXyA2Z.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-YlP0Q93l.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-YlP0Q93l.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-CZ4XcPK9.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-CZ4XcPK9.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-c_8HfHGA.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-c_8HfHGA.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BGtRfxMR.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BGtRfxMR.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-_9yZnhuv.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-_9yZnhuv.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-C_mnc4_u.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-C_mnc4_u.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-sZ_AiSpj.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-sZ_AiSpj.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Dm-BXeqS.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Dm-BXeqS.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Dz4cBepd.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Dz4cBepd.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-CbRG8-WW.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-CbRG8-WW.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CmBXWlr0.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CmBXWlr0.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-B26W6rS7.js.br +0 -0
- package/dist/client/assets/gestureArbiter-B26W6rS7.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-JW5QMYEk.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-JW5QMYEk.js.gz +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-CsKPUwTw.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-CsKPUwTw.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DpN98gN0.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DpN98gN0.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-RHO--43O.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-RHO--43O.js.gz +0 -0
- package/dist/client/assets/layout-Cm_prknW.js.br +0 -0
- package/dist/client/assets/layout-Cm_prknW.js.gz +0 -0
- package/dist/client/assets/linear-BVAsPkvS.js.br +0 -0
- package/dist/client/assets/linear-BVAsPkvS.js.gz +0 -0
- package/dist/client/assets/mermaid.core-ufe77Xaz.js.br +0 -0
- package/dist/client/assets/mermaid.core-ufe77Xaz.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-CRyy1U2d.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-CRyy1U2d.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-3_SIzSH7.js +0 -2
- package/dist/client/assets/passwordBootstrap-3_SIzSH7.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-3_SIzSH7.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DSHgdU9q.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DSHgdU9q.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-lIUeAuhN.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-lIUeAuhN.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-CIgG9vBX.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-CIgG9vBX.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DjP8oesA.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DjP8oesA.js.gz +0 -0
- package/dist/client/assets/rdpSession-BshIobdj.js.br +0 -0
- package/dist/client/assets/rdpSession-BshIobdj.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-DBJnfFHa.js.br +0 -0
- package/dist/client/assets/referenceLongPress-DBJnfFHa.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-Dbvu6IC_.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-Dbvu6IC_.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-1mZ4s-lv.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-1mZ4s-lv.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Cu3hhzlV.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Cu3hhzlV.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-xfm69HlR.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-xfm69HlR.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-u-bh6mDK.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-u-bh6mDK.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BQ_m8Gzd.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-U8WN__SO.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-U8WN__SO.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-FDEo3QWw.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-CMsoNo4F.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-CMsoNo4F.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-CPE6pVwh.js.gz +0 -0
- package/dist/client/assets/videoCompression-BWkZvEjM.js.br +0 -0
- package/dist/client/assets/videoCompression-BWkZvEjM.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-4lWd-R4N.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-4lWd-R4N.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CpKJMCWV.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CpKJMCWV.js.gz +0 -0
|
@@ -52,6 +52,8 @@ import { AgentResumeHistoryStore } from '../agent/resumeHistory.js';
|
|
|
52
52
|
import { completedExecution, ExecutionArchiveStore } from '../agent/executionArchives.js';
|
|
53
53
|
import { archiveTaskWorkspace, restoreTaskWorkspace, snapshotTaskWorkspace } from '../agent/collaborationTaskWorkspace.js';
|
|
54
54
|
import { isTmuxRecoveryCandidate } from '../utils/tmuxRecoveryCandidate.js';
|
|
55
|
+
import { configuredTmuxRecoveryConcurrency, TmuxRecoveryOperationStore } from '../utils/tmuxRecoveryOperations.js';
|
|
56
|
+
import { assessAgentResourceBudget, collectHostResourceSnapshot, configuredAgentResourceBudget } from '../utils/hostResources.js';
|
|
55
57
|
import { AutomationStore, normalizeAutomationSchedule } from '../agent/automationStore.js';
|
|
56
58
|
import { writeBracketedPrompt, canDeliverPromptToAgent } from '../agent/promptDelivery.js';
|
|
57
59
|
import { captureTerminalInputGuard, captureRuntimeInputGuard, waitAndWriteTerminalInput } from '../agent/terminalInput.js';
|
|
@@ -204,7 +206,11 @@ const agentResumeHistory = new AgentResumeHistoryStore(`${TERMDOCK_DIR}/agent-re
|
|
|
204
206
|
const executionArchives = new ExecutionArchiveStore(`${TERMDOCK_DIR}/execution-archives.json`);
|
|
205
207
|
let executionArchiveMutation = Promise.resolve();
|
|
206
208
|
const TMUX_RECOVERY_STATE_FILE = `${TERMDOCK_DIR}/tmux-recovery-state.json`;
|
|
209
|
+
const TMUX_RECOVERY_OPERATIONS_FILE = `${TERMDOCK_DIR}/tmux-recovery-operations.json`;
|
|
207
210
|
const tmuxLifecycle = new TmuxLifecycleCoordinator();
|
|
211
|
+
const tmuxRecoveryConcurrencyLimit = configuredTmuxRecoveryConcurrency();
|
|
212
|
+
const tmuxRecoveryOperations = new TmuxRecoveryOperationStore(TMUX_RECOVERY_OPERATIONS_FILE, tmuxRecoveryConcurrencyLimit, (error) => console.error('[tmux-recovery] operation worker error:', getErrorMessage(error)));
|
|
213
|
+
let tmuxRecoveryHealthTimer = null;
|
|
208
214
|
const intentionallyDeletingTmuxSessions = new Set();
|
|
209
215
|
let tmuxRecoveryState = { lastServerPid: null, incident: null, dismissedSessionIds: [] };
|
|
210
216
|
let persistGlobalStateTimer = null;
|
|
@@ -215,6 +221,10 @@ let globalSessionStateReloadTimer = null;
|
|
|
215
221
|
// receives deltas on every mutation (PUT/DELETE client-state, dead-session
|
|
216
222
|
// reconciliation, etc.). Replaces the 5-second poll on the front-end. ──
|
|
217
223
|
const controlClients = new Map();
|
|
224
|
+
tmuxRecoveryOperations.subscribe(() => {
|
|
225
|
+
latestSessionInventoryAt = 0;
|
|
226
|
+
broadcastClientState();
|
|
227
|
+
});
|
|
218
228
|
let latestSessionInventory = null;
|
|
219
229
|
// Only authoritative observations may remove persisted tmux sessions.
|
|
220
230
|
const inventoryTmuxLiveness = new WeakMap();
|
|
@@ -543,8 +553,10 @@ async function loadGlobalSessionStateFromDisk() {
|
|
|
543
553
|
async function loadTmuxRecoveryStateFromDisk() {
|
|
544
554
|
try {
|
|
545
555
|
const data = await readJsonFileIfExists(TMUX_RECOVERY_STATE_FILE);
|
|
546
|
-
if (!data)
|
|
556
|
+
if (!data) {
|
|
557
|
+
await tmuxRecoveryOperations.load();
|
|
547
558
|
return;
|
|
559
|
+
}
|
|
548
560
|
tmuxRecoveryState = {
|
|
549
561
|
lastServerPid: typeof data.lastServerPid === 'number' && Number.isInteger(data.lastServerPid)
|
|
550
562
|
? data.lastServerPid
|
|
@@ -554,6 +566,7 @@ async function loadTmuxRecoveryStateFromDisk() {
|
|
|
554
566
|
? data.dismissedSessionIds.filter((id) => typeof id === 'string')
|
|
555
567
|
: [],
|
|
556
568
|
};
|
|
569
|
+
await tmuxRecoveryOperations.load();
|
|
557
570
|
}
|
|
558
571
|
catch (error) {
|
|
559
572
|
console.warn('[tmux-recovery] Failed to load recovery state:', getErrorMessage(error));
|
|
@@ -666,6 +679,8 @@ const shutdownWithRecordings = async () => {
|
|
|
666
679
|
if (shuttingDown)
|
|
667
680
|
return;
|
|
668
681
|
shuttingDown = true;
|
|
682
|
+
if (tmuxRecoveryHealthTimer)
|
|
683
|
+
clearInterval(tmuxRecoveryHealthTimer);
|
|
669
684
|
flushPersistAndExit();
|
|
670
685
|
void persistToolbarPresetsNow();
|
|
671
686
|
caffeinateManager.shutdown();
|
|
@@ -682,6 +697,9 @@ process.on('SIGINT', () => { void shutdownWithRecordings(); });
|
|
|
682
697
|
// 服务启动时从磁盘加载(带去重,防止历史累积的重复条目复活)
|
|
683
698
|
void (async () => {
|
|
684
699
|
await Promise.all([loadGlobalSessionStateFromDisk(), loadTmuxRecoveryStateFromDisk()]);
|
|
700
|
+
await sampleTmuxRecoveryHealth();
|
|
701
|
+
tmuxRecoveryHealthTimer = setInterval(() => void sampleTmuxRecoveryHealth(), 15_000);
|
|
702
|
+
tmuxRecoveryHealthTimer.unref?.();
|
|
685
703
|
// Delivery needs the loaded inventory, not optional PTY adoption/metadata backfill.
|
|
686
704
|
// Those steps must not leave durable messages without a background scheduler.
|
|
687
705
|
collaborationDeliveryWorker.start();
|
|
@@ -694,6 +712,7 @@ void (async () => {
|
|
|
694
712
|
});
|
|
695
713
|
pruneOrphanSessions();
|
|
696
714
|
await backfillPersistedTmuxMetadata();
|
|
715
|
+
await tmuxRecoveryOperations.resumePending();
|
|
697
716
|
})();
|
|
698
717
|
caffeinateManager.startNetworkMonitor();
|
|
699
718
|
// 清理磁盘恢复后后端已不存在的 session 引用。
|
|
@@ -3009,8 +3028,21 @@ async function getTmuxServerPid() {
|
|
|
3009
3028
|
}
|
|
3010
3029
|
function getTmuxRecoveryView() {
|
|
3011
3030
|
const incident = tmuxRecoveryState.incident;
|
|
3012
|
-
|
|
3013
|
-
|
|
3031
|
+
const latestOperation = tmuxRecoveryOperations.latest();
|
|
3032
|
+
if (!incident) {
|
|
3033
|
+
if (!latestOperation || ['succeeded', 'partial_failure'].includes(latestOperation.state))
|
|
3034
|
+
return null;
|
|
3035
|
+
const sessions = latestOperation.items.flatMap(item => {
|
|
3036
|
+
const record = globalSessionState.sessions.find(candidate => candidate.sessionId === item.sessionId);
|
|
3037
|
+
const agent = record?.agentResume ? agentBySlug(record.agentResume.slug) : null;
|
|
3038
|
+
if (!record?.tmuxSessionName || !record.cwd || !agent || !record.agentResume?.sessionId)
|
|
3039
|
+
return [];
|
|
3040
|
+
return [{ sessionId: record.sessionId, name: record.name, tmuxSessionName: record.tmuxSessionName,
|
|
3041
|
+
cwd: record.cwd, agentSlug: agent.slug, agentDisplayName: agent.displayName }];
|
|
3042
|
+
});
|
|
3043
|
+
return { id: latestOperation.incidentId, detectedAt: latestOperation.createdAt, operation: latestOperation, sessions };
|
|
3044
|
+
}
|
|
3045
|
+
const operation = latestOperation?.incidentId === incident.id ? latestOperation : null;
|
|
3014
3046
|
const affected = new Set(incident.affectedSessionIds);
|
|
3015
3047
|
const sessions = globalSessionState.sessions.flatMap((record) => {
|
|
3016
3048
|
if (!affected.has(record.sessionId) || record.mode !== 'tmux' || !record.tmuxSessionName
|
|
@@ -3029,7 +3061,7 @@ function getTmuxRecoveryView() {
|
|
|
3029
3061
|
}];
|
|
3030
3062
|
});
|
|
3031
3063
|
return sessions.length > 0
|
|
3032
|
-
? { id: incident.id, detectedAt: incident.detectedAt, sessions }
|
|
3064
|
+
? { id: incident.id, detectedAt: incident.detectedAt, operation, sessions }
|
|
3033
3065
|
: null;
|
|
3034
3066
|
}
|
|
3035
3067
|
async function observeTmuxServerGeneration(liveTmuxSessions) {
|
|
@@ -5938,7 +5970,7 @@ async function resolveShellIntegrationDir() {
|
|
|
5938
5970
|
/**
|
|
5939
5971
|
* Inject termdock shell integration via environment variables.
|
|
5940
5972
|
*
|
|
5941
|
-
* This replaces the old injectShellTitleHooks with
|
|
5973
|
+
* This replaces the old injectShellTitleHooks with full shell
|
|
5942
5974
|
* integration that emits OSC 133 (prompt marks), OSC 2 (title), and OSC 7 (cwd).
|
|
5943
5975
|
*
|
|
5944
5976
|
* Works for both shell mode (env passed to PTY spawn) and tmux mode
|
|
@@ -5958,7 +5990,7 @@ async function injectShellIntegration(shellPath, baseEnv) {
|
|
|
5958
5990
|
}
|
|
5959
5991
|
if (shellType === 'zsh') {
|
|
5960
5992
|
// Create a temporary ZDOTDIR that sources user's zshenv/zshrc then
|
|
5961
|
-
// sources our integration script.
|
|
5993
|
+
// sources our integration script. This preserves the user shell configuration.
|
|
5962
5994
|
const zdotdir = '/tmp/termdock-zsh-' + String(process.pid);
|
|
5963
5995
|
try {
|
|
5964
5996
|
await fs.promises.mkdir(zdotdir, { recursive: true });
|
|
@@ -6122,74 +6154,202 @@ router.get('/tmux/status', async (_req, res) => {
|
|
|
6122
6154
|
const status = await getTmuxStatus();
|
|
6123
6155
|
res.json(status);
|
|
6124
6156
|
});
|
|
6125
|
-
|
|
6126
|
-
const
|
|
6127
|
-
if (!
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
if (
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
const
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
};
|
|
6150
|
-
const activeBackend = findActiveAgentResumeOwner('', target);
|
|
6151
|
-
const activeWriterPid = activeBackend ? null : await findExternalCodexWriter(target);
|
|
6152
|
-
if (activeBackend || activeWriterPid) {
|
|
6153
|
-
outcomes.push({ sessionId: frontendSessionId, restored: true });
|
|
6154
|
-
continue;
|
|
6157
|
+
async function restoreTmuxRecoverySession(operationId, frontendSessionId, reportPhase) {
|
|
6158
|
+
const record = globalSessionState.sessions.find((session) => session.sessionId === frontendSessionId);
|
|
6159
|
+
if (!record || record.mode !== 'tmux' || !record.tmuxSessionName || !record.cwd || !record.agentResume?.sessionId) {
|
|
6160
|
+
throw Object.assign(new Error('Recovery metadata is incomplete'), { code: 'RECOVERY_METADATA_INCOMPLETE' });
|
|
6161
|
+
}
|
|
6162
|
+
const agent = agentBySlug(record.agentResume.slug);
|
|
6163
|
+
const command = agent ? buildResumeCommand(agent, record.agentResume.sessionId, record.agentResume.launchArgv) : null;
|
|
6164
|
+
if (!agent || !command)
|
|
6165
|
+
throw Object.assign(new Error('Agent does not support exact resume'), { code: 'EXACT_RESUME_UNSUPPORTED' });
|
|
6166
|
+
const target = { slug: agent.slug, nativeSessionId: record.agentResume.sessionId, command };
|
|
6167
|
+
const activeBackend = findActiveAgentResumeOwner('', target);
|
|
6168
|
+
const activeWriterPid = activeBackend ? null : await findExternalCodexWriter(target);
|
|
6169
|
+
if (activeBackend || activeWriterPid)
|
|
6170
|
+
return {};
|
|
6171
|
+
await collaborationDeliveryWorker.reconfigure(frontendSessionId, () => tmuxLifecycle.run(`restore:${record.tmuxSessionName}`, async () => {
|
|
6172
|
+
// Reconcile ownership again inside the per-terminal serialization fence.
|
|
6173
|
+
if (findActiveAgentResumeOwner('', target) || await findExternalCodexWriter(target))
|
|
6174
|
+
return;
|
|
6175
|
+
reportPhase('runtime_resolving');
|
|
6176
|
+
await prepareManagedTmuxSessionUnlocked(record.tmuxSessionName, record.cwd);
|
|
6177
|
+
const metadata = await resolveLiveTmuxMetadata(record.tmuxSessionName);
|
|
6178
|
+
const foreground = metadata?.program?.toLowerCase() ?? null;
|
|
6179
|
+
if (foreground && !shellNamesBackend.has(foreground)) {
|
|
6180
|
+
throw Object.assign(new Error(`pane is busy with ${foreground}`), { code: 'RECOVERY_TARGET_BUSY' });
|
|
6155
6181
|
}
|
|
6182
|
+
const previous = collaborationRouting.get(frontendSessionId);
|
|
6183
|
+
if (previous)
|
|
6184
|
+
collaborationRouting.bind({ ...previous, pane: null });
|
|
6185
|
+
reportPhase('guard_captured');
|
|
6186
|
+
const launchGuard = await beginAgentLaunchCheck(frontendSessionId, agent.slug, target.nativeSessionId);
|
|
6156
6187
|
try {
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
const metadata = await resolveLiveTmuxMetadata(record.tmuxSessionName);
|
|
6160
|
-
const foreground = metadata?.program?.toLowerCase() ?? null;
|
|
6161
|
-
if (foreground && !shellNamesBackend.has(foreground)) {
|
|
6162
|
-
throw new Error(`pane is busy with ${foreground}`);
|
|
6163
|
-
}
|
|
6164
|
-
// Explicit recovery may replace the vanished tmux pane. A pending
|
|
6165
|
-
// message alone must never replace that binding.
|
|
6166
|
-
const previous = collaborationRouting.get(frontendSessionId);
|
|
6167
|
-
if (previous)
|
|
6168
|
-
collaborationRouting.bind({ ...previous, pane: null });
|
|
6169
|
-
const launchGuard = await beginAgentLaunchCheck(frontendSessionId, agent.slug, target.nativeSessionId);
|
|
6170
|
-
await writeAutomaticTerminalPrompt(frontendSessionId, command, undefined, launchGuard);
|
|
6171
|
-
}));
|
|
6172
|
-
outcomes.push({ sessionId: frontendSessionId, restored: true });
|
|
6188
|
+
reportPhase('paste_submit');
|
|
6189
|
+
await writeAutomaticTerminalPrompt(frontendSessionId, command, stage => reportPhase(stage), launchGuard);
|
|
6173
6190
|
}
|
|
6174
6191
|
catch (error) {
|
|
6175
|
-
|
|
6192
|
+
const message = getErrorMessage(error);
|
|
6193
|
+
const reportedCode = error?.code;
|
|
6194
|
+
const code = reportedCode === 'TERMINAL_INPUT_TARGET_CHANGED' || message.includes('TERMINAL_INPUT_TARGET_CHANGED')
|
|
6195
|
+
? 'TERMINAL_INPUT_TARGET_CHANGED' : 'RECOVERY_LAUNCH_FAILED';
|
|
6196
|
+
throw Object.assign(new Error(message), { code, operationId, sessionId: frontendSessionId });
|
|
6176
6197
|
}
|
|
6198
|
+
}));
|
|
6199
|
+
const incident = tmuxRecoveryState.incident;
|
|
6200
|
+
if (incident?.affectedSessionIds.includes(frontendSessionId)) {
|
|
6201
|
+
const remaining = incident.affectedSessionIds.filter(id => id !== frontendSessionId);
|
|
6202
|
+
tmuxRecoveryState.incident = remaining.length ? { ...incident, affectedSessionIds: remaining } : null;
|
|
6203
|
+
await persistTmuxRecoveryState();
|
|
6177
6204
|
}
|
|
6178
|
-
const
|
|
6179
|
-
const
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6205
|
+
const paneTty = await runTmux(['display-message', '-p', '-t', record.tmuxSessionName, '#{pane_tty}']).catch(() => '');
|
|
6206
|
+
const normalizedTty = paneTty.trim().replace(/^\/dev\//, '');
|
|
6207
|
+
if (normalizedTty) {
|
|
6208
|
+
const resources = await collectHostResourceSnapshot();
|
|
6209
|
+
const rows = resources.processRows.filter(row => row.tty === normalizedTty);
|
|
6210
|
+
if (rows.some(row => row.agent))
|
|
6211
|
+
return { actualRssBytes: rows.reduce((sum, row) => sum + row.rssBytes, 0) };
|
|
6212
|
+
}
|
|
6213
|
+
return {};
|
|
6214
|
+
}
|
|
6215
|
+
async function resolveTmuxRecoveryScope(scope, incident) {
|
|
6216
|
+
const candidates = incident.affectedSessionIds;
|
|
6217
|
+
if (scope.kind === 'incident')
|
|
6218
|
+
return candidates;
|
|
6219
|
+
if (scope.kind === 'sessions') {
|
|
6220
|
+
if (!Array.isArray(scope.sessionIds) || scope.sessionIds.length === 0)
|
|
6221
|
+
throw Object.assign(new Error('sessionIds must be a non-empty array'), { code: 'INVALID_RECOVERY_SCOPE' });
|
|
6222
|
+
if (scope.sessionIds.some(id => !candidates.includes(id)))
|
|
6223
|
+
throw Object.assign(new Error('Explicit scope may only include sessions in the current recovery incident'), { code: 'RECOVERY_SCOPE_OUTSIDE_INCIDENT' });
|
|
6224
|
+
return [...new Set(scope.sessionIds)];
|
|
6225
|
+
}
|
|
6226
|
+
if (!scope.id)
|
|
6227
|
+
throw Object.assign(new Error('scope id is required'), { code: 'INVALID_RECOVERY_SCOPE' });
|
|
6228
|
+
if (scope.kind === 'group') {
|
|
6229
|
+
const group = collaborationStore.getGroup(scope.id);
|
|
6230
|
+
if (!group || group.deleted)
|
|
6231
|
+
throw Object.assign(new Error('Collaboration group was not found'), { code: 'RECOVERY_GROUP_NOT_FOUND' });
|
|
6232
|
+
return candidates.filter(id => group.sessionIds.includes(id));
|
|
6233
|
+
}
|
|
6234
|
+
const integrationRoot = `${TERMDOCK_DIR}/integration-state`;
|
|
6235
|
+
const portDirectories = await fs.promises.readdir(integrationRoot, { withFileTypes: true }).catch(() => []);
|
|
6236
|
+
const memberIds = new Set();
|
|
6237
|
+
for (const directory of portDirectories.filter(item => item.isDirectory())) {
|
|
6238
|
+
const state = await readJsonFileIfExists(path.join(integrationRoot, directory.name, 'integration-sessions.json'));
|
|
6239
|
+
for (const item of state?.sessions ?? [])
|
|
6240
|
+
if (item.principal_id === scope.id && typeof item.session_id === 'string')
|
|
6241
|
+
memberIds.add(item.session_id);
|
|
6242
|
+
}
|
|
6243
|
+
return candidates.filter(id => memberIds.has(id));
|
|
6244
|
+
}
|
|
6245
|
+
tmuxRecoveryOperations.setRunner(restoreTmuxRecoverySession);
|
|
6246
|
+
const tmuxAgentResourceBudget = configuredAgentResourceBudget();
|
|
6247
|
+
tmuxRecoveryOperations.setAdmission(async (_operationId, sessionId) => {
|
|
6248
|
+
try {
|
|
6249
|
+
const snapshot = await collectHostResourceSnapshot();
|
|
6250
|
+
const group = collaborationStore.list().find(item => item.sessionIds.includes(sessionId));
|
|
6251
|
+
let residentAgentsInGroup = 0;
|
|
6252
|
+
if (group) {
|
|
6253
|
+
const rows = globalSessionState.sessions.filter(item => group.sessionIds.includes(item.sessionId) && item.agentResume && item.tmuxSessionName);
|
|
6254
|
+
const liveSessions = await listLiveTmuxInventorySessions();
|
|
6255
|
+
const liveAgents = new Set(liveSessions.filter(item => item.program && agentBySlug(item.program) !== null).map(item => item.name));
|
|
6256
|
+
residentAgentsInGroup = rows.filter(item => liveAgents.has(item.tmuxSessionName)).length;
|
|
6257
|
+
const memberIds = new Set(group.sessionIds);
|
|
6258
|
+
residentAgentsInGroup += tmuxRecoveryOperations.all().flatMap(operation => operation.items)
|
|
6259
|
+
.filter(item => item.state === 'running' && memberIds.has(item.sessionId)).length;
|
|
6260
|
+
}
|
|
6261
|
+
const result = assessAgentResourceBudget({ snapshot: { ...snapshot, residentAgentCount: snapshot.residentAgentCount + tmuxRecoveryOperations.stats().activeLaunches }, budget: tmuxAgentResourceBudget, residentAgentsInGroup });
|
|
6262
|
+
return { ...result, memAvailableBytes: snapshot.memoryAvailableBytes, estimatedRssBytes: tmuxAgentResourceBudget.estimatedRssBytes };
|
|
6263
|
+
}
|
|
6264
|
+
catch {
|
|
6265
|
+
return { admitted: false, reason: 'RESOURCE_SNAPSHOT_UNAVAILABLE', estimatedRssBytes: tmuxAgentResourceBudget.estimatedRssBytes };
|
|
6266
|
+
}
|
|
6267
|
+
});
|
|
6268
|
+
const TMUX_RECOVERY_HEALTH_FILE = `${TERMDOCK_DIR}/tmux-recovery-health.json`;
|
|
6269
|
+
let latestTmuxRecoveryHealth = null;
|
|
6270
|
+
let tmuxRecoveryHealthSampling = false;
|
|
6271
|
+
async function sampleTmuxRecoveryHealth() {
|
|
6272
|
+
if (tmuxRecoveryHealthSampling)
|
|
6273
|
+
return;
|
|
6274
|
+
tmuxRecoveryHealthSampling = true;
|
|
6275
|
+
try {
|
|
6276
|
+
const snapshot = await collectHostResourceSnapshot();
|
|
6277
|
+
const diagnostics = {
|
|
6278
|
+
sampledAt: snapshot.sampledAt,
|
|
6279
|
+
platform: snapshot.platform,
|
|
6280
|
+
memoryAvailableBytes: snapshot.memoryAvailableBytes,
|
|
6281
|
+
memoryTotalBytes: snapshot.memoryTotalBytes,
|
|
6282
|
+
memorySnapshotAvailable: snapshot.memorySnapshotAvailable,
|
|
6283
|
+
processSnapshotAvailable: snapshot.processSnapshotAvailable,
|
|
6284
|
+
memoryPressure: snapshot.memoryPressure,
|
|
6285
|
+
kswapd: snapshot.kswapd,
|
|
6286
|
+
dStateProcessCount: snapshot.dStateProcessCount,
|
|
6287
|
+
residentAgentCount: snapshot.residentAgentCount,
|
|
6288
|
+
residentAgentRssBytes: snapshot.residentAgentRssBytes,
|
|
6289
|
+
browserProcessCount: snapshot.browserProcessCount,
|
|
6290
|
+
recovery: tmuxRecoveryOperations.stats(),
|
|
6291
|
+
resourceBudget: tmuxAgentResourceBudget,
|
|
6292
|
+
};
|
|
6293
|
+
latestTmuxRecoveryHealth = diagnostics;
|
|
6294
|
+
const temporaryPath = `${TMUX_RECOVERY_HEALTH_FILE}.${process.pid}.tmp`;
|
|
6295
|
+
await fs.promises.writeFile(temporaryPath, JSON.stringify(diagnostics, null, 2), { mode: 0o600 });
|
|
6296
|
+
await fs.promises.rename(temporaryPath, TMUX_RECOVERY_HEALTH_FILE);
|
|
6297
|
+
}
|
|
6298
|
+
catch (error) {
|
|
6299
|
+
console.warn('[tmux-recovery] resource health sample failed:', getErrorMessage(error));
|
|
6300
|
+
}
|
|
6301
|
+
finally {
|
|
6302
|
+
tmuxRecoveryHealthSampling = false;
|
|
6303
|
+
}
|
|
6304
|
+
}
|
|
6305
|
+
router.get('/tmux/recovery/diagnostics', async (_req, res) => {
|
|
6306
|
+
await sampleTmuxRecoveryHealth();
|
|
6307
|
+
return res.json({ health: latestTmuxRecoveryHealth, operation: tmuxRecoveryOperations.latest(), concurrencyLimit: tmuxRecoveryConcurrencyLimit });
|
|
6308
|
+
});
|
|
6309
|
+
router.post('/tmux/recovery/restore-all', async (req, res) => {
|
|
6310
|
+
const incident = tmuxRecoveryState.incident;
|
|
6311
|
+
if (!incident)
|
|
6312
|
+
return res.status(404).json({ error: 'no tmux recovery is pending' });
|
|
6313
|
+
const rawScope = req.body?.scope;
|
|
6314
|
+
let scope = { kind: 'incident' };
|
|
6315
|
+
if (rawScope !== undefined) {
|
|
6316
|
+
if (!rawScope || !['incident', 'group', 'principal', 'sessions'].includes(rawScope.kind))
|
|
6317
|
+
return res.status(400).json({ error: 'invalid recovery scope' });
|
|
6318
|
+
scope = rawScope;
|
|
6319
|
+
}
|
|
6320
|
+
const sessionIds = await resolveTmuxRecoveryScope(scope, incident).catch(error => {
|
|
6321
|
+
res.status(400).json({ error: getErrorMessage(error), code: error.code ?? 'INVALID_RECOVERY_SCOPE' });
|
|
6322
|
+
return null;
|
|
6190
6323
|
});
|
|
6324
|
+
if (!sessionIds)
|
|
6325
|
+
return;
|
|
6326
|
+
try {
|
|
6327
|
+
const operation = await tmuxRecoveryOperations.create({ incidentId: incident.id, sessionIds, scope,
|
|
6328
|
+
idempotencyKey: String(req.body?.idempotencyKey ?? req.headers['idempotency-key'] ?? ''),
|
|
6329
|
+
...(req.body?.concurrency !== undefined ? { concurrency: Number(req.body.concurrency) } : {}) });
|
|
6330
|
+
res.status(202).json({ accepted: true, operation_id: operation.id, operation: tmuxRecoveryOperations.get(operation.id) });
|
|
6331
|
+
}
|
|
6332
|
+
catch (error) {
|
|
6333
|
+
res.status(409).json({ error: getErrorMessage(error), code: error.code ?? 'RECOVERY_OPERATION_FAILED', operation_id: error.operationId });
|
|
6334
|
+
}
|
|
6335
|
+
});
|
|
6336
|
+
router.get('/tmux/recovery/operations', (_req, res) => res.json({ operations: tmuxRecoveryOperations.all().slice(-50) }));
|
|
6337
|
+
router.get('/tmux/recovery/operations/:id', (req, res) => {
|
|
6338
|
+
const operation = tmuxRecoveryOperations.get(req.params.id);
|
|
6339
|
+
return operation ? res.json({ operation }) : res.status(404).json({ error: 'recovery operation not found' });
|
|
6340
|
+
});
|
|
6341
|
+
router.post('/tmux/recovery/operations/:id/pause', async (req, res) => {
|
|
6342
|
+
const operation = await tmuxRecoveryOperations.pause(req.params.id);
|
|
6343
|
+
return operation ? res.json({ operation }) : res.status(404).json({ error: 'recovery operation not found' });
|
|
6344
|
+
});
|
|
6345
|
+
router.post('/tmux/recovery/operations/:id/resume', async (req, res) => {
|
|
6346
|
+
const operation = await tmuxRecoveryOperations.resume(req.params.id);
|
|
6347
|
+
return operation ? res.json({ operation }) : res.status(404).json({ error: 'recovery operation not found' });
|
|
6191
6348
|
});
|
|
6192
6349
|
router.delete('/tmux/recovery', async (_req, res) => {
|
|
6350
|
+
if (tmuxRecoveryOperations.all().some(operation => !['succeeded', 'partial_failure'].includes(operation.state))) {
|
|
6351
|
+
return res.status(409).json({ error: 'a recovery operation is active; pause or finish it before dismissing the incident', code: 'RECOVERY_OPERATION_ACTIVE' });
|
|
6352
|
+
}
|
|
6193
6353
|
tmuxRecoveryState.dismissedSessionIds = Array.from(new Set([
|
|
6194
6354
|
...tmuxRecoveryState.dismissedSessionIds,
|
|
6195
6355
|
...(tmuxRecoveryState.incident?.affectedSessionIds ?? []),
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import { execFile } from 'node:child_process';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { detectAgentFromCommand } from '../agent/registry.js';
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
function parseMemAvailable(contents) {
|
|
8
|
+
const match = contents.match(/^MemAvailable:\s+(\d+)\s+kB$/m);
|
|
9
|
+
return match ? Number(match[1]) * 1024 : null;
|
|
10
|
+
}
|
|
11
|
+
function parsePressure(contents) {
|
|
12
|
+
const parse = (kind) => {
|
|
13
|
+
const line = contents.split('\n').find(item => item.startsWith(`${kind} `));
|
|
14
|
+
const value = line?.match(/\bavg10=([\d.]+)/)?.[1];
|
|
15
|
+
return value === undefined ? null : Number(value);
|
|
16
|
+
};
|
|
17
|
+
const someAvg10 = parse('some');
|
|
18
|
+
const fullAvg10 = parse('full');
|
|
19
|
+
return someAvg10 === null && fullAvg10 === null ? null : { someAvg10, fullAvg10 };
|
|
20
|
+
}
|
|
21
|
+
function parseProcesses(contents, customCommands = {}) {
|
|
22
|
+
return contents.split('\n').flatMap(line => {
|
|
23
|
+
const match = line.trim().match(/^(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(.*)$/);
|
|
24
|
+
if (!match)
|
|
25
|
+
return [];
|
|
26
|
+
const [, pidRaw, pgidRaw, state, ttyRaw, rssRaw, commandRaw] = match;
|
|
27
|
+
const pid = Number(pidRaw), processGroupId = Number(pgidRaw), rssKb = Number(rssRaw);
|
|
28
|
+
if (!Number.isSafeInteger(pid) || pid <= 0 || !Number.isSafeInteger(processGroupId) || processGroupId <= 0 || !Number.isFinite(rssKb))
|
|
29
|
+
return [];
|
|
30
|
+
const command = commandRaw ?? '';
|
|
31
|
+
const comm = (command.split(/\s+/, 1)[0] ?? '').toLowerCase().split('/').at(-1) ?? '';
|
|
32
|
+
return [{
|
|
33
|
+
pid, processGroupId, state: state ?? '?', tty: ttyRaw === '?' ? '' : ttyRaw, command,
|
|
34
|
+
rssBytes: Math.max(0, rssKb) * 1024,
|
|
35
|
+
agent: Boolean(detectAgentFromCommand(command, customCommands)),
|
|
36
|
+
browser: /^(chrome|chromium|firefox|webkit|agent-browser)(-|$)/.test(comm),
|
|
37
|
+
kswapd: /^\[?kswapd\d*\]?$/.test(comm),
|
|
38
|
+
}];
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export async function collectHostResourceSnapshot(customCommands = {}) {
|
|
42
|
+
let memoryAvailableBytes = os.freemem();
|
|
43
|
+
let memorySnapshotAvailable = false;
|
|
44
|
+
let memoryPressure = null;
|
|
45
|
+
if (process.platform === 'linux') {
|
|
46
|
+
const [meminfo, pressure] = await Promise.all([
|
|
47
|
+
fs.readFile('/proc/meminfo', 'utf8').catch(() => ''),
|
|
48
|
+
fs.readFile('/proc/pressure/memory', 'utf8').catch(() => ''),
|
|
49
|
+
]);
|
|
50
|
+
const parsedAvailable = parseMemAvailable(meminfo);
|
|
51
|
+
if (parsedAvailable !== null) {
|
|
52
|
+
memoryAvailableBytes = parsedAvailable;
|
|
53
|
+
memorySnapshotAvailable = true;
|
|
54
|
+
}
|
|
55
|
+
memoryPressure = parsePressure(pressure);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
memorySnapshotAvailable = Number.isFinite(memoryAvailableBytes);
|
|
59
|
+
}
|
|
60
|
+
let processRows = [];
|
|
61
|
+
let processSnapshotAvailable = false;
|
|
62
|
+
try {
|
|
63
|
+
const { stdout } = await execFileAsync('ps', ['-e', '-o', 'pid=,pgid=,stat=,tty=,rss=,args='], { timeout: 5000, maxBuffer: 8 * 1024 * 1024 });
|
|
64
|
+
processRows = parseProcesses(stdout, customCommands);
|
|
65
|
+
processSnapshotAvailable = processRows.length > 0;
|
|
66
|
+
}
|
|
67
|
+
catch { /* process metrics are diagnostic; memory admission remains fail-closed below */ }
|
|
68
|
+
const groupRss = new Map();
|
|
69
|
+
const agentGroupIds = new Set();
|
|
70
|
+
for (const row of processRows) {
|
|
71
|
+
groupRss.set(row.processGroupId, (groupRss.get(row.processGroupId) ?? 0) + row.rssBytes);
|
|
72
|
+
if (row.agent)
|
|
73
|
+
agentGroupIds.add(row.processGroupId);
|
|
74
|
+
}
|
|
75
|
+
const agentRss = [...agentGroupIds].reduce((sum, id) => sum + (groupRss.get(id) ?? 0), 0);
|
|
76
|
+
return {
|
|
77
|
+
sampledAt: Date.now(), platform: process.platform, memoryAvailableBytes, memoryTotalBytes: os.totalmem(), memoryPressure,
|
|
78
|
+
dStateProcessCount: processRows.filter(row => row.state.includes('D')).length,
|
|
79
|
+
kswapd: processRows.filter(row => row.kswapd).map(({ pid, state }) => ({ pid, state })),
|
|
80
|
+
residentAgentCount: agentGroupIds.size,
|
|
81
|
+
residentAgentRssBytes: agentRss,
|
|
82
|
+
browserProcessCount: processRows.filter(row => row.browser).length,
|
|
83
|
+
memorySnapshotAvailable,
|
|
84
|
+
processSnapshotAvailable,
|
|
85
|
+
processRows,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export function configuredAgentResourceBudget(env = process.env) {
|
|
89
|
+
const readMb = (key, fallback, min, max) => {
|
|
90
|
+
const raw = env[key];
|
|
91
|
+
if (raw === undefined || raw.trim() === '')
|
|
92
|
+
return fallback * 1024 ** 2;
|
|
93
|
+
const value = Number(raw);
|
|
94
|
+
if (!Number.isInteger(value) || value < min || value > max)
|
|
95
|
+
throw new Error(`${key} must be an integer from ${min} to ${max} MiB`);
|
|
96
|
+
return value * 1024 ** 2;
|
|
97
|
+
};
|
|
98
|
+
const readCount = (key, fallback, max) => {
|
|
99
|
+
const raw = env[key];
|
|
100
|
+
if (raw === undefined || raw.trim() === '')
|
|
101
|
+
return fallback;
|
|
102
|
+
const value = Number(raw);
|
|
103
|
+
if (!Number.isInteger(value) || value < 1 || value > max)
|
|
104
|
+
throw new Error(`${key} must be an integer from 1 to ${max}`);
|
|
105
|
+
return value;
|
|
106
|
+
};
|
|
107
|
+
return {
|
|
108
|
+
minAvailableBytes: readMb('TD_AGENT_MIN_AVAILABLE_MB', 2048, 256, 131072),
|
|
109
|
+
estimatedRssBytes: readMb('TD_AGENT_ESTIMATED_RSS_MB', 512, 64, 32768),
|
|
110
|
+
maxResidentAgents: readCount('TD_MAX_RESIDENT_AGENTS', 48, 10000),
|
|
111
|
+
maxAgentsPerGroup: readCount('TD_MAX_AGENTS_PER_GROUP', 32, 1000),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export function assessAgentResourceBudget(input) {
|
|
115
|
+
if (input.snapshot.memorySnapshotAvailable === false || input.snapshot.processSnapshotAvailable === false)
|
|
116
|
+
return { admitted: false, reason: 'RESOURCE_SNAPSHOT_UNAVAILABLE' };
|
|
117
|
+
const required = input.budget.minAvailableBytes + input.budget.estimatedRssBytes;
|
|
118
|
+
if (input.snapshot.memoryAvailableBytes < required)
|
|
119
|
+
return { admitted: false, reason: 'MEMORY_BUDGET_LOW' };
|
|
120
|
+
if (input.snapshot.residentAgentCount >= input.budget.maxResidentAgents)
|
|
121
|
+
return { admitted: false, reason: 'MAX_RESIDENT_AGENTS' };
|
|
122
|
+
if (input.residentAgentsInGroup >= input.budget.maxAgentsPerGroup)
|
|
123
|
+
return { admitted: false, reason: 'MAX_GROUP_AGENTS' };
|
|
124
|
+
return { admitted: true };
|
|
125
|
+
}
|
|
126
|
+
export function parseHostResourcesForTest(contents) {
|
|
127
|
+
return { memAvailableBytes: parseMemAvailable(contents.meminfo), pressure: parsePressure(contents.pressure), processes: parseProcesses(contents.ps) };
|
|
128
|
+
}
|