zcf
Version:
Zero-Config Code Flow - One-click configuration tool for Code Cli
641 lines (638 loc) • 24.6 kB
JavaScript
import ansis from 'ansis';
import inquirer from 'inquirer';
import { al as i18n, ak as ensureI18nInitialized, x as SUPPORTED_LANGS, am as addNumbersToChoices, y as LANG_LABELS, aC as updateZcfConfig, aD as changeLanguage, aE as readZcfConfig, ao as promptBoolean, o as openSettingsJson, d as importRecommendedPermissions, b as importRecommendedEnv, a1 as applyAiLanguageDirective, aF as configureOutputStyle, $ as getExistingModelConfig, X as updateCustomModel, Y as updateDefaultModel, aG as isWindows, F as readMcpConfig, K as fixWindowsMcpConfig, G as writeMcpConfig, aH as selectMcpServices, H as backupMcpConfig, aI as getMcpServices, J as buildMcpServerConfig, I as mergeMcpServers, aJ as isCcrInstalled, aK as installCcr, aL as setupCcrConfiguration, a0 as getExistingApiConfig, a3 as promptApiConfigurationAction, aM as modifyApiConfigPartially, an as validateApiKey, V as configureApi, aN as formatApiKeyDisplay, a2 as switchToOfficialLogin } from './simple-config.mjs';
import 'node:fs';
import 'node:process';
import 'node:child_process';
import 'node:os';
import 'node:util';
import 'dayjs';
import 'pathe';
import 'node:url';
import 'inquirer-toggle';
import 'ora';
import 'tinyexec';
import 'semver';
import 'smol-toml';
import 'node:fs/promises';
import 'i18next';
import 'i18next-fs-backend';
async function handleCancellation() {
ensureI18nInitialized();
console.log(ansis.yellow(i18n.t("common:cancelled")));
}
async function handleOfficialLoginMode() {
ensureI18nInitialized();
const success = switchToOfficialLogin();
if (success) {
console.log(ansis.green(`\u2714 ${i18n.t("api:officialLoginConfigured")}`));
} else {
console.log(ansis.red(i18n.t("api:officialLoginFailed")));
}
}
async function handleCustomApiMode() {
ensureI18nInitialized();
const zcfConfig = readZcfConfig();
const codeToolType = zcfConfig?.codeToolType || "claude-code";
if (codeToolType === "claude-code") {
const { configureIncrementalManagement } = await import('./claude-code-incremental-manager.mjs');
await configureIncrementalManagement();
return;
}
const existingConfig = getExistingApiConfig();
if (existingConfig) {
const configAction = await promptApiConfigurationAction();
if (configAction === "keep-existing") {
console.log(ansis.green(`\u2714 ${i18n.t("api:keepExistingConfig")}`));
return;
} else if (configAction === "modify-partial") {
await modifyApiConfigPartially(existingConfig);
return;
}
}
const { apiChoice } = await inquirer.prompt({
type: "list",
name: "apiChoice",
message: i18n.t("api:configureApi"),
choices: addNumbersToChoices([
{
name: `${i18n.t("api:useAuthToken")} - ${ansis.gray(i18n.t("api:authTokenDesc"))}`,
value: "auth_token",
short: i18n.t("api:useAuthToken")
},
{
name: `${i18n.t("api:useApiKey")} - ${ansis.gray(i18n.t("api:apiKeyDesc"))}`,
value: "api_key",
short: i18n.t("api:useApiKey")
},
{ name: i18n.t("api:skipApi"), value: "skip" }
])
});
if (!apiChoice || apiChoice === "skip") {
await handleCancellation();
return;
}
const { url } = await inquirer.prompt({
type: "input",
name: "url",
message: `${i18n.t("api:enterApiUrl")}${i18n.t("common:emptyToSkip")}`,
validate: (value) => {
if (!value) {
return true;
}
try {
void new URL(value);
return true;
} catch {
return i18n.t("api:invalidUrl");
}
}
});
if (url === void 0 || !url) {
await handleCancellation();
return;
}
const keyMessage = apiChoice === "auth_token" ? `${i18n.t("api:enterAuthToken")}${i18n.t("common:emptyToSkip")}` : `${i18n.t("api:enterApiKey")}${i18n.t("common:emptyToSkip")}`;
const { key } = await inquirer.prompt({
type: "input",
name: "key",
message: keyMessage,
validate: (value) => {
if (!value) {
return true;
}
const validation = validateApiKey(value);
if (!validation.isValid) {
return validation.error || i18n.t("api:invalidKeyFormat");
}
return true;
}
});
if (key === void 0 || !key) {
await handleCancellation();
return;
}
const apiConfig = { url, key, authType: apiChoice };
const configuredApi = configureApi(apiConfig);
if (configuredApi) {
console.log(ansis.green(`\u2714 ${i18n.t("api:apiConfigSuccess")}`));
console.log(ansis.gray(` URL: ${configuredApi.url}`));
console.log(ansis.gray(` Key: ${formatApiKeyDisplay(configuredApi.key)}`));
}
}
async function handleCcrProxyMode() {
ensureI18nInitialized();
const ccrStatus = await isCcrInstalled();
if (!ccrStatus.hasCorrectPackage) {
await installCcr();
} else {
console.log(ansis.green(`\u2714 ${i18n.t("ccr:ccrAlreadyInstalled")}`));
}
const ccrConfigured = await setupCcrConfiguration();
if (ccrConfigured) {
console.log(ansis.green(`\u2714 ${i18n.t("ccr:ccrSetupComplete")}`));
}
}
async function handleSwitchConfigMode() {
ensureI18nInitialized();
const { configSwitchCommand } = await import('../cli.mjs').then(function (n) { return n.c; });
await configSwitchCommand({ codeType: "claude-code" });
}
async function configureApiFeature() {
ensureI18nInitialized();
const { mode } = await inquirer.prompt({
type: "list",
name: "mode",
message: i18n.t("api:apiModePrompt"),
choices: addNumbersToChoices([
{ name: i18n.t("api:apiModeOfficial"), value: "official" },
{ name: i18n.t("api:apiModeCustom"), value: "custom" },
{ name: i18n.t("api:apiModeCcr"), value: "ccr" },
{ name: i18n.t("api:apiModeSwitch"), value: "switch" },
{ name: i18n.t("api:apiModeSkip"), value: "skip" }
])
});
if (!mode || mode === "skip") {
await handleCancellation();
return;
}
switch (mode) {
case "official":
await handleOfficialLoginMode();
break;
case "custom":
await handleCustomApiMode();
break;
case "ccr":
await handleCcrProxyMode();
break;
case "switch":
await handleSwitchConfigMode();
break;
default:
await handleCancellation();
break;
}
}
async function configureMcpFeature() {
ensureI18nInitialized();
if (isWindows()) {
const fixWindows = await promptBoolean({
message: i18n.t("configuration:fixWindowsMcp") || "Fix Windows MCP configuration?",
defaultValue: true
});
if (fixWindows) {
const existingConfig = readMcpConfig() || { mcpServers: {} };
const fixedConfig = fixWindowsMcpConfig(existingConfig);
writeMcpConfig(fixedConfig);
console.log(ansis.green(`\u2714 ${i18n.t("configuration:windowsMcpConfigFixed")}`));
}
}
const selectedServices = await selectMcpServices();
if (!selectedServices) {
return;
}
if (selectedServices.length > 0) {
const mcpBackupPath = backupMcpConfig();
if (mcpBackupPath) {
console.log(ansis.gray(`\u2714 ${i18n.t("mcp:mcpBackupSuccess")}: ${mcpBackupPath}`));
}
const newServers = {};
for (const serviceId of selectedServices) {
const service = (await getMcpServices()).find((s) => s.id === serviceId);
if (!service)
continue;
let config = service.config;
if (service.requiresApiKey) {
const { apiKey } = await inquirer.prompt({
type: "input",
name: "apiKey",
message: service.apiKeyPrompt,
validate: async (value) => !!value || i18n.t("api:keyRequired")
});
if (apiKey) {
config = buildMcpServerConfig(service.config, apiKey, service.apiKeyPlaceholder, service.apiKeyEnvVar);
} else {
continue;
}
}
newServers[service.id] = config;
}
const existingConfig = readMcpConfig();
let mergedConfig = mergeMcpServers(existingConfig, newServers);
mergedConfig = fixWindowsMcpConfig(mergedConfig);
writeMcpConfig(mergedConfig);
console.log(ansis.green(`\u2714 ${i18n.t("mcp:mcpConfigSuccess")}`));
}
}
async function configureDefaultModelFeature() {
ensureI18nInitialized();
const existingModel = getExistingModelConfig();
if (existingModel) {
console.log(`
${ansis.blue(`\u2139 ${i18n.t("configuration:existingModelConfig") || "Existing model configuration"}`)}`);
const modelDisplay = existingModel === "default" ? i18n.t("configuration:defaultModelOption") || "Default (Let Claude Code choose)" : existingModel.charAt(0).toUpperCase() + existingModel.slice(1);
console.log(ansis.gray(` ${i18n.t("configuration:currentModel") || "Current model"}: ${modelDisplay}
`));
const modify = await promptBoolean({
message: i18n.t("configuration:modifyModel") || "Modify model configuration?",
defaultValue: false
});
if (!modify) {
console.log(ansis.green(`\u2714 ${i18n.t("configuration:keepModel") || "Keeping existing model configuration"}`));
return;
}
}
const { model } = await inquirer.prompt({
type: "list",
name: "model",
message: i18n.t("configuration:selectDefaultModel") || "Select default model",
choices: addNumbersToChoices([
{
name: i18n.t("configuration:defaultModelOption") || "Default - Let Claude Code choose",
value: "default"
},
{
name: i18n.t("configuration:opusModelOption") || "Opus - Only use opus, high token consumption, use with caution",
value: "opus"
},
{
name: i18n.t("configuration:sonnet1mModelOption") || "Sonnet 1M - 1M context version",
value: "sonnet[1m]"
},
{
name: i18n.t("configuration:customModelOption") || "Custom - Specify custom model names",
value: "custom"
}
]),
default: existingModel ? ["default", "opus", "sonnet[1m]", "custom"].indexOf(existingModel) : 0
});
if (!model) {
await handleCancellation();
return;
}
if (model === "custom") {
const { primaryModel, haikuModel, sonnetModel, opusModel } = await promptCustomModels();
if (!primaryModel.trim() && !haikuModel.trim() && !sonnetModel.trim() && !opusModel.trim()) {
console.log(ansis.yellow(`\u26A0 ${i18n.t("configuration:customModelSkipped") || "Custom model configuration skipped"}`));
return;
}
updateCustomModel(primaryModel, haikuModel, sonnetModel, opusModel);
console.log(ansis.green(`\u2714 ${i18n.t("configuration:customModelConfigured") || "Custom model configuration completed"}`));
return;
}
updateDefaultModel(model);
console.log(ansis.green(`\u2714 ${i18n.t("configuration:modelConfigured") || "Default model configured"}`));
}
async function promptCustomModels(defaultPrimaryModel, defaultHaikuModel, defaultSonnetModel, defaultOpusModel) {
const { primaryModel } = await inquirer.prompt({
type: "input",
name: "primaryModel",
message: `${i18n.t("configuration:enterPrimaryModel")}${i18n.t("common:emptyToSkip")}`,
default: defaultPrimaryModel || ""
});
const { haikuModel } = await inquirer.prompt({
type: "input",
name: "haikuModel",
message: `${i18n.t("configuration:enterHaikuModel")}${i18n.t("common:emptyToSkip")}`,
default: defaultHaikuModel || ""
});
const { sonnetModel } = await inquirer.prompt({
type: "input",
name: "sonnetModel",
message: `${i18n.t("configuration:enterSonnetModel")}${i18n.t("common:emptyToSkip")}`,
default: defaultSonnetModel || ""
});
const { opusModel } = await inquirer.prompt({
type: "input",
name: "opusModel",
message: `${i18n.t("configuration:enterOpusModel")}${i18n.t("common:emptyToSkip")}`,
default: defaultOpusModel || ""
});
return { primaryModel, haikuModel, sonnetModel, opusModel };
}
async function configureAiMemoryFeature() {
ensureI18nInitialized();
const { option } = await inquirer.prompt({
type: "list",
name: "option",
message: i18n.t("configuration:selectMemoryOption") || "Select configuration option",
choices: addNumbersToChoices([
{
name: i18n.t("configuration:configureAiLanguage") || "Configure AI output language",
value: "language"
},
{
name: i18n.t("configuration:configureOutputStyle") || "Configure global AI output style",
value: "outputStyle"
}
])
});
if (!option) {
return;
}
if (option === "language") {
const zcfConfig = readZcfConfig();
const existingLang = zcfConfig?.aiOutputLang;
if (existingLang) {
console.log(
`
${ansis.blue(`\u2139 ${i18n.t("configuration:existingLanguageConfig") || "Existing AI output language configuration"}`)}`
);
console.log(ansis.gray(` ${i18n.t("configuration:currentLanguage") || "Current language"}: ${existingLang}
`));
const modify = await promptBoolean({
message: i18n.t("configuration:modifyLanguage") || "Modify AI output language?",
defaultValue: false
});
if (!modify) {
console.log(ansis.green(`\u2714 ${i18n.t("configuration:keepLanguage") || "Keeping existing language configuration"}`));
return;
}
}
const { selectAiOutputLanguage } = await import('./simple-config.mjs').then(function (n) { return n.bm; });
const aiOutputLang = await selectAiOutputLanguage();
applyAiLanguageDirective(aiOutputLang);
updateZcfConfig({ aiOutputLang });
console.log(ansis.green(`\u2714 ${i18n.t("configuration:aiLanguageConfigured") || "AI output language configured"}`));
} else if (option === "outputStyle") {
await configureOutputStyle();
}
}
async function changeScriptLanguageFeature(currentLang) {
ensureI18nInitialized();
const { lang } = await inquirer.prompt({
type: "list",
name: "lang",
message: i18n.t("language:selectScriptLang"),
choices: addNumbersToChoices(
SUPPORTED_LANGS.map((l) => ({
name: LANG_LABELS[l],
value: l
}))
),
default: SUPPORTED_LANGS.indexOf(currentLang)
});
if (!lang) {
return currentLang;
}
updateZcfConfig({ preferredLang: lang });
await changeLanguage(lang);
console.log(ansis.green(`\u2714 ${i18n.t("language:languageChanged") || "Language changed"}`));
return lang;
}
async function configureCodexDefaultModelFeature() {
ensureI18nInitialized();
const { readCodexConfig } = await import('./simple-config.mjs').then(function (n) { return n.bn; });
const existingConfig = readCodexConfig();
const currentModel = existingConfig?.model;
if (currentModel) {
console.log(`
${ansis.blue(`\u2139 ${i18n.t("configuration:existingModelConfig") || "Existing model configuration"}`)}`);
const modelDisplay = currentModel === "gpt-5-codex" ? "GPT-5-Codex" : currentModel === "gpt-5" ? "GPT-5" : currentModel.charAt(0).toUpperCase() + currentModel.slice(1);
console.log(ansis.gray(` ${i18n.t("configuration:currentModel") || "Current model"}: ${modelDisplay}
`));
const modify = await promptBoolean({
message: i18n.t("configuration:modifyModel") || "Modify model configuration?",
defaultValue: false
});
if (!modify) {
console.log(ansis.green(`\u2714 ${i18n.t("configuration:keepModel") || "Keeping existing model configuration"}`));
return;
}
}
const { model } = await inquirer.prompt({
type: "list",
name: "model",
message: i18n.t("configuration:selectDefaultModel") || "Select default model",
choices: addNumbersToChoices([
{
name: i18n.t("configuration:codexModelOptions.gpt5"),
value: "gpt-5"
},
{
name: i18n.t("configuration:codexModelOptions.gpt5Codex"),
value: "gpt-5-codex"
},
{
name: i18n.t("configuration:codexModelOptions.custom"),
value: "custom"
}
]),
default: currentModel ? ["gpt-5", "gpt-5-codex", "custom"].indexOf(currentModel) : 1
// Default to gpt-5-codex
});
if (!model) {
await handleCancellation();
return;
}
if (model === "custom") {
const { customModel } = await inquirer.prompt({
type: "input",
name: "customModel",
message: `${i18n.t("configuration:enterCustomModel")}${i18n.t("common:emptyToSkip")}`,
default: ""
});
if (!customModel.trim()) {
console.log(ansis.yellow(`\u26A0 ${i18n.t("configuration:customModelSkipped") || "Custom model configuration skipped"}`));
return;
}
await updateCodexModelProvider(customModel.trim());
console.log(ansis.green(`\u2714 ${i18n.t("configuration:customModelConfigured") || "Custom model configuration completed"}`));
return;
}
await updateCodexModelProvider(model);
console.log(ansis.green(`\u2714 ${i18n.t("configuration:modelConfigured") || "Default model configured"}`));
}
async function configureCodexAiMemoryFeature() {
ensureI18nInitialized();
const { option } = await inquirer.prompt({
type: "list",
name: "option",
message: i18n.t("configuration:selectMemoryOption") || "Select configuration option",
choices: addNumbersToChoices([
{
name: i18n.t("configuration:configureAiLanguage") || "Configure AI output language",
value: "language"
},
{
name: i18n.t("configuration:configureSystemPromptStyle") || "Configure global AI system prompt style",
value: "systemPrompt"
}
])
});
if (!option) {
return;
}
if (option === "language") {
const zcfConfig = readZcfConfig();
const existingLang = zcfConfig?.aiOutputLang;
if (existingLang) {
console.log(
`
${ansis.blue(`\u2139 ${i18n.t("configuration:existingLanguageConfig") || "Existing AI output language configuration"}`)}`
);
console.log(ansis.gray(` ${i18n.t("configuration:currentLanguage") || "Current language"}: ${existingLang}
`));
const modify = await promptBoolean({
message: i18n.t("configuration:modifyLanguage") || "Modify AI output language?",
defaultValue: false
});
if (!modify) {
console.log(ansis.green(`\u2714 ${i18n.t("configuration:keepLanguage") || "Keeping existing language configuration"}`));
await ensureLanguageDirectiveInAgents(existingLang);
return;
}
}
const { selectAiOutputLanguage } = await import('./simple-config.mjs').then(function (n) { return n.bm; });
const aiOutputLang = await selectAiOutputLanguage();
await updateCodexLanguageDirective(aiOutputLang);
updateZcfConfig({ aiOutputLang });
console.log(ansis.green(`\u2714 ${i18n.t("configuration:aiLanguageConfigured") || "AI output language configured"}`));
} else if (option === "systemPrompt") {
const zcfConfig = readZcfConfig();
const currentLang = zcfConfig?.aiOutputLang || "English";
const { runCodexSystemPromptSelection } = await import('./simple-config.mjs').then(function (n) { return n.bn; });
await runCodexSystemPromptSelection();
await ensureLanguageDirectiveInAgents(currentLang);
console.log(ansis.green(`\u2714 ${i18n.t("configuration:systemPromptConfigured")}`));
}
}
async function updateCodexModelProvider(modelProvider) {
const { readCodexConfig, writeCodexConfig, backupCodexConfig, getBackupMessage } = await import('./simple-config.mjs').then(function (n) { return n.bn; });
const backupPath = backupCodexConfig();
if (backupPath) {
console.log(ansis.gray(getBackupMessage(backupPath)));
}
const existingConfig = readCodexConfig();
const updatedConfig = {
...existingConfig,
model: modelProvider,
// Set the model field
modelProvider: existingConfig?.modelProvider || null,
// Preserve existing API provider
providers: existingConfig?.providers || [],
mcpServices: existingConfig?.mcpServices || [],
managed: true,
otherConfig: existingConfig?.otherConfig || [],
modelProviderCommented: existingConfig?.modelProviderCommented
};
writeCodexConfig(updatedConfig);
}
async function ensureLanguageDirectiveInAgents(aiOutputLang) {
const { readFile, writeFile, exists } = await import('./simple-config.mjs').then(function (n) { return n.bh; });
const { homedir } = await import('node:os');
const { join } = await import('pathe');
const CODEX_AGENTS_FILE = join(homedir(), ".codex", "AGENTS.md");
if (!exists(CODEX_AGENTS_FILE)) {
console.log(ansis.yellow(i18n.t("codex:agentsFileNotFound")));
return;
}
const content = readFile(CODEX_AGENTS_FILE);
const languageLabels = {
"Chinese": "Chinese-simplified",
"English": "English",
"zh-CN": "Chinese-simplified",
"en": "English"
};
const langLabel = languageLabels[aiOutputLang] || aiOutputLang;
const hasLanguageDirective = /\*\*Most Important:\s*Always respond in [^*]+\*\*/i.test(content);
if (!hasLanguageDirective) {
const { backupCodexAgents, getBackupMessage } = await import('./simple-config.mjs').then(function (n) { return n.bn; });
const backupPath = backupCodexAgents();
if (backupPath) {
console.log(ansis.gray(getBackupMessage(backupPath)));
}
let updatedContent = content;
if (!updatedContent.endsWith("\n")) {
updatedContent += "\n";
}
updatedContent += `
**Most Important:Always respond in ${langLabel}**
`;
writeFile(CODEX_AGENTS_FILE, updatedContent);
console.log(ansis.gray(` ${i18n.t("configuration:addedLanguageDirective")}: ${langLabel}`));
}
}
async function updateCodexLanguageDirective(aiOutputLang) {
const { readFile, writeFile, exists } = await import('./simple-config.mjs').then(function (n) { return n.bh; });
const { backupCodexAgents, getBackupMessage } = await import('./simple-config.mjs').then(function (n) { return n.bn; });
const { homedir } = await import('node:os');
const { join } = await import('pathe');
const CODEX_AGENTS_FILE = join(homedir(), ".codex", "AGENTS.md");
if (!exists(CODEX_AGENTS_FILE)) {
console.log(ansis.yellow(i18n.t("codex:agentsFileNotFound")));
return;
}
const backupPath = backupCodexAgents();
if (backupPath) {
console.log(ansis.gray(getBackupMessage(backupPath)));
}
let content = readFile(CODEX_AGENTS_FILE);
const languageLabels = {
"Chinese": "Chinese-simplified",
"English": "English",
"zh-CN": "Chinese-simplified",
"en": "English"
};
const langLabel = languageLabels[aiOutputLang] || aiOutputLang;
content = content.replace(/\*\*Most Important:\s*Always respond in [^*]+\*\*\s*/g, "");
if (!content.endsWith("\n")) {
content += "\n";
}
content += `
**Most Important:Always respond in ${langLabel}**
`;
writeFile(CODEX_AGENTS_FILE, content);
}
async function configureEnvPermissionFeature() {
ensureI18nInitialized();
const { choice } = await inquirer.prompt({
type: "list",
name: "choice",
message: i18n.t("configuration:selectEnvPermissionOption") || "Select option",
choices: addNumbersToChoices([
{
name: `${i18n.t("configuration:importRecommendedEnv") || "Import environment"} ${ansis.gray(
`- ${i18n.t("configuration:importRecommendedEnvDesc") || "Import env settings"}`
)}`,
value: "env"
},
{
name: `${i18n.t("configuration:importRecommendedPermissions") || "Import permissions"} ${ansis.gray(
`- ${i18n.t("configuration:importRecommendedPermissionsDesc") || "Import permission settings"}`
)}`,
value: "permissions"
},
{
name: `${i18n.t("configuration:openSettingsJson") || "Open settings"} ${ansis.gray(
`- ${i18n.t("configuration:openSettingsJsonDesc") || "View settings file"}`
)}`,
value: "open"
}
])
});
if (!choice) {
await handleCancellation();
return;
}
try {
switch (choice) {
case "env":
await importRecommendedEnv();
console.log(ansis.green(`\u2705 ${i18n.t("configuration:envImportSuccess")}`));
break;
case "permissions":
await importRecommendedPermissions();
console.log(ansis.green(`\u2705 ${i18n.t("configuration:permissionsImportSuccess") || "Permissions imported"}`));
break;
case "open":
console.log(ansis.cyan(i18n.t("configuration:openingSettingsJson") || "Opening settings.json..."));
await openSettingsJson();
break;
}
} catch (error) {
console.error(ansis.red(`${i18n.t("common:error")}: ${error.message}`));
}
}
export { changeScriptLanguageFeature, configureAiMemoryFeature, configureApiFeature, configureCodexAiMemoryFeature, configureCodexDefaultModelFeature, configureDefaultModelFeature, configureEnvPermissionFeature, configureMcpFeature, promptCustomModels };