89 lines
2.3 KiB
TypeScript
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,
|
|
},
|
|
};
|
|
}
|