claude-code-model-switch 2.1.0 → 2.2.1

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
@@ -7,6 +7,7 @@ A CLI tool for switching between different AI models in Claude Code.
7
7
  - Launch Claude Code with a preconfigured model in one command (yolo mode)
8
8
  - Support for multiple pre-configured model environments
9
9
  - `start` injects model config as env vars; `config` persists to settings.json
10
+ - `shared` env vars apply to all models (model-specific values override shared)
10
11
  - Automatic handling of model-related environment variables and defaults
11
12
  - Simple command-line interface
12
13
  - `unset` removes model-related entries from settings.json
@@ -39,6 +40,11 @@ ccms config-path
39
40
 
40
41
  # Remove model-related configuration from settings.json
41
42
  ccms unset
43
+
44
+ # Manage shared environment variables for all models
45
+ ccms shared list
46
+ ccms shared set API_TIMEOUT_MS 600000
47
+ ccms shared unset API_TIMEOUT_MS
42
48
  ```
43
49
 
44
50
  ### Model Configuration
@@ -47,6 +53,10 @@ Edit the configuration file `~/.claude-code-model-switch/settings.json`:
47
53
 
48
54
  ```json
49
55
  {
56
+ "shared": {
57
+ "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
58
+ "API_TIMEOUT_MS": "600000"
59
+ },
50
60
  "models": {
51
61
  "deepseek-chat": {
52
62
  "ANTHROPIC_AUTH_TOKEN": "sk-your-token-here",
@@ -91,11 +101,12 @@ Edit the configuration file `~/.claude-code-model-switch/settings.json`:
91
101
 
92
102
  1. **start command**: `ccms start <model>` injects model config as environment variables and launches `claude` with `--dangerously-skip-permissions` (yolo mode)
93
103
  2. **config command**: `ccms config <model>` writes model config to `~/.claude/settings.json` for persistent configuration
94
- 3. **Default Values**:
104
+ 3. **shared env vars**: variables in `shared` apply to every model; model-specific values override shared ones
105
+ 4. **Default Values**:
95
106
  - `CLAUDE_CODE_MAX_OUTPUT_TOKENS` defaults to `8192`
96
107
  - `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` defaults to `1`
97
108
  - Unset model fields automatically use the value of `ANTHROPIC_MODEL`
98
- 4. **Configuration File**: Empty configuration file `{"models": {}}` is automatically created on first run
109
+ 4. **Configuration File**: Empty configuration file `{"shared": {}, "models": {}}` is automatically created on first run
99
110
  5. **Unset Command**: `ccms unset` removes only model-specific variables from `settings.json` and restores official models
100
111
 
101
112
  ## 📁 File Locations
package/README_CN.md CHANGED
@@ -7,6 +7,7 @@
7
7
  - 一条命令启动 Claude Code 并加载指定模型配置(yolo 模式)
8
8
  - 支持多个预配置模型环境
9
9
  - `start` 将模型配置注入环境变量,`config` 可持久化写入 settings.json
10
+ - 支持 `shared` 公共环境变量,所有模型自动继承(模型配置可覆盖)
10
11
  - 自动处理模型相关环境变量及默认值
11
12
  - 命令行界面,操作简单
12
13
  - `unset` 可清除 settings.json 中的模型相关配置
@@ -39,6 +40,11 @@ ccms config-path
39
40
 
40
41
  # 清除 settings.json 中的模型相关配置,恢复使用官方模型
41
42
  ccms unset
43
+
44
+ # 管理所有模型共享的环境变量
45
+ ccms shared list
46
+ ccms shared set API_TIMEOUT_MS 600000
47
+ ccms shared unset API_TIMEOUT_MS
42
48
  ```
43
49
 
44
50
  ### 配置模型
@@ -47,6 +53,10 @@ ccms unset
47
53
 
48
54
  ```json
49
55
  {
56
+ "shared": {
57
+ "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
58
+ "API_TIMEOUT_MS": "600000"
59
+ },
50
60
  "models": {
51
61
  "deepseek-chat": {
52
62
  "ANTHROPIC_AUTH_TOKEN": "sk-your-token-here",
@@ -91,11 +101,12 @@ ccms unset
91
101
 
92
102
  1. **start 命令**:`ccms start <model>` 将模型配置注入环境变量并启动 `claude`,默认附带 `--dangerously-skip-permissions`(yolo 模式)
93
103
  2. **config 命令**:`ccms config <model>` 将模型配置写入 `~/.claude/settings.json`,适合需要持久化配置的场景
94
- 3. **默认值处理**:
104
+ 3. **shared 公共变量**:`shared` 中的环境变量会应用到所有模型;单个模型里同名变量会覆盖 shared
105
+ 4. **默认值处理**:
95
106
  - `CLAUDE_CODE_MAX_OUTPUT_TOKENS` 默认为 `8192`
96
107
  - `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` 默认为 `1`
97
108
  - 未设置的模型字段会自动使用 `ANTHROPIC_MODEL` 的值
98
- 4. **配置文件**:首次运行时会自动创建空的配置文件 `{"models": {}}`
109
+ 4. **配置文件**:首次运行时会自动创建空的配置文件 `{"shared": {}, "models": {}}`
99
110
  5. **Unset 命令**:`ccms unset` 只删除 `settings.json` 中的模型相关变量,恢复使用官方模型
100
111
 
101
112
  ## 📁 文件位置
@@ -1,4 +1,8 @@
1
1
  {
2
+ "shared": {
3
+ "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
4
+ "API_TIMEOUT_MS": "600000"
5
+ },
2
6
  "models": {
3
7
  "deepseek-chat": {
4
8
  "ANTHROPIC_AUTH_TOKEN": "sk-caf695360e7f4b018bb1b308cbf992eb",
@@ -7,8 +11,7 @@
7
11
  "ANTHROPIC_SMALL_FAST_MODEL": "deepseek-chat",
8
12
  "ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-chat",
9
13
  "ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek-chat",
10
- "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "8192",
11
- "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
14
+ "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "8192"
12
15
  },
13
16
  "claude-3-5-sonnet": {
14
17
  "ANTHROPIC_AUTH_TOKEN": "your-anthropic-token-here",
@@ -21,4 +24,4 @@
21
24
  "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "0"
22
25
  }
23
26
  }
24
- }
27
+ }
package/index.js CHANGED
@@ -61,7 +61,7 @@ function loadModelConfig() {
61
61
  ensureConfigDir();
62
62
 
63
63
  if (!fs.existsSync(MODEL_CONFIG_PATH)) {
64
- const defaultConfig = { models: {} };
64
+ const defaultConfig = { shared: {}, models: {} };
65
65
  fs.writeFileSync(MODEL_CONFIG_PATH, JSON.stringify(defaultConfig, null, 2));
66
66
  return defaultConfig;
67
67
  }
@@ -118,9 +118,25 @@ function reportUnknownModel(model) {
118
118
  process.exit(1);
119
119
  }
120
120
 
121
- function getModelConfig(modelName) {
122
- const config = loadModelConfig();
121
+ function saveModelConfig(config) {
122
+ ensureConfigDir();
123
+ try {
124
+ fs.writeFileSync(MODEL_CONFIG_PATH, JSON.stringify(config, null, 2));
125
+ } catch (error) {
126
+ console.error('Error: Failed to save model configuration file');
127
+ process.exit(1);
128
+ }
129
+ }
123
130
 
131
+ function getSharedEnv(config = loadModelConfig()) {
132
+ const shared = config.shared;
133
+ if (!shared || typeof shared !== 'object' || Array.isArray(shared)) {
134
+ return {};
135
+ }
136
+ return shared;
137
+ }
138
+
139
+ function getRawModelConfig(modelName, config = loadModelConfig()) {
124
140
  if (!config.models || !config.models[modelName]) {
125
141
  reportUnknownModel(modelName);
126
142
  }
@@ -136,6 +152,14 @@ function getModelConfig(modelName) {
136
152
  return modelConfig;
137
153
  }
138
154
 
155
+ function mergeModelConfig(modelName) {
156
+ const config = loadModelConfig();
157
+ const shared = getSharedEnv(config);
158
+ const modelConfig = getRawModelConfig(modelName, config);
159
+
160
+ return { ...shared, ...modelConfig };
161
+ }
162
+
139
163
  function normalizeModelConfig(modelConfig) {
140
164
  const normalized = { ...modelConfig };
141
165
 
@@ -154,9 +178,9 @@ function buildModelEnv(modelConfig) {
154
178
  const normalized = normalizeModelConfig(modelConfig);
155
179
  const env = { ...process.env };
156
180
 
157
- for (const key of MODEL_ENV_VARS) {
158
- if (normalized[key] !== undefined) {
159
- env[key] = String(normalized[key]);
181
+ for (const [key, value] of Object.entries(normalized)) {
182
+ if (value !== undefined && value !== null) {
183
+ env[key] = String(value);
160
184
  }
161
185
  }
162
186
 
@@ -234,11 +258,11 @@ function launchClaude(env, claudeArgs = []) {
234
258
  }
235
259
 
236
260
  function startClaudeWithModel(modelName) {
237
- const modelConfig = getModelConfig(modelName);
261
+ const modelConfig = mergeModelConfig(modelName);
238
262
  const env = buildModelEnv(modelConfig);
239
263
  const claudeArgs = buildStartArgs(getStartArgsFromArgv());
240
264
 
241
- console.log(`[ccms] starting claude code with model: ${env.ANTHROPIC_MODEL} (yolo mode)\n`);
265
+ console.log(`[ccms] model: ${env.ANTHROPIC_MODEL}, endpoint: ${env.ANTHROPIC_BASE_URL} (yolo mode)\n`);
242
266
  launchClaude(env, claudeArgs);
243
267
  }
244
268
 
@@ -250,9 +274,9 @@ function applyModelConfig(modelConfig) {
250
274
  settings.env = {};
251
275
  }
252
276
 
253
- for (const key of MODEL_ENV_VARS) {
254
- if (normalized[key] !== undefined) {
255
- settings.env[key] = normalized[key];
277
+ for (const [key, value] of Object.entries(normalized)) {
278
+ if (value !== undefined && value !== null) {
279
+ settings.env[key] = String(value);
256
280
  }
257
281
  }
258
282
 
@@ -276,10 +300,51 @@ function applyModelConfig(modelConfig) {
276
300
  }
277
301
 
278
302
  function configModel(modelName) {
279
- const modelConfig = getModelConfig(modelName);
303
+ const modelConfig = mergeModelConfig(modelName);
280
304
  applyModelConfig(modelConfig);
281
305
  }
282
306
 
307
+ function listSharedEnv() {
308
+ const shared = getSharedEnv();
309
+ const keys = Object.keys(shared);
310
+
311
+ if (keys.length === 0) {
312
+ console.log('No shared environment variables configured');
313
+ return;
314
+ }
315
+
316
+ console.log('Shared environment variables:');
317
+ keys.forEach((key) => {
318
+ console.log(` ${key}=${shared[key]}`);
319
+ });
320
+ }
321
+
322
+ function setSharedEnv(key, value) {
323
+ const config = loadModelConfig();
324
+
325
+ if (!config.shared || typeof config.shared !== 'object' || Array.isArray(config.shared)) {
326
+ config.shared = {};
327
+ }
328
+
329
+ config.shared[key] = value;
330
+ saveModelConfig(config);
331
+ console.log(`[ccms] shared env set: ${key}=${value}`);
332
+ }
333
+
334
+ function unsetSharedEnv(key) {
335
+ const config = loadModelConfig();
336
+ const shared = getSharedEnv(config);
337
+
338
+ if (!Object.prototype.hasOwnProperty.call(shared, key)) {
339
+ console.log(`No shared environment variable found: ${key}`);
340
+ return;
341
+ }
342
+
343
+ delete config.shared[key];
344
+ saveModelConfig(config);
345
+ console.log(`[ccms] shared env removed: ${key}`);
346
+ }
347
+
283
348
  function unsetModelConfig() {
284
349
  const settings = loadClaudeSettings();
285
350
 
@@ -325,7 +390,7 @@ function listModels() {
325
390
  program
326
391
  .name('ccms')
327
392
  .description('Claude Code Model Switch - Manage and launch Claude Code with configured models')
328
- .version('2.1.0');
393
+ .version('2.2.0');
329
394
 
330
395
  program
331
396
  .command('start <model> [claude-args...]')
@@ -365,6 +430,41 @@ program
365
430
  unsetModelConfig();
366
431
  });
367
432
 
433
+ program
434
+ .command('shared')
435
+ .description('Manage shared environment variables for all models')
436
+ .argument('[action]', 'list, set, or unset')
437
+ .argument('[key]', 'Environment variable name')
438
+ .argument('[value]', 'Environment variable value')
439
+ .action((action, key, value) => {
440
+ if (!action || action === 'list') {
441
+ listSharedEnv();
442
+ return;
443
+ }
444
+
445
+ if (action === 'set') {
446
+ if (!key || value === undefined) {
447
+ console.error('Usage: ccms shared set <key> <value>');
448
+ process.exit(1);
449
+ }
450
+ setSharedEnv(key, value);
451
+ return;
452
+ }
453
+
454
+ if (action === 'unset') {
455
+ if (!key) {
456
+ console.error('Usage: ccms shared unset <key>');
457
+ process.exit(1);
458
+ }
459
+ unsetSharedEnv(key);
460
+ return;
461
+ }
462
+
463
+ console.error(`Unknown shared action: ${action}`);
464
+ console.log('Usage: ccms shared [list|set <key> <value>|unset <key>]');
465
+ process.exit(1);
466
+ });
467
+
368
468
  program.parse();
369
469
 
370
470
  if (!process.argv.slice(2).length) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-model-switch",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "description": "Launch Claude Code with preconfigured model environments",
5
5
  "main": "index.js",
6
6
  "bin": {