mulmoclaude 0.9.0 → 0.9.2
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 +95 -38
- package/bin/mulmoclaude.js +1 -2
- package/client/assets/JsonEditor-CPJfn76E.css +1 -0
- package/client/assets/JsonEditor-CoWGJG3Y.js +10 -0
- package/client/assets/abnfDiagram-VRR7QNED-6nNByj6v-By7GJ3xW.js +1 -0
- package/client/assets/abnfDiagram-VRR7QNED-BrQlVixL.js +1 -0
- package/client/assets/arc-BXutyUAX.js +1 -0
- package/client/assets/arc-zT0wd74V-DulGJNnb.js +1 -0
- package/client/assets/architecture-TIHT7OUA-CYMWc3UT-C7HoFzWN.js +1 -0
- package/client/assets/architecture-TIHT7OUA-aml8u-G9.js +1 -0
- package/client/assets/architectureDiagram-ZJ3FMSHR-Bgnyaj_i-CCpEsCHN.js +36 -0
- package/client/assets/architectureDiagram-ZJ3FMSHR-DJTFpPjB.js +36 -0
- package/client/assets/array-BifhSqXX.js +1 -0
- package/client/assets/array-CApNbSU9-BifhSqXX.js +1 -0
- package/client/assets/blockDiagram-677ZJIJ3-DQ35o5E4-BXS11D3J.js +132 -0
- package/client/assets/blockDiagram-677ZJIJ3-aYvCODxv.js +132 -0
- package/client/assets/c4Diagram-LMCZKHZV-Bau6RMBb.js +10 -0
- package/client/assets/c4Diagram-LMCZKHZV-ClWZeiWo-DXGnOOUf.js +10 -0
- package/client/assets/channel-D9VSi_QV.js +1 -0
- package/client/assets/channel-Di5rtkx0-C3gZcytF.js +1 -0
- package/client/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/client/assets/chunk-2Q5K7J3B-DfKpwyl9-DTxwf5E9.js +1 -0
- package/client/assets/chunk-32BRIVSS-CCt9wtYd-Cwa_IP35.js +1 -0
- package/client/assets/chunk-32BRIVSS-CxzHeys5.js +1 -0
- package/client/assets/chunk-52WLFC77-BtNjbTdw.js +10 -0
- package/client/assets/chunk-52WLFC77-FbBbR4uI-wsNC5d-f.js +10 -0
- package/client/assets/chunk-5VM5RSS4-BYnnUiN6-C4rOnu9A.js +15 -0
- package/client/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/client/assets/chunk-7BUUIJ7U-Bb538aSH.js +1 -0
- package/client/assets/chunk-7BUUIJ7U-hh8aCuGX-mGEpPzQy.js +1 -0
- package/client/assets/chunk-C7G6YPKG-BE8ehsnc.js +1 -0
- package/client/assets/chunk-C7G6YPKG-C87hlS9c-BitbJpTa.js +1 -0
- package/client/assets/{chunk-D8eiyYIV-BY16KEZc.js → chunk-D8eiyYIV-BeeEsA1B.js} +1 -1
- package/client/assets/chunk-EX3LRPZG-Bhgskzyy.js +231 -0
- package/client/assets/chunk-EX3LRPZG-BqGqMXLN-Cl2Eo1_u.js +231 -0
- package/client/assets/chunk-FWX5IMBZ-BkbSAAuW-C2r4AuAO.js +2 -0
- package/client/assets/chunk-FWX5IMBZ-D9sskZb_.js +2 -0
- package/client/assets/chunk-HOUHSVGY-CBCXk7rb.js +1 -0
- package/client/assets/chunk-HOUHSVGY-Cxu0eDlh-owg_DcY0.js +1 -0
- package/client/assets/chunk-ICXQ74PX-DTqPpDQ9.js +2 -0
- package/client/assets/chunk-ICXQ74PX-hiraF_Xj-CdaEwVin.js +2 -0
- package/client/assets/chunk-JWPE2WC7-BQ3zXr2k-DSd3Ct95.js +1 -0
- package/client/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/client/assets/chunk-KEIR6QF5-DNzq6p3w.js +161 -0
- package/client/assets/chunk-MOJQB5TN-CqxshQHA-Cn_EpDyZ.js +88 -0
- package/client/assets/chunk-MOJQB5TN-D1s_zUGz.js +88 -0
- package/client/assets/chunk-OGEWGWER-CjCr7ceX-Dfk5-70y.js +1 -0
- package/client/assets/chunk-OGEWGWER-DzfhxoQd.js +1 -0
- package/client/assets/chunk-PUDLZKDR-BZlebNhn.js +156 -0
- package/client/assets/chunk-PUDLZKDR-Dx6M-vz1-CLyVurOz.js +156 -0
- package/client/assets/chunk-Q4XR5HBZ-BcJb7mwA.js +70 -0
- package/client/assets/chunk-Q4XR5HBZ-CZd-9lTB-qPEYoRNc.js +11 -0
- package/client/assets/chunk-RYQCIY6F-B_IgMG7T.js +1 -0
- package/client/assets/chunk-RYQCIY6F-YkbemkCt-CX4fncTQ.js +1 -0
- package/client/assets/chunk-V7JOEXUC-Buce04o6.js +206 -0
- package/client/assets/chunk-V7JOEXUC-rI0xlC_O-C607vJXy.js +206 -0
- package/client/assets/chunk-VAUOI2AC-CxhkzXN0.js +1 -0
- package/client/assets/chunk-VAUOI2AC-DrcykVNK-CSROJWdE.js +1 -0
- package/client/assets/chunk-VR4S4FIN-D5afNrsS.js +1 -0
- package/client/assets/chunk-VR4S4FIN-O6iF8Yvf-CLIwrgqU.js +1 -0
- package/client/assets/chunk-WYO6CB5R-B83L_z6I-Cs8mg9If.js +127 -0
- package/client/assets/chunk-WYO6CB5R-BXTIdTMw.js +125 -0
- package/client/assets/chunk-XXDRQBXY-DFXH_lWn-B5HjCp2c.js +1 -0
- package/client/assets/chunk-XXDRQBXY-DXMD6ofA.js +1 -0
- package/client/assets/chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js +1 -0
- package/client/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/client/assets/chunk-ZGVPDNZ5-BpFv9JSP-CHYySMjV.js +62 -0
- package/client/assets/chunk-ZGVPDNZ5-CYusI0J_.js +62 -0
- package/client/assets/chunk-ZIRB5QZD-Biy6ZNFG-D1maAiXb.js +32 -0
- package/client/assets/chunk-ZIRB5QZD-C6fEPe3t.js +32 -0
- package/client/assets/classDiagram-OUVF2IWQ-BgAZMSbT-BgxEE1D3.js +1 -0
- package/client/assets/classDiagram-OUVF2IWQ-CBD3Eidl.js +1 -0
- package/client/assets/classDiagram-v2-EOCWNBFH-CBD3Eidl.js +1 -0
- package/client/assets/classDiagram-v2-EOCWNBFH-DxHTyui1-BgxEE1D3.js +1 -0
- package/client/assets/cose-bilkent-JH36ORCC-CuSL2tA8-aMIqISfj.js +1 -0
- package/client/assets/cose-bilkent-JH36ORCC-WAJDBXv1.js +1 -0
- package/client/assets/cynefin-VYW2F7L2-DH2qkJKw.js +1 -0
- package/client/assets/cynefin-VYW2F7L2-DqA3n9nY-BNWhIK_n.js +1 -0
- package/client/assets/cynefinDiagram-TSTJHNR4-CUvPV9MV.js +62 -0
- package/client/assets/cynefinDiagram-TSTJHNR4-x0-0sQ15-Cq1gFcFu.js +62 -0
- package/client/assets/cytoscape.esm-CzdwbRaj-Djp6vQyU.js +321 -0
- package/client/assets/cytoscape.esm-Djp6vQyU.js +321 -0
- package/client/assets/dagre-CXRCoUWR.js +1 -0
- package/client/assets/dagre-VKFMJZFB-CQdfl-bx-CzZZuace.js +4 -0
- package/client/assets/dagre-VKFMJZFB-C_9IS7mB.js +4 -0
- package/client/assets/dagre-wczQIDso-CfevN9RO.js +1 -0
- package/client/assets/defaultLocale-C8Fc0cco.js +1 -0
- package/client/assets/defaultLocale-DUNguUWs-BbDo_yJX.js +1 -0
- package/client/assets/diagram-FQU43EPY-BOSB6VUb-isPdQ8WX.js +3 -0
- package/client/assets/diagram-FQU43EPY-BP8N00-b.js +3 -0
- package/client/assets/diagram-G47NLZAW-DLXrcXsN-BIqj7RKy.js +24 -0
- package/client/assets/diagram-G47NLZAW-ulE1JlWG.js +24 -0
- package/client/assets/diagram-NH7WQ7WH-BMQp1rkF-D6-fOq_v.js +24 -0
- package/client/assets/diagram-NH7WQ7WH-D_IXrL3i.js +24 -0
- package/client/assets/diagram-OA4YK3LP-C-pC6Eyu.js +30 -0
- package/client/assets/diagram-OA4YK3LP-D1wQ0vUj-BNi6QLCK.js +30 -0
- package/client/assets/diagram-WEI45ONY-Di9m35i-.js +41 -0
- package/client/assets/diagram-WEI45ONY-RR0DpF8R-BS3nrb3w.js +41 -0
- package/client/assets/dist-BQBs5pjy-BmtZ7Oc2.js +1 -0
- package/client/assets/dist-CQ3HaWOk.js +1 -0
- package/client/assets/ebnfDiagram-CCIWWBDH-Cbwnim2x.js +1 -0
- package/client/assets/ebnfDiagram-CCIWWBDH-M123uVJ8-9-dq55nQ.js +1 -0
- package/client/assets/erDiagram-Q63AITRT-BWx_-PXG-CWGfG4z5.js +85 -0
- package/client/assets/erDiagram-Q63AITRT-CMbtO3Sm.js +85 -0
- package/client/assets/eventmodeling-45OFAUF4--32SIpkL.js +1 -0
- package/client/assets/eventmodeling-45OFAUF4-_BVSjAXf-DQdL0Icr.js +1 -0
- package/client/assets/flowDiagram-23GEKE2U-BeOc_anm-CuVzKmmU.js +1 -0
- package/client/assets/flowDiagram-23GEKE2U-Dofa_qxG.js +1 -0
- package/client/assets/ganttDiagram-NO4QXBWP-BOoJ1eTw-TWoNmSvq.js +292 -0
- package/client/assets/ganttDiagram-NO4QXBWP-BgoAVKuc.js +292 -0
- package/client/assets/gitGraph-TEB2WS4Q-CH12KLTN-Bz4frAhV.js +1 -0
- package/client/assets/gitGraph-TEB2WS4Q-DIMvNvqt.js +1 -0
- package/client/assets/gitGraphDiagram-IHSO6WYX-B2CJhk_G-ClOKkjxw.js +106 -0
- package/client/assets/gitGraphDiagram-IHSO6WYX-Dmb6KnPz.js +106 -0
- package/client/assets/graphlib-B8gBHxth.js +1 -0
- package/client/assets/graphlib-hY-1btwe-DQjxxcnr.js +1 -0
- package/client/assets/{html2canvas-CDGcmOD3-CKJ6vKPo.js → html2canvas-CDGcmOD3-DRL9pFVl.js} +2 -2
- package/client/assets/{index-9lmYSaus.js → index-Dc0R-HW5.js} +129 -190
- package/client/assets/index-Dxo1Zdd-.css +2 -0
- package/client/assets/{index.es-DqtpmBm8-DFXjJgCa.js → index.es-DqtpmBm8-EQk3NgR8.js} +1 -1
- package/client/assets/info-DKCQHKI2-Cbw3mbiK-GsDZz9IO.js +1 -0
- package/client/assets/info-DKCQHKI2-ViCqobGo.js +1 -0
- package/client/assets/infoDiagram-FWYZ7A6U-HKV7LIG-.js +2 -0
- package/client/assets/infoDiagram-FWYZ7A6U-Mp1X3pBP-ATJv87Ur.js +2 -0
- package/client/assets/init-D6jRqBbL.js +1 -0
- package/client/assets/init-DEsX3bhM-D6jRqBbL.js +1 -0
- package/client/assets/ishikawaDiagram-FXEZZL3T-BNG7tkJu-CGCeOIlJ.js +70 -0
- package/client/assets/ishikawaDiagram-FXEZZL3T-CVDUj46f.js +70 -0
- package/client/assets/journeyDiagram-5HDEW3XC-BA-ESGLP.js +139 -0
- package/client/assets/journeyDiagram-5HDEW3XC-Dbp_hY9X-Bx-IoCe9.js +139 -0
- package/client/assets/kanban-definition-HUTT4EX6-Bqaet01L.js +89 -0
- package/client/assets/kanban-definition-HUTT4EX6-DSTc5u3q-C0JGckt1.js +89 -0
- package/client/assets/katex-CddkPoXu.js +257 -0
- package/client/assets/katex-M0IxphGf-CddkPoXu.js +257 -0
- package/client/assets/lib-4Tgx7rAy.js +114 -0
- package/client/assets/line-B1wBwzrY-f5wxpqoF.js +1 -0
- package/client/assets/line-CkyHfW7d.js +1 -0
- package/client/assets/linear-B9fuEF4c.js +1 -0
- package/client/assets/linear-lrGinF5_-CqNBync7.js +1 -0
- package/client/assets/map-CgrwEyH7-DWQFomlZ.js +1 -0
- package/client/assets/map-DsCK-0Cs.js +1 -0
- package/client/assets/marked.esm-CIU5FDdN.js +64 -0
- package/client/assets/marp-CSq0PPfK.js +3448 -0
- package/client/assets/material-symbols-outlined-DdRFxZLh.woff2 +0 -0
- package/client/assets/mermaid-parser.core-DC7NPJ_M-Ca6XzwfM.js +166 -0
- package/client/assets/mermaid-parser.core-SAwSf4_o.js +7 -0
- package/client/assets/mermaid.core-BBEQfkdJ.js +11 -0
- package/client/assets/mermaid.core-DZM3Ha-E-BxOo-RYG.js +11 -0
- package/client/assets/mindmap-definition-LN4V7U3C-B4BN0efL.js +96 -0
- package/client/assets/mindmap-definition-LN4V7U3C-DYtgcMsY-DynyLTNv.js +96 -0
- package/client/assets/ordinal-B9_Llu10-CU85fhaB.js +1 -0
- package/client/assets/ordinal-CopWnP7w.js +1 -0
- package/client/assets/packet-7NZHBO7P-CY4XRk9N.js +1 -0
- package/client/assets/packet-7NZHBO7P-lwb58iYx--vqSg65J.js +1 -0
- package/client/assets/path-BWPyau1x.js +1 -0
- package/client/assets/path-COivZ1Gw-C5riojK2.js +1 -0
- package/client/assets/pegDiagram-2B236MQR-BOKErjUu.js +1 -0
- package/client/assets/pegDiagram-2B236MQR-C43eIpKM-Bi_5Sxjt.js +1 -0
- package/client/assets/pie-RZYD4A2V-6BHB7bAx.js +1 -0
- package/client/assets/pie-RZYD4A2V-B1UWb4Gu-Dmn2hLWH.js +1 -0
- package/client/assets/pieDiagram-ENE6RG2P-BkTqgJyR-Bcdm19kR.js +39 -0
- package/client/assets/pieDiagram-ENE6RG2P-DtNjvz-U.js +39 -0
- package/client/assets/preload-helper-CZgWQFsJ.js +1 -0
- package/client/assets/purify.es-DY32g7DN.js +3 -0
- package/client/assets/quadrantDiagram-ABIIQ3AL-BQmAJL2v.js +7 -0
- package/client/assets/quadrantDiagram-ABIIQ3AL-Bm1Zjm45-B3orqx_f.js +7 -0
- package/client/assets/radar-I7S5WNFK-7CKcb_l--CqKdnR0v.js +1 -0
- package/client/assets/radar-I7S5WNFK-R7Jhjfdt.js +1 -0
- package/client/assets/railroad-3IZDKUUU-BepSPyHI.js +1 -0
- package/client/assets/railroad-3IZDKUUU-gCySKdnW-CKU26HTP.js +1 -0
- package/client/assets/railroad-abnf-AHOZXSZD-BlsHsp3c.js +1 -0
- package/client/assets/railroad-abnf-AHOZXSZD-BophH4r--CGkGHq54.js +1 -0
- package/client/assets/railroad-ebnf-EBAXGLYW-AZNjl_Zu-B7bn_PF3.js +1 -0
- package/client/assets/railroad-ebnf-EBAXGLYW-B4fMcGEH.js +1 -0
- package/client/assets/railroad-peg-LSFZ7HO6-B1ZrjJIu.js +1 -0
- package/client/assets/railroad-peg-LSFZ7HO6-BSiEEyeb-CP_B-N5g.js +1 -0
- package/client/assets/railroadDiagram-RFXS5EU6-8cz0Iby3.js +1 -0
- package/client/assets/railroadDiagram-RFXS5EU6-BkfbdeAs-Di6jH5I6.js +1 -0
- package/client/assets/requirementDiagram-TGXJPOKE-CrGTTjYg-DmIC7Shp.js +84 -0
- package/client/assets/requirementDiagram-TGXJPOKE-Dlqoe9TY.js +84 -0
- package/client/assets/rough.esm-CSKSodPl.js +1 -0
- package/client/assets/rough.esm-wVmwlXu7-CSKSodPl.js +1 -0
- package/client/assets/runtime-protocol-vue-WG3JLsjz.js +1 -0
- package/client/assets/runtime-vue-lHsY5zMj.js +1 -0
- package/client/assets/sankeyDiagram-HTMAVEWB-BkTKgu8Q.js +40 -0
- package/client/assets/sankeyDiagram-HTMAVEWB-rWXPf03Z-CjV9pCSJ.js +40 -0
- package/client/assets/{schemas-D_RbFtuQ.js → schemas-DGvl73AE.js} +3 -3
- package/client/assets/sequenceDiagram-DBY2YBRQ-BpuXUcFy.js +162 -0
- package/client/assets/sequenceDiagram-DBY2YBRQ-nkJYWO2m-BqS_sTCH.js +162 -0
- package/client/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +1 -0
- package/client/assets/sizeCapture-X5ZJPWSS-F5SadAuT-VHIb4_NU.js +1 -0
- package/client/assets/src-Brzfja-q-DNc4Or4z.js +1 -0
- package/client/assets/src-Cqa3Jy1j.js +1 -0
- package/client/assets/stateDiagram-2N3HPSRC-CdlGXuo7.js +1 -0
- package/client/assets/stateDiagram-2N3HPSRC-T4-clK8b-DQRiB4va.js +1 -0
- package/client/assets/stateDiagram-v2-6OUMAXLB-BN8Jr3ZM.js +1 -0
- package/client/assets/stateDiagram-v2-6OUMAXLB-DIp7nhRd-DbrOhUn_.js +1 -0
- package/client/assets/swimlanes-5IMT3BWC-HmQNEntu-CNBgZdur.js +2 -0
- package/client/assets/swimlanes-5IMT3BWC-iEJg5gki.js +2 -0
- package/client/assets/swimlanesDiagram-G3AALYLV-BeoZhwg7-DpqgeNRA.js +8 -0
- package/client/assets/swimlanesDiagram-G3AALYLV-o-sS6aEP.js +8 -0
- package/client/assets/timeline-definition-FHXFAJF6-Bjh-7QmL.js +120 -0
- package/client/assets/timeline-definition-FHXFAJF6-CNc9jSTP-BKNSymgY.js +120 -0
- package/client/assets/treeView-QDETBFTQ-D3cqRl6k.js +1 -0
- package/client/assets/treeView-QDETBFTQ-nIQcG1h9-O_ri9HxD.js +1 -0
- package/client/assets/treemap-6X3UGDF4-BnsvC8yL-Cfsty1e3.js +1 -0
- package/client/assets/treemap-6X3UGDF4-DGkZcvDG.js +1 -0
- package/client/assets/v4-B29ayslu.js +1 -0
- package/client/assets/vennDiagram-L72KCM5P-Cz3dbe7b.js +34 -0
- package/client/assets/vennDiagram-L72KCM5P-Dr3pTJ_0-C38xwcVo.js +34 -0
- package/client/assets/vue-C6d2VveO.js +1 -0
- package/client/assets/vue-i18n-CL-ejuNu.js +3 -0
- package/client/assets/vue.runtime.esm-bundler-BeoTfMNc.js +4 -0
- package/client/assets/wardley-OPB4EBWU-8Odxkx6V-CCDIVuBs.js +1 -0
- package/client/assets/wardley-OPB4EBWU-CZsCDvKk.js +1 -0
- package/client/assets/wardleyDiagram-EHGQE667-DSFc7ZZa-mlWXRiWx.js +78 -0
- package/client/assets/wardleyDiagram-EHGQE667-DiyoyO_X.js +78 -0
- package/client/assets/xychartDiagram-FW5EYKEG-BP0Nn4Pp-DRRiStGZ.js +7 -0
- package/client/assets/xychartDiagram-FW5EYKEG-DhY1_UGM.js +7 -0
- package/client/index.html +16 -12
- package/package.json +17 -19
- package/server/agent/backend/types.ts +1 -1
- package/server/agent/backgroundSessions.ts +43 -0
- package/server/agent/config.ts +3 -2
- package/server/agent/mcp-tools/manageCollection.ts +4 -4
- package/server/agent/sandboxMounts.ts +1 -1
- package/server/agent/stream.ts +1 -3
- package/server/api/auth/viewToken.ts +1 -1
- package/server/api/bridge/sessionRole.ts +47 -0
- package/server/api/routes/agent.ts +79 -2
- package/server/api/routes/collections.ts +60 -7
- package/server/api/routes/collectionsRegistry.ts +113 -0
- package/server/api/routes/dashboard.ts +47 -0
- package/server/api/routes/feeds.ts +2 -2
- package/server/api/routes/files.ts +1 -2
- package/server/api/routes/pdf.ts +48 -9
- package/server/api/routes/plugins.ts +2 -2
- package/server/api/routes/remoteHost.ts +55 -0
- package/server/api/routes/sessions.ts +20 -11
- package/server/api/routes/share.ts +105 -0
- package/server/api/routes/transcribe.ts +1 -1
- package/server/api/routes/wiki/history.ts +2 -2
- package/server/api/routes/wiki.ts +29 -177
- package/server/api/sandboxStatus.ts +1 -1
- package/server/build/dispatcher.mjs +25 -14928
- package/server/events/collection-change.ts +4 -4
- package/server/events/file-change.ts +2 -2
- package/server/events/scheduler-adapter.ts +3 -3
- package/server/events/task-manager/index.ts +3 -3
- package/server/index.ts +53 -17
- package/server/notifier/engine.ts +3 -3
- package/server/notifier/types.ts +2 -2
- package/server/plugins/html-builtin.ts +15 -1
- package/server/prompts/system/system.md +4 -0
- package/server/remoteHost/auth.ts +29 -0
- package/server/remoteHost/commandChannel.ts +56 -0
- package/server/remoteHost/firebase.ts +16 -0
- package/server/remoteHost/handlers/collectionPage.ts +29 -0
- package/server/remoteHost/handlers/getCollection.ts +32 -0
- package/server/remoteHost/handlers/getFeed.ts +35 -0
- package/server/remoteHost/handlers/index.ts +19 -0
- package/server/remoteHost/handlers/listCollections.ts +29 -0
- package/server/remoteHost/handlers/listFeeds.ts +37 -0
- package/server/remoteHost/handlers/listShortcuts.ts +24 -0
- package/server/remoteHost/handlers/startChat.ts +78 -0
- package/server/remoteHost/hostRunner.ts +122 -0
- package/server/remoteHost/index.ts +117 -0
- package/server/system/config.ts +1 -1
- package/server/system/credentials.ts +2 -3
- package/server/system/docker.ts +9 -8
- package/server/system/env.ts +8 -0
- package/server/system/whisper/index.ts +4 -4
- package/server/utils/claudeConfigPath.ts +57 -0
- package/server/utils/files/dashboard-io.ts +87 -0
- package/server/utils/files/session-io.ts +11 -0
- package/server/utils/markdown/frontmatter.ts +8 -4
- package/server/utils/regex.ts +4 -3
- package/server/utils/share/packHtml.ts +108 -0
- package/server/utils/share/rewriteAssets.ts +178 -0
- package/server/workspace/chat-index/paths.ts +8 -2
- package/server/workspace/chat-index/summarizer.ts +23 -15
- package/server/workspace/collections/configure.ts +5 -3
- package/server/workspace/collections/index.ts +8 -6
- package/server/workspace/collections/notifications.ts +3 -3
- package/server/workspace/collections/types.ts +2 -2
- package/server/workspace/collections/watcher.ts +2 -2
- package/server/workspace/feeds/configure.ts +24 -0
- package/server/workspace/hooks/handlers/skillBridge.ts +2 -2
- package/server/workspace/hooks/handlers/wikiSnapshot.ts +1 -1
- package/server/workspace/paths.ts +8 -1
- package/server/workspace/skills/catalog.ts +1 -3
- package/server/workspace/skills/external/catalog.ts +1 -3
- package/server/workspace/skills/paths.ts +4 -3
- package/server/workspace/skills/writer.ts +1 -4
- package/server/workspace/skills-preset.ts +3 -3
- package/server/workspace/wiki-pages/io.ts +2 -2
- package/server/workspace/workspace.ts +2 -2
- package/src/App.vue +41 -18
- package/src/components/ChatInput.vue +1 -1
- package/src/components/DashboardView.vue +372 -0
- package/src/components/FileContentRenderer.vue +32 -7
- package/src/components/JsonEditor.vue +1 -1
- package/src/components/PluginLauncher.vue +122 -79
- package/src/components/RemoteHostControl.vue +159 -0
- package/src/components/SessionCountBadges.vue +32 -0
- package/src/components/SessionHeaderControls.vue +1 -8
- package/src/components/SessionHistoryPanel.vue +2 -0
- package/src/components/SessionHistoryToggleButton.vue +1 -15
- package/src/components/SidebarHeader.vue +2 -0
- package/src/components/collectionTypes.ts +2 -2
- package/src/composables/accountingHost.ts +24 -0
- package/src/composables/collections/uiHost.ts +17 -2
- package/src/composables/useDashboard.ts +191 -0
- package/src/composables/useMarkdownZip.ts +51 -0
- package/src/composables/useNotifications.ts +1 -4
- package/src/composables/useSharePack.ts +60 -0
- package/src/composables/useTranslatedQueries.ts +15 -95
- package/src/composables/useTranslatedStrings.ts +68 -0
- package/src/composables/useVoiceInput.ts +3 -3
- package/src/config/apiRoutes.ts +57 -9
- package/src/config/createFilePolicy.ts +1 -1
- package/src/config/firebase.ts +12 -0
- package/src/config/firebaseConfig.ts +18 -0
- package/src/config/historyFilters.ts +7 -0
- package/src/config/pubsubChannels.ts +7 -27
- package/src/config/roles.ts +12 -10
- package/src/config/workspacePaths.ts +10 -0
- package/src/index.css +1 -1
- package/src/lang/de.ts +39 -220
- package/src/lang/en.ts +39 -191
- package/src/lang/es.ts +39 -218
- package/src/lang/fr.ts +39 -220
- package/src/lang/ja.ts +39 -216
- package/src/lang/ko.ts +39 -216
- package/src/lang/pt-BR.ts +39 -217
- package/src/lang/zh.ts +39 -215
- package/src/main.ts +3 -0
- package/src/plugins/accounting/definition.ts +12 -8
- package/src/plugins/accounting/index.ts +7 -4
- package/src/plugins/accounting/meta.ts +23 -60
- package/src/plugins/manageSkills/View.vue +9 -1
- package/src/plugins/photoLocations/View.vue +2 -2
- package/src/plugins/presentCollection/definition.ts +2 -2
- package/src/plugins/presentCollection/plugin.ts +2 -2
- package/src/plugins/presentCollection/types.ts +2 -2
- package/src/plugins/presentHtml/definition.ts +1 -1
- package/src/plugins/presentMulmoScript/View.vue +2 -2
- package/src/plugins/presentMulmoScript/helpers.ts +1 -1
- package/src/plugins/presentSVG/View.vue +2 -2
- package/src/plugins/scheduler/TasksTab.vue +1 -2
- package/src/plugins/skill/View.vue +7 -3
- package/src/plugins/spreadsheet/View.vue +3 -3
- package/src/plugins/spreadsheet/engine/functions/logical.ts +8 -1
- package/src/plugins/textResponse/View.vue +43 -16
- package/src/plugins/wiki/View.vue +29 -2
- package/src/plugins/wiki/components/WikiGraphView.vue +1 -1
- package/src/plugins/wiki/components/WikiPageBody.vue +4 -1
- package/src/plugins/wiki/helpers.ts +13 -81
- package/src/plugins/wiki/index.ts +1 -1
- package/src/router/guards.ts +1 -1
- package/src/router/index.ts +12 -0
- package/src/router/pageRoutes.ts +2 -0
- package/src/types/dashboard.ts +40 -0
- package/src/types/session.ts +3 -0
- package/src/utils/blobDownload.ts +19 -0
- package/src/utils/collections/presentSeed.ts +1 -1
- package/src/utils/html/customViewSrcdoc.ts +31 -3
- package/src/utils/html/previewCsp.ts +1 -1
- package/src/utils/id.ts +1 -1
- package/src/utils/image/htmlSrcAttrs.ts +1 -1
- package/src/utils/markdown/frontmatter.ts +12 -7
- package/src/utils/markdown/highlight.ts +22 -0
- package/src/utils/markdown/marpCustomSize.ts +2 -2
- package/src/utils/markdown/mermaidExtension.ts +56 -0
- package/src/utils/markdown/mermaidRender.ts +147 -0
- package/src/utils/markdown/setup.ts +17 -4
- package/src/utils/markdown/useMermaid.ts +33 -0
- package/src/utils/session/longRunning.ts +33 -0
- package/src/utils/session/mergeSessions.ts +1 -1
- package/client/assets/JsonEditor-Di5xGeZY.css +0 -1
- package/client/assets/JsonEditor-o5--tPQH.js +0 -10
- package/client/assets/index-tOu5ArRZ.css +0 -2
- package/client/assets/lib-D6Xy0IFc.js +0 -114
- package/client/assets/marp-D6GXA-EB.js +0 -3452
- package/client/assets/material-symbols-outlined-DtIK7AQn.woff2 +0 -0
- package/client/assets/runtime-protocol-vue-pU0Mw7Zm.js +0 -1
- package/client/assets/runtime-vue-fFYhnNg3.js +0 -1
- package/client/assets/vue-UDIWDtr8.js +0 -1
- package/client/assets/vue-i18n-CQbxVmNs.js +0 -3
- package/client/assets/vue.runtime.esm-bundler-BTyIdNAI.js +0 -4
- package/server/accounting/accountNormalize.ts +0 -32
- package/server/accounting/defaultAccounts.ts +0 -87
- package/server/accounting/eventPublisher.ts +0 -52
- package/server/accounting/journal.ts +0 -252
- package/server/accounting/openingBalances.ts +0 -114
- package/server/accounting/report.ts +0 -237
- package/server/accounting/service.ts +0 -718
- package/server/accounting/snapshotCache.ts +0 -334
- package/server/accounting/timeSeries.ts +0 -265
- package/server/accounting/types.ts +0 -148
- package/server/api/routes/accounting.ts +0 -373
- package/server/api/routes/wiki/frontmatter.ts +0 -34
- package/server/api/routes/wiki/pageIndex.ts +0 -53
- package/server/system/logs/aaa +0 -737
- package/server/system/logs/bb +0 -446
- package/server/utils/files/accounting-io.ts +0 -294
- package/server/workspace/feeds/engine.ts +0 -143
- package/server/workspace/feeds/fetch/httpClient.ts +0 -127
- package/server/workspace/feeds/fetch/rssParser.ts +0 -117
- package/server/workspace/feeds/index.ts +0 -8
- package/server/workspace/feeds/ingestTypes.ts +0 -62
- package/server/workspace/feeds/pathResolver.ts +0 -74
- package/server/workspace/feeds/paths.ts +0 -30
- package/server/workspace/feeds/projectItem.ts +0 -92
- package/server/workspace/feeds/registry.ts +0 -43
- package/server/workspace/feeds/retrievers/httpJson.ts +0 -19
- package/server/workspace/feeds/retrievers/index.ts +0 -27
- package/server/workspace/feeds/retrievers/registerAll.ts +0 -5
- package/server/workspace/feeds/retrievers/rss.ts +0 -24
- package/server/workspace/feeds/state.ts +0 -55
- package/src/composables/useAccountingChannel.ts +0 -58
- package/src/lib/wiki-page/graph.ts +0 -108
- package/src/lib/wiki-page/index-parse.ts +0 -221
- package/src/lib/wiki-page/link.ts +0 -62
- package/src/lib/wiki-page/lint.ts +0 -105
- package/src/lib/wiki-page/paths.ts +0 -35
- package/src/lib/wiki-page/slug.ts +0 -54
- package/src/plugins/accounting/Preview.vue +0 -103
- package/src/plugins/accounting/View.vue +0 -633
- package/src/plugins/accounting/actions.ts +0 -34
- package/src/plugins/accounting/api.ts +0 -301
- package/src/plugins/accounting/components/AccountEditor.vue +0 -250
- package/src/plugins/accounting/components/AccountRow.vue +0 -50
- package/src/plugins/accounting/components/AccountsList.vue +0 -102
- package/src/plugins/accounting/components/AccountsModal.vue +0 -301
- package/src/plugins/accounting/components/BalanceSheet.vue +0 -186
- package/src/plugins/accounting/components/BookSettings.vue +0 -284
- package/src/plugins/accounting/components/BookSwitcher.vue +0 -78
- package/src/plugins/accounting/components/DateRangePicker.vue +0 -140
- package/src/plugins/accounting/components/JournalEntryForm.vue +0 -505
- package/src/plugins/accounting/components/JournalList.vue +0 -554
- package/src/plugins/accounting/components/Ledger.vue +0 -206
- package/src/plugins/accounting/components/NewBookForm.vue +0 -211
- package/src/plugins/accounting/components/OpeningBalancesForm.vue +0 -272
- package/src/plugins/accounting/components/ProfitLoss.vue +0 -160
- package/src/plugins/accounting/components/accountDraft.ts +0 -13
- package/src/plugins/accounting/components/accountNumbering.ts +0 -103
- package/src/plugins/accounting/components/accountValidation.ts +0 -75
- package/src/plugins/accounting/components/useLatestRequest.ts +0 -44
- package/src/plugins/accounting/countries.ts +0 -158
- package/src/plugins/accounting/currencies.ts +0 -77
- package/src/plugins/accounting/dates.ts +0 -51
- package/src/plugins/accounting/fiscalYear.ts +0 -136
- package/src/plugins/accounting/timeSeriesEnums.ts +0 -16
- package/src/plugins/wiki/route.ts +0 -137
- /package/client/assets/{_plugin-vue_export-helper-B67ILkmu.js → _plugin-vue_export-helper-BDNMzG2s.js} +0 -0
- /package/client/assets/{purify.es-B27wDFIb-51iYcXuK.js → purify.es-B27wDFIb-Bu4Grnl0.js} +0 -0
- /package/client/assets/{typeof-DBp4T-Ny-z2wCIsir.js → typeof-DBp4T-Ny-B5XbjTb1.js} +0 -0
|
@@ -34,6 +34,7 @@ interface SessionMeta {
|
|
|
34
34
|
hasUnread?: boolean;
|
|
35
35
|
isBookmarked?: boolean;
|
|
36
36
|
origin?: SessionOrigin;
|
|
37
|
+
userQueryCount?: number;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
async function readSessionMeta(__chatDir: string, sessionId: string): Promise<SessionMeta | null> {
|
|
@@ -77,6 +78,9 @@ export interface SessionSummary {
|
|
|
77
78
|
// User-toggled bookmark — surfaced in the history panel as a
|
|
78
79
|
// dedicated filter chip and a green role-icon tint.
|
|
79
80
|
isBookmarked?: boolean;
|
|
81
|
+
// Number of user turns sent to this session. Lets the history panel
|
|
82
|
+
// tell a one-shot (1) apart from a long-running conversation.
|
|
83
|
+
userQueryCount?: number;
|
|
80
84
|
// Live state from the in-memory session store. Absent when the
|
|
81
85
|
// session has no active entry in the store (i.e. idle / historical).
|
|
82
86
|
//
|
|
@@ -130,6 +134,20 @@ interface SessionRow {
|
|
|
130
134
|
changeMs: number;
|
|
131
135
|
}
|
|
132
136
|
|
|
137
|
+
// Fold the live in-memory session state onto a summary. Extracted so
|
|
138
|
+
// `buildSessionSummary` stays under the cognitive-complexity threshold.
|
|
139
|
+
function applyLiveState(summary: SessionSummary, live: NonNullable<ReturnType<typeof getSession>>): void {
|
|
140
|
+
// Background generations (image/audio/movie) keep the session "busy"
|
|
141
|
+
// even when the agent turn has ended, so the sidebar indicator stays
|
|
142
|
+
// lit across view navigation.
|
|
143
|
+
summary.isRunning = live.isRunning || Object.keys(live.pendingGenerations).length > 0;
|
|
144
|
+
// Narrow predicate — must stay byte-identical to the DELETE 409 gate
|
|
145
|
+
// (`getSession(sessionId)?.isRunning`) so a caller polling this can
|
|
146
|
+
// trust "false ⇒ DELETE will be accepted".
|
|
147
|
+
summary.liveIsRunning = live.isRunning;
|
|
148
|
+
summary.statusMessage = live.statusMessage;
|
|
149
|
+
}
|
|
150
|
+
|
|
133
151
|
// Build a SessionSummary from the gathered inputs. Conditional spreads
|
|
134
152
|
// honour the server tsconfig's `exactOptionalPropertyTypes`; that's
|
|
135
153
|
// why each optional field is set with `if (… !== undefined)` rather
|
|
@@ -153,19 +171,10 @@ function buildSessionSummary(
|
|
|
153
171
|
};
|
|
154
172
|
if (meta.origin) summary.origin = meta.origin;
|
|
155
173
|
if (meta.isBookmarked) summary.isBookmarked = true;
|
|
174
|
+
if (typeof meta.userQueryCount === "number") summary.userQueryCount = meta.userQueryCount;
|
|
156
175
|
if (indexEntry?.summary !== undefined) summary.summary = indexEntry.summary;
|
|
157
176
|
if (indexEntry?.keywords !== undefined) summary.keywords = indexEntry.keywords;
|
|
158
|
-
if (live)
|
|
159
|
-
// Background generations (image/audio/movie) keep the session
|
|
160
|
-
// "busy" even when the agent turn has ended, so the sidebar
|
|
161
|
-
// indicator stays lit across view navigation.
|
|
162
|
-
summary.isRunning = live.isRunning || Object.keys(live.pendingGenerations).length > 0;
|
|
163
|
-
// Narrow predicate — must stay byte-identical to the DELETE
|
|
164
|
-
// 409 gate (`getSession(sessionId)?.isRunning`) so a caller
|
|
165
|
-
// polling this can trust "false ⇒ DELETE will be accepted".
|
|
166
|
-
summary.liveIsRunning = live.isRunning;
|
|
167
|
-
summary.statusMessage = live.statusMessage;
|
|
168
|
-
}
|
|
177
|
+
if (live) applyLiveState(summary, live);
|
|
169
178
|
return summary;
|
|
170
179
|
}
|
|
171
180
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Router, Request, Response } from "express";
|
|
2
|
+
import { API_ROUTES } from "../../../src/config/apiRoutes.js";
|
|
3
|
+
import { WORKSPACE_DIRS } from "../../workspace/paths.js";
|
|
4
|
+
import { packHtmlZip, zipBundle, safeZipName } from "../../utils/share/packHtml.js";
|
|
5
|
+
import { renderMarkdownHtml } from "./pdf.js";
|
|
6
|
+
import { isHtmlPath } from "../../utils/files/html-store.js";
|
|
7
|
+
import { badRequest, serverError } from "../../utils/httpError.js";
|
|
8
|
+
import { errorMessage } from "../../utils/errors.js";
|
|
9
|
+
import { log } from "../../system/logger/index.js";
|
|
10
|
+
|
|
11
|
+
const router = Router();
|
|
12
|
+
|
|
13
|
+
interface PackBody {
|
|
14
|
+
path?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Route-boundary guard: only a canonical `.html` under artifacts/html is
|
|
18
|
+
// packable. Delegates to the repo's shared HTML path policy (`isHtmlPath`
|
|
19
|
+
// = prefix + `.html` ext + canonical form + no `..` + no traversal
|
|
20
|
+
// segment) so malformed input is a deterministic 400, never a 500 from
|
|
21
|
+
// deeper code.
|
|
22
|
+
export function isPackablePath(value: unknown): value is string {
|
|
23
|
+
return typeof value === "string" && isHtmlPath(value);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// POST /api/share/pack — bundle an HTML artifact and its referenced
|
|
27
|
+
// local assets into a single self-contained zip (index.html + assets/),
|
|
28
|
+
// returned as a download. Paths are rewritten to be relative so the
|
|
29
|
+
// unzipped folder opens directly over file://.
|
|
30
|
+
router.post(API_ROUTES.share.pack, async (req: Request<object, unknown, PackBody>, res: Response) => {
|
|
31
|
+
const htmlPath = req.body?.path;
|
|
32
|
+
if (!isPackablePath(htmlPath)) {
|
|
33
|
+
badRequest(res, `path must be a canonical ${WORKSPACE_DIRS.htmls}/*.html file`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const { filename, zip } = await packHtmlZip(htmlPath);
|
|
38
|
+
log.info("share", "pack: ok", { path: htmlPath, bytes: zip.length });
|
|
39
|
+
res.setHeader("Content-Type", "application/zip");
|
|
40
|
+
res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
|
|
41
|
+
res.send(zip);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
// Log the detail server-side; return a generic message so an internal
|
|
44
|
+
// path / stack never reaches the client.
|
|
45
|
+
log.error("share", "pack: threw", { path: htmlPath, error: errorMessage(err) });
|
|
46
|
+
serverError(res, "failed to pack HTML bundle");
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
interface PackMarkdownBody {
|
|
51
|
+
markdown?: string;
|
|
52
|
+
filename?: string;
|
|
53
|
+
baseDir?: string;
|
|
54
|
+
stripFrontmatter?: boolean;
|
|
55
|
+
marp?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// A shared markdown zip is opened directly by the recipient (unlike the
|
|
59
|
+
// PDF path, which puppeteer renders once), so neutralize any script the
|
|
60
|
+
// source markdown carries — `marked` passes raw HTML through. A strict
|
|
61
|
+
// CSP blocks `<script>` / inline handlers / `javascript:` and plugins
|
|
62
|
+
// without stripping content or touching images/styles.
|
|
63
|
+
const SHARE_MARKDOWN_CSP = "script-src 'none'; object-src 'none'; base-uri 'none'";
|
|
64
|
+
|
|
65
|
+
export function withScriptCsp(html: string): string {
|
|
66
|
+
return html.replace(/<head>/i, `<head><meta http-equiv="Content-Security-Policy" content="${SHARE_MARKDOWN_CSP}">`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function buildMarkdownZip(body: PackMarkdownBody): Promise<{ filename: string; zip: Buffer }> {
|
|
70
|
+
const markdown = typeof body.markdown === "string" ? body.markdown : "";
|
|
71
|
+
const baseDir = typeof body.baseDir === "string" ? body.baseDir : undefined;
|
|
72
|
+
const html = withScriptCsp(await renderMarkdownHtml({ markdown, baseDir, stripFrontmatter: body.stripFrontmatter === true, marp: body.marp === true }));
|
|
73
|
+
const zip = await zipBundle([{ bundlePath: "index.html", bytes: Buffer.from(html, "utf-8") }]);
|
|
74
|
+
const filename = typeof body.filename === "string" ? body.filename : "document";
|
|
75
|
+
return { filename: safeZipName(filename.replace(/\.(md|markdown|html?|pdf)$/i, "")), zip };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// POST /api/share/pack-markdown — render markdown (or a wiki page) to a
|
|
79
|
+
// self-contained HTML (CSS inlined, images embedded as data URIs, scripts
|
|
80
|
+
// neutralized) and return it zipped as index.html. Shares the render path
|
|
81
|
+
// with the PDF route (`renderMarkdownHtml`). `baseDir` resolves relative
|
|
82
|
+
// image refs; traversal is rejected downstream by the shared image resolver.
|
|
83
|
+
router.post(API_ROUTES.share.packMarkdown, async (req: Request<object, unknown, PackMarkdownBody>, res: Response) => {
|
|
84
|
+
const { body } = req;
|
|
85
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
86
|
+
badRequest(res, "request body must be a JSON object");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (typeof body.markdown !== "string" || !body.markdown) {
|
|
90
|
+
badRequest(res, "markdown is required");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const { filename, zip } = await buildMarkdownZip(body);
|
|
95
|
+
log.info("share", "pack-markdown: ok", { bytes: zip.length, marp: body.marp === true });
|
|
96
|
+
res.setHeader("Content-Type", "application/zip");
|
|
97
|
+
res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
|
|
98
|
+
res.send(zip);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
log.error("share", "pack-markdown: threw", { error: errorMessage(err) });
|
|
101
|
+
serverError(res, "failed to pack markdown bundle");
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
export default router;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// POST /api/transcribe/model/download start the model download (opt-in)
|
|
6
6
|
//
|
|
7
7
|
// All transcription happens on the machine running this server; no
|
|
8
|
-
// audio leaves it. See plans/feat-voice-input.md.
|
|
8
|
+
// audio leaves it. See plans/done/feat-voice-input.md.
|
|
9
9
|
|
|
10
10
|
import { Router, Request, Response } from "express";
|
|
11
11
|
import { API_ROUTES } from "../../../src/config/apiRoutes.js";
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
//
|
|
9
9
|
// Path safety: both `:slug` and `:stamp` are validated *before*
|
|
10
10
|
// they are joined with the workspace root. The slug check is the
|
|
11
|
-
// shared `isSafeSlug` from
|
|
11
|
+
// shared `isSafeSlug` from `@mulmoclaude/core/wiki`; the stamp
|
|
12
12
|
// check is the `FILENAME_RE` shape exposed via `isSafeStamp`.
|
|
13
13
|
|
|
14
14
|
import { Router, type Request, type Response } from "express";
|
|
@@ -25,7 +25,7 @@ import { workspacePath } from "../../../workspace/workspace.js";
|
|
|
25
25
|
import { pushToolResult } from "../../../events/session-store/index.js";
|
|
26
26
|
import { log } from "../../../system/logger/index.js";
|
|
27
27
|
import { errorMessage } from "../../../utils/errors.js";
|
|
28
|
-
import { isSafeSlug } from "
|
|
28
|
+
import { isSafeSlug } from "@mulmoclaude/core/wiki";
|
|
29
29
|
|
|
30
30
|
const router = Router();
|
|
31
31
|
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { Router, Request, Response } from "express";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import {
|
|
4
|
-
import { readTextSafeSync, readTextSafe } from "../../utils/files/safe.js";
|
|
3
|
+
import { workspacePath } from "../../workspace/paths.js";
|
|
5
4
|
import { writeWikiPage } from "../../workspace/wiki-pages/io.js";
|
|
6
|
-
import { getPageIndex } from "./wiki/pageIndex.js";
|
|
7
|
-
import { parseFrontmatterTags } from "./wiki/frontmatter.js";
|
|
8
5
|
import { badRequest, notFound } from "../../utils/httpError.js";
|
|
9
6
|
import { getOptionalStringQuery } from "../../utils/request.js";
|
|
10
7
|
import { API_ROUTES } from "../../../src/config/apiRoutes.js";
|
|
@@ -15,115 +12,28 @@ import { previewSnippet } from "../../utils/logPreview.js";
|
|
|
15
12
|
// different name avoids the no-shadow clash without renaming the
|
|
16
13
|
// long-standing local.
|
|
17
14
|
import { errorMessage as formatError } from "../../utils/errors.js";
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
import { type WikiPageEntry,
|
|
25
|
-
import {
|
|
26
|
-
import { buildWikiGraph, type WikiGraph } from "../../../src/lib/wiki-page/graph.js";
|
|
15
|
+
// Pure wiki-text helpers (slug rules, lint formatting, types) live in
|
|
16
|
+
// `@mulmoclaude/core/wiki`; the workspace-injected fs read-engine
|
|
17
|
+
// (resolve / index / page / log / graph / lint) lives in
|
|
18
|
+
// `@mulmoclaude/core/wiki/server`, shared with MulmoTerminal. This
|
|
19
|
+
// route is the thin HTTP shell that shapes their output into the
|
|
20
|
+
// canvas response envelope.
|
|
21
|
+
import { wikiSlugify, formatLintReport, type WikiPageEntry, type WikiGraph } from "@mulmoclaude/core/wiki";
|
|
22
|
+
import { resolvePagePath, readWikiIndex, readWikiPage, readWikiLog, loadWikiGraph, collectLintIssues } from "@mulmoclaude/core/wiki/server";
|
|
27
23
|
|
|
28
24
|
const router = Router();
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
// Re-exports kept here for legacy callers that import wiki-text
|
|
27
|
+
// helpers via this route module (e.g. tests in
|
|
28
|
+
// `test/routes/test_wikiHelpers.ts`).
|
|
29
|
+
export { wikiSlugify, buildTableColumnMap, extractHashTags, extractSlugFromBulletHref, parseIndexEntries, parseTagsCell } from "@mulmoclaude/core/wiki";
|
|
30
|
+
export type { WikiPageEntry } from "@mulmoclaude/core/wiki";
|
|
31
|
+
export { findBrokenLinksInPage, findMissingFiles, findOrphanPages, findTagDrift, formatLintReport } from "@mulmoclaude/core/wiki";
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// Wiki-text helpers (slugify, index parsing, lint rules) now live
|
|
39
|
-
// under `src/lib/wiki-page/` as pure modules — see imports above.
|
|
40
|
-
// Re-exports kept here for legacy callers that import via this
|
|
41
|
-
// route module (e.g. tests in `test/routes/test_wikiHelpers.ts`).
|
|
42
|
-
export { wikiSlugify } from "../../../src/lib/wiki-page/slug.js";
|
|
43
|
-
export type { WikiPageEntry } from "../../../src/lib/wiki-page/index-parse.js";
|
|
44
|
-
export { buildTableColumnMap, extractHashTags, extractSlugFromBulletHref, parseIndexEntries, parseTagsCell } from "../../../src/lib/wiki-page/index-parse.js";
|
|
45
|
-
export { findBrokenLinksInPage, findMissingFiles, findOrphanPages, findTagDrift, formatLintReport } from "../../../src/lib/wiki-page/lint.js";
|
|
46
|
-
|
|
47
|
-
// Resolve a page name to an absolute `.md` path using the in-memory
|
|
48
|
-
// page index (see ./wiki/pageIndex.ts). Index is kept fresh via
|
|
49
|
-
// pagesDir mtime, so zero readdir cost on cache hit.
|
|
50
|
-
//
|
|
51
|
-
// `pageName` may carry the `[[target|display]]` form on legacy
|
|
52
|
-
// codepaths (e.g. an old chat-history entry that pre-dates the
|
|
53
|
-
// renderer fix). `parseWikiLink` strips the display half so the
|
|
54
|
-
// lookup uses just the target — same parser the renderer + lint
|
|
55
|
-
// agree on, which is the whole point of the #1297 refactor.
|
|
56
|
-
// Below this length the fuzzy `includes` step skips altogether.
|
|
57
|
-
// CJK / emoji-only / very-short page names get slugified down to a
|
|
58
|
-
// short noise tail (e.g. "日本語タイトル-chromium-{nonce}" →
|
|
59
|
-
// "-chromium-{nonce}"), and that noise will partial-match almost
|
|
60
|
-
// any page that happens to share the suffix. Skipping fuzzy for
|
|
61
|
-
// these slugs avoids silent miss-resolves; the index.md title-match
|
|
62
|
-
// fallback below still handles the legitimate non-ASCII case (#1194).
|
|
63
|
-
const MIN_FUZZY_SLUG_LEN = 6;
|
|
64
|
-
|
|
65
|
-
async function resolvePagePath(pageName: string): Promise<string | null> {
|
|
66
|
-
const dir = pagesDir();
|
|
67
|
-
const { slugs } = await getPageIndex(dir);
|
|
68
|
-
if (slugs.size === 0) return null;
|
|
69
|
-
|
|
70
|
-
const { target } = parseWikiLink(pageName);
|
|
71
|
-
const slug = wikiSlugify(target);
|
|
72
|
-
|
|
73
|
-
if (slug.length > 0) {
|
|
74
|
-
const exact = slugs.get(slug);
|
|
75
|
-
if (exact) return path.join(dir, exact);
|
|
76
|
-
|
|
77
|
-
const fuzzy = pickFuzzyMatch(slug, slugs);
|
|
78
|
-
if (fuzzy) return path.join(dir, fuzzy);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Non-ASCII page names (e.g. Japanese [[wiki links]]) produce empty
|
|
82
|
-
// slugs after slugification. Fall back to matching by title in the
|
|
83
|
-
// wiki index so the link resolves to its page file.
|
|
84
|
-
const indexContent = readFileOrEmpty(indexFile());
|
|
85
|
-
const entries = parseIndexEntries(indexContent);
|
|
86
|
-
const titleMatch = entries.find((entry) => entry.title === target);
|
|
87
|
-
if (titleMatch) {
|
|
88
|
-
const file = slugs.get(titleMatch.slug);
|
|
89
|
-
if (file) return path.join(dir, file);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Walk every indexed slug looking for an `includes`-style match.
|
|
96
|
-
// Returns the single best candidate, or null when the slug is too
|
|
97
|
-
// short to be meaningful OR multiple candidates tie at the top score
|
|
98
|
-
// (ambiguous — leave the resolution to the caller's title-match
|
|
99
|
-
// fallback rather than silently picking iteration-order-first).
|
|
100
|
-
//
|
|
101
|
-
// Score = min(slug.length, key.length) / max(slug.length, key.length).
|
|
102
|
-
// A perfect match where both strings are identical scores 1.0 (but
|
|
103
|
-
// that path is taken by the exact `slugs.get` above, never reaches
|
|
104
|
-
// here). Otherwise the highest-scoring partial match wins, and the
|
|
105
|
-
// score is decoupled from Map iteration order (= filesystem readdir
|
|
106
|
-
// order) so the resolver is deterministic across hosts.
|
|
107
|
-
export function pickFuzzyMatch(slug: string, slugs: ReadonlyMap<string, string>): string | null {
|
|
108
|
-
if (slug.length < MIN_FUZZY_SLUG_LEN) return null;
|
|
109
|
-
let bestFile: string | null = null;
|
|
110
|
-
let bestScore = 0;
|
|
111
|
-
let bestIsTied = false;
|
|
112
|
-
for (const [key, file] of slugs) {
|
|
113
|
-
if (!slug.includes(key) && !key.includes(slug)) continue;
|
|
114
|
-
const shorter = Math.min(slug.length, key.length);
|
|
115
|
-
const longer = Math.max(slug.length, key.length);
|
|
116
|
-
const score = shorter / longer;
|
|
117
|
-
if (score > bestScore) {
|
|
118
|
-
bestScore = score;
|
|
119
|
-
bestFile = file;
|
|
120
|
-
bestIsTied = false;
|
|
121
|
-
} else if (score === bestScore) {
|
|
122
|
-
bestIsTied = true;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return bestIsTied ? null : bestFile;
|
|
126
|
-
}
|
|
33
|
+
// `resolvePagePath` / `pickFuzzyMatch` now live in
|
|
34
|
+
// `@mulmoclaude/core/wiki/server` (shared with MulmoTerminal). This
|
|
35
|
+
// route resolves pages through the imported `readWikiPage` /
|
|
36
|
+
// `resolvePagePath`.
|
|
127
37
|
|
|
128
38
|
router.get(API_ROUTES.wiki.base, async (req: Request, res: Response<WikiResponse | ErrorResponse>) => {
|
|
129
39
|
const slug = getOptionalStringQuery(req, "slug");
|
|
@@ -144,8 +54,7 @@ router.get(API_ROUTES.wiki.base, async (req: Request, res: Response<WikiResponse
|
|
|
144
54
|
return;
|
|
145
55
|
}
|
|
146
56
|
log.info("wiki", "GET index: start");
|
|
147
|
-
const content =
|
|
148
|
-
const pageEntries = parseIndexEntries(content);
|
|
57
|
+
const { content, entries: pageEntries } = readWikiIndex(workspacePath);
|
|
149
58
|
log.info("wiki", "GET index: ok", { pages: pageEntries.length, bytes: content.length });
|
|
150
59
|
res.json({
|
|
151
60
|
data: { action: "index", title: "Wiki Index", content, pageEntries },
|
|
@@ -187,8 +96,7 @@ interface ErrorResponse {
|
|
|
187
96
|
}
|
|
188
97
|
|
|
189
98
|
function buildIndexResponse(action: string): WikiResponse {
|
|
190
|
-
const content =
|
|
191
|
-
const pageEntries = parseIndexEntries(content);
|
|
99
|
+
const { content, entries: pageEntries } = readWikiIndex(workspacePath);
|
|
192
100
|
return {
|
|
193
101
|
data: { action, title: "Wiki Index", content, pageEntries },
|
|
194
102
|
message: content ? `Wiki index — ${pageEntries.length} page(s)` : "Wiki index is empty.",
|
|
@@ -261,13 +169,12 @@ export function toPageResponse(args: { action: string; pageName: string; filePat
|
|
|
261
169
|
}
|
|
262
170
|
|
|
263
171
|
async function buildPageResponse(action: string, pageName: string): Promise<WikiResponse> {
|
|
264
|
-
const filePath = await
|
|
265
|
-
const content = filePath ? readFileOrEmpty(filePath) : "";
|
|
172
|
+
const { filePath, content } = await readWikiPage(workspacePath, pageName);
|
|
266
173
|
return toPageResponse({ action, pageName, filePath, content });
|
|
267
174
|
}
|
|
268
175
|
|
|
269
176
|
function buildLogResponse(action: string): WikiResponse {
|
|
270
|
-
const content =
|
|
177
|
+
const content = readWikiLog(workspacePath);
|
|
271
178
|
return {
|
|
272
179
|
data: { action, title: "Activity Log", content },
|
|
273
180
|
message: content ? "Wiki activity log" : "Activity log is empty.",
|
|
@@ -277,67 +184,12 @@ function buildLogResponse(action: string): WikiResponse {
|
|
|
277
184
|
};
|
|
278
185
|
}
|
|
279
186
|
|
|
280
|
-
//
|
|
281
|
-
//
|
|
282
|
-
//
|
|
283
|
-
// route below is the thin filesystem shell around them.
|
|
284
|
-
|
|
285
|
-
async function collectLintIssues(): Promise<string[]> {
|
|
286
|
-
const dir = pagesDir();
|
|
287
|
-
const { slugs } = await getPageIndex(dir);
|
|
288
|
-
if (slugs.size === 0) {
|
|
289
|
-
return ["- Wiki `pages/` directory does not exist yet. Start ingesting sources."];
|
|
290
|
-
}
|
|
291
|
-
const indexContent = readFileOrEmpty(indexFile());
|
|
292
|
-
const pageEntries = parseIndexEntries(indexContent);
|
|
293
|
-
const indexedSlugs = new Set(pageEntries.map((entry) => entry.slug));
|
|
294
|
-
const pageFiles = [...slugs.values()];
|
|
295
|
-
const fileSlugs = new Set(slugs.keys());
|
|
296
|
-
|
|
297
|
-
const issues: string[] = [];
|
|
298
|
-
issues.push(...findOrphanPages(fileSlugs, indexedSlugs));
|
|
299
|
-
issues.push(...findMissingFiles(pageEntries, fileSlugs));
|
|
300
|
-
// Parallel read: N small markdown files, ~50 KB each. Bounded by
|
|
301
|
-
// the number of wiki pages, not by CPU.
|
|
302
|
-
const contents = await Promise.all(
|
|
303
|
-
pageFiles.map(async (fileName) => {
|
|
304
|
-
const content = await readTextSafe(path.join(dir, fileName));
|
|
305
|
-
return content ?? "";
|
|
306
|
-
}),
|
|
307
|
-
);
|
|
308
|
-
const frontmatterTagsBySlug = new Map<string, string[]>();
|
|
309
|
-
for (let i = 0; i < pageFiles.length; i++) {
|
|
310
|
-
issues.push(...findBrokenLinksInPage(pageFiles[i], contents[i], fileSlugs));
|
|
311
|
-
// Lowercase the map key so a `MyPage.md` filename still matches
|
|
312
|
-
// an `entry.slug` of `mypage` produced by `wikiSlugify` on the
|
|
313
|
-
// wiki-link parser path. `findTagDrift` lowercases the lookup
|
|
314
|
-
// side to match.
|
|
315
|
-
const slug = pageFiles[i].replace(/\.md$/i, "").toLowerCase();
|
|
316
|
-
frontmatterTagsBySlug.set(slug, parseFrontmatterTags(contents[i]));
|
|
317
|
-
}
|
|
318
|
-
issues.push(...findTagDrift(pageEntries, frontmatterTagsBySlug));
|
|
319
|
-
return issues;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Read every page + the index and build the page→page link graph
|
|
323
|
-
// (#wiki-backlinks-graph). Same parallel-read shape as
|
|
324
|
-
// `collectLintIssues` — the link resolution is shared with lint via
|
|
325
|
-
// the pure `buildWikiGraph` helper. No cache: the `graph` action is
|
|
326
|
-
// explicit (user opens the Graph tab / a page's backlinks), and a
|
|
327
|
-
// page-content edit doesn't advance the pagesDir mtime that the page
|
|
328
|
-
// index caches on, so a content-keyed cache would go stale silently.
|
|
329
|
-
async function loadWikiGraph(): Promise<WikiGraph> {
|
|
330
|
-
const dir = pagesDir();
|
|
331
|
-
const { slugs } = await getPageIndex(dir);
|
|
332
|
-
const fileEntries = [...slugs.entries()];
|
|
333
|
-
const contents = await Promise.all(fileEntries.map(async ([, fileName]) => (await readTextSafe(path.join(dir, fileName))) ?? ""));
|
|
334
|
-
const pages = fileEntries.map(([slug], i) => ({ slug, content: contents[i] }));
|
|
335
|
-
const indexEntries = parseIndexEntries(readFileOrEmpty(indexFile()));
|
|
336
|
-
return buildWikiGraph(pages, indexEntries);
|
|
337
|
-
}
|
|
187
|
+
// `collectLintIssues` and `loadWikiGraph` (the read-all-pages loops
|
|
188
|
+
// over the pure lint rules / `buildWikiGraph`) now live in
|
|
189
|
+
// `@mulmoclaude/core/wiki/server`, shared with MulmoTerminal.
|
|
338
190
|
|
|
339
191
|
async function buildGraphResponse(action: string): Promise<WikiResponse> {
|
|
340
|
-
const graph = await loadWikiGraph();
|
|
192
|
+
const graph = await loadWikiGraph(workspacePath);
|
|
341
193
|
log.info("wiki", "POST graph: ok", { nodes: graph.nodes.length, edges: graph.edges.length });
|
|
342
194
|
return {
|
|
343
195
|
data: { action, title: "Wiki Graph", content: "", graph },
|
|
@@ -353,7 +205,7 @@ async function buildGraphResponse(action: string): Promise<WikiResponse> {
|
|
|
353
205
|
type SaveOutcome = { ok: true; absPath: string } | { ok: false; reason: "not-found" };
|
|
354
206
|
|
|
355
207
|
async function saveExistingPage(pageName: string, content: string): Promise<SaveOutcome> {
|
|
356
|
-
const absPath = await resolvePagePath(pageName);
|
|
208
|
+
const absPath = await resolvePagePath(workspacePath, pageName);
|
|
357
209
|
if (!absPath) return { ok: false, reason: "not-found" };
|
|
358
210
|
// Funnel through the wiki-page write helper. Atomic write is
|
|
359
211
|
// guaranteed inside; the helper also routes the (old, new) pair
|
|
@@ -400,7 +252,7 @@ async function handleSaveAction(
|
|
|
400
252
|
}
|
|
401
253
|
|
|
402
254
|
async function buildLintReportResponse(action: string): Promise<WikiResponse> {
|
|
403
|
-
const issues = await collectLintIssues();
|
|
255
|
+
const issues = await collectLintIssues(workspacePath);
|
|
404
256
|
const report = formatLintReport(issues);
|
|
405
257
|
const healthy = issues.length === 0;
|
|
406
258
|
return {
|
|
@@ -32,7 +32,7 @@ export interface BuildSandboxStatusParams {
|
|
|
32
32
|
sandboxEnabled: boolean;
|
|
33
33
|
sshAgentForward: boolean;
|
|
34
34
|
configMountNames: readonly string[];
|
|
35
|
-
sshAuthSock?: string
|
|
35
|
+
sshAuthSock?: string;
|
|
36
36
|
home?: string;
|
|
37
37
|
/** Injected for tests; defaults to `process.platform`. */
|
|
38
38
|
platform?: typeof process.platform;
|