mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-16 23:52:40 -06:00
17c2ce05d8
* fix(secrets): degrade missing TTS SecretRefs at startup * test(secrets): keep non-activating startup strict * test(secrets): mark denied key fixture synthetic * test(secrets): use synthetic TTS key fixture * test(secrets): use neutral TTS key placeholder * test(secrets): isolate TTS key placeholder * test(secrets): shorten TTS ref fixture name * test(secrets): normalize synthetic credential fixtures * test(secrets): isolate optional redaction coverage * fix(secrets): preserve degraded TTS ref ownership * refactor(secrets): keep optional resolver internal * test(secrets): cover default provider alias misses * test(secrets): pin explicit provider ownership * style(secrets): format optional assignment imports * refactor(secrets): keep optional metadata private * style(secrets): restore collector file header * fix(secrets): isolate unavailable SecretRef owners Co-authored-by: snowzlmbot <293528334+snowzlmbot@users.noreply.github.com> * test(secrets): complete provider fixtures * style(status): avoid degraded path shadowing * style(secrets): satisfy runtime lint * refactor(secrets): keep error codes internal * fix(secrets): keep unowned assignments fail closed * fix(secrets): preserve provider resolution batching * fix(secrets): normalize stalled resolution errors * fix(secrets): reject provider limit violations --------- Co-authored-by: snowzlmbot <293528334+snowzlmbot@users.noreply.github.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
148 lines
5.1 KiB
TypeScript
148 lines
5.1 KiB
TypeScript
// Shared bootstrap for status scans.
|
|
// Starts update, Tailscale, agent, and gateway probes with cold-start shortcuts for first-run users.
|
|
|
|
import type { OpenClawConfig } from "../config/types.js";
|
|
import type { UpdateCheckResult } from "../infra/update-check.js";
|
|
import { runExec } from "../process/exec.js";
|
|
import { createEmptyTaskAuditSummary } from "../tasks/task-registry.audit.shared.js";
|
|
import { createEmptyTaskRegistrySummary } from "../tasks/task-registry.summary.js";
|
|
import { buildTailscaleHttpsUrl, resolveGatewayProbeSnapshot } from "./status.scan.shared.js";
|
|
|
|
function buildColdStartUpdateResult(): UpdateCheckResult {
|
|
return {
|
|
root: null,
|
|
installKind: "unknown",
|
|
packageManager: "unknown",
|
|
};
|
|
}
|
|
|
|
function buildColdStartAgentLocalStatuses() {
|
|
return {
|
|
defaultId: "main",
|
|
agents: [],
|
|
totalSessions: 0,
|
|
bootstrapPendingCount: 0,
|
|
};
|
|
}
|
|
|
|
/** Builds an empty summary for cold-start status paths that skip network and session work. */
|
|
export function buildColdStartStatusSummary() {
|
|
return {
|
|
runtimeVersion: null,
|
|
heartbeat: {
|
|
defaultAgentId: "main",
|
|
agents: [],
|
|
},
|
|
channelSummary: [],
|
|
queuedSystemEvents: [],
|
|
degradedSecretOwners: [],
|
|
tasks: createEmptyTaskRegistrySummary(),
|
|
taskAudit: createEmptyTaskAuditSummary(),
|
|
sessions: {
|
|
paths: [],
|
|
count: 0,
|
|
defaults: { model: null, contextTokens: null },
|
|
recent: [],
|
|
byAgent: [],
|
|
},
|
|
};
|
|
}
|
|
|
|
function shouldSkipStatusScanNetworkChecks(params: {
|
|
coldStart: boolean;
|
|
hasConfiguredChannels: boolean;
|
|
all?: boolean;
|
|
}): boolean {
|
|
// First-run users without channels should get instant status instead of waiting on network probes.
|
|
return params.coldStart && !params.hasConfiguredChannels && params.all !== true;
|
|
}
|
|
|
|
type StatusScanExecRunner = (
|
|
command: string,
|
|
args: string[],
|
|
opts?: number | { timeoutMs?: number; maxBuffer?: number; cwd?: string },
|
|
) => Promise<{ stdout: string; stderr: string }>;
|
|
|
|
type StatusScanCoreBootstrapParams<TAgentStatus> = {
|
|
coldStart: boolean;
|
|
cfg: OpenClawConfig;
|
|
hasConfiguredChannels: boolean;
|
|
opts: { timeoutMs?: number; all?: boolean };
|
|
skipUpdateCheck?: boolean;
|
|
fetchGitUpdate?: boolean;
|
|
includeRegistryUpdate?: boolean;
|
|
includeLocalStatusRpcFallback?: boolean;
|
|
gatewayProbeTimeoutMs?: number;
|
|
getTailnetHostname: (runner: StatusScanExecRunner) => Promise<string | null>;
|
|
getUpdateCheckResult: (params: {
|
|
timeoutMs: number;
|
|
fetchGit: boolean;
|
|
includeRegistry: boolean;
|
|
updateConfigChannel?: string | null;
|
|
}) => Promise<UpdateCheckResult>;
|
|
getAgentLocalStatuses: (cfg: OpenClawConfig) => Promise<TAgentStatus>;
|
|
};
|
|
|
|
/** Starts the common async probes used by status scans and exposes their promises to callers. */
|
|
export async function createStatusScanCoreBootstrap<TAgentStatus>(
|
|
params: StatusScanCoreBootstrapParams<TAgentStatus>,
|
|
) {
|
|
const tailscaleMode = params.cfg.gateway?.tailscale?.mode ?? "off";
|
|
const skipColdStartNetworkChecks = shouldSkipStatusScanNetworkChecks({
|
|
coldStart: params.coldStart,
|
|
hasConfiguredChannels: params.hasConfiguredChannels,
|
|
all: params.opts.all,
|
|
});
|
|
const statusTimeoutMs = params.opts.timeoutMs ?? 10_000;
|
|
const updateTimeoutMs = Math.min(params.opts.all ? 6500 : 2500, statusTimeoutMs);
|
|
const tailscaleTimeoutMs = Math.min(1200, statusTimeoutMs);
|
|
const tailscaleDnsPromise =
|
|
tailscaleMode === "off"
|
|
? Promise.resolve<string | null>(null)
|
|
: params
|
|
.getTailnetHostname((cmd, args) =>
|
|
runExec(cmd, args, { timeoutMs: tailscaleTimeoutMs, maxBuffer: 200_000 }),
|
|
)
|
|
.catch(() => null);
|
|
const skipNetworkUpdate = skipColdStartNetworkChecks || params.skipUpdateCheck === true;
|
|
// Update checks can hit git/registry, so cold-start status uses a synthetic unknown result.
|
|
const updatePromise = skipNetworkUpdate
|
|
? Promise.resolve(buildColdStartUpdateResult())
|
|
: params.getUpdateCheckResult({
|
|
timeoutMs: updateTimeoutMs,
|
|
fetchGit: params.fetchGitUpdate ?? true,
|
|
includeRegistry: params.includeRegistryUpdate ?? true,
|
|
updateConfigChannel: params.cfg.update?.channel ?? null,
|
|
});
|
|
const agentStatusPromise = skipColdStartNetworkChecks
|
|
? Promise.resolve(buildColdStartAgentLocalStatuses() as TAgentStatus)
|
|
: params.getAgentLocalStatuses(params.cfg);
|
|
const gatewayProbePromise = resolveGatewayProbeSnapshot({
|
|
cfg: params.cfg,
|
|
opts: {
|
|
...params.opts,
|
|
...(params.gatewayProbeTimeoutMs !== undefined
|
|
? { timeoutMs: params.gatewayProbeTimeoutMs }
|
|
: {}),
|
|
...(skipColdStartNetworkChecks ? { skipProbe: true } : {}),
|
|
localStatusRpcFallback: params.includeLocalStatusRpcFallback !== false,
|
|
},
|
|
});
|
|
|
|
return {
|
|
tailscaleMode,
|
|
tailscaleDnsPromise,
|
|
updatePromise,
|
|
agentStatusPromise,
|
|
gatewayProbePromise,
|
|
skipColdStartNetworkChecks,
|
|
resolveTailscaleHttpsUrl: async () =>
|
|
buildTailscaleHttpsUrl({
|
|
tailscaleMode,
|
|
tailscaleDns: await tailscaleDnsPromise,
|
|
serviceName: params.cfg.gateway?.tailscale?.serviceName,
|
|
controlUiBasePath: params.cfg.gateway?.controlUi?.basePath,
|
|
}),
|
|
};
|
|
}
|