mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-23 14:45:46 +00:00
fix: restore anthropic setup-token auth flow
This commit is contained in:
@@ -1,19 +1,24 @@
|
||||
import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
|
||||
import { formatCliCommand, parseDurationMs } from "openclaw/plugin-sdk/cli-runtime";
|
||||
import type {
|
||||
OpenClawPluginApi,
|
||||
ProviderAuthContext,
|
||||
ProviderAuthMethodNonInteractiveContext,
|
||||
ProviderResolveDynamicModelContext,
|
||||
ProviderRuntimeModel,
|
||||
} from "openclaw/plugin-sdk/plugin-entry";
|
||||
import {
|
||||
applyAuthProfileConfig,
|
||||
createProviderApiKeyAuthMethod,
|
||||
buildTokenProfileId,
|
||||
ensureApiKeyFromOptionEnvOrPrompt,
|
||||
listProfilesForProvider,
|
||||
normalizeApiKeyInput,
|
||||
type OpenClawConfig as ProviderAuthConfig,
|
||||
suggestOAuthProfileIdForLegacyDefault,
|
||||
type AuthProfileStore,
|
||||
type ProviderAuthResult,
|
||||
upsertAuthProfile,
|
||||
validateAnthropicSetupToken,
|
||||
validateApiKeyInput,
|
||||
} from "openclaw/plugin-sdk/provider-auth";
|
||||
import { cloneFirstTemplateModel } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
@@ -50,6 +55,129 @@ const ANTHROPIC_OAUTH_ALLOWLIST = [
|
||||
"anthropic/claude-sonnet-4-5",
|
||||
"anthropic/claude-haiku-4-5",
|
||||
] as const;
|
||||
const ANTHROPIC_SETUP_TOKEN_NOTE_LINES = [
|
||||
"Anthropic setup-token auth is a legacy/manual path in OpenClaw.",
|
||||
"Anthropic told OpenClaw users that OpenClaw counts as a third-party harness, so this path requires Extra Usage on the Claude account.",
|
||||
`If you want a direct API billing path instead, use ${formatCliCommand("openclaw models auth login --provider anthropic --method api-key --set-default")} or ${formatCliCommand("openclaw models auth login --provider anthropic --method cli --set-default")}.`,
|
||||
] as const;
|
||||
|
||||
function normalizeAnthropicSetupTokenInput(value: string): string {
|
||||
return value.replaceAll(/\s+/g, "").trim();
|
||||
}
|
||||
|
||||
function resolveAnthropicSetupTokenProfileId(rawProfileId?: unknown): string {
|
||||
if (typeof rawProfileId === "string") {
|
||||
const trimmed = rawProfileId.trim();
|
||||
if (trimmed.length > 0) {
|
||||
if (trimmed.startsWith(`${PROVIDER_ID}:`)) {
|
||||
return trimmed;
|
||||
}
|
||||
return buildTokenProfileId({ provider: PROVIDER_ID, name: trimmed });
|
||||
}
|
||||
}
|
||||
return `${PROVIDER_ID}:default`;
|
||||
}
|
||||
|
||||
function resolveAnthropicSetupTokenExpiry(rawExpiresIn?: unknown): number | undefined {
|
||||
if (typeof rawExpiresIn !== "string" || rawExpiresIn.trim().length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return Date.now() + parseDurationMs(rawExpiresIn.trim(), { defaultUnit: "d" });
|
||||
}
|
||||
|
||||
async function runAnthropicSetupTokenAuth(ctx: ProviderAuthContext): Promise<ProviderAuthResult> {
|
||||
const providedToken =
|
||||
typeof ctx.opts?.token === "string" && ctx.opts.token.trim().length > 0
|
||||
? normalizeAnthropicSetupTokenInput(ctx.opts.token)
|
||||
: undefined;
|
||||
const token =
|
||||
providedToken ??
|
||||
normalizeAnthropicSetupTokenInput(
|
||||
await ctx.prompter.text({
|
||||
message: "Paste Anthropic setup-token",
|
||||
validate: (value) => validateAnthropicSetupToken(normalizeAnthropicSetupTokenInput(value)),
|
||||
}),
|
||||
);
|
||||
const tokenError = validateAnthropicSetupToken(token);
|
||||
if (tokenError) {
|
||||
throw new Error(tokenError);
|
||||
}
|
||||
|
||||
const profileId = resolveAnthropicSetupTokenProfileId(ctx.opts?.tokenProfileId);
|
||||
const expires = resolveAnthropicSetupTokenExpiry(ctx.opts?.tokenExpiresIn);
|
||||
|
||||
return {
|
||||
profiles: [
|
||||
{
|
||||
profileId,
|
||||
credential: {
|
||||
type: "token",
|
||||
provider: PROVIDER_ID,
|
||||
token,
|
||||
...(expires ? { expires } : {}),
|
||||
},
|
||||
},
|
||||
],
|
||||
defaultModel: DEFAULT_ANTHROPIC_MODEL,
|
||||
notes: [...ANTHROPIC_SETUP_TOKEN_NOTE_LINES],
|
||||
};
|
||||
}
|
||||
|
||||
async function runAnthropicSetupTokenNonInteractive(
|
||||
ctx: ProviderAuthMethodNonInteractiveContext,
|
||||
): Promise<ProviderAuthConfig | null> {
|
||||
const rawToken =
|
||||
typeof ctx.opts.token === "string" ? normalizeAnthropicSetupTokenInput(ctx.opts.token) : "";
|
||||
const tokenError = validateAnthropicSetupToken(rawToken);
|
||||
if (tokenError) {
|
||||
ctx.runtime.error(
|
||||
["Anthropic setup-token auth requires --token with a valid setup-token.", tokenError].join(
|
||||
"\n",
|
||||
),
|
||||
);
|
||||
ctx.runtime.exit(1);
|
||||
return null;
|
||||
}
|
||||
|
||||
const profileId = resolveAnthropicSetupTokenProfileId(ctx.opts.tokenProfileId);
|
||||
const expires = resolveAnthropicSetupTokenExpiry(ctx.opts.tokenExpiresIn);
|
||||
upsertAuthProfile({
|
||||
profileId,
|
||||
credential: {
|
||||
type: "token",
|
||||
provider: PROVIDER_ID,
|
||||
token: rawToken,
|
||||
...(expires ? { expires } : {}),
|
||||
},
|
||||
agentDir: ctx.agentDir,
|
||||
});
|
||||
|
||||
ctx.runtime.log(ANTHROPIC_SETUP_TOKEN_NOTE_LINES[0]);
|
||||
ctx.runtime.log(ANTHROPIC_SETUP_TOKEN_NOTE_LINES[1]);
|
||||
|
||||
const withProfile = applyAuthProfileConfig(ctx.config, {
|
||||
profileId,
|
||||
provider: PROVIDER_ID,
|
||||
mode: "token",
|
||||
});
|
||||
const existingModelConfig =
|
||||
withProfile.agents?.defaults?.model && typeof withProfile.agents.defaults.model === "object"
|
||||
? withProfile.agents.defaults.model
|
||||
: {};
|
||||
return {
|
||||
...withProfile,
|
||||
agents: {
|
||||
...withProfile.agents,
|
||||
defaults: {
|
||||
...withProfile.agents?.defaults,
|
||||
model: {
|
||||
...existingModelConfig,
|
||||
primary: DEFAULT_ANTHROPIC_MODEL,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function resolveAnthropic46ForwardCompatModel(params: {
|
||||
ctx: ProviderResolveDynamicModelContext;
|
||||
@@ -256,6 +384,25 @@ export function registerAnthropicPlugin(api: OpenClawPluginApi): void {
|
||||
runtime: ctx.runtime,
|
||||
}),
|
||||
},
|
||||
createProviderApiKeyAuthMethod({
|
||||
{
|
||||
id: "setup-token",
|
||||
label: "Anthropic setup-token",
|
||||
hint: "Legacy/manual bearer token path; requires Extra Usage when used through OpenClaw",
|
||||
kind: "token",
|
||||
wizard: {
|
||||
choiceId: "setup-token",
|
||||
choiceLabel: "Anthropic setup-token",
|
||||
choiceHint: "Legacy/manual path; requires Extra Usage in OpenClaw",
|
||||
assistantPriority: 40,
|
||||
groupId: "anthropic",
|
||||
groupLabel: "Anthropic",
|
||||
groupHint: "Claude CLI + API key + legacy token",
|
||||
},
|
||||
run: async (ctx: ProviderAuthContext) => await runAnthropicSetupTokenAuth(ctx),
|
||||
runNonInteractive: async (ctx: ProviderAuthMethodNonInteractiveContext) =>
|
||||
await runAnthropicSetupTokenNonInteractive(ctx),
|
||||
},
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId,
|
||||
methodId: "api-key",
|
||||
|
||||
@@ -10,13 +10,22 @@ export function normalizeApiKeyTokenProviderAuthChoice(params: {
|
||||
workspaceDir?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): AuthChoice {
|
||||
if (params.authChoice !== "apiKey" || !params.tokenProvider) {
|
||||
if (!params.tokenProvider) {
|
||||
return params.authChoice;
|
||||
}
|
||||
const normalizedTokenProvider = normalizeTokenProviderInput(params.tokenProvider);
|
||||
if (!normalizedTokenProvider) {
|
||||
return params.authChoice;
|
||||
}
|
||||
if (
|
||||
(params.authChoice === "token" || params.authChoice === "setup-token") &&
|
||||
normalizedTokenProvider === "anthropic"
|
||||
) {
|
||||
return "setup-token";
|
||||
}
|
||||
if (params.authChoice !== "apiKey") {
|
||||
return params.authChoice;
|
||||
}
|
||||
return (
|
||||
(resolveManifestProviderApiKeyChoice({
|
||||
providerId: normalizedTokenProvider,
|
||||
|
||||
@@ -56,19 +56,20 @@ export async function applyAuthChoice(
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
normalizedParams.authChoice === "token" ||
|
||||
normalizedParams.authChoice === "setup-token" ||
|
||||
normalizedParams.authChoice === "oauth"
|
||||
) {
|
||||
if (normalizedParams.authChoice === "token" || normalizedParams.authChoice === "setup-token") {
|
||||
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.',
|
||||
`Auth choice "${normalizedParams.authChoice}" was not matched to a provider setup flow.`,
|
||||
'For Anthropic legacy token auth, use "setup-token" with tokenProvider="anthropic" or choose the Anthropic setup-token entry explicitly.',
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
if (normalizedParams.authChoice === "oauth") {
|
||||
throw new Error(
|
||||
'Auth choice "oauth" is no longer supported directly. Use "setup-token" for Anthropic legacy token auth or a provider-specific OAuth entry.',
|
||||
);
|
||||
}
|
||||
|
||||
return { config: normalizedParams.config };
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ 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 { ProviderAuthMethod, ProviderAuthResult, 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";
|
||||
@@ -51,6 +51,7 @@ vi.mock("./zai-endpoint-detect.js", () => ({
|
||||
|
||||
type StoredAuthProfile = {
|
||||
key?: string;
|
||||
token?: string;
|
||||
keyRef?: { source: string; provider: string; id: string };
|
||||
access?: string;
|
||||
refresh?: string;
|
||||
@@ -651,24 +652,58 @@ describe("applyAuthChoice", () => {
|
||||
|
||||
resolvePluginProviders.mockReturnValue(createDefaultProviderPlugins());
|
||||
|
||||
it("rejects legacy Anthropic token setup aliases", async () => {
|
||||
it("applies Anthropic setup-token auth when the provider exposes the setup flow", async () => {
|
||||
await setupTempState();
|
||||
|
||||
await expect(
|
||||
applyAuthChoice({
|
||||
authChoice: "token",
|
||||
config: {} as OpenClawConfig,
|
||||
prompter: createPrompter({}),
|
||||
runtime: createExitThrowingRuntime(),
|
||||
setDefaultModel: true,
|
||||
opts: { tokenProvider: "anthropic" },
|
||||
resolvePluginProviders.mockReturnValue([
|
||||
createFixedChoiceProvider({
|
||||
providerId: "anthropic",
|
||||
label: "Anthropic",
|
||||
choiceId: "setup-token",
|
||||
method: {
|
||||
id: "setup-token",
|
||||
label: "Anthropic setup-token",
|
||||
kind: "token",
|
||||
run: vi.fn(
|
||||
async (): Promise<ProviderAuthResult> => ({
|
||||
profiles: [
|
||||
{
|
||||
profileId: "anthropic:default",
|
||||
credential: {
|
||||
type: "token",
|
||||
provider: "anthropic",
|
||||
token: `sk-ant-oat01-${"a".repeat(80)}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
defaultModel: "anthropic/claude-sonnet-4-6",
|
||||
}),
|
||||
),
|
||||
},
|
||||
}),
|
||||
).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"),
|
||||
]);
|
||||
|
||||
const result = await applyAuthChoice({
|
||||
authChoice: "token",
|
||||
config: {} as OpenClawConfig,
|
||||
prompter: createPrompter({}),
|
||||
runtime: createExitThrowingRuntime(),
|
||||
setDefaultModel: true,
|
||||
opts: {
|
||||
tokenProvider: "anthropic",
|
||||
token: `sk-ant-oat01-${"a".repeat(80)}`,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.config.auth?.profiles?.["anthropic:default"]).toMatchObject({
|
||||
provider: "anthropic",
|
||||
mode: "token",
|
||||
});
|
||||
expect(resolveAgentModelPrimaryValue(result.config.agents?.defaults?.model)).toBe(
|
||||
"anthropic/claude-sonnet-4-6",
|
||||
);
|
||||
expect((await readAuthProfile("anthropic:default"))?.token).toBe(
|
||||
`sk-ant-oat01-${"a".repeat(80)}`,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -381,14 +381,27 @@ describe("modelsAuthLoginCommand", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects pasted Anthropic token setup", async () => {
|
||||
it("writes pasted Anthropic setup-tokens and logs the legacy warning", async () => {
|
||||
const runtime = createRuntime();
|
||||
mocks.clackText.mockResolvedValue(`sk-ant-oat01-${"a".repeat(80)}`);
|
||||
|
||||
await expect(modelsAuthPasteTokenCommand({ provider: "anthropic" }, runtime)).rejects.toThrow(
|
||||
"Anthropic setup-token auth is no longer available for new setup in OpenClaw.",
|
||||
await modelsAuthPasteTokenCommand({ provider: "anthropic" }, runtime);
|
||||
|
||||
expect(mocks.upsertAuthProfile).toHaveBeenCalledWith({
|
||||
profileId: "anthropic:manual",
|
||||
credential: {
|
||||
type: "token",
|
||||
provider: "anthropic",
|
||||
token: `sk-ant-oat01-${"a".repeat(80)}`,
|
||||
},
|
||||
agentDir: "/tmp/openclaw/agents/main",
|
||||
});
|
||||
expect(runtime.log).toHaveBeenCalledWith(
|
||||
"Anthropic setup-token auth is a legacy/manual path in OpenClaw.",
|
||||
);
|
||||
expect(runtime.log).toHaveBeenCalledWith(
|
||||
"Anthropic told OpenClaw users this path requires Extra Usage on the Claude account.",
|
||||
);
|
||||
|
||||
expect(mocks.upsertAuthProfile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("runs token auth for any token-capable provider plugin", async () => {
|
||||
@@ -434,9 +447,21 @@ describe("modelsAuthLoginCommand", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects setup-token for Anthropic even when explicitly requested", async () => {
|
||||
it("runs setup-token for Anthropic when the provider exposes the method", async () => {
|
||||
const runtime = createRuntime();
|
||||
const runTokenAuth = vi.fn();
|
||||
const runTokenAuth = vi.fn().mockResolvedValue({
|
||||
profiles: [
|
||||
{
|
||||
profileId: "anthropic:default",
|
||||
credential: {
|
||||
type: "token",
|
||||
provider: "anthropic",
|
||||
token: `sk-ant-oat01-${"b".repeat(80)}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
defaultModel: "anthropic/claude-sonnet-4-6",
|
||||
});
|
||||
mocks.resolvePluginProviders.mockReturnValue([
|
||||
{
|
||||
id: "anthropic",
|
||||
@@ -452,13 +477,17 @@ describe("modelsAuthLoginCommand", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
await expect(
|
||||
modelsAuthSetupTokenCommand({ provider: "anthropic", yes: true }, runtime),
|
||||
).rejects.toThrow(
|
||||
"Anthropic setup-token auth is no longer available for new setup in OpenClaw.",
|
||||
);
|
||||
await modelsAuthSetupTokenCommand({ provider: "anthropic", yes: true }, runtime);
|
||||
|
||||
expect(runTokenAuth).not.toHaveBeenCalled();
|
||||
expect(mocks.upsertAuthProfile).not.toHaveBeenCalled();
|
||||
expect(runTokenAuth).toHaveBeenCalledOnce();
|
||||
expect(mocks.upsertAuthProfile).toHaveBeenCalledWith({
|
||||
profileId: "anthropic:default",
|
||||
credential: {
|
||||
type: "token",
|
||||
provider: "anthropic",
|
||||
token: `sk-ant-oat01-${"b".repeat(80)}`,
|
||||
},
|
||||
agentDir: "/tmp/openclaw/agents/main",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,6 +33,7 @@ import type {
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { stylePromptHint, stylePromptMessage } from "../../terminal/prompt-style.js";
|
||||
import { createClackPrompter } from "../../wizard/clack-prompter.js";
|
||||
import { validateAnthropicSetupToken } from "../auth-token.js";
|
||||
import { isRemoteEnvironment } from "../oauth-env.js";
|
||||
import { createVpsAwareOAuthHandlers } from "../oauth-flow.js";
|
||||
import { openUrl } from "../onboard-helpers.js";
|
||||
@@ -81,19 +82,6 @@ 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;
|
||||
@@ -334,7 +322,6 @@ export async function modelsAuthSetupTokenCommand(
|
||||
if (!provider) {
|
||||
throw new Error("No token-capable provider is available.");
|
||||
}
|
||||
throwIfAnthropicTokenSetupDisabled(provider.id);
|
||||
|
||||
if (!opts.yes) {
|
||||
const proceed = await confirm({
|
||||
@@ -377,14 +364,27 @@ 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({
|
||||
message: `Paste token for ${provider}`,
|
||||
validate: (value) => (value?.trim() ? undefined : "Required"),
|
||||
validate: (value) => {
|
||||
const trimmed = value?.trim();
|
||||
if (!trimmed) {
|
||||
return "Required";
|
||||
}
|
||||
if (provider === "anthropic") {
|
||||
return validateAnthropicSetupToken(trimmed.replaceAll(/\s+/g, ""));
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
const token = String(tokenInput ?? "").trim();
|
||||
const token =
|
||||
provider === "anthropic"
|
||||
? String(tokenInput ?? "")
|
||||
.replaceAll(/\s+/g, "")
|
||||
.trim()
|
||||
: String(tokenInput ?? "").trim();
|
||||
|
||||
const expires =
|
||||
opts.expiresIn?.trim() && opts.expiresIn.trim().length > 0
|
||||
@@ -406,6 +406,12 @@ export async function modelsAuthPasteTokenCommand(
|
||||
|
||||
logConfigUpdated(runtime);
|
||||
runtime.log(`Auth profile: ${profileId} (${provider}/token)`);
|
||||
if (provider === "anthropic") {
|
||||
runtime.log("Anthropic setup-token auth is a legacy/manual path in OpenClaw.");
|
||||
runtime.log(
|
||||
"Anthropic told OpenClaw users this path requires Extra Usage on the Claude account.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function modelsAuthAddCommand(_opts: Record<string, never>, runtime: RuntimeEnv) {
|
||||
|
||||
@@ -331,6 +331,42 @@ vi.mock("./onboard-non-interactive/local/auth-choice.plugin-providers.js", async
|
||||
};
|
||||
|
||||
const choiceMap = new Map<string, ChoiceHandler>([
|
||||
[
|
||||
"setup-token",
|
||||
{
|
||||
providerId: "anthropic",
|
||||
label: "Anthropic setup-token",
|
||||
async runNonInteractive(ctx) {
|
||||
const token = normalizeText(ctx.opts.token);
|
||||
if (!token) {
|
||||
ctx.runtime.error("Anthropic setup-token auth requires --token.");
|
||||
ctx.runtime.exit(1);
|
||||
return null;
|
||||
}
|
||||
upsertAuthProfile({
|
||||
profileId: (ctx.opts.tokenProfileId as string | undefined) ?? "anthropic:default",
|
||||
credential: {
|
||||
type: "token",
|
||||
provider: "anthropic",
|
||||
token,
|
||||
} as never,
|
||||
agentDir: ctx.agentDir,
|
||||
});
|
||||
const withProfile = providerApiKeyAuthRuntime.applyAuthProfileConfig(
|
||||
ctx.config as never,
|
||||
{
|
||||
profileId: (ctx.opts.tokenProfileId as string | undefined) ?? "anthropic:default",
|
||||
provider: "anthropic",
|
||||
mode: "token",
|
||||
},
|
||||
);
|
||||
return providerApiKeyAuthRuntime.applyPrimaryModel(
|
||||
withProfile,
|
||||
"anthropic/claude-sonnet-4-6",
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
"apiKey",
|
||||
createApiKeyChoice({
|
||||
@@ -1059,22 +1095,27 @@ describe("onboard (non-interactive): provider auth", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects legacy Anthropic token onboarding", async () => {
|
||||
it("stores legacy Anthropic setup-token onboarding again when explicitly selected", 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 expect(
|
||||
runNonInteractiveSetupWithDefaults(runtime, {
|
||||
authChoice: "token",
|
||||
tokenProvider: "anthropic",
|
||||
token,
|
||||
tokenProfileId: "anthropic:default",
|
||||
}),
|
||||
).rejects.toThrow('Auth choice "token" is no longer supported for Anthropic onboarding.');
|
||||
await runNonInteractiveSetupWithDefaults(runtime, {
|
||||
authChoice: "token",
|
||||
tokenProvider: "anthropic",
|
||||
token,
|
||||
tokenProfileId: "anthropic:default",
|
||||
});
|
||||
|
||||
await expect(fs.access(configPath)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
expect(ensureAuthProfileStore().profiles["anthropic:default"]).toBeUndefined();
|
||||
const cfg = await readJsonFile<ProviderAuthConfigSnapshot>(configPath);
|
||||
expect(cfg.auth?.profiles?.["anthropic:default"]?.provider).toBe("anthropic");
|
||||
expect(cfg.auth?.profiles?.["anthropic:default"]?.mode).toBe("token");
|
||||
expect(cfg.agents?.defaults?.model?.primary).toBe("anthropic/claude-sonnet-4-6");
|
||||
expect(ensureAuthProfileStore().profiles["anthropic:default"]).toMatchObject({
|
||||
provider: "anthropic",
|
||||
type: "token",
|
||||
token: cleanToken,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -127,30 +127,6 @@ export async function applyNonInteractiveAuthChoice(params: {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (authChoice === "setup-token") {
|
||||
runtime.error(
|
||||
[
|
||||
'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);
|
||||
return null;
|
||||
}
|
||||
|
||||
const pluginProviderChoice = await applyNonInteractivePluginProviderChoice({
|
||||
nextConfig,
|
||||
authChoice,
|
||||
@@ -169,6 +145,17 @@ export async function applyNonInteractiveAuthChoice(params: {
|
||||
return pluginProviderChoice;
|
||||
}
|
||||
|
||||
if (authChoice === "setup-token" || authChoice === "token") {
|
||||
runtime.error(
|
||||
[
|
||||
`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"),
|
||||
);
|
||||
runtime.exit(1);
|
||||
return null;
|
||||
}
|
||||
|
||||
const deprecatedChoice = resolveManifestDeprecatedProviderAuthChoice(authChoice as string, {
|
||||
config: nextConfig,
|
||||
env: process.env,
|
||||
@@ -261,11 +248,7 @@ export async function applyNonInteractiveAuthChoice(params: {
|
||||
) {
|
||||
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")
|
||||
? 'Auth choice "oauth" is no longer supported directly. Use "--auth-choice setup-token --token-provider anthropic" for Anthropic legacy token auth, or a provider-specific OAuth choice.'
|
||||
: "OAuth requires interactive mode.",
|
||||
);
|
||||
runtime.exit(1);
|
||||
|
||||
Reference in New Issue
Block a user