yingclaw 2.5.20 → 2.5.25
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 +27 -6
- package/bin/cli.js +201 -24
- package/index.js +2 -0
- package/lib/config.js +137 -24
- package/lib/doctor.js +13 -3
- package/lib/gateway.js +105 -6
- package/lib/openai.js +152 -0
- package/lib/panel.js +17 -5
- package/lib/vscode.js +256 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Claude Code × 国产大模型,一键接入。
|
|
4
4
|
|
|
5
|
-
支持 DeepSeek、Kimi、阿里云百炼(Qwen)、MiniMax、智谱 GLM、小米 MiMo,也支持自定义 Anthropic 兼容接口,无需梯子即可使用 Claude Code。
|
|
5
|
+
支持 DeepSeek、Kimi、火山方舟 Coding Plan、阿里云百炼(Qwen)、MiniMax、智谱 GLM、小米 MiMo,也支持自定义 Anthropic 兼容接口和自定义 OpenAI 兼容接口,无需梯子即可使用 Claude Code。
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
|
|
@@ -44,6 +44,12 @@ claw code
|
|
|
44
44
|
```
|
|
45
45
|
写入 Claude Code 所需的环境变量,之后运行 `claude` 即可。
|
|
46
46
|
|
|
47
|
+
接入 VS Code Claude Code 扩展:
|
|
48
|
+
```bash
|
|
49
|
+
claw vscode
|
|
50
|
+
```
|
|
51
|
+
写入 Claude Code 共享设置 `~/.claude/settings.json`,并在 VS Code 用户设置中关闭 Claude Code 扩展登录提示。VS Code 已打开时需要执行 `Developer: Reload Window` 或完全重启。
|
|
52
|
+
|
|
47
53
|
接入 Claude 桌面应用:
|
|
48
54
|
```bash
|
|
49
55
|
claw desktop
|
|
@@ -57,21 +63,27 @@ claw desktop
|
|
|
57
63
|
| 厂商 | 主模型 | 快速模型 |
|
|
58
64
|
|------|--------|---------|
|
|
59
65
|
| DeepSeek | deepseek-v4-pro[1m] | deepseek-v4-flash[1m] |
|
|
60
|
-
| Kimi / Moonshot | kimi-k2.5 | kimi-k2.5 |
|
|
66
|
+
| Kimi / Moonshot(OpenAI 兼容) | kimi-k2.5 | kimi-k2.5 |
|
|
67
|
+
| 火山方舟 Coding Plan | ark-code-latest | ark-code-latest |
|
|
61
68
|
| 阿里云百炼 | qwen3-max | qwen3.5-plus |
|
|
62
69
|
| MiniMax | MiniMax-M2.7 | MiniMax-M2.7-Turbo |
|
|
63
70
|
| 智谱 GLM | GLM-4.7 | GLM-5-Turbo |
|
|
64
71
|
| 小米 MiMo | mimo-v2.5-pro | mimo-v2.5 |
|
|
65
|
-
|
|
|
72
|
+
| 自定义 Anthropic 兼容接口 | 自动获取或手动输入 | 手动选择 |
|
|
73
|
+
| 自定义 OpenAI 兼容接口 | 自动获取或手动输入 | 手动选择 |
|
|
66
74
|
|
|
67
75
|
DeepSeek 的 `[1m]` 后缀是真实 API 模型 ID,表示 100 万 token 上下文窗口([官方说明](https://api-docs.deepseek.com/zh-cn/quick_start/agent_integrations/claude_code))。
|
|
68
76
|
|
|
77
|
+
火山方舟 Coding Plan 使用官方指定的 Anthropic 兼容 Base URL `https://ark.cn-beijing.volces.com/api/coding`,不要替换成普通推理 API Base URL。
|
|
78
|
+
|
|
69
79
|
## 命令列表
|
|
70
80
|
|
|
71
81
|
```bash
|
|
72
82
|
claw # 交互菜单(无参数时自动进入)
|
|
73
83
|
claw config # 配置 API 连接
|
|
74
84
|
claw code # 接入 Claude Code 终端
|
|
85
|
+
claw vscode # 接入 VS Code Claude Code 扩展
|
|
86
|
+
claw vscode-reset # 恢复 VS Code Claude Code 扩展默认配置
|
|
75
87
|
claw desktop # 接入 Claude 桌面应用
|
|
76
88
|
claw gateway # 启动 Claude 桌面应用本机 Gateway
|
|
77
89
|
claw desktop --direct # 高级:直连写入厂商 Gateway URL
|
|
@@ -115,14 +127,23 @@ CLAUDE_CODE_EFFORT_LEVEL
|
|
|
115
127
|
|
|
116
128
|
- macOS / Windows:写入 `Claude-3p/configLibrary/` 中的 yingclaw entry
|
|
117
129
|
- Claude Desktop 访问 `http://127.0.0.1:18080/yingclaw`
|
|
118
|
-
- Gateway 再转发到当前保存的 Anthropic
|
|
130
|
+
- Gateway 再转发到当前保存的 Anthropic 兼容接口;如当前为 Kimi 或 OpenAI 兼容接口,则自动转换为 `/v1/chat/completions`
|
|
119
131
|
- macOS 使用 LaunchAgent 登录启动 Gateway,Windows 使用 Startup 目录脚本登录启动 Gateway
|
|
120
132
|
- Windows 会同时写入 Roaming / Local 两个 Claude-3p 位置,以兼容不同 Claude Desktop 安装方式
|
|
121
|
-
-
|
|
133
|
+
- Anthropic 兼容接口的终端接入仍直接使用 `ANTHROPIC_*` 环境变量;Kimi 和 OpenAI 兼容接口的终端/VS Code/桌面会通过本机 Gateway 转换协议
|
|
122
134
|
|
|
123
135
|
使用 `inferenceProvider=gateway`、`inferenceGatewayAuthScheme=bearer`,将 Gateway Base URL 指向 yingclaw 本机 Gateway。高级用户可用 `claw desktop --direct` 保留旧式直连写入,但新版 Claude Desktop 可能拒绝非 Claude 模型名。
|
|
124
136
|
|
|
125
|
-
|
|
137
|
+
**自定义 Anthropic 兼容接口**需支持 Anthropic `/v1/messages` 格式。**自定义 OpenAI 兼容接口**需支持 OpenAI `/v1/models` 和 `/v1/chat/completions` 格式,工具会通过本机 Gateway 转换为 Claude 可用的 Messages 格式。工具会根据 Base URL 自动尝试获取模型列表,失败则手动输入。
|
|
138
|
+
|
|
139
|
+
**VS Code 接入**(`claw vscode`)写入 Claude Code 官方共享设置:
|
|
140
|
+
|
|
141
|
+
- 接入时会让你单独选择 VS Code Claude Code 的主模型和快速模型,不影响终端或桌面配置
|
|
142
|
+
- `~/.claude/settings.json` 写入所选主模型,并把已获取的可用聊天模型写入 `availableModels`
|
|
143
|
+
- `env` 中启用 Gateway model discovery,并把所选快速模型写入 `ANTHROPIC_CUSTOM_MODEL_OPTION`
|
|
144
|
+
- VS Code 用户 `settings.json` 写入 `claudeCode.disableLoginPrompt=true`
|
|
145
|
+
- 扩展与 CLI 共享 Claude Code 设置;如果 VS Code 已打开,需重载窗口或重启后生效
|
|
146
|
+
- 如需恢复默认,运行 `claw vscode-reset`,只移除 yingclaw 写入的 Claude Code env 和 VS Code Claude Code 扩展项,保留其它用户设置
|
|
126
147
|
|
|
127
148
|
## 卸载
|
|
128
149
|
|
package/bin/cli.js
CHANGED
|
@@ -13,7 +13,9 @@ const {
|
|
|
13
13
|
validateConfig,
|
|
14
14
|
validateKey,
|
|
15
15
|
normalizeAnthropicBaseUrl,
|
|
16
|
+
getProviderProtocol,
|
|
16
17
|
resolveFastModel,
|
|
18
|
+
sortRelatedModelIds,
|
|
17
19
|
buildClaudeEnv,
|
|
18
20
|
PROVIDERS,
|
|
19
21
|
CLAUDE_ENV_KEYS,
|
|
@@ -23,6 +25,12 @@ const { execSync, spawn, spawnSync } = require('child_process');
|
|
|
23
25
|
const pkg = require('../package.json');
|
|
24
26
|
const { getGatewayAutostartStatus, installGatewayAutostart, removeGatewayAutostart } = require('../lib/autostart');
|
|
25
27
|
const { buildMenuStatusLines, buildStatusView } = require('../lib/panel');
|
|
28
|
+
const {
|
|
29
|
+
buildVsCodeOpenCommand,
|
|
30
|
+
buildVsCodeModelSelectionIds,
|
|
31
|
+
clearVsCodeIntegration,
|
|
32
|
+
writeVsCodeIntegration,
|
|
33
|
+
} = require('../lib/vscode');
|
|
26
34
|
const {
|
|
27
35
|
buildClaudeInstallCommand,
|
|
28
36
|
buildYingclawUpgradeCommand,
|
|
@@ -42,6 +50,7 @@ const {
|
|
|
42
50
|
isDesktopChatModel,
|
|
43
51
|
} = require('../lib/gateway');
|
|
44
52
|
const { runDoctorChecks, summarize, STATUS_OK, STATUS_FAIL, STATUS_WARN, STATUS_INFO } = require('../lib/doctor');
|
|
53
|
+
const { normalizeOpenAiBaseUrl } = require('../lib/openai');
|
|
45
54
|
|
|
46
55
|
const program = new Command();
|
|
47
56
|
|
|
@@ -177,6 +186,12 @@ function getChatModelChoices(models) {
|
|
|
177
186
|
return models.filter(isDesktopChatModel).map(id => ({ name: id, value: id }));
|
|
178
187
|
}
|
|
179
188
|
|
|
189
|
+
function sortModelChoicesByRelatedFamily(choices, mainModel) {
|
|
190
|
+
const order = sortRelatedModelIds(choices.map((choice) => choice.value), mainModel);
|
|
191
|
+
const rank = new Map(order.map((value, index) => [value, index]));
|
|
192
|
+
return [...choices].sort((a, b) => (rank.get(a.value) ?? 9999) - (rank.get(b.value) ?? 9999));
|
|
193
|
+
}
|
|
194
|
+
|
|
180
195
|
function formatFetchedModelCount(chalk, total, chatCount) {
|
|
181
196
|
if (total === chatCount) return chalk.green(`已获取 ${chatCount} 个可用聊天模型`);
|
|
182
197
|
return chalk.green(`已获取 ${total} 个模型,其中 ${chatCount} 个可用于 Claude/桌面聊天`);
|
|
@@ -190,14 +205,35 @@ async function promptManualModel(chalk, message, defaultValue) {
|
|
|
190
205
|
}).then(v => v.trim());
|
|
191
206
|
}
|
|
192
207
|
|
|
193
|
-
|
|
208
|
+
function isOpenAiCompatibleConfig(config) {
|
|
209
|
+
return getProviderProtocol(config) === 'openai';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function ensureGatewayForOpenAiConfig(config) {
|
|
213
|
+
return isOpenAiCompatibleConfig(config) ? ensureDesktopGatewayConfig(config) : config;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function writeClaudeCodeEnv(config) {
|
|
217
|
+
const effectiveConfig = buildClaudeEnv(config);
|
|
218
|
+
return writeEnvToZshrc(
|
|
219
|
+
effectiveConfig.ANTHROPIC_BASE_URL,
|
|
220
|
+
effectiveConfig.ANTHROPIC_AUTH_TOKEN,
|
|
221
|
+
effectiveConfig.ANTHROPIC_MODEL,
|
|
222
|
+
effectiveConfig.CLAUDE_CODE_SUBAGENT_MODEL,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function configureCustomProvider({ chalk, ora, existingConfig, providerKey = 'custom' }) {
|
|
227
|
+
const provider = PROVIDERS[providerKey];
|
|
228
|
+
const protocol = provider.protocol || 'anthropic';
|
|
229
|
+
const sameProvider = existingConfig?.provider === providerKey;
|
|
194
230
|
const baseUrl = await input({
|
|
195
|
-
message: chalk.cyan('Anthropic Base URL'),
|
|
196
|
-
default:
|
|
231
|
+
message: chalk.cyan(protocol === 'openai' ? 'OpenAI Base URL' : 'Anthropic Base URL'),
|
|
232
|
+
default: sameProvider ? existingConfig.baseUrl : undefined,
|
|
197
233
|
validate: (v) => v.trim().length > 0 && isValidUrl(v.trim()) ? true : '请输入有效 URL',
|
|
198
|
-
}).then(v => normalizeAnthropicBaseUrl(v.trim()));
|
|
234
|
+
}).then(v => protocol === 'openai' ? normalizeOpenAiBaseUrl(v.trim()) : normalizeAnthropicBaseUrl(v.trim()));
|
|
199
235
|
|
|
200
|
-
let apiKey =
|
|
236
|
+
let apiKey = sameProvider ? existingConfig.apiKey : '';
|
|
201
237
|
if (apiKey) {
|
|
202
238
|
const keepKey = await confirm({ message: '沿用当前 API Key?', default: true });
|
|
203
239
|
if (!keepKey) apiKey = '';
|
|
@@ -213,7 +249,7 @@ async function configureCustomProvider({ chalk, ora, existingConfig }) {
|
|
|
213
249
|
let modelChoices = [];
|
|
214
250
|
let modelsUrl;
|
|
215
251
|
const fetchSpinner = ora('正在自动获取可用模型...').start();
|
|
216
|
-
const onlineResult = await fetchModelsFromBaseUrl(
|
|
252
|
+
const onlineResult = await fetchModelsFromBaseUrl(providerKey, apiKey, baseUrl);
|
|
217
253
|
if (onlineResult) {
|
|
218
254
|
modelsUrl = onlineResult.modelsUrl;
|
|
219
255
|
modelChoices = getChatModelChoices(onlineResult.models);
|
|
@@ -231,7 +267,11 @@ async function configureCustomProvider({ chalk, ora, existingConfig }) {
|
|
|
231
267
|
if (modelChoices.length > 0) {
|
|
232
268
|
model = await promptModelFromChoices({ chalk, choices: modelChoices, message: '选择主模型' });
|
|
233
269
|
if (model === '__BACK__') return null;
|
|
234
|
-
fastModel = await promptModelFromChoices({
|
|
270
|
+
fastModel = await promptModelFromChoices({
|
|
271
|
+
chalk,
|
|
272
|
+
choices: sortModelChoicesByRelatedFamily(modelChoices, model),
|
|
273
|
+
message: '选择快速模型 / Subagent 模型',
|
|
274
|
+
});
|
|
235
275
|
if (fastModel === '__BACK__') return null;
|
|
236
276
|
} else {
|
|
237
277
|
model = await promptManualModel(chalk, '输入主模型名');
|
|
@@ -239,8 +279,9 @@ async function configureCustomProvider({ chalk, ora, existingConfig }) {
|
|
|
239
279
|
}
|
|
240
280
|
|
|
241
281
|
return {
|
|
242
|
-
provider:
|
|
243
|
-
providerName:
|
|
282
|
+
provider: providerKey,
|
|
283
|
+
providerName: provider.name,
|
|
284
|
+
protocol,
|
|
244
285
|
baseUrl,
|
|
245
286
|
modelsUrl: modelsUrl || undefined,
|
|
246
287
|
apiKey,
|
|
@@ -353,7 +394,7 @@ async function runConfigFlow({ writeCodeEnv = false } = {}) {
|
|
|
353
394
|
if (providerKey === '__BACK__') return;
|
|
354
395
|
provider = PROVIDERS[providerKey];
|
|
355
396
|
if (provider.custom) {
|
|
356
|
-
customConfig = await configureCustomProvider({ chalk, ora });
|
|
397
|
+
customConfig = await configureCustomProvider({ chalk, ora, providerKey });
|
|
357
398
|
if (!customConfig) { step = 'provider'; continue; }
|
|
358
399
|
break;
|
|
359
400
|
}
|
|
@@ -407,9 +448,10 @@ async function runConfigFlow({ writeCodeEnv = false } = {}) {
|
|
|
407
448
|
try {
|
|
408
449
|
const fastModel = customConfig?.fastModel || resolveFastModel(provider, model);
|
|
409
450
|
cfg = customConfig || { provider: providerKey, model, fastModel, apiKey, baseUrl: provider.baseUrl, availableModels };
|
|
451
|
+
cfg = ensureGatewayForOpenAiConfig(cfg);
|
|
410
452
|
saveConfig(cfg);
|
|
411
453
|
if (writeCodeEnv) {
|
|
412
|
-
({ file } =
|
|
454
|
+
({ file } = writeClaudeCodeEnv(cfg));
|
|
413
455
|
}
|
|
414
456
|
spinner.succeed(chalk.green(writeCodeEnv ? 'API 连接已保存,Claude Code 终端已接入' : 'API 连接已保存'));
|
|
415
457
|
} catch (e) {
|
|
@@ -546,7 +588,7 @@ program
|
|
|
546
588
|
|
|
547
589
|
console.log(await getBanner());
|
|
548
590
|
|
|
549
|
-
|
|
591
|
+
let config = loadConfig();
|
|
550
592
|
if (!config) {
|
|
551
593
|
console.log(chalk.red('\n未配置 API 连接,请先运行: claw config\n'));
|
|
552
594
|
return;
|
|
@@ -561,7 +603,9 @@ program
|
|
|
561
603
|
const spinner = ora('写入 Claude Code 终端环境变量...').start();
|
|
562
604
|
let file;
|
|
563
605
|
try {
|
|
564
|
-
|
|
606
|
+
const effectiveConfig = ensureGatewayForOpenAiConfig(config);
|
|
607
|
+
if (effectiveConfig !== config) saveConfig(effectiveConfig);
|
|
608
|
+
({ file } = writeClaudeCodeEnv(effectiveConfig));
|
|
565
609
|
spinner.succeed(chalk.green(`Claude Code 终端已接入 → ${file}`));
|
|
566
610
|
} catch (e) {
|
|
567
611
|
spinner.fail(chalk.red(`写入失败: ${e.message}`));
|
|
@@ -569,10 +613,11 @@ program
|
|
|
569
613
|
}
|
|
570
614
|
|
|
571
615
|
console.log(chalk.dim(getStorageHint(file)));
|
|
616
|
+
const displayedConfig = ensureGatewayForOpenAiConfig(config);
|
|
572
617
|
console.log(boxen(
|
|
573
618
|
chalk.bold('Claude Code 终端已接入\n\n') +
|
|
574
|
-
chalk.dim('Base URL ') + chalk.cyan(
|
|
575
|
-
chalk.dim('模型 ') + chalk.yellow(
|
|
619
|
+
chalk.dim('Base URL ') + chalk.cyan(buildClaudeEnv(displayedConfig).ANTHROPIC_BASE_URL) + '\n' +
|
|
620
|
+
chalk.dim('模型 ') + chalk.yellow(displayedConfig.model) + '\n\n' +
|
|
576
621
|
chalk.white('需要启动时,在主菜单选择“启动 Claude Code”,或直接输入 ') + chalk.cyan.bold('claude') + '\n' +
|
|
577
622
|
chalk.dim(getActivationHint(file)),
|
|
578
623
|
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
|
|
@@ -620,6 +665,119 @@ program
|
|
|
620
665
|
}
|
|
621
666
|
});
|
|
622
667
|
|
|
668
|
+
program
|
|
669
|
+
.command('vscode')
|
|
670
|
+
.description('接入 VS Code Claude Code 扩展')
|
|
671
|
+
.option('--terminal', '让 Claude Code 扩展默认使用终端模式')
|
|
672
|
+
.action(async (options) => {
|
|
673
|
+
const chalk = (await import('chalk')).default;
|
|
674
|
+
const ora = (await import('ora')).default;
|
|
675
|
+
const boxen = (await import('boxen')).default;
|
|
676
|
+
|
|
677
|
+
console.log(await getBanner());
|
|
678
|
+
|
|
679
|
+
const config = loadConfig();
|
|
680
|
+
if (!config) {
|
|
681
|
+
console.log(chalk.red('\n未配置 API 连接,请先运行: claw config\n'));
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
const configProblem = getConfigValidationMessage(config);
|
|
685
|
+
if (configProblem) {
|
|
686
|
+
console.log(chalk.red(`\n配置无效:${configProblem}`));
|
|
687
|
+
console.log(chalk.dim('请运行 claw config 重新配置。\n'));
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
let effectiveConfig = ensureGatewayForOpenAiConfig(config);
|
|
692
|
+
if (effectiveConfig !== config) saveConfig(effectiveConfig);
|
|
693
|
+
|
|
694
|
+
const vscodeModelChoices = getChatModelChoices(buildVsCodeModelSelectionIds(effectiveConfig));
|
|
695
|
+
const vscodeModel = await promptModelFromChoices({
|
|
696
|
+
chalk,
|
|
697
|
+
choices: vscodeModelChoices,
|
|
698
|
+
message: '选择 VS Code Claude Code 主模型',
|
|
699
|
+
allowManual: true,
|
|
700
|
+
});
|
|
701
|
+
if (vscodeModel === '__BACK__') return;
|
|
702
|
+
|
|
703
|
+
const vscodeFastModel = await promptModelFromChoices({
|
|
704
|
+
chalk,
|
|
705
|
+
choices: sortModelChoicesByRelatedFamily(vscodeModelChoices, vscodeModel),
|
|
706
|
+
message: '选择 VS Code Claude Code 快速模型 / 备用模型',
|
|
707
|
+
allowManual: true,
|
|
708
|
+
});
|
|
709
|
+
if (vscodeFastModel === '__BACK__') return;
|
|
710
|
+
|
|
711
|
+
const vscodeConfig = {
|
|
712
|
+
...config,
|
|
713
|
+
...effectiveConfig,
|
|
714
|
+
vscodeModel,
|
|
715
|
+
vscodeFastModel,
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
const spinner = ora('写入 VS Code Claude Code 配置...').start();
|
|
719
|
+
let result;
|
|
720
|
+
try {
|
|
721
|
+
result = writeVsCodeIntegration(vscodeConfig, options.terminal ? { useTerminal: true } : {});
|
|
722
|
+
spinner.succeed(chalk.green('VS Code Claude Code 已接入'));
|
|
723
|
+
} catch (e) {
|
|
724
|
+
spinner.fail(chalk.red(`写入失败: ${e.message}`));
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
console.log(boxen(
|
|
729
|
+
chalk.bold('VS Code Claude Code 扩展已配置\n\n') +
|
|
730
|
+
chalk.dim('共享设置 ') + chalk.cyan(result.claudeSettings.file) + '\n' +
|
|
731
|
+
chalk.dim('VS Code ') + chalk.cyan(result.vscodeSettings.file) + '\n' +
|
|
732
|
+
chalk.dim('主模型 ') + chalk.yellow(vscodeModel) + '\n' +
|
|
733
|
+
chalk.dim('快速模型 ') + chalk.yellow(vscodeFastModel) + '\n\n' +
|
|
734
|
+
chalk.white('已写入 Claude Code settings.json 的 env,并关闭 VS Code 扩展登录提示。\n') +
|
|
735
|
+
chalk.dim('如果 VS Code 已打开,请执行 Developer: Reload Window 或完全重启 VS Code。'),
|
|
736
|
+
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
|
|
737
|
+
));
|
|
738
|
+
|
|
739
|
+
const shouldOpen = await confirm({ message: '是否现在打开 Claude Code 的 VS Code 面板?', default: false });
|
|
740
|
+
if (shouldOpen) {
|
|
741
|
+
const openCommand = buildVsCodeOpenCommand(process.platform);
|
|
742
|
+
spawnSync(openCommand.command, openCommand.args, { stdio: 'ignore', windowsHide: true });
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
program
|
|
747
|
+
.command('vscode-reset')
|
|
748
|
+
.description('恢复 VS Code Claude Code 扩展默认配置')
|
|
749
|
+
.action(async () => {
|
|
750
|
+
const chalk = (await import('chalk')).default;
|
|
751
|
+
const ora = (await import('ora')).default;
|
|
752
|
+
const boxen = (await import('boxen')).default;
|
|
753
|
+
|
|
754
|
+
console.log(await getBanner());
|
|
755
|
+
|
|
756
|
+
const yes = await confirm({
|
|
757
|
+
message: chalk.red('确定要恢复 VS Code Claude Code 扩展默认配置吗?API 连接、终端和桌面配置不会被清除'),
|
|
758
|
+
default: false,
|
|
759
|
+
});
|
|
760
|
+
if (!yes) {
|
|
761
|
+
console.log(chalk.dim('已取消'));
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
const spinner = ora('正在恢复 VS Code Claude Code 默认配置...').start();
|
|
766
|
+
const result = clearVsCodeIntegration();
|
|
767
|
+
|
|
768
|
+
if (result.result === 'updated') {
|
|
769
|
+
spinner.succeed(chalk.green('VS Code Claude Code 已恢复默认'));
|
|
770
|
+
console.log(boxen(
|
|
771
|
+
chalk.bold('已清除以下 VS Code / Claude Code 扩展配置:\n\n') +
|
|
772
|
+
result.files.map(f => chalk.cyan(' • ' + f)).join('\n') +
|
|
773
|
+
'\n\n' + chalk.dim('如果 VS Code 已打开,请执行 Developer: Reload Window 或完全重启 VS Code。'),
|
|
774
|
+
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
|
|
775
|
+
));
|
|
776
|
+
} else {
|
|
777
|
+
spinner.warn(chalk.yellow('没有找到 VS Code Claude Code 的 yingclaw 配置,无需恢复'));
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
|
|
623
781
|
program
|
|
624
782
|
.command('switch')
|
|
625
783
|
.description('快速切换模型(只更新 API 连接)')
|
|
@@ -652,14 +810,15 @@ program
|
|
|
652
810
|
|
|
653
811
|
const provider = PROVIDERS[providerKey];
|
|
654
812
|
if (provider.custom) {
|
|
655
|
-
const customConfig = await configureCustomProvider({ chalk, ora, existingConfig: config });
|
|
813
|
+
const customConfig = await configureCustomProvider({ chalk, ora, existingConfig: config, providerKey });
|
|
656
814
|
if (!customConfig) return;
|
|
657
815
|
|
|
658
816
|
const spinner = ora('切换中...').start();
|
|
659
|
-
|
|
660
|
-
|
|
817
|
+
const savedConfig = ensureGatewayForOpenAiConfig(customConfig);
|
|
818
|
+
saveConfig(savedConfig);
|
|
819
|
+
spinner.succeed(chalk.green(`API 连接已切换至 ${savedConfig.providerName} · ${savedConfig.model}`));
|
|
661
820
|
console.log(chalk.dim('如需让外部 claude 命令使用新模型,请运行 claw code。'));
|
|
662
|
-
await offerDesktopSync(chalk, ora,
|
|
821
|
+
await offerDesktopSync(chalk, ora, savedConfig);
|
|
663
822
|
return;
|
|
664
823
|
}
|
|
665
824
|
|
|
@@ -793,6 +952,10 @@ program
|
|
|
793
952
|
console.log(chalk.yellow('\nClaude 桌面应用 3P 配置目前仅支持 macOS / Windows。\n'));
|
|
794
953
|
return;
|
|
795
954
|
}
|
|
955
|
+
if (options.direct && isOpenAiCompatibleConfig(config)) {
|
|
956
|
+
console.log(chalk.yellow('\nOpenAI 兼容接口必须通过本机 Gateway 转换协议,不能使用直连模式。\n'));
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
796
959
|
|
|
797
960
|
let desktopConfig = config;
|
|
798
961
|
if (!options.direct) {
|
|
@@ -942,7 +1105,7 @@ program
|
|
|
942
1105
|
|
|
943
1106
|
program
|
|
944
1107
|
.command('reset')
|
|
945
|
-
.description('清除 API
|
|
1108
|
+
.description('清除 API 连接、终端环境变量、VS Code 和桌面配置')
|
|
946
1109
|
.action(async () => {
|
|
947
1110
|
const chalk = (await import('chalk')).default;
|
|
948
1111
|
const ora = (await import('ora')).default;
|
|
@@ -965,6 +1128,10 @@ program
|
|
|
965
1128
|
if (desktopCleared.result === 'updated') {
|
|
966
1129
|
cleared.push(...desktopConfigClearedPaths(desktopCleared));
|
|
967
1130
|
}
|
|
1131
|
+
const vscodeCleared = clearVsCodeIntegration();
|
|
1132
|
+
if (vscodeCleared.result === 'updated') {
|
|
1133
|
+
cleared.push(...vscodeCleared.files);
|
|
1134
|
+
}
|
|
968
1135
|
const autostartCleared = removeGatewayAutostart();
|
|
969
1136
|
if (autostartCleared.result === 'removed' && autostartCleared.file) {
|
|
970
1137
|
cleared.push(autostartCleared.file);
|
|
@@ -1131,11 +1298,12 @@ async function renderStatusBar(apiStatus) {
|
|
|
1131
1298
|
const statusLines = buildMenuStatusLines(view, { apiStatus, claudeInstalled, platform: process.platform });
|
|
1132
1299
|
cfgPart = statusLines.map((line, index) => {
|
|
1133
1300
|
if (index === 0) return line.replace('API 正常', chalk.green('API 正常')).replace('API Key 无效', chalk.red('API Key 无效')).replace('网络/服务异常', chalk.yellow('网络/服务异常'));
|
|
1134
|
-
if (line.startsWith('
|
|
1301
|
+
if (line.startsWith('终端未生效')) return chalk.yellow(line);
|
|
1135
1302
|
if (line.startsWith('旧模型名')) return chalk.yellow(line);
|
|
1303
|
+
if (line.startsWith('快速模型跨系列')) return chalk.yellow(line);
|
|
1136
1304
|
if (line.startsWith('Desktop Gateway 未运行')) return chalk.yellow(line);
|
|
1137
1305
|
if (line.startsWith('Desktop Gateway 已运行')) return chalk.green(line);
|
|
1138
|
-
if (line.startsWith('
|
|
1306
|
+
if (line.startsWith('模型')) return line.replace(view.mainModel, chalk.yellow(view.mainModel)).replace(view.fastModel, chalk.yellow(view.fastModel));
|
|
1139
1307
|
return line;
|
|
1140
1308
|
}).join('\n ');
|
|
1141
1309
|
} else {
|
|
@@ -1198,6 +1366,7 @@ async function runAdvancedMenu(chalk, hasConfig) {
|
|
|
1198
1366
|
{ name: '🩺 诊断(一键自检并给出修复建议)', value: 'doctor' },
|
|
1199
1367
|
{ name: '🔁 重新检测 API', value: 'recheck', disabled: !hasConfig && ADVANCED_DISABLED_HINT },
|
|
1200
1368
|
{ name: '↩️ 恢复 Claude Code 终端默认', value: 'code-reset' },
|
|
1369
|
+
{ name: '↩️ 恢复 VS Code Claude Code 默认', value: 'vscode-reset' },
|
|
1201
1370
|
{ name: '↩️ 恢复 Claude 桌面默认', value: 'desktop-reset' },
|
|
1202
1371
|
{ name: '🗑 清除所有 yingclaw 配置', value: 'reset' },
|
|
1203
1372
|
{ name: '⬆️ 检查更新', value: 'update' },
|
|
@@ -1253,6 +1422,7 @@ async function runMenu() {
|
|
|
1253
1422
|
{ name: config ? '🔑 重新配置 API 连接' : '🔑 配置 API 连接', value: 'config' },
|
|
1254
1423
|
{ name: '🔄 切换厂商或模型', value: 'switch', disabled: disabledHint },
|
|
1255
1424
|
{ name: '💻 接入 Claude Code 终端', value: 'code', disabled: disabledHint },
|
|
1425
|
+
{ name: '🧩 接入 VS Code Claude Code 扩展', value: 'vscode', disabled: disabledHint },
|
|
1256
1426
|
{ name: '🖥 接入 Claude 桌面应用', value: 'desktop', disabled: disabledHint },
|
|
1257
1427
|
{ name: '📊 查看当前配置', value: 'status', disabled: !config && '需先配置 API 连接' },
|
|
1258
1428
|
{ name: '🛠 高级 ›', value: 'advanced' },
|
|
@@ -1276,8 +1446,13 @@ async function runMenu() {
|
|
|
1276
1446
|
}
|
|
1277
1447
|
|
|
1278
1448
|
if (resolvedAction === 'launch') {
|
|
1279
|
-
|
|
1449
|
+
let cfg = loadConfig();
|
|
1280
1450
|
if (!cfg || getConfigValidationMessage(cfg)) continue;
|
|
1451
|
+
const effectiveConfig = ensureGatewayForOpenAiConfig(cfg);
|
|
1452
|
+
if (effectiveConfig !== cfg) {
|
|
1453
|
+
saveConfig(effectiveConfig);
|
|
1454
|
+
cfg = effectiveConfig;
|
|
1455
|
+
}
|
|
1281
1456
|
await new Promise((resolve) => {
|
|
1282
1457
|
const child = spawn('claude', [], {
|
|
1283
1458
|
stdio: 'inherit',
|
|
@@ -1297,7 +1472,9 @@ async function runMenu() {
|
|
|
1297
1472
|
install: 'install-claude',
|
|
1298
1473
|
config: 'config',
|
|
1299
1474
|
code: 'code',
|
|
1475
|
+
vscode: 'vscode',
|
|
1300
1476
|
'code-reset': 'code-reset',
|
|
1477
|
+
'vscode-reset': 'vscode-reset',
|
|
1301
1478
|
switch: 'switch',
|
|
1302
1479
|
desktop: 'desktop',
|
|
1303
1480
|
'desktop-reset': 'desktop-reset',
|
|
@@ -1315,7 +1492,7 @@ async function runMenu() {
|
|
|
1315
1492
|
});
|
|
1316
1493
|
|
|
1317
1494
|
// 改 config 的命令需要刷新缓存
|
|
1318
|
-
if (['config', 'switch', 'reset', 'code-reset', 'desktop-reset'].includes(resolvedAction)) {
|
|
1495
|
+
if (['config', 'switch', 'reset', 'code-reset', 'vscode-reset', 'desktop-reset', 'vscode'].includes(resolvedAction)) {
|
|
1319
1496
|
lastCheckResult = undefined;
|
|
1320
1497
|
lastCheckedHash = null;
|
|
1321
1498
|
}
|