zcf 3.3.2 → 3.4.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 +24 -870
- package/dist/chunks/claude-code-config-manager.mjs +8 -7
- package/dist/chunks/claude-code-incremental-manager.mjs +19 -33
- package/dist/chunks/codex-config-switch.mjs +16 -23
- package/dist/chunks/codex-provider-manager.mjs +21 -24
- package/dist/chunks/codex-uninstaller.mjs +12 -6
- package/dist/chunks/commands.mjs +2 -1
- package/dist/chunks/features.mjs +21 -30
- package/dist/chunks/simple-config.mjs +572 -165
- package/dist/cli.mjs +46 -34
- package/dist/i18n/locales/en/codex.json +14 -3
- package/dist/i18n/locales/en/common.json +3 -1
- package/dist/i18n/locales/en/installation.json +16 -1
- package/dist/i18n/locales/en/uninstall.json +2 -2
- package/dist/i18n/locales/zh-CN/codex.json +14 -3
- package/dist/i18n/locales/zh-CN/common.json +3 -1
- package/dist/i18n/locales/zh-CN/installation.json +16 -1
- package/dist/i18n/locales/zh-CN/uninstall.json +2 -2
- package/dist/index.d.mts +52 -7
- package/dist/index.d.ts +52 -7
- package/dist/index.mjs +2 -1
- package/package.json +2 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as nodeFs from 'node:fs';
|
|
2
|
+
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, rmSync, rmdirSync, readdirSync, statSync, unlinkSync, renameSync } from 'node:fs';
|
|
2
3
|
import process from 'node:process';
|
|
3
4
|
import ansis from 'ansis';
|
|
4
5
|
import inquirer from 'inquirer';
|
|
@@ -8,6 +9,7 @@ import { promisify } from 'node:util';
|
|
|
8
9
|
import dayjs from 'dayjs';
|
|
9
10
|
import { dirname, join } from 'pathe';
|
|
10
11
|
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import toggleModule from 'inquirer-toggle';
|
|
11
13
|
import ora from 'ora';
|
|
12
14
|
import semver from 'semver';
|
|
13
15
|
import { stringify, parse } from 'smol-toml';
|
|
@@ -16,7 +18,7 @@ import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
|
|
|
16
18
|
import i18next from 'i18next';
|
|
17
19
|
import Backend from 'i18next-fs-backend';
|
|
18
20
|
|
|
19
|
-
const version = "3.
|
|
21
|
+
const version = "3.4.0";
|
|
20
22
|
const homepage = "https://github.com/UfoMiao/zcf";
|
|
21
23
|
|
|
22
24
|
const i18n = i18next.createInstance();
|
|
@@ -523,7 +525,7 @@ function getPlatform() {
|
|
|
523
525
|
return "linux";
|
|
524
526
|
}
|
|
525
527
|
function isTermux() {
|
|
526
|
-
return !!(process.env.PREFIX && process.env.PREFIX.includes("com.termux")) || !!process.env.TERMUX_VERSION || existsSync("/data/data/com.termux/files/usr");
|
|
528
|
+
return !!(process.env.PREFIX && process.env.PREFIX.includes("com.termux")) || !!process.env.TERMUX_VERSION || nodeFs.existsSync("/data/data/com.termux/files/usr");
|
|
527
529
|
}
|
|
528
530
|
function getTermuxPrefix() {
|
|
529
531
|
return process.env.PREFIX || "/data/data/com.termux/files/usr";
|
|
@@ -535,16 +537,16 @@ function isWSL() {
|
|
|
535
537
|
if (process.env.WSL_DISTRO_NAME) {
|
|
536
538
|
return true;
|
|
537
539
|
}
|
|
538
|
-
if (existsSync("/proc/version")) {
|
|
540
|
+
if (nodeFs.existsSync("/proc/version")) {
|
|
539
541
|
try {
|
|
540
|
-
const version = readFileSync("/proc/version", "utf8");
|
|
542
|
+
const version = nodeFs.readFileSync("/proc/version", "utf8");
|
|
541
543
|
if (version.includes("Microsoft") || version.includes("WSL")) {
|
|
542
544
|
return true;
|
|
543
545
|
}
|
|
544
546
|
} catch {
|
|
545
547
|
}
|
|
546
548
|
}
|
|
547
|
-
if (existsSync("/mnt/c")) {
|
|
549
|
+
if (nodeFs.existsSync("/mnt/c")) {
|
|
548
550
|
return true;
|
|
549
551
|
}
|
|
550
552
|
return false;
|
|
@@ -553,9 +555,9 @@ function getWSLDistro() {
|
|
|
553
555
|
if (process.env.WSL_DISTRO_NAME) {
|
|
554
556
|
return process.env.WSL_DISTRO_NAME;
|
|
555
557
|
}
|
|
556
|
-
if (existsSync("/etc/os-release")) {
|
|
558
|
+
if (nodeFs.existsSync("/etc/os-release")) {
|
|
557
559
|
try {
|
|
558
|
-
const osRelease = readFileSync("/etc/os-release", "utf8");
|
|
560
|
+
const osRelease = nodeFs.readFileSync("/etc/os-release", "utf8");
|
|
559
561
|
const nameMatch = osRelease.match(/^PRETTY_NAME="(.+)"$/m);
|
|
560
562
|
if (nameMatch) {
|
|
561
563
|
return nameMatch[1];
|
|
@@ -570,9 +572,9 @@ function getWSLInfo() {
|
|
|
570
572
|
return null;
|
|
571
573
|
}
|
|
572
574
|
let version = null;
|
|
573
|
-
if (existsSync("/proc/version")) {
|
|
575
|
+
if (nodeFs.existsSync("/proc/version")) {
|
|
574
576
|
try {
|
|
575
|
-
version = readFileSync("/proc/version", "utf8").trim();
|
|
577
|
+
version = nodeFs.readFileSync("/proc/version", "utf8").trim();
|
|
576
578
|
} catch {
|
|
577
579
|
}
|
|
578
580
|
}
|
|
@@ -588,6 +590,9 @@ function getMcpCommand(command = "npx") {
|
|
|
588
590
|
}
|
|
589
591
|
return [command];
|
|
590
592
|
}
|
|
593
|
+
function normalizeTomlPath(str) {
|
|
594
|
+
return str.replace(/\\+/g, "/").replace(/\/+/g, "/");
|
|
595
|
+
}
|
|
591
596
|
function getSystemRoot() {
|
|
592
597
|
if (!isWindows())
|
|
593
598
|
return null;
|
|
@@ -597,7 +602,72 @@ function getSystemRoot() {
|
|
|
597
602
|
systemRoot = env.SYSTEMROOT;
|
|
598
603
|
else if (Object.prototype.hasOwnProperty.call(env, "SystemRoot") && env.SystemRoot)
|
|
599
604
|
systemRoot = env.SystemRoot;
|
|
600
|
-
return systemRoot
|
|
605
|
+
return normalizeTomlPath(systemRoot);
|
|
606
|
+
}
|
|
607
|
+
function shouldUseSudoForGlobalInstall() {
|
|
608
|
+
if (isTermux())
|
|
609
|
+
return false;
|
|
610
|
+
if (getPlatform() !== "linux")
|
|
611
|
+
return false;
|
|
612
|
+
const npmPrefix = getGlobalNpmPrefix();
|
|
613
|
+
if (npmPrefix) {
|
|
614
|
+
if (isPathInsideHome(npmPrefix) || canWriteToPath(npmPrefix))
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
const getuid = process.getuid;
|
|
618
|
+
if (typeof getuid !== "function")
|
|
619
|
+
return false;
|
|
620
|
+
try {
|
|
621
|
+
return getuid() !== 0;
|
|
622
|
+
} catch {
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function wrapCommandWithSudo(command, args) {
|
|
627
|
+
if (shouldUseSudoForGlobalInstall()) {
|
|
628
|
+
return {
|
|
629
|
+
command: "sudo",
|
|
630
|
+
args: [command, ...args],
|
|
631
|
+
usedSudo: true
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
return {
|
|
635
|
+
command,
|
|
636
|
+
args,
|
|
637
|
+
usedSudo: false
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
const WRITE_CHECK_FLAG = 2;
|
|
641
|
+
function normalizePath(path) {
|
|
642
|
+
return normalizeTomlPath(path).replace(/\/+$/, "");
|
|
643
|
+
}
|
|
644
|
+
function isPathInsideHome(path) {
|
|
645
|
+
const home = process.env.HOME;
|
|
646
|
+
if (!home)
|
|
647
|
+
return false;
|
|
648
|
+
const normalizedHome = normalizePath(home);
|
|
649
|
+
const normalizedPath = normalizePath(path);
|
|
650
|
+
return normalizedPath === normalizedHome || normalizedPath.startsWith(`${normalizedHome}/`);
|
|
651
|
+
}
|
|
652
|
+
function canWriteToPath(path) {
|
|
653
|
+
try {
|
|
654
|
+
nodeFs.accessSync(path, WRITE_CHECK_FLAG);
|
|
655
|
+
return true;
|
|
656
|
+
} catch {
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
function getGlobalNpmPrefix() {
|
|
661
|
+
const env = process.env;
|
|
662
|
+
const envPrefix = env.npm_config_prefix || env.NPM_CONFIG_PREFIX || env.PREFIX;
|
|
663
|
+
if (envPrefix)
|
|
664
|
+
return envPrefix;
|
|
665
|
+
const execPath = process.execPath;
|
|
666
|
+
if (execPath) {
|
|
667
|
+
const binDir = dirname(execPath);
|
|
668
|
+
return dirname(binDir);
|
|
669
|
+
}
|
|
670
|
+
return null;
|
|
601
671
|
}
|
|
602
672
|
async function commandExists(command) {
|
|
603
673
|
try {
|
|
@@ -616,7 +686,7 @@ async function commandExists(command) {
|
|
|
616
686
|
`/data/data/com.termux/files/usr/bin/${command}`
|
|
617
687
|
];
|
|
618
688
|
for (const path of possiblePaths) {
|
|
619
|
-
if (existsSync(path)) {
|
|
689
|
+
if (nodeFs.existsSync(path)) {
|
|
620
690
|
return true;
|
|
621
691
|
}
|
|
622
692
|
}
|
|
@@ -629,13 +699,37 @@ async function commandExists(command) {
|
|
|
629
699
|
`${process.env.HOME}/.local/bin/${command}`
|
|
630
700
|
];
|
|
631
701
|
for (const path of commonPaths) {
|
|
632
|
-
if (existsSync(path)) {
|
|
702
|
+
if (nodeFs.existsSync(path)) {
|
|
633
703
|
return true;
|
|
634
704
|
}
|
|
635
705
|
}
|
|
636
706
|
}
|
|
637
707
|
return false;
|
|
638
708
|
}
|
|
709
|
+
function getRecommendedInstallMethods(codeType) {
|
|
710
|
+
const platform2 = getPlatform();
|
|
711
|
+
const wsl = isWSL();
|
|
712
|
+
if (codeType === "claude-code") {
|
|
713
|
+
if (platform2 === "macos") {
|
|
714
|
+
return ["homebrew", "curl", "npm"];
|
|
715
|
+
}
|
|
716
|
+
if (platform2 === "linux" || wsl) {
|
|
717
|
+
return ["curl", "npm"];
|
|
718
|
+
}
|
|
719
|
+
if (platform2 === "windows") {
|
|
720
|
+
return ["powershell", "npm"];
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
if (codeType === "codex") {
|
|
724
|
+
if (platform2 === "macos") {
|
|
725
|
+
return ["homebrew", "npm"];
|
|
726
|
+
}
|
|
727
|
+
if (platform2 === "linux" || wsl || platform2 === "windows") {
|
|
728
|
+
return ["npm"];
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return ["npm"];
|
|
732
|
+
}
|
|
639
733
|
|
|
640
734
|
class FileSystemError extends Error {
|
|
641
735
|
constructor(message, path, cause) {
|
|
@@ -1419,6 +1513,16 @@ const config$1 = {
|
|
|
1419
1513
|
updateDefaultModel: updateDefaultModel
|
|
1420
1514
|
};
|
|
1421
1515
|
|
|
1516
|
+
const togglePrompt = toggleModule?.default?.default || toggleModule?.default || toggleModule;
|
|
1517
|
+
async function promptBoolean(options) {
|
|
1518
|
+
const { message, defaultValue = false, theme } = options;
|
|
1519
|
+
return await togglePrompt({
|
|
1520
|
+
message,
|
|
1521
|
+
default: defaultValue,
|
|
1522
|
+
...theme ? { theme } : {}
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1422
1526
|
const PROVIDER_PRESETS_URL = "https://pub-0dc3e1677e894f07bbea11b17a29e032.r2.dev/providers.json";
|
|
1423
1527
|
async function fetchProviderPresets() {
|
|
1424
1528
|
try {
|
|
@@ -1782,13 +1886,10 @@ async function setupCcrConfiguration() {
|
|
|
1782
1886
|
console.log(ansis.blue(`\u2139 ${i18n.t("ccr:existingCcrConfig")}`));
|
|
1783
1887
|
let shouldBackupAndReconfigure = false;
|
|
1784
1888
|
try {
|
|
1785
|
-
|
|
1786
|
-
type: "confirm",
|
|
1787
|
-
name: "overwrite",
|
|
1889
|
+
shouldBackupAndReconfigure = await promptBoolean({
|
|
1788
1890
|
message: i18n.t("ccr:overwriteCcrConfig"),
|
|
1789
|
-
|
|
1891
|
+
defaultValue: false
|
|
1790
1892
|
});
|
|
1791
|
-
shouldBackupAndReconfigure = result.overwrite;
|
|
1792
1893
|
} catch (error) {
|
|
1793
1894
|
if (error.name === "ExitPromptError") {
|
|
1794
1895
|
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
@@ -1988,11 +2089,9 @@ async function updateCcr(force = false, skipPrompt = false) {
|
|
|
1988
2089
|
console.log(ansis.cyan(format(i18n.t("updater:currentVersion"), { version: currentVersion || "" })));
|
|
1989
2090
|
console.log(ansis.cyan(format(i18n.t("updater:latestVersion"), { version: latestVersion })));
|
|
1990
2091
|
if (!skipPrompt) {
|
|
1991
|
-
const
|
|
1992
|
-
type: "confirm",
|
|
1993
|
-
name: "confirm",
|
|
2092
|
+
const confirm = await promptBoolean({
|
|
1994
2093
|
message: format(i18n.t("updater:confirmUpdate"), { tool: "CCR" }),
|
|
1995
|
-
|
|
2094
|
+
defaultValue: true
|
|
1996
2095
|
});
|
|
1997
2096
|
if (!confirm) {
|
|
1998
2097
|
console.log(ansis.gray(i18n.t("updater:updateSkipped")));
|
|
@@ -2038,11 +2137,9 @@ async function updateClaudeCode(force = false, skipPrompt = false) {
|
|
|
2038
2137
|
console.log(ansis.cyan(format(i18n.t("updater:currentVersion"), { version: currentVersion || "" })));
|
|
2039
2138
|
console.log(ansis.cyan(format(i18n.t("updater:latestVersion"), { version: latestVersion })));
|
|
2040
2139
|
if (!skipPrompt) {
|
|
2041
|
-
const
|
|
2042
|
-
type: "confirm",
|
|
2043
|
-
name: "confirm",
|
|
2140
|
+
const confirm = await promptBoolean({
|
|
2044
2141
|
message: format(i18n.t("updater:confirmUpdate"), { tool: "Claude Code" }),
|
|
2045
|
-
|
|
2142
|
+
defaultValue: true
|
|
2046
2143
|
});
|
|
2047
2144
|
if (!confirm) {
|
|
2048
2145
|
console.log(ansis.gray(i18n.t("updater:updateSkipped")));
|
|
@@ -2088,11 +2185,9 @@ async function updateCometixLine(force = false, skipPrompt = false) {
|
|
|
2088
2185
|
console.log(ansis.cyan(format(i18n.t("updater:currentVersion"), { version: currentVersion || "" })));
|
|
2089
2186
|
console.log(ansis.cyan(format(i18n.t("updater:latestVersion"), { version: latestVersion })));
|
|
2090
2187
|
if (!skipPrompt) {
|
|
2091
|
-
const
|
|
2092
|
-
type: "confirm",
|
|
2093
|
-
name: "confirm",
|
|
2188
|
+
const confirm = await promptBoolean({
|
|
2094
2189
|
message: format(i18n.t("updater:confirmUpdate"), { tool: "CCometixLine" }),
|
|
2095
|
-
|
|
2190
|
+
defaultValue: true
|
|
2096
2191
|
});
|
|
2097
2192
|
if (!confirm) {
|
|
2098
2193
|
console.log(ansis.gray(i18n.t("updater:updateSkipped")));
|
|
@@ -2219,7 +2314,12 @@ async function installCcr() {
|
|
|
2219
2314
|
}
|
|
2220
2315
|
console.log(ansis.cyan(`\u{1F4E6} ${i18n.t("ccr:installingCcr")}`));
|
|
2221
2316
|
try {
|
|
2222
|
-
|
|
2317
|
+
const installArgs = ["install", "-g", "@musistudio/claude-code-router", "--force"];
|
|
2318
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", installArgs);
|
|
2319
|
+
if (usedSudo) {
|
|
2320
|
+
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:usingSudo")}`));
|
|
2321
|
+
}
|
|
2322
|
+
await execAsync$1([command, ...args].join(" "));
|
|
2223
2323
|
console.log(ansis.green(`\u2714 ${i18n.t("ccr:ccrInstallSuccess")}`));
|
|
2224
2324
|
} catch (error) {
|
|
2225
2325
|
if (error.message?.includes("EEXIST")) {
|
|
@@ -2586,16 +2686,10 @@ async function resolveAiOutputLanguage(scriptLang, commandLineOption, savedConfi
|
|
|
2586
2686
|
}
|
|
2587
2687
|
const currentLanguageLabel = getAiOutputLanguageLabel(savedConfig.aiOutputLang) || savedConfig.aiOutputLang;
|
|
2588
2688
|
console.log(ansis.blue(`${i18n.t("language:currentConfigFound")}: ${currentLanguageLabel}`));
|
|
2589
|
-
const
|
|
2590
|
-
type: "confirm",
|
|
2591
|
-
name: "shouldModify",
|
|
2689
|
+
const shouldModify = await promptBoolean({
|
|
2592
2690
|
message: i18n.t("language:modifyConfigPrompt"),
|
|
2593
|
-
|
|
2691
|
+
defaultValue: false
|
|
2594
2692
|
});
|
|
2595
|
-
if (shouldModify === void 0) {
|
|
2596
|
-
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
2597
|
-
process.exit(0);
|
|
2598
|
-
}
|
|
2599
2693
|
if (!shouldModify) {
|
|
2600
2694
|
console.log(ansis.gray(`\u2714 ${i18n.t("language:aiOutputLangHint")}: ${currentLanguageLabel}`));
|
|
2601
2695
|
return savedConfig.aiOutputLang;
|
|
@@ -2641,16 +2735,10 @@ async function resolveTemplateLanguage(commandLineOption, savedConfig, skipPromp
|
|
|
2641
2735
|
}
|
|
2642
2736
|
const currentLanguageLabel = LANG_LABELS[savedConfig.templateLang];
|
|
2643
2737
|
console.log(ansis.blue(`${i18n.t("language:currentTemplateLanguageFound")}: ${currentLanguageLabel}`));
|
|
2644
|
-
const
|
|
2645
|
-
type: "confirm",
|
|
2646
|
-
name: "shouldModify",
|
|
2738
|
+
const shouldModify = await promptBoolean({
|
|
2647
2739
|
message: i18n.t("language:modifyTemplateLanguagePrompt"),
|
|
2648
|
-
|
|
2740
|
+
defaultValue: false
|
|
2649
2741
|
});
|
|
2650
|
-
if (shouldModify === void 0) {
|
|
2651
|
-
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
2652
|
-
process.exit(0);
|
|
2653
|
-
}
|
|
2654
2742
|
if (!shouldModify) {
|
|
2655
2743
|
console.log(ansis.gray(`\u2714 ${i18n.t("language:selectConfigLang")}: ${currentLanguageLabel}`));
|
|
2656
2744
|
return savedConfig.templateLang;
|
|
@@ -2662,16 +2750,10 @@ async function resolveTemplateLanguage(commandLineOption, savedConfig, skipPromp
|
|
|
2662
2750
|
return savedConfig.preferredLang;
|
|
2663
2751
|
}
|
|
2664
2752
|
console.log(ansis.yellow(`${i18n.t("language:usingFallbackTemplate")}: ${LANG_LABELS[savedConfig.preferredLang]}`));
|
|
2665
|
-
const
|
|
2666
|
-
type: "confirm",
|
|
2667
|
-
name: "shouldModify",
|
|
2753
|
+
const shouldModify = await promptBoolean({
|
|
2668
2754
|
message: i18n.t("language:modifyTemplateLanguagePrompt"),
|
|
2669
|
-
|
|
2755
|
+
defaultValue: false
|
|
2670
2756
|
});
|
|
2671
|
-
if (shouldModify === void 0) {
|
|
2672
|
-
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
2673
|
-
process.exit(0);
|
|
2674
|
-
}
|
|
2675
2757
|
if (!shouldModify) {
|
|
2676
2758
|
return savedConfig.preferredLang;
|
|
2677
2759
|
}
|
|
@@ -2695,16 +2777,10 @@ async function resolveSystemPromptStyle(availablePrompts, commandLineOption, sav
|
|
|
2695
2777
|
return currentStyleId;
|
|
2696
2778
|
}
|
|
2697
2779
|
console.log(ansis.blue(`${i18n.t("language:currentSystemPromptFound")}: ${currentStyle.name}`));
|
|
2698
|
-
const
|
|
2699
|
-
type: "confirm",
|
|
2700
|
-
name: "shouldModify",
|
|
2780
|
+
const shouldModify = await promptBoolean({
|
|
2701
2781
|
message: i18n.t("language:modifySystemPromptPrompt"),
|
|
2702
|
-
|
|
2782
|
+
defaultValue: false
|
|
2703
2783
|
});
|
|
2704
|
-
if (shouldModify === void 0) {
|
|
2705
|
-
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
2706
|
-
process.exit(0);
|
|
2707
|
-
}
|
|
2708
2784
|
if (!shouldModify) {
|
|
2709
2785
|
console.log(ansis.gray(`\u2714 ${i18n.t("language:currentSystemPromptFound")}: ${currentStyle.name}`));
|
|
2710
2786
|
return currentStyleId;
|
|
@@ -2931,21 +3007,20 @@ function getRootDir$1() {
|
|
|
2931
3007
|
}
|
|
2932
3008
|
return dirname(currentFilePath);
|
|
2933
3009
|
}
|
|
2934
|
-
async function executeCodexInstallation(isUpdate) {
|
|
2935
|
-
const action = isUpdate ? "update" : "install";
|
|
3010
|
+
async function executeCodexInstallation(isUpdate, skipMethodSelection = false) {
|
|
2936
3011
|
if (isUpdate) {
|
|
2937
3012
|
console.log(ansis.cyan(i18n.t("codex:updatingCli")));
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
if (isUpdate) {
|
|
3013
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@openai/codex@latest"]);
|
|
3014
|
+
if (usedSudo)
|
|
3015
|
+
console.log(ansis.yellow(i18n.t("codex:usingSudo")));
|
|
3016
|
+
const result = await x(command, args);
|
|
3017
|
+
if (result.exitCode !== 0) {
|
|
3018
|
+
throw new Error(`Failed to update codex CLI: exit code ${result.exitCode}`);
|
|
3019
|
+
}
|
|
2946
3020
|
console.log(ansis.green(i18n.t("codex:updateSuccess")));
|
|
2947
3021
|
} else {
|
|
2948
|
-
|
|
3022
|
+
const { installCodex } = await Promise.resolve().then(function () { return installer; });
|
|
3023
|
+
await installCodex(skipMethodSelection);
|
|
2949
3024
|
}
|
|
2950
3025
|
}
|
|
2951
3026
|
function getUninstallOptions() {
|
|
@@ -3243,11 +3318,12 @@ function renderCodexConfig(data) {
|
|
|
3243
3318
|
lines.push("# --- MCP servers added by ZCF ---");
|
|
3244
3319
|
for (const service of data.mcpServices) {
|
|
3245
3320
|
lines.push(`[mcp_servers.${service.id}]`);
|
|
3246
|
-
|
|
3321
|
+
const normalizedCommand = normalizeTomlPath(service.command);
|
|
3322
|
+
lines.push(`command = "${normalizedCommand}"`);
|
|
3247
3323
|
const argsString = service.args.length > 0 ? service.args.map((arg) => `"${arg}"`).join(", ") : "";
|
|
3248
3324
|
lines.push(`args = [${argsString}]`);
|
|
3249
3325
|
if (service.env && Object.keys(service.env).length > 0) {
|
|
3250
|
-
const envEntries = Object.entries(service.env).map(([key, value]) => `${key} =
|
|
3326
|
+
const envEntries = Object.entries(service.env).map(([key, value]) => `${key} = '${value}'`).join(", ");
|
|
3251
3327
|
lines.push(`env = {${envEntries}}`);
|
|
3252
3328
|
}
|
|
3253
3329
|
if (service.startup_timeout_ms) {
|
|
@@ -3275,7 +3351,7 @@ function writeAuthFile(newEntries) {
|
|
|
3275
3351
|
const merged = { ...existing, ...newEntries };
|
|
3276
3352
|
writeJsonConfig(CODEX_AUTH_FILE, merged, { pretty: true });
|
|
3277
3353
|
}
|
|
3278
|
-
async function isCodexInstalled() {
|
|
3354
|
+
async function isCodexInstalled$1() {
|
|
3279
3355
|
try {
|
|
3280
3356
|
const result = await x("npm", ["list", "-g", "--depth=0"]);
|
|
3281
3357
|
if (result.exitCode !== 0) {
|
|
@@ -3344,19 +3420,19 @@ async function checkCodexUpdate() {
|
|
|
3344
3420
|
};
|
|
3345
3421
|
}
|
|
3346
3422
|
}
|
|
3347
|
-
async function installCodexCli() {
|
|
3423
|
+
async function installCodexCli(skipMethodSelection = false) {
|
|
3348
3424
|
ensureI18nInitialized();
|
|
3349
|
-
if (await isCodexInstalled()) {
|
|
3425
|
+
if (await isCodexInstalled$1()) {
|
|
3350
3426
|
const { needsUpdate } = await checkCodexUpdate();
|
|
3351
3427
|
if (needsUpdate) {
|
|
3352
|
-
await executeCodexInstallation(true);
|
|
3428
|
+
await executeCodexInstallation(true, skipMethodSelection);
|
|
3353
3429
|
return;
|
|
3354
3430
|
} else {
|
|
3355
3431
|
console.log(ansis.yellow(i18n.t("codex:alreadyInstalled")));
|
|
3356
3432
|
return;
|
|
3357
3433
|
}
|
|
3358
3434
|
}
|
|
3359
|
-
await executeCodexInstallation(false);
|
|
3435
|
+
await executeCodexInstallation(false, skipMethodSelection);
|
|
3360
3436
|
}
|
|
3361
3437
|
async function runCodexWorkflowImportWithLanguageSelection(options) {
|
|
3362
3438
|
ensureI18nInitialized();
|
|
@@ -3800,15 +3876,13 @@ async function configureCodexApi(options) {
|
|
|
3800
3876
|
if (existingProvider || sessionProvider) {
|
|
3801
3877
|
const sourceType = existingProvider ? "existing" : "session";
|
|
3802
3878
|
const sourceProvider = existingProvider || sessionProvider;
|
|
3803
|
-
const
|
|
3804
|
-
type: "confirm",
|
|
3805
|
-
name: "shouldOverwrite",
|
|
3879
|
+
const shouldOverwrite = await promptBoolean({
|
|
3806
3880
|
message: i18n.t("codex:providerDuplicatePrompt", {
|
|
3807
3881
|
name: sourceProvider.name,
|
|
3808
3882
|
source: sourceType === "existing" ? i18n.t("codex:existingConfig") : i18n.t("codex:currentSession")
|
|
3809
3883
|
}),
|
|
3810
|
-
|
|
3811
|
-
}
|
|
3884
|
+
defaultValue: false
|
|
3885
|
+
});
|
|
3812
3886
|
if (!shouldOverwrite) {
|
|
3813
3887
|
console.log(ansis.yellow(i18n.t("codex:providerDuplicateSkipped")));
|
|
3814
3888
|
continue;
|
|
@@ -3834,12 +3908,10 @@ async function configureCodexApi(options) {
|
|
|
3834
3908
|
providers.push(newProvider);
|
|
3835
3909
|
currentSessionProviders.set(providerId, newProvider);
|
|
3836
3910
|
authEntries[envKey] = answers.apiKey;
|
|
3837
|
-
const
|
|
3838
|
-
type: "confirm",
|
|
3839
|
-
name: "addAnother",
|
|
3911
|
+
const addAnother = await promptBoolean({
|
|
3840
3912
|
message: i18n.t("codex:addProviderPrompt"),
|
|
3841
|
-
|
|
3842
|
-
}
|
|
3913
|
+
defaultValue: false
|
|
3914
|
+
});
|
|
3843
3915
|
addMore = addAnother;
|
|
3844
3916
|
}
|
|
3845
3917
|
if (providers.length === 0) {
|
|
@@ -3873,7 +3945,7 @@ async function configureCodexApi(options) {
|
|
|
3873
3945
|
}
|
|
3874
3946
|
async function runCodexFullInit(options) {
|
|
3875
3947
|
ensureI18nInitialized();
|
|
3876
|
-
await installCodexCli();
|
|
3948
|
+
await installCodexCli(options?.skipPrompt || false);
|
|
3877
3949
|
const aiOutputLang = await runCodexWorkflowImportWithLanguageSelection(options);
|
|
3878
3950
|
await configureCodexApi(options);
|
|
3879
3951
|
await configureCodexMcp(options);
|
|
@@ -3938,11 +4010,9 @@ async function runCodexUpdate(force = false, skipPrompt = false) {
|
|
|
3938
4010
|
console.log(ansis.cyan(format(i18n.t("codex:currentVersion"), { version: currentVersion || "" })));
|
|
3939
4011
|
console.log(ansis.cyan(format(i18n.t("codex:latestVersion"), { version: latestVersion })));
|
|
3940
4012
|
if (!skipPrompt) {
|
|
3941
|
-
const
|
|
3942
|
-
type: "confirm",
|
|
3943
|
-
name: "confirm",
|
|
4013
|
+
const confirm = await promptBoolean({
|
|
3944
4014
|
message: i18n.t("codex:confirmUpdate"),
|
|
3945
|
-
|
|
4015
|
+
defaultValue: true
|
|
3946
4016
|
});
|
|
3947
4017
|
if (!confirm) {
|
|
3948
4018
|
console.log(ansis.gray(i18n.t("codex:updateSkipped")));
|
|
@@ -3970,7 +4040,10 @@ async function runCodexUpdate(force = false, skipPrompt = false) {
|
|
|
3970
4040
|
async function runCodexUninstall() {
|
|
3971
4041
|
ensureI18nInitialized();
|
|
3972
4042
|
const { CodexUninstaller } = await import('./codex-uninstaller.mjs');
|
|
3973
|
-
const
|
|
4043
|
+
const zcfConfig = readZcfConfig();
|
|
4044
|
+
const preferredLang = zcfConfig?.preferredLang;
|
|
4045
|
+
const uninstallLang = preferredLang && SUPPORTED_LANGS.includes(preferredLang) ? preferredLang : "en";
|
|
4046
|
+
const uninstaller = new CodexUninstaller(uninstallLang);
|
|
3974
4047
|
const { mode } = await inquirer.prompt([{
|
|
3975
4048
|
type: "list",
|
|
3976
4049
|
name: "mode",
|
|
@@ -3987,12 +4060,10 @@ async function runCodexUninstall() {
|
|
|
3987
4060
|
}
|
|
3988
4061
|
try {
|
|
3989
4062
|
if (mode === "complete") {
|
|
3990
|
-
const
|
|
3991
|
-
type: "confirm",
|
|
3992
|
-
name: "confirm",
|
|
4063
|
+
const confirm = await promptBoolean({
|
|
3993
4064
|
message: i18n.t("codex:uninstallPrompt"),
|
|
3994
|
-
|
|
3995
|
-
}
|
|
4065
|
+
defaultValue: false
|
|
4066
|
+
});
|
|
3996
4067
|
if (!confirm) {
|
|
3997
4068
|
handleUninstallCancellation();
|
|
3998
4069
|
return;
|
|
@@ -4169,7 +4240,7 @@ const codex = {
|
|
|
4169
4240
|
getBackupMessage: getBackupMessage,
|
|
4170
4241
|
getCodexVersion: getCodexVersion,
|
|
4171
4242
|
installCodexCli: installCodexCli,
|
|
4172
|
-
isCodexInstalled: isCodexInstalled,
|
|
4243
|
+
isCodexInstalled: isCodexInstalled$1,
|
|
4173
4244
|
listCodexProviders: listCodexProviders,
|
|
4174
4245
|
parseCodexConfig: parseCodexConfig,
|
|
4175
4246
|
readCodexConfig: readCodexConfig,
|
|
@@ -4284,12 +4355,20 @@ async function isCometixLineInstalled() {
|
|
|
4284
4355
|
}
|
|
4285
4356
|
async function installCometixLine() {
|
|
4286
4357
|
ensureI18nInitialized();
|
|
4358
|
+
const runInstallCommand = async () => {
|
|
4359
|
+
const installArgs = ["install", "-g", COMETIX_PACKAGE_NAME];
|
|
4360
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", installArgs);
|
|
4361
|
+
if (usedSudo) {
|
|
4362
|
+
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:usingSudo")}`));
|
|
4363
|
+
}
|
|
4364
|
+
await execAsync([command, ...args].join(" "));
|
|
4365
|
+
};
|
|
4287
4366
|
const isInstalled = await isCometixLineInstalled();
|
|
4288
4367
|
if (isInstalled) {
|
|
4289
4368
|
console.log(ansis.green(`\u2714 ${i18n.t("cometix:cometixAlreadyInstalled")}`));
|
|
4290
4369
|
try {
|
|
4291
4370
|
console.log(ansis.blue(`${i18n.t("cometix:installingOrUpdating")}`));
|
|
4292
|
-
await
|
|
4371
|
+
await runInstallCommand();
|
|
4293
4372
|
console.log(ansis.green(`\u2714 ${i18n.t("cometix:installUpdateSuccess")}`));
|
|
4294
4373
|
} catch (error) {
|
|
4295
4374
|
console.log(ansis.yellow(`\u26A0 ${i18n.t("cometix:installUpdateFailed")}: ${error}`));
|
|
@@ -4308,7 +4387,7 @@ async function installCometixLine() {
|
|
|
4308
4387
|
}
|
|
4309
4388
|
try {
|
|
4310
4389
|
console.log(ansis.blue(`${i18n.t("cometix:installingCometix")}`));
|
|
4311
|
-
await
|
|
4390
|
+
await runInstallCommand();
|
|
4312
4391
|
console.log(ansis.green(`\u2714 ${i18n.t("cometix:cometixInstallSuccess")}`));
|
|
4313
4392
|
try {
|
|
4314
4393
|
addCCometixLineConfig();
|
|
@@ -4443,11 +4522,9 @@ async function configureOutputStyle(preselectedStyles, preselectedDefault) {
|
|
|
4443
4522
|
const availableStyles = getAvailableOutputStyles();
|
|
4444
4523
|
if (hasLegacyPersonalityFiles() && !preselectedStyles) {
|
|
4445
4524
|
console.log(ansis.yellow(`\u26A0\uFE0F ${i18n.t("configuration:legacyFilesDetected")}`));
|
|
4446
|
-
const
|
|
4447
|
-
type: "confirm",
|
|
4448
|
-
name: "cleanupLegacy",
|
|
4525
|
+
const cleanupLegacy = await promptBoolean({
|
|
4449
4526
|
message: i18n.t("configuration:cleanupLegacyFiles"),
|
|
4450
|
-
|
|
4527
|
+
defaultValue: true
|
|
4451
4528
|
});
|
|
4452
4529
|
if (cleanupLegacy) {
|
|
4453
4530
|
cleanupLegacyPersonalityFiles();
|
|
@@ -4742,7 +4819,9 @@ ${ansis.cyan(i18n.t("common:complete"))}`);
|
|
|
4742
4819
|
}
|
|
4743
4820
|
|
|
4744
4821
|
function handleExitPromptError(error) {
|
|
4745
|
-
|
|
4822
|
+
const isExitError = error instanceof Error && (error.name === "ExitPromptError" || error.message?.includes("ExitPromptError") || error.message?.includes("User force closed the prompt"));
|
|
4823
|
+
if (isExitError) {
|
|
4824
|
+
ensureI18nInitialized();
|
|
4746
4825
|
console.log(ansis.cyan(`
|
|
4747
4826
|
${i18n.t("common:goodbye")}
|
|
4748
4827
|
`));
|
|
@@ -4762,13 +4841,17 @@ function handleGeneralError(error) {
|
|
|
4762
4841
|
async function isClaudeCodeInstalled() {
|
|
4763
4842
|
return await commandExists("claude");
|
|
4764
4843
|
}
|
|
4765
|
-
async function installClaudeCode() {
|
|
4844
|
+
async function installClaudeCode(skipMethodSelection = false) {
|
|
4766
4845
|
ensureI18nInitialized();
|
|
4846
|
+
const codeType = "claude-code";
|
|
4767
4847
|
const installed = await isClaudeCodeInstalled();
|
|
4768
4848
|
if (installed) {
|
|
4769
4849
|
console.log(ansis.green(`\u2714 ${i18n.t("installation:alreadyInstalled")}`));
|
|
4850
|
+
const version = await detectInstalledVersion(codeType);
|
|
4851
|
+
if (version) {
|
|
4852
|
+
console.log(ansis.gray(` ${i18n.t("installation:detectedVersion", { version })}`));
|
|
4853
|
+
}
|
|
4770
4854
|
await updateClaudeCode();
|
|
4771
|
-
await setInstallMethod("npm");
|
|
4772
4855
|
return;
|
|
4773
4856
|
}
|
|
4774
4857
|
if (isTermux()) {
|
|
@@ -4787,27 +4870,100 @@ async function installClaudeCode() {
|
|
|
4787
4870
|
}
|
|
4788
4871
|
console.log(ansis.gray(i18n.t("installation:wslPathInfo", { path: `${homedir()}/.claude/` })));
|
|
4789
4872
|
}
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4873
|
+
if (skipMethodSelection) {
|
|
4874
|
+
console.log(i18n.t("installation:installing"));
|
|
4875
|
+
try {
|
|
4876
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
|
|
4877
|
+
if (usedSudo) {
|
|
4878
|
+
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:usingSudo")}`));
|
|
4879
|
+
}
|
|
4880
|
+
await exec(command, args);
|
|
4881
|
+
console.log(`\u2714 ${i18n.t("installation:installSuccess")}`);
|
|
4882
|
+
await setInstallMethod("npm");
|
|
4883
|
+
if (isTermux()) {
|
|
4884
|
+
console.log(ansis.gray(`
|
|
4797
4885
|
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4886
|
+
}
|
|
4887
|
+
if (isWSL()) {
|
|
4888
|
+
console.log(ansis.gray(`
|
|
4801
4889
|
${i18n.t("installation:wslInstallSuccess")}`));
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4890
|
+
}
|
|
4891
|
+
} catch (error) {
|
|
4892
|
+
console.error(`\u2716 ${i18n.t("installation:installFailed")}`);
|
|
4893
|
+
if (isTermux()) {
|
|
4894
|
+
console.error(ansis.yellow(`
|
|
4807
4895
|
${i18n.t("installation:termuxInstallHint")}
|
|
4808
4896
|
`));
|
|
4897
|
+
}
|
|
4898
|
+
throw error;
|
|
4899
|
+
}
|
|
4900
|
+
return;
|
|
4901
|
+
}
|
|
4902
|
+
const method = await selectInstallMethod(codeType);
|
|
4903
|
+
if (!method) {
|
|
4904
|
+
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
4905
|
+
return;
|
|
4906
|
+
}
|
|
4907
|
+
const success = await executeInstallMethod(method, codeType);
|
|
4908
|
+
if (!success) {
|
|
4909
|
+
const retrySuccess = await handleInstallFailure(codeType, [method]);
|
|
4910
|
+
if (!retrySuccess) {
|
|
4911
|
+
console.error(ansis.red(`\u2716 ${i18n.t("installation:installFailed")}`));
|
|
4912
|
+
throw new Error(i18n.t("installation:installFailed"));
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
if (isTermux()) {
|
|
4916
|
+
console.log(ansis.gray(`
|
|
4917
|
+
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
4918
|
+
}
|
|
4919
|
+
if (isWSL()) {
|
|
4920
|
+
console.log(ansis.gray(`
|
|
4921
|
+
${i18n.t("installation:wslInstallSuccess")}`));
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4924
|
+
async function isCodexInstalled() {
|
|
4925
|
+
return await commandExists("codex");
|
|
4926
|
+
}
|
|
4927
|
+
async function installCodex(skipMethodSelection = false) {
|
|
4928
|
+
ensureI18nInitialized();
|
|
4929
|
+
const codeType = "codex";
|
|
4930
|
+
const codeTypeName = i18n.t("common:codex");
|
|
4931
|
+
const installed = await isCodexInstalled();
|
|
4932
|
+
if (installed) {
|
|
4933
|
+
console.log(ansis.green(`\u2714 ${codeTypeName} ${i18n.t("installation:alreadyInstalled")}`));
|
|
4934
|
+
const version = await detectInstalledVersion(codeType);
|
|
4935
|
+
if (version) {
|
|
4936
|
+
console.log(ansis.gray(` ${i18n.t("installation:detectedVersion", { version })}`));
|
|
4937
|
+
}
|
|
4938
|
+
return;
|
|
4939
|
+
}
|
|
4940
|
+
if (skipMethodSelection) {
|
|
4941
|
+
console.log(i18n.t("installation:installingWith", { method: "npm", codeType: codeTypeName }));
|
|
4942
|
+
try {
|
|
4943
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@openai/codex"]);
|
|
4944
|
+
if (usedSudo) {
|
|
4945
|
+
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:usingSudo")}`));
|
|
4946
|
+
}
|
|
4947
|
+
await exec(command, args);
|
|
4948
|
+
console.log(ansis.green(`\u2714 ${codeTypeName} ${i18n.t("installation:installSuccess")}`));
|
|
4949
|
+
} catch (error) {
|
|
4950
|
+
console.error(ansis.red(`\u2716 ${codeTypeName} ${i18n.t("installation:installFailed")}`));
|
|
4951
|
+
throw error;
|
|
4952
|
+
}
|
|
4953
|
+
return;
|
|
4954
|
+
}
|
|
4955
|
+
const method = await selectInstallMethod(codeType);
|
|
4956
|
+
if (!method) {
|
|
4957
|
+
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
4958
|
+
return;
|
|
4959
|
+
}
|
|
4960
|
+
const success = await executeInstallMethod(method, codeType);
|
|
4961
|
+
if (!success) {
|
|
4962
|
+
const retrySuccess = await handleInstallFailure(codeType, [method]);
|
|
4963
|
+
if (!retrySuccess) {
|
|
4964
|
+
console.error(ansis.red(`\u2716 ${codeTypeName} ${i18n.t("installation:installFailed")}`));
|
|
4965
|
+
throw new Error(i18n.t("installation:installFailed"));
|
|
4809
4966
|
}
|
|
4810
|
-
throw error;
|
|
4811
4967
|
}
|
|
4812
4968
|
}
|
|
4813
4969
|
async function isLocalClaudeCodeInstalled() {
|
|
@@ -4841,28 +4997,297 @@ async function removeLocalClaudeCode() {
|
|
|
4841
4997
|
throw new Error(`${i18n.t("installation:failedToRemoveLocalInstallation")}: ${error}`);
|
|
4842
4998
|
}
|
|
4843
4999
|
}
|
|
4844
|
-
async function
|
|
5000
|
+
async function getInstallMethodFromConfig(codeType) {
|
|
4845
5001
|
try {
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
config
|
|
5002
|
+
if (codeType === "claude-code") {
|
|
5003
|
+
const { readMcpConfig } = await Promise.resolve().then(function () { return claudeConfig; });
|
|
5004
|
+
const config = readMcpConfig();
|
|
5005
|
+
return config?.installMethod || null;
|
|
4850
5006
|
}
|
|
4851
|
-
|
|
4852
|
-
|
|
5007
|
+
} catch {
|
|
5008
|
+
}
|
|
5009
|
+
return null;
|
|
5010
|
+
}
|
|
5011
|
+
async function uninstallCodeTool(codeType) {
|
|
5012
|
+
ensureI18nInitialized();
|
|
5013
|
+
const codeTypeName = codeType === "claude-code" ? i18n.t("common:claudeCode") : i18n.t("common:codex");
|
|
5014
|
+
let method = await getInstallMethodFromConfig(codeType);
|
|
5015
|
+
if (!method) {
|
|
5016
|
+
if (codeType === "claude-code") {
|
|
5017
|
+
try {
|
|
5018
|
+
const result = await exec("brew", ["list", "--cask", "claude-code"]);
|
|
5019
|
+
if (result.exitCode === 0) {
|
|
5020
|
+
method = "homebrew";
|
|
5021
|
+
}
|
|
5022
|
+
} catch {
|
|
5023
|
+
}
|
|
5024
|
+
} else if (codeType === "codex") {
|
|
5025
|
+
try {
|
|
5026
|
+
const result = await exec("brew", ["list", "codex"]);
|
|
5027
|
+
if (result.exitCode === 0) {
|
|
5028
|
+
method = "homebrew";
|
|
5029
|
+
}
|
|
5030
|
+
} catch {
|
|
5031
|
+
}
|
|
5032
|
+
}
|
|
5033
|
+
if (!method) {
|
|
5034
|
+
method = "npm";
|
|
5035
|
+
}
|
|
5036
|
+
}
|
|
5037
|
+
if (method === "native") {
|
|
5038
|
+
const platform = getPlatform();
|
|
5039
|
+
if (platform === "macos" || platform === "linux") {
|
|
5040
|
+
try {
|
|
5041
|
+
const testResult = codeType === "claude-code" ? await exec("brew", ["list", "--cask", "claude-code"]) : await exec("brew", ["list", "codex"]);
|
|
5042
|
+
if (testResult.exitCode === 0) {
|
|
5043
|
+
method = "homebrew";
|
|
5044
|
+
}
|
|
5045
|
+
} catch {
|
|
5046
|
+
method = "manual";
|
|
5047
|
+
}
|
|
5048
|
+
} else {
|
|
5049
|
+
method = "manual";
|
|
5050
|
+
}
|
|
5051
|
+
}
|
|
5052
|
+
const spinner = ora(i18n.t("installation:uninstallingWith", { method, codeType: codeTypeName })).start();
|
|
5053
|
+
try {
|
|
5054
|
+
switch (method) {
|
|
5055
|
+
case "npm": {
|
|
5056
|
+
const packageName = codeType === "claude-code" ? "@anthropic-ai/claude-code" : "@openai/codex";
|
|
5057
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["uninstall", "-g", packageName]);
|
|
5058
|
+
if (usedSudo) {
|
|
5059
|
+
spinner.info(i18n.t("installation:usingSudo"));
|
|
5060
|
+
spinner.start();
|
|
5061
|
+
}
|
|
5062
|
+
await exec(command, args);
|
|
5063
|
+
break;
|
|
5064
|
+
}
|
|
5065
|
+
case "homebrew": {
|
|
5066
|
+
if (codeType === "claude-code") {
|
|
5067
|
+
await exec("brew", ["uninstall", "--cask", "claude-code"]);
|
|
5068
|
+
} else {
|
|
5069
|
+
await exec("brew", ["uninstall", "codex"]);
|
|
5070
|
+
}
|
|
5071
|
+
break;
|
|
5072
|
+
}
|
|
5073
|
+
case "manual":
|
|
5074
|
+
default: {
|
|
5075
|
+
spinner.warn(i18n.t("installation:manualUninstallRequired", { codeType: codeTypeName }));
|
|
5076
|
+
const command = codeType === "claude-code" ? "claude" : "codex";
|
|
5077
|
+
try {
|
|
5078
|
+
const whichCmd = getPlatform() === "windows" ? "where" : "which";
|
|
5079
|
+
const result = await exec(whichCmd, [command]);
|
|
5080
|
+
if (result.stdout) {
|
|
5081
|
+
const binaryPath = result.stdout.trim().split("\n")[0];
|
|
5082
|
+
spinner.info(i18n.t("installation:binaryLocation", { path: binaryPath }));
|
|
5083
|
+
const platform = getPlatform();
|
|
5084
|
+
if (platform === "windows") {
|
|
5085
|
+
const quotedBinaryPath = `"${binaryPath}"`;
|
|
5086
|
+
await exec("cmd", ["/c", "del", "/f", "/q", quotedBinaryPath]);
|
|
5087
|
+
} else {
|
|
5088
|
+
const { command: rmCmd, args: rmArgs } = wrapCommandWithSudo("rm", ["-f", binaryPath]);
|
|
5089
|
+
if (rmCmd === "sudo") {
|
|
5090
|
+
spinner.info(i18n.t("installation:usingSudo"));
|
|
5091
|
+
spinner.start();
|
|
5092
|
+
}
|
|
5093
|
+
await exec(rmCmd, rmArgs);
|
|
5094
|
+
}
|
|
5095
|
+
}
|
|
5096
|
+
} catch {
|
|
5097
|
+
spinner.fail(i18n.t("installation:failedToLocateBinary", { command }));
|
|
5098
|
+
return false;
|
|
5099
|
+
}
|
|
5100
|
+
break;
|
|
5101
|
+
}
|
|
5102
|
+
}
|
|
5103
|
+
spinner.succeed(i18n.t("installation:uninstallSuccess", { method, codeType: codeTypeName }));
|
|
5104
|
+
return true;
|
|
4853
5105
|
} catch (error) {
|
|
4854
|
-
|
|
5106
|
+
spinner.fail(i18n.t("installation:uninstallFailed", { method, codeType: codeTypeName }));
|
|
5107
|
+
if (error instanceof Error) {
|
|
5108
|
+
console.error(ansis.gray(error.message));
|
|
5109
|
+
}
|
|
5110
|
+
return false;
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
async function setInstallMethod(method = "npm", codeType = "claude-code") {
|
|
5114
|
+
try {
|
|
5115
|
+
if (codeType === "claude-code") {
|
|
5116
|
+
const { readMcpConfig, writeMcpConfig } = await Promise.resolve().then(function () { return claudeConfig; });
|
|
5117
|
+
let config = readMcpConfig();
|
|
5118
|
+
if (!config) {
|
|
5119
|
+
config = { mcpServers: {} };
|
|
5120
|
+
}
|
|
5121
|
+
config.installMethod = method === "npm" ? "npm" : "native";
|
|
5122
|
+
writeMcpConfig(config);
|
|
5123
|
+
}
|
|
5124
|
+
} catch (error) {
|
|
5125
|
+
console.error("Failed to set installMethod:", error);
|
|
4855
5126
|
}
|
|
4856
5127
|
}
|
|
5128
|
+
async function detectInstalledVersion(codeType) {
|
|
5129
|
+
try {
|
|
5130
|
+
const command = codeType === "claude-code" ? "claude" : "codex";
|
|
5131
|
+
const result = await exec(command, ["--version"]);
|
|
5132
|
+
if (result.exitCode === 0 && result.stdout) {
|
|
5133
|
+
const versionMatch = result.stdout.match(/(\d+\.\d+\.\d+)/);
|
|
5134
|
+
return versionMatch ? versionMatch[1] : result.stdout.trim();
|
|
5135
|
+
}
|
|
5136
|
+
} catch {
|
|
5137
|
+
}
|
|
5138
|
+
return null;
|
|
5139
|
+
}
|
|
5140
|
+
function getInstallMethodOptions(codeType, recommendedMethods) {
|
|
5141
|
+
const allMethods = ["npm", "homebrew", "curl", "powershell", "cmd"];
|
|
5142
|
+
const platform = getPlatform();
|
|
5143
|
+
const availableMethods = allMethods.filter((method) => {
|
|
5144
|
+
if (codeType === "codex" && !["npm", "homebrew"].includes(method)) {
|
|
5145
|
+
return false;
|
|
5146
|
+
}
|
|
5147
|
+
if (method === "homebrew")
|
|
5148
|
+
return platform === "macos" || platform === "linux";
|
|
5149
|
+
if (method === "curl")
|
|
5150
|
+
return platform !== "windows" || isWSL();
|
|
5151
|
+
if (method === "powershell" || method === "cmd")
|
|
5152
|
+
return platform === "windows";
|
|
5153
|
+
return true;
|
|
5154
|
+
});
|
|
5155
|
+
const topRecommended = recommendedMethods.length > 0 ? recommendedMethods[0] : null;
|
|
5156
|
+
return availableMethods.map((method) => {
|
|
5157
|
+
const isTopRecommended = method === topRecommended;
|
|
5158
|
+
const title = isTopRecommended ? `${i18n.t(`installation:installMethod${method.charAt(0).toUpperCase() + method.slice(1)}`)} ${ansis.green(`[${i18n.t("installation:recommendedMethod")}]`)}` : i18n.t(`installation:installMethod${method.charAt(0).toUpperCase() + method.slice(1)}`);
|
|
5159
|
+
return {
|
|
5160
|
+
title,
|
|
5161
|
+
value: method
|
|
5162
|
+
};
|
|
5163
|
+
});
|
|
5164
|
+
}
|
|
5165
|
+
async function selectInstallMethod(codeType, excludeMethods = []) {
|
|
5166
|
+
ensureI18nInitialized();
|
|
5167
|
+
const codeTypeName = codeType === "claude-code" ? i18n.t("common:claudeCode") : i18n.t("common:codex");
|
|
5168
|
+
const recommendedMethods = getRecommendedInstallMethods(codeType);
|
|
5169
|
+
const methodOptions = getInstallMethodOptions(codeType, recommendedMethods).filter((option) => !excludeMethods.includes(option.value));
|
|
5170
|
+
if (methodOptions.length === 0) {
|
|
5171
|
+
console.log(ansis.yellow(i18n.t("installation:noMoreMethods")));
|
|
5172
|
+
return null;
|
|
5173
|
+
}
|
|
5174
|
+
const response = await inquirer.prompt({
|
|
5175
|
+
type: "list",
|
|
5176
|
+
name: "method",
|
|
5177
|
+
message: i18n.t("installation:selectInstallMethod", { codeType: codeTypeName }),
|
|
5178
|
+
choices: methodOptions.map((opt) => ({
|
|
5179
|
+
name: opt.title,
|
|
5180
|
+
value: opt.value
|
|
5181
|
+
}))
|
|
5182
|
+
});
|
|
5183
|
+
return response.method || null;
|
|
5184
|
+
}
|
|
5185
|
+
async function executeInstallMethod(method, codeType) {
|
|
5186
|
+
ensureI18nInitialized();
|
|
5187
|
+
const codeTypeName = codeType === "claude-code" ? i18n.t("common:claudeCode") : i18n.t("common:codex");
|
|
5188
|
+
const spinner = ora(i18n.t("installation:installingWith", { method, codeType: codeTypeName })).start();
|
|
5189
|
+
try {
|
|
5190
|
+
switch (method) {
|
|
5191
|
+
case "npm": {
|
|
5192
|
+
const packageName = codeType === "claude-code" ? "@anthropic-ai/claude-code" : "@openai/codex";
|
|
5193
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", packageName]);
|
|
5194
|
+
if (usedSudo) {
|
|
5195
|
+
spinner.info(i18n.t("installation:usingSudo"));
|
|
5196
|
+
spinner.start();
|
|
5197
|
+
}
|
|
5198
|
+
await exec(command, args);
|
|
5199
|
+
await setInstallMethod("npm", codeType);
|
|
5200
|
+
break;
|
|
5201
|
+
}
|
|
5202
|
+
case "homebrew": {
|
|
5203
|
+
if (codeType === "claude-code") {
|
|
5204
|
+
await exec("brew", ["install", "--cask", "claude-code"]);
|
|
5205
|
+
} else {
|
|
5206
|
+
await exec("brew", ["install", "codex"]);
|
|
5207
|
+
}
|
|
5208
|
+
await setInstallMethod("homebrew", codeType);
|
|
5209
|
+
break;
|
|
5210
|
+
}
|
|
5211
|
+
case "curl": {
|
|
5212
|
+
if (codeType === "claude-code") {
|
|
5213
|
+
await exec("bash", ["-c", "curl -fsSL https://claude.ai/install.sh | bash"]);
|
|
5214
|
+
} else {
|
|
5215
|
+
spinner.stop();
|
|
5216
|
+
return await executeInstallMethod("npm", codeType);
|
|
5217
|
+
}
|
|
5218
|
+
await setInstallMethod("curl", codeType);
|
|
5219
|
+
break;
|
|
5220
|
+
}
|
|
5221
|
+
case "powershell": {
|
|
5222
|
+
if (codeType === "claude-code") {
|
|
5223
|
+
await exec("powershell", ["-Command", "irm https://claude.ai/install.ps1 | iex"]);
|
|
5224
|
+
} else {
|
|
5225
|
+
spinner.stop();
|
|
5226
|
+
return await executeInstallMethod("npm", codeType);
|
|
5227
|
+
}
|
|
5228
|
+
await setInstallMethod("powershell", codeType);
|
|
5229
|
+
break;
|
|
5230
|
+
}
|
|
5231
|
+
case "cmd": {
|
|
5232
|
+
if (codeType === "claude-code") {
|
|
5233
|
+
await exec("cmd", ["/c", "curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd"]);
|
|
5234
|
+
} else {
|
|
5235
|
+
spinner.stop();
|
|
5236
|
+
return await executeInstallMethod("npm", codeType);
|
|
5237
|
+
}
|
|
5238
|
+
await setInstallMethod("cmd", codeType);
|
|
5239
|
+
break;
|
|
5240
|
+
}
|
|
5241
|
+
default:
|
|
5242
|
+
throw new Error(`Unsupported install method: ${method}`);
|
|
5243
|
+
}
|
|
5244
|
+
spinner.succeed(i18n.t("installation:installMethodSuccess", { method }));
|
|
5245
|
+
return true;
|
|
5246
|
+
} catch (error) {
|
|
5247
|
+
spinner.fail(i18n.t("installation:installMethodFailed", { method }));
|
|
5248
|
+
if (error instanceof Error) {
|
|
5249
|
+
console.error(ansis.gray(error.message));
|
|
5250
|
+
}
|
|
5251
|
+
return false;
|
|
5252
|
+
}
|
|
5253
|
+
}
|
|
5254
|
+
async function handleInstallFailure(codeType, failedMethods) {
|
|
5255
|
+
ensureI18nInitialized();
|
|
5256
|
+
const response = await inquirer.prompt({
|
|
5257
|
+
type: "confirm",
|
|
5258
|
+
name: "retry",
|
|
5259
|
+
message: i18n.t("installation:tryAnotherMethod"),
|
|
5260
|
+
default: true
|
|
5261
|
+
});
|
|
5262
|
+
if (!response.retry) {
|
|
5263
|
+
return false;
|
|
5264
|
+
}
|
|
5265
|
+
const newMethod = await selectInstallMethod(codeType, failedMethods);
|
|
5266
|
+
if (!newMethod) {
|
|
5267
|
+
return false;
|
|
5268
|
+
}
|
|
5269
|
+
const success = await executeInstallMethod(newMethod, codeType);
|
|
5270
|
+
if (success) {
|
|
5271
|
+
return true;
|
|
5272
|
+
}
|
|
5273
|
+
return await handleInstallFailure(codeType, [...failedMethods, newMethod]);
|
|
5274
|
+
}
|
|
4857
5275
|
|
|
4858
5276
|
const installer = {
|
|
4859
5277
|
__proto__: null,
|
|
5278
|
+
detectInstalledVersion: detectInstalledVersion,
|
|
5279
|
+
executeInstallMethod: executeInstallMethod,
|
|
4860
5280
|
getInstallationStatus: getInstallationStatus,
|
|
5281
|
+
handleInstallFailure: handleInstallFailure,
|
|
4861
5282
|
installClaudeCode: installClaudeCode,
|
|
5283
|
+
installCodex: installCodex,
|
|
4862
5284
|
isClaudeCodeInstalled: isClaudeCodeInstalled,
|
|
5285
|
+
isCodexInstalled: isCodexInstalled,
|
|
4863
5286
|
isLocalClaudeCodeInstalled: isLocalClaudeCodeInstalled,
|
|
4864
5287
|
removeLocalClaudeCode: removeLocalClaudeCode,
|
|
4865
|
-
|
|
5288
|
+
selectInstallMethod: selectInstallMethod,
|
|
5289
|
+
setInstallMethod: setInstallMethod,
|
|
5290
|
+
uninstallCodeTool: uninstallCodeTool
|
|
4866
5291
|
};
|
|
4867
5292
|
|
|
4868
5293
|
async function chooseInstallationMethod() {
|
|
@@ -5432,7 +5857,7 @@ async function init(options = {}) {
|
|
|
5432
5857
|
if (installationStatus.hasLocal) {
|
|
5433
5858
|
if (!installationStatus.hasGlobal) {
|
|
5434
5859
|
console.log(ansis.blue(`${i18n.t("installation:installingGlobalClaudeCode")}...`));
|
|
5435
|
-
await installClaudeCode();
|
|
5860
|
+
await installClaudeCode(true);
|
|
5436
5861
|
console.log(ansis.green(`\u2714 ${i18n.t("installation:globalInstallationCompleted")}`));
|
|
5437
5862
|
}
|
|
5438
5863
|
if (installationStatus.hasGlobal && installationStatus.hasLocal) {
|
|
@@ -5446,20 +5871,14 @@ async function init(options = {}) {
|
|
|
5446
5871
|
}
|
|
5447
5872
|
} else {
|
|
5448
5873
|
if (options.skipPrompt) {
|
|
5449
|
-
await installClaudeCode();
|
|
5874
|
+
await installClaudeCode(true);
|
|
5450
5875
|
} else {
|
|
5451
|
-
const
|
|
5452
|
-
type: "confirm",
|
|
5453
|
-
name: "shouldInstall",
|
|
5876
|
+
const shouldInstall = await promptBoolean({
|
|
5454
5877
|
message: i18n.t("installation:installPrompt"),
|
|
5455
|
-
|
|
5878
|
+
defaultValue: true
|
|
5456
5879
|
});
|
|
5457
|
-
if (shouldInstall === void 0) {
|
|
5458
|
-
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
5459
|
-
process.exit(0);
|
|
5460
|
-
}
|
|
5461
5880
|
if (shouldInstall) {
|
|
5462
|
-
await installClaudeCode();
|
|
5881
|
+
await installClaudeCode(false);
|
|
5463
5882
|
} else {
|
|
5464
5883
|
console.log(ansis.yellow(i18n.t("common:skip")));
|
|
5465
5884
|
}
|
|
@@ -5664,16 +6083,10 @@ async function init(options = {}) {
|
|
|
5664
6083
|
if (options.skipPrompt) {
|
|
5665
6084
|
shouldConfigureMcp = options.mcpServices !== false;
|
|
5666
6085
|
} else {
|
|
5667
|
-
const
|
|
5668
|
-
type: "confirm",
|
|
5669
|
-
name: "shouldConfigureMcp",
|
|
6086
|
+
const userChoice = await promptBoolean({
|
|
5670
6087
|
message: i18n.t("mcp:configureMcp"),
|
|
5671
|
-
|
|
6088
|
+
defaultValue: true
|
|
5672
6089
|
});
|
|
5673
|
-
if (userChoice === void 0) {
|
|
5674
|
-
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
5675
|
-
process.exit(0);
|
|
5676
|
-
}
|
|
5677
6090
|
shouldConfigureMcp = userChoice;
|
|
5678
6091
|
}
|
|
5679
6092
|
if (shouldConfigureMcp) {
|
|
@@ -5749,16 +6162,10 @@ async function init(options = {}) {
|
|
|
5749
6162
|
if (options.skipPrompt) {
|
|
5750
6163
|
shouldInstallCometix = options.installCometixLine !== false;
|
|
5751
6164
|
} else {
|
|
5752
|
-
const
|
|
5753
|
-
type: "confirm",
|
|
5754
|
-
name: "shouldInstallCometix",
|
|
6165
|
+
const userChoice = await promptBoolean({
|
|
5755
6166
|
message: i18n.t("cometix:installCometixPrompt"),
|
|
5756
|
-
|
|
6167
|
+
defaultValue: true
|
|
5757
6168
|
});
|
|
5758
|
-
if (userChoice === void 0) {
|
|
5759
|
-
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
5760
|
-
process.exit(0);
|
|
5761
|
-
}
|
|
5762
6169
|
shouldInstallCometix = userChoice;
|
|
5763
6170
|
}
|
|
5764
6171
|
if (shouldInstallCometix) {
|
|
@@ -6045,4 +6452,4 @@ async function openSettingsJson() {
|
|
|
6045
6452
|
}
|
|
6046
6453
|
}
|
|
6047
6454
|
|
|
6048
|
-
export { getExistingModelConfig as $, API_DEFAULT_URL as A, getAiOutputLanguageLabel as B, CLAUDE_DIR as C, DEFAULT_CODE_TOOL_TYPE as D, getMcpConfigPath as E, readMcpConfig as F, writeMcpConfig as G, backupMcpConfig as H, mergeMcpServers as I, buildMcpServerConfig as J, fixWindowsMcpConfig as K, LEGACY_ZCF_CONFIG_FILES as L, addCompletedOnboarding as M, ensureApiKeyApproved as N, removeApiKeyFromRejected as O, manageApiKeyApproval as P, setPrimaryApiKey as Q, ensureClaudeDir as R, SETTINGS_FILE as S, backupExistingConfig as T, copyConfigFiles as U, configureApi as V, mergeConfigs as W, updateCustomModel as X, updateDefaultModel as Y, ZCF_CONFIG_DIR as Z, mergeSettingsFile as _, commandExists as a,
|
|
6455
|
+
export { getExistingModelConfig as $, API_DEFAULT_URL as A, getAiOutputLanguageLabel as B, CLAUDE_DIR as C, DEFAULT_CODE_TOOL_TYPE as D, getMcpConfigPath as E, readMcpConfig as F, writeMcpConfig as G, backupMcpConfig as H, mergeMcpServers as I, buildMcpServerConfig as J, fixWindowsMcpConfig as K, LEGACY_ZCF_CONFIG_FILES as L, addCompletedOnboarding as M, ensureApiKeyApproved as N, removeApiKeyFromRejected as O, manageApiKeyApproval as P, setPrimaryApiKey as Q, ensureClaudeDir as R, SETTINGS_FILE as S, backupExistingConfig as T, copyConfigFiles as U, configureApi as V, mergeConfigs as W, updateCustomModel as X, updateDefaultModel as Y, ZCF_CONFIG_DIR as Z, mergeSettingsFile as _, commandExists as a, displayBannerWithInfo as a$, getExistingApiConfig as a0, applyAiLanguageDirective as a1, switchToOfficialLogin$1 as a2, promptApiConfigurationAction as a3, isClaudeCodeInstalled as a4, installClaudeCode as a5, isCodexInstalled as a6, installCodex as a7, isLocalClaudeCodeInstalled as a8, getInstallationStatus as a9, readZcfConfig as aA, configureOutputStyle as aB, isWindows as aC, selectMcpServices as aD, getMcpServices as aE, isCcrInstalled as aF, installCcr as aG, setupCcrConfiguration as aH, modifyApiConfigPartially as aI, formatApiKeyDisplay as aJ, readCcrConfig as aK, configureCcrFeature as aL, handleExitPromptError as aM, handleGeneralError as aN, COMETIX_COMMAND_NAME as aO, COMETIX_COMMANDS as aP, installCometixLine as aQ, checkAndUpdateTools as aR, runCodexUpdate as aS, resolveCodeType as aT, writeJsonConfig as aU, displayBanner as aV, version as aW, resolveAiOutputLanguage as aX, updatePromptOnly as aY, selectAndInstallWorkflows as aZ, checkClaudeCodeVersionAndPrompt as a_, removeLocalClaudeCode as aa, uninstallCodeTool as ab, setInstallMethod as ac, detectInstalledVersion as ad, selectInstallMethod as ae, executeInstallMethod as af, handleInstallFailure as ag, ensureI18nInitialized as ah, i18n as ai, addNumbersToChoices as aj, validateApiKey as ak, promptBoolean as al, ensureDir as am, readDefaultTomlConfig as an, createDefaultTomlConfig as ao, exists as ap, readJsonConfig as aq, writeTomlConfig as ar, copyFile as as, detectConfigManagementMode as at, readCodexConfig as au, backupCodexComplete as av, writeCodexConfig as aw, writeAuthFile as ax, updateZcfConfig as ay, changeLanguage as az, importRecommendedEnv as b, runCodexUninstall as b0, configureCodexMcp as b1, configureCodexApi as b2, runCodexWorkflowImportWithLanguageSelection as b3, runCodexFullInit as b4, switchCodexProvider as b5, listCodexProviders as b6, switchToOfficialLogin as b7, switchToProvider as b8, readZcfConfigAsync as b9, initI18n as ba, selectScriptLanguage as bb, index as bc, fsOperations as bd, jsonConfig as be, claudeConfig as bf, config$1 as bg, config as bh, prompts as bi, codex as bj, installer as bk, cleanupPermissions as c, importRecommendedPermissions as d, CLAUDE_MD_FILE as e, ClAUDE_CONFIG_FILE as f, getPlatform as g, CLAUDE_VSC_CONFIG_FILE as h, init as i, CODEX_DIR as j, CODEX_CONFIG_FILE as k, CODEX_AUTH_FILE as l, mergeAndCleanPermissions as m, CODEX_AGENTS_FILE as n, openSettingsJson as o, CODEX_PROMPTS_DIR as p, ZCF_CONFIG_FILE as q, CODE_TOOL_TYPES as r, CODE_TOOL_BANNERS as s, CODE_TOOL_ALIASES as t, isCodeToolType as u, API_ENV_KEY as v, resolveCodeToolType as w, SUPPORTED_LANGS as x, LANG_LABELS as y, AI_OUTPUT_LANGUAGES as z };
|