gateway: add local backend daemon controls to dashboard

This commit is contained in:
William Valentin
2026-02-22 16:36:31 -08:00
parent 44c7409a20
commit 6f2925ed10
6 changed files with 671 additions and 1 deletions
+50
View File
@@ -4,6 +4,7 @@ import type { MetricsSnapshot, EventEntry, ActiveRequestInfo } from '../metrics.
import type { ServiceInfo } from './services.js';
import type { NodeLocation, NodeStatus, NodePushToken } from './node.js';
import type { SessionAnalyticsSnapshot } from '../../session/index.js';
import type { LocalBackendAction, LocalBackendControlResult, LocalBackendStatus } from './localBackends.js';
/** Per-session token usage report returned by system.tokenUsage. */
export interface TokenUsageEntry {
@@ -103,6 +104,20 @@ export interface SystemHandlerDeps {
error?: string;
fetchedAt: number;
}>>;
/** Optional callback to retrieve local backend daemon statuses. */
getLocalBackends?: () => Promise<LocalBackendStatus[]> | LocalBackendStatus[];
/** Optional callback to control local backend daemons. */
controlLocalBackend?: (backend: string, action: string) => Promise<LocalBackendControlResult>;
}
function normalizeErrorMessage(error: unknown): string {
if (error instanceof Error && error.message.trim().length > 0) {
return error.message;
}
if (typeof error === 'string' && error.trim().length > 0) {
return error;
}
return 'Unknown error';
}
export function createSystemHandlers(deps: SystemHandlerDeps) {
@@ -285,5 +300,40 @@ export function createSystemHandlers(deps: SystemHandlerDeps) {
});
return makeResponse(request.id, { providers });
},
'system.localBackends': async (request: GatewayRequest): Promise<OutboundMessage> => {
if (!deps.getLocalBackends) {
return makeResponse(request.id, { backends: [] });
}
try {
const backends = await deps.getLocalBackends();
return makeResponse(request.id, { backends });
} catch (error) {
return makeError(request.id, ErrorCode.InternalError, `Failed to load local backends: ${normalizeErrorMessage(error)}`);
}
},
'system.localBackendControl': async (request: GatewayRequest): Promise<OutboundMessage> => {
if (!deps.controlLocalBackend) {
return makeError(request.id, ErrorCode.InternalError, 'Local backend control is not available in this environment');
}
const params = request.params as { backend?: string; action?: LocalBackendAction } | undefined;
if (!params?.backend || typeof params.backend !== 'string') {
return makeError(request.id, ErrorCode.InvalidRequest, 'backend is required');
}
if (!params?.action || typeof params.action !== 'string') {
return makeError(request.id, ErrorCode.InvalidRequest, 'action is required');
}
if (!['start', 'restart', 'stop'].includes(params.action)) {
return makeError(request.id, ErrorCode.InvalidRequest, 'action must be one of: start, restart, stop');
}
try {
const result = await deps.controlLocalBackend(params.backend, params.action);
return makeResponse(request.id, result);
} catch (error) {
return makeError(request.id, ErrorCode.InternalError, `Local backend control failed: ${normalizeErrorMessage(error)}`);
}
},
};
}