yingclaw 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/README.md +57 -0
- package/bin/cli.js +327 -0
- package/lib/config.js +97 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# yingclaw
|
|
2
|
+
|
|
3
|
+
Claude Code × 国产大模型,一键接入。
|
|
4
|
+
|
|
5
|
+
支持 DeepSeek、阿里云百炼(Qwen)、MiniMax、智谱 GLM、小米 MiMo,无需梯子即可使用 Claude Code。
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g yingclaw
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
安装后会注册一个 `claw` 命令。
|
|
14
|
+
|
|
15
|
+
## 使用步骤
|
|
16
|
+
|
|
17
|
+
**第一步:安装 Claude Code**
|
|
18
|
+
```bash
|
|
19
|
+
claw install-claude
|
|
20
|
+
```
|
|
21
|
+
根据提示选择网络环境(有梯子走官方,没梯子走淘宝镜像)。
|
|
22
|
+
|
|
23
|
+
**第二步:配置国产模型 API**
|
|
24
|
+
```bash
|
|
25
|
+
claw setup
|
|
26
|
+
```
|
|
27
|
+
选择厂商 → 选择模型 → 输入 API Key,自动写入环境变量,配置完成后自动启动 Claude。
|
|
28
|
+
|
|
29
|
+
**第三步:以后直接用**
|
|
30
|
+
```bash
|
|
31
|
+
claude
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 支持的厂商
|
|
35
|
+
|
|
36
|
+
| 厂商 | 模型 |
|
|
37
|
+
|------|------|
|
|
38
|
+
| DeepSeek | V4 Flash、V4 Pro |
|
|
39
|
+
| 阿里云百炼 | Qwen3 Max、Plus、Flash |
|
|
40
|
+
| MiniMax | M2.7、M2.7 Turbo、M2.5 |
|
|
41
|
+
| 智谱 GLM | GLM-4.7、GLM-5.1、GLM-5 Turbo、GLM-4.5 Air |
|
|
42
|
+
| 小米 MiMo | MiMo V2.5 Pro |
|
|
43
|
+
|
|
44
|
+
## 其他命令
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
claw switch # 快速切换模型(无需重新输入 Key)
|
|
48
|
+
claw status # 查看当前配置,验证 Key 是否有效
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 原理
|
|
52
|
+
|
|
53
|
+
各厂商均原生支持 Anthropic API 格式,只需设置 `ANTHROPIC_BASE_URL` 和 `ANTHROPIC_API_KEY` 两个环境变量即可。本工具自动完成配置,无需手动修改任何文件。
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
MIT
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const { select, input, confirm } = require('@inquirer/prompts');
|
|
5
|
+
const { loadConfig, saveConfig, writeEnvToZshrc, PROVIDERS } = require('../lib/config');
|
|
6
|
+
const { execSync, spawn, spawnSync } = require('child_process');
|
|
7
|
+
const https = require('https');
|
|
8
|
+
const pkg = require('../package.json');
|
|
9
|
+
|
|
10
|
+
const program = new Command();
|
|
11
|
+
|
|
12
|
+
async function getBanner() {
|
|
13
|
+
const chalk = (await import('chalk')).default;
|
|
14
|
+
const figlet = require('figlet');
|
|
15
|
+
const boxen = (await import('boxen')).default;
|
|
16
|
+
const title = figlet.textSync('claw', { font: 'Standard', horizontalLayout: 'fitted' });
|
|
17
|
+
return boxen(
|
|
18
|
+
chalk.cyan.bold(title) + '\n' + chalk.dim('Claude Code × 国产大模型 一键接入'),
|
|
19
|
+
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'cyan', margin: { top: 1, bottom: 0 } }
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function validateKey(config) {
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
let url;
|
|
26
|
+
try {
|
|
27
|
+
url = new URL(config.baseUrl + '/v1/messages');
|
|
28
|
+
} catch {
|
|
29
|
+
return resolve(null);
|
|
30
|
+
}
|
|
31
|
+
const body = Buffer.from(JSON.stringify({
|
|
32
|
+
model: config.model,
|
|
33
|
+
max_tokens: 1,
|
|
34
|
+
messages: [{ role: 'user', content: 'hi' }],
|
|
35
|
+
}));
|
|
36
|
+
const req = https.request({
|
|
37
|
+
hostname: url.hostname,
|
|
38
|
+
path: url.pathname,
|
|
39
|
+
method: 'POST',
|
|
40
|
+
timeout: 8000,
|
|
41
|
+
headers: {
|
|
42
|
+
'content-type': 'application/json',
|
|
43
|
+
'x-api-key': config.apiKey,
|
|
44
|
+
'anthropic-version': '2023-06-01',
|
|
45
|
+
'content-length': body.length,
|
|
46
|
+
},
|
|
47
|
+
}, (res) => {
|
|
48
|
+
// 401/403 = Key 无效;2xx/400/500 都说明 Key 被识别了
|
|
49
|
+
if (res.statusCode === 401 || res.statusCode === 403) resolve(false);
|
|
50
|
+
else resolve(true);
|
|
51
|
+
});
|
|
52
|
+
req.on('error', () => resolve(null));
|
|
53
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
54
|
+
req.write(body);
|
|
55
|
+
req.end();
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function showStatus() {
|
|
60
|
+
const chalk = (await import('chalk')).default;
|
|
61
|
+
const boxen = (await import('boxen')).default;
|
|
62
|
+
const ora = (await import('ora')).default;
|
|
63
|
+
const config = loadConfig();
|
|
64
|
+
|
|
65
|
+
if (!config) {
|
|
66
|
+
console.log(chalk.red('\n未配置,请先运行: claw setup\n'));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const provider = PROVIDERS[config.provider];
|
|
71
|
+
const spinner = ora('验证 API Key...').start();
|
|
72
|
+
const valid = await validateKey(config);
|
|
73
|
+
|
|
74
|
+
if (valid === true) spinner.succeed('API Key 有效');
|
|
75
|
+
else if (valid === false) spinner.fail('API Key 无效或已过期');
|
|
76
|
+
else spinner.warn('网络异常,无法验证');
|
|
77
|
+
|
|
78
|
+
const lines = [
|
|
79
|
+
`${chalk.dim('厂商 ')} ${chalk.white.bold(provider?.name || config.provider)}`,
|
|
80
|
+
`${chalk.dim('模型 ')} ${chalk.yellow(config.model)}`,
|
|
81
|
+
`${chalk.dim('API Key ')} ${chalk.dim(config.apiKey.slice(0, 10) + '...')} ${valid === true ? chalk.green('✔') : valid === false ? chalk.red('✘') : chalk.yellow('?')}`,
|
|
82
|
+
`${chalk.dim('Base URL')} ${chalk.cyan(config.baseUrl)}`,
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
console.log(boxen(lines.join('\n'), {
|
|
86
|
+
title: chalk.bold('当前配置'),
|
|
87
|
+
titleAlignment: 'left',
|
|
88
|
+
padding: { top: 0, bottom: 0, left: 2, right: 2 },
|
|
89
|
+
borderStyle: 'round',
|
|
90
|
+
borderColor: valid === true ? 'green' : valid === false ? 'red' : 'yellow',
|
|
91
|
+
margin: { top: 1, bottom: 1 },
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
program
|
|
96
|
+
.name('claw')
|
|
97
|
+
.description('Claude Code × 国产大模型一键接入')
|
|
98
|
+
.version(pkg.version);
|
|
99
|
+
|
|
100
|
+
program
|
|
101
|
+
.command('install-claude')
|
|
102
|
+
.description('安装 Claude Code CLI')
|
|
103
|
+
.action(async () => {
|
|
104
|
+
const chalk = (await import('chalk')).default;
|
|
105
|
+
const ora = (await import('ora')).default;
|
|
106
|
+
const boxen = (await import('boxen')).default;
|
|
107
|
+
|
|
108
|
+
console.log(await getBanner());
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const ver = execSync('claude --version 2>/dev/null', { encoding: 'utf8' }).trim();
|
|
112
|
+
console.log(chalk.green(`\n✔ Claude Code 已安装:${ver}\n`));
|
|
113
|
+
const yes = await confirm({ message: '是否重新安装/更新?', default: false });
|
|
114
|
+
if (!yes) return;
|
|
115
|
+
} catch {}
|
|
116
|
+
|
|
117
|
+
const network = await select({
|
|
118
|
+
message: chalk.cyan('你的网络环境'),
|
|
119
|
+
choices: [
|
|
120
|
+
{ name: '有梯子 / 海外网络(走官方)', value: 'vpn' },
|
|
121
|
+
{ name: '国内网络 / 没有梯子(走镜像)', value: 'cn' },
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const cmd = network === 'vpn'
|
|
126
|
+
? 'npm install -g @anthropic-ai/claude-code'
|
|
127
|
+
: 'npm install -g @anthropic-ai/claude-code --registry=https://registry.npmmirror.com';
|
|
128
|
+
|
|
129
|
+
console.log(chalk.dim('\n安装中,实时输出:\n'));
|
|
130
|
+
|
|
131
|
+
// 实时输出安装日志
|
|
132
|
+
const result = spawnSync(cmd, { shell: true, stdio: 'inherit' });
|
|
133
|
+
|
|
134
|
+
if (result.status === 0) {
|
|
135
|
+
console.log(chalk.green('\n✔ Claude Code 安装成功!'));
|
|
136
|
+
} else {
|
|
137
|
+
console.log(chalk.red('\n✘ 安装失败'));
|
|
138
|
+
console.log(boxen(
|
|
139
|
+
chalk.bold('手动安装:\n\n') +
|
|
140
|
+
chalk.cyan('npm config set registry https://registry.npmmirror.com\n') +
|
|
141
|
+
chalk.cyan('npm install -g @anthropic-ai/claude-code'),
|
|
142
|
+
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'yellow', margin: { top: 1, bottom: 1 } }
|
|
143
|
+
));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const ver = execSync('claude --version', { encoding: 'utf8' }).trim();
|
|
149
|
+
console.log(chalk.green(`✔ 验证成功:${ver}`));
|
|
150
|
+
} catch {
|
|
151
|
+
console.log(chalk.yellow('安装完成,请重开终端后运行 claude'));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
console.log(boxen(
|
|
155
|
+
chalk.bold('下一步\n\n') +
|
|
156
|
+
chalk.cyan(' claw setup') + chalk.dim(' 配置国产模型 API Key'),
|
|
157
|
+
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'cyan', margin: { top: 1, bottom: 1 } }
|
|
158
|
+
));
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
program
|
|
162
|
+
.command('setup')
|
|
163
|
+
.description('配置 API Key 和模型')
|
|
164
|
+
.action(async () => {
|
|
165
|
+
const chalk = (await import('chalk')).default;
|
|
166
|
+
const ora = (await import('ora')).default;
|
|
167
|
+
const boxen = (await import('boxen')).default;
|
|
168
|
+
|
|
169
|
+
console.log(await getBanner());
|
|
170
|
+
|
|
171
|
+
// 已有配置时先展示,再询问
|
|
172
|
+
const existing = loadConfig();
|
|
173
|
+
if (existing) {
|
|
174
|
+
const existingProvider = PROVIDERS[existing.provider];
|
|
175
|
+
console.log(boxen(
|
|
176
|
+
chalk.bold('当前配置\n\n') +
|
|
177
|
+
chalk.dim('厂商 ') + chalk.white(existingProvider?.name || existing.provider) + '\n' +
|
|
178
|
+
chalk.dim('模型 ') + chalk.yellow(existing.model) + '\n' +
|
|
179
|
+
chalk.dim('Key ') + chalk.dim(existing.apiKey.slice(0, 10) + '...'),
|
|
180
|
+
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'yellow', margin: { top: 1, bottom: 1 } }
|
|
181
|
+
));
|
|
182
|
+
const overwrite = await confirm({ message: '覆盖现有配置?', default: false });
|
|
183
|
+
if (!overwrite) return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const providerKey = await select({
|
|
187
|
+
message: chalk.cyan('选择 AI 厂商'),
|
|
188
|
+
choices: Object.entries(PROVIDERS).map(([value, p]) => ({ name: p.name, value })),
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const provider = PROVIDERS[providerKey];
|
|
192
|
+
|
|
193
|
+
const model = await select({
|
|
194
|
+
message: chalk.cyan('选择模型'),
|
|
195
|
+
choices: provider.models,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const apiKey = await input({
|
|
199
|
+
message: chalk.cyan(`${provider.name} API Key`),
|
|
200
|
+
transformer: (v) => v ? chalk.dim('•'.repeat(v.length)) : '',
|
|
201
|
+
validate: (v) => v.trim().length > 0 ? true : 'API Key 不能为空',
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const spinner = ora('写入配置...').start();
|
|
205
|
+
let result, file;
|
|
206
|
+
try {
|
|
207
|
+
const config = { provider: providerKey, model, apiKey: apiKey.trim(), baseUrl: provider.baseUrl };
|
|
208
|
+
saveConfig(config);
|
|
209
|
+
({ result, file } = writeEnvToZshrc(provider.baseUrl, apiKey.trim()));
|
|
210
|
+
spinner.succeed(chalk.green(result === 'updated' ? `环境变量已更新 → ${file}` : `环境变量已写入 → ${file}`));
|
|
211
|
+
} catch (e) {
|
|
212
|
+
spinner.fail(chalk.red(`写入失败: ${e.message}`));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
console.log(chalk.dim(`⚠ API Key 以明文存储在 ${file} 和 ~/.clawai.json`));
|
|
216
|
+
|
|
217
|
+
console.log(boxen(
|
|
218
|
+
chalk.bold('配置完成!\n\n') +
|
|
219
|
+
chalk.dim('ANTHROPIC_BASE_URL ') + chalk.cyan(provider.baseUrl) + '\n' +
|
|
220
|
+
chalk.dim('ANTHROPIC_API_KEY ') + chalk.cyan(apiKey.trim().slice(0, 10) + '...') + '\n\n' +
|
|
221
|
+
chalk.white('下次直接输入 ') + chalk.cyan.bold('claude') + chalk.white(' 即可使用'),
|
|
222
|
+
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
|
|
223
|
+
));
|
|
224
|
+
|
|
225
|
+
const launchSpinner = ora('正在启动 Claude Code...').start();
|
|
226
|
+
await new Promise(r => setTimeout(r, 500));
|
|
227
|
+
launchSpinner.stop();
|
|
228
|
+
|
|
229
|
+
spawn('claude', [], {
|
|
230
|
+
stdio: 'inherit',
|
|
231
|
+
env: { ...process.env, ANTHROPIC_BASE_URL: provider.baseUrl, ANTHROPIC_API_KEY: apiKey.trim() },
|
|
232
|
+
}).on('error', () => {
|
|
233
|
+
console.log(chalk.yellow('\nClaude Code 未找到,请先运行: claw install-claude'));
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
program
|
|
238
|
+
.command('switch')
|
|
239
|
+
.description('快速切换模型(无需重新输入 Key)')
|
|
240
|
+
.action(async () => {
|
|
241
|
+
const chalk = (await import('chalk')).default;
|
|
242
|
+
const ora = (await import('ora')).default;
|
|
243
|
+
|
|
244
|
+
console.log(await getBanner());
|
|
245
|
+
|
|
246
|
+
const config = loadConfig();
|
|
247
|
+
if (!config) {
|
|
248
|
+
console.log(chalk.red('\n未配置,请先运行: claw setup\n'));
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const providerKey = await select({
|
|
253
|
+
message: chalk.cyan('选择 AI 厂商'),
|
|
254
|
+
choices: Object.entries(PROVIDERS).map(([value, p]) => ({ name: p.name, value })),
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const provider = PROVIDERS[providerKey];
|
|
258
|
+
|
|
259
|
+
const model = await select({
|
|
260
|
+
message: chalk.cyan('选择模型'),
|
|
261
|
+
choices: provider.models,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// 切换厂商时询问是否更换 Key
|
|
265
|
+
let apiKey = config.apiKey;
|
|
266
|
+
if (providerKey !== config.provider) {
|
|
267
|
+
const changeKey = await confirm({ message: `切换了厂商,是否更新 API Key?`, default: true });
|
|
268
|
+
if (changeKey) {
|
|
269
|
+
apiKey = await input({
|
|
270
|
+
message: chalk.cyan(`${provider.name} API Key`),
|
|
271
|
+
transformer: (v) => v ? chalk.dim('•'.repeat(v.length)) : '',
|
|
272
|
+
validate: (v) => v.trim().length > 0 ? true : 'API Key 不能为空',
|
|
273
|
+
});
|
|
274
|
+
apiKey = apiKey.trim();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const spinner = ora('切换中...').start();
|
|
279
|
+
const newConfig = { ...config, provider: providerKey, model, baseUrl: provider.baseUrl, apiKey };
|
|
280
|
+
saveConfig(newConfig);
|
|
281
|
+
const { file } = writeEnvToZshrc(provider.baseUrl, apiKey);
|
|
282
|
+
await new Promise(r => setTimeout(r, 300));
|
|
283
|
+
spinner.succeed(chalk.green(`已切换至 ${provider.name} · ${model}`));
|
|
284
|
+
console.log(chalk.dim(`运行 source ${file} 生效,或重新开一个终端`));
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
program
|
|
288
|
+
.command('status')
|
|
289
|
+
.description('查看当前配置和 Key 有效性')
|
|
290
|
+
.action(showStatus);
|
|
291
|
+
|
|
292
|
+
// 无参数时首次引导
|
|
293
|
+
if (process.argv.length === 2) {
|
|
294
|
+
(async () => {
|
|
295
|
+
const chalk = (await import('chalk')).default;
|
|
296
|
+
const boxen = (await import('boxen')).default;
|
|
297
|
+
|
|
298
|
+
console.log(await getBanner());
|
|
299
|
+
|
|
300
|
+
const config = loadConfig();
|
|
301
|
+
const claudeInstalled = (() => {
|
|
302
|
+
try { execSync('claude --version', { stdio: 'pipe' }); return true; } catch { return false; }
|
|
303
|
+
})();
|
|
304
|
+
|
|
305
|
+
if (!config && !claudeInstalled) {
|
|
306
|
+
console.log(boxen(
|
|
307
|
+
chalk.bold('欢迎使用!按以下步骤开始:\n\n') +
|
|
308
|
+
chalk.cyan(' 1. ') + chalk.white('claw install-claude') + chalk.dim(' 安装 Claude Code\n') +
|
|
309
|
+
chalk.cyan(' 2. ') + chalk.white('claw setup') + chalk.dim(' 配置国产模型 API\n') +
|
|
310
|
+
chalk.cyan(' 3. ') + chalk.white('claude') + chalk.dim(' 开始使用'),
|
|
311
|
+
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'cyan', margin: { top: 1, bottom: 1 } }
|
|
312
|
+
));
|
|
313
|
+
} else if (!config) {
|
|
314
|
+
console.log(boxen(
|
|
315
|
+
chalk.bold('Claude Code 已安装,还差一步:\n\n') +
|
|
316
|
+
chalk.cyan(' claw setup') + chalk.dim(' 配置国产模型 API Key'),
|
|
317
|
+
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'yellow', margin: { top: 1, bottom: 1 } }
|
|
318
|
+
));
|
|
319
|
+
} else {
|
|
320
|
+
console.log(chalk.green('✔ 已配置完成,直接运行 ') + chalk.cyan.bold('claude') + chalk.green(' 即可使用\n'));
|
|
321
|
+
await showStatus();
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
})();
|
|
325
|
+
} else {
|
|
326
|
+
program.parse(process.argv);
|
|
327
|
+
}
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
const CONFIG_FILE = path.join(os.homedir(), '.clawai.json');
|
|
6
|
+
|
|
7
|
+
const PROVIDERS = {
|
|
8
|
+
deepseek: {
|
|
9
|
+
name: 'DeepSeek',
|
|
10
|
+
baseUrl: 'https://api.deepseek.com/anthropic',
|
|
11
|
+
models: [
|
|
12
|
+
{ name: 'DeepSeek V4 Flash(快速)', value: 'deepseek-v4-flash' },
|
|
13
|
+
{ name: 'DeepSeek V4 Pro(强力)', value: 'deepseek-v4-pro' },
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
qwen: {
|
|
17
|
+
name: '阿里云百炼 (Qwen)',
|
|
18
|
+
baseUrl: 'https://dashscope.aliyuncs.com/apps/anthropic',
|
|
19
|
+
models: [
|
|
20
|
+
{ name: 'Qwen3 Max(强力)', value: 'qwen3-max' },
|
|
21
|
+
{ name: 'Qwen3 Plus(均衡)', value: 'qwen3-plus' },
|
|
22
|
+
{ name: 'Qwen3 Flash(快速)', value: 'qwen3.5-plus' },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
minimax: {
|
|
26
|
+
name: 'MiniMax',
|
|
27
|
+
baseUrl: 'https://api.minimaxi.com/anthropic',
|
|
28
|
+
models: [
|
|
29
|
+
{ name: 'MiniMax M2.7(旗舰)', value: 'MiniMax-M2.7' },
|
|
30
|
+
{ name: 'MiniMax M2.7 Turbo(快速)', value: 'MiniMax-M2.7-Turbo' },
|
|
31
|
+
{ name: 'MiniMax M2.5(均衡)', value: 'MiniMax-M2.5' },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
glm: {
|
|
35
|
+
name: '智谱 GLM',
|
|
36
|
+
baseUrl: 'https://open.bigmodel.cn/api/anthropic',
|
|
37
|
+
models: [
|
|
38
|
+
{ name: 'GLM-4.7(旗舰)', value: 'GLM-4.7' },
|
|
39
|
+
{ name: 'GLM-5.1(强力)', value: 'GLM-5.1' },
|
|
40
|
+
{ name: 'GLM-5 Turbo(快速)', value: 'GLM-5-Turbo' },
|
|
41
|
+
{ name: 'GLM-4.5 Air(轻量)', value: 'GLM-4.5-Air' },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
mimo: {
|
|
45
|
+
name: '小米 MiMo',
|
|
46
|
+
baseUrl: 'https://api.xiaomimimo.com/anthropic',
|
|
47
|
+
models: [
|
|
48
|
+
{ name: 'MiMo V2.5 Pro(旗舰)', value: 'mimo-v2.5-pro' },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function loadConfig() {
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function saveConfig(config) {
|
|
62
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 写入或更新 shell 配置文件中的环境变量块
|
|
66
|
+
function writeEnvToZshrc(baseUrl, apiKey) {
|
|
67
|
+
// 检测用户 shell,自动选配置文件
|
|
68
|
+
// macOS bash 默认读 ~/.bash_profile(登录 shell),Linux 读 ~/.bashrc
|
|
69
|
+
const shell = process.env.SHELL || '';
|
|
70
|
+
let rcFile;
|
|
71
|
+
if (shell.includes('bash')) {
|
|
72
|
+
const bashProfile = path.join(os.homedir(), '.bash_profile');
|
|
73
|
+
const bashrc = path.join(os.homedir(), '.bashrc');
|
|
74
|
+
rcFile = (process.platform === 'darwin' && fs.existsSync(bashProfile)) ? bashProfile : bashrc;
|
|
75
|
+
} else {
|
|
76
|
+
rcFile = path.join(os.homedir(), '.zshrc');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Key 加引号,避免特殊字符(& = 空格)破坏 shell 语法
|
|
80
|
+
const safeKey = `"${apiKey.replace(/"/g, '\\"')}"`;
|
|
81
|
+
const block = `\n# clawai\nexport ANTHROPIC_BASE_URL=${baseUrl}\nexport ANTHROPIC_API_KEY=${safeKey}\n`;
|
|
82
|
+
const current = fs.existsSync(rcFile) ? fs.readFileSync(rcFile, 'utf8') : '';
|
|
83
|
+
|
|
84
|
+
if (current.includes('# clawai')) {
|
|
85
|
+
const updated = current.replace(
|
|
86
|
+
/# clawai\nexport ANTHROPIC_BASE_URL=[^\n]*\nexport ANTHROPIC_API_KEY=[^\n]*/,
|
|
87
|
+
`# clawai\nexport ANTHROPIC_BASE_URL=${baseUrl}\nexport ANTHROPIC_API_KEY=${safeKey}`
|
|
88
|
+
);
|
|
89
|
+
fs.writeFileSync(rcFile, updated);
|
|
90
|
+
return { result: 'updated', file: rcFile };
|
|
91
|
+
} else {
|
|
92
|
+
fs.appendFileSync(rcFile, block);
|
|
93
|
+
return { result: 'added', file: rcFile };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { loadConfig, saveConfig, writeEnvToZshrc, PROVIDERS, CONFIG_FILE };
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "yingclaw",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Claude Code × 国产大模型一键接入:DeepSeek、Qwen、MiniMax、GLM、MiMo",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"claw": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"claude",
|
|
14
|
+
"claude-code",
|
|
15
|
+
"deepseek",
|
|
16
|
+
"qwen",
|
|
17
|
+
"minimax",
|
|
18
|
+
"glm",
|
|
19
|
+
"mimo",
|
|
20
|
+
"ai",
|
|
21
|
+
"cli"
|
|
22
|
+
],
|
|
23
|
+
"author": "",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"type": "commonjs",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@inquirer/prompts": "^8.4.2",
|
|
28
|
+
"boxen": "^8.0.1",
|
|
29
|
+
"chalk": "^5.6.2",
|
|
30
|
+
"commander": "^14.0.3",
|
|
31
|
+
"figlet": "^1.11.0",
|
|
32
|
+
"ora": "^9.4.0"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
}
|
|
37
|
+
}
|