zcf 2.9.11 → 2.10.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 +32 -0
- package/dist/chunks/simple-config.mjs +1551 -1350
- package/dist/cli.mjs +31 -3
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.mjs +6 -6
- package/package.json +1 -1
|
@@ -1,23 +1,83 @@
|
|
|
1
|
-
import inquirer from 'inquirer';
|
|
2
1
|
import ansis from 'ansis';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
3
|
import { existsSync, mkdirSync, copyFileSync, writeFileSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
4
4
|
import { join, dirname } from 'pathe';
|
|
5
5
|
import dayjs from 'dayjs';
|
|
6
|
+
import { exec as exec$1 } from 'node:child_process';
|
|
7
|
+
import { homedir, platform } from 'node:os';
|
|
8
|
+
import { join as join$1 } from 'node:path';
|
|
9
|
+
import { promisify } from 'node:util';
|
|
6
10
|
import { fileURLToPath } from 'node:url';
|
|
7
|
-
import { exec } from '
|
|
8
|
-
import {
|
|
9
|
-
import { promisify } from 'util';
|
|
11
|
+
import { exec as exec$2 } from 'child_process';
|
|
12
|
+
import { promisify as promisify$1 } from 'util';
|
|
10
13
|
import ora from 'ora';
|
|
11
14
|
import semver from 'semver';
|
|
15
|
+
import { exec } from 'tinyexec';
|
|
12
16
|
import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
|
|
13
|
-
import { exec as exec$2 } from 'node:child_process';
|
|
14
|
-
import { promisify as promisify$1 } from 'node:util';
|
|
15
|
-
import { homedir, platform } from 'node:os';
|
|
16
|
-
import { join as join$1 } from 'node:path';
|
|
17
17
|
|
|
18
|
-
const version = "2.
|
|
18
|
+
const version = "2.10.0";
|
|
19
19
|
const homepage = "https://github.com/UfoMiao/zcf";
|
|
20
20
|
|
|
21
|
+
const WORKFLOW_CONFIGS = [
|
|
22
|
+
{
|
|
23
|
+
id: "sixStepsWorkflow",
|
|
24
|
+
nameKey: "workflowOption.sixStepsWorkflow",
|
|
25
|
+
descriptionKey: "workflowDescription.sixStepsWorkflow",
|
|
26
|
+
defaultSelected: true,
|
|
27
|
+
order: 1,
|
|
28
|
+
commands: ["workflow.md"],
|
|
29
|
+
agents: [],
|
|
30
|
+
autoInstallAgents: false,
|
|
31
|
+
category: "sixStep",
|
|
32
|
+
outputDir: "workflow"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "featPlanUx",
|
|
36
|
+
nameKey: "workflowOption.featPlanUx",
|
|
37
|
+
descriptionKey: "workflowDescription.featPlanUx",
|
|
38
|
+
defaultSelected: true,
|
|
39
|
+
order: 2,
|
|
40
|
+
commands: ["feat.md"],
|
|
41
|
+
agents: [
|
|
42
|
+
{ id: "planner", filename: "planner.md", required: true },
|
|
43
|
+
{ id: "ui-ux-designer", filename: "ui-ux-designer.md", required: true }
|
|
44
|
+
],
|
|
45
|
+
autoInstallAgents: true,
|
|
46
|
+
category: "plan",
|
|
47
|
+
outputDir: "feat"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "gitWorkflow",
|
|
51
|
+
nameKey: "workflowOption.gitWorkflow",
|
|
52
|
+
descriptionKey: "workflowDescription.gitWorkflow",
|
|
53
|
+
defaultSelected: true,
|
|
54
|
+
order: 3,
|
|
55
|
+
commands: ["git-commit.md", "git-rollback.md", "git-cleanBranches.md", "git-worktree.md"],
|
|
56
|
+
agents: [],
|
|
57
|
+
autoInstallAgents: false,
|
|
58
|
+
category: "git",
|
|
59
|
+
outputDir: "git"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "bmadWorkflow",
|
|
63
|
+
nameKey: "workflowOption.bmadWorkflow",
|
|
64
|
+
descriptionKey: "workflowDescription.bmadWorkflow",
|
|
65
|
+
defaultSelected: true,
|
|
66
|
+
order: 4,
|
|
67
|
+
commands: ["bmad-init.md"],
|
|
68
|
+
agents: [],
|
|
69
|
+
autoInstallAgents: false,
|
|
70
|
+
category: "bmad",
|
|
71
|
+
outputDir: "bmad"
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
function getWorkflowConfig(workflowId) {
|
|
75
|
+
return WORKFLOW_CONFIGS.find((config) => config.id === workflowId);
|
|
76
|
+
}
|
|
77
|
+
function getOrderedWorkflows() {
|
|
78
|
+
return [...WORKFLOW_CONFIGS].sort((a, b) => a.order - b.order);
|
|
79
|
+
}
|
|
80
|
+
|
|
21
81
|
const common$1 = {
|
|
22
82
|
// Basic
|
|
23
83
|
multiSelectHint: "\uFF08\u7A7A\u683C\u9009\u62E9\uFF0Ca\u5168\u9009\uFF0Ci\u53CD\u9009\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
|
|
@@ -1129,21 +1189,6 @@ const MCP_SERVICES = [
|
|
|
1129
1189
|
}
|
|
1130
1190
|
];
|
|
1131
1191
|
|
|
1132
|
-
function addNumbersToChoices(choices, startFrom = 1, format = (n) => `${n}. `) {
|
|
1133
|
-
let currentNumber = startFrom;
|
|
1134
|
-
return choices.map((choice) => {
|
|
1135
|
-
if (choice.disabled) {
|
|
1136
|
-
return choice;
|
|
1137
|
-
}
|
|
1138
|
-
const numbered = {
|
|
1139
|
-
...choice,
|
|
1140
|
-
name: `${format(currentNumber)}${choice.name}`
|
|
1141
|
-
};
|
|
1142
|
-
currentNumber++;
|
|
1143
|
-
return numbered;
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
1192
|
class FileSystemError extends Error {
|
|
1148
1193
|
constructor(message, path, cause) {
|
|
1149
1194
|
super(message);
|
|
@@ -1324,6 +1369,21 @@ function updateZcfConfig(updates) {
|
|
|
1324
1369
|
writeZcfConfig(newConfig);
|
|
1325
1370
|
}
|
|
1326
1371
|
|
|
1372
|
+
function addNumbersToChoices(choices, startFrom = 1, format = (n) => `${n}. `) {
|
|
1373
|
+
let currentNumber = startFrom;
|
|
1374
|
+
return choices.map((choice) => {
|
|
1375
|
+
if (choice.disabled) {
|
|
1376
|
+
return choice;
|
|
1377
|
+
}
|
|
1378
|
+
const numbered = {
|
|
1379
|
+
...choice,
|
|
1380
|
+
name: `${format(currentNumber)}${choice.name}`
|
|
1381
|
+
};
|
|
1382
|
+
currentNumber++;
|
|
1383
|
+
return numbered;
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1327
1387
|
const AI_PERSONALITIES = [
|
|
1328
1388
|
{
|
|
1329
1389
|
id: "professional",
|
|
@@ -1370,9 +1430,29 @@ function getExistingPersonality() {
|
|
|
1370
1430
|
function getPersonalityInfo(personalityId) {
|
|
1371
1431
|
return AI_PERSONALITIES.find((p) => p.id === personalityId);
|
|
1372
1432
|
}
|
|
1373
|
-
async function configureAiPersonality(scriptLang,
|
|
1433
|
+
async function configureAiPersonality(scriptLang, preselectedPersonality) {
|
|
1434
|
+
const showExisting = typeof preselectedPersonality === "boolean" ? preselectedPersonality : true;
|
|
1435
|
+
const preselected = typeof preselectedPersonality === "string" ? preselectedPersonality : void 0;
|
|
1374
1436
|
const i18n = getTranslation(scriptLang);
|
|
1375
1437
|
const existingPersonality = getExistingPersonality();
|
|
1438
|
+
if (preselected) {
|
|
1439
|
+
let directive2 = "";
|
|
1440
|
+
if (preselected === "custom") {
|
|
1441
|
+
directive2 = "You are a helpful assistant.";
|
|
1442
|
+
} else {
|
|
1443
|
+
const selected = AI_PERSONALITIES.find((p) => p.id === preselected);
|
|
1444
|
+
if (selected) {
|
|
1445
|
+
directive2 = selected.directive[scriptLang];
|
|
1446
|
+
} else {
|
|
1447
|
+
console.error(ansis.red(`Invalid personality: ${preselected}`));
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
await applyPersonalityDirective(directive2);
|
|
1452
|
+
updateZcfConfig({ aiPersonality: preselected });
|
|
1453
|
+
console.log(ansis.green(`\u2714 ${i18n.configuration.personalityConfigured || "AI personality configured"}`));
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1376
1456
|
if (showExisting && existingPersonality) {
|
|
1377
1457
|
const personalityInfo = getPersonalityInfo(existingPersonality);
|
|
1378
1458
|
if (personalityInfo) {
|
|
@@ -1469,30 +1549,6 @@ function displayBannerWithInfo(subtitle) {
|
|
|
1469
1549
|
`));
|
|
1470
1550
|
}
|
|
1471
1551
|
|
|
1472
|
-
function handleExitPromptError(error) {
|
|
1473
|
-
if (error instanceof Error && error.name === "ExitPromptError") {
|
|
1474
|
-
const zcfConfig = readZcfConfig();
|
|
1475
|
-
const defaultLang = zcfConfig?.preferredLang || "zh-CN";
|
|
1476
|
-
const i18n = getTranslation(defaultLang);
|
|
1477
|
-
console.log(ansis.cyan(`
|
|
1478
|
-
${i18n.common.goodbye}
|
|
1479
|
-
`));
|
|
1480
|
-
process.exit(0);
|
|
1481
|
-
}
|
|
1482
|
-
return false;
|
|
1483
|
-
}
|
|
1484
|
-
function handleGeneralError(error, lang) {
|
|
1485
|
-
const zcfConfig = readZcfConfig();
|
|
1486
|
-
const defaultLang = lang || zcfConfig?.preferredLang || "en";
|
|
1487
|
-
const i18n = getTranslation(defaultLang);
|
|
1488
|
-
const errorMsg = i18n.common.error || "Error";
|
|
1489
|
-
console.error(ansis.red(`${errorMsg}:`), error);
|
|
1490
|
-
if (error instanceof Error) {
|
|
1491
|
-
console.error(ansis.gray(`Stack: ${error.stack}`));
|
|
1492
|
-
}
|
|
1493
|
-
process.exit(1);
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
1552
|
function mergeArraysUnique(arr1, arr2) {
|
|
1497
1553
|
const combined = [...arr1 || [], ...arr2 || []];
|
|
1498
1554
|
return [...new Set(combined)];
|
|
@@ -1927,775 +1983,654 @@ function applyAiLanguageDirective(aiOutputLang) {
|
|
|
1927
1983
|
writeFile(languageFile, directive);
|
|
1928
1984
|
}
|
|
1929
1985
|
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
}
|
|
1938
|
-
return { isValid: true };
|
|
1939
|
-
}
|
|
1940
|
-
function formatApiKeyDisplay(apiKey) {
|
|
1941
|
-
if (!apiKey || apiKey.length < 12) {
|
|
1942
|
-
return apiKey;
|
|
1943
|
-
}
|
|
1944
|
-
return `${apiKey.substring(0, 8)}...${apiKey.substring(apiKey.length - 4)}`;
|
|
1945
|
-
}
|
|
1946
|
-
|
|
1947
|
-
async function configureApiCompletely(i18n, scriptLang, preselectedAuthType) {
|
|
1948
|
-
let authType = preselectedAuthType;
|
|
1949
|
-
if (!authType) {
|
|
1950
|
-
const { authType: selectedAuthType } = await inquirer.prompt({
|
|
1951
|
-
type: "list",
|
|
1952
|
-
name: "authType",
|
|
1953
|
-
message: i18n.api.configureApi,
|
|
1954
|
-
choices: addNumbersToChoices([
|
|
1955
|
-
{
|
|
1956
|
-
name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
|
|
1957
|
-
value: "auth_token",
|
|
1958
|
-
short: i18n.api.useAuthToken
|
|
1959
|
-
},
|
|
1960
|
-
{
|
|
1961
|
-
name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
|
|
1962
|
-
value: "api_key",
|
|
1963
|
-
short: i18n.api.useApiKey
|
|
1964
|
-
}
|
|
1965
|
-
])
|
|
1986
|
+
const PROVIDER_PRESETS_URL = "https://pub-0dc3e1677e894f07bbea11b17a29e032.r2.dev/providers.json";
|
|
1987
|
+
async function fetchProviderPresets() {
|
|
1988
|
+
try {
|
|
1989
|
+
const controller = new AbortController();
|
|
1990
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
1991
|
+
const response = await fetch(PROVIDER_PRESETS_URL, {
|
|
1992
|
+
signal: controller.signal
|
|
1966
1993
|
});
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
}
|
|
1971
|
-
authType = selectedAuthType;
|
|
1972
|
-
}
|
|
1973
|
-
const { url } = await inquirer.prompt({
|
|
1974
|
-
type: "input",
|
|
1975
|
-
name: "url",
|
|
1976
|
-
message: i18n.api.enterApiUrl,
|
|
1977
|
-
validate: (value) => {
|
|
1978
|
-
if (!value) return i18n.api.urlRequired;
|
|
1979
|
-
try {
|
|
1980
|
-
new URL(value);
|
|
1981
|
-
return true;
|
|
1982
|
-
} catch {
|
|
1983
|
-
return i18n.api.invalidUrl;
|
|
1984
|
-
}
|
|
1994
|
+
clearTimeout(timeoutId);
|
|
1995
|
+
if (!response.ok) {
|
|
1996
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
1985
1997
|
}
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1998
|
+
const data = await response.json();
|
|
1999
|
+
const presets = [];
|
|
2000
|
+
if (Array.isArray(data)) {
|
|
2001
|
+
for (const provider of data) {
|
|
2002
|
+
if (provider && typeof provider === "object") {
|
|
2003
|
+
presets.push({
|
|
2004
|
+
name: provider.name || "",
|
|
2005
|
+
provider: provider.name || "",
|
|
2006
|
+
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
2007
|
+
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
2008
|
+
models: provider.models || [],
|
|
2009
|
+
description: provider.description || provider.name || "",
|
|
2010
|
+
transformer: provider.transformer
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
1999
2013
|
}
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2014
|
+
} else if (data && typeof data === "object") {
|
|
2015
|
+
for (const [key, value] of Object.entries(data)) {
|
|
2016
|
+
if (typeof value === "object" && value !== null) {
|
|
2017
|
+
const provider = value;
|
|
2018
|
+
presets.push({
|
|
2019
|
+
name: provider.name || key,
|
|
2020
|
+
provider: key,
|
|
2021
|
+
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
2022
|
+
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
2023
|
+
models: provider.models || [],
|
|
2024
|
+
description: provider.description || "",
|
|
2025
|
+
transformer: provider.transformer
|
|
2026
|
+
});
|
|
2027
|
+
}
|
|
2003
2028
|
}
|
|
2004
|
-
return true;
|
|
2005
2029
|
}
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
return null;
|
|
2030
|
+
return presets;
|
|
2031
|
+
} catch (error) {
|
|
2032
|
+
return getFallbackPresets();
|
|
2010
2033
|
}
|
|
2011
|
-
console.log(ansis.gray(` API Key: ${formatApiKeyDisplay(key)}`));
|
|
2012
|
-
return { url, key, authType };
|
|
2013
2034
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
{ name: i18n.api.modifyAuthType, value: "authType" }
|
|
2028
|
-
])
|
|
2029
|
-
});
|
|
2030
|
-
if (!item) {
|
|
2031
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2032
|
-
return;
|
|
2033
|
-
}
|
|
2034
|
-
if (item === "url") {
|
|
2035
|
-
const { url } = await inquirer.prompt({
|
|
2036
|
-
type: "input",
|
|
2037
|
-
name: "url",
|
|
2038
|
-
message: i18n.api.enterNewApiUrl.replace("{url}", currentConfig.url || i18n.common.none),
|
|
2039
|
-
default: currentConfig.url,
|
|
2040
|
-
validate: (value) => {
|
|
2041
|
-
if (!value) return i18n.api.urlRequired;
|
|
2042
|
-
try {
|
|
2043
|
-
new URL(value);
|
|
2044
|
-
return true;
|
|
2045
|
-
} catch {
|
|
2046
|
-
return i18n.api.invalidUrl;
|
|
2035
|
+
function getFallbackPresets() {
|
|
2036
|
+
return [
|
|
2037
|
+
{
|
|
2038
|
+
name: "dashscope",
|
|
2039
|
+
provider: "dashscope",
|
|
2040
|
+
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
|
|
2041
|
+
requiresApiKey: true,
|
|
2042
|
+
models: ["qwen3-coder-plus"],
|
|
2043
|
+
description: "Alibaba DashScope",
|
|
2044
|
+
transformer: {
|
|
2045
|
+
use: [["maxtoken", { max_tokens: 65536 }]],
|
|
2046
|
+
"qwen3-coder-plus": {
|
|
2047
|
+
use: ["enhancetool"]
|
|
2047
2048
|
}
|
|
2048
2049
|
}
|
|
2049
|
-
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
const authType = currentConfig.authType || "auth_token";
|
|
2062
|
-
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);
|
|
2063
|
-
const { key } = await inquirer.prompt({
|
|
2064
|
-
type: "input",
|
|
2065
|
-
name: "key",
|
|
2066
|
-
message: keyMessage,
|
|
2067
|
-
validate: (value) => {
|
|
2068
|
-
if (!value) {
|
|
2069
|
-
return i18n.api.keyRequired;
|
|
2050
|
+
},
|
|
2051
|
+
{
|
|
2052
|
+
name: "deepseek",
|
|
2053
|
+
provider: "deepseek",
|
|
2054
|
+
baseURL: "https://api.deepseek.com/chat/completions",
|
|
2055
|
+
requiresApiKey: true,
|
|
2056
|
+
models: ["deepseek-chat", "deepseek-reasoner"],
|
|
2057
|
+
description: "DeepSeek AI models",
|
|
2058
|
+
transformer: {
|
|
2059
|
+
use: ["deepseek"],
|
|
2060
|
+
"deepseek-chat": {
|
|
2061
|
+
use: ["tooluse"]
|
|
2070
2062
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2063
|
+
}
|
|
2064
|
+
},
|
|
2065
|
+
{
|
|
2066
|
+
name: "gemini",
|
|
2067
|
+
provider: "gemini",
|
|
2068
|
+
baseURL: "https://generativelanguage.googleapis.com/v1beta/models/",
|
|
2069
|
+
requiresApiKey: true,
|
|
2070
|
+
models: ["gemini-2.5-flash", "gemini-2.5-pro"],
|
|
2071
|
+
description: "Google Gemini models",
|
|
2072
|
+
transformer: {
|
|
2073
|
+
use: ["gemini"]
|
|
2074
|
+
}
|
|
2075
|
+
},
|
|
2076
|
+
{
|
|
2077
|
+
name: "modelscope",
|
|
2078
|
+
provider: "modelscope",
|
|
2079
|
+
baseURL: "https://api-inference.modelscope.cn/v1/chat/completions",
|
|
2080
|
+
requiresApiKey: true,
|
|
2081
|
+
models: ["Qwen/Qwen3-Coder-480B-A35B-Instruct", "Qwen/Qwen3-235B-A22B-Thinking-2507", "ZhipuAI/GLM-4.5"],
|
|
2082
|
+
description: "ModelScope AI models",
|
|
2083
|
+
transformer: {
|
|
2084
|
+
use: [["maxtoken", { max_tokens: 65536 }]],
|
|
2085
|
+
"Qwen/Qwen3-Coder-480B-A35B-Instruct": {
|
|
2086
|
+
use: ["enhancetool"]
|
|
2087
|
+
},
|
|
2088
|
+
"Qwen/Qwen3-235B-A22B-Thinking-2507": {
|
|
2089
|
+
use: ["reasoning"]
|
|
2074
2090
|
}
|
|
2075
|
-
return true;
|
|
2076
2091
|
}
|
|
2077
|
-
}
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2092
|
+
},
|
|
2093
|
+
{
|
|
2094
|
+
name: "openrouter",
|
|
2095
|
+
provider: "openrouter",
|
|
2096
|
+
baseURL: "https://openrouter.ai/api/v1/chat/completions",
|
|
2097
|
+
requiresApiKey: true,
|
|
2098
|
+
models: [
|
|
2099
|
+
"google/gemini-2.5-pro-preview",
|
|
2100
|
+
"anthropic/claude-sonnet-4",
|
|
2101
|
+
"anthropic/claude-3.5-sonnet",
|
|
2102
|
+
"anthropic/claude-3.7-sonnet:thinking"
|
|
2103
|
+
],
|
|
2104
|
+
description: "OpenRouter API",
|
|
2105
|
+
transformer: {
|
|
2106
|
+
use: ["openrouter"]
|
|
2107
|
+
}
|
|
2108
|
+
},
|
|
2109
|
+
{
|
|
2110
|
+
name: "siliconflow",
|
|
2111
|
+
provider: "siliconflow",
|
|
2112
|
+
baseURL: "https://api.siliconflow.cn/v1/chat/completions",
|
|
2113
|
+
requiresApiKey: true,
|
|
2114
|
+
models: ["moonshotai/Kimi-K2-Instruct"],
|
|
2115
|
+
description: "SiliconFlow AI",
|
|
2116
|
+
transformer: {
|
|
2117
|
+
use: [["maxtoken", { max_tokens: 16384 }]]
|
|
2118
|
+
}
|
|
2119
|
+
},
|
|
2120
|
+
{
|
|
2121
|
+
name: "volcengine",
|
|
2122
|
+
provider: "volcengine",
|
|
2123
|
+
baseURL: "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
|
|
2124
|
+
requiresApiKey: true,
|
|
2125
|
+
models: ["deepseek-v3-250324", "deepseek-r1-250528"],
|
|
2126
|
+
description: "Volcengine AI",
|
|
2127
|
+
transformer: {
|
|
2128
|
+
use: ["deepseek"]
|
|
2129
|
+
}
|
|
2108
2130
|
}
|
|
2109
|
-
|
|
2110
|
-
}
|
|
2111
|
-
async function updatePromptOnly(configLang, scriptLang, aiOutputLang) {
|
|
2112
|
-
const i18n = getTranslation(scriptLang);
|
|
2113
|
-
const backupDir = backupExistingConfig();
|
|
2114
|
-
if (backupDir) {
|
|
2115
|
-
console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
|
|
2116
|
-
}
|
|
2117
|
-
copyConfigFiles(configLang, true);
|
|
2118
|
-
if (aiOutputLang) {
|
|
2119
|
-
applyAiLanguageDirective(aiOutputLang);
|
|
2120
|
-
}
|
|
2121
|
-
await configureAiPersonality(scriptLang);
|
|
2122
|
-
console.log(ansis.green(`\u2714 ${i18n.configuration.configSuccess} ${CLAUDE_DIR}`));
|
|
2123
|
-
console.log("\n" + ansis.cyan(i18n.common.complete));
|
|
2124
|
-
}
|
|
2125
|
-
|
|
2126
|
-
function format(template, replacements) {
|
|
2127
|
-
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
2128
|
-
return replacements[key] || match;
|
|
2129
|
-
});
|
|
2131
|
+
];
|
|
2130
2132
|
}
|
|
2131
2133
|
|
|
2132
2134
|
const execAsync$4 = promisify(exec$1);
|
|
2133
|
-
|
|
2135
|
+
const CCR_CONFIG_DIR = join$1(homedir(), ".claude-code-router");
|
|
2136
|
+
const CCR_CONFIG_FILE = join$1(CCR_CONFIG_DIR, "config.json");
|
|
2137
|
+
const CCR_BACKUP_DIR = CCR_CONFIG_DIR;
|
|
2138
|
+
function ensureCcrConfigDir() {
|
|
2139
|
+
if (!existsSync(CCR_CONFIG_DIR)) {
|
|
2140
|
+
mkdirSync(CCR_CONFIG_DIR, { recursive: true });
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
function backupCcrConfig(scriptLang) {
|
|
2144
|
+
const i18n = getTranslation(scriptLang);
|
|
2134
2145
|
try {
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
const result = await execAsync$4(`${command} -v`);
|
|
2138
|
-
stdout = result.stdout;
|
|
2139
|
-
} catch {
|
|
2140
|
-
const result = await execAsync$4(`${command} --version`);
|
|
2141
|
-
stdout = result.stdout;
|
|
2146
|
+
if (!existsSync(CCR_CONFIG_FILE)) {
|
|
2147
|
+
return null;
|
|
2142
2148
|
}
|
|
2143
|
-
const
|
|
2144
|
-
|
|
2145
|
-
|
|
2149
|
+
const timestamp = dayjs().format("YYYY-MM-DDTHH-mm-ss-SSS") + "Z";
|
|
2150
|
+
const backupFileName = `config.json.${timestamp}.bak`;
|
|
2151
|
+
const backupPath = join$1(CCR_BACKUP_DIR, backupFileName);
|
|
2152
|
+
console.log(ansis.cyan(`${i18n.ccr.backupCcrConfig}`));
|
|
2153
|
+
copyFileSync(CCR_CONFIG_FILE, backupPath);
|
|
2154
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrBackupSuccess.replace("{path}", backupPath)}`));
|
|
2155
|
+
return backupPath;
|
|
2156
|
+
} catch (error) {
|
|
2157
|
+
console.error(ansis.red(`${i18n.ccr.ccrBackupFailed}:`), error.message);
|
|
2146
2158
|
return null;
|
|
2147
2159
|
}
|
|
2148
2160
|
}
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
const { stdout } = await execAsync$4(`npm view ${packageName} version`);
|
|
2152
|
-
return stdout.trim();
|
|
2153
|
-
} catch {
|
|
2161
|
+
function readCcrConfig() {
|
|
2162
|
+
if (!existsSync(CCR_CONFIG_FILE)) {
|
|
2154
2163
|
return null;
|
|
2155
2164
|
}
|
|
2165
|
+
return readJsonConfig(CCR_CONFIG_FILE);
|
|
2156
2166
|
}
|
|
2157
|
-
function
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
}
|
|
2161
|
-
return semver.compare(current, latest);
|
|
2167
|
+
function writeCcrConfig(config) {
|
|
2168
|
+
ensureCcrConfigDir();
|
|
2169
|
+
writeJsonConfig(CCR_CONFIG_FILE, config);
|
|
2162
2170
|
}
|
|
2163
|
-
function
|
|
2164
|
-
|
|
2171
|
+
async function configureCcrProxy(ccrConfig) {
|
|
2172
|
+
const settings = readJsonConfig(SETTINGS_FILE) || {};
|
|
2173
|
+
const host = ccrConfig.HOST || "127.0.0.1";
|
|
2174
|
+
const port = ccrConfig.PORT || 3456;
|
|
2175
|
+
const apiKey = ccrConfig.APIKEY || "sk-zcf-x-ccr";
|
|
2176
|
+
if (!settings.env) {
|
|
2177
|
+
settings.env = {};
|
|
2178
|
+
}
|
|
2179
|
+
settings.env.ANTHROPIC_BASE_URL = `http://${host}:${port}`;
|
|
2180
|
+
settings.env.ANTHROPIC_API_KEY = apiKey;
|
|
2181
|
+
writeJsonConfig(SETTINGS_FILE, settings);
|
|
2165
2182
|
}
|
|
2166
|
-
async function
|
|
2167
|
-
const currentVersion = await getInstalledVersion("ccr");
|
|
2168
|
-
const latestVersion = await getLatestVersion("@musistudio/claude-code-router");
|
|
2169
|
-
return {
|
|
2170
|
-
installed: currentVersion !== null,
|
|
2171
|
-
currentVersion,
|
|
2172
|
-
latestVersion,
|
|
2173
|
-
needsUpdate: currentVersion && latestVersion ? shouldUpdate(currentVersion, latestVersion) : false
|
|
2174
|
-
};
|
|
2175
|
-
}
|
|
2176
|
-
async function checkClaudeCodeVersion() {
|
|
2177
|
-
const currentVersion = await getInstalledVersion("claude");
|
|
2178
|
-
const latestVersion = await getLatestVersion("@anthropic-ai/claude-code");
|
|
2179
|
-
return {
|
|
2180
|
-
installed: currentVersion !== null,
|
|
2181
|
-
currentVersion,
|
|
2182
|
-
latestVersion,
|
|
2183
|
-
needsUpdate: currentVersion && latestVersion ? shouldUpdate(currentVersion, latestVersion) : false
|
|
2184
|
-
};
|
|
2185
|
-
}
|
|
2186
|
-
async function checkCometixLineVersion() {
|
|
2187
|
-
const currentVersion = await getInstalledVersion("ccometix");
|
|
2188
|
-
const latestVersion = await getLatestVersion("ccometix");
|
|
2189
|
-
return {
|
|
2190
|
-
installed: currentVersion !== null,
|
|
2191
|
-
currentVersion,
|
|
2192
|
-
latestVersion,
|
|
2193
|
-
needsUpdate: currentVersion && latestVersion ? shouldUpdate(currentVersion, latestVersion) : false
|
|
2194
|
-
};
|
|
2195
|
-
}
|
|
2196
|
-
|
|
2197
|
-
const execAsync$3 = promisify(exec$1);
|
|
2198
|
-
async function updateCcr(scriptLang, force = false) {
|
|
2183
|
+
async function selectCcrPreset(scriptLang) {
|
|
2199
2184
|
const i18n = getTranslation(scriptLang);
|
|
2200
|
-
|
|
2185
|
+
console.log(ansis.cyan(`${i18n.ccr.fetchingPresets}`));
|
|
2186
|
+
const presets = await fetchProviderPresets();
|
|
2187
|
+
if (!presets || presets.length === 0) {
|
|
2188
|
+
console.log(ansis.yellow(`${i18n.ccr.noPresetsAvailable}`));
|
|
2189
|
+
return null;
|
|
2190
|
+
}
|
|
2201
2191
|
try {
|
|
2202
|
-
const
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
console.log(ansis.cyan(format(i18n.updater.latestVersion, { version: latestVersion })));
|
|
2218
|
-
const { confirm } = await inquirer.prompt({
|
|
2219
|
-
type: "confirm",
|
|
2220
|
-
name: "confirm",
|
|
2221
|
-
message: format(i18n.updater.confirmUpdate, { tool: "CCR" }),
|
|
2222
|
-
default: true
|
|
2192
|
+
const choices = [
|
|
2193
|
+
{
|
|
2194
|
+
name: `1. ${i18n.ccr.skipOption}`,
|
|
2195
|
+
value: "skip"
|
|
2196
|
+
},
|
|
2197
|
+
...presets.map((p, index) => ({
|
|
2198
|
+
name: `${index + 2}. ${p.name}`,
|
|
2199
|
+
value: p
|
|
2200
|
+
}))
|
|
2201
|
+
];
|
|
2202
|
+
const { preset } = await inquirer.prompt({
|
|
2203
|
+
type: "list",
|
|
2204
|
+
name: "preset",
|
|
2205
|
+
message: i18n.ccr.selectCcrPreset,
|
|
2206
|
+
choices
|
|
2223
2207
|
});
|
|
2224
|
-
|
|
2225
|
-
console.log(ansis.gray(i18n.updater.updateSkipped));
|
|
2226
|
-
return true;
|
|
2227
|
-
}
|
|
2228
|
-
const updateSpinner = ora(format(i18n.updater.updating, { tool: "CCR" })).start();
|
|
2229
|
-
try {
|
|
2230
|
-
await execAsync$3("npm update -g @musistudio/claude-code-router");
|
|
2231
|
-
updateSpinner.succeed(format(i18n.updater.updateSuccess, { tool: "CCR" }));
|
|
2232
|
-
return true;
|
|
2233
|
-
} catch (error) {
|
|
2234
|
-
updateSpinner.fail(format(i18n.updater.updateFailed, { tool: "CCR" }));
|
|
2235
|
-
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2236
|
-
return false;
|
|
2237
|
-
}
|
|
2208
|
+
return preset;
|
|
2238
2209
|
} catch (error) {
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2210
|
+
if (error.name === "ExitPromptError") {
|
|
2211
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2212
|
+
return null;
|
|
2213
|
+
}
|
|
2214
|
+
throw error;
|
|
2242
2215
|
}
|
|
2243
2216
|
}
|
|
2244
|
-
async function
|
|
2217
|
+
async function configureCcrWithPreset(preset, scriptLang) {
|
|
2245
2218
|
const i18n = getTranslation(scriptLang);
|
|
2246
|
-
const
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
name
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
if (!confirm) {
|
|
2271
|
-
console.log(ansis.gray(i18n.updater.updateSkipped));
|
|
2272
|
-
return true;
|
|
2219
|
+
const provider = {
|
|
2220
|
+
name: preset.name,
|
|
2221
|
+
// Use the original name from JSON
|
|
2222
|
+
api_base_url: preset.baseURL || "",
|
|
2223
|
+
api_key: "",
|
|
2224
|
+
models: preset.models
|
|
2225
|
+
};
|
|
2226
|
+
if (preset.transformer) {
|
|
2227
|
+
provider.transformer = preset.transformer;
|
|
2228
|
+
}
|
|
2229
|
+
if (preset.requiresApiKey) {
|
|
2230
|
+
try {
|
|
2231
|
+
const { apiKey } = await inquirer.prompt({
|
|
2232
|
+
type: "input",
|
|
2233
|
+
name: "apiKey",
|
|
2234
|
+
message: i18n.ccr.enterApiKeyForProvider.replace("{provider}", preset.name),
|
|
2235
|
+
validate: (value) => !!value || i18n.api.keyRequired
|
|
2236
|
+
});
|
|
2237
|
+
provider.api_key = apiKey;
|
|
2238
|
+
} catch (error) {
|
|
2239
|
+
if (error.name === "ExitPromptError") {
|
|
2240
|
+
throw error;
|
|
2241
|
+
}
|
|
2242
|
+
throw error;
|
|
2273
2243
|
}
|
|
2274
|
-
|
|
2244
|
+
} else {
|
|
2245
|
+
provider.api_key = "sk-free";
|
|
2246
|
+
}
|
|
2247
|
+
let defaultModel = preset.models[0];
|
|
2248
|
+
if (preset.models.length > 1) {
|
|
2275
2249
|
try {
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2250
|
+
const { model } = await inquirer.prompt({
|
|
2251
|
+
type: "list",
|
|
2252
|
+
name: "model",
|
|
2253
|
+
message: i18n.ccr.selectDefaultModelForProvider.replace("{provider}", preset.name),
|
|
2254
|
+
choices: preset.models.map((m, index) => ({
|
|
2255
|
+
name: `${index + 1}. ${m}`,
|
|
2256
|
+
value: m
|
|
2257
|
+
}))
|
|
2258
|
+
});
|
|
2259
|
+
defaultModel = model;
|
|
2279
2260
|
} catch (error) {
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2261
|
+
if (error.name === "ExitPromptError") {
|
|
2262
|
+
throw error;
|
|
2263
|
+
}
|
|
2264
|
+
throw error;
|
|
2283
2265
|
}
|
|
2284
|
-
} catch (error) {
|
|
2285
|
-
spinner.fail(i18n.updater.checkFailed);
|
|
2286
|
-
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2287
|
-
return false;
|
|
2288
2266
|
}
|
|
2267
|
+
const router = {
|
|
2268
|
+
default: `${preset.name},${defaultModel}`,
|
|
2269
|
+
// Use the original name
|
|
2270
|
+
background: `${preset.name},${defaultModel}`,
|
|
2271
|
+
think: `${preset.name},${defaultModel}`,
|
|
2272
|
+
longContext: `${preset.name},${defaultModel}`,
|
|
2273
|
+
longContextThreshold: 6e4,
|
|
2274
|
+
webSearch: `${preset.name},${defaultModel}`
|
|
2275
|
+
};
|
|
2276
|
+
const config = {
|
|
2277
|
+
LOG: true,
|
|
2278
|
+
CLAUDE_PATH: "",
|
|
2279
|
+
HOST: "127.0.0.1",
|
|
2280
|
+
PORT: 3456,
|
|
2281
|
+
APIKEY: "sk-zcf-x-ccr",
|
|
2282
|
+
API_TIMEOUT_MS: "600000",
|
|
2283
|
+
PROXY_URL: "",
|
|
2284
|
+
transformers: [],
|
|
2285
|
+
Providers: [provider],
|
|
2286
|
+
Router: router
|
|
2287
|
+
};
|
|
2288
|
+
return config;
|
|
2289
2289
|
}
|
|
2290
|
-
async function
|
|
2290
|
+
async function restartAndCheckCcrStatus(scriptLang) {
|
|
2291
2291
|
const i18n = getTranslation(scriptLang);
|
|
2292
|
-
const spinner = ora(i18n.updater.checkingVersion).start();
|
|
2293
2292
|
try {
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
if (!needsUpdate && !force) {
|
|
2301
|
-
console.log(ansis.green(format(i18n.updater.cometixLineUpToDate, { version: currentVersion || "" })));
|
|
2302
|
-
return true;
|
|
2303
|
-
}
|
|
2304
|
-
if (!latestVersion) {
|
|
2305
|
-
console.log(ansis.yellow(i18n.updater.cannotCheckVersion));
|
|
2306
|
-
return false;
|
|
2307
|
-
}
|
|
2308
|
-
console.log(ansis.cyan(format(i18n.updater.currentVersion, { version: currentVersion || "" })));
|
|
2309
|
-
console.log(ansis.cyan(format(i18n.updater.latestVersion, { version: latestVersion })));
|
|
2310
|
-
const { confirm } = await inquirer.prompt({
|
|
2311
|
-
type: "confirm",
|
|
2312
|
-
name: "confirm",
|
|
2313
|
-
message: format(i18n.updater.confirmUpdate, { tool: "CCometixLine" }),
|
|
2314
|
-
default: true
|
|
2315
|
-
});
|
|
2316
|
-
if (!confirm) {
|
|
2317
|
-
console.log(ansis.gray(i18n.updater.updateSkipped));
|
|
2318
|
-
return true;
|
|
2319
|
-
}
|
|
2320
|
-
const updateSpinner = ora(format(i18n.updater.updating, { tool: "CCometixLine" })).start();
|
|
2321
|
-
try {
|
|
2322
|
-
await execAsync$3("cargo install ccometix");
|
|
2323
|
-
updateSpinner.succeed(format(i18n.updater.updateSuccess, { tool: "CCometixLine" }));
|
|
2324
|
-
return true;
|
|
2325
|
-
} catch (error) {
|
|
2326
|
-
updateSpinner.fail(format(i18n.updater.updateFailed, { tool: "CCometixLine" }));
|
|
2327
|
-
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2328
|
-
return false;
|
|
2329
|
-
}
|
|
2293
|
+
console.log(ansis.cyan(`${i18n.ccr.restartingCcr}`));
|
|
2294
|
+
await execAsync$4("ccr restart");
|
|
2295
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrRestartSuccess}`));
|
|
2296
|
+
console.log(ansis.cyan(`${i18n.ccr.checkingCcrStatus}`));
|
|
2297
|
+
const { stdout } = await execAsync$4("ccr status");
|
|
2298
|
+
console.log(ansis.gray(stdout));
|
|
2330
2299
|
} catch (error) {
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2300
|
+
console.error(ansis.red(`${i18n.ccr.ccrRestartFailed}:`), error.message || error);
|
|
2301
|
+
if (process.env.DEBUG) {
|
|
2302
|
+
console.error("Full error:", error);
|
|
2303
|
+
}
|
|
2334
2304
|
}
|
|
2335
2305
|
}
|
|
2336
|
-
|
|
2306
|
+
function showConfigurationTips(scriptLang, apiKey) {
|
|
2337
2307
|
const i18n = getTranslation(scriptLang);
|
|
2338
2308
|
console.log(ansis.bold.cyan(`
|
|
2339
|
-
\u{
|
|
2340
|
-
`));
|
|
2341
|
-
|
|
2342
|
-
console.log();
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
}
|
|
2347
|
-
|
|
2348
|
-
async function isClaudeCodeInstalled() {
|
|
2349
|
-
return await commandExists("claude");
|
|
2350
|
-
}
|
|
2351
|
-
async function installClaudeCode(lang) {
|
|
2352
|
-
const i18n = getTranslation(lang);
|
|
2353
|
-
const installed = await isClaudeCodeInstalled();
|
|
2354
|
-
if (installed) {
|
|
2355
|
-
console.log(ansis.green(`\u2714 ${i18n.installation.alreadyInstalled}`));
|
|
2356
|
-
await updateClaudeCode(lang);
|
|
2357
|
-
return;
|
|
2358
|
-
}
|
|
2359
|
-
if (isTermux()) {
|
|
2360
|
-
console.log(ansis.yellow(`\u2139 ${i18n.installation.termuxDetected}`));
|
|
2361
|
-
const termuxPrefix = getTermuxPrefix();
|
|
2362
|
-
console.log(ansis.gray(i18n.installation.termuxPathInfo.replace("{path}", termuxPrefix)));
|
|
2363
|
-
console.log(ansis.gray(`Node.js: ${termuxPrefix}/bin/node`));
|
|
2364
|
-
console.log(ansis.gray(`npm: ${termuxPrefix}/bin/npm`));
|
|
2309
|
+
\u{1F4CC} ${i18n.ccr.configTips}:`));
|
|
2310
|
+
console.log(ansis.blue(` \u2022 ${i18n.ccr.advancedConfigTip}`));
|
|
2311
|
+
console.log(ansis.blue(` \u2022 ${i18n.ccr.manualConfigTip}`));
|
|
2312
|
+
console.log(ansis.bold.yellow(` \u2022 ${i18n.ccr.useClaudeCommand}`));
|
|
2313
|
+
if (apiKey) {
|
|
2314
|
+
console.log(ansis.bold.green(` \u2022 ${i18n.ccr.ccrUiApiKey || "CCR UI API Key"}: ${apiKey}`));
|
|
2315
|
+
console.log(ansis.gray(` ${i18n.ccr.ccrUiApiKeyHint || "Use this API key to login to CCR UI"}`));
|
|
2365
2316
|
}
|
|
2366
|
-
console.log(
|
|
2317
|
+
console.log("");
|
|
2318
|
+
}
|
|
2319
|
+
async function setupCcrConfiguration(scriptLang) {
|
|
2320
|
+
const i18n = getTranslation(scriptLang);
|
|
2367
2321
|
try {
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2322
|
+
const existingConfig = readCcrConfig();
|
|
2323
|
+
if (existingConfig) {
|
|
2324
|
+
console.log(ansis.blue(`\u2139 ${i18n.ccr.existingCcrConfig}`));
|
|
2325
|
+
let shouldBackupAndReconfigure = false;
|
|
2326
|
+
try {
|
|
2327
|
+
const result = await inquirer.prompt({
|
|
2328
|
+
type: "confirm",
|
|
2329
|
+
name: "overwrite",
|
|
2330
|
+
message: i18n.ccr.overwriteCcrConfig,
|
|
2331
|
+
default: false
|
|
2332
|
+
});
|
|
2333
|
+
shouldBackupAndReconfigure = result.overwrite;
|
|
2334
|
+
} catch (error) {
|
|
2335
|
+
if (error.name === "ExitPromptError") {
|
|
2336
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2337
|
+
return false;
|
|
2338
|
+
}
|
|
2339
|
+
throw error;
|
|
2340
|
+
}
|
|
2341
|
+
if (!shouldBackupAndReconfigure) {
|
|
2342
|
+
console.log(ansis.yellow(`${i18n.ccr.keepingExistingConfig}`));
|
|
2343
|
+
await configureCcrProxy(existingConfig);
|
|
2344
|
+
return true;
|
|
2345
|
+
}
|
|
2346
|
+
backupCcrConfig(scriptLang);
|
|
2347
|
+
}
|
|
2348
|
+
const preset = await selectCcrPreset(scriptLang);
|
|
2349
|
+
if (!preset) {
|
|
2350
|
+
return false;
|
|
2373
2351
|
}
|
|
2352
|
+
let config;
|
|
2353
|
+
if (preset === "skip") {
|
|
2354
|
+
console.log(ansis.yellow(`${i18n.ccr.skipConfiguring}`));
|
|
2355
|
+
config = {
|
|
2356
|
+
LOG: false,
|
|
2357
|
+
CLAUDE_PATH: "",
|
|
2358
|
+
HOST: "127.0.0.1",
|
|
2359
|
+
PORT: 3456,
|
|
2360
|
+
APIKEY: "sk-zcf-x-ccr",
|
|
2361
|
+
API_TIMEOUT_MS: "600000",
|
|
2362
|
+
PROXY_URL: "",
|
|
2363
|
+
transformers: [],
|
|
2364
|
+
Providers: [],
|
|
2365
|
+
// Empty providers array
|
|
2366
|
+
Router: {
|
|
2367
|
+
// Empty router configuration - user will configure in CCR UI
|
|
2368
|
+
}
|
|
2369
|
+
};
|
|
2370
|
+
} else {
|
|
2371
|
+
config = await configureCcrWithPreset(preset, scriptLang);
|
|
2372
|
+
}
|
|
2373
|
+
writeCcrConfig(config);
|
|
2374
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrConfigSuccess}`));
|
|
2375
|
+
await configureCcrProxy(config);
|
|
2376
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.proxyConfigSuccess}`));
|
|
2377
|
+
await restartAndCheckCcrStatus(scriptLang);
|
|
2378
|
+
showConfigurationTips(scriptLang, config.APIKEY);
|
|
2379
|
+
try {
|
|
2380
|
+
addCompletedOnboarding();
|
|
2381
|
+
} catch (error) {
|
|
2382
|
+
console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
|
|
2383
|
+
}
|
|
2384
|
+
return true;
|
|
2374
2385
|
} catch (error) {
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
${i18n.installation.termuxInstallHint}
|
|
2379
|
-
`));
|
|
2386
|
+
if (error.name === "ExitPromptError") {
|
|
2387
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2388
|
+
return false;
|
|
2380
2389
|
}
|
|
2381
|
-
|
|
2390
|
+
console.error(ansis.red(`${i18n.ccr.ccrConfigFailed}:`), error);
|
|
2391
|
+
return false;
|
|
2382
2392
|
}
|
|
2383
2393
|
}
|
|
2384
|
-
|
|
2385
|
-
async function selectAiOutputLanguage(scriptLang, defaultLang) {
|
|
2394
|
+
async function configureCcrFeature(scriptLang) {
|
|
2386
2395
|
const i18n = getTranslation(scriptLang);
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
`));
|
|
2390
|
-
const aiLangChoices = Object.entries(AI_OUTPUT_LANGUAGES).map(([key, value]) => ({
|
|
2391
|
-
title: value.label,
|
|
2392
|
-
value: key
|
|
2393
|
-
}));
|
|
2394
|
-
const defaultChoice = defaultLang || (scriptLang === "zh-CN" ? "zh-CN" : "en");
|
|
2395
|
-
const { lang } = await inquirer.prompt({
|
|
2396
|
-
type: "list",
|
|
2397
|
-
name: "lang",
|
|
2398
|
-
message: i18n.language.selectAiOutputLang,
|
|
2399
|
-
choices: addNumbersToChoices(aiLangChoices.map((choice) => ({
|
|
2400
|
-
name: choice.title,
|
|
2401
|
-
value: choice.value
|
|
2402
|
-
}))),
|
|
2403
|
-
default: defaultChoice
|
|
2404
|
-
});
|
|
2405
|
-
if (!lang) {
|
|
2406
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2407
|
-
process.exit(0);
|
|
2408
|
-
}
|
|
2409
|
-
let aiOutputLang = lang;
|
|
2410
|
-
if (aiOutputLang === "custom") {
|
|
2411
|
-
const { customLang } = await inquirer.prompt({
|
|
2412
|
-
type: "input",
|
|
2413
|
-
name: "customLang",
|
|
2414
|
-
message: i18n.language.enterCustomLanguage,
|
|
2415
|
-
validate: (value) => !!value || i18n.language?.languageRequired || "Language is required"
|
|
2416
|
-
});
|
|
2417
|
-
if (!customLang) {
|
|
2418
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2419
|
-
process.exit(0);
|
|
2420
|
-
}
|
|
2421
|
-
return customLang;
|
|
2396
|
+
const backupDir = backupExistingConfig();
|
|
2397
|
+
if (backupDir) {
|
|
2398
|
+
console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
|
|
2422
2399
|
}
|
|
2423
|
-
|
|
2400
|
+
await setupCcrConfiguration(scriptLang);
|
|
2424
2401
|
}
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
return
|
|
2429
|
-
}
|
|
2430
|
-
if (currentLang) {
|
|
2431
|
-
return currentLang;
|
|
2432
|
-
}
|
|
2433
|
-
const { lang } = await inquirer.prompt({
|
|
2434
|
-
type: "list",
|
|
2435
|
-
name: "lang",
|
|
2436
|
-
message: "Select ZCF display language / \u9009\u62E9ZCF\u663E\u793A\u8BED\u8A00",
|
|
2437
|
-
choices: addNumbersToChoices(SUPPORTED_LANGS.map((l) => ({
|
|
2438
|
-
name: LANG_LABELS[l],
|
|
2439
|
-
value: l
|
|
2440
|
-
})))
|
|
2402
|
+
|
|
2403
|
+
function format(template, replacements) {
|
|
2404
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
2405
|
+
return replacements[key] || match;
|
|
2441
2406
|
});
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
const execAsync$3 = promisify$1(exec$2);
|
|
2410
|
+
async function getInstalledVersion(command) {
|
|
2411
|
+
try {
|
|
2412
|
+
let stdout;
|
|
2413
|
+
try {
|
|
2414
|
+
const result = await execAsync$3(`${command} -v`);
|
|
2415
|
+
stdout = result.stdout;
|
|
2416
|
+
} catch {
|
|
2417
|
+
const result = await execAsync$3(`${command} --version`);
|
|
2418
|
+
stdout = result.stdout;
|
|
2419
|
+
}
|
|
2420
|
+
const versionMatch = stdout.match(/(\d+\.\d+\.\d+(?:-[\w.]+)?)/);
|
|
2421
|
+
return versionMatch ? versionMatch[1] : null;
|
|
2422
|
+
} catch {
|
|
2423
|
+
return null;
|
|
2445
2424
|
}
|
|
2446
|
-
const scriptLang = lang;
|
|
2447
|
-
updateZcfConfig({
|
|
2448
|
-
version,
|
|
2449
|
-
preferredLang: scriptLang
|
|
2450
|
-
});
|
|
2451
|
-
return scriptLang;
|
|
2452
2425
|
}
|
|
2453
|
-
async function
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
return
|
|
2426
|
+
async function getLatestVersion(packageName) {
|
|
2427
|
+
try {
|
|
2428
|
+
const { stdout } = await execAsync$3(`npm view ${packageName} version`);
|
|
2429
|
+
return stdout.trim();
|
|
2430
|
+
} catch {
|
|
2431
|
+
return null;
|
|
2457
2432
|
}
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2433
|
+
}
|
|
2434
|
+
function compareVersions(current, latest) {
|
|
2435
|
+
if (!semver.valid(current) || !semver.valid(latest)) {
|
|
2436
|
+
return -1;
|
|
2461
2437
|
}
|
|
2462
|
-
return
|
|
2438
|
+
return semver.compare(current, latest);
|
|
2439
|
+
}
|
|
2440
|
+
function shouldUpdate(current, latest) {
|
|
2441
|
+
return compareVersions(current, latest) < 0;
|
|
2442
|
+
}
|
|
2443
|
+
async function checkCcrVersion() {
|
|
2444
|
+
const currentVersion = await getInstalledVersion("ccr");
|
|
2445
|
+
const latestVersion = await getLatestVersion("@musistudio/claude-code-router");
|
|
2446
|
+
return {
|
|
2447
|
+
installed: currentVersion !== null,
|
|
2448
|
+
currentVersion,
|
|
2449
|
+
latestVersion,
|
|
2450
|
+
needsUpdate: currentVersion && latestVersion ? shouldUpdate(currentVersion, latestVersion) : false
|
|
2451
|
+
};
|
|
2452
|
+
}
|
|
2453
|
+
async function checkClaudeCodeVersion() {
|
|
2454
|
+
const currentVersion = await getInstalledVersion("claude");
|
|
2455
|
+
const latestVersion = await getLatestVersion("@anthropic-ai/claude-code");
|
|
2456
|
+
return {
|
|
2457
|
+
installed: currentVersion !== null,
|
|
2458
|
+
currentVersion,
|
|
2459
|
+
latestVersion,
|
|
2460
|
+
needsUpdate: currentVersion && latestVersion ? shouldUpdate(currentVersion, latestVersion) : false
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
async function checkCometixLineVersion() {
|
|
2464
|
+
const currentVersion = await getInstalledVersion("ccometix");
|
|
2465
|
+
const latestVersion = await getLatestVersion("ccometix");
|
|
2466
|
+
return {
|
|
2467
|
+
installed: currentVersion !== null,
|
|
2468
|
+
currentVersion,
|
|
2469
|
+
latestVersion,
|
|
2470
|
+
needsUpdate: currentVersion && latestVersion ? shouldUpdate(currentVersion, latestVersion) : false
|
|
2471
|
+
};
|
|
2463
2472
|
}
|
|
2464
2473
|
|
|
2465
|
-
const
|
|
2466
|
-
|
|
2467
|
-
resolveAiOutputLanguage: resolveAiOutputLanguage,
|
|
2468
|
-
selectAiOutputLanguage: selectAiOutputLanguage,
|
|
2469
|
-
selectScriptLanguage: selectScriptLanguage
|
|
2470
|
-
};
|
|
2471
|
-
|
|
2472
|
-
async function selectMcpServices(scriptLang) {
|
|
2474
|
+
const execAsync$2 = promisify$1(exec$2);
|
|
2475
|
+
async function updateCcr(scriptLang, force = false) {
|
|
2473
2476
|
const i18n = getTranslation(scriptLang);
|
|
2474
|
-
const
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
if (services === void 0) {
|
|
2486
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2487
|
-
return void 0;
|
|
2488
|
-
}
|
|
2489
|
-
return services;
|
|
2490
|
-
}
|
|
2491
|
-
|
|
2492
|
-
const WORKFLOW_CONFIGS = [
|
|
2493
|
-
{
|
|
2494
|
-
id: "sixStepsWorkflow",
|
|
2495
|
-
nameKey: "workflowOption.sixStepsWorkflow",
|
|
2496
|
-
descriptionKey: "workflowDescription.sixStepsWorkflow",
|
|
2497
|
-
defaultSelected: true,
|
|
2498
|
-
order: 1,
|
|
2499
|
-
commands: ["workflow.md"],
|
|
2500
|
-
agents: [],
|
|
2501
|
-
autoInstallAgents: false,
|
|
2502
|
-
category: "sixStep",
|
|
2503
|
-
outputDir: "workflow"
|
|
2504
|
-
},
|
|
2505
|
-
{
|
|
2506
|
-
id: "featPlanUx",
|
|
2507
|
-
nameKey: "workflowOption.featPlanUx",
|
|
2508
|
-
descriptionKey: "workflowDescription.featPlanUx",
|
|
2509
|
-
defaultSelected: true,
|
|
2510
|
-
order: 2,
|
|
2511
|
-
commands: ["feat.md"],
|
|
2512
|
-
agents: [
|
|
2513
|
-
{ id: "planner", filename: "planner.md", required: true },
|
|
2514
|
-
{ id: "ui-ux-designer", filename: "ui-ux-designer.md", required: true }
|
|
2515
|
-
],
|
|
2516
|
-
autoInstallAgents: true,
|
|
2517
|
-
category: "plan",
|
|
2518
|
-
outputDir: "feat"
|
|
2519
|
-
},
|
|
2520
|
-
{
|
|
2521
|
-
id: "gitWorkflow",
|
|
2522
|
-
nameKey: "workflowOption.gitWorkflow",
|
|
2523
|
-
descriptionKey: "workflowDescription.gitWorkflow",
|
|
2524
|
-
defaultSelected: true,
|
|
2525
|
-
order: 3,
|
|
2526
|
-
commands: ["git-commit.md", "git-rollback.md", "git-cleanBranches.md", "git-worktree.md"],
|
|
2527
|
-
agents: [],
|
|
2528
|
-
autoInstallAgents: false,
|
|
2529
|
-
category: "git",
|
|
2530
|
-
outputDir: "git"
|
|
2531
|
-
},
|
|
2532
|
-
{
|
|
2533
|
-
id: "bmadWorkflow",
|
|
2534
|
-
nameKey: "workflowOption.bmadWorkflow",
|
|
2535
|
-
descriptionKey: "workflowDescription.bmadWorkflow",
|
|
2536
|
-
defaultSelected: true,
|
|
2537
|
-
order: 4,
|
|
2538
|
-
commands: ["bmad-init.md"],
|
|
2539
|
-
agents: [],
|
|
2540
|
-
autoInstallAgents: false,
|
|
2541
|
-
category: "bmad",
|
|
2542
|
-
outputDir: "bmad"
|
|
2543
|
-
}
|
|
2544
|
-
];
|
|
2545
|
-
function getWorkflowConfig(workflowId) {
|
|
2546
|
-
return WORKFLOW_CONFIGS.find((config) => config.id === workflowId);
|
|
2547
|
-
}
|
|
2548
|
-
function getOrderedWorkflows() {
|
|
2549
|
-
return [...WORKFLOW_CONFIGS].sort((a, b) => a.order - b.order);
|
|
2550
|
-
}
|
|
2551
|
-
|
|
2552
|
-
function getRootDir() {
|
|
2553
|
-
const currentFilePath = fileURLToPath(import.meta.url);
|
|
2554
|
-
const distDir = dirname(dirname(currentFilePath));
|
|
2555
|
-
return dirname(distDir);
|
|
2556
|
-
}
|
|
2557
|
-
async function selectAndInstallWorkflows(configLang, scriptLang) {
|
|
2558
|
-
const i18n = getTranslation(scriptLang);
|
|
2559
|
-
const workflows = getOrderedWorkflows();
|
|
2560
|
-
const choices = workflows.map((workflow) => {
|
|
2561
|
-
const nameKey = workflow.id;
|
|
2562
|
-
const name = i18n.workflow.workflowOption[nameKey] || workflow.id;
|
|
2563
|
-
return {
|
|
2564
|
-
name,
|
|
2565
|
-
value: workflow.id,
|
|
2566
|
-
checked: workflow.defaultSelected
|
|
2567
|
-
};
|
|
2568
|
-
});
|
|
2569
|
-
const { selectedWorkflows } = await inquirer.prompt({
|
|
2570
|
-
type: "checkbox",
|
|
2571
|
-
name: "selectedWorkflows",
|
|
2572
|
-
message: `${i18n.workflow.selectWorkflowType}${i18n.common.multiSelectHint}`,
|
|
2573
|
-
choices
|
|
2574
|
-
});
|
|
2575
|
-
if (!selectedWorkflows || selectedWorkflows.length === 0) {
|
|
2576
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2577
|
-
return;
|
|
2578
|
-
}
|
|
2579
|
-
await cleanupOldVersionFiles(scriptLang);
|
|
2580
|
-
for (const workflowId of selectedWorkflows) {
|
|
2581
|
-
const config = getWorkflowConfig(workflowId);
|
|
2582
|
-
if (config) {
|
|
2583
|
-
await installWorkflowWithDependencies(config, configLang, scriptLang);
|
|
2477
|
+
const spinner = ora(i18n.updater.checkingVersion).start();
|
|
2478
|
+
try {
|
|
2479
|
+
const { installed, currentVersion, latestVersion, needsUpdate } = await checkCcrVersion();
|
|
2480
|
+
spinner.stop();
|
|
2481
|
+
if (!installed) {
|
|
2482
|
+
console.log(ansis.yellow(i18n.updater.ccrNotInstalled));
|
|
2483
|
+
return false;
|
|
2484
|
+
}
|
|
2485
|
+
if (!needsUpdate && !force) {
|
|
2486
|
+
console.log(ansis.green(format(i18n.updater.ccrUpToDate, { version: currentVersion || "" })));
|
|
2487
|
+
return true;
|
|
2584
2488
|
}
|
|
2489
|
+
if (!latestVersion) {
|
|
2490
|
+
console.log(ansis.yellow(i18n.updater.cannotCheckVersion));
|
|
2491
|
+
return false;
|
|
2492
|
+
}
|
|
2493
|
+
console.log(ansis.cyan(format(i18n.updater.currentVersion, { version: currentVersion || "" })));
|
|
2494
|
+
console.log(ansis.cyan(format(i18n.updater.latestVersion, { version: latestVersion })));
|
|
2495
|
+
const { confirm } = await inquirer.prompt({
|
|
2496
|
+
type: "confirm",
|
|
2497
|
+
name: "confirm",
|
|
2498
|
+
message: format(i18n.updater.confirmUpdate, { tool: "CCR" }),
|
|
2499
|
+
default: true
|
|
2500
|
+
});
|
|
2501
|
+
if (!confirm) {
|
|
2502
|
+
console.log(ansis.gray(i18n.updater.updateSkipped));
|
|
2503
|
+
return true;
|
|
2504
|
+
}
|
|
2505
|
+
const updateSpinner = ora(format(i18n.updater.updating, { tool: "CCR" })).start();
|
|
2506
|
+
try {
|
|
2507
|
+
await execAsync$2("npm update -g @musistudio/claude-code-router");
|
|
2508
|
+
updateSpinner.succeed(format(i18n.updater.updateSuccess, { tool: "CCR" }));
|
|
2509
|
+
return true;
|
|
2510
|
+
} catch (error) {
|
|
2511
|
+
updateSpinner.fail(format(i18n.updater.updateFailed, { tool: "CCR" }));
|
|
2512
|
+
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2513
|
+
return false;
|
|
2514
|
+
}
|
|
2515
|
+
} catch (error) {
|
|
2516
|
+
spinner.fail(i18n.updater.checkFailed);
|
|
2517
|
+
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2518
|
+
return false;
|
|
2585
2519
|
}
|
|
2586
2520
|
}
|
|
2587
|
-
async function
|
|
2588
|
-
const rootDir = getRootDir();
|
|
2521
|
+
async function updateClaudeCode(scriptLang, force = false) {
|
|
2589
2522
|
const i18n = getTranslation(scriptLang);
|
|
2590
|
-
const
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
const workflowName = i18n.workflow.workflowOption[config.id] || config.id;
|
|
2598
|
-
console.log(ansis.cyan(`
|
|
2599
|
-
\u{1F4E6} ${i18n.workflow.installingWorkflow}: ${workflowName}...`));
|
|
2600
|
-
const commandsDir = join(CLAUDE_DIR, "commands", "zcf");
|
|
2601
|
-
if (!existsSync(commandsDir)) {
|
|
2602
|
-
await mkdir(commandsDir, { recursive: true });
|
|
2603
|
-
}
|
|
2604
|
-
for (const commandFile of config.commands) {
|
|
2605
|
-
const commandSource = join(rootDir, "templates", configLang, "workflow", config.category, "commands", commandFile);
|
|
2606
|
-
const destFileName = commandFile;
|
|
2607
|
-
const commandDest = join(commandsDir, destFileName);
|
|
2608
|
-
if (existsSync(commandSource)) {
|
|
2609
|
-
try {
|
|
2610
|
-
await copyFile$1(commandSource, commandDest);
|
|
2611
|
-
result.installedCommands.push(destFileName);
|
|
2612
|
-
console.log(ansis.gray(` \u2714 ${i18n.workflow.installedCommand}: zcf/${destFileName}`));
|
|
2613
|
-
} catch (error) {
|
|
2614
|
-
const errorMsg = `${i18n.workflow.failedToInstallCommand} ${commandFile}: ${error}`;
|
|
2615
|
-
result.errors?.push(errorMsg);
|
|
2616
|
-
console.error(ansis.red(` \u2717 ${errorMsg}`));
|
|
2617
|
-
result.success = false;
|
|
2618
|
-
}
|
|
2523
|
+
const spinner = ora(i18n.updater.checkingVersion).start();
|
|
2524
|
+
try {
|
|
2525
|
+
const { installed, currentVersion, latestVersion, needsUpdate } = await checkClaudeCodeVersion();
|
|
2526
|
+
spinner.stop();
|
|
2527
|
+
if (!installed) {
|
|
2528
|
+
console.log(ansis.yellow(i18n.updater.claudeCodeNotInstalled));
|
|
2529
|
+
return false;
|
|
2619
2530
|
}
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
if (!existsSync(agentsCategoryDir)) {
|
|
2624
|
-
await mkdir(agentsCategoryDir, { recursive: true });
|
|
2531
|
+
if (!needsUpdate && !force) {
|
|
2532
|
+
console.log(ansis.green(format(i18n.updater.claudeCodeUpToDate, { version: currentVersion || "" })));
|
|
2533
|
+
return true;
|
|
2625
2534
|
}
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
if (existsSync(agentSource)) {
|
|
2630
|
-
try {
|
|
2631
|
-
await copyFile$1(agentSource, agentDest);
|
|
2632
|
-
result.installedAgents.push(agent.filename);
|
|
2633
|
-
console.log(ansis.gray(` \u2714 ${i18n.workflow.installedAgent}: zcf/${config.category}/${agent.filename}`));
|
|
2634
|
-
} catch (error) {
|
|
2635
|
-
const errorMsg = `${i18n.workflow.failedToInstallAgent} ${agent.filename}: ${error}`;
|
|
2636
|
-
result.errors?.push(errorMsg);
|
|
2637
|
-
console.error(ansis.red(` \u2717 ${errorMsg}`));
|
|
2638
|
-
if (agent.required) {
|
|
2639
|
-
result.success = false;
|
|
2640
|
-
}
|
|
2641
|
-
}
|
|
2642
|
-
}
|
|
2535
|
+
if (!latestVersion) {
|
|
2536
|
+
console.log(ansis.yellow(i18n.updater.cannotCheckVersion));
|
|
2537
|
+
return false;
|
|
2643
2538
|
}
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2539
|
+
console.log(ansis.cyan(format(i18n.updater.currentVersion, { version: currentVersion || "" })));
|
|
2540
|
+
console.log(ansis.cyan(format(i18n.updater.latestVersion, { version: latestVersion })));
|
|
2541
|
+
const { confirm } = await inquirer.prompt({
|
|
2542
|
+
type: "confirm",
|
|
2543
|
+
name: "confirm",
|
|
2544
|
+
message: format(i18n.updater.confirmUpdate, { tool: "Claude Code" }),
|
|
2545
|
+
default: true
|
|
2546
|
+
});
|
|
2547
|
+
if (!confirm) {
|
|
2548
|
+
console.log(ansis.gray(i18n.updater.updateSkipped));
|
|
2549
|
+
return true;
|
|
2650
2550
|
}
|
|
2651
|
-
|
|
2652
|
-
|
|
2551
|
+
const updateSpinner = ora(format(i18n.updater.updating, { tool: "Claude Code" })).start();
|
|
2552
|
+
try {
|
|
2553
|
+
await execAsync$2("npm update -g @anthropic-ai/claude-code");
|
|
2554
|
+
updateSpinner.succeed(format(i18n.updater.updateSuccess, { tool: "Claude Code" }));
|
|
2555
|
+
return true;
|
|
2556
|
+
} catch (error) {
|
|
2557
|
+
updateSpinner.fail(format(i18n.updater.updateFailed, { tool: "Claude Code" }));
|
|
2558
|
+
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2559
|
+
return false;
|
|
2560
|
+
}
|
|
2561
|
+
} catch (error) {
|
|
2562
|
+
spinner.fail(i18n.updater.checkFailed);
|
|
2563
|
+
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2564
|
+
return false;
|
|
2653
2565
|
}
|
|
2654
|
-
return result;
|
|
2655
2566
|
}
|
|
2656
|
-
async function
|
|
2567
|
+
async function updateCometixLine(scriptLang, force = false) {
|
|
2657
2568
|
const i18n = getTranslation(scriptLang);
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
join(CLAUDE_DIR, "agents", "planner.md"),
|
|
2666
|
-
join(CLAUDE_DIR, "agents", "ui-ux-designer.md")
|
|
2667
|
-
];
|
|
2668
|
-
for (const file of oldCommandFiles) {
|
|
2669
|
-
if (existsSync(file)) {
|
|
2670
|
-
try {
|
|
2671
|
-
await rm(file, { force: true });
|
|
2672
|
-
console.log(ansis.gray(` \u2714 ${i18n.workflow.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2673
|
-
} catch (error) {
|
|
2674
|
-
console.error(ansis.yellow(` \u26A0 ${i18n.workflow.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2675
|
-
}
|
|
2569
|
+
const spinner = ora(i18n.updater.checkingVersion).start();
|
|
2570
|
+
try {
|
|
2571
|
+
const { installed, currentVersion, latestVersion, needsUpdate } = await checkCometixLineVersion();
|
|
2572
|
+
spinner.stop();
|
|
2573
|
+
if (!installed) {
|
|
2574
|
+
console.log(ansis.yellow(i18n.updater.cometixLineNotInstalled));
|
|
2575
|
+
return false;
|
|
2676
2576
|
}
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
try {
|
|
2681
|
-
await rm(file, { force: true });
|
|
2682
|
-
console.log(ansis.gray(` \u2714 ${i18n.workflow.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2683
|
-
} catch (error) {
|
|
2684
|
-
console.error(ansis.yellow(` \u26A0 ${i18n.workflow.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
2685
|
-
}
|
|
2577
|
+
if (!needsUpdate && !force) {
|
|
2578
|
+
console.log(ansis.green(format(i18n.updater.cometixLineUpToDate, { version: currentVersion || "" })));
|
|
2579
|
+
return true;
|
|
2686
2580
|
}
|
|
2581
|
+
if (!latestVersion) {
|
|
2582
|
+
console.log(ansis.yellow(i18n.updater.cannotCheckVersion));
|
|
2583
|
+
return false;
|
|
2584
|
+
}
|
|
2585
|
+
console.log(ansis.cyan(format(i18n.updater.currentVersion, { version: currentVersion || "" })));
|
|
2586
|
+
console.log(ansis.cyan(format(i18n.updater.latestVersion, { version: latestVersion })));
|
|
2587
|
+
const { confirm } = await inquirer.prompt({
|
|
2588
|
+
type: "confirm",
|
|
2589
|
+
name: "confirm",
|
|
2590
|
+
message: format(i18n.updater.confirmUpdate, { tool: "CCometixLine" }),
|
|
2591
|
+
default: true
|
|
2592
|
+
});
|
|
2593
|
+
if (!confirm) {
|
|
2594
|
+
console.log(ansis.gray(i18n.updater.updateSkipped));
|
|
2595
|
+
return true;
|
|
2596
|
+
}
|
|
2597
|
+
const updateSpinner = ora(format(i18n.updater.updating, { tool: "CCometixLine" })).start();
|
|
2598
|
+
try {
|
|
2599
|
+
await execAsync$2("cargo install ccometix");
|
|
2600
|
+
updateSpinner.succeed(format(i18n.updater.updateSuccess, { tool: "CCometixLine" }));
|
|
2601
|
+
return true;
|
|
2602
|
+
} catch (error) {
|
|
2603
|
+
updateSpinner.fail(format(i18n.updater.updateFailed, { tool: "CCometixLine" }));
|
|
2604
|
+
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2605
|
+
return false;
|
|
2606
|
+
}
|
|
2607
|
+
} catch (error) {
|
|
2608
|
+
spinner.fail(i18n.updater.checkFailed);
|
|
2609
|
+
console.error(ansis.red(error instanceof Error ? error.message : String(error)));
|
|
2610
|
+
return false;
|
|
2687
2611
|
}
|
|
2688
2612
|
}
|
|
2613
|
+
async function checkAndUpdateTools(scriptLang) {
|
|
2614
|
+
const i18n = getTranslation(scriptLang);
|
|
2615
|
+
console.log(ansis.bold.cyan(`
|
|
2616
|
+
\u{1F50D} ${i18n.updater.checkingTools}
|
|
2617
|
+
`));
|
|
2618
|
+
await updateCcr(scriptLang);
|
|
2619
|
+
console.log();
|
|
2620
|
+
await updateClaudeCode(scriptLang);
|
|
2621
|
+
console.log();
|
|
2622
|
+
await updateCometixLine(scriptLang);
|
|
2623
|
+
}
|
|
2689
2624
|
|
|
2690
|
-
const execAsync$
|
|
2625
|
+
const execAsync$1 = promisify(exec$1);
|
|
2691
2626
|
async function isCcrInstalled() {
|
|
2692
2627
|
let commandExists = false;
|
|
2693
2628
|
try {
|
|
2694
|
-
await execAsync$
|
|
2629
|
+
await execAsync$1("ccr version");
|
|
2695
2630
|
commandExists = true;
|
|
2696
2631
|
} catch {
|
|
2697
2632
|
try {
|
|
2698
|
-
await execAsync$
|
|
2633
|
+
await execAsync$1("which ccr");
|
|
2699
2634
|
commandExists = true;
|
|
2700
2635
|
} catch {
|
|
2701
2636
|
commandExists = false;
|
|
@@ -2703,7 +2638,7 @@ async function isCcrInstalled() {
|
|
|
2703
2638
|
}
|
|
2704
2639
|
let hasCorrectPackage = false;
|
|
2705
2640
|
try {
|
|
2706
|
-
await execAsync$
|
|
2641
|
+
await execAsync$1("npm list -g @musistudio/claude-code-router");
|
|
2707
2642
|
hasCorrectPackage = true;
|
|
2708
2643
|
} catch {
|
|
2709
2644
|
hasCorrectPackage = false;
|
|
@@ -2723,549 +2658,735 @@ async function installCcr(scriptLang) {
|
|
|
2723
2658
|
}
|
|
2724
2659
|
if (isInstalled && !hasCorrectPackage) {
|
|
2725
2660
|
try {
|
|
2726
|
-
await execAsync$
|
|
2661
|
+
await execAsync$1("npm list -g claude-code-router");
|
|
2727
2662
|
console.log(ansis.yellow(`\u26A0 ${i18n.ccr.detectedIncorrectPackage}`));
|
|
2728
2663
|
try {
|
|
2729
|
-
await execAsync$
|
|
2664
|
+
await execAsync$1("npm uninstall -g claude-code-router");
|
|
2730
2665
|
console.log(ansis.green(`\u2714 ${i18n.ccr.uninstalledIncorrectPackage}`));
|
|
2731
2666
|
} catch (uninstallError) {
|
|
2732
2667
|
console.log(ansis.yellow(`\u26A0 ${i18n.ccr.failedToUninstallIncorrectPackage}`));
|
|
2733
2668
|
}
|
|
2734
|
-
} catch {
|
|
2669
|
+
} catch {
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
console.log(ansis.cyan(`\u{1F4E6} ${i18n.ccr.installingCcr}`));
|
|
2673
|
+
try {
|
|
2674
|
+
await execAsync$1("npm install -g @musistudio/claude-code-router --force");
|
|
2675
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrInstallSuccess}`));
|
|
2676
|
+
} catch (error) {
|
|
2677
|
+
if (error.message?.includes("EEXIST")) {
|
|
2678
|
+
console.log(ansis.yellow(`\u26A0 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
2679
|
+
await updateCcr(scriptLang);
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
console.error(ansis.red(`\u2716 ${i18n.ccr.ccrInstallFailed}`));
|
|
2683
|
+
throw error;
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
const COMETIX_PACKAGE_NAME = "@cometix/ccline";
|
|
2688
|
+
const COMETIX_COMMAND_NAME = "ccline";
|
|
2689
|
+
const COMETIX_COMMANDS = {
|
|
2690
|
+
CHECK_INSTALL: `npm list -g ${COMETIX_PACKAGE_NAME}`,
|
|
2691
|
+
INSTALL: `npm install -g ${COMETIX_PACKAGE_NAME}`,
|
|
2692
|
+
UPDATE: `npm update -g ${COMETIX_PACKAGE_NAME}`,
|
|
2693
|
+
PRINT_CONFIG: `${COMETIX_COMMAND_NAME} --print`
|
|
2694
|
+
};
|
|
2695
|
+
|
|
2696
|
+
function getPlatformStatusLineConfig() {
|
|
2697
|
+
return {
|
|
2698
|
+
type: "command",
|
|
2699
|
+
command: isWindows() ? "%USERPROFILE%\\.claude\\ccline\\ccline.exe" : "~/.claude/ccline/ccline",
|
|
2700
|
+
padding: 0
|
|
2701
|
+
};
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2704
|
+
function addCCometixLineConfig() {
|
|
2705
|
+
try {
|
|
2706
|
+
const statusLineConfig = getPlatformStatusLineConfig();
|
|
2707
|
+
let settings = {};
|
|
2708
|
+
if (exists(SETTINGS_FILE)) {
|
|
2709
|
+
settings = readJsonConfig(SETTINGS_FILE) || {};
|
|
2710
|
+
}
|
|
2711
|
+
settings.statusLine = statusLineConfig;
|
|
2712
|
+
writeJsonConfig(SETTINGS_FILE, settings);
|
|
2713
|
+
return true;
|
|
2714
|
+
} catch (error) {
|
|
2715
|
+
console.error("Failed to add CCometixLine configuration:", error);
|
|
2716
|
+
return false;
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
function hasCCometixLineConfig() {
|
|
2720
|
+
try {
|
|
2721
|
+
if (!exists(SETTINGS_FILE)) {
|
|
2722
|
+
return false;
|
|
2723
|
+
}
|
|
2724
|
+
const settings = readJsonConfig(SETTINGS_FILE);
|
|
2725
|
+
return !!settings?.statusLine?.command?.includes("ccline");
|
|
2726
|
+
} catch (error) {
|
|
2727
|
+
return false;
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
const execAsync = promisify(exec$1);
|
|
2732
|
+
async function isCometixLineInstalled() {
|
|
2733
|
+
try {
|
|
2734
|
+
await execAsync(COMETIX_COMMANDS.CHECK_INSTALL);
|
|
2735
|
+
return true;
|
|
2736
|
+
} catch {
|
|
2737
|
+
return false;
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
async function installCometixLine(scriptLang) {
|
|
2741
|
+
const i18n = getTranslation(scriptLang);
|
|
2742
|
+
const isInstalled = await isCometixLineInstalled();
|
|
2743
|
+
if (isInstalled) {
|
|
2744
|
+
console.log(ansis.green(`\u2714 ${i18n.cometix.cometixAlreadyInstalled}`));
|
|
2745
|
+
try {
|
|
2746
|
+
console.log(ansis.blue(`${i18n.cometix.installingOrUpdating}`));
|
|
2747
|
+
await execAsync(COMETIX_COMMANDS.INSTALL);
|
|
2748
|
+
console.log(ansis.green(`\u2714 ${i18n.cometix.installUpdateSuccess}`));
|
|
2749
|
+
} catch (error) {
|
|
2750
|
+
console.warn(ansis.yellow(`\u26A0 ${i18n.cometix.installUpdateFailed}: ${error}`));
|
|
2751
|
+
}
|
|
2752
|
+
if (!hasCCometixLineConfig()) {
|
|
2753
|
+
try {
|
|
2754
|
+
addCCometixLineConfig();
|
|
2755
|
+
console.log(ansis.green(`\u2714 ${i18n.cometix.statusLineConfigured || "Claude Code statusLine configured"}`));
|
|
2756
|
+
} catch (error) {
|
|
2757
|
+
console.warn(ansis.yellow(`\u26A0 ${i18n.cometix.statusLineConfigFailed || "Failed to configure statusLine"}: ${error}`));
|
|
2758
|
+
}
|
|
2759
|
+
} else {
|
|
2760
|
+
console.log(ansis.blue(`\u2139 ${i18n.cometix.statusLineAlreadyConfigured || "Claude Code statusLine already configured"}`));
|
|
2735
2761
|
}
|
|
2762
|
+
return;
|
|
2736
2763
|
}
|
|
2737
|
-
console.log(ansis.cyan(`\u{1F4E6} ${i18n.ccr.installingCcr}`));
|
|
2738
2764
|
try {
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2765
|
+
console.log(ansis.blue(`${i18n.cometix.installingCometix}`));
|
|
2766
|
+
await execAsync(COMETIX_COMMANDS.INSTALL);
|
|
2767
|
+
console.log(ansis.green(`\u2714 ${i18n.cometix.cometixInstallSuccess}`));
|
|
2768
|
+
try {
|
|
2769
|
+
addCCometixLineConfig();
|
|
2770
|
+
console.log(ansis.green(`\u2714 ${i18n.cometix.statusLineConfigured || "Claude Code statusLine configured"}`));
|
|
2771
|
+
} catch (configError) {
|
|
2772
|
+
console.warn(ansis.yellow(`\u26A0 ${i18n.cometix.statusLineConfigFailed || "Failed to configure statusLine"}: ${configError}`));
|
|
2773
|
+
console.log(ansis.blue(`\u{1F4A1} ${i18n.cometix.statusLineManualConfig || "Please manually add statusLine configuration to Claude Code settings"}`));
|
|
2746
2774
|
}
|
|
2747
|
-
|
|
2775
|
+
} catch (error) {
|
|
2776
|
+
console.error(ansis.red(`\u2717 ${i18n.cometix.cometixInstallFailed}: ${error}`));
|
|
2748
2777
|
throw error;
|
|
2749
2778
|
}
|
|
2750
2779
|
}
|
|
2751
2780
|
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2781
|
+
function validateApiKey(apiKey, lang = "zh-CN") {
|
|
2782
|
+
const i18n = getTranslation(lang);
|
|
2783
|
+
if (!apiKey || apiKey.trim() === "") {
|
|
2784
|
+
return {
|
|
2785
|
+
isValid: false,
|
|
2786
|
+
error: i18n.api.apiKeyValidation.empty
|
|
2787
|
+
};
|
|
2788
|
+
}
|
|
2789
|
+
return { isValid: true };
|
|
2790
|
+
}
|
|
2791
|
+
function formatApiKeyDisplay(apiKey) {
|
|
2792
|
+
if (!apiKey || apiKey.length < 12) {
|
|
2793
|
+
return apiKey;
|
|
2794
|
+
}
|
|
2795
|
+
return `${apiKey.substring(0, 8)}...${apiKey.substring(apiKey.length - 4)}`;
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
async function configureApiCompletely(i18n, scriptLang, preselectedAuthType) {
|
|
2799
|
+
let authType = preselectedAuthType;
|
|
2800
|
+
if (!authType) {
|
|
2801
|
+
const { authType: selectedAuthType } = await inquirer.prompt({
|
|
2802
|
+
type: "list",
|
|
2803
|
+
name: "authType",
|
|
2804
|
+
message: i18n.api.configureApi,
|
|
2805
|
+
choices: addNumbersToChoices([
|
|
2806
|
+
{
|
|
2807
|
+
name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
|
|
2808
|
+
value: "auth_token",
|
|
2809
|
+
short: i18n.api.useAuthToken
|
|
2810
|
+
},
|
|
2811
|
+
{
|
|
2812
|
+
name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
|
|
2813
|
+
value: "api_key",
|
|
2814
|
+
short: i18n.api.useApiKey
|
|
2815
|
+
}
|
|
2816
|
+
])
|
|
2759
2817
|
});
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2818
|
+
if (!selectedAuthType) {
|
|
2819
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2820
|
+
return null;
|
|
2763
2821
|
}
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
});
|
|
2778
|
-
}
|
|
2779
|
-
}
|
|
2780
|
-
} else if (data && typeof data === "object") {
|
|
2781
|
-
for (const [key, value] of Object.entries(data)) {
|
|
2782
|
-
if (typeof value === "object" && value !== null) {
|
|
2783
|
-
const provider = value;
|
|
2784
|
-
presets.push({
|
|
2785
|
-
name: provider.name || key,
|
|
2786
|
-
provider: key,
|
|
2787
|
-
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
2788
|
-
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
2789
|
-
models: provider.models || [],
|
|
2790
|
-
description: provider.description || "",
|
|
2791
|
-
transformer: provider.transformer
|
|
2792
|
-
});
|
|
2793
|
-
}
|
|
2822
|
+
authType = selectedAuthType;
|
|
2823
|
+
}
|
|
2824
|
+
const { url } = await inquirer.prompt({
|
|
2825
|
+
type: "input",
|
|
2826
|
+
name: "url",
|
|
2827
|
+
message: i18n.api.enterApiUrl,
|
|
2828
|
+
validate: (value) => {
|
|
2829
|
+
if (!value) return i18n.api.urlRequired;
|
|
2830
|
+
try {
|
|
2831
|
+
new URL(value);
|
|
2832
|
+
return true;
|
|
2833
|
+
} catch {
|
|
2834
|
+
return i18n.api.invalidUrl;
|
|
2794
2835
|
}
|
|
2795
2836
|
}
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2837
|
+
});
|
|
2838
|
+
if (url === void 0) {
|
|
2839
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2840
|
+
return null;
|
|
2799
2841
|
}
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
models: ["qwen3-coder-plus"],
|
|
2809
|
-
description: "Alibaba DashScope",
|
|
2810
|
-
transformer: {
|
|
2811
|
-
use: [["maxtoken", { max_tokens: 65536 }]],
|
|
2812
|
-
"qwen3-coder-plus": {
|
|
2813
|
-
use: ["enhancetool"]
|
|
2814
|
-
}
|
|
2815
|
-
}
|
|
2816
|
-
},
|
|
2817
|
-
{
|
|
2818
|
-
name: "deepseek",
|
|
2819
|
-
provider: "deepseek",
|
|
2820
|
-
baseURL: "https://api.deepseek.com/chat/completions",
|
|
2821
|
-
requiresApiKey: true,
|
|
2822
|
-
models: ["deepseek-chat", "deepseek-reasoner"],
|
|
2823
|
-
description: "DeepSeek AI models",
|
|
2824
|
-
transformer: {
|
|
2825
|
-
use: ["deepseek"],
|
|
2826
|
-
"deepseek-chat": {
|
|
2827
|
-
use: ["tooluse"]
|
|
2828
|
-
}
|
|
2829
|
-
}
|
|
2830
|
-
},
|
|
2831
|
-
{
|
|
2832
|
-
name: "gemini",
|
|
2833
|
-
provider: "gemini",
|
|
2834
|
-
baseURL: "https://generativelanguage.googleapis.com/v1beta/models/",
|
|
2835
|
-
requiresApiKey: true,
|
|
2836
|
-
models: ["gemini-2.5-flash", "gemini-2.5-pro"],
|
|
2837
|
-
description: "Google Gemini models",
|
|
2838
|
-
transformer: {
|
|
2839
|
-
use: ["gemini"]
|
|
2840
|
-
}
|
|
2841
|
-
},
|
|
2842
|
-
{
|
|
2843
|
-
name: "modelscope",
|
|
2844
|
-
provider: "modelscope",
|
|
2845
|
-
baseURL: "https://api-inference.modelscope.cn/v1/chat/completions",
|
|
2846
|
-
requiresApiKey: true,
|
|
2847
|
-
models: ["Qwen/Qwen3-Coder-480B-A35B-Instruct", "Qwen/Qwen3-235B-A22B-Thinking-2507", "ZhipuAI/GLM-4.5"],
|
|
2848
|
-
description: "ModelScope AI models",
|
|
2849
|
-
transformer: {
|
|
2850
|
-
use: [["maxtoken", { max_tokens: 65536 }]],
|
|
2851
|
-
"Qwen/Qwen3-Coder-480B-A35B-Instruct": {
|
|
2852
|
-
use: ["enhancetool"]
|
|
2853
|
-
},
|
|
2854
|
-
"Qwen/Qwen3-235B-A22B-Thinking-2507": {
|
|
2855
|
-
use: ["reasoning"]
|
|
2856
|
-
}
|
|
2842
|
+
const keyMessage = authType === "auth_token" ? i18n.api.enterAuthToken : i18n.api.enterApiKey;
|
|
2843
|
+
const { key } = await inquirer.prompt({
|
|
2844
|
+
type: "input",
|
|
2845
|
+
name: "key",
|
|
2846
|
+
message: keyMessage,
|
|
2847
|
+
validate: (value) => {
|
|
2848
|
+
if (!value) {
|
|
2849
|
+
return i18n.api.keyRequired;
|
|
2857
2850
|
}
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
provider: "openrouter",
|
|
2862
|
-
baseURL: "https://openrouter.ai/api/v1/chat/completions",
|
|
2863
|
-
requiresApiKey: true,
|
|
2864
|
-
models: [
|
|
2865
|
-
"google/gemini-2.5-pro-preview",
|
|
2866
|
-
"anthropic/claude-sonnet-4",
|
|
2867
|
-
"anthropic/claude-3.5-sonnet",
|
|
2868
|
-
"anthropic/claude-3.7-sonnet:thinking"
|
|
2869
|
-
],
|
|
2870
|
-
description: "OpenRouter API",
|
|
2871
|
-
transformer: {
|
|
2872
|
-
use: ["openrouter"]
|
|
2851
|
+
const validation = validateApiKey(value, scriptLang);
|
|
2852
|
+
if (!validation.isValid) {
|
|
2853
|
+
return validation.error || i18n.api.invalidKeyFormat;
|
|
2873
2854
|
}
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2855
|
+
return true;
|
|
2856
|
+
}
|
|
2857
|
+
});
|
|
2858
|
+
if (key === void 0) {
|
|
2859
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2860
|
+
return null;
|
|
2861
|
+
}
|
|
2862
|
+
console.log(ansis.gray(` API Key: ${formatApiKeyDisplay(key)}`));
|
|
2863
|
+
return { url, key, authType };
|
|
2864
|
+
}
|
|
2865
|
+
async function modifyApiConfigPartially(existingConfig, i18n, scriptLang) {
|
|
2866
|
+
let currentConfig = { ...existingConfig };
|
|
2867
|
+
const latestConfig = getExistingApiConfig();
|
|
2868
|
+
if (latestConfig) {
|
|
2869
|
+
currentConfig = latestConfig;
|
|
2870
|
+
}
|
|
2871
|
+
const { item } = await inquirer.prompt({
|
|
2872
|
+
type: "list",
|
|
2873
|
+
name: "item",
|
|
2874
|
+
message: i18n.api.selectModifyItems,
|
|
2875
|
+
choices: addNumbersToChoices([
|
|
2876
|
+
{ name: i18n.api.modifyApiUrl, value: "url" },
|
|
2877
|
+
{ name: i18n.api.modifyApiKey, value: "key" },
|
|
2878
|
+
{ name: i18n.api.modifyAuthType, value: "authType" }
|
|
2879
|
+
])
|
|
2880
|
+
});
|
|
2881
|
+
if (!item) {
|
|
2882
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2885
|
+
if (item === "url") {
|
|
2886
|
+
const { url } = await inquirer.prompt({
|
|
2887
|
+
type: "input",
|
|
2888
|
+
name: "url",
|
|
2889
|
+
message: i18n.api.enterNewApiUrl.replace("{url}", currentConfig.url || i18n.common.none),
|
|
2890
|
+
default: currentConfig.url,
|
|
2891
|
+
validate: (value) => {
|
|
2892
|
+
if (!value) return i18n.api.urlRequired;
|
|
2893
|
+
try {
|
|
2894
|
+
new URL(value);
|
|
2895
|
+
return true;
|
|
2896
|
+
} catch {
|
|
2897
|
+
return i18n.api.invalidUrl;
|
|
2898
|
+
}
|
|
2884
2899
|
}
|
|
2885
|
-
}
|
|
2886
|
-
{
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2900
|
+
});
|
|
2901
|
+
if (url === void 0) {
|
|
2902
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2903
|
+
return;
|
|
2904
|
+
}
|
|
2905
|
+
currentConfig.url = url;
|
|
2906
|
+
const savedConfig = configureApi(currentConfig);
|
|
2907
|
+
if (savedConfig) {
|
|
2908
|
+
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
2909
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${savedConfig.url}`));
|
|
2910
|
+
}
|
|
2911
|
+
} else if (item === "key") {
|
|
2912
|
+
const authType = currentConfig.authType || "auth_token";
|
|
2913
|
+
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);
|
|
2914
|
+
const { key } = await inquirer.prompt({
|
|
2915
|
+
type: "input",
|
|
2916
|
+
name: "key",
|
|
2917
|
+
message: keyMessage,
|
|
2918
|
+
validate: (value) => {
|
|
2919
|
+
if (!value) {
|
|
2920
|
+
return i18n.api.keyRequired;
|
|
2921
|
+
}
|
|
2922
|
+
const validation = validateApiKey(value, scriptLang);
|
|
2923
|
+
if (!validation.isValid) {
|
|
2924
|
+
return validation.error || i18n.api.invalidKeyFormat;
|
|
2925
|
+
}
|
|
2926
|
+
return true;
|
|
2895
2927
|
}
|
|
2928
|
+
});
|
|
2929
|
+
if (key === void 0) {
|
|
2930
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2931
|
+
return;
|
|
2896
2932
|
}
|
|
2897
|
-
|
|
2933
|
+
currentConfig.key = key;
|
|
2934
|
+
const savedConfig = configureApi(currentConfig);
|
|
2935
|
+
if (savedConfig) {
|
|
2936
|
+
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
2937
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigKey}: ${formatApiKeyDisplay(savedConfig.key)}`));
|
|
2938
|
+
}
|
|
2939
|
+
} else if (item === "authType") {
|
|
2940
|
+
const { authType } = await inquirer.prompt({
|
|
2941
|
+
type: "list",
|
|
2942
|
+
name: "authType",
|
|
2943
|
+
message: i18n.api.selectNewAuthType.replace("{type}", currentConfig.authType || i18n.common.none),
|
|
2944
|
+
choices: addNumbersToChoices([
|
|
2945
|
+
{ name: "Auth Token (OAuth)", value: "auth_token" },
|
|
2946
|
+
{ name: "API Key", value: "api_key" }
|
|
2947
|
+
]),
|
|
2948
|
+
default: currentConfig.authType === "api_key" ? 1 : 0
|
|
2949
|
+
});
|
|
2950
|
+
if (authType === void 0) {
|
|
2951
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2952
|
+
return;
|
|
2953
|
+
}
|
|
2954
|
+
currentConfig.authType = authType;
|
|
2955
|
+
const savedConfig = configureApi(currentConfig);
|
|
2956
|
+
if (savedConfig) {
|
|
2957
|
+
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
2958
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigAuthType}: ${savedConfig.authType}`));
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
async function updatePromptOnly(configLang, scriptLang, aiOutputLang) {
|
|
2963
|
+
const i18n = getTranslation(scriptLang);
|
|
2964
|
+
const backupDir = backupExistingConfig();
|
|
2965
|
+
if (backupDir) {
|
|
2966
|
+
console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
|
|
2967
|
+
}
|
|
2968
|
+
copyConfigFiles(configLang, true);
|
|
2969
|
+
if (aiOutputLang) {
|
|
2970
|
+
applyAiLanguageDirective(aiOutputLang);
|
|
2971
|
+
}
|
|
2972
|
+
await configureAiPersonality(scriptLang);
|
|
2973
|
+
console.log(ansis.green(`\u2714 ${i18n.configuration.configSuccess} ${CLAUDE_DIR}`));
|
|
2974
|
+
console.log("\n" + ansis.cyan(i18n.common.complete));
|
|
2898
2975
|
}
|
|
2899
2976
|
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
const
|
|
2903
|
-
const
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2977
|
+
function handleExitPromptError(error) {
|
|
2978
|
+
if (error instanceof Error && error.name === "ExitPromptError") {
|
|
2979
|
+
const zcfConfig = readZcfConfig();
|
|
2980
|
+
const defaultLang = zcfConfig?.preferredLang || "zh-CN";
|
|
2981
|
+
const i18n = getTranslation(defaultLang);
|
|
2982
|
+
console.log(ansis.cyan(`
|
|
2983
|
+
${i18n.common.goodbye}
|
|
2984
|
+
`));
|
|
2985
|
+
process.exit(0);
|
|
2907
2986
|
}
|
|
2987
|
+
return false;
|
|
2908
2988
|
}
|
|
2909
|
-
function
|
|
2910
|
-
const
|
|
2989
|
+
function handleGeneralError(error, lang) {
|
|
2990
|
+
const zcfConfig = readZcfConfig();
|
|
2991
|
+
const defaultLang = lang || zcfConfig?.preferredLang || "en";
|
|
2992
|
+
const i18n = getTranslation(defaultLang);
|
|
2993
|
+
const errorMsg = i18n.common.error || "Error";
|
|
2994
|
+
console.error(ansis.red(`${errorMsg}:`), error);
|
|
2995
|
+
if (error instanceof Error) {
|
|
2996
|
+
console.error(ansis.gray(`Stack: ${error.stack}`));
|
|
2997
|
+
}
|
|
2998
|
+
process.exit(1);
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
async function isClaudeCodeInstalled() {
|
|
3002
|
+
return await commandExists("claude");
|
|
3003
|
+
}
|
|
3004
|
+
async function installClaudeCode(lang) {
|
|
3005
|
+
const i18n = getTranslation(lang);
|
|
3006
|
+
const installed = await isClaudeCodeInstalled();
|
|
3007
|
+
if (installed) {
|
|
3008
|
+
console.log(ansis.green(`\u2714 ${i18n.installation.alreadyInstalled}`));
|
|
3009
|
+
await updateClaudeCode(lang);
|
|
3010
|
+
return;
|
|
3011
|
+
}
|
|
3012
|
+
if (isTermux()) {
|
|
3013
|
+
console.log(ansis.yellow(`\u2139 ${i18n.installation.termuxDetected}`));
|
|
3014
|
+
const termuxPrefix = getTermuxPrefix();
|
|
3015
|
+
console.log(ansis.gray(i18n.installation.termuxPathInfo.replace("{path}", termuxPrefix)));
|
|
3016
|
+
console.log(ansis.gray(`Node.js: ${termuxPrefix}/bin/node`));
|
|
3017
|
+
console.log(ansis.gray(`npm: ${termuxPrefix}/bin/npm`));
|
|
3018
|
+
}
|
|
3019
|
+
console.log(i18n.installation.installing);
|
|
2911
3020
|
try {
|
|
2912
|
-
|
|
2913
|
-
|
|
3021
|
+
await exec("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
|
|
3022
|
+
console.log(`\u2714 ${i18n.installation.installSuccess}`);
|
|
3023
|
+
if (isTermux()) {
|
|
3024
|
+
console.log(ansis.gray(`
|
|
3025
|
+
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
2914
3026
|
}
|
|
2915
|
-
const timestamp = dayjs().format("YYYY-MM-DDTHH-mm-ss-SSS") + "Z";
|
|
2916
|
-
const backupFileName = `config.json.${timestamp}.bak`;
|
|
2917
|
-
const backupPath = join$1(CCR_BACKUP_DIR, backupFileName);
|
|
2918
|
-
console.log(ansis.cyan(`${i18n.ccr.backupCcrConfig}`));
|
|
2919
|
-
copyFileSync(CCR_CONFIG_FILE, backupPath);
|
|
2920
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrBackupSuccess.replace("{path}", backupPath)}`));
|
|
2921
|
-
return backupPath;
|
|
2922
3027
|
} catch (error) {
|
|
2923
|
-
console.error(
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
}
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
3028
|
+
console.error(`\u2716 ${i18n.installation.installFailed}`);
|
|
3029
|
+
if (isTermux()) {
|
|
3030
|
+
console.error(ansis.yellow(`
|
|
3031
|
+
${i18n.installation.termuxInstallHint}
|
|
3032
|
+
`));
|
|
3033
|
+
}
|
|
3034
|
+
throw error;
|
|
2930
3035
|
}
|
|
2931
|
-
return readJsonConfig(CCR_CONFIG_FILE);
|
|
2932
3036
|
}
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
const
|
|
2942
|
-
|
|
2943
|
-
|
|
3037
|
+
|
|
3038
|
+
async function selectMcpServices(scriptLang) {
|
|
3039
|
+
const i18n = getTranslation(scriptLang);
|
|
3040
|
+
const choices = MCP_SERVICES.map((service) => ({
|
|
3041
|
+
name: `${service.name[scriptLang]} - ${ansis.gray(service.description[scriptLang])}`,
|
|
3042
|
+
value: service.id,
|
|
3043
|
+
selected: false
|
|
3044
|
+
}));
|
|
3045
|
+
const { services } = await inquirer.prompt({
|
|
3046
|
+
type: "checkbox",
|
|
3047
|
+
name: "services",
|
|
3048
|
+
message: `${i18n.mcp.selectMcpServices}${i18n.common.multiSelectHint}`,
|
|
3049
|
+
choices
|
|
3050
|
+
});
|
|
3051
|
+
if (services === void 0) {
|
|
3052
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3053
|
+
return void 0;
|
|
2944
3054
|
}
|
|
2945
|
-
|
|
2946
|
-
settings.env.ANTHROPIC_API_KEY = apiKey;
|
|
2947
|
-
writeJsonConfig(SETTINGS_FILE, settings);
|
|
3055
|
+
return services;
|
|
2948
3056
|
}
|
|
2949
|
-
|
|
3057
|
+
|
|
3058
|
+
async function selectAiOutputLanguage(scriptLang, defaultLang) {
|
|
2950
3059
|
const i18n = getTranslation(scriptLang);
|
|
2951
|
-
console.log(ansis.
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
3060
|
+
console.log(ansis.dim(`
|
|
3061
|
+
${i18n.language.aiOutputLangHint}
|
|
3062
|
+
`));
|
|
3063
|
+
const aiLangChoices = Object.entries(AI_OUTPUT_LANGUAGES).map(([key, value]) => ({
|
|
3064
|
+
title: value.label,
|
|
3065
|
+
value: key
|
|
3066
|
+
}));
|
|
3067
|
+
const defaultChoice = defaultLang || (scriptLang === "zh-CN" ? "zh-CN" : "en");
|
|
3068
|
+
const { lang } = await inquirer.prompt({
|
|
3069
|
+
type: "list",
|
|
3070
|
+
name: "lang",
|
|
3071
|
+
message: i18n.language.selectAiOutputLang,
|
|
3072
|
+
choices: addNumbersToChoices(aiLangChoices.map((choice) => ({
|
|
3073
|
+
name: choice.title,
|
|
3074
|
+
value: choice.value
|
|
3075
|
+
}))),
|
|
3076
|
+
default: defaultChoice
|
|
3077
|
+
});
|
|
3078
|
+
if (!lang) {
|
|
3079
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3080
|
+
process.exit(0);
|
|
2956
3081
|
}
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
}))
|
|
2967
|
-
];
|
|
2968
|
-
const { preset } = await inquirer.prompt({
|
|
2969
|
-
type: "list",
|
|
2970
|
-
name: "preset",
|
|
2971
|
-
message: i18n.ccr.selectCcrPreset,
|
|
2972
|
-
choices
|
|
2973
|
-
});
|
|
2974
|
-
return preset;
|
|
2975
|
-
} catch (error) {
|
|
2976
|
-
if (error.name === "ExitPromptError") {
|
|
3082
|
+
let aiOutputLang = lang;
|
|
3083
|
+
if (aiOutputLang === "custom") {
|
|
3084
|
+
const { customLang } = await inquirer.prompt({
|
|
3085
|
+
type: "input",
|
|
3086
|
+
name: "customLang",
|
|
3087
|
+
message: i18n.language.enterCustomLanguage,
|
|
3088
|
+
validate: (value) => !!value || i18n.language?.languageRequired || "Language is required"
|
|
3089
|
+
});
|
|
3090
|
+
if (!customLang) {
|
|
2977
3091
|
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2978
|
-
|
|
3092
|
+
process.exit(0);
|
|
2979
3093
|
}
|
|
2980
|
-
|
|
3094
|
+
return customLang;
|
|
2981
3095
|
}
|
|
3096
|
+
return aiOutputLang;
|
|
2982
3097
|
}
|
|
2983
|
-
async function
|
|
2984
|
-
const
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
// Use the original name from JSON
|
|
2988
|
-
api_base_url: preset.baseURL || "",
|
|
2989
|
-
api_key: "",
|
|
2990
|
-
models: preset.models
|
|
2991
|
-
};
|
|
2992
|
-
if (preset.transformer) {
|
|
2993
|
-
provider.transformer = preset.transformer;
|
|
3098
|
+
async function selectScriptLanguage(currentLang) {
|
|
3099
|
+
const zcfConfig = readZcfConfig();
|
|
3100
|
+
if (zcfConfig?.preferredLang) {
|
|
3101
|
+
return zcfConfig.preferredLang;
|
|
2994
3102
|
}
|
|
2995
|
-
if (
|
|
2996
|
-
|
|
2997
|
-
const { apiKey } = await inquirer.prompt({
|
|
2998
|
-
type: "input",
|
|
2999
|
-
name: "apiKey",
|
|
3000
|
-
message: i18n.ccr.enterApiKeyForProvider.replace("{provider}", preset.name),
|
|
3001
|
-
validate: (value) => !!value || i18n.api.keyRequired
|
|
3002
|
-
});
|
|
3003
|
-
provider.api_key = apiKey;
|
|
3004
|
-
} catch (error) {
|
|
3005
|
-
if (error.name === "ExitPromptError") {
|
|
3006
|
-
throw error;
|
|
3007
|
-
}
|
|
3008
|
-
throw error;
|
|
3009
|
-
}
|
|
3010
|
-
} else {
|
|
3011
|
-
provider.api_key = "sk-free";
|
|
3103
|
+
if (currentLang) {
|
|
3104
|
+
return currentLang;
|
|
3012
3105
|
}
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
defaultModel = model;
|
|
3026
|
-
} catch (error) {
|
|
3027
|
-
if (error.name === "ExitPromptError") {
|
|
3028
|
-
throw error;
|
|
3029
|
-
}
|
|
3030
|
-
throw error;
|
|
3031
|
-
}
|
|
3106
|
+
const { lang } = await inquirer.prompt({
|
|
3107
|
+
type: "list",
|
|
3108
|
+
name: "lang",
|
|
3109
|
+
message: "Select ZCF display language / \u9009\u62E9ZCF\u663E\u793A\u8BED\u8A00",
|
|
3110
|
+
choices: addNumbersToChoices(SUPPORTED_LANGS.map((l) => ({
|
|
3111
|
+
name: LANG_LABELS[l],
|
|
3112
|
+
value: l
|
|
3113
|
+
})))
|
|
3114
|
+
});
|
|
3115
|
+
if (!lang) {
|
|
3116
|
+
console.log(ansis.yellow("Operation cancelled / \u64CD\u4F5C\u5DF2\u53D6\u6D88"));
|
|
3117
|
+
process.exit(0);
|
|
3032
3118
|
}
|
|
3033
|
-
const
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
longContextThreshold: 6e4,
|
|
3040
|
-
webSearch: `${preset.name},${defaultModel}`
|
|
3041
|
-
};
|
|
3042
|
-
const config = {
|
|
3043
|
-
LOG: true,
|
|
3044
|
-
CLAUDE_PATH: "",
|
|
3045
|
-
HOST: "127.0.0.1",
|
|
3046
|
-
PORT: 3456,
|
|
3047
|
-
APIKEY: "sk-zcf-x-ccr",
|
|
3048
|
-
API_TIMEOUT_MS: "600000",
|
|
3049
|
-
PROXY_URL: "",
|
|
3050
|
-
transformers: [],
|
|
3051
|
-
Providers: [provider],
|
|
3052
|
-
Router: router
|
|
3053
|
-
};
|
|
3054
|
-
return config;
|
|
3119
|
+
const scriptLang = lang;
|
|
3120
|
+
updateZcfConfig({
|
|
3121
|
+
version,
|
|
3122
|
+
preferredLang: scriptLang
|
|
3123
|
+
});
|
|
3124
|
+
return scriptLang;
|
|
3055
3125
|
}
|
|
3056
|
-
async function
|
|
3126
|
+
async function resolveAiOutputLanguage(scriptLang, commandLineOption, savedConfig) {
|
|
3057
3127
|
const i18n = getTranslation(scriptLang);
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
console.log(ansis.
|
|
3063
|
-
|
|
3064
|
-
console.log(ansis.gray(stdout));
|
|
3065
|
-
} catch (error) {
|
|
3066
|
-
console.error(ansis.red(`${i18n.ccr.ccrRestartFailed}:`), error.message || error);
|
|
3067
|
-
if (process.env.DEBUG) {
|
|
3068
|
-
console.error("Full error:", error);
|
|
3069
|
-
}
|
|
3128
|
+
if (commandLineOption) {
|
|
3129
|
+
return commandLineOption;
|
|
3130
|
+
}
|
|
3131
|
+
if (savedConfig?.aiOutputLang) {
|
|
3132
|
+
console.log(ansis.gray(`\u2714 ${i18n.language.aiOutputLangHint}: ${savedConfig.aiOutputLang}`));
|
|
3133
|
+
return savedConfig.aiOutputLang;
|
|
3070
3134
|
}
|
|
3135
|
+
return await selectAiOutputLanguage(scriptLang, scriptLang);
|
|
3071
3136
|
}
|
|
3072
|
-
|
|
3137
|
+
|
|
3138
|
+
const prompts = {
|
|
3139
|
+
__proto__: null,
|
|
3140
|
+
resolveAiOutputLanguage: resolveAiOutputLanguage,
|
|
3141
|
+
selectAiOutputLanguage: selectAiOutputLanguage,
|
|
3142
|
+
selectScriptLanguage: selectScriptLanguage
|
|
3143
|
+
};
|
|
3144
|
+
|
|
3145
|
+
function getRootDir() {
|
|
3146
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
3147
|
+
const distDir = dirname(dirname(currentFilePath));
|
|
3148
|
+
return dirname(distDir);
|
|
3149
|
+
}
|
|
3150
|
+
async function selectAndInstallWorkflows(configLang, scriptLang, preselectedWorkflows) {
|
|
3073
3151
|
const i18n = getTranslation(scriptLang);
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3152
|
+
const workflows = getOrderedWorkflows();
|
|
3153
|
+
const choices = workflows.map((workflow) => {
|
|
3154
|
+
const nameKey = workflow.id;
|
|
3155
|
+
const name = i18n.workflow.workflowOption[nameKey] || workflow.id;
|
|
3156
|
+
return {
|
|
3157
|
+
name,
|
|
3158
|
+
value: workflow.id,
|
|
3159
|
+
checked: workflow.defaultSelected
|
|
3160
|
+
};
|
|
3161
|
+
});
|
|
3162
|
+
let selectedWorkflows;
|
|
3163
|
+
if (preselectedWorkflows) {
|
|
3164
|
+
selectedWorkflows = preselectedWorkflows;
|
|
3165
|
+
} else {
|
|
3166
|
+
const response = await inquirer.prompt({
|
|
3167
|
+
type: "checkbox",
|
|
3168
|
+
name: "selectedWorkflows",
|
|
3169
|
+
message: `${i18n.workflow.selectWorkflowType}${i18n.common.multiSelectHint}`,
|
|
3170
|
+
choices
|
|
3171
|
+
});
|
|
3172
|
+
selectedWorkflows = response.selectedWorkflows;
|
|
3173
|
+
}
|
|
3174
|
+
if (!selectedWorkflows || selectedWorkflows.length === 0) {
|
|
3175
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3176
|
+
return;
|
|
3177
|
+
}
|
|
3178
|
+
await cleanupOldVersionFiles(scriptLang);
|
|
3179
|
+
for (const workflowId of selectedWorkflows) {
|
|
3180
|
+
const config = getWorkflowConfig(workflowId);
|
|
3181
|
+
if (config) {
|
|
3182
|
+
await installWorkflowWithDependencies(config, configLang, scriptLang);
|
|
3183
|
+
}
|
|
3082
3184
|
}
|
|
3083
|
-
console.log("");
|
|
3084
3185
|
}
|
|
3085
|
-
async function
|
|
3186
|
+
async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
3187
|
+
const rootDir = getRootDir();
|
|
3086
3188
|
const i18n = getTranslation(scriptLang);
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3189
|
+
const result = {
|
|
3190
|
+
workflow: config.id,
|
|
3191
|
+
success: true,
|
|
3192
|
+
installedCommands: [],
|
|
3193
|
+
installedAgents: [],
|
|
3194
|
+
errors: []
|
|
3195
|
+
};
|
|
3196
|
+
const workflowName = i18n.workflow.workflowOption[config.id] || config.id;
|
|
3197
|
+
console.log(ansis.cyan(`
|
|
3198
|
+
\u{1F4E6} ${i18n.workflow.installingWorkflow}: ${workflowName}...`));
|
|
3199
|
+
const commandsDir = join(CLAUDE_DIR, "commands", "zcf");
|
|
3200
|
+
if (!existsSync(commandsDir)) {
|
|
3201
|
+
await mkdir(commandsDir, { recursive: true });
|
|
3202
|
+
}
|
|
3203
|
+
for (const commandFile of config.commands) {
|
|
3204
|
+
const commandSource = join(rootDir, "templates", configLang, "workflow", config.category, "commands", commandFile);
|
|
3205
|
+
const destFileName = commandFile;
|
|
3206
|
+
const commandDest = join(commandsDir, destFileName);
|
|
3207
|
+
if (existsSync(commandSource)) {
|
|
3092
3208
|
try {
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
message: i18n.ccr.overwriteCcrConfig,
|
|
3097
|
-
default: false
|
|
3098
|
-
});
|
|
3099
|
-
shouldBackupAndReconfigure = result.overwrite;
|
|
3209
|
+
await copyFile$1(commandSource, commandDest);
|
|
3210
|
+
result.installedCommands.push(destFileName);
|
|
3211
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.installedCommand}: zcf/${destFileName}`));
|
|
3100
3212
|
} catch (error) {
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
throw error;
|
|
3106
|
-
}
|
|
3107
|
-
if (!shouldBackupAndReconfigure) {
|
|
3108
|
-
console.log(ansis.yellow(`${i18n.ccr.keepingExistingConfig}`));
|
|
3109
|
-
await configureCcrProxy(existingConfig);
|
|
3110
|
-
return true;
|
|
3213
|
+
const errorMsg = `${i18n.workflow.failedToInstallCommand} ${commandFile}: ${error}`;
|
|
3214
|
+
result.errors?.push(errorMsg);
|
|
3215
|
+
console.error(ansis.red(` \u2717 ${errorMsg}`));
|
|
3216
|
+
result.success = false;
|
|
3111
3217
|
}
|
|
3112
|
-
backupCcrConfig(scriptLang);
|
|
3113
3218
|
}
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3219
|
+
}
|
|
3220
|
+
if (config.autoInstallAgents && config.agents.length > 0) {
|
|
3221
|
+
const agentsCategoryDir = join(CLAUDE_DIR, "agents", "zcf", config.category);
|
|
3222
|
+
if (!existsSync(agentsCategoryDir)) {
|
|
3223
|
+
await mkdir(agentsCategoryDir, { recursive: true });
|
|
3117
3224
|
}
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
// Empty router configuration - user will configure in CCR UI
|
|
3225
|
+
for (const agent of config.agents) {
|
|
3226
|
+
const agentSource = join(rootDir, "templates", configLang, "workflow", config.category, "agents", agent.filename);
|
|
3227
|
+
const agentDest = join(agentsCategoryDir, agent.filename);
|
|
3228
|
+
if (existsSync(agentSource)) {
|
|
3229
|
+
try {
|
|
3230
|
+
await copyFile$1(agentSource, agentDest);
|
|
3231
|
+
result.installedAgents.push(agent.filename);
|
|
3232
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.installedAgent}: zcf/${config.category}/${agent.filename}`));
|
|
3233
|
+
} catch (error) {
|
|
3234
|
+
const errorMsg = `${i18n.workflow.failedToInstallAgent} ${agent.filename}: ${error}`;
|
|
3235
|
+
result.errors?.push(errorMsg);
|
|
3236
|
+
console.error(ansis.red(` \u2717 ${errorMsg}`));
|
|
3237
|
+
if (agent.required) {
|
|
3238
|
+
result.success = false;
|
|
3239
|
+
}
|
|
3134
3240
|
}
|
|
3135
|
-
}
|
|
3136
|
-
} else {
|
|
3137
|
-
config = await configureCcrWithPreset(preset, scriptLang);
|
|
3138
|
-
}
|
|
3139
|
-
writeCcrConfig(config);
|
|
3140
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrConfigSuccess}`));
|
|
3141
|
-
await configureCcrProxy(config);
|
|
3142
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.proxyConfigSuccess}`));
|
|
3143
|
-
await restartAndCheckCcrStatus(scriptLang);
|
|
3144
|
-
showConfigurationTips(scriptLang, config.APIKEY);
|
|
3145
|
-
try {
|
|
3146
|
-
addCompletedOnboarding();
|
|
3147
|
-
} catch (error) {
|
|
3148
|
-
console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
|
|
3241
|
+
}
|
|
3149
3242
|
}
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3243
|
+
}
|
|
3244
|
+
if (result.success) {
|
|
3245
|
+
console.log(ansis.green(`\u2714 ${workflowName} ${i18n.workflow.workflowInstallSuccess}`));
|
|
3246
|
+
if (config.id === "bmadWorkflow") {
|
|
3247
|
+
console.log(ansis.cyan(`
|
|
3248
|
+
${i18n.workflow.bmadInitPrompt}`));
|
|
3155
3249
|
}
|
|
3156
|
-
|
|
3157
|
-
|
|
3250
|
+
} else {
|
|
3251
|
+
console.log(ansis.red(`\u2717 ${workflowName} ${i18n.workflow.workflowInstallError}`));
|
|
3158
3252
|
}
|
|
3253
|
+
return result;
|
|
3159
3254
|
}
|
|
3160
|
-
async function
|
|
3255
|
+
async function cleanupOldVersionFiles(scriptLang) {
|
|
3161
3256
|
const i18n = getTranslation(scriptLang);
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
};
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
type: "command",
|
|
3181
|
-
command: isWindows() ? "%USERPROFILE%\\.claude\\ccline\\ccline.exe" : "~/.claude/ccline/ccline",
|
|
3182
|
-
padding: 0
|
|
3183
|
-
};
|
|
3184
|
-
}
|
|
3185
|
-
|
|
3186
|
-
function addCCometixLineConfig() {
|
|
3187
|
-
try {
|
|
3188
|
-
const statusLineConfig = getPlatformStatusLineConfig();
|
|
3189
|
-
let settings = {};
|
|
3190
|
-
if (exists(SETTINGS_FILE)) {
|
|
3191
|
-
settings = readJsonConfig(SETTINGS_FILE) || {};
|
|
3257
|
+
console.log(ansis.cyan(`
|
|
3258
|
+
\u{1F9F9} ${i18n.workflow.cleaningOldFiles || "Cleaning up old version files"}...`));
|
|
3259
|
+
const oldCommandFiles = [
|
|
3260
|
+
join(CLAUDE_DIR, "commands", "workflow.md"),
|
|
3261
|
+
join(CLAUDE_DIR, "commands", "feat.md")
|
|
3262
|
+
];
|
|
3263
|
+
const oldAgentFiles = [
|
|
3264
|
+
join(CLAUDE_DIR, "agents", "planner.md"),
|
|
3265
|
+
join(CLAUDE_DIR, "agents", "ui-ux-designer.md")
|
|
3266
|
+
];
|
|
3267
|
+
for (const file of oldCommandFiles) {
|
|
3268
|
+
if (existsSync(file)) {
|
|
3269
|
+
try {
|
|
3270
|
+
await rm(file, { force: true });
|
|
3271
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
3272
|
+
} catch (error) {
|
|
3273
|
+
console.error(ansis.yellow(` \u26A0 ${i18n.workflow.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
3274
|
+
}
|
|
3192
3275
|
}
|
|
3193
|
-
settings.statusLine = statusLineConfig;
|
|
3194
|
-
writeJsonConfig(SETTINGS_FILE, settings);
|
|
3195
|
-
return true;
|
|
3196
|
-
} catch (error) {
|
|
3197
|
-
console.error("Failed to add CCometixLine configuration:", error);
|
|
3198
|
-
return false;
|
|
3199
3276
|
}
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3277
|
+
for (const file of oldAgentFiles) {
|
|
3278
|
+
if (existsSync(file)) {
|
|
3279
|
+
try {
|
|
3280
|
+
await rm(file, { force: true });
|
|
3281
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
3282
|
+
} catch (error) {
|
|
3283
|
+
console.error(ansis.yellow(` \u26A0 ${i18n.workflow.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
3284
|
+
}
|
|
3205
3285
|
}
|
|
3206
|
-
const settings = readJsonConfig(SETTINGS_FILE);
|
|
3207
|
-
return !!settings?.statusLine?.command?.includes("ccline");
|
|
3208
|
-
} catch (error) {
|
|
3209
|
-
return false;
|
|
3210
3286
|
}
|
|
3211
3287
|
}
|
|
3212
3288
|
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3289
|
+
function validateSkipPromptOptions(options) {
|
|
3290
|
+
if (options.allLang) {
|
|
3291
|
+
if (options.allLang === "zh-CN" || options.allLang === "en") {
|
|
3292
|
+
options.lang = options.allLang;
|
|
3293
|
+
options.configLang = options.allLang;
|
|
3294
|
+
options.aiOutputLang = options.allLang;
|
|
3295
|
+
} else {
|
|
3296
|
+
options.lang = "en";
|
|
3297
|
+
options.configLang = "en";
|
|
3298
|
+
options.aiOutputLang = options.allLang;
|
|
3299
|
+
}
|
|
3220
3300
|
}
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3301
|
+
if (!options.configAction) {
|
|
3302
|
+
options.configAction = "backup";
|
|
3303
|
+
}
|
|
3304
|
+
if (!options.lang) {
|
|
3305
|
+
options.lang = "en";
|
|
3306
|
+
}
|
|
3307
|
+
if (!options.configLang) {
|
|
3308
|
+
options.configLang = "en";
|
|
3309
|
+
}
|
|
3310
|
+
if (!options.aiOutputLang) {
|
|
3311
|
+
options.aiOutputLang = "en";
|
|
3312
|
+
}
|
|
3313
|
+
if (!options.aiPersonality) {
|
|
3314
|
+
options.aiPersonality = "professional";
|
|
3315
|
+
}
|
|
3316
|
+
if (typeof options.installCometixLine === "string") {
|
|
3317
|
+
options.installCometixLine = options.installCometixLine.toLowerCase() === "true";
|
|
3318
|
+
}
|
|
3319
|
+
if (options.installCometixLine === void 0) {
|
|
3320
|
+
options.installCometixLine = true;
|
|
3321
|
+
}
|
|
3322
|
+
if (options.configAction && !["new", "backup", "merge", "docs-only", "skip"].includes(options.configAction)) {
|
|
3323
|
+
throw new Error(
|
|
3324
|
+
`Invalid configAction value: ${options.configAction}. Must be 'new', 'backup', 'merge', 'docs-only', or 'skip'`
|
|
3325
|
+
);
|
|
3326
|
+
}
|
|
3327
|
+
if (options.apiType && !["auth_token", "api_key", "ccr_proxy", "skip"].includes(options.apiType)) {
|
|
3328
|
+
throw new Error(
|
|
3329
|
+
`Invalid apiType value: ${options.apiType}. Must be 'auth_token', 'api_key', 'ccr_proxy', or 'skip'`
|
|
3330
|
+
);
|
|
3331
|
+
}
|
|
3332
|
+
if (options.apiType === "api_key" && !options.apiKey) {
|
|
3333
|
+
throw new Error('API key is required when apiType is "api_key"');
|
|
3334
|
+
}
|
|
3335
|
+
if (options.apiType === "auth_token" && !options.apiKey) {
|
|
3336
|
+
throw new Error('API key is required when apiType is "auth_token"');
|
|
3337
|
+
}
|
|
3338
|
+
if (typeof options.mcpServices === "string") {
|
|
3339
|
+
if (options.mcpServices === "skip") {
|
|
3340
|
+
options.mcpServices = false;
|
|
3341
|
+
} else if (options.mcpServices === "all") {
|
|
3342
|
+
options.mcpServices = MCP_SERVICES.filter((s) => !s.requiresApiKey).map((s) => s.id);
|
|
3343
|
+
} else {
|
|
3344
|
+
options.mcpServices = options.mcpServices.split(",").map((s) => s.trim());
|
|
3233
3345
|
}
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3346
|
+
}
|
|
3347
|
+
if (Array.isArray(options.mcpServices)) {
|
|
3348
|
+
const validServices = MCP_SERVICES.map((s) => s.id);
|
|
3349
|
+
for (const service of options.mcpServices) {
|
|
3350
|
+
if (!validServices.includes(service)) {
|
|
3351
|
+
throw new Error(`Invalid MCP service: ${service}. Available services: ${validServices.join(", ")}`);
|
|
3240
3352
|
}
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
if (typeof options.workflows === "string") {
|
|
3356
|
+
if (options.workflows === "skip") {
|
|
3357
|
+
options.workflows = false;
|
|
3358
|
+
} else if (options.workflows === "all") {
|
|
3359
|
+
options.workflows = WORKFLOW_CONFIGS.map((w) => w.id);
|
|
3241
3360
|
} else {
|
|
3242
|
-
|
|
3361
|
+
options.workflows = options.workflows.split(",").map((s) => s.trim());
|
|
3243
3362
|
}
|
|
3244
|
-
return;
|
|
3245
3363
|
}
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
console.log(ansis.green(`\u2714 ${i18n.cometix.statusLineConfigured || "Claude Code statusLine configured"}`));
|
|
3253
|
-
} catch (configError) {
|
|
3254
|
-
console.warn(ansis.yellow(`\u26A0 ${i18n.cometix.statusLineConfigFailed || "Failed to configure statusLine"}: ${configError}`));
|
|
3255
|
-
console.log(ansis.blue(`\u{1F4A1} ${i18n.cometix.statusLineManualConfig || "Please manually add statusLine configuration to Claude Code settings"}`));
|
|
3364
|
+
if (Array.isArray(options.workflows)) {
|
|
3365
|
+
const validWorkflows = WORKFLOW_CONFIGS.map((w) => w.id);
|
|
3366
|
+
for (const workflow of options.workflows) {
|
|
3367
|
+
if (!validWorkflows.includes(workflow)) {
|
|
3368
|
+
throw new Error(`Invalid workflow: ${workflow}. Available workflows: ${validWorkflows.join(", ")}`);
|
|
3369
|
+
}
|
|
3256
3370
|
}
|
|
3257
|
-
}
|
|
3258
|
-
|
|
3259
|
-
|
|
3371
|
+
}
|
|
3372
|
+
if (options.mcpServices === void 0) {
|
|
3373
|
+
options.mcpServices = "all";
|
|
3374
|
+
options.mcpServices = MCP_SERVICES.filter((s) => !s.requiresApiKey).map((s) => s.id);
|
|
3375
|
+
}
|
|
3376
|
+
if (options.workflows === void 0) {
|
|
3377
|
+
options.workflows = "all";
|
|
3378
|
+
options.workflows = WORKFLOW_CONFIGS.map((w) => w.id);
|
|
3260
3379
|
}
|
|
3261
3380
|
}
|
|
3262
|
-
|
|
3263
3381
|
async function init(options = {}) {
|
|
3382
|
+
if (options.skipPrompt) {
|
|
3383
|
+
validateSkipPromptOptions(options);
|
|
3384
|
+
}
|
|
3264
3385
|
try {
|
|
3265
3386
|
if (!options.skipBanner) {
|
|
3266
3387
|
displayBannerWithInfo();
|
|
3267
3388
|
}
|
|
3268
|
-
const scriptLang = await selectScriptLanguage(options.lang);
|
|
3389
|
+
const scriptLang = options.skipPrompt ? options.lang || "en" : await selectScriptLanguage(options.lang);
|
|
3269
3390
|
const i18n = I18N[scriptLang];
|
|
3270
3391
|
if (isTermux()) {
|
|
3271
3392
|
console.log(ansis.yellow(`
|
|
@@ -3273,40 +3394,48 @@ async function init(options = {}) {
|
|
|
3273
3394
|
console.log(ansis.gray(i18n.installation.termuxEnvironmentInfo));
|
|
3274
3395
|
}
|
|
3275
3396
|
let configLang = options.configLang;
|
|
3276
|
-
if (!configLang) {
|
|
3397
|
+
if (!configLang && !options.skipPrompt) {
|
|
3277
3398
|
const { lang } = await inquirer.prompt({
|
|
3278
3399
|
type: "list",
|
|
3279
3400
|
name: "lang",
|
|
3280
3401
|
message: i18n.language.selectConfigLang,
|
|
3281
|
-
choices: addNumbersToChoices(
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3402
|
+
choices: addNumbersToChoices(
|
|
3403
|
+
SUPPORTED_LANGS.map((l) => ({
|
|
3404
|
+
name: `${LANG_LABELS[l]} - ${i18n.language.configLangHint[l]}`,
|
|
3405
|
+
value: l
|
|
3406
|
+
}))
|
|
3407
|
+
)
|
|
3285
3408
|
});
|
|
3286
3409
|
if (!lang) {
|
|
3287
3410
|
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3288
3411
|
process.exit(0);
|
|
3289
3412
|
}
|
|
3290
3413
|
configLang = lang;
|
|
3414
|
+
} else if (!configLang && options.skipPrompt) {
|
|
3415
|
+
configLang = "en";
|
|
3291
3416
|
}
|
|
3292
3417
|
const zcfConfig = readZcfConfig();
|
|
3293
|
-
const aiOutputLang = await resolveAiOutputLanguage(scriptLang, options.aiOutputLang, zcfConfig);
|
|
3418
|
+
const aiOutputLang = options.skipPrompt ? options.aiOutputLang || "en" : await resolveAiOutputLanguage(scriptLang, options.aiOutputLang, zcfConfig);
|
|
3294
3419
|
const installed = await isClaudeCodeInstalled();
|
|
3295
3420
|
if (!installed) {
|
|
3296
|
-
|
|
3297
|
-
type: "confirm",
|
|
3298
|
-
name: "shouldInstall",
|
|
3299
|
-
message: i18n.installation.installPrompt,
|
|
3300
|
-
default: true
|
|
3301
|
-
});
|
|
3302
|
-
if (shouldInstall === void 0) {
|
|
3303
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3304
|
-
process.exit(0);
|
|
3305
|
-
}
|
|
3306
|
-
if (shouldInstall) {
|
|
3421
|
+
if (options.skipPrompt) {
|
|
3307
3422
|
await installClaudeCode(scriptLang);
|
|
3308
3423
|
} else {
|
|
3309
|
-
|
|
3424
|
+
const { shouldInstall } = await inquirer.prompt({
|
|
3425
|
+
type: "confirm",
|
|
3426
|
+
name: "shouldInstall",
|
|
3427
|
+
message: i18n.installation.installPrompt,
|
|
3428
|
+
default: true
|
|
3429
|
+
});
|
|
3430
|
+
if (shouldInstall === void 0) {
|
|
3431
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3432
|
+
process.exit(0);
|
|
3433
|
+
}
|
|
3434
|
+
if (shouldInstall) {
|
|
3435
|
+
await installClaudeCode(scriptLang);
|
|
3436
|
+
} else {
|
|
3437
|
+
console.log(ansis.yellow(i18n.common.skip));
|
|
3438
|
+
}
|
|
3310
3439
|
}
|
|
3311
3440
|
} else {
|
|
3312
3441
|
console.log(ansis.green(`\u2714 ${i18n.installation.alreadyInstalled}`));
|
|
@@ -3314,77 +3443,57 @@ async function init(options = {}) {
|
|
|
3314
3443
|
ensureClaudeDir();
|
|
3315
3444
|
let action = "new";
|
|
3316
3445
|
if (existsSync(SETTINGS_FILE) && !options.force) {
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
{ name: i18n.common.skip, value: "skip" }
|
|
3326
|
-
])
|
|
3327
|
-
});
|
|
3328
|
-
if (!userAction) {
|
|
3329
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3330
|
-
process.exit(0);
|
|
3331
|
-
}
|
|
3332
|
-
action = userAction;
|
|
3333
|
-
if (action === "skip") {
|
|
3334
|
-
console.log(ansis.yellow(i18n.common.skip));
|
|
3335
|
-
return;
|
|
3336
|
-
}
|
|
3337
|
-
}
|
|
3338
|
-
let apiConfig = null;
|
|
3339
|
-
const isNewInstall = !existsSync(SETTINGS_FILE);
|
|
3340
|
-
if (action !== "docs-only" && (isNewInstall || ["backup", "merge"].includes(action))) {
|
|
3341
|
-
const existingApiConfig = getExistingApiConfig();
|
|
3342
|
-
if (existingApiConfig) {
|
|
3343
|
-
console.log("\n" + ansis.blue(`\u2139 ${i18n.api.existingApiConfig}`));
|
|
3344
|
-
console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${existingApiConfig.url || i18n.common.notConfigured}`));
|
|
3345
|
-
console.log(
|
|
3346
|
-
ansis.gray(
|
|
3347
|
-
` ${i18n.api.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.common.notConfigured}`
|
|
3348
|
-
)
|
|
3349
|
-
);
|
|
3350
|
-
console.log(ansis.gray(` ${i18n.api.apiConfigAuthType}: ${existingApiConfig.authType || i18n.common.notConfigured}
|
|
3351
|
-
`));
|
|
3352
|
-
const { action: apiAction } = await inquirer.prompt({
|
|
3446
|
+
if (options.skipPrompt) {
|
|
3447
|
+
action = options.configAction || "backup";
|
|
3448
|
+
if (action === "skip") {
|
|
3449
|
+
console.log(ansis.yellow(i18n.common.skip));
|
|
3450
|
+
return;
|
|
3451
|
+
}
|
|
3452
|
+
} else {
|
|
3453
|
+
const { action: userAction } = await inquirer.prompt({
|
|
3353
3454
|
type: "list",
|
|
3354
3455
|
name: "action",
|
|
3355
|
-
message: i18n.
|
|
3456
|
+
message: i18n.configuration.existingConfig,
|
|
3356
3457
|
choices: addNumbersToChoices([
|
|
3357
|
-
{ name: i18n.
|
|
3358
|
-
{ name: i18n.
|
|
3359
|
-
{ name: i18n.
|
|
3360
|
-
{ name: i18n.
|
|
3361
|
-
{ name: i18n.api.skipApi, value: "skip" }
|
|
3458
|
+
{ name: i18n.configuration.backupAndOverwrite, value: "backup" },
|
|
3459
|
+
{ name: i18n.configuration.updateDocsOnly, value: "docs-only" },
|
|
3460
|
+
{ name: i18n.configuration.mergeConfig, value: "merge" },
|
|
3461
|
+
{ name: i18n.common.skip, value: "skip" }
|
|
3362
3462
|
])
|
|
3363
3463
|
});
|
|
3364
|
-
if (!
|
|
3464
|
+
if (!userAction) {
|
|
3365
3465
|
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3366
3466
|
process.exit(0);
|
|
3367
3467
|
}
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3468
|
+
action = userAction;
|
|
3469
|
+
if (action === "skip") {
|
|
3470
|
+
console.log(ansis.yellow(i18n.common.skip));
|
|
3471
|
+
return;
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
} else if (options.skipPrompt && options.configAction) {
|
|
3475
|
+
action = options.configAction;
|
|
3476
|
+
}
|
|
3477
|
+
let apiConfig = null;
|
|
3478
|
+
const isNewInstall = !existsSync(SETTINGS_FILE);
|
|
3479
|
+
if (action !== "docs-only" && (isNewInstall || ["backup", "merge"].includes(action))) {
|
|
3480
|
+
if (options.skipPrompt) {
|
|
3481
|
+
if (options.apiType === "auth_token" && options.apiKey) {
|
|
3482
|
+
apiConfig = {
|
|
3483
|
+
authType: "auth_token",
|
|
3484
|
+
key: options.apiKey,
|
|
3485
|
+
url: options.apiUrl || "https://api.anthropic.com"
|
|
3486
|
+
};
|
|
3487
|
+
} else if (options.apiType === "api_key" && options.apiKey) {
|
|
3488
|
+
apiConfig = {
|
|
3489
|
+
authType: "api_key",
|
|
3490
|
+
key: options.apiKey,
|
|
3491
|
+
url: options.apiUrl || "https://api.anthropic.com"
|
|
3492
|
+
};
|
|
3493
|
+
} else if (options.apiType === "ccr_proxy") {
|
|
3494
|
+
const ccrInstalled = await isCcrInstalled();
|
|
3495
|
+
if (!ccrInstalled) {
|
|
3385
3496
|
await installCcr(scriptLang);
|
|
3386
|
-
} else {
|
|
3387
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
3388
3497
|
}
|
|
3389
3498
|
const ccrConfigured = await setupCcrConfiguration(scriptLang);
|
|
3390
3499
|
if (ccrConfigured) {
|
|
@@ -3393,50 +3502,108 @@ async function init(options = {}) {
|
|
|
3393
3502
|
}
|
|
3394
3503
|
}
|
|
3395
3504
|
} else {
|
|
3396
|
-
const
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
{
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
name: i18n.api.
|
|
3418
|
-
value: "skip"
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
process.exit(0);
|
|
3425
|
-
}
|
|
3426
|
-
if (apiChoice === "ccr_proxy") {
|
|
3427
|
-
const ccrStatus = await isCcrInstalled();
|
|
3428
|
-
if (!ccrStatus.hasCorrectPackage) {
|
|
3429
|
-
await installCcr(scriptLang);
|
|
3430
|
-
} else {
|
|
3431
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
3505
|
+
const existingApiConfig = getExistingApiConfig();
|
|
3506
|
+
if (existingApiConfig) {
|
|
3507
|
+
console.log("\n" + ansis.blue(`\u2139 ${i18n.api.existingApiConfig}`));
|
|
3508
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${existingApiConfig.url || i18n.common.notConfigured}`));
|
|
3509
|
+
console.log(
|
|
3510
|
+
ansis.gray(
|
|
3511
|
+
` ${i18n.api.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.common.notConfigured}`
|
|
3512
|
+
)
|
|
3513
|
+
);
|
|
3514
|
+
console.log(
|
|
3515
|
+
ansis.gray(` ${i18n.api.apiConfigAuthType}: ${existingApiConfig.authType || i18n.common.notConfigured}
|
|
3516
|
+
`)
|
|
3517
|
+
);
|
|
3518
|
+
const { action: apiAction } = await inquirer.prompt({
|
|
3519
|
+
type: "list",
|
|
3520
|
+
name: "action",
|
|
3521
|
+
message: i18n.api.selectApiAction,
|
|
3522
|
+
choices: addNumbersToChoices([
|
|
3523
|
+
{ name: i18n.api.keepExistingConfig, value: "keep" },
|
|
3524
|
+
{ name: i18n.api.modifyAllConfig, value: "modify-all" },
|
|
3525
|
+
{ name: i18n.api.modifyPartialConfig, value: "modify-partial" },
|
|
3526
|
+
{ name: i18n.api.useCcrProxy, value: "use-ccr" },
|
|
3527
|
+
{ name: i18n.api.skipApi, value: "skip" }
|
|
3528
|
+
])
|
|
3529
|
+
});
|
|
3530
|
+
if (!apiAction) {
|
|
3531
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3532
|
+
process.exit(0);
|
|
3432
3533
|
}
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3534
|
+
if (apiAction === "keep" || apiAction === "skip") {
|
|
3535
|
+
apiConfig = null;
|
|
3536
|
+
if (apiAction === "keep") {
|
|
3537
|
+
try {
|
|
3538
|
+
addCompletedOnboarding();
|
|
3539
|
+
} catch (error) {
|
|
3540
|
+
console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
} else if (apiAction === "modify-partial") {
|
|
3544
|
+
await modifyApiConfigPartially(existingApiConfig, i18n, scriptLang);
|
|
3436
3545
|
apiConfig = null;
|
|
3546
|
+
} else if (apiAction === "modify-all") {
|
|
3547
|
+
apiConfig = await configureApiCompletely(i18n, scriptLang);
|
|
3548
|
+
} else if (apiAction === "use-ccr") {
|
|
3549
|
+
const ccrStatus = await isCcrInstalled();
|
|
3550
|
+
if (!ccrStatus.hasCorrectPackage) {
|
|
3551
|
+
await installCcr(scriptLang);
|
|
3552
|
+
} else {
|
|
3553
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
3554
|
+
}
|
|
3555
|
+
const ccrConfigured = await setupCcrConfiguration(scriptLang);
|
|
3556
|
+
if (ccrConfigured) {
|
|
3557
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
|
|
3558
|
+
apiConfig = null;
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
} else {
|
|
3562
|
+
const { apiChoice } = await inquirer.prompt({
|
|
3563
|
+
type: "list",
|
|
3564
|
+
name: "apiChoice",
|
|
3565
|
+
message: i18n.api.configureApi,
|
|
3566
|
+
choices: [
|
|
3567
|
+
{
|
|
3568
|
+
name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
|
|
3569
|
+
value: "auth_token",
|
|
3570
|
+
short: i18n.api.useAuthToken
|
|
3571
|
+
},
|
|
3572
|
+
{
|
|
3573
|
+
name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
|
|
3574
|
+
value: "api_key",
|
|
3575
|
+
short: i18n.api.useApiKey
|
|
3576
|
+
},
|
|
3577
|
+
{
|
|
3578
|
+
name: `${i18n.api.useCcrProxy} - ${ansis.gray(i18n.api.ccrProxyDesc)}`,
|
|
3579
|
+
value: "ccr_proxy",
|
|
3580
|
+
short: i18n.api.useCcrProxy
|
|
3581
|
+
},
|
|
3582
|
+
{
|
|
3583
|
+
name: i18n.api.skipApi,
|
|
3584
|
+
value: "skip"
|
|
3585
|
+
}
|
|
3586
|
+
]
|
|
3587
|
+
});
|
|
3588
|
+
if (!apiChoice) {
|
|
3589
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3590
|
+
process.exit(0);
|
|
3591
|
+
}
|
|
3592
|
+
if (apiChoice === "ccr_proxy") {
|
|
3593
|
+
const ccrStatus = await isCcrInstalled();
|
|
3594
|
+
if (!ccrStatus.hasCorrectPackage) {
|
|
3595
|
+
await installCcr(scriptLang);
|
|
3596
|
+
} else {
|
|
3597
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
3598
|
+
}
|
|
3599
|
+
const ccrConfigured = await setupCcrConfiguration(scriptLang);
|
|
3600
|
+
if (ccrConfigured) {
|
|
3601
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
|
|
3602
|
+
apiConfig = null;
|
|
3603
|
+
}
|
|
3604
|
+
} else if (apiChoice !== "skip") {
|
|
3605
|
+
apiConfig = await configureApiCompletely(i18n, scriptLang, apiChoice);
|
|
3437
3606
|
}
|
|
3438
|
-
} else if (apiChoice !== "skip") {
|
|
3439
|
-
apiConfig = await configureApiCompletely(i18n, scriptLang, apiChoice);
|
|
3440
3607
|
}
|
|
3441
3608
|
}
|
|
3442
3609
|
}
|
|
@@ -3448,13 +3615,29 @@ async function init(options = {}) {
|
|
|
3448
3615
|
}
|
|
3449
3616
|
if (action === "docs-only") {
|
|
3450
3617
|
copyConfigFiles(configLang, true);
|
|
3451
|
-
|
|
3618
|
+
if (options.skipPrompt) {
|
|
3619
|
+
if (options.workflows !== false) {
|
|
3620
|
+
await selectAndInstallWorkflows(configLang, scriptLang, options.workflows);
|
|
3621
|
+
}
|
|
3622
|
+
} else {
|
|
3623
|
+
await selectAndInstallWorkflows(configLang, scriptLang);
|
|
3624
|
+
}
|
|
3452
3625
|
} else if (["backup", "merge", "new"].includes(action)) {
|
|
3453
3626
|
copyConfigFiles(configLang, false);
|
|
3454
|
-
|
|
3627
|
+
if (options.skipPrompt) {
|
|
3628
|
+
if (options.workflows !== false) {
|
|
3629
|
+
await selectAndInstallWorkflows(configLang, scriptLang, options.workflows);
|
|
3630
|
+
}
|
|
3631
|
+
} else {
|
|
3632
|
+
await selectAndInstallWorkflows(configLang, scriptLang);
|
|
3633
|
+
}
|
|
3455
3634
|
}
|
|
3456
3635
|
applyAiLanguageDirective(aiOutputLang);
|
|
3457
|
-
|
|
3636
|
+
if (options.skipPrompt) {
|
|
3637
|
+
await configureAiPersonality(scriptLang, options.aiPersonality);
|
|
3638
|
+
} else {
|
|
3639
|
+
await configureAiPersonality(scriptLang);
|
|
3640
|
+
}
|
|
3458
3641
|
if (apiConfig && action !== "docs-only") {
|
|
3459
3642
|
const configuredApi = configureApi(apiConfig);
|
|
3460
3643
|
if (configuredApi) {
|
|
@@ -3464,23 +3647,34 @@ async function init(options = {}) {
|
|
|
3464
3647
|
}
|
|
3465
3648
|
}
|
|
3466
3649
|
if (action !== "docs-only") {
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3650
|
+
let shouldConfigureMcp = false;
|
|
3651
|
+
if (options.skipPrompt) {
|
|
3652
|
+
shouldConfigureMcp = options.mcpServices !== false;
|
|
3653
|
+
} else {
|
|
3654
|
+
const { shouldConfigureMcp: userChoice } = await inquirer.prompt({
|
|
3655
|
+
type: "confirm",
|
|
3656
|
+
name: "shouldConfigureMcp",
|
|
3657
|
+
message: i18n.mcp.configureMcp,
|
|
3658
|
+
default: true
|
|
3659
|
+
});
|
|
3660
|
+
if (userChoice === void 0) {
|
|
3661
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3662
|
+
process.exit(0);
|
|
3663
|
+
}
|
|
3664
|
+
shouldConfigureMcp = userChoice;
|
|
3476
3665
|
}
|
|
3477
3666
|
if (shouldConfigureMcp) {
|
|
3478
3667
|
if (isWindows()) {
|
|
3479
3668
|
console.log(ansis.blue(`\u2139 ${i18n.installation.windowsDetected}`));
|
|
3480
3669
|
}
|
|
3481
|
-
|
|
3482
|
-
if (
|
|
3483
|
-
|
|
3670
|
+
let selectedServices;
|
|
3671
|
+
if (options.skipPrompt) {
|
|
3672
|
+
selectedServices = options.mcpServices;
|
|
3673
|
+
} else {
|
|
3674
|
+
selectedServices = await selectMcpServices(scriptLang);
|
|
3675
|
+
if (selectedServices === void 0) {
|
|
3676
|
+
process.exit(0);
|
|
3677
|
+
}
|
|
3484
3678
|
}
|
|
3485
3679
|
if (selectedServices.length > 0) {
|
|
3486
3680
|
const mcpBackupPath = backupMcpConfig();
|
|
@@ -3493,20 +3687,21 @@ async function init(options = {}) {
|
|
|
3493
3687
|
if (!service) continue;
|
|
3494
3688
|
let config = service.config;
|
|
3495
3689
|
if (service.requiresApiKey) {
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
name: "apiKey",
|
|
3499
|
-
message: service.apiKeyPrompt[scriptLang],
|
|
3500
|
-
validate: (value) => !!value || i18n.api.keyRequired
|
|
3501
|
-
});
|
|
3502
|
-
if (apiKey === void 0) {
|
|
3503
|
-
console.log(ansis.yellow(`${i18n.common.skip}: ${service.name[scriptLang]}`));
|
|
3690
|
+
if (options.skipPrompt) {
|
|
3691
|
+
console.log(ansis.yellow(`${i18n.common.skip}: ${service.name[scriptLang]} (requires API key)`));
|
|
3504
3692
|
continue;
|
|
3505
|
-
}
|
|
3506
|
-
if (apiKey) {
|
|
3507
|
-
config = buildMcpServerConfig(service.config, apiKey, service.apiKeyPlaceholder, service.apiKeyEnvVar);
|
|
3508
3693
|
} else {
|
|
3509
|
-
|
|
3694
|
+
const response = await inquirer.prompt({
|
|
3695
|
+
type: "input",
|
|
3696
|
+
name: "apiKey",
|
|
3697
|
+
message: service.apiKeyPrompt[scriptLang],
|
|
3698
|
+
validate: (value) => !!value || i18n.api.keyRequired
|
|
3699
|
+
});
|
|
3700
|
+
if (!response.apiKey) {
|
|
3701
|
+
console.log(ansis.yellow(`${i18n.common.skip}: ${service.name[scriptLang]}`));
|
|
3702
|
+
continue;
|
|
3703
|
+
}
|
|
3704
|
+
config = buildMcpServerConfig(service.config, response.apiKey, service.apiKeyPlaceholder, service.apiKeyEnvVar);
|
|
3510
3705
|
}
|
|
3511
3706
|
}
|
|
3512
3707
|
newServers[service.id] = config;
|
|
@@ -3525,15 +3720,21 @@ async function init(options = {}) {
|
|
|
3525
3720
|
}
|
|
3526
3721
|
const cometixInstalled = await isCometixLineInstalled();
|
|
3527
3722
|
if (!cometixInstalled) {
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3723
|
+
let shouldInstallCometix = false;
|
|
3724
|
+
if (options.skipPrompt) {
|
|
3725
|
+
shouldInstallCometix = options.installCometixLine !== false;
|
|
3726
|
+
} else {
|
|
3727
|
+
const { shouldInstallCometix: userChoice } = await inquirer.prompt({
|
|
3728
|
+
type: "confirm",
|
|
3729
|
+
name: "shouldInstallCometix",
|
|
3730
|
+
message: i18n.cometix.installCometixPrompt,
|
|
3731
|
+
default: true
|
|
3732
|
+
});
|
|
3733
|
+
if (userChoice === void 0) {
|
|
3734
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3735
|
+
process.exit(0);
|
|
3736
|
+
}
|
|
3737
|
+
shouldInstallCometix = userChoice;
|
|
3537
3738
|
}
|
|
3538
3739
|
if (shouldInstallCometix) {
|
|
3539
3740
|
await installCometixLine(scriptLang);
|