yingclaw 1.5.1 → 1.6.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 +13 -1
- package/bin/cli.js +108 -24
- package/lib/config.js +107 -18
- package/lib/panel.js +49 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -50,7 +50,19 @@ claw status # 查看当前配置,验证 Key 是否有效
|
|
|
50
50
|
|
|
51
51
|
## 原理
|
|
52
52
|
|
|
53
|
-
各厂商均原生支持 Anthropic API
|
|
53
|
+
各厂商均原生支持 Anthropic API 格式。本工具会自动写入 Claude Code 所需的环境变量,包括:
|
|
54
|
+
|
|
55
|
+
- `ANTHROPIC_BASE_URL`
|
|
56
|
+
- `ANTHROPIC_AUTH_TOKEN`
|
|
57
|
+
- `ANTHROPIC_API_KEY`
|
|
58
|
+
- `ANTHROPIC_MODEL`
|
|
59
|
+
- `ANTHROPIC_DEFAULT_OPUS_MODEL`
|
|
60
|
+
- `ANTHROPIC_DEFAULT_SONNET_MODEL`
|
|
61
|
+
- `ANTHROPIC_DEFAULT_HAIKU_MODEL`
|
|
62
|
+
- `CLAUDE_CODE_SUBAGENT_MODEL`
|
|
63
|
+
- `CLAUDE_CODE_EFFORT_LEVEL`
|
|
64
|
+
|
|
65
|
+
以 DeepSeek 为例,主模型默认使用 `deepseek-v4-pro[1m]`,Haiku/Subagent 快速模型使用 `deepseek-v4-flash`。如果在线模型列表获取失败,会回退到内置默认列表。
|
|
54
66
|
|
|
55
67
|
## License
|
|
56
68
|
|
package/bin/cli.js
CHANGED
|
@@ -2,10 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
const { Command } = require('commander');
|
|
4
4
|
const { select, input, confirm } = require('@inquirer/prompts');
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
loadConfig,
|
|
7
|
+
saveConfig,
|
|
8
|
+
writeEnvToZshrc,
|
|
9
|
+
fetchModels,
|
|
10
|
+
resetConfig,
|
|
11
|
+
resolveFastModel,
|
|
12
|
+
buildClaudeEnv,
|
|
13
|
+
classifyValidationStatus,
|
|
14
|
+
PROVIDERS,
|
|
15
|
+
} = require('../lib/config');
|
|
6
16
|
const { execSync, spawn, spawnSync } = require('child_process');
|
|
7
17
|
const https = require('https');
|
|
8
18
|
const pkg = require('../package.json');
|
|
19
|
+
const { buildStatusView } = require('../lib/panel');
|
|
9
20
|
|
|
10
21
|
const program = new Command();
|
|
11
22
|
|
|
@@ -14,8 +25,9 @@ async function getBanner() {
|
|
|
14
25
|
const figlet = require('figlet');
|
|
15
26
|
const boxen = (await import('boxen')).default;
|
|
16
27
|
const title = figlet.textSync('claw', { font: 'Standard', horizontalLayout: 'fitted' });
|
|
28
|
+
const subtitle = chalk.dim('Claude Code × 国产大模型 一键接入') + ' ' + chalk.cyan(`v${pkg.version}`);
|
|
17
29
|
return boxen(
|
|
18
|
-
chalk.cyan.bold(title) + '\n' +
|
|
30
|
+
chalk.cyan.bold(title) + '\n' + subtitle,
|
|
19
31
|
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'cyan', margin: { top: 1, bottom: 0 } }
|
|
20
32
|
);
|
|
21
33
|
}
|
|
@@ -45,9 +57,7 @@ async function validateKey(config) {
|
|
|
45
57
|
'content-length': body.length,
|
|
46
58
|
},
|
|
47
59
|
}, (res) => {
|
|
48
|
-
|
|
49
|
-
if (res.statusCode === 401 || res.statusCode === 403) resolve(false);
|
|
50
|
-
else resolve(true);
|
|
60
|
+
resolve(classifyValidationStatus(res.statusCode));
|
|
51
61
|
});
|
|
52
62
|
req.on('error', () => resolve(null));
|
|
53
63
|
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
@@ -56,6 +66,15 @@ async function validateKey(config) {
|
|
|
56
66
|
});
|
|
57
67
|
}
|
|
58
68
|
|
|
69
|
+
function isClaudeInstalled() {
|
|
70
|
+
try {
|
|
71
|
+
execSync('claude --version', { stdio: 'pipe' });
|
|
72
|
+
return true;
|
|
73
|
+
} catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
59
78
|
async function showStatus() {
|
|
60
79
|
const chalk = (await import('chalk')).default;
|
|
61
80
|
const boxen = (await import('boxen')).default;
|
|
@@ -67,7 +86,6 @@ async function showStatus() {
|
|
|
67
86
|
return;
|
|
68
87
|
}
|
|
69
88
|
|
|
70
|
-
const provider = PROVIDERS[config.provider];
|
|
71
89
|
const spinner = ora('验证 API Key...').start();
|
|
72
90
|
const valid = await validateKey(config);
|
|
73
91
|
|
|
@@ -75,12 +93,34 @@ async function showStatus() {
|
|
|
75
93
|
else if (valid === false) spinner.fail('API Key 无效或已过期');
|
|
76
94
|
else spinner.warn('网络异常,无法验证');
|
|
77
95
|
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
96
|
+
const view = buildStatusView(config, {
|
|
97
|
+
apiStatus: valid,
|
|
98
|
+
claudeInstalled: isClaudeInstalled(),
|
|
99
|
+
env: process.env,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const lines = view.lines.map(({ label, value }) => {
|
|
103
|
+
const coloredValue = label === '厂商'
|
|
104
|
+
? chalk.white.bold(value)
|
|
105
|
+
: label.includes('模型')
|
|
106
|
+
? chalk.yellow(value)
|
|
107
|
+
: label === 'Base URL'
|
|
108
|
+
? chalk.cyan(value)
|
|
109
|
+
: label === '当前终端' && value === '未生效'
|
|
110
|
+
? chalk.yellow(value)
|
|
111
|
+
: value;
|
|
112
|
+
return `${chalk.dim(label + ':')} ${coloredValue}`;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
for (const warning of view.warnings) {
|
|
116
|
+
if (warning.includes('建议运行')) {
|
|
117
|
+
const [summary, action] = warning.split(',建议运行 ');
|
|
118
|
+
lines.push(`${chalk.yellow('提示:')} ${chalk.yellow(summary)}`);
|
|
119
|
+
lines.push(`${chalk.yellow('建议:')} ${chalk.yellow(`运行 ${action}`)}`);
|
|
120
|
+
} else {
|
|
121
|
+
lines.push(`${chalk.yellow('提示:')} ${chalk.yellow(warning)}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
84
124
|
|
|
85
125
|
console.log(boxen(lines.join('\n'), {
|
|
86
126
|
title: chalk.bold('当前配置'),
|
|
@@ -237,9 +277,10 @@ program
|
|
|
237
277
|
const spinner = ora('写入配置...').start();
|
|
238
278
|
let result, file;
|
|
239
279
|
try {
|
|
240
|
-
const
|
|
280
|
+
const fastModel = resolveFastModel(provider, model);
|
|
281
|
+
const cfg = { provider: providerKey, model, fastModel, apiKey, baseUrl: provider.baseUrl };
|
|
241
282
|
saveConfig(cfg);
|
|
242
|
-
({ result, file } = writeEnvToZshrc(provider.baseUrl, apiKey));
|
|
283
|
+
({ result, file } = writeEnvToZshrc(provider.baseUrl, apiKey, model, fastModel));
|
|
243
284
|
spinner.succeed(chalk.green(result === 'updated' ? `环境变量已更新 → ${file}` : `环境变量已写入 → ${file}`));
|
|
244
285
|
} catch (e) {
|
|
245
286
|
spinner.fail(chalk.red(`写入失败: ${e.message}`));
|
|
@@ -261,7 +302,7 @@ program
|
|
|
261
302
|
|
|
262
303
|
spawn('claude', [], {
|
|
263
304
|
stdio: 'inherit',
|
|
264
|
-
env: { ...process.env,
|
|
305
|
+
env: { ...process.env, ...buildClaudeEnv({ provider: providerKey, baseUrl: provider.baseUrl, apiKey, model, fastModel: resolveFastModel(provider, model) }) },
|
|
265
306
|
}).on('error', () => {
|
|
266
307
|
console.log(chalk.yellow('\nClaude Code 未找到,请先运行: claw install-claude'));
|
|
267
308
|
});
|
|
@@ -329,9 +370,10 @@ program
|
|
|
329
370
|
if (model === '__BACK__') return;
|
|
330
371
|
|
|
331
372
|
const spinner = ora('切换中...').start();
|
|
332
|
-
const
|
|
373
|
+
const fastModel = resolveFastModel(provider, model);
|
|
374
|
+
const newConfig = { ...config, provider: providerKey, model, fastModel, baseUrl: provider.baseUrl, apiKey };
|
|
333
375
|
saveConfig(newConfig);
|
|
334
|
-
const { file } = writeEnvToZshrc(provider.baseUrl, apiKey);
|
|
376
|
+
const { file } = writeEnvToZshrc(provider.baseUrl, apiKey, model, fastModel);
|
|
335
377
|
await new Promise(r => setTimeout(r, 300));
|
|
336
378
|
spinner.succeed(chalk.green(`已切换至 ${provider.name} · ${model}`));
|
|
337
379
|
console.log(chalk.dim(`运行 source ${file} 生效,或重新开一个终端`));
|
|
@@ -342,19 +384,57 @@ program
|
|
|
342
384
|
.description('查看当前配置和 Key 有效性')
|
|
343
385
|
.action(showStatus);
|
|
344
386
|
|
|
387
|
+
program
|
|
388
|
+
.command('reset')
|
|
389
|
+
.description('恢复默认(清除所有 clawai 配置)')
|
|
390
|
+
.action(async () => {
|
|
391
|
+
const chalk = (await import('chalk')).default;
|
|
392
|
+
const ora = (await import('ora')).default;
|
|
393
|
+
const boxen = (await import('boxen')).default;
|
|
394
|
+
|
|
395
|
+
console.log(await getBanner());
|
|
396
|
+
|
|
397
|
+
const yes = await confirm({
|
|
398
|
+
message: chalk.red('确定要清除所有 clawai 配置吗?此操作不可撤销'),
|
|
399
|
+
default: false,
|
|
400
|
+
});
|
|
401
|
+
if (!yes) {
|
|
402
|
+
console.log(chalk.dim('已取消'));
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const spinner = ora('清除中...').start();
|
|
407
|
+
const cleared = resetConfig();
|
|
408
|
+
await new Promise(r => setTimeout(r, 300));
|
|
409
|
+
|
|
410
|
+
if (cleared.length === 0) {
|
|
411
|
+
spinner.warn(chalk.yellow('没有找到任何配置,无需清除'));
|
|
412
|
+
} else {
|
|
413
|
+
spinner.succeed(chalk.green('已恢复默认'));
|
|
414
|
+
console.log(boxen(
|
|
415
|
+
chalk.bold('已清除以下文件中的 clawai 配置:\n\n') +
|
|
416
|
+
cleared.map(f => chalk.cyan(' • ' + f)).join('\n') +
|
|
417
|
+
'\n\n' + chalk.dim('注:当前终端的环境变量还在内存中,重开终端或 unset 才彻底清除'),
|
|
418
|
+
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
|
|
419
|
+
));
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
|
|
345
423
|
async function renderStatusBar(apiStatus) {
|
|
346
424
|
const chalk = (await import('chalk')).default;
|
|
347
425
|
const config = loadConfig();
|
|
348
|
-
const claudeInstalled = (
|
|
349
|
-
try { execSync('claude --version', { stdio: 'pipe' }); return true; } catch { return false; }
|
|
350
|
-
})();
|
|
426
|
+
const claudeInstalled = isClaudeInstalled();
|
|
351
427
|
|
|
352
428
|
const claudeIcon = claudeInstalled ? chalk.green('●') : chalk.red('●');
|
|
353
429
|
const claudeText = chalk.dim('Claude');
|
|
354
430
|
|
|
355
431
|
let cfgPart;
|
|
356
432
|
if (config) {
|
|
357
|
-
const
|
|
433
|
+
const view = buildStatusView(config, {
|
|
434
|
+
apiStatus,
|
|
435
|
+
claudeInstalled,
|
|
436
|
+
env: process.env,
|
|
437
|
+
});
|
|
358
438
|
let dot;
|
|
359
439
|
if (apiStatus === true) dot = chalk.green('●');
|
|
360
440
|
else if (apiStatus === false) dot = chalk.red('●');
|
|
@@ -364,7 +444,9 @@ async function renderStatusBar(apiStatus) {
|
|
|
364
444
|
: apiStatus === false ? chalk.red(' API ✘')
|
|
365
445
|
: apiStatus === null ? chalk.yellow(' 网络异常')
|
|
366
446
|
: '';
|
|
367
|
-
|
|
447
|
+
const envTag = view.envActive ? chalk.green(' env ✓') : chalk.yellow(' env 未生效');
|
|
448
|
+
const legacyTag = view.warnings.length > 0 ? chalk.yellow(' 旧模型') : '';
|
|
449
|
+
cfgPart = `${dot} ${chalk.white(view.providerName)}${apiTag}${envTag}${legacyTag}\n ${chalk.dim('主 ')}${chalk.yellow(view.mainModel)}${chalk.dim(' 快 ')}${chalk.yellow(view.fastModel)}`;
|
|
368
450
|
} else {
|
|
369
451
|
cfgPart = chalk.red('●') + ' ' + chalk.dim('未配置');
|
|
370
452
|
}
|
|
@@ -399,9 +481,10 @@ async function runMenu() {
|
|
|
399
481
|
{ name: '🤖 启动 Claude Code', value: 'launch', disabled: !config && '需先完成配置' },
|
|
400
482
|
{ name: '📦 安装 Claude Code', value: 'install' },
|
|
401
483
|
{ name: config ? '⚙️ 重新配置(输入新的 API Key)' : '⚙️ 首次配置 API Key 和模型', value: 'setup' },
|
|
402
|
-
{ name: '🔄
|
|
484
|
+
{ name: '🔄 切换厂商或模型', value: 'switch', disabled: !config && '需先完成配置' },
|
|
403
485
|
{ name: '📊 查看当前配置', value: 'status', disabled: !config && '需先完成配置' },
|
|
404
486
|
{ name: '🔁 重新检测 API', value: 'recheck', disabled: !config && '需先完成配置' },
|
|
487
|
+
{ name: '🗑 恢复默认(清除所有配置)', value: 'reset', disabled: !config && '没有可清除的配置' },
|
|
405
488
|
{ name: '退出', value: 'exit' },
|
|
406
489
|
],
|
|
407
490
|
});
|
|
@@ -420,7 +503,7 @@ async function runMenu() {
|
|
|
420
503
|
await new Promise((resolve) => {
|
|
421
504
|
const child = spawn('claude', [], {
|
|
422
505
|
stdio: 'inherit',
|
|
423
|
-
env: { ...process.env,
|
|
506
|
+
env: { ...process.env, ...buildClaudeEnv(cfg) },
|
|
424
507
|
});
|
|
425
508
|
child.on('error', () => {
|
|
426
509
|
console.log(chalk.yellow('\nClaude Code 未找到,请先选择"安装 Claude Code"'));
|
|
@@ -436,6 +519,7 @@ async function runMenu() {
|
|
|
436
519
|
setup: 'setup',
|
|
437
520
|
switch: 'switch',
|
|
438
521
|
status: 'status',
|
|
522
|
+
reset: 'reset',
|
|
439
523
|
};
|
|
440
524
|
|
|
441
525
|
// 执行子命令
|
package/lib/config.js
CHANGED
|
@@ -10,9 +10,10 @@ const PROVIDERS = {
|
|
|
10
10
|
name: 'DeepSeek',
|
|
11
11
|
baseUrl: 'https://api.deepseek.com/anthropic',
|
|
12
12
|
modelsUrl: 'https://api.deepseek.com/v1/models',
|
|
13
|
+
fastModel: 'deepseek-v4-flash',
|
|
13
14
|
models: [
|
|
14
15
|
{ name: 'DeepSeek V4 Flash(快速)', value: 'deepseek-v4-flash' },
|
|
15
|
-
{ name: 'DeepSeek V4 Pro(强力)', value: 'deepseek-v4-pro' },
|
|
16
|
+
{ name: 'DeepSeek V4 Pro(强力)', value: 'deepseek-v4-pro[1m]' },
|
|
16
17
|
],
|
|
17
18
|
},
|
|
18
19
|
qwen: {
|
|
@@ -107,12 +108,63 @@ function loadConfig() {
|
|
|
107
108
|
|
|
108
109
|
function saveConfig(config) {
|
|
109
110
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
111
|
+
try {
|
|
112
|
+
fs.chmodSync(CONFIG_FILE, 0o600);
|
|
113
|
+
} catch {}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function resolveFastModel(provider, model) {
|
|
117
|
+
if (/flash|turbo|haiku|air|lite/i.test(model)) return model;
|
|
118
|
+
return provider?.fastModel || model;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function buildClaudeEnv({ provider, baseUrl, apiKey, model, fastModel }) {
|
|
122
|
+
const resolvedFastModel = fastModel || resolveFastModel(PROVIDERS[provider], model);
|
|
123
|
+
return {
|
|
124
|
+
ANTHROPIC_BASE_URL: baseUrl,
|
|
125
|
+
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
126
|
+
ANTHROPIC_API_KEY: apiKey,
|
|
127
|
+
ANTHROPIC_MODEL: model,
|
|
128
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: model,
|
|
129
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: model,
|
|
130
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: resolvedFastModel,
|
|
131
|
+
CLAUDE_CODE_SUBAGENT_MODEL: resolvedFastModel,
|
|
132
|
+
CLAUDE_CODE_EFFORT_LEVEL: 'max',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function classifyValidationStatus(statusCode) {
|
|
137
|
+
if (statusCode >= 200 && statusCode < 300) return true;
|
|
138
|
+
if (statusCode === 401 || statusCode === 403) return false;
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function shellQuote(value) {
|
|
143
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 构造完整的 clawai 环境变量块
|
|
147
|
+
function buildEnvBlock(baseUrl, apiKey, model, fastModel) {
|
|
148
|
+
const env = buildClaudeEnv({ baseUrl, apiKey, model, fastModel });
|
|
149
|
+
return [
|
|
150
|
+
'',
|
|
151
|
+
'# clawai-start',
|
|
152
|
+
`export ANTHROPIC_BASE_URL=${shellQuote(env.ANTHROPIC_BASE_URL)}`,
|
|
153
|
+
`export ANTHROPIC_AUTH_TOKEN=${shellQuote(env.ANTHROPIC_AUTH_TOKEN)}`,
|
|
154
|
+
`export ANTHROPIC_API_KEY=${shellQuote(env.ANTHROPIC_API_KEY)}`,
|
|
155
|
+
`export ANTHROPIC_MODEL=${shellQuote(env.ANTHROPIC_MODEL)}`,
|
|
156
|
+
`export ANTHROPIC_DEFAULT_OPUS_MODEL=${shellQuote(env.ANTHROPIC_DEFAULT_OPUS_MODEL)}`,
|
|
157
|
+
`export ANTHROPIC_DEFAULT_SONNET_MODEL=${shellQuote(env.ANTHROPIC_DEFAULT_SONNET_MODEL)}`,
|
|
158
|
+
`export ANTHROPIC_DEFAULT_HAIKU_MODEL=${shellQuote(env.ANTHROPIC_DEFAULT_HAIKU_MODEL)}`,
|
|
159
|
+
`export CLAUDE_CODE_SUBAGENT_MODEL=${shellQuote(env.CLAUDE_CODE_SUBAGENT_MODEL)}`,
|
|
160
|
+
`export CLAUDE_CODE_EFFORT_LEVEL=${shellQuote(env.CLAUDE_CODE_EFFORT_LEVEL)}`,
|
|
161
|
+
'# clawai-end',
|
|
162
|
+
'',
|
|
163
|
+
].join('\n');
|
|
110
164
|
}
|
|
111
165
|
|
|
112
166
|
// 写入或更新 shell 配置文件中的环境变量块
|
|
113
|
-
function writeEnvToZshrc(baseUrl, apiKey) {
|
|
114
|
-
// 检测用户 shell,自动选配置文件
|
|
115
|
-
// macOS bash 默认读 ~/.bash_profile(登录 shell),Linux 读 ~/.bashrc
|
|
167
|
+
function writeEnvToZshrc(baseUrl, apiKey, model, fastModel) {
|
|
116
168
|
const shell = process.env.SHELL || '';
|
|
117
169
|
let rcFile;
|
|
118
170
|
if (shell.includes('bash')) {
|
|
@@ -123,22 +175,59 @@ function writeEnvToZshrc(baseUrl, apiKey) {
|
|
|
123
175
|
rcFile = path.join(os.homedir(), '.zshrc');
|
|
124
176
|
}
|
|
125
177
|
|
|
126
|
-
|
|
127
|
-
const safeKey = `"${apiKey.replace(/"/g, '\\"')}"`;
|
|
128
|
-
const block = `\n# clawai\nexport ANTHROPIC_BASE_URL=${baseUrl}\nexport ANTHROPIC_API_KEY=${safeKey}\n`;
|
|
178
|
+
const block = buildEnvBlock(baseUrl, apiKey, model, fastModel);
|
|
129
179
|
const current = fs.existsSync(rcFile) ? fs.readFileSync(rcFile, 'utf8') : '';
|
|
130
180
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
181
|
+
// 兼容旧版(# clawai 单行块)和新版(# clawai-start...# clawai-end 多行块)
|
|
182
|
+
const cleaned = current
|
|
183
|
+
.replace(/\n?# clawai-start[\s\S]*?# clawai-end\n?/g, '')
|
|
184
|
+
.replace(/\n?# clawai\nexport ANTHROPIC_BASE_URL=[^\n]*\nexport ANTHROPIC_API_KEY=[^\n]*\n?/g, '');
|
|
185
|
+
|
|
186
|
+
fs.writeFileSync(rcFile, cleaned + block);
|
|
187
|
+
return { result: cleaned !== current ? 'updated' : 'added', file: rcFile };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 清除所有 clawai 配置(配置文件 + shell 环境变量块)
|
|
191
|
+
function resetConfig() {
|
|
192
|
+
const cleared = [];
|
|
193
|
+
|
|
194
|
+
// 删配置文件
|
|
195
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
196
|
+
fs.unlinkSync(CONFIG_FILE);
|
|
197
|
+
cleared.push(CONFIG_FILE);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 清理所有可能的 rc 文件中的 clawai 块
|
|
201
|
+
const rcFiles = [
|
|
202
|
+
path.join(os.homedir(), '.zshrc'),
|
|
203
|
+
path.join(os.homedir(), '.bashrc'),
|
|
204
|
+
path.join(os.homedir(), '.bash_profile'),
|
|
205
|
+
];
|
|
206
|
+
for (const f of rcFiles) {
|
|
207
|
+
if (!fs.existsSync(f)) continue;
|
|
208
|
+
const content = fs.readFileSync(f, 'utf8');
|
|
209
|
+
if (!content.includes('# clawai')) continue;
|
|
210
|
+
const cleaned = content
|
|
211
|
+
.replace(/\n?# clawai-start[\s\S]*?# clawai-end\n?/g, '')
|
|
212
|
+
.replace(/\n?# clawai\nexport ANTHROPIC_BASE_URL=[^\n]*\nexport ANTHROPIC_API_KEY=[^\n]*\n?/g, '');
|
|
213
|
+
if (cleaned === content) continue;
|
|
214
|
+
fs.writeFileSync(f, cleaned);
|
|
215
|
+
cleared.push(f);
|
|
141
216
|
}
|
|
217
|
+
|
|
218
|
+
return cleared;
|
|
142
219
|
}
|
|
143
220
|
|
|
144
|
-
module.exports = {
|
|
221
|
+
module.exports = {
|
|
222
|
+
loadConfig,
|
|
223
|
+
saveConfig,
|
|
224
|
+
writeEnvToZshrc,
|
|
225
|
+
fetchModels,
|
|
226
|
+
resetConfig,
|
|
227
|
+
resolveFastModel,
|
|
228
|
+
buildClaudeEnv,
|
|
229
|
+
buildEnvBlock,
|
|
230
|
+
classifyValidationStatus,
|
|
231
|
+
PROVIDERS,
|
|
232
|
+
CONFIG_FILE,
|
|
233
|
+
};
|
package/lib/panel.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { buildClaudeEnv, PROVIDERS } = require('./config');
|
|
2
|
+
|
|
3
|
+
function apiStatusText(apiStatus) {
|
|
4
|
+
if (apiStatus === true) return 'API 正常';
|
|
5
|
+
if (apiStatus === false) return 'API Key 无效';
|
|
6
|
+
if (apiStatus === null) return '网络/服务异常';
|
|
7
|
+
return '未检测';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isEnvActive(config, env) {
|
|
11
|
+
const expected = buildClaudeEnv(config);
|
|
12
|
+
return Object.entries(expected).every(([key, value]) => env[key] === value);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function buildStatusView(config, options = {}) {
|
|
16
|
+
const provider = PROVIDERS[config.provider];
|
|
17
|
+
const providerName = provider?.name || config.provider;
|
|
18
|
+
const claudeInstalled = options.claudeInstalled === true;
|
|
19
|
+
const env = options.env || {};
|
|
20
|
+
const expectedEnv = buildClaudeEnv(config);
|
|
21
|
+
const mainModel = expectedEnv.ANTHROPIC_MODEL;
|
|
22
|
+
const fastModel = expectedEnv.CLAUDE_CODE_SUBAGENT_MODEL;
|
|
23
|
+
const envActive = isEnvActive(config, env);
|
|
24
|
+
const warnings = [];
|
|
25
|
+
|
|
26
|
+
if (config.provider === 'deepseek' && config.model === 'deepseek-v4-pro') {
|
|
27
|
+
warnings.push('检测到旧 DeepSeek 模型名,建议运行 claw switch 更新到 deepseek-v4-pro[1m]');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
providerName,
|
|
32
|
+
mainModel,
|
|
33
|
+
fastModel,
|
|
34
|
+
envActive,
|
|
35
|
+
warnings,
|
|
36
|
+
lines: [
|
|
37
|
+
{ label: '厂商', value: providerName },
|
|
38
|
+
{ label: '主模型', value: mainModel },
|
|
39
|
+
{ label: '快速模型', value: fastModel },
|
|
40
|
+
{ label: 'API Key', value: '已保存' },
|
|
41
|
+
{ label: 'API 状态', value: apiStatusText(options.apiStatus) },
|
|
42
|
+
{ label: 'Claude Code', value: claudeInstalled ? '已安装' : '未检测到' },
|
|
43
|
+
{ label: '当前终端', value: envActive ? '已生效' : '未生效' },
|
|
44
|
+
{ label: 'Base URL', value: config.baseUrl },
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { buildStatusView, apiStatusText, isEnvActive };
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yingclaw",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Claude Code × 国产大模型一键接入:DeepSeek、Qwen、MiniMax、GLM、MiMo",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"claw": "bin/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"start": "node bin/cli.js"
|
|
10
|
+
"start": "node bin/cli.js",
|
|
11
|
+
"test": "node --test"
|
|
11
12
|
},
|
|
12
13
|
"keywords": [
|
|
13
14
|
"claude",
|