collabmd 0.1.47 → 0.1.49

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 (305) hide show
  1. package/README.md +70 -11
  2. package/bin/collabmd.js +3 -0
  3. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-C-nmgL5g.js +1 -0
  4. package/dist/client/assets/JetBrainsMono-Regular-BQaDgvhP.woff2 +0 -0
  5. package/dist/client/assets/abnfDiagram-VCTEODGH-CdVZqicp.js +1 -0
  6. package/dist/client/assets/{architectureDiagram-5GKGNRK7-I39sYjHI.js → architectureDiagram-5GKGNRK7-wUo0bXif.js} +3 -3
  7. package/dist/client/assets/babel-CY5KOv-I.js +16 -0
  8. package/dist/client/assets/{blockDiagram-NRAW4CY4-C6DtJy5Y.js → blockDiagram-I7D4REHJ-BRSK3m49.js} +4 -4
  9. package/dist/client/assets/browser-utils-DbXSMI-6.js +1 -0
  10. package/dist/client/assets/{c4Diagram-UCG6FXSJ-CtWtxS9_.js → c4Diagram-7LVT6UL2-yD2poxqw.js} +1 -1
  11. package/dist/client/assets/channel-Fa4GS_-F.js +1 -0
  12. package/dist/client/assets/chunk-2E4U76K2-BPnTT-J3.js +2 -0
  13. package/dist/client/assets/{chunk-TEH6E4GO-DVayPj-q.js → chunk-4HAMMTFA-DStbIfJ1.js} +20 -20
  14. package/dist/client/assets/{chunk-75Z2AOVW-CdFSaPjz.js → chunk-75Z2AOVW-C5pGd3W9.js} +1 -1
  15. package/dist/client/assets/{chunk-CLGD4ZFX-DQLgbQrv.js → chunk-CLGD4ZFX-BMeArk03.js} +1 -1
  16. package/dist/client/assets/chunk-DU6HZSFF-Zkp69c6c.js +127 -0
  17. package/dist/client/assets/{chunk-F27PBJKO-CNHIFhjo.js → chunk-F27PBJKO-D4xNct1r.js} +1 -1
  18. package/dist/client/assets/{chunk-GMAD6QVW-CbcBqPM5.js → chunk-GMAD6QVW-NZ6WpDb8.js} +1 -1
  19. package/dist/client/assets/{chunk-OBVCFTLP-BJR7u1jh.js → chunk-GVQU2GXP-D8SDprfX.js} +1 -1
  20. package/dist/client/assets/{chunk-G27WJ6UU-D9-eg537.js → chunk-IMKFNOWR-D8jEuU3Q.js} +2 -2
  21. package/dist/client/assets/{chunk-JQ64N6SF-DSFNH4bh.js → chunk-L3NEJ4N5-aijKP-eh.js} +1 -1
  22. package/dist/client/assets/chunk-OSK3NFVY-CCbLQAMa.js +10 -0
  23. package/dist/client/assets/{chunk-P2QGCYS3-BRpqY32H.js → chunk-P2QGCYS3-Oag_FdCq.js} +1 -1
  24. package/dist/client/assets/{chunk-POPQ4Y6H-Cb4SlVZF.js → chunk-POPQ4Y6H-CYnpIgXT.js} +1 -1
  25. package/dist/client/assets/{chunk-PWAF6VOD-Cz8IwBB5.js → chunk-PWAF6VOD-JSNrcBmP.js} +1 -1
  26. package/dist/client/assets/{chunk-RM4UPSZO-CWRwxNWJ.js → chunk-RM4UPSZO-DOWllDY8.js} +4 -4
  27. package/dist/client/assets/{chunk-RHFEMEQ7-CWY8cf4H.js → chunk-SHT3W25Y-BiQGljao.js} +5 -5
  28. package/dist/client/assets/{chunk-SVP7TREG-CyHOmcac.js → chunk-SVP7TREG-DB485R5x.js} +1 -1
  29. package/dist/client/assets/{chunk-LCL6LL3I-DmDkVukh.js → chunk-TICWLB2K-8rAVvoS3.js} +4 -4
  30. package/dist/client/assets/{chunk-CKBBP62Z-DG26UiBv.js → chunk-TLUHSLCS-DTY5v0aj.js} +2 -2
  31. package/dist/client/assets/classDiagram-ZZMXUADV-BsQWoR3Q.js +1 -0
  32. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BsQWoR3Q.js +1 -0
  33. package/dist/client/assets/{comment-thread-store-CdcwrObc.js → comment-thread-store-CX98zR6q.js} +1 -1
  34. package/dist/client/assets/comment-threads-BnRwo5B9.js +4 -0
  35. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CdTPi2yO.js → cynefinDiagram-5FMLGOSQ-rTz9RhFH.js} +1 -1
  36. package/dist/client/assets/{dagre-3AP2YEHR-5gtJtd0O.js → dagre-GXQ25YYZ-Cr67wLpQ.js} +2 -2
  37. package/dist/client/assets/{diagram-S7CK7UJ4-BudWz9WU.js → diagram-S7CK7UJ4-DywERe__.js} +2 -2
  38. package/dist/client/assets/{diagram-UQ7AKVKN-3v4rbH-s.js → diagram-UQ7AKVKN-CNYlXtu4.js} +1 -1
  39. package/dist/client/assets/diagram-VSXAHHWV-0H1a61Bn.js +3 -0
  40. package/dist/client/assets/{diagram-VX7I27RA-C_-rj0sw.js → diagram-VX7I27RA-CsJbsx5F.js} +1 -1
  41. package/dist/client/assets/{diagram-Z3DM3KII-B4yxavgj.js → diagram-Z3DM3KII-D0mLxEgI.js} +1 -1
  42. package/dist/client/assets/dist--hk9I5HQ.js +1 -0
  43. package/dist/client/assets/{dist-DutPuFmC.js → dist-B1XEa-Zs.js} +1 -1
  44. package/dist/client/assets/dist-Bd1JucQF.js +1 -0
  45. package/dist/client/assets/{dist-DZAAWTG-.js → dist-C0G3AaXG.js} +1 -1
  46. package/dist/client/assets/dist-ChRoo3IB.js +9 -0
  47. package/dist/client/assets/dist-Dm4w5UId.js +1 -0
  48. package/dist/client/assets/{dist-B_yQaEv_.js → dist-DpYDHR9p.js} +1 -1
  49. package/dist/client/assets/{dist-CietwTVS.js → dist-DzNmC81X.js} +1 -1
  50. package/dist/client/assets/{dist-DDUn5zTz.js → dist-GvvCNBe4.js} +1 -1
  51. package/dist/client/assets/dist-JaU3aRlD.js +19 -0
  52. package/dist/client/assets/{dist-Bw3VIE_v.js → dist-TW8iFRyW.js} +1 -1
  53. package/dist/client/assets/{dist-Dir5dDKZ.js → dist-jVU5Be5P.js} +1 -1
  54. package/dist/client/assets/document-formatter-rDDjRfZ2.js +2 -0
  55. package/dist/client/assets/drawioEditor-JubEEha7.js +9 -0
  56. package/dist/client/assets/ebnfDiagram-PWID7BFC-HeTxHpCU.js +1 -0
  57. package/dist/client/assets/editor-session-Bk6oIvSr.js +54 -0
  58. package/dist/client/assets/embedded-editor-base-BlCgz6mw.css +1 -0
  59. package/dist/client/assets/{erDiagram-SSCWMZ5O-C-iy6VkX.js → erDiagram-RLTQ6QDP-CbrDdd5P.js} +2 -2
  60. package/dist/client/assets/{es-CAsWR6hG.js → es-Bfdp096f.js} +13 -13
  61. package/dist/client/assets/estree-CuT_pB8K.js +44 -0
  62. package/dist/client/assets/excalidraw-element-link-YLIaTnVF.js +7 -0
  63. package/dist/client/assets/{excalidraw-scene-BOben2gr.js → excalidraw-scene-6uROebca.js} +1 -1
  64. package/dist/client/assets/{excalidraw-editor-BlcN6umJ.css → excalidrawEditor-BbSbI80a.css} +1 -1
  65. package/dist/client/assets/excalidrawEditor-dBMbRX_S.js +9 -0
  66. package/dist/client/assets/export-page-CRRQ2NEl.js +57 -0
  67. package/dist/client/assets/exportDocument-BlQRd9_m.js +2 -0
  68. package/dist/client/assets/exportDocument-CNSbjZwu.css +1 -0
  69. package/dist/client/assets/{file-icon-svg-CQAjlWjK.js → file-icon-svg-Y_kpF957.js} +1 -1
  70. package/dist/client/assets/file-kind-BwMIcZbi.js +1 -0
  71. package/dist/client/assets/file-search-DDLyyLDU.js +1 -0
  72. package/dist/client/assets/flowDiagram-HODETNUW-D5Gy0wmg.js +1 -0
  73. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-DtKHAbez.js → ganttDiagram-EL5Y4UJY-BHgOYiZz.js} +1 -1
  74. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-oue8fQ5o.js → gitGraphDiagram-WWUBYQGX-Dl__X3Sk.js} +1 -1
  75. package/dist/client/assets/html-Cdnr2qzU.js +25 -0
  76. package/dist/client/assets/image-ZVKINSPA-DgzW5zAy.js +1 -0
  77. package/dist/client/assets/index-D3RER1SZ.js +1023 -0
  78. package/dist/client/assets/index-DREVKl_K.css +1 -0
  79. package/dist/client/assets/{infoDiagram-RXCK75RN-BMzo6KIn.js → infoDiagram-27XIBGKW-DBpoANC9.js} +1 -1
  80. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-BIM3h287.js → ishikawaDiagram-5VMMS53U-BMPC6hfp.js} +1 -1
  81. package/dist/client/assets/{journeyDiagram-EYS64GPL-mF6Lh8O-.js → journeyDiagram-3NMN7TZE-B1sApUmM.js} +2 -2
  82. package/dist/client/assets/{kanban-definition-3QL26DDD-DLN2NyoR.js → kanban-definition-UXKFOSKX-C9yP4td1.js} +4 -4
  83. package/dist/client/assets/{line-BfTWJmgf.js → line-BFl3ujec.js} +1 -1
  84. package/dist/client/assets/markdown-CByV-4CU.js +70 -0
  85. package/dist/client/assets/{mermaid.core-CLZBlpIW.js → mermaid.core-BrY09czX.js} +5 -5
  86. package/dist/client/assets/mermaid.core-DfcNfOTv.js +1 -0
  87. package/dist/client/assets/{mindmap-definition-FBJOCRG2-CRyHaHTk.js → mindmap-definition-YA3MSWOX-BUQHcYbK.js} +8 -8
  88. package/dist/client/assets/pegDiagram-XKGWAZYB-Bi2Iml7Z.js +1 -0
  89. package/dist/client/assets/{pieDiagram-E7YTZNPT-B5OZPb88.js → pieDiagram-E7YTZNPT-_gYXnMoi.js} +1 -1
  90. package/dist/client/assets/preview-render-compiler-CfxJYldJ.js +41 -0
  91. package/dist/client/assets/preview-render-compiler-Cu8_i9iS.js +1 -0
  92. package/dist/client/assets/{preview-render-worker-BmN79bVc.js → preview-render-worker-c4Yi93qW.js} +41 -35
  93. package/dist/client/assets/prod-BiJL30sl.js +1 -0
  94. package/dist/client/assets/prod-qAk7rHa4.js +177 -0
  95. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-Drrw14YH.js → quadrantDiagram-AXDQQJYC-DwexhEhd.js} +1 -1
  96. package/dist/client/assets/quick-switcher-controller-BoIK_wuG.js +1 -0
  97. package/dist/client/assets/railroadDiagram-O6MQD6OU-BVA5UbMu.js +1 -0
  98. package/dist/client/assets/{requirementDiagram-EFPCY7ZU-CRZG6mRB.js → requirementDiagram-BXWQKSXE-C4cKwiIw.js} +2 -2
  99. package/dist/client/assets/runtime-config-DcmnGcXU.js +1 -0
  100. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-DvarEJpo.js → sankeyDiagram-P5KCCOFB-B5HHkRT2.js} +1 -1
  101. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-B4oWlb-w.js → sequenceDiagram-WJ2MYXX4-BX6jqJFD.js} +6 -6
  102. package/dist/client/assets/standalone-6z5x8CWe.js +29 -0
  103. package/dist/client/assets/{stateDiagram-HBIQ2CUA-qbHIIqRZ.js → stateDiagram-D77RDMKH-CmqxyRGx.js} +1 -1
  104. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-zoLLQVyh.js +1 -0
  105. package/dist/client/assets/{swimlanes-XN3QIQJK-xB6CtUGj.js → swimlanes-42K2YHIH-Bmgb9Zld.js} +1 -1
  106. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DS-KQN37.js +8 -0
  107. package/dist/client/assets/{timeline-definition-24CTP7MA-Lj_yTNlY.js → timeline-definition-24CTP7MA-BA-lVH6P.js} +1 -1
  108. package/dist/client/assets/vault-api-client-iZLhMJKe.js +6 -0
  109. package/dist/client/assets/{vennDiagram-4TSXK5OY-BekRp5Y0.js → vennDiagram-4TSXK5OY-DNdUC_Xj.js} +1 -1
  110. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-kdnV_Hdm.js → wardleyDiagram-VM6X3IG4-C2oY-YbF.js} +1 -1
  111. package/dist/client/assets/wiki-link-resolver-DFIwErjc.js +1 -0
  112. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-DufGwNSB.js → xychartDiagram-S5SC5T6Z-CoIR8CBX.js} +1 -1
  113. package/dist/client/assets/yaml-9ltHPDNO.js +154 -0
  114. package/dist/client/assets/{yjs-provider-reset-guard-DE0vINDP.js → yjs-provider-reset-guard-9ul1qF6k.js} +1 -1
  115. package/dist/client/drawio-editor.html +11 -3
  116. package/dist/client/excalidraw-editor.html +45 -2
  117. package/dist/client/export-document.html +3 -3
  118. package/dist/client/index.html +106 -52
  119. package/integrations/pi/index.ts +94 -0
  120. package/package.json +41 -29
  121. package/src/client/app/drawio-editor.html +2 -2
  122. package/src/client/app/excalidraw-editor.html +2 -2
  123. package/src/client/app/export-document.html +1 -1
  124. package/src/client/app/index.html +88 -50
  125. package/src/client/app/main-entry.js +15 -1
  126. package/src/client/application/app-shell/chat-feature.js +7 -22
  127. package/src/client/application/app-shell/export-feature.js +17 -1
  128. package/src/client/application/app-shell/git-feature.js +51 -14
  129. package/src/client/application/app-shell/presence-feature.js +8 -25
  130. package/src/client/application/app-shell/ui-feature-identity.js +13 -0
  131. package/src/client/application/app-shell/ui-feature-shell.js +199 -99
  132. package/src/client/application/app-shell/ui-feature-sidebar.js +43 -7
  133. package/src/client/application/app-shell/ui-feature-tab-activity.js +19 -6
  134. package/src/client/application/app-shell/ui-feature-toolbar.js +16 -129
  135. package/src/client/application/app-shell-elements.js +8 -0
  136. package/src/client/application/preview-render-compiler.js +1 -87
  137. package/src/client/application/preview-render-scheduler.js +8 -24
  138. package/src/client/application/preview-renderer.js +2 -2
  139. package/src/client/application/webmcp-diagram-renderer.js +47 -0
  140. package/src/client/application/workspace-coordinator.js +23 -4
  141. package/src/client/application/workspace-preview-controller.js +120 -2
  142. package/src/client/application/workspace-route-controller.js +11 -1
  143. package/src/client/assets/vendor/jetbrains-mono/JetBrainsMono-Regular.woff2 +0 -0
  144. package/src/client/assets/vendor/jetbrains-mono/OFL.txt +93 -0
  145. package/src/client/bootstrap/collabmd-app-shell.js +99 -6
  146. package/src/client/domain/document-formatter.js +193 -0
  147. package/src/client/domain/excalidraw-element-link.js +73 -0
  148. package/src/client/domain/excalidraw-scene.js +21 -0
  149. package/src/client/domain/file-search.js +73 -0
  150. package/src/client/domain/git-labels.js +5 -0
  151. package/src/client/domain/hash-routes.js +8 -0
  152. package/src/client/domain/room.js +1 -5
  153. package/src/client/domain/runtime-paths.js +5 -0
  154. package/src/client/domain/vault-paths.js +6 -4
  155. package/src/client/domain/wiki-link-completions.js +20 -20
  156. package/src/client/drawio-editor.js +8 -3
  157. package/src/client/excalidraw-editor.js +287 -16
  158. package/src/client/export/export-host.js +59 -36
  159. package/src/client/export/export-page.js +14 -4
  160. package/src/client/export/export-pipeline.js +270 -42
  161. package/src/client/infrastructure/agent-connection-api-client.js +23 -0
  162. package/src/client/infrastructure/auth-client.js +3 -13
  163. package/src/client/infrastructure/editor-session.js +15 -0
  164. package/src/client/infrastructure/editor-view-adapter.js +73 -14
  165. package/src/client/infrastructure/excalidraw-room-client.js +80 -1
  166. package/src/client/infrastructure/git-api-client.js +39 -33
  167. package/src/client/infrastructure/lobby-presence.js +2 -5
  168. package/src/client/infrastructure/runtime-config.js +11 -1
  169. package/src/client/infrastructure/tab-activity-lock.js +2 -5
  170. package/src/client/infrastructure/vault-api-client.js +33 -26
  171. package/src/client/infrastructure/webmcp-tool-registry.js +283 -0
  172. package/src/client/infrastructure/workspace-sync-client.js +6 -4
  173. package/src/client/presentation/agent-connection-controller.js +255 -0
  174. package/src/client/presentation/backlinks-panel.js +3 -0
  175. package/src/client/presentation/bases-preview-controller.js +59 -26
  176. package/src/client/presentation/comment-ui/comment-ui-card.js +4 -2
  177. package/src/client/presentation/create-menu-presenter.js +19 -16
  178. package/src/client/presentation/excalidraw-embed-controller.js +164 -4
  179. package/src/client/presentation/file-action-controller.js +15 -2
  180. package/src/client/presentation/file-explorer-controller.js +3 -0
  181. package/src/client/presentation/file-explorer-view.js +71 -12
  182. package/src/client/presentation/file-history-view-controller.js +19 -12
  183. package/src/client/presentation/file-tree-state.js +1 -11
  184. package/src/client/presentation/git-diff-view-controller.js +70 -53
  185. package/src/client/presentation/git-panel-controller.js +86 -32
  186. package/src/client/presentation/image-lightbox-controller.js +20 -16
  187. package/src/client/presentation/layout-controller.js +3 -1
  188. package/src/client/presentation/quick-switcher-controller.js +74 -232
  189. package/src/client/presentation/quick-switcher-text-search.js +10 -14
  190. package/src/client/styles/components/navigation.css +1 -2
  191. package/src/client/styles/components/scrollbars.css +10 -0
  192. package/src/client/styles/export-document.css +44 -29
  193. package/src/client/styles/features/agent-connection.css +202 -0
  194. package/src/client/styles/features/collaboration-chat.css +8 -1
  195. package/src/client/styles/features/collaboration-dialogs.css +16 -1
  196. package/src/client/styles/features/collaboration-presence.css +30 -5
  197. package/src/client/styles/features/diff-toolbar.css +1 -1
  198. package/src/client/styles/features/media-lightbox.css +12 -3
  199. package/src/client/styles/features/preview-markdown.css +54 -0
  200. package/src/client/styles/features/quick-switcher.css +20 -11
  201. package/src/client/styles/foundation/themes.css +2 -0
  202. package/src/client/styles/foundation/tokens.css +8 -0
  203. package/src/client/styles/layout/editor-page.css +25 -11
  204. package/src/client/styles/layout/responsive.css +2 -20
  205. package/src/client/styles/layout/shell.css +77 -24
  206. package/src/client/styles/layout/sidebar.css +7 -4
  207. package/src/client/styles/layout/view-modes.css +2 -0
  208. package/src/client/styles/overrides/codemirror.css +31 -19
  209. package/src/client/styles/primitives/controls.css +21 -54
  210. package/src/client/styles/style.css +1 -0
  211. package/src/client/styles/surfaces/excalidraw-editor.css +42 -0
  212. package/src/domain/agent-tool-definitions.js +796 -0
  213. package/src/domain/collabmd-content-capabilities.js +149 -0
  214. package/src/domain/collaboration-protocol.js +2 -0
  215. package/src/domain/comment-threads.js +3 -5
  216. package/src/domain/diagram-source.js +50 -0
  217. package/src/domain/editable-content-revision.js +10 -0
  218. package/src/domain/editable-text.js +3 -0
  219. package/src/domain/exact-text-edits.js +69 -0
  220. package/src/domain/excalidraw-agent-scene.js +522 -0
  221. package/src/domain/excalidraw-agent-verification.js +460 -0
  222. package/src/domain/excalidraw-room-codec.js +1 -5
  223. package/src/domain/file-kind.js +12 -1
  224. package/src/domain/video-embed.js +76 -0
  225. package/src/server/application/agent-connection-service.js +178 -0
  226. package/src/server/application/agent-content-service.js +773 -0
  227. package/src/server/application/workspace-reconciliation.js +5 -7
  228. package/src/server/config/env.js +51 -1
  229. package/src/server/create-app-server.js +36 -0
  230. package/src/server/domain/bases/base-query-service.js +1 -5
  231. package/src/server/domain/collaboration/collaboration-room.js +121 -25
  232. package/src/server/domain/collaboration/protocol.js +4 -0
  233. package/src/server/domain/hosted-workspace.js +21 -0
  234. package/src/server/domain/markdown-reference-extractor.js +15 -1
  235. package/src/server/domain/ripgrep-search-service.js +110 -37
  236. package/src/server/infrastructure/git/git-service.js +19 -1
  237. package/src/server/infrastructure/git/history-service.js +1 -1
  238. package/src/server/infrastructure/git/local-exclude.js +25 -0
  239. package/src/server/infrastructure/git/parsers.js +19 -17
  240. package/src/server/infrastructure/git/path-utils.js +0 -16
  241. package/src/server/infrastructure/git/status-service.js +1 -1
  242. package/src/server/infrastructure/git/untracked-files.js +12 -21
  243. package/src/server/infrastructure/http/create-agent-api-handler.js +171 -0
  244. package/src/server/infrastructure/http/create-git-api-command-handler.js +32 -0
  245. package/src/server/infrastructure/http/create-request-handler.js +28 -0
  246. package/src/server/infrastructure/http/create-static-handler.js +6 -0
  247. package/src/server/infrastructure/mcp/create-agent-mcp-handler.js +199 -0
  248. package/src/server/infrastructure/persistence/agent-connection-store.js +120 -0
  249. package/src/server/infrastructure/persistence/path-utils.js +10 -4
  250. package/src/server/infrastructure/persistence/pull-backup-store.js +2 -27
  251. package/src/server/infrastructure/persistence/vault-file-store.js +11 -2
  252. package/src/server/shared/agent-tool-image.js +22 -0
  253. package/src/server/shared/agent-tool-rate-limiter.js +20 -0
  254. package/src/server/startup/git-remote-bootstrap.js +5 -33
  255. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-BPH4FJkv.js +0 -1
  256. package/dist/client/assets/abnfDiagram-VCTEODGH-BNDLzJ7L.js +0 -1
  257. package/dist/client/assets/browser-utils--H_DkyEF.js +0 -1
  258. package/dist/client/assets/channel-CaoKA_We.js +0 -1
  259. package/dist/client/assets/chunk-DSNCTWBM-8xkug7fQ.js +0 -2
  260. package/dist/client/assets/chunk-DU6HZSFF-COXDzJK1.js +0 -127
  261. package/dist/client/assets/chunk-R7TYR2AO-CTd0Yj98.js +0 -10
  262. package/dist/client/assets/classDiagram-DTDB5LWJ-7fEmn0zV.js +0 -1
  263. package/dist/client/assets/classDiagram-v2-JRS7N3AN-7fEmn0zV.js +0 -1
  264. package/dist/client/assets/comment-threads-9J6Em-_y.js +0 -4
  265. package/dist/client/assets/comment-ui-shared-DWkk9Vh3.js +0 -7
  266. package/dist/client/assets/diagram-VSXAHHWV-CQRVEZR7.js +0 -3
  267. package/dist/client/assets/dist-73RPdcyU.js +0 -1
  268. package/dist/client/assets/dist-CH-sQu3r.js +0 -1
  269. package/dist/client/assets/drawio-editor-BjrxTe3l.js +0 -9
  270. package/dist/client/assets/drawioEditor-DUVZjSSr.js +0 -2
  271. package/dist/client/assets/ebnfDiagram-PWID7BFC-OXa8VEp1.js +0 -1
  272. package/dist/client/assets/editor-session-DfZuRgB_.js +0 -54
  273. package/dist/client/assets/embedded-editor-base-DvMynhKu.css +0 -1
  274. package/dist/client/assets/excalidraw-editor-D7pnUCLH.js +0 -9
  275. package/dist/client/assets/excalidraw-mermaid-stub-BNckbX2n.js +0 -1
  276. package/dist/client/assets/excalidraw-mermaid-stub-DE5FTiZB.js +0 -1
  277. package/dist/client/assets/excalidrawEditor-B0jOK3lv.js +0 -2
  278. package/dist/client/assets/export-page-Bs3xE1fg.js +0 -57
  279. package/dist/client/assets/exportDocument-Dg9MjkIk.js +0 -2
  280. package/dist/client/assets/exportDocument-PnYU3v1s.css +0 -1
  281. package/dist/client/assets/file-kind-BbqgGzpc.js +0 -1
  282. package/dist/client/assets/flowDiagram-A5DVABFB-CiW1eN8C.js +0 -1
  283. package/dist/client/assets/image-ZVKINSPA-0zXSnE7M.js +0 -1
  284. package/dist/client/assets/index-BIpgxyGZ.css +0 -1
  285. package/dist/client/assets/index-Z5sI0Bj-.js +0 -2
  286. package/dist/client/assets/main-Dwnj9Gaq.js +0 -903
  287. package/dist/client/assets/pegDiagram-XKGWAZYB-B31fKPR2.js +0 -1
  288. package/dist/client/assets/preview-render-compiler-BApvlLLo.js +0 -35
  289. package/dist/client/assets/preview-render-compiler-BQ_3h9vD.js +0 -1
  290. package/dist/client/assets/preview-render-profile-CE-5rVTC.js +0 -1
  291. package/dist/client/assets/prod-BiYPMR1i.js +0 -177
  292. package/dist/client/assets/prod-VxdGZ5Ly.js +0 -1
  293. package/dist/client/assets/quick-switcher-controller-ButWPmCI.js +0 -12
  294. package/dist/client/assets/railroadDiagram-O6MQD6OU-BPifP0MB.js +0 -1
  295. package/dist/client/assets/runtime-config-BLrEKVO1.js +0 -1
  296. package/dist/client/assets/stateDiagram-v2-4QOOHH4V-B--gjrit.js +0 -1
  297. package/dist/client/assets/swimlanesDiagram-VK2B7HYN-DO8XNXEg.js +0 -8
  298. package/dist/client/assets/vault-api-client-_LpV1aIn.js +0 -6
  299. package/dist/client/assets/vault-utils-wdQ0RNR9.js +0 -1
  300. package/src/client/app/drawio-editor-entry.js +0 -1
  301. package/src/client/app/excalidraw-editor-entry.js +0 -1
  302. package/src/client/excalidraw-mermaid-stub.js +0 -7
  303. package/src/client/main.js +0 -16
  304. /package/dist/client/assets/{drawio-editor-UQQ8XHou.css → drawioEditor-UQQ8XHou.css} +0 -0
  305. /package/dist/client/assets/{sizeCapture-X5ZJPWSS-B0uUizjq.js → sizeCapture-INFHLROL-B0uUizjq.js} +0 -0
