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.
Files changed (458) hide show
  1. package/README.md +95 -38
  2. package/bin/mulmoclaude.js +1 -2
  3. package/client/assets/JsonEditor-CPJfn76E.css +1 -0
  4. package/client/assets/JsonEditor-CoWGJG3Y.js +10 -0
  5. package/client/assets/abnfDiagram-VRR7QNED-6nNByj6v-By7GJ3xW.js +1 -0
  6. package/client/assets/abnfDiagram-VRR7QNED-BrQlVixL.js +1 -0
  7. package/client/assets/arc-BXutyUAX.js +1 -0
  8. package/client/assets/arc-zT0wd74V-DulGJNnb.js +1 -0
  9. package/client/assets/architecture-TIHT7OUA-CYMWc3UT-C7HoFzWN.js +1 -0
  10. package/client/assets/architecture-TIHT7OUA-aml8u-G9.js +1 -0
  11. package/client/assets/architectureDiagram-ZJ3FMSHR-Bgnyaj_i-CCpEsCHN.js +36 -0
  12. package/client/assets/architectureDiagram-ZJ3FMSHR-DJTFpPjB.js +36 -0
  13. package/client/assets/array-BifhSqXX.js +1 -0
  14. package/client/assets/array-CApNbSU9-BifhSqXX.js +1 -0
  15. package/client/assets/blockDiagram-677ZJIJ3-DQ35o5E4-BXS11D3J.js +132 -0
  16. package/client/assets/blockDiagram-677ZJIJ3-aYvCODxv.js +132 -0
  17. package/client/assets/c4Diagram-LMCZKHZV-Bau6RMBb.js +10 -0
  18. package/client/assets/c4Diagram-LMCZKHZV-ClWZeiWo-DXGnOOUf.js +10 -0
  19. package/client/assets/channel-D9VSi_QV.js +1 -0
  20. package/client/assets/channel-Di5rtkx0-C3gZcytF.js +1 -0
  21. package/client/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  22. package/client/assets/chunk-2Q5K7J3B-DfKpwyl9-DTxwf5E9.js +1 -0
  23. package/client/assets/chunk-32BRIVSS-CCt9wtYd-Cwa_IP35.js +1 -0
  24. package/client/assets/chunk-32BRIVSS-CxzHeys5.js +1 -0
  25. package/client/assets/chunk-52WLFC77-BtNjbTdw.js +10 -0
  26. package/client/assets/chunk-52WLFC77-FbBbR4uI-wsNC5d-f.js +10 -0
  27. package/client/assets/chunk-5VM5RSS4-BYnnUiN6-C4rOnu9A.js +15 -0
  28. package/client/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  29. package/client/assets/chunk-7BUUIJ7U-Bb538aSH.js +1 -0
  30. package/client/assets/chunk-7BUUIJ7U-hh8aCuGX-mGEpPzQy.js +1 -0
  31. package/client/assets/chunk-C7G6YPKG-BE8ehsnc.js +1 -0
  32. package/client/assets/chunk-C7G6YPKG-C87hlS9c-BitbJpTa.js +1 -0
  33. package/client/assets/{chunk-D8eiyYIV-BY16KEZc.js → chunk-D8eiyYIV-BeeEsA1B.js} +1 -1
  34. package/client/assets/chunk-EX3LRPZG-Bhgskzyy.js +231 -0
  35. package/client/assets/chunk-EX3LRPZG-BqGqMXLN-Cl2Eo1_u.js +231 -0
  36. package/client/assets/chunk-FWX5IMBZ-BkbSAAuW-C2r4AuAO.js +2 -0
  37. package/client/assets/chunk-FWX5IMBZ-D9sskZb_.js +2 -0
  38. package/client/assets/chunk-HOUHSVGY-CBCXk7rb.js +1 -0
  39. package/client/assets/chunk-HOUHSVGY-Cxu0eDlh-owg_DcY0.js +1 -0
  40. package/client/assets/chunk-ICXQ74PX-DTqPpDQ9.js +2 -0
  41. package/client/assets/chunk-ICXQ74PX-hiraF_Xj-CdaEwVin.js +2 -0
  42. package/client/assets/chunk-JWPE2WC7-BQ3zXr2k-DSd3Ct95.js +1 -0
  43. package/client/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  44. package/client/assets/chunk-KEIR6QF5-DNzq6p3w.js +161 -0
  45. package/client/assets/chunk-MOJQB5TN-CqxshQHA-Cn_EpDyZ.js +88 -0
  46. package/client/assets/chunk-MOJQB5TN-D1s_zUGz.js +88 -0
  47. package/client/assets/chunk-OGEWGWER-CjCr7ceX-Dfk5-70y.js +1 -0
  48. package/client/assets/chunk-OGEWGWER-DzfhxoQd.js +1 -0
  49. package/client/assets/chunk-PUDLZKDR-BZlebNhn.js +156 -0
  50. package/client/assets/chunk-PUDLZKDR-Dx6M-vz1-CLyVurOz.js +156 -0
  51. package/client/assets/chunk-Q4XR5HBZ-BcJb7mwA.js +70 -0
  52. package/client/assets/chunk-Q4XR5HBZ-CZd-9lTB-qPEYoRNc.js +11 -0
  53. package/client/assets/chunk-RYQCIY6F-B_IgMG7T.js +1 -0
  54. package/client/assets/chunk-RYQCIY6F-YkbemkCt-CX4fncTQ.js +1 -0
  55. package/client/assets/chunk-V7JOEXUC-Buce04o6.js +206 -0
  56. package/client/assets/chunk-V7JOEXUC-rI0xlC_O-C607vJXy.js +206 -0
  57. package/client/assets/chunk-VAUOI2AC-CxhkzXN0.js +1 -0
  58. package/client/assets/chunk-VAUOI2AC-DrcykVNK-CSROJWdE.js +1 -0
  59. package/client/assets/chunk-VR4S4FIN-D5afNrsS.js +1 -0
  60. package/client/assets/chunk-VR4S4FIN-O6iF8Yvf-CLIwrgqU.js +1 -0
  61. package/client/assets/chunk-WYO6CB5R-B83L_z6I-Cs8mg9If.js +127 -0
  62. package/client/assets/chunk-WYO6CB5R-BXTIdTMw.js +125 -0
  63. package/client/assets/chunk-XXDRQBXY-DFXH_lWn-B5HjCp2c.js +1 -0
  64. package/client/assets/chunk-XXDRQBXY-DXMD6ofA.js +1 -0
  65. package/client/assets/chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js +1 -0
  66. package/client/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  67. package/client/assets/chunk-ZGVPDNZ5-BpFv9JSP-CHYySMjV.js +62 -0
  68. package/client/assets/chunk-ZGVPDNZ5-CYusI0J_.js +62 -0
  69. package/client/assets/chunk-ZIRB5QZD-Biy6ZNFG-D1maAiXb.js +32 -0
  70. package/client/assets/chunk-ZIRB5QZD-C6fEPe3t.js +32 -0
  71. package/client/assets/classDiagram-OUVF2IWQ-BgAZMSbT-BgxEE1D3.js +1 -0
  72. package/client/assets/classDiagram-OUVF2IWQ-CBD3Eidl.js +1 -0
  73. package/client/assets/classDiagram-v2-EOCWNBFH-CBD3Eidl.js +1 -0
  74. package/client/assets/classDiagram-v2-EOCWNBFH-DxHTyui1-BgxEE1D3.js +1 -0
  75. package/client/assets/cose-bilkent-JH36ORCC-CuSL2tA8-aMIqISfj.js +1 -0
  76. package/client/assets/cose-bilkent-JH36ORCC-WAJDBXv1.js +1 -0
  77. package/client/assets/cynefin-VYW2F7L2-DH2qkJKw.js +1 -0
  78. package/client/assets/cynefin-VYW2F7L2-DqA3n9nY-BNWhIK_n.js +1 -0
  79. package/client/assets/cynefinDiagram-TSTJHNR4-CUvPV9MV.js +62 -0
  80. package/client/assets/cynefinDiagram-TSTJHNR4-x0-0sQ15-Cq1gFcFu.js +62 -0
  81. package/client/assets/cytoscape.esm-CzdwbRaj-Djp6vQyU.js +321 -0
  82. package/client/assets/cytoscape.esm-Djp6vQyU.js +321 -0
  83. package/client/assets/dagre-CXRCoUWR.js +1 -0
  84. package/client/assets/dagre-VKFMJZFB-CQdfl-bx-CzZZuace.js +4 -0
  85. package/client/assets/dagre-VKFMJZFB-C_9IS7mB.js +4 -0
  86. package/client/assets/dagre-wczQIDso-CfevN9RO.js +1 -0
  87. package/client/assets/defaultLocale-C8Fc0cco.js +1 -0
  88. package/client/assets/defaultLocale-DUNguUWs-BbDo_yJX.js +1 -0
  89. package/client/assets/diagram-FQU43EPY-BOSB6VUb-isPdQ8WX.js +3 -0
  90. package/client/assets/diagram-FQU43EPY-BP8N00-b.js +3 -0
  91. package/client/assets/diagram-G47NLZAW-DLXrcXsN-BIqj7RKy.js +24 -0
  92. package/client/assets/diagram-G47NLZAW-ulE1JlWG.js +24 -0
  93. package/client/assets/diagram-NH7WQ7WH-BMQp1rkF-D6-fOq_v.js +24 -0
  94. package/client/assets/diagram-NH7WQ7WH-D_IXrL3i.js +24 -0
  95. package/client/assets/diagram-OA4YK3LP-C-pC6Eyu.js +30 -0
  96. package/client/assets/diagram-OA4YK3LP-D1wQ0vUj-BNi6QLCK.js +30 -0
  97. package/client/assets/diagram-WEI45ONY-Di9m35i-.js +41 -0
  98. package/client/assets/diagram-WEI45ONY-RR0DpF8R-BS3nrb3w.js +41 -0
  99. package/client/assets/dist-BQBs5pjy-BmtZ7Oc2.js +1 -0
  100. package/client/assets/dist-CQ3HaWOk.js +1 -0
  101. package/client/assets/ebnfDiagram-CCIWWBDH-Cbwnim2x.js +1 -0
  102. package/client/assets/ebnfDiagram-CCIWWBDH-M123uVJ8-9-dq55nQ.js +1 -0
  103. package/client/assets/erDiagram-Q63AITRT-BWx_-PXG-CWGfG4z5.js +85 -0
  104. package/client/assets/erDiagram-Q63AITRT-CMbtO3Sm.js +85 -0
  105. package/client/assets/eventmodeling-45OFAUF4--32SIpkL.js +1 -0
  106. package/client/assets/eventmodeling-45OFAUF4-_BVSjAXf-DQdL0Icr.js +1 -0
  107. package/client/assets/flowDiagram-23GEKE2U-BeOc_anm-CuVzKmmU.js +1 -0
  108. package/client/assets/flowDiagram-23GEKE2U-Dofa_qxG.js +1 -0
  109. package/client/assets/ganttDiagram-NO4QXBWP-BOoJ1eTw-TWoNmSvq.js +292 -0
  110. package/client/assets/ganttDiagram-NO4QXBWP-BgoAVKuc.js +292 -0
  111. package/client/assets/gitGraph-TEB2WS4Q-CH12KLTN-Bz4frAhV.js +1 -0
  112. package/client/assets/gitGraph-TEB2WS4Q-DIMvNvqt.js +1 -0
  113. package/client/assets/gitGraphDiagram-IHSO6WYX-B2CJhk_G-ClOKkjxw.js +106 -0
  114. package/client/assets/gitGraphDiagram-IHSO6WYX-Dmb6KnPz.js +106 -0
  115. package/client/assets/graphlib-B8gBHxth.js +1 -0
  116. package/client/assets/graphlib-hY-1btwe-DQjxxcnr.js +1 -0
  117. package/client/assets/{html2canvas-CDGcmOD3-CKJ6vKPo.js → html2canvas-CDGcmOD3-DRL9pFVl.js} +2 -2
  118. package/client/assets/{index-9lmYSaus.js → index-Dc0R-HW5.js} +129 -190
  119. package/client/assets/index-Dxo1Zdd-.css +2 -0
  120. package/client/assets/{index.es-DqtpmBm8-DFXjJgCa.js → index.es-DqtpmBm8-EQk3NgR8.js} +1 -1
  121. package/client/assets/info-DKCQHKI2-Cbw3mbiK-GsDZz9IO.js +1 -0
  122. package/client/assets/info-DKCQHKI2-ViCqobGo.js +1 -0
  123. package/client/assets/infoDiagram-FWYZ7A6U-HKV7LIG-.js +2 -0
  124. package/client/assets/infoDiagram-FWYZ7A6U-Mp1X3pBP-ATJv87Ur.js +2 -0
  125. package/client/assets/init-D6jRqBbL.js +1 -0
  126. package/client/assets/init-DEsX3bhM-D6jRqBbL.js +1 -0
  127. package/client/assets/ishikawaDiagram-FXEZZL3T-BNG7tkJu-CGCeOIlJ.js +70 -0
  128. package/client/assets/ishikawaDiagram-FXEZZL3T-CVDUj46f.js +70 -0
  129. package/client/assets/journeyDiagram-5HDEW3XC-BA-ESGLP.js +139 -0
  130. package/client/assets/journeyDiagram-5HDEW3XC-Dbp_hY9X-Bx-IoCe9.js +139 -0
  131. package/client/assets/kanban-definition-HUTT4EX6-Bqaet01L.js +89 -0
  132. package/client/assets/kanban-definition-HUTT4EX6-DSTc5u3q-C0JGckt1.js +89 -0
  133. package/client/assets/katex-CddkPoXu.js +257 -0
  134. package/client/assets/katex-M0IxphGf-CddkPoXu.js +257 -0
  135. package/client/assets/lib-4Tgx7rAy.js +114 -0
  136. package/client/assets/line-B1wBwzrY-f5wxpqoF.js +1 -0
  137. package/client/assets/line-CkyHfW7d.js +1 -0
  138. package/client/assets/linear-B9fuEF4c.js +1 -0
  139. package/client/assets/linear-lrGinF5_-CqNBync7.js +1 -0
  140. package/client/assets/map-CgrwEyH7-DWQFomlZ.js +1 -0
  141. package/client/assets/map-DsCK-0Cs.js +1 -0
  142. package/client/assets/marked.esm-CIU5FDdN.js +64 -0
  143. package/client/assets/marp-CSq0PPfK.js +3448 -0
  144. package/client/assets/material-symbols-outlined-DdRFxZLh.woff2 +0 -0
  145. package/client/assets/mermaid-parser.core-DC7NPJ_M-Ca6XzwfM.js +166 -0
  146. package/client/assets/mermaid-parser.core-SAwSf4_o.js +7 -0
  147. package/client/assets/mermaid.core-BBEQfkdJ.js +11 -0
  148. package/client/assets/mermaid.core-DZM3Ha-E-BxOo-RYG.js +11 -0
  149. package/client/assets/mindmap-definition-LN4V7U3C-B4BN0efL.js +96 -0
  150. package/client/assets/mindmap-definition-LN4V7U3C-DYtgcMsY-DynyLTNv.js +96 -0
  151. package/client/assets/ordinal-B9_Llu10-CU85fhaB.js +1 -0
  152. package/client/assets/ordinal-CopWnP7w.js +1 -0
  153. package/client/assets/packet-7NZHBO7P-CY4XRk9N.js +1 -0
  154. package/client/assets/packet-7NZHBO7P-lwb58iYx--vqSg65J.js +1 -0
  155. package/client/assets/path-BWPyau1x.js +1 -0
  156. package/client/assets/path-COivZ1Gw-C5riojK2.js +1 -0
  157. package/client/assets/pegDiagram-2B236MQR-BOKErjUu.js +1 -0
  158. package/client/assets/pegDiagram-2B236MQR-C43eIpKM-Bi_5Sxjt.js +1 -0
  159. package/client/assets/pie-RZYD4A2V-6BHB7bAx.js +1 -0
  160. package/client/assets/pie-RZYD4A2V-B1UWb4Gu-Dmn2hLWH.js +1 -0
  161. package/client/assets/pieDiagram-ENE6RG2P-BkTqgJyR-Bcdm19kR.js +39 -0
  162. package/client/assets/pieDiagram-ENE6RG2P-DtNjvz-U.js +39 -0
  163. package/client/assets/preload-helper-CZgWQFsJ.js +1 -0
  164. package/client/assets/purify.es-DY32g7DN.js +3 -0
  165. package/client/assets/quadrantDiagram-ABIIQ3AL-BQmAJL2v.js +7 -0
  166. package/client/assets/quadrantDiagram-ABIIQ3AL-Bm1Zjm45-B3orqx_f.js +7 -0
  167. package/client/assets/radar-I7S5WNFK-7CKcb_l--CqKdnR0v.js +1 -0
  168. package/client/assets/radar-I7S5WNFK-R7Jhjfdt.js +1 -0
  169. package/client/assets/railroad-3IZDKUUU-BepSPyHI.js +1 -0
  170. package/client/assets/railroad-3IZDKUUU-gCySKdnW-CKU26HTP.js +1 -0
  171. package/client/assets/railroad-abnf-AHOZXSZD-BlsHsp3c.js +1 -0
  172. package/client/assets/railroad-abnf-AHOZXSZD-BophH4r--CGkGHq54.js +1 -0
  173. package/client/assets/railroad-ebnf-EBAXGLYW-AZNjl_Zu-B7bn_PF3.js +1 -0
  174. package/client/assets/railroad-ebnf-EBAXGLYW-B4fMcGEH.js +1 -0
  175. package/client/assets/railroad-peg-LSFZ7HO6-B1ZrjJIu.js +1 -0
  176. package/client/assets/railroad-peg-LSFZ7HO6-BSiEEyeb-CP_B-N5g.js +1 -0
  177. package/client/assets/railroadDiagram-RFXS5EU6-8cz0Iby3.js +1 -0
  178. package/client/assets/railroadDiagram-RFXS5EU6-BkfbdeAs-Di6jH5I6.js +1 -0
  179. package/client/assets/requirementDiagram-TGXJPOKE-CrGTTjYg-DmIC7Shp.js +84 -0
  180. package/client/assets/requirementDiagram-TGXJPOKE-Dlqoe9TY.js +84 -0
  181. package/client/assets/rough.esm-CSKSodPl.js +1 -0
  182. package/client/assets/rough.esm-wVmwlXu7-CSKSodPl.js +1 -0
  183. package/client/assets/runtime-protocol-vue-WG3JLsjz.js +1 -0
  184. package/client/assets/runtime-vue-lHsY5zMj.js +1 -0
  185. package/client/assets/sankeyDiagram-HTMAVEWB-BkTKgu8Q.js +40 -0
  186. package/client/assets/sankeyDiagram-HTMAVEWB-rWXPf03Z-CjV9pCSJ.js +40 -0
  187. package/client/assets/{schemas-D_RbFtuQ.js → schemas-DGvl73AE.js} +3 -3
  188. package/client/assets/sequenceDiagram-DBY2YBRQ-BpuXUcFy.js +162 -0
  189. package/client/assets/sequenceDiagram-DBY2YBRQ-nkJYWO2m-BqS_sTCH.js +162 -0
  190. package/client/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +1 -0
  191. package/client/assets/sizeCapture-X5ZJPWSS-F5SadAuT-VHIb4_NU.js +1 -0
  192. package/client/assets/src-Brzfja-q-DNc4Or4z.js +1 -0
  193. package/client/assets/src-Cqa3Jy1j.js +1 -0
  194. package/client/assets/stateDiagram-2N3HPSRC-CdlGXuo7.js +1 -0
  195. package/client/assets/stateDiagram-2N3HPSRC-T4-clK8b-DQRiB4va.js +1 -0
  196. package/client/assets/stateDiagram-v2-6OUMAXLB-BN8Jr3ZM.js +1 -0
  197. package/client/assets/stateDiagram-v2-6OUMAXLB-DIp7nhRd-DbrOhUn_.js +1 -0
  198. package/client/assets/swimlanes-5IMT3BWC-HmQNEntu-CNBgZdur.js +2 -0
  199. package/client/assets/swimlanes-5IMT3BWC-iEJg5gki.js +2 -0
  200. package/client/assets/swimlanesDiagram-G3AALYLV-BeoZhwg7-DpqgeNRA.js +8 -0
  201. package/client/assets/swimlanesDiagram-G3AALYLV-o-sS6aEP.js +8 -0
  202. package/client/assets/timeline-definition-FHXFAJF6-Bjh-7QmL.js +120 -0
  203. package/client/assets/timeline-definition-FHXFAJF6-CNc9jSTP-BKNSymgY.js +120 -0
  204. package/client/assets/treeView-QDETBFTQ-D3cqRl6k.js +1 -0
  205. package/client/assets/treeView-QDETBFTQ-nIQcG1h9-O_ri9HxD.js +1 -0
  206. package/client/assets/treemap-6X3UGDF4-BnsvC8yL-Cfsty1e3.js +1 -0
  207. package/client/assets/treemap-6X3UGDF4-DGkZcvDG.js +1 -0
  208. package/client/assets/v4-B29ayslu.js +1 -0
  209. package/client/assets/vennDiagram-L72KCM5P-Cz3dbe7b.js +34 -0
  210. package/client/assets/vennDiagram-L72KCM5P-Dr3pTJ_0-C38xwcVo.js +34 -0
  211. package/client/assets/vue-C6d2VveO.js +1 -0
  212. package/client/assets/vue-i18n-CL-ejuNu.js +3 -0
  213. package/client/assets/vue.runtime.esm-bundler-BeoTfMNc.js +4 -0
  214. package/client/assets/wardley-OPB4EBWU-8Odxkx6V-CCDIVuBs.js +1 -0
  215. package/client/assets/wardley-OPB4EBWU-CZsCDvKk.js +1 -0
  216. package/client/assets/wardleyDiagram-EHGQE667-DSFc7ZZa-mlWXRiWx.js +78 -0
  217. package/client/assets/wardleyDiagram-EHGQE667-DiyoyO_X.js +78 -0
  218. package/client/assets/xychartDiagram-FW5EYKEG-BP0Nn4Pp-DRRiStGZ.js +7 -0
  219. package/client/assets/xychartDiagram-FW5EYKEG-DhY1_UGM.js +7 -0
  220. package/client/index.html +16 -12
  221. package/package.json +17 -19
  222. package/server/agent/backend/types.ts +1 -1
  223. package/server/agent/backgroundSessions.ts +43 -0
  224. package/server/agent/config.ts +3 -2
  225. package/server/agent/mcp-tools/manageCollection.ts +4 -4
  226. package/server/agent/sandboxMounts.ts +1 -1
  227. package/server/agent/stream.ts +1 -3
  228. package/server/api/auth/viewToken.ts +1 -1
  229. package/server/api/bridge/sessionRole.ts +47 -0
  230. package/server/api/routes/agent.ts +79 -2
  231. package/server/api/routes/collections.ts +60 -7
  232. package/server/api/routes/collectionsRegistry.ts +113 -0
  233. package/server/api/routes/dashboard.ts +47 -0
  234. package/server/api/routes/feeds.ts +2 -2
  235. package/server/api/routes/files.ts +1 -2
  236. package/server/api/routes/pdf.ts +48 -9
  237. package/server/api/routes/plugins.ts +2 -2
  238. package/server/api/routes/remoteHost.ts +55 -0
  239. package/server/api/routes/sessions.ts +20 -11
  240. package/server/api/routes/share.ts +105 -0
  241. package/server/api/routes/transcribe.ts +1 -1
  242. package/server/api/routes/wiki/history.ts +2 -2
  243. package/server/api/routes/wiki.ts +29 -177
  244. package/server/api/sandboxStatus.ts +1 -1
  245. package/server/build/dispatcher.mjs +25 -14928
  246. package/server/events/collection-change.ts +4 -4
  247. package/server/events/file-change.ts +2 -2
  248. package/server/events/scheduler-adapter.ts +3 -3
  249. package/server/events/task-manager/index.ts +3 -3
  250. package/server/index.ts +53 -17
  251. package/server/notifier/engine.ts +3 -3
  252. package/server/notifier/types.ts +2 -2
  253. package/server/plugins/html-builtin.ts +15 -1
  254. package/server/prompts/system/system.md +4 -0
  255. package/server/remoteHost/auth.ts +29 -0
  256. package/server/remoteHost/commandChannel.ts +56 -0
  257. package/server/remoteHost/firebase.ts +16 -0
  258. package/server/remoteHost/handlers/collectionPage.ts +29 -0
  259. package/server/remoteHost/handlers/getCollection.ts +32 -0
  260. package/server/remoteHost/handlers/getFeed.ts +35 -0
  261. package/server/remoteHost/handlers/index.ts +19 -0
  262. package/server/remoteHost/handlers/listCollections.ts +29 -0
  263. package/server/remoteHost/handlers/listFeeds.ts +37 -0
  264. package/server/remoteHost/handlers/listShortcuts.ts +24 -0
  265. package/server/remoteHost/handlers/startChat.ts +78 -0
  266. package/server/remoteHost/hostRunner.ts +122 -0
  267. package/server/remoteHost/index.ts +117 -0
  268. package/server/system/config.ts +1 -1
  269. package/server/system/credentials.ts +2 -3
  270. package/server/system/docker.ts +9 -8
  271. package/server/system/env.ts +8 -0
  272. package/server/system/whisper/index.ts +4 -4
  273. package/server/utils/claudeConfigPath.ts +57 -0
  274. package/server/utils/files/dashboard-io.ts +87 -0
  275. package/server/utils/files/session-io.ts +11 -0
  276. package/server/utils/markdown/frontmatter.ts +8 -4
  277. package/server/utils/regex.ts +4 -3
  278. package/server/utils/share/packHtml.ts +108 -0
  279. package/server/utils/share/rewriteAssets.ts +178 -0
  280. package/server/workspace/chat-index/paths.ts +8 -2
  281. package/server/workspace/chat-index/summarizer.ts +23 -15
  282. package/server/workspace/collections/configure.ts +5 -3
  283. package/server/workspace/collections/index.ts +8 -6
  284. package/server/workspace/collections/notifications.ts +3 -3
  285. package/server/workspace/collections/types.ts +2 -2
  286. package/server/workspace/collections/watcher.ts +2 -2
  287. package/server/workspace/feeds/configure.ts +24 -0
  288. package/server/workspace/hooks/handlers/skillBridge.ts +2 -2
  289. package/server/workspace/hooks/handlers/wikiSnapshot.ts +1 -1
  290. package/server/workspace/paths.ts +8 -1
  291. package/server/workspace/skills/catalog.ts +1 -3
  292. package/server/workspace/skills/external/catalog.ts +1 -3
  293. package/server/workspace/skills/paths.ts +4 -3
  294. package/server/workspace/skills/writer.ts +1 -4
  295. package/server/workspace/skills-preset.ts +3 -3
  296. package/server/workspace/wiki-pages/io.ts +2 -2
  297. package/server/workspace/workspace.ts +2 -2
  298. package/src/App.vue +41 -18
  299. package/src/components/ChatInput.vue +1 -1
  300. package/src/components/DashboardView.vue +372 -0
  301. package/src/components/FileContentRenderer.vue +32 -7
  302. package/src/components/JsonEditor.vue +1 -1
  303. package/src/components/PluginLauncher.vue +122 -79
  304. package/src/components/RemoteHostControl.vue +159 -0
  305. package/src/components/SessionCountBadges.vue +32 -0
  306. package/src/components/SessionHeaderControls.vue +1 -8
  307. package/src/components/SessionHistoryPanel.vue +2 -0
  308. package/src/components/SessionHistoryToggleButton.vue +1 -15
  309. package/src/components/SidebarHeader.vue +2 -0
  310. package/src/components/collectionTypes.ts +2 -2
  311. package/src/composables/accountingHost.ts +24 -0
  312. package/src/composables/collections/uiHost.ts +17 -2
  313. package/src/composables/useDashboard.ts +191 -0
  314. package/src/composables/useMarkdownZip.ts +51 -0
  315. package/src/composables/useNotifications.ts +1 -4
  316. package/src/composables/useSharePack.ts +60 -0
  317. package/src/composables/useTranslatedQueries.ts +15 -95
  318. package/src/composables/useTranslatedStrings.ts +68 -0
  319. package/src/composables/useVoiceInput.ts +3 -3
  320. package/src/config/apiRoutes.ts +57 -9
  321. package/src/config/createFilePolicy.ts +1 -1
  322. package/src/config/firebase.ts +12 -0
  323. package/src/config/firebaseConfig.ts +18 -0
  324. package/src/config/historyFilters.ts +7 -0
  325. package/src/config/pubsubChannels.ts +7 -27
  326. package/src/config/roles.ts +12 -10
  327. package/src/config/workspacePaths.ts +10 -0
  328. package/src/index.css +1 -1
  329. package/src/lang/de.ts +39 -220
  330. package/src/lang/en.ts +39 -191
  331. package/src/lang/es.ts +39 -218
  332. package/src/lang/fr.ts +39 -220
  333. package/src/lang/ja.ts +39 -216
  334. package/src/lang/ko.ts +39 -216
  335. package/src/lang/pt-BR.ts +39 -217
  336. package/src/lang/zh.ts +39 -215
  337. package/src/main.ts +3 -0
  338. package/src/plugins/accounting/definition.ts +12 -8
  339. package/src/plugins/accounting/index.ts +7 -4
  340. package/src/plugins/accounting/meta.ts +23 -60
  341. package/src/plugins/manageSkills/View.vue +9 -1
  342. package/src/plugins/photoLocations/View.vue +2 -2
  343. package/src/plugins/presentCollection/definition.ts +2 -2
  344. package/src/plugins/presentCollection/plugin.ts +2 -2
  345. package/src/plugins/presentCollection/types.ts +2 -2
  346. package/src/plugins/presentHtml/definition.ts +1 -1
  347. package/src/plugins/presentMulmoScript/View.vue +2 -2
  348. package/src/plugins/presentMulmoScript/helpers.ts +1 -1
  349. package/src/plugins/presentSVG/View.vue +2 -2
  350. package/src/plugins/scheduler/TasksTab.vue +1 -2
  351. package/src/plugins/skill/View.vue +7 -3
  352. package/src/plugins/spreadsheet/View.vue +3 -3
  353. package/src/plugins/spreadsheet/engine/functions/logical.ts +8 -1
  354. package/src/plugins/textResponse/View.vue +43 -16
  355. package/src/plugins/wiki/View.vue +29 -2
  356. package/src/plugins/wiki/components/WikiGraphView.vue +1 -1
  357. package/src/plugins/wiki/components/WikiPageBody.vue +4 -1
  358. package/src/plugins/wiki/helpers.ts +13 -81
  359. package/src/plugins/wiki/index.ts +1 -1
  360. package/src/router/guards.ts +1 -1
  361. package/src/router/index.ts +12 -0
  362. package/src/router/pageRoutes.ts +2 -0
  363. package/src/types/dashboard.ts +40 -0
  364. package/src/types/session.ts +3 -0
  365. package/src/utils/blobDownload.ts +19 -0
  366. package/src/utils/collections/presentSeed.ts +1 -1
  367. package/src/utils/html/customViewSrcdoc.ts +31 -3
  368. package/src/utils/html/previewCsp.ts +1 -1
  369. package/src/utils/id.ts +1 -1
  370. package/src/utils/image/htmlSrcAttrs.ts +1 -1
  371. package/src/utils/markdown/frontmatter.ts +12 -7
  372. package/src/utils/markdown/highlight.ts +22 -0
  373. package/src/utils/markdown/marpCustomSize.ts +2 -2
  374. package/src/utils/markdown/mermaidExtension.ts +56 -0
  375. package/src/utils/markdown/mermaidRender.ts +147 -0
  376. package/src/utils/markdown/setup.ts +17 -4
  377. package/src/utils/markdown/useMermaid.ts +33 -0
  378. package/src/utils/session/longRunning.ts +33 -0
  379. package/src/utils/session/mergeSessions.ts +1 -1
  380. package/client/assets/JsonEditor-Di5xGeZY.css +0 -1
  381. package/client/assets/JsonEditor-o5--tPQH.js +0 -10
  382. package/client/assets/index-tOu5ArRZ.css +0 -2
  383. package/client/assets/lib-D6Xy0IFc.js +0 -114
  384. package/client/assets/marp-D6GXA-EB.js +0 -3452
  385. package/client/assets/material-symbols-outlined-DtIK7AQn.woff2 +0 -0
  386. package/client/assets/runtime-protocol-vue-pU0Mw7Zm.js +0 -1
  387. package/client/assets/runtime-vue-fFYhnNg3.js +0 -1
  388. package/client/assets/vue-UDIWDtr8.js +0 -1
  389. package/client/assets/vue-i18n-CQbxVmNs.js +0 -3
  390. package/client/assets/vue.runtime.esm-bundler-BTyIdNAI.js +0 -4
  391. package/server/accounting/accountNormalize.ts +0 -32
  392. package/server/accounting/defaultAccounts.ts +0 -87
  393. package/server/accounting/eventPublisher.ts +0 -52
  394. package/server/accounting/journal.ts +0 -252
  395. package/server/accounting/openingBalances.ts +0 -114
  396. package/server/accounting/report.ts +0 -237
  397. package/server/accounting/service.ts +0 -718
  398. package/server/accounting/snapshotCache.ts +0 -334
  399. package/server/accounting/timeSeries.ts +0 -265
  400. package/server/accounting/types.ts +0 -148
  401. package/server/api/routes/accounting.ts +0 -373
  402. package/server/api/routes/wiki/frontmatter.ts +0 -34
  403. package/server/api/routes/wiki/pageIndex.ts +0 -53
  404. package/server/system/logs/aaa +0 -737
  405. package/server/system/logs/bb +0 -446
  406. package/server/utils/files/accounting-io.ts +0 -294
  407. package/server/workspace/feeds/engine.ts +0 -143
  408. package/server/workspace/feeds/fetch/httpClient.ts +0 -127
  409. package/server/workspace/feeds/fetch/rssParser.ts +0 -117
  410. package/server/workspace/feeds/index.ts +0 -8
  411. package/server/workspace/feeds/ingestTypes.ts +0 -62
  412. package/server/workspace/feeds/pathResolver.ts +0 -74
  413. package/server/workspace/feeds/paths.ts +0 -30
  414. package/server/workspace/feeds/projectItem.ts +0 -92
  415. package/server/workspace/feeds/registry.ts +0 -43
  416. package/server/workspace/feeds/retrievers/httpJson.ts +0 -19
  417. package/server/workspace/feeds/retrievers/index.ts +0 -27
  418. package/server/workspace/feeds/retrievers/registerAll.ts +0 -5
  419. package/server/workspace/feeds/retrievers/rss.ts +0 -24
  420. package/server/workspace/feeds/state.ts +0 -55
  421. package/src/composables/useAccountingChannel.ts +0 -58
  422. package/src/lib/wiki-page/graph.ts +0 -108
  423. package/src/lib/wiki-page/index-parse.ts +0 -221
  424. package/src/lib/wiki-page/link.ts +0 -62
  425. package/src/lib/wiki-page/lint.ts +0 -105
  426. package/src/lib/wiki-page/paths.ts +0 -35
  427. package/src/lib/wiki-page/slug.ts +0 -54
  428. package/src/plugins/accounting/Preview.vue +0 -103
  429. package/src/plugins/accounting/View.vue +0 -633
  430. package/src/plugins/accounting/actions.ts +0 -34
  431. package/src/plugins/accounting/api.ts +0 -301
  432. package/src/plugins/accounting/components/AccountEditor.vue +0 -250
  433. package/src/plugins/accounting/components/AccountRow.vue +0 -50
  434. package/src/plugins/accounting/components/AccountsList.vue +0 -102
  435. package/src/plugins/accounting/components/AccountsModal.vue +0 -301
  436. package/src/plugins/accounting/components/BalanceSheet.vue +0 -186
  437. package/src/plugins/accounting/components/BookSettings.vue +0 -284
  438. package/src/plugins/accounting/components/BookSwitcher.vue +0 -78
  439. package/src/plugins/accounting/components/DateRangePicker.vue +0 -140
  440. package/src/plugins/accounting/components/JournalEntryForm.vue +0 -505
  441. package/src/plugins/accounting/components/JournalList.vue +0 -554
  442. package/src/plugins/accounting/components/Ledger.vue +0 -206
  443. package/src/plugins/accounting/components/NewBookForm.vue +0 -211
  444. package/src/plugins/accounting/components/OpeningBalancesForm.vue +0 -272
  445. package/src/plugins/accounting/components/ProfitLoss.vue +0 -160
  446. package/src/plugins/accounting/components/accountDraft.ts +0 -13
  447. package/src/plugins/accounting/components/accountNumbering.ts +0 -103
  448. package/src/plugins/accounting/components/accountValidation.ts +0 -75
  449. package/src/plugins/accounting/components/useLatestRequest.ts +0 -44
  450. package/src/plugins/accounting/countries.ts +0 -158
  451. package/src/plugins/accounting/currencies.ts +0 -77
  452. package/src/plugins/accounting/dates.ts +0 -51
  453. package/src/plugins/accounting/fiscalYear.ts +0 -136
  454. package/src/plugins/accounting/timeSeriesEnums.ts +0 -16
  455. package/src/plugins/wiki/route.ts +0 -137
  456. /package/client/assets/{_plugin-vue_export-helper-B67ILkmu.js → _plugin-vue_export-helper-BDNMzG2s.js} +0 -0
  457. /package/client/assets/{purify.es-B27wDFIb-51iYcXuK.js → purify.es-B27wDFIb-Bu4Grnl0.js} +0 -0
  458. /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 { releaseBackgroundSession } from "../../agent/backgroundSessions.js";
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 (failoverAttemptsRemaining > 0 && event.type === EVENT_TYPES.error && typeof event.message === "string" && isStaleSessionError(event.message)) {
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-plugin";
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 "../../workspace/feeds/index.js";
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
- const result = await refreshOne(workspacePath, collection);
300
- log.info("collections", "feed refreshed via collection route", { slug: collection.slug, written: result.written });
301
- res.json({ refreshed: true, written: result.written, errors: result.errors });
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 "../../workspace/feeds/index.js";
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.slug);
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
- | { ok: true; relPath: string; content: string; bytes: number }
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
@@ -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, sans-serif;
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 renderMarpPdf(markdown: string, baseDir?: string): Promise<Buffer> {
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", stripFrontmatter = false } = options;
311
- if (marp) {
312
- return renderMarpPdf(markdown, baseDir);
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-plugin";
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-plugin`) strips markup / escape sequences, collapses
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;