@slaw-ai/server 2026.611.0

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 (1198) hide show
  1. package/LICENSE +26 -0
  2. package/dist/adapters/builtin-adapter-types.d.ts +5 -0
  3. package/dist/adapters/builtin-adapter-types.d.ts.map +1 -0
  4. package/dist/adapters/builtin-adapter-types.js +18 -0
  5. package/dist/adapters/builtin-adapter-types.js.map +1 -0
  6. package/dist/adapters/codex-models.d.ts +5 -0
  7. package/dist/adapters/codex-models.d.ts.map +1 -0
  8. package/dist/adapters/codex-models.js +105 -0
  9. package/dist/adapters/codex-models.js.map +1 -0
  10. package/dist/adapters/cursor-models.d.ts +13 -0
  11. package/dist/adapters/cursor-models.d.ts.map +1 -0
  12. package/dist/adapters/cursor-models.js +148 -0
  13. package/dist/adapters/cursor-models.js.map +1 -0
  14. package/dist/adapters/http/execute.d.ts +3 -0
  15. package/dist/adapters/http/execute.d.ts.map +1 -0
  16. package/dist/adapters/http/execute.js +51 -0
  17. package/dist/adapters/http/execute.js.map +1 -0
  18. package/dist/adapters/http/execute.test.d.ts +2 -0
  19. package/dist/adapters/http/execute.test.d.ts.map +1 -0
  20. package/dist/adapters/http/execute.test.js +40 -0
  21. package/dist/adapters/http/execute.test.js.map +1 -0
  22. package/dist/adapters/http/index.d.ts +3 -0
  23. package/dist/adapters/http/index.d.ts.map +1 -0
  24. package/dist/adapters/http/index.js +20 -0
  25. package/dist/adapters/http/index.js.map +1 -0
  26. package/dist/adapters/http/test.d.ts +3 -0
  27. package/dist/adapters/http/test.d.ts.map +1 -0
  28. package/dist/adapters/http/test.js +106 -0
  29. package/dist/adapters/http/test.js.map +1 -0
  30. package/dist/adapters/index.d.ts +4 -0
  31. package/dist/adapters/index.d.ts.map +1 -0
  32. package/dist/adapters/index.js +3 -0
  33. package/dist/adapters/index.js.map +1 -0
  34. package/dist/adapters/plugin-loader.d.ts +28 -0
  35. package/dist/adapters/plugin-loader.d.ts.map +1 -0
  36. package/dist/adapters/plugin-loader.js +196 -0
  37. package/dist/adapters/plugin-loader.js.map +1 -0
  38. package/dist/adapters/process/execute.d.ts +3 -0
  39. package/dist/adapters/process/execute.d.ts.map +1 -0
  40. package/dist/adapters/process/execute.js +70 -0
  41. package/dist/adapters/process/execute.js.map +1 -0
  42. package/dist/adapters/process/index.d.ts +3 -0
  43. package/dist/adapters/process/index.d.ts.map +1 -0
  44. package/dist/adapters/process/index.js +23 -0
  45. package/dist/adapters/process/index.js.map +1 -0
  46. package/dist/adapters/process/test.d.ts +3 -0
  47. package/dist/adapters/process/test.d.ts.map +1 -0
  48. package/dist/adapters/process/test.js +77 -0
  49. package/dist/adapters/process/test.js.map +1 -0
  50. package/dist/adapters/registry.d.ts +69 -0
  51. package/dist/adapters/registry.d.ts.map +1 -0
  52. package/dist/adapters/registry.js +598 -0
  53. package/dist/adapters/registry.js.map +1 -0
  54. package/dist/adapters/types.d.ts +2 -0
  55. package/dist/adapters/types.d.ts.map +1 -0
  56. package/dist/adapters/types.js +2 -0
  57. package/dist/adapters/types.js.map +1 -0
  58. package/dist/adapters/utils.d.ts +43 -0
  59. package/dist/adapters/utils.d.ts.map +1 -0
  60. package/dist/adapters/utils.js +52 -0
  61. package/dist/adapters/utils.js.map +1 -0
  62. package/dist/agent-auth-jwt.d.ts +14 -0
  63. package/dist/agent-auth-jwt.d.ts.map +1 -0
  64. package/dist/agent-auth-jwt.js +117 -0
  65. package/dist/agent-auth-jwt.js.map +1 -0
  66. package/dist/app.d.ts +39 -0
  67. package/dist/app.d.ts.map +1 -0
  68. package/dist/app.js +386 -0
  69. package/dist/app.js.map +1 -0
  70. package/dist/attachment-types.d.ts +23 -0
  71. package/dist/attachment-types.d.ts.map +1 -0
  72. package/dist/attachment-types.js +98 -0
  73. package/dist/attachment-types.js.map +1 -0
  74. package/dist/auth/better-auth.d.ts +40 -0
  75. package/dist/auth/better-auth.d.ts.map +1 -0
  76. package/dist/auth/better-auth.js +148 -0
  77. package/dist/auth/better-auth.js.map +1 -0
  78. package/dist/config-file.d.ts +24 -0
  79. package/dist/config-file.d.ts.map +1 -0
  80. package/dist/config-file.js +73 -0
  81. package/dist/config-file.js.map +1 -0
  82. package/dist/config.d.ts +44 -0
  83. package/dist/config.d.ts.map +1 -0
  84. package/dist/config.js +247 -0
  85. package/dist/config.js.map +1 -0
  86. package/dist/dev-runner-worktree.d.ts +15 -0
  87. package/dist/dev-runner-worktree.d.ts.map +1 -0
  88. package/dist/dev-runner-worktree.js +101 -0
  89. package/dist/dev-runner-worktree.js.map +1 -0
  90. package/dist/dev-server-status.d.ts +33 -0
  91. package/dist/dev-server-status.d.ts.map +1 -0
  92. package/dist/dev-server-status.js +89 -0
  93. package/dist/dev-server-status.js.map +1 -0
  94. package/dist/dev-watch-ignore.d.ts +2 -0
  95. package/dist/dev-watch-ignore.d.ts.map +1 -0
  96. package/dist/dev-watch-ignore.js +36 -0
  97. package/dist/dev-watch-ignore.js.map +1 -0
  98. package/dist/errors.d.ts +12 -0
  99. package/dist/errors.d.ts.map +1 -0
  100. package/dist/errors.js +28 -0
  101. package/dist/errors.js.map +1 -0
  102. package/dist/first-admin-claim.d.ts +17 -0
  103. package/dist/first-admin-claim.d.ts.map +1 -0
  104. package/dist/first-admin-claim.js +30 -0
  105. package/dist/first-admin-claim.js.map +1 -0
  106. package/dist/home-paths.d.ts +15 -0
  107. package/dist/home-paths.d.ts.map +1 -0
  108. package/dist/home-paths.js +48 -0
  109. package/dist/home-paths.js.map +1 -0
  110. package/dist/http/body-limits.d.ts +4 -0
  111. package/dist/http/body-limits.d.ts.map +1 -0
  112. package/dist/http/body-limits.js +4 -0
  113. package/dist/http/body-limits.js.map +1 -0
  114. package/dist/index.d.ts +10 -0
  115. package/dist/index.d.ts.map +1 -0
  116. package/dist/index.js +786 -0
  117. package/dist/index.js.map +1 -0
  118. package/dist/instance-claim.d.ts +23 -0
  119. package/dist/instance-claim.d.ts.map +1 -0
  120. package/dist/instance-claim.js +126 -0
  121. package/dist/instance-claim.js.map +1 -0
  122. package/dist/lib/join-request-dedupe.d.ts +11 -0
  123. package/dist/lib/join-request-dedupe.d.ts.map +1 -0
  124. package/dist/lib/join-request-dedupe.js +49 -0
  125. package/dist/lib/join-request-dedupe.js.map +1 -0
  126. package/dist/log-redaction.d.ts +11 -0
  127. package/dist/log-redaction.d.ts.map +1 -0
  128. package/dist/log-redaction.js +122 -0
  129. package/dist/log-redaction.js.map +1 -0
  130. package/dist/middleware/auth.d.ts +12 -0
  131. package/dist/middleware/auth.d.ts.map +1 -0
  132. package/dist/middleware/auth.js +302 -0
  133. package/dist/middleware/auth.js.map +1 -0
  134. package/dist/middleware/error-handler.d.ts +17 -0
  135. package/dist/middleware/error-handler.d.ts.map +1 -0
  136. package/dist/middleware/error-handler.js +46 -0
  137. package/dist/middleware/error-handler.js.map +1 -0
  138. package/dist/middleware/http-log-policy.d.ts +2 -0
  139. package/dist/middleware/http-log-policy.d.ts.map +1 -0
  140. package/dist/middleware/http-log-policy.js +52 -0
  141. package/dist/middleware/http-log-policy.js.map +1 -0
  142. package/dist/middleware/index.d.ts +4 -0
  143. package/dist/middleware/index.d.ts.map +1 -0
  144. package/dist/middleware/index.js +4 -0
  145. package/dist/middleware/index.js.map +1 -0
  146. package/dist/middleware/logger.d.ts +4 -0
  147. package/dist/middleware/logger.d.ts.map +1 -0
  148. package/dist/middleware/logger.js +92 -0
  149. package/dist/middleware/logger.js.map +1 -0
  150. package/dist/middleware/operator-mutation-guard.d.ts +3 -0
  151. package/dist/middleware/operator-mutation-guard.d.ts.map +1 -0
  152. package/dist/middleware/operator-mutation-guard.js +70 -0
  153. package/dist/middleware/operator-mutation-guard.js.map +1 -0
  154. package/dist/middleware/private-hostname-guard.d.ts +11 -0
  155. package/dist/middleware/private-hostname-guard.d.ts.map +1 -0
  156. package/dist/middleware/private-hostname-guard.js +78 -0
  157. package/dist/middleware/private-hostname-guard.js.map +1 -0
  158. package/dist/middleware/validate.d.ts +4 -0
  159. package/dist/middleware/validate.d.ts.map +1 -0
  160. package/dist/middleware/validate.js +7 -0
  161. package/dist/middleware/validate.js.map +1 -0
  162. package/dist/onboarding-assets/default/AGENTS.md +18 -0
  163. package/dist/onboarding-assets/squad_lead/AGENTS.md +61 -0
  164. package/dist/onboarding-assets/squad_lead/HEARTBEAT.md +85 -0
  165. package/dist/onboarding-assets/squad_lead/SOUL.md +33 -0
  166. package/dist/onboarding-assets/squad_lead/TOOLS.md +3 -0
  167. package/dist/paths.d.ts +3 -0
  168. package/dist/paths.d.ts.map +1 -0
  169. package/dist/paths.js +31 -0
  170. package/dist/paths.js.map +1 -0
  171. package/dist/realtime/live-events-ws.d.ts +28 -0
  172. package/dist/realtime/live-events-ws.d.ts.map +1 -0
  173. package/dist/realtime/live-events-ws.js +187 -0
  174. package/dist/realtime/live-events-ws.js.map +1 -0
  175. package/dist/redaction.d.ts +5 -0
  176. package/dist/redaction.d.ts.map +1 -0
  177. package/dist/redaction.js +125 -0
  178. package/dist/redaction.js.map +1 -0
  179. package/dist/routes/access.d.ts +75 -0
  180. package/dist/routes/access.d.ts.map +1 -0
  181. package/dist/routes/access.js +3070 -0
  182. package/dist/routes/access.js.map +1 -0
  183. package/dist/routes/activity.d.ts +3 -0
  184. package/dist/routes/activity.d.ts.map +1 -0
  185. package/dist/routes/activity.js +90 -0
  186. package/dist/routes/activity.js.map +1 -0
  187. package/dist/routes/adapters.d.ts +16 -0
  188. package/dist/routes/adapters.d.ts.map +1 -0
  189. package/dist/routes/adapters.js +539 -0
  190. package/dist/routes/adapters.js.map +1 -0
  191. package/dist/routes/agents.d.ts +6 -0
  192. package/dist/routes/agents.d.ts.map +1 -0
  193. package/dist/routes/agents.js +2733 -0
  194. package/dist/routes/agents.js.map +1 -0
  195. package/dist/routes/approvals.d.ts +6 -0
  196. package/dist/routes/approvals.d.ts.map +1 -0
  197. package/dist/routes/approvals.js +300 -0
  198. package/dist/routes/approvals.js.map +1 -0
  199. package/dist/routes/assets.d.ts +4 -0
  200. package/dist/routes/assets.d.ts.map +1 -0
  201. package/dist/routes/assets.js +309 -0
  202. package/dist/routes/assets.js.map +1 -0
  203. package/dist/routes/auth.d.ts +3 -0
  204. package/dist/routes/auth.d.ts.map +1 -0
  205. package/dist/routes/auth.js +82 -0
  206. package/dist/routes/auth.js.map +1 -0
  207. package/dist/routes/authz.d.ts +19 -0
  208. package/dist/routes/authz.d.ts.map +1 -0
  209. package/dist/routes/authz.js +75 -0
  210. package/dist/routes/authz.js.map +1 -0
  211. package/dist/routes/botfather.d.ts +9 -0
  212. package/dist/routes/botfather.d.ts.map +1 -0
  213. package/dist/routes/botfather.js +127 -0
  214. package/dist/routes/botfather.js.map +1 -0
  215. package/dist/routes/cloud-upstreams.d.ts +5 -0
  216. package/dist/routes/cloud-upstreams.d.ts.map +1 -0
  217. package/dist/routes/cloud-upstreams.js +103 -0
  218. package/dist/routes/cloud-upstreams.js.map +1 -0
  219. package/dist/routes/costs.d.ts +11 -0
  220. package/dist/routes/costs.d.ts.map +1 -0
  221. package/dist/routes/costs.js +285 -0
  222. package/dist/routes/costs.js.map +1 -0
  223. package/dist/routes/dashboard.d.ts +3 -0
  224. package/dist/routes/dashboard.d.ts.map +1 -0
  225. package/dist/routes/dashboard.js +15 -0
  226. package/dist/routes/dashboard.js.map +1 -0
  227. package/dist/routes/environment-selection.d.ts +13 -0
  228. package/dist/routes/environment-selection.d.ts.map +1 -0
  229. package/dist/routes/environment-selection.js +30 -0
  230. package/dist/routes/environment-selection.js.map +1 -0
  231. package/dist/routes/environments.d.ts +6 -0
  232. package/dist/routes/environments.d.ts.map +1 -0
  233. package/dist/routes/environments.js +414 -0
  234. package/dist/routes/environments.js.map +1 -0
  235. package/dist/routes/execution-workspaces.d.ts +3 -0
  236. package/dist/routes/execution-workspaces.d.ts.map +1 -0
  237. package/dist/routes/execution-workspaces.js +537 -0
  238. package/dist/routes/execution-workspaces.js.map +1 -0
  239. package/dist/routes/goals.d.ts +3 -0
  240. package/dist/routes/goals.d.ts.map +1 -0
  241. package/dist/routes/goals.js +95 -0
  242. package/dist/routes/goals.js.map +1 -0
  243. package/dist/routes/health.d.ts +9 -0
  244. package/dist/routes/health.d.ts.map +1 -0
  245. package/dist/routes/health.js +143 -0
  246. package/dist/routes/health.js.map +1 -0
  247. package/dist/routes/inbox-dismissals.d.ts +3 -0
  248. package/dist/routes/inbox-dismissals.d.ts.map +1 -0
  249. package/dist/routes/inbox-dismissals.js +58 -0
  250. package/dist/routes/inbox-dismissals.js.map +1 -0
  251. package/dist/routes/index.d.ts +24 -0
  252. package/dist/routes/index.d.ts.map +1 -0
  253. package/dist/routes/index.js +24 -0
  254. package/dist/routes/index.js.map +1 -0
  255. package/dist/routes/instance-database-backups.d.ts +15 -0
  256. package/dist/routes/instance-database-backups.d.ts.map +1 -0
  257. package/dist/routes/instance-database-backups.js +12 -0
  258. package/dist/routes/instance-database-backups.js.map +1 -0
  259. package/dist/routes/instance-settings.d.ts +3 -0
  260. package/dist/routes/instance-settings.d.ts.map +1 -0
  261. package/dist/routes/instance-settings.js +110 -0
  262. package/dist/routes/instance-settings.js.map +1 -0
  263. package/dist/routes/issue-tree-control.d.ts +3 -0
  264. package/dist/routes/issue-tree-control.d.ts.map +1 -0
  265. package/dist/routes/issue-tree-control.js +373 -0
  266. package/dist/routes/issue-tree-control.js.map +1 -0
  267. package/dist/routes/issues-checkout-wakeup.d.ts +9 -0
  268. package/dist/routes/issues-checkout-wakeup.d.ts.map +1 -0
  269. package/dist/routes/issues-checkout-wakeup.js +12 -0
  270. package/dist/routes/issues-checkout-wakeup.js.map +1 -0
  271. package/dist/routes/issues.d.ts +15 -0
  272. package/dist/routes/issues.d.ts.map +1 -0
  273. package/dist/routes/issues.js +5276 -0
  274. package/dist/routes/issues.js.map +1 -0
  275. package/dist/routes/llms.d.ts +3 -0
  276. package/dist/routes/llms.d.ts.map +1 -0
  277. package/dist/routes/llms.js +80 -0
  278. package/dist/routes/llms.js.map +1 -0
  279. package/dist/routes/openapi.d.ts +4 -0
  280. package/dist/routes/openapi.d.ts.map +1 -0
  281. package/dist/routes/openapi.js +3284 -0
  282. package/dist/routes/openapi.js.map +1 -0
  283. package/dist/routes/org-chart-svg.d.ts +25 -0
  284. package/dist/routes/org-chart-svg.d.ts.map +1 -0
  285. package/dist/routes/org-chart-svg.js +656 -0
  286. package/dist/routes/org-chart-svg.js.map +1 -0
  287. package/dist/routes/plugin-ui-static.d.ts +69 -0
  288. package/dist/routes/plugin-ui-static.d.ts.map +1 -0
  289. package/dist/routes/plugin-ui-static.js +411 -0
  290. package/dist/routes/plugin-ui-static.js.map +1 -0
  291. package/dist/routes/plugins.d.ts +121 -0
  292. package/dist/routes/plugins.d.ts.map +1 -0
  293. package/dist/routes/plugins.js +2390 -0
  294. package/dist/routes/plugins.js.map +1 -0
  295. package/dist/routes/projects.d.ts +3 -0
  296. package/dist/routes/projects.d.ts.map +1 -0
  297. package/dist/routes/projects.js +566 -0
  298. package/dist/routes/projects.js.map +1 -0
  299. package/dist/routes/resource-memberships.d.ts +3 -0
  300. package/dist/routes/resource-memberships.d.ts.map +1 -0
  301. package/dist/routes/resource-memberships.js +97 -0
  302. package/dist/routes/resource-memberships.js.map +1 -0
  303. package/dist/routes/routines.d.ts +6 -0
  304. package/dist/routes/routines.d.ts.map +1 -0
  305. package/dist/routes/routines.js +411 -0
  306. package/dist/routes/routines.js.map +1 -0
  307. package/dist/routes/secrets.d.ts +3 -0
  308. package/dist/routes/secrets.d.ts.map +1 -0
  309. package/dist/routes/secrets.js +419 -0
  310. package/dist/routes/secrets.js.map +1 -0
  311. package/dist/routes/sidebar-badges.d.ts +3 -0
  312. package/dist/routes/sidebar-badges.d.ts.map +1 -0
  313. package/dist/routes/sidebar-badges.js +68 -0
  314. package/dist/routes/sidebar-badges.js.map +1 -0
  315. package/dist/routes/sidebar-preferences.d.ts +3 -0
  316. package/dist/routes/sidebar-preferences.d.ts.map +1 -0
  317. package/dist/routes/sidebar-preferences.js +63 -0
  318. package/dist/routes/sidebar-preferences.js.map +1 -0
  319. package/dist/routes/squad-import-paths.d.ts +3 -0
  320. package/dist/routes/squad-import-paths.d.ts.map +1 -0
  321. package/dist/routes/squad-import-paths.js +3 -0
  322. package/dist/routes/squad-import-paths.js.map +1 -0
  323. package/dist/routes/squad-skills.d.ts +3 -0
  324. package/dist/routes/squad-skills.d.ts.map +1 -0
  325. package/dist/routes/squad-skills.js +366 -0
  326. package/dist/routes/squad-skills.js.map +1 -0
  327. package/dist/routes/squads.d.ts +4 -0
  328. package/dist/routes/squads.d.ts.map +1 -0
  329. package/dist/routes/squads.js +450 -0
  330. package/dist/routes/squads.js.map +1 -0
  331. package/dist/routes/user-profiles.d.ts +3 -0
  332. package/dist/routes/user-profiles.d.ts.map +1 -0
  333. package/dist/routes/user-profiles.js +337 -0
  334. package/dist/routes/user-profiles.js.map +1 -0
  335. package/dist/routes/workspace-command-authz.d.ts +14 -0
  336. package/dist/routes/workspace-command-authz.d.ts.map +1 -0
  337. package/dist/routes/workspace-command-authz.js +83 -0
  338. package/dist/routes/workspace-command-authz.js.map +1 -0
  339. package/dist/routes/workspace-runtime-service-authz.d.ts +12 -0
  340. package/dist/routes/workspace-runtime-service-authz.d.ts.map +1 -0
  341. package/dist/routes/workspace-runtime-service-authz.js +96 -0
  342. package/dist/routes/workspace-runtime-service-authz.js.map +1 -0
  343. package/dist/runtime-api.d.ts +19 -0
  344. package/dist/runtime-api.d.ts.map +1 -0
  345. package/dist/runtime-api.js +137 -0
  346. package/dist/runtime-api.js.map +1 -0
  347. package/dist/secrets/aws-secrets-manager-provider.d.ts +87 -0
  348. package/dist/secrets/aws-secrets-manager-provider.d.ts.map +1 -0
  349. package/dist/secrets/aws-secrets-manager-provider.js +964 -0
  350. package/dist/secrets/aws-secrets-manager-provider.js.map +1 -0
  351. package/dist/secrets/configured-provider.d.ts +3 -0
  352. package/dist/secrets/configured-provider.d.ts.map +1 -0
  353. package/dist/secrets/configured-provider.js +8 -0
  354. package/dist/secrets/configured-provider.js.map +1 -0
  355. package/dist/secrets/external-stub-providers.d.ts +5 -0
  356. package/dist/secrets/external-stub-providers.d.ts.map +1 -0
  357. package/dist/secrets/external-stub-providers.js +71 -0
  358. package/dist/secrets/external-stub-providers.js.map +1 -0
  359. package/dist/secrets/local-encrypted-provider.d.ts +3 -0
  360. package/dist/secrets/local-encrypted-provider.d.ts.map +1 -0
  361. package/dist/secrets/local-encrypted-provider.js +244 -0
  362. package/dist/secrets/local-encrypted-provider.js.map +1 -0
  363. package/dist/secrets/provider-registry.d.ts +6 -0
  364. package/dist/secrets/provider-registry.d.ts.map +1 -0
  365. package/dist/secrets/provider-registry.js +24 -0
  366. package/dist/secrets/provider-registry.js.map +1 -0
  367. package/dist/secrets/types.d.ts +138 -0
  368. package/dist/secrets/types.d.ts.map +1 -0
  369. package/dist/secrets/types.js +36 -0
  370. package/dist/secrets/types.js.map +1 -0
  371. package/dist/services/access.d.ts +184 -0
  372. package/dist/services/access.d.ts.map +1 -0
  373. package/dist/services/access.js +542 -0
  374. package/dist/services/access.js.map +1 -0
  375. package/dist/services/activity-log.d.ts +19 -0
  376. package/dist/services/activity-log.d.ts.map +1 -0
  377. package/dist/services/activity-log.js +99 -0
  378. package/dist/services/activity-log.js.map +1 -0
  379. package/dist/services/activity.d.ts +462 -0
  380. package/dist/services/activity.d.ts.map +1 -0
  381. package/dist/services/activity.js +443 -0
  382. package/dist/services/activity.js.map +1 -0
  383. package/dist/services/adapter-plugin-store.d.ts +36 -0
  384. package/dist/services/adapter-plugin-store.d.ts.map +1 -0
  385. package/dist/services/adapter-plugin-store.js +154 -0
  386. package/dist/services/adapter-plugin-store.js.map +1 -0
  387. package/dist/services/agent-instructions.d.ts +91 -0
  388. package/dist/services/agent-instructions.d.ts.map +1 -0
  389. package/dist/services/agent-instructions.js +580 -0
  390. package/dist/services/agent-instructions.js.map +1 -0
  391. package/dist/services/agent-permissions.d.ts +6 -0
  392. package/dist/services/agent-permissions.d.ts.map +1 -0
  393. package/dist/services/agent-permissions.js +20 -0
  394. package/dist/services/agent-permissions.js.map +1 -0
  395. package/dist/services/agent-start-lock.d.ts +2 -0
  396. package/dist/services/agent-start-lock.d.ts.map +1 -0
  397. package/dist/services/agent-start-lock.js +43 -0
  398. package/dist/services/agent-start-lock.js.map +1 -0
  399. package/dist/services/agents.d.ts +2253 -0
  400. package/dist/services/agents.d.ts.map +1 -0
  401. package/dist/services/agents.js +609 -0
  402. package/dist/services/agents.js.map +1 -0
  403. package/dist/services/approvals.d.ts +546 -0
  404. package/dist/services/approvals.d.ts.map +1 -0
  405. package/dist/services/approvals.js +212 -0
  406. package/dist/services/approvals.js.map +1 -0
  407. package/dist/services/assets.d.ts +33 -0
  408. package/dist/services/assets.d.ts.map +1 -0
  409. package/dist/services/assets.js +17 -0
  410. package/dist/services/assets.js.map +1 -0
  411. package/dist/services/authorization.d.ts +67 -0
  412. package/dist/services/authorization.d.ts.map +1 -0
  413. package/dist/services/authorization.js +608 -0
  414. package/dist/services/authorization.js.map +1 -0
  415. package/dist/services/botfather/authoring-lock.d.ts +17 -0
  416. package/dist/services/botfather/authoring-lock.d.ts.map +1 -0
  417. package/dist/services/botfather/authoring-lock.js +23 -0
  418. package/dist/services/botfather/authoring-lock.js.map +1 -0
  419. package/dist/services/botfather/authoring-lock.test.d.ts +2 -0
  420. package/dist/services/botfather/authoring-lock.test.d.ts.map +1 -0
  421. package/dist/services/botfather/authoring-lock.test.js +25 -0
  422. package/dist/services/botfather/authoring-lock.test.js.map +1 -0
  423. package/dist/services/botfather/client.d.ts +26 -0
  424. package/dist/services/botfather/client.d.ts.map +1 -0
  425. package/dist/services/botfather/client.js +113 -0
  426. package/dist/services/botfather/client.js.map +1 -0
  427. package/dist/services/botfather/credentials.d.ts +15 -0
  428. package/dist/services/botfather/credentials.d.ts.map +1 -0
  429. package/dist/services/botfather/credentials.js +39 -0
  430. package/dist/services/botfather/credentials.js.map +1 -0
  431. package/dist/services/botfather/enrollment.d.ts +49 -0
  432. package/dist/services/botfather/enrollment.d.ts.map +1 -0
  433. package/dist/services/botfather/enrollment.js +145 -0
  434. package/dist/services/botfather/enrollment.js.map +1 -0
  435. package/dist/services/botfather/instance-limit-enforcement.d.ts +44 -0
  436. package/dist/services/botfather/instance-limit-enforcement.d.ts.map +1 -0
  437. package/dist/services/botfather/instance-limit-enforcement.js +83 -0
  438. package/dist/services/botfather/instance-limit-enforcement.js.map +1 -0
  439. package/dist/services/botfather/instance-limit-enforcement.test.d.ts +2 -0
  440. package/dist/services/botfather/instance-limit-enforcement.test.d.ts.map +1 -0
  441. package/dist/services/botfather/instance-limit-enforcement.test.js +66 -0
  442. package/dist/services/botfather/instance-limit-enforcement.test.js.map +1 -0
  443. package/dist/services/botfather/limits-store.d.ts +36 -0
  444. package/dist/services/botfather/limits-store.d.ts.map +1 -0
  445. package/dist/services/botfather/limits-store.js +94 -0
  446. package/dist/services/botfather/limits-store.js.map +1 -0
  447. package/dist/services/botfather/limits-store.test.d.ts +2 -0
  448. package/dist/services/botfather/limits-store.test.d.ts.map +1 -0
  449. package/dist/services/botfather/limits-store.test.js +70 -0
  450. package/dist/services/botfather/limits-store.test.js.map +1 -0
  451. package/dist/services/botfather/reporter.d.ts +41 -0
  452. package/dist/services/botfather/reporter.d.ts.map +1 -0
  453. package/dist/services/botfather/reporter.js +448 -0
  454. package/dist/services/botfather/reporter.js.map +1 -0
  455. package/dist/services/botfather/service.d.ts +84 -0
  456. package/dist/services/botfather/service.d.ts.map +1 -0
  457. package/dist/services/botfather/service.js +229 -0
  458. package/dist/services/botfather/service.js.map +1 -0
  459. package/dist/services/botfather/service.test.d.ts +2 -0
  460. package/dist/services/botfather/service.test.d.ts.map +1 -0
  461. package/dist/services/botfather/service.test.js +120 -0
  462. package/dist/services/botfather/service.test.js.map +1 -0
  463. package/dist/services/botfather/skill-catalog.d.ts +28 -0
  464. package/dist/services/botfather/skill-catalog.d.ts.map +1 -0
  465. package/dist/services/botfather/skill-catalog.js +101 -0
  466. package/dist/services/botfather/skill-catalog.js.map +1 -0
  467. package/dist/services/botfather/skill-catalog.test.d.ts +2 -0
  468. package/dist/services/botfather/skill-catalog.test.d.ts.map +1 -0
  469. package/dist/services/botfather/skill-catalog.test.js +151 -0
  470. package/dist/services/botfather/skill-catalog.test.js.map +1 -0
  471. package/dist/services/budgets.d.ts +38 -0
  472. package/dist/services/budgets.d.ts.map +1 -0
  473. package/dist/services/budgets.js +833 -0
  474. package/dist/services/budgets.js.map +1 -0
  475. package/dist/services/catalog-provenance.d.ts +7 -0
  476. package/dist/services/catalog-provenance.d.ts.map +1 -0
  477. package/dist/services/catalog-provenance.js +64 -0
  478. package/dist/services/catalog-provenance.js.map +1 -0
  479. package/dist/services/cloud-upstreams.d.ts +42 -0
  480. package/dist/services/cloud-upstreams.d.ts.map +1 -0
  481. package/dist/services/cloud-upstreams.js +1071 -0
  482. package/dist/services/cloud-upstreams.js.map +1 -0
  483. package/dist/services/costs.d.ts +127 -0
  484. package/dist/services/costs.d.ts.map +1 -0
  485. package/dist/services/costs.js +409 -0
  486. package/dist/services/costs.js.map +1 -0
  487. package/dist/services/cron.d.ts +80 -0
  488. package/dist/services/cron.d.ts.map +1 -0
  489. package/dist/services/cron.js +300 -0
  490. package/dist/services/cron.js.map +1 -0
  491. package/dist/services/dashboard.d.ts +34 -0
  492. package/dist/services/dashboard.d.ts.map +1 -0
  493. package/dist/services/dashboard.js +142 -0
  494. package/dist/services/dashboard.js.map +1 -0
  495. package/dist/services/default-agent-instructions.d.ts +9 -0
  496. package/dist/services/default-agent-instructions.d.ts.map +1 -0
  497. package/dist/services/default-agent-instructions.js +20 -0
  498. package/dist/services/default-agent-instructions.js.map +1 -0
  499. package/dist/services/document-annotations.d.ts +160 -0
  500. package/dist/services/document-annotations.d.ts.map +1 -0
  501. package/dist/services/document-annotations.js +324 -0
  502. package/dist/services/document-annotations.js.map +1 -0
  503. package/dist/services/documents.d.ts +347 -0
  504. package/dist/services/documents.d.ts.map +1 -0
  505. package/dist/services/documents.js +638 -0
  506. package/dist/services/documents.js.map +1 -0
  507. package/dist/services/environment-config.d.ts +55 -0
  508. package/dist/services/environment-config.d.ts.map +1 -0
  509. package/dist/services/environment-config.js +441 -0
  510. package/dist/services/environment-config.js.map +1 -0
  511. package/dist/services/environment-execution-target.d.ts +21 -0
  512. package/dist/services/environment-execution-target.d.ts.map +1 -0
  513. package/dist/services/environment-execution-target.js +121 -0
  514. package/dist/services/environment-execution-target.js.map +1 -0
  515. package/dist/services/environment-probe.d.ts +9 -0
  516. package/dist/services/environment-probe.d.ts.map +1 -0
  517. package/dist/services/environment-probe.js +106 -0
  518. package/dist/services/environment-probe.js.map +1 -0
  519. package/dist/services/environment-run-orchestrator.d.ts +124 -0
  520. package/dist/services/environment-run-orchestrator.d.ts.map +1 -0
  521. package/dist/services/environment-run-orchestrator.js +392 -0
  522. package/dist/services/environment-run-orchestrator.js.map +1 -0
  523. package/dist/services/environment-runtime.d.ts +90 -0
  524. package/dist/services/environment-runtime.d.ts.map +1 -0
  525. package/dist/services/environment-runtime.js +968 -0
  526. package/dist/services/environment-runtime.js.map +1 -0
  527. package/dist/services/environments.d.ts +36 -0
  528. package/dist/services/environments.d.ts.map +1 -0
  529. package/dist/services/environments.js +260 -0
  530. package/dist/services/environments.js.map +1 -0
  531. package/dist/services/execution-workspace-policy.d.ts +42 -0
  532. package/dist/services/execution-workspace-policy.d.ts.map +1 -0
  533. package/dist/services/execution-workspace-policy.js +262 -0
  534. package/dist/services/execution-workspace-policy.js.map +1 -0
  535. package/dist/services/execution-workspaces.d.ts +30 -0
  536. package/dist/services/execution-workspaces.d.ts.map +1 -0
  537. package/dist/services/execution-workspaces.js +645 -0
  538. package/dist/services/execution-workspaces.js.map +1 -0
  539. package/dist/services/finance.d.ts +93 -0
  540. package/dist/services/finance.d.ts.map +1 -0
  541. package/dist/services/finance.js +120 -0
  542. package/dist/services/finance.js.map +1 -0
  543. package/dist/services/github-fetch.d.ts +4 -0
  544. package/dist/services/github-fetch.d.ts.map +1 -0
  545. package/dist/services/github-fetch.js +23 -0
  546. package/dist/services/github-fetch.js.map +1 -0
  547. package/dist/services/goals.d.ts +433 -0
  548. package/dist/services/goals.d.ts.map +1 -0
  549. package/dist/services/goals.js +54 -0
  550. package/dist/services/goals.js.map +1 -0
  551. package/dist/services/heartbeat-circuit-breaker.d.ts +89 -0
  552. package/dist/services/heartbeat-circuit-breaker.d.ts.map +1 -0
  553. package/dist/services/heartbeat-circuit-breaker.js +156 -0
  554. package/dist/services/heartbeat-circuit-breaker.js.map +1 -0
  555. package/dist/services/heartbeat-circuit-breaker.test.d.ts +2 -0
  556. package/dist/services/heartbeat-circuit-breaker.test.d.ts.map +1 -0
  557. package/dist/services/heartbeat-circuit-breaker.test.js +97 -0
  558. package/dist/services/heartbeat-circuit-breaker.test.js.map +1 -0
  559. package/dist/services/heartbeat-run-summary.d.ts +7 -0
  560. package/dist/services/heartbeat-run-summary.d.ts.map +1 -0
  561. package/dist/services/heartbeat-run-summary.js +84 -0
  562. package/dist/services/heartbeat-run-summary.js.map +1 -0
  563. package/dist/services/heartbeat-stop-metadata.d.ts +28 -0
  564. package/dist/services/heartbeat-stop-metadata.d.ts.map +1 -0
  565. package/dist/services/heartbeat-stop-metadata.js +86 -0
  566. package/dist/services/heartbeat-stop-metadata.js.map +1 -0
  567. package/dist/services/heartbeat-stop-metadata.test.d.ts +2 -0
  568. package/dist/services/heartbeat-stop-metadata.test.d.ts.map +1 -0
  569. package/dist/services/heartbeat-stop-metadata.test.js +93 -0
  570. package/dist/services/heartbeat-stop-metadata.test.js.map +1 -0
  571. package/dist/services/heartbeat.d.ts +1578 -0
  572. package/dist/services/heartbeat.d.ts.map +1 -0
  573. package/dist/services/heartbeat.js +8274 -0
  574. package/dist/services/heartbeat.js.map +1 -0
  575. package/dist/services/hire-hook.d.ts +14 -0
  576. package/dist/services/hire-hook.d.ts.map +1 -0
  577. package/dist/services/hire-hook.js +85 -0
  578. package/dist/services/hire-hook.js.map +1 -0
  579. package/dist/services/inbox-dismissals.d.ts +22 -0
  580. package/dist/services/inbox-dismissals.d.ts.map +1 -0
  581. package/dist/services/inbox-dismissals.js +33 -0
  582. package/dist/services/inbox-dismissals.js.map +1 -0
  583. package/dist/services/index.d.ts +50 -0
  584. package/dist/services/index.d.ts.map +1 -0
  585. package/dist/services/index.js +49 -0
  586. package/dist/services/index.js.map +1 -0
  587. package/dist/services/instance-settings.d.ts +12 -0
  588. package/dist/services/instance-settings.d.ts.map +1 -0
  589. package/dist/services/instance-settings.js +142 -0
  590. package/dist/services/instance-settings.js.map +1 -0
  591. package/dist/services/invite-grants.d.ts +15 -0
  592. package/dist/services/invite-grants.d.ts.map +1 -0
  593. package/dist/services/invite-grants.js +50 -0
  594. package/dist/services/invite-grants.js.map +1 -0
  595. package/dist/services/issue-approvals.d.ts +56 -0
  596. package/dist/services/issue-approvals.d.ts.map +1 -0
  597. package/dist/services/issue-approvals.js +153 -0
  598. package/dist/services/issue-approvals.js.map +1 -0
  599. package/dist/services/issue-assignment-wakeup.d.ts +29 -0
  600. package/dist/services/issue-assignment-wakeup.d.ts.map +1 -0
  601. package/dist/services/issue-assignment-wakeup.js +22 -0
  602. package/dist/services/issue-assignment-wakeup.js.map +1 -0
  603. package/dist/services/issue-continuation-summary.d.ts +71 -0
  604. package/dist/services/issue-continuation-summary.d.ts.map +1 -0
  605. package/dist/services/issue-continuation-summary.js +222 -0
  606. package/dist/services/issue-continuation-summary.js.map +1 -0
  607. package/dist/services/issue-execution-policy.d.ts +93 -0
  608. package/dist/services/issue-execution-policy.d.ts.map +1 -0
  609. package/dist/services/issue-execution-policy.js +838 -0
  610. package/dist/services/issue-execution-policy.js.map +1 -0
  611. package/dist/services/issue-goal-fallback.d.ts +18 -0
  612. package/dist/services/issue-goal-fallback.d.ts.map +1 -0
  613. package/dist/services/issue-goal-fallback.js +33 -0
  614. package/dist/services/issue-goal-fallback.js.map +1 -0
  615. package/dist/services/issue-liveness.d.ts +3 -0
  616. package/dist/services/issue-liveness.d.ts.map +1 -0
  617. package/dist/services/issue-liveness.js +2 -0
  618. package/dist/services/issue-liveness.js.map +1 -0
  619. package/dist/services/issue-recovery-actions.d.ts +40 -0
  620. package/dist/services/issue-recovery-actions.d.ts.map +1 -0
  621. package/dist/services/issue-recovery-actions.js +204 -0
  622. package/dist/services/issue-recovery-actions.js.map +1 -0
  623. package/dist/services/issue-references.d.ts +22 -0
  624. package/dist/services/issue-references.d.ts.map +1 -0
  625. package/dist/services/issue-references.js +341 -0
  626. package/dist/services/issue-references.js.map +1 -0
  627. package/dist/services/issue-thread-interactions.d.ts +81 -0
  628. package/dist/services/issue-thread-interactions.d.ts.map +1 -0
  629. package/dist/services/issue-thread-interactions.js +1017 -0
  630. package/dist/services/issue-thread-interactions.js.map +1 -0
  631. package/dist/services/issue-thread-interactions.test.d.ts +2 -0
  632. package/dist/services/issue-thread-interactions.test.d.ts.map +1 -0
  633. package/dist/services/issue-thread-interactions.test.js +195 -0
  634. package/dist/services/issue-thread-interactions.test.js.map +1 -0
  635. package/dist/services/issue-tree-control.d.ts +89 -0
  636. package/dist/services/issue-tree-control.d.ts.map +1 -0
  637. package/dist/services/issue-tree-control.js +933 -0
  638. package/dist/services/issue-tree-control.js.map +1 -0
  639. package/dist/services/issues.d.ts +898 -0
  640. package/dist/services/issues.d.ts.map +1 -0
  641. package/dist/services/issues.js +4705 -0
  642. package/dist/services/issues.js.map +1 -0
  643. package/dist/services/json-schema-secret-refs.d.ts +5 -0
  644. package/dist/services/json-schema-secret-refs.d.ts.map +1 -0
  645. package/dist/services/json-schema-secret-refs.js +67 -0
  646. package/dist/services/json-schema-secret-refs.js.map +1 -0
  647. package/dist/services/live-events.d.ts +17 -0
  648. package/dist/services/live-events.d.ts.map +1 -0
  649. package/dist/services/live-events.js +33 -0
  650. package/dist/services/live-events.js.map +1 -0
  651. package/dist/services/local-service-supervisor.d.ts +56 -0
  652. package/dist/services/local-service-supervisor.d.ts.map +1 -0
  653. package/dist/services/local-service-supervisor.js +284 -0
  654. package/dist/services/local-service-supervisor.js.map +1 -0
  655. package/dist/services/operator-auth.d.ts +271 -0
  656. package/dist/services/operator-auth.d.ts.map +1 -0
  657. package/dist/services/operator-auth.js +361 -0
  658. package/dist/services/operator-auth.js.map +1 -0
  659. package/dist/services/plugin-capability-validator.d.ts +108 -0
  660. package/dist/services/plugin-capability-validator.d.ts.map +1 -0
  661. package/dist/services/plugin-capability-validator.js +314 -0
  662. package/dist/services/plugin-capability-validator.js.map +1 -0
  663. package/dist/services/plugin-config-validator.d.ts +26 -0
  664. package/dist/services/plugin-config-validator.d.ts.map +1 -0
  665. package/dist/services/plugin-config-validator.js +41 -0
  666. package/dist/services/plugin-config-validator.js.map +1 -0
  667. package/dist/services/plugin-database.d.ts +49 -0
  668. package/dist/services/plugin-database.d.ts.map +1 -0
  669. package/dist/services/plugin-database.js +475 -0
  670. package/dist/services/plugin-database.js.map +1 -0
  671. package/dist/services/plugin-dev-watcher.d.ts +30 -0
  672. package/dist/services/plugin-dev-watcher.d.ts.map +1 -0
  673. package/dist/services/plugin-dev-watcher.js +246 -0
  674. package/dist/services/plugin-dev-watcher.js.map +1 -0
  675. package/dist/services/plugin-environment-driver.d.ts +126 -0
  676. package/dist/services/plugin-environment-driver.d.ts.map +1 -0
  677. package/dist/services/plugin-environment-driver.js +226 -0
  678. package/dist/services/plugin-environment-driver.js.map +1 -0
  679. package/dist/services/plugin-event-bus.d.ts +149 -0
  680. package/dist/services/plugin-event-bus.d.ts.map +1 -0
  681. package/dist/services/plugin-event-bus.js +258 -0
  682. package/dist/services/plugin-event-bus.js.map +1 -0
  683. package/dist/services/plugin-host-service-cleanup.d.ts +14 -0
  684. package/dist/services/plugin-host-service-cleanup.d.ts.map +1 -0
  685. package/dist/services/plugin-host-service-cleanup.js +37 -0
  686. package/dist/services/plugin-host-service-cleanup.js.map +1 -0
  687. package/dist/services/plugin-host-services.d.ts +17 -0
  688. package/dist/services/plugin-host-services.d.ts.map +1 -0
  689. package/dist/services/plugin-host-services.js +2460 -0
  690. package/dist/services/plugin-host-services.js.map +1 -0
  691. package/dist/services/plugin-job-coordinator.d.ts +81 -0
  692. package/dist/services/plugin-job-coordinator.d.ts.map +1 -0
  693. package/dist/services/plugin-job-coordinator.js +172 -0
  694. package/dist/services/plugin-job-coordinator.js.map +1 -0
  695. package/dist/services/plugin-job-scheduler.d.ts +163 -0
  696. package/dist/services/plugin-job-scheduler.d.ts.map +1 -0
  697. package/dist/services/plugin-job-scheduler.js +454 -0
  698. package/dist/services/plugin-job-scheduler.js.map +1 -0
  699. package/dist/services/plugin-job-store.d.ts +208 -0
  700. package/dist/services/plugin-job-store.d.ts.map +1 -0
  701. package/dist/services/plugin-job-store.js +350 -0
  702. package/dist/services/plugin-job-store.js.map +1 -0
  703. package/dist/services/plugin-lifecycle.d.ts +203 -0
  704. package/dist/services/plugin-lifecycle.d.ts.map +1 -0
  705. package/dist/services/plugin-lifecycle.js +501 -0
  706. package/dist/services/plugin-lifecycle.js.map +1 -0
  707. package/dist/services/plugin-loader.d.ts +453 -0
  708. package/dist/services/plugin-loader.d.ts.map +1 -0
  709. package/dist/services/plugin-loader.js +1295 -0
  710. package/dist/services/plugin-loader.js.map +1 -0
  711. package/dist/services/plugin-local-folders.d.ts +49 -0
  712. package/dist/services/plugin-local-folders.d.ts.map +1 -0
  713. package/dist/services/plugin-local-folders.js +510 -0
  714. package/dist/services/plugin-local-folders.js.map +1 -0
  715. package/dist/services/plugin-log-retention.d.ts +20 -0
  716. package/dist/services/plugin-log-retention.d.ts.map +1 -0
  717. package/dist/services/plugin-log-retention.js +63 -0
  718. package/dist/services/plugin-log-retention.js.map +1 -0
  719. package/dist/services/plugin-managed-agents.d.ts +15 -0
  720. package/dist/services/plugin-managed-agents.d.ts.map +1 -0
  721. package/dist/services/plugin-managed-agents.js +457 -0
  722. package/dist/services/plugin-managed-agents.js.map +1 -0
  723. package/dist/services/plugin-managed-routines.d.ts +42 -0
  724. package/dist/services/plugin-managed-routines.d.ts.map +1 -0
  725. package/dist/services/plugin-managed-routines.js +416 -0
  726. package/dist/services/plugin-managed-routines.js.map +1 -0
  727. package/dist/services/plugin-managed-skills.d.ts +14 -0
  728. package/dist/services/plugin-managed-skills.d.ts.map +1 -0
  729. package/dist/services/plugin-managed-skills.js +264 -0
  730. package/dist/services/plugin-managed-skills.js.map +1 -0
  731. package/dist/services/plugin-manifest-validator.d.ts +79 -0
  732. package/dist/services/plugin-manifest-validator.d.ts.map +1 -0
  733. package/dist/services/plugin-manifest-validator.js +84 -0
  734. package/dist/services/plugin-manifest-validator.js.map +1 -0
  735. package/dist/services/plugin-registry.d.ts +2550 -0
  736. package/dist/services/plugin-registry.d.ts.map +1 -0
  737. package/dist/services/plugin-registry.js +581 -0
  738. package/dist/services/plugin-registry.js.map +1 -0
  739. package/dist/services/plugin-runtime-sandbox.d.ts +40 -0
  740. package/dist/services/plugin-runtime-sandbox.d.ts.map +1 -0
  741. package/dist/services/plugin-runtime-sandbox.js +154 -0
  742. package/dist/services/plugin-runtime-sandbox.js.map +1 -0
  743. package/dist/services/plugin-secrets-handler.d.ts +83 -0
  744. package/dist/services/plugin-secrets-handler.d.ts.map +1 -0
  745. package/dist/services/plugin-secrets-handler.js +168 -0
  746. package/dist/services/plugin-secrets-handler.js.map +1 -0
  747. package/dist/services/plugin-state-store.d.ts +92 -0
  748. package/dist/services/plugin-state-store.d.ts.map +1 -0
  749. package/dist/services/plugin-state-store.js +190 -0
  750. package/dist/services/plugin-state-store.js.map +1 -0
  751. package/dist/services/plugin-stream-bus.d.ts +29 -0
  752. package/dist/services/plugin-stream-bus.d.ts.map +1 -0
  753. package/dist/services/plugin-stream-bus.js +48 -0
  754. package/dist/services/plugin-stream-bus.js.map +1 -0
  755. package/dist/services/plugin-tool-dispatcher.d.ts +181 -0
  756. package/dist/services/plugin-tool-dispatcher.d.ts.map +1 -0
  757. package/dist/services/plugin-tool-dispatcher.js +224 -0
  758. package/dist/services/plugin-tool-dispatcher.js.map +1 -0
  759. package/dist/services/plugin-tool-registry.d.ts +192 -0
  760. package/dist/services/plugin-tool-registry.d.ts.map +1 -0
  761. package/dist/services/plugin-tool-registry.js +224 -0
  762. package/dist/services/plugin-tool-registry.js.map +1 -0
  763. package/dist/services/plugin-worker-manager.d.ts +262 -0
  764. package/dist/services/plugin-worker-manager.d.ts.map +1 -0
  765. package/dist/services/plugin-worker-manager.js +942 -0
  766. package/dist/services/plugin-worker-manager.js.map +1 -0
  767. package/dist/services/portable-path.d.ts +2 -0
  768. package/dist/services/portable-path.d.ts.map +1 -0
  769. package/dist/services/portable-path.js +15 -0
  770. package/dist/services/portable-path.js.map +1 -0
  771. package/dist/services/principal-access-compatibility.d.ts +26 -0
  772. package/dist/services/principal-access-compatibility.d.ts.map +1 -0
  773. package/dist/services/principal-access-compatibility.js +94 -0
  774. package/dist/services/principal-access-compatibility.js.map +1 -0
  775. package/dist/services/productivity-review.d.ts +83 -0
  776. package/dist/services/productivity-review.d.ts.map +1 -0
  777. package/dist/services/productivity-review.js +652 -0
  778. package/dist/services/productivity-review.js.map +1 -0
  779. package/dist/services/project-workspace-runtime-config.d.ts +4 -0
  780. package/dist/services/project-workspace-runtime-config.d.ts.map +1 -0
  781. package/dist/services/project-workspace-runtime-config.js +54 -0
  782. package/dist/services/project-workspace-runtime-config.js.map +1 -0
  783. package/dist/services/projects.d.ts +99 -0
  784. package/dist/services/projects.d.ts.map +1 -0
  785. package/dist/services/projects.js +879 -0
  786. package/dist/services/projects.js.map +1 -0
  787. package/dist/services/quota-windows.d.ts +9 -0
  788. package/dist/services/quota-windows.d.ts.map +1 -0
  789. package/dist/services/quota-windows.js +56 -0
  790. package/dist/services/quota-windows.js.map +1 -0
  791. package/dist/services/recovery/index.d.ts +10 -0
  792. package/dist/services/recovery/index.d.ts.map +1 -0
  793. package/dist/services/recovery/index.js +6 -0
  794. package/dist/services/recovery/index.js.map +1 -0
  795. package/dist/services/recovery/issue-graph-liveness.d.ts +85 -0
  796. package/dist/services/recovery/issue-graph-liveness.d.ts.map +1 -0
  797. package/dist/services/recovery/issue-graph-liveness.js +356 -0
  798. package/dist/services/recovery/issue-graph-liveness.js.map +1 -0
  799. package/dist/services/recovery/model-profile-hint.d.ts +21 -0
  800. package/dist/services/recovery/model-profile-hint.d.ts.map +1 -0
  801. package/dist/services/recovery/model-profile-hint.js +36 -0
  802. package/dist/services/recovery/model-profile-hint.js.map +1 -0
  803. package/dist/services/recovery/model-profile-hint.test.d.ts +2 -0
  804. package/dist/services/recovery/model-profile-hint.test.d.ts.map +1 -0
  805. package/dist/services/recovery/model-profile-hint.test.js +38 -0
  806. package/dist/services/recovery/model-profile-hint.test.js.map +1 -0
  807. package/dist/services/recovery/origins.d.ts +36 -0
  808. package/dist/services/recovery/origins.d.ts.map +1 -0
  809. package/dist/services/recovery/origins.js +45 -0
  810. package/dist/services/recovery/origins.js.map +1 -0
  811. package/dist/services/recovery/pause-hold-guard.d.ts +6 -0
  812. package/dist/services/recovery/pause-hold-guard.d.ts.map +1 -0
  813. package/dist/services/recovery/pause-hold-guard.js +6 -0
  814. package/dist/services/recovery/pause-hold-guard.js.map +1 -0
  815. package/dist/services/recovery/run-liveness-continuations.d.ts +50 -0
  816. package/dist/services/recovery/run-liveness-continuations.d.ts.map +1 -0
  817. package/dist/services/recovery/run-liveness-continuations.js +117 -0
  818. package/dist/services/recovery/run-liveness-continuations.js.map +1 -0
  819. package/dist/services/recovery/service.d.ts +258 -0
  820. package/dist/services/recovery/service.d.ts.map +1 -0
  821. package/dist/services/recovery/service.js +2892 -0
  822. package/dist/services/recovery/service.js.map +1 -0
  823. package/dist/services/recovery/successful-run-handoff.d.ts +89 -0
  824. package/dist/services/recovery/successful-run-handoff.d.ts.map +1 -0
  825. package/dist/services/recovery/successful-run-handoff.js +304 -0
  826. package/dist/services/recovery/successful-run-handoff.js.map +1 -0
  827. package/dist/services/recovery/successful-run-handoff.test.d.ts +2 -0
  828. package/dist/services/recovery/successful-run-handoff.test.d.ts.map +1 -0
  829. package/dist/services/recovery/successful-run-handoff.test.js +276 -0
  830. package/dist/services/recovery/successful-run-handoff.test.js.map +1 -0
  831. package/dist/services/resource-memberships.d.ts +55 -0
  832. package/dist/services/resource-memberships.d.ts.map +1 -0
  833. package/dist/services/resource-memberships.js +213 -0
  834. package/dist/services/resource-memberships.js.map +1 -0
  835. package/dist/services/routines.d.ts +170 -0
  836. package/dist/services/routines.d.ts.map +1 -0
  837. package/dist/services/routines.js +2015 -0
  838. package/dist/services/routines.js.map +1 -0
  839. package/dist/services/run-continuations.d.ts +3 -0
  840. package/dist/services/run-continuations.d.ts.map +1 -0
  841. package/dist/services/run-continuations.js +2 -0
  842. package/dist/services/run-continuations.js.map +1 -0
  843. package/dist/services/run-liveness.d.ts +46 -0
  844. package/dist/services/run-liveness.d.ts.map +1 -0
  845. package/dist/services/run-liveness.js +275 -0
  846. package/dist/services/run-liveness.js.map +1 -0
  847. package/dist/services/run-log-store.d.ts +34 -0
  848. package/dist/services/run-log-store.d.ts.map +1 -0
  849. package/dist/services/run-log-store.js +111 -0
  850. package/dist/services/run-log-store.js.map +1 -0
  851. package/dist/services/sandbox-provider-runtime.d.ts +132 -0
  852. package/dist/services/sandbox-provider-runtime.d.ts.map +1 -0
  853. package/dist/services/sandbox-provider-runtime.js +216 -0
  854. package/dist/services/sandbox-provider-runtime.js.map +1 -0
  855. package/dist/services/secrets.d.ts +1991 -0
  856. package/dist/services/secrets.d.ts.map +1 -0
  857. package/dist/services/secrets.js +1781 -0
  858. package/dist/services/secrets.js.map +1 -0
  859. package/dist/services/session-workspace-cwd.d.ts +2 -0
  860. package/dist/services/session-workspace-cwd.d.ts.map +1 -0
  861. package/dist/services/session-workspace-cwd.js +24 -0
  862. package/dist/services/session-workspace-cwd.js.map +1 -0
  863. package/dist/services/session-workspace-cwd.test.d.ts +2 -0
  864. package/dist/services/session-workspace-cwd.test.d.ts.map +1 -0
  865. package/dist/services/session-workspace-cwd.test.js +25 -0
  866. package/dist/services/session-workspace-cwd.test.js.map +1 -0
  867. package/dist/services/sidebar-badges.d.ts +14 -0
  868. package/dist/services/sidebar-badges.d.ts.map +1 -0
  869. package/dist/services/sidebar-badges.js +48 -0
  870. package/dist/services/sidebar-badges.js.map +1 -0
  871. package/dist/services/sidebar-preferences.d.ts +9 -0
  872. package/dist/services/sidebar-preferences.d.ts.map +1 -0
  873. package/dist/services/sidebar-preferences.js +82 -0
  874. package/dist/services/sidebar-preferences.js.map +1 -0
  875. package/dist/services/skills-catalog.d.ts +14 -0
  876. package/dist/services/skills-catalog.d.ts.map +1 -0
  877. package/dist/services/skills-catalog.js +171 -0
  878. package/dist/services/skills-catalog.js.map +1 -0
  879. package/dist/services/squad-export-readme.d.ts +17 -0
  880. package/dist/services/squad-export-readme.d.ts.map +1 -0
  881. package/dist/services/squad-export-readme.js +148 -0
  882. package/dist/services/squad-export-readme.js.map +1 -0
  883. package/dist/services/squad-member-roles.d.ts +9 -0
  884. package/dist/services/squad-member-roles.d.ts.map +1 -0
  885. package/dist/services/squad-member-roles.js +48 -0
  886. package/dist/services/squad-member-roles.js.map +1 -0
  887. package/dist/services/squad-portability.d.ts +24 -0
  888. package/dist/services/squad-portability.d.ts.map +1 -0
  889. package/dist/services/squad-portability.js +4093 -0
  890. package/dist/services/squad-portability.js.map +1 -0
  891. package/dist/services/squad-search-rate-limit.d.ts +22 -0
  892. package/dist/services/squad-search-rate-limit.d.ts.map +1 -0
  893. package/dist/services/squad-search-rate-limit.js +38 -0
  894. package/dist/services/squad-search-rate-limit.js.map +1 -0
  895. package/dist/services/squad-search.d.ts +8 -0
  896. package/dist/services/squad-search.d.ts.map +1 -0
  897. package/dist/services/squad-search.js +626 -0
  898. package/dist/services/squad-search.js.map +1 -0
  899. package/dist/services/squad-skills.d.ts +107 -0
  900. package/dist/services/squad-skills.d.ts.map +1 -0
  901. package/dist/services/squad-skills.js +3044 -0
  902. package/dist/services/squad-skills.js.map +1 -0
  903. package/dist/services/squads.d.ts +154 -0
  904. package/dist/services/squads.d.ts.map +1 -0
  905. package/dist/services/squads.js +278 -0
  906. package/dist/services/squads.js.map +1 -0
  907. package/dist/services/wake-cycle-guard.d.ts +44 -0
  908. package/dist/services/wake-cycle-guard.d.ts.map +1 -0
  909. package/dist/services/wake-cycle-guard.js +79 -0
  910. package/dist/services/wake-cycle-guard.js.map +1 -0
  911. package/dist/services/wake-cycle-guard.test.d.ts +2 -0
  912. package/dist/services/wake-cycle-guard.test.d.ts.map +1 -0
  913. package/dist/services/wake-cycle-guard.test.js +67 -0
  914. package/dist/services/wake-cycle-guard.test.js.map +1 -0
  915. package/dist/services/work-products.d.ts +14 -0
  916. package/dist/services/work-products.d.ts.map +1 -0
  917. package/dist/services/work-products.js +100 -0
  918. package/dist/services/work-products.js.map +1 -0
  919. package/dist/services/workspace-operation-log-store.d.ts +33 -0
  920. package/dist/services/workspace-operation-log-store.d.ts.map +1 -0
  921. package/dist/services/workspace-operation-log-store.js +110 -0
  922. package/dist/services/workspace-operation-log-store.js.map +1 -0
  923. package/dist/services/workspace-operations.d.ts +44 -0
  924. package/dist/services/workspace-operations.d.ts.map +1 -0
  925. package/dist/services/workspace-operations.js +211 -0
  926. package/dist/services/workspace-operations.js.map +1 -0
  927. package/dist/services/workspace-realization.d.ts +33 -0
  928. package/dist/services/workspace-realization.d.ts.map +1 -0
  929. package/dist/services/workspace-realization.js +221 -0
  930. package/dist/services/workspace-realization.js.map +1 -0
  931. package/dist/services/workspace-runtime-read-model.d.ts +92 -0
  932. package/dist/services/workspace-runtime-read-model.d.ts.map +1 -0
  933. package/dist/services/workspace-runtime-read-model.js +67 -0
  934. package/dist/services/workspace-runtime-read-model.js.map +1 -0
  935. package/dist/services/workspace-runtime.d.ts +252 -0
  936. package/dist/services/workspace-runtime.d.ts.map +1 -0
  937. package/dist/services/workspace-runtime.js +2519 -0
  938. package/dist/services/workspace-runtime.js.map +1 -0
  939. package/dist/startup-banner.d.ts +32 -0
  940. package/dist/startup-banner.d.ts.map +1 -0
  941. package/dist/startup-banner.js +118 -0
  942. package/dist/startup-banner.js.map +1 -0
  943. package/dist/static-index-html.d.ts +2 -0
  944. package/dist/static-index-html.d.ts.map +1 -0
  945. package/dist/static-index-html.js +7 -0
  946. package/dist/static-index-html.js.map +1 -0
  947. package/dist/storage/index.d.ts +6 -0
  948. package/dist/storage/index.d.ts.map +1 -0
  949. package/dist/storage/index.js +29 -0
  950. package/dist/storage/index.js.map +1 -0
  951. package/dist/storage/local-disk-provider.d.ts +3 -0
  952. package/dist/storage/local-disk-provider.d.ts.map +1 -0
  953. package/dist/storage/local-disk-provider.js +85 -0
  954. package/dist/storage/local-disk-provider.js.map +1 -0
  955. package/dist/storage/provider-registry.d.ts +4 -0
  956. package/dist/storage/provider-registry.d.ts.map +1 -0
  957. package/dist/storage/provider-registry.js +15 -0
  958. package/dist/storage/provider-registry.js.map +1 -0
  959. package/dist/storage/s3-provider.d.ts +11 -0
  960. package/dist/storage/s3-provider.d.ts.map +1 -0
  961. package/dist/storage/s3-provider.js +124 -0
  962. package/dist/storage/s3-provider.js.map +1 -0
  963. package/dist/storage/service.d.ts +3 -0
  964. package/dist/storage/service.d.ts.map +1 -0
  965. package/dist/storage/service.js +120 -0
  966. package/dist/storage/service.js.map +1 -0
  967. package/dist/storage/types.d.ts +59 -0
  968. package/dist/storage/types.d.ts.map +1 -0
  969. package/dist/storage/types.js +2 -0
  970. package/dist/storage/types.js.map +1 -0
  971. package/dist/ui-branding.d.ts +13 -0
  972. package/dist/ui-branding.d.ts.map +1 -0
  973. package/dist/ui-branding.js +187 -0
  974. package/dist/ui-branding.js.map +1 -0
  975. package/dist/version.d.ts +2 -0
  976. package/dist/version.d.ts.map +1 -0
  977. package/dist/version.js +5 -0
  978. package/dist/version.js.map +1 -0
  979. package/dist/vite-html-renderer.d.ts +18 -0
  980. package/dist/vite-html-renderer.d.ts.map +1 -0
  981. package/dist/vite-html-renderer.js +61 -0
  982. package/dist/vite-html-renderer.js.map +1 -0
  983. package/dist/worktree-config.d.ts +19 -0
  984. package/dist/worktree-config.d.ts.map +1 -0
  985. package/dist/worktree-config.js +373 -0
  986. package/dist/worktree-config.js.map +1 -0
  987. package/package.json +92 -0
  988. package/skills/diagnose-why-work-stopped/SKILL.md +161 -0
  989. package/skills/para-memory-files/SKILL.md +104 -0
  990. package/skills/para-memory-files/references/schemas.md +35 -0
  991. package/skills/slaw/SKILL.md +371 -0
  992. package/skills/slaw/references/api-reference.md +879 -0
  993. package/skills/slaw/references/artifacts.md +44 -0
  994. package/skills/slaw/references/issue-workspaces.md +80 -0
  995. package/skills/slaw/references/routines.md +187 -0
  996. package/skills/slaw/references/squad-skills.md +258 -0
  997. package/skills/slaw/references/workflows.md +113 -0
  998. package/skills/slaw/scripts/slaw-upload-artifact.sh +371 -0
  999. package/skills/slaw-converting-plans-to-tasks/SKILL.md +42 -0
  1000. package/skills/slaw-create-agent/SKILL.md +163 -0
  1001. package/skills/slaw-create-agent/references/agent-instruction-templates.md +123 -0
  1002. package/skills/slaw-create-agent/references/agents/coder.md +64 -0
  1003. package/skills/slaw-create-agent/references/agents/qa.md +88 -0
  1004. package/skills/slaw-create-agent/references/agents/securityengineer.md +135 -0
  1005. package/skills/slaw-create-agent/references/agents/uxdesigner.md +115 -0
  1006. package/skills/slaw-create-agent/references/api-reference.md +110 -0
  1007. package/skills/slaw-create-agent/references/baseline-role-guide.md +168 -0
  1008. package/skills/slaw-create-agent/references/draft-review-checklist.md +95 -0
  1009. package/skills/slaw-create-plugin/SKILL.md +154 -0
  1010. package/skills/slaw-dev/SKILL.md +267 -0
  1011. package/skills/terminal-bench-loop/SKILL.md +236 -0
  1012. package/ui-dist/android-chrome-192x192.png +0 -0
  1013. package/ui-dist/android-chrome-512x512.png +0 -0
  1014. package/ui-dist/apple-touch-icon.png +0 -0
  1015. package/ui-dist/assets/apl-B4CMkyY2.js +1 -0
  1016. package/ui-dist/assets/arc-xbLjL0VN.js +1 -0
  1017. package/ui-dist/assets/architectureDiagram-3BPJPVTR-KcFd4B-U.js +36 -0
  1018. package/ui-dist/assets/asciiarmor-Df11BRmG.js +1 -0
  1019. package/ui-dist/assets/asn1-EdZsLKOL.js +1 -0
  1020. package/ui-dist/assets/asterisk-B-8jnY81.js +1 -0
  1021. package/ui-dist/assets/blockDiagram-GPEHLZMM-CSD4otEL.js +132 -0
  1022. package/ui-dist/assets/brainfuck-C4LP7Hcl.js +1 -0
  1023. package/ui-dist/assets/c4Diagram-AAUBKEIU-Cre_NEHp.js +10 -0
  1024. package/ui-dist/assets/channel-BFN8obi8.js +1 -0
  1025. package/ui-dist/assets/chunk-2J33WTMH-CssLBsbh.js +1 -0
  1026. package/ui-dist/assets/chunk-4BX2VUAB-DjiavNFv.js +1 -0
  1027. package/ui-dist/assets/chunk-55IACEB6-C_F0yeYq.js +1 -0
  1028. package/ui-dist/assets/chunk-727SXJPM-B1FAOW4a.js +206 -0
  1029. package/ui-dist/assets/chunk-AQP2D5EJ-Do1241W-.js +231 -0
  1030. package/ui-dist/assets/chunk-FMBD7UC4-BQRrOMZD.js +15 -0
  1031. package/ui-dist/assets/chunk-ND2GUHAM-BPSt3kZ1.js +1 -0
  1032. package/ui-dist/assets/chunk-QZHKN3VN-BSpmhWDD.js +1 -0
  1033. package/ui-dist/assets/classDiagram-4FO5ZUOK-1Ay0zFCU.js +1 -0
  1034. package/ui-dist/assets/classDiagram-v2-Q7XG4LA2-1Ay0zFCU.js +1 -0
  1035. package/ui-dist/assets/clike-B9uivgTg.js +1 -0
  1036. package/ui-dist/assets/clojure-BMjYHr_A.js +1 -0
  1037. package/ui-dist/assets/cmake-BQqOBYOt.js +1 -0
  1038. package/ui-dist/assets/cobol-CWcv1MsR.js +1 -0
  1039. package/ui-dist/assets/coffeescript-S37ZYGWr.js +1 -0
  1040. package/ui-dist/assets/commonlisp-DBKNyK5s.js +1 -0
  1041. package/ui-dist/assets/cose-bilkent-S5V4N54A-CK2f2Te4.js +1 -0
  1042. package/ui-dist/assets/crystal-SjHAIU92.js +1 -0
  1043. package/ui-dist/assets/css-BnMrqG3P.js +1 -0
  1044. package/ui-dist/assets/cypher-C_CwsFkJ.js +1 -0
  1045. package/ui-dist/assets/cytoscape.esm-D8joxN9f.js +321 -0
  1046. package/ui-dist/assets/d-pRatUO7H.js +1 -0
  1047. package/ui-dist/assets/dagre-BM42HDAG-DaOXTN9-.js +4 -0
  1048. package/ui-dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  1049. package/ui-dist/assets/diagram-2AECGRRQ-D0ScQUGy.js +43 -0
  1050. package/ui-dist/assets/diagram-5GNKFQAL-7mH4Cncd.js +10 -0
  1051. package/ui-dist/assets/diagram-KO2AKTUF-aA9kuK-7.js +3 -0
  1052. package/ui-dist/assets/diagram-LMA3HP47-C9UXfmdK.js +24 -0
  1053. package/ui-dist/assets/diagram-OG6HWLK6-Ba3U-x1r.js +24 -0
  1054. package/ui-dist/assets/diff-DbItnlRl.js +1 -0
  1055. package/ui-dist/assets/dockerfile-BKs6k2Af.js +1 -0
  1056. package/ui-dist/assets/dtd-DF_7sFjM.js +1 -0
  1057. package/ui-dist/assets/dylan-DwRh75JA.js +1 -0
  1058. package/ui-dist/assets/ebnf-CDyGwa7X.js +1 -0
  1059. package/ui-dist/assets/ecl-Cabwm37j.js +1 -0
  1060. package/ui-dist/assets/eiffel-CnydiIhH.js +1 -0
  1061. package/ui-dist/assets/elm-vLlmbW-K.js +1 -0
  1062. package/ui-dist/assets/erDiagram-TEJ5UH35-CmskPKH1.js +85 -0
  1063. package/ui-dist/assets/erlang-BNw1qcRV.js +1 -0
  1064. package/ui-dist/assets/factor-kuTfRLto.js +1 -0
  1065. package/ui-dist/assets/fcl-Kvtd6kyn.js +1 -0
  1066. package/ui-dist/assets/flowDiagram-I6XJVG4X-B0iEPqGd.js +162 -0
  1067. package/ui-dist/assets/forth-Ffai-XNe.js +1 -0
  1068. package/ui-dist/assets/fortran-DYz_wnZ1.js +1 -0
  1069. package/ui-dist/assets/ganttDiagram-6RSMTGT7-DtpxlgWQ.js +292 -0
  1070. package/ui-dist/assets/gas-Bneqetm1.js +1 -0
  1071. package/ui-dist/assets/gherkin-heZmZLOM.js +1 -0
  1072. package/ui-dist/assets/gitGraphDiagram-PVQCEYII-VefBjqya.js +106 -0
  1073. package/ui-dist/assets/graph-CAnANduQ.js +1 -0
  1074. package/ui-dist/assets/groovy-D9Dt4D0W.js +1 -0
  1075. package/ui-dist/assets/haskell-Cw1EW3IL.js +1 -0
  1076. package/ui-dist/assets/haxe-H-WmDvRZ.js +1 -0
  1077. package/ui-dist/assets/http-DBlCnlav.js +1 -0
  1078. package/ui-dist/assets/idl-BEugSyMb.js +1 -0
  1079. package/ui-dist/assets/index-B9KxOFt-.js +1 -0
  1080. package/ui-dist/assets/index-BMPCuc-W.js +1 -0
  1081. package/ui-dist/assets/index-Bbfs2D7R.js +1 -0
  1082. package/ui-dist/assets/index-BrgHE5Lg.js +1 -0
  1083. package/ui-dist/assets/index-C5q-Cwlp.js +7 -0
  1084. package/ui-dist/assets/index-C6LpKpr3.js +1 -0
  1085. package/ui-dist/assets/index-CIzt5DFV.js +1 -0
  1086. package/ui-dist/assets/index-CRwAuYPj.js +1 -0
  1087. package/ui-dist/assets/index-CTEnIXsJ.js +1 -0
  1088. package/ui-dist/assets/index-CXGemv2V.js +1 -0
  1089. package/ui-dist/assets/index-ClDiS51u.js +1 -0
  1090. package/ui-dist/assets/index-CvKYfvpz.js +1 -0
  1091. package/ui-dist/assets/index-D2IqxlXD.js +1 -0
  1092. package/ui-dist/assets/index-D97fJMFR.js +522 -0
  1093. package/ui-dist/assets/index-DDHdUa2f.js +1 -0
  1094. package/ui-dist/assets/index-DMZ0QXqi.js +1 -0
  1095. package/ui-dist/assets/index-DMi4KpxO.js +6 -0
  1096. package/ui-dist/assets/index-DZB48Gve.js +1 -0
  1097. package/ui-dist/assets/index-Drr9zRdK.css +1 -0
  1098. package/ui-dist/assets/index-DtGqpE43.js +1 -0
  1099. package/ui-dist/assets/index-Du18kURt.js +2 -0
  1100. package/ui-dist/assets/index-KaLXuTqA.js +1 -0
  1101. package/ui-dist/assets/index-j5NgiILm.js +13 -0
  1102. package/ui-dist/assets/index-u0SfLZ3g.js +3 -0
  1103. package/ui-dist/assets/infoDiagram-5YYISTIA-D2OGH-dO.js +2 -0
  1104. package/ui-dist/assets/init-Gi6I4Gst.js +1 -0
  1105. package/ui-dist/assets/ishikawaDiagram-YF4QCWOH-CnMf3BJj.js +70 -0
  1106. package/ui-dist/assets/javascript-iXu5QeM3.js +1 -0
  1107. package/ui-dist/assets/journeyDiagram-JHISSGLW-BaXdD53T.js +139 -0
  1108. package/ui-dist/assets/julia-DuME0IfC.js +1 -0
  1109. package/ui-dist/assets/kanban-definition-UN3LZRKU-Brt7LjHm.js +89 -0
  1110. package/ui-dist/assets/katex-yT8l5JNH.js +257 -0
  1111. package/ui-dist/assets/layout-DGIYPm2g.js +1 -0
  1112. package/ui-dist/assets/linear-536T6Mkh.js +1 -0
  1113. package/ui-dist/assets/livescript-BwQOo05w.js +1 -0
  1114. package/ui-dist/assets/lua-VAEuO923.js +1 -0
  1115. package/ui-dist/assets/mathematica-DTrFuWx2.js +1 -0
  1116. package/ui-dist/assets/mbox-CNhZ1qSd.js +1 -0
  1117. package/ui-dist/assets/mermaid.core-CURTLVBm.js +303 -0
  1118. package/ui-dist/assets/mindmap-definition-RKZ34NQL-S2tDCU-U.js +96 -0
  1119. package/ui-dist/assets/mirc-CjQqDB4T.js +1 -0
  1120. package/ui-dist/assets/mllike-CXdrOF99.js +1 -0
  1121. package/ui-dist/assets/modelica-Dc1JOy9r.js +1 -0
  1122. package/ui-dist/assets/mscgen-BA5vi2Kp.js +1 -0
  1123. package/ui-dist/assets/mumps-BT43cFF4.js +1 -0
  1124. package/ui-dist/assets/nginx-DdIZxoE0.js +1 -0
  1125. package/ui-dist/assets/nsis-LdVXkNf5.js +1 -0
  1126. package/ui-dist/assets/ntriples-BfvgReVJ.js +1 -0
  1127. package/ui-dist/assets/octave-Ck1zUtKM.js +1 -0
  1128. package/ui-dist/assets/ordinal-Cboi1Yqb.js +1 -0
  1129. package/ui-dist/assets/oz-BzwKVEFT.js +1 -0
  1130. package/ui-dist/assets/pascal--L3eBynH.js +1 -0
  1131. package/ui-dist/assets/perl-CdXCOZ3F.js +1 -0
  1132. package/ui-dist/assets/pieDiagram-4H26LBE5-DD_Ih32z.js +30 -0
  1133. package/ui-dist/assets/pig-CevX1Tat.js +1 -0
  1134. package/ui-dist/assets/powershell-CFHJl5sT.js +1 -0
  1135. package/ui-dist/assets/properties-C78fOPTZ.js +1 -0
  1136. package/ui-dist/assets/protobuf-ChK-085T.js +1 -0
  1137. package/ui-dist/assets/pug-DeIclll2.js +1 -0
  1138. package/ui-dist/assets/puppet-DMA9R1ak.js +1 -0
  1139. package/ui-dist/assets/python-BuPzkPfP.js +1 -0
  1140. package/ui-dist/assets/q-pXgVlZs6.js +1 -0
  1141. package/ui-dist/assets/quadrantDiagram-W4KKPZXB-DA5BPBIK.js +7 -0
  1142. package/ui-dist/assets/r-B6wPVr8A.js +1 -0
  1143. package/ui-dist/assets/requirementDiagram-4Y6WPE33-Em8SPCro.js +84 -0
  1144. package/ui-dist/assets/rpm-CTu-6PCP.js +1 -0
  1145. package/ui-dist/assets/ruby-B2Rjki9n.js +1 -0
  1146. package/ui-dist/assets/sankeyDiagram-5OEKKPKP-BJVC4haY.js +40 -0
  1147. package/ui-dist/assets/sas-B4kiWyti.js +1 -0
  1148. package/ui-dist/assets/scheme-C41bIUwD.js +1 -0
  1149. package/ui-dist/assets/sequenceDiagram-3UESZ5HK-Cskntadf.js +162 -0
  1150. package/ui-dist/assets/shell-CjFT_Tl9.js +1 -0
  1151. package/ui-dist/assets/sieve-C3Gn_uJK.js +1 -0
  1152. package/ui-dist/assets/simple-mode-GW_nhZxv.js +1 -0
  1153. package/ui-dist/assets/smalltalk-CnHTOXQT.js +1 -0
  1154. package/ui-dist/assets/solr-DehyRSwq.js +1 -0
  1155. package/ui-dist/assets/sparql-DkYu6x3z.js +1 -0
  1156. package/ui-dist/assets/spreadsheet-BCZA_wO0.js +1 -0
  1157. package/ui-dist/assets/sql-D0XecflT.js +1 -0
  1158. package/ui-dist/assets/stateDiagram-AJRCARHV-CxlfdaOi.js +1 -0
  1159. package/ui-dist/assets/stateDiagram-v2-BHNVJYJU-eTgftUjW.js +1 -0
  1160. package/ui-dist/assets/stex-C3f8Ysf7.js +1 -0
  1161. package/ui-dist/assets/stylus-B533Al4x.js +1 -0
  1162. package/ui-dist/assets/swift-BzpIVaGY.js +1 -0
  1163. package/ui-dist/assets/tcl-DVfN8rqt.js +1 -0
  1164. package/ui-dist/assets/textile-CnDTJFAw.js +1 -0
  1165. package/ui-dist/assets/tiddlywiki-DO-Gjzrf.js +1 -0
  1166. package/ui-dist/assets/tiki-DGYXhP31.js +1 -0
  1167. package/ui-dist/assets/timeline-definition-PNZ67QCA-LOdaWSSa.js +120 -0
  1168. package/ui-dist/assets/toml-Bm5Em-hy.js +1 -0
  1169. package/ui-dist/assets/troff-wAsdV37c.js +1 -0
  1170. package/ui-dist/assets/ttcn-CfJYG6tj.js +1 -0
  1171. package/ui-dist/assets/ttcn-cfg-B9xdYoR4.js +1 -0
  1172. package/ui-dist/assets/turtle-B1tBg_DP.js +1 -0
  1173. package/ui-dist/assets/vb-CmGdzxic.js +1 -0
  1174. package/ui-dist/assets/vbscript-BuJXcnF6.js +1 -0
  1175. package/ui-dist/assets/velocity-D8B20fx6.js +1 -0
  1176. package/ui-dist/assets/vennDiagram-CIIHVFJN-CJ4ji6B3.js +34 -0
  1177. package/ui-dist/assets/verilog-C6RDOZhf.js +1 -0
  1178. package/ui-dist/assets/vhdl-lSbBsy5d.js +1 -0
  1179. package/ui-dist/assets/wardley-L42UT6IY-CxnVdUVH.js +153 -0
  1180. package/ui-dist/assets/wardleyDiagram-YWT4CUSO-CgGDttpl.js +78 -0
  1181. package/ui-dist/assets/webidl-ZXfAyPTL.js +1 -0
  1182. package/ui-dist/assets/xquery-DzFWVndE.js +1 -0
  1183. package/ui-dist/assets/xychartDiagram-2RQKCTM6-zuQa7bqx.js +7 -0
  1184. package/ui-dist/assets/yacas-BJ4BC0dw.js +1 -0
  1185. package/ui-dist/assets/z80-Hz9HOZM7.js +1 -0
  1186. package/ui-dist/brands/opencode-logo-dark-square.svg +18 -0
  1187. package/ui-dist/brands/opencode-logo-light-square.svg +18 -0
  1188. package/ui-dist/favicon-16x16.png +0 -0
  1189. package/ui-dist/favicon-32x32.png +0 -0
  1190. package/ui-dist/favicon.ico +0 -0
  1191. package/ui-dist/favicon.svg +8 -0
  1192. package/ui-dist/index.html +46 -0
  1193. package/ui-dist/site.webmanifest +30 -0
  1194. package/ui-dist/sw.js +42 -0
  1195. package/ui-dist/worktree-favicon-16x16.png +0 -0
  1196. package/ui-dist/worktree-favicon-32x32.png +0 -0
  1197. package/ui-dist/worktree-favicon.ico +0 -0
  1198. package/ui-dist/worktree-favicon.svg +9 -0
