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,9 +1,11 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { Router, Request, Response } from "express";
|
|
2
3
|
import { getSessionQuery } from "../../utils/request.js";
|
|
3
4
|
import {
|
|
4
5
|
createSessionMeta,
|
|
5
6
|
backfillFirstUserMessage as backfillMeta,
|
|
6
7
|
backfillOrigin,
|
|
8
|
+
incrementUserQueryCount,
|
|
7
9
|
readSessionMetaFull,
|
|
8
10
|
readSessionMeta,
|
|
9
11
|
setClaudeSessionId as setClaudeId,
|
|
@@ -33,7 +35,14 @@ import { createArgsCache, recordToolEvent } from "../../workspace/tool-trace/ind
|
|
|
33
35
|
import { API_ROUTES } from "../../../src/config/apiRoutes.js";
|
|
34
36
|
import { EVENT_TYPES } from "../../../src/types/events.js";
|
|
35
37
|
import { isSessionOrigin, SESSION_ORIGINS, type SessionOrigin } from "../../../src/types/session.js";
|
|
36
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
tryReserveBackgroundSession,
|
|
40
|
+
releaseBackgroundSession,
|
|
41
|
+
registerCompletionHook,
|
|
42
|
+
runCompletionHook,
|
|
43
|
+
MAX_BACKGROUND_SESSIONS,
|
|
44
|
+
type CompletionHook,
|
|
45
|
+
} from "../../agent/backgroundSessions.js";
|
|
37
46
|
// Imports kept commented (instead of deleted) alongside the
|
|
38
47
|
// publishNotification call in `runPostTurnSideEffects` — see the
|
|
39
48
|
// duplicate-notification comment there for context. (`SESSION_ORIGINS`
|
|
@@ -133,6 +142,54 @@ export interface StartChatParams {
|
|
|
133
142
|
|
|
134
143
|
export type StartChatResult = { kind: "started"; chatSessionId: string } | { kind: "error"; error: string; status?: number };
|
|
135
144
|
|
|
145
|
+
/** Outcome of launching a worker session. */
|
|
146
|
+
export type SpawnSystemWorkerResult = { ok: true; chatId: string } | { ok: false; error: string };
|
|
147
|
+
|
|
148
|
+
// Launch a host-side worker session. `hidden` decides visibility:
|
|
149
|
+
// - true → origin `system`: never appears in the session list, runaway-cap
|
|
150
|
+
// reserved, `finalizeRun` invokes the completion hook + cleans up.
|
|
151
|
+
// Used for SCHEDULED agent-ingest refreshes (no one is watching).
|
|
152
|
+
// - false → origin `skill`: a normal visible chat the user can open from
|
|
153
|
+
// history, no cap, NO completion hook (the user watches it run
|
|
154
|
+
// directly). Used for a MANUAL Refresh-button refresh so it's
|
|
155
|
+
// debuggable.
|
|
156
|
+
// Exported so non-MCP host callers (the agent-ingest engine, wired in via
|
|
157
|
+
// `setAgentWorkerRunner`) can spawn one without going through the tool layer.
|
|
158
|
+
export async function spawnSystemWorker(args: {
|
|
159
|
+
message: string;
|
|
160
|
+
roleId: string;
|
|
161
|
+
hidden: boolean;
|
|
162
|
+
onComplete?: CompletionHook;
|
|
163
|
+
}): Promise<SpawnSystemWorkerResult> {
|
|
164
|
+
const chatId = randomUUID();
|
|
165
|
+
const origin: SessionOrigin = args.hidden ? SESSION_ORIGINS.system : SESSION_ORIGINS.skill;
|
|
166
|
+
// The runaway cap guards hidden workers only — a visible run is user-initiated
|
|
167
|
+
// and self-limiting. Reserve ATOMICALLY before launching; rolled back below if
|
|
168
|
+
// the launch fails (otherwise released in `runAgentInBackground`'s finally).
|
|
169
|
+
if (args.hidden && !tryReserveBackgroundSession(chatId)) {
|
|
170
|
+
return { ok: false, error: `too many background sessions already in flight (max ${MAX_BACKGROUND_SESSIONS})` };
|
|
171
|
+
}
|
|
172
|
+
let result: StartChatResult;
|
|
173
|
+
try {
|
|
174
|
+
result = await startChat({ message: args.message, roleId: args.roleId, chatSessionId: chatId, origin });
|
|
175
|
+
} catch (err) {
|
|
176
|
+
// `startChat` is normally fire-and-forget, but a synchronous setup failure
|
|
177
|
+
// can reject — release the reservation so the slot isn't leaked until restart.
|
|
178
|
+
if (args.hidden) releaseBackgroundSession(chatId);
|
|
179
|
+
return { ok: false, error: errorMessage(err) };
|
|
180
|
+
}
|
|
181
|
+
if (result.kind === "error") {
|
|
182
|
+
if (args.hidden) releaseBackgroundSession(chatId); // roll back the reservation
|
|
183
|
+
return { ok: false, error: result.error };
|
|
184
|
+
}
|
|
185
|
+
// Register the completion hook AFTER a successful launch (the subprocess can't
|
|
186
|
+
// finish before this synchronous code returns, so `finalizeRun` won't miss
|
|
187
|
+
// it). Only hidden (system) sessions run it — `finalizeRun` skips the hook for
|
|
188
|
+
// visible origins, which take the normal post-turn path instead.
|
|
189
|
+
if (args.hidden && args.onComplete) registerCompletionHook(chatId, args.onComplete);
|
|
190
|
+
return { ok: true, chatId };
|
|
191
|
+
}
|
|
192
|
+
|
|
136
193
|
export async function startChat(params: StartChatParams): Promise<StartChatResult> {
|
|
137
194
|
const { message, roleId, chatSessionId, selectedImageData, attachments, userTimezone } = params;
|
|
138
195
|
// Bridge-only compat: external bridge clients may still populate
|
|
@@ -224,6 +281,10 @@ export async function startChat(params: StartChatParams): Promise<StartChatResul
|
|
|
224
281
|
await backfillOrigin(chatSessionId, validOrigin);
|
|
225
282
|
}
|
|
226
283
|
}
|
|
284
|
+
// Count this user turn (createSessionMeta seeds no count, so the first
|
|
285
|
+
// turn bumps undefined→1). Lets the sidebar tell a one-shot apart from
|
|
286
|
+
// a long conversation.
|
|
287
|
+
await incrementUserQueryCount(chatSessionId);
|
|
227
288
|
|
|
228
289
|
// Append user message for this turn
|
|
229
290
|
await appendSessionLine(
|
|
@@ -870,6 +931,12 @@ export const _splitSkillAndReplyForTest = splitSkillAndReply;
|
|
|
870
931
|
// }
|
|
871
932
|
// }
|
|
872
933
|
|
|
934
|
+
/** A stale `--resume` failure we can recover from by retrying without it: an
|
|
935
|
+
* error event carrying a stale-session message, while failover budget remains. */
|
|
936
|
+
function isRecoverableStaleSession(event: { type: string; message?: unknown }, failoverAttemptsRemaining: number): boolean {
|
|
937
|
+
return failoverAttemptsRemaining > 0 && event.type === EVENT_TYPES.error && typeof event.message === "string" && isStaleSessionError(event.message);
|
|
938
|
+
}
|
|
939
|
+
|
|
873
940
|
async function runAgentInBackground(params: BackgroundRunParams): Promise<void> {
|
|
874
941
|
const { decoratedMessage, role, chatSessionId, claudeSessionId, abortSignal, resultsFilePath, requestStartedAt, toolArgsCache, attachments, userTimezone } =
|
|
875
942
|
params;
|
|
@@ -908,7 +975,7 @@ async function runAgentInBackground(params: BackgroundRunParams): Promise<void>
|
|
|
908
975
|
attachments,
|
|
909
976
|
userTimezone,
|
|
910
977
|
})) {
|
|
911
|
-
if (
|
|
978
|
+
if (isRecoverableStaleSession(event, failoverAttemptsRemaining)) {
|
|
912
979
|
// Swallow the error — we're about to recover. `break`
|
|
913
980
|
// abandons the current generator; since the event is only
|
|
914
981
|
// yielded after the CLI has already exited non-zero, the
|
|
@@ -918,6 +985,12 @@ async function runAgentInBackground(params: BackgroundRunParams): Promise<void>
|
|
|
918
985
|
failoverAttemptsRemaining--;
|
|
919
986
|
break;
|
|
920
987
|
}
|
|
988
|
+
// A yielded error event (non-zero Claude exit, missing binary, a tool
|
|
989
|
+
// surfacing an error) is a real failure even though the generator
|
|
990
|
+
// didn't throw — record it so `finalizeRun`'s hidden-worker cleanup and
|
|
991
|
+
// the agent-ingest completion hook see `didError`. The stale-session
|
|
992
|
+
// failover above returns earlier, so a recoverable id doesn't count.
|
|
993
|
+
if (event.type === EVENT_TYPES.error) didError = true;
|
|
921
994
|
await handleAgentEvent(event, eventCtx);
|
|
922
995
|
}
|
|
923
996
|
if (!staleSessionDetected) break;
|
|
@@ -978,6 +1051,10 @@ async function finalizeRun(chatSessionId: string, origin: SessionOrigin | undefi
|
|
|
978
1051
|
// plumbing and pollute wiki backlinks), and clean up its files on
|
|
979
1052
|
// success — keep them on error so a failed worker stays inspectable.
|
|
980
1053
|
releaseBackgroundSession(chatSessionId);
|
|
1054
|
+
// Fire any one-shot completion hook (e.g. agent-ingest failure tracking)
|
|
1055
|
+
// AFTER the slot is freed, BEFORE files are cleaned up. Best-effort —
|
|
1056
|
+
// a throwing hook is logged, never propagated.
|
|
1057
|
+
await runCompletionHook(chatSessionId, { didError }).catch(logBackgroundError("background-session-completion-hook"));
|
|
981
1058
|
if (!didError) {
|
|
982
1059
|
await deleteSessionFiles(chatSessionId).catch(logBackgroundError("background-session-cleanup"));
|
|
983
1060
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { Router, Request, Response, NextFunction } from "express";
|
|
12
12
|
import { API_ROUTES } from "../../../src/config/apiRoutes.js";
|
|
13
|
-
import { actionVisible } from "@mulmoclaude/collection
|
|
13
|
+
import { actionVisible } from "@mulmoclaude/core/collection";
|
|
14
14
|
import {
|
|
15
15
|
discoverCollections,
|
|
16
16
|
generateItemId,
|
|
@@ -23,8 +23,10 @@ import {
|
|
|
23
23
|
readItem,
|
|
24
24
|
readSkillTemplate,
|
|
25
25
|
readCustomViewHtml,
|
|
26
|
+
readCustomViewI18n,
|
|
26
27
|
buildActionSeedPrompt,
|
|
27
28
|
buildCollectionActionSeedPrompt,
|
|
29
|
+
promptPathsFor,
|
|
28
30
|
resolveCreateItemId,
|
|
29
31
|
toDetail,
|
|
30
32
|
toSummary,
|
|
@@ -44,7 +46,7 @@ import { badRequest, notFound, conflict, forbidden, serverError } from "../../ut
|
|
|
44
46
|
import { errorMessage } from "../../utils/errors.js";
|
|
45
47
|
import { log } from "../../system/logger/index.js";
|
|
46
48
|
import { workspacePath } from "../../workspace/workspace.js";
|
|
47
|
-
import { refreshOne } from "
|
|
49
|
+
import { refreshOne } from "@mulmoclaude/core/feeds/server";
|
|
48
50
|
import { manageCollection } from "../../agent/mcp-tools/manageCollection.js";
|
|
49
51
|
import { clampCapabilities, mintViewToken, requireViewToken, type ViewCapability } from "../auth/viewToken.js";
|
|
50
52
|
|
|
@@ -279,6 +281,13 @@ interface RefreshResponse {
|
|
|
279
281
|
refreshed: true;
|
|
280
282
|
written: number;
|
|
281
283
|
errors: string[];
|
|
284
|
+
/** True when an agent-ingest refresh dispatched a worker (fire-and-forget):
|
|
285
|
+
* records update asynchronously, so the client shows a note rather than a
|
|
286
|
+
* written count. */
|
|
287
|
+
dispatched?: boolean;
|
|
288
|
+
/** The visible worker's chat session id (manual Refresh only) so the client
|
|
289
|
+
* can open it to watch the refresh run. */
|
|
290
|
+
chatId?: string;
|
|
282
291
|
}
|
|
283
292
|
|
|
284
293
|
// Re-run a feed collection's retrieval now. Generic over kind — the
|
|
@@ -296,9 +305,12 @@ router.post(API_ROUTES.collections.refresh, async (req: Request<{ slug: string }
|
|
|
296
305
|
return;
|
|
297
306
|
}
|
|
298
307
|
try {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
308
|
+
// Manual Refresh button → run a VISIBLE worker (hidden:false) so the user
|
|
309
|
+
// can open the session and watch/debug it. Scheduled refreshes (the
|
|
310
|
+
// `refreshDue` loop) stay hidden. Declarative feeds ignore the flag.
|
|
311
|
+
const result = await refreshOne(workspacePath, collection, { hidden: false });
|
|
312
|
+
log.info("collections", "feed refreshed via collection route", { slug: collection.slug, written: result.written, dispatched: result.dispatched ?? false });
|
|
313
|
+
res.json({ refreshed: true, written: result.written, errors: result.errors, dispatched: result.dispatched, chatId: result.chatId });
|
|
302
314
|
} catch (err) {
|
|
303
315
|
log.warn("collections", "feed refresh failed", { slug: collection.slug, error: errorMessage(err) });
|
|
304
316
|
serverError(res, errorMessage(err));
|
|
@@ -340,7 +352,7 @@ router.post(API_ROUTES.collections.itemAction, async (req: Request<{ slug: strin
|
|
|
340
352
|
return;
|
|
341
353
|
}
|
|
342
354
|
log.info("collections", "action seed built", { slug: collection.slug, itemId: req.params.itemId, actionId: action.id });
|
|
343
|
-
res.json({ prompt: buildActionSeedPrompt(record, template), role: action.role });
|
|
355
|
+
res.json({ prompt: buildActionSeedPrompt(record, template, promptPathsFor(collection, workspacePath)), role: action.role });
|
|
344
356
|
} catch (err) {
|
|
345
357
|
log.warn("collections", "action seed failed", {
|
|
346
358
|
slug: collection.slug,
|
|
@@ -361,7 +373,7 @@ async function buildCollectionActionSeed(collection: LoadedCollection, action: C
|
|
|
361
373
|
if (template === null) return null;
|
|
362
374
|
const items = await listItems(collection.dataDir);
|
|
363
375
|
log.info("collections", "collection action seed built", { slug: collection.slug, actionId: action.id, items: items.length });
|
|
364
|
-
return { prompt: buildCollectionActionSeedPrompt(items, collection.schema, template), role: action.role };
|
|
376
|
+
return { prompt: buildCollectionActionSeedPrompt(items, collection.schema, template, promptPathsFor(collection, workspacePath)), role: action.role };
|
|
365
377
|
}
|
|
366
378
|
|
|
367
379
|
// Like the per-record route but with no `itemId`: there is no record to read or
|
|
@@ -471,6 +483,47 @@ router.get(API_ROUTES.collections.viewFile, async (req: Request<{ slug: string }
|
|
|
471
483
|
}
|
|
472
484
|
});
|
|
473
485
|
|
|
486
|
+
// Translation dict for ONE custom view, locale-filtered server-side. The
|
|
487
|
+
// client passes its active app locale; the host returns only that locale's
|
|
488
|
+
// strings (fallback `"en"`, then `{}`). The view never sees other locales'
|
|
489
|
+
// strings — the host is the picker, the iframe is the consumer. Empty dict
|
|
490
|
+
// + `locale: ""` when the view has no `i18n` declared or the file is
|
|
491
|
+
// absent / malformed; the iframe-side `__MC_VIEW.t(key)` falls back to the
|
|
492
|
+
// key, so an i18n-less view keeps working unchanged.
|
|
493
|
+
router.get(API_ROUTES.collections.viewI18n, async (req: Request<{ slug: string }>, res: Response) => {
|
|
494
|
+
try {
|
|
495
|
+
const { slug } = req.params;
|
|
496
|
+
const viewId = typeof req.query.id === "string" ? req.query.id : "";
|
|
497
|
+
const locale = typeof req.query.locale === "string" ? req.query.locale : "";
|
|
498
|
+
const collection = await loadCollection(slug);
|
|
499
|
+
if (!collection) {
|
|
500
|
+
notFound(res, `collection '${slug}' not found`);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
const view = (collection.schema.views ?? []).find((entry) => entry.id === viewId);
|
|
504
|
+
if (!view) {
|
|
505
|
+
notFound(res, `custom view '${viewId}' not found on collection '${slug}'`);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if (!view.i18n) {
|
|
509
|
+
// The view declared no translation file — return the empty contract so
|
|
510
|
+
// the client doesn't have to special-case "no i18n" with a different
|
|
511
|
+
// shape. `t(key)` will just echo the key.
|
|
512
|
+
res.json({ locale: "", dict: {} });
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const result = await readCustomViewI18n(collection, view.i18n, locale);
|
|
516
|
+
res.json(result);
|
|
517
|
+
} catch (err) {
|
|
518
|
+
// Strip CR/LF before logging — `loadCollection` already rejects malformed
|
|
519
|
+
// slugs above (so this path always has a safe slug in practice), but
|
|
520
|
+
// belt-and-suspenders for log-injection / forged-line resistance per
|
|
521
|
+
// CodeRabbit review on #1842.
|
|
522
|
+
log.warn("collections", "view-i18n read failed", { slug: req.params.slug.replace(/[\r\n]/g, " "), error: errorMessage(err) });
|
|
523
|
+
serverError(res, errorMessage(err));
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
|
|
474
527
|
// Mint a scoped token for a custom view. Behind the global bearer (only the
|
|
475
528
|
// real frontend can mint); clamps requested caps to what the view declared
|
|
476
529
|
// so a `read`-only view can never obtain a `write` token.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Read endpoints for the curated collection registries (Discover tab). Backs
|
|
2
|
+
// `GET /api/collections-registry` by server-fetching every configured registry's
|
|
3
|
+
// published index.json (official receptron/mulmoclaude-collections plus any
|
|
4
|
+
// user-added entries from `config/collections-registries.json`) and returning
|
|
5
|
+
// merged entries. The host never exposes upstream URLs to the client; it proxies
|
|
6
|
+
// + caches each one.
|
|
7
|
+
//
|
|
8
|
+
// The registry import/export engine lives in @mulmoclaude/core/collection/registry,
|
|
9
|
+
// wired to this workspace through the shared `configureCollectionHost` binding (see
|
|
10
|
+
// server/workspace/collections/configure.ts). This route is thin host glue.
|
|
11
|
+
|
|
12
|
+
import { Router, Request, Response } from "express";
|
|
13
|
+
|
|
14
|
+
import type { RegistryEntry, RegistryListResponse, RegistryImportResponse } from "@mulmoclaude/core/collection/registry";
|
|
15
|
+
import { listRegistry, previewCollection, importRegistry, performExport } from "@mulmoclaude/core/collection/registry/server";
|
|
16
|
+
|
|
17
|
+
import { API_ROUTES } from "../../../src/config/apiRoutes.js";
|
|
18
|
+
import { badRequest } from "../../utils/httpError.js";
|
|
19
|
+
import { workspacePath } from "../../workspace/workspace.js";
|
|
20
|
+
|
|
21
|
+
const router = Router();
|
|
22
|
+
|
|
23
|
+
interface ErrorResponse {
|
|
24
|
+
error: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
router.get(API_ROUTES.collectionsRegistry.list, async (_req: Request, res: Response<RegistryListResponse | ErrorResponse>) => {
|
|
28
|
+
res.json(await listRegistry());
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
interface RegistryPreviewResponse {
|
|
32
|
+
entry: RegistryEntry;
|
|
33
|
+
schema: Record<string, unknown>;
|
|
34
|
+
meta: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
router.get(API_ROUTES.collectionsRegistry.preview, async (req: Request, res: Response<RegistryPreviewResponse | ErrorResponse>) => {
|
|
38
|
+
const author = typeof req.query.author === "string" ? req.query.author : "";
|
|
39
|
+
const slug = typeof req.query.slug === "string" ? req.query.slug : "";
|
|
40
|
+
const registry = typeof req.query.registry === "string" && req.query.registry ? req.query.registry : null;
|
|
41
|
+
if (!author || !slug) {
|
|
42
|
+
badRequest(res, "author and slug query params are required");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const result = await previewCollection(author, slug, registry);
|
|
46
|
+
if (!result.ok) {
|
|
47
|
+
res.status(result.status).json({ error: result.error });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
res.json({ entry: result.entry, schema: result.schema, meta: result.meta });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
interface ImportBody {
|
|
54
|
+
author?: unknown;
|
|
55
|
+
slug?: unknown;
|
|
56
|
+
registry?: unknown;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
router.post(API_ROUTES.collectionsRegistry.import, async (req: Request<object, unknown, ImportBody>, res: Response<RegistryImportResponse | ErrorResponse>) => {
|
|
60
|
+
const author = typeof req.body.author === "string" ? req.body.author : "";
|
|
61
|
+
const slug = typeof req.body.slug === "string" ? req.body.slug : "";
|
|
62
|
+
const registry = typeof req.body.registry === "string" && req.body.registry ? req.body.registry : null;
|
|
63
|
+
if (!author || !slug) {
|
|
64
|
+
badRequest(res, "author and slug are required");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const result = await importRegistry(author, slug, workspacePath, registry);
|
|
68
|
+
if (!result.ok) {
|
|
69
|
+
res.status(result.status).json({ error: result.error });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
res.json(result.response);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
interface ExportBody {
|
|
76
|
+
slug?: unknown;
|
|
77
|
+
author?: unknown;
|
|
78
|
+
license?: unknown;
|
|
79
|
+
includeSeed?: unknown;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface ExportResponse {
|
|
83
|
+
outputPath: string;
|
|
84
|
+
fileCount: number;
|
|
85
|
+
seedCount: number;
|
|
86
|
+
seedSkipped: number;
|
|
87
|
+
warnings: string[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
router.post(API_ROUTES.collectionsRegistry.export, async (req: Request<object, unknown, ExportBody>, res: Response<ExportResponse | ErrorResponse>) => {
|
|
91
|
+
const slug = typeof req.body.slug === "string" ? req.body.slug : "";
|
|
92
|
+
const author = typeof req.body.author === "string" ? req.body.author : "";
|
|
93
|
+
if (!slug || !author) {
|
|
94
|
+
badRequest(res, "slug and author are required");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const license = typeof req.body.license === "string" && req.body.license ? req.body.license : "MIT";
|
|
98
|
+
const includeSeed = req.body.includeSeed === true;
|
|
99
|
+
const result = await performExport(slug, { author, license, includeSeed }, workspacePath);
|
|
100
|
+
if (!result.ok) {
|
|
101
|
+
res.status(result.status).json({ error: result.error });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
res.json({
|
|
105
|
+
outputPath: result.outputPath,
|
|
106
|
+
fileCount: result.fileCount,
|
|
107
|
+
seedCount: result.seedCount,
|
|
108
|
+
seedSkipped: result.seedSkipped,
|
|
109
|
+
warnings: result.warnings,
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
export default router;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// HTTP route for the dashboard layout (per-tile view mode + tile order
|
|
2
|
+
// for the favorites grid).
|
|
3
|
+
//
|
|
4
|
+
// GET /api/dashboard → { tiles }
|
|
5
|
+
// PUT /api/dashboard → replace the full list → { tiles }
|
|
6
|
+
//
|
|
7
|
+
// The client owns ordering / view mode and sends the whole array; the
|
|
8
|
+
// server normalises (non-empty slug, dedupe on slug, optional string
|
|
9
|
+
// viewMode) before persisting. A single replace-endpoint mirrors the
|
|
10
|
+
// shortcuts route.
|
|
11
|
+
|
|
12
|
+
import { Router, Request, Response } from "express";
|
|
13
|
+
import { API_ROUTES } from "../../../src/config/apiRoutes.js";
|
|
14
|
+
import type { DashboardFile } from "../../../src/types/dashboard.js";
|
|
15
|
+
import { readDashboard, writeDashboard } from "../../utils/files/dashboard-io.js";
|
|
16
|
+
import { errorMessage } from "../../utils/errors.js";
|
|
17
|
+
import { badRequest, serverError } from "../../utils/httpError.js";
|
|
18
|
+
import { log } from "../../system/logger/index.js";
|
|
19
|
+
|
|
20
|
+
const router = Router();
|
|
21
|
+
|
|
22
|
+
type DashboardResponse = DashboardFile;
|
|
23
|
+
|
|
24
|
+
router.get(API_ROUTES.dashboard, async (_req: Request, res: Response<DashboardResponse>) => {
|
|
25
|
+
try {
|
|
26
|
+
res.json(await readDashboard());
|
|
27
|
+
} catch (err) {
|
|
28
|
+
log.warn("dashboard", "read failed", { error: errorMessage(err) });
|
|
29
|
+
serverError(res, errorMessage(err));
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
router.put(API_ROUTES.dashboard, async (req: Request, res: Response<DashboardResponse>) => {
|
|
34
|
+
const { tiles, rowHeights } = req.body ?? {};
|
|
35
|
+
if (!Array.isArray(tiles)) {
|
|
36
|
+
badRequest(res, "Request body must be { tiles: DashboardTile[], rowHeights?: number[] }");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
res.json(await writeDashboard({ tiles, rowHeights }));
|
|
41
|
+
} catch (err) {
|
|
42
|
+
log.warn("dashboard", "write failed", { error: errorMessage(err) });
|
|
43
|
+
serverError(res, errorMessage(err));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export default router;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { Router, Request, Response } from "express";
|
|
10
10
|
import { workspacePath } from "../../workspace/workspace.js";
|
|
11
11
|
import { API_ROUTES } from "../../../src/config/apiRoutes.js";
|
|
12
|
-
import { listFeeds, readFeedState, removeFeed } from "
|
|
12
|
+
import { listFeeds, readFeedState, removeFeed } from "@mulmoclaude/core/feeds/server";
|
|
13
13
|
import { errorMessage } from "../../utils/errors.js";
|
|
14
14
|
import { serverError } from "../../utils/httpError.js";
|
|
15
15
|
import { log } from "../../system/logger/index.js";
|
|
@@ -33,7 +33,7 @@ router.get(API_ROUTES.feeds.list, async (_req: Request, res: Response<FeedsListR
|
|
|
33
33
|
const feeds = await listFeeds(workspacePath);
|
|
34
34
|
const summaries: FeedSummary[] = [];
|
|
35
35
|
for (const feed of feeds) {
|
|
36
|
-
const state = await readFeedState(workspacePath, feed
|
|
36
|
+
const state = await readFeedState(workspacePath, feed);
|
|
37
37
|
const { ingest } = feed.schema;
|
|
38
38
|
summaries.push({
|
|
39
39
|
slug: feed.slug,
|
|
@@ -753,8 +753,7 @@ router.get(API_ROUTES.files.content, (req: Request<object, unknown, unknown, Pat
|
|
|
753
753
|
});
|
|
754
754
|
|
|
755
755
|
type PutContentValidation =
|
|
756
|
-
|
|
757
|
-
| { ok: false; logMsg: string; logExtra?: Record<string, unknown>; message: string };
|
|
756
|
+
{ ok: true; relPath: string; content: string; bytes: number } | { ok: false; logMsg: string; logExtra?: Record<string, unknown>; message: string };
|
|
758
757
|
|
|
759
758
|
// Runtime-shape gate for PUT /api/files/content's body. Returns either
|
|
760
759
|
// the narrowed inputs + their byte length (computed once and reused
|
package/server/api/routes/pdf.ts
CHANGED
|
@@ -19,7 +19,9 @@ const router = Router();
|
|
|
19
19
|
|
|
20
20
|
const MARKDOWN_CSS = `
|
|
21
21
|
body {
|
|
22
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
|
|
22
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
|
|
23
|
+
"Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic", Meiryo,
|
|
24
|
+
"Noto Sans CJK JP", "Noto Sans JP", sans-serif;
|
|
23
25
|
font-size: 13px;
|
|
24
26
|
line-height: 1.6;
|
|
25
27
|
color: #1f2937;
|
|
@@ -246,7 +248,7 @@ async function renderPdf(fullHtml: string, format: "Letter" | "A4" = "Letter"):
|
|
|
246
248
|
}
|
|
247
249
|
}
|
|
248
250
|
|
|
249
|
-
async function
|
|
251
|
+
async function renderMarpHtml(markdown: string, baseDir?: string): Promise<{ fullHtml: string; slideWidth: number; slideHeight: number }> {
|
|
250
252
|
// Shared render core (@mulmoclaude/markdown-plugin) — the MarpView
|
|
251
253
|
// preview and every host's PDF export use the same Marp config + theme
|
|
252
254
|
// registration + custom-size bridging, so they can't drift. Twemoji
|
|
@@ -273,7 +275,28 @@ div.marpit > svg > foreignObject > section img:not([data-marp-twemoji]) {
|
|
|
273
275
|
max-height: 60cqh;
|
|
274
276
|
object-fit: contain;
|
|
275
277
|
}
|
|
278
|
+
/* CJK font fallback for headless-Chromium PDF render (#1821). The Marp
|
|
279
|
+
default theme's font-family is Latin-only, so puppeteer renders
|
|
280
|
+
Japanese / Chinese / Korean glyphs as tofu on hosts without a CJK
|
|
281
|
+
font. Append a CJK stack on the section root — macOS / Windows hit
|
|
282
|
+
Hiragino / Yu Gothic / Meiryo, Linux hits Noto Sans CJK (must be
|
|
283
|
+
installed on the host). Scoped to the section selector only (no
|
|
284
|
+
descendant combinator) so the cascade still lets the theme's
|
|
285
|
+
per-element fonts win — code and pre keep their monospace stack
|
|
286
|
+
and are not silently replaced with this sans-serif chain. Code
|
|
287
|
+
blocks containing CJK still fall through to the OS's monospace
|
|
288
|
+
CJK substitution (e.g. macOS Osaka / Linux Noto Sans Mono CJK). */
|
|
289
|
+
div.marpit > svg > foreignObject > section {
|
|
290
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
|
|
291
|
+
"Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic", Meiryo,
|
|
292
|
+
"Noto Sans CJK JP", "Noto Sans JP", sans-serif;
|
|
293
|
+
}
|
|
276
294
|
</style></head><body>${inlinedHtml}</body></html>`;
|
|
295
|
+
return { fullHtml, slideWidth, slideHeight };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async function renderMarpPdf(markdown: string, baseDir?: string): Promise<Buffer> {
|
|
299
|
+
const { fullHtml, slideWidth, slideHeight } = await renderMarpHtml(markdown, baseDir);
|
|
277
300
|
const browser = await puppeteer.launch({ headless: true });
|
|
278
301
|
try {
|
|
279
302
|
const page = await browser.newPage();
|
|
@@ -302,18 +325,34 @@ export interface RenderMarkdownPdfOptions {
|
|
|
302
325
|
stripFrontmatter?: boolean;
|
|
303
326
|
}
|
|
304
327
|
|
|
328
|
+
export interface RenderMarkdownHtmlOptions {
|
|
329
|
+
markdown: string;
|
|
330
|
+
/** Render via Marp (slide deck) instead of the `marked` pipeline. */
|
|
331
|
+
marp?: boolean;
|
|
332
|
+
/** Workspace-relative source dir for resolving relative `<img>` refs. */
|
|
333
|
+
baseDir?: string;
|
|
334
|
+
stripFrontmatter?: boolean;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Markdown (or a Marp deck) → a self-contained HTML string: CSS inlined
|
|
338
|
+
* and local images embedded as data URIs. Shared by the PDF render
|
|
339
|
+
* (`renderMarkdownPdf`) and the zip share (`packMarkdownZip`) so the two
|
|
340
|
+
* can never drift. */
|
|
341
|
+
export async function renderMarkdownHtml(options: RenderMarkdownHtmlOptions): Promise<string> {
|
|
342
|
+
const { markdown, marp = false, baseDir, stripFrontmatter = false } = options;
|
|
343
|
+
if (marp) return (await renderMarpHtml(markdown, baseDir)).fullHtml;
|
|
344
|
+
const source = stripFrontmatter ? parseFrontmatter(markdown).body : markdown;
|
|
345
|
+
return wrapHtml(inlineImages(await marked.parse(source), { sourceDir: baseDir }), MARKDOWN_CSS);
|
|
346
|
+
}
|
|
347
|
+
|
|
305
348
|
/** Render markdown (or a Marp deck) to a PDF buffer. The single code
|
|
306
349
|
* path behind both `POST /api/pdf/markdown` and the markdown plugin's
|
|
307
350
|
* `exportPdf` host capability (`server/plugins/markdown-builtin.ts`),
|
|
308
351
|
* so the HTTP route and the plugin dispatch can never drift. */
|
|
309
352
|
export async function renderMarkdownPdf(options: RenderMarkdownPdfOptions): Promise<Buffer> {
|
|
310
|
-
const { markdown, marp = false, baseDir, format = "Letter"
|
|
311
|
-
if (marp)
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
const source = stripFrontmatter ? parseFrontmatter(markdown).body : markdown;
|
|
315
|
-
const html = inlineImages(await marked.parse(source), { sourceDir: baseDir });
|
|
316
|
-
return renderPdf(wrapHtml(html, MARKDOWN_CSS), format);
|
|
353
|
+
const { markdown, marp = false, baseDir, format = "Letter" } = options;
|
|
354
|
+
if (marp) return renderMarpPdf(markdown, baseDir);
|
|
355
|
+
return renderPdf(await renderMarkdownHtml(options), format);
|
|
317
356
|
}
|
|
318
357
|
|
|
319
358
|
function sendPdf(res: Response, buffer: Buffer, filename: string): void {
|
|
@@ -6,7 +6,7 @@ import { executeForm } from "../../../src/plugins/presentForm/plugin.js";
|
|
|
6
6
|
import { executePresentCollection } from "../../../src/plugins/presentCollection/plugin.js";
|
|
7
7
|
import type { PresentCollectionArgs } from "../../../src/plugins/presentCollection/types.js";
|
|
8
8
|
import { loadCollection, validateCollectionRecords } from "../../workspace/collections/index.js";
|
|
9
|
-
import { defangForPrompt } from "@mulmoclaude/collection
|
|
9
|
+
import { defangForPrompt } from "@mulmoclaude/core/collection";
|
|
10
10
|
import { executeOpenCanvas } from "../../../src/plugins/canvas/definition.js";
|
|
11
11
|
import { executePresent3D } from "@gui-chat-plugin/present3d";
|
|
12
12
|
import { executeMapControl } from "@gui-chat-plugin/google-map";
|
|
@@ -259,7 +259,7 @@ bindRoute(
|
|
|
259
259
|
// the LLM reads) so the model — which is told to call presentCollection
|
|
260
260
|
// after every write — fixes the file instead of losing the record.
|
|
261
261
|
// `defangForPrompt` (shared with the client Repair button via
|
|
262
|
-
// `@mulmoclaude/collection
|
|
262
|
+
// `@mulmoclaude/core/collection`) strips markup / escape sequences, collapses
|
|
263
263
|
// whitespace, and clips — so record-controlled text in a validation issue (a
|
|
264
264
|
// filename, id, or enum value) can't be read as instructions once appended to
|
|
265
265
|
// the LLM-facing result.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// HTTP routes for the remote-host runner (phase 1).
|
|
2
|
+
//
|
|
3
|
+
// POST /api/remote-host/connect { idToken } → { status }
|
|
4
|
+
// POST /api/remote-host/disconnect → { status }
|
|
5
|
+
// GET /api/remote-host/status → { status }
|
|
6
|
+
//
|
|
7
|
+
// connect signs in to Firebase as the user with the browser-minted Google ID
|
|
8
|
+
// token and starts the host runner (command loop + presence heartbeat);
|
|
9
|
+
// disconnect stops it and signs out. Bearer-guarded like every other /api route
|
|
10
|
+
// (the browser's apiPost attaches the token); the idToken is a secret carried
|
|
11
|
+
// in the POST body over the loopback listener only — never logged.
|
|
12
|
+
import { Router, Request, Response } from "express";
|
|
13
|
+
|
|
14
|
+
import { API_ROUTES } from "../../../src/config/apiRoutes.js";
|
|
15
|
+
import { connect, disconnect, status, type RemoteHostStatus } from "../../remoteHost/index.js";
|
|
16
|
+
import { errorMessage } from "../../utils/errors.js";
|
|
17
|
+
import { badRequest, serverError } from "../../utils/httpError.js";
|
|
18
|
+
import { log } from "../../system/logger/index.js";
|
|
19
|
+
|
|
20
|
+
const PREFIX = "remote-host";
|
|
21
|
+
const router = Router();
|
|
22
|
+
|
|
23
|
+
interface StatusResponse {
|
|
24
|
+
status: RemoteHostStatus;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
router.post(API_ROUTES.remoteHost.connect, async (req: Request, res: Response<StatusResponse>) => {
|
|
28
|
+
const idToken = typeof req.body?.idToken === "string" ? req.body.idToken.trim() : "";
|
|
29
|
+
if (!idToken) {
|
|
30
|
+
badRequest(res, "Request body must be { idToken: string }");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
res.json({ status: await connect(idToken) });
|
|
35
|
+
} catch (err) {
|
|
36
|
+
// Never include idToken in the message.
|
|
37
|
+
log.warn(PREFIX, "connect failed", { error: errorMessage(err) });
|
|
38
|
+
serverError(res, errorMessage(err, "remote-host connect failed"));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
router.post(API_ROUTES.remoteHost.disconnect, async (_req: Request, res: Response<StatusResponse>) => {
|
|
43
|
+
try {
|
|
44
|
+
res.json({ status: await disconnect() });
|
|
45
|
+
} catch (err) {
|
|
46
|
+
log.warn(PREFIX, "disconnect failed", { error: errorMessage(err) });
|
|
47
|
+
serverError(res, errorMessage(err, "remote-host disconnect failed"));
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
router.get(API_ROUTES.remoteHost.status, (_req: Request, res: Response<StatusResponse>) => {
|
|
52
|
+
res.json({ status: status() });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export default router;
|