yingclaw 1.6.0 → 1.6.2
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 +6 -0
- package/bin/cli.js +42 -22
- package/index.js +4 -0
- package/lib/config.js +23 -1
- package/lib/panel.js +22 -1
- package/package.json +1 -1
package/README.md
CHANGED
package/bin/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
writeEnvToZshrc,
|
|
9
9
|
fetchModels,
|
|
10
10
|
resetConfig,
|
|
11
|
+
validateConfig,
|
|
11
12
|
resolveFastModel,
|
|
12
13
|
buildClaudeEnv,
|
|
13
14
|
classifyValidationStatus,
|
|
@@ -16,7 +17,7 @@ const {
|
|
|
16
17
|
const { execSync, spawn, spawnSync } = require('child_process');
|
|
17
18
|
const https = require('https');
|
|
18
19
|
const pkg = require('../package.json');
|
|
19
|
-
const { buildStatusView } = require('../lib/panel');
|
|
20
|
+
const { buildMenuStatusLines, buildStatusView } = require('../lib/panel');
|
|
20
21
|
|
|
21
22
|
const program = new Command();
|
|
22
23
|
|
|
@@ -66,6 +67,11 @@ async function validateKey(config) {
|
|
|
66
67
|
});
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
function getConfigValidationMessage(config) {
|
|
71
|
+
const validation = validateConfig(config);
|
|
72
|
+
return validation.valid ? null : validation.message;
|
|
73
|
+
}
|
|
74
|
+
|
|
69
75
|
function isClaudeInstalled() {
|
|
70
76
|
try {
|
|
71
77
|
execSync('claude --version', { stdio: 'pipe' });
|
|
@@ -85,6 +91,12 @@ async function showStatus() {
|
|
|
85
91
|
console.log(chalk.red('\n未配置,请先运行: claw setup\n'));
|
|
86
92
|
return;
|
|
87
93
|
}
|
|
94
|
+
const configProblem = getConfigValidationMessage(config);
|
|
95
|
+
if (configProblem) {
|
|
96
|
+
console.log(chalk.red(`\n配置无效:${configProblem}`));
|
|
97
|
+
console.log(chalk.dim('请运行 claw setup 重新配置。\n'));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
88
100
|
|
|
89
101
|
const spinner = ora('验证 API Key...').start();
|
|
90
102
|
const valid = await validateKey(config);
|
|
@@ -218,7 +230,7 @@ program
|
|
|
218
230
|
chalk.bold('当前配置\n\n') +
|
|
219
231
|
chalk.dim('厂商 ') + chalk.white(existingProvider?.name || existing.provider) + '\n' +
|
|
220
232
|
chalk.dim('模型 ') + chalk.yellow(existing.model) + '\n' +
|
|
221
|
-
chalk.dim('Key ') + chalk.dim(existing.apiKey
|
|
233
|
+
chalk.dim('Key ') + chalk.dim(existing.apiKey ? '已保存' : '缺失'),
|
|
222
234
|
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'yellow', margin: { top: 1, bottom: 1 } }
|
|
223
235
|
));
|
|
224
236
|
const overwrite = await confirm({ message: '覆盖现有配置?', default: false });
|
|
@@ -291,7 +303,7 @@ program
|
|
|
291
303
|
console.log(boxen(
|
|
292
304
|
chalk.bold('配置完成!\n\n') +
|
|
293
305
|
chalk.dim('ANTHROPIC_BASE_URL ') + chalk.cyan(provider.baseUrl) + '\n' +
|
|
294
|
-
chalk.dim('ANTHROPIC_API_KEY ') + chalk.cyan(
|
|
306
|
+
chalk.dim('ANTHROPIC_API_KEY ') + chalk.cyan('已保存') + '\n\n' +
|
|
295
307
|
chalk.white('下次直接输入 ') + chalk.cyan.bold('claude') + chalk.white(' 即可使用'),
|
|
296
308
|
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
|
|
297
309
|
));
|
|
@@ -322,6 +334,12 @@ program
|
|
|
322
334
|
console.log(chalk.red('\n未配置,请先运行: claw setup\n'));
|
|
323
335
|
return;
|
|
324
336
|
}
|
|
337
|
+
const configProblem = getConfigValidationMessage(config);
|
|
338
|
+
if (configProblem) {
|
|
339
|
+
console.log(chalk.red(`\n配置无效:${configProblem}`));
|
|
340
|
+
console.log(chalk.dim('请运行 claw setup 重新配置。\n'));
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
325
343
|
|
|
326
344
|
const providerKey = await select({ loop: false,
|
|
327
345
|
message: chalk.cyan('选择 AI 厂商'),
|
|
@@ -435,23 +453,19 @@ async function renderStatusBar(apiStatus) {
|
|
|
435
453
|
claudeInstalled,
|
|
436
454
|
env: process.env,
|
|
437
455
|
});
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
: '';
|
|
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)}`;
|
|
456
|
+
const statusLines = buildMenuStatusLines(view, { apiStatus, claudeInstalled });
|
|
457
|
+
cfgPart = statusLines.map((line, index) => {
|
|
458
|
+
if (index === 0) return line.replace('API 正常', chalk.green('API 正常')).replace('API Key 无效', chalk.red('API Key 无效')).replace('网络/服务异常', chalk.yellow('网络/服务异常'));
|
|
459
|
+
if (line.startsWith('环境变量未生效')) return chalk.yellow(line);
|
|
460
|
+
if (line.startsWith('旧模型名')) return chalk.yellow(line);
|
|
461
|
+
if (line.startsWith('主模型')) return line.replace(view.mainModel, chalk.yellow(view.mainModel)).replace(view.fastModel, chalk.yellow(view.fastModel));
|
|
462
|
+
return line;
|
|
463
|
+
}).join('\n ');
|
|
450
464
|
} else {
|
|
451
465
|
cfgPart = chalk.red('●') + ' ' + chalk.dim('未配置');
|
|
452
466
|
}
|
|
453
467
|
|
|
454
|
-
return ` ${claudeIcon} ${claudeText} ${cfgPart}`;
|
|
468
|
+
return config ? ` ${cfgPart}` : ` ${claudeIcon} ${claudeText} ${cfgPart}`;
|
|
455
469
|
}
|
|
456
470
|
|
|
457
471
|
async function runMenu() {
|
|
@@ -464,7 +478,8 @@ async function runMenu() {
|
|
|
464
478
|
|
|
465
479
|
const config = loadConfig();
|
|
466
480
|
let apiStatus; // undefined = skipped, true/false/null = checked
|
|
467
|
-
|
|
481
|
+
const configProblem = config ? getConfigValidationMessage(config) : null;
|
|
482
|
+
if (config && !configProblem) {
|
|
468
483
|
const spinner = ora('正在检测 API 是否通畅...').start();
|
|
469
484
|
apiStatus = await validateKey(config);
|
|
470
485
|
if (apiStatus === true) spinner.succeed('API 连接正常');
|
|
@@ -472,18 +487,23 @@ async function runMenu() {
|
|
|
472
487
|
else spinner.warn('网络异常,无法连接 API');
|
|
473
488
|
}
|
|
474
489
|
|
|
475
|
-
|
|
490
|
+
if (configProblem) {
|
|
491
|
+
console.log(chalk.red(` ● 配置无效:${configProblem}`));
|
|
492
|
+
console.log(chalk.dim(' 请先选择“首次配置 API Key 和模型”重新配置'));
|
|
493
|
+
} else {
|
|
494
|
+
console.log(await renderStatusBar(apiStatus));
|
|
495
|
+
}
|
|
476
496
|
console.log();
|
|
477
497
|
|
|
478
498
|
const action = await select({ loop: false,
|
|
479
499
|
message: chalk.cyan('选择操作'),
|
|
480
500
|
choices: [
|
|
481
|
-
{ name: '🤖 启动 Claude Code', value: 'launch', disabled: !config && '需先完成配置' },
|
|
501
|
+
{ name: '🤖 启动 Claude Code', value: 'launch', disabled: (!config || configProblem) && '需先完成配置' },
|
|
482
502
|
{ name: '📦 安装 Claude Code', value: 'install' },
|
|
483
503
|
{ name: config ? '⚙️ 重新配置(输入新的 API Key)' : '⚙️ 首次配置 API Key 和模型', value: 'setup' },
|
|
484
|
-
{ name: '🔄 切换厂商或模型', value: 'switch', disabled: !config && '需先完成配置' },
|
|
504
|
+
{ name: '🔄 切换厂商或模型', value: 'switch', disabled: (!config || configProblem) && '需先完成配置' },
|
|
485
505
|
{ name: '📊 查看当前配置', value: 'status', disabled: !config && '需先完成配置' },
|
|
486
|
-
{ name: '🔁 重新检测 API', value: 'recheck', disabled: !config && '需先完成配置' },
|
|
506
|
+
{ name: '🔁 重新检测 API', value: 'recheck', disabled: (!config || configProblem) && '需先完成配置' },
|
|
487
507
|
{ name: '🗑 恢复默认(清除所有配置)', value: 'reset', disabled: !config && '没有可清除的配置' },
|
|
488
508
|
{ name: '退出', value: 'exit' },
|
|
489
509
|
],
|
|
@@ -498,7 +518,7 @@ async function runMenu() {
|
|
|
498
518
|
|
|
499
519
|
if (action === 'launch') {
|
|
500
520
|
const cfg = loadConfig();
|
|
501
|
-
if (!cfg) continue;
|
|
521
|
+
if (!cfg || getConfigValidationMessage(cfg)) continue;
|
|
502
522
|
// 启动 claude 后等它退出,然后回菜单
|
|
503
523
|
await new Promise((resolve) => {
|
|
504
524
|
const child = spawn('claude', [], {
|
package/index.js
ADDED
package/lib/config.js
CHANGED
|
@@ -106,6 +106,21 @@ function loadConfig() {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
function validateConfig(config) {
|
|
110
|
+
if (!config || typeof config !== 'object') {
|
|
111
|
+
return { valid: false, message: '未找到配置' };
|
|
112
|
+
}
|
|
113
|
+
for (const key of ['provider', 'baseUrl', 'apiKey', 'model']) {
|
|
114
|
+
if (typeof config[key] !== 'string' || config[key].trim().length === 0) {
|
|
115
|
+
return { valid: false, message: `配置缺少 ${key}` };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!PROVIDERS[config.provider]) {
|
|
119
|
+
return { valid: false, message: `未知厂商 ${config.provider}` };
|
|
120
|
+
}
|
|
121
|
+
return { valid: true };
|
|
122
|
+
}
|
|
123
|
+
|
|
109
124
|
function saveConfig(config) {
|
|
110
125
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
111
126
|
try {
|
|
@@ -113,6 +128,10 @@ function saveConfig(config) {
|
|
|
113
128
|
} catch {}
|
|
114
129
|
}
|
|
115
130
|
|
|
131
|
+
function providerKeyFromBaseUrl(baseUrl) {
|
|
132
|
+
return Object.entries(PROVIDERS).find(([, provider]) => provider.baseUrl === baseUrl)?.[0];
|
|
133
|
+
}
|
|
134
|
+
|
|
116
135
|
function resolveFastModel(provider, model) {
|
|
117
136
|
if (/flash|turbo|haiku|air|lite/i.test(model)) return model;
|
|
118
137
|
return provider?.fastModel || model;
|
|
@@ -145,7 +164,8 @@ function shellQuote(value) {
|
|
|
145
164
|
|
|
146
165
|
// 构造完整的 clawai 环境变量块
|
|
147
166
|
function buildEnvBlock(baseUrl, apiKey, model, fastModel) {
|
|
148
|
-
const
|
|
167
|
+
const provider = providerKeyFromBaseUrl(baseUrl);
|
|
168
|
+
const env = buildClaudeEnv({ provider, baseUrl, apiKey, model, fastModel });
|
|
149
169
|
return [
|
|
150
170
|
'',
|
|
151
171
|
'# clawai-start',
|
|
@@ -224,7 +244,9 @@ module.exports = {
|
|
|
224
244
|
writeEnvToZshrc,
|
|
225
245
|
fetchModels,
|
|
226
246
|
resetConfig,
|
|
247
|
+
validateConfig,
|
|
227
248
|
resolveFastModel,
|
|
249
|
+
providerKeyFromBaseUrl,
|
|
228
250
|
buildClaudeEnv,
|
|
229
251
|
buildEnvBlock,
|
|
230
252
|
classifyValidationStatus,
|
package/lib/panel.js
CHANGED
|
@@ -46,4 +46,25 @@ function buildStatusView(config, options = {}) {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
function buildMenuStatusLines(view, options = {}) {
|
|
50
|
+
const claudeText = options.claudeInstalled ? 'Claude 已安装' : 'Claude 未安装';
|
|
51
|
+
const lines = [
|
|
52
|
+
`${claudeText} · ${view.providerName} · ${apiStatusText(options.apiStatus)}`,
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
if (view.envActive) {
|
|
56
|
+
lines.push('环境变量已生效');
|
|
57
|
+
} else {
|
|
58
|
+
lines.push('环境变量未生效:运行 source ~/.zshrc 或重开终端');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
lines.push(`主模型 ${view.mainModel} · 快速模型 ${view.fastModel}`);
|
|
62
|
+
|
|
63
|
+
if (view.warnings.some((warning) => warning.includes('旧 DeepSeek 模型名'))) {
|
|
64
|
+
lines.push('旧模型名:选择下方“切换厂商或模型”更新');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return lines;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = { buildMenuStatusLines, buildStatusView, apiStatusText, isEnvActive };
|