@@ -0,0 +1,2390 @@
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 operator-level authentication, and sensitive instance-wide
15
+ * mutations such as install/upgrade require instance-admin privileges.
16
+ *
17
+ * @module server/routes/plugins
18
+ * @see doc/plugins/PLUGIN_SPEC.md for the full plugin specification
19
+ */
20
+ import { access, readdir, readFile } from "node:fs/promises";
21
+ import path from "node:path";
22
+ import { randomUUID } from "node:crypto";
23
+ import { fileURLToPath } from "node:url";
24
+ import { Router } from "express";
25
+ import { and, desc, eq, gte } from "drizzle-orm";
26
+ import { agents, squads, heartbeatRuns, pluginLogs, pluginWebhookDeliveries, projects, } from "@slaw-ai/db";
27
+ import { PLUGIN_STATUSES, } from "@slaw-ai/shared";
28
+ import { pluginRegistryService } from "../services/plugin-registry.js";
29
+ import { pluginLifecycleManager } from "../services/plugin-lifecycle.js";
30
+ import { getPluginUiContributionMetadata } from "../services/plugin-loader.js";
31
+ import { logActivity } from "../services/activity-log.js";
32
+ import { publishGlobalLiveEvent } from "../services/live-events.js";
33
+ import { issueService } from "../services/issues.js";
34
+ import { JsonRpcCallError, PLUGIN_RPC_ERROR_CODES } from "@slaw-ai/plugin-sdk";
35
+ import { assertAuthenticated, assertOperator, assertOperatorOrgAccess, assertSquadAccess, assertInstanceAdmin, getActorInfo, } from "./authz.js";
36
+ import { validateInstanceConfig } from "../services/plugin-config-validator.js";
37
+ import { findLocalFolderDeclaration, getStoredLocalFolders, inspectPluginLocalFolder, requireLocalFolderDeclaration, setStoredLocalFolder, } from "../services/plugin-local-folders.js";
38
+ import { extractSecretRefPathsFromConfig, PLUGIN_SECRET_REFS_DISABLED_MESSAGE, } from "../services/plugin-secrets-handler.js";
39
+ import { badRequest, forbidden, notFound, unauthorized, unprocessable } from "../errors.js";
40
+ /** UUID v4 regex used for plugin ID route resolution. */
41
+ 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;
42
+ const PLUGIN_API_BODY_LIMIT_BYTES = 1_000_000;
43
+ const PLUGIN_SCOPED_API_RESPONSE_HEADER_ALLOWLIST = new Set([
44
+ "cache-control",
45
+ "etag",
46
+ "last-modified",
47
+ "x-request-id",
48
+ ]);
49
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
50
+ const REPO_ROOT = path.resolve(__dirname, "../../..");
51
+ const EXPERIMENTAL_BUNDLED_PLUGIN_PACKAGE_NAMES = new Set([
52
+ "@slaw-ai/plugin-llm-wiki",
53
+ "@slaw-ai/plugin-modal",
54
+ "@slaw-ai/plugin-workspace-diff",
55
+ ]);
56
+ let bundledPluginsCache = null;
57
+ function titleCasePluginName(packageName) {
58
+ const localName = packageName.split("/").pop() ?? packageName;
59
+ return localName
60
+ .replace(/^slaw-plugin-/, "")
61
+ .replace(/^plugin-/, "")
62
+ .split("-")
63
+ .filter(Boolean)
64
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
65
+ .join(" ");
66
+ }
67
+ async function fileExists(filePath) {
68
+ return access(filePath).then(() => true, () => false);
69
+ }
70
+ async function readJsonFile(filePath) {
71
+ try {
72
+ return JSON.parse(await readFile(filePath, "utf8"));
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ async function findPackageJsonFiles(root, maxDepth = 4) {
79
+ if (!(await fileExists(root)))
80
+ return [];
81
+ const packageJsonFiles = [];
82
+ const walk = async (dir, depth) => {
83
+ if (depth > maxDepth)
84
+ return;
85
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
86
+ for (const entry of entries) {
87
+ if (entry.name === "node_modules" || entry.name === "dist")
88
+ continue;
89
+ const entryPath = path.join(dir, entry.name);
90
+ if (entry.isFile() && entry.name === "package.json") {
91
+ packageJsonFiles.push(entryPath);
92
+ }
93
+ else if (entry.isDirectory()) {
94
+ await walk(entryPath, depth + 1);
95
+ }
96
+ }
97
+ };
98
+ await walk(root, 0);
99
+ return packageJsonFiles;
100
+ }
101
+ function manifestSourcePath(packageRoot, pkgJson) {
102
+ const slawPlugin = pkgJson.slawPlugin;
103
+ if (!slawPlugin
104
+ || typeof slawPlugin !== "object"
105
+ || Array.isArray(slawPlugin)) {
106
+ return null;
107
+ }
108
+ const manifestPath = slawPlugin.manifest;
109
+ if (typeof manifestPath !== "string")
110
+ return null;
111
+ const sourcePath = manifestPath
112
+ .replace(/^\.\/dist\//, "./src/")
113
+ .replace(/\.js$/, ".ts");
114
+ return path.resolve(packageRoot, sourcePath);
115
+ }
116
+ function firstStringLiteral(source, key) {
117
+ const match = source.match(new RegExp(`${key}:\\s*(?:"([^"]*)"|'([^']*)'|\`([^\`]*)\`)`, "s"));
118
+ return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;
119
+ }
120
+ async function bundledPluginMetadata(packageRoot, pkgJson) {
121
+ const sourcePath = manifestSourcePath(packageRoot, pkgJson);
122
+ if (!sourcePath || !(await fileExists(sourcePath)))
123
+ return {};
124
+ try {
125
+ const source = await readFile(sourcePath, "utf8");
126
+ const pluginId = source
127
+ .match(/(?:export\s+)?const\s+PLUGIN_ID\s*=\s*(?:"([^"]*)"|'([^']*)'|`([^`]*)`)/)
128
+ ?.slice(1)
129
+ .find(Boolean)
130
+ ?? firstStringLiteral(source, "id")
131
+ ?? null;
132
+ return {
133
+ pluginKey: pluginId ?? undefined,
134
+ displayName: firstStringLiteral(source, "displayName") ?? undefined,
135
+ description: firstStringLiteral(source, "description") ?? undefined,
136
+ };
137
+ }
138
+ catch {
139
+ return {};
140
+ }
141
+ }
142
+ function isExperimentalBundledPlugin(packageRoot, packageName) {
143
+ return (EXPERIMENTAL_BUNDLED_PLUGIN_PACKAGE_NAMES.has(packageName)
144
+ || packageRoot.includes(`${path.sep}sandbox-providers${path.sep}`)
145
+ || packageName.includes("sandbox"));
146
+ }
147
+ async function discoverBundledPlugins() {
148
+ const pluginRoot = path.resolve(REPO_ROOT, "packages/plugins");
149
+ const bundledPlugins = [];
150
+ for (const packageJsonPath of await findPackageJsonFiles(pluginRoot)) {
151
+ const packageRoot = path.dirname(packageJsonPath);
152
+ const pkgJson = await readJsonFile(packageJsonPath);
153
+ const slawPlugin = pkgJson?.slawPlugin;
154
+ if (!pkgJson
155
+ || !slawPlugin
156
+ || typeof slawPlugin !== "object"
157
+ || Array.isArray(slawPlugin)) {
158
+ continue;
159
+ }
160
+ const packageName = pkgJson.name;
161
+ if (typeof packageName !== "string" || packageName.length === 0)
162
+ continue;
163
+ const metadata = await bundledPluginMetadata(packageRoot, pkgJson);
164
+ const tag = packageRoot.includes(`${path.sep}examples${path.sep}`) ? "example" : "first-party";
165
+ bundledPlugins.push({
166
+ packageName,
167
+ pluginKey: metadata.pluginKey ?? packageName,
168
+ displayName: metadata.displayName ?? titleCasePluginName(packageName),
169
+ description: metadata.description
170
+ ?? `Bundled Slaw plugin from ${path.relative(REPO_ROOT, packageRoot)}.`,
171
+ localPath: packageRoot,
172
+ tag,
173
+ experimental: isExperimentalBundledPlugin(packageRoot, packageName),
174
+ });
175
+ }
176
+ return bundledPlugins.sort((left, right) => {
177
+ if (left.tag !== right.tag)
178
+ return left.tag === "first-party" ? -1 : 1;
179
+ return left.displayName.localeCompare(right.displayName);
180
+ });
181
+ }
182
+ async function listBundledPlugins() {
183
+ bundledPluginsCache ??= discoverBundledPlugins().catch((error) => {
184
+ bundledPluginsCache = null;
185
+ throw error;
186
+ });
187
+ return bundledPluginsCache;
188
+ }
189
+ /**
190
+ * Resolve a plugin by either database ID or plugin key.
191
+ *
192
+ * Lookup order:
193
+ * - UUID-like IDs: getById first, then getByKey.
194
+ * - All non-UUID values: getByKey only, never getById. The persisted plugin
195
+ * ID column is a PostgreSQL UUID, so probing it with keys such as
196
+ * "acme.plugin" raises a database cast error before a key lookup can happen.
197
+ *
198
+ * @param registry - The plugin registry service instance
199
+ * @param pluginId - Either a database UUID or plugin key (manifest id)
200
+ * @returns Plugin record or null if not found
201
+ */
202
+ async function resolvePlugin(registry, pluginId) {
203
+ const isUuid = UUID_REGEX.test(pluginId);
204
+ if (!isUuid) {
205
+ return registry.getByKey(pluginId);
206
+ }
207
+ const byId = await registry.getById(pluginId);
208
+ if (byId)
209
+ return byId;
210
+ return registry.getByKey(pluginId);
211
+ }
212
+ /**
213
+ * Create Express router for plugin management API.
214
+ *
215
+ * Routes provided:
216
+ *
217
+ * | Method | Path | Description |
218
+ * |--------|------|-------------|
219
+ * | GET | /plugins | List all plugins (optional ?status= filter) |
220
+ * | GET | /plugins/ui-contributions | Get UI slots from ready plugins |
221
+ * | GET | /plugins/:pluginId | Get single plugin by ID or key |
222
+ * | POST | /plugins/install | Install from npm or local path |
223
+ * | DELETE | /plugins/:pluginId | Uninstall (optional ?purge=true) |
224
+ * | POST | /plugins/:pluginId/enable | Enable a plugin |
225
+ * | POST | /plugins/:pluginId/disable | Disable a plugin |
226
+ * | GET | /plugins/:pluginId/health | Run health diagnostics |
227
+ * | POST | /plugins/:pluginId/upgrade | Upgrade to newer version |
228
+ * | GET | /plugins/:pluginId/jobs | List jobs for a plugin |
229
+ * | GET | /plugins/:pluginId/jobs/:jobId/runs | List runs for a job |
230
+ * | POST | /plugins/:pluginId/jobs/:jobId/trigger | Manually trigger a job |
231
+ * | POST | /plugins/:pluginId/webhooks/:endpointKey | Receive inbound webhook |
232
+ * | GET | /plugins/tools | List all available plugin tools |
233
+ * | GET | /plugins/tools?pluginId=... | List tools for a specific plugin |
234
+ * | POST | /plugins/tools/execute | Execute a plugin tool |
235
+ * | GET | /plugins/:pluginId/config | Get current plugin config |
236
+ * | POST | /plugins/:pluginId/config | Save (upsert) plugin config |
237
+ * | POST | /plugins/:pluginId/config/test | Test config via validateConfig RPC |
238
+ * | POST | /plugins/:pluginId/bridge/data | Proxy getData to plugin worker |
239
+ * | POST | /plugins/:pluginId/bridge/action | Proxy performAction to plugin worker |
240
+ * | POST | /plugins/:pluginId/data/:key | Proxy getData to plugin worker (key in URL) |
241
+ * | POST | /plugins/:pluginId/actions/:key | Proxy performAction to plugin worker (key in URL) |
242
+ * | GET | /plugins/:pluginId/bridge/stream/:channel | SSE stream from worker to UI |
243
+ * | GET | /plugins/:pluginId/dashboard | Aggregated health dashboard data |
244
+ *
245
+ * **Route Ordering Note:** Static routes (like /ui-contributions, /tools) must be
246
+ * registered before parameterized routes (like /:pluginId) to prevent Express from
247
+ * matching them as a plugin ID.
248
+ *
249
+ * @param db - Database connection instance
250
+ * @param jobDeps - Optional job scheduling dependencies
251
+ * @param webhookDeps - Optional webhook ingestion dependencies
252
+ * @param toolDeps - Optional tool dispatcher dependencies
253
+ * @param bridgeDeps - Optional bridge proxy dependencies for getData/performAction
254
+ * @returns Express router with plugin routes mounted
255
+ */
256
+ export function pluginRoutes(db, loader, jobDeps, webhookDeps, toolDeps, bridgeDeps) {
257
+ const router = Router();
258
+ const registry = pluginRegistryService(db);
259
+ const lifecycle = pluginLifecycleManager(db, {
260
+ loader,
261
+ workerManager: bridgeDeps?.workerManager ?? webhookDeps?.workerManager,
262
+ });
263
+ const issuesSvc = issueService(db);
264
+ function matchScopedApiRoute(route, method, requestPath) {
265
+ if (route.method !== method)
266
+ return null;
267
+ const normalize = (value) => value.replace(/\/+$/, "") || "/";
268
+ const routeSegments = normalize(route.path).split("/").filter(Boolean);
269
+ const requestSegments = normalize(requestPath).split("/").filter(Boolean);
270
+ if (routeSegments.length !== requestSegments.length)
271
+ return null;
272
+ const params = {};
273
+ for (let i = 0; i < routeSegments.length; i += 1) {
274
+ const routeSegment = routeSegments[i];
275
+ const requestSegment = requestSegments[i];
276
+ if (routeSegment.startsWith(":")) {
277
+ params[routeSegment.slice(1)] = decodeURIComponent(requestSegment);
278
+ continue;
279
+ }
280
+ if (routeSegment !== requestSegment)
281
+ return null;
282
+ }
283
+ return params;
284
+ }
285
+ function sanitizePluginRequestHeaders(req) {
286
+ const safeHeaderNames = new Set([
287
+ "accept",
288
+ "content-type",
289
+ "user-agent",
290
+ "x-slaw-run-id",
291
+ "x-request-id",
292
+ ]);
293
+ const headers = {};
294
+ for (const [name, value] of Object.entries(req.headers)) {
295
+ const lower = name.toLowerCase();
296
+ if (!safeHeaderNames.has(lower))
297
+ continue;
298
+ if (Array.isArray(value)) {
299
+ headers[lower] = value.join(", ");
300
+ }
301
+ else if (typeof value === "string") {
302
+ headers[lower] = value;
303
+ }
304
+ }
305
+ return headers;
306
+ }
307
+ function applyPluginScopedApiResponseHeaders(res, headers) {
308
+ for (const [name, value] of Object.entries(headers ?? {})) {
309
+ const lower = name.toLowerCase();
310
+ if (!PLUGIN_SCOPED_API_RESPONSE_HEADER_ALLOWLIST.has(lower))
311
+ continue;
312
+ res.setHeader(lower, value);
313
+ }
314
+ }
315
+ function normalizeQuery(query) {
316
+ const normalized = {};
317
+ for (const [key, value] of Object.entries(query)) {
318
+ if (typeof value === "string") {
319
+ normalized[key] = value;
320
+ }
321
+ else if (Array.isArray(value)) {
322
+ normalized[key] = value.map((entry) => String(entry));
323
+ }
324
+ }
325
+ return normalized;
326
+ }
327
+ async function resolveScopedApiSquadId(route, params, req) {
328
+ const resolution = route.squadResolution;
329
+ if (!resolution) {
330
+ if (req.actor.type === "agent" && req.actor.squadId)
331
+ return req.actor.squadId;
332
+ return null;
333
+ }
334
+ if (resolution.from === "body") {
335
+ const body = req.body;
336
+ const squadId = body?.[resolution.key ?? ""];
337
+ return typeof squadId === "string" ? squadId : null;
338
+ }
339
+ if (resolution.from === "query") {
340
+ const value = req.query[resolution.key ?? ""];
341
+ return typeof value === "string" ? value : null;
342
+ }
343
+ const issueId = params[resolution.param ?? ""];
344
+ if (!issueId)
345
+ return null;
346
+ const issue = await issuesSvc.getById(issueId);
347
+ return issue?.squadId ?? null;
348
+ }
349
+ function assertScopedApiAuth(req, route) {
350
+ if (route.auth === "operator") {
351
+ assertOperator(req);
352
+ return;
353
+ }
354
+ if (route.auth === "agent") {
355
+ assertAuthenticated(req);
356
+ if (req.actor.type !== "agent")
357
+ throw forbidden("Agent access required");
358
+ return;
359
+ }
360
+ if (route.auth === "webhook") {
361
+ throw unprocessable("Webhook-scoped plugin API routes require a signature verifier and are not enabled");
362
+ }
363
+ assertAuthenticated(req);
364
+ if (req.actor.type !== "operator" && req.actor.type !== "agent") {
365
+ throw forbidden("Operator or agent access required");
366
+ }
367
+ }
368
+ async function enforceScopedApiCheckout(req, route, params, squadId) {
369
+ const policy = route.checkoutPolicy ?? "none";
370
+ if (policy === "none" || req.actor.type !== "agent")
371
+ return;
372
+ const issueId = params.issueId;
373
+ if (!issueId) {
374
+ throw unprocessable("Checkout-protected plugin API routes require an issueId route parameter");
375
+ }
376
+ const issue = await issuesSvc.getById(issueId);
377
+ if (!issue || issue.squadId !== squadId) {
378
+ throw notFound("Issue not found");
379
+ }
380
+ if (policy === "required-for-agent-in-progress") {
381
+ if (issue.status !== "in_progress" || issue.assigneeAgentId !== req.actor.agentId)
382
+ return;
383
+ }
384
+ const runId = req.actor.runId?.trim();
385
+ if (!runId) {
386
+ throw unauthorized("Agent run id required");
387
+ }
388
+ if (!req.actor.agentId) {
389
+ throw forbidden("Agent authentication required");
390
+ }
391
+ await issuesSvc.assertCheckoutOwner(issueId, req.actor.agentId, runId);
392
+ }
393
+ async function resolvePluginAuditSquadIds(req) {
394
+ if (typeof db.select === "function") {
395
+ const rows = await db
396
+ .select({ id: squads.id })
397
+ .from(squads);
398
+ return rows.map((row) => row.id);
399
+ }
400
+ if (req.actor.type === "agent" && req.actor.squadId) {
401
+ return [req.actor.squadId];
402
+ }
403
+ if (req.actor.type === "operator") {
404
+ return req.actor.squadIds ?? [];
405
+ }
406
+ return [];
407
+ }
408
+ async function logPluginMutationActivity(req, action, entityId, details) {
409
+ const squadIds = await resolvePluginAuditSquadIds(req);
410
+ if (squadIds.length === 0)
411
+ return;
412
+ const actor = getActorInfo(req);
413
+ await Promise.all(squadIds.map((squadId) => logActivity(db, {
414
+ squadId,
415
+ actorType: actor.actorType,
416
+ actorId: actor.actorId,
417
+ agentId: actor.agentId,
418
+ runId: actor.runId,
419
+ action,
420
+ entityType: "plugin",
421
+ entityId,
422
+ details,
423
+ })));
424
+ }
425
+ function assertPluginBridgeScope(req, squadId) {
426
+ if (squadId === undefined || squadId === null) {
427
+ assertInstanceAdmin(req);
428
+ return undefined;
429
+ }
430
+ if (typeof squadId !== "string" || squadId.trim().length === 0) {
431
+ throw badRequest('"squadId" must be a non-empty string when provided');
432
+ }
433
+ assertSquadAccess(req, squadId);
434
+ return squadId;
435
+ }
436
+ function performActionActorContext(req, squadId) {
437
+ const scopedSquadId = squadId ?? null;
438
+ if (req.actor.type === "agent") {
439
+ return {
440
+ type: "agent",
441
+ userId: null,
442
+ agentId: req.actor.agentId ?? null,
443
+ runId: req.actor.runId ?? null,
444
+ squadId: scopedSquadId,
445
+ };
446
+ }
447
+ if (req.actor.type === "operator") {
448
+ return {
449
+ type: "user",
450
+ userId: req.actor.userId ?? null,
451
+ agentId: null,
452
+ runId: req.actor.runId ?? null,
453
+ squadId: scopedSquadId,
454
+ };
455
+ }
456
+ return {
457
+ type: "system",
458
+ userId: null,
459
+ agentId: null,
460
+ runId: req.actor.runId ?? null,
461
+ squadId: scopedSquadId,
462
+ };
463
+ }
464
+ function actionParamsWithAuthorizedSquadScope(params, squadId) {
465
+ const base = params ?? {};
466
+ return squadId === undefined ? base : { ...base, squadId };
467
+ }
468
+ async function validateToolRunContextScope(runContext) {
469
+ const [agent] = await db
470
+ .select({ squadId: agents.squadId })
471
+ .from(agents)
472
+ .where(eq(agents.id, runContext.agentId))
473
+ .limit(1);
474
+ if (!agent || agent.squadId !== runContext.squadId) {
475
+ return '"runContext.agentId" does not belong to "runContext.squadId"';
476
+ }
477
+ const [run] = await db
478
+ .select({ squadId: heartbeatRuns.squadId, agentId: heartbeatRuns.agentId })
479
+ .from(heartbeatRuns)
480
+ .where(eq(heartbeatRuns.id, runContext.runId))
481
+ .limit(1);
482
+ if (!run || run.squadId !== runContext.squadId) {
483
+ return '"runContext.runId" does not belong to "runContext.squadId"';
484
+ }
485
+ if (run.agentId !== runContext.agentId) {
486
+ return '"runContext.runId" does not belong to "runContext.agentId"';
487
+ }
488
+ const [project] = await db
489
+ .select({ squadId: projects.squadId })
490
+ .from(projects)
491
+ .where(eq(projects.id, runContext.projectId))
492
+ .limit(1);
493
+ if (!project || project.squadId !== runContext.squadId) {
494
+ return '"runContext.projectId" does not belong to "runContext.squadId"';
495
+ }
496
+ return null;
497
+ }
498
+ /**
499
+ * GET /api/plugins
500
+ *
501
+ * List all installed plugins, optionally filtered by lifecycle status.
502
+ *
503
+ * Query params:
504
+ * - `status` (optional): Filter by lifecycle status. Must be one of the
505
+ * values in `PLUGIN_STATUSES` (`installed`, `ready`, `error`,
506
+ * `upgrade_pending`, `uninstalled`). Returns HTTP 400 if the value is
507
+ * not a recognised status string.
508
+ *
509
+ * Response: `PluginRecord[]`
510
+ */
511
+ router.get("/plugins", async (req, res) => {
512
+ assertOperatorOrgAccess(req);
513
+ const rawStatus = req.query.status;
514
+ if (rawStatus !== undefined) {
515
+ if (typeof rawStatus !== "string" || !PLUGIN_STATUSES.includes(rawStatus)) {
516
+ res.status(400).json({
517
+ error: `Invalid status '${String(rawStatus)}'. Must be one of: ${PLUGIN_STATUSES.join(", ")}`,
518
+ });
519
+ return;
520
+ }
521
+ }
522
+ const status = rawStatus;
523
+ const plugins = status
524
+ ? await registry.listByStatus(status)
525
+ : await registry.listInstalled();
526
+ res.json(plugins);
527
+ });
528
+ /**
529
+ * GET /api/plugins/examples
530
+ *
531
+ * Return plugin packages bundled in this repo, if present.
532
+ * These can be installed through the normal local-path install flow.
533
+ */
534
+ router.get("/plugins/examples", async (req, res) => {
535
+ assertOperatorOrgAccess(req);
536
+ res.json(await listBundledPlugins());
537
+ });
538
+ // IMPORTANT: Static routes must come before parameterized routes
539
+ // to avoid Express matching "ui-contributions" as a :pluginId
540
+ /**
541
+ * GET /api/plugins/ui-contributions
542
+ *
543
+ * Return UI contributions from all plugins in 'ready' state.
544
+ * Used by the frontend to discover plugin UI slots and launcher metadata.
545
+ *
546
+ * The response is normalized for the frontend slot host:
547
+ * - Only includes plugins with at least one declared UI slot or launcher
548
+ * - Excludes plugins with null/missing manifestJson (defensive)
549
+ * - Slots are extracted from manifest.ui.slots
550
+ * - Launchers are aggregated from legacy manifest.launchers and manifest.ui.launchers
551
+ *
552
+ * Example response:
553
+ * ```json
554
+ * [
555
+ * {
556
+ * "pluginId": "plg_123",
557
+ * "pluginKey": "slaw.claude-usage",
558
+ * "displayName": "Claude Usage",
559
+ * "version": "1.0.0",
560
+ * "uiEntryFile": "index.js",
561
+ * "slots": [],
562
+ * "launchers": [
563
+ * {
564
+ * "id": "claude-usage-toolbar",
565
+ * "displayName": "Claude Usage",
566
+ * "placementZone": "toolbarButton",
567
+ * "action": { "type": "openModal", "target": "ClaudeUsageView" },
568
+ * "render": { "environment": "hostOverlay", "bounds": "wide" }
569
+ * }
570
+ * ]
571
+ * }
572
+ * ]
573
+ * ```
574
+ *
575
+ * Response: PluginUiContribution[]
576
+ */
577
+ router.get("/plugins/ui-contributions", async (req, res) => {
578
+ assertOperatorOrgAccess(req);
579
+ const plugins = await registry.listByStatus("ready");
580
+ const contributions = plugins
581
+ .map((plugin) => {
582
+ // Safety check: manifestJson should always exist for ready plugins, but guard against null
583
+ const manifest = plugin.manifestJson;
584
+ if (!manifest)
585
+ return null;
586
+ const uiMetadata = getPluginUiContributionMetadata(manifest);
587
+ if (!uiMetadata)
588
+ return null;
589
+ return {
590
+ pluginId: plugin.id,
591
+ pluginKey: plugin.pluginKey,
592
+ displayName: manifest.displayName,
593
+ version: plugin.version,
594
+ updatedAt: plugin.updatedAt.toISOString(),
595
+ uiEntryFile: uiMetadata.uiEntryFile,
596
+ slots: uiMetadata.slots,
597
+ launchers: uiMetadata.launchers,
598
+ };
599
+ })
600
+ .filter((item) => item !== null);
601
+ res.json(contributions);
602
+ });
603
+ // ===========================================================================
604
+ // Tool discovery and execution routes
605
+ // ===========================================================================
606
+ /**
607
+ * GET /api/plugins/tools
608
+ *
609
+ * List all available plugin-contributed tools in an agent-friendly format.
610
+ *
611
+ * Query params:
612
+ * - `pluginId` (optional): Filter to tools from a specific plugin
613
+ *
614
+ * Response: `AgentToolDescriptor[]`
615
+ * Errors: 501 if tool dispatcher is not configured
616
+ */
617
+ router.get("/plugins/tools", async (req, res) => {
618
+ assertOperatorOrgAccess(req);
619
+ if (!toolDeps) {
620
+ res.status(501).json({ error: "Plugin tool dispatch is not enabled" });
621
+ return;
622
+ }
623
+ const pluginId = req.query.pluginId;
624
+ const filter = pluginId ? { pluginId } : undefined;
625
+ const tools = toolDeps.toolDispatcher.listToolsForAgent(filter);
626
+ res.json(tools);
627
+ });
628
+ /**
629
+ * POST /api/plugins/tools/execute
630
+ *
631
+ * Execute a plugin-contributed tool by its namespaced name.
632
+ *
633
+ * This is the primary endpoint used by the agent service to invoke
634
+ * plugin tools during an agent run.
635
+ *
636
+ * Request body:
637
+ * - `tool`: Fully namespaced tool name (e.g., "acme.linear:search-issues")
638
+ * - `parameters`: Parameters matching the tool's declared JSON Schema
639
+ * - `runContext`: Agent run context with agentId, runId, squadId, projectId
640
+ *
641
+ * Response: `ToolExecutionResult`
642
+ * Errors:
643
+ * - 400 if request validation fails
644
+ * - 404 if tool is not found
645
+ * - 501 if tool dispatcher is not configured
646
+ * - 502 if the plugin worker is unavailable or the RPC call fails
647
+ */
648
+ router.post("/plugins/tools/execute", async (req, res) => {
649
+ assertOperatorOrgAccess(req);
650
+ if (!toolDeps) {
651
+ res.status(501).json({ error: "Plugin tool dispatch is not enabled" });
652
+ return;
653
+ }
654
+ const body = req.body;
655
+ if (!body) {
656
+ res.status(400).json({ error: "Request body is required" });
657
+ return;
658
+ }
659
+ const { tool, parameters, runContext } = body;
660
+ // Validate required fields
661
+ if (!tool || typeof tool !== "string") {
662
+ res.status(400).json({ error: '"tool" is required and must be a string' });
663
+ return;
664
+ }
665
+ if (!runContext || typeof runContext !== "object") {
666
+ res.status(400).json({ error: '"runContext" is required and must be an object' });
667
+ return;
668
+ }
669
+ if (!runContext.agentId || !runContext.runId || !runContext.squadId || !runContext.projectId) {
670
+ res.status(400).json({
671
+ error: '"runContext" must include agentId, runId, squadId, and projectId',
672
+ });
673
+ return;
674
+ }
675
+ assertSquadAccess(req, runContext.squadId);
676
+ const scopeError = await validateToolRunContextScope(runContext);
677
+ if (scopeError) {
678
+ res.status(403).json({ error: scopeError });
679
+ return;
680
+ }
681
+ // Verify the tool exists
682
+ const registeredTool = toolDeps.toolDispatcher.getTool(tool);
683
+ if (!registeredTool) {
684
+ res.status(404).json({ error: `Tool "${tool}" not found` });
685
+ return;
686
+ }
687
+ try {
688
+ const result = await toolDeps.toolDispatcher.executeTool(tool, parameters ?? {}, runContext);
689
+ res.json(result);
690
+ }
691
+ catch (err) {
692
+ const message = err instanceof Error ? err.message : String(err);
693
+ // Distinguish between "worker not running" (502) and other errors (500)
694
+ if (message.includes("not running") || message.includes("worker")) {
695
+ res.status(502).json({ error: message });
696
+ }
697
+ else {
698
+ res.status(500).json({ error: message });
699
+ }
700
+ }
701
+ });
702
+ /**
703
+ * POST /api/plugins/install
704
+ *
705
+ * Install a plugin from npm or a local filesystem path.
706
+ *
707
+ * Instance-wide plugin installation is restricted to instance admins because
708
+ * the install flow fetches and inspects package contents on the host.
709
+ *
710
+ * Request body:
711
+ * - packageName: npm package name or local path (required)
712
+ * - version: Target version for npm packages (optional)
713
+ * - isLocalPath: Set true if packageName is a local path
714
+ *
715
+ * The installer:
716
+ * 1. Downloads from npm or loads from local path
717
+ * 2. Validates the manifest (schema + capability consistency)
718
+ * 3. Registers in the database
719
+ * 4. Transitions to `ready` state if no new capability approval is needed
720
+ *
721
+ * Response: `PluginRecord`
722
+ *
723
+ * Errors:
724
+ * - `400` — validation failure or install error (package not found, bad manifest, etc.)
725
+ * - `500` — installation succeeded but manifest is missing (indicates a loader bug)
726
+ */
727
+ router.post("/plugins/install", async (req, res) => {
728
+ assertInstanceAdmin(req);
729
+ const { packageName, version, isLocalPath } = req.body;
730
+ // Input validation
731
+ if (!packageName || typeof packageName !== "string") {
732
+ res.status(400).json({ error: "packageName is required and must be a string" });
733
+ return;
734
+ }
735
+ if (version !== undefined && typeof version !== "string") {
736
+ res.status(400).json({ error: "version must be a string if provided" });
737
+ return;
738
+ }
739
+ if (isLocalPath !== undefined && typeof isLocalPath !== "boolean") {
740
+ res.status(400).json({ error: "isLocalPath must be a boolean if provided" });
741
+ return;
742
+ }
743
+ // Validate package name format
744
+ const trimmedPackage = packageName.trim();
745
+ if (trimmedPackage.length === 0) {
746
+ res.status(400).json({ error: "packageName cannot be empty" });
747
+ return;
748
+ }
749
+ // Basic security check for package name (prevent injection)
750
+ if (!isLocalPath && /[<>:"|?*]/.test(trimmedPackage)) {
751
+ res.status(400).json({ error: "packageName contains invalid characters" });
752
+ return;
753
+ }
754
+ try {
755
+ const installOptions = isLocalPath
756
+ ? { localPath: trimmedPackage }
757
+ : { packageName: trimmedPackage, version: version?.trim() };
758
+ const discovered = await loader.installPlugin(installOptions);
759
+ if (!discovered.manifest) {
760
+ res.status(500).json({ error: "Plugin installed but manifest is missing" });
761
+ return;
762
+ }
763
+ // Transition to ready state
764
+ const existingPlugin = await registry.getByKey(discovered.manifest.id);
765
+ if (existingPlugin) {
766
+ await lifecycle.load(existingPlugin.id);
767
+ const updated = await registry.getById(existingPlugin.id);
768
+ await logPluginMutationActivity(req, "plugin.installed", existingPlugin.id, {
769
+ pluginId: existingPlugin.id,
770
+ pluginKey: existingPlugin.pluginKey,
771
+ packageName: updated?.packageName ?? existingPlugin.packageName,
772
+ version: updated?.version ?? existingPlugin.version,
773
+ source: isLocalPath ? "local_path" : "npm",
774
+ });
775
+ publishGlobalLiveEvent({ type: "plugin.ui.updated", payload: { pluginId: existingPlugin.id, action: "installed" } });
776
+ res.json(updated);
777
+ }
778
+ else {
779
+ // This shouldn't happen since installPlugin already registers in the DB
780
+ res.status(500).json({ error: "Plugin installed but not found in registry" });
781
+ }
782
+ }
783
+ catch (err) {
784
+ const message = err instanceof Error ? err.message : String(err);
785
+ res.status(400).json({ error: message });
786
+ }
787
+ });
788
+ /**
789
+ * Map a worker RPC error to a bridge-level error code.
790
+ *
791
+ * JsonRpcCallError carries numeric codes from the plugin RPC error code space.
792
+ * This helper maps them to the string error codes defined in PluginBridgeErrorCode.
793
+ *
794
+ * @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
795
+ */
796
+ function mapRpcErrorToBridgeError(err) {
797
+ if (err instanceof JsonRpcCallError) {
798
+ switch (err.code) {
799
+ case PLUGIN_RPC_ERROR_CODES.WORKER_UNAVAILABLE:
800
+ return {
801
+ code: "WORKER_UNAVAILABLE",
802
+ message: err.message,
803
+ details: err.data,
804
+ };
805
+ case PLUGIN_RPC_ERROR_CODES.CAPABILITY_DENIED:
806
+ return {
807
+ code: "CAPABILITY_DENIED",
808
+ message: err.message,
809
+ details: err.data,
810
+ };
811
+ case PLUGIN_RPC_ERROR_CODES.INVOCATION_SCOPE_DENIED:
812
+ return {
813
+ code: "INVOCATION_SCOPE_DENIED",
814
+ message: err.message,
815
+ details: err.data,
816
+ };
817
+ case PLUGIN_RPC_ERROR_CODES.TIMEOUT:
818
+ return {
819
+ code: "TIMEOUT",
820
+ message: err.message,
821
+ details: err.data,
822
+ };
823
+ case PLUGIN_RPC_ERROR_CODES.WORKER_ERROR:
824
+ return {
825
+ code: "WORKER_ERROR",
826
+ message: err.message,
827
+ details: err.data,
828
+ };
829
+ default:
830
+ return {
831
+ code: "UNKNOWN",
832
+ message: err.message,
833
+ details: err.data,
834
+ };
835
+ }
836
+ }
837
+ const message = err instanceof Error ? err.message : String(err);
838
+ // Worker not running — surface as WORKER_UNAVAILABLE
839
+ if (message.includes("not running") || message.includes("not registered")) {
840
+ return {
841
+ code: "WORKER_UNAVAILABLE",
842
+ message,
843
+ };
844
+ }
845
+ return {
846
+ code: "UNKNOWN",
847
+ message,
848
+ };
849
+ }
850
+ function attachPluginBridgeErrorContext(req, res, err, bridgeError, metadata) {
851
+ const rootError = err instanceof Error ? err : new Error(String(err));
852
+ res.__errorContext = {
853
+ error: {
854
+ message: bridgeError.message,
855
+ stack: rootError.stack,
856
+ name: rootError.name,
857
+ details: {
858
+ ...metadata,
859
+ bridgeCode: bridgeError.code,
860
+ bridgeDetails: bridgeError.details,
861
+ },
862
+ },
863
+ method: req.method,
864
+ url: req.originalUrl,
865
+ reqBody: req.body,
866
+ reqParams: req.params,
867
+ reqQuery: req.query,
868
+ };
869
+ res.err = rootError;
870
+ }
871
+ /**
872
+ * POST /api/plugins/:pluginId/bridge/data
873
+ *
874
+ * Proxy a `getData` call from the plugin UI to the plugin worker.
875
+ *
876
+ * This is the server-side half of the `usePluginData(key, params)` bridge hook.
877
+ * The frontend sends a POST with the data key and optional params; the host
878
+ * forwards the call to the worker via the `getData` RPC method and returns
879
+ * the result.
880
+ *
881
+ * Request body:
882
+ * - `key`: Plugin-defined data key (e.g. `"sync-health"`)
883
+ * - `params`: Optional query parameters forwarded to the worker handler
884
+ *
885
+ * Response: The raw result from the worker's `getData` handler
886
+ *
887
+ * Error response body follows the `PluginBridgeError` shape:
888
+ * `{ code: PluginBridgeErrorCode, message: string, details?: unknown }`
889
+ *
890
+ * Errors:
891
+ * - 400 if request validation fails
892
+ * - 404 if plugin not found
893
+ * - 501 if bridge deps are not configured
894
+ * - 502 if the worker is unavailable or returns an error
895
+ *
896
+ * @see PLUGIN_SPEC.md §13.8 — `getData`
897
+ * @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
898
+ */
899
+ router.post("/plugins/:pluginId/bridge/data", async (req, res) => {
900
+ assertOperatorOrgAccess(req);
901
+ if (!bridgeDeps) {
902
+ res.status(501).json({ error: "Plugin bridge is not enabled" });
903
+ return;
904
+ }
905
+ const { pluginId } = req.params;
906
+ // Resolve plugin
907
+ const plugin = await resolvePlugin(registry, pluginId);
908
+ if (!plugin) {
909
+ res.status(404).json({ error: "Plugin not found" });
910
+ return;
911
+ }
912
+ // Validate plugin is in ready state
913
+ if (plugin.status !== "ready") {
914
+ const bridgeError = {
915
+ code: "WORKER_UNAVAILABLE",
916
+ message: `Plugin is not ready (current status: ${plugin.status})`,
917
+ };
918
+ attachPluginBridgeErrorContext(req, res, new Error(bridgeError.message), bridgeError, {
919
+ pluginId: plugin.id,
920
+ pluginKey: plugin.pluginKey,
921
+ bridgeMethod: "getData",
922
+ });
923
+ res.status(502).json(bridgeError);
924
+ return;
925
+ }
926
+ // Validate request body
927
+ const body = req.body;
928
+ if (!body || !body.key || typeof body.key !== "string") {
929
+ res.status(400).json({ error: '"key" is required and must be a string' });
930
+ return;
931
+ }
932
+ const squadId = assertPluginBridgeScope(req, body.squadId);
933
+ try {
934
+ const result = await bridgeDeps.workerManager.call(plugin.id, "getData", {
935
+ key: body.key,
936
+ ...(squadId ? { squadId } : {}),
937
+ params: body.params ?? {},
938
+ renderEnvironment: body.renderEnvironment ?? null,
939
+ });
940
+ res.json({ data: result });
941
+ }
942
+ catch (err) {
943
+ const bridgeError = mapRpcErrorToBridgeError(err);
944
+ attachPluginBridgeErrorContext(req, res, err, bridgeError, {
945
+ pluginId: plugin.id,
946
+ pluginKey: plugin.pluginKey,
947
+ bridgeMethod: "getData",
948
+ dataKey: body.key,
949
+ });
950
+ res.status(502).json(bridgeError);
951
+ }
952
+ });
953
+ /**
954
+ * POST /api/plugins/:pluginId/bridge/action
955
+ *
956
+ * Proxy a `performAction` call from the plugin UI to the plugin worker.
957
+ *
958
+ * This is the server-side half of the `usePluginAction(key)` bridge hook.
959
+ * The frontend sends a POST with the action key and optional params; the host
960
+ * forwards the call to the worker via the `performAction` RPC method and
961
+ * returns the result.
962
+ *
963
+ * Request body:
964
+ * - `key`: Plugin-defined action key (e.g. `"resync"`)
965
+ * - `params`: Optional parameters forwarded to the worker handler
966
+ *
967
+ * Response: The raw result from the worker's `performAction` handler
968
+ *
969
+ * Error response body follows the `PluginBridgeError` shape:
970
+ * `{ code: PluginBridgeErrorCode, message: string, details?: unknown }`
971
+ *
972
+ * Errors:
973
+ * - 400 if request validation fails
974
+ * - 404 if plugin not found
975
+ * - 501 if bridge deps are not configured
976
+ * - 502 if the worker is unavailable or returns an error
977
+ *
978
+ * @see PLUGIN_SPEC.md §13.9 — `performAction`
979
+ * @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
980
+ */
981
+ router.post("/plugins/:pluginId/bridge/action", async (req, res) => {
982
+ assertAuthenticated(req);
983
+ if (!bridgeDeps) {
984
+ res.status(501).json({ error: "Plugin bridge is not enabled" });
985
+ return;
986
+ }
987
+ const { pluginId } = req.params;
988
+ // Resolve plugin
989
+ const plugin = await resolvePlugin(registry, pluginId);
990
+ if (!plugin) {
991
+ res.status(404).json({ error: "Plugin not found" });
992
+ return;
993
+ }
994
+ // Validate plugin is in ready state
995
+ if (plugin.status !== "ready") {
996
+ const bridgeError = {
997
+ code: "WORKER_UNAVAILABLE",
998
+ message: `Plugin is not ready (current status: ${plugin.status})`,
999
+ };
1000
+ attachPluginBridgeErrorContext(req, res, new Error(bridgeError.message), bridgeError, {
1001
+ pluginId: plugin.id,
1002
+ pluginKey: plugin.pluginKey,
1003
+ bridgeMethod: "performAction",
1004
+ });
1005
+ res.status(502).json(bridgeError);
1006
+ return;
1007
+ }
1008
+ // Validate request body
1009
+ const body = req.body;
1010
+ if (!body || !body.key || typeof body.key !== "string") {
1011
+ res.status(400).json({ error: '"key" is required and must be a string' });
1012
+ return;
1013
+ }
1014
+ const squadId = assertPluginBridgeScope(req, body.squadId);
1015
+ try {
1016
+ const result = await bridgeDeps.workerManager.call(plugin.id, "performAction", {
1017
+ key: body.key,
1018
+ params: actionParamsWithAuthorizedSquadScope(body.params, squadId),
1019
+ actorContext: performActionActorContext(req, squadId),
1020
+ renderEnvironment: body.renderEnvironment ?? null,
1021
+ });
1022
+ res.json({ data: result });
1023
+ }
1024
+ catch (err) {
1025
+ const bridgeError = mapRpcErrorToBridgeError(err);
1026
+ attachPluginBridgeErrorContext(req, res, err, bridgeError, {
1027
+ pluginId: plugin.id,
1028
+ pluginKey: plugin.pluginKey,
1029
+ bridgeMethod: "performAction",
1030
+ actionKey: body.key,
1031
+ });
1032
+ res.status(502).json(bridgeError);
1033
+ }
1034
+ });
1035
+ // ===========================================================================
1036
+ // URL-keyed bridge routes (key as path parameter)
1037
+ // ===========================================================================
1038
+ /**
1039
+ * POST /api/plugins/:pluginId/data/:key
1040
+ *
1041
+ * Proxy a `getData` call from the plugin UI to the plugin worker, with the
1042
+ * data key specified as a URL path parameter instead of in the request body.
1043
+ *
1044
+ * This is a REST-friendly alternative to `POST /plugins/:pluginId/bridge/data`.
1045
+ * The frontend bridge hooks use this endpoint for cleaner URLs.
1046
+ *
1047
+ * Request body (optional):
1048
+ * - `params`: Optional query parameters forwarded to the worker handler
1049
+ *
1050
+ * Response: The raw result from the worker's `getData` handler wrapped as `{ data: T }`
1051
+ *
1052
+ * Error response body follows the `PluginBridgeError` shape:
1053
+ * `{ code: PluginBridgeErrorCode, message: string, details?: unknown }`
1054
+ *
1055
+ * Errors:
1056
+ * - 404 if plugin not found
1057
+ * - 501 if bridge deps are not configured
1058
+ * - 502 if the worker is unavailable or returns an error
1059
+ *
1060
+ * @see PLUGIN_SPEC.md §13.8 — `getData`
1061
+ * @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
1062
+ */
1063
+ router.post("/plugins/:pluginId/data/:key", async (req, res) => {
1064
+ assertOperatorOrgAccess(req);
1065
+ if (!bridgeDeps) {
1066
+ res.status(501).json({ error: "Plugin bridge is not enabled" });
1067
+ return;
1068
+ }
1069
+ const { pluginId, key } = req.params;
1070
+ // Resolve plugin
1071
+ const plugin = await resolvePlugin(registry, pluginId);
1072
+ if (!plugin) {
1073
+ res.status(404).json({ error: "Plugin not found" });
1074
+ return;
1075
+ }
1076
+ // Validate plugin is in ready state
1077
+ if (plugin.status !== "ready") {
1078
+ const bridgeError = {
1079
+ code: "WORKER_UNAVAILABLE",
1080
+ message: `Plugin is not ready (current status: ${plugin.status})`,
1081
+ };
1082
+ attachPluginBridgeErrorContext(req, res, new Error(bridgeError.message), bridgeError, {
1083
+ pluginId: plugin.id,
1084
+ pluginKey: plugin.pluginKey,
1085
+ bridgeMethod: "getData",
1086
+ dataKey: key,
1087
+ });
1088
+ res.status(502).json(bridgeError);
1089
+ return;
1090
+ }
1091
+ const body = req.body;
1092
+ const squadId = assertPluginBridgeScope(req, body?.squadId);
1093
+ try {
1094
+ const result = await bridgeDeps.workerManager.call(plugin.id, "getData", {
1095
+ key,
1096
+ ...(squadId ? { squadId } : {}),
1097
+ params: body?.params ?? {},
1098
+ renderEnvironment: body?.renderEnvironment ?? null,
1099
+ });
1100
+ res.json({ data: result });
1101
+ }
1102
+ catch (err) {
1103
+ const bridgeError = mapRpcErrorToBridgeError(err);
1104
+ attachPluginBridgeErrorContext(req, res, err, bridgeError, {
1105
+ pluginId: plugin.id,
1106
+ pluginKey: plugin.pluginKey,
1107
+ bridgeMethod: "getData",
1108
+ dataKey: key,
1109
+ });
1110
+ res.status(502).json(bridgeError);
1111
+ }
1112
+ });
1113
+ /**
1114
+ * POST /api/plugins/:pluginId/actions/:key
1115
+ *
1116
+ * Proxy a `performAction` call from the plugin UI to the plugin worker, with
1117
+ * the action key specified as a URL path parameter instead of in the request body.
1118
+ *
1119
+ * This is a REST-friendly alternative to `POST /plugins/:pluginId/bridge/action`.
1120
+ * The frontend bridge hooks use this endpoint for cleaner URLs.
1121
+ *
1122
+ * Request body (optional):
1123
+ * - `params`: Optional parameters forwarded to the worker handler
1124
+ *
1125
+ * Response: The raw result from the worker's `performAction` handler wrapped as `{ data: T }`
1126
+ *
1127
+ * Error response body follows the `PluginBridgeError` shape:
1128
+ * `{ code: PluginBridgeErrorCode, message: string, details?: unknown }`
1129
+ *
1130
+ * Errors:
1131
+ * - 404 if plugin not found
1132
+ * - 501 if bridge deps are not configured
1133
+ * - 502 if the worker is unavailable or returns an error
1134
+ *
1135
+ * @see PLUGIN_SPEC.md §13.9 — `performAction`
1136
+ * @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
1137
+ */
1138
+ router.post("/plugins/:pluginId/actions/:key", async (req, res) => {
1139
+ assertAuthenticated(req);
1140
+ if (!bridgeDeps) {
1141
+ res.status(501).json({ error: "Plugin bridge is not enabled" });
1142
+ return;
1143
+ }
1144
+ const { pluginId, key } = req.params;
1145
+ // Resolve plugin
1146
+ const plugin = await resolvePlugin(registry, pluginId);
1147
+ if (!plugin) {
1148
+ res.status(404).json({ error: "Plugin not found" });
1149
+ return;
1150
+ }
1151
+ // Validate plugin is in ready state
1152
+ if (plugin.status !== "ready") {
1153
+ const bridgeError = {
1154
+ code: "WORKER_UNAVAILABLE",
1155
+ message: `Plugin is not ready (current status: ${plugin.status})`,
1156
+ };
1157
+ attachPluginBridgeErrorContext(req, res, new Error(bridgeError.message), bridgeError, {
1158
+ pluginId: plugin.id,
1159
+ pluginKey: plugin.pluginKey,
1160
+ bridgeMethod: "performAction",
1161
+ actionKey: key,
1162
+ });
1163
+ res.status(502).json(bridgeError);
1164
+ return;
1165
+ }
1166
+ const body = req.body;
1167
+ const squadId = assertPluginBridgeScope(req, body?.squadId);
1168
+ try {
1169
+ const result = await bridgeDeps.workerManager.call(plugin.id, "performAction", {
1170
+ key,
1171
+ params: actionParamsWithAuthorizedSquadScope(body?.params, squadId),
1172
+ actorContext: performActionActorContext(req, squadId),
1173
+ renderEnvironment: body?.renderEnvironment ?? null,
1174
+ });
1175
+ res.json({ data: result });
1176
+ }
1177
+ catch (err) {
1178
+ const bridgeError = mapRpcErrorToBridgeError(err);
1179
+ attachPluginBridgeErrorContext(req, res, err, bridgeError, {
1180
+ pluginId: plugin.id,
1181
+ pluginKey: plugin.pluginKey,
1182
+ bridgeMethod: "performAction",
1183
+ actionKey: key,
1184
+ });
1185
+ res.status(502).json(bridgeError);
1186
+ }
1187
+ });
1188
+ // ===========================================================================
1189
+ // SSE stream bridge route
1190
+ // ===========================================================================
1191
+ /**
1192
+ * GET /api/plugins/:pluginId/bridge/stream/:channel
1193
+ *
1194
+ * Server-Sent Events endpoint for real-time streaming from plugin worker to UI.
1195
+ *
1196
+ * The worker pushes events via `ctx.streams.emit(channel, event)` which arrive
1197
+ * as JSON-RPC notifications to the host, get published on the PluginStreamBus,
1198
+ * and are fanned out to all connected SSE clients matching (pluginId, channel,
1199
+ * squadId).
1200
+ *
1201
+ * Query parameters:
1202
+ * - `squadId` (required): Scope events to a specific squad
1203
+ *
1204
+ * SSE event types:
1205
+ * - `message`: A data event from the worker (default)
1206
+ * - `open`: The worker opened the stream channel
1207
+ * - `close`: The worker closed the stream channel — client should disconnect
1208
+ *
1209
+ * Errors:
1210
+ * - 400 if squadId is missing
1211
+ * - 404 if plugin not found
1212
+ * - 501 if bridge deps or stream bus are not configured
1213
+ */
1214
+ router.get("/plugins/:pluginId/bridge/stream/:channel", async (req, res) => {
1215
+ assertOperatorOrgAccess(req);
1216
+ if (!bridgeDeps?.streamBus) {
1217
+ res.status(501).json({ error: "Plugin stream bridge is not enabled" });
1218
+ return;
1219
+ }
1220
+ const { pluginId, channel } = req.params;
1221
+ const squadId = req.query.squadId;
1222
+ if (!squadId) {
1223
+ res.status(400).json({ error: '"squadId" query parameter is required' });
1224
+ return;
1225
+ }
1226
+ const plugin = await resolvePlugin(registry, pluginId);
1227
+ if (!plugin) {
1228
+ res.status(404).json({ error: "Plugin not found" });
1229
+ return;
1230
+ }
1231
+ assertSquadAccess(req, squadId);
1232
+ // Set SSE headers
1233
+ res.writeHead(200, {
1234
+ "Content-Type": "text/event-stream",
1235
+ "Cache-Control": "no-cache",
1236
+ "Connection": "keep-alive",
1237
+ "X-Accel-Buffering": "no",
1238
+ });
1239
+ res.flushHeaders();
1240
+ // Send initial comment to establish the connection
1241
+ res.write(":ok\n\n");
1242
+ let unsubscribed = false;
1243
+ const safeUnsubscribe = () => {
1244
+ if (!unsubscribed) {
1245
+ unsubscribed = true;
1246
+ unsubscribe();
1247
+ }
1248
+ };
1249
+ const unsubscribe = bridgeDeps.streamBus.subscribe(plugin.id, channel, squadId, (event, eventType) => {
1250
+ if (unsubscribed || !res.writable)
1251
+ return;
1252
+ try {
1253
+ if (eventType !== "message") {
1254
+ res.write(`event: ${eventType}\n`);
1255
+ }
1256
+ res.write(`data: ${JSON.stringify(event)}\n\n`);
1257
+ }
1258
+ catch {
1259
+ // Connection closed or write error — stop delivering
1260
+ safeUnsubscribe();
1261
+ }
1262
+ });
1263
+ req.on("close", safeUnsubscribe);
1264
+ res.on("error", safeUnsubscribe);
1265
+ });
1266
+ router.use("/plugins/:pluginId/api", async (req, res) => {
1267
+ if (!bridgeDeps) {
1268
+ res.status(501).json({ error: "Plugin scoped API routes are not enabled" });
1269
+ return;
1270
+ }
1271
+ const { pluginId } = req.params;
1272
+ const plugin = await resolvePlugin(registry, pluginId);
1273
+ if (!plugin) {
1274
+ res.status(404).json({ error: "Plugin not found" });
1275
+ return;
1276
+ }
1277
+ if (plugin.status !== "ready") {
1278
+ res.status(503).json({ error: `Plugin is not ready (current status: ${plugin.status})` });
1279
+ return;
1280
+ }
1281
+ const isWorkerRunning = typeof bridgeDeps.workerManager.isRunning === "function"
1282
+ ? bridgeDeps.workerManager.isRunning(plugin.id)
1283
+ : true;
1284
+ if (!isWorkerRunning) {
1285
+ res.status(503).json({ error: "Plugin worker is not running" });
1286
+ return;
1287
+ }
1288
+ if (!plugin.manifestJson.capabilities.includes("api.routes.register")) {
1289
+ res.status(404).json({ error: "Plugin does not expose scoped API routes" });
1290
+ return;
1291
+ }
1292
+ const requestPath = req.path || "/";
1293
+ const routes = plugin.manifestJson.apiRoutes ?? [];
1294
+ const match = routes
1295
+ .map((route) => ({ route, params: matchScopedApiRoute(route, req.method, requestPath) }))
1296
+ .find((candidate) => candidate.params !== null);
1297
+ if (!match || !match.params) {
1298
+ res.status(404).json({ error: "Plugin API route not found" });
1299
+ return;
1300
+ }
1301
+ try {
1302
+ assertScopedApiAuth(req, match.route);
1303
+ const squadId = await resolveScopedApiSquadId(match.route, match.params, req);
1304
+ if (!squadId) {
1305
+ res.status(400).json({ error: "Unable to resolve squad for plugin API route" });
1306
+ return;
1307
+ }
1308
+ assertSquadAccess(req, squadId);
1309
+ await enforceScopedApiCheckout(req, match.route, match.params, squadId);
1310
+ if (req.method !== "GET" && req.headers["content-type"] && !req.is("application/json")) {
1311
+ res.status(415).json({ error: "Plugin API routes accept JSON requests only" });
1312
+ return;
1313
+ }
1314
+ const requestBody = req.body ?? null;
1315
+ const bodySize = Buffer.byteLength(JSON.stringify(requestBody));
1316
+ if (bodySize > PLUGIN_API_BODY_LIMIT_BYTES) {
1317
+ res.status(413).json({ error: "Plugin API request body is too large" });
1318
+ return;
1319
+ }
1320
+ const actor = getActorInfo(req);
1321
+ const input = {
1322
+ routeKey: match.route.routeKey,
1323
+ method: req.method,
1324
+ path: requestPath,
1325
+ params: match.params,
1326
+ query: normalizeQuery(req.query),
1327
+ body: requestBody,
1328
+ actor: {
1329
+ actorType: actor.actorType,
1330
+ actorId: actor.actorId,
1331
+ agentId: actor.agentId,
1332
+ userId: actor.actorType === "user" ? actor.actorId : null,
1333
+ runId: actor.runId,
1334
+ },
1335
+ squadId,
1336
+ headers: sanitizePluginRequestHeaders(req),
1337
+ };
1338
+ const result = await bridgeDeps.workerManager.call(plugin.id, "handleApiRequest", input);
1339
+ const status = Number.isInteger(result.status) && Number(result.status) >= 200 && Number(result.status) <= 599
1340
+ ? Number(result.status)
1341
+ : 200;
1342
+ applyPluginScopedApiResponseHeaders(res, result.headers);
1343
+ if (status === 204) {
1344
+ res.status(status).end();
1345
+ }
1346
+ else {
1347
+ res.status(status).json(result.body ?? null);
1348
+ }
1349
+ }
1350
+ catch (err) {
1351
+ const status = typeof err.status === "number"
1352
+ ? err.status
1353
+ : err instanceof JsonRpcCallError && (err.code === PLUGIN_RPC_ERROR_CODES.CAPABILITY_DENIED ||
1354
+ err.code === PLUGIN_RPC_ERROR_CODES.INVOCATION_SCOPE_DENIED)
1355
+ ? 403
1356
+ : err instanceof JsonRpcCallError && err.code === PLUGIN_RPC_ERROR_CODES.METHOD_NOT_IMPLEMENTED
1357
+ ? 501
1358
+ : err instanceof JsonRpcCallError
1359
+ ? 502
1360
+ : 500;
1361
+ res.status(status).json({
1362
+ error: err instanceof Error ? err.message : String(err),
1363
+ });
1364
+ }
1365
+ });
1366
+ /**
1367
+ * GET /api/plugins/:pluginId
1368
+ *
1369
+ * Get detailed information about a single plugin.
1370
+ *
1371
+ * The :pluginId parameter accepts either:
1372
+ * - Database UUID (e.g., "abc123-def456")
1373
+ * - Plugin key (e.g., "acme.linear")
1374
+ *
1375
+ * Response: PluginRecord
1376
+ * Errors: 404 if plugin not found
1377
+ */
1378
+ router.get("/plugins/:pluginId", async (req, res) => {
1379
+ assertOperatorOrgAccess(req);
1380
+ const { pluginId } = req.params;
1381
+ const plugin = await resolvePlugin(registry, pluginId);
1382
+ if (!plugin) {
1383
+ res.status(404).json({ error: "Plugin not found" });
1384
+ return;
1385
+ }
1386
+ // Enrich with worker capabilities when available
1387
+ const worker = bridgeDeps?.workerManager.getWorker(plugin.id);
1388
+ const supportsConfigTest = worker
1389
+ ? worker.supportedMethods.includes("validateConfig")
1390
+ : false;
1391
+ res.json({ ...plugin, supportsConfigTest });
1392
+ });
1393
+ /**
1394
+ * DELETE /api/plugins/:pluginId
1395
+ *
1396
+ * Uninstall a plugin.
1397
+ *
1398
+ * Query params:
1399
+ * - purge: If "true", permanently delete all plugin data (hard delete)
1400
+ * Otherwise, soft-delete with 30-day data retention
1401
+ *
1402
+ * Response: PluginRecord (the deleted record)
1403
+ * Errors: 404 if plugin not found, 400 for lifecycle errors
1404
+ */
1405
+ router.delete("/plugins/:pluginId", async (req, res) => {
1406
+ assertInstanceAdmin(req);
1407
+ const { pluginId } = req.params;
1408
+ const purge = req.query.purge === "true";
1409
+ const plugin = await resolvePlugin(registry, pluginId);
1410
+ if (!plugin) {
1411
+ res.status(404).json({ error: "Plugin not found" });
1412
+ return;
1413
+ }
1414
+ try {
1415
+ const result = await lifecycle.unload(plugin.id, purge);
1416
+ await logPluginMutationActivity(req, "plugin.uninstalled", plugin.id, {
1417
+ pluginId: plugin.id,
1418
+ pluginKey: plugin.pluginKey,
1419
+ purge,
1420
+ });
1421
+ publishGlobalLiveEvent({ type: "plugin.ui.updated", payload: { pluginId: plugin.id, action: "uninstalled" } });
1422
+ res.json(result);
1423
+ }
1424
+ catch (err) {
1425
+ const message = err instanceof Error ? err.message : String(err);
1426
+ res.status(400).json({ error: message });
1427
+ }
1428
+ });
1429
+ /**
1430
+ * POST /api/plugins/:pluginId/enable
1431
+ *
1432
+ * Enable a plugin that is currently disabled or in error state.
1433
+ *
1434
+ * Transitions the plugin to 'ready' state after loading and validation.
1435
+ *
1436
+ * Response: PluginRecord
1437
+ * Errors: 404 if plugin not found, 400 for lifecycle errors
1438
+ */
1439
+ router.post("/plugins/:pluginId/enable", async (req, res) => {
1440
+ assertInstanceAdmin(req);
1441
+ const { pluginId } = req.params;
1442
+ const plugin = await resolvePlugin(registry, pluginId);
1443
+ if (!plugin) {
1444
+ res.status(404).json({ error: "Plugin not found" });
1445
+ return;
1446
+ }
1447
+ try {
1448
+ const result = await lifecycle.enable(plugin.id);
1449
+ await logPluginMutationActivity(req, "plugin.enabled", plugin.id, {
1450
+ pluginId: plugin.id,
1451
+ pluginKey: plugin.pluginKey,
1452
+ version: result?.version ?? plugin.version,
1453
+ });
1454
+ publishGlobalLiveEvent({ type: "plugin.ui.updated", payload: { pluginId: plugin.id, action: "enabled" } });
1455
+ res.json(result);
1456
+ }
1457
+ catch (err) {
1458
+ const message = err instanceof Error ? err.message : String(err);
1459
+ res.status(400).json({ error: message });
1460
+ }
1461
+ });
1462
+ /**
1463
+ * POST /api/plugins/:pluginId/disable
1464
+ *
1465
+ * Disable a running plugin.
1466
+ *
1467
+ * Request body (optional):
1468
+ * - reason: Human-readable reason for disabling
1469
+ *
1470
+ * The plugin transitions to 'installed' state and stops processing events.
1471
+ *
1472
+ * Response: PluginRecord
1473
+ * Errors: 404 if plugin not found, 400 for lifecycle errors
1474
+ */
1475
+ router.post("/plugins/:pluginId/disable", async (req, res) => {
1476
+ assertInstanceAdmin(req);
1477
+ const { pluginId } = req.params;
1478
+ const body = req.body;
1479
+ const reason = body?.reason;
1480
+ const plugin = await resolvePlugin(registry, pluginId);
1481
+ if (!plugin) {
1482
+ res.status(404).json({ error: "Plugin not found" });
1483
+ return;
1484
+ }
1485
+ try {
1486
+ const result = await lifecycle.disable(plugin.id, reason);
1487
+ await logPluginMutationActivity(req, "plugin.disabled", plugin.id, {
1488
+ pluginId: plugin.id,
1489
+ pluginKey: plugin.pluginKey,
1490
+ reason: reason ?? null,
1491
+ });
1492
+ publishGlobalLiveEvent({ type: "plugin.ui.updated", payload: { pluginId: plugin.id, action: "disabled" } });
1493
+ res.json(result);
1494
+ }
1495
+ catch (err) {
1496
+ const message = err instanceof Error ? err.message : String(err);
1497
+ res.status(400).json({ error: message });
1498
+ }
1499
+ });
1500
+ /**
1501
+ * GET /api/plugins/:pluginId/health
1502
+ *
1503
+ * Run health diagnostics on a plugin.
1504
+ *
1505
+ * Performs the following checks:
1506
+ * 1. Registry: Plugin is registered in the database
1507
+ * 2. Manifest: Manifest is valid and parseable
1508
+ * 3. Status: Plugin is in 'ready' state
1509
+ * 4. Error state: Plugin has no unhandled errors
1510
+ *
1511
+ * Response: PluginHealthCheckResult
1512
+ * Errors: 404 if plugin not found
1513
+ */
1514
+ router.get("/plugins/:pluginId/health", async (req, res) => {
1515
+ assertOperatorOrgAccess(req);
1516
+ const { pluginId } = req.params;
1517
+ const plugin = await resolvePlugin(registry, pluginId);
1518
+ if (!plugin) {
1519
+ res.status(404).json({ error: "Plugin not found" });
1520
+ return;
1521
+ }
1522
+ const checks = [];
1523
+ // Check 1: Plugin is registered
1524
+ checks.push({
1525
+ name: "registry",
1526
+ passed: true,
1527
+ message: "Plugin found in registry",
1528
+ });
1529
+ // Check 2: Manifest is valid
1530
+ const hasValidManifest = Boolean(plugin.manifestJson?.id);
1531
+ checks.push({
1532
+ name: "manifest",
1533
+ passed: hasValidManifest,
1534
+ message: hasValidManifest ? "Manifest is valid" : "Manifest is invalid or missing",
1535
+ });
1536
+ // Check 3: Plugin status
1537
+ const isHealthy = plugin.status === "ready";
1538
+ checks.push({
1539
+ name: "status",
1540
+ passed: isHealthy,
1541
+ message: `Current status: ${plugin.status}`,
1542
+ });
1543
+ // Check 4: No last error
1544
+ const hasNoError = !plugin.lastError;
1545
+ if (!hasNoError) {
1546
+ checks.push({
1547
+ name: "error_state",
1548
+ passed: false,
1549
+ message: plugin.lastError ?? undefined,
1550
+ });
1551
+ }
1552
+ const result = {
1553
+ pluginId: plugin.id,
1554
+ status: plugin.status,
1555
+ healthy: isHealthy && hasValidManifest && hasNoError,
1556
+ checks,
1557
+ lastError: plugin.lastError ?? undefined,
1558
+ };
1559
+ res.json(result);
1560
+ });
1561
+ /**
1562
+ * GET /api/plugins/:pluginId/logs
1563
+ *
1564
+ * Query recent log entries for a plugin.
1565
+ *
1566
+ * Query params:
1567
+ * - limit: Maximum number of entries (default 25, max 500)
1568
+ * - level: Filter by log level (info, warn, error, debug)
1569
+ * - since: ISO timestamp to filter logs newer than this time
1570
+ *
1571
+ * Response: Array of log entries, newest first.
1572
+ */
1573
+ router.get("/plugins/:pluginId/logs", async (req, res) => {
1574
+ assertOperatorOrgAccess(req);
1575
+ const { pluginId } = req.params;
1576
+ const plugin = await resolvePlugin(registry, pluginId);
1577
+ if (!plugin) {
1578
+ res.status(404).json({ error: "Plugin not found" });
1579
+ return;
1580
+ }
1581
+ const limit = Math.min(Math.max(parseInt(req.query.limit, 10) || 25, 1), 500);
1582
+ const level = req.query.level;
1583
+ const since = req.query.since;
1584
+ const conditions = [eq(pluginLogs.pluginId, plugin.id)];
1585
+ if (level) {
1586
+ conditions.push(eq(pluginLogs.level, level));
1587
+ }
1588
+ if (since) {
1589
+ const sinceDate = new Date(since);
1590
+ if (!isNaN(sinceDate.getTime())) {
1591
+ conditions.push(gte(pluginLogs.createdAt, sinceDate));
1592
+ }
1593
+ }
1594
+ const rows = await db
1595
+ .select()
1596
+ .from(pluginLogs)
1597
+ .where(and(...conditions))
1598
+ .orderBy(desc(pluginLogs.createdAt))
1599
+ .limit(limit);
1600
+ res.json(rows);
1601
+ });
1602
+ /**
1603
+ * POST /api/plugins/:pluginId/upgrade
1604
+ *
1605
+ * Upgrade a plugin to a newer version.
1606
+ *
1607
+ * Upgrades are restricted to instance admins because they fetch and inspect
1608
+ * new package contents on the host before activation.
1609
+ *
1610
+ * Request body (optional):
1611
+ * - version: Target version (defaults to latest)
1612
+ *
1613
+ * If the upgrade adds new capabilities, the plugin transitions to
1614
+ * 'upgrade_pending' state for operator approval. Otherwise, it goes
1615
+ * directly to 'ready'.
1616
+ *
1617
+ * Response: PluginRecord
1618
+ * Errors: 404 if plugin not found, 400 for lifecycle errors
1619
+ */
1620
+ router.post("/plugins/:pluginId/upgrade", async (req, res) => {
1621
+ assertInstanceAdmin(req);
1622
+ const { pluginId } = req.params;
1623
+ const body = req.body;
1624
+ const version = body?.version;
1625
+ const plugin = await resolvePlugin(registry, pluginId);
1626
+ if (!plugin) {
1627
+ res.status(404).json({ error: "Plugin not found" });
1628
+ return;
1629
+ }
1630
+ try {
1631
+ // Upgrade the plugin - this would typically:
1632
+ // 1. Download the new version
1633
+ // 2. Compare capabilities
1634
+ // 3. If new capabilities, mark as upgrade_pending
1635
+ // 4. Otherwise, transition to ready
1636
+ const result = await lifecycle.upgrade(plugin.id, version);
1637
+ await logPluginMutationActivity(req, "plugin.upgraded", plugin.id, {
1638
+ pluginId: plugin.id,
1639
+ pluginKey: plugin.pluginKey,
1640
+ previousVersion: plugin.version,
1641
+ version: result?.version ?? plugin.version,
1642
+ targetVersion: version ?? null,
1643
+ });
1644
+ publishGlobalLiveEvent({ type: "plugin.ui.updated", payload: { pluginId: plugin.id, action: "upgraded" } });
1645
+ res.json(result);
1646
+ }
1647
+ catch (err) {
1648
+ const message = err instanceof Error ? err.message : String(err);
1649
+ res.status(400).json({ error: message });
1650
+ }
1651
+ });
1652
+ // ===========================================================================
1653
+ // Plugin configuration routes
1654
+ // ===========================================================================
1655
+ /**
1656
+ * GET /api/plugins/:pluginId/config
1657
+ *
1658
+ * Retrieve the current instance configuration for a plugin.
1659
+ *
1660
+ * Returns the `PluginConfig` record if one exists, or `null` if the plugin
1661
+ * has not yet been configured.
1662
+ *
1663
+ * Response: `PluginConfig | null`
1664
+ * Errors: 404 if plugin not found
1665
+ */
1666
+ router.get("/plugins/:pluginId/config", async (req, res) => {
1667
+ assertOperatorOrgAccess(req);
1668
+ const { pluginId } = req.params;
1669
+ const plugin = await resolvePlugin(registry, pluginId);
1670
+ if (!plugin) {
1671
+ res.status(404).json({ error: "Plugin not found" });
1672
+ return;
1673
+ }
1674
+ const config = await registry.getConfig(plugin.id);
1675
+ res.json(config);
1676
+ });
1677
+ /**
1678
+ * POST /api/plugins/:pluginId/config
1679
+ *
1680
+ * Save (create or replace) the instance configuration for a plugin.
1681
+ *
1682
+ * The caller provides the full `configJson` object. The server persists it
1683
+ * via `registry.upsertConfig()`.
1684
+ *
1685
+ * Request body:
1686
+ * - `configJson`: Configuration values matching the plugin's `instanceConfigSchema`
1687
+ *
1688
+ * Response: `PluginConfig`
1689
+ * Errors:
1690
+ * - 400 if request validation fails
1691
+ * - 404 if plugin not found
1692
+ */
1693
+ router.post("/plugins/:pluginId/config", async (req, res) => {
1694
+ assertInstanceAdmin(req);
1695
+ const { pluginId } = req.params;
1696
+ const plugin = await resolvePlugin(registry, pluginId);
1697
+ if (!plugin) {
1698
+ res.status(404).json({ error: "Plugin not found" });
1699
+ return;
1700
+ }
1701
+ const body = req.body;
1702
+ if (!body?.configJson || typeof body.configJson !== "object") {
1703
+ res.status(400).json({ error: '"configJson" is required and must be an object' });
1704
+ return;
1705
+ }
1706
+ // Strip devUiUrl unless the caller is an instance admin. devUiUrl activates
1707
+ // a dev-proxy in the static file route that could be abused for SSRF if any
1708
+ // operator-level user were allowed to set it.
1709
+ if ("devUiUrl" in body.configJson &&
1710
+ !(req.actor.type === "operator" && req.actor.isInstanceAdmin)) {
1711
+ delete body.configJson.devUiUrl;
1712
+ }
1713
+ // Validate configJson against the plugin's instanceConfigSchema (if declared).
1714
+ // This ensures CLI/API callers get the same validation the UI performs client-side.
1715
+ const schema = plugin.manifestJson?.instanceConfigSchema;
1716
+ if (schema && Object.keys(schema).length > 0) {
1717
+ const validation = validateInstanceConfig(body.configJson, schema);
1718
+ if (!validation.valid) {
1719
+ res.status(400).json({
1720
+ error: "Configuration does not match the plugin's instanceConfigSchema",
1721
+ fieldErrors: validation.errors,
1722
+ });
1723
+ return;
1724
+ }
1725
+ }
1726
+ try {
1727
+ const secretRefsByPath = extractSecretRefPathsFromConfig(body.configJson, schema);
1728
+ if (secretRefsByPath.size > 0) {
1729
+ res.status(422).json({ error: PLUGIN_SECRET_REFS_DISABLED_MESSAGE });
1730
+ return;
1731
+ }
1732
+ const result = await registry.upsertConfig(plugin.id, {
1733
+ configJson: body.configJson,
1734
+ });
1735
+ await logPluginMutationActivity(req, "plugin.config.updated", plugin.id, {
1736
+ pluginId: plugin.id,
1737
+ pluginKey: plugin.pluginKey,
1738
+ configKeyCount: Object.keys(body.configJson).length,
1739
+ });
1740
+ // Notify the running worker about the config change (PLUGIN_SPEC §25.4.4).
1741
+ // If the worker implements onConfigChanged, send the new config via RPC.
1742
+ // If it doesn't (METHOD_NOT_IMPLEMENTED), restart the worker so it picks
1743
+ // up the new config on re-initialize. If no worker is running, skip.
1744
+ if (bridgeDeps?.workerManager.isRunning(plugin.id)) {
1745
+ try {
1746
+ await bridgeDeps.workerManager.call(plugin.id, "configChanged", { config: body.configJson });
1747
+ }
1748
+ catch (rpcErr) {
1749
+ if (rpcErr instanceof JsonRpcCallError &&
1750
+ rpcErr.code === PLUGIN_RPC_ERROR_CODES.METHOD_NOT_IMPLEMENTED) {
1751
+ // Worker doesn't handle live config — restart it.
1752
+ try {
1753
+ await lifecycle.restartWorker(plugin.id);
1754
+ }
1755
+ catch {
1756
+ // Restart failure is non-fatal for the config save response.
1757
+ }
1758
+ }
1759
+ // Other RPC errors (timeout, unavailable) are non-fatal — config is
1760
+ // already persisted and will take effect on next worker restart.
1761
+ }
1762
+ }
1763
+ res.json(result);
1764
+ }
1765
+ catch (err) {
1766
+ const message = err instanceof Error ? err.message : String(err);
1767
+ res.status(400).json({ error: message });
1768
+ }
1769
+ });
1770
+ /**
1771
+ * POST /api/plugins/:pluginId/config/test
1772
+ *
1773
+ * Test a plugin configuration without persisting it by calling the plugin
1774
+ * worker's `validateConfig` RPC method.
1775
+ *
1776
+ * Only works when the plugin's worker implements `onValidateConfig`.
1777
+ * If the worker does not implement the method, returns
1778
+ * `{ valid: false, supported: false, message: "..." }` with HTTP 200.
1779
+ *
1780
+ * Request body:
1781
+ * - `configJson`: Configuration values to validate
1782
+ *
1783
+ * Response: `{ valid: boolean; message?: string; supported?: boolean }`
1784
+ * Errors:
1785
+ * - 400 if request validation fails
1786
+ * - 404 if plugin not found
1787
+ * - 501 if bridge deps (worker manager) are not configured
1788
+ * - 502 if the worker is unavailable
1789
+ */
1790
+ router.post("/plugins/:pluginId/config/test", async (req, res) => {
1791
+ assertOperatorOrgAccess(req);
1792
+ if (!bridgeDeps) {
1793
+ res.status(501).json({ error: "Plugin bridge is not enabled" });
1794
+ return;
1795
+ }
1796
+ const { pluginId } = req.params;
1797
+ const plugin = await resolvePlugin(registry, pluginId);
1798
+ if (!plugin) {
1799
+ res.status(404).json({ error: "Plugin not found" });
1800
+ return;
1801
+ }
1802
+ if (plugin.status !== "ready") {
1803
+ res.status(400).json({
1804
+ error: `Plugin is not ready (current status: ${plugin.status})`,
1805
+ });
1806
+ return;
1807
+ }
1808
+ const body = req.body;
1809
+ if (!body?.configJson || typeof body.configJson !== "object") {
1810
+ res.status(400).json({ error: '"configJson" is required and must be an object' });
1811
+ return;
1812
+ }
1813
+ // Fast schema-level rejection before hitting the worker RPC.
1814
+ const schema = plugin.manifestJson?.instanceConfigSchema;
1815
+ if (schema && Object.keys(schema).length > 0) {
1816
+ const validation = validateInstanceConfig(body.configJson, schema);
1817
+ if (!validation.valid) {
1818
+ res.status(400).json({
1819
+ error: "Configuration does not match the plugin's instanceConfigSchema",
1820
+ fieldErrors: validation.errors,
1821
+ });
1822
+ return;
1823
+ }
1824
+ }
1825
+ try {
1826
+ const result = await bridgeDeps.workerManager.call(plugin.id, "validateConfig", { config: body.configJson });
1827
+ // The worker returns PluginConfigValidationResult { ok, warnings?, errors? }
1828
+ // Map to the frontend-expected shape { valid, message? }
1829
+ if (result.ok) {
1830
+ const warningText = result.warnings?.length
1831
+ ? `Warnings: ${result.warnings.join("; ")}`
1832
+ : undefined;
1833
+ res.json({ valid: true, message: warningText });
1834
+ }
1835
+ else {
1836
+ const errorText = result.errors?.length
1837
+ ? result.errors.join("; ")
1838
+ : "Configuration validation failed.";
1839
+ res.json({ valid: false, message: errorText });
1840
+ }
1841
+ }
1842
+ catch (err) {
1843
+ // If the worker does not implement validateConfig, return a structured response
1844
+ if (err instanceof JsonRpcCallError &&
1845
+ err.code === PLUGIN_RPC_ERROR_CODES.METHOD_NOT_IMPLEMENTED) {
1846
+ res.json({
1847
+ valid: false,
1848
+ supported: false,
1849
+ message: "This plugin does not support configuration testing.",
1850
+ });
1851
+ return;
1852
+ }
1853
+ // Worker unavailable or other RPC errors
1854
+ const bridgeError = mapRpcErrorToBridgeError(err);
1855
+ res.status(502).json(bridgeError);
1856
+ }
1857
+ });
1858
+ // ===========================================================================
1859
+ // Job scheduling routes
1860
+ // ===========================================================================
1861
+ /**
1862
+ * GET /api/plugins/:pluginId/jobs
1863
+ *
1864
+ * List all scheduled jobs for a plugin.
1865
+ *
1866
+ * Query params:
1867
+ * - `status` (optional): Filter by job status (`active`, `paused`, `failed`)
1868
+ *
1869
+ * Response: PluginJobRecord[]
1870
+ * Errors: 404 if plugin not found
1871
+ */
1872
+ router.get("/plugins/:pluginId/jobs", async (req, res) => {
1873
+ assertOperatorOrgAccess(req);
1874
+ if (!jobDeps) {
1875
+ res.status(501).json({ error: "Job scheduling is not enabled" });
1876
+ return;
1877
+ }
1878
+ const { pluginId } = req.params;
1879
+ const plugin = await resolvePlugin(registry, pluginId);
1880
+ if (!plugin) {
1881
+ res.status(404).json({ error: "Plugin not found" });
1882
+ return;
1883
+ }
1884
+ const rawStatus = req.query.status;
1885
+ const validStatuses = ["active", "paused", "failed"];
1886
+ if (rawStatus !== undefined && !validStatuses.includes(rawStatus)) {
1887
+ res.status(400).json({
1888
+ error: `Invalid status '${rawStatus}'. Must be one of: ${validStatuses.join(", ")}`,
1889
+ });
1890
+ return;
1891
+ }
1892
+ try {
1893
+ const jobs = await jobDeps.jobStore.listJobs(plugin.id, rawStatus);
1894
+ res.json(jobs);
1895
+ }
1896
+ catch (err) {
1897
+ const message = err instanceof Error ? err.message : String(err);
1898
+ res.status(500).json({ error: message });
1899
+ }
1900
+ });
1901
+ /**
1902
+ * GET /api/plugins/:pluginId/jobs/:jobId/runs
1903
+ *
1904
+ * List execution history for a specific job.
1905
+ *
1906
+ * Query params:
1907
+ * - `limit` (optional): Maximum number of runs to return (default: 50)
1908
+ *
1909
+ * Response: PluginJobRunRecord[]
1910
+ * Errors: 404 if plugin not found
1911
+ */
1912
+ router.get("/plugins/:pluginId/jobs/:jobId/runs", async (req, res) => {
1913
+ assertOperatorOrgAccess(req);
1914
+ if (!jobDeps) {
1915
+ res.status(501).json({ error: "Job scheduling is not enabled" });
1916
+ return;
1917
+ }
1918
+ const { pluginId, jobId } = req.params;
1919
+ const plugin = await resolvePlugin(registry, pluginId);
1920
+ if (!plugin) {
1921
+ res.status(404).json({ error: "Plugin not found" });
1922
+ return;
1923
+ }
1924
+ const job = await jobDeps.jobStore.getJobByIdForPlugin(plugin.id, jobId);
1925
+ if (!job) {
1926
+ res.status(404).json({ error: "Job not found" });
1927
+ return;
1928
+ }
1929
+ const limit = req.query.limit ? parseInt(req.query.limit, 10) : 25;
1930
+ if (isNaN(limit) || limit < 1 || limit > 500) {
1931
+ res.status(400).json({ error: "limit must be a number between 1 and 500" });
1932
+ return;
1933
+ }
1934
+ try {
1935
+ const runs = await jobDeps.jobStore.listRunsByJob(jobId, limit);
1936
+ res.json(runs);
1937
+ }
1938
+ catch (err) {
1939
+ const message = err instanceof Error ? err.message : String(err);
1940
+ res.status(500).json({ error: message });
1941
+ }
1942
+ });
1943
+ /**
1944
+ * POST /api/plugins/:pluginId/jobs/:jobId/trigger
1945
+ *
1946
+ * Manually trigger a job execution outside its cron schedule.
1947
+ *
1948
+ * Creates a run with `trigger: "manual"` and dispatches immediately.
1949
+ * The response returns before the job completes (non-blocking).
1950
+ *
1951
+ * Response: `{ runId: string, jobId: string }`
1952
+ * Errors:
1953
+ * - 404 if plugin not found
1954
+ * - 400 if job not found, not active, already running, or worker unavailable
1955
+ */
1956
+ router.post("/plugins/:pluginId/jobs/:jobId/trigger", async (req, res) => {
1957
+ assertInstanceAdmin(req);
1958
+ if (!jobDeps) {
1959
+ res.status(501).json({ error: "Job scheduling is not enabled" });
1960
+ return;
1961
+ }
1962
+ const { pluginId, jobId } = req.params;
1963
+ const plugin = await resolvePlugin(registry, pluginId);
1964
+ if (!plugin) {
1965
+ res.status(404).json({ error: "Plugin not found" });
1966
+ return;
1967
+ }
1968
+ const job = await jobDeps.jobStore.getJobByIdForPlugin(plugin.id, jobId);
1969
+ if (!job) {
1970
+ res.status(404).json({ error: "Job not found" });
1971
+ return;
1972
+ }
1973
+ try {
1974
+ const result = await jobDeps.scheduler.triggerJob(jobId, "manual");
1975
+ res.json(result);
1976
+ }
1977
+ catch (err) {
1978
+ const message = err instanceof Error ? err.message : String(err);
1979
+ res.status(400).json({ error: message });
1980
+ }
1981
+ });
1982
+ // ===========================================================================
1983
+ // Webhook ingestion route
1984
+ // ===========================================================================
1985
+ /**
1986
+ * POST /api/plugins/:pluginId/webhooks/:endpointKey
1987
+ *
1988
+ * Receive an inbound webhook delivery for a plugin.
1989
+ *
1990
+ * This route is called by external systems (e.g. GitHub, Linear, Stripe) to
1991
+ * deliver webhook payloads to a plugin. The host validates that:
1992
+ * 1. The plugin exists and is in 'ready' state
1993
+ * 2. The plugin declares the `webhooks.receive` capability
1994
+ * 3. The `endpointKey` matches a declared webhook in the manifest
1995
+ *
1996
+ * The delivery is recorded in the `plugin_webhook_deliveries` table and
1997
+ * dispatched to the worker via the `handleWebhook` RPC method.
1998
+ *
1999
+ * **Note:** This route does NOT require operator authentication — webhook
2000
+ * endpoints must be publicly accessible for external callers. Signature
2001
+ * verification is the plugin's responsibility.
2002
+ *
2003
+ * Response: `{ deliveryId: string, status: string }`
2004
+ * Errors:
2005
+ * - 404 if plugin not found or endpointKey not declared
2006
+ * - 400 if plugin is not in ready state or lacks webhooks.receive capability
2007
+ * - 502 if the worker is unavailable or the RPC call fails
2008
+ */
2009
+ router.post("/plugins/:pluginId/webhooks/:endpointKey", async (req, res) => {
2010
+ if (!webhookDeps) {
2011
+ res.status(501).json({ error: "Webhook ingestion is not enabled" });
2012
+ return;
2013
+ }
2014
+ const { pluginId, endpointKey } = req.params;
2015
+ // Step 1: Resolve the plugin
2016
+ const plugin = await resolvePlugin(registry, pluginId);
2017
+ if (!plugin) {
2018
+ res.status(404).json({ error: "Plugin not found" });
2019
+ return;
2020
+ }
2021
+ // Step 2: Validate the plugin is in 'ready' state
2022
+ if (plugin.status !== "ready") {
2023
+ res.status(400).json({
2024
+ error: `Plugin is not ready (current status: ${plugin.status})`,
2025
+ });
2026
+ return;
2027
+ }
2028
+ // Step 3: Validate the plugin has webhooks.receive capability
2029
+ const manifest = plugin.manifestJson;
2030
+ if (!manifest) {
2031
+ res.status(400).json({ error: "Plugin manifest is missing" });
2032
+ return;
2033
+ }
2034
+ const capabilities = manifest.capabilities ?? [];
2035
+ if (!capabilities.includes("webhooks.receive")) {
2036
+ res.status(400).json({
2037
+ error: "Plugin does not have the webhooks.receive capability",
2038
+ });
2039
+ return;
2040
+ }
2041
+ // Step 4: Validate the endpointKey exists in the manifest's webhook declarations
2042
+ const declaredWebhooks = manifest.webhooks ?? [];
2043
+ const webhookDecl = declaredWebhooks.find((w) => w.endpointKey === endpointKey);
2044
+ if (!webhookDecl) {
2045
+ res.status(404).json({
2046
+ error: `Webhook endpoint '${endpointKey}' is not declared by this plugin`,
2047
+ });
2048
+ return;
2049
+ }
2050
+ // Step 5: Extract request data
2051
+ const requestId = randomUUID();
2052
+ const rawHeaders = {};
2053
+ for (const [key, value] of Object.entries(req.headers)) {
2054
+ if (typeof value === "string") {
2055
+ rawHeaders[key] = value;
2056
+ }
2057
+ else if (Array.isArray(value)) {
2058
+ rawHeaders[key] = value.join(", ");
2059
+ }
2060
+ }
2061
+ // Use the raw buffer stashed by the express.json() `verify` callback.
2062
+ // This preserves the exact bytes the provider signed, whereas
2063
+ // JSON.stringify(req.body) would re-serialize and break HMAC verification.
2064
+ const stashedRaw = req.rawBody;
2065
+ const rawBody = stashedRaw ? stashedRaw.toString("utf-8") : "";
2066
+ const parsedBody = req.body;
2067
+ const payload = req.body ?? {};
2068
+ // Step 6: Record the delivery in the database
2069
+ const startedAt = new Date();
2070
+ const [delivery] = await db
2071
+ .insert(pluginWebhookDeliveries)
2072
+ .values({
2073
+ pluginId: plugin.id,
2074
+ webhookKey: endpointKey,
2075
+ status: "pending",
2076
+ payload,
2077
+ headers: rawHeaders,
2078
+ startedAt,
2079
+ })
2080
+ .returning({ id: pluginWebhookDeliveries.id });
2081
+ // Step 7: Dispatch to the worker via handleWebhook RPC
2082
+ try {
2083
+ await webhookDeps.workerManager.call(plugin.id, "handleWebhook", {
2084
+ endpointKey,
2085
+ headers: req.headers,
2086
+ rawBody,
2087
+ parsedBody,
2088
+ requestId,
2089
+ });
2090
+ // Step 8: Update delivery record to success
2091
+ const finishedAt = new Date();
2092
+ const durationMs = finishedAt.getTime() - startedAt.getTime();
2093
+ await db
2094
+ .update(pluginWebhookDeliveries)
2095
+ .set({
2096
+ status: "success",
2097
+ durationMs,
2098
+ finishedAt,
2099
+ })
2100
+ .where(eq(pluginWebhookDeliveries.id, delivery.id));
2101
+ res.status(200).json({
2102
+ deliveryId: delivery.id,
2103
+ status: "success",
2104
+ });
2105
+ }
2106
+ catch (err) {
2107
+ // Step 8 (error): Update delivery record to failed
2108
+ const finishedAt = new Date();
2109
+ const durationMs = finishedAt.getTime() - startedAt.getTime();
2110
+ const errorMessage = err instanceof Error ? err.message : String(err);
2111
+ await db
2112
+ .update(pluginWebhookDeliveries)
2113
+ .set({
2114
+ status: "failed",
2115
+ durationMs,
2116
+ error: errorMessage,
2117
+ finishedAt,
2118
+ })
2119
+ .where(eq(pluginWebhookDeliveries.id, delivery.id));
2120
+ res.status(502).json({
2121
+ deliveryId: delivery.id,
2122
+ status: "failed",
2123
+ error: errorMessage,
2124
+ });
2125
+ }
2126
+ });
2127
+ // ===========================================================================
2128
+ // Squad-scoped trusted local folders
2129
+ // ===========================================================================
2130
+ router.get("/plugins/:pluginId/squads/:squadId/local-folders", async (req, res) => {
2131
+ assertOperatorOrgAccess(req);
2132
+ const { pluginId, squadId } = req.params;
2133
+ assertSquadAccess(req, squadId);
2134
+ const plugin = await resolvePlugin(registry, pluginId);
2135
+ if (!plugin) {
2136
+ res.status(404).json({ error: "Plugin not found" });
2137
+ return;
2138
+ }
2139
+ const settings = await registry.getSquadSettings(plugin.id, squadId);
2140
+ const storedFolders = getStoredLocalFolders(settings?.settingsJson);
2141
+ const declarations = plugin.manifestJson.localFolders ?? [];
2142
+ const folderKeys = declarations.map((declaration) => declaration.folderKey);
2143
+ const statuses = await Promise.all(folderKeys.map((folderKey) => inspectPluginLocalFolder({
2144
+ folderKey,
2145
+ declaration: findLocalFolderDeclaration(declarations, folderKey),
2146
+ storedConfig: storedFolders[folderKey] ?? null,
2147
+ })));
2148
+ res.json({
2149
+ pluginId: plugin.id,
2150
+ squadId,
2151
+ declarations,
2152
+ folders: statuses,
2153
+ });
2154
+ });
2155
+ router.get("/plugins/:pluginId/squads/:squadId/local-folders/:folderKey/status", async (req, res) => {
2156
+ assertOperatorOrgAccess(req);
2157
+ const { pluginId, squadId, folderKey } = req.params;
2158
+ assertSquadAccess(req, squadId);
2159
+ const plugin = await resolvePlugin(registry, pluginId);
2160
+ if (!plugin) {
2161
+ res.status(404).json({ error: "Plugin not found" });
2162
+ return;
2163
+ }
2164
+ const settings = await registry.getSquadSettings(plugin.id, squadId);
2165
+ const storedFolders = getStoredLocalFolders(settings?.settingsJson);
2166
+ const declarations = plugin.manifestJson.localFolders ?? [];
2167
+ const declaration = requireLocalFolderDeclaration(declarations, folderKey);
2168
+ const status = await inspectPluginLocalFolder({
2169
+ folderKey,
2170
+ declaration,
2171
+ storedConfig: storedFolders[folderKey] ?? null,
2172
+ });
2173
+ res.json(status);
2174
+ });
2175
+ router.post("/plugins/:pluginId/squads/:squadId/local-folders/:folderKey/validate", async (req, res) => {
2176
+ assertOperatorOrgAccess(req);
2177
+ const { pluginId, squadId, folderKey } = req.params;
2178
+ assertSquadAccess(req, squadId);
2179
+ const plugin = await resolvePlugin(registry, pluginId);
2180
+ if (!plugin) {
2181
+ res.status(404).json({ error: "Plugin not found" });
2182
+ return;
2183
+ }
2184
+ const body = req.body;
2185
+ if (typeof body?.path !== "string" || body.path.trim().length === 0) {
2186
+ res.status(400).json({ error: '"path" is required and must be a non-empty string' });
2187
+ return;
2188
+ }
2189
+ const declaration = requireLocalFolderDeclaration(plugin.manifestJson.localFolders ?? [], folderKey);
2190
+ const status = await inspectPluginLocalFolder({
2191
+ folderKey,
2192
+ declaration,
2193
+ overrideConfig: {
2194
+ path: body.path,
2195
+ },
2196
+ });
2197
+ res.json(status);
2198
+ });
2199
+ router.put("/plugins/:pluginId/squads/:squadId/local-folders/:folderKey", async (req, res) => {
2200
+ assertOperatorOrgAccess(req);
2201
+ const { pluginId, squadId, folderKey } = req.params;
2202
+ assertSquadAccess(req, squadId);
2203
+ const plugin = await resolvePlugin(registry, pluginId);
2204
+ if (!plugin) {
2205
+ res.status(404).json({ error: "Plugin not found" });
2206
+ return;
2207
+ }
2208
+ const body = req.body;
2209
+ if (typeof body?.path !== "string" || body.path.trim().length === 0) {
2210
+ res.status(400).json({ error: '"path" is required and must be a non-empty string' });
2211
+ return;
2212
+ }
2213
+ const existing = await registry.getSquadSettings(plugin.id, squadId);
2214
+ const declaration = requireLocalFolderDeclaration(plugin.manifestJson.localFolders ?? [], folderKey);
2215
+ const status = await inspectPluginLocalFolder({
2216
+ folderKey,
2217
+ declaration,
2218
+ storedConfig: getStoredLocalFolders(existing?.settingsJson)[folderKey] ?? null,
2219
+ overrideConfig: {
2220
+ path: body.path,
2221
+ },
2222
+ });
2223
+ const nextSettings = setStoredLocalFolder(existing?.settingsJson, folderKey, {
2224
+ path: body.path,
2225
+ access: status.access,
2226
+ requiredDirectories: status.requiredDirectories,
2227
+ requiredFiles: status.requiredFiles,
2228
+ });
2229
+ await registry.upsertSquadSettings(plugin.id, squadId, {
2230
+ enabled: existing?.enabled ?? true,
2231
+ settingsJson: nextSettings,
2232
+ lastError: status.healthy ? null : status.problems.map((item) => item.message).join("; "),
2233
+ });
2234
+ await logPluginMutationActivity(req, "plugin.local_folder.configured", plugin.id, {
2235
+ pluginId: plugin.id,
2236
+ pluginKey: plugin.pluginKey,
2237
+ squadId,
2238
+ folderKey,
2239
+ healthy: status.healthy,
2240
+ });
2241
+ res.json(status);
2242
+ });
2243
+ // ===========================================================================
2244
+ // Plugin health dashboard — aggregated diagnostics for the settings page
2245
+ // ===========================================================================
2246
+ /**
2247
+ * GET /api/plugins/:pluginId/dashboard
2248
+ *
2249
+ * Aggregated health dashboard data for a plugin's settings page.
2250
+ *
2251
+ * Returns worker diagnostics (status, uptime, crash history), recent job
2252
+ * runs, recent webhook deliveries, and the current health check result —
2253
+ * all in a single response to avoid multiple round-trips.
2254
+ *
2255
+ * Response: PluginDashboardData
2256
+ * Errors: 404 if plugin not found
2257
+ */
2258
+ router.get("/plugins/:pluginId/dashboard", async (req, res) => {
2259
+ assertOperatorOrgAccess(req);
2260
+ const { pluginId } = req.params;
2261
+ const plugin = await resolvePlugin(registry, pluginId);
2262
+ if (!plugin) {
2263
+ res.status(404).json({ error: "Plugin not found" });
2264
+ return;
2265
+ }
2266
+ // --- Worker diagnostics ---
2267
+ let worker = null;
2268
+ // Try bridgeDeps first (primary source for worker manager), fallback to webhookDeps
2269
+ const wm = bridgeDeps?.workerManager ?? webhookDeps?.workerManager ?? null;
2270
+ if (wm) {
2271
+ const handle = wm.getWorker(plugin.id);
2272
+ if (handle) {
2273
+ const diag = handle.diagnostics();
2274
+ worker = {
2275
+ status: diag.status,
2276
+ pid: diag.pid,
2277
+ uptime: diag.uptime,
2278
+ consecutiveCrashes: diag.consecutiveCrashes,
2279
+ totalCrashes: diag.totalCrashes,
2280
+ pendingRequests: diag.pendingRequests,
2281
+ lastCrashAt: diag.lastCrashAt,
2282
+ nextRestartAt: diag.nextRestartAt,
2283
+ };
2284
+ }
2285
+ }
2286
+ // --- Recent job runs (last 10, newest first) ---
2287
+ let recentJobRuns = [];
2288
+ if (jobDeps) {
2289
+ try {
2290
+ const runs = await jobDeps.jobStore.listRunsByPlugin(plugin.id, undefined, 10);
2291
+ // Also fetch job definitions so we can include jobKey
2292
+ const jobs = await jobDeps.jobStore.listJobs(plugin.id);
2293
+ const jobKeyMap = new Map(jobs.map((j) => [j.id, j.jobKey]));
2294
+ recentJobRuns = runs
2295
+ .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
2296
+ .map((r) => ({
2297
+ id: r.id,
2298
+ jobId: r.jobId,
2299
+ jobKey: jobKeyMap.get(r.jobId) ?? undefined,
2300
+ trigger: r.trigger,
2301
+ status: r.status,
2302
+ durationMs: r.durationMs,
2303
+ error: r.error,
2304
+ startedAt: r.startedAt ? new Date(r.startedAt).toISOString() : null,
2305
+ finishedAt: r.finishedAt ? new Date(r.finishedAt).toISOString() : null,
2306
+ createdAt: new Date(r.createdAt).toISOString(),
2307
+ }));
2308
+ }
2309
+ catch {
2310
+ // Job data unavailable — leave empty
2311
+ }
2312
+ }
2313
+ // --- Recent webhook deliveries (last 10, newest first) ---
2314
+ let recentWebhookDeliveries = [];
2315
+ try {
2316
+ const deliveries = await db
2317
+ .select({
2318
+ id: pluginWebhookDeliveries.id,
2319
+ webhookKey: pluginWebhookDeliveries.webhookKey,
2320
+ status: pluginWebhookDeliveries.status,
2321
+ durationMs: pluginWebhookDeliveries.durationMs,
2322
+ error: pluginWebhookDeliveries.error,
2323
+ startedAt: pluginWebhookDeliveries.startedAt,
2324
+ finishedAt: pluginWebhookDeliveries.finishedAt,
2325
+ createdAt: pluginWebhookDeliveries.createdAt,
2326
+ })
2327
+ .from(pluginWebhookDeliveries)
2328
+ .where(eq(pluginWebhookDeliveries.pluginId, plugin.id))
2329
+ .orderBy(desc(pluginWebhookDeliveries.createdAt))
2330
+ .limit(10);
2331
+ recentWebhookDeliveries = deliveries.map((d) => ({
2332
+ id: d.id,
2333
+ webhookKey: d.webhookKey,
2334
+ status: d.status,
2335
+ durationMs: d.durationMs,
2336
+ error: d.error,
2337
+ startedAt: d.startedAt ? d.startedAt.toISOString() : null,
2338
+ finishedAt: d.finishedAt ? d.finishedAt.toISOString() : null,
2339
+ createdAt: d.createdAt.toISOString(),
2340
+ }));
2341
+ }
2342
+ catch {
2343
+ // Webhook data unavailable — leave empty
2344
+ }
2345
+ // --- Health check (same logic as GET /health) ---
2346
+ const checks = [];
2347
+ checks.push({
2348
+ name: "registry",
2349
+ passed: true,
2350
+ message: "Plugin found in registry",
2351
+ });
2352
+ const hasValidManifest = Boolean(plugin.manifestJson?.id);
2353
+ checks.push({
2354
+ name: "manifest",
2355
+ passed: hasValidManifest,
2356
+ message: hasValidManifest ? "Manifest is valid" : "Manifest is invalid or missing",
2357
+ });
2358
+ const isHealthy = plugin.status === "ready";
2359
+ checks.push({
2360
+ name: "status",
2361
+ passed: isHealthy,
2362
+ message: `Current status: ${plugin.status}`,
2363
+ });
2364
+ const hasNoError = !plugin.lastError;
2365
+ if (!hasNoError) {
2366
+ checks.push({
2367
+ name: "error_state",
2368
+ passed: false,
2369
+ message: plugin.lastError ?? undefined,
2370
+ });
2371
+ }
2372
+ const health = {
2373
+ pluginId: plugin.id,
2374
+ status: plugin.status,
2375
+ healthy: isHealthy && hasValidManifest && hasNoError,
2376
+ checks,
2377
+ lastError: plugin.lastError ?? undefined,
2378
+ };
2379
+ res.json({
2380
+ pluginId: plugin.id,
2381
+ worker,
2382
+ recentJobRuns,
2383
+ recentWebhookDeliveries,
2384
+ health,
2385
+ checkedAt: new Date().toISOString(),
2386
+ });
2387
+ });
2388
+ return router;
2389
+ }
2390
+ //# sourceMappingURL=plugins.js.map