@@ -1,3 +1,6 @@
1
+ import { createAgentApiHandler } from './create-agent-api-handler.js';
2
+ import { createAgentMcpHandler } from '../mcp/create-agent-mcp-handler.js';
3
+ import { createAgentToolRateLimiter } from '../../shared/agent-tool-rate-limiter.js';
1
4
  import { createAuthApiHandler } from './create-auth-api-handler.js';
2
5
  import { createGitApiCommandHandler } from './create-git-api-command-handler.js';
3
6
  import { createGitApiQueryHandler } from './create-git-api-query-handler.js';
@@ -38,6 +41,7 @@ export function createRequestHandler(
38
41
  hostedWorkspaceService = null,
39
42
  githubSetupFlow = null,
40
43
  structurizrWorkspaceService = null,
44
+ agentIntegration = null,
41
45
  ) {
42
46
  const handleStaticRequest = createStaticHandler(config, authService, searchService);
43
47
  const handleAuthApi = createAuthApiHandler({ authService });
@@ -73,6 +77,22 @@ export function createRequestHandler(
73
77
  basePath: config.basePath,
74
78
  service: structurizrWorkspaceService,
75
79
  });
80
+ const agentToolRateLimiter = createAgentToolRateLimiter(config.agentAccess.requestsPerMinute);
81
+ const handleAgentApi = createAgentApiHandler({
82
+ agentConnectionService: agentIntegration?.connectionService,
83
+ agentContentService: agentIntegration?.contentService,
84
+ authService,
85
+ config,
86
+ rateLimiter: agentToolRateLimiter,
87
+ });
88
+ const handleAgentMcp = config.agentAccess.enabled && agentIntegration
89
+ ? createAgentMcpHandler({
90
+ agentConnectionService: agentIntegration.connectionService,
91
+ agentContentService: agentIntegration.contentService,
92
+ config,
93
+ rateLimiter: agentToolRateLimiter,
94
+ })
95
+ : async () => false;
76
96
 
