@@ -86,6 +86,7 @@ const MINI_MAX_MAX_POLL_INTERVAL_MS = 10000;
8686const MINI_MAX_OAUTH_REQUEST_TIMEOUT_MS = 15000 ;
8787const MINI_MAX_OAUTH_TOKEN_REQUEST_TIMEOUT_MS = 15000 ;
8888const OPENCLAW_COMMAND_TIMEOUT_MS = 30000 ;
89+ const NEXU_OFFICIAL_PROVIDER_ID = "nexu" ;
8990const OLLAMA_DEFAULT_BASE_URL = "http://127.0.0.1:11434" ;
9091const OLLAMA_DUMMY_API_KEY = "ollama-local" ;
9192
@@ -112,6 +113,34 @@ function hasSameModels(current: string[], expected: string[]): boolean {
112113 ) ;
113114}
114115
116+ function hasSameCloudModels (
117+ current : ReadonlyArray < {
118+ id : string ;
119+ name ?: string | null ;
120+ provider ?: string | null ;
121+ } > ,
122+ next : ReadonlyArray < {
123+ id : string ;
124+ name ?: string | null ;
125+ provider ?: string | null ;
126+ } > ,
127+ ) : boolean {
128+ const toStableKey = ( model : {
129+ id : string ;
130+ name ?: string | null ;
131+ provider ?: string | null ;
132+ } ) : string =>
133+ `${ model . id } \u0000${ model . name ?? "" } \u0000${ model . provider ?? "" } ` ;
134+
135+ const currentKeys = current . map ( toStableKey ) . sort ( ) ;
136+ const nextKeys = next . map ( toStableKey ) . sort ( ) ;
137+
138+ return (
139+ currentKeys . length === nextKeys . length &&
140+ currentKeys . every ( ( key , index ) => key === nextKeys [ index ] )
141+ ) ;
142+ }
143+
115144const PROVIDER_BASE_URLS : Record < string , string > = {
116145 anthropic : "https://api.anthropic.com/v1" ,
117146 openai : "https://api.openai.com/v1" ,
@@ -400,6 +429,46 @@ export class ModelProviderService {
400429 } ;
401430 }
402431
432+ async refreshNexuOfficialModels ( ) : Promise < {
433+ connected : boolean ;
434+ refreshed : boolean ;
435+ changed : boolean ;
436+ modelCount : number ;
437+ } > {
438+ const before = await this . configStore . getDesktopCloudStatus ( ) ;
439+ if ( ! before . connected ) {
440+ return {
441+ connected : false ,
442+ refreshed : false ,
443+ changed : false ,
444+ modelCount : before . models . length ,
445+ } ;
446+ }
447+
448+ const next = await this . configStore . refreshDesktopCloudModels ( ) ;
449+ const changed = ! hasSameCloudModels ( before . models , next . models ) ;
450+
451+ if ( changed ) {
452+ await this . ensureValidDefaultModel ( ) ;
453+ await this . openclawSyncService . syncAll ( ) ;
454+ logger . info (
455+ {
456+ provider : NEXU_OFFICIAL_PROVIDER_ID ,
457+ previousModelCount : before . models . length ,
458+ modelCount : next . models . length ,
459+ } ,
460+ "nexu_official_models_refreshed" ,
461+ ) ;
462+ }
463+
464+ return {
465+ connected : true ,
466+ refreshed : true ,
467+ changed,
468+ modelCount : next . models . length ,
469+ } ;
470+ }
471+
403472 async upsertProvider (
404473 providerId : string ,
405474 input : Parameters < NexuConfigStore [ "upsertProvider" ] > [ 1 ] ,
0 commit comments