@kb-labs/rest-api-app 1.3.0 → 1.5.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 (257) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +8151 -0
  3. package/dist/index.js.map +1 -0
  4. package/package.json +23 -20
  5. package/.kb/agents/sessions/session-1771531145393-ws3uh6q/turns.json +0 -42
  6. package/.kb/agents/sessions/session-1771548930486-ui6rtvm/turns.json +0 -33
  7. package/.kb/agents/sessions/session-1771549691485-nibqroq/turns.json +0 -133
  8. package/.kb/agents/sessions/session-1771550943182-dkwqdjc/turns.json +0 -130
  9. package/.kb/agents/sessions/session-1771551564182-cr4bwzn/turns.json +0 -349
  10. package/.kb/agents/sessions/session-1771552401370-g4ztwd1/turns.json +0 -206
  11. package/.kb/commit/current/plan.json +0 -248
  12. package/.kb/commit/current/status.json +0 -472
  13. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/health.d.ts +0 -10
  14. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/health.js +0 -215
  15. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/health.js.map +0 -1
  16. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/list.d.ts +0 -10
  17. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/list.js +0 -271
  18. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/list.js.map +0 -1
  19. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/logs.d.ts +0 -10
  20. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/logs.js +0 -230
  21. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/logs.js.map +0 -1
  22. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/metrics.d.ts +0 -10
  23. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/metrics.js +0 -228
  24. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/metrics.js.map +0 -1
  25. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/run.d.ts +0 -11
  26. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/run.js +0 -281
  27. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/run.js.map +0 -1
  28. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/status.d.ts +0 -10
  29. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/status.js +0 -266
  30. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/status.js.map +0 -1
  31. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/workflow-run.d.ts +0 -11
  32. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/workflow-run.js +0 -284
  33. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/workflow-run.js.map +0 -1
  34. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/flags-WLdpSZl4.d.ts +0 -77
  35. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/index.d.ts +0 -2
  36. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/index.js +0 -441
  37. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/index.js.map +0 -1
  38. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/manifest.d.ts +0 -196
  39. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/manifest.js +0 -441
  40. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/manifest.js.map +0 -1
  41. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/cron-list-handler.d.ts +0 -19
  42. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/cron-list-handler.js +0 -48
  43. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/cron-list-handler.js.map +0 -1
  44. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/index.d.ts +0 -6
  45. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/index.js +0 -170
  46. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/index.js.map +0 -1
  47. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-cancel-handler.d.ts +0 -12
  48. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-cancel-handler.js +0 -52
  49. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-cancel-handler.js.map +0 -1
  50. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-detail-handler.d.ts +0 -22
  51. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-detail-handler.js +0 -52
  52. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-detail-handler.js.map +0 -1
  53. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-logs-handler.d.ts +0 -13
  54. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-logs-handler.js +0 -52
  55. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-logs-handler.js.map +0 -1
  56. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-steps-handler.d.ts +0 -9
  57. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-steps-handler.js +0 -43
  58. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-steps-handler.js.map +0 -1
  59. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/jobs-list-handler.d.ts +0 -26
  60. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/jobs-list-handler.js +0 -63
  61. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/jobs-list-handler.js.map +0 -1
  62. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/pending-approvals-handler.d.ts +0 -9
  63. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/pending-approvals-handler.js +0 -46
  64. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/pending-approvals-handler.js.map +0 -1
  65. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/resolve-approval-handler.d.ts +0 -16
  66. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/resolve-approval-handler.js +0 -58
  67. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/resolve-approval-handler.js.map +0 -1
  68. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/run-detail-handler.d.ts +0 -9
  69. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/run-detail-handler.js +0 -46
  70. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/run-detail-handler.js.map +0 -1
  71. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/runs-list-handler.d.ts +0 -11
  72. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/runs-list-handler.js +0 -50
  73. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/runs-list-handler.js.map +0 -1
  74. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/stats-handler.d.ts +0 -7
  75. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/stats-handler.js +0 -39
  76. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/stats-handler.js.map +0 -1
  77. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-detail-handler.d.ts +0 -10
  78. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-detail-handler.js +0 -46
  79. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-detail-handler.js.map +0 -1
  80. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-cancel-handler.d.ts +0 -13
  81. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-cancel-handler.js +0 -47
  82. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-cancel-handler.js.map +0 -1
  83. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-handler.d.ts +0 -14
  84. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-handler.js +0 -53
  85. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-handler.js.map +0 -1
  86. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-runs-handler.d.ts +0 -13
  87. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-runs-handler.js +0 -54
  88. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-runs-handler.js.map +0 -1
  89. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflows-list-handler.d.ts +0 -12
  90. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflows-list-handler.js +0 -51
  91. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflows-list-handler.js.map +0 -1
  92. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/logs-channel.d.ts +0 -17
  93. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/logs-channel.js +0 -133
  94. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/logs-channel.js.map +0 -1
  95. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/progress-channel.d.ts +0 -11
  96. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/progress-channel.js +0 -46
  97. package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/progress-channel.js.map +0 -1
  98. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/health.d.ts +0 -10
  99. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/health.js +0 -215
  100. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/health.js.map +0 -1
  101. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/list.d.ts +0 -10
  102. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/list.js +0 -271
  103. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/list.js.map +0 -1
  104. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/logs.d.ts +0 -10
  105. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/logs.js +0 -230
  106. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/logs.js.map +0 -1
  107. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/metrics.d.ts +0 -10
  108. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/metrics.js +0 -228
  109. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/metrics.js.map +0 -1
  110. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/run.d.ts +0 -11
  111. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/run.js +0 -281
  112. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/run.js.map +0 -1
  113. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/status.d.ts +0 -10
  114. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/status.js +0 -266
  115. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/status.js.map +0 -1
  116. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/workflow-run.d.ts +0 -11
  117. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/workflow-run.js +0 -284
  118. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/workflow-run.js.map +0 -1
  119. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/flags-WLdpSZl4.d.ts +0 -77
  120. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/index.d.ts +0 -2
  121. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/index.js +0 -441
  122. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/index.js.map +0 -1
  123. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/manifest.d.ts +0 -196
  124. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/manifest.js +0 -441
  125. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/manifest.js.map +0 -1
  126. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/cron-list-handler.d.ts +0 -19
  127. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/cron-list-handler.js +0 -48
  128. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/cron-list-handler.js.map +0 -1
  129. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/index.d.ts +0 -6
  130. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/index.js +0 -170
  131. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/index.js.map +0 -1
  132. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-cancel-handler.d.ts +0 -12
  133. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-cancel-handler.js +0 -52
  134. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-cancel-handler.js.map +0 -1
  135. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-detail-handler.d.ts +0 -22
  136. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-detail-handler.js +0 -52
  137. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-detail-handler.js.map +0 -1
  138. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-logs-handler.d.ts +0 -13
  139. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-logs-handler.js +0 -52
  140. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-logs-handler.js.map +0 -1
  141. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-steps-handler.d.ts +0 -9
  142. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-steps-handler.js +0 -43
  143. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-steps-handler.js.map +0 -1
  144. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/jobs-list-handler.d.ts +0 -26
  145. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/jobs-list-handler.js +0 -63
  146. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/jobs-list-handler.js.map +0 -1
  147. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/pending-approvals-handler.d.ts +0 -9
  148. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/pending-approvals-handler.js +0 -46
  149. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/pending-approvals-handler.js.map +0 -1
  150. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/resolve-approval-handler.d.ts +0 -16
  151. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/resolve-approval-handler.js +0 -58
  152. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/resolve-approval-handler.js.map +0 -1
  153. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/run-detail-handler.d.ts +0 -9
  154. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/run-detail-handler.js +0 -46
  155. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/run-detail-handler.js.map +0 -1
  156. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/runs-list-handler.d.ts +0 -11
  157. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/runs-list-handler.js +0 -50
  158. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/runs-list-handler.js.map +0 -1
  159. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/stats-handler.d.ts +0 -7
  160. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/stats-handler.js +0 -39
  161. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/stats-handler.js.map +0 -1
  162. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-detail-handler.d.ts +0 -10
  163. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-detail-handler.js +0 -46
  164. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-detail-handler.js.map +0 -1
  165. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-cancel-handler.d.ts +0 -13
  166. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-cancel-handler.js +0 -47
  167. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-cancel-handler.js.map +0 -1
  168. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-handler.d.ts +0 -14
  169. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-handler.js +0 -53
  170. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-handler.js.map +0 -1
  171. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-runs-handler.d.ts +0 -13
  172. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-runs-handler.js +0 -54
  173. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-runs-handler.js.map +0 -1
  174. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflows-list-handler.d.ts +0 -12
  175. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflows-list-handler.js +0 -51
  176. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflows-list-handler.js.map +0 -1
  177. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/logs-channel.d.ts +0 -17
  178. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/logs-channel.js +0 -133
  179. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/logs-channel.js.map +0 -1
  180. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/progress-channel.d.ts +0 -11
  181. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/progress-channel.js +0 -46
  182. package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/progress-channel.js.map +0 -1
  183. package/eslint.config.js +0 -23
  184. package/src/.kb/agents/sessions/session-1773470583796-ximqnfk/kpi-baseline.json +0 -17
  185. package/src/.kb/memory/sessions/session-1773470583796-ximqnfk/fact-sheet.json +0 -1
  186. package/src/bootstrap.ts +0 -233
  187. package/src/diagnostics/header-debug.ts +0 -37
  188. package/src/events/hub.ts +0 -60
  189. package/src/events/index.ts +0 -1
  190. package/src/index.ts +0 -13
  191. package/src/manifest.ts +0 -21
  192. package/src/middleware/__tests__/cache.spec.ts +0 -184
  193. package/src/middleware/__tests__/envelope.spec.ts +0 -268
  194. package/src/middleware/__tests__/metrics.redis.spec.ts +0 -64
  195. package/src/middleware/__tests__/rate-limit.spec.ts +0 -283
  196. package/src/middleware/__tests__/request-id.spec.ts +0 -160
  197. package/src/middleware/cache.ts +0 -67
  198. package/src/middleware/envelope.ts +0 -136
  199. package/src/middleware/error-guard.ts +0 -69
  200. package/src/middleware/index.ts +0 -37
  201. package/src/middleware/metrics.ts +0 -810
  202. package/src/middleware/mock-mode.ts +0 -44
  203. package/src/middleware/prom-metrics.ts +0 -284
  204. package/src/middleware/rate-limit-routes.ts +0 -73
  205. package/src/middleware/rate-limit.ts +0 -72
  206. package/src/middleware/request-id.ts +0 -41
  207. package/src/middleware/request-timeout.ts +0 -69
  208. package/src/middleware/security-headers.ts +0 -24
  209. package/src/middleware/startup-guard.ts +0 -107
  210. package/src/observability/service-contract.ts +0 -192
  211. package/src/plugins/index.ts +0 -118
  212. package/src/routes/__tests__/adapters-date-helpers.spec.ts +0 -332
  213. package/src/routes/__tests__/adapters.spec.ts +0 -680
  214. package/src/routes/__tests__/cache.spec.ts +0 -264
  215. package/src/routes/__tests__/events.spec.ts +0 -434
  216. package/src/routes/__tests__/health.spec.ts +0 -414
  217. package/src/routes/__tests__/logs.spec.ts +0 -494
  218. package/src/routes/__tests__/observability.spec.ts +0 -392
  219. package/src/routes/__tests__/plugins.spec.ts +0 -931
  220. package/src/routes/__tests__/workflow-management.spec.ts +0 -698
  221. package/src/routes/__tests__/workflows.spec.ts +0 -204
  222. package/src/routes/adapter-call.ts +0 -220
  223. package/src/routes/adapters.ts +0 -1206
  224. package/src/routes/analytics.ts +0 -328
  225. package/src/routes/cache.ts +0 -104
  226. package/src/routes/debug-routes.ts +0 -95
  227. package/src/routes/events.ts +0 -115
  228. package/src/routes/health.ts +0 -348
  229. package/src/routes/hello.ts +0 -47
  230. package/src/routes/index.ts +0 -395
  231. package/src/routes/jobs.ts +0 -253
  232. package/src/routes/logs.ts +0 -718
  233. package/src/routes/metrics.ts +0 -120
  234. package/src/routes/observability.ts +0 -1388
  235. package/src/routes/openapi.ts +0 -101
  236. package/src/routes/platform.ts +0 -143
  237. package/src/routes/plugins.ts +0 -998
  238. package/src/routes/readiness.ts +0 -83
  239. package/src/routes/workflow-management.ts +0 -469
  240. package/src/routes/workflows.ts +0 -256
  241. package/src/server.ts +0 -203
  242. package/src/services/historical-metrics.ts +0 -496
  243. package/src/services/incident-analyzer.ts +0 -289
  244. package/src/services/incident-detector.ts +0 -691
  245. package/src/services/incident-schema.sql +0 -76
  246. package/src/services/incident-storage.ts +0 -593
  247. package/src/services/system-metrics-collector.ts +0 -244
  248. package/src/types/fastify.d.ts +0 -58
  249. package/src/utils/__tests__/sse-auth.spec.ts +0 -113
  250. package/src/utils/path-helpers.ts +0 -15
  251. package/src/utils/repo.ts +0 -42
  252. package/src/utils/schema.ts +0 -20
  253. package/src/utils/sse-auth.ts +0 -81
  254. package/test-sandbox.sh +0 -105
  255. package/tsconfig.build.json +0 -15
  256. package/tsconfig.json +0 -9
  257. package/tsup.config.ts +0 -18
