mcp-agent-foundry 1.0.1 → 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 +4 -2
- package/dist/cli/setup-wizard.d.ts.map +1 -1
- package/dist/cli/setup-wizard.js +1024 -37
- package/dist/cli/setup-wizard.js.map +1 -1
- package/dist/cli/test-connection.d.ts +34 -2
- package/dist/cli/test-connection.d.ts.map +1 -1
- package/dist/cli/test-connection.js +384 -2
- package/dist/cli/test-connection.js.map +1 -1
- package/dist/cli.d.ts +15 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +198 -30
- package/dist/cli.js.map +1 -1
- package/dist/config/defaults.d.ts +2 -2
- package/dist/config/defaults.js +16 -16
- package/dist/config/defaults.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
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Interactive setup wizard for
|
|
2
|
+
* Interactive setup wizard for Agent Foundry
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Agent Foundry is an MCP server for multi-agent orchestration with
|
|
5
|
+
* Git worktree isolation for parallel coding agents.
|
|
5
6
|
* This version supports:
|
|
6
7
|
* - Multiple access modes: API keys OR subscription/CLI passthrough
|
|
7
8
|
* - Configurable orchestrator (any provider can orchestrate)
|
|
8
9
|
* - Full flexibility in role assignment
|
|
10
|
+
* - Git worktree isolation for coding agents
|
|
9
11
|
*
|
|
10
12
|
* Updated January 2026 with latest models from all providers.
|
|
11
13
|
*/
|
|
@@ -16,7 +18,7 @@ import * as path from "node:path";
|
|
|
16
18
|
import * as os from "node:os";
|
|
17
19
|
import { stringify as yamlStringify } from "yaml";
|
|
18
20
|
import { getUserConfigPath, getUserConfigDir } from "../config/defaults.js";
|
|
19
|
-
import { testOpenAIConnection, testGeminiConnection, testDeepSeekConnection, testZaiConnection, 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";
|
|
20
22
|
// ============================================================================
|
|
21
23
|
// Constants - Updated January 2026
|
|
22
24
|
// ============================================================================
|
|
@@ -29,7 +31,14 @@ const PROVIDER_ENV_VARS = {
|
|
|
29
31
|
google: "GEMINI_API_KEY",
|
|
30
32
|
deepseek: "DEEPSEEK_API_KEY",
|
|
31
33
|
zai: "ZAI_API_KEY",
|
|
34
|
+
kimi: "KIMI_API_KEY",
|
|
35
|
+
"kimi-code": "KIMI_API_KEY", // Uses same API key as regular Kimi
|
|
32
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",
|
|
33
42
|
};
|
|
34
43
|
/**
|
|
35
44
|
* Track which environment variables were configured during setup
|
|
@@ -40,7 +49,7 @@ const configuredEnvVars = new Map();
|
|
|
40
49
|
* Updated January 2026
|
|
41
50
|
*
|
|
42
51
|
* NOTE: "subscription" mode (CLI passthrough) only works for the interface
|
|
43
|
-
* you're currently running FROM. Since
|
|
52
|
+
* you're currently running FROM. Since Agent Foundry typically runs from
|
|
44
53
|
* Claude Code, only Anthropic can use subscription mode for the orchestrator.
|
|
45
54
|
* All other providers need API keys for agent roles.
|
|
46
55
|
*/
|
|
@@ -76,6 +85,48 @@ const AVAILABLE_PROVIDERS = [
|
|
|
76
85
|
hint: "GLM-4.7 - Strong agentic coding, very affordable",
|
|
77
86
|
supportsSubscription: false, // Requires API key when called from Claude Code
|
|
78
87
|
},
|
|
88
|
+
{
|
|
89
|
+
value: "kimi",
|
|
90
|
+
label: "Moonshot (Kimi)",
|
|
91
|
+
hint: "Kimi K2.5 - Excellent coding, 1M context window",
|
|
92
|
+
supportsSubscription: false,
|
|
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
|
+
},
|
|
79
130
|
{
|
|
80
131
|
value: "ollama",
|
|
81
132
|
label: "Ollama",
|
|
@@ -138,6 +189,82 @@ const ZAI_MODELS = [
|
|
|
138
189
|
{ value: "glm-4.7-flashx", label: "GLM-4.7 FlashX", hint: "Fast and affordable" },
|
|
139
190
|
{ value: "glm-4.7-flash", label: "GLM-4.7 Flash", hint: "Free tier" },
|
|
140
191
|
];
|
|
192
|
+
/**
|
|
193
|
+
* Moonshot Kimi models (January 2026)
|
|
194
|
+
* https://platform.moonshot.cn/docs/api/model-list
|
|
195
|
+
* K2.5 series with 1M context window
|
|
196
|
+
*/
|
|
197
|
+
const KIMI_MODELS = [
|
|
198
|
+
{ value: "kimi-k2-5-preview", label: "Kimi K2.5 (Preview)", hint: "Latest flagship - 1M context" },
|
|
199
|
+
{ value: "moonshot-v1-128k", label: "Moonshot V1 128K", hint: "Stable, 128K context" },
|
|
200
|
+
{ value: "moonshot-v1-32k", label: "Moonshot V1 32K", hint: "Fast, 32K context" },
|
|
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
|
+
];
|
|
141
268
|
/**
|
|
142
269
|
* Ollama local models
|
|
143
270
|
*/
|
|
@@ -264,18 +391,18 @@ If you're uncertain, say so. Prefer accuracy over completeness.`,
|
|
|
264
391
|
// ============================================================================
|
|
265
392
|
function displayBanner() {
|
|
266
393
|
console.log();
|
|
267
|
-
console.log(color.cyan("
|
|
268
|
-
console.log(color.cyan("│
|
|
394
|
+
console.log(color.cyan("╭───────────────────────────────────────────────────────╮"));
|
|
395
|
+
console.log(color.cyan("│ │"));
|
|
269
396
|
console.log(color.cyan("│ ") +
|
|
270
|
-
color.bold(color.yellow("
|
|
271
|
-
color.bold("
|
|
272
|
-
color.cyan("
|
|
273
|
-
console.log(color.cyan("│
|
|
397
|
+
color.bold(color.yellow("🏗️")) +
|
|
398
|
+
color.bold(" Agent Foundry Setup") +
|
|
399
|
+
color.cyan(" │"));
|
|
400
|
+
console.log(color.cyan("│ │"));
|
|
274
401
|
console.log(color.cyan("│ ") +
|
|
275
|
-
color.dim("Multi-agent orchestration
|
|
402
|
+
color.dim("Multi-agent orchestration with worktree isolation") +
|
|
276
403
|
color.cyan(" │"));
|
|
277
|
-
console.log(color.cyan("│
|
|
278
|
-
console.log(color.cyan("
|
|
404
|
+
console.log(color.cyan("│ │"));
|
|
405
|
+
console.log(color.cyan("╰───────────────────────────────────────────────────────╯"));
|
|
279
406
|
console.log();
|
|
280
407
|
}
|
|
281
408
|
// ============================================================================
|
|
@@ -305,6 +432,20 @@ function getProviderModels(providerName) {
|
|
|
305
432
|
return DEEPSEEK_MODELS;
|
|
306
433
|
case "zai":
|
|
307
434
|
return ZAI_MODELS;
|
|
435
|
+
case "kimi":
|
|
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;
|
|
308
449
|
case "ollama":
|
|
309
450
|
return OLLAMA_MODELS;
|
|
310
451
|
default:
|
|
@@ -314,6 +455,34 @@ function getProviderModels(providerName) {
|
|
|
314
455
|
function getProviderInfo(providerName) {
|
|
315
456
|
return AVAILABLE_PROVIDERS.find((p) => p.value === providerName);
|
|
316
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
|
+
}
|
|
317
486
|
// ============================================================================
|
|
318
487
|
// Provider Configuration Functions
|
|
319
488
|
// ============================================================================
|
|
@@ -640,28 +809,418 @@ async function configureZai() {
|
|
|
640
809
|
// API mode
|
|
641
810
|
p.note("GLM-4.7 is excellent for agentic coding tasks.\n" +
|
|
642
811
|
"Get your API key from:\n" +
|
|
643
|
-
color.cyan("https://z.ai/manage-apikey/apikey-list"), "API Key Setup");
|
|
812
|
+
color.cyan("https://z.ai/manage-apikey/apikey-list"), "API Key Setup");
|
|
813
|
+
while (true) {
|
|
814
|
+
const apiKey = await p.password({
|
|
815
|
+
message: "Enter your Z.AI API key:",
|
|
816
|
+
validate: (v) => validateApiKey(v, "zai"),
|
|
817
|
+
});
|
|
818
|
+
if (p.isCancel(apiKey))
|
|
819
|
+
return null;
|
|
820
|
+
const result = await testConnectionWithSpinner("Z.AI", () => testZaiConnection(apiKey));
|
|
821
|
+
if (result.success) {
|
|
822
|
+
const defaultModel = await p.select({
|
|
823
|
+
message: "Select default GLM model:",
|
|
824
|
+
options: ZAI_MODELS,
|
|
825
|
+
});
|
|
826
|
+
if (p.isCancel(defaultModel))
|
|
827
|
+
return null;
|
|
828
|
+
const envVarName = PROVIDER_ENV_VARS["zai"];
|
|
829
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
830
|
+
return {
|
|
831
|
+
access_mode: "api",
|
|
832
|
+
api_key: "${" + envVarName + "}",
|
|
833
|
+
base_url: "https://api.z.ai/api/paas/v4",
|
|
834
|
+
default_model: defaultModel,
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
const action = await p.select({
|
|
838
|
+
message: "Connection test failed. What would you like to do?",
|
|
839
|
+
options: [
|
|
840
|
+
{ value: "retry", label: "Re-enter API key" },
|
|
841
|
+
{ value: "skip", label: "Skip this provider" },
|
|
842
|
+
{ value: "add", label: "Add anyway" },
|
|
843
|
+
],
|
|
844
|
+
});
|
|
845
|
+
if (p.isCancel(action) || action === "skip")
|
|
846
|
+
return null;
|
|
847
|
+
if (action === "add") {
|
|
848
|
+
const envVarName = PROVIDER_ENV_VARS["zai"];
|
|
849
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
850
|
+
return {
|
|
851
|
+
access_mode: "api",
|
|
852
|
+
api_key: "${" + envVarName + "}",
|
|
853
|
+
base_url: "https://api.z.ai/api/paas/v4",
|
|
854
|
+
default_model: "glm-4.7-flash",
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Configure Moonshot (Kimi) provider
|
|
861
|
+
*/
|
|
862
|
+
async function configureKimi() {
|
|
863
|
+
p.log.step(color.bold("Moonshot (Kimi) Configuration"));
|
|
864
|
+
p.note("Kimi K2.5 excels at coding with a 1M context window.\n" +
|
|
865
|
+
color.bold("Pricing: Very affordable") + "\n\n" +
|
|
866
|
+
"Get your API key from:\n" +
|
|
867
|
+
color.cyan("https://platform.moonshot.cn/console/api-keys"), "Moonshot Setup");
|
|
868
|
+
while (true) {
|
|
869
|
+
const apiKey = await p.password({
|
|
870
|
+
message: "Enter your Moonshot API key:",
|
|
871
|
+
validate: (v) => validateApiKey(v, "kimi"),
|
|
872
|
+
});
|
|
873
|
+
if (p.isCancel(apiKey))
|
|
874
|
+
return null;
|
|
875
|
+
const result = await testConnectionWithSpinner("Moonshot", () => testKimiConnection(apiKey));
|
|
876
|
+
if (result.success) {
|
|
877
|
+
const defaultModel = await p.select({
|
|
878
|
+
message: "Select default Kimi model:",
|
|
879
|
+
options: KIMI_MODELS,
|
|
880
|
+
});
|
|
881
|
+
if (p.isCancel(defaultModel))
|
|
882
|
+
return null;
|
|
883
|
+
const envVarName = PROVIDER_ENV_VARS["kimi"];
|
|
884
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
885
|
+
return {
|
|
886
|
+
access_mode: "api",
|
|
887
|
+
api_key: "${" + envVarName + "}",
|
|
888
|
+
base_url: "https://api.moonshot.ai/v1",
|
|
889
|
+
default_model: defaultModel,
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
const action = await p.select({
|
|
893
|
+
message: "Connection test failed. What would you like to do?",
|
|
894
|
+
options: [
|
|
895
|
+
{ value: "retry", label: "Re-enter API key" },
|
|
896
|
+
{ value: "skip", label: "Skip this provider" },
|
|
897
|
+
{ value: "add", label: "Add anyway" },
|
|
898
|
+
],
|
|
899
|
+
});
|
|
900
|
+
if (p.isCancel(action) || action === "skip")
|
|
901
|
+
return null;
|
|
902
|
+
if (action === "add") {
|
|
903
|
+
const envVarName = PROVIDER_ENV_VARS["kimi"];
|
|
904
|
+
configuredEnvVars.set(envVarName, apiKey);
|
|
905
|
+
return {
|
|
906
|
+
access_mode: "api",
|
|
907
|
+
api_key: "${" + envVarName + "}",
|
|
908
|
+
base_url: "https://api.moonshot.ai/v1",
|
|
909
|
+
default_model: "kimi-k2-5-preview",
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
}
|
|
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");
|
|
644
1203
|
while (true) {
|
|
645
1204
|
const apiKey = await p.password({
|
|
646
|
-
message: "Enter your
|
|
647
|
-
validate: (v) => validateApiKey(v, "
|
|
1205
|
+
message: "Enter your Fireworks AI API key:",
|
|
1206
|
+
validate: (v) => validateApiKey(v, "fireworks"),
|
|
648
1207
|
});
|
|
649
1208
|
if (p.isCancel(apiKey))
|
|
650
1209
|
return null;
|
|
651
|
-
const result = await testConnectionWithSpinner("
|
|
1210
|
+
const result = await testConnectionWithSpinner("Fireworks AI", () => testFireworksConnection(apiKey));
|
|
652
1211
|
if (result.success) {
|
|
653
1212
|
const defaultModel = await p.select({
|
|
654
|
-
message: "Select default
|
|
655
|
-
options:
|
|
1213
|
+
message: "Select default Fireworks AI model:",
|
|
1214
|
+
options: FIREWORKS_MODELS,
|
|
656
1215
|
});
|
|
657
1216
|
if (p.isCancel(defaultModel))
|
|
658
1217
|
return null;
|
|
659
|
-
const envVarName = PROVIDER_ENV_VARS["
|
|
1218
|
+
const envVarName = PROVIDER_ENV_VARS["fireworks"];
|
|
660
1219
|
configuredEnvVars.set(envVarName, apiKey);
|
|
661
1220
|
return {
|
|
662
1221
|
access_mode: "api",
|
|
663
1222
|
api_key: "${" + envVarName + "}",
|
|
664
|
-
base_url: "https://api.
|
|
1223
|
+
base_url: "https://api.fireworks.ai/inference/v1",
|
|
665
1224
|
default_model: defaultModel,
|
|
666
1225
|
};
|
|
667
1226
|
}
|
|
@@ -676,13 +1235,13 @@ async function configureZai() {
|
|
|
676
1235
|
if (p.isCancel(action) || action === "skip")
|
|
677
1236
|
return null;
|
|
678
1237
|
if (action === "add") {
|
|
679
|
-
const envVarName = PROVIDER_ENV_VARS["
|
|
1238
|
+
const envVarName = PROVIDER_ENV_VARS["fireworks"];
|
|
680
1239
|
configuredEnvVars.set(envVarName, apiKey);
|
|
681
1240
|
return {
|
|
682
1241
|
access_mode: "api",
|
|
683
1242
|
api_key: "${" + envVarName + "}",
|
|
684
|
-
base_url: "https://api.
|
|
685
|
-
default_model: "
|
|
1243
|
+
base_url: "https://api.fireworks.ai/inference/v1",
|
|
1244
|
+
default_model: "accounts/fireworks/models/llama-v3p3-70b-instruct",
|
|
686
1245
|
};
|
|
687
1246
|
}
|
|
688
1247
|
}
|
|
@@ -826,6 +1385,75 @@ async function configureRoles(configuredProviders, orchestratorProvider) {
|
|
|
826
1385
|
return roles;
|
|
827
1386
|
}
|
|
828
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
|
+
// ============================================================================
|
|
829
1457
|
// Shell Profile & Config Saving
|
|
830
1458
|
// ============================================================================
|
|
831
1459
|
function getShellProfilePath() {
|
|
@@ -867,7 +1495,7 @@ async function addEnvVarsToShellProfile() {
|
|
|
867
1495
|
p.log.info(`Environment variables already exist in ~/${profileName}`);
|
|
868
1496
|
return;
|
|
869
1497
|
}
|
|
870
|
-
const exportLines = ["", "#
|
|
1498
|
+
const exportLines = ["", "# Agent Foundry"];
|
|
871
1499
|
for (const { name, value } of envVarsToAdd) {
|
|
872
1500
|
exportLines.push(`export ${name}="${value}"`);
|
|
873
1501
|
}
|
|
@@ -883,8 +1511,8 @@ async function addEnvVarsToShellProfile() {
|
|
|
883
1511
|
p.log.error(`Failed to update ${profilePath}`);
|
|
884
1512
|
}
|
|
885
1513
|
}
|
|
886
|
-
function generateConfig(providers, roles) {
|
|
887
|
-
|
|
1514
|
+
function generateConfig(providers, roles, failoverConfig) {
|
|
1515
|
+
const config = {
|
|
888
1516
|
version: "2.0",
|
|
889
1517
|
defaults: {
|
|
890
1518
|
temperature: 0.7,
|
|
@@ -894,6 +1522,31 @@ function generateConfig(providers, roles) {
|
|
|
894
1522
|
roles,
|
|
895
1523
|
providers,
|
|
896
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;
|
|
897
1550
|
}
|
|
898
1551
|
async function saveConfig(config) {
|
|
899
1552
|
const configPath = getUserConfigPath();
|
|
@@ -907,11 +1560,11 @@ async function saveConfig(config) {
|
|
|
907
1560
|
fs.copyFileSync(configPath, backupPath);
|
|
908
1561
|
p.log.info(`Backed up existing config to: ${color.dim(backupPath)}`);
|
|
909
1562
|
}
|
|
910
|
-
const header = `#
|
|
1563
|
+
const header = `# Agent Foundry Configuration
|
|
911
1564
|
# Generated: ${new Date().toISOString()}
|
|
912
|
-
# Documentation: https://github.com/
|
|
1565
|
+
# Documentation: https://github.com/sashabogi/agent-foundry
|
|
913
1566
|
#
|
|
914
|
-
# This configures multi-agent orchestration
|
|
1567
|
+
# This configures multi-agent orchestration with Git worktree isolation.
|
|
915
1568
|
# Supports both API keys and subscription/CLI access modes.
|
|
916
1569
|
#
|
|
917
1570
|
# Environment variables: \${VAR_NAME} syntax
|
|
@@ -952,7 +1605,7 @@ function detectCurrentCLI() {
|
|
|
952
1605
|
}
|
|
953
1606
|
export async function runSetupWizard() {
|
|
954
1607
|
displayBanner();
|
|
955
|
-
p.intro(color.bgCyan(color.black("
|
|
1608
|
+
p.intro(color.bgCyan(color.black(" Agent Foundry Setup ")));
|
|
956
1609
|
const currentCLI = detectCurrentCLI();
|
|
957
1610
|
// ============================================================================
|
|
958
1611
|
// STEP 1: Choose Orchestrator
|
|
@@ -1080,19 +1733,26 @@ export async function runSetupWizard() {
|
|
|
1080
1733
|
"Each role can use any configured provider.", "Step 4: Assign Agent Roles");
|
|
1081
1734
|
const roles = await configureRoles(configuredProviderNames, orchestratorProvider);
|
|
1082
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
|
+
// ============================================================================
|
|
1083
1742
|
// Save Configuration
|
|
1084
1743
|
// ============================================================================
|
|
1085
|
-
const config = generateConfig(providers, roles);
|
|
1744
|
+
const config = generateConfig(providers, roles, failoverConfig);
|
|
1086
1745
|
const saved = await saveConfig(config);
|
|
1087
1746
|
if (saved) {
|
|
1088
1747
|
await addEnvVarsToShellProfile();
|
|
1089
|
-
p.outro(color.green("✓
|
|
1748
|
+
p.outro(color.green("✓ Agent Foundry configured successfully!"));
|
|
1090
1749
|
console.log();
|
|
1091
1750
|
console.log(color.bold(" Summary"));
|
|
1092
1751
|
console.log();
|
|
1093
1752
|
console.log(` Orchestrator: ${color.cyan(orchestratorProvider)} (${orchestratorAccessMode})`);
|
|
1094
|
-
console.log(` Agent Providers: ${configuredProviderNames.filter(
|
|
1753
|
+
console.log(` Agent Providers: ${configuredProviderNames.filter(prov => prov !== orchestratorProvider).join(", ") || "none"}`);
|
|
1095
1754
|
console.log(` Roles: ${Object.keys(roles).join(", ")}`);
|
|
1755
|
+
console.log(` Failover: ${failoverConfig.enabled ? color.green("enabled") : color.dim("disabled")}`);
|
|
1096
1756
|
console.log();
|
|
1097
1757
|
console.log(` Config: ${color.dim(getUserConfigPath())}`);
|
|
1098
1758
|
console.log();
|
|
@@ -1117,6 +1777,20 @@ async function configureProviderWithAPIKey(providerType) {
|
|
|
1117
1777
|
return configureDeepSeek();
|
|
1118
1778
|
case "zai":
|
|
1119
1779
|
return configureZaiAPI();
|
|
1780
|
+
case "kimi":
|
|
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();
|
|
1120
1794
|
case "ollama":
|
|
1121
1795
|
return configureOllama();
|
|
1122
1796
|
default:
|
|
@@ -1284,6 +1958,298 @@ async function configureZaiAPI() {
|
|
|
1284
1958
|
default_model: model,
|
|
1285
1959
|
};
|
|
1286
1960
|
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Configure Moonshot (Kimi) with API key (no subscription option)
|
|
1963
|
+
*/
|
|
1964
|
+
async function configureKimiAPI() {
|
|
1965
|
+
p.note("Kimi K2.5 excels at coding with 1M context window.\n" +
|
|
1966
|
+
"Get your API key from:\n" +
|
|
1967
|
+
color.cyan("https://platform.moonshot.cn/console/api-keys"), "Moonshot Kimi API Setup");
|
|
1968
|
+
const apiKey = await p.password({
|
|
1969
|
+
message: "Enter your Moonshot API key:",
|
|
1970
|
+
validate: (value) => validateApiKey(value, "kimi"),
|
|
1971
|
+
});
|
|
1972
|
+
if (p.isCancel(apiKey))
|
|
1973
|
+
return null;
|
|
1974
|
+
const result = await testConnectionWithSpinner("Moonshot", () => testKimiConnection(apiKey));
|
|
1975
|
+
if (!result.success) {
|
|
1976
|
+
const continueAnyway = await p.confirm({
|
|
1977
|
+
message: `Connection failed: ${result.error}. Continue anyway?`,
|
|
1978
|
+
initialValue: false,
|
|
1979
|
+
});
|
|
1980
|
+
if (p.isCancel(continueAnyway) || !continueAnyway)
|
|
1981
|
+
return null;
|
|
1982
|
+
}
|
|
1983
|
+
const model = await p.select({
|
|
1984
|
+
message: "Select default Kimi model:",
|
|
1985
|
+
options: KIMI_MODELS.map((m) => ({
|
|
1986
|
+
value: m.value,
|
|
1987
|
+
label: m.label,
|
|
1988
|
+
...(m.hint ? { hint: m.hint } : {}),
|
|
1989
|
+
})),
|
|
1990
|
+
});
|
|
1991
|
+
if (p.isCancel(model))
|
|
1992
|
+
return null;
|
|
1993
|
+
// Store API key for later shell profile export
|
|
1994
|
+
configuredEnvVars.set("KIMI_API_KEY", apiKey);
|
|
1995
|
+
return {
|
|
1996
|
+
access_mode: "api",
|
|
1997
|
+
api_key: `\${KIMI_API_KEY}`,
|
|
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",
|
|
2250
|
+
default_model: model,
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
1287
2253
|
// ============================================================================
|
|
1288
2254
|
// Provider Management Functions (for CLI subcommands)
|
|
1289
2255
|
// ============================================================================
|
|
@@ -1337,6 +2303,27 @@ export async function addProvider(providerName) {
|
|
|
1337
2303
|
case "zai":
|
|
1338
2304
|
config = await configureZai();
|
|
1339
2305
|
break;
|
|
2306
|
+
case "kimi":
|
|
2307
|
+
config = await configureKimi();
|
|
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;
|
|
1340
2327
|
case "ollama":
|
|
1341
2328
|
config = await configureOllama();
|
|
1342
2329
|
break;
|
|
@@ -1356,7 +2343,7 @@ export async function testProvider(providerName) {
|
|
|
1356
2343
|
const configPath = getUserConfigPath();
|
|
1357
2344
|
if (!fs.existsSync(configPath)) {
|
|
1358
2345
|
p.log.error("No configuration file found.");
|
|
1359
|
-
p.log.info("Run 'agent-
|
|
2346
|
+
p.log.info("Run 'agent-foundry setup' first to configure providers.");
|
|
1360
2347
|
process.exit(1);
|
|
1361
2348
|
}
|
|
1362
2349
|
// For now, just test based on provider name
|
|
@@ -1383,7 +2370,7 @@ export async function listProviders() {
|
|
|
1383
2370
|
const configPath = getUserConfigPath();
|
|
1384
2371
|
if (!fs.existsSync(configPath)) {
|
|
1385
2372
|
console.log("No configuration file found.");
|
|
1386
|
-
console.log("Run 'agent-
|
|
2373
|
+
console.log("Run 'agent-foundry setup' to configure providers.");
|
|
1387
2374
|
return;
|
|
1388
2375
|
}
|
|
1389
2376
|
try {
|
|
@@ -1392,7 +2379,7 @@ export async function listProviders() {
|
|
|
1392
2379
|
const config = parse(configContent);
|
|
1393
2380
|
if (!config.providers || Object.keys(config.providers).length === 0) {
|
|
1394
2381
|
console.log("No providers configured.");
|
|
1395
|
-
console.log("Run 'agent-
|
|
2382
|
+
console.log("Run 'agent-foundry setup' to add providers.");
|
|
1396
2383
|
return;
|
|
1397
2384
|
}
|
|
1398
2385
|
console.log();
|