feat(cli): improve gateway status output

This commit is contained in:
Peter Steinberger
2026-01-09 09:27:27 +01:00
parent 89132fdd25
commit 5b50c97939
12 changed files with 917 additions and 31 deletions

View File

@@ -32,6 +32,7 @@ export type GatewayClientOptions = {
maxProtocol?: number;
onEvent?: (evt: EventFrame) => void;
onHelloOk?: (hello: HelloOk) => void;
onConnectError?: (err: Error) => void;
onClose?: (code: number, reason: string) => void;
onGap?: (info: { expected: number; received: number }) => void;
};
@@ -130,6 +131,9 @@ export class GatewayClient {
this.opts.onHelloOk?.(helloOk);
})
.catch((err) => {
this.opts.onConnectError?.(
err instanceof Error ? err : new Error(String(err)),
);
const msg = `gateway connect failed: ${String(err)}`;
if (this.opts.mode === "probe") logDebug(msg);
else logError(msg);

123
src/gateway/probe.ts Normal file
View File

@@ -0,0 +1,123 @@
import { randomUUID } from "node:crypto";
import type { SystemPresence } from "../infra/system-presence.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: "cli",
clientVersion: "dev",
mode: "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();
});
}