yingclaw 2.0.7 → 2.0.8

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 CHANGED
@@ -21,7 +21,7 @@ const { execSync, spawn, spawnSync } = require('child_process');
21
21
  const pkg = require('../package.json');
22
22
  const { buildMenuStatusLines, buildStatusView } = require('../lib/panel');
23
23
  const { buildClaudeInstallCommand } = require('../lib/install');
24
- const { clearClaudeDesktopConfig, openClaudeDesktop, writeClaudeDesktopConfig } = require('../lib/desktop');
24
+ const { clearClaudeDesktopConfig, isDesktopConfigured, openClaudeDesktop, writeClaudeDesktopConfig } = require('../lib/desktop');
25
25
 
26
26
  const program = new Command();
27
27
 
@@ -118,6 +118,32 @@ function getSavedConfigHint() {
118
118
  return '⚠ API Key 以明文存储在 ~/.clawai.json';
119
119
  }
120
120
 
121
+ async function offerDesktopSync(chalk, ora, config) {
122
+ if (!isDesktopConfigured()) return;
123
+ const syncDesktop = await confirm({ message: 'Claude 桌面应用已配置,是否同步新模型?', default: true });
124
+ if (!syncDesktop) return;
125
+ const spinner = ora('同步 Claude 桌面应用配置...').start();
126
+ try {
127
+ writeClaudeDesktopConfig(config);
128
+ spinner.succeed(chalk.green('Claude 桌面应用配置已同步'));
129
+ } catch (e) {
130
+ spinner.fail(chalk.red(`桌面配置同步失败: ${e.message}`));
131
+ return;
132
+ }
133
+ if (process.platform === 'darwin') {
134
+ const shouldOpen = await confirm({ message: '是否重启 Claude 桌面应用使新配置生效?', default: true });
135
+ if (shouldOpen) {
136
+ const openSpinner = ora('正在重启 Claude 桌面应用...').start();
137
+ try {
138
+ await openClaudeDesktop();
139
+ openSpinner.succeed(chalk.green('Claude 桌面应用已重启'));
140
+ } catch (e) {
141
+ openSpinner.fail(chalk.red(`打开失败: ${e.message}`));
142
+ }
143
+ }
144
+ }
145
+ }
146
+
121
147
  async function promptModelFromChoices({ chalk, choices, message, backLabel = '↩ 返回上一步', allowManual = true }) {
122
148
  const selected = await select({ loop: false,
123
149
  message: chalk.cyan(message),
@@ -513,7 +539,6 @@ program
513
539
 
514
540
  const spinner = ora('正在恢复 Claude Code 终端默认配置...').start();
515
541
  const cleared = clearClaudeCodeEnv();
516
- await new Promise(r => setTimeout(r, 300));
517
542
 
518
543
  if (cleared.length === 0) {
519
544
  spinner.warn(chalk.yellow('没有找到 Claude Code 终端环境变量,无需恢复'));
@@ -568,9 +593,9 @@ program
568
593
 
569
594
  const spinner = ora('切换中...').start();
570
595
  saveConfig(customConfig);
571
- await new Promise(r => setTimeout(r, 300));
572
596
  spinner.succeed(chalk.green(`API 连接已切换至 ${customConfig.providerName} · ${customConfig.model}`));
573
597
  console.log(chalk.dim('如需让外部 claude 命令使用新模型,请运行 claw code。'));
598
+ await offerDesktopSync(chalk, ora, customConfig);
574
599
  return;
575
600
  }
576
601
 
@@ -616,9 +641,9 @@ program
616
641
  const fastModel = resolveFastModel(provider, model);
617
642
  const newConfig = { ...config, provider: providerKey, model, fastModel, baseUrl: provider.baseUrl, apiKey, availableModels };
618
643
  saveConfig(newConfig);
619
- await new Promise(r => setTimeout(r, 300));
620
644
  spinner.succeed(chalk.green(`API 连接已切换至 ${provider.name} · ${model}`));
621
645
  console.log(chalk.dim('如需让外部 claude 命令使用新模型,请运行 claw code。'));
646
+ await offerDesktopSync(chalk, ora, newConfig);
622
647
  });
623
648
 
624
649
  program
@@ -720,13 +745,16 @@ program
720
745
 
721
746
  const spinner = ora('正在恢复 Claude 桌面应用默认配置...').start();
722
747
  const result = clearClaudeDesktopConfig();
723
- await new Promise(r => setTimeout(r, 300));
724
748
 
725
749
  if (result.result === 'updated') {
726
750
  spinner.succeed(chalk.green('Claude 桌面应用已恢复默认'));
751
+ const cleared = [
752
+ result.dataDir ? require('path').join(result.dataDir, 'configLibrary/') : null,
753
+ result.file,
754
+ ].filter(Boolean);
727
755
  console.log(boxen(
728
756
  chalk.bold('已清除 Claude 桌面应用第三方推理配置:\n\n') +
729
- chalk.cyan(' • ' + result.file) +
757
+ cleared.map(f => chalk.cyan(' • ' + f)).join('\n') +
730
758
  '\n\n' + chalk.dim('如 Claude Desktop 已打开,请完全退出后重新打开。'),
731
759
  { padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
732
760
  ));
@@ -758,9 +786,9 @@ program
758
786
  const cleared = resetConfig();
759
787
  const desktopCleared = clearClaudeDesktopConfig();
760
788
  if (desktopCleared.result === 'updated') {
761
- cleared.push(desktopCleared.file);
789
+ if (desktopCleared.dataDir) cleared.push(require('path').join(desktopCleared.dataDir, 'configLibrary/'));
790
+ if (desktopCleared.file) cleared.push(desktopCleared.file);
762
791
  }
763
- await new Promise(r => setTimeout(r, 300));
764
792
 
765
793
  if (cleared.length === 0) {
766
794
  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) => id === 'deepseek-v4-pro' ? 'deepseek-v4-pro[1m]' : id);
90
- const preferred = ['deepseek-v4-pro[1m]', 'deepseek-v4-flash'];
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(normalizeLegacyDesktopModelId).map(toDesktopModelId))];
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
- const file = options.configFile || getClaudeDesktopConfigPath(options);
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
- if (!current.enterpriseConfig || typeof current.enterpriseConfig !== 'object') {
180
- return { result: 'missing', file };
181
- }
181
+ const next = { ...current };
182
182
 
183
- const enterpriseConfig = { ...current.enterpriseConfig };
184
- for (const key of DESKTOP_GATEWAY_KEYS) {
185
- delete enterpriseConfig[key];
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
- const next = { ...current };
189
- if (Object.keys(enterpriseConfig).length > 0) {
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yingclaw",
3
- "version": "2.0.7",
3
+ "version": "2.0.8",
4
4
  "description": "Claude Code × 国产大模型一键接入:DeepSeek、Kimi、Qwen、MiniMax、GLM、MiMo",
5
5
  "main": "index.js",
6
6
  "bin": {