1- import {
2- BailianError ,
3- ExitCode ,
4- chatPath ,
5- requestJson ,
6- normalizeModelBaseUrl ,
7- type AuthPersistPatch ,
8- type AuthStore ,
9- type Identity ,
10- type Settings ,
11- } from "bailian-cli-core" ;
1+ import { normalizeModelBaseUrl , type AuthPersistPatch , type AuthStore } from "bailian-cli-core" ;
122
133interface ApiKeyLoginDeps {
14- identity : Identity ;
15- settings : Settings ;
164 authStore : AuthStore ;
175}
186
197interface ApiKeyLoginProfile {
20- baseUrl : string ;
218 persistBaseUrl ?: string ;
229 defaultTextModel ?: string ;
2310 defaultVideoModel ?: string ;
@@ -30,65 +17,19 @@ interface ApiKeyLoginProfile {
3017 persistPatch ?: AuthPersistPatch ;
3118}
3219
33- const RETRY_DELAY_BASE_MS = 500 ;
34-
35- function canRetry ( error : unknown ) : boolean {
36- if ( error instanceof BailianError ) {
37- if ( error . exitCode === ExitCode . NETWORK || error . exitCode === ExitCode . TIMEOUT ) return true ;
38- const status = error . api ?. httpStatus ;
39- return status === 401 || ( status !== undefined && status >= 500 ) ;
40- }
41- if ( error instanceof Error ) {
42- return (
43- error . name === "AbortError" ||
44- error . name === "TimeoutError" ||
45- error . message . includes ( "timed out" ) ||
46- error . message === "fetch failed"
47- ) ;
48- }
49- return false ;
50- }
51-
52- export async function validateAndPersistApiKey (
20+ /**
21+ * Persist an API key (and optional profile defaults) without a live model probe.
22+ * Login is credential storage; connectivity is verified on the first API command.
23+ * A former chat/completions smoke test conflated quota/model-access 403s with bad keys.
24+ */
25+ export async function persistApiKey (
5326 deps : ApiKeyLoginDeps ,
5427 key : string ,
5528 profile : ApiKeyLoginProfile ,
5629) : Promise < void > {
57- process . stderr . write ( "Testing key... " ) ;
58- const httpDeps = { identity : deps . identity , settings : deps . settings } ;
59- const baseUrl = normalizeModelBaseUrl ( profile . baseUrl ) ;
6030 const persistBaseUrl = profile . persistBaseUrl
6131 ? normalizeModelBaseUrl ( profile . persistBaseUrl )
6232 : undefined ;
63- const validationModel = "qwen3.8-max" ;
64- const requestOpts = {
65- url : baseUrl + chatPath ( ) ,
66- method : "POST" ,
67- headers : { Authorization : `Bearer ${ key } ` } ,
68- timeout : Math . min ( deps . settings . timeout , 30 ) ,
69- body : {
70- model : validationModel ,
71- messages : [ { role : "user" , content : "hi" } ] ,
72- max_tokens : 1 ,
73- stream : false ,
74- } ,
75- } ;
76-
77- for ( let attempt = 1 ; attempt <= 3 ; attempt ++ ) {
78- try {
79- await requestJson < unknown > ( httpDeps , requestOpts ) ;
80- break ;
81- } catch ( error ) {
82- if ( attempt >= 3 || ! canRetry ( error ) ) {
83- process . stderr . write ( "Failed\n" ) ;
84- throw error ;
85- }
86- const delayMs = RETRY_DELAY_BASE_MS * 2 ** ( attempt - 1 ) ;
87- await new Promise ( ( resolve ) => setTimeout ( resolve , delayMs ) ) ;
88- }
89- }
90-
91- process . stderr . write ( "Valid\n" ) ;
9233 await deps . authStore . login ( {
9334 ...profile . persistPatch ,
9435 api_key : key ,
0 commit comments