@poolzin/pool-bot 2026.2.5 → 2026.2.6

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.
@@ -33,6 +33,15 @@ export function upsertAuthProfile(params) {
33
33
  store.profiles[params.profileId] = params.credential;
34
34
  saveAuthProfileStore(store, params.agentDir);
35
35
  }
36
+ export async function upsertAuthProfileWithLock(params) {
37
+ return await updateAuthProfileStoreWithLock({
38
+ agentDir: params.agentDir,
39
+ updater: (store) => {
40
+ store.profiles[params.profileId] = params.credential;
41
+ return true;
42
+ },
43
+ });
44
+ }
36
45
  export function listProfilesForProvider(store, provider) {
37
46
  const providerKey = normalizeProviderId(provider);
38
47
  return Object.entries(store.profiles)
@@ -4,7 +4,7 @@ export { formatAuthDoctorHint } from "./auth-profiles/doctor.js";
4
4
  export { resolveApiKeyForProfile } from "./auth-profiles/oauth.js";
5
5
  export { resolveAuthProfileOrder } from "./auth-profiles/order.js";
6
6
  export { resolveAuthStorePathForDisplay } from "./auth-profiles/paths.js";
7
- export { listProfilesForProvider, markAuthProfileGood, setAuthProfileOrder, upsertAuthProfile, } from "./auth-profiles/profiles.js";
7
+ export { listProfilesForProvider, markAuthProfileGood, setAuthProfileOrder, upsertAuthProfile, upsertAuthProfileWithLock, } from "./auth-profiles/profiles.js";
8
8
  export { repairOAuthProfileIdMismatch, suggestOAuthProfileIdForLegacyDefault, } from "./auth-profiles/repair.js";
9
9
  export { ensureAuthProfileStore, loadAuthProfileStore, saveAuthProfileStore, } from "./auth-profiles/store.js";
10
10
  export { calculateAuthProfileCooldownMs, clearAuthProfileCooldown, isProfileInCooldown, markAuthProfileCooldown, markAuthProfileFailure, markAuthProfileUsed, resolveProfileUnusableUntilForDisplay, } from "./auth-profiles/usage.js";
@@ -0,0 +1,166 @@
1
+ /** Hugging Face Inference Providers (router) — OpenAI-compatible chat completions. */
2
+ export const HUGGINGFACE_BASE_URL = "https://router.huggingface.co/v1";
3
+ /** Router policy suffixes: router picks backend by cost or speed; no specific provider selection. */
4
+ export const HUGGINGFACE_POLICY_SUFFIXES = ["cheapest", "fastest"];
5
+ /**
6
+ * True when the model ref uses :cheapest or :fastest. When true, provider choice is locked
7
+ * (router decides); do not show an interactive "prefer specific backend" option.
8
+ */
9
+ export function isHuggingfacePolicyLocked(modelRef) {
10
+ const ref = String(modelRef).trim();
11
+ return HUGGINGFACE_POLICY_SUFFIXES.some((s) => ref.endsWith(`:${s}`) || ref === s);
12
+ }
13
+ /** Default cost when not in static catalog (HF pricing varies by provider). */
14
+ const HUGGINGFACE_DEFAULT_COST = {
15
+ input: 0,
16
+ output: 0,
17
+ cacheRead: 0,
18
+ cacheWrite: 0,
19
+ };
20
+ /** Defaults for models discovered from GET /v1/models. */
21
+ const HUGGINGFACE_DEFAULT_CONTEXT_WINDOW = 131072;
22
+ const HUGGINGFACE_DEFAULT_MAX_TOKENS = 8192;
23
+ export const HUGGINGFACE_MODEL_CATALOG = [
24
+ {
25
+ id: "deepseek-ai/DeepSeek-R1",
26
+ name: "DeepSeek R1",
27
+ reasoning: true,
28
+ input: ["text"],
29
+ contextWindow: 131072,
30
+ maxTokens: 8192,
31
+ cost: { input: 3.0, output: 7.0, cacheRead: 3.0, cacheWrite: 3.0 },
32
+ },
33
+ {
34
+ id: "deepseek-ai/DeepSeek-V3.1",
35
+ name: "DeepSeek V3.1",
36
+ reasoning: false,
37
+ input: ["text"],
38
+ contextWindow: 131072,
39
+ maxTokens: 8192,
40
+ cost: { input: 0.6, output: 1.25, cacheRead: 0.6, cacheWrite: 0.6 },
41
+ },
42
+ {
43
+ id: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
44
+ name: "Llama 3.3 70B Instruct Turbo",
45
+ reasoning: false,
46
+ input: ["text"],
47
+ contextWindow: 131072,
48
+ maxTokens: 8192,
49
+ cost: { input: 0.88, output: 0.88, cacheRead: 0.88, cacheWrite: 0.88 },
50
+ },
51
+ {
52
+ id: "openai/gpt-oss-120b",
53
+ name: "GPT-OSS 120B",
54
+ reasoning: false,
55
+ input: ["text"],
56
+ contextWindow: 131072,
57
+ maxTokens: 8192,
58
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
59
+ },
60
+ ];
61
+ export function buildHuggingfaceModelDefinition(model) {
62
+ return {
63
+ id: model.id,
64
+ name: model.name,
65
+ reasoning: model.reasoning,
66
+ input: model.input,
67
+ cost: model.cost,
68
+ contextWindow: model.contextWindow,
69
+ maxTokens: model.maxTokens,
70
+ };
71
+ }
72
+ /**
73
+ * Infer reasoning and display name from Hub-style model id (e.g. "deepseek-ai/DeepSeek-R1").
74
+ */
75
+ function inferredMetaFromModelId(id) {
76
+ const base = id.split("/").pop() ?? id;
77
+ const reasoning = /r1|reasoning|thinking|reason/i.test(id) || /-\d+[tb]?-thinking/i.test(base);
78
+ const name = base.replace(/-/g, " ").replace(/\b(\w)/g, (c) => c.toUpperCase());
79
+ return { name, reasoning };
80
+ }
81
+ /** Prefer API-supplied display name, then owned_by/id, then inferred from id. */
82
+ function displayNameFromApiEntry(entry, inferredName) {
83
+ const fromApi = (typeof entry.name === "string" && entry.name.trim()) ||
84
+ (typeof entry.title === "string" && entry.title.trim()) ||
85
+ (typeof entry.display_name === "string" && entry.display_name.trim());
86
+ if (fromApi) {
87
+ return fromApi;
88
+ }
89
+ if (typeof entry.owned_by === "string" && entry.owned_by.trim()) {
90
+ const base = entry.id.split("/").pop() ?? entry.id;
91
+ return `${entry.owned_by.trim()}/${base}`;
92
+ }
93
+ return inferredName;
94
+ }
95
+ /**
96
+ * Discover chat-completion models from Hugging Face Inference Providers (GET /v1/models).
97
+ * Requires a valid HF token. Falls back to static catalog on failure or in test env.
98
+ */
99
+ export async function discoverHuggingfaceModels(apiKey) {
100
+ if (process.env.VITEST === "true" || process.env.NODE_ENV === "test") {
101
+ return HUGGINGFACE_MODEL_CATALOG.map(buildHuggingfaceModelDefinition);
102
+ }
103
+ const trimmedKey = apiKey?.trim();
104
+ if (!trimmedKey) {
105
+ return HUGGINGFACE_MODEL_CATALOG.map(buildHuggingfaceModelDefinition);
106
+ }
107
+ try {
108
+ // GET https://router.huggingface.co/v1/models — response: { object, data: [{ id, owned_by, architecture: { input_modalities }, providers: [{ provider, context_length?, pricing? }] }] }. POST /v1/chat/completions requires Authorization.
109
+ const response = await fetch(`${HUGGINGFACE_BASE_URL}/models`, {
110
+ signal: AbortSignal.timeout(10_000),
111
+ headers: {
112
+ Authorization: `Bearer ${trimmedKey}`,
113
+ "Content-Type": "application/json",
114
+ },
115
+ });
116
+ if (!response.ok) {
117
+ console.warn(`[huggingface-models] GET /v1/models failed: HTTP ${response.status}, using static catalog`);
118
+ return HUGGINGFACE_MODEL_CATALOG.map(buildHuggingfaceModelDefinition);
119
+ }
120
+ const body = (await response.json());
121
+ const data = body?.data;
122
+ if (!Array.isArray(data) || data.length === 0) {
123
+ console.warn("[huggingface-models] No models in response, using static catalog");
124
+ return HUGGINGFACE_MODEL_CATALOG.map(buildHuggingfaceModelDefinition);
125
+ }
126
+ const catalogById = new Map(HUGGINGFACE_MODEL_CATALOG.map((m) => [m.id, m]));
127
+ const seen = new Set();
128
+ const models = [];
129
+ for (const entry of data) {
130
+ const id = typeof entry?.id === "string" ? entry.id.trim() : "";
131
+ if (!id || seen.has(id)) {
132
+ continue;
133
+ }
134
+ seen.add(id);
135
+ const catalogEntry = catalogById.get(id);
136
+ if (catalogEntry) {
137
+ models.push(buildHuggingfaceModelDefinition(catalogEntry));
138
+ }
139
+ else {
140
+ const inferred = inferredMetaFromModelId(id);
141
+ const name = displayNameFromApiEntry(entry, inferred.name);
142
+ const modalities = entry.architecture?.input_modalities;
143
+ const input = Array.isArray(modalities) && modalities.includes("image") ? ["text", "image"] : ["text"];
144
+ const providers = Array.isArray(entry.providers) ? entry.providers : [];
145
+ const providerWithContext = providers.find((p) => typeof p?.context_length === "number" && p.context_length > 0);
146
+ const contextLength = providerWithContext?.context_length ?? HUGGINGFACE_DEFAULT_CONTEXT_WINDOW;
147
+ models.push({
148
+ id,
149
+ name,
150
+ reasoning: inferred.reasoning,
151
+ input,
152
+ cost: HUGGINGFACE_DEFAULT_COST,
153
+ contextWindow: contextLength,
154
+ maxTokens: HUGGINGFACE_DEFAULT_MAX_TOKENS,
155
+ });
156
+ }
157
+ }
158
+ return models.length > 0
159
+ ? models
160
+ : HUGGINGFACE_MODEL_CATALOG.map(buildHuggingfaceModelDefinition);
161
+ }
162
+ catch (error) {
163
+ console.warn(`[huggingface-models] Discovery failed: ${String(error)}, using static catalog`);
164
+ return HUGGINGFACE_MODEL_CATALOG.map(buildHuggingfaceModelDefinition);
165
+ }
166
+ }
@@ -205,6 +205,9 @@ export function resolveEnvApiKey(provider) {
205
205
  if (normalized === "kimi-coding") {
206
206
  return pick("KIMI_API_KEY") ?? pick("KIMICODE_API_KEY");
207
207
  }
208
+ if (normalized === "huggingface") {
209
+ return pick("HUGGINGFACE_HUB_TOKEN") ?? pick("HF_TOKEN");
210
+ }
208
211
  const envMap = {
209
212
  openai: "OPENAI_API_KEY",
210
213
  google: "GEMINI_API_KEY",
@@ -214,6 +217,7 @@ export function resolveEnvApiKey(provider) {
214
217
  cerebras: "CEREBRAS_API_KEY",
215
218
  xai: "XAI_API_KEY",
216
219
  openrouter: "OPENROUTER_API_KEY",
220
+ litellm: "LITELLM_API_KEY",
217
221
  "vercel-ai-gateway": "AI_GATEWAY_API_KEY",
218
222
  "cloudflare-ai-gateway": "CLOUDFLARE_AI_GATEWAY_API_KEY",
219
223
  moonshot: "MOONSHOT_API_KEY",
@@ -227,6 +231,7 @@ export function resolveEnvApiKey(provider) {
227
231
  qianfan: "QIANFAN_API_KEY",
228
232
  nvidia: "NVIDIA_API_KEY",
229
233
  ollama: "OLLAMA_API_KEY",
234
+ vllm: "VLLM_API_KEY",
230
235
  };
231
236
  const envVar = envMap[normalized];
232
237
  if (!envVar)
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2026.2.5",
3
- "commit": "b05c49be3d3a8a8bba6ab957a7ef10c3c4856f8d",
4
- "builtAt": "2026-02-14T04:39:03.692Z"
2
+ "version": "2026.2.6",
3
+ "commit": "d67aa6d87b042556d38fbe64f7ed4ce6a22dbf4e",
4
+ "builtAt": "2026-02-14T08:48:33.834Z"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  import JSON5 from "json5";
2
- import { readConfigFileSnapshot, writeConfigFile } from "../config/config.js";
2
+ import { readConfigFileSnapshot, validateConfigObjectWithPlugins, writeConfigFile, } from "../config/config.js";
3
3
  import { danger, info } from "../globals.js";
4
4
  import { defaultRuntime } from "../runtime.js";
5
5
  import { formatDocsLink } from "../terminal/links.js";
@@ -259,7 +259,14 @@ export function registerConfigCli(program) {
259
259
  const snapshot = await loadValidConfig();
260
260
  const next = snapshot.config;
261
261
  setAtPath(next, parsedPath, parsedValue);
262
- await writeConfigFile(next);
262
+ const validated = validateConfigObjectWithPlugins(next);
263
+ if (!validated.ok) {
264
+ const issue = validated.issues[0];
265
+ defaultRuntime.error(danger(`Config invalid after set: ${issue.path}: ${issue.message}`));
266
+ defaultRuntime.exit(1);
267
+ return;
268
+ }
269
+ await writeConfigFile(validated.config);
263
270
  defaultRuntime.log(info(`Updated ${path}. Restart the gateway to apply.`));
264
271
  }
265
272
  catch (err) {
@@ -284,7 +291,14 @@ export function registerConfigCli(program) {
284
291
  defaultRuntime.exit(1);
285
292
  return;
286
293
  }
287
- await writeConfigFile(next);
294
+ const validated = validateConfigObjectWithPlugins(next);
295
+ if (!validated.ok) {
296
+ const issue = validated.issues[0];
297
+ defaultRuntime.error(danger(`Config invalid after unset: ${issue.path}: ${issue.message}`));
298
+ defaultRuntime.exit(1);
299
+ return;
300
+ }
301
+ await writeConfigFile(validated.config);
288
302
  defaultRuntime.log(info(`Removed ${path}. Restart the gateway to apply.`));
289
303
  }
290
304
  catch (err) {
@@ -4,15 +4,18 @@ import { formatDocsLink } from "../../terminal/links.js";
4
4
  import { theme } from "../../terminal/theme.js";
5
5
  import { runCommandWithRuntime } from "../cli-utils.js";
6
6
  function resolveInstallDaemonFlag(command, opts) {
7
- if (!command || typeof command !== "object")
7
+ if (!command || typeof command !== "object") {
8
8
  return undefined;
9
+ }
9
10
  const getOptionValueSource = "getOptionValueSource" in command ? command.getOptionValueSource : undefined;
10
- if (typeof getOptionValueSource !== "function")
11
+ if (typeof getOptionValueSource !== "function") {
11
12
  return undefined;
13
+ }
12
14
  // Commander doesn't support option conflicts natively; keep original behavior.
13
15
  // If --skip-daemon is explicitly passed, it wins.
14
- if (getOptionValueSource.call(command, "skipDaemon") === "cli")
16
+ if (getOptionValueSource.call(command, "skipDaemon") === "cli") {
15
17
  return false;
18
+ }
16
19
  if (getOptionValueSource.call(command, "installDaemon") === "cli") {
17
20
  return Boolean(opts.installDaemon);
18
21
  }
@@ -29,7 +32,7 @@ export function registerOnboardCommand(program) {
29
32
  .option("--accept-risk", "Acknowledge that agents are powerful and full system access is risky (required for --non-interactive)", false)
30
33
  .option("--flow <flow>", "Wizard flow: quickstart|advanced|manual")
31
34
  .option("--mode <mode>", "Wizard mode: local|remote")
32
- .option("--auth-choice <choice>", "Auth: setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip")
35
+ .option("--auth-choice <choice>", "Auth: setup-token|token|chutes|vllm|openai-codex|openai-api-key|xai-api-key|qianfan-api-key|openrouter-api-key|litellm-api-key|ai-gateway-api-key|cloudflare-ai-gateway-api-key|moonshot-api-key|moonshot-api-key-cn|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|zai-coding-global|zai-coding-cn|zai-global|zai-cn|xiaomi-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|custom-api-key|skip|together-api-key|huggingface-api-key|nvidia-api-key")
33
36
  .option("--token-provider <id>", "Token provider id (non-interactive; used with --auth-choice token)")
34
37
  .option("--token <token>", "Token value (non-interactive; used with --auth-choice token)")
35
38
  .option("--token-profile-id <id>", "Auth profile id (non-interactive; default: <provider>:manual)")
@@ -38,19 +41,29 @@ export function registerOnboardCommand(program) {
38
41
  .option("--openai-api-key <key>", "OpenAI API key")
39
42
  .option("--openrouter-api-key <key>", "OpenRouter API key")
40
43
  .option("--ai-gateway-api-key <key>", "Vercel AI Gateway API key")
44
+ .option("--cloudflare-ai-gateway-account-id <id>", "Cloudflare Account ID")
45
+ .option("--cloudflare-ai-gateway-gateway-id <id>", "Cloudflare AI Gateway ID")
46
+ .option("--cloudflare-ai-gateway-api-key <key>", "Cloudflare AI Gateway API key")
41
47
  .option("--moonshot-api-key <key>", "Moonshot API key")
42
- .option("--kimi-code-api-key <key>", "Kimi Code API key")
48
+ .option("--kimi-code-api-key <key>", "Kimi Coding API key")
43
49
  .option("--gemini-api-key <key>", "Gemini API key")
44
50
  .option("--zai-api-key <key>", "Z.AI API key")
51
+ .option("--xiaomi-api-key <key>", "Xiaomi API key")
45
52
  .option("--minimax-api-key <key>", "MiniMax API key")
46
53
  .option("--synthetic-api-key <key>", "Synthetic API key")
47
54
  .option("--venice-api-key <key>", "Venice API key")
55
+ .option("--together-api-key <key>", "Together AI API key")
56
+ .option("--huggingface-api-key <key>", "Hugging Face API key (HF token)")
48
57
  .option("--opencode-zen-api-key <key>", "OpenCode Zen API key")
49
58
  .option("--xai-api-key <key>", "xAI API key")
50
- .option("--together-api-key <key>", "Together AI API key")
51
- .option("--qianfan-api-key <key>", "Qianfan API key")
52
- .option("--xiaomi-api-key <key>", "Xiaomi API key")
53
59
  .option("--nvidia-api-key <key>", "NVIDIA API key")
60
+ .option("--litellm-api-key <key>", "LiteLLM API key")
61
+ .option("--qianfan-api-key <key>", "QIANFAN API key")
62
+ .option("--custom-base-url <url>", "Custom provider base URL")
63
+ .option("--custom-api-key <key>", "Custom provider API key (optional)")
64
+ .option("--custom-model-id <id>", "Custom provider model ID")
65
+ .option("--custom-provider-id <id>", "Custom provider ID (optional; auto-derived by default)")
66
+ .option("--custom-compatibility <mode>", "Custom provider API compatibility: openai|anthropic (default: openai)")
54
67
  .option("--gateway-port <port>", "Gateway port")
55
68
  .option("--gateway-bind <mode>", "Gateway bind: loopback|tailnet|lan|auto|custom")
56
69
  .option("--gateway-auth <mode>", "Gateway auth: token|password")
@@ -91,19 +104,29 @@ export function registerOnboardCommand(program) {
91
104
  openaiApiKey: opts.openaiApiKey,
92
105
  openrouterApiKey: opts.openrouterApiKey,
93
106
  aiGatewayApiKey: opts.aiGatewayApiKey,
107
+ cloudflareAiGatewayAccountId: opts.cloudflareAiGatewayAccountId,
108
+ cloudflareAiGatewayGatewayId: opts.cloudflareAiGatewayGatewayId,
109
+ cloudflareAiGatewayApiKey: opts.cloudflareAiGatewayApiKey,
94
110
  moonshotApiKey: opts.moonshotApiKey,
95
111
  kimiCodeApiKey: opts.kimiCodeApiKey,
96
112
  geminiApiKey: opts.geminiApiKey,
97
113
  zaiApiKey: opts.zaiApiKey,
114
+ xiaomiApiKey: opts.xiaomiApiKey,
115
+ qianfanApiKey: opts.qianfanApiKey,
98
116
  minimaxApiKey: opts.minimaxApiKey,
99
117
  syntheticApiKey: opts.syntheticApiKey,
100
118
  veniceApiKey: opts.veniceApiKey,
119
+ togetherApiKey: opts.togetherApiKey,
120
+ huggingfaceApiKey: opts.huggingfaceApiKey,
101
121
  opencodeZenApiKey: opts.opencodeZenApiKey,
102
122
  xaiApiKey: opts.xaiApiKey,
103
- togetherApiKey: opts.togetherApiKey,
104
- qianfanApiKey: opts.qianfanApiKey,
105
- xiaomiApiKey: opts.xiaomiApiKey,
106
123
  nvidiaApiKey: opts.nvidiaApiKey,
124
+ litellmApiKey: opts.litellmApiKey,
125
+ customBaseUrl: opts.customBaseUrl,
126
+ customApiKey: opts.customApiKey,
127
+ customModelId: opts.customModelId,
128
+ customProviderId: opts.customProviderId,
129
+ customCompatibility: opts.customCompatibility,
107
130
  gatewayPort: typeof gatewayPort === "number" && Number.isFinite(gatewayPort)
108
131
  ? gatewayPort
109
132
  : undefined,
@@ -11,10 +11,16 @@ const AUTH_CHOICE_GROUP_DEFS = [
11
11
  hint: "setup-token + API key",
12
12
  choices: ["token", "apiKey"],
13
13
  },
14
+ {
15
+ value: "vllm",
16
+ label: "vLLM",
17
+ hint: "Local/self-hosted OpenAI-compatible",
18
+ choices: ["vllm"],
19
+ },
14
20
  {
15
21
  value: "minimax",
16
22
  label: "MiniMax",
17
- hint: "M2.1 (recommended)",
23
+ hint: "M2.5 (recommended)",
18
24
  choices: ["minimax-portal", "minimax-api", "minimax-api-lightning"],
19
25
  },
20
26
  {
@@ -55,9 +61,9 @@ const AUTH_CHOICE_GROUP_DEFS = [
55
61
  },
56
62
  {
57
63
  value: "zai",
58
- label: "Z.AI (GLM 4.7)",
59
- hint: "API key",
60
- choices: ["zai-api-key"],
64
+ label: "Z.AI",
65
+ hint: "GLM Coding Plan / Global / CN",
66
+ choices: ["zai-coding-global", "zai-coding-cn", "zai-global", "zai-cn"],
61
67
  },
62
68
  {
63
69
  value: "qianfan",
@@ -101,12 +107,24 @@ const AUTH_CHOICE_GROUP_DEFS = [
101
107
  hint: "API key",
102
108
  choices: ["together-api-key"],
103
109
  },
110
+ {
111
+ value: "huggingface",
112
+ label: "Hugging Face",
113
+ hint: "Inference API (HF token)",
114
+ choices: ["huggingface-api-key"],
115
+ },
104
116
  {
105
117
  value: "venice",
106
118
  label: "Venice AI",
107
119
  hint: "Privacy-focused (uncensored models)",
108
120
  choices: ["venice-api-key"],
109
121
  },
122
+ {
123
+ value: "litellm",
124
+ label: "LiteLLM",
125
+ hint: "Unified LLM gateway (100+ providers)",
126
+ choices: ["litellm-api-key"],
127
+ },
110
128
  {
111
129
  value: "cloudflare-ai-gateway",
112
130
  label: "Cloudflare AI Gateway",
@@ -133,6 +151,11 @@ export function buildAuthChoiceOptions(params) {
133
151
  label: "OpenAI Codex (ChatGPT OAuth)",
134
152
  });
135
153
  options.push({ value: "chutes", label: "Chutes (OAuth)" });
154
+ options.push({
155
+ value: "vllm",
156
+ label: "vLLM (custom URL + model)",
157
+ hint: "Local/self-hosted OpenAI-compatible server",
158
+ });
136
159
  options.push({ value: "openai-api-key", label: "OpenAI API key" });
137
160
  options.push({ value: "xai-api-key", label: "xAI (Grok) API key" });
138
161
  options.push({
@@ -145,6 +168,11 @@ export function buildAuthChoiceOptions(params) {
145
168
  label: "Qianfan API key",
146
169
  });
147
170
  options.push({ value: "openrouter-api-key", label: "OpenRouter API key" });
171
+ options.push({
172
+ value: "litellm-api-key",
173
+ label: "LiteLLM API key",
174
+ hint: "Unified gateway for 100+ LLM providers",
175
+ });
148
176
  options.push({
149
177
  value: "ai-gateway-api-key",
150
178
  label: "Vercel AI Gateway API key",
@@ -177,6 +205,11 @@ export function buildAuthChoiceOptions(params) {
177
205
  label: "Together AI API key",
178
206
  hint: "Access to Llama, DeepSeek, Qwen, and more open models",
179
207
  });
208
+ options.push({
209
+ value: "huggingface-api-key",
210
+ label: "Hugging Face API key (HF token)",
211
+ hint: "Inference Providers — OpenAI-compatible chat",
212
+ });
180
213
  options.push({
181
214
  value: "github-copilot",
182
215
  label: "GitHub Copilot (GitHub device login)",
@@ -193,7 +226,27 @@ export function buildAuthChoiceOptions(params) {
193
226
  label: "Google Gemini CLI OAuth",
194
227
  hint: "Uses the bundled Gemini CLI auth plugin",
195
228
  });
196
- options.push({ value: "zai-api-key", label: "Z.AI (GLM 4.7) API key" });
229
+ options.push({ value: "zai-api-key", label: "Z.AI API key" });
230
+ options.push({
231
+ value: "zai-coding-global",
232
+ label: "Coding-Plan-Global",
233
+ hint: "GLM Coding Plan Global (api.z.ai)",
234
+ });
235
+ options.push({
236
+ value: "zai-coding-cn",
237
+ label: "Coding-Plan-CN",
238
+ hint: "GLM Coding Plan CN (open.bigmodel.cn)",
239
+ });
240
+ options.push({
241
+ value: "zai-global",
242
+ label: "Global",
243
+ hint: "Z.AI Global (api.z.ai)",
244
+ });
245
+ options.push({
246
+ value: "zai-cn",
247
+ label: "CN",
248
+ hint: "Z.AI CN (open.bigmodel.cn)",
249
+ });
197
250
  options.push({
198
251
  value: "xiaomi-api-key",
199
252
  label: "Xiaomi API key",
@@ -216,10 +269,10 @@ export function buildAuthChoiceOptions(params) {
216
269
  label: "OpenCode Zen (multi-model proxy)",
217
270
  hint: "Claude, GPT, Gemini via opencode.ai/zen",
218
271
  });
219
- options.push({ value: "minimax-api", label: "MiniMax M2.1" });
272
+ options.push({ value: "minimax-api", label: "MiniMax M2.5" });
220
273
  options.push({
221
274
  value: "minimax-api-lightning",
222
- label: "MiniMax M2.1 Lightning",
275
+ label: "MiniMax M2.5 Lightning",
223
276
  hint: "Faster, higher output cost",
224
277
  });
225
278
  options.push({ value: "custom-api-key", label: "Custom Provider" });