UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

701 lines (700 loc) • 28.3 kB
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 };