327 lines
10 KiB
TypeScript
327 lines
10 KiB
TypeScript
import {
|
|
CLAUDE_CLI_PROFILE_ID,
|
|
CODEX_CLI_PROFILE_ID,
|
|
ensureAuthProfileStore,
|
|
upsertAuthProfile,
|
|
} from "../../../agents/auth-profiles.js";
|
|
import { normalizeProviderId } from "../../../agents/model-selection.js";
|
|
import { parseDurationMs } from "../../../cli/parse-duration.js";
|
|
import type { ClawdbotConfig } from "../../../config/config.js";
|
|
import { upsertSharedEnvVar } from "../../../infra/env-file.js";
|
|
import type { RuntimeEnv } from "../../../runtime.js";
|
|
import { buildTokenProfileId, validateAnthropicSetupToken } from "../../auth-token.js";
|
|
import { applyGoogleGeminiModelDefault } from "../../google-gemini-model-default.js";
|
|
import {
|
|
applyAuthProfileConfig,
|
|
applyMinimaxApiConfig,
|
|
applyMinimaxConfig,
|
|
applyMoonshotConfig,
|
|
applyOpencodeZenConfig,
|
|
applyOpenrouterConfig,
|
|
applySyntheticConfig,
|
|
applyZaiConfig,
|
|
setAnthropicApiKey,
|
|
setGeminiApiKey,
|
|
setMinimaxApiKey,
|
|
setMoonshotApiKey,
|
|
setOpencodeZenApiKey,
|
|
setOpenrouterApiKey,
|
|
setSyntheticApiKey,
|
|
setZaiApiKey,
|
|
} from "../../onboard-auth.js";
|
|
import type { AuthChoice, OnboardOptions } from "../../onboard-types.js";
|
|
import { applyOpenAICodexModelDefault } from "../../openai-codex-model-default.js";
|
|
import { resolveNonInteractiveApiKey } from "../api-keys.js";
|
|
|
|
export async function applyNonInteractiveAuthChoice(params: {
|
|
nextConfig: ClawdbotConfig;
|
|
authChoice: AuthChoice;
|
|
opts: OnboardOptions;
|
|
runtime: RuntimeEnv;
|
|
baseConfig: ClawdbotConfig;
|
|
}): Promise<ClawdbotConfig | null> {
|
|
const { authChoice, opts, runtime, baseConfig } = params;
|
|
let nextConfig = params.nextConfig;
|
|
|
|
if (authChoice === "apiKey") {
|
|
const resolved = await resolveNonInteractiveApiKey({
|
|
provider: "anthropic",
|
|
cfg: baseConfig,
|
|
flagValue: opts.anthropicApiKey,
|
|
flagName: "--anthropic-api-key",
|
|
envVar: "ANTHROPIC_API_KEY",
|
|
runtime,
|
|
});
|
|
if (!resolved) return null;
|
|
if (resolved.source !== "profile") await setAnthropicApiKey(resolved.key);
|
|
return applyAuthProfileConfig(nextConfig, {
|
|
profileId: "anthropic:default",
|
|
provider: "anthropic",
|
|
mode: "api_key",
|
|
});
|
|
}
|
|
|
|
if (authChoice === "token") {
|
|
const providerRaw = opts.tokenProvider?.trim();
|
|
if (!providerRaw) {
|
|
runtime.error("Missing --token-provider for --auth-choice token.");
|
|
runtime.exit(1);
|
|
return null;
|
|
}
|
|
const provider = normalizeProviderId(providerRaw);
|
|
if (provider !== "anthropic") {
|
|
runtime.error("Only --token-provider anthropic is supported for --auth-choice token.");
|
|
runtime.exit(1);
|
|
return null;
|
|
}
|
|
const tokenRaw = opts.token?.trim();
|
|
if (!tokenRaw) {
|
|
runtime.error("Missing --token for --auth-choice token.");
|
|
runtime.exit(1);
|
|
return null;
|
|
}
|
|
const tokenError = validateAnthropicSetupToken(tokenRaw);
|
|
if (tokenError) {
|
|
runtime.error(tokenError);
|
|
runtime.exit(1);
|
|
return null;
|
|
}
|
|
|
|
let expires: number | undefined;
|
|
const expiresInRaw = opts.tokenExpiresIn?.trim();
|
|
if (expiresInRaw) {
|
|
try {
|
|
expires = Date.now() + parseDurationMs(expiresInRaw, { defaultUnit: "d" });
|
|
} catch (err) {
|
|
runtime.error(`Invalid --token-expires-in: ${String(err)}`);
|
|
runtime.exit(1);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
const profileId = opts.tokenProfileId?.trim() || buildTokenProfileId({ provider, name: "" });
|
|
upsertAuthProfile({
|
|
profileId,
|
|
credential: {
|
|
type: "token",
|
|
provider,
|
|
token: tokenRaw.trim(),
|
|
...(expires ? { expires } : {}),
|
|
},
|
|
});
|
|
return applyAuthProfileConfig(nextConfig, {
|
|
profileId,
|
|
provider,
|
|
mode: "token",
|
|
});
|
|
}
|
|
|
|
if (authChoice === "gemini-api-key") {
|
|
const resolved = await resolveNonInteractiveApiKey({
|
|
provider: "google",
|
|
cfg: baseConfig,
|
|
flagValue: opts.geminiApiKey,
|
|
flagName: "--gemini-api-key",
|
|
envVar: "GEMINI_API_KEY",
|
|
runtime,
|
|
});
|
|
if (!resolved) return null;
|
|
if (resolved.source !== "profile") await setGeminiApiKey(resolved.key);
|
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
profileId: "google:default",
|
|
provider: "google",
|
|
mode: "api_key",
|
|
});
|
|
return applyGoogleGeminiModelDefault(nextConfig).next;
|
|
}
|
|
|
|
if (authChoice === "zai-api-key") {
|
|
const resolved = await resolveNonInteractiveApiKey({
|
|
provider: "zai",
|
|
cfg: baseConfig,
|
|
flagValue: opts.zaiApiKey,
|
|
flagName: "--zai-api-key",
|
|
envVar: "ZAI_API_KEY",
|
|
runtime,
|
|
});
|
|
if (!resolved) return null;
|
|
if (resolved.source !== "profile") await setZaiApiKey(resolved.key);
|
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
profileId: "zai:default",
|
|
provider: "zai",
|
|
mode: "api_key",
|
|
});
|
|
return applyZaiConfig(nextConfig);
|
|
}
|
|
|
|
if (authChoice === "openai-api-key") {
|
|
const resolved = await resolveNonInteractiveApiKey({
|
|
provider: "openai",
|
|
cfg: baseConfig,
|
|
flagValue: opts.openaiApiKey,
|
|
flagName: "--openai-api-key",
|
|
envVar: "OPENAI_API_KEY",
|
|
runtime,
|
|
allowProfile: false,
|
|
});
|
|
if (!resolved) return null;
|
|
const key = resolved.key;
|
|
const result = upsertSharedEnvVar({ key: "OPENAI_API_KEY", value: key });
|
|
process.env.OPENAI_API_KEY = key;
|
|
runtime.log(`Saved OPENAI_API_KEY to ${result.path}`);
|
|
return nextConfig;
|
|
}
|
|
|
|
if (authChoice === "openrouter-api-key") {
|
|
const resolved = await resolveNonInteractiveApiKey({
|
|
provider: "openrouter",
|
|
cfg: baseConfig,
|
|
flagValue: opts.openrouterApiKey,
|
|
flagName: "--openrouter-api-key",
|
|
envVar: "OPENROUTER_API_KEY",
|
|
runtime,
|
|
});
|
|
if (!resolved) return null;
|
|
if (resolved.source !== "profile") await setOpenrouterApiKey(resolved.key);
|
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
profileId: "openrouter:default",
|
|
provider: "openrouter",
|
|
mode: "api_key",
|
|
});
|
|
return applyOpenrouterConfig(nextConfig);
|
|
}
|
|
|
|
if (authChoice === "moonshot-api-key") {
|
|
const resolved = await resolveNonInteractiveApiKey({
|
|
provider: "moonshot",
|
|
cfg: baseConfig,
|
|
flagValue: opts.moonshotApiKey,
|
|
flagName: "--moonshot-api-key",
|
|
envVar: "MOONSHOT_API_KEY",
|
|
runtime,
|
|
});
|
|
if (!resolved) return null;
|
|
if (resolved.source !== "profile") await setMoonshotApiKey(resolved.key);
|
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
profileId: "moonshot:default",
|
|
provider: "moonshot",
|
|
mode: "api_key",
|
|
});
|
|
return applyMoonshotConfig(nextConfig);
|
|
}
|
|
|
|
if (authChoice === "synthetic-api-key") {
|
|
const resolved = await resolveNonInteractiveApiKey({
|
|
provider: "synthetic",
|
|
cfg: baseConfig,
|
|
flagValue: opts.syntheticApiKey,
|
|
flagName: "--synthetic-api-key",
|
|
envVar: "SYNTHETIC_API_KEY",
|
|
runtime,
|
|
});
|
|
if (!resolved) return null;
|
|
if (resolved.source !== "profile") await setSyntheticApiKey(resolved.key);
|
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
profileId: "synthetic:default",
|
|
provider: "synthetic",
|
|
mode: "api_key",
|
|
});
|
|
return applySyntheticConfig(nextConfig);
|
|
}
|
|
|
|
if (
|
|
authChoice === "minimax-cloud" ||
|
|
authChoice === "minimax-api" ||
|
|
authChoice === "minimax-api-lightning"
|
|
) {
|
|
const resolved = await resolveNonInteractiveApiKey({
|
|
provider: "minimax",
|
|
cfg: baseConfig,
|
|
flagValue: opts.minimaxApiKey,
|
|
flagName: "--minimax-api-key",
|
|
envVar: "MINIMAX_API_KEY",
|
|
runtime,
|
|
});
|
|
if (!resolved) return null;
|
|
if (resolved.source !== "profile") await setMinimaxApiKey(resolved.key);
|
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
profileId: "minimax:default",
|
|
provider: "minimax",
|
|
mode: "api_key",
|
|
});
|
|
const modelId =
|
|
authChoice === "minimax-api-lightning" ? "MiniMax-M2.1-lightning" : "MiniMax-M2.1";
|
|
return applyMinimaxApiConfig(nextConfig, modelId);
|
|
}
|
|
|
|
if (authChoice === "claude-cli") {
|
|
const store = ensureAuthProfileStore(undefined, {
|
|
allowKeychainPrompt: false,
|
|
});
|
|
if (!store.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
|
runtime.error(
|
|
process.platform === "darwin"
|
|
? 'No Claude Code CLI credentials found. Run interactive onboarding to approve Keychain access for "Claude Code-credentials".'
|
|
: "No Claude Code CLI credentials found at ~/.claude/.credentials.json",
|
|
);
|
|
runtime.exit(1);
|
|
return null;
|
|
}
|
|
return applyAuthProfileConfig(nextConfig, {
|
|
profileId: CLAUDE_CLI_PROFILE_ID,
|
|
provider: "anthropic",
|
|
mode: "token",
|
|
});
|
|
}
|
|
|
|
if (authChoice === "codex-cli") {
|
|
const store = ensureAuthProfileStore();
|
|
if (!store.profiles[CODEX_CLI_PROFILE_ID]) {
|
|
runtime.error("No Codex CLI credentials found at ~/.codex/auth.json");
|
|
runtime.exit(1);
|
|
return null;
|
|
}
|
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
profileId: CODEX_CLI_PROFILE_ID,
|
|
provider: "openai-codex",
|
|
mode: "oauth",
|
|
});
|
|
return applyOpenAICodexModelDefault(nextConfig).next;
|
|
}
|
|
|
|
if (authChoice === "minimax") return applyMinimaxConfig(nextConfig);
|
|
|
|
if (authChoice === "opencode-zen") {
|
|
const resolved = await resolveNonInteractiveApiKey({
|
|
provider: "opencode",
|
|
cfg: baseConfig,
|
|
flagValue: opts.opencodeZenApiKey,
|
|
flagName: "--opencode-zen-api-key",
|
|
envVar: "OPENCODE_API_KEY (or OPENCODE_ZEN_API_KEY)",
|
|
runtime,
|
|
});
|
|
if (!resolved) return null;
|
|
if (resolved.source !== "profile") await setOpencodeZenApiKey(resolved.key);
|
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
profileId: "opencode:default",
|
|
provider: "opencode",
|
|
mode: "api_key",
|
|
});
|
|
return applyOpencodeZenConfig(nextConfig);
|
|
}
|
|
|
|
if (
|
|
authChoice === "oauth" ||
|
|
authChoice === "chutes" ||
|
|
authChoice === "openai-codex" ||
|
|
authChoice === "antigravity"
|
|
) {
|
|
const label = authChoice === "antigravity" ? "Antigravity" : "OAuth";
|
|
runtime.error(`${label} requires interactive mode.`);
|
|
runtime.exit(1);
|
|
return null;
|
|
}
|
|
|
|
return nextConfig;
|
|
}
|