feat(cli): colorize gateway health + daemon output
This commit is contained in:
@@ -67,6 +67,7 @@ import {
|
||||
GOOGLE_GEMINI_DEFAULT_MODEL,
|
||||
} from "./google-gemini-model-default.js";
|
||||
import { healthCommand } from "./health.js";
|
||||
import { formatHealthCheckFailure } from "./health-format.js";
|
||||
import {
|
||||
applyAuthProfileConfig,
|
||||
applyMinimaxConfig,
|
||||
@@ -1286,7 +1287,7 @@ export async function runConfigureWizard(
|
||||
try {
|
||||
await healthCommand({ json: false, timeoutMs: 10_000 }, runtime);
|
||||
} catch (err) {
|
||||
runtime.error(`Health check failed: ${String(err)}`);
|
||||
runtime.error(formatHealthCheckFailure(err));
|
||||
note(
|
||||
[
|
||||
"Docs:",
|
||||
|
||||
@@ -80,6 +80,7 @@ import {
|
||||
shouldSuggestMemorySystem,
|
||||
} from "./doctor-workspace.js";
|
||||
import { healthCommand } from "./health.js";
|
||||
import { formatHealthCheckFailure } from "./health-format.js";
|
||||
import { applyWizardMetadata, printWizardHeader } from "./onboard-helpers.js";
|
||||
import { ensureSystemdUserLingerInteractive } from "./systemd-linger.js";
|
||||
|
||||
@@ -310,7 +311,7 @@ export async function doctorCommand(
|
||||
note("Gateway not running.", "Gateway");
|
||||
note(gatewayDetails.message, "Gateway connection");
|
||||
} else {
|
||||
runtime.error(`Health check failed: ${message}`);
|
||||
runtime.error(formatHealthCheckFailure(err));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,7 +456,7 @@ export async function doctorCommand(
|
||||
note("Gateway not running.", "Gateway");
|
||||
note(gatewayDetails.message, "Gateway connection");
|
||||
} else {
|
||||
runtime.error(`Health check failed: ${message}`);
|
||||
runtime.error(formatHealthCheckFailure(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
src/commands/health-format.test.ts
Normal file
43
src/commands/health-format.test.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { formatHealthCheckFailure } from "./health-format.js";
|
||||
|
||||
const stripAnsi = (input: string) =>
|
||||
input.replace(
|
||||
// biome-ignore lint/suspicious/noControlCharactersInRegex: strip ANSI escape sequences
|
||||
/\u001b\[[0-9;]*m/g,
|
||||
"",
|
||||
);
|
||||
|
||||
describe("formatHealthCheckFailure", () => {
|
||||
it("keeps non-rich output stable", () => {
|
||||
const err = new Error(
|
||||
"gateway closed (1006 abnormal closure): no close reason",
|
||||
);
|
||||
expect(formatHealthCheckFailure(err, { rich: false })).toBe(
|
||||
`Health check failed: ${String(err)}`,
|
||||
);
|
||||
});
|
||||
|
||||
it("formats gateway connection details as indented key/value lines", () => {
|
||||
const err = new Error(
|
||||
[
|
||||
"gateway closed (1006 abnormal closure (no close frame)): no close reason",
|
||||
"Gateway target: ws://127.0.0.1:19001",
|
||||
"Source: local loopback",
|
||||
"Config: /Users/steipete/.clawdbot-dev/clawdbot.json",
|
||||
"Bind: loopback",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
expect(stripAnsi(formatHealthCheckFailure(err, { rich: true }))).toBe(
|
||||
[
|
||||
"Health check failed: gateway closed (1006 abnormal closure (no close frame)): no close reason",
|
||||
" Gateway target: ws://127.0.0.1:19001",
|
||||
" Source: local loopback",
|
||||
" Config: /Users/steipete/.clawdbot-dev/clawdbot.json",
|
||||
" Bind: loopback",
|
||||
].join("\n"),
|
||||
);
|
||||
});
|
||||
});
|
||||
48
src/commands/health-format.ts
Normal file
48
src/commands/health-format.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { colorize, isRich, theme } from "../terminal/theme.js";
|
||||
|
||||
const formatKv = (line: string, rich: boolean) => {
|
||||
const idx = line.indexOf(": ");
|
||||
if (idx <= 0) return colorize(rich, theme.muted, line);
|
||||
const key = line.slice(0, idx);
|
||||
const value = line.slice(idx + 2);
|
||||
|
||||
const valueColor =
|
||||
key === "Gateway target" || key === "Config"
|
||||
? theme.command
|
||||
: key === "Source"
|
||||
? theme.muted
|
||||
: theme.info;
|
||||
|
||||
return `${colorize(rich, theme.muted, `${key}:`)} ${colorize(rich, valueColor, value)}`;
|
||||
};
|
||||
|
||||
export function formatHealthCheckFailure(
|
||||
err: unknown,
|
||||
opts: { rich?: boolean } = {},
|
||||
): string {
|
||||
const rich = opts.rich ?? isRich();
|
||||
const raw = String(err);
|
||||
const message = err instanceof Error ? err.message : raw;
|
||||
|
||||
if (!rich) return `Health check failed: ${raw}`;
|
||||
|
||||
const lines = message
|
||||
.split("\n")
|
||||
.map((l) => l.trimEnd())
|
||||
.filter(Boolean);
|
||||
const detailsIdx = lines.findIndex((l) => l.startsWith("Gateway target: "));
|
||||
|
||||
const summaryLines = (detailsIdx >= 0 ? lines.slice(0, detailsIdx) : lines)
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean);
|
||||
const detailLines = detailsIdx >= 0 ? lines.slice(detailsIdx) : [];
|
||||
|
||||
const summary = summaryLines.length > 0 ? summaryLines.join(" ") : message;
|
||||
const header = colorize(rich, theme.error.bold, "Health check failed");
|
||||
|
||||
const out: string[] = [`${header}: ${summary}`];
|
||||
for (const line of detailLines) {
|
||||
out.push(` ${formatKv(line, rich)}`);
|
||||
}
|
||||
return out.join("\n");
|
||||
}
|
||||
Reference in New Issue
Block a user