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
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
// Domain types for the accounting plugin (opt-in, custom-Role only).
|
|
2
|
-
//
|
|
3
|
-
// Source-of-truth files on disk:
|
|
4
|
-
// data/accounting/config.json ← AccountingConfig
|
|
5
|
-
// data/accounting/books/<id>/accounts.json ← Account[]
|
|
6
|
-
// data/accounting/books/<id>/journal/YYYY-MM.jsonl ← JournalEntry per line
|
|
7
|
-
// data/accounting/books/<id>/snapshots/YYYY-MM.json ← MonthSnapshot (cache)
|
|
8
|
-
//
|
|
9
|
-
// Snapshots are cache only — journal is the single source of truth.
|
|
10
|
-
|
|
11
|
-
import type { SupportedCountryCode } from "../../src/plugins/accounting/countries.js";
|
|
12
|
-
import type { FiscalYearEnd } from "../../src/plugins/accounting/fiscalYear.js";
|
|
13
|
-
|
|
14
|
-
export const ACCOUNT_TYPES = ["asset", "liability", "equity", "income", "expense"] as const;
|
|
15
|
-
export type AccountType = (typeof ACCOUNT_TYPES)[number];
|
|
16
|
-
|
|
17
|
-
/** B/S accounts (assets / liabilities / equity). Used by opening
|
|
18
|
-
* balance validation: opening entries reference balance-sheet
|
|
19
|
-
* accounts only. */
|
|
20
|
-
export const BALANCE_SHEET_ACCOUNT_TYPES: readonly AccountType[] = ["asset", "liability", "equity"];
|
|
21
|
-
|
|
22
|
-
export interface Account {
|
|
23
|
-
/** Stable identifier the journal lines reference. Typically a
|
|
24
|
-
* numeric string ("1000" / "2000" …) but free-form is allowed
|
|
25
|
-
* so the user can adopt their existing numbering. */
|
|
26
|
-
code: string;
|
|
27
|
-
name: string;
|
|
28
|
-
type: AccountType;
|
|
29
|
-
/** Optional free-form note (tax bucket, parent group, …). Not
|
|
30
|
-
* interpreted by the engine — passes through verbatim. */
|
|
31
|
-
note?: string;
|
|
32
|
-
/** Soft-delete flag. When `false`, the account is hidden from
|
|
33
|
-
* entry/ledger dropdowns but stays visible in Manage Accounts
|
|
34
|
-
* and historical entries — accounting integrity requires that
|
|
35
|
-
* a code referenced by a journal line never disappears. Omitted
|
|
36
|
-
* (treated as active) by default to keep the JSON files clean
|
|
37
|
-
* for books created before this field existed. */
|
|
38
|
-
active?: boolean;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface BookSummary {
|
|
42
|
-
id: string;
|
|
43
|
-
name: string;
|
|
44
|
-
/** ISO 4217 (e.g. "USD" / "JPY"). Single-currency per book — no
|
|
45
|
-
* cross-book aggregation. */
|
|
46
|
-
currency: string;
|
|
47
|
-
/** ISO 3166-1 alpha-2 country code (e.g. "US" / "JP" / "GB").
|
|
48
|
-
* Identifies the tax jurisdiction the book is kept under so the
|
|
49
|
-
* Accounting role can give country-aware advice (Japanese T-number
|
|
50
|
-
* under インボイス制度, EU VAT ID, GSTIN, ABN, etc.). Constrained
|
|
51
|
-
* to `SupportedCountryCode` (the curated list shared with the UI
|
|
52
|
-
* dropdown and the LLM tool's JSON-schema enum) so a typo from any
|
|
53
|
-
* ingress path is rejected at the service layer rather than silently
|
|
54
|
-
* persisted. Optional for backward compatibility with books created
|
|
55
|
-
* before the field was introduced; the UI prompts existing books
|
|
56
|
-
* to set it. */
|
|
57
|
-
country?: SupportedCountryCode;
|
|
58
|
-
/** Which calendar-quarter end is the book's fiscal year end:
|
|
59
|
-
* Q1 → March 31, Q2 → June 30, Q3 → September 30, Q4 → December 31.
|
|
60
|
-
* Drives the UI's "current quarter / current year" date-range
|
|
61
|
-
* shortcuts. Optional in the persisted shape for backward
|
|
62
|
-
* compatibility with books written before this field existed —
|
|
63
|
-
* read-side code treats the absent value as Q4 via
|
|
64
|
-
* `resolveFiscalYearEnd`. New books require it at the create
|
|
65
|
-
* boundary; the default is Q4. */
|
|
66
|
-
fiscalYearEnd?: FiscalYearEnd;
|
|
67
|
-
createdAt: string;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export interface AccountingConfig {
|
|
71
|
-
books: BookSummary[];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export type JournalEntryKind = "normal" | "opening" | "void" | "void-marker";
|
|
75
|
-
|
|
76
|
-
export interface JournalLine {
|
|
77
|
-
accountCode: string;
|
|
78
|
-
/** Use exactly one of debit / credit per line, both as positive
|
|
79
|
-
* numbers. The engine treats them as separate fields rather than
|
|
80
|
-
* a single signed amount so the input matches a standard
|
|
81
|
-
* bookkeeping form. */
|
|
82
|
-
debit?: number;
|
|
83
|
-
credit?: number;
|
|
84
|
-
/** Per-line memo (the entry-level memo lives on JournalEntry). */
|
|
85
|
-
memo?: string;
|
|
86
|
-
/** Counterparty's tax-authority-issued registration ID for this
|
|
87
|
-
* line — Japanese 適格請求書発行事業者登録番号 (T-number), EU
|
|
88
|
-
* VAT identification number, UK VAT registration number, India
|
|
89
|
-
* GSTIN, Australia ABN, etc. Required for input-tax-credit
|
|
90
|
-
* eligibility under the Japanese インボイス制度 (effective
|
|
91
|
-
* 2023-10-01) and equivalent regimes elsewhere. Free-form string;
|
|
92
|
-
* format validation belongs upstream (per-jurisdiction). */
|
|
93
|
-
taxRegistrationId?: string;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export interface JournalEntry {
|
|
97
|
-
/** Globally unique within a book — ULID-style; ordering by id
|
|
98
|
-
* reproduces creation order. */
|
|
99
|
-
id: string;
|
|
100
|
-
/** Calendar date the entry is booked for (YYYY-MM-DD). The month
|
|
101
|
-
* part decides which `journal/YYYY-MM.jsonl` file the entry lives
|
|
102
|
-
* in; entries can be for any past / future date. */
|
|
103
|
-
date: string;
|
|
104
|
-
kind: JournalEntryKind;
|
|
105
|
-
lines: JournalLine[];
|
|
106
|
-
/** Entry-level memo. */
|
|
107
|
-
memo?: string;
|
|
108
|
-
/** When `kind === "void-marker"`: id of the entry being voided.
|
|
109
|
-
* When `kind === "void"`: the system-generated reverse entry
|
|
110
|
-
* references the original via this field. */
|
|
111
|
-
voidedEntryId?: string;
|
|
112
|
-
/** Reason supplied by the user when voiding. */
|
|
113
|
-
voidReason?: string;
|
|
114
|
-
/** When this entry was posted via the "edit" flow (void-then-add),
|
|
115
|
-
* this is the id of the entry it replaces. The void + new-entry
|
|
116
|
-
* pair is *not* atomic on the server — the client issues two
|
|
117
|
-
* sequential calls — but recording the link here makes the
|
|
118
|
-
* edit chain queryable later (e.g. "what corrected entry X?"). */
|
|
119
|
-
replacesEntryId?: string;
|
|
120
|
-
/** ISO timestamp the entry was appended to the journal — the
|
|
121
|
-
* authoritative "when did this hit the books" clock. Distinct
|
|
122
|
-
* from `date`, which is the user-visible booking date. */
|
|
123
|
-
createdAt: string;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** Aggregated balance per account at a point in time. The signed
|
|
127
|
-
* number is debit − credit; downstream display logic converts to
|
|
128
|
-
* natural sign per account type (assets debit-positive, liabilities
|
|
129
|
-
* credit-positive). */
|
|
130
|
-
export interface AccountBalance {
|
|
131
|
-
accountCode: string;
|
|
132
|
-
/** Σ debit − Σ credit across all entries up to and including the
|
|
133
|
-
* snapshot's period end. */
|
|
134
|
-
netDebit: number;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export interface MonthSnapshot {
|
|
138
|
-
/** "YYYY-MM" — the closing month covered. */
|
|
139
|
-
period: string;
|
|
140
|
-
/** Closing balances at end of `period`. */
|
|
141
|
-
balances: AccountBalance[];
|
|
142
|
-
/** ISO timestamp the snapshot file was written. */
|
|
143
|
-
builtAt: string;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** Period selector for reports. Either a single closing month or a
|
|
147
|
-
* date range. Always inclusive on both ends. */
|
|
148
|
-
export type ReportPeriod = { kind: "month"; period: string } | { kind: "range"; from: string; to: string };
|
|
@@ -1,373 +0,0 @@
|
|
|
1
|
-
// REST endpoint for the accounting plugin. Single POST dispatch
|
|
2
|
-
// route with an `action` discriminator — matches the todos /
|
|
3
|
-
// scheduler convention so the LLM-facing MCP bridge (which invokes
|
|
4
|
-
// `apiPost` with the tool args verbatim) plugs in without
|
|
5
|
-
// translation.
|
|
6
|
-
//
|
|
7
|
-
// The mounted `<AccountingApp>` View hits this same endpoint
|
|
8
|
-
// directly for tab switches, filter changes, and form submits — no
|
|
9
|
-
// LLM round trip per click. The MCP bridge calls into the same
|
|
10
|
-
// service layer, so manual clicks and Claude tool calls produce
|
|
11
|
-
// identical state changes.
|
|
12
|
-
|
|
13
|
-
import { Router, Request, Response } from "express";
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
AccountingError,
|
|
17
|
-
addEntries,
|
|
18
|
-
createBook,
|
|
19
|
-
updateBook,
|
|
20
|
-
deleteBook,
|
|
21
|
-
getBalanceSheetReport,
|
|
22
|
-
getLedgerReport,
|
|
23
|
-
getOpeningBalances,
|
|
24
|
-
getProfitLossReport,
|
|
25
|
-
getTimeSeriesReport,
|
|
26
|
-
listAccounts,
|
|
27
|
-
listBooks,
|
|
28
|
-
listEntries,
|
|
29
|
-
rebuildSnapshots,
|
|
30
|
-
setOpeningBalances,
|
|
31
|
-
upsertAccount,
|
|
32
|
-
voidEntry,
|
|
33
|
-
} from "../../accounting/service.js";
|
|
34
|
-
import type { BookSummary } from "../../accounting/types.js";
|
|
35
|
-
import { ACCOUNTING_ACTIONS } from "../../../src/plugins/accounting/actions.js";
|
|
36
|
-
import { API_ROUTES } from "../../../src/config/apiRoutes.js";
|
|
37
|
-
import { bindRoute } from "../../utils/router.js";
|
|
38
|
-
import { log } from "../../system/logger/index.js";
|
|
39
|
-
import { asyncHandler } from "../../utils/asyncHandler.js";
|
|
40
|
-
|
|
41
|
-
const router = Router();
|
|
42
|
-
|
|
43
|
-
interface AccountingActionBody {
|
|
44
|
-
action: string;
|
|
45
|
-
[key: string]: unknown;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
interface AccountingErrorResponse {
|
|
49
|
-
error: string;
|
|
50
|
-
details?: unknown;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Tool-result envelope for the MCP-driven `openBook` action. The
|
|
54
|
-
// frontend tool-result renderer keys off `kind: "accounting-app"`
|
|
55
|
-
// to mount `<AccountingApp>` (vs the compact `Preview.vue` which
|
|
56
|
-
// renders summaries for every other action). `message` is picked
|
|
57
|
-
// up by the MCP bridge and surfaced as the tool's text response
|
|
58
|
-
// to the LLM (server/agent/mcp-server.ts).
|
|
59
|
-
interface OpenBookToolResult {
|
|
60
|
-
kind: "accounting-app";
|
|
61
|
-
bookId: string;
|
|
62
|
-
initialTab?: string;
|
|
63
|
-
/** Same shape getBooks returns — included so an LLM that calls
|
|
64
|
-
* openBook doesn't need a follow-up getBooks round-trip to learn
|
|
65
|
-
* what other books exist before its next action. */
|
|
66
|
-
books: BookSummary[];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
type ActionRest = Omit<AccountingActionBody, "action">;
|
|
70
|
-
type ActionHandler = (rest: ActionRest) => Promise<unknown>;
|
|
71
|
-
|
|
72
|
-
// Each action is a tiny adapter that pulls the typed slice it needs
|
|
73
|
-
// out of the loosely-typed body. Validation of the slice shape
|
|
74
|
-
// itself lives inside the service layer (validateEntry,
|
|
75
|
-
// validateOpening) so the adapters can stay one-liners.
|
|
76
|
-
|
|
77
|
-
async function handleOpenBook(rest: ActionRest): Promise<OpenBookToolResult> {
|
|
78
|
-
// openBook requires an explicit `bookId` that resolves to an
|
|
79
|
-
// existing book. On a fresh workspace the LLM is expected to
|
|
80
|
-
// call `createBook` first and then `openBook` with the new id.
|
|
81
|
-
if (typeof rest.bookId !== "string" || rest.bookId === "") {
|
|
82
|
-
throw new AccountingError(400, "openBook: bookId is required. Call 'getBooks' to enumerate, or 'createBook' first on a fresh workspace.");
|
|
83
|
-
}
|
|
84
|
-
const list = await listBooks();
|
|
85
|
-
if (!list.books.some((book) => book.id === rest.bookId)) {
|
|
86
|
-
throw new AccountingError(404, `openBook: book ${JSON.stringify(rest.bookId)} not found`);
|
|
87
|
-
}
|
|
88
|
-
const initialTab = typeof rest.initialTab === "string" ? rest.initialTab : undefined;
|
|
89
|
-
return { kind: "accounting-app", bookId: rest.bookId, initialTab, books: list.books };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async function handleGetReport(rest: ActionRest): Promise<unknown> {
|
|
93
|
-
const kind = String(rest.kind ?? "");
|
|
94
|
-
const periodInput = rest.period as { kind: "month"; period: string } | { kind: "range"; from: string; to: string } | undefined;
|
|
95
|
-
const bookId = rest.bookId as string | undefined;
|
|
96
|
-
if (kind === "balance") {
|
|
97
|
-
if (!periodInput) throw new AccountingError(400, "getReport balance: period is required");
|
|
98
|
-
return getBalanceSheetReport({ bookId, period: periodInput });
|
|
99
|
-
}
|
|
100
|
-
if (kind === "pl") {
|
|
101
|
-
if (!periodInput) throw new AccountingError(400, "getReport pl: period is required");
|
|
102
|
-
return getProfitLossReport({ bookId, period: periodInput });
|
|
103
|
-
}
|
|
104
|
-
if (kind === "ledger") {
|
|
105
|
-
// period is optional for ledger — full-history view from the
|
|
106
|
-
// UI calls getLedger(accountCode, undefined, bookId). The
|
|
107
|
-
// account code, however, is mandatory; without it the request
|
|
108
|
-
// is meaningless and the service would 404 on a blank code.
|
|
109
|
-
if (typeof rest.accountCode !== "string" || rest.accountCode === "") {
|
|
110
|
-
throw new AccountingError(400, "getReport ledger: accountCode is required");
|
|
111
|
-
}
|
|
112
|
-
return getLedgerReport({ bookId, accountCode: rest.accountCode, period: periodInput });
|
|
113
|
-
}
|
|
114
|
-
throw new AccountingError(400, `getReport: unknown kind ${JSON.stringify(kind)}`);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const ACTION_HANDLERS: Record<string, ActionHandler> = {
|
|
118
|
-
[ACCOUNTING_ACTIONS.openBook]: handleOpenBook,
|
|
119
|
-
[ACCOUNTING_ACTIONS.getBooks]: () => listBooks(),
|
|
120
|
-
[ACCOUNTING_ACTIONS.createBook]: async (rest) => {
|
|
121
|
-
// Surface bookId at the top level so the dispatch envelope's
|
|
122
|
-
// `data` carries it like every other write action — the View
|
|
123
|
-
// uses it to preselect the new book on mount.
|
|
124
|
-
const result = await createBook({
|
|
125
|
-
name: String(rest.name ?? ""),
|
|
126
|
-
currency: typeof rest.currency === "string" ? rest.currency : undefined,
|
|
127
|
-
country: typeof rest.country === "string" ? rest.country : undefined,
|
|
128
|
-
fiscalYearEnd: typeof rest.fiscalYearEnd === "string" ? rest.fiscalYearEnd : undefined,
|
|
129
|
-
});
|
|
130
|
-
return { bookId: result.book.id, ...result };
|
|
131
|
-
},
|
|
132
|
-
[ACCOUNTING_ACTIONS.updateBook]: async (rest) => {
|
|
133
|
-
const result = await updateBook({
|
|
134
|
-
bookId: String(rest.bookId ?? ""),
|
|
135
|
-
name: typeof rest.name === "string" ? rest.name : undefined,
|
|
136
|
-
country: typeof rest.country === "string" ? rest.country : undefined,
|
|
137
|
-
fiscalYearEnd: typeof rest.fiscalYearEnd === "string" ? rest.fiscalYearEnd : undefined,
|
|
138
|
-
});
|
|
139
|
-
return { bookId: result.book.id, ...result };
|
|
140
|
-
},
|
|
141
|
-
[ACCOUNTING_ACTIONS.deleteBook]: (rest) => deleteBook({ bookId: String(rest.bookId ?? ""), confirm: rest.confirm === true }),
|
|
142
|
-
[ACCOUNTING_ACTIONS.getAccounts]: (rest) => listAccounts({ bookId: rest.bookId as string | undefined }),
|
|
143
|
-
[ACCOUNTING_ACTIONS.upsertAccount]: (rest) =>
|
|
144
|
-
upsertAccount({
|
|
145
|
-
bookId: rest.bookId as string | undefined,
|
|
146
|
-
// Service validates the shape — route doesn't reach into it.
|
|
147
|
-
account: rest.account as never,
|
|
148
|
-
}),
|
|
149
|
-
[ACCOUNTING_ACTIONS.addEntries]: (rest) =>
|
|
150
|
-
addEntries({
|
|
151
|
-
bookId: rest.bookId as string | undefined,
|
|
152
|
-
// Service validates each entry's shape — route doesn't reach into it.
|
|
153
|
-
entries: (rest.entries ?? []) as never,
|
|
154
|
-
}),
|
|
155
|
-
[ACCOUNTING_ACTIONS.voidEntry]: (rest) =>
|
|
156
|
-
voidEntry({
|
|
157
|
-
bookId: rest.bookId as string | undefined,
|
|
158
|
-
entryId: String(rest.entryId ?? ""),
|
|
159
|
-
reason: rest.reason as string | undefined,
|
|
160
|
-
voidDate: rest.voidDate as string | undefined,
|
|
161
|
-
}),
|
|
162
|
-
[ACCOUNTING_ACTIONS.getJournalEntries]: (rest) =>
|
|
163
|
-
listEntries({
|
|
164
|
-
bookId: rest.bookId as string | undefined,
|
|
165
|
-
from: rest.from as string | undefined,
|
|
166
|
-
to: rest.to as string | undefined,
|
|
167
|
-
accountCode: rest.accountCode as string | undefined,
|
|
168
|
-
}),
|
|
169
|
-
[ACCOUNTING_ACTIONS.getOpeningBalances]: (rest) => getOpeningBalances({ bookId: rest.bookId as string | undefined }),
|
|
170
|
-
[ACCOUNTING_ACTIONS.setOpeningBalances]: (rest) =>
|
|
171
|
-
setOpeningBalances({
|
|
172
|
-
bookId: rest.bookId as string | undefined,
|
|
173
|
-
asOfDate: String(rest.asOfDate ?? ""),
|
|
174
|
-
lines: (rest.lines ?? []) as never,
|
|
175
|
-
memo: rest.memo as string | undefined,
|
|
176
|
-
}),
|
|
177
|
-
[ACCOUNTING_ACTIONS.getReport]: handleGetReport,
|
|
178
|
-
[ACCOUNTING_ACTIONS.getTimeSeries]: (rest) =>
|
|
179
|
-
getTimeSeriesReport({
|
|
180
|
-
bookId: rest.bookId as string | undefined,
|
|
181
|
-
metric: rest.metric,
|
|
182
|
-
granularity: rest.granularity,
|
|
183
|
-
from: rest.from,
|
|
184
|
-
to: rest.to,
|
|
185
|
-
accountCode: rest.accountCode,
|
|
186
|
-
}),
|
|
187
|
-
[ACCOUNTING_ACTIONS.rebuildSnapshots]: (rest) => rebuildSnapshots({ bookId: rest.bookId as string | undefined }),
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
// Actions whose tool-result envelope should carry a `data` field so
|
|
191
|
-
// the sidebar renders a preview card. Everything else returns
|
|
192
|
-
// without `data` and the host gates the preview off (silent action).
|
|
193
|
-
// Reads (lists / reports) and View-driven maintenance ops stay
|
|
194
|
-
// silent — they're invoked from inside the canvas and the LLM will
|
|
195
|
-
// summarise reads in its text reply anyway.
|
|
196
|
-
const PREVIEW_ACTIONS = new Set<string>([
|
|
197
|
-
ACCOUNTING_ACTIONS.openBook,
|
|
198
|
-
ACCOUNTING_ACTIONS.createBook,
|
|
199
|
-
ACCOUNTING_ACTIONS.updateBook,
|
|
200
|
-
ACCOUNTING_ACTIONS.upsertAccount,
|
|
201
|
-
ACCOUNTING_ACTIONS.addEntries,
|
|
202
|
-
ACCOUNTING_ACTIONS.voidEntry,
|
|
203
|
-
ACCOUNTING_ACTIONS.setOpeningBalances,
|
|
204
|
-
]);
|
|
205
|
-
|
|
206
|
-
// LLM-facing `message` tacked onto YES actions. The shared trailer
|
|
207
|
-
// ("The accounting view is shown to the user.") tells the LLM that a
|
|
208
|
-
// canvas / sidebar surface is already visible, so its text reply
|
|
209
|
-
// shouldn't redundantly enumerate the result the user can see — it
|
|
210
|
-
// should narrate what was *done*, not re-list what's on screen.
|
|
211
|
-
const VIEW_VISIBLE_TRAILER = "The accounting view is shown to the user.";
|
|
212
|
-
|
|
213
|
-
type MessageBuilder = (fields: Record<string, unknown>) => string;
|
|
214
|
-
|
|
215
|
-
const MESSAGE_BUILDERS: Record<string, MessageBuilder> = {
|
|
216
|
-
[ACCOUNTING_ACTIONS.openBook]: (fields) => {
|
|
217
|
-
// Include the books list inline so the LLM doesn't need a
|
|
218
|
-
// follow-up getBooks round-trip before deciding what to do
|
|
219
|
-
// next.
|
|
220
|
-
const { books, bookId } = fields;
|
|
221
|
-
const booksFragment = Array.isArray(books) ? ` Books available: ${JSON.stringify(books)}.` : "";
|
|
222
|
-
const idFragment = typeof bookId === "string" ? ` (book id: ${bookId})` : "";
|
|
223
|
-
return `Mounted the accounting app in the canvas${idFragment}.${booksFragment}`;
|
|
224
|
-
},
|
|
225
|
-
[ACCOUNTING_ACTIONS.createBook]: (fields) => {
|
|
226
|
-
const book = fields.book as { id?: string; name?: string } | undefined;
|
|
227
|
-
const subject = book?.name ? `A new book named ${JSON.stringify(book.name)}` : "A new book";
|
|
228
|
-
// The LLM needs book.id to call any follow-up action on this
|
|
229
|
-
// book (getAccounts, addEntries, etc.), so include it in the
|
|
230
|
-
// status message instead of forcing a round-trip via getBooks.
|
|
231
|
-
const idFragment = book?.id ? ` (id: ${book.id})` : "";
|
|
232
|
-
// The View's opening-gate hides every tab except `opening` and
|
|
233
|
-
// `settings` until an opening entry is on file (even a zero-line
|
|
234
|
-
// one). If the agent doesn't tell the user to set opening
|
|
235
|
-
// balances first, the user's "can I add an entry?" attempt
|
|
236
|
-
// silently fails because the New Entry tab isn't even visible.
|
|
237
|
-
// Include the next-step instruction inline so the agent's reply
|
|
238
|
-
// matches the UI's actual constraints.
|
|
239
|
-
return `${subject} has been created${idFragment}. Next required step: set opening balances via setOpeningBalances — the journal-entry, ledger, and report tabs are locked until an opening (even an empty one) is saved.`;
|
|
240
|
-
},
|
|
241
|
-
[ACCOUNTING_ACTIONS.upsertAccount]: (fields) => {
|
|
242
|
-
const account = fields.account as { code?: string; name?: string } | undefined;
|
|
243
|
-
if (account?.code && account?.name) {
|
|
244
|
-
return `Upserted account ${account.code} ${JSON.stringify(account.name)}.`;
|
|
245
|
-
}
|
|
246
|
-
return "Updated the chart of accounts.";
|
|
247
|
-
},
|
|
248
|
-
[ACCOUNTING_ACTIONS.addEntries]: (fields) => {
|
|
249
|
-
const entries = Array.isArray(fields.entries) ? (fields.entries as { id?: string; date?: string }[]) : [];
|
|
250
|
-
if (entries.length === 0) return "Posted 0 journal entries.";
|
|
251
|
-
if (entries.length === 1) {
|
|
252
|
-
const [entry] = entries;
|
|
253
|
-
const idFragment = entry?.id ? ` (id: ${entry.id})` : "";
|
|
254
|
-
return `Posted a journal entry on ${entry?.date ?? "the requested date"}${idFragment}.`;
|
|
255
|
-
}
|
|
256
|
-
// Surface every id so the LLM can later voidEntry any one of
|
|
257
|
-
// them without a follow-up getJournalEntries round-trip.
|
|
258
|
-
const summary = entries.map((entry) => `${entry?.date ?? "?"} (id: ${entry?.id ?? "?"})`).join(", ");
|
|
259
|
-
return `Posted ${entries.length} journal entries: ${summary}.`;
|
|
260
|
-
},
|
|
261
|
-
[ACCOUNTING_ACTIONS.voidEntry]: (fields) => {
|
|
262
|
-
const reverse = fields.reverseEntry as { date?: string } | undefined;
|
|
263
|
-
return `Voided the entry; a reversing pair was posted on ${reverse?.date ?? "today"}.`;
|
|
264
|
-
},
|
|
265
|
-
[ACCOUNTING_ACTIONS.setOpeningBalances]: (fields) => {
|
|
266
|
-
const opening = fields.openingEntry as { date?: string; lines?: unknown } | undefined;
|
|
267
|
-
const verb = fields.replacedExisting === true ? "replaced" : "set";
|
|
268
|
-
const date = opening?.date ?? "the requested date";
|
|
269
|
-
// Surface the actual lines so the LLM can answer follow-up
|
|
270
|
-
// questions like "what's my opening cash?" without a separate
|
|
271
|
-
// getOpeningBalances round-trip. An empty-marker opening
|
|
272
|
-
// (zero lines, used to unlock the gate) gets no fragment.
|
|
273
|
-
const lines = Array.isArray(opening?.lines) ? (opening.lines as unknown[]) : [];
|
|
274
|
-
const linesFragment = lines.length > 0 ? ` Lines: ${JSON.stringify(lines)}.` : "";
|
|
275
|
-
return `Opening balances were ${verb} as of ${date}.${linesFragment}`;
|
|
276
|
-
},
|
|
277
|
-
[ACCOUNTING_ACTIONS.deleteBook]: (fields) => {
|
|
278
|
-
const bookId = fields.deletedBookId as string | undefined;
|
|
279
|
-
const name = fields.deletedBookName as string | undefined;
|
|
280
|
-
const subject = name ? `the book ${JSON.stringify(name)}` : "the book";
|
|
281
|
-
const idFragment = bookId ? ` (id: ${bookId})` : "";
|
|
282
|
-
return `Deleted ${subject}${idFragment}.`;
|
|
283
|
-
},
|
|
284
|
-
[ACCOUNTING_ACTIONS.updateBook]: (fields) => {
|
|
285
|
-
const book = fields.book as { id?: string; name?: string; country?: string; currency?: string } | undefined;
|
|
286
|
-
const name = book?.name ? JSON.stringify(book.name) : "the book";
|
|
287
|
-
const countryFragment = book?.country ? ` (country: ${book.country})` : "";
|
|
288
|
-
return `Updated ${name}${countryFragment}.`;
|
|
289
|
-
},
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
function previewMessage(action: string, fields: Record<string, unknown>): string {
|
|
293
|
-
const head = MESSAGE_BUILDERS[action]?.(fields);
|
|
294
|
-
return head ? `${head} ${VIEW_VISIBLE_TRAILER}` : VIEW_VISIBLE_TRAILER;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
async function dispatch(body: AccountingActionBody): Promise<unknown> {
|
|
298
|
-
const { action, ...rest } = body;
|
|
299
|
-
const handler = ACTION_HANDLERS[action];
|
|
300
|
-
if (!handler) throw new AccountingError(400, `unknown action ${JSON.stringify(action)}`);
|
|
301
|
-
// Stamp the dispatch verb onto the response so the MCP bridge's
|
|
302
|
-
// spread `{ toolName, uuid, ...result }` surfaces it as
|
|
303
|
-
// `ToolResult.action`. The sidebar reads this to label cards as
|
|
304
|
-
// `manageAccounting(openBook)` etc., and it round-trips a refresh
|
|
305
|
-
// because the result envelope is persisted to the chat log.
|
|
306
|
-
// Direct browser callers (the AccountingApp view) ignore the field.
|
|
307
|
-
// Service responses that already set `action` win via the spread.
|
|
308
|
-
const result = await handler(rest);
|
|
309
|
-
const handlerFields = result && typeof result === "object" ? (result as Record<string, unknown>) : { value: result };
|
|
310
|
-
// `data` is the host's preview-eligibility signal (see
|
|
311
|
-
// SessionSidebar.vue's v-if gate). Mirror the handler payload
|
|
312
|
-
// into it for the actions that should render a card; leave it
|
|
313
|
-
// off for silent ones so the gate suppresses the preview.
|
|
314
|
-
const dataField = PREVIEW_ACTIONS.has(action) ? { data: { action, ...handlerFields } } : {};
|
|
315
|
-
// The MCP bridge only forwards `message` / `instructions` to the
|
|
316
|
-
// LLM (`data` / `jsonData` reach the view but not the model). So
|
|
317
|
-
// every action MUST set a message — silence resolves to "Done"
|
|
318
|
-
// and gives the LLM nothing to reason about. Resolution order:
|
|
319
|
-
// 1. handler-set `message` wins (reserved for special-case
|
|
320
|
-
// narration that the standard MESSAGE_BUILDER can't capture);
|
|
321
|
-
// 2. an action with a registered MESSAGE_BUILDER gets the
|
|
322
|
-
// per-action human-friendly summary; this is decoupled from
|
|
323
|
-
// PREVIEW_ACTIONS so silent ops like deleteBook can still
|
|
324
|
-
// narrate without earning a card;
|
|
325
|
-
// 3. everything else returns the JSON-stringified handler
|
|
326
|
-
// payload so the LLM can read the raw data.
|
|
327
|
-
const handlerMessage = typeof handlerFields.message === "string" ? handlerFields.message : undefined;
|
|
328
|
-
const messageField = handlerMessage
|
|
329
|
-
? {}
|
|
330
|
-
: MESSAGE_BUILDERS[action]
|
|
331
|
-
? { message: previewMessage(action, handlerFields) }
|
|
332
|
-
: { message: JSON.stringify(handlerFields) };
|
|
333
|
-
return { action, ...handlerFields, ...messageField, ...dataField };
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
bindRoute(
|
|
337
|
-
router,
|
|
338
|
-
API_ROUTES.accounting.dispatch,
|
|
339
|
-
asyncHandler<Request<object, unknown, AccountingActionBody>, Response<unknown | AccountingErrorResponse>>(
|
|
340
|
-
"accounting",
|
|
341
|
-
"accounting dispatch failed",
|
|
342
|
-
async (req, res) => {
|
|
343
|
-
// Validate the body shape up front so a missing / non-object body
|
|
344
|
-
// surfaces as a 400 instead of crashing `dispatch` and bubbling
|
|
345
|
-
// through to the 500 catch-all.
|
|
346
|
-
const { body } = req;
|
|
347
|
-
if (!body || typeof body !== "object" || typeof body.action !== "string") {
|
|
348
|
-
log.warn("accounting", "POST dispatch: invalid body");
|
|
349
|
-
res.status(400).json({ error: "request body must be an object with a string `action` field" });
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
const { action } = body;
|
|
353
|
-
log.info("accounting", "POST dispatch: start", { action });
|
|
354
|
-
try {
|
|
355
|
-
const result = await dispatch(body);
|
|
356
|
-
log.info("accounting", "POST dispatch: ok", { action });
|
|
357
|
-
res.json(result);
|
|
358
|
-
} catch (err) {
|
|
359
|
-
// Domain errors (AccountingError) map to 4xx with `details`.
|
|
360
|
-
// Anything else rethrows — the asyncHandler wrapper catches
|
|
361
|
-
// it, logs `unexpected error`, and returns a generic 500.
|
|
362
|
-
if (err instanceof AccountingError) {
|
|
363
|
-
log.warn("accounting", "POST dispatch: error", { action, status: err.status, message: err.message });
|
|
364
|
-
res.status(err.status).json({ error: err.message, details: err.details });
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
throw err;
|
|
368
|
-
}
|
|
369
|
-
},
|
|
370
|
-
),
|
|
371
|
-
);
|
|
372
|
-
|
|
373
|
-
export default router;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// Narrow `tags:` field reader for wiki page files. Built on the
|
|
2
|
-
// shared `parseFrontmatter` util (#895 PR C) — js-yaml handles
|
|
3
|
-
// both flow style (`tags: [a, b, c]`) and block-list style:
|
|
4
|
-
//
|
|
5
|
-
// tags:
|
|
6
|
-
// - a
|
|
7
|
-
// - b
|
|
8
|
-
//
|
|
9
|
-
// out of the box, so we just normalise the resulting strings.
|
|
10
|
-
//
|
|
11
|
-
// Anything unparseable returns `[]` — callers use this for a
|
|
12
|
-
// best-effort comparison against index.md, so a noisy file should
|
|
13
|
-
// degrade silently, not throw.
|
|
14
|
-
|
|
15
|
-
import { parseFrontmatter } from "../../../utils/markdown/frontmatter.js";
|
|
16
|
-
|
|
17
|
-
function cleanTagToken(token: string): string {
|
|
18
|
-
return token
|
|
19
|
-
.trim()
|
|
20
|
-
.replace(/^['"]|['"]$/g, "")
|
|
21
|
-
.replace(/^#/, "")
|
|
22
|
-
.toLowerCase();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function parseFrontmatterTags(content: string): string[] {
|
|
26
|
-
const parsed = parseFrontmatter(content);
|
|
27
|
-
if (!parsed.hasHeader) return [];
|
|
28
|
-
const tagsValue = parsed.meta.tags;
|
|
29
|
-
if (!Array.isArray(tagsValue)) return [];
|
|
30
|
-
return tagsValue
|
|
31
|
-
.filter((item): item is string => typeof item === "string")
|
|
32
|
-
.map(cleanTagToken)
|
|
33
|
-
.filter((token) => token.length > 0);
|
|
34
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
// In-memory index of `wiki/pages/*.md` keyed by slug (= filename
|
|
2
|
-
// without the `.md` extension). Kept fresh via `pagesDir` mtime —
|
|
3
|
-
// adding, removing, or renaming a file under `pagesDir` advances
|
|
4
|
-
// the directory's mtime on every major filesystem we target
|
|
5
|
-
// (macOS APFS, Linux ext4, Windows NTFS), so one cheap `stat()`
|
|
6
|
-
// per request is enough to decide whether to rebuild.
|
|
7
|
-
//
|
|
8
|
-
// Eliminates the sync `readdirSync` + linear `find()` in the old
|
|
9
|
-
// `resolvePagePath` — see #201.
|
|
10
|
-
|
|
11
|
-
import { readDirSafeAsync, statSafeAsync } from "../../../utils/files/safe.js";
|
|
12
|
-
|
|
13
|
-
export interface PageIndex {
|
|
14
|
-
mtimeMs: number;
|
|
15
|
-
/** slug → filename (e.g. "sakura-internet" → "sakura-internet.md"). */
|
|
16
|
-
slugs: Map<string, string>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
let cache: PageIndex | null = null;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Get the page index for `pagesDir`. Returns a cached value as long
|
|
23
|
-
* as the directory's mtime hasn't advanced; otherwise rebuilds.
|
|
24
|
-
*
|
|
25
|
-
* Safe to call concurrently — racing builds produce the same result.
|
|
26
|
-
*/
|
|
27
|
-
export async function getPageIndex(pagesDir: string): Promise<PageIndex> {
|
|
28
|
-
const stat = await statSafeAsync(pagesDir);
|
|
29
|
-
if (!stat) {
|
|
30
|
-
// Dir doesn't exist yet (never ingested). Return empty but
|
|
31
|
-
// don't cache a stale-forever value — the next call will
|
|
32
|
-
// re-stat and pick up the dir once it lands.
|
|
33
|
-
return { mtimeMs: 0, slugs: new Map() };
|
|
34
|
-
}
|
|
35
|
-
if (cache && cache.mtimeMs >= stat.mtimeMs) {
|
|
36
|
-
return cache;
|
|
37
|
-
}
|
|
38
|
-
const entries = await readDirSafeAsync(pagesDir);
|
|
39
|
-
const slugs = new Map<string, string>();
|
|
40
|
-
for (const entry of entries) {
|
|
41
|
-
if (!entry.isFile()) continue;
|
|
42
|
-
const { name } = entry;
|
|
43
|
-
if (!name.endsWith(".md")) continue;
|
|
44
|
-
slugs.set(name.slice(0, -".md".length), name);
|
|
45
|
-
}
|
|
46
|
-
cache = { mtimeMs: stat.mtimeMs, slugs };
|
|
47
|
-
return cache;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Test-only: drop the module-level cache. */
|
|
51
|
-
export function __resetPageIndexCache(): void {
|
|
52
|
-
cache = null;
|
|
53
|
-
}
|