UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

468 lines (467 loc) • 23.2 kB
import { i as createLazyRuntimeNamedExport } from "./lazy-runtime-CJrzFzqU.mjs"; import { h as resolveDefaultSecretProviderAlias } from "./ref-contract-D0kWy5jy.mjs"; import { t as formatErrorMessage } from "./errors-O33D01XW.mjs"; import { t as formatCliCommand } from "./command-format-DwEdrDKL.mjs"; import { t as quoteCliArg } from "./quote-cli-arg-BEt71TUh.mjs"; import { s as resolveAgentModelPrimaryValue } from "./model-input-C9K4a_6k.mjs"; import { i as resolveUtilityModelSeparationError, n as materializeUtilityModelSeparation } from "./utility-model-separation-migration-BVcPkqsj.mjs"; import { r as enablePluginWithCapabilityConsent } from "./enable-C2KEdu16.mjs"; import { n as resolveManifestDeprecatedProviderAuthChoice, r as resolveManifestProviderAuthChoice } from "./provider-auth-choices-DMYmVIgL.mjs"; import { n as buildProviderPluginMethodChoice, r as parseProviderPluginMethodChoice, t as PROVIDER_PLUGIN_CHOICE_PREFIX } from "./provider-plugin-choice-BugOkW_k.mjs"; import { n as resolveProviderInstallCatalogEntries, t as resolveDeprecatedProviderInstallCatalogEntry } from "./provider-install-catalog-CCgeBGs_.mjs"; import { o as prepareAgentModelDefaults, s as projectAgentModelDefaults } from "./onboard-agent-target-C1NZC0Wr.mjs"; import { a as normalizeSecretInputModeInput } from "./provider-auth-input-CpNkpIyx.mjs"; import { r as formatAuthChoiceChoicesForCli } from "./auth-choice-options-fF-ZM1DL.mjs"; import { t as resolveLegacyOnboardAuthChoice } from "./auth-choice-legacy-BxqOc5gX.mjs"; import { t as normalizeApiKeyTokenProviderAuthChoice } from "./auth-choice.apply.api-providers-Cb7_Cbhs.mjs"; import { r as ensureModelSelectionRuntimePlugins, t as CODEX_RUNTIME_PLUGIN_ID } from "./runtime-plugin-install-D-P8jolo.mjs"; import { t as createNonInteractiveLoggingPrompter } from "./non-interactive-prompter-C7yy4gSh.mjs"; import { t as resolvePreferredProviderForAuthChoice } from "./provider-auth-choice-preference-BirJ0g3u.mjs"; import { c as parseNonInteractiveCustomApiFlags, d as resolveCustomProviderId, n as applyCustomApiConfig, t as CustomApiError } from "./onboard-custom-config-CbZUo-Nd.mjs"; import { t as rejectOnboardingOption } from "./onboard-options-CNC8ppU5.mjs"; import { t as resolveNonInteractiveApiKey } from "./api-keys-FA5iEP02.mjs"; import path from "node:path"; import fs from "node:fs/promises"; import os from "node:os"; import { isDeepStrictEqual } from "node:util"; //#region src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.ts const loadAuthChoicePluginProvidersRuntime = createLazyRuntimeNamedExport(() => import("./auth-choice.plugin-providers.runtime.js"), "authChoicePluginProvidersRuntime"); /** Applies a plugin-defined auth choice, or returns undefined when it is not plugin-backed. */ async function applyNonInteractivePluginProviderChoice(params) { const { agentDir, workspaceDir } = params.target; const reject = (message) => { rejectOnboardingOption(params.opts, params.runtime, message); return null; }; let nextConfig = params.nextConfig; const prefixedProviderId = parseProviderPluginMethodChoice(params.authChoice)?.providerId; if (prefixedProviderId === "") return reject(`Auth choice ${JSON.stringify(params.authChoice)} is missing a provider id. Use "${PROVIDER_PLUGIN_CHOICE_PREFIX}<provider-id>".`); const preferredProviderId = prefixedProviderId || await resolvePreferredProviderForAuthChoice({ choice: params.authChoice, config: nextConfig, workspaceDir, includeUntrustedWorkspacePlugins: false }); const trustedManifestMatch = resolveManifestProviderAuthChoice(params.authChoice, { config: nextConfig, workspaceDir, includeUntrustedWorkspacePlugins: false }); if (trustedManifestMatch) { if (trustedManifestMatch.modelTarget === "utility") { const error = resolveUtilityModelSeparationError(params.baseConfig); if (error) return reject(error); } const enabled = await enablePluginWithCapabilityConsent(nextConfig, trustedManifestMatch.pluginId, { workspaceDir }); if (!enabled.enabled) return reject(enabled.reason ?? "Provider plugin could not be enabled."); nextConfig = enabled.config; } const { resolveOwningPluginIdsForProviderRef, resolveProviderPluginChoice, resolvePluginProviders } = await loadAuthChoicePluginProvidersRuntime(); const owningPluginIds = preferredProviderId ? resolveOwningPluginIdsForProviderRef({ provider: preferredProviderId, config: nextConfig, workspaceDir }) : void 0; const resolveManifestMethodChoice = (provider, method) => provider.pluginId ? resolveManifestProviderAuthChoice(buildProviderPluginMethodChoice(provider.id, method.id), { config: nextConfig, workspaceDir, pluginId: provider.pluginId, includeUntrustedWorkspacePlugins: false }) : void 0; let providerChoice = resolveProviderPluginChoice({ providers: resolvePluginProviders({ config: nextConfig, workspaceDir, onlyPluginIds: owningPluginIds, ...preferredProviderId ? { providerRefs: [preferredProviderId] } : {}, mode: "setup", includeUntrustedWorkspacePlugins: false }), choice: params.authChoice, manifestChoice: trustedManifestMatch, resolveManifestMethodChoice }); if (!providerChoice) { if (prefixedProviderId) return reject([`Auth choice "${params.authChoice}" was not matched to a trusted provider plugin.`, "If this provider comes from a workspace plugin, trust/allow it first and retry."].join("\n")); if (!trustedManifestMatch && resolveManifestProviderAuthChoice(params.authChoice, { config: nextConfig, workspaceDir, includeUntrustedWorkspacePlugins: true })) return reject([`Auth choice "${params.authChoice}" matched a provider plugin that is not trusted or enabled for setup.`, "If this provider comes from a workspace plugin, trust/allow it first and retry."].join("\n")); const normalizedChoiceId = params.authChoice.trim(); if (!normalizedChoiceId) return; const installCatalog = resolveProviderInstallCatalogEntries({ config: nextConfig, workspaceDir, includeUntrustedWorkspacePlugins: false }); const deprecatedInstallCatalogEntry = installCatalog.find((entry) => entry.deprecatedChoiceIds?.includes(normalizedChoiceId)); if (deprecatedInstallCatalogEntry) return reject(`${JSON.stringify(params.authChoice)} is no longer supported. Use --auth-choice ${JSON.stringify(deprecatedInstallCatalogEntry.choiceId)} instead.`); const installCatalogEntry = installCatalog.find((entry) => entry.choiceId === normalizedChoiceId); if (!installCatalogEntry) return; if (installCatalogEntry.modelTarget === "utility") { const error = resolveUtilityModelSeparationError(params.baseConfig); if (error) return reject(error); } const { ensureOnboardingPluginInstalled } = await import("./onboarding-plugin-install-C1HA9SfE.mjs"); const installResult = await ensureOnboardingPluginInstalled({ cfg: nextConfig, entry: { pluginId: installCatalogEntry.pluginId, label: installCatalogEntry.label, install: installCatalogEntry.install, ...installCatalogEntry.origin === "bundled" ? { trustedSourceLinkedOfficialInstall: true } : {} }, prompter: createNonInteractiveLoggingPrompter(params.runtime, (message) => `Non-interactive setup cannot prompt for plugin install: ${message}`), runtime: params.runtime, workspaceDir, promptInstall: false }); if (!installResult.installed) return reject(`Unable to install the ${installCatalogEntry.label} plugin for non-interactive setup.`); nextConfig = installResult.cfg; providerChoice = resolveProviderPluginChoice({ providers: resolvePluginProviders({ config: nextConfig, workspaceDir, onlyPluginIds: [installCatalogEntry.pluginId], providerRefs: [installCatalogEntry.providerId], mode: "setup", includeUntrustedWorkspacePlugins: false }), choice: params.authChoice, resolveManifestMethodChoice, manifestChoice: installCatalogEntry }); if (!providerChoice) return reject(`Installed plugin "${installCatalogEntry.label}" did not expose auth choice "${params.authChoice}".`); } if (providerChoice.wizard?.modelTarget === "utility") { const error = resolveUtilityModelSeparationError(params.baseConfig); if (error) return reject(error); } const enableResult = await enablePluginWithCapabilityConsent(nextConfig, providerChoice.provider.pluginId ?? providerChoice.provider.id, { workspaceDir }); if (!enableResult.enabled) return reject(`${providerChoice.provider.label} plugin is disabled (${enableResult.reason ?? "blocked"}).`); const method = providerChoice.method; const modelTarget = providerChoice.wizard?.modelTarget; if (!method.runNonInteractive) return reject([`Auth choice "${params.authChoice}" requires interactive mode.`, `The ${providerChoice.provider.label} provider plugin does not implement non-interactive setup.`].join("\n")); const agentScopedModels = enableResult.config.agents?.ownership === "explicit"; const providerConfig = agentScopedModels ? prepareAgentModelDefaults(enableResult.config, params.target) : enableResult.config; const projectProviderResult = (updated) => { const projected = agentScopedModels ? projectAgentModelDefaults(enableResult.config, params.target, updated) : updated; return modelTarget === "utility" ? materializeUtilityModelSeparation(projected, params.baseConfig).config : projected; }; const runNonInteractive = method.runNonInteractive; const context = { authChoice: params.authChoice, config: providerConfig, baseConfig: params.baseConfig, opts: params.opts, runtime: params.runtime, agentDir, workspaceDir, resolveApiKey: params.resolveApiKey, toApiKeyCredential: params.toApiKeyCredential }; const { isSetupCredentialReplacement, saveSetupCredential, selectSetupCredential } = await import("./setup-inference-credentials-N1h1-2Ly.mjs"); let result; if (isSetupCredentialReplacement({ provider: providerChoice.provider.id, baseConfig: params.baseConfig, agentDir })) { const [{ withAuthProfileStoreAgentDir, clearRuntimeAuthProfileStoreSnapshot }, { loadAuthProfileStoreWithoutExternalProfiles, saveAuthProfileStore }, { loadPersistedAuthProfileStore }, { closeAuthProfileReadPool }, { closeOpenClawAgentDatabases }, { splitTrailingAuthProfile }, { resolveSetupModel }, { prepareCustomSetupCredentials }] = await Promise.all([ import("./store-BPjXoHsf.mjs"), import("./store-runtime-Cs6vcjBe.mjs"), import("./persisted-DaYRFvNW.mjs"), import("./sqlite-BWQ-6YdR.mjs"), import("./openclaw-agent-db-DBfBqVnw.mjs"), import("./model-ref-profile-Yh67mgun.mjs"), import("./setup-inference-core-CX10X0r5.mjs"), import("./setup-inference-custom-TAHdmFtq.mjs") ]); const realStore = loadAuthProfileStoreWithoutExternalProfiles(agentDir); const seeded = { version: realStore.version, ...realStore.order ? { order: structuredClone(realStore.order) } : {}, profiles: Object.fromEntries(Object.entries(realStore.profiles).filter(([, credential]) => credential.type !== "oauth" && !credential.setup?.replacement)) }; const stagingRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-setup-credential-")); const stagingAgentDir = path.join(stagingRoot, "agents", "setup", "agent"); let savedProfileId; try { await fs.mkdir(stagingAgentDir, { recursive: true }); result = await withAuthProfileStoreAgentDir(stagingAgentDir, stagingRoot, async () => { saveAuthProfileStore(seeded, stagingAgentDir, { syncExternalCli: false }); return await runNonInteractive({ ...context, agentDir: stagingAgentDir, runtime: { ...params.runtime, exit: (code) => { throw new Error(`Provider setup exited with code ${code}; see its error above.`); } } }); }); if (!result) return null; const prepared = prepareCustomSetupCredentials({ config: structuredClone(result), providerId: providerChoice.provider.id }); const profiles = Object.entries(loadPersistedAuthProfileStore(stagingAgentDir)?.profiles ?? {}).filter(([profileId, credential]) => !isDeepStrictEqual(credential, seeded.profiles[profileId])).map(([profileId, credential]) => ({ profileId, credential })); if (!isDeepStrictEqual(result.models?.providers?.[providerChoice.provider.id]?.apiKey, providerConfig.models?.providers?.[providerChoice.provider.id]?.apiKey)) profiles.push(...prepared.profiles); if (profiles.length > 0) { const selected = modelTarget === "utility" ? result.agents?.defaults?.utilityModel?.trim() : resolveAgentModelPrimaryValue(result.agents?.defaults?.model); const modelRef = resolveSetupModel({ label: providerChoice.provider.label, providerId: providerChoice.provider.id, defaultModel: selected && selectSetupCredential(profiles, selected, prepared.config) ? splitTrailingAuthProfile(selected).model : method.starterModel }); if (typeof modelRef !== "string") throw new Error(modelRef.error); const profile = selectSetupCredential(profiles, modelRef, prepared.config); if (!profile) throw new Error("Provider setup did not save a replacement credential. Your connection is unchanged."); savedProfileId = (await saveSetupCredential({ profile, config: projectProviderResult(prepared.config), baseConfig: params.baseConfig, agentDir, modelRef, authChoice: trustedManifestMatch?.choiceId ?? providerChoice.wizard?.choiceId, pluginId: providerChoice.provider.pluginId })).profile.profileId; } } finally { clearRuntimeAuthProfileStoreSnapshot(stagingAgentDir); closeAuthProfileReadPool({ kind: "root", rootPath: stagingRoot }); closeOpenClawAgentDatabases(stagingRoot); await fs.rm(stagingRoot, { recursive: true, force: true }); } if (savedProfileId) return reject(`Replacement credential saved but inactive. Your connection is unchanged. Test and activate it with:\n${formatCliCommand(`openclaw models auth activate ${quoteCliArg(savedProfileId)} --agent ${quoteCliArg(params.target.agentId)}`)}`); } else result = await runNonInteractive(context); if (!result) return result; const selectedModel = modelTarget === "utility" ? result.agents?.defaults?.utilityModel?.trim() : resolveAgentModelPrimaryValue(result.agents?.defaults?.model); if (modelTarget === "utility") { if (!selectedModel) return reject("This provider did not return a utility model for setup."); const { model: _selectedPrimary, ...defaults } = result.agents?.defaults ?? {}; const primary = providerConfig.agents?.defaults?.model; result = { ...result, agents: { ...result.agents, defaults: { ...defaults, ...primary !== void 0 ? { model: primary } : {} } } }; } if (!selectedModel) return projectProviderResult(result); const runtimes = await ensureModelSelectionRuntimePlugins({ cfg: result, model: selectedModel, prompter: createNonInteractiveLoggingPrompter(params.runtime, (message) => message), runtime: params.runtime, workspaceDir, output: "silent" }); if (!runtimes.ok) return reject(runtimes.message); if (runtimes.codexInstalled) { const { offerPostInstallMigrations } = await import("./setup.post-install-migration-DxOJNEKQ.mjs"); await offerPostInstallMigrations({ config: runtimes.cfg, runtime: params.runtime, installedPluginIds: [CODEX_RUNTIME_PLUGIN_ID], nonInteractive: true }); } return projectProviderResult(runtimes.cfg); } //#endregion //#region src/commands/onboard-non-interactive/local/auth-choice.ts /** Applies a local non-interactive auth choice to the pending OpenClaw config. */ async function applyNonInteractiveAuthChoice(params) { const { opts, runtime, baseConfig } = params; let authChoice = normalizeApiKeyTokenProviderAuthChoice({ authChoice: params.authChoice, tokenProvider: opts.tokenProvider, config: params.nextConfig, workspaceDir: params.target.workspaceDir, env: process.env }); const nextConfig = params.nextConfig; const requestedSecretInputMode = normalizeSecretInputModeInput(opts.secretInputMode); if (opts.secretInputMode && !requestedSecretInputMode) { rejectOnboardingOption(opts, runtime, `Invalid --secret-input-mode. Use "plaintext" or "ref", or run ${formatCliCommand("openclaw onboard")} for interactive setup.`); return null; } const toStoredSecretInput = (paramsLocal) => { const { resolved } = paramsLocal; if (requestedSecretInputMode !== "ref") return resolved.key; if (resolved.source !== "env" || !resolved.envVarName) { const envHint = paramsLocal.envVarName ? `Set ${paramsLocal.envVarName} in env and retry` : "Set the provider API key env var and retry"; rejectOnboardingOption(opts, runtime, [`--secret-input-mode ref requires an explicit environment variable for provider "${paramsLocal.provider}".`, `${envHint}, or use --secret-input-mode plaintext.`].join("\n")); return null; } return { source: "env", provider: resolveDefaultSecretProviderAlias(baseConfig, "env", { preferFirstProviderForSource: true }), id: resolved.envVarName }; }; const resolveApiKey = (input) => resolveNonInteractiveApiKey({ ...input, cfg: nextConfig, runtime, agentDir: params.target.agentDir, workspaceDir: params.target.workspaceDir, secretInputMode: requestedSecretInputMode, json: opts.json }); const toApiKeyCredential = (paramsLocal) => { const stored = toStoredSecretInput({ resolved: paramsLocal.resolved, provider: paramsLocal.provider }); if (!stored) return null; return { type: "api_key", provider: paramsLocal.provider, ...typeof stored === "string" ? { key: stored } : { keyRef: stored }, ...paramsLocal.email ? { email: paramsLocal.email } : {}, ...paramsLocal.metadata ? { metadata: paramsLocal.metadata } : {} }; }; const legacyChoice = resolveLegacyOnboardAuthChoice(authChoice, { config: nextConfig, workspaceDir: params.target.workspaceDir, env: process.env }); if (legacyChoice.deprecated) { runtime.log(legacyChoice.deprecated.message); authChoice = legacyChoice.authChoice; } const deprecatedChoice = resolveManifestDeprecatedProviderAuthChoice(authChoice, { config: nextConfig, workspaceDir: params.target.workspaceDir, env: process.env }); const deprecatedInstallChoice = deprecatedChoice ? void 0 : resolveDeprecatedProviderInstallCatalogEntry(authChoice, { config: nextConfig, workspaceDir: params.target.workspaceDir, env: process.env, includeUntrustedWorkspacePlugins: false }); const replacementChoiceId = deprecatedChoice?.choiceId ?? deprecatedInstallChoice?.choiceId; if (replacementChoiceId) { rejectOnboardingOption(opts, runtime, `${JSON.stringify(authChoice)} is no longer supported. Use --auth-choice ${JSON.stringify(replacementChoiceId)} instead.`); return null; } const validAuthChoices = formatAuthChoiceChoicesForCli({ config: nextConfig, workspaceDir: params.target.workspaceDir, env: process.env }).split("|"); if (!validAuthChoices.includes(authChoice) && !authChoice.startsWith("provider-plugin:")) { rejectOnboardingOption(opts, runtime, `Unknown --auth-choice ${JSON.stringify(authChoice)}. Valid choices: ${validAuthChoices.join(", ")}.`); return null; } const pluginProviderChoice = await applyNonInteractivePluginProviderChoice({ nextConfig, authChoice, opts, runtime, baseConfig, target: params.target, resolveApiKey, toApiKeyCredential }); if (pluginProviderChoice !== void 0) return pluginProviderChoice; if (authChoice === "setup-token" || authChoice === "token") { rejectOnboardingOption(opts, runtime, [`Auth choice "${params.authChoice}" was not matched to a provider setup flow.`, "For Anthropic legacy token auth, use \"--auth-choice setup-token --token-provider anthropic --token <token>\" or pass \"--auth-choice token --token-provider anthropic\"."].join("\n")); return null; } if (authChoice === "custom-api-key") try { const customAuth = parseNonInteractiveCustomApiFlags({ baseUrl: opts.customBaseUrl, modelId: opts.customModelId, compatibility: opts.customCompatibility, apiKey: opts.customApiKey, providerId: opts.customProviderId, supportsImageInput: opts.customImageInput }); const resolvedProviderId = resolveCustomProviderId({ config: nextConfig, baseUrl: customAuth.baseUrl, providerId: customAuth.providerId }); const resolvedCustomApiKey = await resolveApiKey({ provider: resolvedProviderId.providerId, flagValue: customAuth.apiKey, flagName: "--custom-api-key", envVar: "CUSTOM_API_KEY", envVarName: "CUSTOM_API_KEY", required: false }); let customApiKeyInput; if (resolvedCustomApiKey && (requestedSecretInputMode !== "ref" || resolvedCustomApiKey.source !== "profile")) { const stored = toStoredSecretInput({ resolved: resolvedCustomApiKey, provider: resolvedProviderId.providerId, envVarName: "CUSTOM_API_KEY" }); if (!stored) return null; customApiKeyInput = stored; } const result = applyCustomApiConfig({ config: nextConfig, baseUrl: customAuth.baseUrl, modelId: customAuth.modelId, compatibility: customAuth.compatibility, apiKey: customApiKeyInput, providerId: customAuth.providerId, supportsImageInput: customAuth.supportsImageInput, target: params.target }); if (result.providerIdRenamedFrom && result.providerId) runtime.log(`Custom provider ID "${result.providerIdRenamedFrom}" already exists for a different base URL. Using "${result.providerId}".`); if (customApiKeyInput !== void 0 && resolvedCustomApiKey?.source !== "profile") { const { isSetupCredentialReplacement, saveSetupCredential } = await import("./setup-inference-credentials-N1h1-2Ly.mjs"); if (isSetupCredentialReplacement({ provider: result.providerId, baseConfig, agentDir: params.target.agentDir })) { const { prepareCustomSetupCredentials } = await import("./setup-inference-custom-TAHdmFtq.mjs"); const prepared = prepareCustomSetupCredentials(result); const saved = await saveSetupCredential({ profile: prepared.profiles[0], config: prepared.config, baseConfig, agentDir: params.target.agentDir, modelRef: `${result.providerId}/${result.modelId}` }); rejectOnboardingOption(opts, runtime, `Replacement credential saved but inactive. Your connection is unchanged. Test and activate it with:\n${formatCliCommand(`openclaw models auth activate ${quoteCliArg(saved.profile.profileId)} --agent ${quoteCliArg(params.target.agentId)}`)}`); return null; } } return result.config; } catch (err) { const message = err instanceof CustomApiError && (err.code === "missing_required" || err.code === "invalid_compatibility") ? err.message : `Invalid custom provider config: ${err instanceof CustomApiError ? err.message : formatErrorMessage(err)}`; rejectOnboardingOption(opts, runtime, message); return null; } if (authChoice === "chutes" || authChoice === "minimax-global-oauth" || authChoice === "minimax-cn-oauth") { rejectOnboardingOption(opts, runtime, "OAuth requires interactive mode."); return null; } return nextConfig; } //#endregion export { applyNonInteractiveAuthChoice };