fix(cli): keep status json startup path lean

This commit is contained in:
Vincent Koc
2026-04-06 12:24:32 +01:00
parent b4a5156bc3
commit 513c8587b8
7 changed files with 87 additions and 43 deletions

View File

@@ -1,7 +1,4 @@
import { hasPotentialConfiguredChannels } from "../channels/config-presence.js";
import { resolveCommandConfigWithSecrets } from "../cli/command-config-resolution.js";
import { getStatusCommandSecretTargetIds } from "../cli/command-secret-targets.js";
import { readBestEffortConfig } from "../config/config.js";
import type { OpenClawConfig } from "../config/types.js";
import type { collectChannelStatusIssues as collectChannelStatusIssuesFn } from "../infra/channels-status-issues.js";
import { resolveOsSummary } from "../infra/os-summary.js";
@@ -24,6 +21,13 @@ let statusUpdateModulePromise: Promise<typeof import("./status.update.js")> | un
let statusScanRuntimeModulePromise: Promise<typeof import("./status.scan.runtime.js")> | undefined;
let gatewayCallModulePromise: Promise<typeof import("../gateway/call.js")> | undefined;
let statusSummaryModulePromise: Promise<typeof import("./status.summary.js")> | undefined;
let configModulePromise: Promise<typeof import("../config/config.js")> | undefined;
let commandConfigResolutionModulePromise:
| Promise<typeof import("../cli/command-config-resolution.js")>
| undefined;
let commandSecretTargetsModulePromise:
| Promise<typeof import("../cli/command-secret-targets.js")>
| undefined;
function loadStatusScanDepsRuntimeModule() {
statusScanDepsRuntimeModulePromise ??= import("./status.scan.deps.runtime.js");
@@ -55,6 +59,21 @@ function loadStatusSummaryModule() {
return statusSummaryModulePromise;
}
function loadConfigModule() {
configModulePromise ??= import("../config/config.js");
return configModulePromise;
}
function loadCommandConfigResolutionModule() {
commandConfigResolutionModulePromise ??= import("../cli/command-config-resolution.js");
return commandConfigResolutionModulePromise;
}
function loadCommandSecretTargetsModule() {
commandSecretTargetsModulePromise ??= import("../cli/command-secret-targets.js");
return commandSecretTargetsModulePromise;
}
async function resolveStatusChannelsStatus(params: {
cfg: OpenClawConfig;
gatewayReachable: boolean;
@@ -142,12 +161,14 @@ export async function collectStatusScanOverview(params: {
} = await loadStatusScanCommandConfig({
commandName: params.commandName,
allowMissingConfigFastPath: params.allowMissingConfigFastPath,
readBestEffortConfig,
readBestEffortConfig: async () => (await loadConfigModule()).readBestEffortConfig(),
resolveConfig: async (loadedConfig) =>
await resolveCommandConfigWithSecrets({
await (
await loadCommandConfigResolutionModule()
).resolveCommandConfigWithSecrets({
config: loadedConfig,
commandName: params.commandName,
targetIds: getStatusCommandSecretTargetIds(),
targetIds: (await loadCommandSecretTargetsModule()).getStatusCommandSecretTargetIds(),
mode: "read_only_status",
...(params.runtime ? { runtime: params.runtime } : {}),
}),

View File

@@ -12,7 +12,7 @@ vi.mock("../gateway/connection-details.js", () => ({
buildGatewayConnectionDetailsWithResolvers: mocks.buildGatewayConnectionDetailsWithResolvers,
}));
vi.mock("../gateway/probe-auth.js", () => ({
vi.mock("../gateway/probe-target.js", () => ({
resolveGatewayProbeTarget: mocks.resolveGatewayProbeTarget,
}));

View File

@@ -2,19 +2,25 @@ import { existsSync } from "node:fs";
import type { OpenClawConfig } from "../config/types.js";
import { buildGatewayConnectionDetailsWithResolvers } from "../gateway/connection-details.js";
import { normalizeControlUiBasePath } from "../gateway/control-ui-shared.js";
import { resolveGatewayProbeTarget } from "../gateway/probe-auth.js";
import { probeGateway } from "../gateway/probe.js";
import { resolveGatewayProbeTarget } from "../gateway/probe-target.js";
import type { probeGateway as probeGatewayFn } from "../gateway/probe.js";
import type { MemoryProviderStatus } from "../memory-host-sdk/engine-storage.js";
import { pickGatewaySelfPresence } from "./gateway-presence.js";
export { pickGatewaySelfPresence } from "./gateway-presence.js";
let gatewayProbeModulePromise: Promise<typeof import("./status.gateway-probe.js")> | undefined;
let probeGatewayModulePromise: Promise<typeof import("../gateway/probe.js")> | undefined;
function loadGatewayProbeModule() {
gatewayProbeModulePromise ??= import("./status.gateway-probe.js");
return gatewayProbeModulePromise;
}
function loadProbeGatewayModule() {
probeGatewayModulePromise ??= import("../gateway/probe.js");
return probeGatewayModulePromise;
}
export type MemoryStatusSnapshot = MemoryProviderStatus & {
agentId: string;
};
@@ -34,7 +40,7 @@ export type GatewayProbeSnapshot = {
password?: string;
};
gatewayProbeAuthWarning?: string;
gatewayProbe: Awaited<ReturnType<typeof probeGateway>> | null;
gatewayProbe: Awaited<ReturnType<typeof probeGatewayFn>> | null;
gatewayReachable: boolean;
gatewaySelf: ReturnType<typeof pickGatewaySelfPresence>;
gatewayCallOverrides?: {
@@ -96,12 +102,16 @@ export async function resolveGatewayProbeSnapshot(params: {
: { auth: {}, warning: undefined };
let gatewayProbeAuthWarning = gatewayProbeAuthResolution.warning;
const gatewayProbe = shouldProbe
? await probeGateway({
url: gatewayConnection.url,
auth: gatewayProbeAuthResolution.auth,
timeoutMs: Math.min(params.opts.all ? 5000 : 2500, params.opts.timeoutMs ?? 10_000),
detailLevel: params.opts.detailLevel ?? "presence",
}).catch(() => null)
? await loadProbeGatewayModule()
.then(({ probeGateway }) =>
probeGateway({
url: gatewayConnection.url,
auth: gatewayProbeAuthResolution.auth,
timeoutMs: Math.min(params.opts.all ? 5000 : 2500, params.opts.timeoutMs ?? 10_000),
detailLevel: params.opts.detailLevel ?? "presence",
}),
)
.catch(() => null)
: null;
if (
(params.opts.mergeAuthWarningIntoProbeError ?? true) &&

View File

@@ -15,6 +15,7 @@ export type StatusScanSharedMocks = {
getStatusSummary: UnknownMock;
getMemorySearchManager: UnknownMock;
buildGatewayConnectionDetails: UnknownMock;
resolveGatewayProbeTarget: UnknownMock;
probeGateway: UnknownMock;
resolveGatewayProbeAuthResolution: UnknownMock;
ensurePluginRegistryLoaded: UnknownMock;
@@ -32,6 +33,11 @@ export function createStatusScanSharedMocks(configPathLabel: string): StatusScan
getStatusSummary: vi.fn(),
getMemorySearchManager: vi.fn(),
buildGatewayConnectionDetails: vi.fn(),
resolveGatewayProbeTarget: vi.fn(() => ({
mode: "local",
gatewayMode: "local",
remoteUrlMissing: false,
})),
probeGateway: vi.fn(),
resolveGatewayProbeAuthResolution: vi.fn(),
ensurePluginRegistryLoaded: vi.fn(),
@@ -224,6 +230,9 @@ export async function loadStatusScanModuleForTest(
vi.doMock("../gateway/probe.js", () => ({
probeGateway: mocks.probeGateway,
}));
vi.doMock("../gateway/probe-target.js", () => ({
resolveGatewayProbeTarget: mocks.resolveGatewayProbeTarget,
}));
vi.doMock("./status.gateway-probe.js", () => createStatusGatewayProbeModuleMock(mocks));
vi.doMock("../gateway/connection-details.js", () => ({
buildGatewayConnectionDetails: mocks.buildGatewayConnectionDetails,

View File

@@ -1,16 +1,11 @@
import type { OpenClawConfig } from "../config/config.js";
import { resolveGatewayCredentialsWithSecretInputs } from "./call.js";
import {
type ExplicitGatewayAuth,
isGatewaySecretRefUnavailableError,
resolveGatewayProbeCredentialsFromConfig,
} from "./credentials.js";
export type GatewayProbeTargetResolution = {
gatewayMode: "local" | "remote";
mode: "local" | "remote";
remoteUrlMissing: boolean;
};
export { resolveGatewayProbeTarget } from "./probe-target.js";
export type { GatewayProbeTargetResolution } from "./probe-target.js";
function buildGatewayProbeCredentialPolicy(params: {
cfg: OpenClawConfig;
@@ -53,18 +48,6 @@ function resolveGatewayProbeWarning(error: unknown): string | undefined {
return buildUnresolvedProbeAuthWarning(error.path);
}
export function resolveGatewayProbeTarget(cfg: OpenClawConfig): GatewayProbeTargetResolution {
const gatewayMode = cfg.gateway?.mode === "remote" ? "remote" : "local";
const remoteUrlRaw =
typeof cfg.gateway?.remote?.url === "string" ? cfg.gateway.remote.url.trim() : "";
const remoteUrlMissing = gatewayMode === "remote" && !remoteUrlRaw;
return {
gatewayMode,
mode: gatewayMode === "remote" && !remoteUrlMissing ? "remote" : "local",
remoteUrlMissing,
};
}
export function resolveGatewayProbeAuth(params: {
cfg: OpenClawConfig;
mode: "local" | "remote";
@@ -81,13 +64,15 @@ export async function resolveGatewayProbeAuthWithSecretInputs(params: {
explicitAuth?: ExplicitGatewayAuth;
}): Promise<{ token?: string; password?: string }> {
const policy = buildGatewayProbeCredentialPolicy(params);
return await resolveGatewayCredentialsWithSecretInputs({
config: policy.config,
env: policy.env,
explicitAuth: policy.explicitAuth,
modeOverride: policy.modeOverride,
remoteTokenFallback: policy.remoteTokenFallback,
});
return await import("./call.js").then(({ resolveGatewayCredentialsWithSecretInputs }) =>
resolveGatewayCredentialsWithSecretInputs({
config: policy.config,
env: policy.env,
explicitAuth: policy.explicitAuth,
modeOverride: policy.modeOverride,
remoteTokenFallback: policy.remoteTokenFallback,
}),
);
}
export async function resolveGatewayProbeAuthSafeWithSecretInputs(params: {

View File

@@ -0,0 +1,19 @@
import type { OpenClawConfig } from "../config/config.js";
export type GatewayProbeTargetResolution = {
gatewayMode: "local" | "remote";
mode: "local" | "remote";
remoteUrlMissing: boolean;
};
export function resolveGatewayProbeTarget(cfg: OpenClawConfig): GatewayProbeTargetResolution {
const gatewayMode = cfg.gateway?.mode === "remote" ? "remote" : "local";
const remoteUrlRaw =
typeof cfg.gateway?.remote?.url === "string" ? cfg.gateway.remote.url.trim() : "";
const remoteUrlMissing = gatewayMode === "remote" && !remoteUrlRaw;
return {
gatewayMode,
mode: gatewayMode === "remote" && !remoteUrlMissing ? "remote" : "local",
remoteUrlMissing,
};
}

View File

@@ -206,7 +206,7 @@ function normalizeLookupResults(results: LookupResult): readonly LookupAddress[]
if (Array.isArray(results)) {
return results;
}
return [results as LookupAddress];
return [results];
}
export function createPinnedLookup(params: {