claude-ai-switcher 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/AGENTS.md +265 -0
  2. package/ARCHITECTURE.md +162 -0
  3. package/CLAUDE.md +267 -0
  4. package/LICENSE +21 -0
  5. package/QWEN.md +429 -0
  6. package/README.md +833 -0
  7. package/dist/clients/claude-code.d.ts +92 -0
  8. package/dist/clients/claude-code.d.ts.map +1 -0
  9. package/dist/clients/claude-code.js +312 -0
  10. package/dist/clients/claude-code.js.map +1 -0
  11. package/dist/clients/opencode.d.ts +71 -0
  12. package/dist/clients/opencode.d.ts.map +1 -0
  13. package/dist/clients/opencode.js +604 -0
  14. package/dist/clients/opencode.js.map +1 -0
  15. package/dist/config.d.ts +37 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +122 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/display.d.ts +51 -0
  20. package/dist/display.d.ts.map +1 -0
  21. package/dist/display.js +118 -0
  22. package/dist/display.js.map +1 -0
  23. package/dist/hooks/index.d.ts +60 -0
  24. package/dist/hooks/index.d.ts.map +1 -0
  25. package/dist/hooks/index.js +223 -0
  26. package/dist/hooks/index.js.map +1 -0
  27. package/dist/hooks/token-tracker.js +280 -0
  28. package/dist/hooks/visual-enhancements.js +364 -0
  29. package/dist/index.d.ts +9 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +1091 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/models.d.ts +34 -0
  34. package/dist/models.d.ts.map +1 -0
  35. package/dist/models.js +343 -0
  36. package/dist/models.js.map +1 -0
  37. package/dist/providers/alibaba.d.ts +25 -0
  38. package/dist/providers/alibaba.d.ts.map +1 -0
  39. package/dist/providers/alibaba.js +37 -0
  40. package/dist/providers/alibaba.js.map +1 -0
  41. package/dist/providers/anthropic.d.ts +14 -0
  42. package/dist/providers/anthropic.d.ts.map +1 -0
  43. package/dist/providers/anthropic.js +19 -0
  44. package/dist/providers/anthropic.js.map +1 -0
  45. package/dist/providers/gemini.d.ts +44 -0
  46. package/dist/providers/gemini.d.ts.map +1 -0
  47. package/dist/providers/gemini.js +156 -0
  48. package/dist/providers/gemini.js.map +1 -0
  49. package/dist/providers/glm.d.ts +25 -0
  50. package/dist/providers/glm.d.ts.map +1 -0
  51. package/dist/providers/glm.js +89 -0
  52. package/dist/providers/glm.js.map +1 -0
  53. package/dist/providers/ollama.d.ts +48 -0
  54. package/dist/providers/ollama.d.ts.map +1 -0
  55. package/dist/providers/ollama.js +174 -0
  56. package/dist/providers/ollama.js.map +1 -0
  57. package/dist/providers/openrouter.d.ts +24 -0
  58. package/dist/providers/openrouter.d.ts.map +1 -0
  59. package/dist/providers/openrouter.js +36 -0
  60. package/dist/providers/openrouter.js.map +1 -0
  61. package/dist/verify.d.ts +24 -0
  62. package/dist/verify.d.ts.map +1 -0
  63. package/dist/verify.js +262 -0
  64. package/dist/verify.js.map +1 -0
  65. package/package.json +57 -0
  66. package/scripts/copy-hooks.js +15 -0
  67. package/src/clients/claude-code.ts +340 -0
  68. package/src/clients/opencode.ts +618 -0
  69. package/src/config.ts +101 -0
  70. package/src/display.ts +151 -0
  71. package/src/hooks/index.ts +208 -0
  72. package/src/hooks/token-tracker.js +280 -0
  73. package/src/hooks/visual-enhancements.js +364 -0
  74. package/src/index.ts +1263 -0
  75. package/src/models.ts +366 -0
  76. package/src/providers/alibaba.ts +43 -0
  77. package/src/providers/anthropic.ts +23 -0
  78. package/src/providers/gemini.ts +136 -0
  79. package/src/providers/glm.ts +60 -0
  80. package/src/providers/ollama.ts +146 -0
  81. package/src/providers/openrouter.ts +42 -0
  82. package/src/verify.ts +258 -0
  83. package/tsconfig.json +19 -0
