@@ -342,14 +342,14 @@ const PURPLE = RGBA.fromHex("#9370DB")`,
342342 } ,
343343 } ,
344344
345- // Model provider configuration (remove Zen, add qBraid)
346- // This replaces the entire models.ts to use embedded models
345+ // Model provider configuration
346+ // When exclusive=true: replace get() to return only embedded models (original behavior)
347+ // When default=true, exclusive=false: prepend branded models to the models.dev response
347348 {
348349 pattern : "packages/opencode/src/provider/models.ts" ,
349350 transform : async ( content , config ) => {
350- if ( ! config . models ?. exclusive || ! config . models ?. source ) return content
351+ if ( ! config . models ?. source ) return content
351352
352- // Read the models JSON
353353 const modelsPath = path . join ( BRAND_DIR , config . models . source . replace ( "./" , "" ) )
354354 const modelsFile = Bun . file ( modelsPath )
355355 if ( ! ( await modelsFile . exists ( ) ) ) {
@@ -358,16 +358,53 @@ const PURPLE = RGBA.fromHex("#9370DB")`,
358358 }
359359
360360 const modelsJson = await modelsFile . json ( )
361- // Remove schema and comment keys
362361 delete modelsJson . $schema
363362 delete modelsJson . _comment
364363
365- // Replace the get() function with one that returns embedded models directly
364+ if ( config . models . exclusive ) {
365+ // Exclusive mode: only branded models, no models.dev fetch
366+ return content . replace (
367+ / e x p o r t a s y n c f u n c t i o n g e t \( \) \{ [ \s \S ] * ?\n \} / ,
368+ `export async function get() {
369+ // Branding: embedded models (exclusive mode, no external fetch)
370+ return ${ JSON . stringify ( modelsJson ) } as Record<string, Provider>
371+ }` ,
372+ )
373+ }
374+
375+ // Default mode: prepend branded models, then merge models.dev data
376+ // This ensures qBraid models appear first and are the defaults,
377+ // while all upstream providers (Anthropic, OpenAI, Copilot, Codex, etc.)
378+ // remain available.
379+ const brandedModelsStr = JSON . stringify ( modelsJson )
366380 return content . replace (
367381 / e x p o r t a s y n c f u n c t i o n g e t \( \) \{ [ \s \S ] * ?\n \} / ,
368382 `export async function get() {
369- // Branding: embedded models (no external fetch)
370- return ${ JSON . stringify ( modelsJson ) } as Record<string, Provider>
383+ // Branding: qBraid models prepended as defaults
384+ const branded = ${ brandedModelsStr } as Record<string, Provider>
385+
386+ // Fetch upstream models from models.dev (or cache)
387+ let upstream: Record<string, Provider> = {}
388+ try {
389+ const cached = await readCache()
390+ if (cached) {
391+ upstream = cached
392+ } else {
393+ const response = await fetch(url)
394+ if (response.ok) {
395+ upstream = await response.json() as Record<string, Provider>
396+ await writeCache(upstream)
397+ }
398+ }
399+ } catch (e) {
400+ // Fall back to bundled snapshot if fetch fails
401+ try {
402+ upstream = (await import("./models-snapshot")).default as Record<string, Provider>
403+ } catch {}
404+ }
405+
406+ // Merge: branded providers first, then upstream (branded wins on conflict)
407+ return { ...upstream, ...branded }
371408 }` ,
372409 )
373410 } ,
@@ -386,32 +423,29 @@ const PURPLE = RGBA.fromHex("#9370DB")`,
386423 } ,
387424 } ,
388425
389- // Remove builtin plugins (they don't exist for qBraid branding)
426+ // Remove builtin plugins only in exclusive mode.
427+ // In default mode (exclusive=false), keep plugins so Anthropic auth,
428+ // Codex OAuth, Copilot device code, etc. continue to work.
390429 {
391430 pattern : "packages/opencode/src/plugin/index.ts" ,
392431 transform : ( content , config ) => {
393432 if ( ! config . models ?. exclusive ) return content
394433
395- // Clear the BUILTIN array - these npm packages don't exist for branded versions
396- // Match the array with its contents across potential newlines
397434 return content . replace (
398435 / c o n s t B U I L T I N = \[ " [ ^ " ] * " (?: , \s * " [ ^ " ] * " ) * \] / ,
399436 "const BUILTIN: string[] = [] // Cleared by branding - no external plugins" ,
400437 )
401438 } ,
402439 } ,
403440
404- // Remove custom loaders for providers that don't exist in exclusive models
441+ // Remove custom loaders only in exclusive mode.
442+ // In default mode, keep all loaders — they're needed for native provider support
443+ // (Anthropic, OpenAI, Bedrock, Copilot, etc.).
405444 {
406445 pattern : "packages/opencode/src/provider/provider.ts" ,
407446 transform : ( content , config ) => {
408447 if ( ! config . models ?. exclusive ) return content
409448
410- // Comment out all custom loaders when in exclusive mode
411- // This prevents "Provider does not exist in model list" errors
412- // Match the CUSTOM_LOADERS object definition and replace with empty object
413- // The object starts at "const CUSTOM_LOADERS: Record<string, CustomLoader> = {"
414- // and ends with " }" before "export const Model"
415449 return content . replace (
416450 / c o n s t C U S T O M _ L O A D E R S : R e c o r d < s t r i n g , C u s t o m L o a d e r > = \{ [ \s \S ] * ?\n \} (? = \n \n e x p o r t c o n s t M o d e l ) / ,
417451 `const CUSTOM_LOADERS: Record<string, CustomLoader> = {
0 commit comments