1- import {
2- mkdtempSync ,
3- rmSync ,
4- readFileSync ,
5- writeFileSync ,
6- mkdirSync ,
7- readdirSync ,
8- } from "fs" ;
1+ import { mkdtempSync , rmSync , readFileSync , writeFileSync , mkdirSync , readdirSync } from "fs" ;
92import { tmpdir , homedir } from "os" ;
103import { join } from "path" ;
114import { afterEach , beforeEach , describe , expect , test } from "vite-plus/test" ;
@@ -90,12 +83,8 @@ describe("config agent writers", () => {
9083 apiKey : "sk-a" ,
9184 model : "qwen3-max" ,
9285 } ) ;
93- const settings = JSON . parse (
94- readFileSync ( join ( customDir , "settings.json" ) , "utf8" ) ,
95- ) ;
96- expect (
97- ( settings . env as Record < string , string > ) . ANTHROPIC_AUTH_TOKEN ,
98- ) . toBe ( "sk-a" ) ;
86+ const settings = JSON . parse ( readFileSync ( join ( customDir , "settings.json" ) , "utf8" ) ) ;
87+ expect ( ( settings . env as Record < string , string > ) . ANTHROPIC_AUTH_TOKEN ) . toBe ( "sk-a" ) ;
9988 } finally {
10089 delete process . env . CLAUDE_CONFIG_DIR ;
10190 }
@@ -110,35 +99,82 @@ describe("config agent writers", () => {
11099 const settings = readJsonAt ( ".qwen" , "settings.json" ) ;
111100 expect ( settings . $version ) . toBe ( 3 ) ;
112101 const security = settings . security as { auth : Record < string , unknown > } ;
113- // security.auth 只携带 selectedType;凭证在 env + envKey 里
114- expect ( security . auth ) . toEqual ( { selectedType : "openai" } ) ;
115- expect ( ( settings . env as Record < string , string > ) . BAILIAN_CLI_API_KEY ) . toBe (
116- "sk-q" ,
117- ) ;
118- expect ( settings . model ) . toEqual ( { name : "qwen3-coder-plus" } ) ;
119- const providers = settings . modelProviders as Record <
120- string ,
121- Array < Record < string , unknown > >
122- > ;
102+ // security.auth 携带 selectedType 以及凭证兜底(apiKey/baseUrl),
103+ // 避免系统 OPENAI_API_KEY 抢占
104+ expect ( security . auth ) . toEqual ( {
105+ selectedType : "openai" ,
106+ apiKey : "sk-q" ,
107+ baseUrl : OAI_URL ,
108+ } ) ;
109+ expect ( ( settings . env as Record < string , string > ) . BAILIAN_CLI_API_KEY ) . toBe ( "sk-q" ) ;
110+ // model.name 必须与 baseUrl 一同写入(同 id provider 消歧契约)
111+ expect ( settings . model ) . toEqual ( {
112+ name : "qwen3-coder-plus" ,
113+ baseUrl : OAI_URL ,
114+ } ) ;
115+ const providers = settings . modelProviders as Record < string , Array < Record < string , unknown > > > ;
116+ // name 是模型显示名(非 provider 品牌常量),品牌只在 envKey 里
123117 expect ( providers . openai [ 0 ] ) . toMatchObject ( {
124118 id : "qwen3-coder-plus" ,
125- name : "bailian-cli " ,
119+ name : "[Bailian] qwen3-coder-plus " ,
126120 baseUrl : OAI_URL ,
127121 envKey : "BAILIAN_CLI_API_KEY" ,
128122 } ) ;
129123 } ) ;
130124
125+ test ( "qwen-code upsert 时治愈旧的 bailian-cli name 但保留用户自定义 name" , ( ) => {
126+ mkdirSync ( join ( home , ".qwen" ) , { recursive : true } ) ;
127+ writeFileSync (
128+ join ( home , ".qwen" , "settings.json" ) ,
129+ JSON . stringify ( {
130+ modelProviders : {
131+ openai : [
132+ {
133+ id : "qwen3-coder-plus" ,
134+ name : "bailian-cli" ,
135+ baseUrl : OAI_URL ,
136+ envKey : "OLD" ,
137+ } ,
138+ {
139+ id : "my-model" ,
140+ name : "My Custom" ,
141+ baseUrl : OAI_URL ,
142+ envKey : "OLD" ,
143+ } ,
144+ ] ,
145+ } ,
146+ } ) ,
147+ ) ;
148+
149+ // 旧 sentinel 被治愈为显示名
150+ qwenCode . write ( {
151+ baseUrl : OAI_URL ,
152+ apiKey : "sk-q" ,
153+ model : "qwen3-coder-plus" ,
154+ } ) ;
155+ // 用户自定义 name 不被覆盖
156+ qwenCode . write ( { baseUrl : OAI_URL , apiKey : "sk-q" , model : "my-model" } ) ;
157+
158+ const settings = readJsonAt ( ".qwen" , "settings.json" ) ;
159+ const entries = ( settings . modelProviders as Record < string , Array < Record < string , unknown > > > )
160+ . openai ;
161+ const healed = entries . find ( ( entry ) => entry . id === "qwen3-coder-plus" ) ! ;
162+ expect ( healed . name ) . toBe ( "[Bailian] qwen3-coder-plus" ) ;
163+ expect ( healed . envKey ) . toBe ( "BAILIAN_CLI_API_KEY" ) ;
164+ const custom = entries . find ( ( entry ) => entry . id === "my-model" ) ! ;
165+ expect ( custom . name ) . toBe ( "My Custom" ) ;
166+ } ) ;
167+
131168 test ( "qwen-code anthropic 端点走 anthropic 协议" , ( ) => {
132169 qwenCode . write ( {
133170 baseUrl : ANTHROPIC_URL ,
134171 apiKey : "sk-q" ,
135172 model : "qwen3-max" ,
136173 } ) ;
137174 const settings = readJsonAt ( ".qwen" , "settings.json" ) ;
138- expect (
139- ( settings . security as { auth : { selectedType : string } } ) . auth
140- . selectedType ,
141- ) . toBe ( "anthropic" ) ;
175+ expect ( ( settings . security as { auth : { selectedType : string } } ) . auth . selectedType ) . toBe (
176+ "anthropic" ,
177+ ) ;
142178 const providers = settings . modelProviders as Record < string , unknown > ;
143179 expect ( Array . isArray ( providers . anthropic ) ) . toBe ( true ) ;
144180 expect ( providers . openai ) . toBeUndefined ( ) ;
@@ -156,8 +192,7 @@ describe("config agent writers", () => {
156192 model : "qwen3-coder-plus" ,
157193 } ) ;
158194 const settings = readJsonAt ( ".qwen" , "settings.json" ) ;
159- const openaiEntries = ( settings . modelProviders as Record < string , unknown [ ] > )
160- . openai ;
195+ const openaiEntries = ( settings . modelProviders as Record < string , unknown [ ] > ) . openai ;
161196 expect ( openaiEntries ) . toHaveLength ( 1 ) ;
162197 } ) ;
163198
@@ -205,9 +240,7 @@ describe("config agent writers", () => {
205240 expect ( options . baseURL ) . toBe ( ANTHROPIC_URL ) ;
206241 expect ( options . apiKey ) . toBe ( "sk-o" ) ;
207242 expect ( options . setCacheKey ) . toBe ( true ) ;
208- expect (
209- ( provider [ "bailian-cli" ] . models as Record < string , unknown > ) [ "qwen3-max" ] ,
210- ) . toBeDefined ( ) ;
243+ expect ( ( provider [ "bailian-cli" ] . models as Record < string , unknown > ) [ "qwen3-max" ] ) . toBeDefined ( ) ;
211244
212245 // 非 anthropic 端点用 openai-compatible
213246 opencode . write ( { baseUrl : OAI_URL , apiKey : "sk-o" , model : "qwen3-max" } ) ;
@@ -230,9 +263,7 @@ describe("config agent writers", () => {
230263 const config = readJsonAt ( ".openclaw" , "openclaw.json" ) ;
231264 const models = config . models as Record < string , unknown > ;
232265 expect ( models . mode ) . toBe ( "merge" ) ;
233- const bailian = (
234- models . providers as Record < string , Record < string , unknown > >
235- ) [ "bailian-cli" ] ;
266+ const bailian = ( models . providers as Record < string , Record < string , unknown > > ) [ "bailian-cli" ] ;
236267 expect ( bailian . api ) . toBe ( "openai-completions" ) ;
237268 const entry = ( bailian . models as Array < Record < string , unknown > > ) [ 0 ] ;
238269 expect ( entry . id ) . toBe ( "qwen3-coder-plus" ) ;
@@ -258,8 +289,7 @@ describe("config agent writers", () => {
258289 contextWindow : 1000000 ,
259290 } ) ;
260291 const config2 = readJsonAt ( ".openclaw" , "openclaw.json" ) ;
261- const providers2 = ( config2 . models as Record < string , unknown > )
262- . providers as Record <
292+ const providers2 = ( config2 . models as Record < string , unknown > ) . providers as Record <
263293 string ,
264294 { api : string ; models : Array < Record < string , unknown > > }
265295 > ;
@@ -281,9 +311,7 @@ describe("config agent writers", () => {
281311 apiKey : "sk-h" ,
282312 model : "qwen3-coder-plus" ,
283313 } ) ;
284- const config = yaml . parse (
285- readFileSync ( join ( home , ".hermes" , "config.yaml" ) , "utf8" ) ,
286- ) ;
314+ const config = yaml . parse ( readFileSync ( join ( home , ".hermes" , "config.yaml" ) , "utf8" ) ) ;
287315 // OpenAI 兼容端点:按官方文档省略 api_mode;无关顶层键不受影响
288316 expect ( config . model ) . toEqual ( {
289317 default : "qwen3-coder-plus" ,
@@ -299,9 +327,7 @@ describe("config agent writers", () => {
299327 apiKey : "sk-h" ,
300328 model : "qwen3-max" ,
301329 } ) ;
302- const config2 = yaml . parse (
303- readFileSync ( join ( home , ".hermes" , "config.yaml" ) , "utf8" ) ,
304- ) ;
330+ const config2 = yaml . parse ( readFileSync ( join ( home , ".hermes" , "config.yaml" ) , "utf8" ) ) ;
305331 expect ( config2 . model ) . toEqual ( {
306332 default : "qwen3-max" ,
307333 provider : "custom" ,
@@ -326,10 +352,7 @@ describe("config agent writers", () => {
326352 ] . join ( "\n" ) ,
327353 ) ;
328354 // 预置 auth.json 无关键,验证合并保留
329- writeFileSync (
330- join ( home , ".codex" , "auth.json" ) ,
331- JSON . stringify ( { EXISTING : "keep" } ) ,
332- ) ;
355+ writeFileSync ( join ( home , ".codex" , "auth.json" ) , JSON . stringify ( { EXISTING : "keep" } ) ) ;
333356
334357 codex . write ( {
335358 baseUrl : OAI_URL ,
@@ -367,10 +390,7 @@ describe("config agent writers", () => {
367390
368391 test ( "已存在的配置文件会被备份为 .bak.<epoch>" , ( ) => {
369392 mkdirSync ( join ( home , ".openclaw" ) , { recursive : true } ) ;
370- writeFileSync (
371- join ( home , ".openclaw" , "openclaw.json" ) ,
372- JSON . stringify ( { pre : 1 } ) ,
373- ) ;
393+ writeFileSync ( join ( home , ".openclaw" , "openclaw.json" ) , JSON . stringify ( { pre : 1 } ) ) ;
374394
375395 openclaw . write ( { baseUrl : OAI_URL , apiKey : "sk-c" , model : "qwen3-max" } ) ;
376396 const backups = readdirSync ( join ( home , ".openclaw" ) ) . filter ( ( name ) =>
0 commit comments