openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
479 lines (478 loc) • 21.9 kB
JavaScript
import { c as normalizeOptionalString, s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js";
import { s as resolveConfigPath } from "./paths-mvMm5bYV.js";
import { t as createLazyImportLoader } from "./lazy-promise-BONnzNfb.js";
import { n as defaultSlotIdForKey } from "./slots-kpL659LX.js";
import { i as runExec } from "./exec-DubsSJS2.js";
import { f as isLoopbackIpAddress } from "./ip-0oQXo6_w.js";
import { i as GATEWAY_CLIENT_NAMES, r as GATEWAY_CLIENT_MODES } from "./client-info-CcqJJIan.js";
import { t as buildGatewayConnectionDetailsWithResolvers } from "./connection-details-ClaW_IO-.js";
import { t as resolveGatewayProbeTarget } from "./probe-target-4_-2qIux.js";
import { r as normalizeControlUiBasePath } from "./control-ui-shared-Clry3kRy.js";
import { t as createEmptyTaskRegistrySummary } from "./task-registry.summary-BwpoHlXv.js";
import { n as createEmptyTaskAuditSummary } from "./task-registry.audit.shared-CycXrHpp.js";
import { t as resolveOsSummary } from "./os-summary-BQlvd96x.js";
import { n as resolveTailscalePublishedHost } from "./tailscale-status-B3whvNX4.js";
import { t as resolveGatewayAuthTokenSourceConflict } from "./auth-token-source-conflict-DjnAqb2k.js";
import { t as pickGatewaySelfPresence } from "./gateway-presence-f10JpTvH.js";
import { i as isProbeReachable } from "./helpers-Bg9UcXHk.js";
import { existsSync } from "node:fs";
//#region src/commands/status.scan.shared.ts
const gatewayProbeModuleLoader = createLazyImportLoader(() => import("./status.gateway-probe-BQDDYc__.js"));
const probeGatewayModuleLoader = createLazyImportLoader(() => import("./probe-BFjltCXj.js"));
const gatewayCallModuleLoader$1 = createLazyImportLoader(() => import("./call-BEl78mhZ.js"));
function loadGatewayProbeModule() {
return gatewayProbeModuleLoader.load();
}
function loadProbeGatewayModule() {
return probeGatewayModuleLoader.load();
}
function loadGatewayCallModule$1() {
return gatewayCallModuleLoader$1.load();
}
function isLoopbackGatewayUrl(rawUrl) {
try {
const hostname = new URL(rawUrl).hostname.toLowerCase();
const unbracketed = hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
return unbracketed === "localhost" || isLoopbackIpAddress(unbracketed);
} catch {
return false;
}
}
function shouldTryLocalStatusRpcFallback(params) {
if (params.gatewayMode !== "local" || !params.gatewayProbe || params.gatewayProbe.ok || !isLoopbackGatewayUrl(params.gatewayUrl)) return false;
return (params.gatewayProbe.error?.toLowerCase() ?? "").includes("timeout") || params.gatewayProbe.auth?.capability === "unknown";
}
async function applyLocalStatusRpcFallback(params) {
if (params.enabled === false) return params.gatewayProbe;
if (!shouldTryLocalStatusRpcFallback(params)) return params.gatewayProbe;
const boundedFallbackTimeoutMs = Math.min(2e3, Math.max(1e3, params.timeoutMs));
const status = await loadGatewayCallModule$1().then(({ callGateway }) => callGateway({
config: params.cfg,
method: "status",
token: params.gatewayProbeAuth.token,
password: params.gatewayProbeAuth.password,
timeoutMs: params.timeoutMsExplicit ? boundedFallbackTimeoutMs : Math.max(params.cfg.gateway?.handshakeTimeoutMs ?? 0, boundedFallbackTimeoutMs),
mode: GATEWAY_CLIENT_MODES.BACKEND,
clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT
})).catch(() => null);
if (!status) return params.gatewayProbe;
const auth = params.gatewayProbe.auth;
return {
...params.gatewayProbe,
ok: true,
status,
...auth ? { auth: auth.capability === "unknown" ? {
...auth,
capability: "read_only"
} : auth } : {}
};
}
function hasExplicitMemorySearchConfig(cfg, agentId) {
if (cfg.agents?.defaults && Object.hasOwn(cfg.agents.defaults, "memorySearch")) return true;
return (Array.isArray(cfg.agents?.list) ? cfg.agents.list : []).some((agent) => agent?.id === agentId && Object.hasOwn(agent, "memorySearch"));
}
/** Resolves whether memory status should be shown and which slot owns it. */
function resolveMemoryPluginStatus(cfg) {
if (!(cfg.plugins?.enabled !== false)) return {
enabled: false,
slot: null,
reason: "plugins disabled"
};
const raw = normalizeOptionalString(cfg.plugins?.slots?.memory) ?? "";
if (normalizeOptionalLowercaseString(raw) === "none") return {
enabled: false,
slot: null,
reason: "plugins.slots.memory=\"none\""
};
return {
enabled: true,
slot: raw || defaultSlotIdForKey("memory")
};
}
/** Resolves gateway connection details, probe result, auth warnings, and call overrides. */
async function resolveGatewayProbeSnapshot(params) {
const gatewayConnection = buildGatewayConnectionDetailsWithResolvers({ config: params.cfg });
const { gatewayMode, remoteUrlMissing } = resolveGatewayProbeTarget(params.cfg);
const shouldResolveAuth = params.opts.skipProbe !== true && (!remoteUrlMissing || params.opts.resolveAuthWhenRemoteUrlMissing === true);
const shouldProbe = params.opts.skipProbe !== true && (!remoteUrlMissing || params.opts.probeWhenRemoteUrlMissing === true);
const gatewayProbeAuthResolution = shouldResolveAuth ? await loadGatewayProbeModule().then(({ resolveGatewayProbeAuthResolution }) => resolveGatewayProbeAuthResolution(params.cfg)) : {
auth: {},
warning: void 0
};
let gatewayProbeAuthWarning = gatewayProbeAuthResolution.warning;
const defaultProbeTimeoutMs = Math.max(params.opts.all ? 5e3 : 2500, params.cfg.gateway?.handshakeTimeoutMs ?? 0);
const timeoutMsExplicit = params.opts.timeoutMs !== void 0;
const probeTimeoutMs = params.opts.timeoutMs ?? defaultProbeTimeoutMs;
const initialGatewayProbe = shouldProbe ? await loadProbeGatewayModule().then(({ probeGateway }) => probeGateway({
url: gatewayConnection.url,
auth: gatewayProbeAuthResolution.auth,
preauthHandshakeTimeoutMs: params.cfg.gateway?.handshakeTimeoutMs,
timeoutMs: probeTimeoutMs,
detailLevel: params.opts.detailLevel ?? "presence"
})).catch(() => null) : null;
const gatewayProbe = await applyLocalStatusRpcFallback({
cfg: params.cfg,
gatewayMode,
gatewayUrl: gatewayConnection.url,
gatewayProbe: initialGatewayProbe,
gatewayProbeAuth: gatewayProbeAuthResolution.auth,
timeoutMs: probeTimeoutMs,
timeoutMsExplicit,
enabled: params.opts.localStatusRpcFallback !== false
});
if ((params.opts.mergeAuthWarningIntoProbeError ?? true) && gatewayProbeAuthWarning && gatewayProbe?.ok === false) {
gatewayProbe.error = gatewayProbe.error ? `${gatewayProbe.error}; ${gatewayProbeAuthWarning}` : gatewayProbeAuthWarning;
gatewayProbeAuthWarning = void 0;
}
const gatewayReachable = gatewayProbe ? isProbeReachable(gatewayProbe) : false;
const gatewaySelf = gatewayProbe?.presence ? pickGatewaySelfPresence(gatewayProbe.presence) : null;
return {
gatewayConnection,
remoteUrlMissing,
gatewayMode,
gatewayProbeAuth: gatewayProbeAuthResolution.auth,
gatewayProbeAuthWarning,
gatewayProbe,
gatewayReachable,
gatewaySelf,
...remoteUrlMissing ? { gatewayCallOverrides: {
url: gatewayConnection.url,
token: gatewayProbeAuthResolution.auth.token,
password: gatewayProbeAuthResolution.auth.password
} } : {}
};
}
/** Builds the published Tailscale HTTPS Control UI URL when exposure is enabled. */
function buildTailscaleHttpsUrl(params) {
const host = resolveTailscalePublishedHost({
tailscaleMode: params.tailscaleMode,
tailnetHost: params.tailscaleDns,
serviceName: params.serviceName
});
return params.tailscaleMode !== "off" && host ? `https://${host}${normalizeControlUiBasePath(params.controlUiBasePath)}` : null;
}
/** Resolves memory provider status without creating default stores just for status output. */
async function resolveSharedMemoryStatusSnapshot(params) {
const { cfg, agentStatus, memoryPlugin } = params;
if (!memoryPlugin.enabled || !memoryPlugin.slot) return null;
const agentId = agentStatus.defaultId ?? "main";
if (memoryPlugin.slot !== defaultSlotIdForKey("memory")) return await resolveMemoryManagerStatusSnapshot(params, agentId);
const defaultStorePath = params.requireDefaultStore?.(agentId);
if (defaultStorePath && !hasExplicitMemorySearchConfig(cfg, agentId) && !existsSync(defaultStorePath)) return null;
const resolvedMemory = params.resolveMemoryConfig(cfg, agentId);
if (!resolvedMemory) return null;
if (!(hasExplicitMemorySearchConfig(cfg, agentId) || existsSync(resolvedMemory.store.path))) return null;
return await resolveMemoryManagerStatusSnapshot(params, agentId);
}
async function resolveMemoryManagerStatusSnapshot(params, agentId) {
const { manager } = await params.getMemorySearchManager({
cfg: params.cfg,
agentId,
purpose: "status"
});
if (!manager) return null;
try {
try {
if (manager.status().backend === "builtin" && manager.probeVectorStoreAvailability) await manager.probeVectorStoreAvailability();
else await manager.probeVectorAvailability();
} catch {}
return {
agentId,
...manager.status()
};
} finally {
await manager.close?.().catch(() => {});
}
}
//#endregion
//#region src/commands/status.scan.bootstrap-shared.ts
function buildColdStartUpdateResult() {
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. */
function buildColdStartStatusSummary() {
return {
runtimeVersion: null,
heartbeat: {
defaultAgentId: "main",
agents: []
},
channelSummary: [],
queuedSystemEvents: [],
tasks: createEmptyTaskRegistrySummary(),
taskAudit: createEmptyTaskAuditSummary(),
sessions: {
paths: [],
count: 0,
defaults: {
model: null,
contextTokens: null
},
recent: [],
byAgent: []
}
};
}
function shouldSkipStatusScanNetworkChecks(params) {
return params.coldStart && !params.hasConfiguredChannels && params.all !== true;
}
/** Starts the common async probes used by status scans and exposes their promises to callers. */
async function createStatusScanCoreBootstrap(params) {
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 ?? 1e4;
const updateTimeoutMs = Math.min(params.opts.all ? 6500 : 2500, statusTimeoutMs);
const tailscaleTimeoutMs = Math.min(1200, statusTimeoutMs);
const tailscaleDnsPromise = tailscaleMode === "off" ? Promise.resolve(null) : params.getTailnetHostname((cmd, args) => runExec(cmd, args, {
timeoutMs: tailscaleTimeoutMs,
maxBuffer: 2e5
})).catch(() => null);
return {
tailscaleMode,
tailscaleDnsPromise,
updatePromise: skipColdStartNetworkChecks || params.skipUpdateCheck === true ? Promise.resolve(buildColdStartUpdateResult()) : params.getUpdateCheckResult({
timeoutMs: updateTimeoutMs,
fetchGit: params.fetchGitUpdate ?? true,
includeRegistry: params.includeRegistryUpdate ?? true,
updateConfigChannel: params.cfg.update?.channel ?? null
}),
agentStatusPromise: skipColdStartNetworkChecks ? Promise.resolve(buildColdStartAgentLocalStatuses()) : params.getAgentLocalStatuses(params.cfg),
gatewayProbePromise: resolveGatewayProbeSnapshot({
cfg: params.cfg,
opts: {
...params.opts,
...params.gatewayProbeTimeoutMs !== void 0 ? { timeoutMs: params.gatewayProbeTimeoutMs } : {},
...skipColdStartNetworkChecks ? { skipProbe: true } : {},
localStatusRpcFallback: params.includeLocalStatusRpcFallback !== false
}
}),
skipColdStartNetworkChecks,
resolveTailscaleHttpsUrl: async () => buildTailscaleHttpsUrl({
tailscaleMode,
tailscaleDns: await tailscaleDnsPromise,
serviceName: params.cfg.gateway?.tailscale?.serviceName,
controlUiBasePath: params.cfg.gateway?.controlUi?.basePath
})
};
}
//#endregion
//#region src/commands/status.scan.config-shared.ts
/** Returns true when tests should avoid the missing-config cold-start fast path. */
function shouldSkipStatusScanMissingConfigFastPath(env = process.env) {
return env.VITEST === "true" || env.VITEST_POOL_ID !== void 0 || env.NODE_ENV === "test";
}
/** Returns whether status should treat this run as a no-config cold start. */
function resolveStatusScanColdStart(params) {
const env = params?.env ?? process.env;
return !(params?.allowMissingConfigFastPath === true && shouldSkipStatusScanMissingConfigFastPath(env)) && !existsSync(resolveConfigPath(env));
}
/** Loads best-effort config, resolves read-only secrets, and appends status secret diagnostics. */
async function loadStatusScanCommandConfig(params) {
const env = params.env ?? process.env;
const coldStart = resolveStatusScanColdStart({
env,
allowMissingConfigFastPath: params.allowMissingConfigFastPath
});
const sourceConfig = coldStart && params.allowMissingConfigFastPath === true ? {} : await params.readBestEffortConfig();
const { resolvedConfig, diagnostics } = coldStart && params.allowMissingConfigFastPath === true ? {
resolvedConfig: sourceConfig,
diagnostics: []
} : await params.resolveConfig(sourceConfig);
const tokenConflict = resolveGatewayAuthTokenSourceConflict({
cfg: sourceConfig,
env
});
return {
coldStart,
sourceConfig,
resolvedConfig,
secretDiagnostics: tokenConflict ? [...diagnostics, tokenConflict.diagnostic] : diagnostics
};
}
//#endregion
//#region src/commands/status.scan-overview.ts
const statusScanDepsRuntimeModuleLoader = createLazyImportLoader(() => import("./status.scan.deps.runtime.js"));
const statusAgentLocalModuleLoader = createLazyImportLoader(() => import("./status.agent-local-CWXAPk4v.js"));
const statusUpdateModuleLoader = createLazyImportLoader(() => import("./status.update-6zlC0PAl.js"));
const statusScanRuntimeModuleLoader = createLazyImportLoader(() => import("./status.scan.runtime.js"));
const gatewayCallModuleLoader = createLazyImportLoader(() => import("./call-BEl78mhZ.js"));
const statusSummaryModuleLoader = createLazyImportLoader(() => import("./status.summary-Cg9LA3vF.js"));
const channelPluginIdsModuleLoader = createLazyImportLoader(() => import("./channel-plugin-ids-JT38R6k8.js"));
const configModuleLoader = createLazyImportLoader(() => import("./config/config.js"));
const commandConfigResolutionModuleLoader = createLazyImportLoader(() => import("./command-config-resolution-DlBL3NyY.js"));
const commandSecretTargetsModuleLoader = createLazyImportLoader(() => import("./command-secret-targets-C-2Ieaq9.js"));
function loadStatusScanDepsRuntimeModule() {
return statusScanDepsRuntimeModuleLoader.load();
}
function loadStatusAgentLocalModule() {
return statusAgentLocalModuleLoader.load();
}
function loadStatusUpdateModule() {
return statusUpdateModuleLoader.load();
}
function loadStatusScanRuntimeModule() {
return statusScanRuntimeModuleLoader.load();
}
function loadGatewayCallModule() {
return gatewayCallModuleLoader.load();
}
function loadStatusSummaryModule() {
return statusSummaryModuleLoader.load();
}
function loadChannelPluginIdsModule() {
return channelPluginIdsModuleLoader.load();
}
function loadConfigModule() {
return configModuleLoader.load();
}
function loadCommandConfigResolutionModule() {
return commandConfigResolutionModuleLoader.load();
}
function loadCommandSecretTargetsModule() {
return commandSecretTargetsModuleLoader.load();
}
async function resolveStatusChannelsStatus(params) {
if (!params.gatewayReachable) return null;
const { callGateway } = await loadGatewayCallModule();
return await callGateway({
config: params.cfg,
method: "channels.status",
params: {
probe: false,
timeoutMs: Math.min(8e3, params.opts.timeoutMs ?? 1e4)
},
timeoutMs: Math.min(params.opts.all ? 5e3 : 2500, params.opts.timeoutMs ?? 1e4),
...params.useGatewayCallOverrides === true ? params.gatewayCallOverrides ?? {} : {}
}).catch(() => null);
}
/** Collects the common status scan data shared by text, JSON, and status-all commands. */
async function collectStatusScanOverview(params) {
if (params.labels?.loadingConfig) params.progress?.setLabel(params.labels.loadingConfig);
const { coldStart, sourceConfig, resolvedConfig: cfg, secretDiagnostics } = await loadStatusScanCommandConfig({
commandName: params.commandName,
allowMissingConfigFastPath: params.allowMissingConfigFastPath,
readBestEffortConfig: async () => (await loadConfigModule()).readBestEffortConfig({ skipPluginValidation: params.skipConfigPluginValidation }),
resolveConfig: async (loadedConfig) => await (await loadCommandConfigResolutionModule()).resolveCommandConfigWithSecrets({
config: loadedConfig,
commandName: params.commandName,
targetIds: (await loadCommandSecretTargetsModule()).getStatusCommandSecretTargetIds(loadedConfig, process.env, { includeChannelTargets: params.includeChannelSecretTargets }),
mode: "read_only_status",
...params.runtime ? { runtime: params.runtime } : {}
})
});
params.progress?.tick();
const hasConfiguredChannels = params.resolveHasConfiguredChannels ? await params.resolveHasConfiguredChannels(cfg, sourceConfig) : await loadChannelPluginIdsModule().then(({ hasConfiguredChannelsForReadOnlyScope }) => hasConfiguredChannelsForReadOnlyScope({
config: cfg,
activationSourceConfig: sourceConfig
}));
const osSummary = resolveOsSummary();
const gatewayProbeTimeoutMs = typeof params.gatewayProbeTimeoutMs === "function" ? params.gatewayProbeTimeoutMs(cfg) : params.gatewayProbeTimeoutMs;
const bootstrap = await createStatusScanCoreBootstrap({
coldStart,
cfg,
hasConfiguredChannels,
opts: params.opts,
skipUpdateCheck: params.skipUpdateCheck,
fetchGitUpdate: params.fetchGitUpdate,
includeRegistryUpdate: params.includeRegistryUpdate,
includeLocalStatusRpcFallback: params.includeLocalStatusRpcFallback,
gatewayProbeTimeoutMs,
getTailnetHostname: async (runner) => await loadStatusScanDepsRuntimeModule().then(({ getTailnetHostname }) => getTailnetHostname(runner)),
getUpdateCheckResult: async (updateParams) => await loadStatusUpdateModule().then(({ getUpdateCheckResult }) => getUpdateCheckResult(updateParams)),
getAgentLocalStatuses: async (bootstrapCfg) => await loadStatusAgentLocalModule().then(({ getAgentLocalStatuses }) => getAgentLocalStatuses(bootstrapCfg))
});
if (params.labels?.checkingTailscale) params.progress?.setLabel(params.labels.checkingTailscale);
const tailscaleDns = await bootstrap.tailscaleDnsPromise;
params.progress?.tick();
if (params.labels?.checkingForUpdates) params.progress?.setLabel(params.labels.checkingForUpdates);
const update = await bootstrap.updatePromise;
params.progress?.tick();
if (params.labels?.resolvingAgents) params.progress?.setLabel(params.labels.resolvingAgents);
const agentStatus = await bootstrap.agentStatusPromise;
params.progress?.tick();
if (params.labels?.probingGateway) params.progress?.setLabel(params.labels.probingGateway);
const gatewaySnapshot = await bootstrap.gatewayProbePromise;
params.progress?.tick();
const tailscaleHttpsUrl = await bootstrap.resolveTailscaleHttpsUrl();
const includeChannelsData = params.includeChannelsData !== false;
const includeLiveChannelStatus = params.includeLiveChannelStatus !== false;
const { channelsStatus, channelIssues, channels } = includeChannelsData ? await (async () => {
if (params.labels?.queryingChannelStatus) params.progress?.setLabel(params.labels.queryingChannelStatus);
const channelsStatusLocal = includeLiveChannelStatus ? await resolveStatusChannelsStatus({
cfg,
gatewayReachable: gatewaySnapshot.gatewayReachable,
opts: params.opts,
gatewayCallOverrides: gatewaySnapshot.gatewayCallOverrides,
useGatewayCallOverrides: params.useGatewayCallOverridesForChannelsStatus
}) : null;
params.progress?.tick();
const { collectChannelStatusIssues, buildChannelsTable } = await loadStatusScanRuntimeModule().then(({ statusScanRuntime }) => statusScanRuntime);
const channelIssuesLocal = channelsStatusLocal ? collectChannelStatusIssues(channelsStatusLocal) : [];
if (params.labels?.summarizingChannels) params.progress?.setLabel(params.labels.summarizingChannels);
const channelsLocal = await buildChannelsTable(cfg, {
showSecrets: params.showSecrets,
sourceConfig,
includeSetupFallbackPlugins: params.includeChannelSetupRuntimeFallback !== false,
liveChannelStatus: channelsStatusLocal,
...params.channelCredentialResolutionSkipped === true ? { credentialResolutionSkipped: true } : {}
});
params.progress?.tick();
return {
channelsStatus: channelsStatusLocal,
channelIssues: channelIssuesLocal,
channels: channelsLocal
};
})() : {
channelsStatus: null,
channelIssues: [],
channels: {
rows: [],
details: []
}
};
return {
coldStart,
hasConfiguredChannels,
skipColdStartNetworkChecks: bootstrap.skipColdStartNetworkChecks,
cfg,
sourceConfig,
secretDiagnostics,
osSummary,
tailscaleMode: bootstrap.tailscaleMode,
tailscaleDns,
tailscaleHttpsUrl,
update,
gatewaySnapshot,
channelsStatus,
channelIssues,
channels,
agentStatus
};
}
/** Resolves the summary object from overview data, preserving cold-start fast-path behavior. */
async function resolveStatusSummaryFromOverview(params) {
if (params.overview.skipColdStartNetworkChecks) return buildColdStartStatusSummary();
return await loadStatusSummaryModule().then(({ getStatusSummary }) => getStatusSummary({
config: params.overview.cfg,
sourceConfig: params.overview.sourceConfig,
includeChannelSummary: params.includeChannelSummary
}));
}
//#endregion
export { resolveSharedMemoryStatusSnapshot as i, resolveStatusSummaryFromOverview as n, resolveMemoryPluginStatus as r, collectStatusScanOverview as t };