mcporter/src/daemon/request-utils.ts
2026-05-20 17:34:42 +01:00

89 lines
2.3 KiB
TypeScript

import type { ServerDefinition } from '../config.js';
import { keepAliveIdleTimeout } from '../lifecycle.js';
import type { Runtime } from '../runtime.js';
import type { DaemonResponse } from './protocol.js';
export interface ServerActivity {
connected: boolean;
lastUsedAt?: number;
}
export function ensureManaged(server: string, managedServers: Map<string, ServerDefinition>): void {
if (!managedServers.has(server)) {
throw new Error(`Server '${server}' is not managed by the daemon.`);
}
}
export function markActivity(server: string, activity: Map<string, ServerActivity>): void {
const entry = activity.get(server);
if (entry) {
entry.connected = true;
entry.lastUsedAt = Date.now();
} else {
activity.set(server, { connected: true, lastUsedAt: Date.now() });
}
}
export async function evictIdleServers(
runtime: Runtime,
managedServers: Map<string, ServerDefinition>,
activity: Map<string, ServerActivity>
): Promise<void> {
const now = Date.now();
await Promise.all(
Array.from(managedServers.entries()).map(async ([name, definition]) => {
const timeout = keepAliveIdleTimeout(definition);
if (!timeout) {
return;
}
const entry = activity.get(name);
if (!entry?.lastUsedAt) {
return;
}
if (now - entry.lastUsedAt < timeout) {
return;
}
await runtime.close(name).catch(() => {});
activity.set(name, { connected: false });
})
);
}
export function shouldShutdownDaemonForIdle(
lastActivityAt: number,
now: number,
idleTimeoutMs: number | undefined,
activeRequests = 0
): boolean {
return (
activeRequests <= 0 &&
typeof idleTimeoutMs === 'number' &&
idleTimeoutMs > 0 &&
now - lastActivityAt >= idleTimeoutMs
);
}
export function daemonIdleWatcherInterval(idleTimeoutMs: number | undefined): number {
if (!idleTimeoutMs) {
return 30_000;
}
return Math.min(30_000, Math.max(100, Math.floor(idleTimeoutMs / 2)));
}
export function buildErrorResponse(id: string, code: string, error?: unknown): DaemonResponse {
let message = code;
if (error instanceof Error) {
message = error.message;
} else if (typeof error === 'string') {
message = error;
}
return {
id,
ok: false,
error: {
code,
message,
},
};
}