yingclaw 2.0.7 → 2.0.9
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/bin/cli.js +59 -12
- package/lib/config.js +9 -4
- package/lib/desktop.js +32 -24
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -16,12 +16,13 @@ const {
|
|
|
16
16
|
buildClaudeEnv,
|
|
17
17
|
classifyValidationStatus,
|
|
18
18
|
PROVIDERS,
|
|
19
|
+
CLAUDE_ENV_KEYS,
|
|
19
20
|
} = require('../lib/config');
|
|
20
21
|
const { execSync, spawn, spawnSync } = require('child_process');
|
|
21
22
|
const pkg = require('../package.json');
|
|
22
23
|
const { buildMenuStatusLines, buildStatusView } = require('../lib/panel');
|
|
23
24
|
const { buildClaudeInstallCommand } = require('../lib/install');
|
|
24
|
-
const { clearClaudeDesktopConfig, openClaudeDesktop, writeClaudeDesktopConfig } = require('../lib/desktop');
|
|
25
|
+
const { clearClaudeDesktopConfig, isDesktopConfigured, openClaudeDesktop, writeClaudeDesktopConfig } = require('../lib/desktop');
|
|
25
26
|
|
|
26
27
|
const program = new Command();
|
|
27
28
|
|
|
@@ -118,6 +119,32 @@ function getSavedConfigHint() {
|
|
|
118
119
|
return '⚠ API Key 以明文存储在 ~/.clawai.json';
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
async function offerDesktopSync(chalk, ora, config) {
|
|
123
|
+
if (!isDesktopConfigured()) return;
|
|
124
|
+
const syncDesktop = await confirm({ message: 'Claude 桌面应用已配置,是否同步新模型?', default: true });
|
|
125
|
+
if (!syncDesktop) return;
|
|
126
|
+
const spinner = ora('同步 Claude 桌面应用配置...').start();
|
|
127
|
+
try {
|
|
128
|
+
writeClaudeDesktopConfig(config);
|
|
129
|
+
spinner.succeed(chalk.green('Claude 桌面应用配置已同步'));
|
|
130
|
+
} catch (e) {
|
|
131
|
+
spinner.fail(chalk.red(`桌面配置同步失败: ${e.message}`));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (process.platform === 'darwin') {
|
|
135
|
+
const shouldOpen = await confirm({ message: '是否重启 Claude 桌面应用使新配置生效?', default: true });
|
|
136
|
+
if (shouldOpen) {
|
|
137
|
+
const openSpinner = ora('正在重启 Claude 桌面应用...').start();
|
|
138
|
+
try {
|
|
139
|
+
await openClaudeDesktop();
|
|
140
|
+
openSpinner.succeed(chalk.green('Claude 桌面应用已重启'));
|
|
141
|
+
} catch (e) {
|
|
142
|
+
openSpinner.fail(chalk.red(`打开失败: ${e.message}`));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
121
148
|
async function promptModelFromChoices({ chalk, choices, message, backLabel = '↩ 返回上一步', allowManual = true }) {
|
|
122
149
|
const selected = await select({ loop: false,
|
|
123
150
|
message: chalk.cyan(message),
|
|
@@ -513,19 +540,21 @@ program
|
|
|
513
540
|
|
|
514
541
|
const spinner = ora('正在恢复 Claude Code 终端默认配置...').start();
|
|
515
542
|
const cleared = clearClaudeCodeEnv();
|
|
516
|
-
await new Promise(r => setTimeout(r, 300));
|
|
517
543
|
|
|
518
544
|
if (cleared.length === 0) {
|
|
519
545
|
spinner.warn(chalk.yellow('没有找到 Claude Code 终端环境变量,无需恢复'));
|
|
520
546
|
} else {
|
|
521
547
|
spinner.succeed(chalk.green('Claude Code 终端已恢复默认'));
|
|
522
|
-
const
|
|
548
|
+
const isWin = cleared.includes('Windows 用户环境变量');
|
|
549
|
+
const resetNote = isWin
|
|
523
550
|
? '注:当前终端的环境变量还在内存中,重新打开 PowerShell / CMD 后才彻底清除'
|
|
524
|
-
: '
|
|
551
|
+
: '注:当前终端的环境变量还在内存中,重开终端后生效,或在当前终端执行:';
|
|
552
|
+
const unsetCmd = isWin ? null : `unset ${CLAUDE_ENV_KEYS.join(' ')}`;
|
|
525
553
|
console.log(boxen(
|
|
526
554
|
chalk.bold('已清除以下位置中的 Claude Code 终端环境变量:\n\n') +
|
|
527
555
|
cleared.map(f => chalk.cyan(' • ' + f)).join('\n') +
|
|
528
|
-
'\n\n' + chalk.dim(resetNote)
|
|
556
|
+
'\n\n' + chalk.dim(resetNote) +
|
|
557
|
+
(unsetCmd ? '\n' + chalk.cyan(unsetCmd) : ''),
|
|
529
558
|
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
|
|
530
559
|
));
|
|
531
560
|
}
|
|
@@ -568,9 +597,9 @@ program
|
|
|
568
597
|
|
|
569
598
|
const spinner = ora('切换中...').start();
|
|
570
599
|
saveConfig(customConfig);
|
|
571
|
-
await new Promise(r => setTimeout(r, 300));
|
|
572
600
|
spinner.succeed(chalk.green(`API 连接已切换至 ${customConfig.providerName} · ${customConfig.model}`));
|
|
573
601
|
console.log(chalk.dim('如需让外部 claude 命令使用新模型,请运行 claw code。'));
|
|
602
|
+
await offerDesktopSync(chalk, ora, customConfig);
|
|
574
603
|
return;
|
|
575
604
|
}
|
|
576
605
|
|
|
@@ -616,9 +645,9 @@ program
|
|
|
616
645
|
const fastModel = resolveFastModel(provider, model);
|
|
617
646
|
const newConfig = { ...config, provider: providerKey, model, fastModel, baseUrl: provider.baseUrl, apiKey, availableModels };
|
|
618
647
|
saveConfig(newConfig);
|
|
619
|
-
await new Promise(r => setTimeout(r, 300));
|
|
620
648
|
spinner.succeed(chalk.green(`API 连接已切换至 ${provider.name} · ${model}`));
|
|
621
649
|
console.log(chalk.dim('如需让外部 claude 命令使用新模型,请运行 claw code。'));
|
|
650
|
+
await offerDesktopSync(chalk, ora, newConfig);
|
|
622
651
|
});
|
|
623
652
|
|
|
624
653
|
program
|
|
@@ -720,16 +749,34 @@ program
|
|
|
720
749
|
|
|
721
750
|
const spinner = ora('正在恢复 Claude 桌面应用默认配置...').start();
|
|
722
751
|
const result = clearClaudeDesktopConfig();
|
|
723
|
-
await new Promise(r => setTimeout(r, 300));
|
|
724
752
|
|
|
725
753
|
if (result.result === 'updated') {
|
|
726
754
|
spinner.succeed(chalk.green('Claude 桌面应用已恢复默认'));
|
|
755
|
+
const cleared = [
|
|
756
|
+
result.dataDir ? require('path').join(result.dataDir, 'configLibrary/') : null,
|
|
757
|
+
result.file,
|
|
758
|
+
].filter(Boolean);
|
|
727
759
|
console.log(boxen(
|
|
728
760
|
chalk.bold('已清除 Claude 桌面应用第三方推理配置:\n\n') +
|
|
729
|
-
chalk.cyan(' • ' +
|
|
730
|
-
'\n\n' + chalk.dim('如 Claude Desktop 已打开,请完全退出后重新打开。'),
|
|
761
|
+
cleared.map(f => chalk.cyan(' • ' + f)).join('\n'),
|
|
731
762
|
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
|
|
732
763
|
));
|
|
764
|
+
|
|
765
|
+
if (process.platform === 'darwin') {
|
|
766
|
+
const shouldReopen = await confirm({ message: '是否现在重启 Claude 桌面应用使默认配置生效?', default: true });
|
|
767
|
+
if (shouldReopen) {
|
|
768
|
+
const openSpinner = ora('正在重启 Claude 桌面应用...').start();
|
|
769
|
+
try {
|
|
770
|
+
await openClaudeDesktop();
|
|
771
|
+
openSpinner.succeed(chalk.green('Claude 桌面应用已重启(已恢复 1P 模式)'));
|
|
772
|
+
} catch (e) {
|
|
773
|
+
openSpinner.fail(chalk.red(`打开失败: ${e.message}`));
|
|
774
|
+
console.log(chalk.dim('请手动完全退出 Claude 后重新打开。'));
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
} else {
|
|
778
|
+
console.log(chalk.dim('请手动完全退出 Claude 后重新打开。'));
|
|
779
|
+
}
|
|
733
780
|
} else {
|
|
734
781
|
spinner.warn(chalk.yellow('没有找到 Claude 桌面应用第三方推理配置,无需恢复'));
|
|
735
782
|
}
|
|
@@ -758,9 +805,9 @@ program
|
|
|
758
805
|
const cleared = resetConfig();
|
|
759
806
|
const desktopCleared = clearClaudeDesktopConfig();
|
|
760
807
|
if (desktopCleared.result === 'updated') {
|
|
761
|
-
cleared.push(desktopCleared.
|
|
808
|
+
if (desktopCleared.dataDir) cleared.push(require('path').join(desktopCleared.dataDir, 'configLibrary/'));
|
|
809
|
+
if (desktopCleared.file) cleared.push(desktopCleared.file);
|
|
762
810
|
}
|
|
763
|
-
await new Promise(r => setTimeout(r, 300));
|
|
764
811
|
|
|
765
812
|
if (cleared.length === 0) {
|
|
766
813
|
spinner.warn(chalk.yellow('没有找到任何配置,无需清除'));
|
package/lib/config.js
CHANGED
|
@@ -22,10 +22,10 @@ const PROVIDERS = {
|
|
|
22
22
|
name: 'DeepSeek',
|
|
23
23
|
baseUrl: 'https://api.deepseek.com/anthropic',
|
|
24
24
|
modelsUrl: 'https://api.deepseek.com/v1/models',
|
|
25
|
-
fastModel: 'deepseek-v4-flash',
|
|
25
|
+
fastModel: 'deepseek-v4-flash[1m]',
|
|
26
26
|
models: [
|
|
27
27
|
{ name: 'DeepSeek V4 Pro(强力)', value: 'deepseek-v4-pro[1m]' },
|
|
28
|
-
{ name: 'DeepSeek V4 Flash(快速)', value: 'deepseek-v4-flash' },
|
|
28
|
+
{ name: 'DeepSeek V4 Flash(快速)', value: 'deepseek-v4-flash[1m]' },
|
|
29
29
|
],
|
|
30
30
|
},
|
|
31
31
|
kimi: {
|
|
@@ -41,6 +41,7 @@ const PROVIDERS = {
|
|
|
41
41
|
name: '阿里云百炼 (Qwen)',
|
|
42
42
|
baseUrl: 'https://dashscope.aliyuncs.com/apps/anthropic',
|
|
43
43
|
modelsUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1/models',
|
|
44
|
+
fastModel: 'qwen3.5-plus',
|
|
44
45
|
models: [
|
|
45
46
|
{ name: 'Qwen3 Max(强力)', value: 'qwen3-max' },
|
|
46
47
|
{ name: 'Qwen3 Plus(均衡)', value: 'qwen3-plus' },
|
|
@@ -86,8 +87,12 @@ const PROVIDERS = {
|
|
|
86
87
|
function normalizeModelIds(providerKey, ids) {
|
|
87
88
|
if (providerKey !== 'deepseek') return ids;
|
|
88
89
|
|
|
89
|
-
const mapped = ids.map((id) =>
|
|
90
|
-
|
|
90
|
+
const mapped = ids.map((id) => {
|
|
91
|
+
if (id === 'deepseek-v4-pro') return 'deepseek-v4-pro[1m]';
|
|
92
|
+
if (id === 'deepseek-v4-flash') return 'deepseek-v4-flash[1m]';
|
|
93
|
+
return id;
|
|
94
|
+
});
|
|
95
|
+
const preferred = ['deepseek-v4-pro[1m]', 'deepseek-v4-flash[1m]'];
|
|
91
96
|
return [
|
|
92
97
|
...preferred.filter((id) => mapped.includes(id)),
|
|
93
98
|
...mapped.filter((id) => !preferred.includes(id)),
|
package/lib/desktop.js
CHANGED
|
@@ -51,10 +51,6 @@ function readJsonFile(file) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
function normalizeLegacyDesktopModelId(model) {
|
|
55
|
-
return model.replace(/\[\w+\]$/, '');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
54
|
function toDesktopModelId(model) {
|
|
59
55
|
return model.startsWith('claude-') ? model : `claude-${model}`;
|
|
60
56
|
}
|
|
@@ -63,7 +59,7 @@ function collectModels(config) {
|
|
|
63
59
|
const list = Array.isArray(config.availableModels) && config.availableModels.length > 0
|
|
64
60
|
? [config.model, config.fastModel, ...config.availableModels]
|
|
65
61
|
: [config.model, config.fastModel];
|
|
66
|
-
return [...new Set(list.filter(Boolean).map(
|
|
62
|
+
return [...new Set(list.filter(Boolean).map(toDesktopModelId))];
|
|
67
63
|
}
|
|
68
64
|
|
|
69
65
|
// 按官方 schema:所有值必须是字符串(包括布尔、数组都序列化)
|
|
@@ -170,35 +166,46 @@ function writeClaudeDesktopConfig(config, options = {}) {
|
|
|
170
166
|
}
|
|
171
167
|
|
|
172
168
|
function clearClaudeDesktopConfig(options = {}) {
|
|
173
|
-
|
|
169
|
+
// Derive dataDir only when configFile is not explicitly overridden (to avoid touching real system dirs in tests)
|
|
170
|
+
const dataDir = options.dataDir || (options.configFile ? null : getClaudeDesktopDataDir(options));
|
|
171
|
+
const file = options.configFile || (dataDir ? path.join(dataDir, 'claude_desktop_config.json') : null);
|
|
172
|
+
|
|
173
|
+
// Clear configLibrary regardless of whether the main config file exists
|
|
174
|
+
const libResult = clearClaudeDesktopConfigLibrary({ ...options, dataDir });
|
|
175
|
+
|
|
174
176
|
if (!file || !fs.existsSync(file)) {
|
|
175
|
-
return { result: 'missing', file };
|
|
177
|
+
return { result: libResult.result === 'updated' ? 'updated' : 'missing', file, dataDir };
|
|
176
178
|
}
|
|
177
179
|
|
|
178
180
|
const current = readJsonFile(file);
|
|
179
|
-
|
|
180
|
-
return { result: 'missing', file };
|
|
181
|
-
}
|
|
181
|
+
const next = { ...current };
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
// Remove legacy enterpriseConfig gateway keys
|
|
184
|
+
if (next.enterpriseConfig && typeof next.enterpriseConfig === 'object') {
|
|
185
|
+
const enterpriseConfig = { ...next.enterpriseConfig };
|
|
186
|
+
for (const key of DESKTOP_GATEWAY_KEYS) {
|
|
187
|
+
delete enterpriseConfig[key];
|
|
188
|
+
}
|
|
189
|
+
if (Object.keys(enterpriseConfig).length > 0) {
|
|
190
|
+
next.enterpriseConfig = enterpriseConfig;
|
|
191
|
+
} else {
|
|
192
|
+
delete next.enterpriseConfig;
|
|
193
|
+
}
|
|
186
194
|
}
|
|
187
195
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
next.enterpriseConfig = enterpriseConfig;
|
|
191
|
-
} else {
|
|
192
|
-
delete next.enterpriseConfig;
|
|
193
|
-
}
|
|
194
|
-
if (Object.keys(next).length === 1 && next.deploymentMode === '3p') {
|
|
195
|
-
delete next.deploymentMode;
|
|
196
|
-
}
|
|
196
|
+
// Always remove deploymentMode when clearing 3P config
|
|
197
|
+
delete next.deploymentMode;
|
|
197
198
|
|
|
198
199
|
fs.writeFileSync(file, JSON.stringify(next, null, 2) + '\n');
|
|
199
|
-
clearClaudeDesktopConfigLibrary({ ...options, dataDir: path.dirname(file) });
|
|
200
200
|
|
|
201
|
-
return { result: 'updated', file };
|
|
201
|
+
return { result: 'updated', file, dataDir };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function isDesktopConfigured(options = {}) {
|
|
205
|
+
const dir = options.dataDir || getClaudeDesktopDataDir(options);
|
|
206
|
+
if (!dir) return false;
|
|
207
|
+
const meta = readJsonFile(path.join(dir, 'configLibrary', '_meta.json'));
|
|
208
|
+
return typeof meta.appliedId === 'string' && meta.appliedId.length > 0;
|
|
202
209
|
}
|
|
203
210
|
|
|
204
211
|
function buildClaudeDesktopOpenCommands(platform = process.platform) {
|
|
@@ -251,6 +258,7 @@ module.exports = {
|
|
|
251
258
|
clearClaudeDesktopConfig,
|
|
252
259
|
getClaudeDesktopConfigPath,
|
|
253
260
|
getClaudeDesktopDataDir,
|
|
261
|
+
isDesktopConfigured,
|
|
254
262
|
openClaudeDesktop,
|
|
255
263
|
writeClaudeDesktopConfig,
|
|
256
264
|
CLAUDE_DESKTOP_LABEL,
|