iosm-cli 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/README.md +8 -7
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -3
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/models-dev-provider-catalog.d.ts +30 -0
- package/dist/core/models-dev-provider-catalog.d.ts.map +1 -0
- package/dist/core/models-dev-provider-catalog.js +118 -0
- package/dist/core/models-dev-provider-catalog.js.map +1 -0
- package/dist/core/models-dev-providers.d.ts +12 -0
- package/dist/core/models-dev-providers.d.ts.map +1 -0
- package/dist/core/models-dev-providers.js +736 -0
- package/dist/core/models-dev-providers.js.map +1 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +62 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +3 -11
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts +13 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +89 -27
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +13 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +261 -21
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/configuration.md +4 -1
- package/docs/getting-started.md +2 -2
- package/docs/interactive-mode.md +1 -1
- package/package.json +2 -2
|
@@ -15,7 +15,9 @@ import { parseSkillBlock } from "../../core/agent-session.js";
|
|
|
15
15
|
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
16
16
|
import { KeybindingsManager } from "../../core/keybindings.js";
|
|
17
17
|
import { createCompactionSummaryMessage, INTERNAL_UI_META_CUSTOM_TYPE, isInternalUiMetaDetails, } from "../../core/messages.js";
|
|
18
|
+
import { loadModelsDevProviderCatalog, } from "../../core/models-dev-provider-catalog.js";
|
|
18
19
|
import { ModelRegistry } from "../../core/model-registry.js";
|
|
20
|
+
import { MODELS_DEV_PROVIDERS } from "../../core/models-dev-providers.js";
|
|
19
21
|
import { resolveModelScope } from "../../core/model-resolver.js";
|
|
20
22
|
import { getMcpCommandHelp, parseMcpAddCommand, parseMcpTargetCommand, } from "../../core/mcp/index.js";
|
|
21
23
|
import { addMemoryEntry, getMemoryFilePath, readMemoryEntries, removeMemoryEntry, updateMemoryEntry, } from "../../core/memory.js";
|
|
@@ -272,6 +274,41 @@ function resolveDoctorCliToolStatuses() {
|
|
|
272
274
|
});
|
|
273
275
|
}
|
|
274
276
|
const OPENROUTER_PROVIDER_ID = "openrouter";
|
|
277
|
+
const PROVIDER_DISPLAY_NAME_OVERRIDES = {
|
|
278
|
+
"azure-openai-responses": "Azure OpenAI Responses",
|
|
279
|
+
"google-antigravity": "Google Antigravity",
|
|
280
|
+
"google-gemini-cli": "Google Gemini CLI",
|
|
281
|
+
"kimi-coding": "Kimi Coding",
|
|
282
|
+
"openai-codex": "OpenAI Codex",
|
|
283
|
+
"opencode-go": "OpenCode Go",
|
|
284
|
+
"vercel-ai-gateway": "Vercel AI Gateway",
|
|
285
|
+
};
|
|
286
|
+
function toProviderDisplayName(providerId) {
|
|
287
|
+
const override = PROVIDER_DISPLAY_NAME_OVERRIDES[providerId];
|
|
288
|
+
if (override)
|
|
289
|
+
return override;
|
|
290
|
+
return providerId
|
|
291
|
+
.split(/[-_]/g)
|
|
292
|
+
.map((part) => {
|
|
293
|
+
const lower = part.toLowerCase();
|
|
294
|
+
if (lower === "ai")
|
|
295
|
+
return "AI";
|
|
296
|
+
if (lower === "api")
|
|
297
|
+
return "API";
|
|
298
|
+
if (lower === "gpt")
|
|
299
|
+
return "GPT";
|
|
300
|
+
if (lower === "aws")
|
|
301
|
+
return "AWS";
|
|
302
|
+
if (lower === "ui")
|
|
303
|
+
return "UI";
|
|
304
|
+
if (lower === "llm")
|
|
305
|
+
return "LLM";
|
|
306
|
+
if (lower === "id")
|
|
307
|
+
return "ID";
|
|
308
|
+
return part.charAt(0).toUpperCase() + part.slice(1);
|
|
309
|
+
})
|
|
310
|
+
.join(" ");
|
|
311
|
+
}
|
|
275
312
|
function isAbortLikeMessage(message) {
|
|
276
313
|
const normalized = message.trim().toLowerCase();
|
|
277
314
|
return normalized.includes("aborted") || normalized.includes("cancelled");
|
|
@@ -421,6 +458,17 @@ export class InteractiveMode {
|
|
|
421
458
|
this.builtInHeader = undefined;
|
|
422
459
|
// ASCII logo component for startup screen
|
|
423
460
|
this.asciiLogo = undefined;
|
|
461
|
+
// API-key provider labels cached for /login and status messages.
|
|
462
|
+
this.apiKeyProviderDisplayNames = new Map();
|
|
463
|
+
this.modelsDevProviderCatalog = MODELS_DEV_PROVIDERS;
|
|
464
|
+
this.modelsDevProviderCatalogById = new Map(MODELS_DEV_PROVIDERS.map((provider) => [
|
|
465
|
+
provider.id,
|
|
466
|
+
{
|
|
467
|
+
...provider,
|
|
468
|
+
models: [],
|
|
469
|
+
},
|
|
470
|
+
]));
|
|
471
|
+
this.modelsDevProviderCatalogRefreshPromise = undefined;
|
|
424
472
|
// Custom header from extension (undefined = use built-in header)
|
|
425
473
|
this.customHeader = undefined;
|
|
426
474
|
/**
|
|
@@ -1197,6 +1245,8 @@ export class InteractiveMode {
|
|
|
1197
1245
|
// Both are needed: fd for autocomplete, rg for grep tool and bash commands
|
|
1198
1246
|
const [fdPath] = await Promise.all([ensureTool("fd"), ensureTool("rg")]);
|
|
1199
1247
|
this.fdPath = fdPath;
|
|
1248
|
+
// Restore saved default model early so startup header/session are consistent after restart.
|
|
1249
|
+
await this.restoreSavedModelSelectionOnStartup();
|
|
1200
1250
|
// Add header container as first child
|
|
1201
1251
|
this.ui.addChild(this.headerContainer);
|
|
1202
1252
|
// Add header with keybindings from config (unless silenced)
|
|
@@ -1275,6 +1325,8 @@ export class InteractiveMode {
|
|
|
1275
1325
|
this.footerDataProvider.onBranchChange(() => {
|
|
1276
1326
|
this.ui.requestRender();
|
|
1277
1327
|
});
|
|
1328
|
+
// Refresh provider catalog from models.dev in background once per startup.
|
|
1329
|
+
void this.refreshModelsDevProviderCatalog();
|
|
1278
1330
|
// Initialize available provider count for footer display
|
|
1279
1331
|
await this.updateAvailableProviderCount();
|
|
1280
1332
|
}
|
|
@@ -1431,7 +1483,10 @@ export class InteractiveMode {
|
|
|
1431
1483
|
this.showError(`models.json error: ${modelsJsonError}`);
|
|
1432
1484
|
}
|
|
1433
1485
|
if (modelFallbackMessage) {
|
|
1434
|
-
this.
|
|
1486
|
+
const staleNoModelsWarning = this.session.model && modelFallbackMessage.startsWith("No models available.");
|
|
1487
|
+
if (!staleNoModelsWarning) {
|
|
1488
|
+
this.showWarning(modelFallbackMessage);
|
|
1489
|
+
}
|
|
1435
1490
|
}
|
|
1436
1491
|
if (!this.session.model) {
|
|
1437
1492
|
this.showWarning("No model selected. Choose a model to start.");
|
|
@@ -3870,7 +3925,7 @@ export class InteractiveMode {
|
|
|
3870
3925
|
this.updateEditorBorderColor();
|
|
3871
3926
|
this.refreshBuiltInHeader();
|
|
3872
3927
|
const thinkingStr = result.model.reasoning && result.thinkingLevel !== "off" ? ` (thinking: ${result.thinkingLevel})` : "";
|
|
3873
|
-
this.showStatus(`Switched to ${result.model.
|
|
3928
|
+
this.showStatus(`Switched to ${result.model.provider}/${result.model.id}${thinkingStr}`);
|
|
3874
3929
|
}
|
|
3875
3930
|
}
|
|
3876
3931
|
catch (error) {
|
|
@@ -4910,7 +4965,7 @@ export class InteractiveMode {
|
|
|
4910
4965
|
this.footer.invalidate();
|
|
4911
4966
|
this.updateEditorBorderColor();
|
|
4912
4967
|
this.refreshBuiltInHeader();
|
|
4913
|
-
this.showStatus(`Model: ${model.id}`);
|
|
4968
|
+
this.showStatus(`Model: ${model.provider}/${model.id}`);
|
|
4914
4969
|
this.checkDaxnutsEasterEgg(model);
|
|
4915
4970
|
}
|
|
4916
4971
|
catch (error) {
|
|
@@ -4921,6 +4976,7 @@ export class InteractiveMode {
|
|
|
4921
4976
|
this.showModelSelector(searchTerm);
|
|
4922
4977
|
}
|
|
4923
4978
|
async showModelProviderSelector(preferredProvider) {
|
|
4979
|
+
await this.hydrateMissingProviderModelsForSavedAuth();
|
|
4924
4980
|
this.session.modelRegistry.refresh();
|
|
4925
4981
|
let models = [];
|
|
4926
4982
|
try {
|
|
@@ -5032,7 +5088,7 @@ export class InteractiveMode {
|
|
|
5032
5088
|
this.updateEditorBorderColor();
|
|
5033
5089
|
this.refreshBuiltInHeader();
|
|
5034
5090
|
done();
|
|
5035
|
-
this.showStatus(`Model: ${model.id}`);
|
|
5091
|
+
this.showStatus(`Model: ${model.provider}/${model.id}`);
|
|
5036
5092
|
this.checkDaxnutsEasterEgg(model);
|
|
5037
5093
|
}
|
|
5038
5094
|
catch (error) {
|
|
@@ -5341,22 +5397,29 @@ export class InteractiveMode {
|
|
|
5341
5397
|
return;
|
|
5342
5398
|
}
|
|
5343
5399
|
}
|
|
5400
|
+
await this.refreshModelsDevProviderCatalog();
|
|
5401
|
+
const apiKeyProviders = this.getApiKeyLoginProviders(this.modelsDevProviderCatalog);
|
|
5344
5402
|
this.showSelector((done) => {
|
|
5345
|
-
const selector = new OAuthSelectorComponent(mode, this.session.modelRegistry.authStorage, async (
|
|
5403
|
+
const selector = new OAuthSelectorComponent(mode, this.session.modelRegistry.authStorage, async (provider) => {
|
|
5346
5404
|
done();
|
|
5347
5405
|
if (mode === "login") {
|
|
5348
|
-
if (
|
|
5349
|
-
|
|
5406
|
+
if (provider.kind === "api_key") {
|
|
5407
|
+
if (provider.id === OPENROUTER_PROVIDER_ID) {
|
|
5408
|
+
await this.handleOpenRouterApiKeyLogin();
|
|
5409
|
+
}
|
|
5410
|
+
else {
|
|
5411
|
+
await this.handleApiKeyLogin(provider.id, { providerName: provider.name });
|
|
5412
|
+
}
|
|
5350
5413
|
}
|
|
5351
5414
|
else {
|
|
5352
|
-
await this.showLoginDialog(
|
|
5415
|
+
await this.showLoginDialog(provider.id);
|
|
5353
5416
|
}
|
|
5354
5417
|
}
|
|
5355
5418
|
else {
|
|
5356
5419
|
// Logout flow
|
|
5357
|
-
const providerName = this.getProviderDisplayName(
|
|
5420
|
+
const providerName = this.getProviderDisplayName(provider.id);
|
|
5358
5421
|
try {
|
|
5359
|
-
this.session.modelRegistry.authStorage.logout(
|
|
5422
|
+
this.session.modelRegistry.authStorage.logout(provider.id);
|
|
5360
5423
|
this.session.modelRegistry.refresh();
|
|
5361
5424
|
await this.updateAvailableProviderCount();
|
|
5362
5425
|
this.showStatus(`Logged out of ${providerName}`);
|
|
@@ -5368,21 +5431,179 @@ export class InteractiveMode {
|
|
|
5368
5431
|
}, () => {
|
|
5369
5432
|
done();
|
|
5370
5433
|
this.ui.requestRender();
|
|
5371
|
-
});
|
|
5434
|
+
}, apiKeyProviders);
|
|
5372
5435
|
return { component: selector, focus: selector };
|
|
5373
5436
|
});
|
|
5374
5437
|
}
|
|
5438
|
+
async refreshModelsDevProviderCatalog() {
|
|
5439
|
+
if (this.modelsDevProviderCatalogRefreshPromise) {
|
|
5440
|
+
await this.modelsDevProviderCatalogRefreshPromise;
|
|
5441
|
+
return;
|
|
5442
|
+
}
|
|
5443
|
+
this.modelsDevProviderCatalogRefreshPromise = (async () => {
|
|
5444
|
+
const catalog = await loadModelsDevProviderCatalog();
|
|
5445
|
+
this.modelsDevProviderCatalogById = catalog;
|
|
5446
|
+
this.modelsDevProviderCatalog = Array.from(catalog.values())
|
|
5447
|
+
.map((provider) => ({
|
|
5448
|
+
id: provider.id,
|
|
5449
|
+
name: provider.name,
|
|
5450
|
+
env: provider.env,
|
|
5451
|
+
}))
|
|
5452
|
+
.sort((a, b) => a.name.localeCompare(b.name, "en") || a.id.localeCompare(b.id, "en"));
|
|
5453
|
+
})()
|
|
5454
|
+
.catch(() => {
|
|
5455
|
+
this.modelsDevProviderCatalog = MODELS_DEV_PROVIDERS;
|
|
5456
|
+
this.modelsDevProviderCatalogById = new Map(MODELS_DEV_PROVIDERS.map((provider) => [
|
|
5457
|
+
provider.id,
|
|
5458
|
+
{
|
|
5459
|
+
...provider,
|
|
5460
|
+
models: [],
|
|
5461
|
+
},
|
|
5462
|
+
]));
|
|
5463
|
+
})
|
|
5464
|
+
.finally(() => {
|
|
5465
|
+
this.modelsDevProviderCatalogRefreshPromise = undefined;
|
|
5466
|
+
});
|
|
5467
|
+
await this.modelsDevProviderCatalogRefreshPromise;
|
|
5468
|
+
}
|
|
5469
|
+
resolveModelsDevApi(modelNpm) {
|
|
5470
|
+
const npm = modelNpm?.toLowerCase() ?? "";
|
|
5471
|
+
if (npm.includes("anthropic"))
|
|
5472
|
+
return "anthropic-messages";
|
|
5473
|
+
if (npm.includes("google-vertex"))
|
|
5474
|
+
return "google-vertex";
|
|
5475
|
+
if (npm.includes("google"))
|
|
5476
|
+
return "google-generative-ai";
|
|
5477
|
+
if (npm.includes("amazon-bedrock"))
|
|
5478
|
+
return "bedrock-converse-stream";
|
|
5479
|
+
if (npm.includes("mistral"))
|
|
5480
|
+
return "mistral-conversations";
|
|
5481
|
+
if (npm.includes("@ai-sdk/openai") && !npm.includes("compatible"))
|
|
5482
|
+
return "openai-responses";
|
|
5483
|
+
return "openai-completions";
|
|
5484
|
+
}
|
|
5485
|
+
buildModelsDevProviderConfig(providerInfo) {
|
|
5486
|
+
const baseUrl = providerInfo.api ?? providerInfo.models.find((model) => !!model.api)?.api;
|
|
5487
|
+
if (!baseUrl)
|
|
5488
|
+
return undefined;
|
|
5489
|
+
if (providerInfo.models.length === 0)
|
|
5490
|
+
return undefined;
|
|
5491
|
+
const models = providerInfo.models.map((model) => ({
|
|
5492
|
+
id: model.id,
|
|
5493
|
+
name: model.name,
|
|
5494
|
+
api: this.resolveModelsDevApi(model.npm ?? providerInfo.npm),
|
|
5495
|
+
reasoning: model.reasoning,
|
|
5496
|
+
input: [...model.input],
|
|
5497
|
+
cost: model.cost,
|
|
5498
|
+
contextWindow: model.contextWindow,
|
|
5499
|
+
maxTokens: model.maxTokens,
|
|
5500
|
+
headers: Object.keys(model.headers).length > 0 ? model.headers : undefined,
|
|
5501
|
+
}));
|
|
5502
|
+
return {
|
|
5503
|
+
baseUrl,
|
|
5504
|
+
models,
|
|
5505
|
+
};
|
|
5506
|
+
}
|
|
5507
|
+
hasRegisteredProviderModels(providerId) {
|
|
5508
|
+
const registry = this.session.modelRegistry;
|
|
5509
|
+
if (typeof registry.getAll !== "function")
|
|
5510
|
+
return true;
|
|
5511
|
+
return registry.getAll().some((model) => model.provider === providerId);
|
|
5512
|
+
}
|
|
5513
|
+
async hydrateProviderModelsFromModelsDev(providerId) {
|
|
5514
|
+
if (this.hasRegisteredProviderModels(providerId))
|
|
5515
|
+
return true;
|
|
5516
|
+
await this.refreshModelsDevProviderCatalog();
|
|
5517
|
+
const providerInfo = this.modelsDevProviderCatalogById.get(providerId);
|
|
5518
|
+
if (!providerInfo)
|
|
5519
|
+
return false;
|
|
5520
|
+
const config = this.buildModelsDevProviderConfig(providerInfo);
|
|
5521
|
+
if (!config)
|
|
5522
|
+
return false;
|
|
5523
|
+
try {
|
|
5524
|
+
this.session.modelRegistry.registerProvider(providerId, config);
|
|
5525
|
+
return this.hasRegisteredProviderModels(providerId);
|
|
5526
|
+
}
|
|
5527
|
+
catch {
|
|
5528
|
+
return false;
|
|
5529
|
+
}
|
|
5530
|
+
}
|
|
5531
|
+
async hydrateMissingProviderModelsForSavedAuth() {
|
|
5532
|
+
const savedProviders = this.session.modelRegistry.authStorage.list();
|
|
5533
|
+
if (savedProviders.length === 0)
|
|
5534
|
+
return;
|
|
5535
|
+
for (const providerId of savedProviders) {
|
|
5536
|
+
if (this.hasRegisteredProviderModels(providerId))
|
|
5537
|
+
continue;
|
|
5538
|
+
await this.hydrateProviderModelsFromModelsDev(providerId);
|
|
5539
|
+
}
|
|
5540
|
+
}
|
|
5541
|
+
async restoreSavedModelSelectionOnStartup() {
|
|
5542
|
+
if (this.session.model)
|
|
5543
|
+
return;
|
|
5544
|
+
const defaultProvider = this.settingsManager.getDefaultProvider();
|
|
5545
|
+
const defaultModelId = this.settingsManager.getDefaultModel();
|
|
5546
|
+
if (!defaultProvider || !defaultModelId)
|
|
5547
|
+
return;
|
|
5548
|
+
if (!this.hasRegisteredProviderModels(defaultProvider)) {
|
|
5549
|
+
await this.hydrateProviderModelsFromModelsDev(defaultProvider);
|
|
5550
|
+
}
|
|
5551
|
+
const model = this.session.modelRegistry.find(defaultProvider, defaultModelId);
|
|
5552
|
+
if (!model)
|
|
5553
|
+
return;
|
|
5554
|
+
try {
|
|
5555
|
+
const apiKey = await this.session.modelRegistry.getApiKey(model);
|
|
5556
|
+
if (!apiKey)
|
|
5557
|
+
return;
|
|
5558
|
+
}
|
|
5559
|
+
catch {
|
|
5560
|
+
return;
|
|
5561
|
+
}
|
|
5562
|
+
this.session.agent.setModel(model);
|
|
5563
|
+
}
|
|
5564
|
+
getApiKeyLoginProviders(modelsDevProviders) {
|
|
5565
|
+
const providerNames = new Map();
|
|
5566
|
+
this.apiKeyProviderDisplayNames.clear();
|
|
5567
|
+
for (const model of this.session.modelRegistry.getAll()) {
|
|
5568
|
+
if (!providerNames.has(model.provider)) {
|
|
5569
|
+
providerNames.set(model.provider, toProviderDisplayName(model.provider));
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
for (const provider of modelsDevProviders) {
|
|
5573
|
+
const fallbackName = toProviderDisplayName(provider.id);
|
|
5574
|
+
const current = providerNames.get(provider.id);
|
|
5575
|
+
if (!current || current === fallbackName) {
|
|
5576
|
+
providerNames.set(provider.id, provider.name || fallbackName);
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5579
|
+
for (const providerId of this.session.modelRegistry.authStorage.list()) {
|
|
5580
|
+
if (!providerNames.has(providerId)) {
|
|
5581
|
+
providerNames.set(providerId, toProviderDisplayName(providerId));
|
|
5582
|
+
}
|
|
5583
|
+
}
|
|
5584
|
+
const oauthProviderIds = new Set(this.session.modelRegistry.authStorage.getOAuthProviders().map((provider) => provider.id));
|
|
5585
|
+
const providers = [];
|
|
5586
|
+
for (const [id, name] of providerNames.entries()) {
|
|
5587
|
+
if (oauthProviderIds.has(id))
|
|
5588
|
+
continue;
|
|
5589
|
+
this.apiKeyProviderDisplayNames.set(id, name);
|
|
5590
|
+
providers.push({ id, name, kind: "api_key" });
|
|
5591
|
+
}
|
|
5592
|
+
providers.sort((a, b) => a.name.localeCompare(b.name));
|
|
5593
|
+
return providers;
|
|
5594
|
+
}
|
|
5375
5595
|
getProviderDisplayName(providerId) {
|
|
5376
|
-
|
|
5377
|
-
|
|
5596
|
+
const apiKeyName = this.apiKeyProviderDisplayNames.get(providerId);
|
|
5597
|
+
if (apiKeyName) {
|
|
5598
|
+
return apiKeyName;
|
|
5378
5599
|
}
|
|
5379
5600
|
const providerInfo = this.session.modelRegistry.authStorage.getOAuthProviders().find((p) => p.id === providerId);
|
|
5380
|
-
return providerInfo?.name || providerId;
|
|
5601
|
+
return providerInfo?.name || toProviderDisplayName(providerId);
|
|
5381
5602
|
}
|
|
5382
|
-
async
|
|
5603
|
+
async handleApiKeyLogin(providerId, options) {
|
|
5604
|
+
const providerName = options?.providerName || this.getProviderDisplayName(providerId);
|
|
5383
5605
|
const openModelSelector = options?.openModelSelector ?? true;
|
|
5384
|
-
const
|
|
5385
|
-
const existingCredential = this.session.modelRegistry.authStorage.get(OPENROUTER_PROVIDER_ID);
|
|
5606
|
+
const existingCredential = this.session.modelRegistry.authStorage.get(providerId);
|
|
5386
5607
|
if (existingCredential) {
|
|
5387
5608
|
const overwrite = await this.showExtensionConfirm(`${providerName}: replace existing credentials?`, `Stored at ${getAuthPath()}`);
|
|
5388
5609
|
if (!overwrite) {
|
|
@@ -5390,7 +5611,11 @@ export class InteractiveMode {
|
|
|
5390
5611
|
return;
|
|
5391
5612
|
}
|
|
5392
5613
|
}
|
|
5393
|
-
const
|
|
5614
|
+
const promptLines = [`${providerName} API key`];
|
|
5615
|
+
if (options?.createKeyUrl) {
|
|
5616
|
+
promptLines.push(`Create key: ${options.createKeyUrl}`);
|
|
5617
|
+
}
|
|
5618
|
+
const keyInput = await this.showExtensionInput(promptLines.join("\n"), options?.placeholder ?? "api-key");
|
|
5394
5619
|
if (keyInput === undefined) {
|
|
5395
5620
|
this.showStatus(`${providerName} login cancelled.`);
|
|
5396
5621
|
return;
|
|
@@ -5400,13 +5625,28 @@ export class InteractiveMode {
|
|
|
5400
5625
|
this.showWarning(`${providerName} API key cannot be empty.`);
|
|
5401
5626
|
return;
|
|
5402
5627
|
}
|
|
5403
|
-
this.session.modelRegistry.authStorage.set(
|
|
5628
|
+
this.session.modelRegistry.authStorage.set(providerId, { type: "api_key", key: apiKey });
|
|
5629
|
+
let hasProviderModels = this.hasRegisteredProviderModels(providerId);
|
|
5630
|
+
if (!hasProviderModels) {
|
|
5631
|
+
hasProviderModels = await this.hydrateProviderModelsFromModelsDev(providerId);
|
|
5632
|
+
}
|
|
5404
5633
|
await this.updateAvailableProviderCount();
|
|
5405
5634
|
this.showStatus(`${providerName} API key saved to ${getAuthPath()}`);
|
|
5406
|
-
if (openModelSelector) {
|
|
5407
|
-
await this.showModelProviderSelector(
|
|
5635
|
+
if (openModelSelector && hasProviderModels) {
|
|
5636
|
+
await this.showModelProviderSelector(providerId);
|
|
5637
|
+
}
|
|
5638
|
+
else if (openModelSelector) {
|
|
5639
|
+
this.showWarning(`${providerName} configured, but no models are available yet. Run /model after network is available.`);
|
|
5408
5640
|
}
|
|
5409
5641
|
}
|
|
5642
|
+
async handleOpenRouterApiKeyLogin(options) {
|
|
5643
|
+
await this.handleApiKeyLogin(OPENROUTER_PROVIDER_ID, {
|
|
5644
|
+
providerName: "OpenRouter",
|
|
5645
|
+
openModelSelector: options?.openModelSelector,
|
|
5646
|
+
createKeyUrl: "https://openrouter.ai/keys",
|
|
5647
|
+
placeholder: "sk-or-v1-...",
|
|
5648
|
+
});
|
|
5649
|
+
}
|
|
5410
5650
|
async showLoginDialog(providerId) {
|
|
5411
5651
|
const providerInfo = this.session.modelRegistry.authStorage.getOAuthProviders().find((p) => p.id === providerId);
|
|
5412
5652
|
const providerName = this.getProviderDisplayName(providerId);
|