claude-code-model-switch 1.0.0
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/example-config.json +24 -0
- package/index.js +240 -0
- package/package.json +18 -0
- package/prompt.md +45 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"models": {
|
|
3
|
+
"deepseek-chat": {
|
|
4
|
+
"ANTHROPIC_AUTH_TOKEN": "sk-caf695360e7f4b018bb1b308cbf992eb",
|
|
5
|
+
"ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic",
|
|
6
|
+
"ANTHROPIC_MODEL": "deepseek-chat",
|
|
7
|
+
"ANTHROPIC_SMALL_FAST_MODEL": "deepseek-chat",
|
|
8
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-chat",
|
|
9
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek-chat",
|
|
10
|
+
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": "8192",
|
|
11
|
+
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
|
|
12
|
+
},
|
|
13
|
+
"claude-3-5-sonnet": {
|
|
14
|
+
"ANTHROPIC_AUTH_TOKEN": "your-anthropic-token-here",
|
|
15
|
+
"ANTHROPIC_BASE_URL": "https://api.anthropic.com",
|
|
16
|
+
"ANTHROPIC_MODEL": "claude-3-5-sonnet-20241022",
|
|
17
|
+
"ANTHROPIC_SMALL_FAST_MODEL": "claude-3-haiku-20240307",
|
|
18
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-3-5-sonnet-20241022",
|
|
19
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-3-opus-20240229",
|
|
20
|
+
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": "8192",
|
|
21
|
+
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "0"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { program } = require('commander');
|
|
6
|
+
|
|
7
|
+
// Configuration file paths
|
|
8
|
+
const CLAUDE_SETTINGS_PATH = path.join(process.env.HOME, '.claude', 'settings.json');
|
|
9
|
+
const MODEL_CONFIG_PATH = path.join(process.env.HOME, '.claude-code-model-switch', 'settings.json');
|
|
10
|
+
|
|
11
|
+
// Default values
|
|
12
|
+
const DEFAULT_MAX_OUTPUT_TOKENS = '8192';
|
|
13
|
+
const DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC = '1';
|
|
14
|
+
|
|
15
|
+
// Ensure configuration directory exists
|
|
16
|
+
function ensureConfigDir() {
|
|
17
|
+
const configDir = path.dirname(MODEL_CONFIG_PATH);
|
|
18
|
+
if (!fs.existsSync(configDir)) {
|
|
19
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Load model configuration
|
|
24
|
+
function loadModelConfig() {
|
|
25
|
+
ensureConfigDir();
|
|
26
|
+
|
|
27
|
+
if (!fs.existsSync(MODEL_CONFIG_PATH)) {
|
|
28
|
+
// Create empty default configuration file
|
|
29
|
+
const defaultConfig = { models: {} };
|
|
30
|
+
fs.writeFileSync(MODEL_CONFIG_PATH, JSON.stringify(defaultConfig, null, 2));
|
|
31
|
+
return defaultConfig;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const configContent = fs.readFileSync(MODEL_CONFIG_PATH, 'utf8');
|
|
36
|
+
return JSON.parse(configContent);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error('Error: Failed to parse model configuration file');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Load Claude settings
|
|
44
|
+
function loadClaudeSettings() {
|
|
45
|
+
if (!fs.existsSync(CLAUDE_SETTINGS_PATH)) {
|
|
46
|
+
console.error('Error: Claude settings file not found');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const settingsContent = fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf8');
|
|
52
|
+
return JSON.parse(settingsContent);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error('Error: Failed to parse Claude settings file');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Save Claude settings
|
|
60
|
+
function saveClaudeSettings(settings) {
|
|
61
|
+
try {
|
|
62
|
+
fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error('Error: Failed to save Claude settings file');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Get model configuration
|
|
70
|
+
function getModelConfig(modelName) {
|
|
71
|
+
const config = loadModelConfig();
|
|
72
|
+
|
|
73
|
+
if (!config.models || !config.models[modelName]) {
|
|
74
|
+
console.error(`Error: Model "${modelName}" not found`);
|
|
75
|
+
console.log('Available models:', Object.keys(config.models || {}).join(', ') || 'None');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const modelConfig = config.models[modelName];
|
|
80
|
+
|
|
81
|
+
// Check if model configuration is empty
|
|
82
|
+
if (Object.keys(modelConfig).length === 0) {
|
|
83
|
+
console.error(`Error: Model "${modelName}" configuration is empty`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return modelConfig;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Apply model configuration
|
|
91
|
+
function applyModelConfig(modelConfig) {
|
|
92
|
+
const settings = loadClaudeSettings();
|
|
93
|
+
|
|
94
|
+
// Ensure env object exists
|
|
95
|
+
if (!settings.env) {
|
|
96
|
+
settings.env = {};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Only update model-related environment variables
|
|
100
|
+
const modelEnvVars = [
|
|
101
|
+
'ANTHROPIC_AUTH_TOKEN',
|
|
102
|
+
'ANTHROPIC_BASE_URL',
|
|
103
|
+
'ANTHROPIC_MODEL',
|
|
104
|
+
'ANTHROPIC_SMALL_FAST_MODEL',
|
|
105
|
+
'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
106
|
+
'ANTHROPIC_DEFAULT_OPUS_MODEL',
|
|
107
|
+
'CLAUDE_CODE_MAX_OUTPUT_TOKENS',
|
|
108
|
+
'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC'
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
// Apply configuration
|
|
112
|
+
for (const key of modelEnvVars) {
|
|
113
|
+
if (modelConfig[key] !== undefined) {
|
|
114
|
+
settings.env[key] = modelConfig[key];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Handle default value logic
|
|
119
|
+
// Check required environment variables
|
|
120
|
+
const requiredEnvVars = [
|
|
121
|
+
'ANTHROPIC_MODEL',
|
|
122
|
+
'ANTHROPIC_AUTH_TOKEN',
|
|
123
|
+
'ANTHROPIC_BASE_URL'
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
for (const envVar of requiredEnvVars) {
|
|
127
|
+
if (!settings.env[envVar]) {
|
|
128
|
+
console.error(`Error: ${envVar} is required`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// If other model fields are not set, use ANTHROPIC_MODEL value
|
|
134
|
+
const derivedModelFields = [
|
|
135
|
+
'ANTHROPIC_SMALL_FAST_MODEL',
|
|
136
|
+
'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
137
|
+
'ANTHROPIC_DEFAULT_OPUS_MODEL'
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
for (const field of derivedModelFields) {
|
|
141
|
+
if (!settings.env[field]) {
|
|
142
|
+
settings.env[field] = settings.env.ANTHROPIC_MODEL;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Handle special environment variable defaults
|
|
147
|
+
if (!settings.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS) {
|
|
148
|
+
settings.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = DEFAULT_MAX_OUTPUT_TOKENS;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC) {
|
|
152
|
+
settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
saveClaudeSettings(settings);
|
|
156
|
+
console.log(`✅ Switched to model: ${settings.env.ANTHROPIC_MODEL}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// List all models
|
|
160
|
+
function listModels() {
|
|
161
|
+
const config = loadModelConfig();
|
|
162
|
+
const models = Object.keys(config.models || {});
|
|
163
|
+
|
|
164
|
+
if (models.length === 0) {
|
|
165
|
+
console.log('No models configured');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
console.log('Available models:');
|
|
170
|
+
models.forEach(model => {
|
|
171
|
+
console.log(` - ${model}`);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Check if model name is valid
|
|
176
|
+
function isValidModel(modelName) {
|
|
177
|
+
const config = loadModelConfig();
|
|
178
|
+
return config.models &&
|
|
179
|
+
config.models[modelName] &&
|
|
180
|
+
Object.keys(config.models[modelName]).length > 0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Main program
|
|
184
|
+
program
|
|
185
|
+
.name('ccms')
|
|
186
|
+
.description('Claude Code Model Switch - Switch Claude Code models')
|
|
187
|
+
.version('1.0.0')
|
|
188
|
+
.argument('[model]', 'Model name to switch to')
|
|
189
|
+
.action((model) => {
|
|
190
|
+
if (model) {
|
|
191
|
+
// If model name argument provided, attempt to switch model
|
|
192
|
+
if (isValidModel(model)) {
|
|
193
|
+
const modelConfig = getModelConfig(model);
|
|
194
|
+
applyModelConfig(modelConfig);
|
|
195
|
+
} else {
|
|
196
|
+
const config = loadModelConfig();
|
|
197
|
+
const modelConfig = config.models && config.models[model];
|
|
198
|
+
|
|
199
|
+
if (modelConfig && Object.keys(modelConfig).length === 0) {
|
|
200
|
+
console.error(`Error: Model "${model}" configuration is empty`);
|
|
201
|
+
} else {
|
|
202
|
+
console.error(`Error: Model "${model}" not found`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const validModels = Object.keys(config.models || {}).filter(name =>
|
|
206
|
+
config.models[name] && Object.keys(config.models[name]).length > 0
|
|
207
|
+
);
|
|
208
|
+
console.log('Available models:', validModels.join(', ') || 'None');
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
// If no arguments provided, show help information
|
|
213
|
+
program.help();
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
program
|
|
218
|
+
.command('switch <model>')
|
|
219
|
+
.description('Switch to specified model')
|
|
220
|
+
.action((model) => {
|
|
221
|
+
const modelConfig = getModelConfig(model);
|
|
222
|
+
applyModelConfig(modelConfig);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
program
|
|
226
|
+
.command('list')
|
|
227
|
+
.description('List all available models')
|
|
228
|
+
.action(() => {
|
|
229
|
+
listModels();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
program
|
|
233
|
+
.command('config-path')
|
|
234
|
+
.description('Display configuration file paths')
|
|
235
|
+
.action(() => {
|
|
236
|
+
console.log('Model configuration file path:', MODEL_CONFIG_PATH);
|
|
237
|
+
console.log('Claude settings file path:', CLAUDE_SETTINGS_PATH);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-code-model-switch",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A CLI tool to switch Claude Code models",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ccms": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": ["claude", "code", "model", "switch"],
|
|
13
|
+
"author": "",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"commander": "^11.1.0"
|
|
17
|
+
}
|
|
18
|
+
}
|
package/prompt.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
我需要实现一个claude code的模型切换器,这个切换器用node.js实现,可以通过npm安装.
|
|
2
|
+
|
|
3
|
+
实现逻辑非常简单.读取`~/.claude/settings.json`,切换里面和模型有关的环境变量。除了这些env,禁止修改配置文件的任何其他字段。
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"env": {
|
|
8
|
+
"ANTHROPIC_AUTH_TOKEN": "sk-caf695360e7f4b018bb1b308cbf992eb",
|
|
9
|
+
"ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic",
|
|
10
|
+
"ANTHROPIC_MODEL": "deepseek-chat",
|
|
11
|
+
"ANTHROPIC_SMALL_FAST_MODEL": "deepseek-chat",
|
|
12
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-chat",
|
|
13
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek-chat",
|
|
14
|
+
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": "8192",
|
|
15
|
+
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
这个工具本身通过~/.claude-code-models.json配置多个模型,格式为
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"models":{
|
|
25
|
+
"模型名称":{
|
|
26
|
+
"ANTHROPIC_AUTH_TOKEN": "sk-caf695360e7f4b018bb1b308cbf992eb",
|
|
27
|
+
"ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic",
|
|
28
|
+
"ANTHROPIC_MODEL": "deepseek-chat",
|
|
29
|
+
"ANTHROPIC_SMALL_FAST_MODEL": "deepseek-chat",
|
|
30
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-chat",
|
|
31
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek-chat",
|
|
32
|
+
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": "8192",
|
|
33
|
+
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
当用户执行`ccms 模型`命令时,读取`~/.claude-code-model-swich/settings.json`文件,检查是否有这个模型名称,没有报错,有就进行`~/.claude/settings.json`替换
|
|
40
|
+
|
|
41
|
+
重点:
|
|
42
|
+
1. `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`是一个特殊的环境变量,当用户没有设置这个环境变量时,**默认为1**,当用户设置为0时,表示允许非必要的流量,对应修改成0
|
|
43
|
+
2. `CLAUDE_CODE_MAX_OUTPUT_TOKENS`用户没有设置的时候,默认为8192
|
|
44
|
+
3. ANTHROPIC_SMALL_FAST_MODEL、ANTHROPIC_DEFAULT_SONNET_MODEL、ANTHROPIC_DEFAULT_OPUS_MODEL都可以不设置,只有ANTHROPIC_MODEL是必填的,其余三个环境变量在没有设置的时候,沿用ANTHROPIC_MODEL的值。
|
|
45
|
+
4. `~/.claude-code-model-swich/settings.json`文件不存在的时候,创建一个空的默认配置文件`{"models": {}}`,并按照模型不存在的错误提示用户(认为加载了这个空配置文件)
|