@rudderhq/server 0.7.2 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/dist/agent-runtimes/registry.d.ts.map +1 -1
  2. package/dist/agent-runtimes/registry.js +0 -2
  3. package/dist/agent-runtimes/registry.js.map +1 -1
  4. package/dist/app.d.ts.map +1 -1
  5. package/dist/app.js +1 -5
  6. package/dist/app.js.map +1 -1
  7. package/dist/bootstrap/create-http-app.d.ts +1 -2
  8. package/dist/bootstrap/create-http-app.d.ts.map +1 -1
  9. package/dist/bootstrap/create-http-app.js +13 -12
  10. package/dist/bootstrap/create-http-app.js.map +1 -1
  11. package/dist/bootstrap/register-api-routes.d.ts +1 -2
  12. package/dist/bootstrap/register-api-routes.d.ts.map +1 -1
  13. package/dist/bootstrap/register-api-routes.js +20 -4
  14. package/dist/bootstrap/register-api-routes.js.map +1 -1
  15. package/dist/middleware/logger.js +2 -2
  16. package/dist/middleware/logger.js.map +1 -1
  17. package/dist/middleware/logger.test.js +26 -10
  18. package/dist/middleware/logger.test.js.map +1 -1
  19. package/dist/routes/agent-issue-creation.d.ts +25 -0
  20. package/dist/routes/agent-issue-creation.d.ts.map +1 -0
  21. package/dist/routes/agent-issue-creation.js +201 -0
  22. package/dist/routes/agent-issue-creation.js.map +1 -0
  23. package/dist/routes/app-builder.d.ts +3 -1
  24. package/dist/routes/app-builder.d.ts.map +1 -1
  25. package/dist/routes/app-builder.js +31 -11
  26. package/dist/routes/app-builder.js.map +1 -1
  27. package/dist/routes/computer.d.ts +30 -0
  28. package/dist/routes/computer.d.ts.map +1 -0
  29. package/dist/routes/computer.js +231 -0
  30. package/dist/routes/computer.js.map +1 -0
  31. package/dist/routes/health.d.ts.map +1 -1
  32. package/dist/routes/health.js +2 -0
  33. package/dist/routes/health.js.map +1 -1
  34. package/dist/routes/instance-settings.d.ts.map +1 -1
  35. package/dist/routes/instance-settings.js +3 -0
  36. package/dist/routes/instance-settings.js.map +1 -1
  37. package/dist/routes/issues.comments-attachments.d.ts.map +1 -1
  38. package/dist/routes/issues.comments-attachments.js +90 -8
  39. package/dist/routes/issues.comments-attachments.js.map +1 -1
  40. package/dist/routes/issues.d.ts.map +1 -1
  41. package/dist/routes/issues.js +12 -1
  42. package/dist/routes/issues.js.map +1 -1
  43. package/dist/routes/issues.mutations.d.ts.map +1 -1
  44. package/dist/routes/issues.mutations.js +85 -33
  45. package/dist/routes/issues.mutations.js.map +1 -1
  46. package/dist/routes/managed-mcp-runtime.d.ts.map +1 -1
  47. package/dist/routes/managed-mcp-runtime.js +109 -7
  48. package/dist/routes/managed-mcp-runtime.js.map +1 -1
  49. package/dist/routes/rudder-plugins.d.ts +4 -0
  50. package/dist/routes/rudder-plugins.d.ts.map +1 -0
  51. package/dist/routes/rudder-plugins.js +213 -0
  52. package/dist/routes/rudder-plugins.js.map +1 -0
  53. package/dist/services/activity-log.d.ts +0 -3
  54. package/dist/services/activity-log.d.ts.map +1 -1
  55. package/dist/services/activity-log.js +0 -33
  56. package/dist/services/activity-log.js.map +1 -1
  57. package/dist/services/activity.d.ts +3 -3
  58. package/dist/services/agent-issue-creation.d.ts +344 -0
  59. package/dist/services/agent-issue-creation.d.ts.map +1 -0
  60. package/dist/services/agent-issue-creation.js +675 -0
  61. package/dist/services/agent-issue-creation.js.map +1 -0
  62. package/dist/services/agent-run-context.d.ts.map +1 -1
  63. package/dist/services/agent-run-context.js +13 -2
  64. package/dist/services/agent-run-context.js.map +1 -1
  65. package/dist/services/app-builder.d.ts.map +1 -1
  66. package/dist/services/app-builder.js +24 -3
  67. package/dist/services/app-builder.js.map +1 -1
  68. package/dist/services/chat-assistant.d.ts.map +1 -1
  69. package/dist/services/chat-assistant.helpers.d.ts +1 -1
  70. package/dist/services/chat-assistant.js +11 -2
  71. package/dist/services/chat-assistant.js.map +1 -1
  72. package/dist/services/chat-generation-protocol.test.js +2 -2
  73. package/dist/services/chat-generation-protocol.test.js.map +1 -1
  74. package/dist/services/chats.d.ts +2 -2
  75. package/dist/services/computer-broker.d.ts +35 -0
  76. package/dist/services/computer-broker.d.ts.map +1 -0
  77. package/dist/services/computer-broker.js +211 -0
  78. package/dist/services/computer-broker.js.map +1 -0
  79. package/dist/services/computer-broker.test.d.ts +2 -0
  80. package/dist/services/computer-broker.test.d.ts.map +1 -0
  81. package/dist/services/computer-broker.test.js +70 -0
  82. package/dist/services/computer-broker.test.js.map +1 -0
  83. package/dist/services/computer-capability.d.ts +13 -0
  84. package/dist/services/computer-capability.d.ts.map +1 -0
  85. package/dist/services/computer-capability.js +11 -0
  86. package/dist/services/computer-capability.js.map +1 -0
  87. package/dist/services/computer-capability.test.d.ts +2 -0
  88. package/dist/services/computer-capability.test.d.ts.map +1 -0
  89. package/dist/services/computer-capability.test.js +27 -0
  90. package/dist/services/computer-capability.test.js.map +1 -0
  91. package/dist/services/finance.d.ts +1 -1
  92. package/dist/services/index.d.ts +2 -0
  93. package/dist/services/index.d.ts.map +1 -1
  94. package/dist/services/index.js +2 -0
  95. package/dist/services/index.js.map +1 -1
  96. package/dist/services/instance-settings.d.ts.map +1 -1
  97. package/dist/services/instance-settings.js +15 -2
  98. package/dist/services/instance-settings.js.map +1 -1
  99. package/dist/services/integrations/agent-integrations.d.ts +1 -1
  100. package/dist/services/issues.d.ts +86 -1
  101. package/dist/services/issues.d.ts.map +1 -1
  102. package/dist/services/issues.helpers.d.ts +4 -0
  103. package/dist/services/issues.helpers.d.ts.map +1 -1
  104. package/dist/services/issues.helpers.js +28 -6
  105. package/dist/services/issues.helpers.js.map +1 -1
  106. package/dist/services/issues.js +207 -127
  107. package/dist/services/issues.js.map +1 -1
  108. package/dist/services/knowledge-portability/organization-portability.files.d.ts +1 -1
  109. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts +7 -1
  110. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts.map +1 -1
  111. package/dist/services/knowledge-portability/organization-skills.catalog.js +10 -0
  112. package/dist/services/knowledge-portability/organization-skills.catalog.js.map +1 -1
  113. package/dist/services/knowledge-portability/organization-skills.d.ts +1 -0
  114. package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
  115. package/dist/services/knowledge-portability/organization-skills.js +36 -3
  116. package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
  117. package/dist/services/mcp/managed-bindings.d.ts +3 -1
  118. package/dist/services/mcp/managed-bindings.d.ts.map +1 -1
  119. package/dist/services/mcp/managed-bindings.js +20 -3
  120. package/dist/services/mcp/managed-bindings.js.map +1 -1
  121. package/dist/services/mcp/managed-client.d.ts +15 -0
  122. package/dist/services/mcp/managed-client.d.ts.map +1 -1
  123. package/dist/services/mcp/managed-client.js +23 -1
  124. package/dist/services/mcp/managed-client.js.map +1 -1
  125. package/dist/services/mcp/managed-client.test.js +73 -9
  126. package/dist/services/mcp/managed-client.test.js.map +1 -1
  127. package/dist/services/messenger.d.ts +2 -2
  128. package/dist/services/messenger.d.ts.map +1 -1
  129. package/dist/services/messenger.js +126 -49
  130. package/dist/services/messenger.js.map +1 -1
  131. package/dist/services/product-analytics.d.ts +1 -1
  132. package/dist/services/rudder-plugins.d.ts +107 -0
  133. package/dist/services/rudder-plugins.d.ts.map +1 -0
  134. package/dist/services/rudder-plugins.js +1746 -0
  135. package/dist/services/rudder-plugins.js.map +1 -0
  136. package/dist/services/rudder-plugins.test.d.ts +2 -0
  137. package/dist/services/rudder-plugins.test.d.ts.map +1 -0
  138. package/dist/services/rudder-plugins.test.js +191 -0
  139. package/dist/services/rudder-plugins.test.js.map +1 -0
  140. package/dist/services/runtime-kernel/heartbeat.core.d.ts +1 -0
  141. package/dist/services/runtime-kernel/heartbeat.core.d.ts.map +1 -1
  142. package/dist/services/runtime-kernel/heartbeat.core.js +1 -1
  143. package/dist/services/runtime-kernel/heartbeat.core.js.map +1 -1
  144. package/dist/services/runtime-kernel/heartbeat.d.ts +53 -0
  145. package/dist/services/runtime-kernel/heartbeat.d.ts.map +1 -1
  146. package/dist/services/runtime-kernel/heartbeat.js +227 -107
  147. package/dist/services/runtime-kernel/heartbeat.js.map +1 -1
  148. package/dist/services/runtime-kernel/heartbeat.recovery.d.ts.map +1 -1
  149. package/dist/services/runtime-kernel/heartbeat.recovery.js +23 -2
  150. package/dist/services/runtime-kernel/heartbeat.recovery.js.map +1 -1
  151. package/dist/services/runtime-kernel/heartbeat.terminal.d.ts +14 -1
  152. package/dist/services/runtime-kernel/heartbeat.terminal.d.ts.map +1 -1
  153. package/dist/services/runtime-kernel/heartbeat.terminal.js +31 -0
  154. package/dist/services/runtime-kernel/heartbeat.terminal.js.map +1 -1
  155. package/dist/services/runtime-kernel/heartbeat.wakeup.d.ts.map +1 -1
  156. package/dist/services/runtime-kernel/heartbeat.wakeup.js +35 -3
  157. package/dist/services/runtime-kernel/heartbeat.wakeup.js.map +1 -1
  158. package/package.json +18 -18
  159. package/resources/bundled-skills/app-builder/SKILL.md +39 -9
  160. package/resources/bundled-skills/app-builder/assets/scaffold/app/globals.css +109 -16
  161. package/resources/bundled-skills/app-builder/assets/scaffold/components/contacts-workspace.tsx +176 -152
  162. package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/alert.tsx +12 -0
  163. package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/badge.tsx +27 -0
  164. package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/button.tsx +9 -7
  165. package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/card.tsx +15 -3
  166. package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/empty.tsx +18 -0
  167. package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/field.tsx +23 -0
  168. package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/input.tsx +1 -1
  169. package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/label.tsx +1 -1
  170. package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/separator.tsx +6 -0
  171. package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/skeleton.tsx +6 -0
  172. package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/table.tsx +30 -0
  173. package/resources/bundled-skills/app-builder/assets/scaffold/components.json +20 -0
  174. package/resources/bundled-skills/app-builder/assets/scaffold/next-env.d.ts +1 -1
  175. package/resources/bundled-skills/app-builder/assets/scaffold/next.config.ts +19 -0
  176. package/resources/bundled-skills/app-builder/assets/scaffold/package.json +2 -1
  177. package/resources/bundled-skills/app-builder/assets/scaffold/rudder.app.json +1 -1
  178. package/resources/bundled-skills/app-builder/assets/scaffold/rudder.ui.json +13 -0
  179. package/resources/bundled-skills/app-builder/assets/scaffold/scripts/validate-rudder-ui.mjs +50 -0
  180. package/resources/bundled-skills/app-builder/assets/scaffold/tests/e2e/app.spec.ts +35 -1
  181. package/resources/bundled-skills/app-builder/evals/evals.json +2 -1
  182. package/resources/bundled-skills/app-builder/references/design-guidelines.md +19 -2
  183. package/resources/bundled-skills/app-builder/references/rudder-ui-preset.md +73 -0
  184. package/resources/bundled-skills/app-builder/references/scaffold-contract.md +12 -0
  185. package/resources/bundled-skills/app-builder/references/verification.md +6 -0
  186. package/resources/bundled-skills/app-builder/scripts/report-build-status.mjs +83 -0
  187. package/resources/bundled-skills/rudder-docs/SKILL.md +2 -2
  188. package/resources/bundled-skills/rudder-docs/evals/retrieval-authority-evals.json +40 -0
  189. package/resources/bundled-skills/rudder-docs/evals/trigger-evals.json +2 -2
  190. package/resources/bundled-skills/rudder-docs/references/api-reference.md +0 -2
  191. package/resources/bundled-skills/rudder-docs/references/plugin-authoring.md +38 -114
  192. package/resources/bundled-skills/rudder-docs/references/source-map.md +2 -0
  193. package/skills/app-builder/SKILL.md +39 -9
  194. package/skills/app-builder/assets/scaffold/app/globals.css +109 -16
  195. package/skills/app-builder/assets/scaffold/components/contacts-workspace.tsx +176 -152
  196. package/skills/app-builder/assets/scaffold/components/ui/alert.tsx +12 -0
  197. package/skills/app-builder/assets/scaffold/components/ui/badge.tsx +27 -0
  198. package/skills/app-builder/assets/scaffold/components/ui/button.tsx +9 -7
  199. package/skills/app-builder/assets/scaffold/components/ui/card.tsx +15 -3
  200. package/skills/app-builder/assets/scaffold/components/ui/empty.tsx +18 -0
  201. package/skills/app-builder/assets/scaffold/components/ui/field.tsx +23 -0
  202. package/skills/app-builder/assets/scaffold/components/ui/input.tsx +1 -1
  203. package/skills/app-builder/assets/scaffold/components/ui/label.tsx +1 -1
  204. package/skills/app-builder/assets/scaffold/components/ui/separator.tsx +6 -0
  205. package/skills/app-builder/assets/scaffold/components/ui/skeleton.tsx +6 -0
  206. package/skills/app-builder/assets/scaffold/components/ui/table.tsx +30 -0
  207. package/skills/app-builder/assets/scaffold/components.json +20 -0
  208. package/skills/app-builder/assets/scaffold/next-env.d.ts +1 -1
  209. package/skills/app-builder/assets/scaffold/next.config.ts +19 -0
  210. package/skills/app-builder/assets/scaffold/package.json +2 -1
  211. package/skills/app-builder/assets/scaffold/rudder.app.json +1 -1
  212. package/skills/app-builder/assets/scaffold/rudder.ui.json +13 -0
  213. package/skills/app-builder/assets/scaffold/scripts/validate-rudder-ui.mjs +50 -0
  214. package/skills/app-builder/assets/scaffold/tests/e2e/app.spec.ts +35 -1
  215. package/skills/app-builder/evals/evals.json +2 -1
  216. package/skills/app-builder/references/design-guidelines.md +19 -2
  217. package/skills/app-builder/references/rudder-ui-preset.md +73 -0
  218. package/skills/app-builder/references/scaffold-contract.md +12 -0
  219. package/skills/app-builder/references/verification.md +6 -0
  220. package/skills/app-builder/scripts/report-build-status.mjs +83 -0
  221. package/skills/rudder-docs/SKILL.md +2 -2
  222. package/skills/rudder-docs/evals/retrieval-authority-evals.json +40 -0
  223. package/skills/rudder-docs/evals/trigger-evals.json +2 -2
  224. package/skills/rudder-docs/references/api-reference.md +0 -2
  225. package/skills/rudder-docs/references/plugin-authoring.md +38 -114
  226. package/skills/rudder-docs/references/source-map.md +2 -0
  227. package/ui-dist/assets/{LibraryLiveSurface-cHCJmmsl.js → LibraryLiveSurface-CoGcDfo0.js} +1 -1
  228. package/ui-dist/assets/{abnfDiagram-VRR7QNED-BORJvkew.js → abnfDiagram-VRR7QNED-BnPHHNfu.js} +1 -1
  229. package/ui-dist/assets/{arc-DPUzhx87.js → arc-CAvqQQR_.js} +1 -1
  230. package/ui-dist/assets/{architectureDiagram-ZJ3FMSHR-Bz_hY8LM.js → architectureDiagram-ZJ3FMSHR-DDa31YAK.js} +1 -1
  231. package/ui-dist/assets/{blockDiagram-677ZJIJ3-Bq1X3YkP.js → blockDiagram-677ZJIJ3-DDXLU9Ej.js} +1 -1
  232. package/ui-dist/assets/{c4Diagram-LMCZKHZV-BawEM9Pc.js → c4Diagram-LMCZKHZV-zEm0rHCp.js} +1 -1
  233. package/ui-dist/assets/channel-M_3GhwHQ.js +1 -0
  234. package/ui-dist/assets/{chunk-2Q5K7J3B-D0D5ZOaA.js → chunk-2Q5K7J3B-y1Rw45sz.js} +1 -1
  235. package/ui-dist/assets/{chunk-32BRIVSS-T90P7lug.js → chunk-32BRIVSS-Cq8cQBba.js} +1 -1
  236. package/ui-dist/assets/{chunk-5VM5RSS4-jcAERTgh.js → chunk-5VM5RSS4-CjQA6jGI.js} +1 -1
  237. package/ui-dist/assets/{chunk-EX3LRPZG-t9-JXwnR.js → chunk-EX3LRPZG-VVYt7jh3.js} +1 -1
  238. package/ui-dist/assets/{chunk-JWPE2WC7-hqgD1saC.js → chunk-JWPE2WC7-BJC3ISop.js} +1 -1
  239. package/ui-dist/assets/{chunk-MOJQB5TN-BsSUoYzd.js → chunk-MOJQB5TN-ClZgTqQk.js} +1 -1
  240. package/ui-dist/assets/{chunk-RYQCIY6F-YcdDh2Zn.js → chunk-RYQCIY6F-DRei3pn2.js} +1 -1
  241. package/ui-dist/assets/{chunk-V7JOEXUC-ChFyH9Su.js → chunk-V7JOEXUC-BGWrQfpZ.js} +1 -1
  242. package/ui-dist/assets/{chunk-VR4S4FIN-BjVK8IyX.js → chunk-VR4S4FIN-BBYT57Lt.js} +1 -1
  243. package/ui-dist/assets/{chunk-XXDRQBXY-D-TvO9vT.js → chunk-XXDRQBXY-D7P4SoZB.js} +1 -1
  244. package/ui-dist/assets/classDiagram-OUVF2IWQ-G44FJFnB.js +1 -0
  245. package/ui-dist/assets/classDiagram-v2-EOCWNBFH-G44FJFnB.js +1 -0
  246. package/ui-dist/assets/{cose-bilkent-JH36ORCC-DBwhASJG.js → cose-bilkent-JH36ORCC-mJXY7GQo.js} +1 -1
  247. package/ui-dist/assets/{cynefin-VYW2F7L2-SlCRmwl2.js → cynefin-VYW2F7L2-C-I_lPmH.js} +1 -1
  248. package/ui-dist/assets/{cynefinDiagram-TSTJHNR4-DImV6N_p.js → cynefinDiagram-TSTJHNR4-Bm1S_w7H.js} +1 -1
  249. package/ui-dist/assets/{dagre-VKFMJZFB-FH4Wv8Zn.js → dagre-VKFMJZFB-BuPIm1AB.js} +1 -1
  250. package/ui-dist/assets/{diagram-FQU43EPY-CrZvQjId.js → diagram-FQU43EPY-DTBCuV0W.js} +1 -1
  251. package/ui-dist/assets/{diagram-G47NLZAW-B7IGl_T3.js → diagram-G47NLZAW-B4CK8uR2.js} +1 -1
  252. package/ui-dist/assets/{diagram-NH7WQ7WH-CB4i7H1a.js → diagram-NH7WQ7WH-BkbS0GkL.js} +1 -1
  253. package/ui-dist/assets/{diagram-OA4YK3LP-DNyWPYY9.js → diagram-OA4YK3LP-C_mkRe0c.js} +1 -1
  254. package/ui-dist/assets/{diagram-WEI45ONY-BAhxbhpi.js → diagram-WEI45ONY-CHUb3TIJ.js} +1 -1
  255. package/ui-dist/assets/{ebnfDiagram-CCIWWBDH-ClXtQ9qt.js → ebnfDiagram-CCIWWBDH-D_lN4ZOY.js} +1 -1
  256. package/ui-dist/assets/{erDiagram-Q63AITRT-DGXiMYfn.js → erDiagram-Q63AITRT-CRnDqcsy.js} +1 -1
  257. package/ui-dist/assets/{flowDiagram-23GEKE2U-CdZ-02zc.js → flowDiagram-23GEKE2U-ChELY-8y.js} +1 -1
  258. package/ui-dist/assets/{ganttDiagram-NO4QXBWP-TxhzNpta.js → ganttDiagram-NO4QXBWP-QIML25-M.js} +1 -1
  259. package/ui-dist/assets/{gitGraphDiagram-IHSO6WYX-D6Q_w6wR.js → gitGraphDiagram-IHSO6WYX-TB_AKH8l.js} +1 -1
  260. package/ui-dist/assets/{graph-CxNmbpl9.js → graph-HKPvyYQ1.js} +1 -1
  261. package/ui-dist/assets/{index-C12QC1Ex.js → index-Ar0zIwom.js} +1 -1
  262. package/ui-dist/assets/{index-CW21qWFm.js → index-BeNxgoKd.js} +1 -1
  263. package/ui-dist/assets/{index-BujwWE3j.js → index-CB-UdrKN.js} +1 -1
  264. package/ui-dist/assets/{index-Cx6oIZPm.js → index-CfpFS1Tl.js} +555 -581
  265. package/ui-dist/assets/{index-C_nwI4Yj.js → index-CjIeIR2m.js} +1 -1
  266. package/ui-dist/assets/{index-Rau2cGm0.js → index-DCgbySwf.js} +1 -1
  267. package/ui-dist/assets/{index-Do78GymY.js → index-DDMaBNLI.js} +1 -1
  268. package/ui-dist/assets/{index-DU2LI7f9.js → index-DSZmO4bA.js} +1 -1
  269. package/ui-dist/assets/{index-BA9tMBhO.js → index-DfzFi2fx.js} +1 -1
  270. package/ui-dist/assets/{index-BoTz5S-X.js → index-DmEf_U5I.js} +1 -1
  271. package/ui-dist/assets/{index-kbWUQ1um.js → index-OfGDMJiA.js} +1 -1
  272. package/ui-dist/assets/{index-AkhgKOxC.js → index-ZzX8P4M5.js} +1 -1
  273. package/ui-dist/assets/{index-BQ87dGIq.js → index-fDtlbG8Y.js} +1 -1
  274. package/ui-dist/assets/{index-CXnqg1Oq.js → index-gdvFtffQ.js} +1 -1
  275. package/ui-dist/assets/index-noipz8bE.css +1 -0
  276. package/ui-dist/assets/{index-Bxz75ICF.js → index-rnOTiIXd.js} +1 -1
  277. package/ui-dist/assets/{infoDiagram-FWYZ7A6U-CnJd4-Jq.js → infoDiagram-FWYZ7A6U-DUR3sDTd.js} +1 -1
  278. package/ui-dist/assets/{ishikawaDiagram-FXEZZL3T-Bq-qc3pD.js → ishikawaDiagram-FXEZZL3T-CcnYT0s0.js} +1 -1
  279. package/ui-dist/assets/{journeyDiagram-5HDEW3XC-NSmpUGUA.js → journeyDiagram-5HDEW3XC-CqfbG8T1.js} +1 -1
  280. package/ui-dist/assets/{kanban-definition-HUTT4EX6-D7pgWtfA.js → kanban-definition-HUTT4EX6-CYwQM9eR.js} +1 -1
  281. package/ui-dist/assets/{layout-BRuo6LWe.js → layout-B2IlJgLE.js} +1 -1
  282. package/ui-dist/assets/{linear-CkaWI9Nd.js → linear-CcEPcfbO.js} +1 -1
  283. package/ui-dist/assets/{map-CgA6tTpZ.js → map-Bmhfvlpu.js} +1 -1
  284. package/ui-dist/assets/{mermaid.core-DrXOt-P6.js → mermaid.core-T9Q0CuQx.js} +4 -4
  285. package/ui-dist/assets/{mindmap-definition-LN4V7U3C-D9ImeWBe.js → mindmap-definition-LN4V7U3C-CRjQlwN4.js} +1 -1
  286. package/ui-dist/assets/{pegDiagram-2B236MQR-Cq-zZ_zz.js → pegDiagram-2B236MQR-DenccdY8.js} +1 -1
  287. package/ui-dist/assets/{pieDiagram-ENE6RG2P-FgNgy726.js → pieDiagram-ENE6RG2P-CPiTrJcY.js} +1 -1
  288. package/ui-dist/assets/{quadrantDiagram-ABIIQ3AL-BxQbmGmi.js → quadrantDiagram-ABIIQ3AL-fKbvGl_-.js} +1 -1
  289. package/ui-dist/assets/{railroadDiagram-RFXS5EU6-CrpbkPH2.js → railroadDiagram-RFXS5EU6-D6uOjEmA.js} +1 -1
  290. package/ui-dist/assets/{requirementDiagram-TGXJPOKE-DQ-65ZW6.js → requirementDiagram-TGXJPOKE-CQIQ387O.js} +1 -1
  291. package/ui-dist/assets/{sankeyDiagram-HTMAVEWB-Bom0lMZm.js → sankeyDiagram-HTMAVEWB-CKrnONJL.js} +1 -1
  292. package/ui-dist/assets/{sequenceDiagram-DBY2YBRQ-C1MSDLDj.js → sequenceDiagram-DBY2YBRQ-C3vaOiZO.js} +1 -1
  293. package/ui-dist/assets/{sizeCapture-X5ZJPWSS-Bn1vxebD.js → sizeCapture-X5ZJPWSS-F39EvxY0.js} +1 -1
  294. package/ui-dist/assets/{stateDiagram-2N3HPSRC-GRRQpmzI.js → stateDiagram-2N3HPSRC-Bq2sVFQq.js} +1 -1
  295. package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-B94IPtHC.js +1 -0
  296. package/ui-dist/assets/{swimlanes-5IMT3BWC-BcOOCZ4q.js → swimlanes-5IMT3BWC-CSrygB7m.js} +2 -2
  297. package/ui-dist/assets/swimlanesDiagram-G3AALYLV-9WQr2_E9.js +8 -0
  298. package/ui-dist/assets/{timeline-definition-FHXFAJF6-gi6amswh.js → timeline-definition-FHXFAJF6-DSMMlntv.js} +1 -1
  299. package/ui-dist/assets/{vennDiagram-L72KCM5P-B9uqeyQt.js → vennDiagram-L72KCM5P-D5XH96zV.js} +1 -1
  300. package/ui-dist/assets/{wardleyDiagram-EHGQE667-BnqWAMiE.js → wardleyDiagram-EHGQE667-BXX2RFsU.js} +1 -1
  301. package/ui-dist/assets/{xychartDiagram-FW5EYKEG-BJXKltIN.js → xychartDiagram-FW5EYKEG-awBW2lwG.js} +1 -1
  302. package/ui-dist/index.html +2 -2
  303. package/dist/bootstrap/plugin-host-runtime.d.ts +0 -116
  304. package/dist/bootstrap/plugin-host-runtime.d.ts.map +0 -1
  305. package/dist/bootstrap/plugin-host-runtime.js +0 -179
  306. package/dist/bootstrap/plugin-host-runtime.js.map +0 -1
  307. package/dist/bundled-plugins/plugin-linear/README.md +0 -22
  308. package/dist/bundled-plugins/plugin-linear/dist/manifest.js +0 -183
  309. package/dist/bundled-plugins/plugin-linear/dist/manifest.js.map +0 -7
  310. package/dist/bundled-plugins/plugin-linear/dist/ui/index.js +0 -1285
  311. package/dist/bundled-plugins/plugin-linear/dist/ui/index.js.map +0 -7
  312. package/dist/bundled-plugins/plugin-linear/dist/worker.js +0 -10699
  313. package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +0 -7
  314. package/dist/bundled-plugins/plugin-linear/package.json +0 -42
  315. package/dist/routes/plugin-ui-static.d.ts +0 -70
  316. package/dist/routes/plugin-ui-static.d.ts.map +0 -1
  317. package/dist/routes/plugin-ui-static.js +0 -375
  318. package/dist/routes/plugin-ui-static.js.map +0 -1
  319. package/dist/routes/plugins.d.ts +0 -120
  320. package/dist/routes/plugins.d.ts.map +0 -1
  321. package/dist/routes/plugins.js +0 -1125
  322. package/dist/routes/plugins.js.map +0 -1
  323. package/dist/routes/plugins.operations-routes.d.ts +0 -28
  324. package/dist/routes/plugins.operations-routes.d.ts.map +0 -1
  325. package/dist/routes/plugins.operations-routes.js +0 -720
  326. package/dist/routes/plugins.operations-routes.js.map +0 -1
  327. package/dist/services/plugin-capability-validator.d.ts +0 -108
  328. package/dist/services/plugin-capability-validator.d.ts.map +0 -1
  329. package/dist/services/plugin-capability-validator.js +0 -268
  330. package/dist/services/plugin-capability-validator.js.map +0 -1
  331. package/dist/services/plugin-config-validator.d.ts +0 -26
  332. package/dist/services/plugin-config-validator.d.ts.map +0 -1
  333. package/dist/services/plugin-config-validator.js +0 -41
  334. package/dist/services/plugin-config-validator.js.map +0 -1
  335. package/dist/services/plugin-dev-watcher.d.ts +0 -47
  336. package/dist/services/plugin-dev-watcher.d.ts.map +0 -1
  337. package/dist/services/plugin-dev-watcher.js +0 -275
  338. package/dist/services/plugin-dev-watcher.js.map +0 -1
  339. package/dist/services/plugin-event-bus.d.ts +0 -149
  340. package/dist/services/plugin-event-bus.d.ts.map +0 -1
  341. package/dist/services/plugin-event-bus.js +0 -258
  342. package/dist/services/plugin-event-bus.js.map +0 -1
  343. package/dist/services/plugin-host-service-cleanup.d.ts +0 -14
  344. package/dist/services/plugin-host-service-cleanup.d.ts.map +0 -1
  345. package/dist/services/plugin-host-service-cleanup.js +0 -37
  346. package/dist/services/plugin-host-service-cleanup.js.map +0 -1
  347. package/dist/services/plugin-host-services.d.ts +0 -13
  348. package/dist/services/plugin-host-services.d.ts.map +0 -1
  349. package/dist/services/plugin-host-services.js +0 -933
  350. package/dist/services/plugin-host-services.js.map +0 -1
  351. package/dist/services/plugin-job-coordinator.d.ts +0 -81
  352. package/dist/services/plugin-job-coordinator.d.ts.map +0 -1
  353. package/dist/services/plugin-job-coordinator.js +0 -172
  354. package/dist/services/plugin-job-coordinator.js.map +0 -1
  355. package/dist/services/plugin-job-scheduler.d.ts +0 -163
  356. package/dist/services/plugin-job-scheduler.d.ts.map +0 -1
  357. package/dist/services/plugin-job-scheduler.js +0 -452
  358. package/dist/services/plugin-job-scheduler.js.map +0 -1
  359. package/dist/services/plugin-job-store.d.ts +0 -208
  360. package/dist/services/plugin-job-store.d.ts.map +0 -1
  361. package/dist/services/plugin-job-store.js +0 -350
  362. package/dist/services/plugin-job-store.js.map +0 -1
  363. package/dist/services/plugin-lifecycle.d.ts +0 -240
  364. package/dist/services/plugin-lifecycle.d.ts.map +0 -1
  365. package/dist/services/plugin-lifecycle.js +0 -439
  366. package/dist/services/plugin-lifecycle.js.map +0 -1
  367. package/dist/services/plugin-loader.core.d.ts +0 -40
  368. package/dist/services/plugin-loader.core.d.ts.map +0 -1
  369. package/dist/services/plugin-loader.core.js +0 -879
  370. package/dist/services/plugin-loader.core.js.map +0 -1
  371. package/dist/services/plugin-loader.d.ts +0 -30
  372. package/dist/services/plugin-loader.d.ts.map +0 -1
  373. package/dist/services/plugin-loader.helpers.d.ts +0 -495
  374. package/dist/services/plugin-loader.helpers.d.ts.map +0 -1
  375. package/dist/services/plugin-loader.helpers.js +0 -238
  376. package/dist/services/plugin-loader.helpers.js.map +0 -1
  377. package/dist/services/plugin-loader.js +0 -30
  378. package/dist/services/plugin-loader.js.map +0 -1
  379. package/dist/services/plugin-loader.worker-paths.d.ts +0 -37
  380. package/dist/services/plugin-loader.worker-paths.d.ts.map +0 -1
  381. package/dist/services/plugin-loader.worker-paths.js +0 -62
  382. package/dist/services/plugin-loader.worker-paths.js.map +0 -1
  383. package/dist/services/plugin-log-retention.d.ts +0 -20
  384. package/dist/services/plugin-log-retention.d.ts.map +0 -1
  385. package/dist/services/plugin-log-retention.js +0 -63
  386. package/dist/services/plugin-log-retention.js.map +0 -1
  387. package/dist/services/plugin-manifest-validator.d.ts +0 -90
  388. package/dist/services/plugin-manifest-validator.d.ts.map +0 -1
  389. package/dist/services/plugin-manifest-validator.js +0 -72
  390. package/dist/services/plugin-manifest-validator.js.map +0 -1
  391. package/dist/services/plugin-registry.d.ts +0 -2542
  392. package/dist/services/plugin-registry.d.ts.map +0 -1
  393. package/dist/services/plugin-registry.js +0 -536
  394. package/dist/services/plugin-registry.js.map +0 -1
  395. package/dist/services/plugin-runtime-sandbox.d.ts +0 -40
  396. package/dist/services/plugin-runtime-sandbox.d.ts.map +0 -1
  397. package/dist/services/plugin-runtime-sandbox.js +0 -154
  398. package/dist/services/plugin-runtime-sandbox.js.map +0 -1
  399. package/dist/services/plugin-secrets-handler.d.ts +0 -81
  400. package/dist/services/plugin-secrets-handler.d.ts.map +0 -1
  401. package/dist/services/plugin-secrets-handler.js +0 -275
  402. package/dist/services/plugin-secrets-handler.js.map +0 -1
  403. package/dist/services/plugin-state-store.d.ts +0 -92
  404. package/dist/services/plugin-state-store.d.ts.map +0 -1
  405. package/dist/services/plugin-state-store.js +0 -190
  406. package/dist/services/plugin-state-store.js.map +0 -1
  407. package/dist/services/plugin-stream-bus.d.ts +0 -29
  408. package/dist/services/plugin-stream-bus.d.ts.map +0 -1
  409. package/dist/services/plugin-stream-bus.js +0 -48
  410. package/dist/services/plugin-stream-bus.js.map +0 -1
  411. package/dist/services/plugin-tool-dispatcher.d.ts +0 -180
  412. package/dist/services/plugin-tool-dispatcher.d.ts.map +0 -1
  413. package/dist/services/plugin-tool-dispatcher.js +0 -224
  414. package/dist/services/plugin-tool-dispatcher.js.map +0 -1
  415. package/dist/services/plugin-tool-registry.d.ts +0 -192
  416. package/dist/services/plugin-tool-registry.d.ts.map +0 -1
  417. package/dist/services/plugin-tool-registry.js +0 -224
  418. package/dist/services/plugin-tool-registry.js.map +0 -1
  419. package/dist/services/plugin-worker-manager.d.ts +0 -260
  420. package/dist/services/plugin-worker-manager.d.ts.map +0 -1
  421. package/dist/services/plugin-worker-manager.js +0 -836
  422. package/dist/services/plugin-worker-manager.js.map +0 -1
  423. package/ui-dist/assets/channel-C08AyYJr.js +0 -1
  424. package/ui-dist/assets/classDiagram-OUVF2IWQ-BIXq6fec.js +0 -1
  425. package/ui-dist/assets/classDiagram-v2-EOCWNBFH-BIXq6fec.js +0 -1
  426. package/ui-dist/assets/index-BmXb0yC1.css +0 -1
  427. package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-KvSJhiLM.js +0 -1
  428. package/ui-dist/assets/swimlanesDiagram-G3AALYLV-Cr92NnJ4.js +0 -8
