diff --git a/src/cli/daemon-cli/install.ts b/src/cli/daemon-cli/install.ts index 864f0a93ff0..449fd28ab40 100644 --- a/src/cli/daemon-cli/install.ts +++ b/src/cli/daemon-cli/install.ts @@ -4,7 +4,7 @@ import { isGatewayDaemonRuntime, } from "../../commands/daemon-runtime.js"; import { resolveGatewayInstallToken } from "../../commands/gateway-install-token.js"; -import { loadConfig, resolveGatewayPort } from "../../config/config.js"; +import { readBestEffortConfig, resolveGatewayPort } from "../../config/config.js"; import { resolveIsNixMode } from "../../config/paths.js"; import { resolveGatewayService } from "../../daemon/service.js"; import { defaultRuntime } from "../../runtime.js"; @@ -26,7 +26,7 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) { return; } - const cfg = loadConfig(); + const cfg = await readBestEffortConfig(); const portOverride = parsePort(opts.port); if (opts.port !== undefined && portOverride === null) { fail("Invalid port"); diff --git a/src/cli/daemon-cli/lifecycle-core.ts b/src/cli/daemon-cli/lifecycle-core.ts index 6b8c7ee684c..2b4250e9c28 100644 --- a/src/cli/daemon-cli/lifecycle-core.ts +++ b/src/cli/daemon-cli/lifecycle-core.ts @@ -1,5 +1,5 @@ import type { Writable } from "node:stream"; -import { loadConfig } from "../../config/config.js"; +import { readBestEffortConfig } from "../../config/config.js"; import { resolveIsNixMode } from "../../config/paths.js"; import { checkTokenDrift } from "../../daemon/service-audit.js"; import type { GatewayService } from "../../daemon/service.js"; @@ -283,7 +283,7 @@ export async function runServiceRestart(params: { try { const command = await params.service.readCommand(process.env); const serviceToken = command?.environment?.OPENCLAW_GATEWAY_TOKEN; - const cfg = loadConfig(); + const cfg = await readBestEffortConfig(); const configToken = resolveGatewayCredentialsFromConfig({ cfg, env: process.env, diff --git a/src/cli/daemon-cli/lifecycle.ts b/src/cli/daemon-cli/lifecycle.ts index 9c23011d2df..5088316a021 100644 --- a/src/cli/daemon-cli/lifecycle.ts +++ b/src/cli/daemon-cli/lifecycle.ts @@ -1,4 +1,4 @@ -import { loadConfig, resolveGatewayPort } from "../../config/config.js"; +import { readBestEffortConfig, resolveGatewayPort } from "../../config/config.js"; import { resolveGatewayService } from "../../daemon/service.js"; import { defaultRuntime } from "../../runtime.js"; import { theme } from "../../terminal/theme.js"; @@ -32,7 +32,7 @@ async function resolveGatewayRestartPort() { } as NodeJS.ProcessEnv; const portFromArgs = parsePortFromArgs(command?.programArguments); - return portFromArgs ?? resolveGatewayPort(loadConfig(), mergedEnv); + return portFromArgs ?? resolveGatewayPort(await readBestEffortConfig(), mergedEnv); } export async function runDaemonUninstall(opts: DaemonLifecycleOptions = {}) { @@ -70,8 +70,8 @@ export async function runDaemonStop(opts: DaemonLifecycleOptions = {}) { export async function runDaemonRestart(opts: DaemonLifecycleOptions = {}): Promise { const json = Boolean(opts.json); const service = resolveGatewayService(); - const restartPort = await resolveGatewayRestartPort().catch(() => - resolveGatewayPort(loadConfig(), process.env), + const restartPort = await resolveGatewayRestartPort().catch(async () => + resolveGatewayPort(await readBestEffortConfig(), process.env), ); const restartWaitMs = POST_RESTART_HEALTH_ATTEMPTS * POST_RESTART_HEALTH_DELAY_MS; const restartWaitSeconds = Math.round(restartWaitMs / 1000); diff --git a/src/cli/gateway-cli/call.ts b/src/cli/gateway-cli/call.ts index 704a3ee3c8f..da321a8cd36 100644 --- a/src/cli/gateway-cli/call.ts +++ b/src/cli/gateway-cli/call.ts @@ -1,9 +1,11 @@ import type { Command } from "commander"; +import type { OpenClawConfig } from "../../config/config.js"; import { callGateway } from "../../gateway/call.js"; import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../../utils/message-channel.js"; import { withProgress } from "../progress.js"; export type GatewayRpcOpts = { + config?: OpenClawConfig; url?: string; token?: string; password?: string; @@ -30,6 +32,7 @@ export const callGatewayCli = async (method: string, opts: GatewayRpcOpts, param }, async () => await callGateway({ + config: opts.config, url: opts.url, token: opts.token, password: opts.password, diff --git a/src/cli/gateway-cli/register.ts b/src/cli/gateway-cli/register.ts index 29a06a845f1..d19e53d10b9 100644 --- a/src/cli/gateway-cli/register.ts +++ b/src/cli/gateway-cli/register.ts @@ -1,7 +1,7 @@ import type { Command } from "commander"; import { gatewayStatusCommand } from "../../commands/gateway-status.js"; import { formatHealthChannelLines, type HealthSummary } from "../../commands/health.js"; -import { loadConfig } from "../../config/config.js"; +import { readBestEffortConfig } from "../../config/config.js"; import { discoverGatewayBeacons } from "../../infra/bonjour-discovery.js"; import type { CostUsageSummary } from "../../infra/session-cost-usage.js"; import { resolveWideAreaDiscoveryDomain } from "../../infra/widearea-dns.js"; @@ -120,8 +120,9 @@ export function registerGatewayCli(program: Command) { .action(async (method, opts, command) => { await runGatewayCommand(async () => { const rpcOpts = resolveGatewayRpcOptions(opts, command); + const config = await readBestEffortConfig(); const params = JSON.parse(String(opts.params ?? "{}")); - const result = await callGatewayCli(method, rpcOpts, params); + const result = await callGatewayCli(method, { ...rpcOpts, config }, params); if (rpcOpts.json) { defaultRuntime.log(JSON.stringify(result, null, 2)); return; @@ -144,7 +145,8 @@ export function registerGatewayCli(program: Command) { await runGatewayCommand(async () => { const rpcOpts = resolveGatewayRpcOptions(opts, command); const days = parseDaysOption(opts.days); - const result = await callGatewayCli("usage.cost", rpcOpts, { days }); + const config = await readBestEffortConfig(); + const result = await callGatewayCli("usage.cost", { ...rpcOpts, config }, { days }); if (rpcOpts.json) { defaultRuntime.log(JSON.stringify(result, null, 2)); return; @@ -165,7 +167,8 @@ export function registerGatewayCli(program: Command) { .action(async (opts, command) => { await runGatewayCommand(async () => { const rpcOpts = resolveGatewayRpcOptions(opts, command); - const result = await callGatewayCli("health", rpcOpts); + const config = await readBestEffortConfig(); + const result = await callGatewayCli("health", { ...rpcOpts, config }); if (rpcOpts.json) { defaultRuntime.log(JSON.stringify(result, null, 2)); return; @@ -211,7 +214,7 @@ export function registerGatewayCli(program: Command) { .option("--json", "Output JSON", false) .action(async (opts: GatewayDiscoverOpts) => { await runGatewayCommand(async () => { - const cfg = loadConfig(); + const cfg = await readBestEffortConfig(); const wideAreaDomain = resolveWideAreaDiscoveryDomain({ configDomain: cfg.discovery?.wideArea?.domain, }); diff --git a/src/cli/program/register.subclis.ts b/src/cli/program/register.subclis.ts index fc044dbcd92..ac037fa355b 100644 --- a/src/cli/program/register.subclis.ts +++ b/src/cli/program/register.subclis.ts @@ -30,7 +30,7 @@ const shouldEagerRegisterSubcommands = (_argv: string[]) => { const loadConfig = async (): Promise => { const mod = await import("../../config/config.js"); - return mod.loadConfig(); + return await mod.readBestEffortConfig(); }; // Note for humans and agents: diff --git a/src/cli/run-main.ts b/src/cli/run-main.ts index b304f213bfb..abf10a2930c 100644 --- a/src/cli/run-main.ts +++ b/src/cli/run-main.ts @@ -126,8 +126,8 @@ export async function runCli(argv: string[] = process.argv) { if (!shouldSkipPluginRegistration) { // Register plugin CLI commands before parsing const { registerPluginCliCommands } = await import("../plugins/cli.js"); - const { loadConfig } = await import("../config/config.js"); - registerPluginCliCommands(program, loadConfig()); + const { readBestEffortConfig } = await import("../config/config.js"); + registerPluginCliCommands(program, await readBestEffortConfig()); } await program.parseAsync(parseArgv); diff --git a/src/commands/gateway-status.ts b/src/commands/gateway-status.ts index 2b71558202f..4ac54eca0c4 100644 --- a/src/commands/gateway-status.ts +++ b/src/commands/gateway-status.ts @@ -1,5 +1,5 @@ import { withProgress } from "../cli/progress.js"; -import { loadConfig, resolveGatewayPort } from "../config/config.js"; +import { readBestEffortConfig, resolveGatewayPort } from "../config/config.js"; import { probeGateway } from "../gateway/probe.js"; import { discoverGatewayBeacons } from "../infra/bonjour-discovery.js"; import { resolveSshConfig } from "../infra/ssh-config.js"; @@ -35,7 +35,7 @@ export async function gatewayStatusCommand( runtime: RuntimeEnv, ) { const startedAt = Date.now(); - const cfg = loadConfig(); + const cfg = await readBestEffortConfig(); const rich = isRich() && opts.json !== true; const overallTimeoutMs = parseTimeoutMs(opts.timeout, 3000); const wideAreaDomain = resolveWideAreaDiscoveryDomain({ diff --git a/src/commands/health.ts b/src/commands/health.ts index 0280c5dab67..56705c96270 100644 --- a/src/commands/health.ts +++ b/src/commands/health.ts @@ -4,7 +4,7 @@ import { getChannelPlugin, listChannelPlugins } from "../channels/plugins/index. import type { ChannelAccountSnapshot } from "../channels/plugins/types.js"; import { withProgress } from "../cli/progress.js"; import type { OpenClawConfig } from "../config/config.js"; -import { loadConfig } from "../config/config.js"; +import { loadConfig, readBestEffortConfig } from "../config/config.js"; import { loadSessionStore, resolveStorePath } from "../config/sessions.js"; import { buildGatewayConnectionDetails, callGateway } from "../gateway/call.js"; import { info } from "../globals.js"; @@ -526,7 +526,7 @@ export async function healthCommand( opts: { json?: boolean; timeoutMs?: number; verbose?: boolean; config?: OpenClawConfig }, runtime: RuntimeEnv, ) { - const cfg = opts.config ?? loadConfig(); + const cfg = opts.config ?? (await readBestEffortConfig()); // Always query the running gateway; do not open a direct Baileys socket here. const summary = await withProgress( { diff --git a/src/commands/status-all.ts b/src/commands/status-all.ts index 285e0884a43..5d9b86a45b5 100644 --- a/src/commands/status-all.ts +++ b/src/commands/status-all.ts @@ -3,7 +3,11 @@ import { formatCliCommand } from "../cli/command-format.js"; import { resolveCommandSecretRefsViaGateway } from "../cli/command-secret-gateway.js"; import { getStatusCommandSecretTargetIds } from "../cli/command-secret-targets.js"; import { withProgress } from "../cli/progress.js"; -import { loadConfig, readConfigFileSnapshot, resolveGatewayPort } from "../config/config.js"; +import { + readBestEffortConfig, + readConfigFileSnapshot, + resolveGatewayPort, +} from "../config/config.js"; import { readLastGatewayErrorLine } from "../daemon/diagnostics.js"; import { resolveNodeService } from "../daemon/node-service.js"; import type { GatewayService } from "../daemon/service.js"; @@ -38,7 +42,7 @@ export async function statusAllCommand( ): Promise { await withProgress({ label: "Scanning status --all…", total: 11 }, async (progress) => { progress.setLabel("Loading config…"); - const loadedRaw = loadConfig(); + const loadedRaw = await readBestEffortConfig(); const { resolvedConfig: cfg } = await resolveCommandSecretRefsViaGateway({ config: loadedRaw, commandName: "status --all", diff --git a/src/commands/status.agent-local.ts b/src/commands/status.agent-local.ts index b7bb8bdf127..5c57036eb97 100644 --- a/src/commands/status.agent-local.ts +++ b/src/commands/status.agent-local.ts @@ -1,6 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { resolveAgentWorkspaceDir } from "../agents/agent-scope.js"; +import type { OpenClawConfig } from "../config/config.js"; import { loadConfig } from "../config/config.js"; import { loadSessionStore, resolveStorePath } from "../config/sessions.js"; import { listAgentsForGateway } from "../gateway/session-utils.js"; @@ -16,6 +17,13 @@ export type AgentLocalStatus = { lastActiveAgeMs: number | null; }; +type AgentLocalStatusesResult = { + defaultId: string; + agents: AgentLocalStatus[]; + totalSessions: number; + bootstrapPendingCount: number; +}; + async function fileExists(p: string): Promise { try { await fs.access(p); @@ -25,13 +33,9 @@ async function fileExists(p: string): Promise { } } -export async function getAgentLocalStatuses(): Promise<{ - defaultId: string; - agents: AgentLocalStatus[]; - totalSessions: number; - bootstrapPendingCount: number; -}> { - const cfg = loadConfig(); +export async function getAgentLocalStatuses( + cfg: OpenClawConfig = loadConfig(), +): Promise { const agentList = listAgentsForGateway(cfg); const now = Date.now(); diff --git a/src/commands/status.command.ts b/src/commands/status.command.ts index 688ddd726dd..b6bdf33d92e 100644 --- a/src/commands/status.command.ts +++ b/src/commands/status.command.ts @@ -153,6 +153,7 @@ export async function statusCommand( method: "health", params: { probe: true }, timeoutMs: opts.timeoutMs, + config: scan.cfg, }), ) : undefined; @@ -162,6 +163,7 @@ export async function statusCommand( method: "last-heartbeat", params: {}, timeoutMs: opts.timeoutMs, + config: scan.cfg, }).catch(() => null) : null; @@ -219,7 +221,7 @@ export async function statusCommand( const warn = (value: string) => (rich ? theme.warn(value) : value); if (opts.verbose) { - const details = buildGatewayConnectionDetails(); + const details = buildGatewayConnectionDetails({ config: scan.cfg }); runtime.log(info("Gateway connection:")); for (const line of details.message.split("\n")) { runtime.log(` ${line}`); diff --git a/src/commands/status.scan.ts b/src/commands/status.scan.ts index bce208af0cc..38e15e6417b 100644 --- a/src/commands/status.scan.ts +++ b/src/commands/status.scan.ts @@ -1,7 +1,8 @@ import { resolveCommandSecretRefsViaGateway } from "../cli/command-secret-gateway.js"; import { getStatusCommandSecretTargetIds } from "../cli/command-secret-targets.js"; import { withProgress } from "../cli/progress.js"; -import { loadConfig } from "../config/config.js"; +import type { OpenClawConfig } from "../config/config.js"; +import { readBestEffortConfig } from "../config/config.js"; import { buildGatewayConnectionDetails, callGateway } from "../gateway/call.js"; import { normalizeControlUiBasePath } from "../gateway/control-ui-shared.js"; import { probeGateway } from "../gateway/probe.js"; @@ -59,7 +60,7 @@ function unwrapDeferredResult(result: DeferredResult): T { return result.value; } -function resolveMemoryPluginStatus(cfg: ReturnType): MemoryPluginStatus { +function resolveMemoryPluginStatus(cfg: OpenClawConfig): MemoryPluginStatus { const pluginsEnabled = cfg.plugins?.enabled !== false; if (!pluginsEnabled) { return { enabled: false, slot: null, reason: "plugins disabled" }; @@ -72,10 +73,10 @@ function resolveMemoryPluginStatus(cfg: ReturnType): MemoryPl } async function resolveGatewayProbeSnapshot(params: { - cfg: ReturnType; + cfg: OpenClawConfig; opts: { timeoutMs?: number; all?: boolean }; }): Promise { - const gatewayConnection = buildGatewayConnectionDetails(); + const gatewayConnection = buildGatewayConnectionDetails({ config: params.cfg }); const isRemoteMode = params.cfg.gateway?.mode === "remote"; const remoteUrlRaw = typeof params.cfg.gateway?.remote?.url === "string" ? params.cfg.gateway.remote.url : ""; @@ -107,6 +108,7 @@ async function resolveGatewayProbeSnapshot(params: { } async function resolveChannelsStatus(params: { + cfg: OpenClawConfig; gatewayReachable: boolean; opts: { timeoutMs?: number; all?: boolean }; }) { @@ -114,6 +116,7 @@ async function resolveChannelsStatus(params: { return null; } return await callGateway({ + config: params.cfg, method: "channels.status", params: { probe: false, @@ -124,8 +127,8 @@ async function resolveChannelsStatus(params: { } export type StatusScanResult = { - cfg: ReturnType; - sourceConfig: ReturnType; + cfg: OpenClawConfig; + sourceConfig: OpenClawConfig; secretDiagnostics: string[]; osSummary: ReturnType; tailscaleMode: string; @@ -152,7 +155,7 @@ export type StatusScanResult = { }; async function resolveMemoryStatusSnapshot(params: { - cfg: ReturnType; + cfg: OpenClawConfig; agentStatus: Awaited>; memoryPlugin: MemoryPluginStatus; }): Promise { @@ -180,7 +183,7 @@ async function scanStatusJsonFast(opts: { timeoutMs?: number; all?: boolean; }): Promise { - const loadedRaw = loadConfig(); + const loadedRaw = await readBestEffortConfig(); const { resolvedConfig: cfg, diagnostics: secretDiagnostics } = await resolveCommandSecretRefsViaGateway({ config: loadedRaw, @@ -196,7 +199,7 @@ async function scanStatusJsonFast(opts: { fetchGit: true, includeRegistry: true, }); - const agentStatusPromise = getAgentLocalStatuses(); + const agentStatusPromise = getAgentLocalStatuses(cfg); const summaryPromise = getStatusSummary({ config: cfg, sourceConfig: loadedRaw }); const tailscaleDnsPromise = @@ -232,7 +235,7 @@ async function scanStatusJsonFast(opts: { const gatewaySelf = gatewayProbe?.presence ? pickGatewaySelfPresence(gatewayProbe.presence) : null; - const channelsStatusPromise = resolveChannelsStatus({ gatewayReachable, opts }); + const channelsStatusPromise = resolveChannelsStatus({ cfg, gatewayReachable, opts }); const memoryPlugin = resolveMemoryPluginStatus(cfg); const memoryPromise = resolveMemoryStatusSnapshot({ cfg, agentStatus, memoryPlugin }); const [channelsStatus, memory] = await Promise.all([channelsStatusPromise, memoryPromise]); @@ -283,7 +286,7 @@ export async function scanStatus( }, async (progress) => { progress.setLabel("Loading config…"); - const loadedRaw = loadConfig(); + const loadedRaw = await readBestEffortConfig(); const { resolvedConfig: cfg, diagnostics: secretDiagnostics } = await resolveCommandSecretRefsViaGateway({ config: loadedRaw, @@ -307,7 +310,7 @@ export async function scanStatus( includeRegistry: true, }), ); - const agentStatusPromise = deferResult(getAgentLocalStatuses()); + const agentStatusPromise = deferResult(getAgentLocalStatuses(cfg)); const summaryPromise = deferResult( getStatusSummary({ config: cfg, sourceConfig: loadedRaw }), ); @@ -345,7 +348,7 @@ export async function scanStatus( progress.tick(); progress.setLabel("Querying channel status…"); - const channelsStatus = await resolveChannelsStatus({ gatewayReachable, opts }); + const channelsStatus = await resolveChannelsStatus({ cfg, gatewayReachable, opts }); const channelIssues = channelsStatus ? collectChannelStatusIssues(channelsStatus) : []; progress.tick();