4runr-os 2.10.39 → 2.10.41
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/apps/gateway/dist/apps/gateway/src/index.js +14 -4
- package/apps/gateway/dist/apps/gateway/src/index.js.map +1 -1
- package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.d.ts +18 -0
- package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.d.ts.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.js +117 -0
- package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.js.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.d.ts +2 -0
- package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.d.ts.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.js +54 -0
- package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.js.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/routes/monitoring.d.ts +15 -0
- package/apps/gateway/dist/apps/gateway/src/routes/monitoring.d.ts.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/routes/monitoring.js +164 -0
- package/apps/gateway/dist/apps/gateway/src/routes/monitoring.js.map +1 -0
- package/apps/gateway/package-lock.json +204 -353
- package/apps/gateway/src/index.ts +27 -8
- package/apps/gateway/src/metrics/monitoring-detail.ts +162 -0
- package/apps/gateway/src/middleware/log-capture.ts +70 -0
- package/apps/gateway/src/routes/monitoring.ts +298 -0
- package/dist/gateway-client.d.ts +2 -0
- package/dist/gateway-client.d.ts.map +1 -1
- package/dist/gateway-client.js +22 -0
- package/dist/gateway-client.js.map +1 -1
- package/dist/tui-handlers.js +498 -0
- package/dist/tui-handlers.js.map +1 -1
- package/mk3-tui/src/app/render_scheduler.rs +111 -112
- package/mk3-tui/src/app.rs +1078 -295
- package/mk3-tui/src/debug_log.rs +131 -124
- package/mk3-tui/src/io/mod.rs +63 -66
- package/mk3-tui/src/io/protocol.rs +14 -15
- package/mk3-tui/src/io/stdio.rs +31 -32
- package/mk3-tui/src/io/ws.rs +25 -32
- package/mk3-tui/src/main.rs +774 -212
- package/mk3-tui/src/monitoring/mod.rs +428 -0
- package/mk3-tui/src/screens/mod.rs +53 -39
- package/mk3-tui/src/storage/cache.rs +221 -224
- package/mk3-tui/src/storage/mod.rs +5 -6
- package/mk3-tui/src/ui/agent_builder.rs +1148 -922
- package/mk3-tui/src/ui/agent_list.rs +344 -295
- package/mk3-tui/src/ui/boot.rs +145 -148
- package/mk3-tui/src/ui/connection_portal.rs +121 -98
- package/mk3-tui/src/ui/help.rs +340 -284
- package/mk3-tui/src/ui/layout.rs +966 -803
- package/mk3-tui/src/ui/mod.rs +1 -1
- package/mk3-tui/src/ui/portal_monitoring.rs +1027 -147
- package/mk3-tui/src/ui/run_manager.rs +784 -764
- package/mk3-tui/src/ui/safe_viewport.rs +236 -235
- package/mk3-tui/src/ui/settings.rs +414 -362
- package/mk3-tui/src/ui/setup_portal.rs +158 -101
- package/mk3-tui/src/websocket.rs +315 -308
- package/package.json +2 -2
|
@@ -72,7 +72,9 @@ import path from 'path';
|
|
|
72
72
|
import { registerDevKitRoutes } from './devkit/routes.js';
|
|
73
73
|
import { mfaRoutes } from './routes/mfa.js';
|
|
74
74
|
import { gdprRoutes } from './routes/gdpr.js';
|
|
75
|
+
import { registerMonitoringRoutes } from './routes/monitoring.js';
|
|
75
76
|
import { ddosProtection } from './middleware/ddos-protection.js';
|
|
77
|
+
import { wrapLoggerForMonitoring } from './middleware/log-capture.js';
|
|
76
78
|
import { initializeDatabase, shutdownDatabase } from './db/init.js';
|
|
77
79
|
import type { PrismaClient } from '@prisma/client';
|
|
78
80
|
|
|
@@ -86,7 +88,10 @@ if (persistenceMode === 'memory') {
|
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
// Create structured logger (will be updated per-request with correlation ID)
|
|
89
|
-
|
|
91
|
+
let baseLogger = isMemoryMode ? memoryLogger : createLogger('Gateway');
|
|
92
|
+
|
|
93
|
+
// Wrap logger to capture logs for monitoring endpoints (Phase 0)
|
|
94
|
+
baseLogger = wrapLoggerForMonitoring(baseLogger);
|
|
90
95
|
|
|
91
96
|
const PORT = env.PORT;
|
|
92
97
|
const HOST = env.HOST;
|
|
@@ -274,11 +279,16 @@ if (dbResult.client && persistenceMode !== 'memory') {
|
|
|
274
279
|
|
|
275
280
|
// Health endpoint (liveness probe - no auth required)
|
|
276
281
|
fastify.get('/health', async (request, reply) => {
|
|
277
|
-
healthCheckTotal.inc({ endpoint: '/health', status: '200' });
|
|
278
282
|
const alive = isAlive();
|
|
279
283
|
const { getMetricsSummary } = await import('./metrics/index.js');
|
|
280
284
|
const summary = await getMetricsSummary();
|
|
281
285
|
|
|
286
|
+
const statusCode = alive ? 200 : 503;
|
|
287
|
+
reply.status(statusCode);
|
|
288
|
+
|
|
289
|
+
// Track actual HTTP status (not just 200)
|
|
290
|
+
healthCheckTotal.inc({ endpoint: '/health', status: String(statusCode) });
|
|
291
|
+
|
|
282
292
|
return {
|
|
283
293
|
ok: alive,
|
|
284
294
|
status: alive ? 'alive' : 'dead',
|
|
@@ -368,16 +378,16 @@ fastify.get('/metrics', async (request, reply) => {
|
|
|
368
378
|
const runQueue = getQueue();
|
|
369
379
|
if (runQueue) {
|
|
370
380
|
try {
|
|
381
|
+
// BullMQ v4+ getJobCounts: pass state names as separate args, returns exact keys
|
|
371
382
|
const counts = await runQueue.getJobCounts(
|
|
372
|
-
'wait',
|
|
373
|
-
'paused',
|
|
374
|
-
'active',
|
|
375
|
-
'delayed',
|
|
376
383
|
'waiting',
|
|
384
|
+
'active',
|
|
377
385
|
'completed',
|
|
378
|
-
'failed'
|
|
386
|
+
'failed',
|
|
387
|
+
'delayed',
|
|
388
|
+
'paused'
|
|
379
389
|
) as Record<string, number>;
|
|
380
|
-
const waiting = counts['waiting'] ??
|
|
390
|
+
const waiting = counts['waiting'] ?? 0;
|
|
381
391
|
const activeJobs = counts['active'] ?? 0;
|
|
382
392
|
queueJobsWaiting.set({ queue: 'run-execution' }, waiting);
|
|
383
393
|
queueJobsActive.set({ queue: 'run-execution' }, activeJobs);
|
|
@@ -420,6 +430,15 @@ fastify.get('/api/monitoring/summary', async (request, reply) => {
|
|
|
420
430
|
};
|
|
421
431
|
});
|
|
422
432
|
|
|
433
|
+
// Register advanced monitoring routes (Phase 0)
|
|
434
|
+
// SECURITY: In production, these require auth. For local dev (127.0.0.1), consider skipping auth.
|
|
435
|
+
// Deployment guidance: use network policy to restrict access or enable requireAuth.
|
|
436
|
+
const isLocalDev = HOST === '127.0.0.1' || HOST === 'localhost';
|
|
437
|
+
registerMonitoringRoutes(fastify, {
|
|
438
|
+
requireAuth: isLocalDev ? false : requireAuth, // Explicitly false for local, function for prod
|
|
439
|
+
readRateLimit: readRateLimit,
|
|
440
|
+
});
|
|
441
|
+
|
|
423
442
|
// Apply DDoS protection globally (before other middleware)
|
|
424
443
|
fastify.addHook('onRequest', ddosProtection);
|
|
425
444
|
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 3: structured snapshots from prom-client metrics for /api/monitoring/metrics/*
|
|
3
|
+
* (no Prometheus text parsing — reads live registry values.)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
httpRequestTotal,
|
|
8
|
+
httpRequestErrors,
|
|
9
|
+
runsCreated,
|
|
10
|
+
runsStarted,
|
|
11
|
+
runsCompleted,
|
|
12
|
+
runsActive,
|
|
13
|
+
sseConnectionsOpened,
|
|
14
|
+
sseConnectionsClosed,
|
|
15
|
+
sseActiveConnections,
|
|
16
|
+
sseMessagesTotal,
|
|
17
|
+
dbConnectionsActive,
|
|
18
|
+
dbConnectionsIdle,
|
|
19
|
+
redisConnectionsActive,
|
|
20
|
+
} from './index.js';
|
|
21
|
+
|
|
22
|
+
function sumCounterValues(m: { values: Array<{ value: number }> }): number {
|
|
23
|
+
return m.values.reduce((s, x) => s + x.value, 0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function gaugeValue(m: { values: Array<{ value: number }> }): number {
|
|
27
|
+
return m.values[0]?.value ?? 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isFailedRunStatus(status: string): boolean {
|
|
31
|
+
const normalized = status.trim().toLowerCase();
|
|
32
|
+
return [
|
|
33
|
+
'failed',
|
|
34
|
+
'failure',
|
|
35
|
+
'error',
|
|
36
|
+
'errored',
|
|
37
|
+
'cancelled',
|
|
38
|
+
'canceled',
|
|
39
|
+
'timeout',
|
|
40
|
+
'timed_out',
|
|
41
|
+
].includes(normalized);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function getMonitoringHttpPayload(): Promise<Record<string, unknown>> {
|
|
45
|
+
const req = await httpRequestTotal.get();
|
|
46
|
+
let totalRequests = 0;
|
|
47
|
+
const byRoute: Array<{ method: string; route: string; status_code: string; count: number }> =
|
|
48
|
+
[];
|
|
49
|
+
for (const x of req.values) {
|
|
50
|
+
totalRequests += x.value;
|
|
51
|
+
byRoute.push({
|
|
52
|
+
method: String(x.labels.method ?? ''),
|
|
53
|
+
route: String(x.labels.route ?? ''),
|
|
54
|
+
status_code: String(x.labels.status_code ?? ''),
|
|
55
|
+
count: x.value,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
byRoute.sort((a, b) => b.count - a.count);
|
|
59
|
+
|
|
60
|
+
const err = await httpRequestErrors.get();
|
|
61
|
+
let totalErrors = 0;
|
|
62
|
+
const errorsByRoute: Array<{
|
|
63
|
+
method: string;
|
|
64
|
+
route: string;
|
|
65
|
+
error_type: string;
|
|
66
|
+
count: number;
|
|
67
|
+
}> = [];
|
|
68
|
+
for (const x of err.values) {
|
|
69
|
+
totalErrors += x.value;
|
|
70
|
+
errorsByRoute.push({
|
|
71
|
+
method: String(x.labels.method ?? ''),
|
|
72
|
+
route: String(x.labels.route ?? ''),
|
|
73
|
+
error_type: String(x.labels.error_type ?? ''),
|
|
74
|
+
count: x.value,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
errorsByRoute.sort((a, b) => b.count - a.count);
|
|
78
|
+
|
|
79
|
+
const errorRate = totalRequests > 0 ? totalErrors / totalRequests : 0;
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
placeholder: false,
|
|
83
|
+
summary: {
|
|
84
|
+
totalRequests,
|
|
85
|
+
totalErrors,
|
|
86
|
+
errorRate: Number(errorRate.toFixed(4)),
|
|
87
|
+
},
|
|
88
|
+
topRoutes: byRoute.slice(0, 25),
|
|
89
|
+
errorsByRoute: errorsByRoute.slice(0, 15),
|
|
90
|
+
timestamp: new Date().toISOString(),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function getMonitoringRunsPayload(): Promise<Record<string, unknown>> {
|
|
95
|
+
const created = sumCounterValues(await runsCreated.get());
|
|
96
|
+
const started = sumCounterValues(await runsStarted.get());
|
|
97
|
+
const completedByStatus = (await runsCompleted.get()).values.map((x) => ({
|
|
98
|
+
status: String(x.labels.status ?? ''),
|
|
99
|
+
count: x.value,
|
|
100
|
+
}));
|
|
101
|
+
const completed = completedByStatus.reduce((s, x) => s + x.count, 0);
|
|
102
|
+
const failedByStatus = completedByStatus.filter((x) => isFailedRunStatus(x.status));
|
|
103
|
+
const active = gaugeValue(await runsActive.get());
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
placeholder: false,
|
|
107
|
+
summary: {
|
|
108
|
+
created,
|
|
109
|
+
started,
|
|
110
|
+
completed,
|
|
111
|
+
failed: failedByStatus.reduce((s, x) => s + x.count, 0),
|
|
112
|
+
active,
|
|
113
|
+
},
|
|
114
|
+
byStatus: completedByStatus,
|
|
115
|
+
failedStatusLabels: failedByStatus.map((x) => x.status),
|
|
116
|
+
timestamp: new Date().toISOString(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function getMonitoringSsePayload(): Promise<Record<string, unknown>> {
|
|
121
|
+
const opened = sumCounterValues(await sseConnectionsOpened.get());
|
|
122
|
+
const closed = sumCounterValues(await sseConnectionsClosed.get());
|
|
123
|
+
const active = gaugeValue(await sseActiveConnections.get());
|
|
124
|
+
const messages = sumCounterValues(await sseMessagesTotal.get());
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
placeholder: false,
|
|
128
|
+
summary: {
|
|
129
|
+
connectionsOpened: opened,
|
|
130
|
+
connectionsClosed: closed,
|
|
131
|
+
activeConnections: active,
|
|
132
|
+
messagesSent: messages,
|
|
133
|
+
},
|
|
134
|
+
timestamp: new Date().toISOString(),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function getDependencyPoolsPayload(): Promise<{
|
|
139
|
+
database: { active: number; idle: number };
|
|
140
|
+
redis: { active: number };
|
|
141
|
+
telemetry: {
|
|
142
|
+
source: string;
|
|
143
|
+
bestEffort: boolean;
|
|
144
|
+
note: string;
|
|
145
|
+
};
|
|
146
|
+
}> {
|
|
147
|
+
return {
|
|
148
|
+
database: {
|
|
149
|
+
active: gaugeValue(await dbConnectionsActive.get()),
|
|
150
|
+
idle: gaugeValue(await dbConnectionsIdle.get()),
|
|
151
|
+
},
|
|
152
|
+
redis: {
|
|
153
|
+
active: gaugeValue(await redisConnectionsActive.get()),
|
|
154
|
+
},
|
|
155
|
+
telemetry: {
|
|
156
|
+
source: 'prom-client connection gauges',
|
|
157
|
+
bestEffort: true,
|
|
158
|
+
note:
|
|
159
|
+
'Connection gauges are best-effort instrumentation; zeros can mean the gauges are not being updated, not necessarily that dependencies are disconnected.',
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log capture middleware for monitoring log buffer
|
|
3
|
+
* Intercepts logger calls and feeds them to the monitoring log buffer
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { addMonitoringLog } from '../routes/monitoring.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Wrap a logger to capture log entries for monitoring
|
|
10
|
+
* Note: Uses duck typing to avoid strict type checks (compatible with both Logger and memoryLogger)
|
|
11
|
+
*/
|
|
12
|
+
export function wrapLoggerForMonitoring(logger: any): any {
|
|
13
|
+
// Skip if this doesn't look like a standard logger (e.g., memoryLogger)
|
|
14
|
+
if (!logger || typeof logger.info !== 'function') {
|
|
15
|
+
return logger;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const originalInfo = logger.info.bind(logger);
|
|
19
|
+
const originalWarn = logger.warn.bind(logger);
|
|
20
|
+
const originalError = logger.error.bind(logger);
|
|
21
|
+
const originalDebug = logger.debug?.bind(logger);
|
|
22
|
+
|
|
23
|
+
logger.info = (message: string, data?: Record<string, unknown>) => {
|
|
24
|
+
addMonitoringLog({
|
|
25
|
+
timestamp: new Date().toISOString(),
|
|
26
|
+
level: 'info',
|
|
27
|
+
message,
|
|
28
|
+
context: logger.context || 'Gateway',
|
|
29
|
+
...(data && { data }),
|
|
30
|
+
});
|
|
31
|
+
return originalInfo(message, data);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
logger.warn = (message: string, data?: Record<string, unknown>) => {
|
|
35
|
+
addMonitoringLog({
|
|
36
|
+
timestamp: new Date().toISOString(),
|
|
37
|
+
level: 'warn',
|
|
38
|
+
message,
|
|
39
|
+
context: logger.context || 'Gateway',
|
|
40
|
+
...(data && { data }),
|
|
41
|
+
});
|
|
42
|
+
return originalWarn(message, data);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
logger.error = (message: string, data?: Record<string, unknown>) => {
|
|
46
|
+
addMonitoringLog({
|
|
47
|
+
timestamp: new Date().toISOString(),
|
|
48
|
+
level: 'error',
|
|
49
|
+
message,
|
|
50
|
+
context: logger.context || 'Gateway',
|
|
51
|
+
...(data && { data }),
|
|
52
|
+
});
|
|
53
|
+
return originalError(message, data);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (originalDebug) {
|
|
57
|
+
logger.debug = (message: string, data?: Record<string, unknown>) => {
|
|
58
|
+
addMonitoringLog({
|
|
59
|
+
timestamp: new Date().toISOString(),
|
|
60
|
+
level: 'debug',
|
|
61
|
+
message,
|
|
62
|
+
context: logger.context || 'Gateway',
|
|
63
|
+
...(data && { data }),
|
|
64
|
+
});
|
|
65
|
+
return originalDebug(message, data);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return logger;
|
|
70
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advanced monitoring routes for Portal Monitoring (Phase 0)
|
|
3
|
+
* Provides structured logs, dependency details, and extended metrics
|
|
4
|
+
*
|
|
5
|
+
* SECURITY: All monitoring endpoints require authentication in production.
|
|
6
|
+
* For local development, consider network binding (127.0.0.1 only).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
|
|
10
|
+
import { getQueue } from '../queue/index.js';
|
|
11
|
+
import { getRedisClient } from '../db/redis.js';
|
|
12
|
+
import { getPrismaClient } from '../db/prisma.js';
|
|
13
|
+
import { performHealthChecks } from '../health/index.js';
|
|
14
|
+
import { createLogger } from '@4runr/shared';
|
|
15
|
+
import {
|
|
16
|
+
getDependencyPoolsPayload,
|
|
17
|
+
getMonitoringHttpPayload,
|
|
18
|
+
getMonitoringRunsPayload,
|
|
19
|
+
getMonitoringSsePayload,
|
|
20
|
+
} from '../metrics/monitoring-detail.js';
|
|
21
|
+
|
|
22
|
+
const logger = createLogger('Gateway:Monitoring');
|
|
23
|
+
|
|
24
|
+
interface LogEntry {
|
|
25
|
+
timestamp: string;
|
|
26
|
+
level: 'error' | 'warn' | 'info' | 'debug';
|
|
27
|
+
message: string;
|
|
28
|
+
context?: string;
|
|
29
|
+
data?: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// In-memory ring buffer for logs (last 1000 entries)
|
|
33
|
+
const LOG_BUFFER_SIZE = 1000;
|
|
34
|
+
const logBuffer: LogEntry[] = [];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Add log entry to buffer (called by logger middleware or manually)
|
|
38
|
+
*/
|
|
39
|
+
export function addMonitoringLog(entry: LogEntry): void {
|
|
40
|
+
logBuffer.push(entry);
|
|
41
|
+
if (logBuffer.length > LOG_BUFFER_SIZE) {
|
|
42
|
+
logBuffer.shift();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Register monitoring routes
|
|
48
|
+
* @param fastify Fastify instance
|
|
49
|
+
* @param options Optional configuration for route security
|
|
50
|
+
*/
|
|
51
|
+
export function registerMonitoringRoutes(
|
|
52
|
+
fastify: FastifyInstance,
|
|
53
|
+
options?: {
|
|
54
|
+
requireAuth?: boolean | any; // boolean false or auth handler function
|
|
55
|
+
readRateLimit?: any;
|
|
56
|
+
}
|
|
57
|
+
): void {
|
|
58
|
+
const { requireAuth, readRateLimit } = options || {};
|
|
59
|
+
|
|
60
|
+
// Determine if auth is required (requireAuth can be false, function, or undefined)
|
|
61
|
+
const authHandler = requireAuth === false ? null : requireAuth;
|
|
62
|
+
const preHandler = authHandler && readRateLimit
|
|
63
|
+
? [authHandler, readRateLimit]
|
|
64
|
+
: authHandler
|
|
65
|
+
? [authHandler]
|
|
66
|
+
: [];
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* GET /api/monitoring/logs
|
|
70
|
+
* Returns last N Gateway logs for TUI display
|
|
71
|
+
* SECURITY: Requires authentication (contains operational data)
|
|
72
|
+
*/
|
|
73
|
+
fastify.get('/api/monitoring/logs', {
|
|
74
|
+
...(preHandler.length > 0 && { preHandler }),
|
|
75
|
+
}, async (request: FastifyRequest, reply: FastifyReply) => {
|
|
76
|
+
const query = request.query as { limit?: string; level?: string };
|
|
77
|
+
const limit = Math.min(parseInt(query.limit || '100', 10), LOG_BUFFER_SIZE);
|
|
78
|
+
const levelFilter = query.level as LogEntry['level'] | undefined;
|
|
79
|
+
|
|
80
|
+
let logs = logBuffer.slice(-limit);
|
|
81
|
+
|
|
82
|
+
if (levelFilter) {
|
|
83
|
+
logs = logs.filter(log => log.level === levelFilter);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
logs,
|
|
88
|
+
count: logs.length,
|
|
89
|
+
bufferSize: logBuffer.length,
|
|
90
|
+
timestamp: new Date().toISOString(),
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* GET /api/monitoring/dependencies/detail
|
|
96
|
+
* Extended dependency info (connection pools, queue stats, etc.)
|
|
97
|
+
* SECURITY: Requires authentication (exposes infrastructure details)
|
|
98
|
+
*/
|
|
99
|
+
fastify.get('/api/monitoring/dependencies/detail', {
|
|
100
|
+
...(preHandler.length > 0 && { preHandler }),
|
|
101
|
+
}, async (request: FastifyRequest, reply: FastifyReply) => {
|
|
102
|
+
const health = await performHealthChecks();
|
|
103
|
+
const details: {
|
|
104
|
+
database?: {
|
|
105
|
+
status: string;
|
|
106
|
+
latency?: number;
|
|
107
|
+
poolAvailable?: boolean;
|
|
108
|
+
pool?: { active: number; idle: number };
|
|
109
|
+
telemetry?: { source: string; bestEffort: boolean; note: string };
|
|
110
|
+
};
|
|
111
|
+
redis?: {
|
|
112
|
+
status: string;
|
|
113
|
+
latency?: number;
|
|
114
|
+
connectionCountAvailable?: boolean;
|
|
115
|
+
connections?: { active: number };
|
|
116
|
+
telemetry?: { source: string; bestEffort: boolean; note: string };
|
|
117
|
+
};
|
|
118
|
+
queue?: {
|
|
119
|
+
status: string;
|
|
120
|
+
jobs?: {
|
|
121
|
+
waiting: number;
|
|
122
|
+
active: number;
|
|
123
|
+
completed: number;
|
|
124
|
+
failed: number;
|
|
125
|
+
delayed: number;
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
} = {};
|
|
129
|
+
|
|
130
|
+
const pools = await getDependencyPoolsPayload();
|
|
131
|
+
|
|
132
|
+
// Database details
|
|
133
|
+
if (health.checks.database) {
|
|
134
|
+
const dbLatency = health.checks.database.responseTime;
|
|
135
|
+
details.database = {
|
|
136
|
+
status: health.checks.database.status,
|
|
137
|
+
...(dbLatency !== undefined && { latency: dbLatency }),
|
|
138
|
+
poolAvailable: true,
|
|
139
|
+
pool: {
|
|
140
|
+
active: pools.database.active,
|
|
141
|
+
idle: pools.database.idle,
|
|
142
|
+
},
|
|
143
|
+
telemetry: pools.telemetry,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Redis details
|
|
148
|
+
if (health.checks.redis) {
|
|
149
|
+
const redisLatency = health.checks.redis.responseTime;
|
|
150
|
+
details.redis = {
|
|
151
|
+
status: health.checks.redis.status,
|
|
152
|
+
...(redisLatency !== undefined && { latency: redisLatency }),
|
|
153
|
+
connectionCountAvailable: true,
|
|
154
|
+
connections: {
|
|
155
|
+
active: pools.redis.active,
|
|
156
|
+
},
|
|
157
|
+
telemetry: pools.telemetry,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Queue details (real data from BullMQ)
|
|
162
|
+
if (health.checks.queue) {
|
|
163
|
+
details.queue = {
|
|
164
|
+
status: health.checks.queue.status,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const queue = getQueue();
|
|
169
|
+
if (queue) {
|
|
170
|
+
// BullMQ v4+ getJobCounts: pass state names as separate args
|
|
171
|
+
// Returns object with keys matching exact state names passed
|
|
172
|
+
const counts = await queue.getJobCounts(
|
|
173
|
+
'waiting',
|
|
174
|
+
'active',
|
|
175
|
+
'completed',
|
|
176
|
+
'failed',
|
|
177
|
+
'delayed'
|
|
178
|
+
) as Record<string, number>;
|
|
179
|
+
|
|
180
|
+
details.queue.jobs = {
|
|
181
|
+
waiting: counts['waiting'] ?? 0,
|
|
182
|
+
active: counts['active'] ?? 0,
|
|
183
|
+
completed: counts['completed'] ?? 0,
|
|
184
|
+
failed: counts['failed'] ?? 0,
|
|
185
|
+
delayed: counts['delayed'] ?? 0,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
} catch (err) {
|
|
189
|
+
logger.error('Failed to fetch queue job counts', {
|
|
190
|
+
error: err instanceof Error ? err.message : String(err),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
details,
|
|
197
|
+
telemetry: pools.telemetry,
|
|
198
|
+
timestamp: new Date().toISOString(),
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* GET /api/monitoring/metrics/http
|
|
204
|
+
* Detailed HTTP metrics with route breakdown
|
|
205
|
+
* SECURITY: Requires authentication
|
|
206
|
+
*/
|
|
207
|
+
fastify.get('/api/monitoring/metrics/http', {
|
|
208
|
+
...(preHandler.length > 0 && { preHandler }),
|
|
209
|
+
}, async (_request: FastifyRequest, _reply: FastifyReply) => {
|
|
210
|
+
return await getMonitoringHttpPayload();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* GET /api/monitoring/metrics/runs
|
|
215
|
+
* Detailed run metrics
|
|
216
|
+
* SECURITY: Requires authentication
|
|
217
|
+
*/
|
|
218
|
+
fastify.get('/api/monitoring/metrics/runs', {
|
|
219
|
+
...(preHandler.length > 0 && { preHandler }),
|
|
220
|
+
}, async (_request: FastifyRequest, _reply: FastifyReply) => {
|
|
221
|
+
return await getMonitoringRunsPayload();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* GET /api/monitoring/metrics/sse
|
|
226
|
+
* SSE connection / message counters (Phase 3)
|
|
227
|
+
*/
|
|
228
|
+
fastify.get('/api/monitoring/metrics/sse', {
|
|
229
|
+
...(preHandler.length > 0 && { preHandler }),
|
|
230
|
+
}, async (_request: FastifyRequest, _reply: FastifyReply) => {
|
|
231
|
+
return await getMonitoringSsePayload();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* GET /api/monitoring/metrics/queue
|
|
236
|
+
* Detailed queue metrics with job states
|
|
237
|
+
* SECURITY: Requires authentication
|
|
238
|
+
*/
|
|
239
|
+
fastify.get('/api/monitoring/metrics/queue', {
|
|
240
|
+
...(preHandler.length > 0 && { preHandler }),
|
|
241
|
+
}, async (request: FastifyRequest, reply: FastifyReply) => {
|
|
242
|
+
try {
|
|
243
|
+
const queue = getQueue();
|
|
244
|
+
if (!queue) {
|
|
245
|
+
return {
|
|
246
|
+
available: false,
|
|
247
|
+
message: 'Queue not initialized (Redis required)',
|
|
248
|
+
timestamp: new Date().toISOString(),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// BullMQ v4+ getJobCounts: returns exact keys matching state names passed
|
|
253
|
+
const counts = await queue.getJobCounts(
|
|
254
|
+
'waiting',
|
|
255
|
+
'active',
|
|
256
|
+
'completed',
|
|
257
|
+
'failed',
|
|
258
|
+
'delayed',
|
|
259
|
+
'paused'
|
|
260
|
+
) as Record<string, number>;
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
available: true,
|
|
264
|
+
jobs: {
|
|
265
|
+
waiting: counts['waiting'] ?? 0,
|
|
266
|
+
active: counts['active'] ?? 0,
|
|
267
|
+
completed: counts['completed'] ?? 0,
|
|
268
|
+
failed: counts['failed'] ?? 0,
|
|
269
|
+
delayed: counts['delayed'] ?? 0,
|
|
270
|
+
paused: counts['paused'] ?? 0,
|
|
271
|
+
},
|
|
272
|
+
queueName: (queue as any).name || 'run-execution',
|
|
273
|
+
timestamp: new Date().toISOString(),
|
|
274
|
+
};
|
|
275
|
+
} catch (error) {
|
|
276
|
+
logger.error('Failed to get queue metrics', {
|
|
277
|
+
error: error instanceof Error ? error.message : String(error),
|
|
278
|
+
});
|
|
279
|
+
return {
|
|
280
|
+
available: false,
|
|
281
|
+
error: error instanceof Error ? error.message : String(error),
|
|
282
|
+
timestamp: new Date().toISOString(),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
logger.info('Monitoring routes registered (Phase 0)', {
|
|
288
|
+
authRequired: preHandler.length > 0,
|
|
289
|
+
endpoints: [
|
|
290
|
+
'/api/monitoring/logs',
|
|
291
|
+
'/api/monitoring/dependencies/detail',
|
|
292
|
+
'/api/monitoring/metrics/http',
|
|
293
|
+
'/api/monitoring/metrics/runs',
|
|
294
|
+
'/api/monitoring/metrics/sse',
|
|
295
|
+
'/api/monitoring/metrics/queue',
|
|
296
|
+
],
|
|
297
|
+
});
|
|
298
|
+
}
|
package/dist/gateway-client.d.ts
CHANGED
|
@@ -102,5 +102,7 @@ export declare class GatewayClient {
|
|
|
102
102
|
* Raw Prometheus text from GET /metrics (public on Gateway; no auth).
|
|
103
103
|
*/
|
|
104
104
|
prometheusMetrics(): Promise<string>;
|
|
105
|
+
/** GET JSON (monitoring APIs, etc.). Uses same auth headers as the rest of the client. */
|
|
106
|
+
getJson(path: string): Promise<unknown>;
|
|
105
107
|
}
|
|
106
108
|
//# sourceMappingURL=gateway-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-client.d.ts","sourceRoot":"","sources":["../src/gateway-client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"gateway-client.d.ts","sourceRoot":"","sources":["../src/gateway-client.ts"],"names":[],"mappings":"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"}
|
package/dist/gateway-client.js
CHANGED
|
@@ -224,5 +224,27 @@ export class GatewayClient {
|
|
|
224
224
|
const body = response.data;
|
|
225
225
|
return typeof body === 'string' ? body : String(body ?? '');
|
|
226
226
|
}
|
|
227
|
+
/** GET JSON (monitoring APIs, etc.). Uses same auth headers as the rest of the client. */
|
|
228
|
+
async getJson(path) {
|
|
229
|
+
const response = await this.http.get(path, {
|
|
230
|
+
validateStatus: () => true,
|
|
231
|
+
timeout: 15000,
|
|
232
|
+
});
|
|
233
|
+
if (response.status >= 400) {
|
|
234
|
+
const body = response.data;
|
|
235
|
+
const detail = body && typeof body === 'object'
|
|
236
|
+
? String(body.error ??
|
|
237
|
+
body.message ??
|
|
238
|
+
'')
|
|
239
|
+
: typeof body === 'string'
|
|
240
|
+
? body.slice(0, 240)
|
|
241
|
+
: '';
|
|
242
|
+
const authHint = response.status === 401 || response.status === 403
|
|
243
|
+
? ' (authentication failed or expired; reconnect to the Gateway and retry)'
|
|
244
|
+
: '';
|
|
245
|
+
throw new Error(`${path} returned HTTP ${response.status}${authHint}${detail ? `: ${detail}` : ''}`);
|
|
246
|
+
}
|
|
247
|
+
return response.data;
|
|
248
|
+
}
|
|
227
249
|
}
|
|
228
250
|
//# sourceMappingURL=gateway-client.js.map
|