From 06be8bc88ccda6d75090711499c3833d313cba32 Mon Sep 17 00:00:00 2001 From: Askhz <1361267452@qq.com> Date: Thu, 16 Apr 2026 15:07:17 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20csc=20auth=20login=20=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E4=BD=BF=E7=94=A8=20CoStrict=20OAuth=20=E7=99=BB?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `csc auth login` 默认打开 CoStrict 登录页面而非 Claude OAuth 页面。 添加 --claudeai / --console 参数保留 Anthropic OAuth 作为备选。 Co-Authored-By: Claude Opus 4.6 --- src/cli/handlers/auth.ts | 72 +++++++++++++++++++++++++++++++++++++++- src/main.tsx | 8 ++--- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/src/cli/handlers/auth.ts b/src/cli/handlers/auth.ts index f121b8154..4753531db 100644 --- a/src/cli/handlers/auth.ts +++ b/src/cli/handlers/auth.ts @@ -30,6 +30,7 @@ import { saveOAuthTokensIfNeeded, validateForceLoginOrg, } from '../../utils/auth.js' +import { openBrowser } from '../../utils/browser.js' import { saveGlobalConfig } from '../../utils/config.js' import { logForDebugging } from '../../utils/debug.js' import { isRunningOnHomespace } from '../../utils/envUtils.js' @@ -37,11 +38,23 @@ import { errorMessage } from '../../utils/errors.js' import { logError } from '../../utils/log.js' import { getAPIProvider } from '../../utils/model/providers.js' import { getInitialSettings } from '../../utils/settings/settings.js' +import { updateSettingsForSource } from '../../utils/settings/settings.js' import { jsonStringify } from '../../utils/slowOperations.js' import { buildAccountProperties, buildAPIProviderProperties, } from '../../utils/status.js' +import { + buildCoStrictLoginURL, + generateState, + getCoStrictBaseURL, + pollLoginToken, +} from '../../costrict/provider/auth.js' +import { + generateMachineId, + saveCoStrictCredentials, +} from '../../costrict/provider/credentials.js' +import { extractExpiryFromJWT } from '../../costrict/provider/token.js' /** * Shared post-token-acquisition logic. Saves tokens, fetches profile/roles, @@ -109,6 +122,57 @@ export async function installOAuthTokens(tokens: OAuthTokens): Promise { await clearAuthRelatedCaches() } +/** + * CoStrict OAuth login flow for CLI. + * Opens CoStrict login URL in browser and polls for tokens. + */ +async function coStrictLogin(): Promise { + const baseUrl = getCoStrictBaseURL() + const state = generateState() + const machineId = generateMachineId() + const loginUrl = buildCoStrictLoginURL(baseUrl, state, machineId) + + process.stdout.write('Opening browser to sign in…\n') + process.stdout.write(`If the browser didn't open, visit: ${loginUrl}\n`) + + await openBrowser(loginUrl) + + try { + const tokens = await pollLoginToken(baseUrl, state, machineId) + + const expiryDate = extractExpiryFromJWT(tokens.access_token) + await saveCoStrictCredentials({ + id: 'csc', + name: 'CSC Auth', + access_token: tokens.access_token, + refresh_token: tokens.refresh_token, + state, + machine_id: machineId, + base_url: baseUrl, + expiry_date: expiryDate, + updated_at: new Date().toISOString(), + expired_at: expiryDate ? new Date(expiryDate).toISOString() : undefined, + }) + + // Set modelType to costrict + updateSettingsForSource('userSettings', { modelType: 'costrict' as never } as never) + process.env.CLAUDE_CODE_USE_COSTRICT = '1' + + // Mark onboarding complete + saveGlobalConfig(current => { + if (current.hasCompletedOnboarding) return current + return { ...current, hasCompletedOnboarding: true } + }) + + process.stdout.write('Login successful.\n') + process.exit(0) + } catch (err) { + logError(err) + process.stderr.write(`Login failed: ${errorMessage(err)}\n`) + process.exit(1) + } +} + export async function authLogin({ email, sso, @@ -120,6 +184,12 @@ export async function authLogin({ console?: boolean claudeai?: boolean }): Promise { + // Default to CoStrict login; use --claudeai or --console for Anthropic OAuth + if (!claudeai && !useConsole) { + await coStrictLogin() + return + } + if (useConsole && claudeai) { process.stderr.write( 'Error: --console and --claudeai cannot be used together.\n', @@ -129,7 +199,7 @@ export async function authLogin({ const settings = getInitialSettings() // forceLoginMethod is a hard constraint (enterprise setting) — matches ConsoleOAuthFlow behavior. - // Without it, --console selects Console; --claudeai (or no flag) selects claude.ai. + // Without it, --console selects Console; --claudeai selects claude.ai. const loginWithClaudeAi = settings.forceLoginMethod ? settings.forceLoginMethod === 'claudeai' : !useConsole diff --git a/src/main.tsx b/src/main.tsx index 001f659cc..9854ff1dd 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -6092,14 +6092,14 @@ async function run(): Promise { .description("Sign in to your CoStrict account") .option( "--email ", - "Pre-populate email address on the login page", + "Pre-populate email address on the login page (Anthropic OAuth only)", ) - .option("--sso", "Force SSO login flow") + .option("--sso", "Force SSO login flow (Anthropic OAuth only)") .option( "--console", - "Use Anthropic Console (API usage billing) instead of Claude subscription", + "Use Anthropic Console (API usage billing) instead of CoStrict", ) - .option("--claudeai", "Use Claude subscription (default)") + .option("--claudeai", "Use Claude subscription instead of CoStrict") .action( async ({ email, From 22fd08537a4a603d118e9a7c7a5670d0f61ca38d Mon Sep 17 00:00:00 2001 From: costrict Date: Thu, 16 Apr 2026 15:23:05 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20fetch.ts=20prec?= =?UTF-8?q?onnect=20=E9=87=8D=E5=A4=8D=E6=A0=87=E8=AF=86=E7=AC=A6=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- src/costrict/provider/fetch.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/costrict/provider/fetch.ts b/src/costrict/provider/fetch.ts index dabbf532a..e906a7995 100644 --- a/src/costrict/provider/fetch.ts +++ b/src/costrict/provider/fetch.ts @@ -43,8 +43,13 @@ const VERSION = getVersion() * 3. 注入 Authorization 和 CoStrict 特有 headers * 4. 反应性 401 错误恢复(自动重试一次) */ +type CoStrictFetchFn = { + (input: RequestInfo | URL, init?: RequestInit): Promise + preconnect?: (url: string | URL) => void +} + export function createCoStrictFetch() { - const costrictFetch = async ( + const costrictFetch: CoStrictFetchFn = async ( input: RequestInfo | URL, init?: RequestInit, ) => { From 13ec1d4510dfb8176e74d7864da492e1c60e35db Mon Sep 17 00:00:00 2001 From: Askhz <1361267452@qq.com> Date: Thu, 16 Apr 2026 15:29:09 +0800 Subject: [PATCH 3/3] chore: back fetch.ts --- src/costrict/provider/fetch.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/costrict/provider/fetch.ts b/src/costrict/provider/fetch.ts index e906a7995..dabbf532a 100644 --- a/src/costrict/provider/fetch.ts +++ b/src/costrict/provider/fetch.ts @@ -43,13 +43,8 @@ const VERSION = getVersion() * 3. 注入 Authorization 和 CoStrict 特有 headers * 4. 反应性 401 错误恢复(自动重试一次) */ -type CoStrictFetchFn = { - (input: RequestInfo | URL, init?: RequestInit): Promise - preconnect?: (url: string | URL) => void -} - export function createCoStrictFetch() { - const costrictFetch: CoStrictFetchFn = async ( + const costrictFetch = async ( input: RequestInfo | URL, init?: RequestInit, ) => {