@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.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8151 -0
- package/dist/index.js.map +1 -0
- package/package.json +23 -20
- package/.kb/agents/sessions/session-1771531145393-ws3uh6q/turns.json +0 -42
- package/.kb/agents/sessions/session-1771548930486-ui6rtvm/turns.json +0 -33
- package/.kb/agents/sessions/session-1771549691485-nibqroq/turns.json +0 -133
- package/.kb/agents/sessions/session-1771550943182-dkwqdjc/turns.json +0 -130
- package/.kb/agents/sessions/session-1771551564182-cr4bwzn/turns.json +0 -349
- package/.kb/agents/sessions/session-1771552401370-g4ztwd1/turns.json +0 -206
- package/.kb/commit/current/plan.json +0 -248
- package/.kb/commit/current/status.json +0 -472
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/health.d.ts +0 -10
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/health.js +0 -215
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/health.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/list.d.ts +0 -10
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/list.js +0 -271
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/list.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/logs.d.ts +0 -10
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/logs.js +0 -230
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/logs.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/metrics.d.ts +0 -10
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/metrics.js +0 -228
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/metrics.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/run.d.ts +0 -11
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/run.js +0 -281
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/run.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/status.d.ts +0 -10
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/status.js +0 -266
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/status.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/workflow-run.d.ts +0 -11
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/workflow-run.js +0 -284
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/workflow-run.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/flags-WLdpSZl4.d.ts +0 -77
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/index.d.ts +0 -2
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/index.js +0 -441
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/index.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/manifest.d.ts +0 -196
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/manifest.js +0 -441
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/manifest.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/cron-list-handler.d.ts +0 -19
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/cron-list-handler.js +0 -48
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/cron-list-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/index.d.ts +0 -6
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/index.js +0 -170
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/index.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-cancel-handler.d.ts +0 -12
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-cancel-handler.js +0 -52
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-cancel-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-detail-handler.d.ts +0 -22
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-detail-handler.js +0 -52
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-detail-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-logs-handler.d.ts +0 -13
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-logs-handler.js +0 -52
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-logs-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-steps-handler.d.ts +0 -9
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-steps-handler.js +0 -43
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-steps-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/jobs-list-handler.d.ts +0 -26
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/jobs-list-handler.js +0 -63
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/jobs-list-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/pending-approvals-handler.d.ts +0 -9
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/pending-approvals-handler.js +0 -46
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/pending-approvals-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/resolve-approval-handler.d.ts +0 -16
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/resolve-approval-handler.js +0 -58
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/resolve-approval-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/run-detail-handler.d.ts +0 -9
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/run-detail-handler.js +0 -46
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/run-detail-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/runs-list-handler.d.ts +0 -11
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/runs-list-handler.js +0 -50
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/runs-list-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/stats-handler.d.ts +0 -7
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/stats-handler.js +0 -39
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/stats-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-detail-handler.d.ts +0 -10
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-detail-handler.js +0 -46
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-detail-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-cancel-handler.d.ts +0 -13
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-cancel-handler.js +0 -47
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-cancel-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-handler.d.ts +0 -14
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-handler.js +0 -53
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-runs-handler.d.ts +0 -13
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-runs-handler.js +0 -54
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-runs-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflows-list-handler.d.ts +0 -12
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflows-list-handler.js +0 -51
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflows-list-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/logs-channel.d.ts +0 -17
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/logs-channel.js +0 -133
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/logs-channel.js.map +0 -1
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/progress-channel.d.ts +0 -11
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/progress-channel.js +0 -46
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/progress-channel.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/health.d.ts +0 -10
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/health.js +0 -215
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/health.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/list.d.ts +0 -10
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/list.js +0 -271
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/list.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/logs.d.ts +0 -10
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/logs.js +0 -230
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/logs.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/metrics.d.ts +0 -10
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/metrics.js +0 -228
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/metrics.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/run.d.ts +0 -11
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/run.js +0 -281
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/run.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/status.d.ts +0 -10
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/status.js +0 -266
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/status.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/workflow-run.d.ts +0 -11
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/workflow-run.js +0 -284
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/workflow-run.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/flags-WLdpSZl4.d.ts +0 -77
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/index.d.ts +0 -2
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/index.js +0 -441
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/index.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/manifest.d.ts +0 -196
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/manifest.js +0 -441
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/manifest.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/cron-list-handler.d.ts +0 -19
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/cron-list-handler.js +0 -48
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/cron-list-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/index.d.ts +0 -6
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/index.js +0 -170
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/index.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-cancel-handler.d.ts +0 -12
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-cancel-handler.js +0 -52
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-cancel-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-detail-handler.d.ts +0 -22
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-detail-handler.js +0 -52
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-detail-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-logs-handler.d.ts +0 -13
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-logs-handler.js +0 -52
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-logs-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-steps-handler.d.ts +0 -9
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-steps-handler.js +0 -43
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-steps-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/jobs-list-handler.d.ts +0 -26
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/jobs-list-handler.js +0 -63
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/jobs-list-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/pending-approvals-handler.d.ts +0 -9
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/pending-approvals-handler.js +0 -46
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/pending-approvals-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/resolve-approval-handler.d.ts +0 -16
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/resolve-approval-handler.js +0 -58
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/resolve-approval-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/run-detail-handler.d.ts +0 -9
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/run-detail-handler.js +0 -46
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/run-detail-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/runs-list-handler.d.ts +0 -11
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/runs-list-handler.js +0 -50
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/runs-list-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/stats-handler.d.ts +0 -7
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/stats-handler.js +0 -39
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/stats-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-detail-handler.d.ts +0 -10
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-detail-handler.js +0 -46
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-detail-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-cancel-handler.d.ts +0 -13
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-cancel-handler.js +0 -47
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-cancel-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-handler.d.ts +0 -14
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-handler.js +0 -53
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-runs-handler.d.ts +0 -13
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-runs-handler.js +0 -54
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-runs-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflows-list-handler.d.ts +0 -12
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflows-list-handler.js +0 -51
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflows-list-handler.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/logs-channel.d.ts +0 -17
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/logs-channel.js +0 -133
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/logs-channel.js.map +0 -1
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/progress-channel.d.ts +0 -11
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/progress-channel.js +0 -46
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/progress-channel.js.map +0 -1
- package/eslint.config.js +0 -23
- package/src/.kb/agents/sessions/session-1773470583796-ximqnfk/kpi-baseline.json +0 -17
- package/src/.kb/memory/sessions/session-1773470583796-ximqnfk/fact-sheet.json +0 -1
- package/src/bootstrap.ts +0 -233
- package/src/diagnostics/header-debug.ts +0 -37
- package/src/events/hub.ts +0 -60
- package/src/events/index.ts +0 -1
- package/src/index.ts +0 -13
- package/src/manifest.ts +0 -21
- package/src/middleware/__tests__/cache.spec.ts +0 -184
- package/src/middleware/__tests__/envelope.spec.ts +0 -268
- package/src/middleware/__tests__/metrics.redis.spec.ts +0 -64
- package/src/middleware/__tests__/rate-limit.spec.ts +0 -283
- package/src/middleware/__tests__/request-id.spec.ts +0 -160
- package/src/middleware/cache.ts +0 -67
- package/src/middleware/envelope.ts +0 -136
- package/src/middleware/error-guard.ts +0 -69
- package/src/middleware/index.ts +0 -37
- package/src/middleware/metrics.ts +0 -810
- package/src/middleware/mock-mode.ts +0 -44
- package/src/middleware/prom-metrics.ts +0 -284
- package/src/middleware/rate-limit-routes.ts +0 -73
- package/src/middleware/rate-limit.ts +0 -72
- package/src/middleware/request-id.ts +0 -41
- package/src/middleware/request-timeout.ts +0 -69
- package/src/middleware/security-headers.ts +0 -24
- package/src/middleware/startup-guard.ts +0 -107
- package/src/observability/service-contract.ts +0 -192
- package/src/plugins/index.ts +0 -118
- package/src/routes/__tests__/adapters-date-helpers.spec.ts +0 -332
- package/src/routes/__tests__/adapters.spec.ts +0 -680
- package/src/routes/__tests__/cache.spec.ts +0 -264
- package/src/routes/__tests__/events.spec.ts +0 -434
- package/src/routes/__tests__/health.spec.ts +0 -414
- package/src/routes/__tests__/logs.spec.ts +0 -494
- package/src/routes/__tests__/observability.spec.ts +0 -392
- package/src/routes/__tests__/plugins.spec.ts +0 -931
- package/src/routes/__tests__/workflow-management.spec.ts +0 -698
- package/src/routes/__tests__/workflows.spec.ts +0 -204
- package/src/routes/adapter-call.ts +0 -220
- package/src/routes/adapters.ts +0 -1206
- package/src/routes/analytics.ts +0 -328
- package/src/routes/cache.ts +0 -104
- package/src/routes/debug-routes.ts +0 -95
- package/src/routes/events.ts +0 -115
- package/src/routes/health.ts +0 -348
- package/src/routes/hello.ts +0 -47
- package/src/routes/index.ts +0 -395
- package/src/routes/jobs.ts +0 -253
- package/src/routes/logs.ts +0 -718
- package/src/routes/metrics.ts +0 -120
- package/src/routes/observability.ts +0 -1388
- package/src/routes/openapi.ts +0 -101
- package/src/routes/platform.ts +0 -143
- package/src/routes/plugins.ts +0 -998
- package/src/routes/readiness.ts +0 -83
- package/src/routes/workflow-management.ts +0 -469
- package/src/routes/workflows.ts +0 -256
- package/src/server.ts +0 -203
- package/src/services/historical-metrics.ts +0 -496
- package/src/services/incident-analyzer.ts +0 -289
- package/src/services/incident-detector.ts +0 -691
- package/src/services/incident-schema.sql +0 -76
- package/src/services/incident-storage.ts +0 -593
- package/src/services/system-metrics-collector.ts +0 -244
- package/src/types/fastify.d.ts +0 -58
- package/src/utils/__tests__/sse-auth.spec.ts +0 -113
- package/src/utils/path-helpers.ts +0 -15
- package/src/utils/repo.ts +0 -42
- package/src/utils/schema.ts +0 -20
- package/src/utils/sse-auth.ts +0 -81
- package/test-sandbox.sh +0 -105
- package/tsconfig.build.json +0 -15
- package/tsconfig.json +0 -9
- 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
|
-
}
|