mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-16 11:41:08 +00:00
322 lines
8.9 KiB
TypeScript
322 lines
8.9 KiB
TypeScript
import { withProgress } from "../cli/progress.js";
|
|
import { type RuntimeEnv } from "../runtime.js";
|
|
import { runStatusJsonCommand } from "./status-json-command.ts";
|
|
import { buildStatusOverviewSurfaceFromScan } from "./status-overview-surface.ts";
|
|
import {
|
|
loadStatusProviderUsageModule,
|
|
resolveStatusGatewayHealth,
|
|
resolveStatusSecurityAudit,
|
|
resolveStatusRuntimeSnapshot,
|
|
resolveStatusUsageSummary,
|
|
} from "./status-runtime-shared.ts";
|
|
import { buildStatusCommandReportData } from "./status.command-report-data.ts";
|
|
import { buildStatusCommandReportLines } from "./status.command-report.ts";
|
|
import { logGatewayConnectionDetails } from "./status.gateway-connection.ts";
|
|
|
|
let statusScanModulePromise: Promise<typeof import("./status.scan.js")> | undefined;
|
|
let statusScanFastJsonModulePromise:
|
|
| Promise<typeof import("./status.scan.fast-json.js")>
|
|
| undefined;
|
|
let statusAllModulePromise: Promise<typeof import("./status-all.js")> | undefined;
|
|
let statusCommandTextRuntimePromise:
|
|
| Promise<typeof import("./status.command.text-runtime.js")>
|
|
| undefined;
|
|
let statusGatewayConnectionRuntimePromise:
|
|
| Promise<typeof import("./status.gateway-connection.runtime.js")>
|
|
| undefined;
|
|
let statusNodeModeModulePromise: Promise<typeof import("./status.node-mode.js")> | undefined;
|
|
|
|
function loadStatusScanModule() {
|
|
statusScanModulePromise ??= import("./status.scan.js");
|
|
return statusScanModulePromise;
|
|
}
|
|
|
|
function loadStatusScanFastJsonModule() {
|
|
statusScanFastJsonModulePromise ??= import("./status.scan.fast-json.js");
|
|
return statusScanFastJsonModulePromise;
|
|
}
|
|
|
|
function loadStatusAllModule() {
|
|
statusAllModulePromise ??= import("./status-all.js");
|
|
return statusAllModulePromise;
|
|
}
|
|
|
|
function loadStatusCommandTextRuntime() {
|
|
statusCommandTextRuntimePromise ??= import("./status.command.text-runtime.js");
|
|
return statusCommandTextRuntimePromise;
|
|
}
|
|
|
|
function loadStatusGatewayConnectionRuntime() {
|
|
statusGatewayConnectionRuntimePromise ??= import("./status.gateway-connection.runtime.js");
|
|
return statusGatewayConnectionRuntimePromise;
|
|
}
|
|
|
|
function loadStatusNodeModeModule() {
|
|
statusNodeModeModulePromise ??= import("./status.node-mode.js");
|
|
return statusNodeModeModulePromise;
|
|
}
|
|
|
|
function resolvePairingRecoveryContext(params: {
|
|
error?: string | null;
|
|
closeReason?: string | null;
|
|
}): { requestId: string | null } | null {
|
|
const sanitizeRequestId = (value: string): string | null => {
|
|
const trimmed = value.trim();
|
|
if (!trimmed) {
|
|
return null;
|
|
}
|
|
// Keep CLI guidance injection-safe: allow only compact id characters.
|
|
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(trimmed)) {
|
|
return null;
|
|
}
|
|
return trimmed;
|
|
};
|
|
const source = [params.error, params.closeReason]
|
|
.filter((part) => typeof part === "string" && part.trim().length > 0)
|
|
.join(" ");
|
|
if (!source || !/pairing required/i.test(source)) {
|
|
return null;
|
|
}
|
|
const requestIdMatch = source.match(/requestId:\s*([^\s)]+)/i);
|
|
const requestId =
|
|
requestIdMatch && requestIdMatch[1] ? sanitizeRequestId(requestIdMatch[1]) : null;
|
|
return { requestId: requestId || null };
|
|
}
|
|
|
|
export async function statusCommand(
|
|
opts: {
|
|
json?: boolean;
|
|
deep?: boolean;
|
|
usage?: boolean;
|
|
timeoutMs?: number;
|
|
verbose?: boolean;
|
|
all?: boolean;
|
|
},
|
|
runtime: RuntimeEnv,
|
|
) {
|
|
if (opts.all && !opts.json) {
|
|
await loadStatusAllModule().then(({ statusAllCommand }) =>
|
|
statusAllCommand(runtime, { timeoutMs: opts.timeoutMs }),
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (opts.json) {
|
|
await runStatusJsonCommand({
|
|
opts,
|
|
runtime,
|
|
includeSecurityAudit: opts.all === true,
|
|
includePluginCompatibility: true,
|
|
suppressHealthErrors: true,
|
|
scanStatusJsonFast: async (scanOpts, runtimeForScan) =>
|
|
await loadStatusScanFastJsonModule().then(({ scanStatusJsonFast }) =>
|
|
scanStatusJsonFast(scanOpts, runtimeForScan),
|
|
),
|
|
});
|
|
return;
|
|
}
|
|
|
|
const scan = await loadStatusScanModule().then(({ scanStatus }) =>
|
|
scanStatus({ json: false, timeoutMs: opts.timeoutMs, all: opts.all }, runtime),
|
|
);
|
|
|
|
const {
|
|
cfg,
|
|
osSummary,
|
|
tailscaleMode,
|
|
tailscaleDns,
|
|
tailscaleHttpsUrl,
|
|
update,
|
|
gatewayConnection,
|
|
remoteUrlMissing,
|
|
gatewayMode,
|
|
gatewayProbeAuth,
|
|
gatewayProbeAuthWarning,
|
|
gatewayProbe,
|
|
gatewayReachable,
|
|
gatewaySelf,
|
|
channelIssues,
|
|
agentStatus,
|
|
channels,
|
|
summary,
|
|
secretDiagnostics,
|
|
memory,
|
|
memoryPlugin,
|
|
pluginCompatibility,
|
|
} = scan;
|
|
|
|
const {
|
|
securityAudit,
|
|
usage,
|
|
health,
|
|
lastHeartbeat,
|
|
gatewayService: daemon,
|
|
nodeService: nodeDaemon,
|
|
} = await resolveStatusRuntimeSnapshot({
|
|
config: scan.cfg,
|
|
sourceConfig: scan.sourceConfig,
|
|
timeoutMs: opts.timeoutMs,
|
|
usage: opts.usage,
|
|
deep: opts.deep,
|
|
gatewayReachable,
|
|
includeSecurityAudit: true,
|
|
resolveSecurityAudit: async (input) =>
|
|
await withProgress(
|
|
{
|
|
label: "Running security audit…",
|
|
indeterminate: true,
|
|
enabled: true,
|
|
},
|
|
async () => await resolveStatusSecurityAudit(input),
|
|
),
|
|
resolveUsage: async (timeoutMs) =>
|
|
await withProgress(
|
|
{
|
|
label: "Fetching usage snapshot…",
|
|
indeterminate: true,
|
|
enabled: opts.json !== true,
|
|
},
|
|
async () => await resolveStatusUsageSummary(timeoutMs),
|
|
),
|
|
resolveHealth: async (input) =>
|
|
await withProgress(
|
|
{
|
|
label: "Checking gateway health…",
|
|
indeterminate: true,
|
|
enabled: opts.json !== true,
|
|
},
|
|
async () => await resolveStatusGatewayHealth(input),
|
|
),
|
|
});
|
|
|
|
const rich = true;
|
|
const {
|
|
buildStatusUpdateSurface,
|
|
formatCliCommand,
|
|
formatHealthChannelLines,
|
|
formatKTokens,
|
|
formatPromptCacheCompact,
|
|
formatPluginCompatibilityNotice,
|
|
formatTimeAgo,
|
|
formatTokensCompact,
|
|
formatUpdateAvailableHint,
|
|
getTerminalTableWidth,
|
|
info,
|
|
renderTable,
|
|
resolveMemoryCacheSummary,
|
|
resolveMemoryFtsState,
|
|
resolveMemoryVectorState,
|
|
shortenText,
|
|
theme,
|
|
} = await loadStatusCommandTextRuntime();
|
|
const muted = (value: string) => (rich ? theme.muted(value) : value);
|
|
const ok = (value: string) => (rich ? theme.success(value) : value);
|
|
const warn = (value: string) => (rich ? theme.warn(value) : value);
|
|
const updateSurface = buildStatusUpdateSurface({
|
|
updateConfigChannel: cfg.update?.channel,
|
|
update,
|
|
});
|
|
|
|
if (opts.verbose) {
|
|
const { buildGatewayConnectionDetails } = await loadStatusGatewayConnectionRuntime();
|
|
const details = buildGatewayConnectionDetails({ config: scan.cfg });
|
|
logGatewayConnectionDetails({
|
|
runtime,
|
|
info,
|
|
message: details.message,
|
|
trailingBlankLine: true,
|
|
});
|
|
}
|
|
|
|
const tableWidth = getTerminalTableWidth();
|
|
|
|
if (secretDiagnostics.length > 0) {
|
|
runtime.log(theme.warn("Secret diagnostics:"));
|
|
for (const entry of secretDiagnostics) {
|
|
runtime.log(`- ${entry}`);
|
|
}
|
|
runtime.log("");
|
|
}
|
|
|
|
const nodeOnlyGateway = await loadStatusNodeModeModule().then(({ resolveNodeOnlyGatewayInfo }) =>
|
|
resolveNodeOnlyGatewayInfo({
|
|
daemon,
|
|
node: nodeDaemon,
|
|
}),
|
|
);
|
|
const pairingRecovery = resolvePairingRecoveryContext({
|
|
error: gatewayProbe?.error ?? null,
|
|
closeReason: gatewayProbe?.close?.reason ?? null,
|
|
});
|
|
|
|
const usageLines = usage
|
|
? await loadStatusProviderUsageModule().then(({ formatUsageReportLines }) =>
|
|
formatUsageReportLines(usage),
|
|
)
|
|
: undefined;
|
|
const overviewSurface = buildStatusOverviewSurfaceFromScan({
|
|
scan: {
|
|
cfg,
|
|
update,
|
|
tailscaleMode,
|
|
tailscaleDns,
|
|
tailscaleHttpsUrl,
|
|
gatewayMode,
|
|
remoteUrlMissing,
|
|
gatewayConnection,
|
|
gatewayReachable,
|
|
gatewayProbe,
|
|
gatewayProbeAuth,
|
|
gatewayProbeAuthWarning,
|
|
gatewaySelf,
|
|
},
|
|
gatewayService: daemon,
|
|
nodeService: nodeDaemon,
|
|
nodeOnlyGateway,
|
|
});
|
|
const lines = await buildStatusCommandReportLines(
|
|
await buildStatusCommandReportData({
|
|
opts,
|
|
surface: overviewSurface,
|
|
osSummary,
|
|
summary,
|
|
securityAudit,
|
|
health,
|
|
usageLines,
|
|
lastHeartbeat,
|
|
agentStatus,
|
|
channels,
|
|
channelIssues,
|
|
memory,
|
|
memoryPlugin,
|
|
pluginCompatibility,
|
|
pairingRecovery,
|
|
tableWidth,
|
|
ok,
|
|
warn,
|
|
muted,
|
|
shortenText,
|
|
formatCliCommand,
|
|
formatTimeAgo,
|
|
formatKTokens,
|
|
formatTokensCompact,
|
|
formatPromptCacheCompact,
|
|
formatHealthChannelLines,
|
|
formatPluginCompatibilityNotice,
|
|
formatUpdateAvailableHint,
|
|
resolveMemoryVectorState,
|
|
resolveMemoryFtsState,
|
|
resolveMemoryCacheSummary,
|
|
accentDim: theme.accentDim,
|
|
theme,
|
|
renderTable,
|
|
updateValue: updateSurface.updateAvailable
|
|
? warn(`available · ${updateSurface.updateLine}`)
|
|
: updateSurface.updateLine,
|
|
}),
|
|
);
|
|
for (const line of lines) {
|
|
runtime.log(line);
|
|
}
|
|
}
|