@cyberismo/backend 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/app.js +22 -11
- package/dist/app.js.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.js +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/keycloak.d.ts +2 -3
- package/dist/auth/keycloak.js +6 -0
- package/dist/auth/keycloak.js.map +1 -1
- package/dist/auth/mock.d.ts +12 -8
- package/dist/auth/mock.js +119 -31
- package/dist/auth/mock.js.map +1 -1
- package/dist/auth/types.d.ts +7 -1
- package/dist/domain/cards/index.js +11 -84
- package/dist/domain/cards/index.js.map +1 -1
- package/dist/domain/cards/lib.js +2 -11
- package/dist/domain/cards/lib.js.map +1 -1
- package/dist/domain/cards/schema.js +9 -3
- package/dist/domain/cards/schema.js.map +1 -1
- package/dist/domain/cards/service.d.ts +2 -5
- package/dist/domain/cards/service.js +8 -15
- package/dist/domain/cards/service.js.map +1 -1
- package/dist/domain/events/index.d.ts +15 -0
- package/dist/domain/events/index.js +94 -0
- package/dist/domain/events/index.js.map +1 -0
- package/dist/domain/events/lifetime.d.ts +14 -0
- package/dist/domain/events/lifetime.js +25 -0
- package/dist/domain/events/lifetime.js.map +1 -0
- package/dist/domain/events/project-events.d.ts +67 -0
- package/dist/domain/events/project-events.js +200 -0
- package/dist/domain/events/project-events.js.map +1 -0
- package/dist/domain/events/schema.d.ts +22 -0
- package/dist/domain/events/schema.js +20 -0
- package/dist/domain/events/schema.js.map +1 -0
- package/dist/domain/project/index.js +17 -5
- package/dist/domain/project/index.js.map +1 -1
- package/dist/domain/project/schema.d.ts +8 -0
- package/dist/domain/project/schema.js +34 -6
- package/dist/domain/project/schema.js.map +1 -1
- package/dist/domain/project/service.d.ts +9 -5
- package/dist/domain/project/service.js +22 -16
- package/dist/domain/project/service.js.map +1 -1
- package/dist/domain/projects/index.js +5 -1
- package/dist/domain/projects/index.js.map +1 -1
- package/dist/domain/resources/index.js +74 -2
- package/dist/domain/resources/index.js.map +1 -1
- package/dist/domain/resources/schema.d.ts +8 -2
- package/dist/domain/resources/schema.js +6 -1
- package/dist/domain/resources/schema.js.map +1 -1
- package/dist/domain/resources/service.d.ts +13 -1
- package/dist/domain/resources/service.js +45 -2
- package/dist/domain/resources/service.js.map +1 -1
- package/dist/domain/skills/index.d.ts +15 -0
- package/dist/domain/skills/index.js +52 -0
- package/dist/domain/skills/index.js.map +1 -0
- package/dist/domain/skills/schema.d.ts +16 -0
- package/dist/domain/skills/schema.js +18 -0
- package/dist/domain/skills/schema.js.map +1 -0
- package/dist/domain/skills/service.d.ts +14 -0
- package/dist/domain/skills/service.js +16 -0
- package/dist/domain/skills/service.js.map +1 -0
- package/dist/export.d.ts +3 -2
- package/dist/export.js +53 -38
- package/dist/export.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/main.js +23 -10
- package/dist/main.js.map +1 -1
- package/dist/middleware/auth.d.ts +5 -0
- package/dist/middleware/auth.js +20 -4
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/commandManager.d.ts +2 -0
- package/dist/middleware/commandManager.js +1 -0
- package/dist/middleware/commandManager.js.map +1 -1
- package/dist/overlay.d.ts +45 -0
- package/dist/overlay.js +157 -0
- package/dist/overlay.js.map +1 -0
- package/dist/project-errors.d.ts +18 -0
- package/dist/project-errors.js +25 -0
- package/dist/project-errors.js.map +1 -0
- package/dist/project-registry.d.ts +9 -1
- package/dist/project-registry.js +37 -9
- package/dist/project-registry.js.map +1 -1
- package/dist/public/THIRD-PARTY.txt +528 -435
- package/dist/public/assets/__vite-browser-external-PaJ7c2l8.js +1 -0
- package/dist/public/assets/abnfDiagram-VCTEODGH-DB2dfOGe.js +1 -0
- package/dist/public/assets/architecture-7GRP2DOG-2V0X8hxt.js +1 -0
- package/dist/public/assets/architectureDiagram-5GKGNRK7-CRhUR2Ul.js +36 -0
- package/dist/public/assets/blockDiagram-I7D4REHJ-DJKsYF1v.js +129 -0
- package/dist/public/assets/c4Diagram-7LVT6UL2-BTwMzoAF.js +38 -0
- package/dist/public/assets/channel-C0JtPpMA.js +1 -0
- package/dist/public/assets/chunk-4HAMMTFA-DjjgrBsB.js +62 -0
- package/dist/public/assets/chunk-75Z2AOVW-CKqXRGJV.js +2 -0
- package/dist/public/assets/chunk-DU6HZSFF-iJGdiRt7.js +127 -0
- package/dist/public/assets/chunk-F27PBJKO-tDfs6zeO.js +1 -0
- package/dist/public/assets/chunk-FOHPRMQF-DzxwRta7.js +161 -0
- package/dist/public/assets/chunk-GMAD6QVW-DxHVEaGj.js +72 -0
- package/dist/public/assets/chunk-GVQU2GXP-AMxD4_Ta.js +1 -0
- package/dist/public/assets/{chunk-EX3LRPZG-Db0XYyxy.js → chunk-IMKFNOWR-0rz8QrYC.js} +2 -2
- package/dist/public/assets/chunk-L3NEJ4N5-BK3KAKMe.js +1 -0
- package/dist/public/assets/chunk-OSK3NFVY-B8MdrI_M.js +10 -0
- package/dist/public/assets/chunk-P2QGCYS3-D3J5AcHg.js +1 -0
- package/dist/public/assets/{chunk-VR4S4FIN-tUhbELKr.js → chunk-POPQ4Y6H-CsYg_XkK.js} +1 -1
- package/dist/public/assets/{chunk-HOUHSVGY-L35GPy9G.js → chunk-PWAF6VOD-D9cdEIZp.js} +1 -1
- package/dist/public/assets/chunk-SHT3W25Y-1jjokCf3.js +168 -0
- package/dist/public/assets/{chunk-MOJQB5TN-D1shKbMs.js → chunk-SVP7TREG-CIQMl7hl.js} +1 -1
- package/dist/public/assets/{chunk-V7JOEXUC-71b0ABpv.js → chunk-TICWLB2K-Ccsy2PYz.js} +3 -3
- package/dist/public/assets/chunk-XXDRQBXY-B7bWyHjt.js +1 -0
- package/dist/public/assets/classDiagram-ZZMXUADV-mWyXNAXi.js +1 -0
- package/dist/public/assets/classDiagram-v2-VYDZK3BY-mWyXNAXi.js +1 -0
- package/dist/public/assets/cose-bilkent-JH36ORCC-DPzc3Pv4.js +1 -0
- package/dist/public/assets/cynefin-OW5HDTMX-BIVDCYOU.js +1 -0
- package/dist/public/assets/cynefinDiagram-5FMLGOSQ-_V_nrIhD.js +62 -0
- package/dist/public/assets/cytoscape.esm-NgwPT4ez.js +321 -0
- package/dist/public/assets/dagre-C-rnJpZF.js +1 -0
- package/dist/public/assets/dagre-GXQ25YYZ-CJc0wc6f.js +4 -0
- package/dist/public/assets/diagram-S7CK7UJ4-25IjIKyo.js +30 -0
- package/dist/public/assets/diagram-UQ7AKVKN-C5VvsV7x.js +41 -0
- package/dist/public/assets/{diagram-FQU43EPY-DS-ryuRg.js → diagram-VSXAHHWV-Dw3rKVL_.js} +3 -3
- package/dist/public/assets/diagram-VX7I27RA-De_UQv8C.js +24 -0
- package/dist/public/assets/diagram-Z3DM3KII-Bd-65QQW.js +24 -0
- package/dist/public/assets/dist-BDCdLWk4.js +1 -0
- package/dist/public/assets/ebnfDiagram-PWID7BFC-CrxdET5U.js +1 -0
- package/dist/public/assets/erDiagram-RLTQ6QDP-BKo0Uyna.js +99 -0
- package/dist/public/assets/eventmodeling-NTZA5JFV-DKkAdJZd.js +1 -0
- package/dist/public/assets/flowDiagram-HODETNUW-CEE2wjdt.js +1 -0
- package/dist/public/assets/ganttDiagram-EL5Y4UJY-BHFn8vk0.js +292 -0
- package/dist/public/assets/gitGraph-4MIJSDKK-Bpvnb45u.js +1 -0
- package/dist/public/assets/{gitGraphDiagram-IHSO6WYX-CksvJvsQ.js → gitGraphDiagram-WWUBYQGX-Bw0EoxBY.js} +9 -9
- package/dist/public/assets/index-D3HS7YxF.js +2228 -0
- package/dist/public/assets/index-D6WqGtsG.js +27876 -0
- package/dist/public/assets/{index-COSSLZqt.css → index-Sv3WxZ_8.css} +1 -1
- package/dist/public/assets/info-A6RAGUB7-BvvG_zrk.js +1 -0
- package/dist/public/assets/infoDiagram-27XIBGKW-CtpdJmtL.js +2 -0
- package/dist/public/assets/{ishikawaDiagram-FXEZZL3T-CiLpASIW.js → ishikawaDiagram-5VMMS53U-KvT4DU1u.js} +2 -2
- package/dist/public/assets/journeyDiagram-3NMN7TZE-PhGRkXkq.js +139 -0
- package/dist/public/assets/{kanban-definition-HUTT4EX6-DYP_x6zD.js → kanban-definition-UXKFOSKX-Oyx5QgRq.js} +4 -4
- package/dist/public/assets/{katex-DolUETbr.js → katex-ZlcWpGUi.js} +3 -3
- package/dist/public/assets/{line-C8Sfsgt8.js → line-Dr41HTk1.js} +1 -1
- package/dist/public/assets/linear-DDBi9FwZ.js +1 -0
- package/dist/public/assets/mermaid-parser.core-DsIAxDBa.js +7 -0
- package/dist/public/assets/{mindmap-definition-LN4V7U3C-3IAd4gp3.js → mindmap-definition-YA3MSWOX-r8hvd6gP.js} +1 -1
- package/dist/public/assets/packet-AYTQ26CC-B0EpJ2Sc.js +1 -0
- package/dist/public/assets/path-B_0haWDX.js +1 -0
- package/dist/public/assets/pegDiagram-XKGWAZYB-DaNkQTQF.js +1 -0
- package/dist/public/assets/pie-WAS4IAKB-Dt2zttL9.js +1 -0
- package/dist/public/assets/pieDiagram-E7YTZNPT-x0XWEGbd.js +39 -0
- package/dist/public/assets/{quadrantDiagram-ABIIQ3AL-B3czMbdS.js → quadrantDiagram-AXDQQJYC-B9dlNtZl.js} +3 -3
- package/dist/public/assets/radar-RG4KPBEZ-DjbR4-5l.js +1 -0
- package/dist/public/assets/railroad-74A4TZTK-Bk3B5r_R.js +1 -0
- package/dist/public/assets/railroad-abnf-HS5TGJTU-DxaWfACD.js +1 -0
- package/dist/public/assets/railroad-ebnf-LZEXJU2U-DscxXZO0.js +1 -0
- package/dist/public/assets/railroad-peg-WCYAUIDC-BCWzRFzw.js +1 -0
- package/dist/public/assets/railroadDiagram-O6MQD6OU-DhwsoX6A.js +1 -0
- package/dist/public/assets/{requirementDiagram-TGXJPOKE-CmvJWy_S.js → requirementDiagram-BXWQKSXE-BiqZ6axf.js} +2 -2
- package/dist/public/assets/rolldown-runtime-B0Z9INg1.js +1 -0
- package/dist/public/assets/{rough.esm-By172zw-.js → rough.esm-Dy-Kn_BL.js} +1 -1
- package/dist/public/assets/{sankeyDiagram-HTMAVEWB-tAeOzMTn.js → sankeyDiagram-P5KCCOFB-C4rb2pZ9.js} +1 -1
- package/dist/public/assets/{sequenceDiagram-DBY2YBRQ-r0pwijM4.js → sequenceDiagram-WJ2MYXX4-DRKTnGD7.js} +9 -9
- package/dist/public/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/dist/public/assets/src-an_qrnIj.js +1 -0
- package/dist/public/assets/stateDiagram-D77RDMKH-xxdybg5Z.js +1 -0
- package/dist/public/assets/stateDiagram-v2-MP3YSRHH-CXzmJy_a.js +1 -0
- package/dist/public/assets/swimlanes-42K2YHIH-DyGI1gI6.js +1 -0
- package/dist/public/assets/swimlanesDiagram-VR7AAH4N-7hqb6VoW.js +8 -0
- package/dist/public/assets/timeline-definition-24CTP7MA-C2pIp77Q.js +120 -0
- package/dist/public/assets/treeView-Q6P3EWNA-CxRtaaB8.js +1 -0
- package/dist/public/assets/treemap-WGGIJYW6-G1IHiwTa.js +1 -0
- package/dist/public/assets/{vennDiagram-L72KCM5P-ti2qNcvo.js → vennDiagram-4TSXK5OY-CwimQRR9.js} +2 -2
- package/dist/public/assets/wardley-WFR3VGLG-DkQ3TjZX.js +1 -0
- package/dist/public/assets/{wardleyDiagram-EHGQE667-C8lblm8x.js → wardleyDiagram-VM6X3IG4-BOXpwLCv.js} +15 -15
- package/dist/public/assets/{xychartDiagram-FW5EYKEG-US-_k5eu.js → xychartDiagram-S5SC5T6Z-ChOPYsHc.js} +3 -3
- package/dist/public/index.html +19 -26
- package/dist/public/sbom.cdx.json +1 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +6 -0
- package/dist/utils.js.map +1 -1
- package/package.json +12 -13
- package/src/app.ts +22 -11
- package/src/auth/index.ts +1 -1
- package/src/auth/keycloak.ts +8 -3
- package/src/auth/mock.ts +141 -35
- package/src/auth/types.ts +8 -1
- package/src/domain/cards/index.ts +12 -102
- package/src/domain/cards/lib.ts +2 -14
- package/src/domain/cards/schema.ts +11 -3
- package/src/domain/cards/service.ts +9 -24
- package/src/domain/events/index.ts +112 -0
- package/src/domain/events/lifetime.ts +27 -0
- package/src/domain/events/project-events.ts +254 -0
- package/src/domain/events/schema.ts +21 -0
- package/src/domain/project/index.ts +31 -3
- package/src/domain/project/schema.ts +39 -6
- package/src/domain/project/service.ts +42 -17
- package/src/domain/projects/index.ts +5 -1
- package/src/domain/resources/index.ts +85 -2
- package/src/domain/resources/schema.ts +12 -2
- package/src/domain/resources/service.ts +55 -3
- package/src/domain/skills/index.ts +61 -0
- package/src/domain/skills/schema.ts +19 -0
- package/src/domain/skills/service.ts +18 -0
- package/src/export.ts +60 -42
- package/src/index.ts +3 -0
- package/src/main.ts +30 -11
- package/src/middleware/auth.ts +23 -4
- package/src/middleware/commandManager.ts +3 -0
- package/src/overlay.ts +175 -0
- package/src/project-errors.ts +33 -0
- package/src/project-registry.ts +41 -10
- package/src/utils.ts +10 -0
- package/dist/domain/cards/presence.d.ts +0 -50
- package/dist/domain/cards/presence.js +0 -93
- package/dist/domain/cards/presence.js.map +0 -1
- package/dist/public/assets/abnfDiagram-VRR7QNED-Djr4KJ5E.js +0 -1
- package/dist/public/assets/arc-DrR4k_H-.js +0 -1
- package/dist/public/assets/architecture-TIHT7OUA-XL6N-4o9.js +0 -1
- package/dist/public/assets/architectureDiagram-ZJ3FMSHR-DsMU2Uwa.js +0 -36
- package/dist/public/assets/blockDiagram-677ZJIJ3-DRBmQUQJ.js +0 -132
- package/dist/public/assets/c4Diagram-LMCZKHZV-E0rM02LD.js +0 -10
- package/dist/public/assets/channel-D5fnImJy.js +0 -1
- package/dist/public/assets/chunk-32BRIVSS-BnDY0aSV.js +0 -1
- package/dist/public/assets/chunk-52WLFC77-N5YugKaZ.js +0 -10
- package/dist/public/assets/chunk-5FCAYU7R-I3_t_Ic_.js +0 -1
- package/dist/public/assets/chunk-5HE753X5-Dxz40rzH.js +0 -1
- package/dist/public/assets/chunk-5JV3BV7I-CNwCKRLH.js +0 -1
- package/dist/public/assets/chunk-5TONJI2A-ezWQ_Z2j.js +0 -2
- package/dist/public/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
- package/dist/public/assets/chunk-BIQX33UG-DS132-R_.js +0 -1
- package/dist/public/assets/chunk-C7G6YPKG-BN-cNjoj.js +0 -1
- package/dist/public/assets/chunk-CQNSW5MT-D8Mn1I73.js +0 -1
- package/dist/public/assets/chunk-CYSBUYHQ-Dc64Dwdo.js +0 -1
- package/dist/public/assets/chunk-EMLP6XTP-aIK0V8GX.js +0 -1
- package/dist/public/assets/chunk-FWX5IMBZ-BF_P2-Do.js +0 -2
- package/dist/public/assets/chunk-ICXQ74PX-Bb8zaHde.js +0 -2
- package/dist/public/assets/chunk-JG7HCLWE-BtAXK3iQ.js +0 -2
- package/dist/public/assets/chunk-KEIR6QF5-DEvma7oi.js +0 -161
- package/dist/public/assets/chunk-MOZMSUNE-5FIqIBsY.js +0 -1
- package/dist/public/assets/chunk-OGEWGWER-BVIFy9xz.js +0 -1
- package/dist/public/assets/chunk-OSBZ3O6U-Cfd7kf1R.js +0 -1
- package/dist/public/assets/chunk-PUDLZKDR-Db3FSNR9.js +0 -156
- package/dist/public/assets/chunk-Q4XR5HBZ-BuKFRUzR.js +0 -70
- package/dist/public/assets/chunk-QBLGF6JB-DUGZXFh-.js +0 -1
- package/dist/public/assets/chunk-R7FJI6CG-D7mUd-qN.js +0 -1
- package/dist/public/assets/chunk-RYQCIY6F-C_3rESBd.js +0 -1
- package/dist/public/assets/chunk-U6XO7XAA-C3UhlwA1.js +0 -2
- package/dist/public/assets/chunk-VAUOI2AC-C-GBw18h.js +0 -1
- package/dist/public/assets/chunk-WYO6CB5R-BQoO62-B.js +0 -127
- package/dist/public/assets/chunk-XXDRQBXY-Co8n4ZHT.js +0 -1
- package/dist/public/assets/chunk-YOTPTUD7-DhCRKU1A.js +0 -1
- package/dist/public/assets/chunk-ZGVPDNZ5-hpz2Ryq5.js +0 -62
- package/dist/public/assets/chunk-ZIRB5QZD-BpeS1aZ7.js +0 -32
- package/dist/public/assets/classDiagram-OUVF2IWQ-CXdVorPK.js +0 -1
- package/dist/public/assets/classDiagram-v2-EOCWNBFH-CXdVorPK.js +0 -1
- package/dist/public/assets/colors-Cg5Vh3br.js +0 -1
- package/dist/public/assets/cose-bilkent-JH36ORCC-4GoRbloS.js +0 -1
- package/dist/public/assets/cynefin-VYW2F7L2-BPH-25fI.js +0 -1
- package/dist/public/assets/cynefinDiagram-TSTJHNR4-PEf16euv.js +0 -62
- package/dist/public/assets/cytoscape.esm-C3UvhD5t.js +0 -321
- package/dist/public/assets/dagre-Buvkdvvj.js +0 -1
- package/dist/public/assets/dagre-VKFMJZFB-DPsCMBF1.js +0 -4
- package/dist/public/assets/diagram-G47NLZAW-CzUoYkFf.js +0 -24
- package/dist/public/assets/diagram-NH7WQ7WH-MhGnE2fh.js +0 -24
- package/dist/public/assets/diagram-OA4YK3LP-CsQ9aFR_.js +0 -30
- package/dist/public/assets/diagram-WEI45ONY-Er3q49ew.js +0 -41
- package/dist/public/assets/dist-BzwexiH1.js +0 -1
- package/dist/public/assets/ebnfDiagram-CCIWWBDH-AnJAsk14.js +0 -1
- package/dist/public/assets/erDiagram-Q63AITRT-DulA16my.js +0 -85
- package/dist/public/assets/eventmodeling-45OFAUF4-CoIhTpHU.js +0 -1
- package/dist/public/assets/flowDiagram-23GEKE2U-BGMXUq18.js +0 -1
- package/dist/public/assets/ganttDiagram-NO4QXBWP-DAvCPT7_.js +0 -292
- package/dist/public/assets/gitGraph-TEB2WS4Q-BcPsZts5.js +0 -1
- package/dist/public/assets/index-H1CkT-JT.js +0 -447
- package/dist/public/assets/info-DKCQHKI2-7VGzrAVB.js +0 -1
- package/dist/public/assets/infoDiagram-FWYZ7A6U-D_by7lSL.js +0 -2
- package/dist/public/assets/journeyDiagram-5HDEW3XC-DpvFmk7c.js +0 -139
- package/dist/public/assets/linear-BWTvbP1l.js +0 -1
- package/dist/public/assets/map-BaFkSB1l.js +0 -1
- package/dist/public/assets/mermaid-parser.core--8614tkK.js +0 -4
- package/dist/public/assets/ordinal-BdBDtYTt.js +0 -1
- package/dist/public/assets/packet-7NZHBO7P-KYt33H0C.js +0 -1
- package/dist/public/assets/path-HTXttWil.js +0 -1
- package/dist/public/assets/pegDiagram-2B236MQR-BQNFxYz2.js +0 -1
- package/dist/public/assets/pie-RZYD4A2V-xKz9s9I4.js +0 -1
- package/dist/public/assets/pieDiagram-ENE6RG2P-Cy13tg8_.js +0 -39
- package/dist/public/assets/radar-I7S5WNFK-B4QTX-fX.js +0 -1
- package/dist/public/assets/railroad-3IZDKUUU-Co_9rPVC.js +0 -1
- package/dist/public/assets/railroad-abnf-AHOZXSZD-Bhy_c3ol.js +0 -1
- package/dist/public/assets/railroad-ebnf-EBAXGLYW-B9ENjG34.js +0 -1
- package/dist/public/assets/railroad-peg-LSFZ7HO6-CyJxDBsB.js +0 -1
- package/dist/public/assets/railroadDiagram-RFXS5EU6-M2j5SJF2.js +0 -1
- package/dist/public/assets/range-EplFIxc7.js +0 -1
- package/dist/public/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +0 -1
- package/dist/public/assets/src-DOh7_en_.js +0 -1
- package/dist/public/assets/stateDiagram-2N3HPSRC-a9IdkSQG.js +0 -1
- package/dist/public/assets/stateDiagram-v2-6OUMAXLB-s2blPtZ-.js +0 -1
- package/dist/public/assets/swimlanes-5IMT3BWC-5w0Cdxk6.js +0 -2
- package/dist/public/assets/swimlanesDiagram-G3AALYLV-BWa-KRed.js +0 -8
- package/dist/public/assets/time-BM2d9MsQ.js +0 -1
- package/dist/public/assets/timeline-definition-FHXFAJF6-CvXlv_4Y.js +0 -120
- package/dist/public/assets/treeView-QDETBFTQ-B9cxjfpB.js +0 -1
- package/dist/public/assets/treemap-6X3UGDF4-BplEq4hB.js +0 -1
- package/dist/public/assets/treemap-BfjIY-m6.js +0 -1
- package/dist/public/assets/wardley-OPB4EBWU-BX2ghg5e.js +0 -1
- package/src/domain/cards/presence.ts +0 -124
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2026
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU Affero General Public License version 3 as published by
|
|
6
|
+
the Free Software Foundation.
|
|
7
|
+
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
8
|
+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
9
|
+
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
10
|
+
details. You should have received a copy of the GNU Affero General Public
|
|
11
|
+
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const MAX_LIFETIME_MS = 30 * 60_000;
|
|
15
|
+
const ROTATION_MARGIN_MS = 30_000;
|
|
16
|
+
|
|
17
|
+
/** Rotation deadline in ms from now. */
|
|
18
|
+
export function computeLifetimeMs(exp: number, now = Date.now()): number {
|
|
19
|
+
const jittered = MAX_LIFETIME_MS * (0.6 + Math.random() * 0.4);
|
|
20
|
+
const remaining = exp * 1000 - now;
|
|
21
|
+
// Jitter may only shorten: the token `exp` clamp is hard.
|
|
22
|
+
const untilExpiry =
|
|
23
|
+
remaining > ROTATION_MARGIN_MS
|
|
24
|
+
? remaining - ROTATION_MARGIN_MS
|
|
25
|
+
: Math.max(remaining, 0);
|
|
26
|
+
return Math.min(untilExpiry, jittered);
|
|
27
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2026
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU Affero General Public License version 3 as published by
|
|
6
|
+
the Free Software Foundation.
|
|
7
|
+
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
8
|
+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
9
|
+
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
10
|
+
details. You should have received a copy of the GNU Affero General Public
|
|
11
|
+
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { randomUUID } from 'node:crypto';
|
|
15
|
+
import type { SSEMessage } from 'hono/streaming';
|
|
16
|
+
|
|
17
|
+
const LEASE_MS = 90_000;
|
|
18
|
+
const EXPIRY_INTERVAL_MS = 30_000;
|
|
19
|
+
const RETIRED_LEASE_MS = 15_000;
|
|
20
|
+
|
|
21
|
+
export interface PresenceEntry {
|
|
22
|
+
userId: string;
|
|
23
|
+
userName: string;
|
|
24
|
+
mode: 'viewing' | 'editing';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Capabilities {
|
|
28
|
+
canSeeIdentity: boolean;
|
|
29
|
+
canDeclareEditing: boolean;
|
|
30
|
+
canSeePresence: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The socket one connection's client is written to. A retired connection
|
|
34
|
+
* must not be written to at all; `deliver` and `closeQuietly` enforce that,
|
|
35
|
+
* since the route has already ended the stream by then. */
|
|
36
|
+
export interface Delivery {
|
|
37
|
+
send(message: SSEMessage): void;
|
|
38
|
+
close(): void;
|
|
39
|
+
terminate(message: SSEMessage): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface Connection {
|
|
43
|
+
user: { id: string; name: string };
|
|
44
|
+
capabilities: Capabilities;
|
|
45
|
+
delivery: Delivery;
|
|
46
|
+
cardKey: string | null;
|
|
47
|
+
mode: PresenceEntry['mode'];
|
|
48
|
+
sequence: number;
|
|
49
|
+
expiresAt: number;
|
|
50
|
+
retired: boolean;
|
|
51
|
+
/** Last presence list serialized to this connection, for per-recipient dedup. */
|
|
52
|
+
lastSent: string | null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The open subscriptions of one project and the presence leases they hold. */
|
|
56
|
+
export class ProjectEvents {
|
|
57
|
+
private connections = new Map<string, Connection>();
|
|
58
|
+
private expiryTimer: ReturnType<typeof setInterval>;
|
|
59
|
+
|
|
60
|
+
constructor() {
|
|
61
|
+
this.expiryTimer = setInterval(() => this.expire(), EXPIRY_INTERVAL_MS);
|
|
62
|
+
this.expiryTimer.unref();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
connect(
|
|
66
|
+
user: Connection['user'],
|
|
67
|
+
capabilities: Capabilities,
|
|
68
|
+
delivery: Delivery,
|
|
69
|
+
): string {
|
|
70
|
+
const connectionId = randomUUID();
|
|
71
|
+
this.connections.set(connectionId, {
|
|
72
|
+
user,
|
|
73
|
+
capabilities,
|
|
74
|
+
delivery,
|
|
75
|
+
cardKey: null,
|
|
76
|
+
mode: 'viewing',
|
|
77
|
+
sequence: -1,
|
|
78
|
+
expiresAt: 0,
|
|
79
|
+
retired: false,
|
|
80
|
+
lastSent: null,
|
|
81
|
+
});
|
|
82
|
+
// EventSource cannot read response headers, so the stream is the only
|
|
83
|
+
// way back with a connection handle.
|
|
84
|
+
this.deliver(connectionId, {
|
|
85
|
+
event: 'ready',
|
|
86
|
+
data: JSON.stringify({ connectionId }),
|
|
87
|
+
});
|
|
88
|
+
return connectionId;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
update(
|
|
92
|
+
connectionId: string,
|
|
93
|
+
userId: string,
|
|
94
|
+
sequence: number,
|
|
95
|
+
cardKey: string | null,
|
|
96
|
+
mode: PresenceEntry['mode'],
|
|
97
|
+
): boolean {
|
|
98
|
+
const connection = this.connections.get(connectionId);
|
|
99
|
+
if (!connection || connection.retired || connection.user.id !== userId) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
// Monotonic per connection, so a delayed request cannot undo a newer one.
|
|
103
|
+
if (sequence <= connection.sequence) return true;
|
|
104
|
+
// Coerced rather than rejected: a fake editing warning deters others from editing.
|
|
105
|
+
if (!connection.capabilities.canDeclareEditing) mode = 'viewing';
|
|
106
|
+
if (connection.cardKey === cardKey && connection.mode === mode) {
|
|
107
|
+
connection.sequence = sequence;
|
|
108
|
+
connection.expiresAt = Date.now() + LEASE_MS;
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
this.applyMove(connection, sequence, cardKey, mode, Date.now());
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private applyMove(
|
|
116
|
+
connection: Connection,
|
|
117
|
+
sequence: number,
|
|
118
|
+
cardKey: string | null,
|
|
119
|
+
mode: PresenceEntry['mode'],
|
|
120
|
+
now: number,
|
|
121
|
+
): void {
|
|
122
|
+
const previous = connection.cardKey;
|
|
123
|
+
if (previous !== cardKey) connection.lastSent = null;
|
|
124
|
+
Object.assign(connection, {
|
|
125
|
+
sequence,
|
|
126
|
+
cardKey,
|
|
127
|
+
mode,
|
|
128
|
+
expiresAt: now + LEASE_MS,
|
|
129
|
+
});
|
|
130
|
+
if (previous) this.presenceUpdated(previous);
|
|
131
|
+
if (cardKey && cardKey !== previous) this.presenceUpdated(cardKey);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
disconnect(connectionId: string): void {
|
|
135
|
+
const connection = this.connections.get(connectionId);
|
|
136
|
+
if (!connection) return;
|
|
137
|
+
this.connections.delete(connectionId);
|
|
138
|
+
this.closeQuietly(connection);
|
|
139
|
+
if (connection.cardKey) this.presenceUpdated(connection.cardKey);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// `users()` dedups by user id, so a retiring connection's entry can outlive
|
|
143
|
+
// its socket without observers seeing a change.
|
|
144
|
+
retire(connectionId: string): void {
|
|
145
|
+
const connection = this.connections.get(connectionId);
|
|
146
|
+
if (!connection) return;
|
|
147
|
+
connection.retired = true;
|
|
148
|
+
// The entry only has to bridge the client's reconnect. Left on its full
|
|
149
|
+
// lease it would go on reporting the card and mode held at retirement,
|
|
150
|
+
// and `editing` wins in users(), so it would mask the live entry the
|
|
151
|
+
// replacement declares.
|
|
152
|
+
connection.expiresAt = Math.min(
|
|
153
|
+
connection.expiresAt,
|
|
154
|
+
Date.now() + RETIRED_LEASE_MS,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
cardsUpdated(cardKeys: string[], user: Connection['user']): void {
|
|
159
|
+
for (const cardKey of new Set(cardKeys)) {
|
|
160
|
+
const withIdentity = this.encode('card.updated', {
|
|
161
|
+
cardKey,
|
|
162
|
+
userId: user.id,
|
|
163
|
+
userName: user.name,
|
|
164
|
+
});
|
|
165
|
+
const withoutIdentity = this.encode('card.updated', { cardKey });
|
|
166
|
+
for (const [connectionId, connection] of this.onCard(cardKey)) {
|
|
167
|
+
this.deliver(
|
|
168
|
+
connectionId,
|
|
169
|
+
connection.capabilities.canSeeIdentity
|
|
170
|
+
? withIdentity
|
|
171
|
+
: withoutIdentity,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
dispose(): void {
|
|
178
|
+
clearInterval(this.expiryTimer);
|
|
179
|
+
const connections = [...this.connections.values()];
|
|
180
|
+
this.connections.clear();
|
|
181
|
+
for (const connection of connections) {
|
|
182
|
+
this.closeQuietly(connection);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private users(cardKey: string): PresenceEntry[] {
|
|
187
|
+
const users = new Map<string, PresenceEntry>();
|
|
188
|
+
for (const { user, cardKey: key, mode } of this.connections.values()) {
|
|
189
|
+
if (key !== cardKey) continue;
|
|
190
|
+
if (!users.has(user.id) || mode === 'editing') {
|
|
191
|
+
users.set(user.id, { userId: user.id, userName: user.name, mode });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Sorted so identical occupancy always serializes the same way, which
|
|
195
|
+
// the per-recipient dedup in presenceUpdated() depends on.
|
|
196
|
+
return [...users.values()].sort((a, b) => a.userId.localeCompare(b.userId));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private presenceUpdated(cardKey: string): void {
|
|
200
|
+
const users = this.users(cardKey);
|
|
201
|
+
const serialized = JSON.stringify(users);
|
|
202
|
+
const message = this.encode('presence.updated', { cardKey, users });
|
|
203
|
+
for (const [connectionId, connection] of this.onCard(cardKey)) {
|
|
204
|
+
// Presence exists to keep editors from colliding, so a reader has no use
|
|
205
|
+
// for it and is not told who else is here. Readers still declare their
|
|
206
|
+
// own presence, which is why users() keeps them: editors see readers.
|
|
207
|
+
if (!connection.capabilities.canSeePresence) continue;
|
|
208
|
+
if (connection.lastSent === serialized) continue;
|
|
209
|
+
this.deliver(connectionId, message);
|
|
210
|
+
connection.lastSent = serialized;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Subscription governs delivery.
|
|
215
|
+
private onCard(cardKey: string): [string, Connection][] {
|
|
216
|
+
return [...this.connections].filter(([, c]) => c.cardKey === cardKey);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private encode(event: string, data: unknown): SSEMessage {
|
|
220
|
+
return { event, data: JSON.stringify(data) };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private deliver(connectionId: string, message: SSEMessage): void {
|
|
224
|
+
const connection = this.connections.get(connectionId);
|
|
225
|
+
// onCard() still yields a retired connection: it keeps its cardKey so the
|
|
226
|
+
// occupant list does not change while its replacement connects.
|
|
227
|
+
if (!connection || connection.retired) return;
|
|
228
|
+
try {
|
|
229
|
+
connection.delivery.send(message);
|
|
230
|
+
} catch {
|
|
231
|
+
this.disconnect(connectionId);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private expire(): void {
|
|
236
|
+
for (const [connectionId, connection] of this.connections) {
|
|
237
|
+
if (connection.expiresAt > Date.now()) continue;
|
|
238
|
+
if (!connection.retired && !connection.cardKey) continue;
|
|
239
|
+
const cardKey = connection.cardKey;
|
|
240
|
+
if (connection.retired) this.connections.delete(connectionId);
|
|
241
|
+
else connection.cardKey = null;
|
|
242
|
+
if (cardKey) this.presenceUpdated(cardKey);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private closeQuietly(connection: Connection): void {
|
|
247
|
+
if (connection.retired) return;
|
|
248
|
+
try {
|
|
249
|
+
connection.delivery.close();
|
|
250
|
+
} catch {
|
|
251
|
+
/* already closed */
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2026
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU Affero General Public License version 3 as published by
|
|
6
|
+
the Free Software Foundation.
|
|
7
|
+
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
8
|
+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
9
|
+
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
10
|
+
details. You should have received a copy of the GNU Affero General Public
|
|
11
|
+
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
|
|
16
|
+
export const presenceSchema = z.object({
|
|
17
|
+
connectionId: z.uuid(),
|
|
18
|
+
sequence: z.number().int().nonnegative(),
|
|
19
|
+
cardKey: z.string().min(1).nullable(),
|
|
20
|
+
mode: z.enum(['viewing', 'editing']),
|
|
21
|
+
});
|
|
@@ -12,13 +12,16 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { Hono } from 'hono';
|
|
15
|
+
import { disableSSG } from 'hono/ssg';
|
|
15
16
|
import { zValidator } from '../../middleware/zvalidator.js';
|
|
16
17
|
import {
|
|
17
18
|
addHubSchema,
|
|
18
19
|
cleanSchema,
|
|
19
20
|
importModuleSchema,
|
|
20
21
|
moduleParamSchema,
|
|
22
|
+
moduleVersionsQuerySchema,
|
|
21
23
|
removeHubSchema,
|
|
24
|
+
updateModuleSchema,
|
|
22
25
|
updateProjectSchema,
|
|
23
26
|
} from './schema.js';
|
|
24
27
|
import * as projectService from './service.js';
|
|
@@ -57,14 +60,39 @@ router.post(
|
|
|
57
60
|
'/modules/:module/update',
|
|
58
61
|
requireRole(UserRole.Admin),
|
|
59
62
|
zValidator('param', moduleParamSchema),
|
|
63
|
+
zValidator('json', updateModuleSchema),
|
|
60
64
|
async (c) => {
|
|
61
65
|
const commands = c.get('commands');
|
|
62
66
|
const { module } = c.req.valid('param');
|
|
63
|
-
|
|
67
|
+
const { range } = c.req.valid('json');
|
|
68
|
+
await projectService.updateModule(commands, module, range);
|
|
64
69
|
return c.json({ message: 'Module updated' });
|
|
65
70
|
},
|
|
66
71
|
);
|
|
67
72
|
|
|
73
|
+
// These reach the module sources over the network, which an export must not do.
|
|
74
|
+
router.get(
|
|
75
|
+
'/modules/versions',
|
|
76
|
+
disableSSG(),
|
|
77
|
+
requireRole(UserRole.Admin),
|
|
78
|
+
zValidator('query', moduleVersionsQuerySchema),
|
|
79
|
+
async (c) => {
|
|
80
|
+
const commands = c.get('commands');
|
|
81
|
+
const target = c.req.valid('query');
|
|
82
|
+
return c.json(await projectService.listModuleVersions(commands, target));
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
router.get(
|
|
87
|
+
'/modules/update-plan',
|
|
88
|
+
disableSSG(),
|
|
89
|
+
requireRole(UserRole.Admin),
|
|
90
|
+
async (c) => {
|
|
91
|
+
const commands = c.get('commands');
|
|
92
|
+
return c.json(await projectService.getUpdatePlan(commands));
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
|
|
68
96
|
router.get('/modules/importable', requireRole(UserRole.Reader), async (c) => {
|
|
69
97
|
const commands = c.get('commands');
|
|
70
98
|
const modules = await projectService.getImportableModules(commands);
|
|
@@ -77,8 +105,8 @@ router.post(
|
|
|
77
105
|
zValidator('json', importModuleSchema),
|
|
78
106
|
async (c) => {
|
|
79
107
|
const commands = c.get('commands');
|
|
80
|
-
const { source } = c.req.valid('json');
|
|
81
|
-
await projectService.importModule(commands, source);
|
|
108
|
+
const { source, range } = c.req.valid('json');
|
|
109
|
+
await projectService.importModule(commands, source, range);
|
|
82
110
|
return c.json({ message: 'Module imported successfully' });
|
|
83
111
|
},
|
|
84
112
|
);
|
|
@@ -51,11 +51,44 @@ export const cleanSchema = z.object({
|
|
|
51
51
|
dryRun: z.boolean(),
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
+
// Credentials in a source would be stored in cardsConfig.json and echoed in
|
|
55
|
+
// query strings, logs and errors.
|
|
56
|
+
const gitSourceSchema = z
|
|
57
|
+
.string()
|
|
58
|
+
.min(1)
|
|
59
|
+
.refine((s) => s.startsWith('https://') || s.startsWith('git@'), {
|
|
60
|
+
message: 'Source must be a git URL (https:// or git@)',
|
|
61
|
+
})
|
|
62
|
+
.refine(
|
|
63
|
+
(s) => {
|
|
64
|
+
if (!s.startsWith('https://')) return true;
|
|
65
|
+
try {
|
|
66
|
+
const { username, password } = new URL(s);
|
|
67
|
+
return username === '' && password === '';
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{ message: 'Source must be a valid URL without credentials' },
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// A blank range is a valid semver range meaning '*', which would unpin.
|
|
76
|
+
const rangeSchema = z.string().trim().min(1);
|
|
77
|
+
|
|
54
78
|
export const importModuleSchema = z.object({
|
|
55
|
-
source:
|
|
56
|
-
|
|
57
|
-
.min(1)
|
|
58
|
-
.refine((s) => s.startsWith('https://') || s.startsWith('git@'), {
|
|
59
|
-
message: 'Source must be a git URL (https:// or git@)',
|
|
60
|
-
}),
|
|
79
|
+
source: gitSourceSchema,
|
|
80
|
+
range: rangeSchema.optional(),
|
|
61
81
|
});
|
|
82
|
+
|
|
83
|
+
export const updateModuleSchema = z.object({
|
|
84
|
+
range: rangeSchema.optional(),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
export const moduleVersionsQuerySchema = z
|
|
88
|
+
.object({
|
|
89
|
+
module: z.string().min(1).optional(),
|
|
90
|
+
source: gitSourceSchema.optional(),
|
|
91
|
+
})
|
|
92
|
+
.refine((q) => (q.module === undefined) !== (q.source === undefined), {
|
|
93
|
+
message: 'Provide exactly one of module or source',
|
|
94
|
+
});
|
|
@@ -16,12 +16,14 @@ import {
|
|
|
16
16
|
type CommandManager,
|
|
17
17
|
type HubFetchFailure,
|
|
18
18
|
type ModuleSettingFromHub,
|
|
19
|
+
type ProjectModuleInfo,
|
|
20
|
+
type UpdatePlan,
|
|
19
21
|
} from '@cyberismo/data-handler';
|
|
20
22
|
|
|
21
23
|
export type { CleanResult } from '@cyberismo/data-handler';
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
// Never carries the module's source location.
|
|
26
|
+
export interface ProjectModule extends ProjectModuleInfo {
|
|
25
27
|
cardKeyPrefix: string;
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -58,19 +60,17 @@ export interface HubInfo {
|
|
|
58
60
|
|
|
59
61
|
async function toModuleInfo(
|
|
60
62
|
commands: CommandManager,
|
|
61
|
-
|
|
63
|
+
row: ProjectModuleInfo,
|
|
62
64
|
): Promise<ProjectModule> {
|
|
63
65
|
try {
|
|
64
|
-
const data = await commands.
|
|
66
|
+
const data = await commands.modulesCmd.show(row.name);
|
|
65
67
|
return {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
...row,
|
|
69
|
+
name: data.name || row.name,
|
|
70
|
+
cardKeyPrefix: data.cardKeyPrefix || row.name,
|
|
68
71
|
};
|
|
69
72
|
} catch {
|
|
70
|
-
return {
|
|
71
|
-
name: moduleName,
|
|
72
|
-
cardKeyPrefix: moduleName,
|
|
73
|
-
};
|
|
73
|
+
return { ...row, cardKeyPrefix: row.name };
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -79,9 +79,9 @@ export async function getProject(
|
|
|
79
79
|
): Promise<ProjectInfo> {
|
|
80
80
|
return commands.consistent(async () => {
|
|
81
81
|
const project = await commands.showCmd.showProject();
|
|
82
|
-
const modules = await commands.
|
|
82
|
+
const modules = await commands.modulesCmd.inventory();
|
|
83
83
|
const moduleDetails = await Promise.all(
|
|
84
|
-
modules.map((
|
|
84
|
+
modules.map((row) => toModuleInfo(commands, row)),
|
|
85
85
|
);
|
|
86
86
|
|
|
87
87
|
const gitRemoteUrl = (await commands.showCmd.showGitRemoteUrl()) ?? null;
|
|
@@ -125,12 +125,33 @@ export async function updateProject(
|
|
|
125
125
|
return getProject(commands);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
export async function updateModule(
|
|
129
|
-
|
|
128
|
+
export async function updateModule(
|
|
129
|
+
commands: CommandManager,
|
|
130
|
+
module: string,
|
|
131
|
+
range?: string,
|
|
132
|
+
) {
|
|
133
|
+
await commands.modulesCmd.update(range ? { module, range } : { module });
|
|
130
134
|
}
|
|
131
135
|
|
|
132
136
|
export async function updateAllModules(commands: CommandManager) {
|
|
133
|
-
await commands.
|
|
137
|
+
await commands.modulesCmd.update({});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export async function getUpdatePlan(
|
|
141
|
+
commands: CommandManager,
|
|
142
|
+
): Promise<UpdatePlan> {
|
|
143
|
+
return commands.modulesCmd.planUpdate({});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function listModuleVersions(
|
|
147
|
+
commands: CommandManager,
|
|
148
|
+
target: { module?: string; source?: string },
|
|
149
|
+
): Promise<string[]> {
|
|
150
|
+
return commands.modulesCmd.listVersions(
|
|
151
|
+
target.source !== undefined
|
|
152
|
+
? { source: target.source }
|
|
153
|
+
: { module: target.module! },
|
|
154
|
+
);
|
|
134
155
|
}
|
|
135
156
|
|
|
136
157
|
export async function deleteModule(commands: CommandManager, module: string) {
|
|
@@ -158,15 +179,19 @@ export async function getImportableModules(
|
|
|
158
179
|
export async function importModule(
|
|
159
180
|
commands: CommandManager,
|
|
160
181
|
source: string,
|
|
182
|
+
range?: string,
|
|
161
183
|
): Promise<void> {
|
|
162
|
-
await commands.
|
|
184
|
+
await commands.modulesCmd.install(
|
|
185
|
+
source,
|
|
186
|
+
range ? { version: range } : undefined,
|
|
187
|
+
);
|
|
163
188
|
}
|
|
164
189
|
|
|
165
190
|
export async function getHubs(commands: CommandManager): Promise<HubInfo[]> {
|
|
166
191
|
await populateHubCache(commands);
|
|
167
192
|
const hubs = await commands.showCmd.showHubDetails();
|
|
168
193
|
const importedModules = new Set(
|
|
169
|
-
(await commands.
|
|
194
|
+
(await commands.modulesCmd.list()).map((mod) => mod.name),
|
|
170
195
|
);
|
|
171
196
|
return hubs.map((hub) => ({
|
|
172
197
|
location: hub.location,
|
|
@@ -15,6 +15,7 @@ import { Hono } from 'hono';
|
|
|
15
15
|
import { zValidator } from '../../middleware/zvalidator.js';
|
|
16
16
|
import type { ProjectRegistry } from '../../project-registry.js';
|
|
17
17
|
import { requireRole, getCurrentUser } from '../../middleware/auth.js';
|
|
18
|
+
import { isProjectReadOnly } from '../../overlay.js';
|
|
18
19
|
import { UserRole } from '../../types.js';
|
|
19
20
|
import { createProjectSchema, cloneProjectSchema } from './schema.js';
|
|
20
21
|
import * as projectsService from './service.js';
|
|
@@ -39,7 +40,10 @@ export function createProjectsRouter(
|
|
|
39
40
|
*/
|
|
40
41
|
router.get('/', requireRole(UserRole.Reader), (c) => {
|
|
41
42
|
return c.json({
|
|
42
|
-
projects: registry.list()
|
|
43
|
+
projects: registry.list().map((project) => ({
|
|
44
|
+
...project,
|
|
45
|
+
readOnly: isProjectReadOnly(project.prefix),
|
|
46
|
+
})),
|
|
43
47
|
canCreateProjects: !!multiProjectRoot,
|
|
44
48
|
});
|
|
45
49
|
});
|
|
@@ -20,8 +20,9 @@ import { resourceParamsSchema } from '../../common/validationSchemas.js';
|
|
|
20
20
|
import { zValidator } from '../../middleware/zvalidator.js';
|
|
21
21
|
import {
|
|
22
22
|
validateResourceParamsSchema,
|
|
23
|
+
prefixIdentifierParamsSchema,
|
|
24
|
+
skillPreviewBodySchema,
|
|
23
25
|
updateOperationBodySchema,
|
|
24
|
-
workflowGraphParamsSchema,
|
|
25
26
|
workflowGraphQuerySchema,
|
|
26
27
|
} from './schema.js';
|
|
27
28
|
import { requireRole } from '../../middleware/auth.js';
|
|
@@ -188,7 +189,7 @@ router.get(
|
|
|
188
189
|
const commands = c.get('commands');
|
|
189
190
|
return resourceService.listWorkflowGraphParams(commands);
|
|
190
191
|
}),
|
|
191
|
-
zValidator('param',
|
|
192
|
+
zValidator('param', prefixIdentifierParamsSchema),
|
|
192
193
|
zValidator('query', workflowGraphQuerySchema),
|
|
193
194
|
async (c) => {
|
|
194
195
|
const commands = c.get('commands');
|
|
@@ -211,6 +212,88 @@ router.get(
|
|
|
211
212
|
},
|
|
212
213
|
);
|
|
213
214
|
|
|
215
|
+
/**
|
|
216
|
+
* @swagger
|
|
217
|
+
* /api/resources/{prefix}/skills/{identifier}/preview:
|
|
218
|
+
* post:
|
|
219
|
+
* summary: Render a skill's instructions for the configuration editor
|
|
220
|
+
* description: Applies Handlebars templating to the skill content and
|
|
221
|
+
* returns the rendered markdown. For template skills the
|
|
222
|
+
* logic program in query.lp is executed first and its result
|
|
223
|
+
* is used as the template context. Unlike the MCP get_skill
|
|
224
|
+
* tool, the skill does not need to be enabled by the logic
|
|
225
|
+
* programs. Pass skillContent and/or skillQuery to render
|
|
226
|
+
* unsaved editor content instead of what is stored on disk.
|
|
227
|
+
* parameters:
|
|
228
|
+
* - in: path
|
|
229
|
+
* name: prefix
|
|
230
|
+
* required: true
|
|
231
|
+
* schema:
|
|
232
|
+
* type: string
|
|
233
|
+
* - in: path
|
|
234
|
+
* name: identifier
|
|
235
|
+
* required: true
|
|
236
|
+
* schema:
|
|
237
|
+
* type: string
|
|
238
|
+
* requestBody:
|
|
239
|
+
* required: false
|
|
240
|
+
* content:
|
|
241
|
+
* application/json:
|
|
242
|
+
* schema:
|
|
243
|
+
* type: object
|
|
244
|
+
* properties:
|
|
245
|
+
* skillContent:
|
|
246
|
+
* type: string
|
|
247
|
+
* description: Unsaved skill.md content to render.
|
|
248
|
+
* skillQuery:
|
|
249
|
+
* type: string
|
|
250
|
+
* description: Unsaved query.lp content to run.
|
|
251
|
+
* cardKey:
|
|
252
|
+
* type: string
|
|
253
|
+
* description: Card context for a card-specific skill.
|
|
254
|
+
* responses:
|
|
255
|
+
* 200:
|
|
256
|
+
* description: Rendered instructions
|
|
257
|
+
* content:
|
|
258
|
+
* application/json:
|
|
259
|
+
* schema:
|
|
260
|
+
* type: object
|
|
261
|
+
* properties:
|
|
262
|
+
* instructions:
|
|
263
|
+
* type: string
|
|
264
|
+
* description: The rendered skill content as markdown.
|
|
265
|
+
* 400:
|
|
266
|
+
* description: Invalid path parameters, or the query or template failed
|
|
267
|
+
* 404:
|
|
268
|
+
* description: Skill or card not found
|
|
269
|
+
*/
|
|
270
|
+
router.post(
|
|
271
|
+
'/:prefix/skills/:identifier/preview',
|
|
272
|
+
requireRole(UserRole.Admin),
|
|
273
|
+
zValidator('param', prefixIdentifierParamsSchema),
|
|
274
|
+
zValidator('json', skillPreviewBodySchema),
|
|
275
|
+
async (c) => {
|
|
276
|
+
const commands = c.get('commands');
|
|
277
|
+
const { prefix, identifier } = c.req.valid('param');
|
|
278
|
+
const body = c.req.valid('json');
|
|
279
|
+
const skillName = `${prefix}/skills/${identifier}`;
|
|
280
|
+
try {
|
|
281
|
+
const instructions = await resourceService.previewSkill(
|
|
282
|
+
commands,
|
|
283
|
+
skillName,
|
|
284
|
+
body,
|
|
285
|
+
);
|
|
286
|
+
return c.json({ instructions });
|
|
287
|
+
} catch (error) {
|
|
288
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
289
|
+
if (message.includes('not found')) {
|
|
290
|
+
return c.json({ error: message }, 404);
|
|
291
|
+
}
|
|
292
|
+
return c.json({ error: message }, 400);
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
);
|
|
296
|
+
|
|
214
297
|
router.post(
|
|
215
298
|
'/:prefix/:type/:identifier/operation',
|
|
216
299
|
requireRole(UserRole.Admin, [UserRole.Connector]),
|