diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a42b60ec6..9ddbd706ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,12 @@ _Development cycle in progress — entries are added as work merges into `releas --- +## [3.8.10] — Unreleased + +_Development cycle in progress — entries are added as work merges into `release/v3.8.10` and finalized by the release flow._ + +--- + ## [3.8.9] — 2026-06-03 ### ✨ New Features diff --git a/open-sse/config/constants.ts b/open-sse/config/constants.ts index 0a14c1c644..aaa6e442a0 100644 --- a/open-sse/config/constants.ts +++ b/open-sse/config/constants.ts @@ -67,8 +67,8 @@ export const OAUTH_ENDPOINTS = { auth: "https://auth.openai.com/oauth/authorize", }, anthropic: { - token: "https://console.anthropic.com/v1/oauth/token", - auth: "https://console.anthropic.com/v1/oauth/authorize", + token: "https://api.anthropic.com/v1/oauth/token", + auth: "https://api.anthropic.com/v1/oauth/authorize", }, qwen: { token: "https://chat.qwen.ai/api/v1/oauth2/token", // From CLIProxyAPI diff --git a/open-sse/config/providerRegistry.ts b/open-sse/config/providerRegistry.ts index c0a89b8e6a..c7d10fa8f6 100644 --- a/open-sse/config/providerRegistry.ts +++ b/open-sse/config/providerRegistry.ts @@ -647,7 +647,7 @@ const _REGISTRY_EAGER: Record = { oauth: { clientIdEnv: "CLAUDE_OAUTH_CLIENT_ID", clientIdDefault: "9d1c250a-e61b-44d9-88ed-5944d1962f5e", - tokenUrl: "https://console.anthropic.com/v1/oauth/token", + tokenUrl: "https://api.anthropic.com/v1/oauth/token", }, models: [ { @@ -1337,7 +1337,12 @@ const _REGISTRY_EAGER: Record = { // #2900: big-pickle's upstream runs DeepSeek thinking mode — declare the // interleaved reasoning_content contract so follow-up/tool-use turns replay // it (otherwise DeepSeek returns 400 "reasoning_content ... must be passed back"). - { id: "big-pickle", name: "Big Pickle", supportsReasoning: true, interleavedField: "reasoning_content" }, + { + id: "big-pickle", + name: "Big Pickle", + supportsReasoning: true, + interleavedField: "reasoning_content", + }, { id: "deepseek-v4-flash-free", name: "DeepSeek V4 Flash Free", supportsReasoning: true }, { id: "minimax-m2.5-free", name: "MiniMax M2.5 Free", contextLength: 204800 }, { id: "ling-2.6-1t-free", name: "Ling 2.6 Free", contextLength: 262000 }, @@ -1347,7 +1352,13 @@ const _REGISTRY_EAGER: Record = { contextLength: 131000, }, { id: "nemotron-3-super-free", name: "Nemotron 3 Super Free", contextLength: 1000000 }, - { id: "qwen3.6-plus-free", name: "Qwen3.6 Plus Free", targetFormat: "claude", supportsVision: false, contextLength: 200000, }, + { + id: "qwen3.6-plus-free", + name: "Qwen3.6 Plus Free", + targetFormat: "claude", + supportsVision: false, + contextLength: 200000, + }, ], }, @@ -1409,7 +1420,12 @@ const _REGISTRY_EAGER: Record = { // #2900: big-pickle's upstream runs DeepSeek thinking mode — declare the // interleaved reasoning_content contract so follow-up/tool-use turns replay // it (otherwise DeepSeek returns 400 "reasoning_content ... must be passed back"). - { id: "big-pickle", name: "Big Pickle", supportsReasoning: true, interleavedField: "reasoning_content" }, + { + id: "big-pickle", + name: "Big Pickle", + supportsReasoning: true, + interleavedField: "reasoning_content", + }, { id: "gpt-5-nano", name: "GPT 5 Nano", contextLength: 400000 }, { id: "gpt-5", name: "GPT 5" }, { id: "gpt-5-codex", name: "GPT 5 Codex" }, @@ -2346,7 +2362,6 @@ const _REGISTRY_EAGER: Record = { models: [{ id: "auto", name: "Auto" }], }, - kluster: { id: "kluster", alias: "kluster", @@ -2380,7 +2395,6 @@ const _REGISTRY_EAGER: Record = { models: [{ id: "liquid-lfm-40b", name: "Liquid LFM 40B" }], }, - monsterapi: { id: "monsterapi", alias: "monster", @@ -2418,7 +2432,6 @@ const _REGISTRY_EAGER: Record = { ], }, - chutes: { id: "chutes", alias: "chutes", @@ -3587,7 +3600,6 @@ const _REGISTRY_EAGER: Record = { ], }, - hackclub: { id: "hackclub", alias: "hc", diff --git a/src/lib/oauth/constants/oauth.ts b/src/lib/oauth/constants/oauth.ts index ea10329cc6..586bc4abb4 100644 --- a/src/lib/oauth/constants/oauth.ts +++ b/src/lib/oauth/constants/oauth.ts @@ -36,7 +36,7 @@ import { buildGitLabOAuthEndpoints, GITLAB_DUO_DEFAULT_BASE_URL } from "../gitla export const CLAUDE_CONFIG = { clientId: process.env.CLAUDE_OAUTH_CLIENT_ID || "9d1c250a-e61b-44d9-88ed-5944d1962f5e", authorizeUrl: "https://claude.ai/oauth/authorize", - tokenUrl: "https://console.anthropic.com/v1/oauth/token", + tokenUrl: "https://api.anthropic.com/v1/oauth/token", redirectUri: process.env.CLAUDE_CODE_REDIRECT_URI || "https://platform.claude.com/oauth/code/callback", scopes: [ diff --git a/tests/unit/claude-oauth-vps-endpoints.test.ts b/tests/unit/claude-oauth-vps-endpoints.test.ts new file mode 100644 index 0000000000..86762472a6 --- /dev/null +++ b/tests/unit/claude-oauth-vps-endpoints.test.ts @@ -0,0 +1,52 @@ +import { test } from "node:test"; +import assert from "node:assert"; +import { CLAUDE_CONFIG } from "@/lib/oauth/constants/oauth.ts"; +import { OAUTH_ENDPOINTS } from "@omniroute/open-sse/config/constants.ts"; +import { REGISTRY } from "@omniroute/open-sse/config/providerRegistry.ts"; + +test("CLAUDE_CONFIG.tokenUrl uses api.anthropic.com (not console.anthropic.com)", () => { + assert.ok( + CLAUDE_CONFIG.tokenUrl.includes("api.anthropic.com"), + `Expected api.anthropic.com but got ${CLAUDE_CONFIG.tokenUrl}` + ); + assert.equal(CLAUDE_CONFIG.tokenUrl, "https://api.anthropic.com/v1/oauth/token"); +}); + +test("OAUTH_ENDPOINTS.anthropic uses api.anthropic.com for token and auth", () => { + assert.ok( + OAUTH_ENDPOINTS.anthropic.token.includes("api.anthropic.com"), + `Expected api.anthropic.com but got ${OAUTH_ENDPOINTS.anthropic.token}` + ); + assert.ok( + OAUTH_ENDPOINTS.anthropic.auth.includes("api.anthropic.com"), + `Expected api.anthropic.com but got ${OAUTH_ENDPOINTS.anthropic.auth}` + ); + assert.equal(OAUTH_ENDPOINTS.anthropic.token, "https://api.anthropic.com/v1/oauth/token"); + assert.equal(OAUTH_ENDPOINTS.anthropic.auth, "https://api.anthropic.com/v1/oauth/authorize"); +}); + +test("Provider registry claude oauth.tokenUrl uses api.anthropic.com", () => { + const claude = REGISTRY.claude; + assert.ok(claude, "claude provider should exist in registry"); + assert.ok( + claude.oauth?.tokenUrl?.includes("api.anthropic.com"), + `Expected api.anthropic.com but got ${claude.oauth?.tokenUrl}` + ); + assert.equal(claude.oauth.tokenUrl, "https://api.anthropic.com/v1/oauth/token"); +}); + +test("No console.anthropic.com remains in OAuth constants or registry", () => { + const allUrls = [ + CLAUDE_CONFIG.tokenUrl, + OAUTH_ENDPOINTS.anthropic.token, + OAUTH_ENDPOINTS.anthropic.auth, + REGISTRY.claude?.oauth?.tokenUrl, + ].filter(Boolean); + for (const url of allUrls) { + assert.equal( + url.includes("console.anthropic.com"), + false, + `Found console.anthropic.com in ${url}` + ); + } +});