@rudderhq/server 0.2.5-canary.8 → 0.2.5
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/dist/bootstrap/plugin-host-runtime.d.ts +39 -39
- package/dist/bundled-plugins/plugin-linear/dist/worker.js +101 -147
- package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +2 -2
- package/dist/bundled-plugins/plugin-linear/package.json +1 -1
- package/dist/routes/access-onboarding.helpers.d.ts +142 -0
- package/dist/routes/access-onboarding.helpers.d.ts.map +1 -0
- package/dist/routes/access-onboarding.helpers.js +762 -0
- package/dist/routes/access-onboarding.helpers.js.map +1 -0
- package/dist/routes/access.d.ts +2 -48
- package/dist/routes/access.d.ts.map +1 -1
- package/dist/routes/access.helpers.d.ts +109 -0
- package/dist/routes/access.helpers.d.ts.map +1 -0
- package/dist/routes/access.helpers.js +460 -0
- package/dist/routes/access.helpers.js.map +1 -0
- package/dist/routes/access.js +6 -1218
- package/dist/routes/access.js.map +1 -1
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +55 -1057
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/agents.management-routes.d.ts +12 -0
- package/dist/routes/agents.management-routes.d.ts.map +1 -0
- package/dist/routes/agents.management-routes.js +1067 -0
- package/dist/routes/agents.management-routes.js.map +1 -0
- package/dist/routes/chats.d.ts.map +1 -1
- package/dist/routes/chats.js +42 -652
- package/dist/routes/chats.js.map +1 -1
- package/dist/routes/chats.stream-routes.d.ts +12 -0
- package/dist/routes/chats.stream-routes.d.ts.map +1 -0
- package/dist/routes/chats.stream-routes.js +666 -0
- package/dist/routes/chats.stream-routes.js.map +1 -0
- package/dist/routes/issues.comments-attachments.d.ts +12 -0
- package/dist/routes/issues.comments-attachments.d.ts.map +1 -0
- package/dist/routes/issues.comments-attachments.js +511 -0
- package/dist/routes/issues.comments-attachments.js.map +1 -0
- package/dist/routes/issues.d.ts.map +1 -1
- package/dist/routes/issues.js +43 -1128
- package/dist/routes/issues.js.map +1 -1
- package/dist/routes/issues.mutations.d.ts +12 -0
- package/dist/routes/issues.mutations.d.ts.map +1 -0
- package/dist/routes/issues.mutations.js +635 -0
- package/dist/routes/issues.mutations.js.map +1 -0
- package/dist/routes/plugins.d.ts.map +1 -1
- package/dist/routes/plugins.js +14 -694
- package/dist/routes/plugins.js.map +1 -1
- package/dist/routes/plugins.operations-routes.d.ts +28 -0
- package/dist/routes/plugins.operations-routes.d.ts.map +1 -0
- package/dist/routes/plugins.operations-routes.js +720 -0
- package/dist/routes/plugins.operations-routes.js.map +1 -0
- package/dist/services/access.d.ts +21 -21
- package/dist/services/activity.d.ts +19 -19
- package/dist/services/agents.d.ts +158 -158
- package/dist/services/approvals.d.ts +29 -29
- package/dist/services/assets.d.ts +8 -8
- package/dist/services/automations.d.ts +41 -27
- package/dist/services/automations.d.ts.map +1 -1
- package/dist/services/automations.js +287 -110
- package/dist/services/automations.js.map +1 -1
- package/dist/services/automations.scheduler.d.ts +9 -0
- package/dist/services/automations.scheduler.d.ts.map +1 -0
- package/dist/services/automations.scheduler.js +101 -0
- package/dist/services/automations.scheduler.js.map +1 -0
- package/dist/services/board-auth.d.ts +32 -32
- package/dist/services/calendar.d.ts +26 -26
- package/dist/services/chat-assistant.d.ts +3 -47
- package/dist/services/chat-assistant.d.ts.map +1 -1
- package/dist/services/chat-assistant.helpers.d.ts +156 -0
- package/dist/services/chat-assistant.helpers.d.ts.map +1 -0
- package/dist/services/chat-assistant.helpers.js +862 -0
- package/dist/services/chat-assistant.helpers.js.map +1 -0
- package/dist/services/chat-assistant.js +2 -861
- package/dist/services/chat-assistant.js.map +1 -1
- package/dist/services/chats.d.ts +149 -247
- package/dist/services/chats.d.ts.map +1 -1
- package/dist/services/chats.helpers.d.ts +117 -0
- package/dist/services/chats.helpers.d.ts.map +1 -0
- package/dist/services/chats.helpers.js +285 -0
- package/dist/services/chats.helpers.js.map +1 -0
- package/dist/services/chats.js +6 -286
- package/dist/services/chats.js.map +1 -1
- package/dist/services/costs.d.ts +8 -8
- package/dist/services/finance.d.ts +18 -18
- package/dist/services/goals.d.ts +30 -30
- package/dist/services/heartbeat.d.ts +3 -1
- package/dist/services/heartbeat.d.ts.map +1 -1
- package/dist/services/heartbeat.js +3 -1
- package/dist/services/heartbeat.js.map +1 -1
- package/dist/services/issue-approvals.d.ts +4 -4
- package/dist/services/issue-review-wakeup.d.ts +3 -3
- package/dist/services/issues.comments-attachments.d.ts +141 -0
- package/dist/services/issues.comments-attachments.d.ts.map +1 -0
- package/dist/services/issues.comments-attachments.js +313 -0
- package/dist/services/issues.comments-attachments.js.map +1 -0
- package/dist/services/issues.d.ts +205 -256
- package/dist/services/issues.d.ts.map +1 -1
- package/dist/services/issues.helpers.d.ts +87 -0
- package/dist/services/issues.helpers.d.ts.map +1 -0
- package/dist/services/issues.helpers.js +270 -0
- package/dist/services/issues.helpers.js.map +1 -0
- package/dist/services/issues.js +5 -569
- package/dist/services/issues.js.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.core.d.ts +210 -0
- package/dist/services/knowledge-portability/organization-portability.core.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.core.js +997 -0
- package/dist/services/knowledge-portability/organization-portability.core.js.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.d.ts +6 -28
- package/dist/services/knowledge-portability/organization-portability.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.export.d.ts +24 -0
- package/dist/services/knowledge-portability/organization-portability.export.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.export.js +607 -0
- package/dist/services/knowledge-portability/organization-portability.export.js.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.files.d.ts +69 -0
- package/dist/services/knowledge-portability/organization-portability.files.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.files.js +597 -0
- package/dist/services/knowledge-portability/organization-portability.files.js.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.import.d.ts +31 -0
- package/dist/services/knowledge-portability/organization-portability.import.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.import.js +575 -0
- package/dist/services/knowledge-portability/organization-portability.import.js.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.js +37 -3848
- package/dist/services/knowledge-portability/organization-portability.js.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.package.d.ts +72 -0
- package/dist/services/knowledge-portability/organization-portability.package.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.package.js +749 -0
- package/dist/services/knowledge-portability/organization-portability.package.js.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.preview.d.ts +18 -0
- package/dist/services/knowledge-portability/organization-portability.preview.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.preview.js +333 -0
- package/dist/services/knowledge-portability/organization-portability.preview.js.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.resolve-source.d.ts +4 -0
- package/dist/services/knowledge-portability/organization-portability.resolve-source.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.resolve-source.js +86 -0
- package/dist/services/knowledge-portability/organization-portability.resolve-source.js.map +1 -0
- package/dist/services/knowledge-portability/organization-skills.catalog.d.ts +221 -0
- package/dist/services/knowledge-portability/organization-skills.catalog.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-skills.catalog.js +999 -0
- package/dist/services/knowledge-portability/organization-skills.catalog.js.map +1 -0
- package/dist/services/knowledge-portability/organization-skills.d.ts +4 -75
- package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.js +11 -2008
- package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.scans.d.ts +16 -0
- package/dist/services/knowledge-portability/organization-skills.scans.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-skills.scans.js +300 -0
- package/dist/services/knowledge-portability/organization-skills.scans.js.map +1 -0
- package/dist/services/knowledge-portability/organization-skills.sources.d.ts +68 -0
- package/dist/services/knowledge-portability/organization-skills.sources.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-skills.sources.js +728 -0
- package/dist/services/knowledge-portability/organization-skills.sources.js.map +1 -0
- package/dist/services/messenger.d.ts +2 -2
- package/dist/services/messenger.js +2 -2
- package/dist/services/messenger.js.map +1 -1
- package/dist/services/organization-skills.d.ts +3 -1
- package/dist/services/organization-skills.d.ts.map +1 -1
- package/dist/services/organization-skills.js +3 -1
- package/dist/services/organization-skills.js.map +1 -1
- package/dist/services/orgs.d.ts +9 -9
- package/dist/services/plugin-loader.core.d.ts +14 -0
- package/dist/services/plugin-loader.core.d.ts.map +1 -0
- package/dist/services/plugin-loader.core.js +905 -0
- package/dist/services/plugin-loader.core.js.map +1 -0
- package/dist/services/plugin-loader.d.ts +3 -440
- package/dist/services/plugin-loader.d.ts.map +1 -1
- package/dist/services/plugin-loader.helpers.d.ts +468 -0
- package/dist/services/plugin-loader.helpers.d.ts.map +1 -0
- package/dist/services/plugin-loader.helpers.js +263 -0
- package/dist/services/plugin-loader.helpers.js.map +1 -0
- package/dist/services/plugin-loader.js +3 -1191
- package/dist/services/plugin-loader.js.map +1 -1
- package/dist/services/plugin-loader.worker-paths.d.ts +7 -0
- package/dist/services/plugin-loader.worker-paths.d.ts.map +1 -0
- package/dist/services/plugin-loader.worker-paths.js +85 -0
- package/dist/services/plugin-loader.worker-paths.js.map +1 -0
- package/dist/services/plugin-registry.d.ts +123 -123
- package/dist/services/projects.d.ts +8 -8
- package/dist/services/runtime-kernel/heartbeat.core.d.ts +725 -0
- package/dist/services/runtime-kernel/heartbeat.core.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.core.js +525 -0
- package/dist/services/runtime-kernel/heartbeat.core.js.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.d.ts +38 -259
- package/dist/services/runtime-kernel/heartbeat.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.execute.d.ts +5 -0
- package/dist/services/runtime-kernel/heartbeat.execute.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.execute.js +1052 -0
- package/dist/services/runtime-kernel/heartbeat.execute.js.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.js +50 -4142
- package/dist/services/runtime-kernel/heartbeat.js.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.misc.d.ts +30 -0
- package/dist/services/runtime-kernel/heartbeat.misc.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.misc.js +483 -0
- package/dist/services/runtime-kernel/heartbeat.misc.js.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.recovery.d.ts +38 -0
- package/dist/services/runtime-kernel/heartbeat.recovery.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.recovery.js +605 -0
- package/dist/services/runtime-kernel/heartbeat.recovery.js.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.release.d.ts +6 -0
- package/dist/services/runtime-kernel/heartbeat.release.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.release.js +398 -0
- package/dist/services/runtime-kernel/heartbeat.release.js.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.sessions.d.ts +229 -0
- package/dist/services/runtime-kernel/heartbeat.sessions.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.sessions.js +708 -0
- package/dist/services/runtime-kernel/heartbeat.sessions.js.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.wakeup.d.ts +5 -0
- package/dist/services/runtime-kernel/heartbeat.wakeup.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.wakeup.js +552 -0
- package/dist/services/runtime-kernel/heartbeat.wakeup.js.map +1 -0
- package/dist/services/secrets.d.ts +25 -25
- package/dist/services/sidebar-badges.js +1 -1
- package/dist/services/sidebar-badges.js.map +1 -1
- package/dist/services/workspace-runtime.comments.d.ts +6 -0
- package/dist/services/workspace-runtime.comments.d.ts.map +1 -0
- package/dist/services/workspace-runtime.comments.js +17 -0
- package/dist/services/workspace-runtime.comments.js.map +1 -0
- package/dist/services/workspace-runtime.d.ts +4 -163
- package/dist/services/workspace-runtime.d.ts.map +1 -1
- package/dist/services/workspace-runtime.helpers.d.ts +163 -0
- package/dist/services/workspace-runtime.helpers.d.ts.map +1 -0
- package/dist/services/workspace-runtime.helpers.js +360 -0
- package/dist/services/workspace-runtime.helpers.js.map +1 -0
- package/dist/services/workspace-runtime.js +4 -1236
- package/dist/services/workspace-runtime.js.map +1 -1
- package/dist/services/workspace-runtime.lifecycle.d.ts +35 -0
- package/dist/services/workspace-runtime.lifecycle.d.ts.map +1 -0
- package/dist/services/workspace-runtime.lifecycle.js +266 -0
- package/dist/services/workspace-runtime.lifecycle.js.map +1 -0
- package/dist/services/workspace-runtime.services.d.ts +140 -0
- package/dist/services/workspace-runtime.services.d.ts.map +1 -0
- package/dist/services/workspace-runtime.services.js +606 -0
- package/dist/services/workspace-runtime.services.js.map +1 -0
- package/package.json +21 -15
- package/ui-dist/assets/{_basePickBy-DeCw-kw3.js → _basePickBy-N8I9ml5Y.js} +1 -1
- package/ui-dist/assets/{_baseUniq-CVepsVZm.js → _baseUniq-BuSlpRSQ.js} +1 -1
- package/ui-dist/assets/{arc-QifRrkx2.js → arc-qX-dPyA1.js} +1 -1
- package/ui-dist/assets/{architectureDiagram-2XIMDMQ5-CT4me0hw.js → architectureDiagram-2XIMDMQ5-DhjkbXsp.js} +1 -1
- package/ui-dist/assets/{blockDiagram-WCTKOSBZ-uD6J91MI.js → blockDiagram-WCTKOSBZ-JS-tTu3J.js} +1 -1
- package/ui-dist/assets/{c4Diagram-IC4MRINW-D2GM2pzG.js → c4Diagram-IC4MRINW-4DqwCWIx.js} +1 -1
- package/ui-dist/assets/channel-CccCW5_a.js +1 -0
- package/ui-dist/assets/{chunk-4BX2VUAB-D8pPrlss.js → chunk-4BX2VUAB-T37SqBpp.js} +1 -1
- package/ui-dist/assets/{chunk-55IACEB6-CHF68vwj.js → chunk-55IACEB6-BSj9hdqK.js} +1 -1
- package/ui-dist/assets/{chunk-FMBD7UC4-CKmGUf9X.js → chunk-FMBD7UC4-Dkrlh0Wk.js} +1 -1
- package/ui-dist/assets/{chunk-JSJVCQXG-CTBCV-7X.js → chunk-JSJVCQXG-C0ZE3QdB.js} +1 -1
- package/ui-dist/assets/{chunk-KX2RTZJC-DV5XzGob.js → chunk-KX2RTZJC-DOZQM9gW.js} +1 -1
- package/ui-dist/assets/{chunk-NQ4KR5QH-B7diT0e4.js → chunk-NQ4KR5QH-5Yr3U2k8.js} +1 -1
- package/ui-dist/assets/{chunk-QZHKN3VN-BphcSb1i.js → chunk-QZHKN3VN-CvKTufwF.js} +1 -1
- package/ui-dist/assets/{chunk-WL4C6EOR-Bs_jQBMG.js → chunk-WL4C6EOR-IoEM0jyx.js} +1 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-JKk4tCW2.js +1 -0
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-JKk4tCW2.js +1 -0
- package/ui-dist/assets/clone-Onaweg8D.js +1 -0
- package/ui-dist/assets/{cose-bilkent-S5V4N54A-BxfO0pV9.js → cose-bilkent-S5V4N54A-CTvr1OFj.js} +1 -1
- package/ui-dist/assets/{dagre-KLK3FWXG-BiDkAX-Z.js → dagre-KLK3FWXG-UZ-SNjVK.js} +1 -1
- package/ui-dist/assets/{diagram-E7M64L7V-Btz_oxkC.js → diagram-E7M64L7V-D7RAN0Hr.js} +1 -1
- package/ui-dist/assets/{diagram-IFDJBPK2-Cdp8lQxJ.js → diagram-IFDJBPK2-B4LViaFR.js} +1 -1
- package/ui-dist/assets/{diagram-P4PSJMXO-DuTbeAS1.js → diagram-P4PSJMXO-CY1be7ak.js} +1 -1
- package/ui-dist/assets/{erDiagram-INFDFZHY-CzoQlOwo.js → erDiagram-INFDFZHY-Dca0KkvJ.js} +1 -1
- package/ui-dist/assets/{flowDiagram-PKNHOUZH-Diz_MWi3.js → flowDiagram-PKNHOUZH-i-qMvfwg.js} +1 -1
- package/ui-dist/assets/{ganttDiagram-A5KZAMGK-CWeGI1E-.js → ganttDiagram-A5KZAMGK-Wxq2lhbh.js} +1 -1
- package/ui-dist/assets/{gitGraphDiagram-K3NZZRJ6-C3QhZnAN.js → gitGraphDiagram-K3NZZRJ6-DwzgPlAY.js} +1 -1
- package/ui-dist/assets/{graph-KQH4eaLv.js → graph-BAqf89Tz.js} +1 -1
- package/ui-dist/assets/{index-CkEEsJ_9.js → index-4eCzaLuY.js} +1 -1
- package/ui-dist/assets/{index-DCOA92Vt.js → index-8uu-nKqK.js} +1 -1
- package/ui-dist/assets/{index-DtsZnqcf.js → index-B-1NEcI_.js} +1 -1
- package/ui-dist/assets/{index-BvGpil9e.js → index-B0b_3Eu5.js} +1 -1
- package/ui-dist/assets/{index-BMhxh9sB.js → index-B8v0eZjP.js} +1 -1
- package/ui-dist/assets/{index-aKvEm2pJ.js → index-BN7Moj3u.js} +1 -1
- package/ui-dist/assets/{index-iJyjaIGd.js → index-BSpxh3cY.js} +1 -1
- package/ui-dist/assets/{index-DhRKQjzu.js → index-BY44RIi9.js} +1 -1
- package/ui-dist/assets/{index-Z4rTzdcL.js → index-BhyQJhdZ.js} +1 -1
- package/ui-dist/assets/{index-DBxBUiZC.js → index-BkPL_iGU.js} +1 -1
- package/ui-dist/assets/{index-CsSppW5U.js → index-BsPfoHXS.js} +1 -1
- package/ui-dist/assets/{index-B8J1oewY.js → index-BstW7nmv.js} +1 -1
- package/ui-dist/assets/{index-CVZYu_kq.js → index-BwB67Zyz.js} +1 -1
- package/ui-dist/assets/index-C2peSkmT.css +1 -0
- package/ui-dist/assets/{index-Djz3PL1M.js → index-C3ktOsS_.js} +1 -1
- package/ui-dist/assets/{index-BMZfWLwr.js → index-CMyABlS-.js} +1 -1
- package/ui-dist/assets/{index-Cqdw7Lnc.js → index-CyBJ8ujC.js} +1 -1
- package/ui-dist/assets/{index-Ctp_0y5X.js → index-DAxM2W3O.js} +1 -1
- package/ui-dist/assets/{index-_jGthZ-1.js → index-DVZXPmhk.js} +1 -1
- package/ui-dist/assets/{index-CBLnbZVL.js → index-Dc19uAyw.js} +1 -1
- package/ui-dist/assets/index-DzHrwZu1.js +1511 -0
- package/ui-dist/assets/{index-ChTjk1gO.js → index-LJuf53Ye.js} +1 -1
- package/ui-dist/assets/{index-wXEAD8rI.js → index-Ugw5VWWz.js} +1 -1
- package/ui-dist/assets/{index-ciyPUpT9.js → index-YGraEFR7.js} +1 -1
- package/ui-dist/assets/{infoDiagram-LFFYTUFH-DbMzKsuw.js → infoDiagram-LFFYTUFH-jLmDtFVR.js} +1 -1
- package/ui-dist/assets/{ishikawaDiagram-PHBUUO56-CcKXcf2V.js → ishikawaDiagram-PHBUUO56-6OGMyLT8.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-4ABVD52K-BY2GuHyR.js → journeyDiagram-4ABVD52K-yQjl6E0t.js} +1 -1
- package/ui-dist/assets/{kanban-definition-K7BYSVSG-BlZWM0Uz.js → kanban-definition-K7BYSVSG-DkdCeQlS.js} +1 -1
- package/ui-dist/assets/{layout-qHGAYgRY.js → layout-CqSYvZ_w.js} +1 -1
- package/ui-dist/assets/{linear-BigkGXbh.js → linear-B8xGZaoi.js} +1 -1
- package/ui-dist/assets/{mermaid.core-DZ099nW4.js → mermaid.core-AKL_cdyk.js} +4 -4
- package/ui-dist/assets/{mindmap-definition-YRQLILUH-CElDqDe0.js → mindmap-definition-YRQLILUH-Zr-dXC0x.js} +1 -1
- package/ui-dist/assets/{pieDiagram-SKSYHLDU-I2LDYrgm.js → pieDiagram-SKSYHLDU-BvDAU-Nk.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-337W2JSQ-D-U35edU.js → quadrantDiagram-337W2JSQ-Dn9kM62o.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-Z7DCOOCP-FAWtaOKe.js → requirementDiagram-Z7DCOOCP-GIsIh7Sd.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-WA2Y5GQK-CzFHHNNh.js → sankeyDiagram-WA2Y5GQK-CUCuBkuf.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-2WXFIKYE-Cy-UViPL.js → sequenceDiagram-2WXFIKYE-MDpUY2HM.js} +1 -1
- package/ui-dist/assets/{stateDiagram-RAJIS63D-BEdt3CLl.js → stateDiagram-RAJIS63D-BymMpuUU.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-Bi2oCU6d.js +1 -0
- package/ui-dist/assets/{timeline-definition-YZTLITO2-YD1e-WHS.js → timeline-definition-YZTLITO2-B6ofPhhy.js} +1 -1
- package/ui-dist/assets/{treemap-KZPCXAKY-BnqlVkAC.js → treemap-KZPCXAKY-DnLO6w1l.js} +1 -1
- package/ui-dist/assets/{vennDiagram-LZ73GAT5-f4WCy3o6.js → vennDiagram-LZ73GAT5-D0MyZIDl.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-JWTSCODW-CWA35znA.js → xychartDiagram-JWTSCODW-rADY1iUG.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/channel-BV7st2TW.js +0 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-Cw_xj5ie.js +0 -1
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-Cw_xj5ie.js +0 -1
- package/ui-dist/assets/clone-DGshofUt.js +0 -1
- package/ui-dist/assets/index-Ded0dPwB.css +0 -1
- package/ui-dist/assets/index-Jhxth516.js +0 -1510
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-cW7aES_v.js +0 -1
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Plugin management REST API routes
|
|
3
|
+
*
|
|
4
|
+
* This module provides Express routes for managing the complete plugin lifecycle:
|
|
5
|
+
* - Listing and filtering plugins by status
|
|
6
|
+
* - Installing plugins from npm or local paths
|
|
7
|
+
* - Uninstalling plugins (soft delete or hard purge)
|
|
8
|
+
* - Enabling/disabling plugins
|
|
9
|
+
* - Running health diagnostics
|
|
10
|
+
* - Upgrading plugins
|
|
11
|
+
* - Retrieving UI slot contributions for frontend rendering
|
|
12
|
+
* - Discovering and executing plugin-contributed agent tools
|
|
13
|
+
*
|
|
14
|
+
* All routes require board-level authentication (assertBoard middleware).
|
|
15
|
+
*
|
|
16
|
+
* @module server/routes/plugins
|
|
17
|
+
* @see doc/plugins/PLUGIN_SPEC.md for the full plugin specification
|
|
18
|
+
*/
|
|
19
|
+
import { randomUUID } from "node:crypto";
|
|
20
|
+
import { and, desc, eq, gte } from "drizzle-orm";
|
|
21
|
+
import { pluginLogs, pluginWebhookDeliveries } from "@rudderhq/db";
|
|
22
|
+
import { publishGlobalLiveEvent } from "../services/live-events.js";
|
|
23
|
+
import { JsonRpcCallError, PLUGIN_RPC_ERROR_CODES } from "@rudderhq/plugin-sdk";
|
|
24
|
+
import { assertBoard } from "./authz.js";
|
|
25
|
+
import { validateInstanceConfig } from "../services/plugin-config-validator.js";
|
|
26
|
+
export function registerPluginOperationsRoutes(ctx) {
|
|
27
|
+
const { router, db, loader, registry, lifecycle, bridgeDeps, jobDeps, webhookDeps, resolvePlugin, logPluginMutationActivity, mapRpcErrorToBridgeError, } = ctx;
|
|
28
|
+
router.get("/plugins/:pluginId/logs", async (req, res) => {
|
|
29
|
+
assertBoard(req);
|
|
30
|
+
const { pluginId } = req.params;
|
|
31
|
+
const plugin = await resolvePlugin(registry, pluginId);
|
|
32
|
+
if (!plugin) {
|
|
33
|
+
res.status(404).json({ error: "Plugin not found" });
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const limit = Math.min(Math.max(parseInt(req.query.limit, 10) || 25, 1), 500);
|
|
37
|
+
const level = req.query.level;
|
|
38
|
+
const since = req.query.since;
|
|
39
|
+
const conditions = [eq(pluginLogs.pluginId, plugin.id)];
|
|
40
|
+
if (level) {
|
|
41
|
+
conditions.push(eq(pluginLogs.level, level));
|
|
42
|
+
}
|
|
43
|
+
if (since) {
|
|
44
|
+
const sinceDate = new Date(since);
|
|
45
|
+
if (!isNaN(sinceDate.getTime())) {
|
|
46
|
+
conditions.push(gte(pluginLogs.createdAt, sinceDate));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const rows = await db
|
|
50
|
+
.select()
|
|
51
|
+
.from(pluginLogs)
|
|
52
|
+
.where(and(...conditions))
|
|
53
|
+
.orderBy(desc(pluginLogs.createdAt))
|
|
54
|
+
.limit(limit);
|
|
55
|
+
res.json(rows);
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* POST /api/plugins/:pluginId/upgrade
|
|
59
|
+
*
|
|
60
|
+
* Upgrade a plugin to a newer version.
|
|
61
|
+
*
|
|
62
|
+
* Request body (optional):
|
|
63
|
+
* - version: Target version (defaults to latest)
|
|
64
|
+
*
|
|
65
|
+
* If the upgrade adds new capabilities, the plugin transitions to
|
|
66
|
+
* 'upgrade_pending' state for board approval. Otherwise, it goes
|
|
67
|
+
* directly to 'ready'.
|
|
68
|
+
*
|
|
69
|
+
* Response: PluginRecord
|
|
70
|
+
* Errors: 404 if plugin not found, 400 for lifecycle errors
|
|
71
|
+
*/
|
|
72
|
+
router.post("/plugins/:pluginId/upgrade", async (req, res) => {
|
|
73
|
+
assertBoard(req);
|
|
74
|
+
const { pluginId } = req.params;
|
|
75
|
+
const body = req.body;
|
|
76
|
+
const version = body?.version;
|
|
77
|
+
const plugin = await resolvePlugin(registry, pluginId);
|
|
78
|
+
if (!plugin) {
|
|
79
|
+
res.status(404).json({ error: "Plugin not found" });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
// Upgrade the plugin - this would typically:
|
|
84
|
+
// 1. Download the new version
|
|
85
|
+
// 2. Compare capabilities
|
|
86
|
+
// 3. If new capabilities, mark as upgrade_pending
|
|
87
|
+
// 4. Otherwise, transition to ready
|
|
88
|
+
const result = await lifecycle.upgrade(plugin.id, version);
|
|
89
|
+
await logPluginMutationActivity(req, "plugin.upgraded", plugin.id, {
|
|
90
|
+
pluginId: plugin.id,
|
|
91
|
+
pluginKey: plugin.pluginKey,
|
|
92
|
+
previousVersion: plugin.version,
|
|
93
|
+
version: result?.version ?? plugin.version,
|
|
94
|
+
targetVersion: version ?? null,
|
|
95
|
+
});
|
|
96
|
+
publishGlobalLiveEvent({ type: "plugin.ui.updated", payload: { pluginId: plugin.id, action: "upgraded" } });
|
|
97
|
+
res.json(result);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
101
|
+
res.status(400).json({ error: message });
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
// ===========================================================================
|
|
105
|
+
// Plugin configuration routes
|
|
106
|
+
// ===========================================================================
|
|
107
|
+
/**
|
|
108
|
+
* GET /api/plugins/:pluginId/config
|
|
109
|
+
*
|
|
110
|
+
* Retrieve the current instance configuration for a plugin.
|
|
111
|
+
*
|
|
112
|
+
* Returns the `PluginConfig` record if one exists, or `null` if the plugin
|
|
113
|
+
* has not yet been configured.
|
|
114
|
+
*
|
|
115
|
+
* Response: `PluginConfig | null`
|
|
116
|
+
* Errors: 404 if plugin not found
|
|
117
|
+
*/
|
|
118
|
+
router.get("/plugins/:pluginId/config", async (req, res) => {
|
|
119
|
+
assertBoard(req);
|
|
120
|
+
const { pluginId } = req.params;
|
|
121
|
+
const plugin = await resolvePlugin(registry, pluginId);
|
|
122
|
+
if (!plugin) {
|
|
123
|
+
res.status(404).json({ error: "Plugin not found" });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const config = await registry.getConfig(plugin.id);
|
|
127
|
+
res.json(config);
|
|
128
|
+
});
|
|
129
|
+
/**
|
|
130
|
+
* POST /api/plugins/:pluginId/config
|
|
131
|
+
*
|
|
132
|
+
* Save (create or replace) the instance configuration for a plugin.
|
|
133
|
+
*
|
|
134
|
+
* The caller provides the full `configJson` object. The server persists it
|
|
135
|
+
* via `registry.upsertConfig()`.
|
|
136
|
+
*
|
|
137
|
+
* Request body:
|
|
138
|
+
* - `configJson`: Configuration values matching the plugin's `instanceConfigSchema`
|
|
139
|
+
*
|
|
140
|
+
* Response: `PluginConfig`
|
|
141
|
+
* Errors:
|
|
142
|
+
* - 400 if request validation fails
|
|
143
|
+
* - 404 if plugin not found
|
|
144
|
+
*/
|
|
145
|
+
router.post("/plugins/:pluginId/config", async (req, res) => {
|
|
146
|
+
assertBoard(req);
|
|
147
|
+
const { pluginId } = req.params;
|
|
148
|
+
const plugin = await resolvePlugin(registry, pluginId);
|
|
149
|
+
if (!plugin) {
|
|
150
|
+
res.status(404).json({ error: "Plugin not found" });
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const body = req.body;
|
|
154
|
+
if (!body?.configJson || typeof body.configJson !== "object") {
|
|
155
|
+
res.status(400).json({ error: '"configJson" is required and must be an object' });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// Strip devUiUrl unless the caller is an instance admin. devUiUrl activates
|
|
159
|
+
// a dev-proxy in the static file route that could be abused for SSRF if any
|
|
160
|
+
// board-level user were allowed to set it.
|
|
161
|
+
if ("devUiUrl" in body.configJson &&
|
|
162
|
+
!(req.actor.type === "board" && req.actor.isInstanceAdmin)) {
|
|
163
|
+
delete body.configJson.devUiUrl;
|
|
164
|
+
}
|
|
165
|
+
// Validate configJson against the plugin's instanceConfigSchema (if declared).
|
|
166
|
+
// This ensures CLI/API callers get the same validation the UI performs client-side.
|
|
167
|
+
const schema = plugin.manifestJson?.instanceConfigSchema;
|
|
168
|
+
if (schema && Object.keys(schema).length > 0) {
|
|
169
|
+
const validation = validateInstanceConfig(body.configJson, schema);
|
|
170
|
+
if (!validation.valid) {
|
|
171
|
+
res.status(400).json({
|
|
172
|
+
error: "Configuration does not match the plugin's instanceConfigSchema",
|
|
173
|
+
fieldErrors: validation.errors,
|
|
174
|
+
});
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const result = await registry.upsertConfig(plugin.id, {
|
|
180
|
+
configJson: body.configJson,
|
|
181
|
+
});
|
|
182
|
+
await logPluginMutationActivity(req, "plugin.config.updated", plugin.id, {
|
|
183
|
+
pluginId: plugin.id,
|
|
184
|
+
pluginKey: plugin.pluginKey,
|
|
185
|
+
configKeyCount: Object.keys(body.configJson).length,
|
|
186
|
+
});
|
|
187
|
+
// Notify the running worker about the config change (PLUGIN_SPEC §25.4.4).
|
|
188
|
+
// If the worker implements onConfigChanged, send the new config via RPC.
|
|
189
|
+
// If it doesn't (METHOD_NOT_IMPLEMENTED), restart the worker so it picks
|
|
190
|
+
// up the new config on re-initialize. If no worker is running, skip.
|
|
191
|
+
if (bridgeDeps?.workerManager.isRunning(plugin.id)) {
|
|
192
|
+
try {
|
|
193
|
+
await bridgeDeps.workerManager.call(plugin.id, "configChanged", { config: body.configJson });
|
|
194
|
+
}
|
|
195
|
+
catch (rpcErr) {
|
|
196
|
+
if (rpcErr instanceof JsonRpcCallError &&
|
|
197
|
+
rpcErr.code === PLUGIN_RPC_ERROR_CODES.METHOD_NOT_IMPLEMENTED) {
|
|
198
|
+
// Worker doesn't handle live config — restart it.
|
|
199
|
+
try {
|
|
200
|
+
await lifecycle.restartWorker(plugin.id);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// Restart failure is non-fatal for the config save response.
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Other RPC errors (timeout, unavailable) are non-fatal — config is
|
|
207
|
+
// already persisted and will take effect on next worker restart.
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
res.json(result);
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
214
|
+
res.status(400).json({ error: message });
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
/**
|
|
218
|
+
* POST /api/plugins/:pluginId/config/test
|
|
219
|
+
*
|
|
220
|
+
* Test a plugin configuration without persisting it by calling the plugin
|
|
221
|
+
* worker's `validateConfig` RPC method.
|
|
222
|
+
*
|
|
223
|
+
* Only works when the plugin's worker implements `onValidateConfig`.
|
|
224
|
+
* If the worker does not implement the method, returns
|
|
225
|
+
* `{ valid: false, supported: false, message: "..." }` with HTTP 200.
|
|
226
|
+
*
|
|
227
|
+
* Request body:
|
|
228
|
+
* - `configJson`: Configuration values to validate
|
|
229
|
+
*
|
|
230
|
+
* Response: `{ valid: boolean; message?: string; supported?: boolean }`
|
|
231
|
+
* Errors:
|
|
232
|
+
* - 400 if request validation fails
|
|
233
|
+
* - 404 if plugin not found
|
|
234
|
+
* - 501 if bridge deps (worker manager) are not configured
|
|
235
|
+
* - 502 if the worker is unavailable
|
|
236
|
+
*/
|
|
237
|
+
router.post("/plugins/:pluginId/config/test", async (req, res) => {
|
|
238
|
+
assertBoard(req);
|
|
239
|
+
if (!bridgeDeps) {
|
|
240
|
+
res.status(501).json({ error: "Plugin bridge is not enabled" });
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const { pluginId } = req.params;
|
|
244
|
+
const plugin = await resolvePlugin(registry, pluginId);
|
|
245
|
+
if (!plugin) {
|
|
246
|
+
res.status(404).json({ error: "Plugin not found" });
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (plugin.status !== "ready") {
|
|
250
|
+
res.status(400).json({
|
|
251
|
+
error: `Plugin is not ready (current status: ${plugin.status})`,
|
|
252
|
+
});
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const body = req.body;
|
|
256
|
+
if (!body?.configJson || typeof body.configJson !== "object") {
|
|
257
|
+
res.status(400).json({ error: '"configJson" is required and must be an object' });
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
// Fast schema-level rejection before hitting the worker RPC.
|
|
261
|
+
const schema = plugin.manifestJson?.instanceConfigSchema;
|
|
262
|
+
if (schema && Object.keys(schema).length > 0) {
|
|
263
|
+
const validation = validateInstanceConfig(body.configJson, schema);
|
|
264
|
+
if (!validation.valid) {
|
|
265
|
+
res.status(400).json({
|
|
266
|
+
error: "Configuration does not match the plugin's instanceConfigSchema",
|
|
267
|
+
fieldErrors: validation.errors,
|
|
268
|
+
});
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
const result = await bridgeDeps.workerManager.call(plugin.id, "validateConfig", { config: body.configJson });
|
|
274
|
+
// The worker returns PluginConfigValidationResult { ok, warnings?, errors? }
|
|
275
|
+
// Map to the frontend-expected shape { valid, message? }
|
|
276
|
+
if (result.ok) {
|
|
277
|
+
const warningText = result.warnings?.length
|
|
278
|
+
? `Warnings: ${result.warnings.join("; ")}`
|
|
279
|
+
: undefined;
|
|
280
|
+
res.json({ valid: true, message: warningText });
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
const errorText = result.errors?.length
|
|
284
|
+
? result.errors.join("; ")
|
|
285
|
+
: "Configuration validation failed.";
|
|
286
|
+
res.json({ valid: false, message: errorText });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
// If the worker does not implement validateConfig, return a structured response
|
|
291
|
+
if (err instanceof JsonRpcCallError &&
|
|
292
|
+
err.code === PLUGIN_RPC_ERROR_CODES.METHOD_NOT_IMPLEMENTED) {
|
|
293
|
+
res.json({
|
|
294
|
+
valid: false,
|
|
295
|
+
supported: false,
|
|
296
|
+
message: "This plugin does not support configuration testing.",
|
|
297
|
+
});
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
// Worker unavailable or other RPC errors
|
|
301
|
+
const bridgeError = mapRpcErrorToBridgeError(err);
|
|
302
|
+
res.status(502).json(bridgeError);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
// ===========================================================================
|
|
306
|
+
// Job scheduling routes
|
|
307
|
+
// ===========================================================================
|
|
308
|
+
/**
|
|
309
|
+
* GET /api/plugins/:pluginId/jobs
|
|
310
|
+
*
|
|
311
|
+
* List all scheduled jobs for a plugin.
|
|
312
|
+
*
|
|
313
|
+
* Query params:
|
|
314
|
+
* - `status` (optional): Filter by job status (`active`, `paused`, `failed`)
|
|
315
|
+
*
|
|
316
|
+
* Response: PluginJobRecord[]
|
|
317
|
+
* Errors: 404 if plugin not found
|
|
318
|
+
*/
|
|
319
|
+
router.get("/plugins/:pluginId/jobs", async (req, res) => {
|
|
320
|
+
assertBoard(req);
|
|
321
|
+
if (!jobDeps) {
|
|
322
|
+
res.status(501).json({ error: "Job scheduling is not enabled" });
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const { pluginId } = req.params;
|
|
326
|
+
const plugin = await resolvePlugin(registry, pluginId);
|
|
327
|
+
if (!plugin) {
|
|
328
|
+
res.status(404).json({ error: "Plugin not found" });
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const rawStatus = req.query.status;
|
|
332
|
+
const validStatuses = ["active", "paused", "failed"];
|
|
333
|
+
if (rawStatus !== undefined && !validStatuses.includes(rawStatus)) {
|
|
334
|
+
res.status(400).json({
|
|
335
|
+
error: `Invalid status '${rawStatus}'. Must be one of: ${validStatuses.join(", ")}`,
|
|
336
|
+
});
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
try {
|
|
340
|
+
const jobs = await jobDeps.jobStore.listJobs(plugin.id, rawStatus);
|
|
341
|
+
res.json(jobs);
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
345
|
+
res.status(500).json({ error: message });
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
/**
|
|
349
|
+
* GET /api/plugins/:pluginId/jobs/:jobId/runs
|
|
350
|
+
*
|
|
351
|
+
* List execution history for a specific job.
|
|
352
|
+
*
|
|
353
|
+
* Query params:
|
|
354
|
+
* - `limit` (optional): Maximum number of runs to return (default: 50)
|
|
355
|
+
*
|
|
356
|
+
* Response: PluginJobRunRecord[]
|
|
357
|
+
* Errors: 404 if plugin not found
|
|
358
|
+
*/
|
|
359
|
+
router.get("/plugins/:pluginId/jobs/:jobId/runs", async (req, res) => {
|
|
360
|
+
assertBoard(req);
|
|
361
|
+
if (!jobDeps) {
|
|
362
|
+
res.status(501).json({ error: "Job scheduling is not enabled" });
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const { pluginId, jobId } = req.params;
|
|
366
|
+
const plugin = await resolvePlugin(registry, pluginId);
|
|
367
|
+
if (!plugin) {
|
|
368
|
+
res.status(404).json({ error: "Plugin not found" });
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
const job = await jobDeps.jobStore.getJobByIdForPlugin(plugin.id, jobId);
|
|
372
|
+
if (!job) {
|
|
373
|
+
res.status(404).json({ error: "Job not found" });
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const limit = req.query.limit ? parseInt(req.query.limit, 10) : 25;
|
|
377
|
+
if (isNaN(limit) || limit < 1 || limit > 500) {
|
|
378
|
+
res.status(400).json({ error: "limit must be a number between 1 and 500" });
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
try {
|
|
382
|
+
const runs = await jobDeps.jobStore.listRunsByJob(jobId, limit);
|
|
383
|
+
res.json(runs);
|
|
384
|
+
}
|
|
385
|
+
catch (err) {
|
|
386
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
387
|
+
res.status(500).json({ error: message });
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
/**
|
|
391
|
+
* POST /api/plugins/:pluginId/jobs/:jobId/trigger
|
|
392
|
+
*
|
|
393
|
+
* Manually trigger a job execution outside its cron schedule.
|
|
394
|
+
*
|
|
395
|
+
* Creates a run with `trigger: "manual"` and dispatches immediately.
|
|
396
|
+
* The response returns before the job completes (non-blocking).
|
|
397
|
+
*
|
|
398
|
+
* Response: `{ runId: string, jobId: string }`
|
|
399
|
+
* Errors:
|
|
400
|
+
* - 404 if plugin not found
|
|
401
|
+
* - 400 if job not found, not active, already running, or worker unavailable
|
|
402
|
+
*/
|
|
403
|
+
router.post("/plugins/:pluginId/jobs/:jobId/trigger", async (req, res) => {
|
|
404
|
+
assertBoard(req);
|
|
405
|
+
if (!jobDeps) {
|
|
406
|
+
res.status(501).json({ error: "Job scheduling is not enabled" });
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const { pluginId, jobId } = req.params;
|
|
410
|
+
const plugin = await resolvePlugin(registry, pluginId);
|
|
411
|
+
if (!plugin) {
|
|
412
|
+
res.status(404).json({ error: "Plugin not found" });
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const job = await jobDeps.jobStore.getJobByIdForPlugin(plugin.id, jobId);
|
|
416
|
+
if (!job) {
|
|
417
|
+
res.status(404).json({ error: "Job not found" });
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
try {
|
|
421
|
+
const result = await jobDeps.scheduler.triggerJob(jobId, "manual");
|
|
422
|
+
res.json(result);
|
|
423
|
+
}
|
|
424
|
+
catch (err) {
|
|
425
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
426
|
+
res.status(400).json({ error: message });
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
// ===========================================================================
|
|
430
|
+
// Webhook ingestion route
|
|
431
|
+
// ===========================================================================
|
|
432
|
+
/**
|
|
433
|
+
* POST /api/plugins/:pluginId/webhooks/:endpointKey
|
|
434
|
+
*
|
|
435
|
+
* Receive an inbound webhook delivery for a plugin.
|
|
436
|
+
*
|
|
437
|
+
* This route is called by external systems (e.g. GitHub, Linear, Stripe) to
|
|
438
|
+
* deliver webhook payloads to a plugin. The host validates that:
|
|
439
|
+
* 1. The plugin exists and is in 'ready' state
|
|
440
|
+
* 2. The plugin declares the `webhooks.receive` capability
|
|
441
|
+
* 3. The `endpointKey` matches a declared webhook in the manifest
|
|
442
|
+
*
|
|
443
|
+
* The delivery is recorded in the `plugin_webhook_deliveries` table and
|
|
444
|
+
* dispatched to the worker via the `handleWebhook` RPC method.
|
|
445
|
+
*
|
|
446
|
+
* **Note:** This route does NOT require board authentication — webhook
|
|
447
|
+
* endpoints must be publicly accessible for external callers. Signature
|
|
448
|
+
* verification is the plugin's responsibility.
|
|
449
|
+
*
|
|
450
|
+
* Response: `{ deliveryId: string, status: string }`
|
|
451
|
+
* Errors:
|
|
452
|
+
* - 404 if plugin not found or endpointKey not declared
|
|
453
|
+
* - 400 if plugin is not in ready state or lacks webhooks.receive capability
|
|
454
|
+
* - 502 if the worker is unavailable or the RPC call fails
|
|
455
|
+
*/
|
|
456
|
+
router.post("/plugins/:pluginId/webhooks/:endpointKey", async (req, res) => {
|
|
457
|
+
if (!webhookDeps) {
|
|
458
|
+
res.status(501).json({ error: "Webhook ingestion is not enabled" });
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const { pluginId, endpointKey } = req.params;
|
|
462
|
+
// Step 1: Resolve the plugin
|
|
463
|
+
const plugin = await resolvePlugin(registry, pluginId);
|
|
464
|
+
if (!plugin) {
|
|
465
|
+
res.status(404).json({ error: "Plugin not found" });
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
// Step 2: Validate the plugin is in 'ready' state
|
|
469
|
+
if (plugin.status !== "ready") {
|
|
470
|
+
res.status(400).json({
|
|
471
|
+
error: `Plugin is not ready (current status: ${plugin.status})`,
|
|
472
|
+
});
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
// Step 3: Validate the plugin has webhooks.receive capability
|
|
476
|
+
const manifest = plugin.manifestJson;
|
|
477
|
+
if (!manifest) {
|
|
478
|
+
res.status(400).json({ error: "Plugin manifest is missing" });
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const capabilities = manifest.capabilities ?? [];
|
|
482
|
+
if (!capabilities.includes("webhooks.receive")) {
|
|
483
|
+
res.status(400).json({
|
|
484
|
+
error: "Plugin does not have the webhooks.receive capability",
|
|
485
|
+
});
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
// Step 4: Validate the endpointKey exists in the manifest's webhook declarations
|
|
489
|
+
const declaredWebhooks = manifest.webhooks ?? [];
|
|
490
|
+
const webhookDecl = declaredWebhooks.find((w) => w.endpointKey === endpointKey);
|
|
491
|
+
if (!webhookDecl) {
|
|
492
|
+
res.status(404).json({
|
|
493
|
+
error: `Webhook endpoint '${endpointKey}' is not declared by this plugin`,
|
|
494
|
+
});
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
// Step 5: Extract request data
|
|
498
|
+
const requestId = randomUUID();
|
|
499
|
+
const rawHeaders = {};
|
|
500
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
501
|
+
if (typeof value === "string") {
|
|
502
|
+
rawHeaders[key] = value;
|
|
503
|
+
}
|
|
504
|
+
else if (Array.isArray(value)) {
|
|
505
|
+
rawHeaders[key] = value.join(", ");
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
// Use the raw buffer stashed by the express.json() `verify` callback.
|
|
509
|
+
// This preserves the exact bytes the provider signed, whereas
|
|
510
|
+
// JSON.stringify(req.body) would re-serialize and break HMAC verification.
|
|
511
|
+
const stashedRaw = req.rawBody;
|
|
512
|
+
const rawBody = stashedRaw ? stashedRaw.toString("utf-8") : "";
|
|
513
|
+
const parsedBody = req.body;
|
|
514
|
+
const payload = req.body ?? {};
|
|
515
|
+
// Step 6: Record the delivery in the database
|
|
516
|
+
const startedAt = new Date();
|
|
517
|
+
const [delivery] = await db
|
|
518
|
+
.insert(pluginWebhookDeliveries)
|
|
519
|
+
.values({
|
|
520
|
+
pluginId: plugin.id,
|
|
521
|
+
webhookKey: endpointKey,
|
|
522
|
+
status: "pending",
|
|
523
|
+
payload,
|
|
524
|
+
headers: rawHeaders,
|
|
525
|
+
startedAt,
|
|
526
|
+
})
|
|
527
|
+
.returning({ id: pluginWebhookDeliveries.id });
|
|
528
|
+
// Step 7: Dispatch to the worker via handleWebhook RPC
|
|
529
|
+
try {
|
|
530
|
+
await webhookDeps.workerManager.call(plugin.id, "handleWebhook", {
|
|
531
|
+
endpointKey,
|
|
532
|
+
headers: req.headers,
|
|
533
|
+
rawBody,
|
|
534
|
+
parsedBody,
|
|
535
|
+
requestId,
|
|
536
|
+
});
|
|
537
|
+
// Step 8: Update delivery record to success
|
|
538
|
+
const finishedAt = new Date();
|
|
539
|
+
const durationMs = finishedAt.getTime() - startedAt.getTime();
|
|
540
|
+
await db
|
|
541
|
+
.update(pluginWebhookDeliveries)
|
|
542
|
+
.set({
|
|
543
|
+
status: "success",
|
|
544
|
+
durationMs,
|
|
545
|
+
finishedAt,
|
|
546
|
+
})
|
|
547
|
+
.where(eq(pluginWebhookDeliveries.id, delivery.id));
|
|
548
|
+
res.status(200).json({
|
|
549
|
+
deliveryId: delivery.id,
|
|
550
|
+
status: "success",
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
catch (err) {
|
|
554
|
+
// Step 8 (error): Update delivery record to failed
|
|
555
|
+
const finishedAt = new Date();
|
|
556
|
+
const durationMs = finishedAt.getTime() - startedAt.getTime();
|
|
557
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
558
|
+
await db
|
|
559
|
+
.update(pluginWebhookDeliveries)
|
|
560
|
+
.set({
|
|
561
|
+
status: "failed",
|
|
562
|
+
durationMs,
|
|
563
|
+
error: errorMessage,
|
|
564
|
+
finishedAt,
|
|
565
|
+
})
|
|
566
|
+
.where(eq(pluginWebhookDeliveries.id, delivery.id));
|
|
567
|
+
res.status(502).json({
|
|
568
|
+
deliveryId: delivery.id,
|
|
569
|
+
status: "failed",
|
|
570
|
+
error: errorMessage,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
// ===========================================================================
|
|
575
|
+
// Plugin health dashboard — aggregated diagnostics for the settings page
|
|
576
|
+
// ===========================================================================
|
|
577
|
+
/**
|
|
578
|
+
* GET /api/plugins/:pluginId/dashboard
|
|
579
|
+
*
|
|
580
|
+
* Aggregated health dashboard data for a plugin's settings page.
|
|
581
|
+
*
|
|
582
|
+
* Returns worker diagnostics (status, uptime, crash history), recent job
|
|
583
|
+
* runs, recent webhook deliveries, and the current health check result —
|
|
584
|
+
* all in a single response to avoid multiple round-trips.
|
|
585
|
+
*
|
|
586
|
+
* Response: PluginDashboardData
|
|
587
|
+
* Errors: 404 if plugin not found
|
|
588
|
+
*/
|
|
589
|
+
router.get("/plugins/:pluginId/dashboard", async (req, res) => {
|
|
590
|
+
assertBoard(req);
|
|
591
|
+
const { pluginId } = req.params;
|
|
592
|
+
const plugin = await resolvePlugin(registry, pluginId);
|
|
593
|
+
if (!plugin) {
|
|
594
|
+
res.status(404).json({ error: "Plugin not found" });
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
// --- Worker diagnostics ---
|
|
598
|
+
let worker = null;
|
|
599
|
+
// Try bridgeDeps first (primary source for worker manager), fallback to webhookDeps
|
|
600
|
+
const wm = bridgeDeps?.workerManager ?? webhookDeps?.workerManager ?? null;
|
|
601
|
+
if (wm) {
|
|
602
|
+
const handle = wm.getWorker(plugin.id);
|
|
603
|
+
if (handle) {
|
|
604
|
+
const diag = handle.diagnostics();
|
|
605
|
+
worker = {
|
|
606
|
+
status: diag.status,
|
|
607
|
+
pid: diag.pid,
|
|
608
|
+
uptime: diag.uptime,
|
|
609
|
+
consecutiveCrashes: diag.consecutiveCrashes,
|
|
610
|
+
totalCrashes: diag.totalCrashes,
|
|
611
|
+
pendingRequests: diag.pendingRequests,
|
|
612
|
+
lastCrashAt: diag.lastCrashAt,
|
|
613
|
+
nextRestartAt: diag.nextRestartAt,
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
// --- Recent job runs (last 10, newest first) ---
|
|
618
|
+
let recentJobRuns = [];
|
|
619
|
+
if (jobDeps) {
|
|
620
|
+
try {
|
|
621
|
+
const runs = await jobDeps.jobStore.listRunsByPlugin(plugin.id, undefined, 10);
|
|
622
|
+
// Also fetch job definitions so we can include jobKey
|
|
623
|
+
const jobs = await jobDeps.jobStore.listJobs(plugin.id);
|
|
624
|
+
const jobKeyMap = new Map(jobs.map((j) => [j.id, j.jobKey]));
|
|
625
|
+
recentJobRuns = runs
|
|
626
|
+
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
|
627
|
+
.map((r) => ({
|
|
628
|
+
id: r.id,
|
|
629
|
+
jobId: r.jobId,
|
|
630
|
+
jobKey: jobKeyMap.get(r.jobId) ?? undefined,
|
|
631
|
+
trigger: r.trigger,
|
|
632
|
+
status: r.status,
|
|
633
|
+
durationMs: r.durationMs,
|
|
634
|
+
error: r.error,
|
|
635
|
+
startedAt: r.startedAt ? new Date(r.startedAt).toISOString() : null,
|
|
636
|
+
finishedAt: r.finishedAt ? new Date(r.finishedAt).toISOString() : null,
|
|
637
|
+
createdAt: new Date(r.createdAt).toISOString(),
|
|
638
|
+
}));
|
|
639
|
+
}
|
|
640
|
+
catch {
|
|
641
|
+
// Job data unavailable — leave empty
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
// --- Recent webhook deliveries (last 10, newest first) ---
|
|
645
|
+
let recentWebhookDeliveries = [];
|
|
646
|
+
try {
|
|
647
|
+
const deliveries = await db
|
|
648
|
+
.select({
|
|
649
|
+
id: pluginWebhookDeliveries.id,
|
|
650
|
+
webhookKey: pluginWebhookDeliveries.webhookKey,
|
|
651
|
+
status: pluginWebhookDeliveries.status,
|
|
652
|
+
durationMs: pluginWebhookDeliveries.durationMs,
|
|
653
|
+
error: pluginWebhookDeliveries.error,
|
|
654
|
+
startedAt: pluginWebhookDeliveries.startedAt,
|
|
655
|
+
finishedAt: pluginWebhookDeliveries.finishedAt,
|
|
656
|
+
createdAt: pluginWebhookDeliveries.createdAt,
|
|
657
|
+
})
|
|
658
|
+
.from(pluginWebhookDeliveries)
|
|
659
|
+
.where(eq(pluginWebhookDeliveries.pluginId, plugin.id))
|
|
660
|
+
.orderBy(desc(pluginWebhookDeliveries.createdAt))
|
|
661
|
+
.limit(10);
|
|
662
|
+
recentWebhookDeliveries = deliveries.map((d) => ({
|
|
663
|
+
id: d.id,
|
|
664
|
+
webhookKey: d.webhookKey,
|
|
665
|
+
status: d.status,
|
|
666
|
+
durationMs: d.durationMs,
|
|
667
|
+
error: d.error,
|
|
668
|
+
startedAt: d.startedAt ? d.startedAt.toISOString() : null,
|
|
669
|
+
finishedAt: d.finishedAt ? d.finishedAt.toISOString() : null,
|
|
670
|
+
createdAt: d.createdAt.toISOString(),
|
|
671
|
+
}));
|
|
672
|
+
}
|
|
673
|
+
catch {
|
|
674
|
+
// Webhook data unavailable — leave empty
|
|
675
|
+
}
|
|
676
|
+
// --- Health check (same logic as GET /health) ---
|
|
677
|
+
const checks = [];
|
|
678
|
+
checks.push({
|
|
679
|
+
name: "registry",
|
|
680
|
+
passed: true,
|
|
681
|
+
message: "Plugin found in registry",
|
|
682
|
+
});
|
|
683
|
+
const hasValidManifest = Boolean(plugin.manifestJson?.id);
|
|
684
|
+
checks.push({
|
|
685
|
+
name: "manifest",
|
|
686
|
+
passed: hasValidManifest,
|
|
687
|
+
message: hasValidManifest ? "Manifest is valid" : "Manifest is invalid or missing",
|
|
688
|
+
});
|
|
689
|
+
const isHealthy = plugin.status === "ready";
|
|
690
|
+
checks.push({
|
|
691
|
+
name: "status",
|
|
692
|
+
passed: isHealthy,
|
|
693
|
+
message: `Current status: ${plugin.status}`,
|
|
694
|
+
});
|
|
695
|
+
const hasNoError = !plugin.lastError;
|
|
696
|
+
if (!hasNoError) {
|
|
697
|
+
checks.push({
|
|
698
|
+
name: "error_state",
|
|
699
|
+
passed: false,
|
|
700
|
+
message: plugin.lastError ?? undefined,
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
const health = {
|
|
704
|
+
pluginId: plugin.id,
|
|
705
|
+
status: plugin.status,
|
|
706
|
+
healthy: isHealthy && hasValidManifest && hasNoError,
|
|
707
|
+
checks,
|
|
708
|
+
lastError: plugin.lastError ?? undefined,
|
|
709
|
+
};
|
|
710
|
+
res.json({
|
|
711
|
+
pluginId: plugin.id,
|
|
712
|
+
worker,
|
|
713
|
+
recentJobRuns,
|
|
714
|
+
recentWebhookDeliveries,
|
|
715
|
+
health,
|
|
716
|
+
checkedAt: new Date().toISOString(),
|
|
717
|
+
});
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
//# sourceMappingURL=plugins.operations-routes.js.map
|