zcf 3.4.0 → 3.4.2
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 +6 -0
- package/dist/chunks/claude-code-config-manager.mjs +7 -7
- package/dist/chunks/claude-code-incremental-manager.mjs +1 -1
- package/dist/chunks/codex-config-switch.mjs +7 -7
- package/dist/chunks/codex-provider-manager.mjs +3 -3
- package/dist/chunks/codex-uninstaller.mjs +2 -2
- package/dist/chunks/commands.mjs +1 -1
- package/dist/chunks/features.mjs +10 -10
- package/dist/chunks/simple-config.mjs +1014 -58
- package/dist/cli.mjs +6 -6
- package/dist/i18n/locales/en/codex.json +6 -1
- package/dist/i18n/locales/en/common.json +2 -1
- package/dist/i18n/locales/en/configuration.json +2 -0
- package/dist/i18n/locales/en/installation.json +34 -1
- package/dist/i18n/locales/zh-CN/codex.json +6 -1
- package/dist/i18n/locales/zh-CN/common.json +2 -1
- package/dist/i18n/locales/zh-CN/configuration.json +2 -0
- package/dist/i18n/locales/zh-CN/installation.json +34 -1
- package/dist/index.d.mts +47 -12
- package/dist/index.d.ts +47 -12
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import process from 'node:process';
|
|
|
4
4
|
import ansis from 'ansis';
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
6
|
import { exec as exec$1 } from 'node:child_process';
|
|
7
|
-
import { homedir, platform } from 'node:os';
|
|
7
|
+
import { homedir, platform as platform$1 } from 'node:os';
|
|
8
8
|
import { promisify } from 'node:util';
|
|
9
9
|
import dayjs from 'dayjs';
|
|
10
10
|
import { dirname, join } from 'pathe';
|
|
@@ -18,7 +18,7 @@ import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
|
|
|
18
18
|
import i18next from 'i18next';
|
|
19
19
|
import Backend from 'i18next-fs-backend';
|
|
20
20
|
|
|
21
|
-
const version = "3.4.
|
|
21
|
+
const version = "3.4.2";
|
|
22
22
|
const homepage = "https://github.com/UfoMiao/zcf";
|
|
23
23
|
|
|
24
24
|
const i18n = i18next.createInstance();
|
|
@@ -517,7 +517,7 @@ function displayBannerWithInfo(subtitle) {
|
|
|
517
517
|
|
|
518
518
|
const WINDOWS_WRAPPED_COMMANDS = ["npx", "uvx", "uv"];
|
|
519
519
|
function getPlatform() {
|
|
520
|
-
const p = platform();
|
|
520
|
+
const p = platform$1();
|
|
521
521
|
if (p === "win32")
|
|
522
522
|
return "windows";
|
|
523
523
|
if (p === "darwin")
|
|
@@ -703,9 +703,105 @@ async function commandExists(command) {
|
|
|
703
703
|
return true;
|
|
704
704
|
}
|
|
705
705
|
}
|
|
706
|
+
if (getPlatform() === "macos") {
|
|
707
|
+
const homebrewPaths = await getHomebrewCommandPaths(command);
|
|
708
|
+
for (const path of homebrewPaths) {
|
|
709
|
+
if (nodeFs.existsSync(path)) {
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
706
714
|
}
|
|
707
715
|
return false;
|
|
708
716
|
}
|
|
717
|
+
async function getHomebrewCommandPaths(command) {
|
|
718
|
+
const paths = [];
|
|
719
|
+
const homebrewPrefixes = [
|
|
720
|
+
"/opt/homebrew",
|
|
721
|
+
// Apple Silicon (M1/M2)
|
|
722
|
+
"/usr/local"
|
|
723
|
+
// Intel Mac
|
|
724
|
+
];
|
|
725
|
+
for (const prefix of homebrewPrefixes) {
|
|
726
|
+
paths.push(`${prefix}/bin/${command}`);
|
|
727
|
+
}
|
|
728
|
+
for (const prefix of homebrewPrefixes) {
|
|
729
|
+
const cellarNodePath = `${prefix}/Cellar/node`;
|
|
730
|
+
if (nodeFs.existsSync(cellarNodePath)) {
|
|
731
|
+
try {
|
|
732
|
+
const versions = nodeFs.readdirSync(cellarNodePath);
|
|
733
|
+
for (const version of versions) {
|
|
734
|
+
const binPath = `${cellarNodePath}/${version}/bin/${command}`;
|
|
735
|
+
paths.push(binPath);
|
|
736
|
+
}
|
|
737
|
+
} catch {
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
const caskNameMap = {
|
|
742
|
+
claude: "claude-code",
|
|
743
|
+
codex: "codex"
|
|
744
|
+
};
|
|
745
|
+
const caskName = caskNameMap[command];
|
|
746
|
+
if (caskName) {
|
|
747
|
+
for (const prefix of homebrewPrefixes) {
|
|
748
|
+
const caskroomPath = `${prefix}/Caskroom/${caskName}`;
|
|
749
|
+
if (nodeFs.existsSync(caskroomPath)) {
|
|
750
|
+
try {
|
|
751
|
+
const versions = nodeFs.readdirSync(caskroomPath).filter((v) => !v.startsWith("."));
|
|
752
|
+
for (const version of versions) {
|
|
753
|
+
const binPath = `${caskroomPath}/${version}/${command}`;
|
|
754
|
+
paths.push(binPath);
|
|
755
|
+
}
|
|
756
|
+
} catch {
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return paths;
|
|
762
|
+
}
|
|
763
|
+
async function findCommandPath(command) {
|
|
764
|
+
try {
|
|
765
|
+
const cmd = getPlatform() === "windows" ? "where" : "which";
|
|
766
|
+
const res = await exec(cmd, [command]);
|
|
767
|
+
if (res.exitCode === 0 && res.stdout) {
|
|
768
|
+
return res.stdout.trim().split("\n")[0];
|
|
769
|
+
}
|
|
770
|
+
} catch {
|
|
771
|
+
}
|
|
772
|
+
const commonPaths = [
|
|
773
|
+
`/usr/local/bin/${command}`,
|
|
774
|
+
`/usr/bin/${command}`,
|
|
775
|
+
`/bin/${command}`,
|
|
776
|
+
`${process.env.HOME}/.local/bin/${command}`
|
|
777
|
+
];
|
|
778
|
+
for (const path of commonPaths) {
|
|
779
|
+
if (nodeFs.existsSync(path)) {
|
|
780
|
+
return path;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
if (getPlatform() === "macos") {
|
|
784
|
+
const homebrewPaths = await getHomebrewCommandPaths(command);
|
|
785
|
+
for (const path of homebrewPaths) {
|
|
786
|
+
if (nodeFs.existsSync(path)) {
|
|
787
|
+
return path;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
if (isTermux()) {
|
|
792
|
+
const termuxPrefix = getTermuxPrefix();
|
|
793
|
+
const termuxPaths = [
|
|
794
|
+
`${termuxPrefix}/bin/${command}`,
|
|
795
|
+
`${termuxPrefix}/usr/bin/${command}`
|
|
796
|
+
];
|
|
797
|
+
for (const path of termuxPaths) {
|
|
798
|
+
if (nodeFs.existsSync(path)) {
|
|
799
|
+
return path;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
709
805
|
function getRecommendedInstallMethods(codeType) {
|
|
710
806
|
const platform2 = getPlatform();
|
|
711
807
|
const wsl = isWSL();
|
|
@@ -731,6 +827,26 @@ function getRecommendedInstallMethods(codeType) {
|
|
|
731
827
|
return ["npm"];
|
|
732
828
|
}
|
|
733
829
|
|
|
830
|
+
const platform = {
|
|
831
|
+
__proto__: null,
|
|
832
|
+
commandExists: commandExists,
|
|
833
|
+
findCommandPath: findCommandPath,
|
|
834
|
+
getHomebrewCommandPaths: getHomebrewCommandPaths,
|
|
835
|
+
getMcpCommand: getMcpCommand,
|
|
836
|
+
getPlatform: getPlatform,
|
|
837
|
+
getRecommendedInstallMethods: getRecommendedInstallMethods,
|
|
838
|
+
getSystemRoot: getSystemRoot,
|
|
839
|
+
getTermuxPrefix: getTermuxPrefix,
|
|
840
|
+
getWSLDistro: getWSLDistro,
|
|
841
|
+
getWSLInfo: getWSLInfo,
|
|
842
|
+
isTermux: isTermux,
|
|
843
|
+
isWSL: isWSL,
|
|
844
|
+
isWindows: isWindows,
|
|
845
|
+
normalizeTomlPath: normalizeTomlPath,
|
|
846
|
+
shouldUseSudoForGlobalInstall: shouldUseSudoForGlobalInstall,
|
|
847
|
+
wrapCommandWithSudo: wrapCommandWithSudo
|
|
848
|
+
};
|
|
849
|
+
|
|
734
850
|
class FileSystemError extends Error {
|
|
735
851
|
constructor(message, path, cause) {
|
|
736
852
|
super(message);
|
|
@@ -2014,6 +2130,327 @@ async function getLatestVersion(packageName, maxRetries = 3) {
|
|
|
2014
2130
|
}
|
|
2015
2131
|
return null;
|
|
2016
2132
|
}
|
|
2133
|
+
async function getClaudeCodeInstallationSource() {
|
|
2134
|
+
if (getPlatform() !== "macos") {
|
|
2135
|
+
const commandPath2 = await findCommandPath("claude");
|
|
2136
|
+
return {
|
|
2137
|
+
isHomebrew: false,
|
|
2138
|
+
commandPath: commandPath2,
|
|
2139
|
+
source: commandPath2 ? "other" : "not-found"
|
|
2140
|
+
};
|
|
2141
|
+
}
|
|
2142
|
+
const commandPath = await findCommandPath("claude");
|
|
2143
|
+
if (!commandPath) {
|
|
2144
|
+
return { isHomebrew: false, commandPath: null, source: "not-found" };
|
|
2145
|
+
}
|
|
2146
|
+
const isFromCaskroom = commandPath.includes("/Caskroom/claude-code/");
|
|
2147
|
+
if (isFromCaskroom) {
|
|
2148
|
+
return { isHomebrew: true, commandPath, source: "homebrew-cask" };
|
|
2149
|
+
}
|
|
2150
|
+
try {
|
|
2151
|
+
const { stdout: realPath } = await execAsync$3(`readlink -f "${commandPath}" 2>/dev/null || realpath "${commandPath}" 2>/dev/null || echo "${commandPath}"`);
|
|
2152
|
+
const resolvedPath = realPath.trim();
|
|
2153
|
+
if (resolvedPath.includes("/Caskroom/claude-code/")) {
|
|
2154
|
+
return { isHomebrew: true, commandPath, source: "homebrew-cask" };
|
|
2155
|
+
}
|
|
2156
|
+
} catch {
|
|
2157
|
+
}
|
|
2158
|
+
if (commandPath.includes("/node_modules/") || commandPath.includes("/npm/") || commandPath.includes("/Cellar/node/")) {
|
|
2159
|
+
return { isHomebrew: false, commandPath, source: "npm" };
|
|
2160
|
+
}
|
|
2161
|
+
return { isHomebrew: false, commandPath, source: "other" };
|
|
2162
|
+
}
|
|
2163
|
+
async function detectAllClaudeCodeInstallations() {
|
|
2164
|
+
const installations = [];
|
|
2165
|
+
const checkedPaths = /* @__PURE__ */ new Set();
|
|
2166
|
+
const activeCommandPath = await findCommandPath("claude");
|
|
2167
|
+
let activeResolvedPath = null;
|
|
2168
|
+
if (activeCommandPath) {
|
|
2169
|
+
try {
|
|
2170
|
+
const { stdout } = await execAsync$3(`readlink -f "${activeCommandPath}" 2>/dev/null || realpath "${activeCommandPath}" 2>/dev/null || echo "${activeCommandPath}"`);
|
|
2171
|
+
activeResolvedPath = stdout.trim();
|
|
2172
|
+
} catch {
|
|
2173
|
+
activeResolvedPath = activeCommandPath;
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
async function getVersionFromPath(path) {
|
|
2177
|
+
try {
|
|
2178
|
+
const { stdout } = await execAsync$3(`"${path}" -v 2>/dev/null || "${path}" --version 2>/dev/null`);
|
|
2179
|
+
const versionMatch = stdout.match(/(\d+\.\d+\.\d+(?:-[\w.]+)?)/);
|
|
2180
|
+
return versionMatch ? versionMatch[1] : null;
|
|
2181
|
+
} catch {
|
|
2182
|
+
return null;
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
function isActivePath(path) {
|
|
2186
|
+
if (!activeResolvedPath)
|
|
2187
|
+
return false;
|
|
2188
|
+
return path === activeResolvedPath || path === activeCommandPath;
|
|
2189
|
+
}
|
|
2190
|
+
async function addInstallation(path, source) {
|
|
2191
|
+
let resolvedPath = path;
|
|
2192
|
+
try {
|
|
2193
|
+
const { stdout } = await execAsync$3(`readlink -f "${path}" 2>/dev/null || realpath "${path}" 2>/dev/null || echo "${path}"`);
|
|
2194
|
+
resolvedPath = stdout.trim();
|
|
2195
|
+
} catch {
|
|
2196
|
+
}
|
|
2197
|
+
if (checkedPaths.has(resolvedPath))
|
|
2198
|
+
return;
|
|
2199
|
+
checkedPaths.add(resolvedPath);
|
|
2200
|
+
if (!nodeFs.existsSync(path))
|
|
2201
|
+
return;
|
|
2202
|
+
const version = await getVersionFromPath(path);
|
|
2203
|
+
installations.push({
|
|
2204
|
+
source,
|
|
2205
|
+
path,
|
|
2206
|
+
version,
|
|
2207
|
+
isActive: isActivePath(path) || isActivePath(resolvedPath)
|
|
2208
|
+
});
|
|
2209
|
+
}
|
|
2210
|
+
if (activeCommandPath && nodeFs.existsSync(activeCommandPath)) {
|
|
2211
|
+
let activeSource = "other";
|
|
2212
|
+
if (activeResolvedPath?.includes("/Caskroom/claude-code/")) {
|
|
2213
|
+
activeSource = "homebrew-cask";
|
|
2214
|
+
} else if (activeResolvedPath?.includes("/node_modules/") || activeResolvedPath?.includes("/npm/") || activeResolvedPath?.includes("/fnm_multishells/") || activeResolvedPath?.includes("/.nvm/") || activeResolvedPath?.includes("/Cellar/node/") || activeCommandPath.includes("/fnm_multishells/") || activeCommandPath.includes("/.nvm/")) {
|
|
2215
|
+
activeSource = "npm";
|
|
2216
|
+
}
|
|
2217
|
+
await addInstallation(activeCommandPath, activeSource);
|
|
2218
|
+
}
|
|
2219
|
+
if (getPlatform() === "macos") {
|
|
2220
|
+
const homebrewPaths = await getHomebrewCommandPaths("claude");
|
|
2221
|
+
for (const path of homebrewPaths) {
|
|
2222
|
+
if (path.includes("/Caskroom/claude-code/")) {
|
|
2223
|
+
await addInstallation(path, "homebrew-cask");
|
|
2224
|
+
} else if (path.includes("/Cellar/node/")) {
|
|
2225
|
+
await addInstallation(path, "npm-homebrew-node");
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
try {
|
|
2229
|
+
await execAsync$3("brew list --cask claude-code");
|
|
2230
|
+
const homebrewPrefixes = ["/opt/homebrew", "/usr/local"];
|
|
2231
|
+
for (const prefix of homebrewPrefixes) {
|
|
2232
|
+
const caskroomPath = `${prefix}/Caskroom/claude-code`;
|
|
2233
|
+
if (nodeFs.existsSync(caskroomPath)) {
|
|
2234
|
+
const versions = nodeFs.readdirSync(caskroomPath).filter((v) => !v.startsWith("."));
|
|
2235
|
+
for (const version of versions) {
|
|
2236
|
+
const claudePath = `${caskroomPath}/${version}/claude`;
|
|
2237
|
+
await addInstallation(claudePath, "homebrew-cask");
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
} catch {
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
const npmGlobalPaths = [
|
|
2245
|
+
"/usr/local/bin/claude",
|
|
2246
|
+
"/usr/bin/claude",
|
|
2247
|
+
`${process.env.HOME}/.npm-global/bin/claude`,
|
|
2248
|
+
`${process.env.HOME}/.local/bin/claude`
|
|
2249
|
+
];
|
|
2250
|
+
for (const path of npmGlobalPaths) {
|
|
2251
|
+
if (nodeFs.existsSync(path)) {
|
|
2252
|
+
let resolvedPath = path;
|
|
2253
|
+
try {
|
|
2254
|
+
const { stdout } = await execAsync$3(`readlink -f "${path}" 2>/dev/null || realpath "${path}" 2>/dev/null || echo "${path}"`);
|
|
2255
|
+
resolvedPath = stdout.trim();
|
|
2256
|
+
} catch {
|
|
2257
|
+
}
|
|
2258
|
+
if (resolvedPath.includes("/node_modules/") || resolvedPath.includes("/npm/")) {
|
|
2259
|
+
await addInstallation(path, "npm");
|
|
2260
|
+
} else if (resolvedPath.includes("/Caskroom/")) ; else {
|
|
2261
|
+
await addInstallation(path, "other");
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
if (getPlatform() === "macos") {
|
|
2266
|
+
const homebrewPrefixes = ["/opt/homebrew", "/usr/local"];
|
|
2267
|
+
for (const prefix of homebrewPrefixes) {
|
|
2268
|
+
const cellarNodePath = `${prefix}/Cellar/node`;
|
|
2269
|
+
if (nodeFs.existsSync(cellarNodePath)) {
|
|
2270
|
+
try {
|
|
2271
|
+
const versions = nodeFs.readdirSync(cellarNodePath);
|
|
2272
|
+
for (const version of versions) {
|
|
2273
|
+
const claudePath = `${cellarNodePath}/${version}/bin/claude`;
|
|
2274
|
+
await addInstallation(claudePath, "npm-homebrew-node");
|
|
2275
|
+
}
|
|
2276
|
+
} catch {
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
return installations;
|
|
2282
|
+
}
|
|
2283
|
+
async function checkDuplicateInstallations() {
|
|
2284
|
+
const installations = await detectAllClaudeCodeInstallations();
|
|
2285
|
+
const activeInstallation = installations.find((i) => i.isActive) || null;
|
|
2286
|
+
const inactiveInstallations = installations.filter((i) => !i.isActive);
|
|
2287
|
+
const homebrewInstallation = installations.find((i) => i.source === "homebrew-cask") || null;
|
|
2288
|
+
const npmInstallation = installations.find((i) => i.source === "npm" || i.source === "npm-homebrew-node") || null;
|
|
2289
|
+
const hasDuplicates = homebrewInstallation !== null && npmInstallation !== null;
|
|
2290
|
+
const recommendation = hasDuplicates ? "remove-npm" : "none";
|
|
2291
|
+
return {
|
|
2292
|
+
hasDuplicates,
|
|
2293
|
+
installations,
|
|
2294
|
+
activeInstallation,
|
|
2295
|
+
inactiveInstallations,
|
|
2296
|
+
homebrewInstallation,
|
|
2297
|
+
npmInstallation,
|
|
2298
|
+
recommendation
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2301
|
+
function getSourceDisplayName(source, i18n) {
|
|
2302
|
+
const sourceMap = {
|
|
2303
|
+
"homebrew-cask": i18n.t("installation:sourceHomebrewCask"),
|
|
2304
|
+
"npm": i18n.t("installation:sourceNpm"),
|
|
2305
|
+
"npm-homebrew-node": i18n.t("installation:sourceNpmHomebrewNode"),
|
|
2306
|
+
"curl": i18n.t("installation:sourceCurl"),
|
|
2307
|
+
"other": i18n.t("installation:sourceOther")
|
|
2308
|
+
};
|
|
2309
|
+
return sourceMap[source] || source;
|
|
2310
|
+
}
|
|
2311
|
+
async function performNpmRemovalAndActivateHomebrew(_npmInstallation, homebrewInstallation, tinyExec, i18n, ansis) {
|
|
2312
|
+
const ora = (await import('ora')).default;
|
|
2313
|
+
const spinner = ora(i18n.t("installation:removingDuplicateInstallation")).start();
|
|
2314
|
+
try {
|
|
2315
|
+
const { wrapCommandWithSudo } = await Promise.resolve().then(function () { return platform; });
|
|
2316
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["uninstall", "-g", "@anthropic-ai/claude-code"]);
|
|
2317
|
+
if (usedSudo) {
|
|
2318
|
+
spinner.info(i18n.t("installation:usingSudo"));
|
|
2319
|
+
spinner.start();
|
|
2320
|
+
}
|
|
2321
|
+
await tinyExec(command, args);
|
|
2322
|
+
spinner.succeed(i18n.t("installation:duplicateRemoved"));
|
|
2323
|
+
if (homebrewInstallation && !homebrewInstallation.isActive) {
|
|
2324
|
+
console.log("");
|
|
2325
|
+
console.log(ansis.cyan(`\u{1F517} ${i18n.t("installation:activatingHomebrew")}`));
|
|
2326
|
+
const { createHomebrewSymlink } = await Promise.resolve().then(function () { return installer; });
|
|
2327
|
+
const symlinkResult = await createHomebrewSymlink("claude", homebrewInstallation.path);
|
|
2328
|
+
if (symlinkResult.success) {
|
|
2329
|
+
console.log(ansis.green(`\u2714 ${i18n.t("installation:symlinkCreated", { path: symlinkResult.symlinkPath || "/usr/local/bin/claude" })}`));
|
|
2330
|
+
} else {
|
|
2331
|
+
console.log(ansis.yellow(`\u26A0 ${i18n.t("installation:manualSymlinkHint")}`));
|
|
2332
|
+
if (symlinkResult.error) {
|
|
2333
|
+
console.log(ansis.gray(` ${symlinkResult.error}`));
|
|
2334
|
+
} else {
|
|
2335
|
+
const homebrewBin = nodeFs.existsSync("/opt/homebrew/bin") ? "/opt/homebrew/bin" : "/usr/local/bin";
|
|
2336
|
+
console.log(ansis.gray(` sudo ln -sf "${homebrewInstallation.path}" ${homebrewBin}/claude`));
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
return { hadDuplicates: true, resolved: true, action: "removed-npm" };
|
|
2341
|
+
} catch (error) {
|
|
2342
|
+
spinner.fail(i18n.t("installation:duplicateRemovalFailed"));
|
|
2343
|
+
if (error instanceof Error) {
|
|
2344
|
+
console.error(ansis.gray(error.message));
|
|
2345
|
+
}
|
|
2346
|
+
return { hadDuplicates: true, resolved: false, action: "kept-both" };
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
async function handleDuplicateInstallations(skipPrompt = false) {
|
|
2350
|
+
const { ensureI18nInitialized, format, i18n } = await Promise.resolve().then(function () { return index; });
|
|
2351
|
+
const ansis = (await import('ansis')).default;
|
|
2352
|
+
ensureI18nInitialized();
|
|
2353
|
+
const duplicateInfo = await checkDuplicateInstallations();
|
|
2354
|
+
if (!duplicateInfo.hasDuplicates) {
|
|
2355
|
+
return { hadDuplicates: false, resolved: true, action: "no-duplicates" };
|
|
2356
|
+
}
|
|
2357
|
+
const { npmInstallation, homebrewInstallation } = duplicateInfo;
|
|
2358
|
+
console.log("");
|
|
2359
|
+
console.log(ansis.yellow.bold(i18n.t("installation:duplicateInstallationsDetected")));
|
|
2360
|
+
console.log(ansis.gray(i18n.t("installation:duplicateInstallationsWarning")));
|
|
2361
|
+
console.log("");
|
|
2362
|
+
if (homebrewInstallation) {
|
|
2363
|
+
const isActive = homebrewInstallation.isActive;
|
|
2364
|
+
const statusIcon = isActive ? "\u2705" : "\u26A0\uFE0F";
|
|
2365
|
+
const statusColor = isActive ? ansis.green : ansis.yellow;
|
|
2366
|
+
console.log(ansis.cyan.bold(`\u{1F37A} Homebrew Cask ${i18n.t("installation:recommendedMethod")}:`));
|
|
2367
|
+
console.log(ansis.white(` ${i18n.t("installation:installationSource")}: ${statusColor(getSourceDisplayName(homebrewInstallation.source, i18n))}`));
|
|
2368
|
+
console.log(ansis.white(` ${i18n.t("installation:installationPath")}: ${ansis.gray(homebrewInstallation.path)}`));
|
|
2369
|
+
if (homebrewInstallation.version) {
|
|
2370
|
+
console.log(ansis.white(` ${i18n.t("installation:installationVersion")}: ${ansis.cyan(homebrewInstallation.version)}`));
|
|
2371
|
+
}
|
|
2372
|
+
console.log(ansis.white(` ${statusIcon} ${isActive ? i18n.t("installation:currentActiveInstallation") : i18n.t("installation:inactiveInstallations")}`));
|
|
2373
|
+
console.log("");
|
|
2374
|
+
}
|
|
2375
|
+
if (npmInstallation) {
|
|
2376
|
+
const isActive = npmInstallation.isActive;
|
|
2377
|
+
console.log(ansis.yellow.bold(`\u{1F4E6} npm ${i18n.t("installation:notRecommended")}:`));
|
|
2378
|
+
console.log(ansis.white(` ${i18n.t("installation:installationSource")}: ${ansis.yellow(getSourceDisplayName(npmInstallation.source, i18n))}`));
|
|
2379
|
+
console.log(ansis.white(` ${i18n.t("installation:installationPath")}: ${ansis.gray(npmInstallation.path)}`));
|
|
2380
|
+
if (npmInstallation.version) {
|
|
2381
|
+
console.log(ansis.white(` ${i18n.t("installation:installationVersion")}: ${ansis.cyan(npmInstallation.version)}`));
|
|
2382
|
+
if (homebrewInstallation?.version && npmInstallation.version !== homebrewInstallation.version) {
|
|
2383
|
+
console.log(ansis.red(` ${format(i18n.t("installation:versionMismatchWarning"), {
|
|
2384
|
+
npmVersion: npmInstallation.version,
|
|
2385
|
+
homebrewVersion: homebrewInstallation.version
|
|
2386
|
+
})}`));
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
if (isActive) {
|
|
2390
|
+
console.log(ansis.white(` \u26A0\uFE0F ${i18n.t("installation:currentActiveInstallation")}`));
|
|
2391
|
+
}
|
|
2392
|
+
console.log("");
|
|
2393
|
+
}
|
|
2394
|
+
console.log(ansis.cyan(`\u{1F4A1} ${i18n.t("installation:recommendRemoveNpm")}`));
|
|
2395
|
+
console.log("");
|
|
2396
|
+
if (!npmInstallation) {
|
|
2397
|
+
return { hadDuplicates: true, resolved: false, action: "kept-both" };
|
|
2398
|
+
}
|
|
2399
|
+
const { exec: tinyExec } = await import('tinyexec');
|
|
2400
|
+
if (skipPrompt) {
|
|
2401
|
+
console.log(ansis.cyan(`\u{1F504} ${i18n.t("installation:autoRemovingNpm")}`));
|
|
2402
|
+
return await performNpmRemovalAndActivateHomebrew(
|
|
2403
|
+
npmInstallation,
|
|
2404
|
+
homebrewInstallation,
|
|
2405
|
+
tinyExec,
|
|
2406
|
+
i18n,
|
|
2407
|
+
ansis
|
|
2408
|
+
);
|
|
2409
|
+
}
|
|
2410
|
+
const inquirer = (await import('inquirer')).default;
|
|
2411
|
+
const sourceDisplayName = getSourceDisplayName(npmInstallation.source, i18n);
|
|
2412
|
+
const confirmMessage = format(i18n.t("installation:confirmRemoveDuplicate"), { source: sourceDisplayName });
|
|
2413
|
+
const { action } = await inquirer.prompt([
|
|
2414
|
+
{
|
|
2415
|
+
type: "list",
|
|
2416
|
+
name: "action",
|
|
2417
|
+
message: confirmMessage,
|
|
2418
|
+
choices: [
|
|
2419
|
+
{
|
|
2420
|
+
name: `\u2705 ${i18n.t("common:yes")} - ${i18n.t("installation:removingDuplicateInstallation")}`,
|
|
2421
|
+
value: "remove"
|
|
2422
|
+
},
|
|
2423
|
+
{
|
|
2424
|
+
name: `\u274C ${i18n.t("installation:keepBothInstallations")}`,
|
|
2425
|
+
value: "keep"
|
|
2426
|
+
}
|
|
2427
|
+
]
|
|
2428
|
+
}
|
|
2429
|
+
]);
|
|
2430
|
+
if (action === "keep") {
|
|
2431
|
+
console.log(ansis.gray(i18n.t("installation:duplicateWarningContinue")));
|
|
2432
|
+
return { hadDuplicates: true, resolved: false, action: "kept-both" };
|
|
2433
|
+
}
|
|
2434
|
+
return await performNpmRemovalAndActivateHomebrew(
|
|
2435
|
+
npmInstallation,
|
|
2436
|
+
homebrewInstallation,
|
|
2437
|
+
tinyExec,
|
|
2438
|
+
i18n,
|
|
2439
|
+
ansis
|
|
2440
|
+
);
|
|
2441
|
+
}
|
|
2442
|
+
async function getHomebrewClaudeCodeVersion() {
|
|
2443
|
+
try {
|
|
2444
|
+
const { stdout } = await execAsync$3("brew info --cask claude-code --json=v2");
|
|
2445
|
+
const info = JSON.parse(stdout);
|
|
2446
|
+
if (info.casks && info.casks.length > 0) {
|
|
2447
|
+
return info.casks[0].version;
|
|
2448
|
+
}
|
|
2449
|
+
return null;
|
|
2450
|
+
} catch {
|
|
2451
|
+
return null;
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2017
2454
|
function compareVersions(current, latest) {
|
|
2018
2455
|
if (!semver.valid(current) || !semver.valid(latest)) {
|
|
2019
2456
|
return -1;
|
|
@@ -2035,12 +2472,22 @@ async function checkCcrVersion() {
|
|
|
2035
2472
|
}
|
|
2036
2473
|
async function checkClaudeCodeVersion() {
|
|
2037
2474
|
const currentVersion = await getInstalledVersion("claude");
|
|
2038
|
-
const
|
|
2475
|
+
const installationInfo = await getClaudeCodeInstallationSource();
|
|
2476
|
+
const { isHomebrew, commandPath, source: installationSource } = installationInfo;
|
|
2477
|
+
let latestVersion;
|
|
2478
|
+
if (isHomebrew) {
|
|
2479
|
+
latestVersion = await getHomebrewClaudeCodeVersion();
|
|
2480
|
+
} else {
|
|
2481
|
+
latestVersion = await getLatestVersion("@anthropic-ai/claude-code");
|
|
2482
|
+
}
|
|
2039
2483
|
return {
|
|
2040
2484
|
installed: currentVersion !== null,
|
|
2041
2485
|
currentVersion,
|
|
2042
2486
|
latestVersion,
|
|
2043
|
-
needsUpdate: currentVersion && latestVersion ? shouldUpdate(currentVersion, latestVersion) : false
|
|
2487
|
+
needsUpdate: currentVersion && latestVersion ? shouldUpdate(currentVersion, latestVersion) : false,
|
|
2488
|
+
isHomebrew,
|
|
2489
|
+
commandPath,
|
|
2490
|
+
installationSource
|
|
2044
2491
|
};
|
|
2045
2492
|
}
|
|
2046
2493
|
async function checkCometixLineVersion() {
|
|
@@ -2120,7 +2567,7 @@ async function updateClaudeCode(force = false, skipPrompt = false) {
|
|
|
2120
2567
|
ensureI18nInitialized();
|
|
2121
2568
|
const spinner = ora(i18n.t("updater:checkingVersion")).start();
|
|
2122
2569
|
try {
|
|
2123
|
-
const { installed, currentVersion, latestVersion, needsUpdate } = await checkClaudeCodeVersion();
|
|
2570
|
+
const { installed, currentVersion, latestVersion, needsUpdate, isHomebrew } = await checkClaudeCodeVersion();
|
|
2124
2571
|
spinner.stop();
|
|
2125
2572
|
if (!installed) {
|
|
2126
2573
|
console.log(ansis.yellow(i18n.t("updater:claudeCodeNotInstalled")));
|
|
@@ -2148,9 +2595,14 @@ async function updateClaudeCode(force = false, skipPrompt = false) {
|
|
|
2148
2595
|
} else {
|
|
2149
2596
|
console.log(ansis.cyan(format(i18n.t("updater:autoUpdating"), { tool: "Claude Code" })));
|
|
2150
2597
|
}
|
|
2151
|
-
const
|
|
2598
|
+
const toolName = isHomebrew ? "Claude Code (Homebrew)" : "Claude Code";
|
|
2599
|
+
const updateSpinner = ora(format(i18n.t("updater:updating"), { tool: toolName })).start();
|
|
2152
2600
|
try {
|
|
2153
|
-
|
|
2601
|
+
if (isHomebrew) {
|
|
2602
|
+
await execAsync$2("brew upgrade --cask claude-code");
|
|
2603
|
+
} else {
|
|
2604
|
+
await execAsync$2("claude update");
|
|
2605
|
+
}
|
|
2154
2606
|
updateSpinner.succeed(format(i18n.t("updater:updateSuccess"), { tool: "Claude Code" }));
|
|
2155
2607
|
return true;
|
|
2156
2608
|
} catch (error) {
|
|
@@ -2217,6 +2669,15 @@ async function checkAndUpdateTools(skipPrompt = false) {
|
|
|
2217
2669
|
console.log(ansis.bold.cyan(`
|
|
2218
2670
|
\u{1F50D} ${i18n.t("updater:checkingTools")}
|
|
2219
2671
|
`));
|
|
2672
|
+
try {
|
|
2673
|
+
const duplicateResult = await handleDuplicateInstallations(skipPrompt);
|
|
2674
|
+
if (duplicateResult.hadDuplicates) {
|
|
2675
|
+
console.log();
|
|
2676
|
+
}
|
|
2677
|
+
} catch (error) {
|
|
2678
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2679
|
+
console.warn(ansis.yellow(`\u26A0 Duplicate installation check failed: ${errorMessage}`));
|
|
2680
|
+
}
|
|
2220
2681
|
const results = [];
|
|
2221
2682
|
try {
|
|
2222
2683
|
const success = await updateCcr(false, skipPrompt);
|
|
@@ -2962,7 +3423,7 @@ async function configureCodexMcp(options) {
|
|
|
2962
3423
|
command: serviceConfig.command,
|
|
2963
3424
|
args: serviceConfig.args,
|
|
2964
3425
|
env: Object.keys(env).length > 0 ? env : void 0,
|
|
2965
|
-
|
|
3426
|
+
startup_timeout_sec: 30
|
|
2966
3427
|
});
|
|
2967
3428
|
}
|
|
2968
3429
|
const mergedMap = /* @__PURE__ */ new Map();
|
|
@@ -3007,15 +3468,52 @@ function getRootDir$1() {
|
|
|
3007
3468
|
}
|
|
3008
3469
|
return dirname(currentFilePath);
|
|
3009
3470
|
}
|
|
3471
|
+
async function detectCodexInstallMethod() {
|
|
3472
|
+
try {
|
|
3473
|
+
const brewResult = await x("brew", ["list", "--cask", "codex"], { throwOnError: false });
|
|
3474
|
+
if (brewResult.exitCode === 0) {
|
|
3475
|
+
return "homebrew";
|
|
3476
|
+
}
|
|
3477
|
+
} catch {
|
|
3478
|
+
}
|
|
3479
|
+
try {
|
|
3480
|
+
const npmResult = await x("npm", ["list", "-g", "@openai/codex"], { throwOnError: false });
|
|
3481
|
+
if (npmResult.exitCode === 0 && npmResult.stdout.includes("@openai/codex")) {
|
|
3482
|
+
return "npm";
|
|
3483
|
+
}
|
|
3484
|
+
} catch {
|
|
3485
|
+
}
|
|
3486
|
+
return "unknown";
|
|
3487
|
+
}
|
|
3010
3488
|
async function executeCodexInstallation(isUpdate, skipMethodSelection = false) {
|
|
3011
3489
|
if (isUpdate) {
|
|
3012
3490
|
console.log(ansis.cyan(i18n.t("codex:updatingCli")));
|
|
3013
|
-
const
|
|
3014
|
-
if (
|
|
3015
|
-
console.log(ansis.
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3491
|
+
const installMethod = await detectCodexInstallMethod();
|
|
3492
|
+
if (installMethod === "homebrew") {
|
|
3493
|
+
console.log(ansis.gray(i18n.t("codex:detectedHomebrew")));
|
|
3494
|
+
const result = await x("brew", ["upgrade", "--cask", "codex"]);
|
|
3495
|
+
if (result.exitCode !== 0) {
|
|
3496
|
+
throw new Error(`Failed to update codex via Homebrew: exit code ${result.exitCode}`);
|
|
3497
|
+
}
|
|
3498
|
+
} else if (installMethod === "npm") {
|
|
3499
|
+
console.log(ansis.gray(i18n.t("codex:detectedNpm")));
|
|
3500
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@openai/codex@latest"]);
|
|
3501
|
+
if (usedSudo)
|
|
3502
|
+
console.log(ansis.yellow(i18n.t("codex:usingSudo")));
|
|
3503
|
+
const result = await x(command, args);
|
|
3504
|
+
if (result.exitCode !== 0) {
|
|
3505
|
+
throw new Error(`Failed to update codex CLI: exit code ${result.exitCode}`);
|
|
3506
|
+
}
|
|
3507
|
+
} else {
|
|
3508
|
+
console.log(ansis.yellow(i18n.t("codex:unknownInstallMethod")));
|
|
3509
|
+
console.log(ansis.gray(i18n.t("codex:fallingBackToNpm")));
|
|
3510
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@openai/codex@latest"]);
|
|
3511
|
+
if (usedSudo)
|
|
3512
|
+
console.log(ansis.yellow(i18n.t("codex:usingSudo")));
|
|
3513
|
+
const result = await x(command, args);
|
|
3514
|
+
if (result.exitCode !== 0) {
|
|
3515
|
+
throw new Error(`Failed to update codex CLI: exit code ${result.exitCode}`);
|
|
3516
|
+
}
|
|
3019
3517
|
}
|
|
3020
3518
|
console.log(ansis.green(i18n.t("codex:updateSuccess")));
|
|
3021
3519
|
} else {
|
|
@@ -3100,9 +3598,91 @@ function backupCodexPrompts() {
|
|
|
3100
3598
|
function getBackupMessage(path) {
|
|
3101
3599
|
if (!path)
|
|
3102
3600
|
return "";
|
|
3103
|
-
|
|
3601
|
+
if (!i18n.isInitialized) {
|
|
3602
|
+
return `Backup created: ${path}`;
|
|
3603
|
+
}
|
|
3104
3604
|
return i18n.t("codex:backupSuccess", { path });
|
|
3105
3605
|
}
|
|
3606
|
+
function needsEnvKeyMigration() {
|
|
3607
|
+
if (!exists(CODEX_CONFIG_FILE))
|
|
3608
|
+
return false;
|
|
3609
|
+
try {
|
|
3610
|
+
const content = readFile(CODEX_CONFIG_FILE);
|
|
3611
|
+
const hasOldEnvKey = /^\s*env_key\s*=/m.test(content);
|
|
3612
|
+
return hasOldEnvKey;
|
|
3613
|
+
} catch {
|
|
3614
|
+
return false;
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
function migrateEnvKeyToTempEnvKey() {
|
|
3618
|
+
if (!exists(CODEX_CONFIG_FILE))
|
|
3619
|
+
return false;
|
|
3620
|
+
try {
|
|
3621
|
+
const content = readFile(CODEX_CONFIG_FILE);
|
|
3622
|
+
if (!needsEnvKeyMigration())
|
|
3623
|
+
return false;
|
|
3624
|
+
const backupPath = backupCodexConfig();
|
|
3625
|
+
if (backupPath) {
|
|
3626
|
+
console.log(ansis.gray(getBackupMessage(backupPath)));
|
|
3627
|
+
}
|
|
3628
|
+
const migratedContent = migrateEnvKeyInContent(content);
|
|
3629
|
+
writeFile(CODEX_CONFIG_FILE, migratedContent);
|
|
3630
|
+
updateTomlConfig(ZCF_CONFIG_FILE, {
|
|
3631
|
+
codex: {
|
|
3632
|
+
envKeyMigrated: true
|
|
3633
|
+
}
|
|
3634
|
+
});
|
|
3635
|
+
const message = i18n.isInitialized ? i18n.t("codex:envKeyMigrationComplete") : "\u2714 env_key to temp_env_key migration completed";
|
|
3636
|
+
console.log(ansis.green(message));
|
|
3637
|
+
return true;
|
|
3638
|
+
} catch (error) {
|
|
3639
|
+
console.error(ansis.yellow(`env_key migration warning: ${error.message}`));
|
|
3640
|
+
return false;
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
function migrateEnvKeyInContent(content) {
|
|
3644
|
+
const lines = content.split("\n");
|
|
3645
|
+
const result = [];
|
|
3646
|
+
let currentSectionHasTempEnvKey = false;
|
|
3647
|
+
let currentSection = "";
|
|
3648
|
+
const sectionHasTempEnvKey = /* @__PURE__ */ new Map();
|
|
3649
|
+
let tempSection = "";
|
|
3650
|
+
for (const line of lines) {
|
|
3651
|
+
const sectionMatch = line.match(/^\s*\[([^\]]+)\]/);
|
|
3652
|
+
if (sectionMatch) {
|
|
3653
|
+
tempSection = sectionMatch[1];
|
|
3654
|
+
}
|
|
3655
|
+
if (tempSection && /^\s*temp_env_key\s*=/.test(line)) {
|
|
3656
|
+
sectionHasTempEnvKey.set(tempSection, true);
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
for (const line of lines) {
|
|
3660
|
+
const sectionMatch = line.match(/^\s*\[([^\]]+)\]/);
|
|
3661
|
+
if (sectionMatch) {
|
|
3662
|
+
currentSection = sectionMatch[1];
|
|
3663
|
+
currentSectionHasTempEnvKey = sectionHasTempEnvKey.get(currentSection) || false;
|
|
3664
|
+
}
|
|
3665
|
+
const envKeyMatch = line.match(/^(\s*)env_key(\s*=.*)$/);
|
|
3666
|
+
if (envKeyMatch) {
|
|
3667
|
+
if (currentSectionHasTempEnvKey) {
|
|
3668
|
+
continue;
|
|
3669
|
+
} else {
|
|
3670
|
+
result.push(`${envKeyMatch[1]}temp_env_key${envKeyMatch[2]}`);
|
|
3671
|
+
continue;
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
result.push(line);
|
|
3675
|
+
}
|
|
3676
|
+
return result.join("\n");
|
|
3677
|
+
}
|
|
3678
|
+
function ensureEnvKeyMigration() {
|
|
3679
|
+
const tomlConfig = readDefaultTomlConfig();
|
|
3680
|
+
if (tomlConfig?.codex?.envKeyMigrated)
|
|
3681
|
+
return;
|
|
3682
|
+
if (needsEnvKeyMigration()) {
|
|
3683
|
+
migrateEnvKeyToTempEnvKey();
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3106
3686
|
function sanitizeProviderName(input) {
|
|
3107
3687
|
const cleaned = input.trim();
|
|
3108
3688
|
if (!cleaned)
|
|
@@ -3135,7 +3715,7 @@ function parseCodexConfig(content) {
|
|
|
3135
3715
|
name: provider.name || id,
|
|
3136
3716
|
baseUrl: provider.base_url || "",
|
|
3137
3717
|
wireApi: provider.wire_api || "responses",
|
|
3138
|
-
|
|
3718
|
+
tempEnvKey: provider.temp_env_key || "OPENAI_API_KEY",
|
|
3139
3719
|
requiresOpenaiAuth: provider.requires_openai_auth !== false,
|
|
3140
3720
|
model: provider.model || void 0
|
|
3141
3721
|
// Parse model field from provider
|
|
@@ -3144,14 +3724,23 @@ function parseCodexConfig(content) {
|
|
|
3144
3724
|
}
|
|
3145
3725
|
const mcpServices = [];
|
|
3146
3726
|
if (tomlData.mcp_servers) {
|
|
3727
|
+
const KNOWN_MCP_FIELDS = /* @__PURE__ */ new Set(["command", "args", "env", "startup_timeout_sec"]);
|
|
3147
3728
|
for (const [id, mcpData] of Object.entries(tomlData.mcp_servers)) {
|
|
3148
3729
|
const mcp = mcpData;
|
|
3730
|
+
const extraFields = {};
|
|
3731
|
+
for (const [key, value] of Object.entries(mcp)) {
|
|
3732
|
+
if (!KNOWN_MCP_FIELDS.has(key)) {
|
|
3733
|
+
extraFields[key] = value;
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3149
3736
|
mcpServices.push({
|
|
3150
3737
|
id,
|
|
3151
3738
|
command: mcp.command || id,
|
|
3152
3739
|
args: mcp.args || [],
|
|
3153
3740
|
env: Object.keys(mcp.env || {}).length > 0 ? mcp.env : void 0,
|
|
3154
|
-
|
|
3741
|
+
startup_timeout_sec: mcp.startup_timeout_sec,
|
|
3742
|
+
// Only add extraFields if there are any extra fields
|
|
3743
|
+
extraFields: Object.keys(extraFields).length > 0 ? extraFields : void 0
|
|
3155
3744
|
});
|
|
3156
3745
|
}
|
|
3157
3746
|
}
|
|
@@ -3250,9 +3839,74 @@ function parseCodexConfig(content) {
|
|
|
3250
3839
|
};
|
|
3251
3840
|
}
|
|
3252
3841
|
}
|
|
3842
|
+
function formatInlineTableValue(value) {
|
|
3843
|
+
if (value === null || value === void 0) {
|
|
3844
|
+
return "";
|
|
3845
|
+
}
|
|
3846
|
+
if (typeof value === "string") {
|
|
3847
|
+
const normalized = normalizeTomlPath(value);
|
|
3848
|
+
return `'${normalized}'`;
|
|
3849
|
+
}
|
|
3850
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
3851
|
+
return String(value);
|
|
3852
|
+
}
|
|
3853
|
+
if (Array.isArray(value)) {
|
|
3854
|
+
const items = value.map((item) => {
|
|
3855
|
+
if (typeof item === "string") {
|
|
3856
|
+
const normalized = normalizeTomlPath(item);
|
|
3857
|
+
return `'${normalized}'`;
|
|
3858
|
+
}
|
|
3859
|
+
if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
3860
|
+
return formatInlineTable(item);
|
|
3861
|
+
}
|
|
3862
|
+
return String(item);
|
|
3863
|
+
}).join(", ");
|
|
3864
|
+
return `[${items}]`;
|
|
3865
|
+
}
|
|
3866
|
+
if (typeof value === "object") {
|
|
3867
|
+
return formatInlineTable(value);
|
|
3868
|
+
}
|
|
3869
|
+
return String(value);
|
|
3870
|
+
}
|
|
3871
|
+
function formatInlineTable(obj) {
|
|
3872
|
+
const entries = Object.entries(obj).filter(([_, v]) => v !== null && v !== void 0).map(([k, v]) => `${k} = ${formatInlineTableValue(v)}`).join(", ");
|
|
3873
|
+
return `{${entries}}`;
|
|
3874
|
+
}
|
|
3875
|
+
function formatTomlField(key, value) {
|
|
3876
|
+
if (value === null || value === void 0) {
|
|
3877
|
+
return "";
|
|
3878
|
+
}
|
|
3879
|
+
if (typeof value === "string") {
|
|
3880
|
+
const normalized = normalizeTomlPath(value);
|
|
3881
|
+
const escaped = normalized.replace(/"/g, '\\"');
|
|
3882
|
+
return `${key} = "${escaped}"`;
|
|
3883
|
+
}
|
|
3884
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
3885
|
+
return `${key} = ${value}`;
|
|
3886
|
+
}
|
|
3887
|
+
if (Array.isArray(value)) {
|
|
3888
|
+
const items = value.map((item) => {
|
|
3889
|
+
if (typeof item === "string") {
|
|
3890
|
+
const normalized = normalizeTomlPath(item);
|
|
3891
|
+
const escaped = normalized.replace(/"/g, '\\"');
|
|
3892
|
+
return `"${escaped}"`;
|
|
3893
|
+
}
|
|
3894
|
+
if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
3895
|
+
return formatInlineTable(item);
|
|
3896
|
+
}
|
|
3897
|
+
return String(item);
|
|
3898
|
+
}).join(", ");
|
|
3899
|
+
return `${key} = [${items}]`;
|
|
3900
|
+
}
|
|
3901
|
+
if (typeof value === "object") {
|
|
3902
|
+
return `${key} = ${formatInlineTable(value)}`;
|
|
3903
|
+
}
|
|
3904
|
+
return "";
|
|
3905
|
+
}
|
|
3253
3906
|
function readCodexConfig() {
|
|
3254
3907
|
if (!exists(CODEX_CONFIG_FILE))
|
|
3255
3908
|
return null;
|
|
3909
|
+
ensureEnvKeyMigration();
|
|
3256
3910
|
try {
|
|
3257
3911
|
const content = readFile(CODEX_CONFIG_FILE);
|
|
3258
3912
|
return parseCodexConfig(content);
|
|
@@ -3306,7 +3960,7 @@ function renderCodexConfig(data) {
|
|
|
3306
3960
|
lines.push(`name = "${provider.name}"`);
|
|
3307
3961
|
lines.push(`base_url = "${provider.baseUrl}"`);
|
|
3308
3962
|
lines.push(`wire_api = "${provider.wireApi}"`);
|
|
3309
|
-
lines.push(`
|
|
3963
|
+
lines.push(`temp_env_key = "${provider.tempEnvKey}"`);
|
|
3310
3964
|
lines.push(`requires_openai_auth = ${provider.requiresOpenaiAuth}`);
|
|
3311
3965
|
if (provider.model) {
|
|
3312
3966
|
lines.push(`model = "${provider.model}"`);
|
|
@@ -3326,8 +3980,16 @@ function renderCodexConfig(data) {
|
|
|
3326
3980
|
const envEntries = Object.entries(service.env).map(([key, value]) => `${key} = '${value}'`).join(", ");
|
|
3327
3981
|
lines.push(`env = {${envEntries}}`);
|
|
3328
3982
|
}
|
|
3329
|
-
if (service.
|
|
3330
|
-
lines.push(`
|
|
3983
|
+
if (service.startup_timeout_sec) {
|
|
3984
|
+
lines.push(`startup_timeout_sec = ${service.startup_timeout_sec}`);
|
|
3985
|
+
}
|
|
3986
|
+
if (service.extraFields) {
|
|
3987
|
+
for (const [key, value] of Object.entries(service.extraFields)) {
|
|
3988
|
+
const formatted = formatTomlField(key, value);
|
|
3989
|
+
if (formatted) {
|
|
3990
|
+
lines.push(formatted);
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3331
3993
|
}
|
|
3332
3994
|
lines.push("");
|
|
3333
3995
|
}
|
|
@@ -3342,6 +4004,7 @@ function renderCodexConfig(data) {
|
|
|
3342
4004
|
return result;
|
|
3343
4005
|
}
|
|
3344
4006
|
function writeCodexConfig(data) {
|
|
4007
|
+
ensureEnvKeyMigration();
|
|
3345
4008
|
ensureDir(CODEX_DIR);
|
|
3346
4009
|
writeFile(CODEX_CONFIG_FILE, renderCodexConfig(data));
|
|
3347
4010
|
}
|
|
@@ -3353,26 +4016,46 @@ function writeAuthFile(newEntries) {
|
|
|
3353
4016
|
}
|
|
3354
4017
|
async function isCodexInstalled$1() {
|
|
3355
4018
|
try {
|
|
3356
|
-
const
|
|
3357
|
-
if (
|
|
3358
|
-
return
|
|
4019
|
+
const npmResult = await x("npm", ["list", "-g", "--depth=0"]);
|
|
4020
|
+
if (npmResult.exitCode === 0 && npmResult.stdout.includes("@openai/codex@")) {
|
|
4021
|
+
return true;
|
|
3359
4022
|
}
|
|
3360
|
-
return result.stdout.includes("@openai/codex@");
|
|
3361
4023
|
} catch {
|
|
3362
|
-
return false;
|
|
3363
4024
|
}
|
|
4025
|
+
try {
|
|
4026
|
+
const brewResult = await x("brew", ["list", "--cask", "codex"], { throwOnError: false });
|
|
4027
|
+
if (brewResult.exitCode === 0) {
|
|
4028
|
+
return true;
|
|
4029
|
+
}
|
|
4030
|
+
} catch {
|
|
4031
|
+
}
|
|
4032
|
+
return false;
|
|
3364
4033
|
}
|
|
3365
4034
|
async function getCodexVersion() {
|
|
3366
4035
|
try {
|
|
3367
|
-
const
|
|
3368
|
-
if (
|
|
3369
|
-
|
|
4036
|
+
const npmResult = await x("npm", ["list", "-g", "--depth=0"]);
|
|
4037
|
+
if (npmResult.exitCode === 0) {
|
|
4038
|
+
const match = npmResult.stdout.match(/@openai\/codex@(\S+)/);
|
|
4039
|
+
if (match) {
|
|
4040
|
+
return match[1];
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
} catch {
|
|
4044
|
+
}
|
|
4045
|
+
try {
|
|
4046
|
+
const brewResult = await x("brew", ["info", "--cask", "codex", "--json=v2"], { throwOnError: false });
|
|
4047
|
+
if (brewResult.exitCode === 0) {
|
|
4048
|
+
const info = JSON.parse(brewResult.stdout);
|
|
4049
|
+
if (info.casks && Array.isArray(info.casks) && info.casks.length > 0) {
|
|
4050
|
+
const cask = info.casks[0];
|
|
4051
|
+
if (cask.installed && typeof cask.installed === "string") {
|
|
4052
|
+
return cask.installed;
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
3370
4055
|
}
|
|
3371
|
-
const match = result.stdout.match(/@openai\/codex@(\S+)/);
|
|
3372
|
-
return match ? match[1] : null;
|
|
3373
4056
|
} catch {
|
|
3374
|
-
return null;
|
|
3375
4057
|
}
|
|
4058
|
+
return null;
|
|
3376
4059
|
}
|
|
3377
4060
|
async function checkCodexUpdate() {
|
|
3378
4061
|
try {
|
|
@@ -3457,8 +4140,8 @@ async function runCodexSystemPromptSelection(skipPrompt = false) {
|
|
|
3457
4140
|
const rootDir = getRootDir$1();
|
|
3458
4141
|
const templateRoot = join(rootDir, "templates", "codex");
|
|
3459
4142
|
const zcfConfig$1 = readZcfConfig();
|
|
3460
|
-
const { readDefaultTomlConfig } = await Promise.resolve().then(function () { return zcfConfig; });
|
|
3461
|
-
const tomlConfig =
|
|
4143
|
+
const { readDefaultTomlConfig: readDefaultTomlConfig2 } = await Promise.resolve().then(function () { return zcfConfig; });
|
|
4144
|
+
const tomlConfig = readDefaultTomlConfig2();
|
|
3462
4145
|
const { resolveTemplateLanguage } = await Promise.resolve().then(function () { return prompts; });
|
|
3463
4146
|
const preferredLang = await resolveTemplateLanguage(
|
|
3464
4147
|
void 0,
|
|
@@ -3518,9 +4201,9 @@ async function runCodexSystemPromptSelection(skipPrompt = false) {
|
|
|
3518
4201
|
}
|
|
3519
4202
|
writeFile(CODEX_AGENTS_FILE, content);
|
|
3520
4203
|
try {
|
|
3521
|
-
const { updateTomlConfig } = await Promise.resolve().then(function () { return zcfConfig; });
|
|
3522
|
-
const { ZCF_CONFIG_FILE } = await Promise.resolve().then(function () { return constants; });
|
|
3523
|
-
|
|
4204
|
+
const { updateTomlConfig: updateTomlConfig2 } = await Promise.resolve().then(function () { return zcfConfig; });
|
|
4205
|
+
const { ZCF_CONFIG_FILE: ZCF_CONFIG_FILE2 } = await Promise.resolve().then(function () { return constants; });
|
|
4206
|
+
updateTomlConfig2(ZCF_CONFIG_FILE2, {
|
|
3524
4207
|
codex: {
|
|
3525
4208
|
systemPromptStyle: systemPrompt
|
|
3526
4209
|
}
|
|
@@ -3679,7 +4362,7 @@ async function applyCustomApiConfig(customApiConfig) {
|
|
|
3679
4362
|
name: providerName,
|
|
3680
4363
|
baseUrl: baseUrl || "https://api.anthropic.com",
|
|
3681
4364
|
wireApi: "claude",
|
|
3682
|
-
|
|
4365
|
+
tempEnvKey: `${providerId.toUpperCase()}_API_KEY`,
|
|
3683
4366
|
requiresOpenaiAuth: false
|
|
3684
4367
|
});
|
|
3685
4368
|
if (token) {
|
|
@@ -3870,7 +4553,7 @@ async function configureCodexApi(options) {
|
|
|
3870
4553
|
}
|
|
3871
4554
|
}
|
|
3872
4555
|
const providerId = sanitizeProviderName(answers.providerName);
|
|
3873
|
-
const
|
|
4556
|
+
const tempEnvKey = `${providerId.toUpperCase().replace(/-/g, "_")}_API_KEY`;
|
|
3874
4557
|
const existingProvider = existingMap.get(providerId);
|
|
3875
4558
|
const sessionProvider = currentSessionProviders.get(providerId);
|
|
3876
4559
|
if (existingProvider || sessionProvider) {
|
|
@@ -3900,14 +4583,14 @@ async function configureCodexApi(options) {
|
|
|
3900
4583
|
name: answers.providerName,
|
|
3901
4584
|
baseUrl: selectedProvider2 === "custom" ? answers.baseUrl : prefilledBaseUrl,
|
|
3902
4585
|
wireApi: selectedProvider2 === "custom" ? answers.wireApi || "responses" : prefilledWireApi,
|
|
3903
|
-
|
|
4586
|
+
tempEnvKey,
|
|
3904
4587
|
requiresOpenaiAuth: true,
|
|
3905
4588
|
model: customModel || prefilledModel || "gpt-5-codex"
|
|
3906
4589
|
// Use custom model, provider's default model, or fallback
|
|
3907
4590
|
};
|
|
3908
4591
|
providers.push(newProvider);
|
|
3909
4592
|
currentSessionProviders.set(providerId, newProvider);
|
|
3910
|
-
authEntries[
|
|
4593
|
+
authEntries[tempEnvKey] = answers.apiKey;
|
|
3911
4594
|
const addAnother = await promptBoolean({
|
|
3912
4595
|
message: i18n.t("codex:addProviderPrompt"),
|
|
3913
4596
|
defaultValue: false
|
|
@@ -3927,8 +4610,8 @@ async function configureCodexApi(options) {
|
|
|
3927
4610
|
}]);
|
|
3928
4611
|
const selectedProvider = providers.find((provider) => provider.id === defaultProvider);
|
|
3929
4612
|
if (selectedProvider) {
|
|
3930
|
-
const
|
|
3931
|
-
const defaultApiKey = authEntries[
|
|
4613
|
+
const tempEnvKey = selectedProvider.tempEnvKey;
|
|
4614
|
+
const defaultApiKey = authEntries[tempEnvKey] ?? existingAuth[tempEnvKey] ?? null;
|
|
3932
4615
|
if (defaultApiKey)
|
|
3933
4616
|
authEntries.OPENAI_API_KEY = defaultApiKey;
|
|
3934
4617
|
}
|
|
@@ -4213,7 +4896,7 @@ async function switchToProvider(providerId) {
|
|
|
4213
4896
|
};
|
|
4214
4897
|
writeCodexConfig(updatedConfig);
|
|
4215
4898
|
const auth = readJsonConfig(CODEX_AUTH_FILE, { defaultValue: {} }) || {};
|
|
4216
|
-
const envValue = auth[provider.
|
|
4899
|
+
const envValue = auth[provider.tempEnvKey] || null;
|
|
4217
4900
|
auth.OPENAI_API_KEY = envValue;
|
|
4218
4901
|
writeJsonConfig(CODEX_AUTH_FILE, auth, { pretty: true });
|
|
4219
4902
|
console.log(ansis.green(i18n.t("codex:providerSwitchSuccess", { provider: providerId })));
|
|
@@ -4237,11 +4920,15 @@ const codex = {
|
|
|
4237
4920
|
configureCodexApi: configureCodexApi,
|
|
4238
4921
|
configureCodexMcp: configureCodexMcp,
|
|
4239
4922
|
createBackupDirectory: createBackupDirectory,
|
|
4923
|
+
ensureEnvKeyMigration: ensureEnvKeyMigration,
|
|
4240
4924
|
getBackupMessage: getBackupMessage,
|
|
4241
4925
|
getCodexVersion: getCodexVersion,
|
|
4242
4926
|
installCodexCli: installCodexCli,
|
|
4243
4927
|
isCodexInstalled: isCodexInstalled$1,
|
|
4244
4928
|
listCodexProviders: listCodexProviders,
|
|
4929
|
+
migrateEnvKeyInContent: migrateEnvKeyInContent,
|
|
4930
|
+
migrateEnvKeyToTempEnvKey: migrateEnvKeyToTempEnvKey,
|
|
4931
|
+
needsEnvKeyMigration: needsEnvKeyMigration,
|
|
4245
4932
|
parseCodexConfig: parseCodexConfig,
|
|
4246
4933
|
readCodexConfig: readCodexConfig,
|
|
4247
4934
|
renderCodexConfig: renderCodexConfig,
|
|
@@ -4851,6 +5538,10 @@ async function installClaudeCode(skipMethodSelection = false) {
|
|
|
4851
5538
|
if (version) {
|
|
4852
5539
|
console.log(ansis.gray(` ${i18n.t("installation:detectedVersion", { version })}`));
|
|
4853
5540
|
}
|
|
5541
|
+
const verification = await verifyInstallation(codeType);
|
|
5542
|
+
if (verification.symlinkCreated) {
|
|
5543
|
+
displayVerificationResult(verification, codeType);
|
|
5544
|
+
}
|
|
4854
5545
|
await updateClaudeCode();
|
|
4855
5546
|
return;
|
|
4856
5547
|
}
|
|
@@ -4873,13 +5564,15 @@ async function installClaudeCode(skipMethodSelection = false) {
|
|
|
4873
5564
|
if (skipMethodSelection) {
|
|
4874
5565
|
console.log(i18n.t("installation:installing"));
|
|
4875
5566
|
try {
|
|
4876
|
-
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
|
|
5567
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@anthropic-ai/claude-code", "--force"]);
|
|
4877
5568
|
if (usedSudo) {
|
|
4878
5569
|
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:usingSudo")}`));
|
|
4879
5570
|
}
|
|
4880
5571
|
await exec(command, args);
|
|
4881
5572
|
console.log(`\u2714 ${i18n.t("installation:installSuccess")}`);
|
|
4882
5573
|
await setInstallMethod("npm");
|
|
5574
|
+
const verification = await verifyInstallation(codeType);
|
|
5575
|
+
displayVerificationResult(verification, codeType);
|
|
4883
5576
|
if (isTermux()) {
|
|
4884
5577
|
console.log(ansis.gray(`
|
|
4885
5578
|
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
@@ -4940,12 +5633,14 @@ async function installCodex(skipMethodSelection = false) {
|
|
|
4940
5633
|
if (skipMethodSelection) {
|
|
4941
5634
|
console.log(i18n.t("installation:installingWith", { method: "npm", codeType: codeTypeName }));
|
|
4942
5635
|
try {
|
|
4943
|
-
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@openai/codex"]);
|
|
5636
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@openai/codex", "--force"]);
|
|
4944
5637
|
if (usedSudo) {
|
|
4945
5638
|
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:usingSudo")}`));
|
|
4946
5639
|
}
|
|
4947
5640
|
await exec(command, args);
|
|
4948
5641
|
console.log(ansis.green(`\u2714 ${codeTypeName} ${i18n.t("installation:installSuccess")}`));
|
|
5642
|
+
const verification = await verifyInstallation(codeType);
|
|
5643
|
+
displayVerificationResult(verification, codeType);
|
|
4949
5644
|
} catch (error) {
|
|
4950
5645
|
console.error(ansis.red(`\u2716 ${codeTypeName} ${i18n.t("installation:installFailed")}`));
|
|
4951
5646
|
throw error;
|
|
@@ -5023,7 +5718,7 @@ async function uninstallCodeTool(codeType) {
|
|
|
5023
5718
|
}
|
|
5024
5719
|
} else if (codeType === "codex") {
|
|
5025
5720
|
try {
|
|
5026
|
-
const result = await exec("brew", ["list", "codex"]);
|
|
5721
|
+
const result = await exec("brew", ["list", "--cask", "codex"]);
|
|
5027
5722
|
if (result.exitCode === 0) {
|
|
5028
5723
|
method = "homebrew";
|
|
5029
5724
|
}
|
|
@@ -5038,7 +5733,7 @@ async function uninstallCodeTool(codeType) {
|
|
|
5038
5733
|
const platform = getPlatform();
|
|
5039
5734
|
if (platform === "macos" || platform === "linux") {
|
|
5040
5735
|
try {
|
|
5041
|
-
const testResult = codeType === "claude-code" ? await exec("brew", ["list", "--cask", "claude-code"]) : await exec("brew", ["list", "codex"]);
|
|
5736
|
+
const testResult = codeType === "claude-code" ? await exec("brew", ["list", "--cask", "claude-code"]) : await exec("brew", ["list", "--cask", "codex"]);
|
|
5042
5737
|
if (testResult.exitCode === 0) {
|
|
5043
5738
|
method = "homebrew";
|
|
5044
5739
|
}
|
|
@@ -5052,7 +5747,8 @@ async function uninstallCodeTool(codeType) {
|
|
|
5052
5747
|
const spinner = ora(i18n.t("installation:uninstallingWith", { method, codeType: codeTypeName })).start();
|
|
5053
5748
|
try {
|
|
5054
5749
|
switch (method) {
|
|
5055
|
-
case "npm":
|
|
5750
|
+
case "npm":
|
|
5751
|
+
case "npm-global": {
|
|
5056
5752
|
const packageName = codeType === "claude-code" ? "@anthropic-ai/claude-code" : "@openai/codex";
|
|
5057
5753
|
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["uninstall", "-g", packageName]);
|
|
5058
5754
|
if (usedSudo) {
|
|
@@ -5066,7 +5762,7 @@ async function uninstallCodeTool(codeType) {
|
|
|
5066
5762
|
if (codeType === "claude-code") {
|
|
5067
5763
|
await exec("brew", ["uninstall", "--cask", "claude-code"]);
|
|
5068
5764
|
} else {
|
|
5069
|
-
await exec("brew", ["uninstall", "codex"]);
|
|
5765
|
+
await exec("brew", ["uninstall", "--cask", "codex"]);
|
|
5070
5766
|
}
|
|
5071
5767
|
break;
|
|
5072
5768
|
}
|
|
@@ -5110,7 +5806,7 @@ async function uninstallCodeTool(codeType) {
|
|
|
5110
5806
|
return false;
|
|
5111
5807
|
}
|
|
5112
5808
|
}
|
|
5113
|
-
async function setInstallMethod(method
|
|
5809
|
+
async function setInstallMethod(method, codeType = "claude-code") {
|
|
5114
5810
|
try {
|
|
5115
5811
|
if (codeType === "claude-code") {
|
|
5116
5812
|
const { readMcpConfig, writeMcpConfig } = await Promise.resolve().then(function () { return claudeConfig; });
|
|
@@ -5118,7 +5814,7 @@ async function setInstallMethod(method = "npm", codeType = "claude-code") {
|
|
|
5118
5814
|
if (!config) {
|
|
5119
5815
|
config = { mcpServers: {} };
|
|
5120
5816
|
}
|
|
5121
|
-
config.installMethod = method === "npm" ? "npm" :
|
|
5817
|
+
config.installMethod = method === "npm" ? "npm-global" : method;
|
|
5122
5818
|
writeMcpConfig(config);
|
|
5123
5819
|
}
|
|
5124
5820
|
} catch (error) {
|
|
@@ -5137,6 +5833,22 @@ async function detectInstalledVersion(codeType) {
|
|
|
5137
5833
|
}
|
|
5138
5834
|
return null;
|
|
5139
5835
|
}
|
|
5836
|
+
function getInstallMethodLabel(method) {
|
|
5837
|
+
switch (method) {
|
|
5838
|
+
case "npm":
|
|
5839
|
+
return i18n.t("installation:installMethodNpm");
|
|
5840
|
+
case "homebrew":
|
|
5841
|
+
return i18n.t("installation:installMethodHomebrew");
|
|
5842
|
+
case "curl":
|
|
5843
|
+
return i18n.t("installation:installMethodCurl");
|
|
5844
|
+
case "powershell":
|
|
5845
|
+
return i18n.t("installation:installMethodPowershell");
|
|
5846
|
+
case "cmd":
|
|
5847
|
+
return i18n.t("installation:installMethodCmd");
|
|
5848
|
+
default:
|
|
5849
|
+
return method;
|
|
5850
|
+
}
|
|
5851
|
+
}
|
|
5140
5852
|
function getInstallMethodOptions(codeType, recommendedMethods) {
|
|
5141
5853
|
const allMethods = ["npm", "homebrew", "curl", "powershell", "cmd"];
|
|
5142
5854
|
const platform = getPlatform();
|
|
@@ -5155,7 +5867,8 @@ function getInstallMethodOptions(codeType, recommendedMethods) {
|
|
|
5155
5867
|
const topRecommended = recommendedMethods.length > 0 ? recommendedMethods[0] : null;
|
|
5156
5868
|
return availableMethods.map((method) => {
|
|
5157
5869
|
const isTopRecommended = method === topRecommended;
|
|
5158
|
-
const
|
|
5870
|
+
const methodLabel = getInstallMethodLabel(method);
|
|
5871
|
+
const title = isTopRecommended ? `${methodLabel} ${ansis.green(`[${i18n.t("installation:recommendedMethod")}]`)}` : methodLabel;
|
|
5159
5872
|
return {
|
|
5160
5873
|
title,
|
|
5161
5874
|
value: method
|
|
@@ -5190,7 +5903,7 @@ async function executeInstallMethod(method, codeType) {
|
|
|
5190
5903
|
switch (method) {
|
|
5191
5904
|
case "npm": {
|
|
5192
5905
|
const packageName = codeType === "claude-code" ? "@anthropic-ai/claude-code" : "@openai/codex";
|
|
5193
|
-
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", packageName]);
|
|
5906
|
+
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", packageName, "--force"]);
|
|
5194
5907
|
if (usedSudo) {
|
|
5195
5908
|
spinner.info(i18n.t("installation:usingSudo"));
|
|
5196
5909
|
spinner.start();
|
|
@@ -5203,7 +5916,7 @@ async function executeInstallMethod(method, codeType) {
|
|
|
5203
5916
|
if (codeType === "claude-code") {
|
|
5204
5917
|
await exec("brew", ["install", "--cask", "claude-code"]);
|
|
5205
5918
|
} else {
|
|
5206
|
-
await exec("brew", ["install", "codex"]);
|
|
5919
|
+
await exec("brew", ["install", "--cask", "codex"]);
|
|
5207
5920
|
}
|
|
5208
5921
|
await setInstallMethod("homebrew", codeType);
|
|
5209
5922
|
break;
|
|
@@ -5242,6 +5955,8 @@ async function executeInstallMethod(method, codeType) {
|
|
|
5242
5955
|
throw new Error(`Unsupported install method: ${method}`);
|
|
5243
5956
|
}
|
|
5244
5957
|
spinner.succeed(i18n.t("installation:installMethodSuccess", { method }));
|
|
5958
|
+
const verification = await verifyInstallation(codeType);
|
|
5959
|
+
displayVerificationResult(verification, codeType);
|
|
5245
5960
|
return true;
|
|
5246
5961
|
} catch (error) {
|
|
5247
5962
|
spinner.fail(i18n.t("installation:installMethodFailed", { method }));
|
|
@@ -5272,10 +5987,187 @@ async function handleInstallFailure(codeType, failedMethods) {
|
|
|
5272
5987
|
}
|
|
5273
5988
|
return await handleInstallFailure(codeType, [...failedMethods, newMethod]);
|
|
5274
5989
|
}
|
|
5990
|
+
async function isCommandInPath(command) {
|
|
5991
|
+
try {
|
|
5992
|
+
const cmd = getPlatform() === "windows" ? "where" : "which";
|
|
5993
|
+
const res = await exec(cmd, [command]);
|
|
5994
|
+
return res.exitCode === 0;
|
|
5995
|
+
} catch {
|
|
5996
|
+
return false;
|
|
5997
|
+
}
|
|
5998
|
+
}
|
|
5999
|
+
async function verifyInstallation(codeType) {
|
|
6000
|
+
const command = codeType === "claude-code" ? "claude" : "codex";
|
|
6001
|
+
const commandInPath = await isCommandInPath(command);
|
|
6002
|
+
if (commandInPath) {
|
|
6003
|
+
const version = await detectInstalledVersion(codeType);
|
|
6004
|
+
return {
|
|
6005
|
+
success: true,
|
|
6006
|
+
commandPath: await findCommandPath(command),
|
|
6007
|
+
version,
|
|
6008
|
+
needsSymlink: false,
|
|
6009
|
+
symlinkCreated: false
|
|
6010
|
+
};
|
|
6011
|
+
}
|
|
6012
|
+
if (getPlatform() === "macos") {
|
|
6013
|
+
const homebrewPaths = await getHomebrewCommandPaths(command);
|
|
6014
|
+
let foundPath = null;
|
|
6015
|
+
for (const path of homebrewPaths) {
|
|
6016
|
+
if (exists(path)) {
|
|
6017
|
+
foundPath = path;
|
|
6018
|
+
break;
|
|
6019
|
+
}
|
|
6020
|
+
}
|
|
6021
|
+
if (foundPath) {
|
|
6022
|
+
const symlinkResult = await createHomebrewSymlink(command, foundPath);
|
|
6023
|
+
if (symlinkResult.success) {
|
|
6024
|
+
const version = await detectInstalledVersion(codeType);
|
|
6025
|
+
return {
|
|
6026
|
+
success: true,
|
|
6027
|
+
commandPath: symlinkResult.symlinkPath,
|
|
6028
|
+
version,
|
|
6029
|
+
needsSymlink: true,
|
|
6030
|
+
symlinkCreated: true
|
|
6031
|
+
};
|
|
6032
|
+
}
|
|
6033
|
+
return {
|
|
6034
|
+
success: false,
|
|
6035
|
+
commandPath: foundPath,
|
|
6036
|
+
version: null,
|
|
6037
|
+
needsSymlink: true,
|
|
6038
|
+
symlinkCreated: false,
|
|
6039
|
+
error: symlinkResult.error
|
|
6040
|
+
};
|
|
6041
|
+
}
|
|
6042
|
+
}
|
|
6043
|
+
if (isTermux()) {
|
|
6044
|
+
const termuxPrefix = getTermuxPrefix();
|
|
6045
|
+
const termuxPaths = [
|
|
6046
|
+
`${termuxPrefix}/bin/${command}`,
|
|
6047
|
+
`${termuxPrefix}/usr/bin/${command}`
|
|
6048
|
+
];
|
|
6049
|
+
for (const path of termuxPaths) {
|
|
6050
|
+
if (exists(path)) {
|
|
6051
|
+
const version = await detectInstalledVersion(codeType);
|
|
6052
|
+
return {
|
|
6053
|
+
success: true,
|
|
6054
|
+
commandPath: path,
|
|
6055
|
+
version,
|
|
6056
|
+
needsSymlink: false,
|
|
6057
|
+
symlinkCreated: false
|
|
6058
|
+
};
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
}
|
|
6062
|
+
return {
|
|
6063
|
+
success: false,
|
|
6064
|
+
commandPath: null,
|
|
6065
|
+
version: null,
|
|
6066
|
+
needsSymlink: false,
|
|
6067
|
+
symlinkCreated: false,
|
|
6068
|
+
error: "Command not found in any known location"
|
|
6069
|
+
};
|
|
6070
|
+
}
|
|
6071
|
+
async function createHomebrewSymlink(command, sourcePath) {
|
|
6072
|
+
const homebrewBinPaths = [
|
|
6073
|
+
"/opt/homebrew/bin",
|
|
6074
|
+
// Apple Silicon (M1/M2)
|
|
6075
|
+
"/usr/local/bin"
|
|
6076
|
+
// Intel Mac
|
|
6077
|
+
];
|
|
6078
|
+
let targetDir = null;
|
|
6079
|
+
for (const binPath of homebrewBinPaths) {
|
|
6080
|
+
if (nodeFs.existsSync(binPath)) {
|
|
6081
|
+
targetDir = binPath;
|
|
6082
|
+
break;
|
|
6083
|
+
}
|
|
6084
|
+
}
|
|
6085
|
+
if (!targetDir) {
|
|
6086
|
+
return {
|
|
6087
|
+
success: false,
|
|
6088
|
+
symlinkPath: null,
|
|
6089
|
+
error: "No suitable Homebrew bin directory found"
|
|
6090
|
+
};
|
|
6091
|
+
}
|
|
6092
|
+
const symlinkPath = join(targetDir, command);
|
|
6093
|
+
try {
|
|
6094
|
+
const stats = nodeFs.lstatSync(symlinkPath);
|
|
6095
|
+
if (stats.isSymbolicLink()) {
|
|
6096
|
+
const existingTarget = nodeFs.readlinkSync(symlinkPath);
|
|
6097
|
+
if (existingTarget === sourcePath) {
|
|
6098
|
+
return {
|
|
6099
|
+
success: true,
|
|
6100
|
+
symlinkPath
|
|
6101
|
+
};
|
|
6102
|
+
}
|
|
6103
|
+
nodeFs.unlinkSync(symlinkPath);
|
|
6104
|
+
} else {
|
|
6105
|
+
return {
|
|
6106
|
+
success: false,
|
|
6107
|
+
symlinkPath: null,
|
|
6108
|
+
error: `File already exists at ${symlinkPath} and is not a symlink`
|
|
6109
|
+
};
|
|
6110
|
+
}
|
|
6111
|
+
} catch (error) {
|
|
6112
|
+
if (error.code !== "ENOENT") {
|
|
6113
|
+
return {
|
|
6114
|
+
success: false,
|
|
6115
|
+
symlinkPath: null,
|
|
6116
|
+
error: `Failed to check existing file: ${error}`
|
|
6117
|
+
};
|
|
6118
|
+
}
|
|
6119
|
+
}
|
|
6120
|
+
try {
|
|
6121
|
+
nodeFs.symlinkSync(sourcePath, symlinkPath);
|
|
6122
|
+
return {
|
|
6123
|
+
success: true,
|
|
6124
|
+
symlinkPath
|
|
6125
|
+
};
|
|
6126
|
+
} catch (error) {
|
|
6127
|
+
if (error.code === "EACCES") {
|
|
6128
|
+
return {
|
|
6129
|
+
success: false,
|
|
6130
|
+
symlinkPath: null,
|
|
6131
|
+
error: `Permission denied. Try running: sudo ln -sf ${sourcePath} ${symlinkPath}`
|
|
6132
|
+
};
|
|
6133
|
+
}
|
|
6134
|
+
return {
|
|
6135
|
+
success: false,
|
|
6136
|
+
symlinkPath: null,
|
|
6137
|
+
error: `Failed to create symlink: ${error.message}`
|
|
6138
|
+
};
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
function displayVerificationResult(result, codeType) {
|
|
6142
|
+
ensureI18nInitialized();
|
|
6143
|
+
const codeTypeName = codeType === "claude-code" ? i18n.t("common:claudeCode") : i18n.t("common:codex");
|
|
6144
|
+
if (result.success) {
|
|
6145
|
+
if (result.symlinkCreated) {
|
|
6146
|
+
console.log(ansis.green(`\u2714 ${codeTypeName} ${i18n.t("installation:verificationSuccess")}`));
|
|
6147
|
+
console.log(ansis.gray(` ${i18n.t("installation:symlinkCreated", { path: result.commandPath })}`));
|
|
6148
|
+
}
|
|
6149
|
+
if (result.version) {
|
|
6150
|
+
console.log(ansis.gray(` ${i18n.t("installation:detectedVersion", { version: result.version })}`));
|
|
6151
|
+
}
|
|
6152
|
+
} else {
|
|
6153
|
+
console.log(ansis.yellow(`\u26A0 ${codeTypeName} ${i18n.t("installation:verificationFailed")}`));
|
|
6154
|
+
if (result.commandPath) {
|
|
6155
|
+
console.log(ansis.gray(` ${i18n.t("installation:foundAtPath", { path: result.commandPath })}`));
|
|
6156
|
+
}
|
|
6157
|
+
if (result.error) {
|
|
6158
|
+
console.log(ansis.gray(` ${result.error}`));
|
|
6159
|
+
}
|
|
6160
|
+
if (result.needsSymlink && !result.symlinkCreated) {
|
|
6161
|
+
console.log(ansis.yellow(` ${i18n.t("installation:manualSymlinkHint")}`));
|
|
6162
|
+
}
|
|
6163
|
+
}
|
|
6164
|
+
}
|
|
5275
6165
|
|
|
5276
6166
|
const installer = {
|
|
5277
6167
|
__proto__: null,
|
|
6168
|
+
createHomebrewSymlink: createHomebrewSymlink,
|
|
5278
6169
|
detectInstalledVersion: detectInstalledVersion,
|
|
6170
|
+
displayVerificationResult: displayVerificationResult,
|
|
5279
6171
|
executeInstallMethod: executeInstallMethod,
|
|
5280
6172
|
getInstallationStatus: getInstallationStatus,
|
|
5281
6173
|
handleInstallFailure: handleInstallFailure,
|
|
@@ -5287,7 +6179,8 @@ const installer = {
|
|
|
5287
6179
|
removeLocalClaudeCode: removeLocalClaudeCode,
|
|
5288
6180
|
selectInstallMethod: selectInstallMethod,
|
|
5289
6181
|
setInstallMethod: setInstallMethod,
|
|
5290
|
-
uninstallCodeTool: uninstallCodeTool
|
|
6182
|
+
uninstallCodeTool: uninstallCodeTool,
|
|
6183
|
+
verifyInstallation: verifyInstallation
|
|
5291
6184
|
};
|
|
5292
6185
|
|
|
5293
6186
|
async function chooseInstallationMethod() {
|
|
@@ -5869,6 +6762,17 @@ async function init(options = {}) {
|
|
|
5869
6762
|
console.log(ansis.green(`\u2714 ${i18n.t("installation:localInstallationRemoved")}`));
|
|
5870
6763
|
}
|
|
5871
6764
|
}
|
|
6765
|
+
const { verifyInstallation, displayVerificationResult } = await Promise.resolve().then(function () { return installer; });
|
|
6766
|
+
const verification = await verifyInstallation("claude-code");
|
|
6767
|
+
if (verification.symlinkCreated) {
|
|
6768
|
+
console.log(ansis.green(`\u2714 ${i18n.t("installation:alreadyInstalled")}`));
|
|
6769
|
+
displayVerificationResult(verification, "claude-code");
|
|
6770
|
+
} else if (!verification.success) {
|
|
6771
|
+
console.log(ansis.yellow(`\u26A0 ${i18n.t("installation:verificationFailed")}`));
|
|
6772
|
+
if (verification.error) {
|
|
6773
|
+
console.log(ansis.gray(` ${verification.error}`));
|
|
6774
|
+
}
|
|
6775
|
+
}
|
|
5872
6776
|
} else {
|
|
5873
6777
|
if (options.skipPrompt) {
|
|
5874
6778
|
await installClaudeCode(true);
|
|
@@ -5940,18 +6844,21 @@ async function init(options = {}) {
|
|
|
5940
6844
|
options.apiModel = options.apiModel || preset.claudeCode.defaultModels[0];
|
|
5941
6845
|
options.apiFastModel = options.apiFastModel || preset.claudeCode.defaultModels[1];
|
|
5942
6846
|
}
|
|
6847
|
+
await saveSingleConfigToToml(apiConfig, options.provider, options);
|
|
5943
6848
|
} else if (options.apiType === "auth_token" && options.apiKey) {
|
|
5944
6849
|
apiConfig = {
|
|
5945
6850
|
authType: "auth_token",
|
|
5946
6851
|
key: options.apiKey,
|
|
5947
6852
|
url: options.apiUrl || API_DEFAULT_URL
|
|
5948
6853
|
};
|
|
6854
|
+
await saveSingleConfigToToml(apiConfig, void 0, options);
|
|
5949
6855
|
} else if (options.apiType === "api_key" && options.apiKey) {
|
|
5950
6856
|
apiConfig = {
|
|
5951
6857
|
authType: "api_key",
|
|
5952
6858
|
key: options.apiKey,
|
|
5953
6859
|
url: options.apiUrl || API_DEFAULT_URL
|
|
5954
6860
|
};
|
|
6861
|
+
await saveSingleConfigToToml(apiConfig, void 0, options);
|
|
5955
6862
|
} else if (options.apiType === "ccr_proxy") {
|
|
5956
6863
|
const ccrStatus = await isCcrInstalled();
|
|
5957
6864
|
if (!ccrStatus.hasCorrectPackage) {
|
|
@@ -6321,6 +7228,55 @@ async function handleCodexConfigs(configs) {
|
|
|
6321
7228
|
console.log(ansis.green(`\u2714 ${i18n.t("multi-config:defaultProviderSet", { name: defaultConfig.name })}`));
|
|
6322
7229
|
}
|
|
6323
7230
|
}
|
|
7231
|
+
async function saveSingleConfigToToml(apiConfig, provider, options) {
|
|
7232
|
+
try {
|
|
7233
|
+
const { ClaudeCodeConfigManager } = await import('./claude-code-config-manager.mjs');
|
|
7234
|
+
const profile = await convertSingleConfigToProfile(apiConfig, provider, options);
|
|
7235
|
+
const result = await ClaudeCodeConfigManager.addProfile(profile);
|
|
7236
|
+
if (result.success) {
|
|
7237
|
+
const savedProfile = result.addedProfile || ClaudeCodeConfigManager.getProfileByName(profile.name) || profile;
|
|
7238
|
+
if (savedProfile.id) {
|
|
7239
|
+
await ClaudeCodeConfigManager.switchProfile(savedProfile.id);
|
|
7240
|
+
await ClaudeCodeConfigManager.applyProfileSettings(savedProfile);
|
|
7241
|
+
}
|
|
7242
|
+
console.log(ansis.green(`\u2714 ${i18n.t("configuration:singleConfigSaved", { name: profile.name })}`));
|
|
7243
|
+
} else {
|
|
7244
|
+
console.warn(ansis.yellow(`${i18n.t("configuration:singleConfigSaveFailed")}: ${result.error}`));
|
|
7245
|
+
}
|
|
7246
|
+
} catch (error) {
|
|
7247
|
+
console.warn(ansis.yellow(`${i18n.t("configuration:singleConfigSaveFailed")}: ${error instanceof Error ? error.message : String(error)}`));
|
|
7248
|
+
}
|
|
7249
|
+
}
|
|
7250
|
+
async function convertSingleConfigToProfile(apiConfig, provider, options) {
|
|
7251
|
+
const { ClaudeCodeConfigManager } = await import('./claude-code-config-manager.mjs');
|
|
7252
|
+
const configName = provider && provider !== "custom" ? provider : "custom-config";
|
|
7253
|
+
let baseUrl = apiConfig.url || API_DEFAULT_URL;
|
|
7254
|
+
let primaryModel = options?.apiModel;
|
|
7255
|
+
let fastModel = options?.apiFastModel;
|
|
7256
|
+
let authType = apiConfig.authType;
|
|
7257
|
+
if (provider && provider !== "custom") {
|
|
7258
|
+
const { getProviderPreset } = await import('./api-providers.mjs');
|
|
7259
|
+
const preset = getProviderPreset(provider);
|
|
7260
|
+
if (preset?.claudeCode) {
|
|
7261
|
+
baseUrl = apiConfig.url || preset.claudeCode.baseUrl;
|
|
7262
|
+
authType = preset.claudeCode.authType;
|
|
7263
|
+
if (preset.claudeCode.defaultModels && preset.claudeCode.defaultModels.length >= 2) {
|
|
7264
|
+
primaryModel = primaryModel || preset.claudeCode.defaultModels[0];
|
|
7265
|
+
fastModel = fastModel || preset.claudeCode.defaultModels[1];
|
|
7266
|
+
}
|
|
7267
|
+
}
|
|
7268
|
+
}
|
|
7269
|
+
const profile = {
|
|
7270
|
+
name: configName,
|
|
7271
|
+
authType,
|
|
7272
|
+
apiKey: apiConfig.key,
|
|
7273
|
+
baseUrl,
|
|
7274
|
+
primaryModel,
|
|
7275
|
+
fastModel,
|
|
7276
|
+
id: ClaudeCodeConfigManager.generateProfileId(configName)
|
|
7277
|
+
};
|
|
7278
|
+
return profile;
|
|
7279
|
+
}
|
|
6324
7280
|
async function convertToClaudeCodeProfile(config) {
|
|
6325
7281
|
const { ClaudeCodeConfigManager } = await import('./claude-code-config-manager.mjs');
|
|
6326
7282
|
let baseUrl = config.url;
|
|
@@ -6368,7 +7324,7 @@ async function convertToCodexProvider(config) {
|
|
|
6368
7324
|
name: config.name,
|
|
6369
7325
|
baseUrl,
|
|
6370
7326
|
wireApi,
|
|
6371
|
-
|
|
7327
|
+
tempEnvKey: API_ENV_KEY,
|
|
6372
7328
|
requiresOpenaiAuth: false,
|
|
6373
7329
|
model
|
|
6374
7330
|
};
|
|
@@ -6452,4 +7408,4 @@ async function openSettingsJson() {
|
|
|
6452
7408
|
}
|
|
6453
7409
|
}
|
|
6454
7410
|
|
|
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,
|
|
7411
|
+
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, updatePromptOnly 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, writeAuthFile as aA, updateZcfConfig as aB, changeLanguage as aC, readZcfConfig as aD, configureOutputStyle as aE, isWindows as aF, selectMcpServices as aG, getMcpServices as aH, isCcrInstalled as aI, installCcr as aJ, setupCcrConfiguration as aK, modifyApiConfigPartially as aL, formatApiKeyDisplay as aM, readCcrConfig as aN, configureCcrFeature as aO, handleExitPromptError as aP, handleGeneralError as aQ, COMETIX_COMMAND_NAME as aR, COMETIX_COMMANDS as aS, installCometixLine as aT, checkAndUpdateTools as aU, runCodexUpdate as aV, resolveCodeType as aW, writeJsonConfig as aX, displayBanner as aY, version as aZ, resolveAiOutputLanguage as a_, removeLocalClaudeCode as aa, uninstallCodeTool as ab, setInstallMethod as ac, detectInstalledVersion as ad, selectInstallMethod as ae, executeInstallMethod as af, handleInstallFailure as ag, verifyInstallation as ah, createHomebrewSymlink as ai, displayVerificationResult as aj, ensureI18nInitialized as ak, i18n as al, addNumbersToChoices as am, validateApiKey as an, promptBoolean as ao, ensureDir as ap, readDefaultTomlConfig as aq, createDefaultTomlConfig as ar, exists as as, readJsonConfig as at, writeTomlConfig as au, copyFile as av, detectConfigManagementMode as aw, readCodexConfig as ax, backupCodexComplete as ay, writeCodexConfig as az, importRecommendedEnv as b, selectAndInstallWorkflows as b0, checkClaudeCodeVersionAndPrompt as b1, displayBannerWithInfo as b2, runCodexUninstall as b3, configureCodexMcp as b4, configureCodexApi as b5, runCodexWorkflowImportWithLanguageSelection as b6, runCodexFullInit as b7, switchCodexProvider as b8, listCodexProviders as b9, switchToOfficialLogin as ba, switchToProvider as bb, readZcfConfigAsync as bc, initI18n as bd, selectScriptLanguage as be, index as bf, fsOperations as bg, jsonConfig as bh, claudeConfig as bi, config$1 as bj, config as bk, prompts as bl, codex as bm, installer as bn, 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 };
|