(timeoutMs = 6000)
| 412 | * with no key, no network, or no /models support simply contributes nothing. |
| 413 | */ |
| 414 | export async function detectConfiguredApiModels(timeoutMs = 6000): Promise<ApiCatalogModel[]> { |
| 415 | // The `setup` command runs the wizard WITHOUT the main bootstrap, so ~/.qodex/.env |
| 416 | // hasn't been loaded into process.env yet — without this, every configured provider |
| 417 | // looks key-less and we'd discover nothing. Existing env values still win (no clobber). |
| 418 | try { |
| 419 | const { loadEnvFileIntoProcess } = await import('./env-writer.js'); |
| 420 | await loadEnvFileIntoProcess(); |
| 421 | } catch { /* best-effort */ } |
| 422 | |
| 423 | let cfg: any; |
| 424 | try { cfg = await loadConfig(process.cwd()); } catch { return []; } |
| 425 | const customRaw = (cfg?.providers as any)?.custom; |
| 426 | if (!Array.isArray(customRaw) || customRaw.length === 0) return []; |
| 427 | const { providers: defs } = validateCustomProviders(customRaw); |
| 428 | const active = defs.filter(d => !!process.env[d.apiKeyEnv]); |
| 429 | if (active.length === 0) return []; |
| 430 | |
| 431 | const out: ApiCatalogModel[] = []; |
| 432 | await Promise.allSettled(active.map(async def => { |
| 433 | const provider = new CustomOpenAIProvider(def, process.env[def.apiKeyEnv]); |
| 434 | const models = await Promise.race<ModelInfo[]>([ |
| 435 | provider.listModels(), |
| 436 | new Promise<ModelInfo[]>(resolve => setTimeout(() => resolve([]), timeoutMs)), |
| 437 | ]); |
| 438 | for (const m of models) out.push({ provider: def.name, id: m.id, contextWindow: m.contextWindow }); |
| 439 | })); |
| 440 | return out; |
| 441 | } |
| 442 | |
| 443 | export function buildModelChoices( |
| 444 | hw: HardwareProfile, |
no test coverage detected