UNPKG

zcf

Version:

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

426 lines (423 loc) 15.6 kB
import ansis from 'ansis'; import inquirer from 'inquirer'; import { ak as ensureI18nInitialized, ax as detectConfigManagementMode, al as i18n, am as addNumbersToChoices, ao as promptBoolean, at as readJsonConfig, l as CODEX_AUTH_FILE } from './simple-config.mjs'; import { deleteProviders, addProviderToExisting, editExistingProvider } from './codex-provider-manager.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 configureIncrementalManagement() { ensureI18nInitialized(); const managementMode = detectConfigManagementMode(); if (managementMode.mode !== "management" || !managementMode.hasProviders) { console.log(ansis.yellow(i18n.t("codex:noExistingProviders"))); return; } console.log(ansis.cyan(i18n.t("codex:incrementalManagementTitle"))); console.log(ansis.gray(i18n.t("codex:currentProviderCount", { count: managementMode.providerCount }))); if (managementMode.currentProvider) { console.log(ansis.gray(i18n.t("codex:currentDefaultProvider", { provider: managementMode.currentProvider }))); } const choices = [ { name: i18n.t("codex:addProvider"), value: "add" }, { name: i18n.t("codex:editProvider"), value: "edit" }, { name: i18n.t("codex:copyProvider"), value: "copy" }, { name: i18n.t("codex:deleteProvider"), value: "delete" }, { name: i18n.t("common:skip"), value: "skip" } ]; const { action } = await inquirer.prompt([{ type: "list", name: "action", message: i18n.t("codex:selectAction"), choices: addNumbersToChoices(choices) }]); if (!action || action === "skip") { console.log(ansis.yellow(i18n.t("common:skip"))); return; } switch (action) { case "add": await handleAddProvider(); break; case "edit": await handleEditProvider(managementMode.providers); break; case "copy": await handleCopyProvider(managementMode.providers); break; case "delete": await handleDeleteProvider(managementMode.providers); break; } } async function handleAddProvider() { const { getApiProviders } = await import('./api-providers.mjs'); const apiProviders = getApiProviders("codex"); const providerChoices = [ { name: i18n.t("api:customProvider"), value: "custom" }, ...apiProviders.map((p) => ({ name: p.name, value: p.id })) ]; const { selectedProvider } = await inquirer.prompt([{ type: "list", name: "selectedProvider", message: i18n.t("api:selectApiProvider"), choices: addNumbersToChoices(providerChoices) }]); let prefilledBaseUrl; let prefilledWireApi; let prefilledModel; if (selectedProvider !== "custom") { const provider2 = apiProviders.find((p) => p.id === selectedProvider); if (provider2?.codex) { prefilledBaseUrl = provider2.codex.baseUrl; prefilledWireApi = provider2.codex.wireApi; prefilledModel = provider2.codex.defaultModel; console.log(ansis.gray(i18n.t("api:providerSelected", { name: provider2.name }))); } } const answers = await inquirer.prompt([ { type: "input", name: "providerName", message: i18n.t("codex:providerNamePrompt"), default: selectedProvider !== "custom" ? apiProviders.find((p) => p.id === selectedProvider)?.name : void 0, validate: (input) => { const trimmed = input.trim(); if (!trimmed) return i18n.t("codex:providerNameRequired"); if (!/^[\w\-\s.]+$/.test(trimmed)) return i18n.t("codex:providerNameInvalid"); return true; } }, { type: "input", name: "baseUrl", message: i18n.t("codex:providerBaseUrlPrompt"), default: prefilledBaseUrl || "https://api.openai.com/v1", when: () => selectedProvider === "custom", validate: (input) => !!input.trim() || i18n.t("codex:providerBaseUrlRequired") }, { type: "list", name: "wireApi", message: i18n.t("codex:providerProtocolPrompt"), choices: [ { name: i18n.t("codex:protocolResponses"), value: "responses" }, { name: i18n.t("codex:protocolChat"), value: "chat" } ], default: prefilledWireApi || "responses", when: () => selectedProvider === "custom" }, { type: "input", name: "apiKey", message: selectedProvider !== "custom" ? i18n.t("api:enterProviderApiKey", { provider: apiProviders.find((p) => p.id === selectedProvider)?.name || selectedProvider }) : i18n.t("codex:providerApiKeyPrompt"), validate: (input) => !!input.trim() || i18n.t("codex:providerApiKeyRequired") } ]); const providerId = answers.providerName.trim().toLowerCase().replace(/\s+/g, "-").replace(/\./g, "-").replace(/[^a-z0-9\-]/g, ""); const managementMode = detectConfigManagementMode(); const existingProvider = managementMode.providers?.find((p) => p.id === providerId); if (existingProvider) { const shouldOverwrite = await promptBoolean({ message: i18n.t("codex:providerDuplicatePrompt", { name: existingProvider.name, source: i18n.t("codex:existingConfig") }), defaultValue: false }); if (!shouldOverwrite) { console.log(ansis.yellow(i18n.t("codex:providerDuplicateSkipped"))); return; } } const provider = { id: providerId, name: answers.providerName.trim(), baseUrl: selectedProvider === "custom" ? answers.baseUrl.trim() : prefilledBaseUrl, wireApi: selectedProvider === "custom" ? answers.wireApi : prefilledWireApi, tempEnvKey: `${providerId.toUpperCase().replace(/-/g, "_")}_API_KEY`, requiresOpenaiAuth: true, model: prefilledModel || "gpt-5-codex" // Use provider's default model or fallback }; const result = await addProviderToExisting(provider, answers.apiKey.trim(), true); if (result.success) { console.log(ansis.green(i18n.t("codex:providerAdded", { name: result.addedProvider?.name }))); if (result.backupPath) { console.log(ansis.gray(i18n.t("common:backupCreated", { path: result.backupPath }))); } const setAsDefault = await promptBoolean({ message: i18n.t("multi-config:setAsDefaultPrompt"), defaultValue: true }); if (setAsDefault) { const { switchToProvider } = await import('./simple-config.mjs').then(function (n) { return n.bn; }); const switched = await switchToProvider(provider.id); if (switched) { console.log(ansis.green(i18n.t("multi-config:profileSetAsDefault", { name: provider.name }))); } } } else { console.log(ansis.red(i18n.t("codex:providerAddFailed", { error: result.error }))); } } async function handleEditProvider(providers) { const choices = providers.map((provider2) => ({ name: `${provider2.name} (${provider2.baseUrl})`, value: provider2.id })); const { selectedProviderId } = await inquirer.prompt([{ type: "list", name: "selectedProviderId", message: i18n.t("codex:selectProviderToEdit"), choices: addNumbersToChoices(choices) }]); if (!selectedProviderId) { console.log(ansis.yellow(i18n.t("common:cancelled"))); return; } const provider = providers.find((p) => p.id === selectedProviderId); if (!provider) { console.log(ansis.red(i18n.t("codex:providerNotFound"))); return; } const existingAuth = readJsonConfig(CODEX_AUTH_FILE, { defaultValue: {} }) || {}; const existingApiKey = existingAuth[provider.tempEnvKey] || ""; const answers = await inquirer.prompt([ { type: "input", name: "providerName", message: i18n.t("codex:providerNamePrompt"), default: provider.name, validate: (input) => { const trimmed = input.trim(); if (!trimmed) return i18n.t("codex:providerNameRequired"); if (!/^[\w\-\s]+$/.test(trimmed)) return i18n.t("codex:providerNameInvalid"); return true; } }, { type: "input", name: "baseUrl", message: i18n.t("codex:providerBaseUrlPrompt"), default: provider.baseUrl, validate: (input) => !!input.trim() || i18n.t("codex:providerBaseUrlRequired") }, { type: "list", name: "wireApi", message: i18n.t("codex:providerProtocolPrompt"), choices: [ { name: i18n.t("codex:protocolResponses"), value: "responses" }, { name: i18n.t("codex:protocolChat"), value: "chat" } ], default: provider.wireApi }, { type: "input", name: "apiKey", message: i18n.t("codex:providerApiKeyPrompt"), default: existingApiKey, // Show old API key from auth.json validate: (input) => !!input.trim() || i18n.t("codex:providerApiKeyRequired") } ]); const { model } = await inquirer.prompt([ { type: "input", name: "model", message: i18n.t("codex:providerModelPrompt"), default: provider.model || "gpt-5-codex", validate: (input) => !!input.trim() || i18n.t("codex:providerModelRequired") } ]); const updates = { name: answers.providerName.trim(), baseUrl: answers.baseUrl.trim(), wireApi: answers.wireApi, apiKey: answers.apiKey.trim(), model: model.trim() }; const result = await editExistingProvider(selectedProviderId, updates); if (result.success) { console.log(ansis.green(i18n.t("codex:providerUpdated", { name: result.updatedProvider?.name }))); if (result.backupPath) { console.log(ansis.gray(i18n.t("common:backupCreated", { path: result.backupPath }))); } } else { console.log(ansis.red(i18n.t("codex:providerUpdateFailed", { error: result.error }))); } } async function handleCopyProvider(providers) { const choices = providers.map((provider2) => ({ name: `${provider2.name} (${provider2.baseUrl})`, value: provider2.id })); const { selectedProviderId } = await inquirer.prompt([{ type: "list", name: "selectedProviderId", message: i18n.t("codex:selectProviderToCopy"), choices: addNumbersToChoices(choices) }]); if (!selectedProviderId) { console.log(ansis.yellow(i18n.t("common:cancelled"))); return; } const provider = providers.find((p) => p.id === selectedProviderId); if (!provider) { console.log(ansis.red(i18n.t("codex:providerNotFound"))); return; } console.log(ansis.cyan(` ${i18n.t("codex:copyingProvider", { name: provider.name })}`)); const existingAuth = readJsonConfig(CODEX_AUTH_FILE, { defaultValue: {} }) || {}; const existingApiKey = existingAuth[provider.tempEnvKey] || ""; const copiedName = `${provider.name}-copy`; const answers = await inquirer.prompt([ { type: "input", name: "providerName", message: i18n.t("codex:providerNamePrompt"), default: copiedName, validate: (input) => { const trimmed = input.trim(); if (!trimmed) return i18n.t("codex:providerNameRequired"); if (!/^[\w\-\s.]+$/.test(trimmed)) return i18n.t("codex:providerNameInvalid"); return true; } }, { type: "input", name: "baseUrl", message: i18n.t("codex:providerBaseUrlPrompt"), default: provider.baseUrl, validate: (input) => !!input.trim() || i18n.t("codex:providerBaseUrlRequired") }, { type: "list", name: "wireApi", message: i18n.t("codex:providerProtocolPrompt"), choices: [ { name: i18n.t("codex:protocolResponses"), value: "responses" }, { name: i18n.t("codex:protocolChat"), value: "chat" } ], default: provider.wireApi }, { type: "input", name: "apiKey", message: i18n.t("codex:providerApiKeyPrompt"), default: existingApiKey, // Show old API key from auth.json validate: (input) => !!input.trim() || i18n.t("codex:providerApiKeyRequired") } ]); const { model } = await inquirer.prompt([ { type: "input", name: "model", message: i18n.t("codex:providerModelPrompt"), default: provider.model || "gpt-5-codex", validate: (input) => !!input.trim() || i18n.t("codex:providerModelRequired") } ]); const providerId = answers.providerName.trim().toLowerCase().replace(/\s+/g, "-").replace(/\./g, "-").replace(/[^a-z0-9\-]/g, ""); const copiedProvider = { id: providerId, name: answers.providerName.trim(), baseUrl: answers.baseUrl.trim(), wireApi: answers.wireApi, tempEnvKey: `${providerId.toUpperCase().replace(/-/g, "_")}_API_KEY`, requiresOpenaiAuth: provider.requiresOpenaiAuth ?? true, model: model.trim() }; const result = await addProviderToExisting(copiedProvider, answers.apiKey.trim(), false); if (result.success) { console.log(ansis.green(i18n.t("codex:providerCopied", { name: result.addedProvider?.name }))); if (result.backupPath) { console.log(ansis.gray(i18n.t("common:backupCreated", { path: result.backupPath }))); } const setAsDefault = await promptBoolean({ message: i18n.t("multi-config:setAsDefaultPrompt"), defaultValue: false }); if (setAsDefault) { const { switchToProvider } = await import('./simple-config.mjs').then(function (n) { return n.bn; }); const switched = await switchToProvider(copiedProvider.id); if (switched) { console.log(ansis.green(i18n.t("multi-config:profileSetAsDefault", { name: copiedProvider.name }))); } } } else { console.log(ansis.red(i18n.t("codex:providerCopyFailed", { error: result.error }))); } } async function handleDeleteProvider(providers) { const choices = providers.map((provider) => ({ name: `${provider.name} (${provider.baseUrl})`, value: provider.id })); const { selectedProviderIds } = await inquirer.prompt({ type: "checkbox", name: "selectedProviderIds", message: i18n.t("codex:selectProvidersToDelete"), choices, validate: (input) => { const selected = input; if (!selected || selected.length === 0) { return i18n.t("codex:selectAtLeastOne"); } if (selected.length === providers.length) { return i18n.t("codex:cannotDeleteAll"); } return true; } }); if (!selectedProviderIds || selectedProviderIds.length === 0) { console.log(ansis.yellow(i18n.t("common:cancelled"))); return; } const selectedNames = selectedProviderIds.map( (id) => providers.find((p) => p.id === id)?.name || id ).join(", "); const confirmDelete = await promptBoolean({ message: i18n.t("codex:confirmDeleteProviders", { providers: selectedNames }), defaultValue: false }); if (!confirmDelete) { console.log(ansis.yellow(i18n.t("common:cancelled"))); return; } const result = await deleteProviders(selectedProviderIds); if (result.success) { console.log(ansis.green(i18n.t("codex:providersDeleted", { count: selectedProviderIds.length }))); if (result.newDefaultProvider) { console.log(ansis.cyan(i18n.t("codex:newDefaultProvider", { provider: result.newDefaultProvider }))); } if (result.backupPath) { console.log(ansis.gray(i18n.t("common:backupCreated", { path: result.backupPath }))); } } else { console.log(ansis.red(i18n.t("codex:providersDeleteFailed", { error: result.error }))); } } const codexConfigSwitch = { configureIncrementalManagement }; export { configureIncrementalManagement, codexConfigSwitch as default };