yingclaw 1.7.8 → 1.7.12

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 CHANGED
@@ -22,18 +22,43 @@ claw install-claude
22
22
  ```
23
23
  根据提示选择网络环境(有梯子走官方,没梯子走淘宝镜像)。
24
24
 
25
- **第二步:配置国产模型 API**
25
+ **第二步:配置 API 连接**
26
26
  ```bash
27
- claw setup
27
+ claw config
28
+ ```
29
+ 选择厂商 → 输入 API Key → 选择模型。这个步骤只保存 API 连接,不会修改终端环境变量,也不会修改 Claude 桌面应用配置。
30
+
31
+ **第三步:选择接入目标**
32
+
33
+ 接入 Claude Code 终端:
34
+ ```bash
35
+ claw code
28
36
  ```
29
- 选择厂商 输入 API Key 选择模型,自动写入环境变量,配置完成后自动启动 Claude。
37
+ 写入 Claude Code 所需的环境变量。配置完成后不会自动启动 Claude Code,需要时可在主菜单选择“启动 Claude Code”或直接运行 `claude`。
30
38
 
31
- **可选:配置 Claude 桌面应用**
39
+ 接入 Claude 桌面应用:
32
40
  ```bash
33
41
  claw desktop
34
42
  ```
35
- 将当前模型配置写入 Claude Desktop 的第三方推理(Cowork on 3P)本地配置。配置后需要完全退出并重新打开 Claude 桌面应用。
36
- macOS 会询问是否自动重启 Claude 桌面应用;Windows 需要手动重新打开。
43
+ 将当前模型配置写入 Claude Desktop 的第三方推理(Cowork on 3P)本地配置。macOS 会询问是否立即打开 Claude 桌面应用;Windows 需要手动打开。如果配置未生效,请完全退出 Claude Desktop 后重新打开。
44
+
45
+ 恢复 Claude Code 终端默认配置:
46
+ ```bash
47
+ claw code-reset
48
+ ```
49
+ 只清除 Claude Code 终端环境变量,不影响 API 连接和 Claude 桌面配置。
50
+
51
+ 恢复 Claude 桌面应用默认配置:
52
+ ```bash
53
+ claw desktop-reset
54
+ ```
55
+ 只清除 Claude Desktop 第三方推理配置,不影响终端里的 API Key 和模型配置。
56
+
57
+ 兼容旧命令:
58
+ ```bash
59
+ claw setup
60
+ ```
61
+ 等价于 `claw config` + `claw code`,用于一键配置 API 并接入 Claude Code 终端。
37
62
 
38
63
  选择“自定义 Anthropic 兼容接口”时,需要输入:
39
64
 
@@ -44,7 +69,7 @@ macOS 会询问是否自动重启 Claude 桌面应用;Windows 需要手动重
44
69
 
45
70
  注意:模型列表能获取不代表一定可用于 Claude Code 或 Claude 桌面应用。自定义接口还必须支持 Anthropic `/v1/messages`,否则请求会被网关拒绝。Claude 桌面应用的 Gateway Base URL 还必须使用 HTTPS。
46
71
 
47
- **第三步:以后直接用**
72
+ **以后直接用**
48
73
  ```bash
49
74
  claude
50
75
  ```
@@ -64,10 +89,15 @@ claude
64
89
  ## 其他命令
65
90
 
66
91
  ```bash
