Spaces:
Paused
Paused
| import { randomUUID } from "node:crypto"; | |
| import type { SystemPresence } from "../infra/system-presence.js"; | |
| import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js"; | |
| import { GatewayClient } from "./client.js"; | |
| export type GatewayProbeAuth = { | |
| token?: string; | |
| password?: string; | |
| }; | |
| export type GatewayProbeClose = { | |
| code: number; | |
| reason: string; | |
| hint?: string; | |
| }; | |
| export type GatewayProbeResult = { | |
| ok: boolean; | |
| url: string; | |
| connectLatencyMs: number | null; | |
| error: string | null; | |
| close: GatewayProbeClose | null; | |
| health: unknown; | |
| status: unknown; | |
| presence: SystemPresence[] | null; | |
| configSnapshot: unknown; | |
| }; | |
| function formatError(err: unknown): string { | |
| if (err instanceof Error) { | |
| return err.message; | |
| } | |
| return String(err); | |
| } | |
| export async function probeGateway(opts: { | |
| url: string; | |
| auth?: GatewayProbeAuth; | |
| timeoutMs: number; | |
| }): Promise<GatewayProbeResult> { | |
| const startedAt = Date.now(); | |
| const instanceId = randomUUID(); | |
| let connectLatencyMs: number | null = null; | |
| let connectError: string | null = null; | |
| let close: GatewayProbeClose | null = null; | |
| return await new Promise<GatewayProbeResult>((resolve) => { | |
| let settled = false; | |
| const settle = (result: Omit<GatewayProbeResult, "url">) => { | |
| if (settled) { | |
| return; | |
| } | |
| settled = true; | |
| clearTimeout(timer); | |
| client.stop(); | |
| resolve({ url: opts.url, ...result }); | |
| }; | |
| const client = new GatewayClient({ | |
| url: opts.url, | |
| token: opts.auth?.token, | |
| password: opts.auth?.password, | |
| clientName: GATEWAY_CLIENT_NAMES.CLI, | |
| clientVersion: "dev", | |
| mode: GATEWAY_CLIENT_MODES.PROBE, | |
| instanceId, | |
| onConnectError: (err) => { | |
| connectError = formatError(err); | |
| }, | |
| onClose: (code, reason) => { | |
| close = { code, reason }; | |
| }, | |
| onHelloOk: async () => { | |
| connectLatencyMs = Date.now() - startedAt; | |
| try { | |
| const [health, status, presence, configSnapshot] = await Promise.all([ | |
| client.request("health"), | |
| client.request("status"), | |
| client.request("system-presence"), | |
| client.request("config.get", {}), | |
| ]); | |
| settle({ | |
| ok: true, | |
| connectLatencyMs, | |
| error: null, | |
| close, | |
| health, | |
| status, | |
| presence: Array.isArray(presence) ? (presence as SystemPresence[]) : null, | |
| configSnapshot, | |
| }); | |
| } catch (err) { | |
| settle({ | |
| ok: false, | |
| connectLatencyMs, | |
| error: formatError(err), | |
| close, | |
| health: null, | |
| status: null, | |
| presence: null, | |
| configSnapshot: null, | |
| }); | |
| } | |
| }, | |
| }); | |
| const timer = setTimeout( | |
| () => { | |
| settle({ | |
| ok: false, | |
| connectLatencyMs, | |
| error: connectError ? `connect failed: ${connectError}` : "timeout", | |
| close, | |
| health: null, | |
| status: null, | |
| presence: null, | |
| configSnapshot: null, | |
| }); | |
| }, | |
| Math.max(250, opts.timeoutMs), | |
| ); | |
| client.start(); | |
| }); | |
| } | |