@kb-labs/rest-api-app 1.3.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 (255) hide show
  1. package/.kb/agents/sessions/session-1771531145393-ws3uh6q/turns.json +42 -0
  2. package/.kb/agents/sessions/session-1771548930486-ui6rtvm/turns.json +33 -0
  3. package/.kb/agents/sessions/session-1771549691485-nibqroq/turns.json +133 -0
  4. package/.kb/agents/sessions/session-1771550943182-dkwqdjc/turns.json +130 -0
  5. package/.kb/agents/sessions/session-1771551564182-cr4bwzn/turns.json +349 -0
  6. package/.kb/agents/sessions/session-1771552401370-g4ztwd1/turns.json +206 -0
  7. package/.kb/commit/current/plan.json +248 -0
  8. package/.kb/commit/current/status.json +472 -0
  9. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/health.d.ts +10 -0
  10. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/health.js +215 -0
  11. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/health.js.map +1 -0
  12. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/list.d.ts +10 -0
  13. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/list.js +271 -0
  14. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/list.js.map +1 -0
  15. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/logs.d.ts +10 -0
  16. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/logs.js +230 -0
  17. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/logs.js.map +1 -0
  18. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/metrics.d.ts +10 -0
  19. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/metrics.js +228 -0
  20. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/metrics.js.map +1 -0
  21. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/run.d.ts +11 -0
  22. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/run.js +281 -0
  23. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/run.js.map +1 -0
  24. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/status.d.ts +10 -0
  25. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/status.js +266 -0
  26. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/status.js.map +1 -0
  27. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/workflow-run.d.ts +11 -0
  28. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/workflow-run.js +284 -0
  29. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/workflow-run.js.map +1 -0
  30. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/flags-WLdpSZl4.d.ts +77 -0
  31. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/index.d.ts +2 -0
  32. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/index.js +441 -0
  33. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/index.js.map +1 -0
  34. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/manifest.d.ts +196 -0
  35. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/manifest.js +441 -0
  36. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/manifest.js.map +1 -0
  37. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/cron-list-handler.d.ts +19 -0
  38. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/cron-list-handler.js +48 -0
  39. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/cron-list-handler.js.map +1 -0
  40. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/index.d.ts +6 -0
  41. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/index.js +170 -0
  42. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/index.js.map +1 -0
  43. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-cancel-handler.d.ts +12 -0
  44. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-cancel-handler.js +52 -0
  45. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-cancel-handler.js.map +1 -0
  46. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-detail-handler.d.ts +22 -0
  47. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-detail-handler.js +52 -0
  48. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-detail-handler.js.map +1 -0
  49. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-logs-handler.d.ts +13 -0
  50. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-logs-handler.js +52 -0
  51. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-logs-handler.js.map +1 -0
  52. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-steps-handler.d.ts +9 -0
  53. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-steps-handler.js +43 -0
  54. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-steps-handler.js.map +1 -0
  55. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/jobs-list-handler.d.ts +26 -0
  56. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/jobs-list-handler.js +63 -0
  57. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/jobs-list-handler.js.map +1 -0
  58. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/pending-approvals-handler.d.ts +9 -0
  59. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/pending-approvals-handler.js +46 -0
  60. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/pending-approvals-handler.js.map +1 -0
  61. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/resolve-approval-handler.d.ts +16 -0
  62. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/resolve-approval-handler.js +58 -0
  63. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/resolve-approval-handler.js.map +1 -0
  64. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/run-detail-handler.d.ts +9 -0
  65. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/run-detail-handler.js +46 -0
  66. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/run-detail-handler.js.map +1 -0
  67. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/runs-list-handler.d.ts +11 -0
  68. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/runs-list-handler.js +50 -0
  69. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/runs-list-handler.js.map +1 -0
  70. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/stats-handler.d.ts +7 -0
  71. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/stats-handler.js +39 -0
  72. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/stats-handler.js.map +1 -0
  73. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-detail-handler.d.ts +10 -0
  74. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-detail-handler.js +46 -0
  75. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-detail-handler.js.map +1 -0
  76. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-cancel-handler.d.ts +13 -0
  77. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-cancel-handler.js +47 -0
  78. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-cancel-handler.js.map +1 -0
  79. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-handler.d.ts +14 -0
  80. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-handler.js +53 -0
  81. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-handler.js.map +1 -0
  82. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-runs-handler.d.ts +13 -0
  83. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-runs-handler.js +54 -0
  84. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-runs-handler.js.map +1 -0
  85. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflows-list-handler.d.ts +12 -0
  86. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflows-list-handler.js +51 -0
  87. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflows-list-handler.js.map +1 -0
  88. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/logs-channel.d.ts +17 -0
  89. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/logs-channel.js +133 -0
  90. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/logs-channel.js.map +1 -0
  91. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/progress-channel.d.ts +11 -0
  92. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/progress-channel.js +46 -0
  93. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/progress-channel.js.map +1 -0
  94. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/health.d.ts +10 -0
  95. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/health.js +215 -0
  96. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/health.js.map +1 -0
  97. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/list.d.ts +10 -0
  98. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/list.js +271 -0
  99. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/list.js.map +1 -0
  100. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/logs.d.ts +10 -0
  101. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/logs.js +230 -0
  102. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/logs.js.map +1 -0
  103. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/metrics.d.ts +10 -0
  104. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/metrics.js +228 -0
  105. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/metrics.js.map +1 -0
  106. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/run.d.ts +11 -0
  107. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/run.js +281 -0
  108. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/run.js.map +1 -0
  109. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/status.d.ts +10 -0
  110. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/status.js +266 -0
  111. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/status.js.map +1 -0
  112. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/workflow-run.d.ts +11 -0
  113. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/workflow-run.js +284 -0
  114. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/workflow-run.js.map +1 -0
  115. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/flags-WLdpSZl4.d.ts +77 -0
  116. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/index.d.ts +2 -0
  117. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/index.js +441 -0
  118. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/index.js.map +1 -0
  119. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/manifest.d.ts +196 -0
  120. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/manifest.js +441 -0
  121. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/manifest.js.map +1 -0
  122. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/cron-list-handler.d.ts +19 -0
  123. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/cron-list-handler.js +48 -0
  124. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/cron-list-handler.js.map +1 -0
  125. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/index.d.ts +6 -0
  126. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/index.js +170 -0
  127. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/index.js.map +1 -0
  128. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-cancel-handler.d.ts +12 -0
  129. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-cancel-handler.js +52 -0
  130. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-cancel-handler.js.map +1 -0
  131. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-detail-handler.d.ts +22 -0
  132. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-detail-handler.js +52 -0
  133. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-detail-handler.js.map +1 -0
  134. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-logs-handler.d.ts +13 -0
  135. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-logs-handler.js +52 -0
  136. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-logs-handler.js.map +1 -0
  137. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-steps-handler.d.ts +9 -0
  138. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-steps-handler.js +43 -0
  139. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-steps-handler.js.map +1 -0
  140. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/jobs-list-handler.d.ts +26 -0
  141. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/jobs-list-handler.js +63 -0
  142. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/jobs-list-handler.js.map +1 -0
  143. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/pending-approvals-handler.d.ts +9 -0
  144. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/pending-approvals-handler.js +46 -0
  145. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/pending-approvals-handler.js.map +1 -0
  146. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/resolve-approval-handler.d.ts +16 -0
  147. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/resolve-approval-handler.js +58 -0
  148. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/resolve-approval-handler.js.map +1 -0
  149. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/run-detail-handler.d.ts +9 -0
  150. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/run-detail-handler.js +46 -0
  151. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/run-detail-handler.js.map +1 -0
  152. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/runs-list-handler.d.ts +11 -0
  153. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/runs-list-handler.js +50 -0
  154. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/runs-list-handler.js.map +1 -0
  155. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/stats-handler.d.ts +7 -0
  156. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/stats-handler.js +39 -0
  157. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/stats-handler.js.map +1 -0
  158. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-detail-handler.d.ts +10 -0
  159. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-detail-handler.js +46 -0
  160. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-detail-handler.js.map +1 -0
  161. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-cancel-handler.d.ts +13 -0
  162. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-cancel-handler.js +47 -0
  163. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-cancel-handler.js.map +1 -0
  164. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-handler.d.ts +14 -0
  165. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-handler.js +53 -0
  166. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-handler.js.map +1 -0
  167. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-runs-handler.d.ts +13 -0
  168. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-runs-handler.js +54 -0
  169. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-runs-handler.js.map +1 -0
  170. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflows-list-handler.d.ts +12 -0
  171. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflows-list-handler.js +51 -0
  172. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflows-list-handler.js.map +1 -0
  173. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/logs-channel.d.ts +17 -0
  174. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/logs-channel.js +133 -0
  175. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/logs-channel.js.map +1 -0
  176. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/progress-channel.d.ts +11 -0
  177. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/progress-channel.js +46 -0
  178. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/progress-channel.js.map +1 -0
  179. package/README.md +54 -0
  180. package/eslint.config.js +23 -0
  181. package/package.json +72 -0
  182. package/src/.kb/agents/sessions/session-1773470583796-ximqnfk/kpi-baseline.json +17 -0
  183. package/src/.kb/memory/sessions/session-1773470583796-ximqnfk/fact-sheet.json +1 -0
  184. package/src/bootstrap.ts +233 -0
  185. package/src/diagnostics/header-debug.ts +37 -0
  186. package/src/events/hub.ts +60 -0
  187. package/src/events/index.ts +1 -0
  188. package/src/index.ts +13 -0
  189. package/src/manifest.ts +21 -0
  190. package/src/middleware/__tests__/cache.spec.ts +184 -0
  191. package/src/middleware/__tests__/envelope.spec.ts +268 -0
  192. package/src/middleware/__tests__/metrics.redis.spec.ts +64 -0
  193. package/src/middleware/__tests__/rate-limit.spec.ts +283 -0
  194. package/src/middleware/__tests__/request-id.spec.ts +160 -0
  195. package/src/middleware/cache.ts +67 -0
  196. package/src/middleware/envelope.ts +136 -0
  197. package/src/middleware/error-guard.ts +69 -0
  198. package/src/middleware/index.ts +37 -0
  199. package/src/middleware/metrics.ts +810 -0
  200. package/src/middleware/mock-mode.ts +44 -0
  201. package/src/middleware/prom-metrics.ts +284 -0
  202. package/src/middleware/rate-limit-routes.ts +73 -0
  203. package/src/middleware/rate-limit.ts +72 -0
  204. package/src/middleware/request-id.ts +41 -0
  205. package/src/middleware/request-timeout.ts +69 -0
  206. package/src/middleware/security-headers.ts +24 -0
  207. package/src/middleware/startup-guard.ts +107 -0
  208. package/src/observability/service-contract.ts +192 -0
  209. package/src/plugins/index.ts +118 -0
  210. package/src/routes/__tests__/adapters-date-helpers.spec.ts +332 -0
  211. package/src/routes/__tests__/adapters.spec.ts +680 -0
  212. package/src/routes/__tests__/cache.spec.ts +264 -0
  213. package/src/routes/__tests__/events.spec.ts +434 -0
  214. package/src/routes/__tests__/health.spec.ts +414 -0
  215. package/src/routes/__tests__/logs.spec.ts +494 -0
  216. package/src/routes/__tests__/observability.spec.ts +392 -0
  217. package/src/routes/__tests__/plugins.spec.ts +931 -0
  218. package/src/routes/__tests__/workflow-management.spec.ts +698 -0
  219. package/src/routes/__tests__/workflows.spec.ts +204 -0
  220. package/src/routes/adapter-call.ts +220 -0
  221. package/src/routes/adapters.ts +1206 -0
  222. package/src/routes/analytics.ts +328 -0
  223. package/src/routes/cache.ts +104 -0
  224. package/src/routes/debug-routes.ts +95 -0
  225. package/src/routes/events.ts +115 -0
  226. package/src/routes/health.ts +348 -0
  227. package/src/routes/hello.ts +47 -0
  228. package/src/routes/index.ts +395 -0
  229. package/src/routes/jobs.ts +253 -0
  230. package/src/routes/logs.ts +718 -0
  231. package/src/routes/metrics.ts +120 -0
  232. package/src/routes/observability.ts +1388 -0
  233. package/src/routes/openapi.ts +101 -0
  234. package/src/routes/platform.ts +143 -0
  235. package/src/routes/plugins.ts +998 -0
  236. package/src/routes/readiness.ts +83 -0
  237. package/src/routes/workflow-management.ts +469 -0
  238. package/src/routes/workflows.ts +256 -0
  239. package/src/server.ts +203 -0
  240. package/src/services/historical-metrics.ts +496 -0
  241. package/src/services/incident-analyzer.ts +289 -0
  242. package/src/services/incident-detector.ts +691 -0
  243. package/src/services/incident-schema.sql +76 -0
  244. package/src/services/incident-storage.ts +593 -0
  245. package/src/services/system-metrics-collector.ts +244 -0
  246. package/src/types/fastify.d.ts +58 -0
  247. package/src/utils/__tests__/sse-auth.spec.ts +113 -0
  248. package/src/utils/path-helpers.ts +15 -0
  249. package/src/utils/repo.ts +42 -0
  250. package/src/utils/schema.ts +20 -0
  251. package/src/utils/sse-auth.ts +81 -0
  252. package/test-sandbox.sh +105 -0
  253. package/tsconfig.build.json +15 -0
  254. package/tsconfig.json +9 -0
  255. package/tsup.config.ts +18 -0
