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,334 +0,0 @@
1
- // Monthly balance snapshot cache.
2
- //
3
- // Source of truth: the journal JSONL files. Snapshots are derived
4
- // state — `data/accounting/books/<id>/snapshots/YYYY-MM.json` is
5
- // only ever a perf optimization. The invariant we maintain:
6
- //
7
- // for any (book, period) pair,
8
- // getOrBuildSnapshot(book, period)
9
- // ===
10
- // aggregateBalances(<all entries up to period end>)
11
- //
12
- // I.e. running with snapshots and running without snapshots must
13
- // produce byte-identical results. The unit test for this lives in
14
- // `test/accounting/test_snapshotCache.ts`.
15
- //
16
- // Rebuild policy: writes call `scheduleRebuild(bookId, fromPeriod)`
17
- // after invalidating stale snapshot files. Each book has at most one
18
- // rebuild in flight; additional writes during a running rebuild merge
19
- // into a single queued follow-up (so a burst of N writes runs at most
20
- // two rebuilds). `getOrBuildSnapshot` keeps a lazy fallback so a
21
- // report requested before the rebuild reaches that month is still
22
- // correct — it just builds inline.
23
- //
24
- // Test API: `awaitRebuildIdle(bookId)` and `inspectRebuildQueue(bookId)`
25
- // are diagnostics that let tests assert on queue state without
26
- // sleep-and-poll. Production code never needs them.
27
-
28
- import {
29
- invalidateSnapshotsFrom as ioInvalidateFrom,
30
- invalidateAllSnapshots as ioInvalidateAll,
31
- listJournalPeriods,
32
- readJournalMonth,
33
- readSnapshot,
34
- writeSnapshot,
35
- } from "../utils/files/accounting-io.js";
36
- import { aggregateBalances } from "./report.js";
37
- import { publishBookChange } from "./eventPublisher.js";
38
- import { log } from "../system/logger/index.js";
39
- import { errorMessage } from "../utils/errors.js";
40
- import { ACCOUNTING_BOOK_EVENT_KINDS } from "../../src/config/pubsubChannels.js";
41
- import type { AccountBalance, JournalEntry, MonthSnapshot } from "./types.js";
42
-
43
- function previousPeriod(period: string): string {
44
- // YYYY-MM → previous YYYY-MM. December rolls back to the previous
45
- // year's December.
46
- const [year, month] = period.split("-").map((segment) => parseInt(segment, 10));
47
- if (month === 1) return `${(year - 1).toString().padStart(4, "0")}-12`;
48
- return `${year.toString().padStart(4, "0")}-${(month - 1).toString().padStart(2, "0")}`;
49
- }
50
-
51
- function mergeBalances(base: readonly AccountBalance[], delta: readonly AccountBalance[]): AccountBalance[] {
52
- const map = new Map<string, number>();
53
- for (const row of base) map.set(row.accountCode, row.netDebit);
54
- for (const row of delta) {
55
- map.set(row.accountCode, (map.get(row.accountCode) ?? 0) + row.netDebit);
56
- }
57
- return Array.from(map.entries())
58
- .map(([accountCode, netDebit]) => ({ accountCode, netDebit }))
59
- .sort((lhs, rhs) => lhs.accountCode.localeCompare(rhs.accountCode));
60
- }
61
-
62
- async function buildEmptySnapshot(bookId: string, period: string, workspaceRoot?: string): Promise<MonthSnapshot> {
63
- const empty: MonthSnapshot = { period, balances: [], builtAt: new Date().toISOString() };
64
- await writeSnapshot(bookId, empty, workspaceRoot);
65
- return empty;
66
- }
67
-
68
- /** Build a snapshot at end-of-`period` for one book, lazily relying
69
- * on the previous month's snapshot if it exists. Falls all the way
70
- * back to the earliest journal month if no upstream snapshot is
71
- * available. Always writes the result to disk before returning. */
72
- export async function getOrBuildSnapshot(bookId: string, period: string, workspaceRoot?: string): Promise<MonthSnapshot> {
73
- const cached = await readSnapshot(bookId, period, workspaceRoot);
74
- if (cached) return cached;
75
-
76
- // Earliest journal month determines where the recursion stops.
77
- // If the book has no journal at all, return an empty snapshot.
78
- const periods = await listJournalPeriods(bookId, workspaceRoot);
79
- if (periods.length === 0 || period < periods[0]) {
80
- return buildEmptySnapshot(bookId, period, workspaceRoot);
81
- }
82
-
83
- const { entries } = await readJournalMonth(bookId, period, workspaceRoot);
84
- const monthDelta = aggregateBalances(entries);
85
-
86
- // Get the prior month's closing snapshot — recurse, which will
87
- // either hit cache or build the chain back to the start.
88
- let priorBalances: readonly AccountBalance[] = [];
89
- if (period > periods[0]) {
90
- const prior = previousPeriod(period);
91
- const priorSnap = await getOrBuildSnapshot(bookId, prior, workspaceRoot);
92
- priorBalances = priorSnap.balances;
93
- }
94
- const merged = mergeBalances(priorBalances, monthDelta);
95
- const snap: MonthSnapshot = {
96
- period,
97
- balances: merged,
98
- builtAt: new Date().toISOString(),
99
- };
100
- await writeSnapshot(bookId, snap, workspaceRoot);
101
- return snap;
102
- }
103
-
104
- /** Compute closing balances at end-of-`period` from journal alone,
105
- * bypassing the snapshot cache. Used by the byte-equality
106
- * invariant test, and as a safety net for "compute without
107
- * trusting cache" paths. */
108
- export async function balancesAtEndOf(bookId: string, period: string, workspaceRoot?: string): Promise<AccountBalance[]> {
109
- const periods = await listJournalPeriods(bookId, workspaceRoot);
110
- const all: JournalEntry[] = [];
111
- for (const monthKey of periods) {
112
- if (period < monthKey) break;
113
- const { entries } = await readJournalMonth(bookId, monthKey, workspaceRoot);
114
- for (const entry of entries) all.push(entry);
115
- }
116
- return aggregateBalances(all);
117
- }
118
-
119
- /** Drop snapshots for `fromPeriod` and later. Re-export from
120
- * accounting-io for callers that conceptually live in the cache
121
- * layer (so they don't reach into the IO module). */
122
- export async function invalidateSnapshotsFrom(bookId: string, fromPeriod: string, workspaceRoot?: string): Promise<{ removed: string[] }> {
123
- return ioInvalidateFrom(bookId, fromPeriod, workspaceRoot);
124
- }
125
-
126
- /** Drop all snapshots and rebuild from scratch. Used by the
127
- * `rebuildSnapshots` admin action. Returns the periods that were
128
- * rebuilt. */
129
- export async function rebuildAllSnapshots(bookId: string, workspaceRoot?: string): Promise<{ rebuilt: string[] }> {
130
- await ioInvalidateAll(bookId, workspaceRoot);
131
- const periods = await listJournalPeriods(bookId, workspaceRoot);
132
- for (const monthKey of periods) {
133
- await getOrBuildSnapshot(bookId, monthKey, workspaceRoot);
134
- }
135
- return { rebuilt: periods };
136
- }
137
-
138
- // ── async rebuild queue ────────────────────────────────────────────
139
- //
140
- // Per-book queue. A `running` promise represents the in-flight
141
- // rebuild. While it's running, additional `scheduleRebuild` calls
142
- // merge their `fromPeriod` into `pendingFromPeriod` (taking the
143
- // minimum so the next pass covers everyone's invalidation), and we
144
- // kick off a follow-up once the current one resolves.
145
-
146
- interface RebuildQueueEntry {
147
- running: Promise<void>;
148
- pendingFromPeriod: string | null;
149
- pendingWorkspaceRoot: string | undefined;
150
- coalescedWriteCount: number;
151
- runningFromPeriod: string;
152
- /** Set by `cancelRebuild` (called from `deleteBook`). The runRebuild
153
- * loop checks before each write so a rebuild cannot resurrect the
154
- * book directory after `removeBookDir` has run. */
155
- cancelled: boolean;
156
- }
157
-
158
- const rebuildQueues = new Map<string, RebuildQueueEntry>();
159
-
160
- function minPeriod(lhs: string | null, rhs: string): string {
161
- if (lhs === null) return rhs;
162
- return lhs < rhs ? lhs : rhs;
163
- }
164
-
165
- function isInvalidatedDuringRebuild(bookId: string, period: string): boolean {
166
- // A pending invalidation that covers `period` means the queued
167
- // follow-up rebuild will redo this period. Skip the in-flight
168
- // write so we don't pollute the cache with stale data while a
169
- // fresher computation is queued. Without this guard, the
170
- // sequence "rebuild reads journal → caller writes a new entry →
171
- // caller invalidates → rebuild writes (stale) snapshot" leaves
172
- // the cache lying about the latest state.
173
- const queue = rebuildQueues.get(bookId);
174
- return queue !== undefined && queue.pendingFromPeriod !== null && period >= queue.pendingFromPeriod;
175
- }
176
-
177
- function isCancelled(bookId: string): boolean {
178
- return rebuildQueues.get(bookId)?.cancelled === true;
179
- }
180
-
181
- async function runRebuild(bookId: string, fromPeriod: string, workspaceRoot: string | undefined): Promise<void> {
182
- const startedAt = Date.now();
183
- log.info("accounting", "snapshot rebuild started", { bookId, fromPeriod });
184
- publishBookChange(bookId, { kind: ACCOUNTING_BOOK_EVENT_KINDS.snapshotsRebuilding, period: fromPeriod });
185
- const periods = await listJournalPeriods(bookId, workspaceRoot);
186
- const targets = periods.filter((monthKey) => monthKey >= fromPeriod);
187
- let written = 0;
188
- for (const monthKey of targets) {
189
- if (isCancelled(bookId)) break;
190
- if (isInvalidatedDuringRebuild(bookId, monthKey)) break;
191
- // Compute fresh from journal — bypasses getOrBuildSnapshot's
192
- // own write side-effect so the staleness check below is the
193
- // only writer in the rebuild path.
194
- const balances = await balancesAtEndOf(bookId, monthKey, workspaceRoot);
195
- if (isCancelled(bookId)) break;
196
- if (isInvalidatedDuringRebuild(bookId, monthKey)) break;
197
- await writeSnapshot(bookId, { period: monthKey, balances, builtAt: new Date().toISOString() }, workspaceRoot);
198
- if (isCancelled(bookId)) {
199
- // The book was deleted between our last check and the write —
200
- // `writeSnapshot` will have re-created the book directory tree
201
- // via mkdir-recursive. Undo it so we don't leave an orphaned
202
- // directory after `deleteBook` has run.
203
- await ioInvalidateFrom(bookId, monthKey, workspaceRoot);
204
- break;
205
- }
206
- if (isInvalidatedDuringRebuild(bookId, monthKey)) {
207
- // A concurrent invalidate raced ahead between our last check
208
- // and the disk write. The data we just wrote may be stale
209
- // relative to the latest journal — undo so the queued
210
- // follow-up rebuild starts from a clean slate.
211
- await ioInvalidateFrom(bookId, monthKey, workspaceRoot);
212
- break;
213
- }
214
- written += 1;
215
- publishBookChange(bookId, { kind: ACCOUNTING_BOOK_EVENT_KINDS.snapshotsReady, period: monthKey });
216
- }
217
- log.info("accounting", "snapshot rebuild done", { bookId, periods: written, durationMs: Date.now() - startedAt });
218
- }
219
-
220
- function startRebuild(bookId: string, fromPeriod: string, workspaceRoot: string | undefined): RebuildQueueEntry {
221
- const entry: RebuildQueueEntry = {
222
- running: Promise.resolve(),
223
- pendingFromPeriod: null,
224
- pendingWorkspaceRoot: undefined,
225
- coalescedWriteCount: 1,
226
- runningFromPeriod: fromPeriod,
227
- cancelled: false,
228
- };
229
- entry.running = runRebuild(bookId, fromPeriod, workspaceRoot)
230
- .catch((err) => {
231
- // A rebuild failure is logged but does not poison the queue —
232
- // the next `scheduleRebuild` call will start a fresh promise.
233
- log.error("accounting", "snapshot rebuild failed", { bookId, fromPeriod, error: errorMessage(err) });
234
- })
235
- .then(() => {
236
- // Drain any work that piled up while we were running.
237
- const current = rebuildQueues.get(bookId);
238
- if (!current) return;
239
- // If the book was cancelled mid-rebuild (e.g. deleteBook ran),
240
- // drop the queue entry entirely — we must not start a successor
241
- // that would re-create the deleted book directory.
242
- if (current.cancelled) {
243
- rebuildQueues.delete(bookId);
244
- return;
245
- }
246
- if (current.pendingFromPeriod !== null) {
247
- const nextFrom = current.pendingFromPeriod;
248
- const nextRoot = current.pendingWorkspaceRoot;
249
- const carriedCount = current.coalescedWriteCount;
250
- const successor = startRebuild(bookId, nextFrom, nextRoot);
251
- successor.coalescedWriteCount += carriedCount;
252
- rebuildQueues.set(bookId, successor);
253
- } else {
254
- rebuildQueues.delete(bookId);
255
- }
256
- });
257
- return entry;
258
- }
259
-
260
- /** Schedule a background rebuild for `bookId` starting at `fromPeriod`.
261
- * Multiple calls during an in-flight rebuild coalesce into a single
262
- * follow-up rebuild that covers the minimum `fromPeriod` seen.
263
- * Returns immediately — the rebuild runs on its own promise chain. */
264
- export function scheduleRebuild(bookId: string, fromPeriod: string, workspaceRoot?: string): void {
265
- const existing = rebuildQueues.get(bookId);
266
- if (!existing) {
267
- rebuildQueues.set(bookId, startRebuild(bookId, fromPeriod, workspaceRoot));
268
- return;
269
- }
270
- existing.pendingFromPeriod = minPeriod(existing.pendingFromPeriod, fromPeriod);
271
- existing.pendingWorkspaceRoot = workspaceRoot;
272
- existing.coalescedWriteCount += 1;
273
- }
274
-
275
- /** Test/diagnostic: resolves when no rebuild is running or queued for
276
- * `bookId`. Also called by `deleteBook` after `cancelRebuild` to
277
- * ensure a previously running rebuild has fully stopped before the
278
- * caller removes the book's directory on disk. */
279
- export async function awaitRebuildIdle(bookId: string): Promise<void> {
280
- while (rebuildQueues.has(bookId)) {
281
- const entry = rebuildQueues.get(bookId);
282
- if (!entry) return;
283
- await entry.running;
284
- }
285
- }
286
-
287
- /** Mark the book's in-flight rebuild as cancelled. The runRebuild
288
- * loop checks before each write and bails out, so a subsequent
289
- * `removeBookDir` cannot race with a `writeSnapshot` that would
290
- * re-create the directory tree. Pair with `awaitRebuildIdle(bookId)`
291
- * to wait for the in-flight rebuild to finish bailing. */
292
- export function cancelRebuild(bookId: string): void {
293
- const entry = rebuildQueues.get(bookId);
294
- if (!entry) return;
295
- entry.cancelled = true;
296
- // Drop pending too — a cancelled book should not get a successor
297
- // rebuild after the in-flight one drains.
298
- entry.pendingFromPeriod = null;
299
- }
300
-
301
- /** Test/diagnostic: snapshot of the per-book queue state. Stable
302
- * enough to assert against; fields may grow over time. */
303
- export function inspectRebuildQueue(bookId: string): {
304
- running: boolean;
305
- runningFromPeriod: string | null;
306
- pendingFromPeriod: string | null;
307
- coalescedWriteCount: number;
308
- } {
309
- const entry = rebuildQueues.get(bookId);
310
- if (!entry) {
311
- return { running: false, runningFromPeriod: null, pendingFromPeriod: null, coalescedWriteCount: 0 };
312
- }
313
- return {
314
- running: true,
315
- runningFromPeriod: entry.runningFromPeriod,
316
- pendingFromPeriod: entry.pendingFromPeriod,
317
- coalescedWriteCount: entry.coalescedWriteCount,
318
- };
319
- }
320
-
321
- /** Test-only — drain all in-flight rebuilds, then drop queue state.
322
- * Awaiting first means a leftover rebuild can't continue writing
323
- * into the next test's tmp dir after we clear the bookkeeping. */
324
- export async function _resetRebuildQueueForTesting(): Promise<void> {
325
- // Mark everything cancelled so loops bail at their next checkpoint
326
- // instead of continuing through every period.
327
- for (const entry of rebuildQueues.values()) {
328
- entry.cancelled = true;
329
- entry.pendingFromPeriod = null;
330
- }
331
- const pending = Array.from(rebuildQueues.values()).map((entry) => entry.running);
332
- await Promise.allSettled(pending);
333
- rebuildQueues.clear();
334
- }
@@ -1,265 +0,0 @@
1
- // Time-series aggregation for the accounting plugin: bucketise a
2
- // date range by month / fiscal quarter / fiscal year and roll up a
3
- // metric (revenue / expense / net income / closing balance of a
4
- // specific account) into a chart-ready `(label, value)[]` series.
5
- //
6
- // LLM-facing only — the in-canvas Accounting `<View>` keeps using
7
- // `getReport` for its tab-driven UI. `getTimeSeries` exists so a
8
- // single tool round-trip can answer "chart my quarterly revenue
9
- // over the last two years" without the LLM fanning out N calls and
10
- // stitching the buckets itself.
11
- //
12
- // Pure module: no I/O, no service-layer awareness. Caller hands in
13
- // the entries / accounts already loaded; we return the points.
14
-
15
- import type { Account, AccountType, JournalEntry } from "./types.js";
16
- import { aggregateBalances } from "./report.js";
17
- import { fiscalYearEndMonth, type FiscalYearEnd } from "../../src/plugins/accounting/fiscalYear.js";
18
- import {
19
- TIME_SERIES_GRANULARITIES,
20
- TIME_SERIES_METRICS,
21
- type TimeSeriesGranularity,
22
- type TimeSeriesMetric,
23
- } from "../../src/plugins/accounting/timeSeriesEnums.js";
24
-
25
- export { TIME_SERIES_GRANULARITIES, TIME_SERIES_METRICS };
26
- export type { TimeSeriesGranularity, TimeSeriesMetric };
27
-
28
- export interface Bucket {
29
- /** Inclusive YYYY-MM-DD lower bound. */
30
- from: string;
31
- /** Inclusive YYYY-MM-DD upper bound. */
32
- to: string;
33
- /** Chart x-axis label. Format depends on granularity:
34
- * "YYYY-MM" / "FY{endYear}-Q{1..4}" / "FY{endYear}". For fiscal
35
- * years that don't align with the calendar year (Q1/Q2/Q3 books)
36
- * the FY is named by its END calendar year — matches Japanese
37
- * "令和7年度" convention (Apr 2025 - Mar 2026 = FY2026). */
38
- label: string;
39
- }
40
-
41
- export interface TimeSeriesPoint {
42
- label: string;
43
- from: string;
44
- to: string;
45
- /** Single number, natural-sign per metric. Revenue and net income
46
- * positive when income exceeds expense; expense reported as a
47
- * positive cost; account balance follows the account's display
48
- * sign (assets debit-positive, liabilities/equity credit-positive). */
49
- value: number;
50
- }
51
-
52
- // ── date arithmetic (pure, no Date for parsing/formatting) ─────────
53
-
54
- function pad2(num: number): string {
55
- return String(num).padStart(2, "0");
56
- }
57
-
58
- function fmtYmd(year: number, month: number, day: number): string {
59
- return `${year}-${pad2(month)}-${pad2(day)}`;
60
- }
61
-
62
- interface YmdParts {
63
- year: number;
64
- month: number;
65
- day: number;
66
- }
67
-
68
- function parseYmd(value: string): YmdParts {
69
- const [year, month, day] = value.split("-").map((segment) => parseInt(segment, 10));
70
- return { year, month, day };
71
- }
72
-
73
- function lastDayOf(year: number, month: number): number {
74
- // UTC day 0 of next month = last day of this month, immune to TZ.
75
- return new Date(Date.UTC(year, month, 0)).getUTCDate();
76
- }
77
-
78
- function addDay(date: YmdParts): YmdParts {
79
- const stepped = new Date(Date.UTC(date.year, date.month - 1, date.day + 1));
80
- return {
81
- year: stepped.getUTCFullYear(),
82
- month: stepped.getUTCMonth() + 1,
83
- day: stepped.getUTCDate(),
84
- };
85
- }
86
-
87
- // ── fiscal-year arithmetic (year-month, no Date) ───────────────────
88
-
89
- interface FyAnchor {
90
- /** Calendar year the fiscal year STARTED in. */
91
- fyStartYear: number;
92
- /** Calendar year the fiscal year ENDS in (used for labelling). */
93
- fyEndYear: number;
94
- /** First calendar month of the fiscal year (1-based). */
95
- startMonth: number;
96
- }
97
-
98
- function fyAnchorFor(date: YmdParts, end: FiscalYearEnd): FyAnchor {
99
- const closingMonth = fiscalYearEndMonth(end);
100
- const startMonth = (closingMonth % 12) + 1; // month after the close
101
- // FY containing date: started this calendar year if date.month is
102
- // at-or-after startMonth, else previous calendar year. Q4 books
103
- // (startMonth = 1) always land in the same calendar year — covered
104
- // by the same predicate.
105
- const fyStartYear = date.month >= startMonth ? date.year : date.year - 1;
106
- const fyEndYear = closingMonth === 12 ? fyStartYear : fyStartYear + 1;
107
- return { fyStartYear, fyEndYear, startMonth };
108
- }
109
-
110
- // ── per-granularity bucket lookup ──────────────────────────────────
111
-
112
- function monthBucketContaining(date: YmdParts): Bucket {
113
- return {
114
- from: fmtYmd(date.year, date.month, 1),
115
- to: fmtYmd(date.year, date.month, lastDayOf(date.year, date.month)),
116
- label: `${date.year}-${pad2(date.month)}`,
117
- };
118
- }
119
-
120
- function quarterBucketContaining(date: YmdParts, end: FiscalYearEnd): Bucket {
121
- const anchor = fyAnchorFor(date, end);
122
- const offset = (date.month - anchor.startMonth + 12) % 12; // 0..11
123
- const qIdx = Math.floor(offset / 3); // 0..3
124
- // Flat month-index from the calendar epoch makes year rollover
125
- // arithmetic trivial.
126
- const startFlat = anchor.fyStartYear * 12 + (anchor.startMonth - 1) + qIdx * 3;
127
- const endFlat = startFlat + 2;
128
- const qStartYear = Math.floor(startFlat / 12);
129
- const qStartMonth = (startFlat % 12) + 1;
130
- const qEndYear = Math.floor(endFlat / 12);
131
- const qEndMonth = (endFlat % 12) + 1;
132
- return {
133
- from: fmtYmd(qStartYear, qStartMonth, 1),
134
- to: fmtYmd(qEndYear, qEndMonth, lastDayOf(qEndYear, qEndMonth)),
135
- label: `FY${anchor.fyEndYear}-Q${qIdx + 1}`,
136
- };
137
- }
138
-
139
- function yearBucketContaining(date: YmdParts, end: FiscalYearEnd): Bucket {
140
- const anchor = fyAnchorFor(date, end);
141
- const startFlat = anchor.fyStartYear * 12 + (anchor.startMonth - 1);
142
- const endFlat = startFlat + 11;
143
- const yStartYear = Math.floor(startFlat / 12);
144
- const yStartMonth = (startFlat % 12) + 1;
145
- const yEndYear = Math.floor(endFlat / 12);
146
- const yEndMonth = (endFlat % 12) + 1;
147
- return {
148
- from: fmtYmd(yStartYear, yStartMonth, 1),
149
- to: fmtYmd(yEndYear, yEndMonth, lastDayOf(yEndYear, yEndMonth)),
150
- label: `FY${anchor.fyEndYear}`,
151
- };
152
- }
153
-
154
- function bucketContaining(date: YmdParts, granularity: TimeSeriesGranularity, end: FiscalYearEnd): Bucket {
155
- if (granularity === "month") return monthBucketContaining(date);
156
- if (granularity === "quarter") return quarterBucketContaining(date, end);
157
- return yearBucketContaining(date, end);
158
- }
159
-
160
- /** Walk inclusive `[from, to]` and return every bucket that overlaps
161
- * it, ordered ascending by `from`. The first bucket is the one
162
- * CONTAINING `from` — it can extend earlier than `from`; the last
163
- * bucket is the one CONTAINING `to` — it can extend past `to`. The
164
- * caller's response echoes the input range so the LLM can label the
165
- * chart truthfully ("Revenue Apr 2025 – Sep 2026" even though the
166
- * outermost buckets cover Apr-Jun 2025 and Jul-Sep 2026). */
167
- export function bucketize(input: { from: string; to: string; granularity: TimeSeriesGranularity; fiscalYearEnd: FiscalYearEnd }): Bucket[] {
168
- if (input.from > input.to) return [];
169
- const start = parseYmd(input.from);
170
- const result: Bucket[] = [];
171
- let bucket = bucketContaining(start, input.granularity, input.fiscalYearEnd);
172
- // Buckets are contiguous; once a bucket's `from` is past `input.to`
173
- // every subsequent bucket is too.
174
- while (bucket.from <= input.to) {
175
- result.push(bucket);
176
- const next = addDay(parseYmd(bucket.to));
177
- bucket = bucketContaining(next, input.granularity, input.fiscalYearEnd);
178
- }
179
- return result;
180
- }
181
-
182
- // ── value computation ──────────────────────────────────────────────
183
-
184
- /** Convert raw netDebit to natural-sign presentation per account
185
- * type. Mirrors the helper in `report.ts` (kept private there). */
186
- function naturalSign(type: AccountType, netDebit: number): number {
187
- if (type === "asset" || type === "expense") return netDebit;
188
- return -netDebit;
189
- }
190
-
191
- interface PresentationTotals {
192
- income: number;
193
- expense: number;
194
- }
195
-
196
- /** Sum presented income and expense values across the supplied
197
- * entries. Used for window-based metrics (revenue / expense /
198
- * netIncome). Opening entries reference B/S accounts only and so
199
- * contribute zero to either total — including them is harmless. */
200
- function presentedPlTotals(entries: readonly JournalEntry[], accountTypeByCode: ReadonlyMap<string, AccountType>): PresentationTotals {
201
- const balances = aggregateBalances(entries);
202
- let income = 0;
203
- let expense = 0;
204
- for (const row of balances) {
205
- const type = accountTypeByCode.get(row.accountCode);
206
- if (!type) continue;
207
- if (type === "income") income += naturalSign(type, row.netDebit);
208
- else if (type === "expense") expense += naturalSign(type, row.netDebit);
209
- }
210
- return { income, expense };
211
- }
212
-
213
- function entriesInWindow(entries: readonly JournalEntry[], from: string, toDate: string): JournalEntry[] {
214
- return entries.filter((entry) => entry.date >= from && entry.date <= toDate);
215
- }
216
-
217
- function entriesUpTo(entries: readonly JournalEntry[], toDate: string): JournalEntry[] {
218
- return entries.filter((entry) => entry.date <= toDate);
219
- }
220
-
221
- function valueForBucket(input: {
222
- bucket: Bucket;
223
- entries: readonly JournalEntry[];
224
- accounts: readonly Account[];
225
- metric: TimeSeriesMetric;
226
- accountCode?: string;
227
- }): number {
228
- const accountTypeByCode = new Map(input.accounts.map((acct) => [acct.code, acct.type]));
229
- if (input.metric === "accountBalance") {
230
- const code = input.accountCode;
231
- if (!code) return 0; // guarded at the route — defensive zero.
232
- const type = accountTypeByCode.get(code);
233
- if (!type) return 0;
234
- const cumulative = entriesUpTo(input.entries, input.bucket.to);
235
- const balances = aggregateBalances(cumulative);
236
- const row = balances.find((balance) => balance.accountCode === code);
237
- return row ? naturalSign(type, row.netDebit) : 0;
238
- }
239
- const window = entriesInWindow(input.entries, input.bucket.from, input.bucket.to);
240
- const totals = presentedPlTotals(window, accountTypeByCode);
241
- if (input.metric === "revenue") return totals.income;
242
- if (input.metric === "expense") return totals.expense;
243
- return totals.income - totals.expense; // netIncome
244
- }
245
-
246
- export function buildTimeSeries(input: {
247
- buckets: readonly Bucket[];
248
- entries: readonly JournalEntry[];
249
- accounts: readonly Account[];
250
- metric: TimeSeriesMetric;
251
- accountCode?: string;
252
- }): TimeSeriesPoint[] {
253
- return input.buckets.map((bucket) => ({
254
- label: bucket.label,
255
- from: bucket.from,
256
- to: bucket.to,
257
- value: valueForBucket({
258
- bucket,
259
- entries: input.entries,
260
- accounts: input.accounts,
261
- metric: input.metric,
262
- accountCode: input.accountCode,
263
- }),
264
- }));
265
- }