openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
701 lines (700 loc) • 28.3 kB
JavaScript
import { t as createDeferredCore } from "./deferred-D0La5CRk.mjs";
import { n as captureAsyncWorkTracker, r as getAsyncWorkSignal, t as AsyncWorkScope } from "./async-work-scope-DDiH0P7W.mjs";
import { h as normalizeUniqueStringEntries } from "./string-normalization-D6DKCpwE.mjs";
import { t as appendConfigPathSegment } from "./dot-path-CVD5Jolm.mjs";
import { f as normalizeSecretInputString, g as resolveSecretInputRef, o as hasConfiguredSecretInput } from "./types.secrets-DD-Q2SS_.mjs";
import { t as formatErrorMessage } from "./errors-BvOkmQM5.mjs";
import { t as formatCliCommand } from "./command-format-DwEdrDKL.mjs";
import { t as createSubsystemLogger } from "./subsystem-4NzR43uz.mjs";
import { c as resolveSessionStorePathCore } from "./paths-Bonddxfk.mjs";
import { r as normalizeProviderId } from "./provider-id-D1VImdKk.mjs";
import { a as resolveAgentDir, d as resolveAgentWorkspaceDir, g as resolveDefaultAgentId } from "./agent-scope-config-BEk6-ThS.mjs";
import { r as cloneEnvWithPlatformSemantics } from "./config-env-vars-BzUE_dOh.mjs";
import { f as resolveConfigSecretRef, i as copyConfigResolutionFactsExcept, r as copyConfigResolutionFacts } from "./resolution-facts-NmRQ-VhX.mjs";
import { r as DEFAULT_PROVIDER } from "./defaults-Jag53zwP.mjs";
import "./agent-scope-bJOokm5r.mjs";
import { o as resolveMergedModelProviderEntry } from "./model-provider-config-BVE2xsBm.mjs";
import { i as resolveProviderIdForAuth } from "./provider-auth-aliases-CAzJGfWN.mjs";
import { n as parseModelRef, t as findNormalizedProviderValue } from "./model-selection-normalize-B-Anaau2.mjs";
import { n as recordAgentCleanupFailure } from "./run-cleanup-timeout-CHdeH1f3.mjs";
import { v as resolveAuthProfileDatabasePath } from "./sqlite-Bk_bX_Ka.mjs";
import { s as getScopedAuthProfileEnv } from "./store-C7nkCeF3.mjs";
import { l as upsertAuthProfileWithLock } from "./profiles-6K3k_v-S.mjs";
import { r as clearRuntimeAuthProfileStoreSnapshotCore } from "./runtime-snapshots-DU02_IZD.mjs";
import { p as prepareSystemAgentRunAdmission } from "./admitted-run-context-D0yzk2io.mjs";
import { i as externalCliDiscoveryScoped } from "./external-cli-discovery-C550HCjn.mjs";
import { f as listProfilesForProvider, i as resolveAuthProfileEligibility, o as resolveAuthProfileOrderWithMetadata } from "./order-C69p83pK.mjs";
import { s as isNonSecretApiKeyMarker } from "./model-auth-markers-BKfhEhYJ.mjs";
import { t as resolveEnvApiKey } from "./model-auth-env-BNV47PY-.mjs";
import { a as hasSyntheticLocalProviderAuthConfig, o as hasUsableCustomProviderApiKey, v as resolveProviderEntryApiKeyBinding, x as resolveUsableCustomProviderApiKey, y as resolveProviderEntryApiKeyProfileReference } from "./model-auth-provider-config-Cwa51-db.mjs";
import { n as ensureAuthProfileStore } from "./store-runtime-DPTEKEq5.mjs";
import "./model-selection-Cpoq0B2R.mjs";
import { t as resolveAuthProfileDisplayLabel } from "./auth-profiles-DpzfW6rH.mjs";
import "./model-auth-CGgrsVoA.mjs";
import { d as readPreparedModelCatalog } from "./prepared-model-catalog-KgiwpXAj.mjs";
import { t as resolveSecretRefString } from "./resolve-37oyiAde.mjs";
import { i as describeFailoverError } from "./failover-error-JM6M2oyF.mjs";
import { n as prepareInternalSessionEffectsSession, r as removeInternalSessionEffectsSession } from "./internal-session-effects-DphZRLwy.mjs";
import { t as FAILOVER_PROBE_STATUS } from "./probe-status-BaXI8Vaw.mjs";
import { r as extractAgentRunTerminalError, t as agentRunHasVisibleReply } from "./agent-run-result-CG4UvlhG.mjs";
import { r as formatMs } from "./shared-DqbOZ4lY.mjs";
import { i as redactStatusSecrets } from "./format-gpPS89nk.mjs";
import { t as disposeOpenClawAgentDatabaseByPath } from "./openclaw-agent-db-disposal-DnSGSuyy.mjs";
import path from "node:path";
import { AsyncLocalStorage } from "node:async_hooks";
import fs from "node:fs/promises";
import os from "node:os";
import crypto from "node:crypto";
import pMap from "p-map";
//#region src/commands/models/list.probe.cleanup.ts
const log = createSubsystemLogger("models/probe");
/** Keep private credential files until their database and registration have settled. */
async function disposeAuthProbeDirectory(ownedDir, env) {
clearRuntimeAuthProfileStoreSnapshotCore(ownedDir);
try {
await disposeOpenClawAgentDatabaseByPath(resolveAuthProfileDatabasePath(ownedDir), { env });
} catch (cause) {
throw new Error(`Auth probe database cleanup failed; retained ${ownedDir}: ${formatErrorMessage(cause)}`, { cause });
}
await fs.rm(ownedDir, {
recursive: true,
force: true
});
}
/** Probe-owned files and locks follow actual work without extending run admission. */
async function createAuthProbeWork(signal) {
const parent = captureAsyncWorkTracker();
const parentSignal = getAsyncWorkSignal();
const ready = createDeferredCore();
const completion = createDeferredCore();
parent(() => {
ready.resolve();
return completion.promise;
}).catch(ready.reject);
await ready.promise;
const work = new AsyncWorkScope();
const context = work.run(() => AsyncLocalStorage.snapshot());
const abortListeners = /* @__PURE__ */ new Map();
for (const source of [signal, parentSignal]) {
if (!source || abortListeners.has(source)) continue;
const abort = () => context(() => work.beginClose(source.reason));
abortListeners.set(source, abort);
source.addEventListener("abort", abort, { once: true });
if (source.aborted) abort();
}
return {
run(operation) {
return work.track(operation);
},
async settle(cleanup) {
const finish = async () => {
try {
await AsyncWorkScope.runWhenAllIdle(() => [work], () => context(() => work.drain()));
await cleanup();
} catch (error) {
recordAgentCleanupFailure();
completion.reject(error);
throw error;
} finally {
for (const [source, abort] of abortListeners) source.removeEventListener("abort", abort);
completion.resolve();
}
};
if (!work.hasPendingWork) {
await finish();
return;
}
finish().catch((error) => {
log.warn(`Auth check cleanup failed: ${redactStatusSecrets(formatErrorMessage(error))}`);
});
}
};
}
//#endregion
//#region src/commands/models/list.probe.models.ts
function buildProbeCandidateMap(modelCandidates) {
const map = /* @__PURE__ */ new Map();
for (const raw of modelCandidates) {
const parsed = parseModelRef(raw ?? "", DEFAULT_PROVIDER);
if (parsed && !map.has(parsed.provider)) map.set(parsed.provider, parsed.model);
}
return map;
}
function probePriority(provider, modelId) {
const id = modelId.trim().toLowerCase();
if (provider !== "anthropic") return 50;
if (/^claude-haiku-4-5-\d{8}$/.test(id)) return 0;
if (id === "claude-haiku-4-5") return 1;
if (id === "claude-sonnet-5" || id.startsWith("claude-sonnet-5-")) return 2;
if (id === "claude-sonnet-4-6" || id.startsWith("claude-sonnet-4-6-")) return 3;
if (id.startsWith("claude-sonnet-4-")) return 4;
if (id.startsWith("claude-3-")) return 100;
return 50;
}
/** Selects a requested-provider candidate before falling back to its catalog rows. */
function selectProbeModel(params) {
const { provider, candidates, catalog } = params;
const direct = candidates.get(provider);
if (direct) return {
provider,
model: direct
};
const fromCatalog = catalog.filter((entry) => normalizeProviderId(entry.provider) === provider && entry.status !== "deprecated" && entry.status !== "disabled").toSorted((a, b) => probePriority(provider, a.id) - probePriority(provider, b.id))[0];
return fromCatalog ? {
provider,
model: fromCatalog.id
} : null;
}
//#endregion
//#region src/commands/models/list.probe.ts
const PROBE_PROMPT = "Reply with OK. Do not use tools.";
function buildProbeResult(target, outcome) {
return {
provider: target.provider,
model: target.model ? `${target.model.provider}/${target.model.model}` : void 0,
profileId: target.profileId,
label: target.label,
source: target.source,
mode: target.mode,
...outcome
};
}
function buildNoModelProbeResult(target) {
return buildProbeResult(target, {
status: "no_model",
reasonCode: "no_model",
error: "No model available for check"
});
}
function mapFailoverReasonToProbeStatus(reason) {
return reason ? FAILOVER_PROBE_STATUS[reason] ?? "unknown" : "unknown";
}
const ELIGIBILITY_PROBE_ERRORS = {
expired: "token credentials are expired.",
invalid_expires: "token expires must be a positive Unix ms timestamp.",
missing_credential: "no inline credential or SecretRef is configured.",
unresolved_ref: "configured SecretRef could not be resolved.",
ineligible_profile: "profile is incompatible with provider config."
};
function mapEligibilityReasonToProbeReasonCode(reasonCode) {
switch (reasonCode) {
case "missing_credential":
case "expired":
case "invalid_expires":
case "unresolved_ref": return reasonCode;
default: return "ineligible_profile";
}
}
function resolveProbeSecretRef(profile, cfg) {
const defaults = cfg.secrets?.defaults;
if (profile.type === "api_key") return resolveSecretInputRef({
value: profile.key,
refValue: profile.keyRef,
defaults
}).ref;
if (profile.type === "token") return resolveSecretInputRef({
value: profile.token,
refValue: profile.tokenRef,
defaults
}).ref;
return null;
}
function formatUnresolvedRefProbeError(refLabel) {
return `Auth profile credentials are missing or expired.\n↳ Auth reason [unresolved_ref]: could not resolve SecretRef "${refLabel}".`;
}
function createProbeConfig(cfg, target) {
if (!target.useRuntimeAuth && !target.boundValue) return cfg;
const value = target.useRuntimeAuth ? void 0 : target.boundValue;
const entry = value ? resolveMergedModelProviderEntry(cfg, target.provider) : void 0;
const next = {
...cfg,
...entry?.providerConfig ? { models: {
...cfg.models,
providers: {
...cfg.models?.providers,
[entry.providerKey]: {
...entry.providerConfig,
apiKey: value,
auth: target.mode === "oauth" || target.mode === "token" ? target.mode : "api-key"
}
}
} } : {},
auth: {
...cfg.auth,
order: {
...cfg.auth?.order,
[target.provider]: []
}
}
};
if (entry?.providerConfig) copyConfigResolutionFactsExcept(cfg, next, [`${appendConfigPathSegment("models.providers", entry.providerKey)}.apiKey`]);
else copyConfigResolutionFacts(cfg, next);
return next;
}
async function resolveConfiguredProbeCredential(params) {
const ref = resolveConfigSecretRef({
config: params.cfg,
path: params.path,
value: params.input,
defaults: params.cfg.secrets?.defaults
});
if (!ref) return normalizeSecretInputString(params.input) ?? null;
try {
return await resolveSecretRefString(ref, {
config: params.cfg,
env: process.env,
cache: params.cache
});
} catch {
return null;
}
}
async function maybeResolveUnresolvedRefIssue(params) {
if (!params.profile) return null;
const ref = resolveProbeSecretRef(params.profile, params.cfg);
if (!ref) return null;
try {
await resolveSecretRefString(ref, {
config: params.cfg,
env: process.env,
cache: params.cache
});
return null;
} catch {
return {
reasonCode: "unresolved_ref",
error: formatUnresolvedRefProbeError(`${ref.source}:${ref.provider}:${ref.id}`)
};
}
}
async function buildProbeTargets(params) {
const { cfg, agentDir, providers, modelCandidates, options, workspaceDir } = params;
const authAliasLookupParams = {
config: cfg,
workspaceDir
};
const store = ensureAuthProfileStore(agentDir, { externalCli: externalCliDiscoveryScoped({
config: cfg,
allowKeychainPrompt: false,
providerIds: providers.map((provider) => resolveProviderIdForAuth(provider, authAliasLookupParams)),
profileIds: options.profileIds
}) });
const providerFilter = options.provider?.trim();
const providerFilterKey = providerFilter ? normalizeProviderId(providerFilter) : null;
const profileFilter = new Set(normalizeUniqueStringEntries(options.profileIds));
const refResolveCache = {};
const catalog = await readPreparedModelCatalog({
config: cfg,
...params.agentId ? { agentId: params.agentId } : {},
...agentDir ? { agentDir } : {},
...workspaceDir ? { workspaceDir } : {},
readOnly: true
});
const candidates = buildProbeCandidateMap(modelCandidates);
const targets = [];
const results = [];
const appendTarget = (target) => {
if (target.model) targets.push(target);
else results.push(buildNoModelProbeResult(target));
};
for (const provider of providers) {
const providerKey = normalizeProviderId(provider);
const authProviderKey = resolveProviderIdForAuth(providerKey, authAliasLookupParams);
if (providerFilterKey && providerKey !== providerFilterKey) continue;
const model = selectProbeModel({
provider: providerKey,
candidates,
catalog
});
const configuredProviderEntry = resolveMergedModelProviderEntry(cfg, providerKey);
const configuredProvider = configuredProviderEntry?.providerConfig;
const hasConfiguredProviderSecretRef = Boolean(configuredProviderEntry && resolveConfigSecretRef({
config: cfg,
path: `${appendConfigPathSegment("models.providers", configuredProviderEntry.providerKey)}.apiKey`,
value: configuredProvider?.apiKey,
defaults: cfg.secrets?.defaults
}));
const includeDirectKeys = options.includeDirectKeys === true && profileFilter.size === 0;
const includeConfigKey = includeDirectKeys && hasConfiguredSecretInput(configuredProvider?.apiKey, cfg.secrets?.defaults);
const profileIds = [.../* @__PURE__ */ new Set([...listProfilesForProvider(store, authProviderKey), ...authProviderKey === providerKey ? [] : listProfilesForProvider(store, providerKey)])];
const configuredReference = includeConfigKey ? resolveProviderEntryApiKeyProfileReference({
cfg,
provider: providerKey,
store
}) : { kind: "none" };
const configuredBinding = configuredReference.kind === "profile" && !profileIds.includes(configuredReference.profileId) ? await resolveProviderEntryApiKeyBinding({
cfg,
provider: providerKey,
store,
agentDir
}) : null;
const configuredValue = configuredProviderEntry && includeConfigKey && configuredReference.kind !== "profile" && configuredReference.kind !== "profile-incompatible" ? configuredReference.kind === "marker" ? resolveUsableCustomProviderApiKey({
cfg,
provider: providerKey,
env: process.env
})?.apiKey ?? null : await resolveConfiguredProbeCredential({
cfg,
input: configuredProvider?.apiKey,
path: `${appendConfigPathSegment("models.providers", configuredProviderEntry.providerKey)}.apiKey`,
cache: refResolveCache
}) : null;
const configuredMode = configuredProvider?.auth === "oauth" || configuredProvider?.auth === "token" ? configuredProvider.auth : "api_key";
const resolvedEnvironmentValue = includeDirectKeys && !hasConfiguredProviderSecretRef ? resolveEnvApiKey(authProviderKey, process.env, {
config: cfg,
workspaceDir
}) : null;
const environmentValue = resolvedEnvironmentValue?.apiKey === configuredValue ? null : resolvedEnvironmentValue;
const configuredTargetLabel = configuredReference.kind === "marker" && configuredValue && isNonSecretApiKeyMarker(configuredValue, { includeEnvVarName: false }) ? "provider" : "config";
const explicitOrder = findNormalizedProviderValue(store.order, authProviderKey) ?? findNormalizedProviderValue(store.order, providerKey) ?? findNormalizedProviderValue(cfg?.auth?.order, authProviderKey) ?? findNormalizedProviderValue(cfg?.auth?.order, providerKey);
const orderResolution = resolveAuthProfileOrderWithMetadata({
cfg,
store,
provider: providerKey,
forModel: model?.model
});
const allowedProfiles = orderResolution.hasExplicitOrder ? new Set(orderResolution.profileIds) : null;
const filteredProfiles = profileFilter.size ? profileIds.filter((id) => profileFilter.has(id)) : profileIds;
for (const profileId of filteredProfiles) {
const profile = store.profiles[profileId];
const mode = profile?.type;
const target = {
provider: providerKey,
model,
profileId,
label: resolveAuthProfileDisplayLabel({
cfg,
store,
profileId
}),
source: "profile",
mode
};
const isConfigBoundProfile = includeConfigKey && configuredReference.kind === "profile" && profileId === configuredReference.profileId;
let issue;
if (!isConfigBoundProfile && explicitOrder && !explicitOrder.includes(profileId)) issue = {
reasonCode: "excluded_by_auth_order",
error: "Excluded by auth.order for this provider."
};
else if (!isConfigBoundProfile && allowedProfiles && !allowedProfiles.has(profileId)) {
const reasonCode = mapEligibilityReasonToProbeReasonCode(resolveAuthProfileEligibility({
cfg,
store,
provider: providerKey,
profileId
}).reasonCode);
issue = {
reasonCode,
error: `Auth profile credentials are missing or expired.\n↳ Auth reason [${reasonCode}]: ${ELIGIBILITY_PROBE_ERRORS[reasonCode]}`
};
} else issue = await maybeResolveUnresolvedRefIssue({
cfg,
profile,
cache: refResolveCache
});
if (issue) results.push(buildProbeResult(target, {
status: "unknown",
...issue
}));
else appendTarget(target);
}
if (filteredProfiles.length === 0 && profileFilter.size > 0) continue;
if (includeConfigKey) {
const target = {
provider: providerKey,
model,
...configuredReference.kind === "profile" || configuredReference.kind === "profile-incompatible" ? { profileId: configuredReference.profileId } : {},
label: "config",
source: "models.json",
mode: configuredMode
};
const unresolvedResult = (error) => ({
...target,
model: model ? `${model.provider}/${model.model}` : void 0,
status: model ? "unknown" : "no_model",
reasonCode: model ? "unresolved_ref" : "no_model",
error: model ? error : "No model available for check"
});
if (configuredReference.kind === "profile-incompatible") results.push({
...target,
model: model ? `${model.provider}/${model.model}` : void 0,
status: "unknown",
reasonCode: "ineligible_profile",
error: "Configured API key references an incompatible auth profile."
});
else if (configuredReference.kind === "profile") {
if (!profileIds.includes(configuredReference.profileId)) {
if (configuredBinding?.kind === "profile-resolved" && model) targets.push({
...target,
profileId: configuredBinding.auth.profileId,
mode: configuredBinding.auth.mode,
boundValue: configuredBinding.auth.apiKey
});
else results.push(unresolvedResult("Configured auth profile could not be resolved."));
}
} else if (!configuredValue) results.push(unresolvedResult("Configured API key could not be resolved."));
else appendTarget({
...target,
label: configuredTargetLabel,
boundValue: configuredValue,
...configuredReference.kind === "marker" ? { useRuntimeAuth: true } : {}
});
}
if (environmentValue) {
const mode = configuredProvider?.auth === "oauth" || configuredProvider?.auth === "token" ? configuredProvider.auth : environmentValue.source.includes("OAUTH_TOKEN") ? "oauth" : "api_key";
appendTarget({
provider: providerKey,
model,
label: environmentValue.source,
source: "env",
mode,
boundValue: environmentValue.apiKey
});
}
if (filteredProfiles.length > 0 || includeConfigKey || environmentValue) continue;
const hasUsableModelsJsonKey = hasUsableCustomProviderApiKey(cfg, providerKey);
const hasSyntheticLocalAuth = hasSyntheticLocalProviderAuthConfig({
cfg,
provider: providerKey
});
if (orderResolution.hasExplicitOrder && !hasUsableModelsJsonKey && !hasSyntheticLocalAuth) continue;
const envKey = orderResolution.hasExplicitOrder || hasConfiguredProviderSecretRef ? null : resolveEnvApiKey(authProviderKey, process.env, {
config: cfg,
workspaceDir
});
if (!envKey && !hasUsableModelsJsonKey && !hasSyntheticLocalAuth) continue;
const source = envKey ? "env" : "models.json";
appendTarget({
provider: providerKey,
model,
label: source,
source,
mode: envKey?.source.includes("OAUTH_TOKEN") ? "oauth" : "api_key",
...hasSyntheticLocalAuth && !envKey && !hasUsableModelsJsonKey ? { useRuntimeAuth: true } : {}
});
}
return {
targets,
results
};
}
async function probeTarget(params) {
const { cfg, agentId, agentDir, workspaceDir, storePath, target, timeoutMs, maxTokens } = params;
const probeConfig = createProbeConfig(cfg, target);
if (!target.model) return buildNoModelProbeResult(target);
const model = target.model;
const runId = `probe-${target.provider}-${crypto.randomUUID()}`;
let isolatedAgentDir = null;
let isolatedAuthEnv;
let isolatedProfileId;
let sessionTarget;
let preparedRunAdmission;
const work = await createAuthProbeWork(params.abortSignal);
const start = Date.now();
const buildResult = (status, error) => buildProbeResult(target, {
status,
...error ? { error } : {},
latencyMs: Date.now() - start
});
try {
sessionTarget = await prepareInternalSessionEffectsSession({
agentId,
cwd: workspaceDir,
runId,
storePath
});
if (target.boundValue || target.useRuntimeAuth) {
isolatedAgentDir = await fs.realpath(await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-auth-probe-")));
isolatedAuthEnv = cloneEnvWithPlatformSemantics(getScopedAuthProfileEnv() ?? process.env);
}
if (target.boundValue && !target.useRuntimeAuth && isolatedAgentDir) {
isolatedProfileId = `${target.provider}:probe-${crypto.randomUUID()}`;
const value = target.boundValue;
const profile = target.mode === "oauth" ? {
type: "oauth",
provider: target.provider,
access: value,
refresh: "not-a-real",
expires: Date.now() + 36e5
} : target.mode === "token" ? {
type: "token",
provider: target.provider,
token: value
} : {
type: "api_key",
provider: target.provider,
key: value
};
if (!await upsertAuthProfileWithLock({
profileId: isolatedProfileId,
credential: profile,
agentDir: isolatedAgentDir
})) throw new Error("Could not prepare isolated auth check profile");
}
const { runEmbeddedAgent } = await import("./embedded-agent-WpG0US1O.mjs");
const probeSessionTarget = sessionTarget;
preparedRunAdmission = prepareSystemAgentRunAdmission(probeConfig, runId, agentId, "models.auth-probe");
const runResult = await work.run(() => runEmbeddedAgent({
preparedRunAdmission,
sessionId: probeSessionTarget.sessionId,
sessionKey: probeSessionTarget.sessionKey,
sessionTarget: probeSessionTarget,
agentId,
workspaceDir,
agentDir: isolatedAgentDir ?? agentDir,
config: probeConfig,
prompt: PROBE_PROMPT,
provider: model.provider,
model: model.model,
requestedRouteResolution: "resolved",
modelFallbacksOverride: [],
authProfileId: isolatedProfileId ?? target.profileId,
authProfileIdSource: isolatedProfileId || target.profileId ? "user" : void 0,
timeoutMs,
runId,
lane: `auth-probe:${target.provider}:${target.profileId ?? target.source}`,
thinkLevel: "off",
reasoningLevel: "off",
verboseLevel: "off",
streamParams: { maxTokens },
agentHarnessRuntimeOverride: "openclaw",
disableTools: true,
modelRun: true,
cleanupBundleMcpOnRunEnd: true,
...isolatedAgentDir ? { preparedModelRuntimeMode: "isolated-read-only" } : {},
abortSignal: params.abortSignal
}));
const terminalError = extractAgentRunTerminalError(runResult);
if (terminalError) throw new Error(terminalError);
if (!agentRunHasVisibleReply(runResult)) return buildResult("format", "The model did not return a visible check response.");
return buildResult("ok");
} catch (err) {
const described = describeFailoverError(err);
return buildResult(mapFailoverReasonToProbeStatus(described.reason), redactStatusSecrets(described.message));
} finally {
preparedRunAdmission?.close();
await work.settle(async () => {
const cleanups = [() => removeInternalSessionEffectsSession(sessionTarget)];
if (isolatedAgentDir) {
const ownedDir = isolatedAgentDir;
cleanups.push(() => disposeAuthProbeDirectory(ownedDir, isolatedAuthEnv));
}
const errors = [];
for (const cleanup of cleanups) try {
await cleanup();
} catch (error) {
errors.push(error);
}
if (errors.length === 1) throw errors[0];
if (errors.length > 1) throw new AggregateError(errors, "Auth check resources could not all be released", { cause: errors[0] });
});
}
}
async function runTargetsWithConcurrency(params) {
const { cfg, targets, options, onProgress } = params;
const { timeoutMs, maxTokens } = options;
const concurrency = Math.max(1, Math.min(targets.length || 1, options.concurrency));
const agentId = params.agentId ?? resolveDefaultAgentId(cfg);
const agentDir = params.agentDir ?? resolveAgentDir(cfg, agentId);
const workspaceDir = params.workspaceDir ?? resolveAgentWorkspaceDir(cfg, agentId);
const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId });
await fs.mkdir(workspaceDir, { recursive: true });
let completed = 0;
return await pMap(targets, async (target) => {
onProgress?.({
completed,
total: targets.length,
label: `Checking ${target.provider}${target.profileId ? ` (${target.label})` : ""}`
});
const result = await probeTarget({
cfg,
agentId,
agentDir,
workspaceDir,
storePath,
target,
timeoutMs,
maxTokens,
abortSignal: params.abortSignal
});
completed += 1;
onProgress?.({
completed,
total: targets.length
});
return result;
}, {
concurrency,
stopOnError: true,
...params.abortSignal ? { signal: params.abortSignal } : {}
});
}
function formatActiveGatewayModelsProbeRefusal(identity) {
return `A Gateway is running for this state directory (pid ${identity.pid}, port ${identity.port}). Stop the Gateway first (${formatCliCommand("openclaw gateway stop")}), then rerun models status --probe.`;
}
/** Own canonical state only for direct CLI probes; Gateway RPC probes already run under its lock. */
async function withAuthProbeStateOwnership(ownership, run) {
if (!ownership) return await run();
const { acquireEmbeddedStateLock, createEmbeddedStateSignalBridge } = await import("./embedded-state-lock-CKs5BZsU.mjs");
const signalBridge = createEmbeddedStateSignalBridge(ownership.process ?? process);
let stateLock;
let work;
try {
work = await createAuthProbeWork(signalBridge.signal);
stateLock = await acquireEmbeddedStateLock({
options: ownership.gatewayLockOptions,
signal: signalBridge.signal,
formatActiveGatewayRefusal: formatActiveGatewayModelsProbeRefusal
});
return await work.run(() => run(signalBridge.signal));
} finally {
const release = async () => {
try {
await stateLock?.release();
} finally {
signalBridge.dispose();
}
};
if (work) await work.settle(release);
else await release();
}
}
async function runAuthProbes(params) {
return await withAuthProbeStateOwnership(params.stateOwnership, async (abortSignal) => {
const startedAt = Date.now();
const plan = await buildProbeTargets(params);
const totalTargets = plan.targets.length;
params.onProgress?.({
completed: 0,
total: totalTargets
});
const results = totalTargets ? await runTargetsWithConcurrency({
...params,
targets: plan.targets,
abortSignal
}) : [];
const finishedAt = Date.now();
return {
startedAt,
finishedAt,
durationMs: finishedAt - startedAt,
totalTargets,
options: params.options,
results: [...plan.results, ...results]
};
});
}
function sortProbeResults(results) {
return results.toSorted((a, b) => {
const provider = a.provider.localeCompare(b.provider);
if (provider !== 0) return provider;
const aLabel = a.label || a.profileId || "";
const bLabel = b.label || b.profileId || "";
return aLabel.localeCompare(bLabel);
});
}
function describeProbeSummary(summary) {
if (summary.totalTargets === 0) return "No check targets.";
return `Checked ${summary.totalTargets} target${summary.totalTargets === 1 ? "" : "s"} in ${formatMs(summary.durationMs)}`;
}
//#endregion
export { sortProbeResults as a, runAuthProbes as i, describeProbeSummary as n, withAuthProbeStateOwnership as o, mapFailoverReasonToProbeStatus as r, buildProbeTargets as t };