package/src/models.ts ADDED
@@ -0,0 +1,366 @@
1
+ export interface Model {
2
+ id: string;
3
+ name: string;
4
+ contextWindow: number; // in tokens
5
+ capabilities: string[];
6
+ description: string;
7
+ }
8
+
9
+ export interface Provider {
10
+ id: string;
11
+ name: string;
12
+ endpoint?: string;
13
+ models: Model[];
14
+ }
15
+
16
+ export interface ModelTierMap {
17
+ opus: string;
18
+ sonnet: string;
19
+ haiku: string;
20
+ }
21
+
22
+ // Default GLM tier map per Z.AI docs (https://docs.z.ai/devpack/latest-model)
23
+ // GLM-5.2 with 1M context leads opus, with the fast turbo models filling sonnet/haiku
24
+ export const GLM_DEFAULT_TIER_MAP: ModelTierMap = {
25
+ opus: "glm-5.2[1m]",
26
+ sonnet: "glm-5-turbo",
27
+ haiku: "glm-5v-turbo"
28
+ };
29
+
30
+ // Default OpenRouter tier map
31
+ export const OPENROUTER_DEFAULT_TIER_MAP: ModelTierMap = {
32
+ opus: "qwen/qwen3.6-plus:free",
33
+ sonnet: "openrouter/free",
34
+ haiku: "openrouter/free"
35
+ };
36
+
37
+ // Default Ollama tier map (via LiteLLM proxy on port 4000)
38
+ export const OLLAMA_DEFAULT_TIER_MAP: ModelTierMap = {
39
+ opus: "deepseek-r1:latest",
40
+ sonnet: "qwen2.5-coder:latest",
41
+ haiku: "llama3.1:latest"
42
+ };
43
+
44
+ // Default Gemini tier map (via LiteLLM proxy on port 4001)
45
+ export const GEMINI_DEFAULT_TIER_MAP: ModelTierMap = {
46
+ opus: "gemini-2.5-pro",
47
+ sonnet: "gemini-2.5-flash",
48
+ haiku: "gemini-2.5-flash-lite"
49
+ };
50
+
51
+ // For Alibaba: tier mapping based on model capabilities
52
+ // Default mapping when no specific model chosen: Opus = qwen3.7-plus, Sonnet = qwen3.6-plus, Haiku = kimi-k2.5
53
+ // When specific model selected: Opus = selected model, Sonnet = qwen3.7-plus, Haiku = qwen3.6-plus
54
+ export function getAlibabaTierMap(model: string): ModelTierMap {
55
+ // Use custom defaults when using the default model
56
+ if (model === "qwen3.7-plus") {
57
+ return {
58
+ opus: "qwen3.7-plus",
59
+ sonnet: "qwen3.6-plus",
60
+ haiku: "kimi-k2.5"
61
+ };
62
+ } else {
63
+ // For other specific models, use the selected model as opus
64
+ return {
65
+ opus: model,
66
+ sonnet: "qwen3.7-plus",
67
+ haiku: "qwen3.6-plus"
68
+ };
69
+ }
70
+ }
71
+
72
+ // Format context window for display
73
+ export function formatContext(tokens: number): string {
74
+ if (tokens >= 1000000) {
75
+ return `${(tokens / 1000000).toFixed(0)}M tokens`;
76
+ } else if (tokens >= 1000) {
77
+ return `${(tokens / 1000).toFixed(0)}K tokens`;
78
+ }
79
+ return `${tokens} tokens`;
80
+ }
81
+
82
+ // Alibaba Coding Plan Models
83
+ export const alibabaModels: Model[] = [
84
+ {
85
+ id: "qwen3.7-plus",
86
+ name: "Qwen3.7-Plus",
87
+ contextWindow: 1000000,
88
+ capabilities: ["Text Generation", "Deep Thinking", "Visual Understanding"],
89
+ description: "Most capable Qwen model with balanced performance, speed, and cost. Supports thinking/non-thinking modes, visual understanding, and a 1M context window."
90
+ },
91
+ {
92
+ id: "qwen3.6-plus",
93
+ name: "Qwen3.6-Plus",
94
+ contextWindow: 1000000,
95
+ capabilities: ["Text Generation", "Deep Thinking", "Visual Understanding"],
96
+ description: "Balanced performance, speed, and cost. Supports thinking/non-thinking modes with 1M context window."
97
+ },
98
+ {
99
+ id: "qwen3-max-2026-01-23",
100
+ name: "Qwen3-Max (2026-01-23)",
101
+ contextWindow: 262144,
102
+ capabilities: ["Text Generation", "Deep Thinking"],
103
+ description: "Most capable model for complex, multi-step tasks with enhanced reasoning."
104
+ },
105
+ {
106
+ id: "qwen3-coder-next",
107
+ name: "Qwen3-Coder-Next",
108
+ contextWindow: 262144,
109
+ capabilities: ["Text Generation", "Coding Agent"],
110
+ description: "Next-generation coding model with advanced Coding Agent capabilities, tool calling, and autonomous programming."
111
+ },
112
+ {
113
+ id: "qwen3-coder-plus",
114
+ name: "Qwen3-Coder-Plus",
115
+ contextWindow: 1000000,
116
+ capabilities: ["Text Generation", "Coding"],
117
+ description: "Latest code generation model with Coding Agent support, tool calling, and autonomous programming capabilities. 1M context for large codebases."
118
+ },
119
+ {
120
+ id: "glm-5",
121
+ name: "GLM-5",
122
+ contextWindow: 200000,
123
+ capabilities: ["Text Generation", "Deep Thinking"],
124
+ description: "Zhipu's flagship model with enhanced reasoning and deep thinking capabilities."
125
+ },
126
+ {
127
+ id: "glm-4.7",
128
+ name: "GLM-4.7",
129
+ contextWindow: 256000,
130
+ capabilities: ["Text Generation", "Deep Thinking"],
131
+ description: "Zhipu's balanced model with strong reasoning and code understanding."
132
+ },
133
+ {
134
+ id: "glm-4.7-flash",
135
+ name: "GLM-4.7-Flash",
136
+ contextWindow: 256000,
137
+ capabilities: ["Text Generation", "Fast Inference"],
138
+ description: "Zhipu's fast inference model optimized for speed while maintaining quality."
139
+ },
140
+ {
141
+ id: "kimi-k2.5",
142
+ name: "Kimi K2.5",
143
+ contextWindow: 200000,
144
+ capabilities: ["Text Generation", "Deep Thinking", "Visual Understanding"],
145
+ description: "Moonshot AI's Kimi model with 200K context and multimodal capabilities."
146
+ },
147
+ {
148
+ id: "MiniMax-M2.5",
149
+ name: "MiniMax-M2.5",
150
+ contextWindow: 200000,
151
+ capabilities: ["Text Generation", "Deep Thinking"],
152
+ description: "MiniMax's advanced model with strong reasoning and generation capabilities."
153
+ }
154
+ ];
155
+
156
+ // GLM/Z.AI Models (via coding-helper)
157
+ export const glmModels: Model[] = [
158
+ {
159
+ id: "glm-5.2[1m]",
160
+ name: "GLM-5.2 (1M Context)",
161
+ contextWindow: 1000000,
162
+ capabilities: ["Text Generation", "Deep Thinking"],
163
+ description: "Zhipu's latest flagship model with state-of-the-art reasoning and a 1M context window. Recommended by Z.AI for the opus tier."
164
+ },
165
+ {
166
+ id: "glm-5v-turbo",
167
+ name: "GLM-5V-Turbo",
168
+ contextWindow: 200000,
169
+ capabilities: ["Text Generation", "Deep Thinking", "Visual Understanding", "Visual Programming"],
170
+ description: "Zhipu's first multimodal coding foundation model. Natively understands images, videos, design drafts, and screenshots for visual programming."
171
+ },
172
+ {
173
+ id: "glm-5-turbo",
174
+ name: "GLM-5-Turbo",
175
+ contextWindow: 200000,
176
+ capabilities: ["Text Generation", "Deep Thinking", "Fast Responses"],
177
+ description: "Zhipu's fast turbo model combining strong reasoning with low latency."
178
+ },
179
+ {
180
+ id: "glm-5.1",
181
+ name: "GLM-5.1",
182
+ contextWindow: 200000,
183
+ capabilities: ["Text Generation", "Deep Thinking"],
184
+ description: "Zhipu's GLM-5.1 model with enhanced reasoning and deep thinking capabilities."
185
+ },
186
+ {
187
+ id: "glm-4.7",
188
+ name: "GLM-4.7",
189
+ contextWindow: 256000,
190
+ capabilities: ["Text Generation", "Deep Thinking"],
191
+ description: "Zhipu's balanced model with strong reasoning and code understanding."
192
+ },
193
+ {
194
+ id: "glm-4.7-flash",
195
+ name: "GLM-4.7-Flash",
196
+ contextWindow: 256000,
197
+ capabilities: ["Text Generation", "Fast Inference"],
198
+ description: "Zhipu's fast inference model optimized for speed while maintaining quality."
199
+ }
200
+ ];
201
+
202
+ // OpenRouter Models
203
+ export const openrouterModels: Model[] = [
204
+ {
205
+ id: "qwen/qwen3.6-plus:free",
206
+ name: "Qwen3.6 Plus (Free)",
207
+ contextWindow: 131072,
208
+ capabilities: ["Text Generation", "Deep Thinking"],
209
+ description: "Free Qwen3.6 Plus model via OpenRouter with strong reasoning capabilities."
210
+ },
211
+ {
212
+ id: "openrouter/free",
213
+ name: "OpenRouter Free",
214
+ contextWindow: 131072,
215
+ capabilities: ["Text Generation"],
216
+ description: "OpenRouter's free tier model for basic usage."
217
+ }
218
+ ];
219
+
220
+ // Ollama Models (local, via LiteLLM proxy)
221
+ export const ollamaModels: Model[] = [
222
+ {
223
+ id: "deepseek-r1:latest",
224
+ name: "DeepSeek R1",
225
+ contextWindow: 128000,
226
+ capabilities: ["Text Generation", "Deep Thinking", "Reasoning"],
227
+ description: "DeepSeek's reasoning model with deep thinking capabilities for complex problem-solving."
228
+ },
229
+ {
230
+ id: "qwen2.5-coder:latest",
231
+ name: "Qwen 2.5 Coder",
232
+ contextWindow: 128000,
233
+ capabilities: ["Text Generation", "Coding", "Tool Calling"],
234
+ description: "Alibaba's code-specialized model with strong coding and tool calling capabilities."
235
+ },
236
+ {
237
+ id: "llama3.1:latest",
238
+ name: "Llama 3.1",
239
+ contextWindow: 128000,
240
+ capabilities: ["Text Generation", "Code", "Vision"],
241
+ description: "Meta's Llama 3.1 with strong general performance, code generation, and vision support."
242
+ },
243
+ {
244
+ id: "codellama:latest",
245
+ name: "Code Llama",
246
+ contextWindow: 100000,
247
+ capabilities: ["Text Generation", "Coding"],
248
+ description: "Meta's code-specialized Llama model optimized for code generation and understanding."
249
+ }
250
+ ];
251
+
252
+ // Gemini Models (via LiteLLM proxy)
253
+ export const geminiModels: Model[] = [
254
+ {
255
+ id: "gemini-2.5-pro",
256
+ name: "Gemini 2.5 Pro",
257
+ contextWindow: 1000000,
258
+ capabilities: ["Text Generation", "Deep Thinking", "Code", "Vision"],
259
+ description: "Google's most capable Gemini model with deep thinking, 1M context, and multimodal support."
260
+ },
261
+ {
262
+ id: "gemini-2.5-flash",
263
+ name: "Gemini 2.5 Flash",
264
+ contextWindow: 1000000,
265
+ capabilities: ["Text Generation", "Fast Responses", "Code"],
266
+ description: "Google's fast Gemini model with 1M context, optimized for speed while maintaining quality."
267
+ },
268
+ {
269
+ id: "gemini-2.5-flash-lite",
270
+ name: "Gemini 2.5 Flash Lite",
271
+ contextWindow: 1000000,
272
+ capabilities: ["Text Generation", "Cost-optimized"],
273
+ description: "Google's cost-optimized Gemini model with 1M context for budget-conscious usage."
274
+ }
275
+ ];
276
+
277
+ // Anthropic Models (default)
278
+ export const anthropicModels: Model[] = [
279
+ {
280
+ id: "claude-opus-4-6-20250205",
281
+ name: "Claude Opus 4.6",
282
+ contextWindow: 200000,
283
+ capabilities: ["Text Generation", "Code", "Vision", "Complex Reasoning"],
284
+ description: "Anthropic's most powerful model for complex tasks requiring deep expertise."
285
+ },
286
+ {
287
+ id: "claude-opus-4-5-20251101",
288
+ name: "Claude Opus 4.5",
289
+ contextWindow: 200000,
290
+ capabilities: ["Text Generation", "Code", "Vision", "Complex Reasoning"],
291
+ description: "Previous generation Opus model with excellent performance."
292
+ },
293
+ {
294
+ id: "claude-sonnet-4-6-20250219",
295
+ name: "Claude Sonnet 4.6",
296
+ contextWindow: 200000,
297
+ capabilities: ["Text Generation", "Code", "Vision"],
298
+ description: "Balanced model offering great performance at lower cost."
299
+ },
300
+ {
301
+ id: "claude-sonnet-4-5-20250814",
302
+ name: "Claude Sonnet 4.5",
303
+ contextWindow: 200000,
304
+ capabilities: ["Text Generation", "Code", "Vision"],
305
+ description: "Previous generation Sonnet model with strong capabilities."
306
+ },
307
+ {
308
+ id: "claude-haiku-4-5-20251015",
309
+ name: "Claude Haiku 4.5",
310
+ contextWindow: 200000,
311
+ capabilities: ["Text Generation", "Fast Responses"],
312
+ description: "Anthropic's fastest model for quick, simple tasks."
313
+ }
314
+ ];
315
+
316
+ // Provider definitions
317
+ export const providers: Record<string, Provider> = {
318
+ anthropic: {
319
+ id: "anthropic",
320
+ name: "Anthropic (Default)",
321
+ models: anthropicModels
322
+ },
323
+ alibaba: {
324
+ id: "alibaba",
325
+ name: "Alibaba Coding Plan",
326
+ endpoint: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic",
327
+ models: alibabaModels
328
+ },
329
+ glm: {
330
+ id: "glm",
331
+ name: "GLM/Z.AI",
332
+ models: glmModels
333
+ },
334
+ openrouter: {
335
+ id: "openrouter",
336
+ name: "OpenRouter",
337
+ endpoint: "https://openrouter.ai/api/v1",
338
+ models: openrouterModels
339
+ },
340
+ ollama: {
341
+ id: "ollama",
342
+ name: "Ollama (Local)",
343
+ endpoint: "http://localhost:4000",
344
+ models: ollamaModels
345
+ },
346
+ gemini: {
347
+ id: "gemini",
348
+ name: "Gemini (Google)",
349
+ endpoint: "http://localhost:4001",
350
+ models: geminiModels
351
+ }
352
+ };
353
+
354
+ // Get model by ID from a provider
355
+ export function getModel(providerId: string, modelId: string): Model | undefined {
356
+ const provider = providers[providerId];
357
+ if (!provider) return undefined;
358
+ return provider.models.find(m => m.id === modelId);
359
+ }
360
+
361
+ // Get all models for a provider
362
+ export function getModels(providerId: string): Model[] {
363
+ const provider = providers[providerId];
364
+ if (!provider) return [];
365
+ return provider.models;
366
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Alibaba Coding Plan Provider Configuration
3
+ *
4
+ * Configures Claude Code and OpenCode to use Alibaba's Coding Plan
5
+ * with Qwen, GLM, Kimi, and MiniMax models via Anthropic-compatible API.
6
+ */
7
+
8
+ import { providers, alibabaModels } from "../models";
9
+
10
+ export const ALIBABA_PROVIDER = providers.alibaba;
11
+
12
+ export interface AlibabaConfig {
13
+ provider: "alibaba";
14
+ apiKey: string;
15
+ model: string;
16
+ endpoint: string;
17
+ }
18
+
19
+ export const ALIBABA_ENDPOINT = "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic";
20
+ export const ALIBABA_VERIFY_URL = "https://coding-intl.dashscope.aliyuncs.com/compatible-mode/v1/models";
21
+
22
+ export function getAlibabaConfig(apiKey: string, model?: string): AlibabaConfig {
23
+ return {
24
+ provider: "alibaba",
25
+ apiKey,
26
+ model: model || "qwen3.7-plus",
27
+ endpoint: ALIBABA_ENDPOINT
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Get available Alibaba models
33
+ */
34
+ export function getAvailableModels() {
35
+ return alibabaModels;
36
+ }
37
+
38
+ /**
39
+ * Find a model by ID
40
+ */
41
+ export function findModel(modelId: string) {
42
+ return alibabaModels.find(m => m.id === modelId);
43
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Anthropic Provider Configuration
3
+ *
4
+ * Sets Claude Code and OpenCode to use native Anthropic models
5
+ * by removing MCP overrides and using default settings.
6
+ */
7
+
8
+ import { providers } from "../models";
9
+
10
+ export const ANTHROPIC_PROVIDER = providers.anthropic;
11
+
12
+ export interface AnthropicConfig {
13
+ provider: "anthropic";
14
+ apiKey?: string;
15
+ model?: string;
16
+ }
17
+
18
+ export function getAnthropicConfig(): AnthropicConfig {
19
+ return {
20
+ provider: "anthropic",
21
+ model: process.env.ANTHROPIC_MODEL || "claude-opus-4-6-20250205"
22
+ };
23
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Gemini Provider Configuration
3
+ *
4
+ * Uses Google Gemini models via a LiteLLM proxy for Anthropic API compatibility.
5
+ * Gemini only speaks OpenAI format, so LiteLLM translates Anthropic Messages API
6
+ * requests into OpenAI Chat Completions format.
7
+ *
8
+ * Prerequisites:
9
+ * - LiteLLM installed (pip install 'litellm[proxy]')
10
+ * - Google API key from https://aistudio.google.com/apikey
11
+ * - LiteLLM proxy running on port 4001
12
+ */
13
+
14
+ import { providers, geminiModels } from "../models";
15
+
16
+ export const GEMINI_PROVIDER = providers.gemini;
17
+
18
+ export interface GeminiConfig {
19
+ provider: "gemini";
20
+ apiKey: string;
21
+ model: string;
22
+ endpoint: string;
23
+ }
24
+
25
+ export const GEMINI_ENDPOINT = "http://localhost:4001";
26
+ export const GEMINI_LITELLM_PORT = 4001;
27
+
28
+ export function getGeminiConfig(apiKey: string, model?: string): GeminiConfig {
29
+ return {
30
+ provider: "gemini",
31
+ apiKey,
32
+ model: model || "gemini-2.5-pro",
33
+ endpoint: GEMINI_ENDPOINT
34
+ };
35
+ }
36
+
37
+ export function getAvailableModels() {
38
+ return geminiModels;
39
+ }
40
+
41
+ export function findModel(modelId: string) {
42
+ return geminiModels.find(m => m.id === modelId);
43
+ }
44
+
45
+ /**
46
+ * Check if litellm is installed
47
+ */
48
+ export async function isLitellmInstalled(): Promise<boolean> {
49
+ try {
50
+ const { platform } = await import("os");
51
+ const { exec } = await import("child_process");
52
+ const { promisify } = await import("util");
53
+ const execAsync = promisify(exec);
54
+
55
+ const cmd = platform() === "win32" ? "where litellm" : "which litellm";
56
+ await execAsync(cmd);
57
+ return true;
58
+ } catch {
59
+ return false;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Validate a Gemini API key by hitting the models endpoint
65
+ */
66
+ export async function isGeminiKeyValid(apiKey: string): Promise<boolean> {
67
+ try {
68
+ const controller = new AbortController();
69
+ const timeout = setTimeout(() => controller.abort(), 5000);
70
+ const resp = await fetch(
71
+ "https://generativelanguage.googleapis.com/v1beta/models",
72
+ { signal: controller.signal, headers: { "x-goog-api-key": apiKey } }
73
+ );
74
+ clearTimeout(timeout);
75
+ return resp.ok;
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Check if LiteLLM proxy is running on the Gemini port
83
+ */
84
+ export async function isLitellmProxyRunning(port: number = GEMINI_LITELLM_PORT): Promise<boolean> {
85
+ try {
86
+ const controller = new AbortController();
87
+ const timeout = setTimeout(() => controller.abort(), 3000);
88
+ const resp = await fetch(`http://localhost:${port}/health`, {
89
+ signal: controller.signal
90
+ });
91
+ clearTimeout(timeout);
92
+ return resp.ok;
93
+ } catch {
94
+ return false;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Start LiteLLM proxy for Gemini as a detached background process
100
+ */
101
+ export async function startGeminiLitellmProxy(
102
+ apiKey: string,
103
+ model: string,
104
+ port: number = GEMINI_LITELLM_PORT
105
+ ): Promise<{ success: boolean; error?: string }> {
106
+ try {
107
+ // Already running?
108
+ if (await isLitellmProxyRunning(port)) {
109
+ return { success: true };
110
+ }
111
+
112
+ const { spawn } = await import("child_process");
113
+ const child = spawn("litellm", ["--model", `gemini/${model}`, "--port", String(port)], {
114
+ detached: true,
115
+ stdio: "ignore",
116
+ shell: true,
117
+ env: { ...process.env, GEMINI_API_KEY: apiKey }
118
+ });
119
+ child.unref();
120
+
121
+ // Poll health endpoint for up to 5 seconds
122
+ for (let i = 0; i < 10; i++) {
123
+ await new Promise(resolve => setTimeout(resolve, 500));
124
+ if (await isLitellmProxyRunning(port)) {
125
+ return { success: true };
126
+ }
127
+ }
128
+
129
+ return { success: false, error: "LiteLLM proxy did not start within 5 seconds" };
130
+ } catch (error) {
131
+ return {
132
+ success: false,
133
+ error: error instanceof Error ? error.message : "Failed to start LiteLLM proxy"
134
+ };
135
+ }
136
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * GLM/Z.AI Provider Configuration
3
+ *
4
+ * Uses coding-helper to manage GLM Coding Plan settings.
5
+ * This provider delegates to the @z_ai/coding-helper package.
6
+ */
7
+
8
+ import { platform } from "os";
9
+ import { providers, glmModels } from "../models";
10
+
11
+ export const GLM_PROVIDER = providers.glm;
12
+
13
+ export interface GLMConfig {
14
+ provider: "glm";
15
+ apiKey?: string;
16
+ model?: string;
17
+ }
18
+
19
+ export function getGLMConfig(): GLMConfig {
20
+ return {
21
+ provider: "glm",
22
+ model: process.env.ZHIPUAI_MODEL || process.env.ZAI_MODEL || "glm-5.1"
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Check if coding-helper is installed
28
+ */
29
+ export async function isCodingHelperInstalled(): Promise<boolean> {
30
+ try {
31
+ const { exec } = await import("child_process");
32
+ const { promisify } = await import("util");
33
+ const execAsync = promisify(exec);
34
+
35
+ const checkCommand = platform() === "win32" ? "where coding-helper" : "which coding-helper";
36
+ await execAsync(checkCommand);
37
+ return true;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Reload GLM configuration into Claude Code using coding-helper
45
+ */
46
+ export async function reloadGLMConfig(): Promise<{ success: boolean; error?: string }> {
47
+ try {
48
+ const { exec } = await import("child_process");
49
+ const { promisify } = await import("util");
50
+ const execAsync = promisify(exec);
51
+
52
+ await execAsync("coding-helper auth reload claude");
53
+ return { success: true };
54
+ } catch (error) {
55
+ return {
56
+ success: false,
57
+ error: error instanceof Error ? error.message : "Failed to reload GLM config"
58
+ };
59
+ }
60
+ }