yingclaw 2.5.18 → 2.5.19

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 {
21
21
  } = require('../lib/config');
22
22
  const { execSync, spawn, spawnSync } = require('child_process');
23
23
  const pkg = require('../package.json');
24
- const { getGatewayAutostartStatus, installGatewayAutostart } = require('../lib/autostart');
24
+ const { getGatewayAutostartStatus, installGatewayAutostart, removeGatewayAutostart } = require('../lib/autostart');
25
25
  const { buildMenuStatusLines, buildStatusView } = require('../lib/panel');
26
26
  const { buildClaudeInstallCommand, checkNodeEnv, getNodeInstallGuide, getInstallFailureHints } = require('../lib/install');
27
27
  const { clearClaudeDesktopConfig, isDesktopConfigured, openClaudeDesktop, writeClaudeDesktopConfig } = require('../lib/desktop');
@@ -102,6 +102,20 @@ function getSavedConfigHint() {
102
102
  return '⚠ API Key 以明文存储在 ~/.clawai.json';
103
103
  }
104
104
 
105
+ function clearCurrentProcessClaudeEnv() {
106
+ for (const key of CLEAR_CLAUDE_ENV_KEYS) {
107
+ delete process.env[key];
108
+ }
109
+ }
110
+
111
+ function desktopConfigClearedPaths(result) {
112
+ const path = require('path');
113
+ return [
114
+ ...(Array.isArray(result.dataDirs) ? result.dataDirs.map(dir => path.join(dir, 'configLibrary/')) : []),
115
+ ...(Array.isArray(result.files) ? result.files : [result.file]),
116
+ ].filter(Boolean);
117
+ }
118
+
105
119
  async function offerDesktopSync(chalk, ora, config) {
106
120
  if (!isDesktopConfigured()) return;
107
121
  const syncDesktop = await confirm({ message: 'Claude 桌面应用已配置,是否同步新模型?', default: true });
@@ -789,7 +803,10 @@ program
789
803
  spinner.warn(chalk.yellow('当前系统暂不支持自动配置 Claude 桌面应用'));
790
804
  return;
791
805
  }
