zcf 2.7.1 → 2.8.1
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 +79 -23
- package/bin/zcf.mjs +1 -1
- package/dist/{shared/zcf.CZ3RjfyP.mjs → chunks/simple-config.mjs} +1362 -329
- package/dist/cli.mjs +492 -131
- package/dist/index.d.mts +22 -240
- package/dist/index.d.ts +22 -240
- package/dist/index.mjs +12 -1
- package/package.json +6 -1
|
@@ -5,10 +5,21 @@ import { join, dirname } from 'pathe';
|
|
|
5
5
|
import dayjs from 'dayjs';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { exec } from 'tinyexec';
|
|
8
|
-
import {
|
|
8
|
+
import { exec as exec$1 } from 'child_process';
|
|
9
|
+
import { promisify } from 'util';
|
|
10
|
+
import ora from 'ora';
|
|
11
|
+
import prompts$1 from 'prompts';
|
|
12
|
+
import { readFile as readFile$1, writeFile as writeFile$1, mkdir } from 'fs/promises';
|
|
13
|
+
import { join as join$1 } from 'path';
|
|
14
|
+
import { homedir as homedir$1 } from 'os';
|
|
15
|
+
import semver from 'semver';
|
|
16
|
+
import { rm, mkdir as mkdir$1, copyFile as copyFile$1 } from 'node:fs/promises';
|
|
17
|
+
import { exec as exec$2 } from 'node:child_process';
|
|
18
|
+
import { promisify as promisify$1 } from 'node:util';
|
|
9
19
|
import { homedir, platform } from 'node:os';
|
|
20
|
+
import { join as join$2 } from 'node:path';
|
|
10
21
|
|
|
11
|
-
const version = "2.
|
|
22
|
+
const version = "2.8.1";
|
|
12
23
|
const homepage = "https://github.com/UfoMiao/zcf";
|
|
13
24
|
|
|
14
25
|
const common$1 = {
|
|
@@ -52,6 +63,7 @@ const installation$1 = {
|
|
|
52
63
|
installPrompt: "\u68C0\u6D4B\u5230 Claude Code \u672A\u5B89\u88C5\uFF0C\u662F\u5426\u81EA\u52A8\u5B89\u88C5\uFF1F",
|
|
53
64
|
installing: "\u6B63\u5728\u5B89\u88C5 Claude Code...",
|
|
54
65
|
installSuccess: "Claude Code \u5B89\u88C5\u6210\u529F",
|
|
66
|
+
alreadyInstalled: "Claude Code \u5DF2\u5B89\u88C5",
|
|
55
67
|
installFailed: "Claude Code \u5B89\u88C5\u5931\u8D25",
|
|
56
68
|
npmNotFound: "npm \u672A\u5B89\u88C5\u3002\u8BF7\u5148\u5B89\u88C5 Node.js \u548C npm\u3002",
|
|
57
69
|
// Termux specific
|
|
@@ -70,6 +82,8 @@ const api$1 = {
|
|
|
70
82
|
authTokenDesc: "\u9002\u7528\u4E8E\u901A\u8FC7 OAuth \u6216\u6D4F\u89C8\u5668\u767B\u5F55\u83B7\u53D6\u7684\u4EE4\u724C",
|
|
71
83
|
useApiKey: "\u4F7F\u7528 API Key (\u5BC6\u94A5\u8BA4\u8BC1)",
|
|
72
84
|
apiKeyDesc: "\u9002\u7528\u4E8E\u4ECE Anthropic Console \u83B7\u53D6\u7684 API \u5BC6\u94A5",
|
|
85
|
+
useCcrProxy: "\u4F7F\u7528 CCR \u4EE3\u7406",
|
|
86
|
+
ccrProxyDesc: "\u901A\u8FC7 Claude Code Router \u4F7F\u7528\u591A\u4E2A AI \u6A21\u578B",
|
|
73
87
|
skipApi: "\u8DF3\u8FC7\uFF08\u7A0D\u540E\u624B\u52A8\u914D\u7F6E\uFF09",
|
|
74
88
|
enterApiUrl: "\u8BF7\u8F93\u5165 API URL",
|
|
75
89
|
enterAuthToken: "\u8BF7\u8F93\u5165 Auth Token",
|
|
@@ -117,11 +131,21 @@ const configuration$1 = {
|
|
|
117
131
|
// Model configuration
|
|
118
132
|
selectDefaultModel: "\u9009\u62E9\u9ED8\u8BA4\u6A21\u578B",
|
|
119
133
|
modelConfigSuccess: "\u9ED8\u8BA4\u6A21\u578B\u5DF2\u914D\u7F6E",
|
|
134
|
+
existingModelConfig: "\u68C0\u6D4B\u5230\u5DF2\u6709\u6A21\u578B\u914D\u7F6E",
|
|
135
|
+
currentModel: "\u5F53\u524D\u6A21\u578B",
|
|
136
|
+
modifyModel: "\u662F\u5426\u4FEE\u6539\u6A21\u578B\u914D\u7F6E\uFF1F",
|
|
137
|
+
keepModel: "\u4FDD\u6301\u5F53\u524D\u6A21\u578B\u914D\u7F6E",
|
|
138
|
+
defaultModelOption: "\u9ED8\u8BA4\uFF08\u8BA9 Claude Code \u81EA\u52A8\u9009\u62E9\uFF09",
|
|
139
|
+
modelConfigured: "\u9ED8\u8BA4\u6A21\u578B\u5DF2\u914D\u7F6E",
|
|
120
140
|
// AI memory configuration
|
|
121
141
|
selectMemoryOption: "\u9009\u62E9\u914D\u7F6E\u9009\u9879",
|
|
122
142
|
configureAiLanguage: "\u914D\u7F6E AI \u8F93\u51FA\u8BED\u8A00",
|
|
123
143
|
configureAiPersonality: "\u914D\u7F6E AI \u4E2A\u6027\u98CE\u683C",
|
|
124
144
|
aiLanguageConfigured: "AI \u8F93\u51FA\u8BED\u8A00\u5DF2\u914D\u7F6E",
|
|
145
|
+
existingLanguageConfig: "\u68C0\u6D4B\u5230\u5DF2\u6709 AI \u8F93\u51FA\u8BED\u8A00\u914D\u7F6E",
|
|
146
|
+
currentLanguage: "\u5F53\u524D\u8BED\u8A00",
|
|
147
|
+
modifyLanguage: "\u662F\u5426\u4FEE\u6539 AI \u8F93\u51FA\u8BED\u8A00\uFF1F",
|
|
148
|
+
keepLanguage: "\u4FDD\u6301\u5F53\u524D\u8BED\u8A00\u914D\u7F6E",
|
|
125
149
|
// AI personality
|
|
126
150
|
selectAiPersonality: "\u9009\u62E9 AI \u4E2A\u6027\u98CE\u683C",
|
|
127
151
|
customPersonalityHint: "\u5B9A\u4E49\u4F60\u81EA\u5DF1\u7684\u4E2A\u6027",
|
|
@@ -147,7 +171,17 @@ const configuration$1 = {
|
|
|
147
171
|
openSettingsJsonDesc: "\u9AD8\u7EA7\u7528\u6237\u81EA\u5B9A\u4E49",
|
|
148
172
|
envImportSuccess: "\u73AF\u5883\u53D8\u91CF\u5DF2\u5BFC\u5165",
|
|
149
173
|
permissionsImportSuccess: "\u6743\u9650\u914D\u7F6E\u5DF2\u5BFC\u5165",
|
|
150
|
-
openingSettingsJson: "\u6B63\u5728\u6253\u5F00 settings.json..."
|
|
174
|
+
openingSettingsJson: "\u6B63\u5728\u6253\u5F00 settings.json...",
|
|
175
|
+
// JSON config related
|
|
176
|
+
invalidConfiguration: "\u914D\u7F6E\u65E0\u6548",
|
|
177
|
+
failedToParseJson: "\u89E3\u6790 JSON \u6587\u4EF6\u5931\u8D25:",
|
|
178
|
+
failedToBackupConfig: "\u5907\u4EFD\u914D\u7F6E\u5931\u8D25",
|
|
179
|
+
failedToReadTemplateSettings: "\u8BFB\u53D6\u6A21\u677F\u8BBE\u7F6E\u5931\u8D25",
|
|
180
|
+
failedToMergeSettings: "\u5408\u5E76\u8BBE\u7F6E\u5931\u8D25",
|
|
181
|
+
preservingExistingSettings: "\u4FDD\u7559\u73B0\u6709\u8BBE\u7F6E",
|
|
182
|
+
memoryDirNotFound: "\u672A\u627E\u5230\u8BB0\u5FC6\u76EE\u5F55",
|
|
183
|
+
failedToSetOnboarding: "\u8BBE\u7F6E\u5F15\u5BFC\u6807\u5FD7\u5931\u8D25",
|
|
184
|
+
fixWindowsMcp: "\u4FEE\u590D Windows MCP \u914D\u7F6E\uFF1F"
|
|
151
185
|
};
|
|
152
186
|
|
|
153
187
|
const mcp$1 = {
|
|
@@ -174,11 +208,14 @@ const menu$1 = {
|
|
|
174
208
|
menuOptions: {
|
|
175
209
|
fullInit: "\u5B8C\u6574\u521D\u59CB\u5316",
|
|
176
210
|
importWorkflow: "\u5BFC\u5165\u5DE5\u4F5C\u6D41",
|
|
211
|
+
configureApiOrCcr: "\u914D\u7F6E API \u6216 CCR \u4EE3\u7406",
|
|
177
212
|
configureApi: "\u914D\u7F6E API",
|
|
178
213
|
configureMcp: "\u914D\u7F6E MCP",
|
|
179
214
|
configureModel: "\u914D\u7F6E\u9ED8\u8BA4\u6A21\u578B",
|
|
180
215
|
configureAiMemory: "\u914D\u7F6E Claude \u5168\u5C40\u8BB0\u5FC6",
|
|
181
216
|
configureEnvPermission: "\u5BFC\u5165\u63A8\u8350\u73AF\u5883\u53D8\u91CF\u548C\u6743\u9650\u914D\u7F6E",
|
|
217
|
+
configureCcr: "\u914D\u7F6E\u6A21\u578B\u4EE3\u7406 (CCR)",
|
|
218
|
+
ccrManagement: "CCR",
|
|
182
219
|
ccusage: "ccusage",
|
|
183
220
|
installBmad: "\u5B89\u88C5 BMad Method",
|
|
184
221
|
clearCache: "\u6E05\u9664\u504F\u597D\u7F13\u5B58",
|
|
@@ -186,13 +223,16 @@ const menu$1 = {
|
|
|
186
223
|
exit: "\u9000\u51FA"
|
|
187
224
|
},
|
|
188
225
|
menuDescriptions: {
|
|
189
|
-
fullInit: "\u5B89\u88C5 Claude Code + \u5BFC\u5165\u5DE5\u4F5C\u6D41 + \u914D\u7F6E API + \u914D\u7F6E MCP \u670D\u52A1",
|
|
226
|
+
fullInit: "\u5B89\u88C5 Claude Code + \u5BFC\u5165\u5DE5\u4F5C\u6D41 + \u914D\u7F6E API \u6216 CCR \u4EE3\u7406 + \u914D\u7F6E MCP \u670D\u52A1",
|
|
190
227
|
importWorkflow: "\u4EC5\u5BFC\u5165/\u66F4\u65B0\u5DE5\u4F5C\u6D41\u76F8\u5173\u6587\u4EF6",
|
|
228
|
+
configureApiOrCcr: "\u914D\u7F6E API URL\u3001\u8BA4\u8BC1\u4FE1\u606F\u6216 CCR \u4EE3\u7406",
|
|
191
229
|
configureApi: "\u914D\u7F6E API URL \u548C\u8BA4\u8BC1\u4FE1\u606F",
|
|
192
230
|
configureMcp: "\u914D\u7F6E MCP \u670D\u52A1\uFF08\u542B Windows \u4FEE\u590D\uFF09",
|
|
193
231
|
configureModel: "\u8BBE\u7F6E\u9ED8\u8BA4\u6A21\u578B\uFF08opus/sonnet\uFF09",
|
|
194
232
|
configureAiMemory: "\u914D\u7F6E AI \u8F93\u51FA\u8BED\u8A00\u548C\u89D2\u8272\u98CE\u683C",
|
|
195
233
|
configureEnvPermission: "\u5BFC\u5165\u9690\u79C1\u4FDD\u62A4\u73AF\u5883\u53D8\u91CF\u548C\u7CFB\u7EDF\u6743\u9650\u914D\u7F6E",
|
|
234
|
+
configureCcr: "\u914D\u7F6E Claude Code Router \u4EE5\u4F7F\u7528\u591A\u4E2A AI \u6A21\u578B",
|
|
235
|
+
ccrManagement: "\u914D\u7F6E Claude Code Router \u4EE5\u4F7F\u7528\u591A\u4E2A AI \u6A21\u578B",
|
|
196
236
|
ccusage: "Claude Code \u7528\u91CF\u5206\u6790",
|
|
197
237
|
installBmad: "AI \u9A71\u52A8\u7684\u5F00\u53D1\u65B9\u6CD5\u8BBA\u6846\u67B6",
|
|
198
238
|
clearCache: "\u6E05\u9664\u504F\u597D\u8BED\u8A00\u7B49\u7F13\u5B58",
|
|
@@ -326,19 +366,121 @@ const tools$1 = {
|
|
|
326
366
|
errorDetails: "\u9519\u8BEF\u8BE6\u60C5:"
|
|
327
367
|
};
|
|
328
368
|
|
|
369
|
+
const ccrMessages$1 = {
|
|
370
|
+
// Installation
|
|
371
|
+
installingCcr: "\u6B63\u5728\u5B89\u88C5 Claude Code Router...",
|
|
372
|
+
ccrInstallSuccess: "Claude Code Router \u5B89\u88C5\u6210\u529F",
|
|
373
|
+
ccrInstallFailed: "\u5B89\u88C5 Claude Code Router \u5931\u8D25",
|
|
374
|
+
ccrAlreadyInstalled: "Claude Code Router \u5DF2\u5B89\u88C5",
|
|
375
|
+
// Configuration
|
|
376
|
+
configureCcr: "\u914D\u7F6E\u6A21\u578B\u4EE3\u7406 (CCR)",
|
|
377
|
+
useCcrProxy: "\u4F7F\u7528 CCR \u4EE3\u7406",
|
|
378
|
+
ccrProxyDesc: "\u901A\u8FC7 Claude Code Router \u8FDE\u63A5\u591A\u4E2A AI \u6A21\u578B",
|
|
379
|
+
fetchingPresets: "\u6B63\u5728\u83B7\u53D6\u63D0\u4F9B\u5546\u9884\u8BBE...",
|
|
380
|
+
noPresetsAvailable: "\u6CA1\u6709\u53EF\u7528\u7684\u9884\u8BBE",
|
|
381
|
+
selectCcrPreset: "\u9009\u62E9\u4E00\u4E2A\u63D0\u4F9B\u5546\u9884\u8BBE\uFF1A",
|
|
382
|
+
keyRequired: "API \u5BC6\u94A5\u4E0D\u80FD\u4E3A\u7A7A",
|
|
383
|
+
// Existing config
|
|
384
|
+
existingCcrConfig: "\u53D1\u73B0\u73B0\u6709\u7684 CCR \u914D\u7F6E",
|
|
385
|
+
overwriteCcrConfig: "\u662F\u5426\u5907\u4EFD\u73B0\u6709\u7684 CCR \u914D\u7F6E\u5E76\u91CD\u65B0\u914D\u7F6E\uFF1F",
|
|
386
|
+
keepingExistingConfig: "\u4FDD\u7559\u73B0\u6709\u914D\u7F6E",
|
|
387
|
+
backupCcrConfig: "\u6B63\u5728\u5907\u4EFD\u73B0\u6709\u7684 CCR \u914D\u7F6E...",
|
|
388
|
+
ccrBackupSuccess: "CCR \u914D\u7F6E\u5DF2\u5907\u4EFD\u5230\uFF1A{path}",
|
|
389
|
+
ccrBackupFailed: "\u5907\u4EFD CCR \u914D\u7F6E\u5931\u8D25",
|
|
390
|
+
// Model selection
|
|
391
|
+
selectDefaultModelForProvider: "\u9009\u62E9 {provider} \u7684\u9ED8\u8BA4\u6A21\u578B\uFF1A",
|
|
392
|
+
enterApiKeyForProvider: "\u8BF7\u8F93\u5165 {provider} \u7684 API \u5BC6\u94A5\uFF1A",
|
|
393
|
+
// Skip option
|
|
394
|
+
skipOption: "\u8DF3\u8FC7\uFF0C\u5728 CCR \u4E2D\u81EA\u884C\u914D\u7F6E",
|
|
395
|
+
skipConfiguring: "\u8DF3\u8FC7\u9884\u8BBE\u914D\u7F6E\uFF0C\u5C06\u521B\u5EFA\u7A7A\u914D\u7F6E\u6846\u67B6",
|
|
396
|
+
// Success/Error messages
|
|
397
|
+
ccrConfigSuccess: "CCR \u914D\u7F6E\u5DF2\u4FDD\u5B58",
|
|
398
|
+
proxyConfigSuccess: "\u4EE3\u7406\u8BBE\u7F6E\u5DF2\u914D\u7F6E",
|
|
399
|
+
ccrConfigFailed: "\u914D\u7F6E CCR \u5931\u8D25",
|
|
400
|
+
ccrSetupComplete: "CCR \u8BBE\u7F6E\u5B8C\u6210",
|
|
401
|
+
fetchPresetsError: "\u83B7\u53D6\u63D0\u4F9B\u5546\u9884\u8BBE\u5931\u8D25",
|
|
402
|
+
failedToStartCcrService: "\u542F\u52A8 CCR \u670D\u52A1\u5931\u8D25",
|
|
403
|
+
errorStartingCcrService: "\u542F\u52A8 CCR \u670D\u52A1\u65F6\u51FA\u9519",
|
|
404
|
+
// CCR service status
|
|
405
|
+
ccrRestartSuccess: "CCR \u670D\u52A1\u5DF2\u91CD\u542F",
|
|
406
|
+
ccrRestartFailed: "CCR \u670D\u52A1\u91CD\u542F\u5931\u8D25",
|
|
407
|
+
// Configuration tips
|
|
408
|
+
configTips: "\u914D\u7F6E\u63D0\u793A",
|
|
409
|
+
useClaudeCommand: "\u8BF7\u4F7F\u7528 claude \u547D\u4EE4\u542F\u52A8 Claude Code\uFF08\u800C\u975E ccr code\uFF09",
|
|
410
|
+
advancedConfigTip: "\u60A8\u53EF\u4EE5\u4F7F\u7528 ccr ui \u547D\u4EE4\u8FDB\u884C\u66F4\u9AD8\u7EA7\u7684\u914D\u7F6E",
|
|
411
|
+
manualConfigTip: "\u624B\u52A8\u4FEE\u6539\u914D\u7F6E\u6587\u4EF6\u540E\uFF0C\u8BF7\u6267\u884C ccr restart \u4F7F\u914D\u7F6E\u751F\u6548",
|
|
412
|
+
// CCR Menu
|
|
413
|
+
ccrMenuTitle: "CCR - Claude Code Router \u7BA1\u7406",
|
|
414
|
+
ccrMenuOptions: {
|
|
415
|
+
initCcr: "\u521D\u59CB\u5316 CCR",
|
|
416
|
+
startUi: "\u542F\u52A8 CCR UI",
|
|
417
|
+
checkStatus: "\u67E5\u8BE2 CCR \u72B6\u6001",
|
|
418
|
+
restart: "\u91CD\u542F CCR",
|
|
419
|
+
start: "\u542F\u52A8 CCR",
|
|
420
|
+
stop: "\u505C\u6B62 CCR",
|
|
421
|
+
back: "\u8FD4\u56DE\u4E3B\u83DC\u5355"
|
|
422
|
+
},
|
|
423
|
+
ccrMenuDescriptions: {
|
|
424
|
+
initCcr: "\u5B89\u88C5\u5E76\u914D\u7F6E CCR",
|
|
425
|
+
startUi: "\u6253\u5F00 Web \u754C\u9762\u7BA1\u7406 CCR",
|
|
426
|
+
checkStatus: "\u67E5\u770B CCR \u670D\u52A1\u8FD0\u884C\u72B6\u6001",
|
|
427
|
+
restart: "\u91CD\u542F CCR \u670D\u52A1",
|
|
428
|
+
start: "\u542F\u52A8 CCR \u670D\u52A1",
|
|
429
|
+
stop: "\u505C\u6B62 CCR \u670D\u52A1"
|
|
430
|
+
},
|
|
431
|
+
// Command execution messages
|
|
432
|
+
startingCcrUi: "\u6B63\u5728\u542F\u52A8 CCR UI...",
|
|
433
|
+
ccrUiStarted: "CCR UI \u5DF2\u542F\u52A8",
|
|
434
|
+
checkingCcrStatus: "\u6B63\u5728\u67E5\u8BE2 CCR \u72B6\u6001...",
|
|
435
|
+
ccrStatusTitle: "CCR \u72B6\u6001\u4FE1\u606F\uFF1A",
|
|
436
|
+
restartingCcr: "\u6B63\u5728\u91CD\u542F CCR...",
|
|
437
|
+
ccrRestarted: "CCR \u5DF2\u91CD\u542F",
|
|
438
|
+
startingCcr: "\u6B63\u5728\u542F\u52A8 CCR...",
|
|
439
|
+
ccrStarted: "CCR \u5DF2\u542F\u52A8",
|
|
440
|
+
stoppingCcr: "\u6B63\u5728\u505C\u6B62 CCR...",
|
|
441
|
+
ccrStopped: "CCR \u5DF2\u505C\u6B62",
|
|
442
|
+
ccrCommandFailed: "\u6267\u884C CCR \u547D\u4EE4\u5931\u8D25",
|
|
443
|
+
// Configuration check messages
|
|
444
|
+
ccrNotConfigured: "CCR \u5C1A\u672A\u914D\u7F6E\u3002\u8BF7\u5148\u521D\u59CB\u5316 CCR\u3002",
|
|
445
|
+
pleaseInitFirst: "\u8BF7\u9009\u62E9\u9009\u9879 1 \u6765\u521D\u59CB\u5316 CCR\u3002",
|
|
446
|
+
// UI API Key messages
|
|
447
|
+
ccrUiApiKey: "CCR UI \u767B\u5F55\u5BC6\u94A5",
|
|
448
|
+
ccrUiApiKeyHint: "\u4F7F\u7528\u6B64\u5BC6\u94A5\u767B\u5F55 CCR UI \u754C\u9762"
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
const updater$1 = {
|
|
452
|
+
checkingVersion: "\u6B63\u5728\u68C0\u67E5\u7248\u672C...",
|
|
453
|
+
checkingTools: "\u68C0\u67E5\u5DE5\u5177\u7248\u672C",
|
|
454
|
+
ccrNotInstalled: "CCR \u672A\u5B89\u88C5",
|
|
455
|
+
ccrUpToDate: "CCR \u5DF2\u662F\u6700\u65B0\u7248\u672C (v{version})",
|
|
456
|
+
claudeCodeNotInstalled: "Claude Code \u672A\u5B89\u88C5",
|
|
457
|
+
claudeCodeUpToDate: "Claude Code \u5DF2\u662F\u6700\u65B0\u7248\u672C (v{version})",
|
|
458
|
+
cannotCheckVersion: "\u65E0\u6CD5\u68C0\u67E5\u6700\u65B0\u7248\u672C",
|
|
459
|
+
currentVersion: "\u5F53\u524D\u7248\u672C: v{version}",
|
|
460
|
+
latestVersion: "\u6700\u65B0\u7248\u672C: v{version}",
|
|
461
|
+
confirmUpdate: "\u662F\u5426\u66F4\u65B0 {tool} \u5230\u6700\u65B0\u7248\u672C\uFF1F",
|
|
462
|
+
updateSkipped: "\u5DF2\u8DF3\u8FC7\u66F4\u65B0",
|
|
463
|
+
updating: "\u6B63\u5728\u66F4\u65B0 {tool}...",
|
|
464
|
+
updateSuccess: "{tool} \u66F4\u65B0\u6210\u529F\uFF01",
|
|
465
|
+
updateFailed: "{tool} \u66F4\u65B0\u5931\u8D25",
|
|
466
|
+
checkFailed: "\u7248\u672C\u68C0\u67E5\u5931\u8D25"
|
|
467
|
+
};
|
|
468
|
+
|
|
329
469
|
const zhCN = {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
470
|
+
common: common$1,
|
|
471
|
+
language: language$1,
|
|
472
|
+
installation: installation$1,
|
|
473
|
+
api: api$1,
|
|
474
|
+
configuration: configuration$1,
|
|
475
|
+
mcp: mcp$1,
|
|
476
|
+
menu: menu$1,
|
|
477
|
+
workflow: workflow$1,
|
|
478
|
+
cli: cli$1,
|
|
479
|
+
bmad: bmad$1,
|
|
480
|
+
errors: errors$1,
|
|
481
|
+
tools: tools$1,
|
|
482
|
+
ccr: ccrMessages$1,
|
|
483
|
+
updater: updater$1
|
|
342
484
|
};
|
|
343
485
|
|
|
344
486
|
const common = {
|
|
@@ -382,6 +524,7 @@ const installation = {
|
|
|
382
524
|
installPrompt: "Claude Code not found. Install automatically?",
|
|
383
525
|
installing: "Installing Claude Code...",
|
|
384
526
|
installSuccess: "Claude Code installed successfully",
|
|
527
|
+
alreadyInstalled: "Claude Code is already installed",
|
|
385
528
|
installFailed: "Failed to install Claude Code",
|
|
386
529
|
npmNotFound: "npm is not installed. Please install Node.js and npm first.",
|
|
387
530
|
// Termux specific
|
|
@@ -400,6 +543,8 @@ const api = {
|
|
|
400
543
|
authTokenDesc: "For tokens obtained via OAuth or browser login",
|
|
401
544
|
useApiKey: "Use API Key (Key authentication)",
|
|
402
545
|
apiKeyDesc: "For API keys from Anthropic Console",
|
|
546
|
+
useCcrProxy: "Use CCR Proxy",
|
|
547
|
+
ccrProxyDesc: "Use multiple AI models via Claude Code Router",
|
|
403
548
|
skipApi: "Skip (configure manually later)",
|
|
404
549
|
enterApiUrl: "Enter API URL",
|
|
405
550
|
enterAuthToken: "Enter Auth Token",
|
|
@@ -447,11 +592,21 @@ const configuration = {
|
|
|
447
592
|
// Model configuration
|
|
448
593
|
selectDefaultModel: "Select default model",
|
|
449
594
|
modelConfigSuccess: "Default model configured",
|
|
595
|
+
existingModelConfig: "Existing model configuration detected",
|
|
596
|
+
currentModel: "Current model",
|
|
597
|
+
modifyModel: "Modify model configuration?",
|
|
598
|
+
keepModel: "Keeping existing model configuration",
|
|
599
|
+
defaultModelOption: "Default (Let Claude Code choose)",
|
|
600
|
+
modelConfigured: "Default model configured",
|
|
450
601
|
// AI memory configuration
|
|
451
602
|
selectMemoryOption: "Select configuration option",
|
|
452
603
|
configureAiLanguage: "Configure AI output language",
|
|
453
604
|
configureAiPersonality: "Configure AI personality",
|
|
454
605
|
aiLanguageConfigured: "AI output language configured",
|
|
606
|
+
existingLanguageConfig: "Existing AI output language configuration detected",
|
|
607
|
+
currentLanguage: "Current language",
|
|
608
|
+
modifyLanguage: "Modify AI output language?",
|
|
609
|
+
keepLanguage: "Keeping existing language configuration",
|
|
455
610
|
// AI personality
|
|
456
611
|
selectAiPersonality: "Select AI personality",
|
|
457
612
|
customPersonalityHint: "Define your own personality",
|
|
@@ -477,7 +632,17 @@ const configuration = {
|
|
|
477
632
|
openSettingsJsonDesc: "Advanced user customization",
|
|
478
633
|
envImportSuccess: "Environment variables imported",
|
|
479
634
|
permissionsImportSuccess: "Permissions imported",
|
|
480
|
-
openingSettingsJson: "Opening settings.json..."
|
|
635
|
+
openingSettingsJson: "Opening settings.json...",
|
|
636
|
+
// JSON config related
|
|
637
|
+
invalidConfiguration: "Invalid configuration",
|
|
638
|
+
failedToParseJson: "Failed to parse JSON file:",
|
|
639
|
+
failedToBackupConfig: "Failed to backup config",
|
|
640
|
+
failedToReadTemplateSettings: "Failed to read template settings",
|
|
641
|
+
failedToMergeSettings: "Failed to merge settings",
|
|
642
|
+
preservingExistingSettings: "Preserving existing settings",
|
|
643
|
+
memoryDirNotFound: "Memory directory not found",
|
|
644
|
+
failedToSetOnboarding: "Failed to set onboarding flag",
|
|
645
|
+
fixWindowsMcp: "Fix Windows MCP configuration?"
|
|
481
646
|
};
|
|
482
647
|
|
|
483
648
|
const mcp = {
|
|
@@ -504,11 +669,14 @@ const menu = {
|
|
|
504
669
|
menuOptions: {
|
|
505
670
|
fullInit: "Full initialization",
|
|
506
671
|
importWorkflow: "Import workflow",
|
|
672
|
+
configureApiOrCcr: "Configure API / CCR proxy",
|
|
507
673
|
configureApi: "Configure API",
|
|
508
674
|
configureMcp: "Configure MCP",
|
|
509
675
|
configureModel: "Configure default model",
|
|
510
676
|
configureAiMemory: "Configure Claude global memory",
|
|
511
677
|
configureEnvPermission: "Import recommended environment variables and permissions",
|
|
678
|
+
configureCcr: "Configure Model Proxy (CCR)",
|
|
679
|
+
ccrManagement: "CCR",
|
|
512
680
|
ccusage: "ccusage",
|
|
513
681
|
installBmad: "Install BMad Method",
|
|
514
682
|
clearCache: "Clear preference cache",
|
|
@@ -516,13 +684,16 @@ const menu = {
|
|
|
516
684
|
exit: "Exit"
|
|
517
685
|
},
|
|
518
686
|
menuDescriptions: {
|
|
519
|
-
fullInit: "Install Claude Code + Import workflow + Configure API + Configure MCP services",
|
|
687
|
+
fullInit: "Install Claude Code + Import workflow + Configure API or CCR proxy + Configure MCP services",
|
|
520
688
|
importWorkflow: "Import/update workflow-related files only",
|
|
689
|
+
configureApiOrCcr: "Configure API URL, authentication or CCR proxy",
|
|
521
690
|
configureApi: "Configure API URL and authentication",
|
|
522
691
|
configureMcp: "Configure MCP services (includes Windows fix)",
|
|
523
692
|
configureModel: "Set default model (opus/sonnet)",
|
|
524
693
|
configureAiMemory: "Configure AI output language and personality",
|
|
525
694
|
configureEnvPermission: "Import privacy protection environment variables and system permissions",
|
|
695
|
+
configureCcr: "Configure Claude Code Router to use multiple AI models",
|
|
696
|
+
ccrManagement: "Configure Claude Code Router to use multiple AI models",
|
|
526
697
|
ccusage: "Claude Code usage analysis",
|
|
527
698
|
installBmad: "AI-driven development methodology framework",
|
|
528
699
|
clearCache: "Clear preference language and other caches",
|
|
@@ -656,25 +827,130 @@ const tools = {
|
|
|
656
827
|
errorDetails: "Error details:"
|
|
657
828
|
};
|
|
658
829
|
|
|
830
|
+
const ccrMessages = {
|
|
831
|
+
// Installation
|
|
832
|
+
installingCcr: "Installing Claude Code Router...",
|
|
833
|
+
ccrInstallSuccess: "Claude Code Router installed successfully",
|
|
834
|
+
ccrInstallFailed: "Failed to install Claude Code Router",
|
|
835
|
+
ccrAlreadyInstalled: "Claude Code Router is already installed",
|
|
836
|
+
// Configuration
|
|
837
|
+
configureCcr: "Configure Model Proxy (CCR)",
|
|
838
|
+
useCcrProxy: "Use CCR Proxy",
|
|
839
|
+
ccrProxyDesc: "Connect to multiple AI models via Claude Code Router",
|
|
840
|
+
fetchingPresets: "Fetching provider presets...",
|
|
841
|
+
noPresetsAvailable: "No presets available",
|
|
842
|
+
selectCcrPreset: "Select a provider preset:",
|
|
843
|
+
keyRequired: "API key is required",
|
|
844
|
+
// Existing config
|
|
845
|
+
existingCcrConfig: "Existing CCR configuration found",
|
|
846
|
+
overwriteCcrConfig: "Backup existing CCR configuration and reconfigure?",
|
|
847
|
+
keepingExistingConfig: "Keeping existing configuration",
|
|
848
|
+
backupCcrConfig: "Backing up existing CCR configuration...",
|
|
849
|
+
ccrBackupSuccess: "CCR configuration backed up to: {path}",
|
|
850
|
+
ccrBackupFailed: "Failed to backup CCR configuration",
|
|
851
|
+
// Model selection
|
|
852
|
+
selectDefaultModelForProvider: "Select default model for {provider}:",
|
|
853
|
+
enterApiKeyForProvider: "Enter API key for {provider}:",
|
|
854
|
+
// Skip option
|
|
855
|
+
skipOption: "Skip, configure in CCR manually",
|
|
856
|
+
skipConfiguring: "Skipping preset configuration, will create empty configuration framework",
|
|
857
|
+
// Success/Error messages
|
|
858
|
+
ccrConfigSuccess: "CCR configuration saved",
|
|
859
|
+
proxyConfigSuccess: "Proxy settings configured",
|
|
860
|
+
ccrConfigFailed: "Failed to configure CCR",
|
|
861
|
+
ccrSetupComplete: "CCR setup complete",
|
|
862
|
+
fetchPresetsError: "Failed to fetch provider presets",
|
|
863
|
+
failedToStartCcrService: "Failed to start CCR service",
|
|
864
|
+
errorStartingCcrService: "Error starting CCR service",
|
|
865
|
+
// CCR service status
|
|
866
|
+
ccrRestartSuccess: "CCR service restarted",
|
|
867
|
+
ccrRestartFailed: "Failed to restart CCR service",
|
|
868
|
+
// Configuration tips
|
|
869
|
+
configTips: "Configuration Tips",
|
|
870
|
+
useClaudeCommand: "Use the claude command to start Claude Code (not ccr code)",
|
|
871
|
+
advancedConfigTip: "You can use the ccr ui command for advanced configuration",
|
|
872
|
+
manualConfigTip: "After manually modifying the configuration file, run ccr restart to apply changes",
|
|
873
|
+
// CCR Menu
|
|
874
|
+
ccrMenuTitle: "CCR - Claude Code Router Management",
|
|
875
|
+
ccrMenuOptions: {
|
|
876
|
+
initCcr: "Initialize CCR",
|
|
877
|
+
startUi: "Start CCR UI",
|
|
878
|
+
checkStatus: "Check CCR Status",
|
|
879
|
+
restart: "Restart CCR",
|
|
880
|
+
start: "Start CCR",
|
|
881
|
+
stop: "Stop CCR",
|
|
882
|
+
back: "Back to Main Menu"
|
|
883
|
+
},
|
|
884
|
+
ccrMenuDescriptions: {
|
|
885
|
+
initCcr: "Install and configure CCR",
|
|
886
|
+
startUi: "Open web interface to manage CCR",
|
|
887
|
+
checkStatus: "View CCR service status",
|
|
888
|
+
restart: "Restart CCR service",
|
|
889
|
+
start: "Start CCR service",
|
|
890
|
+
stop: "Stop CCR service"
|
|
891
|
+
},
|
|
892
|
+
// Command execution messages
|
|
893
|
+
startingCcrUi: "Starting CCR UI...",
|
|
894
|
+
ccrUiStarted: "CCR UI started",
|
|
895
|
+
checkingCcrStatus: "Checking CCR status...",
|
|
896
|
+
ccrStatusTitle: "CCR Status:",
|
|
897
|
+
restartingCcr: "Restarting CCR...",
|
|
898
|
+
ccrRestarted: "CCR restarted",
|
|
899
|
+
startingCcr: "Starting CCR...",
|
|
900
|
+
ccrStarted: "CCR started",
|
|
901
|
+
stoppingCcr: "Stopping CCR...",
|
|
902
|
+
ccrStopped: "CCR stopped",
|
|
903
|
+
ccrCommandFailed: "Failed to execute CCR command",
|
|
904
|
+
// Configuration check messages
|
|
905
|
+
ccrNotConfigured: "CCR is not configured yet. Please initialize CCR first.",
|
|
906
|
+
pleaseInitFirst: "Please select option 1 to initialize CCR.",
|
|
907
|
+
// UI API Key messages
|
|
908
|
+
ccrUiApiKey: "CCR UI API Key",
|
|
909
|
+
ccrUiApiKeyHint: "Use this API key to login to CCR UI"
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
const updater = {
|
|
913
|
+
checkingVersion: "Checking version...",
|
|
914
|
+
checkingTools: "Checking tool versions",
|
|
915
|
+
ccrNotInstalled: "CCR is not installed",
|
|
916
|
+
ccrUpToDate: "CCR is up to date (v{version})",
|
|
917
|
+
claudeCodeNotInstalled: "Claude Code is not installed",
|
|
918
|
+
claudeCodeUpToDate: "Claude Code is up to date (v{version})",
|
|
919
|
+
cannotCheckVersion: "Cannot check latest version",
|
|
920
|
+
currentVersion: "Current version: v{version}",
|
|
921
|
+
latestVersion: "Latest version: v{version}",
|
|
922
|
+
confirmUpdate: "Update {tool} to the latest version?",
|
|
923
|
+
updateSkipped: "Update skipped",
|
|
924
|
+
updating: "Updating {tool}...",
|
|
925
|
+
updateSuccess: "{tool} updated successfully!",
|
|
926
|
+
updateFailed: "{tool} update failed",
|
|
927
|
+
checkFailed: "Version check failed"
|
|
928
|
+
};
|
|
929
|
+
|
|
659
930
|
const en = {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
931
|
+
common,
|
|
932
|
+
language,
|
|
933
|
+
installation,
|
|
934
|
+
api,
|
|
935
|
+
configuration,
|
|
936
|
+
mcp,
|
|
937
|
+
menu,
|
|
938
|
+
workflow,
|
|
939
|
+
cli,
|
|
940
|
+
bmad,
|
|
941
|
+
errors,
|
|
942
|
+
tools,
|
|
943
|
+
ccr: ccrMessages,
|
|
944
|
+
updater
|
|
672
945
|
};
|
|
673
946
|
|
|
674
|
-
const
|
|
947
|
+
const I18N$1 = {
|
|
675
948
|
"zh-CN": zhCN,
|
|
676
949
|
en
|
|
677
950
|
};
|
|
951
|
+
function getTranslation(lang) {
|
|
952
|
+
return I18N$1[lang];
|
|
953
|
+
}
|
|
678
954
|
|
|
679
955
|
const CLAUDE_DIR = join(homedir(), ".claude");
|
|
680
956
|
const SETTINGS_FILE = join(CLAUDE_DIR, "settings.json");
|
|
@@ -692,7 +968,7 @@ const AI_OUTPUT_LANGUAGES = {
|
|
|
692
968
|
en: { label: "English", directive: "Always respond in English" },
|
|
693
969
|
custom: { label: "Custom", directive: "" }
|
|
694
970
|
};
|
|
695
|
-
const I18N =
|
|
971
|
+
const I18N = I18N$1;
|
|
696
972
|
const MCP_SERVICES = [
|
|
697
973
|
{
|
|
698
974
|
id: "context7",
|
|
@@ -763,6 +1039,21 @@ const MCP_SERVICES = [
|
|
|
763
1039
|
}
|
|
764
1040
|
];
|
|
765
1041
|
|
|
1042
|
+
function addNumbersToChoices(choices, startFrom = 1, format = (n) => `${n}. `) {
|
|
1043
|
+
let currentNumber = startFrom;
|
|
1044
|
+
return choices.map((choice) => {
|
|
1045
|
+
if (choice.disabled) {
|
|
1046
|
+
return choice;
|
|
1047
|
+
}
|
|
1048
|
+
const numbered = {
|
|
1049
|
+
...choice,
|
|
1050
|
+
name: `${format(currentNumber)}${choice.name}`
|
|
1051
|
+
};
|
|
1052
|
+
currentNumber++;
|
|
1053
|
+
return numbered;
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
|
|
766
1057
|
class FileSystemError extends Error {
|
|
767
1058
|
constructor(message, path, cause) {
|
|
768
1059
|
super(message);
|
|
@@ -874,8 +1165,8 @@ function readJsonConfig(path, options = {}) {
|
|
|
874
1165
|
const content = readFile(path);
|
|
875
1166
|
const data = JSON.parse(content);
|
|
876
1167
|
if (validate && !validate(data)) {
|
|
877
|
-
const i18n =
|
|
878
|
-
console.warn(`${i18n.invalidConfiguration} (${path})`);
|
|
1168
|
+
const i18n = getTranslation(readZcfConfig()?.preferredLang || "en");
|
|
1169
|
+
console.warn(`${i18n.configuration.invalidConfiguration} (${path})`);
|
|
879
1170
|
return defaultValue;
|
|
880
1171
|
}
|
|
881
1172
|
if (sanitize) {
|
|
@@ -883,8 +1174,8 @@ function readJsonConfig(path, options = {}) {
|
|
|
883
1174
|
}
|
|
884
1175
|
return data;
|
|
885
1176
|
} catch (error) {
|
|
886
|
-
const i18n =
|
|
887
|
-
console.error(`${i18n.failedToParseJson} ${path}`, error);
|
|
1177
|
+
const i18n = getTranslation(readZcfConfig()?.preferredLang || "en");
|
|
1178
|
+
console.error(`${i18n.configuration.failedToParseJson} ${path}`, error);
|
|
888
1179
|
return defaultValue;
|
|
889
1180
|
}
|
|
890
1181
|
}
|
|
@@ -909,8 +1200,8 @@ function backupJsonConfig(path, backupDir) {
|
|
|
909
1200
|
copyFile(path, backupPath);
|
|
910
1201
|
return backupPath;
|
|
911
1202
|
} catch (error) {
|
|
912
|
-
const i18n =
|
|
913
|
-
console.error(i18n.failedToBackupConfig, error);
|
|
1203
|
+
const i18n = getTranslation(readZcfConfig()?.preferredLang || "en");
|
|
1204
|
+
console.error(i18n.configuration.failedToBackupConfig, error);
|
|
914
1205
|
return null;
|
|
915
1206
|
}
|
|
916
1207
|
}
|
|
@@ -990,23 +1281,23 @@ function getPersonalityInfo(personalityId) {
|
|
|
990
1281
|
return AI_PERSONALITIES.find((p) => p.id === personalityId);
|
|
991
1282
|
}
|
|
992
1283
|
async function configureAiPersonality(scriptLang, showExisting = true) {
|
|
993
|
-
const i18n =
|
|
1284
|
+
const i18n = getTranslation(scriptLang);
|
|
994
1285
|
const existingPersonality = getExistingPersonality();
|
|
995
1286
|
if (showExisting && existingPersonality) {
|
|
996
1287
|
const personalityInfo = getPersonalityInfo(existingPersonality);
|
|
997
1288
|
if (personalityInfo) {
|
|
998
|
-
console.log("\n" + ansis.blue(`\u2139 ${i18n.existingPersonality || "Existing AI personality configuration"}`));
|
|
1289
|
+
console.log("\n" + ansis.blue(`\u2139 ${i18n.configuration.existingPersonality || "Existing AI personality configuration"}`));
|
|
999
1290
|
console.log(
|
|
1000
|
-
ansis.gray(` ${i18n.currentPersonality || "Current personality"}: ${personalityInfo.name[scriptLang]}`)
|
|
1291
|
+
ansis.gray(` ${i18n.configuration.currentPersonality || "Current personality"}: ${personalityInfo.name[scriptLang]}`)
|
|
1001
1292
|
);
|
|
1002
1293
|
const { modify } = await inquirer.prompt({
|
|
1003
1294
|
type: "confirm",
|
|
1004
1295
|
name: "modify",
|
|
1005
|
-
message: i18n.modifyPersonality || "Modify AI personality?",
|
|
1296
|
+
message: i18n.configuration.modifyPersonality || "Modify AI personality?",
|
|
1006
1297
|
default: false
|
|
1007
1298
|
});
|
|
1008
1299
|
if (!modify) {
|
|
1009
|
-
console.log(ansis.green(`\u2714 ${i18n.keepPersonality || "Keeping existing personality"}`));
|
|
1300
|
+
console.log(ansis.green(`\u2714 ${i18n.configuration.keepPersonality || "Keeping existing personality"}`));
|
|
1010
1301
|
return;
|
|
1011
1302
|
}
|
|
1012
1303
|
}
|
|
@@ -1014,16 +1305,16 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
|
|
|
1014
1305
|
const { personality } = await inquirer.prompt({
|
|
1015
1306
|
type: "list",
|
|
1016
1307
|
name: "personality",
|
|
1017
|
-
message: i18n.selectAiPersonality || "Select AI personality",
|
|
1018
|
-
choices: AI_PERSONALITIES.map((p) => ({
|
|
1019
|
-
name: p.id !== "custom" ? `${p.name[scriptLang]} - ${ansis.gray(p.directive[scriptLang].substring(0, 50) + "...")}` : `${p.name[scriptLang]} - ${ansis.gray(i18n.customPersonalityHint || "Define your own personality")}`,
|
|
1308
|
+
message: i18n.configuration.selectAiPersonality || "Select AI personality",
|
|
1309
|
+
choices: addNumbersToChoices(AI_PERSONALITIES.map((p) => ({
|
|
1310
|
+
name: p.id !== "custom" ? `${p.name[scriptLang]} - ${ansis.gray(p.directive[scriptLang].substring(0, 50) + "...")}` : `${p.name[scriptLang]} - ${ansis.gray(i18n.configuration.customPersonalityHint || "Define your own personality")}`,
|
|
1020
1311
|
value: p.id,
|
|
1021
1312
|
short: p.name[scriptLang]
|
|
1022
|
-
})),
|
|
1313
|
+
}))),
|
|
1023
1314
|
default: existingPersonality ? AI_PERSONALITIES.findIndex((p) => p.id === existingPersonality) : 0
|
|
1024
1315
|
});
|
|
1025
1316
|
if (!personality) {
|
|
1026
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1317
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1027
1318
|
return;
|
|
1028
1319
|
}
|
|
1029
1320
|
let directive = "";
|
|
@@ -1031,11 +1322,11 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
|
|
|
1031
1322
|
const { customDirective } = await inquirer.prompt({
|
|
1032
1323
|
type: "input",
|
|
1033
1324
|
name: "customDirective",
|
|
1034
|
-
message: i18n.enterCustomPersonality || "Enter custom personality directive",
|
|
1035
|
-
validate: (value) => !!value || i18n.directiveCannotBeEmpty
|
|
1325
|
+
message: i18n.configuration.enterCustomPersonality || "Enter custom personality directive",
|
|
1326
|
+
validate: (value) => !!value || i18n.configuration.directiveCannotBeEmpty
|
|
1036
1327
|
});
|
|
1037
1328
|
if (!customDirective) {
|
|
1038
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1329
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1039
1330
|
return;
|
|
1040
1331
|
}
|
|
1041
1332
|
directive = customDirective;
|
|
@@ -1047,14 +1338,16 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
|
|
|
1047
1338
|
}
|
|
1048
1339
|
await applyPersonalityDirective(directive);
|
|
1049
1340
|
updateZcfConfig({ aiPersonality: personality });
|
|
1050
|
-
console.log(ansis.green(`\u2714 ${i18n.personalityConfigured || "AI personality configured"}`));
|
|
1341
|
+
console.log(ansis.green(`\u2714 ${i18n.configuration.personalityConfigured || "AI personality configured"}`));
|
|
1051
1342
|
}
|
|
1052
1343
|
async function applyPersonalityDirective(directive) {
|
|
1053
1344
|
try {
|
|
1054
1345
|
const personalityFile = join(CLAUDE_DIR, "personality.md");
|
|
1055
1346
|
writeFile(personalityFile, directive);
|
|
1056
1347
|
} catch (error) {
|
|
1057
|
-
|
|
1348
|
+
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1349
|
+
const errorI18n = getTranslation(lang);
|
|
1350
|
+
console.error(ansis.red(errorI18n.configuration.failedToApplyPersonality || "Failed to apply personality"), error);
|
|
1058
1351
|
}
|
|
1059
1352
|
}
|
|
1060
1353
|
|
|
@@ -1090,9 +1383,9 @@ function handleExitPromptError(error) {
|
|
|
1090
1383
|
if (error instanceof Error && error.name === "ExitPromptError") {
|
|
1091
1384
|
const zcfConfig = readZcfConfig();
|
|
1092
1385
|
const defaultLang = zcfConfig?.preferredLang || "zh-CN";
|
|
1093
|
-
const i18n =
|
|
1386
|
+
const i18n = getTranslation(defaultLang);
|
|
1094
1387
|
console.log(ansis.cyan(`
|
|
1095
|
-
${i18n.goodbye}
|
|
1388
|
+
${i18n.common.goodbye}
|
|
1096
1389
|
`));
|
|
1097
1390
|
process.exit(0);
|
|
1098
1391
|
}
|
|
@@ -1101,7 +1394,8 @@ ${i18n.goodbye}
|
|
|
1101
1394
|
function handleGeneralError(error, lang) {
|
|
1102
1395
|
const zcfConfig = readZcfConfig();
|
|
1103
1396
|
const defaultLang = lang || zcfConfig?.preferredLang || "en";
|
|
1104
|
-
const
|
|
1397
|
+
const i18n = getTranslation(defaultLang);
|
|
1398
|
+
const errorMsg = i18n.common.error || "Error";
|
|
1105
1399
|
console.error(ansis.red(`${errorMsg}:`), error);
|
|
1106
1400
|
if (error instanceof Error) {
|
|
1107
1401
|
console.error(ansis.gray(`Stack: ${error.stack}`));
|
|
@@ -1200,6 +1494,142 @@ function mergeAndCleanPermissions(templatePermissions, userPermissions) {
|
|
|
1200
1494
|
return cleanupPermissions(template, user);
|
|
1201
1495
|
}
|
|
1202
1496
|
|
|
1497
|
+
function getPlatform() {
|
|
1498
|
+
const p = platform();
|
|
1499
|
+
if (p === "win32") return "windows";
|
|
1500
|
+
if (p === "darwin") return "macos";
|
|
1501
|
+
return "linux";
|
|
1502
|
+
}
|
|
1503
|
+
function isTermux() {
|
|
1504
|
+
return !!(process.env.PREFIX && process.env.PREFIX.includes("com.termux")) || !!process.env.TERMUX_VERSION || existsSync("/data/data/com.termux/files/usr");
|
|
1505
|
+
}
|
|
1506
|
+
function getTermuxPrefix() {
|
|
1507
|
+
return process.env.PREFIX || "/data/data/com.termux/files/usr";
|
|
1508
|
+
}
|
|
1509
|
+
function isWindows() {
|
|
1510
|
+
return getPlatform() === "windows";
|
|
1511
|
+
}
|
|
1512
|
+
function getMcpCommand() {
|
|
1513
|
+
if (isWindows()) {
|
|
1514
|
+
return ["cmd", "/c", "npx"];
|
|
1515
|
+
}
|
|
1516
|
+
return ["npx"];
|
|
1517
|
+
}
|
|
1518
|
+
async function commandExists(command) {
|
|
1519
|
+
try {
|
|
1520
|
+
const cmd = getPlatform() === "windows" ? "where" : "which";
|
|
1521
|
+
const res = await exec(cmd, [command]);
|
|
1522
|
+
if (res.exitCode === 0) {
|
|
1523
|
+
return true;
|
|
1524
|
+
}
|
|
1525
|
+
} catch {
|
|
1526
|
+
}
|
|
1527
|
+
if (isTermux()) {
|
|
1528
|
+
const termuxPrefix = getTermuxPrefix();
|
|
1529
|
+
const possiblePaths = [
|
|
1530
|
+
`${termuxPrefix}/bin/${command}`,
|
|
1531
|
+
`${termuxPrefix}/usr/bin/${command}`,
|
|
1532
|
+
`/data/data/com.termux/files/usr/bin/${command}`
|
|
1533
|
+
];
|
|
1534
|
+
for (const path of possiblePaths) {
|
|
1535
|
+
if (existsSync(path)) {
|
|
1536
|
+
return true;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
if (getPlatform() !== "windows") {
|
|
1541
|
+
const commonPaths = [
|
|
1542
|
+
`/usr/local/bin/${command}`,
|
|
1543
|
+
`/usr/bin/${command}`,
|
|
1544
|
+
`/bin/${command}`,
|
|
1545
|
+
`${process.env.HOME}/.local/bin/${command}`
|
|
1546
|
+
];
|
|
1547
|
+
for (const path of commonPaths) {
|
|
1548
|
+
if (existsSync(path)) {
|
|
1549
|
+
return true;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
return false;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
function getMcpConfigPath() {
|
|
1557
|
+
return ClAUDE_CONFIG_FILE;
|
|
1558
|
+
}
|
|
1559
|
+
function readMcpConfig() {
|
|
1560
|
+
return readJsonConfig(ClAUDE_CONFIG_FILE);
|
|
1561
|
+
}
|
|
1562
|
+
function writeMcpConfig(config) {
|
|
1563
|
+
writeJsonConfig(ClAUDE_CONFIG_FILE, config);
|
|
1564
|
+
}
|
|
1565
|
+
function backupMcpConfig() {
|
|
1566
|
+
const backupBaseDir = join(CLAUDE_DIR, "backup");
|
|
1567
|
+
return backupJsonConfig(ClAUDE_CONFIG_FILE, backupBaseDir);
|
|
1568
|
+
}
|
|
1569
|
+
function mergeMcpServers(existing, newServers) {
|
|
1570
|
+
const config = existing || { mcpServers: {} };
|
|
1571
|
+
if (!config.mcpServers) {
|
|
1572
|
+
config.mcpServers = {};
|
|
1573
|
+
}
|
|
1574
|
+
Object.assign(config.mcpServers, newServers);
|
|
1575
|
+
return config;
|
|
1576
|
+
}
|
|
1577
|
+
function applyPlatformCommand(config) {
|
|
1578
|
+
if (config.command === "npx" && isWindows()) {
|
|
1579
|
+
const mcpCmd = getMcpCommand();
|
|
1580
|
+
config.command = mcpCmd[0];
|
|
1581
|
+
config.args = [...mcpCmd.slice(1), ...config.args || []];
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
function buildMcpServerConfig(baseConfig, apiKey, placeholder = "YOUR_EXA_API_KEY", envVarName) {
|
|
1585
|
+
const config = deepClone(baseConfig);
|
|
1586
|
+
applyPlatformCommand(config);
|
|
1587
|
+
if (!apiKey) {
|
|
1588
|
+
return config;
|
|
1589
|
+
}
|
|
1590
|
+
if (envVarName && config.env) {
|
|
1591
|
+
config.env[envVarName] = apiKey;
|
|
1592
|
+
return config;
|
|
1593
|
+
}
|
|
1594
|
+
if (config.args) {
|
|
1595
|
+
config.args = config.args.map((arg) => arg.replace(placeholder, apiKey));
|
|
1596
|
+
}
|
|
1597
|
+
if (config.url) {
|
|
1598
|
+
config.url = config.url.replace(placeholder, apiKey);
|
|
1599
|
+
}
|
|
1600
|
+
return config;
|
|
1601
|
+
}
|
|
1602
|
+
function fixWindowsMcpConfig(config) {
|
|
1603
|
+
if (!isWindows() || !config.mcpServers) {
|
|
1604
|
+
return config;
|
|
1605
|
+
}
|
|
1606
|
+
const fixed = { ...config };
|
|
1607
|
+
for (const [, serverConfig] of Object.entries(fixed.mcpServers)) {
|
|
1608
|
+
if (serverConfig && typeof serverConfig === "object" && "command" in serverConfig) {
|
|
1609
|
+
applyPlatformCommand(serverConfig);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
return fixed;
|
|
1613
|
+
}
|
|
1614
|
+
function addCompletedOnboarding() {
|
|
1615
|
+
try {
|
|
1616
|
+
let config = readMcpConfig();
|
|
1617
|
+
if (!config) {
|
|
1618
|
+
config = { mcpServers: {} };
|
|
1619
|
+
}
|
|
1620
|
+
if (config.hasCompletedOnboarding === true) {
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
config.hasCompletedOnboarding = true;
|
|
1624
|
+
writeMcpConfig(config);
|
|
1625
|
+
} catch (error) {
|
|
1626
|
+
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1627
|
+
const i18n = getTranslation(lang);
|
|
1628
|
+
console.error(i18n.configuration?.failedToAddOnboardingFlag || "Failed to add onboarding flag", error);
|
|
1629
|
+
throw error;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1203
1633
|
function ensureClaudeDir() {
|
|
1204
1634
|
ensureDir(CLAUDE_DIR);
|
|
1205
1635
|
}
|
|
@@ -1239,8 +1669,8 @@ function copyConfigFiles(lang, onlyMd = false) {
|
|
|
1239
1669
|
function copyClaudeMemoryFiles(lang, rootDir) {
|
|
1240
1670
|
const memorySourceDir = join(rootDir, "templates", lang, "memory");
|
|
1241
1671
|
if (!exists(memorySourceDir)) {
|
|
1242
|
-
const i18n =
|
|
1243
|
-
throw new Error(`${i18n.memoryDirNotFound || "Memory directory not found:"} ${memorySourceDir}`);
|
|
1672
|
+
const i18n = getTranslation(lang);
|
|
1673
|
+
throw new Error(`${i18n.configuration.memoryDirNotFound || "Memory directory not found:"} ${memorySourceDir}`);
|
|
1244
1674
|
}
|
|
1245
1675
|
const files = readDir(memorySourceDir);
|
|
1246
1676
|
files?.forEach((file) => {
|
|
@@ -1259,7 +1689,9 @@ function getDefaultSettings() {
|
|
|
1259
1689
|
const templateSettingsPath = join(rootDir, "templates", "settings.json");
|
|
1260
1690
|
return readJsonConfig(templateSettingsPath) || {};
|
|
1261
1691
|
} catch (error) {
|
|
1262
|
-
|
|
1692
|
+
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1693
|
+
const i18n = getTranslation(lang);
|
|
1694
|
+
console.error(i18n.configuration.failedToReadTemplateSettings || "Failed to read template settings", error);
|
|
1263
1695
|
return {};
|
|
1264
1696
|
}
|
|
1265
1697
|
}
|
|
@@ -1284,6 +1716,13 @@ function configureApi(apiConfig) {
|
|
|
1284
1716
|
settings.env.ANTHROPIC_BASE_URL = apiConfig.url;
|
|
1285
1717
|
}
|
|
1286
1718
|
writeJsonConfig(SETTINGS_FILE, settings);
|
|
1719
|
+
try {
|
|
1720
|
+
addCompletedOnboarding();
|
|
1721
|
+
} catch (error) {
|
|
1722
|
+
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1723
|
+
const i18n = getTranslation(lang);
|
|
1724
|
+
console.error(i18n.configuration.failedToSetOnboarding || "Failed to set onboarding flag", error);
|
|
1725
|
+
}
|
|
1287
1726
|
return apiConfig;
|
|
1288
1727
|
}
|
|
1289
1728
|
function mergeConfigs(sourceFile, targetFile) {
|
|
@@ -1299,14 +1738,20 @@ function updateDefaultModel(model) {
|
|
|
1299
1738
|
if (existingSettings) {
|
|
1300
1739
|
settings = existingSettings;
|
|
1301
1740
|
}
|
|
1302
|
-
|
|
1741
|
+
if (model === "default") {
|
|
1742
|
+
delete settings.model;
|
|
1743
|
+
} else {
|
|
1744
|
+
settings.model = model;
|
|
1745
|
+
}
|
|
1303
1746
|
writeJsonConfig(SETTINGS_FILE, settings);
|
|
1304
1747
|
}
|
|
1305
1748
|
function mergeSettingsFile(templatePath, targetPath) {
|
|
1306
1749
|
try {
|
|
1307
1750
|
const templateSettings = readJsonConfig(templatePath);
|
|
1308
1751
|
if (!templateSettings) {
|
|
1309
|
-
|
|
1752
|
+
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1753
|
+
const i18n = getTranslation(lang);
|
|
1754
|
+
console.error(i18n.configuration?.failedToReadTemplateSettings || "Failed to read template settings");
|
|
1310
1755
|
return;
|
|
1311
1756
|
}
|
|
1312
1757
|
if (!exists(targetPath)) {
|
|
@@ -1333,14 +1778,28 @@ function mergeSettingsFile(templatePath, targetPath) {
|
|
|
1333
1778
|
}
|
|
1334
1779
|
writeJsonConfig(targetPath, mergedSettings);
|
|
1335
1780
|
} catch (error) {
|
|
1336
|
-
|
|
1781
|
+
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1782
|
+
const i18n = getTranslation(lang);
|
|
1783
|
+
console.error(i18n.configuration.failedToMergeSettings || "Failed to merge settings", error);
|
|
1337
1784
|
if (exists(targetPath)) {
|
|
1338
|
-
|
|
1785
|
+
const lang2 = readZcfConfig()?.preferredLang || "en";
|
|
1786
|
+
const i18n2 = getTranslation(lang2);
|
|
1787
|
+
console.warn(i18n2.configuration.preservingExistingSettings || "Preserving existing settings");
|
|
1339
1788
|
} else {
|
|
1340
1789
|
copyFile(templatePath, targetPath);
|
|
1341
1790
|
}
|
|
1342
1791
|
}
|
|
1343
1792
|
}
|
|
1793
|
+
function getExistingModelConfig() {
|
|
1794
|
+
const settings = readJsonConfig(SETTINGS_FILE);
|
|
1795
|
+
if (!settings) {
|
|
1796
|
+
return null;
|
|
1797
|
+
}
|
|
1798
|
+
if (!settings.model) {
|
|
1799
|
+
return "default";
|
|
1800
|
+
}
|
|
1801
|
+
return settings.model;
|
|
1802
|
+
}
|
|
1344
1803
|
function getExistingApiConfig() {
|
|
1345
1804
|
const settings = readJsonConfig(SETTINGS_FILE);
|
|
1346
1805
|
if (!settings || !settings.env) {
|
|
@@ -1379,11 +1838,11 @@ function applyAiLanguageDirective(aiOutputLang) {
|
|
|
1379
1838
|
}
|
|
1380
1839
|
|
|
1381
1840
|
function validateApiKey(apiKey, lang = "zh-CN") {
|
|
1382
|
-
const i18n =
|
|
1841
|
+
const i18n = getTranslation(lang);
|
|
1383
1842
|
if (!apiKey || apiKey.trim() === "") {
|
|
1384
1843
|
return {
|
|
1385
1844
|
isValid: false,
|
|
1386
|
-
error: i18n.apiKeyValidation.empty
|
|
1845
|
+
error: i18n.api.apiKeyValidation.empty
|
|
1387
1846
|
};
|
|
1388
1847
|
}
|
|
1389
1848
|
return { isValid: true };
|
|
@@ -1401,22 +1860,22 @@ async function configureApiCompletely(i18n, scriptLang, preselectedAuthType) {
|
|
|
1401
1860
|
const { authType: selectedAuthType } = await inquirer.prompt({
|
|
1402
1861
|
type: "list",
|
|
1403
1862
|
name: "authType",
|
|
1404
|
-
message: i18n.configureApi,
|
|
1405
|
-
choices: [
|
|
1863
|
+
message: i18n.api.configureApi,
|
|
1864
|
+
choices: addNumbersToChoices([
|
|
1406
1865
|
{
|
|
1407
|
-
name: `${i18n.useAuthToken} - ${ansis.gray(i18n.authTokenDesc)}`,
|
|
1866
|
+
name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
|
|
1408
1867
|
value: "auth_token",
|
|
1409
|
-
short: i18n.useAuthToken
|
|
1868
|
+
short: i18n.api.useAuthToken
|
|
1410
1869
|
},
|
|
1411
1870
|
{
|
|
1412
|
-
name: `${i18n.useApiKey} - ${ansis.gray(i18n.apiKeyDesc)}`,
|
|
1871
|
+
name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
|
|
1413
1872
|
value: "api_key",
|
|
1414
|
-
short: i18n.useApiKey
|
|
1873
|
+
short: i18n.api.useApiKey
|
|
1415
1874
|
}
|
|
1416
|
-
]
|
|
1875
|
+
])
|
|
1417
1876
|
});
|
|
1418
1877
|
if (!selectedAuthType) {
|
|
1419
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1878
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1420
1879
|
return null;
|
|
1421
1880
|
}
|
|
1422
1881
|
authType = selectedAuthType;
|
|
@@ -1424,39 +1883,39 @@ async function configureApiCompletely(i18n, scriptLang, preselectedAuthType) {
|
|
|
1424
1883
|
const { url } = await inquirer.prompt({
|
|
1425
1884
|
type: "input",
|
|
1426
1885
|
name: "url",
|
|
1427
|
-
message: i18n.enterApiUrl,
|
|
1886
|
+
message: i18n.api.enterApiUrl,
|
|
1428
1887
|
validate: (value) => {
|
|
1429
|
-
if (!value) return i18n.urlRequired;
|
|
1888
|
+
if (!value) return i18n.api.urlRequired;
|
|
1430
1889
|
try {
|
|
1431
1890
|
new URL(value);
|
|
1432
1891
|
return true;
|
|
1433
1892
|
} catch {
|
|
1434
|
-
return i18n.invalidUrl;
|
|
1893
|
+
return i18n.api.invalidUrl;
|
|
1435
1894
|
}
|
|
1436
1895
|
}
|
|
1437
1896
|
});
|
|
1438
1897
|
if (url === void 0) {
|
|
1439
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1898
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1440
1899
|
return null;
|
|
1441
1900
|
}
|
|
1442
|
-
const keyMessage = authType === "auth_token" ? i18n.enterAuthToken : i18n.enterApiKey;
|
|
1901
|
+
const keyMessage = authType === "auth_token" ? i18n.api.enterAuthToken : i18n.api.enterApiKey;
|
|
1443
1902
|
const { key } = await inquirer.prompt({
|
|
1444
1903
|
type: "input",
|
|
1445
1904
|
name: "key",
|
|
1446
1905
|
message: keyMessage,
|
|
1447
1906
|
validate: (value) => {
|
|
1448
1907
|
if (!value) {
|
|
1449
|
-
return i18n.keyRequired;
|
|
1908
|
+
return i18n.api.keyRequired;
|
|
1450
1909
|
}
|
|
1451
1910
|
const validation = validateApiKey(value, scriptLang);
|
|
1452
1911
|
if (!validation.isValid) {
|
|
1453
|
-
return validation.error || i18n.invalidKeyFormat;
|
|
1912
|
+
return validation.error || i18n.api.invalidKeyFormat;
|
|
1454
1913
|
}
|
|
1455
1914
|
return true;
|
|
1456
1915
|
}
|
|
1457
1916
|
});
|
|
1458
1917
|
if (key === void 0) {
|
|
1459
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1918
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1460
1919
|
return null;
|
|
1461
1920
|
}
|
|
1462
1921
|
console.log(ansis.gray(` API Key: ${formatApiKeyDisplay(key)}`));
|
|
@@ -1471,276 +1930,356 @@ async function modifyApiConfigPartially(existingConfig, i18n, scriptLang) {
|
|
|
1471
1930
|
const { item } = await inquirer.prompt({
|
|
1472
1931
|
type: "list",
|
|
1473
1932
|
name: "item",
|
|
1474
|
-
message: i18n.selectModifyItems,
|
|
1475
|
-
choices: [
|
|
1476
|
-
{ name: i18n.modifyApiUrl, value: "url" },
|
|
1477
|
-
{ name: i18n.modifyApiKey, value: "key" },
|
|
1478
|
-
{ name: i18n.modifyAuthType, value: "authType" }
|
|
1479
|
-
]
|
|
1933
|
+
message: i18n.api.selectModifyItems,
|
|
1934
|
+
choices: addNumbersToChoices([
|
|
1935
|
+
{ name: i18n.api.modifyApiUrl, value: "url" },
|
|
1936
|
+
{ name: i18n.api.modifyApiKey, value: "key" },
|
|
1937
|
+
{ name: i18n.api.modifyAuthType, value: "authType" }
|
|
1938
|
+
])
|
|
1480
1939
|
});
|
|
1481
1940
|
if (!item) {
|
|
1482
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1941
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1483
1942
|
return;
|
|
1484
1943
|
}
|
|
1485
1944
|
if (item === "url") {
|
|
1486
1945
|
const { url } = await inquirer.prompt({
|
|
1487
1946
|
type: "input",
|
|
1488
1947
|
name: "url",
|
|
1489
|
-
message: i18n.enterNewApiUrl.replace("{url}", currentConfig.url || i18n.none),
|
|
1948
|
+
message: i18n.api.enterNewApiUrl.replace("{url}", currentConfig.url || i18n.common.none),
|
|
1490
1949
|
default: currentConfig.url,
|
|
1491
1950
|
validate: (value) => {
|
|
1492
|
-
if (!value) return i18n.urlRequired;
|
|
1951
|
+
if (!value) return i18n.api.urlRequired;
|
|
1493
1952
|
try {
|
|
1494
1953
|
new URL(value);
|
|
1495
1954
|
return true;
|
|
1496
1955
|
} catch {
|
|
1497
|
-
return i18n.invalidUrl;
|
|
1956
|
+
return i18n.api.invalidUrl;
|
|
1498
1957
|
}
|
|
1499
1958
|
}
|
|
1500
1959
|
});
|
|
1501
1960
|
if (url === void 0) {
|
|
1502
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1961
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1503
1962
|
return;
|
|
1504
1963
|
}
|
|
1505
1964
|
currentConfig.url = url;
|
|
1506
1965
|
const savedConfig = configureApi(currentConfig);
|
|
1507
1966
|
if (savedConfig) {
|
|
1508
|
-
console.log(ansis.green(`\u2714 ${i18n.modificationSaved}`));
|
|
1509
|
-
console.log(ansis.gray(` ${i18n.apiConfigUrl}: ${savedConfig.url}`));
|
|
1967
|
+
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
1968
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${savedConfig.url}`));
|
|
1510
1969
|
}
|
|
1511
1970
|
} else if (item === "key") {
|
|
1512
1971
|
const authType = currentConfig.authType || "auth_token";
|
|
1513
|
-
const keyMessage = authType === "auth_token" ? i18n.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.none) : i18n.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.none);
|
|
1972
|
+
const keyMessage = authType === "auth_token" ? i18n.api.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.common.none) : i18n.api.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.common.none);
|
|
1514
1973
|
const { key } = await inquirer.prompt({
|
|
1515
1974
|
type: "input",
|
|
1516
1975
|
name: "key",
|
|
1517
1976
|
message: keyMessage,
|
|
1518
1977
|
validate: (value) => {
|
|
1519
1978
|
if (!value) {
|
|
1520
|
-
return i18n.keyRequired;
|
|
1979
|
+
return i18n.api.keyRequired;
|
|
1521
1980
|
}
|
|
1522
1981
|
const validation = validateApiKey(value, scriptLang);
|
|
1523
1982
|
if (!validation.isValid) {
|
|
1524
|
-
return validation.error || i18n.invalidKeyFormat;
|
|
1983
|
+
return validation.error || i18n.api.invalidKeyFormat;
|
|
1525
1984
|
}
|
|
1526
1985
|
return true;
|
|
1527
1986
|
}
|
|
1528
1987
|
});
|
|
1529
1988
|
if (key === void 0) {
|
|
1530
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1989
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1531
1990
|
return;
|
|
1532
1991
|
}
|
|
1533
1992
|
currentConfig.key = key;
|
|
1534
1993
|
const savedConfig = configureApi(currentConfig);
|
|
1535
1994
|
if (savedConfig) {
|
|
1536
|
-
console.log(ansis.green(`\u2714 ${i18n.modificationSaved}`));
|
|
1537
|
-
console.log(ansis.gray(` ${i18n.apiConfigKey}: ${formatApiKeyDisplay(savedConfig.key)}`));
|
|
1995
|
+
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
1996
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigKey}: ${formatApiKeyDisplay(savedConfig.key)}`));
|
|
1538
1997
|
}
|
|
1539
1998
|
} else if (item === "authType") {
|
|
1540
1999
|
const { authType } = await inquirer.prompt({
|
|
1541
2000
|
type: "list",
|
|
1542
2001
|
name: "authType",
|
|
1543
|
-
message: i18n.selectNewAuthType.replace("{type}", currentConfig.authType || i18n.none),
|
|
1544
|
-
choices: [
|
|
2002
|
+
message: i18n.api.selectNewAuthType.replace("{type}", currentConfig.authType || i18n.common.none),
|
|
2003
|
+
choices: addNumbersToChoices([
|
|
1545
2004
|
{ name: "Auth Token (OAuth)", value: "auth_token" },
|
|
1546
2005
|
{ name: "API Key", value: "api_key" }
|
|
1547
|
-
],
|
|
2006
|
+
]),
|
|
1548
2007
|
default: currentConfig.authType === "api_key" ? 1 : 0
|
|
1549
2008
|
});
|
|
1550
2009
|
if (authType === void 0) {
|
|
1551
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2010
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1552
2011
|
return;
|
|
1553
2012
|
}
|
|
1554
2013
|
currentConfig.authType = authType;
|
|
1555
2014
|
const savedConfig = configureApi(currentConfig);
|
|
1556
2015
|
if (savedConfig) {
|
|
1557
|
-
console.log(ansis.green(`\u2714 ${i18n.modificationSaved}`));
|
|
1558
|
-
console.log(ansis.gray(` ${i18n.apiConfigAuthType}: ${savedConfig.authType}`));
|
|
2016
|
+
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
2017
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigAuthType}: ${savedConfig.authType}`));
|
|
1559
2018
|
}
|
|
1560
2019
|
}
|
|
1561
2020
|
}
|
|
1562
2021
|
async function updatePromptOnly(configLang, scriptLang, aiOutputLang) {
|
|
1563
|
-
const i18n =
|
|
2022
|
+
const i18n = getTranslation(scriptLang);
|
|
1564
2023
|
const backupDir = backupExistingConfig();
|
|
1565
2024
|
if (backupDir) {
|
|
1566
|
-
console.log(ansis.gray(`\u2714 ${i18n.backupSuccess}: ${backupDir}`));
|
|
2025
|
+
console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
|
|
1567
2026
|
}
|
|
1568
2027
|
copyConfigFiles(configLang, true);
|
|
1569
2028
|
if (aiOutputLang) {
|
|
1570
2029
|
applyAiLanguageDirective(aiOutputLang);
|
|
1571
2030
|
}
|
|
1572
2031
|
await configureAiPersonality(scriptLang);
|
|
1573
|
-
console.log(ansis.green(`\u2714 ${i18n.configSuccess} ${CLAUDE_DIR}`));
|
|
1574
|
-
console.log("\n" + ansis.cyan(i18n.complete));
|
|
2032
|
+
console.log(ansis.green(`\u2714 ${i18n.configuration.configSuccess} ${CLAUDE_DIR}`));
|
|
2033
|
+
console.log("\n" + ansis.cyan(i18n.common.complete));
|
|
1575
2034
|
}
|
|
1576
2035
|
|
|
1577
|
-
function
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
return "linux";
|
|
2036
|
+
function format(template, replacements) {
|
|
2037
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
2038
|
+
return replacements[key] || match;
|
|
2039
|
+
});
|
|
1582
2040
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
2041
|
+
|
|
2042
|
+
const execAsync$3 = promisify(exec$1);
|
|
2043
|
+
const CACHE_DIR = join$1(homedir$1(), ".claude", "cache");
|
|
2044
|
+
const CACHE_FILE = join$1(CACHE_DIR, "version-cache.json");
|
|
2045
|
+
const CACHE_DURATION = 24 * 60 * 60 * 1e3;
|
|
2046
|
+
async function ensureCacheDir() {
|
|
2047
|
+
try {
|
|
2048
|
+
await mkdir(CACHE_DIR, { recursive: true });
|
|
2049
|
+
} catch (error) {
|
|
2050
|
+
}
|
|
1585
2051
|
}
|
|
1586
|
-
function
|
|
1587
|
-
|
|
2052
|
+
async function readCache() {
|
|
2053
|
+
try {
|
|
2054
|
+
const content = await readFile$1(CACHE_FILE, "utf-8");
|
|
2055
|
+
return JSON.parse(content);
|
|
2056
|
+
} catch {
|
|
2057
|
+
return {};
|
|
2058
|
+
}
|
|
1588
2059
|
}
|
|
1589
|
-
function
|
|
1590
|
-
|
|
2060
|
+
async function writeCache(cache) {
|
|
2061
|
+
await ensureCacheDir();
|
|
2062
|
+
await writeFile$1(CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
1591
2063
|
}
|
|
1592
|
-
function
|
|
1593
|
-
|
|
1594
|
-
|
|
2064
|
+
async function getInstalledVersion(command) {
|
|
2065
|
+
try {
|
|
2066
|
+
let stdout;
|
|
2067
|
+
try {
|
|
2068
|
+
const result = await execAsync$3(`${command} -v`);
|
|
2069
|
+
stdout = result.stdout;
|
|
2070
|
+
} catch {
|
|
2071
|
+
const result = await execAsync$3(`${command} --version`);
|
|
2072
|
+
stdout = result.stdout;
|
|
2073
|
+
}
|
|
2074
|
+
const versionMatch = stdout.match(/(\d+\.\d+\.\d+(?:-[\w.]+)?)/);
|
|
2075
|
+
return versionMatch ? versionMatch[1] : null;
|
|
2076
|
+
} catch {
|
|
2077
|
+
return null;
|
|
1595
2078
|
}
|
|
1596
|
-
return ["npx"];
|
|
1597
2079
|
}
|
|
1598
|
-
async function
|
|
1599
|
-
|
|
1600
|
-
const
|
|
1601
|
-
const
|
|
1602
|
-
if (
|
|
1603
|
-
return
|
|
2080
|
+
async function getLatestVersion(packageName, useCache = true) {
|
|
2081
|
+
if (useCache) {
|
|
2082
|
+
const cache = await readCache();
|
|
2083
|
+
const cached = cache[packageName];
|
|
2084
|
+
if (cached && Date.now() - cached.checkedAt < CACHE_DURATION) {
|
|
2085
|
+
return cached.version;
|
|
1604
2086
|
}
|
|
2087
|
+
}
|
|
2088
|
+
try {
|
|
2089
|
+
const { stdout } = await execAsync$3(`npm view ${packageName} version`);
|
|
2090
|
+
const version = stdout.trim();
|
|
2091
|
+
const cache = await readCache();
|
|
2092
|
+
cache[packageName] = {
|
|
2093
|
+
version,
|
|
2094
|
+
checkedAt: Date.now()
|
|
2095
|
+
};
|
|
2096
|
+
await writeCache(cache);
|
|
2097
|
+
return version;
|
|
1605
2098
|
} catch {
|
|
2099
|
+
return null;
|
|
1606
2100
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
2101
|
+
}
|
|
2102
|
+
function compareVersions(current, latest) {
|
|
2103
|
+
if (!semver.valid(current) || !semver.valid(latest)) {
|
|
2104
|
+
return -1;
|
|
2105
|
+
}
|
|
2106
|
+
return semver.compare(current, latest);
|
|
2107
|
+
}
|
|
2108
|
+
function shouldUpdate(current, latest) {
|
|
2109
|
+
return compareVersions(current, latest) < 0;
|
|
2110
|
+
}
|
|
2111
|
+
async function checkCcrVersion() {
|
|
2112
|
+
const currentVersion = await getInstalledVersion("ccr");
|
|
2113
|
+
let latestVersion = await getLatestVersion("@musistudio/claude-code-router");
|
|
2114
|
+
if (!latestVersion) {
|
|
2115
|
+
latestVersion = await getLatestVersion("claude-code-router");
|
|
2116
|
+
}
|
|
2117
|
+
return {
|
|
2118
|
+
installed: currentVersion !== null,
|
|
2119
|
+
currentVersion,
|
|
2120
|
+
latestVersion,
|
|
2121
|
+
needsUpdate: currentVersion && latestVersion ? shouldUpdate(currentVersion, latestVersion) : false
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
async function checkClaudeCodeVersion() {
|
|
2125
|
+
const currentVersion = await getInstalledVersion("claude");
|
|
2126
|
+
const latestVersion = await getLatestVersion("@anthropic-ai/claude-code");
|
|
2127
|
+
return {
|
|
2128
|
+
installed: currentVersion !== null,
|
|
2129
|
+
currentVersion,
|
|
2130
|
+
latestVersion,
|
|
2131
|
+
needsUpdate: currentVersion && latestVersion ? shouldUpdate(currentVersion, latestVersion) : false
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
const execAsync$2 = promisify(exec$1);
|
|
2136
|
+
async function updateCcr(scriptLang, force = false) {
|
|
2137
|
+
const i18n = getTranslation(scriptLang);
|
|
2138
|
+
const spinner = ora(i18n.updater.checkingVersion).start();
|
|
2139
|
+
try {
|
|
2140
|
+
const { installed, currentVersion, latestVersion, needsUpdate } = await checkCcrVersion();
|
|
2141
|
+
spinner.stop();
|
|
2142
|
+
if (!installed) {
|
|
2143
|
+
console.log(ansis.yellow(i18n.updater.ccrNotInstalled));
|
|
2144
|
+
return false;
|
|
2145
|
+
}
|
|
2146
|
+
if (!needsUpdate && !force) {
|
|
2147
|
+
console.log(ansis.green(format(i18n.updater.ccrUpToDate, { version: currentVersion || "" })));
|
|
2148
|
+
return true;
|
|
2149
|
+
}
|
|
2150
|
+
if (!latestVersion) {
|
|
2151
|
+
console.log(ansis.yellow(i18n.updater.cannotCheckVersion));
|
|
2152
|
+
return false;
|
|
2153
|
+
}
|
|
2154
|
+
console.log(ansis.cyan(format(i18n.updater.currentVersion, { version: currentVersion || "" })));
|
|
2155
|
+
console.log(ansis.cyan(format(i18n.updater.latestVersion, { version: latestVersion })));
|
|
2156
|
+
const { confirm } = await prompts$1({
|
|
2157
|
+
type: "confirm",
|
|
2158
|
+
name: "confirm",
|
|
2159
|
+
message: format(i18n.updater.confirmUpdate, { tool: "CCR" }),
|
|
2160
|
+
initial: true
|
|
2161
|
+
});
|
|
2162
|
+
if (!confirm) {
|
|
2163
|
+
console.log(ansis.gray(i18n.updater.updateSkipped));
|
|
2164
|
+
return true;
|
|
2165
|
+
}
|
|
2166
|
+
const updateSpinner = ora(format(i18n.updater.updating, { tool: "CCR" })).start();
|
|
2167
|
+
try {
|
|
2168
|
+
try {
|
|
2169
|
+
await execAsync$2("npm update -g @musistudio/claude-code-router");
|
|
2170
|
+
} catch {
|
|
2171
|
+
await execAsync$2("npm update -g claude-code-router");
|
|
1617
2172
|
}
|
|
2173
|
+
updateSpinner.succeed(format(i18n.updater.updateSuccess, { tool: "CCR" }));
|
|
2174
|
+
return true;
|
|
2175
|
+
} catch (error) {
|
|
2176
|
+
updateSpinner.fail(format(i18n.updater.updateFailed, { tool: "CCR" }));
|
|
2177
|
+
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2178
|
+
return false;
|
|
1618
2179
|
}
|
|
2180
|
+
} catch (error) {
|
|
2181
|
+
spinner.fail(i18n.updater.checkFailed);
|
|
2182
|
+
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2183
|
+
return false;
|
|
1619
2184
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
2185
|
+
}
|
|
2186
|
+
async function updateClaudeCode(scriptLang, force = false) {
|
|
2187
|
+
const i18n = getTranslation(scriptLang);
|
|
2188
|
+
const spinner = ora(i18n.updater.checkingVersion).start();
|
|
2189
|
+
try {
|
|
2190
|
+
const { installed, currentVersion, latestVersion, needsUpdate } = await checkClaudeCodeVersion();
|
|
2191
|
+
spinner.stop();
|
|
2192
|
+
if (!installed) {
|
|
2193
|
+
console.log(ansis.yellow(i18n.updater.claudeCodeNotInstalled));
|
|
2194
|
+
return false;
|
|
2195
|
+
}
|
|
2196
|
+
if (!needsUpdate && !force) {
|
|
2197
|
+
console.log(ansis.green(format(i18n.updater.claudeCodeUpToDate, { version: currentVersion || "" })));
|
|
2198
|
+
return true;
|
|
2199
|
+
}
|
|
2200
|
+
if (!latestVersion) {
|
|
2201
|
+
console.log(ansis.yellow(i18n.updater.cannotCheckVersion));
|
|
2202
|
+
return false;
|
|
2203
|
+
}
|
|
2204
|
+
console.log(ansis.cyan(format(i18n.updater.currentVersion, { version: currentVersion || "" })));
|
|
2205
|
+
console.log(ansis.cyan(format(i18n.updater.latestVersion, { version: latestVersion })));
|
|
2206
|
+
const { confirm } = await prompts$1({
|
|
2207
|
+
type: "confirm",
|
|
2208
|
+
name: "confirm",
|
|
2209
|
+
message: format(i18n.updater.confirmUpdate, { tool: "Claude Code" }),
|
|
2210
|
+
initial: true
|
|
2211
|
+
});
|
|
2212
|
+
if (!confirm) {
|
|
2213
|
+
console.log(ansis.gray(i18n.updater.updateSkipped));
|
|
2214
|
+
return true;
|
|
1631
2215
|
}
|
|
2216
|
+
const updateSpinner = ora(format(i18n.updater.updating, { tool: "Claude Code" })).start();
|
|
2217
|
+
try {
|
|
2218
|
+
await execAsync$2("npm update -g @anthropic-ai/claude-code");
|
|
2219
|
+
updateSpinner.succeed(format(i18n.updater.updateSuccess, { tool: "Claude Code" }));
|
|
2220
|
+
return true;
|
|
2221
|
+
} catch (error) {
|
|
2222
|
+
updateSpinner.fail(format(i18n.updater.updateFailed, { tool: "Claude Code" }));
|
|
2223
|
+
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2224
|
+
return false;
|
|
2225
|
+
}
|
|
2226
|
+
} catch (error) {
|
|
2227
|
+
spinner.fail(i18n.updater.checkFailed);
|
|
2228
|
+
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2229
|
+
return false;
|
|
1632
2230
|
}
|
|
1633
|
-
|
|
2231
|
+
}
|
|
2232
|
+
async function checkAndUpdateTools(scriptLang) {
|
|
2233
|
+
const i18n = getTranslation(scriptLang);
|
|
2234
|
+
console.log(ansis.bold.cyan(`
|
|
2235
|
+
\u{1F50D} ${i18n.updater.checkingTools}
|
|
2236
|
+
`));
|
|
2237
|
+
await updateCcr(scriptLang);
|
|
2238
|
+
console.log();
|
|
2239
|
+
await updateClaudeCode(scriptLang);
|
|
1634
2240
|
}
|
|
1635
2241
|
|
|
1636
2242
|
async function isClaudeCodeInstalled() {
|
|
1637
2243
|
return await commandExists("claude");
|
|
1638
2244
|
}
|
|
1639
2245
|
async function installClaudeCode(lang) {
|
|
1640
|
-
const i18n =
|
|
2246
|
+
const i18n = getTranslation(lang);
|
|
2247
|
+
const installed = await isClaudeCodeInstalled();
|
|
2248
|
+
if (installed) {
|
|
2249
|
+
console.log(ansis.green(`\u2714 ${i18n.installation.alreadyInstalled}`));
|
|
2250
|
+
await updateClaudeCode(lang);
|
|
2251
|
+
return;
|
|
2252
|
+
}
|
|
1641
2253
|
if (isTermux()) {
|
|
1642
|
-
console.log(ansis.yellow(`\u2139 ${i18n.termuxDetected}`));
|
|
2254
|
+
console.log(ansis.yellow(`\u2139 ${i18n.installation.termuxDetected}`));
|
|
1643
2255
|
const termuxPrefix = getTermuxPrefix();
|
|
1644
|
-
console.log(ansis.gray(i18n.termuxPathInfo.replace("{path}", termuxPrefix)));
|
|
2256
|
+
console.log(ansis.gray(i18n.installation.termuxPathInfo.replace("{path}", termuxPrefix)));
|
|
1645
2257
|
console.log(ansis.gray(`Node.js: ${termuxPrefix}/bin/node`));
|
|
1646
2258
|
console.log(ansis.gray(`npm: ${termuxPrefix}/bin/npm`));
|
|
1647
2259
|
}
|
|
1648
|
-
console.log(i18n.installing);
|
|
2260
|
+
console.log(i18n.installation.installing);
|
|
1649
2261
|
try {
|
|
1650
2262
|
await exec("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
|
|
1651
|
-
console.log(`\u2714 ${i18n.installSuccess}`);
|
|
2263
|
+
console.log(`\u2714 ${i18n.installation.installSuccess}`);
|
|
1652
2264
|
if (isTermux()) {
|
|
1653
2265
|
console.log(ansis.gray(`
|
|
1654
2266
|
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
1655
2267
|
}
|
|
1656
2268
|
} catch (error) {
|
|
1657
|
-
console.error(`\u2716 ${i18n.installFailed}`);
|
|
2269
|
+
console.error(`\u2716 ${i18n.installation.installFailed}`);
|
|
1658
2270
|
if (isTermux()) {
|
|
1659
2271
|
console.error(ansis.yellow(`
|
|
1660
|
-
${i18n.termuxInstallHint}
|
|
2272
|
+
${i18n.installation.termuxInstallHint}
|
|
1661
2273
|
`));
|
|
1662
2274
|
}
|
|
1663
2275
|
throw error;
|
|
1664
2276
|
}
|
|
1665
2277
|
}
|
|
1666
2278
|
|
|
1667
|
-
function getMcpConfigPath() {
|
|
1668
|
-
return ClAUDE_CONFIG_FILE;
|
|
1669
|
-
}
|
|
1670
|
-
function readMcpConfig() {
|
|
1671
|
-
return readJsonConfig(ClAUDE_CONFIG_FILE);
|
|
1672
|
-
}
|
|
1673
|
-
function writeMcpConfig(config) {
|
|
1674
|
-
writeJsonConfig(ClAUDE_CONFIG_FILE, config);
|
|
1675
|
-
}
|
|
1676
|
-
function backupMcpConfig() {
|
|
1677
|
-
const backupBaseDir = join(CLAUDE_DIR, "backup");
|
|
1678
|
-
return backupJsonConfig(ClAUDE_CONFIG_FILE, backupBaseDir);
|
|
1679
|
-
}
|
|
1680
|
-
function mergeMcpServers(existing, newServers) {
|
|
1681
|
-
const config = existing || { mcpServers: {} };
|
|
1682
|
-
if (!config.mcpServers) {
|
|
1683
|
-
config.mcpServers = {};
|
|
1684
|
-
}
|
|
1685
|
-
Object.assign(config.mcpServers, newServers);
|
|
1686
|
-
return config;
|
|
1687
|
-
}
|
|
1688
|
-
function applyPlatformCommand(config) {
|
|
1689
|
-
if (config.command === "npx" && isWindows()) {
|
|
1690
|
-
const mcpCmd = getMcpCommand();
|
|
1691
|
-
config.command = mcpCmd[0];
|
|
1692
|
-
config.args = [...mcpCmd.slice(1), ...config.args || []];
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
function buildMcpServerConfig(baseConfig, apiKey, placeholder = "YOUR_EXA_API_KEY", envVarName) {
|
|
1696
|
-
const config = deepClone(baseConfig);
|
|
1697
|
-
applyPlatformCommand(config);
|
|
1698
|
-
if (!apiKey) {
|
|
1699
|
-
return config;
|
|
1700
|
-
}
|
|
1701
|
-
if (envVarName && config.env) {
|
|
1702
|
-
config.env[envVarName] = apiKey;
|
|
1703
|
-
return config;
|
|
1704
|
-
}
|
|
1705
|
-
if (config.args) {
|
|
1706
|
-
config.args = config.args.map((arg) => arg.replace(placeholder, apiKey));
|
|
1707
|
-
}
|
|
1708
|
-
if (config.url) {
|
|
1709
|
-
config.url = config.url.replace(placeholder, apiKey);
|
|
1710
|
-
}
|
|
1711
|
-
return config;
|
|
1712
|
-
}
|
|
1713
|
-
function fixWindowsMcpConfig(config) {
|
|
1714
|
-
if (!isWindows() || !config.mcpServers) {
|
|
1715
|
-
return config;
|
|
1716
|
-
}
|
|
1717
|
-
const fixed = { ...config };
|
|
1718
|
-
for (const [, serverConfig] of Object.entries(fixed.mcpServers)) {
|
|
1719
|
-
if (serverConfig && typeof serverConfig === "object" && "command" in serverConfig) {
|
|
1720
|
-
applyPlatformCommand(serverConfig);
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
|
-
return fixed;
|
|
1724
|
-
}
|
|
1725
|
-
function addCompletedOnboarding() {
|
|
1726
|
-
try {
|
|
1727
|
-
let config = readMcpConfig();
|
|
1728
|
-
if (!config) {
|
|
1729
|
-
config = { mcpServers: {} };
|
|
1730
|
-
}
|
|
1731
|
-
config.hasCompletedOnboarding = true;
|
|
1732
|
-
config = fixWindowsMcpConfig(config);
|
|
1733
|
-
writeMcpConfig(config);
|
|
1734
|
-
} catch (error) {
|
|
1735
|
-
console.error(I18N[readZcfConfig()?.preferredLang || "en"].failedToAddOnboardingFlag, error);
|
|
1736
|
-
throw error;
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
2279
|
async function selectAiOutputLanguage(scriptLang, defaultLang) {
|
|
1741
|
-
const i18n =
|
|
2280
|
+
const i18n = getTranslation(scriptLang);
|
|
1742
2281
|
console.log(ansis.dim(`
|
|
1743
|
-
${i18n.aiOutputLangHint}
|
|
2282
|
+
${i18n.language.aiOutputLangHint}
|
|
1744
2283
|
`));
|
|
1745
2284
|
const aiLangChoices = Object.entries(AI_OUTPUT_LANGUAGES).map(([key, value]) => ({
|
|
1746
2285
|
title: value.label,
|
|
@@ -1750,15 +2289,15 @@ async function selectAiOutputLanguage(scriptLang, defaultLang) {
|
|
|
1750
2289
|
const { lang } = await inquirer.prompt({
|
|
1751
2290
|
type: "list",
|
|
1752
2291
|
name: "lang",
|
|
1753
|
-
message: i18n.selectAiOutputLang,
|
|
1754
|
-
choices: aiLangChoices.map((choice) => ({
|
|
2292
|
+
message: i18n.language.selectAiOutputLang,
|
|
2293
|
+
choices: addNumbersToChoices(aiLangChoices.map((choice) => ({
|
|
1755
2294
|
name: choice.title,
|
|
1756
2295
|
value: choice.value
|
|
1757
|
-
})),
|
|
2296
|
+
}))),
|
|
1758
2297
|
default: defaultChoice
|
|
1759
2298
|
});
|
|
1760
2299
|
if (!lang) {
|
|
1761
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2300
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1762
2301
|
process.exit(0);
|
|
1763
2302
|
}
|
|
1764
2303
|
let aiOutputLang = lang;
|
|
@@ -1766,11 +2305,11 @@ async function selectAiOutputLanguage(scriptLang, defaultLang) {
|
|
|
1766
2305
|
const { customLang } = await inquirer.prompt({
|
|
1767
2306
|
type: "input",
|
|
1768
2307
|
name: "customLang",
|
|
1769
|
-
message: i18n.enterCustomLanguage,
|
|
1770
|
-
validate: (value) => !!value || i18n.languageRequired
|
|
2308
|
+
message: i18n.language.enterCustomLanguage,
|
|
2309
|
+
validate: (value) => !!value || i18n.language?.languageRequired || "Language is required"
|
|
1771
2310
|
});
|
|
1772
2311
|
if (!customLang) {
|
|
1773
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2312
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1774
2313
|
process.exit(0);
|
|
1775
2314
|
}
|
|
1776
2315
|
return customLang;
|
|
@@ -1789,10 +2328,10 @@ async function selectScriptLanguage(currentLang) {
|
|
|
1789
2328
|
type: "list",
|
|
1790
2329
|
name: "lang",
|
|
1791
2330
|
message: "Select ZCF display language / \u9009\u62E9ZCF\u663E\u793A\u8BED\u8A00",
|
|
1792
|
-
choices: SUPPORTED_LANGS.map((l) => ({
|
|
2331
|
+
choices: addNumbersToChoices(SUPPORTED_LANGS.map((l) => ({
|
|
1793
2332
|
name: LANG_LABELS[l],
|
|
1794
2333
|
value: l
|
|
1795
|
-
}))
|
|
2334
|
+
})))
|
|
1796
2335
|
});
|
|
1797
2336
|
if (!lang) {
|
|
1798
2337
|
console.log(ansis.yellow("Operation cancelled / \u64CD\u4F5C\u5DF2\u53D6\u6D88"));
|
|
@@ -1806,19 +2345,26 @@ async function selectScriptLanguage(currentLang) {
|
|
|
1806
2345
|
return scriptLang;
|
|
1807
2346
|
}
|
|
1808
2347
|
async function resolveAiOutputLanguage(scriptLang, commandLineOption, savedConfig) {
|
|
1809
|
-
const i18n =
|
|
2348
|
+
const i18n = getTranslation(scriptLang);
|
|
1810
2349
|
if (commandLineOption) {
|
|
1811
2350
|
return commandLineOption;
|
|
1812
2351
|
}
|
|
1813
2352
|
if (savedConfig?.aiOutputLang) {
|
|
1814
|
-
console.log(ansis.gray(`\u2714 ${i18n.aiOutputLangHint}: ${savedConfig.aiOutputLang}`));
|
|
2353
|
+
console.log(ansis.gray(`\u2714 ${i18n.language.aiOutputLangHint}: ${savedConfig.aiOutputLang}`));
|
|
1815
2354
|
return savedConfig.aiOutputLang;
|
|
1816
2355
|
}
|
|
1817
2356
|
return await selectAiOutputLanguage(scriptLang, scriptLang);
|
|
1818
2357
|
}
|
|
1819
2358
|
|
|
2359
|
+
const prompts = {
|
|
2360
|
+
__proto__: null,
|
|
2361
|
+
resolveAiOutputLanguage: resolveAiOutputLanguage,
|
|
2362
|
+
selectAiOutputLanguage: selectAiOutputLanguage,
|
|
2363
|
+
selectScriptLanguage: selectScriptLanguage
|
|
2364
|
+
};
|
|
2365
|
+
|
|
1820
2366
|
async function selectMcpServices(scriptLang) {
|
|
1821
|
-
const i18n =
|
|
2367
|
+
const i18n = getTranslation(scriptLang);
|
|
1822
2368
|
const choices = MCP_SERVICES.map((service) => ({
|
|
1823
2369
|
name: `${service.name[scriptLang]} - ${ansis.gray(service.description[scriptLang])}`,
|
|
1824
2370
|
value: service.id,
|
|
@@ -1827,11 +2373,11 @@ async function selectMcpServices(scriptLang) {
|
|
|
1827
2373
|
const { services } = await inquirer.prompt({
|
|
1828
2374
|
type: "checkbox",
|
|
1829
2375
|
name: "services",
|
|
1830
|
-
message: `${i18n.selectMcpServices}${i18n.multiSelectHint}`,
|
|
2376
|
+
message: `${i18n.mcp.selectMcpServices}${i18n.common.multiSelectHint}`,
|
|
1831
2377
|
choices
|
|
1832
2378
|
});
|
|
1833
2379
|
if (services === void 0) {
|
|
1834
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2380
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1835
2381
|
return void 0;
|
|
1836
2382
|
}
|
|
1837
2383
|
return services;
|
|
@@ -1891,11 +2437,11 @@ function getRootDir() {
|
|
|
1891
2437
|
return dirname(distDir);
|
|
1892
2438
|
}
|
|
1893
2439
|
async function selectAndInstallWorkflows(configLang, scriptLang) {
|
|
1894
|
-
const i18n =
|
|
2440
|
+
const i18n = getTranslation(scriptLang);
|
|
1895
2441
|
const workflows = getOrderedWorkflows();
|
|
1896
2442
|
const choices = workflows.map((workflow) => {
|
|
1897
2443
|
const nameKey = workflow.id;
|
|
1898
|
-
const name = i18n.workflowOption[nameKey] || workflow.id;
|
|
2444
|
+
const name = i18n.workflow.workflowOption[nameKey] || workflow.id;
|
|
1899
2445
|
return {
|
|
1900
2446
|
name,
|
|
1901
2447
|
value: workflow.id,
|
|
@@ -1905,11 +2451,11 @@ async function selectAndInstallWorkflows(configLang, scriptLang) {
|
|
|
1905
2451
|
const { selectedWorkflows } = await inquirer.prompt({
|
|
1906
2452
|
type: "checkbox",
|
|
1907
2453
|
name: "selectedWorkflows",
|
|
1908
|
-
message: `${i18n.selectWorkflowType}${i18n.multiSelectHint}`,
|
|
2454
|
+
message: `${i18n.workflow.selectWorkflowType}${i18n.common.multiSelectHint}`,
|
|
1909
2455
|
choices
|
|
1910
2456
|
});
|
|
1911
2457
|
if (!selectedWorkflows || selectedWorkflows.length === 0) {
|
|
1912
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2458
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1913
2459
|
return;
|
|
1914
2460
|
}
|
|
1915
2461
|
await cleanupOldVersionFiles(scriptLang);
|
|
@@ -1922,7 +2468,7 @@ async function selectAndInstallWorkflows(configLang, scriptLang) {
|
|
|
1922
2468
|
}
|
|
1923
2469
|
async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
1924
2470
|
const rootDir = getRootDir();
|
|
1925
|
-
const i18n =
|
|
2471
|
+
const i18n = getTranslation(scriptLang);
|
|
1926
2472
|
const result = {
|
|
1927
2473
|
workflow: config.id,
|
|
1928
2474
|
success: true,
|
|
@@ -1930,12 +2476,12 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
|
1930
2476
|
installedAgents: [],
|
|
1931
2477
|
errors: []
|
|
1932
2478
|
};
|
|
1933
|
-
const workflowName = i18n.workflowOption[config.id] || config.id;
|
|
2479
|
+
const workflowName = i18n.workflow.workflowOption[config.id] || config.id;
|
|
1934
2480
|
console.log(ansis.cyan(`
|
|
1935
|
-
\u{1F4E6} ${i18n.installingWorkflow}: ${workflowName}...`));
|
|
2481
|
+
\u{1F4E6} ${i18n.workflow.installingWorkflow}: ${workflowName}...`));
|
|
1936
2482
|
const commandsDir = join(CLAUDE_DIR, "commands", "zcf");
|
|
1937
2483
|
if (!existsSync(commandsDir)) {
|
|
1938
|
-
await mkdir(commandsDir, { recursive: true });
|
|
2484
|
+
await mkdir$1(commandsDir, { recursive: true });
|
|
1939
2485
|
}
|
|
1940
2486
|
for (const commandFile of config.commands) {
|
|
1941
2487
|
const commandSource = join(rootDir, "templates", configLang, "workflow", config.category, "commands", commandFile);
|
|
@@ -1945,9 +2491,9 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
|
1945
2491
|
try {
|
|
1946
2492
|
await copyFile$1(commandSource, commandDest);
|
|
1947
2493
|
result.installedCommands.push(destFileName);
|
|
1948
|
-
console.log(ansis.gray(` \u2714 ${i18n.installedCommand}: zcf/${destFileName}`));
|
|
2494
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.installedCommand}: zcf/${destFileName}`));
|
|
1949
2495
|
} catch (error) {
|
|
1950
|
-
const errorMsg = `${i18n.failedToInstallCommand} ${commandFile}: ${error}`;
|
|
2496
|
+
const errorMsg = `${i18n.workflow.failedToInstallCommand} ${commandFile}: ${error}`;
|
|
1951
2497
|
result.errors?.push(errorMsg);
|
|
1952
2498
|
console.error(ansis.red(` \u2717 ${errorMsg}`));
|
|
1953
2499
|
result.success = false;
|
|
@@ -1957,7 +2503,7 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
|
1957
2503
|
if (config.autoInstallAgents && config.agents.length > 0) {
|
|
1958
2504
|
const agentsCategoryDir = join(CLAUDE_DIR, "agents", "zcf", config.category);
|
|
1959
2505
|
if (!existsSync(agentsCategoryDir)) {
|
|
1960
|
-
await mkdir(agentsCategoryDir, { recursive: true });
|
|
2506
|
+
await mkdir$1(agentsCategoryDir, { recursive: true });
|
|
1961
2507
|
}
|
|
1962
2508
|
for (const agent of config.agents) {
|
|
1963
2509
|
const agentSource = join(rootDir, "templates", configLang, "workflow", config.category, "agents", agent.filename);
|
|
@@ -1966,9 +2512,9 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
|
1966
2512
|
try {
|
|
1967
2513
|
await copyFile$1(agentSource, agentDest);
|
|
1968
2514
|
result.installedAgents.push(agent.filename);
|
|
1969
|
-
console.log(ansis.gray(` \u2714 ${i18n.installedAgent}: zcf/${config.category}/${agent.filename}`));
|
|
2515
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.installedAgent}: zcf/${config.category}/${agent.filename}`));
|
|
1970
2516
|
} catch (error) {
|
|
1971
|
-
const errorMsg = `${i18n.failedToInstallAgent} ${agent.filename}: ${error}`;
|
|
2517
|
+
const errorMsg = `${i18n.workflow.failedToInstallAgent} ${agent.filename}: ${error}`;
|
|
1972
2518
|
result.errors?.push(errorMsg);
|
|
1973
2519
|
console.error(ansis.red(` \u2717 ${errorMsg}`));
|
|
1974
2520
|
if (agent.required) {
|
|
@@ -1979,20 +2525,20 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
|
1979
2525
|
}
|
|
1980
2526
|
}
|
|
1981
2527
|
if (result.success) {
|
|
1982
|
-
console.log(ansis.green(`\u2714 ${workflowName} ${i18n.workflowInstallSuccess}`));
|
|
2528
|
+
console.log(ansis.green(`\u2714 ${workflowName} ${i18n.workflow.workflowInstallSuccess}`));
|
|
1983
2529
|
if (config.id === "bmadWorkflow") {
|
|
1984
2530
|
console.log(ansis.cyan(`
|
|
1985
|
-
${i18n.bmadInitPrompt}`));
|
|
2531
|
+
${i18n.bmad.bmadInitPrompt}`));
|
|
1986
2532
|
}
|
|
1987
2533
|
} else {
|
|
1988
|
-
console.log(ansis.red(`\u2717 ${workflowName} ${i18n.workflowInstallError}`));
|
|
2534
|
+
console.log(ansis.red(`\u2717 ${workflowName} ${i18n.workflow.workflowInstallError}`));
|
|
1989
2535
|
}
|
|
1990
2536
|
return result;
|
|
1991
2537
|
}
|
|
1992
2538
|
async function cleanupOldVersionFiles(scriptLang) {
|
|
1993
|
-
const i18n =
|
|
2539
|
+
const i18n = getTranslation(scriptLang);
|
|
1994
2540
|
console.log(ansis.cyan(`
|
|
1995
|
-
\u{1F9F9} ${i18n.cleaningOldFiles || "Cleaning up old version files"}...`));
|
|
2541
|
+
\u{1F9F9} ${i18n.workflow.cleaningOldFiles || "Cleaning up old version files"}...`));
|
|
1996
2542
|
const oldCommandFiles = [
|
|
1997
2543
|
join(CLAUDE_DIR, "commands", "workflow.md"),
|
|
1998
2544
|
join(CLAUDE_DIR, "commands", "feat.md")
|
|
@@ -2005,9 +2551,9 @@ async function cleanupOldVersionFiles(scriptLang) {
|
|
|
2005
2551
|
if (existsSync(file)) {
|
|
2006
2552
|
try {
|
|
2007
2553
|
await rm(file, { force: true });
|
|
2008
|
-
console.log(ansis.gray(` \u2714 ${i18n.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2554
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2009
2555
|
} catch (error) {
|
|
2010
|
-
console.error(ansis.yellow(` \u26A0 ${i18n.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2556
|
+
console.error(ansis.yellow(` \u26A0 ${i18n.workflow.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2011
2557
|
}
|
|
2012
2558
|
}
|
|
2013
2559
|
}
|
|
@@ -2015,14 +2561,468 @@ async function cleanupOldVersionFiles(scriptLang) {
|
|
|
2015
2561
|
if (existsSync(file)) {
|
|
2016
2562
|
try {
|
|
2017
2563
|
await rm(file, { force: true });
|
|
2018
|
-
console.log(ansis.gray(` \u2714 ${i18n.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2564
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2019
2565
|
} catch (error) {
|
|
2020
|
-
console.error(ansis.yellow(` \u26A0 ${i18n.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2566
|
+
console.error(ansis.yellow(` \u26A0 ${i18n.workflow.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2021
2567
|
}
|
|
2022
2568
|
}
|
|
2023
2569
|
}
|
|
2024
2570
|
}
|
|
2025
2571
|
|
|
2572
|
+
const execAsync$1 = promisify$1(exec$2);
|
|
2573
|
+
async function isCcrInstalled() {
|
|
2574
|
+
try {
|
|
2575
|
+
await execAsync$1("ccr version");
|
|
2576
|
+
return true;
|
|
2577
|
+
} catch {
|
|
2578
|
+
try {
|
|
2579
|
+
await execAsync$1("which ccr");
|
|
2580
|
+
return true;
|
|
2581
|
+
} catch {
|
|
2582
|
+
return false;
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
async function installCcr(scriptLang) {
|
|
2587
|
+
const i18n = getTranslation(scriptLang);
|
|
2588
|
+
const installed = await isCcrInstalled();
|
|
2589
|
+
if (installed) {
|
|
2590
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
2591
|
+
await updateCcr(scriptLang);
|
|
2592
|
+
return;
|
|
2593
|
+
}
|
|
2594
|
+
console.log(ansis.cyan(`\u{1F4E6} ${i18n.ccr.installingCcr}`));
|
|
2595
|
+
try {
|
|
2596
|
+
await execAsync$1("npm install -g claude-code-router --force");
|
|
2597
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrInstallSuccess}`));
|
|
2598
|
+
} catch (error) {
|
|
2599
|
+
if (error.message?.includes("EEXIST")) {
|
|
2600
|
+
console.log(ansis.yellow(`\u26A0 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
2601
|
+
await updateCcr(scriptLang);
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2604
|
+
console.error(ansis.red(`\u2716 ${i18n.ccr.ccrInstallFailed}`));
|
|
2605
|
+
throw error;
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
const PROVIDER_PRESETS_URL = "https://pub-0dc3e1677e894f07bbea11b17a29e032.r2.dev/providers.json";
|
|
2610
|
+
async function fetchProviderPresets() {
|
|
2611
|
+
try {
|
|
2612
|
+
const controller = new AbortController();
|
|
2613
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
2614
|
+
const response = await fetch(PROVIDER_PRESETS_URL, {
|
|
2615
|
+
signal: controller.signal
|
|
2616
|
+
});
|
|
2617
|
+
clearTimeout(timeoutId);
|
|
2618
|
+
if (!response.ok) {
|
|
2619
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
2620
|
+
}
|
|
2621
|
+
const data = await response.json();
|
|
2622
|
+
const presets = [];
|
|
2623
|
+
if (Array.isArray(data)) {
|
|
2624
|
+
for (const provider of data) {
|
|
2625
|
+
if (provider && typeof provider === "object") {
|
|
2626
|
+
presets.push({
|
|
2627
|
+
name: provider.name || "",
|
|
2628
|
+
provider: provider.name || "",
|
|
2629
|
+
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
2630
|
+
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
2631
|
+
models: provider.models || [],
|
|
2632
|
+
description: provider.description || provider.name || "",
|
|
2633
|
+
transformer: provider.transformer
|
|
2634
|
+
});
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
} else if (data && typeof data === "object") {
|
|
2638
|
+
for (const [key, value] of Object.entries(data)) {
|
|
2639
|
+
if (typeof value === "object" && value !== null) {
|
|
2640
|
+
const provider = value;
|
|
2641
|
+
presets.push({
|
|
2642
|
+
name: provider.name || key,
|
|
2643
|
+
provider: key,
|
|
2644
|
+
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
2645
|
+
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
2646
|
+
models: provider.models || [],
|
|
2647
|
+
description: provider.description || "",
|
|
2648
|
+
transformer: provider.transformer
|
|
2649
|
+
});
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
return presets;
|
|
2654
|
+
} catch (error) {
|
|
2655
|
+
return getFallbackPresets();
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
function getFallbackPresets() {
|
|
2659
|
+
return [
|
|
2660
|
+
{
|
|
2661
|
+
name: "dashscope",
|
|
2662
|
+
provider: "dashscope",
|
|
2663
|
+
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
|
|
2664
|
+
requiresApiKey: true,
|
|
2665
|
+
models: ["qwen3-coder-plus"],
|
|
2666
|
+
description: "Alibaba DashScope",
|
|
2667
|
+
transformer: {
|
|
2668
|
+
use: [["maxtoken", { max_tokens: 65536 }]],
|
|
2669
|
+
"qwen3-coder-plus": {
|
|
2670
|
+
use: ["enhancetool"]
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
},
|
|
2674
|
+
{
|
|
2675
|
+
name: "deepseek",
|
|
2676
|
+
provider: "deepseek",
|
|
2677
|
+
baseURL: "https://api.deepseek.com/chat/completions",
|
|
2678
|
+
requiresApiKey: true,
|
|
2679
|
+
models: ["deepseek-chat", "deepseek-reasoner"],
|
|
2680
|
+
description: "DeepSeek AI models",
|
|
2681
|
+
transformer: {
|
|
2682
|
+
use: ["deepseek"],
|
|
2683
|
+
"deepseek-chat": {
|
|
2684
|
+
use: ["tooluse"]
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
},
|
|
2688
|
+
{
|
|
2689
|
+
name: "gemini",
|
|
2690
|
+
provider: "gemini",
|
|
2691
|
+
baseURL: "https://generativelanguage.googleapis.com/v1beta/models/",
|
|
2692
|
+
requiresApiKey: true,
|
|
2693
|
+
models: ["gemini-2.5-flash", "gemini-2.5-pro"],
|
|
2694
|
+
description: "Google Gemini models",
|
|
2695
|
+
transformer: {
|
|
2696
|
+
use: ["gemini"]
|
|
2697
|
+
}
|
|
2698
|
+
},
|
|
2699
|
+
{
|
|
2700
|
+
name: "modelscope",
|
|
2701
|
+
provider: "modelscope",
|
|
2702
|
+
baseURL: "https://api-inference.modelscope.cn/v1/chat/completions",
|
|
2703
|
+
requiresApiKey: true,
|
|
2704
|
+
models: ["Qwen/Qwen3-Coder-480B-A35B-Instruct", "Qwen/Qwen3-235B-A22B-Thinking-2507", "ZhipuAI/GLM-4.5"],
|
|
2705
|
+
description: "ModelScope AI models",
|
|
2706
|
+
transformer: {
|
|
2707
|
+
use: [["maxtoken", { max_tokens: 65536 }]],
|
|
2708
|
+
"Qwen/Qwen3-Coder-480B-A35B-Instruct": {
|
|
2709
|
+
use: ["enhancetool"]
|
|
2710
|
+
},
|
|
2711
|
+
"Qwen/Qwen3-235B-A22B-Thinking-2507": {
|
|
2712
|
+
use: ["reasoning"]
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
},
|
|
2716
|
+
{
|
|
2717
|
+
name: "openrouter",
|
|
2718
|
+
provider: "openrouter",
|
|
2719
|
+
baseURL: "https://openrouter.ai/api/v1/chat/completions",
|
|
2720
|
+
requiresApiKey: true,
|
|
2721
|
+
models: [
|
|
2722
|
+
"google/gemini-2.5-pro-preview",
|
|
2723
|
+
"anthropic/claude-sonnet-4",
|
|
2724
|
+
"anthropic/claude-3.5-sonnet",
|
|
2725
|
+
"anthropic/claude-3.7-sonnet:thinking"
|
|
2726
|
+
],
|
|
2727
|
+
description: "OpenRouter API",
|
|
2728
|
+
transformer: {
|
|
2729
|
+
use: ["openrouter"]
|
|
2730
|
+
}
|
|
2731
|
+
},
|
|
2732
|
+
{
|
|
2733
|
+
name: "siliconflow",
|
|
2734
|
+
provider: "siliconflow",
|
|
2735
|
+
baseURL: "https://api.siliconflow.cn/v1/chat/completions",
|
|
2736
|
+
requiresApiKey: true,
|
|
2737
|
+
models: ["moonshotai/Kimi-K2-Instruct"],
|
|
2738
|
+
description: "SiliconFlow AI",
|
|
2739
|
+
transformer: {
|
|
2740
|
+
use: [["maxtoken", { max_tokens: 16384 }]]
|
|
2741
|
+
}
|
|
2742
|
+
},
|
|
2743
|
+
{
|
|
2744
|
+
name: "volcengine",
|
|
2745
|
+
provider: "volcengine",
|
|
2746
|
+
baseURL: "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
|
|
2747
|
+
requiresApiKey: true,
|
|
2748
|
+
models: ["deepseek-v3-250324", "deepseek-r1-250528"],
|
|
2749
|
+
description: "Volcengine AI",
|
|
2750
|
+
transformer: {
|
|
2751
|
+
use: ["deepseek"]
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
];
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
const execAsync = promisify$1(exec$2);
|
|
2758
|
+
const CCR_CONFIG_DIR = join$2(homedir(), ".claude-code-router");
|
|
2759
|
+
const CCR_CONFIG_FILE = join$2(CCR_CONFIG_DIR, "config.json");
|
|
2760
|
+
const CCR_BACKUP_DIR = CCR_CONFIG_DIR;
|
|
2761
|
+
function ensureCcrConfigDir() {
|
|
2762
|
+
if (!existsSync(CCR_CONFIG_DIR)) {
|
|
2763
|
+
mkdirSync(CCR_CONFIG_DIR, { recursive: true });
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
function backupCcrConfig(scriptLang) {
|
|
2767
|
+
const i18n = getTranslation(scriptLang);
|
|
2768
|
+
try {
|
|
2769
|
+
if (!existsSync(CCR_CONFIG_FILE)) {
|
|
2770
|
+
return null;
|
|
2771
|
+
}
|
|
2772
|
+
const timestamp = dayjs().format("YYYY-MM-DDTHH-mm-ss-SSS") + "Z";
|
|
2773
|
+
const backupFileName = `config.json.${timestamp}.bak`;
|
|
2774
|
+
const backupPath = join$2(CCR_BACKUP_DIR, backupFileName);
|
|
2775
|
+
console.log(ansis.cyan(`${i18n.ccr.backupCcrConfig}`));
|
|
2776
|
+
copyFileSync(CCR_CONFIG_FILE, backupPath);
|
|
2777
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrBackupSuccess.replace("{path}", backupPath)}`));
|
|
2778
|
+
return backupPath;
|
|
2779
|
+
} catch (error) {
|
|
2780
|
+
console.error(ansis.red(`${i18n.ccr.ccrBackupFailed}:`), error.message);
|
|
2781
|
+
return null;
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
function readCcrConfig() {
|
|
2785
|
+
if (!existsSync(CCR_CONFIG_FILE)) {
|
|
2786
|
+
return null;
|
|
2787
|
+
}
|
|
2788
|
+
return readJsonConfig(CCR_CONFIG_FILE);
|
|
2789
|
+
}
|
|
2790
|
+
function writeCcrConfig(config) {
|
|
2791
|
+
ensureCcrConfigDir();
|
|
2792
|
+
writeJsonConfig(CCR_CONFIG_FILE, config);
|
|
2793
|
+
}
|
|
2794
|
+
async function configureCcrProxy(ccrConfig) {
|
|
2795
|
+
const settings = readJsonConfig(SETTINGS_FILE) || {};
|
|
2796
|
+
const host = ccrConfig.HOST || "127.0.0.1";
|
|
2797
|
+
const port = ccrConfig.PORT || 3456;
|
|
2798
|
+
const apiKey = ccrConfig.APIKEY || "sk-zcf-x-ccr";
|
|
2799
|
+
if (!settings.env) {
|
|
2800
|
+
settings.env = {};
|
|
2801
|
+
}
|
|
2802
|
+
settings.env.ANTHROPIC_BASE_URL = `http://${host}:${port}`;
|
|
2803
|
+
settings.env.ANTHROPIC_API_KEY = apiKey;
|
|
2804
|
+
writeJsonConfig(SETTINGS_FILE, settings);
|
|
2805
|
+
}
|
|
2806
|
+
async function selectCcrPreset(scriptLang) {
|
|
2807
|
+
const i18n = getTranslation(scriptLang);
|
|
2808
|
+
console.log(ansis.cyan(`${i18n.ccr.fetchingPresets}`));
|
|
2809
|
+
const presets = await fetchProviderPresets();
|
|
2810
|
+
if (!presets || presets.length === 0) {
|
|
2811
|
+
console.log(ansis.yellow(`${i18n.ccr.noPresetsAvailable}`));
|
|
2812
|
+
return null;
|
|
2813
|
+
}
|
|
2814
|
+
try {
|
|
2815
|
+
const choices = [
|
|
2816
|
+
...presets.map((p, index) => ({
|
|
2817
|
+
name: `${index + 1}. ${p.name}`,
|
|
2818
|
+
value: p
|
|
2819
|
+
})),
|
|
2820
|
+
{
|
|
2821
|
+
name: `${presets.length + 1}. ${i18n.ccr.skipOption}`,
|
|
2822
|
+
value: "skip"
|
|
2823
|
+
}
|
|
2824
|
+
];
|
|
2825
|
+
const { preset } = await inquirer.prompt({
|
|
2826
|
+
type: "list",
|
|
2827
|
+
name: "preset",
|
|
2828
|
+
message: i18n.ccr.selectCcrPreset,
|
|
2829
|
+
choices
|
|
2830
|
+
});
|
|
2831
|
+
return preset;
|
|
2832
|
+
} catch (error) {
|
|
2833
|
+
if (error.name === "ExitPromptError") {
|
|
2834
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2835
|
+
return null;
|
|
2836
|
+
}
|
|
2837
|
+
throw error;
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
async function configureCcrWithPreset(preset, scriptLang) {
|
|
2841
|
+
const i18n = getTranslation(scriptLang);
|
|
2842
|
+
const provider = {
|
|
2843
|
+
name: preset.name,
|
|
2844
|
+
// Use the original name from JSON
|
|
2845
|
+
api_base_url: preset.baseURL || "",
|
|
2846
|
+
api_key: "",
|
|
2847
|
+
models: preset.models
|
|
2848
|
+
};
|
|
2849
|
+
if (preset.transformer) {
|
|
2850
|
+
provider.transformer = preset.transformer;
|
|
2851
|
+
}
|
|
2852
|
+
if (preset.requiresApiKey) {
|
|
2853
|
+
try {
|
|
2854
|
+
const { apiKey } = await inquirer.prompt({
|
|
2855
|
+
type: "input",
|
|
2856
|
+
name: "apiKey",
|
|
2857
|
+
message: i18n.ccr.enterApiKeyForProvider.replace("{provider}", preset.name),
|
|
2858
|
+
validate: (value) => !!value || i18n.api.keyRequired
|
|
2859
|
+
});
|
|
2860
|
+
provider.api_key = apiKey;
|
|
2861
|
+
} catch (error) {
|
|
2862
|
+
if (error.name === "ExitPromptError") {
|
|
2863
|
+
throw error;
|
|
2864
|
+
}
|
|
2865
|
+
throw error;
|
|
2866
|
+
}
|
|
2867
|
+
} else {
|
|
2868
|
+
provider.api_key = "sk-free";
|
|
2869
|
+
}
|
|
2870
|
+
let defaultModel = preset.models[0];
|
|
2871
|
+
if (preset.models.length > 1) {
|
|
2872
|
+
try {
|
|
2873
|
+
const { model } = await inquirer.prompt({
|
|
2874
|
+
type: "list",
|
|
2875
|
+
name: "model",
|
|
2876
|
+
message: i18n.ccr.selectDefaultModelForProvider.replace("{provider}", preset.name),
|
|
2877
|
+
choices: preset.models.map((m, index) => ({
|
|
2878
|
+
name: `${index + 1}. ${m}`,
|
|
2879
|
+
value: m
|
|
2880
|
+
}))
|
|
2881
|
+
});
|
|
2882
|
+
defaultModel = model;
|
|
2883
|
+
} catch (error) {
|
|
2884
|
+
if (error.name === "ExitPromptError") {
|
|
2885
|
+
throw error;
|
|
2886
|
+
}
|
|
2887
|
+
throw error;
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
const router = {
|
|
2891
|
+
default: `${preset.name},${defaultModel}`,
|
|
2892
|
+
// Use the original name
|
|
2893
|
+
background: `${preset.name},${defaultModel}`,
|
|
2894
|
+
think: `${preset.name},${defaultModel}`,
|
|
2895
|
+
longContext: `${preset.name},${defaultModel}`,
|
|
2896
|
+
longContextThreshold: 6e4,
|
|
2897
|
+
webSearch: `${preset.name},${defaultModel}`
|
|
2898
|
+
};
|
|
2899
|
+
const config = {
|
|
2900
|
+
LOG: true,
|
|
2901
|
+
CLAUDE_PATH: "",
|
|
2902
|
+
HOST: "127.0.0.1",
|
|
2903
|
+
PORT: 3456,
|
|
2904
|
+
APIKEY: "sk-zcf-x-ccr",
|
|
2905
|
+
API_TIMEOUT_MS: "600000",
|
|
2906
|
+
PROXY_URL: "",
|
|
2907
|
+
transformers: [],
|
|
2908
|
+
Providers: [provider],
|
|
2909
|
+
Router: router
|
|
2910
|
+
};
|
|
2911
|
+
return config;
|
|
2912
|
+
}
|
|
2913
|
+
async function restartAndCheckCcrStatus(scriptLang) {
|
|
2914
|
+
const i18n = getTranslation(scriptLang);
|
|
2915
|
+
try {
|
|
2916
|
+
console.log(ansis.cyan(`${i18n.ccr.restartingCcr}`));
|
|
2917
|
+
await execAsync("ccr restart");
|
|
2918
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrRestartSuccess}`));
|
|
2919
|
+
console.log(ansis.cyan(`${i18n.ccr.checkingCcrStatus}`));
|
|
2920
|
+
const { stdout } = await execAsync("ccr status");
|
|
2921
|
+
console.log(ansis.gray(stdout));
|
|
2922
|
+
} catch (error) {
|
|
2923
|
+
console.error(ansis.red(`${i18n.ccr.ccrRestartFailed}:`), error.message || error);
|
|
2924
|
+
if (process.env.DEBUG) {
|
|
2925
|
+
console.error("Full error:", error);
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
function showConfigurationTips(scriptLang, apiKey) {
|
|
2930
|
+
const i18n = getTranslation(scriptLang);
|
|
2931
|
+
console.log(ansis.bold.cyan(`
|
|
2932
|
+
\u{1F4CC} ${i18n.ccr.configTips}:`));
|
|
2933
|
+
console.log(ansis.blue(` \u2022 ${i18n.ccr.advancedConfigTip}`));
|
|
2934
|
+
console.log(ansis.blue(` \u2022 ${i18n.ccr.manualConfigTip}`));
|
|
2935
|
+
console.log(ansis.bold.yellow(` \u2022 ${i18n.ccr.useClaudeCommand}`));
|
|
2936
|
+
if (apiKey) {
|
|
2937
|
+
console.log(ansis.bold.green(` \u2022 ${i18n.ccr.ccrUiApiKey || "CCR UI API Key"}: ${apiKey}`));
|
|
2938
|
+
console.log(ansis.gray(` ${i18n.ccr.ccrUiApiKeyHint || "Use this API key to login to CCR UI"}`));
|
|
2939
|
+
}
|
|
2940
|
+
console.log("");
|
|
2941
|
+
}
|
|
2942
|
+
async function setupCcrConfiguration(scriptLang) {
|
|
2943
|
+
const i18n = getTranslation(scriptLang);
|
|
2944
|
+
try {
|
|
2945
|
+
const existingConfig = readCcrConfig();
|
|
2946
|
+
if (existingConfig) {
|
|
2947
|
+
console.log(ansis.blue(`\u2139 ${i18n.ccr.existingCcrConfig}`));
|
|
2948
|
+
let shouldBackupAndReconfigure = false;
|
|
2949
|
+
try {
|
|
2950
|
+
const result = await inquirer.prompt({
|
|
2951
|
+
type: "confirm",
|
|
2952
|
+
name: "overwrite",
|
|
2953
|
+
message: i18n.ccr.overwriteCcrConfig,
|
|
2954
|
+
default: false
|
|
2955
|
+
});
|
|
2956
|
+
shouldBackupAndReconfigure = result.overwrite;
|
|
2957
|
+
} catch (error) {
|
|
2958
|
+
if (error.name === "ExitPromptError") {
|
|
2959
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2960
|
+
return false;
|
|
2961
|
+
}
|
|
2962
|
+
throw error;
|
|
2963
|
+
}
|
|
2964
|
+
if (!shouldBackupAndReconfigure) {
|
|
2965
|
+
console.log(ansis.yellow(`${i18n.ccr.keepingExistingConfig}`));
|
|
2966
|
+
await configureCcrProxy(existingConfig);
|
|
2967
|
+
return true;
|
|
2968
|
+
}
|
|
2969
|
+
backupCcrConfig(scriptLang);
|
|
2970
|
+
}
|
|
2971
|
+
const preset = await selectCcrPreset(scriptLang);
|
|
2972
|
+
if (!preset) {
|
|
2973
|
+
return false;
|
|
2974
|
+
}
|
|
2975
|
+
let config;
|
|
2976
|
+
if (preset === "skip") {
|
|
2977
|
+
console.log(ansis.yellow(`${i18n.ccr.skipConfiguring}`));
|
|
2978
|
+
config = {
|
|
2979
|
+
LOG: false,
|
|
2980
|
+
CLAUDE_PATH: "",
|
|
2981
|
+
HOST: "127.0.0.1",
|
|
2982
|
+
PORT: 3456,
|
|
2983
|
+
APIKEY: "sk-zcf-x-ccr",
|
|
2984
|
+
API_TIMEOUT_MS: "600000",
|
|
2985
|
+
PROXY_URL: "",
|
|
2986
|
+
transformers: [],
|
|
2987
|
+
Providers: [],
|
|
2988
|
+
// Empty providers array
|
|
2989
|
+
Router: {
|
|
2990
|
+
// Empty router configuration - user will configure in CCR UI
|
|
2991
|
+
}
|
|
2992
|
+
};
|
|
2993
|
+
} else {
|
|
2994
|
+
config = await configureCcrWithPreset(preset, scriptLang);
|
|
2995
|
+
}
|
|
2996
|
+
writeCcrConfig(config);
|
|
2997
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrConfigSuccess}`));
|
|
2998
|
+
await configureCcrProxy(config);
|
|
2999
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.proxyConfigSuccess}`));
|
|
3000
|
+
await restartAndCheckCcrStatus(scriptLang);
|
|
3001
|
+
showConfigurationTips(scriptLang, config.APIKEY);
|
|
3002
|
+
try {
|
|
3003
|
+
addCompletedOnboarding();
|
|
3004
|
+
} catch (error) {
|
|
3005
|
+
console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
|
|
3006
|
+
}
|
|
3007
|
+
return true;
|
|
3008
|
+
} catch (error) {
|
|
3009
|
+
if (error.name === "ExitPromptError") {
|
|
3010
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3011
|
+
return false;
|
|
3012
|
+
}
|
|
3013
|
+
console.error(ansis.red(`${i18n.ccr.ccrConfigFailed}:`), error);
|
|
3014
|
+
return false;
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
async function configureCcrFeature(scriptLang) {
|
|
3018
|
+
const i18n = getTranslation(scriptLang);
|
|
3019
|
+
const backupDir = backupExistingConfig();
|
|
3020
|
+
if (backupDir) {
|
|
3021
|
+
console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
|
|
3022
|
+
}
|
|
3023
|
+
await setupCcrConfiguration(scriptLang);
|
|
3024
|
+
}
|
|
3025
|
+
|
|
2026
3026
|
async function init(options = {}) {
|
|
2027
3027
|
try {
|
|
2028
3028
|
if (!options.skipBanner) {
|
|
@@ -2032,22 +3032,22 @@ async function init(options = {}) {
|
|
|
2032
3032
|
const i18n = I18N[scriptLang];
|
|
2033
3033
|
if (isTermux()) {
|
|
2034
3034
|
console.log(ansis.yellow(`
|
|
2035
|
-
\u2139 ${i18n.termuxDetected}`));
|
|
2036
|
-
console.log(ansis.gray(i18n.termuxEnvironmentInfo));
|
|
3035
|
+
\u2139 ${i18n.installation.termuxDetected}`));
|
|
3036
|
+
console.log(ansis.gray(i18n.installation.termuxEnvironmentInfo));
|
|
2037
3037
|
}
|
|
2038
3038
|
let configLang = options.configLang;
|
|
2039
3039
|
if (!configLang) {
|
|
2040
3040
|
const { lang } = await inquirer.prompt({
|
|
2041
3041
|
type: "list",
|
|
2042
3042
|
name: "lang",
|
|
2043
|
-
message: i18n.selectConfigLang,
|
|
2044
|
-
choices: SUPPORTED_LANGS.map((l) => ({
|
|
2045
|
-
name: `${LANG_LABELS[l]} - ${i18n.configLangHint[l]}`,
|
|
3043
|
+
message: i18n.language.selectConfigLang,
|
|
3044
|
+
choices: addNumbersToChoices(SUPPORTED_LANGS.map((l) => ({
|
|
3045
|
+
name: `${LANG_LABELS[l]} - ${i18n.language.configLangHint[l]}`,
|
|
2046
3046
|
value: l
|
|
2047
|
-
}))
|
|
3047
|
+
})))
|
|
2048
3048
|
});
|
|
2049
3049
|
if (!lang) {
|
|
2050
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
3050
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2051
3051
|
process.exit(0);
|
|
2052
3052
|
}
|
|
2053
3053
|
configLang = lang;
|
|
@@ -2059,20 +3059,20 @@ async function init(options = {}) {
|
|
|
2059
3059
|
const { shouldInstall } = await inquirer.prompt({
|
|
2060
3060
|
type: "confirm",
|
|
2061
3061
|
name: "shouldInstall",
|
|
2062
|
-
message: i18n.installPrompt,
|
|
3062
|
+
message: i18n.installation.installPrompt,
|
|
2063
3063
|
default: true
|
|
2064
3064
|
});
|
|
2065
3065
|
if (shouldInstall === void 0) {
|
|
2066
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
3066
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2067
3067
|
process.exit(0);
|
|
2068
3068
|
}
|
|
2069
3069
|
if (shouldInstall) {
|
|
2070
3070
|
await installClaudeCode(scriptLang);
|
|
2071
3071
|
} else {
|
|
2072
|
-
console.log(ansis.yellow(i18n.skip));
|
|
3072
|
+
console.log(ansis.yellow(i18n.common.skip));
|
|
2073
3073
|
}
|
|
2074
3074
|
} else {
|
|
2075
|
-
console.log(ansis.green(`\u2714 ${i18n.
|
|
3075
|
+
console.log(ansis.green(`\u2714 ${i18n.installation.alreadyInstalled}`));
|
|
2076
3076
|
}
|
|
2077
3077
|
ensureClaudeDir();
|
|
2078
3078
|
let action = "new";
|
|
@@ -2080,21 +3080,21 @@ async function init(options = {}) {
|
|
|
2080
3080
|
const { action: userAction } = await inquirer.prompt({
|
|
2081
3081
|
type: "list",
|
|
2082
3082
|
name: "action",
|
|
2083
|
-
message: i18n.existingConfig,
|
|
2084
|
-
choices: [
|
|
2085
|
-
{ name: i18n.backupAndOverwrite, value: "backup" },
|
|
2086
|
-
{ name: i18n.updateDocsOnly, value: "docs-only" },
|
|
2087
|
-
{ name: i18n.mergeConfig, value: "merge" },
|
|
2088
|
-
{ name: i18n.skip, value: "skip" }
|
|
2089
|
-
]
|
|
3083
|
+
message: i18n.configuration.existingConfig,
|
|
3084
|
+
choices: addNumbersToChoices([
|
|
3085
|
+
{ name: i18n.configuration.backupAndOverwrite, value: "backup" },
|
|
3086
|
+
{ name: i18n.configuration.updateDocsOnly, value: "docs-only" },
|
|
3087
|
+
{ name: i18n.configuration.mergeConfig, value: "merge" },
|
|
3088
|
+
{ name: i18n.common.skip, value: "skip" }
|
|
3089
|
+
])
|
|
2090
3090
|
});
|
|
2091
3091
|
if (!userAction) {
|
|
2092
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
3092
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2093
3093
|
process.exit(0);
|
|
2094
3094
|
}
|
|
2095
3095
|
action = userAction;
|
|
2096
3096
|
if (action === "skip") {
|
|
2097
|
-
console.log(ansis.yellow(i18n.skip));
|
|
3097
|
+
console.log(ansis.yellow(i18n.common.skip));
|
|
2098
3098
|
return;
|
|
2099
3099
|
}
|
|
2100
3100
|
}
|
|
@@ -2103,65 +3103,103 @@ async function init(options = {}) {
|
|
|
2103
3103
|
if (action !== "docs-only" && (isNewInstall || ["backup", "merge"].includes(action))) {
|
|
2104
3104
|
const existingApiConfig = getExistingApiConfig();
|
|
2105
3105
|
if (existingApiConfig) {
|
|
2106
|
-
console.log("\n" + ansis.blue(`\u2139 ${i18n.existingApiConfig}`));
|
|
2107
|
-
console.log(ansis.gray(` ${i18n.apiConfigUrl}: ${existingApiConfig.url || i18n.notConfigured}`));
|
|
3106
|
+
console.log("\n" + ansis.blue(`\u2139 ${i18n.api.existingApiConfig}`));
|
|
3107
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${existingApiConfig.url || i18n.common.notConfigured}`));
|
|
2108
3108
|
console.log(
|
|
2109
3109
|
ansis.gray(
|
|
2110
|
-
` ${i18n.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.notConfigured}`
|
|
3110
|
+
` ${i18n.api.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.common.notConfigured}`
|
|
2111
3111
|
)
|
|
2112
3112
|
);
|
|
2113
|
-
console.log(ansis.gray(` ${i18n.apiConfigAuthType}: ${existingApiConfig.authType || i18n.notConfigured}
|
|
3113
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigAuthType}: ${existingApiConfig.authType || i18n.common.notConfigured}
|
|
2114
3114
|
`));
|
|
2115
3115
|
const { action: apiAction } = await inquirer.prompt({
|
|
2116
3116
|
type: "list",
|
|
2117
3117
|
name: "action",
|
|
2118
|
-
message: i18n.selectApiAction,
|
|
2119
|
-
choices: [
|
|
2120
|
-
{ name: i18n.keepExistingConfig, value: "keep" },
|
|
2121
|
-
{ name: i18n.modifyAllConfig, value: "modify-all" },
|
|
2122
|
-
{ name: i18n.modifyPartialConfig, value: "modify-partial" },
|
|
2123
|
-
{ name: i18n.
|
|
2124
|
-
|
|
3118
|
+
message: i18n.api.selectApiAction,
|
|
3119
|
+
choices: addNumbersToChoices([
|
|
3120
|
+
{ name: i18n.api.keepExistingConfig, value: "keep" },
|
|
3121
|
+
{ name: i18n.api.modifyAllConfig, value: "modify-all" },
|
|
3122
|
+
{ name: i18n.api.modifyPartialConfig, value: "modify-partial" },
|
|
3123
|
+
{ name: i18n.api.useCcrProxy, value: "use-ccr" },
|
|
3124
|
+
{ name: i18n.api.skipApi, value: "skip" }
|
|
3125
|
+
])
|
|
2125
3126
|
});
|
|
2126
3127
|
if (!apiAction) {
|
|
2127
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
3128
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2128
3129
|
process.exit(0);
|
|
2129
3130
|
}
|
|
2130
3131
|
if (apiAction === "keep" || apiAction === "skip") {
|
|
2131
3132
|
apiConfig = null;
|
|
3133
|
+
if (apiAction === "keep") {
|
|
3134
|
+
try {
|
|
3135
|
+
addCompletedOnboarding();
|
|
3136
|
+
} catch (error) {
|
|
3137
|
+
console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
2132
3140
|
} else if (apiAction === "modify-partial") {
|
|
2133
3141
|
await modifyApiConfigPartially(existingApiConfig, i18n, scriptLang);
|
|
2134
3142
|
apiConfig = null;
|
|
2135
3143
|
} else if (apiAction === "modify-all") {
|
|
2136
3144
|
apiConfig = await configureApiCompletely(i18n, scriptLang);
|
|
3145
|
+
} else if (apiAction === "use-ccr") {
|
|
3146
|
+
const ccrInstalled = await isCcrInstalled();
|
|
3147
|
+
if (!ccrInstalled) {
|
|
3148
|
+
console.log(ansis.yellow(`${i18n.ccr.installingCcr}`));
|
|
3149
|
+
await installCcr(scriptLang);
|
|
3150
|
+
} else {
|
|
3151
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
3152
|
+
}
|
|
3153
|
+
const ccrConfigured = await setupCcrConfiguration(scriptLang);
|
|
3154
|
+
if (ccrConfigured) {
|
|
3155
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
|
|
3156
|
+
apiConfig = null;
|
|
3157
|
+
}
|
|
2137
3158
|
}
|
|
2138
3159
|
} else {
|
|
2139
3160
|
const { apiChoice } = await inquirer.prompt({
|
|
2140
3161
|
type: "list",
|
|
2141
3162
|
name: "apiChoice",
|
|
2142
|
-
message: i18n.configureApi,
|
|
3163
|
+
message: i18n.api.configureApi,
|
|
2143
3164
|
choices: [
|
|
2144
3165
|
{
|
|
2145
|
-
name: `${i18n.useAuthToken} - ${ansis.gray(i18n.authTokenDesc)}`,
|
|
3166
|
+
name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
|
|
2146
3167
|
value: "auth_token",
|
|
2147
|
-
short: i18n.useAuthToken
|
|
3168
|
+
short: i18n.api.useAuthToken
|
|
2148
3169
|
},
|
|
2149
3170
|
{
|
|
2150
|
-
name: `${i18n.useApiKey} - ${ansis.gray(i18n.apiKeyDesc)}`,
|
|
3171
|
+
name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
|
|
2151
3172
|
value: "api_key",
|
|
2152
|
-
short: i18n.useApiKey
|
|
3173
|
+
short: i18n.api.useApiKey
|
|
2153
3174
|
},
|
|
2154
3175
|
{
|
|
2155
|
-
name: i18n.
|
|
3176
|
+
name: `${i18n.api.useCcrProxy} - ${ansis.gray(i18n.api.ccrProxyDesc)}`,
|
|
3177
|
+
value: "ccr_proxy",
|
|
3178
|
+
short: i18n.api.useCcrProxy
|
|
3179
|
+
},
|
|
3180
|
+
{
|
|
3181
|
+
name: i18n.api.skipApi,
|
|
2156
3182
|
value: "skip"
|
|
2157
3183
|
}
|
|
2158
3184
|
]
|
|
2159
3185
|
});
|
|
2160
3186
|
if (!apiChoice) {
|
|
2161
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
3187
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2162
3188
|
process.exit(0);
|
|
2163
3189
|
}
|
|
2164
|
-
if (apiChoice
|
|
3190
|
+
if (apiChoice === "ccr_proxy") {
|
|
3191
|
+
const ccrInstalled = await isCcrInstalled();
|
|
3192
|
+
if (!ccrInstalled) {
|
|
3193
|
+
await installCcr(scriptLang);
|
|
3194
|
+
} else {
|
|
3195
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
3196
|
+
}
|
|
3197
|
+
const ccrConfigured = await setupCcrConfiguration(scriptLang);
|
|
3198
|
+
if (ccrConfigured) {
|
|
3199
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
|
|
3200
|
+
apiConfig = null;
|
|
3201
|
+
}
|
|
3202
|
+
} else if (apiChoice !== "skip") {
|
|
2165
3203
|
apiConfig = await configureApiCompletely(i18n, scriptLang, apiChoice);
|
|
2166
3204
|
}
|
|
2167
3205
|
}
|
|
@@ -2169,7 +3207,7 @@ async function init(options = {}) {
|
|
|
2169
3207
|
if (["backup", "docs-only", "merge"].includes(action)) {
|
|
2170
3208
|
const backupDir = backupExistingConfig();
|
|
2171
3209
|
if (backupDir) {
|
|
2172
|
-
console.log(ansis.gray(`\u2714 ${i18n.backupSuccess}: ${backupDir}`));
|
|
3210
|
+
console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
|
|
2173
3211
|
}
|
|
2174
3212
|
}
|
|
2175
3213
|
if (action === "docs-only") {
|
|
@@ -2184,30 +3222,25 @@ async function init(options = {}) {
|
|
|
2184
3222
|
if (apiConfig && action !== "docs-only") {
|
|
2185
3223
|
const configuredApi = configureApi(apiConfig);
|
|
2186
3224
|
if (configuredApi) {
|
|
2187
|
-
console.log(ansis.green(`\u2714 ${i18n.apiConfigSuccess}`));
|
|
3225
|
+
console.log(ansis.green(`\u2714 ${i18n.api.apiConfigSuccess}`));
|
|
2188
3226
|
console.log(ansis.gray(` URL: ${configuredApi.url}`));
|
|
2189
3227
|
console.log(ansis.gray(` Key: ${formatApiKeyDisplay(configuredApi.key)}`));
|
|
2190
|
-
try {
|
|
2191
|
-
addCompletedOnboarding();
|
|
2192
|
-
} catch (error) {
|
|
2193
|
-
console.error(ansis.red(i18n.failedToSetOnboarding), error);
|
|
2194
|
-
}
|
|
2195
3228
|
}
|
|
2196
3229
|
}
|
|
2197
3230
|
if (action !== "docs-only") {
|
|
2198
3231
|
const { shouldConfigureMcp } = await inquirer.prompt({
|
|
2199
3232
|
type: "confirm",
|
|
2200
3233
|
name: "shouldConfigureMcp",
|
|
2201
|
-
message: i18n.configureMcp,
|
|
3234
|
+
message: i18n.mcp.configureMcp,
|
|
2202
3235
|
default: true
|
|
2203
3236
|
});
|
|
2204
3237
|
if (shouldConfigureMcp === void 0) {
|
|
2205
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
3238
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2206
3239
|
process.exit(0);
|
|
2207
3240
|
}
|
|
2208
3241
|
if (shouldConfigureMcp) {
|
|
2209
3242
|
if (isWindows()) {
|
|
2210
|
-
console.log(ansis.blue(`\u2139 ${
|
|
3243
|
+
console.log(ansis.blue(`\u2139 ${i18n.installation.windowsDetected}`));
|
|
2211
3244
|
}
|
|
2212
3245
|
const selectedServices = await selectMcpServices(scriptLang);
|
|
2213
3246
|
if (selectedServices === void 0) {
|
|
@@ -2216,7 +3249,7 @@ async function init(options = {}) {
|
|
|
2216
3249
|
if (selectedServices.length > 0) {
|
|
2217
3250
|
const mcpBackupPath = backupMcpConfig();
|
|
2218
3251
|
if (mcpBackupPath) {
|
|
2219
|
-
console.log(ansis.gray(`\u2714 ${i18n.mcpBackupSuccess}: ${mcpBackupPath}`));
|
|
3252
|
+
console.log(ansis.gray(`\u2714 ${i18n.mcp.mcpBackupSuccess}: ${mcpBackupPath}`));
|
|
2220
3253
|
}
|
|
2221
3254
|
const newServers = {};
|
|
2222
3255
|
for (const serviceId of selectedServices) {
|
|
@@ -2228,10 +3261,10 @@ async function init(options = {}) {
|
|
|
2228
3261
|
type: "input",
|
|
2229
3262
|
name: "apiKey",
|
|
2230
3263
|
message: service.apiKeyPrompt[scriptLang],
|
|
2231
|
-
validate: (value) => !!value || i18n.keyRequired
|
|
3264
|
+
validate: (value) => !!value || i18n.api.keyRequired
|
|
2232
3265
|
});
|
|
2233
3266
|
if (apiKey === void 0) {
|
|
2234
|
-
console.log(ansis.yellow(`${i18n.skip}: ${service.name[scriptLang]}`));
|
|
3267
|
+
console.log(ansis.yellow(`${i18n.common.skip}: ${service.name[scriptLang]}`));
|
|
2235
3268
|
continue;
|
|
2236
3269
|
}
|
|
2237
3270
|
if (apiKey) {
|
|
@@ -2247,9 +3280,9 @@ async function init(options = {}) {
|
|
|
2247
3280
|
mergedConfig = fixWindowsMcpConfig(mergedConfig);
|
|
2248
3281
|
try {
|
|
2249
3282
|
writeMcpConfig(mergedConfig);
|
|
2250
|
-
console.log(ansis.green(`\u2714 ${i18n.mcpConfigSuccess}`));
|
|
3283
|
+
console.log(ansis.green(`\u2714 ${i18n.mcp.mcpConfigSuccess}`));
|
|
2251
3284
|
} catch (error) {
|
|
2252
|
-
console.error(ansis.red(`${i18n.failedToWriteMcpConfig} ${error}`));
|
|
3285
|
+
console.error(ansis.red(`${i18n.configuration.failedToWriteMcpConfig} ${error}`));
|
|
2253
3286
|
}
|
|
2254
3287
|
}
|
|
2255
3288
|
}
|
|
@@ -2259,8 +3292,8 @@ async function init(options = {}) {
|
|
|
2259
3292
|
preferredLang: scriptLang,
|
|
2260
3293
|
aiOutputLang
|
|
2261
3294
|
});
|
|
2262
|
-
console.log(ansis.green(`\u2714 ${i18n.configSuccess} ${CLAUDE_DIR}`));
|
|
2263
|
-
console.log("\n" + ansis.cyan(i18n.complete));
|
|
3295
|
+
console.log(ansis.green(`\u2714 ${i18n.configuration.configSuccess} ${CLAUDE_DIR}`));
|
|
3296
|
+
console.log("\n" + ansis.cyan(i18n.common.complete));
|
|
2264
3297
|
} catch (error) {
|
|
2265
3298
|
if (!handleExitPromptError(error)) {
|
|
2266
3299
|
handleGeneralError(error, options.lang);
|
|
@@ -2346,4 +3379,4 @@ async function openSettingsJson() {
|
|
|
2346
3379
|
}
|
|
2347
3380
|
}
|
|
2348
3381
|
|
|
2349
|
-
export {
|
|
3382
|
+
export { readCcrConfig as $, AI_OUTPUT_LANGUAGES as A, writeMcpConfig as B, CLAUDE_DIR as C, backupMcpConfig as D, mergeMcpServers as E, buildMcpServerConfig as F, fixWindowsMcpConfig as G, addCompletedOnboarding as H, I18N as I, getTranslation as J, addNumbersToChoices as K, LEGACY_ZCF_CONFIG_FILE as L, MCP_SERVICES as M, updateZcfConfig as N, readZcfConfig as O, configureAiPersonality as P, isWindows as Q, selectMcpServices as R, SETTINGS_FILE as S, formatApiKeyDisplay as T, modifyApiConfigPartially as U, isCcrInstalled as V, installCcr as W, setupCcrConfiguration as X, validateApiKey as Y, ZCF_CONFIG_FILE as Z, readZcfConfigAsync as _, importRecommendedEnv as a, configureCcrFeature as a0, handleExitPromptError as a1, handleGeneralError as a2, displayBanner as a3, selectScriptLanguage as a4, resolveAiOutputLanguage as a5, updatePromptOnly as a6, selectAndInstallWorkflows as a7, version as a8, displayBannerWithInfo as a9, checkAndUpdateTools as aa, prompts as ab, importRecommendedPermissions as b, commandExists as c, cleanupPermissions as d, CLAUDE_MD_FILE as e, ClAUDE_CONFIG_FILE as f, getPlatform as g, SUPPORTED_LANGS as h, init as i, LANG_LABELS as j, isClaudeCodeInstalled as k, installClaudeCode as l, mergeAndCleanPermissions as m, ensureClaudeDir as n, openSettingsJson as o, backupExistingConfig as p, copyConfigFiles as q, configureApi as r, mergeConfigs as s, mergeSettingsFile as t, updateDefaultModel as u, getExistingModelConfig as v, getExistingApiConfig as w, applyAiLanguageDirective as x, getMcpConfigPath as y, readMcpConfig as z };
|