mcp-agent-foundry 1.1.0 → 1.2.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/README.md +22 -2
- package/dist/cli/setup-wizard.d.ts.map +1 -1
- package/dist/cli/setup-wizard.js +873 -8
- package/dist/cli/setup-wizard.js.map +1 -1
- package/dist/cli/test-connection.d.ts +28 -0
- package/dist/cli/test-connection.d.ts.map +1 -1
- package/dist/cli/test-connection.js +335 -1
- package/dist/cli/test-connection.js.map +1 -1
- package/dist/cli.d.ts +13 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +169 -1
- package/dist/cli.js.map +1 -1
- package/dist/config/validator.d.ts +113 -0
- package/dist/config/validator.d.ts.map +1 -1
- package/dist/config/validator.js +113 -0
- package/dist/config/validator.js.map +1 -1
- package/dist/failover/health-tracker.d.ts +175 -0
- package/dist/failover/health-tracker.d.ts.map +1 -0
- package/dist/failover/health-tracker.js +350 -0
- package/dist/failover/health-tracker.js.map +1 -0
- package/dist/failover/index.d.ts +9 -0
- package/dist/failover/index.d.ts.map +1 -0
- package/dist/failover/index.js +9 -0
- package/dist/failover/index.js.map +1 -0
- package/dist/failover/orchestrator.d.ts +189 -0
- package/dist/failover/orchestrator.d.ts.map +1 -0
- package/dist/failover/orchestrator.js +488 -0
- package/dist/failover/orchestrator.js.map +1 -0
- package/dist/failover/pricing.d.ts +115 -0
- package/dist/failover/pricing.d.ts.map +1 -0
- package/dist/failover/pricing.js +283 -0
- package/dist/failover/pricing.js.map +1 -0
- package/dist/persistence/state-schema.d.ts +50 -0
- package/dist/persistence/state-schema.d.ts.map +1 -1
- package/dist/persistence/state-schema.js +2 -0
- package/dist/persistence/state-schema.js.map +1 -1
- package/dist/providers/fireworks.d.ts +23 -0
- package/dist/providers/fireworks.d.ts.map +1 -0
- package/dist/providers/fireworks.js +31 -0
- package/dist/providers/fireworks.js.map +1 -0
- package/dist/providers/groq.d.ts +23 -0
- package/dist/providers/groq.d.ts.map +1 -0
- package/dist/providers/groq.js +31 -0
- package/dist/providers/groq.js.map +1 -0
- package/dist/providers/kimi-code.d.ts +32 -0
- package/dist/providers/kimi-code.d.ts.map +1 -0
- package/dist/providers/kimi-code.js +46 -0
- package/dist/providers/kimi-code.js.map +1 -0
- package/dist/providers/kimi.d.ts +1 -1
- package/dist/providers/kimi.js +1 -1
- package/dist/providers/openrouter.d.ts +23 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.js +31 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/perplexity.d.ts +29 -0
- package/dist/providers/perplexity.d.ts.map +1 -0
- package/dist/providers/perplexity.js +51 -0
- package/dist/providers/perplexity.js.map +1 -0
- package/dist/providers/together.d.ts +23 -0
- package/dist/providers/together.d.ts.map +1 -0
- package/dist/providers/together.js +31 -0
- package/dist/providers/together.js.map +1 -0
- package/dist/router/engine.d.ts +21 -0
- package/dist/router/engine.d.ts.map +1 -1
- package/dist/router/engine.js +81 -21
- package/dist/router/engine.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +49 -0
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +52 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/setup-wizard.js
CHANGED
|
@@ -18,7 +18,7 @@ import * as path from "node:path";
|
|
|
18
18
|
import * as os from "node:os";
|
|
19
19
|
import { stringify as yamlStringify } from "yaml";
|
|
20
20
|
import { getUserConfigPath, getUserConfigDir } from "../config/defaults.js";
|
|
21
|
-
import { testOpenAIConnection, testGeminiConnection, testDeepSeekConnection, testZaiConnection, testKimiConnection, testOllamaConnection, testAnthropicConnection, testConnectionWithSpinner, } from "./test-connection.js";
|
|
21
|
+
import { testOpenAIConnection, testGeminiConnection, testDeepSeekConnection, testZaiConnection, testKimiConnection, testKimiCodeConnection, testOllamaConnection, testAnthropicConnection, testPerplexityConnection, testOpenRouterConnection, testGroqConnection, testTogetherConnection, testFireworksConnection, testConnectionWithSpinner, } from "./test-connection.js";
|
|
22
22
|
// ============================================================================
|
|
23
23
|
// Constants - Updated January 2026
|
|
24
24
|
// ============================================================================
|
|
@@ -32,7 +32,13 @@ const PROVIDER_ENV_VARS = {
|
|
|
32
32
|
deepseek: "DEEPSEEK_API_KEY",
|
|
33
33
|
zai: "ZAI_API_KEY",
|
|
34
34
|
kimi: "KIMI_API_KEY",
|
|
35
|
+
"kimi-code": "KIMI_API_KEY", // Uses same API key as regular Kimi
|
|
35
36
|
ollama: "",
|
|
37
|
+
perplexity: "PERPLEXITY_API_KEY",
|
|
38
|
+
openrouter: "OPENROUTER_API_KEY",
|
|
39
|
+
groq: "GROQ_API_KEY",
|
|
40
|
+
together: "TOGETHER_API_KEY",
|
|
41
|
+
fireworks: "FIREWORKS_API_KEY",
|
|
36
42
|
};
|
|
37
43
|
/**
|
|
38
44
|
* Track which environment variables were configured during setup
|
|
@@ -85,6 +91,42 @@ const AVAILABLE_PROVIDERS = [
|
|
|
85
91
|
hint: "Kimi K2.5 - Excellent coding, 1M context window",
|
|
86
92
|
supportsSubscription: false,
|
|
87
93
|
},
|
|
94
|
+
{
|
|
95
|
+
value: "kimi-code",
|
|
96
|
+
label: "Kimi Code",
|
|
97
|
+
hint: "Dedicated coding endpoint - uses Kimi subscription",
|
|
98
|
+
supportsSubscription: false,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
value: "perplexity",
|
|
102
|
+
label: "Perplexity",
|
|
103
|
+
hint: "Sonar models - Web search grounded, citations",
|
|
104
|
+
supportsSubscription: false,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
value: "openrouter",
|
|
108
|
+
label: "OpenRouter",
|
|
109
|
+
hint: "Gateway to 300+ models via single API",
|
|
110
|
+
supportsSubscription: false,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
value: "groq",
|
|
114
|
+
label: "Groq",
|
|
115
|
+
hint: "Ultra-fast LPU inference - 18x faster",
|
|
116
|
+
supportsSubscription: false,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
value: "together",
|
|
120
|
+
label: "Together AI",
|
|
121
|
+
hint: "200+ open models, sub-100ms latency",
|
|
122
|
+
supportsSubscription: false,
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
value: "fireworks",
|
|
126
|
+
label: "Fireworks AI",
|
|
127
|
+
hint: "Fast inference, DeepSeek/Llama hosting",
|
|
128
|
+
supportsSubscription: false,
|
|
129
|
+
},
|
|
88
130
|
{
|
|
89
131
|
value: "ollama",
|
|
90
132
|
label: "Ollama",
|
|
@@ -157,6 +199,72 @@ const KIMI_MODELS = [
|
|
|
157
199
|
{ value: "moonshot-v1-128k", label: "Moonshot V1 128K", hint: "Stable, 128K context" },
|
|
158
200
|
{ value: "moonshot-v1-32k", label: "Moonshot V1 32K", hint: "Fast, 32K context" },
|
|
159
201
|
];
|
|
202
|
+
/**
|
|
203
|
+
* Kimi Code models (January 2026)
|
|
204
|
+
* Separate API endpoint for coding-focused Kimi
|
|
205
|
+
* Uses Kimi subscription, no separate API credits needed
|
|
206
|
+
*/
|
|
207
|
+
const KIMI_CODE_MODELS = [
|
|
208
|
+
{ value: "kimi-for-coding", label: "Kimi for Coding", hint: "Optimized for code generation" },
|
|
209
|
+
];
|
|
210
|
+
/**
|
|
211
|
+
* Perplexity models (January 2026)
|
|
212
|
+
* https://docs.perplexity.ai/
|
|
213
|
+
* Web search grounded responses with citations
|
|
214
|
+
*/
|
|
215
|
+
const PERPLEXITY_MODELS = [
|
|
216
|
+
{ value: "sonar-deep-research", label: "Sonar Deep Research", hint: "Deep research with citations" },
|
|
217
|
+
{ value: "sonar-reasoning-pro", label: "Sonar Reasoning Pro", hint: "Advanced reasoning + search" },
|
|
218
|
+
{ value: "sonar-reasoning", label: "Sonar Reasoning", hint: "Reasoning with web grounding" },
|
|
219
|
+
{ value: "sonar-pro", label: "Sonar Pro", hint: "Enhanced search capabilities" },
|
|
220
|
+
{ value: "sonar", label: "Sonar", hint: "Fast search-grounded responses" },
|
|
221
|
+
{ value: "r1-1776", label: "R1-1776", hint: "Reasoning model" },
|
|
222
|
+
];
|
|
223
|
+
/**
|
|
224
|
+
* OpenRouter models (January 2026)
|
|
225
|
+
* https://openrouter.ai/models
|
|
226
|
+
* Gateway to 300+ models
|
|
227
|
+
*/
|
|
228
|
+
const OPENROUTER_MODELS = [
|
|
229
|
+
{ value: "openrouter/auto", label: "Auto", hint: "Automatically selects best model" },
|
|
230
|
+
{ value: "anthropic/claude-3.5-sonnet", label: "Claude 3.5 Sonnet", hint: "Via OpenRouter" },
|
|
231
|
+
{ value: "openai/gpt-4o", label: "GPT-4o", hint: "Via OpenRouter" },
|
|
232
|
+
{ value: "google/gemini-pro-1.5", label: "Gemini Pro 1.5", hint: "Via OpenRouter" },
|
|
233
|
+
{ value: "meta-llama/llama-3.3-70b-instruct", label: "Llama 3.3 70B", hint: "Via OpenRouter" },
|
|
234
|
+
];
|
|
235
|
+
/**
|
|
236
|
+
* Groq models (January 2026)
|
|
237
|
+
* https://console.groq.com/docs/models
|
|
238
|
+
* Ultra-fast LPU inference
|
|
239
|
+
*/
|
|
240
|
+
const GROQ_MODELS = [
|
|
241
|
+
{ value: "llama-3.3-70b-versatile", label: "Llama 3.3 70B Versatile", hint: "Best all-around" },
|
|
242
|
+
{ value: "llama-3.1-8b-instant", label: "Llama 3.1 8B Instant", hint: "Ultra-fast, small" },
|
|
243
|
+
{ value: "mixtral-8x7b-32768", label: "Mixtral 8x7B", hint: "MoE model, 32K context" },
|
|
244
|
+
{ value: "gemma2-9b-it", label: "Gemma 2 9B IT", hint: "Google's open model" },
|
|
245
|
+
];
|
|
246
|
+
/**
|
|
247
|
+
* Together AI models (January 2026)
|
|
248
|
+
* https://docs.together.ai/docs/inference-models
|
|
249
|
+
* 200+ open models with sub-100ms latency
|
|
250
|
+
*/
|
|
251
|
+
const TOGETHER_MODELS = [
|
|
252
|
+
{ value: "meta-llama/Llama-3.3-70B-Instruct-Turbo", label: "Llama 3.3 70B Turbo", hint: "Fast, high quality" },
|
|
253
|
+
{ value: "deepseek-ai/DeepSeek-V3", label: "DeepSeek V3", hint: "Advanced reasoning" },
|
|
254
|
+
{ value: "Qwen/Qwen2.5-72B-Instruct-Turbo", label: "Qwen 2.5 72B Turbo", hint: "Strong multilingual" },
|
|
255
|
+
{ value: "mistralai/Mixtral-8x22B-Instruct-v0.1", label: "Mixtral 8x22B", hint: "Large MoE model" },
|
|
256
|
+
];
|
|
257
|
+
/**
|
|
258
|
+
* Fireworks AI models (January 2026)
|
|
259
|
+
* https://docs.fireworks.ai/models/models
|
|
260
|
+
* Fast inference for open models
|
|
261
|
+
*/
|
|
262
|
+
const FIREWORKS_MODELS = [
|
|
263
|
+
{ value: "accounts/fireworks/models/llama-v3p3-70b-instruct", label: "Llama 3.3 70B", hint: "Fast inference" },
|
|
264
|
+
{ value: "accounts/fireworks/models/deepseek-v3", label: "DeepSeek V3", hint: "Advanced reasoning" },
|
|
265
|
+
{ value: "accounts/fireworks/models/mixtral-8x22b-instruct", label: "Mixtral 8x22B", hint: "Large MoE" },
|
|
266
|
+
{ value: "accounts/fireworks/models/qwen2p5-72b-instruct", label: "Qwen 2.5 72B", hint: "Multilingual" },
|
|
267
|
+
];
|
|
160
268
|
/**
|
|
161
269
|
* Ollama local models
|
|
162
270
|
*/
|
|
@@ -326,6 +434,18 @@ function getProviderModels(providerName) {
|
|
|
326
434
|
return ZAI_MODELS;
|
|
327
435
|
case "kimi":
|
|
328
436
|
return KIMI_MODELS;
|
|
437
|
+
case "kimi-code":
|
|
438
|
+
return KIMI_CODE_MODELS;
|
|
439
|
+
case "perplexity":
|
|
440
|
+
return PERPLEXITY_MODELS;
|
|
441
|
+
case "openrouter":
|
|
442
|
+
return OPENROUTER_MODELS;
|
|
443
|
+
case "groq":
|
|
444
|
+
return GROQ_MODELS;
|
|
445
|
+
case "together":
|
|
446
|
+
return TOGETHER_MODELS;
|
|
447
|
+
case "fireworks":
|
|
448
|
+
return FIREWORKS_MODELS;
|
|
329
449
|
case "ollama":
|
|
330
450
|
return OLLAMA_MODELS;
|
|
331
451
|
default:
|
|
@@ -335,6 +455,34 @@ function getProviderModels(providerName) {
|
|
|
335
455
|
function getProviderInfo(providerName) {
|
|
336
456
|
return AVAILABLE_PROVIDERS.find((p) => p.value === providerName);
|
|
337
457
|
}
|
|
458
|
+
/**
|
|
459
|
+
* Get display name for a provider.
|
|
460
|
+
*/
|
|
461
|
+
function getProviderDisplayName(provider) {
|
|
462
|
+
const displayNames = {
|
|
463
|
+
anthropic: "Anthropic (Claude)",
|
|
464
|
+
openai: "OpenAI (GPT)",
|
|
465
|
+
google: "Google (Gemini)",
|
|
466
|
+
deepseek: "DeepSeek",
|
|
467
|
+
zai: "Z.AI (GLM)",
|
|
468
|
+
kimi: "Moonshot (Kimi)",
|
|
469
|
+
"kimi-code": "Kimi Code",
|
|
470
|
+
ollama: "Ollama (Local)",
|
|
471
|
+
perplexity: "Perplexity",
|
|
472
|
+
openrouter: "OpenRouter",
|
|
473
|
+
groq: "Groq",
|
|
474
|
+
together: "Together AI",
|
|
475
|
+
fireworks: "Fireworks AI",
|
|
476
|
+
};
|
|
477
|
+
return displayNames[provider] || provider;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Get default model for a provider.
|
|
481
|
+
*/
|
|
482
|
+
function getDefaultModelForProvider(provider) {
|
|
483
|
+
const models = getProviderModels(provider);
|
|
484
|
+
return models[0]?.value ?? "default";
|
|
485
|
+
}
|
|
338
486
|
// ============================================================================
|
|
339
487
|
// Provider Configuration Functions
|
|
340
488
|
// ============================================================================
|
|
@@ -737,7 +885,7 @@ async function configureKimi() {
|
|
|
737
885
|
return {
|
|
738
886
|
access_mode: "api",
|
|
739
887
|
api_key: "${" + envVarName + "}",
|
|
740
|
-
base_url: "https://api.moonshot.
|
|
888
|
+
base_url: "https://api.moonshot.ai/v1",
|
|
741
889
|
default_model: defaultModel,
|
|
742
890
|
};
|
|
743
891
|
}
|
|
@@ -757,12 +905,347 @@ async function configureKimi() {
|
|
|
757
905
|
return {
|
|
758
906
|
access_mode: "api",
|
|
759
907
|
api_key: "${" + envVarName + "}",
|
|
760
|
-
base_url: "https://api.moonshot.
|
|
908
|
+
base_url: "https://api.moonshot.ai/v1",
|
|
761
909
|
default_model: "kimi-k2-5-preview",
|
|
762
910
|
};
|
|
763
911
|
}
|
|
764
912
|
}
|
|
765
913
|
}
|
|
914
|
+
/**
|
|
915
|
+
* Configure Kimi Code provider
|
|
916
|
+
*
|
|
917
|
+
* Kimi Code is a separate API endpoint from the regular Moonshot API.
|
|
918
|
+
* Uses the same Kimi subscription/API key but different base URL.
|
|
919
|
+
* CRITICAL: Requires User-Agent: claude-code/1.0 header.
|
|
920
|
+
*/
|
|
921
|
+
async function configureKimiCode() {
|
|
922
|
+
p.log.step(color.bold("Kimi Code Configuration"));
|
|
923
|
+
p.note("Kimi Code is a dedicated coding endpoint.\n" +
|
|
924
|
+
color.bold("Uses your Kimi subscription - no separate API credits needed!") + "\n\n" +
|
|
925
|
+
"Uses your existing Kimi/Moonshot API key.\n" +
|
|
926
|
+
"Get your API key from:\n" +
|
|
927
|
+
color.cyan("https://platform.moonshot.cn/console/api-keys"), "Kimi Code Setup");
|
|
928
|
+
while (true) {
|
|
929
|
+
const apiKey = await p.password({
|
|
930
|
+
message: "Enter your Kimi API key:",
|
|
931
|
+
validate: (v) => validateApiKey(v, "kimi-code"),
|
|
932
|
+
});
|
|
933
|
+
if (p.isCancel(apiKey))
|
|
934
|
+
return null;
|
|
935
|
+
const result = await testConnectionWithSpinner("Kimi Code", () => testKimiCodeConnection(apiKey));
|
|
936
|
+
if (result.success) {
|
|
937
|
+
const defaultModel = await p.select({
|
|
938
|
+
message: "Select default Kimi Code model:",
|
|
939
|
+
options: KIMI_CODE_MODELS,
|
|
940
|
+
});
|
|
941
|
+
if (p.isCancel(defaultModel))
|
|
942
|
+
return null;
|
|
943
|
+
const envVarName = PROVIDER_ENV_VARS["kimi-code"];
|
|
944
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
945
|
+
return {
|
|
946
|
+
access_mode: "api",
|
|
947
|
+
api_key: "${" + envVarName + "}",
|
|
948
|
+
base_url: "https://api.kimi.com/coding/v1",
|
|
949
|
+
default_model: defaultModel,
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
const action = await p.select({
|
|
953
|
+
message: "Connection test failed. What would you like to do?",
|
|
954
|
+
options: [
|
|
955
|
+
{ value: "retry", label: "Re-enter API key" },
|
|
956
|
+
{ value: "skip", label: "Skip this provider" },
|
|
957
|
+
{ value: "add", label: "Add anyway" },
|
|
958
|
+
],
|
|
959
|
+
});
|
|
960
|
+
if (p.isCancel(action) || action === "skip")
|
|
961
|
+
return null;
|
|
962
|
+
if (action === "add") {
|
|
963
|
+
const envVarName = PROVIDER_ENV_VARS["kimi-code"];
|
|
964
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
965
|
+
return {
|
|
966
|
+
access_mode: "api",
|
|
967
|
+
api_key: "${" + envVarName + "}",
|
|
968
|
+
base_url: "https://api.kimi.com/coding/v1",
|
|
969
|
+
default_model: "kimi-for-coding",
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* Configure Perplexity provider (API only)
|
|
976
|
+
*/
|
|
977
|
+
async function configurePerplexity() {
|
|
978
|
+
p.log.step(color.bold("Perplexity Configuration"));
|
|
979
|
+
p.note("Perplexity provides web search grounded responses with citations.\n" +
|
|
980
|
+
"Sonar models return real-time web information.\n\n" +
|
|
981
|
+
"Get your API key from:\n" +
|
|
982
|
+
color.cyan("https://www.perplexity.ai/settings/api"), "Perplexity Setup");
|
|
983
|
+
while (true) {
|
|
984
|
+
const apiKey = await p.password({
|
|
985
|
+
message: "Enter your Perplexity API key:",
|
|
986
|
+
validate: (v) => validateApiKey(v, "perplexity"),
|
|
987
|
+
});
|
|
988
|
+
if (p.isCancel(apiKey))
|
|
989
|
+
return null;
|
|
990
|
+
const result = await testConnectionWithSpinner("Perplexity", () => testPerplexityConnection(apiKey));
|
|
991
|
+
if (result.success) {
|
|
992
|
+
const defaultModel = await p.select({
|
|
993
|
+
message: "Select default Perplexity model:",
|
|
994
|
+
options: PERPLEXITY_MODELS,
|
|
995
|
+
});
|
|
996
|
+
if (p.isCancel(defaultModel))
|
|
997
|
+
return null;
|
|
998
|
+
const envVarName = PROVIDER_ENV_VARS["perplexity"];
|
|
999
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
1000
|
+
return {
|
|
1001
|
+
access_mode: "api",
|
|
1002
|
+
api_key: "${" + envVarName + "}",
|
|
1003
|
+
base_url: "https://api.perplexity.ai",
|
|
1004
|
+
default_model: defaultModel,
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
const action = await p.select({
|
|
1008
|
+
message: "Connection test failed. What would you like to do?",
|
|
1009
|
+
options: [
|
|
1010
|
+
{ value: "retry", label: "Re-enter API key" },
|
|
1011
|
+
{ value: "skip", label: "Skip this provider" },
|
|
1012
|
+
{ value: "add", label: "Add anyway" },
|
|
1013
|
+
],
|
|
1014
|
+
});
|
|
1015
|
+
if (p.isCancel(action) || action === "skip")
|
|
1016
|
+
return null;
|
|
1017
|
+
if (action === "add") {
|
|
1018
|
+
const envVarName = PROVIDER_ENV_VARS["perplexity"];
|
|
1019
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
1020
|
+
return {
|
|
1021
|
+
access_mode: "api",
|
|
1022
|
+
api_key: "${" + envVarName + "}",
|
|
1023
|
+
base_url: "https://api.perplexity.ai",
|
|
1024
|
+
default_model: "sonar",
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Configure OpenRouter provider (API only)
|
|
1031
|
+
*/
|
|
1032
|
+
async function configureOpenRouter() {
|
|
1033
|
+
p.log.step(color.bold("OpenRouter Configuration"));
|
|
1034
|
+
p.note("OpenRouter provides access to 300+ models via a single API.\n" +
|
|
1035
|
+
"Use 'openrouter/auto' to automatically select the best model.\n\n" +
|
|
1036
|
+
"Get your API key from:\n" +
|
|
1037
|
+
color.cyan("https://openrouter.ai/keys"), "OpenRouter Setup");
|
|
1038
|
+
while (true) {
|
|
1039
|
+
const apiKey = await p.password({
|
|
1040
|
+
message: "Enter your OpenRouter API key:",
|
|
1041
|
+
validate: (v) => validateApiKey(v, "openrouter"),
|
|
1042
|
+
});
|
|
1043
|
+
if (p.isCancel(apiKey))
|
|
1044
|
+
return null;
|
|
1045
|
+
const result = await testConnectionWithSpinner("OpenRouter", () => testOpenRouterConnection(apiKey));
|
|
1046
|
+
if (result.success) {
|
|
1047
|
+
const defaultModel = await p.select({
|
|
1048
|
+
message: "Select default OpenRouter model:",
|
|
1049
|
+
options: OPENROUTER_MODELS,
|
|
1050
|
+
});
|
|
1051
|
+
if (p.isCancel(defaultModel))
|
|
1052
|
+
return null;
|
|
1053
|
+
const envVarName = PROVIDER_ENV_VARS["openrouter"];
|
|
1054
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
1055
|
+
return {
|
|
1056
|
+
access_mode: "api",
|
|
1057
|
+
api_key: "${" + envVarName + "}",
|
|
1058
|
+
base_url: "https://openrouter.ai/api/v1",
|
|
1059
|
+
default_model: defaultModel,
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
const action = await p.select({
|
|
1063
|
+
message: "Connection test failed. What would you like to do?",
|
|
1064
|
+
options: [
|
|
1065
|
+
{ value: "retry", label: "Re-enter API key" },
|
|
1066
|
+
{ value: "skip", label: "Skip this provider" },
|
|
1067
|
+
{ value: "add", label: "Add anyway" },
|
|
1068
|
+
],
|
|
1069
|
+
});
|
|
1070
|
+
if (p.isCancel(action) || action === "skip")
|
|
1071
|
+
return null;
|
|
1072
|
+
if (action === "add") {
|
|
1073
|
+
const envVarName = PROVIDER_ENV_VARS["openrouter"];
|
|
1074
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
1075
|
+
return {
|
|
1076
|
+
access_mode: "api",
|
|
1077
|
+
api_key: "${" + envVarName + "}",
|
|
1078
|
+
base_url: "https://openrouter.ai/api/v1",
|
|
1079
|
+
default_model: "openrouter/auto",
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Configure Groq provider (API only)
|
|
1086
|
+
*/
|
|
1087
|
+
async function configureGroq() {
|
|
1088
|
+
p.log.step(color.bold("Groq Configuration"));
|
|
1089
|
+
p.note("Groq provides ultra-fast LPU inference - up to 18x faster than GPU.\n" +
|
|
1090
|
+
color.bold("Free tier available!") + "\n\n" +
|
|
1091
|
+
"Get your API key from:\n" +
|
|
1092
|
+
color.cyan("https://console.groq.com/keys"), "Groq Setup");
|
|
1093
|
+
while (true) {
|
|
1094
|
+
const apiKey = await p.password({
|
|
1095
|
+
message: "Enter your Groq API key:",
|
|
1096
|
+
validate: (v) => validateApiKey(v, "groq"),
|
|
1097
|
+
});
|
|
1098
|
+
if (p.isCancel(apiKey))
|
|
1099
|
+
return null;
|
|
1100
|
+
const result = await testConnectionWithSpinner("Groq", () => testGroqConnection(apiKey));
|
|
1101
|
+
if (result.success) {
|
|
1102
|
+
const defaultModel = await p.select({
|
|
1103
|
+
message: "Select default Groq model:",
|
|
1104
|
+
options: GROQ_MODELS,
|
|
1105
|
+
});
|
|
1106
|
+
if (p.isCancel(defaultModel))
|
|
1107
|
+
return null;
|
|
1108
|
+
const envVarName = PROVIDER_ENV_VARS["groq"];
|
|
1109
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
1110
|
+
return {
|
|
1111
|
+
access_mode: "api",
|
|
1112
|
+
api_key: "${" + envVarName + "}",
|
|
1113
|
+
base_url: "https://api.groq.com/openai/v1",
|
|
1114
|
+
default_model: defaultModel,
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
const action = await p.select({
|
|
1118
|
+
message: "Connection test failed. What would you like to do?",
|
|
1119
|
+
options: [
|
|
1120
|
+
{ value: "retry", label: "Re-enter API key" },
|
|
1121
|
+
{ value: "skip", label: "Skip this provider" },
|
|
1122
|
+
{ value: "add", label: "Add anyway" },
|
|
1123
|
+
],
|
|
1124
|
+
});
|
|
1125
|
+
if (p.isCancel(action) || action === "skip")
|
|
1126
|
+
return null;
|
|
1127
|
+
if (action === "add") {
|
|
1128
|
+
const envVarName = PROVIDER_ENV_VARS["groq"];
|
|
1129
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
1130
|
+
return {
|
|
1131
|
+
access_mode: "api",
|
|
1132
|
+
api_key: "${" + envVarName + "}",
|
|
1133
|
+
base_url: "https://api.groq.com/openai/v1",
|
|
1134
|
+
default_model: "llama-3.3-70b-versatile",
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Configure Together AI provider (API only)
|
|
1141
|
+
*/
|
|
1142
|
+
async function configureTogether() {
|
|
1143
|
+
p.log.step(color.bold("Together AI Configuration"));
|
|
1144
|
+
p.note("Together AI provides 200+ open models with sub-100ms latency.\n" +
|
|
1145
|
+
color.bold("$1 free credit to start!") + "\n\n" +
|
|
1146
|
+
"Get your API key from:\n" +
|
|
1147
|
+
color.cyan("https://api.together.xyz/settings/api-keys"), "Together AI Setup");
|
|
1148
|
+
while (true) {
|
|
1149
|
+
const apiKey = await p.password({
|
|
1150
|
+
message: "Enter your Together AI API key:",
|
|
1151
|
+
validate: (v) => validateApiKey(v, "together"),
|
|
1152
|
+
});
|
|
1153
|
+
if (p.isCancel(apiKey))
|
|
1154
|
+
return null;
|
|
1155
|
+
const result = await testConnectionWithSpinner("Together AI", () => testTogetherConnection(apiKey));
|
|
1156
|
+
if (result.success) {
|
|
1157
|
+
const defaultModel = await p.select({
|
|
1158
|
+
message: "Select default Together AI model:",
|
|
1159
|
+
options: TOGETHER_MODELS,
|
|
1160
|
+
});
|
|
1161
|
+
if (p.isCancel(defaultModel))
|
|
1162
|
+
return null;
|
|
1163
|
+
const envVarName = PROVIDER_ENV_VARS["together"];
|
|
1164
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
1165
|
+
return {
|
|
1166
|
+
access_mode: "api",
|
|
1167
|
+
api_key: "${" + envVarName + "}",
|
|
1168
|
+
base_url: "https://api.together.xyz/v1",
|
|
1169
|
+
default_model: defaultModel,
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
const action = await p.select({
|
|
1173
|
+
message: "Connection test failed. What would you like to do?",
|
|
1174
|
+
options: [
|
|
1175
|
+
{ value: "retry", label: "Re-enter API key" },
|
|
1176
|
+
{ value: "skip", label: "Skip this provider" },
|
|
1177
|
+
{ value: "add", label: "Add anyway" },
|
|
1178
|
+
],
|
|
1179
|
+
});
|
|
1180
|
+
if (p.isCancel(action) || action === "skip")
|
|
1181
|
+
return null;
|
|
1182
|
+
if (action === "add") {
|
|
1183
|
+
const envVarName = PROVIDER_ENV_VARS["together"];
|
|
1184
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
1185
|
+
return {
|
|
1186
|
+
access_mode: "api",
|
|
1187
|
+
api_key: "${" + envVarName + "}",
|
|
1188
|
+
base_url: "https://api.together.xyz/v1",
|
|
1189
|
+
default_model: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Configure Fireworks AI provider (API only)
|
|
1196
|
+
*/
|
|
1197
|
+
async function configureFireworks() {
|
|
1198
|
+
p.log.step(color.bold("Fireworks AI Configuration"));
|
|
1199
|
+
p.note("Fireworks AI provides fast inference for DeepSeek, Llama, and other models.\n" +
|
|
1200
|
+
color.bold("$1 free credit to start!") + "\n\n" +
|
|
1201
|
+
"Get your API key from:\n" +
|
|
1202
|
+
color.cyan("https://fireworks.ai/account/api-keys"), "Fireworks AI Setup");
|
|
1203
|
+
while (true) {
|
|
1204
|
+
const apiKey = await p.password({
|
|
1205
|
+
message: "Enter your Fireworks AI API key:",
|
|
1206
|
+
validate: (v) => validateApiKey(v, "fireworks"),
|
|
1207
|
+
});
|
|
1208
|
+
if (p.isCancel(apiKey))
|
|
1209
|
+
return null;
|
|
1210
|
+
const result = await testConnectionWithSpinner("Fireworks AI", () => testFireworksConnection(apiKey));
|
|
1211
|
+
if (result.success) {
|
|
1212
|
+
const defaultModel = await p.select({
|
|
1213
|
+
message: "Select default Fireworks AI model:",
|
|
1214
|
+
options: FIREWORKS_MODELS,
|
|
1215
|
+
});
|
|
1216
|
+
if (p.isCancel(defaultModel))
|
|
1217
|
+
return null;
|
|
1218
|
+
const envVarName = PROVIDER_ENV_VARS["fireworks"];
|
|
1219
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
1220
|
+
return {
|
|
1221
|
+
access_mode: "api",
|
|
1222
|
+
api_key: "${" + envVarName + "}",
|
|
1223
|
+
base_url: "https://api.fireworks.ai/inference/v1",
|
|
1224
|
+
default_model: defaultModel,
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
const action = await p.select({
|
|
1228
|
+
message: "Connection test failed. What would you like to do?",
|
|
1229
|
+
options: [
|
|
1230
|
+
{ value: "retry", label: "Re-enter API key" },
|
|
1231
|
+
{ value: "skip", label: "Skip this provider" },
|
|
1232
|
+
{ value: "add", label: "Add anyway" },
|
|
1233
|
+
],
|
|
1234
|
+
});
|
|
1235
|
+
if (p.isCancel(action) || action === "skip")
|
|
1236
|
+
return null;
|
|
1237
|
+
if (action === "add") {
|
|
1238
|
+
const envVarName = PROVIDER_ENV_VARS["fireworks"];
|
|
1239
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
1240
|
+
return {
|
|
1241
|
+
access_mode: "api",
|
|
1242
|
+
api_key: "${" + envVarName + "}",
|
|
1243
|
+
base_url: "https://api.fireworks.ai/inference/v1",
|
|
1244
|
+
default_model: "accounts/fireworks/models/llama-v3p3-70b-instruct",
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
766
1249
|
/**
|
|
767
1250
|
* Configure Ollama provider (local)
|
|
768
1251
|
*/
|
|
@@ -902,6 +1385,75 @@ async function configureRoles(configuredProviders, orchestratorProvider) {
|
|
|
902
1385
|
return roles;
|
|
903
1386
|
}
|
|
904
1387
|
// ============================================================================
|
|
1388
|
+
// Failover Configuration
|
|
1389
|
+
// ============================================================================
|
|
1390
|
+
/**
|
|
1391
|
+
* Configure failover settings.
|
|
1392
|
+
*/
|
|
1393
|
+
async function configureFailover(configuredProviders) {
|
|
1394
|
+
p.note("Failover automatically switches to backup providers when errors occur.\n" +
|
|
1395
|
+
"This keeps your coding agents running even during rate limits.", "Failover Configuration");
|
|
1396
|
+
const enableFailover = await p.confirm({
|
|
1397
|
+
message: "Enable automatic failover?",
|
|
1398
|
+
initialValue: true,
|
|
1399
|
+
});
|
|
1400
|
+
if (p.isCancel(enableFailover)) {
|
|
1401
|
+
p.cancel("Setup cancelled");
|
|
1402
|
+
process.exit(0);
|
|
1403
|
+
}
|
|
1404
|
+
if (!enableFailover) {
|
|
1405
|
+
return {
|
|
1406
|
+
enabled: false,
|
|
1407
|
+
preferCostEfficient: false,
|
|
1408
|
+
roleFailbacks: {},
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
const preferCostEfficient = await p.confirm({
|
|
1412
|
+
message: "Prefer cheapest healthy provider when no fallback specified?",
|
|
1413
|
+
initialValue: false,
|
|
1414
|
+
});
|
|
1415
|
+
if (p.isCancel(preferCostEfficient)) {
|
|
1416
|
+
p.cancel("Setup cancelled");
|
|
1417
|
+
process.exit(0);
|
|
1418
|
+
}
|
|
1419
|
+
// Configure fallback order for coder role if multiple providers
|
|
1420
|
+
const roleFailbacks = {};
|
|
1421
|
+
if (configuredProviders.length > 1) {
|
|
1422
|
+
const configureCoderFallback = await p.confirm({
|
|
1423
|
+
message: `Configure fallback order for the "coder" role? (${configuredProviders.length} providers available)`,
|
|
1424
|
+
initialValue: true,
|
|
1425
|
+
});
|
|
1426
|
+
if (p.isCancel(configureCoderFallback)) {
|
|
1427
|
+
p.cancel("Setup cancelled");
|
|
1428
|
+
process.exit(0);
|
|
1429
|
+
}
|
|
1430
|
+
if (configureCoderFallback) {
|
|
1431
|
+
// Create options from configured providers
|
|
1432
|
+
const providerOptions = configuredProviders.map((prov) => ({
|
|
1433
|
+
value: prov,
|
|
1434
|
+
label: getProviderDisplayName(prov),
|
|
1435
|
+
}));
|
|
1436
|
+
const fallbackSelection = await p.multiselect({
|
|
1437
|
+
message: "Select fallback providers in order of preference (first = primary, rest = fallbacks):",
|
|
1438
|
+
options: providerOptions,
|
|
1439
|
+
required: true,
|
|
1440
|
+
});
|
|
1441
|
+
if (p.isCancel(fallbackSelection)) {
|
|
1442
|
+
p.cancel("Setup cancelled");
|
|
1443
|
+
process.exit(0);
|
|
1444
|
+
}
|
|
1445
|
+
if (Array.isArray(fallbackSelection) && fallbackSelection.length > 1) {
|
|
1446
|
+
roleFailbacks["coder"] = fallbackSelection;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
return {
|
|
1451
|
+
enabled: true,
|
|
1452
|
+
preferCostEfficient: preferCostEfficient,
|
|
1453
|
+
roleFailbacks,
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
// ============================================================================
|
|
905
1457
|
// Shell Profile & Config Saving
|
|
906
1458
|
// ============================================================================
|
|
907
1459
|
function getShellProfilePath() {
|
|
@@ -959,8 +1511,8 @@ async function addEnvVarsToShellProfile() {
|
|
|
959
1511
|
p.log.error(`Failed to update ${profilePath}`);
|
|
960
1512
|
}
|
|
961
1513
|
}
|
|
962
|
-
function generateConfig(providers, roles) {
|
|
963
|
-
|
|
1514
|
+
function generateConfig(providers, roles, failoverConfig) {
|
|
1515
|
+
const config = {
|
|
964
1516
|
version: "2.0",
|
|
965
1517
|
defaults: {
|
|
966
1518
|
temperature: 0.7,
|
|
@@ -970,6 +1522,31 @@ function generateConfig(providers, roles) {
|
|
|
970
1522
|
roles,
|
|
971
1523
|
providers,
|
|
972
1524
|
};
|
|
1525
|
+
// Add failover configuration to defaults
|
|
1526
|
+
if (failoverConfig?.enabled) {
|
|
1527
|
+
config.defaults.failover = {
|
|
1528
|
+
enabled: true,
|
|
1529
|
+
prefer_cost_efficient: failoverConfig.preferCostEfficient,
|
|
1530
|
+
health_check_interval_ms: 60000,
|
|
1531
|
+
cooldown_ms: 300000,
|
|
1532
|
+
};
|
|
1533
|
+
// Add fallback chains to roles
|
|
1534
|
+
for (const [roleName, fallbackProviders] of Object.entries(failoverConfig.roleFailbacks)) {
|
|
1535
|
+
if (config.roles[roleName] && fallbackProviders.length > 1) {
|
|
1536
|
+
const [primary, ...rest] = fallbackProviders;
|
|
1537
|
+
// Update primary provider
|
|
1538
|
+
config.roles[roleName].provider = primary;
|
|
1539
|
+
// Add fallback chain
|
|
1540
|
+
config.roles[roleName].fallback_chain = {
|
|
1541
|
+
providers: rest.map((provider) => ({
|
|
1542
|
+
provider,
|
|
1543
|
+
model: getDefaultModelForProvider(provider),
|
|
1544
|
+
})),
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
return config;
|
|
973
1550
|
}
|
|
974
1551
|
async function saveConfig(config) {
|
|
975
1552
|
const configPath = getUserConfigPath();
|
|
@@ -1156,9 +1733,15 @@ export async function runSetupWizard() {
|
|
|
1156
1733
|
"Each role can use any configured provider.", "Step 4: Assign Agent Roles");
|
|
1157
1734
|
const roles = await configureRoles(configuredProviderNames, orchestratorProvider);
|
|
1158
1735
|
// ============================================================================
|
|
1736
|
+
// STEP 5: Configure Failover
|
|
1737
|
+
// ============================================================================
|
|
1738
|
+
p.note("Failover provides automatic recovery when providers fail.\n" +
|
|
1739
|
+
"Configure backup providers to keep agents running.", "Step 5: Failover Settings");
|
|
1740
|
+
const failoverConfig = await configureFailover(configuredProviderNames);
|
|
1741
|
+
// ============================================================================
|
|
1159
1742
|
// Save Configuration
|
|
1160
1743
|
// ============================================================================
|
|
1161
|
-
const config = generateConfig(providers, roles);
|
|
1744
|
+
const config = generateConfig(providers, roles, failoverConfig);
|
|
1162
1745
|
const saved = await saveConfig(config);
|
|
1163
1746
|
if (saved) {
|
|
1164
1747
|
await addEnvVarsToShellProfile();
|
|
@@ -1167,8 +1750,9 @@ export async function runSetupWizard() {
|
|
|
1167
1750
|
console.log(color.bold(" Summary"));
|
|
1168
1751
|
console.log();
|
|
1169
1752
|
console.log(` Orchestrator: ${color.cyan(orchestratorProvider)} (${orchestratorAccessMode})`);
|
|
1170
|
-
console.log(` Agent Providers: ${configuredProviderNames.filter(
|
|
1753
|
+
console.log(` Agent Providers: ${configuredProviderNames.filter(prov => prov !== orchestratorProvider).join(", ") || "none"}`);
|
|
1171
1754
|
console.log(` Roles: ${Object.keys(roles).join(", ")}`);
|
|
1755
|
+
console.log(` Failover: ${failoverConfig.enabled ? color.green("enabled") : color.dim("disabled")}`);
|
|
1172
1756
|
console.log();
|
|
1173
1757
|
console.log(` Config: ${color.dim(getUserConfigPath())}`);
|
|
1174
1758
|
console.log();
|
|
@@ -1195,6 +1779,18 @@ async function configureProviderWithAPIKey(providerType) {
|
|
|
1195
1779
|
return configureZaiAPI();
|
|
1196
1780
|
case "kimi":
|
|
1197
1781
|
return configureKimiAPI();
|
|
1782
|
+
case "kimi-code":
|
|
1783
|
+
return configureKimiCodeAPI();
|
|
1784
|
+
case "perplexity":
|
|
1785
|
+
return configurePerplexityAPI();
|
|
1786
|
+
case "openrouter":
|
|
1787
|
+
return configureOpenRouterAPI();
|
|
1788
|
+
case "groq":
|
|
1789
|
+
return configureGroqAPI();
|
|
1790
|
+
case "together":
|
|
1791
|
+
return configureTogetherAPI();
|
|
1792
|
+
case "fireworks":
|
|
1793
|
+
return configureFireworksAPI();
|
|
1198
1794
|
case "ollama":
|
|
1199
1795
|
return configureOllama();
|
|
1200
1796
|
default:
|
|
@@ -1399,7 +1995,258 @@ async function configureKimiAPI() {
|
|
|
1399
1995
|
return {
|
|
1400
1996
|
access_mode: "api",
|
|
1401
1997
|
api_key: `\${KIMI_API_KEY}`,
|
|
1402
|
-
base_url: "https://api.moonshot.
|
|
1998
|
+
base_url: "https://api.moonshot.ai/v1",
|
|
1999
|
+
default_model: model,
|
|
2000
|
+
};
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Configure Kimi Code with API key (no subscription option)
|
|
2004
|
+
*/
|
|
2005
|
+
async function configureKimiCodeAPI() {
|
|
2006
|
+
p.note("Kimi Code is a dedicated coding endpoint.\n" +
|
|
2007
|
+
color.bold("Uses your Kimi subscription - no separate API credits!") + "\n\n" +
|
|
2008
|
+
"Uses your existing Kimi/Moonshot API key.\n" +
|
|
2009
|
+
"Get your API key from:\n" +
|
|
2010
|
+
color.cyan("https://platform.moonshot.cn/console/api-keys"), "Kimi Code API Setup");
|
|
2011
|
+
const apiKey = await p.password({
|
|
2012
|
+
message: "Enter your Kimi API key:",
|
|
2013
|
+
validate: (value) => validateApiKey(value, "kimi-code"),
|
|
2014
|
+
});
|
|
2015
|
+
if (p.isCancel(apiKey))
|
|
2016
|
+
return null;
|
|
2017
|
+
const result = await testConnectionWithSpinner("Kimi Code", () => testKimiCodeConnection(apiKey));
|
|
2018
|
+
if (!result.success) {
|
|
2019
|
+
const continueAnyway = await p.confirm({
|
|
2020
|
+
message: `Connection failed: ${result.error}. Continue anyway?`,
|
|
2021
|
+
initialValue: false,
|
|
2022
|
+
});
|
|
2023
|
+
if (p.isCancel(continueAnyway) || !continueAnyway)
|
|
2024
|
+
return null;
|
|
2025
|
+
}
|
|
2026
|
+
const model = await p.select({
|
|
2027
|
+
message: "Select default Kimi Code model:",
|
|
2028
|
+
options: KIMI_CODE_MODELS.map((m) => ({
|
|
2029
|
+
value: m.value,
|
|
2030
|
+
label: m.label,
|
|
2031
|
+
...(m.hint ? { hint: m.hint } : {}),
|
|
2032
|
+
})),
|
|
2033
|
+
});
|
|
2034
|
+
if (p.isCancel(model))
|
|
2035
|
+
return null;
|
|
2036
|
+
// Store API key for later shell profile export (uses same key as regular Kimi)
|
|
2037
|
+
configuredEnvVars.set("KIMI_API_KEY", apiKey);
|
|
2038
|
+
return {
|
|
2039
|
+
access_mode: "api",
|
|
2040
|
+
api_key: `\${KIMI_API_KEY}`,
|
|
2041
|
+
base_url: "https://api.kimi.com/coding/v1",
|
|
2042
|
+
default_model: model,
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
/**
|
|
2046
|
+
* Configure Perplexity with API key (no subscription option)
|
|
2047
|
+
*/
|
|
2048
|
+
async function configurePerplexityAPI() {
|
|
2049
|
+
p.note("Perplexity provides web search grounded responses with citations.\n" +
|
|
2050
|
+
"Get your API key from:\n" +
|
|
2051
|
+
color.cyan("https://www.perplexity.ai/settings/api"), "Perplexity API Setup");
|
|
2052
|
+
const apiKey = await p.password({
|
|
2053
|
+
message: "Enter your Perplexity API key:",
|
|
2054
|
+
validate: (value) => validateApiKey(value, "perplexity"),
|
|
2055
|
+
});
|
|
2056
|
+
if (p.isCancel(apiKey))
|
|
2057
|
+
return null;
|
|
2058
|
+
const result = await testConnectionWithSpinner("Perplexity", () => testPerplexityConnection(apiKey));
|
|
2059
|
+
if (!result.success) {
|
|
2060
|
+
const continueAnyway = await p.confirm({
|
|
2061
|
+
message: `Connection failed: ${result.error}. Continue anyway?`,
|
|
2062
|
+
initialValue: false,
|
|
2063
|
+
});
|
|
2064
|
+
if (p.isCancel(continueAnyway) || !continueAnyway)
|
|
2065
|
+
return null;
|
|
2066
|
+
}
|
|
2067
|
+
const model = await p.select({
|
|
2068
|
+
message: "Select default Perplexity model:",
|
|
2069
|
+
options: PERPLEXITY_MODELS.map((m) => ({
|
|
2070
|
+
value: m.value,
|
|
2071
|
+
label: m.label,
|
|
2072
|
+
...(m.hint ? { hint: m.hint } : {}),
|
|
2073
|
+
})),
|
|
2074
|
+
});
|
|
2075
|
+
if (p.isCancel(model))
|
|
2076
|
+
return null;
|
|
2077
|
+
// Store API key for later shell profile export
|
|
2078
|
+
configuredEnvVars.set("PERPLEXITY_API_KEY", apiKey);
|
|
2079
|
+
return {
|
|
2080
|
+
access_mode: "api",
|
|
2081
|
+
api_key: `\${PERPLEXITY_API_KEY}`,
|
|
2082
|
+
base_url: "https://api.perplexity.ai",
|
|
2083
|
+
default_model: model,
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
* Configure OpenRouter with API key (no subscription option)
|
|
2088
|
+
*/
|
|
2089
|
+
async function configureOpenRouterAPI() {
|
|
2090
|
+
p.note("OpenRouter provides access to 300+ models via a single API.\n" +
|
|
2091
|
+
"Get your API key from:\n" +
|
|
2092
|
+
color.cyan("https://openrouter.ai/keys"), "OpenRouter API Setup");
|
|
2093
|
+
const apiKey = await p.password({
|
|
2094
|
+
message: "Enter your OpenRouter API key:",
|
|
2095
|
+
validate: (value) => validateApiKey(value, "openrouter"),
|
|
2096
|
+
});
|
|
2097
|
+
if (p.isCancel(apiKey))
|
|
2098
|
+
return null;
|
|
2099
|
+
const result = await testConnectionWithSpinner("OpenRouter", () => testOpenRouterConnection(apiKey));
|
|
2100
|
+
if (!result.success) {
|
|
2101
|
+
const continueAnyway = await p.confirm({
|
|
2102
|
+
message: `Connection failed: ${result.error}. Continue anyway?`,
|
|
2103
|
+
initialValue: false,
|
|
2104
|
+
});
|
|
2105
|
+
if (p.isCancel(continueAnyway) || !continueAnyway)
|
|
2106
|
+
return null;
|
|
2107
|
+
}
|
|
2108
|
+
const model = await p.select({
|
|
2109
|
+
message: "Select default OpenRouter model:",
|
|
2110
|
+
options: OPENROUTER_MODELS.map((m) => ({
|
|
2111
|
+
value: m.value,
|
|
2112
|
+
label: m.label,
|
|
2113
|
+
...(m.hint ? { hint: m.hint } : {}),
|
|
2114
|
+
})),
|
|
2115
|
+
});
|
|
2116
|
+
if (p.isCancel(model))
|
|
2117
|
+
return null;
|
|
2118
|
+
// Store API key for later shell profile export
|
|
2119
|
+
configuredEnvVars.set("OPENROUTER_API_KEY", apiKey);
|
|
2120
|
+
return {
|
|
2121
|
+
access_mode: "api",
|
|
2122
|
+
api_key: `\${OPENROUTER_API_KEY}`,
|
|
2123
|
+
base_url: "https://openrouter.ai/api/v1",
|
|
2124
|
+
default_model: model,
|
|
2125
|
+
};
|
|
2126
|
+
}
|
|
2127
|
+
/**
|
|
2128
|
+
* Configure Groq with API key (no subscription option)
|
|
2129
|
+
*/
|
|
2130
|
+
async function configureGroqAPI() {
|
|
2131
|
+
p.note("Groq provides ultra-fast LPU inference - up to 18x faster than GPU.\n" +
|
|
2132
|
+
color.bold("Free tier available!") + "\n" +
|
|
2133
|
+
"Get your API key from:\n" +
|
|
2134
|
+
color.cyan("https://console.groq.com/keys"), "Groq API Setup");
|
|
2135
|
+
const apiKey = await p.password({
|
|
2136
|
+
message: "Enter your Groq API key:",
|
|
2137
|
+
validate: (value) => validateApiKey(value, "groq"),
|
|
2138
|
+
});
|
|
2139
|
+
if (p.isCancel(apiKey))
|
|
2140
|
+
return null;
|
|
2141
|
+
const result = await testConnectionWithSpinner("Groq", () => testGroqConnection(apiKey));
|
|
2142
|
+
if (!result.success) {
|
|
2143
|
+
const continueAnyway = await p.confirm({
|
|
2144
|
+
message: `Connection failed: ${result.error}. Continue anyway?`,
|
|
2145
|
+
initialValue: false,
|
|
2146
|
+
});
|
|
2147
|
+
if (p.isCancel(continueAnyway) || !continueAnyway)
|
|
2148
|
+
return null;
|
|
2149
|
+
}
|
|
2150
|
+
const model = await p.select({
|
|
2151
|
+
message: "Select default Groq model:",
|
|
2152
|
+
options: GROQ_MODELS.map((m) => ({
|
|
2153
|
+
value: m.value,
|
|
2154
|
+
label: m.label,
|
|
2155
|
+
...(m.hint ? { hint: m.hint } : {}),
|
|
2156
|
+
})),
|
|
2157
|
+
});
|
|
2158
|
+
if (p.isCancel(model))
|
|
2159
|
+
return null;
|
|
2160
|
+
// Store API key for later shell profile export
|
|
2161
|
+
configuredEnvVars.set("GROQ_API_KEY", apiKey);
|
|
2162
|
+
return {
|
|
2163
|
+
access_mode: "api",
|
|
2164
|
+
api_key: `\${GROQ_API_KEY}`,
|
|
2165
|
+
base_url: "https://api.groq.com/openai/v1",
|
|
2166
|
+
default_model: model,
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
/**
|
|
2170
|
+
* Configure Together AI with API key (no subscription option)
|
|
2171
|
+
*/
|
|
2172
|
+
async function configureTogetherAPI() {
|
|
2173
|
+
p.note("Together AI provides 200+ open models with sub-100ms latency.\n" +
|
|
2174
|
+
color.bold("$1 free credit to start!") + "\n" +
|
|
2175
|
+
"Get your API key from:\n" +
|
|
2176
|
+
color.cyan("https://api.together.xyz/settings/api-keys"), "Together AI API Setup");
|
|
2177
|
+
const apiKey = await p.password({
|
|
2178
|
+
message: "Enter your Together AI API key:",
|
|
2179
|
+
validate: (value) => validateApiKey(value, "together"),
|
|
2180
|
+
});
|
|
2181
|
+
if (p.isCancel(apiKey))
|
|
2182
|
+
return null;
|
|
2183
|
+
const result = await testConnectionWithSpinner("Together AI", () => testTogetherConnection(apiKey));
|
|
2184
|
+
if (!result.success) {
|
|
2185
|
+
const continueAnyway = await p.confirm({
|
|
2186
|
+
message: `Connection failed: ${result.error}. Continue anyway?`,
|
|
2187
|
+
initialValue: false,
|
|
2188
|
+
});
|
|
2189
|
+
if (p.isCancel(continueAnyway) || !continueAnyway)
|
|
2190
|
+
return null;
|
|
2191
|
+
}
|
|
2192
|
+
const model = await p.select({
|
|
2193
|
+
message: "Select default Together AI model:",
|
|
2194
|
+
options: TOGETHER_MODELS.map((m) => ({
|
|
2195
|
+
value: m.value,
|
|
2196
|
+
label: m.label,
|
|
2197
|
+
...(m.hint ? { hint: m.hint } : {}),
|
|
2198
|
+
})),
|
|
2199
|
+
});
|
|
2200
|
+
if (p.isCancel(model))
|
|
2201
|
+
return null;
|
|
2202
|
+
// Store API key for later shell profile export
|
|
2203
|
+
configuredEnvVars.set("TOGETHER_API_KEY", apiKey);
|
|
2204
|
+
return {
|
|
2205
|
+
access_mode: "api",
|
|
2206
|
+
api_key: `\${TOGETHER_API_KEY}`,
|
|
2207
|
+
base_url: "https://api.together.xyz/v1",
|
|
2208
|
+
default_model: model,
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
/**
|
|
2212
|
+
* Configure Fireworks AI with API key (no subscription option)
|
|
2213
|
+
*/
|
|
2214
|
+
async function configureFireworksAPI() {
|
|
2215
|
+
p.note("Fireworks AI provides fast inference for DeepSeek, Llama, and other models.\n" +
|
|
2216
|
+
color.bold("$1 free credit to start!") + "\n" +
|
|
2217
|
+
"Get your API key from:\n" +
|
|
2218
|
+
color.cyan("https://fireworks.ai/account/api-keys"), "Fireworks AI API Setup");
|
|
2219
|
+
const apiKey = await p.password({
|
|
2220
|
+
message: "Enter your Fireworks AI API key:",
|
|
2221
|
+
validate: (value) => validateApiKey(value, "fireworks"),
|
|
2222
|
+
});
|
|
2223
|
+
if (p.isCancel(apiKey))
|
|
2224
|
+
return null;
|
|
2225
|
+
const result = await testConnectionWithSpinner("Fireworks AI", () => testFireworksConnection(apiKey));
|
|
2226
|
+
if (!result.success) {
|
|
2227
|
+
const continueAnyway = await p.confirm({
|
|
2228
|
+
message: `Connection failed: ${result.error}. Continue anyway?`,
|
|
2229
|
+
initialValue: false,
|
|
2230
|
+
});
|
|
2231
|
+
if (p.isCancel(continueAnyway) || !continueAnyway)
|
|
2232
|
+
return null;
|
|
2233
|
+
}
|
|
2234
|
+
const model = await p.select({
|
|
2235
|
+
message: "Select default Fireworks AI model:",
|
|
2236
|
+
options: FIREWORKS_MODELS.map((m) => ({
|
|
2237
|
+
value: m.value,
|
|
2238
|
+
label: m.label,
|
|
2239
|
+
...(m.hint ? { hint: m.hint } : {}),
|
|
2240
|
+
})),
|
|
2241
|
+
});
|
|
2242
|
+
if (p.isCancel(model))
|
|
2243
|
+
return null;
|
|
2244
|
+
// Store API key for later shell profile export
|
|
2245
|
+
configuredEnvVars.set("FIREWORKS_API_KEY", apiKey);
|
|
2246
|
+
return {
|
|
2247
|
+
access_mode: "api",
|
|
2248
|
+
api_key: `\${FIREWORKS_API_KEY}`,
|
|
2249
|
+
base_url: "https://api.fireworks.ai/inference/v1",
|
|
1403
2250
|
default_model: model,
|
|
1404
2251
|
};
|
|
1405
2252
|
}
|
|
@@ -1459,6 +2306,24 @@ export async function addProvider(providerName) {
|
|
|
1459
2306
|
case "kimi":
|
|
1460
2307
|
config = await configureKimi();
|
|
1461
2308
|
break;
|
|
2309
|
+
case "kimi-code":
|
|
2310
|
+
config = await configureKimiCode();
|
|
2311
|
+
break;
|
|
2312
|
+
case "perplexity":
|
|
2313
|
+
config = await configurePerplexity();
|
|
2314
|
+
break;
|
|
2315
|
+
case "openrouter":
|
|
2316
|
+
config = await configureOpenRouter();
|
|
2317
|
+
break;
|
|
2318
|
+
case "groq":
|
|
2319
|
+
config = await configureGroq();
|
|
2320
|
+
break;
|
|
2321
|
+
case "together":
|
|
2322
|
+
config = await configureTogether();
|
|
2323
|
+
break;
|
|
2324
|
+
case "fireworks":
|
|
2325
|
+
config = await configureFireworks();
|
|
2326
|
+
break;
|
|
1462
2327
|
case "ollama":
|
|
1463
2328
|
config = await configureOllama();
|
|
1464
2329
|
break;
|