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/dist/verify.js ADDED
@@ -0,0 +1,262 @@
1
+ "use strict";
2
+ /**
3
+ * API Key Verification
4
+ *
5
+ * Makes lightweight requests to each provider's API to verify keys are valid.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.maskKey = maskKey;
42
+ exports.verifyAllKeys = verifyAllKeys;
43
+ const TIMEOUT_MS = 5000;
44
+ function maskKey(key) {
45
+ if (key.length <= 8)
46
+ return "****";
47
+ return key.slice(0, 4) + "..." + key.slice(-4);
48
+ }
49
+ async function fetchWithTimeout(url, init) {
50
+ const controller = new AbortController();
51
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
52
+ try {
53
+ return await fetch(url, { ...init, signal: controller.signal });
54
+ }
55
+ finally {
56
+ clearTimeout(timer);
57
+ }
58
+ }
59
+ /**
60
+ * Verify Alibaba Coding Plan API key with a minimal chat completion request.
61
+ */
62
+ async function verifyAlibaba(apiKey) {
63
+ try {
64
+ const res = await fetchWithTimeout("https://dashscope.aliyuncs.com/compatible-mode/v1/models", {
65
+ method: "GET",
66
+ headers: {
67
+ "Authorization": `Bearer ${apiKey}`,
68
+ }
69
+ });
70
+ // 200 OK means key works, 401/403 means invalid
71
+ if (res.ok) {
72
+ return { provider: "alibaba", status: "ok", message: "Key valid" };
73
+ }
74
+ if (res.status === 401 || res.status === 403) {
75
+ return { provider: "alibaba", status: "invalid", message: "Authentication failed" };
76
+ }
77
+ return { provider: "alibaba", status: "error", message: `HTTP ${res.status}` };
78
+ }
79
+ catch {
80
+ return { provider: "alibaba", status: "error", message: "Connection failed" };
81
+ }
82
+ }
83
+ /**
84
+ * Verify OpenRouter API key by listing models.
85
+ */
86
+ async function verifyOpenRouter(apiKey) {
87
+ try {
88
+ const res = await fetchWithTimeout("https://openrouter.ai/api/v1/models", {
89
+ method: "GET",
90
+ headers: {
91
+ "Authorization": `Bearer ${apiKey}`,
92
+ "Content-Type": "application/json"
93
+ }
94
+ });
95
+ if (res.ok) {
96
+ return { provider: "openrouter", status: "ok", message: "Key valid" };
97
+ }
98
+ if (res.status === 401 || res.status === 403) {
99
+ return { provider: "openrouter", status: "invalid", message: "Authentication failed" };
100
+ }
101
+ return { provider: "openrouter", status: "error", message: `HTTP ${res.status}` };
102
+ }
103
+ catch {
104
+ return { provider: "openrouter", status: "error", message: "Connection failed" };
105
+ }
106
+ }
107
+ /**
108
+ * Verify GLM/Z.AI by checking if coding-helper is installed and authenticated.
109
+ * Uses the coding-helper CLI status check rather than a direct API call.
110
+ */
111
+ async function verifyGLM() {
112
+ try {
113
+ const { exec } = await Promise.resolve().then(() => __importStar(require("child_process")));
114
+ const { promisify } = await Promise.resolve().then(() => __importStar(require("util")));
115
+ const execAsync = promisify(exec);
116
+ const { platform } = await Promise.resolve().then(() => __importStar(require("os")));
117
+ const checkCmd = platform() === "win32" ? "where coding-helper" : "which coding-helper";
118
+ try {
119
+ await execAsync(checkCmd);
120
+ }
121
+ catch {
122
+ return { provider: "glm", status: "error", message: "coding-helper not installed" };
123
+ }
124
+ // Try to ping the GLM API — if env vars are set, the auth should work
125
+ const glmModel = process.env.ZHIPUAI_MODEL || process.env.ZAI_MODEL;
126
+ if (glmModel) {
127
+ return { provider: "glm", status: "ok", message: "coding-helper installed, env vars set" };
128
+ }
129
+ return { provider: "glm", status: "ok", message: "coding-helper installed" };
130
+ }
131
+ catch {
132
+ return { provider: "glm", status: "error", message: "Check failed" };
133
+ }
134
+ }
135
+ /**
136
+ * Verify Anthropic API key (optional — uses ANTHROPIC_API_KEY env var).
137
+ */
138
+ async function verifyAnthropic(apiKey) {
139
+ try {
140
+ const res = await fetchWithTimeout("https://api.anthropic.com/v1/models", {
141
+ method: "GET",
142
+ headers: {
143
+ "x-api-key": apiKey,
144
+ "anthropic-version": "2023-06-01",
145
+ "Content-Type": "application/json"
146
+ }
147
+ });
148
+ if (res.ok) {
149
+ return { provider: "anthropic", status: "ok", message: "Key valid" };
150
+ }
151
+ if (res.status === 401 || res.status === 403) {
152
+ return { provider: "anthropic", status: "invalid", message: "Authentication failed" };
153
+ }
154
+ return { provider: "anthropic", status: "error", message: `HTTP ${res.status}` };
155
+ }
156
+ catch {
157
+ return { provider: "anthropic", status: "error", message: "Connection failed" };
158
+ }
159
+ }
160
+ /**
161
+ * Verify all configured API keys in parallel.
162
+ */
163
+ async function verifyAllKeys(keys) {
164
+ const checks = [];
165
+ if (keys.alibaba) {
166
+ checks.push(verifyAlibaba(keys.alibaba));
167
+ }
168
+ else {
169
+ checks.push(Promise.resolve({ provider: "alibaba", status: "missing" }));
170
+ }
171
+ if (keys.openrouter) {
172
+ checks.push(verifyOpenRouter(keys.openrouter));
173
+ }
174
+ else {
175
+ checks.push(Promise.resolve({ provider: "openrouter", status: "missing" }));
176
+ }
177
+ if (keys.anthropic) {
178
+ checks.push(verifyAnthropic(keys.anthropic));
179
+ }
180
+ else {
181
+ checks.push(Promise.resolve({ provider: "anthropic", status: "missing" }));
182
+ }
183
+ if (keys.checkGLM) {
184
+ checks.push(verifyGLM());
185
+ }
186
+ else {
187
+ checks.push(Promise.resolve({ provider: "glm", status: "skipped" }));
188
+ }
189
+ if (keys.checkOllama) {
190
+ checks.push(verifyOllama());
191
+ }
192
+ else {
193
+ checks.push(Promise.resolve({ provider: "ollama", status: "skipped" }));
194
+ }
195
+ if (keys.gemini) {
196
+ checks.push(verifyGemini(keys.gemini));
197
+ }
198
+ else {
199
+ checks.push(Promise.resolve({ provider: "gemini", status: "missing" }));
200
+ }
201
+ return Promise.all(checks);
202
+ }
203
+ /**
204
+ * Verify Ollama by checking LiteLLM proxy and Ollama service.
205
+ */
206
+ async function verifyOllama() {
207
+ try {
208
+ // Check LiteLLM proxy on port 4000
209
+ try {
210
+ const res = await fetchWithTimeout("http://localhost:4000/health", { method: "GET" });
211
+ if (!res.ok) {
212
+ return { provider: "ollama", status: "error", message: "LiteLLM proxy not healthy" };
213
+ }
214
+ }
215
+ catch {
216
+ return { provider: "ollama", status: "error", message: "LiteLLM proxy not running on port 4000" };
217
+ }
218
+ // Check Ollama on port 11434
219
+ try {
220
+ const res = await fetchWithTimeout("http://localhost:11434/api/tags", { method: "GET" });
221
+ if (res.ok) {
222
+ return { provider: "ollama", status: "ok", message: "Ollama + LiteLLM proxy running" };
223
+ }
224
+ return { provider: "ollama", status: "error", message: "Ollama not responding" };
225
+ }
226
+ catch {
227
+ return { provider: "ollama", status: "error", message: "Ollama not running on port 11434" };
228
+ }
229
+ }
230
+ catch {
231
+ return { provider: "ollama", status: "error", message: "Check failed" };
232
+ }
233
+ }
234
+ /**
235
+ * Verify Gemini API key and LiteLLM proxy.
236
+ */
237
+ async function verifyGemini(apiKey) {
238
+ // Verify Gemini API key independently of proxy status
239
+ try {
240
+ const res = await fetchWithTimeout("https://generativelanguage.googleapis.com/v1beta/models", { method: "GET", headers: { "x-goog-api-key": apiKey } });
241
+ if (res.ok) {
242
+ // Key is valid — also check proxy status for informational purposes
243
+ let proxyMsg = "";
244
+ try {
245
+ const proxyRes = await fetchWithTimeout("http://localhost:4001/health", { method: "GET" });
246
+ proxyMsg = proxyRes.ok ? ", proxy running" : ", proxy not running";
247
+ }
248
+ catch {
249
+ proxyMsg = ", proxy not running";
250
+ }
251
+ return { provider: "gemini", status: "ok", message: `Key valid${proxyMsg}` };
252
+ }
253
+ if (res.status === 400 || res.status === 401 || res.status === 403) {
254
+ return { provider: "gemini", status: "invalid", message: "Authentication failed" };
255
+ }
256
+ return { provider: "gemini", status: "error", message: `HTTP ${res.status}` };
257
+ }
258
+ catch {
259
+ return { provider: "gemini", status: "error", message: "Connection failed" };
260
+ }
261
+ }
262
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "claude-ai-switcher",
3
+ "version": "1.1.4",
4
+ "description": "Switch between AI providers (Anthropic, GLM, Alibaba Qwen, OpenRouter, Ollama, Gemini) for Claude Code and OpenCode",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "claude-switch": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist/",
11
+ "src/",
12
+ "scripts/",
13
+ "tsconfig.json",
14
+ "AGENTS.md",
15
+ "ARCHITECTURE.md",
16
+ "CLAUDE.md",
17
+ "QWEN.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc && npm run copy-hooks",
21
+ "copy-hooks": "node scripts/copy-hooks.js",
22
+ "start": "node dist/index.js",
23
+ "dev": "ts-node src/index.ts",
24
+ "clean": "rimraf dist"
25
+ },
26
+ "keywords": [
27
+ "claude",
28
+ "ai",
29
+ "qwen",
30
+ "glm",
31
+ "anthropic",
32
+ "alibaba",
33
+ "opencode",
34
+ "openrouter",
35
+ "ollama",
36
+ "gemini",
37
+ "litellm"
38
+ ],
39
+ "author": "Chad Yantorno",
40
+ "license": "MIT",
41
+ "dependencies": {
42
+ "commander": "^11.1.0",
43
+ "fs-extra": "^11.2.0",
44
+ "chalk": "^5.3.0",
45
+ "ora": "^8.0.1"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^20.10.0",
49
+ "@types/fs-extra": "^11.0.4",
50
+ "rimraf": "^5.0.0",
51
+ "typescript": "^5.3.0",
52
+ "ts-node": "^10.9.2"
53
+ },
54
+ "engines": {
55
+ "node": ">=18.0.0"
56
+ }
57
+ }
@@ -0,0 +1,15 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ const srcDir = path.join(__dirname, '..', 'src', 'hooks');
5
+ const destDir = path.join(__dirname, '..', 'dist', 'hooks');
6
+
7
+ fs.ensureDirSync(destDir);
8
+ fs.copySync(srcDir, destDir, {
9
+ filter: (src) => {
10
+ if (fs.statSync(src).isDirectory()) return true;
11
+ return path.extname(src) === '.js';
12
+ }
13
+ });
14
+
15
+ console.log('Copied hook files to dist/hooks/');
@@ -0,0 +1,340 @@
1
+ /**
2
+ * Claude Code Client Handler
3
+ *
4
+ * Manages ~/.claude/settings.json and ~/.claude.json for Claude Code
5
+ */
6
+
7
+ import * as fs from "fs-extra";
8
+ import * as path from "path";
9
+ import * as os from "os";
10
+ import { ModelTierMap } from "../models";
11
+
12
+ export interface ClaudeSettings {
13
+ mcpServers?: Record<string, any>;
14
+ [key: string]: any;
15
+ }
16
+
17
+ export interface ClaudeJson {
18
+ hasCompletedOnboarding?: boolean;
19
+ [key: string]: any;
20
+ }
21
+
22
+ export interface MCPService {
23
+ type: string;
24
+ command?: string;
25
+ args?: string[];
26
+ env?: Record<string, string>;
27
+ url?: string;
28
+ headers?: Record<string, string>;
29
+ }
30
+
31
+ const CLAUDE_DIR = path.join(os.homedir(), ".claude");
32
+ const SETTINGS_FILE = path.join(CLAUDE_DIR, "settings.json");
33
+ const CLAUDE_JSON = path.join(os.homedir(), ".claude.json");
34
+
35
+ const TIER_ENV_KEYS = {
36
+ opus: "ANTHROPIC_DEFAULT_OPUS_MODEL",
37
+ sonnet: "ANTHROPIC_DEFAULT_SONNET_MODEL",
38
+ haiku: "ANTHROPIC_DEFAULT_HAIKU_MODEL"
39
+ } as const;
40
+
41
+ function applyTierMap(settings: ClaudeSettings, tierMap: ModelTierMap): void {
42
+ settings.env = settings.env || {};
43
+ settings.env[TIER_ENV_KEYS.opus] = tierMap.opus;
44
+ settings.env[TIER_ENV_KEYS.sonnet] = tierMap.sonnet;
45
+ settings.env[TIER_ENV_KEYS.haiku] = tierMap.haiku;
46
+ }
47
+
48
+ function clearTierMap(settings: ClaudeSettings): void {
49
+ if (settings.env) {
50
+ delete settings.env[TIER_ENV_KEYS.opus];
51
+ delete settings.env[TIER_ENV_KEYS.sonnet];
52
+ delete settings.env[TIER_ENV_KEYS.haiku];
53
+ if (Object.keys(settings.env).length === 0) {
54
+ delete settings.env;
55
+ }
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Check if Claude settings file exists
61
+ */
62
+ export function claudeSettingsExists(): boolean {
63
+ return fs.existsSync(SETTINGS_FILE);
64
+ }
65
+
66
+ /**
67
+ * Check if ~/.claude.json exists
68
+ */
69
+ export function claudeJsonExists(): boolean {
70
+ return fs.existsSync(CLAUDE_JSON);
71
+ }
72
+
73
+ /**
74
+ * Read current Claude settings
75
+ */
76
+ export async function readClaudeSettings(): Promise<ClaudeSettings> {
77
+ if (!claudeSettingsExists()) {
78
+ return {};
79
+ }
80
+
81
+ const content = await fs.readFile(SETTINGS_FILE, "utf-8");
82
+ return JSON.parse(content);
83
+ }
84
+
85
+ /**
86
+ * Read ~/.claude.json
87
+ */
88
+ export async function readClaudeJson(): Promise<ClaudeJson> {
89
+ if (!claudeJsonExists()) {
90
+ return {};
91
+ }
92
+
93
+ const content = await fs.readFile(CLAUDE_JSON, "utf-8");
94
+ return JSON.parse(content);
95
+ }
96
+
97
+ /**
98
+ * Write Claude settings with backup
99
+ */
100
+ export async function writeClaudeSettings(settings: ClaudeSettings): Promise<void> {
101
+ // Ensure directory exists
102
+ await fs.ensureDir(CLAUDE_DIR);
103
+
104
+ // Backup existing settings if they exist
105
+ if (claudeSettingsExists()) {
106
+ const backupPath = `${SETTINGS_FILE}.backup.${Date.now()}`;
107
+ await fs.copyFile(SETTINGS_FILE, backupPath);
108
+ }
109
+
110
+ // Write new settings
111
+ await fs.writeFile(SETTINGS_FILE, JSON.stringify(settings, null, 2), "utf-8");
112
+ }
113
+
114
+ /**
115
+ * Write ~/.claude.json with backup
116
+ */
117
+ export async function writeClaudeJson(config: ClaudeJson): Promise<void> {
118
+ // Backup existing file if it exists
119
+ if (claudeJsonExists()) {
120
+ const backupPath = `${CLAUDE_JSON}.backup.${Date.now()}`;
121
+ await fs.copyFile(CLAUDE_JSON, backupPath);
122
+ }
123
+
124
+ // Write new config
125
+ await fs.writeFile(CLAUDE_JSON, JSON.stringify(config, null, 2), "utf-8");
126
+ }
127
+
128
+ /**
129
+ * Ensure hasCompletedOnboarding is set to true in ~/.claude.json
130
+ * This prevents "Unable to connect to Anthropic services" error
131
+ */
132
+ export async function ensureOnboardingComplete(): Promise<void> {
133
+ const config = await readClaudeJson();
134
+ config.hasCompletedOnboarding = true;
135
+ await writeClaudeJson(config);
136
+ }
137
+
138
+ /**
139
+ * Configure Claude Code for Alibaba Coding Plan
140
+ */
141
+ export async function configureAlibaba(apiKey: string, model: string, tierMap: ModelTierMap): Promise<void> {
142
+ await ensureOnboardingComplete();
143
+
144
+ const settings = await readClaudeSettings();
145
+
146
+ settings.env = settings.env || {};
147
+ settings.env["ANTHROPIC_AUTH_TOKEN"] = apiKey;
148
+ settings.env["ANTHROPIC_BASE_URL"] = "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic";
149
+ settings.env["ANTHROPIC_MODEL"] = model;
150
+
151
+ applyTierMap(settings, tierMap);
152
+ await writeClaudeSettings(settings);
153
+ }
154
+
155
+ /**
156
+ * Configure Claude Code for Anthropic (default)
157
+ * Removes MCP overrides and tier map env vars to use native Claude
158
+ */
159
+ export async function configureAnthropic(): Promise<void> {
160
+ await ensureOnboardingComplete();
161
+
162
+ const settings = await readClaudeSettings();
163
+
164
+ if (settings.mcpServers) {
165
+ delete settings.mcpServers["alibaba-coding-plan"];
166
+ delete settings.mcpServers["glm-coding-plan"];
167
+ }
168
+
169
+ // Clear Alibaba env vars
170
+ if (settings.env) {
171
+ delete settings.env["ANTHROPIC_AUTH_TOKEN"];
172
+ delete settings.env["ANTHROPIC_BASE_URL"];
173
+ delete settings.env["ANTHROPIC_MODEL"];
174
+ }
175
+
176
+ clearTierMap(settings);
177
+ await writeClaudeSettings(settings);
178
+ }
179
+
180
+ /**
181
+ * Configure Claude Code for GLM via coding-helper
182
+ * Clears provider-specific env vars (e.g. Alibaba) before applying GLM tier map
183
+ */
184
+ export async function configureGLM(tierMap: ModelTierMap): Promise<void> {
185
+ await ensureOnboardingComplete();
186
+
187
+ const settings = await readClaudeSettings();
188
+
189
+ // Clear other provider env vars (e.g. Alibaba)
190
+ if (settings.env) {
191
+ delete settings.env["ANTHROPIC_AUTH_TOKEN"];
192
+ delete settings.env["ANTHROPIC_BASE_URL"];
193
+ delete settings.env["ANTHROPIC_MODEL"];
194
+ }
195
+
196
+ applyTierMap(settings, tierMap);
197
+ await writeClaudeSettings(settings);
198
+ }
199
+
200
+ /**
201
+ * Configure Claude Code for OpenRouter
202
+ * Sets env vars to route through OpenRouter's Anthropic-compatible API
203
+ */
204
+ export async function configureOpenRouter(apiKey: string, model: string, tierMap: ModelTierMap): Promise<void> {
205
+ await ensureOnboardingComplete();
206
+
207
+ const settings = await readClaudeSettings();
208
+
209
+ settings.env = settings.env || {};
210
+ settings.env["ANTHROPIC_AUTH_TOKEN"] = apiKey;
211
+ settings.env["ANTHROPIC_BASE_URL"] = "https://openrouter.ai/api/v1";
212
+ settings.env["ANTHROPIC_MODEL"] = model;
213
+
214
+ applyTierMap(settings, tierMap);
215
+ await writeClaudeSettings(settings);
216
+ }
217
+
218
+ /**
219
+ * Configure Claude Code for Ollama (via LiteLLM proxy on port 4000)
220
+ */
221
+ export async function configureOllama(model: string, tierMap: ModelTierMap): Promise<void> {
222
+ await ensureOnboardingComplete();
223
+
224
+ const settings = await readClaudeSettings();
225
+
226
+ settings.env = settings.env || {};
227
+ settings.env["ANTHROPIC_AUTH_TOKEN"] = "ollama";
228
+ settings.env["ANTHROPIC_BASE_URL"] = "http://localhost:4000";
229
+ settings.env["ANTHROPIC_MODEL"] = model;
230
+
231
+ applyTierMap(settings, tierMap);
232
+ await writeClaudeSettings(settings);
233
+ }
234
+
235
+ /**
236
+ * Configure Claude Code for Gemini (via LiteLLM proxy on port 4001)
237
+ */
238
+ export async function configureGemini(apiKey: string, model: string, tierMap: ModelTierMap): Promise<void> {
239
+ await ensureOnboardingComplete();
240
+
241
+ const settings = await readClaudeSettings();
242
+
243
+ settings.env = settings.env || {};
244
+ settings.env["ANTHROPIC_AUTH_TOKEN"] = apiKey;
245
+ settings.env["ANTHROPIC_BASE_URL"] = "http://localhost:4001";
246
+ settings.env["ANTHROPIC_MODEL"] = model;
247
+
248
+ applyTierMap(settings, tierMap);
249
+ await writeClaudeSettings(settings);
250
+ }
251
+
252
+ /**
253
+ * Get current provider from Claude settings
254
+ */
255
+ export async function getCurrentProvider(): Promise<{
256
+ provider: string;
257
+ model?: string;
258
+ endpoint?: string;
259
+ tierMap?: { opus?: string; sonnet?: string; haiku?: string };
260
+ } | null> {
261
+ if (!claudeSettingsExists()) {
262
+ return { provider: "anthropic" };
263
+ }
264
+
265
+ const settings = await readClaudeSettings();
266
+
267
+ const tierMap = settings.env ? {
268
+ opus: settings.env[TIER_ENV_KEYS.opus],
269
+ sonnet: settings.env[TIER_ENV_KEYS.sonnet],
270
+ haiku: settings.env[TIER_ENV_KEYS.haiku]
271
+ } : undefined;
272
+
273
+ // Check for Alibaba via env vars
274
+ if (settings.env?.["ANTHROPIC_BASE_URL"]?.includes("coding-intl.dashscope.aliyuncs.com")) {
275
+ return {
276
+ provider: "alibaba",
277
+ model: settings.env["ANTHROPIC_MODEL"],
278
+ endpoint: settings.env["ANTHROPIC_BASE_URL"],
279
+ tierMap
280
+ };
281
+ }
282
+
283
+ // Check for OpenRouter via env vars
284
+ if (settings.env?.["ANTHROPIC_BASE_URL"]?.includes("openrouter.ai")) {
285
+ return {
286
+ provider: "openrouter",
287
+ model: settings.env["ANTHROPIC_MODEL"],
288
+ endpoint: settings.env["ANTHROPIC_BASE_URL"],
289
+ tierMap
290
+ };
291
+ }
292
+
293
+ // Check for Ollama via LiteLLM proxy on port 4000
294
+ if (settings.env?.["ANTHROPIC_BASE_URL"]?.includes("localhost:4000")) {
295
+ return {
296
+ provider: "ollama",
297
+ model: settings.env["ANTHROPIC_MODEL"],
298
+ endpoint: settings.env["ANTHROPIC_BASE_URL"],
299
+ tierMap
300
+ };
301
+ }
302
+
303
+ // Check for Gemini via LiteLLM proxy on port 4001
304
+ if (settings.env?.["ANTHROPIC_BASE_URL"]?.includes("localhost:4001")) {
305
+ return {
306
+ provider: "gemini",
307
+ model: settings.env["ANTHROPIC_MODEL"],
308
+ endpoint: settings.env["ANTHROPIC_BASE_URL"],
309
+ tierMap
310
+ };
311
+ }
312
+
313
+ if (settings.mcpServers?.["glm-coding-plan"]) {
314
+ return {
315
+ provider: "glm",
316
+ model: settings.mcpServers["glm-coding-plan"].model,
317
+ tierMap
318
+ };
319
+ }
320
+
321
+ // Check for GLM via z.ai endpoint (set by coding-helper auth reload)
322
+ if (settings.env?.["ANTHROPIC_BASE_URL"]?.includes(".z.ai")) {
323
+ return {
324
+ provider: "glm",
325
+ model: settings.env["ANTHROPIC_MODEL"],
326
+ endpoint: settings.env["ANTHROPIC_BASE_URL"],
327
+ tierMap
328
+ };
329
+ }
330
+
331
+ // Check for GLM via tier map env vars (no BASE_URL but tier aliases are set)
332
+ if (!settings.env?.["ANTHROPIC_BASE_URL"] && tierMap?.opus) {
333
+ return {
334
+ provider: "glm",
335
+ tierMap
336
+ };
337
+ }
338
+
339
+ return { provider: "anthropic" };
340
+ }