From d8e138c7433796b6e2520bcec09e58c57dc44256 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 15 Mar 2026 19:55:08 -0700 Subject: [PATCH] Gateway: add presence-only probe mode for status --- src/commands/status.scan.test.ts | 3 +++ src/commands/status.scan.ts | 1 + src/gateway/probe.test.ts | 14 ++++++++++++++ src/gateway/probe.ts | 19 ++++++++++++++++++- 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/commands/status.scan.test.ts b/src/commands/status.scan.test.ts index b94f1f0ece0..55f323f0b4a 100644 --- a/src/commands/status.scan.test.ts +++ b/src/commands/status.scan.test.ts @@ -246,6 +246,9 @@ describe("scanStatus", () => { await scanStatus({ json: true }, {} as never); expect(mocks.ensurePluginRegistryLoaded).toHaveBeenCalledWith({ scope: "channels" }); + expect(mocks.probeGateway).toHaveBeenCalledWith( + expect.objectContaining({ detailLevel: "presence" }), + ); expect(mocks.callGateway).not.toHaveBeenCalledWith( expect.objectContaining({ method: "channels.status" }), ); diff --git a/src/commands/status.scan.ts b/src/commands/status.scan.ts index f7661573578..88dd21e7177 100644 --- a/src/commands/status.scan.ts +++ b/src/commands/status.scan.ts @@ -98,6 +98,7 @@ async function resolveGatewayProbeSnapshot(params: { url: gatewayConnection.url, auth: gatewayProbeAuthResolution.auth, timeoutMs: Math.min(params.opts.all ? 5000 : 2500, params.opts.timeoutMs ?? 10_000), + detailLevel: "presence", }).catch(() => null); if (gatewayProbeAuthWarning && gatewayProbe?.ok === false) { gatewayProbe.error = gatewayProbe.error diff --git a/src/gateway/probe.test.ts b/src/gateway/probe.test.ts index 6cd7d64fc51..f91dc5148d5 100644 --- a/src/gateway/probe.test.ts +++ b/src/gateway/probe.test.ts @@ -81,4 +81,18 @@ describe("probeGateway", () => { expect(result.ok).toBe(true); expect(gatewayClientState.requests).toEqual([]); }); + + it("fetches only presence for presence-only probes", async () => { + const result = await probeGateway({ + url: "ws://127.0.0.1:18789", + timeoutMs: 1_000, + detailLevel: "presence", + }); + + expect(result.ok).toBe(true); + expect(gatewayClientState.requests).toEqual(["system-presence"]); + expect(result.health).toBeNull(); + expect(result.status).toBeNull(); + expect(result.configSnapshot).toBeNull(); + }); }); diff --git a/src/gateway/probe.ts b/src/gateway/probe.ts index 40740987fb0..87a77b8bfef 100644 --- a/src/gateway/probe.ts +++ b/src/gateway/probe.ts @@ -34,6 +34,7 @@ export async function probeGateway(opts: { auth?: GatewayProbeAuth; timeoutMs: number; includeDetails?: boolean; + detailLevel?: "none" | "presence" | "full"; }): Promise { const startedAt = Date.now(); const instanceId = randomUUID(); @@ -49,6 +50,8 @@ export async function probeGateway(opts: { } })(); + const detailLevel = opts.includeDetails === false ? "none" : (opts.detailLevel ?? "full"); + return await new Promise((resolve) => { let settled = false; const settle = (result: Omit) => { @@ -79,7 +82,7 @@ export async function probeGateway(opts: { }, onHelloOk: async () => { connectLatencyMs = Date.now() - startedAt; - if (opts.includeDetails === false) { + if (detailLevel === "none") { settle({ ok: true, connectLatencyMs, @@ -93,6 +96,20 @@ export async function probeGateway(opts: { return; } try { + if (detailLevel === "presence") { + const presence = await client.request("system-presence"); + settle({ + ok: true, + connectLatencyMs, + error: null, + close, + health: null, + status: null, + presence: Array.isArray(presence) ? (presence as SystemPresence[]) : null, + configSnapshot: null, + }); + return; + } const [health, status, presence, configSnapshot] = await Promise.all([ client.request("health"), client.request("status"),