| 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<T>( |
| parent: Command, |
| description: string, |
| collect: (cfg: OpenClawConfig, agentId: string) => T | Promise<T>, |
| format: (value: T) => string, |
| ): void { |
| parent |
| .command("providers") |
| .description(description) |
| .option("--agent <id>", "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<CapabilityTransport>; |
| 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<string, unknown>).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 <id> 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<string>(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<string, unknown>; |
| const pluginEntries = (params.cfg.plugins?.entries ?? {}) as Record<string, { config?: unknown }>; |
| const ttsProviders = (params.cfg.tts?.providers ?? {}) as Record<string, unknown>; |
| 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 <provider/model>."); |
| } |
| 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<string>; |
| allowedPaths?: Set<string>; |
| forcedActivePaths?: Set<string>; |
| optionalActivePaths?: Set<string>; |
| config?: OpenClawConfig; |
| }): Promise<OpenClawConfig> { |
| 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); |
| } |
| } |
|
|