fix(secrets): align ref contracts and non-interactive ref persistence

This commit is contained in:
joshavant
2026-02-25 19:36:32 -06:00
committed by Peter Steinberger
parent 86622ebea9
commit 8944b75e16
11 changed files with 680 additions and 340 deletions

View File

@@ -184,6 +184,34 @@ describe("ensureApiKeyFromEnvOrPrompt", () => {
expect(text).not.toHaveBeenCalled();
});
it("fails ref mode without select when fallback env var is missing", async () => {
delete process.env.MINIMAX_API_KEY;
delete process.env.MINIMAX_OAUTH_TOKEN;
const { confirm, text } = createPromptSpies({
confirmResult: true,
textResult: "prompt-key",
});
const setCredential = vi.fn(async () => undefined);
await expect(
ensureApiKeyFromEnvOrPrompt({
config: {},
provider: "minimax",
envLabel: "MINIMAX_API_KEY",
promptMessage: "Enter key",
normalize: (value) => value.trim(),
validate: () => undefined,
prompter: createPrompter({ confirm, text }),
secretInputMode: "ref",
setCredential,
}),
).rejects.toThrow(
'Environment variable "MINIMAX_API_KEY" is required for --secret-input-mode ref in non-interactive onboarding.',
);
expect(setCredential).not.toHaveBeenCalled();
});
it("re-prompts after provider ref validation failure and succeeds with env ref", async () => {
process.env.MINIMAX_API_KEY = "env-key";
delete process.env.MINIMAX_OAUTH_TOKEN;

View File

@@ -1,12 +1,12 @@
import { resolveEnvApiKey } from "../agents/model-auth.js";
import type { OpenClawConfig } from "../config/types.js";
import {
DEFAULT_SECRET_PROVIDER_ALIAS,
type SecretInput,
type SecretRef,
} from "../config/types.secrets.js";
import { type SecretInput, type SecretRef } from "../config/types.secrets.js";
import { encodeJsonPointerToken } from "../secrets/json-pointer.js";
import { PROVIDER_ENV_VARS } from "../secrets/provider-env-vars.js";
import {
isValidFileSecretRefId,
resolveDefaultSecretProviderAlias,
} from "../secrets/ref-contract.js";
import { resolveSecretRefString } from "../secrets/resolve.js";
import type { WizardPrompter } from "../wizard/prompts.js";
import { formatApiKeyPreview } from "./auth-choice.api-key.js";
@@ -16,20 +16,9 @@ import type { SecretInputMode } from "./onboard-types.js";
const ENV_SOURCE_LABEL_RE = /(?:^|:\s)([A-Z][A-Z0-9_]*)$/;
const ENV_SECRET_REF_ID_RE = /^[A-Z][A-Z0-9_]{0,127}$/;
const FILE_SECRET_REF_SEGMENT_RE = /^(?:[^~]|~0|~1)*$/;
type SecretRefChoice = "env" | "provider";
function isValidFileSecretRefId(value: string): boolean {
if (!value.startsWith("/")) {
return false;
}
return value
.slice(1)
.split("/")
.every((segment) => FILE_SECRET_REF_SEGMENT_RE.test(segment));
}
function formatErrorMessage(error: unknown): string {
if (error instanceof Error && typeof error.message === "string" && error.message.trim()) {
return error.message;
@@ -51,59 +40,33 @@ function resolveDefaultFilePointerId(provider: string): string {
return `/providers/${encodeJsonPointerToken(provider)}/apiKey`;
}
function resolveDefaultProviderAlias(
config: OpenClawConfig,
source: "env" | "file" | "exec",
): string {
const configured =
source === "env"
? config.secrets?.defaults?.env
: source === "file"
? config.secrets?.defaults?.file
: config.secrets?.defaults?.exec;
if (configured?.trim()) {
return configured.trim();
}
const providers = config.secrets?.providers;
if (providers) {
for (const [providerName, provider] of Object.entries(providers)) {
if (provider?.source === source) {
return providerName;
}
}
}
return DEFAULT_SECRET_PROVIDER_ALIAS;
}
function resolveRefFallbackInput(params: {
config: OpenClawConfig;
provider: string;
preferredEnvVar?: string;
envKeyValue?: string;
}): { input: SecretInput; resolvedValue: string } {
}): { ref: SecretRef; resolvedValue: string } {
const fallbackEnvVar = params.preferredEnvVar ?? resolveDefaultProviderEnvVar(params.provider);
if (fallbackEnvVar) {
const value = process.env[fallbackEnvVar]?.trim();
if (value) {
return {
input: {
source: "env",
provider: resolveDefaultProviderAlias(params.config, "env"),
id: fallbackEnvVar,
},
resolvedValue: value,
};
}
if (!fallbackEnvVar) {
throw new Error(
`No default environment variable mapping found for provider "${params.provider}". Set a provider-specific env var, or re-run onboarding in an interactive terminal to configure a ref.`,
);
}
if (params.envKeyValue?.trim()) {
return {
input: params.envKeyValue.trim(),
resolvedValue: params.envKeyValue.trim(),
};
const value = process.env[fallbackEnvVar]?.trim();
if (!value) {
throw new Error(
`Environment variable "${fallbackEnvVar}" is required for --secret-input-mode ref in non-interactive onboarding.`,
);
}
throw new Error(
`No environment variable found for provider "${params.provider}". Re-run onboarding in an interactive terminal to set a secret reference.`,
);
return {
ref: {
source: "env",
provider: resolveDefaultSecretProviderAlias(params.config, "env", {
preferFirstProviderForSource: true,
}),
id: fallbackEnvVar,
},
resolvedValue: value,
};
}
async function resolveApiKeyRefForOnboarding(params: {
@@ -163,7 +126,9 @@ async function resolveApiKeyRefForOnboarding(params: {
}
const ref: SecretRef = {
source: "env",
provider: resolveDefaultProviderAlias(params.config, "env"),
provider: resolveDefaultSecretProviderAlias(params.config, "env", {
preferFirstProviderForSource: true,
}),
id: envVar,
};
const resolvedValue = await resolveSecretRefString(ref, {
@@ -187,7 +152,9 @@ async function resolveApiKeyRefForOnboarding(params: {
);
continue;
}
const defaultProvider = resolveDefaultProviderAlias(params.config, "file");
const defaultProvider = resolveDefaultSecretProviderAlias(params.config, "file", {
preferFirstProviderForSource: true,
});
const selectedProvider = await params.prompter.select<string>({
message: "Select secret provider",
initialValue:
@@ -477,9 +444,8 @@ export async function ensureApiKeyFromEnvOrPrompt(params: {
config: params.config,
provider: params.provider,
preferredEnvVar: envKey?.source ? extractEnvVarFromSourceLabel(envKey.source) : undefined,
envKeyValue: envKey?.apiKey,
});
await params.setCredential(fallback.input, selectedMode);
await params.setCredential(fallback.ref, selectedMode);
return fallback.resolvedValue;
}
const resolved = await resolveApiKeyRefForOnboarding({

View File

@@ -442,6 +442,36 @@ describe("onboard (non-interactive): provider auth", () => {
},
);
it("stores the detected env alias as keyRef for opencode ref mode", async () => {
await withOnboardEnv("openclaw-onboard-ref-opencode-alias-", async ({ runtime }) => {
await withEnvAsync(
{
OPENCODE_API_KEY: undefined,
OPENCODE_ZEN_API_KEY: "opencode-zen-env-key",
},
async () => {
await runNonInteractiveOnboardingWithDefaults(runtime, {
authChoice: "opencode-zen",
secretInputMode: "ref",
skipSkills: true,
});
const store = ensureAuthProfileStore();
const profile = store.profiles["opencode:default"];
expect(profile?.type).toBe("api_key");
if (profile?.type === "api_key") {
expect(profile.key).toBeUndefined();
expect(profile.keyRef).toEqual({
source: "env",
provider: "default",
id: "OPENCODE_ZEN_API_KEY",
});
}
},
);
});
});
it("rejects vLLM auth choice in non-interactive mode", async () => {
await withOnboardEnv("openclaw-onboard-vllm-non-interactive-", async ({ runtime }) => {
await expect(

View File

@@ -11,6 +11,14 @@ import type { SecretInputMode } from "../onboard-types.js";
export type NonInteractiveApiKeySource = "flag" | "env" | "profile";
function parseEnvVarNameFromSourceLabel(source: string | undefined): string | undefined {
if (!source) {
return undefined;
}
const match = /^(?:shell env: |env: )([A-Z][A-Z0-9_]*)$/.exec(source.trim());
return match?.[1];
}
async function resolveApiKeyFromProfiles(params: {
provider: string;
cfg: OpenClawConfig;
@@ -52,7 +60,7 @@ export async function resolveNonInteractiveApiKey(params: {
allowProfile?: boolean;
required?: boolean;
secretInputMode?: SecretInputMode;
}): Promise<{ key: string; source: NonInteractiveApiKeySource } | null> {
}): Promise<{ key: string; source: NonInteractiveApiKeySource; envVarName?: string } | null> {
const flagKey = normalizeOptionalSecretInput(params.flagValue);
const envResolved = resolveEnvApiKey(params.provider);
const explicitEnvVar = params.envVarName?.trim();
@@ -60,6 +68,7 @@ export async function resolveNonInteractiveApiKey(params: {
? normalizeOptionalSecretInput(process.env[explicitEnvVar])
: undefined;
const resolvedEnvKey = envResolved?.apiKey ?? explicitEnvKey;
const resolvedEnvVarName = parseEnvVarNameFromSourceLabel(envResolved?.source) ?? explicitEnvVar;
if (params.secretInputMode === "ref") {
if (!resolvedEnvKey && flagKey) {
@@ -73,7 +82,17 @@ export async function resolveNonInteractiveApiKey(params: {
return null;
}
if (resolvedEnvKey) {
return { key: resolvedEnvKey, source: "env" };
if (!resolvedEnvVarName) {
params.runtime.error(
[
`--secret-input-mode ref requires an explicit environment variable for provider "${params.provider}".`,
`Set ${params.envVar} in env and retry, or use --secret-input-mode plaintext.`,
].join("\n"),
);
params.runtime.exit(1);
return null;
}
return { key: resolvedEnvKey, source: "env", envVarName: resolvedEnvVarName };
}
}
@@ -82,7 +101,7 @@ export async function resolveNonInteractiveApiKey(params: {
}
if (resolvedEnvKey) {
return { key: resolvedEnvKey, source: "env" };
return { key: resolvedEnvKey, source: "env", envVarName: resolvedEnvVarName };
}
if (params.allowProfile ?? true) {

View File

@@ -4,6 +4,7 @@ import { parseDurationMs } from "../../../cli/parse-duration.js";
import type { OpenClawConfig } from "../../../config/config.js";
import type { SecretInput } from "../../../config/types.secrets.js";
import type { RuntimeEnv } from "../../../runtime.js";
import { resolveDefaultSecretProviderAlias } from "../../../secrets/ref-contract.js";
import { normalizeSecretInput } from "../../../utils/normalize-secret-input.js";
import { normalizeSecretInputModeInput } from "../../auth-choice.apply-helpers.js";
import { buildTokenProfileId, validateAnthropicSetupToken } from "../../auth-token.js";
@@ -67,6 +68,10 @@ import { applyOpenAIConfig } from "../../openai-model-default.js";
import { detectZaiEndpoint } from "../../zai-endpoint-detect.js";
import { resolveNonInteractiveApiKey } from "../api-keys.js";
type ResolvedNonInteractiveApiKey = NonNullable<
Awaited<ReturnType<typeof resolveNonInteractiveApiKey>>
>;
export async function applyNonInteractiveAuthChoice(params: {
nextConfig: OpenClawConfig;
authChoice: AuthChoice;
@@ -85,13 +90,50 @@ export async function applyNonInteractiveAuthChoice(params: {
const apiKeyStorageOptions = requestedSecretInputMode
? { secretInputMode: requestedSecretInputMode }
: undefined;
const resolveApiKey = (
input: Parameters<typeof resolveNonInteractiveApiKey>[0],
): ReturnType<typeof resolveNonInteractiveApiKey> =>
const toStoredSecretInput = (resolved: ResolvedNonInteractiveApiKey): SecretInput | null => {
if (requestedSecretInputMode !== "ref") {
return resolved.key;
}
if (resolved.source !== "env") {
return resolved.key;
}
if (!resolved.envVarName) {
runtime.error(
[
`Unable to determine which environment variable to store as a ref for provider "${authChoice}".`,
"Set an explicit provider env var and retry, or use --secret-input-mode plaintext.",
].join("\n"),
);
runtime.exit(1);
return null;
}
return {
source: "env",
provider: resolveDefaultSecretProviderAlias(baseConfig, "env", {
preferFirstProviderForSource: true,
}),
id: resolved.envVarName,
};
};
const resolveApiKey = (input: Parameters<typeof resolveNonInteractiveApiKey>[0]) =>
resolveNonInteractiveApiKey({
...input,
secretInputMode: requestedSecretInputMode,
});
const maybeSetResolvedApiKey = async (
resolved: ResolvedNonInteractiveApiKey,
setter: (value: SecretInput) => Promise<void> | void,
): Promise<boolean> => {
if (resolved.source === "profile") {
return true;
}
const stored = toStoredSecretInput(resolved);
if (!stored) {
return false;
}
await setter(stored);
return true;
};
if (authChoice === "claude-cli" || authChoice === "codex-cli") {
runtime.error(
@@ -138,8 +180,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setAnthropicApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setAnthropicApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
return applyAuthProfileConfig(nextConfig, {
profileId: "anthropic:default",
@@ -215,8 +261,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setGeminiApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setGeminiApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "google:default",
@@ -244,8 +294,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setZaiApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setZaiApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "zai:default",
@@ -293,8 +347,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setXiaomiApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setXiaomiApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "xiaomi:default",
@@ -316,8 +374,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
setXaiApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setXaiApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "xai:default",
@@ -339,8 +401,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setMistralApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setMistralApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "mistral:default",
@@ -362,8 +428,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setVolcengineApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setVolcengineApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "volcengine:default",
@@ -385,8 +455,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setByteplusApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setByteplusApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "byteplus:default",
@@ -408,8 +482,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
setQianfanApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setQianfanApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "qianfan:default",
@@ -431,8 +509,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setOpenaiApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setOpenaiApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "openai:default",
@@ -454,8 +536,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setOpenrouterApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setOpenrouterApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "openrouter:default",
@@ -477,8 +563,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setKilocodeApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setKilocodeApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "kilocode:default",
@@ -500,8 +590,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setLitellmApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setLitellmApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "litellm:default",
@@ -523,8 +617,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setVercelAiGatewayApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setVercelAiGatewayApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "vercel-ai-gateway:default",
@@ -559,10 +657,14 @@ export async function applyNonInteractiveAuthChoice(params: {
return null;
}
if (resolved.source !== "profile") {
const stored = toStoredSecretInput(resolved);
if (!stored) {
return null;
}
await setCloudflareAiGatewayConfig(
accountId,
gatewayId,
resolved.key,
stored,
undefined,
apiKeyStorageOptions,
);
@@ -592,8 +694,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setMoonshotApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setMoonshotApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "moonshot:default",
@@ -623,8 +729,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setKimiCodingApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setKimiCodingApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "kimi-coding:default",
@@ -646,8 +756,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setSyntheticApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setSyntheticApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "synthetic:default",
@@ -669,8 +783,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setVeniceApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setVeniceApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "venice:default",
@@ -700,8 +818,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setMinimaxApiKey(resolved.key, undefined, profileId, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setMinimaxApiKey(value, undefined, profileId, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId,
@@ -731,8 +853,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setOpencodeZenApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setOpencodeZenApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "opencode:default",
@@ -754,8 +880,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setTogetherApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setTogetherApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "together:default",
@@ -777,8 +907,12 @@ export async function applyNonInteractiveAuthChoice(params: {
if (!resolved) {
return null;
}
if (resolved.source !== "profile") {
await setHuggingfaceApiKey(resolved.key, undefined, apiKeyStorageOptions);
if (
!(await maybeSetResolvedApiKey(resolved, (value) =>
setHuggingfaceApiKey(value, undefined, apiKeyStorageOptions),
))
) {
return null;
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "huggingface:default",
@@ -812,10 +946,18 @@ export async function applyNonInteractiveAuthChoice(params: {
runtime,
required: false,
});
const customApiKeyInput: SecretInput | undefined =
requestedSecretInputMode === "ref" && resolvedCustomApiKey?.source === "env"
? { source: "env", provider: "default", id: "CUSTOM_API_KEY" }
: resolvedCustomApiKey?.key;
let customApiKeyInput: SecretInput | undefined;
if (resolvedCustomApiKey) {
if (requestedSecretInputMode === "ref") {
const stored = toStoredSecretInput(resolvedCustomApiKey);
if (!stored) {
return null;
}
customApiKeyInput = stored;
} else {
customApiKeyInput = resolvedCustomApiKey.key;
}
}
const result = applyCustomApiConfig({
config: nextConfig,
baseUrl: customAuth.baseUrl,