import { parseStrictFiniteNumber, parseStrictPositiveInteger, } from "@openclaw/normalization-core/number-coercion"; import type { Command } from "commander"; import { resolveAgentOperationAgentId, resolveConfiguredAgentId, } from "../../agents/agent-scope-config.js"; import { resolveAgentDir } from "../../agents/agent-scope.js"; import { listProfilesForProvider, loadAuthProfileStoreForRuntime, } from "../../agents/auth-profiles.js"; import { getRuntimeConfig, getRuntimeConfigSourceSnapshot, setRuntimeConfigSnapshot, } from "../../config/config.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { defaultRuntime } from "../../runtime.js"; import { getProviderEnvVars } from "../../secrets/provider-env-vars.js"; import { runCommandWithRuntime } from "../cli-utils.js"; import { resolveCommandConfigWithSecrets } from "../command-config-resolution.js"; import { inheritOptionFromParent } from "../command-options.js"; import { parseTimeoutMsWithFallback } from "../parse-timeout.js"; import type { CapabilityTransport } from "./metadata.js"; import { emitJsonOrText } from "./output.js"; export function registerLocalProvidersCommand( parent: Command, description: string, collect: (cfg: OpenClawConfig, agentId: string) => T | Promise, format: (value: T) => string, ): void { parent .command("providers") .description(description) .option("--agent ", "Agent whose provider state should be inspected") .option("--json", "Output JSON", false) .action(async (opts, command) => { await runCommandWithRuntime(defaultRuntime, async () => { const cfg = getRuntimeConfig(); const agentId = resolveCapabilityProviderAgentId( cfg, resolveCapabilityAgentOption(command, opts.agent), ); const result = await collect(cfg, agentId); emitJsonOrText(defaultRuntime, Boolean(opts.json), result, format); }); }); } export function resolveTransport(opts: { local?: boolean; gateway?: boolean; supported: Array; defaultTransport: CapabilityTransport; }): CapabilityTransport { if (opts.local && opts.gateway) { throw new Error("Pass only one of --local or --gateway."); } if (opts.local) { if (!opts.supported.includes("local")) { throw new Error("This command does not support --local."); } return "local"; } if (opts.gateway) { if (!opts.supported.includes("gateway")) { throw new Error("This command does not support --gateway."); } return "gateway"; } return opts.defaultTransport; } function hasOwnKeys(value: unknown): boolean { return Boolean( value && typeof value === "object" && Object.keys(value as Record).length > 0, ); } export function resolveSelectedProviderFromModelRef( modelRef: string | undefined, ): string | undefined { return resolveModelRefOverride(modelRef).provider; } export function resolveCapabilityProviderAgentId( cfg: OpenClawConfig, rawAgentId: string | undefined, surface = "inference provider inspection", ): string { const requestedAgentId = rawAgentId?.trim(); if (rawAgentId !== undefined && !requestedAgentId) { throw new Error("--agent must not be blank"); } const agentId = resolveAgentOperationAgentId(cfg, requestedAgentId, { surface, hint: "Pass --agent or set agents.defaults.systemAgent.agentId.", }); return resolveConfiguredAgentId(cfg, agentId); } export function resolveCapabilityAgentOption( command: Command | undefined, rawAgentId: unknown, ): string | undefined { return typeof rawAgentId === "string" ? rawAgentId : inheritOptionFromParent(command, "agent"); } function getAuthProfileIdsForProvider( cfg: OpenClawConfig, providerId: string, agentId: string, ): string[] { const agentDir = resolveAgentDir(cfg, agentId); const store = loadAuthProfileStoreForRuntime(agentDir); return listProfilesForProvider(store, providerId); } export function providerHasGenericConfig(params: { cfg: OpenClawConfig; providerId: string; /** Omit only for aggregate/global callers that intentionally exclude agent auth stores. */ agentId?: string; envVars?: string[]; }): boolean { const modelsProviders = (params.cfg.models?.providers ?? {}) as Record; const pluginEntries = (params.cfg.plugins?.entries ?? {}) as Record; const ttsProviders = (params.cfg.tts?.providers ?? {}) as Record; const envVars = params.envVars ?? getProviderEnvVars(params.providerId, { config: params.cfg, includeUntrustedWorkspacePlugins: false, }); const envConfigured = envVars.some((envVar) => Boolean(process.env[envVar]?.trim())); return ( (params.agentId ? getAuthProfileIdsForProvider(params.cfg, params.providerId, params.agentId).length > 0 : false) || hasOwnKeys(modelsProviders[params.providerId]) || hasOwnKeys(pluginEntries[params.providerId]?.config) || hasOwnKeys(ttsProviders[params.providerId]) || envConfigured ); } export function resolveModelRefOverride(raw: string | undefined): { provider?: string; model?: string; } { const trimmed = raw?.trim(); if (!trimmed) { return {}; } const slash = trimmed.indexOf("/"); if (slash <= 0 || slash === trimmed.length - 1) { return { model: trimmed }; } return { provider: trimmed.slice(0, slash), model: trimmed.slice(slash + 1), }; } export function requireProviderModelOverride( raw: string | undefined, ): { provider: string; model: string } | undefined { const resolved = resolveModelRefOverride(raw); if (!raw?.trim()) { return undefined; } if (!resolved.provider || !resolved.model) { throw new Error("Model overrides must use the form ."); } return { provider: resolved.provider, model: resolved.model, }; } export function parseOptionalFiniteNumber( raw: string | number | undefined, label: string, ): number | undefined { if (raw === undefined) { return undefined; } const value = parseStrictFiniteNumber(raw); if (value === undefined) { throw new Error(`${label} must be a finite number`); } return value; } export function parseOptionalPositiveInteger(raw: unknown, label: string): number | undefined { if (raw === undefined) { return undefined; } const value = parseStrictPositiveInteger(raw); if (value === undefined) { throw new Error(`${label} must be a positive integer`); } return value; } export function parseOptionalTimeoutMs(raw: string | number | undefined): number | undefined { if (raw === undefined) { return undefined; } return parseTimeoutMsWithFallback(raw, 0, { invalidType: "error" }); } export async function resolveLocalCapabilityRuntimeConfig(params: { commandName: string; targetIds: Set; allowedPaths?: Set; forcedActivePaths?: Set; optionalActivePaths?: Set; config?: OpenClawConfig; }): Promise { const cfg = params.config ?? getRuntimeConfig(); const { effectiveConfig } = await resolveCommandConfigWithSecrets({ config: cfg, commandName: params.commandName, targetIds: params.targetIds, ...(params.allowedPaths ? { allowedPaths: params.allowedPaths } : {}), ...(params.forcedActivePaths ? { forcedActivePaths: params.forcedActivePaths } : {}), ...(params.optionalActivePaths ? { optionalActivePaths: params.optionalActivePaths } : {}), runtime: defaultRuntime, autoEnable: true, }); pinRuntimeConfigSnapshot(effectiveConfig); return effectiveConfig; } export function pinRuntimeConfigSnapshot(config: OpenClawConfig): void { const sourceConfig = getRuntimeConfigSourceSnapshot(); if (sourceConfig) { setRuntimeConfigSnapshot(config, sourceConfig); } else { setRuntimeConfigSnapshot(config); } }