@@ -4,58 +4,106 @@ import { backup, readJson, writeJsonAtomic, isAnthropicEndpoint, type AgentDef }
44
55const ENV_KEY = "BAILIAN_CLI_API_KEY" ;
66
7+ function displayName ( model : string ) : string {
8+ return `${ model } (bailian-cli)` ;
9+ }
10+
11+ /** Entries we previously wrote, or still own via envKey. */
12+ function isBailianCliEntry ( entry : Record < string , unknown > ) : boolean {
13+ return entry . envKey === ENV_KEY || entry . name === "bailian-cli" ;
14+ }
15+
716/**
817 * Qwen Code keys `modelProviders` and `security.auth.selectedType` by the SDK
918 * protocol (an AuthType string), not by a free-form provider id — the runtime
10- * resolver indexes credentials/defaults by protocol. The `bailian-cli` brand
11- * therefore lives in the model entry `name` and the env var name.
19+ * resolver indexes credentials/defaults by protocol. Ownership is tracked via
20+ * `envKey` (`BAILIAN_CLI_API_KEY`) and a display `name` suffix `(bailian-cli)`.
21+ *
22+ * Qwen Code does not support duplicate model `id`s (only the first loads), so
23+ * we must never overwrite a pre-existing Token Plan / third-party entry that
24+ * shares the same id.
1225 */
1326export default {
1427 label : "Qwen Code" ,
1528 write ( { baseUrl, apiKey, model } ) {
1629 const settingsPath = join ( homedir ( ) , ".qwen" , "settings.json" ) ;
1730 const protocol = isAnthropicEndpoint ( baseUrl ) ? "anthropic" : "openai" ;
31+ const warnings : string [ ] = [ ] ;
1832
1933 backup ( settingsPath ) ;
2034 const settings = readJson ( settingsPath ) ;
2135
2236 // env — API key read by the provider entry's envKey.
37+ // Qwen Code treats settings.json `env` as lowest priority; a process/shell
38+ // value for the same key wins and can make the first launch fail.
2339 const env = ( settings . env ?? { } ) as Record < string , string > ;
2440 env [ ENV_KEY ] = apiKey ;
2541 settings . env = env ;
2642
27- // modelProviders[<protocol>] — upsert the bailian-cli model entry.
43+ const processEnvValue = process . env [ ENV_KEY ] ;
44+ if ( processEnvValue !== undefined && processEnvValue !== apiKey ) {
45+ warnings . push (
46+ `Shell/environment ${ ENV_KEY } is set and overrides settings.json. ` +
47+ `Unset it (e.g. \`unset ${ ENV_KEY } \`) so the key written here takes effect.` ,
48+ ) ;
49+ }
50+
51+ // modelProviders[<protocol>] — upsert only bailian-cli-owned entries.
2852 const providers = ( settings . modelProviders ?? { } ) as Record <
2953 string ,
3054 Array < Record < string , unknown > >
3155 > ;
3256 const entries = ( providers [ protocol ] ?? [ ] ) as Array < Record < string , unknown > > ;
33- const existing = entries . find (
34- ( entry ) => entry . id === model && ( entry . baseUrl ?? "" ) === baseUrl ,
35- ) ;
36- if ( existing ) {
37- existing . name = "bailian-cli" ;
38- existing . baseUrl = baseUrl ;
39- existing . envKey = ENV_KEY ;
57+ const owned = entries . find ( ( entry ) => isBailianCliEntry ( entry ) && entry . id === model ) ;
58+ const conflicting = entries . find ( ( entry ) => ! isBailianCliEntry ( entry ) && entry . id === model ) ;
59+
60+ if ( owned ) {
61+ owned . name = displayName ( model ) ;
62+ owned . baseUrl = baseUrl ;
63+ owned . envKey = ENV_KEY ;
64+ } else if ( conflicting ) {
65+ const existingName =
66+ typeof conflicting . name === "string" && conflicting . name . length > 0
67+ ? conflicting . name
68+ : String ( conflicting . id ) ;
69+ warnings . push (
70+ `Model id "${ model } " already exists as "${ existingName } "; left unchanged ` +
71+ `(Qwen Code loads only the first entry per id). Remove or rename that ` +
72+ `entry if you want bailian-cli to own this model.` ,
73+ ) ;
4074 } else {
41- entries . push ( { id : model , name : "bailian-cli" , baseUrl, envKey : ENV_KEY } ) ;
75+ entries . push ( {
76+ id : model ,
77+ name : displayName ( model ) ,
78+ baseUrl,
79+ envKey : ENV_KEY ,
80+ } ) ;
4281 }
4382 providers [ protocol ] = entries ;
4483 settings . modelProviders = providers ;
4584
46- // security.auth — select the protocol and carry the OpenAI-compatible creds.
85+ // security.auth — select protocol only. Prefer modelProviders + envKey for
86+ // credentials; apiKey/baseUrl on auth are deprecated in Qwen Code.
4787 const security = ( settings . security ?? { } ) as Record < string , unknown > ;
48- security . auth = { selectedType : protocol , apiKey, baseUrl } ;
88+ const previousAuth =
89+ security . auth && typeof security . auth === "object"
90+ ? ( security . auth as Record < string , unknown > )
91+ : { } ;
92+ security . auth = { ...previousAuth , selectedType : protocol } ;
93+ // Drop deprecated inline creds so they cannot disagree with envKey lookup.
94+ delete ( security . auth as Record < string , unknown > ) . apiKey ;
95+ delete ( security . auth as Record < string , unknown > ) . baseUrl ;
4996 settings . security = security ;
5097
51- // model — active model, disambiguated by baseUrl.
98+ // model — active model id ( disambiguated by baseUrl when supported) .
5299 settings . model = { name : model , baseUrl } ;
53100
54101 writeJsonAtomic ( settingsPath , settings ) ;
55102
56103 return {
57104 paths : [ settingsPath ] ,
58105 nextStep : "Run `qwen` to start using Qwen Code with DashScope." ,
106+ warnings : warnings . length > 0 ? warnings : undefined ,
59107 } ;
60108 } ,
61109} satisfies AgentDef ;
0 commit comments