zcf 2.7.0 → 2.8.0
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 +44 -16
- package/bin/zcf.mjs +1 -1
- package/dist/cli.mjs +413 -129
- package/dist/index.d.mts +15 -238
- package/dist/index.d.ts +15 -238
- package/dist/index.mjs +4 -1
- package/dist/shared/{zcf.DgEuqnSI.mjs → zcf.CLtDS8Pu.mjs} +1077 -345
- package/package.json +1 -1
|
@@ -6,9 +6,12 @@ import dayjs from 'dayjs';
|
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { exec } from 'tinyexec';
|
|
8
8
|
import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
|
|
9
|
+
import { exec as exec$1 } from 'node:child_process';
|
|
10
|
+
import { promisify } from 'node:util';
|
|
9
11
|
import { homedir, platform } from 'node:os';
|
|
12
|
+
import { join as join$1 } from 'node:path';
|
|
10
13
|
|
|
11
|
-
const version = "2.
|
|
14
|
+
const version = "2.8.0";
|
|
12
15
|
const homepage = "https://github.com/UfoMiao/zcf";
|
|
13
16
|
|
|
14
17
|
const common$1 = {
|
|
@@ -23,7 +26,7 @@ const common$1 = {
|
|
|
23
26
|
none: "\u65E0",
|
|
24
27
|
notConfigured: "\u672A\u914D\u7F6E",
|
|
25
28
|
spaceToSelectReturn: "- \u7A7A\u683C\u9009\u62E9\uFF0C\u56DE\u8F66\u63D0\u4EA4",
|
|
26
|
-
enterChoice: "\u8BF7\u8F93\u5165\u9009\u9879\uFF0C\u56DE\u8F66\u786E\u8BA4",
|
|
29
|
+
enterChoice: "\u8BF7\u8F93\u5165\u9009\u9879\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF08\u4E0D\u533A\u5206\u5927\u5C0F\u5199\uFF09",
|
|
27
30
|
invalidChoice: "\u65E0\u6548\u9009\u62E9\u3002\u8BF7\u8F93\u5165\u6709\u6548\u9009\u9879\u3002",
|
|
28
31
|
goodbye: "\u{1F44B} \u611F\u8C22\u4F7F\u7528 ZCF\uFF01\u518D\u89C1\uFF01",
|
|
29
32
|
returnToMenu: "\u8FD4\u56DE\u4E3B\u83DC\u5355\uFF1F",
|
|
@@ -52,6 +55,7 @@ const installation$1 = {
|
|
|
52
55
|
installPrompt: "\u68C0\u6D4B\u5230 Claude Code \u672A\u5B89\u88C5\uFF0C\u662F\u5426\u81EA\u52A8\u5B89\u88C5\uFF1F",
|
|
53
56
|
installing: "\u6B63\u5728\u5B89\u88C5 Claude Code...",
|
|
54
57
|
installSuccess: "Claude Code \u5B89\u88C5\u6210\u529F",
|
|
58
|
+
alreadyInstalled: "Claude Code \u5DF2\u5B89\u88C5",
|
|
55
59
|
installFailed: "Claude Code \u5B89\u88C5\u5931\u8D25",
|
|
56
60
|
npmNotFound: "npm \u672A\u5B89\u88C5\u3002\u8BF7\u5148\u5B89\u88C5 Node.js \u548C npm\u3002",
|
|
57
61
|
// Termux specific
|
|
@@ -70,6 +74,8 @@ const api$1 = {
|
|
|
70
74
|
authTokenDesc: "\u9002\u7528\u4E8E\u901A\u8FC7 OAuth \u6216\u6D4F\u89C8\u5668\u767B\u5F55\u83B7\u53D6\u7684\u4EE4\u724C",
|
|
71
75
|
useApiKey: "\u4F7F\u7528 API Key (\u5BC6\u94A5\u8BA4\u8BC1)",
|
|
72
76
|
apiKeyDesc: "\u9002\u7528\u4E8E\u4ECE Anthropic Console \u83B7\u53D6\u7684 API \u5BC6\u94A5",
|
|
77
|
+
useCcrProxy: "\u4F7F\u7528 CCR \u4EE3\u7406",
|
|
78
|
+
ccrProxyDesc: "\u901A\u8FC7 Claude Code Router \u4F7F\u7528\u591A\u4E2A AI \u6A21\u578B",
|
|
73
79
|
skipApi: "\u8DF3\u8FC7\uFF08\u7A0D\u540E\u624B\u52A8\u914D\u7F6E\uFF09",
|
|
74
80
|
enterApiUrl: "\u8BF7\u8F93\u5165 API URL",
|
|
75
81
|
enterAuthToken: "\u8BF7\u8F93\u5165 Auth Token",
|
|
@@ -147,7 +153,17 @@ const configuration$1 = {
|
|
|
147
153
|
openSettingsJsonDesc: "\u9AD8\u7EA7\u7528\u6237\u81EA\u5B9A\u4E49",
|
|
148
154
|
envImportSuccess: "\u73AF\u5883\u53D8\u91CF\u5DF2\u5BFC\u5165",
|
|
149
155
|
permissionsImportSuccess: "\u6743\u9650\u914D\u7F6E\u5DF2\u5BFC\u5165",
|
|
150
|
-
openingSettingsJson: "\u6B63\u5728\u6253\u5F00 settings.json..."
|
|
156
|
+
openingSettingsJson: "\u6B63\u5728\u6253\u5F00 settings.json...",
|
|
157
|
+
// JSON config related
|
|
158
|
+
invalidConfiguration: "\u914D\u7F6E\u65E0\u6548",
|
|
159
|
+
failedToParseJson: "\u89E3\u6790 JSON \u6587\u4EF6\u5931\u8D25:",
|
|
160
|
+
failedToBackupConfig: "\u5907\u4EFD\u914D\u7F6E\u5931\u8D25",
|
|
161
|
+
failedToReadTemplateSettings: "\u8BFB\u53D6\u6A21\u677F\u8BBE\u7F6E\u5931\u8D25",
|
|
162
|
+
failedToMergeSettings: "\u5408\u5E76\u8BBE\u7F6E\u5931\u8D25",
|
|
163
|
+
preservingExistingSettings: "\u4FDD\u7559\u73B0\u6709\u8BBE\u7F6E",
|
|
164
|
+
memoryDirNotFound: "\u672A\u627E\u5230\u8BB0\u5FC6\u76EE\u5F55",
|
|
165
|
+
failedToSetOnboarding: "\u8BBE\u7F6E\u5F15\u5BFC\u6807\u5FD7\u5931\u8D25",
|
|
166
|
+
fixWindowsMcp: "\u4FEE\u590D Windows MCP \u914D\u7F6E\uFF1F"
|
|
151
167
|
};
|
|
152
168
|
|
|
153
169
|
const mcp$1 = {
|
|
@@ -174,11 +190,14 @@ const menu$1 = {
|
|
|
174
190
|
menuOptions: {
|
|
175
191
|
fullInit: "\u5B8C\u6574\u521D\u59CB\u5316",
|
|
176
192
|
importWorkflow: "\u5BFC\u5165\u5DE5\u4F5C\u6D41",
|
|
193
|
+
configureApiOrCcr: "\u914D\u7F6E API \u6216 CCR \u4EE3\u7406",
|
|
177
194
|
configureApi: "\u914D\u7F6E API",
|
|
178
195
|
configureMcp: "\u914D\u7F6E MCP",
|
|
179
196
|
configureModel: "\u914D\u7F6E\u9ED8\u8BA4\u6A21\u578B",
|
|
180
197
|
configureAiMemory: "\u914D\u7F6E Claude \u5168\u5C40\u8BB0\u5FC6",
|
|
181
198
|
configureEnvPermission: "\u5BFC\u5165\u63A8\u8350\u73AF\u5883\u53D8\u91CF\u548C\u6743\u9650\u914D\u7F6E",
|
|
199
|
+
configureCcr: "\u914D\u7F6E\u6A21\u578B\u4EE3\u7406 (CCR)",
|
|
200
|
+
ccrManagement: "CCR",
|
|
182
201
|
ccusage: "ccusage",
|
|
183
202
|
installBmad: "\u5B89\u88C5 BMad Method",
|
|
184
203
|
clearCache: "\u6E05\u9664\u504F\u597D\u7F13\u5B58",
|
|
@@ -186,13 +205,16 @@ const menu$1 = {
|
|
|
186
205
|
exit: "\u9000\u51FA"
|
|
187
206
|
},
|
|
188
207
|
menuDescriptions: {
|
|
189
|
-
fullInit: "\u5B89\u88C5 Claude Code + \u5BFC\u5165\u5DE5\u4F5C\u6D41 + \u914D\u7F6E API + \u914D\u7F6E MCP \u670D\u52A1",
|
|
208
|
+
fullInit: "\u5B89\u88C5 Claude Code + \u5BFC\u5165\u5DE5\u4F5C\u6D41 + \u914D\u7F6E API \u6216 CCR \u4EE3\u7406 + \u914D\u7F6E MCP \u670D\u52A1",
|
|
190
209
|
importWorkflow: "\u4EC5\u5BFC\u5165/\u66F4\u65B0\u5DE5\u4F5C\u6D41\u76F8\u5173\u6587\u4EF6",
|
|
210
|
+
configureApiOrCcr: "\u914D\u7F6E API URL\u3001\u8BA4\u8BC1\u4FE1\u606F\u6216 CCR \u4EE3\u7406",
|
|
191
211
|
configureApi: "\u914D\u7F6E API URL \u548C\u8BA4\u8BC1\u4FE1\u606F",
|
|
192
212
|
configureMcp: "\u914D\u7F6E MCP \u670D\u52A1\uFF08\u542B Windows \u4FEE\u590D\uFF09",
|
|
193
213
|
configureModel: "\u8BBE\u7F6E\u9ED8\u8BA4\u6A21\u578B\uFF08opus/sonnet\uFF09",
|
|
194
214
|
configureAiMemory: "\u914D\u7F6E AI \u8F93\u51FA\u8BED\u8A00\u548C\u89D2\u8272\u98CE\u683C",
|
|
195
215
|
configureEnvPermission: "\u5BFC\u5165\u9690\u79C1\u4FDD\u62A4\u73AF\u5883\u53D8\u91CF\u548C\u7CFB\u7EDF\u6743\u9650\u914D\u7F6E",
|
|
216
|
+
configureCcr: "\u914D\u7F6E Claude Code Router \u4EE5\u4F7F\u7528\u591A\u4E2A AI \u6A21\u578B",
|
|
217
|
+
ccrManagement: "\u914D\u7F6E Claude Code Router \u4EE5\u4F7F\u7528\u591A\u4E2A AI \u6A21\u578B",
|
|
196
218
|
ccusage: "Claude Code \u7528\u91CF\u5206\u6790",
|
|
197
219
|
installBmad: "AI \u9A71\u52A8\u7684\u5F00\u53D1\u65B9\u6CD5\u8BBA\u6846\u67B6",
|
|
198
220
|
clearCache: "\u6E05\u9664\u504F\u597D\u8BED\u8A00\u7B49\u7F13\u5B58",
|
|
@@ -326,19 +348,102 @@ const tools$1 = {
|
|
|
326
348
|
errorDetails: "\u9519\u8BEF\u8BE6\u60C5:"
|
|
327
349
|
};
|
|
328
350
|
|
|
351
|
+
const ccrMessages$1 = {
|
|
352
|
+
// Installation
|
|
353
|
+
installingCcr: "\u6B63\u5728\u5B89\u88C5 Claude Code Router...",
|
|
354
|
+
ccrInstallSuccess: "Claude Code Router \u5B89\u88C5\u6210\u529F",
|
|
355
|
+
ccrInstallFailed: "\u5B89\u88C5 Claude Code Router \u5931\u8D25",
|
|
356
|
+
ccrAlreadyInstalled: "Claude Code Router \u5DF2\u5B89\u88C5",
|
|
357
|
+
// Configuration
|
|
358
|
+
configureCcr: "\u914D\u7F6E\u6A21\u578B\u4EE3\u7406 (CCR)",
|
|
359
|
+
useCcrProxy: "\u4F7F\u7528 CCR \u4EE3\u7406",
|
|
360
|
+
ccrProxyDesc: "\u901A\u8FC7 Claude Code Router \u8FDE\u63A5\u591A\u4E2A AI \u6A21\u578B",
|
|
361
|
+
fetchingPresets: "\u6B63\u5728\u83B7\u53D6\u63D0\u4F9B\u5546\u9884\u8BBE...",
|
|
362
|
+
noPresetsAvailable: "\u6CA1\u6709\u53EF\u7528\u7684\u9884\u8BBE",
|
|
363
|
+
selectCcrPreset: "\u9009\u62E9\u4E00\u4E2A\u63D0\u4F9B\u5546\u9884\u8BBE\uFF1A",
|
|
364
|
+
keyRequired: "API \u5BC6\u94A5\u4E0D\u80FD\u4E3A\u7A7A",
|
|
365
|
+
// Existing config
|
|
366
|
+
existingCcrConfig: "\u53D1\u73B0\u73B0\u6709\u7684 CCR \u914D\u7F6E",
|
|
367
|
+
overwriteCcrConfig: "\u662F\u5426\u5907\u4EFD\u73B0\u6709\u7684 CCR \u914D\u7F6E\u5E76\u91CD\u65B0\u914D\u7F6E\uFF1F",
|
|
368
|
+
keepingExistingConfig: "\u4FDD\u7559\u73B0\u6709\u914D\u7F6E",
|
|
369
|
+
backupCcrConfig: "\u6B63\u5728\u5907\u4EFD\u73B0\u6709\u7684 CCR \u914D\u7F6E...",
|
|
370
|
+
ccrBackupSuccess: "CCR \u914D\u7F6E\u5DF2\u5907\u4EFD\u5230\uFF1A{path}",
|
|
371
|
+
ccrBackupFailed: "\u5907\u4EFD CCR \u914D\u7F6E\u5931\u8D25",
|
|
372
|
+
// Model selection
|
|
373
|
+
selectDefaultModelForProvider: "\u9009\u62E9 {provider} \u7684\u9ED8\u8BA4\u6A21\u578B\uFF1A",
|
|
374
|
+
enterApiKeyForProvider: "\u8BF7\u8F93\u5165 {provider} \u7684 API \u5BC6\u94A5\uFF1A",
|
|
375
|
+
// Skip option
|
|
376
|
+
skipOption: "\u8DF3\u8FC7\uFF0C\u5728 CCR \u4E2D\u81EA\u884C\u914D\u7F6E",
|
|
377
|
+
skipConfiguring: "\u8DF3\u8FC7\u9884\u8BBE\u914D\u7F6E\uFF0C\u5C06\u521B\u5EFA\u7A7A\u914D\u7F6E\u6846\u67B6",
|
|
378
|
+
// Success/Error messages
|
|
379
|
+
ccrConfigSuccess: "CCR \u914D\u7F6E\u5DF2\u4FDD\u5B58",
|
|
380
|
+
proxyConfigSuccess: "\u4EE3\u7406\u8BBE\u7F6E\u5DF2\u914D\u7F6E",
|
|
381
|
+
ccrConfigFailed: "\u914D\u7F6E CCR \u5931\u8D25",
|
|
382
|
+
ccrSetupComplete: "CCR \u8BBE\u7F6E\u5B8C\u6210",
|
|
383
|
+
fetchPresetsError: "\u83B7\u53D6\u63D0\u4F9B\u5546\u9884\u8BBE\u5931\u8D25",
|
|
384
|
+
failedToStartCcrService: "\u542F\u52A8 CCR \u670D\u52A1\u5931\u8D25",
|
|
385
|
+
errorStartingCcrService: "\u542F\u52A8 CCR \u670D\u52A1\u65F6\u51FA\u9519",
|
|
386
|
+
// CCR service status
|
|
387
|
+
ccrRestartSuccess: "CCR \u670D\u52A1\u5DF2\u91CD\u542F",
|
|
388
|
+
ccrRestartFailed: "CCR \u670D\u52A1\u91CD\u542F\u5931\u8D25",
|
|
389
|
+
// Configuration tips
|
|
390
|
+
configTips: "\u914D\u7F6E\u63D0\u793A",
|
|
391
|
+
useClaudeCommand: "\u8BF7\u4F7F\u7528 claude \u547D\u4EE4\u542F\u52A8 Claude Code\uFF08\u800C\u975E ccr code\uFF09",
|
|
392
|
+
advancedConfigTip: "\u60A8\u53EF\u4EE5\u4F7F\u7528 ccr ui \u547D\u4EE4\u8FDB\u884C\u66F4\u9AD8\u7EA7\u7684\u914D\u7F6E",
|
|
393
|
+
manualConfigTip: "\u624B\u52A8\u4FEE\u6539\u914D\u7F6E\u6587\u4EF6\u540E\uFF0C\u8BF7\u6267\u884C ccr restart \u4F7F\u914D\u7F6E\u751F\u6548",
|
|
394
|
+
// CCR Menu
|
|
395
|
+
ccrMenuTitle: "CCR - Claude Code Router \u7BA1\u7406",
|
|
396
|
+
ccrMenuOptions: {
|
|
397
|
+
initCcr: "\u521D\u59CB\u5316 CCR",
|
|
398
|
+
startUi: "\u542F\u52A8 CCR UI",
|
|
399
|
+
checkStatus: "\u67E5\u8BE2 CCR \u72B6\u6001",
|
|
400
|
+
restart: "\u91CD\u542F CCR",
|
|
401
|
+
start: "\u542F\u52A8 CCR",
|
|
402
|
+
stop: "\u505C\u6B62 CCR",
|
|
403
|
+
back: "\u8FD4\u56DE\u4E3B\u83DC\u5355"
|
|
404
|
+
},
|
|
405
|
+
ccrMenuDescriptions: {
|
|
406
|
+
initCcr: "\u5B89\u88C5\u5E76\u914D\u7F6E CCR",
|
|
407
|
+
startUi: "\u6253\u5F00 Web \u754C\u9762\u7BA1\u7406 CCR",
|
|
408
|
+
checkStatus: "\u67E5\u770B CCR \u670D\u52A1\u8FD0\u884C\u72B6\u6001",
|
|
409
|
+
restart: "\u91CD\u542F CCR \u670D\u52A1",
|
|
410
|
+
start: "\u542F\u52A8 CCR \u670D\u52A1",
|
|
411
|
+
stop: "\u505C\u6B62 CCR \u670D\u52A1"
|
|
412
|
+
},
|
|
413
|
+
// Command execution messages
|
|
414
|
+
startingCcrUi: "\u6B63\u5728\u542F\u52A8 CCR UI...",
|
|
415
|
+
ccrUiStarted: "CCR UI \u5DF2\u542F\u52A8",
|
|
416
|
+
checkingCcrStatus: "\u6B63\u5728\u67E5\u8BE2 CCR \u72B6\u6001...",
|
|
417
|
+
ccrStatusTitle: "CCR \u72B6\u6001\u4FE1\u606F\uFF1A",
|
|
418
|
+
restartingCcr: "\u6B63\u5728\u91CD\u542F CCR...",
|
|
419
|
+
ccrRestarted: "CCR \u5DF2\u91CD\u542F",
|
|
420
|
+
startingCcr: "\u6B63\u5728\u542F\u52A8 CCR...",
|
|
421
|
+
ccrStarted: "CCR \u5DF2\u542F\u52A8",
|
|
422
|
+
stoppingCcr: "\u6B63\u5728\u505C\u6B62 CCR...",
|
|
423
|
+
ccrStopped: "CCR \u5DF2\u505C\u6B62",
|
|
424
|
+
ccrCommandFailed: "\u6267\u884C CCR \u547D\u4EE4\u5931\u8D25",
|
|
425
|
+
// Configuration check messages
|
|
426
|
+
ccrNotConfigured: "CCR \u5C1A\u672A\u914D\u7F6E\u3002\u8BF7\u5148\u521D\u59CB\u5316 CCR\u3002",
|
|
427
|
+
pleaseInitFirst: "\u8BF7\u9009\u62E9\u9009\u9879 1 \u6765\u521D\u59CB\u5316 CCR\u3002",
|
|
428
|
+
// UI API Key messages
|
|
429
|
+
ccrUiApiKey: "CCR UI \u767B\u5F55\u5BC6\u94A5",
|
|
430
|
+
ccrUiApiKeyHint: "\u4F7F\u7528\u6B64\u5BC6\u94A5\u767B\u5F55 CCR UI \u754C\u9762"
|
|
431
|
+
};
|
|
432
|
+
|
|
329
433
|
const zhCN = {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
434
|
+
common: common$1,
|
|
435
|
+
language: language$1,
|
|
436
|
+
installation: installation$1,
|
|
437
|
+
api: api$1,
|
|
438
|
+
configuration: configuration$1,
|
|
439
|
+
mcp: mcp$1,
|
|
440
|
+
menu: menu$1,
|
|
441
|
+
workflow: workflow$1,
|
|
442
|
+
cli: cli$1,
|
|
443
|
+
bmad: bmad$1,
|
|
444
|
+
errors: errors$1,
|
|
445
|
+
tools: tools$1,
|
|
446
|
+
ccr: ccrMessages$1
|
|
342
447
|
};
|
|
343
448
|
|
|
344
449
|
const common = {
|
|
@@ -353,7 +458,7 @@ const common = {
|
|
|
353
458
|
none: "None",
|
|
354
459
|
notConfigured: "Not configured",
|
|
355
460
|
spaceToSelectReturn: "- Space to select. Return to submit",
|
|
356
|
-
enterChoice: "Enter your choice
|
|
461
|
+
enterChoice: "Enter your choice and press enter (case-insensitive)",
|
|
357
462
|
invalidChoice: "Invalid choice. Please enter a valid option.",
|
|
358
463
|
goodbye: "\u{1F44B} Thanks for using ZCF! Goodbye!",
|
|
359
464
|
returnToMenu: "Return to main menu?",
|
|
@@ -382,6 +487,7 @@ const installation = {
|
|
|
382
487
|
installPrompt: "Claude Code not found. Install automatically?",
|
|
383
488
|
installing: "Installing Claude Code...",
|
|
384
489
|
installSuccess: "Claude Code installed successfully",
|
|
490
|
+
alreadyInstalled: "Claude Code is already installed",
|
|
385
491
|
installFailed: "Failed to install Claude Code",
|
|
386
492
|
npmNotFound: "npm is not installed. Please install Node.js and npm first.",
|
|
387
493
|
// Termux specific
|
|
@@ -400,6 +506,8 @@ const api = {
|
|
|
400
506
|
authTokenDesc: "For tokens obtained via OAuth or browser login",
|
|
401
507
|
useApiKey: "Use API Key (Key authentication)",
|
|
402
508
|
apiKeyDesc: "For API keys from Anthropic Console",
|
|
509
|
+
useCcrProxy: "Use CCR Proxy",
|
|
510
|
+
ccrProxyDesc: "Use multiple AI models via Claude Code Router",
|
|
403
511
|
skipApi: "Skip (configure manually later)",
|
|
404
512
|
enterApiUrl: "Enter API URL",
|
|
405
513
|
enterAuthToken: "Enter Auth Token",
|
|
@@ -477,7 +585,17 @@ const configuration = {
|
|
|
477
585
|
openSettingsJsonDesc: "Advanced user customization",
|
|
478
586
|
envImportSuccess: "Environment variables imported",
|
|
479
587
|
permissionsImportSuccess: "Permissions imported",
|
|
480
|
-
openingSettingsJson: "Opening settings.json..."
|
|
588
|
+
openingSettingsJson: "Opening settings.json...",
|
|
589
|
+
// JSON config related
|
|
590
|
+
invalidConfiguration: "Invalid configuration",
|
|
591
|
+
failedToParseJson: "Failed to parse JSON file:",
|
|
592
|
+
failedToBackupConfig: "Failed to backup config",
|
|
593
|
+
failedToReadTemplateSettings: "Failed to read template settings",
|
|
594
|
+
failedToMergeSettings: "Failed to merge settings",
|
|
595
|
+
preservingExistingSettings: "Preserving existing settings",
|
|
596
|
+
memoryDirNotFound: "Memory directory not found",
|
|
597
|
+
failedToSetOnboarding: "Failed to set onboarding flag",
|
|
598
|
+
fixWindowsMcp: "Fix Windows MCP configuration?"
|
|
481
599
|
};
|
|
482
600
|
|
|
483
601
|
const mcp = {
|
|
@@ -504,11 +622,14 @@ const menu = {
|
|
|
504
622
|
menuOptions: {
|
|
505
623
|
fullInit: "Full initialization",
|
|
506
624
|
importWorkflow: "Import workflow",
|
|
625
|
+
configureApiOrCcr: "Configure API / CCR proxy",
|
|
507
626
|
configureApi: "Configure API",
|
|
508
627
|
configureMcp: "Configure MCP",
|
|
509
628
|
configureModel: "Configure default model",
|
|
510
629
|
configureAiMemory: "Configure Claude global memory",
|
|
511
630
|
configureEnvPermission: "Import recommended environment variables and permissions",
|
|
631
|
+
configureCcr: "Configure Model Proxy (CCR)",
|
|
632
|
+
ccrManagement: "CCR",
|
|
512
633
|
ccusage: "ccusage",
|
|
513
634
|
installBmad: "Install BMad Method",
|
|
514
635
|
clearCache: "Clear preference cache",
|
|
@@ -516,13 +637,16 @@ const menu = {
|
|
|
516
637
|
exit: "Exit"
|
|
517
638
|
},
|
|
518
639
|
menuDescriptions: {
|
|
519
|
-
fullInit: "Install Claude Code + Import workflow + Configure API + Configure MCP services",
|
|
640
|
+
fullInit: "Install Claude Code + Import workflow + Configure API or CCR proxy + Configure MCP services",
|
|
520
641
|
importWorkflow: "Import/update workflow-related files only",
|
|
642
|
+
configureApiOrCcr: "Configure API URL, authentication or CCR proxy",
|
|
521
643
|
configureApi: "Configure API URL and authentication",
|
|
522
644
|
configureMcp: "Configure MCP services (includes Windows fix)",
|
|
523
645
|
configureModel: "Set default model (opus/sonnet)",
|
|
524
646
|
configureAiMemory: "Configure AI output language and personality",
|
|
525
647
|
configureEnvPermission: "Import privacy protection environment variables and system permissions",
|
|
648
|
+
configureCcr: "Configure Claude Code Router to use multiple AI models",
|
|
649
|
+
ccrManagement: "Configure Claude Code Router to use multiple AI models",
|
|
526
650
|
ccusage: "Claude Code usage analysis",
|
|
527
651
|
installBmad: "AI-driven development methodology framework",
|
|
528
652
|
clearCache: "Clear preference language and other caches",
|
|
@@ -656,25 +780,111 @@ const tools = {
|
|
|
656
780
|
errorDetails: "Error details:"
|
|
657
781
|
};
|
|
658
782
|
|
|
783
|
+
const ccrMessages = {
|
|
784
|
+
// Installation
|
|
785
|
+
installingCcr: "Installing Claude Code Router...",
|
|
786
|
+
ccrInstallSuccess: "Claude Code Router installed successfully",
|
|
787
|
+
ccrInstallFailed: "Failed to install Claude Code Router",
|
|
788
|
+
ccrAlreadyInstalled: "Claude Code Router is already installed",
|
|
789
|
+
// Configuration
|
|
790
|
+
configureCcr: "Configure Model Proxy (CCR)",
|
|
791
|
+
useCcrProxy: "Use CCR Proxy",
|
|
792
|
+
ccrProxyDesc: "Connect to multiple AI models via Claude Code Router",
|
|
793
|
+
fetchingPresets: "Fetching provider presets...",
|
|
794
|
+
noPresetsAvailable: "No presets available",
|
|
795
|
+
selectCcrPreset: "Select a provider preset:",
|
|
796
|
+
keyRequired: "API key is required",
|
|
797
|
+
// Existing config
|
|
798
|
+
existingCcrConfig: "Existing CCR configuration found",
|
|
799
|
+
overwriteCcrConfig: "Backup existing CCR configuration and reconfigure?",
|
|
800
|
+
keepingExistingConfig: "Keeping existing configuration",
|
|
801
|
+
backupCcrConfig: "Backing up existing CCR configuration...",
|
|
802
|
+
ccrBackupSuccess: "CCR configuration backed up to: {path}",
|
|
803
|
+
ccrBackupFailed: "Failed to backup CCR configuration",
|
|
804
|
+
// Model selection
|
|
805
|
+
selectDefaultModelForProvider: "Select default model for {provider}:",
|
|
806
|
+
enterApiKeyForProvider: "Enter API key for {provider}:",
|
|
807
|
+
// Skip option
|
|
808
|
+
skipOption: "Skip, configure in CCR manually",
|
|
809
|
+
skipConfiguring: "Skipping preset configuration, will create empty configuration framework",
|
|
810
|
+
// Success/Error messages
|
|
811
|
+
ccrConfigSuccess: "CCR configuration saved",
|
|
812
|
+
proxyConfigSuccess: "Proxy settings configured",
|
|
813
|
+
ccrConfigFailed: "Failed to configure CCR",
|
|
814
|
+
ccrSetupComplete: "CCR setup complete",
|
|
815
|
+
fetchPresetsError: "Failed to fetch provider presets",
|
|
816
|
+
failedToStartCcrService: "Failed to start CCR service",
|
|
817
|
+
errorStartingCcrService: "Error starting CCR service",
|
|
818
|
+
// CCR service status
|
|
819
|
+
ccrRestartSuccess: "CCR service restarted",
|
|
820
|
+
ccrRestartFailed: "Failed to restart CCR service",
|
|
821
|
+
// Configuration tips
|
|
822
|
+
configTips: "Configuration Tips",
|
|
823
|
+
useClaudeCommand: "Use the claude command to start Claude Code (not ccr code)",
|
|
824
|
+
advancedConfigTip: "You can use the ccr ui command for advanced configuration",
|
|
825
|
+
manualConfigTip: "After manually modifying the configuration file, run ccr restart to apply changes",
|
|
826
|
+
// CCR Menu
|
|
827
|
+
ccrMenuTitle: "CCR - Claude Code Router Management",
|
|
828
|
+
ccrMenuOptions: {
|
|
829
|
+
initCcr: "Initialize CCR",
|
|
830
|
+
startUi: "Start CCR UI",
|
|
831
|
+
checkStatus: "Check CCR Status",
|
|
832
|
+
restart: "Restart CCR",
|
|
833
|
+
start: "Start CCR",
|
|
834
|
+
stop: "Stop CCR",
|
|
835
|
+
back: "Back to Main Menu"
|
|
836
|
+
},
|
|
837
|
+
ccrMenuDescriptions: {
|
|
838
|
+
initCcr: "Install and configure CCR",
|
|
839
|
+
startUi: "Open web interface to manage CCR",
|
|
840
|
+
checkStatus: "View CCR service status",
|
|
841
|
+
restart: "Restart CCR service",
|
|
842
|
+
start: "Start CCR service",
|
|
843
|
+
stop: "Stop CCR service"
|
|
844
|
+
},
|
|
845
|
+
// Command execution messages
|
|
846
|
+
startingCcrUi: "Starting CCR UI...",
|
|
847
|
+
ccrUiStarted: "CCR UI started",
|
|
848
|
+
checkingCcrStatus: "Checking CCR status...",
|
|
849
|
+
ccrStatusTitle: "CCR Status:",
|
|
850
|
+
restartingCcr: "Restarting CCR...",
|
|
851
|
+
ccrRestarted: "CCR restarted",
|
|
852
|
+
startingCcr: "Starting CCR...",
|
|
853
|
+
ccrStarted: "CCR started",
|
|
854
|
+
stoppingCcr: "Stopping CCR...",
|
|
855
|
+
ccrStopped: "CCR stopped",
|
|
856
|
+
ccrCommandFailed: "Failed to execute CCR command",
|
|
857
|
+
// Configuration check messages
|
|
858
|
+
ccrNotConfigured: "CCR is not configured yet. Please initialize CCR first.",
|
|
859
|
+
pleaseInitFirst: "Please select option 1 to initialize CCR.",
|
|
860
|
+
// UI API Key messages
|
|
861
|
+
ccrUiApiKey: "CCR UI API Key",
|
|
862
|
+
ccrUiApiKeyHint: "Use this API key to login to CCR UI"
|
|
863
|
+
};
|
|
864
|
+
|
|
659
865
|
const en = {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
866
|
+
common,
|
|
867
|
+
language,
|
|
868
|
+
installation,
|
|
869
|
+
api,
|
|
870
|
+
configuration,
|
|
871
|
+
mcp,
|
|
872
|
+
menu,
|
|
873
|
+
workflow,
|
|
874
|
+
cli,
|
|
875
|
+
bmad,
|
|
876
|
+
errors,
|
|
877
|
+
tools,
|
|
878
|
+
ccr: ccrMessages
|
|
672
879
|
};
|
|
673
880
|
|
|
674
|
-
const
|
|
881
|
+
const I18N$1 = {
|
|
675
882
|
"zh-CN": zhCN,
|
|
676
883
|
en
|
|
677
884
|
};
|
|
885
|
+
function getTranslation(lang) {
|
|
886
|
+
return I18N$1[lang];
|
|
887
|
+
}
|
|
678
888
|
|
|
679
889
|
const CLAUDE_DIR = join(homedir(), ".claude");
|
|
680
890
|
const SETTINGS_FILE = join(CLAUDE_DIR, "settings.json");
|
|
@@ -692,7 +902,7 @@ const AI_OUTPUT_LANGUAGES = {
|
|
|
692
902
|
en: { label: "English", directive: "Always respond in English" },
|
|
693
903
|
custom: { label: "Custom", directive: "" }
|
|
694
904
|
};
|
|
695
|
-
const I18N =
|
|
905
|
+
const I18N = I18N$1;
|
|
696
906
|
const MCP_SERVICES = [
|
|
697
907
|
{
|
|
698
908
|
id: "context7",
|
|
@@ -748,8 +958,8 @@ const MCP_SERVICES = [
|
|
|
748
958
|
},
|
|
749
959
|
requiresApiKey: true,
|
|
750
960
|
apiKeyPrompt: {
|
|
751
|
-
"zh-CN": "\u8BF7\u8F93\u5165 Exa API Key",
|
|
752
|
-
en: "Enter Exa API Key"
|
|
961
|
+
"zh-CN": "\u8BF7\u8F93\u5165 Exa API Key\uFF08\u53EF\u4ECE https://dashboard.exa.ai/api-keys \u83B7\u53D6\uFF09",
|
|
962
|
+
en: "Enter Exa API Key (get from https://dashboard.exa.ai/api-keys)"
|
|
753
963
|
},
|
|
754
964
|
apiKeyEnvVar: "EXA_API_KEY",
|
|
755
965
|
config: {
|
|
@@ -763,6 +973,21 @@ const MCP_SERVICES = [
|
|
|
763
973
|
}
|
|
764
974
|
];
|
|
765
975
|
|
|
976
|
+
function addNumbersToChoices(choices, startFrom = 1, format = (n) => `${n}. `) {
|
|
977
|
+
let currentNumber = startFrom;
|
|
978
|
+
return choices.map((choice) => {
|
|
979
|
+
if (choice.disabled) {
|
|
980
|
+
return choice;
|
|
981
|
+
}
|
|
982
|
+
const numbered = {
|
|
983
|
+
...choice,
|
|
984
|
+
name: `${format(currentNumber)}${choice.name}`
|
|
985
|
+
};
|
|
986
|
+
currentNumber++;
|
|
987
|
+
return numbered;
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
|
|
766
991
|
class FileSystemError extends Error {
|
|
767
992
|
constructor(message, path, cause) {
|
|
768
993
|
super(message);
|
|
@@ -874,8 +1099,8 @@ function readJsonConfig(path, options = {}) {
|
|
|
874
1099
|
const content = readFile(path);
|
|
875
1100
|
const data = JSON.parse(content);
|
|
876
1101
|
if (validate && !validate(data)) {
|
|
877
|
-
const i18n =
|
|
878
|
-
console.warn(`${i18n.invalidConfiguration} (${path})`);
|
|
1102
|
+
const i18n = getTranslation(readZcfConfig()?.preferredLang || "en");
|
|
1103
|
+
console.warn(`${i18n.configuration.invalidConfiguration} (${path})`);
|
|
879
1104
|
return defaultValue;
|
|
880
1105
|
}
|
|
881
1106
|
if (sanitize) {
|
|
@@ -883,8 +1108,8 @@ function readJsonConfig(path, options = {}) {
|
|
|
883
1108
|
}
|
|
884
1109
|
return data;
|
|
885
1110
|
} catch (error) {
|
|
886
|
-
const i18n =
|
|
887
|
-
console.error(`${i18n.failedToParseJson} ${path}`, error);
|
|
1111
|
+
const i18n = getTranslation(readZcfConfig()?.preferredLang || "en");
|
|
1112
|
+
console.error(`${i18n.configuration.failedToParseJson} ${path}`, error);
|
|
888
1113
|
return defaultValue;
|
|
889
1114
|
}
|
|
890
1115
|
}
|
|
@@ -909,8 +1134,8 @@ function backupJsonConfig(path, backupDir) {
|
|
|
909
1134
|
copyFile(path, backupPath);
|
|
910
1135
|
return backupPath;
|
|
911
1136
|
} catch (error) {
|
|
912
|
-
const i18n =
|
|
913
|
-
console.error(i18n.failedToBackupConfig, error);
|
|
1137
|
+
const i18n = getTranslation(readZcfConfig()?.preferredLang || "en");
|
|
1138
|
+
console.error(i18n.configuration.failedToBackupConfig, error);
|
|
914
1139
|
return null;
|
|
915
1140
|
}
|
|
916
1141
|
}
|
|
@@ -990,23 +1215,23 @@ function getPersonalityInfo(personalityId) {
|
|
|
990
1215
|
return AI_PERSONALITIES.find((p) => p.id === personalityId);
|
|
991
1216
|
}
|
|
992
1217
|
async function configureAiPersonality(scriptLang, showExisting = true) {
|
|
993
|
-
const i18n =
|
|
1218
|
+
const i18n = getTranslation(scriptLang);
|
|
994
1219
|
const existingPersonality = getExistingPersonality();
|
|
995
1220
|
if (showExisting && existingPersonality) {
|
|
996
1221
|
const personalityInfo = getPersonalityInfo(existingPersonality);
|
|
997
1222
|
if (personalityInfo) {
|
|
998
|
-
console.log("\n" + ansis.blue(`\u2139 ${i18n.existingPersonality || "Existing AI personality configuration"}`));
|
|
1223
|
+
console.log("\n" + ansis.blue(`\u2139 ${i18n.configuration.existingPersonality || "Existing AI personality configuration"}`));
|
|
999
1224
|
console.log(
|
|
1000
|
-
ansis.gray(` ${i18n.currentPersonality || "Current personality"}: ${personalityInfo.name[scriptLang]}`)
|
|
1225
|
+
ansis.gray(` ${i18n.configuration.currentPersonality || "Current personality"}: ${personalityInfo.name[scriptLang]}`)
|
|
1001
1226
|
);
|
|
1002
1227
|
const { modify } = await inquirer.prompt({
|
|
1003
1228
|
type: "confirm",
|
|
1004
1229
|
name: "modify",
|
|
1005
|
-
message: i18n.modifyPersonality || "Modify AI personality?",
|
|
1230
|
+
message: i18n.configuration.modifyPersonality || "Modify AI personality?",
|
|
1006
1231
|
default: false
|
|
1007
1232
|
});
|
|
1008
1233
|
if (!modify) {
|
|
1009
|
-
console.log(ansis.green(`\u2714 ${i18n.keepPersonality || "Keeping existing personality"}`));
|
|
1234
|
+
console.log(ansis.green(`\u2714 ${i18n.configuration.keepPersonality || "Keeping existing personality"}`));
|
|
1010
1235
|
return;
|
|
1011
1236
|
}
|
|
1012
1237
|
}
|
|
@@ -1014,16 +1239,16 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
|
|
|
1014
1239
|
const { personality } = await inquirer.prompt({
|
|
1015
1240
|
type: "list",
|
|
1016
1241
|
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")}`,
|
|
1242
|
+
message: i18n.configuration.selectAiPersonality || "Select AI personality",
|
|
1243
|
+
choices: addNumbersToChoices(AI_PERSONALITIES.map((p) => ({
|
|
1244
|
+
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
1245
|
value: p.id,
|
|
1021
1246
|
short: p.name[scriptLang]
|
|
1022
|
-
})),
|
|
1247
|
+
}))),
|
|
1023
1248
|
default: existingPersonality ? AI_PERSONALITIES.findIndex((p) => p.id === existingPersonality) : 0
|
|
1024
1249
|
});
|
|
1025
1250
|
if (!personality) {
|
|
1026
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1251
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1027
1252
|
return;
|
|
1028
1253
|
}
|
|
1029
1254
|
let directive = "";
|
|
@@ -1031,11 +1256,11 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
|
|
|
1031
1256
|
const { customDirective } = await inquirer.prompt({
|
|
1032
1257
|
type: "input",
|
|
1033
1258
|
name: "customDirective",
|
|
1034
|
-
message: i18n.enterCustomPersonality || "Enter custom personality directive",
|
|
1035
|
-
validate: (value) => !!value || i18n.directiveCannotBeEmpty
|
|
1259
|
+
message: i18n.configuration.enterCustomPersonality || "Enter custom personality directive",
|
|
1260
|
+
validate: (value) => !!value || i18n.configuration.directiveCannotBeEmpty
|
|
1036
1261
|
});
|
|
1037
1262
|
if (!customDirective) {
|
|
1038
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1263
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1039
1264
|
return;
|
|
1040
1265
|
}
|
|
1041
1266
|
directive = customDirective;
|
|
@@ -1047,14 +1272,16 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
|
|
|
1047
1272
|
}
|
|
1048
1273
|
await applyPersonalityDirective(directive);
|
|
1049
1274
|
updateZcfConfig({ aiPersonality: personality });
|
|
1050
|
-
console.log(ansis.green(`\u2714 ${i18n.personalityConfigured || "AI personality configured"}`));
|
|
1275
|
+
console.log(ansis.green(`\u2714 ${i18n.configuration.personalityConfigured || "AI personality configured"}`));
|
|
1051
1276
|
}
|
|
1052
1277
|
async function applyPersonalityDirective(directive) {
|
|
1053
1278
|
try {
|
|
1054
1279
|
const personalityFile = join(CLAUDE_DIR, "personality.md");
|
|
1055
1280
|
writeFile(personalityFile, directive);
|
|
1056
1281
|
} catch (error) {
|
|
1057
|
-
|
|
1282
|
+
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1283
|
+
const errorI18n = getTranslation(lang);
|
|
1284
|
+
console.error(ansis.red(errorI18n.configuration.failedToApplyPersonality || "Failed to apply personality"), error);
|
|
1058
1285
|
}
|
|
1059
1286
|
}
|
|
1060
1287
|
|
|
@@ -1090,9 +1317,9 @@ function handleExitPromptError(error) {
|
|
|
1090
1317
|
if (error instanceof Error && error.name === "ExitPromptError") {
|
|
1091
1318
|
const zcfConfig = readZcfConfig();
|
|
1092
1319
|
const defaultLang = zcfConfig?.preferredLang || "zh-CN";
|
|
1093
|
-
const i18n =
|
|
1320
|
+
const i18n = getTranslation(defaultLang);
|
|
1094
1321
|
console.log(ansis.cyan(`
|
|
1095
|
-
${i18n.goodbye}
|
|
1322
|
+
${i18n.common.goodbye}
|
|
1096
1323
|
`));
|
|
1097
1324
|
process.exit(0);
|
|
1098
1325
|
}
|
|
@@ -1101,7 +1328,8 @@ ${i18n.goodbye}
|
|
|
1101
1328
|
function handleGeneralError(error, lang) {
|
|
1102
1329
|
const zcfConfig = readZcfConfig();
|
|
1103
1330
|
const defaultLang = lang || zcfConfig?.preferredLang || "en";
|
|
1104
|
-
const
|
|
1331
|
+
const i18n = getTranslation(defaultLang);
|
|
1332
|
+
const errorMsg = i18n.common.error || "Error";
|
|
1105
1333
|
console.error(ansis.red(`${errorMsg}:`), error);
|
|
1106
1334
|
if (error instanceof Error) {
|
|
1107
1335
|
console.error(ansis.gray(`Stack: ${error.stack}`));
|
|
@@ -1200,6 +1428,142 @@ function mergeAndCleanPermissions(templatePermissions, userPermissions) {
|
|
|
1200
1428
|
return cleanupPermissions(template, user);
|
|
1201
1429
|
}
|
|
1202
1430
|
|
|
1431
|
+
function getPlatform() {
|
|
1432
|
+
const p = platform();
|
|
1433
|
+
if (p === "win32") return "windows";
|
|
1434
|
+
if (p === "darwin") return "macos";
|
|
1435
|
+
return "linux";
|
|
1436
|
+
}
|
|
1437
|
+
function isTermux() {
|
|
1438
|
+
return !!(process.env.PREFIX && process.env.PREFIX.includes("com.termux")) || !!process.env.TERMUX_VERSION || existsSync("/data/data/com.termux/files/usr");
|
|
1439
|
+
}
|
|
1440
|
+
function getTermuxPrefix() {
|
|
1441
|
+
return process.env.PREFIX || "/data/data/com.termux/files/usr";
|
|
1442
|
+
}
|
|
1443
|
+
function isWindows() {
|
|
1444
|
+
return getPlatform() === "windows";
|
|
1445
|
+
}
|
|
1446
|
+
function getMcpCommand() {
|
|
1447
|
+
if (isWindows()) {
|
|
1448
|
+
return ["cmd", "/c", "npx"];
|
|
1449
|
+
}
|
|
1450
|
+
return ["npx"];
|
|
1451
|
+
}
|
|
1452
|
+
async function commandExists(command) {
|
|
1453
|
+
try {
|
|
1454
|
+
const cmd = getPlatform() === "windows" ? "where" : "which";
|
|
1455
|
+
const res = await exec(cmd, [command]);
|
|
1456
|
+
if (res.exitCode === 0) {
|
|
1457
|
+
return true;
|
|
1458
|
+
}
|
|
1459
|
+
} catch {
|
|
1460
|
+
}
|
|
1461
|
+
if (isTermux()) {
|
|
1462
|
+
const termuxPrefix = getTermuxPrefix();
|
|
1463
|
+
const possiblePaths = [
|
|
1464
|
+
`${termuxPrefix}/bin/${command}`,
|
|
1465
|
+
`${termuxPrefix}/usr/bin/${command}`,
|
|
1466
|
+
`/data/data/com.termux/files/usr/bin/${command}`
|
|
1467
|
+
];
|
|
1468
|
+
for (const path of possiblePaths) {
|
|
1469
|
+
if (existsSync(path)) {
|
|
1470
|
+
return true;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
if (getPlatform() !== "windows") {
|
|
1475
|
+
const commonPaths = [
|
|
1476
|
+
`/usr/local/bin/${command}`,
|
|
1477
|
+
`/usr/bin/${command}`,
|
|
1478
|
+
`/bin/${command}`,
|
|
1479
|
+
`${process.env.HOME}/.local/bin/${command}`
|
|
1480
|
+
];
|
|
1481
|
+
for (const path of commonPaths) {
|
|
1482
|
+
if (existsSync(path)) {
|
|
1483
|
+
return true;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
return false;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
function getMcpConfigPath() {
|
|
1491
|
+
return ClAUDE_CONFIG_FILE;
|
|
1492
|
+
}
|
|
1493
|
+
function readMcpConfig() {
|
|
1494
|
+
return readJsonConfig(ClAUDE_CONFIG_FILE);
|
|
1495
|
+
}
|
|
1496
|
+
function writeMcpConfig(config) {
|
|
1497
|
+
writeJsonConfig(ClAUDE_CONFIG_FILE, config);
|
|
1498
|
+
}
|
|
1499
|
+
function backupMcpConfig() {
|
|
1500
|
+
const backupBaseDir = join(CLAUDE_DIR, "backup");
|
|
1501
|
+
return backupJsonConfig(ClAUDE_CONFIG_FILE, backupBaseDir);
|
|
1502
|
+
}
|
|
1503
|
+
function mergeMcpServers(existing, newServers) {
|
|
1504
|
+
const config = existing || { mcpServers: {} };
|
|
1505
|
+
if (!config.mcpServers) {
|
|
1506
|
+
config.mcpServers = {};
|
|
1507
|
+
}
|
|
1508
|
+
Object.assign(config.mcpServers, newServers);
|
|
1509
|
+
return config;
|
|
1510
|
+
}
|
|
1511
|
+
function applyPlatformCommand(config) {
|
|
1512
|
+
if (config.command === "npx" && isWindows()) {
|
|
1513
|
+
const mcpCmd = getMcpCommand();
|
|
1514
|
+
config.command = mcpCmd[0];
|
|
1515
|
+
config.args = [...mcpCmd.slice(1), ...config.args || []];
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
function buildMcpServerConfig(baseConfig, apiKey, placeholder = "YOUR_EXA_API_KEY", envVarName) {
|
|
1519
|
+
const config = deepClone(baseConfig);
|
|
1520
|
+
applyPlatformCommand(config);
|
|
1521
|
+
if (!apiKey) {
|
|
1522
|
+
return config;
|
|
1523
|
+
}
|
|
1524
|
+
if (envVarName && config.env) {
|
|
1525
|
+
config.env[envVarName] = apiKey;
|
|
1526
|
+
return config;
|
|
1527
|
+
}
|
|
1528
|
+
if (config.args) {
|
|
1529
|
+
config.args = config.args.map((arg) => arg.replace(placeholder, apiKey));
|
|
1530
|
+
}
|
|
1531
|
+
if (config.url) {
|
|
1532
|
+
config.url = config.url.replace(placeholder, apiKey);
|
|
1533
|
+
}
|
|
1534
|
+
return config;
|
|
1535
|
+
}
|
|
1536
|
+
function fixWindowsMcpConfig(config) {
|
|
1537
|
+
if (!isWindows() || !config.mcpServers) {
|
|
1538
|
+
return config;
|
|
1539
|
+
}
|
|
1540
|
+
const fixed = { ...config };
|
|
1541
|
+
for (const [, serverConfig] of Object.entries(fixed.mcpServers)) {
|
|
1542
|
+
if (serverConfig && typeof serverConfig === "object" && "command" in serverConfig) {
|
|
1543
|
+
applyPlatformCommand(serverConfig);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
return fixed;
|
|
1547
|
+
}
|
|
1548
|
+
function addCompletedOnboarding() {
|
|
1549
|
+
try {
|
|
1550
|
+
let config = readMcpConfig();
|
|
1551
|
+
if (!config) {
|
|
1552
|
+
config = { mcpServers: {} };
|
|
1553
|
+
}
|
|
1554
|
+
if (config.hasCompletedOnboarding === true) {
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
config.hasCompletedOnboarding = true;
|
|
1558
|
+
writeMcpConfig(config);
|
|
1559
|
+
} catch (error) {
|
|
1560
|
+
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1561
|
+
const i18n = getTranslation(lang);
|
|
1562
|
+
console.error(i18n.configuration?.failedToAddOnboardingFlag || "Failed to add onboarding flag", error);
|
|
1563
|
+
throw error;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1203
1567
|
function ensureClaudeDir() {
|
|
1204
1568
|
ensureDir(CLAUDE_DIR);
|
|
1205
1569
|
}
|
|
@@ -1239,8 +1603,8 @@ function copyConfigFiles(lang, onlyMd = false) {
|
|
|
1239
1603
|
function copyClaudeMemoryFiles(lang, rootDir) {
|
|
1240
1604
|
const memorySourceDir = join(rootDir, "templates", lang, "memory");
|
|
1241
1605
|
if (!exists(memorySourceDir)) {
|
|
1242
|
-
const i18n =
|
|
1243
|
-
throw new Error(`${i18n.memoryDirNotFound || "Memory directory not found:"} ${memorySourceDir}`);
|
|
1606
|
+
const i18n = getTranslation(lang);
|
|
1607
|
+
throw new Error(`${i18n.configuration.memoryDirNotFound || "Memory directory not found:"} ${memorySourceDir}`);
|
|
1244
1608
|
}
|
|
1245
1609
|
const files = readDir(memorySourceDir);
|
|
1246
1610
|
files?.forEach((file) => {
|
|
@@ -1259,7 +1623,9 @@ function getDefaultSettings() {
|
|
|
1259
1623
|
const templateSettingsPath = join(rootDir, "templates", "settings.json");
|
|
1260
1624
|
return readJsonConfig(templateSettingsPath) || {};
|
|
1261
1625
|
} catch (error) {
|
|
1262
|
-
|
|
1626
|
+
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1627
|
+
const i18n = getTranslation(lang);
|
|
1628
|
+
console.error(i18n.configuration.failedToReadTemplateSettings || "Failed to read template settings", error);
|
|
1263
1629
|
return {};
|
|
1264
1630
|
}
|
|
1265
1631
|
}
|
|
@@ -1284,6 +1650,13 @@ function configureApi(apiConfig) {
|
|
|
1284
1650
|
settings.env.ANTHROPIC_BASE_URL = apiConfig.url;
|
|
1285
1651
|
}
|
|
1286
1652
|
writeJsonConfig(SETTINGS_FILE, settings);
|
|
1653
|
+
try {
|
|
1654
|
+
addCompletedOnboarding();
|
|
1655
|
+
} catch (error) {
|
|
1656
|
+
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1657
|
+
const i18n = getTranslation(lang);
|
|
1658
|
+
console.error(i18n.configuration.failedToSetOnboarding || "Failed to set onboarding flag", error);
|
|
1659
|
+
}
|
|
1287
1660
|
return apiConfig;
|
|
1288
1661
|
}
|
|
1289
1662
|
function mergeConfigs(sourceFile, targetFile) {
|
|
@@ -1306,7 +1679,9 @@ function mergeSettingsFile(templatePath, targetPath) {
|
|
|
1306
1679
|
try {
|
|
1307
1680
|
const templateSettings = readJsonConfig(templatePath);
|
|
1308
1681
|
if (!templateSettings) {
|
|
1309
|
-
|
|
1682
|
+
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1683
|
+
const i18n = getTranslation(lang);
|
|
1684
|
+
console.error(i18n.configuration?.failedToReadTemplateSettings || "Failed to read template settings");
|
|
1310
1685
|
return;
|
|
1311
1686
|
}
|
|
1312
1687
|
if (!exists(targetPath)) {
|
|
@@ -1333,9 +1708,13 @@ function mergeSettingsFile(templatePath, targetPath) {
|
|
|
1333
1708
|
}
|
|
1334
1709
|
writeJsonConfig(targetPath, mergedSettings);
|
|
1335
1710
|
} catch (error) {
|
|
1336
|
-
|
|
1711
|
+
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1712
|
+
const i18n = getTranslation(lang);
|
|
1713
|
+
console.error(i18n.configuration.failedToMergeSettings || "Failed to merge settings", error);
|
|
1337
1714
|
if (exists(targetPath)) {
|
|
1338
|
-
|
|
1715
|
+
const lang2 = readZcfConfig()?.preferredLang || "en";
|
|
1716
|
+
const i18n2 = getTranslation(lang2);
|
|
1717
|
+
console.warn(i18n2.configuration.preservingExistingSettings || "Preserving existing settings");
|
|
1339
1718
|
} else {
|
|
1340
1719
|
copyFile(templatePath, targetPath);
|
|
1341
1720
|
}
|
|
@@ -1379,11 +1758,11 @@ function applyAiLanguageDirective(aiOutputLang) {
|
|
|
1379
1758
|
}
|
|
1380
1759
|
|
|
1381
1760
|
function validateApiKey(apiKey, lang = "zh-CN") {
|
|
1382
|
-
const i18n =
|
|
1761
|
+
const i18n = getTranslation(lang);
|
|
1383
1762
|
if (!apiKey || apiKey.trim() === "") {
|
|
1384
1763
|
return {
|
|
1385
1764
|
isValid: false,
|
|
1386
|
-
error: i18n.apiKeyValidation.empty
|
|
1765
|
+
error: i18n.api.apiKeyValidation.empty
|
|
1387
1766
|
};
|
|
1388
1767
|
}
|
|
1389
1768
|
return { isValid: true };
|
|
@@ -1401,22 +1780,22 @@ async function configureApiCompletely(i18n, scriptLang, preselectedAuthType) {
|
|
|
1401
1780
|
const { authType: selectedAuthType } = await inquirer.prompt({
|
|
1402
1781
|
type: "list",
|
|
1403
1782
|
name: "authType",
|
|
1404
|
-
message: i18n.configureApi,
|
|
1405
|
-
choices: [
|
|
1783
|
+
message: i18n.api.configureApi,
|
|
1784
|
+
choices: addNumbersToChoices([
|
|
1406
1785
|
{
|
|
1407
|
-
name: `${i18n.useAuthToken} - ${ansis.gray(i18n.authTokenDesc)}`,
|
|
1786
|
+
name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
|
|
1408
1787
|
value: "auth_token",
|
|
1409
|
-
short: i18n.useAuthToken
|
|
1788
|
+
short: i18n.api.useAuthToken
|
|
1410
1789
|
},
|
|
1411
1790
|
{
|
|
1412
|
-
name: `${i18n.useApiKey} - ${ansis.gray(i18n.apiKeyDesc)}`,
|
|
1791
|
+
name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
|
|
1413
1792
|
value: "api_key",
|
|
1414
|
-
short: i18n.useApiKey
|
|
1793
|
+
short: i18n.api.useApiKey
|
|
1415
1794
|
}
|
|
1416
|
-
]
|
|
1795
|
+
])
|
|
1417
1796
|
});
|
|
1418
1797
|
if (!selectedAuthType) {
|
|
1419
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1798
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1420
1799
|
return null;
|
|
1421
1800
|
}
|
|
1422
1801
|
authType = selectedAuthType;
|
|
@@ -1424,39 +1803,39 @@ async function configureApiCompletely(i18n, scriptLang, preselectedAuthType) {
|
|
|
1424
1803
|
const { url } = await inquirer.prompt({
|
|
1425
1804
|
type: "input",
|
|
1426
1805
|
name: "url",
|
|
1427
|
-
message: i18n.enterApiUrl,
|
|
1806
|
+
message: i18n.api.enterApiUrl,
|
|
1428
1807
|
validate: (value) => {
|
|
1429
|
-
if (!value) return i18n.urlRequired;
|
|
1808
|
+
if (!value) return i18n.api.urlRequired;
|
|
1430
1809
|
try {
|
|
1431
1810
|
new URL(value);
|
|
1432
1811
|
return true;
|
|
1433
1812
|
} catch {
|
|
1434
|
-
return i18n.invalidUrl;
|
|
1813
|
+
return i18n.api.invalidUrl;
|
|
1435
1814
|
}
|
|
1436
1815
|
}
|
|
1437
1816
|
});
|
|
1438
1817
|
if (url === void 0) {
|
|
1439
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1818
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1440
1819
|
return null;
|
|
1441
1820
|
}
|
|
1442
|
-
const keyMessage = authType === "auth_token" ? i18n.enterAuthToken : i18n.enterApiKey;
|
|
1821
|
+
const keyMessage = authType === "auth_token" ? i18n.api.enterAuthToken : i18n.api.enterApiKey;
|
|
1443
1822
|
const { key } = await inquirer.prompt({
|
|
1444
1823
|
type: "input",
|
|
1445
1824
|
name: "key",
|
|
1446
1825
|
message: keyMessage,
|
|
1447
1826
|
validate: (value) => {
|
|
1448
1827
|
if (!value) {
|
|
1449
|
-
return i18n.keyRequired;
|
|
1828
|
+
return i18n.api.keyRequired;
|
|
1450
1829
|
}
|
|
1451
1830
|
const validation = validateApiKey(value, scriptLang);
|
|
1452
1831
|
if (!validation.isValid) {
|
|
1453
|
-
return validation.error || i18n.invalidKeyFormat;
|
|
1832
|
+
return validation.error || i18n.api.invalidKeyFormat;
|
|
1454
1833
|
}
|
|
1455
1834
|
return true;
|
|
1456
1835
|
}
|
|
1457
1836
|
});
|
|
1458
1837
|
if (key === void 0) {
|
|
1459
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1838
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1460
1839
|
return null;
|
|
1461
1840
|
}
|
|
1462
1841
|
console.log(ansis.gray(` API Key: ${formatApiKeyDisplay(key)}`));
|
|
@@ -1471,276 +1850,144 @@ async function modifyApiConfigPartially(existingConfig, i18n, scriptLang) {
|
|
|
1471
1850
|
const { item } = await inquirer.prompt({
|
|
1472
1851
|
type: "list",
|
|
1473
1852
|
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
|
-
]
|
|
1853
|
+
message: i18n.api.selectModifyItems,
|
|
1854
|
+
choices: addNumbersToChoices([
|
|
1855
|
+
{ name: i18n.api.modifyApiUrl, value: "url" },
|
|
1856
|
+
{ name: i18n.api.modifyApiKey, value: "key" },
|
|
1857
|
+
{ name: i18n.api.modifyAuthType, value: "authType" }
|
|
1858
|
+
])
|
|
1480
1859
|
});
|
|
1481
1860
|
if (!item) {
|
|
1482
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1861
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1483
1862
|
return;
|
|
1484
1863
|
}
|
|
1485
1864
|
if (item === "url") {
|
|
1486
1865
|
const { url } = await inquirer.prompt({
|
|
1487
1866
|
type: "input",
|
|
1488
1867
|
name: "url",
|
|
1489
|
-
message: i18n.enterNewApiUrl.replace("{url}", currentConfig.url || i18n.none),
|
|
1868
|
+
message: i18n.api.enterNewApiUrl.replace("{url}", currentConfig.url || i18n.common.none),
|
|
1490
1869
|
default: currentConfig.url,
|
|
1491
1870
|
validate: (value) => {
|
|
1492
|
-
if (!value) return i18n.urlRequired;
|
|
1871
|
+
if (!value) return i18n.api.urlRequired;
|
|
1493
1872
|
try {
|
|
1494
1873
|
new URL(value);
|
|
1495
1874
|
return true;
|
|
1496
1875
|
} catch {
|
|
1497
|
-
return i18n.invalidUrl;
|
|
1876
|
+
return i18n.api.invalidUrl;
|
|
1498
1877
|
}
|
|
1499
1878
|
}
|
|
1500
1879
|
});
|
|
1501
1880
|
if (url === void 0) {
|
|
1502
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1881
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1503
1882
|
return;
|
|
1504
1883
|
}
|
|
1505
1884
|
currentConfig.url = url;
|
|
1506
1885
|
const savedConfig = configureApi(currentConfig);
|
|
1507
1886
|
if (savedConfig) {
|
|
1508
|
-
console.log(ansis.green(`\u2714 ${i18n.modificationSaved}`));
|
|
1509
|
-
console.log(ansis.gray(` ${i18n.apiConfigUrl}: ${savedConfig.url}`));
|
|
1887
|
+
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
1888
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${savedConfig.url}`));
|
|
1510
1889
|
}
|
|
1511
1890
|
} else if (item === "key") {
|
|
1512
1891
|
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);
|
|
1892
|
+
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
1893
|
const { key } = await inquirer.prompt({
|
|
1515
1894
|
type: "input",
|
|
1516
1895
|
name: "key",
|
|
1517
1896
|
message: keyMessage,
|
|
1518
1897
|
validate: (value) => {
|
|
1519
1898
|
if (!value) {
|
|
1520
|
-
return i18n.keyRequired;
|
|
1899
|
+
return i18n.api.keyRequired;
|
|
1521
1900
|
}
|
|
1522
1901
|
const validation = validateApiKey(value, scriptLang);
|
|
1523
1902
|
if (!validation.isValid) {
|
|
1524
|
-
return validation.error || i18n.invalidKeyFormat;
|
|
1903
|
+
return validation.error || i18n.api.invalidKeyFormat;
|
|
1525
1904
|
}
|
|
1526
1905
|
return true;
|
|
1527
1906
|
}
|
|
1528
1907
|
});
|
|
1529
1908
|
if (key === void 0) {
|
|
1530
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1909
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1531
1910
|
return;
|
|
1532
1911
|
}
|
|
1533
1912
|
currentConfig.key = key;
|
|
1534
1913
|
const savedConfig = configureApi(currentConfig);
|
|
1535
1914
|
if (savedConfig) {
|
|
1536
|
-
console.log(ansis.green(`\u2714 ${i18n.modificationSaved}`));
|
|
1537
|
-
console.log(ansis.gray(` ${i18n.apiConfigKey}: ${formatApiKeyDisplay(savedConfig.key)}`));
|
|
1915
|
+
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
1916
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigKey}: ${formatApiKeyDisplay(savedConfig.key)}`));
|
|
1538
1917
|
}
|
|
1539
1918
|
} else if (item === "authType") {
|
|
1540
1919
|
const { authType } = await inquirer.prompt({
|
|
1541
1920
|
type: "list",
|
|
1542
1921
|
name: "authType",
|
|
1543
|
-
message: i18n.selectNewAuthType.replace("{type}", currentConfig.authType || i18n.none),
|
|
1544
|
-
choices: [
|
|
1922
|
+
message: i18n.api.selectNewAuthType.replace("{type}", currentConfig.authType || i18n.common.none),
|
|
1923
|
+
choices: addNumbersToChoices([
|
|
1545
1924
|
{ name: "Auth Token (OAuth)", value: "auth_token" },
|
|
1546
1925
|
{ name: "API Key", value: "api_key" }
|
|
1547
|
-
],
|
|
1926
|
+
]),
|
|
1548
1927
|
default: currentConfig.authType === "api_key" ? 1 : 0
|
|
1549
1928
|
});
|
|
1550
1929
|
if (authType === void 0) {
|
|
1551
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
1930
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1552
1931
|
return;
|
|
1553
1932
|
}
|
|
1554
1933
|
currentConfig.authType = authType;
|
|
1555
1934
|
const savedConfig = configureApi(currentConfig);
|
|
1556
1935
|
if (savedConfig) {
|
|
1557
|
-
console.log(ansis.green(`\u2714 ${i18n.modificationSaved}`));
|
|
1558
|
-
console.log(ansis.gray(` ${i18n.apiConfigAuthType}: ${savedConfig.authType}`));
|
|
1936
|
+
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
1937
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigAuthType}: ${savedConfig.authType}`));
|
|
1559
1938
|
}
|
|
1560
1939
|
}
|
|
1561
1940
|
}
|
|
1562
1941
|
async function updatePromptOnly(configLang, scriptLang, aiOutputLang) {
|
|
1563
|
-
const i18n =
|
|
1942
|
+
const i18n = getTranslation(scriptLang);
|
|
1564
1943
|
const backupDir = backupExistingConfig();
|
|
1565
1944
|
if (backupDir) {
|
|
1566
|
-
console.log(ansis.gray(`\u2714 ${i18n.backupSuccess}: ${backupDir}`));
|
|
1945
|
+
console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
|
|
1567
1946
|
}
|
|
1568
1947
|
copyConfigFiles(configLang, true);
|
|
1569
1948
|
if (aiOutputLang) {
|
|
1570
1949
|
applyAiLanguageDirective(aiOutputLang);
|
|
1571
1950
|
}
|
|
1572
1951
|
await configureAiPersonality(scriptLang);
|
|
1573
|
-
console.log(ansis.green(`\u2714 ${i18n.configSuccess} ${CLAUDE_DIR}`));
|
|
1574
|
-
console.log("\n" + ansis.cyan(i18n.complete));
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
function getPlatform() {
|
|
1578
|
-
const p = platform();
|
|
1579
|
-
if (p === "win32") return "windows";
|
|
1580
|
-
if (p === "darwin") return "macos";
|
|
1581
|
-
return "linux";
|
|
1582
|
-
}
|
|
1583
|
-
function isTermux() {
|
|
1584
|
-
return !!(process.env.PREFIX && process.env.PREFIX.includes("com.termux")) || !!process.env.TERMUX_VERSION || existsSync("/data/data/com.termux/files/usr");
|
|
1585
|
-
}
|
|
1586
|
-
function getTermuxPrefix() {
|
|
1587
|
-
return process.env.PREFIX || "/data/data/com.termux/files/usr";
|
|
1588
|
-
}
|
|
1589
|
-
function isWindows() {
|
|
1590
|
-
return getPlatform() === "windows";
|
|
1591
|
-
}
|
|
1592
|
-
function getMcpCommand() {
|
|
1593
|
-
if (isWindows()) {
|
|
1594
|
-
return ["cmd", "/c", "npx"];
|
|
1595
|
-
}
|
|
1596
|
-
return ["npx"];
|
|
1597
|
-
}
|
|
1598
|
-
async function commandExists(command) {
|
|
1599
|
-
try {
|
|
1600
|
-
const cmd = getPlatform() === "windows" ? "where" : "which";
|
|
1601
|
-
const res = await exec(cmd, [command]);
|
|
1602
|
-
if (res.exitCode === 0) {
|
|
1603
|
-
return true;
|
|
1604
|
-
}
|
|
1605
|
-
} catch {
|
|
1606
|
-
}
|
|
1607
|
-
if (isTermux()) {
|
|
1608
|
-
const termuxPrefix = getTermuxPrefix();
|
|
1609
|
-
const possiblePaths = [
|
|
1610
|
-
`${termuxPrefix}/bin/${command}`,
|
|
1611
|
-
`${termuxPrefix}/usr/bin/${command}`,
|
|
1612
|
-
`/data/data/com.termux/files/usr/bin/${command}`
|
|
1613
|
-
];
|
|
1614
|
-
for (const path of possiblePaths) {
|
|
1615
|
-
if (existsSync(path)) {
|
|
1616
|
-
return true;
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
if (getPlatform() !== "windows") {
|
|
1621
|
-
const commonPaths = [
|
|
1622
|
-
`/usr/local/bin/${command}`,
|
|
1623
|
-
`/usr/bin/${command}`,
|
|
1624
|
-
`/bin/${command}`,
|
|
1625
|
-
`${process.env.HOME}/.local/bin/${command}`
|
|
1626
|
-
];
|
|
1627
|
-
for (const path of commonPaths) {
|
|
1628
|
-
if (existsSync(path)) {
|
|
1629
|
-
return true;
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
return false;
|
|
1952
|
+
console.log(ansis.green(`\u2714 ${i18n.configuration.configSuccess} ${CLAUDE_DIR}`));
|
|
1953
|
+
console.log("\n" + ansis.cyan(i18n.common.complete));
|
|
1634
1954
|
}
|
|
1635
1955
|
|
|
1636
1956
|
async function isClaudeCodeInstalled() {
|
|
1637
1957
|
return await commandExists("claude");
|
|
1638
1958
|
}
|
|
1639
1959
|
async function installClaudeCode(lang) {
|
|
1640
|
-
const i18n =
|
|
1960
|
+
const i18n = getTranslation(lang);
|
|
1641
1961
|
if (isTermux()) {
|
|
1642
|
-
console.log(ansis.yellow(`\u2139 ${i18n.termuxDetected}`));
|
|
1962
|
+
console.log(ansis.yellow(`\u2139 ${i18n.installation.termuxDetected}`));
|
|
1643
1963
|
const termuxPrefix = getTermuxPrefix();
|
|
1644
|
-
console.log(ansis.gray(i18n.termuxPathInfo.replace("{path}", termuxPrefix)));
|
|
1964
|
+
console.log(ansis.gray(i18n.installation.termuxPathInfo.replace("{path}", termuxPrefix)));
|
|
1645
1965
|
console.log(ansis.gray(`Node.js: ${termuxPrefix}/bin/node`));
|
|
1646
1966
|
console.log(ansis.gray(`npm: ${termuxPrefix}/bin/npm`));
|
|
1647
1967
|
}
|
|
1648
|
-
console.log(i18n.installing);
|
|
1968
|
+
console.log(i18n.installation.installing);
|
|
1649
1969
|
try {
|
|
1650
1970
|
await exec("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
|
|
1651
|
-
console.log(`\u2714 ${i18n.installSuccess}`);
|
|
1971
|
+
console.log(`\u2714 ${i18n.installation.installSuccess}`);
|
|
1652
1972
|
if (isTermux()) {
|
|
1653
1973
|
console.log(ansis.gray(`
|
|
1654
1974
|
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
1655
1975
|
}
|
|
1656
1976
|
} catch (error) {
|
|
1657
|
-
console.error(`\u2716 ${i18n.installFailed}`);
|
|
1977
|
+
console.error(`\u2716 ${i18n.installation.installFailed}`);
|
|
1658
1978
|
if (isTermux()) {
|
|
1659
1979
|
console.error(ansis.yellow(`
|
|
1660
|
-
${i18n.termuxInstallHint}
|
|
1980
|
+
${i18n.installation.termuxInstallHint}
|
|
1661
1981
|
`));
|
|
1662
1982
|
}
|
|
1663
1983
|
throw error;
|
|
1664
1984
|
}
|
|
1665
1985
|
}
|
|
1666
1986
|
|
|
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
1987
|
async function selectAiOutputLanguage(scriptLang, defaultLang) {
|
|
1741
|
-
const i18n =
|
|
1988
|
+
const i18n = getTranslation(scriptLang);
|
|
1742
1989
|
console.log(ansis.dim(`
|
|
1743
|
-
${i18n.aiOutputLangHint}
|
|
1990
|
+
${i18n.language.aiOutputLangHint}
|
|
1744
1991
|
`));
|
|
1745
1992
|
const aiLangChoices = Object.entries(AI_OUTPUT_LANGUAGES).map(([key, value]) => ({
|
|
1746
1993
|
title: value.label,
|
|
@@ -1750,15 +1997,15 @@ async function selectAiOutputLanguage(scriptLang, defaultLang) {
|
|
|
1750
1997
|
const { lang } = await inquirer.prompt({
|
|
1751
1998
|
type: "list",
|
|
1752
1999
|
name: "lang",
|
|
1753
|
-
message: i18n.selectAiOutputLang,
|
|
1754
|
-
choices: aiLangChoices.map((choice) => ({
|
|
2000
|
+
message: i18n.language.selectAiOutputLang,
|
|
2001
|
+
choices: addNumbersToChoices(aiLangChoices.map((choice) => ({
|
|
1755
2002
|
name: choice.title,
|
|
1756
2003
|
value: choice.value
|
|
1757
|
-
})),
|
|
2004
|
+
}))),
|
|
1758
2005
|
default: defaultChoice
|
|
1759
2006
|
});
|
|
1760
2007
|
if (!lang) {
|
|
1761
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2008
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1762
2009
|
process.exit(0);
|
|
1763
2010
|
}
|
|
1764
2011
|
let aiOutputLang = lang;
|
|
@@ -1766,11 +2013,11 @@ async function selectAiOutputLanguage(scriptLang, defaultLang) {
|
|
|
1766
2013
|
const { customLang } = await inquirer.prompt({
|
|
1767
2014
|
type: "input",
|
|
1768
2015
|
name: "customLang",
|
|
1769
|
-
message: i18n.enterCustomLanguage,
|
|
1770
|
-
validate: (value) => !!value || i18n.languageRequired
|
|
2016
|
+
message: i18n.language.enterCustomLanguage,
|
|
2017
|
+
validate: (value) => !!value || i18n.language?.languageRequired || "Language is required"
|
|
1771
2018
|
});
|
|
1772
2019
|
if (!customLang) {
|
|
1773
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2020
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1774
2021
|
process.exit(0);
|
|
1775
2022
|
}
|
|
1776
2023
|
return customLang;
|
|
@@ -1789,10 +2036,10 @@ async function selectScriptLanguage(currentLang) {
|
|
|
1789
2036
|
type: "list",
|
|
1790
2037
|
name: "lang",
|
|
1791
2038
|
message: "Select ZCF display language / \u9009\u62E9ZCF\u663E\u793A\u8BED\u8A00",
|
|
1792
|
-
choices: SUPPORTED_LANGS.map((l) => ({
|
|
2039
|
+
choices: addNumbersToChoices(SUPPORTED_LANGS.map((l) => ({
|
|
1793
2040
|
name: LANG_LABELS[l],
|
|
1794
2041
|
value: l
|
|
1795
|
-
}))
|
|
2042
|
+
})))
|
|
1796
2043
|
});
|
|
1797
2044
|
if (!lang) {
|
|
1798
2045
|
console.log(ansis.yellow("Operation cancelled / \u64CD\u4F5C\u5DF2\u53D6\u6D88"));
|
|
@@ -1806,19 +2053,19 @@ async function selectScriptLanguage(currentLang) {
|
|
|
1806
2053
|
return scriptLang;
|
|
1807
2054
|
}
|
|
1808
2055
|
async function resolveAiOutputLanguage(scriptLang, commandLineOption, savedConfig) {
|
|
1809
|
-
const i18n =
|
|
2056
|
+
const i18n = getTranslation(scriptLang);
|
|
1810
2057
|
if (commandLineOption) {
|
|
1811
2058
|
return commandLineOption;
|
|
1812
2059
|
}
|
|
1813
2060
|
if (savedConfig?.aiOutputLang) {
|
|
1814
|
-
console.log(ansis.gray(`\u2714 ${i18n.aiOutputLangHint}: ${savedConfig.aiOutputLang}`));
|
|
2061
|
+
console.log(ansis.gray(`\u2714 ${i18n.language.aiOutputLangHint}: ${savedConfig.aiOutputLang}`));
|
|
1815
2062
|
return savedConfig.aiOutputLang;
|
|
1816
2063
|
}
|
|
1817
2064
|
return await selectAiOutputLanguage(scriptLang, scriptLang);
|
|
1818
2065
|
}
|
|
1819
2066
|
|
|
1820
2067
|
async function selectMcpServices(scriptLang) {
|
|
1821
|
-
const i18n =
|
|
2068
|
+
const i18n = getTranslation(scriptLang);
|
|
1822
2069
|
const choices = MCP_SERVICES.map((service) => ({
|
|
1823
2070
|
name: `${service.name[scriptLang]} - ${ansis.gray(service.description[scriptLang])}`,
|
|
1824
2071
|
value: service.id,
|
|
@@ -1827,11 +2074,11 @@ async function selectMcpServices(scriptLang) {
|
|
|
1827
2074
|
const { services } = await inquirer.prompt({
|
|
1828
2075
|
type: "checkbox",
|
|
1829
2076
|
name: "services",
|
|
1830
|
-
message: `${i18n.selectMcpServices}${i18n.multiSelectHint}`,
|
|
2077
|
+
message: `${i18n.mcp.selectMcpServices}${i18n.common.multiSelectHint}`,
|
|
1831
2078
|
choices
|
|
1832
2079
|
});
|
|
1833
2080
|
if (services === void 0) {
|
|
1834
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2081
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1835
2082
|
return void 0;
|
|
1836
2083
|
}
|
|
1837
2084
|
return services;
|
|
@@ -1891,11 +2138,11 @@ function getRootDir() {
|
|
|
1891
2138
|
return dirname(distDir);
|
|
1892
2139
|
}
|
|
1893
2140
|
async function selectAndInstallWorkflows(configLang, scriptLang) {
|
|
1894
|
-
const i18n =
|
|
2141
|
+
const i18n = getTranslation(scriptLang);
|
|
1895
2142
|
const workflows = getOrderedWorkflows();
|
|
1896
2143
|
const choices = workflows.map((workflow) => {
|
|
1897
2144
|
const nameKey = workflow.id;
|
|
1898
|
-
const name = i18n.workflowOption[nameKey] || workflow.id;
|
|
2145
|
+
const name = i18n.workflow.workflowOption[nameKey] || workflow.id;
|
|
1899
2146
|
return {
|
|
1900
2147
|
name,
|
|
1901
2148
|
value: workflow.id,
|
|
@@ -1905,11 +2152,11 @@ async function selectAndInstallWorkflows(configLang, scriptLang) {
|
|
|
1905
2152
|
const { selectedWorkflows } = await inquirer.prompt({
|
|
1906
2153
|
type: "checkbox",
|
|
1907
2154
|
name: "selectedWorkflows",
|
|
1908
|
-
message: `${i18n.selectWorkflowType}${i18n.multiSelectHint}`,
|
|
2155
|
+
message: `${i18n.workflow.selectWorkflowType}${i18n.common.multiSelectHint}`,
|
|
1909
2156
|
choices
|
|
1910
2157
|
});
|
|
1911
2158
|
if (!selectedWorkflows || selectedWorkflows.length === 0) {
|
|
1912
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2159
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
1913
2160
|
return;
|
|
1914
2161
|
}
|
|
1915
2162
|
await cleanupOldVersionFiles(scriptLang);
|
|
@@ -1922,7 +2169,7 @@ async function selectAndInstallWorkflows(configLang, scriptLang) {
|
|
|
1922
2169
|
}
|
|
1923
2170
|
async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
1924
2171
|
const rootDir = getRootDir();
|
|
1925
|
-
const i18n =
|
|
2172
|
+
const i18n = getTranslation(scriptLang);
|
|
1926
2173
|
const result = {
|
|
1927
2174
|
workflow: config.id,
|
|
1928
2175
|
success: true,
|
|
@@ -1930,9 +2177,9 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
|
1930
2177
|
installedAgents: [],
|
|
1931
2178
|
errors: []
|
|
1932
2179
|
};
|
|
1933
|
-
const workflowName = i18n.workflowOption[config.id] || config.id;
|
|
2180
|
+
const workflowName = i18n.workflow.workflowOption[config.id] || config.id;
|
|
1934
2181
|
console.log(ansis.cyan(`
|
|
1935
|
-
\u{1F4E6} ${i18n.installingWorkflow}: ${workflowName}...`));
|
|
2182
|
+
\u{1F4E6} ${i18n.workflow.installingWorkflow}: ${workflowName}...`));
|
|
1936
2183
|
const commandsDir = join(CLAUDE_DIR, "commands", "zcf");
|
|
1937
2184
|
if (!existsSync(commandsDir)) {
|
|
1938
2185
|
await mkdir(commandsDir, { recursive: true });
|
|
@@ -1945,9 +2192,9 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
|
1945
2192
|
try {
|
|
1946
2193
|
await copyFile$1(commandSource, commandDest);
|
|
1947
2194
|
result.installedCommands.push(destFileName);
|
|
1948
|
-
console.log(ansis.gray(` \u2714 ${i18n.installedCommand}: zcf/${destFileName}`));
|
|
2195
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.installedCommand}: zcf/${destFileName}`));
|
|
1949
2196
|
} catch (error) {
|
|
1950
|
-
const errorMsg = `${i18n.failedToInstallCommand} ${commandFile}: ${error}`;
|
|
2197
|
+
const errorMsg = `${i18n.workflow.failedToInstallCommand} ${commandFile}: ${error}`;
|
|
1951
2198
|
result.errors?.push(errorMsg);
|
|
1952
2199
|
console.error(ansis.red(` \u2717 ${errorMsg}`));
|
|
1953
2200
|
result.success = false;
|
|
@@ -1966,9 +2213,9 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
|
1966
2213
|
try {
|
|
1967
2214
|
await copyFile$1(agentSource, agentDest);
|
|
1968
2215
|
result.installedAgents.push(agent.filename);
|
|
1969
|
-
console.log(ansis.gray(` \u2714 ${i18n.installedAgent}: zcf/${config.category}/${agent.filename}`));
|
|
2216
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.installedAgent}: zcf/${config.category}/${agent.filename}`));
|
|
1970
2217
|
} catch (error) {
|
|
1971
|
-
const errorMsg = `${i18n.failedToInstallAgent} ${agent.filename}: ${error}`;
|
|
2218
|
+
const errorMsg = `${i18n.workflow.failedToInstallAgent} ${agent.filename}: ${error}`;
|
|
1972
2219
|
result.errors?.push(errorMsg);
|
|
1973
2220
|
console.error(ansis.red(` \u2717 ${errorMsg}`));
|
|
1974
2221
|
if (agent.required) {
|
|
@@ -1979,20 +2226,20 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
|
1979
2226
|
}
|
|
1980
2227
|
}
|
|
1981
2228
|
if (result.success) {
|
|
1982
|
-
console.log(ansis.green(`\u2714 ${workflowName} ${i18n.workflowInstallSuccess}`));
|
|
2229
|
+
console.log(ansis.green(`\u2714 ${workflowName} ${i18n.workflow.workflowInstallSuccess}`));
|
|
1983
2230
|
if (config.id === "bmadWorkflow") {
|
|
1984
2231
|
console.log(ansis.cyan(`
|
|
1985
|
-
${i18n.bmadInitPrompt}`));
|
|
2232
|
+
${i18n.bmad.bmadInitPrompt}`));
|
|
1986
2233
|
}
|
|
1987
2234
|
} else {
|
|
1988
|
-
console.log(ansis.red(`\u2717 ${workflowName} ${i18n.workflowInstallError}`));
|
|
2235
|
+
console.log(ansis.red(`\u2717 ${workflowName} ${i18n.workflow.workflowInstallError}`));
|
|
1989
2236
|
}
|
|
1990
2237
|
return result;
|
|
1991
2238
|
}
|
|
1992
2239
|
async function cleanupOldVersionFiles(scriptLang) {
|
|
1993
|
-
const i18n =
|
|
2240
|
+
const i18n = getTranslation(scriptLang);
|
|
1994
2241
|
console.log(ansis.cyan(`
|
|
1995
|
-
\u{1F9F9} ${i18n.cleaningOldFiles || "Cleaning up old version files"}...`));
|
|
2242
|
+
\u{1F9F9} ${i18n.workflow.cleaningOldFiles || "Cleaning up old version files"}...`));
|
|
1996
2243
|
const oldCommandFiles = [
|
|
1997
2244
|
join(CLAUDE_DIR, "commands", "workflow.md"),
|
|
1998
2245
|
join(CLAUDE_DIR, "commands", "feat.md")
|
|
@@ -2005,9 +2252,9 @@ async function cleanupOldVersionFiles(scriptLang) {
|
|
|
2005
2252
|
if (existsSync(file)) {
|
|
2006
2253
|
try {
|
|
2007
2254
|
await rm(file, { force: true });
|
|
2008
|
-
console.log(ansis.gray(` \u2714 ${i18n.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2255
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2009
2256
|
} catch (error) {
|
|
2010
|
-
console.error(ansis.yellow(` \u26A0 ${i18n.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2257
|
+
console.error(ansis.yellow(` \u26A0 ${i18n.workflow.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2011
2258
|
}
|
|
2012
2259
|
}
|
|
2013
2260
|
}
|
|
@@ -2015,14 +2262,466 @@ async function cleanupOldVersionFiles(scriptLang) {
|
|
|
2015
2262
|
if (existsSync(file)) {
|
|
2016
2263
|
try {
|
|
2017
2264
|
await rm(file, { force: true });
|
|
2018
|
-
console.log(ansis.gray(` \u2714 ${i18n.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2265
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2019
2266
|
} catch (error) {
|
|
2020
|
-
console.error(ansis.yellow(` \u26A0 ${i18n.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2267
|
+
console.error(ansis.yellow(` \u26A0 ${i18n.workflow.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2021
2268
|
}
|
|
2022
2269
|
}
|
|
2023
2270
|
}
|
|
2024
2271
|
}
|
|
2025
2272
|
|
|
2273
|
+
const execAsync$1 = promisify(exec$1);
|
|
2274
|
+
async function isCcrInstalled() {
|
|
2275
|
+
try {
|
|
2276
|
+
await execAsync$1("ccr version");
|
|
2277
|
+
return true;
|
|
2278
|
+
} catch {
|
|
2279
|
+
try {
|
|
2280
|
+
await execAsync$1("which ccr");
|
|
2281
|
+
return true;
|
|
2282
|
+
} catch {
|
|
2283
|
+
return false;
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
async function installCcr(scriptLang) {
|
|
2288
|
+
const i18n = getTranslation(scriptLang);
|
|
2289
|
+
const installed = await isCcrInstalled();
|
|
2290
|
+
if (installed) {
|
|
2291
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
console.log(ansis.cyan(`\u{1F4E6} ${i18n.ccr.installingCcr}`));
|
|
2295
|
+
try {
|
|
2296
|
+
await execAsync$1("npm install -g claude-code-router --force");
|
|
2297
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrInstallSuccess}`));
|
|
2298
|
+
} catch (error) {
|
|
2299
|
+
if (error.message?.includes("EEXIST")) {
|
|
2300
|
+
console.log(ansis.yellow(`\u26A0 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
console.error(ansis.red(`\u2716 ${i18n.ccr.ccrInstallFailed}`));
|
|
2304
|
+
throw error;
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
const PROVIDER_PRESETS_URL = "https://pub-0dc3e1677e894f07bbea11b17a29e032.r2.dev/providers.json";
|
|
2309
|
+
async function fetchProviderPresets() {
|
|
2310
|
+
try {
|
|
2311
|
+
const controller = new AbortController();
|
|
2312
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
2313
|
+
const response = await fetch(PROVIDER_PRESETS_URL, {
|
|
2314
|
+
signal: controller.signal
|
|
2315
|
+
});
|
|
2316
|
+
clearTimeout(timeoutId);
|
|
2317
|
+
if (!response.ok) {
|
|
2318
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
2319
|
+
}
|
|
2320
|
+
const data = await response.json();
|
|
2321
|
+
const presets = [];
|
|
2322
|
+
if (Array.isArray(data)) {
|
|
2323
|
+
for (const provider of data) {
|
|
2324
|
+
if (provider && typeof provider === "object") {
|
|
2325
|
+
presets.push({
|
|
2326
|
+
name: provider.name || "",
|
|
2327
|
+
provider: provider.name || "",
|
|
2328
|
+
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
2329
|
+
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
2330
|
+
models: provider.models || [],
|
|
2331
|
+
description: provider.description || provider.name || "",
|
|
2332
|
+
transformer: provider.transformer
|
|
2333
|
+
});
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
} else if (data && typeof data === "object") {
|
|
2337
|
+
for (const [key, value] of Object.entries(data)) {
|
|
2338
|
+
if (typeof value === "object" && value !== null) {
|
|
2339
|
+
const provider = value;
|
|
2340
|
+
presets.push({
|
|
2341
|
+
name: provider.name || key,
|
|
2342
|
+
provider: key,
|
|
2343
|
+
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
2344
|
+
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
2345
|
+
models: provider.models || [],
|
|
2346
|
+
description: provider.description || "",
|
|
2347
|
+
transformer: provider.transformer
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
return presets;
|
|
2353
|
+
} catch (error) {
|
|
2354
|
+
return getFallbackPresets();
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
function getFallbackPresets() {
|
|
2358
|
+
return [
|
|
2359
|
+
{
|
|
2360
|
+
name: "dashscope",
|
|
2361
|
+
provider: "dashscope",
|
|
2362
|
+
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
|
|
2363
|
+
requiresApiKey: true,
|
|
2364
|
+
models: ["qwen3-coder-plus"],
|
|
2365
|
+
description: "Alibaba DashScope",
|
|
2366
|
+
transformer: {
|
|
2367
|
+
use: [["maxtoken", { max_tokens: 65536 }]],
|
|
2368
|
+
"qwen3-coder-plus": {
|
|
2369
|
+
use: ["enhancetool"]
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
},
|
|
2373
|
+
{
|
|
2374
|
+
name: "deepseek",
|
|
2375
|
+
provider: "deepseek",
|
|
2376
|
+
baseURL: "https://api.deepseek.com/chat/completions",
|
|
2377
|
+
requiresApiKey: true,
|
|
2378
|
+
models: ["deepseek-chat", "deepseek-reasoner"],
|
|
2379
|
+
description: "DeepSeek AI models",
|
|
2380
|
+
transformer: {
|
|
2381
|
+
use: ["deepseek"],
|
|
2382
|
+
"deepseek-chat": {
|
|
2383
|
+
use: ["tooluse"]
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
},
|
|
2387
|
+
{
|
|
2388
|
+
name: "gemini",
|
|
2389
|
+
provider: "gemini",
|
|
2390
|
+
baseURL: "https://generativelanguage.googleapis.com/v1beta/models/",
|
|
2391
|
+
requiresApiKey: true,
|
|
2392
|
+
models: ["gemini-2.5-flash", "gemini-2.5-pro"],
|
|
2393
|
+
description: "Google Gemini models",
|
|
2394
|
+
transformer: {
|
|
2395
|
+
use: ["gemini"]
|
|
2396
|
+
}
|
|
2397
|
+
},
|
|
2398
|
+
{
|
|
2399
|
+
name: "modelscope",
|
|
2400
|
+
provider: "modelscope",
|
|
2401
|
+
baseURL: "https://api-inference.modelscope.cn/v1/chat/completions",
|
|
2402
|
+
requiresApiKey: true,
|
|
2403
|
+
models: ["Qwen/Qwen3-Coder-480B-A35B-Instruct", "Qwen/Qwen3-235B-A22B-Thinking-2507", "ZhipuAI/GLM-4.5"],
|
|
2404
|
+
description: "ModelScope AI models",
|
|
2405
|
+
transformer: {
|
|
2406
|
+
use: [["maxtoken", { max_tokens: 65536 }]],
|
|
2407
|
+
"Qwen/Qwen3-Coder-480B-A35B-Instruct": {
|
|
2408
|
+
use: ["enhancetool"]
|
|
2409
|
+
},
|
|
2410
|
+
"Qwen/Qwen3-235B-A22B-Thinking-2507": {
|
|
2411
|
+
use: ["reasoning"]
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
},
|
|
2415
|
+
{
|
|
2416
|
+
name: "openrouter",
|
|
2417
|
+
provider: "openrouter",
|
|
2418
|
+
baseURL: "https://openrouter.ai/api/v1/chat/completions",
|
|
2419
|
+
requiresApiKey: true,
|
|
2420
|
+
models: [
|
|
2421
|
+
"google/gemini-2.5-pro-preview",
|
|
2422
|
+
"anthropic/claude-sonnet-4",
|
|
2423
|
+
"anthropic/claude-3.5-sonnet",
|
|
2424
|
+
"anthropic/claude-3.7-sonnet:thinking"
|
|
2425
|
+
],
|
|
2426
|
+
description: "OpenRouter API",
|
|
2427
|
+
transformer: {
|
|
2428
|
+
use: ["openrouter"]
|
|
2429
|
+
}
|
|
2430
|
+
},
|
|
2431
|
+
{
|
|
2432
|
+
name: "siliconflow",
|
|
2433
|
+
provider: "siliconflow",
|
|
2434
|
+
baseURL: "https://api.siliconflow.cn/v1/chat/completions",
|
|
2435
|
+
requiresApiKey: true,
|
|
2436
|
+
models: ["moonshotai/Kimi-K2-Instruct"],
|
|
2437
|
+
description: "SiliconFlow AI",
|
|
2438
|
+
transformer: {
|
|
2439
|
+
use: [["maxtoken", { max_tokens: 16384 }]]
|
|
2440
|
+
}
|
|
2441
|
+
},
|
|
2442
|
+
{
|
|
2443
|
+
name: "volcengine",
|
|
2444
|
+
provider: "volcengine",
|
|
2445
|
+
baseURL: "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
|
|
2446
|
+
requiresApiKey: true,
|
|
2447
|
+
models: ["deepseek-v3-250324", "deepseek-r1-250528"],
|
|
2448
|
+
description: "Volcengine AI",
|
|
2449
|
+
transformer: {
|
|
2450
|
+
use: ["deepseek"]
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
];
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
const execAsync = promisify(exec$1);
|
|
2457
|
+
const CCR_CONFIG_DIR = join$1(homedir(), ".claude-code-router");
|
|
2458
|
+
const CCR_CONFIG_FILE = join$1(CCR_CONFIG_DIR, "config.json");
|
|
2459
|
+
const CCR_BACKUP_DIR = CCR_CONFIG_DIR;
|
|
2460
|
+
function ensureCcrConfigDir() {
|
|
2461
|
+
if (!existsSync(CCR_CONFIG_DIR)) {
|
|
2462
|
+
mkdirSync(CCR_CONFIG_DIR, { recursive: true });
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
function backupCcrConfig(scriptLang) {
|
|
2466
|
+
const i18n = getTranslation(scriptLang);
|
|
2467
|
+
try {
|
|
2468
|
+
if (!existsSync(CCR_CONFIG_FILE)) {
|
|
2469
|
+
return null;
|
|
2470
|
+
}
|
|
2471
|
+
const timestamp = dayjs().format("YYYY-MM-DDTHH-mm-ss-SSS") + "Z";
|
|
2472
|
+
const backupFileName = `config.json.${timestamp}.bak`;
|
|
2473
|
+
const backupPath = join$1(CCR_BACKUP_DIR, backupFileName);
|
|
2474
|
+
console.log(ansis.cyan(`${i18n.ccr.backupCcrConfig}`));
|
|
2475
|
+
copyFileSync(CCR_CONFIG_FILE, backupPath);
|
|
2476
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrBackupSuccess.replace("{path}", backupPath)}`));
|
|
2477
|
+
return backupPath;
|
|
2478
|
+
} catch (error) {
|
|
2479
|
+
console.error(ansis.red(`${i18n.ccr.ccrBackupFailed}:`), error.message);
|
|
2480
|
+
return null;
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
function readCcrConfig() {
|
|
2484
|
+
if (!existsSync(CCR_CONFIG_FILE)) {
|
|
2485
|
+
return null;
|
|
2486
|
+
}
|
|
2487
|
+
return readJsonConfig(CCR_CONFIG_FILE);
|
|
2488
|
+
}
|
|
2489
|
+
function writeCcrConfig(config) {
|
|
2490
|
+
ensureCcrConfigDir();
|
|
2491
|
+
writeJsonConfig(CCR_CONFIG_FILE, config);
|
|
2492
|
+
}
|
|
2493
|
+
async function configureCcrProxy(ccrConfig) {
|
|
2494
|
+
const settings = readJsonConfig(SETTINGS_FILE) || {};
|
|
2495
|
+
const host = ccrConfig.HOST || "127.0.0.1";
|
|
2496
|
+
const port = ccrConfig.PORT || 3456;
|
|
2497
|
+
const apiKey = ccrConfig.APIKEY || "sk-zcf-x-ccr";
|
|
2498
|
+
if (!settings.env) {
|
|
2499
|
+
settings.env = {};
|
|
2500
|
+
}
|
|
2501
|
+
settings.env.ANTHROPIC_BASE_URL = `http://${host}:${port}`;
|
|
2502
|
+
settings.env.ANTHROPIC_API_KEY = apiKey;
|
|
2503
|
+
writeJsonConfig(SETTINGS_FILE, settings);
|
|
2504
|
+
}
|
|
2505
|
+
async function selectCcrPreset(scriptLang) {
|
|
2506
|
+
const i18n = getTranslation(scriptLang);
|
|
2507
|
+
console.log(ansis.cyan(`${i18n.ccr.fetchingPresets}`));
|
|
2508
|
+
const presets = await fetchProviderPresets();
|
|
2509
|
+
if (!presets || presets.length === 0) {
|
|
2510
|
+
console.log(ansis.yellow(`${i18n.ccr.noPresetsAvailable}`));
|
|
2511
|
+
return null;
|
|
2512
|
+
}
|
|
2513
|
+
try {
|
|
2514
|
+
const choices = [
|
|
2515
|
+
...presets.map((p, index) => ({
|
|
2516
|
+
name: `${index + 1}. ${p.name}`,
|
|
2517
|
+
value: p
|
|
2518
|
+
})),
|
|
2519
|
+
{
|
|
2520
|
+
name: `${presets.length + 1}. ${i18n.ccr.skipOption}`,
|
|
2521
|
+
value: "skip"
|
|
2522
|
+
}
|
|
2523
|
+
];
|
|
2524
|
+
const { preset } = await inquirer.prompt({
|
|
2525
|
+
type: "list",
|
|
2526
|
+
name: "preset",
|
|
2527
|
+
message: i18n.ccr.selectCcrPreset,
|
|
2528
|
+
choices
|
|
2529
|
+
});
|
|
2530
|
+
return preset;
|
|
2531
|
+
} catch (error) {
|
|
2532
|
+
if (error.name === "ExitPromptError") {
|
|
2533
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2534
|
+
return null;
|
|
2535
|
+
}
|
|
2536
|
+
throw error;
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
async function configureCcrWithPreset(preset, scriptLang) {
|
|
2540
|
+
const i18n = getTranslation(scriptLang);
|
|
2541
|
+
const provider = {
|
|
2542
|
+
name: preset.name,
|
|
2543
|
+
// Use the original name from JSON
|
|
2544
|
+
api_base_url: preset.baseURL || "",
|
|
2545
|
+
api_key: "",
|
|
2546
|
+
models: preset.models
|
|
2547
|
+
};
|
|
2548
|
+
if (preset.transformer) {
|
|
2549
|
+
provider.transformer = preset.transformer;
|
|
2550
|
+
}
|
|
2551
|
+
if (preset.requiresApiKey) {
|
|
2552
|
+
try {
|
|
2553
|
+
const { apiKey } = await inquirer.prompt({
|
|
2554
|
+
type: "input",
|
|
2555
|
+
name: "apiKey",
|
|
2556
|
+
message: i18n.ccr.enterApiKeyForProvider.replace("{provider}", preset.name),
|
|
2557
|
+
validate: (value) => !!value || i18n.api.keyRequired
|
|
2558
|
+
});
|
|
2559
|
+
provider.api_key = apiKey;
|
|
2560
|
+
} catch (error) {
|
|
2561
|
+
if (error.name === "ExitPromptError") {
|
|
2562
|
+
throw error;
|
|
2563
|
+
}
|
|
2564
|
+
throw error;
|
|
2565
|
+
}
|
|
2566
|
+
} else {
|
|
2567
|
+
provider.api_key = "sk-free";
|
|
2568
|
+
}
|
|
2569
|
+
let defaultModel = preset.models[0];
|
|
2570
|
+
if (preset.models.length > 1) {
|
|
2571
|
+
try {
|
|
2572
|
+
const { model } = await inquirer.prompt({
|
|
2573
|
+
type: "list",
|
|
2574
|
+
name: "model",
|
|
2575
|
+
message: i18n.ccr.selectDefaultModelForProvider.replace("{provider}", preset.name),
|
|
2576
|
+
choices: preset.models.map((m, index) => ({
|
|
2577
|
+
name: `${index + 1}. ${m}`,
|
|
2578
|
+
value: m
|
|
2579
|
+
}))
|
|
2580
|
+
});
|
|
2581
|
+
defaultModel = model;
|
|
2582
|
+
} catch (error) {
|
|
2583
|
+
if (error.name === "ExitPromptError") {
|
|
2584
|
+
throw error;
|
|
2585
|
+
}
|
|
2586
|
+
throw error;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
const router = {
|
|
2590
|
+
default: `${preset.name},${defaultModel}`,
|
|
2591
|
+
// Use the original name
|
|
2592
|
+
background: `${preset.name},${defaultModel}`,
|
|
2593
|
+
think: `${preset.name},${defaultModel}`,
|
|
2594
|
+
longContext: `${preset.name},${defaultModel}`,
|
|
2595
|
+
longContextThreshold: 6e4,
|
|
2596
|
+
webSearch: `${preset.name},${defaultModel}`
|
|
2597
|
+
};
|
|
2598
|
+
const config = {
|
|
2599
|
+
LOG: true,
|
|
2600
|
+
CLAUDE_PATH: "",
|
|
2601
|
+
HOST: "127.0.0.1",
|
|
2602
|
+
PORT: 3456,
|
|
2603
|
+
APIKEY: "sk-zcf-x-ccr",
|
|
2604
|
+
API_TIMEOUT_MS: "600000",
|
|
2605
|
+
PROXY_URL: "",
|
|
2606
|
+
transformers: [],
|
|
2607
|
+
Providers: [provider],
|
|
2608
|
+
Router: router
|
|
2609
|
+
};
|
|
2610
|
+
return config;
|
|
2611
|
+
}
|
|
2612
|
+
async function restartAndCheckCcrStatus(scriptLang) {
|
|
2613
|
+
const i18n = getTranslation(scriptLang);
|
|
2614
|
+
try {
|
|
2615
|
+
console.log(ansis.cyan(`${i18n.ccr.restartingCcr}`));
|
|
2616
|
+
await execAsync("ccr restart");
|
|
2617
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrRestartSuccess}`));
|
|
2618
|
+
console.log(ansis.cyan(`${i18n.ccr.checkingCcrStatus}`));
|
|
2619
|
+
const { stdout } = await execAsync("ccr status");
|
|
2620
|
+
console.log(ansis.gray(stdout));
|
|
2621
|
+
} catch (error) {
|
|
2622
|
+
console.error(ansis.red(`${i18n.ccr.ccrRestartFailed}:`), error.message || error);
|
|
2623
|
+
if (process.env.DEBUG) {
|
|
2624
|
+
console.error("Full error:", error);
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
function showConfigurationTips(scriptLang, apiKey) {
|
|
2629
|
+
const i18n = getTranslation(scriptLang);
|
|
2630
|
+
console.log(ansis.bold.cyan(`
|
|
2631
|
+
\u{1F4CC} ${i18n.ccr.configTips}:`));
|
|
2632
|
+
console.log(ansis.blue(` \u2022 ${i18n.ccr.advancedConfigTip}`));
|
|
2633
|
+
console.log(ansis.blue(` \u2022 ${i18n.ccr.manualConfigTip}`));
|
|
2634
|
+
console.log(ansis.bold.yellow(` \u2022 ${i18n.ccr.useClaudeCommand}`));
|
|
2635
|
+
if (apiKey) {
|
|
2636
|
+
console.log(ansis.bold.green(` \u2022 ${i18n.ccr.ccrUiApiKey || "CCR UI API Key"}: ${apiKey}`));
|
|
2637
|
+
console.log(ansis.gray(` ${i18n.ccr.ccrUiApiKeyHint || "Use this API key to login to CCR UI"}`));
|
|
2638
|
+
}
|
|
2639
|
+
console.log("");
|
|
2640
|
+
}
|
|
2641
|
+
async function setupCcrConfiguration(scriptLang) {
|
|
2642
|
+
const i18n = getTranslation(scriptLang);
|
|
2643
|
+
try {
|
|
2644
|
+
const existingConfig = readCcrConfig();
|
|
2645
|
+
if (existingConfig) {
|
|
2646
|
+
console.log(ansis.blue(`\u2139 ${i18n.ccr.existingCcrConfig}`));
|
|
2647
|
+
let shouldBackupAndReconfigure = false;
|
|
2648
|
+
try {
|
|
2649
|
+
const result = await inquirer.prompt({
|
|
2650
|
+
type: "confirm",
|
|
2651
|
+
name: "overwrite",
|
|
2652
|
+
message: i18n.ccr.overwriteCcrConfig,
|
|
2653
|
+
default: false
|
|
2654
|
+
});
|
|
2655
|
+
shouldBackupAndReconfigure = result.overwrite;
|
|
2656
|
+
} catch (error) {
|
|
2657
|
+
if (error.name === "ExitPromptError") {
|
|
2658
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2659
|
+
return false;
|
|
2660
|
+
}
|
|
2661
|
+
throw error;
|
|
2662
|
+
}
|
|
2663
|
+
if (!shouldBackupAndReconfigure) {
|
|
2664
|
+
console.log(ansis.yellow(`${i18n.ccr.keepingExistingConfig}`));
|
|
2665
|
+
await configureCcrProxy(existingConfig);
|
|
2666
|
+
return true;
|
|
2667
|
+
}
|
|
2668
|
+
backupCcrConfig(scriptLang);
|
|
2669
|
+
}
|
|
2670
|
+
const preset = await selectCcrPreset(scriptLang);
|
|
2671
|
+
if (!preset) {
|
|
2672
|
+
return false;
|
|
2673
|
+
}
|
|
2674
|
+
let config;
|
|
2675
|
+
if (preset === "skip") {
|
|
2676
|
+
console.log(ansis.yellow(`${i18n.ccr.skipConfiguring}`));
|
|
2677
|
+
config = {
|
|
2678
|
+
LOG: false,
|
|
2679
|
+
CLAUDE_PATH: "",
|
|
2680
|
+
HOST: "127.0.0.1",
|
|
2681
|
+
PORT: 3456,
|
|
2682
|
+
APIKEY: "sk-zcf-x-ccr",
|
|
2683
|
+
API_TIMEOUT_MS: "600000",
|
|
2684
|
+
PROXY_URL: "",
|
|
2685
|
+
transformers: [],
|
|
2686
|
+
Providers: [],
|
|
2687
|
+
// Empty providers array
|
|
2688
|
+
Router: {
|
|
2689
|
+
// Empty router configuration - user will configure in CCR UI
|
|
2690
|
+
}
|
|
2691
|
+
};
|
|
2692
|
+
} else {
|
|
2693
|
+
config = await configureCcrWithPreset(preset, scriptLang);
|
|
2694
|
+
}
|
|
2695
|
+
writeCcrConfig(config);
|
|
2696
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrConfigSuccess}`));
|
|
2697
|
+
await configureCcrProxy(config);
|
|
2698
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.proxyConfigSuccess}`));
|
|
2699
|
+
await restartAndCheckCcrStatus(scriptLang);
|
|
2700
|
+
showConfigurationTips(scriptLang, config.APIKEY);
|
|
2701
|
+
try {
|
|
2702
|
+
addCompletedOnboarding();
|
|
2703
|
+
} catch (error) {
|
|
2704
|
+
console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
|
|
2705
|
+
}
|
|
2706
|
+
return true;
|
|
2707
|
+
} catch (error) {
|
|
2708
|
+
if (error.name === "ExitPromptError") {
|
|
2709
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2710
|
+
return false;
|
|
2711
|
+
}
|
|
2712
|
+
console.error(ansis.red(`${i18n.ccr.ccrConfigFailed}:`), error);
|
|
2713
|
+
return false;
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
async function configureCcrFeature(scriptLang) {
|
|
2717
|
+
const i18n = getTranslation(scriptLang);
|
|
2718
|
+
const backupDir = backupExistingConfig();
|
|
2719
|
+
if (backupDir) {
|
|
2720
|
+
console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
|
|
2721
|
+
}
|
|
2722
|
+
await setupCcrConfiguration(scriptLang);
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2026
2725
|
async function init(options = {}) {
|
|
2027
2726
|
try {
|
|
2028
2727
|
if (!options.skipBanner) {
|
|
@@ -2032,22 +2731,22 @@ async function init(options = {}) {
|
|
|
2032
2731
|
const i18n = I18N[scriptLang];
|
|
2033
2732
|
if (isTermux()) {
|
|
2034
2733
|
console.log(ansis.yellow(`
|
|
2035
|
-
\u2139 ${i18n.termuxDetected}`));
|
|
2036
|
-
console.log(ansis.gray(i18n.termuxEnvironmentInfo));
|
|
2734
|
+
\u2139 ${i18n.installation.termuxDetected}`));
|
|
2735
|
+
console.log(ansis.gray(i18n.installation.termuxEnvironmentInfo));
|
|
2037
2736
|
}
|
|
2038
2737
|
let configLang = options.configLang;
|
|
2039
2738
|
if (!configLang) {
|
|
2040
2739
|
const { lang } = await inquirer.prompt({
|
|
2041
2740
|
type: "list",
|
|
2042
2741
|
name: "lang",
|
|
2043
|
-
message: i18n.selectConfigLang,
|
|
2044
|
-
choices: SUPPORTED_LANGS.map((l) => ({
|
|
2045
|
-
name: `${LANG_LABELS[l]} - ${i18n.configLangHint[l]}`,
|
|
2742
|
+
message: i18n.language.selectConfigLang,
|
|
2743
|
+
choices: addNumbersToChoices(SUPPORTED_LANGS.map((l) => ({
|
|
2744
|
+
name: `${LANG_LABELS[l]} - ${i18n.language.configLangHint[l]}`,
|
|
2046
2745
|
value: l
|
|
2047
|
-
}))
|
|
2746
|
+
})))
|
|
2048
2747
|
});
|
|
2049
2748
|
if (!lang) {
|
|
2050
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2749
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2051
2750
|
process.exit(0);
|
|
2052
2751
|
}
|
|
2053
2752
|
configLang = lang;
|
|
@@ -2059,20 +2758,20 @@ async function init(options = {}) {
|
|
|
2059
2758
|
const { shouldInstall } = await inquirer.prompt({
|
|
2060
2759
|
type: "confirm",
|
|
2061
2760
|
name: "shouldInstall",
|
|
2062
|
-
message: i18n.installPrompt,
|
|
2761
|
+
message: i18n.installation.installPrompt,
|
|
2063
2762
|
default: true
|
|
2064
2763
|
});
|
|
2065
2764
|
if (shouldInstall === void 0) {
|
|
2066
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2765
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2067
2766
|
process.exit(0);
|
|
2068
2767
|
}
|
|
2069
2768
|
if (shouldInstall) {
|
|
2070
2769
|
await installClaudeCode(scriptLang);
|
|
2071
2770
|
} else {
|
|
2072
|
-
console.log(ansis.yellow(i18n.skip));
|
|
2771
|
+
console.log(ansis.yellow(i18n.common.skip));
|
|
2073
2772
|
}
|
|
2074
2773
|
} else {
|
|
2075
|
-
console.log(ansis.green(`\u2714 ${i18n.
|
|
2774
|
+
console.log(ansis.green(`\u2714 ${i18n.installation.alreadyInstalled}`));
|
|
2076
2775
|
}
|
|
2077
2776
|
ensureClaudeDir();
|
|
2078
2777
|
let action = "new";
|
|
@@ -2080,21 +2779,21 @@ async function init(options = {}) {
|
|
|
2080
2779
|
const { action: userAction } = await inquirer.prompt({
|
|
2081
2780
|
type: "list",
|
|
2082
2781
|
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
|
-
]
|
|
2782
|
+
message: i18n.configuration.existingConfig,
|
|
2783
|
+
choices: addNumbersToChoices([
|
|
2784
|
+
{ name: i18n.configuration.backupAndOverwrite, value: "backup" },
|
|
2785
|
+
{ name: i18n.configuration.updateDocsOnly, value: "docs-only" },
|
|
2786
|
+
{ name: i18n.configuration.mergeConfig, value: "merge" },
|
|
2787
|
+
{ name: i18n.common.skip, value: "skip" }
|
|
2788
|
+
])
|
|
2090
2789
|
});
|
|
2091
2790
|
if (!userAction) {
|
|
2092
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2791
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2093
2792
|
process.exit(0);
|
|
2094
2793
|
}
|
|
2095
2794
|
action = userAction;
|
|
2096
2795
|
if (action === "skip") {
|
|
2097
|
-
console.log(ansis.yellow(i18n.skip));
|
|
2796
|
+
console.log(ansis.yellow(i18n.common.skip));
|
|
2098
2797
|
return;
|
|
2099
2798
|
}
|
|
2100
2799
|
}
|
|
@@ -2103,65 +2802,103 @@ async function init(options = {}) {
|
|
|
2103
2802
|
if (action !== "docs-only" && (isNewInstall || ["backup", "merge"].includes(action))) {
|
|
2104
2803
|
const existingApiConfig = getExistingApiConfig();
|
|
2105
2804
|
if (existingApiConfig) {
|
|
2106
|
-
console.log("\n" + ansis.blue(`\u2139 ${i18n.existingApiConfig}`));
|
|
2107
|
-
console.log(ansis.gray(` ${i18n.apiConfigUrl}: ${existingApiConfig.url || i18n.notConfigured}`));
|
|
2805
|
+
console.log("\n" + ansis.blue(`\u2139 ${i18n.api.existingApiConfig}`));
|
|
2806
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${existingApiConfig.url || i18n.common.notConfigured}`));
|
|
2108
2807
|
console.log(
|
|
2109
2808
|
ansis.gray(
|
|
2110
|
-
` ${i18n.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.notConfigured}`
|
|
2809
|
+
` ${i18n.api.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.common.notConfigured}`
|
|
2111
2810
|
)
|
|
2112
2811
|
);
|
|
2113
|
-
console.log(ansis.gray(` ${i18n.apiConfigAuthType}: ${existingApiConfig.authType || i18n.notConfigured}
|
|
2812
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigAuthType}: ${existingApiConfig.authType || i18n.common.notConfigured}
|
|
2114
2813
|
`));
|
|
2115
2814
|
const { action: apiAction } = await inquirer.prompt({
|
|
2116
2815
|
type: "list",
|
|
2117
2816
|
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
|
-
|
|
2817
|
+
message: i18n.api.selectApiAction,
|
|
2818
|
+
choices: addNumbersToChoices([
|
|
2819
|
+
{ name: i18n.api.keepExistingConfig, value: "keep" },
|
|
2820
|
+
{ name: i18n.api.modifyAllConfig, value: "modify-all" },
|
|
2821
|
+
{ name: i18n.api.modifyPartialConfig, value: "modify-partial" },
|
|
2822
|
+
{ name: i18n.api.useCcrProxy, value: "use-ccr" },
|
|
2823
|
+
{ name: i18n.api.skipApi, value: "skip" }
|
|
2824
|
+
])
|
|
2125
2825
|
});
|
|
2126
2826
|
if (!apiAction) {
|
|
2127
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2827
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2128
2828
|
process.exit(0);
|
|
2129
2829
|
}
|
|
2130
2830
|
if (apiAction === "keep" || apiAction === "skip") {
|
|
2131
2831
|
apiConfig = null;
|
|
2832
|
+
if (apiAction === "keep") {
|
|
2833
|
+
try {
|
|
2834
|
+
addCompletedOnboarding();
|
|
2835
|
+
} catch (error) {
|
|
2836
|
+
console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2132
2839
|
} else if (apiAction === "modify-partial") {
|
|
2133
2840
|
await modifyApiConfigPartially(existingApiConfig, i18n, scriptLang);
|
|
2134
2841
|
apiConfig = null;
|
|
2135
2842
|
} else if (apiAction === "modify-all") {
|
|
2136
2843
|
apiConfig = await configureApiCompletely(i18n, scriptLang);
|
|
2844
|
+
} else if (apiAction === "use-ccr") {
|
|
2845
|
+
const ccrInstalled = await isCcrInstalled();
|
|
2846
|
+
if (!ccrInstalled) {
|
|
2847
|
+
console.log(ansis.yellow(`${i18n.ccr.installingCcr}`));
|
|
2848
|
+
await installCcr(scriptLang);
|
|
2849
|
+
} else {
|
|
2850
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
2851
|
+
}
|
|
2852
|
+
const ccrConfigured = await setupCcrConfiguration(scriptLang);
|
|
2853
|
+
if (ccrConfigured) {
|
|
2854
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
|
|
2855
|
+
apiConfig = null;
|
|
2856
|
+
}
|
|
2137
2857
|
}
|
|
2138
2858
|
} else {
|
|
2139
2859
|
const { apiChoice } = await inquirer.prompt({
|
|
2140
2860
|
type: "list",
|
|
2141
2861
|
name: "apiChoice",
|
|
2142
|
-
message: i18n.configureApi,
|
|
2862
|
+
message: i18n.api.configureApi,
|
|
2143
2863
|
choices: [
|
|
2144
2864
|
{
|
|
2145
|
-
name: `${i18n.useAuthToken} - ${ansis.gray(i18n.authTokenDesc)}`,
|
|
2865
|
+
name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
|
|
2146
2866
|
value: "auth_token",
|
|
2147
|
-
short: i18n.useAuthToken
|
|
2867
|
+
short: i18n.api.useAuthToken
|
|
2148
2868
|
},
|
|
2149
2869
|
{
|
|
2150
|
-
name: `${i18n.useApiKey} - ${ansis.gray(i18n.apiKeyDesc)}`,
|
|
2870
|
+
name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
|
|
2151
2871
|
value: "api_key",
|
|
2152
|
-
short: i18n.useApiKey
|
|
2872
|
+
short: i18n.api.useApiKey
|
|
2153
2873
|
},
|
|
2154
2874
|
{
|
|
2155
|
-
name: i18n.
|
|
2875
|
+
name: `${i18n.api.useCcrProxy} - ${ansis.gray(i18n.api.ccrProxyDesc)}`,
|
|
2876
|
+
value: "ccr_proxy",
|
|
2877
|
+
short: i18n.api.useCcrProxy
|
|
2878
|
+
},
|
|
2879
|
+
{
|
|
2880
|
+
name: i18n.api.skipApi,
|
|
2156
2881
|
value: "skip"
|
|
2157
2882
|
}
|
|
2158
2883
|
]
|
|
2159
2884
|
});
|
|
2160
2885
|
if (!apiChoice) {
|
|
2161
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2886
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2162
2887
|
process.exit(0);
|
|
2163
2888
|
}
|
|
2164
|
-
if (apiChoice
|
|
2889
|
+
if (apiChoice === "ccr_proxy") {
|
|
2890
|
+
const ccrInstalled = await isCcrInstalled();
|
|
2891
|
+
if (!ccrInstalled) {
|
|
2892
|
+
await installCcr(scriptLang);
|
|
2893
|
+
} else {
|
|
2894
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
2895
|
+
}
|
|
2896
|
+
const ccrConfigured = await setupCcrConfiguration(scriptLang);
|
|
2897
|
+
if (ccrConfigured) {
|
|
2898
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
|
|
2899
|
+
apiConfig = null;
|
|
2900
|
+
}
|
|
2901
|
+
} else if (apiChoice !== "skip") {
|
|
2165
2902
|
apiConfig = await configureApiCompletely(i18n, scriptLang, apiChoice);
|
|
2166
2903
|
}
|
|
2167
2904
|
}
|
|
@@ -2169,7 +2906,7 @@ async function init(options = {}) {
|
|
|
2169
2906
|
if (["backup", "docs-only", "merge"].includes(action)) {
|
|
2170
2907
|
const backupDir = backupExistingConfig();
|
|
2171
2908
|
if (backupDir) {
|
|
2172
|
-
console.log(ansis.gray(`\u2714 ${i18n.backupSuccess}: ${backupDir}`));
|
|
2909
|
+
console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
|
|
2173
2910
|
}
|
|
2174
2911
|
}
|
|
2175
2912
|
if (action === "docs-only") {
|
|
@@ -2184,30 +2921,25 @@ async function init(options = {}) {
|
|
|
2184
2921
|
if (apiConfig && action !== "docs-only") {
|
|
2185
2922
|
const configuredApi = configureApi(apiConfig);
|
|
2186
2923
|
if (configuredApi) {
|
|
2187
|
-
console.log(ansis.green(`\u2714 ${i18n.apiConfigSuccess}`));
|
|
2924
|
+
console.log(ansis.green(`\u2714 ${i18n.api.apiConfigSuccess}`));
|
|
2188
2925
|
console.log(ansis.gray(` URL: ${configuredApi.url}`));
|
|
2189
2926
|
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
2927
|
}
|
|
2196
2928
|
}
|
|
2197
2929
|
if (action !== "docs-only") {
|
|
2198
2930
|
const { shouldConfigureMcp } = await inquirer.prompt({
|
|
2199
2931
|
type: "confirm",
|
|
2200
2932
|
name: "shouldConfigureMcp",
|
|
2201
|
-
message: i18n.configureMcp,
|
|
2933
|
+
message: i18n.mcp.configureMcp,
|
|
2202
2934
|
default: true
|
|
2203
2935
|
});
|
|
2204
2936
|
if (shouldConfigureMcp === void 0) {
|
|
2205
|
-
console.log(ansis.yellow(i18n.cancelled));
|
|
2937
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2206
2938
|
process.exit(0);
|
|
2207
2939
|
}
|
|
2208
2940
|
if (shouldConfigureMcp) {
|
|
2209
2941
|
if (isWindows()) {
|
|
2210
|
-
console.log(ansis.blue(`\u2139 ${
|
|
2942
|
+
console.log(ansis.blue(`\u2139 ${i18n.installation.windowsDetected}`));
|
|
2211
2943
|
}
|
|
2212
2944
|
const selectedServices = await selectMcpServices(scriptLang);
|
|
2213
2945
|
if (selectedServices === void 0) {
|
|
@@ -2216,7 +2948,7 @@ async function init(options = {}) {
|
|
|
2216
2948
|
if (selectedServices.length > 0) {
|
|
2217
2949
|
const mcpBackupPath = backupMcpConfig();
|
|
2218
2950
|
if (mcpBackupPath) {
|
|
2219
|
-
console.log(ansis.gray(`\u2714 ${i18n.mcpBackupSuccess}: ${mcpBackupPath}`));
|
|
2951
|
+
console.log(ansis.gray(`\u2714 ${i18n.mcp.mcpBackupSuccess}: ${mcpBackupPath}`));
|
|
2220
2952
|
}
|
|
2221
2953
|
const newServers = {};
|
|
2222
2954
|
for (const serviceId of selectedServices) {
|
|
@@ -2228,10 +2960,10 @@ async function init(options = {}) {
|
|
|
2228
2960
|
type: "input",
|
|
2229
2961
|
name: "apiKey",
|
|
2230
2962
|
message: service.apiKeyPrompt[scriptLang],
|
|
2231
|
-
validate: (value) => !!value || i18n.keyRequired
|
|
2963
|
+
validate: (value) => !!value || i18n.api.keyRequired
|
|
2232
2964
|
});
|
|
2233
2965
|
if (apiKey === void 0) {
|
|
2234
|
-
console.log(ansis.yellow(`${i18n.skip}: ${service.name[scriptLang]}`));
|
|
2966
|
+
console.log(ansis.yellow(`${i18n.common.skip}: ${service.name[scriptLang]}`));
|
|
2235
2967
|
continue;
|
|
2236
2968
|
}
|
|
2237
2969
|
if (apiKey) {
|
|
@@ -2247,9 +2979,9 @@ async function init(options = {}) {
|
|
|
2247
2979
|
mergedConfig = fixWindowsMcpConfig(mergedConfig);
|
|
2248
2980
|
try {
|
|
2249
2981
|
writeMcpConfig(mergedConfig);
|
|
2250
|
-
console.log(ansis.green(`\u2714 ${i18n.mcpConfigSuccess}`));
|
|
2982
|
+
console.log(ansis.green(`\u2714 ${i18n.mcp.mcpConfigSuccess}`));
|
|
2251
2983
|
} catch (error) {
|
|
2252
|
-
console.error(ansis.red(`${i18n.failedToWriteMcpConfig} ${error}`));
|
|
2984
|
+
console.error(ansis.red(`${i18n.configuration.failedToWriteMcpConfig} ${error}`));
|
|
2253
2985
|
}
|
|
2254
2986
|
}
|
|
2255
2987
|
}
|
|
@@ -2259,8 +2991,8 @@ async function init(options = {}) {
|
|
|
2259
2991
|
preferredLang: scriptLang,
|
|
2260
2992
|
aiOutputLang
|
|
2261
2993
|
});
|
|
2262
|
-
console.log(ansis.green(`\u2714 ${i18n.configSuccess} ${CLAUDE_DIR}`));
|
|
2263
|
-
console.log("\n" + ansis.cyan(i18n.complete));
|
|
2994
|
+
console.log(ansis.green(`\u2714 ${i18n.configuration.configSuccess} ${CLAUDE_DIR}`));
|
|
2995
|
+
console.log("\n" + ansis.cyan(i18n.common.complete));
|
|
2264
2996
|
} catch (error) {
|
|
2265
2997
|
if (!handleExitPromptError(error)) {
|
|
2266
2998
|
handleGeneralError(error, options.lang);
|
|
@@ -2346,4 +3078,4 @@ async function openSettingsJson() {
|
|
|
2346
3078
|
}
|
|
2347
3079
|
}
|
|
2348
3080
|
|
|
2349
|
-
export {
|
|
3081
|
+
export { readCcrConfig as $, AI_OUTPUT_LANGUAGES as A, backupMcpConfig as B, CLAUDE_DIR as C, mergeMcpServers as D, buildMcpServerConfig as E, fixWindowsMcpConfig as F, addCompletedOnboarding as G, getTranslation as H, I18N as I, addNumbersToChoices as J, updateZcfConfig as K, LEGACY_ZCF_CONFIG_FILE as L, MCP_SERVICES as M, readZcfConfig as N, resolveAiOutputLanguage 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, updatePromptOnly as a5, selectAndInstallWorkflows as a6, version as a7, displayBannerWithInfo as a8, 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, getExistingApiConfig as v, applyAiLanguageDirective as w, getMcpConfigPath as x, readMcpConfig as y, writeMcpConfig as z };
|