limbo-ai 1.11.0 → 1.12.0
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/cli.js +62 -11
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -50,6 +50,12 @@ const MODEL_CATALOG = {
|
|
|
50
50
|
menuModels: ['claude-opus-4-6', 'claude-sonnet-4-6'],
|
|
51
51
|
supportedModels: ['claude-opus-4-6', 'claude-sonnet-4-6', 'claude-opus-4-1', 'claude-sonnet-4'],
|
|
52
52
|
},
|
|
53
|
+
'openrouter:api-key': {
|
|
54
|
+
provider: 'openrouter',
|
|
55
|
+
defaultModel: 'auto',
|
|
56
|
+
menuModels: [],
|
|
57
|
+
supportedModels: [],
|
|
58
|
+
},
|
|
53
59
|
};
|
|
54
60
|
|
|
55
61
|
const ASCII_ART = String.raw`
|
|
@@ -211,6 +217,7 @@ const TEXT = {
|
|
|
211
217
|
providerQuestion: 'AI Provider',
|
|
212
218
|
providerOpenAI: 'Codex (OpenAI)',
|
|
213
219
|
providerAnthropic: 'Claude (Anthropic)',
|
|
220
|
+
providerOpenRouter: 'OpenRouter (100+ models)',
|
|
214
221
|
accessMethodQuestion: 'Access method',
|
|
215
222
|
accessSubscriptionOpenAI: 'ChatGPT / Codex subscription',
|
|
216
223
|
accessSubscriptionAnthropic: 'Claude Code subscription',
|
|
@@ -225,6 +232,11 @@ const TEXT = {
|
|
|
225
232
|
requiredField: 'This field is required.',
|
|
226
233
|
invalidOpenAIKey: 'OpenAI API keys usually start with "sk-".',
|
|
227
234
|
invalidAnthropicKey: 'Anthropic API keys usually start with "sk-ant-".',
|
|
235
|
+
openRouterApiKeyPrompt: ' OpenRouter API key (sk-or-...): ',
|
|
236
|
+
openRouterKeyWarn: 'OpenRouter API keys usually start with "sk-or-". Proceeding anyway.',
|
|
237
|
+
openRouterKeyHint: 'Get your key at: https://openrouter.ai/keys',
|
|
238
|
+
openRouterModelPrompt: ' Model name (blank = auto-routing): ',
|
|
239
|
+
openRouterModelHint: 'Examples: anthropic/claude-sonnet-4-6, openai/gpt-4o, google/gemini-2.5-pro',
|
|
228
240
|
telegramQuestion: 'Want to speak to Limbo through Telegram?',
|
|
229
241
|
telegramBotFatherSteps: [
|
|
230
242
|
'To create a Telegram bot:',
|
|
@@ -312,6 +324,7 @@ const TEXT = {
|
|
|
312
324
|
providerQuestion: 'AI Provider',
|
|
313
325
|
providerOpenAI: 'Codex (OpenAI)',
|
|
314
326
|
providerAnthropic: 'Claude (Anthropic)',
|
|
327
|
+
providerOpenRouter: 'OpenRouter (100+ modelos)',
|
|
315
328
|
accessMethodQuestion: 'Metodo de acceso',
|
|
316
329
|
accessSubscriptionOpenAI: 'Suscripcion ChatGPT / Codex',
|
|
317
330
|
accessSubscriptionAnthropic: 'Suscripcion Claude Code',
|
|
@@ -326,6 +339,11 @@ const TEXT = {
|
|
|
326
339
|
requiredField: 'Este campo es obligatorio.',
|
|
327
340
|
invalidOpenAIKey: 'Las API keys de OpenAI normalmente empiezan con "sk-".',
|
|
328
341
|
invalidAnthropicKey: 'Las API keys de Anthropic normalmente empiezan con "sk-ant-".',
|
|
342
|
+
openRouterApiKeyPrompt: ' OpenRouter API key (sk-or-...): ',
|
|
343
|
+
openRouterKeyWarn: 'Las API keys de OpenRouter normalmente empiezan con "sk-or-". Continuando igual.',
|
|
344
|
+
openRouterKeyHint: 'Consegui tu key en: https://openrouter.ai/keys',
|
|
345
|
+
openRouterModelPrompt: ' Nombre del modelo (vacio = auto-routing): ',
|
|
346
|
+
openRouterModelHint: 'Ejemplos: anthropic/claude-sonnet-4-6, openai/gpt-4o, google/gemini-2.5-pro',
|
|
329
347
|
telegramQuestion: 'Quieres hablar con Limbo por Telegram?',
|
|
330
348
|
telegramBotFatherSteps: [
|
|
331
349
|
'Para crear un bot de Telegram:',
|
|
@@ -590,7 +608,7 @@ function writeSecrets(cfg, existingEnv = {}) {
|
|
|
590
608
|
}
|
|
591
609
|
|
|
592
610
|
const SECRET_KEYS = new Set([
|
|
593
|
-
'LLM_API_KEY', 'OPENAI_API_KEY', 'ANTHROPIC_API_KEY',
|
|
611
|
+
'LLM_API_KEY', 'OPENAI_API_KEY', 'ANTHROPIC_API_KEY', 'OPENROUTER_API_KEY',
|
|
594
612
|
'TELEGRAM_BOT_TOKEN', 'OPENCLAW_GATEWAY_TOKEN',
|
|
595
613
|
]);
|
|
596
614
|
|
|
@@ -615,12 +633,29 @@ function waitForHealthy(lang, maxAttempts = 12) {
|
|
|
615
633
|
return false;
|
|
616
634
|
}
|
|
617
635
|
|
|
636
|
+
function deriveProviderFamily(provider) {
|
|
637
|
+
if (!provider) return 'anthropic';
|
|
638
|
+
if (provider.startsWith('openai')) return 'openai';
|
|
639
|
+
if (provider === 'openrouter') return 'openrouter';
|
|
640
|
+
return 'anthropic';
|
|
641
|
+
}
|
|
642
|
+
|
|
618
643
|
function getModelCatalog(providerFamily, authMode) {
|
|
619
644
|
return MODEL_CATALOG[`${providerFamily}:${authMode}`];
|
|
620
645
|
}
|
|
621
646
|
|
|
622
647
|
async function chooseModel(lang, providerFamily, authMode) {
|
|
623
648
|
const catalog = getModelCatalog(providerFamily, authMode);
|
|
649
|
+
|
|
650
|
+
if (!catalog.menuModels.length) {
|
|
651
|
+
console.log(` ${c.dim}${t(lang, 'openRouterModelHint')}${c.reset}`);
|
|
652
|
+
const modelName = await promptValidated(
|
|
653
|
+
t(lang, 'openRouterModelPrompt'),
|
|
654
|
+
(value) => ({ ok: true, value: value || catalog.defaultModel }),
|
|
655
|
+
);
|
|
656
|
+
return modelName;
|
|
657
|
+
}
|
|
658
|
+
|
|
624
659
|
const options = catalog.menuModels.map((model) => ({ label: model, value: model }));
|
|
625
660
|
options.push({ label: t(lang, 'customModel'), value: '__custom__' });
|
|
626
661
|
|
|
@@ -654,17 +689,23 @@ async function collectConfig(existingEnv = {}) {
|
|
|
654
689
|
const providerFamily = (await selectMenu(t(language, 'providerQuestion'), [
|
|
655
690
|
{ label: t(language, 'providerOpenAI'), value: 'openai' },
|
|
656
691
|
{ label: t(language, 'providerAnthropic'), value: 'anthropic' },
|
|
692
|
+
{ label: t(language, 'providerOpenRouter'), value: 'openrouter' },
|
|
657
693
|
], language)).value;
|
|
658
694
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
695
|
+
let accessMethod;
|
|
696
|
+
if (providerFamily === 'openrouter') {
|
|
697
|
+
accessMethod = 'api-key';
|
|
698
|
+
} else {
|
|
699
|
+
accessMethod = (await selectMenu(t(language, 'accessMethodQuestion'), [
|
|
700
|
+
{
|
|
701
|
+
label: providerFamily === 'openai'
|
|
702
|
+
? t(language, 'accessSubscriptionOpenAI')
|
|
703
|
+
: t(language, 'accessSubscriptionAnthropic'),
|
|
704
|
+
value: 'subscription',
|
|
705
|
+
},
|
|
706
|
+
{ label: t(language, 'accessApiKey'), value: 'api-key' },
|
|
707
|
+
], language)).value;
|
|
708
|
+
}
|
|
668
709
|
|
|
669
710
|
const modelName = await chooseModel(language, providerFamily, accessMethod);
|
|
670
711
|
const provider = getModelCatalog(providerFamily, accessMethod).provider;
|
|
@@ -680,6 +721,16 @@ async function collectConfig(existingEnv = {}) {
|
|
|
680
721
|
return { ok: true, value };
|
|
681
722
|
},
|
|
682
723
|
);
|
|
724
|
+
} else if (providerFamily === 'openrouter') {
|
|
725
|
+
console.log(` ${c.dim}${t(language, 'openRouterKeyHint')}${c.reset}`);
|
|
726
|
+
apiKey = await promptValidated(
|
|
727
|
+
t(language, 'openRouterApiKeyPrompt'),
|
|
728
|
+
(value) => {
|
|
729
|
+
if (!value) return { ok: false, message: t(language, 'requiredField') };
|
|
730
|
+
if (!value.startsWith('sk-or-')) warn(t(language, 'openRouterKeyWarn'));
|
|
731
|
+
return { ok: true, value };
|
|
732
|
+
},
|
|
733
|
+
);
|
|
683
734
|
} else {
|
|
684
735
|
apiKey = await promptValidated(
|
|
685
736
|
t(language, 'anthropicApiKeyPrompt'),
|
|
@@ -1138,7 +1189,7 @@ async function cmdStart() {
|
|
|
1138
1189
|
cfg = {
|
|
1139
1190
|
language: lang,
|
|
1140
1191
|
provider: existingEnv.MODEL_PROVIDER || 'anthropic',
|
|
1141
|
-
providerFamily: (existingEnv.MODEL_PROVIDER
|
|
1192
|
+
providerFamily: deriveProviderFamily(existingEnv.MODEL_PROVIDER),
|
|
1142
1193
|
authMode: existingEnv.AUTH_MODE || 'api-key',
|
|
1143
1194
|
modelName: existingEnv.MODEL_NAME || 'claude-opus-4-6',
|
|
1144
1195
|
telegramEnabled: existingEnv.TELEGRAM_ENABLED || 'false',
|