termdock 1.4.309 → 1.4.310
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +3 -1
- package/README.md +3 -1
- package/THIRD_PARTY_NOTICES.md +30 -0
- package/dist/client/assets/{App-ggIA_xqR.js → App-CytIMrFr.js} +62 -55
- package/dist/client/assets/App-CytIMrFr.js.br +0 -0
- package/dist/client/assets/App-CytIMrFr.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-BRTR2IsK.js +9 -0
- package/dist/client/assets/ArchitecturePanel-BRTR2IsK.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-BRTR2IsK.js.gz +0 -0
- package/dist/client/assets/{ArchitecturePanel-Cmpmp6FS.css → ArchitecturePanel-B_BDrrfR.css} +1 -1
- package/dist/client/assets/ArchitecturePanel-B_BDrrfR.css.br +0 -0
- package/dist/client/assets/ArchitecturePanel-B_BDrrfR.css.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-D1Puc7B3.js → ChangeWalkthroughPanel-Bm9Tvd6H.js} +2 -2
- package/dist/client/assets/ChangeWalkthroughPanel-Bm9Tvd6H.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-Bm9Tvd6H.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-BagYBDlJ.js +1 -0
- package/dist/client/assets/CollaborationMainWorkspace-BagYBDlJ.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-BagYBDlJ.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-D7LXylWr.js +2 -0
- package/dist/client/assets/{ComputerControlView-D5Lw9sNA.js → ComputerControlView-CC6r2cD3.js} +2 -2
- package/dist/client/assets/ComputerControlView-CC6r2cD3.js.br +0 -0
- package/dist/client/assets/ComputerControlView-CC6r2cD3.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-DYJrGfiG.js → DagPlayground-BqAadqBT.js} +1 -1
- package/dist/client/assets/DagPlayground-BqAadqBT.js.br +0 -0
- package/dist/client/assets/DagPlayground-BqAadqBT.js.gz +0 -0
- package/dist/client/assets/{DiffLab-5zdcFW3z.js → DiffLab-CrEVLy-j.js} +1 -1
- package/dist/client/assets/DiffLab-CrEVLy-j.js.br +0 -0
- package/dist/client/assets/DiffLab-CrEVLy-j.js.gz +0 -0
- package/dist/client/assets/{DiffReview-BBBC_NjV.js → DiffReview-C8e2GQxb.js} +1 -1
- package/dist/client/assets/DiffReview-C8e2GQxb.js.br +0 -0
- package/dist/client/assets/DiffReview-C8e2GQxb.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-CbhosFSB.js → DiffReviewLab-RQ1NmpZE.js} +2 -2
- package/dist/client/assets/DiffReviewLab-RQ1NmpZE.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-RQ1NmpZE.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-BuQwFQ4V.js → DiffViewer-KljgMa9h.js} +8 -8
- package/dist/client/assets/DiffViewer-KljgMa9h.js.br +0 -0
- package/dist/client/assets/DiffViewer-KljgMa9h.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-nO2VmqdP.js → ModelPreview-B9UR1TR3.js} +1 -1
- package/dist/client/assets/ModelPreview-B9UR1TR3.js.br +0 -0
- package/dist/client/assets/ModelPreview-B9UR1TR3.js.gz +0 -0
- package/dist/client/assets/RightSidebar-DSH93gN5.js +1677 -0
- package/dist/client/assets/RightSidebar-DSH93gN5.js.br +0 -0
- package/dist/client/assets/RightSidebar-DSH93gN5.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-yi0K8GDG.js → abnfDiagram-VCTEODGH-WFUf9n1A.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-WFUf9n1A.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-WFUf9n1A.js.gz +0 -0
- package/dist/client/assets/{app-B6FdOF6K.js → app-Bey8S6UU.js} +4 -4
- package/dist/client/assets/app-Bey8S6UU.js.br +0 -0
- package/dist/client/assets/app-Bey8S6UU.js.gz +0 -0
- package/dist/client/assets/{arc-_nLI_-XZ.js → arc-BXKxIsA3.js} +1 -1
- package/dist/client/assets/arc-BXKxIsA3.js.br +0 -0
- package/dist/client/assets/arc-BXKxIsA3.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-CikrA78O.js → architectureDiagram-5GKGNRK7-DAfRrSxr.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DAfRrSxr.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DAfRrSxr.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-CWJq6qJ7.js → blockDiagram-I7D4REHJ-1HbSy7rb.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-1HbSy7rb.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-1HbSy7rb.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-gzPzLGvs.js → c4Diagram-7LVT6UL2-Dxwui-M_.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-Dxwui-M_.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-Dxwui-M_.js.gz +0 -0
- package/dist/client/assets/channel-Dkh0SMv-.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-CfX9l18E.js → chunk-2Q5K7J3B-CJSdJ_A1.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-JSV1yrXM.js → chunk-5VM5RSS4-BNEXn4bI.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-CQ0LXeap.js → chunk-F27PBJKO-BaM6iLMb.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-BaM6iLMb.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-BaM6iLMb.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-CJOEPkK9.js → chunk-IMKFNOWR-DxPJBdLq.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-DxPJBdLq.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-DxPJBdLq.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-Dpp7Xu_E.js → chunk-JWPE2WC7-BwEOSoiu.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-9hDa1b8z.js → chunk-POPQ4Y6H-CbYcBz8G.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-BskkG9gg.js → chunk-SVP7TREG-CpnycZ3K.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-CpnycZ3K.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-CpnycZ3K.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-DyzuP-9I.js → chunk-TICWLB2K-5oWwjAOF.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-5oWwjAOF.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-5oWwjAOF.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-C31_1i6z.js → chunk-XXDRQBXY-VWUm4Sz5.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-DqvmSdwt.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-DqvmSdwt.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-Bm-ESDOc.js → cose-bilkent-JH36ORCC-gpF5Tw44.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-gpF5Tw44.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-gpF5Tw44.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-DbFWAPvX.js → cynefin-OW5HDTMX-HFr7zRQ0.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-HFr7zRQ0.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-HFr7zRQ0.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-Ca0IKRdH.js → cynefinDiagram-5FMLGOSQ-LccKgJj0.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-LccKgJj0.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-LccKgJj0.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-D_5uEMjJ.js → dagre-GXQ25YYZ-C7eLk1r1.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-C7eLk1r1.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-C7eLk1r1.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-DWVpndRP.js → diagram-S7CK7UJ4-DkWPZYAg.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-DkWPZYAg.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-DkWPZYAg.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-DmYTya3w.js → diagram-UQ7AKVKN-DluTsMXz.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-DluTsMXz.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-DluTsMXz.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-BEA6kqNj.js → diagram-VSXAHHWV-BLXEmXmu.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-BLXEmXmu.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-BLXEmXmu.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-CU9vP0P0.js → diagram-VX7I27RA-DO3J99WU.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-DO3J99WU.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-DO3J99WU.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-B_GvE2g-.js → diagram-Z3DM3KII-kvkSusTy.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-kvkSusTy.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-kvkSusTy.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-BLrPQuLE.js → ebnfDiagram-PWID7BFC-Bxd9hatc.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Bxd9hatc.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Bxd9hatc.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-DlXgoyZ-.js → erDiagram-RLTQ6QDP-BWc3uM23.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-BWc3uM23.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-BWc3uM23.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-fPHWxEDZ.js → flowDiagram-HODETNUW-BQhPtc9z.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-BQhPtc9z.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BQhPtc9z.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-B8MOv6ug.js → ganttDiagram-EL5Y4UJY-DzmTLgVr.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-DzmTLgVr.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-DzmTLgVr.js.gz +0 -0
- package/dist/client/assets/{gestureArbiter-JW8vcVRm.js → gestureArbiter-BuNZMZ7A.js} +1 -1
- package/dist/client/assets/gestureArbiter-BuNZMZ7A.js.br +0 -0
- package/dist/client/assets/gestureArbiter-BuNZMZ7A.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-lH9gSoFx.js → gitGraphDiagram-WWUBYQGX-r0Onfl7K.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-r0Onfl7K.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-r0Onfl7K.js.gz +0 -0
- package/dist/client/assets/{index-CNY7CTVa.css → index-C6D5a-dg.css} +1 -1
- package/dist/client/assets/index-C6D5a-dg.css.br +0 -0
- package/dist/client/assets/{index-CNY7CTVa.css.gz → index-C6D5a-dg.css.gz} +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-CSG29Ty_.js → infoDiagram-27XIBGKW-C52jpDzv.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-CcwSE5qV.js → ishikawaDiagram-5VMMS53U-C-l0eHmO.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-C-l0eHmO.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-C-l0eHmO.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-CXFUeP2c.js → journeyDiagram-3NMN7TZE-DmhgEOjX.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DmhgEOjX.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DmhgEOjX.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-KEHwljd2.js → kanban-definition-UXKFOSKX-BnZWlxIT.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-BnZWlxIT.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-BnZWlxIT.js.gz +0 -0
- package/dist/client/assets/{layout-DqYx0MnO.js → layout-BrxPpC7u.js} +1 -1
- package/dist/client/assets/layout-BrxPpC7u.js.br +0 -0
- package/dist/client/assets/layout-BrxPpC7u.js.gz +0 -0
- package/dist/client/assets/{linear-BnG3XaJW.js → linear-DaEpqVcH.js} +1 -1
- package/dist/client/assets/linear-DaEpqVcH.js.br +0 -0
- package/dist/client/assets/linear-DaEpqVcH.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-Ce00rdM6.js → mermaid.core-CidoOS12.js} +5 -5
- package/dist/client/assets/mermaid.core-CidoOS12.js.br +0 -0
- package/dist/client/assets/mermaid.core-CidoOS12.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-D0hOW4SY.js → mindmap-definition-YA3MSWOX-Bpr3CVLy.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-Bpr3CVLy.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-Bpr3CVLy.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-D7hFrc0r.js +2 -0
- package/dist/client/assets/passwordBootstrap-D7hFrc0r.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-D7hFrc0r.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-UKp9t91x.js → pegDiagram-XKGWAZYB-Bw2JpSYn.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-Bw2JpSYn.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-Bw2JpSYn.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-Dif97wbH.js → pieDiagram-E7YTZNPT-BaKc3ifQ.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-BaKc3ifQ.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-BaKc3ifQ.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-BPyHtH91.js → quadrantDiagram-AXDQQJYC-B0Yi-dl9.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-B0Yi-dl9.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-B0Yi-dl9.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-Wleq9LmS.js → railroadDiagram-O6MQD6OU-DCcCQofU.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DCcCQofU.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DCcCQofU.js.gz +0 -0
- package/dist/client/assets/{rdpSession-CobFMSUs.js → rdpSession-DY81W5BY.js} +1 -1
- package/dist/client/assets/rdpSession-DY81W5BY.js.br +0 -0
- package/dist/client/assets/rdpSession-DY81W5BY.js.gz +0 -0
- package/dist/client/assets/{referenceLongPress-bZ0xp-v2.js → referenceLongPress-CAzWqXsY.js} +1 -1
- package/dist/client/assets/referenceLongPress-CAzWqXsY.js.br +0 -0
- package/dist/client/assets/referenceLongPress-CAzWqXsY.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-BL4pp0Ta.js → requirementDiagram-BXWQKSXE-CEJFRPWH.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-CEJFRPWH.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-CEJFRPWH.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-CV1cKx37.js → sankeyDiagram-P5KCCOFB-B3Wk45FA.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-B3Wk45FA.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-B3Wk45FA.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-BF2InLwV.js → sequenceDiagram-WJ2MYXX4-BejEsITb.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BejEsITb.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BejEsITb.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-CldUyStR.js → sizeCapture-INFHLROL-DZ5NJZnM.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-DZ5NJZnM.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-DZ5NJZnM.js.gz +0 -0
- package/dist/client/assets/{sparkles-Cg-yPdE2.js → sparkles-DQGLdN2b.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-CByehbEd.js → stateDiagram-D77RDMKH-D8SSEwHa.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-D8SSEwHa.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-D8SSEwHa.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-D1KQOetv.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH--1_7YTL4.js → swimlanes-42K2YHIH-Dow-EKMG.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-Dow-EKMG.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-Dow-EKMG.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DQbZCzq9.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-CXfXJFK6.js → timeline-definition-24CTP7MA-DpQ9D5f6.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-DpQ9D5f6.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-DpQ9D5f6.js.gz +0 -0
- package/dist/client/assets/{triangle-alert-CbMRJ6sx.js → triangle-alert-Bdf_dBch.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-byr6UQpL.js → vennDiagram-4TSXK5OY-DoemAmM2.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-DoemAmM2.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-DoemAmM2.js.gz +0 -0
- package/dist/client/assets/{videoCompression-ivJAy00d.js → videoCompression-Bg4gUQUf.js} +1 -1
- package/dist/client/assets/videoCompression-Bg4gUQUf.js.br +0 -0
- package/dist/client/assets/videoCompression-Bg4gUQUf.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-FJduk-7s.js → wardleyDiagram-VM6X3IG4-MernEZ54.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-MernEZ54.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-MernEZ54.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-Ct1fBPFS.js → xychartDiagram-S5SC5T6Z-CzO3HEHD.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CzO3HEHD.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CzO3HEHD.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.js +1 -1
- package/dist/client/sw.js.br +0 -0
- package/dist/client/sw.js.gz +0 -0
- package/dist/server/agent/integrationCli.js +23 -0
- package/dist/server/agent/integrationServer.js +7 -0
- package/dist/server/agent/integrationSessions.js +24 -1
- package/dist/server/agent/sessionLifecycle.js +2 -2
- package/dist/server/architecture/cli.js +295 -0
- package/dist/server/architecture/document.js +328 -0
- package/dist/server/architecture/documentDiff.js +34 -0
- package/dist/server/architecture/fieldMutation.js +52 -0
- package/dist/server/architecture/flowchart.js +62 -0
- package/dist/server/architecture/mermaidDiff.js +85 -0
- package/dist/server/architecture/routes.js +117 -0
- package/dist/server/architecture/store.js +502 -0
- package/dist/server/cli.js +7 -1
- package/dist/server/entry.js +2 -0
- package/dist/server/federation/accessPolicy.js +9 -1
- package/docs/integrations.md +40 -0
- package/package.json +3 -1
- package/runtime-manifest.json +4 -4
- package/dist/client/assets/App-ggIA_xqR.js.br +0 -0
- package/dist/client/assets/App-ggIA_xqR.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-Cmpmp6FS.css.br +0 -0
- package/dist/client/assets/ArchitecturePanel-Cmpmp6FS.css.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-CoBspW_C.js +0 -12
- package/dist/client/assets/ArchitecturePanel-CoBspW_C.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-CoBspW_C.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-D1Puc7B3.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-D1Puc7B3.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-BPyK5Xs8.js +0 -1
- package/dist/client/assets/CollaborationMainWorkspace-BPyK5Xs8.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-BPyK5Xs8.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-CTSJJqze.js +0 -2
- package/dist/client/assets/ComputerControlView-D5Lw9sNA.js.br +0 -0
- package/dist/client/assets/ComputerControlView-D5Lw9sNA.js.gz +0 -0
- package/dist/client/assets/DagPlayground-DYJrGfiG.js.br +0 -0
- package/dist/client/assets/DagPlayground-DYJrGfiG.js.gz +0 -0
- package/dist/client/assets/DiffLab-5zdcFW3z.js.br +0 -0
- package/dist/client/assets/DiffLab-5zdcFW3z.js.gz +0 -0
- package/dist/client/assets/DiffReview-BBBC_NjV.js.br +0 -0
- package/dist/client/assets/DiffReview-BBBC_NjV.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-CbhosFSB.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-CbhosFSB.js.gz +0 -0
- package/dist/client/assets/DiffViewer-BuQwFQ4V.js.br +0 -0
- package/dist/client/assets/DiffViewer-BuQwFQ4V.js.gz +0 -0
- package/dist/client/assets/ModelPreview-nO2VmqdP.js.br +0 -0
- package/dist/client/assets/ModelPreview-nO2VmqdP.js.gz +0 -0
- package/dist/client/assets/RightSidebar-BoNzpS2K.js +0 -1677
- package/dist/client/assets/RightSidebar-BoNzpS2K.js.br +0 -0
- package/dist/client/assets/RightSidebar-BoNzpS2K.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-yi0K8GDG.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-yi0K8GDG.js.gz +0 -0
- package/dist/client/assets/app-B6FdOF6K.js.br +0 -0
- package/dist/client/assets/app-B6FdOF6K.js.gz +0 -0
- package/dist/client/assets/arc-_nLI_-XZ.js.br +0 -0
- package/dist/client/assets/arc-_nLI_-XZ.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CikrA78O.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CikrA78O.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-CWJq6qJ7.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-CWJq6qJ7.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-gzPzLGvs.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-gzPzLGvs.js.gz +0 -0
- package/dist/client/assets/channel-BYsFmOmF.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-CQ0LXeap.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-CQ0LXeap.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-CJOEPkK9.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-CJOEPkK9.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-BskkG9gg.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-BskkG9gg.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-DyzuP-9I.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-DyzuP-9I.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-BnmZv6Ns.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-BnmZv6Ns.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-Bm-ESDOc.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-Bm-ESDOc.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-DbFWAPvX.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-DbFWAPvX.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Ca0IKRdH.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Ca0IKRdH.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-D_5uEMjJ.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-D_5uEMjJ.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-DWVpndRP.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-DWVpndRP.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-DmYTya3w.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-DmYTya3w.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-BEA6kqNj.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-BEA6kqNj.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-CU9vP0P0.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-CU9vP0P0.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-B_GvE2g-.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-B_GvE2g-.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-BLrPQuLE.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-BLrPQuLE.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-DlXgoyZ-.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-DlXgoyZ-.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-fPHWxEDZ.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-fPHWxEDZ.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-B8MOv6ug.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-B8MOv6ug.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-JW8vcVRm.js.br +0 -0
- package/dist/client/assets/gestureArbiter-JW8vcVRm.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-lH9gSoFx.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-lH9gSoFx.js.gz +0 -0
- package/dist/client/assets/index-CNY7CTVa.css.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-CcwSE5qV.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-CcwSE5qV.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-CXFUeP2c.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-CXFUeP2c.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-KEHwljd2.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-KEHwljd2.js.gz +0 -0
- package/dist/client/assets/layout-DqYx0MnO.js.br +0 -0
- package/dist/client/assets/layout-DqYx0MnO.js.gz +0 -0
- package/dist/client/assets/linear-BnG3XaJW.js.br +0 -0
- package/dist/client/assets/linear-BnG3XaJW.js.gz +0 -0
- package/dist/client/assets/mermaid.core-Ce00rdM6.js.br +0 -0
- package/dist/client/assets/mermaid.core-Ce00rdM6.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-D0hOW4SY.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-D0hOW4SY.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-Bncb8qF_.js +0 -2
- package/dist/client/assets/passwordBootstrap-Bncb8qF_.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-Bncb8qF_.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-UKp9t91x.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-UKp9t91x.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-Dif97wbH.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-Dif97wbH.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-BPyHtH91.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-BPyHtH91.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-Wleq9LmS.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-Wleq9LmS.js.gz +0 -0
- package/dist/client/assets/rdpSession-CobFMSUs.js.br +0 -0
- package/dist/client/assets/rdpSession-CobFMSUs.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-bZ0xp-v2.js.br +0 -0
- package/dist/client/assets/referenceLongPress-bZ0xp-v2.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-BL4pp0Ta.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-BL4pp0Ta.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CV1cKx37.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CV1cKx37.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BF2InLwV.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BF2InLwV.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-CldUyStR.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-CldUyStR.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-CByehbEd.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-CByehbEd.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CTcrJ-w0.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH--1_7YTL4.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH--1_7YTL4.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-RSMV9AhO.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-CXfXJFK6.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-CXfXJFK6.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-byr6UQpL.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-byr6UQpL.js.gz +0 -0
- package/dist/client/assets/videoCompression-ivJAy00d.js.br +0 -0
- package/dist/client/assets/videoCompression-ivJAy00d.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-FJduk-7s.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-FJduk-7s.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-Ct1fBPFS.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-Ct1fBPFS.js.gz +0 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
5
|
+
import { execFile } from 'node:child_process';
|
|
6
|
+
import { promisify } from 'node:util';
|
|
7
|
+
import YAML from 'yaml';
|
|
8
|
+
import { ARCHITECTURE_FIELDS, ARCHITECTURE_FILE, ARCHITECTURE_DIRECTORY, analysisFile, architectureScope, isArchitectureFile, isArchitectureSourcePath, parseArchitecture } from './document.js';
|
|
9
|
+
import { parseFlowchart } from './flowchart.js';
|
|
10
|
+
import { diffArchitecture } from './documentDiff.js';
|
|
11
|
+
const runFile = promisify(execFile);
|
|
12
|
+
const digest = (value) => createHash('sha256').update(value).digest('hex');
|
|
13
|
+
const missing = (error) => error?.code === 'ENOENT';
|
|
14
|
+
const safeName = (name) => /^[a-zA-Z][a-zA-Z0-9_-]{0,79}$/.test(name);
|
|
15
|
+
const names = Object.fromEntries(ARCHITECTURE_FIELDS.map(field => [field, field === 'diagram' ? 'diagram.mmd' : `${field}.md`]));
|
|
16
|
+
export class ArchitectureConflict extends Error {
|
|
17
|
+
}
|
|
18
|
+
/** Bound all document IO to real project directories; refuse symlinks in document trees. */
|
|
19
|
+
export class ArchitectureStore {
|
|
20
|
+
root;
|
|
21
|
+
constructor(root) {
|
|
22
|
+
this.root = root;
|
|
23
|
+
}
|
|
24
|
+
async bounded(relative, create = false) {
|
|
25
|
+
if (!relative || relative.startsWith('/') || /[\\\x00-\x1f]/.test(relative) || relative.split('/').some(part => !part || part === '.' || part === '..'))
|
|
26
|
+
throw new Error('Unsafe architecture path');
|
|
27
|
+
const root = await fs.realpath(this.root);
|
|
28
|
+
let current = root;
|
|
29
|
+
const parts = relative.split('/');
|
|
30
|
+
for (let index = 0; index < parts.length; index++) {
|
|
31
|
+
current = path.join(current, parts[index]);
|
|
32
|
+
try {
|
|
33
|
+
const stat = await fs.lstat(current);
|
|
34
|
+
if (stat.isSymbolicLink())
|
|
35
|
+
throw new Error('Architecture paths cannot contain symlinks');
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (!missing(error))
|
|
39
|
+
throw error;
|
|
40
|
+
if (create && index < parts.length - 1)
|
|
41
|
+
await fs.mkdir(current).catch(async (error) => { if (error.code !== 'EEXIST' || !(await fs.lstat(current)).isDirectory())
|
|
42
|
+
throw error; });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return current;
|
|
46
|
+
}
|
|
47
|
+
async text(relative) {
|
|
48
|
+
const target = await this.bounded(relative);
|
|
49
|
+
try {
|
|
50
|
+
const stat = await fs.stat(target);
|
|
51
|
+
if (!stat.isFile() || stat.size > 5_000_000)
|
|
52
|
+
throw new Error('Architecture file exceeds the read limit');
|
|
53
|
+
return await fs.readFile(target, 'utf8');
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (missing(error))
|
|
57
|
+
return null;
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async atomic(relative, content) {
|
|
62
|
+
const target = await this.bounded(relative, true), temp = `${target}.${randomUUID()}.tmp`;
|
|
63
|
+
try {
|
|
64
|
+
await fs.writeFile(temp, content, { flag: 'wx', mode: 0o600 });
|
|
65
|
+
await fs.rename(temp, target);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
await fs.rm(temp, { force: true });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async locked(operation) {
|
|
72
|
+
const lock = await this.bounded('.termdock/architecture.lock', true);
|
|
73
|
+
let handle;
|
|
74
|
+
const deadline = Date.now() + 10_000;
|
|
75
|
+
while (!handle) {
|
|
76
|
+
try {
|
|
77
|
+
handle = await fs.open(lock, 'wx', 0o600);
|
|
78
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid, time: Date.now() }));
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error.code !== 'EEXIST')
|
|
82
|
+
throw error;
|
|
83
|
+
if (Date.now() >= deadline)
|
|
84
|
+
throw new ArchitectureConflict('Another architecture update is in progress');
|
|
85
|
+
try {
|
|
86
|
+
const owner = JSON.parse(await fs.readFile(lock, 'utf8'));
|
|
87
|
+
try {
|
|
88
|
+
process.kill(owner.pid, 0);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (error.code === 'ESRCH') {
|
|
92
|
+
await fs.rm(lock, { force: true });
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch { /* A newly created lock may not yet contain its owner. */ }
|
|
98
|
+
await new Promise(resolve => setTimeout(resolve, 30));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
return await operation();
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
await handle.close();
|
|
106
|
+
await fs.rm(lock, { force: true });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async list() {
|
|
110
|
+
const result = [];
|
|
111
|
+
if (await this.text(ARCHITECTURE_FILE))
|
|
112
|
+
result.push(ARCHITECTURE_FILE);
|
|
113
|
+
const folder = await this.bounded(ARCHITECTURE_DIRECTORY);
|
|
114
|
+
try {
|
|
115
|
+
for (const entry of await fs.readdir(folder, { withFileTypes: true }))
|
|
116
|
+
if (entry.isFile() && isArchitectureFile(`${ARCHITECTURE_DIRECTORY}/${entry.name}`))
|
|
117
|
+
result.push(`${ARCHITECTURE_DIRECTORY}/${entry.name}`);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (!missing(error))
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
if ((await fs.readdir(await this.bounded('.omm'), { withFileTypes: true })).some(entry => entry.isDirectory() && safeName(entry.name)))
|
|
125
|
+
result.push('.omm');
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (!missing(error))
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
return result.sort();
|
|
132
|
+
}
|
|
133
|
+
async read(file = ARCHITECTURE_FILE) {
|
|
134
|
+
if (!isArchitectureFile(file))
|
|
135
|
+
throw new Error('Invalid architecture document');
|
|
136
|
+
if (file === '.omm')
|
|
137
|
+
return this.readOmm();
|
|
138
|
+
const content = await this.text(file);
|
|
139
|
+
return { document: content ? parseArchitecture(content) : null, revision: content ? digest(content) : null };
|
|
140
|
+
}
|
|
141
|
+
async git() {
|
|
142
|
+
try {
|
|
143
|
+
const [{ stdout: gitCommit }, { stdout: gitBranch }] = await Promise.all([runFile('git', ['rev-parse', '--short', 'HEAD'], { cwd: this.root, timeout: 2000 }), runFile('git', ['branch', '--show-current'], { cwd: this.root, timeout: 2000 })]);
|
|
144
|
+
return { gitCommit: gitCommit.trim(), gitBranch: gitBranch.trim() };
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return {};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
historyDirectory(file) { return `.termdock/architecture-history/${digest(file).slice(0, 24)}`; }
|
|
151
|
+
async archive(file, previous) {
|
|
152
|
+
if (!previous.document)
|
|
153
|
+
return;
|
|
154
|
+
const folder = this.historyDirectory(file);
|
|
155
|
+
const id = `${Date.now()}-${randomUUID()}`;
|
|
156
|
+
await this.atomic(`${folder}/${id}.json`, JSON.stringify(previous.document, null, 2));
|
|
157
|
+
const entries = (await fs.readdir(await this.bounded(folder))).filter(name => /^\d+-[a-f0-9-]+\.json$/.test(name)).sort().reverse();
|
|
158
|
+
for (const old of entries.slice(30))
|
|
159
|
+
await fs.unlink(await this.bounded(`${folder}/${old}`));
|
|
160
|
+
}
|
|
161
|
+
async history(file) {
|
|
162
|
+
if (!isArchitectureFile(file))
|
|
163
|
+
throw new Error('Invalid architecture document');
|
|
164
|
+
const current = await this.read(file);
|
|
165
|
+
if (!current.document)
|
|
166
|
+
return [];
|
|
167
|
+
const folder = this.historyDirectory(file);
|
|
168
|
+
let entries;
|
|
169
|
+
try {
|
|
170
|
+
entries = (await fs.readdir(await this.bounded(folder))).filter(name => /^\d+-[a-f0-9-]+\.json$/.test(name)).sort().reverse().slice(0, 30);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
if (missing(error))
|
|
174
|
+
return [];
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
return Promise.all(entries.map(async (name) => {
|
|
178
|
+
const previous = parseArchitecture((await this.text(`${folder}/${name}`)));
|
|
179
|
+
return { id: name.slice(0, -5), savedAt: previous.generatedAt, changes: diffArchitecture(previous, current.document) };
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
async historical(file, id) {
|
|
183
|
+
if (!isArchitectureFile(file) || !/^\d+-[a-f0-9-]+$/.test(id))
|
|
184
|
+
throw new Error('Invalid architecture version');
|
|
185
|
+
const content = await this.text(`${this.historyDirectory(file)}/${id}.json`);
|
|
186
|
+
if (!content)
|
|
187
|
+
throw new Error('Architecture version not found');
|
|
188
|
+
return parseArchitecture(content);
|
|
189
|
+
}
|
|
190
|
+
async save(file, input, expectedRevision) {
|
|
191
|
+
if (!isArchitectureFile(file) || file === '.omm')
|
|
192
|
+
throw new Error('Use field writes for .omm documents');
|
|
193
|
+
const document = parseArchitecture(JSON.stringify(input));
|
|
194
|
+
if (document.analysis && analysisFile(document.analysis) !== file)
|
|
195
|
+
throw new Error('Architecture scope does not match its file');
|
|
196
|
+
return this.locked(async () => {
|
|
197
|
+
const previous = await this.read(file);
|
|
198
|
+
if (expectedRevision !== undefined && previous.revision !== expectedRevision)
|
|
199
|
+
throw new ArchitectureConflict('Architecture changed; reload before saving');
|
|
200
|
+
const changes = diffArchitecture(previous.document, document);
|
|
201
|
+
if (previous.document && !changes.length && ['summary', 'analysis', 'details', 'origin'].every(key => JSON.stringify(previous.document[key]) === JSON.stringify(document[key])))
|
|
202
|
+
return previous;
|
|
203
|
+
await this.archive(file, previous);
|
|
204
|
+
const now = new Date().toISOString(), git = await this.git();
|
|
205
|
+
document.generatedAt = now;
|
|
206
|
+
document.meta = { ...previous.document?.meta, created: previous.document?.meta?.created ?? now, updated: now, updateCount: (previous.document?.meta?.updateCount ?? 0) + 1, ...git };
|
|
207
|
+
for (const view of document.perspectives) {
|
|
208
|
+
const oldView = previous.document?.perspectives.find(old => old.id === view.id);
|
|
209
|
+
view.meta = !oldView || changes.some(change => change.perspective === view.id) ? { created: oldView?.meta?.created ?? now, updated: now, updateCount: (oldView?.meta?.updateCount ?? 0) + 1, ...git, ...(oldView?.diagram ? { previousDiagram: oldView.diagram } : {}) } : oldView.meta;
|
|
210
|
+
for (const node of view.nodes) {
|
|
211
|
+
const old = oldView?.nodes.find(old => old.id === node.id);
|
|
212
|
+
if (!old || changes.some(change => change.perspective === view.id && change.node === node.id))
|
|
213
|
+
node.meta = { created: old?.meta?.created ?? now, updated: now, updateCount: (old?.meta?.updateCount ?? 0) + 1, ...git, ...(old?.diagram ? { previousDiagram: old.diagram } : {}) };
|
|
214
|
+
else
|
|
215
|
+
node.meta = old.meta;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const content = JSON.stringify(document, null, 2);
|
|
219
|
+
await this.atomic(file, content);
|
|
220
|
+
return { document, revision: digest(content) };
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
ommPath(element) {
|
|
224
|
+
if (!element || element.split('/').length > 12 || !element.split('/').every(safeName))
|
|
225
|
+
throw new Error('Invalid .omm element path');
|
|
226
|
+
return `.omm/${element}`;
|
|
227
|
+
}
|
|
228
|
+
async ommElements() {
|
|
229
|
+
const result = [];
|
|
230
|
+
const walk = async (relative, depth) => {
|
|
231
|
+
if (depth > 12 || result.length > 2400)
|
|
232
|
+
throw new Error('.omm tree exceeds the architecture limit');
|
|
233
|
+
let entries;
|
|
234
|
+
try {
|
|
235
|
+
entries = await fs.readdir(await this.bounded(relative), { withFileTypes: true });
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
if (missing(error))
|
|
239
|
+
return;
|
|
240
|
+
throw error;
|
|
241
|
+
}
|
|
242
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
243
|
+
if (!safeName(entry.name))
|
|
244
|
+
continue;
|
|
245
|
+
if (entry.isSymbolicLink())
|
|
246
|
+
throw new Error('.omm tree cannot contain symlinks');
|
|
247
|
+
if (entry.isDirectory()) {
|
|
248
|
+
const next = `${relative}/${entry.name}`;
|
|
249
|
+
result.push(next.slice(5));
|
|
250
|
+
await walk(next, depth + 1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
await walk('.omm', 0);
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
257
|
+
async readOmmFields(element) {
|
|
258
|
+
const base = this.ommPath(element), fields = {};
|
|
259
|
+
for (const field of ARCHITECTURE_FIELDS) {
|
|
260
|
+
const value = await this.text(`${base}/${names[field]}`);
|
|
261
|
+
if (value !== null)
|
|
262
|
+
fields[field] = value;
|
|
263
|
+
}
|
|
264
|
+
const raw = await this.text(`${base}/meta.yaml`);
|
|
265
|
+
if (!raw)
|
|
266
|
+
return fields;
|
|
267
|
+
const m = YAML.parse(raw, { maxAliasCount: 10 });
|
|
268
|
+
const meta = {};
|
|
269
|
+
for (const [from, to] of [['created', 'created'], ['updated', 'updated'], ['git_commit', 'gitCommit'], ['git_branch', 'gitBranch'], ['prev_diagram', 'previousDiagram']])
|
|
270
|
+
if (typeof m?.[from] === 'string')
|
|
271
|
+
meta[to] = m[from];
|
|
272
|
+
if (typeof m?.update_count === 'number' && Number.isSafeInteger(m.update_count) && m.update_count >= 0)
|
|
273
|
+
meta.updateCount = m.update_count;
|
|
274
|
+
return { ...fields, meta };
|
|
275
|
+
}
|
|
276
|
+
async validateOmmMerge(files) {
|
|
277
|
+
const temporary = await fs.mkdtemp(path.join(os.tmpdir(), 'termdock-omm-import-'));
|
|
278
|
+
try {
|
|
279
|
+
const stage = new ArchitectureStore(temporary), merged = { ...(await this.exportOmmBundle()).files, ...files };
|
|
280
|
+
for (const [name, value] of Object.entries(merged))
|
|
281
|
+
await stage.atomic(`.omm/${name}`, value);
|
|
282
|
+
const result = await stage.readOmm();
|
|
283
|
+
if (!result.document)
|
|
284
|
+
throw new Error('Bundle contains no architecture perspectives');
|
|
285
|
+
}
|
|
286
|
+
finally {
|
|
287
|
+
await fs.rm(temporary, { recursive: true, force: true });
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
async writeOmmField(element, field, content, expectedRevision) {
|
|
291
|
+
if (!ARCHITECTURE_FIELDS.includes(field) || typeof content !== 'string' || content.length > 100_000)
|
|
292
|
+
throw new Error('Invalid architecture field');
|
|
293
|
+
const base = this.ommPath(element);
|
|
294
|
+
if (field === 'diagram') {
|
|
295
|
+
const issues = parseFlowchart(content).issues.filter(issue => !issue.startsWith('Connection needs'));
|
|
296
|
+
if (issues.length)
|
|
297
|
+
throw new Error(issues.join('\n'));
|
|
298
|
+
}
|
|
299
|
+
return this.locked(async () => {
|
|
300
|
+
const previous = await this.readOmm();
|
|
301
|
+
if (expectedRevision !== undefined && previous.revision !== expectedRevision)
|
|
302
|
+
throw new ArchitectureConflict('Architecture changed; reload before saving');
|
|
303
|
+
const old = await this.readOmmFields(element);
|
|
304
|
+
if (old[field] === content)
|
|
305
|
+
return previous;
|
|
306
|
+
await this.validateOmmMerge({ [`${element}/${names[field]}`]: content });
|
|
307
|
+
await this.archive('.omm', previous);
|
|
308
|
+
const now = new Date().toISOString();
|
|
309
|
+
const git = await this.git();
|
|
310
|
+
// Field writes preserve the upstream file layout and metadata convention.
|
|
311
|
+
await this.atomic(`${base}/${names[field]}`, content);
|
|
312
|
+
const meta = { created: old.meta?.created ?? now, updated: now, update_count: (old.meta?.updateCount ?? 0) + 1,
|
|
313
|
+
last_field: field, git_commit: git.gitCommit, git_branch: git.gitBranch,
|
|
314
|
+
...(field === 'diagram' && old.diagram ? { prev_diagram: old.diagram } : old.meta?.previousDiagram ? { prev_diagram: old.meta.previousDiagram } : {}) };
|
|
315
|
+
await this.atomic(`${base}/meta.yaml`, YAML.stringify(meta));
|
|
316
|
+
return this.readOmm();
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
async deleteOmmElement(element) { const target = this.ommPath(element); await this.locked(async () => { await this.archive('.omm', await this.readOmm()); await fs.rm(await this.bounded(target), { recursive: true, force: false }); }); }
|
|
320
|
+
async readOmm() {
|
|
321
|
+
const elements = await this.ommElements();
|
|
322
|
+
if (!elements.length)
|
|
323
|
+
return { document: null, revision: null };
|
|
324
|
+
const data = new Map();
|
|
325
|
+
let total = 0;
|
|
326
|
+
for (let offset = 0; offset < elements.length; offset += 16) {
|
|
327
|
+
for (const [element, fields] of await Promise.all(elements.slice(offset, offset + 16).map(async (element) => [element, await this.readOmmFields(element)]))) {
|
|
328
|
+
total += Buffer.byteLength(JSON.stringify(fields), 'utf8');
|
|
329
|
+
if (total > 5_000_000)
|
|
330
|
+
throw new Error('.omm documentation exceeds the read limit');
|
|
331
|
+
data.set(element, fields);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const perspectives = [];
|
|
335
|
+
const uid = (element) => `e_${digest(element).slice(0, 20)}`;
|
|
336
|
+
const label = (value) => value.replace(/-/g, ' ').replace(/^\w/, ch => ch.toUpperCase());
|
|
337
|
+
const links = [];
|
|
338
|
+
for (const perspective of elements.filter(element => !element.includes('/'))) {
|
|
339
|
+
const fields = data.get(perspective);
|
|
340
|
+
const nodes = new Map(), edges = [];
|
|
341
|
+
const add = async (element, parentId, diagramLabel) => {
|
|
342
|
+
const old = nodes.get(element);
|
|
343
|
+
if (old)
|
|
344
|
+
return old;
|
|
345
|
+
const entry = data.get(element) ?? {};
|
|
346
|
+
const lines = (diagramLabel ?? '').split('\n');
|
|
347
|
+
const files = [];
|
|
348
|
+
for (const line of lines.slice(1)) {
|
|
349
|
+
const relative = line.trim();
|
|
350
|
+
if (isArchitectureSourcePath(relative)) {
|
|
351
|
+
try {
|
|
352
|
+
if ((await fs.stat(await this.bounded(relative))).isFile())
|
|
353
|
+
files.push({ path: relative });
|
|
354
|
+
}
|
|
355
|
+
catch { /* Documentation can reference a directory or a removed file. */ }
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const node = { id: uid(element), documentPath: element, title: lines[0]?.replace(/^@/, '') || label(element.split('/').pop()), summary: (entry.description ?? '').replace(/[#*`]/g, '').split(/\n\s*\n/)[0].slice(0, 600), files, ...entry, ...(parentId ? { parentId } : {}) };
|
|
359
|
+
nodes.set(element, node);
|
|
360
|
+
const graph = parseFlowchart(entry.diagram ?? '');
|
|
361
|
+
for (const child of graph.nodes) {
|
|
362
|
+
const childElement = `${element}/${child.id}`, childNode = await add(childElement, node.id, child.label);
|
|
363
|
+
childNode.role = child.role;
|
|
364
|
+
if (child.ref)
|
|
365
|
+
links.push({ node: childNode, target: child.ref });
|
|
366
|
+
}
|
|
367
|
+
for (const edge of graph.edges)
|
|
368
|
+
edges.push({ from: uid(`${element}/${edge.from}`), to: uid(`${element}/${edge.to}`), label: edge.label });
|
|
369
|
+
// Include documented children even if an old diagram has not mentioned them yet.
|
|
370
|
+
for (const child of elements.filter(child => child.startsWith(`${element}/`) && child.slice(element.length + 1).indexOf('/') === -1))
|
|
371
|
+
await add(child, node.id);
|
|
372
|
+
return node;
|
|
373
|
+
};
|
|
374
|
+
const graph = parseFlowchart(fields.diagram ?? '');
|
|
375
|
+
for (const child of graph.nodes) {
|
|
376
|
+
const node = await add(`${perspective}/${child.id}`, undefined, child.label);
|
|
377
|
+
node.role = child.role;
|
|
378
|
+
if (child.ref)
|
|
379
|
+
links.push({ node, target: child.ref });
|
|
380
|
+
}
|
|
381
|
+
for (const child of elements.filter(child => child.startsWith(`${perspective}/`) && child.split('/').length === 2))
|
|
382
|
+
await add(child);
|
|
383
|
+
for (const edge of graph.edges)
|
|
384
|
+
edges.push({ from: uid(`${perspective}/${edge.from}`), to: uid(`${perspective}/${edge.to}`), label: edge.label });
|
|
385
|
+
if (!nodes.size)
|
|
386
|
+
nodes.set(perspective, { id: uid(perspective), title: label(perspective), summary: fields.description ?? '', files: [], ...fields });
|
|
387
|
+
perspectives.push({ id: perspective, title: label(perspective), summary: (fields.description ?? '').split(/\n\s*\n/)[0].slice(0, 600), nodes: [...nodes.values()], edges, ...fields });
|
|
388
|
+
}
|
|
389
|
+
for (const link of links)
|
|
390
|
+
if (perspectives.some(view => view.id === link.target))
|
|
391
|
+
link.node.references = [...(link.node.references ?? []), { perspective: link.target }];
|
|
392
|
+
const generatedAt = [...data.values()].map(value => value.meta?.updated).filter((value) => !!value && Number.isFinite(Date.parse(value))).sort().pop() ?? new Date(0).toISOString();
|
|
393
|
+
const document = parseArchitecture(JSON.stringify({ version: 1, origin: 'omm', generatedAt, summary: `${path.basename(this.root)} architecture`, perspectives }));
|
|
394
|
+
const config = await this.text('.omm/config.yaml');
|
|
395
|
+
return { document, revision: digest(JSON.stringify([...data]) + (config ?? '')) };
|
|
396
|
+
}
|
|
397
|
+
async config(language) {
|
|
398
|
+
const raw = await this.text('.omm/config.yaml');
|
|
399
|
+
const config = raw ? YAML.parse(raw, { maxAliasCount: 10 }) : { version: '0.1.0', language: 'en' };
|
|
400
|
+
if (!config || typeof config !== 'object' || Array.isArray(config))
|
|
401
|
+
throw new Error('Invalid .omm configuration');
|
|
402
|
+
if (language !== undefined) {
|
|
403
|
+
if (!/^[a-z]{2}(?:-[A-Z]{2})?$/.test(language))
|
|
404
|
+
throw new Error('Invalid language');
|
|
405
|
+
config.language = language;
|
|
406
|
+
await this.locked(() => this.atomic('.omm/config.yaml', YAML.stringify(config)));
|
|
407
|
+
}
|
|
408
|
+
return { language: typeof config?.language === 'string' ? config.language : 'en' };
|
|
409
|
+
}
|
|
410
|
+
async exportOmmBundle(document) {
|
|
411
|
+
const files = {};
|
|
412
|
+
if (!document) {
|
|
413
|
+
for (const element of await this.ommElements()) {
|
|
414
|
+
for (const name of [...Object.values(names), 'meta.yaml']) {
|
|
415
|
+
const value = await this.text(`.omm/${element}/${name}`);
|
|
416
|
+
if (value !== null)
|
|
417
|
+
files[`${element}/${name}`] = value;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const config = await this.text('.omm/config.yaml');
|
|
421
|
+
if (config)
|
|
422
|
+
files['config.yaml'] = config;
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
files['config.yaml'] = YAML.stringify({ version: '0.1.0' });
|
|
426
|
+
for (const view of document.perspectives) {
|
|
427
|
+
const elementPath = (node) => {
|
|
428
|
+
const ids = [], visited = new Set();
|
|
429
|
+
while (node) {
|
|
430
|
+
if (visited.has(node.id))
|
|
431
|
+
throw new Error('Cyclic architecture');
|
|
432
|
+
visited.add(node.id);
|
|
433
|
+
ids.unshift(node.id);
|
|
434
|
+
node = view.nodes.find(parent => parent.id === node.parentId);
|
|
435
|
+
}
|
|
436
|
+
return [view.id, ...ids].join('/');
|
|
437
|
+
};
|
|
438
|
+
for (const item of [view, ...view.nodes]) {
|
|
439
|
+
const node = item === view ? undefined : item, base = elementPath(node), scope = architectureScope(view, node?.id);
|
|
440
|
+
for (const field of ARCHITECTURE_FIELDS) {
|
|
441
|
+
const value = item[field] ?? (field === 'description' ? item.summary : undefined);
|
|
442
|
+
if (value !== undefined && field !== 'diagram')
|
|
443
|
+
files[`${base}/${names[field]}`] = value;
|
|
444
|
+
}
|
|
445
|
+
if (scope.nodes.length) {
|
|
446
|
+
const label = (value) => value.replace(/["<>|`\\\r\n]/g, ' ');
|
|
447
|
+
files[`${base}/diagram.mmd`] = ['graph LR', ...scope.nodes.map(child => `${child.id}["${child.references?.[0]?.perspective ? `@${child.references[0].perspective}` : label(child.title)}${child.files.length ? `\\n${child.files.map(file => file.path).join('\\n')}` : ''}"]`), ...scope.edges.map(edge => `${edge.from} -->|"${label(edge.label)}"| ${edge.to}`), ...scope.nodes.filter(child => child.role).map(child => `class ${child.id} ${child.role}`)].join('\n');
|
|
448
|
+
}
|
|
449
|
+
else if (item.diagram)
|
|
450
|
+
files[`${base}/diagram.mmd`] = item.diagram;
|
|
451
|
+
const meta = item.meta ?? { created: document.generatedAt, updated: document.generatedAt, updateCount: 0 };
|
|
452
|
+
files[`${base}/meta.yaml`] = YAML.stringify({ created: meta.created, updated: meta.updated, update_count: meta.updateCount, git_commit: meta.gitCommit, git_branch: meta.gitBranch, prev_diagram: meta.previousDiagram });
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (Buffer.byteLength(JSON.stringify(files), 'utf8') > 5_000_000)
|
|
457
|
+
throw new Error('Export exceeds the architecture limit');
|
|
458
|
+
return { format: 'termdock-omm-bundle', version: 1, files };
|
|
459
|
+
}
|
|
460
|
+
async importOmmBundle(input, expectedRevision) {
|
|
461
|
+
const bundle = input;
|
|
462
|
+
if (bundle?.format !== 'termdock-omm-bundle' || bundle.version !== 1 || !bundle.files || typeof bundle.files !== 'object' || Array.isArray(bundle.files))
|
|
463
|
+
throw new Error('Invalid .omm bundle');
|
|
464
|
+
const files = Object.entries(bundle.files);
|
|
465
|
+
if (!files.length || files.length > 16_801 || Buffer.byteLength(JSON.stringify(input), 'utf8') > 5_000_000)
|
|
466
|
+
throw new Error('Invalid .omm bundle size');
|
|
467
|
+
for (const [name, value] of files) {
|
|
468
|
+
const parts = name.split('/'), filename = parts.pop();
|
|
469
|
+
if (!(name === 'config.yaml' || (parts.length > 0 && parts.length <= 12 && parts.every(safeName) && [...Object.values(names), 'meta.yaml'].includes(filename))) || typeof value !== 'string' || value.length > 100_000)
|
|
470
|
+
throw new Error(`Unsafe .omm bundle file: ${name}`);
|
|
471
|
+
await this.bounded(`.omm/${name}`);
|
|
472
|
+
if (filename?.endsWith('.yaml'))
|
|
473
|
+
YAML.parse(value, { maxAliasCount: 10 });
|
|
474
|
+
}
|
|
475
|
+
await this.locked(async () => {
|
|
476
|
+
const previous = await this.readOmm();
|
|
477
|
+
if (expectedRevision !== undefined && previous.revision !== expectedRevision)
|
|
478
|
+
throw new ArchitectureConflict('Architecture changed; reload before importing');
|
|
479
|
+
// Validate the merged tree in isolation before touching the project's documents.
|
|
480
|
+
await this.validateOmmMerge(Object.fromEntries(files));
|
|
481
|
+
await this.archive('.omm', previous);
|
|
482
|
+
// A bundle merges explicit files, preserving unrelated existing perspectives.
|
|
483
|
+
// Every destination is checked before the first write; no business files are touched.
|
|
484
|
+
const undo = [];
|
|
485
|
+
try {
|
|
486
|
+
for (const [name, value] of files) {
|
|
487
|
+
undo.push([name, await this.text(`.omm/${name}`)]);
|
|
488
|
+
await this.atomic(`.omm/${name}`, value);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
catch (error) {
|
|
492
|
+
for (const [name, value] of undo.reverse()) {
|
|
493
|
+
if (value === null)
|
|
494
|
+
await fs.rm(await this.bounded(`.omm/${name}`), { force: true });
|
|
495
|
+
else
|
|
496
|
+
await this.atomic(`.omm/${name}`, value);
|
|
497
|
+
}
|
|
498
|
+
throw error;
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
package/dist/server/cli.js
CHANGED
|
@@ -10,6 +10,10 @@ 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
|
+
if (['architecture', 'arch'].includes(process.argv[2] ?? '')) {
|
|
14
|
+
const { runArchitectureCli } = await import('./architecture/cli.js');
|
|
15
|
+
process.exit(await runArchitectureCli(process.argv.slice(3)));
|
|
16
|
+
}
|
|
13
17
|
// Federation commands run before normal server/terminal CLI initialization.
|
|
14
18
|
if (process.argv.slice(2).some(arg => arg === '--federation-pairing' || arg === '--federation-relay')) {
|
|
15
19
|
const { runFederationCli } = await import('./federation/cli.js');
|
|
@@ -199,6 +203,8 @@ Options:
|
|
|
199
203
|
-h, --help Show this help message
|
|
200
204
|
|
|
201
205
|
Short commands:
|
|
206
|
+
architecture Analyze, browse and edit recursive project architecture
|
|
207
|
+
(see td architecture --help)
|
|
202
208
|
agent-plugin [--json]
|
|
203
209
|
Explain how an Agent integrates itself. JSON mode is
|
|
204
210
|
machine-readable and intended for Agent consumption.
|
|
@@ -2112,7 +2118,7 @@ function runPluginInitJson() {
|
|
|
2112
2118
|
collaboration: {
|
|
2113
2119
|
capabilities: 'Optional routing hints shown beside this Agent in collaboration groups.',
|
|
2114
2120
|
universalTransport: 'Every detected Agent receives durable inbox messages through its PTY and can use td collab when it has shell access.',
|
|
2115
|
-
hooks: 'Hooks
|
|
2121
|
+
hooks: 'Hooks publish declared lifecycle/status events; collaboration does not wait for an idle phase and does not require hooks.',
|
|
2116
2122
|
},
|
|
2117
2123
|
security: {
|
|
2118
2124
|
source: 'Only public HTTPS repository roots on approved Git hosts or explicit local paths are accepted; URL credentials are rejected.',
|
package/dist/server/entry.js
CHANGED
|
@@ -38,6 +38,7 @@ import { exitWithStartupFailure } from './utils/startupFailure.js';
|
|
|
38
38
|
import { androidRecordings } from './android/recording.js';
|
|
39
39
|
import { stopAllScrcpySessions } from './android/scrcpy.js';
|
|
40
40
|
import filesystemRoutes from './routes/filesystem.js';
|
|
41
|
+
import { architectureRoutes } from './architecture/routes.js';
|
|
41
42
|
import authRoutes from './routes/auth.js';
|
|
42
43
|
import notificationRoutes from './routes/notifications.js';
|
|
43
44
|
import { createOnboardingRouter } from './routes/onboarding.js';
|
|
@@ -346,6 +347,7 @@ export function createApp(options = {}) {
|
|
|
346
347
|
app.use('/api/terminal', terminalRoutes);
|
|
347
348
|
// 文件系统路由(继承 /api/terminal 上的 auth + CSRF 保护)
|
|
348
349
|
app.use('/api/terminal/fs', filesystemRoutes);
|
|
350
|
+
app.use('/api/terminal/architecture', architectureRoutes());
|
|
349
351
|
// 本机 Android 设备桥接(adb/scrcpy)。经加密隧道时按业务 API 处理,
|
|
350
352
|
// 仅全权服务授权可达;投屏 WS 在 federation runtime 中按 full-service 校验。
|
|
351
353
|
app.use('/api/android', requireAuth({ bypass: isTrustedLocalCliRequest }));
|
|
@@ -40,7 +40,7 @@ const fileReads = new Set(['list', 'watch', 'search', 'read', 'git-blob', 'eda-p
|
|
|
40
40
|
const filePosts = new Set(['git-action', 'apply-hunk', 'upload', 'open-in-file-browser']);
|
|
41
41
|
const settings = new Set(['settings', 'toolbar-presets', 'program-detection', 'context-draft']);
|
|
42
42
|
const serviceReads = new Set(['update', 'auto-title/catalog', 'agent-hooks', 'agent-launchers', 'agent-plugins', 'quota', 'tmux/status', 'tmux/sessions', 'agent-resume-history']);
|
|
43
|
-
const reservedSessionIds = new Set(['fs', 'settings', 'client-state', 'session-inventory', 'operations', 'tmux', 'create', 'force-kill', 'serialize-state', 'agent-resume-history', 'toolbar-presets', 'context-draft', 'program-detection', 'update', 'quota', 'agent-hooks', 'agent-launchers', 'agent-plugins', 'agent-plugin-icon', 'auto-title', 'directory-suggestions']);
|
|
43
|
+
const reservedSessionIds = new Set(['architecture', 'fs', 'settings', 'client-state', 'session-inventory', 'operations', 'tmux', 'create', 'force-kill', 'serialize-state', 'agent-resume-history', 'toolbar-presets', 'context-draft', 'program-detection', 'update', 'quota', 'agent-hooks', 'agent-launchers', 'agent-plugins', 'agent-plugin-icon', 'auto-title', 'directory-suggestions']);
|
|
44
44
|
export function resolveAccessRequest(method, path) {
|
|
45
45
|
if (!isBusinessApiPath(method, path))
|
|
46
46
|
return null;
|
|
@@ -57,6 +57,14 @@ export function resolveAccessRequest(method, path) {
|
|
|
57
57
|
if (!pathname.startsWith(prefix))
|
|
58
58
|
return null;
|
|
59
59
|
const route = pathname.slice(prefix.length);
|
|
60
|
+
if (route.startsWith('architecture/')) {
|
|
61
|
+
const op = route.slice('architecture/'.length);
|
|
62
|
+
if (read && ['documents', 'document', 'history', 'version', 'bundle'].includes(op))
|
|
63
|
+
return { action: 'file.read' };
|
|
64
|
+
if (method === 'PUT' && ['document', 'field', 'bundle'].includes(op))
|
|
65
|
+
return { action: 'file.write' };
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
60
68
|
if (route.startsWith('fs/')) {
|
|
61
69
|
const op = route.slice(3);
|
|
62
70
|
if (read && (fileReads.has(op) || /^preview\/.+/.test(op)))
|
package/docs/integrations.md
CHANGED
|
@@ -209,6 +209,46 @@ td integration update --file /private/policy.json
|
|
|
209
209
|
未配置 `startupInput` 的旧 profile 保留原投递方式;TD 不猜测各家 TUI 的加载画面。
|
|
210
210
|
管理员 CLI 会先读取运行服务能力,旧服务缺接口或能力时失败,不会忽略条件后继续配置。
|
|
211
211
|
|
|
212
|
+
### 清除既有会话的屏幕启动条件(1.4.310)
|
|
213
|
+
|
|
214
|
+
从策略中删除 `startupInput` 只影响新会话,既有会话仍保留原启动快照。需要让指定
|
|
215
|
+
旧会话使用普通终端投递时,由本机管理员通过正式入口显式解除:
|
|
216
|
+
|
|
217
|
+
```sh
|
|
218
|
+
# 先由持有该会话的身份回读当前 generation
|
|
219
|
+
td collab --principal report-bridge session get <TD-session-id>
|
|
220
|
+
# 本机管理员执行,不使用 principal 凭据;generation 必须取当前实际值
|
|
221
|
+
td integration clear-startup-input <TD-session-id> --generation <N>
|
|
222
|
+
td collab --principal report-bridge session get <TD-session-id>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
最低 CLI / 实际服务版本均为 **1.4.310**;先回读能力
|
|
226
|
+
`session_startup_input_clear=true`、`session_startup_input_clear_min_version=1.4.310`。
|
|
227
|
+
CLI 在旧服务上返回 `SESSION_STARTUP_INPUT_CLEAR_UNSUPPORTED`,不尝试修改私有文件。
|
|
228
|
+
本机认证接口为 `POST /admin/sessions/:id/startup-input/clear`,请求仅含
|
|
229
|
+
`{"generation":N}`,回复 `{session: …}`;集成 principal 无权调用这个管理员入口。
|
|
230
|
+
|
|
231
|
+
操作原子持久化删除该记录的 `profile.startupInput`、`startup_condition` 和
|
|
232
|
+
`startup_input`,设置公开字段 **`startup_input_disabled=true`**,generation 增加 1。
|
|
233
|
+
以后对该逻辑会话执行实际 restore 也不重新继承 profile 的屏幕条件;新创建的其它
|
|
234
|
+
会话仍按各自策略启动。此入口只支持显式禁用,不提供重新启用或更换启动配置。
|
|
235
|
+
原逻辑 ID / 原生 UUID / argv / model / cwd / 包装器 / 配置指纹 / operation ID /
|
|
236
|
+
终端绑定 / 消息历史及 reply 关联保持原状,不启动、退出或重建 Agent。
|
|
237
|
+
会话事件仍使用现有 `session.<state>`,payload 带更新后的 session / generation /
|
|
238
|
+
`startup_input_disabled`,沿原持久订阅与 ACK 机制推送。
|
|
239
|
+
|
|
240
|
+
管理员解除条件后,**已经 pending 的消息可能随下一次投递继续发送**;不重发
|
|
241
|
+
`delivered` 正文。它解除的是额外画面限制,不证明 Agent 输入已初始化、已读、空闲
|
|
242
|
+
或已消费,`delivered` 仍只表示终端写入,结果仍看明确回复。运行中亦可执行,
|
|
243
|
+
不需要修改窗口尺寸或等待模型/footer 文案。
|
|
244
|
+
|
|
245
|
+
原子保存失败不解除条件。代次不匹配返回 `SESSION_GENERATION_MISMATCH`,缺失或
|
|
246
|
+
非法代次返回 `INVALID_SESSION_GENERATION`;有启动/恢复/释放进行中或结果不明时
|
|
247
|
+
返回 `SESSION_OPERATION_IN_PROGRESS`;记录不存在返回 `INTEGRATION_SESSION_NOT_FOUND`。
|
|
248
|
+
用新 generation 对已禁用会话重复清除是 no-op,不再增加代次;旧 generation 仍
|
|
249
|
+
拒绝。若命令回执丢失,先 get 查证新代次与禁用字段,不盲重试旧参数。既有 release /
|
|
250
|
+
restore 幂等键的历史结果保持不变,以 get 为当前状态依据。
|
|
251
|
+
|
|
212
252
|
## 版本与能力核验
|
|
213
253
|
|
|
214
254
|
```sh
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "termdock",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.310",
|
|
4
4
|
"description": "A complete web-based terminal application with modern UI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": ".desktop-dist/main.js",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
".": "./dist/server/entry.js"
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
|
+
"THIRD_PARTY_NOTICES.md",
|
|
15
16
|
"dist",
|
|
16
17
|
"runtime-manifest.json",
|
|
17
18
|
"README.md",
|
|
@@ -101,6 +102,7 @@
|
|
|
101
102
|
"uuid": "^13.0.0",
|
|
102
103
|
"web-push": "^3.6.7",
|
|
103
104
|
"ws": "^8.21.0",
|
|
105
|
+
"yaml": "^2.9.1",
|
|
104
106
|
"zustand": "^4.5.0"
|
|
105
107
|
},
|
|
106
108
|
"devDependencies": {
|