@@ -1,1125 +0,0 @@
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/engineering/PLUGIN_RUNTIME_CONTRACT.md for the current plugin runtime contract
18
- */
19
- import { organizations } from "@rudderhq/db";
20
- import { JsonRpcCallError, PLUGIN_RPC_ERROR_CODES } from "@rudderhq/plugin-sdk";
21
- import { PLUGIN_STATUSES, } from "@rudderhq/shared";
22
- import { Router } from "express";
23
- import { existsSync } from "node:fs";
24
- import path from "node:path";
25
- import { fileURLToPath } from "node:url";
26
- import { logActivity } from "../services/activity-log.js";
27
- import { publishGlobalLiveEvent } from "../services/live-events.js";
28
- import { pluginLifecycleManager } from "../services/plugin-lifecycle.js";
29
- import { getPluginUiContributionMetadata } from "../services/plugin-loader.js";
30
- import { pluginRegistryService } from "../services/plugin-registry.js";
31
- import { isPostgresError } from "../services/postgres-errors.js";
32
- import { assertBoard, assertCompanyAccess, getActorInfo } from "./authz.js";
33
- import { registerPluginOperationsRoutes } from "./plugins.operations-routes.js";
34
- /** UUID v4 regex used for plugin ID route resolution. */
35
- const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
36
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
37
- const REPO_ROOT = path.resolve(__dirname, "../../..");
38
- const SERVER_PACKAGE_ROOT = path.resolve(__dirname, "../..");
39
- const BUNDLED_PLUGIN_CATALOG = [
40
- {
41
- packageName: "@rudderhq/plugin-file-browser-example",
42
- pluginKey: "rudder-file-browser-example",
43
- displayName: "File Browser (Example)",
44
- description: "Example plugin that adds a Files link in project navigation plus a project detail file browser.",
45
- localPath: "packages/plugins/examples/plugin-file-browser-example",
46
- tag: "example",
47
- },
48
- {
49
- packageName: "@rudderhq/plugin-kitchen-sink-example",
50
- pluginKey: "rudder-kitchen-sink-example",
51
- displayName: "Kitchen Sink (Example)",
52
- description: "Reference plugin that demonstrates the current Rudder plugin API surface, bridge flows, UI extension surfaces, jobs, webhooks, tools, streams, and trusted local workspace/process demos.",
53
- localPath: "packages/plugins/examples/plugin-kitchen-sink-example",
54
- tag: "example",
55
- },
56
- {
57
- packageName: "@rudderhq/plugin-linear",
58
- pluginKey: "rudder.linear",
59
- displayName: "Linear",
60
- description: "Import-first Linear connector for Rudder issues.",
61
- localPath: "packages/plugins/examples/plugin-linear",
62
- tag: "available",
63
- },
64
- ];
65
- function resolveBundledPluginPath(plugin) {
66
- const sourceCheckoutPath = path.resolve(REPO_ROOT, plugin.localPath);
67
- if (existsSync(sourceCheckoutPath))
68
- return sourceCheckoutPath;
69
- const packagedPath = path.resolve(SERVER_PACKAGE_ROOT, "dist/bundled-plugins", path.basename(plugin.localPath));
70
- if (existsSync(packagedPath))
71
- return packagedPath;
72
- return null;
73
- }
74
- function listBundledPlugins() {
75
- return BUNDLED_PLUGIN_CATALOG.flatMap((plugin) => {
76
- const resolvedLocalPath = resolveBundledPluginPath(plugin);
77
- if (!resolvedLocalPath)
78
- return [];
79
- return [{ ...plugin, localPath: resolvedLocalPath }];
80
- });
81
- }
82
- function listBundledPluginExamples() {
83
- return listBundledPlugins().filter((plugin) => plugin.tag === "example");
84
- }
85
- /**
86
- * Resolve a plugin by either database ID or plugin key.
87
- *
88
- * Lookup order:
89
- * - UUID-like IDs: getById first, then getByKey.
90
- * - Scoped package keys (e.g. "@scope/name"): getByKey only, never getById.
91
- * - Other non-UUID IDs: try getById first (test/memory registries may allow this),
92
- * then fallback to getByKey. Any UUID parse error from getById is ignored.
93
- *
94
- * @param registry - The plugin registry service instance
95
- * @param pluginId - Either a database UUID or plugin key (manifest id)
96
- * @returns Plugin record or null if not found
97
- */
98
- async function resolvePlugin(registry, pluginId) {
99
- const isUuid = UUID_REGEX.test(pluginId);
100
- const isScopedPackageKey = pluginId.startsWith("@") || pluginId.includes("/");
101
- // Scoped package IDs are valid plugin keys but invalid UUIDs.
102
- // Skip getById() entirely to avoid Postgres uuid parse errors.
103
- if (isScopedPackageKey && !isUuid) {
104
- return registry.getByKey(pluginId);
105
- }
106
- try {
107
- const byId = await registry.getById(pluginId);
108
- if (byId)
109
- return byId;
110
- }
111
- catch (error) {
112
- // Ignore invalid UUID cast errors and continue with key lookup.
113
- if (!isPostgresError(error, "22P02")) {
114
- throw error;
115
- }
116
- }
117
- return registry.getByKey(pluginId);
118
- }
119
- /**
120
- * Create Express router for plugin management API.
121
- *
122
- * Routes provided:
123
- *
124
- * | Method | Path | Description |
125
- * |--------|------|-------------|
126
- * | GET | /plugins | List all plugins (optional ?status= filter) |
127
- * | GET | /plugins/ui-contributions | Get UI slots from ready plugins |
128
- * | GET | /plugins/:pluginId | Get single plugin by ID or key |
129
- * | POST | /plugins/install | Install from npm or local path |
130
- * | DELETE | /plugins/:pluginId | Uninstall (optional ?purge=true) |
131
- * | POST | /plugins/:pluginId/enable | Enable a plugin |
132
- * | POST | /plugins/:pluginId/disable | Disable a plugin |
133
- * | GET | /plugins/:pluginId/health | Run health diagnostics |
134
- * | POST | /plugins/:pluginId/upgrade | Upgrade to newer version |
135
- * | GET | /plugins/:pluginId/jobs | List jobs for a plugin |
136
- * | GET | /plugins/:pluginId/jobs/:jobId/runs | List runs for a job |
137
- * | POST | /plugins/:pluginId/jobs/:jobId/trigger | Manually trigger a job |
138
- * | POST | /plugins/:pluginId/webhooks/:endpointKey | Receive inbound webhook |
139
- * | GET | /plugins/tools | List all available plugin tools |
140
- * | GET | /plugins/tools?pluginId=... | List tools for a specific plugin |
141
- * | POST | /plugins/tools/execute | Execute a plugin tool |
142
- * | GET | /plugins/:pluginId/config | Get current plugin config |
143
- * | POST | /plugins/:pluginId/config | Save (upsert) plugin config |
144
- * | POST | /plugins/:pluginId/config/test | Test config via validateConfig RPC |
145
- * | POST | /plugins/:pluginId/bridge/data | Proxy getData to plugin worker |
146
- * | POST | /plugins/:pluginId/bridge/action | Proxy performAction to plugin worker |
147
- * | POST | /plugins/:pluginId/data/:key | Proxy getData to plugin worker (key in URL) |
148
- * | POST | /plugins/:pluginId/actions/:key | Proxy performAction to plugin worker (key in URL) |
149
- * | GET | /plugins/:pluginId/bridge/stream/:channel | SSE stream from worker to UI |
150
- * | GET | /plugins/:pluginId/dashboard | Aggregated health dashboard data |
151
- *
152
- * **Route Ordering Note:** Static routes (like /ui-contributions, /tools) must be
153
- * registered before parameterized routes (like /:pluginId) to prevent Express from
154
- * matching them as a plugin ID.
155
- *
156
- * @param db - Database connection instance
157
- * @param jobDeps - Optional job scheduling dependencies
158
- * @param webhookDeps - Optional webhook ingestion dependencies
159
- * @param toolDeps - Optional tool dispatcher dependencies
160
- * @param bridgeDeps - Optional bridge proxy dependencies for getData/performAction
161
- * @returns Express router with plugin routes mounted
162
- */
163
- export function pluginRoutes(db, loader, jobDeps, webhookDeps, toolDeps, bridgeDeps) {
164
- const router = Router();
165
- const registry = pluginRegistryService(db);
166
- const lifecycle = pluginLifecycleManager(db, {
167
- loader,
168
- workerManager: bridgeDeps?.workerManager ?? webhookDeps?.workerManager,
169
- });
170
- async function resolvePluginAuditCompanyIds(req) {
171
- if (typeof db.select === "function") {
172
- const rows = await db
173
- .select({ id: organizations.id })
174
- .from(organizations);
175
- return rows.map((row) => row.id);
176
- }
177
- if (req.actor.type === "agent" && req.actor.orgId) {
178
- return [req.actor.orgId];
179
- }
180
- if (req.actor.type === "board") {
181
- return req.actor.orgIds ?? [];
182
- }
183
- return [];
184
- }
185
- async function logPluginMutationActivity(req, action, entityId, details) {
186
- const orgIds = await resolvePluginAuditCompanyIds(req);
187
- if (orgIds.length === 0)
188
- return;
189
- const actor = getActorInfo(req);
190
- await Promise.all(orgIds.map((orgId) => logActivity(db, {
191
- orgId,
192
- actorType: actor.actorType,
193
- actorId: actor.actorId,
194
- agentId: actor.agentId,
195
- runId: actor.runId,
196
- action,
197
- entityType: "plugin",
198
- entityId,
199
- details,
200
- })));
201
- }
202
- /**
203
- * GET /api/plugins
204
- *
205
- * List all installed plugins, optionally filtered by lifecycle status.
206
- *
207
- * Query params:
208
- * - `status` (optional): Filter by lifecycle status. Must be one of the
209
- * values in `PLUGIN_STATUSES` (`installed`, `ready`, `error`,
210
- * `upgrade_pending`, `uninstalled`). Returns HTTP 400 if the value is
211
- * not a recognised status string.
212
- *
213
- * Response: `PluginRecord[]`
214
- */
215
- router.get("/plugins", async (req, res) => {
216
- assertBoard(req);
217
- const rawStatus = req.query.status;
218
- if (rawStatus !== undefined) {
219
- if (typeof rawStatus !== "string" || !PLUGIN_STATUSES.includes(rawStatus)) {
220
- res.status(400).json({
221
- error: `Invalid status '${String(rawStatus)}'. Must be one of: ${PLUGIN_STATUSES.join(", ")}`,
222
- });
223
- return;
224
- }
225
- }
226
- const status = rawStatus;
227
- const plugins = status
228
- ? await registry.listByStatus(status)
229
- : await registry.listInstalled();
230
- res.json(plugins);
231
- });
232
- /**
233
- * GET /api/plugins/examples
234
- *
235
- * Return first-party example plugins bundled in this repo, if present.
236
- * These can be installed through the normal local-path install flow.
237
- */
238
- router.get("/plugins/examples", async (req, res) => {
239
- assertBoard(req);
240
- res.json(listBundledPluginExamples());
241
- });
242
- /**
243
- * GET /api/plugins/available
244
- *
245
- * Return first-party plugins available for one-click local-path install.
246
- * Production builds can include non-example plugins in this catalog.
247
- */
248
- router.get("/plugins/available", async (req, res) => {
249
- assertBoard(req);
250
- res.json(listBundledPlugins());
251
- });
252
- // IMPORTANT: Static routes must come before parameterized routes
253
- // to avoid Express matching "ui-contributions" as a :pluginId
254
- /**
255
- * GET /api/plugins/ui-contributions
256
- *
257
- * Return UI contributions from all plugins in 'ready' state.
258
- * Used by the frontend to discover plugin UI slots and launcher metadata.
259
- *
260
- * The response is normalized for the frontend slot host:
261
- * - Only includes plugins with at least one declared UI slot or launcher
262
- * - Excludes plugins with null/missing manifestJson (defensive)
263
- * - Slots are extracted from manifest.ui.slots
264
- * - Launchers are aggregated from legacy manifest.launchers and manifest.ui.launchers
265
- *
266
- * Example response:
267
- * ```json
268
- * [
269
- * {
270
- * "pluginId": "plg_123",
271
- * "pluginKey": "rudder.claude-usage",
272
- * "displayName": "Claude Usage",
273
- * "version": "1.0.0",
274
- * "uiEntryFile": "index.js",
275
- * "slots": [],
276
- * "launchers": [
277
- * {
278
- * "id": "claude-usage-toolbar",
279
- * "displayName": "Claude Usage",
280
- * "placementZone": "toolbarButton",
281
- * "action": { "type": "openModal", "target": "ClaudeUsageView" },
282
- * "render": { "environment": "hostOverlay", "bounds": "wide" }
283
- * }
284
- * ]
285
- * }
286
- * ]
287
- * ```
288
- *
289
- * Response: PluginUiContribution[]
290
- */
291
- router.get("/plugins/ui-contributions", async (req, res) => {
292
- assertBoard(req);
293
- const plugins = await registry.listByStatus("ready");
294
- const contributions = plugins
295
- .map((plugin) => {
296
- // Safety check: manifestJson should always exist for ready plugins, but guard against null
297
- const manifest = plugin.manifestJson;
298
- if (!manifest)
299
- return null;
300
- const uiMetadata = getPluginUiContributionMetadata(manifest);
301
- if (!uiMetadata)
302
- return null;
303
- return {
304
- pluginId: plugin.id,
305
- pluginKey: plugin.pluginKey,
306
- displayName: manifest.displayName,
307
- version: plugin.version,
308
- updatedAt: plugin.updatedAt.toISOString(),
309
- uiEntryFile: uiMetadata.uiEntryFile,
310
- slots: uiMetadata.slots,
311
- launchers: uiMetadata.launchers,
312
- };
313
- })
314
- .filter((item) => item !== null);
315
- res.json(contributions);
316
- });
317
- // ===========================================================================
318
- // Tool discovery and execution routes
319
- // ===========================================================================
320
- /**
321
- * GET /api/plugins/tools
322
- *
323
- * List all available plugin-contributed tools in an agent-friendly format.
324
- *
325
- * Query params:
326
- * - `pluginId` (optional): Filter to tools from a specific plugin
327
- *
328
- * Response: `AgentToolDescriptor[]`
329
- * Errors: 501 if tool dispatcher is not configured
330
- */
331
- router.get("/plugins/tools", async (req, res) => {
332
- assertBoard(req);
333
- if (!toolDeps) {
334
- res.status(501).json({ error: "Plugin tool dispatch is not enabled" });
335
- return;
336
- }
337
- const pluginId = req.query.pluginId;
338
- const filter = pluginId ? { pluginId } : undefined;
339
- const tools = toolDeps.toolDispatcher.listToolsForAgent(filter);
340
- res.json(tools);
341
- });
342
- /**
343
- * POST /api/plugins/tools/execute
344
- *
345
- * Execute a plugin-contributed tool by its namespaced name.
346
- *
347
- * This is the primary endpoint used by the agent service to invoke
348
- * plugin tools during an agent run.
349
- *
350
- * Request body:
351
- * - `tool`: Fully namespaced tool name (e.g., "acme.linear:search-issues")
352
- * - `parameters`: Parameters matching the tool's declared JSON Schema
353
- * - `runContext`: Agent run context with agentId, runId, orgId, projectId
354
- *
355
- * Response: `ToolExecutionResult`
356
- * Errors:
357
- * - 400 if request validation fails
358
- * - 404 if tool is not found
359
- * - 501 if tool dispatcher is not configured
360
- * - 502 if the plugin worker is unavailable or the RPC call fails
361
- */
362
- router.post("/plugins/tools/execute", async (req, res) => {
363
- assertBoard(req);
364
- if (!toolDeps) {
365
- res.status(501).json({ error: "Plugin tool dispatch is not enabled" });
366
- return;
367
- }
368
- const body = req.body;
369
- if (!body) {
370
- res.status(400).json({ error: "Request body is required" });
371
- return;
372
- }
373
- const { tool, parameters, runContext } = body;
374
- // Validate required fields
375
- if (!tool || typeof tool !== "string") {
376
- res.status(400).json({ error: '"tool" is required and must be a string' });
377
- return;
378
- }
379
- if (!runContext || typeof runContext !== "object") {
380
- res.status(400).json({ error: '"runContext" is required and must be an object' });
381
- return;
382
- }
383
- if (!runContext.agentId || !runContext.runId || !runContext.orgId || !runContext.projectId) {
384
- res.status(400).json({
385
- error: '"runContext" must include agentId, runId, orgId, and projectId',
386
- });
387
- return;
388
- }
389
- assertCompanyAccess(req, runContext.orgId);
390
- // Verify the tool exists
391
- const registeredTool = toolDeps.toolDispatcher.getTool(tool);
392
- if (!registeredTool) {
393
- res.status(404).json({ error: `Tool "${tool}" not found` });
394
- return;
395
- }
396
- try {
397
- const result = await toolDeps.toolDispatcher.executeTool(tool, parameters ?? {}, runContext);
398
- res.json(result);
399
- }
400
- catch (err) {
401
- const message = err instanceof Error ? err.message : String(err);
402
- // Distinguish between "worker not running" (502) and other errors (500)
403
- if (message.includes("not running") || message.includes("worker")) {
404
- res.status(502).json({ error: message });
405
- }
406
- else {
407
- res.status(500).json({ error: message });
408
- }
409
- }
410
- });
411
- /**
412
- * POST /api/plugins/install
413
- *
414
- * Install a plugin from npm or a local filesystem path.
415
- *
416
- * Request body:
417
- * - packageName: npm package name or local path (required)
418
- * - version: Target version for npm packages (optional)
419
- * - isLocalPath: Set true if packageName is a local path
420
- *
421
- * The installer:
422
- * 1. Downloads from npm or loads from local path
423
- * 2. Validates the manifest (schema + capability consistency)
424
- * 3. Registers in the database
425
- * 4. Transitions to `ready` state if no new capability approval is needed
426
- *
427
- * Response: `PluginRecord`
428
- *
429
- * Errors:
430
- * - `400` — validation failure or install error (package not found, bad manifest, etc.)
431
- * - `500` — installation succeeded but manifest is missing (indicates a loader bug)
432
- */
433
- router.post("/plugins/install", async (req, res) => {
434
- assertBoard(req);
435
- const { packageName, version, isLocalPath } = req.body;
436
- // Input validation
437
- if (!packageName || typeof packageName !== "string") {
438
- res.status(400).json({ error: "packageName is required and must be a string" });
439
- return;
440
- }
441
- if (version !== undefined && typeof version !== "string") {
442
- res.status(400).json({ error: "version must be a string if provided" });
443
- return;
444
- }
445
- if (isLocalPath !== undefined && typeof isLocalPath !== "boolean") {
446
- res.status(400).json({ error: "isLocalPath must be a boolean if provided" });
447
- return;
448
- }
449
- // Validate package name format
450
- const trimmedPackage = packageName.trim();
451
- if (trimmedPackage.length === 0) {
452
- res.status(400).json({ error: "packageName cannot be empty" });
453
- return;
454
- }
455
- // Basic security check for package name (prevent injection)
456
- if (!isLocalPath && /[<>:"|?*]/.test(trimmedPackage)) {
457
- res.status(400).json({ error: "packageName contains invalid characters" });
458
- return;
459
- }
460
- try {
461
- const installOptions = isLocalPath
462
- ? { localPath: trimmedPackage }
463
- : { packageName: trimmedPackage, version: version?.trim() };
464
- const discovered = await loader.installPlugin(installOptions);
465
- if (!discovered.manifest) {
466
- res.status(500).json({ error: "Plugin installed but manifest is missing" });
467
- return;
468
- }
469
- // Transition to ready state
470
- const existingPlugin = await registry.getByKey(discovered.manifest.id);
471
- if (existingPlugin) {
472
- await lifecycle.load(existingPlugin.id);
473
- const updated = await registry.getById(existingPlugin.id);
474
- await logPluginMutationActivity(req, "plugin.installed", existingPlugin.id, {
475
- pluginId: existingPlugin.id,
476
- pluginKey: existingPlugin.pluginKey,
477
- packageName: updated?.packageName ?? existingPlugin.packageName,
478
- version: updated?.version ?? existingPlugin.version,
479
- source: isLocalPath ? "local_path" : "npm",
480
- });
481
- publishGlobalLiveEvent({ type: "plugin.ui.updated", payload: { pluginId: existingPlugin.id, action: "installed" } });
482
- res.json(updated);
483
- }
484
- else {
485
- // This shouldn't happen since installPlugin already registers in the DB
486
- res.status(500).json({ error: "Plugin installed but not found in registry" });
487
- }
488
- }
489
- catch (err) {
490
- const message = err instanceof Error ? err.message : String(err);
491
- res.status(400).json({ error: message });
492
- }
493
- });
494
- /**
495
- * Map a worker RPC error to a bridge-level error code.
496
- *
497
- * JsonRpcCallError carries numeric codes from the plugin RPC error code space.
498
- * This helper maps them to the string error codes defined in PluginBridgeErrorCode.
499
- *
500
- * @see doc/engineering/PLUGIN_RUNTIME_CONTRACT.md — Error Propagation Through The Bridge
501
- */
502
- function mapRpcErrorToBridgeError(err) {
503
- if (err instanceof JsonRpcCallError) {
504
- switch (err.code) {
505
- case PLUGIN_RPC_ERROR_CODES.WORKER_UNAVAILABLE:
506
- return {
507
- code: "WORKER_UNAVAILABLE",
508
- message: err.message,
509
- details: err.data,
510
- };
511
- case PLUGIN_RPC_ERROR_CODES.CAPABILITY_DENIED:
512
- return {
513
- code: "CAPABILITY_DENIED",
514
- message: err.message,
515
- details: err.data,
516
- };
517
- case PLUGIN_RPC_ERROR_CODES.TIMEOUT:
518
- return {
519
- code: "TIMEOUT",
520
- message: err.message,
521
- details: err.data,
522
- };
523
- case PLUGIN_RPC_ERROR_CODES.WORKER_ERROR:
524
- return {
525
- code: "WORKER_ERROR",
526
- message: err.message,
527
- details: err.data,
528
- };
529
- default:
530
- return {
531
- code: "UNKNOWN",
532
- message: err.message,
533
- details: err.data,
534
- };
535
- }
536
- }
537
- const message = err instanceof Error ? err.message : String(err);
538
- // Worker not running — surface as WORKER_UNAVAILABLE
539
- if (message.includes("not running") || message.includes("not registered")) {
540
- return {
541
- code: "WORKER_UNAVAILABLE",
542
- message,
543
- };
544
- }
545
- return {
546
- code: "UNKNOWN",
547
- message,
548
- };
549
- }
550
- /**
551
- * POST /api/plugins/:pluginId/bridge/data
552
- *
553
- * Proxy a `getData` call from the plugin UI to the plugin worker.
554
- *
555
- * This is the server-side half of the `usePluginData(key, params)` bridge hook.
556
- * The frontend sends a POST with the data key and optional params; the host
557
- * forwards the call to the worker via the `getData` RPC method and returns
558
- * the result.
559
- *
560
- * Request body:
561
- * - `key`: Plugin-defined data key (e.g. `"sync-health"`)
562
- * - `params`: Optional query parameters forwarded to the worker handler
563
- *
564
- * Response: The raw result from the worker's `getData` handler
565
- *
566
- * Error response body follows the `PluginBridgeError` shape:
567
- * `{ code: PluginBridgeErrorCode, message: string, details?: unknown }`
568
- *
569
- * Errors:
570
- * - 400 if request validation fails
571
- * - 404 if plugin not found
572
- * - 501 if bridge deps are not configured
573
- * - 502 if the worker is unavailable or returns an error
574
- *
575
- * @see doc/engineering/PLUGIN_RUNTIME_CONTRACT.md — `getData`
576
- * @see doc/engineering/PLUGIN_RUNTIME_CONTRACT.md — Error Propagation Through The Bridge
577
- */
578
- router.post("/plugins/:pluginId/bridge/data", async (req, res) => {
579
- assertBoard(req);
580
- if (!bridgeDeps) {
581
- res.status(501).json({ error: "Plugin bridge is not enabled" });
582
- return;
583
- }
584
- const { pluginId } = req.params;
585
- // Resolve plugin
586
- const plugin = await resolvePlugin(registry, pluginId);
587
- if (!plugin) {
588
- res.status(404).json({ error: "Plugin not found" });
589
- return;
590
- }
591
- // Validate plugin is in ready state
592
- if (plugin.status !== "ready") {
593
- const bridgeError = {
594
- code: "WORKER_UNAVAILABLE",
595
- message: `Plugin is not ready (current status: ${plugin.status})`,
596
- };
597
- res.status(502).json(bridgeError);
598
- return;
599
- }
600
- // Validate request body
601
- const body = req.body;
602
- if (!body || !body.key || typeof body.key !== "string") {
603
- res.status(400).json({ error: '"key" is required and must be a string' });
604
- return;
605
- }
606
- if (body.orgId) {
607
- assertCompanyAccess(req, body.orgId);
608
- }
609
- try {
610
- const result = await bridgeDeps.workerManager.call(plugin.id, "getData", {
611
- key: body.key,
612
- params: body.params ?? {},
613
- renderEnvironment: body.renderEnvironment ?? null,
614
- });
615
- res.json({ data: result });
616
- }
617
- catch (err) {
618
- const bridgeError = mapRpcErrorToBridgeError(err);
619
- res.status(502).json(bridgeError);
620
- }
621
- });
622
- /**
623
- * POST /api/plugins/:pluginId/bridge/action
624
- *
625
- * Proxy a `performAction` call from the plugin UI to the plugin worker.
626
- *
627
- * This is the server-side half of the `usePluginAction(key)` bridge hook.
628
- * The frontend sends a POST with the action key and optional params; the host
629
- * forwards the call to the worker via the `performAction` RPC method and
630
- * returns the result.
631
- *
632
- * Request body:
633
- * - `key`: Plugin-defined action key (e.g. `"resync"`)
634
- * - `params`: Optional parameters forwarded to the worker handler
635
- *
636
- * Response: The raw result from the worker's `performAction` handler
637
- *
638
- * Error response body follows the `PluginBridgeError` shape:
639
- * `{ code: PluginBridgeErrorCode, message: string, details?: unknown }`
640
- *
641
- * Errors:
642
- * - 400 if request validation fails
643
- * - 404 if plugin not found
644
- * - 501 if bridge deps are not configured
645
- * - 502 if the worker is unavailable or returns an error
646
- *
647
- * @see doc/engineering/PLUGIN_RUNTIME_CONTRACT.md — `performAction`
648
- * @see doc/engineering/PLUGIN_RUNTIME_CONTRACT.md — Error Propagation Through The Bridge
649
- */
650
- router.post("/plugins/:pluginId/bridge/action", async (req, res) => {
651
- assertBoard(req);
652
- if (!bridgeDeps) {
653
- res.status(501).json({ error: "Plugin bridge is not enabled" });
654
- return;
655
- }
656
- const { pluginId } = req.params;
657
- // Resolve plugin
658
- const plugin = await resolvePlugin(registry, pluginId);
659
- if (!plugin) {
660
- res.status(404).json({ error: "Plugin not found" });
661
- return;
662
- }
663
- // Validate plugin is in ready state
664
- if (plugin.status !== "ready") {
665
- const bridgeError = {
666
- code: "WORKER_UNAVAILABLE",
667
- message: `Plugin is not ready (current status: ${plugin.status})`,
668
- };
669
- res.status(502).json(bridgeError);
670
- return;
671
- }
672
- // Validate request body
673
- const body = req.body;
674
- if (!body || !body.key || typeof body.key !== "string") {
675
- res.status(400).json({ error: '"key" is required and must be a string' });
676
- return;
677
- }
678
- if (body.orgId) {
679
- assertCompanyAccess(req, body.orgId);
680
- }
681
- try {
682
- const result = await bridgeDeps.workerManager.call(plugin.id, "performAction", {
683
- key: body.key,
684
- params: body.params ?? {},
685
- renderEnvironment: body.renderEnvironment ?? null,
686
- });
687
- res.json({ data: result });
688
- }
689
- catch (err) {
690
- const bridgeError = mapRpcErrorToBridgeError(err);
691
- res.status(502).json(bridgeError);
692
- }
693
- });
694
- // ===========================================================================
695
- // URL-keyed bridge routes (key as path parameter)
696
- // ===========================================================================
697
- /**
698
- * POST /api/plugins/:pluginId/data/:key
699
- *
700
- * Proxy a `getData` call from the plugin UI to the plugin worker, with the
701
- * data key specified as a URL path parameter instead of in the request body.
702
- *
703
- * This is a REST-friendly alternative to `POST /plugins/:pluginId/bridge/data`.
704
- * The frontend bridge hooks use this endpoint for cleaner URLs.
705
- *
706
- * Request body (optional):
707
- * - `params`: Optional query parameters forwarded to the worker handler
708
- *
709
- * Response: The raw result from the worker's `getData` handler wrapped as `{ data: T }`
710
- *
711
- * Error response body follows the `PluginBridgeError` shape:
712
- * `{ code: PluginBridgeErrorCode, message: string, details?: unknown }`
713
- *
714
- * Errors:
715
- * - 404 if plugin not found
716
- * - 501 if bridge deps are not configured
717
- * - 502 if the worker is unavailable or returns an error
718
- *
719
- * @see doc/engineering/PLUGIN_RUNTIME_CONTRACT.md — `getData`
720
- * @see doc/engineering/PLUGIN_RUNTIME_CONTRACT.md — Error Propagation Through The Bridge
721
- */
722
- router.post("/plugins/:pluginId/data/:key", async (req, res) => {
723
- assertBoard(req);
724
- if (!bridgeDeps) {
725
- res.status(501).json({ error: "Plugin bridge is not enabled" });
726
- return;
727
- }
728
- const { pluginId, key } = req.params;
729
- // Resolve plugin
730
- const plugin = await resolvePlugin(registry, pluginId);
731
- if (!plugin) {
732
- res.status(404).json({ error: "Plugin not found" });
733
- return;
734
- }
735
- // Validate plugin is in ready state
736
- if (plugin.status !== "ready") {
737
- const bridgeError = {
738
- code: "WORKER_UNAVAILABLE",
739
- message: `Plugin is not ready (current status: ${plugin.status})`,
740
- };
741
- res.status(502).json(bridgeError);
742
- return;
743
- }
744
- const body = req.body;
745
- if (body?.orgId) {
746
- assertCompanyAccess(req, body.orgId);
747
- }
748
- try {
749
- const result = await bridgeDeps.workerManager.call(plugin.id, "getData", {
750
- key,
751
- params: body?.params ?? {},
752
- renderEnvironment: body?.renderEnvironment ?? null,
753
- });
754
- res.json({ data: result });
755
- }
756
- catch (err) {
757
- const bridgeError = mapRpcErrorToBridgeError(err);
758
- res.status(502).json(bridgeError);
759
- }
760
- });
761
- /**
762
- * POST /api/plugins/:pluginId/actions/:key
763
- *
764
- * Proxy a `performAction` call from the plugin UI to the plugin worker, with
765
- * the action key specified as a URL path parameter instead of in the request body.
766
- *
767
- * This is a REST-friendly alternative to `POST /plugins/:pluginId/bridge/action`.
768
- * The frontend bridge hooks use this endpoint for cleaner URLs.
769
- *
770
- * Request body (optional):
771
- * - `params`: Optional parameters forwarded to the worker handler
772
- *
773
- * Response: The raw result from the worker's `performAction` handler wrapped as `{ data: T }`
774
- *
775
- * Error response body follows the `PluginBridgeError` shape:
776
- * `{ code: PluginBridgeErrorCode, message: string, details?: unknown }`
777
- *
778
- * Errors:
779
- * - 404 if plugin not found
780
- * - 501 if bridge deps are not configured
781
- * - 502 if the worker is unavailable or returns an error
782
- *
783
- * @see doc/engineering/PLUGIN_RUNTIME_CONTRACT.md — `performAction`
784
- * @see doc/engineering/PLUGIN_RUNTIME_CONTRACT.md — Error Propagation Through The Bridge
785
- */
786
- router.post("/plugins/:pluginId/actions/:key", async (req, res) => {
787
- assertBoard(req);
788
- if (!bridgeDeps) {
789
- res.status(501).json({ error: "Plugin bridge is not enabled" });
790
- return;
791
- }
792
- const { pluginId, key } = req.params;
793
- // Resolve plugin
794
- const plugin = await resolvePlugin(registry, pluginId);
795
- if (!plugin) {
796
- res.status(404).json({ error: "Plugin not found" });
797
- return;
798
- }
799
- // Validate plugin is in ready state
800
- if (plugin.status !== "ready") {
801
- const bridgeError = {
802
- code: "WORKER_UNAVAILABLE",
803
- message: `Plugin is not ready (current status: ${plugin.status})`,
804
- };
805
- res.status(502).json(bridgeError);
806
- return;
807
- }
808
- const body = req.body;
809
- if (body?.orgId) {
810
- assertCompanyAccess(req, body.orgId);
811
- }
812
- try {
813
- const result = await bridgeDeps.workerManager.call(plugin.id, "performAction", {
814
- key,
815
- params: body?.params ?? {},
816
- renderEnvironment: body?.renderEnvironment ?? null,
817
- });
818
- res.json({ data: result });
819
- }
820
- catch (err) {
821
- const bridgeError = mapRpcErrorToBridgeError(err);
822
- res.status(502).json(bridgeError);
823
- }
824
- });
825
- // ===========================================================================
826
- // SSE stream bridge route
827
- // ===========================================================================
828
- /**
829
- * GET /api/plugins/:pluginId/bridge/stream/:channel
830
- *
831
- * Server-Sent Events endpoint for real-time streaming from plugin worker to UI.
832
- *
833
- * The worker pushes events via `ctx.streams.emit(channel, event)` which arrive
834
- * as JSON-RPC notifications to the host, get published on the PluginStreamBus,
835
- * and are fanned out to all connected SSE clients matching (pluginId, channel,
836
- * orgId).
837
- *
838
- * Query parameters:
839
- * - `orgId` (required): Scope events to a specific organization
840
- *
841
- * SSE event types:
842
- * - `message`: A data event from the worker (default)
843
- * - `open`: The worker opened the stream channel
844
- * - `close`: The worker closed the stream channel — client should disconnect
845
- *
846
- * Errors:
847
- * - 400 if orgId is missing
848
- * - 404 if plugin not found
849
- * - 501 if bridge deps or stream bus are not configured
850
- */
851
- router.get("/plugins/:pluginId/bridge/stream/:channel", async (req, res) => {
852
- assertBoard(req);
853
- if (!bridgeDeps?.streamBus) {
854
- res.status(501).json({ error: "Plugin stream bridge is not enabled" });
855
- return;
856
- }
857
- const { pluginId, channel } = req.params;
858
- const orgId = req.query.orgId;
859
- if (!orgId) {
860
- res.status(400).json({ error: '"orgId" query parameter is required' });
861
- return;
862
- }
863
- const plugin = await resolvePlugin(registry, pluginId);
864
- if (!plugin) {
865
- res.status(404).json({ error: "Plugin not found" });
866
- return;
867
- }
868
- assertCompanyAccess(req, orgId);
869
- // Set SSE headers
870
- res.writeHead(200, {
871
- "Content-Type": "text/event-stream",
872
- "Cache-Control": "no-cache",
873
- "Connection": "keep-alive",
874
- "X-Accel-Buffering": "no",
875
- });
876
- res.flushHeaders();
877
- // Send initial comment to establish the connection
878
- res.write(":ok\n\n");
879
- let unsubscribed = false;
880
- const safeUnsubscribe = () => {
881
- if (!unsubscribed) {
882
- unsubscribed = true;
883
- unsubscribe();
884
- }
885
- };
886
- const unsubscribe = bridgeDeps.streamBus.subscribe(plugin.id, channel, orgId, (event, eventType) => {
887
- if (unsubscribed || !res.writable)
888
- return;
889
- try {
890
- if (eventType !== "message") {
891
- res.write(`event: ${eventType}\n`);
892
- }
893
- res.write(`data: ${JSON.stringify(event)}\n\n`);
894
- }
895
- catch {
896
- // Connection closed or write error — stop delivering
897
- safeUnsubscribe();
898
- }
899
- });
900
- req.on("close", safeUnsubscribe);
901
- res.on("error", safeUnsubscribe);
902
- });
903
- /**
904
- * GET /api/plugins/:pluginId
905
- *
906
- * Get detailed information about a single plugin.
907
- *
908
- * The :pluginId parameter accepts either:
909
- * - Database UUID (e.g., "abc123-def456")
910
- * - Plugin key (e.g., "acme.linear")
911
- *
912
- * Response: PluginRecord
913
- * Errors: 404 if plugin not found
914
- */
915
- router.get("/plugins/:pluginId", async (req, res) => {
916
- assertBoard(req);
917
- const { pluginId } = req.params;
918
- const plugin = await resolvePlugin(registry, pluginId);
919
- if (!plugin) {
920
- res.status(404).json({ error: "Plugin not found" });
921
- return;
922
- }
923
- // Enrich with worker capabilities when available
924
- const worker = bridgeDeps?.workerManager.getWorker(plugin.id);
925
- const supportsConfigTest = worker
926
- ? worker.supportedMethods.includes("validateConfig")
927
- : false;
928
- res.json({ ...plugin, supportsConfigTest });
929
- });
930
- /**
931
- * DELETE /api/plugins/:pluginId
932
- *
933
- * Uninstall a plugin.
934
- *
935
- * Query params:
936
- * - purge: If "true", permanently delete all plugin data (hard delete)
937
- * Otherwise, soft-delete with 30-day data retention
938
- *
939
- * Response: PluginRecord (the deleted record)
940
- * Errors: 404 if plugin not found, 400 for lifecycle errors
941
- */
942
- router.delete("/plugins/:pluginId", async (req, res) => {
943
- assertBoard(req);
944
- const { pluginId } = req.params;
945
- const purge = req.query.purge === "true";
946
- const plugin = await resolvePlugin(registry, pluginId);
947
- if (!plugin) {
948
- res.status(404).json({ error: "Plugin not found" });
949
- return;
950
- }
951
- try {
952
- const result = await lifecycle.unload(plugin.id, purge);
953
- await logPluginMutationActivity(req, "plugin.uninstalled", plugin.id, {
954
- pluginId: plugin.id,
955
- pluginKey: plugin.pluginKey,
956
- purge,
957
- });
958
- publishGlobalLiveEvent({ type: "plugin.ui.updated", payload: { pluginId: plugin.id, action: "uninstalled" } });
959
- res.json(result);
960
- }
961
- catch (err) {
962
- const message = err instanceof Error ? err.message : String(err);
963
- res.status(400).json({ error: message });
964
- }
965
- });
966
- /**
967
- * POST /api/plugins/:pluginId/enable
968
- *
969
- * Enable a plugin that is currently disabled or in error state.
970
- *
971
- * Transitions the plugin to 'ready' state after loading and validation.
972
- *
973
- * Response: PluginRecord
974
- * Errors: 404 if plugin not found, 400 for lifecycle errors
975
- */
976
- router.post("/plugins/:pluginId/enable", async (req, res) => {
977
- assertBoard(req);
978
- const { pluginId } = req.params;
979
- const plugin = await resolvePlugin(registry, pluginId);
980
- if (!plugin) {
981
- res.status(404).json({ error: "Plugin not found" });
982
- return;
983
- }
984
- try {
985
- const result = await lifecycle.enable(plugin.id);
986
- await logPluginMutationActivity(req, "plugin.enabled", plugin.id, {
987
- pluginId: plugin.id,
988
- pluginKey: plugin.pluginKey,
989
- version: result?.version ?? plugin.version,
990
- });
991
- publishGlobalLiveEvent({ type: "plugin.ui.updated", payload: { pluginId: plugin.id, action: "enabled" } });
992
- res.json(result);
993
- }
994
- catch (err) {
995
- const message = err instanceof Error ? err.message : String(err);
996
- res.status(400).json({ error: message });
997
- }
998
- });
999
- /**
1000
- * POST /api/plugins/:pluginId/disable
1001
- *
1002
- * Disable a running plugin.
1003
- *
1004
- * Request body (optional):
1005
- * - reason: Human-readable reason for disabling
1006
- *
1007
- * The plugin transitions to 'installed' state and stops processing events.
1008
- *
1009
- * Response: PluginRecord
1010
- * Errors: 404 if plugin not found, 400 for lifecycle errors
1011
- */
1012
- router.post("/plugins/:pluginId/disable", async (req, res) => {
1013
- assertBoard(req);
1014
- const { pluginId } = req.params;
1015
- const body = req.body;
1016
- const reason = body?.reason;
1017
- const plugin = await resolvePlugin(registry, pluginId);
1018
- if (!plugin) {
1019
- res.status(404).json({ error: "Plugin not found" });
1020
- return;
1021
- }
1022
- try {
1023
- const result = await lifecycle.disable(plugin.id, reason);
1024
- await logPluginMutationActivity(req, "plugin.disabled", plugin.id, {
1025
- pluginId: plugin.id,
1026
- pluginKey: plugin.pluginKey,
1027
- reason: reason ?? null,
1028
- });
1029
- publishGlobalLiveEvent({ type: "plugin.ui.updated", payload: { pluginId: plugin.id, action: "disabled" } });
1030
- res.json(result);
1031
- }
1032
- catch (err) {
1033
- const message = err instanceof Error ? err.message : String(err);
1034
- res.status(400).json({ error: message });
1035
- }
1036
- });
1037
- /**
1038
- * GET /api/plugins/:pluginId/health
1039
- *
1040
- * Run health diagnostics on a plugin.
1041
- *
1042
- * Performs the following checks:
1043
- * 1. Registry: Plugin is registered in the database
1044
- * 2. Manifest: Manifest is valid and parseable
1045
- * 3. Status: Plugin is in 'ready' state
1046
- * 4. Error state: Plugin has no unhandled errors
1047
- *
1048
- * Response: PluginHealthCheckResult
1049
- * Errors: 404 if plugin not found
1050
- */
1051
- router.get("/plugins/:pluginId/health", async (req, res) => {
1052
- assertBoard(req);
1053
- const { pluginId } = req.params;
1054
- const plugin = await resolvePlugin(registry, pluginId);
1055
- if (!plugin) {
1056
- res.status(404).json({ error: "Plugin not found" });
1057
- return;
1058
- }
1059
- const checks = [];
1060
- // Check 1: Plugin is registered
1061
- checks.push({
1062
- name: "registry",
1063
- passed: true,
1064
- message: "Plugin found in registry",
1065
- });
1066
- // Check 2: Manifest is valid
1067
- const hasValidManifest = Boolean(plugin.manifestJson?.id);
1068
- checks.push({
1069
- name: "manifest",
1070
- passed: hasValidManifest,
1071
- message: hasValidManifest ? "Manifest is valid" : "Manifest is invalid or missing",
1072
- });
1073
- // Check 3: Plugin status
1074
- const isHealthy = plugin.status === "ready";
1075
- checks.push({
1076
- name: "status",
1077
- passed: isHealthy,
1078
- message: `Current status: ${plugin.status}`,
1079
- });
1080
- // Check 4: No last error
1081
- const hasNoError = !plugin.lastError;
1082
- if (!hasNoError) {
1083
- checks.push({
1084
- name: "error_state",
1085
- passed: false,
1086
- message: plugin.lastError ?? undefined,
1087
- });
1088
- }
1089
- const result = {
1090
- pluginId: plugin.id,
1091
- status: plugin.status,
1092
- healthy: isHealthy && hasValidManifest && hasNoError,
1093
- checks,
1094
- lastError: plugin.lastError ?? undefined,
1095
- };
1096
- res.json(result);
1097
- });
1098
- /**
1099
- * GET /api/plugins/:pluginId/logs
1100
- *
1101
- * Query recent log entries for a plugin.
1102
- *
1103
- * Query params:
1104
- * - limit: Maximum number of entries (default 25, max 500)
1105
- * - level: Filter by log level (info, warn, error, debug)
1106
- * - since: ISO timestamp to filter logs newer than this time
1107
- *
1108
- * Response: Array of log entries, newest first.
1109
- */
1110
- registerPluginOperationsRoutes({
1111
- router,
1112
- db,
1113
- loader,
1114
- registry,
1115
- lifecycle,
1116
- bridgeDeps,
1117
- jobDeps,
1118
- webhookDeps,
1119
- resolvePlugin,
1120
- logPluginMutationActivity,
1121
- mapRpcErrorToBridgeError,
1122
- });
1123
- return router;
1124
- }
1125
- //# sourceMappingURL=plugins.js.map