@@ -0,0 +1,998 @@
1
+ /**
2
+ * @module @kb-labs/rest-api-app/routes/plugins
3
+ * Plugin routes registration
4
+ *
5
+ * Uses @kb-labs/plugin-execution for unified execution layer.
6
+ */
7
+
8
+ import type { FastifyInstance } from 'fastify';
9
+ import type { RestApiConfig } from '@kb-labs/rest-api-core';
10
+ import type { IEntityRegistry, RegistrySnapshot } from '@kb-labs/core-registry';
11
+ import type { ManifestV3 } from '@kb-labs/plugin-contracts';
12
+ import { validateManifest } from '@kb-labs/plugin-contracts';
13
+ import { mountRoutes } from '@kb-labs/plugin-execution/http';
14
+ import { mountWebSocketChannels } from '@kb-labs/plugin-execution';
15
+ import { combineManifestsToRegistry } from '@kb-labs/rest-api-core';
16
+ import { platform } from '@kb-labs/core-runtime';
17
+ import {
18
+ logDiagnosticEvent,
19
+ type DiagnosticLogLevel,
20
+ } from '@kb-labs/core-platform';
21
+ import type { DiagnosticReasonCode } from '@kb-labs/core-contracts';
22
+ import * as path from 'node:path';
23
+ import * as fs from 'node:fs/promises';
24
+ import type { ReadinessState } from './readiness';
25
+ import { resolveWorkspaceRoot } from '@kb-labs/core-workspace';
26
+ import { metricsCollector, restDomainOperationMetrics } from '../middleware/metrics.js';
27
+ import { performance } from 'node:perf_hooks';
28
+
29
+ interface SnapshotManifestEntry {
30
+ pluginId: string;
31
+ manifest: ManifestV3;
32
+ pluginRoot: string;
33
+ }
34
+
35
+ function extractSnapshotManifests(snapshot: RegistrySnapshot): SnapshotManifestEntry[] {
36
+ return (snapshot.manifests || []).map(entry => ({
37
+ pluginId: entry.pluginId,
38
+ manifest: entry.manifest,
39
+ pluginRoot: entry.pluginRoot,
40
+ }));
41
+ }
42
+
43
+ /**
44
+ * Register plugin routes
45
+ */
46
+ export async function registerPluginRoutes(
47
+ server: FastifyInstance,
48
+ config: RestApiConfig,
49
+ repoRoot: string,
50
+ registry: IEntityRegistry,
51
+ readiness?: ReadinessState
52
+ ): Promise<void> {
53
+ const gatewayTimeoutMs = config.timeouts?.requestTimeout ?? 30_000;
54
+ // Stats aggregated from sequential mount results
55
+ const stats = {
56
+ mountedRoutes: 0,
57
+ mountedChannels: 0,
58
+ errors: 0,
59
+ };
60
+
61
+ if (readiness) {
62
+ readiness.pluginRoutesMounted = false;
63
+ readiness.pluginMountInProgress = true;
64
+ readiness.pluginRoutesCount = 0;
65
+ readiness.pluginRouteErrors = 0;
66
+ readiness.pluginRouteFailures = [];
67
+ readiness.pluginRoutesLastDurationMs = null;
68
+ }
69
+
70
+ metricsCollector.resetPluginRouteBudgets();
71
+
72
+ const mountMetrics = metricsCollector.beginPluginMount();
73
+ const mountStart = performance.now();
74
+
75
+ try {
76
+ const workspaceResolution = await resolveWorkspaceRoot({
77
+ startDir: repoRoot,
78
+ env: {
79
+ KB_LABS_WORKSPACE_ROOT: process.env.KB_LABS_WORKSPACE_ROOT,
80
+ KB_LABS_REPO_ROOT: process.env.KB_LABS_REPO_ROOT,
81
+ },
82
+ });
83
+ const workspaceRoot = workspaceResolution.rootDir;
84
+ platform.logger.info('Resolved workspace root', {
85
+ repoRoot,
86
+ workspaceRoot,
87
+ source: workspaceResolution.source,
88
+ });
89
+
90
+ const snapshot = registry.snapshot();
91
+ const manifests = extractSnapshotManifests(snapshot);
92
+
93
+ // Log discovery summary
94
+ platform.logger.info('Plugin discovery summary', {
95
+ totalManifests: manifests.length,
96
+ rev: snapshot.rev,
97
+ partial: snapshot.partial,
98
+ stale: snapshot.stale,
99
+ plugins: manifests.map(e => ({
100
+ id: e.manifest.id,
101
+ restRoutes: e.manifest.rest?.routes?.length ?? 0,
102
+ wsChannels: e.manifest.ws?.channels?.length ?? 0,
103
+ })),
104
+ });
105
+
106
+ if (snapshot.partial || snapshot.stale) {
107
+ if (snapshot.partial) {
108
+ logDiagnosticEvent(platform.logger, {
109
+ domain: 'registry',
110
+ event: 'plugin.registry.snapshot',
111
+ level: 'warn',
112
+ reasonCode: 'snapshot_partial',
113
+ message: 'Registry snapshot is partial',
114
+ outcome: 'failed',
115
+ serviceId: 'rest',
116
+ evidence: {
117
+ rev: snapshot.rev,
118
+ generatedAt: snapshot.generatedAt,
119
+ },
120
+ });
121
+ }
122
+
123
+ if (snapshot.stale) {
124
+ logDiagnosticEvent(platform.logger, {
125
+ domain: 'registry',
126
+ event: 'plugin.registry.snapshot',
127
+ level: 'warn',
128
+ reasonCode: 'snapshot_stale',
129
+ message: 'Registry snapshot is stale',
130
+ outcome: 'failed',
131
+ serviceId: 'rest',
132
+ evidence: {
133
+ rev: snapshot.rev,
134
+ generatedAt: snapshot.generatedAt,
135
+ expiresAt: snapshot.expiresAt,
136
+ },
137
+ });
138
+ }
139
+ }
140
+
141
+ emitRegistrySnapshotDiagnostics(snapshot);
142
+
143
+ // Use platform's unified ExecutionBackend (initialized in bootstrap.ts)
144
+ const backend = platform.executionBackend;
145
+
146
+ // Filter plugins that have routes or channels to mount
147
+ const mountableManifests = manifests
148
+ .filter(entry =>
149
+ (entry.manifest.rest?.routes && entry.manifest.rest.routes.length > 0) ||
150
+ (entry.manifest.ws?.channels && entry.manifest.ws.channels.length > 0)
151
+ );
152
+
153
+ platform.logger.info('Plugins selected for route mounting', {
154
+ mountable: mountableManifests.length,
155
+ skipped: manifests.length - mountableManifests.length,
156
+ mountablePlugins: mountableManifests.map(e => e.manifest.id),
157
+ });
158
+
159
+ // ── Phase 1: Batch-validate all handler files in one I/O pass ──
160
+ // Collect all handler file paths across all plugins, then validate in a single Promise.all()
161
+ // This avoids interleaved fs.access() calls that caused race conditions with parallel mounting.
162
+ //
163
+ // Handler paths in manifests are relative to the plugin's dist/ directory, because manifests
164
+ // are loaded from dist/manifest.js. pluginRoot points to the package root, so we resolve
165
+ // handlers relative to pluginRoot/dist/ to match where the compiled files actually live.
166
+ const handlerChecks: Array<{ key: string; filePath: string }> = [];
167
+ for (const entry of mountableManifests) {
168
+ if (entry.manifest.rest?.routes) {
169
+ for (const route of entry.manifest.rest.routes) {
170
+ const handlerFile = route.handler.split('#')[0];
171
+ if (handlerFile) {
172
+ const pluginDistRoot = path.join(entry.pluginRoot, 'dist');
173
+ const handlerPath = path.resolve(pluginDistRoot, handlerFile);
174
+ handlerChecks.push({
175
+ key: `${entry.manifest.id}::${route.method} ${route.path}`,
176
+ filePath: handlerPath,
177
+ });
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ const handlerAccessResults = await Promise.all(
184
+ handlerChecks.map(async (check) => {
185
+ try {
186
+ await fs.access(check.filePath);
187
+ return { key: check.key, filePath: check.filePath, exists: true };
188
+ } catch {
189
+ return { key: check.key, filePath: check.filePath, exists: false };
190
+ }
191
+ })
192
+ );
193
+
194
+ // Build lookup: "pluginId::METHOD /path" → { exists, filePath }
195
+ const handlerExistsMap = new Map<string, { exists: boolean; filePath: string }>();
196
+ for (const result of handlerAccessResults) {
197
+ handlerExistsMap.set(result.key, { exists: result.exists, filePath: result.filePath });
198
+ }
199
+
200
+ platform.logger.debug('Batch handler file validation completed', {
201
+ totalHandlers: handlerChecks.length,
202
+ validHandlers: handlerAccessResults.filter(r => r.exists).length,
203
+ invalidHandlers: handlerAccessResults.filter(r => !r.exists).length,
204
+ });
205
+
206
+ // ── Phase 2: Sequential plugin mounting ──
207
+ // Routes are registered in-memory on Fastify (~0.1ms per route).
208
+ // Sequential mounting eliminates race conditions with zero performance cost.
209
+ let mountedRoutes = 0;
210
+ let mountedChannels = 0;
211
+ let errors = 0;
212
+ const succeeded: string[] = [];
213
+ const failed: string[] = [];
214
+ const routeFailures: Array<{ id: string; error: string }> = [];
215
+
216
+ for (const entry of mountableManifests) {
217
+ const { manifest, pluginRoot } = entry;
218
+ const pluginDistRoot = path.join(pluginRoot, 'dist');
219
+
220
+ // Validate routes using pre-built handler map (no I/O here)
221
+ const restValidationErrors: string[] = [];
222
+ if (manifest.rest?.routes) {
223
+ for (const route of manifest.rest.routes) {
224
+ const handlerFile = route.handler.split('#')[0];
225
+ if (!handlerFile) {
226
+ restValidationErrors.push(
227
+ `Route ${route.method} ${route.path}: Invalid handler reference "${route.handler}"`
228
+ );
229
+ continue;
230
+ }
231
+ const lookupKey = `${manifest.id}::${route.method} ${route.path}`;
232
+ const check = handlerExistsMap.get(lookupKey);
233
+ if (check && !check.exists) {
234
+ restValidationErrors.push(
235
+ `Route ${route.method} ${route.path}: Handler file not found: ${check.filePath}`
236
+ );
237
+ }
238
+ }
239
+ }
240
+
241
+ if (restValidationErrors.length > 0) {
242
+ const reasonCode = inferRouteValidationReasonCode(restValidationErrors);
243
+ logDiagnosticEvent(platform.logger, {
244
+ event: 'plugin.routes.validation',
245
+ level: resolveRouteValidationLogLevel(restValidationErrors, manifest.rest?.routes?.length ?? 0),
246
+ reasonCode,
247
+ message: 'Plugin route validation failed',
248
+ outcome: 'failed',
249
+ pluginId: manifest.id,
250
+ pluginVersion: manifest.version,
251
+ serviceId: 'rest',
252
+ issues: restValidationErrors,
253
+ remediation: 'Verify handler file paths, exports, and route schema.',
254
+ evidence: {
255
+ pluginRoot,
256
+ totalRoutes: manifest.rest?.routes?.length ?? 0,
257
+ },
258
+ });
259
+
260
+ platform.logger.warn('REST validation errors found, will skip problematic routes but continue mounting others', {
261
+ plugin: `${manifest.id}@${manifest.version}`,
262
+ pluginRoot,
263
+ remediation: 'Verify REST handler file and export exist',
264
+ errors: restValidationErrors,
265
+ });
266
+
267
+ const errorPaths = new Set(restValidationErrors.map((error) => {
268
+ const match = error.match(/Route\s+(\w+)\s+([^\s:]+)/);
269
+ return match ? `${match[1]} ${match[2]}` : null;
270
+ }).filter(Boolean));
271
+
272
+ const validRoutes = manifest.rest!.routes!.filter((route) => {
273
+ const routeKey = `${route.method} ${route.path}`;
274
+ return !errorPaths.has(routeKey);
275
+ });
276
+
277
+ if (validRoutes.length === 0) {
278
+ restDomainOperationMetrics.recordOperation('plugin.routes.mount', 0, 'error');
279
+ logDiagnosticEvent(platform.logger, {
280
+ event: 'plugin.routes.validation',
281
+ level: 'error',
282
+ reasonCode,
283
+ message: 'All plugin routes failed validation; skipping plugin',
284
+ outcome: 'failed',
285
+ pluginId: manifest.id,
286
+ pluginVersion: manifest.version,
287
+ serviceId: 'rest',
288
+ issues: restValidationErrors,
289
+ evidence: {
290
+ pluginRoot,
291
+ },
292
+ });
293
+ errors += 1;
294
+ failed.push(manifest.id);
295
+ routeFailures.push({
296
+ id: manifest.id,
297
+ error: summarizeValidationErrors(restValidationErrors),
298
+ });
299
+ continue;
300
+ }
301
+
302
+ manifest.rest!.routes = validRoutes;
303
+ platform.logger.info('Filtered routes, mounting valid ones', {
304
+ plugin: `${manifest.id}@${manifest.version}`,
305
+ totalRoutes: manifest.rest!.routes.length + restValidationErrors.length,
306
+ validRoutes: validRoutes.length,
307
+ skippedRoutes: restValidationErrors.length,
308
+ });
309
+ }
310
+
311
+ try {
312
+ const start = performance.now();
313
+
314
+ let pluginBasePath: string;
315
+ if (manifest.rest?.basePath) {
316
+ if (manifest.rest.basePath.startsWith('/api/')) {
317
+ pluginBasePath = manifest.rest.basePath;
318
+ } else if (manifest.rest.basePath.startsWith('/v1/')) {
319
+ pluginBasePath = manifest.rest.basePath.replace(/^\/v1/, config.basePath);
320
+ } else {
321
+ pluginBasePath = `${config.basePath}${manifest.rest.basePath}`;
322
+ }
323
+ } else {
324
+ pluginBasePath = `${config.basePath}/plugins/${manifest.id}`;
325
+ }
326
+
327
+ platform.logger.info('Mounting plugin routes', {
328
+ plugin: `${manifest.id}@${manifest.version}`,
329
+ configBasePath: config.basePath,
330
+ manifestBasePath: manifest.rest?.basePath,
331
+ pluginBasePath,
332
+ pluginRoot,
333
+ routes: manifest.rest?.routes?.length ?? 0,
334
+ });
335
+
336
+ await mountRoutes(server, manifest, {
337
+ backend,
338
+ pluginRoot: pluginDistRoot,
339
+ workspaceRoot,
340
+ basePath: pluginBasePath,
341
+ defaultTimeoutMs: gatewayTimeoutMs,
342
+ });
343
+
344
+ const duration = performance.now() - start;
345
+ const routesCount = manifest.rest?.routes?.length ?? 0;
346
+
347
+ for (const route of manifest.rest?.routes ?? []) {
348
+ metricsCollector.registerRouteBudget(
349
+ route.method,
350
+ `${pluginBasePath}${route.path}`,
351
+ route.timeoutMs ?? gatewayTimeoutMs,
352
+ manifest.id
353
+ );
354
+ }
355
+
356
+ mountMetrics.recordSuccess(manifest.id, routesCount, duration);
357
+ restDomainOperationMetrics.recordOperation('plugin.routes.mount', duration, 'ok');
358
+
359
+ for (const route of manifest.rest?.routes ?? []) {
360
+ platform.logger.info('Mounted route', {
361
+ plugin: manifest.id,
362
+ method: route.method,
363
+ path: `${pluginBasePath}${route.path}`,
364
+ handler: route.handler,
365
+ });
366
+ }
367
+
368
+ platform.logger.info('Successfully mounted plugin routes', {
369
+ plugin: `${manifest.id}@${manifest.version}`,
370
+ pluginBasePath,
371
+ routesCount,
372
+ durationMs: Number(duration.toFixed(2)),
373
+ });
374
+
375
+ // Mount WebSocket channels if present
376
+ let channelsCount = 0;
377
+ if (manifest.ws?.channels && manifest.ws.channels.length > 0) {
378
+ try {
379
+ const wsStart = performance.now();
380
+ let wsBasePath: string = manifest.ws.basePath || `/v1/ws/plugins/${manifest.id}`;
381
+ // Normalize: /v1/ → config.basePath (same as REST routes) so gateway
382
+ // can proxy both HTTP and WS under the same /api/v1 prefix.
383
+ if (wsBasePath.startsWith('/v1/')) {
384
+ wsBasePath = wsBasePath.replace(/^\/v1/, config.basePath);
385
+ }
386
+
387
+ platform.logger.info('Mounting WebSocket channels', {
388
+ plugin: `${manifest.id}@${manifest.version}`,
389
+ wsBasePath,
390
+ channels: manifest.ws.channels.length,
391
+ });
392
+
393
+ const wsResult = await mountWebSocketChannels(server, manifest, {
394
+ backend,
395
+ pluginRoot: pluginDistRoot,
396
+ workspaceRoot,
397
+ basePath: wsBasePath,
398
+ defaultTimeoutMs: manifest.ws.defaults?.timeoutMs ?? gatewayTimeoutMs,
399
+ defaultMaxMessageSize: manifest.ws.defaults?.maxMessageSize,
400
+ });
401
+
402
+ const wsDuration = performance.now() - wsStart;
403
+ channelsCount = wsResult.mounted;
404
+
405
+ if (wsResult.mounted > 0) {
406
+ platform.logger.info('Successfully mounted WebSocket channels', {
407
+ plugin: `${manifest.id}@${manifest.version}`,
408
+ channels: wsResult.mounted,
409
+ durationMs: Number(wsDuration.toFixed(2)),
410
+ });
411
+ }
412
+
413
+ if (wsResult.errors.length > 0) {
414
+ platform.logger.warn('WebSocket channel mounting had errors', {
415
+ plugin: `${manifest.id}@${manifest.version}`,
416
+ errors: wsResult.errors,
417
+ });
418
+ }
419
+ } catch (wsError) {
420
+ logDiagnosticEvent(platform.logger, {
421
+ event: 'plugin.ws.mount',
422
+ level: 'error',
423
+ reasonCode: 'ws_mount_failed',
424
+ message: 'Failed to mount plugin WebSocket channels',
425
+ outcome: 'failed',
426
+ error: wsError instanceof Error ? wsError : new Error(String(wsError)),
427
+ pluginId: manifest.id,
428
+ pluginVersion: manifest.version,
429
+ serviceId: 'rest',
430
+ });
431
+ platform.logger.error(
432
+ 'Failed to mount WebSocket channels',
433
+ wsError instanceof Error ? wsError : new Error(String(wsError)),
434
+ { plugin: manifest.id }
435
+ );
436
+ }
437
+ }
438
+
439
+ mountedRoutes += routesCount;
440
+ mountedChannels += channelsCount;
441
+ succeeded.push(manifest.id);
442
+ } catch (error) {
443
+ mountMetrics.recordFailure(manifest.id, shortErrorMessage(error));
444
+ restDomainOperationMetrics.recordOperation('plugin.routes.mount', 0, 'error');
445
+ logDiagnosticEvent(platform.logger, {
446
+ event: 'plugin.routes.mount',
447
+ level: 'error',
448
+ reasonCode: 'route_mount_failed',
449
+ message: 'Failed to mount plugin routes',
450
+ outcome: 'failed',
451
+ error: error instanceof Error ? error : new Error(String(error)),
452
+ pluginId: manifest.id,
453
+ pluginVersion: manifest.version,
454
+ serviceId: 'rest',
455
+ });
456
+ platform.logger.error(
457
+ 'Failed to mount plugin routes',
458
+ error instanceof Error ? error : new Error(String(error)),
459
+ { plugin: manifest.id }
460
+ );
461
+ errors += 1;
462
+ failed.push(manifest.id);
463
+ routeFailures.push({
464
+ id: manifest.id,
465
+ error: `rest_mount_failed ${shortErrorMessage(error)}`,
466
+ });
467
+ }
468
+ }
469
+
470
+ // Update stats and readiness
471
+ stats.mountedRoutes = mountedRoutes;
472
+ stats.mountedChannels = mountedChannels;
473
+ stats.errors = errors;
474
+ if (readiness) {
475
+ readiness.pluginRouteFailures = routeFailures;
476
+ }
477
+
478
+ // Log summary
479
+ platform.logger.info('Sequential plugin route and WebSocket channel mounting completed', {
480
+ total: mountableManifests.length,
481
+ succeeded: succeeded.length,
482
+ failed: failed.length,
483
+ mountedRoutes,
484
+ mountedChannels,
485
+ errors,
486
+ });
487
+ } catch (error) {
488
+ logDiagnosticEvent(platform.logger, {
489
+ domain: 'registry',
490
+ event: 'plugin.discovery',
491
+ level: 'error',
492
+ reasonCode: 'plugin_discovery_failed',
493
+ message: 'Plugin discovery failed during route registration',
494
+ outcome: 'failed',
495
+ error: error instanceof Error ? error : new Error(String(error)),
496
+ serviceId: 'rest',
497
+ });
498
+ platform.logger.error(
499
+ 'Plugin discovery failed',
500
+ error instanceof Error ? error : new Error(String(error))
501
+ );
502
+ stats.errors += 1;
503
+ if (readiness) {
504
+ readiness.pluginRoutesCount = stats.mountedRoutes;
505
+ readiness.pluginRouteErrors = stats.errors;
506
+ readiness.pluginRoutesMounted = false;
507
+ readiness.pluginMountInProgress = false;
508
+ readiness.pluginRoutesLastDurationMs = null;
509
+ readiness.pluginRouteFailures.push({
510
+ id: 'discovery',
511
+ error: `rest_discovery_failed ${shortErrorMessage(error)}`,
512
+ });
513
+ }
514
+ metricsCollector.completePluginMount(platform.logger as any);
515
+ return;
516
+ }
517
+
518
+ if (readiness) {
519
+ readiness.pluginRoutesCount = stats.mountedRoutes;
520
+ readiness.pluginRouteErrors = stats.errors;
521
+ readiness.pluginRoutesMounted = stats.errors === 0;
522
+ readiness.pluginMountInProgress = false;
523
+ readiness.lastPluginMountTs = new Date().toISOString();
524
+ readiness.pluginRoutesLastDurationMs = Number(
525
+ (performance.now() - mountStart).toFixed(2)
526
+ );
527
+ }
528
+
529
+ metricsCollector.completePluginMount(platform.logger as any);
530
+ }
531
+
532
+ /**
533
+ * Register plugin snapshot endpoints for Studio
534
+ */
535
+ export async function registerPluginSnapshotRoutes(
536
+ server: FastifyInstance,
537
+ config: RestApiConfig,
538
+ registry: IEntityRegistry
539
+ ): Promise<void> {
540
+ const basePath = config.basePath;
541
+
542
+ server.post(`${basePath}/plugins/refresh`, async (_request, reply) => {
543
+ try {
544
+ await restDomainOperationMetrics.observeOperation('plugin.registry.refresh', async () => {
545
+ await registry.refresh();
546
+ });
547
+ const snapshot = await restDomainOperationMetrics.observeOperation('plugin.registry.snapshot', async () =>
548
+ registry.snapshot(),
549
+ );
550
+ emitRegistrySnapshotDiagnostics(snapshot);
551
+
552
+ reply.type('application/json');
553
+ return {
554
+ ok: true,
555
+ data: {
556
+ rev: snapshot.rev,
557
+ total: snapshot.plugins.length,
558
+ generatedAt: snapshot.generatedAt,
559
+ partial: snapshot.partial,
560
+ stale: snapshot.stale,
561
+ },
562
+ };
563
+ } catch (error) {
564
+ logDiagnosticEvent(platform.logger, {
565
+ domain: 'registry',
566
+ event: 'plugin.registry.refresh',
567
+ level: 'error',
568
+ reasonCode: 'registry_refresh_failed',
569
+ message: 'Failed to refresh plugin registry',
570
+ outcome: 'failed',
571
+ error: error instanceof Error ? error : new Error(String(error)),
572
+ serviceId: 'rest',
573
+ });
574
+ platform.logger.error(
575
+ 'Failed to refresh plugin registry',
576
+ error instanceof Error ? error : new Error(String(error))
577
+ );
578
+ reply.code(500).send({
579
+ ok: false,
580
+ error: 'Failed to refresh plugin registry',
581
+ message: error instanceof Error ? error.message : String(error),
582
+ });
583
+ }
584
+ });
585
+
586
+ // Raw manifests endpoint for Studio/debug consumers
587
+ server.get(`${basePath}/plugins/registry`, async (_request, reply) => {
588
+ try {
589
+ const snapshot = await restDomainOperationMetrics.observeOperation('plugin.registry.snapshot', async () =>
590
+ registry.snapshot(),
591
+ );
592
+
593
+ // Get build timestamps and validate manifests
594
+ const manifestsWithTimestamps = await Promise.all(
595
+ snapshot.manifests.map(async (entry) => {
596
+ let buildTimestamp: string | undefined;
597
+ try {
598
+ const distPath = path.join(entry.pluginRoot, 'dist');
599
+ const stats = await fs.stat(distPath);
600
+ buildTimestamp = stats.mtime.toISOString();
601
+ } catch {
602
+ // dist/ doesn't exist or not accessible, skip
603
+ }
604
+
605
+ // Validate manifest structure
606
+ const validation = validateManifest(entry.manifest);
607
+
608
+ return {
609
+ pluginId: entry.pluginId,
610
+ manifest: entry.manifest,
611
+ pluginRoot: entry.pluginRoot,
612
+ source: entry.source,
613
+ discoveredAt: snapshot.generatedAt,
614
+ buildTimestamp,
615
+ validation: {
616
+ valid: validation.valid,
617
+ errors: validation.errors,
618
+ },
619
+ };
620
+ })
621
+ );
622
+
623
+ reply.type('application/json');
624
+ return {
625
+ manifests: manifestsWithTimestamps,
626
+ apiBasePath: basePath,
627
+ diagnostics: {
628
+ total: snapshot.diagnostics?.length ?? 0,
629
+ items: (snapshot.diagnostics ?? []).map((diagnostic) => ({
630
+ severity: diagnostic.severity,
631
+ code: diagnostic.code,
632
+ reasonCode: mapRegistryDiagnosticReasonCode(diagnostic.code),
633
+ message: diagnostic.message,
634
+ pluginId: diagnostic.context?.pluginId,
635
+ filePath: diagnostic.context?.filePath,
636
+ remediation: diagnostic.remediation,
637
+ ts: diagnostic.ts,
638
+ })),
639
+ },
640
+ };
641
+ } catch (error) {
642
+ platform.logger.error(
643
+ 'Failed to get plugin registry',
644
+ error instanceof Error ? error : new Error(String(error))
645
+ );
646
+ reply.code(500).send({
647
+ error: 'Failed to load plugin registry',
648
+ message: error instanceof Error ? error.message : String(error),
649
+ });
650
+ }
651
+ });
652
+
653
+ // Studio Registry V2 — returns plugin pages and menus for Module Federation
654
+ server.get(`${basePath}/studio/registry`, {
655
+ schema: { tags: ['Studio'], summary: 'Get Studio Registry V2 (plugin pages for Module Federation)' },
656
+ }, async (_request, reply) => {
657
+ try {
658
+ const snapshot = await restDomainOperationMetrics.observeOperation('plugin.studio.registry', async () =>
659
+ registry.snapshot(),
660
+ );
661
+ const manifests = extractSnapshotManifests(snapshot);
662
+
663
+ // Only plugins with studio V2 config
664
+ const studioManifests = manifests
665
+ .filter(entry => entry.manifest.studio?.version === 2)
666
+ .map(entry => ({ manifest: entry.manifest, pluginRoot: entry.pluginRoot }));
667
+
668
+ const studioRegistry = combineManifestsToRegistry(studioManifests);
669
+
670
+ reply.type('application/json');
671
+ return studioRegistry;
672
+ } catch (error) {
673
+ platform.logger.error('Failed to generate studio registry', error instanceof Error ? error : new Error(String(error)));
674
+ reply.code(500).send({
675
+ error: 'Failed to generate studio registry',
676
+ message: error instanceof Error ? error.message : String(error),
677
+ });
678
+ }
679
+ });
680
+
681
+ // Widget bundle static files — serves dist/widgets/ from plugin packages
682
+ server.get<{
683
+ Params: { scope: string; name: string; '*': string };
684
+ }>('/plugins/@:scope/:name/widgets/*', {
685
+ schema: { hide: true },
686
+ }, async (request, reply) => {
687
+ const { scope, name } = request.params;
688
+ const filePath = request.params['*'];
689
+
690
+ if (!scope || !name || !filePath || filePath.includes('..')) {
691
+ return reply.code(400).send({ error: 'Invalid path' });
692
+ }
693
+
694
+ const pluginId = `@${scope}/${name}`;
695
+
696
+ // Find the plugin's widgetBundleDir from registry
697
+ const snapshot = registry.snapshot();
698
+ const manifests = extractSnapshotManifests(snapshot);
699
+ const entry = manifests.find(e => e.manifest.id === pluginId && e.manifest.studio?.version === 2);
700
+
701
+ if (!entry) {
702
+ return reply.code(404).send({ error: `No widget bundle for ${pluginId}` });
703
+ }
704
+
705
+ const { join, extname } = await import('node:path');
706
+ const { createReadStream, existsSync, statSync } = await import('node:fs');
707
+
708
+ const bundleDir = join(entry.pluginRoot, 'dist', 'widgets');
709
+ const fullPath = join(bundleDir, filePath);
710
+
711
+ if (!fullPath.startsWith(bundleDir)) {
712
+ return reply.code(400).send({ error: 'Invalid path' });
713
+ }
714
+
715
+ if (!existsSync(fullPath) || !statSync(fullPath).isFile()) {
716
+ return reply.code(404).send({ error: `Widget file not found: ${pluginId}/widgets/${filePath}` });
717
+ }
718
+
719
+ const MIME: Record<string, string> = {
720
+ '.js': 'application/javascript', '.mjs': 'application/javascript',
721
+ '.css': 'text/css', '.json': 'application/json', '.map': 'application/json',
722
+ };
723
+
724
+ const ext = extname(filePath);
725
+ const isEntry = filePath === 'remoteEntry.js';
726
+
727
+ return reply
728
+ .code(200)
729
+ .header('Content-Type', MIME[ext] ?? 'application/octet-stream')
730
+ .header('Content-Length', statSync(fullPath).size)
731
+ .header('Cache-Control', isEntry ? 'public, max-age=10, must-revalidate' : 'public, max-age=31536000, immutable')
732
+ .send(createReadStream(fullPath));
733
+ });
734
+
735
+ // Plugin registry health endpoint
736
+ server.get(`${basePath}/plugins/health`, async (_request, reply) => {
737
+ try {
738
+ const snapshot = await restDomainOperationMetrics.observeOperation('plugin.registry.health', async () =>
739
+ registry.snapshot(),
740
+ );
741
+
742
+ // Collect validation errors from all manifests
743
+ const validationIssues: Array<{ pluginId: string; errors: string[] }> = [];
744
+ for (const entry of snapshot.manifests) {
745
+ const validation = validateManifest(entry.manifest);
746
+ if (!validation.valid) {
747
+ validationIssues.push({
748
+ pluginId: entry.pluginId,
749
+ errors: validation.errors,
750
+ });
751
+ }
752
+ }
753
+
754
+ // Registry resolution errors (plugins that failed to load)
755
+ const registryErrors = (snapshot as any).errors ?? [];
756
+ const discoveryDiagnostics = snapshot.diagnostics ?? [];
757
+ const discoveryErrorCount = discoveryDiagnostics.filter((diagnostic) => diagnostic.severity === 'error').length;
758
+
759
+ // Determine why registry is partial
760
+ const partialReasons: string[] = [];
761
+ if (snapshot.corrupted) {
762
+ partialReasons.push('snapshot_corrupted');
763
+ }
764
+ if (registryErrors.length > 0) {
765
+ partialReasons.push('registry_errors');
766
+ }
767
+ if (discoveryErrorCount > 0) {
768
+ partialReasons.push('discovery_diagnostics');
769
+ }
770
+ // If partial but no clear reason, it's likely initialization
771
+ if (snapshot.partial && partialReasons.length === 0) {
772
+ partialReasons.push('initialization_incomplete');
773
+ }
774
+
775
+ reply.type('application/json');
776
+ return {
777
+ healthy: !snapshot.partial && !snapshot.stale && validationIssues.length === 0 && registryErrors.length === 0 && discoveryErrorCount === 0,
778
+ snapshot: {
779
+ partial: snapshot.partial,
780
+ stale: snapshot.stale,
781
+ corrupted: snapshot.corrupted,
782
+ rev: snapshot.rev,
783
+ generatedAt: snapshot.generatedAt,
784
+ totalManifests: snapshot.manifests.length,
785
+ partialReasons,
786
+ },
787
+ registryErrors: {
788
+ total: registryErrors.length,
789
+ items: registryErrors.map((err: any) => ({
790
+ pluginPath: err.pluginPath,
791
+ pluginId: err.pluginId,
792
+ error: err.error,
793
+ code: err.code,
794
+ })),
795
+ },
796
+ diagnostics: {
797
+ total: discoveryDiagnostics.length,
798
+ errors: discoveryErrorCount,
799
+ warnings: discoveryDiagnostics.filter((diagnostic) => diagnostic.severity === 'warning').length,
800
+ items: discoveryDiagnostics.map((diagnostic) => ({
801
+ severity: diagnostic.severity,
802
+ code: diagnostic.code,
803
+ reasonCode: mapRegistryDiagnosticReasonCode(diagnostic.code),
804
+ message: diagnostic.message,
805
+ pluginId: diagnostic.context?.pluginId,
806
+ filePath: diagnostic.context?.filePath,
807
+ remediation: diagnostic.remediation,
808
+ ts: diagnostic.ts,
809
+ })),
810
+ },
811
+ validation: {
812
+ totalIssues: validationIssues.length,
813
+ issues: validationIssues,
814
+ },
815
+ message:
816
+ registryErrors.length > 0
817
+ ? `Registry is partial - ${registryErrors.length} plugin(s) failed to load. Check registryErrors.items for details.`
818
+ : discoveryErrorCount > 0
819
+ ? `Registry has discovery diagnostics - ${discoveryErrorCount} blocking issue(s). Check diagnostics.items for details.`
820
+ : snapshot.partial && partialReasons.includes('initialization_incomplete')
821
+ ? 'Registry is partial - initialization incomplete. Try refreshing or wait for the snapshot rebuild to complete.'
822
+ : snapshot.partial
823
+ ? `Registry is partial - reasons: ${partialReasons.join(', ')}`
824
+ : snapshot.stale
825
+ ? 'Registry is stale - plugin snapshot may be outdated.'
826
+ : validationIssues.length > 0
827
+ ? `${validationIssues.length} plugin(s) have validation errors.`
828
+ : 'All plugins are healthy.',
829
+ };
830
+ } catch (error) {
831
+ platform.logger.error('Failed to get plugin health', error instanceof Error ? error : new Error(String(error)));
832
+ reply.code(500).send({
833
+ error: 'Failed to get plugin health',
834
+ message: error instanceof Error ? error.message : String(error),
835
+ });
836
+ }
837
+ });
838
+
839
+ // AI Assistant endpoint: ask questions about a plugin
840
+ server.post(`${basePath}/plugins/:pluginId/ask`, async (request, reply) => {
841
+ try {
842
+ const { pluginId } = request.params as { pluginId: string };
843
+ const { question } = request.body as { question: string };
844
+
845
+ if (!question || typeof question !== 'string') {
846
+ reply.code(400).send({
847
+ error: 'Bad request',
848
+ message: 'Question is required and must be a string',
849
+ });
850
+ return;
851
+ }
852
+
853
+ // Get plugin manifest
854
+ const snapshot = await restDomainOperationMetrics.observeOperation('plugin.registry.snapshot', async () =>
855
+ registry.snapshot(),
856
+ );
857
+ const pluginEntry = snapshot.manifests.find((entry) => entry.pluginId === pluginId);
858
+
859
+ if (!pluginEntry) {
860
+ reply.code(404).send({
861
+ error: 'Plugin not found',
862
+ message: `Plugin ${pluginId} not found in registry`,
863
+ });
864
+ return;
865
+ }
866
+
867
+ // Build prompt for LLM
868
+ const systemPrompt = `You are a helpful assistant that explains KB Labs plugins based on their manifest.
869
+ Provide clear, concise answers about the plugin's capabilities, API endpoints, permissions, and usage.
870
+ Be specific and reference the actual values from the manifest.`;
871
+
872
+ const userPrompt = `Plugin Manifest:
873
+ ${JSON.stringify(pluginEntry.manifest, null, 2)}
874
+
875
+ User Question: ${question}
876
+
877
+ Please answer the question based on the plugin manifest above.`;
878
+
879
+ // Call LLM
880
+ const response = await restDomainOperationMetrics.observeOperation('plugin.assistant.ask', () =>
881
+ platform.llm.complete(userPrompt, {
882
+ systemPrompt,
883
+ temperature: 0.3,
884
+ maxTokens: 1000,
885
+ }),
886
+ );
887
+
888
+ reply.type('application/json');
889
+ return {
890
+ answer: response.content,
891
+ usage: response.usage,
892
+ };
893
+ } catch (error) {
894
+ platform.logger.error('Failed to get AI answer about plugin', error instanceof Error ? error : new Error(String(error)));
895
+ reply.code(500).send({
896
+ error: 'Failed to get AI answer',
897
+ message: error instanceof Error ? error.message : String(error),
898
+ });
899
+ }
900
+ });
901
+ }
902
+
903
+ /**
904
+ * Shutdown execution backend gracefully.
905
+ * NOTE: ExecutionBackend is now owned by platform, shutdown via platform.shutdown()
906
+ */
907
+ export async function shutdownExecutionBackend(): Promise<void> {
908
+ // ExecutionBackend lifecycle is now managed by platform
909
+ // Call platform.shutdown() instead to shutdown all services including executionBackend
910
+ await platform.shutdown();
911
+ }
912
+
913
+ function summarizeValidationErrors(errors: string[]): string {
914
+ if (errors.length === 0) {
915
+ return 'rest_validation_failed';
916
+ }
917
+ const first = errors[0] || 'rest_validation_failed';
918
+ const prefix = first.split(':')[0]?.trim() ?? 'rest_validation_failed';
919
+ return `rest_validation_failed ${prefix}`.trim();
920
+ }
921
+
922
+ function shortErrorMessage(error: unknown): string {
923
+ if (!error) {
924
+ return 'unknown_error';
925
+ }
926
+ const message = error instanceof Error ? error.message : String(error);
927
+ const firstLine = message.trim().split('\n')[0] ?? message.trim();
928
+ return firstLine.length > 120 ? `${firstLine.slice(0, 117)}...` : firstLine;
929
+ }
930
+
931
+ function mapDiagnosticSeverityToLogLevel(
932
+ severity: 'error' | 'warning' | 'info' | 'debug',
933
+ ): DiagnosticLogLevel {
934
+ switch (severity) {
935
+ case 'error':
936
+ return 'error';
937
+ case 'warning':
938
+ return 'warn';
939
+ case 'debug':
940
+ return 'debug';
941
+ default:
942
+ return 'info';
943
+ }
944
+ }
945
+
946
+ function mapRegistryDiagnosticReasonCode(code: string): DiagnosticReasonCode {
947
+ switch (code) {
948
+ case 'MANIFEST_NOT_FOUND':
949
+ return 'manifest_missing';
950
+ case 'MANIFEST_VALIDATION_ERROR':
951
+ case 'MANIFEST_PARSE_ERROR':
952
+ return 'manifest_invalid';
953
+ case 'MANIFEST_LOAD_TIMEOUT':
954
+ return 'manifest_load_timeout';
955
+ case 'INTEGRITY_MISMATCH':
956
+ return 'integrity_mismatch';
957
+ default:
958
+ return 'plugin_discovery_failed';
959
+ }
960
+ }
961
+
962
+ function emitRegistrySnapshotDiagnostics(snapshot: RegistrySnapshot): void {
963
+ for (const diagnostic of snapshot.diagnostics ?? []) {
964
+ logDiagnosticEvent(platform.logger, {
965
+ domain: 'registry',
966
+ event: 'plugin.discovery.diagnostic',
967
+ level: mapDiagnosticSeverityToLogLevel(diagnostic.severity),
968
+ reasonCode: mapRegistryDiagnosticReasonCode(diagnostic.code),
969
+ message: diagnostic.message,
970
+ outcome: diagnostic.severity === 'error' ? 'failed' : 'skipped',
971
+ pluginId: diagnostic.context?.pluginId,
972
+ serviceId: 'rest',
973
+ manifestPath: diagnostic.context?.filePath,
974
+ discoveryCode: diagnostic.code,
975
+ remediation: diagnostic.remediation,
976
+ evidence: {
977
+ severity: diagnostic.severity,
978
+ entityKind: diagnostic.context?.entityKind,
979
+ entityId: diagnostic.context?.entityId,
980
+ ts: diagnostic.ts,
981
+ },
982
+ });
983
+ }
984
+ }
985
+
986
+ function inferRouteValidationReasonCode(errors: string[]): DiagnosticReasonCode {
987
+ if (errors.some((error) => error.includes('Handler file not found'))) {
988
+ return 'handler_not_found';
989
+ }
990
+ return 'route_validation_failed';
991
+ }
992
+
993
+ function resolveRouteValidationLogLevel(
994
+ errors: string[],
995
+ totalRoutes: number,
996
+ ): DiagnosticLogLevel {
997
+ return errors.length >= totalRoutes ? 'error' : 'warn';
998
+ }