openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
581 lines (580 loc) • 27.3 kB
JavaScript
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js";
import { d as resolveGatewayPort } from "./paths-mvMm5bYV.js";
import { n as isRich, r as theme } from "./theme-vjDs9tao.js";
import { u as readConfigFileSnapshot } from "./io-Gi7-pyU-.js";
import { t as formatConfigIssueLine } from "./issue-format-CwCoGNbt.js";
import "./config-C9RxTsn1.js";
import { c as classifyPortListener, d as isExpectedGatewayListeners, l as formatPortDiagnostics, o as inspectPortUsage, u as isDualStackLoopbackGatewayListeners } from "./ports-CoxQQbiY.js";
import { a as resolveGatewaySupervisorLogPaths, i as resolveGatewayRestartLogPath, r as resolveGatewayLogPaths } from "./restart-logs-BvZvRKQN.js";
import { t as readLastGatewayErrorLine } from "./diagnostics-C0o1p3sH.js";
import { a as classifyOAuthRefreshFailureReason } from "./oauth-refresh-failure-C54bUOW9.js";
import { l as summarizeRestartSentinel, o as readRestartSentinel } from "./restart-sentinel-BFAG1Tav.js";
import { t as buildWorkspaceSkillStatus } from "./status-Dlj2eT9o.js";
import { t as canExecRequestNode } from "./exec-defaults-BenGbA5f.js";
import { n as formatTimeAgo } from "./format-relative-Bjc3l98W.js";
import { r as withProgress } from "./progress-CN3xUiCO.js";
import { n as renderTable, t as getTerminalTableWidth } from "./table-CpFtGZut.js";
import { t as getRemoteSkillEligibility } from "./remote-DoOmoEi1.js";
import { c as formatPluginCompatibilityNotice, n as buildPluginCompatibilityNotices } from "./status-TB29O9XH.js";
import { n as resolveStatusAllConnectionDetails } from "./status.gateway-connection-BhoLn6II.js";
import { f as redactSecrets } from "./format-Bar-LTbU.js";
import { d as buildStatusOverviewSurfaceFromOverview, i as resolveStatusGatewayHealthSafe, n as resolveStatusGatewayDiagnosticsSafe, s as resolveStatusServiceSummaries } from "./status-runtime-shared-D72cMRcz.js";
import { a as buildStatusChannelsSection, c as buildStatusOverviewSection, f as formatUpdateRestartActionLines, i as buildStatusChannelDetailsSections, m as buildStatusAllOverviewRows, n as appendStatusSectionHeading, p as formatUpdateRestartStatusValue, r as buildStatusAgentsSection, t as appendStatusReportSections } from "./text-report-C_U929Hv.js";
import { t as resolveNodeOnlyGatewayInfo } from "./status.node-mode-BaeooGjs.js";
import { t as collectStatusScanOverview } from "./status.scan-overview-BXn7EgEP.js";
import fs from "node:fs/promises";
//#region src/commands/status-all/report-data.ts
function resolveStatusAllConfigPath(path) {
const trimmed = path?.trim();
return trimmed && trimmed.length > 0 ? trimmed : "(unknown config path)";
}
/** Collects local diagnosis inputs that are not part of the shared overview scan. */
async function resolveStatusAllLocalDiagnosis(params) {
const { overview } = params;
const snap = await readConfigFileSnapshot().catch(() => null);
const configPath = resolveStatusAllConfigPath(snap?.path);
const health = params.nodeOnlyGateway ? void 0 : await resolveStatusGatewayHealthSafe({
config: overview.cfg,
timeoutMs: Math.min(8e3, params.timeoutMs ?? 1e4),
gatewayReachable: params.gatewayReachable,
gatewayProbeError: params.gatewayProbe?.error ?? null,
...params.gatewayCallOverrides ? { callOverrides: params.gatewayCallOverrides } : {}
});
const diagnostics = params.nodeOnlyGateway ? null : await resolveStatusGatewayDiagnosticsSafe({
config: overview.cfg,
timeoutMs: Math.min(5e3, params.timeoutMs ?? 1e4),
gatewayReachable: params.gatewayReachable,
...params.gatewayCallOverrides ? { callOverrides: params.gatewayCallOverrides } : {}
});
params.progress.setLabel("Checking local state…");
const sentinel = await readRestartSentinel().catch(() => null);
const lastErr = await readLastGatewayErrorLine(process.env).catch(() => null);
const port = resolveGatewayPort(overview.cfg);
const portUsage = await inspectPortUsage(port).catch(() => null);
params.progress.tick();
const defaultWorkspace = overview.agentStatus.agents.find((a) => a.id === overview.agentStatus.defaultId)?.workspaceDir ?? overview.agentStatus.agents[0]?.workspaceDir ?? null;
const skillStatus = defaultWorkspace != null ? (() => {
try {
return buildWorkspaceSkillStatus(defaultWorkspace, {
config: overview.cfg,
eligibility: { remote: getRemoteSkillEligibility({ advertiseExecNode: canExecRequestNode({
cfg: overview.cfg,
agentId: overview.agentStatus.defaultId
}) }) }
});
} catch {
return null;
}
})() : null;
const pluginCompatibility = buildPluginCompatibilityNotices({ config: overview.cfg });
return {
configPath,
health,
diagnosis: {
snap,
remoteUrlMissing: overview.gatewaySnapshot.remoteUrlMissing,
secretDiagnostics: overview.secretDiagnostics,
sentinel,
lastErr,
port,
portUsage,
tailscaleMode: overview.tailscaleMode,
tailscale: {
backendState: null,
dnsName: overview.tailscaleDns,
ips: [],
error: null
},
tailscaleHttpsUrl: overview.tailscaleHttpsUrl,
skillStatus,
pluginCompatibility,
channelsStatus: overview.channelsStatus,
channelIssues: overview.channelIssues,
agentStatus: overview.agentStatus,
gatewayReachable: params.gatewayReachable,
health,
deliveryDiagnostics: diagnostics,
nodeOnlyGateway: params.nodeOnlyGateway
}
};
}
/** Builds the full status-all report data model from a completed overview scan. */
async function buildStatusAllReportData(params) {
const gatewaySnapshot = params.overview.gatewaySnapshot;
const { configPath, health, diagnosis } = await resolveStatusAllLocalDiagnosis({
overview: params.overview,
progress: params.progress,
gatewayReachable: gatewaySnapshot.gatewayReachable,
gatewayProbe: gatewaySnapshot.gatewayProbe,
gatewayCallOverrides: gatewaySnapshot.gatewayCallOverrides,
nodeOnlyGateway: params.nodeOnlyGateway,
timeoutMs: params.timeoutMs
});
return {
overviewRows: buildStatusAllOverviewRows({
surface: buildStatusOverviewSurfaceFromOverview({
overview: params.overview,
gatewayService: params.daemon,
nodeService: params.nodeService,
nodeOnlyGateway: params.nodeOnlyGateway
}),
osLabel: params.overview.osSummary.label,
configPath,
secretDiagnosticsCount: params.overview.secretDiagnostics.length,
updateRestartValue: formatUpdateRestartStatusValue(diagnosis.sentinel?.payload),
agentStatus: params.overview.agentStatus,
tailscaleBackendState: diagnosis.tailscale.backendState
}),
channels: params.overview.channels,
channelIssues: params.overview.channelIssues.map((issue) => ({
channel: issue.channel,
message: issue.message
})),
agentStatus: params.overview.agentStatus,
connectionDetailsForReport: resolveStatusAllConnectionDetails({
nodeOnlyGateway: params.nodeOnlyGateway,
remoteUrlMissing: gatewaySnapshot.remoteUrlMissing,
gatewayConnection: gatewaySnapshot.gatewayConnection,
bindMode: params.overview.cfg.gateway?.bind ?? "loopback",
configPath
}),
diagnosis: {
...diagnosis,
health
}
};
}
//#endregion
//#region src/commands/status-all/gateway.ts
/** Reads the last non-empty lines from a gateway log file, returning an empty list on read failure. */
async function readFileTailLines(filePath, maxLines) {
const raw = await fs.readFile(filePath, "utf8").catch(() => "");
if (!raw.trim()) return [];
const lines = raw.replace(/\r/g, "").split("\n");
return lines.slice(Math.max(0, lines.length - maxLines)).map((line) => line.trimEnd()).filter((line) => line.trim().length > 0);
}
function countMatches(haystack, needle) {
if (!haystack || !needle) return 0;
return haystack.split(needle).length - 1;
}
function shorten(message, maxLen) {
const cleaned = message.replace(/\s+/g, " ").trim();
if (cleaned.length <= maxLen) return cleaned;
return `${cleaned.slice(0, Math.max(0, maxLen - 1))}…`;
}
function normalizeGwsLine(line) {
return line.replace(/\s+runId=[^\s]+/g, "").replace(/\s+conn=[^\s]+/g, "").replace(/\s+id=[^\s]+/g, "").replace(/\s+error=Error:.*$/g, "").trim();
}
function consumeJsonBlock(lines, startIndex) {
const startLine = lines[startIndex] ?? "";
const braceAt = startLine.indexOf("{");
if (braceAt < 0) return null;
const parts = [startLine.slice(braceAt)];
let depth = countMatches(parts[0] ?? "", "{") - countMatches(parts[0] ?? "", "}");
let i = startIndex;
while (depth > 0 && i + 1 < lines.length) {
i += 1;
const next = lines[i] ?? "";
parts.push(next);
depth += countMatches(next, "{") - countMatches(next, "}");
}
return {
json: parts.join("\n"),
endIndex: i
};
}
/** Summarizes gateway log tail lines, grouping repeated failures and trimming long output. */
function summarizeLogTail(rawLines, opts) {
const maxLines = Math.max(6, opts?.maxLines ?? 26);
const out = [];
const groups = /* @__PURE__ */ new Map();
const addGroup = (key, base) => {
const existing = groups.get(key);
if (existing) {
existing.count += 1;
return;
}
groups.set(key, {
count: 1,
index: out.length,
base
});
out.push(base);
};
const addLine = (line) => {
const trimmed = line.trimEnd();
if (!trimmed) return;
out.push(trimmed);
};
const lines = rawLines.map((line) => line.trimEnd()).filter(Boolean);
for (let i = 0; i < lines.length; i += 1) {
const line = lines[i] ?? "";
const trimmedStart = line.trimStart();
if ((trimmedStart.startsWith("\"") || trimmedStart === "}" || trimmedStart === "{" || trimmedStart.startsWith("}") || trimmedStart.startsWith("{")) && !trimmedStart.startsWith("[") && !trimmedStart.startsWith("#")) continue;
const tokenRefresh = line.match(/^\[([^\]]+)\]\s+Token refresh failed:\s*(\d+)\s*(\{)?\s*$/);
if (tokenRefresh) {
const tag = tokenRefresh[1] ?? "unknown";
const status = tokenRefresh[2] ?? "unknown";
const block = consumeJsonBlock(lines, i);
if (block) {
i = block.endIndex;
const parsed = (() => {
try {
return JSON.parse(block.json);
} catch {
return null;
}
})();
const code = normalizeOptionalString(parsed?.error?.code) ?? null;
const msg = normalizeOptionalString(parsed?.error?.message) ?? null;
const refreshReason = classifyOAuthRefreshFailureReason(msg ?? "");
const msgShort = msg ? refreshReason ? "re-auth required" : shorten(msg, 52) : null;
const base = `[${tag}] token refresh ${status}${code ? ` ${code}` : ""}${msgShort ? ` · ${msgShort}` : ""}`;
addGroup(`token:${tag}:${status}:${code ?? ""}:${msgShort ?? ""}`, base);
continue;
}
}
const embedded = line.match(/^Embedded agent failed before reply:\s+OAuth token refresh failed for ([^:]+):/);
if (embedded) {
const provider = normalizeOptionalString(embedded[1]) || "unknown";
addGroup(`embedded:${provider}`, `Embedded agent: OAuth token refresh failed (${provider})`);
continue;
}
if (line.startsWith("[gws]") && line.includes("errorCode=UNAVAILABLE") && line.includes("OAuth token refresh failed")) {
const normalized = normalizeGwsLine(line);
addGroup(`gws:${normalized}`, normalized);
continue;
}
addLine(line);
}
for (const g of groups.values()) {
if (g.count <= 1) continue;
out[g.index] = `${g.base} ×${g.count}`;
}
const deduped = [];
for (const line of out) {
if (deduped[deduped.length - 1] === line) continue;
deduped.push(line);
}
if (deduped.length <= maxLines) return deduped;
const head = Math.min(6, Math.floor(maxLines / 3));
const tail = Math.max(1, maxLines - head - 1);
return [
...deduped.slice(0, head),
`… ${deduped.length - head - tail} lines omitted …`,
...deduped.slice(-tail)
];
}
//#endregion
//#region src/commands/status-all/diagnosis.ts
const AGENT_ACTIVITY_SOFT_WARNING_MS = 30 * 6e4;
function countRecentAgentSessions(agentStatus, thresholdMs) {
return agentStatus.agents.filter((agent) => agent.lastActiveAgeMs != null && agent.lastActiveAgeMs <= thresholdMs).length;
}
function countGatewayListenerPids(portUsage) {
const pids = /* @__PURE__ */ new Set();
for (const listener of portUsage.listeners) {
if (classifyPortListener(listener, portUsage.port) !== "gateway") continue;
if (typeof listener.pid === "number" && Number.isFinite(listener.pid)) pids.add(listener.pid);
}
return pids.size;
}
function isDeliveryDiagnosticsLike(value) {
return Boolean(value && typeof value === "object");
}
function countDeliveryEvent(snapshot, type) {
const value = snapshot.summary?.byType?.[type];
return typeof value === "number" && Number.isFinite(value) ? value : 0;
}
function latestDeliveryEventAgeMs(snapshot) {
const latestTs = (snapshot.events ?? []).filter((event) => [
"message.received",
"message.dispatch.started",
"message.dispatch.completed",
"session.turn.created",
"message.processed"
].includes(event.type ?? "")).reduce((max, event) => {
const ts = event.ts;
return typeof ts === "number" && Number.isFinite(ts) ? Math.max(max, ts) : max;
}, 0);
return latestTs > 0 ? Date.now() - latestTs : null;
}
/** Appends config, gateway, channel, delivery, and log diagnostics to the status-all report. */
async function appendStatusAllDiagnosis(params) {
const { lines, muted, ok, warn, fail } = params;
const emitCheck = (label, status) => {
const icon = status === "ok" ? ok("✓") : status === "warn" ? warn("!") : fail("✗");
const colored = status === "ok" ? ok(label) : status === "warn" ? warn(label) : fail(label);
lines.push(`${icon} ${colored}`);
};
lines.push("");
lines.push(muted("Gateway connection details:"));
for (const line of redactSecrets(params.connectionDetailsForReport).split("\n").map((l) => l.trimEnd())) lines.push(` ${muted(line)}`);
lines.push("");
if (params.snap) {
const status = !params.snap.exists ? "fail" : params.snap.valid ? "ok" : "warn";
emitCheck(`Config: ${params.snap.path ?? "(unknown)"}`, status);
const issues = [...params.snap.legacyIssues ?? [], ...params.snap.issues ?? []];
const uniqueIssues = issues.filter((issue, index) => issues.findIndex((x) => x.path === issue.path && x.message === issue.message) === index);
for (const issue of uniqueIssues.slice(0, 12)) lines.push(` ${formatConfigIssueLine(issue, "-")}`);
if (uniqueIssues.length > 12) lines.push(` ${muted(`… +${uniqueIssues.length - 12} more`)}`);
} else emitCheck("Config: read failed", "warn");
if (params.remoteUrlMissing) {
lines.push("");
emitCheck("Gateway remote mode misconfigured (gateway.remote.url missing)", "warn");
lines.push(` ${muted("Fix: set gateway.remote.url, or set gateway.mode=local.")}`);
}
emitCheck(`Secret diagnostics (${params.secretDiagnostics.length})`, params.secretDiagnostics.length === 0 ? "ok" : "warn");
for (const diagnostic of params.secretDiagnostics.slice(0, 10)) lines.push(` - ${muted(redactSecrets(diagnostic))}`);
if (params.secretDiagnostics.length > 10) lines.push(` ${muted(`… +${params.secretDiagnostics.length - 10} more`)}`);
if (params.sentinel?.payload) {
emitCheck("Restart sentinel present", "warn");
lines.push(` ${muted(`${summarizeRestartSentinel(params.sentinel.payload)} · ${formatTimeAgo(Date.now() - params.sentinel.payload.ts)}`)}`);
const updateRestartValue = formatUpdateRestartStatusValue(params.sentinel.payload, { formatTimeAgo });
if (updateRestartValue) lines.push(` ${muted(`Update restart: ${updateRestartValue}`)}`);
for (const line of formatUpdateRestartActionLines(params.sentinel.payload)) lines.push(` ${muted(line)}`);
} else emitCheck("Restart sentinel: none", "ok");
const lastErrClean = normalizeOptionalString(params.lastErr) ?? "";
const isTrivialLastErr = lastErrClean.length < 8 || lastErrClean === "}" || lastErrClean === "{";
if (lastErrClean && !isTrivialLastErr) {
lines.push("");
lines.push(muted("Gateway last log line:"));
lines.push(` ${muted(redactSecrets(lastErrClean))}`);
}
if (params.portUsage) {
const benignDualStackLoopback = isDualStackLoopbackGatewayListeners(params.portUsage.listeners, params.port);
const expectedGatewayListeners = isExpectedGatewayListeners(params.portUsage.listeners, params.port);
const portOk = params.portUsage.listeners.length === 0 || expectedGatewayListeners;
emitCheck(`Port ${params.port}`, portOk ? "ok" : "warn");
if (!portOk) {
const gatewayPidCount = countGatewayListenerPids(params.portUsage);
if (gatewayPidCount > 1) lines.push(` ${muted(`${gatewayPidCount} OpenClaw gateway processes appear to be listening on port ${params.port}; stop stale gateway processes before trusting channel health.`)}`);
for (const line of formatPortDiagnostics(params.portUsage)) lines.push(` ${muted(line)}`);
} else if (benignDualStackLoopback) lines.push(` ${muted("Detected dual-stack loopback listeners (127.0.0.1 + ::1) for one gateway process.")}`);
else if (expectedGatewayListeners) lines.push(` ${muted("Detected OpenClaw Gateway listener on the configured port.")}`);
}
{
const backend = params.tailscale.backendState ?? "unknown";
const okBackend = backend === "Running";
const hasDns = Boolean(params.tailscale.dnsName);
emitCheck(params.tailscaleMode === "off" ? `Tailscale exposure: off · daemon ${backend}${params.tailscale.dnsName ? ` · ${params.tailscale.dnsName}` : ""}` : `Tailscale exposure: ${params.tailscaleMode} · daemon ${backend}${params.tailscale.dnsName ? ` · ${params.tailscale.dnsName}` : ""}`, okBackend && (params.tailscaleMode === "off" || hasDns) ? "ok" : "warn");
if (params.tailscale.error) lines.push(` ${muted(`error: ${params.tailscale.error}`)}`);
if (params.tailscale.ips.length > 0) lines.push(` ${muted(`ips: ${params.tailscale.ips.slice(0, 3).join(", ")}${params.tailscale.ips.length > 3 ? "…" : ""}`)}`);
if (params.tailscaleHttpsUrl) lines.push(` ${muted(`https: ${params.tailscaleHttpsUrl}`)}`);
}
if (params.skillStatus) {
const eligible = params.skillStatus.skills.filter((s) => s.eligible).length;
const missing = params.skillStatus.skills.filter((s) => s.eligible && Object.values(s.missing).some((arr) => arr.length)).length;
emitCheck(`Skills: ${eligible} eligible · ${missing} missing · ${params.skillStatus.workspaceDir}`, missing === 0 ? "ok" : "warn");
}
emitCheck(`Plugin compatibility (${params.pluginCompatibility.length || "none"})`, params.pluginCompatibility.length === 0 ? "ok" : "warn");
for (const notice of params.pluginCompatibility.slice(0, 12)) {
const severity = notice.severity === "warn" ? "warn" : "info";
lines.push(` - [${severity}] ${formatPluginCompatibilityNotice(notice)}`);
}
if (params.pluginCompatibility.length > 12) lines.push(` ${muted(`… +${params.pluginCompatibility.length - 12} more`)}`);
if (params.agentStatus) {
const recentSessions = countRecentAgentSessions(params.agentStatus, AGENT_ACTIVITY_SOFT_WARNING_MS);
const shouldWarn = params.agentStatus.totalSessions > 0 && recentSessions === 0;
emitCheck(`Agent activity: ${recentSessions} active in 30m · ${params.agentStatus.totalSessions} sessions`, shouldWarn ? "warn" : "ok");
if (shouldWarn) lines.push(` ${muted("No agent session was updated in the last 30m; if channels received messages, verify inbound dispatch and turn creation.")}`);
}
if (params.deliveryDiagnostics != null) if (isDeliveryDiagnosticsLike(params.deliveryDiagnostics)) {
const received = countDeliveryEvent(params.deliveryDiagnostics, "message.received");
const dispatchStarted = countDeliveryEvent(params.deliveryDiagnostics, "message.dispatch.started");
const dispatchCompleted = countDeliveryEvent(params.deliveryDiagnostics, "message.dispatch.completed");
const turnsCreated = countDeliveryEvent(params.deliveryDiagnostics, "session.turn.created");
const processed = countDeliveryEvent(params.deliveryDiagnostics, "message.processed");
const hasReceivedWithoutDispatch = received > 0 && dispatchStarted === 0 && processed === 0;
const hasDispatchWithoutTurn = dispatchStarted > 0 && turnsCreated === 0 && processed < dispatchStarted;
const hasDispatchGap = dispatchStarted - dispatchCompleted >= 2;
const latestAgeMs = latestDeliveryEventAgeMs(params.deliveryDiagnostics);
emitCheck(`Inbound delivery telemetry: received ${received} · dispatch ${dispatchStarted}/${dispatchCompleted} · turns ${turnsCreated} · processed ${processed}`, hasReceivedWithoutDispatch || hasDispatchWithoutTurn || hasDispatchGap ? "warn" : "ok");
if (latestAgeMs != null) lines.push(` ${muted(`latest delivery event: ${formatTimeAgo(latestAgeMs)}`)}`);
if (hasReceivedWithoutDispatch) lines.push(` ${muted("Messages were received, but no gateway dispatch started; inspect inbound routing and dispatch handoff.")}`);
if (hasDispatchWithoutTurn) lines.push(` ${muted("Gateway dispatch started, but no agent turn was created; inspect reply resolver and session creation.")}`);
if (hasDispatchGap) lines.push(` ${muted("Multiple gateway dispatches have not completed yet; if this persists, inspect stuck sessions or model runs.")}`);
} else emitCheck("Inbound delivery telemetry: unavailable", "warn");
else if (params.gatewayReachable && !params.nodeOnlyGateway) emitCheck("Inbound delivery telemetry: unavailable", "warn");
params.progress.setLabel("Reading logs…");
const logPaths = (() => {
try {
return process.platform === "darwin" ? resolveGatewaySupervisorLogPaths(process.env, { platform: "darwin" }) : resolveGatewayLogPaths(process.env);
} catch {
return null;
}
})();
if (logPaths) {
params.progress.setLabel("Reading logs…");
const restartLogPath = resolveGatewayRestartLogPath(process.env);
const readStderr = process.platform !== "darwin";
const [stderrTail, stdoutTail, restartTail] = await Promise.all([
readStderr ? readFileTailLines(logPaths.stderrPath, 40).catch(() => []) : [],
readFileTailLines(logPaths.stdoutPath, 40).catch(() => []),
readFileTailLines(restartLogPath, 30).catch(() => [])
]);
if (stderrTail.length > 0 || stdoutTail.length > 0) {
lines.push("");
lines.push(muted(`Gateway logs (tail, summarized): ${logPaths.logDir}`));
if (readStderr) {
lines.push(` ${muted(`# stderr: ${logPaths.stderrPath}`)}`);
for (const line of summarizeLogTail(stderrTail, { maxLines: 22 }).map(redactSecrets)) lines.push(` ${muted(line)}`);
}
lines.push(` ${muted(`# stdout: ${logPaths.stdoutPath}`)}`);
for (const line of summarizeLogTail(stdoutTail, { maxLines: 22 }).map(redactSecrets)) lines.push(` ${muted(line)}`);
}
if (restartTail.length > 0) {
lines.push("");
lines.push(muted(`Gateway restart attempts (tail): ${restartLogPath}`));
for (const line of summarizeLogTail(restartTail, { maxLines: 16 }).map(redactSecrets)) lines.push(` ${muted(line)}`);
}
}
params.progress.tick();
if (params.channelsStatus) {
emitCheck(`Channel issues (${params.channelIssues.length || "none"})`, params.channelIssues.length === 0 ? "ok" : "warn");
for (const issue of params.channelIssues.slice(0, 12)) {
const fixText = issue.fix ? ` · fix: ${issue.fix}` : "";
lines.push(` - ${issue.channel}[${issue.accountId}] ${issue.kind}: ${issue.message}${fixText}`);
}
if (params.channelIssues.length > 12) lines.push(` ${muted(`… +${params.channelIssues.length - 12} more`)}`);
} else if (params.nodeOnlyGateway) emitCheck(`Channel issues skipped (node-only mode; query ${params.nodeOnlyGateway.gatewayTarget})`, "ok");
else emitCheck(`Channel issues skipped (gateway ${params.gatewayReachable ? "query failed" : "unreachable"})`, "warn");
const healthErr = (() => {
if (!params.health || typeof params.health !== "object") return "";
const record = params.health;
if (!("error" in record)) return "";
const value = record.error;
if (!value) return "";
if (typeof value === "string") return value;
try {
return JSON.stringify(value, null, 2);
} catch {
return "[unserializable error]";
}
})();
if (healthErr) {
lines.push("");
lines.push(muted("Gateway health:"));
lines.push(` ${muted(redactSecrets(healthErr))}`);
}
lines.push("");
lines.push(muted("Pasteable debug report. Auth tokens redacted."));
lines.push("Troubleshooting: https://docs.openclaw.ai/troubleshooting");
lines.push("");
}
//#endregion
//#region src/commands/status-all/report-lines.ts
/** Builds the complete status-all text report, including overview tables and diagnosis lines. */
async function buildStatusAllReportLines(params) {
const rich = isRich();
const heading = (text) => rich ? theme.heading(text) : text;
const ok = (text) => rich ? theme.success(text) : text;
const warn = (text) => rich ? theme.warn(text) : text;
const fail = (text) => rich ? theme.error(text) : text;
const muted = (text) => rich ? theme.muted(text) : text;
const tableWidth = getTerminalTableWidth();
const lines = [];
lines.push(heading("OpenClaw status --all"));
appendStatusReportSections({
lines,
heading,
sections: [
buildStatusOverviewSection({
width: tableWidth,
renderTable,
rows: params.overviewRows
}),
buildStatusChannelsSection({
width: tableWidth,
renderTable,
rows: params.channels.rows,
channelIssues: params.channelIssues,
ok,
warn,
muted,
accentDim: theme.accentDim,
formatIssueMessage: (message) => message.slice(0, 90)
}),
...buildStatusChannelDetailsSections({
details: params.channels.details,
width: tableWidth,
renderTable,
ok,
warn
}),
buildStatusAgentsSection({
width: tableWidth,
renderTable,
agentStatus: params.agentStatus,
ok,
warn
})
]
});
appendStatusSectionHeading({
lines,
heading,
title: "Diagnosis (read-only)"
});
await appendStatusAllDiagnosis({
lines,
progress: params.progress,
muted,
ok,
warn,
fail,
connectionDetailsForReport: params.connectionDetailsForReport,
...params.diagnosis
});
return lines;
}
//#endregion
//#region src/commands/status-all.ts
/** Runs the full read-only status report and writes it to the runtime logger. */
async function statusAllCommand(runtime, opts) {
await withProgress({
label: "Scanning status --all…",
total: 11
}, async (progress) => {
const overview = await collectStatusScanOverview({
commandName: "status --all",
opts: { timeoutMs: opts?.timeoutMs },
showSecrets: false,
runtime,
useGatewayCallOverridesForChannelsStatus: true,
progress,
labels: {
loadingConfig: "Loading config…",
checkingTailscale: "Checking Tailscale…",
checkingForUpdates: "Checking for updates…",
resolvingAgents: "Scanning agents…",
probingGateway: "Probing gateway…",
queryingChannelStatus: "Querying gateway…",
summarizingChannels: "Summarizing channels…"
}
});
progress.setLabel("Checking services…");
const [daemon, nodeService] = await resolveStatusServiceSummaries();
const nodeOnlyGateway = await resolveNodeOnlyGatewayInfo({
daemon,
node: nodeService
});
progress.tick();
const lines = await buildStatusAllReportLines({
progress,
...await buildStatusAllReportData({
overview,
daemon,
nodeService,
nodeOnlyGateway,
progress,
timeoutMs: opts?.timeoutMs
})
});
progress.setLabel("Rendering…");
runtime.log(lines.join("\n"));
progress.tick();
});
}
//#endregion
export { statusAllCommand };