claude-code-model-switch 2.2.2 → 2.2.3
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/.github/workflows/release.yml +96 -0
- package/README.md +5 -3
- package/README_CN.md +5 -3
- package/argv.js +33 -0
- package/index.js +10 -10
- package/package.json +6 -2
- package/test/argv.test.js +34 -0
- package/test/defaults.test.js +12 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
name: Release npm package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
version:
|
|
7
|
+
description: "Version to publish, for example 2.2.4"
|
|
8
|
+
required: true
|
|
9
|
+
type: string
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: npm-release
|
|
17
|
+
cancel-in-progress: false
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
release:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- name: Check out source
|
|
24
|
+
uses: actions/checkout@v6
|
|
25
|
+
with:
|
|
26
|
+
fetch-depth: 0
|
|
27
|
+
|
|
28
|
+
- name: Set up Node.js
|
|
29
|
+
uses: actions/setup-node@v7
|
|
30
|
+
with:
|
|
31
|
+
node-version: 24
|
|
32
|
+
registry-url: https://registry.npmjs.org
|
|
33
|
+
package-manager-cache: false
|
|
34
|
+
|
|
35
|
+
- name: Validate release version
|
|
36
|
+
env:
|
|
37
|
+
RELEASE_VERSION: ${{ inputs.version }}
|
|
38
|
+
run: |
|
|
39
|
+
if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
|
|
40
|
+
echo "Invalid semantic version: $RELEASE_VERSION"
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
PACKAGE_NAME="$(node -p "require('./package.json').name")"
|
|
45
|
+
if git ls-remote --exit-code --tags origin "refs/tags/v$RELEASE_VERSION" >/dev/null 2>&1; then
|
|
46
|
+
echo "Release tag v$RELEASE_VERSION already exists"
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
if npm view "$PACKAGE_NAME@$RELEASE_VERSION" version --registry=https://registry.npmjs.org >/dev/null 2>&1; then
|
|
51
|
+
echo "npm version $RELEASE_VERSION is already published"
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
CURRENT_VERSION="$(node -p "require('./package.json').version")"
|
|
56
|
+
if [[ "$CURRENT_VERSION" != "$RELEASE_VERSION" ]]; then
|
|
57
|
+
npm version "$RELEASE_VERSION" --no-git-tag-version --dry-run
|
|
58
|
+
else
|
|
59
|
+
echo "Package is already at version $RELEASE_VERSION"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
- name: Install dependencies
|
|
63
|
+
run: npm ci
|
|
64
|
+
|
|
65
|
+
- name: Run tests
|
|
66
|
+
run: npm test
|
|
67
|
+
|
|
68
|
+
- name: Update package version
|
|
69
|
+
env:
|
|
70
|
+
RELEASE_VERSION: ${{ inputs.version }}
|
|
71
|
+
run: |
|
|
72
|
+
CURRENT_VERSION="$(node -p "require('./package.json').version")"
|
|
73
|
+
if [[ "$CURRENT_VERSION" != "$RELEASE_VERSION" ]]; then
|
|
74
|
+
npm version "$RELEASE_VERSION" --no-git-tag-version
|
|
75
|
+
else
|
|
76
|
+
echo "Package is already at version $RELEASE_VERSION"
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
- name: Publish package
|
|
80
|
+
run: npm publish --access public
|
|
81
|
+
|
|
82
|
+
- name: Commit and push release
|
|
83
|
+
env:
|
|
84
|
+
RELEASE_VERSION: ${{ inputs.version }}
|
|
85
|
+
BRANCH_NAME: ${{ github.ref_name }}
|
|
86
|
+
run: |
|
|
87
|
+
git config user.name "github-actions[bot]"
|
|
88
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
89
|
+
if ! git diff --quiet -- package.json package-lock.json; then
|
|
90
|
+
git add package.json package-lock.json
|
|
91
|
+
git commit -m "chore(release): $RELEASE_VERSION"
|
|
92
|
+
else
|
|
93
|
+
echo "Package version files are already up to date"
|
|
94
|
+
fi
|
|
95
|
+
git tag "v$RELEASE_VERSION"
|
|
96
|
+
git push origin "HEAD:$BRANCH_NAME" "v$RELEASE_VERSION"
|
package/README.md
CHANGED
|
@@ -95,7 +95,8 @@ Edit the configuration file `~/.claude-code-model-switch/settings.json`:
|
|
|
95
95
|
| `ANTHROPIC_DEFAULT_SONNET_MODEL` | ❌ | Same as `ANTHROPIC_MODEL` | Default Sonnet model |
|
|
96
96
|
| `ANTHROPIC_DEFAULT_OPUS_MODEL` | ❌ | Same as `ANTHROPIC_MODEL` | Default Opus model |
|
|
97
97
|
| `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | ❌ | `8192` | Maximum output tokens |
|
|
98
|
-
| `
|
|
98
|
+
| `CLAUDE_CODE_ATTRIBUTION_HEADER` | ❌ | `0` | Attribution header setting |
|
|
99
|
+
| `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | ❌ | Not set | Disable non-essential traffic |
|
|
99
100
|
|
|
100
101
|
### Special Rules
|
|
101
102
|
|
|
@@ -104,7 +105,8 @@ Edit the configuration file `~/.claude-code-model-switch/settings.json`:
|
|
|
104
105
|
3. **shared env vars**: variables in `shared` apply to every model; model-specific values override shared ones
|
|
105
106
|
4. **Default Values**:
|
|
106
107
|
- `CLAUDE_CODE_MAX_OUTPUT_TOKENS` defaults to `8192`
|
|
107
|
-
- `
|
|
108
|
+
- `CLAUDE_CODE_ATTRIBUTION_HEADER` defaults to `0`
|
|
109
|
+
- `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` is only set when explicitly configured
|
|
108
110
|
- Unset model fields automatically use the value of `ANTHROPIC_MODEL`
|
|
109
111
|
4. **Configuration File**: Empty configuration file `{"shared": {}, "models": {}}` is automatically created on first run
|
|
110
112
|
5. **Unset Command**: `ccms unset` removes only model-specific variables from `settings.json` and restores official models
|
|
@@ -138,4 +140,4 @@ Issues and Pull Requests are welcome!
|
|
|
138
140
|
|
|
139
141
|
## 🔗 Repository
|
|
140
142
|
|
|
141
|
-
https://github.com/musnows/claude-code-model-switch
|
|
143
|
+
https://github.com/musnows/claude-code-model-switch
|
package/README_CN.md
CHANGED
|
@@ -95,7 +95,8 @@ ccms shared unset API_TIMEOUT_MS
|
|
|
95
95
|
| `ANTHROPIC_DEFAULT_SONNET_MODEL` | ❌ | 同 `ANTHROPIC_MODEL` | 默认 Sonnet 模型 |
|
|
96
96
|
| `ANTHROPIC_DEFAULT_OPUS_MODEL` | ❌ | 同 `ANTHROPIC_MODEL` | 默认 Opus 模型 |
|
|
97
97
|
| `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | ❌ | `8192` | 最大输出令牌数 |
|
|
98
|
-
| `
|
|
98
|
+
| `CLAUDE_CODE_ATTRIBUTION_HEADER` | ❌ | `0` | 归属标识头设置 |
|
|
99
|
+
| `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | ❌ | 不设置 | 禁用非必要流量 |
|
|
99
100
|
|
|
100
101
|
### 特殊规则
|
|
101
102
|
|
|
@@ -104,7 +105,8 @@ ccms shared unset API_TIMEOUT_MS
|
|
|
104
105
|
3. **shared 公共变量**:`shared` 中的环境变量会应用到所有模型;单个模型里同名变量会覆盖 shared
|
|
105
106
|
4. **默认值处理**:
|
|
106
107
|
- `CLAUDE_CODE_MAX_OUTPUT_TOKENS` 默认为 `8192`
|
|
107
|
-
- `
|
|
108
|
+
- `CLAUDE_CODE_ATTRIBUTION_HEADER` 默认为 `0`
|
|
109
|
+
- `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` 仅在显式配置时设置
|
|
108
110
|
- 未设置的模型字段会自动使用 `ANTHROPIC_MODEL` 的值
|
|
109
111
|
4. **配置文件**:首次运行时会自动创建空的配置文件 `{"shared": {}, "models": {}}`
|
|
110
112
|
5. **Unset 命令**:`ccms unset` 只删除 `settings.json` 中的模型相关变量,恢复使用官方模型
|
|
@@ -138,4 +140,4 @@ MIT License
|
|
|
138
140
|
|
|
139
141
|
## 🔗 开源地址
|
|
140
142
|
|
|
141
|
-
https://github.com/musnows/claude-code-model-switch
|
|
143
|
+
https://github.com/musnows/claude-code-model-switch
|
package/argv.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
function findStartModelIndex(args) {
|
|
2
|
+
for (let index = 0; index < args.length - 1; index += 1) {
|
|
3
|
+
if (args[index] === 'start' && args[index + 1] && !args[index + 1].startsWith('-')) {
|
|
4
|
+
return index;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return -1;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function normalizeStartCommandArgs(args) {
|
|
12
|
+
if (args[0] === 'start') {
|
|
13
|
+
return args;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const startIndex = findStartModelIndex(args);
|
|
17
|
+
if (startIndex === -1) {
|
|
18
|
+
return args;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const modelName = args[startIndex + 1];
|
|
22
|
+
return [
|
|
23
|
+
'start',
|
|
24
|
+
modelName,
|
|
25
|
+
...args.slice(0, startIndex),
|
|
26
|
+
...args.slice(startIndex + 2),
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = {
|
|
31
|
+
findStartModelIndex,
|
|
32
|
+
normalizeStartCommandArgs,
|
|
33
|
+
};
|
package/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { spawn } = require('child_process');
|
|
6
6
|
const { program } = require('commander');
|
|
7
|
+
const { normalizeStartCommandArgs } = require('./argv');
|
|
7
8
|
|
|
8
9
|
const CLAUDE_SETTINGS_PATH = path.join(process.env.HOME, '.claude', 'settings.json');
|
|
9
10
|
const MODEL_CONFIG_PATH = path.join(process.env.HOME, '.claude-code-model-switch', 'settings.json');
|
|
@@ -11,7 +12,6 @@ const CLAUDE_BIN = process.env.CLAUDE_BIN || 'claude';
|
|
|
11
12
|
const YOLO_ARGS = ['--dangerously-skip-permissions'];
|
|
12
13
|
|
|
13
14
|
const DEFAULT_MAX_OUTPUT_TOKENS = '8192';
|
|
14
|
-
const DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC = '1';
|
|
15
15
|
const DEFAULT_ATTRIBUTION_HEADER = '0';
|
|
16
16
|
const DEFAULT_MAX_CONTEXT_TOKENS = '200000';
|
|
17
17
|
const DEFAULT_AUTOCOMPACT_PCT_OVERRIDE = '80';
|
|
@@ -225,10 +225,6 @@ function buildModelEnv(modelConfig) {
|
|
|
225
225
|
env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = DEFAULT_MAX_OUTPUT_TOKENS;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
if (!env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC) {
|
|
229
|
-
env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
228
|
return env;
|
|
233
229
|
}
|
|
234
230
|
|
|
@@ -327,10 +323,6 @@ function applyModelConfig(modelConfig) {
|
|
|
327
323
|
settings.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = DEFAULT_MAX_OUTPUT_TOKENS;
|
|
328
324
|
}
|
|
329
325
|
|
|
330
|
-
if (!settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC) {
|
|
331
|
-
settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
326
|
saveClaudeSettings(settings);
|
|
335
327
|
console.log(`Switched to model: ${settings.env.ANTHROPIC_MODEL}`);
|
|
336
328
|
}
|
|
@@ -428,7 +420,7 @@ function listModels() {
|
|
|
428
420
|
program
|
|
429
421
|
.name('ccms')
|
|
430
422
|
.description('Claude Code Model Switch - Manage and launch Claude Code with configured models')
|
|
431
|
-
.version('
|
|
423
|
+
.version(require('./package.json').version);
|
|
432
424
|
|
|
433
425
|
program
|
|
434
426
|
.command('start <model> [claude-args...]')
|
|
@@ -503,6 +495,14 @@ program
|
|
|
503
495
|
process.exit(1);
|
|
504
496
|
});
|
|
505
497
|
|
|
498
|
+
// Multica may place Claude protocol args before the ccms subcommand. Normalize
|
|
499
|
+
// only when a concrete start/model pair exists; otherwise leave argv untouched
|
|
500
|
+
// so Commander keeps its normal unsupported-argument error.
|
|
501
|
+
process.argv = [
|
|
502
|
+
...process.argv.slice(0, 2),
|
|
503
|
+
...normalizeStartCommandArgs(process.argv.slice(2)),
|
|
504
|
+
];
|
|
505
|
+
|
|
506
506
|
program.parse();
|
|
507
507
|
|
|
508
508
|
if (!process.argv.slice(2).length) {
|
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-model-switch",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"description": "Launch Claude Code with preconfigured model environments",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/musnows/claude-code-model-switch"
|
|
8
|
+
},
|
|
5
9
|
"main": "index.js",
|
|
6
10
|
"bin": {
|
|
7
11
|
"ccms": "index.js"
|
|
8
12
|
},
|
|
9
13
|
"scripts": {
|
|
10
|
-
"test": "
|
|
14
|
+
"test": "node --test"
|
|
11
15
|
},
|
|
12
16
|
"keywords": [
|
|
13
17
|
"claude",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const test = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
|
|
4
|
+
const { findStartModelIndex, normalizeStartCommandArgs } = require('../argv');
|
|
5
|
+
|
|
6
|
+
test('finds start and model after protocol arguments', () => {
|
|
7
|
+
const args = ['-p', '--output-format', 'stream-json', 'start', 'dsf'];
|
|
8
|
+
|
|
9
|
+
assert.equal(findStartModelIndex(args), 3);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('moves start and model before protocol arguments', () => {
|
|
13
|
+
const args = ['-p', '--output-format', 'stream-json', 'start', 'dsf'];
|
|
14
|
+
|
|
15
|
+
assert.deepEqual(normalizeStartCommandArgs(args), [
|
|
16
|
+
'start',
|
|
17
|
+
'dsf',
|
|
18
|
+
'-p',
|
|
19
|
+
'--output-format',
|
|
20
|
+
'stream-json',
|
|
21
|
+
]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('preserves the standard start command order', () => {
|
|
25
|
+
const args = ['start', 'glm53flash', '-p', 'hello'];
|
|
26
|
+
|
|
27
|
+
assert.deepEqual(normalizeStartCommandArgs(args), args);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('leaves arguments unchanged when no start model pair exists', () => {
|
|
31
|
+
const args = ['-p', 'hello'];
|
|
32
|
+
|
|
33
|
+
assert.deepEqual(normalizeStartCommandArgs(args), args);
|
|
34
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
|
|
6
|
+
const source = fs.readFileSync(path.join(__dirname, '..', 'index.js'), 'utf8');
|
|
7
|
+
|
|
8
|
+
test('does not assign a default non-essential traffic setting', () => {
|
|
9
|
+
assert.doesNotMatch(source, /DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC/);
|
|
10
|
+
assert.doesNotMatch(source, /env\.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC\s*=/);
|
|
11
|
+
assert.doesNotMatch(source, /settings\.env\.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC\s*=/);
|
|
12
|
+
});
|