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,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
|
|
16
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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};
|