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 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 `.env` file in your project directory with this syntax:
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 | Example |
141
- | ---------------- | ------------------------- | ------------------------------ |
142
- | `set-default` | Interactive default setup | `claude-switch set-default` |
143
- | `show-defaults` | View current defaults | `claude-switch show-defaults` |
144
- | `clear-defaults` | Reset all defaults | `claude-switch clear-defaults` |
145
- | `help` | Show help information | `claude-switch --help` |
146
- | `version` | Show Version information | `claude-switch --version` |
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
- ## ⚠️ Important: Clearing Default Settings
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
- If you go through the option `set-default`, you are setting a default provider and model and the application remembers your last provider choice to provide a faster experience. However, if you want to change providers or access the full menu again, you must clear these defaults first.
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) Help Aliases: (help, -h, --help)
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-6 - Quick select
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
@@ -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 (defaultProvider && defaultProvider !== "default") {
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
- const selectedProvider = await showProviderMenu();
239
-
240
- // Handle special menu options
241
- switch (selectedProvider.id) {
242
- case "help":
243
- showUsage();
244
- return;
245
- case "set-default":
246
- await setupDefaults();
247
- handlePostConfiguration("restart", true);
248
- return;
249
- case "show-defaults":
250
- showDefaults();
251
- return;
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
- // For provider selection, show model selection
255
- let selectedModel = null;
256
- if (selectedProvider.id !== "original") {
257
- selectedModel = await showModelSelectionForProvider(selectedProvider);
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
- // Launch the selected provider with the selected model
261
- switch (selectedProvider.id) {
262
- case "openrouter":
263
- await launchOpenRouter(false, [], selectedModel);
264
- break;
265
- case "anthropic":
266
- await launchAnthropic(false, [], selectedModel);
267
- break;
268
- case "ollama":
269
- await launchOllama(false, [], selectedModel);
270
- break;
271
- case "original":
272
- await launchDefault([]);
273
- break;
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('./lib/config'),
11
-
10
+ ...require("./lib/config"),
11
+
12
12
  // Provider launchers
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
-
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('./lib/menu').showProviderMenu,
20
- showUsage: require('./lib/menu').showUsage,
21
- showDefaults: require('./lib/menu').showDefaults,
22
- setupDefaults: require('./lib/menu').setupDefaults,
23
- showModelSelectionForProvider: require('./lib/menu').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
- updateEnvFile,
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
- updateEnvFile("ANTHROPIC_API_KEY", newApiKey);
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
- updateEnvFile("ANTHROPIC_API_KEY", apiKey);
449
+ updateConfigFile("ANTHROPIC_API_KEY", apiKey, null);
452
450
  envVars.ANTHROPIC_API_KEY = apiKey;
453
451
  }
454
452