claude-code-provider-switch 1.1.4 → 1.1.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.
- package/README.md +88 -13
- package/bin/claude-switch.js +78 -37
- package/index.js +19 -13
- package/lib/anthropic.js +3 -5
- package/lib/config.js +491 -36
- package/lib/menu.js +80 -52
- package/lib/ollama.js +3 -5
- package/lib/openrouter.js +2 -4
- package/package.json +12 -10
- package/test/run-tests.js +56 -0
- package/test/test-comprehensive.js +282 -0
- package/test/test-provider-integration.js +392 -0
- package/test/test-validation-errors.js +324 -0
package/README.md
CHANGED
|
@@ -72,13 +72,16 @@ claude-switch show-defaults
|
|
|
72
72
|
|
|
73
73
|
# Clear all defaults
|
|
74
74
|
claude-switch clear-defaults
|
|
75
|
+
|
|
76
|
+
# Manage API keys interactively
|
|
77
|
+
claude-switch api-keys
|
|
75
78
|
```
|
|
76
79
|
|
|
77
80
|
## ⚙️ Configuration
|
|
78
81
|
|
|
79
82
|
### Environment Variables
|
|
80
83
|
|
|
81
|
-
The CLI is designed to create a
|
|
84
|
+
The CLI is designed to create a `~/.claude/.claude-switch-env` (in your home directory) with this syntax:
|
|
82
85
|
|
|
83
86
|
```env
|
|
84
87
|
# API Keys for different providers
|
|
@@ -92,12 +95,69 @@ ANTHROPIC_MODEL=claude-3-5-sonnet-latest
|
|
|
92
95
|
OLLAMA_MODEL=minimax-m2.5:cloud
|
|
93
96
|
|
|
94
97
|
# Default provider and model settings
|
|
95
|
-
DEFAULT_PROVIDER=original
|
|
98
|
+
DEFAULT_PROVIDER=default # Use 'default' to show menu on startup, or set to 'openrouter', 'anthropic', 'ollama', or 'original'
|
|
96
99
|
DEFAULT_MODEL=
|
|
97
100
|
```
|
|
98
101
|
|
|
99
102
|
When you run the CLI for the first time, based on your selection, it will ask for the API keys or Auth Tokens and after this will be saved in the `.env` file for future use.
|
|
100
103
|
|
|
104
|
+
### Global vs Local Configuration
|
|
105
|
+
|
|
106
|
+
The CLI supports two configuration modes:
|
|
107
|
+
|
|
108
|
+
#### Global Configuration (Default)
|
|
109
|
+
|
|
110
|
+
- **Location**: `~/.claude/.claude-switch-env` (in your home directory)
|
|
111
|
+
- **Priority**: Used when no local `.env` file exists
|
|
112
|
+
- **Benefit**: Share configuration across all projects
|
|
113
|
+
- **Creation**: Automatically created on first run when no local `.env` exists
|
|
114
|
+
|
|
115
|
+
#### Local Configuration
|
|
116
|
+
|
|
117
|
+
- **Location**: `.env` file in your project directory
|
|
118
|
+
- **Priority**: Overrides global configuration when present
|
|
119
|
+
- **Benefit**: Project-specific settings
|
|
120
|
+
- **Creation**: Create manually or use "Save Configuration Locally" menu option
|
|
121
|
+
|
|
122
|
+
#### Configuration Display
|
|
123
|
+
|
|
124
|
+
The main menu shows which configuration source is active:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
Configuration: Global (~/.claude/.claude-switch-en)
|
|
128
|
+
Configuration: Local (current_folder/.env)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### Menu Behavior
|
|
132
|
+
|
|
133
|
+
- **No defaults set**: Shows interactive menu for provider selection
|
|
134
|
+
- **Defaults set**: Auto-launches with configured provider
|
|
135
|
+
- **Fresh install**: Always shows menu until you set defaults
|
|
136
|
+
|
|
137
|
+
#### Switching Between Modes
|
|
138
|
+
|
|
139
|
+
- **To Local**: Use "Save Configuration Locally" option from main menu
|
|
140
|
+
- **To Global**: Delete local `.env` file to fall back to global config
|
|
141
|
+
- **Priority**: Local always takes precedence over global when both exist
|
|
142
|
+
|
|
143
|
+
### API Key Management
|
|
144
|
+
|
|
145
|
+
For easier API key management, use the interactive menu:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Launch interactive API key management
|
|
149
|
+
claude-switch api-keys
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Or access it through the main menu (option 6) when you run `claude-switch` without arguments.
|
|
153
|
+
|
|
154
|
+
**Features:**
|
|
155
|
+
|
|
156
|
+
- 🔐 **Secure**: API keys are masked for display (shows only first 4 and last 4 characters)
|
|
157
|
+
- 🎯 **Visual**: Clear status indicators (✅/❌) show which providers have keys configured
|
|
158
|
+
- ⚡ **Interactive**: Arrow key navigation with visual selection indicators
|
|
159
|
+
- 🔄 **Flexible**: Update, remove, or set new API keys interactively
|
|
160
|
+
|
|
101
161
|
### Provider Setup
|
|
102
162
|
|
|
103
163
|
#### OpenRouter
|
|
@@ -137,13 +197,14 @@ When you run the CLI for the first time, based on your selection, it will ask fo
|
|
|
137
197
|
|
|
138
198
|
### Configuration Commands
|
|
139
199
|
|
|
140
|
-
| Command | Description
|
|
141
|
-
| ---------------- |
|
|
142
|
-
| `set-default` | Interactive default setup
|
|
143
|
-
| `show-defaults` | View current defaults
|
|
144
|
-
| `clear-defaults` | Reset all defaults
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
200
|
+
| Command | Description | Example |
|
|
201
|
+
| ---------------- | ----------------------------- | ------------------------------ |
|
|
202
|
+
| `set-default` | Interactive default setup | `claude-switch set-default` |
|
|
203
|
+
| `show-defaults` | View current defaults | `claude-switch show-defaults` |
|
|
204
|
+
| `clear-defaults` | Reset all defaults | `claude-switch clear-defaults` |
|
|
205
|
+
| `api-keys` | Manage API keys interactively | `claude-switch api-keys` |
|
|
206
|
+
| `help` | Show help information | `claude-switch --help` |
|
|
207
|
+
| `version` | Show Version information | `claude-switch --version` |
|
|
147
208
|
|
|
148
209
|
### Aliases
|
|
149
210
|
|
|
@@ -154,7 +215,8 @@ When you run the CLI for the first time, based on your selection, it will ask fo
|
|
|
154
215
|
| `ollama` | `oll` |
|
|
155
216
|
| `original` | `original`, `orig`, `def`, `d` |
|
|
156
217
|
|
|
157
|
-
|
|
218
|
+
|
|
219
|
+
### ⚠️ Important:Clearing Defaults
|
|
158
220
|
|
|
159
221
|
**If the interactive menu doesn't show and Claude Code opens directly with a predefined provider**, you need to clear the default settings:
|
|
160
222
|
|
|
@@ -164,7 +226,7 @@ claude-switch clear-defaults
|
|
|
164
226
|
|
|
165
227
|
### Why This Happens
|
|
166
228
|
|
|
167
|
-
|
|
229
|
+
When you use the "Set as Default" option, the application saves your provider and model choices to provide a faster experience. However, if you want to change providers or access the full menu again, you must clear these defaults first.
|
|
168
230
|
|
|
169
231
|
### What Clearing Defaults Does
|
|
170
232
|
|
|
@@ -197,12 +259,13 @@ Available providers:
|
|
|
197
259
|
3) Anthropic Aliases: (anthropic, ant)
|
|
198
260
|
4) Original Claude Code Aliases: (original, orig, def, d)
|
|
199
261
|
5) Set as Default Aliases: (set-default)
|
|
200
|
-
6)
|
|
262
|
+
6) Manage API Keys Aliases: (api-keys, keys)
|
|
263
|
+
7) Help Aliases: (help, -h, --help)
|
|
201
264
|
|
|
202
265
|
Controls:
|
|
203
266
|
↑/↓ - Navigate
|
|
204
267
|
Enter - Select provider
|
|
205
|
-
1-
|
|
268
|
+
1-7 - Quick select
|
|
206
269
|
ESC - Exit
|
|
207
270
|
```
|
|
208
271
|
|
|
@@ -218,6 +281,9 @@ const {
|
|
|
218
281
|
getDefaultProvider,
|
|
219
282
|
launchOpenRouter,
|
|
220
283
|
launchAnthropic,
|
|
284
|
+
showApiKeyMenu,
|
|
285
|
+
updateApiKey,
|
|
286
|
+
maskApiKey,
|
|
221
287
|
} = require("claude-code-provider-switch");
|
|
222
288
|
|
|
223
289
|
// Set default provider programmatically
|
|
@@ -229,6 +295,15 @@ console.log(`Current provider: ${current}`);
|
|
|
229
295
|
|
|
230
296
|
// Launch specific provider
|
|
231
297
|
await launchOpenRouter(false, [], "gpt-4");
|
|
298
|
+
|
|
299
|
+
// Manage API keys programmatically
|
|
300
|
+
await showApiKeyMenu(); // Show interactive API key menu
|
|
301
|
+
await updateApiKey({
|
|
302
|
+
id: "openrouter",
|
|
303
|
+
name: "OpenRouter",
|
|
304
|
+
envVar: "OPENROUTER_AUTH_TOKEN",
|
|
305
|
+
}); // Update specific API key
|
|
306
|
+
console.log(maskApiKey("sk-1234567890abcdef")); // "sk-1234...cdef"
|
|
232
307
|
```
|
|
233
308
|
|
|
234
309
|
### Development
|
package/bin/claude-switch.js
CHANGED
|
@@ -187,6 +187,18 @@ async function main(forceMenu = false) {
|
|
|
187
187
|
return;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
if (args[0] === "save-local" || args[0] === "save-locally") {
|
|
191
|
+
const { saveConfigurationLocally } = require("../lib/config");
|
|
192
|
+
await saveConfigurationLocally();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (args[0] === "api-keys") {
|
|
197
|
+
const { showApiKeyMenu } = require("../lib/config");
|
|
198
|
+
await showApiKeyMenu();
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
190
202
|
// Handle --version flag
|
|
191
203
|
if (args[0] === "--version" || args[0] === "-v") {
|
|
192
204
|
try {
|
|
@@ -206,7 +218,11 @@ async function main(forceMenu = false) {
|
|
|
206
218
|
const defaultProvider = getDefaultProvider();
|
|
207
219
|
const defaultModel = getDefaultModel();
|
|
208
220
|
|
|
209
|
-
if (
|
|
221
|
+
if (
|
|
222
|
+
defaultProvider &&
|
|
223
|
+
defaultProvider !== null &&
|
|
224
|
+
defaultProvider !== "default"
|
|
225
|
+
) {
|
|
210
226
|
const { log } = require("../lib/config");
|
|
211
227
|
log(
|
|
212
228
|
`Using default: ${defaultProvider}${defaultModel ? ` (${defaultModel})` : ""}`,
|
|
@@ -234,45 +250,70 @@ async function main(forceMenu = false) {
|
|
|
234
250
|
return;
|
|
235
251
|
}
|
|
236
252
|
|
|
237
|
-
// No defaults set, show interactive menu
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
+
// No defaults set, show interactive menu - use loop to prevent recursion issues
|
|
254
|
+
mainLoop: while (true) {
|
|
255
|
+
const selectedProvider = await showProviderMenu();
|
|
256
|
+
|
|
257
|
+
// Handle special menu options
|
|
258
|
+
switch (selectedProvider.id) {
|
|
259
|
+
case "help":
|
|
260
|
+
showUsage();
|
|
261
|
+
return;
|
|
262
|
+
case "set-default":
|
|
263
|
+
await setupDefaults();
|
|
264
|
+
handlePostConfiguration("restart", true);
|
|
265
|
+
return;
|
|
266
|
+
case "show-defaults":
|
|
267
|
+
showDefaults();
|
|
268
|
+
return;
|
|
269
|
+
case "api-keys":
|
|
270
|
+
const { showApiKeyMenu } = require("../lib/config");
|
|
271
|
+
await showApiKeyMenu();
|
|
272
|
+
// Continue the loop to show menu again
|
|
273
|
+
continue mainLoop;
|
|
274
|
+
case "save-local":
|
|
275
|
+
const { saveConfigurationLocally, log } = require("../lib/config");
|
|
276
|
+
await saveConfigurationLocally();
|
|
277
|
+
// Show menu again after saving locally
|
|
278
|
+
log("Press Enter to continue...", "cyan");
|
|
279
|
+
const continueRl = require("readline").createInterface({
|
|
280
|
+
input: process.stdin,
|
|
281
|
+
output: process.stdout,
|
|
282
|
+
});
|
|
283
|
+
await new Promise((resolve) => {
|
|
284
|
+
continueRl.question("", () => {
|
|
285
|
+
continueRl.close();
|
|
286
|
+
resolve();
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
// Continue the loop to show menu again
|
|
290
|
+
continue mainLoop;
|
|
291
|
+
}
|
|
253
292
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
293
|
+
// For provider selection, show model selection
|
|
294
|
+
let selectedModel = null;
|
|
295
|
+
if (selectedProvider.id !== "original") {
|
|
296
|
+
selectedModel = await showModelSelectionForProvider(selectedProvider);
|
|
297
|
+
}
|
|
259
298
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
299
|
+
// Launch the selected provider with the selected model
|
|
300
|
+
switch (selectedProvider.id) {
|
|
301
|
+
case "openrouter":
|
|
302
|
+
await launchOpenRouter(false, [], selectedModel);
|
|
303
|
+
break;
|
|
304
|
+
case "anthropic":
|
|
305
|
+
await launchAnthropic(false, [], selectedModel);
|
|
306
|
+
break;
|
|
307
|
+
case "ollama":
|
|
308
|
+
const modelToUse = selectedModel || getProviderDefaultModel("ollama");
|
|
309
|
+
await launchOllama(false, [], modelToUse);
|
|
310
|
+
break;
|
|
311
|
+
case "original":
|
|
312
|
+
await launchDefault([]);
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
return;
|
|
274
316
|
}
|
|
275
|
-
return;
|
|
276
317
|
}
|
|
277
318
|
|
|
278
319
|
const command = args[0].toLowerCase();
|
package/index.js
CHANGED
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Claude Code Provider Switcher - Main Entry Point
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* This is the main entry point for importing the package as a module.
|
|
5
5
|
* For CLI usage, use the bin/claude-switch.js file.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
module.exports = {
|
|
9
9
|
// Configuration utilities
|
|
10
|
-
...require(
|
|
11
|
-
|
|
10
|
+
...require("./lib/config"),
|
|
11
|
+
|
|
12
12
|
// Provider launchers
|
|
13
|
-
launchOpenRouter: require(
|
|
14
|
-
launchAnthropic: require(
|
|
15
|
-
launchOllama: require(
|
|
16
|
-
launchDefault: require(
|
|
17
|
-
|
|
13
|
+
launchOpenRouter: require("./lib/openrouter").launchOpenRouter,
|
|
14
|
+
launchAnthropic: require("./lib/anthropic").launchAnthropic,
|
|
15
|
+
launchOllama: require("./lib/ollama").launchOllama,
|
|
16
|
+
launchDefault: require("./lib/default").launchDefault,
|
|
17
|
+
|
|
18
18
|
// Menu functions
|
|
19
|
-
showProviderMenu: require(
|
|
20
|
-
showUsage: require(
|
|
21
|
-
showDefaults: require(
|
|
22
|
-
setupDefaults: require(
|
|
23
|
-
showModelSelectionForProvider:
|
|
19
|
+
showProviderMenu: require("./lib/menu").showProviderMenu,
|
|
20
|
+
showUsage: require("./lib/menu").showUsage,
|
|
21
|
+
showDefaults: require("./lib/menu").showDefaults,
|
|
22
|
+
setupDefaults: require("./lib/menu").setupDefaults,
|
|
23
|
+
showModelSelectionForProvider:
|
|
24
|
+
require("./lib/menu").showModelSelectionForProvider,
|
|
25
|
+
|
|
26
|
+
// API key management
|
|
27
|
+
showApiKeyMenu: require("./lib/config").showApiKeyMenu,
|
|
28
|
+
updateApiKey: require("./lib/config").updateApiKey,
|
|
29
|
+
maskApiKey: require("./lib/config").maskApiKey,
|
|
24
30
|
};
|
package/lib/anthropic.js
CHANGED
|
@@ -8,7 +8,7 @@ const {
|
|
|
8
8
|
log,
|
|
9
9
|
loadEnvFile,
|
|
10
10
|
promptForApiKey,
|
|
11
|
-
|
|
11
|
+
updateConfigFile,
|
|
12
12
|
findBestMatchingModel,
|
|
13
13
|
} = require("./config");
|
|
14
14
|
const { modelCache } = require("./cache");
|
|
@@ -151,7 +151,7 @@ async function showModelSelection() {
|
|
|
151
151
|
log("Anthropic API key is required for model selection", "red");
|
|
152
152
|
process.exit(1);
|
|
153
153
|
}
|
|
154
|
-
|
|
154
|
+
updateConfigFile("ANTHROPIC_API_KEY", newApiKey, null);
|
|
155
155
|
apiKey = newApiKey;
|
|
156
156
|
}
|
|
157
157
|
|
|
@@ -436,8 +436,6 @@ async function launchAnthropic(
|
|
|
436
436
|
extraArgs = [],
|
|
437
437
|
directModel = null,
|
|
438
438
|
) {
|
|
439
|
-
log("Launching Claude Code with Anthropic settings...", "green");
|
|
440
|
-
|
|
441
439
|
const envVars = loadEnvFile();
|
|
442
440
|
log(`Loading environment from: ${envVars.envFile}`, "yellow");
|
|
443
441
|
|
|
@@ -448,7 +446,7 @@ async function launchAnthropic(
|
|
|
448
446
|
log("Error: Anthropic API key is required", "red");
|
|
449
447
|
process.exit(1);
|
|
450
448
|
}
|
|
451
|
-
|
|
449
|
+
updateConfigFile("ANTHROPIC_API_KEY", apiKey, null);
|
|
452
450
|
envVars.ANTHROPIC_API_KEY = apiKey;
|
|
453
451
|
}
|
|
454
452
|
|