termdock 1.4.174 → 1.4.176
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/.desktop-dist/main.js +154 -7
- package/dist/client/assets/App-BXYZyI_k.js +150 -0
- package/dist/client/assets/App-BXYZyI_k.js.br +0 -0
- package/dist/client/assets/App-BXYZyI_k.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-DeVtCf73.js +2 -0
- package/dist/client/assets/ChangeWalkthroughPanel-DeVtCf73.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-DeVtCf73.js.gz +0 -0
- package/dist/client/assets/DagPlayground-ZIqLZoMo.js +1 -0
- package/dist/client/assets/DagPlayground-ZIqLZoMo.js.br +0 -0
- package/dist/client/assets/DagPlayground-ZIqLZoMo.js.gz +0 -0
- package/dist/client/assets/{DiffLab-CdbPsjKS.js → DiffLab-CSMf8So2.js} +2 -2
- package/dist/client/assets/DiffLab-CSMf8So2.js.br +0 -0
- package/dist/client/assets/DiffLab-CSMf8So2.js.gz +0 -0
- package/dist/client/assets/DiffReview-CE5lW1zQ.js +1 -0
- package/dist/client/assets/DiffReview-CE5lW1zQ.js.br +0 -0
- package/dist/client/assets/DiffReview-CE5lW1zQ.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-CpUW75fZ.js +2 -0
- package/dist/client/assets/DiffReviewLab-CpUW75fZ.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-CpUW75fZ.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-hdxcM9yF.js → DiffViewer-BomvZ4kq.js} +5 -5
- package/dist/client/assets/DiffViewer-BomvZ4kq.js.br +0 -0
- package/dist/client/assets/DiffViewer-BomvZ4kq.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-BZ454pgM.js → ModelPreview-BWdZFyhh.js} +3 -3
- package/dist/client/assets/ModelPreview-BWdZFyhh.js.br +0 -0
- package/dist/client/assets/ModelPreview-BWdZFyhh.js.gz +0 -0
- package/dist/client/assets/RightSidebar-CYVwFbOc.js +79 -0
- package/dist/client/assets/RightSidebar-CYVwFbOc.js.br +0 -0
- package/dist/client/assets/RightSidebar-CYVwFbOc.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-C185Omck.js → abnfDiagram-VCTEODGH-BIPsLStE.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-BIPsLStE.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-BIPsLStE.js.gz +0 -0
- package/dist/client/assets/app-D6KzR8B3.js +34 -0
- package/dist/client/assets/app-D6KzR8B3.js.br +0 -0
- package/dist/client/assets/app-D6KzR8B3.js.gz +0 -0
- package/dist/client/assets/{arc-DNt-DTCl.js → arc-DzeT6hBj.js} +1 -1
- package/dist/client/assets/arc-DzeT6hBj.js.br +0 -0
- package/dist/client/assets/arc-DzeT6hBj.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-C5Nwm_Qz.js → architectureDiagram-5GKGNRK7-B1qsxd_5.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-B1qsxd_5.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-B1qsxd_5.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-C5aR_7V9.js → blockDiagram-I7D4REHJ-NLsa29Jp.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-NLsa29Jp.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-NLsa29Jp.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-DjV6ZNwd.js → c4Diagram-7LVT6UL2-DBAUvou3.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-DBAUvou3.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DBAUvou3.js.gz +0 -0
- package/dist/client/assets/channel-BhwBAWhq.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-Ct7nd4cL.js → chunk-2Q5K7J3B-BsVAXGLa.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-CXem4UYP.js → chunk-5VM5RSS4-BKtILsst.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-DyMNEawE.js → chunk-F27PBJKO-Dn9zcQZG.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-Dn9zcQZG.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-Dn9zcQZG.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-BsW7iIrh.js → chunk-IMKFNOWR-MVtVQeTr.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-MVtVQeTr.js.br +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-BsW7iIrh.js.gz → chunk-IMKFNOWR-MVtVQeTr.js.gz} +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-EO8E1q1J.js → chunk-JWPE2WC7-DliIIMe7.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-0One8T9x.js → chunk-POPQ4Y6H-XBSus3IC.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-CDu41dkj.js → chunk-SVP7TREG-wleSQcJR.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-wleSQcJR.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-wleSQcJR.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-BACjgX9v.js → chunk-TICWLB2K-Dq6LdswN.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-Dq6LdswN.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-Dq6LdswN.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-BJWowF6E.js → chunk-XXDRQBXY-xafyKlM-.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-slQB0ns2.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-slQB0ns2.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-WTwHOCdm.js → cose-bilkent-JH36ORCC-BR7Ohnm_.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-BR7Ohnm_.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-BR7Ohnm_.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-D4y8TkaO.js → cynefin-OW5HDTMX-CmPjQgER.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-CmPjQgER.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CmPjQgER.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-BzN8Ggk_.js → cynefinDiagram-5FMLGOSQ-CFyR9RvM.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CFyR9RvM.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CFyR9RvM.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-CwMYtt5E.js → dagre-GXQ25YYZ-D_RVMRO1.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-D_RVMRO1.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-D_RVMRO1.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-BnTeHiAb.js → diagram-S7CK7UJ4-6fr-ZoPj.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-6fr-ZoPj.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-6fr-ZoPj.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-BNlh251g.js → diagram-UQ7AKVKN-CgwiUtz0.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-CgwiUtz0.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-CgwiUtz0.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-B5FftXkQ.js → diagram-VSXAHHWV-CLxzTB9k.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-CLxzTB9k.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CLxzTB9k.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-C51ZNXR2.js → diagram-VX7I27RA-DjxDAvdZ.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-DjxDAvdZ.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-DjxDAvdZ.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-CXoZH2qf.js → diagram-Z3DM3KII-CDPOjiz4.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-CDPOjiz4.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-CDPOjiz4.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-C7YFLlx4.js → ebnfDiagram-PWID7BFC-BLbwyF1O.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-BLbwyF1O.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-BLbwyF1O.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-BNQlS7-g.js → erDiagram-RLTQ6QDP-DbjtZnJx.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-DbjtZnJx.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-DbjtZnJx.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-DIQXZ06I.js → flowDiagram-HODETNUW-BirJInAN.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-BirJInAN.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BirJInAN.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-BaJQfOqD.js → ganttDiagram-EL5Y4UJY-d6Q9vSbv.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-d6Q9vSbv.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-d6Q9vSbv.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-C2J0D0kV.css +1 -0
- package/dist/client/assets/gestureArbiter-C2J0D0kV.css.br +0 -0
- package/dist/client/assets/gestureArbiter-C2J0D0kV.css.gz +0 -0
- package/dist/client/assets/gestureArbiter-D1SNS_gK.js +1 -0
- package/dist/client/assets/gestureArbiter-D1SNS_gK.js.br +0 -0
- package/dist/client/assets/gestureArbiter-D1SNS_gK.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-tQ4E6pwN.js → gitGraphDiagram-WWUBYQGX-BWBoIeS6.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BWBoIeS6.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BWBoIeS6.js.gz +0 -0
- package/dist/client/assets/{index-DlxiaPv1.css → index-DUI8KRf6.css} +1 -1
- package/dist/client/assets/index-DUI8KRf6.css.br +0 -0
- package/dist/client/assets/index-DUI8KRf6.css.gz +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-DTOUeQrN.js → infoDiagram-27XIBGKW-BH4AynkM.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-8sBi-gla.js → ishikawaDiagram-5VMMS53U-BQw90AKR.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-8sBi-gla.js.br → ishikawaDiagram-5VMMS53U-BQw90AKR.js.br} +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BQw90AKR.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-ZwikziWh.js → journeyDiagram-3NMN7TZE-DISkQocf.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DISkQocf.js.br +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-ZwikziWh.js.gz → journeyDiagram-3NMN7TZE-DISkQocf.js.gz} +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-wjuw2jQ4.js → kanban-definition-UXKFOSKX-Qg2LyEVy.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-Qg2LyEVy.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-Qg2LyEVy.js.gz +0 -0
- package/dist/client/assets/{layout-nASMIj-E.js → layout-Cd7-4_Vr.js} +1 -1
- package/dist/client/assets/layout-Cd7-4_Vr.js.br +0 -0
- package/dist/client/assets/layout-Cd7-4_Vr.js.gz +0 -0
- package/dist/client/assets/{linear-pGaQeqW5.js → linear-DFTi2fTn.js} +1 -1
- package/dist/client/assets/linear-DFTi2fTn.js.br +0 -0
- package/dist/client/assets/linear-DFTi2fTn.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-CKEBWbQz.js → mermaid.core-2v726lUU.js} +5 -5
- package/dist/client/assets/mermaid.core-2v726lUU.js.br +0 -0
- package/dist/client/assets/mermaid.core-2v726lUU.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-CBjYcnwd.js → mindmap-definition-YA3MSWOX-_FpwteCy.js} +4 -4
- package/dist/client/assets/mindmap-definition-YA3MSWOX-_FpwteCy.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-_FpwteCy.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-D9Ffc7NY.js +2 -0
- package/dist/client/assets/passwordBootstrap-D9Ffc7NY.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-D9Ffc7NY.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-DJqvclIp.js → pegDiagram-XKGWAZYB-DLFf-HmZ.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-DLFf-HmZ.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DLFf-HmZ.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-DLV4WPwc.js → pieDiagram-E7YTZNPT-DqTnfXHG.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-DqTnfXHG.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-DqTnfXHG.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-BN7Zpsb9.js → quadrantDiagram-AXDQQJYC-B_oBH52Q.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-B_oBH52Q.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-B_oBH52Q.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-DfHX6MUm.js → railroadDiagram-O6MQD6OU-HxFQ7Pce.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-HxFQ7Pce.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-HxFQ7Pce.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-B3NGIjvv.js +1 -0
- package/dist/client/assets/referenceLongPress-B3NGIjvv.js.br +0 -0
- package/dist/client/assets/referenceLongPress-B3NGIjvv.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-tQVSFg_k.js → requirementDiagram-BXWQKSXE-NZdfaZFk.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-NZdfaZFk.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-NZdfaZFk.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-C7JW1-Wh.js → sankeyDiagram-P5KCCOFB-BgCctv6X.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-BgCctv6X.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-BgCctv6X.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-BVeDiFDI.js → sequenceDiagram-WJ2MYXX4-Br42Za1R.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Br42Za1R.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Br42Za1R.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-CBRaCpeP.js → sizeCapture-INFHLROL-FT4z4S1b.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-FT4z4S1b.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-FT4z4S1b.js.gz +0 -0
- package/dist/client/assets/sparkles-BaGaevME.js +1 -0
- package/dist/client/assets/{stateDiagram-D77RDMKH-CiLpGRuf.js → stateDiagram-D77RDMKH-DD0t9f84.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-DD0t9f84.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-DD0t9f84.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BhvVVB56.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-BkzVtkVY.js → swimlanes-42K2YHIH-DcC8pGLx.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-DcC8pGLx.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-DcC8pGLx.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DSC5lz0i.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-DKyfGITP.js → timeline-definition-24CTP7MA-DnwO_Pxd.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-DnwO_Pxd.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-DnwO_Pxd.js.gz +0 -0
- package/dist/client/assets/triangle-alert-BCQO9Ap9.js +1 -0
- package/dist/client/assets/{vennDiagram-4TSXK5OY-CRgkkrgx.js → vennDiagram-4TSXK5OY-IVNSrZDM.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-IVNSrZDM.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-IVNSrZDM.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-Cr7yS4CZ.js → wardleyDiagram-VM6X3IG4-BnGetaQL.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BnGetaQL.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BnGetaQL.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-DkPYl6E0.js → xychartDiagram-S5SC5T6Z-C01eBN-W.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-C01eBN-W.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-C01eBN-W.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/preview-shell.html +1 -0
- package/dist/client/preview-shell.js +20 -0
- package/dist/client/preview-shell.js.br +0 -0
- package/dist/client/preview-shell.js.gz +0 -0
- package/dist/client/sw-secure-api.js +132 -0
- package/dist/client/sw-secure-api.js.br +0 -0
- package/dist/client/sw-secure-api.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/collaborationCli.js +298 -0
- package/dist/server/agent/collaborationPrompt.js +10 -2
- package/dist/server/agent/collaborationProtocol.js +50 -0
- package/dist/server/agent/collaborationRoutes.js +86 -0
- package/dist/server/agent/collaborationStore.js +249 -21
- package/dist/server/cli.js +28 -168
- package/dist/server/entry.js +114 -51
- package/dist/server/federation/accessPolicy.js +122 -0
- package/dist/server/federation/authorization.js +226 -0
- package/dist/server/federation/bootstrapCors.js +23 -0
- package/dist/server/federation/cli.js +185 -0
- package/dist/server/federation/deviceNames.js +29 -0
- package/dist/server/federation/directRoutes.js +72 -0
- package/dist/server/federation/invitations.js +123 -0
- package/dist/server/federation/openAccess.js +25 -0
- package/dist/server/federation/originPolicy.js +19 -0
- package/dist/server/federation/packets.js +116 -0
- package/dist/server/federation/passwordBootstrap.js +86 -0
- package/dist/server/federation/passwordLoginRoutes.js +98 -0
- package/dist/server/federation/relay.js +346 -0
- package/dist/server/federation/requestContext.js +4 -0
- package/dist/server/federation/routeAccess.js +124 -0
- package/dist/server/federation/routeInvitations.js +160 -0
- package/dist/server/federation/runtime.js +537 -0
- package/dist/server/federation/secureProtocol.js +223 -0
- package/dist/server/federation/socketDuplex.js +42 -0
- package/dist/server/federation/streamLimits.js +5 -0
- package/dist/server/routes/filesystem.js +2 -1
- package/dist/server/routes/terminal.js +40 -72
- package/dist/server/utils/authProtection.js +13 -0
- package/dist/server/utils/csrfProtection.js +3 -0
- package/dist/server/utils/publicSecurity.js +8 -3
- package/package.json +10 -3
- package/runtime-manifest.json +4 -4
- package/dist/client/assets/ChangeWalkthroughPanel-hj8I8Cwy.js +0 -2
- package/dist/client/assets/ChangeWalkthroughPanel-hj8I8Cwy.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-hj8I8Cwy.js.gz +0 -0
- package/dist/client/assets/DagPlayground-BRoB8JX5.js +0 -1
- package/dist/client/assets/DagPlayground-BRoB8JX5.js.br +0 -0
- package/dist/client/assets/DagPlayground-BRoB8JX5.js.gz +0 -0
- package/dist/client/assets/DiffLab-CdbPsjKS.js.br +0 -0
- package/dist/client/assets/DiffLab-CdbPsjKS.js.gz +0 -0
- package/dist/client/assets/DiffReview-MQXjrRsW.js +0 -1
- package/dist/client/assets/DiffReview-MQXjrRsW.js.br +0 -0
- package/dist/client/assets/DiffReview-MQXjrRsW.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-C47CNvDM.js +0 -2
- package/dist/client/assets/DiffReviewLab-C47CNvDM.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-C47CNvDM.js.gz +0 -0
- package/dist/client/assets/DiffViewer-hdxcM9yF.js.br +0 -0
- package/dist/client/assets/DiffViewer-hdxcM9yF.js.gz +0 -0
- package/dist/client/assets/ModelPreview-BZ454pgM.js.br +0 -0
- package/dist/client/assets/ModelPreview-BZ454pgM.js.gz +0 -0
- package/dist/client/assets/RightSidebar-i0Oq68YI.js +0 -55
- package/dist/client/assets/RightSidebar-i0Oq68YI.js.br +0 -0
- package/dist/client/assets/RightSidebar-i0Oq68YI.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-C185Omck.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-C185Omck.js.gz +0 -0
- package/dist/client/assets/app-DAp8rVMc.js +0 -175
- package/dist/client/assets/app-DAp8rVMc.js.br +0 -0
- package/dist/client/assets/app-DAp8rVMc.js.gz +0 -0
- package/dist/client/assets/arc-DNt-DTCl.js.br +0 -0
- package/dist/client/assets/arc-DNt-DTCl.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-C5Nwm_Qz.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-C5Nwm_Qz.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-C5aR_7V9.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-C5aR_7V9.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DjV6ZNwd.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DjV6ZNwd.js.gz +0 -0
- package/dist/client/assets/channel-uhLrATdy.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-DyMNEawE.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-DyMNEawE.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-BsW7iIrh.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-CDu41dkj.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-CDu41dkj.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-BACjgX9v.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-BACjgX9v.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-CnxFfzm9.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-CnxFfzm9.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-WTwHOCdm.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-WTwHOCdm.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-D4y8TkaO.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-D4y8TkaO.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BzN8Ggk_.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BzN8Ggk_.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-CwMYtt5E.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-CwMYtt5E.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-BnTeHiAb.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-BnTeHiAb.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BNlh251g.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BNlh251g.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-B5FftXkQ.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-B5FftXkQ.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-C51ZNXR2.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-C51ZNXR2.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-CXoZH2qf.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-CXoZH2qf.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-C7YFLlx4.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-C7YFLlx4.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-BNQlS7-g.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-BNQlS7-g.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-DIQXZ06I.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-DIQXZ06I.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-BaJQfOqD.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-BaJQfOqD.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-tQ4E6pwN.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-tQ4E6pwN.js.gz +0 -0
- package/dist/client/assets/index-DlxiaPv1.css.br +0 -0
- package/dist/client/assets/index-DlxiaPv1.css.gz +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-8sBi-gla.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-ZwikziWh.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-wjuw2jQ4.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-wjuw2jQ4.js.gz +0 -0
- package/dist/client/assets/layout-nASMIj-E.js.br +0 -0
- package/dist/client/assets/layout-nASMIj-E.js.gz +0 -0
- package/dist/client/assets/linear-pGaQeqW5.js.br +0 -0
- package/dist/client/assets/linear-pGaQeqW5.js.gz +0 -0
- package/dist/client/assets/mermaid.core-CKEBWbQz.js.br +0 -0
- package/dist/client/assets/mermaid.core-CKEBWbQz.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-CBjYcnwd.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-CBjYcnwd.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DJqvclIp.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DJqvclIp.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-DLV4WPwc.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-DLV4WPwc.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-BN7Zpsb9.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-BN7Zpsb9.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DfHX6MUm.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DfHX6MUm.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-tQVSFg_k.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-tQVSFg_k.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-C7JW1-Wh.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-C7JW1-Wh.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BVeDiFDI.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BVeDiFDI.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-CBRaCpeP.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-CBRaCpeP.js.gz +0 -0
- package/dist/client/assets/sparkles-D0cwIvB0.js +0 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-CiLpGRuf.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-CiLpGRuf.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BRbS0rv2.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-BkzVtkVY.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-BkzVtkVY.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-CeWFezkt.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-DKyfGITP.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-DKyfGITP.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-CRgkkrgx.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-CRgkkrgx.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cr7yS4CZ.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cr7yS4CZ.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-DkPYl6E0.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-DkPYl6E0.js.gz +0 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import crypto from 'node:crypto';
|
|
4
|
+
import { COLLAB_LIMITS, CollaborationError, STATUS_RANK, validateExtras } from './collaborationProtocol.js';
|
|
4
5
|
const MAX_MESSAGES = 2_000;
|
|
5
6
|
const MESSAGE_KINDS = new Set(['message', 'ask', 'reply', 'task', 'handoff', 'done']);
|
|
6
7
|
export class CollaborationStore {
|
|
7
8
|
filePath;
|
|
8
9
|
document = { version: 2, groups: [], messages: [] };
|
|
10
|
+
persistedDocument = JSON.stringify(this.document);
|
|
9
11
|
constructor(filePath) {
|
|
10
12
|
this.filePath = filePath;
|
|
11
13
|
try {
|
|
@@ -14,10 +16,21 @@ export class CollaborationStore {
|
|
|
14
16
|
this.document.groups = parsed.groups.filter((group) => group && typeof group.id === 'string' && Array.isArray(group.sessionIds));
|
|
15
17
|
}
|
|
16
18
|
if (Array.isArray(parsed.messages)) {
|
|
17
|
-
this.document.messages = parsed.messages.filter((message) => message && typeof message.id === 'string' && typeof message.toSessionId === 'string' && MESSAGE_KINDS.has(message.kind))
|
|
19
|
+
this.document.messages = parsed.messages.filter((message) => message && typeof message.id === 'string' && typeof message.toSessionId === 'string' && MESSAGE_KINDS.has(message.kind));
|
|
20
|
+
for (const message of this.document.messages) {
|
|
21
|
+
message.sequence ??= (this.document.sequence ?? 0) + 1;
|
|
22
|
+
this.document.sequence = Math.max(this.document.sequence ?? 0, message.sequence);
|
|
23
|
+
}
|
|
18
24
|
}
|
|
25
|
+
this.document.sequence = Math.max(parsed.sequence ?? 0, this.document.sequence ?? 0);
|
|
26
|
+
this.document.idempotency = parsed.idempotency ?? {};
|
|
27
|
+
this.document.consumers = parsed.consumers ?? {};
|
|
28
|
+
this.document.transport = parsed.transport ?? {};
|
|
29
|
+
this.document.fragments = parsed.fragments ?? {};
|
|
30
|
+
this.document.prunedThrough = parsed.prunedThrough ?? {};
|
|
19
31
|
}
|
|
20
32
|
catch { /* first run */ }
|
|
33
|
+
this.persistedDocument = JSON.stringify(this.document);
|
|
21
34
|
}
|
|
22
35
|
list() {
|
|
23
36
|
return this.document.groups.filter((group) => !group.deleted).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
@@ -92,21 +105,41 @@ export class CollaborationStore {
|
|
|
92
105
|
throw new Error('协作组不存在');
|
|
93
106
|
if (!MESSAGE_KINDS.has(input.kind))
|
|
94
107
|
throw new Error('消息类型无效');
|
|
95
|
-
const
|
|
96
|
-
|
|
108
|
+
const extras = validateExtras(input);
|
|
109
|
+
const content = input.content;
|
|
110
|
+
if (Buffer.byteLength(content) > COLLAB_LIMITS.message_bytes)
|
|
111
|
+
throw new CollaborationError('MESSAGE_TOO_LARGE', `Message exceeds ${COLLAB_LIMITS.message_bytes} UTF-8 bytes`, 413);
|
|
112
|
+
if (!content.trim())
|
|
97
113
|
throw new Error('消息不能为空');
|
|
98
114
|
const recipients = Array.from(new Set(input.toSessionIds)).filter((id) => group.sessionIds.includes(id) && id !== input.fromSessionId);
|
|
99
115
|
if (recipients.length === 0)
|
|
100
116
|
throw new Error('没有有效的接收会话');
|
|
101
117
|
const now = Date.now();
|
|
118
|
+
const key = input.idempotencyKey ? JSON.stringify([input.groupId, input.fromSessionId, input.idempotencyKey]) : null;
|
|
119
|
+
const hash = crypto.createHash('sha256').update(stableJson([recipients.slice().sort(), input.kind, content, input.threadId ?? null, input.replyTo ?? null, extras.responseKind, extras.metadata, extras.task, extras.expiresAt])).digest('hex');
|
|
120
|
+
const previous = key ? this.document.idempotency?.[key] : null;
|
|
121
|
+
if (previous && previous.expiresAt > now) {
|
|
122
|
+
if (previous.hash !== hash)
|
|
123
|
+
throw new CollaborationError('IDEMPOTENCY_CONFLICT', 'This key was already used with a different payload', 409);
|
|
124
|
+
const originals = previous.ids.map((id) => this.getMessage(id));
|
|
125
|
+
if (originals.some((message) => !message))
|
|
126
|
+
throw new CollaborationError('IDEMPOTENCY_RECORD_GONE', 'Original message removed; this key cannot be reused yet', 409);
|
|
127
|
+
return originals;
|
|
128
|
+
}
|
|
102
129
|
const threadId = input.threadId?.trim() || crypto.randomUUID();
|
|
103
130
|
const messages = recipients.map((toSessionId) => ({
|
|
131
|
+
idempotencyKey: input.idempotencyKey, responseKind: extras.responseKind, metadata: extras.metadata, task: extras.task, expiresAt: extras.expiresAt,
|
|
132
|
+
sequence: this.nextSequence(),
|
|
104
133
|
id: crypto.randomUUID(), groupId: group.id, fromSessionId: input.fromSessionId,
|
|
105
134
|
toSessionId, kind: input.kind, content, threadId,
|
|
106
135
|
replyTo: input.replyTo?.trim() || null, status: 'pending', createdAt: now,
|
|
107
136
|
deliveredAt: null, readAt: null,
|
|
108
137
|
}));
|
|
109
|
-
|
|
138
|
+
if (messages.some((message) => Buffer.byteLength(JSON.stringify(message)) > COLLAB_LIMITS.wire_bytes))
|
|
139
|
+
throw new CollaborationError('MESSAGE_WIRE_TOO_LARGE', `Encoded message exceeds ${COLLAB_LIMITS.wire_bytes} JSON bytes`, 413);
|
|
140
|
+
this.document.messages.push(...messages);
|
|
141
|
+
if (key)
|
|
142
|
+
(this.document.idempotency ??= {})[key] = { hash, ids: messages.map((message) => message.id), expiresAt: now + COLLAB_LIMITS.idempotency_retention_ms };
|
|
110
143
|
this.persist();
|
|
111
144
|
return messages;
|
|
112
145
|
}
|
|
@@ -114,12 +147,18 @@ export class CollaborationStore {
|
|
|
114
147
|
return this.document.messages.filter((message) => message.groupId === groupId).slice(-Math.max(1, Math.min(limit, 500)));
|
|
115
148
|
}
|
|
116
149
|
getMessage(id) {
|
|
150
|
+
this.expire();
|
|
117
151
|
return this.document.messages.find((message) => message.id === id) ?? null;
|
|
118
152
|
}
|
|
119
153
|
inbox(sessionId, options = {}) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
154
|
+
this.expire();
|
|
155
|
+
const matches = this.document.messages.filter((message) => message.toSessionId === sessionId && (!options.pendingOnly || message.status === 'pending'));
|
|
156
|
+
const limit = Math.max(1, Math.min(options.limit ?? 50, 200));
|
|
157
|
+
return options.pendingOnly ? matches.slice(0, limit) : matches.slice(-limit);
|
|
158
|
+
}
|
|
159
|
+
pendingCount(sessionId) {
|
|
160
|
+
this.expire();
|
|
161
|
+
return this.document.messages.filter((message) => message.toSessionId === sessionId && message.status === 'pending').length;
|
|
123
162
|
}
|
|
124
163
|
markDelivered(messageIds) {
|
|
125
164
|
return this.updateStatus(messageIds, 'delivered');
|
|
@@ -129,14 +168,15 @@ export class CollaborationStore {
|
|
|
129
168
|
}
|
|
130
169
|
updateStatus(messageIds, status) {
|
|
131
170
|
const ids = new Set(messageIds);
|
|
171
|
+
this.expire();
|
|
132
172
|
const now = Date.now();
|
|
133
173
|
const changed = [];
|
|
134
174
|
this.document.messages = this.document.messages.map((message) => {
|
|
135
|
-
if (!ids.has(message.id))
|
|
175
|
+
if (!ids.has(message.id) || message.status === 'expired' || message.status === 'failed' || message.status === 'read')
|
|
136
176
|
return message;
|
|
137
177
|
const updated = status === 'read'
|
|
138
|
-
? { ...message, status, deliveredAt: message.deliveredAt ?? now, readAt: now }
|
|
139
|
-
: message.status === 'pending' ? { ...message, status, deliveredAt: now } : message;
|
|
178
|
+
? { ...message, status, deliveredAt: message.deliveredAt ?? now, readAt: now, readSource: 'explicit', deliverySource: message.deliverySource ?? (message.deliveredAt === null ? 'consumer_read' : 'legacy_or_unspecified') }
|
|
179
|
+
: message.status === 'pending' ? { ...message, status, deliveredAt: now, deliverySource: 'pty_written' } : message;
|
|
140
180
|
changed.push(updated);
|
|
141
181
|
return updated;
|
|
142
182
|
});
|
|
@@ -145,9 +185,11 @@ export class CollaborationStore {
|
|
|
145
185
|
return changed;
|
|
146
186
|
}
|
|
147
187
|
federationSnapshot() {
|
|
188
|
+
this.expire();
|
|
148
189
|
const groups = this.document.groups.filter((group) => group.federated);
|
|
149
190
|
const ids = new Set(groups.filter((group) => !group.deleted).map((group) => group.id));
|
|
150
|
-
|
|
191
|
+
const messages = this.document.messages.filter((message) => ids.has(message.groupId));
|
|
192
|
+
return { groups, messages, transportDiagnostics: Object.fromEntries(messages.flatMap((message) => this.document.transport?.[message.id] ? [[message.id, this.document.transport[message.id]]] : [])) };
|
|
151
193
|
}
|
|
152
194
|
mergeFederatedGroup(group) {
|
|
153
195
|
if (!group.federated || typeof group.id !== 'string' || !group.id.startsWith('cross-') || typeof group.name !== 'string' || !group.name.trim()
|
|
@@ -169,7 +211,7 @@ export class CollaborationStore {
|
|
|
169
211
|
}
|
|
170
212
|
mergeFederatedMessages(messages) {
|
|
171
213
|
let changed = false;
|
|
172
|
-
const rank =
|
|
214
|
+
const rank = STATUS_RANK;
|
|
173
215
|
for (const message of messages) {
|
|
174
216
|
if (!message || typeof message !== 'object')
|
|
175
217
|
continue;
|
|
@@ -178,31 +220,217 @@ export class CollaborationStore {
|
|
|
178
220
|
|| (message.fromSessionId !== null && !group.sessionIds.includes(message.fromSessionId))
|
|
179
221
|
|| typeof message.id !== 'string' || typeof message.threadId !== 'string' || !Number.isFinite(message.createdAt)
|
|
180
222
|
|| typeof message.content !== 'string'
|
|
181
|
-
|| message.content
|
|
182
|
-
|| !Object.hasOwn(rank, message.status))
|
|
223
|
+
|| Buffer.byteLength(message.content) > COLLAB_LIMITS.message_bytes || !MESSAGE_KINDS.has(message.kind)
|
|
224
|
+
|| !Object.hasOwn(rank, message.status) || Buffer.byteLength(JSON.stringify(message)) > COLLAB_LIMITS.wire_bytes)
|
|
183
225
|
continue;
|
|
226
|
+
try {
|
|
227
|
+
validateExtras(message);
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
184
232
|
const index = this.document.messages.findIndex((item) => item.id === message.id);
|
|
185
233
|
if (index < 0) {
|
|
186
|
-
this.document.messages.push(message);
|
|
234
|
+
this.document.messages.push({ ...message, sequence: this.nextSequence() });
|
|
187
235
|
changed = true;
|
|
188
236
|
}
|
|
189
237
|
else if (rank[message.status] > rank[this.document.messages[index].status]) {
|
|
190
238
|
const existing = this.document.messages[index];
|
|
191
239
|
// A receipt may advance status, but never rewrite an existing message.
|
|
192
240
|
this.document.messages[index] = { ...existing, status: message.status,
|
|
193
|
-
deliveredAt: message.deliveredAt, readAt: message.readAt };
|
|
241
|
+
deliveredAt: message.deliveredAt, readAt: message.readAt, failureReason: message.failureReason, readSource: message.readSource, deliverySource: message.deliverySource };
|
|
194
242
|
changed = true;
|
|
195
243
|
}
|
|
196
244
|
}
|
|
197
|
-
if (changed)
|
|
198
|
-
this.
|
|
245
|
+
if (changed)
|
|
246
|
+
this.persist();
|
|
247
|
+
}
|
|
248
|
+
nextSequence() { return this.document.sequence = (this.document.sequence ?? 0) + 1; }
|
|
249
|
+
expire() {
|
|
250
|
+
let changed = false;
|
|
251
|
+
for (const message of this.document.messages)
|
|
252
|
+
if (message.status === 'pending' && message.expiresAt && message.expiresAt <= Date.now()) {
|
|
253
|
+
message.status = 'expired';
|
|
254
|
+
message.failureReason = 'MESSAGE_EXPIRED';
|
|
255
|
+
changed = true;
|
|
256
|
+
}
|
|
257
|
+
if (changed)
|
|
258
|
+
this.persist();
|
|
259
|
+
}
|
|
260
|
+
fail(id, reason) {
|
|
261
|
+
const message = this.getMessage(id);
|
|
262
|
+
if (message?.status === 'pending') {
|
|
263
|
+
message.status = 'failed';
|
|
264
|
+
message.failureReason = reason;
|
|
265
|
+
this.persist();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
diagnostic(id) { return this.document.transport?.[id] ?? null; }
|
|
269
|
+
recordTransport(id, diagnostic) {
|
|
270
|
+
if (!this.getMessage(id))
|
|
271
|
+
return;
|
|
272
|
+
(this.document.transport ??= {})[id] = diagnostic;
|
|
273
|
+
this.persist();
|
|
274
|
+
}
|
|
275
|
+
receipt(id) {
|
|
276
|
+
const message = this.getMessage(id);
|
|
277
|
+
if (!message)
|
|
278
|
+
throw new CollaborationError('MESSAGE_NOT_FOUND', 'Message not found or no longer retained', 404);
|
|
279
|
+
const replies = this.document.messages.filter((reply) => reply.replyTo === id && reply.fromSessionId === message.toSessionId && reply.toSessionId === message.fromSessionId);
|
|
280
|
+
const diagnostic = this.diagnostic(id);
|
|
281
|
+
return { message_id: id, thread_id: message.threadId, status: message.status, queued_at: message.createdAt,
|
|
282
|
+
delivered_at: message.deliveredAt, read_at: message.readAt, expires_at: message.expiresAt ?? null,
|
|
283
|
+
failure_reason: message.failureReason ?? null, delivery_semantics: message.deliverySource ?? (message.deliveredAt === null ? 'not_delivered' : 'legacy_or_unspecified'), read_semantics: message.readAt ? message.readSource ?? 'legacy_or_unspecified' : 'not_read',
|
|
284
|
+
idempotency_key: message.idempotencyKey ?? null,
|
|
285
|
+
ack_at: replies.find((reply) => reply.responseKind === 'ack')?.createdAt ?? null,
|
|
286
|
+
reply_ids: replies.map((reply) => reply.id), result_ids: replies.filter((reply) => reply.responseKind === 'result').map((reply) => reply.id),
|
|
287
|
+
response_kind: message.responseKind ?? null, task: message.task ?? null,
|
|
288
|
+
size_bytes: Buffer.byteLength(message.content),
|
|
289
|
+
relay_online: diagnostic && Date.now() - diagnostic.checked_at < 15_000 ? diagnostic.relay_online : null,
|
|
290
|
+
peer_reachable: diagnostic && Date.now() - diagnostic.checked_at < 15_000 ? diagnostic.peer_reachable : null,
|
|
291
|
+
attempt_count: diagnostic?.attempt_count ?? 0, next_retry_at: diagnostic?.next_retry_at ?? null,
|
|
292
|
+
last_error: diagnostic?.last_error ?? null, transport_checked_at: diagnostic?.checked_at ?? null,
|
|
293
|
+
fragments_sent: diagnostic?.fragments_sent ?? 0, fragments_total: diagnostic?.fragments_total ?? 0 };
|
|
294
|
+
}
|
|
295
|
+
sessionFacts(sessionId, online, turnState) {
|
|
296
|
+
const reports = this.document.messages.filter((message) => message.fromSessionId === sessionId);
|
|
297
|
+
const tasks = new Map();
|
|
298
|
+
for (const message of reports)
|
|
299
|
+
if (message.task)
|
|
300
|
+
tasks.set(message.task.task_id, { task_id: message.task.task_id, status: message.task.status, reported_at: message.createdAt, message_id: message.id });
|
|
301
|
+
return { session_state: online ? 'online' : 'offline', turn_state: turnState === 'done' ? 'ended' : turnState ?? 'unknown',
|
|
302
|
+
task_state: 'unknown', tasks: [...tasks.values()], last_heartbeat: null, last_tool_activity_at: null,
|
|
303
|
+
last_message_at: reports.length ? Math.max(...reports.map((message) => message.createdAt)) : null,
|
|
304
|
+
state_source: 'optional_adapter', task_source: 'explicit_message' };
|
|
305
|
+
}
|
|
306
|
+
page(sessionId, options = {}) {
|
|
307
|
+
this.expire();
|
|
308
|
+
if (options.responseKind && !['ack', 'progress', 'result'].includes(options.responseKind))
|
|
309
|
+
throw new CollaborationError('INVALID_RESPONSE_KIND', 'response_kind must be ack, progress or result');
|
|
310
|
+
if (options.kind && !MESSAGE_KINDS.has(options.kind))
|
|
311
|
+
throw new CollaborationError('INVALID_KIND', 'Unknown message kind');
|
|
312
|
+
const limit = options.limit ?? COLLAB_LIMITS.default_page_size;
|
|
313
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > COLLAB_LIMITS.max_page_size)
|
|
314
|
+
throw new CollaborationError('INVALID_LIMIT', 'limit must be 1–200');
|
|
315
|
+
if (options.consumer !== undefined && (!options.consumer || options.consumer.length > 128))
|
|
316
|
+
throw new CollaborationError('INVALID_CONSUMER', 'consumer must contain 1–128 characters');
|
|
317
|
+
if (options.cursor && options.afterId)
|
|
318
|
+
throw new CollaborationError('INVALID_CURSOR', 'Choose cursor or after-id');
|
|
319
|
+
const scope = JSON.stringify([sessionId, options.group ?? null, options.thread ?? null, options.from ?? null, options.kind ?? null, options.responseKind ?? null, options.unread ?? false, options.since ?? null]);
|
|
320
|
+
const consumerKey = JSON.stringify([scope, options.consumer]);
|
|
321
|
+
let after = options.consumer ? this.document.consumers?.[consumerKey] ?? 0 : 0;
|
|
322
|
+
if (options.cursor) {
|
|
323
|
+
try {
|
|
324
|
+
const decoded = JSON.parse(Buffer.from(options.cursor, 'base64url').toString());
|
|
325
|
+
if (decoded.scope !== scope || !Number.isSafeInteger(decoded.sequence) || decoded.sequence < 0)
|
|
326
|
+
throw new Error();
|
|
327
|
+
after = decoded.sequence;
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
throw new CollaborationError('INVALID_CURSOR', 'Cursor is invalid or belongs to different filters');
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (options.afterId) {
|
|
334
|
+
const anchor = this.getMessage(options.afterId);
|
|
335
|
+
if (!anchor || anchor.toSessionId !== sessionId)
|
|
336
|
+
throw new CollaborationError('CURSOR_GONE', 'after-id is unknown or no longer retained; resync explicitly', 410);
|
|
337
|
+
after = anchor.sequence ?? 0;
|
|
338
|
+
}
|
|
339
|
+
const incremental = Boolean(options.cursor || options.afterId || options.consumer || options.since !== undefined);
|
|
340
|
+
const candidates = this.document.messages.filter((message) => message.toSessionId === sessionId && (message.sequence ?? 0) > after
|
|
341
|
+
&& (!options.unread || message.status !== 'read') && (options.since === undefined || message.createdAt > options.since)
|
|
342
|
+
&& (!options.from || message.fromSessionId === options.from) && (!options.group || message.groupId === options.group)
|
|
343
|
+
&& (!options.thread || message.threadId === options.thread) && (!options.kind || message.kind === options.kind)
|
|
344
|
+
&& (!options.responseKind || message.responseKind === options.responseKind))
|
|
345
|
+
.sort((a, b) => (a.sequence ?? 0) - (b.sequence ?? 0));
|
|
346
|
+
const messages = incremental || options.order === 'oldest' ? candidates.slice(0, limit) : candidates.slice().sort((a, b) => Number(a.status === 'read') - Number(b.status === 'read') || (b.sequence ?? 0) - (a.sequence ?? 0)).slice(0, limit);
|
|
347
|
+
const sequence = messages.length ? Math.max(...messages.map((message) => message.sequence ?? 0)) : after;
|
|
348
|
+
const next_cursor = Buffer.from(JSON.stringify({ scope, sequence })).toString('base64url');
|
|
349
|
+
return { messages, next_cursor, retention_gap: incremental && after < (this.document.prunedThrough?.[sessionId] ?? 0), has_more: incremental ? candidates.length > messages.length : false, consumer: options.consumer ?? null };
|
|
350
|
+
}
|
|
351
|
+
commitCursor(sessionId, cursor, consumer) {
|
|
352
|
+
if (!consumer || consumer.length > 128)
|
|
353
|
+
throw new CollaborationError('INVALID_CONSUMER', 'consumer must contain 1–128 characters');
|
|
354
|
+
try {
|
|
355
|
+
const decoded = JSON.parse(Buffer.from(cursor, 'base64url').toString());
|
|
356
|
+
if (JSON.parse(decoded.scope)[0] !== sessionId || !Number.isSafeInteger(decoded.sequence) || decoded.sequence < 0 || decoded.sequence > (this.document.sequence ?? 0))
|
|
357
|
+
throw new Error();
|
|
358
|
+
const key = JSON.stringify([decoded.scope, consumer]);
|
|
359
|
+
(this.document.consumers ??= {})[key] = Math.max(this.document.consumers?.[key] ?? 0, decoded.sequence);
|
|
199
360
|
this.persist();
|
|
200
361
|
}
|
|
362
|
+
catch {
|
|
363
|
+
throw new CollaborationError('INVALID_CURSOR', 'Invalid consumption cursor');
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
acceptFragment(fragment) {
|
|
367
|
+
const group = this.getGroup(fragment.group_id);
|
|
368
|
+
if (!group?.federated || group.deleted || typeof fragment.message_id !== 'string' || !/^[a-zA-Z0-9_-]{1,128}$/.test(fragment.message_id) || ['__proto__', 'constructor', 'prototype'].includes(fragment.message_id) || !Number.isInteger(fragment.total) || fragment.total < 1 || fragment.total > 64
|
|
369
|
+
|| !Number.isInteger(fragment.index) || fragment.index < 0 || fragment.index >= fragment.total || !/^[a-f0-9]{64}$/.test(fragment.sha256)
|
|
370
|
+
|| typeof fragment.data !== 'string' || !/^[A-Za-z0-9+/]*={0,2}$/.test(fragment.data) || Buffer.from(fragment.data, 'base64').length > COLLAB_LIMITS.fragment_bytes)
|
|
371
|
+
throw new CollaborationError('INVALID_FRAGMENT', 'Invalid message fragment');
|
|
372
|
+
const existing = this.getMessage(fragment.message_id);
|
|
373
|
+
if (existing) {
|
|
374
|
+
if (existing.groupId !== fragment.group_id)
|
|
375
|
+
throw new CollaborationError('FRAGMENT_CONFLICT', 'Message belongs to another group', 409);
|
|
376
|
+
return { message_id: existing.id, received: fragment.total, total: fragment.total, complete: true };
|
|
377
|
+
}
|
|
378
|
+
const fragments = this.document.fragments ??= {};
|
|
379
|
+
for (const [id, state] of Object.entries(fragments))
|
|
380
|
+
if (state.createdAt + COLLAB_LIMITS.fragment_ttl_ms < Date.now())
|
|
381
|
+
delete fragments[id];
|
|
382
|
+
if (!fragments[fragment.message_id] && Object.keys(fragments).length >= 32)
|
|
383
|
+
throw new CollaborationError('FRAGMENT_CAPACITY', 'Too many incomplete messages', 429);
|
|
384
|
+
const state = fragments[fragment.message_id] ??= { groupId: fragment.group_id, total: fragment.total, sha256: fragment.sha256, chunks: {}, createdAt: Date.now() };
|
|
385
|
+
if (state.groupId !== fragment.group_id || state.total !== fragment.total || state.sha256 !== fragment.sha256 || (state.chunks[fragment.index] && state.chunks[fragment.index] !== fragment.data))
|
|
386
|
+
throw new CollaborationError('FRAGMENT_CONFLICT', 'Conflicting fragment', 409);
|
|
387
|
+
state.chunks[fragment.index] = fragment.data;
|
|
388
|
+
const received = Object.keys(state.chunks).length;
|
|
389
|
+
if (received === state.total) {
|
|
390
|
+
const bytes = Buffer.concat(Array.from({ length: state.total }, (_, index) => Buffer.from(state.chunks[index], 'base64')));
|
|
391
|
+
if (crypto.createHash('sha256').update(bytes).digest('hex') !== state.sha256)
|
|
392
|
+
throw new CollaborationError('FRAGMENT_CHECKSUM', 'Message checksum mismatch');
|
|
393
|
+
const message = JSON.parse(bytes.toString());
|
|
394
|
+
if (message.id !== fragment.message_id || message.groupId !== fragment.group_id)
|
|
395
|
+
throw new CollaborationError('FRAGMENT_IDENTITY', 'Message identity mismatch');
|
|
396
|
+
this.mergeFederatedMessages([message]);
|
|
397
|
+
if (!this.getMessage(message.id))
|
|
398
|
+
throw new CollaborationError('INVALID_MESSAGE', 'Reassembled message rejected');
|
|
399
|
+
delete fragments[fragment.message_id];
|
|
400
|
+
}
|
|
401
|
+
this.persist();
|
|
402
|
+
return { message_id: fragment.message_id, received, total: state.total, complete: received === state.total };
|
|
201
403
|
}
|
|
202
404
|
persist() {
|
|
203
|
-
|
|
405
|
+
const now = Date.now();
|
|
406
|
+
for (const [key, record] of Object.entries(this.document.idempotency ?? {}))
|
|
407
|
+
if (record.expiresAt <= now)
|
|
408
|
+
delete this.document.idempotency[key];
|
|
409
|
+
const protectedIds = new Set(Object.values(this.document.idempotency ?? {}).flatMap((record) => record.ids));
|
|
410
|
+
const history = this.document.messages.filter((message) => message.status !== 'pending' && !protectedIds.has(message.id)).slice(-MAX_MESSAGES);
|
|
411
|
+
const retained = new Set(history.map((message) => message.id));
|
|
412
|
+
this.document.messages = this.document.messages.filter((message) => {
|
|
413
|
+
if (message.status === 'pending' || protectedIds.has(message.id) || retained.has(message.id))
|
|
414
|
+
return true;
|
|
415
|
+
(this.document.prunedThrough ??= {})[message.toSessionId] = Math.max(this.document.prunedThrough?.[message.toSessionId] ?? 0, message.sequence ?? 0);
|
|
416
|
+
delete this.document.transport?.[message.id];
|
|
417
|
+
return false;
|
|
418
|
+
});
|
|
204
419
|
const temporaryPath = `${this.filePath}.${process.pid}.tmp`;
|
|
205
|
-
|
|
206
|
-
|
|
420
|
+
try {
|
|
421
|
+
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
422
|
+
const serialized = JSON.stringify(this.document);
|
|
423
|
+
fs.writeFileSync(temporaryPath, serialized, { mode: 0o600 });
|
|
424
|
+
fs.renameSync(temporaryPath, this.filePath);
|
|
425
|
+
this.persistedDocument = serialized;
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
this.document = JSON.parse(this.persistedDocument);
|
|
429
|
+
throw error;
|
|
430
|
+
}
|
|
207
431
|
}
|
|
208
432
|
}
|
|
433
|
+
function stableJson(value) {
|
|
434
|
+
return JSON.stringify(value, (_key, entry) => entry && typeof entry === 'object' && !Array.isArray(entry)
|
|
435
|
+
? Object.fromEntries(Object.entries(entry).sort(([a], [b]) => a.localeCompare(b))) : entry);
|
|
436
|
+
}
|
package/dist/server/cli.js
CHANGED
|
@@ -10,6 +10,12 @@ import { resolve } from 'path';
|
|
|
10
10
|
// dotenv 不会覆盖已存在的环境变量,因此系统环境变量和命令行参数优先级最高
|
|
11
11
|
loadDotenv({ path: resolve(os.homedir(), '.termdock', '.env'), quiet: true });
|
|
12
12
|
loadDotenv({ path: resolve(process.cwd(), '.env'), quiet: true });
|
|
13
|
+
// Federation commands run before normal server/terminal CLI initialization.
|
|
14
|
+
if (process.argv.slice(2).some(arg => arg === '--federation-pairing' || arg === '--federation-relay')) {
|
|
15
|
+
const { runFederationCli } = await import('./federation/cli.js');
|
|
16
|
+
process.exit(await runFederationCli(process.argv.slice(2)));
|
|
17
|
+
}
|
|
18
|
+
import { parseCollaborationCommand, executeCollaborationCommand, COLLAB_HELP } from './agent/collaborationCli.js';
|
|
13
19
|
import fs from 'fs';
|
|
14
20
|
import http from 'http';
|
|
15
21
|
import https from 'https';
|
|
@@ -183,7 +189,7 @@ Short commands:
|
|
|
183
189
|
Emit one lifecycle/status event from an Agent hook.
|
|
184
190
|
pi Same as agent-plugin
|
|
185
191
|
collab status Show this Session's collaboration groups and peers
|
|
186
|
-
collab inbox Read
|
|
192
|
+
collab inbox Read messages without acknowledging (see collab --help)
|
|
187
193
|
collab send <session-id> <message>
|
|
188
194
|
Send a durable message to a related Session
|
|
189
195
|
collab handoff <session-id> <summary>
|
|
@@ -678,53 +684,11 @@ function parseArgs(argv) {
|
|
|
678
684
|
}
|
|
679
685
|
}
|
|
680
686
|
else if (command === 'collab') {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
if (action === 'status' || action === 'inbox') {
|
|
684
|
-
collab = { action, json };
|
|
685
|
-
}
|
|
686
|
-
else if (action === 'send' || action === 'handoff' || action === 'reply') {
|
|
687
|
-
const target = argv[2];
|
|
688
|
-
const message = argv.slice(3).filter((part) => part !== '--json').join(' ').trim();
|
|
689
|
-
if (!target || !message) {
|
|
690
|
-
console.error(`${ICON.err} ${c.red(`Usage: td collab ${action} <${action === 'reply' ? 'message-id' : 'session-id'}> <message>`)}`);
|
|
691
|
-
process.exit(1);
|
|
692
|
-
}
|
|
693
|
-
collab = { action, target, message, json };
|
|
694
|
-
}
|
|
695
|
-
else if (action === 'add' || action === 'remove') {
|
|
696
|
-
const groupId = argv[2];
|
|
697
|
-
const sessionId = argv[3];
|
|
698
|
-
if (!groupId || !sessionId || groupId.startsWith('-') || sessionId.startsWith('-')) {
|
|
699
|
-
console.error(`${ICON.err} ${c.red(`Usage: td collab ${action} <group-id> <session-id>`)}`);
|
|
700
|
-
process.exit(1);
|
|
701
|
-
}
|
|
702
|
-
collab = { action, groupId, sessionId, json };
|
|
703
|
-
}
|
|
704
|
-
else if (action === 'spawn') {
|
|
705
|
-
const groupId = argv[2];
|
|
706
|
-
const agentSlug = argv[3];
|
|
707
|
-
const optionValue = (flag) => {
|
|
708
|
-
const index = argv.indexOf(flag);
|
|
709
|
-
const value = index >= 0 ? argv[index + 1] : undefined;
|
|
710
|
-
return value && !value.startsWith('--') ? value : undefined;
|
|
711
|
-
};
|
|
712
|
-
if (!groupId || !agentSlug || groupId.startsWith('-') || agentSlug.startsWith('-')) {
|
|
713
|
-
console.error(`${ICON.err} ${c.red('Usage: td collab spawn <group-id> <agent-slug> [--name <name>] [--cwd <path>] [--task <text>]')}`);
|
|
714
|
-
process.exit(1);
|
|
715
|
-
}
|
|
716
|
-
collab = {
|
|
717
|
-
action,
|
|
718
|
-
groupId,
|
|
719
|
-
agentSlug,
|
|
720
|
-
name: optionValue('--name'),
|
|
721
|
-
cwd: optionValue('--cwd'),
|
|
722
|
-
task: optionValue('--task'),
|
|
723
|
-
json,
|
|
724
|
-
};
|
|
687
|
+
try {
|
|
688
|
+
collab = parseCollaborationCommand(argv.slice(1));
|
|
725
689
|
}
|
|
726
|
-
|
|
727
|
-
console.error(
|
|
690
|
+
catch (error) {
|
|
691
|
+
console.error(JSON.stringify({ ok: false, code: 'INVALID_ARGUMENT', error: error instanceof Error ? error.message : String(error) }));
|
|
728
692
|
process.exit(1);
|
|
729
693
|
}
|
|
730
694
|
argv = [];
|
|
@@ -1219,7 +1183,7 @@ async function readStdinText() {
|
|
|
1219
1183
|
process.stdin.on('error', reject);
|
|
1220
1184
|
});
|
|
1221
1185
|
}
|
|
1222
|
-
async function postLocalJson(baseUrl, token, endpoint, payload) {
|
|
1186
|
+
async function postLocalJson(baseUrl, token, endpoint, payload, timeoutMs = 30_000) {
|
|
1223
1187
|
const url = new URL(endpoint, baseUrl);
|
|
1224
1188
|
const body = JSON.stringify(payload);
|
|
1225
1189
|
const isHttps = url.protocol === 'https:';
|
|
@@ -1244,12 +1208,14 @@ async function postLocalJson(baseUrl, token, endpoint, payload) {
|
|
|
1244
1208
|
res.on('data', (chunk) => { responseBody += chunk; });
|
|
1245
1209
|
res.on('end', () => resolve({ statusCode: res.statusCode ?? 0, body: responseBody }));
|
|
1246
1210
|
});
|
|
1211
|
+
const timer = setTimeout(() => req.destroy(new Error('Local collaboration request timed out')), timeoutMs);
|
|
1212
|
+
req.on('close', () => clearTimeout(timer));
|
|
1247
1213
|
req.on('error', reject);
|
|
1248
1214
|
req.write(body);
|
|
1249
1215
|
req.end();
|
|
1250
1216
|
});
|
|
1251
1217
|
}
|
|
1252
|
-
async function getLocalJson(baseUrl, token, endpoint) {
|
|
1218
|
+
async function getLocalJson(baseUrl, token, endpoint, timeoutMs = 30_000) {
|
|
1253
1219
|
const url = new URL(endpoint, baseUrl);
|
|
1254
1220
|
const isHttps = url.protocol === 'https:';
|
|
1255
1221
|
const requestImpl = isHttps ? https.request : http.request;
|
|
@@ -1271,14 +1237,20 @@ async function getLocalJson(baseUrl, token, endpoint) {
|
|
|
1271
1237
|
res.on('data', (chunk) => { responseBody += chunk; });
|
|
1272
1238
|
res.on('end', () => resolve({ statusCode: res.statusCode ?? 0, body: responseBody }));
|
|
1273
1239
|
});
|
|
1240
|
+
const timer = setTimeout(() => req.destroy(new Error('Local collaboration request timed out')), timeoutMs);
|
|
1241
|
+
req.on('close', () => clearTimeout(timer));
|
|
1274
1242
|
req.on('error', reject);
|
|
1275
1243
|
req.end();
|
|
1276
1244
|
});
|
|
1277
1245
|
}
|
|
1278
1246
|
async function runCollab(command) {
|
|
1247
|
+
if (command.action === 'help') {
|
|
1248
|
+
console.log(COLLAB_HELP);
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1279
1251
|
const runningState = getRunningState();
|
|
1280
1252
|
if (!runningState?.localApiToken) {
|
|
1281
|
-
console.error(
|
|
1253
|
+
console.error(JSON.stringify({ ok: false, code: 'SERVICE_UNAVAILABLE', error: 'Termdock is not running or its local API token is unavailable.' }));
|
|
1282
1254
|
process.exit(1);
|
|
1283
1255
|
}
|
|
1284
1256
|
const backendSessionId = process.env.TERMDOCK_BACKEND_SESSION_ID?.trim() || null;
|
|
@@ -1299,128 +1271,16 @@ async function runCollab(command) {
|
|
|
1299
1271
|
}
|
|
1300
1272
|
}
|
|
1301
1273
|
if (!backendSessionId && !tmuxSessionName) {
|
|
1302
|
-
console.error(
|
|
1274
|
+
console.error(JSON.stringify({ ok: false, code: 'SESSION_NOT_FOUND', error: 'td collab must run inside a Termdock-managed Session.' }));
|
|
1303
1275
|
process.exit(1);
|
|
1304
1276
|
}
|
|
1305
1277
|
const context = backendSessionId ? { backendSessionId } : { tmuxSessionName };
|
|
1306
|
-
const contextQuery = new URLSearchParams(Object.entries(context).filter((entry) => typeof entry[1] === 'string')).toString();
|
|
1307
1278
|
const baseUrl = runningState.localUrl
|
|
1308
1279
|
?? `${runningState.scheme ?? 'http'}://${runningState.host === '0.0.0.0' ? 'localhost' : runningState.host}:${runningState.port}`;
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
else if (command.action === 'inbox') {
|
|
1314
|
-
response = await getLocalJson(baseUrl, runningState.localApiToken, `/api/terminal/operations/orchestration/inbox?${contextQuery}&markRead=true`);
|
|
1315
|
-
}
|
|
1316
|
-
else if (command.action === 'reply') {
|
|
1317
|
-
response = await postLocalJson(baseUrl, runningState.localApiToken, '/api/terminal/operations/orchestration/reply', {
|
|
1318
|
-
...context,
|
|
1319
|
-
messageId: command.target,
|
|
1320
|
-
content: command.message,
|
|
1321
|
-
});
|
|
1322
|
-
}
|
|
1323
|
-
else if (command.action === 'add' || command.action === 'remove') {
|
|
1324
|
-
response = await postLocalJson(baseUrl, runningState.localApiToken, '/api/terminal/operations/orchestration/members', {
|
|
1325
|
-
...context,
|
|
1326
|
-
groupId: command.groupId,
|
|
1327
|
-
targetSessionId: command.sessionId,
|
|
1328
|
-
action: command.action,
|
|
1329
|
-
});
|
|
1330
|
-
}
|
|
1331
|
-
else if (command.action === 'spawn') {
|
|
1332
|
-
response = await postLocalJson(baseUrl, runningState.localApiToken, '/api/terminal/operations/orchestration/spawn', {
|
|
1333
|
-
...context,
|
|
1334
|
-
groupId: command.groupId,
|
|
1335
|
-
agentSlug: command.agentSlug,
|
|
1336
|
-
name: command.name,
|
|
1337
|
-
cwd: command.cwd,
|
|
1338
|
-
task: command.task,
|
|
1339
|
-
});
|
|
1340
|
-
}
|
|
1341
|
-
else {
|
|
1342
|
-
response = await postLocalJson(baseUrl, runningState.localApiToken, '/api/terminal/operations/orchestration/send', {
|
|
1343
|
-
...context,
|
|
1344
|
-
targetSessionId: command.target,
|
|
1345
|
-
message: command.message,
|
|
1346
|
-
kind: command.action === 'handoff' ? 'handoff' : 'message',
|
|
1347
|
-
});
|
|
1348
|
-
}
|
|
1349
|
-
let body = {};
|
|
1350
|
-
try {
|
|
1351
|
-
body = JSON.parse(response.body || '{}');
|
|
1352
|
-
}
|
|
1353
|
-
catch { /* handled below */ }
|
|
1354
|
-
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
1355
|
-
console.error(`${ICON.err} ${c.red(typeof body.error === 'string' ? body.error : 'Collaboration request failed')}`);
|
|
1356
|
-
process.exit(1);
|
|
1357
|
-
}
|
|
1358
|
-
if (command.json) {
|
|
1359
|
-
console.log(JSON.stringify(body, null, 2));
|
|
1360
|
-
return;
|
|
1361
|
-
}
|
|
1362
|
-
if (command.action === 'status') {
|
|
1363
|
-
const groups = Array.isArray(body.groups) ? body.groups : [];
|
|
1364
|
-
const peers = Array.isArray(body.peers) ? body.peers : [];
|
|
1365
|
-
const sessions = Array.isArray(body.sessions) ? body.sessions : [];
|
|
1366
|
-
const agents = Array.isArray(body.agents) ? body.agents : [];
|
|
1367
|
-
console.log(`${ICON.ok} ${c.green(`Collaboration: ${groups.length} group(s), ${peers.length} peer(s)`)}`);
|
|
1368
|
-
for (const group of groups) {
|
|
1369
|
-
console.log(` ${c.green(group.name ?? 'Group')} ${c.dim(`[${group.id ?? ''}]`)}`);
|
|
1370
|
-
if (group.sessionIds?.length)
|
|
1371
|
-
console.log(` ${c.dim(`${group.sessionIds.length} member(s)`)}`);
|
|
1372
|
-
}
|
|
1373
|
-
if (peers.length > 0)
|
|
1374
|
-
console.log(`\n${c.dim('Current group peers:')}`);
|
|
1375
|
-
for (const peer of peers) {
|
|
1376
|
-
console.log(` ${c.cyan(peer.name ?? peer.sessionId ?? 'Session')} ${c.dim(`[${peer.sessionId ?? ''}]`)}`);
|
|
1377
|
-
console.log(` ${peer.status ?? 'offline'} · ${peer.capability ?? ''}`);
|
|
1378
|
-
if (peer.currentTask)
|
|
1379
|
-
console.log(` ${c.dim(peer.currentTask)}`);
|
|
1380
|
-
}
|
|
1381
|
-
for (const group of groups) {
|
|
1382
|
-
const memberIds = new Set(group.sessionIds ?? []);
|
|
1383
|
-
const candidates = sessions.filter((session) => session.sessionId && !memberIds.has(session.sessionId));
|
|
1384
|
-
if (candidates.length > 0) {
|
|
1385
|
-
console.log(`\n${c.dim(`Sessions available to add to ${group.name ?? 'group'} [${group.id ?? ''}]:`)}`);
|
|
1386
|
-
for (const session of candidates) {
|
|
1387
|
-
console.log(` ${session.name ?? 'Session'} ${c.dim(`[${session.sessionId ?? ''}]`)} · ${session.status ?? 'offline'}`);
|
|
1388
|
-
if (session.cwd)
|
|
1389
|
-
console.log(` ${c.dim(session.cwd)}`);
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
if (agents.length > 0) {
|
|
1394
|
-
console.log(`\n${c.dim('Agent types available to spawn:')}`);
|
|
1395
|
-
for (const agent of agents)
|
|
1396
|
-
console.log(` ${agent.displayName ?? agent.slug ?? 'Agent'} ${c.dim(`[${agent.slug ?? ''}]`)}`);
|
|
1397
|
-
}
|
|
1398
|
-
return;
|
|
1399
|
-
}
|
|
1400
|
-
if (command.action === 'inbox') {
|
|
1401
|
-
const messages = Array.isArray(body.messages) ? body.messages : [];
|
|
1402
|
-
if (messages.length === 0) {
|
|
1403
|
-
console.log(`${ICON.ok} ${c.dim('Collaboration inbox is empty.')}`);
|
|
1404
|
-
return;
|
|
1405
|
-
}
|
|
1406
|
-
for (const message of messages) {
|
|
1407
|
-
console.log(`${c.cyan(message.kind ?? 'message')} ${c.dim(`#${message.id ?? ''}`)} from ${message.fromSessionId ?? 'user'}`);
|
|
1408
|
-
console.log(` ${message.content ?? ''}`);
|
|
1409
|
-
}
|
|
1410
|
-
return;
|
|
1411
|
-
}
|
|
1412
|
-
const delivery = body.delivery;
|
|
1413
|
-
if (delivery?.reason) {
|
|
1414
|
-
console.log(`${delivery.serviceUnavailable ? '服务不可达' : '等待投递'}: ${delivery.reason}`);
|
|
1415
|
-
return;
|
|
1416
|
-
}
|
|
1417
|
-
const success = command.action === 'reply' ? 'Reply sent.'
|
|
1418
|
-
: command.action === 'handoff' ? 'Handoff sent.'
|
|
1419
|
-
: command.action === 'add' ? 'Session added to collaboration group.'
|
|
1420
|
-
: command.action === 'remove' ? 'Session removed from collaboration group.'
|
|
1421
|
-
: command.action === 'spawn' ? 'Agent Session created and added to collaboration group.'
|
|
1422
|
-
: 'Message sent.';
|
|
1423
|
-
console.log(`${ICON.ok} ${c.green(success)}`);
|
|
1280
|
+
process.exitCode = await executeCollaborationCommand(command, Object.fromEntries(Object.entries(context).filter((entry) => typeof entry[1] === 'string')), { request: (method, endpoint, body, timeout) => method === 'GET'
|
|
1281
|
+
? getLocalJson(baseUrl, runningState.localApiToken, endpoint, timeout)
|
|
1282
|
+
: postLocalJson(baseUrl, runningState.localApiToken, endpoint, body, timeout),
|
|
1283
|
+
write: (line) => console.log(line), stdin: readStdinText });
|
|
1424
1284
|
}
|
|
1425
1285
|
async function runInjectChangeAudit(source) {
|
|
1426
1286
|
const raw = source === true
|
|
@@ -3463,7 +3323,7 @@ async function main() {
|
|
|
3463
3323
|
}
|
|
3464
3324
|
if (options.collab) {
|
|
3465
3325
|
await runCollab(options.collab);
|
|
3466
|
-
|
|
3326
|
+
return; // Preserve the receipt exit code and allow piped JSON output to drain.
|
|
3467
3327
|
}
|
|
3468
3328
|
if (options.pluginInit) {
|
|
3469
3329
|
if (options.pluginInitJson)
|