792
- spinner.succeed(chalk.green(`Claude 桌面应用配置已写入 → ${result.file}`));
806
+ const extraFiles = Array.isArray(result.files) && result.files.length > 1
807
+ ? chalk.dim(`(已同步 ${result.files.length} 个配置位置)`)
808
+ : '';
809
+ spinner.succeed(chalk.green(`Claude 桌面应用配置已写入 → ${result.file}`) + extraFiles);
793
810
  } catch (e) {
794
811
  spinner.fail(chalk.red(`写入失败: ${e.message}`));
795
812
  return;
@@ -882,10 +899,7 @@ program
882
899
 
883
900
  if (result.result === 'updated') {
884
901
  spinner.succeed(chalk.green('Claude 桌面应用已恢复默认'));
885
- const cleared = [
886
- result.dataDir ? require('path').join(result.dataDir, 'configLibrary/') : null,
887
- result.file,
888
- ].filter(Boolean);
902
+ const cleared = desktopConfigClearedPaths(result);
889
903
  console.log(boxen(
890
904
  chalk.bold('已清除 Claude 桌面应用第三方推理配置:\n\n') +
891
905
  cleared.map(f => chalk.cyan(' • ' + f)).join('\n'),
@@ -942,8 +956,11 @@ program
942
956
  const cleared = resetConfig();
943
957
  const desktopCleared = clearClaudeDesktopConfig();
944
958
  if (desktopCleared.result === 'updated') {
945
- if (desktopCleared.dataDir) cleared.push(require('path').join(desktopCleared.dataDir, 'configLibrary/'));
946
- if (desktopCleared.file) cleared.push(desktopCleared.file);
959
+ cleared.push(...desktopConfigClearedPaths(desktopCleared));
960
+ }
961
+ const autostartCleared = removeGatewayAutostart();
962
+ if (autostartCleared.result === 'removed' && autostartCleared.file) {
963
+ cleared.push(autostartCleared.file);
947
964
  }
948
965
 
949
966
  if (cleared.length === 0) {
@@ -1272,10 +1289,13 @@ async function runMenu() {
1272
1289
  });
1273
1290
 
1274
1291
  // 改 config 的命令需要刷新缓存
1275
- if (['config', 'switch', 'reset', 'code-reset'].includes(resolvedAction)) {
1292
+ if (['config', 'switch', 'reset', 'code-reset', 'desktop-reset'].includes(resolvedAction)) {
1276
1293
  lastCheckResult = undefined;
1277
1294
  lastCheckedHash = null;
1278
1295
  }
1296
+ if (process.platform === 'win32' && ['reset', 'code-reset'].includes(resolvedAction)) {
1297
+ clearCurrentProcessClaudeEnv();
1298
+ }
1279
1299
  // 安装 Claude 后刷新检测缓存
1280
1300
  if (resolvedAction === 'install') {
1281
1301
  invalidateClaudeInstalledCache();
package/lib/autostart.js CHANGED
@@ -188,6 +188,36 @@ function installGatewayAutostart(options = {}) {
188
188
  return { result: 'installed', file };
189
189
  }
190
190
 
191
+ function removeGatewayAutostart(options = {}) {
192
+ const platform = options.platform || process.platform;
193
+ if (platform === 'win32') {
194
+ const file = options.file || getWindowsStartupScriptPath(options);
195
+ if (!fs.existsSync(file)) {
196
+ return { result: 'missing', file };
197
+ }
198
+ fs.unlinkSync(file);
199
+ return { result: 'removed', file };
200
+ }
201
+
202
+ if (platform !== 'darwin') {
203
+ return { result: 'unsupported', file: null };
204
+ }
205
+
206
+ const homeDir = options.homeDir || os.homedir();
207
+ const file = options.file || getMacLaunchAgentPath({ homeDir });
208
+ const uid = options.uid || (typeof process.getuid === 'function' ? process.getuid() : null);
209
+ const runner = options.runner || spawnSync;
210
+
211
+ if (uid != null) {
212
+ runLaunchctl(runner, ['bootout', `gui/${uid}/${GATEWAY_LAUNCH_AGENT_LABEL}`], { optional: true });
213
+ }
214
+ if (!fs.existsSync(file)) {
215
+ return { result: 'missing', file };
216
+ }
217
+ fs.unlinkSync(file);
218
+ return { result: 'removed', file };
219
+ }
220
+
191
221
  function getGatewayAutostartStatus(options = {}) {
192
222
  const platform = options.platform || process.platform;
193
223
  if (platform === 'win32') {
@@ -237,4 +267,5 @@ module.exports = {
237
267
  getMacLaunchAgentPath,
238
268
  getWindowsStartupScriptPath,
239
269
  installGatewayAutostart,
270
+ removeGatewayAutostart,
240
271
  };
package/lib/config.js CHANGED
@@ -407,6 +407,9 @@ function clearClaudeCodeEnv(options = {}) {
407
407
 
408
408
  if (platform === 'win32') {
409
409
  runWindowsEnvCommands(buildWindowsClearEnvCommands(), options.runner || spawnSync, { ignoreErrors: true });
410
+ for (const key of CLEAR_CLAUDE_ENV_KEYS) {
411
+ delete process.env[key];
412
+ }
410
413
  cleared.push(WINDOWS_ENV_LABEL);
411
414
  return cleared;
412
415
  }
package/lib/desktop.js CHANGED
@@ -55,6 +55,25 @@ function getClaudeDesktopDataDir(options = {}) {
55
55
  return null;
56
56
  }
57
57
 
58
+ function getClaudeDesktopDataDirs(options = {}) {
59
+ if (options.dataDir) return [options.dataDir];
60
+
61
+ const platform = options.platform || process.platform;
62
+ const homeDir = options.homeDir || os.homedir();
63
+
64
+ if (platform === 'win32') {
65
+ const roamingAppData = options.appData || process.env.APPDATA || [homeDir, 'AppData', 'Roaming'].join('\\');
66
+ const localAppData = options.localAppData || process.env.LOCALAPPDATA || [homeDir, 'AppData', 'Local'].join('\\');
67
+ return [...new Set([
68
+ roamingAppData + '\\Claude-3p',
69
+ localAppData + '\\Claude-3p',
70
+ ])];
71
+ }
72
+
73
+ const dataDir = getClaudeDesktopDataDir(options);
74
+ return dataDir ? [dataDir] : [];
75
+ }
76
+
58
77
  function getClaudeDesktopConfigPath(options = {}) {
59
78
  const platform = options.platform || process.platform;
60
79
  const dataDir = options.dataDir || getClaudeDesktopDataDir(options);
@@ -172,7 +191,8 @@ function buildClaudeDesktopDirectEnterpriseConfig(config, options = {}) {
172
191
 
173
192
  // 只清除 yingclaw 写入的那条 entry,保留用户其它 3P 配置
174
193
  function clearClaudeDesktopConfigLibrary(options = {}) {
175
- const dir = options.configLibraryDir || getClaudeDesktopConfigLibraryDir(options);
194
+ const dataDir = options.dataDir || getClaudeDesktopDataDir(options);
195
+ const dir = options.configLibraryDir || (dataDir ? path.join(dataDir, 'configLibrary') : null);
176
196
  if (!dir || !fs.existsSync(dir)) {
177
197
  return { result: 'missing', dir };
178
198
  }
@@ -214,17 +234,11 @@ function clearClaudeDesktopConfigLibrary(options = {}) {
214
234
  return { result: 'updated', dir };
215
235
  }
216
236
 
217
- // 写入 Claude-3p/configLibrary/ 下的 enterprise config 条目(主进程从此处读取)
218
- function writeClaudeDesktopConfig(config, options = {}) {
237
+ function writeClaudeDesktopConfigToDir(config, options, dataDir) {
219
238
  const effectiveConfig = options.direct
220
239
  ? config
221
240
  : ensureDesktopGatewayConfig(config, options);
222
241
 
223
- const dataDir = options.dataDir || getClaudeDesktopDataDir(options);
224
- if (!dataDir) {
225
- return { result: 'unsupported', file: null };
226
- }
227
-
228
242
  const configLibraryDir = path.join(dataDir, 'configLibrary');
229
243
  const existingMetaFile = path.join(configLibraryDir, '_meta.json');
230
244
  const existingMeta = readJsonFile(existingMetaFile);
@@ -257,7 +271,7 @@ function writeClaudeDesktopConfig(config, options = {}) {
257
271
  appliedId: uuid,
258
272
  entries: [...otherEntries, { id: uuid, name: YINGCLAW_ENTRY_NAME }],
259
273
  isManaged: typeof existingMeta.isManaged === 'boolean' ? existingMeta.isManaged : false,
260
- platform: existingMeta.platform || process.platform,
274
+ platform: existingMeta.platform || options.platform || process.platform,
261
275
  };
262
276
 
263
277
  fs.mkdirSync(configLibraryDir, { recursive: true });
@@ -295,9 +309,32 @@ function writeClaudeDesktopConfig(config, options = {}) {
295
309
  return { result: beforeEntry === entryBody ? 'unchanged' : 'updated', file, config: effectiveConfig };
296
310
  }
297
311
 
298
- function clearClaudeDesktopConfig(options = {}) {
312
+ // 写入 Claude-3p/configLibrary/ 下的 enterprise config 条目(主进程从此处读取)
313
+ function writeClaudeDesktopConfig(config, options = {}) {
314
+ const dataDirs = options.configFile
315
+ ? [options.dataDir || path.dirname(options.configFile)]
316
+ : getClaudeDesktopDataDirs(options);
317
+ if (dataDirs.length === 0) {
318
+ return { result: 'unsupported', file: null, files: [] };
319
+ }
320
+
321
+ const results = dataDirs.map((dataDir, index) => {
322
+ const writeOptions = index === 0
323
+ ? options
324
+ : { ...options, configFile: null, dataDir };
325
+ return writeClaudeDesktopConfigToDir(config, writeOptions, dataDir);
326
+ });
327
+ const primary = results[0];
328
+ return {
329
+ ...primary,
330
+ result: results.some((item) => item.result === 'updated') ? 'updated' : primary.result,
331
+ files: results.map((item) => item.file).filter(Boolean),
332
+ dataDirs,
333
+ };
334
+ }
335
+
336
+ function clearClaudeDesktopConfigAtDir(options = {}, dataDir) {
299
337
  // Derive dataDir only when configFile is not explicitly overridden (to avoid touching real system dirs in tests)
300
- const dataDir = options.dataDir || (options.configFile ? null : getClaudeDesktopDataDir(options));
301
338
  const file = options.configFile || (dataDir ? path.join(dataDir, 'claude_desktop_config.json') : null);
302
339
 
303
340
  // Clear configLibrary regardless of whether the main config file exists
@@ -320,7 +357,7 @@ function clearClaudeDesktopConfig(options = {}) {
320
357
  // Remove legacy enterpriseConfig gateway keys
321
358
  if (next.enterpriseConfig && typeof next.enterpriseConfig === 'object') {
322
359
  const enterpriseConfig = { ...next.enterpriseConfig };
323
- for (const key of DESKTOP_GATEWAY_KEYS) {
360
+ for (const key of MAC_POLICY_KEYS) {
324
361
  delete enterpriseConfig[key];
325
362
  }
326
363
  if (Object.keys(enterpriseConfig).length > 0) {
@@ -338,12 +375,36 @@ function clearClaudeDesktopConfig(options = {}) {
338
375
  return { result: 'updated', file, dataDir };
339
376
  }
340
377
 
378
+ function clearClaudeDesktopConfig(options = {}) {
379
+ const dataDirs = options.configFile
380
+ ? [options.dataDir || path.dirname(options.configFile)]
381
+ : getClaudeDesktopDataDirs(options);
382
+
383
+ if (dataDirs.length === 0) {
384
+ return { result: 'missing', file: null, files: [], dataDir: null, dataDirs: [] };
385
+ }
386
+
387
+ const results = dataDirs.map((dataDir, index) => {
388
+ const clearOptions = index === 0
389
+ ? options
390
+ : { ...options, configFile: null, dataDir };
391
+ return clearClaudeDesktopConfigAtDir(clearOptions, dataDir);
392
+ });
393
+ const primary = results[0];
394
+ return {
395
+ ...primary,
396
+ result: results.some((item) => item.result === 'updated') ? 'updated' : primary.result,
397
+ files: results.map((item) => item.file).filter(Boolean),
398
+ dataDirs,
399
+ };
400
+ }
401
+
341
402
  function isDesktopConfigured(options = {}) {
342
- const dir = options.dataDir || getClaudeDesktopDataDir(options);
343
- if (!dir) return false;
344
- const meta = readJsonFile(path.join(dir, 'configLibrary', '_meta.json'));
345
- if (!Array.isArray(meta.entries)) return false;
346
- return meta.entries.some((entry) => entry && entry.name === YINGCLAW_ENTRY_NAME);
403
+ return getClaudeDesktopDataDirs(options).some((dir) => {
404
+ const meta = readJsonFile(path.join(dir, 'configLibrary', '_meta.json'));
405
+ if (!Array.isArray(meta.entries)) return false;
406
+ return meta.entries.some((entry) => entry && entry.name === YINGCLAW_ENTRY_NAME);
407
+ });
347
408
  }
348
409
 
349
410
  function buildClaudeDesktopOpenCommands(platform = process.platform, options = {}) {
@@ -432,6 +493,7 @@ module.exports = {
432
493
  getClaudeDesktopConfigLibraryDir,
433
494
  getClaudeDesktopConfigPath,
434
495
  getClaudeDesktopDataDir,
496
+ getClaudeDesktopDataDirs,
435
497
  isDesktopConfigured,
436
498
  openClaudeDesktop,
437
499
  writeClaudeDesktopConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yingclaw",
3
- "version": "2.5.18",
3
+ "version": "2.5.19",
4
4
  "description": "Claude Code × 国产大模型一键接入:DeepSeek、Kimi、Qwen、MiniMax、GLM、MiMo",
5
5
  "main": "index.js",
6
6
  "bin": {