UNPKG

zcf

Version:

Zero-Config Code Flow - One-click configuration tool for Code Cli

641 lines (638 loc) 24.6 kB
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 };