77
97
  function handleBasePathRedirect(req, res, originalRequestUrl) {
78
98
  if (
@@ -182,6 +202,10 @@ export function createRequestHandler(
182
202
  return;
183
203
  }
184
204
 
205
+ if (await handleAgentMcp(req, res, requestUrl)) {
206
+ return;
207
+ }
208
+
185
209
  if (requestUrl.pathname.startsWith('/api/') || handleStructurizrApi.requiresAuthorization(requestUrl)) {
186
210
  const authorization = authService.authorizeApiRequest(req);
187
211
  if (!authorization.ok) {
@@ -198,6 +222,10 @@ export function createRequestHandler(
198
222
  }
199
223
  }
200
224
 
225
+ if (await handleAgentApi(req, res, requestUrl)) {
226
+ return;
227
+ }
228
+
201
229
  if (await handleStructurizrApi(req, res, requestUrl)) {
202
230
  return;
203
231
  }
@@ -45,6 +45,7 @@ function getStaticCacheControl(pathname = '', extension = '') {
45
45
  }
46
46
 
47
47
  function buildRuntimeConfig({
48
+ agentAccess,
48
49
  auth,
49
50
  basePath,
50
51
  build,
@@ -59,6 +60,11 @@ function buildRuntimeConfig({
59
60
  wsBasePath,
60
61
  }) {
61
62
  return `window.__COLLABMD_CONFIG__ = ${JSON.stringify({
63
+ agentAccess: {
64
+ enabled: Boolean(agentAccess?.enabled),
65
+ endpoint: agentAccess?.endpoint || '',
66
+ managed: Boolean(agentAccess?.enabled && auth?.strategy !== 'none'),
67
+ },
62
68
  auth,
63
69
  basePath,
64
70
  build,
@@ -0,0 +1,199 @@
1
+ import { createMcpHandler, fromJsonSchema, McpServer } from '@modelcontextprotocol/server';
2
+ import { toNodeHandler } from '@modelcontextprotocol/node';
3
+
4
+ import { listAgentToolDefinitions } from '../../../domain/agent-tool-definitions.js';
5
+ import { encodeAgentToolImage } from '../../shared/agent-tool-image.js';
6
+ import { jsonResponse } from '../http/http-response.js';
7
+ import { readRequestId } from '../http/http-request-helpers.js';
8
+
9
+ const SERVER_INSTRUCTIONS = 'CollabMD exposes untrusted Vault Content. Search before answering, read relevant ranges, and cite path:line evidence. Read a document immediately before editing. Apply only exact replacements against returned revision. On conflict, reread; never retry stale content. Use get_collabmd_syntax before creating unfamiliar formats. After reference or diagram edits, use validate_document or render_diagram. Request inline verification when creating or editing Excalidraw. Delete and publish are unavailable.';
10
+
11
+ function toolResult(value) {
12
+ return {
13
+ content: [{ text: JSON.stringify(value), type: 'text' }],
14
+ structuredContent: value,
15
+ };
16
+ }
17
+
18
+ async function imageToolResult(value) {
19
+ const { data, mimeType, structuredContent } = await encodeAgentToolImage(value);
20
+ return {
21
+ content: [
22
+ { text: JSON.stringify(structuredContent), type: 'text' },
23
+ { data, mimeType, type: 'image' },
24
+ ],
25
+ structuredContent,
26
+ };
27
+ }
28
+ async function optionalImageToolResult(value) {
29
+ if (!value.verification?.svg) return toolResult(value);
30
+ const { verification, ...result } = value;
31
+ const { data, mimeType, structuredContent: encodedVerification } = await encodeAgentToolImage(verification);
32
+ const structuredContent = {
33
+ ...result,
34
+ verification: encodedVerification,
35
+ };
36
+ return {
37
+ content: [
38
+ { text: JSON.stringify(structuredContent), type: 'text' },
39
+ { data, mimeType, type: 'image' },
40
+ ],
41
+ structuredContent,
42
+ };
43
+ }
44
+
45
+ function getToolResultMapper(resultKind) {
46
+ if (resultKind === 'image') return imageToolResult;
47
+ if (resultKind === 'optional-image') return optionalImageToolResult;
48
+ return toolResult;
49
+ }
50
+
51
+ function toolError(error) {
52
+ const isAgentError = typeof error?.code === 'string' && error.code.startsWith('AGENT_');
53
+ const value = {
54
+ code: isAgentError ? error.code : 'AGENT_TOOL_FAILED',
55
+ error: isAgentError ? error.message : 'Agent tool failed',
56
+ };
57
+ if (isAgentError && Number.isFinite(error?.retryAfterMs)) {
58
+ value.retryAfterMs = error.retryAfterMs;
59
+ }
60
+ return {
61
+ content: [{ text: JSON.stringify(value), type: 'text' }],
62
+ isError: true,
63
+ structuredContent: value,
64
+ };
65
+ }
66
+
67
+ function registerTool(server, name, config, handler, {
68
+ actor,
69
+ rateLimiter,
70
+ resultMapper = toolResult,
71
+ }) {
72
+ server.registerTool(name, config, async (input, context) => {
73
+ try {
74
+ rateLimiter(actor.rateLimitKey);
75
+ return await resultMapper(await handler(input, context));
76
+ } catch (error) {
77
+ if (typeof error?.code !== 'string' || !error.code.startsWith('AGENT_')) {
78
+ console.error(`[mcp] ${name} failed (${actor.requestId}):`, error?.message || 'Unknown error');
79
+ }
80
+ return toolError(error);
81
+ }
82
+ });
83
+ }
84
+
85
+ function buildAgentServer({ actor, agentContentService, rateLimiter, version }) {
86
+ const server = new McpServer(
87
+ { name: 'collabmd', version },
88
+ { instructions: SERVER_INSTRUCTIONS },
89
+ );
90
+ for (const definition of listAgentToolDefinitions(actor.scopes)) {
91
+ registerTool(server, definition.name, {
92
+ annotations: definition.annotations,
93
+ description: definition.description,
94
+ inputSchema: fromJsonSchema(definition.inputSchema),
95
+ outputSchema: fromJsonSchema(definition.outputSchema),
96
+ }, (input, context) => agentContentService[definition.method](actor, {
97
+ ...input,
98
+ signal: context.signal,
99
+ }), {
100
+ actor,
101
+ rateLimiter,
102
+ resultMapper: getToolResultMapper(definition.resultKind),
103
+ });
104
+ }
105
+ return server;
106
+ }
107
+
108
+ function readHttpHostname(value) {
109
+ try {
110
+ const url = new URL(value);
111
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') return '';
112
+ return url.hostname.replace(/^\[|\]$/gu, '').toLowerCase();
113
+ } catch {
114
+ return '';
115
+ }
116
+ }
117
+
118
+ function readRequestHostname(req) {
119
+ return readHttpHostname(`http://${String(req.headers.host || '')}`);
120
+ }
121
+
122
+ export function createAgentMcpHandler({
123
+ agentConnectionService,
124
+ agentContentService,
125
+ config,
126
+ rateLimiter,
127
+ }) {
128
+ const mcpHandler = createMcpHandler(({ authInfo }) => buildAgentServer({
129
+ actor: authInfo.extra.actor,
130
+ agentContentService,
131
+ rateLimiter,
132
+ version: config.build?.version || '0.1.0',
133
+ }));
134
+ const nodeHandler = toNodeHandler(mcpHandler, {
135
+ onerror: (error) => console.error('[mcp] Transport failed:', error.message),
136
+ });
137
+
138
+ return async function handleAgentMcp(req, res, requestUrl) {
139
+ if (requestUrl.pathname !== '/mcp') return false;
140
+ const authRequired = config.auth.strategy !== 'none';
141
+ const allowedHosts = config.agentAccess.allowedHosts;
142
+ const hostname = readRequestHostname(req);
143
+ const hasOrigin = Boolean(req.headers.origin);
144
+ const originHostname = hasOrigin ? readHttpHostname(String(req.headers.origin)) : '';
145
+ const browserOriginAllowed = hasOrigin
146
+ && hostname
147
+ && originHostname === hostname
148
+ && allowedHosts.includes(hostname);
149
+ if (
150
+ (authRequired && (!hostname || !allowedHosts.includes(hostname)))
151
+ || (hasOrigin && !browserOriginAllowed)
152
+ ) {
153
+ jsonResponse(req, res, 403, { error: 'MCP request origin or host is not allowed' });
154
+ return true;
155
+ }
156
+
157
+ let actor;
158
+ let token = '';
159
+ if (authRequired) {
160
+ const authorization = String(req.headers.authorization || '');
161
+ const match = authorization.match(/^Bearer\s+(.+)$/iu);
162
+ if (!match) {
163
+ res.setHeader('WWW-Authenticate', 'Bearer scope="vault:read"');
164
+ jsonResponse(req, res, 401, { error: 'Agent Connection token is required' });
165
+ return true;
166
+ }
167
+ token = match[1];
168
+ try {
169
+ actor = await agentConnectionService.authenticateToken(token);
170
+ } catch (error) {
171
+ res.setHeader('WWW-Authenticate', 'Bearer scope="vault:read"');
172
+ jsonResponse(req, res, Number(error?.statusCode) || 401, {
173
+ code: error?.code || 'AGENT_TOKEN_INVALID',
174
+ error: error?.message || 'Agent Connection token is invalid',
175
+ });
176
+ return true;
177
+ }
178
+ } else {
179
+ actor = {
180
+ collaborator: null,
181
+ connectionId: 'anonymous',
182
+ scopes: ['vault:edit', 'vault:read'],
183
+ };
184
+ }
185
+
186
+ actor.requestId = readRequestId(req);
187
+ actor.rateLimitKey = actor.connectionId === 'anonymous'
188
+ ? `anonymous:${req.socket?.remoteAddress || 'unknown'}`
189
+ : actor.connectionId;
190
+ req.auth = {
191
+ clientId: actor.connectionId,
192
+ extra: { actor },
193
+ scopes: actor.scopes,
194
+ token,
195
+ };
196
+ await nodeHandler(req, res);
197
+ return true;
198
+ };
199
+ }
@@ -0,0 +1,120 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+
4
+ function parseScopes(value) {
5
+ try {
6
+ const scopes = JSON.parse(value || '[]');
7
+ return Array.isArray(scopes) ? scopes : [];
8
+ } catch {
9
+ return [];
10
+ }
11
+ }
12
+
13
+ function rowToConnection(row) {
14
+ if (!row) return null;
15
+ return {
16
+ clientKind: row.client_kind,
17
+ createdAt: row.created_at,
18
+ expiresAt: row.expires_at,
19
+ id: row.id,
20
+ label: row.label,
21
+ revokedAt: row.revoked_at,
22
+ scopes: parseScopes(row.scopes_json),
23
+ subjectEmail: row.subject_email,
24
+ subjectId: row.subject_id,
25
+ subjectType: row.subject_type,
26
+ tokenHash: row.token_hash,
27
+ };
28
+ }
29
+
30
+ export class AgentConnectionStore {
31
+ constructor({ dbPath }) {
32
+ this.dbPath = dbPath;
33
+ this.db = null;
34
+ this.statements = new Map();
35
+ }
36
+
37
+ async initialize() {
38
+ if (this.db) return;
39
+ await mkdir(dirname(this.dbPath), { recursive: true });
40
+ const { DatabaseSync } = await import('node:sqlite');
41
+ this.db = new DatabaseSync(this.dbPath);
42
+ this.db.exec('PRAGMA journal_mode = WAL');
43
+ this.db.exec(`
44
+ CREATE TABLE IF NOT EXISTS agent_connections (
45
+ id TEXT PRIMARY KEY,
46
+ subject_type TEXT NOT NULL,
47
+ subject_id TEXT NOT NULL,
48
+ subject_email TEXT NOT NULL DEFAULT '',
49
+ label TEXT NOT NULL,
50
+ client_kind TEXT NOT NULL,
51
+ scopes_json TEXT NOT NULL,
52
+ token_hash TEXT NOT NULL UNIQUE,
53
+ created_at INTEGER NOT NULL,
54
+ expires_at INTEGER NOT NULL,
55
+ revoked_at INTEGER
56
+ );
57
+ CREATE INDEX IF NOT EXISTS agent_connections_subject_idx
58
+ ON agent_connections(subject_type, subject_id, created_at DESC);
59
+ `);
60
+ }
61
+
62
+ prepare(key, sql) {
63
+ if (!this.statements.has(key)) this.statements.set(key, this.db.prepare(sql));
64
+ return this.statements.get(key);
65
+ }
66
+
67
+ async close() {
68
+ this.statements.clear();
69
+ this.db?.close();
70
+ this.db = null;
71
+ }
72
+
73
+ async create(connection) {
74
+ this.prepare(
75
+ 'create',
76
+ `INSERT INTO agent_connections (
77
+ id, subject_type, subject_id, subject_email, label, client_kind,
78
+ scopes_json, token_hash, created_at, expires_at, revoked_at
79
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`,
80
+ ).run(
81
+ connection.id,
82
+ connection.subjectType,
83
+ connection.subjectId,
84
+ connection.subjectEmail,
85
+ connection.label,
86
+ connection.clientKind,
87
+ JSON.stringify(connection.scopes),
88
+ connection.tokenHash,
89
+ connection.createdAt,
90
+ connection.expiresAt,
91
+ );
92
+ return connection;
93
+ }
94
+
95
+ async getByTokenHash(tokenHash) {
96
+ return rowToConnection(this.prepare(
97
+ 'getByTokenHash',
98
+ 'SELECT * FROM agent_connections WHERE token_hash = ? LIMIT 1',
99
+ ).get(tokenHash));
100
+ }
101
+
102
+ async listBySubject(subjectType, subjectId) {
103
+ return this.prepare(
104
+ 'listBySubject',
105
+ `SELECT * FROM agent_connections
106
+ WHERE subject_type = ? AND subject_id = ? AND revoked_at IS NULL
107
+ ORDER BY created_at DESC`,
108
+ ).all(subjectType, subjectId).map(rowToConnection);
109
+ }
110
+
111
+ async revoke(id, subjectType, subjectId, revokedAt) {
112
+ const result = this.prepare(
113
+ 'revoke',
114
+ `UPDATE agent_connections SET revoked_at = ?
115
+ WHERE id = ? AND subject_type = ? AND subject_id = ? AND revoked_at IS NULL`,
116
+ ).run(revokedAt, id, subjectType, subjectId);
117
+ return Number(result.changes) > 0;
118
+ }
119
+
120
+ }
@@ -3,7 +3,7 @@ import { isAbsolute, normalize, relative, resolve } from 'path';
3
3
  import { isVaultFilePath } from '../../../domain/file-kind.js';
4
4
 
5
5
  export const IGNORED_DIRECTORIES = new Set(['.git', '.obsidian', '.trash', 'node_modules', '.DS_Store']);
6
- export const VAULT_FILE_PATH_REQUIREMENT = '.md, .markdown, .mdx, .base, .excalidraw, .drawio, .mmd, .mermaid, .puml, .plantuml, .dsl, .png, .jpg, .jpeg, .webp, .gif, or .svg';
6
+ export const VAULT_FILE_PATH_REQUIREMENT = '.md, .markdown, .mdx, .html, .htm, .base, .excalidraw, .drawio, .mmd, .mermaid, .puml, .plantuml, .dsl, .pdf, .png, .jpg, .jpeg, .webp, .gif, or .svg';
7
7
  export const INVALID_VAULT_FILE_PATH_ERROR = `Invalid file path — must end in ${VAULT_FILE_PATH_REQUIREMENT}`;
8
8
  export const INVALID_DIRECTORY_PATH_ERROR = 'Invalid directory path';
9
9
 
@@ -12,12 +12,18 @@ export function isIgnoredVaultEntry(name) {
12
12
  }
13
13
 
14
14
  function normalizeRequestedPath(requestedPath) {
15
- const normalized = normalize(String(requestedPath ?? '').trim().replace(/\\/g, '/'));
16
- if (!normalized || normalized === '.') {
15
+ const value = String(requestedPath ?? '').trim().replace(/\\/g, '/');
16
+ const segments = value.split('/');
17
+ if (
18
+ !value
19
+ || isAbsolute(value)
20
+ || segments.some((segment) => segment === '.' || segment === '..')
21
+ ) {
17
22
  return '';
18
23
  }
19
24
 
20
- return normalized;
25
+ const normalized = normalize(value);
26
+ return normalized === '.' ? '' : normalized;
21
27
  }
22
28
 
23
29
  export function sanitizeVaultPath(vaultDir, requestedPath) {
@@ -1,6 +1,7 @@
1
1
  import { copyFile, mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
2
2
  import { dirname, join, resolve } from 'node:path';
3
3
 
4
+ import { ensureCollabMetadataGitExclude } from '../git/local-exclude.js';
4
5
  import { sanitizeVaultPath } from './path-utils.js';
5
6
 
6
7
  const PULL_BACKUP_STORAGE_ROOT = '.collabmd/pull-backups';
@@ -98,32 +99,6 @@ function formatSummary({
98
99
  ].join('\n');
99
100
  }
100
101
 
101
- async function ensureLocalGitExclude(vaultDir) {
102
- const excludePath = resolve(vaultDir, '.git/info/exclude');
103
- let existingContent = '';
104
-
105
- try {
106
- existingContent = await readFile(excludePath, 'utf8');
107
- } catch (error) {
108
- if (error?.code !== 'ENOENT') {
109
- throw error;
110
- }
111
- }
112
-
113
- const lines = existingContent
114
- .split(/\r?\n/u)
115
- .map((line) => line.trim())
116
- .filter(Boolean);
117
- if (lines.includes('.collabmd') || lines.includes('.collabmd/')) {
118
- return;
119
- }
120
-
121
- const prefix = existingContent.length > 0 && !existingContent.endsWith('\n')
122
- ? '\n'
123
- : '';
124
- await writeFile(excludePath, `${existingContent}${prefix}.collabmd/\n`, 'utf8');
125
- }
126
-
127
102
  export class PullBackupStore {
128
103
  constructor({ vaultDir }) {
129
104
  this.vaultDir = resolve(vaultDir);
@@ -148,7 +123,7 @@ export class PullBackupStore {
148
123
  headRef = null,
149
124
  targetRef = null,
150
125
  } = {}) {
151
- await ensureLocalGitExclude(this.vaultDir);
126
+ await ensureCollabMetadataGitExclude(this.vaultDir);
152
127
 
153
128
  const backupId = createBackupId(headRef, createdAt);
154
129
  const backupDir = this.getBackupPath(backupId);
@@ -1,5 +1,5 @@
1
1
  import { createReadStream } from 'fs';
2
- import { mkdir, readFile, readdir, rename, rm, rmdir, stat, writeFile } from 'fs/promises';
2
+ import { copyFile, mkdir, readFile, readdir, rename, rm, rmdir, stat, writeFile } from 'fs/promises';
3
3
  import { basename, dirname, extname, join, relative, resolve } from 'path';
4
4
  import sharp from 'sharp';
5
5
 
@@ -28,6 +28,7 @@ const EDITABLE_VAULT_CONTENT_PATH_ERRORS = {
28
28
  base: 'Invalid file path — must end in .base',
29
29
  drawio: 'Invalid file path — must end in .drawio',
30
30
  excalidraw: 'Invalid file path — must end in .excalidraw',
31
+ html: 'Invalid file path — must end in .html or .htm',
31
32
  markdown: 'Invalid file path',
32
33
  mermaid: 'Invalid file path — must end in .mmd or .mermaid',
33
34
  plantuml: 'Invalid file path — must end in .puml or .plantuml',
@@ -66,6 +67,7 @@ const TEXT_FILE_MIME_TYPES = Object.freeze({
66
67
  base: 'text/yaml; charset=utf-8',
67
68
  drawio: 'application/xml; charset=utf-8',
68
69
  excalidraw: 'application/json; charset=utf-8',
70
+ html: 'text/html; charset=utf-8',
69
71
  markdown: 'text/markdown; charset=utf-8',
70
72
  mermaid: 'text/plain; charset=utf-8',
71
73
  pdf: 'application/pdf',
@@ -706,7 +708,11 @@ export class VaultFileStore {
706
708
  : null;
707
709
 
708
710
  if (backupPath) {
709
- await rename(operation.targetPath, backupPath);
711
+ if (operation.kind === 'write') {
712
+ await copyFile(operation.targetPath, backupPath);
713
+ } else {
714
+ await rename(operation.targetPath, backupPath);
715
+ }
710
716
  }
711
717
 
712
718
  try {
@@ -719,6 +725,9 @@ export class VaultFileStore {
719
725
  }
720
726
  } catch (error) {
721
727
  if (backupPath) {
728
+ if (operation.kind === 'write') {
729
+ await rm(operation.targetPath, { force: true });
730
+ }
722
731
  await rename(backupPath, operation.targetPath);
723
732
  }
724
733
  throw error;
@@ -0,0 +1,22 @@
1
+ import sharp from 'sharp';
2
+
3
+ export async function encodeAgentToolImage(value) {
4
+ const { format, svg } = value;
5
+ const metadata = { ...value };
6
+ delete metadata.format;
7
+ delete metadata.scene;
8
+ delete metadata.svg;
9
+ const mimeType = format === 'svg' ? 'image/svg+xml' : 'image/png';
10
+ const image = format === 'svg'
11
+ ? Buffer.from(svg)
12
+ : await sharp(Buffer.from(svg)).png().toBuffer();
13
+ return {
14
+ data: image.toString('base64'),
15
+ mimeType,
16
+ structuredContent: {
17
+ ...metadata,
18
+ format,
19
+ mimeType,
20
+ },
21
+ };
22
+ }
@@ -0,0 +1,20 @@
1
+ export function createAgentToolRateLimiter(requestsPerMinute) {
2
+ const limit = Math.max(1, Number.parseInt(requestsPerMinute, 10) || 120);
3
+ const windows = new Map();
4
+ return (key) => {
5
+ const timestamp = Date.now();
6
+ let window = windows.get(key);
7
+ if (!window || timestamp >= window.resetAt) {
8
+ window = { count: 0, resetAt: timestamp + 60_000 };
9
+ windows.set(key, window);
10
+ }
11
+ if (window.count >= limit) {
12
+ const error = new Error('Agent tool request limit reached; retry after the current window');
13
+ error.code = 'AGENT_RATE_LIMITED';
14
+ error.retryAfterMs = Math.max(1, window.resetAt - timestamp);
15
+ error.statusCode = 429;
16
+ throw error;
17
+ }
18
+ window.count += 1;
19
+ };
20
+ }
@@ -4,6 +4,8 @@ import { tmpdir } from 'node:os';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { promisify } from 'node:util';
6
6
 
7
+ import { ensureCollabMetadataGitExclude } from '../infrastructure/git/local-exclude.js';
8
+
7
9
  const execFile = promisify(execFileCallback);
8
10
  const GIT_TIMEOUT_MS = 20_000;
9
11
  const GIT_MAX_BUFFER_BYTES = 5 * 1024 * 1024;
@@ -150,36 +152,6 @@ async function cloneIntoVault(vaultDir, repoUrl, options = {}) {
150
152
  });
151
153
  }
152
154
 
153
- async function ensureLocalGitIgnore(vaultDir) {
154
- const excludeFilePath = join(vaultDir, '.git', 'info', 'exclude');
155
- let existingContent = '';
156
-
157
- try {
158
- existingContent = await readFile(excludeFilePath, 'utf8');
159
- } catch (error) {
160
- if (error?.code !== 'ENOENT') {
161
- throw error;
162
- }
163
- }
164
-
165
- const lines = existingContent
166
- .split(/\r?\n/u)
167
- .map((line) => line.trim());
168
-
169
- if (lines.includes('.collabmd') || lines.includes('.collabmd/')) {
170
- return;
171
- }
172
-
173
- const prefix = existingContent.length > 0 && !existingContent.endsWith('\n')
174
- ? '\n'
175
- : '';
176
- await writeFile(
177
- excludeFilePath,
178
- `${existingContent}${prefix}.collabmd/\n`,
179
- 'utf8',
180
- );
181
- }
182
-
183
155
  async function updateExistingCheckout(vaultDir, repoUrl, options = {}) {
184
156
  const originUrl = await getOriginUrl(vaultDir, options);
185
157
  if (originUrl !== repoUrl) {
@@ -384,7 +356,7 @@ export async function prepareConfigForStartup(config, options = {}) {
384
356
  ...options,
385
357
  commandEnv: config.git.commandEnv,
386
358
  });
387
- await ensureLocalGitIgnore(config.vaultDir);
359
+ await ensureCollabMetadataGitExclude(config.vaultDir);
388
360
  await ensureRepositoryIdentity(config.vaultDir, config.git, {
389
361
  ...options,
390
362
  commandEnv: config.git.commandEnv,
@@ -404,7 +376,7 @@ export async function prepareConfigForStartup(config, options = {}) {
404
376
  ...options,
405
377
  commandEnv: config.git.commandEnv,
406
378
  });
407
- await ensureLocalGitIgnore(config.vaultDir);
379
+ await ensureCollabMetadataGitExclude(config.vaultDir);
408
380
  await ensureRepositoryIdentity(config.vaultDir, config.git, {
409
381
  ...options,
410
382
  commandEnv: config.git.commandEnv,
@@ -422,7 +394,7 @@ export async function prepareConfigForStartup(config, options = {}) {
422
394
  ...options,
423
395
  commandEnv: config.git.commandEnv,
424
396
  });
425
- await ensureLocalGitIgnore(config.vaultDir);
397
+ await ensureCollabMetadataGitExclude(config.vaultDir);
426
398
  await ensureRepositoryIdentity(config.vaultDir, config.git, {
427
399
  ...options,
428
400
  commandEnv: config.git.commandEnv,
@@ -1 +0,0 @@
1
- import{i as e}from"./rolldown-runtime-Dd_uD5pT.js";import{B as t,L as n,N as r,Q as i,R as a,W as o,Z as s,c,dt as l,et as u,pt as d,t as f}from"./dist-RKBTaQMb.js";import"./chunk-SRAX5OIU-KFQikY-4.js";import{n as p,t as m}from"./jsx-runtime-BpzPEenQ.js";import{a as h,o as g,p as _,s as v}from"./dist-ByrG2qBA.js";var y=e(p(),1),b=m(),x=c.define({token(e){return e.match(/^%%.*$/)?`comment`:e.match(/^"(?:[^"\\]|\\.)*"/)?`string`:e.match(/^(flowchart|graph|sequenceDiagram|classDiagram|stateDiagram|erDiagram|gantt|pie|mindmap|journey|gitGraph|timeline|quadrantChart|sankey|xychart)\b/i)||e.match(/^(TB|TD|BT|RL|LR)\b/)||e.match(/^(subgraph|end|participant|actor|loop|alt|else|opt|par|critical|break|rect|note|over|activate|deactivate|title|section|class|style|linkStyle|classDef|click)\b/i)?`keyword`:e.match(/^[-.=<>|ox]+>/)||e.match(/^<[-.=<>|ox]+/)||e.match(/^--+|\.\.+|==+/)?`operator`:e.match(/^[[\](){}|<>]/)?`bracket`:e.match(/^[A-Za-z_][A-Za-z0-9_]*/)?`variableName`:e.match(/^\d+(\.\d+)?/)?`number`:e.match(/^[,:;]/)?`punctuation`:(e.eatSpace()||e.next(),null)}});function S(){return x}var C=t.theme({"&":{backgroundColor:`#1e1e1e`,color:`#d4d4d4`},".cm-content":{caretColor:`#fff`},".cm-cursor":{borderLeftColor:`#fff`},".cm-gutters":{backgroundColor:`#1e1e1e`,color:`#858585`,border:`none`},".cm-activeLineGutter":{backgroundColor:`#2a2a2a`},".cm-activeLine":{backgroundColor:`#2a2a2a`},".cm-errorLine":{backgroundColor:`rgba(255, 0, 0, 0.15)`}},{dark:!0}),w=f.define([{tag:n.keyword,color:`#569cd6`},{tag:n.string,color:`#ce9178`},{tag:n.comment,color:`#6a9955`},{tag:n.number,color:`#b5cea8`},{tag:n.operator,color:`#d4d4d4`},{tag:n.punctuation,color:`#d4d4d4`},{tag:n.variableName,color:`#9cdcfe`},{tag:n.bracket,color:`#ffd700`}]),T=t.theme({"&":{backgroundColor:`#ffffff`,color:`#1e1e1e`},".cm-content":{caretColor:`#000`},".cm-cursor":{borderLeftColor:`#000`},".cm-gutters":{backgroundColor:`#fff`,color:`#999`,border:`none`},".cm-activeLineGutter":{backgroundColor:`#e8e8e8`},".cm-activeLine":{backgroundColor:`#e8e8e8`},".cm-errorLine":{backgroundColor:`rgba(255, 0, 0, 0.1)`}}),E=f.define([{tag:n.keyword,color:`#0000ff`},{tag:n.string,color:`#a31515`},{tag:n.comment,color:`#008000`},{tag:n.number,color:`#098658`},{tag:n.operator,color:`#1e1e1e`},{tag:n.punctuation,color:`#1e1e1e`},{tag:n.variableName,color:`#001080`},{tag:n.bracket,color:`#af00db`}]),D=a.line({class:`cm-errorLine`}),O=(e,n)=>{if(!e||e<1||e>n.lines)return t.decorations.of(a.none);let r=n.line(e);return t.decorations.of(a.set([D.range(r.from)]))},k=e=>e===`dark`?[C,r(w)]:[T,r(E)],A=({value:e,onChange:n,onKeyboardSubmit:r,placeholder:a,theme:c,errorLine:f})=>{let p=(0,y.useRef)(null),m=(0,y.useRef)(null),x=(0,y.useRef)(n),C=(0,y.useRef)(r),w=(0,y.useRef)(new l),T=(0,y.useRef)(new l);return x.current=n,C.current=r,(0,y.useEffect)(()=>{if(!p.current)return;let n=w.current,r=new t({state:d.create({doc:e,extensions:[s.of([{key:`Mod-Enter`,run:()=>(C.current?.(),!0)},{key:`Mod-Shift-z`,run:_,preventDefault:!0}]),t.updateListener.of(e=>{e.docChanged&&x.current(e.state.doc.toString())}),g(),s.of([...h,...v]),i(),t.lineWrapping,n.of(k(c)),T.current.of([]),S(),o({drawRangeCursor:!0}),...a?[u(a)]:[]]}),parent:p.current});return m.current=r,r.focus(),()=>{r.destroy(),m.current=null}},[]),(0,y.useEffect)(()=>{let e=m.current;e&&e.dispatch({effects:w.current.reconfigure(k(c))})},[c]),(0,y.useEffect)(()=>{let e=m.current;e&&e.dispatch({effects:T.current.reconfigure(O(f,e.state.doc))})},[f]),(0,y.useEffect)(()=>{let t=m.current;if(!t)return;let n=t.state.doc.toString();e!==n&&t.dispatch({changes:{from:0,to:n.length,insert:e}})},[e]),(0,b.jsx)(`div`,{ref:p,className:`ttd-dialog-input ttd-dialog-input--codemirror`})};export{A as default};
@@ -1 +0,0 @@
1
- import{n as e}from"./chunk-XHIXRSVI-BFQ6B8BE.js";import{n as t}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as n}from"./src-oBChb5qS.js";import"./chunk-DU6HZSFF-COXDzJK1.js";import"./chunk-CLGD4ZFX-DQLgbQrv.js";import{n as r,r as i,t as a}from"./chunk-SVP7TREG-CyHOmcac.js";import{t as o}from"./chunk-JWPE2WC7-DVXcaiue.js";import{t as s}from"./mermaid-parser.core-Bl0efOMt.js";var c=e().RailroadAbnf.parser.LangiumParser,l=t(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=t(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=t(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=t(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=t(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=t(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=t(e=>{o(e,a),e.title&&a.setTitle(e.title),e.rules.map(e=>a.addRule(m(e)))},`populateDb`),g={parser:{parse:t(e=>{a.clear(),n.debug(`[ABNF Parser] Starting Langium parse`);let t=c.parse(e);if(t.lexerErrors.length>0||t.parserErrors.length>0)throw new s(t);let r=t.value;n.debug(`[ABNF Parser] Parsed rules:`,r.rules.length),h(r),n.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:a}},db:a,renderer:i,styles:r};export{g as diagram};
@@ -1 +0,0 @@
1
- function e(){return typeof window>`u`?globalThis:window}function t({accept:t=``,multiple:n=!1}={}){return new Promise(r=>{let i=document.createElement(`input`),a=e();i.type=`file`,i.accept=t,i.multiple=n,i.style.position=`fixed`,i.style.left=`-9999px`,document.body.appendChild(i);let o=!1,s=null,c=(e=[])=>{o||(o=!0,s&&a.clearTimeout(s),a.removeEventListener(`focus`,l),i.remove(),r(Array.from(e??[])))},l=()=>{s=a.setTimeout(()=>{o||i.files?.length||c()},250)};i.addEventListener(`change`,()=>c(i.files),{once:!0}),i.addEventListener(`cancel`,()=>c(),{once:!0}),a.addEventListener(`focus`,l,{once:!0}),i.click()})}function n(t,n){let r=e();return typeof r.requestIdleCallback==`function`?r.requestIdleCallback(t,{timeout:n}):r.setTimeout(()=>t({didTimeout:!1,timeRemaining:()=>0}),1)}function r(t){if(t===null)return;let n=e();if(typeof n.cancelIdleCallback==`function`){n.cancelIdleCallback(t);return}n.clearTimeout(t)}function i(e,t,n){if(!e||!t)return!1;let r=t.getBoundingClientRect(),i=e.getBoundingClientRect();return i.bottom>=r.top-n&&i.top<=r.bottom+n}function a(t,n,{removeDelayMs:r=3e4,revokeDelayMs:i=r}={}){let a=e(),o=URL.createObjectURL(t),s=document.createElement(`a`);s.href=o,s.download=n,s.style&&(s.style.display=`none`),document.body.appendChild(s),s.click(),r>0?a.setTimeout(()=>s.remove(),r):s.remove(),a.setTimeout(()=>URL.revokeObjectURL(o),i)}function o(e=``,t=`download`){let n=String(e).match(/filename\*=UTF-8''([^;]+)/iu);if(n)try{return decodeURIComponent(n[1])}catch{return t}return String(e).match(/filename="([^"]+)"/iu)?.[1]||t}export{t as a,o as i,a as n,n as o,i as r,r as t};
@@ -1 +0,0 @@
1
- import{at as e,ot as t}from"./chunk-DU6HZSFF-COXDzJK1.js";var n=(n,r)=>t.lang.round(e.parse(n)[r]);export{n as t};