67
- claw switch # 快速切换模型(无需重新输入 Key)
68
- claw desktop # 配置 Claude 桌面应用第三方推理
69
- claw status # 查看当前配置,验证 Key 是否有效
70
- claw reset # 清除配置和环境变量
92
+ claw config # 配置 API 连接,不修改终端或桌面
93
+ claw code # 接入 Claude Code 终端
94
+ claw code-reset # 恢复 Claude Code 终端默认配置
95
+ claw switch # 快速切换模型(只更新 API 连接)
96
+ claw desktop # 接入 Claude 桌面应用第三方推理
97
+ claw desktop-reset # 恢复 Claude 桌面应用默认配置
98
+ claw setup # 兼容旧命令:config + code
99
+ claw status # 查看当前配置,验证 Key 是否有效
100
+ claw reset # 清除 API 连接、终端环境变量和桌面配置
71
101
  ```
72
102
 
73
103
  ## 卸载
@@ -78,7 +108,7 @@ npm uninstall -g yingclaw
78
108
 
79
109
  ## 原理
80
110
 
81
- 各厂商均原生支持 Anthropic API 格式。本工具会自动写入 Claude Code 所需的环境变量。macOS / Linux / WSL 写入 shell 配置文件,Windows 写入用户级环境变量,包括:
111
+ 各厂商均原生支持 Anthropic API 格式。`claw config` 只保存 API 连接;`claw code` 才会写入 Claude Code 所需的环境变量。macOS / Linux / WSL 写入 shell 配置文件,Windows 写入用户级环境变量,包括:
82
112
 
83
113
  - `ANTHROPIC_BASE_URL`
84
114
  - `ANTHROPIC_AUTH_TOKEN`
package/bin/cli.js CHANGED
@@ -6,6 +6,7 @@ const {
6
6
  loadConfig,
7
7
  saveConfig,
8
8
  writeEnvToZshrc,
9
+ clearClaudeCodeEnv,
9
10
  fetchModels,
10
11
  fetchModelsFromBaseUrl,
11
12
  resetConfig,
@@ -20,7 +21,7 @@ const { execSync, spawn, spawnSync } = require('child_process');
20
21
  const pkg = require('../package.json');
21
22
  const { buildMenuStatusLines, buildStatusView } = require('../lib/panel');
22
23
  const { buildClaudeInstallCommand } = require('../lib/install');
23
- const { clearClaudeDesktopConfig, restartClaudeDesktop, writeClaudeDesktopConfig } = require('../lib/desktop');
24
+ const { clearClaudeDesktopConfig, openClaudeDesktop, writeClaudeDesktopConfig } = require('../lib/desktop');
24
25
 
25
26
  const program = new Command();
26
27
 
@@ -109,8 +110,12 @@ function getStorageHint(file) {
109
110
  return `⚠ API Key 以明文存储在 ${file} 和 ~/.clawai.json`;
110
111
  }
111
112
 
112
- function getDesktopRestartHint() {
113
- return '请完全退出 Claude 桌面应用后重新打开,第三方模型配置才会生效';
113
+ function getDesktopOpenHint() {
114
+ return '请打开 Claude 桌面应用;如配置未生效,请完全退出后重新打开';
115
+ }
116
+
117
+ function getSavedConfigHint() {
118
+ return '⚠ API Key 以明文存储在 ~/.clawai.json';
114
119
  }
115
120
 
116
121
  async function promptModelFromChoices({ chalk, choices, message, backLabel = '↩ 返回上一步', allowManual = true }) {
@@ -201,13 +206,13 @@ async function showStatus() {
201
206
  const config = loadConfig();
202
207
 
203
208
  if (!config) {
204
- console.log(chalk.red('\n未配置,请先运行: claw setup\n'));
209
+ console.log(chalk.red('\n未配置,请先运行: claw config\n'));
205
210
  return;
206
211
  }
207
212
  const configProblem = getConfigValidationMessage(config);
208
213
  if (configProblem) {
209
214
  console.log(chalk.red(`\n配置无效:${configProblem}`));
210
- console.log(chalk.dim('请运行 claw setup 重新配置。\n'));
215
+ console.log(chalk.dim('请运行 claw config 重新配置。\n'));
211
216
  return;
212
217
  }
213
218
 
@@ -257,6 +262,113 @@ async function showStatus() {
257
262
  }));
258
263
  }
259
264
 
265
+ async function runConfigFlow({ writeCodeEnv = false } = {}) {
266
+ const chalk = (await import('chalk')).default;
267
+ const ora = (await import('ora')).default;
268
+ const boxen = (await import('boxen')).default;
269
+
270
+ console.log(await getBanner());
271
+
272
+ const existing = loadConfig();
273
+ if (existing) {
274
+ const existingProvider = PROVIDERS[existing.provider];
275
+ console.log(boxen(
276
+ chalk.bold('当前 API 连接\n\n') +
277
+ chalk.dim('厂商 ') + chalk.white(existingProvider?.name || existing.provider) + '\n' +
278
+ chalk.dim('模型 ') + chalk.yellow(existing.model) + '\n' +
279
+ chalk.dim('Key ') + chalk.dim(existing.apiKey ? '已保存' : '缺失'),
280
+ { padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'yellow', margin: { top: 1, bottom: 1 } }
281
+ ));
282
+ const overwrite = await confirm({ message: '覆盖现有 API 连接?', default: false });
283
+ if (!overwrite) return;
284
+ }
285
+
286
+ let providerKey, provider, apiKey, model, customConfig;
287
+ let step = 'provider';
288
+
289
+ while (true) {
290
+ if (step === 'provider') {
291
+ providerKey = await select({ loop: false,
292
+ message: chalk.cyan('选择 AI 厂商'),
293
+ choices: [
294
+ ...Object.entries(PROVIDERS).map(([value, p]) => ({ name: p.name, value })),
295
+ { name: chalk.dim('↩ 返回主菜单'), value: '__BACK__' },
296
+ ],
297
+ });
298
+ if (providerKey === '__BACK__') return;
299
+ provider = PROVIDERS[providerKey];
300
+ if (provider.custom) {
301
+ customConfig = await configureCustomProvider({ chalk, ora });
302
+ if (!customConfig) { step = 'provider'; continue; }
303
+ break;
304
+ }
305
+ step = 'apikey';
306
+ } else if (step === 'apikey') {
307
+ const k = await input({
308
+ message: chalk.cyan(`${provider.name} API Key(输入 b 返回上一步)`),
309
+ transformer: (v) => v && v !== 'b' ? chalk.dim('•'.repeat(v.length)) : v,
310
+ validate: (v) => v.trim().length > 0 ? true : 'API Key 不能为空',
311
+ });
312
+ if (k.trim() === 'b') { step = 'provider'; continue; }
313
+ apiKey = k.trim();
314
+ step = 'model';
315
+ } else if (step === 'model') {
316
+ const fetchSpinner = ora('正在获取可用模型...').start();
317
+ const onlineModels = await fetchModels(providerKey, apiKey);
318
+ let modelChoices;
319
+ if (onlineModels && onlineModels.length > 0) {
320
+ fetchSpinner.succeed(chalk.green(`已获取 ${onlineModels.length} 个可用模型`));
321
+ modelChoices = onlineModels.map(id => ({ name: id, value: id }));
322
+ } else {
323
+ fetchSpinner.warn(chalk.yellow('无法获取在线列表,使用内置默认列表'));
324
+ modelChoices = provider.models;
325
+ }
326
+
327
+ const m = await select({ loop: false,
328
+ message: chalk.cyan('选择模型'),
329
+ choices: [
330
+ ...modelChoices,
331
+ { name: chalk.dim('↩ 返回上一步(重新输入 Key)'), value: '__BACK__' },
332
+ ],
333
+ });
334
+ if (m === '__BACK__') { step = 'apikey'; continue; }
335
+ model = m;
336
+ break;
337
+ }
338
+ }
339
+
340
+ const spinner = ora(writeCodeEnv ? '保存 API 连接并接入 Claude Code 终端...' : '保存 API 连接...').start();
341
+ let file;
342
+ let cfg;
343
+ try {
344
+ const fastModel = customConfig?.fastModel || resolveFastModel(provider, model);
345
+ cfg = customConfig || { provider: providerKey, model, fastModel, apiKey, baseUrl: provider.baseUrl };
346
+ saveConfig(cfg);
347
+ if (writeCodeEnv) {
348
+ ({ file } = writeEnvToZshrc(cfg.baseUrl, cfg.apiKey, cfg.model, cfg.fastModel));
349
+ }
350
+ spinner.succeed(chalk.green(writeCodeEnv ? 'API 连接已保存,Claude Code 终端已接入' : 'API 连接已保存'));
351
+ } catch (e) {
352
+ spinner.fail(chalk.red(`写入失败: ${e.message}`));
353
+ return;
354
+ }
355
+
356
+ console.log(chalk.dim(writeCodeEnv ? getStorageHint(file) : getSavedConfigHint()));
357
+
358
+ const nextStep = writeCodeEnv
359
+ ? chalk.white('需要启动时,在主菜单选择“启动 Claude Code”,或直接输入 ') + chalk.cyan.bold('claude') + '\n' + chalk.dim(getActivationHint(file))
360
+ : chalk.white('下一步可选择“接入 Claude Code 终端”或“接入 Claude 桌面应用”。');
361
+
362
+ console.log(boxen(
363
+ chalk.bold(writeCodeEnv ? 'Claude Code 终端配置完成!\n\n' : 'API 连接配置完成!\n\n') +
364
+ chalk.dim('Base URL ') + chalk.cyan(cfg.baseUrl) + '\n' +
365
+ chalk.dim('API Key ') + chalk.cyan('已保存') + '\n' +
366
+ chalk.dim('模型 ') + chalk.yellow(cfg.model) + '\n\n' +
367
+ nextStep,
368
+ { padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
369
+ ));
370
+ }
371
+
260
372
  program
261
373
  .name('claw')
262
374
  .description('Claude Code × 国产大模型一键接入')
@@ -318,14 +430,25 @@ program
318
430
 
319
431
  console.log(boxen(
320
432
  chalk.bold('下一步\n\n') +
321
- chalk.cyan(' claw setup') + chalk.dim(' 配置国产模型 API Key'),
433
+ chalk.cyan(' claw config') + chalk.dim(' 配置 API 连接\n') +
434
+ chalk.cyan(' claw code') + chalk.dim(' 接入 Claude Code 终端'),
322
435
  { padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'cyan', margin: { top: 1, bottom: 1 } }
323
436
  ));
324
437
  });
325
438
 
439
+ program
440
+ .command('config')
441
+ .description('配置 API 连接(不修改终端或桌面)')
442
+ .action(() => runConfigFlow({ writeCodeEnv: false }));
443
+
326
444
  program
327
445
  .command('setup')
328
- .description('配置 API Key 和模型')
446
+ .description('配置 API 并接入 Claude Code 终端(兼容旧命令)')
447
+ .action(() => runConfigFlow({ writeCodeEnv: true }));
448
+
449
+ program
450
+ .command('code')
451
+ .description('接入 Claude Code 终端')
329
452
  .action(async () => {
330
453
  const chalk = (await import('chalk')).default;
331
454
  const ora = (await import('ora')).default;
@@ -333,112 +456,81 @@ program
333
456
 
334
457
  console.log(await getBanner());
335
458
 
336
- // 已有配置时先展示,再询问
337
- const existing = loadConfig();
338
- if (existing) {
339
- const existingProvider = PROVIDERS[existing.provider];
340
- console.log(boxen(
341
- chalk.bold('当前配置\n\n') +
342
- chalk.dim('厂商 ') + chalk.white(existingProvider?.name || existing.provider) + '\n' +
343
- chalk.dim('模型 ') + chalk.yellow(existing.model) + '\n' +
344
- chalk.dim('Key ') + chalk.dim(existing.apiKey ? '已保存' : '缺失'),
345
- { padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'yellow', margin: { top: 1, bottom: 1 } }
346
- ));
347
- const overwrite = await confirm({ message: '覆盖现有配置?', default: false });
348
- if (!overwrite) return;
459
+ const config = loadConfig();
460
+ if (!config) {
461
+ console.log(chalk.red('\n未配置 API 连接,请先运行: claw config\n'));
462
+ return;
349
463
  }
350
-
351
- let providerKey, provider, apiKey, model, customConfig;
352
- let step = 'provider';
353
-
354
- while (true) {
355
- if (step === 'provider') {
356
- providerKey = await select({ loop: false,
357
- message: chalk.cyan('选择 AI 厂商'),
358
- choices: [
359
- ...Object.entries(PROVIDERS).map(([value, p]) => ({ name: p.name, value })),
360
- { name: chalk.dim('↩ 返回主菜单'), value: '__BACK__' },
361
- ],
362
- });
363
- if (providerKey === '__BACK__') return;
364
- provider = PROVIDERS[providerKey];
365
- if (provider.custom) {
366
- customConfig = await configureCustomProvider({ chalk, ora });
367
- if (!customConfig) { step = 'provider'; continue; }
368
- break;
369
- }
370
- step = 'apikey';
371
- } else if (step === 'apikey') {
372
- const k = await input({
373
- message: chalk.cyan(`${provider.name} API Key(输入 b 返回上一步)`),
374
- transformer: (v) => v && v !== 'b' ? chalk.dim('•'.repeat(v.length)) : v,
375
- validate: (v) => v.trim().length > 0 ? true : 'API Key 不能为空',
376
- });
377
- if (k.trim() === 'b') { step = 'provider'; continue; }
378
- apiKey = k.trim();
379
- step = 'model';
380
- } else if (step === 'model') {
381
- const fetchSpinner = ora('正在获取可用模型...').start();
382
- const onlineModels = await fetchModels(providerKey, apiKey);
383
- let modelChoices;
384
- if (onlineModels && onlineModels.length > 0) {
385
- fetchSpinner.succeed(chalk.green(`已获取 ${onlineModels.length} 个可用模型`));
386
- modelChoices = onlineModels.map(id => ({ name: id, value: id }));
387
- } else {
388
- fetchSpinner.warn(chalk.yellow('无法获取在线列表,使用内置默认列表'));
389
- modelChoices = provider.models;
390
- }
391
-
392
- const m = await select({ loop: false,
393
- message: chalk.cyan('选择模型'),
394
- choices: [
395
- ...modelChoices,
396
- { name: chalk.dim('↩ 返回上一步(重新输入 Key)'), value: '__BACK__' },
397
- ],
398
- });
399
- if (m === '__BACK__') { step = 'apikey'; continue; }
400
- model = m;
401
- break;
402
- }
464
+ const configProblem = getConfigValidationMessage(config);
465
+ if (configProblem) {
466
+ console.log(chalk.red(`\n配置无效:${configProblem}`));
467
+ console.log(chalk.dim('请运行 claw config 重新配置。\n'));
468
+ return;
403
469
  }
404
470
 
405
- const spinner = ora('写入配置...').start();
406
- let result, file;
471
+ const spinner = ora('写入 Claude Code 终端环境变量...').start();
472
+ let file;
407
473
  try {
408
- const fastModel = customConfig?.fastModel || resolveFastModel(provider, model);
409
- const cfg = customConfig || { provider: providerKey, model, fastModel, apiKey, baseUrl: provider.baseUrl };
410
- saveConfig(cfg);
411
- ({ result, file } = writeEnvToZshrc(cfg.baseUrl, cfg.apiKey, cfg.model, cfg.fastModel));
412
- spinner.succeed(chalk.green(result === 'updated' ? `环境变量已更新 → ${file}` : `环境变量已写入 → ${file}`));
474
+ ({ file } = writeEnvToZshrc(config.baseUrl, config.apiKey, config.model, config.fastModel));
475
+ spinner.succeed(chalk.green(`Claude Code 终端已接入 ${file}`));
413
476
  } catch (e) {
414
477
  spinner.fail(chalk.red(`写入失败: ${e.message}`));
415
478
  return;
416
479
  }
417
- console.log(chalk.dim(getStorageHint(file)));
418
480
 
481
+ console.log(chalk.dim(getStorageHint(file)));
419
482
  console.log(boxen(
420
- chalk.bold('配置完成!\n\n') +
421
- chalk.dim('ANTHROPIC_BASE_URL ') + chalk.cyan((customConfig || { baseUrl: provider.baseUrl }).baseUrl) + '\n' +
422
- chalk.dim('ANTHROPIC_API_KEY ') + chalk.cyan('已保存') + '\n\n' +
423
- chalk.white('下次直接输入 ') + chalk.cyan.bold('claude') + chalk.white(' 即可使用'),
483
+ chalk.bold('Claude Code 终端已接入\n\n') +
484
+ chalk.dim('Base URL ') + chalk.cyan(config.baseUrl) + '\n' +
485
+ chalk.dim('模型 ') + chalk.yellow(config.model) + '\n\n' +
486
+ chalk.white('需要启动时,在主菜单选择“启动 Claude Code”,或直接输入 ') + chalk.cyan.bold('claude') + '\n' +
487
+ chalk.dim(getActivationHint(file)),
424
488
  { padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
425
489
  ));
490
+ });
491
+
492
+ program
493
+ .command('code-reset')
494
+ .description('恢复 Claude Code 终端默认配置')
495
+ .action(async () => {
496
+ const chalk = (await import('chalk')).default;
497
+ const ora = (await import('ora')).default;
498
+ const boxen = (await import('boxen')).default;
426
499
 
427
- const launchSpinner = ora('正在启动 Claude Code...').start();
428
- await new Promise(r => setTimeout(r, 500));
429
- launchSpinner.stop();
500
+ console.log(await getBanner());
430
501
 
431
- spawn('claude', [], {
432
- stdio: 'inherit',
433
- env: { ...process.env, ...buildClaudeEnv(customConfig || { provider: providerKey, baseUrl: provider.baseUrl, apiKey, model, fastModel: resolveFastModel(provider, model) }) },
434
- }).on('error', () => {
435
- console.log(chalk.yellow('\nClaude Code 未找到,请先运行: claw install-claude'));
502
+ const yes = await confirm({
503
+ message: chalk.red('确定要恢复 Claude Code 终端默认配置吗?API 连接和桌面配置不会被清除'),
504
+ default: false,
436
505
  });
506
+ if (!yes) {
507
+ console.log(chalk.dim('已取消'));
508
+ return;
509
+ }
510
+
511
+ const spinner = ora('正在恢复 Claude Code 终端默认配置...').start();
512
+ const cleared = clearClaudeCodeEnv();
513
+ await new Promise(r => setTimeout(r, 300));
514
+
515
+ if (cleared.length === 0) {
516
+ spinner.warn(chalk.yellow('没有找到 Claude Code 终端环境变量,无需恢复'));
517
+ } else {
518
+ spinner.succeed(chalk.green('Claude Code 终端已恢复默认'));
519
+ const resetNote = cleared.includes('Windows 用户环境变量')
520
+ ? '注:当前终端的环境变量还在内存中,重新打开 PowerShell / CMD 后才彻底清除'
521
+ : '注:当前终端的环境变量还在内存中,重开终端或 unset 才彻底清除';
522
+ console.log(boxen(
523
+ chalk.bold('已清除以下位置中的 Claude Code 终端环境变量:\n\n') +
524
+ cleared.map(f => chalk.cyan(' • ' + f)).join('\n') +
525
+ '\n\n' + chalk.dim(resetNote),
526
+ { padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
527
+ ));
528
+ }
437
529
  });
438
530
 
439
531
  program
440
532
  .command('switch')
441
- .description('快速切换模型(无需重新输入 Key)')
533
+ .description('快速切换模型(只更新 API 连接)')
442
534
  .action(async () => {
443
535
  const chalk = (await import('chalk')).default;
444
536
  const ora = (await import('ora')).default;
@@ -447,13 +539,13 @@ program
447
539
 
448
540
  const config = loadConfig();
449
541
  if (!config) {
450
- console.log(chalk.red('\n未配置,请先运行: claw setup\n'));
542
+ console.log(chalk.red('\n未配置,请先运行: claw config\n'));
451
543
  return;
452
544
  }
453
545
  const configProblem = getConfigValidationMessage(config);
454
546
  if (configProblem) {
455
547
  console.log(chalk.red(`\n配置无效:${configProblem}`));
456
- console.log(chalk.dim('请运行 claw setup 重新配置。\n'));
548
+ console.log(chalk.dim('请运行 claw config 重新配置。\n'));
457
549
  return;
458
550
  }
459
551
 
@@ -473,10 +565,9 @@ program
473
565
 
474
566
  const spinner = ora('切换中...').start();
475
567
  saveConfig(customConfig);
476
- const { file } = writeEnvToZshrc(customConfig.baseUrl, customConfig.apiKey, customConfig.model, customConfig.fastModel);
477
568
  await new Promise(r => setTimeout(r, 300));
478
- spinner.succeed(chalk.green(`已切换至 ${customConfig.providerName} · ${customConfig.model}`));
479
- console.log(chalk.dim(getActivationHint(file)));
569
+ spinner.succeed(chalk.green(`API 连接已切换至 ${customConfig.providerName} · ${customConfig.model}`));
570
+ console.log(chalk.dim('如需让外部 claude 命令使用新模型,请运行 claw code。'));
480
571
  return;
481
572
  }
482
573
 
@@ -519,10 +610,9 @@ program
519
610
  const fastModel = resolveFastModel(provider, model);
520
611
  const newConfig = { ...config, provider: providerKey, model, fastModel, baseUrl: provider.baseUrl, apiKey };
521
612
  saveConfig(newConfig);
522
- const { file } = writeEnvToZshrc(provider.baseUrl, apiKey, model, fastModel);
523
613
  await new Promise(r => setTimeout(r, 300));
524
- spinner.succeed(chalk.green(`已切换至 ${provider.name} · ${model}`));
525
- console.log(chalk.dim(getActivationHint(file)));
614
+ spinner.succeed(chalk.green(`API 连接已切换至 ${provider.name} · ${model}`));
615
+ console.log(chalk.dim('如需让外部 claude 命令使用新模型,请运行 claw code。'));
526
616
  });
527
617
 
528
618
  program
@@ -532,7 +622,7 @@ program
532
622
 
533
623
  program
534
624
  .command('desktop')
535
- .description('配置 Claude 桌面应用使用当前模型')
625
+ .description('接入 Claude 桌面应用使用当前模型')
536
626
  .action(async () => {
537
627
  const chalk = (await import('chalk')).default;
538
628
  const ora = (await import('ora')).default;
@@ -542,13 +632,13 @@ program
542
632
 
543
633
  const config = loadConfig();
544
634
  if (!config) {
545
- console.log(chalk.red('\n未配置,请先运行: claw setup\n'));
635
+ console.log(chalk.red('\n未配置 API 连接,请先运行: claw config\n'));
546
636
  return;
547
637
  }
548
638
  const configProblem = getConfigValidationMessage(config);
549
639
  if (configProblem) {
550
640
  console.log(chalk.red(`\n配置无效:${configProblem}`));
551
- console.log(chalk.dim('请运行 claw setup 重新配置。\n'));
641
+ console.log(chalk.dim('请运行 claw config 重新配置。\n'));
552
642
  return;
553
643
  }
554
644
 
@@ -576,31 +666,72 @@ program
576
666
  chalk.dim('Base URL ') + chalk.cyan(config.baseUrl) + '\n' +
577
667
  chalk.dim('模型 ') + chalk.yellow(config.model) + '\n' +
578
668
  chalk.dim('认证方式 ') + chalk.cyan('bearer') + '\n\n' +
579
- chalk.yellow(getDesktopRestartHint()) + '\n' +
669
+ chalk.yellow(getDesktopOpenHint()) + '\n' +
580
670
  chalk.dim('要求:网关必须支持 Anthropic POST /v1/messages,且 Base URL 必须是 HTTPS。'),
581
671
  { padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'cyan', margin: { top: 1, bottom: 1 } }
582
672
  ));
583
673
 
584
674
  if (process.platform === 'darwin') {
585
- const shouldRestart = await confirm({ message: '是否自动重启 Claude 桌面应用?', default: false });
586
- if (shouldRestart) {
587
- const restartSpinner = ora('正在重启 Claude 桌面应用...').start();
675
+ const shouldOpen = await confirm({ message: '是否现在打开 Claude 桌面应用?', default: true });
676
+ if (shouldOpen) {
677
+ const openSpinner = ora('正在打开 Claude 桌面应用...').start();
588
678
  try {
589
- await restartClaudeDesktop();
590
- restartSpinner.succeed(chalk.green('Claude 桌面应用已重新打开'));
679
+ openClaudeDesktop();
680
+ openSpinner.succeed(chalk.green('Claude 桌面应用已打开'));
591
681
  } catch (e) {
592
- restartSpinner.fail(chalk.red(`重启失败: ${e.message}`));
593
- console.log(chalk.dim(getDesktopRestartHint()));
682
+ openSpinner.fail(chalk.red(`打开失败: ${e.message}`));
683
+ console.log(chalk.dim(getDesktopOpenHint()));
594
684
  }
595
685
  }
596
686
  } else {
597
- console.log(chalk.dim(getDesktopRestartHint()));
687
+ console.log(chalk.dim(getDesktopOpenHint()));
688
+ }
689
+ });
690
+
691
+ program
692
+ .command('desktop-reset')
693
+ .description('恢复 Claude 桌面应用默认配置')
694
+ .action(async () => {
695
+ const chalk = (await import('chalk')).default;
696
+ const ora = (await import('ora')).default;
697
+ const boxen = (await import('boxen')).default;
698
+
699
+ console.log(await getBanner());
700
+
701
+ if (process.platform !== 'darwin' && process.platform !== 'win32') {
702
+ console.log(chalk.yellow('\nClaude 桌面应用 3P 配置目前仅支持 macOS / Windows。\n'));
703
+ return;
704
+ }
705
+
706
+ const yes = await confirm({
707
+ message: chalk.red('确定要恢复 Claude 桌面应用默认配置吗?终端配置不会被清除'),
708
+ default: false,
709
+ });
710
+ if (!yes) {
711
+ console.log(chalk.dim('已取消'));
712
+ return;
713
+ }
714
+
715
+ const spinner = ora('正在恢复 Claude 桌面应用默认配置...').start();
716
+ const result = clearClaudeDesktopConfig();
717
+ await new Promise(r => setTimeout(r, 300));
718
+
719
+ if (result.result === 'updated') {
720
+ spinner.succeed(chalk.green('Claude 桌面应用已恢复默认'));
721
+ console.log(boxen(
722
+ chalk.bold('已清除 Claude 桌面应用第三方推理配置:\n\n') +
723
+ chalk.cyan(' • ' + result.file) +
724
+ '\n\n' + chalk.dim('如 Claude Desktop 已打开,请完全退出后重新打开。'),
725
+ { padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
726
+ ));
727
+ } else {
728
+ spinner.warn(chalk.yellow('没有找到 Claude 桌面应用第三方推理配置,无需恢复'));
598
729
  }
599
730
  });
600
731
 
601
732
  program
602
733
  .command('reset')
603
- .description('恢复默认(清除所有 clawai 配置)')
734
+ .description('清除 API 连接、终端环境变量和桌面配置')
604
735
  .action(async () => {
605
736
  const chalk = (await import('chalk')).default;
606
737
  const ora = (await import('ora')).default;
@@ -692,7 +823,7 @@ async function runMenu() {
692
823
 
693
824
  if (configProblem) {
694
825
  console.log(chalk.red(` ● 配置无效:${configProblem}`));
695
- console.log(chalk.dim(' 请先选择“首次配置 API Key 和模型”重新配置'));
826
+ console.log(chalk.dim(' 请先选择“配置 API 连接”重新配置'));
696
827
  } else {
697
828
  console.log(await renderStatusBar(apiStatus));
698
829
  }
@@ -701,14 +832,17 @@ async function runMenu() {
701
832
  const action = await select({ loop: false,
702
833
  message: chalk.cyan('选择操作'),
703
834
  choices: [
704
- { name: '🤖 启动 Claude Code', value: 'launch', disabled: (!config || configProblem) && '需先完成配置' },
835
+ { name: '🤖 启动 Claude Code', value: 'launch', disabled: (!config || configProblem) && '需先配置 API 连接' },
705
836
  { name: '📦 安装 Claude Code', value: 'install' },
706
- { name: config ? '⚙️ 重新配置(输入新的 API Key)' : '⚙️ 首次配置 API Key 和模型', value: 'setup' },
707
- { name: '🔄 切换厂商或模型', value: 'switch', disabled: (!config || configProblem) && '需先完成配置' },
708
- { name: '🖥 配置 Claude 桌面应用', value: 'desktop', disabled: (!config || configProblem) && '需先完成配置' },
709
- { name: '📊 查看当前配置', value: 'status', disabled: !config && '需先完成配置' },
710
- { name: '🔁 重新检测 API', value: 'recheck', disabled: (!config || configProblem) && '需先完成配置' },
711
- { name: '🗑 恢复默认(清除所有配置)', value: 'reset' },
837
+ { name: config ? '🔑 重新配置 API 连接' : '🔑 配置 API 连接', value: 'config' },
838
+ { name: '💻 接入 Claude Code 终端', value: 'code', disabled: (!config || configProblem) && '需先配置 API 连接' },
839
+ { name: '🔄 切换厂商或模型', value: 'switch', disabled: (!config || configProblem) && '需先配置 API 连接' },
840
+ { name: '🖥 接入 Claude 桌面应用', value: 'desktop', disabled: (!config || configProblem) && '需先配置 API 连接' },
841
+ { name: '↩️ 恢复 Claude Code 终端默认', value: 'code-reset' },
842
+ { name: '↩️ 恢复 Claude 桌面默认', value: 'desktop-reset' },
843
+ { name: '📊 查看当前配置', value: 'status', disabled: !config && '需先配置 API 连接' },
844
+ { name: '🔁 重新检测 API', value: 'recheck', disabled: (!config || configProblem) && '需先配置 API 连接' },
845
+ { name: '🗑 清除所有 yingclaw 配置', value: 'reset' },
712
846
  { name: '退出', value: 'exit' },
713
847
  ],
714
848
  });
@@ -740,9 +874,13 @@ async function runMenu() {
740
874
 
741
875
  const cmdMap = {
742
876
  install: 'install-claude',
877
+ config: 'config',
743
878
  setup: 'setup',
879
+ code: 'code',
880
+ 'code-reset': 'code-reset',
744
881
  switch: 'switch',
745
882
  desktop: 'desktop',
883
+ 'desktop-reset': 'desktop-reset',
746
884
  status: 'status',
747
885
  reset: 'reset',
748
886
  };
package/lib/config.js CHANGED
@@ -330,18 +330,10 @@ function writeEnvToZshrc(baseUrl, apiKey, model, fastModel, options = {}) {
330
330
  return { result: cleaned !== current ? 'updated' : 'added', file: rcFile };
331
331
  }
332
332
 
333
- // 清除所有 clawai 配置(配置文件 + shell 环境变量块)
334
- function resetConfig(options = {}) {
333
+ function clearClaudeCodeEnv(options = {}) {
335
334
  const cleared = [];
336
- const configFile = options.configFile || CONFIG_FILE;
337
335
  const platform = options.platform || process.platform;
338
336
 
339
- // 删配置文件
340
- if (fs.existsSync(configFile)) {
341
- fs.unlinkSync(configFile);
342
- cleared.push(configFile);
343
- }
344
-
345
337
  if (platform === 'win32') {
346
338
  runWindowsEnvCommands(buildWindowsClearEnvCommands(), options.runner || spawnSync, { ignoreErrors: true });
347
339
  cleared.push(WINDOWS_ENV_LABEL);
@@ -369,12 +361,27 @@ function resetConfig(options = {}) {
369
361
  return cleared;
370
362
  }
371
363
 
364
+ // 清除所有 clawai 配置(配置文件 + shell 环境变量块)
365
+ function resetConfig(options = {}) {
366
+ const cleared = [];
367
+ const configFile = options.configFile || CONFIG_FILE;
368
+
369
+ // 删配置文件
370
+ if (fs.existsSync(configFile)) {
371
+ fs.unlinkSync(configFile);
372
+ cleared.push(configFile);
373
+ }
374
+
375
+ return [...cleared, ...clearClaudeCodeEnv(options)];
376
+ }
377
+
372
378
  module.exports = {
373
379
  loadConfig,
374
380
  saveConfig,
375
381
  writeEnvToZshrc,
376
382
  buildWindowsSetEnvCommands,
377
383
  buildWindowsClearEnvCommands,
384
+ clearClaudeCodeEnv,
378
385
  fetchModels,
379
386
  resetConfig,
380
387
  validateConfig,
package/lib/desktop.js CHANGED
@@ -119,43 +119,34 @@ function clearClaudeDesktopConfig(options = {}) {
119
119
  return { result: 'updated', file };
120
120
  }
121
121
 
122
- function buildClaudeDesktopRestartCommands(platform = process.platform) {
122
+ function buildClaudeDesktopOpenCommands(platform = process.platform) {
123
123
  if (platform !== 'darwin') return null;
124
124
  return [
125
- { command: 'osascript', args: ['-e', 'tell application "Claude" to quit'] },
126
125
  { command: 'open', args: ['-a', 'Claude'] },
127
126
  ];
128
127
  }
129
128
 
130
- function delay(ms) {
131
- return new Promise(resolve => setTimeout(resolve, ms));
132
- }
133
-
134
- async function restartClaudeDesktop(options = {}) {
129
+ function openClaudeDesktop(options = {}) {
135
130
  const platform = options.platform || process.platform;
136
- const commands = buildClaudeDesktopRestartCommands(platform);
131
+ const commands = buildClaudeDesktopOpenCommands(platform);
137
132
  if (!commands) return { result: 'unsupported' };
138
133
 
139
134
  const runner = options.runner || spawnSync;
140
- const delayMs = options.delayMs ?? 1500;
141
- const [quitCommand, openCommand] = commands;
142
-
143
- runner(quitCommand.command, quitCommand.args, { stdio: 'ignore', windowsHide: true });
144
- await delay(delayMs);
135
+ const [openCommand] = commands;
145
136
  const result = runner(openCommand.command, openCommand.args, { stdio: 'ignore', windowsHide: true });
146
137
  if (result.status !== 0) {
147
- throw new Error('Claude 桌面应用重新打开失败');
138
+ throw new Error('Claude 桌面应用打开失败');
148
139
  }
149
140
 
150
- return { result: 'restarted' };
141
+ return { result: 'opened' };
151
142
  }
152
143
 
153
144
  module.exports = {
154
145
  buildClaudeDesktopEnterpriseConfig,
155
- buildClaudeDesktopRestartCommands,
146
+ buildClaudeDesktopOpenCommands,
156
147
  clearClaudeDesktopConfig,
157
148
  getClaudeDesktopConfigPath,
158
- restartClaudeDesktop,
149
+ openClaudeDesktop,
159
150
  writeClaudeDesktopConfig,
160
151
  CLAUDE_DESKTOP_LABEL,
161
152
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yingclaw",
3
- "version": "1.7.8",
3
+ "version": "1.7.12",
4
4
  "description": "Claude Code × 国产大模型一键接入:DeepSeek、Kimi、Qwen、MiniMax、GLM、MiMo",
5
5
  "main": "index.js",
6
6
  "bin": {