mcp-agent-foundry 1.1.0 → 1.3.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.
Files changed (76) hide show
  1. package/README.md +22 -2
  2. package/dist/cli/setup-wizard.d.ts.map +1 -1
  3. package/dist/cli/setup-wizard.js +873 -8
  4. package/dist/cli/setup-wizard.js.map +1 -1
  5. package/dist/cli/test-connection.d.ts +28 -0
  6. package/dist/cli/test-connection.d.ts.map +1 -1
  7. package/dist/cli/test-connection.js +335 -1
  8. package/dist/cli/test-connection.js.map +1 -1
  9. package/dist/cli.d.ts +13 -1
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +169 -1
  12. package/dist/cli.js.map +1 -1
  13. package/dist/config/validator.d.ts +113 -0
  14. package/dist/config/validator.d.ts.map +1 -1
  15. package/dist/config/validator.js +113 -0
  16. package/dist/config/validator.js.map +1 -1
  17. package/dist/failover/health-tracker.d.ts +175 -0
  18. package/dist/failover/health-tracker.d.ts.map +1 -0
  19. package/dist/failover/health-tracker.js +350 -0
  20. package/dist/failover/health-tracker.js.map +1 -0
  21. package/dist/failover/index.d.ts +9 -0
  22. package/dist/failover/index.d.ts.map +1 -0
  23. package/dist/failover/index.js +9 -0
  24. package/dist/failover/index.js.map +1 -0
  25. package/dist/failover/orchestrator.d.ts +189 -0
  26. package/dist/failover/orchestrator.d.ts.map +1 -0
  27. package/dist/failover/orchestrator.js +488 -0
  28. package/dist/failover/orchestrator.js.map +1 -0
  29. package/dist/failover/pricing.d.ts +115 -0
  30. package/dist/failover/pricing.d.ts.map +1 -0
  31. package/dist/failover/pricing.js +283 -0
  32. package/dist/failover/pricing.js.map +1 -0
  33. package/dist/persistence/state-schema.d.ts +50 -0
  34. package/dist/persistence/state-schema.d.ts.map +1 -1
  35. package/dist/persistence/state-schema.js +2 -0
  36. package/dist/persistence/state-schema.js.map +1 -1
  37. package/dist/providers/fireworks.d.ts +23 -0
  38. package/dist/providers/fireworks.d.ts.map +1 -0
  39. package/dist/providers/fireworks.js +31 -0
  40. package/dist/providers/fireworks.js.map +1 -0
  41. package/dist/providers/groq.d.ts +23 -0
  42. package/dist/providers/groq.d.ts.map +1 -0
  43. package/dist/providers/groq.js +31 -0
  44. package/dist/providers/groq.js.map +1 -0
  45. package/dist/providers/kimi-code.d.ts +32 -0
  46. package/dist/providers/kimi-code.d.ts.map +1 -0
  47. package/dist/providers/kimi-code.js +46 -0
  48. package/dist/providers/kimi-code.js.map +1 -0
  49. package/dist/providers/kimi.d.ts +1 -1
  50. package/dist/providers/kimi.js +1 -1
  51. package/dist/providers/openrouter.d.ts +23 -0
  52. package/dist/providers/openrouter.d.ts.map +1 -0
  53. package/dist/providers/openrouter.js +31 -0
  54. package/dist/providers/openrouter.js.map +1 -0
  55. package/dist/providers/perplexity.d.ts +29 -0
  56. package/dist/providers/perplexity.d.ts.map +1 -0
  57. package/dist/providers/perplexity.js +51 -0
  58. package/dist/providers/perplexity.js.map +1 -0
  59. package/dist/providers/together.d.ts +23 -0
  60. package/dist/providers/together.d.ts.map +1 -0
  61. package/dist/providers/together.js +31 -0
  62. package/dist/providers/together.js.map +1 -0
  63. package/dist/router/engine.d.ts +21 -0
  64. package/dist/router/engine.d.ts.map +1 -1
  65. package/dist/router/engine.js +81 -21
  66. package/dist/router/engine.js.map +1 -1
  67. package/dist/server.d.ts.map +1 -1
  68. package/dist/server.js +49 -0
  69. package/dist/server.js.map +1 -1
  70. package/dist/types.d.ts +52 -1
  71. package/dist/types.d.ts.map +1 -1
  72. package/dist/types.js +14 -0
  73. package/dist/types.js.map +1 -1
  74. package/package.json +5 -2
  75. package/scripts/postinstall.js +78 -0
  76. package/scripts/preuninstall.js +67 -0
@@ -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.cn/v1",
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.cn/v1",
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
- return {
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(p => p !== orchestratorProvider).join(", ") || "none"}`);
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.cn/v1",
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;