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.
- package/README.md +70 -11
- package/bin/collabmd.js +3 -0
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-C-nmgL5g.js +1 -0
- package/dist/client/assets/JetBrainsMono-Regular-BQaDgvhP.woff2 +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-CdVZqicp.js +1 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-I39sYjHI.js → architectureDiagram-5GKGNRK7-wUo0bXif.js} +3 -3
- package/dist/client/assets/babel-CY5KOv-I.js +16 -0
- package/dist/client/assets/{blockDiagram-NRAW4CY4-C6DtJy5Y.js → blockDiagram-I7D4REHJ-BRSK3m49.js} +4 -4
- package/dist/client/assets/browser-utils-DbXSMI-6.js +1 -0
- package/dist/client/assets/{c4Diagram-UCG6FXSJ-CtWtxS9_.js → c4Diagram-7LVT6UL2-yD2poxqw.js} +1 -1
- package/dist/client/assets/channel-Fa4GS_-F.js +1 -0
- package/dist/client/assets/chunk-2E4U76K2-BPnTT-J3.js +2 -0
- package/dist/client/assets/{chunk-TEH6E4GO-DVayPj-q.js → chunk-4HAMMTFA-DStbIfJ1.js} +20 -20
- package/dist/client/assets/{chunk-75Z2AOVW-CdFSaPjz.js → chunk-75Z2AOVW-C5pGd3W9.js} +1 -1
- package/dist/client/assets/{chunk-CLGD4ZFX-DQLgbQrv.js → chunk-CLGD4ZFX-BMeArk03.js} +1 -1
- package/dist/client/assets/chunk-DU6HZSFF-Zkp69c6c.js +127 -0
- package/dist/client/assets/{chunk-F27PBJKO-CNHIFhjo.js → chunk-F27PBJKO-D4xNct1r.js} +1 -1
- package/dist/client/assets/{chunk-GMAD6QVW-CbcBqPM5.js → chunk-GMAD6QVW-NZ6WpDb8.js} +1 -1
- package/dist/client/assets/{chunk-OBVCFTLP-BJR7u1jh.js → chunk-GVQU2GXP-D8SDprfX.js} +1 -1
- package/dist/client/assets/{chunk-G27WJ6UU-D9-eg537.js → chunk-IMKFNOWR-D8jEuU3Q.js} +2 -2
- package/dist/client/assets/{chunk-JQ64N6SF-DSFNH4bh.js → chunk-L3NEJ4N5-aijKP-eh.js} +1 -1
- package/dist/client/assets/chunk-OSK3NFVY-CCbLQAMa.js +10 -0
- package/dist/client/assets/{chunk-P2QGCYS3-BRpqY32H.js → chunk-P2QGCYS3-Oag_FdCq.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-Cb4SlVZF.js → chunk-POPQ4Y6H-CYnpIgXT.js} +1 -1
- package/dist/client/assets/{chunk-PWAF6VOD-Cz8IwBB5.js → chunk-PWAF6VOD-JSNrcBmP.js} +1 -1
- package/dist/client/assets/{chunk-RM4UPSZO-CWRwxNWJ.js → chunk-RM4UPSZO-DOWllDY8.js} +4 -4
- package/dist/client/assets/{chunk-RHFEMEQ7-CWY8cf4H.js → chunk-SHT3W25Y-BiQGljao.js} +5 -5
- package/dist/client/assets/{chunk-SVP7TREG-CyHOmcac.js → chunk-SVP7TREG-DB485R5x.js} +1 -1
- package/dist/client/assets/{chunk-LCL6LL3I-DmDkVukh.js → chunk-TICWLB2K-8rAVvoS3.js} +4 -4
- package/dist/client/assets/{chunk-CKBBP62Z-DG26UiBv.js → chunk-TLUHSLCS-DTY5v0aj.js} +2 -2
- package/dist/client/assets/classDiagram-ZZMXUADV-BsQWoR3Q.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-BsQWoR3Q.js +1 -0
- package/dist/client/assets/{comment-thread-store-CdcwrObc.js → comment-thread-store-CX98zR6q.js} +1 -1
- package/dist/client/assets/comment-threads-BnRwo5B9.js +4 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CdTPi2yO.js → cynefinDiagram-5FMLGOSQ-rTz9RhFH.js} +1 -1
- package/dist/client/assets/{dagre-3AP2YEHR-5gtJtd0O.js → dagre-GXQ25YYZ-Cr67wLpQ.js} +2 -2
- package/dist/client/assets/{diagram-S7CK7UJ4-BudWz9WU.js → diagram-S7CK7UJ4-DywERe__.js} +2 -2
- package/dist/client/assets/{diagram-UQ7AKVKN-3v4rbH-s.js → diagram-UQ7AKVKN-CNYlXtu4.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-0H1a61Bn.js +3 -0
- package/dist/client/assets/{diagram-VX7I27RA-C_-rj0sw.js → diagram-VX7I27RA-CsJbsx5F.js} +1 -1
- package/dist/client/assets/{diagram-Z3DM3KII-B4yxavgj.js → diagram-Z3DM3KII-D0mLxEgI.js} +1 -1
- package/dist/client/assets/dist--hk9I5HQ.js +1 -0
- package/dist/client/assets/{dist-DutPuFmC.js → dist-B1XEa-Zs.js} +1 -1
- package/dist/client/assets/dist-Bd1JucQF.js +1 -0
- package/dist/client/assets/{dist-DZAAWTG-.js → dist-C0G3AaXG.js} +1 -1
- package/dist/client/assets/dist-ChRoo3IB.js +9 -0
- package/dist/client/assets/dist-Dm4w5UId.js +1 -0
- package/dist/client/assets/{dist-B_yQaEv_.js → dist-DpYDHR9p.js} +1 -1
- package/dist/client/assets/{dist-CietwTVS.js → dist-DzNmC81X.js} +1 -1
- package/dist/client/assets/{dist-DDUn5zTz.js → dist-GvvCNBe4.js} +1 -1
- package/dist/client/assets/dist-JaU3aRlD.js +19 -0
- package/dist/client/assets/{dist-Bw3VIE_v.js → dist-TW8iFRyW.js} +1 -1
- package/dist/client/assets/{dist-Dir5dDKZ.js → dist-jVU5Be5P.js} +1 -1
- package/dist/client/assets/document-formatter-rDDjRfZ2.js +2 -0
- package/dist/client/assets/drawioEditor-JubEEha7.js +9 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-HeTxHpCU.js +1 -0
- package/dist/client/assets/editor-session-Bk6oIvSr.js +54 -0
- package/dist/client/assets/embedded-editor-base-BlCgz6mw.css +1 -0
- package/dist/client/assets/{erDiagram-SSCWMZ5O-C-iy6VkX.js → erDiagram-RLTQ6QDP-CbrDdd5P.js} +2 -2
- package/dist/client/assets/{es-CAsWR6hG.js → es-Bfdp096f.js} +13 -13
- package/dist/client/assets/estree-CuT_pB8K.js +44 -0
- package/dist/client/assets/excalidraw-element-link-YLIaTnVF.js +7 -0
- package/dist/client/assets/{excalidraw-scene-BOben2gr.js → excalidraw-scene-6uROebca.js} +1 -1
- package/dist/client/assets/{excalidraw-editor-BlcN6umJ.css → excalidrawEditor-BbSbI80a.css} +1 -1
- package/dist/client/assets/excalidrawEditor-dBMbRX_S.js +9 -0
- package/dist/client/assets/export-page-CRRQ2NEl.js +57 -0
- package/dist/client/assets/exportDocument-BlQRd9_m.js +2 -0
- package/dist/client/assets/exportDocument-CNSbjZwu.css +1 -0
- package/dist/client/assets/{file-icon-svg-CQAjlWjK.js → file-icon-svg-Y_kpF957.js} +1 -1
- package/dist/client/assets/file-kind-BwMIcZbi.js +1 -0
- package/dist/client/assets/file-search-DDLyyLDU.js +1 -0
- package/dist/client/assets/flowDiagram-HODETNUW-D5Gy0wmg.js +1 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-DtKHAbez.js → ganttDiagram-EL5Y4UJY-BHgOYiZz.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-oue8fQ5o.js → gitGraphDiagram-WWUBYQGX-Dl__X3Sk.js} +1 -1
- package/dist/client/assets/html-Cdnr2qzU.js +25 -0
- package/dist/client/assets/image-ZVKINSPA-DgzW5zAy.js +1 -0
- package/dist/client/assets/index-D3RER1SZ.js +1023 -0
- package/dist/client/assets/index-DREVKl_K.css +1 -0
- package/dist/client/assets/{infoDiagram-RXCK75RN-BMzo6KIn.js → infoDiagram-27XIBGKW-DBpoANC9.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-BIM3h287.js → ishikawaDiagram-5VMMS53U-BMPC6hfp.js} +1 -1
- package/dist/client/assets/{journeyDiagram-EYS64GPL-mF6Lh8O-.js → journeyDiagram-3NMN7TZE-B1sApUmM.js} +2 -2
- package/dist/client/assets/{kanban-definition-3QL26DDD-DLN2NyoR.js → kanban-definition-UXKFOSKX-C9yP4td1.js} +4 -4
- package/dist/client/assets/{line-BfTWJmgf.js → line-BFl3ujec.js} +1 -1
- package/dist/client/assets/markdown-CByV-4CU.js +70 -0
- package/dist/client/assets/{mermaid.core-CLZBlpIW.js → mermaid.core-BrY09czX.js} +5 -5
- package/dist/client/assets/mermaid.core-DfcNfOTv.js +1 -0
- package/dist/client/assets/{mindmap-definition-FBJOCRG2-CRyHaHTk.js → mindmap-definition-YA3MSWOX-BUQHcYbK.js} +8 -8
- package/dist/client/assets/pegDiagram-XKGWAZYB-Bi2Iml7Z.js +1 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-B5OZPb88.js → pieDiagram-E7YTZNPT-_gYXnMoi.js} +1 -1
- package/dist/client/assets/preview-render-compiler-CfxJYldJ.js +41 -0
- package/dist/client/assets/preview-render-compiler-Cu8_i9iS.js +1 -0
- package/dist/client/assets/{preview-render-worker-BmN79bVc.js → preview-render-worker-c4Yi93qW.js} +41 -35
- package/dist/client/assets/prod-BiJL30sl.js +1 -0
- package/dist/client/assets/prod-qAk7rHa4.js +177 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-Drrw14YH.js → quadrantDiagram-AXDQQJYC-DwexhEhd.js} +1 -1
- package/dist/client/assets/quick-switcher-controller-BoIK_wuG.js +1 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BVA5UbMu.js +1 -0
- package/dist/client/assets/{requirementDiagram-EFPCY7ZU-CRZG6mRB.js → requirementDiagram-BXWQKSXE-C4cKwiIw.js} +2 -2
- package/dist/client/assets/runtime-config-DcmnGcXU.js +1 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-DvarEJpo.js → sankeyDiagram-P5KCCOFB-B5HHkRT2.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-B4oWlb-w.js → sequenceDiagram-WJ2MYXX4-BX6jqJFD.js} +6 -6
- package/dist/client/assets/standalone-6z5x8CWe.js +29 -0
- package/dist/client/assets/{stateDiagram-HBIQ2CUA-qbHIIqRZ.js → stateDiagram-D77RDMKH-CmqxyRGx.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-zoLLQVyh.js +1 -0
- package/dist/client/assets/{swimlanes-XN3QIQJK-xB6CtUGj.js → swimlanes-42K2YHIH-Bmgb9Zld.js} +1 -1
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DS-KQN37.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-Lj_yTNlY.js → timeline-definition-24CTP7MA-BA-lVH6P.js} +1 -1
- package/dist/client/assets/vault-api-client-iZLhMJKe.js +6 -0
- package/dist/client/assets/{vennDiagram-4TSXK5OY-BekRp5Y0.js → vennDiagram-4TSXK5OY-DNdUC_Xj.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-kdnV_Hdm.js → wardleyDiagram-VM6X3IG4-C2oY-YbF.js} +1 -1
- package/dist/client/assets/wiki-link-resolver-DFIwErjc.js +1 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-DufGwNSB.js → xychartDiagram-S5SC5T6Z-CoIR8CBX.js} +1 -1
- package/dist/client/assets/yaml-9ltHPDNO.js +154 -0
- package/dist/client/assets/{yjs-provider-reset-guard-DE0vINDP.js → yjs-provider-reset-guard-9ul1qF6k.js} +1 -1
- package/dist/client/drawio-editor.html +11 -3
- package/dist/client/excalidraw-editor.html +45 -2
- package/dist/client/export-document.html +3 -3
- package/dist/client/index.html +106 -52
- package/integrations/pi/index.ts +94 -0
- package/package.json +41 -29
- package/src/client/app/drawio-editor.html +2 -2
- package/src/client/app/excalidraw-editor.html +2 -2
- package/src/client/app/export-document.html +1 -1
- package/src/client/app/index.html +88 -50
- package/src/client/app/main-entry.js +15 -1
- package/src/client/application/app-shell/chat-feature.js +7 -22
- package/src/client/application/app-shell/export-feature.js +17 -1
- package/src/client/application/app-shell/git-feature.js +51 -14
- package/src/client/application/app-shell/presence-feature.js +8 -25
- package/src/client/application/app-shell/ui-feature-identity.js +13 -0
- package/src/client/application/app-shell/ui-feature-shell.js +199 -99
- package/src/client/application/app-shell/ui-feature-sidebar.js +43 -7
- package/src/client/application/app-shell/ui-feature-tab-activity.js +19 -6
- package/src/client/application/app-shell/ui-feature-toolbar.js +16 -129
- package/src/client/application/app-shell-elements.js +8 -0
- package/src/client/application/preview-render-compiler.js +1 -87
- package/src/client/application/preview-render-scheduler.js +8 -24
- package/src/client/application/preview-renderer.js +2 -2
- package/src/client/application/webmcp-diagram-renderer.js +47 -0
- package/src/client/application/workspace-coordinator.js +23 -4
- package/src/client/application/workspace-preview-controller.js +120 -2
- package/src/client/application/workspace-route-controller.js +11 -1
- package/src/client/assets/vendor/jetbrains-mono/JetBrainsMono-Regular.woff2 +0 -0
- package/src/client/assets/vendor/jetbrains-mono/OFL.txt +93 -0
- package/src/client/bootstrap/collabmd-app-shell.js +99 -6
- package/src/client/domain/document-formatter.js +193 -0
- package/src/client/domain/excalidraw-element-link.js +73 -0
- package/src/client/domain/excalidraw-scene.js +21 -0
- package/src/client/domain/file-search.js +73 -0
- package/src/client/domain/git-labels.js +5 -0
- package/src/client/domain/hash-routes.js +8 -0
- package/src/client/domain/room.js +1 -5
- package/src/client/domain/runtime-paths.js +5 -0
- package/src/client/domain/vault-paths.js +6 -4
- package/src/client/domain/wiki-link-completions.js +20 -20
- package/src/client/drawio-editor.js +8 -3
- package/src/client/excalidraw-editor.js +287 -16
- package/src/client/export/export-host.js +59 -36
- package/src/client/export/export-page.js +14 -4
- package/src/client/export/export-pipeline.js +270 -42
- package/src/client/infrastructure/agent-connection-api-client.js +23 -0
- package/src/client/infrastructure/auth-client.js +3 -13
- package/src/client/infrastructure/editor-session.js +15 -0
- package/src/client/infrastructure/editor-view-adapter.js +73 -14
- package/src/client/infrastructure/excalidraw-room-client.js +80 -1
- package/src/client/infrastructure/git-api-client.js +39 -33
- package/src/client/infrastructure/lobby-presence.js +2 -5
- package/src/client/infrastructure/runtime-config.js +11 -1
- package/src/client/infrastructure/tab-activity-lock.js +2 -5
- package/src/client/infrastructure/vault-api-client.js +33 -26
- package/src/client/infrastructure/webmcp-tool-registry.js +283 -0
- package/src/client/infrastructure/workspace-sync-client.js +6 -4
- package/src/client/presentation/agent-connection-controller.js +255 -0
- package/src/client/presentation/backlinks-panel.js +3 -0
- package/src/client/presentation/bases-preview-controller.js +59 -26
- package/src/client/presentation/comment-ui/comment-ui-card.js +4 -2
- package/src/client/presentation/create-menu-presenter.js +19 -16
- package/src/client/presentation/excalidraw-embed-controller.js +164 -4
- package/src/client/presentation/file-action-controller.js +15 -2
- package/src/client/presentation/file-explorer-controller.js +3 -0
- package/src/client/presentation/file-explorer-view.js +71 -12
- package/src/client/presentation/file-history-view-controller.js +19 -12
- package/src/client/presentation/file-tree-state.js +1 -11
- package/src/client/presentation/git-diff-view-controller.js +70 -53
- package/src/client/presentation/git-panel-controller.js +86 -32
- package/src/client/presentation/image-lightbox-controller.js +20 -16
- package/src/client/presentation/layout-controller.js +3 -1
- package/src/client/presentation/quick-switcher-controller.js +74 -232
- package/src/client/presentation/quick-switcher-text-search.js +10 -14
- package/src/client/styles/components/navigation.css +1 -2
- package/src/client/styles/components/scrollbars.css +10 -0
- package/src/client/styles/export-document.css +44 -29
- package/src/client/styles/features/agent-connection.css +202 -0
- package/src/client/styles/features/collaboration-chat.css +8 -1
- package/src/client/styles/features/collaboration-dialogs.css +16 -1
- package/src/client/styles/features/collaboration-presence.css +30 -5
- package/src/client/styles/features/diff-toolbar.css +1 -1
- package/src/client/styles/features/media-lightbox.css +12 -3
- package/src/client/styles/features/preview-markdown.css +54 -0
- package/src/client/styles/features/quick-switcher.css +20 -11
- package/src/client/styles/foundation/themes.css +2 -0
- package/src/client/styles/foundation/tokens.css +8 -0
- package/src/client/styles/layout/editor-page.css +25 -11
- package/src/client/styles/layout/responsive.css +2 -20
- package/src/client/styles/layout/shell.css +77 -24
- package/src/client/styles/layout/sidebar.css +7 -4
- package/src/client/styles/layout/view-modes.css +2 -0
- package/src/client/styles/overrides/codemirror.css +31 -19
- package/src/client/styles/primitives/controls.css +21 -54
- package/src/client/styles/style.css +1 -0
- package/src/client/styles/surfaces/excalidraw-editor.css +42 -0
- package/src/domain/agent-tool-definitions.js +796 -0
- package/src/domain/collabmd-content-capabilities.js +149 -0
- package/src/domain/collaboration-protocol.js +2 -0
- package/src/domain/comment-threads.js +3 -5
- package/src/domain/diagram-source.js +50 -0
- package/src/domain/editable-content-revision.js +10 -0
- package/src/domain/editable-text.js +3 -0
- package/src/domain/exact-text-edits.js +69 -0
- package/src/domain/excalidraw-agent-scene.js +522 -0
- package/src/domain/excalidraw-agent-verification.js +460 -0
- package/src/domain/excalidraw-room-codec.js +1 -5
- package/src/domain/file-kind.js +12 -1
- package/src/domain/video-embed.js +76 -0
- package/src/server/application/agent-connection-service.js +178 -0
- package/src/server/application/agent-content-service.js +773 -0
- package/src/server/application/workspace-reconciliation.js +5 -7
- package/src/server/config/env.js +51 -1
- package/src/server/create-app-server.js +36 -0
- package/src/server/domain/bases/base-query-service.js +1 -5
- package/src/server/domain/collaboration/collaboration-room.js +121 -25
- package/src/server/domain/collaboration/protocol.js +4 -0
- package/src/server/domain/hosted-workspace.js +21 -0
- package/src/server/domain/markdown-reference-extractor.js +15 -1
- package/src/server/domain/ripgrep-search-service.js +110 -37
- package/src/server/infrastructure/git/git-service.js +19 -1
- package/src/server/infrastructure/git/history-service.js +1 -1
- package/src/server/infrastructure/git/local-exclude.js +25 -0
- package/src/server/infrastructure/git/parsers.js +19 -17
- package/src/server/infrastructure/git/path-utils.js +0 -16
- package/src/server/infrastructure/git/status-service.js +1 -1
- package/src/server/infrastructure/git/untracked-files.js +12 -21
- package/src/server/infrastructure/http/create-agent-api-handler.js +171 -0
- package/src/server/infrastructure/http/create-git-api-command-handler.js +32 -0
- package/src/server/infrastructure/http/create-request-handler.js +28 -0
- package/src/server/infrastructure/http/create-static-handler.js +6 -0
- package/src/server/infrastructure/mcp/create-agent-mcp-handler.js +199 -0
- package/src/server/infrastructure/persistence/agent-connection-store.js +120 -0
- package/src/server/infrastructure/persistence/path-utils.js +10 -4
- package/src/server/infrastructure/persistence/pull-backup-store.js +2 -27
- package/src/server/infrastructure/persistence/vault-file-store.js +11 -2
- package/src/server/shared/agent-tool-image.js +22 -0
- package/src/server/shared/agent-tool-rate-limiter.js +20 -0
- package/src/server/startup/git-remote-bootstrap.js +5 -33
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-BPH4FJkv.js +0 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-BNDLzJ7L.js +0 -1
- package/dist/client/assets/browser-utils--H_DkyEF.js +0 -1
- package/dist/client/assets/channel-CaoKA_We.js +0 -1
- package/dist/client/assets/chunk-DSNCTWBM-8xkug7fQ.js +0 -2
- package/dist/client/assets/chunk-DU6HZSFF-COXDzJK1.js +0 -127
- package/dist/client/assets/chunk-R7TYR2AO-CTd0Yj98.js +0 -10
- package/dist/client/assets/classDiagram-DTDB5LWJ-7fEmn0zV.js +0 -1
- package/dist/client/assets/classDiagram-v2-JRS7N3AN-7fEmn0zV.js +0 -1
- package/dist/client/assets/comment-threads-9J6Em-_y.js +0 -4
- package/dist/client/assets/comment-ui-shared-DWkk9Vh3.js +0 -7
- package/dist/client/assets/diagram-VSXAHHWV-CQRVEZR7.js +0 -3
- package/dist/client/assets/dist-73RPdcyU.js +0 -1
- package/dist/client/assets/dist-CH-sQu3r.js +0 -1
- package/dist/client/assets/drawio-editor-BjrxTe3l.js +0 -9
- package/dist/client/assets/drawioEditor-DUVZjSSr.js +0 -2
- package/dist/client/assets/ebnfDiagram-PWID7BFC-OXa8VEp1.js +0 -1
- package/dist/client/assets/editor-session-DfZuRgB_.js +0 -54
- package/dist/client/assets/embedded-editor-base-DvMynhKu.css +0 -1
- package/dist/client/assets/excalidraw-editor-D7pnUCLH.js +0 -9
- package/dist/client/assets/excalidraw-mermaid-stub-BNckbX2n.js +0 -1
- package/dist/client/assets/excalidraw-mermaid-stub-DE5FTiZB.js +0 -1
- package/dist/client/assets/excalidrawEditor-B0jOK3lv.js +0 -2
- package/dist/client/assets/export-page-Bs3xE1fg.js +0 -57
- package/dist/client/assets/exportDocument-Dg9MjkIk.js +0 -2
- package/dist/client/assets/exportDocument-PnYU3v1s.css +0 -1
- package/dist/client/assets/file-kind-BbqgGzpc.js +0 -1
- package/dist/client/assets/flowDiagram-A5DVABFB-CiW1eN8C.js +0 -1
- package/dist/client/assets/image-ZVKINSPA-0zXSnE7M.js +0 -1
- package/dist/client/assets/index-BIpgxyGZ.css +0 -1
- package/dist/client/assets/index-Z5sI0Bj-.js +0 -2
- package/dist/client/assets/main-Dwnj9Gaq.js +0 -903
- package/dist/client/assets/pegDiagram-XKGWAZYB-B31fKPR2.js +0 -1
- package/dist/client/assets/preview-render-compiler-BApvlLLo.js +0 -35
- package/dist/client/assets/preview-render-compiler-BQ_3h9vD.js +0 -1
- package/dist/client/assets/preview-render-profile-CE-5rVTC.js +0 -1
- package/dist/client/assets/prod-BiYPMR1i.js +0 -177
- package/dist/client/assets/prod-VxdGZ5Ly.js +0 -1
- package/dist/client/assets/quick-switcher-controller-ButWPmCI.js +0 -12
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BPifP0MB.js +0 -1
- package/dist/client/assets/runtime-config-BLrEKVO1.js +0 -1
- package/dist/client/assets/stateDiagram-v2-4QOOHH4V-B--gjrit.js +0 -1
- package/dist/client/assets/swimlanesDiagram-VK2B7HYN-DO8XNXEg.js +0 -8
- package/dist/client/assets/vault-api-client-_LpV1aIn.js +0 -6
- package/dist/client/assets/vault-utils-wdQ0RNR9.js +0 -1
- package/src/client/app/drawio-editor-entry.js +0 -1
- package/src/client/app/excalidraw-editor-entry.js +0 -1
- package/src/client/excalidraw-mermaid-stub.js +0 -7
- package/src/client/main.js +0 -16
- /package/dist/client/assets/{drawio-editor-UQQ8XHou.css → drawioEditor-UQQ8XHou.css} +0 -0
- /package/dist/client/assets/{sizeCapture-X5ZJPWSS-B0uUizjq.js → sizeCapture-INFHLROL-B0uUizjq.js} +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { execFile as execFileCallback } from 'node:child_process';
|
|
2
|
+
import { glob, readFile, stat } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
2
4
|
import { promisify } from 'node:util';
|
|
3
5
|
|
|
4
6
|
import { getVaultFileKind } from '../../domain/file-kind.js';
|
|
@@ -17,6 +19,8 @@ const TEXT_SEARCH_GLOBS = Object.freeze([
|
|
|
17
19
|
'*.md',
|
|
18
20
|
'*.markdown',
|
|
19
21
|
'*.mdx',
|
|
22
|
+
'*.html',
|
|
23
|
+
'*.htm',
|
|
20
24
|
'*.base',
|
|
21
25
|
'*.mmd',
|
|
22
26
|
'*.mermaid',
|
|
@@ -24,9 +28,15 @@ const TEXT_SEARCH_GLOBS = Object.freeze([
|
|
|
24
28
|
'*.plantuml',
|
|
25
29
|
'*.dsl',
|
|
26
30
|
'*.drawio',
|
|
27
|
-
'*.excalidraw',
|
|
28
31
|
]);
|
|
29
32
|
|
|
33
|
+
function parseByteLimit(value) {
|
|
34
|
+
const match = String(value ?? '').trim().match(/^(\d+)([KMG])?$/iu);
|
|
35
|
+
if (!match) return 1024 * 1024;
|
|
36
|
+
const multipliers = { K: 1024, M: 1024 ** 2, G: 1024 ** 3 };
|
|
37
|
+
return Number(match[1]) * (multipliers[match[2]?.toUpperCase()] ?? 1);
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
function normalizePositiveInt(value, fallback, { min = 1, max = Number.MAX_SAFE_INTEGER } = {}) {
|
|
31
41
|
const parsed = Number.parseInt(value ?? '', 10);
|
|
32
42
|
if (!Number.isInteger(parsed) || parsed < min) {
|
|
@@ -216,6 +226,72 @@ export function parseRipgrepJson(stdout, {
|
|
|
216
226
|
};
|
|
217
227
|
}
|
|
218
228
|
|
|
229
|
+
async function searchExcalidrawText({ maxFileSize, maxFiles, maxSnippetsPerFile, query, signal, vaultDir }) {
|
|
230
|
+
const files = [];
|
|
231
|
+
let matchCount = 0;
|
|
232
|
+
let truncated = false;
|
|
233
|
+
const normalizedQuery = query.toLocaleLowerCase();
|
|
234
|
+
const paths = glob('**/*.excalidraw', {
|
|
235
|
+
cwd: vaultDir,
|
|
236
|
+
exclude: ['**/.*', '**/node_modules/**'],
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
for await (const filePath of paths) {
|
|
240
|
+
signal?.throwIfAborted?.();
|
|
241
|
+
const absolutePath = join(vaultDir, filePath);
|
|
242
|
+
if ((await stat(absolutePath)).size > parseByteLimit(maxFileSize)) continue;
|
|
243
|
+
|
|
244
|
+
let scene;
|
|
245
|
+
try {
|
|
246
|
+
scene = JSON.parse(await readFile(absolutePath, 'utf8'));
|
|
247
|
+
} catch {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const snippets = [];
|
|
252
|
+
let fileMatchCount = 0;
|
|
253
|
+
for (const element of Array.isArray(scene?.elements) ? scene.elements : []) {
|
|
254
|
+
if (element?.isDeleted || element?.type !== 'text' || typeof element.text !== 'string') continue;
|
|
255
|
+
const text = element.text;
|
|
256
|
+
const normalizedText = text.toLocaleLowerCase();
|
|
257
|
+
let matchStart = normalizedText.indexOf(normalizedQuery);
|
|
258
|
+
while (matchStart >= 0) {
|
|
259
|
+
fileMatchCount += 1;
|
|
260
|
+
matchCount += 1;
|
|
261
|
+
if (snippets.length < maxSnippetsPerFile) {
|
|
262
|
+
const snippet = createSnippet(text, matchStart, matchStart + query.length);
|
|
263
|
+
snippets.push({
|
|
264
|
+
column: matchStart + 1,
|
|
265
|
+
line: 1,
|
|
266
|
+
matchEnd: snippet.matchEnd,
|
|
267
|
+
matchStart: snippet.matchStart,
|
|
268
|
+
text: snippet.text,
|
|
269
|
+
});
|
|
270
|
+
} else {
|
|
271
|
+
truncated = true;
|
|
272
|
+
}
|
|
273
|
+
matchStart = normalizedText.indexOf(normalizedQuery, matchStart + Math.max(query.length, 1));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (fileMatchCount > 0) {
|
|
278
|
+
if (files.length >= maxFiles) {
|
|
279
|
+
truncated = true;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
files.push({
|
|
283
|
+
file: filePath.replaceAll('\\', '/'),
|
|
284
|
+
kind: 'excalidraw',
|
|
285
|
+
matchCount: fileMatchCount,
|
|
286
|
+
snippets,
|
|
287
|
+
truncated: fileMatchCount > snippets.length,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return { files, matchCount, truncated };
|
|
293
|
+
}
|
|
294
|
+
|
|
219
295
|
export class RipgrepSearchService {
|
|
220
296
|
constructor({
|
|
221
297
|
execFileImpl = execFile,
|
|
@@ -307,7 +383,7 @@ export class RipgrepSearchService {
|
|
|
307
383
|
maxFileSize: this.maxFileSize,
|
|
308
384
|
maxSnippetsPerFile: this.maxSnippetsPerFile,
|
|
309
385
|
});
|
|
310
|
-
|
|
386
|
+
let parsed;
|
|
311
387
|
try {
|
|
312
388
|
const result = await this.execFileImpl('rg', args, {
|
|
313
389
|
cwd: this.vaultDir,
|
|
@@ -316,56 +392,53 @@ export class RipgrepSearchService {
|
|
|
316
392
|
...(signal ? { signal } : {}),
|
|
317
393
|
timeout: this.timeoutMs,
|
|
318
394
|
});
|
|
319
|
-
|
|
395
|
+
parsed = parseRipgrepJson(result.stdout, {
|
|
320
396
|
maxFiles,
|
|
321
397
|
maxSnippetsPerFile: this.maxSnippetsPerFile,
|
|
322
398
|
query: normalizedQuery,
|
|
323
399
|
});
|
|
324
|
-
parsed.search = this.getClientConfig();
|
|
325
|
-
|
|
326
|
-
logPerfEvent(this.perfLoggingEnabled, 'search-query', {
|
|
327
|
-
durationMs: Date.now() - startedAt,
|
|
328
|
-
fileCount: parsed.files.length,
|
|
329
|
-
matchCount: parsed.matchCount,
|
|
330
|
-
truncated: parsed.truncated,
|
|
331
|
-
});
|
|
332
|
-
return parsed;
|
|
333
400
|
} catch (error) {
|
|
334
401
|
if (error?.code === 1) {
|
|
335
|
-
|
|
402
|
+
parsed = createEmptySearchResult({
|
|
336
403
|
query: normalizedQuery,
|
|
337
404
|
searchAvailable: this.available,
|
|
338
405
|
});
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
406
|
+
} else {
|
|
407
|
+
const isMaxBuffer = error?.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER';
|
|
408
|
+
parsed = parseRipgrepJson(error?.stdout ?? '', {
|
|
409
|
+
maxFiles,
|
|
410
|
+
maxSnippetsPerFile: this.maxSnippetsPerFile,
|
|
411
|
+
query: normalizedQuery,
|
|
345
412
|
});
|
|
346
|
-
|
|
413
|
+
if (isMaxBuffer && parsed.files.length > 0) {
|
|
414
|
+
parsed.truncated = true;
|
|
415
|
+
} else {
|
|
416
|
+
error.statusCode = error?.signal === 'SIGTERM' ? 504 : 500;
|
|
417
|
+
throw error;
|
|
418
|
+
}
|
|
347
419
|
}
|
|
420
|
+
}
|
|
348
421
|
|
|
349
|
-
|
|
350
|
-
const
|
|
351
|
-
|
|
422
|
+
if (parsed.files.length < maxFiles) {
|
|
423
|
+
const excalidraw = await searchExcalidrawText({
|
|
424
|
+
maxFileSize: this.maxFileSize,
|
|
425
|
+
maxFiles: maxFiles - parsed.files.length,
|
|
352
426
|
maxSnippetsPerFile: this.maxSnippetsPerFile,
|
|
353
427
|
query: normalizedQuery,
|
|
428
|
+
signal,
|
|
429
|
+
vaultDir: this.vaultDir,
|
|
354
430
|
});
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
logPerfEvent(this.perfLoggingEnabled, 'search-query', {
|
|
359
|
-
durationMs: Date.now() - startedAt,
|
|
360
|
-
fileCount: parsed.files.length,
|
|
361
|
-
matchCount: parsed.matchCount,
|
|
362
|
-
truncated: true,
|
|
363
|
-
});
|
|
364
|
-
return parsed;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
error.statusCode = error?.signal === 'SIGTERM' ? 504 : 500;
|
|
368
|
-
throw error;
|
|
431
|
+
parsed.files.push(...excalidraw.files);
|
|
432
|
+
parsed.matchCount += excalidraw.matchCount;
|
|
433
|
+
parsed.truncated ||= excalidraw.truncated;
|
|
369
434
|
}
|
|
435
|
+
parsed.search = this.getClientConfig();
|
|
436
|
+
logPerfEvent(this.perfLoggingEnabled, 'search-query', {
|
|
437
|
+
durationMs: Date.now() - startedAt,
|
|
438
|
+
fileCount: parsed.files.length,
|
|
439
|
+
matchCount: parsed.matchCount,
|
|
440
|
+
truncated: parsed.truncated,
|
|
441
|
+
});
|
|
442
|
+
return parsed;
|
|
370
443
|
}
|
|
371
444
|
}
|
|
@@ -146,9 +146,18 @@ export class GitService {
|
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
async stageAll() {
|
|
150
|
+
await this.commandRunner.execGit(['add', '-A', '--']);
|
|
151
|
+
this.invalidateStatusCache();
|
|
152
|
+
return {
|
|
153
|
+
ok: true,
|
|
154
|
+
workspaceChange: createEmptyWorkspaceChange(),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
149
158
|
async unstageFile(path) {
|
|
150
159
|
const normalizedPath = normalizeRelativeGitPath(path);
|
|
151
|
-
await this.commandRunner.execGit(['reset', '
|
|
160
|
+
await this.commandRunner.execGit(['reset', '--', normalizedPath]);
|
|
152
161
|
this.invalidateStatusCache();
|
|
153
162
|
return {
|
|
154
163
|
ok: true,
|
|
@@ -157,6 +166,15 @@ export class GitService {
|
|
|
157
166
|
};
|
|
158
167
|
}
|
|
159
168
|
|
|
169
|
+
async unstageAll() {
|
|
170
|
+
await this.commandRunner.execGit(['reset', '--']);
|
|
171
|
+
this.invalidateStatusCache();
|
|
172
|
+
return {
|
|
173
|
+
ok: true,
|
|
174
|
+
workspaceChange: createEmptyWorkspaceChange(),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
160
178
|
async commitStaged({ author = null, message } = {}) {
|
|
161
179
|
const normalizedMessage = String(message ?? '').trim();
|
|
162
180
|
if (!normalizedMessage) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
export async function ensureCollabMetadataGitExclude(vaultDir) {
|
|
5
|
+
const excludePath = resolve(vaultDir, '.git/info/exclude');
|
|
6
|
+
let existingContent = '';
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
existingContent = await readFile(excludePath, 'utf8');
|
|
10
|
+
} catch (error) {
|
|
11
|
+
if (error?.code !== 'ENOENT') {
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const lines = existingContent
|
|
17
|
+
.split(/\r?\n/u)
|
|
18
|
+
.map((line) => line.trim());
|
|
19
|
+
if (lines.includes('.collabmd') || lines.includes('.collabmd/')) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const prefix = existingContent && !existingContent.endsWith('\n') ? '\n' : '';
|
|
24
|
+
await writeFile(excludePath, `${existingContent}${prefix}.collabmd/\n`, 'utf8');
|
|
25
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decodeQuotedPath,
|
|
1
|
+
import { decodeQuotedPath, stripDiffPrefix } from './path-utils.js';
|
|
2
2
|
import { createEmptyBranchStatus, createStatusInfo } from './responses.js';
|
|
3
3
|
|
|
4
4
|
function parseBranchLine(line) {
|
|
@@ -91,11 +91,8 @@ export function splitContentLines(content) {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
export function parseStatusOutput(output) {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
.filter(Boolean);
|
|
97
|
-
const [branchLine = '', ...statusLines] = lines;
|
|
98
|
-
const branch = parseBranchLine(branchLine);
|
|
94
|
+
const records = String(output ?? '').split('\0');
|
|
95
|
+
const branch = parseBranchLine(records.shift() || '');
|
|
99
96
|
const sections = {
|
|
100
97
|
staged: [],
|
|
101
98
|
'working-tree': [],
|
|
@@ -103,14 +100,15 @@ export function parseStatusOutput(output) {
|
|
|
103
100
|
};
|
|
104
101
|
const uniquePaths = new Set();
|
|
105
102
|
|
|
106
|
-
for (
|
|
107
|
-
|
|
103
|
+
for (let index = 0; index < records.length; index += 1) {
|
|
104
|
+
const record = records[index];
|
|
105
|
+
if (record.length < 3) {
|
|
108
106
|
continue;
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
const indexStatus =
|
|
112
|
-
const workTreeStatus =
|
|
113
|
-
const
|
|
109
|
+
const indexStatus = record[0];
|
|
110
|
+
const workTreeStatus = record[1];
|
|
111
|
+
const path = record.slice(3);
|
|
114
112
|
|
|
115
113
|
if (indexStatus === '!' && workTreeStatus === '!') {
|
|
116
114
|
continue;
|
|
@@ -120,7 +118,7 @@ export function parseStatusOutput(output) {
|
|
|
120
118
|
const file = {
|
|
121
119
|
...createStatusInfo('?'),
|
|
122
120
|
oldPath: null,
|
|
123
|
-
path
|
|
121
|
+
path,
|
|
124
122
|
scope: 'untracked',
|
|
125
123
|
};
|
|
126
124
|
sections.untracked.push(file);
|
|
@@ -128,13 +126,17 @@ export function parseStatusOutput(output) {
|
|
|
128
126
|
continue;
|
|
129
127
|
}
|
|
130
128
|
|
|
131
|
-
const
|
|
129
|
+
const hasSourcePath = ['R', 'C'].includes(indexStatus) || ['R', 'C'].includes(workTreeStatus);
|
|
130
|
+
const oldPath = hasSourcePath ? records[index + 1] || null : null;
|
|
131
|
+
if (hasSourcePath) {
|
|
132
|
+
index += 1;
|
|
133
|
+
}
|
|
132
134
|
|
|
133
135
|
if (indexStatus !== ' ') {
|
|
134
136
|
const file = {
|
|
135
137
|
...createStatusInfo(indexStatus),
|
|
136
|
-
oldPath
|
|
137
|
-
path
|
|
138
|
+
oldPath,
|
|
139
|
+
path,
|
|
138
140
|
scope: 'staged',
|
|
139
141
|
};
|
|
140
142
|
sections.staged.push(file);
|
|
@@ -144,8 +146,8 @@ export function parseStatusOutput(output) {
|
|
|
144
146
|
if (workTreeStatus !== ' ') {
|
|
145
147
|
const file = {
|
|
146
148
|
...createStatusInfo(workTreeStatus),
|
|
147
|
-
oldPath
|
|
148
|
-
path
|
|
149
|
+
oldPath,
|
|
150
|
+
path,
|
|
149
151
|
scope: 'working-tree',
|
|
150
152
|
};
|
|
151
153
|
sections['working-tree'].push(file);
|
|
@@ -34,22 +34,6 @@ export function normalizeRelativeGitPath(pathValue) {
|
|
|
34
34
|
return normalized.join('/');
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export function parseRenamePath(rawPath) {
|
|
38
|
-
const decodedPath = decodeQuotedPath(rawPath);
|
|
39
|
-
const separator = ' -> ';
|
|
40
|
-
const separatorIndex = decodedPath.indexOf(separator);
|
|
41
|
-
if (separatorIndex === -1) {
|
|
42
|
-
return {
|
|
43
|
-
oldPath: null,
|
|
44
|
-
path: decodedPath,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
oldPath: decodedPath.slice(0, separatorIndex),
|
|
50
|
-
path: decodedPath.slice(separatorIndex + separator.length),
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
37
|
|
|
54
38
|
export function stripDiffPrefix(pathValue) {
|
|
55
39
|
const normalizedPath = String(pathValue ?? '').replace(/\s+$/u, '');
|
|
@@ -42,7 +42,7 @@ export class GitStatusService {
|
|
|
42
42
|
|
|
43
43
|
const statusPromise = (async () => {
|
|
44
44
|
const parsed = parseStatusOutput(
|
|
45
|
-
await this.commandRunner.execGit(['status', '--porcelain=v1', '--branch', '--untracked-files=all']),
|
|
45
|
+
await this.commandRunner.execGit(['status', '--porcelain=v1', '-z', '--branch', '--untracked-files=all']),
|
|
46
46
|
);
|
|
47
47
|
const sections = [
|
|
48
48
|
{ files: parsed.sections.staged, key: 'staged', label: 'Staged Changes' },
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createReadStream } from 'node:fs';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import { createInterface } from 'node:readline';
|
|
4
5
|
|
|
5
6
|
import { getVaultFileKind, isImageAttachmentFilePath } from '../../../domain/file-kind.js';
|
|
6
7
|
import { mapWithConcurrency } from '../../shared/async-utils.js';
|
|
@@ -13,29 +14,19 @@ async function countFileLines(filePath) {
|
|
|
13
14
|
return 0;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const lines = createInterface({
|
|
18
|
+
crlfDelay: Infinity,
|
|
19
|
+
input: createReadStream(filePath, { encoding: 'utf8' }),
|
|
20
|
+
});
|
|
21
|
+
let lineCount = 0;
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
for await (const line of lines) {
|
|
24
|
+
if (typeof line === 'string') {
|
|
25
|
+
lineCount += 1;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
lastCharacter = chunk[chunk.length - 1];
|
|
29
|
-
lineCount += chunk.match(/\n/gu)?.length ?? 0;
|
|
30
|
-
});
|
|
31
|
-
stream.on('error', reject);
|
|
32
|
-
stream.on('end', () => {
|
|
33
|
-
if (sawData && lastCharacter !== '\n') {
|
|
34
|
-
lineCount += 1;
|
|
35
|
-
}
|
|
36
|
-
resolve(lineCount);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
29
|
+
return lineCount;
|
|
39
30
|
}
|
|
40
31
|
|
|
41
32
|
function buildSyntheticAddedFileDiff(pathValue, content) {
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { fromJsonSchema } from '@modelcontextprotocol/server';
|
|
2
|
+
|
|
3
|
+
import { getAgentToolDefinition } from '../../../domain/agent-tool-definitions.js';
|
|
4
|
+
import { encodeAgentToolImage } from '../../shared/agent-tool-image.js';
|
|
5
|
+
import { readRequestId } from './http-request-helpers.js';
|
|
6
|
+
import { jsonResponse } from './http-response.js';
|
|
7
|
+
import { parseJsonBody } from './request-body.js';
|
|
8
|
+
|
|
9
|
+
const WEBMCP_TOOL_PATH = '/api/agent/tools/';
|
|
10
|
+
const WEBMCP_INPUT_SCHEMAS = new Map();
|
|
11
|
+
|
|
12
|
+
function createAgentError(code, message, statusCode) {
|
|
13
|
+
const error = new Error(message);
|
|
14
|
+
error.code = code;
|
|
15
|
+
error.statusCode = statusCode;
|
|
16
|
+
return error;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function sendAgentError(req, res, error) {
|
|
20
|
+
const statusCode = Number(error?.statusCode) || 500;
|
|
21
|
+
if (statusCode >= 500) console.error('[agent] Request failed:', error.message);
|
|
22
|
+
jsonResponse(req, res, statusCode, {
|
|
23
|
+
code: error?.code || 'AGENT_REQUEST_FAILED',
|
|
24
|
+
error: statusCode >= 500 ? 'Agent request failed' : error.message,
|
|
25
|
+
...(Number.isFinite(error?.retryAfterMs) ? { retryAfterMs: error.retryAfterMs } : {}),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function validateWebMcpInput(definition, input) {
|
|
30
|
+
let schema = WEBMCP_INPUT_SCHEMAS.get(definition.name);
|
|
31
|
+
if (!schema) {
|
|
32
|
+
schema = fromJsonSchema(definition.inputSchema);
|
|
33
|
+
WEBMCP_INPUT_SCHEMAS.set(definition.name, schema);
|
|
34
|
+
}
|
|
35
|
+
const validation = await schema['~standard'].validate(input);
|
|
36
|
+
if (validation.issues?.length) {
|
|
37
|
+
throw createAgentError('AGENT_INPUT_INVALID', 'WebMCP tool input does not match its schema', 400);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function createWebMcpActor({ agentConnectionService, authService, config, req }) {
|
|
42
|
+
let subject;
|
|
43
|
+
if (config.auth.strategy === 'none') {
|
|
44
|
+
subject = {
|
|
45
|
+
collaborator: null,
|
|
46
|
+
subjectId: req.socket?.remoteAddress || 'unknown',
|
|
47
|
+
subjectType: 'anonymous',
|
|
48
|
+
};
|
|
49
|
+
} else {
|
|
50
|
+
const user = authService.getAuthenticatedUser?.(req) ?? null;
|
|
51
|
+
subject = await agentConnectionService.resolveManagementSubject(user);
|
|
52
|
+
}
|
|
53
|
+
const actorId = `webmcp:${subject.subjectType}:${subject.subjectId}`;
|
|
54
|
+
return {
|
|
55
|
+
collaborator: subject.collaborator,
|
|
56
|
+
connectionId: actorId,
|
|
57
|
+
origin: 'webmcp',
|
|
58
|
+
rateLimitKey: actorId,
|
|
59
|
+
requestId: readRequestId(req),
|
|
60
|
+
scopes: ['vault:edit', 'vault:read'],
|
|
61
|
+
sourceRef: 'webmcp',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function mapWebMcpResult(definition, value) {
|
|
66
|
+
if (definition.resultKind === 'image' && value.svg) {
|
|
67
|
+
const { data, mimeType, structuredContent } = await encodeAgentToolImage(value);
|
|
68
|
+
return {
|
|
69
|
+
...structuredContent,
|
|
70
|
+
image: { data, encoding: 'base64', mimeType },
|
|
71
|
+
scene: value.scene,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (definition.resultKind === 'optional-image' && value.verification?.svg) {
|
|
75
|
+
const { verification, ...result } = value;
|
|
76
|
+
const { data, mimeType, structuredContent } = await encodeAgentToolImage(verification);
|
|
77
|
+
return {
|
|
78
|
+
...result,
|
|
79
|
+
image: { data, encoding: 'base64', mimeType },
|
|
80
|
+
verification: {
|
|
81
|
+
...structuredContent,
|
|
82
|
+
scene: verification.scene,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function createAgentApiHandler({
|
|
90
|
+
agentConnectionService,
|
|
91
|
+
agentContentService,
|
|
92
|
+
authService,
|
|
93
|
+
config,
|
|
94
|
+
rateLimiter,
|
|
95
|
+
}) {
|
|
96
|
+
return async function handleAgentApi(req, res, requestUrl) {
|
|
97
|
+
if (!requestUrl.pathname.startsWith('/api/agent')) return false;
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
if (requestUrl.pathname.startsWith(WEBMCP_TOOL_PATH)) {
|
|
101
|
+
if (req.method !== 'POST') {
|
|
102
|
+
throw createAgentError('AGENT_METHOD_NOT_ALLOWED', 'WebMCP tools require POST', 405);
|
|
103
|
+
}
|
|
104
|
+
const name = decodeURIComponent(requestUrl.pathname.slice(WEBMCP_TOOL_PATH.length));
|
|
105
|
+
const definition = getAgentToolDefinition(name);
|
|
106
|
+
if (!definition?.webMcp) {
|
|
107
|
+
throw createAgentError('AGENT_TOOL_NOT_FOUND', 'WebMCP tool not found', 404);
|
|
108
|
+
}
|
|
109
|
+
const input = await parseJsonBody(req);
|
|
110
|
+
await validateWebMcpInput(definition, input);
|
|
111
|
+
const actor = await createWebMcpActor({
|
|
112
|
+
agentConnectionService,
|
|
113
|
+
authService,
|
|
114
|
+
config,
|
|
115
|
+
req,
|
|
116
|
+
});
|
|
117
|
+
rateLimiter(actor.rateLimitKey);
|
|
118
|
+
const cancellation = new AbortController();
|
|
119
|
+
const abort = () => cancellation.abort();
|
|
120
|
+
req.once('aborted', abort);
|
|
121
|
+
try {
|
|
122
|
+
const value = await agentContentService[definition.method](actor, {
|
|
123
|
+
...input,
|
|
124
|
+
signal: cancellation.signal,
|
|
125
|
+
});
|
|
126
|
+
jsonResponse(req, res, 200, await mapWebMcpResult(definition, value));
|
|
127
|
+
} finally {
|
|
128
|
+
req.off('aborted', abort);
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!config.agentAccess?.enabled) {
|
|
134
|
+
jsonResponse(req, res, 404, { error: 'Agent Access is not enabled' });
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
const user = authService.getAuthenticatedUser?.(req) ?? null;
|
|
138
|
+
if (requestUrl.pathname === '/api/agent/connections' && req.method === 'GET') {
|
|
139
|
+
jsonResponse(req, res, 200, {
|
|
140
|
+
connections: await agentConnectionService.listConnections(user),
|
|
141
|
+
});
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
if (requestUrl.pathname === '/api/agent/connections' && req.method === 'POST') {
|
|
145
|
+
const body = await parseJsonBody(req);
|
|
146
|
+
jsonResponse(req, res, 201, await agentConnectionService.createConnection({
|
|
147
|
+
clientKind: body?.clientKind,
|
|
148
|
+
label: body?.label,
|
|
149
|
+
scopes: body?.scopes,
|
|
150
|
+
user,
|
|
151
|
+
}));
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
if (requestUrl.pathname.startsWith('/api/agent/connections/') && req.method === 'DELETE') {
|
|
155
|
+
const connectionId = decodeURIComponent(
|
|
156
|
+
requestUrl.pathname.slice('/api/agent/connections/'.length).split('/')[0] || '',
|
|
157
|
+
);
|
|
158
|
+
jsonResponse(req, res, 200, await agentConnectionService.revokeConnection({
|
|
159
|
+
connectionId,
|
|
160
|
+
user,
|
|
161
|
+
}));
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
jsonResponse(req, res, 404, { error: 'Agent endpoint not found' });
|
|
165
|
+
return true;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
sendAgentError(req, res, error);
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -84,6 +84,38 @@ export function createGitApiCommandHandler({
|
|
|
84
84
|
return true;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
if (requestUrl.pathname === '/api/git/stage-all' && req.method === 'POST') {
|
|
88
|
+
try {
|
|
89
|
+
jsonResponse(req, res, 200, await applyWorkspaceMutationEffects({
|
|
90
|
+
action: 'stage-all',
|
|
91
|
+
req,
|
|
92
|
+
responsePayload: await workspaceMutationCoordinator.runManagedWorkspaceMutation(
|
|
93
|
+
() => gitService.stageAll(),
|
|
94
|
+
),
|
|
95
|
+
workspaceMutationCoordinator,
|
|
96
|
+
}));
|
|
97
|
+
} catch (error) {
|
|
98
|
+
handleApiError(req, res, error, '[api] Failed to stage all git changes:', 'Failed to stage all git changes');
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (requestUrl.pathname === '/api/git/unstage-all' && req.method === 'POST') {
|
|
104
|
+
try {
|
|
105
|
+
jsonResponse(req, res, 200, await applyWorkspaceMutationEffects({
|
|
106
|
+
action: 'unstage-all',
|
|
107
|
+
req,
|
|
108
|
+
responsePayload: await workspaceMutationCoordinator.runManagedWorkspaceMutation(
|
|
109
|
+
() => gitService.unstageAll(),
|
|
110
|
+
),
|
|
111
|
+
workspaceMutationCoordinator,
|
|
112
|
+
}));
|
|
113
|
+
} catch (error) {
|
|
114
|
+
handleApiError(req, res, error, '[api] Failed to unstage all git changes:', 'Failed to unstage all git changes');
|
|
115
|
+
}
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
87
119
|
if (requestUrl.pathname === '/api/git/commit' && req.method === 'POST') {
|
|
88
120
|
try {
|
|
89
121
|
const body = await parseRequiredBody(req, res, 'message');
|