@@ -113,6 +113,9 @@ const DEFAULT_GROK_LOCAL_MODEL = DEFAULT_GROK_BUILD_MODEL;
113113const DEFAULT_QWEN_CODE_LOCAL_MODEL = DEFAULT_QWEN_CODE_MODEL ;
114114const DEFAULT_DEEPSEEK_TUI_LOCAL_MODEL = DEFAULT_DEEPSEEK_TUI_MODEL ;
115115const CC_SWITCH_CLAUDE_COMMON_CONFIG_KEY = 'common_config_claude' ;
116+ const WESIGHT_MANAGED_META_KEY = '__wesight_managed' ;
117+ const WESIGHT_ACTIVE_API_KEY_ENV = 'WESIGHT_APIKEY_ACTIVE_PROVIDER' ;
118+ const WESIGHT_ACTIVE_API_KEY_PLACEHOLDER = `\${${ WESIGHT_ACTIVE_API_KEY_ENV } }` ;
116119const CLAUDE_MODEL_ENV_KEYS = [
117120 'ANTHROPIC_AUTH_TOKEN' ,
118121 'ANTHROPIC_API_KEY' ,
@@ -217,7 +220,7 @@ const sanitizeProviderKey = (value: string): string => {
217220 return key || 'wesight' ;
218221} ;
219222
220- const buildCodexConfig = ( providerName : string , baseUrl : string , model : string ) : string => {
223+ export const buildCodexConfig = ( providerName : string , baseUrl : string , model : string ) : string => {
221224 const providerKey = sanitizeProviderKey ( providerName ) ;
222225 return [
223226 `model_provider = ${ tomlString ( providerKey ) } ` ,
@@ -234,6 +237,89 @@ const buildCodexConfig = (providerName: string, baseUrl: string, model: string):
234237 ] . filter ( ( line ) => line !== '' ) . join ( '\n' ) ;
235238} ;
236239
240+ const isWesightPlaceholder = ( value : unknown ) : boolean => {
241+ return typeof value === 'string'
242+ && / ^ \$ \{ (?: W E S I G H T | L O B S T E R ) _ [ A - Z 0 - 9 _ ] + \} $ / . test ( value . trim ( ) ) ;
243+ } ;
244+
245+ const removeTrailingBlankLines = ( value : string ) : string => {
246+ return value . replace ( / \s + $ / g, '' ) ;
247+ } ;
248+
249+ const splitTomlHeadAndTables = ( configText : string ) : { head : string ; tables : string } => {
250+ const match = configText . match ( / ^ \s * \[ / m) ;
251+ if ( ! match || match . index === undefined ) {
252+ return { head : configText , tables : '' } ;
253+ }
254+ return {
255+ head : configText . slice ( 0 , match . index ) ,
256+ tables : configText . slice ( match . index ) ,
257+ } ;
258+ } ;
259+
260+ const upsertTomlTopLevelString = ( head : string , key : string , value : string ) : string => {
261+ const line = `${ key } = ${ tomlString ( value ) } ` ;
262+ const pattern = new RegExp ( `^\\s*${ key } \\s*=.*$` , 'm' ) ;
263+ if ( pattern . test ( head ) ) {
264+ return head . replace ( pattern , line ) ;
265+ }
266+ const trimmed = removeTrailingBlankLines ( head ) ;
267+ return trimmed ? `${ trimmed } \n${ line } \n` : `${ line } \n` ;
268+ } ;
269+
270+ const upsertTomlTopLevelBoolean = ( head : string , key : string , value : boolean ) : string => {
271+ const line = `${ key } = ${ value ? 'true' : 'false' } ` ;
272+ const pattern = new RegExp ( `^\\s*${ key } \\s*=.*$` , 'm' ) ;
273+ if ( pattern . test ( head ) ) {
274+ return head . replace ( pattern , line ) ;
275+ }
276+ const trimmed = removeTrailingBlankLines ( head ) ;
277+ return trimmed ? `${ trimmed } \n${ line } \n` : `${ line } \n` ;
278+ } ;
279+
280+ const replaceCodexProviderTable = (
281+ tables : string ,
282+ providerKey : string ,
283+ providerName : string ,
284+ baseUrl : string ,
285+ ) : string => {
286+ const escaped = providerKey . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ;
287+ const providerBlock = [
288+ `[model_providers.${ providerKey } ]` ,
289+ `name = ${ tomlString ( providerName || providerKey ) } ` ,
290+ baseUrl . trim ( ) ? `base_url = ${ tomlString ( baseUrl . trim ( ) ) } ` : '' ,
291+ 'wire_api = "responses"' ,
292+ 'requires_openai_auth = true' ,
293+ '' ,
294+ ] . filter ( ( line ) => line !== '' ) . join ( '\n' ) ;
295+ const tablePattern = new RegExp (
296+ `(^|\\n)\\[model_providers\\.${ escaped } \\][\\s\\S]*?(?=\\n\\[|$)` ,
297+ 'm' ,
298+ ) ;
299+ if ( tablePattern . test ( tables ) ) {
300+ return tables . replace ( tablePattern , ( match , prefix ) => `${ prefix } ${ providerBlock } ` ) ;
301+ }
302+ const trimmed = removeTrailingBlankLines ( tables ) ;
303+ return trimmed ? `${ trimmed } \n\n${ providerBlock } \n` : `${ providerBlock } \n` ;
304+ } ;
305+
306+ export const mergeCodexConfigForWesightModel = (
307+ existingText : string ,
308+ providerName : string ,
309+ baseUrl : string ,
310+ model : string ,
311+ ) : string => {
312+ const providerKey = sanitizeProviderKey ( providerName ) ;
313+ const split = splitTomlHeadAndTables ( existingText ) ;
314+ let head = split . head ;
315+ head = upsertTomlTopLevelString ( head , 'model_provider' , providerKey ) ;
316+ head = upsertTomlTopLevelString ( head , 'model' , model || DEFAULT_CODEX_MODEL ) ;
317+ head = upsertTomlTopLevelString ( head , 'model_reasoning_effort' , 'high' ) ;
318+ head = upsertTomlTopLevelBoolean ( head , 'disable_response_storage' , true ) ;
319+ const tables = replaceCodexProviderTable ( split . tables , providerKey , providerName , baseUrl ) ;
320+ return `${ removeTrailingBlankLines ( head ) } \n\n${ removeTrailingBlankLines ( tables ) } \n` ;
321+ } ;
322+
237323const extractTomlString = ( configText : string , key : string ) : string => {
238324 const match = configText . match ( new RegExp ( `^\\s*${ key } \\s*=\\s*["']([^"']*)["']` , 'm' ) ) ;
239325 return match ?. [ 1 ] ?. trim ( ) ?? '' ;
@@ -316,10 +402,26 @@ const buildClaudeEnvForConfig = (
316402 existingEnv : Record < string , unknown > ,
317403 config : CoworkApiConfig ,
318404) : Record < string , unknown > => {
405+ const existingAuthToken = getString ( existingEnv . ANTHROPIC_AUTH_TOKEN ) ;
406+ const existingApiKey = getString ( existingEnv . ANTHROPIC_API_KEY ) ;
407+ const hasUserCredential = Boolean (
408+ existingAuthToken
409+ || existingApiKey ,
410+ ) ;
411+ const credentialMatchesWesightConfig = Boolean (
412+ config . apiKey
413+ && ( existingAuthToken === config . apiKey || existingApiKey === config . apiKey ) ,
414+ ) ;
415+ const shouldWriteCredential = ! hasUserCredential
416+ || credentialMatchesWesightConfig
417+ || isWesightPlaceholder ( existingEnv . ANTHROPIC_AUTH_TOKEN )
418+ || isWesightPlaceholder ( existingEnv . ANTHROPIC_API_KEY ) ;
319419 return {
320420 ...existingEnv ,
321- ANTHROPIC_AUTH_TOKEN : config . apiKey ,
322- ANTHROPIC_API_KEY : config . apiKey ,
421+ ...( shouldWriteCredential ? {
422+ ANTHROPIC_AUTH_TOKEN : WESIGHT_ACTIVE_API_KEY_PLACEHOLDER ,
423+ ANTHROPIC_API_KEY : WESIGHT_ACTIVE_API_KEY_PLACEHOLDER ,
424+ } : { } ) ,
323425 ANTHROPIC_BASE_URL : config . baseURL ,
324426 ANTHROPIC_MODEL : config . model ,
325427 ANTHROPIC_REASONING_MODEL : config . model ,
@@ -344,13 +446,6 @@ const mergeClaudeSettingsWithProvider = (
344446 ...providerEnv ,
345447 } ;
346448
347- for ( const key of CLAUDE_MODEL_ENV_KEYS ) {
348- if ( ! Object . prototype . hasOwnProperty . call ( commonEnv , key )
349- && ! Object . prototype . hasOwnProperty . call ( providerEnv , key ) ) {
350- delete env [ key ] ;
351- }
352- }
353-
354449 return {
355450 ...existingSettings ,
356451 ...commonConfig ,
@@ -359,13 +454,32 @@ const mergeClaudeSettingsWithProvider = (
359454 } ;
360455} ;
361456
362- const buildClaudeSettingsForConfig = (
457+ export const mergeClaudeSettingsForWesightModel = (
363458 existingSettings : Record < string , unknown > ,
364459 config : CoworkApiConfig ,
365460) : Record < string , unknown > => {
461+ const existingManaged = getNestedRecord ( existingSettings , WESIGHT_MANAGED_META_KEY ) ;
462+ const existingClaude = getNestedRecord ( existingManaged , 'claudeCode' ) ;
463+ const previousEnvKeys = Array . isArray ( existingClaude . envKeys )
464+ ? existingClaude . envKeys . filter ( ( key ) : key is string => typeof key === 'string' )
465+ : [ ] ;
466+ const existingEnv = { ...getNestedRecord ( existingSettings , 'env' ) } ;
467+ for ( const key of previousEnvKeys ) {
468+ if ( isWesightPlaceholder ( existingEnv [ key ] ) ) {
469+ delete existingEnv [ key ] ;
470+ }
471+ }
472+ const env = buildClaudeEnvForConfig ( existingEnv , config ) ;
366473 return {
367474 ...existingSettings ,
368- env : buildClaudeEnvForConfig ( getNestedRecord ( existingSettings , 'env' ) , config ) ,
475+ env,
476+ [ WESIGHT_MANAGED_META_KEY ] : {
477+ ...existingManaged ,
478+ claudeCode : {
479+ envKeys : CLAUDE_MODEL_ENV_KEYS . filter ( ( key ) => Object . prototype . hasOwnProperty . call ( env , key ) ) ,
480+ secretEnv : WESIGHT_ACTIVE_API_KEY_ENV ,
481+ } ,
482+ } ,
369483 } ;
370484} ;
371485
@@ -537,7 +651,7 @@ const upsertCcSwitchClaudeProvider = (
537651 const providers = readCcSwitchClaudeProviders ( db ) ;
538652 const targetProvider = findCcSwitchProviderForConfig ( providers , config , settingsCurrentProviderId , providerName ) ;
539653 const now = Date . now ( ) ;
540- const settingsConfig = buildClaudeSettingsForConfig ( targetProvider ?. settingsConfig ?? { } , config ) ;
654+ const settingsConfig = mergeClaudeSettingsForWesightModel ( targetProvider ?. settingsConfig ?? { } , config ) ;
541655 const providerId = targetProvider ?. id ?? randomUUID ( ) ;
542656 const existingMeta = targetProvider ?. meta ?? { } ;
543657 const meta = {
@@ -654,7 +768,7 @@ const syncClaudeCodeFromWesightModel = (): void => {
654768 }
655769
656770 const settings = readJsonObject ( paths . primaryConfigPath ) ?? { } ;
657- writeJsonObject ( paths . primaryConfigPath , buildClaudeSettingsForConfig ( settings , config ) ) ;
771+ writeJsonObject ( paths . primaryConfigPath , mergeClaudeSettingsForWesightModel ( settings , config ) ) ;
658772} ;
659773
660774const syncCodexFromWesightModel = ( ) : void => {
@@ -666,14 +780,14 @@ const syncCodexFromWesightModel = (): void => {
666780
667781 const providerName = resolved . providerMetadata ?. providerName || 'wesight' ;
668782 const paths = getCliConfigPaths ( 'codex' ) ;
669- const authPath = paths . secondaryConfigPaths [ 0 ] || path . join ( path . dirname ( paths . primaryConfigPath ) , 'auth.json' ) ;
670- const auth = readJsonObject ( authPath ) ?? { } ;
783+ const existingConfigText = fs . existsSync ( paths . primaryConfigPath )
784+ ? fs . readFileSync ( paths . primaryConfigPath , 'utf8' )
785+ : '' ;
671786
672- atomicWrite ( paths . primaryConfigPath , buildCodexConfig ( providerName , config . baseURL , config . model ) ) ;
673- writeJsonObject ( authPath , {
674- ...auth ,
675- OPENAI_API_KEY : config . apiKey ,
676- } ) ;
787+ atomicWrite (
788+ paths . primaryConfigPath ,
789+ mergeCodexConfigForWesightModel ( existingConfigText , providerName , config . baseURL , config . model ) ,
790+ ) ;
677791} ;
678792
679793export const syncOpenCodeGlobalConfigFromWesightModel = ( ) : void => {
0 commit comments