Skip to content

Commit 09df56e

Browse files
committed
docs: document gateway status health comments
1 parent 473f651 commit 09df56e

7 files changed

Lines changed: 42 additions & 1 deletion

File tree

src/commands/gateway-status.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/** CLI entrypoint for `openclaw gateway status`. */
12
import { isRich } from "../../packages/terminal-core/src/theme.js";
23
import { withProgress } from "../cli/progress.js";
34
import { readBestEffortConfig, resolveGatewayPort } from "../config/config.js";
@@ -35,6 +36,7 @@ function loadGatewayTlsModule() {
3536
return gatewayTlsModuleLoader.load();
3637
}
3738

39+
/** Resolves gateway status inputs, probes targets, then writes JSON or text output. */
3840
export async function gatewayStatusCommand(
3941
opts: {
4042
url?: string;
@@ -66,6 +68,8 @@ export async function gatewayStatusCommand(
6668
sanitizeSshTarget(opts.sshIdentity) ?? sanitizeSshTarget(cfg.gateway?.remote?.sshIdentity);
6769

6870
if (!sshTarget) {
71+
// Remote URL inference gives users a useful SSH default without requiring
72+
// gateway.remote.sshTarget when the host already appears in config.
6973
sshTarget = inferSshTargetFromRemoteUrl(cfg.gateway?.remote?.url);
7074
}
7175

src/commands/gateway-status/output.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/** Text and JSON rendering for the gateway status command. */
12
import { colorize, theme } from "../../../packages/terminal-core/src/theme.js";
23
import type { RuntimeEnv } from "../../runtime.js";
34
import { writeRuntimeJson } from "../../runtime.js";
@@ -12,6 +13,7 @@ import {
1213
} from "./helpers.js";
1314
import type { GatewayStatusProbedTarget } from "./probe-run.js";
1415

16+
/** Warning emitted when gateway status finds degraded or surprising probe state. */
1517
export type GatewayStatusWarning = {
1618
code: string;
1719
message: string;
@@ -38,6 +40,7 @@ function readModelPricingDegradedDetail(health: unknown): string | null {
3840
: "pricing bootstrap or refresh failed";
3941
}
4042

43+
/** Chooses the reachable target that best represents the user's requested gateway. */
4144
export function pickPrimaryProbedTarget(probed: GatewayStatusProbedTarget[]) {
4245
const reachable = probed.filter((entry) => isProbeReachable(entry.probe));
4346
return (
@@ -49,6 +52,7 @@ export function pickPrimaryProbedTarget(probed: GatewayStatusProbedTarget[]) {
4952
);
5053
}
5154

55+
/** Builds operator-facing warnings from probe, discovery, and SSH tunnel results. */
5256
export function buildGatewayStatusWarnings(params: {
5357
probed: GatewayStatusProbedTarget[];
5458
sshTarget: string | null;
@@ -88,6 +92,8 @@ export function buildGatewayStatusWarnings(params: {
8892
});
8993
}
9094
if (reachable.length > 1) {
95+
// Multiple reachable gateways are valid for isolated profiles but surprising
96+
// enough to call out before users debug against the wrong process.
9197
warnings.push({
9298
code: "multiple_gateways",
9399
message:
@@ -137,6 +143,7 @@ export function buildGatewayStatusWarnings(params: {
137143
return warnings;
138144
}
139145

146+
/** Writes the machine-readable gateway status payload and exits nonzero when unreachable. */
140147
export function writeGatewayStatusJson(params: {
141148
runtime: RuntimeEnv;
142149
startedAt: number;
@@ -193,6 +200,7 @@ export function writeGatewayStatusJson(params: {
193200
}
194201
}
195202

203+
/** Writes the human-readable gateway status report and exits nonzero when unreachable. */
196204
export function writeGatewayStatusText(params: {
197205
runtime: RuntimeEnv;
198206
rich: boolean;

src/commands/gateway-status/probe-run.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/** Runs gateway discovery, optional SSH tunneling, and per-target probes. */
12
import {
23
normalizeOptionalString,
34
readStringValue,
@@ -19,6 +20,7 @@ import {
1920
type GatewayStatusTarget,
2021
} from "./helpers.js";
2122

23+
/** Single gateway status target plus probe details and derived display metadata. */
2224
export type GatewayStatusProbedTarget = {
2325
target: GatewayStatusTarget;
2426
probe: Awaited<ReturnType<typeof probeGateway>>;
@@ -27,6 +29,7 @@ export type GatewayStatusProbedTarget = {
2729
authDiagnostics: string[];
2830
};
2931

32+
/** Probes configured, explicit, and optionally SSH-discovered gateway targets. */
3033
export async function runGatewayStatusProbePass(params: {
3134
cfg: OpenClawConfig;
3235
opts: {
@@ -93,6 +96,8 @@ export async function runGatewayStatusProbePass(params: {
9396
});
9497
}
9598

99+
// Prefer the concurrently-started tunnel, but allow auto-discovered SSH
100+
// targets to start after Bonjour finishes.
96101
const tunnel =
97102
tunnelFirst ||
98103
(sshTarget && !sshTunnelStarted && !sshTunnelError ? await tryStartTunnel() : null);
@@ -159,7 +164,7 @@ export async function runGatewayStatusProbePass(params: {
159164
try {
160165
await tunnel.stop();
161166
} catch {
162-
// best-effort
167+
// Status output must not fail just because tunnel cleanup races process exit.
163168
}
164169
}
165170
}

src/commands/gateway-status/test-support.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Test-only config helpers for gateway status SecretRef scenarios.
2+
/** Builds gateway config where local and remote auth values use environment SecretRefs. */
13
export function createSecretRefGatewayConfig(params?: { gatewayMode?: "local" | "remote" }) {
24
return {
35
secrets: {

src/commands/health-format.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/** Formatting helpers for `openclaw health` failures and channel summaries. */
12
import { asNullableRecord } from "@openclaw/normalization-core/record-coerce";
23
import { colorize, isRich, theme } from "../../packages/terminal-core/src/theme.js";
34
import { formatChannelStatusState } from "../channels/plugins/status-state.js";
@@ -21,6 +22,7 @@ const formatKv = (line: string, rich: boolean) => {
2122
return `${colorize(rich, theme.muted, `${key}:`)} ${colorize(rich, valueColor, value)}`;
2223
};
2324

25+
/** Formats thrown health errors with rich detail lines when terminal color is enabled. */
2426
export function formatHealthCheckFailure(err: unknown, opts: { rich?: boolean } = {}): string {
2527
const rich = opts.rich ?? isRich();
2628
const raw = String(err);
@@ -128,6 +130,7 @@ const isProbeFailure = (summary: ChannelAccountHealthSummary): boolean => {
128130
return ok === false;
129131
};
130132

133+
/** Formats one terse health line per channel, optionally including every account. */
131134
export const formatHealthChannelLines = (
132135
summary: HealthSummary,
133136
opts: {

src/commands/health.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/** Collects and renders gateway health for channels, agents, plugins, and sessions. */
12
import { resolveTimerTimeoutMs } from "@openclaw/normalization-core/number-coercion";
23
import { asNullableRecord } from "@openclaw/normalization-core/record-coerce";
34
import { styleHealthChannelLine } from "../../packages/terminal-core/src/health-style.js";
@@ -101,6 +102,8 @@ const buildNonSensitiveProbeFailure = (
101102
return undefined;
102103
}
103104

105+
// Preserve the actionable Full Disk Access failure while stripping the local
106+
// username path before health leaves the gateway.
104107
const error = redactIMessageProbeErrorMessage(record.error);
105108
if (
106109
!/\bimsg\b/i.test(error) ||
@@ -155,6 +158,7 @@ function formatEventLoopHealthLine(summary: HealthSummary): string | null {
155158
}`;
156159
}
157160

161+
/** Formats optional model-pricing cache degradation for text health output. */
158162
export function formatModelPricingHealthLine(summary: HealthSummary): string | null {
159163
const modelPricing = summary.modelPricing;
160164
if (!modelPricing || modelPricing.state === "disabled") {
@@ -184,6 +188,7 @@ function buildContextEngineHealthSummary(): ContextEngineHealthSummary | undefin
184188
return quarantined.length > 0 ? { quarantined } : undefined;
185189
}
186190

191+
/** Formats context engine quarantine state for text health output. */
187192
export function formatContextEngineHealthLine(summary: HealthSummary): string | null {
188193
const quarantined = summary.contextEngines?.quarantined ?? [];
189194
if (quarantined.length === 0) {
@@ -403,6 +408,7 @@ async function resolveHealthAccountContext(params: {
403408
};
404409
}
405410

411+
/** Builds the gateway-side health snapshot for channels, agents, plugins, and sessions. */
406412
export async function getHealthSnapshot(params?: {
407413
timeoutMs?: number;
408414
probe?: boolean;
@@ -471,6 +477,8 @@ export async function getHealthSnapshot(params?: {
471477
),
472478
),
473479
);
480+
// Probe preferred/default/bound accounts first, but include all configured
481+
// accounts so verbose health can explain account-specific failures.
474482
debugHealth("channel", {
475483
id: plugin.id,
476484
accountIds,
@@ -623,6 +631,7 @@ export async function getHealthSnapshot(params?: {
623631
return summary;
624632
}
625633

634+
/** Runs the `openclaw health` command against the gateway and renders JSON or text. */
626635
export async function healthCommand(
627636
opts: {
628637
json?: boolean;

src/commands/health.types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Shared summary types returned by gateway health and rendered by the CLI.
2+
/** Health snapshot for one configured channel account. */
13
export type ChannelAccountHealthSummary = {
24
accountId: string;
35
configured?: boolean;
@@ -8,10 +10,12 @@ export type ChannelAccountHealthSummary = {
810
[key: string]: unknown;
911
};
1012

13+
/** Channel-level health summary with optional per-account details. */
1114
export type ChannelHealthSummary = ChannelAccountHealthSummary & {
1215
accounts?: Record<string, ChannelAccountHealthSummary>;
1316
};
1417

18+
/** Agent heartbeat and session-store health metadata. */
1519
export type AgentHealthSummary = {
1620
agentId: string;
1721
name?: string;
@@ -20,6 +24,7 @@ export type AgentHealthSummary = {
2024
sessions: HealthSummary["sessions"];
2125
};
2226

27+
/** Plugin load error details safe for the health payload. */
2328
export type PluginHealthErrorSummary = {
2429
id: string;
2530
origin: string;
@@ -30,11 +35,13 @@ export type PluginHealthErrorSummary = {
3035
error: string;
3136
};
3237

38+
/** Plugin registry health summary. */
3339
export type PluginHealthSummary = {
3440
loaded: string[];
3541
errors: PluginHealthErrorSummary[];
3642
};
3743

44+
/** Context engine quarantine entry included in health output. */
3845
export type ContextEngineHealthQuarantineSummary = {
3946
engineId: string;
4047
owner?: string;
@@ -43,13 +50,16 @@ export type ContextEngineHealthQuarantineSummary = {
4350
failedAt: number;
4451
};
4552

53+
/** Context engine health summary. */
4654
export type ContextEngineHealthSummary = {
4755
quarantined: ContextEngineHealthQuarantineSummary[];
4856
};
4957

58+
/** Optional model pricing cache health reported by the gateway. */
5059
export type ModelPricingHealthSummary =
5160
import("../gateway/model-pricing-cache-state.js").GatewayModelPricingHealth;
5261

62+
/** Full gateway health payload consumed by `openclaw health`. */
5363
export type HealthSummary = {
5464
ok: true;
5565
ts: number;

0 commit comments

Comments
 (0)