termdock 1.4.322 → 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-VDM6hrwp.js → ArchitecturePanel-CJXvqmtu.js} +2 -2
- 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-B64j3i8o.js → ChangeWalkthroughPanel-ChPZVfRz.js} +1 -1
- 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-DkNhqCj4.js +1 -0
- 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-Co-TTYas.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-kJczdJ6J.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-BaaioC-q.js → DiffLab-CX1AwU2v.js} +1 -1
- 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-Wfck3RIS.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-Dblr3wgw.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-C-utC5Ga.js → DiffViewer-BDwtwbQM.js} +1 -1
- 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-BBLnHWVQ.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-CUHSFkmZ.js → RightSidebar-jOQJePDV.js} +36 -36
- 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-DDd99QpA.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-C3dck_E0.js → arc-BecvFgjf.js} +1 -1
- package/dist/client/assets/arc-BecvFgjf.js.br +0 -0
- package/dist/client/assets/arc-BecvFgjf.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-C3bCQc2w.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-znMn6iCQ.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-C0sopIsj.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-f7USyccJ.js → chunk-2Q5K7J3B-Cat1Dsqx.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-Ck2xirDs.js → chunk-5VM5RSS4-CT0S2-E8.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-DaDCIj30.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-DEC_On5x.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-BrswC3n2.js → chunk-JWPE2WC7-DLvwOWZm.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-ClMbDV47.js → chunk-POPQ4Y6H-BpiRuG56.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-D7_1VwC5.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-D5jzVTZ8.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-DOhaoLcX.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-DOsaKgiX.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-CMcLh_wh.js → cynefin-OW5HDTMX-R6vltrl4.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-R6vltrl4.js.br +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-CMcLh_wh.js.gz → cynefin-OW5HDTMX-R6vltrl4.js.gz} +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-Dd8bfq9V.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-BlW7fAi0.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-B0-tKahG.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-BQumj4Dw.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-Zx_FLmPP.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-CEZvatNH.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-CJ9IEQ8j.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-sQQMv3ea.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-D3rMpj7M.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-B6QZiE0-.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-Bw9bltcu.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-Cczz34oI.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-mdnkxjeD.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/index-Ctz-IDjR.css +1 -0
- package/dist/client/assets/index-Ctz-IDjR.css.br +0 -0
- package/dist/client/assets/{index-B4bEMUyn.css.gz → index-Ctz-IDjR.css.gz} +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-DyrPP3Yf.js → infoDiagram-27XIBGKW-BRmB1D5B.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-CEPH_ePM.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-CQ0PjBy7.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-BpQUXA60.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-BdptC7rq.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-C2cSRCxu.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-ClVMFyAU.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-D0AFCmsY.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-R2qlU2Pq.js → passwordBootstrap-CPNGDwv6.js} +1 -1
- 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-BGAh0bwg.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-CDOZUF44.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-D_Qn6NTU.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-qUVGWhJN.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-hgCM4i-j.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-BEMsP11a.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-BBQMQuVZ.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-CeEExsqW.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-7ZPO-X0a.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-MBjz7Snm.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-C9ShmGPf.js → sparkles-DzadqrlX.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-DtLp89uP.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-Bc02S1q-.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-BLLwbOm5.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-i_L8ws6U.js → triangle-alert-D_6xTTiX.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-8FcrAdbk.js → vennDiagram-4TSXK5OY-AWGJtiv3.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-8FcrAdbk.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-RIaDabW4.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-paL2wRrJ.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-BM5qJ4E7.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 +2 -2
- package/dist/client/index.html.br +0 -0
- package/dist/client/index.html.gz +0 -0
- package/dist/client/sw-encrypted-background.js +1 -1
- package/dist/client/sw-encrypted-background.js.br +0 -0
- package/dist/client/sw-encrypted-background.js.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/collaborationGroupRoutes.js +15 -0
- package/dist/server/agent/collaborationStore.js +63 -2
- package/dist/server/agent/collaborationTaskRoutes.js +5 -1
- package/dist/server/agent/integrationCli.js +38 -20
- package/dist/server/agent/integrationServer.js +145 -3
- package/dist/server/federation/packets.js +179 -28
- package/dist/server/federation/requestContext.js +6 -1
- package/dist/server/federation/runtime.js +19 -8
- package/dist/server/routes/terminal.js +285 -85
- package/dist/server/utils/autoUpdate.js +55 -8
- package/dist/server/utils/hostResources.js +128 -0
- package/dist/server/utils/npmUpdate.js +84 -8
- package/dist/server/utils/terminalPasteBuffer.js +66 -0
- package/dist/server/utils/tmuxClientCapabilities.js +4 -0
- package/dist/server/utils/tmuxCopySelection.js +94 -0
- package/dist/server/utils/tmuxEmptyServerProbe.js +15 -0
- package/dist/server/utils/tmuxRecoveryOperations.js +348 -0
- package/docs/integrations.md +36 -0
- package/package.json +5 -2
- package/runtime-manifest.json +3 -3
- package/.desktop-dist/main.js +0 -1989
- package/dist/client/assets/App-DEKo4SNL.js +0 -186
- package/dist/client/assets/App-DEKo4SNL.js.br +0 -0
- package/dist/client/assets/App-DEKo4SNL.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-VDM6hrwp.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-VDM6hrwp.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-B64j3i8o.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-B64j3i8o.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-DG8TQNqo.js +0 -1
- package/dist/client/assets/CollaborationMainWorkspace-DG8TQNqo.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-DG8TQNqo.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-msQsgVaI.js +0 -2
- package/dist/client/assets/ComputerControlView-Co-TTYas.js.br +0 -0
- package/dist/client/assets/ComputerControlView-Co-TTYas.js.gz +0 -0
- package/dist/client/assets/DagPlayground-kJczdJ6J.js.br +0 -0
- package/dist/client/assets/DagPlayground-kJczdJ6J.js.gz +0 -0
- package/dist/client/assets/DiffLab-BaaioC-q.js.br +0 -0
- package/dist/client/assets/DiffLab-BaaioC-q.js.gz +0 -0
- package/dist/client/assets/DiffReview-Wfck3RIS.js.br +0 -0
- package/dist/client/assets/DiffReview-Wfck3RIS.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-Dblr3wgw.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-Dblr3wgw.js.gz +0 -0
- package/dist/client/assets/DiffViewer-C-utC5Ga.js.br +0 -0
- package/dist/client/assets/DiffViewer-C-utC5Ga.js.gz +0 -0
- package/dist/client/assets/ModelPreview-BBLnHWVQ.js.br +0 -0
- package/dist/client/assets/ModelPreview-BBLnHWVQ.js.gz +0 -0
- package/dist/client/assets/RightSidebar-CUHSFkmZ.js.br +0 -0
- package/dist/client/assets/RightSidebar-CUHSFkmZ.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-DDd99QpA.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-DDd99QpA.js.gz +0 -0
- package/dist/client/assets/app-CfqF7_nh.js +0 -35
- package/dist/client/assets/app-CfqF7_nh.js.br +0 -0
- package/dist/client/assets/app-CfqF7_nh.js.gz +0 -0
- package/dist/client/assets/arc-C3dck_E0.js.br +0 -0
- package/dist/client/assets/arc-C3dck_E0.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-C3bCQc2w.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-C3bCQc2w.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-znMn6iCQ.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-znMn6iCQ.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-C0sopIsj.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-C0sopIsj.js.gz +0 -0
- package/dist/client/assets/channel-CMC8t83j.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-DaDCIj30.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-DaDCIj30.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-DEC_On5x.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-DEC_On5x.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-D7_1VwC5.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-D7_1VwC5.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-D5jzVTZ8.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-D5jzVTZ8.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-ElMHzA-E.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-ElMHzA-E.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-DOsaKgiX.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-DOsaKgiX.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CMcLh_wh.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Dd8bfq9V.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Dd8bfq9V.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-BlW7fAi0.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-BlW7fAi0.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-B0-tKahG.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-B0-tKahG.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BQumj4Dw.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BQumj4Dw.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-Zx_FLmPP.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-Zx_FLmPP.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-CEZvatNH.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-CEZvatNH.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-CJ9IEQ8j.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-CJ9IEQ8j.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-sQQMv3ea.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-sQQMv3ea.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-D3rMpj7M.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-D3rMpj7M.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-B6QZiE0-.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-B6QZiE0-.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-Bw9bltcu.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-Bw9bltcu.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-Cczz34oI.js.br +0 -0
- package/dist/client/assets/gestureArbiter-Cczz34oI.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-mdnkxjeD.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-mdnkxjeD.js.gz +0 -0
- package/dist/client/assets/index-B4bEMUyn.css +0 -1
- package/dist/client/assets/index-B4bEMUyn.css.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-CEPH_ePM.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-CEPH_ePM.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-CQ0PjBy7.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-CQ0PjBy7.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-BpQUXA60.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-BpQUXA60.js.gz +0 -0
- package/dist/client/assets/layout-BdptC7rq.js.br +0 -0
- package/dist/client/assets/layout-BdptC7rq.js.gz +0 -0
- package/dist/client/assets/linear-C2cSRCxu.js.br +0 -0
- package/dist/client/assets/linear-C2cSRCxu.js.gz +0 -0
- package/dist/client/assets/mermaid.core-ClVMFyAU.js.br +0 -0
- package/dist/client/assets/mermaid.core-ClVMFyAU.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-D0AFCmsY.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-D0AFCmsY.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-R2qlU2Pq.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-R2qlU2Pq.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-BGAh0bwg.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-BGAh0bwg.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-CDOZUF44.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-CDOZUF44.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-D_Qn6NTU.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-D_Qn6NTU.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-qUVGWhJN.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-qUVGWhJN.js.gz +0 -0
- package/dist/client/assets/rdpSession-hgCM4i-j.js.br +0 -0
- package/dist/client/assets/rdpSession-hgCM4i-j.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-BEMsP11a.js.br +0 -0
- package/dist/client/assets/referenceLongPress-BEMsP11a.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-BBQMQuVZ.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-BBQMQuVZ.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CeEExsqW.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CeEExsqW.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-7ZPO-X0a.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-7ZPO-X0a.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-MBjz7Snm.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-MBjz7Snm.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-DtLp89uP.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-DtLp89uP.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CIQb4Dp7.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-Bc02S1q-.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-Bc02S1q-.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-RiKL7eXI.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-BLLwbOm5.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-BLLwbOm5.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-8FcrAdbk.js.gz +0 -0
- package/dist/client/assets/videoCompression-RIaDabW4.js.br +0 -0
- package/dist/client/assets/videoCompression-RIaDabW4.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-paL2wRrJ.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-paL2wRrJ.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-BM5qJ4E7.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-BM5qJ4E7.js.gz +0 -0
|
Binary file
|
|
Binary file
|
package/dist/client/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
if(!self.define){let e,s={};const n=(n,i)=>(n=new URL(n+".js",i).href,s[n]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=s,document.head.appendChild(e)}else e=n,importScripts(n),s()}).then(()=>{let e=s[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(i,a)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let
|
|
1
|
+
if(!self.define){let e,s={};const n=(n,i)=>(n=new URL(n+".js",i).href,s[n]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=s,document.head.appendChild(e)}else e=n,importScripts(n),s()}).then(()=>{let e=s[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(i,a)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let c={};const r=e=>n(e,o),d={module:{uri:o},exports:c,require:r};s[o]=Promise.all(i.map(e=>d[e]||r(e))).then(e=>(a(...e),c))}}define(["./workbox-9845bf53"],function(e){"use strict";importScripts("sw-encrypted-background.js","sw-notifications.js","sw-secure-api.js","sw-downloads.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"index.html",revision:"140615ef2cf415473ada0b3ab2bce1ae"},{url:"assets/app-VCmiASzh.js",revision:null},{url:"assets/react-XDCjal2T.js",revision:null},{url:"assets/dnd-BCFAPC0L.js",revision:null},{url:"assets/terminal-C7X98Amf.js",revision:null},{url:"assets/index-Ctz-IDjR.css",revision:null},{url:"icons/agents/opencode.svg",revision:"851c2931d95821ba2b39b68587b48976"},{url:"icons/agents/kimi.svg",revision:"5366c710f4f763022fe159a69cf17409"},{url:"icons/agents/grok.svg",revision:"cd745b0b964545bc7a6b4f36021c3298"},{url:"icons/agents/goose.svg",revision:"c79b927278983c19098d77b4b0c11ab3"},{url:"icons/agents/gemini.svg",revision:"7a64f6d6f80a908255e9c0256903882e"},{url:"icons/agents/droid.svg",revision:"f28723b8c5c115e0908ba3c72613e256"},{url:"icons/agents/cursor.svg",revision:"a1528f3d3cda497f75a5db28defb19de"},{url:"icons/agents/copilot.svg",revision:"dd52e96c1eac0b6ec4bedcd0ddd3b52d"},{url:"icons/agents/codex.svg",revision:"608e55babb0b217e1f2da287342b46d3"},{url:"icons/agents/claude.svg",revision:"08af0225b7acdd8e8f573f3247e5e1c3"},{url:"icons/agents/amp.svg",revision:"af12f8b33faa81e4d46ea7b93c7e43f3"},{url:"apple-touch-icon-180x180.png",revision:"628c2e2f28a9b545aa57be1352025643"},{url:"boot-logo.png",revision:"2e3a25efd9b0b8ca1eab53b5f2284556"},{url:"favicon.ico",revision:"2994debfa6f59a0898e24eb0c54d64b5"},{url:"ipad-status-bar.js",revision:"3a8080edb8a5cc658a7908f6ad69537f"},{url:"maskable-icon-512x512.png",revision:"4d489331fd81f0713c400b26426442b9"},{url:"pwa-192x192.png",revision:"4d6f899888cfb8ccd7a1fd3c9e583ad2"},{url:"pwa-512x512.png",revision:"0885dd7ec8932e7f878b013d51a380a4"},{url:"robots.txt",revision:"d9339907142917dee4aac78494ac9cfe"},{url:"sw-downloads.js",revision:"48dab2e78bd93b4eee27fca487eccfde"},{url:"sw-notifications.js",revision:"fde578590ea8b0540d488ef448b48f45"},{url:"sw-secure-api.js",revision:"cf2bdbd03d41d66d60344c0b5a1075de"},{url:"manifest.webmanifest",revision:"83f9a43b74c236012adafb077fb6d774"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html"),{denylist:[/^\/api\//,/^\/__termdock-download\//,/^\/health$/]})),e.registerRoute(/\/assets\/.*\.(?:js|css)$/,new e.CacheFirst({cacheName:"termdock-lazy-assets-v1",plugins:[new e.CacheableResponsePlugin({statuses:[0,200]}),new e.ExpirationPlugin({maxEntries:96,maxAgeSeconds:2592e3})]}),"GET"),e.registerRoute(/\/fonts\/terminal\/[^/]+-[a-f0-9]{12}\.woff2$/,new e.CacheFirst({cacheName:"termdock-fonts-v2",plugins:[new e.CacheableResponsePlugin({statuses:[0,200]}),new e.ExpirationPlugin({maxEntries:384,maxAgeSeconds:2592e3})]}),"GET"),e.registerRoute(/\/fonts\/.*\.(?:woff2|ttf)$/,new e.StaleWhileRevalidate({cacheName:"termdock-fonts-v1",plugins:[new e.ExpirationPlugin({maxEntries:24,maxAgeSeconds:2592e3})]}),"GET")});
|
package/dist/client/sw.js.br
CHANGED
|
Binary file
|
package/dist/client/sw.js.gz
CHANGED
|
Binary file
|
|
@@ -9,6 +9,21 @@ export function collaborationGroupRoutes(options) {
|
|
|
9
9
|
res.json({ federationVersion: 1, capabilities: { groupRevision: 1, groupPromotion: 1, groupMove: 1, serverCollaboration: options.save ? 1 : undefined },
|
|
10
10
|
groups: options.store.list(), sessions: options.sessions() });
|
|
11
11
|
});
|
|
12
|
+
for (const [verb, archived] of [['archive', true], ['unarchive', false]]) {
|
|
13
|
+
router.post(`/collaboration-groups/:groupId/${verb}`, (req, res) => {
|
|
14
|
+
try {
|
|
15
|
+
const body = req.body ?? {};
|
|
16
|
+
if (Object.keys(body).some(key => !['expectedUpdatedAt', 'idempotencyKey'].includes(key)))
|
|
17
|
+
throw new CollaborationError('INVALID_GROUP_ARCHIVE_REQUEST', 'Only expectedUpdatedAt and idempotencyKey are accepted', 400);
|
|
18
|
+
const result = options.store.setLocalGroupArchived(req.params.groupId, archived, body.expectedUpdatedAt, body.idempotencyKey);
|
|
19
|
+
res.json(result);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
res.status(error instanceof CollaborationError ? error.httpStatus : 500).json({ code: error instanceof CollaborationError ? error.code : 'GROUP_ARCHIVE_FAILED',
|
|
23
|
+
...(error instanceof CollaborationError && error.details ? error.details : {}), error: error instanceof Error ? error.message : 'Group archive operation failed' });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
12
27
|
router.post('/collaboration-groups', async (req, res) => {
|
|
13
28
|
try {
|
|
14
29
|
const body = req.body ?? {};
|
|
@@ -82,6 +82,7 @@ export class CollaborationStore {
|
|
|
82
82
|
historyAvailable = true;
|
|
83
83
|
groupCreationAvailable = true;
|
|
84
84
|
groupRenamesAvailable = true;
|
|
85
|
+
groupArchivesAvailable = true;
|
|
85
86
|
/** `drawId` is the random source behind newCollaborationId. It is a
|
|
86
87
|
* constructor seam so tests can force a collision and prove the guard
|
|
87
88
|
* below actually skips it; production always uses the CSPRNG default. */
|
|
@@ -126,6 +127,16 @@ export class CollaborationStore {
|
|
|
126
127
|
&& typeof value.applied.name === 'string' && !!value.applied.name.trim() && value.applied.name.length <= 256
|
|
127
128
|
&& !COLLAB_NAME_FORBIDDEN.test(value.applied.name) && Number.isSafeInteger(value.applied.updatedAt) && value.applied.updatedAt >= 0));
|
|
128
129
|
this.document.localGroupRenames = this.groupRenamesAvailable ? renames ?? {} : {};
|
|
130
|
+
const archives = parsed.localGroupArchives;
|
|
131
|
+
this.groupArchivesAvailable = [1, 2].includes(parsed.version ?? 0) && Array.isArray(parsed.groups)
|
|
132
|
+
&& (parsed.localGroupArchivesComplete === undefined || parsed.localGroupArchivesComplete === true)
|
|
133
|
+
&& (archives === undefined || archives !== null && typeof archives === 'object' && !Array.isArray(archives)
|
|
134
|
+
&& Object.entries(archives).every(([key, value]) => /^[a-f0-9]{64}$/.test(key) && value && typeof value.hash === 'string' && /^[a-f0-9]{64}$/.test(value.hash)
|
|
135
|
+
&& value.applied && typeof value.applied.id === 'string' && /^[a-zA-Z0-9._-]{1,80}$/.test(value.applied.id)
|
|
136
|
+
&& typeof value.applied.name === 'string' && !!value.applied.name.trim() && value.applied.name.length <= 256
|
|
137
|
+
&& !COLLAB_NAME_FORBIDDEN.test(value.applied.name) && Number.isSafeInteger(value.applied.updatedAt) && value.applied.updatedAt >= 0
|
|
138
|
+
&& (value.applied.archivedAt === undefined || Number.isSafeInteger(value.applied.archivedAt) && value.applied.archivedAt >= 0)));
|
|
139
|
+
this.document.localGroupArchives = this.groupArchivesAvailable ? archives ?? {} : {};
|
|
129
140
|
this.document.sequence = Math.max(parsed.sequence ?? 0, this.document.sequence ?? 0);
|
|
130
141
|
this.document.idempotency = parsed.idempotency ?? {};
|
|
131
142
|
this.document.consumers = parsed.consumers ?? {};
|
|
@@ -138,10 +149,12 @@ export class CollaborationStore {
|
|
|
138
149
|
this.historyAvailable = error.code === 'ENOENT';
|
|
139
150
|
this.groupCreationAvailable = this.historyAvailable;
|
|
140
151
|
this.groupRenamesAvailable = this.historyAvailable;
|
|
152
|
+
this.groupArchivesAvailable = this.historyAvailable;
|
|
141
153
|
}
|
|
142
154
|
this.document.terminalHistoryComplete = this.historyAvailable;
|
|
143
155
|
this.document.localGroupRequestsComplete = this.groupCreationAvailable;
|
|
144
156
|
this.document.localGroupRenamesComplete = this.groupRenamesAvailable;
|
|
157
|
+
this.document.localGroupArchivesComplete = this.groupArchivesAvailable;
|
|
145
158
|
this.persistedDocument = JSON.stringify(this.document);
|
|
146
159
|
}
|
|
147
160
|
list() {
|
|
@@ -248,7 +261,54 @@ export class CollaborationStore {
|
|
|
248
261
|
throw new CollaborationError('GROUP_NOT_LOCAL', 'Administrator metadata operations require a local group', 409);
|
|
249
262
|
if (!Number.isSafeInteger(group.updatedAt) || group.updatedAt < 0)
|
|
250
263
|
throw new CollaborationError('GROUP_STATE_UNCONFIRMED', 'Group revision is unavailable', 409);
|
|
251
|
-
return { id: group.id, name: group.name, updatedAt: group.updatedAt };
|
|
264
|
+
return { id: group.id, name: group.name, updatedAt: group.updatedAt, ...(group.archivedAt !== undefined ? { archivedAt: group.archivedAt } : {}) };
|
|
265
|
+
}
|
|
266
|
+
listLocalGroupMetadata(includeArchived = false) {
|
|
267
|
+
return this.list().filter(group => !group.federated && (includeArchived || group.archivedAt === undefined))
|
|
268
|
+
.map(group => ({ id: group.id, name: group.name, updatedAt: group.updatedAt,
|
|
269
|
+
...(group.archivedAt !== undefined ? { archivedAt: group.archivedAt } : {}) }));
|
|
270
|
+
}
|
|
271
|
+
/** Archive is a reversible discovery flag; it never changes membership or message routing. */
|
|
272
|
+
setLocalGroupArchived(id, archived, expectedUpdatedAt, key) {
|
|
273
|
+
if (typeof id !== 'string' || !/^[a-zA-Z0-9._-]{1,80}$/.test(id))
|
|
274
|
+
throw new CollaborationError('INVALID_GROUP_ID', 'An exact group ID is required', 400);
|
|
275
|
+
if (typeof archived !== 'boolean')
|
|
276
|
+
throw new CollaborationError('INVALID_GROUP_ARCHIVE_REQUEST', 'Archive state must be explicit', 400);
|
|
277
|
+
if (!Number.isSafeInteger(expectedUpdatedAt) || expectedUpdatedAt < 0)
|
|
278
|
+
throw new CollaborationError('INVALID_GROUP_VERSION', 'expected_updated_at must be a non-negative safe integer', 400);
|
|
279
|
+
const requestKey = localGroupRequestKey(key);
|
|
280
|
+
if (!this.groupArchivesAvailable)
|
|
281
|
+
throw new CollaborationError('GROUP_ARCHIVE_STATE_UNCONFIRMED', 'Archive journal is unavailable; reconcile persisted state before changing group visibility', 409);
|
|
282
|
+
const hash = crypto.createHash('sha256').update(JSON.stringify(['archive-local-group', id, archived, expectedUpdatedAt])).digest('hex');
|
|
283
|
+
const previous = this.document.localGroupArchives?.[requestKey];
|
|
284
|
+
if (previous) {
|
|
285
|
+
if (previous.hash !== hash)
|
|
286
|
+
throw new CollaborationError('IDEMPOTENCY_CONFLICT', 'This administrator archive key was used with different parameters', 409);
|
|
287
|
+
const target = this.getGroup(id);
|
|
288
|
+
if (!target || target.deleted || target.federated)
|
|
289
|
+
throw new CollaborationError('GROUP_ARCHIVE_TARGET_UNAVAILABLE', 'Original local archive target is unavailable; no replacement is created', 409);
|
|
290
|
+
const group = this.getLocalGroupMetadata(id);
|
|
291
|
+
this.persist(true);
|
|
292
|
+
return { group, applied: { ...previous.applied }, replayed: true };
|
|
293
|
+
}
|
|
294
|
+
const current = this.getLocalGroupMetadata(id);
|
|
295
|
+
if (current.updatedAt !== expectedUpdatedAt)
|
|
296
|
+
throw new CollaborationError('GROUP_CHANGED', 'Group changed; read its current revision before deciding again', 409, { currentUpdatedAt: current.updatedAt });
|
|
297
|
+
if (Object.keys(this.document.localGroupArchives ?? {}).length >= 2000)
|
|
298
|
+
throw new CollaborationError('GROUP_ARCHIVE_LIMIT', 'Administrator archive journal has reached its 2000-record limit', 409);
|
|
299
|
+
const target = this.getGroup(id);
|
|
300
|
+
const currentlyArchived = target.archivedAt !== undefined;
|
|
301
|
+
const changed = currentlyArchived !== archived;
|
|
302
|
+
const updatedAt = changed ? Math.max(Date.now(), current.updatedAt + 1) : current.updatedAt;
|
|
303
|
+
if (!Number.isSafeInteger(updatedAt))
|
|
304
|
+
throw new CollaborationError('GROUP_STATE_UNCONFIRMED', 'Group revision cannot advance safely', 409);
|
|
305
|
+
const archivedAt = archived ? currentlyArchived ? target.archivedAt : Date.now() : undefined;
|
|
306
|
+
const applied = { id, name: target.name, updatedAt, ...(archivedAt !== undefined ? { archivedAt } : {}) };
|
|
307
|
+
this.document = { ...this.document, groups: this.document.groups.map(group => group.id === id
|
|
308
|
+
? { ...group, updatedAt, ...(archivedAt !== undefined ? { archivedAt } : { archivedAt: undefined }) } : group),
|
|
309
|
+
localGroupArchives: { ...this.document.localGroupArchives, [requestKey]: { hash, applied } } };
|
|
310
|
+
this.persist(true);
|
|
311
|
+
return { group: { ...applied }, applied: { ...applied }, replayed: false };
|
|
252
312
|
}
|
|
253
313
|
/** Display metadata and its original receipt share a durable commit. */
|
|
254
314
|
renameLocalGroup(id, value, expectedUpdatedAt, key) {
|
|
@@ -282,7 +342,8 @@ export class CollaborationStore {
|
|
|
282
342
|
const updatedAt = name === current.name ? current.updatedAt : Math.max(Date.now(), current.updatedAt + 1);
|
|
283
343
|
if (!Number.isSafeInteger(updatedAt))
|
|
284
344
|
throw new CollaborationError('GROUP_STATE_UNCONFIRMED', 'Group revision cannot advance safely', 409);
|
|
285
|
-
const
|
|
345
|
+
const target = this.getGroup(id);
|
|
346
|
+
const applied = { id, name, updatedAt, ...(target.archivedAt !== undefined ? { archivedAt: target.archivedAt } : {}) };
|
|
286
347
|
this.document = { ...this.document, groups: this.document.groups.map(group => group.id === id ? { ...group, name, updatedAt } : group),
|
|
287
348
|
localGroupRenames: { ...this.document.localGroupRenames, [requestKey]: { hash, applied } } };
|
|
288
349
|
this.persist(true);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import { CollaborationError } from './collaborationProtocol.js';
|
|
3
3
|
import { assertPeerRegistrationAuthority } from './collaborationPeerTransport.js';
|
|
4
|
+
import { hasEncryptedServiceAccess } from '../federation/requestContext.js';
|
|
4
5
|
export function collaborationTaskRoutes(options) {
|
|
5
6
|
const router = Router();
|
|
6
7
|
const groupId = (value) => {
|
|
@@ -15,8 +16,11 @@ export function collaborationTaskRoutes(options) {
|
|
|
15
16
|
if (!tasks || !peers)
|
|
16
17
|
throw new CollaborationError('TASK_SERVICE_STARTING', '任务服务正在启动,请稍后重试', 503);
|
|
17
18
|
if (!options.agent) {
|
|
19
|
+
// Reading the inbox does not register peers or create durable authority.
|
|
20
|
+
// Open-mode full-service viewers may read; mutations still require admin.
|
|
18
21
|
try {
|
|
19
|
-
|
|
22
|
+
if (!(['GET', 'HEAD'].includes(req.method) && hasEncryptedServiceAccess(req)))
|
|
23
|
+
assertPeerRegistrationAuthority(req);
|
|
20
24
|
}
|
|
21
25
|
catch {
|
|
22
26
|
throw new CollaborationError('TASK_PERMISSION_DENIED', '当前连接无权处理协作任务', 403);
|
|
@@ -26,6 +26,14 @@ export const INTEGRATION_HELP = `td integration — local scoped integration adm
|
|
|
26
26
|
GROUP_CHANGED, GROUP_NOT_FOUND, GROUP_NOT_LOCAL, GROUP_STATE_UNCONFIRMED,
|
|
27
27
|
GROUP_RENAME_TARGET_UNAVAILABLE, GROUP_RENAME_STATE_UNCONFIRMED,
|
|
28
28
|
GROUP_RENAME_LIMIT, ADMIN_LOCAL_GROUP_RENAME_UNSUPPORTED.
|
|
29
|
+
group archive|unarchive <exact-group-id> --expected-updated-at <N> --idempotency-key <key>
|
|
30
|
+
Administrator only; minimum CLI/running service 1.4.323; admin_local_group_archive=true.
|
|
31
|
+
Reversible discovery metadata only. Default groups list hides archived groups;
|
|
32
|
+
groups --include-archived lists them. group get always includes archivedAt when set.
|
|
33
|
+
Membership, principals, sessions, runtime, messages and history are unchanged.
|
|
34
|
+
Retry uncertain results with the same operation, revision and key; GROUP_CHANGED
|
|
35
|
+
requires rereading current metadata and choosing a new key. Archive receipts are
|
|
36
|
+
retained separately from the immutable group-create snapshot.
|
|
29
37
|
group create --name <name> --idempotency-key <key>
|
|
30
38
|
Administrator only; create an empty local group without a terminal source.
|
|
31
39
|
Minimum CLI/running service 1.4.318; admin_local_group_create=true.
|
|
@@ -125,29 +133,33 @@ export async function runIntegrationAdmin(argv, port, adminToken, write) {
|
|
|
125
133
|
return 0;
|
|
126
134
|
}
|
|
127
135
|
const socketPath = integrationSocketPath(port);
|
|
128
|
-
if (action === 'group' && ['get', 'rename'].includes(argv[1])) {
|
|
136
|
+
if (action === 'group' && ['get', 'rename', 'archive', 'unarchive'].includes(argv[1])) {
|
|
129
137
|
const operation = argv[1], id = argv[2], args = {};
|
|
130
|
-
const usage = 'Usage: td integration group get <exact-id> | group rename <exact-id> --name <name> --expected-updated-at <N> --idempotency-key <key>';
|
|
138
|
+
const usage = 'Usage: td integration group get <exact-id> | group rename <exact-id> --name <name> --expected-updated-at <N> --idempotency-key <key> | group archive|unarchive <exact-id> --expected-updated-at <N> --idempotency-key <key>';
|
|
131
139
|
if (!id || !/^[a-zA-Z0-9._-]{1,80}$/.test(id))
|
|
132
140
|
throw new CollaborationError('INVALID_GROUP_ID', usage, 400);
|
|
133
141
|
if (operation === 'get' && argv.length !== 3)
|
|
134
142
|
throw new CollaborationError('INVALID_GROUP_RENAME_REQUEST', usage, 400);
|
|
135
|
-
if (operation
|
|
143
|
+
if (operation !== 'get') {
|
|
136
144
|
for (let i = 3; i < argv.length; i += 2) {
|
|
137
|
-
|
|
138
|
-
|
|
145
|
+
const allowed = operation === 'rename' ? ['--name', '--expected-updated-at', '--idempotency-key'] : ['--expected-updated-at', '--idempotency-key'];
|
|
146
|
+
if (!allowed.includes(argv[i]) || !argv[i + 1] || Object.hasOwn(args, argv[i]))
|
|
147
|
+
throw new CollaborationError(operation === 'rename' ? 'INVALID_GROUP_RENAME_REQUEST' : 'INVALID_GROUP_ARCHIVE_REQUEST', usage, 400);
|
|
139
148
|
args[argv[i]] = argv[i + 1];
|
|
140
149
|
}
|
|
141
|
-
if (Object.keys(args).length !== 3 || !args['--name']?.trim() || !args['--idempotency-key']?.trim()
|
|
142
|
-
|
|
150
|
+
if (operation === 'rename' ? Object.keys(args).length !== 3 || !args['--name']?.trim() || !args['--idempotency-key']?.trim()
|
|
151
|
+
: Object.keys(args).length !== 2 || !args['--idempotency-key']?.trim())
|
|
152
|
+
throw new CollaborationError(operation === 'rename' ? 'INVALID_GROUP_RENAME_REQUEST' : 'INVALID_GROUP_ARCHIVE_REQUEST', usage, 400);
|
|
143
153
|
if (!/^(0|[1-9][0-9]*)$/.test(args['--expected-updated-at']) || !Number.isSafeInteger(Number(args['--expected-updated-at'])))
|
|
144
154
|
throw new CollaborationError('INVALID_GROUP_VERSION', usage, 400);
|
|
145
155
|
}
|
|
146
156
|
const capabilities = parsed(await request(socketPath, 'GET', '/admin/capabilities', undefined, headers(undefined, adminToken)));
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
157
|
+
const isArchive = operation === 'archive' || operation === 'unarchive';
|
|
158
|
+
if (capabilities[isArchive ? 'admin_local_group_archive' : 'admin_local_group_rename'] !== true)
|
|
159
|
+
throw new CollaborationError(isArchive ? 'ADMIN_LOCAL_GROUP_ARCHIVE_UNSUPPORTED' : 'ADMIN_LOCAL_GROUP_RENAME_UNSUPPORTED', `CLI and running service must support this operation (minimum 1.4.${isArchive ? '323' : '319'})`, 409);
|
|
160
|
+
const body = operation === 'get' ? undefined : { ...(operation === 'rename' ? { name: args['--name'] } : {}), expected_updated_at: Number(args['--expected-updated-at']), idempotency_key: args['--idempotency-key'] };
|
|
161
|
+
const suffix = operation === 'get' ? '' : `/${operation}`;
|
|
162
|
+
write(JSON.stringify(parsed(await request(socketPath, body ? 'POST' : 'GET', `/admin/groups/${encodeURIComponent(id)}${suffix}`, body, headers(undefined, adminToken)))));
|
|
151
163
|
return 0;
|
|
152
164
|
}
|
|
153
165
|
if (action === 'group') {
|
|
@@ -213,16 +225,22 @@ export async function runIntegrationAdmin(argv, port, adminToken, write) {
|
|
|
213
225
|
write(JSON.stringify(parsed(await request(socketPath, 'GET', `/admin/sessions/${encodeURIComponent(argv[1])}/restore-diagnostics`, undefined, headers(undefined, adminToken)))));
|
|
214
226
|
return 0;
|
|
215
227
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
228
|
+
if (action !== 'groups' && action !== 'list')
|
|
229
|
+
for (let index = 1; index < argv.length; index += 2) {
|
|
230
|
+
const flag = argv[index];
|
|
231
|
+
if (!['--file', '--credential-file'].includes(flag) || !argv[index + 1] || options[flag])
|
|
232
|
+
throw new Error('Invalid integration arguments');
|
|
233
|
+
options[flag] = argv[index + 1];
|
|
234
|
+
}
|
|
222
235
|
if (action === 'groups' || action === 'list') {
|
|
223
|
-
if (argv.length !== 1)
|
|
224
|
-
throw new Error('
|
|
225
|
-
|
|
236
|
+
if (action === 'list' && argv.length !== 1 || action === 'groups' && argv.length > 2 || action === 'groups' && argv.length === 2 && argv[1] !== '--include-archived')
|
|
237
|
+
throw new Error('Usage: td integration groups [--include-archived] | list');
|
|
238
|
+
if (action === 'groups' && argv[1] === '--include-archived') {
|
|
239
|
+
const capabilities = parsed(await request(socketPath, 'GET', '/admin/capabilities', undefined, headers(undefined, adminToken)));
|
|
240
|
+
if (capabilities.admin_local_group_archive !== true)
|
|
241
|
+
throw new CollaborationError('ADMIN_LOCAL_GROUP_ARCHIVE_UNSUPPORTED', 'CLI and running service must support archived group discovery (minimum 1.4.323)', 409);
|
|
242
|
+
}
|
|
243
|
+
write(JSON.stringify(parsed(await request(socketPath, 'GET', action === 'groups' ? `/admin/groups${argv[1] ? '?include_archived=true' : ''}` : '/admin/principals', undefined, headers(undefined, adminToken)))));
|
|
226
244
|
return 0;
|
|
227
245
|
}
|
|
228
246
|
if (action === 'update') {
|
|
@@ -3,10 +3,10 @@ import { createServer } from 'node:http';
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
|
-
import { timingSafeEqual } from 'node:crypto';
|
|
6
|
+
import { createHash, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
7
7
|
import { CollaborationError, resolveIdPrefix } from './collaborationProtocol.js';
|
|
8
8
|
import { terminalMessage } from './collaborationStore.js';
|
|
9
|
-
import { IntegrationStore, INTEGRATION_PROTOCOL, INTEGRATION_OPERATIONS, integrationError } from './integrationStore.js';
|
|
9
|
+
import { IntegrationStore, INTEGRATION_PROTOCOL, INTEGRATION_OPERATIONS, atomicJson, integrationError } from './integrationStore.js';
|
|
10
10
|
import { IntegrationSessions, publicIntegrationSession } from './integrationSessions.js';
|
|
11
11
|
import { getTermdockVersion } from '../utils/version.js';
|
|
12
12
|
export function integrationSocketPath(port) { return path.join(homedir(), '.termdock', `integration-${port}.sock`); }
|
|
@@ -30,10 +30,32 @@ export class IntegrationRuntime {
|
|
|
30
30
|
sent = new Map();
|
|
31
31
|
syncing = false;
|
|
32
32
|
retry;
|
|
33
|
+
releaseBatchFile;
|
|
34
|
+
releaseBatchOperations = [];
|
|
35
|
+
releaseBatchRunning = false;
|
|
33
36
|
constructor(options) {
|
|
34
37
|
this.options = options;
|
|
35
38
|
this.store = new IntegrationStore(path.join(options.directory, 'integrations.json'));
|
|
36
39
|
this.sessions = new IntegrationSessions(path.join(options.directory, 'integration-sessions.json'), options.sessions);
|
|
40
|
+
this.releaseBatchFile = path.join(options.directory, 'session-release-batches.json');
|
|
41
|
+
try {
|
|
42
|
+
const saved = JSON.parse(fs.readFileSync(this.releaseBatchFile, 'utf8'));
|
|
43
|
+
if (!Array.isArray(saved))
|
|
44
|
+
throw new Error('Invalid batch release journal');
|
|
45
|
+
this.releaseBatchOperations = saved;
|
|
46
|
+
for (const operation of this.releaseBatchOperations) {
|
|
47
|
+
if (operation.state === 'running')
|
|
48
|
+
operation.state = 'queued';
|
|
49
|
+
for (const item of operation.items)
|
|
50
|
+
if (item.state === 'running')
|
|
51
|
+
item.state = 'queued';
|
|
52
|
+
}
|
|
53
|
+
atomicJson(this.releaseBatchFile, this.releaseBatchOperations);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error.code !== 'ENOENT')
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
37
59
|
this.stops.push(options.messages.registerRuntimeGuard(id => this.sessions.assertRuntimeMessage(id)));
|
|
38
60
|
const changed = () => { try {
|
|
39
61
|
this.sync();
|
|
@@ -68,7 +90,7 @@ export class IntegrationRuntime {
|
|
|
68
90
|
if (!expected.length || given.length !== expected.length || !timingSafeEqual(given, expected))
|
|
69
91
|
integrationError('INTEGRATION_ADMIN_REQUIRED', 'Local administration credential is required', 401);
|
|
70
92
|
};
|
|
71
|
-
app.get('/admin/groups', run(req => { admin(req); return { groups: options.messages.
|
|
93
|
+
app.get('/admin/groups', run(req => { admin(req); return { groups: options.messages.listLocalGroupMetadata(req.query.include_archived === 'true') }; }));
|
|
72
94
|
app.get('/admin/groups/:id', run(req => { admin(req); return { group: options.messages.getLocalGroupMetadata(req.params.id) }; }));
|
|
73
95
|
app.post('/admin/groups/:id/rename', run(req => {
|
|
74
96
|
admin(req);
|
|
@@ -78,6 +100,22 @@ export class IntegrationRuntime {
|
|
|
78
100
|
}
|
|
79
101
|
return options.messages.renameLocalGroup(req.params.id, body.name, body.expected_updated_at, body.idempotency_key);
|
|
80
102
|
}));
|
|
103
|
+
app.post('/admin/groups/:id/archive', run(req => {
|
|
104
|
+
admin(req);
|
|
105
|
+
const body = req.body;
|
|
106
|
+
if (!body || typeof body !== 'object' || Array.isArray(body) || Object.keys(body).some(key => !['expected_updated_at', 'idempotency_key'].includes(key))) {
|
|
107
|
+
integrationError('INVALID_GROUP_ARCHIVE_REQUEST', 'Specify only expected_updated_at and idempotency_key');
|
|
108
|
+
}
|
|
109
|
+
return options.messages.setLocalGroupArchived(req.params.id, true, body.expected_updated_at, body.idempotency_key);
|
|
110
|
+
}));
|
|
111
|
+
app.post('/admin/groups/:id/unarchive', run(req => {
|
|
112
|
+
admin(req);
|
|
113
|
+
const body = req.body;
|
|
114
|
+
if (!body || typeof body !== 'object' || Array.isArray(body) || Object.keys(body).some(key => !['expected_updated_at', 'idempotency_key'].includes(key))) {
|
|
115
|
+
integrationError('INVALID_GROUP_ARCHIVE_REQUEST', 'Specify only expected_updated_at and idempotency_key');
|
|
116
|
+
}
|
|
117
|
+
return options.messages.setLocalGroupArchived(req.params.id, false, body.expected_updated_at, body.idempotency_key);
|
|
118
|
+
}));
|
|
81
119
|
app.post('/admin/groups', run(req => {
|
|
82
120
|
admin(req);
|
|
83
121
|
const body = req.body;
|
|
@@ -216,6 +254,19 @@ export class IntegrationRuntime {
|
|
|
216
254
|
app.post('/sessions/:id/restore', run(async (req) => { const p = principal(req); this.authorize(p, 'session.restore'); const session = await this.sessions.restore(p, req.params.id, req.body?.idempotency_key); this.sync(); return { session: publicIntegrationSession(session) }; }));
|
|
217
255
|
app.post('/sessions/:id/retry-cold', run(async (req) => { const p = principal(req); this.authorize(p, 'session.create'); const session = await this.sessions.retryCold(p, req.params.id, req.body?.generation, req.body?.idempotency_key); this.sync(); return { session: publicIntegrationSession(session) }; }));
|
|
218
256
|
app.post('/sessions/:id/release', run(async (req) => { const p = principal(req); this.authorize(p, 'session.release'); const session = await this.sessions.release(p, req.params.id, req.body?.generation, req.body?.idempotency_key); this.sync(); return { session: publicIntegrationSession(session) }; }));
|
|
257
|
+
app.post('/sessions/release-batch', run(async (req) => {
|
|
258
|
+
const p = principal(req);
|
|
259
|
+
this.authorize(p, 'session.release');
|
|
260
|
+
return this.createReleaseBatch(p, req.body?.session_ids, req.body?.idempotency_key);
|
|
261
|
+
}));
|
|
262
|
+
app.get('/sessions/release-batches/:id', run(req => {
|
|
263
|
+
const p = principal(req);
|
|
264
|
+
this.authorize(p, 'session.release');
|
|
265
|
+
const operation = this.releaseBatchOperations.find(item => item.id === req.params.id && item.principal_id === p.id && item.group_id === p.groupId);
|
|
266
|
+
if (!operation)
|
|
267
|
+
integrationError('RELEASE_BATCH_NOT_FOUND', 'Batch release operation was not found', 404);
|
|
268
|
+
return { operation: structuredClone(operation) };
|
|
269
|
+
}));
|
|
219
270
|
app.post('/sessions/:id/rename', run(async (req) => { const p = principal(req); this.authorize(p, 'session.rename'); const session = await this.sessions.rename(p, req.params.id, req.body?.generation, req.body?.name, req.body?.idempotency_key); this.sync(); return { session: publicIntegrationSession(session) }; }));
|
|
220
271
|
app.post('/sessions/:id/close', run(async (req) => { const p = principal(req); this.authorize(p, 'session.close'); const session = await this.sessions.closeSession(p, req.params.id, req.body?.generation, req.body?.idempotency_key); this.sync(); return { session: publicIntegrationSession(session) }; }));
|
|
221
272
|
app.get('/events', run((req, res) => {
|
|
@@ -254,13 +305,103 @@ export class IntegrationRuntime {
|
|
|
254
305
|
session_close: true, session_close_min_version: '1.4.315',
|
|
255
306
|
admin_local_group_create: true, admin_local_group_create_min_version: '1.4.318',
|
|
256
307
|
admin_local_group_rename: true, admin_local_group_rename_min_version: '1.4.319',
|
|
308
|
+
admin_local_group_archive: true, admin_local_group_archive_min_version: '1.4.323',
|
|
257
309
|
integration_policy_update: true,
|
|
258
310
|
session_restore_diagnostics: true,
|
|
259
311
|
native_identity_linux_flock_owner: process.platform === 'linux',
|
|
260
312
|
background_task_records: true, task_purpose_configuration: true, explicit_execution_state: true,
|
|
261
313
|
integration_message_send: true, integration_message_reply: true, integration_message_history: true,
|
|
314
|
+
session_release_batch: true, session_release_batch_min_version: getTermdockVersion(),
|
|
262
315
|
permissions: INTEGRATION_OPERATIONS };
|
|
263
316
|
}
|
|
317
|
+
async createReleaseBatch(principal, rawSessionIds, rawKey) {
|
|
318
|
+
if (!Array.isArray(rawSessionIds) || rawSessionIds.length < 1 || rawSessionIds.length > 100
|
|
319
|
+
|| rawSessionIds.some(id => typeof id !== 'string' || !/^[a-zA-Z0-9._-]{1,80}$/.test(id))) {
|
|
320
|
+
integrationError('INVALID_RELEASE_BATCH', 'session_ids must contain 1–100 exact session IDs');
|
|
321
|
+
}
|
|
322
|
+
if (typeof rawKey !== 'string' || !rawKey.trim() || rawKey.length > 160)
|
|
323
|
+
integrationError('INVALID_IDEMPOTENCY_KEY', 'A 1–160 character idempotency_key is required');
|
|
324
|
+
const sessionIds = [...new Set(rawSessionIds)].sort();
|
|
325
|
+
const keyHash = createHash('sha256').update(`${principal.id}:${rawKey}`).digest('hex');
|
|
326
|
+
const requestHash = createHash('sha256').update(JSON.stringify([principal.groupId, sessionIds])).digest('hex');
|
|
327
|
+
const existing = this.releaseBatchOperations.find(operation => operation.idempotency_key === keyHash);
|
|
328
|
+
if (existing) {
|
|
329
|
+
if (existing.request_hash !== requestHash)
|
|
330
|
+
integrationError('IDEMPOTENCY_CONFLICT', 'This batch release key was used with different sessions', 409);
|
|
331
|
+
return { accepted: true, operation: structuredClone(existing), replayed: true };
|
|
332
|
+
}
|
|
333
|
+
if (this.releaseBatchOperations.length >= 1000)
|
|
334
|
+
integrationError('RELEASE_BATCH_HISTORY_FULL', 'Batch release history is full; reconcile retained operations before creating another', 503);
|
|
335
|
+
const items = [];
|
|
336
|
+
for (const sessionId of sessionIds) {
|
|
337
|
+
const session = await this.sessions.get(principal, sessionId);
|
|
338
|
+
items.push({ session_id: sessionId, generation: session.generation ?? 1, state: 'queued' });
|
|
339
|
+
}
|
|
340
|
+
const now = Date.now();
|
|
341
|
+
const operation = { id: randomUUID(), principal_id: principal.id, group_id: principal.groupId,
|
|
342
|
+
idempotency_key: keyHash, request_hash: requestHash, state: 'queued', created_at: now, updated_at: now, items };
|
|
343
|
+
this.releaseBatchOperations.push(operation);
|
|
344
|
+
try {
|
|
345
|
+
atomicJson(this.releaseBatchFile, this.releaseBatchOperations);
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
this.releaseBatchOperations = this.releaseBatchOperations.filter(item => item.id !== operation.id);
|
|
349
|
+
throw error;
|
|
350
|
+
}
|
|
351
|
+
void this.pumpReleaseBatches();
|
|
352
|
+
return { accepted: true, operation: structuredClone(operation), replayed: false };
|
|
353
|
+
}
|
|
354
|
+
async pumpReleaseBatches() {
|
|
355
|
+
if (this.releaseBatchRunning)
|
|
356
|
+
return;
|
|
357
|
+
this.releaseBatchRunning = true;
|
|
358
|
+
try {
|
|
359
|
+
for (;;) {
|
|
360
|
+
const operation = this.releaseBatchOperations.find(item => item.state === 'queued' || item.state === 'running');
|
|
361
|
+
if (!operation)
|
|
362
|
+
break;
|
|
363
|
+
operation.state = 'running';
|
|
364
|
+
operation.updated_at = Date.now();
|
|
365
|
+
atomicJson(this.releaseBatchFile, this.releaseBatchOperations);
|
|
366
|
+
for (const item of operation.items) {
|
|
367
|
+
if (item.state !== 'queued')
|
|
368
|
+
continue;
|
|
369
|
+
item.state = 'running';
|
|
370
|
+
operation.updated_at = Date.now();
|
|
371
|
+
atomicJson(this.releaseBatchFile, this.releaseBatchOperations);
|
|
372
|
+
try {
|
|
373
|
+
const policy = this.store.policies().find(candidate => candidate.id === operation.principal_id && candidate.groupId === operation.group_id && !candidate.revoked);
|
|
374
|
+
if (!policy)
|
|
375
|
+
throw Object.assign(new Error('Integration principal was revoked before release'), { code: 'INTEGRATION_REVOKED' });
|
|
376
|
+
const activePrincipal = policy;
|
|
377
|
+
this.store.authorize(activePrincipal, 'session.release');
|
|
378
|
+
const session = await this.sessions.release(activePrincipal, item.session_id, item.generation, `${operation.idempotency_key}:${item.session_id}`);
|
|
379
|
+
if (session.state !== 'released')
|
|
380
|
+
throw Object.assign(new Error(session.error_code ?? 'Session release did not reach released state'), { code: session.error_code ?? 'SESSION_RELEASE_FAILED' });
|
|
381
|
+
item.state = 'released';
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
const value = error;
|
|
385
|
+
item.state = 'failed';
|
|
386
|
+
item.error_code = typeof value?.code === 'string' ? value.code : 'SESSION_RELEASE_FAILED';
|
|
387
|
+
item.error = (error instanceof Error ? error.message : String(error)).slice(0, 500);
|
|
388
|
+
}
|
|
389
|
+
operation.updated_at = Date.now();
|
|
390
|
+
atomicJson(this.releaseBatchFile, this.releaseBatchOperations);
|
|
391
|
+
}
|
|
392
|
+
operation.state = operation.items.some(item => item.state === 'failed') ? 'partial_failure' : 'succeeded';
|
|
393
|
+
operation.updated_at = Date.now();
|
|
394
|
+
atomicJson(this.releaseBatchFile, this.releaseBatchOperations);
|
|
395
|
+
this.sync();
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
catch (error) {
|
|
399
|
+
console.error('[integration-release-batch] worker stopped; persisted operation will reconcile on restart:', error);
|
|
400
|
+
}
|
|
401
|
+
finally {
|
|
402
|
+
this.releaseBatchRunning = false;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
264
405
|
error(error) {
|
|
265
406
|
return { type: 'error', ok: false, code: error instanceof CollaborationError ? error.code : 'INTEGRATION_INTERNAL_ERROR',
|
|
266
407
|
...(error instanceof CollaborationError && error.code === 'GROUP_CHANGED' && Number.isSafeInteger(error.details?.currentUpdatedAt)
|
|
@@ -458,6 +599,7 @@ export class IntegrationRuntime {
|
|
|
458
599
|
await new Promise((resolve, reject) => { this.server.once('error', reject); this.server.listen(this.options.socketPath, () => { this.server.removeListener('error', reject); fs.chmodSync(this.options.socketPath, 0o600); resolve(); }); });
|
|
459
600
|
this.sync();
|
|
460
601
|
await this.sessions.refreshAll();
|
|
602
|
+
void this.pumpReleaseBatches();
|
|
461
603
|
}
|
|
462
604
|
async close() {
|
|
463
605
|
for (const stop of this.stops)
|