yingclaw 2.1.6 → 2.1.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
@@ -415,7 +415,7 @@ program
415
415
  console.log(await getBanner());
416
416
 
417
417
  try {
418
- const ver = execSync('claude --version 2>/dev/null', { encoding: 'utf8' }).trim();
418
+ const ver = execSync('claude --version', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
419
419
  console.log(chalk.green(`\n✔ Claude Code 已安装:${ver}\n`));
420
420
  const yes = await confirm({ message: '是否重新安装/更新?', default: false });
421
421
  if (!yes) return;
@@ -435,8 +435,8 @@ program
435
435
 
436
436
  console.log(chalk.dim('\n安装中,实时输出:\n'));
437
437
 
438
- // 实时输出安装日志
439
- const result = spawnSync(installCommand.command, installCommand.args, { stdio: 'inherit' });
438
+ // 实时输出安装日志(Windows 下 npm 是 npm.cmd,需要 shell: true 才能找到)
439
+ const result = spawnSync(installCommand.command, installCommand.args, { stdio: 'inherit', shell: process.platform === 'win32' });
440
440
 
441
441
  if (result.status === 0) {
442
442
  console.log(chalk.green('\n✔ Claude Code 安装成功!'));
@@ -857,11 +857,20 @@ program
857
857
  return;
858
858
  }
859
859
 
860
- const installCmd = buildClaudeInstallCommand('vpn');
861
- const upgradeCmd = { command: installCmd.command, args: [...installCmd.args.slice(0, -1), 'yingclaw@latest'] };
860
+ const network = await select({ loop: false,
861
+ message: chalk.cyan('你的网络环境'),
862
+ choices: [
863
+ { name: '有梯子 / 海外网络(走官方)', value: 'vpn' },
864
+ { name: '国内网络 / 没有梯子(走镜像)', value: 'cn' },
865
+ ],
866
+ });
867
+
868
+ const upgradeArgs = ['install', '-g', 'yingclaw@latest'];
869
+ if (network === 'cn') upgradeArgs.push('--registry=https://registry.npmmirror.com');
870
+ const upgradeCmd = { command: 'npm', args: upgradeArgs };
862
871
 
863
872
  console.log(chalk.dim('\n升级中...\n'));
864
- const result = spawnSync(upgradeCmd.command, upgradeCmd.args, { stdio: 'inherit' });
873
+ const result = spawnSync(upgradeCmd.command, upgradeCmd.args, { stdio: 'inherit', shell: process.platform === 'win32' });
865
874
 
866
875
  if (result.status === 0) {
867
876
  console.log(boxen(
@@ -1042,6 +1051,7 @@ async function runMenu() {
1042
1051
  const child = spawn('claude', [], {
1043
1052
  stdio: 'inherit',
1044
1053
  env: { ...process.env, ...buildClaudeEnv(cfg) },
1054
+ shell: process.platform === 'win32',
1045
1055
  });
1046
1056
  child.on('error', () => {
1047
1057
  console.log(chalk.yellow('\nClaude Code 未找到,请先选择"安装 Claude Code"'));
package/lib/config.js CHANGED
@@ -407,4 +407,5 @@ module.exports = {
407
407
  classifyValidationStatus,
408
408
  PROVIDERS,
409
409
  CONFIG_FILE,
410
+ CLAUDE_ENV_KEYS,
410
411
  };
package/lib/desktop.js CHANGED
@@ -6,6 +6,7 @@ const { spawnSync } = require('child_process');
6
6
  const { normalizeAnthropicBaseUrl } = require('./config');
7
7
 
8
8
  const CLAUDE_DESKTOP_LABEL = 'Claude 桌面应用配置';
9
+ const YINGCLAW_ENTRY_NAME = 'yingclaw';
9
10
  const DESKTOP_GATEWAY_KEYS = [
10
11
  'inferenceProvider',
11
12
  'inferenceGatewayBaseUrl',
@@ -84,6 +85,7 @@ function buildClaudeDesktopEnterpriseConfig(config, options = {}) {
84
85
  };
85
86
  }
86
87
 
88
+ // 只清除 yingclaw 写入的那条 entry,保留用户其它 3P 配置
87
89
  function clearClaudeDesktopConfigLibrary(options = {}) {
88
90
  const dir = options.configLibraryDir || getClaudeDesktopConfigLibraryDir(options);
89
91
  if (!dir || !fs.existsSync(dir)) {
@@ -91,27 +93,37 @@ function clearClaudeDesktopConfigLibrary(options = {}) {
91
93
  }
92
94
 
93
95
  const metaFile = path.join(dir, '_meta.json');
96
+ if (!fs.existsSync(metaFile)) {
97
+ return { result: 'missing', dir };
98
+ }
99
+
94
100
  const meta = readJsonFile(metaFile);
95
- const ids = new Set();
96
- if (typeof meta.appliedId === 'string' && meta.appliedId) {
97
- ids.add(meta.appliedId);
101
+ const entries = Array.isArray(meta.entries) ? meta.entries : [];
102
+ const yingclawEntries = entries.filter((entry) => entry && entry.name === YINGCLAW_ENTRY_NAME);
103
+ if (yingclawEntries.length === 0) {
104
+ return { result: 'missing', dir };
98
105
  }
99
- if (Array.isArray(meta.entries)) {
100
- for (const entry of meta.entries) {
101
- if (entry && typeof entry.id === 'string' && entry.id) {
102
- ids.add(entry.id);
106
+
107
+ for (const entry of yingclawEntries) {
108
+ if (entry && typeof entry.id === 'string' && entry.id) {
109
+ const file = path.join(dir, `${entry.id}.json`);
110
+ if (fs.existsSync(file)) {
111
+ fs.unlinkSync(file);
103
112
  }
104
113
  }
105
114
  }
106
115
 
107
- for (const id of ids) {
108
- const file = path.join(dir, `${id}.json`);
109
- if (fs.existsSync(file)) {
110
- fs.unlinkSync(file);
111
- }
112
- }
113
- if (fs.existsSync(metaFile)) {
116
+ const remaining = entries.filter((entry) => entry && entry.name !== YINGCLAW_ENTRY_NAME);
117
+ const yingclawIds = new Set(yingclawEntries.map((e) => e.id));
118
+ const newAppliedId = yingclawIds.has(meta.appliedId)
119
+ ? (remaining[0]?.id || '')
120
+ : meta.appliedId;
121
+
122
+ if (remaining.length === 0 && !newAppliedId) {
114
123
  fs.unlinkSync(metaFile);
124
+ } else {
125
+ const newMeta = { ...meta, appliedId: newAppliedId, entries: remaining };
126
+ fs.writeFileSync(metaFile, JSON.stringify(newMeta, null, 2) + '\n');
115
127
  }
116
128
 
117
129
  return { result: 'updated', dir };
@@ -132,7 +144,10 @@ function writeClaudeDesktopConfig(config, options = {}) {
132
144
  const configLibraryDir = path.join(dataDir, 'configLibrary');
133
145
  const existingMetaFile = path.join(configLibraryDir, '_meta.json');
134
146
  const existingMeta = readJsonFile(existingMetaFile);
135
- const uuid = existingMeta.appliedId || options.uuid || crypto.randomUUID();
147
+ const existingEntries = Array.isArray(existingMeta.entries) ? existingMeta.entries : [];
148
+ // 用 name='yingclaw' 标记自己的 entry;不复用用户的 appliedId 以免覆盖别家的配置
149
+ const existingYingclaw = existingEntries.find((entry) => entry && entry.name === YINGCLAW_ENTRY_NAME);
150
+ const uuid = existingYingclaw?.id || options.uuid || crypto.randomUUID();
136
151
 
137
152
  const models = collectModels(config);
138
153
 
@@ -146,11 +161,13 @@ function writeClaudeDesktopConfig(config, options = {}) {
146
161
  deploymentOrganizationUuid: uuid,
147
162
  };
148
163
 
164
+ const otherEntries = existingEntries.filter((entry) => entry && entry.name !== YINGCLAW_ENTRY_NAME);
149
165
  const meta = {
166
+ ...existingMeta,
150
167
  appliedId: uuid,
151
- entries: [{ id: uuid, name: 'yingclaw' }],
152
- isManaged: false,
153
- platform: process.platform,
168
+ entries: [...otherEntries, { id: uuid, name: YINGCLAW_ENTRY_NAME }],
169
+ isManaged: typeof existingMeta.isManaged === 'boolean' ? existingMeta.isManaged : false,
170
+ platform: existingMeta.platform || process.platform,
154
171
  };
155
172
 
156
173
  fs.mkdirSync(configLibraryDir, { recursive: true });
@@ -210,7 +227,8 @@ function isDesktopConfigured(options = {}) {
210
227
  const dir = options.dataDir || getClaudeDesktopDataDir(options);
211
228
  if (!dir) return false;
212
229
  const meta = readJsonFile(path.join(dir, 'configLibrary', '_meta.json'));
213
- return typeof meta.appliedId === 'string' && meta.appliedId.length > 0;
230
+ if (!Array.isArray(meta.entries)) return false;
231
+ return meta.entries.some((entry) => entry && entry.name === YINGCLAW_ENTRY_NAME);
214
232
  }
215
233
 
216
234
  function buildClaudeDesktopOpenCommands(platform = process.platform) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yingclaw",
3
- "version": "2.1.6",
3
+ "version": "2.1.8",
4
4
  "description": "Claude Code × 国产大模型一键接入:DeepSeek、Kimi、Qwen、MiniMax、GLM、MiMo",
5
5
  "main": "index.js",
6
6
  "bin": {