@@ -1,1388 +0,0 @@
1
- /**
2
- * @module @kb-labs/rest-api-app/routes/observability
3
- * Observability endpoints for monitoring system internals
4
- */
5
-
6
- import type { FastifyInstance } from 'fastify';
7
- import type { RestApiConfig } from '@kb-labs/rest-api-core';
8
- import type { PlatformServices } from '@kb-labs/plugin-contracts';
9
- import { normalizeBasePath, resolvePaths } from '../utils/path-helpers';
10
- import { exec } from 'node:child_process';
11
- import { promisify } from 'node:util';
12
- import { hostname } from 'node:os';
13
- import type { HistoricalMetricsCollector } from '../services/historical-metrics';
14
- import type { IncidentStorage } from '../services/incident-storage';
15
- import { IncidentAnalyzer } from '../services/incident-analyzer';
16
- import type { SystemMetrics } from '../services/system-metrics-collector';
17
- import { metricsCollector } from '../middleware/metrics.js';
18
-
19
- const execAsync = promisify(exec);
20
-
21
- // DevKit health cache key and TTL (10 minutes)
22
- // This prevents excessive process spawning that can overload the system
23
- const DEVKIT_CACHE_KEY = 'observability:devkit-health';
24
- const DEVKIT_CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes
25
-
26
- /**
27
- * Register observability routes
28
- *
29
- * These are system-level observability endpoints that expose internal metrics
30
- * and health information about platform components (State Broker, DevKit, etc.)
31
- */
32
- export async function registerObservabilityRoutes(
33
- fastify: FastifyInstance,
34
- config: RestApiConfig,
35
- repoRoot: string,
36
- historicalMetrics?: HistoricalMetricsCollector,
37
- incidentStorage?: IncidentStorage,
38
- platform?: PlatformServices
39
- ): Promise<void> {
40
- const basePath = normalizeBasePath(config.basePath);
41
- const stateBrokerPaths = resolvePaths(basePath, '/observability/state-broker');
42
- const devkitPaths = resolvePaths(basePath, '/observability/devkit');
43
- const systemMetricsPaths = resolvePaths(basePath, '/observability/system-metrics');
44
- const metricsHistoryPaths = resolvePaths(basePath, '/observability/metrics/history');
45
- const metricsHeatmapPaths = resolvePaths(basePath, '/observability/metrics/heatmap');
46
- const incidentsListPaths = resolvePaths(basePath, '/observability/incidents');
47
- const incidentsDetailPaths = resolvePaths(basePath, '/observability/incidents/:id');
48
- const incidentsCreatePaths = resolvePaths(basePath, '/observability/incidents');
49
- const incidentsHistoryPaths = resolvePaths(basePath, '/observability/incidents/history');
50
- const incidentsResolvePaths = resolvePaths(basePath, '/observability/incidents/:id/resolve');
51
- const incidentsAnalyzePaths = resolvePaths(basePath, '/observability/incidents/:id/analyze');
52
- const insightsChatPaths = resolvePaths(basePath, '/observability/insights/chat');
53
-
54
- // GET /api/v1/observability/state-broker
55
- // Returns statistics from State Broker daemon (cache hits, namespaces, etc.)
56
- for (const path of stateBrokerPaths) {
57
- fastify.get(path, async (_request, reply) => {
58
- try {
59
- const stateBrokerUrl = process.env.KB_STATE_DAEMON_URL || 'http://localhost:7777';
60
-
61
- fastify.log.debug({ url: stateBrokerUrl }, 'Fetching State Broker stats');
62
-
63
- const response = await fetch(`${stateBrokerUrl}/stats`, {
64
- signal: AbortSignal.timeout(5000), // 5s timeout
65
- });
66
-
67
- if (!response.ok) {
68
- fastify.log.warn({
69
- status: response.status,
70
- url: stateBrokerUrl,
71
- }, 'State Broker responded with error');
72
-
73
- return reply.code(503).send({
74
- ok: false,
75
- error: {
76
- code: 'STATE_BROKER_UNAVAILABLE',
77
- message: 'State Broker daemon is not available',
78
- details: {
79
- url: stateBrokerUrl,
80
- status: response.status,
81
- },
82
- },
83
- });
84
- }
85
-
86
- const stats = await response.json() as any;
87
-
88
- fastify.log.debug({
89
- totalEntries: stats.totalEntries,
90
- hitRate: stats.hitRate,
91
- }, 'State Broker stats retrieved successfully');
92
-
93
- return {
94
- ok: true,
95
- data: stats,
96
- meta: {
97
- source: 'state-broker',
98
- daemonUrl: stateBrokerUrl,
99
- },
100
- };
101
- } catch (error) {
102
- platform?.logger.error('Failed to fetch State Broker stats', error instanceof Error ? error : new Error(String(error)));
103
-
104
- // Check if it's a timeout error
105
- const isTimeout = error instanceof Error && error.name === 'AbortError';
106
-
107
- return reply.code(503).send({
108
- ok: false,
109
- error: {
110
- code: isTimeout ? 'STATE_BROKER_TIMEOUT' : 'STATE_BROKER_ERROR',
111
- message: isTimeout
112
- ? 'State Broker daemon did not respond in time'
113
- : error instanceof Error ? error.message : String(error),
114
- details: {
115
- isTimeout,
116
- },
117
- },
118
- });
119
- }
120
- });
121
- }
122
-
123
- // GET /api/v1/observability/devkit
124
- // Returns DevKit health check results (monorepo health score, issues, etc.)
125
- // CACHED for 10 minutes via platform.cache to prevent excessive process spawning
126
- for (const path of devkitPaths) {
127
- fastify.get(path, async (_request, reply) => {
128
- const now = Date.now();
129
-
130
- // Try to get from platform.cache first
131
- if (platform?.cache) {
132
- try {
133
- const cached = await platform.cache.get<{
134
- data: unknown;
135
- timestamp: number;
136
- expiresAt: number;
137
- }>(DEVKIT_CACHE_KEY);
138
-
139
- if (cached && now < cached.expiresAt) {
140
- const remainingTtl = Math.round((cached.expiresAt - now) / 1000);
141
- fastify.log.debug({ remainingTtl }, 'Returning cached DevKit health from platform.cache');
142
-
143
- return {
144
- ok: true,
145
- data: cached.data,
146
- meta: {
147
- source: 'devkit-cli',
148
- repoRoot,
149
- cached: true,
150
- cachedAt: cached.timestamp,
151
- expiresAt: cached.expiresAt,
152
- ttlSeconds: remainingTtl,
153
- },
154
- };
155
- }
156
- } catch (cacheError) {
157
- fastify.log.warn({ err: cacheError }, 'Failed to read from platform.cache, proceeding without cache');
158
- }
159
- }
160
-
161
- try {
162
- fastify.log.debug({ cwd: repoRoot }, 'Executing DevKit health check (cache miss)');
163
-
164
- const { stdout, stderr } = await execAsync('npx kb-devkit-health --json', {
165
- cwd: repoRoot,
166
- timeout: 30000, // 30s timeout
167
- env: {
168
- ...process.env,
169
- // Ensure DevKit runs in non-interactive mode
170
- CI: 'true',
171
- },
172
- });
173
-
174
- if (stderr) {
175
- fastify.log.warn({ stderr }, 'DevKit health check produced warnings');
176
- }
177
-
178
- const health = JSON.parse(stdout);
179
- const expiresAt = now + DEVKIT_CACHE_TTL_MS;
180
-
181
- // Cache the result for 10 minutes via platform.cache
182
- if (platform?.cache) {
183
- try {
184
- await platform.cache.set(DEVKIT_CACHE_KEY, {
185
- data: health,
186
- timestamp: now,
187
- expiresAt,
188
- }, DEVKIT_CACHE_TTL_MS);
189
- fastify.log.debug({
190
- healthScore: health.healthScore,
191
- grade: health.grade,
192
- cachedUntil: new Date(expiresAt).toISOString(),
193
- }, 'DevKit health check completed and cached in platform.cache');
194
- } catch (cacheError) {
195
- fastify.log.warn({ err: cacheError }, 'Failed to write to platform.cache');
196
- }
197
- }
198
-
199
- return {
200
- ok: true,
201
- data: health,
202
- meta: {
203
- source: 'devkit-cli',
204
- repoRoot,
205
- command: 'npx kb-devkit-health --json',
206
- cached: false,
207
- cachedAt: now,
208
- expiresAt,
209
- ttlSeconds: DEVKIT_CACHE_TTL_MS / 1000,
210
- },
211
- };
212
- } catch (error) {
213
- // Try to extract partial data from error's stdout
214
- let partialData: unknown = null;
215
- if (error && typeof error === 'object' && 'stdout' in error) {
216
- try {
217
- partialData = JSON.parse((error as { stdout: string }).stdout);
218
- } catch {
219
- partialData = null;
220
- }
221
- }
222
-
223
- platform?.logger.error('Failed to execute DevKit health check', error instanceof Error ? error : new Error(String(error)));
224
-
225
- return reply.code(500).send({
226
- ok: false,
227
- error: {
228
- code: 'DEVKIT_ERROR',
229
- message: error instanceof Error ? error.message : 'Failed to execute DevKit health check',
230
- details: {
231
- partialData,
232
- },
233
- },
234
- });
235
- }
236
- });
237
- }
238
-
239
- // GET /api/v1/observability/system-metrics
240
- // Returns system resource metrics (CPU, memory, uptime, load) from all REST API instances
241
- for (const path of systemMetricsPaths) {
242
- fastify.get(path, async (_request, reply) => {
243
- try {
244
- if (!platform?.cache) {
245
- return reply.code(503).send({
246
- ok: false,
247
- error: {
248
- code: 'PLATFORM_CACHE_UNAVAILABLE',
249
- message: 'Platform cache is not available',
250
- },
251
- });
252
- }
253
-
254
- fastify.log.debug('Fetching system metrics from all instances');
255
-
256
- // Get all system-metrics:* keys from platform.cache
257
- const allMetrics: SystemMetrics[] = [];
258
-
259
- // Try to scan for all system-metrics keys
260
- // Note: platform.cache may not have scan(), so we'll handle both cases
261
- try {
262
- // Try to use scan if available (Redis adapter)
263
- if ('scan' in platform.cache && typeof (platform.cache as any).scan === 'function') {
264
- const keys = await (platform.cache as any).scan('system-metrics:*');
265
-
266
- for (const key of keys) {
267
- const metrics = await platform.cache.get<SystemMetrics>(key);
268
- if (metrics) {
269
- allMetrics.push(metrics);
270
- }
271
- }
272
- } else {
273
- // Fallback: InMemory adapter doesn't have scan, but we can try common instance IDs
274
- // This is a limitation - we won't see all instances unless we track them separately
275
- // For now, we'll just try to get the current instance's metrics
276
- const currentInstanceId = hostname();
277
- const metrics = await platform.cache.get<SystemMetrics>(`system-metrics:${currentInstanceId}`);
278
-
279
- if (metrics) {
280
- allMetrics.push(metrics);
281
- }
282
-
283
- fastify.log.debug('Platform cache does not support scan(), showing current instance only');
284
- }
285
- } catch (scanError) {
286
- fastify.log.warn({ err: scanError }, 'Failed to scan platform.cache for system metrics');
287
-
288
- // Fallback: try current instance
289
- const currentInstanceId = hostname();
290
- const metrics = await platform.cache.get<SystemMetrics>(`system-metrics:${currentInstanceId}`);
291
-
292
- if (metrics) {
293
- allMetrics.push(metrics);
294
- }
295
- }
296
-
297
- if (allMetrics.length === 0) {
298
- return reply.code(404).send({
299
- ok: false,
300
- error: {
301
- code: 'NO_METRICS_FOUND',
302
- message: 'No system metrics found. Metrics collector may not be running.',
303
- },
304
- });
305
- }
306
-
307
- // Sort by timestamp (newest first)
308
- allMetrics.sort((a, b) => b.timestamp - a.timestamp);
309
-
310
- // Calculate aggregated metrics
311
- const now = Date.now();
312
- const avgCpu = allMetrics.reduce((sum, m) => sum + m.cpu.percentage, 0) / allMetrics.length;
313
- const avgMemory = allMetrics.reduce((sum, m) => sum + m.memory.rssPercentage, 0) / allMetrics.length;
314
- const avgHeap = allMetrics.reduce((sum, m) => sum + m.memory.heapPercentage, 0) / allMetrics.length;
315
-
316
- // Categorize instances by health status based on age
317
- const activeInstances = allMetrics.filter(m => (now - m.timestamp) < 30000); // Active if updated in last 30s
318
- const staleInstances = allMetrics.filter(m => (now - m.timestamp) >= 30000 && (now - m.timestamp) < 60000); // Stale if 30-60s old
319
- const deadInstances = allMetrics.filter(m => (now - m.timestamp) >= 60000); // Dead if >60s old
320
-
321
- fastify.log.debug({
322
- totalInstances: allMetrics.length,
323
- activeInstances: activeInstances.length,
324
- staleInstances: staleInstances.length,
325
- deadInstances: deadInstances.length,
326
- }, 'System metrics retrieved');
327
-
328
- return {
329
- ok: true,
330
- data: {
331
- instances: allMetrics,
332
- summary: {
333
- totalInstances: allMetrics.length,
334
- activeInstances: activeInstances.length,
335
- staleInstances: staleInstances.length,
336
- deadInstances: deadInstances.length,
337
- avgCpu: parseFloat(avgCpu.toFixed(2)),
338
- avgMemory: parseFloat(avgMemory.toFixed(2)),
339
- avgHeap: parseFloat(avgHeap.toFixed(2)),
340
- },
341
- },
342
- meta: {
343
- source: 'platform-cache',
344
- timestamp: now,
345
- },
346
- };
347
- } catch (error) {
348
- platform?.logger.error('Failed to fetch system metrics', error instanceof Error ? error : new Error(String(error)));
349
-
350
- return reply.code(500).send({
351
- ok: false,
352
- error: {
353
- code: 'SYSTEM_METRICS_ERROR',
354
- message: error instanceof Error ? error.message : 'Failed to fetch system metrics',
355
- },
356
- });
357
- }
358
- });
359
- }
360
-
361
- // GET /api/v1/observability/metrics/history
362
- // Returns historical time-series metrics data
363
- for (const path of metricsHistoryPaths) {
364
- fastify.get(path, {
365
- schema: {
366
- querystring: {
367
- type: 'object',
368
- properties: {
369
- metric: {
370
- type: 'string',
371
- enum: ['requests', 'errors', 'latency', 'uptime'],
372
- },
373
- range: {
374
- type: 'string',
375
- enum: ['1m', '5m', '10m', '30m', '1h'],
376
- },
377
- interval: {
378
- type: 'string',
379
- enum: ['5s', '1m', '5m'],
380
- },
381
- },
382
- required: ['metric', 'range'],
383
- },
384
- response: {
385
- 200: {
386
- type: 'object',
387
- properties: {
388
- ok: { type: 'boolean' },
389
- data: {
390
- type: 'array',
391
- items: {
392
- type: 'object',
393
- properties: {
394
- timestamp: { type: 'number' },
395
- value: { type: 'number' },
396
- },
397
- },
398
- },
399
- meta: {
400
- type: 'object',
401
- properties: {
402
- source: { type: 'string' },
403
- metric: { type: 'string' },
404
- range: { type: 'string' },
405
- interval: { type: 'string' },
406
- points: { type: 'number' },
407
- },
408
- },
409
- },
410
- },
411
- },
412
- },
413
- }, async (request, reply: any) => {
414
- if (!historicalMetrics) {
415
- return reply.code(503).send({
416
- ok: false,
417
- error: {
418
- code: 'HISTORICAL_METRICS_UNAVAILABLE',
419
- message: 'Historical metrics collector is not initialized',
420
- },
421
- });
422
- }
423
-
424
- const query = request.query as {
425
- metric: 'requests' | 'errors' | 'latency' | 'uptime';
426
- range: '1m' | '5m' | '10m' | '30m' | '1h';
427
- interval?: '5s' | '1m' | '5m';
428
- };
429
-
430
- try {
431
- fastify.log.debug({ query }, 'Querying historical metrics');
432
-
433
- const data = await historicalMetrics.queryHistory({
434
- metric: query.metric,
435
- range: query.range,
436
- interval: query.interval,
437
- });
438
-
439
- fastify.log.debug({
440
- metric: query.metric,
441
- range: query.range,
442
- points: data.length,
443
- }, 'Historical metrics retrieved');
444
-
445
- return {
446
- ok: true,
447
- data,
448
- meta: {
449
- source: 'historical-metrics-collector',
450
- metric: query.metric,
451
- range: query.range,
452
- interval: query.interval ?? '5s',
453
- points: data.length,
454
- },
455
- };
456
- } catch (error) {
457
- platform?.logger.error('Failed to query historical metrics', error instanceof Error ? error : new Error(String(error)), { query });
458
-
459
- return reply.code(500).send({
460
- ok: false,
461
- error: {
462
- code: 'HISTORICAL_METRICS_ERROR',
463
- message: error instanceof Error ? error.message : 'Failed to query historical metrics',
464
- },
465
- });
466
- }
467
- });
468
- }
469
-
470
- // GET /api/v1/observability/metrics/heatmap
471
- // Returns heatmap aggregated data (7 days × 24 hours)
472
- for (const path of metricsHeatmapPaths) {
473
- fastify.get(path, {
474
- schema: {
475
- querystring: {
476
- type: 'object',
477
- properties: {
478
- metric: {
479
- type: 'string',
480
- enum: ['latency', 'errors', 'requests'],
481
- },
482
- days: {
483
- type: 'integer',
484
- enum: [7, 14, 30],
485
- },
486
- },
487
- required: ['metric'],
488
- },
489
- response: {
490
- 200: {
491
- type: 'object',
492
- properties: {
493
- ok: { type: 'boolean' },
494
- data: {
495
- type: 'array',
496
- items: {
497
- type: 'object',
498
- properties: {
499
- day: { type: 'string' },
500
- hour: { type: 'number' },
501
- value: { type: 'number' },
502
- },
503
- },
504
- },
505
- meta: {
506
- type: 'object',
507
- properties: {
508
- source: { type: 'string' },
509
- metric: { type: 'string' },
510
- days: { type: 'number' },
511
- cells: { type: 'number' },
512
- },
513
- },
514
- },
515
- },
516
- },
517
- },
518
- }, async (request, reply: any) => {
519
- if (!historicalMetrics) {
520
- return reply.code(503).send({
521
- ok: false,
522
- error: {
523
- code: 'HISTORICAL_METRICS_UNAVAILABLE',
524
- message: 'Historical metrics collector is not initialized',
525
- },
526
- });
527
- }
528
-
529
- const query = request.query as {
530
- metric: 'latency' | 'errors' | 'requests';
531
- days?: 7 | 14 | 30;
532
- };
533
-
534
- try {
535
- fastify.log.debug({ query }, 'Querying heatmap data');
536
-
537
- const data = await historicalMetrics.queryHeatmap({
538
- metric: query.metric,
539
- days: query.days ?? 7,
540
- });
541
-
542
- fastify.log.debug({
543
- metric: query.metric,
544
- cells: data.length,
545
- }, 'Heatmap data retrieved');
546
-
547
- return {
548
- ok: true,
549
- data,
550
- meta: {
551
- source: 'historical-metrics-collector',
552
- metric: query.metric,
553
- days: query.days ?? 7,
554
- cells: data.length,
555
- },
556
- };
557
- } catch (error) {
558
- platform?.logger.error('Failed to query heatmap data', error instanceof Error ? error : new Error(String(error)), { query });
559
-
560
- return reply.code(500).send({
561
- ok: false,
562
- error: {
563
- code: 'HEATMAP_ERROR',
564
- message: error instanceof Error ? error.message : 'Failed to query heatmap data',
565
- },
566
- });
567
- }
568
- });
569
- }
570
-
571
- // GET /api/v1/observability/incidents
572
- // List incidents with filters (active by default, use includeResolved=true for all)
573
- for (const path of incidentsListPaths) {
574
- fastify.get(path, {
575
- schema: {
576
- querystring: {
577
- type: 'object',
578
- properties: {
579
- limit: { type: 'integer', default: 50 },
580
- severity: {
581
- type: 'string',
582
- enum: ['critical', 'warning', 'info'],
583
- },
584
- type: {
585
- type: 'string',
586
- enum: ['error_rate', 'latency_spike', 'plugin_failure', 'adapter_failure', 'system_health', 'custom'],
587
- },
588
- from: { type: 'integer' },
589
- to: { type: 'integer' },
590
- includeResolved: { type: 'boolean', default: false },
591
- },
592
- },
593
- },
594
- }, async (request, reply) => {
595
- if (!incidentStorage) {
596
- return reply.code(503).send({
597
- ok: false,
598
- error: {
599
- code: 'INCIDENTS_NOT_CONFIGURED',
600
- message: 'Incident storage is not configured',
601
- },
602
- });
603
- }
604
-
605
- try {
606
- const query = request.query as any;
607
- const incidents = await incidentStorage.queryIncidents({
608
- limit: query.limit ?? 50,
609
- severity: query.severity,
610
- type: query.type,
611
- from: query.from,
612
- to: query.to,
613
- includeResolved: query.includeResolved ?? false,
614
- });
615
-
616
- // Get stats for summary
617
- const stats = await incidentStorage.getStats();
618
-
619
- return {
620
- ok: true,
621
- data: {
622
- incidents,
623
- summary: {
624
- total: stats.total,
625
- unresolved: stats.unresolved,
626
- bySeverity: stats.bySeverity,
627
- showing: incidents.length,
628
- },
629
- },
630
- };
631
- } catch (error) {
632
- fastify.log.error({ err: error }, 'Failed to list incidents');
633
-
634
- return reply.code(500).send({
635
- ok: false,
636
- error: {
637
- code: 'INCIDENTS_LIST_ERROR',
638
- message: error instanceof Error ? error.message : 'Failed to list incidents',
639
- },
640
- });
641
- }
642
- });
643
- }
644
-
645
- // GET /api/v1/observability/incidents/:id
646
- // Get incident details by ID
647
- for (const path of incidentsDetailPaths) {
648
- fastify.get(path, {
649
- schema: {
650
- params: {
651
- type: 'object',
652
- properties: {
653
- id: { type: 'string' },
654
- },
655
- required: ['id'],
656
- },
657
- },
658
- }, async (request, reply) => {
659
- if (!incidentStorage) {
660
- return reply.code(503).send({
661
- ok: false,
662
- error: {
663
- code: 'INCIDENTS_NOT_CONFIGURED',
664
- message: 'Incident storage is not configured',
665
- },
666
- });
667
- }
668
-
669
- const { id } = request.params as { id: string };
670
-
671
- try {
672
- const incident = await incidentStorage.getIncident(id);
673
-
674
- if (!incident) {
675
- return reply.code(404).send({
676
- ok: false,
677
- error: {
678
- code: 'INCIDENT_NOT_FOUND',
679
- message: `Incident ${id} not found`,
680
- },
681
- });
682
- }
683
-
684
- // Track analytics
685
- if (platform?.analytics) {
686
- platform.analytics.track('incident.viewed', {
687
- incidentId: id,
688
- type: incident.type,
689
- severity: incident.severity,
690
- isResolved: !!incident.resolvedAt,
691
- hasAIAnalysis: !!incident.aiAnalysis,
692
- }).catch(() => {
693
- // Silently ignore analytics errors
694
- });
695
- }
696
-
697
- return {
698
- ok: true,
699
- data: incident,
700
- };
701
- } catch (error) {
702
- fastify.log.error({ err: error }, 'Failed to get incident');
703
-
704
- return reply.code(500).send({
705
- ok: false,
706
- error: {
707
- code: 'INCIDENT_GET_ERROR',
708
- message: error instanceof Error ? error.message : 'Failed to get incident',
709
- },
710
- });
711
- }
712
- });
713
- }
714
-
715
- // POST /api/v1/observability/incidents
716
- // Create a new incident record
717
- for (const path of incidentsCreatePaths) {
718
- fastify.post(path, {
719
- schema: {
720
- body: {
721
- type: 'object',
722
- properties: {
723
- type: {
724
- type: 'string',
725
- enum: ['error_rate', 'latency_spike', 'plugin_failure', 'adapter_failure', 'system_health', 'custom'],
726
- },
727
- severity: {
728
- type: 'string',
729
- enum: ['critical', 'warning', 'info'],
730
- },
731
- title: { type: 'string' },
732
- details: { type: 'string' },
733
- rootCause: { type: 'string' },
734
- affectedServices: {
735
- type: 'array',
736
- items: { type: 'string' },
737
- },
738
- timestamp: { type: 'number' },
739
- metadata: { type: 'object' },
740
- },
741
- required: ['type', 'severity', 'title', 'details'],
742
- },
743
- },
744
- }, async (request, reply) => {
745
- if (!incidentStorage) {
746
- return reply.code(503).send({
747
- ok: false,
748
- error: {
749
- code: 'INCIDENT_STORAGE_UNAVAILABLE',
750
- message: 'Incident storage is not initialized',
751
- },
752
- });
753
- }
754
-
755
- try {
756
- const payload = request.body as any;
757
- const incident = await incidentStorage.createIncident(payload);
758
-
759
- return {
760
- ok: true,
761
- data: incident,
762
- meta: {
763
- source: 'incident-storage',
764
- },
765
- };
766
- } catch (error) {
767
- platform?.logger.error('Failed to create incident', error instanceof Error ? error : new Error(String(error)));
768
-
769
- return reply.code(500).send({
770
- ok: false,
771
- error: {
772
- code: 'INCIDENT_CREATE_ERROR',
773
- message: error instanceof Error ? error.message : 'Failed to create incident',
774
- },
775
- });
776
- }
777
- });
778
- }
779
-
780
- // GET /api/v1/observability/incidents/history
781
- // Query incident history with filters
782
- for (const path of incidentsHistoryPaths) {
783
- fastify.get(path, {
784
- schema: {
785
- querystring: {
786
- type: 'object',
787
- properties: {
788
- limit: { type: 'integer', minimum: 1, maximum: 500 },
789
- severity: { type: 'string', enum: ['critical', 'warning', 'info'] },
790
- type: {
791
- type: 'string',
792
- enum: ['error_rate', 'latency_spike', 'plugin_failure', 'adapter_failure', 'system_health', 'custom'],
793
- },
794
- from: { type: 'integer' },
795
- to: { type: 'integer' },
796
- includeResolved: { type: 'boolean' },
797
- },
798
- },
799
- },
800
- }, async (request, reply) => {
801
- if (!incidentStorage) {
802
- return reply.code(503).send({
803
- ok: false,
804
- error: {
805
- code: 'INCIDENT_STORAGE_UNAVAILABLE',
806
- message: 'Incident storage is not initialized',
807
- },
808
- });
809
- }
810
-
811
- try {
812
- const query = request.query as any;
813
- const incidents = await incidentStorage.queryIncidents({
814
- limit: query.limit ?? 50,
815
- severity: query.severity,
816
- type: query.type,
817
- from: query.from,
818
- to: query.to,
819
- includeResolved: query.includeResolved ?? false,
820
- });
821
-
822
- return {
823
- ok: true,
824
- data: incidents,
825
- meta: {
826
- source: 'incident-storage',
827
- count: incidents.length,
828
- },
829
- };
830
- } catch (error) {
831
- platform?.logger.error('Failed to query incidents', error instanceof Error ? error : new Error(String(error)));
832
-
833
- return reply.code(500).send({
834
- ok: false,
835
- error: {
836
- code: 'INCIDENT_QUERY_ERROR',
837
- message: error instanceof Error ? error.message : 'Failed to query incidents',
838
- },
839
- });
840
- }
841
- });
842
- }
843
-
844
- // POST /api/v1/observability/incidents/:id/resolve
845
- // Mark an incident as resolved
846
- for (const path of incidentsResolvePaths) {
847
- fastify.post(path, {
848
- schema: {
849
- params: {
850
- type: 'object',
851
- properties: {
852
- id: { type: 'string' },
853
- },
854
- required: ['id'],
855
- },
856
- body: {
857
- type: 'object',
858
- properties: {
859
- resolutionNotes: { type: 'string' },
860
- },
861
- },
862
- },
863
- }, async (request, reply) => {
864
- if (!incidentStorage) {
865
- return reply.code(503).send({
866
- ok: false,
867
- error: {
868
- code: 'INCIDENT_STORAGE_UNAVAILABLE',
869
- message: 'Incident storage is not initialized',
870
- },
871
- });
872
- }
873
-
874
- try {
875
- const { id } = request.params as { id: string };
876
- const body = request.body as { resolutionNotes?: string };
877
-
878
- const incident = await incidentStorage.resolveIncident(id, body.resolutionNotes);
879
-
880
- if (!incident) {
881
- return reply.code(404).send({
882
- ok: false,
883
- error: {
884
- code: 'INCIDENT_NOT_FOUND',
885
- message: `Incident with id ${id} not found`,
886
- },
887
- });
888
- }
889
-
890
- return {
891
- ok: true,
892
- data: incident,
893
- meta: {
894
- source: 'incident-storage',
895
- },
896
- };
897
- } catch (error) {
898
- platform?.logger.error('Failed to resolve incident', error instanceof Error ? error : new Error(String(error)));
899
-
900
- return reply.code(500).send({
901
- ok: false,
902
- error: {
903
- code: 'INCIDENT_RESOLVE_ERROR',
904
- message: error instanceof Error ? error.message : 'Failed to resolve incident',
905
- },
906
- });
907
- }
908
- });
909
- }
910
-
911
- // POST /api/v1/observability/insights/chat
912
- // AI-powered insights chat using LLM with system metrics as context
913
- for (const path of insightsChatPaths) {
914
- fastify.post(path, {
915
- schema: {
916
- body: {
917
- type: 'object',
918
- properties: {
919
- question: { type: 'string', minLength: 1 },
920
- context: {
921
- type: 'object',
922
- properties: {
923
- includeMetrics: { type: 'boolean' },
924
- includeIncidents: { type: 'boolean' },
925
- includeHistory: { type: 'boolean' },
926
- timeRange: { type: 'string', enum: ['1h', '6h', '24h', '7d'] },
927
- plugins: { type: 'array', items: { type: 'string' } },
928
- },
929
- },
930
- },
931
- required: ['question'],
932
- },
933
- },
934
- }, async (request, reply) => {
935
- if (!platform?.llm) {
936
- return reply.code(503).send({
937
- ok: false,
938
- error: {
939
- code: 'LLM_UNAVAILABLE',
940
- message: 'LLM adapter is not configured. AI Insights requires an LLM adapter.',
941
- },
942
- });
943
- }
944
-
945
- try {
946
- const body = request.body as {
947
- question: string;
948
- context?: {
949
- includeMetrics?: boolean;
950
- includeIncidents?: boolean;
951
- includeHistory?: boolean;
952
- timeRange?: '1h' | '6h' | '24h' | '7d';
953
- plugins?: string[];
954
- };
955
- };
956
-
957
- const contextConfig = {
958
- includeMetrics: body.context?.includeMetrics ?? true,
959
- includeIncidents: body.context?.includeIncidents ?? true,
960
- includeHistory: body.context?.includeHistory ?? true,
961
- timeRange: body.context?.timeRange ?? '24h',
962
- plugins: body.context?.plugins ?? [],
963
- };
964
-
965
- // Build context from real data
966
- let contextText = '';
967
-
968
- // Fetch current metrics
969
- if (contextConfig.includeMetrics) {
970
- try {
971
- const metrics = metricsCollector.getMetrics();
972
-
973
- const errorRate = metrics.requests.total
974
- ? (((metrics.requests.clientErrors ?? 0) + (metrics.requests.serverErrors ?? 0)) / metrics.requests.total * 100)
975
- : 0;
976
-
977
- contextText += `\n## Current System Metrics\n`;
978
- contextText += `- Total requests: ${metrics.requests.total}\n`;
979
- contextText += `- Active requests: ${metrics.requests.active}\n`;
980
- contextText += `- Error rate: ${errorRate.toFixed(2)}%\n`;
981
- contextText += `- Average latency: ${metrics.latency.average.toFixed(0)}ms\n`;
982
- contextText += `- Max latency: ${metrics.latency.max.toFixed(0)}ms\n`;
983
-
984
- if (metrics.perPlugin.length > 0) {
985
- contextText += `\n### Per-Plugin Metrics\n`;
986
- const pluginsToShow = contextConfig.plugins.length > 0
987
- ? metrics.perPlugin.filter(plugin => plugin.pluginId && contextConfig.plugins.includes(plugin.pluginId))
988
- : metrics.perPlugin.slice(0, 10);
989
-
990
- for (const plugin of pluginsToShow) {
991
- const totalRequests = plugin.total ?? 0;
992
- const errorCount = Object.entries(plugin.statuses ?? {})
993
- .filter(([statusGroup]) => statusGroup.startsWith('4') || statusGroup.startsWith('5'))
994
- .reduce((sum, [, count]) => sum + count, 0);
995
- const pluginErrorRate = totalRequests > 0
996
- ? (errorCount / totalRequests * 100).toFixed(2)
997
- : '0.00';
998
- const avgLatency = totalRequests > 0 ? plugin.totalDuration / totalRequests : 0;
999
- contextText += `- ${plugin.pluginId ?? 'unknown'}: ${totalRequests} requests, ${pluginErrorRate}% errors, ${avgLatency.toFixed(0)}ms avg latency\n`;
1000
- }
1001
- }
1002
- } catch (metricsError) {
1003
- fastify.log.warn({ err: metricsError }, 'Failed to build metrics context for insights');
1004
- }
1005
- }
1006
-
1007
- // Fetch recent incidents
1008
- if (contextConfig.includeIncidents && incidentStorage) {
1009
- try {
1010
- const incidents = await incidentStorage.queryIncidents({ limit: 10 });
1011
- if (incidents.length > 0) {
1012
- contextText += `\n## Recent Incidents (${incidents.length})\n`;
1013
- for (const incident of incidents) {
1014
- contextText += `- [${incident.severity.toUpperCase()}] ${incident.title}\n`;
1015
- if (incident.details) {
1016
- contextText += ` Details: ${incident.details.slice(0, 100)}${incident.details.length > 100 ? '...' : ''}\n`;
1017
- }
1018
- }
1019
- }
1020
- } catch (incidentError) {
1021
- fastify.log.warn({ err: incidentError }, 'Failed to fetch incidents for insights context');
1022
- }
1023
- }
1024
-
1025
- // Build prompt
1026
- const prompt = `You are an AI assistant analyzing a software platform's observability data.
1027
-
1028
- ${contextText}
1029
-
1030
- User Question: ${body.question}
1031
-
1032
- Provide a clear, actionable response based on the data above. Include:
1033
- 1. Direct answer to the question
1034
- 2. Supporting evidence from the metrics/incidents
1035
- 3. Recommendations if applicable
1036
-
1037
- Be concise but thorough. Use markdown formatting.`;
1038
-
1039
- fastify.log.debug({ question: body.question, contextLength: contextText.length }, 'Calling LLM for insights');
1040
-
1041
- const result = await platform.llm.complete(prompt, {
1042
- systemPrompt: 'You are a DevOps and SRE expert assistant. Analyze system metrics and provide actionable insights. Be concise, technical, and helpful.',
1043
- temperature: 0.7,
1044
- maxTokens: 1000,
1045
- });
1046
-
1047
- const totalTokens = result.usage.promptTokens + result.usage.completionTokens;
1048
-
1049
- fastify.log.debug({ tokensUsed: totalTokens }, 'LLM response received for insights');
1050
-
1051
- // Track analytics
1052
- if (platform.analytics) {
1053
- platform.analytics.track('ai_insights.chat', {
1054
- questionLength: body.question.length,
1055
- contextIncluded: Object.keys(contextConfig).filter(k => (contextConfig as any)[k]),
1056
- timeRange: contextConfig.timeRange,
1057
- pluginsFiltered: contextConfig.plugins.length,
1058
- promptTokens: result.usage.promptTokens,
1059
- completionTokens: result.usage.completionTokens,
1060
- totalTokens,
1061
- model: result.model,
1062
- }).catch(() => {
1063
- // Silently ignore analytics errors
1064
- });
1065
- }
1066
-
1067
- return {
1068
- ok: true,
1069
- data: {
1070
- answer: result.content.trim(),
1071
- context: Object.keys(contextConfig).filter(k => (contextConfig as any)[k]),
1072
- usage: {
1073
- promptTokens: result.usage.promptTokens,
1074
- completionTokens: result.usage.completionTokens,
1075
- totalTokens,
1076
- },
1077
- },
1078
- meta: {
1079
- source: 'llm-insights',
1080
- model: result.model,
1081
- },
1082
- };
1083
- } catch (error) {
1084
- platform?.logger.error('Failed to generate insights', error instanceof Error ? error : new Error(String(error)));
1085
-
1086
- // Track error analytics
1087
- if (platform?.analytics) {
1088
- platform.analytics.track('ai_insights.error', {
1089
- error: error instanceof Error ? error.message : 'Unknown error',
1090
- questionLength: (request.body as any)?.question?.length ?? 0,
1091
- }).catch(() => {
1092
- // Silently ignore analytics errors
1093
- });
1094
- }
1095
-
1096
- return reply.code(500).send({
1097
- ok: false,
1098
- error: {
1099
- code: 'INSIGHTS_ERROR',
1100
- message: error instanceof Error ? error.message : 'Failed to generate insights',
1101
- },
1102
- });
1103
- }
1104
- });
1105
- }
1106
-
1107
- // POST /api/v1/observability/incidents/:id/analyze
1108
- // Analyze incident using AI to identify root causes and recommendations
1109
- for (const path of incidentsAnalyzePaths) {
1110
- fastify.post(path, async (request, reply) => {
1111
- if (!incidentStorage) {
1112
- return reply.code(503).send({
1113
- ok: false,
1114
- error: {
1115
- code: 'INCIDENTS_NOT_CONFIGURED',
1116
- message: 'Incident storage is not configured',
1117
- },
1118
- });
1119
- }
1120
-
1121
- const { id } = request.params as { id: string };
1122
-
1123
- try {
1124
- // Fetch incident
1125
- const incident = await incidentStorage.getIncident(id);
1126
-
1127
- if (!incident) {
1128
- return reply.code(404).send({
1129
- ok: false,
1130
- error: {
1131
- code: 'INCIDENT_NOT_FOUND',
1132
- message: `Incident ${id} not found`,
1133
- },
1134
- });
1135
- }
1136
-
1137
- // Check if already analyzed
1138
- if (incident.aiAnalysis && incident.aiAnalyzedAt) {
1139
- const ageMs = Date.now() - incident.aiAnalyzedAt;
1140
- const ageMinutes = Math.floor(ageMs / 60000);
1141
-
1142
- // Return cached analysis if less than 1 hour old
1143
- if (ageMs < 60 * 60 * 1000) {
1144
- return {
1145
- ok: true,
1146
- data: {
1147
- ...incident.aiAnalysis,
1148
- cached: true,
1149
- analyzedAt: incident.aiAnalyzedAt,
1150
- ageMinutes,
1151
- },
1152
- };
1153
- }
1154
- }
1155
-
1156
- // Analyze incident with LLM
1157
- const analyzer = new IncidentAnalyzer(
1158
- {
1159
- debug: process.env.NODE_ENV !== 'production',
1160
- },
1161
- fastify.log as any
1162
- );
1163
-
1164
- const analysis = await analyzer.analyze(incident);
1165
-
1166
- // Store analysis in incident
1167
- await incidentStorage.updateAIAnalysis(id, analysis);
1168
-
1169
- // Track analytics
1170
- if (platform?.analytics) {
1171
- platform.analytics.track('incident.analyzed', {
1172
- incidentId: id,
1173
- type: incident.type,
1174
- severity: incident.severity,
1175
- rootCausesCount: analysis.rootCauses.length,
1176
- recommendationsCount: analysis.recommendations.length,
1177
- }).catch(() => {
1178
- // Silently ignore analytics errors
1179
- });
1180
- }
1181
-
1182
- return {
1183
- ok: true,
1184
- data: {
1185
- ...analysis,
1186
- cached: false,
1187
- },
1188
- };
1189
- } catch (error) {
1190
- fastify.log.error({ err: error }, 'Failed to analyze incident');
1191
-
1192
- // Track error in analytics
1193
- if (platform?.analytics) {
1194
- platform.analytics.track('incident.analysis_error', {
1195
- incidentId: id,
1196
- error: error instanceof Error ? error.message : 'Unknown error',
1197
- }).catch(() => {
1198
- // Silently ignore analytics errors
1199
- });
1200
- }
1201
-
1202
- return reply.code(500).send({
1203
- ok: false,
1204
- error: {
1205
- code: 'ANALYSIS_ERROR',
1206
- message: error instanceof Error ? error.message : 'Failed to analyze incident',
1207
- },
1208
- });
1209
- }
1210
- });
1211
- }
1212
-
1213
- // ========================================
1214
- // TEST ENDPOINTS (for incident testing)
1215
- // ========================================
1216
-
1217
- // Test endpoint: Create incident manually
1218
- const testCreateIncidentPaths = resolvePaths(basePath, '/test/create-incident');
1219
- for (const path of testCreateIncidentPaths) {
1220
- fastify.post(path, async (request, reply) => {
1221
- if (!incidentStorage) {
1222
- return reply.code(500).send({
1223
- ok: false,
1224
- error: { code: 'INCIDENT_STORAGE_NOT_INITIALIZED', message: 'Incident storage not initialized' },
1225
- });
1226
- }
1227
-
1228
- try {
1229
- const {
1230
- type = 'custom',
1231
- severity = 'warning',
1232
- title = 'Test Incident',
1233
- details,
1234
- relatedData,
1235
- } = request.body as any;
1236
-
1237
- const incident = await incidentStorage.createIncident({
1238
- type,
1239
- severity,
1240
- title,
1241
- details: details || 'This is a test incident created manually for testing purposes.',
1242
- timestamp: Date.now(),
1243
- metadata: {
1244
- testMode: true,
1245
- createdVia: 'test-endpoint',
1246
- },
1247
- relatedData: relatedData || {
1248
- timeline: [
1249
- {
1250
- timestamp: Date.now(),
1251
- event: 'Test incident created via /test/create-incident',
1252
- source: 'manual' as const,
1253
- },
1254
- ],
1255
- },
1256
- });
1257
-
1258
- fastify.log.info({ id: incident.id }, 'Test incident created');
1259
-
1260
- return { ok: true, data: incident };
1261
- } catch (error) {
1262
- fastify.log.error({ err: error }, 'Failed to create test incident');
1263
- return reply.code(500).send({
1264
- ok: false,
1265
- error: {
1266
- code: 'TEST_INCIDENT_CREATION_FAILED',
1267
- message: error instanceof Error ? error.message : 'Failed to create test incident',
1268
- },
1269
- });
1270
- }
1271
- });
1272
- }
1273
-
1274
- // Test endpoint: Trigger errors (auto-creates error_rate incident)
1275
- const testTriggerErrorsPaths = resolvePaths(basePath, '/test/trigger-errors');
1276
- for (const path of testTriggerErrorsPaths) {
1277
- fastify.get(path, async (request, reply) => {
1278
- const { count = 10 } = request.query as any;
1279
- const errorCount = Math.min(Math.max(1, parseInt(count, 10) || 10), 100); // 1-100 errors
1280
-
1281
- fastify.log.info(`Triggering ${errorCount} test errors`);
1282
-
1283
- // Generate errors
1284
- for (let i = 0; i < errorCount; i++) {
1285
- // Log errors to platform
1286
- platform?.logger.error(`Test error ${i + 1}/${errorCount}`, undefined, {
1287
- testMode: true,
1288
- errorNumber: i + 1,
1289
- totalErrors: errorCount,
1290
- });
1291
-
1292
- // Make the request fail to increase error rate
1293
- if (i === errorCount - 1) {
1294
- // Last error - return error response
1295
- return reply.code(500).send({
1296
- ok: false,
1297
- error: {
1298
- code: 'TEST_ERROR_TRIGGERED',
1299
- message: `Generated ${errorCount} test errors. Check /observability/incidents in ~30 seconds for auto-created incident.`,
1300
- },
1301
- });
1302
- }
1303
- }
1304
-
1305
- return {
1306
- ok: true,
1307
- message: `Triggered ${errorCount} test errors. Incident should be auto-created in next detection cycle (~30s).`,
1308
- };
1309
- });
1310
- }
1311
-
1312
- // Test endpoint: Simulate high latency (auto-creates latency_spike incident)
1313
- const testSimulateLatencyPaths = resolvePaths(basePath, '/test/simulate-latency');
1314
- for (const path of testSimulateLatencyPaths) {
1315
- fastify.get(path, async (request, reply) => {
1316
- const { delay = 2000 } = request.query as any;
1317
- const delayMs = Math.min(Math.max(100, parseInt(delay, 10) || 2000), 10000); // 100ms-10s
1318
-
1319
- fastify.log.info(`Simulating ${delayMs}ms latency`);
1320
-
1321
- // Simulate slow response
1322
- await new Promise(resolve => {
1323
- setTimeout(resolve, delayMs);
1324
- });
1325
-
1326
- return {
1327
- ok: true,
1328
- message: `Simulated ${delayMs}ms latency. Call this endpoint multiple times to trigger latency_spike incident.`,
1329
- actualDelay: delayMs,
1330
- };
1331
- });
1332
- }
1333
-
1334
- // Test endpoint: Bulk error generator (guaranteed to trigger incident)
1335
- const testBulkErrorsPaths = resolvePaths(basePath, '/test/bulk-errors');
1336
- for (const path of testBulkErrorsPaths) {
1337
- fastify.post(path, async (request, reply) => {
1338
- const { successCount = 10, errorCount = 50 } = request.body as any;
1339
-
1340
- fastify.log.info({ successCount, errorCount }, 'Generating bulk requests for incident testing');
1341
-
1342
- // Generate successful requests
1343
- for (let i = 0; i < successCount; i++) {
1344
- platform?.logger.info(`Bulk test - success ${i + 1}/${successCount}`);
1345
- }
1346
-
1347
- // Generate error requests with varied endpoints
1348
- const testEndpoints = [
1349
- 'POST /api/v1/test/endpoint-a',
1350
- 'GET /api/v1/test/endpoint-b',
1351
- 'PUT /api/v1/test/endpoint-c',
1352
- 'DELETE /api/v1/test/endpoint-d',
1353
- 'POST /api/v1/test/endpoint-e',
1354
- ];
1355
-
1356
- for (let i = 0; i < errorCount; i++) {
1357
- const endpoint = testEndpoints[i % testEndpoints.length];
1358
- const errorTypes = [
1359
- 'Connection timeout',
1360
- 'Validation failed',
1361
- 'Database query error',
1362
- 'Authentication failed',
1363
- 'Rate limit exceeded',
1364
- ];
1365
- const errorType = errorTypes[i % errorTypes.length];
1366
-
1367
- platform?.logger.error(`${errorType}: ${endpoint}`, undefined, {
1368
- testMode: true,
1369
- bulkTest: true,
1370
- errorIndex: i,
1371
- endpoint,
1372
- });
1373
- }
1374
-
1375
- const totalRequests = successCount + errorCount;
1376
- const errorRate = (errorCount / totalRequests) * 100;
1377
-
1378
- return {
1379
- ok: true,
1380
- message: `Generated ${totalRequests} requests (${successCount} success, ${errorCount} errors)`,
1381
- errorRate: `${errorRate.toFixed(1)}%`,
1382
- note: 'Incident should be auto-created in next detection cycle (~30s)',
1383
- };
1384
- });
1385
- }
1386
-
1387
- fastify.log.info('Observability routes registered (including test endpoints)');
1388
- }