@poolzin/pool-bot 2026.2.5 → 2026.2.7
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/dist/agents/auth-profiles/profiles.js +9 -0
- package/dist/agents/auth-profiles.js +1 -1
- package/dist/agents/huggingface-models.js +166 -0
- package/dist/agents/model-auth.js +5 -0
- package/dist/build-info.json +3 -3
- package/dist/cli/config-cli.js +17 -3
- package/dist/cli/program/register.onboard.js +34 -11
- package/dist/commands/auth-choice-options.js +60 -7
- package/dist/commands/auth-choice.apply.api-providers.js +149 -98
- package/dist/commands/auth-choice.apply.huggingface.js +130 -0
- package/dist/commands/auth-choice.apply.openrouter.js +77 -0
- package/dist/commands/auth-choice.apply.vllm.js +92 -0
- package/dist/commands/auth-choice.preferred-provider.js +9 -0
- package/dist/commands/onboard-auth.config-core.js +207 -8
- package/dist/commands/onboard-auth.credentials.js +35 -5
- package/dist/commands/onboard-auth.js +3 -3
- package/dist/commands/onboard-auth.models.js +45 -0
- package/dist/commands/onboard-custom.js +181 -70
- package/dist/commands/onboard-non-interactive/api-keys.js +10 -1
- package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +14 -7
- package/dist/commands/onboard-non-interactive/local/auth-choice.js +322 -144
- package/dist/commands/zai-endpoint-detect.js +97 -0
- package/dist/config/legacy.migrations.part-3.js +57 -0
- package/dist/daemon/constants.js +7 -3
- package/dist/tui/components/filterable-select-list.js +5 -2
- package/dist/tui/components/searchable-select-list.js +6 -1
- package/dist/tui/tui-command-handlers.js +13 -5
- package/package.json +5 -5
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { ensureAuthProfileStore, resolveAuthProfileOrder } from "../agents/auth-profiles.js";
|
|
2
2
|
import { resolveEnvApiKey } from "../agents/model-auth.js";
|
|
3
3
|
import { formatApiKeyPreview, normalizeApiKeyInput, validateApiKeyInput, } from "./auth-choice.api-key.js";
|
|
4
|
+
import { applyAuthChoiceHuggingface } from "./auth-choice.apply.huggingface.js";
|
|
5
|
+
import { applyAuthChoiceOpenRouter } from "./auth-choice.apply.openrouter.js";
|
|
4
6
|
import { applyDefaultModelChoice } from "./auth-choice.default-model.js";
|
|
5
7
|
import { applyGoogleGeminiModelDefault, GOOGLE_GEMINI_DEFAULT_MODEL, } from "./google-gemini-model-default.js";
|
|
6
|
-
import { applyAuthProfileConfig, applyCloudflareAiGatewayConfig, applyCloudflareAiGatewayProviderConfig, applyQianfanConfig, applyQianfanProviderConfig, applyKimiCodeConfig, applyKimiCodeProviderConfig, applyMoonshotConfig, applyMoonshotConfigCn, applyMoonshotProviderConfig, applyMoonshotProviderConfigCn,
|
|
8
|
+
import { applyAuthProfileConfig, applyCloudflareAiGatewayConfig, applyCloudflareAiGatewayProviderConfig, applyQianfanConfig, applyQianfanProviderConfig, applyKimiCodeConfig, applyKimiCodeProviderConfig, applyLitellmConfig, applyLitellmProviderConfig, applyMoonshotConfig, applyMoonshotConfigCn, applyMoonshotProviderConfig, applyMoonshotProviderConfigCn, applyNvidiaConfig, applyNvidiaProviderConfig, applyOpencodeZenConfig, applyOpencodeZenProviderConfig, applySyntheticConfig, applySyntheticProviderConfig, applyTogetherConfig, applyTogetherProviderConfig, applyVeniceConfig, applyVeniceProviderConfig, applyVercelAiGatewayConfig, applyVercelAiGatewayProviderConfig, applyXiaomiConfig, applyXiaomiProviderConfig, applyZaiConfig, applyZaiProviderConfig, CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF, LITELLM_DEFAULT_MODEL_REF, NVIDIA_DEFAULT_MODEL_REF, QIANFAN_DEFAULT_MODEL_REF, KIMI_CODING_MODEL_REF, MOONSHOT_DEFAULT_MODEL_REF, SYNTHETIC_DEFAULT_MODEL_REF, TOGETHER_DEFAULT_MODEL_REF, VENICE_DEFAULT_MODEL_REF, VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF, XIAOMI_DEFAULT_MODEL_REF, setCloudflareAiGatewayConfig, setQianfanApiKey, setGeminiApiKey, setLitellmApiKey, setKimiCodingApiKey, setMoonshotApiKey, setNvidiaApiKey, setOpencodeZenApiKey, setSyntheticApiKey, setTogetherApiKey, setVeniceApiKey, setVercelAiGatewayApiKey, setXiaomiApiKey, setZaiApiKey, ZAI_DEFAULT_MODEL_REF, } from "./onboard-auth.js";
|
|
7
9
|
import { OPENCODE_ZEN_DEFAULT_MODEL } from "./opencode-zen-model-default.js";
|
|
10
|
+
import { detectZaiEndpoint } from "./zai-endpoint-detect.js";
|
|
8
11
|
export async function applyAuthChoiceApiProviders(params) {
|
|
9
12
|
let nextConfig = params.config;
|
|
10
13
|
let agentModelOverride;
|
|
@@ -22,6 +25,9 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
22
25
|
if (params.opts.tokenProvider === "openrouter") {
|
|
23
26
|
authChoice = "openrouter-api-key";
|
|
24
27
|
}
|
|
28
|
+
else if (params.opts.tokenProvider === "litellm") {
|
|
29
|
+
authChoice = "litellm-api-key";
|
|
30
|
+
}
|
|
25
31
|
else if (params.opts.tokenProvider === "vercel-ai-gateway") {
|
|
26
32
|
authChoice = "ai-gateway-api-key";
|
|
27
33
|
}
|
|
@@ -53,6 +59,9 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
53
59
|
else if (params.opts.tokenProvider === "together") {
|
|
54
60
|
authChoice = "together-api-key";
|
|
55
61
|
}
|
|
62
|
+
else if (params.opts.tokenProvider === "huggingface") {
|
|
63
|
+
authChoice = "huggingface-api-key";
|
|
64
|
+
}
|
|
56
65
|
else if (params.opts.tokenProvider === "opencode") {
|
|
57
66
|
authChoice = "opencode-zen";
|
|
58
67
|
}
|
|
@@ -64,75 +73,64 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
64
73
|
}
|
|
65
74
|
}
|
|
66
75
|
if (authChoice === "openrouter-api-key") {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
store,
|
|
73
|
-
provider: "openrouter",
|
|
74
|
-
});
|
|
76
|
+
return applyAuthChoiceOpenRouter(params);
|
|
77
|
+
}
|
|
78
|
+
if (authChoice === "litellm-api-key") {
|
|
79
|
+
const store = ensureAuthProfileStore(params.agentDir, { allowKeychainPrompt: false });
|
|
80
|
+
const profileOrder = resolveAuthProfileOrder({ cfg: nextConfig, store, provider: "litellm" });
|
|
75
81
|
const existingProfileId = profileOrder.find((profileId) => Boolean(store.profiles[profileId]));
|
|
76
82
|
const existingCred = existingProfileId ? store.profiles[existingProfileId] : undefined;
|
|
77
|
-
let profileId = "
|
|
78
|
-
let mode = "api_key";
|
|
83
|
+
let profileId = "litellm:default";
|
|
79
84
|
let hasCredential = false;
|
|
80
|
-
if (existingProfileId && existingCred?.type) {
|
|
85
|
+
if (existingProfileId && existingCred?.type === "api_key") {
|
|
81
86
|
profileId = existingProfileId;
|
|
82
|
-
mode =
|
|
83
|
-
existingCred.type === "oauth"
|
|
84
|
-
? "oauth"
|
|
85
|
-
: existingCred.type === "token"
|
|
86
|
-
? "token"
|
|
87
|
-
: "api_key";
|
|
88
87
|
hasCredential = true;
|
|
89
88
|
}
|
|
90
|
-
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "
|
|
91
|
-
await
|
|
89
|
+
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "litellm") {
|
|
90
|
+
await setLitellmApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir);
|
|
92
91
|
hasCredential = true;
|
|
93
92
|
}
|
|
94
93
|
if (!hasCredential) {
|
|
95
|
-
|
|
94
|
+
await params.prompter.note("LiteLLM provides a unified API to 100+ LLM providers.\nGet your API key from your LiteLLM proxy or https://litellm.ai\nDefault proxy runs on http://localhost:4000", "LiteLLM");
|
|
95
|
+
const envKey = resolveEnvApiKey("litellm");
|
|
96
96
|
if (envKey) {
|
|
97
97
|
const useExisting = await params.prompter.confirm({
|
|
98
|
-
message: `Use existing
|
|
98
|
+
message: `Use existing LITELLM_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
|
99
99
|
initialValue: true,
|
|
100
100
|
});
|
|
101
101
|
if (useExisting) {
|
|
102
|
-
await
|
|
102
|
+
await setLitellmApiKey(envKey.apiKey, params.agentDir);
|
|
103
103
|
hasCredential = true;
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
if (!hasCredential) {
|
|
107
|
+
const key = await params.prompter.text({
|
|
108
|
+
message: "Enter LiteLLM API key",
|
|
109
|
+
validate: validateApiKeyInput,
|
|
110
|
+
});
|
|
111
|
+
await setLitellmApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
112
|
+
hasCredential = true;
|
|
113
|
+
}
|
|
114
114
|
}
|
|
115
115
|
if (hasCredential) {
|
|
116
116
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
117
117
|
profileId,
|
|
118
|
-
provider: "
|
|
119
|
-
mode,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
{
|
|
123
|
-
const applied = await applyDefaultModelChoice({
|
|
124
|
-
config: nextConfig,
|
|
125
|
-
setDefaultModel: params.setDefaultModel,
|
|
126
|
-
defaultModel: OPENROUTER_DEFAULT_MODEL_REF,
|
|
127
|
-
applyDefaultConfig: applyOpenrouterConfig,
|
|
128
|
-
applyProviderConfig: applyOpenrouterProviderConfig,
|
|
129
|
-
noteDefault: OPENROUTER_DEFAULT_MODEL_REF,
|
|
130
|
-
noteAgentModel,
|
|
131
|
-
prompter: params.prompter,
|
|
118
|
+
provider: "litellm",
|
|
119
|
+
mode: "api_key",
|
|
132
120
|
});
|
|
133
|
-
nextConfig = applied.config;
|
|
134
|
-
agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
|
|
135
121
|
}
|
|
122
|
+
const applied = await applyDefaultModelChoice({
|
|
123
|
+
config: nextConfig,
|
|
124
|
+
setDefaultModel: params.setDefaultModel,
|
|
125
|
+
defaultModel: LITELLM_DEFAULT_MODEL_REF,
|
|
126
|
+
applyDefaultConfig: applyLitellmConfig,
|
|
127
|
+
applyProviderConfig: applyLitellmProviderConfig,
|
|
128
|
+
noteDefault: LITELLM_DEFAULT_MODEL_REF,
|
|
129
|
+
noteAgentModel,
|
|
130
|
+
prompter: params.prompter,
|
|
131
|
+
});
|
|
132
|
+
nextConfig = applied.config;
|
|
133
|
+
agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
|
|
136
134
|
return { config: nextConfig, agentModelOverride };
|
|
137
135
|
}
|
|
138
136
|
if (authChoice === "ai-gateway-api-key") {
|
|
@@ -159,7 +157,7 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
159
157
|
message: "Enter Vercel AI Gateway API key",
|
|
160
158
|
validate: validateApiKeyInput,
|
|
161
159
|
});
|
|
162
|
-
await setVercelAiGatewayApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
|
160
|
+
await setVercelAiGatewayApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
163
161
|
}
|
|
164
162
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
165
163
|
profileId: "vercel-ai-gateway:default",
|
|
@@ -190,16 +188,16 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
190
188
|
if (!accountId) {
|
|
191
189
|
const value = await params.prompter.text({
|
|
192
190
|
message: "Enter Cloudflare Account ID",
|
|
193
|
-
validate: (val) => (String(val).trim() ? undefined : "Account ID is required"),
|
|
191
|
+
validate: (val) => (String(val ?? "").trim() ? undefined : "Account ID is required"),
|
|
194
192
|
});
|
|
195
|
-
accountId = String(value).trim();
|
|
193
|
+
accountId = String(value ?? "").trim();
|
|
196
194
|
}
|
|
197
195
|
if (!gatewayId) {
|
|
198
196
|
const value = await params.prompter.text({
|
|
199
197
|
message: "Enter Cloudflare AI Gateway ID",
|
|
200
|
-
validate: (val) => (String(val).trim() ? undefined : "Gateway ID is required"),
|
|
198
|
+
validate: (val) => (String(val ?? "").trim() ? undefined : "Gateway ID is required"),
|
|
201
199
|
});
|
|
202
|
-
gatewayId = String(value).trim();
|
|
200
|
+
gatewayId = String(value ?? "").trim();
|
|
203
201
|
}
|
|
204
202
|
};
|
|
205
203
|
const optsApiKey = normalizeApiKeyInput(params.opts?.cloudflareAiGatewayApiKey ?? "");
|
|
@@ -230,7 +228,7 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
230
228
|
message: "Enter Cloudflare AI Gateway API key",
|
|
231
229
|
validate: validateApiKeyInput,
|
|
232
230
|
});
|
|
233
|
-
await setCloudflareAiGatewayConfig(accountId, gatewayId, normalizeApiKeyInput(String(key)), params.agentDir);
|
|
231
|
+
await setCloudflareAiGatewayConfig(accountId, gatewayId, normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
234
232
|
hasCredential = true;
|
|
235
233
|
}
|
|
236
234
|
if (hasCredential) {
|
|
@@ -284,7 +282,7 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
284
282
|
message: "Enter Moonshot API key",
|
|
285
283
|
validate: validateApiKeyInput,
|
|
286
284
|
});
|
|
287
|
-
await setMoonshotApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
|
285
|
+
await setMoonshotApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
288
286
|
}
|
|
289
287
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
290
288
|
profileId: "moonshot:default",
|
|
@@ -328,7 +326,7 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
328
326
|
message: "Enter Moonshot API key (.cn)",
|
|
329
327
|
validate: validateApiKeyInput,
|
|
330
328
|
});
|
|
331
|
-
await setMoonshotApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
|
329
|
+
await setMoonshotApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
332
330
|
}
|
|
333
331
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
334
332
|
profileId: "moonshot:default",
|
|
@@ -381,7 +379,7 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
381
379
|
message: "Enter Kimi Coding API key",
|
|
382
380
|
validate: validateApiKeyInput,
|
|
383
381
|
});
|
|
384
|
-
await setKimiCodingApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
|
382
|
+
await setKimiCodingApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
385
383
|
}
|
|
386
384
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
387
385
|
profileId: "kimi-coding:default",
|
|
@@ -426,7 +424,7 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
426
424
|
message: "Enter Gemini API key",
|
|
427
425
|
validate: validateApiKeyInput,
|
|
428
426
|
});
|
|
429
|
-
await setGeminiApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
|
427
|
+
await setGeminiApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
430
428
|
}
|
|
431
429
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
432
430
|
profileId: "google:default",
|
|
@@ -446,10 +444,30 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
446
444
|
}
|
|
447
445
|
return { config: nextConfig, agentModelOverride };
|
|
448
446
|
}
|
|
449
|
-
if (authChoice === "zai-api-key"
|
|
447
|
+
if (authChoice === "zai-api-key" ||
|
|
448
|
+
authChoice === "zai-coding-global" ||
|
|
449
|
+
authChoice === "zai-coding-cn" ||
|
|
450
|
+
authChoice === "zai-global" ||
|
|
451
|
+
authChoice === "zai-cn") {
|
|
452
|
+
let endpoint;
|
|
453
|
+
if (authChoice === "zai-coding-global") {
|
|
454
|
+
endpoint = "coding-global";
|
|
455
|
+
}
|
|
456
|
+
else if (authChoice === "zai-coding-cn") {
|
|
457
|
+
endpoint = "coding-cn";
|
|
458
|
+
}
|
|
459
|
+
else if (authChoice === "zai-global") {
|
|
460
|
+
endpoint = "global";
|
|
461
|
+
}
|
|
462
|
+
else if (authChoice === "zai-cn") {
|
|
463
|
+
endpoint = "cn";
|
|
464
|
+
}
|
|
465
|
+
// Input API key
|
|
450
466
|
let hasCredential = false;
|
|
467
|
+
let apiKey = "";
|
|
451
468
|
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "zai") {
|
|
452
|
-
|
|
469
|
+
apiKey = normalizeApiKeyInput(params.opts.token);
|
|
470
|
+
await setZaiApiKey(apiKey, params.agentDir);
|
|
453
471
|
hasCredential = true;
|
|
454
472
|
}
|
|
455
473
|
const envKey = resolveEnvApiKey("zai");
|
|
@@ -459,7 +477,8 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
459
477
|
initialValue: true,
|
|
460
478
|
});
|
|
461
479
|
if (useExisting) {
|
|
462
|
-
|
|
480
|
+
apiKey = envKey.apiKey;
|
|
481
|
+
await setZaiApiKey(apiKey, params.agentDir);
|
|
463
482
|
hasCredential = true;
|
|
464
483
|
}
|
|
465
484
|
}
|
|
@@ -468,42 +487,71 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
468
487
|
message: "Enter Z.AI API key",
|
|
469
488
|
validate: validateApiKeyInput,
|
|
470
489
|
});
|
|
471
|
-
|
|
490
|
+
apiKey = normalizeApiKeyInput(String(key ?? ""));
|
|
491
|
+
await setZaiApiKey(apiKey, params.agentDir);
|
|
492
|
+
}
|
|
493
|
+
// zai-api-key: auto-detect endpoint + choose a working default model.
|
|
494
|
+
let modelIdOverride;
|
|
495
|
+
if (!endpoint) {
|
|
496
|
+
const detected = await detectZaiEndpoint({ apiKey });
|
|
497
|
+
if (detected) {
|
|
498
|
+
endpoint = detected.endpoint;
|
|
499
|
+
modelIdOverride = detected.modelId;
|
|
500
|
+
await params.prompter.note(detected.note, "Z.AI endpoint");
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
endpoint = await params.prompter.select({
|
|
504
|
+
message: "Select Z.AI endpoint",
|
|
505
|
+
options: [
|
|
506
|
+
{
|
|
507
|
+
value: "coding-global",
|
|
508
|
+
label: "Coding-Plan-Global",
|
|
509
|
+
hint: "GLM Coding Plan Global (api.z.ai)",
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
value: "coding-cn",
|
|
513
|
+
label: "Coding-Plan-CN",
|
|
514
|
+
hint: "GLM Coding Plan CN (open.bigmodel.cn)",
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
value: "global",
|
|
518
|
+
label: "Global",
|
|
519
|
+
hint: "Z.AI Global (api.z.ai)",
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
value: "cn",
|
|
523
|
+
label: "CN",
|
|
524
|
+
hint: "Z.AI CN (open.bigmodel.cn)",
|
|
525
|
+
},
|
|
526
|
+
],
|
|
527
|
+
initialValue: "global",
|
|
528
|
+
});
|
|
529
|
+
}
|
|
472
530
|
}
|
|
473
531
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
474
532
|
profileId: "zai:default",
|
|
475
533
|
provider: "zai",
|
|
476
534
|
mode: "api_key",
|
|
477
535
|
});
|
|
478
|
-
{
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
},
|
|
498
|
-
},
|
|
499
|
-
}),
|
|
500
|
-
noteDefault: ZAI_DEFAULT_MODEL_REF,
|
|
501
|
-
noteAgentModel,
|
|
502
|
-
prompter: params.prompter,
|
|
503
|
-
});
|
|
504
|
-
nextConfig = applied.config;
|
|
505
|
-
agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
|
|
506
|
-
}
|
|
536
|
+
const defaultModel = modelIdOverride ? `zai/${modelIdOverride}` : ZAI_DEFAULT_MODEL_REF;
|
|
537
|
+
const applied = await applyDefaultModelChoice({
|
|
538
|
+
config: nextConfig,
|
|
539
|
+
setDefaultModel: params.setDefaultModel,
|
|
540
|
+
defaultModel,
|
|
541
|
+
applyDefaultConfig: (config) => applyZaiConfig(config, {
|
|
542
|
+
endpoint,
|
|
543
|
+
...(modelIdOverride ? { modelId: modelIdOverride } : {}),
|
|
544
|
+
}),
|
|
545
|
+
applyProviderConfig: (config) => applyZaiProviderConfig(config, {
|
|
546
|
+
endpoint,
|
|
547
|
+
...(modelIdOverride ? { modelId: modelIdOverride } : {}),
|
|
548
|
+
}),
|
|
549
|
+
noteDefault: defaultModel,
|
|
550
|
+
noteAgentModel,
|
|
551
|
+
prompter: params.prompter,
|
|
552
|
+
});
|
|
553
|
+
nextConfig = applied.config;
|
|
554
|
+
agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
|
|
507
555
|
return { config: nextConfig, agentModelOverride };
|
|
508
556
|
}
|
|
509
557
|
if (authChoice === "xiaomi-api-key") {
|
|
@@ -528,7 +576,7 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
528
576
|
message: "Enter Xiaomi API key",
|
|
529
577
|
validate: validateApiKeyInput,
|
|
530
578
|
});
|
|
531
|
-
await setXiaomiApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
|
579
|
+
await setXiaomiApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
532
580
|
}
|
|
533
581
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
534
582
|
profileId: "xiaomi:default",
|
|
@@ -553,14 +601,14 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
553
601
|
}
|
|
554
602
|
if (authChoice === "synthetic-api-key") {
|
|
555
603
|
if (params.opts?.token && params.opts?.tokenProvider === "synthetic") {
|
|
556
|
-
await setSyntheticApiKey(String(params.opts.token).trim(), params.agentDir);
|
|
604
|
+
await setSyntheticApiKey(String(params.opts.token ?? "").trim(), params.agentDir);
|
|
557
605
|
}
|
|
558
606
|
else {
|
|
559
607
|
const key = await params.prompter.text({
|
|
560
608
|
message: "Enter Synthetic API key",
|
|
561
609
|
validate: (value) => (value?.trim() ? undefined : "Required"),
|
|
562
610
|
});
|
|
563
|
-
await setSyntheticApiKey(String(key).trim(), params.agentDir);
|
|
611
|
+
await setSyntheticApiKey(String(key ?? "").trim(), params.agentDir);
|
|
564
612
|
}
|
|
565
613
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
566
614
|
profileId: "synthetic:default",
|
|
@@ -612,7 +660,7 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
612
660
|
message: "Enter Venice AI API key",
|
|
613
661
|
validate: validateApiKeyInput,
|
|
614
662
|
});
|
|
615
|
-
await setVeniceApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
|
663
|
+
await setVeniceApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
616
664
|
}
|
|
617
665
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
618
666
|
profileId: "venice:default",
|
|
@@ -664,7 +712,7 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
664
712
|
message: "Enter OpenCode Zen API key",
|
|
665
713
|
validate: validateApiKeyInput,
|
|
666
714
|
});
|
|
667
|
-
await setOpencodeZenApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
|
715
|
+
await setOpencodeZenApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
668
716
|
}
|
|
669
717
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
670
718
|
profileId: "opencode:default",
|
|
@@ -715,7 +763,7 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
715
763
|
message: "Enter Together AI API key",
|
|
716
764
|
validate: validateApiKeyInput,
|
|
717
765
|
});
|
|
718
|
-
await setTogetherApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
|
766
|
+
await setTogetherApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
719
767
|
}
|
|
720
768
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
721
769
|
profileId: "together:default",
|
|
@@ -738,6 +786,9 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
738
786
|
}
|
|
739
787
|
return { config: nextConfig, agentModelOverride };
|
|
740
788
|
}
|
|
789
|
+
if (authChoice === "huggingface-api-key") {
|
|
790
|
+
return applyAuthChoiceHuggingface({ ...params, authChoice });
|
|
791
|
+
}
|
|
741
792
|
if (authChoice === "qianfan-api-key") {
|
|
742
793
|
let hasCredential = false;
|
|
743
794
|
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "qianfan") {
|
|
@@ -766,7 +817,7 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
766
817
|
message: "Enter QIANFAN API key",
|
|
767
818
|
validate: validateApiKeyInput,
|
|
768
819
|
});
|
|
769
|
-
setQianfanApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
|
820
|
+
setQianfanApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
770
821
|
}
|
|
771
822
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
772
823
|
profileId: "qianfan:default",
|
|
@@ -817,7 +868,7 @@ export async function applyAuthChoiceApiProviders(params) {
|
|
|
817
868
|
message: "Enter NVIDIA API key",
|
|
818
869
|
validate: validateApiKeyInput,
|
|
819
870
|
});
|
|
820
|
-
setNvidiaApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
|
871
|
+
setNvidiaApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
821
872
|
}
|
|
822
873
|
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
823
874
|
profileId: "nvidia:default",
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { discoverHuggingfaceModels, isHuggingfacePolicyLocked, } from "../agents/huggingface-models.js";
|
|
2
|
+
import { resolveEnvApiKey } from "../agents/model-auth.js";
|
|
3
|
+
import { formatApiKeyPreview, normalizeApiKeyInput, validateApiKeyInput, } from "./auth-choice.api-key.js";
|
|
4
|
+
import { applyDefaultModelChoice } from "./auth-choice.default-model.js";
|
|
5
|
+
import { ensureModelAllowlistEntry } from "./model-allowlist.js";
|
|
6
|
+
import { applyAuthProfileConfig, applyHuggingfaceProviderConfig, setHuggingfaceApiKey, HUGGINGFACE_DEFAULT_MODEL_REF, } from "./onboard-auth.js";
|
|
7
|
+
export async function applyAuthChoiceHuggingface(params) {
|
|
8
|
+
if (params.authChoice !== "huggingface-api-key") {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
let nextConfig = params.config;
|
|
12
|
+
let agentModelOverride;
|
|
13
|
+
const noteAgentModel = async (model) => {
|
|
14
|
+
if (!params.agentId) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
await params.prompter.note(`Default model set to ${model} for agent "${params.agentId}".`, "Model configured");
|
|
18
|
+
};
|
|
19
|
+
let hasCredential = false;
|
|
20
|
+
let hfKey = "";
|
|
21
|
+
if (!hasCredential && params.opts?.token && params.opts.tokenProvider === "huggingface") {
|
|
22
|
+
hfKey = normalizeApiKeyInput(params.opts.token);
|
|
23
|
+
await setHuggingfaceApiKey(hfKey, params.agentDir);
|
|
24
|
+
hasCredential = true;
|
|
25
|
+
}
|
|
26
|
+
if (!hasCredential) {
|
|
27
|
+
await params.prompter.note([
|
|
28
|
+
"Hugging Face Inference Providers offer OpenAI-compatible chat completions.",
|
|
29
|
+
"Create a token at: https://huggingface.co/settings/tokens (fine-grained, 'Make calls to Inference Providers').",
|
|
30
|
+
].join("\n"), "Hugging Face");
|
|
31
|
+
}
|
|
32
|
+
if (!hasCredential) {
|
|
33
|
+
const envKey = resolveEnvApiKey("huggingface");
|
|
34
|
+
if (envKey) {
|
|
35
|
+
const useExisting = await params.prompter.confirm({
|
|
36
|
+
message: `Use existing Hugging Face token (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
|
37
|
+
initialValue: true,
|
|
38
|
+
});
|
|
39
|
+
if (useExisting) {
|
|
40
|
+
hfKey = envKey.apiKey;
|
|
41
|
+
await setHuggingfaceApiKey(hfKey, params.agentDir);
|
|
42
|
+
hasCredential = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!hasCredential) {
|
|
47
|
+
const key = await params.prompter.text({
|
|
48
|
+
message: "Enter Hugging Face API key (HF token)",
|
|
49
|
+
validate: validateApiKeyInput,
|
|
50
|
+
});
|
|
51
|
+
hfKey = normalizeApiKeyInput(String(key ?? ""));
|
|
52
|
+
await setHuggingfaceApiKey(hfKey, params.agentDir);
|
|
53
|
+
}
|
|
54
|
+
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
55
|
+
profileId: "huggingface:default",
|
|
56
|
+
provider: "huggingface",
|
|
57
|
+
mode: "api_key",
|
|
58
|
+
});
|
|
59
|
+
const models = await discoverHuggingfaceModels(hfKey);
|
|
60
|
+
const modelRefPrefix = "huggingface/";
|
|
61
|
+
const options = [];
|
|
62
|
+
for (const m of models) {
|
|
63
|
+
const baseRef = `${modelRefPrefix}${m.id}`;
|
|
64
|
+
const label = m.name ?? m.id;
|
|
65
|
+
options.push({ value: baseRef, label });
|
|
66
|
+
options.push({ value: `${baseRef}:cheapest`, label: `${label} (cheapest)` });
|
|
67
|
+
options.push({ value: `${baseRef}:fastest`, label: `${label} (fastest)` });
|
|
68
|
+
}
|
|
69
|
+
const defaultRef = HUGGINGFACE_DEFAULT_MODEL_REF;
|
|
70
|
+
options.sort((a, b) => {
|
|
71
|
+
if (a.value === defaultRef) {
|
|
72
|
+
return -1;
|
|
73
|
+
}
|
|
74
|
+
if (b.value === defaultRef) {
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
return a.label.localeCompare(b.label, undefined, { sensitivity: "base" });
|
|
78
|
+
});
|
|
79
|
+
const selectedModelRef = options.length === 0
|
|
80
|
+
? defaultRef
|
|
81
|
+
: options.length === 1
|
|
82
|
+
? options[0].value
|
|
83
|
+
: await params.prompter.select({
|
|
84
|
+
message: "Default Hugging Face model",
|
|
85
|
+
options,
|
|
86
|
+
initialValue: options.some((o) => o.value === defaultRef)
|
|
87
|
+
? defaultRef
|
|
88
|
+
: options[0].value,
|
|
89
|
+
});
|
|
90
|
+
if (isHuggingfacePolicyLocked(selectedModelRef)) {
|
|
91
|
+
await params.prompter.note("Provider locked — router will choose backend by cost or speed.", "Hugging Face");
|
|
92
|
+
}
|
|
93
|
+
const applied = await applyDefaultModelChoice({
|
|
94
|
+
config: nextConfig,
|
|
95
|
+
setDefaultModel: params.setDefaultModel,
|
|
96
|
+
defaultModel: selectedModelRef,
|
|
97
|
+
applyDefaultConfig: (config) => {
|
|
98
|
+
const withProvider = applyHuggingfaceProviderConfig(config);
|
|
99
|
+
const existingModel = withProvider.agents?.defaults?.model;
|
|
100
|
+
const withPrimary = {
|
|
101
|
+
...withProvider,
|
|
102
|
+
agents: {
|
|
103
|
+
...withProvider.agents,
|
|
104
|
+
defaults: {
|
|
105
|
+
...withProvider.agents?.defaults,
|
|
106
|
+
model: {
|
|
107
|
+
...(existingModel && typeof existingModel === "object" && "fallbacks" in existingModel
|
|
108
|
+
? {
|
|
109
|
+
fallbacks: existingModel.fallbacks,
|
|
110
|
+
}
|
|
111
|
+
: {}),
|
|
112
|
+
primary: selectedModelRef,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
return ensureModelAllowlistEntry({
|
|
118
|
+
cfg: withPrimary,
|
|
119
|
+
modelRef: selectedModelRef,
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
applyProviderConfig: applyHuggingfaceProviderConfig,
|
|
123
|
+
noteDefault: selectedModelRef,
|
|
124
|
+
noteAgentModel,
|
|
125
|
+
prompter: params.prompter,
|
|
126
|
+
});
|
|
127
|
+
nextConfig = applied.config;
|
|
128
|
+
agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
|
|
129
|
+
return { config: nextConfig, agentModelOverride };
|
|
130
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ensureAuthProfileStore, resolveAuthProfileOrder } from "../agents/auth-profiles.js";
|
|
2
|
+
import { resolveEnvApiKey } from "../agents/model-auth.js";
|
|
3
|
+
import { formatApiKeyPreview, normalizeApiKeyInput, validateApiKeyInput, } from "./auth-choice.api-key.js";
|
|
4
|
+
import { applyDefaultModelChoice } from "./auth-choice.default-model.js";
|
|
5
|
+
import { applyAuthProfileConfig, applyOpenrouterConfig, applyOpenrouterProviderConfig, setOpenrouterApiKey, OPENROUTER_DEFAULT_MODEL_REF, } from "./onboard-auth.js";
|
|
6
|
+
export async function applyAuthChoiceOpenRouter(params) {
|
|
7
|
+
let nextConfig = params.config;
|
|
8
|
+
let agentModelOverride;
|
|
9
|
+
const noteAgentModel = async (model) => {
|
|
10
|
+
if (!params.agentId) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
await params.prompter.note(`Default model set to ${model} for agent "${params.agentId}".`, "Model configured");
|
|
14
|
+
};
|
|
15
|
+
const store = ensureAuthProfileStore(params.agentDir, { allowKeychainPrompt: false });
|
|
16
|
+
const profileOrder = resolveAuthProfileOrder({
|
|
17
|
+
cfg: nextConfig,
|
|
18
|
+
store,
|
|
19
|
+
provider: "openrouter",
|
|
20
|
+
});
|
|
21
|
+
const existingProfileId = profileOrder.find((profileId) => Boolean(store.profiles[profileId]));
|
|
22
|
+
const existingCred = existingProfileId ? store.profiles[existingProfileId] : undefined;
|
|
23
|
+
let profileId = "openrouter:default";
|
|
24
|
+
let mode = "api_key";
|
|
25
|
+
let hasCredential = false;
|
|
26
|
+
if (existingProfileId && existingCred?.type) {
|
|
27
|
+
profileId = existingProfileId;
|
|
28
|
+
mode =
|
|
29
|
+
existingCred.type === "oauth" ? "oauth" : existingCred.type === "token" ? "token" : "api_key";
|
|
30
|
+
hasCredential = true;
|
|
31
|
+
}
|
|
32
|
+
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "openrouter") {
|
|
33
|
+
await setOpenrouterApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir);
|
|
34
|
+
hasCredential = true;
|
|
35
|
+
}
|
|
36
|
+
if (!hasCredential) {
|
|
37
|
+
const envKey = resolveEnvApiKey("openrouter");
|
|
38
|
+
if (envKey) {
|
|
39
|
+
const useExisting = await params.prompter.confirm({
|
|
40
|
+
message: `Use existing OPENROUTER_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
|
41
|
+
initialValue: true,
|
|
42
|
+
});
|
|
43
|
+
if (useExisting) {
|
|
44
|
+
await setOpenrouterApiKey(envKey.apiKey, params.agentDir);
|
|
45
|
+
hasCredential = true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (!hasCredential) {
|
|
50
|
+
const key = await params.prompter.text({
|
|
51
|
+
message: "Enter OpenRouter API key",
|
|
52
|
+
validate: validateApiKeyInput,
|
|
53
|
+
});
|
|
54
|
+
await setOpenrouterApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir);
|
|
55
|
+
hasCredential = true;
|
|
56
|
+
}
|
|
57
|
+
if (hasCredential) {
|
|
58
|
+
nextConfig = applyAuthProfileConfig(nextConfig, {
|
|
59
|
+
profileId,
|
|
60
|
+
provider: "openrouter",
|
|
61
|
+
mode,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const applied = await applyDefaultModelChoice({
|
|
65
|
+
config: nextConfig,
|
|
66
|
+
setDefaultModel: params.setDefaultModel,
|
|
67
|
+
defaultModel: OPENROUTER_DEFAULT_MODEL_REF,
|
|
68
|
+
applyDefaultConfig: applyOpenrouterConfig,
|
|
69
|
+
applyProviderConfig: applyOpenrouterProviderConfig,
|
|
70
|
+
noteDefault: OPENROUTER_DEFAULT_MODEL_REF,
|
|
71
|
+
noteAgentModel,
|
|
72
|
+
prompter: params.prompter,
|
|
73
|
+
});
|
|
74
|
+
nextConfig = applied.config;
|
|
75
|
+
agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
|
|
76
|
+
return { config: nextConfig, agentModelOverride };
|
|
77
|
+
}
|