60 lines
2.6 KiB
TypeScript
60 lines
2.6 KiB
TypeScript
import type { FastifyInstance } from 'fastify';
|
|
import { z } from 'zod';
|
|
import { getHermesTelemetrySnapshot, getHermesTokenUsageSnapshot } from './repository.js';
|
|
import { HermesInstanceIdSchema, HermesTelemetrySnapshotSchema, HermesTokenUsageSnapshotSchema } from './types.js';
|
|
import { requireAdmin } from '../../lib/auth.js';
|
|
|
|
const ParamsSchema = z.object({ instance: HermesInstanceIdSchema });
|
|
|
|
export async function hermesTelemetryRoutes(fastify: FastifyInstance) {
|
|
// GET /api/hermes/telemetry/:instance
|
|
// Admin-only: this endpoint shells out to `hermes` CLI in the instance
|
|
// owner's environment (`runuser -u uma --` for Bheem) and reads the
|
|
// watchdog log + backup repo. Treat it as privileged the same way the
|
|
// VM/system endpoints are. See `dashboard/DEPLOYMENT.md` Privilege Surface.
|
|
fastify.get('/hermes/telemetry/:instance', {
|
|
preHandler: async (req) => requireAdmin(req),
|
|
}, async (req, reply) => {
|
|
let params: z.infer<typeof ParamsSchema>;
|
|
try {
|
|
params = ParamsSchema.parse(req.params);
|
|
} catch (err) {
|
|
return reply.code(400).send({ error: 'Invalid instance', detail: (err as Error).message });
|
|
}
|
|
|
|
try {
|
|
const snapshot = await getHermesTelemetrySnapshot(params.instance);
|
|
// Validate our own response so a shape regression surfaces here as a
|
|
// 500 rather than a corrupt UI state — same approach as hermes-ops.
|
|
const validated = HermesTelemetrySnapshotSchema.parse(snapshot);
|
|
return reply.send(validated);
|
|
} catch (err) {
|
|
fastify.log.error(err, 'failed to build hermes telemetry snapshot');
|
|
return reply.code(500).send({ error: 'Failed to build hermes telemetry snapshot' });
|
|
}
|
|
});
|
|
|
|
// GET /api/hermes/telemetry/:instance/token-usage
|
|
// Admin-only: aggregate/session-level token analytics from Hermes state.db.
|
|
// The repository deliberately avoids selecting raw message content.
|
|
fastify.get('/hermes/telemetry/:instance/token-usage', {
|
|
preHandler: async (req) => requireAdmin(req),
|
|
}, async (req, reply) => {
|
|
let params: z.infer<typeof ParamsSchema>;
|
|
try {
|
|
params = ParamsSchema.parse(req.params);
|
|
} catch (err) {
|
|
return reply.code(400).send({ error: 'Invalid instance', detail: (err as Error).message });
|
|
}
|
|
|
|
try {
|
|
const snapshot = await getHermesTokenUsageSnapshot(params.instance);
|
|
const validated = HermesTokenUsageSnapshotSchema.parse(snapshot);
|
|
return reply.send(validated);
|
|
} catch (err) {
|
|
fastify.log.error(err, 'failed to build hermes token usage snapshot');
|
|
return reply.code(500).send({ error: 'Failed to build hermes token usage snapshot' });
|
|
}
|
|
});
|
|
}
|