fix(anthropic): remove setup-token onboarding path

This commit is contained in:
Peter Steinberger 2026-04-04 15:45:57 +09:00
parent 15bee338e9
commit 1b4bb5be19
No known key found for this signature in database
15 changed files with 175 additions and 307 deletions

View File

@ -20,18 +20,7 @@
"assistantPriority": -20,
"groupId": "anthropic",
"groupLabel": "Anthropic",
"groupHint": "Claude CLI + setup-token + API key"
},
{
"provider": "anthropic",
"method": "setup-token",
"choiceId": "token",
"choiceLabel": "Anthropic token (paste setup-token)",
"choiceHint": "Run `claude setup-token` elsewhere, then paste the token here",
"assistantVisibility": "manual-only",
"groupId": "anthropic",
"groupLabel": "Anthropic",
"groupHint": "Claude CLI + setup-token + API key"
"groupHint": "Claude CLI + API key"
},
{
"provider": "anthropic",
@ -40,7 +29,7 @@
"choiceLabel": "Anthropic API key",
"groupId": "anthropic",
"groupLabel": "Anthropic",
"groupHint": "Claude CLI + setup-token + API key",
"groupHint": "Claude CLI + API key",
"optionKey": "anthropicApiKey",
"cliFlag": "--anthropic-api-key",
"cliOption": "--anthropic-api-key <key>",

View File

@ -1,27 +1,19 @@
import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
import { parseDurationMs } from "openclaw/plugin-sdk/cli-runtime";
import type {
OpenClawPluginApi,
ProviderAuthContext,
ProviderResolveDynamicModelContext,
ProviderRuntimeModel,
} from "openclaw/plugin-sdk/plugin-entry";
import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
import {
CLAUDE_CLI_PROFILE_ID,
applyAuthProfileConfig,
buildTokenProfileId,
ensureApiKeyFromOptionEnvOrPrompt,
listProfilesForProvider,
normalizeApiKeyInput,
normalizeSecretInput,
normalizeSecretInputModeInput,
promptSecretRefForSetup,
resolveSecretInputModeForEnvSelection,
suggestOAuthProfileIdForLegacyDefault,
type AuthProfileStore,
type ProviderAuthResult,
upsertAuthProfile,
validateAnthropicSetupToken,
validateApiKeyInput,
} from "openclaw/plugin-sdk/provider-auth";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
@ -167,144 +159,6 @@ function buildAnthropicAuthDoctorHint(params: {
].join("\n");
}
async function runAnthropicSetupToken(ctx: ProviderAuthContext): Promise<ProviderAuthResult> {
await ctx.prompter.note(
["Run `claude setup-token` in your terminal.", "Then paste the generated token below."].join(
"\n",
),
"Anthropic setup-token",
);
const requestedSecretInputMode = normalizeSecretInputModeInput(ctx.secretInputMode);
const selectedMode = ctx.allowSecretRefPrompt
? await resolveSecretInputModeForEnvSelection({
prompter: ctx.prompter,
explicitMode: requestedSecretInputMode,
copy: {
modeMessage: "How do you want to provide this setup token?",
plaintextLabel: "Paste setup token now",
plaintextHint: "Stores the token directly in the auth profile",
},
})
: "plaintext";
let token = "";
let tokenRef: { source: "env" | "file" | "exec"; provider: string; id: string } | undefined;
if (selectedMode === "ref") {
const resolved = await promptSecretRefForSetup({
provider: "anthropic-setup-token",
config: ctx.config,
prompter: ctx.prompter,
preferredEnvVar: "ANTHROPIC_SETUP_TOKEN",
copy: {
sourceMessage: "Where is this Anthropic setup token stored?",
envVarPlaceholder: "ANTHROPIC_SETUP_TOKEN",
},
});
token = resolved.resolvedValue.trim();
tokenRef = resolved.ref;
} else {
const tokenRaw = await ctx.prompter.text({
message: "Paste Anthropic setup-token",
validate: (value) => validateAnthropicSetupToken(String(value ?? "")),
});
token = String(tokenRaw ?? "").trim();
}
const tokenError = validateAnthropicSetupToken(token);
if (tokenError) {
throw new Error(tokenError);
}
const profileNameRaw = await ctx.prompter.text({
message: "Token name (blank = default)",
placeholder: "default",
});
return {
profiles: [
{
profileId: buildTokenProfileId({
provider: PROVIDER_ID,
name: String(profileNameRaw ?? ""),
}),
credential: {
type: "token",
provider: PROVIDER_ID,
token,
...(tokenRef ? { tokenRef } : {}),
},
},
],
};
}
async function runAnthropicSetupTokenNonInteractive(ctx: {
config: ProviderAuthContext["config"];
opts: {
tokenProvider?: string;
token?: string;
tokenExpiresIn?: string;
tokenProfileId?: string;
};
runtime: ProviderAuthContext["runtime"];
agentDir?: string;
}): Promise<ProviderAuthContext["config"] | null> {
const provider = ctx.opts.tokenProvider?.trim().toLowerCase();
if (!provider) {
ctx.runtime.error("Missing --token-provider for --auth-choice token.");
ctx.runtime.exit(1);
return null;
}
if (provider !== PROVIDER_ID) {
ctx.runtime.error("Only --token-provider anthropic is supported for --auth-choice token.");
ctx.runtime.exit(1);
return null;
}
const token = normalizeSecretInput(ctx.opts.token);
if (!token) {
ctx.runtime.error("Missing --token for --auth-choice token.");
ctx.runtime.exit(1);
return null;
}
const tokenError = validateAnthropicSetupToken(token);
if (tokenError) {
ctx.runtime.error(tokenError);
ctx.runtime.exit(1);
return null;
}
let expires: number | undefined;
const expiresInRaw = ctx.opts.tokenExpiresIn?.trim();
if (expiresInRaw) {
try {
expires = Date.now() + parseDurationMs(expiresInRaw, { defaultUnit: "d" });
} catch (err) {
ctx.runtime.error(`Invalid --token-expires-in: ${String(err)}`);
ctx.runtime.exit(1);
return null;
}
}
const profileId =
ctx.opts.tokenProfileId?.trim() || buildTokenProfileId({ provider: PROVIDER_ID, name: "" });
upsertAuthProfile({
profileId,
agentDir: ctx.agentDir,
credential: {
type: "token",
provider: PROVIDER_ID,
token,
...(expires ? { expires } : {}),
},
});
return applyAuthProfileConfig(ctx.config, {
profileId,
provider: PROVIDER_ID,
mode: "token",
});
}
async function runAnthropicCliMigration(ctx: ProviderAuthContext): Promise<ProviderAuthResult> {
if (!hasClaudeCliAuth()) {
throw new Error(
@ -385,7 +239,7 @@ export async function registerAnthropicPlugin(api: OpenClawPluginApi): Promise<v
assistantPriority: -20,
groupId: "anthropic",
groupLabel: "Anthropic",
groupHint: "Claude CLI + setup-token + API key",
groupHint: "Claude CLI + API key",
modelAllowlist: {
allowedKeys: [...ANTHROPIC_OAUTH_ALLOWLIST].map((model) =>
model.replace(/^anthropic\//, "claude-cli/"),
@ -401,34 +255,6 @@ export async function registerAnthropicPlugin(api: OpenClawPluginApi): Promise<v
runtime: ctx.runtime,
}),
},
{
id: "setup-token",
label: "setup-token (claude)",
hint: "Paste a setup-token from `claude setup-token`",
kind: "token",
wizard: {
choiceId: "token",
choiceLabel: "Anthropic token (paste setup-token)",
choiceHint: "Run `claude setup-token` elsewhere, then paste the token here",
assistantVisibility: "manual-only",
groupId: "anthropic",
groupLabel: "Anthropic",
groupHint: "Claude CLI + setup-token + API key",
modelAllowlist: {
allowedKeys: [...ANTHROPIC_OAUTH_ALLOWLIST],
initialSelections: ["anthropic/claude-sonnet-4-6"],
message: "Anthropic OAuth models",
},
},
run: async (ctx: ProviderAuthContext) => await runAnthropicSetupToken(ctx),
runNonInteractive: async (ctx) =>
await runAnthropicSetupTokenNonInteractive({
config: ctx.config,
opts: ctx.opts,
runtime: ctx.runtime,
agentDir: ctx.agentDir,
}),
},
createProviderApiKeyAuthMethod({
providerId: PROVIDER_ID,
methodId: "api-key",
@ -445,7 +271,7 @@ export async function registerAnthropicPlugin(api: OpenClawPluginApi): Promise<v
choiceLabel: "Anthropic API key",
groupId: "anthropic",
groupLabel: "Anthropic",
groupHint: "Claude CLI + setup-token + API key",
groupHint: "Claude CLI + API key",
},
}),
],
@ -481,7 +307,8 @@ export async function registerAnthropicPlugin(api: OpenClawPluginApi): Promise<v
? "adaptive"
: undefined,
resolveUsageAuth: async (ctx) => await ctx.resolveOAuthToken(),
fetchUsageSnapshot: async (ctx) => await fetchClaudeUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn),
fetchUsageSnapshot: async (ctx) =>
await fetchClaudeUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn),
isCacheTtlEligible: () => true,
buildAuthDoctorHint: (ctx) =>
buildAnthropicAuthDoctorHint({

View File

@ -25,7 +25,7 @@ describe("anthropic stream wrappers", () => {
vi.restoreAllMocks();
});
it("strips context-1m for subscription setup-token auth and warns", () => {
it("strips context-1m for Claude CLI or legacy token auth and warns", () => {
const warn = vi.spyOn(__testing.log, "warn").mockImplementation(() => undefined);
const headers = runWrapper("sk-ant-oat01-123");
expect(headers?.["anthropic-beta"]).toBeDefined();

View File

@ -128,7 +128,7 @@ export function createAnthropicBetaHeadersWrapper(
: betas;
if (isOauth && requestedContext1m) {
log.warn(
`ignoring context1m for Anthropic subscription (OAuth setup-token) auth on ${model.provider}/${model.id}; falling back to the standard context window because Anthropic rejects context-1m beta with OAuth auth`,
`ignoring context1m for Anthropic Claude CLI or legacy token auth on ${model.provider}/${model.id}; falling back to the standard context window because Anthropic rejects context-1m beta with non-API-key auth`,
);
}

View File

@ -295,7 +295,7 @@ export function registerModelsCli(program: Command) {
auth
.command("add")
.description("Interactive auth helper (setup-token or paste token)")
.description("Interactive auth helper (provider auth or paste token)")
.action(async () => {
await runModelsCommand(async () => {
await modelsAuthAddCommand({}, defaultRuntime);
@ -324,7 +324,7 @@ export function registerModelsCli(program: Command) {
auth
.command("setup-token")
.description("Run a provider CLI to create/sync a token (TTY required)")
.option("--provider <name>", "Provider id (default: anthropic)")
.option("--provider <name>", "Provider id")
.option("--yes", "Skip confirmation", false)
.action(async (opts) => {
await runModelsCommand(async () => {

View File

@ -19,11 +19,6 @@ describe("auth choice legacy aliases", () => {
});
it("sources deprecated cli aliases from plugin manifests", () => {
expect(resolveLegacyAuthChoiceAliasesForCli()).toEqual([
"setup-token",
"oauth",
"claude-cli",
"codex-cli",
]);
expect(resolveLegacyAuthChoiceAliasesForCli()).toEqual(["claude-cli", "codex-cli"]);
});
});

View File

@ -1,9 +1,9 @@
import type { OpenClawConfig } from "../config/config.js";
import type { AuthChoice } from "./onboard-types.js";
import {
resolveManifestDeprecatedProviderAuthChoice,
resolveManifestProviderAuthChoices,
} from "../plugins/provider-auth-choices.js";
import type { AuthChoice } from "./onboard-types.js";
function resolveLegacyCliBackendChoice(
choice: string,
@ -32,7 +32,7 @@ export function resolveLegacyAuthChoiceAliasesForCli(params?: {
.flatMap((choice) => choice.deprecatedChoiceIds ?? [])
.filter((choice): choice is AuthChoice => choice.endsWith("-cli"))
.toSorted((left, right) => left.localeCompare(right));
return ["setup-token", "oauth", ...manifestCliAliases];
return manifestCliAliases;
}
export function normalizeLegacyOnboardAuthChoice(

View File

@ -57,15 +57,6 @@ describe("buildAuthChoiceOptions", () => {
groupId: "copilot",
groupLabel: "Copilot",
},
{
pluginId: "anthropic",
providerId: "anthropic",
methodId: "setup-token",
choiceId: "token",
choiceLabel: "Anthropic token (paste setup-token)",
groupId: "anthropic",
groupLabel: "Anthropic",
},
{
pluginId: "openai",
providerId: "openai",
@ -202,7 +193,6 @@ describe("buildAuthChoiceOptions", () => {
for (const value of [
"github-copilot",
"token",
"zai-api-key",
"xiaomi-api-key",
"minimax-global-api",
@ -295,8 +285,6 @@ describe("buildAuthChoiceOptions", () => {
includeSkip: true,
}).split("|");
expect(cliChoices).toContain("setup-token");
expect(cliChoices).toContain("oauth");
expect(cliChoices).toContain("claude-cli");
expect(cliChoices).toContain("codex-cli");
});
@ -378,7 +366,7 @@ describe("buildAuthChoiceOptions", () => {
expect(litellmGroup?.options.some((opt) => opt.value === "litellm-api-key")).toBe(true);
});
it("prefers Anthropic Claude CLI over API key and hides manual-only setup-token in grouped selection", () => {
it("prefers Anthropic Claude CLI over API key in grouped selection", () => {
resolveManifestProviderAuthChoices.mockReturnValue([
{
pluginId: "anthropic",
@ -399,16 +387,6 @@ describe("buildAuthChoiceOptions", () => {
groupId: "anthropic",
groupLabel: "Anthropic",
},
{
pluginId: "anthropic",
providerId: "anthropic",
methodId: "setup-token",
choiceId: "token",
choiceLabel: "Anthropic token (paste setup-token)",
assistantVisibility: "manual-only",
groupId: "anthropic",
groupLabel: "Anthropic",
},
]);
const { groups } = buildAuthChoiceGroups({
store: EMPTY_STORE,

View File

@ -1,12 +1,12 @@
import type { OpenClawConfig } from "../config/config.js";
import { applyAuthChoiceLoadedPluginProvider } from "../plugins/provider-auth-choice.js";
import type { RuntimeEnv } from "../runtime.js";
import type { WizardPrompter } from "../wizard/prompts.js";
import type { AuthChoice, OnboardOptions } from "./onboard-types.js";
import { applyAuthChoiceLoadedPluginProvider } from "../plugins/provider-auth-choice.js";
import { normalizeLegacyOnboardAuthChoice } from "./auth-choice-legacy.js";
import { applyAuthChoiceApiProviders } from "./auth-choice.apply.api-providers.js";
import { normalizeApiKeyTokenProviderAuthChoice } from "./auth-choice.apply.api-providers.js";
import { applyAuthChoiceOAuth } from "./auth-choice.apply.oauth.js";
import type { AuthChoice, OnboardOptions } from "./onboard-types.js";
export type ApplyAuthChoiceParams = {
authChoice: AuthChoice;
@ -56,5 +56,19 @@ export async function applyAuthChoice(
}
}
if (
normalizedParams.authChoice === "token" ||
normalizedParams.authChoice === "setup-token" ||
normalizedParams.authChoice === "oauth"
) {
throw new Error(
[
`Auth choice "${normalizedParams.authChoice}" is no longer supported for Anthropic setup in OpenClaw.`,
"Existing Anthropic token profiles still run if they are already configured.",
'Use "anthropic-cli" or "apiKey" instead.',
].join("\n"),
);
}
return { config: normalizedParams.config };
}

View File

@ -1,19 +1,19 @@
import fs from "node:fs/promises";
import type { OAuthCredentials } from "@mariozechner/pi-ai";
import fs from "node:fs/promises";
import { afterEach, describe, expect, it, vi } from "vitest";
import { resolveAgentDir } from "../agents/agent-scope.js";
import type { OpenClawConfig } from "../config/config.js";
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
import type { ModelProviderConfig } from "../config/types.models.js";
import type { ProviderAuthMethod, ProviderPlugin } from "../plugins/types.js";
import type { WizardPrompter } from "../wizard/prompts.js";
import type { AuthChoice } from "./onboard-types.js";
import { resolveAgentDir } from "../agents/agent-scope.js";
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
import { GOOGLE_GEMINI_DEFAULT_MODEL } from "../plugin-sdk/google.js";
import { MINIMAX_CN_API_BASE_URL } from "../plugin-sdk/minimax.js";
import { ZAI_CODING_CN_BASE_URL, ZAI_CODING_GLOBAL_BASE_URL } from "../plugin-sdk/zai.js";
import { createProviderApiKeyAuthMethod } from "../plugins/provider-api-key-auth.js";
import { providerApiKeyAuthRuntime } from "../plugins/provider-api-key-auth.runtime.js";
import type { ProviderAuthMethod, ProviderPlugin } from "../plugins/types.js";
import type { WizardPrompter } from "../wizard/prompts.js";
import { applyAuthChoice, resolvePreferredProviderForAuthChoice } from "./auth-choice.js";
import type { AuthChoice } from "./onboard-types.js";
import {
authProfilePathForAgent,
createAuthTestLifecycle,
@ -651,6 +651,27 @@ describe("applyAuthChoice", () => {
resolvePluginProviders.mockReturnValue(createDefaultProviderPlugins());
it("rejects legacy Anthropic token setup aliases", async () => {
await setupTempState();
await expect(
applyAuthChoice({
authChoice: "token",
config: {} as OpenClawConfig,
prompter: createPrompter({}),
runtime: createExitThrowingRuntime(),
setDefaultModel: true,
opts: { tokenProvider: "anthropic" },
}),
).rejects.toThrow(
[
'Auth choice "token" is no longer supported for Anthropic setup in OpenClaw.',
"Existing Anthropic token profiles still run if they are already configured.",
'Use "anthropic-cli" or "apiKey" instead.',
].join("\n"),
);
});
it("does not throw when openai-codex oauth fails", async () => {
await setupTempState();

View File

@ -381,6 +381,16 @@ describe("modelsAuthLoginCommand", () => {
});
});
it("rejects pasted Anthropic token setup", async () => {
const runtime = createRuntime();
await expect(modelsAuthPasteTokenCommand({ provider: "anthropic" }, runtime)).rejects.toThrow(
"Anthropic setup-token auth is no longer available for new setup in OpenClaw.",
);
expect(mocks.upsertAuthProfile).not.toHaveBeenCalled();
});
it("runs token auth for any token-capable provider plugin", async () => {
const runtime = createRuntime();
const runTokenAuth = vi.fn().mockResolvedValue({
@ -423,4 +433,32 @@ describe("modelsAuthLoginCommand", () => {
agentDir: "/tmp/openclaw/agents/main",
});
});
it("rejects setup-token for Anthropic even when explicitly requested", async () => {
const runtime = createRuntime();
const runTokenAuth = vi.fn();
mocks.resolvePluginProviders.mockReturnValue([
{
id: "anthropic",
label: "Anthropic",
auth: [
{
id: "setup-token",
label: "setup-token",
kind: "token",
run: runTokenAuth,
},
],
},
]);
await expect(
modelsAuthSetupTokenCommand({ provider: "anthropic", yes: true }, runtime),
).rejects.toThrow(
"Anthropic setup-token auth is no longer available for new setup in OpenClaw.",
);
expect(runTokenAuth).not.toHaveBeenCalled();
expect(mocks.upsertAuthProfile).not.toHaveBeenCalled();
});
});

View File

@ -5,6 +5,14 @@ import {
select as clackSelect,
text as clackText,
} from "@clack/prompts";
import type { AuthProfileCredential } from "../../agents/auth-profiles/types.js";
import type { OpenClawConfig } from "../../config/config.js";
import type {
ProviderAuthMethod,
ProviderAuthResult,
ProviderPlugin,
} from "../../plugins/types.js";
import type { RuntimeEnv } from "../../runtime.js";
import {
resolveAgentDir,
resolveAgentWorkspaceDir,
@ -16,21 +24,13 @@ import {
loadAuthProfileStoreForRuntime,
upsertAuthProfile,
} from "../../agents/auth-profiles.js";
import type { AuthProfileCredential } from "../../agents/auth-profiles/types.js";
import { normalizeProviderId } from "../../agents/model-selection.js";
import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
import { formatCliCommand } from "../../cli/command-format.js";
import { parseDurationMs } from "../../cli/parse-duration.js";
import type { OpenClawConfig } from "../../config/config.js";
import { logConfigUpdated } from "../../config/logging.js";
import { applyAuthProfileConfig } from "../../plugins/provider-auth-helpers.js";
import { resolvePluginProviders } from "../../plugins/providers.runtime.js";
import type {
ProviderAuthMethod,
ProviderAuthResult,
ProviderPlugin,
} from "../../plugins/types.js";
import type { RuntimeEnv } from "../../runtime.js";
import { stylePromptHint, stylePromptMessage } from "../../terminal/prompt-style.js";
import { createClackPrompter } from "../../wizard/clack-prompter.js";
import { isRemoteEnvironment } from "../oauth-env.js";
@ -81,6 +81,19 @@ function resolveDefaultTokenProfileId(provider: string): string {
return `${normalizeProviderId(provider)}:manual`;
}
function throwIfAnthropicTokenSetupDisabled(provider: string): void {
if (normalizeProviderId(provider) !== "anthropic") {
return;
}
throw new Error(
[
"Anthropic setup-token auth is no longer available for new setup in OpenClaw.",
"Existing Anthropic token profiles still run if they are already configured.",
`Use ${formatCliCommand("openclaw models auth login --provider anthropic --method cli --set-default")} or an Anthropic API key instead.`,
].join("\n"),
);
}
type ResolvedModelsAuthContext = {
config: OpenClawConfig;
agentDir: string;
@ -317,13 +330,11 @@ export async function modelsAuthSetupTokenCommand(
}
const provider =
resolveRequestedProviderOrThrow(tokenProviders, opts.provider ?? "anthropic") ??
tokenProviders.find((candidate) => normalizeProviderId(candidate.id) === "anthropic") ??
tokenProviders[0] ??
null;
resolveRequestedProviderOrThrow(tokenProviders, opts.provider) ?? tokenProviders[0] ?? null;
if (!provider) {
throw new Error("No token-capable provider is available.");
}
throwIfAnthropicTokenSetupDisabled(provider.id);
if (!opts.yes) {
const proceed = await confirm({
@ -366,6 +377,7 @@ export async function modelsAuthPasteTokenCommand(
throw new Error("Missing --provider.");
}
const provider = normalizeProviderId(rawProvider);
throwIfAnthropicTokenSetupDisabled(provider);
const profileId = opts.profileId?.trim() || resolveDefaultTokenProfileId(provider);
const tokenInput = await text({

View File

@ -330,33 +330,6 @@ vi.mock("./onboard-non-interactive/local/auth-choice.plugin-providers.js", async
},
};
const anthropicTokenChoice: ChoiceHandler = {
providerId: "anthropic",
label: "Anthropic",
runNonInteractive: async (ctx) => {
const token = normalizeText(ctx.opts.token);
if (!token) {
ctx.runtime.error("Missing --token for --auth-choice token.");
ctx.runtime.exit(1);
return null;
}
upsertAuthProfile({
profileId: "anthropic:default",
credential: {
type: "token",
provider: "anthropic",
token,
},
agentDir: ctx.agentDir,
});
return providerApiKeyAuthRuntime.applyAuthProfileConfig(ctx.config as never, {
profileId: "anthropic:default",
provider: "anthropic",
mode: "token",
});
},
};
const choiceMap = new Map<string, ChoiceHandler>([
[
"apiKey",
@ -581,7 +554,6 @@ vi.mock("./onboard-non-interactive/local/auth-choice.plugin-providers.js", async
}),
}),
],
["token", anthropicTokenChoice],
]);
return {
@ -1087,30 +1059,31 @@ describe("onboard (non-interactive): provider auth", () => {
});
});
it("stores token auth profile", async () => {
it("rejects legacy Anthropic token onboarding", async () => {
await withOnboardEnv("openclaw-onboard-token-", async ({ configPath, runtime }) => {
const cleanToken = `sk-ant-oat01-${"a".repeat(80)}`;
const token = `${cleanToken.slice(0, 30)}\r${cleanToken.slice(30)}`;
await runNonInteractiveSetupWithDefaults(runtime, {
authChoice: "token",
tokenProvider: "anthropic",
token,
tokenProfileId: "anthropic:default",
});
await expect(
runNonInteractiveSetupWithDefaults(runtime, {
authChoice: "token",
tokenProvider: "anthropic",
token,
tokenProfileId: "anthropic:default",
}),
).rejects.toThrow("Process exited with code 1");
expect(runtime.error).toHaveBeenCalledWith(
[
'Auth choice "token" is no longer supported for Anthropic onboarding.',
"Existing Anthropic token profiles still run if they are already configured.",
'Use "--auth-choice anthropic-cli" or "--auth-choice apiKey" instead.',
].join("\n"),
);
const cfg = await readJsonFile<ProviderAuthConfigSnapshot>(configPath);
expect(cfg.auth?.profiles?.["anthropic:default"]?.provider).toBe("anthropic");
expect(cfg.auth?.profiles?.["anthropic:default"]?.mode).toBe("token");
const store = ensureAuthProfileStore();
const profile = store.profiles["anthropic:default"];
expect(profile?.type).toBe("token");
if (profile?.type === "token") {
expect(profile.provider).toBe("anthropic");
expect(profile.token).toBe(cleanToken);
}
expect(cfg.auth?.profiles?.["anthropic:default"]).toBeUndefined();
expect(ensureAuthProfileStore().profiles["anthropic:default"]).toBeUndefined();
});
});

View File

@ -1,8 +1,9 @@
import type { ApiKeyCredential } from "../../../agents/auth-profiles/types.js";
import type { OpenClawConfig } from "../../../config/config.js";
import type { SecretInput } from "../../../config/types.secrets.js";
import { resolveManifestDeprecatedProviderAuthChoice } from "../../../plugins/provider-auth-choices.js";
import type { RuntimeEnv } from "../../../runtime.js";
import type { AuthChoice, OnboardOptions } from "../../onboard-types.js";
import { resolveManifestDeprecatedProviderAuthChoice } from "../../../plugins/provider-auth-choices.js";
import { resolveDefaultSecretProviderAlias } from "../../../secrets/ref-contract.js";
import {
formatDeprecatedNonInteractiveAuthChoiceError,
@ -16,7 +17,6 @@ import {
parseNonInteractiveCustomApiFlags,
resolveCustomProviderId,
} from "../../onboard-custom.js";
import type { AuthChoice, OnboardOptions } from "../../onboard-types.js";
import { resolveNonInteractiveApiKey } from "../api-keys.js";
import { applyNonInteractivePluginProviderChoice } from "./auth-choice.plugin-providers.js";
@ -130,8 +130,21 @@ export async function applyNonInteractiveAuthChoice(params: {
if (authChoice === "setup-token") {
runtime.error(
[
'Auth choice "setup-token" requires interactive mode.',
'Use "--auth-choice token" with --token and --token-provider anthropic.',
'Auth choice "setup-token" is no longer supported for Anthropic onboarding.',
"Existing Anthropic token profiles still run if they are already configured.",
'Use "--auth-choice anthropic-cli" or "--auth-choice apiKey" instead.',
].join("\n"),
);
runtime.exit(1);
return null;
}
if (authChoice === "token") {
runtime.error(
[
'Auth choice "token" is no longer supported for Anthropic onboarding.',
"Existing Anthropic token profiles still run if they are already configured.",
'Use "--auth-choice anthropic-cli" or "--auth-choice apiKey" instead.',
].join("\n"),
);
runtime.exit(1);
@ -246,7 +259,15 @@ export async function applyNonInteractiveAuthChoice(params: {
authChoice === "minimax-global-oauth" ||
authChoice === "minimax-cn-oauth"
) {
runtime.error("OAuth requires interactive mode.");
runtime.error(
authChoice === "oauth"
? [
'Auth choice "oauth" is no longer supported for Anthropic onboarding.',
"Existing Anthropic token profiles still run if they are already configured.",
'Use "--auth-choice anthropic-cli" or "--auth-choice apiKey" instead.',
].join("\n")
: "OAuth requires interactive mode.",
);
runtime.exit(1);
return null;
}

View File

@ -1,4 +1,5 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ProviderPlugin } from "./types.js";
import {
buildProviderPluginMethodChoice,
resolveProviderModelPickerEntries,
@ -6,7 +7,6 @@ import {
resolveProviderWizardOptions,
runProviderModelSelectedHook,
} from "./provider-wizard.js";
import type { ProviderPlugin } from "./types.js";
const resolvePluginProviders = vi.hoisted(() => vi.fn<() => ProviderPlugin[]>(() => []));
vi.mock("./providers.runtime.js", () => ({
@ -291,24 +291,24 @@ describe("provider wizard boundaries", () => {
label: "Anthropic",
auth: [
{
id: "setup-token",
label: "setup-token",
kind: "token",
id: "cli",
label: "Claude CLI",
kind: "custom",
wizard: {
choiceId: "token",
choiceId: "anthropic-cli",
modelAllowlist: {
allowedKeys: ["anthropic/claude-sonnet-4-6"],
initialSelections: ["anthropic/claude-sonnet-4-6"],
message: "Anthropic OAuth models",
allowedKeys: ["claude-cli/claude-sonnet-4-6"],
initialSelections: ["claude-cli/claude-sonnet-4-6"],
message: "Claude CLI models",
},
},
run: vi.fn(),
},
],
}),
choice: "token",
choice: "anthropic-cli",
expectedOption: {
value: "token",
value: "anthropic-cli",
label: "Anthropic",
groupId: "anthropic",
groupLabel: "Anthropic",